00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
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
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
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
00088
00089
00090
00091
00092
00093
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
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
00121
00122
00123
00124
00125
00126
00127 void
00128 nmg_show_each_loop(struct shell *s, long int **classlist, int new, int fancy, const char *str)
00129
00130
00131
00132
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
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;
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
00241 if( eu->vu_p->v_p != eu_next->eumate_p->vu_p->v_p )
00242 continue;
00243
00244
00245
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
00320 if( eu->vu_p->v_p != eu_next->eumate_p->vu_p->v_p )
00321 continue;
00322
00323
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
00359
00360
00361
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
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
00441
00442
00443
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
00504 vu2 = nmg_find_vertex_in_lu( v1, lu2 );
00505
00506 if( !vu2 )
00507 continue;
00508
00509
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
00576
00577
00578
00579
00580
00581
00582
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
00610
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
00686
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
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
00731
00732
00733
00734
00735
00736
00737
00738
00739 nelem = (m->maxindex)*4+1;
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
00749 nmg_sanitize_s_lv(sA, OT_BOOLPLACE);
00750 nmg_sanitize_s_lv(sB, OT_BOOLPLACE);
00751
00752
00753
00754
00755
00756
00757
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
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
00779
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
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
00811
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
00830
00831
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
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
00905
00906
00907 nmg_vmodel(m);
00908 #endif
00909 }
00910
00911 #if 0
00912
00913 nmg_m_reindex( m, 0 );
00914
00915
00916
00917
00918
00919
00920
00921
00922
00923
00924
00925 nelem = (m->maxindex)*4+1;
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
00941
00942
00943
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
00958
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
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
00999
01000
01001
01002
01003
01004 if( !nmg_shell_is_empty(sA) ) {
01005 nmg_s_radial_check( sA, tol );
01006
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
01018 if (rt_g.NMG_debug & (DEBUG_GRAPHCL|DEBUG_PL_LOOP)) {
01019 nmg_class_nothing_broken = 1;
01020
01021
01022 nmg_show_broken_classifier_stuff((long *)sA, &classlist[0], 1, 0, "sA result");
01023 }
01024
01025
01026
01027
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
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
01073
01074
01075 nmg_vmodel(m);
01076 #endif
01077 }
01078
01079 nmg_kill_wire_edges( sA );
01080
01081 return(sA);
01082 }
01083
01084
01085
01086
01087
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
01109 nmg_kr( rB );
01110
01111 NMG_CK_SHELL(s);
01112 NMG_CK_REGION(r);
01113
01114
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
01130
01131
01132
01133
01134
01135
01136
01137
01138
01139
01140
01141
01142
01143
01144
01145
01146
01147
01148
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
01195
01196
01197
01198
01199
01200
01201
01202
01203
01204
01205
01206
01207
01208
01209
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
01253
01254
01255
01256
01257
01258
01259
01260
01261
01262
01263
01264
01265
01266
01267
01268
01269
01270
01271
01272
01273
01274
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
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
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
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
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
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
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( ®->s_hd, reg );
01378 }
01379 }
01380
01381
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
01391 db_free_tree(tl, resp);
01392 db_free_tree(tr, resp);
01393
01394
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
01403
01404
01405
01406
01407
01408
01409
01410
01411
01412
01413
01414
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
01434
01435
01436
01437
01438 (void)nmg_model_fuse( m, tol );
01439
01440
01441
01442
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
01455 ret = 1;
01456 goto out;
01457 }
01458
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
01473
01474
01475
01476
01477
01478
01479