cut.c

Go to the documentation of this file.
00001 /*                           C U T . C
00002  * BRL-CAD
00003  *
00004  * Copyright (c) 1990-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 ray */
00023 /*@{*/
00024 /** @file cut.c
00025  *  Cut space into lots of small boxes (RPPs actually).
00026  *
00027  *  Call tree for default path through the code:
00028  *      rt_cut_it()
00029  *              rt_cut_extend() for all solids in model
00030  *              rt_ct_optim()
00031  *                      rt_ct_old_assess()
00032  *                      rt_ct_box()
00033  *                              rt_ct_populate_box()
00034  *                                      rt_ck_overlap()
00035  *
00036  *
00037  *  Author -
00038  *      Michael John Muuss
00039  *
00040  *  Source -
00041  *      SECAD/VLD Computing Consortium, Bldg 394
00042  *      The U. S. Army Ballistic Research Laboratory
00043  *      Aberdeen Proving Ground, Maryland  21005-5066
00044  *
00045  */
00046 /*@}*/
00047 
00048 #ifndef lint
00049 static const char RCScut[] = "@(#)$Header: /cvsroot/brlcad/brlcad/src/librt/cut.c,v 14.11 2006/09/16 02:04:24 lbutler Exp $ (BRL)";
00050 #endif
00051 
00052 #include "common.h"
00053 
00054 #include <stdlib.h>
00055 #include <stdio.h>
00056 #include <math.h>
00057 #ifdef HAVE_STRING_H
00058 #  include <string.h>
00059 #else
00060 #  include <strings.h>
00061 #endif
00062 
00063 #include "machine.h"
00064 #include "vmath.h"
00065 #include "raytrace.h"
00066 #include "nmg.h"
00067 #include "plot3.h"
00068 #include "./debug.h"
00069 
00070 
00071 HIDDEN int              rt_ck_overlap BU_ARGS((const vect_t min,
00072                                                const vect_t max,
00073                                                const struct soltab *stp,
00074                                                const struct rt_i *rtip));
00075 HIDDEN int              rt_ct_box BU_ARGS((struct rt_i *rtip,
00076                                            union cutter *cutp,
00077                                            int axis, double where, int force));
00078 HIDDEN void             rt_ct_optim BU_ARGS((struct rt_i *rtip,
00079                                              union cutter *cutp, int depth));
00080 HIDDEN void             rt_ct_free BU_ARGS((struct rt_i *rtip,
00081                                             union cutter *cutp));
00082 HIDDEN void             rt_ct_release_storage BU_ARGS((union cutter *cutp));
00083 
00084 HIDDEN void             rt_ct_measure BU_ARGS((struct rt_i *rtip,
00085                                                union cutter *cutp, int depth));
00086 HIDDEN union cutter     *rt_ct_get BU_ARGS((struct rt_i *rtip));
00087 void            rt_plot_cut BU_ARGS((FILE *fp, struct rt_i *rtip,
00088                                              union cutter *cutp, int lvl));
00089 
00090 BU_EXTERN(void          rt_pr_cut_info, (const struct rt_i *rtip,
00091                                         const char *str));
00092 HIDDEN int              rt_ct_old_assess(register union cutter *,
00093                                          register int,
00094                                          double *,
00095                                          double *);
00096 
00097 #define AXIS(depth)     ((depth)%3)     /* cuts: X, Y, Z, repeat */
00098 
00099 /*
00100  *                      R T _ C U T _ O N E _ A X I S
00101  *
00102  *  As a temporary aid until NUgrid is working, use NUgrid
00103  *  histogram to perform a preliminary partitioning of space,
00104  *  along a single axis.
00105  *  The tree built here is expected to be further refined.
00106  *  The bu_ptbl "boxes" contains a list of the boxnodes, for convenience.
00107  *
00108  *  Each span runs from [min].nu_spos to [max].nu_epos.
00109  */
00110 union cutter *
00111 rt_cut_one_axis(struct bu_ptbl *boxes, struct rt_i *rtip, int axis, int min, int max, struct nugridnode *nuginfop)
00112 {
00113         register struct soltab *stp;
00114         union cutter    *box;
00115         int     cur;
00116         int     slice;
00117 
00118         BU_CK_PTBL(boxes);
00119         RT_CK_RTI(rtip);
00120 
00121         if( min == max )  {
00122                 /* Down to one cell, generate a boxnode */
00123                 slice = min;
00124                 box = (union cutter *)bu_calloc( 1, sizeof(union cutter),
00125                         "union cutter");
00126                 box->bn.bn_type = CUT_BOXNODE;
00127                 box->bn.bn_len = 0;
00128                 box->bn.bn_maxlen = rtip->nsolids;
00129                 box->bn.bn_list = (struct soltab **)bu_malloc(
00130                         box->bn.bn_maxlen * sizeof(struct soltab *),
00131                         "xbox boxnode []" );
00132                 VMOVE( box->bn.bn_min, rtip->mdl_min );
00133                 VMOVE( box->bn.bn_max, rtip->mdl_max );
00134                 box->bn.bn_min[axis] = nuginfop->nu_axis[axis][slice].nu_spos;
00135                 box->bn.bn_max[axis] = nuginfop->nu_axis[axis][slice].nu_epos;
00136                 box->bn.bn_len = 0;
00137 
00138                 /* Search all solids for those in this slice */
00139                 RT_VISIT_ALL_SOLTABS_START( stp, rtip )  {
00140                         RT_CHECK_SOLTAB(stp);
00141                         if( !rt_ck_overlap( box->bn.bn_min, box->bn.bn_max,
00142                                             stp, rtip ) )
00143                                 continue;
00144                         box->bn.bn_list[box->bn.bn_len++] = stp;
00145                 } RT_VISIT_ALL_SOLTABS_END
00146 
00147                 bu_ptbl_ins( boxes, (long *)box );
00148                 return box;
00149         }
00150 
00151         cur = (min + max + 1) / 2;
00152         /* Recurse on both sides, then build a cutnode */
00153         box = (union cutter *)bu_calloc( 1, sizeof(union cutter),
00154                 "union cutter");
00155         box->cn.cn_type = CUT_CUTNODE;
00156         box->cn.cn_axis = axis;
00157         box->cn.cn_point = nuginfop->nu_axis[axis][cur].nu_spos;
00158         box->cn.cn_l = rt_cut_one_axis( boxes, rtip, axis, min, cur-1, nuginfop );
00159         box->cn.cn_r = rt_cut_one_axis( boxes, rtip, axis, cur, max, nuginfop );
00160         return box;
00161 }
00162 
00163 /*
00164  *                      R T _ C U T _ O P T I M I Z E _ P A R A L L E L
00165  *
00166  *  Process all the nodes in the global array rtip->rti_cuts_waiting,
00167  *  until none remain.
00168  *  This routine is run in parallel.
00169  */
00170 void
00171 rt_cut_optimize_parallel(int cpu, genptr_t arg)
00172 {
00173         struct rt_i     *rtip = (struct rt_i *)arg;
00174         union cutter    *cp;
00175         int             i;
00176 
00177         RT_CK_RTI(rtip);
00178         for(;;)  {
00179 
00180                 bu_semaphore_acquire( RT_SEM_WORKER );
00181                 i = rtip->rti_cuts_waiting.end--;       /* get first free index */
00182                 bu_semaphore_release( RT_SEM_WORKER );
00183                 i -= 1;                         /* change to last used index */
00184 
00185                 if( i < 0 )  break;
00186 
00187                 cp = (union cutter *)BU_PTBL_GET( &rtip->rti_cuts_waiting, i );
00188 
00189                 rt_ct_optim( rtip, cp, Z );
00190         }
00191 }
00192 
00193 #define CMP(_p1,_p2,_memb,_ind) \
00194         (*(const struct soltab **)(_p1))->_memb[_ind] < \
00195         (*(const struct soltab **)(_p2))->_memb[_ind] ? -1 : \
00196         (*(const struct soltab **)(_p1))->_memb[_ind] > \
00197         (*(const struct soltab **)(_p2))->_memb[_ind] ? 1 : 0
00198 
00199 /* Functions for use with qsort */
00200 HIDDEN int rt_projXmin_comp BU_ARGS((const void * p1, const void * p2));
00201 HIDDEN int rt_projXmax_comp BU_ARGS((const void * p1, const void * p2));
00202 HIDDEN int rt_projYmin_comp BU_ARGS((const void * p1, const void * p2));
00203 HIDDEN int rt_projYmax_comp BU_ARGS((const void * p1, const void * p2));
00204 HIDDEN int rt_projZmin_comp BU_ARGS((const void * p1, const void * p2));
00205 HIDDEN int rt_projZmax_comp BU_ARGS((const void * p1, const void * p2));
00206 
00207 HIDDEN int
00208 rt_projXmin_comp(const void *p1, const void *p2)
00209 {
00210         return CMP(p1,p2,st_min,X);
00211 }
00212 
00213 HIDDEN int
00214 rt_projXmax_comp(const void *p1, const void *p2)
00215 {
00216         return CMP(p1,p2,st_max,X);
00217 }
00218 
00219 HIDDEN int
00220 rt_projYmin_comp(const void *p1, const void *p2)
00221 {
00222         return CMP(p1,p2,st_min,Y);
00223 }
00224 
00225 HIDDEN int
00226 rt_projYmax_comp(const void *p1, const void *p2)
00227 {
00228         return CMP(p1,p2,st_max,Y);
00229 }
00230 
00231 HIDDEN int
00232 rt_projZmin_comp(const void *p1, const void *p2)
00233 {
00234         return CMP(p1,p2,st_min,Z);
00235 }
00236 
00237 HIDDEN int
00238 rt_projZmax_comp(const void *p1, const void *p2)
00239 {
00240         return CMP(p1,p2,st_max,Z);
00241 }
00242 
00243 HIDDEN struct cmp_pair {
00244         int (*cmp_min) BU_ARGS((const void *, const void *));
00245         int (*cmp_max) BU_ARGS((const void *, const void *));
00246 } pairs[] = {
00247         { rt_projXmin_comp, rt_projXmax_comp },
00248         { rt_projYmin_comp, rt_projYmax_comp },
00249         { rt_projZmin_comp, rt_projZmax_comp }
00250 };
00251 
00252 /*
00253  *                      R T _ N U G R I D _ C U T
00254  *
00255  *   Makes a NUGrid node (CUT_NUGRIDNODE), filling the cells with solids
00256  *   from the given list.
00257  */
00258 
00259 void
00260 rt_nugrid_cut(register struct nugridnode *nugnp, register struct boxnode *fromp, struct rt_i *rtip, int just_collect_info, int depth)
00261 {
00262 #define USE_HIST 0
00263 #if USE_HIST
00264         struct bu_hist start_hist[3];   /* where solid RPPs start */
00265         struct bu_hist end_hist[3];     /* where solid RPPs end */
00266 #endif
00267         register struct soltab **stpp;
00268 
00269         int     nu_ncells;              /* # cells along one axis */
00270         int     nu_sol_per_cell;        /* avg # solids per cell */
00271         int     nu_max_ncells;          /* hard limit on nu_ncells */
00272         int     pseudo_depth;           /* "fake" depth to tell rt_ct_optim */
00273         register int    i;
00274         int     xp, yp, zp;
00275         vect_t  xmin, xmax, ymin, ymax, zmin, zmax;
00276         struct boxnode nu_xbox, nu_ybox, nu_zbox;
00277 
00278         if( nugnp->nu_type != CUT_NUGRIDNODE )
00279                 rt_bomb( "rt_nugrid_cut: passed non-nugridnode" );
00280 
00281         /*
00282          *  Build histograms of solid RPP extent distribution
00283          *  when projected onto X, Y, and Z axes.
00284          *  First stage in implementing the Gigante NUgrid algorithm.
00285          *  (Proc. Ausgraph 1990, pg 157)
00286          *  XXX For databases where the model diameter (diagonal through
00287          *  XXX the model RPP) is small or huge, a histogram with
00288          *  XXX floating-point ranges will be needed.
00289          *  XXX Integers should suffice for now.
00290          *
00291          *  Goal is to keep the average number of solids per cell constant.
00292          *  Assuming a uniform distribution, the number of cells along
00293          *  each axis will need to be (nsol / desired_avg)**(1/3).
00294          *  This is increased by two to err on the generous side,
00295          *  resulting in a 3*3*3 grid even for small numbers of solids.
00296          *
00297          *  Presumably the caller will have set rtip->rti_nu_gfactor to
00298          *  RT_NU_GFACTOR_DEFAULT (although it may change it depending on
00299          *  the user's wishes or what it feels will be optimal for this
00300          *  model.)  The default value was computed in the following fashion:
00301          *  Suppose the ratio of the running times of ft_shot and
00302          *  rt_advance_to_next_cell is K, i.e.,
00303          *
00304          *          avg_running_time( ft_shot )
00305          *  K := ---------------------------------
00306          *         avg_running_time( rt_advance )
00307          *
00308          *  Now suppose we divide each axis into G*n^R segments, yielding
00309          *  G^3 n^(3R) cells.  We expect each cell to contain
00310          *  G^(-3) n^(1-3R) solids, and hence the running time of firing
00311          *  a single ray through the model will be proportional to
00312          *
00313          *  G*n^R * [ 1 + K G^(-3) n^(1-3R) ] = G*n^R + KG^(-2) n^(1-2R).
00314          *
00315          *  Since we wish for the running time of this algorithm to be low,
00316          *  we wish to minimize the degree of this polynomial.  The optimal
00317          *  choice for R is 1/3.  So the above equation becomes
00318          *
00319          *  G*n^(1/3) + KG^(-2) n^(1/3) = [ G + KG^(-2) ] n^(1/3).
00320          *
00321          *  We now wish to find an optimal value of G in terms of K.
00322          *  Using basic calculus we see that
00323          *
00324          *                 G := (2K)^(1/3).
00325          *
00326          *  It has been experimentally observed that K ~ 5/3 when the model
00327          *  is mostly arb8s.   Hence RT_NU_GFACTOR_DEFAULT := 1.5.
00328          *
00329          *  XXX More tests should be done for this.
00330          */
00331 
00332         if( rtip->rti_nu_gfactor < 0.01 ||
00333             rtip->rti_nu_gfactor > 10*RT_NU_GFACTOR_DEFAULT ) {
00334                 rtip->rti_nu_gfactor = RT_NU_GFACTOR_DEFAULT;
00335                 bu_log(
00336       "rt_nugrid_cut: warning: rtip->rti_nu_gfactor not set, setting to %g\n",
00337                         RT_NU_GFACTOR_DEFAULT );
00338         }
00339 
00340         nu_ncells = (int)ceil( 2.0 + rtip->rti_nu_gfactor *
00341                                pow( (double)fromp->bn_len, 1.0/3.0 ) );
00342         if( rtip->rti_nugrid_dimlimit > 0 &&
00343             nu_ncells > rtip->rti_nugrid_dimlimit )
00344                 nu_ncells = rtip->rti_nugrid_dimlimit;
00345         nu_sol_per_cell = (fromp->bn_len + nu_ncells - 1) / nu_ncells;
00346         nu_max_ncells = 2*nu_ncells + 8;
00347 #if 0
00348         pseudo_depth = depth+(int)log((double)(nu_ncells*nu_ncells*nu_ncells));
00349 #else
00350         pseudo_depth = depth;
00351 #endif
00352 
00353         if( RT_G_DEBUG&DEBUG_CUT )
00354                 bu_log(
00355                      "\nnu_ncells=%d, nu_sol_per_cell=%d, nu_max_ncells=%d\n",
00356                      nu_ncells, nu_sol_per_cell, nu_max_ncells );
00357 
00358 #if USE_HIST
00359         for( i=0; i<3; i++ ) {
00360                 bu_hist_init( &start_hist[i],
00361                               fromp->bn_min[i],
00362                               fromp->bn_max[i],
00363                               nu_ncells*100 );
00364                 bu_hist_init( &end_hist[i],
00365                               fromp->bn_min[i],
00366                               fromp->bn_max[i],
00367                               nu_ncells*100 );
00368         }
00369 
00370 
00371         for( i = 0, stpp = fromp->bn_list;
00372              i < fromp->bn_len;
00373              i++, stpp++ ) {
00374                 register struct soltab *stp = *stpp;
00375                 RT_CK_SOLTAB( stp );
00376                 if( stp->st_aradius <= 0 )  continue;
00377                 if( stp->st_aradius >= INFINITY )  continue;
00378                 for( j=0; j<3; j++ )  {
00379                         BU_HIST_TALLY( &start_hist[j],stp->st_min[j] );
00380                         BU_HIST_TALLY( &end_hist[j],  stp->st_max[j] );
00381                 }
00382         }
00383 #endif
00384 
00385         /* Allocate memory for nugrid axis parition. */
00386 
00387         for( i=0; i<3; i++ )
00388                 nugnp->nu_axis[i] = (struct nu_axis *)bu_malloc(
00389                         nu_max_ncells*sizeof(struct nu_axis), "NUgrid axis" );
00390 
00391 #if USE_HIST
00392         /*
00393          *  Next part of NUgrid algorithm:
00394          *  Decide where cell boundaries should be, along each axis.
00395          *  Each cell will be an interval across the histogram.
00396          *  For each interval, track the number of new solids that
00397          *  have *started* in the interval, and the number of existing solids
00398          *  that have *ended* in the interval.
00399          *  Continue widening the interval another histogram element
00400          *  in width, until either the number of starts or endings
00401          *  exceeds the goal for the average number of solids per
00402          *  cell, nu_sol_per_cell = (nsolids / nu_ncells).
00403          */
00404 
00405         for( i=0; i<3; i++ )  {
00406                 fastf_t pos;            /* Current interval start pos */
00407                 int     nstart = 0;
00408                 int     nend = 0;
00409                 struct bu_hist  *shp = &start_hist[i];
00410                 struct bu_hist  *ehp = &end_hist[i];
00411                 int     hindex = 0;
00412                 int     axi = 0;
00413 
00414                 if( shp->hg_min != ehp->hg_min )
00415                         rt_bomb("cut_it: hg_min error\n");
00416                 pos = shp->hg_min;
00417                 nugnp->nu_axis[i][axi].nu_spos = pos;
00418                 for( hindex = 0; hindex < shp->hg_nbins; hindex++ )  {
00419                         if( pos > shp->hg_max )  break;
00420                         /* Advance interval one more histogram entry */
00421                         /* NOTE:  Peeks into histogram structures! */
00422                         nstart += shp->hg_bins[hindex];
00423                         nend += ehp->hg_bins[hindex];
00424                         pos += shp->hg_clumpsize;
00425 #if 1
00426                         if( nstart < nu_sol_per_cell &&
00427                             nend < nu_sol_per_cell ) continue;
00428 #else
00429                         if( nstart + nend < 2 * nu_sol_per_cell )
00430                                 continue;
00431 #endif
00432                         /* End current interval, start new one */
00433                         nugnp->nu_axis[i][axi].nu_epos = pos;
00434                         nugnp->nu_axis[i][axi].nu_width =
00435                                 pos - nugnp->nu_axis[i][axi].nu_spos;
00436                         if( axi >= nu_max_ncells-1 )  {
00437                                 bu_log("NUgrid ran off end, axis=%d, axi=%d\n",
00438                                         i, axi);
00439                                 pos = shp->hg_max+1;
00440                                 break;
00441                         }
00442                         nugnp->nu_axis[i][++axi].nu_spos = pos;
00443                         nstart = 0;
00444                         nend = 0;
00445                 }
00446                 /* End final interval */
00447                 nugnp->nu_axis[i][axi].nu_epos = pos;
00448                 nugnp->nu_axis[i][axi].nu_width =
00449                         pos - nugnp->nu_axis[i][axi].nu_spos;
00450                 nugnp->nu_cells_per_axis[i] = axi+1;
00451         }
00452 
00453         /* Finished with temporary data structures */
00454         for( i=0; i<3; i++ ) {
00455                 bu_hist_free( &start_hist[i] );
00456                 bu_hist_free( &end_hist[i] );
00457         }
00458 #else
00459         {
00460                 struct soltab **list_min, **list_max;
00461                 register struct soltab **l1, **l2;
00462                 register int nstart, nend, axi, len = fromp->bn_len;
00463                 register fastf_t pos;
00464 
00465                 list_min = (struct soltab **)bu_malloc( len *
00466                                                       sizeof(struct soltab *),
00467                                                         "min solid list" );
00468                 list_max = (struct soltab **)bu_malloc( len *
00469                                                       sizeof(struct soltab *),
00470                                                         "max solid list" );
00471                 bcopy( fromp->bn_list, list_min, len*sizeof(struct soltab *) );
00472                 bcopy( fromp->bn_list, list_max, len*sizeof(struct soltab *) );
00473                 for( i=0; i<3; i++ ) {
00474                         qsort( (genptr_t)list_min, len,
00475                                sizeof(struct soltab *), pairs[i].cmp_min );
00476                         qsort( (genptr_t)list_max, len,
00477                                sizeof(struct soltab *), pairs[i].cmp_max );
00478                         nstart = nend = axi = 0;
00479                         l1 = list_min;
00480                         l2 = list_max;
00481 
00482                         pos = fromp->bn_min[i];
00483                         nugnp->nu_axis[i][axi].nu_spos = pos;
00484 
00485                         while( l1-list_min < len ||
00486                                l2-list_max < len ) {
00487                                 if( l2-list_max >= len ||
00488                                     (l1-list_min < len &&
00489                                      (*l1)->st_min[i] < (*l2)->st_max[i]) ) {
00490                                         pos = (*l1++)->st_min[i];
00491                                         ++nstart;
00492                                 } else {
00493                                         pos = (*l2++)->st_max[i];
00494                                         ++nend;
00495                                 }
00496 
00497 #if 1
00498                                 if( nstart < nu_sol_per_cell &&
00499                                     nend < nu_sol_per_cell )
00500 #else
00501                                 if( nstart + nend < nu_sol_per_cell )
00502 #endif
00503                                         continue;
00504 
00505                                 /* Don't make really teeny intervals. */
00506                                 if( pos <= nugnp->nu_axis[i][axi].nu_spos
00507 #if 1
00508                                            + 1.0
00509 #endif
00510                                            + rtip->rti_tol.dist )
00511                                         continue;
00512 
00513                                 /* don't make any more cuts if we've gone
00514                                    past the end. */
00515                                 if( pos >= fromp->bn_max[i]
00516 #if 1
00517                                            - 1.0
00518 #endif
00519                                            - rtip->rti_tol.dist )
00520                                         continue;
00521 
00522                                 /* End current interval, start new one */
00523                                 nugnp->nu_axis[i][axi].nu_epos = pos;
00524                                 nugnp->nu_axis[i][axi].nu_width =
00525                                         pos - nugnp->nu_axis[i][axi].nu_spos;
00526                                 if( axi >= nu_max_ncells-1 )  {
00527                                         bu_log(
00528                                       "NUgrid ran off end, axis=%d, axi=%d\n",
00529                                                i, axi);
00530                                         rt_bomb( "rt_nugrid_cut: NUgrid ran off end" );
00531                                 }
00532                                 nugnp->nu_axis[i][++axi].nu_spos = pos;
00533                                 nstart = 0;
00534                                 nend = 0;
00535                         }
00536                         /* End final interval */
00537                         if( pos < fromp->bn_max[i] ) pos = fromp->bn_max[i];
00538                         nugnp->nu_axis[i][axi].nu_epos = pos;
00539                         nugnp->nu_axis[i][axi].nu_width =
00540                                 pos - nugnp->nu_axis[i][axi].nu_spos;
00541                         nugnp->nu_cells_per_axis[i] = axi+1;
00542                 }
00543                 bu_free( (genptr_t)list_min, "solid list min sort" );
00544                 bu_free( (genptr_t)list_max, "solid list max sort" );
00545         }
00546 
00547 #endif
00548 
00549         nugnp->nu_stepsize[X] = 1;
00550         nugnp->nu_stepsize[Y] = nugnp->nu_cells_per_axis[X] *
00551                                         nugnp->nu_stepsize[X];
00552         nugnp->nu_stepsize[Z] = nugnp->nu_cells_per_axis[Y] *
00553                                         nugnp->nu_stepsize[Y];
00554 
00555         /* For debugging */
00556         if( RT_G_DEBUG&DEBUG_CUT ) for( i=0; i<3; i++ ) {
00557                 register int j;
00558                 bu_log( "NUgrid %c axis:  %d cells\n", "XYZ*"[i],
00559                         nugnp->nu_cells_per_axis[i] );
00560                 for( j=0; j<nugnp->nu_cells_per_axis[i]; j++ ) {
00561                         bu_log( "  %g .. %g, w=%g\n",
00562                                 nugnp->nu_axis[i][j].nu_spos,
00563                                 nugnp->nu_axis[i][j].nu_epos,
00564                                 nugnp->nu_axis[i][j].nu_width );
00565                 }
00566         }
00567 
00568         /* If we were just asked to collect info, we are done.
00569            The binary space partioning algorithm (sometimes) needs just this.*/
00570 
00571         if( just_collect_info ) return;
00572 
00573         /* For the moment, re-use "union cutter" */
00574         nugnp->nu_grid = (union cutter *)bu_malloc(
00575                 nugnp->nu_cells_per_axis[X] *
00576                 nugnp->nu_cells_per_axis[Y] *
00577                 nugnp->nu_cells_per_axis[Z] * sizeof(union cutter),
00578                 "3-D NUgrid union cutter []" );
00579         nu_xbox.bn_len = 0;
00580         nu_xbox.bn_maxlen = fromp->bn_len;
00581         nu_xbox.bn_list = (struct soltab **)bu_malloc(
00582                 nu_xbox.bn_maxlen * sizeof(struct soltab *),
00583                 "xbox boxnode []" );
00584         nu_ybox.bn_len = 0;
00585         nu_ybox.bn_maxlen = fromp->bn_len;
00586         nu_ybox.bn_list = (struct soltab **)bu_malloc(
00587                 nu_ybox.bn_maxlen * sizeof(struct soltab *),
00588                 "ybox boxnode []" );
00589         nu_zbox.bn_len = 0;
00590         nu_zbox.bn_maxlen = fromp->bn_len;
00591         nu_zbox.bn_list = (struct soltab **)bu_malloc(
00592                 nu_zbox.bn_maxlen * sizeof(struct soltab *),
00593                 "zbox boxnode []" );
00594         /* Build each of the X slices */
00595         for( xp = 0; xp < nugnp->nu_cells_per_axis[X]; xp++ ) {
00596                 VMOVE( xmin, fromp->bn_min );
00597                 VMOVE( xmax, fromp->bn_max );
00598                 xmin[X] = nugnp->nu_axis[X][xp].nu_spos;
00599                 xmax[X] = nugnp->nu_axis[X][xp].nu_epos;
00600                 VMOVE( nu_xbox.bn_min, xmin );
00601                 VMOVE( nu_xbox.bn_max, xmax );
00602                 nu_xbox.bn_len = 0;
00603 
00604                 /* Search all solids for those in this X slice */
00605                 for( i = 0, stpp = fromp->bn_list;
00606                      i < fromp->bn_len;
00607                      i++, stpp++ ) {
00608                         if( !rt_ck_overlap( xmin, xmax, *stpp, rtip ) )
00609                                 continue;
00610                         nu_xbox.bn_list[nu_xbox.bn_len++] = *stpp;
00611                 }
00612 
00613                 /* Build each of the Y slices in this X slice */
00614                 for( yp = 0; yp < nugnp->nu_cells_per_axis[Y]; yp++ ) {
00615                         VMOVE( ymin, xmin );
00616                         VMOVE( ymax, xmax );
00617                         ymin[Y] = nugnp->nu_axis[Y][yp].nu_spos;
00618                         ymax[Y] = nugnp->nu_axis[Y][yp].nu_epos;
00619                         VMOVE( nu_ybox.bn_min, ymin );
00620                         VMOVE( nu_ybox.bn_max, ymax );
00621                         nu_ybox.bn_len = 0;
00622                         /* Search X slice for membs of this Y slice */
00623                         for( i=0; i<nu_xbox.bn_len; i++ )  {
00624                                 if( !rt_ck_overlap( ymin, ymax,
00625                                                     nu_xbox.bn_list[i],
00626                                                     rtip ) )
00627                                         continue;
00628                                 nu_ybox.bn_list[nu_ybox.bn_len++] =
00629                                         nu_xbox.bn_list[i];
00630                         }
00631                         /* Build each of the Z slices in this Y slice*/
00632                         /* Each of these will be a final cell */
00633                         for( zp = 0; zp < nugnp->nu_cells_per_axis[Z]; zp++ ) {
00634                                 register union cutter *cutp =
00635                                         &nugnp->nu_grid[
00636                                                 zp*nugnp->nu_stepsize[Z] +
00637                                                 yp*nugnp->nu_stepsize[Y] +
00638                                                 xp*nugnp->nu_stepsize[X]];
00639 
00640                                 VMOVE( zmin, ymin );
00641                                 VMOVE( zmax, ymax );
00642                                 zmin[Z] = nugnp->nu_axis[Z][zp].nu_spos;
00643                                 zmax[Z] = nugnp->nu_axis[Z][zp].nu_epos;
00644                                 cutp->cut_type = CUT_BOXNODE;
00645                                 VMOVE( cutp->bn.bn_min, zmin );
00646                                 VMOVE( cutp->bn.bn_max, zmax );
00647                                 /* Build up a temporary list in nu_zbox first,
00648                                  * then copy list over to cutp->bn */
00649                                 nu_zbox.bn_len = 0;
00650                                 /* Search Y slice for members of this Z slice*/
00651                                 for( i=0; i<nu_ybox.bn_len; i++ )  {
00652                                         if( !rt_ck_overlap( zmin, zmax,
00653                                                             nu_ybox.bn_list[i],
00654                                                             rtip ) )
00655                                                 continue;
00656                                         nu_zbox.bn_list[nu_zbox.bn_len++] =
00657                                                 nu_ybox.bn_list[i];
00658                                 }
00659 
00660                                 if( nu_zbox.bn_len <= 0 )  {
00661                                         /* Empty cell */
00662                                         cutp->bn.bn_list = (struct soltab **)0;
00663                                         cutp->bn.bn_len = 0;
00664                                         cutp->bn.bn_maxlen = 0;
00665                                         continue;
00666                                 }
00667                                 /* Allocate just enough space for list,
00668                                  * and copy it in */
00669 
00670                                 cutp->bn.bn_list =
00671                                         (struct soltab **)bu_malloc(
00672                                                 nu_zbox.bn_len *
00673                                                 sizeof(struct soltab *),
00674                                                 "NUgrid cell bn_list[]" );
00675                                 cutp->bn.bn_len = cutp->bn.bn_maxlen =
00676                                         nu_zbox.bn_len;
00677                                 bcopy( (char *)nu_zbox.bn_list,
00678                                        (char *)cutp->bn.bn_list,
00679                                        nu_zbox.bn_len *
00680                                        sizeof(struct soltab *) );
00681 
00682                                 if( rtip->rti_nugrid_dimlimit > 0 ) {
00683 #if 1
00684                                         rt_ct_optim( rtip, cutp, pseudo_depth);
00685 #else
00686                                 /* Recurse, but only if we're cutting down on
00687                                    the cellsize. */
00688                                         if( cutp->bn.bn_len > 5 &&
00689                                     cutp->bn.bn_len < fromp->bn_len>>1 ) {
00690 
00691                                         /* Make a little NUGRID node here
00692                                            to clean things up */
00693                                         union cutter temp;
00694 
00695                                         temp = *cutp;  /* union copy */
00696                                         cutp->cut_type = CUT_NUGRIDNODE;
00697                                         /* recursive call! */
00698                                         rt_nugrid_cut( &cutp->nugn,
00699                                                        &temp.bn, rtip, 0,
00700                                                        depth+1 );
00701                                         }
00702 #endif
00703                                 }
00704                         }
00705                 }
00706         }
00707 
00708         bu_free( (genptr_t)nu_zbox.bn_list, "nu_zbox bn_list[]" );
00709         bu_free( (genptr_t)nu_ybox.bn_list, "nu_ybox bn_list[]" );
00710         bu_free( (genptr_t)nu_xbox.bn_list, "nu_xbox bn_list[]" );
00711 }
00712 
00713 int
00714 rt_split_mostly_empty_cells( struct rt_i *rtip, union cutter *cutp )
00715 {
00716         point_t max, min;
00717         struct soltab *stp;
00718         struct rt_piecelist pl;
00719         fastf_t range[3], empty[3], tmp;
00720         int upper_or_lower[3];
00721         fastf_t max_empty;
00722         int max_empty_dir;
00723         int i;
00724         int num_splits=0;
00725 
00726         switch( cutp->cut_type ) {
00727         case CUT_CUTNODE:
00728                 num_splits += rt_split_mostly_empty_cells( rtip, cutp->cn.cn_l );
00729                 num_splits += rt_split_mostly_empty_cells( rtip, cutp->cn.cn_r );
00730                 break;
00731         case CUT_BOXNODE:
00732                 /* find the actual bounds of stuff in this cell */
00733                 if( cutp->bn.bn_len == 0 && cutp->bn.bn_piecelen == 0 ) {
00734                         break;
00735                 }
00736                 VSETALL( min, MAX_FASTF );
00737                 VREVERSE( max, min );
00738 
00739                 for( i=0 ; i<cutp->bn.bn_len ; i++ ) {
00740                         stp = cutp->bn.bn_list[i];
00741                         VMIN( min, stp->st_min );
00742                         VMAX( max, stp->st_max );
00743                 }
00744 
00745                 for( i=0 ; i<cutp->bn.bn_piecelen ; i++ ) {
00746                         int j;
00747 
00748                         pl = cutp->bn.bn_piecelist[i];
00749                         for( j=0 ; j<pl.npieces ; j++ ) {
00750                                 int piecenum;
00751 
00752                                 piecenum = pl.pieces[j];
00753                                 VMIN( min, pl.stp->st_piece_rpps[piecenum].min );
00754                                 VMAX( max, pl.stp->st_piece_rpps[piecenum].max );
00755                         }
00756                 }
00757 
00758                 /* clip min and max to the bounds of this cell */
00759                 for( i=X ; i<=Z ; i++ ) {
00760                         if( min[i] < cutp->bn.bn_min[i] ) {
00761                                 min[i] = cutp->bn.bn_min[i];
00762                         }
00763                         if( max[i] > cutp->bn.bn_max[i] ) {
00764                                 max[i] = cutp->bn.bn_max[i];
00765                         }
00766                 }
00767 
00768                 /* min and max now have the real bounds of data in this cell */
00769                 VSUB2( range, cutp->bn.bn_max, cutp->bn.bn_min );
00770                 for( i=X ; i<=Z ; i++ ) {
00771                         empty[i] = cutp->bn.bn_max[i] - max[i];
00772                         upper_or_lower[i] = 1; /* upper section is empty */
00773                         tmp = min[i] - cutp->bn.bn_min[i];
00774                         if( tmp > empty[i] ) {
00775                                 empty[i] = tmp;
00776                                 upper_or_lower[i] = 0;  /* lower section is empty */
00777                         }
00778                 }
00779                 max_empty = empty[X];
00780                 max_empty_dir = X;
00781                 if( empty[Y] > max_empty ) {
00782                         max_empty = empty[Y];
00783                         max_empty_dir = Y;
00784                 }
00785                 if( empty[Z] > max_empty ) {
00786                         max_empty = empty[Z];
00787                         max_empty_dir = Z;
00788                 }
00789                 if( max_empty / range[max_empty_dir] > 0.5 ) {
00790                         /* this cell is over 50% empty in this direction, split it */
00791 
00792                         fastf_t where;
00793 
00794                         /* select cutting plane, but move it slightly off any geometry */
00795                         if( upper_or_lower[max_empty_dir] ) {
00796                                 where = max[max_empty_dir] + rtip->rti_tol.dist;
00797                                 if( where >= cutp->bn.bn_max[max_empty_dir] ) {
00798                                        return( num_splits );
00799                                 }
00800                         } else {
00801                                 where = min[max_empty_dir] - rtip->rti_tol.dist;
00802                                 if( where <= cutp->bn.bn_min[max_empty_dir] ) {
00803                                        return( num_splits );
00804                                 }
00805                         }
00806                         if( where - cutp->bn.bn_min[max_empty_dir] < 2.0 ||
00807                             cutp->bn.bn_max[max_empty_dir] - where < 2.0 ) {
00808                                 /* will make a box too small */
00809                                 return( num_splits );
00810                         }
00811                         if( rt_ct_box( rtip, cutp, max_empty_dir, where, 1 ) ) {
00812                                 num_splits++;
00813                                 num_splits += rt_split_mostly_empty_cells( rtip, cutp->cn.cn_l );
00814                                 num_splits += rt_split_mostly_empty_cells( rtip, cutp->cn.cn_r );
00815                         }
00816                 }
00817                 break;
00818         }
00819 
00820         return( num_splits );
00821 }
00822 
00823 
00824 
00825 /*
00826  *                      R T _ C U T _ I T
00827  *
00828  *  Go through all the solids in the model, given the model mins and maxes,
00829  *  and generate a cutting tree.  A strategy better than incrementally
00830  *  cutting each solid is to build a box node which contains everything
00831  *  in the model, and optimize it.
00832  *
00833  *  This is the main entry point into space partitioning from rt_prep().
00834  */
00835 void
00836 rt_cut_it(register struct rt_i *rtip, int ncpu)
00837 {
00838         register struct soltab *stp;
00839         union cutter *finp;     /* holds the finite solids */
00840         FILE *plotfp;
00841         int num_splits=0;
00842 
00843         /* Make a list of all solids into one special boxnode, then refine. */
00844         BU_GETUNION( finp, cutter );
00845         finp->cut_type = CUT_BOXNODE;
00846         VMOVE( finp->bn.bn_min, rtip->mdl_min );
00847         VMOVE( finp->bn.bn_max, rtip->mdl_max );
00848         finp->bn.bn_len = 0;
00849         finp->bn.bn_maxlen = rtip->nsolids+1;
00850         finp->bn.bn_list = (struct soltab **)bu_malloc(
00851                 finp->bn.bn_maxlen * sizeof(struct soltab *),
00852                 "rt_cut_it: initial list alloc" );
00853 
00854         rtip->rti_inf_box.cut_type = CUT_BOXNODE;
00855 
00856         RT_VISIT_ALL_SOLTABS_START( stp, rtip ) {
00857                 /* Ignore "dead" solids in the list.  (They failed prep) */
00858                 if( stp->st_aradius <= 0 )  continue;
00859 
00860                 /* Infinite and finite solids all get lumpped together */
00861                 rt_cut_extend( finp, stp, rtip );
00862 
00863                 if( stp->st_aradius >= INFINITY )  {
00864                         /* Also add infinite solids to a special BOXNODE */
00865                         rt_cut_extend( &rtip->rti_inf_box, stp, rtip );
00866                 }
00867         } RT_VISIT_ALL_SOLTABS_END
00868 
00869         /* Dynamic decisions on tree limits.  Note that there will be
00870          * (2**rtip->rti_cutdepth)*rtip->rti_cutlen potential leaf slots.
00871          * Also note that solids will typically span several leaves.
00872          */
00873         rtip->rti_cutlen = (int)log((double)rtip->nsolids);  /* ln ~= log2 */
00874         rtip->rti_cutdepth = 2 * rtip->rti_cutlen;
00875         if( rtip->rti_cutlen < 3 )  rtip->rti_cutlen = 3;
00876         if( rtip->rti_cutdepth < 12 )  rtip->rti_cutdepth = 12;
00877         if( rtip->rti_cutdepth > 24 )  rtip->rti_cutdepth = 24;     /* !! */
00878         if( RT_G_DEBUG&DEBUG_CUT )
00879                 bu_log( "Before Space Partitioning: Max Tree Depth=%d, Cuttoff primitive count=%d\n",
00880                         rtip->rti_cutdepth, rtip->rti_cutlen );
00881 
00882         bu_ptbl_init( &rtip->rti_cuts_waiting, rtip->nsolids,
00883                       "rti_cuts_waiting ptbl" );
00884 
00885         if( rtip->rti_hasty_prep )  {
00886                 rtip->rti_space_partition = RT_PART_NUBSPT;
00887                 rtip->rti_cutdepth = 6;
00888         }
00889 
00890         switch( rtip->rti_space_partition ) {
00891         case RT_PART_NUGRID:
00892                 rtip->rti_CutHead.cut_type = CUT_NUGRIDNODE;
00893                 rt_nugrid_cut( &rtip->rti_CutHead.nugn, &finp->bn, rtip, 0,0 );
00894                 rt_fr_cut( rtip, finp ); /* done with finite solids box */
00895                 break;
00896         case RT_PART_NUBSPT: {
00897 #ifdef NEW_WAY
00898                 struct nugridnode nuginfo;
00899 
00900                 /* Collect statistics to assist binary space partition tree
00901                    construction */
00902                 nuginfo.nu_type = CUT_NUGRIDNODE;
00903                 rt_nugrid_cut( &nuginfo, &fin_box, rtip, 1, 0 );
00904 #endif
00905                 rtip->rti_CutHead = *finp;      /* union copy */
00906 #ifdef NEW_WAY
00907                 if( rtip->nsolids < 50000 )  {
00908 #endif
00909                 /* Old way, non-parallel */
00910                 /* For some reason, this algorithm produces a substantial
00911                  * performance improvement over the parallel way, below.  The
00912                  * benchmark tests seem to be very sensitive to how the space
00913                  * partitioning is laid out.  Until we go to full NUgrid, this
00914                  * will have to do.  */
00915                         rt_ct_optim( rtip, &rtip->rti_CutHead, 0 );
00916 #ifdef NEW_WAY
00917                 } else {
00918 
00919                         XXX This hasnt been tested since massive
00920                             NUgrid changes were made
00921 
00922                         /* New way, mostly parallel */
00923                         union cutter    *head;
00924                         int     i;
00925 
00926                         head = rt_cut_one_axis( &rtip->rti_cuts_waiting, rtip,
00927                             Y, 0, nuginfo.nu_cells_per_axis[Y]-1, &nuginfo );
00928                         rtip->rti_CutHead = *head;      /* struct copy */
00929                         bu_free( (char *)head, "union cutter" );
00930 
00931                         if(RT_G_DEBUG&DEBUG_CUTDETAIL)  {
00932                                 for( i=0; i<3; i++ )  {
00933                                         bu_log("\nNUgrid %c axis:  %d cells\n",
00934                                      "XYZ*"[i], nuginfo.nu_cells_per_axis[i] );
00935                                 }
00936                                 rt_pr_cut( &rtip->rti_CutHead, 0 );
00937                         }
00938 
00939                         if( ncpu <= 1 )  {
00940                                 rt_cut_optimize_parallel(0, rtip);
00941                         } else {
00942                                 bu_parallel( rt_cut_optimize_parallel, ncpu, rtip );
00943                         }
00944                 }
00945 #endif
00946                 /* one more pass to find cells that are mostly empty */
00947                 num_splits = rt_split_mostly_empty_cells( rtip,  &rtip->rti_CutHead );
00948 
00949                 if( RT_G_DEBUG&DEBUG_CUT ) {
00950                         bu_log( "rt_split_mostly_empty_cells(): split %d cells\n", num_splits );
00951                 }
00952 
00953                 break; }
00954         default:
00955                 rt_bomb( "rt_cut_it: unknown space partitioning method\n" );
00956         }
00957 
00958         bu_free( (genptr_t)finp, "finite solid box" );
00959 
00960         /* Measure the depth of tree, find max # of RPPs in a cut node */
00961 
00962         bu_hist_init( &rtip->rti_hist_cellsize, 0.0, 400.0, 400 );
00963         bu_hist_init( &rtip->rti_hist_cell_pieces, 0.0, 400.0, 400 );
00964         bu_hist_init( &rtip->rti_hist_cutdepth, 0.0,
00965                       (fastf_t)rtip->rti_cutdepth+1, rtip->rti_cutdepth+1 );
00966         bzero( rtip->rti_ncut_by_type, sizeof(rtip->rti_ncut_by_type) );
00967         rt_ct_measure( rtip, &rtip->rti_CutHead, 0 );
00968         if( RT_G_DEBUG&DEBUG_CUT )  {
00969                 rt_pr_cut_info( rtip, "Cut" );
00970         }
00971 
00972         if( RT_G_DEBUG&DEBUG_CUTDETAIL ) {
00973                 /* Produce a voluminous listing of the cut tree */
00974                 rt_pr_cut( &rtip->rti_CutHead, 0 );
00975         }
00976 
00977         if( RT_G_DEBUG&DEBUG_PLOTBOX ) {
00978                 /* Debugging code to plot cuts */
00979                 if( (plotfp=fopen("rtcut.plot", "w"))!=NULL ) {
00980                         pdv_3space( plotfp, rtip->rti_pmin, rtip->rti_pmax );
00981                                 /* Plot all the cutting boxes */
00982                         rt_plot_cut( plotfp, rtip, &rtip->rti_CutHead, 0 );
00983                         (void)fclose(plotfp);
00984                 }
00985         }
00986 }
00987 
00988 /*
00989  *                      R T _ C U T _ E X T E N D
00990  *
00991  *  Add a solid into a given boxnode, extending the lists there.
00992  *  This is used only for building the root node, which will then
00993  *  be subdivided.
00994  *
00995  *  Solids with pieces go onto a special list.
00996  */
00997 void
00998 rt_cut_extend(register union cutter *cutp, struct soltab *stp, const struct rt_i *rtip)
00999 {
01000         RT_CK_SOLTAB(stp);
01001         RT_CK_RTI(rtip);
01002 
01003         BU_ASSERT( cutp->cut_type == CUT_BOXNODE );
01004 
01005         if(RT_G_DEBUG&DEBUG_CUTDETAIL)  {
01006                 bu_log("rt_cut_extend(cutp=x%x) %s npieces=%d\n",
01007                         cutp, stp->st_name, stp->st_npieces);
01008         }
01009 
01010         if( stp->st_npieces > 0 )  {
01011                 struct rt_piecelist *plp;
01012                 register int i;
01013 
01014                 if( cutp->bn.bn_piecelist == NULL )  {
01015                         /* Allocate enough piecelist's to hold all solids */
01016                         BU_ASSERT( rtip->nsolids > 0 );
01017                         cutp->bn.bn_piecelist = (struct rt_piecelist *) bu_malloc(
01018                                 sizeof(struct rt_piecelist) * (rtip->nsolids + 2),
01019                                 "rt_ct_box bn_piecelist (root node)" );
01020                         cutp->bn.bn_piecelen = 0;       /* sanity */
01021                         cutp->bn.bn_maxpiecelen = rtip->nsolids + 2;
01022                 }
01023                 plp = &cutp->bn.bn_piecelist[cutp->bn.bn_piecelen++];
01024                 plp->magic = RT_PIECELIST_MAGIC;
01025                 plp->stp = stp;
01026 
01027                 /* List every index that this solid has */
01028                 plp->npieces = stp->st_npieces;
01029                 plp->pieces = (long *)bu_malloc(
01030                         sizeof(long) * plp->npieces,
01031                         "pieces[]" );
01032                 for( i = stp->st_npieces-1; i >= 0; i-- )
01033                         plp->pieces[i] = i;
01034 
01035                 return;
01036         }
01037 
01038         /* No pieces, list the entire solid on bn_list */
01039         if( cutp->bn.bn_len >= cutp->bn.bn_maxlen )  {
01040                 /* Need to get more space in list.  */
01041                 if( cutp->bn.bn_maxlen <= 0 )  {
01042                         /* Initial allocation */
01043                         if( rtip->rti_cutlen > rtip->nsolids )
01044                                 cutp->bn.bn_maxlen = rtip->rti_cutlen;
01045                         else
01046                                 cutp->bn.bn_maxlen = rtip->nsolids + 2;
01047                         cutp->bn.bn_list = (struct soltab **)bu_malloc(
01048                                 cutp->bn.bn_maxlen * sizeof(struct soltab *),
01049                                 "rt_cut_extend: initial list alloc" );
01050                 } else {
01051                         cutp->bn.bn_maxlen *= 8;
01052                         cutp->bn.bn_list = (struct soltab **) bu_realloc(
01053                                 (genptr_t)cutp->bn.bn_list,
01054                                 sizeof(struct soltab *) * cutp->bn.bn_maxlen,
01055                                 "rt_cut_extend: list extend" );
01056                 }
01057         }
01058         cutp->bn.bn_list[cutp->bn.bn_len++] = stp;
01059 }
01060 
01061 /*
01062  *                      R T _ C T _ P L A N
01063  *
01064  *  Attempt to make an "optimal" cut of the given boxnode.
01065  *  Consider cuts along all three axis planes, and choose
01066  *  the one with the smallest "offcenter" metric.
01067  *
01068  *  Returns -
01069  *      -1      No cut is possible
01070  *       0      Node has been cut
01071  */
01072 HIDDEN int
01073 rt_ct_plan(struct rt_i *rtip, union cutter *cutp, int depth)
01074 {
01075         int     axis;
01076         int     status[3];
01077         double  where[3];
01078         double  offcenter[3];
01079         int     best;
01080         double  bestoff;
01081 
01082         RT_CK_RTI(rtip);
01083         for( axis = X; axis <= Z; axis++ )  {
01084 #if 0
01085  /* New way */
01086                 status[axis] = rt_ct_assess(
01087                         cutp, axis, &where[axis], &offcenter[axis] );
01088 #else
01089  /* Old way */
01090                 status[axis] = rt_ct_old_assess(
01091                         cutp, axis, &where[axis], &offcenter[axis] );
01092 #endif
01093         }
01094 
01095         for(;;)  {
01096                 best = -1;
01097                 bestoff = INFINITY;
01098                 for( axis = X; axis <= Z; axis++ )  {
01099                         if( status[axis] <= 0 )  continue;
01100                         if( offcenter[axis] >= bestoff )  continue;
01101                         /* This one is better than previous ones */
01102                         best = axis;
01103                         bestoff = offcenter[axis];
01104                 }
01105 
01106                 if( best < 0 )  return(-1);     /* No cut is possible */
01107 
01108                 if( rt_ct_box( rtip, cutp, best, where[best], 0 ) > 0 )
01109                         return(0);              /* OK */
01110 
01111                 /*
01112                  *  This cut failed to reduce complexity on either side.
01113                  *  Mark this status as bad, and try the next-best
01114                  *  opportunity, if any.
01115                  */
01116                 status[best] = 0;
01117         }
01118 }
01119 
01120 /*
01121  *                      R T _ C T _ A S S E S S
01122  *
01123  *  Assess the possibility of making a cut along the indicated axis.
01124  *
01125  *  Returns -
01126  *      0       if a cut along this axis is not possible
01127  *      1       if a cut along this axis *is* possible, plus:
01128  *              *where          is proposed cut point, and
01129  *              *offcenter      is distance from "optimum" cut location.
01130  */
01131 HIDDEN int
01132 rt_ct_assess(register union cutter *cutp, register int axis, double *where_p, double *offcenter_p)
01133 {
01134         register int    i;
01135         register double val;
01136         register double where;
01137         register double offcenter;      /* Closest distance from midpoint */
01138         register double middle;         /* midpoint */
01139         register double left, right;
01140 
01141         if( cutp->bn.bn_len <= 1 )  return(0);          /* Forget it */
01142 
01143         /*
01144          *  In absolute terms, each box must be at least 1mm wide after cut,
01145          *  so there is no need subdividing anything smaller than twice that.
01146          */
01147         if( (right=cutp->bn.bn_max[axis])-(left=cutp->bn.bn_min[axis]) <= 2.0 )
01148                 return(0);
01149 
01150         /*
01151          *  Split distance between min and max in half.
01152          *  Find the closest edge of a solid's bounding RPP
01153          *  to the mid-point, and split there.
01154          *  This should ordinarily guarantee that at least one side of the
01155          *  cut has one less item in it.
01156          *
01157          * XXX This should be much more sophisticated.
01158          * XXX Consider making a list of candidate cut points
01159          * (max and min of each bn_list[] element) with
01160          * the subscript stored.
01161          * Eliminaate candidates outside the current range.
01162          * Sort the list.
01163          * Eliminate duplicate candidates.
01164          * The the element in the middle of the candidate list.
01165          * Compute offcenter from middle of range as now.
01166          */
01167         middle = (left + right) * 0.5;
01168         where = offcenter = INFINITY;
01169         for( i=0; i < cutp->bn.bn_len; i++ )  {
01170                 /* left (min) edge */
01171                 val = cutp->bn.bn_list[i]->st_min[axis];
01172                 if( val > left && val < right )  {
01173                         register double d;
01174                         if( (d = val - middle) < 0 )  d = (-d);
01175                         if( d < offcenter )  {
01176                                 offcenter = d;
01177                                 where = val;
01178                         }
01179                 }
01180                 /* right (max) edge */
01181                 val = cutp->bn.bn_list[i]->st_max[axis];
01182                 if( val > left && val < right )  {
01183                         register double d;
01184                         if( (d = val - middle) < 0 )  d = (-d);
01185                         if( d < offcenter )  {
01186                                 offcenter = d;
01187                                 where = val;
01188                         }
01189                 }
01190         }
01191         if( offcenter >= INFINITY )
01192                 return(0);      /* no candidates? */
01193         if( where <= left || where >= right )
01194                 return(0);      /* not reasonable */
01195 
01196         if( where - left <= 1.0 || right - where <= 1.0 )
01197                 return(0);      /* cut will be too small */
01198 
01199         *where_p = where;
01200         *offcenter_p = offcenter;
01201         return(1);              /* OK */
01202 }
01203 
01204 #define PIECE_BLOCK     512
01205 
01206 /*
01207  *                      R T _ C T _ P O P U L A T E _ B O X
01208  *
01209  *  Given that 'outp' has been given a bounding box smaller than
01210  *  that of 'inp', copy over everything which still fits in the smaller box.
01211  *
01212  *  Returns -
01213  *      0       if outp has the same number of items as inp
01214  *      1       if outp has fewer items than inp
01215  */
01216 HIDDEN int
01217 rt_ct_populate_box(union cutter *outp, const union cutter *inp, struct rt_i *rtip)
01218 {
01219         register int    i;
01220         int success = 0;
01221         const struct bn_tol *tol = &rtip->rti_tol;
01222 
01223         /* Examine the solids */
01224         outp->bn.bn_len = 0;
01225         outp->bn.bn_maxlen = inp->bn.bn_len;
01226         if( outp->bn.bn_maxlen > 0 )  {
01227                 outp->bn.bn_list = (struct soltab **) bu_malloc(
01228                         sizeof(struct soltab *) * outp->bn.bn_maxlen,
01229                         "bn_list" );
01230                 for( i = inp->bn.bn_len-1; i >= 0; i-- )  {
01231                         struct soltab *stp = inp->bn.bn_list[i];
01232                         if( !rt_ck_overlap(outp->bn.bn_min, outp->bn.bn_max,
01233                             stp, rtip))
01234                                 continue;
01235                         outp->bn.bn_list[outp->bn.bn_len++] = stp;
01236                 }
01237                 if( outp->bn.bn_len < inp->bn.bn_len )  success = 1;
01238         } else {
01239                 outp->bn.bn_list = (struct soltab **)NULL;
01240         }
01241 
01242         /* Examine the solid pieces */
01243         outp->bn.bn_piecelen = 0;
01244         if( inp->bn.bn_piecelen <= 0 )  {
01245                 outp->bn.bn_piecelist = (struct rt_piecelist *)NULL;
01246                 outp->bn.bn_maxpiecelen = 0;
01247                 return success;
01248         }
01249 
01250         outp->bn.bn_piecelist = (struct rt_piecelist *) bu_malloc(
01251                 sizeof(struct rt_piecelist) * inp->bn.bn_piecelen,
01252                 "rt_piecelist" );
01253         outp->bn.bn_maxpiecelen = inp->bn.bn_piecelen;
01254 #if 0
01255         for( i = inp->bn.bn_piecelen-1; i >= 0; i-- )  {
01256                 struct rt_piecelist *plp = &inp->bn.bn_piecelist[i];    /* input */
01257                 struct soltab *stp = plp->stp;
01258                 struct rt_piecelist *olp = &outp->bn.bn_piecelist[outp->bn.bn_piecelen]; /* output */
01259                 int j;
01260 
01261                 RT_CK_PIECELIST(plp);
01262                 RT_CK_SOLTAB(stp);
01263                 olp->pieces = (long *)bu_malloc(
01264                         sizeof(long) * plp->npieces,
01265                         "olp->pieces[]" );
01266                 olp->npieces = 0;
01267 
01268                 /* Loop for every piece of this solid */
01269                 for( j = plp->npieces-1; j >= 0; j-- )  {
01270                         long indx = plp->pieces[j];
01271                         struct bound_rpp *rpp = &stp->st_piece_rpps[indx];
01272                         if( !V3RPP_OVERLAP_TOL(
01273                             outp->bn.bn_min, outp->bn.bn_max,
01274                             rpp->min, rpp->max, tol) )
01275                                 continue;
01276                         olp->pieces[olp->npieces++] = indx;
01277                 }
01278                 if( olp->npieces > 0 )  {
01279                         /* This solid contributed pieces to the output box */
01280                         olp->magic = RT_PIECELIST_MAGIC;
01281                         olp->stp = stp;
01282                         outp->bn.bn_piecelen++;
01283                         if( olp->npieces < plp->npieces ) success = 1;
01284                 } else {
01285                         bu_free( (char *)olp->pieces, "olp->pieces[]");
01286                         olp->pieces = NULL;
01287                 }
01288         }
01289 
01290 #else
01291         for( i = inp->bn.bn_piecelen-1; i >= 0; i-- )  {
01292                 struct rt_piecelist *plp = &inp->bn.bn_piecelist[i];    /* input */
01293                 struct soltab *stp = plp->stp;
01294                 struct rt_piecelist *olp = &outp->bn.bn_piecelist[outp->bn.bn_piecelen]; /* output */
01295                 int j,k;
01296                 long piece_list[PIECE_BLOCK];   /* array of pieces */
01297                 long piece_count=0;             /* count of used slots in above array */
01298                 long *more_pieces=NULL;         /* dynamically allocated array for overflow of above array */
01299                 long more_piece_count=0;        /* number of slots used in dynamic array */
01300                 long more_piece_len=0;          /* allocated length of dynamic array */
01301 
01302                 RT_CK_PIECELIST(plp);
01303                 RT_CK_SOLTAB(stp);
01304 
01305                 /* Loop for every piece of this solid */
01306                 for( j = plp->npieces-1; j >= 0; j-- )  {
01307                         long indx = plp->pieces[j];
01308                         struct bound_rpp *rpp = &stp->st_piece_rpps[indx];
01309                         if( !V3RPP_OVERLAP_TOL(
01310                             outp->bn.bn_min, outp->bn.bn_max,
01311                             rpp->min, rpp->max, tol) )
01312                                 continue;
01313                         if( piece_count < PIECE_BLOCK ) {
01314                                 piece_list[piece_count++] = indx;
01315                         } else if( more_piece_count >= more_piece_len ) {
01316                                 /* this should be an extemely rare occurrence */
01317                                 more_piece_len += PIECE_BLOCK;
01318                                 more_pieces = (long *)bu_realloc( more_pieces, more_piece_len * sizeof( long ),
01319                                                                   "more_pieces" );
01320                                 more_pieces[more_piece_count++] = indx;
01321                         } else {
01322                                 more_pieces[more_piece_count++] = indx;
01323                         }
01324                 }
01325                 olp->npieces = piece_count + more_piece_count;
01326                 if( olp->npieces > 0 )  {
01327                         /* This solid contributed pieces to the output box */
01328                         olp->magic = RT_PIECELIST_MAGIC;
01329                         olp->stp = stp;
01330                         outp->bn.bn_piecelen++;
01331                         olp->pieces = (long *)bu_malloc( sizeof(long) * olp->npieces, "olp->pieces[]" );
01332                         for( j=0 ; j<piece_count ; j++ ) {
01333                                 olp->pieces[j] = piece_list[j];
01334                         }
01335                         k = piece_count;
01336                         for( j=0 ; j<more_piece_count ; j++ ) {
01337                                 olp->pieces[k++] = more_pieces[j];
01338                         }
01339                         if( more_pieces ) {
01340                                 bu_free( (char *)more_pieces, "more_pieces" );
01341                         }
01342                         if( olp->npieces < plp->npieces ) success = 1;
01343                 } else {
01344                         olp->pieces = NULL;
01345                         /*                      if( plp->npieces > 0 ) success = 1; */
01346                 }
01347         }
01348 #endif
01349 
01350         return success;
01351 }
01352 
01353 /*
01354  *                      R T _ C T _ B O X
01355  *
01356  *  Cut the given box node with a plane along the given axis,
01357  *  at the specified distance "where".
01358  *  Convert the caller's box node into a cut node, allocating two
01359  *  additional box nodes for the new leaves.
01360  *
01361  *  If, according to the classifier, both sides have the same number
01362  *  of solids, then nothing is changed, and an error is returned.
01363  *
01364  *  The storage strategy used is to make the maximum length of
01365  *  each of the two child boxnodes be the current length of the
01366  *  source node.
01367  *
01368  *  Returns -
01369  *      0       failure
01370  *      1       success
01371  */
01372 HIDDEN int
01373 rt_ct_box(struct rt_i *rtip, register union cutter *cutp, register int axis, double where, int force)
01374 {
01375         register union cutter   *rhs, *lhs;
01376         int success = 0;
01377 
01378         RT_CK_RTI(rtip);
01379         if(RT_G_DEBUG&DEBUG_CUTDETAIL)  {
01380                 bu_log("rt_ct_box(x%x, %c) %g .. %g .. %g\n",
01381                         cutp, "XYZ345"[axis],
01382                         cutp->bn.bn_min[axis],
01383                         where,
01384                         cutp->bn.bn_max[axis]);
01385         }
01386 
01387         /* LEFT side */
01388         lhs = rt_ct_get(rtip);
01389         lhs->bn.bn_type = CUT_BOXNODE;
01390         VMOVE( lhs->bn.bn_min, cutp->bn.bn_min );
01391         VMOVE( lhs->bn.bn_max, cutp->bn.bn_max );
01392         lhs->bn.bn_max[axis] = where;
01393 
01394         success = rt_ct_populate_box( lhs, cutp, rtip );
01395 
01396         /* RIGHT side */
01397         rhs = rt_ct_get(rtip);
01398         rhs->bn.bn_type = CUT_BOXNODE;
01399         VMOVE( rhs->bn.bn_min, cutp->bn.bn_min );
01400         VMOVE( rhs->bn.bn_max, cutp->bn.bn_max );
01401         rhs->bn.bn_min[axis] = where;
01402 
01403         success += rt_ct_populate_box( rhs, cutp, rtip );
01404 
01405         /* Check to see if complexity didn't decrease */
01406         if( success == 0 && !force )  {
01407                 /*
01408                  *  This cut operation did no good, release storage,
01409                  *  and let caller attempt something else.
01410                  */
01411                 if(RT_G_DEBUG&DEBUG_CUTDETAIL)  {
01412                         static char axis_str[] = "XYZw";
01413                         bu_log("rt_ct_box:  no luck, len=%d, axis=%c\n",
01414                                 cutp->bn.bn_len, axis_str[axis] );
01415                 }
01416                 rt_ct_free( rtip, rhs );
01417                 rt_ct_free( rtip, lhs );
01418                 return(0);              /* fail */
01419         }
01420 
01421         /* Success, convert callers box node into a cut node */
01422         rt_ct_release_storage( cutp );
01423 
01424         cutp->cut_type = CUT_CUTNODE;
01425         cutp->cn.cn_axis = axis;
01426         cutp->cn.cn_point = where;
01427         cutp->cn.cn_l = lhs;
01428         cutp->cn.cn_r = rhs;
01429         return(1);                      /* success */
01430 }
01431 
01432 /*
01433  *                      R T _ C K _ O V E R L A P
01434  *
01435  *  See if any part of the solid is contained within the bounding box (RPP).
01436  *
01437  *  If the solid RPP at least partly overlaps the bounding RPP,
01438  *  invoke the per-solid "classifier" method to perform a more
01439  *  rigorous check.
01440  *
01441  *  Returns -
01442  *      !0      if object overlaps box.
01443  *      0       if no overlap.
01444  */
01445 HIDDEN int
01446 rt_ck_overlap(register const fastf_t *min, register const fastf_t *max, register const struct soltab *stp, register const struct rt_i *rtip)
01447 {
01448         RT_CHECK_SOLTAB(stp);
01449         if( RT_G_DEBUG&DEBUG_BOXING )  {
01450                 bu_log("rt_ck_overlap(%s)\n",stp->st_name);
01451                 VPRINT(" box min", min);
01452                 VPRINT(" sol min", stp->st_min);
01453                 VPRINT(" box max", max);
01454                 VPRINT(" sol max", stp->st_max);
01455         }
01456         /* Ignore "dead" solids in the list.  (They failed prep) */
01457         if( stp->st_aradius <= 0 )  return(0);
01458 
01459         /* Only check RPP on finite solids */
01460         if( stp->st_aradius < INFINITY )  {
01461                 if( V3RPP_DISJOINT( stp->st_min, stp->st_max, min, max ) )
01462                         goto fail;
01463         }
01464 
01465         /* RPP overlaps, invoke per-solid method for detailed check */
01466         if( rt_functab[stp->st_id].ft_classify( stp, min, max,
01467                           &rtip->rti_tol ) == RT_CLASSIFY_OUTSIDE )  goto fail;
01468 
01469         if( RT_G_DEBUG&DEBUG_BOXING )  bu_log("rt_ck_overlap:  TRUE\n");
01470         return(1);
01471 fail:
01472         if( RT_G_DEBUG&DEBUG_BOXING )  bu_log("rt_ck_overlap:  FALSE\n");
01473         return(0);
01474 }
01475 
01476 /*
01477  *                      R T _ C T _ P I E C E C O U N T
01478  *
01479  *  Returns the total number of solids and solid "pieces" in a boxnode.
01480  */
01481 HIDDEN int
01482 rt_ct_piececount(const union cutter *cutp)
01483 {
01484         int     i;
01485         int     count;
01486 
01487         BU_ASSERT( cutp->cut_type == CUT_BOXNODE );
01488 
01489         count = cutp->bn.bn_len;
01490 
01491         if( cutp->bn.bn_piecelen <= 0 )  return count;
01492 
01493         for( i = cutp->bn.bn_piecelen-1; i >= 0; i-- )  {
01494                 count += cutp->bn.bn_piecelist[i].npieces;
01495         }
01496         return count;
01497 }
01498 
01499 /*
01500  *                      R T _ C T _ O P T I M
01501  *
01502  *  Optimize a cut tree.  Work on nodes which are over the pre-set limits,
01503  *  subdividing until either the limit on tree depth runs out, or until
01504  *  subdivision no longer gives different results, which could easily be
01505  *  the case when several solids involved in a CSG operation overlap in
01506  *  space.
01507  */
01508 HIDDEN void
01509 rt_ct_optim(struct rt_i *rtip, register union cutter *cutp, int depth)
01510 {
01511         int oldlen;
01512 
01513         if( cutp->cut_type == CUT_CUTNODE )  {
01514                 rt_ct_optim( rtip, cutp->cn.cn_l, depth+1 );
01515                 rt_ct_optim( rtip, cutp->cn.cn_r, depth+1 );
01516                 return;
01517         }
01518         if( cutp->cut_type != CUT_BOXNODE )  {
01519                 bu_log("rt_ct_optim: bad node x%x\n", cutp->cut_type);
01520                 return;
01521         }
01522 
01523         oldlen = rt_ct_piececount(cutp);        /* save before rt_ct_box() */
01524         if( RT_G_DEBUG&DEBUG_CUTDETAIL )  bu_log("rt_ct_optim( cutp=x%x, depth=%d ) piececount=%d\n", cutp, depth, oldlen);
01525 
01526         /*
01527          * BOXNODE (leaf)
01528          */
01529         if( oldlen <= 1 )
01530                 return;         /* this box is already optimal */
01531         if( depth > rtip->rti_cutdepth )  return;               /* too deep */
01532 
01533         /* Attempt to subdivide finer than rtip->rti_cutlen near treetop */
01534         /**** XXX This test can be improved ****/
01535         if( depth >= 6 && oldlen <= rtip->rti_cutlen )
01536                 return;                         /* Fine enough */
01537 #if NEW_WAY
01538  /* New way */
01539         /*
01540          *  Attempt to make an optimal cut
01541          */
01542         if( rt_ct_plan( rtip, cutp, depth ) < 0 )  {
01543                 /* Unable to further subdivide this box node */
01544                 return;
01545         }
01546 #else
01547  /* Old (Release 3.7) way */
01548  {
01549         int did_a_cut;
01550         int i;
01551         int axis;
01552         double where, offcenter;
01553         /*
01554          *  In general, keep subdividing until things don't get any better.
01555          *  Really we might want to proceed for 2-3 levels.
01556          *
01557          *  First, make certain this is a worthwhile cut.
01558          *  In absolute terms, each box must be at least 1mm wide after cut.
01559          */
01560         axis = AXIS(depth);
01561 #if 1
01562         did_a_cut = 0;
01563         for( i=0 ; i<3 ; i++ ) {
01564                 axis += i;
01565                 if( axis > Z ) {
01566                         axis = X;
01567                 }
01568                 if( cutp->bn.bn_max[axis]-cutp->bn.bn_min[axis] < 2.0 ) {
01569                         continue;
01570                 }
01571                 if( rt_ct_old_assess( cutp, axis, &where, &offcenter ) <= 0 ) {
01572                         continue;
01573                 }
01574                 if( rt_ct_box( rtip, cutp, axis, where, 0 ) == 0 )  {
01575                         continue;
01576                 } else {
01577                         did_a_cut = 1;
01578                         break;
01579                 }
01580         }
01581 
01582         if( !did_a_cut ) {
01583                 return;
01584         }
01585 #else
01586         if( cutp->bn.bn_max[axis]-cutp->bn.bn_min[axis] < 2.0 )
01587                 return;
01588         if( rt_ct_old_assess( cutp, axis, &where, &offcenter ) <= 0 )
01589                 return;                 /* not practical */
01590         if( rt_ct_box( rtip, cutp, axis, where, 0 ) == 0 )  {
01591                 if( rt_ct_old_assess( cutp, AXIS(depth+1), &where, &offcenter ) <= 0 )
01592                         return;                 /* not practical */
01593                 if( rt_ct_box( rtip, cutp, AXIS(depth+1), where, 0 ) == 0 )
01594                         return; /* hopeless */
01595         }
01596 #endif
01597         if( rt_ct_piececount(cutp->cn.cn_l) >= oldlen &&
01598             rt_ct_piececount(cutp->cn.cn_r) >= oldlen )  {
01599                 if( RT_G_DEBUG&DEBUG_CUTDETAIL )
01600                 bu_log("rt_ct_optim(cutp=x%x, depth=%d) oldlen=%d, lhs=%d, rhs=%d, hopeless\n",
01601                         cutp, depth, oldlen,
01602                         rt_ct_piececount(cutp->cn.cn_l),
01603                         rt_ct_piececount(cutp->cn.cn_r) );
01604                 return; /* hopeless */
01605         }
01606  }
01607 #endif
01608         /* Box node is now a cut node, recurse */
01609         rt_ct_optim( rtip, cutp->cn.cn_l, depth+1 );
01610         rt_ct_optim( rtip, cutp->cn.cn_r, depth+1 );
01611 }
01612 
01613 /*
01614  *                      R T _ C T _ O L D _ A S S E S S
01615  *
01616  *  From RCS revision 9.4
01617  *  NOTE:  Changing from rt_ct_assess() to this seems to result
01618  *  in a *massive* change in cut tree size.
01619  *      This version results in nbins=22, maxlen=3, avg=1.09,
01620  *  while new vewsion results in nbins=42, maxlen=3, avg=1.667 (on moss.g).
01621  */
01622 HIDDEN int
01623 rt_ct_old_assess(register union cutter *cutp, register int axis, double *where_p, double *offcenter_p)
01624 {
01625         double          val;
01626         double          offcenter;              /* Closest distance from midpoint */
01627         double          where;          /* Point closest to midpoint */
01628         double          middle;         /* midpoint */
01629         double          d;
01630         fastf_t         max, min;
01631         register int    i;
01632         register double left, right;
01633 
01634         if(RT_G_DEBUG&DEBUG_CUTDETAIL)bu_log("rt_ct_old_assess(x%x, %c)\n",cutp,"XYZ345"[axis]);
01635 
01636         /*  In absolute terms, each box must be at least 1mm wide after cut. */
01637         if( (right=cutp->bn.bn_max[axis])-(left=cutp->bn.bn_min[axis]) < 2.0 )
01638                 return(0);
01639 
01640         /*
01641          *  Split distance between min and max in half.
01642          *  Find the closest edge of a solid's bounding RPP
01643          *  to the mid-point, and split there.
01644          *  This should ordinarily guarantee that at least one side of the
01645          *  cut has one less item in it.
01646          */
01647         min = MAX_FASTF;
01648         max = -min;
01649         where = left;
01650         middle = (left + right) * 0.5;
01651         offcenter = middle - where;     /* how far off 'middle', 'where' is */
01652         for( i=0; i < cutp->bn.bn_len; i++ )  {
01653                 val = cutp->bn.bn_list[i]->st_min[axis];
01654                 if( val < min ) min = val;
01655                 if( val > max ) max = val;
01656                 d = val - middle;
01657                 if( d < 0 )  d = (-d);
01658                 if( d < offcenter )  {
01659                         offcenter = d;
01660                         where = val-0.1;
01661                 }
01662                 val = cutp->bn.bn_list[i]->st_max[axis];
01663                 if( val < min ) min = val;
01664                 if( val > max ) max = val;
01665                 d = val - middle;
01666                 if( d < 0 )  d = (-d);
01667                 if( d < offcenter )  {
01668                         offcenter = d;
01669                         where = val+0.1;
01670                 }
01671         }
01672 
01673         /* Loop over all the solid pieces */
01674         for( i = cutp->bn.bn_piecelen-1; i >= 0; i-- )  {
01675                 struct rt_piecelist *plp = &cutp->bn.bn_piecelist[i];
01676                 struct soltab *stp = plp->stp;
01677                 int     j;
01678 
01679                 RT_CK_PIECELIST(plp);
01680                 for( j = plp->npieces-1; j >= 0; j-- )  {
01681                         int indx = plp->pieces[j];
01682                         struct bound_rpp *rpp = &stp->st_piece_rpps[indx];
01683 
01684                         val = rpp->min[axis];
01685                         if( val < min ) min = val;
01686                         if( val > max ) max = val;
01687                         d = val - middle;
01688                         if( d < 0 )  d = (-d);
01689                         if( d < offcenter )  {
01690                                 offcenter = d;
01691                                 where = val-0.1;
01692                         }
01693                         val = rpp->max[axis];
01694                         if( val < min ) min = val;
01695                         if( val > max ) max = val;
01696                         d = val - middle;
01697                         if( d < 0 )  d = (-d);
01698                         if( d < offcenter )  {
01699                                 offcenter = d;
01700                                 where = val+0.1;
01701                         }
01702                 }
01703         }
01704 
01705         if(RT_G_DEBUG&DEBUG_CUTDETAIL)bu_log("rt_ct_old_assess() left=%g, where=%g, right=%g, offcenter=%g\n",
01706 
01707               left, where, right, offcenter);
01708 
01709         if( where < min || where > max ) {
01710                 /* this will make an empty cell.
01711                  * try splitting the range instead
01712                  */
01713                 where = (max + min) / 2.0;
01714                 offcenter = where - middle;
01715                 if( offcenter < 0 ) {
01716                         offcenter = -offcenter;
01717                 }
01718         }
01719 
01720         if( where <= left || where >= right )
01721                 return(0);      /* not reasonable */
01722 
01723         if( where - left <= 1.0 || right - where <= 1.0 )
01724                 return(0);      /* cut will be too small */
01725 
01726         /* We are going to cut */
01727         *where_p = where;
01728         *offcenter_p = offcenter;
01729         return(1);
01730 }
01731 
01732 /*
01733  *                      R T _ C T _ G E T
01734  *
01735  *  This routine must run in parallel
01736  */
01737 HIDDEN union cutter *
01738 rt_ct_get(struct rt_i *rtip)
01739 {
01740         register union cutter *cutp;
01741 
01742         RT_CK_RTI(rtip);
01743         bu_semaphore_acquire(RT_SEM_MODEL);
01744         if( !rtip->rti_busy_cutter_nodes.l.magic )
01745                 bu_ptbl_init( &rtip->rti_busy_cutter_nodes, 128, "rti_busy_cutter_nodes" );
01746 
01747         if( rtip->rti_CutFree == CUTTER_NULL )  {
01748                 register int bytes;
01749 
01750                 bytes = bu_malloc_len_roundup(64*sizeof(union cutter));
01751                 cutp = (union cutter *)bu_malloc(bytes," rt_ct_get");
01752                 /* Remember this allocation for later */
01753                 bu_ptbl_ins( &rtip->rti_busy_cutter_nodes, (long *)cutp );
01754                 /* Now, dice it up */
01755                 while( bytes >= sizeof(union cutter) )  {
01756                         cutp->cut_forw = rtip->rti_CutFree;
01757                         rtip->rti_CutFree = cutp++;
01758                         bytes -= sizeof(union cutter);
01759                 }
01760         }
01761         cutp = rtip->rti_CutFree;
01762         rtip->rti_CutFree = cutp->cut_forw;
01763         bu_semaphore_release(RT_SEM_MODEL);
01764 
01765         cutp->cut_forw = CUTTER_NULL;
01766         return(cutp);
01767 }
01768 
01769 /*
01770  *                      R T _ C T _ R E L E A S E _ S T O R A G E
01771  *
01772  *  Release subordinate storage
01773  */
01774 HIDDEN void
01775 rt_ct_release_storage(register union cutter *cutp)
01776 {
01777         int i;
01778 
01779         switch( cutp->cut_type )  {
01780 
01781         case CUT_CUTNODE:
01782                 break;
01783 
01784         case CUT_BOXNODE:
01785                 if( cutp->bn.bn_list )  {
01786                         bu_free( (char *)cutp->bn.bn_list, "bn_list[]");
01787                         cutp->bn.bn_list = (struct soltab **)NULL;
01788                 }
01789                 cutp->bn.bn_len = 0;
01790                 cutp->bn.bn_maxlen = 0;
01791 
01792                 if( cutp->bn.bn_piecelist )  {
01793                         for( i=0 ; i<cutp->bn.bn_piecelen ; i++ ) {
01794                                 struct rt_piecelist *olp = &cutp->bn.bn_piecelist[i];
01795                                 if( olp->pieces ) {
01796                                         bu_free( (char *)olp->pieces, "olp->pieces" );
01797                                 }
01798                         }
01799                         bu_free( (char *)cutp->bn.bn_piecelist, "bn_piecelist[]" );
01800                         cutp->bn.bn_piecelist = (struct rt_piecelist *)NULL;
01801                 }
01802                 cutp->bn.bn_piecelen = 0;
01803                 cutp->bn.bn_maxpiecelen = 0;
01804                 break;
01805 
01806         case CUT_NUGRIDNODE:
01807                 bu_free( (genptr_t)cutp->nugn.nu_grid, "NUGrid children" );
01808                 cutp->nugn.nu_grid = NULL; /* sanity */
01809 
01810                 for( i=0; i<3; i++ ) {
01811                         bu_free( (genptr_t)cutp->nugn.nu_axis[i],
01812                                  "NUGrid axis" );
01813                         cutp->nugn.nu_axis[i] = NULL; /* sanity */
01814                 }
01815                 break;
01816 
01817         default:
01818                 bu_log("rt_ct_release_storage: Unknown type=x%x\n", cutp->cut_type );
01819                 break;
01820         }
01821 }
01822 
01823 /*
01824  *                      R T _ C T _ F R E E
01825  *
01826  *  This routine must run in parallel
01827  */
01828 HIDDEN void
01829 rt_ct_free(struct rt_i *rtip, register union cutter *cutp)
01830 {
01831         RT_CK_RTI(rtip);
01832 
01833         rt_ct_release_storage( cutp );
01834 
01835         /* Put on global free list */
01836         bu_semaphore_acquire(RT_SEM_MODEL);
01837         cutp->cut_forw = rtip->rti_CutFree;
01838         rtip->rti_CutFree = cutp;
01839         bu_semaphore_release(RT_SEM_MODEL);
01840 }
01841 
01842 /*
01843  *                      R T _ P R _ C U T
01844  *
01845  *  Print out a cut tree.
01846  */
01847 void
01848 rt_pr_cut(register const union cutter *cutp, int lvl)
01849 
01850                                 /* recursion level */
01851 {
01852         register int i,j;
01853 
01854         bu_log("%.8x ", cutp);
01855         for( i=lvl; i>0; i-- )
01856                 bu_log("   ");
01857 
01858         if( cutp == CUTTER_NULL )  {
01859                 bu_log("Null???\n");
01860                 return;
01861         }
01862 
01863         switch( cutp->cut_type )  {
01864 
01865         case CUT_CUTNODE:
01866                 bu_log("CUT L %c < %f\n",
01867                         "XYZ?"[cutp->cn.cn_axis],
01868                         cutp->cn.cn_point );
01869                 rt_pr_cut( cutp->cn.cn_l, lvl+1 );
01870 
01871                 bu_log("%.8x ", cutp);
01872                 for( i=lvl; i>0; i-- )
01873                         bu_log("   ");
01874                 bu_log("CUT R %c >= %f\n",
01875                         "XYZ?"[cutp->cn.cn_axis],
01876                         cutp->cn.cn_point );
01877                 rt_pr_cut( cutp->cn.cn_r, lvl+1 );
01878                 return;
01879 
01880         case CUT_BOXNODE:
01881                 bu_log("BOX Contains %d prims (%d alloc), %d prims with pieces:\n",
01882                         cutp->bn.bn_len, cutp->bn.bn_maxlen,
01883                         cutp->bn.bn_piecelen );
01884                 bu_log("        ");
01885                 for( i=lvl; i>0; i-- )
01886                         bu_log("   ");
01887                 VPRINT(" min", cutp->bn.bn_min);
01888                 bu_log("        ");
01889                 for( i=lvl; i>0; i-- )
01890                         bu_log("   ");
01891                 VPRINT(" max", cutp->bn.bn_max);
01892 
01893                 /* Print names of regular solids */
01894                 for( i=0; i < cutp->bn.bn_len; i++ )  {
01895                         bu_log("        ");
01896                         for( j=lvl; j>0; j-- )
01897                                 bu_log("   ");
01898                         bu_log("    %s\n",
01899                                 cutp->bn.bn_list[i]->st_name);
01900                 }
01901 
01902                 /* Print names and piece lists of solids with pieces */
01903                 for( i=0; i < cutp->bn.bn_piecelen; i++ )  {
01904                         struct rt_piecelist *plp = &(cutp->bn.bn_piecelist[i]);
01905                         struct soltab *stp;
01906                         int j;
01907 
01908                         RT_CK_PIECELIST(plp);
01909                         stp = plp->stp;
01910                         RT_CK_SOLTAB(stp);
01911 
01912                         bu_log("        ");
01913                         for( j=lvl; j>0; j-- )
01914                                 bu_log("   ");
01915                         bu_log("    %s, %d pieces: ",
01916                                 stp->st_name, plp->npieces);
01917 
01918                         /* Loop for every piece of this solid */
01919                         for( j=0; j < plp->npieces; j++ )  {
01920                                 long indx = plp->pieces[j];
01921                                 bu_log("%ld, ", indx);
01922                         }
01923                         bu_log("\n");
01924                 }
01925                 return;
01926 
01927         case CUT_NUGRIDNODE:
01928                 /* not implemented yet */
01929         default:
01930                 bu_log("Unknown type=x%x\n", cutp->cut_type );
01931                 break;
01932         }
01933         return;
01934 }
01935 
01936 /*
01937  *                      R T _ F R _ C U T
01938  *
01939  *  Free a whole cut tree below the indicated node.
01940  *  The strategy we use here is to free everything BELOW the given
01941  *  node, so as not to clobber rti_CutHead !
01942  */
01943 void
01944 rt_fr_cut(struct rt_i *rtip, register union cutter *cutp)
01945 {
01946         RT_CK_RTI(rtip);
01947         if( cutp == CUTTER_NULL )  {
01948                 bu_log("rt_fr_cut NULL\n");
01949                 return;
01950         }
01951 
01952         switch( cutp->cut_type )  {
01953 
01954         case CUT_CUTNODE:
01955                 rt_fr_cut( rtip, cutp->cn.cn_l );
01956                 rt_ct_free( rtip, cutp->cn.cn_l );
01957                 cutp->cn.cn_l = CUTTER_NULL;
01958 
01959                 rt_fr_cut( rtip, cutp->cn.cn_r );
01960                 rt_ct_free( rtip, cutp->cn.cn_r );
01961                 cutp->cn.cn_r = CUTTER_NULL;
01962                 return;
01963 
01964         case CUT_BOXNODE:
01965                 rt_ct_release_storage(cutp);
01966                 return;
01967 
01968         case CUT_NUGRIDNODE: {
01969                 register int i;
01970                 int len = cutp->nugn.nu_cells_per_axis[X] *
01971                         cutp->nugn.nu_cells_per_axis[Y] *
01972                         cutp->nugn.nu_cells_per_axis[Z];
01973                 register union cutter *bp = cutp->nugn.nu_grid;
01974 
01975                 for( i = len-1; i >= 0; i-- )  {
01976                         rt_fr_cut( rtip, bp );
01977                         bp->cut_type = 7;       /* sanity */
01978                         bp++;
01979                 }
01980 
01981                 rt_ct_release_storage(cutp);
01982                 return; }
01983 
01984         default:
01985                 bu_log("rt_fr_cut: Unknown type=x%x\n", cutp->cut_type );
01986                 break;
01987         }
01988         return;
01989 }
01990 
01991 /*
01992  *                      R T _ P L O T _ C U T
01993  */
01994 HIDDEN void
01995 rt_plot_cut(FILE *fp, struct rt_i *rtip, register union cutter *cutp, int lvl)
01996 {
01997         RT_CK_RTI(rtip);
01998         switch( cutp->cut_type )  {
01999         case CUT_NUGRIDNODE: {
02000                 union cutter *bp;
02001                 int i, x, y, z;
02002                 int xmax = cutp->nugn.nu_cells_per_axis[X],
02003                     ymax = cutp->nugn.nu_cells_per_axis[Y],
02004                     zmax = cutp->nugn.nu_cells_per_axis[Z];
02005                 if( !cutp->nugn.nu_grid ) return;
02006                 /* Don't draw the boxnodes since that produces far too many
02007                    segments.  Optimize the output a little by drawing whole
02008                    line segments (below). */
02009                 for( i=0, bp=cutp->nugn.nu_grid; i < xmax*ymax*zmax; i++, bp++ ) {
02010                         if( bp->cut_type != CUT_BOXNODE )
02011                                 rt_plot_cut( fp, rtip, bp, lvl );
02012                 }
02013                 pl_color( fp, 180, 180, 220 );
02014                 --xmax; --ymax; --zmax;
02015 
02016                 /* XY plane */
02017                 pd_3line( fp,
02018                           cutp->nugn.nu_axis[X][0].nu_spos,
02019                           cutp->nugn.nu_axis[Y][0].nu_spos,
02020                           cutp->nugn.nu_axis[Z][0].nu_spos,
02021                           cutp->nugn.nu_axis[X][0].nu_spos,
02022                           cutp->nugn.nu_axis[Y][0].nu_spos,
02023                           cutp->nugn.nu_axis[Z][zmax].nu_epos);
02024                 for( y=0; y<=ymax; y++ ) {
02025                         pd_3line( fp,
02026                                   cutp->nugn.nu_axis[X][0].nu_spos,
02027                                   cutp->nugn.nu_axis[Y][y].nu_epos,
02028                                   cutp->nugn.nu_axis[Z][0].nu_spos,
02029                                   cutp->nugn.nu_axis[X][0].nu_spos,
02030                                   cutp->nugn.nu_axis[Y][y].nu_epos,
02031                                   cutp->nugn.nu_axis[Z][zmax].nu_epos);
02032                 }
02033                 for( x=0; x<=xmax; x++ ) {
02034                         pd_3line( fp,
02035                                   cutp->nugn.nu_axis[X][x].nu_epos,
02036                                   cutp->nugn.nu_axis[Y][0].nu_spos,
02037                                   cutp->nugn.nu_axis[Z][0].nu_spos,
02038                                   cutp->nugn.nu_axis[X][x].nu_epos,
02039                                   cutp->nugn.nu_axis[Y][0].nu_spos,
02040                                   cutp->nugn.nu_axis[Z][zmax].nu_epos);
02041                         for( y=0; y<=ymax; y++ ) {
02042                                 pd_3line( fp,
02043                                           cutp->nugn.nu_axis[X][x].nu_epos,
02044                                           cutp->nugn.nu_axis[Y][y].nu_epos,
02045                                           cutp->nugn.nu_axis[Z][0].nu_spos,
02046                                           cutp->nugn.nu_axis[X][x].nu_epos,
02047                                           cutp->nugn.nu_axis[Y][y].nu_epos,
02048                                           cutp->nugn.nu_axis[Z][zmax].nu_epos);
02049                         }
02050                 }
02051 
02052                 /* YZ plane */
02053                 pd_3line( fp,
02054                           cutp->nugn.nu_axis[X][0].nu_spos,
02055                           cutp->nugn.nu_axis[Y][0].nu_spos,
02056                           cutp->nugn.nu_axis[Z][0].nu_spos,
02057                           cutp->nugn.nu_axis[X][xmax].nu_epos,
02058                           cutp->nugn.nu_axis[Y][0].nu_spos,
02059                           cutp->nugn.nu_axis[Z][0].nu_spos);
02060                 for( z=0; z<=zmax; z++ ) {
02061                         pd_3line( fp,
02062                                   cutp->nugn.nu_axis[X][0].nu_spos,
02063                                   cutp->nugn.nu_axis[Y][0].nu_spos,
02064                                   cutp->nugn.nu_axis[Z][z].nu_epos,
02065                                   cutp->nugn.nu_axis[X][zmax].nu_epos,
02066                                   cutp->nugn.nu_axis[Y][0].nu_spos,
02067                                   cutp->nugn.nu_axis[Z][z].nu_epos);
02068                 }
02069                 for( y=0; y<=ymax; y++ ) {
02070                         pd_3line( fp,
02071                                   cutp->nugn.nu_axis[X][0].nu_spos,
02072                                   cutp->nugn.nu_axis[Y][y].nu_epos,
02073                                   cutp->nugn.nu_axis[Z][0].nu_spos,
02074                                   cutp->nugn.nu_axis[X][xmax].nu_epos,
02075                                   cutp->nugn.nu_axis[Y][y].nu_epos,
02076                                   cutp->nugn.nu_axis[Z][0].nu_spos);
02077                         for( z=0; z<=zmax; z++ ) {
02078                                 pd_3line( fp,
02079                                           cutp->nugn.nu_axis[X][0].nu_spos,
02080                                           cutp->nugn.nu_axis[Y][y].nu_epos,
02081                                           cutp->nugn.nu_axis[Z][z].nu_epos,
02082                                           cutp->nugn.nu_axis[X][zmax].nu_epos,
02083                                           cutp->nugn.nu_axis[Y][y].nu_epos,
02084                                           cutp->nugn.nu_axis[Z][z].nu_epos);
02085                         }
02086                 }
02087 
02088                 /* XZ plane */
02089                 pd_3line( fp,
02090                           cutp->nugn.nu_axis[X][0].nu_spos,
02091                           cutp->nugn.nu_axis[Y][0].nu_spos,
02092                           cutp->nugn.nu_axis[Z][0].nu_spos,
02093                           cutp->nugn.nu_axis[X][0].nu_spos,
02094                           cutp->nugn.nu_axis[Y][ymax].nu_epos,
02095                           cutp->nugn.nu_axis[Z][0].nu_spos);
02096                 for( z=0; z<=zmax; z++ ) {
02097                         pd_3line( fp,
02098                                   cutp->nugn.nu_axis[X][0].nu_spos,
02099                                   cutp->nugn.nu_axis[Y][0].nu_spos,
02100                                   cutp->nugn.nu_axis[Z][z].nu_epos,
02101                                   cutp->nugn.nu_axis[X][0].nu_spos,
02102                                   cutp->nugn.nu_axis[Y][ymax].nu_epos,
02103                                   cutp->nugn.nu_axis[Z][z].nu_epos);
02104                 }
02105                 for( x=0; x<=xmax; x++ ) {
02106                         pd_3line( fp,
02107                                   cutp->nugn.nu_axis[X][x].nu_epos,
02108                                   cutp->nugn.nu_axis[Y][0].nu_spos,
02109                                   cutp->nugn.nu_axis[Z][0].nu_spos,
02110                                   cutp->nugn.nu_axis[X][x].nu_epos,
02111                                   cutp->nugn.nu_axis[Y][ymax].nu_epos,
02112                                   cutp->nugn.nu_axis[Z][0].nu_spos);
02113                         for( z=0; z<=zmax; z++ ) {
02114                                 pd_3line( fp,
02115                                           cutp->nugn.nu_axis[X][x].nu_epos,
02116                                           cutp->nugn.nu_axis[Y][0].nu_spos,
02117                                           cutp->nugn.nu_axis[Z][z].nu_epos,
02118                                           cutp->nugn.nu_axis[X][x].nu_epos,
02119                                           cutp->nugn.nu_axis[Y][ymax].nu_epos,
02120                                           cutp->nugn.nu_axis[Z][z].nu_epos);
02121                         }
02122                 }
02123 
02124                 return; }
02125         case CUT_CUTNODE:
02126                 rt_plot_cut( fp, rtip, cutp->cn.cn_l, lvl+1 );
02127                 rt_plot_cut( fp, rtip, cutp->cn.cn_r, lvl+1 );
02128                 return;
02129         case CUT_BOXNODE:
02130                 /* Should choose color based on lvl, need a table */
02131                 pl_color( fp,
02132                         (AXIS(lvl)==0)?255:0,
02133                         (AXIS(lvl)==1)?255:0,
02134                         (AXIS(lvl)==2)?255:0 );
02135                 pdv_3box( fp, cutp->bn.bn_min, cutp->bn.bn_max );
02136                 return;
02137         }
02138         return;
02139 }
02140 
02141 /*
02142  *                      R T _ C T _ M E A S U R E
02143  *
02144  *  Find the maximum number of solids in a leaf node,
02145  *  and other interesting statistics.
02146  */
02147 HIDDEN void
02148 rt_ct_measure(register struct rt_i *rtip, register union cutter *cutp, int depth)
02149 {
02150         register int    len;
02151 
02152         RT_CK_RTI(rtip);
02153         switch( cutp->cut_type ) {
02154         case CUT_NUGRIDNODE: {
02155                 register int i;
02156                 register union cutter *bp;
02157                 len = cutp->nugn.nu_cells_per_axis[X] *
02158                         cutp->nugn.nu_cells_per_axis[Y] *
02159                         cutp->nugn.nu_cells_per_axis[Z];
02160                 rtip->rti_ncut_by_type[CUT_NUGRIDNODE]++;
02161                 for( i=0, bp=cutp->nugn.nu_grid; i<len; i++, bp++ )
02162                         rt_ct_measure( rtip, bp, depth+1 );
02163                 return; }
02164         case CUT_CUTNODE:
02165                 rtip->rti_ncut_by_type[CUT_CUTNODE]++;
02166                 rt_ct_measure( rtip, cutp->cn.cn_l, len = (depth+1) );
02167                 rt_ct_measure( rtip, cutp->cn.cn_r, len );
02168                 return;
02169         case CUT_BOXNODE:
02170                 rtip->rti_ncut_by_type[CUT_BOXNODE]++;
02171                 rtip->rti_cut_totobj += (len = cutp->bn.bn_len);
02172                 if( rtip->rti_cut_maxlen < len )
02173                         rtip->rti_cut_maxlen = len;
02174                 if( rtip->rti_cut_maxdepth < depth )
02175                         rtip->rti_cut_maxdepth = depth;
02176                 BU_HIST_TALLY( &rtip->rti_hist_cellsize, len );
02177                 len = rt_ct_piececount( cutp ) - len;
02178                 BU_HIST_TALLY( &rtip->rti_hist_cell_pieces, len );
02179                 BU_HIST_TALLY( &rtip->rti_hist_cutdepth, depth );
02180                 if( len == 0 ) {
02181                         rtip->nempty_cells++;
02182                 }
02183                 return;
02184         default:
02185                 bu_log("rt_ct_measure: bad node x%x\n", cutp->cut_type);
02186                 return;
02187         }
02188 }
02189 
02190 /*
02191  *                      R T _ C U T _ C L E A N
02192  *
02193  *  The rtip->rti_CutFree list can not be freed directly
02194  *  because  is bulk allocated.
02195  *  Fortunately, we have a list of all the bu_malloc()'ed blocks.
02196  *  This routine may be called before the first frame is done,
02197  *  so it must be prepared for uninitialized items.
02198  */
02199 void
02200 rt_cut_clean(struct rt_i *rtip)
02201 {
02202         genptr_t        *p;
02203 
02204         RT_CK_RTI(rtip);
02205 
02206         if( rtip->rti_cuts_waiting.l.magic )
02207                 bu_ptbl_free( &rtip->rti_cuts_waiting );
02208 
02209         /* Abandon the linked list of diced-up structures */
02210         rtip->rti_CutFree = CUTTER_NULL;
02211 
02212         if( BU_LIST_UNINITIALIZED(&rtip->rti_busy_cutter_nodes.l) )
02213                 return;
02214 
02215         /* Release the blocks we got from bu_malloc() */
02216         for( BU_PTBL_FOR( p, (genptr_t *), &rtip->rti_busy_cutter_nodes ) )  {
02217                 bu_free( *p, "rt_ct_get" );
02218         }
02219         bu_ptbl_free( &rtip->rti_busy_cutter_nodes );
02220 }
02221 
02222 /*
02223  *                      R T _ P R _ C U T _ I N F O
02224  */
02225 void
02226 rt_pr_cut_info(const struct rt_i *rtip, const char *str)
02227 {
02228         const struct nugridnode *nugnp;
02229         int                     i;
02230 
02231         RT_CK_RTI(rtip);
02232 
02233         bu_log("%s %s: %d nu, %d cut, %d box (%d empty)\n",
02234                 str,
02235                 rtip->rti_space_partition == RT_PART_NUGRID ?
02236                         "NUGrid" : "NUBSP",
02237                 rtip->rti_ncut_by_type[CUT_NUGRIDNODE],
02238                 rtip->rti_ncut_by_type[CUT_CUTNODE],
02239                 rtip->rti_ncut_by_type[CUT_BOXNODE],
02240                 rtip->nempty_cells );
02241         bu_log( "Cut: maxdepth=%d, nbins=%d, maxlen=%d, avg=%g\n",
02242                 rtip->rti_cut_maxdepth,
02243                 rtip->rti_ncut_by_type[CUT_BOXNODE],
02244                 rtip->rti_cut_maxlen,
02245                 ((double)rtip->rti_cut_totobj) /
02246                 rtip->rti_ncut_by_type[CUT_BOXNODE] );
02247         bu_hist_pr( &rtip->rti_hist_cellsize,
02248                     "cut_tree: Number of prims per leaf cell");
02249         bu_hist_pr( &rtip->rti_hist_cell_pieces,
02250                     "cut_tree: Number of prim pieces per leaf cell");
02251         bu_hist_pr( &rtip->rti_hist_cutdepth,
02252                     "cut_tree: Depth (height)");
02253 
02254         switch( rtip->rti_space_partition )  {
02255         case RT_PART_NUGRID:
02256                 nugnp = &rtip->rti_CutHead.nugn;
02257                 if( nugnp->nu_type != CUT_NUGRIDNODE )
02258                         bu_bomb( "rt_pr_cut_info: passed non-nugridnode" );
02259 
02260                 for( i=0; i<3; i++ ) {
02261                         register int j;
02262                         bu_log( "NUgrid %c axis:  %d cells\n", "XYZ*"[i],
02263                                 nugnp->nu_cells_per_axis[i] );
02264                         for( j=0; j<nugnp->nu_cells_per_axis[i]; j++ ) {
02265                                 bu_log( "  %g .. %g, w=%g\n",
02266                                         nugnp->nu_axis[i][j].nu_spos,
02267                                         nugnp->nu_axis[i][j].nu_epos,
02268                                         nugnp->nu_axis[i][j].nu_width );
02269                         }
02270                 }
02271                 break;
02272         case RT_PART_NUBSPT:
02273                 /* Anything good to print here? */
02274                 break;
02275         default:
02276                 bu_bomb("rt_pr_cut_info() bad rti_space_partition\n");
02277         }
02278 }
02279 
02280 void
02281 remove_from_bsp( struct soltab *stp, union cutter *cutp, struct bn_tol *tol )
02282 {
02283         int index;
02284         int i;
02285 
02286         switch( cutp->cut_type ) {
02287         case CUT_BOXNODE:
02288                 if( stp->st_npieces ) {
02289                         int remove_count, new_count;
02290                         struct rt_piecelist *new_piece_list;
02291 
02292                         index = 0;
02293                         remove_count = 0;
02294                         for( index=0 ; index<cutp->bn.bn_piecelen ; index++ ) {
02295                                 if( cutp->bn.bn_piecelist[index].stp == stp ) {
02296                                         remove_count++;
02297                                 }
02298                         }
02299 
02300                         if( remove_count ) {
02301                                 new_count = cutp->bn.bn_piecelen - remove_count;
02302                                 if( new_count > 0 ) {
02303                                         new_piece_list = (struct rt_piecelist *)bu_calloc(
02304                                                                     new_count,
02305                                                                     sizeof( struct rt_piecelist ),
02306                                                                     "bn_piecelist" );
02307 
02308                                         i = 0;
02309                                         for( index=0 ; index<cutp->bn.bn_piecelen ; index++ ) {
02310                                                 if( cutp->bn.bn_piecelist[index].stp != stp ) {
02311                                                         new_piece_list[i] = cutp->bn.bn_piecelist[index];
02312                                                         i++;
02313                                                 }
02314                                         }
02315                                 } else {
02316                                         new_count = 0;
02317                                         new_piece_list = NULL;
02318                                 }
02319 
02320                                 for( index=0 ; index<cutp->bn.bn_piecelen ; index++ ) {
02321                                         if( cutp->bn.bn_piecelist[index].stp == stp ) {
02322                                                 bu_free( cutp->bn.bn_piecelist[index].pieces, "pieces" );
02323                                         }
02324                                 }
02325                                 bu_free( cutp->bn.bn_piecelist, "piecelist" );
02326                                 cutp->bn.bn_piecelist = new_piece_list;
02327                                 cutp->bn.bn_piecelen = new_count;
02328                                 cutp->bn.bn_maxpiecelen = new_count;
02329                         }
02330                 } else {
02331                         for( index=0 ; index < cutp->bn.bn_len ; index++ ) {
02332                                 if( cutp->bn.bn_list[index] == stp ) {
02333                                         /* found it, now remove it */
02334                                         cutp->bn.bn_len--;
02335                                         for( i=index ; i < cutp->bn.bn_len ; i++ ) {
02336                                                 cutp->bn.bn_list[i] = cutp->bn.bn_list[i+1];
02337                                         }
02338                                         return;
02339                                 }
02340                         }
02341                 }
02342                 break;
02343         case CUT_CUTNODE:
02344                 if( stp->st_min[cutp->cn.cn_axis] > cutp->cn.cn_point + tol->dist ) {
02345                         remove_from_bsp( stp, cutp->cn.cn_r, tol );
02346                 } else if( stp->st_max[cutp->cn.cn_axis] < cutp->cn.cn_point - tol->dist ) {
02347                         remove_from_bsp( stp, cutp->cn.cn_l, tol );
02348                 } else {
02349                         remove_from_bsp( stp, cutp->cn.cn_r, tol );
02350                         remove_from_bsp( stp, cutp->cn.cn_l, tol );
02351                 }
02352                 break;
02353         default:
02354                 bu_log( "remove_from_bsp(): unrecognized cut type (%d) in BSP!!!\n" );
02355                 bu_bomb( "remove_from_bsp(): unrecognized cut type in BSP!!!\n" );
02356         }
02357 }
02358 
02359 #define PIECE_BLOCK 512
02360 
02361 void
02362 insert_in_bsp( struct soltab *stp, union cutter *cutp )
02363 {
02364         int i,j;
02365 
02366         switch( cutp->cut_type ) {
02367         case CUT_BOXNODE:
02368                 if( stp->st_npieces == 0 ) {
02369                         /* add the solid in this box */
02370                         if( cutp->bn.bn_len >= cutp->bn.bn_maxlen ) {
02371                                 /* need more space */
02372                                 if( cutp->bn.bn_maxlen <= 0 )  {
02373                                         /* Initial allocation */
02374                                         cutp->bn.bn_maxlen = 5;
02375                                         cutp->bn.bn_list = (struct soltab **)bu_malloc(
02376                                                    cutp->bn.bn_maxlen * sizeof(struct soltab *),
02377                                                         "insert_in_bsp: initial list alloc" );
02378                                 } else {
02379                                         cutp->bn.bn_maxlen += 5;
02380                                         cutp->bn.bn_list = (struct soltab **) bu_realloc(
02381                                                                 (genptr_t)cutp->bn.bn_list,
02382                                                                 sizeof(struct soltab *) * cutp->bn.bn_maxlen,
02383                                                                 "insert_in_bsp: list extend" );
02384                                 }
02385                         }
02386                         cutp->bn.bn_list[cutp->bn.bn_len++] = stp;
02387 
02388                 } else {
02389                         /* this solid uses pieces, add the appropriate pieces to this box */
02390                         long pieces[PIECE_BLOCK];
02391                         long *more_pieces=NULL;
02392                         long more_pieces_alloced=0;
02393                         long more_pieces_count=0;
02394                         long piece_count=0;
02395                         struct rt_piecelist *plp;
02396 
02397                         for( i=0 ; i<stp->st_npieces ; i++ ) {
02398                                 struct bound_rpp *piece_rpp=&stp->st_piece_rpps[i];
02399                                 if( V3RPP_OVERLAP( piece_rpp->min, piece_rpp->max, cutp->bn.bn_min, cutp->bn.bn_max ) ) {
02400                                         if( piece_count < PIECE_BLOCK ) {
02401                                                 pieces[piece_count++] = i;
02402                                         } else if( more_pieces_alloced == 0 ) {
02403                                                 more_pieces_alloced = stp->st_npieces - PIECE_BLOCK;
02404                                                 more_pieces = (long *)bu_malloc( sizeof( long ) * more_pieces_alloced,
02405                                                                                  "more_pieces" );
02406                                                 more_pieces[more_pieces_count++] = i;
02407                                         } else {
02408                                                 more_pieces[more_pieces_count++] = i;
02409                                         }
02410                                 }
02411                         }
02412 
02413                         if( cutp->bn.bn_piecelen >= cutp->bn.bn_maxpiecelen ) {
02414                                 cutp->bn.bn_piecelist = (struct rt_piecelist *)bu_realloc( cutp->bn.bn_piecelist,
02415                                                                       sizeof( struct rt_piecelist ) * (++cutp->bn.bn_maxpiecelen),
02416                                                                       "cutp->bn.bn_piecelist" );
02417                         }
02418 
02419                         if( !piece_count ) {
02420                                 return;
02421                         }
02422 
02423                         plp = &cutp->bn.bn_piecelist[cutp->bn.bn_piecelen++];
02424                         plp->magic = RT_PIECELIST_MAGIC;
02425                         plp->stp = stp;
02426                         plp->npieces = piece_count + more_pieces_count;
02427                         plp->pieces = (long *)bu_malloc( plp->npieces * sizeof( long ), "plp->pieces" );
02428                         for( i=0 ; i<piece_count ; i++ ) {
02429                                 plp->pieces[i] = pieces[i];
02430                         }
02431                         j = piece_count;
02432                         for( i=0 ; i<more_pieces_count ; i++ ) {
02433                                 plp->pieces[j++] = more_pieces[i];
02434                         }
02435 
02436                         if( more_pieces ) {
02437                                 bu_free( (char *)more_pieces, "more_pieces" );
02438                         }
02439                 }
02440                 break;
02441         case CUT_CUTNODE:
02442                 if( stp->st_min[cutp->cn.cn_axis] >= cutp->cn.cn_point ) {
02443                         insert_in_bsp( stp, cutp->cn.cn_r );
02444                 } else if( stp->st_max[cutp->cn.cn_axis] < cutp->cn.cn_point ) {
02445                         insert_in_bsp( stp, cutp->cn.cn_l );
02446                 } else {
02447                         insert_in_bsp( stp, cutp->cn.cn_r );
02448                         insert_in_bsp( stp, cutp->cn.cn_l );
02449                 }
02450                 break;
02451         default:
02452                 bu_log( "insert_in_bsp(): unrecognized cut type (%d) in BSP!!!\n" );
02453                 bu_bomb( "insert_in_bsp(): unrecognized cut type in BSP!!!\n" );
02454         }
02455 
02456 }
02457 
02458 void
02459 fill_out_bsp( struct rt_i *rtip, union cutter *cutp, struct resource *resp, fastf_t bb[6] )
02460 {
02461         fastf_t bb2[6];
02462         int i, j;
02463 
02464         switch( cutp->cut_type ) {
02465         case CUT_BOXNODE:
02466                 j = 3;
02467                 for( i=0 ; i<3 ; i++ ) {
02468                         if( bb[i] >= INFINITY ) {
02469                                 /* this node is at the edge of the model BB, make it fill the BB */
02470                                 cutp->bn.bn_min[i] = rtip->mdl_min[i];
02471                         }
02472                         if( bb[j] <= -INFINITY ) {
02473                                 /* this node is at the edge of the model BB, make it fill the BB */
02474                                 cutp->bn.bn_max[i] = rtip->mdl_max[i];
02475                         }
02476                         j++;
02477                 }
02478                 break;
02479         case CUT_CUTNODE:
02480                 VMOVE( bb2, bb );
02481                 VMOVE( &bb2[3], &bb[3] );
02482                 bb[cutp->cn.cn_axis] = cutp->cn.cn_point;
02483                 fill_out_bsp( rtip, cutp->cn.cn_r, resp, bb );
02484                 bb2[cutp->cn.cn_axis + 3] = cutp->cn.cn_point;
02485                 fill_out_bsp( rtip, cutp->cn.cn_l, resp, bb2 );
02486                 break;
02487         default:
02488                 bu_log( "fill_out_bsp(): unrecognized cut type (%d) in BSP!!!\n" );
02489                 bu_bomb( "fill_out_bsp(): unrecognized cut type in BSP!!!\n" );
02490         }
02491 
02492 }
02493 
02494 /*
02495  * Local Variables:
02496  * mode: C
02497  * tab-width: 8
02498  * c-basic-offset: 4
02499  * indent-tabs-mode: t
02500  * End:
02501  * ex: shiftwidth=4 tabstop=8
02502  */

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