nmg_misc.c

Go to the documentation of this file.
00001 /*                      N M G _ M I S C . 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_misc.c
00026  *      As the name implies, these are miscellaneous routines that work with
00027  *      the NMG structures.
00028  *
00029  *
00030  *  Authors -
00031  *      John R. Anderson
00032  *      Lee A. Butler
00033  *      Michael John Muuss
00034  *
00035  *  Source -
00036  *      The U. S. Army Research Laboratory
00037  *      Aberdeen Proving Ground, Maryland  21005-5068  USA
00038  */
00039 /*@}*/
00040 
00041 #ifndef lint
00042 static const char RCSid[] = "@(#)$Header: /cvsroot/brlcad/brlcad/src/librt/nmg_misc.c,v 14.15 2006/09/16 02:04:25 lbutler Exp $ (ARL)";
00043 #endif
00044 
00045 #include "common.h"
00046 
00047 
00048 #include <math.h>
00049 #include <stdio.h>
00050 #ifdef HAVE_STRING_H
00051 #include <string.h>
00052 #else
00053 #include <strings.h>
00054 #endif
00055 #include "machine.h"
00056 #include "vmath.h"
00057 #include "nmg.h"
00058 #include "raytrace.h"
00059 #include "nurb.h"
00060 #include "rtgeom.h"
00061 
00062 #include "db.h"         /* for debugging stuff at bottom */
00063 
00064 /**
00065  *      N M G _ S N U R B _ C A L C _ L U _ U V _ O R I E N T
00066  */
00067 int
00068 nmg_snurb_calc_lu_uv_orient(const struct loopuse *lu)
00069 {
00070         struct edgeuse *eu;
00071         int edge_count=0;
00072         int edge_no;
00073         vect_t area;
00074         point_t *pts;
00075 
00076         NMG_CK_LOOPUSE( lu );
00077 
00078         if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) != NMG_EDGEUSE_MAGIC )
00079                 rt_bomb( "nmg_snurb_calc_lu_uv_orient: LU has no edges\n" );
00080 
00081         if( *lu->up.magic_p != NMG_FACEUSE_MAGIC )
00082                 rt_bomb( "nmg_snurb_calc_lu_uv_orient: LU is not part of a faceuse\n" );
00083 
00084         NMG_CK_FACEUSE( lu->up.fu_p );
00085         NMG_CK_FACE( lu->up.fu_p->f_p );
00086 
00087         if( *lu->up.fu_p->f_p->g.magic_p != NMG_FACE_G_SNURB_MAGIC )
00088                 rt_bomb( "nmg_snurb_calc_lu_uv_orient: LU is not part of a SNURB face\n" );
00089 
00090         /* count "psuedo-vertices" in loop */
00091         for( BU_LIST_FOR( eu, edgeuse, &lu->down_hd ) )
00092         {
00093                 struct edge_g_cnurb *eg;
00094 
00095                 NMG_CK_EDGEUSE( eu );
00096 
00097                 if( *eu->g.magic_p != NMG_EDGE_G_CNURB_MAGIC )
00098                         rt_bomb( "nmg_snurb_calc_lu_uv_orient: EU on NURB face does not have edge_g_cnurb geometry\n" );
00099 
00100                 eg = eu->g.cnurb_p;
00101                 NMG_CK_EDGE_G_CNURB( eg );
00102 
00103                 if( eg->order <= 0 )
00104                         edge_count++;
00105                 else
00106                         edge_count += 5;
00107         }
00108 
00109         /* allocate memory for "psuedo-vertices" */
00110         pts = (point_t *)bu_calloc( edge_count, sizeof( point_t ), "Orient_nurb_face_loops: pts" );
00111 
00112         /* Assign uv geometry to each "psuedo-vertex" */
00113         edge_no = 0;
00114         for( BU_LIST_FOR( eu, edgeuse, &lu->down_hd ) )
00115         {
00116                 struct edge_g_cnurb *eg;
00117                 struct vertexuse *vu;
00118                 struct vertexuse_a_cnurb *vg1;
00119 
00120                 eg = eu->g.cnurb_p;
00121 
00122                 if( eg->order <= 0 )
00123                 {
00124                         vu = eu->vu_p;
00125                         NMG_CK_VERTEXUSE( vu );
00126                         if( *vu->a.magic_p != NMG_VERTEXUSE_A_CNURB_MAGIC )
00127                                 rt_bomb( "Orient_nurb_face_loops: vertexuse in face_g_snurb faceuse doesn't have edge_g_cnurb attribute\n" );
00128                         vg1 = vu->a.cnurb_p;
00129                         VMOVE( pts[edge_no], vg1->param )
00130                         edge_no++;
00131                 }
00132                 else
00133                 {
00134                         fastf_t t1,t2;
00135                         hpoint_t crv_pt;
00136                         int coords;
00137                         int i;
00138 
00139                         t1 = eg->k.knots[0];
00140                         t2 = eg->k.knots[eg->k.k_size-1];
00141                         coords = RT_NURB_EXTRACT_COORDS( eg->pt_type );
00142 
00143                         for( i=0 ; i<5 ; i++ )
00144                         {
00145                                 fastf_t t;
00146 
00147                                 t = t1 + (t2 - t1)*0.2*(fastf_t)i;
00148 
00149                                 VSETALLN( crv_pt, 0.0, coords )
00150                                 rt_nurb_c_eval( eg, t, crv_pt );
00151                                 if( RT_NURB_IS_PT_RATIONAL( eg->pt_type ) )
00152                                         VSCALE( pts[edge_no], crv_pt, crv_pt[coords-1] )
00153                                 else
00154                                         VMOVE( pts[edge_no], crv_pt )
00155                                 edge_no++;
00156                         }
00157                 }
00158         }
00159 
00160         /* translate loop such that pts[0] is at (0,0,0) */
00161         for( edge_no=1 ; edge_no<edge_count ; edge_no++ )
00162         {
00163                 VSUB2( pts[edge_no], pts[edge_no], pts[0] )
00164                 pts[edge_no][Z] = 0.0;
00165         }
00166         VSETALL( pts[0], 0.0 )
00167 
00168         /* calculate area of loop in uv-space */
00169         VSETALL( area, 0.0 );
00170         for( edge_no=1 ; edge_no<edge_count-1 ; edge_no++ )
00171         {
00172                 vect_t cross;
00173 
00174                 VCROSS( cross, pts[edge_no], pts[edge_no+1] );
00175                 VADD2( area, area, cross );
00176         }
00177 
00178         bu_free( (char *)pts, "nmg_snurb_calc_lu_uv_orient: pts" );
00179 
00180         if( area[Z] > 0.0 )
00181                 return( OT_SAME );
00182         if( area[Z] < 0.0 )
00183                 return( OT_OPPOSITE );
00184 
00185         return( OT_NONE );
00186 }
00187 
00188 void
00189 nmg_snurb_fu_eval(const struct faceuse *fu, const fastf_t u, const fastf_t v, fastf_t *pt_on_srf)
00190 {
00191         struct face *f;
00192         hpoint_t tmp_pt;
00193 
00194         NMG_CK_FACEUSE( fu );
00195 
00196         f = fu->f_p;
00197         NMG_CK_FACE( f );
00198         if( !f->g.magic_p )
00199         {
00200                 bu_log( "nmg_snurb_fu_get_norm: face has no geometry (x%x)\n", f );
00201                 rt_bomb( "nmg_snurb_fu_get_norm: bad face\n" );
00202         }
00203         if( *f->g.magic_p != NMG_FACE_G_SNURB_MAGIC )
00204         {
00205                 bu_log( "nmg_snurb_fu_get_norm: face is not a NURB face (x%x)\n", f );
00206                 rt_bomb( "nmg_snurb_fu_get_norm: bad face\n" );
00207         }
00208 
00209         VSETALLN( tmp_pt, 0.0, 4 );
00210         rt_nurb_s_eval( f->g.snurb_p, u, v, tmp_pt );
00211 
00212         if( RT_NURB_IS_PT_RATIONAL(f->g.snurb_p->pt_type) )
00213         {
00214                 double f;
00215 
00216                 f = 1.0 / tmp_pt[3];
00217                 VSCALE( pt_on_srf, tmp_pt, f );
00218         }
00219         else
00220                 VMOVE( pt_on_srf, tmp_pt )
00221 }
00222 
00223 void
00224 nmg_snurb_fu_get_norm(const struct faceuse *fu, const fastf_t u, const fastf_t v, fastf_t *norm)
00225 {
00226         struct face *f;
00227 
00228         NMG_CK_FACEUSE( fu );
00229 
00230         f = fu->f_p;
00231         NMG_CK_FACE( f );
00232         if( !f->g.magic_p )
00233         {
00234                 bu_log( "nmg_snurb_fu_get_norm: face has no geometry (x%x)\n", f );
00235                 rt_bomb( "nmg_snurb_fu_get_norm: bad face\n" );
00236         }
00237         if( *f->g.magic_p != NMG_FACE_G_SNURB_MAGIC )
00238         {
00239                 bu_log( "nmg_snurb_fu_get_norm: face is not a NURB face (x%x)\n", f );
00240                 rt_bomb( "nmg_snurb_fu_get_norm: bad face\n" );
00241         }
00242 
00243         rt_nurb_s_norm( f->g.snurb_p, u, v, norm );
00244 
00245         if( (fu->orientation != OT_SAME) != (f->flip != 0 ) )
00246                 VREVERSE( norm, norm )
00247 }
00248 
00249 void
00250 nmg_snurb_fu_get_norm_at_vu(const struct faceuse *fu, const struct vertexuse *vu, fastf_t *norm)
00251 {
00252         struct vertexuse_a_cnurb *va;
00253 
00254         NMG_CK_FACEUSE( fu );
00255         NMG_CK_VERTEXUSE( vu );
00256 
00257         if( !vu->a.magic_p )
00258         {
00259                 bu_log( "nmg_snurb_fu_get_norm_at_vu: vertexuse does not have an attribute (x%x)\n", vu );
00260                 rt_bomb( "nmg_snurb_fu_get_norm_at_vu: bad VU\n" );
00261         }
00262 
00263         if( *vu->a.magic_p != NMG_VERTEXUSE_A_CNURB_MAGIC )
00264         {
00265                 bu_log( "nmg_snurb_fu_get_norm_at_vu: vertexuse does not have a cnurb attribute (x%x)\n", vu );
00266                 rt_bomb( "nmg_snurb_fu_get_norm_at_vu: bad VU\n" );
00267         }
00268 
00269         va = vu->a.cnurb_p;
00270         NMG_CK_VERTEXUSE_A_CNURB( va );
00271 
00272         nmg_snurb_fu_get_norm( fu, va->param[0], va->param[1], norm );
00273 
00274 }
00275 
00276 void
00277 nmg_find_zero_length_edges(const struct model *m)
00278 {
00279         struct bu_ptbl eu_tab;
00280         struct edgeuse *eu;
00281         int i;
00282 
00283         bu_ptbl_init( &eu_tab, 64, " &eu_tab");
00284 
00285         nmg_edgeuse_tabulate( &eu_tab, &m->magic );
00286 
00287         for( i=0 ; i<BU_PTBL_END( &eu_tab ) ; i++ )
00288         {
00289                 struct loopuse *lu;
00290 
00291                 eu = (struct edgeuse *)BU_PTBL_GET( &eu_tab, i );
00292                 NMG_CK_EDGEUSE( eu );
00293 
00294                 if( eu->vu_p->v_p != eu->eumate_p->vu_p->v_p )
00295                         continue;
00296 
00297                 /* found a zero length edge */
00298 
00299                 bu_log( "Edgeuse x%x (vp %x to vp %x)\n" , eu, eu->vu_p->v_p, eu->eumate_p->vu_p->v_p );
00300                 if( *eu->up.magic_p != NMG_LOOPUSE_MAGIC )
00301                 {
00302                         bu_log( "\tThis is a wire edge\n" );
00303                         continue;
00304                 }
00305 
00306                 lu = eu->up.lu_p;
00307 
00308                 nmg_pr_lu_briefly( lu, "" );
00309         }
00310 
00311         bu_ptbl_free( &eu_tab);
00312 }
00313 
00314 /**
00315  *      N M G _ F I N D _ T O P _ F A C E _ I N _ D I R
00316  *
00317  *      Finds the topmost face in a shell (in given direction).
00318  *      Expects to have a translation table (variable "flags") for
00319  *      the model, and will ignore face structures that have their
00320  *      flag set in the table.
00321  *
00322  *      dir must be X,Y, or Z
00323  */
00324 struct face *
00325 nmg_find_top_face_in_dir(const struct shell *s, int dir, long int *flags)
00326 {
00327         fastf_t extreme_value=(-MAX_FASTF);
00328         fastf_t extreme_slope=(-MAX_FASTF);
00329         vect_t edge;
00330         vect_t normal;
00331         struct face *f_top=(struct face *)NULL;
00332         struct edge *e_top=(struct edge *)NULL;
00333         struct vertex *vp_top=(struct vertex *)NULL;
00334         struct loopuse *lu;
00335         struct faceuse *fu;
00336         struct edgeuse *eu,*eu1;
00337         struct vertexuse *vu;
00338         struct vertex *v1,*v2;
00339         int bottommost=0;
00340 
00341         if( rt_g.NMG_debug & DEBUG_BASIC )
00342                 bu_log( "nmg_find_top_face_in_dir( s = x%x , dir=%d , flags = x%x )\n" , s, dir , flags );
00343 
00344         NMG_CK_SHELL( s );
00345 
00346         if( dir < X || dir > Z )
00347         {
00348                 bu_log( "nmg_find_top_face_in_dir: illegal direction: %d\n", dir );
00349                 return( (struct face *)NULL );
00350         }
00351 
00352 #if 0
00353         if( dir < 0 )
00354         {
00355                 bottommost = 1;
00356                 dir = (-dir);
00357                 extreme_value = MAX_FASTF;
00358                 extreme_slope = MAX_FASTF;
00359         }
00360 #endif
00361         /* find extreme vertex */
00362         for( BU_LIST_FOR( fu , faceuse , &s->fu_hd ) )
00363         {
00364                 NMG_CK_FACEUSE( fu );
00365 
00366                 /* skip flagged faceuses */
00367                 if( NMG_INDEX_TEST( flags , fu->f_p ) )
00368                         continue;
00369                 for( BU_LIST_FOR( lu , loopuse , &fu->lu_hd ) )
00370                 {
00371                         NMG_CK_LOOPUSE( lu );
00372                         if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) == NMG_EDGEUSE_MAGIC )
00373                         {
00374                                 for( BU_LIST_FOR( eu , edgeuse , &lu->down_hd ) )
00375                                 {
00376                                         NMG_CK_EDGEUSE( eu );
00377                                         if( bottommost )
00378                                         {
00379                                                 if( eu->vu_p->v_p->vg_p->coord[dir] < extreme_value )
00380                                                 {
00381                                                         extreme_value = eu->vu_p->v_p->vg_p->coord[dir];
00382                                                         vp_top = eu->vu_p->v_p;
00383                                                 }
00384                                         }
00385                                         else
00386                                         {
00387                                                 if( eu->vu_p->v_p->vg_p->coord[dir] > extreme_value )
00388                                                 {
00389                                                         extreme_value = eu->vu_p->v_p->vg_p->coord[dir];
00390                                                         vp_top = eu->vu_p->v_p;
00391                                                 }
00392                                         }
00393                                 }
00394                         }
00395                 }
00396         }
00397         if( vp_top == (struct vertex *)NULL )
00398         {
00399                 bu_log( "Find_top_face_in_dir: Could not find extreme vertex" );
00400                 return( (struct face *)NULL );
00401         }
00402 
00403         if( rt_g.NMG_debug & DEBUG_BASIC )
00404                 bu_log( "top vertex is x%x at ( %g %g %g )\n",
00405                         vp_top, V3ARGS( vp_top->vg_p->coord ) );
00406 
00407         /* find edge from vp_top with extreme slope in "dir" direction */
00408         for( BU_LIST_FOR( vu , vertexuse , &vp_top->vu_hd ) )
00409         {
00410                 struct vertexuse *vu1;
00411 
00412                 NMG_CK_VERTEXUSE( vu );
00413 
00414                 /* only consider edgeuses */
00415                 if( *vu->up.magic_p != NMG_EDGEUSE_MAGIC )
00416                         continue;
00417 
00418                 eu = vu->up.eu_p;
00419                 NMG_CK_EDGEUSE( eu );
00420 
00421                 if( rt_g.NMG_debug & DEBUG_BASIC )
00422                 {
00423                         bu_log( "Checking edge (%g %g %g)<->(%g %g %g)\n",
00424                                 V3ARGS( eu->vu_p->v_p->vg_p->coord ),
00425                                 V3ARGS( eu->eumate_p->vu_p->v_p->vg_p->coord ) );
00426                 }
00427 
00428                 /* skip wire edges */
00429                 if( *eu->up.magic_p != NMG_LOOPUSE_MAGIC )
00430                         continue;
00431 
00432                 /* skip wire loops */
00433                 if( *eu->up.lu_p->up.magic_p != NMG_FACEUSE_MAGIC )
00434                         continue;
00435 
00436                 /* skip finished faces */
00437                 if( NMG_INDEX_TEST( flags , eu->up.lu_p->up.fu_p->f_p ) )
00438                         continue;
00439 
00440                 /* skip edges from other shells */
00441                 if( nmg_find_s_of_eu( eu ) != s )
00442                         continue;
00443 
00444                 /* skip zero length edges */
00445                 if( eu->eumate_p->vu_p->v_p == vp_top )
00446                         continue;
00447 
00448                 /* get vertex at other end of this edge */
00449                 vu1 = eu->eumate_p->vu_p;
00450                 NMG_CK_VERTEXUSE( vu1 );
00451 
00452                 /* make a unit vector in direction of edgeuse */
00453                 VSUB2( edge , vu1->v_p->vg_p->coord , vu->v_p->vg_p->coord );
00454                 VUNITIZE( edge );
00455 
00456                 if( rt_g.NMG_debug & DEBUG_BASIC )
00457                 {
00458                         bu_log( "Checking edge (%g %g %g)<->(%g %g %g)\n",
00459                                 V3ARGS( eu->vu_p->v_p->vg_p->coord ),
00460                                 V3ARGS( eu->eumate_p->vu_p->v_p->vg_p->coord ) );
00461                         bu_log( "\tedge direction = (%g %g %g)\n", V3ARGS( edge ) );
00462                         bu_log( "\t\textreme slope = %g\n", extreme_slope );
00463                 }
00464 
00465                 /* check against current maximum slope */
00466                 if( bottommost )
00467                 {
00468                         if( edge[dir] < extreme_slope )
00469                         {
00470                                 extreme_slope = edge[dir];
00471                                 e_top = eu->e_p;
00472                         }
00473                 }
00474                 else
00475                 {
00476                         if( edge[dir] > extreme_slope )
00477                         {
00478                                 if( rt_g.NMG_debug & DEBUG_BASIC )
00479                                         bu_log( "New top edge!\n" );
00480                                 extreme_slope = edge[dir];
00481                                 e_top = eu->e_p;
00482                         }
00483                 }
00484         }
00485         if( e_top == (struct edge *)NULL )
00486         {
00487                 bu_log( "Fix_normals: Could not find uppermost edge" );
00488                 return( (struct face *)NULL );
00489         }
00490 
00491         eu = e_top->eu_p;
00492 
00493 #if 0
00494         /* if the top edge is a free edge, don't use it */
00495         if( eu->eumate_p == eu->radial_p )
00496                 return( (struct face *)NULL );
00497 #endif
00498         v1 = eu->vu_p->v_p;
00499         NMG_CK_VERTEX( v1 );
00500         v2 = eu->eumate_p->vu_p->v_p;
00501         NMG_CK_VERTEX( v2 );
00502 
00503         if( rt_g.NMG_debug & DEBUG_BASIC )
00504                 bu_log( "top EU is x%x (%g %g %g) <-> (%g %g %g)\n",
00505                         eu, V3ARGS( v1->vg_p->coord ),
00506                         V3ARGS( v2->vg_p->coord ) );
00507 
00508         /* now find the face containing edge between v1 nad v2
00509          with "left-pointing vector" having the most extreme slope */
00510         if( bottommost )
00511                 extreme_slope = MAX_FASTF;
00512         else
00513                 extreme_slope = (-MAX_FASTF);
00514 
00515         for( BU_LIST_FOR( vu, vertexuse, &v1->vu_hd ) )
00516         {
00517                 vect_t left;
00518                 vect_t edge_dir;
00519 
00520                 NMG_CK_VERTEXUSE( vu );
00521                 if( *vu->up.magic_p != NMG_EDGEUSE_MAGIC )
00522                         continue;
00523 
00524                 eu1 = vu->up.eu_p;
00525                 NMG_CK_EDGEUSE( eu1 );
00526 
00527                 /* don't bother with anything but faces */
00528                 if( *eu1->up.magic_p != NMG_LOOPUSE_MAGIC )
00529                         continue;
00530 
00531                 /* skip edges not between correct vertices */
00532                 if( eu1->eumate_p->vu_p->v_p != v2 )
00533                         continue;
00534 
00535                 lu = eu1->up.lu_p;
00536                 NMG_CK_LOOPUSE( lu );
00537                 if( *lu->up.magic_p != NMG_FACEUSE_MAGIC )
00538                         continue;
00539 
00540                 /* fu is a faceuse containing "eu1" */
00541                 fu = lu->up.fu_p;
00542                 NMG_CK_FACEUSE( fu );
00543 
00544                 /* skip faces from other shells and flagged faceuses */
00545                 if( fu->s_p != s || NMG_INDEX_TEST( flags, fu->f_p ) )
00546                         continue;
00547 
00548                 /* make a vector in the direction of "eu1" */
00549                 if( rt_g.NMG_debug & DEBUG_BASIC )
00550                         bu_log( "test EU is x%x (%g %g %g) <-> (%g %g %g)\n",
00551                                 eu, V3ARGS( eu->vu_p->v_p->vg_p->coord ),
00552                                 V3ARGS( eu->eumate_p->vu_p->v_p->vg_p->coord ) );
00553 
00554                 VSUB2( edge_dir , eu1->eumate_p->vu_p->v_p->vg_p->coord , eu1->vu_p->v_p->vg_p->coord );
00555 
00556                 if( rt_g.NMG_debug & DEBUG_BASIC )
00557                         bu_log( "edge_dir is ( %g %g %g )\n", V3ARGS( edge_dir ) );
00558 
00559                 /* find the normal for this faceuse */
00560                 if( *fu->f_p->g.magic_p == NMG_FACE_G_PLANE_MAGIC )
00561                         NMG_GET_FU_NORMAL( normal, fu )
00562                 else if( *fu->f_p->g.magic_p == NMG_FACE_G_SNURB_MAGIC )
00563                         nmg_snurb_fu_get_norm_at_vu( fu, eu1->vu_p, normal );
00564 
00565                 if( rt_g.NMG_debug & DEBUG_BASIC )
00566                         bu_log( "fu normal is ( %g %g %g )\n" , V3ARGS( normal ) );
00567 
00568                 /* normal cross edge direction gives vector in face */
00569                 VCROSS( left , normal , edge_dir );
00570 
00571                 /* unitize to get slope */
00572                 VUNITIZE( left );
00573                 if( rt_g.NMG_debug & DEBUG_BASIC )
00574                 {
00575                         bu_log( "left vector is ( %g %g %g )\n", V3ARGS( left ) );
00576                         bu_log( "\textreme slope in %d direction is %g\n", dir, extreme_slope );
00577                 }
00578 
00579                 /* check against current most extreme slope */
00580                 if( bottommost )
00581                 {
00582                         if( left[dir] < extreme_slope )
00583                         {
00584                                 extreme_slope = left[dir];
00585                                 f_top = fu->f_p;
00586                         }
00587                 }
00588                 else
00589                 {
00590                         if( left[dir] > extreme_slope )
00591                         {
00592                                 if( rt_g.NMG_debug & DEBUG_BASIC )
00593                                         bu_log( "new f_top\n" );
00594                                 extreme_slope = left[dir];
00595                                 f_top = fu->f_p;
00596                         }
00597                 }
00598         }
00599 
00600         if( f_top == (struct face *)NULL )
00601         {
00602                 bu_log( "Nmg_find_top_face_in_dir: Could not find uppermost face" );
00603                 return( (struct face *)NULL );
00604         }
00605 
00606         if( rt_g.NMG_debug & DEBUG_BASIC )
00607                 bu_log( "nmg_find_top_face_in_dir: top face = x%x, dir = %d, top vertex = x%x ( %g %g %g )\n",
00608                         f_top, dir, vp_top, V3ARGS( vp_top->vg_p->coord ) );
00609 
00610         return( f_top );
00611 }
00612 
00613 /**
00614  *      N M G _ F I N D _ T O P _ F A C E
00615  *
00616  *      Finds the topmost face in a shell (in some direction).
00617  *      Expects to have a translation table (variable "flags") for
00618  *      the model, and will ignore face structures that have their
00619  *      flag set in the table.
00620  *
00621  * returns the top face in some direction.
00622  *
00623  * dir will be set to X, Y, or Z to indicate which top face was found.
00624  */
00625 
00626 struct face *
00627 nmg_find_top_face(const struct shell *s, int *dir, long int *flags)
00628 {
00629         struct face *top_face;
00630 
00631         for( *dir=X ; *dir<=Z ; (*dir)++ )
00632                 if( (top_face=nmg_find_top_face_in_dir( s, *dir, flags )) != (struct face *)NULL )
00633                         return( top_face );
00634 
00635         /* give up!! */
00636         bu_log( "Nmg_find_top_face: Cannot find a top face\n" );
00637         *dir = (-32000); /* will hopefully cause an error if used */
00638         return( (struct face *)NULL );
00639 
00640 }
00641 
00642 /**     N M G _ A S S O C _ V O I D _ S H E L L S
00643  *
00644  * Passed an bu_ptbl structure containing one shell, this routine
00645  * examines the other shells in the region to determine if any are void shells
00646  * within the shell on the bu_ptbl list. Any such void shells found are added
00647  * to the bu_ptbl list. The final result is a ptbl list of shells where the
00648  * first shell on the list is the outer shell, and any additional shells one
00649  * the list are void shells within that outer shell. This is a support routine
00650  * for "nmg_find_outer_and_void_shells" and gets called for every outer shell
00651  * in the region
00652  */
00653 struct top_face
00654 {
00655         struct shell *s;
00656         struct face *f;
00657         int dir;
00658         vect_t normal;
00659 };
00660 
00661 static void
00662 nmg_assoc_void_shells(const struct nmgregion *r, struct bu_ptbl *shells, const struct bn_tol *ttol)
00663 {
00664         struct shell *outer_shell,*void_s,*s;
00665         struct faceuse *fu;
00666         struct loopuse *lu;
00667         struct edgeuse *eu;
00668         long *flags;
00669         struct top_face *top_faces;
00670         int total_shells=0;
00671         int i;
00672         int dir;
00673 
00674         NMG_CK_REGION( r );
00675         BU_CK_PTBL( shells );
00676         BN_CK_TOL( ttol );
00677 
00678         outer_shell = (struct shell *)BU_PTBL_GET( shells , 0 );
00679         NMG_CK_SHELL( outer_shell );
00680 
00681         /* count shells in region */
00682         for( BU_LIST_FOR( s , shell , &r->s_hd ) )
00683                 total_shells++;
00684 
00685         /* make an array of shells and top faces */
00686         top_faces = (struct top_face *)bu_calloc( total_shells , sizeof( struct top_face ) , "nmg_assoc_void_shells: top_faces" );
00687 
00688         /* make flags array for use by "nmg_find_top_face" */
00689         flags = (long *)bu_calloc( r->m_p->maxindex , sizeof( long ) , "nmg_find_outer_and_void_shells: flags" );
00690 
00691         top_faces[0].s = outer_shell;
00692         top_faces[0].f = nmg_find_top_face( outer_shell, &dir , flags );
00693         top_faces[0].dir = dir;
00694         fu = top_faces[0].f->fu_p;
00695         if( fu->orientation != OT_SAME )
00696                 fu = fu->fumate_p;
00697         NMG_GET_FU_NORMAL( top_faces[0].normal , fu );
00698 
00699         /* fill in top_faces array */
00700         i = 0;
00701         for( BU_LIST_FOR( s , shell , &r->s_hd ) )
00702         {
00703                 if( s == outer_shell )
00704                         continue;
00705 
00706                 top_faces[++i].s = s;
00707                 top_faces[i].f = nmg_find_top_face( s, &dir , flags );
00708                 top_faces[i].dir = dir;
00709                 if( top_faces[i].f == (struct face *)NULL )
00710                         bu_log( "WARNING: nmg_assoc_void_shells() could not find top face for shell x%x\n", s );
00711                 else
00712                 {
00713                         fu = top_faces[i].f->fu_p;
00714                         if( fu->orientation != OT_SAME )
00715                                 fu = fu->fumate_p;
00716                         NMG_GET_FU_NORMAL( top_faces[i].normal , fu );
00717                 }
00718         }
00719 
00720         /* look for voids */
00721         for( BU_LIST_FOR( void_s , shell , &r->s_hd ) )
00722         {
00723                 struct face *void_f;
00724                 int wrong_void=0;
00725                 vect_t normal;
00726 
00727                 if( void_s == outer_shell )
00728                         continue;
00729 
00730                 NMG_CK_SHELL( void_s );
00731 
00732                 void_f = (struct face *)NULL;
00733                 for( i=0 ; i<total_shells ; i++ )
00734                 {
00735                         if( top_faces[i].s == void_s )
00736                         {
00737                                 void_f = top_faces[i].f;
00738                                 dir = top_faces[i].dir;
00739                                 VMOVE( normal , top_faces[i].normal );
00740                                 break;
00741                         }
00742                 }
00743                 if( void_f == (struct face *)NULL )
00744                         rt_bomb( "nmg_assoc_void_shells: no top face for a shell\n" );
00745 
00746                 if( normal[dir] < 0.0)
00747                 {
00748                         /* this is a void shell */
00749                         struct face *int_f;
00750                         struct shell *test_s;
00751                         int breakout=0;
00752                         int not_in_this_shell=0;
00753 
00754                         /* this is a void shell
00755                          * but does it belong with outer_shell */
00756                         if( !V3RPP1_IN_RPP2( void_s->sa_p->min_pt , void_s->sa_p->max_pt , outer_shell->sa_p->min_pt , outer_shell->sa_p->max_pt ) )
00757                         {
00758                                 continue;
00759                         }
00760 
00761                         for( BU_LIST_FOR( fu , faceuse , &void_s->fu_hd ) )
00762                         {
00763                                 for( BU_LIST_FOR( lu , loopuse , &fu->lu_hd ) )
00764                                 {
00765                                         if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) != NMG_EDGEUSE_MAGIC )
00766                                                 continue;
00767                                         for( BU_LIST_FOR( eu , edgeuse , &lu->down_hd ) )
00768                                         {
00769                                                 int class;
00770 
00771                                                 class = nmg_class_pt_s( eu->vu_p->v_p->vg_p->coord , outer_shell, 0 , ttol );
00772 
00773                                                 if( class == NMG_CLASS_AoutB )
00774                                                 {
00775                                                         breakout = 1;
00776                                                         not_in_this_shell = 1;
00777                                                         break;
00778                                                 }
00779                                         }
00780                                         if( breakout )
00781                                                 break;
00782                                 }
00783                                 if( breakout )
00784                                         break;
00785                         }
00786 
00787                         if( not_in_this_shell )
00788                                 continue;
00789 
00790                         int_f = (struct face *)NULL;
00791                         for( i=0 ; i<total_shells ; i++ )
00792                         {
00793                                 if( top_faces[i].s == void_s )
00794                                 {
00795                                         int_f = top_faces[i].f;
00796                                         break;
00797                                 }
00798                         }
00799                         if( int_f == (struct face *)NULL )
00800                                 rt_bomb( "nmg_assoc_void_shells: no top face for a shell\n" );
00801 
00802                         /* Make sure there are no other external shells between these two */
00803                         for( BU_LIST_FOR( test_s , shell , &r->s_hd ) )
00804                         {
00805                                 vect_t test_norm;
00806                                 struct face *test_f;
00807                                 int test_dir = 0;
00808 
00809                                 /* don't check against the outer shell or the candidate void shell */
00810                                 if( test_s == void_s || test_s == outer_shell )
00811                                         continue;
00812 
00813                                 /* find top face for the test shell */
00814                                 test_f = (struct face *)NULL;
00815                                 for( i=0 ; i<total_shells ; i++ )
00816                                 {
00817                                         if( top_faces[i].s == test_s )
00818                                         {
00819                                                 test_f = top_faces[i].f;
00820                                                 test_dir = top_faces[i].dir;
00821                                                 VMOVE( test_norm , top_faces[i].normal );
00822                                                 break;
00823                                         }
00824                                 }
00825                                 if( test_f == (struct face *)NULL )
00826                                         rt_bomb( "nmg_assoc_void_shells: no top face for a shell\n" );
00827 
00828                                 /* skip test shells that are void shells */
00829                                 if( test_norm[test_dir] < 0.0)
00830                                         continue;
00831 
00832                                 /* if the void shell is not within the test shell, continue */
00833                                 if( !V3RPP1_IN_RPP2( void_s->sa_p->min_pt , void_s->sa_p->max_pt , test_s->sa_p->min_pt , test_s->sa_p->max_pt ) )
00834                                         continue;
00835 
00836                                 /* the void shell may be within this shell */
00837                                 /* XXXX Need code here to check if candidate void shell (void_s)
00838                                  * XXXX is within test shell (test_s) and test shell is
00839                                  * XXXX is within outer shell (outer_shell)
00840                                 if( void_s in test_s and test_s in outer_shell )
00841                                 {
00842                                         wrong_void = 1;
00843                                         break;
00844                                 }
00845                                 */
00846                         }
00847                         if( wrong_void )
00848                         {
00849                                 continue;
00850                         }
00851 
00852                         /* This void shell belongs with shell outer_s
00853                          * add it to the list of shells */
00854                         bu_ptbl_ins( shells , (long *)void_s );
00855                 }
00856         }
00857         bu_free( (char *)flags , "nmg_assoc_void_shells: flags" );
00858 }
00859 
00860 /**     N M G _ F I N D _ O U T E R _ A N D _ V O I D _ S H E L L S
00861  *
00862  * This routine takes a region and constructs an array of bu_ptbl lists.
00863  * A list is created for each outer shell, and that shell is the first item
00864  * on the list. Additional shells on any list are void shells within that
00865  * lists outer shell. This routine calls "nmg_decompose_shell" for every
00866  * shell in the region, so the original region topology may be changed
00867  * to accomplish this. No geometry is altered.
00868  */
00869 int
00870 nmg_find_outer_and_void_shells(struct nmgregion *r, struct bu_ptbl ***shells, const struct bn_tol *tol)
00871 {
00872         struct bu_ptbl *outer_shells;
00873         struct shell *s;
00874         int i;
00875         int total_shells=0;
00876         int outer_shell_count;
00877         int re_bound=0;
00878         int dir;
00879         long *flags;
00880 
00881         NMG_CK_REGION( r );
00882         BN_CK_TOL( tol );
00883 
00884         /* Decompose shells */
00885         outer_shells = (struct bu_ptbl *)bu_malloc( sizeof( struct bu_ptbl ) , "nmg_find_outer_and_void_shells: outer_shells" );
00886         bu_ptbl_init( outer_shells , 64, " outer_shells ");
00887         for (BU_LIST_FOR(s, shell, &r->s_hd))
00888         {
00889                 NMG_CK_SHELL( s );
00890                 bu_ptbl_ins( outer_shells , (long *)s );
00891         }
00892         for( i=0 ; i<BU_PTBL_END( outer_shells ) ; i++ )
00893         {
00894                 s = (struct shell *)BU_PTBL_GET( outer_shells , i );
00895                 if( nmg_decompose_shell( s , tol ) > 1 )
00896                         re_bound = 1;
00897         }
00898         bu_ptbl_reset( outer_shells );
00899 
00900         if( re_bound )
00901                 nmg_region_a( r , tol );
00902 
00903         for (BU_LIST_FOR(s, shell, &r->s_hd))
00904                 total_shells++;
00905 
00906         flags = (long *)bu_calloc( r->m_p->maxindex , sizeof( long ) , "nmg_find_outer_and_void_shells: flags" );
00907 
00908         for( BU_LIST_FOR( s , shell , &r->s_hd ) )
00909         {
00910                 struct face *f;
00911                 struct faceuse *fu;
00912                 vect_t normal;
00913 
00914                 f = (struct face *)NULL;
00915                 for( dir = X ; dir <= Z ; dir++ ) {
00916                         if( (f = nmg_find_top_face_in_dir( s, dir , flags )) == (struct face *)NULL )
00917                                 continue;
00918 
00919                         fu = f->fu_p;
00920                         if( fu->orientation != OT_SAME )
00921                                 fu = fu->fumate_p;
00922                         if( fu->orientation != OT_SAME )
00923                                 rt_bomb( "nmg_find_outer_and_void_shells: Neither faceuse nor mate have OT_SAME orient\n" );
00924 
00925                         NMG_GET_FU_NORMAL( normal , fu );
00926                         if( normal[dir] >= 0.0) {
00927                                 bu_ptbl_ins( outer_shells , (long *)s );        /* outer shell */
00928                                 break;
00929                         }
00930                 }
00931 
00932                 if( f == (struct face *)NULL ) {
00933                         bu_bomb( "nmg_find_outer_and_void_shells: cannot find top face in a shell\n" );
00934                 }
00935         }
00936 
00937         /* outer_shells is now a list of all the outer shells in the region */
00938         outer_shell_count = BU_PTBL_END( outer_shells );
00939 
00940         *shells = (struct bu_ptbl **)bu_calloc( BU_PTBL_END( outer_shells ) , sizeof( struct bu_ptbl *) ,
00941                         "nmg_find_outer_and_void_shells: shells" );
00942         for( i=0 ; i<BU_PTBL_END( outer_shells ) ; i++ )
00943         {
00944                 (*shells)[i] = (struct bu_ptbl *)bu_malloc( sizeof( struct bu_ptbl ) ,
00945                         "nmg_find_outer_and_void_shells: shells[]" );
00946 
00947                 bu_ptbl_init( (*shells)[i] , 64, "(*shells)[i]");
00948                 BU_CK_PTBL( (*shells)[i] );
00949                 bu_ptbl_ins( (*shells)[i] , BU_PTBL_GET( outer_shells , i ) );
00950                 if( outer_shell_count != total_shells ) /* must be some void shells */
00951                         nmg_assoc_void_shells( r , (*shells)[i] , tol );
00952         }
00953 
00954         bu_free( (char *)flags , "nmg_find_outer_and_void_shells: flags" );
00955         bu_ptbl_free( outer_shells );
00956         return( outer_shell_count );
00957 }
00958 
00959 /**     N M G _ M A R K _ E D G E S _ R E A L
00960  *
00961  * Sets the "is_real" flag on all edges at or below the
00962  * pointer passed. Returns the number of flags set.
00963  */
00964 int
00965 nmg_mark_edges_real(const long int *magic_p)
00966 {
00967         struct bu_ptbl edges;
00968         int i,count;
00969 
00970         nmg_edge_tabulate( &edges , magic_p );
00971 
00972         count = BU_PTBL_END( &edges );
00973         for( i=0 ; i<count ; i++ )
00974         {
00975                 struct edge *e;
00976 
00977                 e = (struct edge *)BU_PTBL_GET( &edges , i );
00978                 NMG_CK_EDGE( e );
00979 
00980                 e->is_real = 1;
00981         }
00982 
00983         bu_ptbl_free( &edges );
00984 
00985         return( count );
00986 }
00987 
00988 /**     N M G _ T A B U L A T E _ F A C E _ G _ V E R T S
00989  *
00990  * Tabulates all vertices in faces that use fg
00991  */
00992 
00993 void
00994 nmg_tabulate_face_g_verts(struct bu_ptbl *tab, const struct face_g_plane *fg)
00995 {
00996         struct face *f;
00997 
00998         NMG_CK_FACE_G_PLANE( fg );
00999 
01000         bu_ptbl_init( tab, 64, " tab");
01001 
01002         /* loop through all faces using fg */
01003         for( BU_LIST_FOR( f , face , &fg->f_hd ) )
01004         {
01005                 struct faceuse *fu;
01006                 struct loopuse *lu;
01007 
01008                 NMG_CK_FACE( f );
01009 
01010                 /* get one of the two uses of this face */
01011                 fu = f->fu_p;
01012                 NMG_CK_FACEUSE( fu );
01013 
01014                 /* Visit each loop in this faceuse */
01015                 for( BU_LIST_FOR( lu , loopuse , &fu->lu_hd ) )
01016                 {
01017                         NMG_CK_LOOPUSE( lu );
01018 
01019                         /* include loops of a single vertex */
01020                         if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) == NMG_VERTEXUSE_MAGIC )
01021                         {
01022                                 struct vertexuse *vu;
01023                                 struct vertex *v;
01024 
01025                                 vu = BU_LIST_FIRST(vertexuse, &lu->down_hd);
01026                                 NMG_CK_VERTEXUSE( vu );
01027                                 v = vu->v_p;
01028                                 NMG_CK_VERTEX( v );
01029 
01030                                 /* insert vertex into table */
01031                                 bu_ptbl_ins_unique( tab , (long *)v );
01032                         }
01033                         else
01034                         {
01035                                 struct edgeuse *eu;
01036 
01037                                 /* visit each edgeuse in the loop */
01038                                 for( BU_LIST_FOR( eu , edgeuse , &lu->down_hd ) )
01039                                 {
01040                                         struct vertexuse *vu;
01041                                         struct vertex *v;
01042 
01043                                         NMG_CK_EDGEUSE( eu );
01044                                         vu = eu->vu_p;
01045                                         NMG_CK_VERTEXUSE( vu );
01046                                         v = vu->v_p;
01047                                         NMG_CK_VERTEX( v );
01048 
01049                                         /* insert vertex into table */
01050                                         bu_ptbl_ins_unique( tab , (long *)v );
01051                                 }
01052                         }
01053                 }
01054         }
01055 }
01056 
01057 /**     N M G _ I S E C T _ S H E L L _ S E L F
01058  *
01059  * Intersects all faces in a shell with all other faces in the same shell
01060  * Intended for use after extrusion
01061  *
01062  */
01063 void
01064 nmg_isect_shell_self(struct shell *s, const struct bn_tol *tol)
01065 {
01066         struct model *m;
01067         struct nmgregion *r;
01068         struct shell *s_fu;
01069         struct faceuse *fu;
01070         struct bu_ptbl fus;
01071         int fu_no;
01072         int fu2_no;
01073 
01074         NMG_CK_SHELL( s );
01075         BN_CK_TOL( tol );
01076 
01077         m = nmg_find_model( &s->l.magic );
01078         NMG_CK_MODEL( m );
01079 
01080         nmg_vmodel( m );
01081 
01082         r = s->r_p;
01083         NMG_CK_REGION( r );
01084 
01085         s_fu = nmg_msv( r );
01086         NMG_CK_SHELL( s_fu );
01087 
01088         bu_ptbl_init( &fus , 64, " &fus ");
01089 
01090         for( BU_LIST_FOR( fu , faceuse , &s->fu_hd ) )
01091         {
01092                 NMG_CK_FACEUSE( fu );
01093 
01094                 if( fu->orientation == OT_SAME )
01095                         bu_ptbl_ins( &fus , (long *)fu );
01096         }
01097 
01098         /* intersect each face with every other face in the shell */
01099         for( fu_no=0 ; fu_no < BU_PTBL_END( &fus ) ; fu_no ++ )
01100         {
01101                 struct faceuse *fu2;
01102 
01103                 fu = (struct faceuse *)BU_PTBL_GET( &fus , fu_no );
01104 
01105                 NMG_CK_FACEUSE( fu );
01106 
01107                 /* move fu to another shell to avoid radial edge problems */
01108                 nmg_mv_fu_between_shells( s_fu, s, fu );
01109 
01110                 /* consider intersection this faceuse with all the faceuses
01111                  * after it in the list
01112                  */
01113                 for( fu2_no=fu_no+1 ; fu2_no < BU_PTBL_END( &fus ) ; fu2_no++ )
01114                 {
01115                         struct face *f,*f2;
01116 
01117                         fu2 = (struct faceuse *)BU_PTBL_GET( &fus , fu2_no );
01118 
01119                         if( rt_g.NMG_debug & DEBUG_BASIC )
01120                                 bu_log( "nmg_extrude_cleanup: fu=x%x, fu2=x%x\n" , fu , fu2 );
01121 
01122                         /* skip faceuses radial to fu or not OT_SAME */
01123                         if( fu2->orientation != OT_SAME || nmg_faces_are_radial( fu , fu2 ) )
01124                                 continue;
01125 
01126                         f = fu->f_p;
01127                         f2 = fu2->f_p;
01128 
01129                         /* skip faceuse pairs that don't have overlapping BB's */
01130                         if( !V3RPP_OVERLAP( f->min_pt , f->max_pt , f2->min_pt , f2->max_pt ) )
01131                                 continue;
01132 
01133                         if( rt_g.NMG_debug & DEBUG_BASIC )
01134                                 bu_log( "nmg_extrude_cleanup: calling nmg_isect_two_generic_faces( fu=x%x , fu2=x%x )\n" , fu , fu2 );
01135 
01136                         nmg_isect_two_generic_faces( fu , fu2 , tol );
01137                 }
01138                 /* move fu back where it belongs */
01139                 while( BU_LIST_NON_EMPTY( &s_fu->fu_hd ) )
01140                 {
01141                         struct faceuse *fu_tmp;
01142 
01143                         fu_tmp = BU_LIST_FIRST( faceuse , &s_fu->fu_hd );
01144                         NMG_CK_FACEUSE( fu_tmp );
01145 
01146                         if( rt_g.NMG_debug & DEBUG_BASIC )
01147                                 bu_log( "nmg_extrude_cleanup: moving fu x%x back\n" , fu_tmp );
01148 
01149                         nmg_mv_fu_between_shells( s, s_fu, fu_tmp );
01150                 }
01151         }
01152 
01153         /* get rid of the temporary shell */
01154         nmg_ks( s_fu );
01155 
01156         bu_ptbl_free( &fus);
01157 }
01158 
01159 /**     N M G _ N E X T _ R A D I A L _ E D G E U S E
01160  *
01161  * Traverse radial edgeuse around specified edgeuse looking for
01162  * one that meets optional restrictions. If a shell is specified
01163  * only edgeuse from that shell will be considered. If wires is
01164  * non-zero, wire edges will be considered, otherwise, wire edges
01165  * are ignored.
01166  *
01167  * returns:
01168  *      radial edgeuse
01169  */
01170 
01171 struct edgeuse *
01172 nmg_next_radial_eu(const struct edgeuse *eu, const struct shell *s, const int wires)
01173 {
01174         struct edgeuse *ret_eu;
01175 
01176         NMG_CK_EDGEUSE( eu );
01177         if( s )
01178                 NMG_CK_SHELL( s );
01179 
01180         if( s && nmg_find_s_of_eu( eu ) != s )
01181                 rt_bomb( "nmg_find_radial_eu: eu is not in specified shell\n" );
01182 
01183         if( !wires && !nmg_find_fu_of_eu( eu ) )
01184                 rt_bomb( "nmg_find_radial_eu: wire edges not specified, but eu is a wire!!\n" );
01185 
01186         ret_eu = eu->eumate_p->radial_p;
01187         while(
01188               (!wires & (nmg_find_fu_of_eu(ret_eu) == (struct faceuse *)NULL))
01189               ||
01190               ( (s != (struct shell *)NULL) &&
01191                 nmg_find_s_of_eu(ret_eu) != s  )
01192               )
01193                         ret_eu = ret_eu->eumate_p->radial_p;
01194 
01195         return( ret_eu );
01196 }
01197 
01198 /**     N M G _ P R E V _ R A D I A L _ E D G E U S E
01199  *
01200  * Traverse radial edgeuse around specified edgeuse in opposite
01201  * direction from nmg_next_radial_eu, looking for
01202  * one that meets optional restrictions. If a shell is specified
01203  * only edgeuse from that shell will be considered. If wires is
01204  * non-zero, wire edges will be considered, otherwise, wire edges
01205  * are ignored.
01206  *
01207  * returns:
01208  *      radial edgeuse
01209  */
01210 
01211 struct edgeuse *
01212 nmg_prev_radial_eu(const struct edgeuse *eu, const struct shell *s, const int wires)
01213 {
01214         struct edgeuse *ret_eu;
01215 
01216         NMG_CK_EDGEUSE( eu );
01217         if( s )
01218                 NMG_CK_SHELL( s );
01219 
01220         if( s && nmg_find_s_of_eu( eu ) != s )
01221                 rt_bomb( "nmg_find_radial_eu: eu is not in specified shell\n" );
01222 
01223         if( !wires && !nmg_find_fu_of_eu( eu ) )
01224                 rt_bomb( "nmg_find_radial_eu: wire edges not specified, but eu is a wire!!\n" );
01225 
01226         ret_eu = eu->radial_p->eumate_p;
01227         while( ( !wires & (nmg_find_fu_of_eu( ret_eu ) == (struct faceuse *)NULL)) ||
01228                 ( (s != (struct shell *)NULL) && nmg_find_s_of_eu( ret_eu ) != s  ) )
01229                         ret_eu = ret_eu->radial_p->eumate_p;
01230 
01231         return( ret_eu );
01232 }
01233 
01234 /**     N M G _ R A D I A L _ F A C E _ C O U N T
01235  *
01236  * Counts the number of faces (actually, the number of radial edgeuse/mate pairs)
01237  * around eu. If s is specified, only edgeuses in shell s are counted. Wire
01238  * edgeuses are not counted.
01239  *
01240  * returns:
01241  *      number of edgeuse/mate pairs radiall around eu that meet restrictions
01242  */
01243 int
01244 nmg_radial_face_count(const struct edgeuse *eu, const struct shell *s)
01245 {
01246         int face_count=1;
01247         struct edgeuse *eu1;
01248 
01249         NMG_CK_EDGEUSE( eu );
01250         if( s )
01251                 NMG_CK_SHELL( s );
01252 
01253         /* count radial faces on this edge */
01254         eu1 = eu->eumate_p->radial_p;
01255         while( eu1 != eu && eu1 != eu->eumate_p )
01256         {
01257                 /* ignore other shells and don't count wires */
01258                 if( (!s || nmg_find_s_of_eu( eu1 ) == s) &&
01259                         nmg_find_fu_of_eu( eu1 ) != (struct faceuse *)NULL )
01260                                 face_count++;
01261                 eu1 = eu1->eumate_p->radial_p;
01262         }
01263 
01264         return( face_count );
01265 }
01266 
01267 /**     N M G _ C H E C K _ C L O S E D _ S H E L L
01268  *
01269  *      Looks at every eu in OT_SAME fu's. If any eu
01270  *      has no radials, then it must be the edge of a
01271  *      dangling face and therfore the edge of an opening.
01272  *
01273  *  returns:
01274  *      0 - O.K.
01275  *      1 - found a hole
01276  */
01277 int
01278 nmg_check_closed_shell(const struct shell *s, const struct bn_tol *tol)
01279 {
01280         struct faceuse *fu;
01281 
01282         NMG_CK_SHELL( s );
01283         BN_CK_TOL( tol );
01284 
01285         for( BU_LIST_FOR( fu , faceuse , &s->fu_hd ) )
01286         {
01287                 struct loopuse *lu;
01288 
01289                 NMG_CK_FACEUSE( fu );
01290 
01291                 if( fu->orientation != OT_SAME )
01292                         continue;
01293 
01294                 for( BU_LIST_FOR( lu , loopuse , &fu->lu_hd ) )
01295                 {
01296                         struct edgeuse *eu;
01297 
01298                         NMG_CK_LOOPUSE( lu );
01299 
01300                         if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) != NMG_EDGEUSE_MAGIC )
01301                                 continue;
01302 
01303                         for( BU_LIST_FOR( eu , edgeuse , &lu->down_hd ) )
01304                         {
01305                                 struct edgeuse *next_eu;
01306 
01307                                 next_eu = nmg_next_radial_eu( eu, s , 0 );
01308                                 if( next_eu == eu || next_eu == eu->eumate_p )
01309                                         return( 1 );
01310                         }
01311                 }
01312         }
01313 
01314         return( 0 );
01315 }
01316 
01317 /**     N M G _ M O V E _ L U _ B E T W E E N _ F U S
01318  *
01319  * Moves lu from src faceuse to dest faceuse
01320  *
01321  * returns:
01322  *      0 - All is well
01323  *      1 - src faceuse is now empty
01324  */
01325 int
01326 nmg_move_lu_between_fus(struct faceuse *dest, struct faceuse *src, struct loopuse *lu)
01327 {
01328         struct loopuse *lumate;
01329         int src_is_empty;
01330 
01331         NMG_CK_FACEUSE( dest );
01332         NMG_CK_FACEUSE( dest->fumate_p );
01333         NMG_CK_FACEUSE( src );
01334         NMG_CK_FACEUSE( src->fumate_p );
01335         NMG_CK_LOOPUSE( lu );
01336 
01337         if( rt_g.NMG_debug & DEBUG_BASIC )
01338                 bu_log( "nmg_move_lu_between_fus( dest=x%x, src=x%x, lu=x%x)\n", dest, src, lu );
01339 
01340         if( lu->up.fu_p != src )
01341         {
01342                 bu_log( "nmg_move_lu_between_fus( dest=x%x, src=x%x, lu=x%x)\n", dest, src, lu );
01343                 rt_bomb( "\tlu is not in src faceuse\n" );
01344         }
01345 
01346         if( dest == src )
01347                 return( 0 );
01348 
01349         lumate = lu->lumate_p;
01350         NMG_CK_LOOPUSE( lumate );
01351 
01352         /* remove lu from src faceuse */
01353         BU_LIST_DEQUEUE( &lu->l );
01354         src_is_empty = BU_LIST_IS_EMPTY( &src->lu_hd );
01355 
01356         /* remove lumate from src faceuse mate */
01357         BU_LIST_DEQUEUE( &lumate->l );
01358         if( src_is_empty != BU_LIST_IS_EMPTY( &src->fumate_p->lu_hd ) )
01359         {
01360                 bu_log( "nmg_move_lu_between_fus( dest=x%x, src=x%x, lu=x%x)\n", dest, src, lu );
01361                 if( src_is_empty )
01362                         rt_bomb( "\tsrc faceuse contains only lu, but src->fumate_p has more!!\n" );
01363                 rt_bomb( "\tsrc->fumate_p faceuse contains only lu->lumate_p, but src has more!!\n" );
01364         }
01365 
01366         /* add lu to dest faceuse */
01367         BU_LIST_INSERT( &dest->lu_hd, &lu->l );
01368 
01369         /* add lumate to dest mate */
01370         BU_LIST_INSERT( &dest->fumate_p->lu_hd, &lumate->l );
01371 
01372         /* adjust up pointers */
01373         lu->up.fu_p = dest;
01374         lumate->up.fu_p = dest->fumate_p;
01375 
01376         return( src_is_empty );
01377 }
01378 
01379 /**     N M G _ L O O P _ P L A N E _ N E W E L L
01380  *
01381  *      Calculate the plane equation of a loop using Newell's Method
01382  *      (See "Graphics Gems III", David Kirk editor, Academic Press, Inc. 1992)
01383  *
01384  *      If the loop orientation is OT_OPPOSITE, the normal of the plane
01385  *      is reversed.
01386  */
01387 void
01388 nmg_loop_plane_newell(const struct loopuse *lu, fastf_t *pl)
01389 {
01390         struct edgeuse *eu;
01391         double hmin,hmax;
01392         double pl_tmp[3];
01393 
01394         NMG_CK_LOOPUSE( lu );
01395 
01396         VSETALL( pl, 0.0 );
01397         pl[H] = 0.0;
01398 
01399         VSETALL( pl_tmp, 0.0 );
01400 
01401         /* make sure we have a loop of edges */
01402         if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) != NMG_EDGEUSE_MAGIC )
01403                 return;
01404 
01405         /* check if this loop is a crack */
01406         if( nmg_loop_is_a_crack( lu ) )
01407                 return;
01408 
01409         if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) != NMG_EDGEUSE_MAGIC )
01410                 return;
01411 
01412         for( BU_LIST_FOR( eu, edgeuse, &lu->down_hd ) )
01413         {
01414                 struct edgeuse *eu_next;
01415                 struct vertex_g *vg;
01416                 struct vertex_g *vg_next;
01417 
01418                 vg = eu->vu_p->v_p->vg_p;
01419                 eu_next = BU_LIST_PNEXT_CIRC( edgeuse, &eu->l );
01420                 vg_next = eu_next->vu_p->v_p->vg_p;
01421 
01422                 pl_tmp[X] += ( vg->coord[Y] - vg_next->coord[Y]) * ( vg->coord[Z] + vg_next->coord[Z] );
01423                 pl_tmp[Y] += ( vg->coord[Z] - vg_next->coord[Z]) * ( vg->coord[X] + vg_next->coord[X] );
01424                 pl_tmp[Z] += ( vg->coord[X] - vg_next->coord[X]) * ( vg->coord[Y] + vg_next->coord[Y] );
01425         }
01426 
01427         VUNITIZE( pl_tmp );
01428         VMOVE( pl, pl_tmp );
01429 
01430         hmin = MAX_FASTF;
01431         hmax = (-hmin);
01432 
01433         for( BU_LIST_FOR( eu, edgeuse, &lu->down_hd ) )
01434         {
01435                 struct vertex_g *vg;
01436                 fastf_t htest;
01437 
01438                 vg = eu->vu_p->v_p->vg_p;
01439                 htest = VDOT( vg->coord, pl );
01440                 if( htest > hmax )
01441                         hmax = htest;
01442                 if( htest < hmin )
01443                         hmin = htest;
01444         }
01445 
01446         pl[H] = (hmax + hmin)/2.0;
01447 
01448         if( lu->orientation == OT_OPPOSITE )
01449                 HREVERSE( pl, pl );
01450 }
01451 
01452 /**     N M G _ L O O P _ P L A N E _ A R E A
01453  *
01454  *  Calculates a plane equation and the area of a loop
01455  *
01456  *  returns:
01457  *      the area of the loop
01458  *      less than zero indicates an error
01459  *
01460  *  pl is assigned the plane equation for the loop
01461  *
01462  */
01463 
01464 fastf_t
01465 nmg_loop_plane_area(const struct loopuse *lu, fastf_t *pl)
01466 {
01467         fastf_t area;
01468         fastf_t vect_mag;
01469         fastf_t vect_mag_inv;
01470         fastf_t pt_dot_plane=0.0;
01471         fastf_t pt_count=0.0;
01472         plane_t plane;
01473         struct edgeuse *eu;
01474         vect_t trans;
01475 
01476         NMG_CK_LOOPUSE( lu );
01477 
01478         /* make sure we have a loop of edges */
01479         if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) != NMG_EDGEUSE_MAGIC )
01480                 return( (fastf_t)(-1.0) );
01481 
01482         /* check if this loop is a crack */
01483         if( nmg_loop_is_a_crack( lu ) )
01484                 return( (fastf_t)(-1.0) );
01485 
01486         /* calculate a translation to put one vertex at the origin
01487          * not necessary, but good for accuracy.
01488          * Also, origin must be in plane of loop for this
01489          * method to work
01490          */
01491         eu = BU_LIST_FIRST( edgeuse , &lu->down_hd );
01492         NMG_CK_VERTEXUSE( eu->vu_p );
01493         NMG_CK_VERTEX( eu->vu_p->v_p );
01494         NMG_CK_VERTEX_G( eu->vu_p->v_p->vg_p );
01495         VMOVE( trans , eu->vu_p->v_p->vg_p->coord );
01496 
01497         VSET( plane , 0.0 , 0.0 , 0.0 );
01498 
01499         /* Calculate area and plane normal.
01500          * Cross product of each pair of vertices gives twice
01501          * the area of the triangle formed by the origin and
01502          * the two vertices. (positive if counter-clockwise,
01503          * negative if clockwise). In counter_clockwise case,
01504          * sum of all cross products around loop adds area for
01505          * edges away from origin and subtracts area for edges
01506          * near origin, leaving twice the area of the polygon.
01507          */
01508         for( BU_LIST_FOR( eu , edgeuse , &lu->down_hd ) )
01509         {
01510                 struct edgeuse *next_eu;
01511                 struct vertex *vp,*next_vp;
01512                 vect_t cross;
01513                 point_t p1,p2;
01514 
01515                 vp = eu->vu_p->v_p;
01516 
01517                 next_eu = BU_LIST_PNEXT_CIRC( edgeuse , &eu->l );
01518                 NMG_CK_EDGEUSE( next_eu );
01519                 NMG_CK_VERTEXUSE( next_eu->vu_p );
01520 
01521                 next_vp = next_eu->vu_p->v_p;
01522                 NMG_CK_VERTEX( next_vp );
01523                 NMG_CK_VERTEX_G( next_vp->vg_p );
01524 
01525                 VSUB2( p1 , vp->vg_p->coord , trans );
01526                 VSUB2( p2 , next_vp->vg_p->coord , trans );
01527                 VCROSS( cross , p1 , p2 );
01528                 VADD2( plane , plane , cross );
01529         }
01530 
01531         vect_mag = MAGNITUDE( plane );
01532 
01533         /* Error if the area is too small to unitize the normal vector */
01534         if( vect_mag < SMALL_FASTF )
01535                 return( (fastf_t)(-1.0) );
01536 
01537         area = 0.5 * vect_mag;
01538         vect_mag_inv = 1.0/vect_mag;
01539 
01540         VSCALE( plane , plane , vect_mag_inv );
01541 
01542         /* calculate plane[3] as average distance to plane */
01543         for( BU_LIST_FOR( eu , edgeuse , &lu->down_hd ) )
01544         {
01545                 pt_dot_plane += VDOT( plane , eu->vu_p->v_p->vg_p->coord );
01546                 pt_count++;
01547         }
01548 
01549         /* Error if we don't have at least 3 vertices in this loop */
01550         if( pt_count < 3 )
01551                 return( (fastf_t)(-1.0) );
01552 
01553         plane[3] = pt_dot_plane/pt_count;
01554         HMOVE( pl , plane );
01555 
01556         return( area );
01557 }
01558 
01559 /**     N M G _ C A L C _ F A C E _ P L A N E
01560  *
01561  * Calculate face geometry using a least squares fit or Newell's method.
01562  *
01563  * If fu does not already have a face_g_plane associated, only
01564  * vertices in fu will participate, and if it has only one loop
01565  * Newell's method will be used rather than a least square fit.
01566  *
01567  * if fu has a face_g_plane, then all vertices in any face that
01568  * references the same face_g_plane will participate in the
01569  * fit for the face plane.
01570  *
01571  * Returns:
01572  *      0 - All is well
01573  *      1 - Failed to calculate face geometry
01574  *
01575  */
01576 
01577 int
01578 nmg_calc_face_plane(struct faceuse *fu_in, fastf_t *pl)
01579 {
01580         struct faceuse *fu;
01581         struct bu_ptbl verts;
01582         plane_t old_pl;
01583         struct face *f;
01584         struct face_g_plane *fg;
01585         struct loopuse *lu;
01586         mat_t matrix;
01587         mat_t inverse;
01588         fastf_t det;
01589         double one_over_vertex_count;
01590         vect_t vsum;
01591         fastf_t min_dist=MAX_FASTF;
01592         fastf_t max_dist=(-MAX_FASTF);
01593         int i;
01594         int got_dir=0;
01595         int failed=0;
01596         int loop_count=0;
01597 
01598         fu = fu_in;
01599         NMG_CK_FACEUSE( fu );
01600 
01601         /* find an OT_SAME loop to use for calculating general direction of normal */
01602         for( BU_LIST_FOR( lu , loopuse , &fu->lu_hd ) )
01603         {
01604                 if( !got_dir && BU_LIST_FIRST_MAGIC( &lu->down_hd ) == NMG_EDGEUSE_MAGIC )
01605                 {
01606                         /* get general direction for face normal */
01607                         nmg_loop_plane_newell( lu , old_pl );
01608                         if( old_pl[X] != 0.0 || old_pl[Y] != 0.0 || old_pl[Z] != 0.0 )
01609                                 got_dir = 1;
01610                 }
01611 
01612                 loop_count++;
01613         }
01614 
01615         if( !got_dir )
01616                 return( 1 );
01617 
01618         f = fu->f_p;
01619         NMG_CK_FACE( f );
01620         fg = f->g.plane_p;
01621         if( fg )
01622         {
01623                 struct face *f1;
01624 
01625                 NMG_CK_FACE_G_PLANE( fg );
01626 
01627                 /* count loops using this face geometry */
01628                 loop_count = 0;
01629                 for( BU_LIST_FOR( f1 , face, &fg->f_hd ) )
01630                 {
01631                         for( BU_LIST_FOR( lu, loopuse, &f1->fu_p->lu_hd ) )
01632                                 loop_count++;
01633                 }
01634 
01635                 /* if this face geometry only has one loop using it, just use Newell's method */
01636                 if( loop_count < 2 )
01637                 {
01638                         HMOVE( pl, old_pl );
01639                         return( 0 );
01640                 }
01641 
01642                 nmg_tabulate_face_g_verts( &verts , fg );
01643         }
01644         else            /* just use vertices from this faceuse */
01645         {
01646                 /* If this faceuse only has one loop, just use Newell's method */
01647                 if( loop_count < 2 )
01648                 {
01649                         HMOVE( pl, old_pl );
01650                         return( 0 );
01651                 }
01652 
01653                 nmg_vertex_tabulate( &verts , &fu->l.magic );
01654         }
01655 
01656         /* Get the direction for the plane normal in "old_pl".
01657          * Make sure we are dealing with OT_SAME or OT_UNSPEC faceuse.
01658          */
01659         if( fu->orientation != OT_UNSPEC )
01660         {
01661                 if( fu->orientation != OT_SAME )
01662                         fu = fu->fumate_p;
01663                 if( fu->orientation != OT_SAME )
01664                 {
01665                         bu_log( "nmg_calc_face_plane: fu x%x has no OT_SAME use\n" , fu );
01666                         bu_ptbl_free( &verts );
01667                         return( 1 );
01668                 }
01669         }
01670 
01671         /* build matrix */
01672         MAT_ZERO( matrix );
01673         VSET( vsum , 0.0 , 0.0 , 0.0 );
01674 
01675         one_over_vertex_count = 1.0/(double)(BU_PTBL_END( &verts ));
01676 
01677         for( i=0 ; i<BU_PTBL_END( &verts ) ; i++ )
01678         {
01679                 struct vertex *v;
01680                 struct vertex_g *vg;
01681 
01682                 v = (struct vertex *)BU_PTBL_GET( &verts , i );
01683                 vg = v->vg_p;
01684 
01685                 matrix[0] += vg->coord[X] * vg->coord[X];
01686                 matrix[1] += vg->coord[X] * vg->coord[Y];
01687                 matrix[2] += vg->coord[X] * vg->coord[Z];
01688                 matrix[5] += vg->coord[Y] * vg->coord[Y];
01689                 matrix[6] += vg->coord[Y] * vg->coord[Z];
01690                 matrix[10] += vg->coord[Z] * vg->coord[Z];
01691 
01692                 vsum[X] += vg->coord[X];
01693                 vsum[Y] += vg->coord[Y];
01694                 vsum[Z] += vg->coord[Z];
01695         }
01696         matrix[4] = matrix[1];
01697         matrix[8] = matrix[2];
01698         matrix[9] = matrix[6];
01699         matrix[15] = 1.0;
01700 
01701 
01702         /* Check that we don't have a singular matrix */
01703         det = bn_mat_determinant( matrix );
01704 
01705         if( !NEAR_ZERO( det , SMALL_FASTF ) )
01706         {
01707                 fastf_t inv_len_pl;
01708 
01709                 /* invert matrix */
01710                 bn_mat_inv( inverse , matrix );
01711 
01712                 /* get normal vector */
01713                 MAT4X3PNT( pl , inverse , vsum );
01714 
01715                 /* unitize direction vector */
01716                 inv_len_pl = 1.0/(MAGNITUDE( pl ));
01717                 HSCALE( pl , pl , inv_len_pl );
01718 
01719                 /* get average vertex coordinates */
01720                 VSCALE( vsum, vsum, one_over_vertex_count );
01721 
01722                 /* get distance from plane to orgin */
01723                 pl[H] = VDOT( pl , vsum );
01724 
01725                 /* make sure it points in the correct direction */
01726                 if( VDOT( pl , old_pl ) < 0.0 )
01727                         HREVERSE( pl , pl );
01728         }
01729         else
01730         {
01731                 struct vertex *v,*v0;
01732                 int x_same=1;
01733                 int y_same=1;
01734                 int z_same=1;
01735 
01736                 /* singular matrix, may occur if all vertices have the same zero
01737                  * component.
01738                  */
01739                 v0 = (struct vertex *)BU_PTBL_GET( &verts , 0 );
01740                 for( i=1 ; i<BU_PTBL_END( &verts ) ; i++ )
01741                 {
01742                         v = (struct vertex *)BU_PTBL_GET( &verts , i );
01743 
01744                         if( v->vg_p->coord[X] != v0->vg_p->coord[X] )
01745                                 x_same = 0;
01746                         if( v->vg_p->coord[Y] != v0->vg_p->coord[Y] )
01747                                 y_same = 0;
01748                         if( v->vg_p->coord[Z] != v0->vg_p->coord[Z] )
01749                                 z_same = 0;
01750 
01751                         if( !x_same && !y_same && !z_same )
01752                                 break;
01753                 }
01754 
01755                 if( x_same )
01756                 {
01757                         VSET( pl , 1.0 , 0.0 , 0.0 );
01758                 }
01759                 else if( y_same )
01760                 {
01761                         VSET( pl , 0.0 , 1.0 , 0.0 );
01762                 }
01763                 else if( z_same )
01764                 {
01765                         VSET( pl , 0.0 , 0.0 , 1.0 );
01766                 }
01767 
01768                 if( x_same || y_same || z_same )
01769                 {
01770                         /* get average vertex coordinates */
01771                         VSCALE( vsum, vsum, one_over_vertex_count );
01772 
01773                         /* get distance from plane to orgin */
01774                         pl[H] = VDOT( pl , vsum );
01775 
01776                         /* make sure it points in the correct direction */
01777                         if( VDOT( pl , old_pl ) < 0.0 )
01778                                 HREVERSE( pl , pl );
01779                 }
01780                 else
01781                 {
01782                         bu_log( "nmg_calc_face_plane: Cannot calculate plane for fu x%x\n" , fu );
01783                         nmg_pr_fu_briefly( fu , (char *)NULL );
01784                         bu_log( "%d verts\n" , BU_PTBL_END( &verts ) );
01785                         failed = 1;
01786                 }
01787         }
01788 
01789         /* make sure plane is at center of range of vertices */
01790         for( i=0 ; i<BU_PTBL_END( &verts ) ; i++ )
01791         {
01792                 struct vertex *v;
01793                 struct vertex_g *vg;
01794                 fastf_t dist;
01795 
01796                 v = (struct vertex *)BU_PTBL_GET( &verts, i );
01797                 vg = v->vg_p;
01798 
01799                 dist = DIST_PT_PLANE( vg->coord, pl );
01800                 if( dist > max_dist )
01801                         max_dist = dist;
01802                 if( dist < min_dist )
01803                         min_dist = dist;
01804         }
01805 
01806         pl[H] += (max_dist + min_dist)/2.0;
01807 
01808         bu_ptbl_free( &verts );
01809         return( failed );
01810 }
01811 
01812 /**     N M G _ C A L C _ F A C E _ G
01813  *
01814  * interface to nmg_calc_face_plane(), calls nmg_face_g with the
01815  * resulting plane
01816  *
01817  */
01818 int
01819 nmg_calc_face_g(struct faceuse *fu)
01820 {
01821         plane_t pl;
01822         int ret_val;
01823 
01824         ret_val = nmg_calc_face_plane( fu, pl );
01825 
01826         if( !ret_val )
01827                 nmg_face_g( fu, pl );
01828 
01829         return( ret_val );
01830 }
01831 
01832 /**
01833  *                      N M G _ F A C E U S E _ A R E A
01834  *
01835  *      The following routines calculate surface area of
01836  *      NMG objects. Note that this includes all surfaces,
01837  *      not just external surfaces, i.e., an NMG object consisting
01838  *      of two adjacent cubes with a coincident face will have a
01839  *      surface area of 12*s*s (s is length of one side)
01840  */
01841 
01842 fastf_t
01843 nmg_faceuse_area(const struct faceuse *fu)
01844 {
01845         struct loopuse *lu;
01846         plane_t plane;
01847         fastf_t area=0.0;
01848         fastf_t tmp_area;
01849 
01850         NMG_CK_FACEUSE( fu );
01851 
01852         for( BU_LIST_FOR( lu , loopuse , &fu->lu_hd ) )
01853         {
01854                 if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) != NMG_EDGEUSE_MAGIC )
01855                         continue;
01856 
01857                 tmp_area = nmg_loop_plane_area( lu , plane );
01858                 if( tmp_area < 0.0 )
01859                         continue;
01860 
01861                 if( lu->orientation == OT_SAME )
01862                         area += tmp_area;
01863                 else if( lu->orientation == OT_OPPOSITE )
01864                         area -= tmp_area;
01865                 else
01866                 {
01867                         bu_log( "nmg_faceuse_area: Cannot calculate area (lu with %s orientation)\n",
01868                                 nmg_orientation( lu->orientation ) );
01869                         return( (fastf_t)-1.0 );
01870                 }
01871         }
01872 
01873         return( area );
01874 }
01875 
01876 fastf_t
01877 nmg_shell_area(const struct shell *s)
01878 {
01879         fastf_t area=0.0;
01880         fastf_t tmp_area;
01881         struct faceuse *fu;
01882 
01883         NMG_CK_SHELL( s );
01884 
01885         for( BU_LIST_FOR( fu , faceuse , &s->fu_hd ) )
01886         {
01887                 if( fu->orientation != OT_SAME )
01888                         continue;
01889 
01890                 tmp_area = nmg_faceuse_area( fu );
01891                 if( tmp_area < 0.0 )
01892                         continue;
01893 
01894                 area += tmp_area;
01895         }
01896 
01897         return( area );
01898 }
01899 
01900 fastf_t
01901 nmg_region_area(const struct nmgregion *r)
01902 {
01903         struct shell *s;
01904         fastf_t area=0.0;
01905 
01906         NMG_CK_REGION( r );
01907 
01908         for( BU_LIST_FOR( s , shell , &r->s_hd ) )
01909                 area += nmg_shell_area( s );
01910 
01911         return( area );
01912 }
01913 
01914 fastf_t
01915 nmg_model_area(const struct model *m)
01916 {
01917         struct nmgregion *r;
01918         fastf_t area=0.0;
01919 
01920         NMG_CK_MODEL( m );
01921 
01922         for( BU_LIST_FOR( r , nmgregion , &m->r_hd ) )
01923                 area += nmg_region_area( r );
01924 
01925         return( area );
01926 }
01927 
01928 /**     R T _ D I S T _ L I N E 3 _ L I N E 3
01929  *
01930  *  Calculate closest approach of two lines
01931  *
01932  *  returns:
01933  *      -2 -> lines are parallel and do not intersect
01934  *      -1 -> lines are parallel and collinear
01935  *       0 -> lines intersect
01936  *       1 -> lines do not intersect
01937  *  For return values less than zero, dist is not set.
01938  *  For return valuse of 0 or 1, dist[0] is the distance
01939  *  from p1 in the d1 direction to the point of closest
01940  *  approach for that line.  Similar for the second line.
01941  *  d1 and d2 must be unit direction vectors.
01942 XXX How is this different from bn_isect_line3_line3() ?
01943 XXX Why are the calling sequences just slightly different?
01944 XXX Can we pick the better one, and get rid of the other one?
01945 XXX If not, can we document how they differ?
01946  */
01947 
01948 int
01949 rt_dist_line3_line3(fastf_t *dist, const fastf_t *p1, const fastf_t *d1, const fastf_t *p2, const fastf_t *d2, const struct bn_tol *tol)
01950 {
01951         fastf_t d1_d2;
01952         point_t a1,a2;
01953         vect_t a1_to_a2;
01954         vect_t p2_to_p1;
01955         fastf_t min_dist;
01956         fastf_t tol_dist_sq;
01957         fastf_t tol_dist;
01958 
01959         BN_CK_TOL( tol );
01960 
01961         if( tol->dist > 0.0 )
01962                 tol_dist = tol->dist;
01963         else
01964                 tol_dist = 0.005;
01965 
01966         if( tol->dist_sq > 0.0 )
01967                 tol_dist_sq = tol->dist_sq;
01968         else
01969                 tol_dist_sq = tol_dist * tol_dist;
01970 
01971         if( !NEAR_ZERO( MAGSQ( d1 ) - 1.0 , tol_dist_sq ) )
01972         {
01973                 bu_log( "rt_dist_line3_line3: non-unit length direction vector ( %f %f %f )\n" , V3ARGS( d1 ) );
01974                 rt_bomb( "rt_dist_line3_line3\n" );
01975         }
01976 
01977         if( !NEAR_ZERO( MAGSQ( d2 ) - 1.0 , tol_dist_sq ) )
01978         {
01979                 bu_log( "rt_dist_line3_line3: non-unit length direction vector ( %f %f %f )\n" , V3ARGS( d2 ) );
01980                 rt_bomb( "rt_dist_line3_line3\n" );
01981         }
01982 
01983         d1_d2 = VDOT( d1 , d2 );
01984 
01985         if( BN_VECT_ARE_PARALLEL( d1_d2 , tol ) )
01986         {
01987                 if( bn_dist_line3_pt3( p1 , d1 , p2 ) > tol_dist )
01988                         return( -2 ); /* parallel, but not collinear */
01989                 else
01990                         return( -1 ); /* parallel and collinear */
01991         }
01992 
01993         VSUB2( p2_to_p1 , p1 , p2 );
01994         dist[0] = (d1_d2 * VDOT( p2_to_p1 , d2 ) - VDOT( p2_to_p1 , d1 ))/(1.0 - d1_d2 * d1_d2 );
01995         dist[1] = dist[0] * d1_d2 + VDOT( p2_to_p1 , d2 );
01996 
01997         VJOIN1( a1 , p1 , dist[0] , d1 );
01998         VJOIN1( a2 , p2 , dist[1] , d2 );
01999 
02000         VSUB2( a1_to_a2 , a2 , a1 );
02001         min_dist = MAGNITUDE( a1_to_a2 );
02002         if( min_dist < tol_dist )
02003                 return( 0 );
02004         else
02005                 return( 1 );
02006 }
02007 
02008 /**     R T _ D I S T _ L I N E 3 _ L S E G 3
02009  *
02010  *  calculate intersection or closest approach of
02011  *  a line and a line segement.
02012  *
02013  *  returns:
02014  *      -2 -> line and line segment are parallel and collinear.
02015  *      -1 -> line and line segment are parallel, not collinear.
02016  *       0 -> intersection between points a and b.
02017  *       1 -> intersection outside a and b.
02018  *       2 -> closest approach is between a and b.
02019  *       3 -> closest approach is outside a and b.
02020  *
02021  *  dist[0] is actual distance from p in d direction to
02022  *  closest portion of segment.
02023  *  dist[1] is ratio of distance from a to b (0.0 at a, and 1.0 at b),
02024  *  dist[1] may be less than 0 or greater than 1.
02025  *  For return values less than 0, closest approach is defined as
02026  *  closest to point p.
02027  *  Direction vector, d, must be unit length.
02028 XXX This should be moved to libbn/plane.c
02029 XXX probably renamed as bn_dist_line3_line3().
02030  */
02031 
02032 int
02033 rt_dist_line3_lseg3(fastf_t *dist, const fastf_t *p, const fastf_t *d, const fastf_t *a, const fastf_t *b, const struct bn_tol *tol)
02034 {
02035         vect_t a_to_b;
02036         vect_t a_dir;
02037         fastf_t len_ab;
02038         int outside_segment;
02039         int ret;
02040 
02041         BN_CK_TOL( tol );
02042 
02043         VSUB2( a_to_b , b , a );
02044         len_ab = MAGNITUDE( a_to_b );
02045         VSCALE( a_dir , a_to_b , (1.0/len_ab) );
02046 
02047         ret = rt_dist_line3_line3( dist , p , d , a , a_dir , tol );
02048 
02049         if( ret < 0 )
02050         {
02051                 vect_t to_a,to_b;
02052                 fastf_t dist_to_a,dist_to_b;
02053 
02054                 VSUB2( to_a , a , p );
02055                 VSUB2( to_b , b , p );
02056                 dist_to_a = VDOT( to_a , d );
02057                 dist_to_b = VDOT( to_b , d );
02058 
02059                 if( dist_to_a <= dist_to_b )
02060                 {
02061                         dist[0] = dist_to_a;
02062                         dist[1] = 0.0;
02063                 }
02064                 else
02065                 {
02066                         dist[0] = dist_to_b;
02067                         dist[1] = 1.0;
02068                 }
02069                 return( ret );
02070         }
02071 
02072         if( dist[1] >= (-tol->dist) && dist[1] <= len_ab + tol->dist )
02073         {
02074                 /* intersect or closest approach between a and b */
02075                 outside_segment = 0;
02076                 dist[1] = dist[1]/len_ab;
02077                 if( dist[1] < 0.0 )
02078                         dist[1] = 0.0;
02079                 if( dist[1] > 1.0 )
02080                         dist[1] = 1.0;
02081         }
02082         else
02083         {
02084                 outside_segment = 1;
02085                 dist[1] = dist[1]/len_ab;
02086         }
02087 
02088         return( 2*ret + outside_segment );
02089 }
02090 
02091 
02092 /** N M G _ P U R G E _ U N W A N T E D _ I N T E R S E C T I O N _ P O I
02093  *  N T S
02094  *
02095  *      Make sure that the list of intersection points doesn't contain
02096  *      any vertexuses from loops whose bounding boxes don;t overlap the
02097  *      bounding box of a loop in the given faceuse.
02098  *
02099  *      This is really a special purpose routine to help the intersection
02100  *      operations of the boolean process.  The only reason it's here instead
02101  *      of nmg_inter.c is that it knows too much about the format and contents
02102  *      of an bu_ptbl structure.
02103  */
02104 void
02105 nmg_purge_unwanted_intersection_points(struct bu_ptbl *vert_list, fastf_t *mag_list, const struct faceuse *fu, const struct bn_tol *tol)
02106 {
02107         int                     i;
02108         int                     j;
02109         struct vertexuse        *vu;
02110         struct loopuse          *lu;
02111         const struct loop_g     *lg;
02112         const struct loopuse    *fu2lu;
02113         const struct loop_g     *fu2lg = (const struct loop_g *)NULL;
02114         int                     overlap = 0;
02115 
02116         NMG_CK_FACEUSE(fu);
02117         BN_CK_TOL(tol);
02118 
02119         if (rt_g.NMG_debug & DEBUG_POLYSECT)
02120         {
02121                 bu_log("nmg_purge_unwanted_intersection_points(0x%08x, 0x%08x)\n", vert_list, fu);
02122                 bu_log("\t%d vertexuses in list\n", vert_list->end );
02123         }
02124 
02125         for (i=0 ; i < vert_list->end ; i++) {
02126                 vu = (struct vertexuse *)vert_list->buffer[i];
02127                 if( vu->l.magic != NMG_VERTEXUSE_MAGIC )
02128                 {
02129                         /* vertexuse probably killed by nmg_repair_v_near_v() */
02130                         /* delete the entry from the vertex list */
02131                         for (j=i ; j < vert_list->end ; j++)
02132                         {
02133                                 vert_list->buffer[j] = vert_list->buffer[j+1];
02134                                 mag_list[j] = mag_list[j+1];
02135                         }
02136 
02137                         --(vert_list->end);
02138                         vert_list->buffer[vert_list->end] = (long *)NULL;
02139                         mag_list[vert_list->end] = MAX_FASTF;
02140                         --i;
02141                         continue;
02142                 }
02143                 NMG_CK_VERTEXUSE(vu);
02144                 lu = nmg_find_lu_of_vu( vu );
02145                 NMG_CK_LOOPUSE(lu);
02146                 lg = lu->l_p->lg_p;
02147                 NMG_CK_LOOP_G(lg);
02148 
02149                 if (rt_g.NMG_debug & DEBUG_POLYSECT) {
02150                         bu_log("vu[%d]: 0x%08x (%g %g %g) lu: 0x%08x %s\n",
02151                                 i, vu, V3ARGS(vu->v_p->vg_p->coord),
02152                                 lu, nmg_orientation(lu->orientation) );
02153                         bu_log("\tlu BBox: (%g %g %g) (%g %g %g)\n",
02154                                 V3ARGS(lg->min_pt), V3ARGS(lg->max_pt) );
02155                 }
02156                 if (lu->up.fu_p->f_p == fu->f_p)
02157                         bu_log("I'm checking against my own face?\n");
02158 
02159                 /* If the bounding box of a loop doesn't intersect the
02160                  * bounding box of a loop in the other face, it shouldn't
02161                  * be on the list of intersecting loops.
02162                  */
02163                 overlap = 0;
02164                 for (BU_LIST_FOR(fu2lu, loopuse, &fu->lu_hd )){
02165                         NMG_CK_LOOPUSE(fu2lu);
02166                         NMG_CK_LOOP(fu2lu->l_p);
02167 
02168                         switch(fu2lu->orientation)  {
02169                         case OT_BOOLPLACE:
02170                                 /*  If this loop is destined for removal
02171                                  *  by sanitize(), skip it.
02172                                  */
02173                                 continue;
02174                         case OT_UNSPEC:
02175                                 /* If this is a loop of a lone vertex,
02176                                  * it was deposited into the
02177                                  * other loop as part of an intersection
02178                                  * operation, and is quite important.
02179                                  */
02180                                 if( BU_LIST_FIRST_MAGIC(&fu2lu->down_hd) != NMG_VERTEXUSE_MAGIC )
02181                                         bu_log("nmg_purge_unwanted_intersection_points() non self-loop OT_UNSPEC vertexuse in fu2?\n");
02182                                 break;
02183                         case OT_SAME:
02184                         case OT_OPPOSITE:
02185                                 break;
02186                         default:
02187                                 bu_log("nmg_purge_unwanted_intersection_points encountered %s loop in fu2\n",
02188                                         nmg_orientation(fu2lu->orientation));
02189                                 /* Process it anyway */
02190                                 break;
02191                         }
02192 
02193                         fu2lg = fu2lu->l_p->lg_p;
02194                         NMG_CK_LOOP_G(fu2lg);
02195 
02196                         if (rt_g.NMG_debug & DEBUG_POLYSECT) {
02197                                 bu_log("\tfu2lu BBox: (%g %g %g)  (%g %g %g) %s\n",
02198                                         V3ARGS(fu2lg->min_pt), V3ARGS(fu2lg->max_pt),
02199                                         nmg_orientation(fu2lu->orientation) );
02200                         }
02201 
02202                         if (V3RPP_OVERLAP_TOL(fu2lg->min_pt, fu2lg->max_pt,
02203                             lg->min_pt, lg->max_pt, tol)) {
02204                                 overlap = 1;
02205                                 break;
02206                         }
02207                 }
02208                 if (!overlap) {
02209                         /* why is this vertexuse in the list? */
02210                         if (rt_g.NMG_debug & DEBUG_POLYSECT) {
02211                                 bu_log("nmg_purge_unwanted_intersection_points This little bugger slipped in somehow.  Deleting it from the list.\n");
02212                                 nmg_pr_vu_briefly(vu, (char *)NULL);
02213                         }
02214                         if( BU_LIST_FIRST_MAGIC(&lu->down_hd) == NMG_VERTEXUSE_MAGIC &&
02215                             lu->orientation == OT_UNSPEC )  {
02216                                 /* Drop this loop of a single vertex in sanitize() */
02217                                 if (rt_g.NMG_debug & DEBUG_POLYSECT)
02218                                         bu_log("nmg_purge_unwanted_intersection_points() remarking as OT_BOOLPLACE\n");
02219                                 lu->orientation =
02220                                   lu->lumate_p->orientation = OT_BOOLPLACE;
02221                         }
02222 
02223                         /* delete the entry from the vertex list */
02224                         for (j=i ; j < vert_list->end ; j++)
02225                         {
02226                                 vert_list->buffer[j] = vert_list->buffer[j+1];
02227                                 mag_list[j] = mag_list[j+1];
02228                         }
02229 
02230                         --(vert_list->end);
02231                         vert_list->buffer[vert_list->end] = (long *)NULL;
02232                         mag_list[vert_list->end] = MAX_FASTF;
02233                         --i;
02234                 }
02235         }
02236         if (rt_g.NMG_debug & DEBUG_POLYSECT)
02237                 bu_log("\tAt end of nmg_purge_unwanted_intersection_points, %d vertexuses in list\n",
02238                                 vert_list->end );
02239 }
02240 
02241 
02242 /**                             N M G _ I N _ O R _ R E F
02243  *
02244  *      if the given vertexuse "vu" is in the table given by "b" or if "vu"
02245  *      references a vertex which is refernced by a vertexuse in the table,
02246  *      then we return 1.  Otherwise, we return 0.
02247  */
02248 int
02249 nmg_in_or_ref(struct vertexuse *vu, struct bu_ptbl *b)
02250 {
02251         union {
02252                 struct vertexuse **vu;
02253                 long **magic_p;
02254         } p;
02255         int i;
02256 
02257         p.magic_p = b->buffer;
02258         for (i=0 ; i < b->end ; ++i) {
02259                 if (p.vu[i] && *p.magic_p[i] == NMG_VERTEXUSE_MAGIC &&
02260                     (p.vu[i] == vu || p.vu[i]->v_p == vu->v_p))
02261                         return(1);
02262         }
02263         return(0);
02264 }
02265 
02266 /**
02267  *                      N M G _ R E B O U N D
02268  *
02269  *  Re-compute all the bounding boxes in the NMG model.
02270  *  Bounding boxes are presently found in these structures:
02271  *      loop_g
02272  *      face_g
02273  *      shell_a
02274  *      nmgregion_a
02275  *  The re-bounding must be performed in a bottom-up manner,
02276  *  computing the loops first, and working up to the nmgregions.
02277  */
02278 void
02279 nmg_rebound(struct model *m, const struct bn_tol *tol)
02280 {
02281         struct nmgregion        *r;
02282         struct shell            *s;
02283         struct faceuse          *fu;
02284         struct face             *f;
02285         struct loopuse          *lu;
02286         struct loop             *l;
02287         register int            *flags;
02288 
02289         NMG_CK_MODEL(m);
02290         BN_CK_TOL(tol);
02291 
02292         flags = (int *)bu_calloc( m->maxindex*2, sizeof(int), "rebound flags[]" );
02293 
02294         for( BU_LIST_FOR( r, nmgregion, &m->r_hd ) )  {
02295                 NMG_CK_REGION(r);
02296                 for( BU_LIST_FOR( s, shell, &r->s_hd ) )  {
02297                         NMG_CK_SHELL(s);
02298 
02299                         /* Loops in faces in shell */
02300                         for( BU_LIST_FOR( fu, faceuse, &s->fu_hd ) )  {
02301                                 NMG_CK_FACEUSE(fu);
02302                                 /* Loops in face */
02303                                 for( BU_LIST_FOR( lu, loopuse, &fu->lu_hd ) )  {
02304                                         NMG_CK_LOOPUSE(lu);
02305                                         l = lu->l_p;
02306                                         NMG_CK_LOOP(l);
02307                                         if( NMG_INDEX_FIRST_TIME(flags,l) )
02308                                                 nmg_loop_g(l, tol);
02309                                 }
02310                         }
02311                         /* Faces in shell */
02312                         for( BU_LIST_FOR( fu, faceuse, &s->fu_hd ) )  {
02313                                 NMG_CK_FACEUSE(fu);
02314                                 f = fu->f_p;
02315                                 NMG_CK_FACE(f);
02316 
02317                                 /* Rebound the face */
02318                                 if( NMG_INDEX_FIRST_TIME(flags,f) )
02319                                         nmg_face_bb( f, tol );
02320                         }
02321 
02322                         /* Wire loops in shell */
02323                         for( BU_LIST_FOR( lu, loopuse, &s->lu_hd ) )  {
02324                                 NMG_CK_LOOPUSE(lu);
02325                                 l = lu->l_p;
02326                                 NMG_CK_LOOP(l);
02327                                 if( NMG_INDEX_FIRST_TIME(flags,l) )
02328                                         nmg_loop_g(l, tol);
02329                         }
02330 
02331                         /*
02332                          *  Rebound the shell.
02333                          *  This routine handles wire edges and lone vertices.
02334                          */
02335                         if( NMG_INDEX_FIRST_TIME(flags,s) )
02336                                 nmg_shell_a( s, tol );
02337                 }
02338 
02339                 /* Rebound the nmgregion */
02340                 nmg_region_a( r, tol );
02341         }
02342 
02343         bu_free( (char *)flags, "rebound flags[]" );
02344 }
02345 
02346 /**
02347  *                      N M G _ C O U N T _ S H E L L _ K I D S
02348  */
02349 void
02350 nmg_count_shell_kids(const struct model *m, long unsigned int *total_faces, long unsigned int *total_wires, long unsigned int *total_points)
02351 {
02352         short *tbl;
02353 
02354         const struct nmgregion *r;
02355         const struct shell *s;
02356         const struct faceuse *fu;
02357         const struct loopuse *lu;
02358         const struct edgeuse *eu;
02359 
02360         NMG_CK_MODEL(m);
02361 
02362         tbl = (short *)bu_calloc(m->maxindex+1, sizeof(char),
02363                 "face/wire/point counted table");
02364 
02365         *total_faces = *total_wires = *total_points = 0;
02366         for (BU_LIST_FOR(r, nmgregion, &m->r_hd)) {
02367                 for (BU_LIST_FOR(s, shell, &r->s_hd)) {
02368                         if (s->vu_p) {
02369                                 total_points++;
02370                                 continue;
02371                         }
02372                         for (BU_LIST_FOR(fu, faceuse, &s->fu_hd)) {
02373                                 if (NMG_INDEX_TEST_AND_SET(tbl, fu->f_p))
02374                                         (*total_faces)++;
02375                         }
02376                         for (BU_LIST_FOR(lu, loopuse, &s->lu_hd)) {
02377                                 if (NMG_INDEX_TEST_AND_SET(tbl, lu->l_p))
02378                                         (*total_wires)++;
02379                         }
02380                         for (BU_LIST_FOR(eu, edgeuse, &s->eu_hd)) {
02381                                 if (NMG_INDEX_TEST_AND_SET(tbl, eu->e_p))
02382                                         (*total_wires)++;
02383                         }
02384                 }
02385         }
02386 
02387         bu_free((char *)tbl, "face/wire/point counted table");
02388 }
02389 
02390 /**
02391  *      O R D E R _ T B L
02392  *
02393  *      private support routine for nmg_close_shell
02394  *      creates an array of indices into a table of edgeuses, ordered
02395  *      end-to-end. This may or may not create an actual loop.
02396  *
02397  *      Arguments:
02398  *      tbl is the table (provided by caller)
02399  *      index is the array of indices created by order_tbl
02400  *      tbl_size is the size of the table (provided by caller)
02401  *      loop_size is the number of edgeuses in the loop (calculated by order_tbl)
02402  */
02403 static void
02404 order_tbl(struct bu_ptbl *tbl, int start_index, int **index, int tbl_size, int *loop_size)
02405 {
02406         int i,j,k;
02407         int found;
02408         struct edgeuse *eu,*eu1;
02409         struct vertex *start_v;
02410 
02411         /* create an index into the table, ordered to create a loop */
02412         if( *index == NULL )
02413                 (*index) = (int *)bu_calloc( tbl_size , sizeof( int ) , "Table index" );
02414 
02415         for( i=0 ; i<tbl_size ; i++ )
02416                 (*index)[i] = (-1);
02417 
02418         /* start the loop at index = start_index */
02419         (*index)[0] = start_index;
02420         *loop_size = 1;
02421         eu = (struct edgeuse *)BU_PTBL_GET( tbl , start_index );
02422         start_v = eu->vu_p->v_p;
02423         i = 0;
02424         found = 1;
02425         while( eu->eumate_p->vu_p->v_p != start_v && found )
02426         {
02427                 found = 0;
02428 
02429                 /* Look for edgeuse that starts where "eu" ends */
02430                 for( j=0 ; j<tbl_size ; j++ )
02431                 {
02432                         int already_used = 0;
02433 
02434                         eu1 = (struct edgeuse *)BU_PTBL_GET( tbl , j );
02435 
02436                         /* don't use same edgeuse twice!!! */
02437                         for( k=0 ; k<(*loop_size) ; k++ )
02438                         {
02439                                 if( eu1 == (struct edgeuse *)BU_PTBL_GET( tbl, (*index)[k] ) )
02440                                 {
02441                                         already_used = 1;
02442                                         break;
02443                                 }
02444                         }
02445                         if( already_used )
02446                                 continue;
02447                         if( eu1->vu_p->v_p == eu->eumate_p->vu_p->v_p )
02448                         {
02449                                 /* Found it */
02450                                 found = 1;
02451                                 (*index)[++i] = j;
02452                                 (*loop_size)++;
02453                                 eu = eu1;
02454                                 break;
02455                         }
02456                 }
02457         }
02458 }
02459 
02460 /**
02461  *      N M G _ C L O S E _ S H E L L
02462  *
02463  *      Examines the passed shell and, if there are holes, closes them
02464  *      note that not much care is taken as to how the holes are closed
02465  *      so the results are not entirely predictable.
02466  *      A list of free edges is created (edges bounding only one face).
02467  *      New faces are constructed by taking two consecutive edges
02468  *      and making a face. The newly created edge is added to the list
02469  *      of free edges and the two used ones are removed.
02470  *
02471  */
02472 void
02473 nmg_close_shell(struct shell *s, const struct bn_tol *tol)
02474 {
02475         struct bu_ptbl eu_tbl;          /* table of free edgeuses from shell */
02476         struct bu_ptbl vert_tbl;        /* table of vertices for use in nmg_cface */
02477         int *index;                     /* array of indices into eu_tbl, ordered to form a loop */
02478         int loop_size;                  /* number of edgeueses in loop */
02479         struct faceuse *fu;
02480         struct loopuse *lu;
02481         struct edgeuse *eu;
02482         int start_loop;
02483         int i;
02484         int found;
02485 
02486         if( rt_g.NMG_debug & DEBUG_BASIC )
02487                 bu_log( "nmg_close_shell: s = x%x\n" , s );
02488 
02489         NMG_CK_SHELL( s );
02490         BN_CK_TOL( tol );
02491 
02492         index = NULL;
02493 
02494         /* construct a table of free edges */
02495         (void)bu_ptbl_init( &eu_tbl , 64, " &eu_tbl ");
02496 
02497         /* loop through all the faces in the shell */
02498         for( BU_LIST_FOR( fu , faceuse , &s->fu_hd ) )
02499         {
02500                 NMG_CK_FACEUSE( fu );
02501                 /* only look at OT_SAME faces */
02502                 if( fu->orientation == OT_SAME )
02503                 {
02504                         /* loop through each loopuse in the face */
02505                         for( BU_LIST_FOR( lu , loopuse , &fu->lu_hd ) )
02506                         {
02507                                 NMG_CK_LOOPUSE( lu );
02508                                 /* ignore loops that are just a vertex */
02509                                 if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) ==
02510                                         NMG_VERTEXUSE_MAGIC )
02511                                                 continue;
02512 
02513                                 /* loop through all the edgeuses in the loop */
02514                                 for( BU_LIST_FOR( eu , edgeuse , &lu->down_hd ) )
02515                                 {
02516                                         NMG_CK_EDGEUSE( eu );
02517                                         /* if this edgeuse is a free edge, add its mate to the list */
02518                                         if( eu->radial_p == eu->eumate_p )
02519                                                 (void)bu_ptbl_ins( &eu_tbl , (long *) eu->eumate_p );
02520                                 }
02521                         }
02522                 }
02523         }
02524 
02525         /* if there is nothing in our list of free edges, the shell is already closed */
02526         if( BU_PTBL_END( &eu_tbl ) == 0 )
02527         {
02528                 bu_ptbl_free( &eu_tbl );
02529                 return;
02530         }
02531 
02532         /* create a table of vertices */
02533         (void)bu_ptbl_init( &vert_tbl , 64, " &vert_tbl ");
02534 
02535         while( BU_PTBL_END( &eu_tbl ) )
02536         {
02537                 int give_up_on_face=0;
02538 
02539                 /* Create an index into the table that orders the edgeuses into a loop */
02540                 start_loop = -1;
02541                 loop_size = 0;
02542                 while( loop_size < 2 )
02543                 {
02544                         if( ++start_loop > BU_PTBL_END( &eu_tbl ) - 3 )
02545                                 break;
02546                         order_tbl( &eu_tbl, start_loop , &index , BU_PTBL_END( &eu_tbl ) , &loop_size );
02547                 }
02548 
02549                 /* Create new faces to close the shell */
02550                 while( loop_size > 3 )
02551                 {
02552                         struct edgeuse *eu1, *eu2=NULL, *eu_new = NULL;
02553                         struct edgeuse **eu_used;       /* array of edgueses used, for deletion */
02554                         int edges_used=0;                       /* number of edges used in making a face */
02555                         int found_face=0;               /* flag - indicates that a face with the correct normal will be created */
02556                         int start_index,end_index;      /* start and stop index for loop */
02557                         int coplanar;                   /* flag - indicates entire loop is coplanar */
02558                         plane_t pl1,pl2;                /* planes for checking coplanarity of loop */
02559                         point_t pt[3];                  /* points for calculating planes */
02560 
02561                         /* Look for an easy way out, maybe this loop is planar */
02562                         /* first, calculate a plane from the first three non-collinear vertices */
02563                         start_index = 0;
02564                         end_index = start_index + 3;
02565 
02566                         for( i=start_index ; i<end_index ; i++ )
02567                         {
02568                                 eu = (struct edgeuse *)BU_PTBL_GET( &eu_tbl , index[i] );
02569                                 VMOVE( pt[i-start_index] , eu->vu_p->v_p->vg_p->coord );
02570                         }
02571                         while( bn_mk_plane_3pts( pl1 , pt[0] , pt[1] , pt[2] , tol ) && end_index<loop_size )
02572                         {
02573                                 start_index++;
02574                                 end_index++;
02575                                 for( i=start_index ; i<end_index ; i++ )
02576                                 {
02577                                         eu = (struct edgeuse *)BU_PTBL_GET( &eu_tbl , index[i] );
02578                                         VMOVE( pt[i-start_index] , eu->vu_p->v_p->vg_p->coord );
02579                                 }
02580                         }
02581                         if( end_index == loop_size )
02582                         {
02583                                 if( BU_PTBL_END( &eu_tbl ) > loop_size )
02584                                 {
02585                                         int old_loop_size=loop_size;
02586 
02587                                         loop_size = 0;
02588                                         while( loop_size < 3 )
02589                                         {
02590                                                 found = 1;
02591                                                 for( start_loop=index[0]+1 ; start_loop < BU_PTBL_END( &eu_tbl )-3 ; start_loop++ )
02592                                                 {
02593                                                         found = 0;
02594                                                         for( i=0 ; i<old_loop_size ; i++ )
02595                                                         {
02596                                                                 if( index[i] == start_loop )
02597                                                                 {
02598                                                                         found = 1;
02599                                                                         break;
02600                                                                 }
02601                                                         }
02602                                                         if( !found )
02603                                                                 break;
02604                                                 }
02605                                                 if( found )
02606                                                 {
02607                                                         /* Could not even make a plane, this is some serious screw-up */
02608                                                         bu_log( "nmg_close_shell: cannot make any planes from loop, old_loop_size = %d\n", old_loop_size );
02609                                                         for( i=0 ; i<old_loop_size ; i++ )
02610                                                         {
02611                                                                 eu= (struct edgeuse *)BU_PTBL_GET( &eu_tbl , index[i] );
02612                                                                 bu_log( "(%g %g %g) <-> (%g %g %g)\n",
02613                                                                         V3ARGS( eu->vu_p->v_p->vg_p->coord ),
02614                                                                         V3ARGS( eu->eumate_p->vu_p->v_p->vg_p->coord ) );
02615                                                         }
02616                                                         bu_bomb( "nmg_close_shell: cannot make any planes from loop\n" );
02617                                                 }
02618                                                 order_tbl( &eu_tbl, start_loop , &index , BU_PTBL_END( &eu_tbl ) , &loop_size );
02619                                         }
02620                                 }
02621                         }
02622 
02623                         /* now we have one plane, let's check the others */
02624                         coplanar = 1;
02625                         while( end_index < loop_size && coplanar )
02626                         {
02627                                 end_index +=3;
02628                                 if( end_index > loop_size )
02629                                         end_index = loop_size;
02630                                 start_index = end_index - 3;
02631 
02632                                 for( i=start_index ; i<end_index ; i++ )
02633                                 {
02634                                         eu = (struct edgeuse *)BU_PTBL_GET( &eu_tbl , index[i] );
02635                                         VMOVE( pt[i-start_index] , eu->vu_p->v_p->vg_p->coord );
02636                                 }
02637 
02638                                 /* if these three points make a plane, is it coplanar with
02639                                  * our first one??? */
02640                                 if( !bn_mk_plane_3pts( pl2 , pt[0] , pt[1] , pt[2] , tol ) )
02641                                 {
02642                                         if( (i=bn_coplanar( pl1 , pl2 , tol )) < 1 )
02643                                                 coplanar = 0;
02644                                 }
02645                         }
02646 
02647                         if( coplanar )  /* excellent! - just make one big face */
02648                         {
02649                                 struct edgeuse *eu_tmp;
02650 
02651                                 /* put vertices in table */
02652                                 bu_ptbl_reset( &vert_tbl );
02653                                 for( i=0 ; i<loop_size ; i++ )
02654                                 {
02655                                         eu = (struct edgeuse *)BU_PTBL_GET( &eu_tbl , index[i] );
02656                                         bu_ptbl_ins( &vert_tbl , (long *)&eu->vu_p->v_p );
02657                                 }
02658 
02659                                 /* make face */
02660                                 fu = nmg_cmface( s , (struct vertex ***)BU_PTBL_BASEADDR(&vert_tbl) , loop_size );
02661 
02662                                 /* face geometry */
02663                                 if( nmg_loop_plane_area( BU_LIST_FIRST( loopuse , &fu->lu_hd ) , pl2 ) < 0.0 )
02664                                 {
02665                                         bu_log( "Failed planeeq\n" );
02666                                         nmg_kfu( fu );
02667                                 }
02668                                 else
02669                                 {
02670                                         nmg_face_g( fu , pl2 );
02671 
02672                                         /* Calculate face bounding box */
02673                                         nmg_face_bb( fu->f_p , tol );
02674                                 }
02675 
02676                                 /* now eliminate loop from table */
02677                                 eu_used = (struct edgeuse **)bu_calloc( loop_size , sizeof( struct edguse *) , "edges used list" );
02678                                 for( i=0 ; i<loop_size ; i++ )
02679                                         eu_used[i] = (struct edgeuse *)BU_PTBL_GET( &eu_tbl , index[i] );
02680 
02681                                 for( i=0 ; i<loop_size ; i++ )
02682                                         bu_ptbl_rm( &eu_tbl , (long *)eu_used[i] );
02683 
02684                                 bu_free( (char *)eu_used , "edge used list" );
02685 
02686                                 /* may need to remove one more edgeuse from table */
02687                                 eu_tmp = nmg_find_e( (*(struct vertex **)BU_PTBL_GET( &vert_tbl, 0 )), (*(struct vertex **)BU_PTBL_GET( &vert_tbl, loop_size-1 )), (struct shell *)NULL, (struct edge *)NULL );
02688                                 if( eu_tmp )
02689                                 {
02690                                         if( eu_tmp->radial_p != eu_tmp->eumate_p )
02691                                         {
02692                                                 for( i=0 ; i<BU_PTBL_END( &eu_tbl ) ; i++ )
02693                                                 {
02694                                                         struct edgeuse *eu2;
02695 
02696                                                         eu2 = (struct edgeuse *)BU_PTBL_GET( &eu_tbl, i );
02697                                                         if( EDGESADJ( eu2, eu_tmp ) )
02698                                                         {
02699                                                                 bu_ptbl_rm( &eu_tbl , (long *)eu2 );
02700                                                                 break;
02701                                                         }
02702                                                 }
02703                                         }
02704                                 }
02705 
02706                                 /* set some flags to get us back to start of loop */
02707                                 found_face = 1;
02708                                 give_up_on_face = 1;
02709                         }
02710 
02711                         /* OK, so we have to do this one little-by-little */
02712                         start_index = -1;
02713                         end_index = -1;
02714                         while( !found_face )
02715                         {
02716                                 /* refresh the vertex list */
02717                                 (void)bu_ptbl_reset( &vert_tbl );
02718 
02719                                 if( end_index == 0 )
02720                                 {
02721                                         give_up_on_face = 1;
02722                                         break;
02723                                 }
02724                                 start_index++;
02725                                 end_index = start_index + 1;
02726                                 if( end_index == loop_size )
02727                                         end_index = 0;
02728 
02729                                 /* Get two edgeuses from the loop */
02730                                 eu1 = (struct edgeuse *)BU_PTBL_GET( &eu_tbl , index[start_index] );
02731                                 bu_ptbl_ins( &vert_tbl , (long *)&eu1->vu_p->v_p );
02732 
02733                                 eu2 = (struct edgeuse *)BU_PTBL_GET( &eu_tbl , index[end_index] );
02734                                 bu_ptbl_ins( &vert_tbl , (long *)&eu2->vu_p->v_p );
02735 
02736                                 edges_used = 2;
02737                                 /* if the edges are collinear, we can't make a face */
02738                                 while( bn_3pts_collinear(
02739                                         eu1->vu_p->v_p->vg_p->coord,
02740                                         eu2->vu_p->v_p->vg_p->coord,
02741                                         eu2->eumate_p->vu_p->v_p->vg_p->coord,
02742                                         tol ) && edges_used < loop_size )
02743                                 {
02744                                         /* So, add another edge */
02745                                         end_index++;
02746                                         if( end_index == loop_size )
02747                                                 end_index = 0;
02748                                         eu1 = eu2;
02749                                         eu2 = (struct edgeuse *)BU_PTBL_GET( &eu_tbl , index[end_index]);
02750                                         bu_ptbl_ins( &vert_tbl , (long *)&eu2->vu_p->v_p );
02751                                         edges_used++;
02752                                 }
02753 
02754                                 found_face = 1;
02755 #if 0
02756                                 /* check that newly created edge isn't already a winged edge */
02757                                 vfirst = (*(struct vertex **)BU_PTBL_GET( &vert_tbl, 0 ));
02758                                 vlast = (*(struct vertex **)BU_PTBL_GET( &vert_tbl, edges_used-1 ));
02759                                 eu_tmp = nmg_find_e( vfirst, vlast, (struct shell *)NULL, (struct edge *)NULL );
02760                                 if( eu_tmp )
02761                                 {
02762                                         if( eu_tmp->radial_p != eu_tmp->eumate_p )
02763                                                 found_face = 0;
02764                                 }
02765 #endif
02766 
02767                         }
02768 
02769                         if( give_up_on_face )
02770                         {
02771                                 loop_size = 0;
02772                                 start_loop = -1;
02773                                 break;
02774                         }
02775 
02776                         /* add last vertex to table */
02777                         bu_ptbl_ins( &vert_tbl , (long *)&eu2->eumate_p->vu_p->v_p );
02778 
02779                         /* save list of used edges to be removed later */
02780                         eu_used = (struct edgeuse **)bu_calloc( edges_used , sizeof( struct edguse *) , "edges used list" );
02781                         for( i=0 ; i<edges_used ; i++ )
02782                                 eu_used[i] = (struct edgeuse *)BU_PTBL_GET( &eu_tbl , index[i] );
02783 
02784                         /* make a face */
02785                         fu = nmg_cmface( s , (struct vertex ***)BU_PTBL_BASEADDR(&vert_tbl) , edges_used+1 );
02786                         /* out with the old, in with the new */
02787                         for( i=0 ; i<edges_used ; i++ )
02788                                 bu_ptbl_rm( &eu_tbl , (long *)eu_used[i] );
02789 
02790                         if( nmg_loop_plane_area( BU_LIST_FIRST( loopuse , &fu->lu_hd ) , pl2 ) < 0.0 )
02791                         {
02792                                 bu_log( "Failed planeeq\n" );
02793                                 nmg_kfu( fu );
02794                         }
02795                         else
02796                         {
02797                                 nmg_face_g( fu , pl2 );
02798 
02799                                 /* find the newly created edgeuse */
02800                                 found = 0;
02801                                 for( BU_LIST_FOR( lu , loopuse , &fu->lu_hd ) )
02802                                 {
02803                                         NMG_CK_LOOPUSE( lu );
02804                                         if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) ==
02805                                                 NMG_VERTEXUSE_MAGIC )
02806                                                         continue;
02807                                         for( BU_LIST_FOR( eu , edgeuse , &lu->down_hd ) )
02808                                         {
02809                                                 NMG_CK_EDGEUSE( eu );
02810                                                 if( eu->vu_p->v_p == (*(struct vertex **)BU_PTBL_GET( &vert_tbl , 0 ))
02811                                                 && eu->eumate_p->vu_p->v_p == (*(struct vertex **)BU_PTBL_GET( &vert_tbl , edges_used) ) )
02812                                                 {
02813                                                         eu_new = eu;
02814                                                         found = 1;
02815                                                         break;
02816                                                 }
02817                                                 else if( eu->vu_p->v_p == (*(struct vertex **)BU_PTBL_GET( &vert_tbl , edges_used))
02818                                                 && eu->eumate_p->vu_p->v_p == (*(struct vertex **)BU_PTBL_GET( &vert_tbl , 0 ) ) )
02819                                                 {
02820                                                         eu_new = eu->eumate_p;
02821                                                         found = 1;
02822                                                         break;
02823                                                 }
02824 
02825                                         }
02826                                         if( found )
02827                                                 break;
02828                                 }
02829 
02830                                 if( eu_new->radial_p == eu_new->eumate_p )
02831                                         bu_ptbl_ins( &eu_tbl , (long *)eu_new );
02832                                 else
02833                                 {
02834                                         struct edgeuse *eu_tmp;
02835 
02836                                         /* find third eu to be removed from eu_tbl */
02837                                         for( i=0 ; i<BU_PTBL_END( &eu_tbl ) ; i++ )
02838                                         {
02839                                                 eu_tmp = (struct edgeuse *)BU_PTBL_GET( &eu_tbl, i );
02840                                                 if( eu_tmp->vu_p->v_p == eu_new->vu_p->v_p &&
02841                                                     eu_tmp->eumate_p->vu_p->v_p == eu_new->eumate_p->vu_p->v_p )
02842                                                 {
02843                                                         bu_ptbl_rm( &eu_tbl , (long *)eu_tmp );
02844                                                         break;
02845                                                 }
02846                                         }
02847                                 }
02848                         }
02849 
02850                         bu_free( (char *)eu_used , "edge used list" );
02851 
02852                         /* re-order loop */
02853                         loop_size = 0;
02854                         start_loop = -1;
02855                         while( loop_size < 3 && start_loop < BU_PTBL_END( &eu_tbl )-3 )
02856                                 order_tbl( &eu_tbl, ++start_loop , &index , BU_PTBL_END( &eu_tbl ) , &loop_size );
02857                 }
02858 
02859                 if( give_up_on_face )
02860                         continue;
02861 
02862                 if( loop_size == 2 )
02863                 {
02864                         bu_ptbl_reset( &vert_tbl );
02865                         eu = (struct edgeuse *)BU_PTBL_GET( &eu_tbl , index[0] );
02866                         (void)bu_ptbl_ins( &vert_tbl , (long *)&eu->vu_p->v_p);
02867                         eu = (struct edgeuse *)BU_PTBL_GET( &eu_tbl , index[1] );
02868                         (void)bu_ptbl_ins( &vert_tbl , (long *)&eu->vu_p->v_p);
02869                         (void)bu_ptbl_ins( &vert_tbl , (long *)&eu->eumate_p->vu_p->v_p);
02870                         if( !bn_3pts_collinear(
02871                                 ((*(struct vertex **)BU_PTBL_GET( &vert_tbl , 0 )))->vg_p->coord,
02872                                 ((*(struct vertex **)BU_PTBL_GET( &vert_tbl , 1 )))->vg_p->coord,
02873                                 ((*(struct vertex **)BU_PTBL_GET( &vert_tbl , 2 )))->vg_p->coord,
02874                                 tol ) )
02875                         {
02876                                 fu = nmg_cmface( s , (struct vertex ***)BU_PTBL_BASEADDR(&vert_tbl) , 3 );
02877                                 if( nmg_calc_face_g( fu ) )
02878                                 {
02879                                         bu_log( "Failed planeeq\n" );
02880                                         nmg_kfu( fu );
02881                                 }
02882                         }
02883                         else
02884                                 bu_log( "Not makeing face, edges are collinear!\n" );
02885 
02886                         loop_size = 0;
02887                         continue;
02888                 }
02889                 else if( loop_size < 2 )
02890                 {
02891                         loop_size = 0;
02892                         continue;
02893                 }
02894 
02895                 /* if the last 3 vertices are collinear, then don't make the last face */
02896                 bu_ptbl_reset( &vert_tbl );
02897                 for( i=0 ; i<3 ; i++ )
02898                 {
02899                         eu = (struct edgeuse *)BU_PTBL_GET( &eu_tbl , index[i] );
02900                         (void)bu_ptbl_ins( &vert_tbl , (long *)&eu->vu_p->v_p);
02901                 }
02902 
02903                 if( !bn_3pts_collinear(
02904                         ((*(struct vertex **)BU_PTBL_GET( &vert_tbl , 0 )))->vg_p->coord,
02905                         ((*(struct vertex **)BU_PTBL_GET( &vert_tbl , 1 )))->vg_p->coord,
02906                         ((*(struct vertex **)BU_PTBL_GET( &vert_tbl , 2 )))->vg_p->coord,
02907                         tol ) )
02908                 {
02909 
02910                         /* Create last face from remaining 3 edges */
02911                         fu = nmg_cmface( s , (struct vertex ***)BU_PTBL_BASEADDR(&vert_tbl) , 3 );
02912                         if( nmg_calc_face_g( fu ) )
02913                                 bu_log( "Failed planeeq\n" );
02914 
02915                 }
02916                 else
02917                         bu_log( "Not makeing face, edges are collinear!\n" );
02918 
02919                 /* remove the last three edges from the table */
02920                 {
02921                         struct edgeuse *eu1,*eu2,*eu3;
02922 
02923                         eu1 = (struct edgeuse *)BU_PTBL_GET( &eu_tbl , index[0] );
02924                         eu2 = (struct edgeuse *)BU_PTBL_GET( &eu_tbl , index[1] );
02925                         eu3 = (struct edgeuse *)BU_PTBL_GET( &eu_tbl , index[2] );
02926                         bu_ptbl_rm( &eu_tbl , (long *)eu1 );
02927                         bu_ptbl_rm( &eu_tbl , (long *)eu2 );
02928                         bu_ptbl_rm( &eu_tbl , (long *)eu3 );
02929                 }
02930         }
02931 
02932         /* Free up all the memory */
02933         bu_free( (char *)index , "index" );
02934         bu_ptbl_free( &eu_tbl );
02935         bu_ptbl_free( &vert_tbl );
02936 
02937 }
02938 
02939 /**
02940  *      N M G _ D U P _ S H E L L
02941  *
02942  *      Duplicate a shell and return the new copy. New shell is
02943  *      in the same region.
02944  *
02945  *  The vertex geometry is copied from the source faces into topologically
02946  *  distinct (new) vertex and vertex_g structs.
02947  *  They will start out being geometricly coincident, but it is anticipated
02948  *  that the caller will modify the geometry, e.g. as in an extrude operation.
02949  *
02950  *  NOTE: This routine creates a translation table that gives the
02951  *  correspondence between old and new structures, the caller is responsible
02952  *  for freeing this memory. Warning - NOT EVERY structure is assigned a
02953  *  correspondence.
02954  */
02955 struct shell *
02956 nmg_dup_shell(struct shell *s, long int ***trans_tbl, const struct bn_tol *tol)
02957 {
02958 
02959         struct model *m;
02960         struct shell *new_s;
02961         struct faceuse *fu;
02962         struct loopuse *lu,*new_lu;
02963         struct edgeuse *eu;
02964         struct faceuse *new_fu;
02965         struct bu_ptbl faces;
02966         int tbl_size;
02967 
02968         if( rt_g.NMG_debug & DEBUG_BASIC)
02969                 bu_log( "nmg_dup_shell( s = x%x , trans_tbl = x%x )\n" , s , trans_tbl );
02970 
02971         NMG_CK_SHELL( s );
02972         BN_CK_TOL( tol );
02973 
02974         m = nmg_find_model( (long *)s );
02975 
02976         /* create translation table double size to accomodate both copies */
02977         tbl_size = m->maxindex * 3;
02978         (*trans_tbl) = (long **)bu_calloc(tbl_size, sizeof(long *),
02979                 "nmg_dup_shell trans_tbl" );
02980 
02981         bu_ptbl_init( &faces , 64, " &faces ");
02982 
02983         new_s = nmg_msv( s->r_p );
02984         if( s->index >= tbl_size ) rt_bomb( "nmg_dup_shell: trans table exceeded\n" );
02985         NMG_INDEX_ASSIGN( (*trans_tbl) , s , (long *)new_s );
02986         if( new_s->index >= tbl_size ) rt_bomb( "nmg_dup_shell: trans table exceeded\n" );
02987         NMG_INDEX_ASSIGN( (*trans_tbl) , new_s , (long *)s );
02988 
02989         /* copy face uses */
02990         for( BU_LIST_FOR( fu , faceuse , &s->fu_hd ) )
02991         {
02992                 NMG_CK_FACEUSE( fu );
02993                 if( fu->orientation == OT_SAME )
02994                 {
02995                         new_fu = (struct faceuse *)NULL;
02996                         for( BU_LIST_FOR( lu , loopuse , &fu->lu_hd ) )
02997                         {
02998                                 NMG_CK_LOOPUSE( lu );
02999                                 if( new_fu )
03000                                 {
03001                                         new_lu = nmg_dup_loop( lu , &new_fu->l.magic , (*trans_tbl) );
03002                                         if( lu->index >= tbl_size ) rt_bomb( "nmg_dup_shell: trans table exceeded\n" );
03003                                         NMG_INDEX_ASSIGN( (*trans_tbl) , lu , (long *)new_lu );
03004                                         if( new_lu->index >= tbl_size ) rt_bomb( "nmg_dup_shell: trans table exceeded\n" );
03005                                         NMG_INDEX_ASSIGN( (*trans_tbl) , new_lu , (long *)lu );
03006                                 }
03007                                 else
03008                                 {
03009                                         new_lu = nmg_dup_loop( lu , &new_s->l.magic , (*trans_tbl) );
03010                                         if( new_lu->index >= tbl_size ) rt_bomb( "nmg_dup_shell: trans table exceeded\n" );
03011                                         NMG_INDEX_ASSIGN( (*trans_tbl) , lu , (long *)new_lu );
03012                                         if( lu->index >= tbl_size ) rt_bomb( "nmg_dup_shell: trans table exceeded\n" );
03013                                         NMG_INDEX_ASSIGN( (*trans_tbl) , new_lu , (long *)lu );
03014                                         new_fu = nmg_mf( new_lu );
03015                                         if( lu->orientation == OT_OPPOSITE )
03016                                         {
03017                                                 /* nmg_mf forces loops to OT_SAME */
03018                                                 new_lu->orientation = OT_OPPOSITE;
03019                                                 new_lu->lumate_p->orientation = OT_OPPOSITE;
03020                                         }
03021                                         if( fu->index >= tbl_size ) rt_bomb( "nmg_dup_shell: trans table exceeded\n" );
03022                                         NMG_INDEX_ASSIGN( (*trans_tbl) , fu , (long *)new_fu );
03023                                         if( new_fu->index >= tbl_size ) rt_bomb( "nmg_dup_shell: trans table exceeded\n" );
03024                                         NMG_INDEX_ASSIGN( (*trans_tbl) , new_fu , (long *)fu );
03025                                         if( fu->fumate_p->index >= tbl_size ) rt_bomb( "nmg_dup_shell: trans table exceeded\n" );
03026                                         NMG_INDEX_ASSIGN( (*trans_tbl) , fu->fumate_p , (long *)new_fu->fumate_p );
03027                                         if( new_fu->fumate_p->index >= tbl_size ) rt_bomb( "nmg_dup_shell: trans table exceeded\n" );
03028                                         NMG_INDEX_ASSIGN( (*trans_tbl) , new_fu->fumate_p , (long *)fu->fumate_p );
03029                                         if( fu->f_p->index >= tbl_size ) rt_bomb( "nmg_dup_shell: trans table exceeded\n" );
03030                                         NMG_INDEX_ASSIGN( (*trans_tbl) , fu->f_p , (long *)new_fu->f_p );
03031                                         if( new_fu->f_p->index >= tbl_size ) rt_bomb( "nmg_dup_shell: trans table exceeded\n" );
03032                                         NMG_INDEX_ASSIGN( (*trans_tbl) , new_fu->f_p , (long *)fu->f_p );
03033                                 }
03034                         }
03035                         if (fu->f_p->g.plane_p)
03036                         {
03037 #if 1
03038                                 /* Do it this way if you expect to change the normals */
03039                                 plane_t n;
03040                                 NMG_GET_FU_PLANE( n, fu );
03041                                 nmg_face_g(new_fu, n);
03042 #else
03043                                 /* Do it this way to share fu's geometry struct */
03044                                 nmg_jfg( fu, new_fu );
03045 #endif
03046 
03047                                 /* XXX Perhaps this should be new_fu->f_p->g.plane_p ? */
03048                                 if( fu->f_p->g.plane_p->index >= tbl_size ) rt_bomb( "nmg_dup_shell: trans table exceeded\n" );
03049                                 NMG_INDEX_ASSIGN( (*trans_tbl) , fu->f_p->g.plane_p , (long *)new_fu->f_p->g.plane_p );
03050                                 if( new_fu->f_p->g.plane_p->index >= tbl_size ) rt_bomb( "nmg_dup_shell: trans table exceeded\n" );
03051                                 NMG_INDEX_ASSIGN( (*trans_tbl) , new_fu->f_p->g.plane_p , (long *)fu->f_p->g.plane_p );
03052                         }
03053                         new_fu->orientation = fu->orientation;
03054                         new_fu->fumate_p->orientation = fu->fumate_p->orientation;
03055                         bu_ptbl_ins( &faces , (long *)new_fu );
03056                 }
03057         }
03058 
03059         /* glue new faces */
03060         nmg_gluefaces( (struct faceuse **)BU_PTBL_BASEADDR( &faces) , BU_PTBL_END( &faces ), tol );
03061         bu_ptbl_free( &faces );
03062 
03063         /* copy wire loops */
03064         for( BU_LIST_FOR( lu , loopuse , &s->lu_hd ) )
03065         {
03066                 NMG_CK_LOOPUSE( lu );
03067                 new_lu = nmg_dup_loop( lu , &new_s->l.magic , (*trans_tbl) );
03068                 if( lu->index >= tbl_size ) rt_bomb( "nmg_dup_shell: trans table exceeded\n" );
03069                 NMG_INDEX_ASSIGN( (*trans_tbl) , lu , (long *)new_lu );
03070                 if( new_lu->index >= tbl_size ) rt_bomb( "nmg_dup_shell: trans table exceeded\n" );
03071                 NMG_INDEX_ASSIGN( (*trans_tbl) , new_lu , (long *)lu );
03072         }
03073 
03074         /* copy wire edges */
03075         for( BU_LIST_FOR( eu , edgeuse , &s->eu_hd ) )
03076         {
03077                 struct vertex *old_v1,*old_v2,*new_v1,*new_v2;
03078                 struct edgeuse *new_eu;
03079 
03080                 NMG_CK_EDGEUSE( eu );
03081                 NMG_CK_VERTEXUSE( eu->vu_p );
03082                 NMG_CK_VERTEX( eu->vu_p->v_p );
03083                 NMG_CK_EDGEUSE( eu->eumate_p );
03084                 NMG_CK_VERTEXUSE( eu->eumate_p->vu_p );
03085                 NMG_CK_VERTEX( eu->eumate_p->vu_p->v_p );
03086 
03087                 old_v1 = eu->vu_p->v_p;
03088                 new_v1 = NMG_INDEX_GETP(vertex, (*trans_tbl), old_v1);
03089                 old_v2 = eu->eumate_p->vu_p->v_p;
03090                 new_v2 = NMG_INDEX_GETP(vertex, (*trans_tbl), old_v2);
03091 
03092                 /* make the wire edge */
03093                 new_eu = nmg_me( new_v1 , new_v2 , new_s );
03094                 if( eu->index >= tbl_size ) rt_bomb( "nmg_dup_shell: trans table exceeded\n" );
03095                 NMG_INDEX_ASSIGN( (*trans_tbl) , eu , (long *)new_eu );
03096                 if( new_eu->index >= tbl_size ) rt_bomb( "nmg_dup_shell: trans table exceeded\n" );
03097                 NMG_INDEX_ASSIGN( (*trans_tbl) , new_eu , (long *)eu );
03098 
03099                 new_v1 = new_eu->vu_p->v_p;
03100                 if( old_v1->index >= tbl_size ) rt_bomb( "nmg_dup_shell: trans table exceeded\n" );
03101                 NMG_INDEX_ASSIGN( (*trans_tbl) , old_v1 , (long *)new_v1 );
03102                 if( new_v1->index >= tbl_size ) rt_bomb( "nmg_dup_shell: trans table exceeded\n" );
03103                 NMG_INDEX_ASSIGN( (*trans_tbl) , new_v1 , (long *)old_v1 );
03104                 if( !new_v1->vg_p )
03105                 {
03106                         nmg_vertex_gv( new_v1 , old_v1->vg_p->coord );
03107                         if( old_v1->vg_p->index >= tbl_size ) rt_bomb( "nmg_dup_shell: trans table exceeded\n" );
03108                         NMG_INDEX_ASSIGN( (*trans_tbl) , old_v1->vg_p , (long *)new_v1->vg_p );
03109                         if( new_v1->vg_p->index >= tbl_size ) rt_bomb( "nmg_dup_shell: trans table exceeded\n" );
03110                         NMG_INDEX_ASSIGN( (*trans_tbl) , new_v1->vg_p , (long *)old_v1->vg_p );
03111                 }
03112 
03113                 new_v2 = new_eu->eumate_p->vu_p->v_p;
03114                 if( old_v2->index >= tbl_size ) rt_bomb( "nmg_dup_shell: trans table exceeded\n" );
03115                 NMG_INDEX_ASSIGN( (*trans_tbl) , old_v2 , (long *)new_v2 );
03116                 if( new_v2->index >= tbl_size ) rt_bomb( "nmg_dup_shell: trans table exceeded\n" );
03117                 NMG_INDEX_ASSIGN( (*trans_tbl) , new_v2 , (long *)old_v2 );
03118                 if( !new_v2->vg_p )
03119                 {
03120                         nmg_vertex_gv( new_v2 , old_v2->vg_p->coord );
03121                         if( old_v2->vg_p->index >= tbl_size ) rt_bomb( "nmg_dup_shell: trans table exceeded\n" );
03122                         NMG_INDEX_ASSIGN( (*trans_tbl) , old_v2->vg_p , (long *)new_v2->vg_p );
03123                         if( new_v2->vg_p->index >= tbl_size ) rt_bomb( "nmg_dup_shell: trans table exceeded\n" );
03124                         NMG_INDEX_ASSIGN( (*trans_tbl) , new_v2->vg_p , (long *)old_v2->vg_p );
03125                 }
03126 
03127         }
03128 
03129 #if 0
03130         /* XXX for this to work nmg_mvu and nmg_mvvu must not be private
03131          *     perhaps there is another way???? */
03132         /* copy vertex use
03133          * This must be done last, since other routines may steal it */
03134         if( s->vu_p )
03135         {
03136                 old_vu = s->vu_p;
03137                 NMG_CK_VERTEXUSE( old_vu );
03138                 old_v = old_vu->v_p;
03139                 NMG_CK_VERTEX( old_v );
03140                 new_v = NMG_INDEX_GETP(vertex, (*trans_tbl), old_v);
03141                 if( new_v )
03142                 {
03143                         /* already copied vertex, just need a use */
03144                         if( new_s->vu_p )
03145                                 (void )nmg_kvu( new_s->vu_p );
03146                         new_s->vu_p = nmg_mvu( new_v , (long *)new_s , m );
03147                 }
03148                 else
03149                 {
03150                         /* make a new vertex and use */
03151                         new_s->vu_p = nmg_mvvu( (long *)new_s , m );
03152                         new_v = new_s->vu_p->v_p;
03153 
03154                         /* put entry in table */
03155                         if( old_v->index >= tbl_size ) rt_bomb( "nmg_dup_shell: trans table exceeded\n" );
03156                         NMG_INDEX_ASSIGN( (*trans_tbl) , old_v , (long *)new_v );
03157                         if( new_v->index >= tbl_size ) rt_bomb( "nmg_dup_shell: trans table exceeded\n" );
03158                         NMG_INDEX_ASSIGN( (*trans_tbl) , new_v , (long *)old_v );
03159 
03160                         /* assign the same geometry as the old copy */
03161                         nmg_vertex_gv( new_v , old_v->vg_p->coord );
03162                         if( old_v->vg_p->index >= tbl_size ) rt_bomb( "nmg_dup_shell: trans table exceeded\n" );
03163                         NMG_INDEX_ASSIGN( (*trans_tbl) , old_v->vg_p , (long *)new_v->vg_p );
03164                         if( new_v->vg_p->index >= tbl_size ) rt_bomb( "nmg_dup_shell: trans table exceeded\n" );
03165                         NMG_INDEX_ASSIGN( (*trans_tbl) , new_v->vg_p , (long *)old_v->vg_p );
03166                 }
03167         }
03168 #endif
03169 
03170         return( new_s );
03171 }
03172 
03173 /*      Routines to use the bu_ptbl structures as a stack of edgeuse structures */
03174 
03175 #define NMG_PUSH( _ptr , _stack )       bu_ptbl_ins( _stack , (long *) _ptr );
03176 
03177 struct edgeuse
03178 *nmg_pop_eu(struct bu_ptbl *stack)
03179 {
03180         struct edgeuse *eu;
03181 
03182         /* return a NULL if stack is empty */
03183         if( BU_PTBL_END( stack ) == 0 )
03184                 return( (struct edgeuse *)NULL );
03185 
03186         /* get last edgeuse on the stack */
03187         eu = (struct edgeuse *)BU_PTBL_GET( stack , BU_PTBL_END( stack )-1 );
03188 
03189         /* remove that edgeuse from the stack */
03190         bu_ptbl_rm( stack , (long *)eu );
03191 
03192         return( eu );
03193 }
03194 
03195 void
03196 nmg_reverse_radials(struct faceuse *fu, const struct bn_tol *tol)
03197 {
03198         struct loopuse *lu;
03199 
03200         if( rt_g.NMG_debug & DEBUG_BASIC )
03201                 bu_log( "nmg_reverse_radials( fu = x%x )\n" , fu );
03202 
03203         NMG_CK_FACEUSE( fu );
03204         BN_CK_TOL( tol );
03205 
03206         for( BU_LIST_FOR( lu, loopuse, &fu->lu_hd ) )
03207         {
03208                 struct edgeuse *eu;
03209                 struct edgeuse *eu_radial;
03210                 struct edgeuse *eumate;
03211                 struct edgeuse *eumate_radial;
03212 
03213                 NMG_CK_LOOPUSE( lu );
03214 
03215                 if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) != NMG_EDGEUSE_MAGIC )
03216                         continue;
03217 
03218                 for( BU_LIST_FOR( eu, edgeuse, &lu->down_hd ) )
03219                 {
03220                         eu_radial = eu->radial_p;
03221                         eumate = eu->eumate_p;
03222                         eumate_radial = eumate->radial_p;
03223 
03224                         /* if no radials continue to next edgeuse in loop */
03225                         if( eu_radial == eumate )
03226                                 continue;
03227 
03228                         /* Note: there is no problem if this loop is radial to another
03229                          * loop in the same face, the radials will get switched as we process
03230                          * the first loop, then switched back as we process the second
03231                          */
03232 
03233                         eu_radial->radial_p = eumate;
03234                         eu->radial_p = eumate_radial;
03235                         eumate_radial->radial_p = eu;
03236                         eumate->radial_p = eu_radial;
03237                 }
03238         }
03239 }
03240 
03241 /* XXX Don't use this, use nmg_s_radial_harmonize() at the right time. */
03242 /**     N M G _ R E V E R S E _ F A C E _ A N D _ R A D I A L S
03243  *
03244  *      This routine calls "nmg_reverse_face" and also makes the radial
03245  *      pointers connect faces of like orientation (i.e., OT_SAME to OT_SAME and
03246  *      OT_OPPOSITE to OT_OPPOSITE).
03247  */
03248 
03249 void
03250 nmg_reverse_face_and_radials(struct faceuse *fu, const struct bn_tol *tol)
03251 {
03252         struct loopuse *lu;
03253 
03254         if( rt_g.NMG_debug & DEBUG_BASIC )
03255                 bu_log( "nmg_reverse_face_and_radials( fu = x%x )\n" , fu );
03256 
03257         NMG_CK_FACEUSE( fu );
03258         BN_CK_TOL( tol );
03259 
03260         /* reverse face */
03261         nmg_reverse_face( fu );
03262 
03263         for( BU_LIST_FOR( lu, loopuse, &fu->lu_hd ) )
03264         {
03265                 struct edgeuse *eu;
03266                 struct edgeuse *eu_radial;
03267                 struct edgeuse *eumate;
03268                 struct edgeuse *eumate_radial;
03269 
03270                 NMG_CK_LOOPUSE( lu );
03271 
03272                 if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) != NMG_EDGEUSE_MAGIC )
03273                         continue;
03274 
03275                 for( BU_LIST_FOR( eu, edgeuse, &lu->down_hd ) )
03276                 {
03277                         eu_radial = eu->radial_p;
03278                         eumate = eu->eumate_p;
03279                         eumate_radial = eumate->radial_p;
03280 
03281                         /* if no radials continue to next edgeuse in loop */
03282                         if( eu_radial == eumate )
03283                                 continue;
03284 
03285                         /* Note: there is no problem if this loop is radial to another
03286                          * loop in the same face, the radials will get switched as we process
03287                          * the first loop, then switched back as we process the second
03288                          */
03289 
03290                         eu_radial->radial_p = eumate;
03291                         eu->radial_p = eumate_radial;
03292                         eumate_radial->radial_p = eu;
03293                         eumate->radial_p = eu_radial;
03294                 }
03295         }
03296 }
03297 
03298 /**     N M G _ S H E L L _ I S _ V O I D
03299  *
03300  * determines if the shell is a void shell or an exterior shell
03301  * by finding the topmost face (in some direction) and looking at
03302  * the component of the OT_SAME faceuse normal in that direction.
03303  *
03304  * returns:
03305  *      0 - shell is exterior shell
03306  *      1 - shell is a void shell
03307  *      -1 - cannot determine
03308  *
03309  * It is expected that this shell is the result of nmg_decompose_shells.
03310  */
03311 int
03312 nmg_shell_is_void(const struct shell *s)
03313 {
03314         struct model *m;
03315         struct face *f;
03316         struct faceuse *fu;
03317         vect_t normal;
03318         int dir;
03319         long *flags;
03320 
03321         NMG_CK_SHELL( s );
03322 
03323         m = nmg_find_model( &s->l.magic );
03324         NMG_CK_MODEL( m );
03325 
03326         flags = (long *)bu_calloc( m->maxindex , sizeof( long ) , "nmg_shell_is_void: flags " );
03327 
03328         f = nmg_find_top_face( s, &dir , flags );
03329 
03330         bu_free( (char *)flags , "nmg_shell_is_void: flags" );
03331 
03332         if( f == (struct face *)NULL )
03333                 return( -1 );
03334 
03335         NMG_CK_FACE( f );
03336         NMG_CK_FACE_G_PLANE( f->g.plane_p );
03337         fu = f->fu_p;
03338         NMG_CK_FACEUSE( fu );
03339 
03340         if( fu->orientation != OT_SAME )
03341                 fu = fu->fumate_p;
03342         if( fu->orientation != OT_SAME )
03343                 return( -1 );
03344 
03345         NMG_GET_FU_NORMAL( normal , fu );
03346 
03347         if( normal[dir] == 0.0 )
03348                 return( -1 );
03349         if( normal[dir] < 0.0)
03350                 return( 1 );
03351         else
03352                 return( 0 );
03353 }
03354 
03355 /**     N M G _ P R O P A G A T E _ N O R M A L S
03356  *
03357  *      This routine expects "fu_in" to have a correctly oriented normal.
03358  *      It then checks all faceuses in the same shell it can reach via radial structures, and
03359  *      reverses faces and modifies radial structures as needed to result in
03360  *      a consistent NMG shell structure. The "flags" variable is a translation table
03361  *      for the model, and as each face is checked, its flag is set. Faces with flags
03362  *      that have already been set will not be checked by this routine.
03363  */
03364 
03365 void
03366 nmg_propagate_normals(struct faceuse *fu_in, long int *flags, const struct bn_tol *tol)
03367 {
03368         struct bu_ptbl stack;
03369         struct loopuse *lu;
03370         struct edgeuse *eu,*eu1;
03371         struct faceuse *fu;
03372 
03373         if( rt_g.NMG_debug & DEBUG_BASIC )
03374                 bu_log( "nmg_propagate_normals( fu_in = x%x , flags = x%x )\n" , fu_in , flags );
03375 
03376         NMG_CK_FACEUSE( fu_in );
03377         BN_CK_TOL( tol );
03378 
03379         fu = fu_in;
03380         if( fu->orientation != OT_SAME )
03381                 fu = fu->fumate_p;
03382         if( fu->orientation != OT_SAME )
03383         {
03384                 bu_log( "nmg_propagate_normals: Could not find OT_SAME orientation of faceuse x%x\n" , fu_in );
03385                 return;
03386         }
03387 
03388         /* set flag for this face since we know this one is OK */
03389         NMG_INDEX_SET( flags , fu->f_p );
03390 
03391         /* Use the ptbl structure as a stack */
03392         bu_ptbl_init( &stack , 64, " &stack ");
03393 
03394         /* push all edgeuses of "fu" onto the stack */
03395         for( BU_LIST_FOR( lu , loopuse , &fu->lu_hd ) )
03396         {
03397                 NMG_CK_LOOPUSE( lu );
03398                 if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) != NMG_EDGEUSE_MAGIC )
03399                         continue;
03400                 for( BU_LIST_FOR( eu , edgeuse , &lu->down_hd ) )
03401                 {
03402                         /* don't push free edges on the stack */
03403                         if( eu->radial_p->eumate_p != eu )
03404                                 NMG_PUSH( eu , &stack );
03405                 }
03406         }
03407 
03408         /* now pop edgeuses from stack, go to radial face, fix its normal,
03409          * and push its edgeuses onto the stack */
03410 
03411         while( (eu1 = nmg_pop_eu( &stack )) != (struct edgeuse *)NULL )
03412         {
03413                 /* eu1 is an edgeuse on an OT_SAME face, so its radial
03414                  * should be in an OT_SAME also */
03415 
03416                 NMG_CK_EDGEUSE( eu1 );
03417 
03418                 eu = eu1->radial_p;
03419                 NMG_CK_EDGEUSE( eu );
03420 
03421                 /* find the face that contains this edgeuse */
03422                 fu = nmg_find_fu_of_eu( eu );
03423                 if( !fu )
03424                         continue;
03425 
03426                 NMG_CK_FACEUSE( fu );
03427 
03428                 /* if this face has already been processed, skip it */
03429                 if( NMG_INDEX_TEST_AND_SET( flags , fu->f_p ) )
03430                 {
03431                         if( rt_g.NMG_debug & DEBUG_BASIC )
03432                                 bu_log( "nmg_propagate_normals: checking fu x%x, eu = x%x, eu1 = x%x\n" , fu, eu, eu1 );
03433 
03434                         if( fu->orientation == OT_SAME )
03435                         {
03436                                 if( eu1->vu_p->v_p == eu->vu_p->v_p &&
03437                                         eu1->eumate_p->vu_p->v_p == eu->eumate_p->vu_p->v_p )
03438                                 {
03439                                         /* edge direction is wrong, need to reverse
03440                                          * both the face and the radials
03441                                          */
03442                                         nmg_reverse_face_and_radials( fu , tol );
03443                                 }
03444                                 /* else - this is the way it should be */
03445                         }
03446                         else if( fu->orientation == OT_OPPOSITE )
03447                         {
03448                                 if( eu1->vu_p->v_p == eu->vu_p->v_p &&
03449                                         eu1->eumate_p->vu_p->v_p == eu->eumate_p->vu_p->v_p )
03450                                 {
03451                                         /* just swap radial pointer around */
03452                                         nmg_reverse_radials( fu , tol );
03453                                 }
03454                                 else
03455                                 {
03456                                         /* just reverse the face */
03457                                         nmg_reverse_face( fu );
03458                                 }
03459                         }
03460                         else
03461                         {
03462                                 bu_log( "nmg_propagate_normals: found an unoriented face!!!!\n" );
03463                                 nmg_pr_fu_briefly( fu, "" );
03464                                 rt_bomb( "nmg_propagate_normals: found an unoriented face!!!!\n" );
03465                         }
03466 
03467                         /* make sure we are dealing with an OT_SAME faceuse */
03468                         if( fu->orientation != OT_SAME )
03469                                 fu = fu->fumate_p;
03470 
03471                         /* push all edgeuses of "fu" onto the stack */
03472                         for( BU_LIST_FOR( lu , loopuse , &fu->lu_hd ) )
03473                         {
03474                                 NMG_CK_LOOPUSE( lu );
03475                                 if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) != NMG_EDGEUSE_MAGIC )
03476                                         continue;
03477                                 for( BU_LIST_FOR( eu , edgeuse , &lu->down_hd ) )
03478                                 {
03479                                         /* don't push free edges on the stack */
03480                                         if( eu->radial_p->eumate_p != eu )
03481                                                 NMG_PUSH( eu , &stack );
03482                                 }
03483                         }
03484                 }
03485         }
03486 
03487         /* free the stack */
03488         bu_ptbl_free( &stack );
03489 }
03490 
03491 
03492 /**             N M G _ D I S C O N N E C T _ S H E L L S
03493  *
03494  *      looks for edges that have uses in more than one shell in region.
03495  *      creates new edges so that there is no sharing of edges among shells
03496  */
03497 static void
03498 nmg_disconnect_shells(struct nmgregion *r)
03499 {
03500         struct shell *s1;
03501         struct bu_ptbl edges;
03502         int count=0;
03503         int i;
03504 
03505         NMG_CK_REGION( r );
03506 
03507         /* count number of shells in this region */
03508         for( BU_LIST_FOR( s1, shell, &r->s_hd ) )
03509                 count++;
03510 
03511         /* if there is less than two shells, nothing to do */
03512         if( count < 2 )
03513                 return;
03514 
03515         /* get a list of all edges in this region */
03516         nmg_edge_tabulate( &edges, &r->l.magic );
03517 
03518         /* look at every edge in region */
03519         for( i=0 ; i<BU_PTBL_END( &edges ) ; i++ )
03520         {
03521                 struct edge *e;
03522                 struct edgeuse *eu;
03523                 struct edgeuse *eu1;
03524                 struct shell *needs_disconnect=(struct shell *)NULL;
03525 
03526                 e = (struct edge *)BU_PTBL_GET( &edges, i );
03527                 NMG_CK_EDGE( e );
03528 
03529                 /* find shell of a use of this edge */
03530                 eu = e->eu_p;
03531                 s1 = nmg_find_s_of_eu( eu );
03532 
03533                 /* check if any other use of this edge is from a different shell */
03534                 eu1 = eu->eumate_p->radial_p;
03535                 while( eu1 != eu &&  eu1 != eu->eumate_p )
03536                 {
03537                         struct shell *s2;
03538 
03539                         if( (s2 = nmg_find_s_of_eu( eu1 )) != s1 )
03540                         {
03541                                 needs_disconnect = s2;
03542                                 break;
03543                         }
03544                         eu1 = eu1->eumate_p->radial_p;
03545                 }
03546 
03547                 while( needs_disconnect )
03548                 {
03549                         struct edgeuse *eu2;
03550                         struct edgeuse *start_eu;
03551                         struct edgeuse *last;
03552                         int last_orientation;
03553 
03554                         /* disconnect first use of this edge in shell 'needs_disconnect' */
03555                         start_eu = eu1->radial_p;
03556                         nmg_unglueedge( eu1 );
03557                         last = eu1;
03558                         last_orientation = (nmg_find_fu_of_eu( eu1 ))->orientation;
03559 
03560                         /* now disconnect all other uses, reconnecting them to eu1 as we go  */
03561                         while( nmg_find_s_of_eu( start_eu ) == needs_disconnect )
03562                                 start_eu = start_eu->eumate_p->radial_p;
03563                         eu2 = start_eu;
03564                         do
03565                         {
03566                                 struct edgeuse *next_eu;
03567                                 struct faceuse *fu2;
03568 
03569                                 /* find uses in 'needs_disconnect' shell */
03570                                 next_eu = eu2->eumate_p->radial_p;
03571                                 if( nmg_find_s_of_eu( eu2 ) == needs_disconnect )
03572                                 {
03573 
03574                                         /* disconnect this use */
03575                                         nmg_unglueedge( eu2 );
03576                                         fu2 = nmg_find_fu_of_eu( eu2 );
03577 
03578                                         /* reconnect it to 'needs_disconnect' shell */
03579                                         if( fu2->orientation == last_orientation)
03580                                         {
03581                                                 nmg_je( last, eu2 );
03582                                                 last = eu2->eumate_p;
03583                                                 last_orientation = fu2->fumate_p->orientation;
03584                                         }
03585                                         else
03586                                         {
03587                                                 nmg_je( last, eu2->eumate_p );
03588                                                 last_orientation = fu2->orientation;
03589                                                 last = eu2;
03590                                         }
03591                                 }
03592                                 eu2 = next_eu;
03593                         } while( eu2 != start_eu && eu2->eumate_p != start_eu );
03594 
03595                         /* now check remaining uses */
03596                         eu1 = eu->eumate_p->radial_p;
03597                         needs_disconnect = (struct shell *)NULL;
03598                         while( eu1 != eu &&  eu1 != eu->eumate_p )
03599                         {
03600                                 struct shell *s2;
03601 
03602                                 if( (s2 = nmg_find_s_of_eu( eu1 )) != s1 )
03603                                 {
03604                                         needs_disconnect = s2;
03605                                         break;
03606                                 }
03607                                 eu1 = eu1->eumate_p->radial_p;
03608                         }
03609                 }
03610 
03611         }
03612 }
03613 
03614 /**             N M G _ C O N N E C T _ S A M E _ F U _ O R I E N T S
03615  *
03616  *      looks for radially connected faceuses that have disagreeing orientations.
03617  *      if such a condiftion is found, the radial pointers are rearranged to make
03618  *      the radial faces agree in orientation.
03619  */
03620 void
03621 nmg_connect_same_fu_orients(struct shell *s)
03622 {
03623         struct faceuse *fu;
03624 
03625         for( BU_LIST_FOR( fu, faceuse, &s->fu_hd ) )
03626         {
03627                 struct loopuse *lu;
03628 
03629                 if( fu->orientation != OT_SAME )
03630                         continue;
03631 
03632                 for( BU_LIST_FOR( lu, loopuse, &fu->lu_hd ) )
03633                 {
03634                         struct edgeuse *eu;
03635 
03636                         if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) != NMG_EDGEUSE_MAGIC )
03637                                 continue;
03638 
03639                         for( BU_LIST_FOR( eu, edgeuse, &lu->down_hd ) )
03640                         {
03641                                 struct faceuse *fu2;
03642                                 struct faceuse *fu3;
03643                                 struct edgeuse *eu2;
03644 
03645                                 eu2 = eu->radial_p;
03646 
03647                                 if( eu2 == eu->eumate_p )
03648                                         continue;
03649 
03650                                 fu2 = nmg_find_fu_of_eu( eu2 );
03651                                 if( fu2->orientation == OT_SAME )
03652                                         continue;
03653 
03654                                 fu3 = nmg_find_fu_of_eu( eu->eumate_p->radial_p );
03655                                 if( fu3->orientation == OT_OPPOSITE )
03656                                         continue;
03657                         }
03658                 }
03659         }
03660 }
03661 
03662 /**             N M G _ F I X _ D E C O M P O S E D_ S H E L L _ N O R M A L S
03663  *
03664  *      Routine to set all OT_SAME faceuse normals to outward direction.
03665  *      Assumes that there are no other shells sharing edges with this one.
03666  */
03667 
03668 void
03669 nmg_fix_decomposed_shell_normals(struct shell *s, const struct bn_tol *tol)
03670 {
03671         struct model *m;
03672         struct face *f_top;
03673         struct faceuse *fu;
03674         vect_t normal;
03675         int dir;
03676         int missed_faces;
03677         long *flags;
03678 
03679         NMG_CK_SHELL( s );
03680         BN_CK_TOL( tol );
03681 
03682         m = s->r_p->m_p;
03683         flags = (long *)bu_calloc( m->maxindex , sizeof( long ) , "nmg_fix_decomposed_shell_normals: flags" );
03684 
03685 missed:
03686         /* find the top face */
03687         f_top = nmg_find_top_face( s, &dir , flags );
03688         if( f_top == (struct face *)NULL )
03689         {
03690                 bu_log( "nmg_fix_decomposed_shell_normals: Could not get a top face from nmg_find_top_face()\n" );
03691                 bu_log( "\tWARNING: continuing without fixing normals!!!!\n" );
03692                 bu_free( (char *)flags, "nmg_fix_decomposed_shell_normals: flags" );
03693                 return;
03694         }
03695         if( *f_top->g.magic_p != NMG_FACE_G_PLANE_MAGIC )
03696         {
03697                 NMG_INDEX_SET( flags, f_top );
03698                 goto missed;
03699         }
03700 
03701         if( NMG_INDEX_TEST( flags, f_top ) )
03702                 bu_log(" nmg_find_top_face returned a flagged face %x\n" , f_top );
03703 
03704         NMG_CK_FACE( f_top );
03705         fu = f_top->fu_p;
03706         NMG_CK_FACEUSE( fu );
03707         if( fu->orientation != OT_SAME )
03708                 fu = fu->fumate_p;
03709         if( fu->orientation != OT_SAME )
03710         {
03711                 bu_log( "nmg_fix_decomposed_shell_normals: no OT_SAME use of top face\n" );
03712                 bu_free( (char *)flags , "nmg_fix_decomposed_shell_normals: flags" );
03713                 return;
03714         }
03715         NMG_GET_FU_NORMAL( normal , fu );
03716 
03717         if( rt_g.NMG_debug & DEBUG_BASIC )
03718         {
03719                 bu_log( "\tnmg_fix_decomposed_shell_normals: top face is x%x in %d direction, OT_SAME use is x%x\n", f_top, dir, fu );
03720                 bu_log( "\toutward normal = ( %g %g %g )\n" , V3ARGS( normal ) );
03721         }
03722 
03723         /* f_top is the topmost face (in the "dir" direction), so its OT_SAME use should have a
03724          * normal with component in the "dir" direction */
03725         if( normal[dir] < 0.0 )
03726         {
03727                 if( rt_g.NMG_debug & DEBUG_BASIC )
03728                         bu_log( "nmg_fix_decomposed_shell_normals: reversing fu x%x\n" , fu );
03729 
03730                 nmg_reverse_face_and_radials( fu, tol );
03731         }
03732 
03733         /* get OT_SAME use of top face */
03734         fu = f_top->fu_p;
03735         if( fu->orientation != OT_SAME )
03736                 fu = fu->fumate_p;
03737 
03738         NMG_CK_FACEUSE( fu );
03739 
03740         /* fu is now known to be a correctly oriented faceuse,
03741          * propagate this throughout the shell, face by face, by
03742          * traversing the shell using the radial edge structure */
03743 
03744         nmg_propagate_normals( fu , flags , tol );
03745 
03746         if( rt_g.NMG_debug & DEBUG_BASIC )
03747         {
03748                 vect_t new_norm;
03749 
03750                 NMG_GET_FU_NORMAL( new_norm , fu );
03751                 bu_log( "nmg_fix_decomposed_shell_normals: After propagation top faceuse normal is ( %g %g %g )\n",
03752                         V3ARGS( new_norm ) );
03753         }
03754 
03755         /* check if all the faces have been processed */
03756         missed_faces = 0;
03757         for( BU_LIST_FOR( fu , faceuse , &s->fu_hd ) )
03758         {
03759                 NMG_CK_FACEUSE( fu );
03760                 if( fu->orientation == OT_SAME )
03761                 {
03762                         if( !NMG_INDEX_TEST( flags , fu->f_p ) )
03763                                 missed_faces++;
03764                 }
03765         }
03766 
03767         if( missed_faces )
03768         {
03769                 bu_log( "nmg_fix_decomposed_shell_normals: missed %d faces in shell x%x (was it decomposed?)\n",
03770                         missed_faces, s );
03771                 rt_bomb( "nmg_fix_decomposed_shell_normals: missed faces in shell (was it decomposed?)\n" );
03772         }
03773 
03774         bu_free( (char *)flags, "flags" );
03775 }
03776 
03777 /**             N M G _ M K _ M O D E L _ F R O M _ R E G I O N
03778  *
03779  *      Creates a new model from an existing nmgregion.
03780  *      Will refuse to create new model if the passed nmgregion has
03781  *      children with uses in another nmgregion.
03782  *
03783  *      The reindex flag indicates if the new model should be reindexed.
03784  *              If non-zero, nmg_m_reindex() is called.
03785  *              If zero, the maxindex field of the new model is filled by finding
03786  *                      the largest index value in the new model and adding one.
03787  *              This is useful if the indices in the region are being used for table look-up.
03788  */
03789 struct model *
03790 nmg_mk_model_from_region(struct nmgregion *r, int reindex)
03791 {
03792         struct model *m;
03793         struct bu_ptbl tbl;
03794         int i;
03795         int other_uses=0;
03796 
03797         NMG_CK_REGION( r );
03798 
03799 
03800         /* check if anything in this region has uses in another region */
03801         nmg_vertex_tabulate( &tbl, &r->l.magic );
03802 
03803         for( i=0 ; i<BU_PTBL_END( &tbl ) ; i++ )
03804         {
03805                 struct vertex *v;
03806                 struct vertexuse *vu;
03807 
03808                 v = (struct vertex *)BU_PTBL_GET( &tbl, i );
03809                 NMG_CK_VERTEX( v );
03810 
03811                 for( BU_LIST_FOR( vu, vertexuse, &v->vu_hd ) )
03812                 {
03813                         if( (nmg_find_s_of_vu( vu ))->r_p != r )
03814                         {
03815                                 bu_log( "vertexuse x%x (v=x%x) at (%g %g %g) has use in another region\n",
03816                                         vu, v, V3ARGS( v->vg_p->coord ) );
03817                                 other_uses++;
03818                         }
03819                 }
03820         }
03821         bu_ptbl_free( &tbl );
03822 
03823         nmg_edge_tabulate( &tbl, &r->l.magic );
03824         for( i=0 ; i<BU_PTBL_END( &tbl ) ; i++ )
03825         {
03826                 struct edge *e;
03827                 struct edgeuse *eu;
03828                 struct edgeuse *eu1;
03829 
03830                 e = (struct edge *)BU_PTBL_GET( &tbl, i );
03831                 NMG_CK_EDGE( e );
03832 
03833                 eu = e->eu_p;
03834                 NMG_CK_EDGEUSE( eu );
03835 
03836                 eu1 = eu->radial_p->eumate_p;
03837                 while( eu1 != eu )
03838                 {
03839                         if( (nmg_find_s_of_eu( eu1 ))->r_p != r )
03840                         {
03841                                 bu_log( "edgeuse x%x (e=x%x) at (%g %g %g)<->(%g %g %g0 has use in another region\n",
03842                                         eu, e, V3ARGS( eu->vu_p->v_p->vg_p->coord ), V3ARGS( eu->eumate_p->vu_p->v_p->vg_p->coord ) );
03843                                 other_uses++;
03844                         }
03845                         eu1 = eu1->radial_p->eumate_p;
03846                 }
03847         }
03848         bu_ptbl_free( &tbl );
03849 
03850         if( other_uses )
03851         {
03852                 return( (struct model *)NULL );
03853         }
03854 
03855         m = nmg_mm();
03856 
03857         BU_LIST_DEQUEUE( &r->l );
03858 
03859         BU_LIST_INSERT( &m->r_hd, &r->l );
03860 
03861         r->m_p = m;
03862 
03863         if( reindex )
03864                 nmg_m_reindex( m , 0 );
03865         else
03866                 m->maxindex = nmg_find_max_index( m ) + 1;
03867 
03868         return( m );
03869 }
03870 
03871 /**             N M G _ F I X _ N O R M A L S
03872  *
03873  *      Routine to set faceuse normlas to correct direction.
03874  *
03875  *      Method:
03876  *              1. Make a  copy of the shell in another model.
03877  *              2. Decompose the copy into constituent shells.
03878  *              3. Set all normals of constituent shells outward (no void shells)
03879  *              4. Use nmg_classify_s_vs_s() for every pair of shells.
03880  *              5. Mark any shell that is inside an odd number of other shells as a void shell.
03881  *              6. Compare original faceuses with the results in the copy and adjust normals as needed.
03882  *              7. Destroy the copy model.
03883  */
03884 void
03885 nmg_fix_normals(struct shell *s_orig, const struct bn_tol *tol)
03886 {
03887         struct model *tmp_m;
03888         struct model *m;
03889         struct shell *dup_s;
03890         struct shell *s1;
03891         struct nmgregion *tmp_r;
03892         struct faceuse *fu;
03893         struct bu_ptbl reverse;
03894         int shell_count;
03895         long **trans_tbl;
03896 
03897         if( rt_g.NMG_debug & DEBUG_BASIC )
03898                 bu_log( "nmg_fix_normals( s = x%x )\n" , s_orig );
03899 
03900         NMG_CK_SHELL( s_orig );
03901         BN_CK_TOL( tol );
03902 
03903         /* Currently we can only fix normals for planar faces
03904          * check that there are no TNURB faces
03905          */
03906         for( BU_LIST_FOR( fu, faceuse, &s_orig->fu_hd ) )
03907         {
03908                 struct face *f;
03909 
03910                 NMG_CK_FACEUSE( fu );
03911 
03912                 if( fu->orientation != OT_SAME )
03913                         continue;
03914 
03915                 f = fu->f_p;
03916 
03917                 if( !f->g.magic_p )
03918                 {
03919                         bu_log( "nmg_fix_normals failed, found a face with no geometry (x%x)\n", f );
03920                         return;
03921                 }
03922 
03923                 if( *f->g.magic_p != NMG_FACE_G_PLANE_MAGIC )
03924                 {
03925                         bu_log( "nmg_fix_normals: non-planar face found (x%x)\n", f );
03926                         bu_log( "       cannot fix normals\n" );
03927                         return;
03928                 }
03929         }
03930 
03931         m = s_orig->r_p->m_p;
03932 
03933         /* make a temporary nmgregion for us to work in */
03934         tmp_r = nmg_mrsv( m );
03935 
03936         /* get rid of the automatically created shell */
03937         (void)nmg_ks( BU_LIST_FIRST( shell, &tmp_r->s_hd ) );
03938 
03939         /* make a copy of the shell of interest */
03940         dup_s = nmg_dup_shell( s_orig, &trans_tbl, tol );
03941 
03942         /* move the copy to our work area */
03943         nmg_mv_shell_to_region( dup_s, tmp_r );
03944 
03945         /* move duplicate shell to another model */
03946         tmp_m = nmg_mk_model_from_region( tmp_r, 0 ); /* don't reindex, We need the old indices */
03947         nmg_rebound( tmp_m, tol );
03948 
03949         /* decompose the shell */
03950         shell_count = nmg_decompose_shell( dup_s, tol );
03951 
03952         if( shell_count == 1 )
03953         {
03954                 /* just one shell, so fix it and return */
03955                 (void)nmg_km( tmp_m );
03956                 bu_free( (char *)trans_tbl, "translate table" );
03957                 nmg_connect_same_fu_orients( s_orig );
03958                 nmg_fix_decomposed_shell_normals( s_orig, tol );
03959                 return;
03960         }
03961 
03962         /* make sure the shells don't share any edges */
03963         nmg_disconnect_shells( tmp_r );
03964 
03965         /* Make sure all OT_SAME faceuses are radial to OT_SAME faceuses */
03966         for( BU_LIST_FOR( s1, shell, &tmp_r->s_hd ) )
03967                 nmg_connect_same_fu_orients( s1 );
03968 
03969         /* Decomposed into more than one shell.
03970          * Need to check for inner void shells.
03971          * Start by making all the shells look like solids (no voids).
03972          */
03973         for( BU_LIST_FOR( s1, shell, &tmp_r->s_hd ) )
03974                 nmg_fix_decomposed_shell_normals( s1, tol );
03975 
03976         /* initialize a list of shells to be reversed */
03977         bu_ptbl_init( &reverse, 8, "Ptbl for nmg_fix_normals" );
03978 
03979         /* now check which shells are inside others */
03980         for( BU_LIST_FOR( s1, shell, &tmp_r->s_hd ) )
03981         {
03982                 struct shell *s2;
03983                 int inner_count=0;
03984 
03985                 for( BU_LIST_FOR( s2, shell, &tmp_r->s_hd ) )
03986                 {
03987                         int class;
03988 
03989                         if( s1 == s2 )
03990                                 continue;
03991 
03992                         class = nmg_classify_s_vs_s( s1, s2, tol );
03993                         if( class == NMG_CLASS_AinB )
03994                                 inner_count++;
03995                         else if( class == NMG_CLASS_Unknown )
03996                         {
03997                                 bu_log( "nmg_fix_normals: nmg_classify_s_vs_s() failed for shells x%x and x%x\n", s1, s2 );
03998                                 bu_log( "   Continueing anyway (shell is likely to have incorrectly oriented normals)\n" );
03999                         }
04000                 }
04001 
04002                 if( inner_count % 2 )
04003                 {
04004                         /* shell s1 is inside an odd number of shells, so it must be a void */
04005                         bu_ptbl_ins( &reverse, (long *)s1 );
04006                 }
04007         }
04008 
04009         /* now set faces in orignal shell to match our calculations */
04010         nmg_connect_same_fu_orients( s_orig );
04011 
04012         for( BU_LIST_FOR( s1, shell, &tmp_r->s_hd ) )
04013         {
04014                 int reversed;
04015 
04016                 if( bu_ptbl_locate( &reverse, (long *)s1 ) == (-1 ))
04017                         reversed = 0;
04018                 else
04019                         reversed = 1;
04020 
04021                 for( BU_LIST_FOR( fu, faceuse, &s1->fu_hd ) )
04022                 {
04023                         struct faceuse *fu_in_s;
04024                         vect_t normal;
04025                         vect_t normal_in_s;
04026 
04027                         if( fu->orientation != OT_SAME )
04028                                 continue;
04029 
04030                         fu_in_s = NMG_INDEX_GETP( faceuse, trans_tbl, fu );
04031                         if( !fu_in_s )
04032                         {
04033                                 bu_log( "fu x%x does not have corrrespondence in original shell\n", fu );
04034                                 nmg_pr_fu_briefly( fu, "" );
04035                                 continue;
04036                         }
04037                         if( fu_in_s->orientation != OT_SAME  )
04038                                 fu_in_s = fu_in_s->fumate_p;
04039 
04040                         NMG_GET_FU_NORMAL( normal, fu );
04041                         if( reversed )
04042                                 VREVERSE( normal, normal )
04043 
04044                         NMG_GET_FU_NORMAL( normal_in_s, fu_in_s );
04045 
04046                         if( VDOT( normal, normal_in_s ) < 0.0 )
04047                         {
04048                                 nmg_reverse_face_and_radials( fu_in_s, tol );
04049                         }
04050                 }
04051         }
04052 
04053         bu_ptbl_free( &reverse );
04054         bu_free( (char *)trans_tbl, "translation table" );
04055 
04056         nmg_km( tmp_m );
04057 }
04058 
04059 /**     N M G _ B R E A K _ L O N G _ E D G E S
04060  *
04061  *      This codes looks for situations as illustrated:
04062  *
04063  *    *------->*-------->*--------->*
04064  *    *<----------------------------*
04065  *
04066  *      where one long edgeuse (the bottom one above) and two or more
04067  *      shorter edgeusess (the tops ones) are collinear and have the same
04068  *      start and end vertices.  The code breaks the longer edgeuse into
04069  *      ones that can be radials of the shorter ones.
04070  *      Returns the number of splits performed.
04071  */
04072 
04073 int
04074 nmg_break_long_edges(struct shell *s, const struct bn_tol *tol)
04075 {
04076         struct faceuse *fu;
04077         struct loopuse *lu;
04078         struct edgeuse *eu;
04079         int split_count=0;
04080 
04081         if( rt_g.NMG_debug & DEBUG_BASIC )
04082                 bu_log( "nmg_break_long_edges( s = x%x )\n" , s );
04083 
04084         NMG_CK_SHELL( s );
04085         BN_CK_TOL( tol );
04086 
04087         /* look at every edgeuse in the shell */
04088         for( BU_LIST_FOR( fu , faceuse , &s->fu_hd ) )
04089         {
04090                 NMG_CK_FACEUSE( fu );
04091 
04092                 for( BU_LIST_FOR( lu , loopuse , &fu->lu_hd ) )
04093                 {
04094                         NMG_CK_LOOPUSE( lu );
04095 
04096                         /* skip loops of a single vertex */
04097                         if( BU_LIST_FIRST_MAGIC(&lu->down_hd) != NMG_EDGEUSE_MAGIC )
04098                                 continue;
04099 
04100                         for( BU_LIST_FOR( eu , edgeuse , &lu->down_hd ) )
04101                         {
04102                                 struct vertexuse *vu;
04103 
04104                                 NMG_CK_EDGEUSE( eu );
04105 
04106                                 /* look for an edgeuse that terminates on this vertex */
04107                                 for( BU_LIST_FOR( vu , vertexuse , &eu->vu_p->v_p->vu_hd ) )
04108                                 {
04109                                         struct edgeuse *eu1;
04110 
04111                                         NMG_CK_VERTEXUSE( vu );
04112 
04113                                         /* skip vertexuses that are not part of an edge */
04114                                         if( *vu->up.magic_p != NMG_EDGEUSE_MAGIC )
04115                                                 continue;
04116 
04117                                         eu1 = vu->up.eu_p;
04118 
04119                                         /* don't consider the edge we already found!!! */
04120                                         if( eu1 == eu )
04121                                                 continue;
04122 
04123                                         /* if it terminates at the same vertex as eu, skip it */
04124                                         if( eu1->eumate_p->vu_p->v_p == eu->eumate_p->vu_p->v_p )
04125                                                 continue;
04126 
04127                                         /* get the mate (so it terminates at "vu") */
04128                                         eu1 = eu1->eumate_p;
04129 
04130                                         /* make sure it is collinear with "eu" */
04131                                         if( bn_3pts_collinear( eu->vu_p->v_p->vg_p->coord ,
04132                                                 eu->eumate_p->vu_p->v_p->vg_p->coord ,
04133                                                 eu1->vu_p->v_p->vg_p->coord , tol ) )
04134                                         {
04135                                                 /* make sure we break the longer edge
04136                                                  * and that the edges are in opposite directions */
04137                                                 vect_t v0,v1;
04138 
04139                                                 VSUB2( v0 , eu->eumate_p->vu_p->v_p->vg_p->coord , eu->vu_p->v_p->vg_p->coord );
04140                                                 VSUB2( v1 , eu1->eumate_p->vu_p->v_p->vg_p->coord , eu1->vu_p->v_p->vg_p->coord );
04141 
04142                                                 if (MAGSQ( v0 ) > MAGSQ( v1 ) && VDOT( v0 , v1 ) < 0.0 )
04143                                                 {
04144                                                         if( rt_g.NMG_debug & DEBUG_BASIC )
04145                                                         {
04146                                                                 bu_log( "Breaking edge from ( %f %f %f ) to ( %f %f %f ) at ( %f %f %f )\n",
04147                                                                         V3ARGS( eu->vu_p->v_p->vg_p->coord ),
04148                                                                         V3ARGS( eu->eumate_p->vu_p->v_p->vg_p->coord ),
04149                                                                         V3ARGS( eu1->vu_p->v_p->vg_p->coord ) );
04150                                                         }
04151                                                         (void) nmg_ebreak(eu1->vu_p->v_p, eu);
04152                                                         split_count++;
04153                                                 }
04154                                                 else if( rt_g.NMG_debug & DEBUG_BASIC )
04155                                                 {
04156                                                         bu_log( "Not splitting collinear edges x%x and x%x:\n", eu , eu1 );
04157                                                         bu_log( "\t( %f %f %f ) -> ( %f %f %f )\n",
04158                                                                 V3ARGS( eu->vu_p->v_p->vg_p->coord ),
04159                                                                 V3ARGS( eu->eumate_p->vu_p->v_p->vg_p->coord ) );
04160                                                         bu_log( "\t( %f %f %f ) -> ( %f %f %f )\n",
04161                                                                 V3ARGS( eu1->vu_p->v_p->vg_p->coord ),
04162                                                                 V3ARGS( eu1->eumate_p->vu_p->v_p->vg_p->coord ) );
04163                                                 }
04164                                         }
04165                                 }
04166                         }
04167                 }
04168         }
04169         return( split_count );
04170 }
04171 
04172 /**     N M G _ M K _ N E W _ F A C E _ F R O M _ L O O P
04173  *
04174  *  Remove a loopuse from an existing face and construct a new face
04175  *  from that loop
04176  *
04177  *  Returns new faceuse as built by nmg_mf()
04178  *
04179  */
04180 struct faceuse *
04181 nmg_mk_new_face_from_loop(struct loopuse *lu)
04182 {
04183         struct shell *s;
04184         struct faceuse *fu;
04185         struct loopuse *lu1;
04186         struct loopuse *lu_mate;
04187         int ot_same_loops=0;
04188 
04189         if( rt_g.NMG_debug & DEBUG_BASIC )
04190                 bu_log( "nmg_mk_new_face_from_loop( lu = x%x )\n" , lu );
04191 
04192         NMG_CK_LOOPUSE( lu );
04193 
04194         if( *lu->up.magic_p != NMG_FACEUSE_MAGIC )
04195         {
04196                 bu_log( "nmg_mk_new_face_from_loop: loopuse is not in a faceuse\n" );
04197                 return( (struct faceuse *)NULL );
04198         }
04199 
04200         fu = lu->up.fu_p;
04201         NMG_CK_FACEUSE( fu );
04202 
04203         s = fu->s_p;
04204         NMG_CK_SHELL( s );
04205 
04206         /* Count the number of exterior loops in this faceuse */
04207         for( BU_LIST_FOR( lu1 , loopuse , &fu->lu_hd ) )
04208         {
04209                 NMG_CK_LOOPUSE( lu1 );
04210                 if( lu1->orientation == OT_SAME )
04211                         ot_same_loops++;
04212         }
04213 
04214         if( ot_same_loops == 1 && lu->orientation == OT_SAME )
04215         {
04216                 bu_log( "nmg_mk_new_face_from_loop: cannot remove only exterior loop from faceuse\n" );
04217                 return( (struct faceuse *)NULL );
04218         }
04219 
04220         lu_mate = lu->lumate_p;
04221 
04222         /* remove loopuse from faceuse */
04223         BU_LIST_DEQUEUE( &lu->l );
04224 
04225         /* remove its mate from faceuse mate */
04226         BU_LIST_DEQUEUE( &lu_mate->l );
04227 
04228         /* insert these loops in the shells list of wire loops
04229          * put the original loopuse at the head of the list
04230          * so that it will end up as the returned faceuse from "nmg_mf"
04231          */
04232         BU_LIST_INSERT( &s->lu_hd , &lu_mate->l );
04233         BU_LIST_INSERT( &s->lu_hd , &lu->l );
04234 
04235         /* set the "up" pointers to the shell */
04236         lu->up.s_p = s;
04237         lu_mate->up.s_p = s;
04238 
04239         /* Now make the new face */
04240         return( nmg_mf( lu ) );
04241 }
04242 
04243 /* state for nmg_split_loops_into_faces */
04244 struct nmg_split_loops_state
04245 {
04246         long            *flags;         /* index based array of flags for model */
04247         const struct bn_tol     *tol;
04248         int             split;          /* count of faces split */
04249 };
04250 
04251 void
04252 nmg_split_loops_handler(long int *fu_p, genptr_t sl_state, int after)
04253 {
04254         struct faceuse *fu;
04255         struct nmg_split_loops_state *state;
04256         struct loopuse *lu;
04257         const struct bn_tol *tol;
04258         int otsame_loops=0;
04259         int otopp_loops=0;
04260 
04261         fu = (struct faceuse *)fu_p;
04262         NMG_CK_FACEUSE( fu );
04263 
04264         state = (struct nmg_split_loops_state *)sl_state;
04265         tol = state->tol;
04266 
04267         if( fu->orientation != OT_SAME )
04268                 return;
04269 
04270         if( !NMG_INDEX_TEST_AND_SET( state->flags , fu ) )  return;
04271 
04272         NMG_INDEX_SET( state->flags , fu->fumate_p );
04273 
04274         for( BU_LIST_FOR( lu , loopuse , &fu->lu_hd ) )
04275         {
04276                 NMG_CK_LOOPUSE( lu );
04277 
04278                 if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) != NMG_EDGEUSE_MAGIC )
04279                         continue;
04280 
04281                 if( lu->orientation == OT_SAME )
04282                         otsame_loops++;
04283                 else if( lu->orientation == OT_OPPOSITE )
04284                         otopp_loops++;
04285                 else
04286                 {
04287                         bu_log( "nmg_split_loops_into_faces: facuse (x%x) with %s loopuse (x%x)\n",
04288                                 fu , nmg_orientation( lu->orientation ) , lu );
04289                         return;
04290                 }
04291         }
04292 
04293         /* if there is only one OT_SAME loop in this faceuse, nothing to do */
04294         if( otsame_loops == 1 )
04295                 return;
04296 
04297         if( otopp_loops && otsame_loops )
04298         {
04299                 struct bu_ptbl inside_loops;
04300 
04301                 bu_ptbl_init( &inside_loops , 64, " &inside_loops ");
04302 
04303                 lu = BU_LIST_FIRST( loopuse , &fu->lu_hd );
04304                 while( BU_LIST_NOT_HEAD( lu , &fu->lu_hd ) )
04305                 {
04306                         struct faceuse *new_fu;
04307                         struct loopuse *lu_next;
04308                         struct loopuse *lu1;
04309                         plane_t plane;
04310                         int index;
04311 
04312                         lu_next = BU_LIST_PNEXT( loopuse , &lu->l );
04313 
04314                         if( otsame_loops == 1 )
04315                         {
04316                                 /* done */
04317                                 bu_ptbl_free( &inside_loops );
04318                                 return;
04319                         }
04320 
04321                         if( lu->orientation != OT_SAME )
04322                         {
04323                                 lu = lu_next;
04324                                 continue;
04325                         }
04326 
04327                         /* find OT_OPPOSITE loops for this exterior loop */
04328                         for( BU_LIST_FOR( lu1 , loopuse , &fu->lu_hd ) )
04329                         {
04330                                 struct loopuse *lu2;
04331                                 int hole_in_lu=1;
04332 
04333                                 if( lu1 == lu )
04334                                         continue;
04335 
04336                                 /* skip loops that are not OT_OPPOSITE */
04337                                 if( lu1->orientation != OT_OPPOSITE )
04338                                         continue;
04339 
04340                                 /* skip loops that are not within lu */
04341                                 if( nmg_classify_lu_lu( lu1 , lu , tol ) != NMG_CLASS_AinB )
04342                                         continue;
04343 
04344                                 /* lu1 is an OT_OPPOSITE loopuse within the OT_SAME lu
04345                                  * but lu1 may be within yet another loopuse that is
04346                                  * also within lu (nested loops)
04347                                  */
04348 
04349                                 /* look for another OT_SAME loopuse within lu */
04350                                 for( BU_LIST_FOR( lu2 , loopuse , &fu->lu_hd ) )
04351                                 {
04352 
04353                                         if( lu2 == lu || lu2 == lu1 )
04354                                                 continue;
04355 
04356                                         if( lu2->orientation != OT_SAME )
04357                                                 continue;
04358 
04359                                         if( nmg_classify_lu_lu( lu2 , lu , tol ) == NMG_CLASS_AinB )
04360                                         {
04361                                                 /* lu2 is within lu, does it contain lu1?? */
04362                                                 if( nmg_classify_lu_lu( lu1 , lu2 , tol ) == NMG_CLASS_AinB )
04363                                                 {
04364                                                         /* Yes, lu1 is within lu2, so lu1 is not
04365                                                          * a hole in lu
04366                                                          */
04367                                                         hole_in_lu = 0;;
04368                                                         break;
04369                                                 }
04370                                         }
04371                                 }
04372 
04373                                 if( hole_in_lu )
04374                                         bu_ptbl_ins( &inside_loops , (long *)lu1 );
04375                         }
04376 
04377                         NMG_GET_FU_PLANE( plane , fu );
04378 
04379                         new_fu = nmg_mk_new_face_from_loop( lu );
04380                         nmg_face_g( new_fu , plane );
04381 
04382                         for( index=0 ; index<BU_PTBL_END( &inside_loops ) ; index++ )
04383                         {
04384                                 lu1 = (struct loopuse *)BU_PTBL_GET( &inside_loops , index );
04385                                 nmg_move_lu_between_fus( new_fu , fu , lu1 );
04386                                 otopp_loops--;
04387                         }
04388                         nmg_face_bb( new_fu->f_p, tol );
04389                         bu_ptbl_reset( &inside_loops );
04390                         otsame_loops--;
04391                         lu = lu_next;
04392                 }
04393                 bu_ptbl_free( &inside_loops );
04394         }
04395         else if( otsame_loops )
04396         {
04397                 /* all loops are of the same orientation, just make a face for every loop */
04398                 int first=1;
04399                 struct faceuse *new_fu;
04400 
04401                 lu = BU_LIST_FIRST( loopuse , &fu->lu_hd );
04402                 while( BU_LIST_NOT_HEAD( lu , &fu->lu_hd ) )
04403                 {
04404                         struct loopuse *next_lu;
04405 
04406                         next_lu = BU_LIST_PNEXT( loopuse , &lu->l );
04407 
04408                         if( first )
04409                                 first = 0;
04410                         else
04411                         {
04412                                 plane_t plane;
04413 
04414                                 if( lu->orientation == OT_SAME )
04415                                 {
04416                                         NMG_GET_FU_PLANE( plane , fu );
04417                                 }
04418                                 else
04419                                 {
04420                                         NMG_GET_FU_PLANE( plane , fu->fumate_p );
04421                                 }
04422                                 new_fu = nmg_mk_new_face_from_loop( lu );
04423                                 nmg_face_g( new_fu , plane );
04424                                 nmg_face_bb( new_fu->f_p, tol );
04425                         }
04426 
04427                         lu = next_lu;
04428                 }
04429         }
04430         else
04431         {
04432                 /* faceuse has only OT_OPPOSITE loopuses */
04433                 bu_log( "nmg_split_loops_into_faces: fu (x%x) has only OT_OPPOSITE loopuses, ignored\n" , fu );
04434         }
04435 }
04436 
04437 /**     N M G _ S P L I T _ L O O P S _ I N T O _ F A C E S
04438  *
04439  *      Visits each faceuse and splits disjoint loops into
04440  *      seperate faces.
04441  *
04442  *      Returns the number of faces modified.
04443  */
04444 int
04445 nmg_split_loops_into_faces(long int *magic_p, const struct bn_tol *tol)
04446 {
04447         struct model *m;
04448         struct nmg_split_loops_state sl_state;
04449         int count;
04450         static const struct nmg_visit_handlers htab = {NULL, NULL, NULL, NULL, NULL,
04451                                                        NULL, NULL, NULL, NULL, nmg_split_loops_handler,
04452                                                        NULL, NULL, NULL, NULL, NULL,
04453                                                        NULL, NULL, NULL, NULL, NULL,
04454                                                        NULL, NULL, NULL, NULL, NULL};
04455         /* htab.aft_faceuse = nmg_split_loops_handler; */
04456 
04457         if( rt_g.NMG_debug & DEBUG_BASIC )
04458                 bu_log( "nmg_split_loops_into_faces( magic_p = x%x )\n" , magic_p );
04459 
04460         BN_CK_TOL( tol );
04461 
04462         m = nmg_find_model( magic_p );
04463         NMG_CK_MODEL( m );
04464 
04465         BN_CK_TOL( tol );
04466 
04467         sl_state.split = 0;
04468         sl_state.flags = (long *)bu_calloc( m->maxindex*2 , sizeof( long ) , "nmg_split_loops_into_faces: flags" );
04469         sl_state.tol = tol;
04470 
04471         nmg_visit( magic_p , &htab , (genptr_t)&sl_state );
04472 
04473         count = sl_state.split;
04474 
04475         bu_free( (char *)sl_state.flags , "nmg_split_loops_into_faces: flags" );
04476 
04477         return( count );
04478 }
04479 
04480 /**     N M G _ D E C O M P O S E _ S H E L L
04481  *
04482  *      Accepts one shell and breaks it to the minimum number
04483  *      of disjoint shells.
04484  *
04485  *      explicit returns:
04486  *              # of resulting shells ( 1 indicates that nothing was done )
04487  *
04488  *      implicit returns:
04489  *              additional shells in the passed in shell's region.
04490  */
04491 int
04492 nmg_decompose_shell(struct shell *s, const struct bn_tol *tol)
04493 {
04494         int missed_faces;
04495         int no_of_shells=1;
04496         int shell_no=1;
04497         int i,j;
04498         struct model *m;
04499         struct nmgregion *r;
04500         struct shell *new_s;
04501         struct faceuse *fu;
04502         struct loopuse *lu;
04503         struct edgeuse *eu;
04504         struct edgeuse *eu1;
04505         struct faceuse *missed_fu = NULL;
04506         struct bu_ptbl stack;
04507         struct bu_ptbl shared_edges;
04508         long *flags;
04509 
04510         if( rt_g.NMG_debug & DEBUG_BASIC )
04511                 bu_log( "nmg_decompose_shell( s = x%x ) START\n" , s );
04512 
04513         NMG_CK_SHELL( s );
04514 
04515         BN_CK_TOL( tol );
04516 
04517         /* Make an index table to insure we visit each face once and only once */
04518         r = s->r_p;
04519         NMG_CK_REGION( r );
04520         m = r->m_p;
04521         NMG_CK_MODEL( m );
04522         flags = (long *)bu_calloc( m->maxindex*2 , sizeof( long ) , "nmg_decompose_shell: flags" );
04523 
04524         bu_ptbl_init( &stack , 64, " &stack ");
04525         bu_ptbl_init( &shared_edges , 64, " &shared_edges ");
04526 
04527         /* Need to be sure that every face has just one OT_SAME loop */
04528         (void)nmg_split_loops_into_faces( &s->l.magic , tol );
04529 
04530         /* get first faceuse from shell */
04531         fu = BU_LIST_FIRST( faceuse , &s->fu_hd );
04532         NMG_CK_FACEUSE( fu );
04533         if( fu->orientation != OT_SAME )
04534                 fu = fu->fumate_p;
04535         if( fu->orientation != OT_SAME )
04536                 rt_bomb( "First face in shell has no OT_SAME uses!!!!\n" );
04537 
04538         /* put all edguses of first faceuse on the stack */
04539         for( BU_LIST_FOR( lu , loopuse , &fu->lu_hd ) )
04540         {
04541                 NMG_CK_LOOPUSE( lu );
04542                 if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) != NMG_EDGEUSE_MAGIC )
04543                         continue;
04544 
04545                 for( BU_LIST_FOR( eu , edgeuse , &lu->down_hd ) )
04546                 {
04547                         /* build two lists, one of winged edges, the other not */
04548                         if( nmg_radial_face_count( eu , s ) > 2 )
04549                                 bu_ptbl_ins_unique( &shared_edges , (long *)eu );
04550                         else
04551                                 bu_ptbl_ins_unique( &stack , (long *)eu );
04552                 }
04553         }
04554 
04555         /* Mark first faceuse and mate with shell number */
04556         NMG_INDEX_ASSIGN( flags , fu , shell_no );
04557         NMG_INDEX_ASSIGN( flags , fu->fumate_p , shell_no );
04558 
04559         /* now pop edgeuse of the stack and visit faces radial to edgeuse */
04560         while( (eu1 = nmg_pop_eu( &stack )) != (struct edgeuse *)NULL )
04561         {
04562                 NMG_CK_EDGEUSE( eu1 );
04563 
04564                 /* move to the radial */
04565                 eu = eu1->radial_p;
04566                 NMG_CK_EDGEUSE( eu );
04567 
04568                 /* make sure we stay within the intended shell */
04569                 while( nmg_find_s_of_eu( eu ) != s && eu != eu1 && eu != eu1->eumate_p )
04570                         eu = eu->eumate_p->radial_p;
04571 
04572                 if( eu == eu1 || eu == eu1->eumate_p )
04573                         continue;
04574 
04575                 fu = nmg_find_fu_of_eu( eu );
04576                 NMG_CK_FACEUSE( fu );
04577 
04578                 if( fu->orientation != OT_SAME )
04579                         fu = fu->fumate_p;
04580 
04581                 /* if this faceuse has already been visited, skip it */
04582                 if( !NMG_INDEX_TEST( flags , fu ) )
04583                 {
04584                         /* push all edgeuses of "fu" onto the stacks */
04585                         for( BU_LIST_FOR( lu , loopuse , &fu->lu_hd ) )
04586                         {
04587                                 NMG_CK_LOOPUSE( lu );
04588 
04589                                 if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) != NMG_EDGEUSE_MAGIC )
04590                                         continue;
04591 
04592                                 for( BU_LIST_FOR( eu , edgeuse , &lu->down_hd ) )
04593                                 {
04594                                         /* build two lists, one of winged edges, the other not */
04595                                         if( nmg_radial_face_count( eu , s ) > 2 )
04596                                                 bu_ptbl_ins_unique( &shared_edges , (long *)eu );
04597                                         else
04598                                                 bu_ptbl_ins_unique( &stack , (long *)eu );
04599                                 }
04600                         }
04601                         /* Mark this faceuse and its mate with a shell number */
04602                         NMG_INDEX_ASSIGN( flags , fu , shell_no );
04603                         NMG_INDEX_ASSIGN( flags , fu->fumate_p , shell_no );
04604                 }
04605         }
04606 
04607         /* count number of faces that were not visited */
04608         missed_faces = 0;
04609         for( BU_LIST_FOR( fu , faceuse , &s->fu_hd ) )
04610         {
04611                 NMG_CK_FACEUSE( fu );
04612                 if( fu->orientation == OT_SAME )
04613                 {
04614                         if( !NMG_INDEX_TEST( flags , fu ) )
04615                         {
04616                                 missed_faces++;
04617                                 missed_fu = fu;
04618                         }
04619                 }
04620         }
04621 
04622         if( !missed_faces )     /* nothing to do, just one shell */
04623         {
04624                 bu_free( (char *)flags , "nmg_decompose_shell: flags " );
04625                 bu_ptbl_free( &stack );
04626                 bu_ptbl_free( &shared_edges );
04627                 return( no_of_shells );
04628         }
04629 
04630         while( missed_faces )
04631         {
04632                 struct edgeuse *unassigned_eu = NULL;
04633                 int *shells_at_edge;
04634                 int new_shell_no=0;
04635 
04636                 bu_ptbl_reset( &stack );
04637 
04638                 /* Look at the list of shared edges to see if anything can be deduced */
04639                 shells_at_edge = (int *)bu_calloc( no_of_shells+1 , sizeof( int ) , "nmg_decompose_shell: shells_at_edge" );
04640 
04641                 for( i=0 ; i<BU_PTBL_END( &shared_edges ) ; i++ )
04642                 {
04643                         int faces_at_edge=0;
04644                         int k;
04645 
04646                         /* Construct a list of shells for this edge.
04647                          * shells_at_edge[i] is the number of edgeuses of this
04648                          * edge that have been assigned to shell number i.
04649                          * shells_at_edge[0] is the number of uses of this edge
04650                          * that have not been asigned to any shell yet
04651                          */
04652                         for( k=0 ; k<=no_of_shells ; k++ )
04653                                 shells_at_edge[k] = 0;
04654 
04655                         unassigned_eu = NULL;
04656 
04657                         eu = (struct edgeuse *)BU_PTBL_GET( &shared_edges , i );
04658                         NMG_CK_EDGEUSE( eu );
04659 
04660                         eu1 = eu;
04661                         do
04662                         {
04663                                 struct faceuse *fu_of_eu;
04664 
04665                                 fu_of_eu = nmg_find_fu_of_eu( eu1 );
04666 
04667                                 faces_at_edge++;
04668                                 if( !NMG_INDEX_TEST( flags , fu_of_eu ) )
04669                                         unassigned_eu = eu1;
04670                                 shells_at_edge[ NMG_INDEX_GET( flags , fu_of_eu ) ]++;
04671 
04672                                 eu1 = nmg_next_radial_eu( eu1 , s , 0 );
04673                         }
04674                         while( eu1 != eu );
04675 
04676                         if( shells_at_edge[0] == 1 && unassigned_eu )
04677                         {
04678                                 /* Only one edgeuse at this edge is unassigned, should be
04679                                  * able to determine which shell it belongs in */
04680 
04681                                 for( j=1 ; j<=no_of_shells ; j++ )
04682                                 {
04683                                         if( shells_at_edge[j] == 1 )
04684                                         {
04685                                                 /* unassigned edgeuse should belong to shell j */
04686                                                 new_shell_no = j;
04687                                                 break;
04688                                         }
04689                                 }
04690                         }
04691                         else if( shells_at_edge[0] == 0 )
04692                         {
04693                                 /* all uses of this edge have been assigned
04694                                  * it can be deleted from the table
04695                                  */
04696                                 bu_ptbl_rm( &shared_edges , (long *)eu );
04697                         }
04698                         if( new_shell_no )
04699                                 break;
04700                 }
04701 
04702                 if( !new_shell_no )
04703                 {
04704                         /* Couldn't find a definite edgeuse to start a new shell
04705                          * so use radial parity to pick an edgeuse that should not be
04706                          * part of the same shell as ones already assigned
04707                          */
04708                         for( i=0 ; i<BU_PTBL_END( &shared_edges ) ; i++ )
04709                         {
04710                                 struct faceuse *fu_of_eu1;
04711                                 int found_missed_face=0;
04712 
04713                                 eu = (struct edgeuse *)BU_PTBL_GET( &shared_edges , i );
04714                                 NMG_CK_EDGEUSE( eu );
04715 
04716                                 eu1 = eu;
04717                                 do
04718                                 {
04719                                         /* look for unassigned edgeuses */
04720                                         fu_of_eu1 = nmg_find_fu_of_eu( eu1 );
04721                                         NMG_CK_FACEUSE( fu_of_eu1 );
04722                                         if( !NMG_INDEX_TEST( flags , fu_of_eu1 ) )
04723                                         {
04724                                                 struct edgeuse *eu2;
04725                                                 struct faceuse *fu_of_eu2;
04726 
04727                                                 /* look for a neighboring edgeuse that
04728                                                  * has been assigned
04729                                                  */
04730                                                 eu2 = nmg_prev_radial_eu( eu1 , s , 0 );
04731                                                 fu_of_eu2 = nmg_find_fu_of_eu( eu2 );
04732                                                 NMG_CK_FACEUSE( fu_of_eu2 );
04733                                                 if( NMG_INDEX_TEST( flags , fu_of_eu2 ) )
04734                                                 {
04735                                                         /* eu2 has been assigned
04736                                                          * compare orientation parity
04737                                                          */
04738                                                         if( fu_of_eu2->orientation ==
04739                                                                 fu_of_eu1->orientation )
04740                                                         {
04741                                                                 /* These should not be in the same
04742                                                                  * shell, so start a new shell
04743                                                                  * at this faceuse
04744                                                                  */
04745                                                                 missed_fu = fu_of_eu1;
04746                                                                 found_missed_face = 1;
04747                                                         }
04748                                                 }
04749                                                 if( found_missed_face )
04750                                                         break;
04751 
04752                                                 eu2 = nmg_next_radial_eu( eu1 , s , 0 );
04753                                                 fu_of_eu2 = nmg_find_fu_of_eu( eu2 );
04754                                                 NMG_CK_FACEUSE( fu_of_eu2 );
04755                                                 if( NMG_INDEX_TEST( flags , fu_of_eu2 ) )
04756                                                 {
04757                                                         /* eu2 has been assigned
04758                                                          * compare orientation parity
04759                                                          */
04760                                                         if( fu_of_eu2->orientation ==
04761                                                                 fu_of_eu1->orientation )
04762                                                         {
04763                                                                 /* These should not be in the same
04764                                                                  * shell, so start a new shell
04765                                                                  * at this faceuse
04766                                                                  */
04767                                                                 missed_fu = fu_of_eu1;
04768                                                                 found_missed_face = 1;
04769                                                         }
04770                                                 }
04771 
04772                                         }
04773                                         if( found_missed_face )
04774                                                 break;
04775                                         eu1 = nmg_next_radial_eu( eu1 , s , 0 );
04776                                 }
04777                                 while( eu1 != eu );
04778 
04779                                 if( found_missed_face )
04780                                         break;
04781                         }
04782                 }
04783 
04784                 bu_free( (char *)shells_at_edge , "nmg_decompose_shell: shells_at_edge" );
04785 
04786                 /* make a new shell number */
04787                 if( new_shell_no )
04788                 {
04789                         shell_no = new_shell_no;
04790                         fu = nmg_find_fu_of_eu( unassigned_eu );
04791                 }
04792                 else
04793                 {
04794                         shell_no = (++no_of_shells);
04795                         NMG_CK_FACEUSE( missed_fu );
04796                         fu = missed_fu;
04797                 }
04798 
04799                 if( fu->orientation != OT_SAME )
04800                         fu = fu->fumate_p;
04801 
04802                 if( !NMG_INDEX_TEST( flags , fu ) )
04803                 {
04804                         /* move this missed face to the new shell */
04805 
04806                         /* push all edgeuses of "fu" onto the stack */
04807                         for( BU_LIST_FOR( lu , loopuse , &fu->lu_hd ) )
04808                         {
04809                                 NMG_CK_LOOPUSE( lu );
04810 
04811                                 if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) != NMG_EDGEUSE_MAGIC )
04812                                         continue;
04813 
04814                                 for( BU_LIST_FOR( eu , edgeuse , &lu->down_hd ) )
04815                                 {
04816                                         /* build two lists, one of winged edges, the other not */
04817                                         if( nmg_radial_face_count( eu , s ) > 2 )
04818                                                 bu_ptbl_ins_unique( &shared_edges , (long *)eu );
04819                                         else
04820                                                 bu_ptbl_ins_unique( &stack , (long *)eu );
04821                                 }
04822                         }
04823 
04824                         /* Mark this faceuse with a shell number */
04825                         NMG_INDEX_ASSIGN( flags , fu , shell_no );
04826                         NMG_INDEX_ASSIGN( flags , fu->fumate_p , shell_no );
04827 
04828                 }
04829                 else
04830                         rt_bomb( "nmg_decompose_shell: Missed face wasn't missed???\n" );
04831 
04832                 /* now pop edgeuse of the stack and visit faces radial to edgeuse */
04833                 while( (eu1 = nmg_pop_eu( &stack )) != (struct edgeuse *)NULL )
04834                 {
04835                         NMG_CK_EDGEUSE( eu1 );
04836 
04837                         /* move to the radial */
04838                         eu = eu1->radial_p;
04839                         NMG_CK_EDGEUSE( eu );
04840 
04841                         /* stay within the original shell */
04842                         while( nmg_find_s_of_eu( eu ) != s && eu != eu1 && eu != eu1->eumate_p )
04843                                 eu = eu->eumate_p->radial_p;
04844 
04845                         if( eu == eu1 || eu == eu1->eumate_p )
04846                                 continue;
04847 
04848                         fu = nmg_find_fu_of_eu( eu );
04849                         NMG_CK_FACEUSE( fu );
04850 
04851                         /* if this face has already been visited, skip it */
04852                         if( !NMG_INDEX_TEST( flags , fu ) )
04853                         {
04854                                 /* push all edgeuses of "fu" onto the stack */
04855                                 for( BU_LIST_FOR( lu , loopuse , &fu->lu_hd ) )
04856                                 {
04857                                         NMG_CK_LOOPUSE( lu );
04858                                         if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) != NMG_EDGEUSE_MAGIC )
04859                                                 continue;
04860                                         for( BU_LIST_FOR( eu , edgeuse , &lu->down_hd ) )
04861                                         {
04862                                                 /* build two lists, one of winged edges, the other not */
04863                                                 if( nmg_radial_face_count( eu , s ) > 2 )
04864                                                         bu_ptbl_ins_unique( &shared_edges , (long *)eu );
04865                                                 else
04866                                                         bu_ptbl_ins_unique( &stack , (long *)eu );
04867                                         }
04868                                 }
04869 
04870                                 /* Mark this faceuse with a shell number */
04871                                 NMG_INDEX_ASSIGN( flags , fu , shell_no );
04872                                 NMG_INDEX_ASSIGN( flags , fu->fumate_p , shell_no );
04873 
04874                         }
04875                 }
04876 
04877                 /* count number of faces that were not visited */
04878                 missed_faces = 0;
04879                 for( BU_LIST_FOR( fu , faceuse , &s->fu_hd ) )
04880                 {
04881                         NMG_CK_FACEUSE( fu );
04882                         if( fu->orientation == OT_SAME )
04883                         {
04884                                 if( !NMG_INDEX_TEST( flags , fu ) )
04885                                 {
04886                                         missed_faces++;
04887                                         missed_fu = fu;
04888                                 }
04889                         }
04890                 }
04891         }
04892 
04893         /* Now build the new shells */
04894         for( shell_no=2 ; shell_no<=no_of_shells ; shell_no++ )
04895         {
04896                 struct bu_ptbl faces;
04897 
04898                 bu_ptbl_init( &faces, 64, "Faces ptbl for nmg_decompose_shell" );
04899 
04900                 /* Make a shell */
04901                 new_s = nmg_msv( r );
04902                 (void)nmg_kvu( new_s->vu_p );
04903 
04904                 /* Move faces marked with this shell_no to this shell */
04905                 fu = BU_LIST_FIRST( faceuse , &s->fu_hd );
04906                 while( BU_LIST_NOT_HEAD( fu , &s->fu_hd ) )
04907                 {
04908                         struct faceuse *next_fu;
04909 
04910                         next_fu = BU_LIST_NEXT( faceuse , &fu->l );
04911                         while( BU_LIST_NOT_HEAD( next_fu , &s->fu_hd ) && next_fu->orientation != OT_SAME )
04912                                 next_fu = BU_LIST_NEXT( faceuse , &next_fu->l );
04913 
04914                         if( fu->orientation != OT_SAME )
04915                         {
04916                                 fu = next_fu;
04917                                 continue;
04918                         }
04919 
04920                         if( NMG_INDEX_GET( flags , fu ) == shell_no )
04921                         {
04922                                 nmg_mv_fu_between_shells( new_s , s , fu );
04923                                 bu_ptbl_ins( &faces, (long *)fu );
04924                         }
04925 
04926                         fu = next_fu;
04927                 }
04928 
04929                 nmg_gluefaces( (struct faceuse **)BU_PTBL_BASEADDR( &faces ), BU_PTBL_END( &faces ), tol );
04930                 bu_ptbl_free( &faces );
04931                 nmg_shell_a( new_s, tol );
04932         }
04933         bu_free( (char *)flags , "nmg_decompose_shell: flags " );
04934         bu_ptbl_free( &stack );
04935         bu_ptbl_free( &shared_edges );
04936 
04937         nmg_rebound( m ,tol );
04938 
04939         if( rt_g.NMG_debug & DEBUG_BASIC )
04940                 bu_log( "nmg_decompose_shell END (%d shells)\n" , no_of_shells );
04941 
04942         return( no_of_shells );
04943 }
04944 
04945 /**
04946  *                      N M G _ S T A S H _ M O D E L _ T O _ F I L E
04947  *
04948  *  Store an NMG model as a separate .g file, for later examination.
04949  *  Don't free the model, as the caller may still have uses for it.
04950  *
04951  *  NON-PARALLEL because of rt_uniresource.
04952  */
04953 void
04954 nmg_stash_model_to_file(const char *filename, const struct model *m, const char *title)
04955 {
04956         struct rt_wdb           *fp;
04957         struct rt_db_internal   intern;
04958         struct bu_external      ext;
04959         int                     ret;
04960         int                     flags;
04961         char                    *name="error.s";
04962 
04963         bu_log("nmg_stash_model_to_file('%s', x%x, %s)\n", filename, m, title);
04964 
04965         NMG_CK_MODEL(m);
04966         nmg_vmodel(m);
04967 
04968         if( (fp = wdb_fopen(filename)) == NULL )  {
04969                 perror(filename);
04970                 return;
04971         }
04972 
04973         RT_INIT_DB_INTERNAL(&intern);
04974         intern.idb_major_type = DB5_MAJORTYPE_BRLCAD;
04975         intern.idb_type = ID_NMG;
04976         intern.idb_meth = &rt_functab[ID_NMG];
04977         intern.idb_ptr = (genptr_t)m;
04978 
04979         if( fp->dbip->dbi_version <= 4 )  {
04980                 BU_INIT_EXTERNAL( &ext );
04981                 ret = intern.idb_meth->ft_export( &ext, &intern, 1.0, fp->dbip, &rt_uniresource );
04982                 if( ret < 0 )  {
04983                         bu_log("rt_db_put_internal(%s):  solid export failure\n",
04984                                 name);
04985                         ret = -1;
04986                         goto out;
04987                 }
04988                 db_wrap_v4_external( &ext, name );
04989         } else {
04990                 if( rt_db_cvt_to_external5( &ext, name, &intern, 1.0, fp->dbip, &rt_uniresource, intern.idb_major_type ) < 0 )  {
04991                         bu_log("wdb_export(%s): solid export failure\n",
04992                                 name );
04993                         ret = -2;
04994                         goto out;
04995                 }
04996         }
04997         BU_CK_EXTERNAL( &ext );
04998 
04999         flags = db_flags_internal( &intern );
05000         ret = wdb_export_external( fp, &ext, name, flags, intern.idb_type );
05001 out:
05002         bu_free_external( &ext );
05003         wdb_close( fp );
05004 
05005         bu_log("nmg_stash_model_to_file(): wrote error.s to '%s'\n",
05006                 filename);
05007 }
05008 
05009 /* state for nmg_unbreak_edge */
05010 struct nmg_unbreak_state
05011 {
05012         long            *flags;         /* index based array of flags for model */
05013         int             unbroken;       /* count of edges mended */
05014 };
05015 
05016 /**
05017  *                      N M G _ U N B R E A K _ H A N D L E R
05018  *
05019  *      edgeuse visit routine for nmg_unbreak_region_edges.
05020  *
05021  *      checks if edgeuse "eu" and its successor are candidates to be unbroken.
05022  *      looks for two consectutive edgeuses sharing the same
05023  *      edge geometry. Checks that the middle vertex has no
05024  *      other uses, and,  if so, kills the second edgeuse.
05025  *      Also moves the vu of the first edgeuse mate to the vu
05026  *      of the killed edgeuse mate.
05027  */
05028 void
05029 nmg_unbreak_handler(long int *eup, genptr_t state, int after)
05030 {
05031         struct edgeuse *eu1,*eu2;
05032         struct edge *e;
05033         struct edge_g_lseg *eg;
05034         struct nmg_unbreak_state *ub_state;
05035         struct vertex   *vb;
05036         int             ret;
05037 
05038         eu1 = (struct edgeuse *)eup;
05039         NMG_CK_EDGEUSE( eu1 );
05040 
05041         ub_state = (struct nmg_unbreak_state *)state;
05042 
05043         /* there is a temptation to do a NMG_INDEX_SET( ub_state->flags , eu1->eumate_p )
05044          * here to avoid looking at this edgeuse's mate, but since we are only looking
05045          * forward, we must look at ALL edgeuses
05046          */
05047 
05048         /* make sure we only visit this edgeuse once */
05049         if( !NMG_INDEX_TEST_AND_SET( ub_state->flags , eu1 ) )  return;
05050 
05051         e = eu1->e_p;
05052         NMG_CK_EDGE( e );
05053 
05054         eg = eu1->g.lseg_p;
05055         if( !eg )  {
05056                 bu_log( "nmg_unbreak_handler: no geomtry for edge x%x\n" , e );
05057                 return;
05058         }
05059         NMG_CK_EDGE_G_EITHER(eg);
05060 
05061         /* if the edge geometry doesn't have at least two uses, this
05062          * is not a candidate for unbreaking */
05063         if( bu_list_len( &eg->eu_hd2 ) < 2*2 )  {
05064                 /* bu_log("nmg_unbreak_handler: usage < 4\n"); */
05065                 return;
05066         }
05067 
05068         /* Check for two consecutive uses, by looking forward. */
05069         eu2 = BU_LIST_PNEXT_CIRC( edgeuse , eu1 );
05070         NMG_CK_EDGEUSE( eu2 );
05071         if( eu2->g.lseg_p != eg )
05072         {
05073                 /* Can't look backward here, or nmg_unbreak_edge()
05074                  * will be asked to kill *this* edgeuse, which
05075                  * will blow our caller's mind.
05076                  */
05077                 /* bu_log("nmg_unbreak_handler: eu1 edge geom not shared with eu2\n"); */
05078                 return;
05079         }
05080         vb = eu2->vu_p->v_p;
05081         NMG_CK_VERTEX(vb);
05082 
05083         /* at this point, the situation is:
05084 
05085                      eu1          eu2
05086                 *----------->*----------->*
05087                 A------------B------------C
05088                 *<-----------*<-----------*
05089                     eu1mate      eu2mate
05090         */
05091         ret = nmg_unbreak_edge( eu1 );
05092         if( ret != 0 )  return;
05093 
05094         /* keep a count of unbroken edges */
05095         ub_state->unbroken++;
05096 }
05097 
05098 /**
05099  *                      N M G _ U N B R E A K _ R E G I O N _ E D G E S
05100  *
05101  *      Uses the visit handler to call nmg_unbreak_handler for
05102  *      each edgeuse below the region (or any other NMG element).
05103  *
05104  *      returns the number of edges mended
05105  */
05106 int
05107 nmg_unbreak_region_edges(long int *magic_p)
05108 {
05109         struct model *m;
05110         struct nmg_unbreak_state ub_state;
05111         int count;
05112         static const struct nmg_visit_handlers htab = {NULL, NULL, NULL, NULL, NULL,
05113                                                        NULL, NULL, NULL, NULL, NULL,
05114                                                        NULL, NULL, NULL, NULL, NULL,
05115                                                        NULL, NULL, nmg_unbreak_handler, NULL, NULL,
05116                                                        NULL, NULL, NULL, NULL, NULL};
05117         /* htab.aft_edgeuse = nmg_unbreak_handler; */
05118 
05119         if( rt_g.NMG_debug & DEBUG_BASIC )
05120                 bu_log( "nmg_unbreak_region_edges( magic_p = x%x )\n" , magic_p );
05121 
05122         m = nmg_find_model( magic_p );
05123         NMG_CK_MODEL( m );
05124 
05125         ub_state.unbroken = 0;
05126         ub_state.flags = (long *)bu_calloc( m->maxindex*2 , sizeof( long ) , "nmg_unbreak_region_edges: flags" );
05127 
05128         nmg_visit( magic_p , &htab , (genptr_t)&ub_state );
05129 
05130         count = ub_state.unbroken;
05131 
05132         bu_free( (char *)ub_state.flags , "nmg_unbreak_region_edges: flags" );
05133 
05134         return( count );
05135 }
05136 
05137 /**
05138  *                      R T _ D I S T _ P T 3 _ L I N E 3
05139  *
05140  *  Find the distance from a point P to a line described
05141  *  by the endpoint A and direction dir, and the point of closest approach (PCA).
05142  *
05143  *                      P
05144  *                     *
05145  *                    /.
05146  *                   / .
05147  *                  /  .
05148  *                 /   . (dist)
05149  *                /    .
05150  *               /     .
05151  *              *------*-------->
05152  *              A      PCA      dir
05153  *
05154  *  There are three distinct cases, with these return codes -
05155  *      0       P is within tolerance of point A.  *dist = 0, pca=A.
05156  *      1       P is within tolerance of line.  *dist = 0, pca=computed.
05157  *      2       P is "above/below" line.  *dist=|PCA-P|, pca=computed.
05158  *
05159  *
05160  * XXX For efficiency, a version of this routine that provides the
05161  * XXX distance squared would be faster.
05162 XXX This should be moved into libbn/plane.c,
05163 XXX probably named bn_dist_pt3_line3().
05164  */
05165 int
05166 rt_dist_pt3_line3(fastf_t *dist, fastf_t *pca, const fastf_t *a, const fastf_t *dir, const fastf_t *p, const struct bn_tol *tol)
05167 {
05168         vect_t  AtoP;           /* P-A */
05169         vect_t  unit_dir;       /* unitized dir vector */
05170         fastf_t A_P_sq;         /* |P-A|**2 */
05171         fastf_t t;              /* distance along ray of projection of P */
05172         fastf_t dsq;            /* sqaure of distance from p to line */
05173 
05174         if( rt_g.NMG_debug & DEBUG_BASIC )
05175                 bu_log( "rt_dist_pt3_line3( a=( %f %f %f ), dir=( %f %f %f ), p=( %f %f %f )\n" ,
05176                         V3ARGS( a ) , V3ARGS( dir ) , V3ARGS( p ) );
05177 
05178         BN_CK_TOL(tol);
05179 
05180         /* Check proximity to endpoint A */
05181         VSUB2(AtoP, p, a);
05182         if( (A_P_sq = MAGSQ(AtoP)) < tol->dist_sq )  {
05183                 /* P is within the tol->dist radius circle around A */
05184                 VMOVE( pca, a );
05185                 *dist = 0.0;
05186                 return( 0 );
05187         }
05188 
05189         VMOVE( unit_dir , dir );
05190         VUNITIZE( unit_dir );
05191 
05192         /* compute distance (in actual units) along line to PROJECTION of
05193          * point p onto the line: point pca
05194          */
05195         t = VDOT(AtoP, unit_dir);
05196 
05197         VJOIN1( pca , a , t , unit_dir );
05198         if( (dsq = A_P_sq - t*t) < tol->dist_sq )
05199         {
05200                 /* P is within tolerance of the line */
05201                 *dist = 0.0;
05202                 return( 1 );
05203         }
05204         else
05205         {
05206                 /* P is off line */
05207                 *dist = sqrt( dsq );
05208                 return( 2 );
05209         }
05210 }
05211 
05212 /**
05213  *      N M G _ M V _ S H E L L _ T O _ R E G I O N
05214  *
05215  *  Move a shell from one nmgregion to another.
05216  *  Will bomb if shell and region aren't in the same model.
05217  *
05218  *  returns:
05219  *      0 - all is well
05220  *      1 - nmgregion that gave up the shell is now empty!!!!
05221  *
05222  */
05223 int
05224 nmg_mv_shell_to_region(struct shell *s, struct nmgregion *r)
05225 {
05226         int ret_val;
05227 
05228         if( rt_g.NMG_debug & DEBUG_BASIC )
05229                 bu_log( "nmg_mv_shell_to_region( s=x%x , r=x%x )\n" , s , r );
05230 
05231         NMG_CK_SHELL( s );
05232         NMG_CK_REGION( r );
05233 
05234         if( s->r_p == r )
05235         {
05236                 bu_log( "nmg_mv_shell_to_region: Attempt to move shell to region it is already in\n" );
05237                 return( 0 );
05238         }
05239 
05240         if( nmg_find_model( &s->l.magic ) != nmg_find_model( &r->l.magic ) )
05241                 rt_bomb( "nmg_mv_shell_to_region: Cannot move shell to a different model\n" );
05242 
05243         BU_LIST_DEQUEUE( &s->l );
05244         if( BU_LIST_IS_EMPTY( &s->r_p->s_hd ) )
05245                 ret_val = 1;
05246         else
05247                 ret_val = 0;
05248 
05249         BU_LIST_APPEND( &r->s_hd , &s->l );
05250 
05251         s->r_p = r;
05252 
05253         return( ret_val );
05254 }
05255 
05256 /**     N M G _ F I N D _ I S E C T _ F A C E S
05257  *
05258  *      Find all faces that contain vertex "new_v"
05259  *      Put them in a bu_ptbl "faces"
05260  *
05261  *      returns:
05262  *              the number of faces that contain the vertex
05263  *
05264  *      and fills in the table with the faces.
05265  *      Counts edges at this vertex where radial is mate (free_edges)
05266  */
05267 int
05268 nmg_find_isect_faces(const struct vertex *new_v, struct bu_ptbl *faces, int *free_edges, const struct bn_tol *tol)
05269 {
05270         struct faceuse *fu;
05271         struct face_g_plane *fg;
05272         struct vertexuse *vu;
05273         int i;
05274         int unique;
05275 
05276         if( rt_g.NMG_debug & DEBUG_BASIC )
05277                 bu_log( "nmg_find_isect_faces( new_v=x%x , faces=x%x )\n" , new_v , faces );
05278 
05279         NMG_CK_VERTEX( new_v );
05280         BN_CK_TOL( tol );
05281         BU_CK_PTBL( faces );
05282 
05283         /* loop through vertex's vu list */
05284         for( BU_LIST_FOR( vu , vertexuse , &new_v->vu_hd ) )
05285         {
05286                 NMG_CK_VERTEXUSE( vu );
05287                 fu = nmg_find_fu_of_vu( vu );
05288                 if( fu->orientation != OT_SAME )
05289                         continue;;
05290 
05291                 NMG_CK_FACEUSE( fu );
05292                 fg = fu->f_p->g.plane_p;
05293 
05294                 /* check if this face is different from the ones on list */
05295                 unique = 1;
05296                 for( i=0 ; i<BU_PTBL_END( faces ) ; i++ )
05297                 {
05298                         struct face *fp;
05299 
05300                         fp = (struct face *)BU_PTBL_GET( faces , i );
05301                         if( fp->g.plane_p == fg || bn_coplanar( fg->N , fp->g.plane_p->N , tol ) > 0 )
05302                         {
05303                                 unique = 0;
05304                                 break;
05305                         }
05306                 }
05307 
05308                 /* if it is not already on the list, add it */
05309                 if( unique )
05310                 {
05311                         struct edgeuse *eu1;
05312 
05313                         bu_ptbl_ins( faces , (long *)fu->f_p );
05314                         /* Count the number of free edges containing new_v */
05315 
05316                         if( *vu->up.magic_p != NMG_EDGEUSE_MAGIC )
05317                                 continue;
05318 
05319                         eu1 = vu->up.eu_p;
05320                         if( eu1->eumate_p == eu1->radial_p )
05321                                 (*free_edges)++;
05322                         else
05323                         {
05324                                 eu1 = BU_LIST_PPREV_CIRC( edgeuse , eu1 );
05325                                 if( eu1->eumate_p == eu1->radial_p )
05326                                         (*free_edges)++;
05327                         }
05328                 }
05329         }
05330         return( BU_PTBL_END( faces ) );
05331 }
05332 
05333 /**     N M G _ S I M P L E _ V E R T E X _ S O L V E
05334  *
05335  *      given a vertex and a list of faces (not more than three)
05336  *      that should intersect at the vertex, calculate a new
05337  *      location for the vertex.
05338  *
05339  *      returns:
05340  *              0 - if everything is OK
05341  *              1 - failure
05342  *
05343  *      and modifies the geometry of the vertex to the new location
05344  */
05345 int
05346 nmg_simple_vertex_solve(struct vertex *new_v, const struct bu_ptbl *faces, const struct bn_tol *tol)
05347 {
05348         struct vertex_g *vg;
05349         int failed=0;
05350 
05351         if( rt_g.NMG_debug & DEBUG_BASIC )
05352         {
05353                 struct face *f;
05354                 struct faceuse *fu;
05355                 plane_t pl;
05356                 int i;
05357 
05358                 bu_log( "nmg_simple_vertex_solve( new_v=x%x , %d faces )\n" , new_v , BU_PTBL_END( faces ));
05359 
05360                 for( i=0 ; i<BU_PTBL_END( faces ) ; i++ )
05361                 {
05362                         f = (struct face *)BU_PTBL_GET( faces, i );
05363                         fu = f->fu_p;
05364                         if( fu->orientation != OT_SAME )
05365                                 fu = fu->fumate_p;
05366                         if( fu->orientation != OT_SAME )
05367                                 bu_log( "\tface (x%x) has no OT_SAME use\n", f );
05368                         NMG_GET_FU_PLANE( pl, fu );
05369                         bu_log( "\t#%d: %g %g %g %g\n", i, V4ARGS( pl ) );
05370                 }
05371         }
05372 
05373         NMG_CK_VERTEX( new_v );
05374         BU_CK_PTBL( faces );
05375         BN_CK_TOL( tol );
05376 
05377         vg = new_v->vg_p;
05378         NMG_CK_VERTEX_G( vg );
05379 
05380         switch( BU_PTBL_END( faces ) )
05381         {
05382                 struct face *fp1,*fp2,*fp3;
05383                 plane_t pl1;
05384                 fastf_t vert_move_len;
05385                 fastf_t pl_dot;
05386 
05387                 case 0:
05388                         bu_log( "nmg_simple_vertex_solve: vertex not in any face planes!!!\n" );
05389                         failed = 1;
05390                         break;
05391 
05392                 case 1:         /* just move the vertex to the plane */
05393                         fp1 = (struct face *)BU_PTBL_GET( faces , 0 );
05394                         vert_move_len = DIST_PT_PLANE( vg->coord , fp1->g.plane_p->N );
05395                         VJOIN1( vg->coord , vg->coord , -vert_move_len , fp1->g.plane_p->N );
05396                         break;
05397 
05398                 case 2:
05399                         fp1 = (struct face *)BU_PTBL_GET( faces , 0 );
05400                         fp2 = (struct face *)BU_PTBL_GET( faces , 1 );
05401 
05402                         pl_dot = VDOT( fp1->g.plane_p->N, fp2->g.plane_p->N );
05403                         if( NEAR_ZERO( pl_dot - 1.0 , tol->perp ) || NEAR_ZERO( pl_dot + 1.0, tol->perp) )
05404                         {
05405                                 vect_t move_vect;
05406 
05407                                 /* treat as a single plane */
05408                                 vert_move_len = (DIST_PT_PLANE( vg->coord , fp1->g.plane_p->N )
05409                                                 + DIST_PT_PLANE( vg->coord , fp2->g.plane_p->N ))/2.0;
05410                                 VADD2( move_vect, fp1->g.plane_p->N, fp2->g.plane_p->N );
05411                                 VUNITIZE( move_vect );
05412                                 VJOIN1( vg->coord, vg->coord, -vert_move_len, move_vect );
05413                         }
05414                         else
05415                         {
05416                                 /* create a third plane perpendicular to first two */
05417 
05418                                 VCROSS( pl1 , fp1->g.plane_p->N , fp2->g.plane_p->N );
05419 
05420                                 VUNITIZE( pl1 );
05421                                 pl1[3] = VDOT( vg->coord , pl1 );
05422                                 if( bn_mkpoint_3planes( vg->coord , fp1->g.plane_p->N , fp2->g.plane_p->N , pl1 ) )
05423                                 {
05424                                         bu_log( "nmg_simple_vertex_solve: Cannot find new coords for two planes\n" );
05425                                         bu_log( "\tplanes are ( %f %f %f %f ) and ( %f %f %f %f )\n",
05426                                                 V4ARGS( fp1->g.plane_p->N ) ,
05427                                                 V4ARGS( fp2->g.plane_p->N ) );
05428                                         bu_log( "\tcalculated third plane is ( %f %f %f %f )\n" , V4ARGS( pl1 ) );
05429                                         failed = 1;
05430                                         break;
05431                                 }
05432                         }
05433                         break;
05434 
05435                 case 3:         /* just intersect the three planes */
05436                         fp1 = (struct face *)BU_PTBL_GET( faces , 0 );
05437                         fp2 = (struct face *)BU_PTBL_GET( faces , 1 );
05438                         fp3 = (struct face *)BU_PTBL_GET( faces , 2 );
05439                         if( bn_mkpoint_3planes( vg->coord , fp1->g.plane_p->N , fp2->g.plane_p->N , fp3->g.plane_p->N ) )
05440                         {
05441                                 bu_log( "nmg_simple_vertex_solve: failed for 3 planes:\n" );
05442                                 bu_log( "\t( %f %f %f %f )\n" , V4ARGS( fp1->g.plane_p->N ) );
05443                                 bu_log( "\t( %f %f %f %f )\n" , V4ARGS( fp2->g.plane_p->N ) );
05444                                 bu_log( "\t( %f %f %f %f )\n" , V4ARGS( fp3->g.plane_p->N ) );
05445                                 failed = 1;
05446                                 break;
05447                         }
05448                         break;
05449                 default:
05450                         failed = 1;
05451                         bu_log( "nmg_simple_vertex_solve: Called for a complex vertex\n" );
05452                         break;
05453         }
05454 
05455         if( failed )
05456                 bu_log( "nmg_simple_vertex_solve: Failed to determine new coordinates for vertex at ( %f %f %f )\n",
05457                         V3ARGS( new_v->vg_p->coord ) );
05458         else if( rt_g.NMG_debug & DEBUG_BASIC )
05459                 bu_log( "nmg_simple_vertex_solve: new coords = ( %f %f %f )\n",
05460                         V3ARGS( new_v->vg_p->coord ) );
05461 
05462         return( failed );
05463 }
05464 
05465 /**     N M G _ C K _ V E R T _ O N _ F U S
05466  *
05467  *  Check all uses of a vertex to see if it lies within tolerance
05468  *  of all faces where it is used
05469  *
05470  * returns:
05471  *      0 - All is well
05472  *      1 - vertex is off face plane by at least tol->dist for at least one face
05473  */
05474 int
05475 nmg_ck_vert_on_fus(const struct vertex *v, const struct bn_tol *tol)
05476 {
05477         struct vertexuse *vu;
05478         fastf_t max_dist=0.0;
05479         int ret_val=0;
05480 
05481         NMG_CK_VERTEX( v );
05482         BN_CK_TOL( tol );
05483 
05484         NMG_CK_VERTEX_G( v->vg_p );
05485 
05486         for( BU_LIST_FOR( vu , vertexuse , &v->vu_hd ) )
05487         {
05488                 struct faceuse *fu;
05489                 fastf_t dist;
05490 
05491                 fu = nmg_find_fu_of_vu( vu );
05492                 if( !fu )
05493                         continue;
05494 
05495                 NMG_CK_FACEUSE( fu );
05496                 NMG_CK_FACE( fu->f_p );
05497                 NMG_CK_FACE_G_PLANE( fu->f_p->g.plane_p );
05498                 dist = DIST_PT_PLANE( v->vg_p->coord , fu->f_p->g.plane_p->N );
05499                 dist = (dist < 0.0 ? (-dist) : dist);
05500                 if( dist > tol->dist )
05501                 {
05502                         ret_val = 1;
05503 
05504                         if( dist > max_dist )
05505                                 max_dist = dist;
05506 
05507                         bu_log( "nmg_ck_vert_on_fus: v=x%x vu=x%x ( %f %f %f ) is %g from\n\tfaceuse x%x f x%x\n" , v , vu , V3ARGS( v->vg_p->coord ) , dist , fu , fu->f_p );
05508                 }
05509         }
05510 
05511         if( ret_val )
05512                 bu_log( "nmg_ck_vert_on_fus: v=x%x ( %f %f %f ) max distance of %g from faceuses\n" , v , V3ARGS( v->vg_p->coord ) , max_dist );
05513 
05514         return( ret_val );
05515 }
05516 
05517 /* struct used by nmg_complex_vertex_solve
05518  * to store info about one edge
05519  * that contains the vertex in question
05520  */
05521 struct intersect_fus
05522 {
05523         struct faceuse *fu[2];  /* fu's that intersect at this edge */
05524         struct edgeuse *eu;     /* edgeuse in fu[0] that emanates from vertex */
05525         point_t start;          /* calculated start point of edge line */
05526         vect_t dir;             /* calculated direction of edge line */
05527         point_t pt;             /* a point on the edge a small distance from the vertex */
05528         int got_pt;             /* flag indicating that the above point has been obtained */
05529         int free_edge;          /* flag indicating that this is a free edge */
05530         struct vertex *vp;      /* a vertex pointer for above point */
05531 };
05532 
05533 /**     N M G _ P R _ I N T E R
05534  *
05535  * debug printing of the table of intersect_fus structs used by extruder
05536  */
05537 
05538 static void
05539 nmg_pr_inter(const struct vertex *new_v, const struct bu_ptbl *int_faces)
05540 {
05541         int i;
05542         struct bn_tol tol;
05543 
05544         NMG_CK_VERTEX( new_v );
05545         BU_CK_PTBL( int_faces );
05546 
05547         tol.magic = BN_TOL_MAGIC;
05548         tol.dist = 0.005;
05549         tol.dist_sq = tol.dist * tol.dist;
05550         tol.perp = 1e-6;
05551         tol.para = 1 - tol.perp;
05552 
05553         bu_log( "\nint_faces at vertex x%x ( %f %f %f )\n" , new_v , V3ARGS( new_v->vg_p->coord ) );
05554         for( i=0 ; i<BU_PTBL_END( int_faces ) ; i++ )
05555         {
05556                 struct intersect_fus *i_fus;
05557                 struct face *fp1,*fp2;
05558                 plane_t pl;
05559 
05560                 i_fus = (struct intersect_fus *)BU_PTBL_GET( int_faces , i );
05561 
05562                 bu_log( "edge number %d, x%x\n" , i , i_fus );
05563                 if( i_fus->fu[0] )
05564                         fp1 = i_fus->fu[0]->f_p;
05565                 else
05566                         fp1 = NULL;
05567                 if( i_fus->fu[1] )
05568                 {
05569                         fp2 = i_fus->fu[1]->f_p;
05570                         NMG_GET_FU_PLANE( pl , i_fus->fu[1] );
05571                 }
05572                 else
05573                         fp2 = NULL;
05574 
05575                 if( i_fus->fu[1] )
05576                         bu_log( "\tfu1 = x%x (face=x%x), fu2 = x%x (face=x%x) ( %f %f %f %f )\n" , i_fus->fu[0] , fp1 , i_fus->fu[1] , fp2 , V4ARGS( pl ) );
05577                 else
05578                         bu_log( "\tfu1 = x%x (face=x%x), fu2 = x%x (face=x%x)\n" , i_fus->fu[0] , fp1 , i_fus->fu[1] , fp2 );
05579 
05580                 if( i_fus->eu == NULL )
05581                         bu_log( "\teu = NULL\n" );
05582                 else if( i_fus->eu->vu_p == NULL )
05583                         bu_log( "\teu = x%x , vu_p = NULL\n" , i_fus->eu );
05584                 else
05585                 {
05586                         struct faceuse *fu;
05587 
05588                         bu_log( "\teu = x%x, from x%x ( %f %f %f ) to x%x ( %f %f %f )\n" , i_fus->eu,
05589                                 i_fus->eu->vu_p->v_p , V3ARGS( i_fus->eu->vu_p->v_p->vg_p->coord ),
05590                                 i_fus->eu->eumate_p->vu_p->v_p , V3ARGS( i_fus->eu->eumate_p->vu_p->v_p->vg_p->coord ) );
05591                         if( i_fus->fu[0] )
05592                         {
05593                                 fu = nmg_find_fu_of_eu( i_fus->eu );
05594                                 if( fu != i_fus->fu[0] )
05595                                         bu_log( "****ERROR**** eu is not in fu1 it's in x%x\n" , fu );
05596                         }
05597                         else
05598                         {
05599                                 fu = nmg_find_fu_of_eu( i_fus->eu );
05600                                 if( fu != i_fus->fu[1] )
05601                                         bu_log( "****ERROR**** eu is not in fu2, it's in x%x\n" , fu );
05602                         }
05603 #if 0
05604                         /* XXXX sometimes this gives a zero length edge error in spite of the check!! */
05605                         if( !bn_pt3_pt3_equal( i_fus->eu->vu_p->v_p->vg_p->coord, i_fus->eu->eumate_p->vu_p->v_p, &tol ) )
05606                                 nmg_pr_fu_around_eu( i_fus->eu , &tol );
05607 #endif
05608                 }
05609 
05610                 bu_log( "\tstart = ( %f %f %f ) , dir = ( %f %f %f )\n" , V3ARGS( i_fus->start ) , V3ARGS( i_fus->dir ) );
05611                 bu_log( "\tpt = ( %f %f %f )\n" , V3ARGS( i_fus->pt ) );
05612                 bu_log( "\tfree_edge = %d\n" , i_fus->free_edge );
05613                 if( i_fus->eu && i_fus->eu->vu_p )
05614                 {
05615                         if( i_fus->eu->eumate_p != i_fus->eu->radial_p &&
05616                             i_fus->free_edge )
05617                                 bu_log( "****ERROR**** this is NOT a free edge\n" );
05618                         if( i_fus->eu->eumate_p == i_fus->eu->radial_p &&
05619                             !i_fus->free_edge )
05620                                 bu_log( "****ERROR**** This is a free edge\n" );
05621                 }
05622                 if( i_fus->vp )
05623                         bu_log( "\tvp = x%x ( %f %f %f )\n" , i_fus->vp , V3ARGS( i_fus->vp->vg_p->coord ) );
05624                 else
05625                         bu_log( "\tvp = NULL\n" );
05626         }
05627 }
05628 
05629 /**     N M G _ G E T _ E D G E _ L I N E S
05630  *
05631  * Fill in the intersect_fus structures for edges around
05632  * new_v. Does not fill in "pt" or "vp".
05633  *
05634  *      returns:
05635  *              0 - All is well
05636  *              1 - Failure
05637  */
05638 
05639 static int
05640 nmg_get_edge_lines(struct vertex *new_v, struct bu_ptbl *int_faces, const struct bn_tol *tol)
05641 {
05642         struct vertex_g *vg;
05643         struct vertexuse *vu;
05644         struct edgeuse *eu,*eu1;
05645         struct faceuse *fu;
05646         struct model *m;
05647         struct nmgregion *r;
05648         struct bn_tol tol_tmp;
05649         int done=0;
05650         int edge_no;
05651 
05652         NMG_CK_VERTEX( new_v );
05653         vg = new_v->vg_p;
05654         NMG_CK_VERTEX_G( vg );
05655         BN_CK_TOL( tol );
05656         BU_CK_PTBL( int_faces );
05657 
05658         if( rt_g.NMG_debug & DEBUG_BASIC )
05659                 bu_log( "nmg_get_edge_lines( new_v=x%x , int_faces=x%x )\n" , new_v , int_faces );
05660 
05661         /* A temporary tolerance struct for times when I don't want tolerancing */
05662         tol_tmp.magic = BN_TOL_MAGIC;
05663         tol_tmp.dist = 0.0;
05664         tol_tmp.dist_sq = 0.0;
05665         tol_tmp.perp = 0.0;
05666         tol_tmp.para = 1.0;
05667 
05668         m = nmg_find_model( &new_v->magic );
05669         NMG_CK_MODEL( m );
05670         r = BU_LIST_FIRST( nmgregion , &m->r_hd );
05671         NMG_CK_REGION( r );
05672         NMG_CK_REGION_A( r->ra_p );
05673 
05674         /* look for a dangling edge emanating from this vertex */
05675         eu1 = (struct edgeuse *)NULL;
05676         for( BU_LIST_FOR( vu , vertexuse , &new_v->vu_hd ) )
05677         {
05678                 if( *vu->up.magic_p != NMG_EDGEUSE_MAGIC )
05679                         continue;
05680 
05681                 eu = vu->up.eu_p->eumate_p;
05682                 fu = nmg_find_fu_of_eu( eu );
05683                 if( !fu )
05684                         continue;
05685 
05686                 if( fu->orientation != OT_SAME )
05687                         continue;
05688 
05689                 if( eu->eumate_p == eu->radial_p )
05690                 {
05691                         /* found a dangling edge, start processing here */
05692                         plane_t pl;
05693                         struct intersect_fus *i_fus;
05694 
05695                         /* create and initialize an intersect_fus struct for this edge */
05696                         i_fus = (struct intersect_fus *)bu_malloc( sizeof( struct intersect_fus ) , "nmg_get_edge_lines: i_fus" );
05697                         i_fus->fu[0] = NULL;
05698                         i_fus->fu[1] = fu;
05699                         i_fus->eu = eu;
05700                         i_fus->vp = (struct vertex *)NULL;
05701                         VSET( i_fus->pt , 0.0 , 0.0 , 0.0 );
05702                         i_fus->got_pt = 0;
05703                         i_fus->free_edge = 1;
05704                         eu1 = BU_LIST_PNEXT_CIRC( edgeuse , &eu->l );
05705 
05706                         VSUB2( i_fus->dir , eu->vu_p->v_p->vg_p->coord , eu->eumate_p->vu_p->v_p->vg_p->coord );
05707                         VUNITIZE( i_fus->dir );
05708                         NMG_GET_FU_PLANE( pl , fu );
05709                         VJOIN1( i_fus->start , vg->coord , (-DIST_PT_PLANE( vg->coord , pl )) , pl );
05710 
05711                         /* Save this info in the int_faces table */
05712                         bu_ptbl_ins( int_faces , (long *)i_fus );
05713 
05714                         break;
05715                 }
05716         }
05717 
05718         if( !eu1 )
05719         {
05720                 int found_start=0;
05721 
05722                 /* get the an edgeuse emanating from new_v */
05723                 for( BU_LIST_FOR( vu , vertexuse , &new_v->vu_hd ) )
05724                 {
05725                         NMG_CK_VERTEXUSE( vu );
05726                         if( *vu->up.magic_p != NMG_EDGEUSE_MAGIC )
05727                                 continue;
05728 
05729                         eu1 = vu->up.eu_p;
05730 
05731                         fu = nmg_find_fu_of_eu( eu1 );
05732                         NMG_CK_FACEUSE( fu );
05733 
05734                         if( fu->orientation == OT_SAME )
05735                         {
05736                                 found_start = 1;
05737                                 break;
05738                         }
05739                 }
05740                 if( !found_start )
05741                 {
05742                         bu_log( "Cannot find edgeuse in OT_SAME faceuse starting at ( %f %f %f )\n",
05743                                 V3ARGS( new_v->vg_p->coord ) );
05744                         return( 1 );
05745                 }
05746         }
05747 
05748         eu = eu1;
05749 
05750         /* loop through all the edges emanating from new_v */
05751         while( !done )
05752         {
05753                 fastf_t dist;
05754                 point_t start;
05755                 vect_t dir;
05756                 vect_t eu_dir;
05757                 int ret_val;
05758                 struct intersect_fus *i_fus;
05759                 struct faceuse *fu1,*fu2;
05760 
05761                 NMG_CK_EDGEUSE( eu );
05762 
05763                 if( eu->vu_p->v_p != new_v )
05764                 {
05765                         /* This can happen if the faces of the shell are not properly
05766                          * oriented such as might happen when an object is incorrectly
05767                          * modelled in FASTGEN and run through the patch-g converter
05768                          */
05769                         bu_log( "nmg_get_edge_lines: Bad solid!!!\n" );
05770                         for( edge_no=0 ; edge_no<BU_PTBL_END( int_faces ) ; edge_no++ )
05771                         {
05772                                 struct intersect_fus *i_fus;
05773 
05774                                 i_fus = (struct intersect_fus *)BU_PTBL_GET( int_faces , edge_no );
05775 
05776                                 bu_free( (char *)i_fus , "nmg_get_edge_lines: i_fus" );
05777                         }
05778                         return( 1 );
05779                 }
05780 
05781                 /* get the direction of the original edge (away from the vertex) */
05782                 VSUB2( eu_dir , eu->eumate_p->vu_p->v_p->vg_p->coord , eu->vu_p->v_p->vg_p->coord );
05783 
05784                 /* get the two faces that intersect along this edge */
05785                 fu1 = nmg_find_fu_of_eu( eu );
05786                 fu2 = nmg_find_fu_of_eu( eu->radial_p );
05787 
05788                 /* initialize the intersect structure for this edge */
05789                 i_fus = (struct intersect_fus *)bu_malloc( sizeof( struct intersect_fus ) , "nmg_inside_vert: intersection list" );
05790                 i_fus->fu[0] = fu1;
05791                 if( eu->radial_p == eu->eumate_p )
05792                 {
05793                         i_fus->fu[1] = (struct faceuse *)NULL;
05794                         i_fus->free_edge = 1;
05795                         done = 1;
05796                 }
05797                 else
05798                 {
05799                         i_fus->fu[1] = fu2;
05800                         i_fus->free_edge = 0;
05801                 }
05802                 i_fus->eu = eu;
05803                 i_fus->vp = (struct vertex *)NULL;
05804                 VSET( i_fus->pt , 0.0 , 0.0 , 0.0 );
05805                 i_fus->got_pt = 0;
05806                 VSET( i_fus->start , 0.0 , 0.0 , 0.0 );
05807                 VSET( i_fus->dir , 0.0 , 0.0 , 0.0 );
05808 
05809                 /* if edge is between loops of same face , don't calculate an edge line */
05810                 if( fu1->f_p != fu2->f_p )
05811                 {
05812                         /* find the new edge line at the intersection of these two faces
05813                          * the line is defined by start and dir */
05814 
05815                         ret_val = bn_isect_2planes( start, dir,
05816                                                     fu1->f_p->g.plane_p->N,
05817                                                     fu2->f_p->g.plane_p->N,
05818                                                     new_v->vg_p->coord,
05819                                                     &tol_tmp );
05820                         if( ret_val )
05821                         {
05822                                 /* Cannot find line for this edge */
05823                                 bu_log( "nmg_inside_vert: Cannot find new edge between two planes\n" );
05824                                 bu_log( "return from bn_isect_2planes is %d\n" , ret_val );
05825                                 bu_log( "\tplanes are ( %f %f %f %f ) and ( %f %f %f %f )\n" ,
05826                                         V4ARGS( fu1->f_p->g.plane_p->N ),
05827                                         V4ARGS( fu2->f_p->g.plane_p->N ) );
05828                                 bu_log( "\tfus x%x and x%x, faces x%x and x%x\n" ,
05829                                         fu1, fu2, fu1->f_p, fu2->f_p );
05830                                 nmg_pr_fu_briefly( fu1 , "fu1: " );
05831                                 nmg_pr_fu_briefly( fu2 , "fu2: " );
05832                                 rt_bomb( "Can't find plane intersection\n" );
05833                         }
05834                         /* Make the start point at closest approach to old vertex */
05835                         (void)rt_dist_pt3_line3( &dist , start , start , dir , new_v->vg_p->coord , tol );
05836 
05837                         /* Make sure the calculated direction is away from the vertex */
05838                         if( VDOT( eu_dir , dir ) < 0.0 )
05839                                 VREVERSE( dir , dir );
05840                         VMOVE( i_fus->start , start );
05841                         VMOVE( i_fus->dir , dir );
05842                 }
05843                 else if( i_fus->free_edge )
05844                 {
05845                         plane_t pl;
05846 
05847                         /* for a dangling edge, use the same direction as the original edge
05848                          * just move the start point to the new plane
05849                          */
05850 
05851                         NMG_GET_FU_PLANE( pl , fu1 );
05852 
05853                         VMOVE( i_fus->dir , eu_dir );
05854                         VUNITIZE( i_fus->dir );
05855 
05856                         VJOIN1( i_fus->start , vg->coord , (-DIST_PT_PLANE( vg->coord , pl )) , pl );
05857 
05858                 }
05859 
05860                 /* Save this info in the int_faces table */
05861                 bu_ptbl_ins( int_faces , (long *)i_fus );
05862 
05863                 if( !done )
05864                 {
05865                         /* move on to the next edge emanating from new_v */
05866                         eu = eu->radial_p;
05867                         eu = BU_LIST_PNEXT_CIRC( edgeuse , eu );
05868                         if( eu == eu1 )
05869                                 done = 1;
05870                 }
05871         }
05872         if( rt_g.NMG_debug & DEBUG_BASIC )
05873         {
05874                 bu_log( "After getting edge lines:\n" );
05875                 nmg_pr_inter( new_v , int_faces );
05876         }
05877 
05878         return( 0 );
05879 }
05880 
05881 /**     N M G _ G E T _ M A X _ E D G E _ I N T E R S
05882  *
05883  * Fill in the "pt" portion of the "intersect_fus" structure
05884  * for edges around new_v by calculating the intersection with neighboring
05885  * edges and selecting the furthest one from new_v.
05886  */
05887 static int
05888 nmg_get_max_edge_inters(const struct vertex *new_v, struct bu_ptbl *int_faces, const struct bu_ptbl *faces, const struct bn_tol *tol)
05889 {
05890         struct model *m;
05891         struct nmgregion *r;
05892         int edge_no;
05893 
05894         if( rt_g.NMG_debug & DEBUG_BASIC )
05895                 bu_log( "nmg_get_max_edge_inters( new_v = x%x , %d intersect_fus structs , %d faces )\n" , new_v , BU_PTBL_END( int_faces ) , BU_PTBL_END( faces ) );
05896 
05897         NMG_CK_VERTEX( new_v );
05898         BN_CK_TOL( tol );
05899         BU_CK_PTBL( int_faces );
05900 
05901         m = nmg_find_model( &new_v->magic );
05902         NMG_CK_MODEL( m );
05903         r = BU_LIST_FIRST( nmgregion , &m->r_hd );
05904         NMG_CK_REGION( r );
05905         NMG_CK_REGION_A( r->ra_p );
05906 
05907         /* loop through edges departing from new_v */
05908         for( edge_no=0 ; edge_no<BU_PTBL_END( int_faces ) ; edge_no++ )
05909         {
05910                 struct intersect_fus *edge_fus,*other_fus;
05911                 fastf_t max_dist,dist[2];
05912                 int next_edge_no,prev_edge_no;
05913                 int other_index;
05914 
05915                 edge_fus = (struct intersect_fus *)BU_PTBL_GET( int_faces , edge_no );
05916 
05917                 /* don't calculate intersect point for edge between two loops of same face */
05918                 if( edge_fus->fu[0] && edge_fus->fu[1] &&
05919                          edge_fus->fu[0]->f_p == edge_fus->fu[1]->f_p )
05920                                 continue;
05921 
05922                 /* Find intersections with neighboring edges and keep the one
05923                  * furthest up the edge
05924                  */
05925                 max_dist = (-MAX_FASTF);
05926 
05927                 /* start with next edge */
05928                 next_edge_no = edge_no + 1;
05929                 if( next_edge_no == BU_PTBL_END( int_faces ) )
05930                         next_edge_no = 0;
05931 
05932                 other_fus = (struct intersect_fus *)BU_PTBL_GET( int_faces , next_edge_no );
05933 
05934                 /* skip over edges betwen loops of same face */
05935                 while( other_fus->fu[0] == other_fus->fu[1] && other_fus != edge_fus )
05936                 {
05937                         next_edge_no++;
05938                         if( next_edge_no == BU_PTBL_END( int_faces ) )
05939                                 next_edge_no = 0;
05940 
05941                         other_fus = (struct intersect_fus *)BU_PTBL_GET( int_faces , next_edge_no );
05942 
05943                 }
05944 
05945                 /* if we found another edge, calculate its intersection with the edge */
05946                 if( other_fus != edge_fus )
05947                 {
05948                         if( !rt_dist_line3_line3( dist , edge_fus->start , edge_fus->dir , other_fus->start , other_fus->dir , tol ) )
05949                         {
05950                                 if( rt_g.NMG_debug & DEBUG_BASIC )
05951                                         bu_log( "Edge #%d intersects edge #%d at dist = %f\n" , edge_no , next_edge_no , dist[0] );
05952                                 if( NEAR_ZERO( dist[0] , tol->dist ) )
05953                                         dist[0] = 0.0;
05954                                 if( dist[0] > max_dist )
05955                                         max_dist = dist[0];
05956                         }
05957                 }
05958 
05959                 /* now check the previous neighboring edge */
05960                 prev_edge_no = edge_no - 1;
05961                 if( prev_edge_no < 0 )
05962                         prev_edge_no = BU_PTBL_END( int_faces ) - 1;
05963 
05964                 other_fus = (struct intersect_fus *)BU_PTBL_GET( int_faces , prev_edge_no );
05965 
05966                 while( other_fus->fu[0] == other_fus->fu[1] && other_fus != edge_fus )
05967                 {
05968                         prev_edge_no--;
05969                         if( prev_edge_no < 0 )
05970                                 prev_edge_no = BU_PTBL_END( int_faces ) - 1;
05971 
05972                         other_fus = (struct intersect_fus *)BU_PTBL_GET( int_faces , prev_edge_no );
05973                 }
05974 
05975                 if( other_fus != edge_fus )
05976                 {
05977                         if( rt_dist_line3_line3( dist , edge_fus->start , edge_fus->dir , other_fus->start , other_fus->dir , tol ) >= 0 )
05978                         {
05979                                 if( rt_g.NMG_debug & DEBUG_BASIC )
05980                                         bu_log( "Edge #%d intersects edge #%d at dist = %f\n" , edge_no , prev_edge_no , dist[0] );
05981                                 if( NEAR_ZERO( dist[0] , tol->dist ) )
05982                                         dist[0] = 0.0;
05983                                 if( dist[0] > max_dist )
05984                                         max_dist = dist[0];
05985                         }
05986                 }
05987 
05988                 if( max_dist < 0.0 )
05989                 {
05990                         /* Now check for intersections with other planes */
05991                         for( other_index=0 ; other_index<BU_PTBL_END( int_faces ) ; other_index ++ )
05992                         {
05993                                 struct face *f;
05994 
05995                                 if( other_index == edge_no )
05996                                         continue;
05997 
05998                                 other_fus = (struct intersect_fus *)BU_PTBL_GET( int_faces , other_index );
05999 
06000                                 if( !other_fus->fu[0] )
06001                                         continue;
06002 
06003                                 NMG_CK_FACEUSE( other_fus->fu[0] );
06004                                 f = other_fus->fu[0]->f_p;
06005 
06006                                 if( edge_fus->fu[0] && f == edge_fus->fu[0]->f_p )
06007                                         continue;
06008 
06009                                 if( edge_fus->fu[1] && f == edge_fus->fu[1]->f_p )
06010                                         continue;
06011 
06012                                 /* Do not intersect with a plane that this edge is parallel to */
06013                                 if( NEAR_ZERO( VDOT( f->g.plane_p->N , edge_fus->dir ) , tol->perp ) )
06014                                         continue;
06015 
06016                                 if( bn_isect_line3_plane( &dist[0] , edge_fus->start , edge_fus->dir , f->g.plane_p->N , tol ) > 1 )
06017                                         continue;
06018 
06019                                 if( rt_g.NMG_debug & DEBUG_BASIC )
06020                                         bu_log( "Edge #%d intersects fu[0] from edge #%d at dist = %f\n" , edge_no , other_index , dist[0] );
06021 
06022                                 if( NEAR_ZERO( dist[0] , tol->dist ) )
06023                                         dist[0] = 0.0;
06024 
06025                                 if( dist[0] > max_dist )
06026                                         max_dist = dist[0];
06027                         }
06028                 }
06029 
06030                 /* if any intersections have been found, save the point in edge_fus->pt */
06031                 if( max_dist > (-MAX_FASTF) )
06032                 {
06033                         VJOIN1( edge_fus->pt , edge_fus->start , max_dist , edge_fus->dir );
06034                         edge_fus->got_pt = 1;
06035                 }
06036         }
06037 
06038         /* if no intersection was found, just use the edge-line start point */
06039         for( edge_no=0 ; edge_no < BU_PTBL_END( int_faces ) ; edge_no++ )
06040         {
06041                 struct intersect_fus *edge_fus;
06042 
06043                 edge_fus = (struct intersect_fus *)BU_PTBL_GET( int_faces , edge_no );
06044                 if( !edge_fus->got_pt )
06045                         VMOVE( edge_fus->pt , edge_fus->start )
06046         }
06047 
06048         if( rt_g.NMG_debug & DEBUG_BASIC )
06049         {
06050                 bu_log( "After nmg_get_max_edge_inters:\n" );
06051                 nmg_pr_inter( new_v , int_faces );
06052         }
06053 
06054         return( 0 );
06055 }
06056 
06057 /**     N M G _ F U S E _ I N T E R S
06058  *
06059  * eliminate "j_fus" from the table "int_faces" and
06060  * adjust the info in "i_fus".
06061  * This is done when the "vp" vertices of the two structures
06062  * have been joined.
06063  */
06064 static void
06065 nmg_fuse_inters(struct intersect_fus *i_fus, struct intersect_fus *j_fus, struct bu_ptbl *int_faces, const struct bn_tol *tol)
06066 {
06067         struct edgeuse *radial_eu;
06068         struct edgeuse *prev_eu;
06069 
06070         BU_CK_PTBL( int_faces );
06071         BN_CK_TOL( tol );
06072 
06073         if( rt_g.NMG_debug & DEBUG_BASIC )
06074                 bu_log( "nmg_fuse_inters: i_fus=x%x, j_fus=x%x, %d edges\n" , i_fus, j_fus, BU_PTBL_END( int_faces ) );
06075 
06076         /* remember the radial edge of the structure to be deleted */
06077         radial_eu = j_fus->eu->radial_p;
06078 
06079         /* if the vertices have been joined prev_eu and j_fus->eu should be adjacent */
06080         prev_eu = BU_LIST_PPREV_CIRC( edgeuse , &j_fus->eu->l );
06081 
06082         if( EDGESADJ( prev_eu , j_fus->eu ) )
06083         {
06084                 nmg_keu( prev_eu );
06085                 nmg_keu( j_fus->eu );
06086         }
06087         else
06088                 bu_log( "nmg_fuse_inter_verts: ERROR: can't find adjacent edges to kill\n" );
06089 
06090         /* the other face for this edge is now j_fus->fu[1] */
06091         i_fus->fu[1] = j_fus->fu[1];
06092 
06093         /* if there are other faces along the edges that have been brought together
06094          * do a radial join
06095          */
06096         if( i_fus->fu[0] && j_fus->fu[1] )
06097         {
06098                 if( rt_g.NMG_debug & DEBUG_BASIC )
06099                 {
06100                         bu_log( "radial join of eu's x%x and x%x\n" , i_fus->eu , radial_eu );
06101                         bu_log( "\tx%x to x%x and x%x to x%x\n" ,
06102                                 i_fus->eu->vu_p->v_p, i_fus->eu->eumate_p->vu_p->v_p,
06103                                 radial_eu->vu_p->v_p, radial_eu->eumate_p->vu_p->v_p );
06104                 }
06105                 nmg_radial_join_eu( i_fus->eu , radial_eu , tol );
06106         }
06107 
06108         /* If this is a dangling edge, need to adjust the eu pointer */
06109         if( !i_fus->fu[0] )
06110                 i_fus->eu = radial_eu;
06111         NMG_CK_EDGEUSE( i_fus->eu );
06112 
06113         /* if the deleted structure was for a dangling edge,
06114          * then this edge is now dangling
06115          */
06116         if( j_fus->free_edge )
06117                 i_fus->free_edge = 1;
06118 
06119         bu_ptbl_rm( int_faces , (long *)j_fus );
06120         bu_free( (char *)j_fus , "nmg_split_edges_at_pts: j_fus " );
06121 
06122 }
06123 
06124 /**     N M G _ S P L I T _ E D G E S _ A T _ P T S
06125  *
06126  * Using the info in the table of intersect_fus structs,
06127  * split the edgeuse (eu) in each struct at the point (pt)
06128  * store the new vertices in the structure (vp) and assign
06129  * the geometry.
06130  *
06131  */
06132 static void
06133 nmg_split_edges_at_pts(const struct vertex *new_v, struct bu_ptbl *int_faces, const struct bn_tol *tol)
06134 {
06135         int edge_no;
06136 
06137         if( rt_g.NMG_debug & DEBUG_BASIC )
06138                 bu_log( "nmg_split_edges_at_pts( new_v = x%x , %d intersect_fus structs)\n" , new_v , BU_PTBL_END( int_faces ) );
06139 
06140         BN_CK_TOL( tol );
06141         BU_CK_PTBL( int_faces );
06142         NMG_CK_VERTEX( new_v );
06143 
06144         /* loop through all edges departing from new_v */
06145         for( edge_no=0 ; edge_no < BU_PTBL_END( int_faces ) ; edge_no++ )
06146         {
06147                 struct intersect_fus *i_fus;
06148                 struct edgeuse *new_eu;
06149 
06150                 i_fus = (struct intersect_fus *)BU_PTBL_GET( int_faces , edge_no );
06151 
06152                 /* skip edges between two loops of same face, for now */
06153                 if( i_fus->fu[0] && i_fus->fu[1] && i_fus->fu[0]->f_p == i_fus->fu[1]->f_p )
06154                         continue;
06155 
06156                 if( bn_pt3_pt3_equal( new_v->vg_p->coord , i_fus->pt , tol ) )
06157                 {
06158                         /* if pt is within tolerance of new_v, don't split the edge */
06159                         i_fus->vp = (struct vertex *)NULL;
06160                         VMOVE( i_fus->pt , new_v->vg_p->coord );
06161                         VMOVE( i_fus->start , new_v->vg_p->coord );
06162                         VSUB2( i_fus->dir , i_fus->eu->eumate_p->vu_p->v_p->vg_p->coord , i_fus->eu->vu_p->v_p->vg_p->coord );
06163                         VUNITIZE( i_fus->dir );
06164                         continue;
06165                 }
06166                 new_eu = nmg_esplit( i_fus->vp , i_fus->eu, 0 );
06167                 i_fus->vp = new_eu->vu_p->v_p;
06168 
06169                 /* Need to keep track of correct eu in this case */
06170                 if( i_fus->free_edge && !i_fus->fu[0] )
06171                         i_fus->eu = new_eu;
06172 
06173                 /* Assign geometry to the new vertex */
06174                 if( i_fus && !i_fus->vp->vg_p )
06175                         nmg_vertex_gv( i_fus->vp , i_fus->pt );
06176         }
06177         if( rt_g.NMG_debug & DEBUG_BASIC )
06178         {
06179                 bu_log( "After splitting edges:\n" );
06180                 nmg_pr_inter( new_v , int_faces );
06181         }
06182 
06183         /* Now take care of edges between two loops of same face */
06184         edge_no = 0;
06185         while( edge_no < BU_PTBL_END( int_faces ) )
06186         {
06187                 int next_edge_no;
06188                 struct intersect_fus *i_fus,*j_fus;
06189 
06190                 next_edge_no = edge_no + 1;
06191                 if( next_edge_no == BU_PTBL_END( int_faces ) )
06192                         next_edge_no = 0;
06193 
06194                 i_fus = (struct intersect_fus *)BU_PTBL_GET( int_faces , edge_no );
06195                 j_fus = (struct intersect_fus *)BU_PTBL_GET( int_faces , next_edge_no );
06196 
06197                 /* look at all edges in the same face as i_fus->fu[1] */
06198                 while( j_fus->fu[0] && j_fus->fu[1] &&
06199                        j_fus->fu[0]->f_p == j_fus->fu[1]->f_p &&
06200                        j_fus != i_fus )
06201                 {
06202                         /* if both edges are dangling, there is nothing to do */
06203                         if( i_fus->free_edge && j_fus->free_edge )
06204                                 break;
06205 
06206                         /* if we haven't assigned a vertex, skip this edge */
06207                         if( !i_fus->vp )
06208                                 break;
06209 
06210                         /* split the neighbor at the first structure's "vp"
06211                          * this moves the neighboring edge's endpoint to
06212                          * fall on the first edge.
06213                          */
06214                         (void) nmg_esplit( i_fus->vp , j_fus->eu, 0 );
06215 
06216                         /* now we can ignore this edge */
06217                         nmg_fuse_inters( i_fus , j_fus , int_faces , tol );
06218 
06219                         /* go to the next edge */
06220                         if( next_edge_no == BU_PTBL_END( int_faces ) )
06221                                 next_edge_no = 0;
06222 
06223                         j_fus = (struct intersect_fus *)BU_PTBL_GET( int_faces , next_edge_no );
06224 
06225                 }
06226                 edge_no++;
06227         }
06228         if( rt_g.NMG_debug & DEBUG_BASIC )
06229         {
06230                 bu_log( "After loops of same face\n" );
06231                 nmg_pr_inter( new_v , int_faces );
06232         }
06233 }
06234 
06235 /**     N M G _ R E M O V E _ S H O R T _ E U S _ I N T E R
06236  *
06237  * kill all zero length edgeuses in faces around new_v
06238  *
06239  */
06240 static void
06241 nmg_remove_short_eus_inter(struct vertex *new_v, struct bu_ptbl *int_faces, const struct bn_tol *tol)
06242 {
06243         int edge_no;
06244         struct vertexuse *vu;
06245 
06246         NMG_CK_VERTEX( new_v );
06247         BU_CK_PTBL( int_faces );
06248         BN_CK_TOL( tol );
06249 
06250         if( rt_g.NMG_debug & DEBUG_BASIC )
06251                 bu_log( "nmg_remove_short_eus: new_v=x%x ( %f %f %f ), %d edges\n" , new_v, V3ARGS( new_v->vg_p->coord ), BU_PTBL_END( int_faces ) );
06252 
06253         /* first join any of the "vp" in the intersect_fus structs that are
06254          * within tolerance of new-v
06255          */
06256         for( edge_no=0 ; edge_no<BU_PTBL_END( int_faces ) ; edge_no++ )
06257         {
06258                 struct intersect_fus *edge_fus;
06259 
06260                 edge_fus = (struct intersect_fus *)BU_PTBL_GET( int_faces , edge_no );
06261 
06262                 if( !edge_fus->vp )
06263                         continue;
06264 
06265                 if( !bn_pt3_pt3_equal( new_v->vg_p->coord , edge_fus->vp->vg_p->coord , tol ) )
06266                         continue;
06267 
06268                 nmg_jv( new_v , edge_fus->vp );
06269                 edge_fus->vp = new_v;
06270         }
06271 
06272         /* look at all faces around new_v */
06273         vu = BU_LIST_FIRST( vertexuse , &new_v->vu_hd );
06274         while( BU_LIST_NOT_HEAD( vu , &new_v->vu_hd ) )
06275         {
06276                 struct vertexuse *vu_next;
06277                 struct faceuse *fu;
06278                 struct loopuse *lu;
06279                 struct faceuse *bad_fu=(struct faceuse *)NULL;
06280                 int bad_loop=0;
06281 
06282                 NMG_CK_VERTEXUSE( vu );
06283 
06284                 vu_next = BU_LIST_PNEXT( vertexuse , &vu->l );
06285 
06286                 if( *vu->up.magic_p != NMG_EDGEUSE_MAGIC )
06287                 {
06288                         vu = vu_next;
06289                         continue;
06290                 }
06291 
06292                 fu = nmg_find_fu_of_vu( vu );
06293                 NMG_CK_FACEUSE( fu );
06294 
06295                 /* look at all loops in these faces */
06296                 lu = BU_LIST_FIRST( loopuse , &fu->lu_hd );
06297                 while( BU_LIST_NOT_HEAD( lu , &fu->lu_hd ) )
06298                 {
06299                         struct loopuse *lu_next;
06300                         struct edgeuse *eu;
06301 
06302                         NMG_CK_LOOPUSE( lu );
06303 
06304                         lu_next = BU_LIST_PNEXT( loopuse , &lu->l );
06305 
06306                         eu = BU_LIST_FIRST( edgeuse , &lu->down_hd );
06307                         while( BU_LIST_NOT_HEAD( eu , &lu->down_hd ) )
06308                         {
06309                                 struct edgeuse *eu_next;
06310 
06311                                 NMG_CK_EDGEUSE( eu );
06312 
06313                                 eu_next = BU_LIST_PNEXT( edgeuse , &eu->l );
06314 
06315                                 /* kill edges that are to/from same vertex */
06316                                 if( eu->vu_p->v_p == eu->eumate_p->vu_p->v_p )
06317                                 {
06318                                         while( (vu_next == eu->vu_p || vu_next == eu->eumate_p->vu_p ) &&
06319                                                 BU_LIST_NOT_HEAD( vu_next , &new_v->vu_hd ) )
06320                                                         vu_next = BU_LIST_PNEXT( vertexuse , &vu_next->l );
06321                                         while( (eu_next == eu || eu_next == eu->eumate_p) &&
06322                                                 BU_LIST_NOT_HEAD( eu_next , &lu->down_hd ) )
06323                                                         eu_next = BU_LIST_PNEXT( edgeuse , &eu_next->l );
06324 
06325                                         if( rt_g.NMG_debug & DEBUG_BASIC )
06326                                                 bu_log( "\tkilling eu x%x (x%x)\n" , eu , eu->eumate_p );
06327 
06328                                         bad_loop = nmg_keu( eu );
06329                                 }
06330                                 /* kill edges with length less than tol->dist */
06331                                 else if( bn_pt3_pt3_equal( eu->vu_p->v_p->vg_p->coord , eu->eumate_p->vu_p->v_p->vg_p->coord , tol ) )
06332                                 {
06333                                         struct edgeuse *prev_eu;
06334 
06335                                         prev_eu = BU_LIST_PPREV_CIRC( edgeuse , &eu->l );
06336                                         NMG_CK_EDGEUSE( prev_eu );
06337 
06338                                         prev_eu->eumate_p->vu_p->v_p = eu->eumate_p->vu_p->v_p;
06339 
06340                                         while( (vu_next == eu->vu_p || vu_next == eu->eumate_p->vu_p ) &&
06341                                                 BU_LIST_NOT_HEAD( vu_next , &new_v->vu_hd ) )
06342                                                         vu_next = BU_LIST_PNEXT( vertexuse , &vu_next->l );
06343                                         while( (eu_next == eu || eu_next == eu->eumate_p) &&
06344                                                 BU_LIST_NOT_HEAD( eu_next , &lu->down_hd ) )
06345                                                         eu_next = BU_LIST_PNEXT( edgeuse , &eu_next->l );
06346 
06347                                         if( rt_g.NMG_debug & DEBUG_BASIC )
06348                                                 bu_log( "\tkilling eu x%x (x%x)\n" , eu , eu->eumate_p );
06349 
06350                                         bad_loop = nmg_keu( eu );
06351                                 }
06352 
06353                                 if( bad_loop )
06354                                 {
06355                                         /* emptied a loop, so kill it */
06356                                         while( (lu_next == lu || lu_next == lu->lumate_p) &&
06357                                                 BU_LIST_NOT_HEAD( lu_next , &fu->lu_hd ) )
06358                                                         lu_next = BU_LIST_PNEXT( loopuse , &lu_next->l );
06359 
06360                                         bad_fu = nmg_find_fu_of_lu( lu );
06361                                         if( !nmg_klu( lu ) )
06362                                                 bad_fu = (struct faceuse *)NULL;
06363 
06364                                         break;
06365                                 }
06366 
06367                                 eu = eu_next;
06368                         }
06369                         if( bad_fu )
06370                         {
06371                                 /* emptied a faceuse, so kill it */
06372                                 if( nmg_kfu( bad_fu ) )
06373                                 {
06374                                         /* I can't believe I emptied the whole thing!! */
06375                                         bu_log( "nmg_remove_short_eus_inter: nmg_kfu emptied shell!!!\n" );
06376                                         break;
06377                                 }
06378                         }
06379                         lu = lu_next;
06380                 }
06381 
06382                 vu = vu_next;
06383         }
06384 }
06385 
06386 /**     N M G _ S I M P L I F Y _ I N T E R
06387  *
06388  * Eliminates adjacent intersect_fus structs with collinear edges
06389  *
06390  */
06391 static void
06392 nmg_simplify_inter(const struct vertex *new_v, struct bu_ptbl *int_faces, const struct bn_tol *tol)
06393 {
06394         int edge_no=0;
06395         int next_edge_no;
06396 
06397         if( rt_g.NMG_debug & DEBUG_BASIC )
06398                 bu_log( "nmg_simplify_inter( new_v=x%x ( %f %f %f ), int_faces=x%x)\n",
06399                         new_v, V3ARGS( new_v->vg_p->coord ) , int_faces );
06400 
06401         NMG_CK_VERTEX( new_v );
06402         BU_CK_PTBL( int_faces );
06403         BN_CK_TOL( tol );
06404 
06405         while( BU_PTBL_END( int_faces ) > 1 && edge_no < BU_PTBL_END( int_faces ) )
06406         {
06407                 struct intersect_fus *i_fus;
06408                 struct intersect_fus *j_fus;
06409 
06410                 /* get two consectutive structures */
06411                 i_fus = (struct intersect_fus *)BU_PTBL_GET( int_faces , edge_no );
06412                 next_edge_no = edge_no+1;
06413                 if( next_edge_no == BU_PTBL_END( int_faces ) )
06414                          next_edge_no = 0;
06415                 j_fus = (struct intersect_fus *)BU_PTBL_GET( int_faces , next_edge_no );
06416 
06417                 /* skip open space */
06418                 if( (i_fus->free_edge || j_fus->free_edge) && next_edge_no == 0 )
06419                 {
06420                         edge_no++;
06421                         continue;
06422                 }
06423 
06424                 /* Don't fuse free edges together */
06425                 if( i_fus->free_edge && j_fus->free_edge )
06426                 {
06427                         edge_no++;
06428                         continue;
06429                 }
06430 
06431                 /* if either vertex or edgeuse is null, skip */
06432                 if( i_fus->vp == NULL || j_fus->vp == NULL ||
06433                     i_fus->eu == NULL || j_fus->eu == NULL )
06434                 {
06435                         edge_no++;
06436                         continue;
06437                 }
06438 
06439                 /* If either vertex is new_v, skip */
06440                 if( i_fus->vp == new_v || j_fus->vp == new_v )
06441                 {
06442                         edge_no++;
06443                         continue;
06444                 }
06445 
06446                 NMG_CK_VERTEX( i_fus->vp );
06447                 NMG_CK_VERTEX( j_fus->vp );
06448                 NMG_CK_EDGEUSE( i_fus->eu );
06449                 NMG_CK_EDGEUSE( j_fus->eu );
06450 
06451                 /* if the two vertices are within tolerance,
06452                  * fuse them
06453                  */
06454                 if( i_fus->vp == j_fus->vp )
06455                 {
06456                         nmg_fuse_inters( i_fus , j_fus , int_faces , tol );
06457                         continue;
06458                 }
06459                 else if( bn_pt3_pt3_equal( i_fus->vp->vg_p->coord , j_fus->vp->vg_p->coord , tol ) )
06460                 {
06461                         nmg_jv( i_fus->vp , j_fus->vp );
06462                         nmg_fuse_inters( i_fus , j_fus , int_faces , tol );
06463                         continue;
06464                 }
06465                 else if( bn_3pts_collinear( i_fus->vp->vg_p->coord , j_fus->vp->vg_p->coord , new_v->vg_p->coord , tol ) )
06466                 {
06467                         fastf_t i_dist,j_dist;
06468                         vect_t i_dist_to_new_v,j_dist_to_new_v;
06469 
06470                         /* all three points are collinear,
06471                          * may need to split edges
06472                          */
06473 
06474                         VSUB2( i_dist_to_new_v , new_v->vg_p->coord , i_fus->vp->vg_p->coord );
06475                         VSUB2( j_dist_to_new_v , new_v->vg_p->coord , j_fus->vp->vg_p->coord );
06476 
06477                         if( VDOT( i_dist_to_new_v , j_dist_to_new_v ) < 0.0 )
06478                         {
06479                                 /* points are collinear with new_v, but in opoosit directions */
06480                                 edge_no++;
06481                                 continue;
06482                         }
06483 
06484                         i_dist = MAGSQ( i_dist_to_new_v );
06485                         j_dist = MAGSQ( j_dist_to_new_v );
06486 
06487                         if( i_dist < tol->dist_sq || j_dist < tol->dist_sq )
06488                                 rt_bomb( "nmg_simplify_inter: vertex within tolerance of new_v\n" );
06489 
06490                         if( rt_g.NMG_debug & DEBUG_BASIC )
06491                                 bu_log( "\tCollinear vertices x%x, x%x, and x%x\n",
06492                                                 new_v , i_fus->vp , j_fus->vp );
06493 
06494                         if( i_dist > j_dist && j_dist > tol->dist_sq )
06495                         {
06496                                 /* j point is closer to new_v than i point
06497                                  * split edge at j point
06498                                  */
06499 
06500                                 if( rt_g.NMG_debug & DEBUG_BASIC )
06501                                         bu_log( "\tSplitting i_fus->eu x%x at vertex x%x\n" , i_fus->eu , j_fus->vp );
06502 
06503                                 (void)nmg_esplit( j_fus->vp , i_fus->eu, 0 );
06504                                 i_fus->vp = j_fus->vp;
06505                                 nmg_fuse_inters( i_fus , j_fus , int_faces , tol );
06506 
06507                                 continue;
06508                         }
06509                         else if( j_dist > i_dist && i_dist > tol->dist_sq )
06510                         {
06511                                 /* i point is closer to new_v than j point
06512                                  * split edge at i point
06513                                  */
06514 
06515                                 if( rt_g.NMG_debug & DEBUG_BASIC )
06516                                         bu_log( "\tSplitting j_fus->eu x%x at vertex x%x\n" , j_fus->eu , i_fus->vp );
06517 
06518                                 (void)nmg_esplit( i_fus->vp , j_fus->eu, 0 );
06519                                 nmg_fuse_inters( i_fus , j_fus , int_faces , tol );
06520                                 continue;
06521                         }
06522                 }
06523                 edge_no++;
06524         }
06525         if( rt_g.NMG_debug & DEBUG_BASIC )
06526         {
06527                 bu_log( "\nAfter nmg_simplify_inter:\n" );
06528                 nmg_pr_inter( new_v , int_faces );
06529         }
06530 }
06531 
06532 /**     N M G _ M A K E _ F A C E S _ A T _ V E R T
06533  *
06534  * Make new faces around vertex new_v using info in
06535  * the table of intersect_fu structures. Each structure
06536  * contains a vertex on an edge departing new_v.
06537  * Vertices from two consecutive edges are combined with
06538  * new_v to form triangular faces around new_v
06539  *
06540  */
06541 void
06542 nmg_make_faces_at_vert(struct vertex *new_v, struct bu_ptbl *int_faces, const struct bn_tol *tol)
06543 {
06544         struct loopuse *old_lu;
06545         int edge_no=0,next_edge_no;
06546 
06547         if( rt_g.NMG_debug & DEBUG_BASIC )
06548                 bu_log( "nmg_make_faces_at_vert( x%x , %d intersect_fus structs)\n" , new_v , BU_PTBL_END( int_faces ) );
06549 
06550         NMG_CK_VERTEX( new_v );
06551         BU_CK_PTBL( int_faces );
06552         BN_CK_TOL( tol );
06553 
06554         if( BU_PTBL_END( int_faces ) == 1 )
06555         {
06556                 struct intersect_fus *i_fus;
06557 
06558                 /* only one intersect point is left, move new_v to it
06559                  * and don't make any faces
06560                  */
06561                 i_fus = (struct intersect_fus *)BU_PTBL_GET( int_faces , 0 );
06562                 if( i_fus->vp )
06563                 {
06564                         i_fus = (struct intersect_fus *)BU_PTBL_GET( int_faces , 0 );
06565 
06566                         VMOVE( new_v->vg_p->coord , i_fus->vp->vg_p->coord );
06567                         nmg_jv( new_v , i_fus->vp );
06568                 }
06569                 return;
06570         }
06571 
06572         if( BU_PTBL_END( int_faces ) == 2 )
06573         {
06574                 struct intersect_fus *i_fus,*j_fus;
06575                 point_t center_pt;
06576 
06577                 /* only two intersect points left, if they are not on free edges,
06578                  *  move new_v to the center of the connecting line. No new faces needed
06579                  */
06580                 i_fus = (struct intersect_fus *)BU_PTBL_GET( int_faces , 0 );
06581                 j_fus = (struct intersect_fus *)BU_PTBL_GET( int_faces , 1 );
06582 
06583                 if( i_fus->vp && j_fus->vp && !i_fus->free_edge && !j_fus->free_edge )
06584                 {
06585                         VCOMB2( center_pt , 0.5 , i_fus->vp->vg_p->coord , 0.5 , j_fus->vp->vg_p->coord );
06586                         VMOVE( new_v->vg_p->coord , center_pt );
06587                 }
06588                 return;
06589         }
06590 
06591         /* Need to make new faces.
06592          * loop around the vertex, looking at
06593          * pairs of adjacent edges and deciding
06594          * if a new face needs to be constructed
06595          * from the two intersect vertices and new_v
06596          */
06597         while( edge_no < BU_PTBL_END( int_faces ) )
06598         {
06599                 struct intersect_fus *i_fus;
06600                 struct intersect_fus *j_fus;
06601                 struct vertexuse *vu1,*vu2;
06602                 struct edgeuse *eu;
06603                 struct loopuse *lu;
06604                 struct loopuse *new_lu;
06605                 struct faceuse *new_fu;
06606                 struct faceuse *fu;
06607 
06608                 /* get two consectutive structures */
06609                 i_fus = (struct intersect_fus *)BU_PTBL_GET( int_faces , edge_no );
06610                 next_edge_no = edge_no+1;
06611                 if( next_edge_no == BU_PTBL_END( int_faces ) )
06612                          next_edge_no = 0;
06613                 j_fus = (struct intersect_fus *)BU_PTBL_GET( int_faces , next_edge_no );
06614 
06615                 /* Don't construct a new face across open space */
06616                 if( (i_fus->free_edge || j_fus->free_edge) && next_edge_no == 0 )
06617                 {
06618                         edge_no++;
06619                         continue;
06620                 }
06621 
06622                 /* if the two vertices are the same, no face needed */
06623                 if( i_fus->vp == j_fus->vp )
06624                 {
06625                         edge_no++;
06626                         continue;
06627                 }
06628 
06629                 /* if either vertex is null, no face needed */
06630                 if( i_fus->vp == NULL || j_fus->vp == NULL || i_fus->eu == NULL || j_fus->eu == NULL )
06631                 {
06632                         edge_no++;
06633                         continue;
06634                 }
06635 
06636                 /* Don't make faces with two vertices the same */
06637                 if( i_fus->vp == new_v || j_fus->vp == new_v )
06638                 {
06639                         edge_no++;
06640                         continue;
06641                 }
06642 
06643                 NMG_CK_VERTEX( i_fus->vp );
06644                 NMG_CK_VERTEX( j_fus->vp );
06645                 NMG_CK_EDGEUSE( i_fus->eu );
06646                 NMG_CK_EDGEUSE( j_fus->eu );
06647 
06648                 /* don't make degenerate faces */
06649                 if( bn_3pts_collinear( i_fus->vp->vg_p->coord , j_fus->vp->vg_p->coord , new_v->vg_p->coord , tol ) )
06650                 {
06651                         edge_no++;
06652                         continue;
06653                 }
06654 
06655                 /* O.K., here is where we actually start making faces.
06656                  * Find uses of the two vertices in the same loopuse
06657                  */
06658                 old_lu = j_fus->eu->up.lu_p;
06659                 vu1 = (struct vertexuse *)NULL;
06660                 vu2 = (struct vertexuse *)NULL;
06661                 for( BU_LIST_FOR( eu , edgeuse , &old_lu->down_hd ) )
06662                 {
06663                         if( eu->vu_p->v_p == i_fus->vp )
06664                                 vu1 = eu->vu_p;
06665                         else if( eu->vu_p->v_p == j_fus->vp )
06666                                 vu2 = eu->vu_p;
06667                 }
06668 
06669                 if( vu1 == NULL || vu2 == NULL )
06670                 {
06671                         bu_log( "nmg_make_faces_at_vert: ERROR: Can't find loop containing vertices x%x and x%x\n" , i_fus->vp, j_fus->vp );
06672                         bu_log( "\t( %f %f %f ) and ( %f %f %f )\n" , V3ARGS( i_fus->vp->vg_p->coord ) , V3ARGS( j_fus->vp->vg_p->coord ) );
06673                         edge_no++;
06674                         continue;
06675                 }
06676 
06677                 /* make sure the two vertices have a third between,
06678                  * otherwise, don't cut the loop
06679                  */
06680                 eu = vu1->up.eu_p;
06681                 if( eu->eumate_p->vu_p == vu2 )
06682                 {
06683                         edge_no++;
06684                         continue;
06685                 }
06686                 eu = vu2->up.eu_p;
06687                 if( eu->eumate_p->vu_p == vu1 )
06688                 {
06689                         edge_no++;
06690                         continue;
06691                 }
06692 
06693                 /* cut the face loop across the two vertices */
06694                 new_lu = nmg_cut_loop( vu1 , vu2 );
06695 
06696                 /* Fix orientations.
06697                  * We will never be cutting an OT_OPPOSITE loop
06698                  * so the will always be OT_SAME
06699                  */
06700                 new_lu->orientation = OT_SAME;
06701                 new_lu->lumate_p->orientation = OT_SAME;
06702                 old_lu->orientation = OT_SAME;
06703                 old_lu->lumate_p->orientation = OT_SAME;
06704 
06705                 /* find which loopuse contains new_v
06706                  * this will be the one to become a new face
06707                  */
06708                 lu = NULL;
06709 
06710                 /* first check old_lu */
06711                 for( BU_LIST_FOR( eu , edgeuse , &old_lu->down_hd ) )
06712                 {
06713                         if( eu->vu_p->v_p == new_v )
06714                         {
06715                                 lu = old_lu;
06716                                 break;
06717                         }
06718                 }
06719 
06720                 /* if not found check new_lu */
06721                 if( lu == NULL )
06722                 {
06723                         for( BU_LIST_FOR( eu , edgeuse , &new_lu->down_hd ) )
06724                         {
06725                                 if( eu->vu_p->v_p == new_v )
06726                                 {
06727                                         lu = old_lu;
06728                                         break;
06729                                 }
06730                         }
06731                 }
06732 
06733                 if( lu == NULL )
06734                 {
06735                         fu = old_lu->up.fu_p;
06736                         bu_log( "nmg_make_faces_at_vert: can't find loop for new face\n" );
06737                         bu_log( "vu1 = x%x (x%x) vu2 = x%x (x%x)\n" , vu1 , vu1->v_p , vu2 , vu2->v_p );
06738                         bu_log( "new_v = x%x\n" , new_v );
06739                         bu_log( "old_lu = x%x , new_lu = x%x\n" , old_lu , new_lu );
06740                         nmg_pr_fu_briefly( fu , (char *)NULL );
06741                         rt_bomb( "nmg_make_faces_at_vert: can't find loop for new face\n" );
06742                 }
06743 
06744                 /* make the new face from the new loop */
06745                 new_fu = nmg_mk_new_face_from_loop( lu );
06746 
06747                 /* update the intersect_fus structs (probably not necessary at this point) */
06748                 j_fus->fu[0] = new_fu;
06749                 i_fus->fu[1] = new_fu;
06750 
06751                 NMG_CK_FACEUSE( new_fu );
06752 
06753                 /* calculate a plane equation for the new face */
06754                 if( nmg_calc_face_g( new_fu ) )
06755                 {
06756                         bu_log( "nmg_make_faces_at_vert: Failed to calculate plane eqn for face:\n " );
06757                         bu_log( "\tnew_v is x%x at ( %f %f %f )\n" , new_v , V3ARGS( new_v->vg_p->coord ) );
06758                         if( bn_3pts_collinear( new_v->vg_p->coord,
06759                             vu1->v_p->vg_p->coord, vu2->v_p->vg_p->coord,
06760                             tol ) )
06761                                 bu_log( "\tPoints are collinear\n" );
06762                         nmg_pr_fu_briefly( new_fu , " " );
06763                 }
06764                 nmg_face_bb( new_fu->f_p , tol );
06765 
06766                 edge_no++;
06767         }
06768 }
06769 
06770 /**     N M G _ K I L L _ C R A C K S _ A T _ V E R T E X
06771  *
06772  * Look at all faces around vertex new_v and kill any two
06773  * consecutive eu's that go from a vertex to a second then back
06774  * to the original vertex
06775  */
06776 void
06777 nmg_kill_cracks_at_vertex(const struct vertex *vp)
06778 {
06779         struct bu_ptbl fus_at_vert;
06780         struct vertexuse *vu;
06781         struct faceuse *fu;
06782         int fu_no;
06783 
06784         if( rt_g.NMG_debug & DEBUG_BASIC )
06785                 bu_log( "nmg_kill_cracks_at_vertex( vp=x%x )\n" , vp );
06786 
06787         NMG_CK_VERTEX( vp );
06788 
06789         /* first make a list of all the faceuses at this vertex */
06790         bu_ptbl_init( &fus_at_vert , 64, " &fus_at_vert ");
06791 
06792         for( BU_LIST_FOR( vu , vertexuse , &vp->vu_hd ) )
06793         {
06794                 NMG_CK_VERTEXUSE( vu );
06795 
06796                 fu = nmg_find_fu_of_vu( vu );
06797                 if( !fu )
06798                         continue;
06799 
06800                 NMG_CK_FACEUSE( fu );
06801                 bu_ptbl_ins_unique( &fus_at_vert , (long *)fu );
06802         }
06803 
06804         /* Now look at these faceuses for cracks ( jaunts from a vertex and back to the same ) */
06805         for( fu_no=0 ; fu_no<BU_PTBL_END( &fus_at_vert ) ; fu_no++ )
06806         {
06807                 struct loopuse *lu;
06808                 int bad_face=0;
06809 
06810                 fu = (struct faceuse *)BU_PTBL_GET( &fus_at_vert , fu_no );
06811                 NMG_CK_FACEUSE( fu );
06812 
06813                 lu = BU_LIST_FIRST( loopuse , &fu->lu_hd );
06814                 while( BU_LIST_NOT_HEAD( lu , &fu->lu_hd ) )
06815                 {
06816                         struct loopuse *lu_next;
06817                         struct edgeuse *eu;
06818                         int bad_loop=0;
06819 
06820                         NMG_CK_LOOPUSE( lu );
06821 
06822                         lu_next = BU_LIST_NEXT( loopuse , &lu->l );
06823 
06824                         if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) != NMG_EDGEUSE_MAGIC )
06825                         {
06826                                 lu = lu_next;
06827                                 continue;
06828                         }
06829 
06830                         eu = BU_LIST_FIRST( edgeuse , &lu->down_hd );
06831                         while( BU_LIST_NOT_HEAD( eu , &lu->down_hd ) )
06832                         {
06833                                 struct edgeuse *eu_prev;
06834                                 struct edgeuse *eu_next;
06835 
06836                                 NMG_CK_EDGEUSE( eu );
06837 
06838                                 eu_next = BU_LIST_NEXT( edgeuse , &eu->l );
06839                                 eu_prev = BU_LIST_PPREV_CIRC( edgeuse , &eu->l );
06840                                 NMG_CK_EDGEUSE( eu_prev );
06841 
06842                                 /* Check for a crack */
06843                                 if( EDGESADJ( eu , eu_prev ) )
06844                                 {
06845                                         /* found a crack, kill it */
06846                                         if( nmg_keu( eu ) )
06847                                         {
06848                                                 /* This should never happen */
06849                                                 bu_log( "ERROR: nmg_kill_cracks_at_vert: bad loopuse x%x\n" , lu );
06850                                                 bad_loop = 1;
06851                                                 break;
06852                                         }
06853                                         if( nmg_keu( eu_prev ) )
06854                                         {
06855                                                 bad_loop = 1;
06856                                                 break;
06857                                         }
06858                                 }
06859                                 eu = eu_next;
06860                         }
06861                         if( bad_loop )
06862                         {
06863                                 if( nmg_klu( lu ) )
06864                                 {
06865                                         bad_face = 1;
06866                                         break;
06867                                 }
06868                         }
06869                         lu = lu_next;
06870                 }
06871                 if( bad_face )
06872                 {
06873                         if( nmg_kfu( fu ) )
06874                                 bu_log( "ERROR:nmg_kill_cracks_at_vert: bad shell!!!\n" );
06875                 }
06876         }
06877         bu_ptbl_free( &fus_at_vert );
06878 }
06879 
06880 /**     N M G _ D I S T _ T O _ C R O S S
06881  *
06882  * Used by nmg_fix_crossed edges to calculate the point
06883  * where two edges cross
06884  *
06885  *      returns:
06886  *              distance to intersection if edge intersect
06887  *              -1.0 if they don't
06888  */
06889 static fastf_t
06890 nmg_dist_to_cross(const struct intersect_fus *i_fus, const struct intersect_fus *j_fus, fastf_t *new_pt, const struct bn_tol *tol)
06891 {
06892         plane_t pl;
06893         struct edgeuse *i_next_eu,*j_next_eu;
06894         struct vertex *i_end,*j_end;
06895         struct vertex *i_start,*j_start;
06896         point_t i_end_pt,j_end_pt;
06897         vect_t i_dir,j_dir;
06898         fastf_t dist[2];
06899 
06900         BN_CK_TOL( tol );
06901 
06902         if( i_fus->fu[1] )
06903                 NMG_GET_FU_PLANE( pl , i_fus->fu[1] )
06904 
06905         /* get edgeuses leaving from new vertices */
06906         if( !i_fus->fu[0] )
06907                 i_next_eu = BU_LIST_PPREV_CIRC( edgeuse , &i_fus->eu->l );
06908         else
06909                 i_next_eu = BU_LIST_PNEXT_CIRC( edgeuse , &i_fus->eu->l );
06910 
06911         if( !j_fus->fu[0] )
06912                 j_next_eu = BU_LIST_PPREV_CIRC( edgeuse , &j_fus->eu->l );
06913         else
06914                 j_next_eu = BU_LIST_PNEXT_CIRC( edgeuse , &j_fus->eu->l );
06915 
06916         NMG_CK_EDGEUSE( i_next_eu );
06917         NMG_CK_EDGEUSE( j_next_eu );
06918 
06919         /* get endpoints for these edges */
06920         i_end = i_next_eu->eumate_p->vu_p->v_p;
06921         j_end = j_next_eu->eumate_p->vu_p->v_p;
06922 
06923         NMG_CK_VERTEX( i_end );
06924         NMG_CK_VERTEX( j_end );
06925 
06926         /* since the other end of these edges may not have been adjusted yet
06927          * project the endpoints onto the face plane
06928          */
06929         if( i_fus->fu[1] )
06930         {
06931                 VJOIN1( i_end_pt , i_end->vg_p->coord , -(DIST_PT_PLANE( i_end->vg_p->coord , pl )) , pl )
06932                 VJOIN1( j_end_pt , j_end->vg_p->coord , -(DIST_PT_PLANE( j_end->vg_p->coord , pl )) , pl )
06933         }
06934         else
06935         {
06936                 VMOVE( i_end_pt , i_end->vg_p->coord )
06937                 VMOVE( j_end_pt , j_end->vg_p->coord )
06938         }
06939 
06940         /* get start points, guaranteed to be on plane */
06941         i_start =  i_next_eu->vu_p->v_p;
06942         j_start =  j_next_eu->vu_p->v_p;
06943 
06944         NMG_CK_VERTEX( i_start );
06945         NMG_CK_VERTEX( j_start );
06946 
06947         /* calculate direction vectors for use by bn_isect_lseg3_lseg3 */
06948         VSUB2( i_dir , i_end_pt , i_start->vg_p->coord );
06949         VSUB2( j_dir , j_end_pt , j_start->vg_p->coord );
06950 
06951         if( rt_g.NMG_debug & DEBUG_BASIC )
06952         {
06953                 bu_log( "nmg_dist_to_cross: checking edges x%x and x%x:\n" , i_fus , j_fus );
06954                 bu_log( "\t( %f %f %f ) <-> ( %f %f %f )\n", V3ARGS( i_start->vg_p->coord ), V3ARGS( i_end_pt ) );
06955                 bu_log( "\t( %f %f %f ) <-> ( %f %f %f )\n", V3ARGS( j_start->vg_p->coord ), V3ARGS( j_end_pt ) );
06956         }
06957 
06958         if( i_fus->free_edge && j_fus->free_edge )
06959         {
06960                 fastf_t max_dist0;
06961                 fastf_t max_dist1;
06962                 int ret_val;
06963 
06964                 if( rt_g.NMG_debug & DEBUG_BASIC )
06965                         bu_log( "\tBoth are free edges\n" );
06966 
06967                 max_dist0 = MAGNITUDE( i_dir );
06968                 VSCALE( i_dir , i_dir , (1.0/max_dist0) )
06969                 max_dist1 = MAGNITUDE( j_dir );
06970                 VSCALE( j_dir , j_dir , (1.0/max_dist1) )
06971 
06972                 /* check if these two edges intersect or pass near each other */
06973                 if( (ret_val=rt_dist_line3_line3( dist , i_start->vg_p->coord , i_dir ,
06974                         j_start->vg_p->coord , j_dir , tol )) >= 0 )
06975                 {
06976                         if( rt_g.NMG_debug & DEBUG_BASIC )
06977                         {
06978                                 bu_log( "max_dists = %f , %f\n" , max_dist0,max_dist1 );
06979                                 bu_log( "dist = %f , %f\n" , dist[0] , dist[1] );
06980                         }
06981 
06982                         /* if the closest approach or intersect point is
06983                          * within the edge endpoints, this is a real intersection
06984                          */
06985                         if( dist[0] >= 0.0 && dist[0] <= max_dist0 &&
06986                             dist[1] >= 0.0 && dist[1] <= max_dist1 )
06987                         {
06988                                 plane_t pl1,pl2,pl3;
06989 
06990                                 if( rt_g.NMG_debug & DEBUG_BASIC )
06991                                 {
06992                                         point_t tmp_pt;
06993 
06994                                         bu_log( "\t\tintersection!!\n" );
06995                                         VJOIN1( tmp_pt , i_start->vg_p->coord , dist[0] , i_dir );
06996                                         bu_log( "\t\t\t( %f %f %f )\n" , V3ARGS( tmp_pt ) );
06997                                         VJOIN1( tmp_pt , j_start->vg_p->coord , dist[1] , j_dir );
06998                                         bu_log( "\t\t\t( %f %f %f )\n" , V3ARGS( tmp_pt ) );
06999                                 }
07000 
07001                                 /* calculate the intersect point */
07002                                 NMG_GET_FU_PLANE( pl1 , j_fus->fu[1] );
07003                                 NMG_GET_FU_PLANE( pl2 , i_fus->fu[0] );
07004                                 VCROSS( pl3 , pl1 , pl2 );
07005                                 pl3[3] = VDOT( pl3 , i_fus->vp->vg_p->coord );
07006                                 bn_mkpoint_3planes( new_pt , pl1 , pl2 , pl3 );
07007 
07008                                 return( dist[0] );
07009                         }
07010                         else
07011                                 return( (fastf_t)(-1.0) );
07012                 }
07013                 else
07014                 {
07015                         if( rt_g.NMG_debug & DEBUG_BASIC )
07016                                 bu_log( "ret_val = %d\n" , ret_val );
07017 
07018                         return( (fastf_t)(-1.0) );
07019                 }
07020         }
07021         else
07022         {
07023                 /* check if these two edges intersect */
07024                 if( bn_isect_lseg3_lseg3( dist , i_start->vg_p->coord , i_dir ,
07025                         j_start->vg_p->coord , j_dir , tol ) == 1 )
07026                 {
07027                         fastf_t len0;
07028 
07029                         len0 = MAGNITUDE( i_dir );
07030 
07031                         /* calculate intersection point */
07032                         if( dist[0] == 0.0 )
07033                                 VMOVE( new_pt , i_start->vg_p->coord )
07034                         else if( dist[0] == 1.0 )
07035                                 VMOVE( new_pt , i_end_pt )
07036                         else if( dist[1] == 0.0 )
07037                                 VMOVE( new_pt , j_start->vg_p->coord )
07038                         else if( dist[1] == 1.0 )
07039                                 VMOVE( new_pt , j_end_pt )
07040                         else
07041                                 VJOIN1( new_pt , i_start->vg_p->coord , dist[0] , i_dir )
07042 
07043                         if( rt_g.NMG_debug & DEBUG_BASIC )
07044                                 bu_log( "\tdist=%f, new_pt=( %f %f %f )\n" , dist[0] , V3ARGS( new_pt ) );
07045 
07046                         return( dist[0]*len0 );
07047                 }
07048                 else
07049                         return( (fastf_t)(-1.0) );
07050         }
07051 }
07052 
07053 /**     N M G _ F I X _ C R O S S E D _ L O O P S
07054  *
07055  * Detect situations where edges have been split, but new vertices are
07056  * in wrong order. This typically happens as shown:
07057  *
07058  *                  new face planes
07059  *                  |
07060  *                  |
07061  *   \       \   /  |    /
07062  *    \       \ /<--|   /
07063  *     \       X       /
07064  *      \     / \     /
07065  *       \   /___\   /
07066  *        \         /
07067  *         \       /<- original face planes
07068  *          \     /
07069  *           \___/
07070  *
07071  * This can be detected by checking if the edges leaving from the new
07072  * vertices cross. If so, the middle face is deleted and the
07073  * two vertices are fused.
07074  *
07075  */
07076 static void
07077 nmg_fix_crossed_loops(struct vertex *new_v, struct bu_ptbl *int_faces, const struct bn_tol *tol)
07078 {
07079         int edge_no;
07080 
07081         if( rt_g.NMG_debug & DEBUG_BASIC )
07082                 bu_log( "nmg_fix_crossed_loops( new_v=x%x ( %f %f %f ), %d edges)\n", new_v , V3ARGS( new_v->vg_p->coord ) , BU_PTBL_END( int_faces ) );
07083 
07084         NMG_CK_VERTEX( new_v );
07085         BU_CK_PTBL( int_faces );
07086         BN_CK_TOL( tol );
07087 
07088         /* first check for edges that cross both adjacent edges */
07089         if( BU_PTBL_END( int_faces ) > 2 )
07090         {
07091                 for( edge_no=0 ; edge_no<BU_PTBL_END( int_faces ) ; edge_no++ )
07092                 {
07093                         int next_edge_no,prev_edge_no;
07094                         struct intersect_fus *edge_fus;
07095                         struct intersect_fus *next_fus,*prev_fus;
07096                         fastf_t dist1,dist2;
07097                         point_t pt1,pt2;
07098 
07099                         edge_fus = (struct intersect_fus *)BU_PTBL_GET( int_faces , edge_no );
07100 
07101                         if( !edge_fus->vp )
07102                                 continue;
07103 
07104                         /* look at next edge */
07105                         next_edge_no = edge_no + 1;
07106                         if( next_edge_no == BU_PTBL_END( int_faces ) )
07107                                 next_edge_no = 0;
07108 
07109                         next_fus = (struct intersect_fus *)BU_PTBL_GET( int_faces , next_edge_no );
07110 
07111                         /* Don't want to fuse two dangling edges */
07112                         if( next_fus->vp && (!edge_fus->free_edge || !next_fus->free_edge) )
07113                                 dist1 = nmg_dist_to_cross( edge_fus , next_fus , pt1 , tol );
07114                         else
07115                                 dist1 = (-1.0);
07116 
07117                         /* look at previous edge */
07118                         prev_edge_no = edge_no - 1;
07119                         if( prev_edge_no < 0 )
07120                                 prev_edge_no = BU_PTBL_END( int_faces ) - 1;
07121 
07122                         prev_fus = (struct intersect_fus *)BU_PTBL_GET( int_faces , prev_edge_no );
07123 
07124                         /* Don't want to fuse two dangling edges */
07125                         if( prev_fus->vp && (!edge_fus->free_edge || !prev_fus->free_edge) )
07126                                 dist2 = nmg_dist_to_cross( edge_fus , prev_fus , pt2 , tol );
07127                         else
07128                                 dist2 = (-1.0);
07129 
07130                         /* if no intersections, continue */
07131                         if( dist1 < tol->dist || dist2 < tol->dist )
07132                                 continue;
07133 
07134                         if( rt_g.NMG_debug & DEBUG_BASIC )
07135                         {
07136                                 bu_log( "fus=x%x, prev=x%x, next=x%x, dist1=%f, dist2=%f\n",
07137                                         edge_fus,next_fus,prev_fus,dist1,dist2 );
07138                                 bu_log( "\t( %f %f %f ), ( %f %f %f )\n" , V3ARGS( pt1 ) , V3ARGS( pt2 ) );
07139                         }
07140 
07141                         /* if both intersections are at the same point, merge all three */
07142                         if( bn_pt3_pt3_equal( pt1 , pt2 , tol ) )
07143                         {
07144                                 if( rt_g.NMG_debug & DEBUG_BASIC )
07145                                         bu_log( "\tMerging all three points to pt1\n" );
07146 
07147                                 VMOVE( edge_fus->vp->vg_p->coord , pt1 );
07148                                 VMOVE( edge_fus->pt , pt1 );
07149                                 VMOVE( next_fus->vp->vg_p->coord , pt1 );
07150                                 VMOVE( next_fus->pt , pt1 );
07151                                 VMOVE( prev_fus->vp->vg_p->coord , pt1 );
07152                                 VMOVE( prev_fus->pt , pt1 );
07153                         }
07154                         else if( dist1 > dist2 )
07155                         {
07156                                 /* merge edge point with next edge point */
07157                                 if( rt_g.NMG_debug & DEBUG_BASIC )
07158                                         bu_log( "\tMerging edge and next to pt1, moving prev to pt2\n");
07159                                 VMOVE( edge_fus->vp->vg_p->coord , pt1 );
07160                                 VMOVE( edge_fus->pt , pt1 );
07161                                 VMOVE( next_fus->vp->vg_p->coord , pt1 );
07162                                 VMOVE( next_fus->pt , pt1 );
07163 
07164                                 VMOVE( prev_fus->vp->vg_p->coord , pt2 );
07165                                 VMOVE( prev_fus->pt , pt2 );
07166                         }
07167                         else
07168                         {
07169                                 /* merge edge point with previous point */
07170                                 if( rt_g.NMG_debug & DEBUG_BASIC )
07171                                         bu_log( "\tMerging edge and prev to pt2, moving next to pt1\n" );
07172                                 VMOVE( edge_fus->vp->vg_p->coord , pt2 );
07173                                 VMOVE( edge_fus->pt , pt2 );
07174                                 VMOVE( prev_fus->vp->vg_p->coord , pt2 );
07175                                 VMOVE( prev_fus->pt , pt2 );
07176 
07177                                 VMOVE( next_fus->vp->vg_p->coord , pt1 );
07178                                 VMOVE( next_fus->pt , pt1 );
07179                         }
07180                 }
07181         }
07182 
07183         if( rt_g.NMG_debug & DEBUG_BASIC )
07184         {
07185                 bu_log( "After fixing edges that intersect two edges:\n" );
07186                 nmg_pr_inter( new_v , int_faces );
07187         }
07188 
07189         /* now look for edges that cross just a single adjacent edge */
07190         for( edge_no=0 ; edge_no<BU_PTBL_END( int_faces ) ; edge_no++ )
07191         {
07192                 int next_edge_no;
07193                 struct intersect_fus *edge_fus;
07194                 struct intersect_fus *next_fus;
07195                 point_t pt;
07196                 fastf_t dist;
07197 
07198                 edge_fus = (struct intersect_fus *)BU_PTBL_GET( int_faces , edge_no );
07199 
07200                 if( !edge_fus->vp )
07201                         continue;
07202 
07203                 /* just look at next edge */
07204                 next_edge_no = edge_no + 1;
07205                 if( next_edge_no == BU_PTBL_END( int_faces ) )
07206                         next_edge_no = 0;
07207 
07208                 next_fus = (struct intersect_fus *)BU_PTBL_GET( int_faces , next_edge_no );
07209 
07210                 if( !next_fus->vp )
07211                         continue;
07212 
07213                 /* check for intersection */
07214                 dist = nmg_dist_to_cross( edge_fus , next_fus , pt , tol );
07215 
07216                 if( dist > tol->dist )
07217                 {
07218                         /* there is an intersection */
07219                         if( rt_g.NMG_debug & DEBUG_BASIC )
07220                         {
07221                                 bu_log( "edge x%x intersect next edge x%x\n" , edge_fus , next_fus );
07222                                 bu_log( "\tdist=%f, ( %f %f %f )\n" , dist , V3ARGS( pt ) );
07223                         }
07224                         if( edge_fus->free_edge && next_fus->free_edge )
07225                         {
07226                                 /* if both edges are free edges, move new_v to the intersection */
07227                                 VMOVE( edge_fus->vp->vg_p->coord , pt );
07228                                 VMOVE( edge_fus->pt , pt );
07229                                 VMOVE( next_fus->vp->vg_p->coord , pt );
07230                                 VMOVE( next_fus->pt , pt );
07231                                 VMOVE( new_v->vg_p->coord , pt );
07232                         }
07233                         else
07234                         {
07235                                 /* just merge the two points */
07236                                 VMOVE( edge_fus->vp->vg_p->coord , pt );
07237                                 VMOVE( edge_fus->pt , pt );
07238                                 VMOVE( next_fus->vp->vg_p->coord , pt );
07239                                 VMOVE( next_fus->pt , pt );
07240                         }
07241                 }
07242         }
07243         if( rt_g.NMG_debug & DEBUG_BASIC )
07244         {
07245                 bu_log( "After nmg_fix_crossed_loops:\n" );
07246                 nmg_pr_inter( new_v , int_faces );
07247         }
07248 }
07249 
07250 /*      N M G _ C A L C _ N E W _ V
07251  *
07252  * Calculates a new geometry for new_v
07253  */
07254 static int
07255 nmg_calc_new_v(struct vertex *new_v, const struct bu_ptbl *int_faces, const struct bn_tol *tol)
07256 {
07257         plane_t *planes;
07258         int pl_count;
07259         int i;
07260 
07261         NMG_CK_VERTEX( new_v );
07262         BU_CK_PTBL( int_faces );
07263         BN_CK_TOL( tol );
07264 
07265         if( rt_g.NMG_debug & DEBUG_BASIC )
07266                 bu_log( "nmg_calc_new_v: (%f %f %f) , %d faces\n" , V3ARGS( new_v->vg_p->coord ) , BU_PTBL_END( int_faces ) );
07267 
07268         /* make space for at least three planes */
07269         i = BU_PTBL_END( int_faces );
07270         if( i < 3 )
07271                 i = 3;
07272         planes = (plane_t *)bu_calloc( i , sizeof( plane_t ) , "nmg_calc_new_v: planes" );
07273 
07274         pl_count = 0;
07275 
07276         for( i=0 ; i<BU_PTBL_END( int_faces ) ; i++ )
07277         {
07278                 struct intersect_fus *fus;
07279                 plane_t pl;
07280                 int j;
07281                 int unique=1;
07282 
07283                 fus = (struct intersect_fus *)BU_PTBL_GET( int_faces , i );
07284 
07285                 if( !fus->fu[0] )
07286                         continue;
07287 
07288                 NMG_CK_FACEUSE( fus->fu[0] );
07289                 NMG_GET_FU_PLANE( pl , fus->fu[0] );
07290 
07291                 for( j=0 ; j<pl_count ; j++ )
07292                 {
07293                         if( bn_coplanar( planes[j] , pl , tol ) > 0 )
07294                         {
07295                                 unique = 0;
07296                                 break;
07297                         }
07298                 }
07299 
07300                 if( !unique )
07301                         continue;
07302 
07303                 VMOVE( planes[pl_count] , pl );
07304                 planes[pl_count][H] = pl[H];
07305                 pl_count++;
07306         }
07307 
07308         if( pl_count > 2 )
07309         {
07310                 if( bn_isect_planes( new_v->vg_p->coord , (const plane_t *)planes , pl_count ) )
07311                 {
07312                         bu_log( "nmg_cacl_new_v: Cannot solve for new geometry at ( %f %f %f )\n",
07313                                 V3ARGS( new_v->vg_p->coord ) );
07314                         bu_free( (char *)planes , "nmg_calc_new_v: planes" );
07315                         return( 1 );
07316                 }
07317         }
07318         else if( pl_count == 1 )
07319         {
07320                 fastf_t vert_move_len;
07321 
07322                 /* move the vertex to the plane */
07323                 vert_move_len = DIST_PT_PLANE( new_v->vg_p->coord , planes[0] );
07324                 VJOIN1( new_v->vg_p->coord , new_v->vg_p->coord , -vert_move_len , planes[0] );
07325         }
07326         else if( pl_count == 2 )
07327         {
07328                 VCROSS( planes[2] , planes[0] , planes[1] );
07329                 planes[2][H] = VDOT( new_v->vg_p->coord , planes[2] );
07330                 pl_count = 3;
07331                 if( bn_mkpoint_3planes( new_v->vg_p->coord , planes[0] , planes[1] , planes[2] ) )
07332                 {
07333                         bu_log( "nmg_cacl_new_v: 3 planes do not intersect at a point\n" );
07334                         bu_free( (char *)planes , "nmg_calc_new_v: planes" );
07335                         return( 1 );
07336                 }
07337         }
07338         else
07339         {
07340                 bu_log( "nmg_calc_new_v: No face planes at vertex x%x (%f %f %f)\n",
07341                         new_v , V3ARGS( new_v->vg_p->coord ) );
07342                 bu_free( (char *)planes , "nmg_calc_new_v: planes" );
07343                 return( 1 );
07344         }
07345 
07346         if( rt_g.NMG_debug & DEBUG_BASIC )
07347                 bu_log( "\tnew_v = ( %f %f %f )\n" , V3ARGS( new_v->vg_p->coord ) );
07348 
07349         bu_free( (char *)planes , "nmg_calc_new_v: planes" );
07350 
07351         for( i=0 ; i<BU_PTBL_END( int_faces ) ; i++ )
07352         {
07353                 struct intersect_fus *fus;
07354                 fastf_t dist;
07355 
07356                 fus = (struct intersect_fus *)BU_PTBL_GET( int_faces , i );
07357 
07358                 (void) rt_dist_pt3_line3( &dist , fus->start , fus->start , fus->dir , new_v->vg_p->coord , tol );
07359         }
07360 
07361         if( rt_g.NMG_debug & DEBUG_BASIC )
07362         {
07363                 bu_log( "After nmg_calc_new_v:\n" );
07364                 nmg_pr_inter( new_v , int_faces );
07365         }
07366 
07367         return( 0 );
07368 }
07369 
07370 /**     N M G _ C O M P L E X _ V E R T E X _ S O L V E
07371  *
07372  *      This is intended to handle the cases the "nmg_simple_vertex_solve"
07373  *      can't do (more than three faces intersecting at a vertex)
07374  *
07375  *      This routine may create new edges and/or faces and
07376  *      Modifies the location of "new_v"
07377  *
07378  *      if approximate is non-zero, the new geomatry is
07379  *      approximated by calculating the point with minimum
07380  *      distance to all the intersecting faces
07381  *
07382  *      returns:
07383  *              0 - if everything is OK
07384  *              1 - failure
07385  */
07386 
07387 int
07388 nmg_complex_vertex_solve(struct vertex *new_v, const struct bu_ptbl *faces, const int free_edges, const int approximate, const struct bn_tol *tol)
07389 {
07390         struct faceuse *fu;
07391         struct face *fp1;
07392         struct bu_ptbl int_faces;
07393         int i;
07394 
07395         /* More than 3 faces intersect at vertex (new_v)
07396          * Calculate intersection point along each edge
07397          * emanating from new_v */
07398 
07399         if( rt_g.NMG_debug & DEBUG_BASIC )
07400                 bu_log( "nmg_complex_vertex_solve( new_v = x%x , %d faces )\n" , new_v , BU_PTBL_END( faces ) );
07401 
07402         NMG_CK_VERTEX( new_v );
07403         BU_CK_PTBL( faces );
07404         BN_CK_TOL( tol );
07405 
07406         if( approximate )
07407         {
07408                 plane_t *planes;
07409                 int plane_count;
07410 
07411                 plane_count = BU_PTBL_END( faces );
07412 
07413                 planes = (plane_t *)bu_calloc( plane_count+free_edges , sizeof( plane_t ) , "nmg_complex_vertex_solve: planes" );
07414 
07415 
07416                 for( i=0 ; i<BU_PTBL_END( faces ) ; i++ )
07417                 {
07418                         fp1 = (struct face *)BU_PTBL_GET( faces , i );
07419                         fu = fp1->fu_p;
07420                         NMG_GET_FU_PLANE( planes[i] , fu );
07421 
07422                         if( rt_g.NMG_debug & DEBUG_BASIC )
07423                                 bu_log( "\t plane #%d: %g %g %g %g\n", i, V4ARGS( planes[i] ) );
07424                 }
07425 
07426                 if( rt_g.NMG_debug & DEBUG_BASIC )
07427                 {
07428                         int j;
07429 
07430                         for( i=0 ; i<BU_PTBL_END( faces ); i++ )
07431                         {
07432                                 fastf_t dot;
07433 
07434                                 dot = VDOT( planes[i], new_v->vg_p->coord );
07435                                 bu_log( "\tVDOT( #%d, new_v ) - dist = %g\n", i, dot-planes[i][3] );
07436 
07437                                 for( j=0 ; j<BU_PTBL_END( faces ) ; j++ )
07438                                 {
07439                                         dot = VDOT( planes[i], planes[j] );
07440                                         bu_log( "\tVDOT( #%d, #%d ) = %g\n",i,j, dot );
07441                                 }
07442                         }
07443                 }
07444 
07445                 if( free_edges )
07446                 {
07447                         int free_edge_count=0;
07448                         struct vertexuse *vu;
07449                         struct faceuse *fu_free=(struct faceuse *)NULL;
07450                         struct edgeuse *eu_free=(struct edgeuse *)NULL;
07451 
07452                         plane_count = BU_PTBL_END( faces );
07453 
07454                         for( BU_LIST_FOR( vu, vertexuse, &new_v->vu_hd ) )
07455                         {
07456                                 struct edgeuse *eu;
07457 
07458                                 if( *vu->up.magic_p != NMG_EDGEUSE_MAGIC )
07459                                         continue;
07460 
07461                                 eu = vu->up.eu_p;
07462 
07463                                 if( eu->radial_p == eu->eumate_p )
07464                                 {
07465                                         vect_t fu_norm;
07466 
07467                                         /* this is a free edges */
07468                                         eu_free = eu;
07469                                         fu_free = nmg_find_fu_of_eu( eu_free );
07470                                         free_edge_count++;
07471 
07472                                         NMG_GET_FU_NORMAL( fu_norm, fu_free );
07473 
07474                                         VCROSS( planes[plane_count], fu_norm, eu_free->g.lseg_p->e_dir );
07475                                         VUNITIZE( planes[plane_count] );
07476 
07477                                         planes[plane_count][3] = VDOT( planes[plane_count], new_v->vg_p->coord );
07478 
07479                                         if( rt_g.NMG_debug & DEBUG_BASIC )
07480                                                 bu_log( "\t added plane #%d: %g %g %g %g\n", plane_count, V4ARGS( planes[plane_count] ) );
07481 
07482                                         plane_count++;
07483 
07484                                         if( free_edge_count == free_edges )
07485                                                 break;
07486                                 }
07487                         }
07488                 }
07489 
07490                 if( bn_isect_planes( new_v->vg_p->coord , (const plane_t *)planes , plane_count ) )
07491                 {
07492                         bu_log( "nmg_complex_vertex_solve: Could not calculate new geometry at ( %f %f %f )\n",
07493                                 V3ARGS( new_v->vg_p->coord ) );
07494                         bu_free( (char *) planes , "nmg_complex_vertex_solve: planes" );
07495                         return( 1 );
07496                 }
07497                 bu_free( (char *) planes , "nmg_complex_vertex_solve: planes" );
07498 
07499                 if( rt_g.NMG_debug & DEBUG_BASIC )
07500                         bu_log( "nmg_complex_vertex_solve: new coords = ( %f %f %f )\n",
07501                                 V3ARGS( new_v->vg_p->coord ) );
07502                 return( 0 );
07503         }
07504 
07505         bu_ptbl_init( &int_faces , 64, " &int_faces ");
07506 
07507         /* get int_faces table (of intersect_fus structures) partially filled in
07508          * with fu's, eu, and edge line definition
07509          */
07510         if( nmg_get_edge_lines( new_v , &int_faces , tol ) )
07511         {
07512                 bu_ptbl_free( &int_faces );
07513                 return( 1 );
07514         }
07515 
07516         /* calculate geometry for new_v */
07517         if( nmg_calc_new_v( new_v , &int_faces , tol ) )
07518         {
07519                 bu_ptbl_free( &int_faces );
07520                 return( 1 );
07521         }
07522 
07523         /* fill in "pt" portion of intersect_fus structures with points
07524          * that are the intersections of the edge line with the other
07525          * edges that meet at new_v. The intersection that is furthest
07526          * up the edge away from new_v is selected
07527          */
07528         if (nmg_get_max_edge_inters( new_v , &int_faces , faces , tol ) )
07529         {
07530                 bu_ptbl_free( &int_faces );
07531                 return( 1 );
07532         }
07533 
07534         /* split edges at intersection points */
07535         nmg_split_edges_at_pts( new_v , &int_faces , tol );
07536 
07537         /* fix intersection points that cause loops that cross themselves */
07538         nmg_fix_crossed_loops( new_v , &int_faces , tol );
07539 
07540         nmg_remove_short_eus_inter( new_v , &int_faces , tol );
07541 
07542         nmg_simplify_inter( new_v , &int_faces , tol );
07543 
07544         /* Build needed faces */
07545         nmg_make_faces_at_vert( new_v , &int_faces , tol );
07546 
07547         /* Where faces were not built, cracks have formed */
07548         nmg_kill_cracks_at_vertex( new_v );
07549 
07550         /* free some memory */
07551         for( i=0 ; i<BU_PTBL_END( &int_faces ) ; i++ )
07552         {
07553                 struct intersect_fus *i_fus;
07554 
07555                 i_fus = (struct intersect_fus *)BU_PTBL_GET( &int_faces , i );
07556                 bu_free( (char *)i_fus , "nmg_complex_vertex_solve: intersect_fus struct\n" );
07557         }
07558         bu_ptbl_free( &int_faces );
07559 
07560         if( rt_g.NMG_debug & DEBUG_BASIC )
07561                 bu_log( "nmg_co,mplex_vertex_solve: new coords = ( %f %f %f )\n",
07562                         V3ARGS( new_v->vg_p->coord ) );
07563 
07564         return( 0 );
07565 }
07566 
07567 /**     N M G _ B A D _ F A C E _ N O R M A L S
07568  *
07569  *      Look for faceuses in the shell with normals that do
07570  *      not agree with the geometry (i.e., in the wrong direction)
07571  *
07572  *      return:
07573  *              1 - at least one faceuse with a bad normal was found
07574  *              0 - no faceuses with bad normals were found
07575  */
07576 int
07577 nmg_bad_face_normals(const struct shell *s, const struct bn_tol *tol)
07578 {
07579         struct faceuse *fu;
07580         struct loopuse *lu;
07581         vect_t old_normal;
07582         plane_t new_plane;
07583 
07584         NMG_CK_SHELL( s );
07585         BN_CK_TOL( tol );
07586 
07587         for( BU_LIST_FOR( fu , faceuse , &s->fu_hd ) )
07588         {
07589                 fastf_t area = -1;
07590 
07591                 NMG_CK_FACEUSE( fu );
07592 
07593                 /* only check OT_SAME faseuses */
07594                 if( fu->orientation != OT_SAME )
07595                         continue;
07596 
07597                 /* get current normal */
07598                 NMG_GET_FU_NORMAL( old_normal , fu );
07599 
07600                 for( BU_LIST_FOR( lu , loopuse , &fu->lu_hd ) )
07601                 {
07602                         NMG_CK_LOOPUSE( lu );
07603 
07604                         if( lu->orientation != OT_SAME && lu->orientation != OT_OPPOSITE )
07605                                 continue;
07606 
07607                         if( (area = nmg_loop_plane_area( lu , new_plane )) > 0.0 )
07608                         {
07609                                 if( lu->orientation != OT_SAME )
07610                                         VREVERSE( new_plane , new_plane )
07611                                 break;
07612                         }
07613                 }
07614 
07615                 if( area > 0.0 )
07616                 {
07617                         if( VDOT( old_normal , new_plane ) < 0.0 )
07618                                 return( 1 );
07619                 }
07620         }
07621         return( 0 );
07622 }
07623 
07624 /**
07625  *      N M G _ F A C E S _ A R E _ R A D I A L
07626  *
07627  *      checks if two faceuses are radial to each other
07628  *
07629  *      returns
07630  *              1 - the two faceuses are radial to each other
07631  *              0 - otherwise
07632  *
07633  */
07634 int
07635 nmg_faces_are_radial(const struct faceuse *fu1, const struct faceuse *fu2)
07636 {
07637         struct edgeuse *eu,*eu_tmp;
07638         struct loopuse *lu;
07639 
07640         NMG_CK_FACEUSE( fu1 );
07641         NMG_CK_FACEUSE( fu2 );
07642 
07643         /* look at every loop in the faceuse #1 */
07644         for( BU_LIST_FOR( lu , loopuse , &fu1->lu_hd ) )
07645         {
07646                 if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) != NMG_EDGEUSE_MAGIC )
07647                         continue;
07648 
07649                 /* look at every edgeuse in the loop */
07650                 for( BU_LIST_FOR( eu , edgeuse , &lu->down_hd ) )
07651                 {
07652                         /* now search radially around edge */
07653                         eu_tmp = eu->eumate_p->radial_p;
07654                         while( eu_tmp != eu && eu_tmp != eu->eumate_p )
07655                         {
07656                                 struct faceuse *fu_tmp;
07657 
07658                                 /* find radial faceuse */
07659                                 fu_tmp = nmg_find_fu_of_eu( eu_tmp );
07660 
07661                                 /* if its the same as fu2 or its mate, the faceuses are radial */
07662                                 if( fu_tmp == fu2 || fu_tmp == fu2->fumate_p )
07663                                         return( 1 );
07664 
07665                                 /* go to next radial edgeuse */
07666                                 eu_tmp = eu_tmp->eumate_p->radial_p;
07667                         }
07668                 }
07669         }
07670 
07671         return( 0 );
07672 }
07673 
07674 /**     N M G _ M O V E _ E D G E _ T H R U _ P T
07675  *
07676  *      moves indicated edgeuse (mv_eu) so that it passes thru
07677  *      the given point (pt). The direction of the edgeuse
07678  *      is not changed, so new edgeuse is parallel to the original.
07679  *
07680  *      plane equations of all radial faces on this edge are changed
07681  *      and all vertices (except one anchor point) in radial loops are adjusted
07682  *      Note that the anchor point is chosen arbitrarily.
07683  *
07684  *      returns:
07685  *              1 - failure
07686  *              0 - success
07687  */
07688 
07689 int
07690 nmg_move_edge_thru_pt(struct edgeuse *mv_eu, const fastf_t *pt, const struct bn_tol *tol)
07691 {
07692         struct faceuse *fu,*fu1;
07693         struct edgeuse *eu,*eu1;
07694         struct edge_g_lseg *eg;
07695         struct vertex *v1,*v2;
07696         struct model *m;
07697         vect_t e_dir;
07698         struct bu_ptbl tmp_faces[2];
07699         struct bu_ptbl faces;
07700         int count;
07701         long *flags;
07702 
07703         if( rt_g.NMG_debug & DEBUG_BASIC )
07704                 bu_log( "nmg_move_edge_thru_pt( mv_eu=x%x , pt=( %f %f %f) )\n" , mv_eu , V3ARGS( pt ) );
07705 
07706         NMG_CK_EDGEUSE( mv_eu );
07707         BN_CK_TOL( tol );
07708 
07709         m = nmg_find_model( &mv_eu->l.magic );
07710         NMG_CK_MODEL( m );
07711 
07712         /* get endpoint vertices */
07713         v1 = mv_eu->vu_p->v_p;
07714         NMG_CK_VERTEX( v1 );
07715         v2 = mv_eu->eumate_p->vu_p->v_p;
07716         NMG_CK_VERTEX( v2 );
07717 
07718         eg = mv_eu->g.lseg_p;
07719 
07720         /* get edge direction */
07721         if( v1 != v2 )
07722         {
07723                 if( eg )
07724                 {
07725                         NMG_CK_EDGE_G_LSEG(eg);
07726                         VMOVE( e_dir , eg->e_dir );
07727                         if( mv_eu->orientation == OT_OPPOSITE )
07728                         {
07729                                 VREVERSE( e_dir , e_dir );
07730                         }
07731                 }
07732                 else
07733                 {
07734                         nmg_edge_g( mv_eu );
07735                         eg = mv_eu->g.lseg_p;
07736                         VMOVE( e_dir , eg->e_dir );
07737                 }
07738                 VUNITIZE( e_dir );
07739         }
07740 
07741         eu = mv_eu;
07742         fu1 = nmg_find_fu_of_eu( eu );
07743 
07744         if( fu1 == NULL )
07745         {
07746                 vect_t to_pt;
07747                 vect_t move_v;
07748                 fastf_t edir_comp;
07749                 point_t new_loc;
07750 
07751                 /* This must be a wire edge, just adjust the endpoints */
07752                 /* keep edge the same length, and move vertices perpendicular to e_dir */
07753 
07754                 VSUB2( to_pt , pt , v1->vg_p->coord );
07755                 edir_comp = VDOT( to_pt , e_dir );
07756                 VJOIN1( move_v , to_pt , -edir_comp , e_dir );
07757 
07758                 /* move the vertices */
07759                 VADD2( new_loc , v1->vg_p->coord , move_v );
07760                 nmg_vertex_gv( v1 , new_loc );
07761 
07762                 if( v2 != v1 )
07763                 {
07764                         VADD2( new_loc , v2->vg_p->coord , move_v );
07765                         nmg_vertex_gv( v2 , new_loc );
07766                 }
07767 
07768                 /* adjust edge geometry */
07769                 if( !eg )
07770                         nmg_edge_g( eu );
07771                 else
07772                         VMOVE( eg->e_pt , new_loc )
07773 
07774                 if( *eu->up.magic_p == NMG_LOOPUSE_MAGIC )
07775                 {
07776                         struct edgeuse *tmp_eu;
07777 
07778                         /* edge is part of a wire loop
07779                          * need to adjust geometry neighbor edges
07780                          */
07781 
07782                         tmp_eu = BU_LIST_PNEXT_CIRC( edgeuse , &eu->l );
07783                         NMG_CK_EDGEUSE( tmp_eu );
07784                         if( *tmp_eu->g.magic_p == NMG_EDGE_G_LSEG_MAGIC )
07785                         {
07786                                 VMOVE( tmp_eu->g.lseg_p->e_pt , tmp_eu->vu_p->v_p->vg_p->coord )
07787                                 VSUB2( tmp_eu->g.lseg_p->e_dir, tmp_eu->eumate_p->vu_p->v_p->vg_p->coord, tmp_eu->g.lseg_p->e_pt)
07788                         }
07789                         tmp_eu = BU_LIST_PPREV_CIRC( edgeuse , &eu->l );
07790                         NMG_CK_EDGEUSE( tmp_eu );
07791                         if( *tmp_eu->g.magic_p == NMG_EDGE_G_LSEG_MAGIC )
07792                         {
07793                                 VMOVE( tmp_eu->g.lseg_p->e_pt , tmp_eu->vu_p->v_p->vg_p->coord )
07794                                 VSUB2( tmp_eu->g.lseg_p->e_dir, tmp_eu->eumate_p->vu_p->v_p->vg_p->coord, tmp_eu->g.lseg_p->e_pt)
07795                         }
07796                 }
07797 
07798                 return( 0 );
07799         }
07800 
07801         /* can only handle edges with up to two radial faces */
07802         if( mv_eu->radial_p->eumate_p != mv_eu->eumate_p->radial_p && mv_eu->radial_p != mv_eu->eumate_p )
07803         {
07804                 bu_log( "Cannot handle edges with more than two radial faces\n" );
07805                 return( 1 );
07806         }
07807 
07808         bu_ptbl_init( &tmp_faces[0] , 64, " &tmp_faces[0] ");
07809         bu_ptbl_init( &tmp_faces[1] , 64, " &tmp_faces[1] ");
07810 
07811         /* cannot handle complex vertices yet */
07812         if( nmg_find_isect_faces( v1 , &tmp_faces[0] , &count , tol ) > 3 ||
07813             nmg_find_isect_faces( v2 , &tmp_faces[1] , &count , tol ) > 3 )
07814         {
07815                 bu_log( "nmg_move_edge_thru_pt: cannot handle complex vertices yet\n" );
07816                 bu_ptbl_free( &tmp_faces[0] );
07817                 bu_ptbl_free( &tmp_faces[1] );
07818                 return( 1 );
07819         }
07820 
07821         /* Move edge geometry to new point */
07822         if( eg )
07823         {
07824                 VMOVE( eg->e_pt , pt );
07825         }
07826 
07827         /* modify plane equation for each face radial to mv_eu */
07828         fu = fu1;
07829         do
07830         {
07831                 struct edgeuse *eu_next;
07832                 plane_t plane;
07833                 int cross_direction;
07834                 vect_t norm;
07835                 int done;
07836 
07837                 NMG_CK_EDGEUSE( eu );
07838                 NMG_CK_FACEUSE( fu );
07839 
07840                 if( fu->orientation == OT_SAME )
07841                         NMG_GET_FU_NORMAL( norm , fu )
07842                 else
07843                         NMG_GET_FU_NORMAL( norm , fu->fumate_p )
07844 
07845                 /* find an anchor point for face to rotate about
07846                  * go forward in loop until we find a vertex that is
07847                  * far enough from the line of mv_eu to produce a
07848                  * non-zero cross product
07849                  */
07850                 eu_next = eu;
07851                 done = 0;
07852                 while( !done )
07853                 {
07854                         vect_t to_anchor;
07855                         vect_t next_dir;
07856                         vect_t cross;
07857                         struct vertex *anchor_v;
07858                         fastf_t mag;
07859 
07860                         /* get next edgeuse in loop */
07861                         eu_next = BU_LIST_PNEXT_CIRC( edgeuse , &eu_next->l );
07862 
07863                         /* check if we have circled the entire loop */
07864                         if( eu_next == eu )
07865                         {
07866                                 bu_log( "nmg_move_edge_thru_pt: cannot calculate new plane eqn\n" );
07867                                 return( 1 );
07868                         }
07869 
07870                         /* anchor point is endpoint of this edgeuse */
07871                         anchor_v = eu_next->eumate_p->vu_p->v_p;
07872 
07873                         VSUB2( next_dir , anchor_v->vg_p->coord , eu_next->vu_p->v_p->vg_p->coord );
07874                         VCROSS( cross , e_dir , next_dir );
07875                         if( VDOT( cross , norm ) < 0.0 )
07876                                 cross_direction = 1;
07877                         else
07878                                 cross_direction = 0;
07879 
07880                         /* calculate new plane */
07881                         VSUB2( to_anchor , anchor_v->vg_p->coord , pt );
07882                         if( cross_direction )
07883                         {
07884                                 VCROSS( plane , to_anchor , e_dir );
07885                         }
07886                         else
07887                         {
07888                                 VCROSS( plane , e_dir , to_anchor );
07889                         }
07890 
07891                         mag = MAGNITUDE( plane );
07892                         if( mag > SQRT_SMALL_FASTF )
07893                         {
07894                                 /* this is an acceptable plane */
07895                                 mag = 1.0/mag;
07896                                 VSCALE( plane , plane , mag );
07897                                 plane[3] = VDOT( plane , pt );
07898 
07899                                 /* assign this plane to the face */
07900                                 if( fu->orientation == OT_SAME )
07901                                         nmg_face_g( fu , plane );
07902                                 else
07903                                         nmg_face_g( fu->fumate_p , plane );
07904                                 done = 1;
07905                         }
07906                 }
07907 
07908                 /* move on to next radial face */
07909                 eu = eu->eumate_p->radial_p;
07910                 fu = nmg_find_fu_of_eu( eu );
07911         }
07912         while( fu != fu1 && fu != fu1->fumate_p );
07913 
07914         /* now recalculate vertex coordinates for all affected vertices,
07915          * could be lots of them
07916          */
07917 
07918         flags = (long *)bu_calloc( m->maxindex , sizeof( long ) , "nmg_move_edge_thru_pt: flags" );
07919         bu_ptbl_init( &faces , 64, " &faces ");
07920 
07921         eu1 = mv_eu;
07922         fu1 = nmg_find_fu_of_eu( eu1 );
07923         fu = fu1;
07924         do
07925         {
07926                 struct loopuse *lu;
07927 
07928                 for( BU_LIST_FOR( lu , loopuse , &fu->lu_hd ) )
07929                 {
07930                         struct edgeuse *eu;
07931 
07932                         NMG_CK_LOOPUSE( lu );
07933 
07934                         if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) == NMG_VERTEXUSE_MAGIC )
07935                         {
07936                                 struct vertexuse *vu;
07937                                 vu = BU_LIST_FIRST( vertexuse , &lu->down_hd );
07938                                 if( NMG_INDEX_TEST_AND_SET( flags , vu->v_p ) )
07939                                 {
07940                                         bu_ptbl_reset( &faces );
07941 
07942                                         /* find all unique faces that intersect at this vertex (vu->v_p) */
07943                                         if( nmg_find_isect_faces( vu->v_p , &faces , &count , tol ) > 3 )
07944                                         {
07945                                                 bu_log( "mg_move_edge_thru_pt: Cannot handle complex vertices\n" );
07946                                                 bu_ptbl_free( &faces );
07947                                                 bu_free( (char *)flags , "mg_move_edge_thru_pt: flags" );
07948                                                 return( 1 );
07949                                         }
07950 
07951                                         if( nmg_simple_vertex_solve( vu->v_p , &faces, tol ) )
07952                                         {
07953                                                 /* failed */
07954                                                 bu_log( "nmg_move_edge_thru_pt: Could not solve simple vertex\n" );
07955                                                 bu_ptbl_free( &faces );
07956                                                 bu_free( (char *)flags , "mg_move_edge_thru_pt: flags" );
07957                                                 return( 1 );
07958                                         }
07959                                 }
07960                                 continue;
07961                         }
07962 
07963                         for( BU_LIST_FOR( eu , edgeuse , &lu->down_hd ) )
07964                         {
07965                                 struct vertexuse *vu;
07966 
07967                                 vu = eu->vu_p;
07968                                 if( NMG_INDEX_TEST_AND_SET( flags , vu->v_p ) )
07969                                 {
07970 
07971                                         bu_ptbl_reset( &faces );
07972 
07973                                         /* find all unique faces that intersect at this vertex (vu->v_p) */
07974                                         if( nmg_find_isect_faces( vu->v_p , &faces , &count , tol ) > 3 )
07975                                         {
07976                                                 bu_log( "mg_move_edge_thru_pt: Cannot handle complex vertices\n" );
07977                                                 bu_ptbl_free( &faces );
07978                                                 bu_free( (char *)flags , "mg_move_edge_thru_pt: flags" );
07979                                                 return( 1 );
07980                                         }
07981 
07982                                         if( BU_PTBL_END( &faces ) == 1 &&
07983                                                 (mv_eu->vu_p->v_p == vu->v_p ||
07984                                                  mv_eu->eumate_p->vu_p->v_p == vu->v_p) )
07985                                         {
07986                                                 vect_t to_pt;
07987                                                 vect_t mv_vect;
07988                                                 vect_t eu_dir;
07989 
07990                                                 /* special case for edge of a dangling face */
07991 
07992                                                 /* just move vertex to new edge geometry */
07993                                                 VSUB2( eu_dir, eu->eumate_p->vu_p->v_p->vg_p->coord, vu->v_p->vg_p->coord );
07994                                                 VUNITIZE( eu_dir );
07995                                                 VSUB2( to_pt, pt , vu->v_p->vg_p->coord );
07996                                                 VJOIN1( mv_vect, to_pt, -VDOT( e_dir, to_pt ), e_dir );
07997                                                 VADD2( vu->v_p->vg_p->coord, vu->v_p->vg_p->coord, mv_vect);
07998                                         }
07999                                         else
08000                                         {
08001                                                 if( nmg_simple_vertex_solve( vu->v_p , &faces, tol ) )
08002                                                 {
08003                                                         /* failed */
08004                                                         bu_log( "nmg_move_edge_thru_pt: Could not solve simple vertex\n" );
08005                                                         bu_ptbl_free( &faces );
08006                                                         bu_free( (char *)flags , "mg_move_edge_thru_pt: flags" );
08007                                                         return( 1 );
08008                                                 }
08009                                         }
08010 
08011                                         /* adjust edge geometry */
08012                                         if( eu->e_p != mv_eu->e_p )
08013                                         {
08014                                                 if( !eu->g.magic_p )
08015                                                         nmg_edge_g( eu );
08016                                                 else
08017                                                 {
08018                                                         VMOVE( eu->g.lseg_p->e_pt, vu->v_p->vg_p->coord );
08019                                                         VSUB2( eu->g.lseg_p->e_dir,
08020                                                                 eu->eumate_p->vu_p->v_p->vg_p->coord,
08021                                                                 vu->v_p->vg_p->coord );
08022                                                 }
08023                                         }
08024                                 }
08025                         }
08026                 }
08027 
08028                 /* move on to next radial face */
08029                 eu1 = eu1->eumate_p->radial_p;
08030                 fu = nmg_find_fu_of_eu( eu1 );
08031         }
08032         while( fu != fu1 && fu != fu1->fumate_p );
08033 
08034         bu_free( (char *)flags , "mg_move_edge_thru_pt: flags" );
08035         bu_ptbl_free( &faces );
08036 
08037         return( 0 );
08038 }
08039 
08040 /**     N M G _ V L I S T _ T O _ W I R E _ E D G E S
08041  *
08042  *      Convert a vlist to NMG wire edges
08043  *
08044  */
08045 void
08046 nmg_vlist_to_wire_edges(struct shell *s, const struct bu_list *vhead)
08047 {
08048         const struct bn_vlist *vp;
08049         struct edgeuse *eu;
08050         struct vertex *v1,*v2;
08051         point_t pt1,pt2;
08052 
08053         NMG_CK_SHELL( s );
08054         NMG_CK_LIST( vhead );
08055 
08056         v1 = (struct vertex *)NULL;
08057         v2 = (struct vertex *)NULL;
08058 
08059         vp = BU_LIST_FIRST( bn_vlist , vhead );
08060         if( vp->nused < 2 )
08061                 return;
08062 
08063         for( BU_LIST_FOR( vp , bn_vlist , vhead ) )
08064         {
08065                 register int i;
08066                 register int nused = vp->nused;
08067                 vect_t edge_vec;
08068 
08069                 for( i=0 ; i<nused ; i++ )
08070                 {
08071                         switch( vp->cmd[i] )
08072                         {
08073                                 case BN_VLIST_LINE_MOVE:
08074                                 case BN_VLIST_POLY_MOVE:
08075                                         v1 = (struct vertex *)NULL;
08076                                         v2 = (struct vertex *)NULL;
08077                                         VMOVE( pt2 , vp->pt[i] );
08078                                         break;
08079                                 case BN_VLIST_LINE_DRAW:
08080                                 case BN_VLIST_POLY_DRAW:
08081                                         VSUB2( edge_vec , pt2 , vp->pt[i] );
08082                                         if( VNEAR_ZERO( edge_vec , SMALL_FASTF ) )
08083                                                 break;
08084                                         VMOVE( pt1 , pt2 );
08085                                         v1 = v2;
08086                                         VMOVE( pt2 , vp->pt[i] );
08087                                         v2 = (struct vertex *)NULL;
08088                                         eu = nmg_me( v1 , v2 , s );
08089                                         v1 = eu->vu_p->v_p;
08090                                         v2 = eu->eumate_p->vu_p->v_p;
08091                                         nmg_vertex_gv( v2 , pt2 );
08092                                         if( !v1->vg_p )
08093                                                 nmg_vertex_gv( v1 , pt1 );
08094                                         nmg_edge_g( eu );
08095                                         break;
08096                                 case BN_VLIST_POLY_START:
08097                                 case BN_VLIST_POLY_END:
08098                                         break;
08099                         }
08100                 }
08101         }
08102 }
08103 
08104 void
08105 nmg_follow_free_edges_to_vertex(const struct vertex *vpa, const struct vertex *vpb, struct bu_ptbl *bad_verts, const struct shell *s, const struct edgeuse *eu, struct bu_ptbl *verts, int *found)
08106 {
08107         struct vertexuse *vu;
08108 
08109         BU_CK_PTBL( bad_verts );
08110         NMG_CK_EDGEUSE( eu );
08111         NMG_CK_VERTEX( vpa );
08112         NMG_CK_VERTEX( vpb );
08113         if( s )
08114                 NMG_CK_SHELL( s );
08115 
08116         NMG_CK_VERTEX( eu->eumate_p->vu_p->v_p );
08117 
08118         if( rt_g.NMG_debug & DEBUG_BASIC )
08119         {
08120                 bu_log( "nmg_follow_free_edges_to_vertex( vpa=x%x, vpb=x%x s=x%x, eu=x%x, found=%d )\n",
08121                         vpa,vpb,s,eu,*found );
08122         }
08123 
08124         for( BU_LIST_FOR( vu , vertexuse , &eu->eumate_p->vu_p->v_p->vu_hd ) )
08125         {
08126                 struct edgeuse *eu1;
08127 
08128                 NMG_CK_VERTEXUSE( vu );
08129 
08130                 if( *vu->up.magic_p != NMG_EDGEUSE_MAGIC )
08131                         continue;
08132 
08133                 if( s && (nmg_find_s_of_vu( vu ) != s ) )
08134                         continue;
08135 
08136                 eu1 = vu->up.eu_p;
08137 
08138                 NMG_CK_EDGEUSE( eu1 );
08139 
08140                 if( rt_g.NMG_debug & DEBUG_BASIC )
08141                         bu_log( "\tchecking eu x%x: x%x ( %f %f %f )\n\t\tto x%x ( %f %f %f )\n", eu1,
08142                                 eu1->vu_p->v_p, V3ARGS( eu1->vu_p->v_p->vg_p->coord ),
08143                                 eu1->eumate_p->vu_p->v_p, V3ARGS( eu1->eumate_p->vu_p->v_p->vg_p->coord ) );
08144 
08145                 /* stick to free edges */
08146                 if( eu1->eumate_p != eu1->radial_p )
08147                 {
08148                         if( rt_g.NMG_debug & DEBUG_BASIC )
08149                                 bu_log( "\t\tnot a dangling edge\n" );
08150                         continue;
08151                 }
08152 
08153                 /* don`t go back the way we came */
08154                 if( eu1 == eu->eumate_p )
08155                 {
08156                         if( rt_g.NMG_debug & DEBUG_BASIC )
08157                                 bu_log( "\t\tback the way we came\n" );
08158                         continue;
08159                 }
08160 
08161                 if( eu1->eumate_p->vu_p->v_p == vpb )
08162                 {
08163                         /* found it!!! */
08164                         if( rt_g.NMG_debug & DEBUG_BASIC )
08165                                 bu_log( "\t\tfound goal\n" );
08166                         bu_ptbl_ins( verts , (long *)vu->v_p );
08167                         bu_ptbl_ins( verts , (long *)vpb );
08168                         *found = 1;
08169                 }
08170                 else if( eu1->eumate_p->vu_p->v_p == vpa )
08171                 {
08172                         /* back where we started */
08173                         if( rt_g.NMG_debug & DEBUG_BASIC )
08174                                 bu_log( "\t\tback at start\n" );
08175                         continue;
08176                 }
08177                 else if( bu_ptbl_locate( bad_verts , (long *)eu1->eumate_p->vu_p->v_p ) != (-1))
08178                 {
08179                         /* this is the wrong way */
08180                         if( rt_g.NMG_debug & DEBUG_BASIC )
08181                                 bu_log( "\t\tA bad vertex\n" );
08182                         continue;
08183                 }
08184                 else if( bu_ptbl_locate( verts , (long *)eu1->eumate_p->vu_p->v_p ) != (-1))
08185                 {
08186                         /* This is a loop !!!! */
08187                         if( rt_g.NMG_debug & DEBUG_BASIC )
08188                                 bu_log( "a loop\n" );
08189                         continue;
08190                 }
08191                 else
08192                 {
08193                         if( rt_g.NMG_debug & DEBUG_BASIC )
08194                                 bu_log( "\t\tinserting vertex x%x\n" , vu->v_p );
08195                         bu_ptbl_ins( verts , (long *)vu->v_p );
08196                         if( rt_g.NMG_debug & DEBUG_BASIC )
08197                                 bu_log( "\t\tCalling follow edges\n" );
08198                         nmg_follow_free_edges_to_vertex( vpa , vpb , bad_verts , s , eu1 , verts , found );
08199                         if( *found < 0 )
08200                         {
08201                                 if( rt_g.NMG_debug & DEBUG_BASIC )
08202                                 {
08203                                         bu_log( "\t\treturn is %d\n" , *found );
08204                                         bu_log( "\t\t\tremove vertex x%x\n" , vu->v_p );
08205                                 }
08206                                 bu_ptbl_rm( verts , (long *)vu->v_p );
08207                                 *found = 0;
08208                         }
08209                 }
08210                 if( *found )
08211                         return;
08212         }
08213 
08214         *found = (-1);
08215 }
08216 
08217 static struct bu_ptbl *
08218 nmg_find_path(const struct vertex *vpa, const struct vertex *vpb, struct bu_ptbl *bad_verts, const struct shell *s)
08219 {
08220         int done;
08221         static struct bu_ptbl verts;
08222         struct vertexuse *vua;
08223 
08224 
08225         BU_CK_PTBL( bad_verts );
08226         NMG_CK_VERTEX( vpa );
08227         NMG_CK_VERTEX( vpb );
08228 
08229         if( rt_g.NMG_debug & DEBUG_BASIC )
08230         {
08231                 int i;
08232 
08233                 bu_log( "nmg_find_path( vpa=x%x ( %f %f %f ), vpb=x%x ( %f %f %f )\n",
08234                         vpa, V3ARGS( vpa->vg_p->coord ), vpb, V3ARGS( vpb->vg_p->coord ) );
08235                 bu_log( "\t%d vertices to avoid\n" , BU_PTBL_END( bad_verts ) );
08236                 for( i=0 ; i<BU_PTBL_END( bad_verts ) ; i++ )
08237                 {
08238                         struct vertex *vpbad;
08239 
08240                         vpbad = (struct vertex *)BU_PTBL_GET( bad_verts , i );
08241                         bu_log( "\tx%x ( %f %f %f )\n" , vpbad , V3ARGS( vpbad->vg_p->coord ) );
08242                 }
08243         }
08244 
08245         bu_ptbl_init( &verts , 64, " &verts ");
08246         bu_ptbl_ins( &verts , (long *)vpa );
08247 
08248         for( BU_LIST_FOR( vua , vertexuse , &vpa->vu_hd ) )
08249         {
08250                 struct edgeuse *eua;
08251 
08252                 NMG_CK_VERTEXUSE( vua );
08253 
08254                 if( *vua->up.magic_p != NMG_EDGEUSE_MAGIC )
08255                         continue;
08256 
08257                 if( s && (nmg_find_s_of_vu( vua ) != s) )
08258                         continue;
08259 
08260                 eua = vua->up.eu_p;
08261 
08262                 NMG_CK_EDGEUSE( eua );
08263 
08264                 if( rt_g.NMG_debug & DEBUG_BASIC )
08265                         bu_log( "\tchecking eu x%x: x%x ( %f %f %f )\n\t\tto x%x ( %f %f %f )\n", eua,
08266                                 eua->vu_p->v_p, V3ARGS( eua->vu_p->v_p->vg_p->coord ),
08267                                 eua->eumate_p->vu_p->v_p, V3ARGS( eua->eumate_p->vu_p->v_p->vg_p->coord ) );
08268 
08269                 if( eua->eumate_p != eua->radial_p )
08270                 {
08271                         if( rt_g.NMG_debug & DEBUG_BASIC )
08272                                 bu_log( "\t\tback the way we came!\n" );
08273                         continue;
08274                 }
08275 
08276                 if( bu_ptbl_locate( bad_verts , (long *)eua->eumate_p->vu_p->v_p ) != (-1) )
08277                 {
08278                         if( rt_g.NMG_debug & DEBUG_BASIC )
08279                                 bu_log( "\t\tOne of the bad vertices!!\n" );
08280                         continue;
08281                 }
08282 
08283                 if( eua->eumate_p->vu_p->v_p == vpb )
08284                 {
08285                         if( rt_g.NMG_debug & DEBUG_BASIC )
08286                                 bu_log( "\t\tfound goal!!\n" );
08287                         bu_ptbl_ins( &verts , (long *)vpb );
08288                         return( &verts );
08289                 }
08290 
08291                 done = 0;
08292                 if( rt_g.NMG_debug & DEBUG_BASIC )
08293                         bu_log( "\tCall follow edges\n" );
08294                 nmg_follow_free_edges_to_vertex( vpa, vpb, bad_verts, s, eua, &verts, &done );
08295 
08296                 if( done == 1 )
08297                         break;
08298 
08299                 bu_ptbl_reset( &verts );
08300                 bu_ptbl_ins( &verts , (long *)vpa );
08301         }
08302 
08303         if( done != 1 )
08304                 bu_ptbl_init( &verts , 64, " &verts ");
08305 
08306         return( &verts );
08307 }
08308 
08309 void
08310 nmg_glue_face_in_shell(const struct faceuse *fu, struct shell *s, const struct bn_tol *tol)
08311 {
08312         struct loopuse *lu;
08313 
08314         NMG_CK_FACEUSE( fu );
08315         NMG_CK_SHELL( s );
08316         BN_CK_TOL( tol );
08317 
08318         for( BU_LIST_FOR( lu , loopuse , &fu->lu_hd ) )
08319         {
08320                 struct edgeuse *eu;
08321 
08322                 NMG_CK_LOOPUSE( lu );
08323                 if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) != NMG_EDGEUSE_MAGIC )
08324                         continue;
08325 
08326                 for( BU_LIST_FOR( eu , edgeuse , &lu->down_hd ) )
08327                 {
08328                         struct edgeuse *eu1;
08329 
08330                         NMG_CK_EDGEUSE( eu );
08331 
08332                         eu1 = nmg_findeu( eu->vu_p->v_p, eu->eumate_p->vu_p->v_p, s, eu, 1 );
08333                         if( eu1 )
08334                         {
08335                                 NMG_CK_EDGEUSE( eu1 );
08336                                 nmg_radial_join_eu( eu1, eu, tol );
08337                         }
08338                 }
08339         }
08340 }
08341 
08342 #if 0
08343 static int
08344 nmg_vert_is_normalward( v, vbase, norm )
08345 struct vertex *v;
08346 struct vertex *vbase;
08347 vect_t norm;
08348 {
08349         vect_t to_v;
08350 
08351         NMG_CK_VERTEX( v );
08352         NMG_CK_VERTEX( vbase );
08353 
08354         VSUB2( to_v, v->vg_p->coord, vbase->vg_p->coord );
08355         if( VDOT( to_v, norm ) > 0.0 )
08356                 return( 1 );
08357         else
08358                 return( 0 );
08359 }
08360 
08361 
08362 /* Join EU's running from v1 to v2 and from v2 to v3 */
08363 static void
08364 Join_eus( v1, v2, v3, tol )
08365 struct vertex *v1,*v2,*v3;
08366 const struct bn_tol *tol;
08367 {
08368         struct edgeuse *eu1;
08369         struct edgeuse *eu2;
08370 
08371         if( rt_g.NMG_debug & DEBUG_BASIC )
08372         {
08373                 bu_log( "Join_eus:\n" );
08374                 bu_log( "\tv1 = x%x ( %g %g %g )\n", v1, V3ARGS( v1->vg_p->coord ) );
08375                 bu_log( "\tv2 = x%x ( %g %g %g )\n", v2, V3ARGS( v2->vg_p->coord ) );
08376                 bu_log( "\tv3 = x%x ( %g %g %g )\n", v3, V3ARGS( v3->vg_p->coord ) );
08377         }
08378 
08379         eu1 = nmg_find_e( v1, v2, (struct shell *)NULL, (struct edge *)NULL );
08380         if( !eu1 )
08381         {
08382                 if( rt_g.NMG_debug & DEBUG_BASIC )
08383                         bu_log( "\tNo edge found from v1 to v2\n" );
08384         }
08385         else
08386         {
08387                 NMG_CK_EDGEUSE( eu1 );
08388 
08389                 eu2 = nmg_find_e( v1, v2, (struct shell *)NULL, eu1->e_p );
08390                 while( eu2 )
08391                 {
08392                         NMG_CK_EDGEUSE( eu2 );
08393 
08394                         if( rt_g.NMG_debug & DEBUG_BASIC )
08395                                 bu_log( "Joining eus x%x and x%x\n" , eu1, eu2 );
08396                         nmg_radial_join_eu( eu1, eu2, tol );
08397 
08398                         eu2 = nmg_find_e( v1, v2, (struct shell *)NULL, eu1->e_p );
08399                 }
08400         }
08401 
08402         eu1 = nmg_find_e( v2, v3, (struct shell *)NULL, (struct edge *)NULL );
08403         if( !eu1 )
08404         {
08405                 if( rt_g.NMG_debug & DEBUG_BASIC )
08406                         bu_log( "\tNo edge found from v2 to v3\n" );
08407         }
08408         else
08409         {
08410                 NMG_CK_EDGEUSE( eu1 );
08411 
08412                 eu2 = nmg_find_e( v2, v3, (struct shell *)NULL, eu1->e_p );
08413                 while( eu2 )
08414                 {
08415                         NMG_CK_EDGEUSE( eu2 );
08416                         if( rt_g.NMG_debug & DEBUG_BASIC )
08417                                 bu_log( "Joining eus x%x and x%x\n" , eu1, eu2 );
08418                         nmg_radial_join_eu( eu1, eu2, tol );
08419 
08420                         eu2 = nmg_find_e( v2, v3, (struct shell *)NULL, eu1->e_p );
08421                 }
08422         }
08423 }
08424 #endif
08425 
08426 static int
08427 nmg_make_connect_faces(struct shell *dst, struct vertex *vpa, struct vertex *vpb, struct bu_ptbl *verts, const struct bn_tol *tol)
08428 {
08429         int done=0;
08430         int i,j;
08431         int verts_in_face=0;
08432         struct vertex *face_verts[20];
08433         struct vertex *v;
08434         int max_vert_no=19;
08435         int made_face;
08436         int faces_made=0;
08437         fastf_t dist_to_a_sq,dist_to_b_sq;
08438         vect_t to_vpa,to_vpb;
08439 
08440         if( rt_g.NMG_debug & DEBUG_BASIC )
08441         {
08442                 bu_log( "nmg_make_connect_faces( dst=x%x, vpa=x%x ( %f %f %f ), vpb=x%x ( %f %f %f )\n",
08443                                 dst, vpa, V3ARGS( vpa->vg_p->coord ), vpb, V3ARGS( vpb->vg_p->coord ) );
08444                 for( i=0 ; i<BU_PTBL_END( verts ) ; i++ )
08445                 {
08446                         struct vertex *v;
08447 
08448                         v = (struct vertex *)BU_PTBL_GET( verts , i );
08449                         bu_log( "\tx%x ( %f %f %f )\n" , v , V3ARGS( v->vg_p->coord ) );
08450                 }
08451         }
08452 
08453         NMG_CK_SHELL( dst );
08454         NMG_CK_VERTEX( vpa );
08455         NMG_CK_VERTEX( vpb );
08456         BU_CK_PTBL( verts );
08457         BN_CK_TOL( tol );
08458 
08459         if( BU_PTBL_END( verts ) < 1 )
08460         {
08461                 bu_log( "nmg_make_connect_faces: no list of vertices from other shell\n" );
08462                 return( 0 );
08463         }
08464         if( BU_PTBL_END( verts ) == 1 )
08465         {
08466                 face_verts[0] = vpb;
08467                 face_verts[1] = vpa;
08468                 face_verts[2] = (struct vertex *)BU_PTBL_GET( verts , 0 );
08469                 i = 0;
08470         }
08471         else
08472         {
08473                 /* set up for first face */
08474                 face_verts[0] = vpa;
08475                 face_verts[1] = (struct vertex *)BU_PTBL_GET( verts , 0 );
08476                 face_verts[2] = (struct vertex *)BU_PTBL_GET( verts , 1 );
08477                 i = 1;
08478         }
08479         v = face_verts[2];
08480         verts_in_face = 3;
08481 
08482         VSUB2( to_vpa , vpa->vg_p->coord , v->vg_p->coord );
08483         VSUB2( to_vpb , vpb->vg_p->coord , v->vg_p->coord );
08484 
08485         dist_to_a_sq = MAGSQ( to_vpa );
08486         dist_to_b_sq = MAGSQ( to_vpb );
08487 
08488         while( 1 )
08489         {
08490                 struct faceuse *new_fu;
08491                 struct loopuse *lu;
08492                 plane_t pl;
08493                 fastf_t area;
08494                 int still_collinear=0;
08495 
08496                 made_face = 0;
08497 
08498                 /* if the current points are all collinear, add another vertex */
08499                 while( bn_3pts_collinear( face_verts[0]->vg_p->coord,
08500                                 face_verts[1]->vg_p->coord,
08501                                 face_verts[verts_in_face - 1]->vg_p->coord, tol) )
08502                 {
08503                         if( verts_in_face >= max_vert_no )
08504                         {
08505                                 still_collinear = 1;
08506                                 break;
08507                         }
08508 
08509                         if( i+1 >= BU_PTBL_END( verts ) )
08510                         {
08511                                 still_collinear = 1;
08512                                 break;
08513                         }
08514                         i++;
08515                         face_verts[verts_in_face] = (struct vertex *)BU_PTBL_GET( verts , i );
08516                         verts_in_face++;
08517                 }
08518 
08519                 if( !still_collinear )
08520                 {
08521                         /* make the new face */
08522 
08523                         if( rt_g.NMG_debug & DEBUG_BASIC )
08524                         {
08525                                 int debug_int;
08526 
08527                                 bu_log( "make face:\n" );
08528                                 for( debug_int=0 ; debug_int<verts_in_face ; debug_int++ )
08529                                         bu_log( "\tx%x ( %f %f %f )\n" , face_verts[debug_int],
08530                                                 V3ARGS( face_verts[debug_int]->vg_p->coord ) );
08531                         }
08532 
08533                         new_fu = nmg_cface( dst , face_verts , verts_in_face );
08534                         lu = BU_LIST_FIRST( loopuse , &new_fu->lu_hd );
08535                         area = nmg_loop_plane_area( lu , pl );
08536 
08537                         if( area <= 0.0 )
08538                         {
08539                                 bu_log( "Bad lu:\n" );
08540                                 nmg_pr_lu_briefly( lu, " " );
08541                                 nmg_kfu( new_fu );
08542                                 rt_bomb( "nmg_make_connect_faces: Failed to calculate plane eqn\n" );
08543                         }
08544                         else
08545                         {
08546                                 made_face = 1;
08547                                 faces_made++;
08548 
08549                                 nmg_face_g( new_fu , pl );
08550                                 nmg_loop_g( lu->l_p, tol );
08551 
08552                                 /* glue this face in */
08553                                 nmg_glue_face_in_shell( new_fu , dst , tol );
08554                         }
08555                 }
08556                 else
08557                         made_face = 0;
08558 
08559                 /* If we are half way to the other end of the edge,
08560                  * switch from vpa to vpb for the basis of the faces.
08561                  * Need to make the "middle" face.
08562                  */
08563                 if( dist_to_b_sq <= dist_to_a_sq && face_verts[0] == vpa )
08564                 {
08565                         face_verts[0] = vpb;
08566                         face_verts[1] = vpa;
08567                         face_verts[2] = v;
08568                         verts_in_face = 3;
08569                 }
08570                 else
08571                 {
08572                         /* Get ready for next face and check if done */
08573                         i++;
08574 
08575                         if( i < BU_PTBL_END( verts ) )
08576                         {
08577                                 v = (struct vertex *)BU_PTBL_GET( verts , i );
08578                                 NMG_CK_VERTEX( v );
08579 
08580                                 VSUB2( to_vpa , vpa->vg_p->coord , v->vg_p->coord );
08581                                 VSUB2( to_vpb , vpb->vg_p->coord , v->vg_p->coord );
08582 
08583                                 dist_to_a_sq = MAGSQ( to_vpa );
08584                                 dist_to_b_sq = MAGSQ( to_vpb );
08585                                 face_verts[1] = face_verts[verts_in_face-1];
08586                                 face_verts[2] = v;
08587                                 verts_in_face = 3;
08588                         }
08589                         else if( face_verts[0] == vpa )
08590                         {
08591                                 if( done )
08592                                         break;
08593 
08594                                 /* make last face */
08595                                 face_verts[0] = vpb;
08596                                 face_verts[1] = vpa;
08597                                 face_verts[2] = face_verts[verts_in_face-1];
08598                                 verts_in_face = 3;
08599                                 done = 1;
08600                         }
08601                         else    /* we are done */
08602                                 break;
08603                 }
08604 
08605         }
08606 
08607         if( !made_face )
08608         {
08609                 int found;
08610 
08611                 if( !faces_made )
08612                 {
08613                         /* put all the vertices on the list */
08614 
08615                         /* check for vpa */
08616                         found = 0;
08617                         for( i=0 ; i<verts_in_face ; i++ )
08618                         {
08619                                 if( face_verts[i] == vpa )
08620                                 {
08621                                         found = 1;
08622                                         break;
08623                                 }
08624                         }
08625                         if( !found )
08626                         {
08627                                 if( verts_in_face < 19 )
08628                                 {
08629                                         face_verts[verts_in_face] = vpa;
08630                                         verts_in_face++;
08631                                 }
08632                         }
08633 
08634                         /* check for vpb */
08635                         found = 0;
08636                         for( i=0 ; i<verts_in_face ; i++ )
08637                         {
08638                                 if( face_verts[i] == vpb )
08639                                 {
08640                                         found = 1;
08641                                         break;
08642                                 }
08643                         }
08644                         if( !found )
08645                         {
08646                                 if( verts_in_face < 19 )
08647                                 {
08648                                         face_verts[verts_in_face] = vpb;
08649                                         verts_in_face++;
08650                                 }
08651                         }
08652 
08653                         /* check verts table */
08654                         for( j=0 ; j<BU_PTBL_END( verts ) ; j++ )
08655                         {
08656                                 struct vertex *v_tmp;
08657 
08658                                 v_tmp = (struct vertex *)BU_PTBL_GET( verts, j );
08659                                 found = 0;
08660                                 for( i=0 ; i<verts_in_face ; i++ )
08661                                 {
08662                                         if( face_verts[i] == v_tmp )
08663                                         {
08664                                                 found = 1;
08665                                                 break;
08666                                         }
08667                                 }
08668                                 if( !found )
08669                                 {
08670                                         if( verts_in_face < 19 )
08671                                         {
08672                                                 face_verts[verts_in_face] = v_tmp;
08673                                                 verts_in_face++;
08674                                         }
08675                                 }
08676                         }
08677                 }
08678 #if 1
08679                 return( 1 );
08680 #else
08681                 if( rt_g.NMG_debug & DEBUG_BASIC )
08682                 {
08683                         bu_log( "nmg_make_connect_faces: Looking for edges to split verts in face = %d\n", verts_in_face );
08684                         for( i=0 ; i<verts_in_face ; i++ )
08685                                 bu_log( "\t x%x ( %g %g %g )\n", face_verts[i], V3ARGS( face_verts[i]->vg_p->coord ) );
08686                 }
08687 
08688                 for( i=0 ; i<verts_in_face-2 ; i++ )
08689                 {
08690                         for( j=i+1 ; j<verts_in_face-1 ; j++ )
08691                         {
08692                                 for( k=j+1 ; k<verts_in_face ; k++ )
08693                                 {
08694                                         struct edgeuse *eu;
08695 
08696                                         if( rt_g.NMG_debug & DEBUG_BASIC )
08697                                         {
08698                                                 bu_log( "Checking collinearity of:\n" );
08699                                                 bu_log( "\t x%x ( %g %g %g )\n", face_verts[i], V3ARGS( face_verts[i]->vg_p->coord ) );
08700                                                 bu_log( "\t x%x ( %g %g %g )\n", face_verts[j], V3ARGS( face_verts[j]->vg_p->coord ) );
08701                                                 bu_log( "\t x%x ( %g %g %g )\n", face_verts[k], V3ARGS( face_verts[k]->vg_p->coord ) );
08702                                         }
08703 
08704                                         if( !bn_3pts_collinear( face_verts[i]->vg_p->coord,
08705                                                                 face_verts[j]->vg_p->coord,
08706                                                                 face_verts[k]->vg_p->coord, tol ) )
08707                                                 continue;
08708                                         if( rt_g.NMG_debug & DEBUG_BASIC )
08709                                                 bu_log( "\t They are collinear\n" );
08710 
08711                                         if( (eu = nmg_findeu( face_verts[i], face_verts[j], (struct shell *)NULL,
08712                                                 (struct edgeuse *)NULL, 0 ) ) )
08713                                         {
08714                                                 if( rt_g.NMG_debug & DEBUG_BASIC )
08715                                                         bu_log( "\tFound eu between x%x and x%x\n", face_verts[i], face_verts[j] );
08716 
08717                                                 if( !bn_dist_pt3_lseg3( &dist, pca,
08718                                                         face_verts[i]->vg_p->coord,
08719                                                         face_verts[j]->vg_p->coord,
08720                                                         face_verts[k]->vg_p->coord, tol ) )
08721                                                 {
08722                                                         new_eu = nmg_esplit( face_verts[k], eu, 1 );
08723                                                         Join_eus( face_verts[i], face_verts[k], face_verts[j], tol );
08724                                                 }
08725                                                 else if( rt_g.NMG_debug & DEBUG_BASIC )
08726                                                         bu_log( "\t\tvertex x%x is outside eu\n" , face_verts[k] );
08727                                         }
08728                                         if( (eu = nmg_findeu( face_verts[i], face_verts[k], (struct shell *)NULL,
08729                                                 (struct edgeuse *)NULL, 0 ) ) )
08730                                         {
08731                                                 if( rt_g.NMG_debug & DEBUG_BASIC )
08732                                                         bu_log( "\tFound eu between x%x and x%x\n", face_verts[i], face_verts[k] );
08733 
08734                                                 if( !bn_dist_pt3_lseg3( &dist, pca,
08735                                                         face_verts[i]->vg_p->coord,
08736                                                         face_verts[k]->vg_p->coord,
08737                                                         face_verts[j]->vg_p->coord, tol ) )
08738                                                 {
08739                                                         new_eu = nmg_esplit( face_verts[j], eu, 1 );
08740                                                         Join_eus( face_verts[i], face_verts[j], face_verts[k], tol );
08741                                                 }
08742                                                 else if( rt_g.NMG_debug & DEBUG_BASIC )
08743                                                         bu_log( "\t\tvertex x%x is outside eu\n" , face_verts[j] );
08744                                         }
08745                                         if( (eu = nmg_findeu( face_verts[j], face_verts[k], (struct shell *)NULL,
08746                                                 (struct edgeuse *)NULL, 0 ) ) )
08747                                         {
08748                                                 if( rt_g.NMG_debug & DEBUG_BASIC )
08749                                                         bu_log( "\tFound eu between x%x and x%x\n", face_verts[j], face_verts[k] );
08750 
08751                                                 if( !bn_dist_pt3_lseg3( &dist, pca,
08752                                                         face_verts[j]->vg_p->coord,
08753                                                         face_verts[k]->vg_p->coord,
08754                                                         face_verts[i]->vg_p->coord, tol ) )
08755                                                 {
08756                                                         new_eu = nmg_esplit( face_verts[i], eu, 1 );
08757                                                         Join_eus( face_verts[j], face_verts[i], face_verts[k], tol );
08758                                                 }
08759                                                 else if( rt_g.NMG_debug & DEBUG_BASIC )
08760                                                         bu_log( "\t\tvertex x%x is outside eu\n" , face_verts[i] );
08761                                         }
08762                                 }
08763                         }
08764                 }
08765 #endif
08766         }
08767         return( 0 );
08768 }
08769 
08770 /**     N M G _ O P E N _ S H E L L S _ C O N N E C T
08771  *
08772  *      Two open shells are connected along their free edges by building
08773  *      new faces.  The resluting closed shell is in "dst", and "src" shell
08774  *      is destroyed.  The "copy_tbl" is a translation table that provides
08775  *      a one-to-one translation between the vertices in the two shells, i.e.,
08776  *      NMG_INDEX_GETP(vertex, copy_tbl, v), where v is a pointer to a vertex
08777  *      in "dst" shell, provides a pointer to the corresponding vertex in "src" shell
08778  *
08779  *      returns:
08780  *              0 - All is well
08781  *              1 - failure
08782  */
08783 
08784 /*      structure for use by nmg_open_shells_connect */
08785 struct dangle
08786 {
08787         struct vertex *va,*vb;          /* vertices of a dangling edge in dst shell */
08788         struct vertex *v1,*v2;          /* corresponding vertices in src shell */
08789         struct bu_ptbl bad_verts;       /* list of vertices to avoid when finding path
08790                                          * from v1 to v2 */
08791         int needs_edge_breaking;
08792 };
08793 
08794 int
08795 nmg_open_shells_connect(struct shell *dst, struct shell *src, const long int **copy_tbl, const struct bn_tol *tol)
08796 {
08797         struct faceuse *fu;
08798         struct loopuse *lu;
08799         struct edgeuse *eu;
08800         struct bu_ptbl faces;
08801         struct bu_ptbl dangles;
08802         int open_src,open_dst;
08803         int i,j,k;
08804 
08805         if( rt_g.NMG_debug & DEBUG_BASIC )
08806                 bu_log( "nmg_open_shells_connect( dst=x%x , src=x%x , copy_tbl=x%x)\n" , dst , src , copy_tbl );
08807 
08808         NMG_CK_SHELL( dst );
08809         NMG_CK_SHELL( src );
08810         BN_CK_TOL( tol );
08811 
08812         open_src = nmg_check_closed_shell( src , tol );
08813         open_dst = nmg_check_closed_shell( dst , tol );
08814 
08815         if( !open_dst && !open_src )
08816         {
08817                 /* both shells are closed, just join them */
08818                 nmg_js( dst, src, tol );
08819                 return( 0 );
08820         }
08821 
08822         if( !open_dst )
08823         {
08824                 bu_log( "nmg_open_shells_connect: destination shell is closed!\n" );
08825                 return( 1 );
08826         }
08827 
08828         if( !open_src )
08829         {
08830                 bu_log( "nmg_open_shells_connect: source shell is closed!\n" );
08831                 return( 1 );
08832         }
08833 
08834         bu_ptbl_init( &dangles , 64, " &dangles ");
08835 
08836         /* find free edges in "dst" shell */
08837         for( BU_LIST_FOR( fu , faceuse , &dst->fu_hd ) )
08838         {
08839                 NMG_CK_FACEUSE( fu );
08840                 if( fu->orientation != OT_SAME )
08841                         continue;
08842                 for( BU_LIST_FOR( lu , loopuse , &fu->lu_hd ) )
08843                 {
08844                         NMG_CK_LOOPUSE( lu );
08845                         if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) == NMG_VERTEXUSE_MAGIC )
08846                                 continue;
08847 
08848                         for( BU_LIST_FOR( eu , edgeuse , &lu->down_hd ) )
08849                         {
08850                                 NMG_CK_EDGEUSE( eu );
08851                                 if( eu->eumate_p == eu->radial_p )
08852                                 {
08853                                         struct dangle *dang;
08854                                         struct vertexuse *test_vu;
08855                                         struct vertex *vpbad1,*vpbada;
08856 
08857                                         /* found a dangling edge, put it in the list */
08858                                         dang = (struct dangle *)bu_malloc( sizeof( struct dangle ) ,
08859                                                 "nmg_open_shells_connect: dang" );
08860 
08861                                         dang->needs_edge_breaking = 0;
08862                                         dang->va = eu->vu_p->v_p;
08863                                         NMG_CK_VERTEX( dang->va );
08864                                         dang->v1 = NMG_INDEX_GETP(vertex, copy_tbl, dang->va );
08865                                         NMG_CK_VERTEX( dang->v1 );
08866                                         dang->vb = eu->eumate_p->vu_p->v_p;
08867                                         NMG_CK_VERTEX( dang->vb );
08868                                         dang->v2 = NMG_INDEX_GETP(vertex, copy_tbl, dang->vb );
08869                                         NMG_CK_VERTEX( dang->v2 );
08870 
08871                                         bu_ptbl_init( &dang->bad_verts , 64, " &dang->bad_verts ");
08872 
08873                                         /* look for other dangling edges around this one
08874                                          * to establish direction for nmg_find_path
08875                                          * to look in
08876                                          */
08877 
08878                                         for( BU_LIST_FOR( test_vu , vertexuse , &dang->va->vu_hd ) )
08879                                         {
08880                                                 struct edgeuse *test_eu;
08881 
08882                                                 if( *test_vu->up.magic_p != NMG_EDGEUSE_MAGIC )
08883                                                         continue;
08884 
08885                                                 test_eu = test_vu->up.eu_p;
08886                                                 if( test_eu == eu )
08887                                                         continue;
08888 
08889                                                 if( test_eu->eumate_p->vu_p->v_p == dang->vb )
08890                                                         continue;
08891 
08892                                                 if( test_eu->eumate_p == test_eu->radial_p )
08893                                                 {
08894                                                         /* another dangling edge, don't want
08895                                                          * nmg_find_path to wander off
08896                                                          * in this direction
08897                                                          */
08898                                                         vpbada = test_eu->eumate_p->vu_p->v_p;
08899                                                         vpbad1 = NMG_INDEX_GETP( vertex , copy_tbl , vpbada );
08900                                                         if( vpbad1 )
08901                                                                 bu_ptbl_ins( &dang->bad_verts , (long *)vpbad1 );
08902                                                 }
08903                                         }
08904 
08905                                         for( BU_LIST_FOR( test_vu , vertexuse , &dang->vb->vu_hd ) )
08906                                         {
08907                                                 struct edgeuse *test_eu;
08908 
08909                                                 if( *test_vu->up.magic_p != NMG_EDGEUSE_MAGIC )
08910                                                         continue;
08911 
08912                                                 test_eu = test_vu->up.eu_p;
08913                                                 if( test_eu == eu->eumate_p )
08914                                                         continue;
08915 
08916 
08917                                                 if( test_eu->eumate_p->vu_p->v_p == dang->va )
08918                                                         continue;
08919 
08920                                                 if( test_eu->eumate_p == test_eu->radial_p )
08921                                                 {
08922                                                         /* another dangling edge, don't want
08923                                                          * nmg_find_path to wander off
08924                                                          * in this direction
08925                                                          */
08926                                                         vpbada = test_eu->eumate_p->vu_p->v_p;
08927                                                         vpbad1 = NMG_INDEX_GETP( vertex , copy_tbl , vpbada );
08928                                                         if( vpbad1 )
08929                                                                 bu_ptbl_ins( &dang->bad_verts , (long *)vpbad1 );
08930                                                 }
08931                                         }
08932                                 bu_ptbl_ins( &dangles , (long *)dang );
08933                                 }
08934                         }
08935                 }
08936         }
08937 
08938         /* now build the faces to connect the dangling edges */
08939         for( i=0 ; i<BU_PTBL_END( &dangles ) ; i++ )
08940         {
08941                 struct dangle *dang;
08942                 struct bu_ptbl *verts;
08943 
08944                 dang = (struct dangle *)BU_PTBL_GET( &dangles , i );
08945 
08946                 /* find vertices between vp1 and vp2 */
08947                 verts = nmg_find_path( dang->v1 , dang->v2 , &dang->bad_verts , src );
08948 
08949                 /* make faces connecting the two shells */
08950                 if( BU_PTBL_END( verts ) > 1 )
08951                         dang->needs_edge_breaking = nmg_make_connect_faces( dst , dang->va , dang->vb , verts , tol );
08952                 else
08953                 {
08954                         bu_log( "nmg_open_shells_connect: unable to make connecting face\n" );
08955                         bu_log( "\tfor edge from x%x ( %f %f %f )\n\t\tto x%x ( %f %f %f )\n",
08956                                 dang->va, V3ARGS( dang->va->vg_p->coord ),
08957                                 dang->vb, V3ARGS( dang->vb->vg_p->coord ) );
08958                 }
08959 
08960                 bu_ptbl_free( verts );
08961                 bu_ptbl_free( &dang->bad_verts );
08962         }
08963 
08964         while( BU_PTBL_END( &dangles ) )
08965         {
08966                 struct vertex *verts[4];
08967                 struct dangle *dang;
08968                 struct edgeuse *eu;
08969                 fastf_t dist;
08970                 point_t pca;
08971                 int done;
08972 
08973                 dang = (struct dangle *)BU_PTBL_GET( &dangles , BU_PTBL_END( &dangles )-1 );
08974                 if( dang->needs_edge_breaking )
08975                 {
08976 
08977                         dang = (struct dangle *)BU_PTBL_GET( &dangles, BU_PTBL_END( &dangles ) - 1 );
08978                         verts[0] = dang->va;
08979                         verts[1] = dang->vb;
08980                         verts[2] = dang->v1;
08981                         verts[3] = dang->v2;
08982 
08983                         for( i=0 ; i<3 ; i++ )
08984                         {
08985                                 for( j=i+1 ; j<4 ; j++ )
08986                                 {
08987                                         eu = nmg_findeu( verts[i], verts[j],
08988                                                 0, 0, 1 );
08989                                         if( !eu )
08990                                                 continue;
08991 
08992                                         for( k=0 ; k<4 ; k++ )
08993                                         {
08994                                                 if( k == i || k == j )
08995                                                         continue;
08996                                                 if( bn_dist_pt3_lseg3( &dist, pca,
08997                                                         verts[i]->vg_p->coord,
08998                                                         verts[j]->vg_p->coord,
08999                                                         verts[k]->vg_p->coord,  tol ) )
09000                                                         continue;
09001 
09002                                                 (void)nmg_esplit( verts[k], eu, 1 );
09003                                         }
09004                                 }
09005                         }
09006                         done = 0;
09007                         while( !done )
09008                         {
09009                                 done = 1;
09010                                 for( i=0 ; i<3 ; i++ )
09011                                 {
09012                                         for( j=i+1 ; j<4 ; j++ )
09013                                         {
09014                                                 struct edgeuse *eu2;
09015 
09016                                                 eu = nmg_findeu( verts[i], verts[j], 0, 0, 1 );
09017                                                 if( !eu )
09018                                                         continue;
09019 
09020                                                 eu2 = nmg_findeu( verts[i], verts[j], 0, eu, 1 );
09021                                                 if( !eu2 )
09022                                                         continue;
09023 
09024                                                 if( eu2->vu_p->v_p == eu->vu_p->v_p )
09025                                                         eu2 = eu2->eumate_p;
09026                                                 nmg_je( eu, eu2 );
09027                                                 done = 0;
09028                                         }
09029                                 }
09030                         }
09031                 }
09032                 bu_ptbl_rm( &dangles , (long *)dang );
09033                 bu_free( (char *)dang , "nmg_open_shells_connect: dang" );
09034         }
09035 
09036         bu_ptbl_free( &dangles);
09037         nmg_js( dst , src , tol );
09038 
09039         /* now glue it all together */
09040         bu_ptbl_init( &faces , 64, " &faces ");
09041         for( BU_LIST_FOR( fu , faceuse , &dst->fu_hd ) )
09042         {
09043                 NMG_CK_FACEUSE( fu );
09044                 if( fu->orientation == OT_SAME )
09045                         bu_ptbl_ins( &faces , (long *)fu );
09046         }
09047         nmg_gluefaces( (struct faceuse **)BU_PTBL_BASEADDR( &faces) , BU_PTBL_END( &faces ), tol );
09048         bu_ptbl_free( &faces );
09049 
09050         return( 0 );
09051 }
09052 
09053 /**     N M G _ I N _ V E R T
09054  *
09055  *      Move vertex so it is at the intersection of the newly created faces
09056  *
09057  *      This routine is used by "nmg_extrude_shell" to move vertices. Each
09058  *      plane has already been moved a distance inward and
09059  *      the surface normals have been reversed.
09060  *
09061  *      if approximate is non-zero, then the coordinates of the new
09062  *      vertex may be calculated as the point with minimum distance
09063  *      to all the faces that intersect at the vertex for vertices
09064  *      where more than three faces intersect.
09065  *
09066  */
09067 int
09068 nmg_in_vert(struct vertex *new_v, const int approximate, const struct bn_tol *tol)
09069 {
09070         struct bu_ptbl faces;
09071         int failed=0;
09072         int free_edges=0;
09073         int face_count;
09074 
09075         if( rt_g.NMG_debug & DEBUG_BASIC )
09076                 bu_log( "nmg_in_vert( new_v=x%x at ( %f %f %f) )\n" , new_v , V3ARGS( new_v->vg_p->coord ) );
09077 
09078         NMG_CK_VERTEX( new_v );
09079         BN_CK_TOL( tol );
09080 
09081         bu_ptbl_init( &faces , 64, " &faces ");
09082 
09083         /* find all unique faces that intersect at this vertex (new_v) */
09084         face_count = nmg_find_isect_faces( new_v , &faces , &free_edges , tol );
09085 
09086         if( rt_g.NMG_debug & DEBUG_BASIC )
09087                 bu_log( "\tnmg_in_vert: found %d faces at new_v, %d free_edges\n",
09088                         face_count, free_edges );
09089 
09090         if( (face_count < 4 && !free_edges) || face_count < 3 )
09091         {
09092                 if( nmg_simple_vertex_solve( new_v , &faces, tol ) )
09093                 {
09094                         failed = 1;
09095                         bu_log( "Could not solve simple vertex\n" );
09096                 }
09097         }
09098         else
09099         {
09100                 if( nmg_complex_vertex_solve( new_v , &faces , free_edges , approximate , tol ) )
09101                 {
09102                         failed = 1;
09103                         bu_log( "Could not solve complex vertex\n" );
09104                 }
09105         }
09106 
09107         /* Free memory */
09108         bu_ptbl_free( &faces );
09109 
09110         return( failed );
09111 }
09112 
09113 /**             N M G _ M I R R O R _ M O D E L
09114  *
09115  *      mirror model across the y-axis
09116  *      this does not copy the model, it changes the
09117  *      model passed to it
09118  */
09119 
09120 void
09121 nmg_mirror_model(struct model *m)
09122 {
09123         struct bu_ptbl vertices;
09124         struct nmgregion *r;
09125         int i;
09126         long *flags;
09127 
09128         NMG_CK_MODEL( m );
09129 
09130         /* mirror all vertices across the y axis */
09131         nmg_vertex_tabulate( &vertices , &m->magic );
09132 
09133         for( i=0 ; i<BU_PTBL_END( &vertices ) ; i++ )
09134         {
09135                 struct vertex *v;
09136 
09137                 v = (struct vertex *)BU_PTBL_GET( &vertices , i );
09138                 NMG_CK_VERTEX( v );
09139 
09140                 v->vg_p->coord[Y] = (-v->vg_p->coord[Y]);
09141         }
09142         (void)bu_ptbl_free( &vertices );
09143 
09144         /* adjust the direction of all the faces */
09145         flags = (long *)bu_calloc( m->maxindex , sizeof( long ) , "nmg_mirror_model: flags" );
09146         for( BU_LIST_FOR( r , nmgregion , &m->r_hd ) )
09147         {
09148                 struct shell *s;
09149 
09150                 for( BU_LIST_FOR( s , shell , &r->s_hd ) )
09151                 {
09152                         struct faceuse *fu;
09153 
09154                         for( BU_LIST_FOR( fu , faceuse , &s->fu_hd ) )
09155                         {
09156                                 int orientation;
09157 
09158                                 if( NMG_INDEX_TEST_AND_SET( flags , fu ) )
09159                                 {
09160                                         /* switch orientations of all faceuses */
09161                                         orientation = fu->orientation;
09162                                         fu->orientation = fu->fumate_p->orientation;
09163                                         fu->fumate_p->orientation = orientation;
09164                                         NMG_INDEX_SET( flags , fu->fumate_p );
09165 
09166                                         if( NMG_INDEX_TEST_AND_SET( flags , fu->f_p->g.plane_p ) )
09167                                         {
09168                                                 /* correct normal vector */
09169                                                 fu->f_p->g.plane_p->N[Y] = (-fu->f_p->g.plane_p->N[Y]);
09170                                         }
09171                                 }
09172                         }
09173                 }
09174         }
09175 
09176         bu_free( (char *)flags , "nmg_mirror_model: flags " );
09177 }
09178 
09179 int
09180 nmg_kill_cracks(struct shell *s)
09181 {
09182         struct faceuse *fu;
09183         struct faceuse *fu_next;
09184         int empty_shell=0;
09185 
09186         if( rt_g.NMG_debug & DEBUG_BASIC )
09187                 bu_log( "nmg_kill_cracks( s=%x )\n" , s );
09188 
09189         NMG_CK_SHELL( s );
09190 #if 1
09191         /* Loops may be inadvertently connected with a crack,
09192          * this code is to dissconnect them and kill the connecting crack.
09193          * Look for cracks that are two EU's from one loop that
09194          * share the same edge, but are not consectutive in the loop.
09195          * This will require a split_lu to handle.
09196          */
09197         for( BU_LIST_FOR( fu, faceuse, &s->fu_hd ) )
09198         {
09199                 struct loopuse *lu;
09200                 NMG_CK_FACEUSE( fu );
09201 
09202                 if( fu->orientation != OT_SAME )
09203                         continue;
09204 
09205                 for( BU_LIST_FOR( lu, loopuse, &fu->lu_hd ) )
09206                 {
09207                         struct edgeuse *eu;
09208 
09209                         NMG_CK_LOOPUSE( lu );
09210 
09211                         if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) != NMG_EDGEUSE_MAGIC )
09212                                 continue;
09213 again:
09214                         for( BU_LIST_FOR( eu, edgeuse, &lu->down_hd ) )
09215                         {
09216                                 struct edgeuse *eu2;
09217 
09218                                 NMG_CK_EDGEUSE( eu );
09219 
09220                                 for( BU_LIST_FOR( eu2, edgeuse, &lu->down_hd ) )
09221                                 {
09222                                         struct loopuse *new_lu1;
09223                                         struct loopuse *new_lu2;
09224 
09225                                         if( eu == eu2 )
09226                                                 continue;
09227 
09228                                         /* check if eu and eu2 share an edge */
09229                                         if( eu->e_p != eu2->e_p )
09230                                                 continue;
09231 
09232                                         if( eu2 == BU_LIST_PNEXT_CIRC( edgeuse, &eu->l ) )
09233                                         {
09234                                                 /* this is just a regular crack, catch it later */
09235                                                 continue;
09236                                         }
09237                                         if( eu2 == BU_LIST_PPREV_CIRC( edgeuse, &eu->l ) )
09238                                         {
09239                                                 /* this is just a regular crack, catch it later */
09240                                                 continue;
09241                                         }
09242 
09243                                         if( eu->vu_p->v_p == eu2->vu_p->v_p )
09244                                         {
09245                                                 /* This must be part of an accordion, catch it later */
09246                                                 continue;
09247                                         }
09248 
09249                                         if( eu->eumate_p->vu_p->v_p != eu2->vu_p->v_p )
09250                                         {
09251                                                 /* this is a problem!!! */
09252                                                 bu_log( "nmg_kill_cracks: found a strange crack at eu1=x%x, eu2=x%x\n", eu, eu2 );
09253                                                 nmg_pr_lu_briefly( lu, "" );
09254                                                 rt_bomb( "nmg_kill_cracks: found a strange crack\n" );
09255                                         }
09256 
09257                                         new_lu1 = nmg_split_lu_at_vu( lu, eu->vu_p );
09258                                         new_lu2 = nmg_split_lu_at_vu( new_lu1, eu2->vu_p );
09259 
09260                                         nmg_klu( new_lu2 );
09261 
09262                                         nmg_lu_reorient( lu );
09263                                         nmg_lu_reorient( new_lu1 );
09264 
09265                                         goto again;
09266                                 }
09267                         }
09268                 }
09269         }
09270 #endif
09271         fu = BU_LIST_FIRST( faceuse, &s->fu_hd );
09272         while( BU_LIST_NOT_HEAD( fu, &s->fu_hd ) )
09273         {
09274                 struct loopuse *lu;
09275                 struct loopuse *lu_next;
09276                 int empty_face=0;
09277 
09278                 NMG_CK_FACEUSE( fu );
09279 
09280                 fu_next = BU_LIST_PNEXT( faceuse, &fu->l );
09281                 while( BU_LIST_NOT_HEAD( fu_next, &s->fu_hd )
09282                         && fu_next == fu->fumate_p )
09283                                 fu_next = BU_LIST_PNEXT( faceuse, &fu_next->l );
09284 
09285                 lu = BU_LIST_FIRST( loopuse, &fu->lu_hd );
09286                 while( BU_LIST_NOT_HEAD( lu, &fu->lu_hd ) )
09287                 {
09288                         struct edgeuse *eu;
09289                         struct edgeuse *eu_next;
09290                         int empty_loop=0;
09291 
09292                         NMG_CK_LOOPUSE( lu );
09293 
09294                         lu_next = BU_LIST_PNEXT( loopuse, &lu->l );
09295 
09296                         if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) != NMG_EDGEUSE_MAGIC )
09297                         {
09298                                 lu = lu_next;
09299                                 continue;
09300                         }
09301 
09302 crack_top:
09303                         for( BU_LIST_FOR( eu, edgeuse, &lu->down_hd ) )
09304                         {
09305                                 NMG_CK_EDGEUSE( eu );
09306 
09307                                 eu_next = BU_LIST_PNEXT_CIRC( edgeuse, &eu->l );
09308                                 NMG_CK_EDGEUSE( eu_next );
09309                                 /* check if eu and eu_next form a jaunt */
09310                                 if( eu->vu_p->v_p != eu_next->eumate_p->vu_p->v_p )
09311                                         continue;
09312 
09313                                 if( nmg_keu( eu ) )
09314                                         empty_loop = 1;
09315                                 else if( nmg_keu( eu_next ) )
09316                                         empty_loop = 1;
09317 
09318                                 if( empty_loop )
09319                                         break;
09320 
09321                                 goto crack_top;
09322                         }
09323                         if( empty_loop )
09324                         {
09325                                 if( nmg_klu( lu ) )
09326                                 {
09327                                         empty_face = 1;
09328                                         break;
09329                                 }
09330                         }
09331                         lu = lu_next;
09332                 }
09333                 if( empty_face )
09334                 {
09335                         if( nmg_kfu( fu ) )
09336                         {
09337                                 empty_shell = 1;
09338                                 break;
09339                         }
09340                 }
09341                 fu = fu_next;
09342         }
09343 
09344         if( empty_shell )
09345         {
09346                 if( rt_g.NMG_debug & DEBUG_BASIC )
09347                         bu_log( "nmg_kill_cracks: ret = %d\n" , empty_shell );
09348                 return( empty_shell );
09349         }
09350 
09351 
09352         if( rt_g.NMG_debug & DEBUG_BASIC )
09353                 bu_log( "nmg_kill_cracks: ret = %d\n" , empty_shell );
09354 
09355         return( empty_shell );
09356 }
09357 
09358 int
09359 nmg_kill_zero_length_edgeuses(struct model *m)
09360 {
09361         struct nmgregion *r;
09362         int empty_model=0;
09363 
09364         if( rt_g.NMG_debug & DEBUG_BASIC )
09365                 bu_log( "nmg_kill_zero_length_edgeuses( m=%x )\n", m );
09366 
09367         NMG_CK_MODEL( m );
09368 
09369         r = BU_LIST_FIRST( nmgregion, &m->r_hd );
09370         while( BU_LIST_NOT_HEAD( r, &m->r_hd ) )
09371         {
09372                 struct nmgregion *next_r;
09373                 struct shell *s;
09374                 int empty_region=0;
09375 
09376                 NMG_CK_REGION( r );
09377 
09378                 next_r = BU_LIST_PNEXT( nmgregion, &r->l );
09379 
09380                 s = BU_LIST_FIRST( shell, &r->s_hd );
09381                 while( BU_LIST_NOT_HEAD( s, &r->s_hd ) )
09382                 {
09383                         struct shell *next_s;
09384                         struct faceuse *fu;
09385                         int empty_shell=0;
09386 
09387                         NMG_CK_SHELL( s );
09388 
09389                         next_s = BU_LIST_PNEXT( shell, &s->l );
09390 
09391                         fu = BU_LIST_FIRST( faceuse, &s->fu_hd );
09392                         while( BU_LIST_NOT_HEAD( fu, &s->fu_hd ) )
09393                         {
09394                                 struct loopuse *lu;
09395                                 struct faceuse *next_fu;
09396                                 int empty_fu=0;
09397 
09398                                 NMG_CK_FACEUSE( fu );
09399 
09400                                 next_fu = BU_LIST_PNEXT( faceuse, &fu->l );
09401                                 if( next_fu == fu->fumate_p )
09402                                         next_fu = BU_LIST_PNEXT( faceuse, &next_fu->l );
09403 
09404                                 lu = BU_LIST_FIRST( loopuse, &fu->lu_hd );
09405                                 while( BU_LIST_NOT_HEAD( lu, &fu->lu_hd ) )
09406                                 {
09407                                         struct edgeuse *eu;
09408                                         struct loopuse *next_lu;
09409                                         int empty_loop=0;
09410 
09411                                         NMG_CK_LOOPUSE( lu );
09412 
09413                                         next_lu = BU_LIST_PNEXT( loopuse, &lu->l );
09414 
09415                                         if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) != NMG_EDGEUSE_MAGIC )
09416                                         {
09417                                                 lu = next_lu;
09418                                                 continue;
09419                                         }
09420 
09421                                         eu = BU_LIST_FIRST( edgeuse, &lu->down_hd );
09422                                         while( BU_LIST_NOT_HEAD( eu, &lu->down_hd ) )
09423                                         {
09424                                                 struct edgeuse *next_eu;
09425                                                 struct edgeuse *next_eu_circ;
09426 
09427                                                 NMG_CK_EDGEUSE( eu );
09428 
09429                                                 next_eu = BU_LIST_PNEXT( edgeuse, &eu->l );
09430                                                 next_eu_circ = BU_LIST_PNEXT_CIRC( edgeuse, &eu->l );
09431 
09432                                                 if( eu->vu_p->v_p == next_eu_circ->vu_p->v_p )
09433                                                 {
09434                                                         /* found a zero length edgeuse */
09435                                                         if( nmg_keu( eu ) )
09436                                                                 empty_loop = 1;
09437                                                 }
09438                                                 eu = next_eu;
09439                                         }
09440                                         if( empty_loop )
09441                                         {
09442                                                 if( nmg_klu( lu ) )
09443                                                         empty_fu = 1;
09444                                         }
09445                                         lu = next_lu;
09446                                 }
09447                                 if( empty_fu )
09448                                 {
09449                                         if( nmg_kfu( fu ) )
09450                                                 empty_shell = 1;
09451                                 }
09452                                 fu = next_fu;
09453                         }
09454                         if( empty_shell )
09455                         {
09456                                 if( nmg_ks( s ) )
09457                                         empty_region = 1;
09458                         }
09459                         s = next_s;
09460                 }
09461                 if( empty_region )
09462                 {
09463                         if( nmg_kr( r ) )
09464                                 empty_model = 1;
09465                 }
09466                 r = next_r;
09467         }
09468 
09469         if( empty_model )
09470                 return( 1 );
09471         else
09472                 return( 0 );
09473 }
09474 
09475 /**     N M G _ M A K E _ F A C E S _ W I T H I N _ T O L
09476  *
09477  * Check all vertices on faces of specified shell. Any face containing
09478  * vertices more than tol->dist off the plane of the face will be
09479  * triangulated and broken into seperate faces
09480  */
09481 void
09482 nmg_make_faces_within_tol(struct shell *s, const struct bn_tol *tol)
09483 {
09484         struct bu_ptbl faceuses;
09485         struct faceuse *fu;
09486         int i;
09487 
09488         NMG_CK_SHELL( s );
09489         BN_CK_TOL( tol );
09490 
09491         bu_ptbl_init( &faceuses, 64, " &faceuses");
09492 
09493         for( BU_LIST_FOR( fu, faceuse, &s->fu_hd ) )
09494         {
09495                 NMG_CK_FACEUSE( fu );
09496 
09497                 if( fu->orientation != OT_SAME )
09498                         continue;
09499 
09500                 bu_ptbl_ins( &faceuses, (long *)fu );
09501         }
09502 
09503         for (i = 0; i < BU_PTBL_END( &faceuses ); i++)
09504         {
09505                 fu = (struct faceuse *)BU_PTBL_GET( &faceuses, i );
09506 
09507                 /* check if all the vertices for this face lie on the plane */
09508                 if( nmg_ck_fu_verts( fu, fu->f_p, tol ) )
09509                 {
09510                         plane_t pl;
09511 
09512                         /* Need to triangulate this face */
09513                         nmg_triangulate_fu( fu, tol );
09514 
09515                         /* split each triangular loop into its own face */
09516                         (void)nmg_split_loops_into_faces( &fu->l.magic, tol );
09517 
09518                         if( nmg_calc_face_plane( fu, pl ) )
09519                         {
09520                                 bu_log( "nmg_make_faces_within_tol(): nmg_calc_face_plane() failed\n" );
09521                                 rt_bomb( "nmg_make_faces_within_tol(): nmg_calc_face_plane() failed" );
09522                         }
09523                         nmg_face_new_g( fu, pl );
09524                 }
09525         }
09526 
09527         for( BU_LIST_FOR( fu, faceuse, &s->fu_hd ) )
09528         {
09529                 plane_t pl;
09530 
09531                 if( fu->orientation != OT_SAME )
09532                         continue;
09533 
09534                 if( bu_ptbl_locate( &faceuses, (long *)fu ) != (-1) )
09535                         continue;
09536 
09537                 if( nmg_calc_face_plane( fu, pl ) )
09538                 {
09539                         bu_log( "nmg_make_faces_within_tol(): nmg_calc_face_plane() failed\n" );
09540                         rt_bomb( "nmg_make_faces_within_tol(): nmg_calc_face_plane() failed" );
09541                 }
09542 
09543                 nmg_face_new_g( fu, pl );
09544         }
09545 
09546         bu_ptbl_free( &faceuses);
09547 }
09548 
09549 void
09550 nmg_intersect_loops_self(struct shell *s, const struct bn_tol *tol)
09551 {
09552         struct faceuse *fu;
09553         struct bu_ptbl edgeuses;
09554 
09555         NMG_CK_SHELL( s );
09556         BN_CK_TOL( tol );
09557 
09558         bu_ptbl_init( &edgeuses, 64, " &edgeuses");
09559 
09560         for( BU_LIST_FOR( fu, faceuse, &s->fu_hd ) )
09561         {
09562                 struct loopuse *lu;
09563 
09564                 NMG_CK_FACEUSE( fu );
09565 
09566                 if( fu->orientation != OT_SAME )
09567                         continue;
09568 
09569                 for( BU_LIST_FOR( lu, loopuse, &fu->lu_hd ) )
09570                 {
09571                         struct edgeuse *eu;
09572                         int i,j;
09573 
09574                         NMG_CK_LOOPUSE( lu );
09575 
09576                         if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) != NMG_EDGEUSE_MAGIC )
09577                                 continue;
09578 top:
09579                         for( BU_LIST_FOR( eu, edgeuse, &lu->down_hd ) )
09580                                 bu_ptbl_ins( &edgeuses, (long *)eu );
09581 
09582                         for( i=0 ; i<BU_PTBL_END( &edgeuses ); i++ )
09583                         {
09584                                 vect_t eu_dir;
09585 
09586                                 eu = (struct edgeuse *)BU_PTBL_GET( &edgeuses, i );
09587                                 VSUB2( eu_dir, eu->eumate_p->vu_p->v_p->vg_p->coord, eu->vu_p->v_p->vg_p->coord );
09588 
09589                                 for( j=i+1 ; j<BU_PTBL_END( &edgeuses ) ; j++ )
09590                                 {
09591                                         struct edgeuse *eu2;
09592                                         struct edgeuse *new_eu=(struct edgeuse *)NULL;
09593                                         struct vertex *v;
09594                                         vect_t eu2_dir;
09595                                         point_t int_pt;
09596                                         fastf_t dist[2];
09597                                         int code;
09598 
09599                                         eu2 = (struct edgeuse *)BU_PTBL_GET( &edgeuses, j );
09600                                         VSUB2( eu2_dir, eu2->eumate_p->vu_p->v_p->vg_p->coord, eu2->vu_p->v_p->vg_p->coord );
09601 
09602                                         code = bn_isect_lseg3_lseg3( dist, eu->vu_p->v_p->vg_p->coord, eu_dir,
09603                                                 eu2->vu_p->v_p->vg_p->coord, eu2_dir, tol );
09604 
09605                                         if( code < 0 )  /* no intersection */
09606                                                 continue;
09607 
09608                                         if( code == 0 )
09609                                         {
09610                                                 if( dist[0] > 0.0 && dist[0] != 1.0 )
09611                                                 {
09612                                                         VJOIN1( int_pt, eu->vu_p->v_p->vg_p->coord, dist[0], eu_dir );
09613                                                         v = (struct vertex *)NULL;
09614                                                         new_eu = nmg_ebreaker( v, eu, tol );
09615                                                         nmg_vertex_gv( new_eu->vu_p->v_p, int_pt );
09616                                                 }
09617                                                 if( dist[1] > 0.0 && dist[1] != 1.0 )
09618                                                 {
09619                                                         VJOIN1( int_pt, eu->vu_p->v_p->vg_p->coord, dist[1], eu_dir );
09620                                                         v = (struct vertex *)NULL;
09621                                                         new_eu = nmg_ebreaker( v, eu, tol );
09622                                                         nmg_vertex_gv( new_eu->vu_p->v_p, int_pt );
09623                                                 }
09624                                         }
09625                                         else
09626                                         {
09627                                                 if( dist[0] != 0.0 && dist[0] != 1.0 )
09628                                                 {
09629                                                         VJOIN1( int_pt, eu->vu_p->v_p->vg_p->coord, dist[0], eu_dir );
09630                                                         v = (struct vertex *)NULL;
09631                                                         new_eu = nmg_ebreaker( v, eu, tol );
09632                                                         nmg_vertex_gv( new_eu->vu_p->v_p, int_pt );
09633                                                 }
09634                                                 if( dist[1] != 0.0 && dist[1] != 1.0 )
09635                                                 {
09636                                                         VJOIN1( int_pt, eu2->vu_p->v_p->vg_p->coord, dist[1], eu2_dir );
09637                                                         v = (struct vertex *)NULL;
09638                                                         new_eu = nmg_ebreaker( v, eu2, tol );
09639                                                         nmg_vertex_gv( new_eu->vu_p->v_p, int_pt );
09640                                                 }
09641 
09642                                         }
09643                                         if( new_eu )
09644                                         {
09645                                                 bu_ptbl_reset( &edgeuses);
09646                                                 goto top;
09647                                         }
09648                                 }
09649                         }
09650 
09651                         bu_ptbl_reset( &edgeuses);
09652                 }
09653         }
09654 
09655         bu_ptbl_free( &edgeuses);
09656 }
09657 
09658 /**     R T _ J O I N _ C N U R B S
09659  *
09660  * Join a list of cnurb structs into a single cnurb.
09661  * The curves must have matching endpoints, otherwise
09662  * nothing is done and (struct cnurb *)NULL is returned.
09663  *
09664  * Returns:
09665  *      A single cnurb structure that joins all the
09666  *      cnurbs on the list.
09667  */
09668 struct edge_g_cnurb *
09669 rt_join_cnurbs(struct bu_list *crv_head)
09670 {
09671         struct edge_g_cnurb *crv,*next_crv;
09672         struct edge_g_cnurb *new_crv=(struct edge_g_cnurb *)NULL;
09673         fastf_t knot_delta=0.0;
09674         fastf_t last_knot=0.0;
09675         int ncoords;
09676         int knot_index=(-1);
09677         int max_order=0;
09678         int ctl_points=1;
09679         int ctl_index=(-1);
09680         int knot_length=0;
09681         int pt_type=0;
09682         int curve_count=0;
09683         int i,j;
09684 
09685         BU_CK_LIST_HEAD( crv_head );
09686 
09687         /* Check that all curves are the same pt_type and
09688          * have mutliplicity equal to order at endpoints.
09689          */
09690         for( BU_LIST_FOR( crv, edge_g_cnurb, crv_head ) )
09691         {
09692                 curve_count++;
09693                 rt_nurb_c_print( crv);
09694                 if( crv->order > max_order )
09695                         max_order = crv->order;
09696 
09697                 i = 0;
09698                 while( crv->k.knots[++i] == crv->k.knots[0] );
09699                 if( i != crv->order )
09700                 {
09701                         bu_log( "Curve does not have multiplicity equal to order at start:\n" );
09702                         rt_nurb_c_print( crv);
09703                         return( new_crv );
09704                 }
09705 
09706                 i = crv->k.k_size - 1;
09707                 while( crv->k.knots[--i] == crv->k.knots[crv->k.k_size - 1] );
09708                 if( crv->k.k_size - i - 1 != crv->order )
09709                 {
09710                         bu_log( "Curve does not have multiplicity equal to order at end:\n" );
09711                         rt_nurb_c_print( crv);
09712                         return( new_crv );
09713                 }
09714 
09715                 if( pt_type == 0 )
09716                         pt_type = crv->pt_type;
09717                 else
09718                 {
09719                         if( crv->pt_type != pt_type )
09720                         {
09721                                 bu_log( "rt_join_cnurbs: curves are not the same pt_type (%d vs %d)\n",
09722                                         pt_type, crv->pt_type );
09723                                 return( new_crv );
09724                         }
09725                 }
09726         }
09727 
09728         /* If there is only one entry on list, just return it */
09729         if( curve_count < 2 )
09730         {
09731                 crv = BU_LIST_FIRST( edge_g_cnurb, crv_head );
09732                 BU_LIST_DEQUEUE( &crv->l );
09733                 return( crv );
09734         }
09735 
09736         /* Raise each curve to order max_order */
09737         for( BU_LIST_FOR( crv, edge_g_cnurb, crv_head ) )
09738         {
09739                 if( crv->order == max_order )
09740                         continue;
09741 
09742                 /* This curve must have its order raised to max_order */
09743                 /* XXXX Need a routine to raise order of a curve */
09744                 rt_bomb( "rt_join_cnurbs: Need to raise order of curve\n" );
09745         }
09746 
09747         /* Check that endponts match */
09748         crv = BU_LIST_FIRST( edge_g_cnurb, crv_head );
09749         ncoords = RT_NURB_EXTRACT_COORDS( crv->pt_type );
09750         next_crv = BU_LIST_NEXT( edge_g_cnurb, &crv->l );
09751         while( BU_LIST_NOT_HEAD( &next_crv->l, crv_head ) )
09752         {
09753                 int endpoints_equal;
09754 
09755                 endpoints_equal = 1;
09756 
09757                 for( i=0 ; i<ncoords ; i++ )
09758                 {
09759                         /* It is tempting to use a tolerance here, but these coordinates may be
09760                          * x/y/z or x/y or u/v, ...
09761                          */
09762                         if( crv->ctl_points[(crv->c_size-1)*ncoords+i] == next_crv->ctl_points[i] )
09763                                 continue;
09764                         else
09765                         {
09766                                 endpoints_equal = 0;
09767                                 break;
09768                         }
09769                 }
09770                 if( endpoints_equal )
09771                 {
09772                         /* Nothing needed here, go to next curve */
09773                         crv = next_crv;
09774                         next_crv = BU_LIST_NEXT( edge_g_cnurb, &crv->l );
09775                         continue;
09776                 }
09777 
09778                 bu_log( "rt_join_cnurbs: Curve endpoints do not match:\n" );
09779                 rt_nurb_c_print( crv );
09780                 rt_nurb_c_print( next_crv );
09781                 return( new_crv );
09782         }
09783 
09784         /* Get new knot size and polygon size */
09785         crv = BU_LIST_FIRST( edge_g_cnurb, crv_head );
09786         knot_length = crv->order;
09787         for( BU_LIST_FOR( crv, edge_g_cnurb, crv_head ) )
09788         {
09789                 ctl_points += (crv->c_size - 1);
09790                 knot_length += (crv->k.k_size - crv->order - 1);
09791 
09792         }
09793         knot_length++;
09794 
09795         new_crv = rt_nurb_new_cnurb( max_order, knot_length, ctl_points, pt_type );
09796 
09797         crv = BU_LIST_FIRST( edge_g_cnurb, crv_head );
09798 
09799         /* copy first knot values from first curve */
09800         for( i=0 ; i<crv->order ; i++ )
09801                 new_crv->k.knots[++knot_index] = crv->k.knots[i];
09802 
09803         /* copy first control point from first curve */
09804         for( j=0 ; j<ncoords ; j++ )
09805                 new_crv->ctl_points[j] = crv->ctl_points[j];
09806 
09807         ctl_index = 0;
09808         knot_delta = new_crv->k.knots[knot_index];
09809 
09810         /* copy each curve to the new combined curve */
09811         for( BU_LIST_FOR( crv, edge_g_cnurb, crv_head ) )
09812         {
09813                 /* copy interior knots */
09814                 for( i=crv->order ; i<crv->k.k_size-crv->order ; i++ )
09815                 {
09816                         new_crv->k.knots[++knot_index] = crv->k.knots[i] + knot_delta;
09817                 }
09818 
09819                 /* copy endpoint knots (reduce multiplicity by one) */
09820                 for( i=0 ; i<crv->order-1 ; i++ )
09821                         new_crv->k.knots[++knot_index] = crv->k.knots[crv->k.k_size-1] + knot_delta;
09822 
09823                 knot_delta += crv->k.knots[crv->k.k_size-1];
09824                 last_knot = new_crv->k.knots[knot_index];
09825 
09826                 /* copy control points (skip duplicate initial point) */
09827                 for( i=1 ; i<crv->c_size ; i++ )
09828                 {
09829                         ctl_index++;
09830                         VMOVEN( &new_crv->ctl_points[ctl_index*ncoords], &crv->ctl_points[i*ncoords], ncoords );
09831                 }
09832         }
09833         new_crv->k.knots[++knot_index] = last_knot;
09834 
09835         return( new_crv );
09836 }
09837 
09838 /**     R T _ A R C 2 D _ T O _ C N U R B
09839  *
09840  * Convert a 2D arc to a NURB curve.
09841  *      point_type indicates what type of CNURB is requested.
09842  *      The arc start, end, and center must be at the same Z
09843  *      coordinate value if point_type is RT_NURB_PT_XYZ. For values of
09844  *      point_type of RT_NURB_PT_XY or RT_NURB_PT_UV, the Z coordinate
09845  *      is ignored. (Note that point_type must be one of the point types
09846  *      defined in nurb.h). The arc is constructed counter-clockwise
09847  *      (as viewed from the +Z direction).
09848  */
09849 struct edge_g_cnurb *
09850 rt_arc2d_to_cnurb(fastf_t *i_center, fastf_t *i_start, fastf_t *i_end, int point_type, const struct bn_tol *tol)
09851 {
09852         struct edge_g_cnurb *crv;
09853         struct bu_list crv_head;
09854         point_t center;
09855         point_t start;
09856         point_t end;
09857         double angle;
09858         double angles[3];
09859         double radius;
09860         double tmp_radius;
09861         vect_t v1, v2;
09862         vect_t ref1, ref2;
09863         vect_t norm;
09864         point_t start1;
09865         point_t end1;
09866         int nsegs;
09867         int pt_type;
09868         int ncoords = 0;
09869         int i;
09870 
09871         BN_CK_TOL( tol );
09872 
09873         VMOVE( start, i_start )
09874         VMOVE( center, i_center )
09875         VMOVE( end, i_end )
09876         switch( point_type )
09877         {
09878                 case RT_NURB_PT_XY:
09879                 case RT_NURB_PT_UV:
09880                         ncoords = 3;
09881                         start[Z] = 0.0;
09882                         center[Z] = 0.0;
09883                         end[Z] = 0.0;
09884                         break;
09885                 case RT_NURB_PT_XYZ:
09886                 case RT_NURB_PT_DATA:
09887                 case RT_NURB_PT_PROJ:
09888                         ncoords = 4;
09889                         break;
09890         }
09891 
09892         if( point_type == RT_NURB_PT_XYZ )
09893         {
09894                 /* check for points at same Z-coordinate value */
09895                 if( center[Z] - start[Z] > tol->dist )
09896                 {
09897                         bu_log( "rt_arc2d_to_cnurb: center and start points not at same Z value (%g vs %g)\n",
09898                                         center[Z], start[Z] );
09899                         return( (struct edge_g_cnurb *)NULL );
09900                 }
09901 
09902                 if( end[Z] - start[Z] > tol->dist )
09903                 {
09904                         bu_log( "rt_arc2d_to_cnurb: end and start points not at same Z value (%g vs %g)\n",
09905                                         end[Z], start[Z] );
09906                         return( (struct edge_g_cnurb *)NULL );
09907                 }
09908 
09909                 if( end[Z] - center[Z] > tol->dist )
09910                 {
09911                         bu_log( "rt_arc2d_to_cnurb: end and center points not at same Z value (%g vs %g)\n",
09912                                         end[Z], center[Z] );
09913                         return( (struct edge_g_cnurb *)NULL );
09914                 }
09915         }
09916 
09917         /* point type is point_type with weighting factor (rational) */
09918         pt_type = RT_NURB_MAKE_PT_TYPE( ncoords, point_type, RT_NURB_PT_RATIONAL );
09919 
09920         /* calculate radius twice */
09921         if( ncoords == 4 )
09922         {
09923                 VSUB2( v1, start, center );
09924                 radius = MAGNITUDE( v1 );
09925                 VSUB2( v2, end, center );
09926                 tmp_radius = MAGNITUDE( v2 );
09927         }
09928         else
09929         {
09930                 radius = sqrt( (start[X] - center[X])*(start[X] - center[X]) +
09931                         (start[Y] - center[Y])*(start[Y] - center[Y]) );
09932                 tmp_radius = sqrt( (end[X] - center[X])*(end[X] - center[X]) +
09933                         (end[Y] - center[Y])*(end[Y] - center[Y]) );
09934         }
09935 
09936         /* make sure radii are consistent */
09937         if( !NEAR_ZERO( radius - tmp_radius, tol->dist ) )
09938         {
09939                 bu_log( "rt_arc2d_to_cnurb: distances from center to start and center to end are different\n" );
09940                 bu_log( "                        (%g and %g)\n", radius, tmp_radius );
09941                 return( (struct edge_g_cnurb *)NULL );
09942         }
09943 
09944         /* construct coord system ref1,ref2,norm */
09945         VSET( norm, 0.0, 0.0, 1.0 );
09946         VMOVE( ref1, v1 );
09947         VSCALE( ref1, ref1, 1.0/radius );
09948         VCROSS( ref2, norm, ref1 );
09949 
09950         /* calculate angle of arc */
09951         angle = atan2( VDOT( v2, ref2 ), VDOT( v2, ref1 ) );
09952         if( angle <= 0.0 )
09953                 angle += 2.0*bn_pi;
09954 
09955         if( angle < 150.0*bn_pi/180.0 ) /* angle is reasonable to do in one segment */
09956         {
09957                 fastf_t dist1, dist2;
09958                 vect_t t1, t2;
09959                 int ret_val;
09960 
09961                 /* second control point is intersection of tangent lines */
09962                 VMOVE( t1, ref2 );
09963                 if( VDOT( t1, v2 ) > 0.0 )
09964                         VREVERSE( t1, t1 );
09965                 VCROSS( t2, v2, norm );
09966                 VUNITIZE( t2 );
09967                 if( VDOT( t2, v1 ) > 0.0 )
09968                         VREVERSE( t2, t2 );
09969                 if( (ret_val=bn_isect_line3_line3( &dist1, &dist2, start, t1, end, t2, tol )) < 1 )
09970                 {
09971                         bu_log( "rt_arc2d_to_cnurb: Cannot calculate second control point\n" );
09972                         bu_log( "                   bn_isect_line3_line3 returns %d\n" , ret_val );
09973                         return( (struct edge_g_cnurb *)NULL );
09974                 }
09975 
09976                 /* Get memory for this curve (order=3, 6 knot values, 3 control points) */
09977                 crv = rt_nurb_new_cnurb( 3, 6, 3, pt_type );
09978 
09979                 /* Set knot values */
09980                 for( i=0 ; i<3 ; i++ )
09981                         crv->k.knots[i] = 0.0;
09982                 for( i=3 ; i<6 ; i++ )
09983                         crv->k.knots[i] = 1.0;
09984 
09985                 /* First and third control points are start and end */
09986                 VMOVEN( crv->ctl_points, start, ncoords-1 )
09987                 VMOVEN( &crv->ctl_points[8], end, ncoords-1 )
09988 
09989                 /* weights are 1.0 for the endpoints */
09990                 crv->ctl_points[ncoords-1] = 1.0;
09991                 crv->ctl_points[3*ncoords-1] = 1.0;
09992 
09993                 /* second control point is as calculated above */
09994                 VJOIN1N( &crv->ctl_points[ncoords], start, dist1, t1, ncoords-1 )
09995 
09996                 /* weight for second control point is cosine of half the arc angle */
09997                 crv->ctl_points[2*ncoords-1] = cos( angle/2.0 );
09998 
09999                 /* scale middle point by its weight */
10000                 VSCALEN( &crv->ctl_points[ncoords], &crv->ctl_points[ncoords], crv->ctl_points[2*ncoords-1], ncoords-1 )
10001 
10002                 return( crv );
10003         }
10004 
10005         /* Angle is too great for one segment.
10006          * Make up to three segments and join them.
10007          */
10008 
10009         if( angle < 1.5*bn_pi )
10010         {
10011                 /* do it in two segments */
10012                 nsegs = 2;
10013                 angles[0] = angle/2.0;
10014                 angles[1] = angle;
10015         }
10016         else
10017         {
10018                 /* use three segments */
10019                 nsegs = 3;
10020                 angles[0] = angle/3.0;
10021                 angles[1] = 2.0*angles[0];
10022                 angles[2] = angle;
10023         }
10024 
10025         /* initialize bu_list structure to hold list of curves */
10026         BU_LIST_INIT( &crv_head );
10027 
10028         /* Make each arc segment */
10029         VMOVE( end1, start );
10030         for( i=0 ; i<nsegs ; i++ )
10031         {
10032                 VMOVE( start1, end1 );
10033                 if( i == nsegs-1 )
10034                         VMOVE( end1, end )
10035                 else
10036                         VJOIN2( end1, center, radius*cos( angles[i] ), ref1, radius*sin( angles[i] ), ref2 )
10037 
10038                 crv = rt_arc2d_to_cnurb( center, start1, end1, point_type, tol );
10039                 BU_LIST_INSERT( &crv_head, &crv->l );
10040         }
10041 
10042         /* join the arc segments into one edge_g_cnurb */
10043         crv = rt_join_cnurbs( &crv_head );
10044 
10045         return( crv );
10046 }
10047 
10048 int
10049 nmg_break_edge_at_verts(struct edge *e, struct bu_ptbl *verts, const struct bn_tol *tol)
10050 {
10051         int break_count=0;
10052         int j;
10053         struct vertex *va,*vb;
10054         struct bu_ptbl edges;
10055 
10056         BN_CK_TOL( tol );
10057         NMG_CK_EDGE( e );
10058         BU_CK_PTBL( verts );
10059 
10060         bu_ptbl_init( &edges, 64, " &edges");
10061         bu_ptbl_ins( &edges, (long *)e );
10062 
10063 start:
10064 
10065         while( BU_PTBL_END( &edges ) )
10066         {
10067                 struct edge *e1;
10068                 struct edgeuse *eu;
10069 
10070                 e1 = (struct edge *)BU_PTBL_GET( &edges, BU_PTBL_END( &edges )-1 );
10071                 NMG_CK_EDGE( e1 );
10072                 eu = e1->eu_p;
10073                 NMG_CK_EDGEUSE( eu );
10074 
10075                 va = eu->vu_p->v_p;
10076                 NMG_CK_VERTEX( va );
10077                 vb = eu->eumate_p->vu_p->v_p;
10078                 NMG_CK_VERTEX( vb );
10079 
10080                 for( j=0 ; j<BU_PTBL_END( verts ) ; j++ )
10081                 {
10082                         struct edgeuse *eu_new;
10083                         struct vertex *v;
10084                         fastf_t dist;
10085                         point_t pca;
10086 
10087                         v = (struct vertex *)BU_PTBL_GET( verts, j );
10088                         NMG_CK_VERTEX( v );
10089 
10090                         if( v == va || v == vb )
10091                                 continue;
10092 
10093                         if( bn_dist_pt3_lseg3( &dist, pca, va->vg_p->coord, vb->vg_p->coord, v->vg_p->coord, tol ) )
10094                                 continue;
10095 
10096                         eu_new = nmg_esplit( v, eu, 1 );
10097                         break_count++;
10098 
10099                         bu_ptbl_rm( &edges, (long *)e1 );
10100                         bu_ptbl_ins( &edges, (long *)eu->e_p );
10101                         bu_ptbl_ins( &edges, (long *)eu_new->e_p );
10102 
10103                         goto start;
10104                 }
10105                 bu_ptbl_rm( &edges, (long *)e1 );
10106         }
10107         bu_ptbl_free( &edges);
10108         return( break_count );
10109 }
10110 
10111 int
10112 nmg_break_edges(long int *magic_p, const struct bn_tol *tol)
10113 {
10114         struct bu_ptbl edges;
10115         struct bu_ptbl verts;
10116         int i;
10117         int break_count=0;
10118 
10119         BN_CK_TOL( tol );
10120 
10121         nmg_edge_tabulate( &edges, magic_p );
10122         nmg_vertex_tabulate( &verts, magic_p );
10123 
10124         for( i=0 ; i<BU_PTBL_END( &edges ); i++ )
10125         {
10126                 struct edge *e;
10127                 int edge_break_count=0;
10128 
10129                 e = (struct edge *)BU_PTBL_GET( &edges, i );
10130                 NMG_CK_EDGE( e );
10131                 edge_break_count = nmg_break_edge_at_verts( e, &verts, tol );
10132                 break_count += edge_break_count;
10133         }
10134         bu_ptbl_free( &edges);
10135         bu_ptbl_free( &verts);
10136 
10137         return( break_count );
10138 }
10139 
10140 
10141 static int
10142 Shell_is_arb(struct shell *s, struct bu_ptbl *tab)
10143 {
10144         struct faceuse *fu;
10145         struct face *f;
10146         int four_verts=0;
10147         int three_verts=0;
10148         int face_count=0;
10149         int loop_count;
10150 
10151         NMG_CK_SHELL( s );
10152 
10153         nmg_vertex_tabulate( tab, &s->l.magic );
10154 
10155         if( BU_PTBL_END( tab ) > 8 || BU_PTBL_END( tab ) < 4 )
10156                 goto not_arb;
10157 
10158         for( BU_LIST_FOR( fu, faceuse, &s->fu_hd ) )
10159         {
10160                 struct loopuse *lu;
10161                 vect_t fu_norm;
10162 
10163                 NMG_CK_FACEUSE( fu );
10164 
10165                 if( fu->orientation != OT_SAME )
10166                         continue;
10167 
10168                 f = fu->f_p;
10169                 NMG_CK_FACE( f );
10170 
10171                 if( *f->g.magic_p != NMG_FACE_G_PLANE_MAGIC )
10172                         goto not_arb;
10173 
10174                 NMG_GET_FU_NORMAL( fu_norm, fu );
10175 
10176                 loop_count = 0;
10177                 for( BU_LIST_FOR( lu, loopuse, &fu->lu_hd ) )
10178                 {
10179                         struct edgeuse *eu;
10180 
10181                         NMG_CK_LOOPUSE( lu );
10182 
10183                         if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) != NMG_EDGEUSE_MAGIC )
10184                                 goto not_arb;
10185 
10186                         loop_count++;
10187 
10188                         /* face must be a single loop */
10189                         if( loop_count > 1 )
10190                                 goto not_arb;
10191 
10192                         for( BU_LIST_FOR( eu, edgeuse, &lu->down_hd ) )
10193                         {
10194                                 struct edgeuse *eu_radial;
10195                                 struct faceuse *fu_radial;
10196                                 struct face *f_radial;
10197                                 vect_t norm_radial;
10198                                 vect_t eu_dir;
10199                                 vect_t cross;
10200                                 fastf_t dot;
10201 
10202                                 NMG_CK_EDGEUSE( eu );
10203 
10204                                 eu_radial = nmg_next_radial_eu( eu, s, 0 );
10205 
10206                                 /* Can't have any dangling faces */
10207                                 if( eu_radial == eu || eu_radial == eu->eumate_p )
10208                                         goto not_arb;
10209 
10210                                 fu_radial = nmg_find_fu_of_eu( eu_radial );
10211                                 NMG_CK_FACEUSE( fu_radial );
10212 
10213                                 if( fu_radial->orientation != OT_SAME )
10214                                         fu_radial = fu_radial->fumate_p;
10215 
10216                                 f_radial = fu_radial->f_p;
10217                                 NMG_CK_FACE( f_radial );
10218 
10219                                 /* faces must be planar */
10220                                 if( *f_radial->g.magic_p != NMG_FACE_G_PLANE_MAGIC )
10221                                         goto not_arb;
10222 
10223 
10224                                 /* Make sure shell is convex by checking that edguses
10225                                  * run in direction fu_norm X norm_radial
10226                                  */
10227                                 NMG_GET_FU_NORMAL( norm_radial, fu_radial );
10228 
10229                                 dot = VDOT( norm_radial, fu_norm );
10230 
10231                                 if( !NEAR_ZERO( dot - 1.0, 0.00001 ) )
10232                                 {
10233 
10234                                         VCROSS( cross, fu_norm, norm_radial );
10235 
10236                                         if( eu->orientation == OT_NONE )
10237                                         {
10238                                                 VSUB2( eu_dir, eu->vu_p->v_p->vg_p->coord, eu->eumate_p->vu_p->v_p->vg_p->coord )
10239                                                 if( eu->orientation != OT_SAME )
10240                                                         VREVERSE( eu_dir, eu_dir )
10241                                         }
10242                                         else
10243                                                 VMOVE( eu_dir, eu->g.lseg_p->e_dir )
10244 
10245                                         if( eu->orientation == OT_SAME || eu->orientation == OT_NONE )
10246                                         {
10247                                                 if( VDOT( cross, eu_dir ) < 0.0 )
10248                                                         goto not_arb;
10249                                         }
10250                                         else
10251                                         {
10252                                                 if( VDOT( cross, eu_dir ) > 0.0 )
10253                                                         goto not_arb;
10254                                         }
10255                                 }
10256                         }
10257                 }
10258         }
10259 
10260         /* count face types */
10261         for( BU_LIST_FOR( fu, faceuse, &s->fu_hd ) )
10262         {
10263                 struct loopuse *lu;
10264                 int vert_count=0;
10265 
10266                 if( fu->orientation != OT_SAME )
10267                         continue;
10268 
10269                 face_count++;
10270                 for( BU_LIST_FOR( lu, loopuse, &fu->lu_hd ) )
10271                 {
10272                         struct edgeuse *eu;
10273 
10274                         for( BU_LIST_FOR( eu, edgeuse, &lu->down_hd ) )
10275                                 vert_count++;
10276                 }
10277 
10278                 if( vert_count == 3 )
10279                         three_verts++;
10280                 else if( vert_count == 4 )
10281                         four_verts++;
10282         }
10283 
10284         /* must have only three and four vertices in each face */
10285         if( face_count != three_verts + four_verts )
10286                 goto not_arb;
10287 
10288         /* which type of arb is this?? */
10289         switch( BU_PTBL_END( tab ) )
10290         {
10291                 case 4:         /* each face must have 3 vertices */
10292                         if( three_verts != 4 || four_verts != 0 )
10293                                 goto not_arb;
10294                         break;
10295                 case 5:         /* one face with 4 verts, four with 3 verts */
10296                         if( four_verts != 1 || three_verts != 4 )
10297                                 goto not_arb;
10298                         break;
10299                 case 6:         /* three faces with 4 verts, two with 3 verts */
10300                         if( three_verts != 2 || four_verts != 3 )
10301                                 goto not_arb;
10302                         break;
10303                 case 7:         /* four faces with 4 verts, two with 3 verts */
10304                         if( four_verts != 4 || three_verts != 2 )
10305                                 goto not_arb;
10306                         break;
10307                 case 8:         /* each face must have 4 vertices */
10308                         if( four_verts != 6 || three_verts != 0 )
10309                                 goto not_arb;
10310                         break;
10311         }
10312 
10313         return( BU_PTBL_END( tab ) );
10314 
10315 
10316 not_arb:
10317         bu_ptbl_free( tab);
10318         return( 0 );
10319 }
10320 
10321 /**             N M G _ T O _ A R B
10322  *
10323  *      Converts an NMG to an ARB, if possible.
10324  *
10325  *      NMG must have been coplanar face merged and simplified
10326  *
10327  *      Returns:
10328  *              1 - Equivalent ARB was constructed
10329  *              0 - Cannot construct an equivalent ARB
10330  *
10331  *      The newly constructed arb is in "arb_int"
10332  */
10333 int
10334 nmg_to_arb( const struct model *m, struct rt_arb_internal *arb_int )
10335 {
10336         struct nmgregion *r;
10337         struct shell *s;
10338         struct faceuse *fu;
10339         struct loopuse *lu;
10340         struct edgeuse *eu;
10341         struct vertex *v;
10342         struct edgeuse *eu_start;
10343         struct faceuse *fu1;
10344         struct bu_ptbl tab;
10345         int face_verts;
10346         int i,j;
10347         int found;
10348         int ret_val = 0;
10349 
10350         NMG_CK_MODEL( m );
10351 
10352         r = BU_LIST_FIRST( nmgregion, &m->r_hd );
10353 
10354         /* must be a single region */
10355         if( BU_LIST_NEXT_NOT_HEAD( &r->l, &m->r_hd ) )
10356                 return( 0 );
10357 
10358         s = BU_LIST_FIRST( shell, &r->s_hd );
10359         NMG_CK_SHELL( s );
10360 
10361         /* must be a single shell */
10362         if( BU_LIST_NEXT_NOT_HEAD( &s->l, &r->s_hd ) )
10363                 return( 0 );
10364 
10365         switch( Shell_is_arb( s, &tab ) )
10366         {
10367                 case 0:
10368                         ret_val = 0;
10369                         break;
10370                 case 4:
10371                         v = (struct vertex *)BU_PTBL_GET( &tab, 0 );
10372                         NMG_CK_VERTEX( v );
10373                         VMOVE( arb_int->pt[0], v->vg_p->coord );
10374                         v = (struct vertex *)BU_PTBL_GET( &tab, 1 );
10375                         NMG_CK_VERTEX( v );
10376                         VMOVE( arb_int->pt[1], v->vg_p->coord );
10377                         v = (struct vertex *)BU_PTBL_GET( &tab, 2 );
10378                         NMG_CK_VERTEX( v );
10379                         VMOVE( arb_int->pt[2], v->vg_p->coord );
10380                         VMOVE( arb_int->pt[3], v->vg_p->coord );
10381                         v = (struct vertex *)BU_PTBL_GET( &tab, 3 );
10382                         NMG_CK_VERTEX( v );
10383                         VMOVE( arb_int->pt[4], v->vg_p->coord );
10384                         VMOVE( arb_int->pt[5], v->vg_p->coord );
10385                         VMOVE( arb_int->pt[6], v->vg_p->coord );
10386                         VMOVE( arb_int->pt[7], v->vg_p->coord );
10387 
10388                         bu_ptbl_free( &tab);
10389                         ret_val = 1;
10390                         break;
10391                 case 5:
10392                         fu = BU_LIST_FIRST( faceuse, &s->fu_hd );
10393                         face_verts = 0;
10394                         while( face_verts != 4 )
10395                         {
10396                                 face_verts = 0;
10397                                 fu = BU_LIST_PNEXT_CIRC( faceuse, &fu->l );
10398                                 lu = BU_LIST_FIRST( loopuse, &fu->lu_hd );
10399                                 for( BU_LIST_FOR( eu, edgeuse, &lu->down_hd ) )
10400                                         face_verts++;
10401                         }
10402                         NMG_CK_FACEUSE( fu );
10403                         if( fu->orientation != OT_SAME )
10404                                 fu = fu->fumate_p;
10405                         NMG_CK_FACEUSE( fu );
10406 
10407                         lu = BU_LIST_FIRST( loopuse, &fu->lu_hd );
10408                         j = 0;
10409                         eu_start = BU_LIST_FIRST( edgeuse, &lu->down_hd );
10410                         for( BU_LIST_FOR( eu, edgeuse, &lu->down_hd ) )
10411                         {
10412                                 VMOVE( arb_int->pt[j], eu->vu_p->v_p->vg_p->coord )
10413                                 j++;
10414                         }
10415 
10416                         eu = eu_start->radial_p;
10417                         eu = BU_LIST_PNEXT_CIRC( edgeuse, &eu->l );
10418                         eu = eu->eumate_p;
10419                         for( i=0 ; i<4 ; i++ )
10420                         {
10421                                 VMOVE( arb_int->pt[j], eu->vu_p->v_p->vg_p->coord )
10422                                 j++;
10423                         }
10424 
10425                         bu_ptbl_free( &tab);
10426                         ret_val = 1;
10427                         break;
10428                 case 6:
10429                         fu = BU_LIST_FIRST( faceuse, &s->fu_hd );
10430                         face_verts = 0;
10431                         while( face_verts != 3 )
10432                         {
10433                                 face_verts = 0;
10434                                 fu = BU_LIST_PNEXT_CIRC( faceuse, &fu->l );
10435                                 lu = BU_LIST_FIRST( loopuse, &fu->lu_hd );
10436                                 for( BU_LIST_FOR( eu, edgeuse, &lu->down_hd ) )
10437                                         face_verts++;
10438                         }
10439                         NMG_CK_FACEUSE( fu );
10440                         if( fu->orientation != OT_SAME )
10441                                 fu = fu->fumate_p;
10442                         NMG_CK_FACEUSE( fu );
10443 
10444                         lu = BU_LIST_FIRST( loopuse, &fu->lu_hd );
10445 
10446                         eu_start = BU_LIST_FIRST( edgeuse, &lu->down_hd );
10447                         eu = eu_start;
10448                         VMOVE( arb_int->pt[1], eu->vu_p->v_p->vg_p->coord );
10449                         eu = BU_LIST_PNEXT_CIRC( edgeuse, &eu->l );
10450                         VMOVE( arb_int->pt[0], eu->vu_p->v_p->vg_p->coord );
10451                         eu = BU_LIST_PNEXT_CIRC( edgeuse, &eu->l );
10452                         VMOVE( arb_int->pt[4], eu->vu_p->v_p->vg_p->coord );
10453                         VMOVE( arb_int->pt[5], eu->vu_p->v_p->vg_p->coord );
10454 
10455                         eu = eu_start->radial_p;
10456                         eu = BU_LIST_PNEXT_CIRC( edgeuse, &eu->l );
10457                         eu = BU_LIST_PNEXT_CIRC( edgeuse, &eu->l );
10458                         eu = eu->radial_p->eumate_p;
10459                         VMOVE( arb_int->pt[2], eu->vu_p->v_p->vg_p->coord );
10460                         eu = BU_LIST_PNEXT_CIRC( edgeuse, &eu->l );
10461                         VMOVE( arb_int->pt[3], eu->vu_p->v_p->vg_p->coord );
10462                         eu = BU_LIST_PNEXT_CIRC( edgeuse, &eu->l );
10463                         VMOVE( arb_int->pt[6], eu->vu_p->v_p->vg_p->coord );
10464                         VMOVE( arb_int->pt[7], eu->vu_p->v_p->vg_p->coord );
10465 
10466                         bu_ptbl_free( &tab);
10467                         ret_val = 1;
10468                         break;
10469                 case 7:
10470                         found = 0;
10471                         fu = BU_LIST_FIRST( faceuse, &s->fu_hd );
10472                         while( !found )
10473                         {
10474                                 int verts4=0,verts3=0;
10475 
10476                                 lu = BU_LIST_FIRST( loopuse, &fu->lu_hd );
10477                                 for( BU_LIST_FOR( eu, edgeuse, &lu->down_hd ) )
10478                                 {
10479                                         struct loopuse *lu1;
10480                                         struct edgeuse *eu1;
10481                                         int vert_count=0;
10482 
10483                                         fu1 = nmg_find_fu_of_eu( eu->radial_p );
10484                                         lu1 = BU_LIST_FIRST( loopuse, &fu1->lu_hd );
10485                                         for( BU_LIST_FOR( eu1, edgeuse, &lu1->down_hd ) )
10486                                                 vert_count++;
10487 
10488                                         if( vert_count == 4 )
10489                                                 verts4++;
10490                                         else if( vert_count == 3 )
10491                                                 verts3++;
10492                                 }
10493 
10494                                 if( verts4 == 2 && verts3 == 2 )
10495                                         found = 1;
10496                         }
10497                         if( fu->orientation != OT_SAME )
10498                                 fu = fu->fumate_p;
10499                         NMG_CK_FACEUSE( fu );
10500 
10501                         lu = BU_LIST_FIRST( loopuse, &fu->lu_hd );
10502                         j = 0;
10503                         eu_start = BU_LIST_FIRST( edgeuse, &lu->down_hd );
10504                         for( BU_LIST_FOR( eu, edgeuse, &lu->down_hd ) )
10505                         {
10506                                 VMOVE( arb_int->pt[j], eu->vu_p->v_p->vg_p->coord )
10507                                 j++;
10508                         }
10509 
10510                         eu = eu_start->radial_p;
10511                         eu = BU_LIST_PNEXT_CIRC( edgeuse, &eu->l );
10512                         eu = BU_LIST_PNEXT_CIRC( edgeuse, &eu->l );
10513                         eu = eu->radial_p->eumate_p;
10514                         fu1 = nmg_find_fu_of_eu( eu );
10515                         if( nmg_faces_are_radial( fu, fu1 ) )
10516                         {
10517                                 eu = eu_start->radial_p;
10518                                 eu = BU_LIST_PNEXT_CIRC( edgeuse, &eu->l );
10519                                 eu = eu->radial_p->eumate_p;
10520                         }
10521                         for( i=0 ; i<4 ; i++ )
10522                         {
10523                                 VMOVE( arb_int->pt[j], eu->vu_p->v_p->vg_p->coord )
10524                                 j++;
10525                                 eu = BU_LIST_PNEXT_CIRC( edgeuse, &eu->l );
10526                         }
10527 
10528                         bu_ptbl_free( &tab);
10529                         ret_val = 1;
10530                         break;
10531                 case 8:
10532                         fu = BU_LIST_FIRST( faceuse, &s->fu_hd );
10533                         NMG_CK_FACEUSE( fu );
10534                         if( fu->orientation != OT_SAME )
10535                                 fu = fu->fumate_p;
10536                         NMG_CK_FACEUSE( fu );
10537 
10538                         lu = BU_LIST_FIRST( loopuse, &fu->lu_hd );
10539                         j = 0;
10540                         eu_start = BU_LIST_FIRST( edgeuse, &lu->down_hd );
10541                         for( BU_LIST_FOR( eu, edgeuse, &lu->down_hd ) )
10542                         {
10543                                 VMOVE( arb_int->pt[j], eu->vu_p->v_p->vg_p->coord )
10544                                 j++;
10545                         }
10546 
10547                         eu = eu_start->radial_p;
10548                         eu = BU_LIST_PNEXT_CIRC( edgeuse, &eu->l );
10549                         eu = BU_LIST_PNEXT_CIRC( edgeuse, &eu->l );
10550                         eu = eu->radial_p->eumate_p;
10551                         for( i=0 ; i<4 ; i++ )
10552                         {
10553                                 VMOVE( arb_int->pt[j], eu->vu_p->v_p->vg_p->coord )
10554                                 j++;
10555                                 eu = BU_LIST_PNEXT_CIRC( edgeuse, &eu->l );
10556                         }
10557 
10558                         bu_ptbl_free( &tab);
10559                         ret_val = 1;
10560                         break;
10561                 default:
10562                         rt_bomb( "Shell_is_arb screwed up" );
10563                         break;
10564         }
10565         if( ret_val )
10566                 arb_int->magic = RT_ARB_INTERNAL_MAGIC;
10567         return( ret_val );
10568 }
10569 
10570 /**             N M G _ T O _ T G C
10571  *
10572  *      Converts an NMG to a TGC, if possible.
10573  *
10574  *      NMG must have been coplanar face merged and simplified
10575  *
10576  *      Returns:
10577  *              1 - Equivalent TGC was constructed
10578  *              0 - Cannot construct an equivalent TGC
10579  *
10580  *      The newly constructed tgc is in "tgc_int"
10581  *
10582  *      Currently only supports RCC, and creates circumscribed RCC
10583  */
10584 int
10585 nmg_to_tgc(
10586         const struct model *m,
10587         struct rt_tgc_internal *tgc_int,
10588         const struct bn_tol *tol )
10589 {
10590         struct nmgregion *r;
10591         struct shell *s;
10592         struct faceuse *fu;
10593         struct loopuse *lu;
10594         struct edgeuse *eu;
10595         struct faceuse *fu_base=(struct faceuse *)NULL;
10596         struct faceuse *fu_top=(struct faceuse *)NULL;
10597         int three_vert_faces=0;
10598         int four_vert_faces=0;
10599         int many_vert_faces=0;
10600         int base_vert_count=0;
10601         int top_vert_count=0;
10602         point_t sum;
10603         fastf_t vert_count=0.0;
10604         fastf_t one_over_vert_count;
10605         point_t base_center;
10606         fastf_t min_base_r_sq;
10607         fastf_t max_base_r_sq;
10608         fastf_t sum_base_r_sq;
10609         fastf_t ave_base_r_sq;
10610         fastf_t base_r;
10611         point_t top_center;
10612         fastf_t min_top_r_sq;
10613         fastf_t max_top_r_sq;
10614         fastf_t sum_top_r_sq;
10615         fastf_t ave_top_r_sq;
10616         fastf_t top_r;
10617         plane_t top_pl;
10618         plane_t base_pl;
10619         vect_t plv_1,plv_2;
10620 
10621         NMG_CK_MODEL( m );
10622 
10623         BN_CK_TOL( tol );
10624 
10625         r = BU_LIST_FIRST( nmgregion, &m->r_hd );
10626 
10627         /* must be a single region */
10628         if( BU_LIST_NEXT_NOT_HEAD( &r->l, &m->r_hd ) )
10629                 return( 0 );
10630 
10631         s = BU_LIST_FIRST( shell, &r->s_hd );
10632         NMG_CK_SHELL( s );
10633 
10634         /* must be a single shell */
10635         if( BU_LIST_NEXT_NOT_HEAD( &s->l, &r->s_hd ) )
10636                 return( 0 );
10637 
10638         for( BU_LIST_FOR( fu, faceuse, &s->fu_hd ) )
10639         {
10640                 int lu_count=0;
10641 
10642                 NMG_CK_FACEUSE( fu );
10643                 if( fu->orientation != OT_SAME )
10644                         continue;
10645 
10646                 vert_count = 0.0;
10647 
10648                 for( BU_LIST_FOR( lu, loopuse, &fu->lu_hd ) )
10649                 {
10650 
10651                         NMG_CK_LOOPUSE( lu );
10652 
10653                         if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) != NMG_EDGEUSE_MAGIC )
10654                                 return( 0 );
10655 
10656                         lu_count++;
10657                         if( lu_count > 1 )
10658                                 return( 0 );
10659 
10660                         for( BU_LIST_FOR( eu, edgeuse, &lu->down_hd ) )
10661                                 vert_count++;
10662                 }
10663 
10664                 if( vert_count < 3 )
10665                         return( 0 );
10666 
10667                 if( vert_count == 4 )
10668                         four_vert_faces++;
10669                 else if( vert_count ==3 )
10670                         three_vert_faces++;
10671                 else
10672                 {
10673                         many_vert_faces++;
10674                         if( many_vert_faces > 2 )
10675                                 return( 0 );
10676 
10677                         if( many_vert_faces == 1 )
10678                         {
10679                                 fu_base = fu;
10680                                 base_vert_count = vert_count;
10681                                 NMG_GET_FU_PLANE( base_pl, fu_base );
10682                         }
10683                         else if( many_vert_faces == 2 )
10684                         {
10685                                 fu_top = fu;
10686                                 top_vert_count = vert_count;
10687                                 NMG_GET_FU_PLANE( top_pl, fu_top );
10688                         }
10689                 }
10690         }
10691         /* if there are any three vertex faces,
10692          * there must be an even number of them
10693          */
10694         if( three_vert_faces%2 )
10695                 return( 0 );
10696 
10697         /* base and top must have same number of vertices */
10698         if( base_vert_count != top_vert_count )
10699                 return( 0 );
10700 
10701         /* Must have correct number of side faces */
10702         if( (base_vert_count != four_vert_faces) &&
10703             (base_vert_count*2 != three_vert_faces ) )
10704                 return( 0 );
10705 
10706         if( !NEAR_ZERO( 1.0 + VDOT( top_pl, base_pl ), tol->perp ) )
10707                 return( 0 );
10708 
10709         /* This looks like a good candidate,
10710          * Calculate center of base and top faces
10711          */
10712 
10713         vert_count = 0.0;
10714         VSETALL( sum, 0.0 );
10715         lu = BU_LIST_FIRST( loopuse, &fu_base->lu_hd );
10716         for( BU_LIST_FOR( eu, edgeuse, &lu->down_hd ) )
10717         {
10718                 struct vertex_g *vg;
10719 
10720                 NMG_CK_EDGEUSE( eu );
10721 
10722                 NMG_CK_VERTEXUSE( eu->vu_p );
10723                 NMG_CK_VERTEX( eu->vu_p->v_p );
10724                 vg = eu->vu_p->v_p->vg_p;
10725                 NMG_CK_VERTEX_G( vg );
10726 
10727                 VADD2( sum, sum, vg->coord );
10728                 vert_count++;
10729         }
10730 
10731         one_over_vert_count = 1.0/vert_count;
10732         VSCALE( base_center, sum, one_over_vert_count );
10733 
10734         /* Calculate Average Radius */
10735         min_base_r_sq = MAX_FASTF;
10736         max_base_r_sq = (-min_base_r_sq);
10737         sum_base_r_sq = 0.0;
10738         lu = BU_LIST_FIRST( loopuse, &fu_base->lu_hd );
10739         for( BU_LIST_FOR( eu, edgeuse, &lu->down_hd ) )
10740         {
10741                 struct vertex_g *vg;
10742                 vect_t rad_vect;
10743                 fastf_t r_sq;
10744 
10745                 vg = eu->vu_p->v_p->vg_p;
10746 
10747                 VSUB2( rad_vect, vg->coord, base_center );
10748                 r_sq = MAGSQ( rad_vect );
10749                 if( r_sq > max_base_r_sq )
10750                         max_base_r_sq = r_sq;
10751                 if( r_sq < min_base_r_sq )
10752                         min_base_r_sq = r_sq;
10753 
10754                 sum_base_r_sq += r_sq;
10755         }
10756 
10757         ave_base_r_sq = sum_base_r_sq/vert_count;
10758 
10759         base_r = sqrt( max_base_r_sq );
10760 
10761         if( !NEAR_ZERO( (max_base_r_sq - ave_base_r_sq)/ave_base_r_sq, 0.001 ) ||
10762             !NEAR_ZERO( (min_base_r_sq - ave_base_r_sq)/ave_base_r_sq, 0.001 ) )
10763                         return( 0 );
10764 
10765         VSETALL( sum, 0.0 );
10766         lu = BU_LIST_FIRST( loopuse, &fu_top->lu_hd );
10767         for( BU_LIST_FOR( eu, edgeuse, &lu->down_hd ) )
10768         {
10769                 struct vertex_g *vg;
10770 
10771                 NMG_CK_EDGEUSE( eu );
10772 
10773                 NMG_CK_VERTEXUSE( eu->vu_p );
10774                 NMG_CK_VERTEX( eu->vu_p->v_p );
10775                 vg = eu->vu_p->v_p->vg_p;
10776                 NMG_CK_VERTEX_G( vg );
10777 
10778                 VADD2( sum, sum, vg->coord );
10779         }
10780 
10781         VSCALE( top_center, sum, one_over_vert_count );
10782 
10783         /* Calculate Average Radius */
10784         min_top_r_sq = MAX_FASTF;
10785         max_top_r_sq = (-min_top_r_sq);
10786         sum_top_r_sq = 0.0;
10787         lu = BU_LIST_FIRST( loopuse, &fu_top->lu_hd );
10788         for( BU_LIST_FOR( eu, edgeuse, &lu->down_hd ) )
10789         {
10790                 struct vertex_g *vg;
10791                 vect_t rad_vect;
10792                 fastf_t r_sq;
10793 
10794                 vg = eu->vu_p->v_p->vg_p;
10795 
10796                 VSUB2( rad_vect, vg->coord, top_center );
10797                 r_sq = MAGSQ( rad_vect );
10798                 if( r_sq > max_top_r_sq )
10799                         max_top_r_sq = r_sq;
10800                 if( r_sq < min_top_r_sq )
10801                         min_top_r_sq = r_sq;
10802 
10803                 sum_top_r_sq += r_sq;
10804         }
10805 
10806         ave_top_r_sq = sum_top_r_sq/vert_count;
10807         top_r = sqrt( max_top_r_sq );
10808 
10809         if( !NEAR_ZERO( (max_top_r_sq - ave_top_r_sq)/ave_top_r_sq, 0.001 ) ||
10810             !NEAR_ZERO( (min_top_r_sq - ave_top_r_sq)/ave_top_r_sq, 0.001 ) )
10811                         return( 0 );
10812 
10813 
10814         VMOVE( tgc_int->v, base_center );
10815         VSUB2( tgc_int->h, top_center, base_center );
10816 
10817         bn_vec_perp( plv_1, top_pl );
10818         VCROSS( plv_2, top_pl, plv_1 );
10819         VUNITIZE( plv_1 );
10820         VUNITIZE( plv_2 );
10821         VSCALE( tgc_int->a, plv_1, base_r );
10822         VSCALE( tgc_int->b, plv_2, base_r );
10823         VSCALE( tgc_int->c, plv_1, top_r );
10824         VSCALE( tgc_int->d, plv_2, top_r );
10825 
10826         tgc_int->magic = RT_TGC_INTERNAL_MAGIC;
10827 
10828         return( 1 );
10829 }
10830 
10831 
10832 /**             N M G _ L U _ I S _ C O N V E X
10833  *
10834  *      Checks if lu is convex
10835  *
10836  * Returns:
10837  *      1 - is loop is convex, or lu is a loop of a single vertex
10838  *      0 - otherwise
10839  */
10840 int
10841 nmg_lu_is_convex(struct loopuse *lu, const struct bn_tol *tol)
10842 {
10843         struct edgeuse *eu1,*eu2,*eu3,*eu_start;
10844 
10845         NMG_CK_LOOPUSE( lu );
10846         BN_CK_TOL( tol );
10847 
10848         if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) != NMG_EDGEUSE_MAGIC )
10849                 return( 1 );
10850 
10851         if( !lu->l_p->lg_p )
10852                 nmg_loop_g( lu->l_p, tol );
10853 
10854         eu1 = BU_LIST_FIRST( edgeuse, &lu->down_hd );
10855         eu_start = eu1;
10856         eu2 = BU_LIST_PNEXT_CIRC( edgeuse, &eu1->l );
10857         eu3 = BU_LIST_PNEXT_CIRC( edgeuse, &eu2->l );
10858 
10859         if( eu1->vu_p->v_p == eu3->eumate_p->vu_p->v_p )
10860                 return( 1 );            /* triangle */
10861 
10862         while( eu3 != eu_start )
10863         {
10864                 int class;
10865                 struct vertex_g *vg1,*vg3;
10866                 point_t mid_pt;
10867 
10868                 vg1 = eu1->vu_p->v_p->vg_p;
10869                 NMG_CK_VERTEX_G( vg1 );
10870 
10871                 vg3 = eu3->vu_p->v_p->vg_p;
10872                 NMG_CK_VERTEX_G( vg3 );
10873 
10874                 VBLEND2( mid_pt, 0.5, vg1->coord, 0.5, vg3->coord );
10875 
10876                 class = nmg_class_pt_lu_except( mid_pt, lu, NULL, tol );
10877 
10878                 if( (class == NMG_CLASS_AoutB && lu->orientation == OT_SAME) ||
10879                     (class == NMG_CLASS_AinB  && lu->orientation == OT_OPPOSITE) )
10880                                 return( 0 );
10881                 else
10882                 {
10883                         eu1 = BU_LIST_PNEXT_CIRC( edgeuse, &eu1->l );
10884                         eu2 = BU_LIST_PNEXT_CIRC( edgeuse, &eu1->l );
10885                         eu3 = BU_LIST_PNEXT_CIRC( edgeuse, &eu2->l );
10886                 }
10887         }
10888 
10889         return( 1 );
10890 }
10891 
10892 /**
10893  *                      N M G _ T O _ P O L Y
10894  *
10895  * XXX This routine is deprecated in favor of BoTs
10896  */
10897 int
10898 nmg_to_poly(const struct model *m, struct rt_pg_internal *poly_int, const struct bn_tol *tol)
10899 {
10900         struct nmgregion *r;
10901         struct shell  *s;
10902         struct faceuse *fu;
10903         struct loopuse *lu;
10904         struct edgeuse *eu;
10905         struct model *dup_m;
10906         struct nmgregion *dup_r;
10907         struct shell *dup_s;
10908         int max_count;
10909         int count_npts;
10910         int face_count=0;
10911 #if 0
10912         struct rt_pg_face_internal *_poly;
10913 #endif
10914 
10915         NMG_CK_MODEL( m );
10916 
10917         BN_CK_TOL( tol );
10918 
10919         for( BU_LIST_FOR( r, nmgregion, &m->r_hd ) )
10920         {
10921                 for( BU_LIST_FOR( s, shell, &r->s_hd ) )
10922                 {
10923                         if( nmg_check_closed_shell( s, tol ) )
10924                                 return( 0 );
10925                 }
10926         }
10927 
10928         dup_m = nmg_mm();
10929         dup_r = nmg_mrsv( dup_m );
10930         dup_s = BU_LIST_FIRST( shell, &dup_r->s_hd );
10931 
10932         for( BU_LIST_FOR( r, nmgregion, &m->r_hd ) )
10933         {
10934                 for( BU_LIST_FOR( s, shell, &r->s_hd ) )
10935                 {
10936                         for( BU_LIST_FOR( fu, faceuse, &s->fu_hd ) )
10937                         {
10938                                 if( fu->orientation != OT_SAME )
10939                                         continue;
10940                                 (void)nmg_dup_face( fu, dup_s );
10941                         }
10942                 }
10943         }
10944 
10945         for( BU_LIST_FOR( dup_r, nmgregion, &dup_m->r_hd ) )
10946         {
10947                 for( BU_LIST_FOR( dup_s, shell, &dup_r->s_hd ) )
10948                 {
10949                         for( BU_LIST_FOR( fu , faceuse , &dup_s->fu_hd ) )
10950                         {
10951                                 NMG_CK_FACEUSE( fu );
10952 
10953                                 /* only do OT_SAME faces */
10954                                 if( fu->orientation != OT_SAME )
10955                                         continue;
10956 
10957                                 /* count vertices in loops */
10958                                 max_count = 0;
10959                                 for( BU_LIST_FOR( lu , loopuse , &fu->lu_hd ) )
10960                                 {
10961                                         NMG_CK_LOOPUSE( lu );
10962                                         if( BU_LIST_FIRST_MAGIC(&lu->down_hd) != NMG_EDGEUSE_MAGIC )
10963                                                 continue;
10964 
10965                                         if( lu->orientation != OT_SAME )
10966                                         {
10967                                                 /* triangulate holes */
10968                                                 max_count = 6;
10969                                                 break;
10970                                         }
10971 
10972                                         count_npts = 0;
10973                                         for( BU_LIST_FOR( eu , edgeuse , &lu->down_hd ) )
10974                                                 count_npts++;
10975 
10976                                         if( count_npts > 5 )
10977                                         {
10978                                                 max_count = count_npts;
10979                                                 break;
10980                                         }
10981                                         if( !nmg_lu_is_convex( lu, tol ) )
10982                                         {
10983                                                 /* triangulate non-convex faces */
10984                                                 max_count = 6;
10985                                                 break;
10986                                         }
10987                                 }
10988 
10989                                 /* if any loop has more than 5 vertices, triangulate the face */
10990                                 if( max_count > 5 )
10991                                 {
10992                                         if( rt_g.NMG_debug & DEBUG_BASIC )
10993                                                 bu_log( "write_shell_as_polysolid: triangulating fu x%x\n", fu );
10994                                         nmg_triangulate_fu( fu, tol );
10995                                 }
10996 
10997                                 for( BU_LIST_FOR( lu , loopuse , &fu->lu_hd ) )
10998                                 {
10999                                         NMG_CK_LOOPUSE( lu );
11000                                         if( BU_LIST_FIRST_MAGIC(&lu->down_hd) != NMG_EDGEUSE_MAGIC )
11001                                                 continue;
11002 
11003                                         face_count++;
11004                                 }
11005                         }
11006                 }
11007         }
11008         poly_int->npoly = face_count;
11009         poly_int->poly = (struct rt_pg_face_internal *)bu_calloc( face_count,
11010                 sizeof( struct rt_pg_face_internal ), "nmg_to_poly: poly" );
11011 
11012         face_count = 0;
11013         for( BU_LIST_FOR( dup_r, nmgregion, &dup_m->r_hd ) )
11014         {
11015                 for( BU_LIST_FOR( dup_s, shell, &dup_r->s_hd ) )
11016                 {
11017                         for( BU_LIST_FOR( fu , faceuse , &dup_s->fu_hd ) )
11018                         {
11019                                 vect_t norm;
11020 
11021                                 NMG_CK_FACEUSE( fu );
11022 
11023                                 /* only do OT_SAME faces */
11024                                 if( fu->orientation != OT_SAME )
11025                                         continue;
11026 
11027                                 NMG_GET_FU_NORMAL( norm, fu );
11028 
11029                                 for( BU_LIST_FOR( lu, loopuse, &fu->lu_hd ) )
11030                                 {
11031                                         int pt_no=0;
11032 
11033                                         if( BU_LIST_FIRST_MAGIC(&lu->down_hd) != NMG_EDGEUSE_MAGIC )
11034                                                 continue;
11035 
11036                                         /* count vertices in loop */
11037                                         count_npts = 0;
11038                                         for( BU_LIST_FOR( eu , edgeuse , &lu->down_hd ) )
11039                                                 count_npts++;
11040 
11041                                         poly_int->poly[face_count].npts = count_npts;
11042                                         poly_int->poly[face_count].verts = (fastf_t *) bu_calloc( 3*count_npts, sizeof( fastf_t ), "nmg_to_poly: verts" );
11043                                         poly_int->poly[face_count].norms = (fastf_t *) bu_calloc( 3*count_npts, sizeof( fastf_t ), "nmg_to_poly: norms" );
11044 
11045                                         for( BU_LIST_FOR( eu , edgeuse , &lu->down_hd ) )
11046                                         {
11047                                                 struct vertex_g *vg;
11048 
11049                                                 vg = eu->vu_p->v_p->vg_p;
11050                                                 NMG_CK_VERTEX_G( vg );
11051 
11052                                                 VMOVE( &(poly_int->poly[face_count].verts[pt_no*3]), vg->coord );
11053                                                 VMOVE( &(poly_int->poly[face_count].norms[pt_no*3]), norm );
11054 
11055                                                 pt_no++;
11056                                         }
11057                                         face_count++;
11058                                 }
11059                         }
11060                 }
11061         }
11062 
11063         poly_int->magic = RT_PG_INTERNAL_MAGIC;
11064         nmg_km( dup_m );
11065 
11066         return( 1 );
11067 }
11068 
11069 int
11070 nmg_simplify_shell_edges(struct shell *s, const struct bn_tol *tol)
11071 {
11072         struct faceuse *fu;
11073         int count=0;
11074 
11075         NMG_CK_SHELL( s );
11076         BN_CK_TOL( tol );
11077 
11078         fu = BU_LIST_FIRST( faceuse, &s->fu_hd );
11079         while( BU_LIST_NOT_HEAD( &fu->l, &s->fu_hd ) )
11080         {
11081                 struct faceuse *fu_next;
11082                 struct loopuse *lu;
11083                 int empty_fu=0;
11084 
11085                 NMG_CK_FACEUSE( fu );
11086 
11087                 fu_next = BU_LIST_PNEXT( faceuse, &fu->l );
11088 
11089                 if( fu->orientation != OT_SAME )
11090                 {
11091                         fu = fu_next;
11092                         continue;
11093                 }
11094 
11095                 lu = BU_LIST_FIRST( loopuse, &fu->lu_hd );
11096 
11097                 while( BU_LIST_NOT_HEAD( &lu->l, &fu->lu_hd ) )
11098                 {
11099                         struct loopuse *lu_next;
11100                         struct edge *ep1,*ep2;
11101                         struct edgeuse *eu;
11102                         struct edgeuse *eu_next;
11103                         vect_t dir_eu;
11104                         vect_t dir_next;
11105 
11106                         NMG_CK_LOOPUSE( lu );
11107 
11108                         lu_next = BU_LIST_PNEXT( loopuse, &lu->l );
11109 
11110                         if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) != NMG_EDGEUSE_MAGIC )
11111                         {
11112                                 lu = lu_next;
11113                                 continue;
11114                         }
11115 
11116                         eu = BU_LIST_FIRST( edgeuse, &lu->down_hd );
11117                         while( BU_LIST_NOT_HEAD( &eu->l, &lu->down_hd ) )
11118                         {
11119                                 struct vertex *v1,*v2,*v3;
11120                                 struct vertex_g *vg1,*vg2,*vg3;
11121                                 struct vertexuse *vu;
11122                                 struct vertexuse *next_vu;
11123                                 struct edgeuse *eu_tmp;
11124                                 point_t pca;
11125                                 fastf_t dist;
11126                                 int skip;
11127                                 int empty_lu=0;
11128 
11129                                 v1 = eu->vu_p->v_p;
11130                                 ep1 = eu->e_p;
11131                                 eu_next = BU_LIST_PNEXT_CIRC( edgeuse, &eu->l );
11132                                 ep2 = eu_next->e_p;
11133                                 v2 = eu_next->vu_p->v_p;
11134 
11135                                 if( v1 == v2 )
11136                                 {
11137                                         /* this is a crack */
11138                                         eu = BU_LIST_PNEXT( edgeuse, &eu->l );
11139                                         continue;
11140                                 }
11141 
11142                                 /* make sure there are no uses of v2 outside this shell,
11143                                  * and that all uses are for either ep1 or ep2 */
11144                                 skip = 0;
11145                                 for( BU_LIST_FOR( vu, vertexuse, &v2->vu_hd ) )
11146                                 {
11147                                         if( nmg_find_s_of_vu( vu ) != s )
11148                                         {
11149                                                 skip = 1;
11150                                                 break;
11151                                         }
11152                                         if( *vu->up.magic_p != NMG_EDGEUSE_MAGIC )
11153                                                 continue;
11154 
11155                                         if( vu->up.eu_p->e_p != ep1 &&
11156                                             vu->up.eu_p->e_p != ep2 )
11157                                         {
11158                                                 skip = 1;
11159                                                 break;
11160                                         }
11161                                 }
11162 
11163                                 if( skip )
11164                                 {
11165                                         /* can't kill eu_next */
11166                                         eu = BU_LIST_PNEXT( edgeuse, &eu->l );
11167                                         continue;
11168                                 }
11169 
11170                                 vg2 = v2->vg_p;
11171 
11172                                 v1 = eu->vu_p->v_p;
11173                                 vg1 = v1->vg_p;
11174                                 v3 = eu_next->eumate_p->vu_p->v_p;
11175                                 vg3 = v3->vg_p;
11176 
11177                                 VSUB2( dir_eu, vg2->coord, vg1->coord );
11178                                 VSUB2( dir_next, vg3->coord, vg2->coord );
11179 
11180                                 skip = 1;
11181                                 if( rt_dist_pt3_line3( &dist, pca, vg1->coord, dir_eu, vg3->coord, tol ) < 2 )
11182                                         skip = 0;
11183                                 else if( rt_dist_pt3_line3( &dist, pca, vg2->coord, dir_next, vg1->coord, tol ) < 2 )
11184                                         skip = 0;
11185 
11186                                 if( skip )
11187                                 {
11188                                         /* can't kill eu_next */
11189                                         eu = BU_LIST_PNEXT( edgeuse, &eu->l );
11190                                         continue;
11191                                 }
11192 
11193                                 count++;
11194                                 /* kill all uses of eu_next edge */
11195                                 eu_tmp = eu_next->radial_p;
11196                                 while( eu_tmp != eu_next->eumate_p )
11197                                 {
11198                                         nmg_keu( eu_tmp );
11199                                         eu_tmp = eu_next->radial_p;
11200                                 }
11201                                 nmg_keu( eu_next );
11202 
11203                                 /* move all ep1 EU's using v2 to vertex v3 */
11204                                 vu = BU_LIST_FIRST( vertexuse, &v2->vu_hd );
11205                                 while( BU_LIST_NOT_HEAD( &vu->l, &v2->vu_hd ) )
11206                                 {
11207                                         NMG_CK_VERTEXUSE( vu );
11208                                         next_vu = BU_LIST_PNEXT( vertexuse, &vu->l );
11209                                         if( *vu->up.magic_p != NMG_EDGEUSE_MAGIC )
11210                                         {
11211                                                 vu = next_vu;
11212                                                 continue;
11213                                         }
11214 
11215                                         eu_tmp = vu->up.eu_p;
11216                                         NMG_CK_EDGEUSE( eu );
11217 
11218                                         if( eu->e_p != ep1 )
11219                                         {
11220                                                 vu = next_vu;
11221                                                 continue;
11222                                         }
11223 
11224                                         nmg_movevu( eu_tmp->vu_p, v3 );
11225 
11226                                         vu = next_vu;
11227                                         continue;
11228                                 }
11229 
11230                                 if( BU_LIST_IS_EMPTY( &lu->down_hd ) )
11231                                 {
11232                                         empty_lu = 1;
11233                                         empty_fu = nmg_klu( lu );
11234                                 }
11235                                 if( empty_lu )
11236                                         break;
11237 
11238                         }
11239 
11240                         if( empty_fu )
11241                                 break;
11242                         lu = lu_next;
11243                 }
11244                 if( empty_fu )
11245                 {
11246                         if( fu_next == fu->fumate_p )
11247                                 fu_next = BU_LIST_NEXT( faceuse, &fu_next->l );
11248                         (void)nmg_kfu( fu );
11249                 }
11250                 fu = fu_next;
11251         }
11252         return( count );
11253 }
11254 
11255 #define EDGE_COLLAPSE_DEBUG 0
11256 
11257 int
11258 nmg_select_collapse(const fastf_t max_dist1, const fastf_t max_dot1, const int flip1, const fastf_t max_dist2, const fastf_t max_dot2, const int flip2, const fastf_t max_dot, const fastf_t tol_dist)
11259 {
11260         unsigned int ret;
11261 
11262         ret = 1 | 2;
11263 
11264         if( flip1 )
11265                 ret = ret & ~1;
11266 
11267         if( flip2 )
11268                 ret = ret & ~2;
11269 
11270         if( max_dist1 > tol_dist )
11271                 ret = ret & ~1;
11272 
11273         if( max_dist2 > tol_dist )
11274                 ret = ret & ~2;
11275 
11276         if( max_dot1 > max_dot )
11277                 ret = ret & ~1;
11278 
11279         if( max_dot2 > max_dot )
11280                 ret = ret & ~2;
11281 
11282         if( ret == (1 | 2) )
11283         {
11284                 if( max_dot1 < max_dot2 )
11285                         ret = 1;
11286                 else
11287                         ret =  2;
11288         }
11289 
11290 #if EDGE_COLLAPSE_DEBUG
11291 bu_log( "\nmax_dot=%g, tol_coll=%g\n", max_dot, tol_dist );
11292 bu_log( "max_dist1, max_dot1, flip1: %g %g %d\n", max_dist1, max_dot1, flip1 );
11293 bu_log( "max_dist2, max_dot2, flip2: %g %g %d\n", max_dist2, max_dot2, flip2 );
11294 bu_log( "choice = %d\n", ret );
11295 #endif
11296 
11297         return( ret );
11298 }
11299 
11300 /**             N M G _ E D G E _ C O L L A P S E
11301  *
11302  * Routine to decimate an NMG model through edge collapse
11303  * to obtain a model with less faces at a greater tolerance
11304  *
11305  *  tol_coll is the tolerance distance to be used for choosing
11306  *  edges to collapse
11307  *
11308  * Model must already be triangulated (this is not checked for)
11309  *
11310  * returns number of edges collapsed
11311  */
11312 int
11313 nmg_edge_collapse(struct model *m, const struct bn_tol *tol, const fastf_t tol_coll, const fastf_t min_angle)
11314 {
11315         fastf_t max_dot;
11316         struct bu_ptbl edge_table;
11317         long edge_no=0;
11318         long count=0;
11319         long sub_count=1;
11320         int choice;
11321         int i;
11322 
11323         NMG_CK_MODEL( m );
11324         BN_CK_TOL( tol );
11325 
11326         max_dot = cos( min_angle * bn_pi / 180.0 );
11327 
11328         /* Each triangle must be its own face */
11329         (void)nmg_split_loops_into_faces( &m->magic, tol );
11330 
11331         nmg_edge_tabulate( &edge_table, &m->magic );
11332 
11333         while( sub_count )
11334         {
11335                 sub_count = 0;
11336                 for( edge_no=0 ; edge_no < BU_PTBL_END( &edge_table ) ; edge_no++ )
11337                 {
11338                         int done=0;
11339                         struct edge *e;
11340                         struct edgeuse *eu, *eu2, *eu3;
11341                         struct faceuse *fu, *fu2;
11342                         struct vertex *v1, *v2;
11343                         struct vertexuse *vu;
11344                         int real_count1, real_count2;
11345                         vect_t edge_dir;
11346                         fastf_t max_dist1, max_dist2;
11347                         fastf_t max_dot1, max_dot2;
11348                         int flip1, flip2;
11349                         int no_collapse;
11350                         int free_edge;
11351 
11352                         max_dist1 = -1.0;
11353                         max_dist2 = -1.0;
11354                         max_dot1 = -1.0;
11355                         max_dot2 = -1.0;
11356 
11357                         e = (struct edge *)BU_PTBL_GET( &edge_table, edge_no );
11358                         if( !e )
11359                                 continue;
11360                         if( e->magic != NMG_EDGE_MAGIC )
11361                                 continue;
11362 
11363                         NMG_CK_EDGE( e );
11364                         eu = e->eu_p;
11365                         NMG_CK_EDGEUSE( eu );
11366 
11367                         v1 = eu->vu_p->v_p;
11368                         NMG_CK_VERTEX( v1 );
11369                         v2 = eu->eumate_p->vu_p->v_p;
11370                         NMG_CK_VERTEX( v2 );
11371 
11372                         /* don't collapse free edges */
11373                         free_edge = 0;
11374                         eu2 = eu;
11375                         do
11376                         {
11377                                 eu3 = eu2;
11378                                 do {
11379                                         if( eu3->radial_p->eumate_p == eu3 )
11380                                         {
11381                                                 free_edge = 1;
11382                                                 break;
11383                                         }
11384                                         eu3 = BU_LIST_PNEXT_CIRC( edgeuse, &eu3->l );
11385 
11386                                 } while ( eu3 != eu2 );
11387                                 if( free_edge )
11388                                         break;
11389 
11390                                 eu2 = eu2->radial_p->eumate_p;
11391                         } while( eu2 != eu );
11392 
11393                         if( free_edge )
11394                         {
11395 #if EDGE_COLLAPSE_DEBUG
11396                                 bu_log( "Not collapsing free edge at (%g %g %g)<->(%g %g %g)\n",
11397                                         V3ARGS( v1->vg_p->coord ), V3ARGS( v2->vg_p->coord ) );
11398 #endif
11399                                 continue;
11400                         }
11401 
11402                         /* consider collapsing this edge */
11403 #if EDGE_COLLAPSE_DEBUG
11404                         bu_log( "Consider collapsing e x%x (%g %g %g)<->(%g %g %g)\n", e, V3ARGS( v1->vg_p->coord ), V3ARGS( v2->vg_p->coord ) );
11405 #endif
11406 
11407                         /* don't collapse where more than 2 real edges meet */
11408                         real_count1 = 0;
11409 
11410                         for( BU_LIST_FOR( vu, vertexuse, &v1->vu_hd ) )
11411                         {
11412                                 struct edgeuse *eu1;
11413 
11414                                 if( *vu->up.magic_p != NMG_EDGEUSE_MAGIC )
11415                                         continue;
11416 
11417                                 eu1 = vu->up.eu_p;
11418 
11419                                 fu = nmg_find_fu_of_eu( eu1 );
11420                                 if( fu->orientation != OT_SAME )
11421                                         continue;
11422 
11423                                 if( eu1->e_p->is_real )
11424                                         real_count1++;
11425                         }
11426 
11427                         if( real_count1 > 2 )
11428                         {
11429 #if EDGE_COLLAPSE_DEBUG
11430                                 bu_log( "\t%d real edges at v1\n", real_count1 );
11431 #endif
11432                                 continue;
11433                         }
11434 
11435                         real_count2 = 0;
11436 
11437                         for( BU_LIST_FOR( vu, vertexuse, &v2->vu_hd ) )
11438                         {
11439                                 struct edgeuse *eu1;
11440 
11441                                 if( *vu->up.magic_p != NMG_EDGEUSE_MAGIC )
11442                                         continue;
11443 
11444                                 eu1 = vu->up.eu_p;
11445 
11446                                 fu = nmg_find_fu_of_eu( eu1 );
11447                                 if( fu->orientation != OT_SAME )
11448                                         continue;
11449 
11450                                 if( eu1->e_p->is_real )
11451                                         real_count2++;
11452                         }
11453 
11454                         if( real_count2 > 2 )
11455                         {
11456 #if EDGE_COLLAPSE_DEBUG
11457                                 bu_log( "\t%d real edges at v1\n", real_count1 );
11458 #endif
11459                                 continue;
11460                         }
11461 
11462                         /* if real edges are involved, only collapse along the real edges */
11463                         if( (real_count1 || real_count2) && !e->is_real )
11464                         {
11465 #if EDGE_COLLAPSE_DEBUG
11466                                 bu_log( "\tthis edge is not real\n" );
11467 #endif
11468                                 continue;
11469                         }
11470 #if EDGE_COLLAPSE_DEBUG
11471                         bu_log( "\tpassed real edges test\n" );
11472 #endif
11473 
11474                         VSUB2( edge_dir, v2->vg_p->coord, v1->vg_p->coord )
11475                         VUNITIZE( edge_dir )
11476 
11477                         fu = nmg_find_fu_of_eu( eu );
11478                         NMG_CK_FACEUSE( fu );
11479                         if( fu->orientation != OT_SAME )
11480                                 fu = fu->fumate_p;
11481                         if( fu->orientation != OT_SAME )
11482                         {
11483                                 bu_log( "nmg_edge_collapse: fu (x%x) has no OT_SAME use!!!\n", fu );
11484                                 continue;
11485                         }
11486                         eu2 = eu->radial_p;
11487                         NMG_CK_EDGEUSE( eu2 );
11488                         fu2 = nmg_find_fu_of_eu( eu2 );
11489                         NMG_CK_FACEUSE( fu2 );
11490                         if( fu2->orientation != OT_SAME )
11491                                 fu2 = fu2->fumate_p;
11492                         if( fu2->orientation != OT_SAME )
11493                         {
11494                                 bu_log( "nmg_edge_collapse: fu2 (x%x) has no OT_SAME use!!!\n", fu2 );
11495                                 continue;
11496                         }
11497 
11498 #if EDGE_COLLAPSE_DEBUG
11499                         bu_log( "\tCheck moving v1 to v2\n" );
11500 #endif
11501                         /* check if moving v1 to v2 would flip any loops */
11502                         flip1 = 0;
11503                         for( BU_LIST_FOR( vu, vertexuse, &v1->vu_hd ) )
11504                         {
11505                                 struct edgeuse *eu1, *eu2;
11506                                 vect_t vec1, vec2, vec3;
11507                                 vect_t norma;
11508                                 struct vertex_g *vg1, *vg2;
11509                                 plane_t normb;
11510                                 fastf_t dist;
11511                                 fastf_t dot1;
11512 
11513                                 if( *vu->up.magic_p != NMG_EDGEUSE_MAGIC )
11514                                         continue;
11515 
11516                                 eu1 = vu->up.eu_p;
11517                                 eu2 = BU_LIST_PNEXT_CIRC( edgeuse, &eu1->l );
11518 
11519                                 if( eu2->eumate_p->vu_p->v_p == v2 )
11520                                         continue;
11521 
11522                                 if( eu2->vu_p->v_p == v2 )
11523                                         continue;
11524 
11525                                 vg1 = eu2->vu_p->v_p->vg_p;
11526                                 vg2 = eu2->eumate_p->vu_p->v_p->vg_p;
11527 
11528                                 VSUB2( vec1, v1->vg_p->coord, vg1->coord )
11529                                 VSUB2( vec2, vg2->coord, vg1->coord )
11530                                 VCROSS( norma, vec1, vec2 )
11531 #if EDGE_COLLAPSE_DEBUG
11532                                 bu_log( "\t\toriginal tri (%g %g %g) (%g %g %g) (%g %g %g)\n",
11533                                         V3ARGS( v1->vg_p->coord ),
11534                                         V3ARGS( vg1->coord ),
11535                                         V3ARGS( vg2->coord ) );
11536 #endif
11537                                 VSUB2( vec1, v2->vg_p->coord, vg1->coord )
11538                                 VCROSS( normb, vec1, vec2 )
11539                                 VSUB2( vec3, v2->vg_p->coord, vg2->coord )
11540                                 VUNITIZE( vec1 );
11541                                 VUNITIZE( vec2 );
11542                                 VUNITIZE( vec3 );
11543 
11544                                 dot1 = VDOT( vec1, vec2 );
11545                                 if( dot1 > max_dot1 )
11546                                         max_dot1 = dot1;
11547                                 dot1 = VDOT( vec1, vec3 );
11548                                 if( dot1 > max_dot1 )
11549                                         max_dot1 = dot1;
11550                                 dot1 = -VDOT( vec2, vec3 );
11551                                 if( dot1 > max_dot1 )
11552                                         max_dot1 = dot1;
11553 #if EDGE_COLLAPSE_DEBUG
11554                                 bu_log( "\t\tnew tri (%g %g %g) (%g %g %g) (%g %g %g)\n",
11555                                         V3ARGS( v2->vg_p->coord ),
11556                                         V3ARGS( vg1->coord ),
11557                                         V3ARGS( vg2->coord ) );
11558 #endif
11559                                 fu = nmg_find_fu_of_eu( eu1 );
11560                                 if( fu->orientation == OT_SAME )
11561                                 {
11562                                         VUNITIZE( normb )
11563                                         normb[3] = VDOT( normb, v2->vg_p->coord );
11564 
11565                                         dist = fabs( DIST_PT_PLANE( v1->vg_p->coord, normb ) );
11566 #if EDGE_COLLAPSE_DEBUG
11567                                         bu_log( "\t\t\tdist = %g\n", dist );
11568 #endif
11569                                         if( dist > max_dist1 )
11570                                                 max_dist1 = dist;
11571                                 }
11572 #if EDGE_COLLAPSE_DEBUG
11573                                 else
11574                                         bu_log( "\t\t\tfu->orientation = %s\n", nmg_orientation( fu->orientation ) );
11575 #endif
11576 
11577                                 if( VDOT( norma, normb ) <= 0.0 )
11578                                 {
11579                                         /* this would flip a loop */
11580                                         flip1 = 1;
11581 #if EDGE_COLLAPSE_DEBUG
11582                                         bu_log( "\t\t\tflip1 = 1\n" );
11583 #endif
11584                                         break;
11585                                 }
11586 
11587                         }
11588 
11589                         /* check if moving v2 to v1 would flip any loops */
11590 #if EDGE_COLLAPSE_DEBUG
11591                         bu_log( "\tCheck moving v2 to v1\n" );
11592 #endif
11593                         flip2 = 0;
11594                         for( BU_LIST_FOR( vu, vertexuse, &v2->vu_hd ) )
11595                         {
11596                                 struct edgeuse *eu1, *eu2;
11597                                 vect_t vec1, vec2, vec3;
11598                                 vect_t norma;
11599                                 plane_t normb;
11600                                 struct vertex_g *vg1, *vg2;
11601                                 fastf_t dist, dot1;
11602 
11603                                 if( *vu->up.magic_p != NMG_EDGEUSE_MAGIC )
11604                                         continue;
11605 
11606                                 eu1 = vu->up.eu_p;
11607                                 eu2 = BU_LIST_PNEXT_CIRC( edgeuse, &eu1->l );
11608 
11609                                 if( eu2->eumate_p->vu_p->v_p == v1 )
11610                                         continue;
11611 
11612                                 if( eu2->vu_p->v_p == v1 )
11613                                         continue;
11614 
11615                                 vg1 = eu2->vu_p->v_p->vg_p;
11616                                 vg2 = eu2->eumate_p->vu_p->v_p->vg_p;
11617 
11618                                 VSUB2( vec1, v2->vg_p->coord, vg1->coord )
11619                                 VSUB2( vec2, vg2->coord, vg1->coord )
11620                                 VCROSS( norma, vec1, vec2 )
11621 #if EDGE_COLLAPSE_DEBUG
11622                                 bu_log( "\t\toriginal tri (%g %g %g) (%g %g %g) (%g %g %g)\n",
11623                                         V3ARGS( v2->vg_p->coord ),
11624                                         V3ARGS( vg1->coord ),
11625                                         V3ARGS( vg2->coord ) );
11626 #endif
11627 
11628                                 VSUB2( vec1, v1->vg_p->coord, vg1->coord )
11629                                 VCROSS( normb, vec1, vec2 )
11630                                 VSUB2( vec3, v1->vg_p->coord, vg2->coord );
11631                                 VSUB2( vec3, v2->vg_p->coord, vg2->coord )
11632                                 VUNITIZE( vec1 );
11633                                 VUNITIZE( vec2 );
11634                                 VUNITIZE( vec3 );
11635 
11636                                 dot1 = VDOT( vec1, vec2 );
11637                                 if( dot1 > max_dot2 )
11638                                         max_dot2 = dot1;
11639                                 dot1 = VDOT( vec1, vec3 );
11640                                 if( dot1 > max_dot2 )
11641                                         max_dot2 = dot1;
11642                                 dot1 = -VDOT( vec2, vec3 );
11643                                 if( dot1 > max_dot2 )
11644                                         max_dot2 = dot1;
11645 #if EDGE_COLLAPSE_DEBUG
11646                                 bu_log( "\t\tnew tri (%g %g %g) (%g %g %g) (%g %g %g)\n",
11647                                         V3ARGS( v1->vg_p->coord ),
11648                                         V3ARGS( vg1->coord ),
11649                                         V3ARGS( vg2->coord ) );
11650 #endif
11651 
11652                                 fu = nmg_find_fu_of_eu( eu1 );
11653                                 if( fu->orientation == OT_SAME )
11654                                 {
11655                                         VUNITIZE( normb )
11656                                         normb[3] = VDOT( normb, v1->vg_p->coord );
11657 
11658                                         dist = fabs( DIST_PT_PLANE( v2->vg_p->coord, normb ));
11659 #if EDGE_COLLAPSE_DEBUG
11660                                         bu_log( "\t\t\tdist = %g\n", dist );
11661 #endif
11662                                         if( dist > max_dist2 )
11663                                                 max_dist2 = dist;
11664                                 }
11665 #if EDGE_COLLAPSE_DEBUG
11666                                 else
11667                                         bu_log( "\t\t\tfu->orientation = %s\n", nmg_orientation( fu->orientation ) );
11668 #endif
11669 
11670                                 if( VDOT( norma, normb ) <= 0.0 )
11671                                 {
11672                                         /* this would flip a loop */
11673 #if EDGE_COLLAPSE_DEBUG
11674                                         bu_log( "\t\t\tflip2 = 1\n" );
11675 #endif
11676                                         flip2 = 1;
11677                                         break;
11678                                 }
11679                         }
11680 #if EDGE_COLLAPSE_DEBUG
11681                         bu_log( "\tmax_dist1=%g, flip1=%d, max_dist2=%g, flip2=%d\n", max_dist1, flip1, max_dist2, flip2 );
11682 #endif
11683 
11684                         if( max_dist1 < 0.0 )
11685                                 max_dist1 = MAX_FASTF;
11686                         if( max_dist2 < 0.0 )
11687                                 max_dist2 = MAX_FASTF;
11688                         if( ((max_dist1 > tol_coll) || (flip1 > 0) ) &&
11689                             ((max_dist2 > tol_coll) || (flip2 > 0) ) )
11690                                 continue;
11691 
11692 
11693                         /* one last check to insure we don't collapse two faces into
11694                          * a flat plane, i.e., don't collapse an ARB4 into a single
11695                          * triangle.
11696                          */
11697                         no_collapse = 0;
11698                         for( BU_LIST_FOR( vu, vertexuse, &v1->vu_hd ) )
11699                         {
11700                                 struct edgeuse *eu1, *eu1a;
11701                                 struct edgeuse *eu2, *eu2a;
11702                                 struct vertexuse *vu2;
11703 
11704                                 if( *vu->up.magic_p != NMG_EDGEUSE_MAGIC )
11705                                         continue;
11706 
11707                                 eu1 = vu->up.eu_p;
11708                                 if( eu1->e_p == e )
11709                                         continue;
11710 
11711                                 eu1a = BU_LIST_PNEXT_CIRC( edgeuse, &eu1->l );
11712 
11713                                 for( BU_LIST_FOR( vu2, vertexuse, &v2->vu_hd ) )
11714                                 {
11715                                         if( *vu2->up.magic_p != NMG_EDGEUSE_MAGIC )
11716                                                 continue;
11717 
11718                                         eu2 = vu2->up.eu_p;
11719                                         if( eu2->e_p == e )
11720                                                 continue;
11721                                         if( eu2->e_p == eu1->e_p )
11722                                                 continue;
11723 
11724                                         eu2a = BU_LIST_PNEXT_CIRC( edgeuse, &eu2->l );
11725 
11726                                         if( NMG_ARE_EUS_ADJACENT( eu1a, eu2a ) )
11727                                         {
11728                                                 no_collapse = 1;
11729                                                 break;
11730                                         }
11731                                 }
11732 
11733                                 if( no_collapse )
11734                                         break;
11735                         }
11736 
11737                         if( no_collapse )
11738                         {
11739 #if EDGE_COLLAPSE_DEBUG
11740                                 bu_log( "\tNot collapsing edge (would create 2D object)\n" );
11741 #endif
11742                                 continue;
11743                         }
11744 
11745                         choice = nmg_select_collapse( max_dist1, max_dot1, flip1,
11746                                                   max_dist2, max_dot2, flip2,
11747                                                   max_dot, tol_coll );
11748 
11749                         if( !choice )
11750                                 continue;
11751 #if EDGE_COLLAPSE_DEBUG
11752                         bu_log( "\tCollapsing edge\n" );
11753 #endif
11754                         /* edge will be collapsed */
11755                         bu_ptbl_zero( &edge_table, (long *)e );
11756 
11757                         sub_count++;
11758 
11759                         /* kill the loops radial to the edge being collapsed */
11760                         eu2 = eu->radial_p;
11761                         done = 0;
11762                         while( !done )
11763                         {
11764                                 struct edgeuse *next;
11765                                 struct loopuse *lu;
11766                                 struct faceuse *fu;
11767 
11768                                 next = eu2->eumate_p->radial_p;
11769                                 if( next == eu2 )
11770                                         done = 1;
11771 
11772                                 if( *eu2->up.magic_p != NMG_LOOPUSE_MAGIC )
11773                                         (void)nmg_keu( eu2 );
11774                                 else
11775                                 {
11776                                         lu = eu2->up.lu_p;
11777                                         if( *lu->up.magic_p != NMG_FACEUSE_MAGIC )
11778                                                 (void)nmg_klu( lu );
11779                                         else
11780                                         {
11781                                                 fu = lu->up.fu_p;
11782                                                 if( nmg_klu( lu ) )
11783                                                         nmg_kfu( fu );
11784                                         }
11785                                 }
11786                                 eu2 = next;
11787                         }
11788 
11789                         if( choice == 1 )
11790                         {
11791                                 struct edgeuse *eu1,*eu2;
11792                                 struct vertexuse *vu2;
11793 #if EDGE_COLLAPSE_DEBUG
11794                                 bu_log( "\tMoving v1 to v2 (%g %g %g) -> (%g %g %g)\n", V3ARGS( v1->vg_p->coord ), V3ARGS( v2->vg_p->coord ) );
11795 #endif
11796                                 /* vertex1 will be moved to vertex2 */
11797 
11798                                 /* recalculate edge geometry */
11799                                 for( BU_LIST_FOR( vu, vertexuse, &v1->vu_hd ) )
11800                                 {
11801                                         struct faceuse *fu;
11802                                         struct edge_g_lseg *eg;
11803                                         struct vertex_g *v1a;
11804 
11805                                         if( *vu->up.magic_p != NMG_EDGEUSE_MAGIC )
11806                                                 continue;
11807 
11808                                         eu1 = vu->up.eu_p;
11809                                         fu = nmg_find_fu_of_eu( eu1 );
11810                                         if( !fu )
11811                                                 continue;
11812 
11813                                         if( fu->orientation != OT_SAME )
11814                                                 continue;
11815 
11816                                         if( !eu1->g.magic_p )
11817                                                 nmg_edge_g( eu1 );
11818                                         else if( *eu1->g.magic_p != NMG_EDGE_G_LSEG_MAGIC )
11819                                                 continue;
11820 
11821                                         eg = eu1->g.lseg_p;
11822                                         v1a = eu1->eumate_p->vu_p->v_p->vg_p;
11823                                         VSUB2( eg->e_dir, v2->vg_p->coord, v1a->coord );
11824                                         VMOVE( eg->e_pt, v2->vg_p->coord );
11825                                 }
11826 
11827                                 done = 0;
11828                                 while( !done )
11829                                 {
11830                                         vu = BU_LIST_FIRST( vertexuse, &v1->vu_hd );
11831                                         if( BU_LIST_LAST( vertexuse, &v1->vu_hd ) == vu )
11832                                                 done = 1;
11833                                         nmg_movevu( vu, v2 );
11834                                 }
11835                                 for( BU_LIST_FOR( vu, vertexuse, &v2->vu_hd ) )
11836                                 {
11837                                         if( *vu->up.magic_p != NMG_EDGEUSE_MAGIC )
11838                                                 continue;
11839 
11840                                         eu1 = vu->up.eu_p;
11841 
11842                                         vu2 = BU_LIST_NEXT( vertexuse, &vu->l );
11843                                         while( BU_LIST_NOT_HEAD( &vu2->l, &v2->vu_hd ) )
11844                                         {
11845                                                 if( *vu2->up.magic_p != NMG_EDGEUSE_MAGIC )
11846                                                 {
11847                                                         vu2 = BU_LIST_NEXT( vertexuse, &vu2->l );
11848                                                         continue;
11849                                                 }
11850                                                 eu2 = vu2->up.eu_p;
11851                                                 if( eu2->eumate_p->vu_p->v_p == eu1->eumate_p->vu_p->v_p &&
11852                                                         eu2->e_p != eu1->e_p )
11853                                                 {
11854                                                         struct edge *e1, *e2;
11855 
11856                                                         e1 = eu1->e_p;
11857                                                         e2 = eu2->e_p;
11858 
11859                                                         nmg_radial_join_eu_NEW( eu2, eu1, tol );
11860                                                         if( eu1->e_p != e1 )
11861                                                                 bu_ptbl_zero( &edge_table, (long *)e1 );
11862                                                         if( eu2->e_p != e2 )
11863                                                                 bu_ptbl_zero( &edge_table, (long *)e2 );
11864                                                 }
11865                                                 vu2 = BU_LIST_NEXT( vertexuse, &vu2->l );
11866                                         }
11867                                 }
11868                         }
11869                         else if( choice == 2 )
11870                         {
11871                                 struct edgeuse *eu1,*eu2;
11872                                 struct vertexuse *vu2;
11873 #if EDGE_COLLAPSE_DEBUG
11874                                 bu_log( "\tMoving v2 to v1 (%g %g %g) -> (%g %g %g)\n", V3ARGS( v2->vg_p->coord ), V3ARGS( v1->vg_p->coord ) );
11875 #endif
11876                                 /* vertex2 will be moved to vertex1 */
11877                                 /* recalculate edge geometry */
11878                                 for( BU_LIST_FOR( vu, vertexuse, &v2->vu_hd ) )
11879                                 {
11880                                         struct faceuse *fu;
11881                                         struct edge_g_lseg *eg;
11882                                         struct vertex_g *v1a;
11883 
11884                                         if( *vu->up.magic_p != NMG_EDGEUSE_MAGIC )
11885                                                 continue;
11886 
11887                                         eu1 = vu->up.eu_p;
11888                                         fu = nmg_find_fu_of_eu( eu1 );
11889                                         if( !fu )
11890                                                 continue;
11891 
11892                                         if( fu->orientation != OT_SAME )
11893                                                 continue;
11894 
11895                                         if( !eu1->g.magic_p )
11896                                                 nmg_edge_g( eu1 );
11897                                         else if( *eu1->g.magic_p != NMG_EDGE_G_LSEG_MAGIC )
11898                                                 continue;
11899 
11900                                         eg = eu1->g.lseg_p;
11901                                         v1a = eu1->eumate_p->vu_p->v_p->vg_p;
11902                                         VSUB2( eg->e_dir, v1->vg_p->coord, v1a->coord );
11903                                         VMOVE( eg->e_pt, v1->vg_p->coord );
11904                                 }
11905 
11906                                 done = 0;
11907                                 while( !done )
11908                                 {
11909                                         vu = BU_LIST_FIRST( vertexuse, &v2->vu_hd );
11910                                         if( BU_LIST_LAST( vertexuse, &v2->vu_hd ) == vu )
11911                                                 done = 1;
11912                                         nmg_movevu( vu, v1 );
11913                                 }
11914                                 for( BU_LIST_FOR( vu, vertexuse, &v1->vu_hd ) )
11915                                 {
11916                                         if( *vu->up.magic_p != NMG_EDGEUSE_MAGIC )
11917                                                 continue;
11918 
11919                                         eu1 = vu->up.eu_p;
11920 
11921                                         vu2 = BU_LIST_NEXT( vertexuse, &vu->l );
11922                                         while( BU_LIST_NOT_HEAD( &vu2->l, &v1->vu_hd ) )
11923                                         {
11924                                                 if( *vu2->up.magic_p != NMG_EDGEUSE_MAGIC )
11925                                                 {
11926                                                         vu2 = BU_LIST_NEXT( vertexuse, &vu2->l );
11927                                                         continue;
11928                                                 }
11929                                                 eu2 = vu2->up.eu_p;
11930                                                 if( eu2->eumate_p->vu_p->v_p == eu1->eumate_p->vu_p->v_p &&
11931                                                         eu2->e_p != eu1->e_p )
11932                                                 {
11933                                                         struct edge *e1, *e2;
11934 
11935                                                         e1 = eu1->e_p;
11936                                                         e2 = eu2->e_p;
11937                                                         nmg_radial_join_eu_NEW( eu2, eu1, tol );
11938                                                         if( eu1->e_p != e1 )
11939                                                                 bu_ptbl_zero( &edge_table, (long *)e1 );
11940                                                         if( eu2->e_p != e2 )
11941                                                                 bu_ptbl_zero( &edge_table, (long *)e2 );
11942                                                 }
11943                                                 vu2 = BU_LIST_NEXT( vertexuse, &vu2->l );
11944                                         }
11945                                 }
11946                         }
11947                         else
11948                                 continue;
11949                 }
11950                 count += sub_count;
11951         }
11952 
11953         if( count )
11954         {
11955                 /* recalculate face planes */
11956                 /* re-use edge table space */
11957                 bu_ptbl_reset( &edge_table );
11958                 nmg_face_tabulate( &edge_table, &m->magic );
11959 
11960                 for( i=0 ; i<BU_PTBL_END( &edge_table ) ; i++ )
11961                 {
11962                         struct face *f;
11963                         struct faceuse *fu;
11964 
11965                         f = (struct face *)BU_PTBL_GET( &edge_table, i );
11966                         NMG_CK_FACE( f );
11967                         fu = f->fu_p;
11968                         if( fu->orientation != OT_SAME )
11969                                 fu = fu->fumate_p;
11970                         if( fu->orientation != OT_SAME )
11971                                 bu_bomb( "nmg_edge_collapse: Face has no OT_SAME use!!!\n" );
11972 
11973                         nmg_calc_face_g( fu );
11974                 }
11975         }
11976         bu_ptbl_free( &edge_table );
11977         return( count );
11978 }
11979 
11980 /**                     N M G _ B O T
11981  *
11982  *      Convert an NMG to a BOT solid
11983  */
11984 
11985 struct rt_bot_internal *
11986 nmg_bot(struct shell *s, const struct bn_tol *tol)
11987 {
11988         struct rt_bot_internal  *bot;
11989         struct bu_ptbl          nmg_vertices;
11990         struct bu_ptbl          nmg_faces;
11991         int                     i, face_no;
11992         struct vertex           *v;
11993 
11994         NMG_CK_SHELL( s );
11995         BN_CK_TOL(tol);
11996 
11997         /* first convert the NMG to triangles */
11998         (void)nmg_triangulate_shell(s, tol);
11999 
12000         /* make a list of all the vertices */
12001         nmg_vertex_tabulate( &nmg_vertices, &s->l.magic );
12002 
12003         /* and a list of all the faces */
12004         nmg_face_tabulate( &nmg_faces, &s->l.magic );
12005 
12006         /* now build the BOT */
12007         bot = (struct rt_bot_internal *)bu_calloc(1, sizeof( struct rt_bot_internal ), "BOT from NMG" );
12008 
12009         bot->magic = RT_BOT_INTERNAL_MAGIC;
12010         bot->mode = RT_BOT_SOLID;
12011         bot->orientation = RT_BOT_CCW;
12012         bot->bot_flags = 0;
12013 
12014         bot->num_vertices = BU_PTBL_LEN( &nmg_vertices );
12015         bot->num_faces = 0;
12016 
12017         /* count the number of triangles */
12018         for( i=0 ; i<BU_PTBL_LEN( &nmg_faces ); i++ )
12019         {
12020                 struct face *f;
12021                 struct faceuse *fu;
12022                 struct loopuse *lu;
12023 
12024                 f = (struct face *)BU_PTBL_GET( &nmg_faces, i );
12025                 NMG_CK_FACE( f );
12026 
12027                 fu = f->fu_p;
12028 
12029                 if( fu->orientation != OT_SAME )
12030                 {
12031                         fu = fu->fumate_p;
12032                         if( fu->orientation != OT_SAME )
12033                         {
12034                                 bu_log( "nmg_bot(): Face has no OT_SAME use!!!!\n" );
12035                                 bu_free( (char *)bot->vertices, "BOT vertices" );
12036                                 bu_free( (char *)bot->faces, "BOT faces" );
12037                                 bu_free( (char *)bot, "BOT" );
12038                                 return( (struct rt_bot_internal *)NULL );
12039                         }
12040                 }
12041 
12042                 for( BU_LIST_FOR( lu, loopuse, &fu->lu_hd ) )
12043                 {
12044                         if( BU_LIST_FIRST_MAGIC(&lu->down_hd) != NMG_EDGEUSE_MAGIC )
12045                                 continue;
12046                         bot->num_faces++;
12047                 }
12048         }
12049 
12050         bot->faces = (int *)bu_calloc( bot->num_faces * 3, sizeof( int ), "BOT faces" );
12051         bot->vertices = (fastf_t *)bu_calloc( bot->num_vertices * 3, sizeof( fastf_t ), "BOT vertices" );
12052 
12053         bot->thickness = (fastf_t *)NULL;
12054         bot->face_mode = (struct bu_bitv *)NULL;
12055 
12056         /* fill in the vertices */
12057         for( i=0 ; i<BU_PTBL_LEN( &nmg_vertices ) ; i++ )
12058         {
12059                 struct vertex_g *vg;
12060 
12061                 v = (struct vertex *)BU_PTBL_GET( &nmg_vertices, i );
12062                 NMG_CK_VERTEX( v );
12063 
12064                 vg = v->vg_p;
12065                 NMG_CK_VERTEX_G( vg );
12066 
12067                 VMOVE( &bot->vertices[i*3], vg->coord );
12068         }
12069 
12070         /* fill in the faces */
12071         face_no = 0;
12072         for( i=0 ; i<BU_PTBL_LEN( &nmg_faces ); i++ )
12073         {
12074                 struct face *f;
12075                 struct faceuse *fu;
12076                 struct loopuse *lu;
12077 
12078                 f = (struct face *)BU_PTBL_GET( &nmg_faces, i );
12079                 NMG_CK_FACE( f );
12080 
12081                 fu = f->fu_p;
12082 
12083                 if( fu->orientation != OT_SAME )
12084                 {
12085                         fu = fu->fumate_p;
12086                         if( fu->orientation != OT_SAME )
12087                         {
12088                                 bu_log( "nmg_bot(): Face has no OT_SAME use!!!!\n" );
12089                                 bu_free( (char *)bot->vertices, "BOT vertices" );
12090                                 bu_free( (char *)bot->faces, "BOT faces" );
12091                                 bu_free( (char *)bot, "BOT" );
12092                                 return( (struct rt_bot_internal *)NULL );
12093                         }
12094                 }
12095 
12096                 for( BU_LIST_FOR( lu, loopuse, &fu->lu_hd ) )
12097                 {
12098                         struct edgeuse *eu;
12099                         int vertex_no=0;
12100 
12101                         if( BU_LIST_FIRST_MAGIC(&lu->down_hd) != NMG_EDGEUSE_MAGIC )
12102                                 continue;
12103 
12104                         for( BU_LIST_FOR( eu, edgeuse, &lu->down_hd ) )
12105                         {
12106                                 struct vertex *v;
12107 
12108                                 if( vertex_no > 2 )
12109                                 {
12110                                         bu_log( "nmg_bot(): Face has not been triangulated!!!\n" );
12111                                         bu_free( (char *)bot->vertices, "BOT vertices" );
12112                                         bu_free( (char *)bot->faces, "BOT faces" );
12113                                         bu_free( (char *)bot, "BOT" );
12114                                         return( (struct rt_bot_internal *)NULL );
12115                                 }
12116 
12117                                 v = eu->vu_p->v_p;
12118                                 NMG_CK_VERTEX( v );
12119 
12120 
12121                                 bot->faces[ face_no*3 + vertex_no ] = bu_ptbl_locate( &nmg_vertices, (long *)v );
12122 
12123                                 vertex_no++;
12124                         }
12125 
12126                         face_no++;
12127                 }
12128         }
12129 
12130         bu_ptbl_free( &nmg_vertices );
12131         bu_ptbl_free( &nmg_faces );
12132 
12133         return( bot );
12134 }
12135 
12136 /**     N M G _ V L I S T _ T O _ E U
12137  *
12138  * create wire edges corresponding to the lines in the vlist. The wire edges are
12139  * created in the specified shell
12140  *
12141  */
12142 void
12143 nmg_vlist_to_eu( struct bu_list *vlist, struct shell *s )
12144 {
12145     point_t pt1, pt2;
12146     struct bn_vlist *vp;
12147     struct edgeuse *eu;
12148     struct vertex *v=NULL;
12149     struct vertex *polyStartV=NULL;
12150 
12151     for( BU_LIST_FOR( vp, bn_vlist, vlist ) )  {
12152         register int    i;
12153         register int    nused = vp->nused;
12154         register int    *cmd = vp->cmd;
12155         register point_t *pt = vp->pt;
12156         for( i = 0; i < nused; i++,cmd++,pt++ )  {
12157             switch( *cmd ) {
12158             case BN_VLIST_LINE_MOVE:
12159             case BN_VLIST_POLY_MOVE:
12160                 VMOVE( pt2, *pt );
12161                 v = NULL;
12162                 polyStartV = NULL;
12163                 break;
12164             case BN_VLIST_LINE_DRAW:
12165             case BN_VLIST_POLY_DRAW:
12166                 VMOVE( pt1, pt2 );
12167                 VMOVE( pt2, *pt );
12168                 eu = nmg_me( v, NULL, s );
12169                 if( v == NULL ) {
12170                     nmg_vertex_gv( eu->vu_p->v_p, pt1 );
12171                 }
12172                 nmg_vertex_gv( eu->eumate_p->vu_p->v_p, pt2 );
12173                 v = eu->eumate_p->vu_p->v_p;
12174                 if( polyStartV == NULL ) polyStartV = eu->vu_p->v_p;
12175                 break;
12176             case BN_VLIST_POLY_END:
12177                 if( v != NULL &&  polyStartV != NULL ) {
12178                     eu = nmg_me( v, polyStartV, s );
12179                 }
12180                 break;
12181             case BN_VLIST_POLY_START:
12182                 polyStartV = NULL;
12183                 v = NULL;
12184                 break;
12185             default:
12186                 break;
12187             }
12188         }
12189     }
12190 }
12191 
12192 /*
12193  * Local Variables:
12194  * mode: C
12195  * tab-width: 8
12196  * c-basic-offset: 4
12197  * indent-tabs-mode: t
12198  * End:
12199  * ex: shiftwidth=4 tabstop=8
12200  */

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