nmg_bool.c

Go to the documentation of this file.
00001 /*                      N M G _ B O O L . 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 /** @addgroup nmg */
00023 /*@{*/
00024 /** @file nmg_bool.c
00025  *      Support for boolean operations on NMG objects.  Most of the routines
00026  *      in here are static/local to this file.  The interfaces here are the
00027  *      functions "nmg_do_bool" and "nmg_mesh_faces".  The former does boolean
00028  *      operations on a pair of shells.  The latter is a function to make
00029  *      edges shared between two faces whenever possible.
00030  *
00031  *  Authors -
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 #ifndef lint
00041 static const char RCSid[] = "@(#)$Header: /cvsroot/brlcad/brlcad/src/librt/nmg_bool.c,v 14.13 2006/09/16 02:04:25 lbutler Exp $ (ARL)";
00042 #endif
00043 
00044 #include "common.h"
00045 
00046 #include <stdlib.h>
00047 #include <stdio.h>
00048 #include <math.h>
00049 #include <string.h>
00050 
00051 #include "machine.h"
00052 #include "vmath.h"
00053 #include "nmg.h"
00054 #include "raytrace.h"
00055 #include "./debug.h"
00056 
00057 
00058 extern int nmg_class_nothing_broken;
00059 
00060 /* XXX Move to nmg_manif.c or nmg_ck.c */
00061 struct dangling_faceuse_state {
00062         char            *visited;
00063         const char      *manifolds;
00064         int             count;
00065 };
00066 
00067 int debug_file_count=0;
00068 
00069 static void
00070 nmg_dangling_handler(long int *longp, genptr_t state, int first)
00071 {
00072         register struct faceuse *fu = (struct faceuse *)longp;
00073         register struct dangling_faceuse_state *sp =
00074                 (struct dangling_faceuse_state *)state;
00075 
00076         NMG_CK_FACEUSE(fu);
00077         if( fu->orientation != OT_SAME )  return;
00078         /* If this faceuse has been processed before, do nothing more */
00079         if( !NMG_INDEX_FIRST_TIME(sp->visited, fu) )  return;
00080 
00081         if( nmg_dangling_face(fu, sp->manifolds ) )  {
00082                 sp->count++;
00083         }
00084 }
00085 
00086 /**
00087  *                      N M G _ H A S _ D A N G L I N G _ F A C E S
00088  *
00089  *  Argument is expected to be model, region, shell, or faceuse pointer.
00090  *
00091  *  Returns -
00092  *      0       No dangling faces
00093  *      !0      Has dangling faces
00094  */
00095 int
00096 nmg_has_dangling_faces(long int *magic_p, const char *manifolds)
00097 {
00098         struct model                    *m;
00099         struct dangling_faceuse_state   st;
00100         static const struct nmg_visit_handlers  handlers = {NULL, NULL, NULL, NULL, NULL,
00101                                                             NULL, NULL, NULL, nmg_dangling_handler, NULL,
00102                                                             NULL, NULL, NULL, NULL, NULL,
00103                                                             NULL, NULL, NULL, NULL, NULL,
00104                                                             NULL, NULL, NULL, NULL, NULL};
00105         /* handlers.bef_faceuse = nmg_dangling_handler; */
00106 
00107         m = nmg_find_model( magic_p );
00108         NMG_CK_MODEL(m);
00109         st.visited = (char *)bu_calloc(m->maxindex+1, sizeof(char), "visited[]");
00110         st.manifolds = manifolds;
00111         st.count = 0;
00112 
00113         nmg_visit( magic_p, &handlers, (genptr_t)&st );
00114 
00115         bu_free( (char *)st.visited, "visited[]");
00116         return st.count;
00117 }
00118 
00119 /**
00120  *                      N M G _ S H O W _ E A C H _ L O O P
00121  *
00122  *  Within a shell, show each loop as a separate display.
00123  *  Pause after displaying each one.
00124  *
00125  *  Note that in "non-fancy" mode, show_broken_eu() draws just the edge.
00126  */
00127 void
00128 nmg_show_each_loop(struct shell *s, long int **classlist, int new, int fancy, const char *str)
00129 
00130 
00131                                 /* non-zero means flush previous vlist */
00132                                 /* non-zero means pause after the display */
00133 {
00134         struct faceuse  *fu;
00135         struct loopuse  *lu;
00136         char            buf[128];
00137         long            save;
00138 
00139         NMG_CK_SHELL(s);
00140         save = rt_g.NMG_debug;
00141         for( BU_LIST_FOR( fu, faceuse, &s->fu_hd ) )  {
00142                 NMG_CK_FACEUSE(fu);
00143                 if( fu->orientation == OT_OPPOSITE )  continue;
00144                 for( BU_LIST_FOR( lu, loopuse, &fu->lu_hd ) )  {
00145                         NMG_CK_LOOPUSE(lu);
00146                         if( BU_LIST_FIRST_MAGIC(&lu->down_hd) == NMG_VERTEXUSE_MAGIC )
00147                                 continue;
00148                         /* Display only OT_SAME, and OT_UNSPEC et.al.  */
00149                         if( lu->orientation == OT_OPPOSITE )  continue;
00150 
00151                         sprintf(buf, "%s=x%lx", str, (unsigned long)lu);
00152                         nmg_show_broken_classifier_stuff(&lu->l.magic, classlist, new, fancy, buf);
00153                 }
00154         }
00155         for( BU_LIST_FOR( lu, loopuse, &s->lu_hd ) )  {
00156                 sprintf(buf, "%s=x%lx (wire)", str, (unsigned long)lu);
00157                 nmg_show_broken_classifier_stuff(&lu->l.magic, classlist, new, fancy, buf);
00158         }
00159         rt_g.NMG_debug = save;          /* restore it */
00160 }
00161 
00162 void
00163 stash_shell(struct shell *s, char *file_name, char *title, const struct bn_tol *tol)
00164 {
00165         struct model *m;
00166         struct nmgregion *r;
00167         struct shell *new_s;
00168         struct faceuse *fu;
00169         char counted_name[256];
00170 
00171         m = nmg_mm();
00172         r = nmg_mrsv( m );
00173         new_s = BU_LIST_FIRST( shell, &r->s_hd );
00174 
00175         for( BU_LIST_FOR( fu, faceuse, &s->fu_hd ) )
00176         {
00177                 if( fu->orientation != OT_SAME )
00178                         continue;
00179 
00180                 (void)nmg_dup_face( fu, new_s );
00181         }
00182 
00183         nmg_rebound( m, tol );
00184         sprintf( counted_name, "%s%d.g", file_name, debug_file_count );
00185         nmg_stash_model_to_file( counted_name, m, title );
00186         nmg_km( m );
00187 }
00188 
00189 void
00190 nmg_kill_non_common_cracks(struct shell *sA, struct shell *sB)
00191 {
00192         struct faceuse *fu;
00193         struct faceuse *fu_next;
00194 
00195         if( rt_g.NMG_debug & DEBUG_BASIC )
00196                 bu_log( "nmg_kill_non_common_cracks( s=%x and %x )\n" , sA, sB );
00197 
00198         NMG_CK_SHELL( sA );
00199         NMG_CK_SHELL( sB );
00200 
00201         fu = BU_LIST_FIRST( faceuse, &sA->fu_hd );
00202         while( BU_LIST_NOT_HEAD( fu, &sA->fu_hd ) )
00203         {
00204                 struct loopuse *lu;
00205                 struct loopuse *lu_next;
00206                 int empty_face=0;
00207 
00208                 NMG_CK_FACEUSE( fu );
00209 
00210                 fu_next = BU_LIST_PNEXT( faceuse, &fu->l );
00211                 while( BU_LIST_NOT_HEAD( fu_next, &sA->fu_hd )
00212                         && fu_next == fu->fumate_p )
00213                                 fu_next = BU_LIST_PNEXT( faceuse, &fu_next->l );
00214 
00215                 lu = BU_LIST_FIRST( loopuse, &fu->lu_hd );
00216                 while( BU_LIST_NOT_HEAD( lu, &fu->lu_hd ) )
00217                 {
00218                         struct edgeuse *eu;
00219                         struct edgeuse *eu_next;
00220                         int empty_loop=0;
00221 
00222                         NMG_CK_LOOPUSE( lu );
00223 
00224                         lu_next = BU_LIST_PNEXT( loopuse, &lu->l );
00225 
00226                         if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) != NMG_EDGEUSE_MAGIC )
00227                         {
00228                                 lu = lu_next;
00229                                 continue;
00230                         }
00231 
00232 crack_topA:
00233                         for( BU_LIST_FOR( eu, edgeuse, &lu->down_hd ) )
00234                         {
00235                                 NMG_CK_EDGEUSE( eu );
00236 
00237                                 eu_next = BU_LIST_PNEXT_CIRC( edgeuse, &eu->l );
00238                                 NMG_CK_EDGEUSE( eu_next );
00239 
00240                                 /* check if eu and eu_next form a jaunt */
00241                                 if( eu->vu_p->v_p != eu_next->eumate_p->vu_p->v_p )
00242                                         continue;
00243 
00244                                 /* check if vertex at apex is in other shell
00245                                  * if so, we need this vertex, can't kill crack
00246                                  */
00247                                 if( nmg_find_v_in_shell( eu_next->vu_p->v_p, sB, 0 ) )
00248                                         continue;
00249 
00250                                 if( nmg_keu( eu ) )
00251                                         empty_loop = 1;
00252                                 else if( nmg_keu( eu_next ) )
00253                                         empty_loop = 1;
00254 
00255                                 if( empty_loop )
00256                                         break;
00257 
00258                                 goto crack_topA;
00259                         }
00260                         if( empty_loop )
00261                         {
00262                                 if( nmg_klu( lu ) )
00263                                 {
00264                                         empty_face = 1;
00265                                         break;
00266                                 }
00267                         }
00268                         lu = lu_next;
00269                 }
00270                 if( empty_face )
00271                 {
00272                         if( nmg_kfu( fu ) )
00273                         {
00274                                 break;
00275                         }
00276                 }
00277                 fu = fu_next;
00278         }
00279 
00280         fu = BU_LIST_FIRST( faceuse, &sB->fu_hd );
00281         while( BU_LIST_NOT_HEAD( fu, &sB->fu_hd ) )
00282         {
00283                 struct loopuse *lu;
00284                 struct loopuse *lu_next;
00285                 int empty_face=0;
00286 
00287                 NMG_CK_FACEUSE( fu );
00288 
00289                 fu_next = BU_LIST_PNEXT( faceuse, &fu->l );
00290                 while( BU_LIST_NOT_HEAD( fu_next, &sB->fu_hd )
00291                         && fu_next == fu->fumate_p )
00292                                 fu_next = BU_LIST_PNEXT( faceuse, &fu_next->l );
00293 
00294                 lu = BU_LIST_FIRST( loopuse, &fu->lu_hd );
00295                 while( BU_LIST_NOT_HEAD( lu, &fu->lu_hd ) )
00296                 {
00297                         struct edgeuse *eu;
00298                         struct edgeuse *eu_next;
00299                         int empty_loop=0;
00300 
00301                         NMG_CK_LOOPUSE( lu );
00302 
00303                         lu_next = BU_LIST_PNEXT( loopuse, &lu->l );
00304 
00305                         if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) != NMG_EDGEUSE_MAGIC )
00306                         {
00307                                 lu = lu_next;
00308                                 continue;
00309                         }
00310 
00311 crack_top:
00312                         for( BU_LIST_FOR( eu, edgeuse, &lu->down_hd ) )
00313                         {
00314                                 NMG_CK_EDGEUSE( eu );
00315 
00316                                 eu_next = BU_LIST_PNEXT_CIRC( edgeuse, &eu->l );
00317                                 NMG_CK_EDGEUSE( eu_next );
00318 
00319                                 /* check if eu and eu_next form a jaunt */
00320                                 if( eu->vu_p->v_p != eu_next->eumate_p->vu_p->v_p )
00321                                         continue;
00322 
00323                                 /* check if crack apex is in other shell */
00324                                 if( nmg_find_v_in_shell( eu_next->vu_p->v_p, sA, 0 ) )
00325                                         continue;
00326 
00327                                 if( nmg_keu( eu ) )
00328                                         empty_loop = 1;
00329                                 else if( nmg_keu( eu_next ) )
00330                                         empty_loop = 1;
00331 
00332                                 if( empty_loop )
00333                                         break;
00334 
00335                                 goto crack_top;
00336                         }
00337                         if( empty_loop )
00338                         {
00339                                 if( nmg_klu( lu ) )
00340                                 {
00341                                         empty_face = 1;
00342                                         break;
00343                                 }
00344                         }
00345                         lu = lu_next;
00346                 }
00347                 if( empty_face )
00348                 {
00349                         if( nmg_kfu( fu ) )
00350                         {
00351                                 break;
00352                         }
00353                 }
00354                 fu = fu_next;
00355         }
00356 }
00357 
00358 /**                     N M G _ C L A S S I F Y _ S H A R E D _ E D G E S _ V E R T S
00359  *
00360  *      Preprocessor routine for classifier to get all the easy shared edges and
00361  *      vertices marked as shared.
00362  */
00363 
00364 static void
00365 nmg_classify_shared_edges_verts(struct shell *sA, struct shell *sB, long int **classlist)
00366 {
00367         struct bu_ptbl verts;
00368         struct bu_ptbl edges;
00369         int i;
00370 
00371         if (rt_g.NMG_debug & DEBUG_CLASSIFY)
00372                 bu_log( "nmg_classify_shared_edges_verts( sA=x%x, sB=x%x )\n", sA, sB );
00373 
00374         NMG_CK_SHELL( sA );
00375         NMG_CK_SHELL( sB );
00376 
00377         nmg_vertex_tabulate( &verts, &sA->l.magic );
00378         for( i=0 ; i<BU_PTBL_END( &verts ) ; i++ )
00379         {
00380                 struct vertex *v;
00381                 struct vertexuse *vu;
00382 
00383                 v = (struct vertex *)BU_PTBL_GET( &verts, i );
00384                 NMG_CK_VERTEX( v );
00385 
00386                 for( BU_LIST_FOR( vu, vertexuse, &v->vu_hd ) )
00387                 {
00388                         NMG_CK_VERTEXUSE( vu );
00389 
00390                         if( nmg_find_s_of_vu( vu ) == sB )
00391                         {
00392                                 /* set classification in both lists */
00393                                 NMG_INDEX_SET(classlist[NMG_CLASS_AonBshared], v );
00394                                 NMG_INDEX_SET(classlist[4 + NMG_CLASS_AonBshared], v );
00395 
00396                                 if (rt_g.NMG_debug & DEBUG_CLASSIFY)
00397                                         bu_log( "nmg_classify_shared_edges_verts: v=x%x is shared\n", v );
00398 
00399                                 break;
00400                         }
00401                 }
00402         }
00403         bu_ptbl_free( &verts);
00404 
00405         nmg_edge_tabulate( &edges, &sA->l.magic );
00406         for( i=0 ; i<BU_PTBL_END( &edges ) ; i++ )
00407         {
00408                 struct edge *e;
00409                 struct edgeuse *eu;
00410                 struct edgeuse *eu_start;
00411 
00412                 e = (struct edge *)BU_PTBL_GET( &edges, i );
00413                 NMG_CK_EDGE( e );
00414 
00415                 eu_start = e->eu_p;
00416                 NMG_CK_EDGEUSE( eu_start );
00417 
00418                 eu = eu_start;
00419                 do
00420                 {
00421                         if( nmg_find_s_of_eu( eu ) == sB )
00422                         {
00423                                 NMG_INDEX_SET(classlist[NMG_CLASS_AonBshared], e );
00424                                 NMG_INDEX_SET(classlist[4 + NMG_CLASS_AonBshared], e );
00425 
00426                                 if (rt_g.NMG_debug & DEBUG_CLASSIFY)
00427                                         bu_log( "nmg_classify_shared_edges_verts: e=x%x is shared\n", e );
00428 
00429                                 break;
00430                         }
00431 
00432                         eu = eu->eumate_p->radial_p;
00433                         NMG_CK_EDGEUSE( eu );
00434 
00435                 } while( eu != eu_start && eu->eumate_p != eu_start );
00436         }
00437         bu_ptbl_free( &edges);
00438 }
00439 
00440 /**             N M G _ K I L L _ A N T I _ L O O P S
00441  *
00442  *      Look for same loop in opposite direction in shell "s",
00443  *      Kill them.
00444  */
00445 
00446 void
00447 nmg_kill_anti_loops(struct shell *s, const struct bn_tol *tol)
00448 {
00449         struct bu_ptbl loops;
00450         struct faceuse *fu;
00451         struct loopuse *lu;
00452         int i,j;
00453 
00454         NMG_CK_SHELL( s );
00455         BN_CK_TOL( tol );
00456 
00457         bu_ptbl_init( &loops, 64, " &loops");
00458 
00459         for( BU_LIST_FOR( fu, faceuse, &s->fu_hd ) )
00460         {
00461                 NMG_CK_FACEUSE( fu );
00462 
00463                 if( fu->orientation != OT_SAME )
00464                         continue;
00465 
00466                 for( BU_LIST_FOR( lu, loopuse, &fu->lu_hd ) )
00467                 {
00468                         NMG_CK_LOOPUSE( lu );
00469 
00470                         if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) != NMG_EDGEUSE_MAGIC )
00471                                 continue;
00472 
00473                         bu_ptbl_ins( &loops, (long *)lu );
00474                 }
00475         }
00476 
00477         for( i=0 ; i < BU_PTBL_END( &loops ) ; i++ )
00478         {
00479                 struct loopuse *lu1;
00480                 struct edgeuse *eu1_start;
00481                 struct vertex *v1;
00482 
00483                 lu1 = (struct loopuse *)BU_PTBL_GET( &loops, i );
00484                 NMG_CK_LOOPUSE( lu1 );
00485 
00486                 eu1_start = BU_LIST_FIRST( edgeuse, &lu1->down_hd );
00487                 NMG_CK_EDGEUSE( eu1_start );
00488                 v1 = eu1_start->vu_p->v_p;
00489                 NMG_CK_VERTEX( v1 );
00490 
00491                 for( j=i+1 ; j<BU_PTBL_END( &loops ) ; j++ )
00492                 {
00493                         struct loopuse *lu2;
00494                         struct edgeuse *eu1;
00495                         struct edgeuse *eu2;
00496                         struct vertexuse *vu2;
00497                         struct faceuse *fu1,*fu2;
00498                         int anti=1;
00499 
00500                         lu2 = (struct loopuse *)BU_PTBL_GET( &loops, j );
00501                         NMG_CK_LOOPUSE( lu2 );
00502 
00503                         /* look for v1 in lu2 */
00504                         vu2 = nmg_find_vertex_in_lu( v1, lu2 );
00505 
00506                         if( !vu2 )
00507                                 continue;
00508 
00509                         /* found common vertex, now look for the rest */
00510                         eu2 = vu2->up.eu_p;
00511                         NMG_CK_EDGEUSE( eu2 );
00512                         eu1 = eu1_start;
00513                         do
00514                         {
00515                                 eu2 = BU_LIST_PNEXT_CIRC( edgeuse, &eu2->l );
00516                                 eu1 = BU_LIST_PPREV_CIRC( edgeuse, &eu1->l );
00517 
00518                                 if( eu2->vu_p->v_p != eu1->vu_p->v_p )
00519                                 {
00520                                         anti = 0;
00521                                         break;
00522                                 }
00523                         } while( eu1 != eu1_start );
00524 
00525                         if( !anti )
00526                                 continue;
00527 
00528                         fu1 = lu1->up.fu_p;
00529                         fu2 = lu2->up.fu_p;
00530 
00531                         if( fu1 == fu2 )
00532                                 continue;
00533 
00534                         if( nmg_klu( lu1 ) )
00535                         {
00536                                 if( nmg_kfu( fu1 ) )
00537                                         goto out;
00538                         }
00539                         if( nmg_klu( lu2 ) )
00540                         {
00541                                 if( nmg_kfu( fu2 ) )
00542                                         goto out;
00543                         }
00544 
00545                         bu_ptbl_rm( &loops, (long *)lu1 );
00546                         bu_ptbl_rm( &loops, (long *)lu2 );
00547                         i--;
00548                         break;
00549                 }
00550         }
00551 out:
00552         bu_ptbl_free( &loops);
00553 }
00554 
00555 void
00556 nmg_kill_wire_edges(struct shell *s)
00557 {
00558         struct loopuse *lu;
00559         struct edgeuse *eu;
00560 
00561         while( BU_LIST_NON_EMPTY( &s->lu_hd ) )
00562         {
00563                 lu = BU_LIST_FIRST( loopuse, &s->lu_hd );
00564                 nmg_klu( lu );
00565         }
00566 
00567         while( BU_LIST_NON_EMPTY( &s->eu_hd ) )
00568         {
00569                 eu = BU_LIST_FIRST( edgeuse, &s->eu_hd );
00570                 nmg_keu( eu );
00571         }
00572 }
00573 
00574 /**
00575  *                      N M G _ B O O L
00576  *
00577  *      Perform boolean operations on a pair of shells.
00578  *
00579  *  The return is an updated version of shell A.
00580  *  Shell B is destroyed.
00581  *
00582  *  XXX this probably should operate on regions, not shells.
00583  */
00584 static struct shell * nmg_bool(struct shell *sA, struct shell *sB, const int oper, const struct bn_tol *tol)
00585 {
00586         int     i;
00587         int     nelem;
00588         long    *classlist[8];
00589         FILE    *fd, *fp;
00590         struct model            *m;
00591         struct nmgregion        *rA;
00592         struct nmgregion        *rB;
00593 
00594         NMG_CK_SHELL(sA);
00595         NMG_CK_SHELL(sB);
00596         rA = sA->r_p;
00597         rB = sB->r_p;
00598         NMG_CK_REGION(rA);
00599         NMG_CK_REGION(rB);
00600         m = rA->m_p;
00601         NMG_CK_MODEL(m);
00602 
00603         debug_file_count++;
00604         if( rt_g.NMG_debug & DEBUG_VERIFY )  {
00605 #if 0
00606                 nmg_vshell( &rA->s_hd, rA );
00607                 nmg_vshell( &rB->s_hd, rB );
00608 #else
00609                 /* Sometimes the tessllations of non-participating regions
00610                  * are damaged during a boolean operation.  Check everything.
00611                  */
00612                 nmg_vmodel(m);
00613 #endif
00614 #if VERBOSE_VERIFY
00615                 bu_log("\n==================== Shell A:\n");
00616                 nmg_pr_s_briefly(sA, 0);
00617                 bu_log("\n==================== Shell B:\n");
00618                 nmg_pr_s_briefly(sB, 0);
00619                 bu_log("\n====================\n");
00620 #endif
00621         }
00622 #if 0
00623 nmg_s_radial_check( sA, tol );
00624 nmg_s_radial_check( sB, tol );
00625 #endif
00626 
00627         nmg_shell_coplanar_face_merge(sA, tol, 1);
00628         nmg_shell_coplanar_face_merge(sB, tol, 1);
00629 
00630         nmg_model_fuse( m, tol );
00631 
00632         if (nmg_check_closed_shell(sA, tol)) {
00633                 if (rt_g.NMG_debug & DEBUG_BOOL &&
00634                     rt_g.NMG_debug & DEBUG_PLOTEM) {
00635                         if ((fp=fopen("Unclosed.pl", "w")) != (FILE *)NULL) {
00636                                 bu_log("Plotting unclosed NMG shell\n");
00637                                 nmg_pl_s(fp, sA);
00638                                 fclose(fp);
00639                         }
00640                 }
00641                 if (rt_g.NMG_debug & DEBUG_BOOL)
00642                         nmg_pr_s(sA, "");
00643 
00644                 bu_log("nmg_bool: sA is unclosed, barging ahead\n");
00645         }
00646 
00647         if (nmg_check_closed_shell(sB, tol)) {
00648                 if (rt_g.NMG_debug & DEBUG_BOOL &&
00649                     rt_g.NMG_debug & DEBUG_PLOTEM) {
00650                         if ((fp=fopen("Unclosed.pl", "w")) != (FILE *)NULL) {
00651                                 bu_log("Plotting unclosed NMG shell\n");
00652                                 nmg_pl_s(fp, sB);
00653                                 fclose(fp);
00654                         }
00655                 }
00656                 if (rt_g.NMG_debug & DEBUG_BOOL)
00657                         nmg_pr_s(sB, "");
00658                 bu_log("nmg_bool: sB is unclosed, barging ahead\n");
00659         }
00660 
00661 
00662         if (rt_g.NMG_debug & DEBUG_BOOL && rt_g.NMG_debug & DEBUG_PLOTEM) {
00663                 if ((fp=fopen("shellA.pl", "w")) == (FILE*)NULL) {
00664                         (void)perror("shellA.pl");
00665                         bu_bomb("unable to open shellA.pl for writing");
00666                 }
00667                 bu_log("plotting shellA.pl\n");
00668                 nmg_pl_s(fp, sA);
00669                 fclose(fp);
00670 
00671                 if ((fp=fopen("shellB.pl", "w")) == (FILE*)NULL) {
00672                         (void)perror("shellB.pl");
00673                         bu_bomb("unable to open shellB.pl for writing");
00674                 }
00675                 bu_log("plotting shellB.pl\n");
00676                 nmg_pl_s(fp, sB);
00677                 fclose(fp);
00678         }
00679 
00680         if( rt_g.NMG_debug & DEBUG_VERIFY )  {
00681 #if 0
00682                 nmg_vshell( &rA->s_hd, rA );
00683                 nmg_vshell( &rB->s_hd, rB );
00684 #else
00685                 /* Sometimes the tessllations of non-participating regions
00686                  * are damaged during a boolean operation.  Check everything.
00687                  */
00688                 nmg_vmodel(m);
00689 #endif
00690 #if VERBOSE_VERIFY
00691                 bu_log("\n==================== Shell A: ====== before crackshells\n");
00692                 nmg_pr_s_briefly(sA, 0);
00693                 bu_log("\n==================== Shell B:\n");
00694                 nmg_pr_s_briefly(sB, 0);
00695                 bu_log("\n====================\n");
00696 #endif
00697         }
00698 
00699 #if 1
00700         if (rt_g.NMG_debug & DEBUG_BOOL)
00701         {
00702                 char file_name[256];
00703 
00704                 sprintf( file_name, "before%d.g", debug_file_count );
00705                 nmg_stash_model_to_file( file_name, m, "Before crackshells" );
00706         }
00707 #endif
00708 
00709         /* Perform shell/shell intersections */
00710         nmg_crackshells(sA, sB, tol);
00711 
00712         if (rt_g.NMG_debug & DEBUG_BOOL)
00713         {
00714                 stash_shell( sA, "a1_", "sA", tol );
00715                 stash_shell( sB, "b1_", "sB", tol );
00716                 bu_log( "Just After Crackshells:\nShell A:\n" );
00717                 nmg_pr_s_briefly( sA, 0 );
00718                 bu_log( "Just After Crackshells:\nShell B:\n" );
00719                 nmg_pr_s_briefly( sB, 0 );
00720         }
00721 
00722         (void)nmg_model_vertex_fuse( m, tol );
00723 
00724         (void)nmg_kill_anti_loops( sA, tol );
00725         (void)nmg_kill_anti_loops( sB, tol );
00726 
00727         nmg_m_reindex( m, 0 );
00728 
00729         /*
00730          *  Allocate storage for classlist[].
00731          *  Get all 8 lists at once, and just build pointers for the rest.
00732          *  XXX In some cases, number of items may grow
00733          *  XXX (e.g. added vu's, loops) as things are demoted, etc.
00734          *  XXX Try to accomodate here by reserving some extra table space.
00735          *
00736          *  XXX The classlist really only needs to be an unsigned char,
00737          *  XXX not a long*.
00738          */
00739         nelem = (m->maxindex)*4+1;              /* includes extra space */
00740         classlist[0] = (long *)bu_calloc( 8 * nelem + 1,
00741                 sizeof(long), "nmg_bool classlist[8]" );
00742         for( i = 1; i < 8; i++ )  {
00743                 classlist[i] = classlist[0] + i * nelem;
00744         }
00745 
00746         nmg_classify_shared_edges_verts( sA, sB, classlist );
00747 
00748         /* clean things up now that the intersections have been built */
00749         nmg_sanitize_s_lv(sA, OT_BOOLPLACE);
00750         nmg_sanitize_s_lv(sB, OT_BOOLPLACE);
00751 
00752         /* Separate any touching loops, so classifier does not have any
00753          * really complex loops to do.
00754          * In particular, it is necessary for (r410) to make
00755          * interior (touching) loop segments into true interior loops
00756          * that are separate from the exterior loop,
00757          * so the classifier can assess each separately.
00758          */
00759         nmg_s_split_touchingloops(sA, tol);
00760         nmg_s_split_touchingloops(sB, tol);
00761 
00762         (void)nmg_kill_cracks( sA );
00763         (void)nmg_kill_cracks( sB );
00764 
00765         /* eliminate unecessary breaks in edges */
00766         (void)nmg_simplify_shell_edges( sA, tol );
00767         (void)nmg_simplify_shell_edges( sB, tol );
00768 
00769         (void)nmg_model_break_e_on_v( m, tol );
00770 
00771         (void)nmg_model_edge_fuse( m , tol );
00772 
00773         if( rt_g.NMG_debug & DEBUG_VERIFY )  {
00774 #if 0
00775                 nmg_vshell( &rA->s_hd, rA );
00776                 nmg_vshell( &rB->s_hd, rB );
00777 #else
00778                 /* Sometimes the tessllations of non-participating regions
00779                  * are damaged during a boolean operation.  Check everything.
00780                  */
00781                 nmg_vmodel(m);
00782 #endif
00783 #if VERBOSE_VERIFY
00784                 bu_log("\n==================== Shell A: ====== before mesh_shell_shell\n");
00785                 nmg_pr_s_briefly(sA, 0);
00786                 bu_log("\n==================== Shell B:\n");
00787                 nmg_pr_s_briefly(sB, 0);
00788                 bu_log("\n====================\n");
00789 #endif
00790                 if( (i = nmg_model_fuse( m, tol )) > 0 )  {
00791                         bu_log("NOTICE: nmg_bool: fused %d entities while cracking shells\n", i);
00792                         rt_bomb("nmg_bool() entities unfused after nmg_crackshells()\n");
00793                 }
00794         }
00795 #if 1
00796         /* Temporary search */
00797         if( nmg_has_dangling_faces( (long *)rA, (char *)NULL ) )
00798                 bu_log("Dangling faces detected in rA before classification\n");
00799         if( nmg_has_dangling_faces( (long *)rB, (char *)NULL ) )
00800                 bu_log("Dangling faces detected in rB before classification\n");
00801         if( nmg_has_dangling_faces( (long *)m, (char *)NULL ) )
00802                 bu_log("Dangling faces detected in model before classification\n");
00803 #endif
00804 
00805         if( rt_g.NMG_debug & DEBUG_VERIFY )  {
00806 #if 0
00807                 nmg_vshell( &rA->s_hd, rA );
00808                 nmg_vshell( &rB->s_hd, rB );
00809 #else
00810                 /* Sometimes the tessllations of non-participating regions
00811                  * are damaged during a boolean operation.  Check everything.
00812                  */
00813                 nmg_vmodel(m);
00814 #endif
00815 #if VERBOSE_VERIFY
00816                 bu_log("\n==================== Shell A: ====== after mesh_shell_shell\n");
00817                 nmg_pr_s_briefly(sA, 0);
00818                 bu_log("\n==================== Shell B:\n");
00819                 nmg_pr_s_briefly(sB, 0);
00820                 bu_log("\n====================\n");
00821 #endif
00822         }
00823 #if 0
00824 nmg_s_radial_check( sA, tol );
00825 nmg_s_radial_check( sB, tol );
00826 #endif
00827 
00828         /*
00829          *  Before splitting, join up small loop fragments into large
00830          *  ones, so that maximal splits will be possible.
00831          *  This is essential for cutting holes in faces, e.g. Test3.r
00832          */
00833         if (rt_g.NMG_debug & DEBUG_BOOL)
00834         {
00835                 char file_name[256];
00836 
00837                 sprintf( file_name, "notjoined%d.g", debug_file_count );
00838                 nmg_stash_model_to_file( file_name, m, "Before s_join_touchingloops" );
00839         }
00840 #if 0
00841         nmg_s_join_touchingloops(sA, tol);
00842         nmg_s_join_touchingloops(sB, tol);
00843         if (rt_g.NMG_debug & DEBUG_BOOL)
00844         {
00845                 char file_name[256];
00846 
00847                 sprintf( file_name, "joined%d.g", debug_file_count );
00848                 nmg_stash_model_to_file( file_name, m, "After s_join_touchingloops" );
00849         }
00850 #endif
00851 
00852         /* Re-build bounding boxes, edge geometry, as needed. */
00853         nmg_shell_a(sA, tol);
00854         nmg_shell_a(sB, tol);
00855 
00856         if (rt_g.NMG_debug & DEBUG_BOOL)
00857         {
00858                 stash_shell( sA, "a", "sA", tol );
00859                 stash_shell( sB, "b", "sB", tol );
00860 
00861                 bu_log( "sA:\n" );
00862                 nmg_pr_s_briefly( sA, 0 );
00863                 bu_log( "sB:\n" );
00864                 nmg_pr_s_briefly( sB, 0 );
00865         }
00866 
00867         if (rt_g.NMG_debug & DEBUG_BOOL)
00868         {
00869                 char file_name[256];
00870 
00871                 sprintf( file_name, "after%d.g", debug_file_count );
00872                 nmg_stash_model_to_file( file_name, m, "After crackshells" );
00873         }
00874 
00875         if (rt_g.NMG_debug & DEBUG_BOOL) {
00876                 if (rt_g.NMG_debug & DEBUG_PLOTEM) {
00877                         if ((fd = fopen("Cracked_Shells.pl", "w")) == (FILE *)NULL) {
00878                                 (void)perror("Cracked_Shells");
00879                                 bu_bomb("unable to open Cracked_Shells.pl for writing");
00880                         }
00881                         bu_log("plotting Cracked_Shells.pl\n");
00882 
00883                         nmg_pl_s(fd, sA);
00884                         nmg_pl_s(fd, sB);
00885                         (void)fclose(fd);
00886 
00887                         nmg_pl_isect("isectA.pl", sA, tol);
00888                         nmg_pl_isect("isectB.pl", sB, tol);
00889                 }
00890 
00891                 bu_log("check 2\n");
00892         }
00893 
00894         if (nmg_ck_closed_surf(sA, tol))
00895                 bu_log("nmg_bool() WARNING: sA unclosed before classification.  Boldly pressing on.\n");
00896         if (nmg_ck_closed_surf(sB, tol))
00897                 bu_log("nmg_bool() WARNING: sB unclosed before classification.  Boldly pressing on.\n");
00898 
00899         if( rt_g.NMG_debug & DEBUG_VERIFY )  {
00900 #if 0
00901                 nmg_vshell( &rA->s_hd, rA );
00902                 nmg_vshell( &rB->s_hd, rB );
00903 #else
00904                 /* Sometimes the tessllations of non-participating regions
00905                  * are damaged during a boolean operation.  Check everything.
00906                  */
00907                 nmg_vmodel(m);
00908 #endif
00909         }
00910 
00911 #if 0
00912         /* Reindex structures before classification or evaluation. */
00913         nmg_m_reindex( m, 0 );
00914 
00915         /*
00916          *  Allocate storage for classlist[].
00917          *  Get all 8 lists at once, and just build pointers for the rest.
00918          *  XXX In some cases, number of items may grow
00919          *  XXX (e.g. added vu's, loops) as things are demoted, etc.
00920          *  XXX Try to accomodate here by reserving some extra table space.
00921          *
00922          *  XXX The classlist really only needs to be an unsigned char,
00923          *  XXX not a long*.
00924          */
00925         nelem = (m->maxindex)*4+1;              /* includes extra space */
00926         classlist[0] = (long *)bu_calloc( 8 * nelem + 1,
00927                 sizeof(long), "nmg_bool classlist[8]" );
00928         for( i = 1; i < 8; i++ )  {
00929                 classlist[i] = classlist[0] + i * nelem;
00930         }
00931 #endif
00932         nmg_class_nothing_broken = 1;
00933         if (rt_g.NMG_debug & (DEBUG_GRAPHCL|DEBUG_PL_LOOP)) {
00934                 nmg_show_broken_classifier_stuff((long *)sA, &classlist[0],
00935                         nmg_class_nothing_broken, 1, "unclassed sA");
00936                 nmg_show_broken_classifier_stuff((long *)sB, &classlist[4], 1, 1, "unclassed sB");
00937         }
00938 
00939         /*
00940          *  Classify A -vs- B, then B -vs- A.
00941          *  Carry onAonBshared and onAonBanti classifications forward
00942          *  from first step to second step.
00943          *  A -vs- B live in classlist[0..3], B -vs- A live in classlist[4..7].
00944          */
00945         nmg_class_shells(sA, sB, &classlist[0], tol);
00946         memcpy( (char *)classlist[4+NMG_CLASS_AonBshared],
00947                 (char *)classlist[0+NMG_CLASS_AonBshared],
00948                 nelem*sizeof(long) );
00949         memcpy( (char *)classlist[4+NMG_CLASS_AonBanti],
00950                 (char *)classlist[0+NMG_CLASS_AonBanti],
00951                 nelem*sizeof(long) );
00952         nmg_class_shells(sB, sA, &classlist[4], tol);
00953 
00954         if (rt_g.NMG_debug & (DEBUG_GRAPHCL|DEBUG_PL_LOOP)) {
00955                 nmg_class_nothing_broken = 1;
00956 
00957                 /* Show each loop, one at a time, non-fancy */
00958                 /* XXX Should have it's own bit, or combination -- not always wanted */
00959                 nmg_show_each_loop(sA, &classlist[0], 1, 0, "sA lu");
00960                 nmg_show_each_loop(sB, &classlist[4], 1, 0, "sB lu");
00961 
00962                 /* Show each shell as a whole */
00963                 nmg_show_broken_classifier_stuff((long *)sA, &classlist[0], 1, 0, "sA classed");
00964                 nmg_show_broken_classifier_stuff((long *)sB, &classlist[4], 1, 0, "sB classed");
00965         }
00966 #if 1
00967         if( rt_g.NMG_debug & DEBUG_BOOL )
00968         {
00969                 bu_log( "Just before nmg_evaluate_boolean:\nShell A:\n" );
00970                 nmg_pr_s_briefly( sA , 0 );
00971                 bu_log( "Shell B:\n" );
00972                 nmg_pr_s_briefly( sB , 0 );
00973         }
00974 #endif
00975 nmg_s_radial_check( sA, tol );
00976 nmg_s_radial_check( sB, tol );
00977         nmg_evaluate_boolean( sA, sB, oper, classlist, tol);
00978 
00979 #if 1
00980         if( rt_g.NMG_debug & DEBUG_BOOL )
00981         {
00982                 bu_log( "Just after nmg_evaluate_boolean:\nShell A:\n" );
00983                 nmg_pr_s_briefly( sA , 0 );
00984                 bu_log( "Shell B:\n" );
00985                 nmg_pr_s_briefly( sB , 0 );
00986         }
00987 #endif
00988 
00989         if( rt_g.NMG_debug & DEBUG_VERIFY )  {
00990                 nmg_vmodel(m);
00991                 if( (i = nmg_model_fuse( m, tol )) > 0 )  {
00992                         bu_log("ERROR: nmg_bool: fused %d entities after BOOLEAN.  Isect bug.\n", i);
00993                         rt_bomb("nmg_bool() entities unfused after nmg_evaluate_boolean()\n");
00994                 }
00995         }
00996 
00997         /*
00998          *  nmg_evaluate_boolean() may return an invalid shell,
00999          *  i.e., one that has absolutely nothing in it.
01000          *  This is an indication that the shell should be deleted
01001          *  from the region, an operation which can not be accomplished
01002          *  this far down in the subroutine tree.
01003          */
01004         if( !nmg_shell_is_empty(sA) )  {
01005 nmg_s_radial_check( sA, tol );
01006                 /* Temporary search */
01007                 if( nmg_has_dangling_faces( (long *)rA, (char *)NULL ) )
01008                 bu_log("Dangling faces detected in rA after boolean\n");
01009                 if( nmg_has_dangling_faces( (long *)rB, (char *)NULL ) )
01010                         bu_log("Dangling faces detected in rB after boolean\n");
01011                 if( nmg_has_dangling_faces( (long *)m, (char *)NULL ) )  {
01012                         if(rt_g.NMG_debug)
01013                                 nmg_stash_model_to_file( "dangle.g", m, "After Boolean" );
01014                         rt_bomb("nmg_bool() Dangling faces detected after boolean\n");
01015                 }
01016 
01017                 /* Do this before table size changes */
01018                 if (rt_g.NMG_debug & (DEBUG_GRAPHCL|DEBUG_PL_LOOP)) {
01019                         nmg_class_nothing_broken = 1;
01020 
01021                         /* Show final result of the boolean */
01022                         nmg_show_broken_classifier_stuff((long *)sA, &classlist[0], 1, 0, "sA result");
01023                 }
01024 
01025                 /*  Go back and combine loops
01026                  *  of faces together wherever possible
01027                  *  to reduce the loop/edge count.
01028                  */
01029                 nmg_simplify_shell(sA);
01030                 if( rt_g.NMG_debug & DEBUG_VERIFY )
01031                         nmg_vshell( &rA->s_hd, rA );
01032 
01033                 (void) nmg_unbreak_region_edges( &sA->l.magic );
01034 
01035 #if 1
01036         if( rt_g.NMG_debug & DEBUG_BOOL )
01037         {
01038                 bu_log( "Just after nmg_simplify_shell:\nShell A:\n" );
01039                 nmg_pr_s_briefly( sA , 0 );
01040                 bu_log( "Shell B:\n" );
01041                 nmg_pr_s_briefly( sB , 0 );
01042         }
01043 #endif
01044                 /* Bounding boxes may have changed */
01045                 nmg_shell_a(sA, tol);
01046 
01047                 if( nmg_ck_closed_surf(sA, tol) )  {
01048                         if( rt_g.NMG_debug )
01049                                 bu_log("nmg_bool() WARNING: sA unclosed at return, barging on.\n");
01050                         else
01051                                 rt_bomb("nmg_bool() sA unclosed at return, aborting.\n");
01052                 }
01053 nmg_s_radial_check( sA, tol );
01054 
01055                 if( rt_g.NMG_debug & DEBUG_BOOL )
01056                 {
01057                         char tmp_name[256];
01058                         sprintf( tmp_name, "after_bool_%d.g", debug_file_count );
01059                         nmg_stash_model_to_file( tmp_name, m, "After Boolean" );
01060                 }
01061         }
01062 
01063         bu_free( (char *)classlist[0], "nmg_bool classlist[8]" );
01064 
01065         if (rt_g.NMG_debug & DEBUG_BOOL) {
01066                 bu_log("Returning from NMG_BOOL\n");
01067         }
01068         if( rt_g.NMG_debug & DEBUG_VERIFY )  {
01069 #if 0
01070                 nmg_vshell( &rA->s_hd, rA );
01071 #else
01072                 /* Sometimes the tessllations of non-participating regions
01073                  * are damaged during a boolean operation.  Check everything.
01074                  */
01075                 nmg_vmodel(m);
01076 #endif
01077         }
01078 
01079         nmg_kill_wire_edges( sA );
01080 
01081         return(sA);
01082 }
01083 
01084 /*
01085  *                      N M G _ D O _ B O O L
01086  *
01087  *      BUG: we assume only one shell per region
01088  */
01089 struct nmgregion *
01090 nmg_do_bool(struct nmgregion *rA, struct nmgregion *rB, const int oper, const struct bn_tol *tol)
01091 {
01092         struct shell            *s;
01093         struct nmgregion        *r;
01094 
01095         NMG_CK_REGION(rA);
01096         NMG_CK_REGION(rB);
01097 
01098 #if 1
01099 nmg_region_v_unique( rA, tol );
01100 nmg_region_v_unique( rB, tol );
01101 #endif
01102 
01103         s = nmg_bool(BU_LIST_FIRST(shell, &rA->s_hd),
01104                 BU_LIST_FIRST(shell, &rB->s_hd),
01105                 oper, tol);
01106         r = s->r_p;
01107 
01108         /* shell B was destroyed, need to eliminate region B */
01109         nmg_kr( rB );
01110 
01111         NMG_CK_SHELL(s);
01112         NMG_CK_REGION(r);
01113 
01114         /* If shell A became empty, eliminate it from the returned region */
01115         if( nmg_shell_is_empty(s) )
01116         {
01117                 nmg_ks(s);
01118                 if( BU_LIST_NON_EMPTY( &r->s_hd ) )
01119                 {
01120                         rt_bomb( "nmg_do_bool: Result of Boolean is an empty shell, but region is not empty!!!\n" );
01121                 }
01122                 nmg_kr( r );
01123                 return( (struct nmgregion *)NULL );
01124         }
01125 
01126         return(r);
01127 }
01128 
01129 /* XXX move to ??? Everything from here on down needs to go into another module */
01130 
01131 
01132 /**
01133  *                      N M G _ B O O L T R E E _ L E A F _ T E S S
01134  *
01135  *  Called from db_walk_tree() each time a tree leaf is encountered.
01136  *  The primitive solid, in external format, is provided in 'ep',
01137  *  and the type of that solid (e.g. ID_ELL) is in 'id'.
01138  *  The full tree state including the accumulated transformation matrix
01139  *  and the current tolerancing is in 'tsp',
01140  *  and the full path from root to leaf is in 'pathp'.
01141  *
01142  *  Import the solid, tessellate it into an NMG, stash a pointer to
01143  *  the tessellation in a new tree structure (union), and return a
01144  *  pointer to that.
01145  *
01146  *  Usually given as an argument to, and called from db_walk_tree().
01147  *
01148  *  This routine must be prepared to run in parallel.
01149  */
01150 union tree *
01151 nmg_booltree_leaf_tess(struct db_tree_state *tsp, struct db_full_path *pathp, struct rt_db_internal *ip, genptr_t client_data)
01152 {
01153         struct model            *m;
01154         struct nmgregion        *r1;
01155         union tree              *curtree;
01156         struct directory        *dp;
01157 
01158         NMG_CK_MODEL(*tsp->ts_m);
01159         BN_CK_TOL(tsp->ts_tol);
01160         RT_CK_TESS_TOL(tsp->ts_ttol);
01161         RT_CK_DB_INTERNAL(ip);
01162         RT_CK_RESOURCE(tsp->ts_resp);
01163 
01164         RT_CK_FULL_PATH(pathp);
01165         dp = DB_FULL_PATH_CUR_DIR(pathp);
01166         RT_CK_DIR(dp);
01167 
01168         m = nmg_mm();
01169 
01170         if (ip->idb_meth->ft_tessellate(
01171             &r1, m, ip, tsp->ts_ttol, tsp->ts_tol) < 0) {
01172                 bu_log("nmg_booltree_leaf_tess(%s): tessellation failure\n", dp->d_namep);
01173                 return(TREE_NULL);
01174         }
01175 
01176         NMG_CK_REGION(r1);
01177         if( rt_g.NMG_debug & DEBUG_VERIFY )  {
01178                 nmg_vshell( &r1->s_hd, r1 );
01179         }
01180 
01181         RT_GET_TREE( curtree, tsp->ts_resp );
01182         curtree->magic = RT_TREE_MAGIC;
01183         curtree->tr_op = OP_NMG_TESS;
01184         curtree->tr_d.td_name = bu_strdup(dp->d_namep);
01185         curtree->tr_d.td_r = r1;
01186 
01187         if (RT_G_DEBUG&DEBUG_TREEWALK)
01188                 bu_log("nmg_booltree_leaf_tess(%s) OK\n", dp->d_namep);
01189 
01190         return(curtree);
01191 }
01192 
01193 /**
01194  *                      N M G _ B O O L T R E E _ L E A F _ T N U R B
01195  *
01196  *  Called from db_walk_tree() each time a tree leaf is encountered.
01197  *  The primitive solid, in external format, is provided in 'ep',
01198  *  and the type of that solid (e.g. ID_ELL) is in 'id'.
01199  *  The full tree state including the accumulated transformation matrix
01200  *  and the current tolerancing is in 'tsp',
01201  *  and the full path from root to leaf is in 'pathp'.
01202  *
01203  *  Import the solid, convert it into an NMG using t-NURBS,
01204  *  stash a pointer in a new tree structure (union), and return a
01205  *  pointer to that.
01206  *
01207  *  Usually given as an argument to, and called from db_walk_tree().
01208  *
01209  *  This routine must be prepared to run in parallel.
01210  */
01211 union tree *
01212 nmg_booltree_leaf_tnurb(struct db_tree_state *tsp, struct db_full_path *pathp, struct rt_db_internal *ip, genptr_t client_data)
01213 {
01214         struct nmgregion        *r1;
01215         union tree              *curtree;
01216         struct directory        *dp;
01217 
01218         NMG_CK_MODEL(*tsp->ts_m);
01219         BN_CK_TOL(tsp->ts_tol);
01220         RT_CK_TESS_TOL(tsp->ts_ttol);
01221         RT_CK_DB_INTERNAL(ip);
01222         RT_CK_RESOURCE(tsp->ts_resp);
01223 
01224         RT_CK_FULL_PATH(pathp);
01225         dp = DB_FULL_PATH_CUR_DIR(pathp);
01226         RT_CK_DIR(dp);
01227 
01228         if (ip->idb_meth->ft_tnurb(
01229             &r1, *tsp->ts_m, ip, tsp->ts_tol) < 0) {
01230                 bu_log("nmg_booltree_leaf_tnurb(%s): CSG to t-NURB conversation failure\n", dp->d_namep);
01231                 return(TREE_NULL);
01232         }
01233 
01234         NMG_CK_REGION(r1);
01235         if( rt_g.NMG_debug & DEBUG_VERIFY )  {
01236                 nmg_vshell( &r1->s_hd, r1 );
01237         }
01238 
01239         RT_GET_TREE( curtree, tsp->ts_resp );
01240         curtree->magic = RT_TREE_MAGIC;
01241         curtree->tr_op = OP_NMG_TESS;
01242         curtree->tr_d.td_name = bu_strdup(dp->d_namep);
01243         curtree->tr_d.td_r = r1;
01244 
01245         if (RT_G_DEBUG&DEBUG_TREEWALK)
01246                 bu_log("nmg_booltree_leaf_tnurb(%s) OK\n", dp->d_namep);
01247 
01248         return(curtree);
01249 }
01250 
01251 /**
01252  *                      N M G _ B O O L T R E E _ E V A L U A T E
01253  *
01254  *  Given a tree of leaf nodes tesselated earlier by nmg_booltree_leaf_tess(),
01255  *  use recursion to do a depth-first traversal of the tree,
01256  *  evaluating each pair of boolean operations
01257  *  and reducing that result to a single nmgregion.
01258  *
01259  *  Usually called from a do_region_end() handler from db_walk_tree().
01260  *  For an example of several, see mged/dodraw.c.
01261  *
01262  *  Returns an OP_NMG_TESS union tree node, which will contain the
01263  *  resulting region and it's name, as a dynamic string.
01264  *  The caller is responsible for releasing the string, and the node,
01265  *  by calling db_free_tree() on the node.
01266  *
01267  *  It is *essential* that the caller call nmg_model_fuse() before
01268  *  calling this subroutine.
01269  *
01270  *  Returns NULL if there is no geometry to return.
01271  *
01272  *  Typical calls will be of this form:
01273  *      (void)nmg_model_fuse( m, tol );
01274  *      curtree = nmg_booltree_evaluate( curtree, tol );
01275  */
01276 union tree *
01277 nmg_booltree_evaluate(register union tree *tp, const struct bn_tol *tol, struct resource *resp)
01278 {
01279         union tree              *tl;
01280         union tree              *tr;
01281         struct nmgregion        *reg;
01282         int                     op;
01283         const char              *op_str;
01284         char                    *name;
01285 
01286         RT_CK_TREE(tp);
01287         BN_CK_TOL(tol);
01288         RT_CK_RESOURCE(resp);
01289 
01290         switch(tp->tr_op) {
01291         case OP_NOP:
01292                 return(0);
01293         case OP_NMG_TESS:
01294                 /* Hit a tree leaf */
01295                 if( rt_g.NMG_debug & DEBUG_VERIFY )  {
01296                         nmg_vshell( &tp->tr_d.td_r->s_hd, tp->tr_d.td_r );
01297                 }
01298                 return tp;
01299         case OP_UNION:
01300                 op = NMG_BOOL_ADD;
01301                 op_str = " u ";
01302                 break;
01303         case OP_INTERSECT:
01304                 op = NMG_BOOL_ISECT;
01305                 op_str = " + ";
01306                 break;
01307         case OP_SUBTRACT:
01308                 op = NMG_BOOL_SUB;
01309                 op_str = " - ";
01310                 break;
01311         default:
01312                 bu_log("nmg_booltree_evaluate: bad op %d\n", tp->tr_op);
01313                 return(0);
01314         }
01315         /* Handle a boolean operation node.  First get it's leaves. */
01316         tl = nmg_booltree_evaluate(tp->tr_b.tb_left, tol, resp);
01317         tr = nmg_booltree_evaluate(tp->tr_b.tb_right, tol, resp);
01318         if (tl == 0 || !tl->tr_d.td_r) {
01319                 if (tr == 0 || !tr->tr_d.td_r)
01320                         return 0;
01321                 if( op == NMG_BOOL_ADD )
01322                         return tr;
01323                 /* For sub and intersect, if lhs is 0, result is null */
01324                 db_free_tree(tr, resp);
01325                 tp->tr_b.tb_right = TREE_NULL;
01326                 tp->tr_op = OP_NOP;
01327                 return 0;
01328         }
01329         if (tr == 0 || !tr->tr_d.td_r) {
01330                 if (tl == 0 || !tl->tr_d.td_r)
01331                         return 0;
01332                 if( op == NMG_BOOL_ISECT )  {
01333                         db_free_tree(tl, resp);
01334                         tp->tr_b.tb_left = TREE_NULL;
01335                         tp->tr_op = OP_NOP;
01336                         return 0;
01337                 }
01338                 /* For sub and add, if rhs is 0, result is lhs */
01339                 return tl;
01340         }
01341         if( tl->tr_op != OP_NMG_TESS )  rt_bomb("nmg_booltree_evaluate() bad left tree\n");
01342         if( tr->tr_op != OP_NMG_TESS )  rt_bomb("nmg_booltree_evaluate() bad right tree\n");
01343 
01344 bu_log(" {%s}%s{%s}\n", tl->tr_d.td_name, op_str, tr->tr_d.td_name );
01345 
01346         NMG_CK_REGION(tr->tr_d.td_r);
01347         NMG_CK_REGION(tl->tr_d.td_r);
01348         if (nmg_ck_closed_region(tr->tr_d.td_r, tol) != 0)
01349                 bu_log("nmg_booltree_evaluate:  WARNING, non-closed shell (r), barging ahead\n");
01350         if (nmg_ck_closed_region(tl->tr_d.td_r, tol) != 0)
01351                 bu_log("nmg_booltree_evaluate:  WARNING, non-closed shell (l), barging ahead\n");
01352 nmg_r_radial_check( tr->tr_d.td_r, tol );
01353 nmg_r_radial_check( tl->tr_d.td_r, tol );
01354 
01355         if( rt_g.NMG_debug & DEBUG_BOOL )
01356         {
01357                 bu_log( "Before model fuse\nShell A:\n" );
01358                 nmg_pr_s_briefly( BU_LIST_FIRST( shell, &tl->tr_d.td_r->s_hd ), "" );
01359                 bu_log( "Shell B:\n" );
01360                 nmg_pr_s_briefly( BU_LIST_FIRST( shell, &tr->tr_d.td_r->s_hd ), "" );
01361         }
01362 
01363         /* move operands into the same model */
01364         if( tr->tr_d.td_r->m_p != tl->tr_d.td_r->m_p )
01365                 nmg_merge_models( tl->tr_d.td_r->m_p, tr->tr_d.td_r->m_p );
01366 
01367         /* input r1 and r2 are destroyed, output is new region */
01368         reg = nmg_do_bool(tl->tr_d.td_r, tr->tr_d.td_r, op, tol);
01369         tl->tr_d.td_r = NULL;
01370         tr->tr_d.td_r = NULL;
01371         if( reg )
01372         {
01373                 NMG_CK_REGION(reg);
01374                 nmg_r_radial_check( reg, tol );
01375 
01376                 if( rt_g.NMG_debug & DEBUG_VERIFY )  {
01377                         nmg_vshell( &reg->s_hd, reg );
01378                 }
01379         }
01380 
01381         /* Build string of result name */
01382         name = (char *)bu_malloc( strlen(tl->tr_d.td_name)+3+strlen(tr->tr_d.td_name)+2+1,
01383                 "nmg_booltree_evaluate name");
01384         name[0] = '(';
01385         strcpy( name+1, tl->tr_d.td_name );
01386         strcat( name+1, op_str );
01387         strcat( name+1, tr->tr_d.td_name );
01388         strcat( name+1, ")" );
01389 
01390         /* Clean up child tree nodes (and their names) */
01391         db_free_tree(tl, resp);
01392         db_free_tree(tr, resp);
01393 
01394         /* Convert argument binary node into a result node */
01395         tp->tr_op = OP_NMG_TESS;
01396         tp->tr_d.td_r = reg;
01397         tp->tr_d.td_name = name;
01398         return tp;
01399 }
01400 
01401 /**
01402  *                      N M G _ B O O L E A N
01403  *
01404  *  This is the main application interface to the NMG Boolean Evaluator.
01405  *
01406  *  This routine has the opportunity to do "once only" operations
01407  *  before and after the boolean tree is walked.
01408  *
01409  *  Returns -
01410  *      0       Boolean went OK.  Result region is in tp->tr_d.td_r
01411  *      !0      Boolean produced null result.
01412  *
01413  *  The caller is responsible for freeing 'tp' in either case,
01414  *  typically with db_free_tree(tp);
01415  */
01416 int
01417 nmg_boolean( union tree *tp, struct model *m, const struct bn_tol *tol, struct resource *resp )
01418 {
01419         union tree      *result;
01420         int             ret;
01421 
01422         RT_CK_TREE(tp);
01423         NMG_CK_MODEL(m);
01424         BN_CK_TOL(tol);
01425         RT_CK_RESOURCE(resp);
01426 
01427         if (rt_g.NMG_debug & (DEBUG_BOOL|DEBUG_BASIC) )  {
01428                 bu_log("\n\nnmg_boolean( tp=x%x, m=x%x ) START\n",
01429                         tp, m );
01430         }
01431 
01432         /*
01433          *  Find all entities within geometric tolerance of each other
01434          *  and "fuse" them, establishing topological sharing.
01435          *  Also breaks all edges on all vertices that are within tolerance.
01436          *  Operate on the entire model at once.
01437          */
01438         (void)nmg_model_fuse( m, tol );
01439 
01440         /*
01441          *  Evaluate the nodes of the boolean tree one at a time,
01442          *  until only a single region remains.
01443          */
01444         result = nmg_booltree_evaluate( tp, tol, resp );
01445         RT_CK_TREE( result );
01446         if( result != tp )  rt_bomb("nmg_boolean() result of nmg_booltree_evaluate() isn't tp\n");
01447         if( tp->tr_op != OP_NMG_TESS )  {
01448                 bu_log("nmg_boolean() result of nmg_booltree_evaluate() op != OP_NMG_TESS\n");
01449                 rt_pr_tree( tp, 0 );
01450                 ret = 1;
01451                 goto out;
01452         }
01453         if( tp->tr_d.td_r == (struct nmgregion *)NULL )  {
01454                 /* Pointers are all OK, but boolean produced null set */
01455                 ret = 1;
01456                 goto out;
01457         }
01458         /* move result into correct model */
01459         nmg_merge_models( m, tp->tr_d.td_r->m_p );
01460         ret = 0;
01461 
01462 out:
01463         if (rt_g.NMG_debug & (DEBUG_BOOL|DEBUG_BASIC) )  {
01464                 bu_log("nmg_boolean( tp=x%x, m=x%x ) END, ret=%d\n\n",
01465                         tp, m, ret );
01466         }
01467         return ret;
01468 }
01469 /*@}*/
01470 
01471 /*
01472  * Local Variables:
01473  * mode: C
01474  * tab-width: 8
01475  * c-basic-offset: 4
01476  * indent-tabs-mode: t
01477  * End:
01478  * ex: shiftwidth=4 tabstop=8
01479  */

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