nmg_fuse.c

Go to the documentation of this file.
00001 /*                      N M G _ F U S E . C
00002  * BRL-CAD
00003  *
00004  * Copyright (c) 1993-2006 United States Government as represented by
00005  * the U.S. Army Research Laboratory.
00006  *
00007  * This library is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public License
00009  * as published by the Free Software Foundation; either version 2 of
00010  * the License, or (at your option) any later version.
00011  *
00012  * This library is distributed in the hope that it will be useful, but
00013  * WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Library General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with this file; see the file named COPYING for more
00019  * information.
00020  */
00021 
00022 /** @addtogroup nmg */
00023 
00024 /*@{*/
00025 /** @file nmg_fuse.c
00026  *  Routines to "fuse" entities together that are geometrically identical
00027  *  (within tolerance) into entities that share underlying geometry
00028  *  structures, so that the relationship is explicit.
00029  *
00030  *  Authors -
00031  *      Michael John Muuss
00032  *      John R Anderson
00033  *
00034  *  Source -
00035  *      The U. S. Army Research Laboratory
00036  *      Aberdeen Proving Ground, Maryland  21005-5068  USA
00037  */
00038 /*@}*/
00039 
00040 #ifndef lint
00041 static const char RCSid[] = "@(#)$Header: /cvsroot/brlcad/brlcad/src/librt/nmg_fuse.c,v 14.15 2006/09/16 02:04:25 lbutler Exp $ (ARL)";
00042 #endif
00043 
00044 #include "common.h"
00045 
00046 #include <stddef.h>
00047 #include <stdio.h>
00048 #ifdef HAVE_STRING_H
00049 #  include <string.h>
00050 #else
00051 #  include <strings.h>
00052 #endif
00053 #include <math.h>
00054 
00055 #include "machine.h"
00056 #include "vmath.h"
00057 #include "nmg.h"
00058 #include "raytrace.h"
00059 #include "nurb.h"
00060 #include "./debug.h"
00061 
00062 extern int debug_file_count;
00063 
00064 
00065 struct pt_list
00066 {
00067         struct bu_list l;
00068         point_t xyz;
00069         fastf_t t;
00070 };
00071 
00072 BU_EXTERN(void                  nmg_split_trim,
00073                                 (const struct edge_g_cnurb *cnrb,
00074                                 const struct face_g_snurb *snrb,
00075                                 fastf_t t,
00076                                 struct pt_list *pt0, struct pt_list *pt1,
00077                                 const struct bn_tol *tol));
00078 
00079 /**
00080  *                      N M G _ I S _ C O M M O N _ B I G L O O P
00081  *
00082  *  Do two faces share by topology at least one loop of 3 or more vertices?
00083  *
00084  *  Require that at least three distinct edge geometries be involved.
00085  *
00086  * XXX Won't catch sharing of faces with only self-loops and no edge loops.
00087  */
00088 int
00089 nmg_is_common_bigloop(const struct face *f1, const struct face *f2)
00090 {
00091         const struct faceuse    *fu1;
00092         const struct loopuse    *lu1;
00093         const struct edgeuse    *eu1;
00094         const long              *magic1 = (long *)NULL;
00095         const long              *magic2 = (long *)NULL;
00096         int     nverts;
00097         int     nbadv;
00098         int     got_three;
00099 
00100         NMG_CK_FACE(f1);
00101         NMG_CK_FACE(f2);
00102 
00103         fu1 = f1->fu_p;
00104         NMG_CK_FACEUSE(fu1);
00105 
00106         /* For all loopuses in fu1 */
00107         for( BU_LIST_FOR( lu1, loopuse, &fu1->lu_hd ) )  {
00108                 if( BU_LIST_FIRST_MAGIC(&lu1->down_hd) == NMG_VERTEXUSE_MAGIC )
00109                         continue;
00110                 nverts = 0;
00111                 nbadv = 0;
00112                 magic1 = (long *)NULL;
00113                 magic2 = (long *)NULL;
00114                 got_three = 0;
00115                 for( BU_LIST_FOR( eu1, edgeuse, &lu1->down_hd ) )  {
00116                         nverts++;
00117                         NMG_CK_EDGE_G_LSEG(eu1->g.lseg_p);
00118                         if( !magic1 )  {
00119                                 magic1 = eu1->g.magic_p;
00120                         } else if( !magic2 )  {
00121                                 if( eu1->g.magic_p != magic1 )  {
00122                                         magic2 = eu1->g.magic_p;
00123                                 }
00124                         } else {
00125                                 if( eu1->g.magic_p != magic1 &&
00126                                     eu1->g.magic_p != magic2 )  {
00127                                         got_three = 1;
00128                                 }
00129                         }
00130                         if( nmg_is_vertex_in_face( eu1->vu_p->v_p, f2 ) )
00131                                 continue;
00132                         nbadv++;
00133                         break;
00134                 }
00135                 if( nbadv <= 0 && nverts >= 3 && got_three )  return 1;
00136         }
00137         return 0;
00138 }
00139 
00140 /**
00141  *                      N M G _ R E G I O N _ V _ U N I Q U E
00142  *
00143  *  Ensure that all the vertices in r1 are still geometricaly unique.
00144  *  This will be true after nmg_region_both_vfuse() has been called,
00145  *  and should remain true throughout the intersection process.
00146  */
00147 void
00148 nmg_region_v_unique(struct nmgregion *r1, const struct bn_tol *tol)
00149 {
00150         int     i;
00151         int     j;
00152         struct bu_ptbl  t;
00153 
00154         NMG_CK_REGION(r1);
00155         BN_CK_TOL(tol);
00156 
00157         nmg_vertex_tabulate( &t, &r1->l.magic );
00158 
00159         for( i = BU_PTBL_END(&t)-1; i >= 0; i-- )  {
00160                 register struct vertex  *vi;
00161                 vi = (struct vertex *)BU_PTBL_GET(&t, i);
00162                 NMG_CK_VERTEX(vi);
00163                 if( !vi->vg_p )  continue;
00164 
00165                 for( j = i-1; j >= 0; j-- )  {
00166                         register struct vertex  *vj;
00167                         vj = (struct vertex *)BU_PTBL_GET(&t, j);
00168                         NMG_CK_VERTEX(vj);
00169                         if( !vj->vg_p )  continue;
00170                         if( !bn_pt3_pt3_equal( vi->vg_p->coord, vj->vg_p->coord, tol ) )
00171                                 continue;
00172                         /* They are the same */
00173                         bu_log("nmg_region_v_unique():  2 verts are the same, within tolerance\n");
00174                         nmg_pr_v( vi, 0 );
00175                         nmg_pr_v( vj, 0 );
00176                         rt_bomb("nmg_region_v_unique()\n");
00177                 }
00178         }
00179         bu_ptbl_free( &t);
00180 }
00181 
00182 /**
00183  *                      N M G _ P T B L _ V F U S E
00184  *
00185  *  Working from the end to the front, scan for geometric duplications
00186  *  within a single list of vertex structures.
00187  *
00188  *  Exists primarily as a support routine for nmg_model_vertex_fuse().
00189  */
00190 int
00191 nmg_ptbl_vfuse(struct bu_ptbl *t, const struct bn_tol *tol)
00192 {
00193         int     count = 0;
00194         int     i;
00195         int     j;
00196 
00197         for( i = BU_PTBL_END(t)-1; i >= 0; i-- )  {
00198                 register struct vertex  *vi;
00199                 vi = (struct vertex *)BU_PTBL_GET(t, i);
00200                 NMG_CK_VERTEX(vi);
00201                 if( !vi->vg_p )  continue;
00202 
00203                 for( j = i-1; j >= 0; j-- )  {
00204                         register struct vertex  *vj;
00205                         vj = (struct vertex *)BU_PTBL_GET(t, j);
00206                         NMG_CK_VERTEX(vj);
00207                         if( !vj->vg_p )  continue;
00208                         if( !bn_pt3_pt3_equal( vi->vg_p->coord, vj->vg_p->coord, tol ) )
00209                                 continue;
00210                         /* They are the same, fuse vi into vj */
00211                         nmg_jv( vj, vi );
00212                         bu_ptbl_rm( t, (long *)vi );
00213                         count++;
00214                         break;
00215                 }
00216         }
00217         return count;
00218 }
00219 
00220 /**
00221  *                      N M G _ R E G I O N _ B O T H _ V F U S E
00222  *
00223  *  For every element in t1, scan t2 for geometric duplications.
00224  *
00225  *  Deleted elements in t2 are marked by a null vertex pointer,
00226  *  rather than bothering to do a BU_PTBL_RM, which will re-copy the
00227  *  list to compress it.
00228  *
00229  *  Exists as a support routine for nmg_two_region_vertex_fuse()
00230  */
00231 int
00232 nmg_region_both_vfuse(struct bu_ptbl *t1, struct bu_ptbl *t2, const struct bn_tol *tol)
00233 {
00234         int     count = 0;
00235         int     i;
00236         int     j;
00237 
00238         /* Verify t2 is good to start with */
00239         for( j = BU_PTBL_END(t2)-1; j >= 0; j-- )  {
00240                 register struct vertex  *vj;
00241                 vj = (struct vertex *)BU_PTBL_GET(t2, j);
00242                 NMG_CK_VERTEX(vj);
00243         }
00244 
00245         for( i = BU_PTBL_END(t1)-1; i >= 0; i-- )  {
00246                 register struct vertex  *vi;
00247                 vi = (struct vertex *)BU_PTBL_GET(t1, i);
00248                 NMG_CK_VERTEX(vi);
00249                 if( !vi->vg_p )  continue;
00250 
00251                 for( j = BU_PTBL_END(t2)-1; j >= 0; j-- )  {
00252                         register struct vertex  *vj;
00253                         vj = (struct vertex *)BU_PTBL_GET(t2, j);
00254                         if(!vj) continue;
00255                         NMG_CK_VERTEX(vj);
00256                         if( !vj->vg_p )  continue;
00257                         if( !bn_pt3_pt3_equal( vi->vg_p->coord, vj->vg_p->coord, tol ) )
00258                                 continue;
00259                         /* They are the same, fuse vj into vi */
00260                         nmg_jv( vi, vj );
00261                         BU_PTBL_GET(t2, j) = 0;
00262                         count++;
00263                 }
00264         }
00265         return count;
00266 }
00267 
00268 #if 0
00269 /**
00270  *                      N M G _ T W O _ R E G I O N _ V E R T E X _ F U S E
00271  *
00272  *  This routine should not be used.  Instead, call nmg_model_vertex_fuse().
00273  */
00274 int
00275 nmg_two_region_vertex_fuse( r1, r2, tol )
00276 struct nmgregion        *r1;
00277 struct nmgregion        *r2;
00278 const struct bn_tol     *tol;
00279 {
00280         struct bu_ptbl  t1;
00281         struct bu_ptbl  t2;
00282         int             total = 0;
00283 
00284         NMG_CK_REGION(r1);
00285         NMG_CK_REGION(r2);
00286         BN_CK_TOL(tol);
00287 
00288         if( r1->m_p != r2->m_p )  rt_bomb("nmg_two_region_vertex_fuse:  regions not in same model\n");
00289 
00290         nmg_vertex_tabulate( &t1, &r1->l.magic );
00291         nmg_vertex_tabulate( &t2, &r2->l.magic );
00292 
00293         total = nmg_ptbl_vfuse( &t1, tol );
00294         total += nmg_region_both_vfuse( &t1, &t2, tol );
00295 
00296         bu_ptbl_free( &t1);
00297         bu_ptbl_free( &t2);
00298 
00299         return total;
00300 }
00301 #endif
00302 
00303 /**
00304  *                      N M G _ M O D E L _ V E R T E X _ F U S E
00305  *
00306  *  Fuse together any vertices in the nmgmodel that are geometricly
00307  *  identical, within the tolerance.
00308  */
00309 int
00310 nmg_model_vertex_fuse(struct model *m, const struct bn_tol *tol)
00311 {
00312         struct bu_ptbl  t1;
00313         int             total = 0;
00314 
00315         NMG_CK_MODEL(m);
00316         BN_CK_TOL(tol);
00317 
00318         nmg_vertex_tabulate( &t1, &m->magic );
00319 
00320         total = nmg_ptbl_vfuse( &t1, tol );
00321 
00322         bu_ptbl_free( &t1);
00323 
00324         if( rt_g.NMG_debug & DEBUG_BASIC && total > 0 )
00325                 bu_log("nmg_model_vertex_fuse() %d\n", total);
00326         return total;
00327 }
00328 
00329 /**
00330  *              N M G _ C N U R B _ I S _ L I N E A R
00331  *
00332  *      Checks if cnurb is linear
00333  *
00334  *      Returns:
00335  *              1 - cnurb is linear
00336  *              0 - either cnurb is not linear, or it's not obvious
00337  */
00338 
00339 int
00340 nmg_cnurb_is_linear(const struct edge_g_cnurb *cnrb)
00341 {
00342         int i;
00343         int coords;
00344         int last_index;
00345         int linear=0;
00346 
00347         NMG_CK_EDGE_G_CNURB( cnrb );
00348 
00349         if( rt_g.NMG_debug & DEBUG_MESH )
00350         {
00351                 bu_log( "nmg_cnurb_is_linear( x%x )\n", cnrb );
00352                 rt_nurb_c_print( cnrb );
00353         }
00354 
00355         if( cnrb->order <= 0 )
00356         {
00357                 linear = 1;
00358                 goto out;
00359         }
00360 
00361         if( cnrb->order == 2 )
00362         {
00363                 if( cnrb->c_size == 2 )
00364                 {
00365                         linear = 1;
00366                         goto out;
00367                 }
00368         }
00369 
00370         coords = RT_NURB_EXTRACT_COORDS( cnrb->pt_type );
00371         last_index = (cnrb->c_size - 1)*coords;
00372 
00373         /* Check if all control points are either the start point or end point */
00374         for( i=1 ; i<cnrb->c_size-2 ; i++ )
00375         {
00376                 if( VEQUAL( &cnrb->ctl_points[0], &cnrb->ctl_points[i] ) )
00377                         continue;
00378                 if( VEQUAL( &cnrb->ctl_points[last_index], &cnrb->ctl_points[i] ) )
00379                         continue;
00380 
00381                 goto out;
00382         }
00383 
00384         linear = 1;
00385 
00386 out:
00387         if( rt_g.NMG_debug & DEBUG_MESH )
00388                 bu_log( "nmg_cnurb_is_linear( x%x ) returning %d\n", cnrb, linear );
00389 
00390         return( linear );
00391 }
00392 
00393 /**
00394  *                      N M G _ S N U R B _ I S _ P L A N A R
00395  *
00396  *      Checks if snurb surface is planar
00397  *
00398  *      Returns:
00399  *              0 - surface is not planar
00400  *              1 - surface is planar (within tolerance)
00401  */
00402 
00403 int
00404 nmg_snurb_is_planar(const struct face_g_snurb *srf, const struct bn_tol *tol)
00405 {
00406         plane_t pl = {(fastf_t)0.0};
00407         int i;
00408         int coords;
00409         mat_t matrix;
00410         mat_t inverse;
00411         vect_t vsum;
00412         double det;
00413         double one_over_vertex_count;
00414         int planar=0;
00415 
00416         NMG_CK_FACE_G_SNURB( srf );
00417         BN_CK_TOL( tol );
00418 
00419         if( rt_g.NMG_debug & DEBUG_MESH )
00420         {
00421                 bu_log( "nmg_snurb_is_planar( x%x )\n", srf );
00422 #if 1
00423                 rt_nurb_s_print( "", srf );
00424 #endif
00425         }
00426 
00427         if( srf->order[0] == 2 && srf->order[1] == 2 )
00428         {
00429                 if( srf->s_size[0] == 2 && srf->s_size[1] == 2 )
00430                 {
00431                         planar = 1;
00432                         goto out;
00433                 }
00434         }
00435 
00436         /* build matrix */
00437         MAT_ZERO( matrix );
00438         VSET( vsum , 0.0 , 0.0 , 0.0 );
00439 
00440         one_over_vertex_count = 1.0/(double)(srf->s_size[0]*srf->s_size[1]);
00441         coords = RT_NURB_EXTRACT_COORDS( srf->pt_type );
00442 
00443         /* calculate an average plane for all control points */
00444         for( i=0 ; i<srf->s_size[0]*srf->s_size[1] ; i++ )
00445         {
00446                 fastf_t *pt;
00447 
00448                 pt = &srf->ctl_points[ i*coords ];
00449 
00450                 matrix[0] += pt[X] * pt[X];
00451                 matrix[1] += pt[X] * pt[Y];
00452                 matrix[2] += pt[X] * pt[Z];
00453                 matrix[5] += pt[Y] * pt[Y];
00454                 matrix[6] += pt[Y] * pt[Z];
00455                 matrix[10] += pt[Z] * pt[Z];
00456 
00457                 vsum[X] += pt[X];
00458                 vsum[Y] += pt[Y];
00459                 vsum[Z] += pt[Z];
00460         }
00461         matrix[4] = matrix[1];
00462         matrix[8] = matrix[2];
00463         matrix[9] = matrix[6];
00464         matrix[15] = 1.0;
00465 
00466         /* Check that we don't have a singular matrix */
00467         det = bn_mat_determinant( matrix );
00468 
00469         if( !NEAR_ZERO( det , SMALL_FASTF ) )
00470         {
00471                 fastf_t inv_len_pl;
00472 
00473                 /* invert matrix */
00474                 bn_mat_inv( inverse , matrix );
00475 
00476                 /* get normal vector */
00477                 MAT4X3PNT( pl , inverse , vsum );
00478 
00479                 /* unitize direction vector */
00480                 inv_len_pl = 1.0/(MAGNITUDE( pl ));
00481                 HSCALE( pl , pl , inv_len_pl );
00482 
00483                 /* get average vertex coordinates */
00484                 VSCALE( vsum, vsum, one_over_vertex_count );
00485 
00486                 /* get distance from plane to orgin */
00487                 pl[H] = VDOT( pl , vsum );
00488 
00489         }
00490         else
00491         {
00492                 int x_same=1;
00493                 int y_same=1;
00494                 int z_same=1;
00495 
00496                 /* singular matrix, may occur if all vertices have the same zero
00497                  * component.
00498                  */
00499                 for( i=1 ; i<srf->s_size[0]*srf->s_size[1] ; i++ )
00500                 {
00501                         if( srf->ctl_points[i*coords+X] != srf->ctl_points[X] )
00502                                 x_same = 0;
00503                         if( srf->ctl_points[i*coords+Y] != srf->ctl_points[Y] )
00504                                 y_same = 0;
00505                         if( srf->ctl_points[i*coords+Z] != srf->ctl_points[Z] )
00506                                 z_same = 0;
00507 
00508                         if( !x_same && !y_same && !z_same )
00509                                 break;
00510                 }
00511 
00512                 if( x_same )
00513                 {
00514                         VSET( pl , 1.0 , 0.0 , 0.0 );
00515                 }
00516                 else if( y_same )
00517                 {
00518                         VSET( pl , 0.0 , 1.0 , 0.0 );
00519                 }
00520                 else if( z_same )
00521                 {
00522                         VSET( pl , 0.0 , 0.0 , 1.0 );
00523                 }
00524 
00525                 if( x_same || y_same || z_same )
00526                 {
00527                         /* get average vertex coordinates */
00528                         VSCALE( vsum, vsum, one_over_vertex_count );
00529 
00530                         /* get distance from plane to orgin */
00531                         pl[H] = VDOT( pl , vsum );
00532 
00533                 }
00534                 else
00535                 {
00536                         bu_log( "nmg_snurb_is_plana: Cannot calculate plane for snurb x%x\n" , srf );
00537                         rt_nurb_s_print( "", srf );
00538                         rt_bomb( "nmg_snurb_is_plana: Cannot calculate plane for snurb\n" );
00539                 }
00540         }
00541 
00542         /* Now verify that every control point is on this plane */
00543         for( i=0 ; i<srf->s_size[0]*srf->s_size[1] ; i++ )
00544         {
00545                 fastf_t *pt;
00546                 fastf_t dist;
00547 
00548                 pt = &srf->ctl_points[ i*coords ];
00549 
00550                 dist = DIST_PT_PLANE( pt, pl );
00551                 if( dist > tol->dist )
00552                         goto out;
00553         }
00554 
00555         planar = 1;
00556 out:
00557         if( rt_g.NMG_debug & DEBUG_MESH )
00558                 bu_log( "nmg_snurb_is_planar( x%x ) returning %d\n", srf, planar );
00559 
00560         return( planar );
00561 
00562 }
00563 
00564 void
00565 nmg_eval_linear_trim_curve(const struct face_g_snurb *snrb, const fastf_t *uvw, fastf_t *xyz)
00566 {
00567         int coords;
00568         hpoint_t xyz1;
00569 
00570         if( snrb )
00571         {
00572                 NMG_CK_FACE_G_SNURB( snrb );
00573                 rt_nurb_s_eval( snrb, uvw[0], uvw[1], xyz1 );
00574                 if( RT_NURB_IS_PT_RATIONAL( snrb->pt_type ) )
00575                 {
00576                         fastf_t inverse_weight;
00577 
00578                         coords = RT_NURB_EXTRACT_COORDS( snrb->pt_type );
00579                         inverse_weight = 1.0/xyz1[coords-1];
00580 
00581                         VSCALE( xyz, xyz1, inverse_weight );
00582                 }
00583                 else
00584                         VMOVE( xyz, xyz1 )
00585         }
00586         else
00587                 VMOVE( xyz, uvw )
00588 
00589 }
00590 
00591 void
00592 nmg_eval_trim_curve(const struct edge_g_cnurb *cnrb, const struct face_g_snurb *snrb, const fastf_t t, fastf_t *xyz)
00593 {
00594         hpoint_t uvw;
00595         hpoint_t xyz1;
00596         int coords;
00597 
00598         NMG_CK_EDGE_G_CNURB( cnrb );
00599         if( snrb )
00600         {
00601                 NMG_CK_FACE_G_SNURB( snrb )
00602         }
00603 
00604         rt_nurb_c_eval( cnrb, t, uvw );
00605 
00606         if( RT_NURB_IS_PT_RATIONAL( cnrb->pt_type ) )
00607         {
00608                 fastf_t inverse_weight;
00609 
00610                 coords = RT_NURB_EXTRACT_COORDS( cnrb->pt_type );
00611                 inverse_weight = 1.0/uvw[coords-1];
00612 
00613                 VSCALE( uvw, uvw, inverse_weight );
00614         }
00615 
00616         if( snrb )
00617         {
00618                 rt_nurb_s_eval( snrb, uvw[0], uvw[1], xyz1 );
00619                 if( RT_NURB_IS_PT_RATIONAL( snrb->pt_type ) )
00620                 {
00621                         fastf_t inverse_weight;
00622 
00623                         coords = RT_NURB_EXTRACT_COORDS( snrb->pt_type );
00624                         inverse_weight = 1.0/xyz1[coords-1];
00625 
00626                         VSCALE( xyz, xyz1, inverse_weight );
00627                 }
00628                 else
00629                         VMOVE( xyz, xyz1 )
00630         }
00631         else
00632                 VMOVE( xyz, uvw )
00633 
00634 #if 0
00635         if( rt_g.NMG_debug & DEBUG_MESH )
00636                 bu_log( "nmg_eval_trim_curve returning (%g %g %g )\n", V3ARGS( xyz ) );
00637 #endif
00638 
00639 }
00640 
00641 void
00642 nmg_split_trim(const struct edge_g_cnurb *cnrb, const struct face_g_snurb *snrb, fastf_t t, struct pt_list *pt0, struct pt_list *pt1, const struct bn_tol *tol)
00643 {
00644         struct pt_list *pt_new;
00645         fastf_t t_sub;
00646         vect_t seg;
00647 
00648         NMG_CK_EDGE_G_CNURB( cnrb );
00649         NMG_CK_FACE_G_SNURB( snrb );
00650         BN_CK_TOL( tol );
00651 #if 0
00652         if( rt_g.NMG_debug & DEBUG_MESH )
00653                 bu_log( "nmg_split_trim( cnrb=x%x, snrb=x%x, t=%g, pt0=x%x, pt1=x%x )START\n",
00654                         cnrb, snrb, t, pt0, pt1 );
00655 #endif
00656         pt_new = (struct pt_list *)bu_malloc( sizeof( struct pt_list ), "g_split_trim: pt_new" );
00657         pt_new->t = t;
00658 
00659         if( pt_new->t < pt0->t || pt_new->t > pt1->t )
00660         {
00661                 bu_log( "nmg_split_trim: split parameter (%g) is not between ends (%g and %g)\n",
00662                         t, pt0->t, pt1->t );
00663                 rt_bomb( "nmg_split_trim: split parameteris not between ends\n" );
00664         }
00665 
00666         nmg_eval_trim_curve( cnrb, snrb, pt_new->t, pt_new->xyz );
00667 
00668         BU_LIST_INSERT( &pt1->l, &pt_new->l );
00669 
00670         VSUB2( seg, pt0->xyz, pt_new->xyz );
00671         if( MAGSQ( seg ) > tol->dist_sq )
00672         {
00673                 t_sub = (pt0->t + pt_new->t)/2.0;
00674 #if 0
00675                 if( rt_g.NMG_debug & DEBUG_MESH )
00676                         bu_log( "nmg_split_trim: recursing at t=%g (%g,%g)\n",
00677                                         t_sub, pt0->t, pt_new->t );
00678 #endif
00679                 nmg_split_trim( cnrb, snrb, t_sub, pt0, pt_new, tol );
00680         }
00681 
00682         VSUB2( seg, pt_new->xyz, pt1->xyz );
00683         if( MAGSQ( seg ) > tol->dist_sq )
00684         {
00685                 t_sub = (pt_new->t + pt1->t)/2.0;
00686 #if 0
00687                 if( rt_g.NMG_debug & DEBUG_MESH )
00688                         bu_log( "nmg_split_trim: recursing at t=%g (%g,%g)\n",
00689                                         t_sub, pt_new->t, pt1->t );
00690 #endif
00691                 nmg_split_trim( cnrb, snrb, t_sub, pt_new, pt1, tol );
00692         }
00693 #if 0
00694         if( rt_g.NMG_debug & DEBUG_MESH )
00695                 bu_log( "nmg_split_trim( cnrb=x%x, snrb=x%x, t=%g, pt0=x%x, pt1=x%x ) END\n",
00696                         cnrb, snrb, t, pt0, pt1 );
00697 #endif
00698 
00699 }
00700 
00701 void
00702 nmg_eval_trim_to_tol(const struct edge_g_cnurb *cnrb, const struct face_g_snurb *snrb, const fastf_t t0, const fastf_t t1, struct bu_list *head, const struct bn_tol *tol)
00703 {
00704         fastf_t t;
00705         struct pt_list *pt0,*pt1;
00706 
00707         NMG_CK_EDGE_G_CNURB( cnrb );
00708         NMG_CK_FACE_G_SNURB( snrb );
00709         BN_CK_TOL( tol );
00710 
00711         if( rt_g.NMG_debug & DEBUG_MESH )
00712                 bu_log( "nmg_eval_trim_to_tol( cnrb=x%x, snrb=x%x, t0=%g, t1=%g ) START\n",
00713                                 cnrb, snrb, t0, t1 );
00714 
00715         pt0 = (struct pt_list *)bu_malloc( sizeof( struct pt_list ), "nmg_eval_trim_to_tol: pt0 " );
00716         pt0->t = t0;
00717         nmg_eval_trim_curve( cnrb, snrb, pt0->t, pt0->xyz );
00718         BU_LIST_INSERT( head, &pt0->l );
00719 
00720         pt1 = (struct pt_list *)bu_malloc( sizeof( struct pt_list ), "nmg_eval_trim_to_tol: pt1 " );
00721         pt1->t = t1;
00722         nmg_eval_trim_curve( cnrb, snrb, pt1->t, pt1->xyz );
00723         BU_LIST_INSERT( head, &pt1->l );
00724 
00725         t = (t0 + t1)/2.0;
00726         nmg_split_trim( cnrb, snrb, t, pt0, pt1, tol );
00727 
00728         if( rt_g.NMG_debug & DEBUG_MESH )
00729                 bu_log( "nmg_eval_trim_to_tol( cnrb=x%x, snrb=x%x, t0=%g, t1=%g ) END\n",
00730                                 cnrb, snrb, t0, t1 );
00731 }
00732 
00733 void
00734 nmg_split_linear_trim(const struct face_g_snurb *snrb, const fastf_t *uvw1, const fastf_t *uvw, const fastf_t *uvw2, struct pt_list *pt0, struct pt_list *pt1, const struct bn_tol *tol)
00735 {
00736         struct pt_list *pt_new;
00737         fastf_t t_sub;
00738         fastf_t uvw_sub[3];
00739         vect_t seg;
00740 
00741         if( snrb )
00742                 NMG_CK_FACE_G_SNURB( snrb );
00743         BN_CK_TOL( tol );
00744 #if 0
00745         if( rt_g.NMG_debug & DEBUG_MESH )
00746                 bu_log( "nmg_split_linear_trim( snrb=x%x, pt0=x%x, pt1=x%x )START\n",
00747                         snrb, pt0, pt1 );
00748 #endif
00749         pt_new = (struct pt_list *)bu_malloc( sizeof( struct pt_list ), "g_split_trim: pt_new" );
00750         pt_new->t = 0.5*(pt0->t + pt1->t);
00751 
00752         VBLEND2( uvw_sub, 1.0 - pt_new->t, uvw1, pt_new->t, uvw2 );
00753         nmg_eval_linear_trim_curve( snrb, uvw_sub, pt_new->xyz );
00754 
00755         BU_LIST_INSERT( &pt1->l, &pt_new->l );
00756 
00757 #if 0
00758         if( rt_g.NMG_debug & DEBUG_MESH )
00759                 bu_log( "nmg_split_linear_trim: new segments (%g %g %g) <-> (%g %g %g) <-> (%g %g %g)\n",
00760                         V3ARGS( pt0->xyz ), V3ARGS( pt_new->xyz ), V3ARGS( pt1->xyz ) );
00761 #endif
00762 
00763         VSUB2( seg, pt0->xyz, pt_new->xyz );
00764         if( MAGSQ( seg ) > tol->dist_sq )
00765         {
00766                 t_sub = (pt0->t + pt_new->t)/2.0;
00767                 VBLEND2( uvw_sub, 1.0 - t_sub, uvw1, t_sub, uvw2 );
00768 #if 0
00769                 if( rt_g.NMG_debug & DEBUG_MESH )
00770                         bu_log( "nmg_split_linear_trim: recursing at t=%g (%g,%g)\n",
00771                                         t_sub, pt0->t, pt_new->t );
00772 #endif
00773                 nmg_split_linear_trim( snrb, uvw1, uvw_sub, uvw2, pt0, pt_new, tol );
00774         }
00775 
00776         VSUB2( seg, pt_new->xyz, pt1->xyz );
00777         if( MAGSQ( seg ) > tol->dist_sq )
00778         {
00779                 t_sub = (pt_new->t + pt1->t)/2.0;
00780                 VBLEND2( uvw_sub, 1.0 - t_sub, uvw1, t_sub, uvw2 );
00781 #if 0
00782                 if( rt_g.NMG_debug & DEBUG_MESH )
00783                         bu_log( "nmg_split_linear_trim: recursing at t=%g (%g,%g)\n",
00784                                         t_sub, pt_new->t, pt1->t );
00785 #endif
00786                 nmg_split_linear_trim( snrb, uvw1, uvw_sub, uvw2, pt0, pt_new, tol );
00787         }
00788 #if 0
00789         if( rt_g.NMG_debug & DEBUG_MESH )
00790                 bu_log( "nmg_split_linear_trim( snrb=x%x, pt0=x%x, pt1=x%x ) END\n",
00791                         snrb, pt0, pt1 );
00792 #endif
00793 
00794 }
00795 
00796 void
00797 nmg_eval_linear_trim_to_tol(const struct edge_g_cnurb *cnrb, const struct face_g_snurb *snrb, const fastf_t *uvw1, const fastf_t *uvw2, struct bu_list *head, const struct bn_tol *tol)
00798 {
00799         fastf_t uvw[3];
00800         struct pt_list *pt0,*pt1;
00801 
00802         NMG_CK_EDGE_G_CNURB( cnrb );
00803         NMG_CK_FACE_G_SNURB( snrb );
00804         BN_CK_TOL( tol );
00805 
00806         NMG_CK_EDGE_G_CNURB( cnrb );
00807         NMG_CK_FACE_G_SNURB( snrb );
00808         BN_CK_TOL( tol );
00809 
00810         if( rt_g.NMG_debug & DEBUG_MESH )
00811                 bu_log( "nmg_eval_linear_trim_to_tol( cnrb=x%x, snrb=x%x, uvw1=( %g %g %g), uvw2=( %g %g %g ) ) START\n",
00812                                 cnrb, snrb, V3ARGS( uvw1 ), V3ARGS( uvw2 ) );
00813 
00814         pt0 = (struct pt_list *)bu_malloc( sizeof( struct pt_list ), "nmg_eval_linear_trim_to_tol: pt0 " );
00815         pt0->t = 0.0;
00816         nmg_eval_linear_trim_curve( snrb, uvw1, pt0->xyz );
00817         BU_LIST_INSERT( head, &pt0->l );
00818 
00819         pt1 = (struct pt_list *)bu_malloc( sizeof( struct pt_list ), "nmg_eval_linear_trim_to_tol: pt1 " );
00820         pt1->t = 1.0;
00821         nmg_eval_linear_trim_curve( snrb, uvw2, pt1->xyz );
00822         BU_LIST_INSERT( head, &pt1->l );
00823 
00824 
00825         VBLEND2( uvw, 0.5, uvw1, 0.5, uvw2 )
00826         nmg_split_linear_trim( snrb, uvw1, uvw, uvw2, pt0, pt1, tol );
00827 
00828         if( rt_g.NMG_debug & DEBUG_MESH )
00829                 bu_log( "nmg_eval_linear_trim_to_tol( cnrb=x%x, snrb=x%x ) END\n",
00830                                 cnrb, snrb );
00831 }
00832 
00833 /* check for coincidence at twenty interior points along a cnurb */
00834 #define         CHECK_NUMBER    20
00835 
00836 /**             N M G _ C N U R B _ L S E G _ C O I N C I D E N T
00837  *
00838  *      Checks if CNURB is coincident with line segment from pt1 to pt2
00839  *      by calculating a number of points along the CNURB and checking
00840  *      if they lie on the line between pt1 and pt2 (within tolerance).
00841  *              NOTE: eu1 must be the EU referencing cnrb!!!!
00842  *
00843  *      Returns:
00844  *              0 - not coincident
00845  *              1 - coincident
00846  */
00847 int
00848 nmg_cnurb_lseg_coincident(const struct edgeuse *eu1, const struct edge_g_cnurb *cnrb, const struct face_g_snurb *snrb, const fastf_t *pt1, const fastf_t *pt2, const struct bn_tol *tol)
00849 {
00850         fastf_t t0,t1,t;
00851         fastf_t delt;
00852         int coincident=0;
00853         int i;
00854 
00855 
00856         NMG_CK_EDGEUSE( eu1 );
00857         NMG_CK_EDGE_G_CNURB( cnrb );
00858 
00859         if( rt_g.NMG_debug & DEBUG_MESH )
00860                 bu_log( "nmg_cnurb_lseg_coincident( eu1=x%x, cnrb=x%x, snrb=x%x, pt1=(%g %g %g), pt2=(%g %g %g)\n",
00861                         eu1, cnrb, snrb, V3ARGS( pt1 ), V3ARGS( pt2 ) );
00862 
00863         if( eu1->g.cnurb_p != cnrb )
00864         {
00865                 bu_log( "nmg_cnurb_lseg_coincident: cnrb x%x isn't from eu x%x\n",
00866                         cnrb, eu1 );
00867                 rt_bomb( "nmg_cnurb_lseg_coincident: cnrb and eu1 disagree\n" );
00868         }
00869 
00870         if( snrb )
00871                 NMG_CK_FACE_G_SNURB( snrb );
00872 
00873         BN_CK_TOL( tol );
00874 
00875         if( cnrb->order <= 0 )
00876         {
00877                 /* cnrb is linear in parameter space */
00878                 struct vertexuse *vu1;
00879                 struct vertexuse *vu2;
00880                 struct vertexuse_a_cnurb *vua1;
00881                 struct vertexuse_a_cnurb *vua2;
00882 
00883                 if( !snrb )
00884                         rt_bomb( "nmg_cnurb_lseg_coincident: No CNURB nor SNURB!!\n" );
00885 
00886                 vu1 = eu1->vu_p;
00887                 NMG_CK_VERTEXUSE( vu1 );
00888                 if( !vu1->a.magic_p )
00889                 {
00890                         bu_log( "nmg_cnurb_lseg_coincident: vu (x%x) has no attributes\n",
00891                                 vu1 );
00892                         rt_bomb( "nmg_cnurb_lseg_coincident: vu has no attributes\n" );
00893                 }
00894 
00895                 if( *vu1->a.magic_p != NMG_VERTEXUSE_A_CNURB_MAGIC )
00896                 {
00897                         bu_log( "nmg_cnurb_lseg_coincident: vu (x%x) from CNURB EU (x%x) is not CNURB\n",
00898                                 vu1, eu1 );
00899                         rt_bomb( "nmg_cnurb_lseg_coincident: vu from CNURB EU is not CNURB\n" );
00900                 }
00901 
00902                 vua1 = vu1->a.cnurb_p;
00903                 NMG_CK_VERTEXUSE_A_CNURB( vua1 );
00904 
00905                 vu2 = eu1->eumate_p->vu_p;
00906                 NMG_CK_VERTEXUSE( vu2 );
00907                 if( !vu2->a.magic_p )
00908                 {
00909                         bu_log( "nmg_cnurb_lseg_coincident: vu (x%x) has no attributes\n",
00910                                 vu2 );
00911                         rt_bomb( "nmg_cnurb_lseg_coincident: vu has no attributes\n" );
00912                 }
00913 
00914                 if( *vu2->a.magic_p != NMG_VERTEXUSE_A_CNURB_MAGIC )
00915                 {
00916                         bu_log( "nmg_cnurb_lseg_coincident: vu (x%x) from CNURB EU (x%x) is not CNURB\n",
00917                                 vu2, eu1 );
00918                         rt_bomb( "nmg_cnurb_lseg_coincident: vu from CNURB EU is not CNURB\n" );
00919                 }
00920 
00921                 vua2 = vu2->a.cnurb_p;
00922                 NMG_CK_VERTEXUSE_A_CNURB( vua2 );
00923 
00924                 coincident = 1;
00925                 for( i=0 ; i<CHECK_NUMBER ; i++ )
00926                 {
00927                         point_t uvw;
00928                         point_t xyz;
00929                         fastf_t blend;
00930                         fastf_t dist;
00931                         point_t pca;
00932 
00933                         blend = (double)(i+1)/(double)(CHECK_NUMBER+1);
00934                         VBLEND2( uvw, blend, vua1->param, (1.0-blend), vua2->param );
00935 
00936                         nmg_eval_linear_trim_curve( snrb, uvw, xyz );
00937 
00938                         if( bn_dist_pt3_lseg3( &dist, pca, pt1, pt2, xyz, tol ) > 2 )
00939                         {
00940                                 coincident = 0;
00941                                 break;
00942                         }
00943                 }
00944                 if( rt_g.NMG_debug & DEBUG_MESH )
00945                         bu_log( "nmg_cnurb_lseg_coincident returning %d\n", coincident );
00946                 return( coincident );
00947         }
00948 
00949         t0 = cnrb->k.knots[0];
00950         t1 = cnrb->k.knots[cnrb->k.k_size-1];
00951         delt = (t1 - t0)/(double)(CHECK_NUMBER+1);
00952 
00953         coincident = 1;
00954         for( i=0 ; i<CHECK_NUMBER ; i++ )
00955         {
00956                 point_t xyz;
00957                 fastf_t dist;
00958                 point_t pca;
00959 
00960                 t = t0 + (double)(i+1)*delt;
00961 
00962                 nmg_eval_trim_curve( cnrb, snrb, t, xyz );
00963 
00964                 if( bn_dist_pt3_lseg3( &dist, pca, pt1, pt2, xyz, tol ) > 2 )
00965                 {
00966                         coincident = 0;
00967                         break;
00968                 }
00969         }
00970         if( rt_g.NMG_debug & DEBUG_MESH )
00971                 bu_log( "nmg_cnurb_lseg_coincident returning %d\n", coincident );
00972         return( coincident );
00973 }
00974 
00975 /**             N M G _ C N U R B _ I S _ O N _ C R V
00976  *
00977  *      Checks if CNURB eu lies on curve contained in list headed at "head"
00978  *      "Head" must contain a list of points (struct pt_list) each within
00979  *      tolerance of the next. (Just checks at "CHECK_NUMBER" points for now).
00980  *
00981  *      Returns:
00982  *               0 - cnurb is not on curve;
00983  *               1 - cnurb is on curve
00984  */
00985 int
00986 nmg_cnurb_is_on_crv(const struct edgeuse *eu, const struct edge_g_cnurb *cnrb, const struct face_g_snurb *snrb, const struct bu_list *head, const struct bn_tol *tol)
00987 {
00988         int i;
00989         int coincident;
00990         fastf_t t, t0, t1;
00991         fastf_t delt;
00992 
00993         NMG_CK_EDGEUSE( eu );
00994         NMG_CK_EDGE_G_CNURB( cnrb );
00995         if( snrb )
00996                 NMG_CK_FACE_G_SNURB( snrb );
00997         BU_CK_LIST_HEAD( head );
00998         BN_CK_TOL( tol );
00999 
01000         if( rt_g.NMG_debug & DEBUG_MESH )
01001                 bu_log( "nmg_cnurb_is_on_crv( eu=x%x, cnrb=x%x, snrb=x%x, head=x%x )\n",
01002                         eu, cnrb, snrb, head );
01003         if( cnrb->order <= 0 )
01004         {
01005                 struct vertexuse *vu1,*vu2;
01006                 struct vertexuse_a_cnurb *vu1a,*vu2a;
01007                 fastf_t blend;
01008                 point_t uvw;
01009                 point_t xyz;
01010 
01011                 /* cnurb is linear in parameter space */
01012 
01013                 vu1 = eu->vu_p;
01014                 NMG_CK_VERTEXUSE( vu1 );
01015                 vu2 = eu->eumate_p->vu_p;
01016                 NMG_CK_VERTEXUSE( vu2 );
01017 
01018                 if( !vu1->a.magic_p )
01019                 {
01020                         bu_log( "nmg_cnurb_is_on_crv(): vu (x%x) on CNURB EU (x%x) has no attributes\n",
01021                                 vu1, eu );
01022                         rt_bomb( "nmg_cnurb_is_on_crv(): vu on CNURB EU has no attributes\n" );
01023                 }
01024                 if( *vu1->a.magic_p != NMG_VERTEXUSE_A_CNURB_MAGIC )
01025                 {
01026                         bu_log( "nmg_cnurb_is_on_crv(): vu (x%x) on CNURB EU (x%x) is not CNURB\n",
01027                                 vu1, eu );
01028                         rt_bomb( "nmg_cnurb_is_on_crv(): vu on CNURB EU is not CNURB\n" );
01029                 }
01030                 vu1a = vu1->a.cnurb_p;
01031                 NMG_CK_VERTEXUSE_A_CNURB( vu1a );
01032 
01033                 if( !vu2->a.magic_p )
01034                 {
01035                         bu_log( "nmg_cnurb_is_on_crv(): vu (x%x) on CNURB EU (x%x) has no attributes\n",
01036                                 vu2, eu->eumate_p );
01037                         rt_bomb( "nmg_cnurb_is_on_crv(): vu on CNURB EU has no attributes\n" );
01038                 }
01039                 if( *vu2->a.magic_p != NMG_VERTEXUSE_A_CNURB_MAGIC )
01040                 {
01041                         bu_log( "nmg_cnurb_is_on_crv(): vu (x%x) on CNURB EU (x%x) is not CNURB\n",
01042                                 vu2, eu->eumate_p );
01043                         rt_bomb( "nmg_cnurb_is_on_crv(): vu on CNURB EU is not CNURB\n" );
01044                 }
01045                 vu2a = vu2->a.cnurb_p;
01046                 NMG_CK_VERTEXUSE_A_CNURB( vu2a );
01047 
01048                 coincident = 1;
01049                 for( i=0 ; i<CHECK_NUMBER ; i++ )
01050                 {
01051                         struct pt_list *pt;
01052                         int found=0;
01053 
01054                         blend = (double)(i+1)/(double)(CHECK_NUMBER+1);
01055 
01056                         VBLEND2( uvw, blend, vu1a->param, (1.0-blend), vu2a->param );
01057 
01058                         nmg_eval_linear_trim_curve( snrb, uvw, xyz );
01059 
01060                         for( BU_LIST_FOR( pt, pt_list, head ) )
01061                         {
01062                                 vect_t diff;
01063 
01064                                 VSUB2( diff, xyz, pt->xyz );
01065                                 if( MAGSQ( diff ) <= tol->dist_sq )
01066                                 {
01067                                         found = 1;
01068                                         break;
01069                                 }
01070                         }
01071                         if( !found )
01072                         {
01073                                 coincident = 0;
01074                                 break;
01075                         }
01076                 }
01077                 if( rt_g.NMG_debug & DEBUG_MESH )
01078                         bu_log( "nmg_cnurb_is_on_crv() returning %d\n", coincident );
01079                 return( coincident );
01080         }
01081 
01082         coincident = 1;
01083         t0 = cnrb->k.knots[0];
01084         t1 = cnrb->k.knots[cnrb->k.k_size-1];
01085         delt = (t1 - t0)/(double)(CHECK_NUMBER+1);
01086         for( i=0 ; i<CHECK_NUMBER ; i++ )
01087         {
01088                 point_t xyz;
01089                 struct pt_list *pt;
01090                 int found;
01091 
01092                 t = t0 + (double)(i+1)*delt;
01093 
01094                 nmg_eval_trim_curve( cnrb, snrb, t, xyz );
01095 
01096                 found = 0;
01097                 for( BU_LIST_FOR( pt, pt_list, head ) )
01098                 {
01099                         vect_t diff;
01100 
01101                         VSUB2( diff, xyz, pt->xyz );
01102                         if( MAGSQ( diff ) <= tol->dist_sq )
01103                         {
01104                                 found = 1;
01105                                 break;
01106                         }
01107                 }
01108                 if( !found )
01109                 {
01110                         coincident = 0;
01111                         break;
01112                 }
01113         }
01114 
01115         if( rt_g.NMG_debug & DEBUG_MESH )
01116                 bu_log( "nmg_cnurb_is_on_crv returning %d\n", coincident );
01117         return( coincident );
01118 }
01119 
01120 /**
01121  *                      N M G _ M O D E L _ E D G E _ F U S E
01122  */
01123 #if 0
01124 int
01125 nmg_model_edge_fuse( m, tol )
01126 struct model            *m;
01127 const struct bn_tol     *tol;
01128 {
01129         struct bu_ptbl  eutab;
01130         int             total = 0;
01131         int             non_lseg=0;
01132         register int    i,j;
01133 
01134         NMG_CK_MODEL(m);
01135         BN_CK_TOL(tol);
01136 
01137         /* Make a list of all the edgeuse structs in the model */
01138 again:
01139         total = 0;
01140         nmg_edgeuse_tabulate( &eutab, &m->magic );
01141 
01142         for( i = BU_PTBL_END(&eutab)-1; i >= 0; i-- )  {
01143                 struct edgeuse          *eu1;
01144                 struct edge             *e1;
01145                 register struct vertex  *v1a, *v1b;
01146                 int                     lseg1=0;
01147                 int lseg2;
01148 
01149                 eu1 = (struct edgeuse *)BU_PTBL_GET(&eutab, i);
01150                 NMG_CK_EDGEUSE(eu1);
01151                 e1 = eu1->e_p;
01152                 NMG_CK_EDGE(e1);
01153                 NMG_CK_EDGE_G_EITHER(eu1->g.magic_p);
01154 
01155                 v1a = eu1->vu_p->v_p;
01156                 v1b = eu1->eumate_p->vu_p->v_p;
01157                 NMG_CK_VERTEX(v1a);
01158                 NMG_CK_VERTEX(v1b);
01159 
01160                 if( v1a == v1b )  {
01161                         if( *eu1->g.magic_p == NMG_EDGE_G_LSEG_MAGIC )  {
01162                                 /* 0-length lseg edge.  Eliminate. */
01163                                 /* These should't happen, but need fixing. */
01164                                 if( nmg_keu( eu1 ) )  {
01165                                         bu_log("nmg_model_edge_fuse() WARNING:  deletion of 0-length edgeuse=x%x caused parent to go empty.\n", eu1);
01166                                 }
01167                                 bu_log("nmg_model_edge_fuse() 0-length edgeuse=x%x deleted\n", eu1);
01168                                 /* XXX no way to know where edgeuse mate will be */
01169                                 bu_ptbl_free( &eutab);
01170                                 goto again;
01171                         }
01172                         /* For g_cnurb edges, could check for distinct param values on either end of edge */
01173                 }
01174 
01175                 if( !eu1->g.magic_p )
01176                 {
01177                         bu_log( "nmg_model_edge_fuse() WARNING: eu (x%x) has no geometry\n" , eu1 );
01178                         continue;
01179                 }
01180 
01181                 /* Check if eu1 is a line segment */
01182                 if( *eu1->g.magic_p == NMG_EDGE_G_LSEG_MAGIC )
01183                         lseg1 = 1;
01184                 else if( *eu1->g.magic_p == NMG_EDGE_G_CNURB_MAGIC )
01185                 {
01186                         non_lseg++;
01187                         continue;
01188                 }
01189                 else
01190 
01191                 {
01192                         bu_log( "nmg_model_edge_fuse() WARNING: eu (x%x) has unknown geometry\n" , eu1 );
01193                         continue;
01194                 }
01195 
01196                 /* For performance, don't recheck pointers here */
01197                 for( j = i-1; j >= 0; j-- )  {
01198                         register struct edgeuse *eu2;
01199                         register struct vertex  *v2a, *v2b;
01200                         int eus_are_coincident=0;
01201 
01202 
01203                         eu2 = (struct edgeuse *)BU_PTBL_GET(&eutab,j);
01204 
01205                         if( rt_g.NMG_debug & DEBUG_MESH )
01206                                 bu_log( "nmg_mode_edge_fuse: checking eus x%x and x%x\n", eu1, eu2 );
01207 
01208                         /* Do this vertex test first, to reduce memory loads */
01209                         v2a = eu2->vu_p->v_p;
01210                         if( v2a != v1a && v2a != v1b )  continue;
01211 
01212                         if( e1 == eu2->e_p )  continue; /* Already shared */
01213 
01214                         v2b = eu2->eumate_p->vu_p->v_p;
01215                         if( (v2a != v1a || v2b != v1b) &&
01216                             (v2b != v1a || v2a != v1b) )
01217                                 continue;
01218 
01219                         if( !eu2->g.magic_p )
01220                         {
01221                                 bu_log( "nmg_model_edge_fuse() WARNING: eu (x%x) has no geometry\n" , eu2 );
01222                                 continue;
01223                         }
01224 
01225                         /* check if eu2 is a line segment */
01226                         lseg2 = 0;
01227                         if( *eu2->g.magic_p == NMG_EDGE_G_LSEG_MAGIC )
01228                                 lseg2 = 1;
01229                         else if( *eu2->g.magic_p == NMG_EDGE_G_CNURB_MAGIC )
01230                                 continue;
01231                         else
01232                         {
01233                                 bu_log( "nmg_model_edge_fuse() WARNING: eu (x%x) has unknown geometry\n" , eu2 );
01234                                 continue;
01235                         }
01236 
01237                         /* EU's share endpoints */
01238                         if( lseg1 && lseg2 )
01239                         {
01240                                 /* both are line segments */
01241                                 nmg_radial_join_eu(eu1, eu2, tol);
01242                                 total++;
01243                         }
01244                 }
01245 
01246         }
01247         if( rt_g.NMG_debug & DEBUG_BASIC && total > 0 )
01248                 bu_log("nmg_model_edge_fuse(): %d edges fused\n", total);
01249         bu_ptbl_free( &eutab);
01250 
01251         if( non_lseg )
01252                 bu_log( "Warning: CNURB edges not fused!!!\n" );
01253 
01254         return total;
01255 }
01256 #else
01257 int
01258 nmg_model_edge_fuse(struct model *m, const struct bn_tol *tol)
01259 {
01260         struct bu_ptbl edges;
01261         int i, j;
01262         int count=0;
01263 
01264         NMG_CK_MODEL( m );
01265         BN_CK_TOL( tol );
01266 
01267         nmg_edge_tabulate( &edges, &m->magic );
01268 
01269         for( i=0 ; i<BU_PTBL_END( &edges )-1 ; i++ )
01270         {
01271                 struct edge *e1;
01272                 struct edgeuse *eu1;
01273 
01274                 e1 = (struct edge *)BU_PTBL_GET( &edges, i );
01275                 if(!e1 || !e1->index || e1->magic != NMG_EDGE_MAGIC )
01276                         continue;
01277                 eu1 = e1->eu_p;
01278                 if( !eu1 )
01279                         continue;
01280                 if( *eu1->g.magic_p != NMG_EDGE_G_LSEG_MAGIC )
01281                         continue;
01282 
01283                 for( j=i+1 ; j<BU_PTBL_END( &edges ) ; j++ )
01284                 {
01285                         struct edge *e2;
01286                         struct edgeuse *eu2;
01287 
01288                         e2 = (struct edge *)BU_PTBL_GET( &edges, j );
01289                         if( !e2 || !e2->index || e2->magic != NMG_EDGE_MAGIC )
01290                                 continue;
01291                         eu2 = e2->eu_p;
01292                         if( !eu2 )
01293                                 continue;
01294                         if( *eu2->g.magic_p != NMG_EDGE_G_LSEG_MAGIC )
01295                                 continue;
01296 
01297                         if( NMG_ARE_EUS_ADJACENT( eu1, eu2 ) )
01298                         {
01299                                 count++;
01300                                 nmg_radial_join_eu(eu1, eu2, tol);
01301                                 if(!e2->magic)
01302                                         bu_ptbl_zero( &edges, (long *)e2 );
01303                         }
01304                 }
01305         }
01306 
01307         bu_ptbl_free( &edges );
01308 
01309         return( count );
01310 }
01311 #endif
01312 
01313 
01314 /**
01315  *                      N M G _ M O D E L _ E D G E _ G _ F U S E
01316  *
01317  *  The present algorithm is a consequence of the old edge geom ptr structure.
01318  *  XXX This might be better formulated by generating a list of all
01319  *  edge_g structs in the model, and comparing *them* pairwise.
01320  */
01321 int
01322 nmg_model_edge_g_fuse(struct model *m, const struct bn_tol *tol)
01323 {
01324         struct bu_ptbl  etab;
01325         int             total = 0;
01326         register int    i,j;
01327 
01328         NMG_CK_MODEL(m);
01329         BN_CK_TOL(tol);
01330 
01331         /* Make a list of all the edge geometry structs in the model */
01332         nmg_edge_g_tabulate( &etab, &m->magic );
01333 
01334         for( i = BU_PTBL_END(&etab)-1; i >= 0; i-- )  {
01335                 struct edge_g_lseg              *eg1;
01336                 struct edgeuse                  *eu1;
01337 
01338                 eg1 = (struct edge_g_lseg *)BU_PTBL_GET(&etab, i);
01339                 NMG_CK_EDGE_G_EITHER(eg1);
01340 
01341                 /* XXX Need routine to compare two cnurbs geometricly */
01342                 if( eg1->l.magic == NMG_EDGE_G_CNURB_MAGIC )  {
01343                         continue;
01344                 }
01345 
01346                 NMG_CK_EDGE_G_LSEG(eg1);
01347                 eu1 = BU_LIST_MAIN_PTR( edgeuse, BU_LIST_FIRST( bu_list, &eg1->eu_hd2 ), l2 );
01348                 NMG_CK_EDGEUSE(eu1);
01349 
01350                 for( j = i-1; j >= 0; j-- )  {
01351                         struct edge_g_lseg      *eg2;
01352                         struct edgeuse          *eu2;
01353 
01354                         eg2 = (struct edge_g_lseg *)BU_PTBL_GET(&etab,j);
01355                         NMG_CK_EDGE_G_EITHER(eg2);
01356                         if( eg2->l.magic == NMG_EDGE_G_CNURB_MAGIC )  continue;
01357                         NMG_CK_EDGE_G_LSEG(eg2);
01358                         eu2 = BU_LIST_MAIN_PTR( edgeuse, BU_LIST_FIRST( bu_list, &eg2->eu_hd2 ), l2 );
01359                         NMG_CK_EDGEUSE(eu2);
01360 
01361                         if( eg1 == eg2 )  rt_bomb("nmg_model_edge_g_fuse() edge_g listed twice in ptbl?\n");
01362 
01363                         if( !nmg_2edgeuse_g_coincident( eu1, eu2, tol ) )  continue;
01364 
01365                         /* Comitted to fusing two edge_g_lseg's.
01366                          * Make all instances of eg1 become eg2.
01367                          * XXX really should check ALL edges using eg1
01368                          * XXX against ALL edges using eg2 for coincidence.
01369                          */
01370                         total++;
01371                         nmg_jeg( eg2, eg1 );
01372                         BU_PTBL_GET(&etab,i) = (long *)NULL;
01373                         break;
01374                 }
01375         }
01376         bu_ptbl_free( &etab);
01377         if( rt_g.NMG_debug & DEBUG_BASIC && total > 0 )
01378                 bu_log("nmg_model_edge_g_fuse(): %d edge_g_lseg's fused\n", total);
01379         return total;
01380 }
01381 
01382 #define TOL_MULTIPLES   1.0
01383 /**
01384  *                      N M G _ C K _ F U _ V E R T S
01385  *
01386  *  Check that all the vertices in fu1 are within tol->dist of fu2's surface.
01387  *  fu1 and fu2 may be the same face, or different.
01388  *
01389  *  This is intended to be a geometric check only, not a topology check.
01390  *  Topology may have become inappropriately shared.
01391  *
01392  *  Returns -
01393  *      0       All is well, or all verts are within TOL_MULTIPLES*tol->dist of fu2
01394  *      count   Number of verts *not* on fu2's surface when at least one is
01395  *              more than TOL_MULTIPLES*tol->dist from fu2.
01396  */
01397 int
01398 nmg_ck_fu_verts(struct faceuse *fu1, struct face *f2, const struct bn_tol *tol)
01399 {
01400         const struct face_g_plane       *fg2;
01401         FAST fastf_t            dist;
01402         fastf_t                 worst = 0;
01403         int                     count = 0;
01404         struct loopuse          *lu;
01405 
01406         NMG_CK_FACEUSE( fu1 );
01407         NMG_CK_FACE( f2 );
01408         BN_CK_TOL(tol);
01409 
01410         fg2 = f2->g.plane_p;
01411         NMG_CK_FACE_G_PLANE(fg2);
01412 
01413         for ( BU_LIST_FOR( lu, loopuse, &fu1->lu_hd ) ) {
01414                 struct edgeuse *eu;
01415 
01416                 if (BU_LIST_FIRST_MAGIC(&lu->down_hd) == NMG_VERTEXUSE_MAGIC) {
01417                         register struct vertexuse *vu = BU_LIST_FIRST( vertexuse, &lu->down_hd );
01418                         register struct vertex *v = vu->v_p;
01419                         register struct vertex_g *vg;
01420 
01421                         NMG_CK_VERTEX(v);
01422                         vg = v->vg_p;
01423                         if( !vg )  rt_bomb("nmg_ck_fu_verts(): vertex with no geometry?\n");
01424                         NMG_CK_VERTEX_G(vg);
01425 
01426                         /* Geometry check */
01427                         dist = DIST_PT_PLANE(vg->coord, fg2->N);
01428                         if( dist > tol->dist || dist < (-tol->dist) )  {
01429                                 if (rt_g.NMG_debug & DEBUG_MESH)  {
01430                                         bu_log("nmg_ck_fu_verts(x%x, x%x) v x%x off face by %e\n",
01431                                                fu1, f2, v, dist );
01432                                         VPRINT(" pt", vg->coord);
01433                                         PLPRINT(" fg2", fg2->N);
01434                                 }
01435                                 count++;
01436                                 if( dist < 0.0 ) {
01437                                         dist = (-dist);
01438                                 }
01439                                 if( dist > worst ) {
01440                                         worst = dist;
01441                                 }
01442                         }
01443                 }
01444 
01445                 for( BU_LIST_FOR( eu, edgeuse, &lu->down_hd ) ) {
01446                         struct vertex *v = eu->vu_p->v_p;
01447                         struct vertex_g *vg;
01448 
01449                         NMG_CK_VERTEX(v);
01450                         vg = v->vg_p;
01451                         if( !vg )  rt_bomb("nmg_ck_fu_verts(): vertex with no geometry?\n");
01452                         NMG_CK_VERTEX_G(vg);
01453 
01454                         /* Geometry check */
01455                         dist = DIST_PT_PLANE(vg->coord, fg2->N);
01456                         if( dist > tol->dist || dist < (-tol->dist) )  {
01457                                 if (rt_g.NMG_debug & DEBUG_MESH)  {
01458                                         bu_log("nmg_ck_fu_verts(x%x, x%x) v x%x off face by %e\n",
01459                                                fu1, f2, v, dist );
01460                                         VPRINT(" pt", vg->coord);
01461                                         PLPRINT(" fg2", fg2->N);
01462                                 }
01463                                 count++;
01464                                 if( dist < 0.0 ) {
01465                                         dist = (-dist);
01466                                 }
01467                                 if( dist > worst ) {
01468                                         worst = dist;
01469                                 }
01470                         }
01471                 }
01472         }
01473 
01474         if (worst > TOL_MULTIPLES*tol->dist) {
01475                 return count;
01476         } else {
01477                 return( 0 );
01478         }
01479 }
01480 
01481 /**                     N M G _ C K _ F G _ V E R T S
01482  *
01483  * Similar to nmg_ck_fu_verts, but checks all vertices that use the same
01484  * face geometry as fu1
01485  *  fu1 and f2 may be the same face, or different.
01486  *
01487  *  This is intended to be a geometric check only, not a topology check.
01488  *  Topology may have become inappropriately shared.
01489  *
01490  *  Returns -
01491  *      0       All is well.
01492  *      count   Number of verts *not* on fu2's surface.
01493  *
01494  */
01495 int
01496 nmg_ck_fg_verts(struct faceuse *fu1, struct face *f2, const struct bn_tol *tol)
01497 {
01498         struct face_g_plane *fg1;
01499         struct faceuse *fu;
01500         struct face *f;
01501         int count=0;
01502 
01503         NMG_CK_FACEUSE( fu1 );
01504         NMG_CK_FACE( f2 );
01505         BN_CK_TOL( tol );
01506 
01507         NMG_CK_FACE( fu1->f_p );
01508         fg1 = fu1->f_p->g.plane_p;
01509         NMG_CK_FACE_G_PLANE( fg1 );
01510 
01511         for( BU_LIST_FOR( f , face , &fg1->f_hd ) )
01512         {
01513                 NMG_CK_FACE( f );
01514 
01515                 fu = f->fu_p;
01516                 NMG_CK_FACEUSE( fu );
01517 
01518                 count += nmg_ck_fu_verts( fu , f2 , tol );
01519         }
01520 
01521         return( count );
01522 }
01523 
01524 /**
01525  *                      N M G _ T W O _ F A C E _ F U S E
01526  *
01527  *  XXX A better algorithm would be to compare loop by loop.
01528  *  If the two faces share all the verts of at least one loop of 3 or more
01529  *  vertices, then they should be shared.  Otherwise it will be awkward
01530  *  having shared loop(s) on non-shared faces!!
01531  *
01532  *  Compare the geometry of two faces, and fuse them if they are the
01533  *  same within tolerance.
01534  *  First compare the plane equations.  If they are "similar" (within tol),
01535  *  then check all verts in f2 to make sure that they are within tol->dist
01536  *  of f1's geometry.  If they are, then fuse the face geometry.
01537  *
01538  *  Returns -
01539  *      0       Faces were not fused.
01540  *      >0      Faces were successfully fused.
01541  */
01542 int
01543 nmg_two_face_fuse(struct face *f1, struct face *f2, const struct bn_tol *tol)
01544 {
01545         register struct face_g_plane    *fg1;
01546         register struct face_g_plane    *fg2;
01547         int                     flip2 = 0;
01548 
01549         NMG_CK_FACE(f1);
01550         NMG_CK_FACE(f2);
01551         BN_CK_TOL(tol);
01552 
01553         fg1 = f1->g.plane_p;
01554         fg2 = f2->g.plane_p;
01555 
01556         if( !fg1 || !fg2 )  {
01557                 if (rt_g.NMG_debug & DEBUG_MESH)  {
01558                         bu_log("nmg_two_face_fuse(x%x, x%x) null fg fg1=x%x, fg2=x%x\n",
01559                                 f1, f2, fg1, fg2);
01560                 }
01561                 return 0;
01562         }
01563 
01564         NMG_CK_FACE_G_PLANE(fg1);
01565         NMG_CK_FACE_G_PLANE(fg2);
01566 
01567         if( fg1 == fg2 )  {
01568                 if (rt_g.NMG_debug & DEBUG_MESH)  {
01569                         bu_log("nmg_two_face_fuse(x%x, x%x) fg already shared\n",
01570                                 f1, f2);
01571                 }
01572                 return 0;       /* Already shared */
01573         }
01574 #ifdef TOPOLOGY_CHECK
01575         /*
01576          *  First, a topology check.
01577          *  If the two faces share one entire loop (of at least 3 verts)
01578          *  according to topology, then by all rights the faces MUST be
01579          *  shared.
01580          */
01581 
01582         if( fabs(VDOT(fg1->N, fg2->N)) >= 0.99  &&
01583             fabs(fg1->N[3]) - fabs(fg2->N[3]) < 100 * tol->dist  &&
01584             nmg_is_common_bigloop( f1, f2 ) )  {
01585                 if( VDOT( fg1->N, fg2->N ) < 0 )  flip2 = 1;
01586                 if (rt_g.NMG_debug & DEBUG_MESH)  {
01587                         bu_log("nmg_two_face_fuse(x%x, x%x) faces have a common loop, they MUST be fused.  flip2=%d\n",
01588                                 f1, f2, flip2);
01589                 }
01590                 goto must_fuse;
01591         }
01592 
01593         /* See if faces are coplanar */
01594         code = bn_coplanar( fg1->N, fg2->N, tol );
01595         if( code <= 0 )  {
01596                 if (rt_g.NMG_debug & DEBUG_MESH)  {
01597                         bu_log("nmg_two_face_fuse(x%x, x%x) faces non-coplanar\n",
01598                                 f1, f2);
01599                 }
01600                 return 0;
01601         }
01602         if( code == 1 )
01603                 flip2 = 0;
01604         else
01605                 flip2 = 1;
01606 
01607         if (rt_g.NMG_debug & DEBUG_MESH)  {
01608                 bu_log("nmg_two_face_fuse(x%x, x%x) coplanar faces, bn_coplanar code=%d, flip2=%d\n",
01609                         f1, f2, code, flip2);
01610         }
01611 #else
01612         if( VDOT( fg1->N, fg2->N ) > 0.0 )
01613                 flip2 = 0;
01614         else
01615                 flip2 = 1;
01616 
01617         if( nmg_ck_fg_verts( f1->fu_p, f2, tol ) != 0 )  {
01618                 if (rt_g.NMG_debug & DEBUG_MESH)  {
01619                         bu_log("nmg_two_face_fuse: f1 verts not within tol of f2's surface, can't fuse\n");
01620                 }
01621                 return 0;
01622         }
01623 #endif
01624 
01625         /*
01626          *  Plane equations match, within tol.
01627          *  Before conducting a merge, verify that
01628          *  all the verts in f2 are within tol->dist
01629          *  of f1's surface.
01630          */
01631         if( nmg_ck_fg_verts( f2->fu_p, f1, tol ) != 0 )  {
01632                 if (rt_g.NMG_debug & DEBUG_MESH)  {
01633                         bu_log("nmg_two_face_fuse: f2 verts not within tol of f1's surface, can't fuse\n");
01634                 }
01635                 return 0;
01636         }
01637 
01638 #ifdef TOPPLOGY_CHECK
01639 must_fuse:
01640 #endif
01641         /* All points are on the plane, it's OK to fuse */
01642         if( flip2 == 0 )  {
01643                 if (rt_g.NMG_debug & DEBUG_MESH)  {
01644                         bu_log("joining face geometry (same dir) f1=x%x, f2=x%x\n", f1, f2);
01645                         PLPRINT(" fg1", fg1->N);
01646                         PLPRINT(" fg2", fg2->N);
01647                 }
01648                 nmg_jfg( f1, f2 );
01649         } else {
01650                 register struct face    *fn;
01651                 if (rt_g.NMG_debug & DEBUG_MESH)  {
01652                         bu_log("joining face geometry (opposite dirs)\n");
01653 
01654                         bu_log(" f1=x%x, flip=%d", f1, f1->flip);
01655                         PLPRINT(" fg1", fg1->N);
01656 
01657                         bu_log(" f2=x%x, flip=%d", f2, f2->flip);
01658                         PLPRINT(" fg2", fg2->N);
01659                 }
01660                 /* Flip flags of faces using fg2, first! */
01661                 for( BU_LIST_FOR( fn, face, &fg2->f_hd ) )  {
01662                         NMG_CK_FACE(fn);
01663                         fn->flip = !fn->flip;
01664                         if (rt_g.NMG_debug & DEBUG_MESH)  {
01665                                 bu_log("f=x%x, new flip=%d\n", fn, fn->flip);
01666                         }
01667                 }
01668                 nmg_jfg( f1, f2 );
01669         }
01670         return 1;
01671 }
01672 
01673 /**
01674  *                      N M G _ M O D E L _ F A C E _ F U S E
01675  *
01676  *  A routine to find all face geometry structures in an nmg model that
01677  *  have the same plane equation, and have them share face geometry.
01678  *  (See also nmg_shell_coplanar_face_merge(), which actually moves
01679  *  the loops into one face).
01680  *
01681  *  The criteria for two face geometry structs being the "same" are:
01682  *      1) The plane equations must be the same, within tolerance.
01683  *      2) All the vertices on the 2nd face must lie within the
01684  *         distance tolerance of the 1st face's plane equation.
01685  */
01686 int
01687 nmg_model_face_fuse(struct model *m, const struct bn_tol *tol)
01688 {
01689         struct bu_ptbl  ftab;
01690         int             total = 0;
01691         register int    i,j;
01692 
01693         NMG_CK_MODEL(m);
01694         BN_CK_TOL(tol);
01695 
01696         /* Make a list of all the face structs in the model */
01697         nmg_face_tabulate( &ftab, &m->magic );
01698 
01699         for( i = BU_PTBL_END(&ftab)-1; i >= 0; i-- )  {
01700                 register struct face    *f1;
01701                 register struct face_g_plane    *fg1;
01702                 f1 = (struct face *)BU_PTBL_GET(&ftab, i);
01703                 NMG_CK_FACE(f1);
01704                 NMG_CK_FACE_G_EITHER(f1->g.magic_p);
01705 
01706                 if( *f1->g.magic_p == NMG_FACE_G_SNURB_MAGIC )  {
01707                         /* XXX Need routine to compare 2 snurbs for equality here */
01708                         continue;
01709                 }
01710 
01711                 fg1 = f1->g.plane_p;
01712                 NMG_CK_FACE_G_PLANE(fg1);
01713 
01714                 /* Check that all the verts of f1 are within tol of face */
01715                 if( nmg_ck_fu_verts( f1->fu_p, f1, tol ) != 0 )  {
01716                         if( rt_g.NMG_debug )  {
01717                                 PLPRINT(" f1", f1->g.plane_p->N);
01718                                 nmg_pr_fu_briefly(f1->fu_p, 0);
01719                         }
01720                         rt_bomb("nmg_model_face_fuse(): verts not within tol of containing face\n");
01721                 }
01722 
01723                 for( j = i-1; j >= 0; j-- )  {
01724                         register struct face    *f2;
01725                         register struct face_g_plane    *fg2;
01726 
01727                         f2 = (struct face *)BU_PTBL_GET(&ftab, j);
01728                         NMG_CK_FACE(f2);
01729                         fg2 = f2->g.plane_p;
01730                         if( !fg2 )  continue;
01731                         NMG_CK_FACE_G_PLANE(fg2);
01732 
01733                         if( fg1 == fg2 )  continue;     /* Already shared */
01734 
01735                         if( nmg_two_face_fuse( f1, f2, tol ) > 0 )
01736                                 total++;
01737                 }
01738         }
01739         bu_ptbl_free( &ftab);
01740         if( rt_g.NMG_debug & DEBUG_BASIC && total > 0 )
01741                 bu_log("nmg_model_face_fuse: %d faces fused\n", total);
01742         return total;
01743 }
01744 
01745 int
01746 nmg_break_all_es_on_v(long int *magic_p, struct vertex *v, const struct bn_tol *tol)
01747 {
01748         struct bu_ptbl eus;
01749         int i;
01750         int count=0;
01751         const char *magic_type;
01752 
01753         if( rt_g.NMG_debug & DEBUG_BOOL )
01754                 bu_log( "nmg_break_all_es_on_v( magic=x%x, v=x%x )\n", magic_p, v );
01755 
01756         NMG_CK_VERTEX( v );
01757         BN_CK_TOL( tol );
01758 
01759         magic_type = bu_identify_magic( *magic_p );
01760         if ( !strcmp( magic_type, "NULL" ) ||
01761              !strcmp( magic_type, "Unknown_Magic" )  )
01762         {
01763                 bu_log( "Bad magic pointer passed to nmg_break_all_es_on_v (%s)\n", magic_type );
01764                 rt_bomb( "Bad magic pointer passed to nmg_break_all_es_on_v()\n" );
01765         }
01766 
01767         nmg_edgeuse_tabulate( &eus, magic_p );
01768 
01769         for( i=0 ; i<BU_PTBL_END( &eus ) ; i++ )
01770         {
01771                 struct edgeuse *eu;
01772                 struct edgeuse *eu_next, *eu_prev;
01773                 struct vertex *va;
01774                 struct vertex *vb;
01775                 fastf_t dist;
01776                 int code;
01777 
01778                 eu = (struct edgeuse *)BU_PTBL_GET( &eus, i );
01779                 NMG_CK_EDGEUSE( eu );
01780 
01781                 if( eu->g.magic_p && *eu->g.magic_p == NMG_EDGE_G_CNURB_MAGIC )
01782                         continue;
01783                 va = eu->vu_p->v_p;
01784                 vb = eu->eumate_p->vu_p->v_p;
01785                 NMG_CK_VERTEX(va);
01786                 NMG_CK_VERTEX(vb);
01787 
01788                 if( va == v ) continue;
01789                 if( vb == v ) continue;
01790 
01791                 eu_next = BU_LIST_PNEXT_CIRC( edgeuse, &eu->l );
01792                 eu_prev = BU_LIST_PPREV_CIRC( edgeuse, &eu->l );
01793 
01794                 if( eu_prev->vu_p->v_p == v )
01795                         continue;
01796 
01797                 if( eu_next->eumate_p->vu_p->v_p == v )
01798                         continue;
01799 
01800                 code = bn_isect_pt_lseg( &dist, va->vg_p->coord, vb->vg_p->coord,
01801                         v->vg_p->coord, tol );
01802                 if( code < 1 )  continue;       /* missed */
01803                 if( code == 1 || code == 2 )  {
01804                         bu_log("nmg_break_all_es_on_v() code=%d, why wasn't this vertex fused?\n", code);
01805                         bu_log( "\teu=x%x, v=x%x\n", eu, v );
01806                         continue;
01807                 }
01808                 /* Break edge on vertex, but don't fuse yet. */
01809 
01810                 if( rt_g.NMG_debug & DEBUG_BOOL )
01811                         bu_log( "\tnmg_break_all_es_on_v: breaking eu x%x on v x%x\n", eu, v );
01812 
01813                 (void)nmg_ebreak( v, eu );
01814                 count++;
01815         }
01816         bu_ptbl_free( &eus);
01817         return( count );
01818 }
01819 
01820 /**
01821  *                      N M G _ M O D E L _ B R E A K _ E _ O N _ V
01822  *
01823  *  As the first step in evaluating a boolean formula,
01824  *  before starting to do face/face intersections, compare every
01825  *  edge in the model with every vertex in the model.
01826  *  If the vertex is within tolerance of the edge, break the edge,
01827  *  and enrole the new edge on a list of edges still to be processed.
01828  *
01829  *  A list of edges and a list of vertices are built, and then processed.
01830  *
01831  *  Space partitioning could improve the performance of this algorithm.
01832  *  For the moment, a brute-force approach is used.
01833  *
01834  *  Returns -
01835  *      Number of edges broken.
01836  */
01837 int
01838 nmg_model_break_e_on_v(struct model *m, const struct bn_tol *tol)
01839 {
01840         int             count = 0;
01841         struct bu_ptbl  verts;
01842         struct bu_ptbl  edgeuses;
01843         struct bu_ptbl  new_edgeuses;
01844         register struct edgeuse **eup;
01845 
01846         NMG_CK_MODEL(m);
01847         BN_CK_TOL(tol);
01848 
01849         nmg_e_and_v_tabulate( &edgeuses, &verts, &m->magic );
01850 
01851         /* Repeat the process until no new edgeuses are created */
01852         while( BU_PTBL_LEN( &edgeuses ) > 0 )  {
01853                 (void)bu_ptbl_init( &new_edgeuses, 64, " &new_edgeuses");
01854 
01855                 for( eup = (struct edgeuse **)BU_PTBL_LASTADDR(&edgeuses);
01856                      eup >= (struct edgeuse **)BU_PTBL_BASEADDR(&edgeuses);
01857                      eup--
01858                 )  {
01859                         register struct edgeuse *eu;
01860                         register struct vertex  *va;
01861                         register struct vertex  *vb;
01862                         register struct vertex  **vp;
01863 
01864                         eu = *eup;
01865                         NMG_CK_EDGEUSE(eu);
01866                         if( eu->g.magic_p && *eu->g.magic_p == NMG_EDGE_G_CNURB_MAGIC )
01867                                 continue;
01868                         va = eu->vu_p->v_p;
01869                         vb = eu->eumate_p->vu_p->v_p;
01870                         NMG_CK_VERTEX(va);
01871                         NMG_CK_VERTEX(vb);
01872                         for( vp = (struct vertex **)BU_PTBL_LASTADDR(&verts);
01873                              vp >= (struct vertex **)BU_PTBL_BASEADDR(&verts);
01874                              vp--
01875                         )  {
01876                                 register struct vertex  *v;
01877                                 fastf_t                 dist;
01878                                 int                     code;
01879                                 struct edgeuse          *new_eu;
01880 
01881                                 v = *vp;
01882                                 /* very expensive to keep checking on this inner loop */
01883                                 /* NMG_CK_VERTEX(v); */
01884                                 if( va == v )  continue;
01885                                 if( vb == v )  continue;
01886 
01887                                 /* A good candidate for inline expansion */
01888                                 code = bn_isect_pt_lseg( &dist,
01889                                         va->vg_p->coord,
01890                                         vb->vg_p->coord,
01891                                         v->vg_p->coord, tol );
01892                                 if( code < 1 )  continue;       /* missed */
01893                                 if( code == 1 || code == 2 )  {
01894                                         bu_log("nmg_model_break_e_on_v() code=%d, why wasn't this vertex fused?\n", code);
01895                                         continue;
01896                                 }
01897 
01898                                 if (rt_g.NMG_debug & (DEBUG_BOOL|DEBUG_BASIC) )
01899                                         bu_log( "nmg_model_break_e_on_v(): breaking eu x%x (e=x%x) at vertex x%x\n", eu,eu->e_p, v );
01900 
01901                                 /* Break edge on vertex, but don't fuse yet. */
01902                                 new_eu = nmg_ebreak( v, eu );
01903                                 /* Put new edges into follow-on list */
01904                                 bu_ptbl_ins( &new_edgeuses, &new_eu->l.magic );
01905 
01906                                 /* reset vertex vb */
01907                                 vb = eu->eumate_p->vu_p->v_p;
01908                                 count++;
01909                         }
01910                 }
01911                 bu_ptbl_free( &edgeuses);
01912                 edgeuses = new_edgeuses;                /* struct copy */
01913         }
01914         bu_ptbl_free( &edgeuses);
01915         bu_ptbl_free( &verts);
01916         if (rt_g.NMG_debug & (DEBUG_BOOL|DEBUG_BASIC) )
01917                 bu_log("nmg_model_break_e_on_v() broke %d edges\n", count);
01918         return count;
01919 }
01920 
01921 /**
01922  *                      N M G _ M O D E L _ F U S E
01923  *
01924  *  This is the primary application interface to the geometry fusing support.
01925  *  Fuse together all data structures that are equal to each other,
01926  *  within tolerance.
01927  *
01928  *  The algorithm is three part:
01929  *      1)  Fuse together all vertices.
01930  *      2)  Fuse together all face geometry, where appropriate.
01931  *      3)  Fuse together all edges.
01932  *
01933  *  Edge fusing is handled last, because the difficult part there is
01934  *  sorting faces radially around the edge.
01935  *  It is important to know whether faces are shared or not
01936  *  at that point.
01937  *
01938  *  XXX It would be more efficient to build all the ptbl's at once,
01939  *  XXX with a single traversal of the model.
01940  */
01941 int
01942 nmg_model_fuse(struct model *m, const struct bn_tol *tol)
01943 {
01944         int     total = 0;
01945 
01946         NMG_CK_MODEL(m);
01947         BN_CK_TOL(tol);
01948 
01949         /* XXXX vertex fusing and edge breaking can produce vertices that are
01950          * not within tolerance of their face. Edge breaking needs to be moved
01951          * to step 1.5, then a routine to make sure all vertices are within
01952          * tolerance of owning face must be called if "total" is greater than zero.
01953          * This routine may have to triangulate the face if an appropriate plane
01954          * cannot be calculated.
01955          */
01956 
01957         /* Step 1 -- the vertices. */
01958         if( rt_g.NMG_debug & DEBUG_BASIC )
01959                 bu_log( "nmg_model_fuse: vertices\n" );
01960         total += nmg_model_vertex_fuse( m, tol );
01961 
01962         /* Step 1.5 -- break edges on vertices, before fusing edges */
01963         if( rt_g.NMG_debug & DEBUG_BASIC )
01964                 bu_log( "nmg_model_fuse: break edges\n" );
01965         total += nmg_model_break_e_on_v( m, tol );
01966 
01967         if( total )
01968         {
01969                 struct nmgregion *r;
01970                 struct shell *s;
01971 
01972                 /* vertices and/or edges have been moved,
01973                  * may have created out-of-tolerance faces
01974                  */
01975 
01976                 for( BU_LIST_FOR( r, nmgregion, &m->r_hd ) )
01977                 {
01978                         for( BU_LIST_FOR( s, shell, &r->s_hd ) )
01979                                 nmg_make_faces_within_tol( s, tol );
01980                 }
01981         }
01982 
01983         /* Step 2 -- the face geometry */
01984         if( rt_g.NMG_debug & DEBUG_BASIC )
01985                 bu_log( "nmg_model_fuse: faces\n" );
01986         total += nmg_model_face_fuse( m, tol );
01987 
01988         /* Step 3 -- edges */
01989         if( rt_g.NMG_debug & DEBUG_BASIC )
01990                 bu_log( "nmg_model_fuse: edges\n" );
01991         total += nmg_model_edge_fuse( m, tol );
01992 
01993         /* Step 4 -- edge geometry */
01994         if( rt_g.NMG_debug & DEBUG_BASIC )
01995                 bu_log( "nmg_model_fuse: edge geometries\n" );
01996         total += nmg_model_edge_g_fuse( m, tol );
01997 
01998         if( rt_g.NMG_debug & DEBUG_BASIC && total > 0 )
01999                 bu_log("nmg_model_fuse(): %d entities fused\n", total);
02000         return total;
02001 }
02002 
02003 
02004 /* -------------------- RADIAL -------------------- */
02005 
02006 /**
02007  *                      N M G _ R A D I A L _ S O R T E D _ L I S T _ I N S E R T
02008  *
02009  *  Build sorted list, with 'ang' running from zero to 2*pi.
02010  *  New edgeuses with same angle as an edgeuse already on the list
02011  *  are added AFTER the last existing one, for lack of any better way
02012  *  to break the tie.
02013  */
02014 void
02015 nmg_radial_sorted_list_insert(struct bu_list *hd, struct nmg_radial *rad)
02016 {
02017         struct nmg_radial       *cur;
02018         register fastf_t        rad_ang;
02019 
02020         BU_CK_LIST_HEAD(hd);
02021         NMG_CK_RADIAL(rad);
02022 
02023         if(BU_LIST_IS_EMPTY(hd))  {
02024                 BU_LIST_APPEND( hd, &rad->l );
02025                 return;
02026         }
02027 
02028         /* Put wires at the front */
02029         if( rad->fu == (struct faceuse *)NULL )  {
02030                 /* Add before first element */
02031                 BU_LIST_APPEND( hd, &rad->l );
02032                 return;
02033         }
02034 
02035         rad_ang = rad->ang;
02036 
02037         /*  Check for trivial append at end of list.
02038          *  This is a very common case, when input list is sorted.
02039          */
02040         cur = BU_LIST_PREV(nmg_radial, hd);
02041         if( cur->fu && rad_ang >= cur->ang )  {
02042                 BU_LIST_INSERT( hd, &rad->l );
02043                 return;
02044         }
02045 
02046         /* Brute force search through hd's list, going backwards */
02047         for( BU_LIST_FOR_BACKWARDS( cur, nmg_radial, hd ) )  {
02048                 if( cur->fu == (struct faceuse *)NULL )  continue;
02049                 if( rad_ang >= cur->ang )  {
02050                         BU_LIST_APPEND( &cur->l, &rad->l );
02051                         return;
02052                 }
02053         }
02054 
02055         /* Add before first element */
02056         BU_LIST_APPEND( hd, &rad->l );
02057 }
02058 
02059 /**
02060  *  Not only verity that list is monotone increasing, but that
02061  *  pointer integrity still exists.
02062  */
02063 void
02064 nmg_radial_verify_pointers(const struct bu_list *hd, const struct bn_tol *tol)
02065 {
02066         register struct nmg_radial      *rad;
02067         register fastf_t                amin = -64;
02068         register struct nmg_radial      *prev;
02069         register struct nmg_radial      *next;
02070 
02071         BU_CK_LIST_HEAD(hd);
02072         BN_CK_TOL(tol);
02073 
02074         /* Verify pointers increasing */
02075         for( BU_LIST_FOR( rad, nmg_radial, hd ) )  {
02076                 /* Verify pointer integrity */
02077                 prev = BU_LIST_PPREV_CIRC(nmg_radial, rad);
02078                 next = BU_LIST_PNEXT_CIRC(nmg_radial, rad);
02079                 if( rad->eu != prev->eu->radial_p->eumate_p )
02080                         rt_bomb("nmg_radial_verify_pointers() eu not radial+mate forw from prev\n");
02081                 if( rad->eu->eumate_p != prev->eu->radial_p )
02082                         rt_bomb("nmg_radial_verify_pointers() eumate not radial from prev\n");
02083                 if( rad->eu != next->eu->eumate_p->radial_p )
02084                         rt_bomb("nmg_radial_verify_pointers() eu not mate+radial back from next\n");
02085                 if( rad->eu->eumate_p != next->eu->eumate_p->radial_p->eumate_p )
02086                         rt_bomb("nmg_radial_verify_pointers() eumate not mate+radial+mate back from next\n");
02087 
02088                 if( rad->fu == (struct faceuse *)NULL )  continue;
02089                 if( rad->ang < amin )  {
02090                         nmg_pr_radial_list( hd, tol );
02091                         bu_log(" previous angle=%g > current=%g\n",
02092                                 amin*bn_radtodeg, rad->ang*bn_radtodeg);
02093                         rt_bomb("nmg_radial_verify_pointers() not monotone increasing\n");
02094                 }
02095                 amin = rad->ang;
02096         }
02097 }
02098 
02099 /**
02100  *
02101  *  Verify that the angles are monotone increasing.
02102  *  Wire edgeuses are ignored.
02103  */
02104 void
02105 nmg_radial_verify_monotone(const struct bu_list *hd, const struct bn_tol *tol)
02106 {
02107         register struct nmg_radial      *rad;
02108         register fastf_t                amin = -64;
02109 
02110         BU_CK_LIST_HEAD(hd);
02111         BN_CK_TOL(tol);
02112 
02113         /* Verify monotone increasing */
02114         for( BU_LIST_FOR( rad, nmg_radial, hd ) )  {
02115                 if( rad->fu == (struct faceuse *)NULL )  continue;
02116                 if( rad->ang < amin )  {
02117                         nmg_pr_radial_list( hd, tol );
02118                         bu_log(" previous angle=%g > current=%g\n",
02119                                 amin*bn_radtodeg, rad->ang*bn_radtodeg);
02120                         rt_bomb("nmg_radial_verify_monotone() not monotone increasing\n");
02121                 }
02122                 amin = rad->ang;
02123         }
02124 }
02125 
02126 /**             N M G _ I N S U R E _ L I S T _ I S _ I N C R E A S I N G
02127  *
02128  *      Check if the passed bu_list is in increasing order. If not,
02129  *      reverse the order of the list.
02130  * XXX Isn't the word "ensure"?
02131  */
02132 void
02133 nmg_insure_radial_list_is_increasing(struct bu_list *hd, fastf_t amin, fastf_t amax)
02134 {
02135         struct nmg_radial *rad;
02136         fastf_t cur_value=(-MAX_FASTF);
02137         int increasing=1;
02138 
02139         BU_CK_LIST_HEAD( hd );
02140 
02141         /* if we don't have more than 3 entries, it doesn't matter */
02142 
02143         if( bu_list_len( hd ) < 3 )
02144                 return;
02145 
02146         for( BU_LIST_FOR( rad, nmg_radial, hd ) )
02147         {
02148                 /* skip wire edges */
02149                 if( rad->fu == (struct faceuse *)NULL )
02150                         continue;
02151 
02152                 /* if increasing, just keep checking */
02153                 if( rad->ang >= cur_value )
02154                 {
02155                         cur_value = rad->ang;
02156                         continue;
02157                 }
02158 
02159                 /* angle decreases, is it going from max to min?? */
02160                 if( rad->ang == amin && cur_value == amax )
02161                 {
02162                         /* O.K., just went from max to min */
02163                         cur_value = rad->ang;
02164                         continue;
02165                 }
02166 
02167                 /* if we get here, this list is not increasing!!! */
02168                 increasing = 0;
02169                 break;
02170         }
02171 
02172         if( increasing )        /* all is well */
02173                 return;
02174 
02175         /* reverse order of the list */
02176         bu_list_reverse( hd );
02177 
02178         /* Need to exchange eu with eu->eumate_p for each eu on the list */
02179         for( BU_LIST_FOR( rad, nmg_radial, hd ) )
02180         {
02181                 rad->eu = rad->eu->eumate_p;
02182                 rad->fu = nmg_find_fu_of_eu( rad->eu );
02183         }
02184 }
02185 
02186 /**
02187  *                      N M G _ R A D I A L _ B U I L D _ L I S T
02188  *
02189  *  The coordinate system is expected to have been chosen in such a
02190  *  way that the radial list of faces around this edge are circularly
02191  *  increasing (CCW) in their angle.  Put them in the list in exactly
02192  *  the order they occur around the edge.  Then, at the end, move the
02193  *  list head to lie between the maximum and minimum angles, so that the
02194  *  list head is crossed as the angle goes around through zero.
02195  *  Now the list is monotone increasing.
02196  *
02197  *  The edgeuse's radial pointer takes us in the CCW direction.
02198  *
02199  *  If the list contains nmg_radial structures r1, r2, r3, r4,
02200  *  then going CCW around the edge we will encounter:
02201  *
02202  *                      (from i-1)                      (from i+1)
02203  *  r1->eu->eumate_p    r4->eu->radial_p                r2->eu->eumate_p->radial_p->eumate_p
02204  *  r1->eu              r4->eu->radial_p->eumate_p      r2->eu->eumate_p->radial_p
02205  *  r2->eu->eumate_p    r1->eu->radial_p                r3->eu->eumate_p->radial_p->eumate_p
02206  *  r2->eu              r1->eu->radial_p->eumate_p      r3->eu->eumate_p->radial_p
02207  *  r3->eu->eumate_p    r2->eu->radial_p                r4->eu->eumate_p->radial_p->eumate_p
02208  *  r3->eu              r2->eu->radial_p->eumate_p      r4->eu->eumate_p->radial_p
02209  *  r4->eu->eumate_p    r3->eu->radial_p                r1->eu->eumate_p->radial_p->eumate_p
02210  *  r4->eu              r3->eu->radial_p->eumate_p      r1->eu->eumate_p->radial_p
02211  */
02212 void
02213 nmg_radial_build_list(struct bu_list *hd, struct bu_ptbl *shell_tbl, int existing, struct edgeuse *eu, const fastf_t *xvec, const fastf_t *yvec, const fastf_t *zvec, const struct bn_tol *tol)
02214 
02215                                         /* may be null */
02216 
02217 
02218 
02219 
02220 
02221                                         /* for printing */
02222 {
02223         struct edgeuse          *teu;
02224         struct nmg_radial       *rad;
02225         fastf_t                 amin;
02226         fastf_t                 amax;
02227         int                     non_wire_edges=0;
02228         struct nmg_radial       *rmin = (struct nmg_radial *)NULL;
02229         struct nmg_radial       *rmax = (struct nmg_radial *)NULL;
02230         struct nmg_radial       *first;
02231 
02232         BU_CK_LIST_HEAD(hd);
02233         if(shell_tbl) BU_CK_PTBL(shell_tbl);
02234         NMG_CK_EDGEUSE(eu);
02235         BN_CK_TOL(tol);
02236 
02237         if( rt_g.NMG_debug & DEBUG_BASIC || rt_g.NMG_debug & DEBUG_MESH_EU )
02238                 bu_log("nmg_radial_build_list( existing=%d, eu=x%x )\n", existing, eu );
02239 
02240         amin = 64;
02241         amax = -64;
02242 
02243         teu = eu;
02244         for(;;)  {
02245                 BU_GETSTRUCT( rad, nmg_radial );
02246                 rad->l.magic = NMG_RADIAL_MAGIC;
02247                 rad->eu = teu;
02248                 rad->fu = nmg_find_fu_of_eu( teu );
02249                 if( rad->fu )  {
02250                         /* We depend on ang being strictly in the range 0..2pi */
02251                         rad->ang = nmg_measure_fu_angle( teu, xvec, yvec, zvec );
02252                         non_wire_edges++;
02253 
02254                         if( rad->ang < amin )  {
02255                                 amin = rad->ang;
02256                                 rmin = rad;
02257                         }
02258                         if( rad->ang > amax )  {
02259                                 amax = rad->ang;
02260                                 rmax = rad;
02261                         }
02262                 } else {
02263                         /* Wire edge.  Set a preposterous angle */
02264                         rad->ang = -bn_pi;      /* -180 */
02265                 }
02266                 rad->s = nmg_find_s_of_eu( teu );
02267                 rad->existing_flag = existing;
02268                 rad->needs_flip = 0;    /* not yet determined */
02269                 rad->is_crack = 0;      /* not yet determined */
02270                 rad->is_outie = 0;      /* not yet determined */
02271 
02272                 if( rt_g.NMG_debug & DEBUG_MESH_EU )
02273                         bu_log( "\trad->eu = %x, rad->ang = %g\n", rad->eu, rad->ang );
02274 
02275                 /* Just append.  Should already be properly sorted. */
02276                 BU_LIST_INSERT( hd, &(rad->l) );
02277 
02278                 /* Add to list of all shells involved at this edge */
02279                 if(shell_tbl) bu_ptbl_ins_unique( shell_tbl, &(rad->s->l.magic) );
02280 
02281                 /* Advance to next edgeuse pair */
02282                 teu = teu->radial_p->eumate_p;
02283                 if( teu == eu )  break;
02284         }
02285 
02286         nmg_insure_radial_list_is_increasing( hd, amin, amax );
02287 
02288         /* Increasing, with min or max value possibly repeated */
02289         if( !rmin )  {
02290                 /* Nothing but wire edgeuses, done. */
02291                 return;
02292         }
02293 
02294         /* At least one non-wire edgeuse was found */
02295         if( non_wire_edges < 2 )
02296         {
02297                 /* only one non wire entry in list */
02298                 return;
02299         }
02300 
02301         if( rt_g.NMG_debug & DEBUG_MESH_EU )
02302         {
02303                 struct nmg_radial *next;
02304 
02305                 bu_log("amin=%g min_eu=x%x, amax=%g max_eu=x%x\n",
02306                 rmin->ang * bn_radtodeg, rmin->eu,
02307                 rmax->ang * bn_radtodeg, rmax->eu );
02308 
02309                 for( BU_LIST_FOR( next, nmg_radial, hd ) )
02310                         bu_log( "%x: eu=%x, fu=%x, ang=%g\n" , next, next->eu, next->fu, next->ang );
02311         }
02312 
02313         /* Skip to extremal repeated max&min.  Ignore wires */
02314         first = rmax;
02315         for(;;)  {
02316                 struct nmg_radial       *next;
02317                 next = rmax;
02318                 do {
02319                         next = BU_LIST_PNEXT_CIRC(nmg_radial, next);
02320                 } while( next->fu == (struct faceuse *)NULL );
02321                 if( next->ang >= amax )
02322                 {
02323                         rmax = next;            /* a repeated max */
02324                         if( rmax == first )     /* we have gone all the way around (All angles are same ) */
02325                                 break;
02326                 }
02327                 else
02328                         break;
02329         }
02330         /* wires before min establish new rmin */
02331         first = rmin;
02332         for(;;)  {
02333                 struct nmg_radial       *next;
02334 
02335                 while( (next = BU_LIST_PPREV_CIRC(nmg_radial, rmin))->fu == (struct faceuse *)NULL )
02336                         rmin = next;
02337                 next = BU_LIST_PPREV_CIRC(nmg_radial, rmin);
02338                 if( next->ang <= amin )
02339                 {
02340                         rmin = next;            /* a repeated min */
02341                         if( rmin == first )     /* all the way round again (All angles are same ) */
02342                         {
02343                                 /* set rmin to next entry after rmax */
02344                                 rmin = BU_LIST_PNEXT_CIRC( nmg_radial, rmax );
02345                                 break;
02346                         }
02347                 }
02348                 else
02349                         break;
02350         }
02351 
02352         /* Move list head so that it is inbetween min and max entries. */
02353         if( BU_LIST_PNEXT_CIRC(nmg_radial, rmax) == rmin )  {
02354                 /* Maximum entry is followed by minimum.  Ascending --> CCW */
02355                 BU_LIST_DEQUEUE( hd );
02356                 /* Append head after maximum, before minimum */
02357                 BU_LIST_APPEND( &(rmax->l), hd );
02358                 nmg_radial_verify_pointers( hd, tol );
02359         } else {
02360                 bu_log("  %f %f %f --- %f %f %f\n",
02361                         V3ARGS(eu->vu_p->v_p->vg_p->coord),
02362                         V3ARGS(eu->eumate_p->vu_p->v_p->vg_p->coord) );
02363                 bu_log("amin=%g min_eu=x%x, amax=%g max_eu=x%x B\n",
02364                         rmin->ang * bn_radtodeg, rmin->eu,
02365                         rmax->ang * bn_radtodeg, rmax->eu );
02366                 nmg_pr_radial_list( hd, tol );
02367                 nmg_pr_fu_around_eu_vecs( eu, xvec, yvec, zvec, tol );
02368                 rt_bomb("nmg_radial_build_list() min and max angle not adjacent in list (or list not monotone increasing)\n");
02369         }
02370 }
02371 
02372 /**
02373  *                      N M G _ R A D I A L _ M E R G E _ L I S T S
02374  *
02375  *  Merge all of the src list into the dest list, sorting by angles.
02376  */
02377 void
02378 nmg_radial_merge_lists(struct bu_list *dest, struct bu_list *src, const struct bn_tol *tol)
02379 {
02380         struct nmg_radial       *rad;
02381 
02382         BU_CK_LIST_HEAD(dest);
02383         BU_CK_LIST_HEAD(src);
02384         BN_CK_TOL(tol);
02385 
02386         while( BU_LIST_WHILE( rad, nmg_radial, src ) )  {
02387                 BU_LIST_DEQUEUE( &rad->l );
02388                 nmg_radial_sorted_list_insert( dest, rad );
02389         }
02390 }
02391 
02392 /**
02393  *                      N M G _ I S _ C R A C K _ O U T I E
02394  *
02395  *  If there is more than one edgeuse of a loopuse along an edge, then
02396  *  it is a "topological crack".  There are two kinds, an "innie",
02397  *  where the crack is a null-area incursion into the interior of the loop,
02398  *  and an "outie", where the crack is a null-area protrusion outside
02399  *  of the interior of the loop.
02400  *
02401  *                       "Outie"                 "Innie"
02402  *                      *-------*               *-------*
02403  *                      |       ^               |       ^
02404  *                      v       |               v       |
02405  *              *<------*       |               *--->*  |
02406  *              *---M-->*       |               *<-M-*  |
02407  *                      |       |               |       |
02408  *                      v       |               v       |
02409  *                      *------>*               *------>*
02410  *
02411  *  The algorithm used is to compute the geometric midpoint of the crack
02412  *  edge, "delete" that edge from the loop, and then classify the midpoint
02413  *  ("M") against the remainder of the loop.  If the edge midpoint
02414  *  is inside the remains of the loop, then the crack is an "innie",
02415  *  otherwise it is an "outie".
02416  *
02417  *  When there are an odd number of edgeuses along the crack, then the
02418  *  situation is "nasty":
02419  *
02420  *                       "Nasty"
02421  *                      *-------*
02422  *                      |       ^
02423  *                      v       |
02424  *              *<------*       |
02425  *              *------>*       |
02426  *              *<------*       |
02427  *              *------>*       |
02428  *              *<------*       |
02429  *              |               |
02430  *              |               |
02431  *              v               |
02432  *              *------------->*
02433  *
02434  *  The caller is responsible for making sure that the edgeuse is not
02435  *  a wire edgeuse (i.e. that the edgeuse is part of a loop).
02436  *
02437  *  In the "Nasty" case, all the edgeuse pairs are "outies" except for
02438  *  the last lone edgeuse, which should be handled as a "non-crack".
02439  *  Walk the loopuse's edgeuses list in edgeuse order to see which one
02440  *  is the last (non-crack) repeated edgeuse.
02441  *  For efficiency, detecting and dealing with this condition is left
02442  *  up to the caller, and is not checked for here.
02443  */
02444 int
02445 nmg_is_crack_outie(const struct edgeuse *eu, const struct bn_tol *tol)
02446 {
02447         const struct loopuse    *lu;
02448         const struct edge       *e;
02449         point_t                 midpt;
02450         const fastf_t           *a, *b;
02451         int                     class;
02452 
02453         NMG_CK_EDGEUSE(eu);
02454         BN_CK_TOL(tol);
02455 
02456         lu = eu->up.lu_p;
02457         NMG_CK_LOOPUSE(lu);
02458         e = eu->e_p;
02459         NMG_CK_EDGE(e);
02460 
02461         /* If ENTIRE loop is a crack, there is no surface area, it's an outie */
02462         if( nmg_loop_is_a_crack( lu ) )  return 1;
02463 
02464         a = eu->vu_p->v_p->vg_p->coord;
02465         b = eu->eumate_p->vu_p->v_p->vg_p->coord;
02466         VADD2SCALE( midpt, a, b, 0.5 );
02467 
02468         /* Ensure edge is long enough so midpoint is not within tol of verts */
02469         {
02470 #if 1
02471                 /* all we want here is a classification of the midpoint,
02472                  * so let's create a temporary tolerance that will work!!! */
02473 
02474                 struct bn_tol tmp_tol;
02475                 struct faceuse          *fu;
02476                 plane_t                 pl;
02477                 fastf_t                 dist;
02478 
02479                 tmp_tol = (*tol);
02480                 if( *lu->up.magic_p != NMG_FACEUSE_MAGIC )
02481                         rt_bomb( "Nmg_is_crack_outie called with non-face loop" );
02482 
02483                 fu = lu->up.fu_p;
02484                 NMG_CK_FACEUSE( fu );
02485                 NMG_GET_FU_PLANE( pl, fu );
02486                 dist = DIST_PT_PLANE( midpt, pl );
02487                 VJOIN1( midpt, midpt , -dist, pl )
02488                 dist = fabs( DIST_PT_PLANE( midpt, pl ) );
02489                 if( dist > SQRT_SMALL_FASTF )
02490                 {
02491                         tmp_tol.dist = dist*2.0;
02492                         tmp_tol.dist_sq = tmp_tol.dist * tmp_tol.dist;
02493                 }
02494                 else
02495                 {
02496                         tmp_tol.dist = SQRT_SMALL_FASTF;
02497                         tmp_tol.dist_sq = SMALL_FASTF;
02498                 }
02499                 class = nmg_class_pt_lu_except( midpt, lu, e, &tmp_tol );
02500 #else
02501                 rt_pr_tol(tol);
02502                 bu_log(" eu=x%x, len=%g\n", eu, MAGNITUDE(diff) );
02503                 rt_bomb("nmg_is_crack_outie() edge is too short to bisect.  Increase tolerance and re-run.\n");
02504 #endif
02505         }
02506         if( rt_g.NMG_debug & DEBUG_BASIC )  {
02507                 bu_log("nmg_is_crack_outie(eu=x%x) lu=x%x, e=x%x, class=%s\n",
02508                         eu, lu, e, nmg_class_name(class) );
02509         }
02510 
02511         if( lu->orientation == OT_SAME )  {
02512                 if( class == NMG_CLASS_AinB || class == NMG_CLASS_AonBshared )
02513                         return 0;               /* an "innie" */
02514                 if( class == NMG_CLASS_AoutB )
02515                         return 1;               /* an "outie" */
02516         } else {
02517                 /* It's a hole loop, things work backwards. */
02518                 if( class == NMG_CLASS_AinB || class == NMG_CLASS_AonBshared )
02519                         return 1;               /* an "outie" */
02520                 if( class == NMG_CLASS_AoutB )
02521                         return 0;               /* an "innie" */
02522         }
02523 
02524         /* Other classifications "shouldn't happen". */
02525         bu_log("nmg_is_crack_outie(eu=x%x), lu=x%x(%s)\n  midpt_class=%s, midpt=(%g, %g, %g)\n",
02526                 eu,
02527                 lu, nmg_orientation(lu->orientation),
02528                 nmg_class_name(class),
02529                 V3ARGS(midpt) );
02530         nmg_pr_lu_briefly( lu, 0 );
02531         rt_bomb("nmg_is_crack_outie() got unexpected midpt classification from nmg_class_pt_lu_except()\n");
02532 
02533         return( -1 ); /* make the compiler happy */
02534 }
02535 
02536 /**
02537  *                      N M G _ F I N D _ R A D I A L _ E U
02538  */
02539 struct nmg_radial *
02540 nmg_find_radial_eu(const struct bu_list *hd, const struct edgeuse *eu)
02541 {
02542         register struct nmg_radial      *rad;
02543 
02544         BU_CK_LIST_HEAD(hd);
02545         NMG_CK_EDGEUSE(eu);
02546 
02547         for( BU_LIST_FOR( rad, nmg_radial, hd ) )  {
02548                 if( rad->eu == eu )  return rad;
02549                 if( rad->eu->eumate_p == eu )  return rad;
02550         }
02551         bu_log("nmg_find_radial_eu() eu=x%x\n", eu);
02552         rt_bomb("nmg_find_radial_eu() given edgeuse not found on list\n");
02553 
02554         return( (struct nmg_radial *)NULL );
02555 }
02556 
02557 /**
02558  *                      N M G _ F I N D _ N E X T _ U S E _ O F _ 2 E _ I N _ L U
02559  *
02560  *  Find the next use of either of two edges in the loopuse.
02561  *  The second edge pointer may be NULL.
02562  */
02563 const struct edgeuse *
02564 nmg_find_next_use_of_2e_in_lu(const struct edgeuse *eu, const struct edge *e1, const struct edge *e2)
02565 
02566 
02567                                         /* may be NULL */
02568 {
02569         register const struct edgeuse   *neu;
02570 
02571         NMG_CK_EDGEUSE(eu);
02572         NMG_CK_LOOPUSE(eu->up.lu_p);    /* sanity */
02573         NMG_CK_EDGE(e1);
02574         if(e2) NMG_CK_EDGE(e2);
02575 
02576         neu = eu;
02577         do  {
02578                 neu = BU_LIST_PNEXT_CIRC( edgeuse, &neu->l );
02579         } while( neu->e_p != e1 && neu->e_p != e2 );
02580         return neu;
02581 
02582 }
02583 
02584 /**
02585  *                      N M G _ R A D I A L _ M A R K _ C R A C K S
02586  *
02587  *  For every edgeuse, if there are other edgeuses around this edge
02588  *  from the same face, then mark them all as part of a "crack".
02589  *
02590  *  To be a crack the two edgeuses must be from the same loopuse.
02591  *
02592  *  If the count of repeated ("crack") edgeuses is even, then
02593  *  classify the entire crack as an "innie" or an "outie".
02594  *  If the count is odd, this is a "Nasty" --
02595  *  all but one edgeuse are marked as "outies",
02596  *  and the remaining one is marked as a non-crack.
02597  *  The "outie" edgeuses are marked off in pairs,
02598  *  in the loopuses's edgeuse order.
02599  */
02600 void
02601 nmg_radial_mark_cracks(struct bu_list *hd, const struct edge *e1, const struct edge *e2, const struct bn_tol *tol)
02602 
02603 
02604                                         /* may be NULL */
02605 
02606 {
02607         struct nmg_radial       *rad;
02608         struct nmg_radial       *other;
02609         const struct loopuse    *lu;
02610         const struct edgeuse    *eu;
02611         register int            uses;
02612         int                     outie = -1;
02613 
02614         BU_CK_LIST_HEAD(hd);
02615         NMG_CK_EDGE(e1);
02616         if(e2) NMG_CK_EDGE(e2);
02617         BN_CK_TOL(tol);
02618 
02619         for( BU_LIST_FOR( rad, nmg_radial, hd ) )  {
02620                 NMG_CK_RADIAL(rad);
02621                 if( rad->is_crack )  continue;
02622                 if( !rad->fu ) continue;                /* skip wire edges */
02623                 lu = rad->eu->up.lu_p;
02624                 uses = 0;
02625 
02626                 /* Search the remainder of the list for other uses */
02627                 for( other = BU_LIST_PNEXT( nmg_radial, rad );
02628                      BU_LIST_NOT_HEAD( other, hd );
02629                      other = BU_LIST_PNEXT( nmg_radial, other )
02630                 )  {
02631                         if( !other->fu ) continue;      /* skip wire edges */
02632                         /* Only consider edgeuses from the same loopuse */
02633                         if( other->eu->up.lu_p != lu &&
02634                             other->eu->eumate_p->up.lu_p != lu )
02635                                 continue;
02636                         uses++;
02637                 }
02638                 if( uses <= 0 )  {
02639                         /* The main search continues to end of list */
02640                         continue;               /* not a crack */
02641                 }
02642                 uses++;         /* account for first use too */
02643 
02644                 /* OK, we have a crack. Which kind? */
02645                 if( (uses & 1) == 0 )  {
02646                         /* Even number of edgeuses. */
02647                         outie = nmg_is_crack_outie( rad->eu, tol );
02648                         rad->is_crack = 1;
02649                         rad->is_outie = outie;
02650                         if (rt_g.NMG_debug & DEBUG_MESH_EU )  {
02651                                 bu_log( "nmg_radial_mark_cracks() EVEN crack eu=x%x, uses=%d, outie=%d\n",
02652                                         rad->eu, uses, outie );
02653                         }
02654                         /* Mark all the rest of them the same way */
02655                         for( other = BU_LIST_PNEXT( nmg_radial, rad );
02656                              BU_LIST_NOT_HEAD( other, hd );
02657                              other = BU_LIST_PNEXT( nmg_radial, other )
02658                         )  {
02659                                 if( !other->fu ) continue;      /* skip wire edges */
02660                                 /* Only consider edgeuses from the same loopuse */
02661                                 if( other->eu->up.lu_p != lu &&
02662                                     other->eu->eumate_p->up.lu_p != lu )
02663                                         continue;
02664                                 other->is_crack = 1;
02665                                 other->is_outie = outie;
02666                         }
02667                         if (rt_g.NMG_debug & DEBUG_MESH_EU )  {
02668                                 bu_log("Printing loopuse and resulting radial list:\n");
02669                                 nmg_pr_lu_briefly( lu, 0 );
02670                                 nmg_pr_radial_list( hd, tol );
02671                         }
02672                         continue;
02673                 }
02674                 /*
02675                  *  Odd number of edgeuses.  Traverse in loopuse order.
02676                  *  All but the last one are "outies", last one is "innie"
02677                  */
02678                 if (rt_g.NMG_debug & DEBUG_MESH_EU )  {
02679                         bu_log( "nmg_radial_mark_cracks() ODD crack eu=x%x, uses=%d, outie=%d\n",
02680                                 rad->eu, uses, outie );
02681                 }
02682                 /* Mark off pairs of edgeuses, one per trip through loop. */
02683                 eu = rad->eu;
02684                 for( ; uses >= 2; uses-- )  {
02685                         eu = nmg_find_next_use_of_2e_in_lu( eu, e1, e2 );
02686                         if (rt_g.NMG_debug & DEBUG_MESH_EU )  {
02687                                 bu_log("rad->eu=x%x, eu=x%x, uses=%d\n",
02688                                         rad->eu, eu, uses);
02689                         }
02690                         if( eu == rad->eu )  {
02691                                 nmg_pr_lu_briefly( lu, 0 );
02692                                 nmg_pr_radial_list( hd, tol );
02693                                 rt_bomb("nmg_radial_mark_cracks() loop too short!\n");
02694                         }
02695 
02696                         other = nmg_find_radial_eu( hd, eu );
02697                         /* Mark 'em as "outies" */
02698                         other->is_crack = 1;
02699                         other->is_outie = 1;
02700                 }
02701 
02702                 /* Should only be one left, this one is an "innie":  it borders surface area */
02703                 eu = nmg_find_next_use_of_2e_in_lu( eu, e1, e2 );
02704                 if( eu != rad->eu )  {
02705                         nmg_pr_lu_briefly( lu, 0 );
02706                         nmg_pr_radial_list( hd, tol );
02707                         rt_bomb("nmg_radial_mark_cracks() loop didn't return to start\n");
02708                 }
02709 
02710                 rad->is_crack = 1;
02711                 rad->is_outie = 0;              /* "innie" */
02712         }
02713 }
02714 
02715 /**
02716  *                      N M G _ R A D I A L _ F I N D _ A N _ O R I G I N A L
02717  *
02718  *  Returns -
02719  *      NULL            No edgeuses from indicated shell on this list
02720  *      nmg_radial*     An original, else first newbie, else a newbie crack.
02721  */
02722 struct nmg_radial *
02723 nmg_radial_find_an_original(const struct bu_list *hd, const struct shell *s, const struct bn_tol *tol)
02724 {
02725         register struct nmg_radial      *rad;
02726         struct nmg_radial       *fallback = (struct nmg_radial *)NULL;
02727         int                     seen_shell = 0;
02728 
02729         BU_CK_LIST_HEAD(hd);
02730         NMG_CK_SHELL(s);
02731 
02732         /* First choice:  find an original, non-crack, non-wire */
02733         for( BU_LIST_FOR( rad, nmg_radial, hd ) )  {
02734                 NMG_CK_RADIAL(rad);
02735                 if( rad->s != s )  continue;
02736                 seen_shell++;
02737                 if( rad->is_outie )  {
02738                         fallback = rad;
02739                         continue;       /* skip "outie" cracks */
02740                 }
02741                 if( !rad->fu )  continue;       /* skip wires */
02742                 if( rad->existing_flag )  return rad;
02743         }
02744         if( !seen_shell )  return (struct nmg_radial *)NULL;    /* No edgeuses from that shell, at all! */
02745 
02746         /* Next, an original crack would be OK */
02747         if(fallback) return fallback;
02748 
02749         /* If there were no originals, find first newbie */
02750         for( BU_LIST_FOR( rad, nmg_radial, hd ) )  {
02751                 if( rad->s != s )  continue;
02752                 if( rad->is_outie )  {
02753                         fallback = rad;
02754                         continue;       /* skip "outie" cracks */
02755                 }
02756                 if( !rad->fu )  {
02757                         continue;       /* skip wires */
02758                 }
02759                 return rad;
02760         }
02761         /* If no ordinary newbies, provide a newbie crack */
02762         if(fallback) return fallback;
02763 
02764         /* No ordinary newbiew or newbie cracks, any wires? */
02765         for( BU_LIST_FOR( rad, nmg_radial, hd ) )  {
02766                 if( rad->s != s )  continue;
02767                 if( rad->is_outie )  {
02768                         continue;       /* skip "outie" cracks */
02769                 }
02770                 if( !rad->fu )  {
02771                         fallback = rad;
02772                         continue;       /* skip wires */
02773                 }
02774                 return rad;
02775         }
02776         if(fallback) return fallback;
02777 
02778         bu_log("nmg_radial_find_an_original() shell=x%x\n", s);
02779         nmg_pr_radial_list( hd, tol );
02780         rt_bomb("nmg_radial_find_an_original() No entries from indicated shell\n");
02781 
02782         return( (struct nmg_radial *)NULL );
02783 }
02784 
02785 /**
02786  *                      N M G _ R A D I A L _ M A R K _ F L I P S
02787  *
02788  *  For a given shell, find an original edgeuse from that shell,
02789  *  and then mark parity violators with a "flip" flag.
02790  */
02791 int
02792 nmg_radial_mark_flips(struct bu_list *hd, const struct shell *s, const struct bn_tol *tol)
02793 {
02794         struct nmg_radial       *rad;
02795         struct nmg_radial       *orig;
02796         register int            expected_ot;
02797         int                     count = 0;
02798         int                     nflip = 0;
02799 
02800         BU_CK_LIST_HEAD(hd);
02801         NMG_CK_SHELL(s);
02802         BN_CK_TOL(tol);
02803 
02804         orig = nmg_radial_find_an_original( hd, s, tol );
02805         NMG_CK_RADIAL(orig);
02806         if( orig->is_outie )  {
02807                 /* Only originals were "outie" cracks.  No flipping */
02808                 return 0;
02809         }
02810 
02811         if( !orig->fu )
02812         {
02813                 /* nothing but wires */
02814                 return( 0 );
02815         }
02816         if( !orig->existing_flag )  {
02817                 /* There were no originals.  Do something sensible to check the newbies */
02818                 if( !orig->fu )  {
02819                         /* Nothing but wires */
02820                         return 0;
02821                 }
02822 #if 0
02823                 /*  Given that there were no existing edgeuses
02824                  *  from this shell, make our first addition
02825                  *  oppose the previous radial faceuse, just to be nice.
02826                  */
02827                 rad = BU_LIST_PPREV_CIRC( nmg_radial, orig );
02828                 /* Hopefully it isn't a wire */
02829                 if( rad->eu->vu_p->v_p == orig->eu->vu_p->v_p )  {
02830                         /* Flip everything, for general compatibility */
02831                         for( BU_LIST_FOR( rad, nmg_radial, hd ) )  {
02832                                 if( rad->s != s )  continue;
02833                                 if( !rad->fu )  continue;       /* skip wires */
02834                                 rad->needs_flip = !rad->needs_flip;
02835                         }
02836                 }
02837 #endif
02838         }
02839 
02840         expected_ot = !(orig->fu->orientation == OT_SAME);
02841         if( !orig->is_outie ) count++;  /* Don't count orig if "outie" crack */
02842 
02843         for( BU_LIST_FOR_CIRC( rad, nmg_radial, orig ) )  {
02844                 if( rad->s != s )  continue;
02845                 if( !rad->fu )  continue;       /* skip wires */
02846                 if( rad->is_outie ) continue;   /* skip "outie" cracks */
02847                 count++;
02848                 if( expected_ot == (rad->fu->orientation == OT_SAME) )  {
02849                         expected_ot = !expected_ot;
02850                         continue;
02851                 }
02852                 /* Mis-match detected */
02853                 if (rt_g.NMG_debug & DEBUG_MESH_EU ) {
02854                         bu_log("nmg_radial_mark_flips() Mis-match detected, setting flip flag eu=x%x\n", rad->eu);
02855                 }
02856                 rad->needs_flip = !rad->needs_flip;
02857                 nflip++;
02858                 /* With this one flipped, set expectation for next */
02859                 expected_ot = !expected_ot;
02860         }
02861 
02862         if( count & 1 )  {
02863 #if 0
02864                 bu_log("nmg_radial_mark_flips() NOTICE dangling fu=x%x detected at eu=x%x in shell=x%x, proceeding.\n  %g %g %g --- %g %g %g\n",
02865                         orig->fu, orig->eu, s,
02866                         V3ARGS(orig->eu->vu_p->v_p->vg_p->coord),
02867                         V3ARGS(orig->eu->eumate_p->vu_p->v_p->vg_p->coord)
02868                 );
02869                 nmg_pr_radial_list( hd, tol );
02870 #endif
02871                 return count;
02872         }
02873 
02874         if( expected_ot == (orig->fu->orientation == OT_SAME) )
02875                 return nflip;
02876 
02877         bu_log("nmg_radial_mark_flips() unable to establish proper orientation parity.\n  eu count=%d, shell=x%x, expectation=%d\n",
02878                 count, s, expected_ot);
02879         nmg_pr_radial_list( hd, tol );
02880         rt_bomb("nmg_radial_mark_flips() unable to establish proper orientation parity.\n");
02881 
02882         return( 0 ); /* for compiler */
02883 }
02884 
02885 /**
02886  *                      N M G _ R A D I A L _ C H E C K _ P A R I T Y
02887  *
02888  *  For each shell, check orientation parity of edgeuses within that shell.
02889  */
02890 int
02891 nmg_radial_check_parity(const struct bu_list *hd, const struct bu_ptbl *shells, const struct bn_tol *tol)
02892 {
02893         struct nmg_radial       *rad;
02894         struct shell            **sp;
02895         struct nmg_radial       *orig;
02896         register int            expected_ot;
02897         int                     count = 0;
02898 
02899         BU_CK_LIST_HEAD(hd);
02900         BU_CK_PTBL(shells);
02901         BN_CK_TOL(tol);
02902 
02903         for( sp = (struct shell **)BU_PTBL_LASTADDR(shells);
02904              sp >= (struct shell **)BU_PTBL_BASEADDR(shells); sp--
02905         )  {
02906 
02907                 NMG_CK_SHELL(*sp);
02908                 orig = nmg_radial_find_an_original( hd, *sp, tol );
02909                 if( !orig )  continue;
02910                 NMG_CK_RADIAL(orig);
02911                 if( !orig->existing_flag )  {
02912                         /* There were no originals.  Do something sensible to check the newbies */
02913                         if( !orig->fu )  continue;      /* Nothing but wires */
02914                 }
02915                 if( orig->is_outie )  continue; /* Loop was nothing but outies */
02916                 expected_ot = !(orig->fu->orientation == OT_SAME);
02917 
02918                 for( BU_LIST_FOR_CIRC( rad, nmg_radial, orig ) )  {
02919                         if( rad->s != *sp )  continue;
02920                         if( !rad->fu )  continue;       /* skip wires */
02921                         if( rad->is_outie ) continue;   /* skip "outie" cracks */
02922                         if( expected_ot == (rad->fu->orientation == OT_SAME) )  {
02923                                 expected_ot = !expected_ot;
02924                                 continue;
02925                         }
02926                         /* Mis-match detected */
02927                         bu_log("nmg_radial_check_parity() bad parity eu=x%x, s=x%x\n",
02928                                 rad->eu, *sp);
02929                         count++;
02930                         /* Set expectation for next */
02931                         expected_ot = !expected_ot;
02932                 }
02933                 if( expected_ot == (orig->fu->orientation == OT_SAME) )
02934                         continue;
02935                 bu_log("nmg_radial_check_parity() bad parity at END eu=x%x, s=x%x\n",
02936                         rad->eu, *sp);
02937                 count++;
02938         }
02939         return count;
02940 }
02941 
02942 /**
02943  *                      N M G _ R A D I A L _ I M P L E M E N T _ D E C I S I O N S
02944  *
02945  *  For all non-original edgeuses in the list, place them in the proper
02946  *  place around the destination edge.
02947  */
02948 void
02949 nmg_radial_implement_decisions(struct bu_list *hd, const struct bn_tol *tol, struct edgeuse *eu1, fastf_t *xvec, fastf_t *yvec, fastf_t *zvec)
02950 
02951                                         /* for printing */
02952                                 /* temp */
02953                                                 /*** temp ***/
02954 {
02955         struct nmg_radial       *rad;
02956         struct nmg_radial       *prev;
02957         int                     skipped;
02958 
02959         BU_CK_LIST_HEAD(hd);
02960         BN_CK_TOL(tol);
02961 
02962         if( rt_g.NMG_debug & DEBUG_BASIC )
02963                 bu_log("nmg_radial_implement_decisions() BEGIN\n");
02964 
02965 again:
02966         skipped = 0;
02967         for( BU_LIST_FOR( rad, nmg_radial, hd ) )  {
02968                 struct edgeuse  *dest;
02969 
02970                 if( rad->existing_flag )  continue;
02971                 prev = BU_LIST_PPREV_CIRC( nmg_radial, rad );
02972                 if( !prev->existing_flag )  {
02973                         /* Previous eu isn't in place yet, can't do this one until next pass. */
02974                         skipped++;
02975                         continue;
02976                 }
02977 
02978                 /*
02979                  *  Insert "rad" CCW radial from "prev".
02980                  *
02981                  */
02982                 if (rt_g.NMG_debug & DEBUG_MESH_EU ) {
02983                         bu_log("Before -- ");
02984                         nmg_pr_fu_around_eu_vecs( eu1, xvec, yvec, zvec, tol );
02985                         nmg_pr_radial("prev:", (const struct nmg_radial *)prev);
02986                         nmg_pr_radial(" rad:", (const struct nmg_radial *)rad);
02987                 }
02988                 dest = prev->eu;
02989                 if( rad->needs_flip )  {
02990                         struct edgeuse  *other_eu = rad->eu->eumate_p;
02991 
02992                         nmg_je( dest, rad->eu );
02993                         rad->eu = other_eu;
02994                         rad->fu = nmg_find_fu_of_eu( other_eu );
02995                         rad->needs_flip = 0;
02996                 } else {
02997                         nmg_je( dest, rad->eu->eumate_p );
02998                 }
02999                 rad->existing_flag = 1;
03000                 if (rt_g.NMG_debug & DEBUG_MESH_EU ) {
03001                         bu_log("After -- ");
03002                         nmg_pr_fu_around_eu_vecs( eu1, xvec, yvec, zvec, tol );
03003                 }
03004         }
03005         if( skipped )  {
03006                 if( rt_g.NMG_debug & DEBUG_BASIC )
03007                         bu_log("nmg_radial_implement_decisions() %d remaining, go again\n", skipped);
03008                 goto again;
03009         }
03010 
03011         if( rt_g.NMG_debug & DEBUG_BASIC )
03012                 bu_log("nmg_radial_implement_decisions() END\n");
03013 }
03014 
03015 /**
03016  *                      N M G _ P R _ R A D I A L
03017  */
03018 void
03019 nmg_pr_radial(const char *title, const struct nmg_radial *rad)
03020 {
03021         struct face             *f;
03022         int                     orient;
03023 
03024         NMG_CK_RADIAL(rad);
03025         if( !rad->fu )  {
03026                 f = (struct face *)NULL;
03027                 orient = 'W';
03028         } else {
03029                 f = rad->fu->f_p;
03030                 orient = nmg_orientation(rad->fu->orientation)[3];
03031         }
03032         bu_log("%s%8.8x, mate of \\/\n",
03033                 title,
03034                 rad->eu->eumate_p
03035         );
03036         bu_log("%s%8.8x, f=%8.8x, fu=%8.8x=%c, s=%8.8x %s %c%c%c %g deg\n",
03037                 title,
03038                 rad->eu,
03039                 f, rad->fu, orient,
03040                 rad->s,
03041                 rad->existing_flag ? "old" : "new",
03042                 rad->needs_flip ? 'F' : '/',
03043                 rad->is_crack ? 'C' : '/',
03044                 rad->is_outie ? 'O' : (rad->is_crack ? 'I' : '/'),
03045                 rad->ang * bn_radtodeg
03046         );
03047 }
03048 
03049 /**
03050  *                      N M G _ P R _ R A D I A L _ L I S T
03051  *
03052  *  Patterned after nmg_pr_fu_around_eu_vecs(), with similar format.
03053  */
03054 void
03055 nmg_pr_radial_list(const struct bu_list *hd, const struct bn_tol *tol)
03056 
03057                                         /* for printing */
03058 {
03059         struct nmg_radial       *rad;
03060 
03061         BU_CK_LIST_HEAD(hd);
03062         BN_CK_TOL(tol);
03063 
03064         bu_log("nmg_pr_radial_list( hd=x%x )\n", hd);
03065 
03066         for( BU_LIST_FOR( rad, nmg_radial, hd ) )  {
03067                 NMG_CK_RADIAL(rad);
03068                 nmg_pr_radial(" ", rad);
03069         }
03070 }
03071 
03072 
03073 /**             N M G _ D O _ R A D I A L _ F L I P S
03074  *
03075  *      This routine looks for nmg_radial structures with the same angle,
03076  *      and sorts them to match the order of nmg_radial structures that
03077  *      are not at that same angle
03078  */
03079 void
03080 nmg_do_radial_flips(struct bu_list *hd)
03081 {
03082         struct nmg_radial       *start_same;
03083         struct bn_tol   tol;
03084 
03085                 tol.magic = BN_TOL_MAGIC;
03086                 tol.dist = 0.005;
03087                 tol.dist_sq = tol.dist * tol.dist;
03088                 tol.perp = 1e-6;
03089                 tol.para = 1 - tol.perp;
03090 
03091         BU_CK_LIST_HEAD( hd );
03092 
03093         start_same = BU_LIST_FIRST( nmg_radial, hd );
03094         while( BU_LIST_NOT_HEAD( &start_same->l, hd ) )
03095         {
03096                 struct nmg_radial       *next_after_same;
03097                 struct nmg_radial       *end_same;
03098                 struct nmg_radial       *same;
03099                 struct nmg_radial       *check;
03100 
03101                 if( !start_same->fu )
03102                 {
03103                         start_same = BU_LIST_PNEXT( nmg_radial, &start_same->l );
03104                         continue;
03105                 }
03106 
03107                 end_same = BU_LIST_PNEXT_CIRC( nmg_radial, &start_same->l );
03108                 while( (end_same->ang == start_same->ang && end_same != start_same)
03109                         || !end_same->fu )
03110                         end_same = BU_LIST_PNEXT_CIRC( nmg_radial, &end_same->l );
03111                 end_same = BU_LIST_PPREV_CIRC( nmg_radial, &end_same->l );
03112 
03113                 if( start_same == end_same )
03114                 {
03115                         start_same = BU_LIST_PNEXT( nmg_radial, &start_same->l );
03116                         continue;
03117                 }
03118 
03119                 /* more than one eu at same angle, sort them according to shell */
03120                 next_after_same = BU_LIST_PNEXT_CIRC( nmg_radial, &end_same->l );
03121                 while( !next_after_same->fu && next_after_same != start_same )
03122                         next_after_same = BU_LIST_PNEXT_CIRC( nmg_radial, &next_after_same->l );
03123 
03124                 if( next_after_same == start_same )
03125                 {
03126                         /* no other radials with faces */
03127                         return;
03128                 }
03129 
03130                 check = next_after_same;
03131                 while( start_same != end_same && check != start_same )
03132                 {
03133                         same = end_same;
03134                         while( same->s != check->s && same != start_same )
03135                                 same = BU_LIST_PPREV_CIRC( nmg_radial, &same->l );
03136 
03137                         if( same->s != check->s )
03138                         {
03139                                 /* couldn't find any other radial from shell "same->s"
03140                                  * so put look at next radial
03141                                  */
03142 
03143                                 check = BU_LIST_PNEXT_CIRC( nmg_radial, &check->l );
03144                                 continue;
03145                         }
03146 
03147                         /* same->s matches check->s, so move "same" to right after end_same */
03148                         if( same == start_same )
03149                         {
03150                                 /* starting radial matches, need to move it and
03151                                  * set pointer to new start_same
03152                                  */
03153                                 start_same = BU_LIST_PNEXT_CIRC( nmg_radial, &start_same->l );
03154                                 BU_LIST_DEQUEUE( &same->l );
03155                                 BU_LIST_APPEND( &end_same->l, &same->l );
03156                         }
03157                         else if( same == end_same )
03158                         {
03159                                 /* already in correct place, just move end_same */
03160                                 end_same = BU_LIST_PPREV_CIRC( nmg_radial, &end_same->l );
03161                         }
03162                         else
03163                         {
03164                                 BU_LIST_DEQUEUE( &same->l );
03165                                 BU_LIST_APPEND( &end_same->l, &same->l );
03166                         }
03167 
03168                         check = BU_LIST_PNEXT_CIRC( nmg_radial, &check->l );
03169                 }
03170 
03171                 start_same = BU_LIST_PNEXT( nmg_radial, &end_same->l );
03172         }
03173 }
03174 
03175 /**              N M G _ D O _ R A D I A L _ J O I N
03176  *
03177  *      Perform radial join of edges in list "hd" based on direction with respect
03178  *      to "eu1ref"
03179  */
03180 
03181 void
03182 nmg_do_radial_join(struct bu_list *hd, struct edgeuse *eu1ref, fastf_t *xvec, fastf_t *yvec, fastf_t *zvec, const struct bn_tol *tol)
03183 {
03184         struct nmg_radial       *rad;
03185         struct nmg_radial       *prev;
03186         vect_t                  ref_dir;
03187         int                     skipped;
03188 
03189         BU_CK_LIST_HEAD( hd );
03190         NMG_CK_EDGEUSE( eu1ref );
03191         BN_CK_TOL( tol );
03192 
03193         if (rt_g.NMG_debug & DEBUG_MESH_EU )
03194                 bu_log( "nmg_do_radial_join() START\n" );
03195 
03196         nmg_do_radial_flips( hd );
03197 
03198         VSUB2( ref_dir, eu1ref->eumate_p->vu_p->v_p->vg_p->coord, eu1ref->vu_p->v_p->vg_p->coord );
03199 
03200         if (rt_g.NMG_debug & DEBUG_MESH_EU )
03201                 bu_log( "ref_dir = ( %g %g %g )\n", V3ARGS( ref_dir ) );
03202 
03203 top:
03204 
03205         if (rt_g.NMG_debug & DEBUG_MESH_EU )
03206         {
03207                 bu_log( "At top of nmg_do_radial_join:\n" );
03208                 nmg_pr_radial_list( hd, tol );
03209         }
03210 
03211         skipped = 0;
03212         for( BU_LIST_FOR( rad, nmg_radial, hd ) )
03213         {
03214                 struct edgeuse *dest;
03215                 struct edgeuse *src;
03216                 vect_t src_dir;
03217                 vect_t dest_dir;
03218 
03219                 if( rad->existing_flag )
03220                         continue;
03221 
03222                 prev = BU_LIST_PPREV_CIRC( nmg_radial, rad );
03223                 if( !prev->existing_flag )
03224                 {
03225                         skipped++;
03226                         continue;
03227                 }
03228 
03229                 VSUB2( dest_dir, prev->eu->eumate_p->vu_p->v_p->vg_p->coord, prev->eu->vu_p->v_p->vg_p->coord );
03230                 VSUB2( src_dir, rad->eu->eumate_p->vu_p->v_p->vg_p->coord, rad->eu->vu_p->v_p->vg_p->coord );
03231 
03232                 if( !prev->fu || !rad->fu )
03233                 {
03234                         nmg_je( prev->eu, rad->eu );
03235                         continue;
03236                 }
03237 
03238                 if( VDOT( dest_dir, ref_dir ) < 0.0 )
03239                         dest = prev->eu->eumate_p;
03240                 else
03241                         dest = prev->eu;
03242 
03243                 if( VDOT( src_dir, ref_dir ) > 0.0 )
03244                         src = rad->eu->eumate_p;
03245                 else
03246                         src = rad->eu;
03247 
03248                 if (rt_g.NMG_debug & DEBUG_MESH_EU )
03249                 {
03250                         bu_log("Before -- ");
03251                         nmg_pr_fu_around_eu_vecs( eu1ref, xvec, yvec, zvec, tol );
03252                         nmg_pr_radial("prev:", prev);
03253                         nmg_pr_radial(" rad:", rad);
03254 
03255                         if( VDOT( dest_dir, ref_dir ) < 0.0 )
03256                                 bu_log( "dest_dir disagrees with eu1ref\n" );
03257                         else
03258                                 bu_log( "dest_dir agrees with eu1ref\n" );
03259 
03260                         if( VDOT( src_dir, ref_dir ) < 0.0 )
03261                                 bu_log( "src_dir disagrees with eu1ref\n" );
03262                         else
03263                                 bu_log( "src_dir agrees with eu1ref\n" );
03264 
03265                         bu_log( "Joining dest_eu=x%x to src_eu=x%x\n", dest, src );
03266                 }
03267 
03268                 nmg_je( dest, src );
03269                 rad->existing_flag = 1;
03270                 if (rt_g.NMG_debug & DEBUG_MESH_EU )
03271                 {
03272                         bu_log("After -- ");
03273                         nmg_pr_fu_around_eu_vecs( eu1ref, xvec, yvec, zvec, tol );
03274                 }
03275         }
03276 
03277         if( skipped )
03278                 goto top;
03279 
03280         if (rt_g.NMG_debug & DEBUG_MESH_EU )
03281                 bu_log( "nmg_do_radial_join() DONE\n\n" );
03282 }
03283 
03284 /**
03285  *                      N M G _ R A D I A L _ J O I N _ E U _ N E W
03286  *
03287  *  A new routine, that uses "global information" about the edge
03288  *  to plan the operations to be performed.
03289  */
03290 void
03291 nmg_radial_join_eu_NEW(struct edgeuse *eu1, struct edgeuse *eu2, const struct bn_tol *tol)
03292 {
03293         struct edgeuse          *eu1ref;                /* reference eu for eu1 */
03294         struct edgeuse          *eu2ref;
03295         struct faceuse          *fu1;
03296         struct faceuse          *fu2;
03297         struct nmg_radial       *rad;
03298         vect_t                  xvec, yvec, zvec;
03299         struct bu_list          list1;
03300         struct bu_list          list2;
03301         struct bu_ptbl          shell_tbl;
03302 
03303         NMG_CK_EDGEUSE(eu1);
03304         NMG_CK_EDGEUSE(eu2);
03305         BN_CK_TOL(tol);
03306 
03307         if( eu1->e_p == eu2->e_p )  return;
03308 
03309         if( !NMG_ARE_EUS_ADJACENT(eu1, eu2) )
03310                 rt_bomb("nmg_radial_join_eu_NEW() edgeuses don't share vertices.\n");
03311 
03312         if( eu1->vu_p->v_p == eu1->eumate_p->vu_p->v_p )  rt_bomb("nmg_radial_join_eu_NEW(): 0 length edge (topology)\n");
03313 
03314         if( bn_pt3_pt3_equal( eu1->vu_p->v_p->vg_p->coord,
03315             eu1->eumate_p->vu_p->v_p->vg_p->coord, tol ) )
03316                 rt_bomb("nmg_radial_join_eu_NEW(): 0 length edge (geometry)\n");
03317 
03318         /* Ensure faces are of same orientation, if both eu's have faces */
03319         fu1 = nmg_find_fu_of_eu(eu1);
03320         fu2 = nmg_find_fu_of_eu(eu2);
03321         if( fu1 && fu2 )  {
03322                 if( fu1->orientation != fu2->orientation ){
03323                         eu2 = eu2->eumate_p;
03324                         fu2 = nmg_find_fu_of_eu(eu2);
03325                         if( fu1->orientation != fu2->orientation )
03326                                 rt_bomb( "nmg_radial_join_eu_NEW(): Cannot find matching orientations for faceuses\n" );
03327                 }
03328         }
03329 
03330         if( eu1->eumate_p->radial_p == eu1 && eu2->eumate_p->radial_p == eu2 &&
03331                 nmg_find_s_of_eu( eu1 ) == nmg_find_s_of_eu( eu2 ) )
03332         {
03333                 /* Only joining two edges, let's keep it simple */
03334                 nmg_je( eu1, eu2 );
03335                 if( eu1->g.magic_p && eu2->g.magic_p )
03336                 {
03337                         if( eu1->g.magic_p != eu2->g.magic_p )
03338                                 nmg_jeg( eu1->g.lseg_p, eu2->g.lseg_p );
03339                 }
03340                 else if( eu1->g.magic_p && !eu2->g.magic_p )
03341                         (void)nmg_use_edge_g( eu2, eu1->g.magic_p );
03342                 else if( !eu1->g.magic_p && eu2->g.magic_p )
03343                         (void)nmg_use_edge_g( eu1, eu2->g.magic_p );
03344                 else
03345                 {
03346                         nmg_edge_g( eu1 );
03347                         nmg_use_edge_g( eu2, eu1->g.magic_p );
03348                 }
03349                 return;
03350         }
03351 
03352         /* XXX This angle-based algorithm can't yet handle snurb faces! */
03353         if( fu1 && fu1->f_p->g.magic_p && *fu1->f_p->g.magic_p == NMG_FACE_G_SNURB_MAGIC )  return;
03354         if( fu2 && fu2->f_p->g.magic_p && *fu2->f_p->g.magic_p == NMG_FACE_G_SNURB_MAGIC )  return;
03355 
03356         /*  Construct local coordinate system for this edge,
03357          *  so all angles can be measured relative to a common reference.
03358          */
03359         if( fu1 )  {
03360                 if( fu1->orientation == OT_SAME )
03361                         eu1ref = eu1;
03362                 else
03363                         eu1ref = eu1->eumate_p;
03364         } else {
03365                 /* eu1 is a wire, find a non-wire, if there are any */
03366                 eu1ref = nmg_find_ot_same_eu_of_e(eu1->e_p);
03367         }
03368         if( eu1ref->vu_p->v_p == eu2->vu_p->v_p )  {
03369                 eu2ref = eu2;
03370         } else {
03371                 eu2ref = eu2->eumate_p;
03372         }
03373         nmg_eu_2vecs_perp( xvec, yvec, zvec, eu1ref, tol );
03374 
03375         /* Build the primary lists that describe the situation */
03376         BU_LIST_INIT( &list1 );
03377         BU_LIST_INIT( &list2 );
03378         bu_ptbl_init( &shell_tbl, 64, " &shell_tbl");
03379 
03380         nmg_radial_build_list( &list1, &shell_tbl, 1, eu1ref, xvec, yvec, zvec, tol );
03381         nmg_radial_build_list( &list2, &shell_tbl, 0, eu2ref, xvec, yvec, zvec, tol );
03382 
03383 #if 0
03384         /* OK so far? */
03385         nmg_pr_radial_list( &list1, tol );
03386         nmg_pr_radial_list( &list2, tol );
03387         nmg_pr_ptbl( "Participating shells", &shell_tbl, 1 );
03388 #endif
03389 
03390         if (rt_g.NMG_debug & DEBUG_MESH_EU ) {
03391                 bu_log("nmg_radial_join_eu_NEW(eu1=x%x, eu2=x%x) e1=x%x, e2=x%x\n",
03392                         eu1, eu2,
03393                         eu1->e_p, eu2->e_p);
03394                 nmg_euprint("\tJoining", eu1);
03395                 bu_log( "Faces around eu1:\n" );
03396                 nmg_pr_fu_around_eu_vecs( eu1ref, xvec, yvec, zvec, tol );
03397                 nmg_pr_radial_list( &list1, tol );
03398 
03399                 bu_log( "Faces around eu2:\n" );
03400                 nmg_pr_fu_around_eu_vecs( eu2ref, xvec, yvec, zvec, tol );
03401                 nmg_pr_radial_list( &list2, tol );
03402                 nmg_pr_ptbl( "Participating shells", &shell_tbl, 1 );
03403         }
03404 
03405 #if 0
03406         count1 = nmg_radial_check_parity( &list1, &shell_tbl, tol );
03407         count2 = nmg_radial_check_parity( &list2, &shell_tbl, tol );
03408         if( count1 || count2 ) bu_log("nmg_radial_join_eu_NEW() bad parity at the outset, %d, %d\n", count1, count2);
03409 #endif
03410 
03411         /* Merge the two lists, sorting by angles */
03412         nmg_radial_merge_lists( &list1, &list2, tol );
03413         nmg_radial_verify_monotone( &list1, tol );
03414 
03415 #if 1
03416         if (rt_g.NMG_debug & DEBUG_MESH_EU )
03417         {
03418                 bu_log( "Before nmg_do_radial_join():\n" );
03419                 bu_log( "xvec=(%g %g %g), yvec=(%g %g %g), zvec=(%g %g %g)\n" , V3ARGS( xvec ), V3ARGS( yvec ), V3ARGS( zvec ) );
03420                 nmg_pr_fu_around_eu_vecs( eu2ref, xvec, yvec, zvec, tol );
03421         }
03422         nmg_do_radial_join( &list1, eu1ref, xvec, yvec, zvec, tol );
03423 
03424         /* Clean up */
03425         bu_ptbl_free( &shell_tbl);
03426         while( BU_LIST_WHILE( rad, nmg_radial, &list1 ) )  {
03427                 BU_LIST_DEQUEUE( &rad->l );
03428                 bu_free( (char *)rad, "nmg_radial" );
03429         }
03430         return;
03431 #else
03432 
03433         nmg_radial_mark_cracks( &list1, eu1->e_p, eu2->e_p, tol );
03434 
03435         for( sp = (struct shell **)BU_PTBL_LASTADDR(&shell_tbl);
03436              sp >= (struct shell **)BU_PTBL_BASEADDR(&shell_tbl); sp--
03437         )  {
03438                 nmg_radial_mark_flips( &list1, *sp, tol );
03439         }
03440 
03441         if (rt_g.NMG_debug & DEBUG_MESH_EU ) {
03442                 bu_log("marked list:\n");
03443                 bu_log("  edge: %g %g %g -> %g %g %g\n",
03444                         V3ARGS(eu1ref->vu_p->v_p->vg_p->coord),
03445                         V3ARGS(eu1ref->eumate_p->vu_p->v_p->vg_p->coord) );
03446                 nmg_pr_radial_list( &list1, tol );
03447         }
03448 
03449         nmg_radial_implement_decisions( &list1, tol, eu1ref, xvec, yvec, zvec );
03450 
03451         if (rt_g.NMG_debug & DEBUG_MESH_EU ) {
03452                 /* How did it come out? */
03453                 nmg_pr_radial_list( &list1, tol );
03454                 nmg_pr_fu_around_eu_vecs( eu1ref, xvec, yvec, zvec, tol );
03455         }
03456         count1 = nmg_radial_check_parity( &list1, &shell_tbl, tol );
03457         if( count1 )  bu_log("nmg_radial_join_eu_NEW() bad parity at completion, %d\n", count1);
03458         nmg_radial_verify_pointers( &list1, tol );
03459 
03460         nmg_eu_radial_check( eu1ref, nmg_find_s_of_eu(eu1), tol );      /* expensive */
03461 #endif
03462 }
03463 
03464 /**
03465  *                      N M G _ R A D I A L _ E X C H A N G E _ M A R K E D
03466  *
03467  *  Exchange eu and eu->eumate_p on the radial list, where marked.
03468  */
03469 void
03470 nmg_radial_exchange_marked(struct bu_list *hd, const struct bn_tol *tol)
03471 
03472                                         /* for printing */
03473 {
03474         struct nmg_radial       *rad;
03475 
03476         BU_CK_LIST_HEAD(hd);
03477         BN_CK_TOL(tol);
03478 
03479         for( BU_LIST_FOR( rad, nmg_radial, hd ) )  {
03480                 struct edgeuse  *eu;
03481                 struct edgeuse  *eumate;
03482                 struct edgeuse  *before;
03483                 struct edgeuse  *after;
03484 
03485                 if( !rad->needs_flip )  continue;
03486 
03487                 /*
03488                  *  Initial sequencing is:
03489                  *    before(radial), eu, eumate, after(radial)
03490                  */
03491                 eu = rad->eu;
03492                 eumate = eu->eumate_p;
03493                 before = eu->radial_p;
03494                 after = eumate->radial_p;
03495 
03496                 /*
03497                  *  Rearrange order to be:
03498                  *      before, eumate, eu, after.
03499                  */
03500                 before->radial_p = eumate;
03501                 eumate->radial_p = before;
03502 
03503                 after->radial_p = eu;
03504                 eu->radial_p = after;
03505 
03506                 rad->eu = eumate;
03507                 rad->fu = nmg_find_fu_of_eu( rad->eu );
03508                 rad->needs_flip = 0;
03509         }
03510 }
03511 
03512 /**
03513  *                      N M G _ S _ R A D I A L _ H A R M O N I Z E
03514  *
03515  *  Visit each edge in this shell exactly once.
03516  *  Where the radial edgeuse parity has become disrupted
03517  *  due to a boolean operation or whatever, fix it.
03518  */
03519 void
03520 nmg_s_radial_harmonize(struct shell *s, const struct bn_tol *tol)
03521 {
03522         struct bu_ptbl  edges;
03523         struct edgeuse  *eu;
03524         struct bu_list  list;
03525         vect_t          xvec, yvec, zvec;
03526         struct edge     **ep;
03527 
03528         NMG_CK_SHELL(s);
03529         BN_CK_TOL(tol);
03530 
03531         if( rt_g.NMG_debug & DEBUG_BASIC )
03532                 bu_log("nmg_s_radial_harmonize( s=x%x ) BEGIN\n", s);
03533 
03534         nmg_edge_tabulate( &edges, &s->l.magic );
03535         for( ep = (struct edge **)BU_PTBL_LASTADDR(&edges);
03536              ep >= (struct edge **)BU_PTBL_BASEADDR(&edges); ep--
03537         )  {
03538                 struct nmg_radial       *rad;
03539                 int     nflip;
03540 
03541                 NMG_CK_EDGE(*ep);
03542                 eu = nmg_find_ot_same_eu_of_e( *ep );
03543                 nmg_eu_2vecs_perp( xvec, yvec, zvec, eu, tol );
03544 
03545                 BU_LIST_INIT( &list );
03546 
03547                 nmg_radial_build_list( &list, (struct bu_ptbl *)NULL, 1, eu, xvec, yvec, zvec, tol );
03548                 nflip = nmg_radial_mark_flips( &list, s, tol );
03549                 if( nflip )  {
03550                         if (rt_g.NMG_debug & DEBUG_MESH_EU ) {
03551                                 bu_log("Flips needed:\n");
03552                                 nmg_pr_radial_list( &list, tol );
03553                         }
03554                         /* Now, do the flips */
03555                         nmg_radial_exchange_marked( &list, tol );
03556                         if (rt_g.NMG_debug & DEBUG_MESH_EU ) {
03557                                 nmg_pr_fu_around_eu_vecs( eu, xvec, yvec, zvec, tol );
03558                         }
03559                 }
03560                 /* Release the storage */
03561                 while( BU_LIST_WHILE( rad, nmg_radial, &list ) )  {
03562                         BU_LIST_DEQUEUE( &rad->l );
03563                         bu_free( (char *)rad, "nmg_radial" );
03564                 }
03565         }
03566 
03567         bu_ptbl_free( &edges);
03568 
03569         if( rt_g.NMG_debug & DEBUG_BASIC )
03570                 bu_log("nmg_s_radial_harmonize( s=x%x ) END\n", s);
03571 }
03572 
03573 /**
03574  *                      N M G  _ E U _ R A D I A L _ C H E C K
03575  *
03576  *  Where the radial edgeuse parity has become disrupted, note it.
03577  *
03578  *  Returns -
03579  *      0       OK
03580  *      !0      Radial parity problem detected
03581  */
03582 int
03583 nmg_eu_radial_check(const struct edgeuse *eu, const struct shell *s, const struct bn_tol *tol)
03584 {
03585 #if 1
03586         return( 0 );
03587 #else
03588         struct bu_list  list;
03589         vect_t          xvec, yvec, zvec;
03590         struct nmg_radial       *rad;
03591         int     nflip;
03592 
03593         NMG_CK_EDGEUSE(eu);
03594         BN_CK_TOL(tol);
03595 
03596         if (rt_g.NMG_debug & DEBUG_BASIC)  {
03597                 bu_log("nmg_eu_radial_check(eu=x%x, s=x%x)\n", eu, s);
03598         }
03599 
03600         fu = nmg_find_fu_of_eu(eu);
03601         if( fu && fu->orientation != OT_SAME )  eu = eu->eumate_p;
03602         nmg_eu_2vecs_perp( xvec, yvec, zvec, eu, tol );
03603 
03604         BU_LIST_INIT( &list );
03605 
03606         /* In bad cases, this routine may rt_bomb() */
03607         nmg_radial_build_list( &list, NULL, 1, eu, xvec, yvec, zvec, tol );
03608 
03609         nmg_radial_mark_cracks( &list, eu->e_p, NULL, tol );
03610 
03611         nflip = nmg_radial_mark_flips( &list, s, tol );
03612         if( nflip )  {
03613                 struct nmg_radial *rad;
03614                 bu_log("nmg_eu_radial_check(x%x) %d flips needed\n  %g %g %g --- %g %g %g\n",
03615                         s, nflip,
03616                         V3ARGS(eu->vu_p->v_p->vg_p->coord),
03617                         V3ARGS(eu->eumate_p->vu_p->v_p->vg_p->coord) );
03618                 nmg_pr_radial_list( &list, tol );
03619                 if (rt_g.NMG_debug & DEBUG_MESH_EU ) {
03620                         nmg_pr_fu_around_eu_vecs( eu, xvec, yvec, zvec, tol );
03621                 }
03622 
03623                 if (rt_g.NMG_debug)
03624                 {
03625                         char tmp_name[256];
03626 
03627                         sprintf( tmp_name, "radial_check_%d.g", debug_file_count );
03628                         nmg_stash_model_to_file( tmp_name, nmg_find_model( &eu->l.magic ), "error" );
03629                         for( BU_LIST_FOR( rad, nmg_radial, &list ) )
03630                                 nmg_pr_fu_briefly( rad->fu, "" );
03631                 }
03632         }
03633 
03634         /* Release the storage */
03635         while( BU_LIST_WHILE( rad, nmg_radial, &list ) )  {
03636                 BU_LIST_DEQUEUE( &rad->l );
03637                 bu_free( (char *)rad, "nmg_radial" );
03638         }
03639         return nflip;
03640 #endif
03641 }
03642 
03643 /**
03644  *                      N M G _ S _ R A D I A L _ C H E C K
03645  *
03646  *  Visit each edge in this shell exactly once, and check it.
03647  */
03648 void
03649 nmg_s_radial_check(struct shell *s, const struct bn_tol *tol)
03650 {
03651         struct bu_ptbl  edges;
03652         struct edgeuse  *eu;
03653         struct edge     **ep;
03654 
03655         NMG_CK_SHELL(s);
03656         BN_CK_TOL(tol);
03657 
03658         if( rt_g.NMG_debug & DEBUG_BASIC )
03659                 bu_log("nmg_s_radial_check( s=x%x ) BEGIN\n", s);
03660 
03661         nmg_edge_tabulate( &edges, &s->l.magic );
03662         for( ep = (struct edge **)BU_PTBL_LASTADDR(&edges);
03663              ep >= (struct edge **)BU_PTBL_BASEADDR(&edges); ep--
03664         )  {
03665                 NMG_CK_EDGE(*ep);
03666                 eu = nmg_find_ot_same_eu_of_e( *ep );
03667                 nmg_eu_radial_check( eu, s, tol );
03668         }
03669 
03670         bu_ptbl_free( &edges);
03671 
03672         if( rt_g.NMG_debug & DEBUG_BASIC )
03673                 bu_log("nmg_s_radial_check( s=x%x ) END\n", s);
03674 }
03675 
03676 /**
03677  *                      N M G _ R _ R A D I A L _ C H E C K
03678  */
03679 void
03680 nmg_r_radial_check(const struct nmgregion *r, const struct bn_tol *tol)
03681 {
03682         struct shell    *s;
03683 
03684         NMG_CK_REGION(r);
03685         BN_CK_TOL(tol);
03686 
03687         if( rt_g.NMG_debug & DEBUG_BASIC )
03688                 bu_log("nmg_r_radial_check( r=x%x )\n", r);
03689 
03690         for( BU_LIST_FOR( s, shell, &r->s_hd ) )  {
03691                 NMG_CK_SHELL(s);
03692                 nmg_s_radial_check( s, tol );
03693         }
03694 }
03695 
03696 /*
03697  * Local Variables:
03698  * mode: C
03699  * tab-width: 8
03700  * c-basic-offset: 4
03701  * indent-tabs-mode: t
03702  * End:
03703  * ex: shiftwidth=4 tabstop=8
03704  */

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