db_tree.c

Go to the documentation of this file.
00001 /*                       D B _ T R E E . C
00002  * BRL-CAD
00003  *
00004  * Copyright (c) 1988-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 dbio */
00023 
00024 /*@{*/
00025 /** @file db_tree.c
00026  *
00027  * Functions -
00028  *      db_walk_tree            Parallel tree walker
00029  *      db_path_to_mat          Given a path, return a matrix.
00030  *      db_region_mat           Given a name, return a matrix
00031  *
00032  *  Author -
00033  *      Michael John Muuss
00034  *
00035  *  Source -
00036  *      SECAD/VLD Computing Consortium, Bldg 394
00037  *      The U. S. Army Ballistic Research Laboratory
00038  *      Aberdeen Proving Ground, Maryland  21005-5066
00039  *
00040  */
00041 
00042 #ifndef lint
00043 static const char RCSid[] = "@(#)$Header: /cvsroot/brlcad/brlcad/src/librt/db_tree.c,v 14.14 2006/09/16 02:04:24 lbutler Exp $ (BRL)";
00044 #endif
00045 
00046 #include "common.h"
00047 
00048 #include <stdio.h>
00049 #include <math.h>
00050 #ifdef HAVE_STRING_H
00051 #  include <string.h>
00052 #else
00053 #  include <strings.h>
00054 #endif
00055 
00056 #include "machine.h"
00057 #include "bu.h"
00058 #include "vmath.h"
00059 #include "bn.h"
00060 #include "nmg.h"
00061 #include "raytrace.h"
00062 
00063 #include "./debug.h"
00064 
00065 
00066 BU_EXTERN(void db_ck_tree, (const union tree *tp));
00067 
00068 /**
00069  *                      D B _ D U P _ D B _ T R E E _ S T A T E
00070  *
00071  *  Duplicate the contents of a db_tree_state structure,
00072  *  including a private copy of the ts_mater field(s) and the attribute/value set.
00073  */
00074 void
00075 db_dup_db_tree_state(struct db_tree_state *otsp, const struct db_tree_state *itsp)
00076 {
00077         int             shader_len=0;
00078         int             i;
00079 
00080         RT_CK_DBTS(itsp);
00081         RT_CK_DBI(itsp->ts_dbip);
00082 
00083         *otsp = *itsp;                  /* struct copy */
00084 
00085         if( itsp->ts_mater.ma_shader )
00086                 shader_len = strlen( itsp->ts_mater.ma_shader );
00087         if( shader_len )
00088         {
00089                 otsp->ts_mater.ma_shader = (char *)bu_malloc( shader_len+1, "db_new_combined_tree_state: ma_shader" );
00090                 strcpy( otsp->ts_mater.ma_shader, itsp->ts_mater.ma_shader );
00091         }
00092         else
00093                 otsp->ts_mater.ma_shader = (char *)NULL;
00094 
00095         if( itsp->ts_attrs.count > 0 ) {
00096                 bu_avs_init( &otsp->ts_attrs, itsp->ts_attrs.count, "otsp->ts_attrs" );
00097                 for( i=0 ; i<itsp->ts_attrs.count ; i++ )
00098                         bu_avs_add( &otsp->ts_attrs, itsp->ts_attrs.avp[i].name,
00099                                     itsp->ts_attrs.avp[i].value );
00100         } else {
00101                 bu_avs_init_empty( &otsp->ts_attrs );
00102         }
00103 }
00104 
00105 /**
00106  *                      D B _ F R E E _ D B _ T R E E _ S T A T E
00107  *
00108  *  Release dynamic fields inside the structure, but not the structure itself.
00109  */
00110 void
00111 db_free_db_tree_state( struct db_tree_state *tsp )
00112 {
00113         RT_CK_DBTS(tsp);
00114         RT_CK_DBI(tsp->ts_dbip);
00115         if( tsp->ts_mater.ma_shader )  {
00116                 bu_free( tsp->ts_mater.ma_shader, "db_free_combined_tree_state: ma_shader" );
00117                 tsp->ts_mater.ma_shader = (char *)NULL;         /* sanity */
00118         }
00119         if( tsp->ts_attrs.max > 0 ) {
00120                 bu_avs_free( &tsp->ts_attrs );
00121                 tsp->ts_attrs.avp = (struct bu_attribute_value_pair *)NULL;
00122         }
00123         tsp->ts_dbip = (struct db_i *)NULL;                     /* sanity */
00124 }
00125 
00126 /**
00127  *                      D B _ I N I T _ D B _ T R E E _ S T A T E
00128  *
00129  *  In most cases, you don't want to use this routine, you want to
00130  *  struct copy mged_initial_tree_state or rt_initial_tree_state,
00131  *  and then set ts_dbip in your copy.
00132  */
00133 void
00134 db_init_db_tree_state( struct db_tree_state *tsp, struct db_i *dbip, struct resource *resp )
00135 {
00136         RT_CK_DBI(dbip);
00137         RT_CK_RESOURCE(resp);
00138 
00139         bzero( (char *)tsp, sizeof(*tsp) );
00140         tsp->magic = RT_DBTS_MAGIC;
00141         tsp->ts_dbip = dbip;
00142         tsp->ts_resp = resp;
00143         bu_avs_init_empty( &tsp->ts_attrs );
00144         MAT_IDN( tsp->ts_mat ); /* XXX should use null pointer convention! */
00145 }
00146 
00147 /**
00148  *                      D B _ N E W _ C O M B I N E D _ T R E E _ S T A T E
00149  */
00150 struct combined_tree_state *
00151 db_new_combined_tree_state(register const struct db_tree_state *tsp, register const struct db_full_path *pathp)
00152 {
00153         struct combined_tree_state      *new;
00154 
00155         RT_CK_DBTS(tsp);
00156         RT_CK_FULL_PATH(pathp);
00157         RT_CK_DBI(tsp->ts_dbip);
00158 
00159         BU_GETSTRUCT( new, combined_tree_state );
00160         new->magic = RT_CTS_MAGIC;
00161         db_dup_db_tree_state( &(new->cts_s), tsp );
00162         db_full_path_init( &(new->cts_p) );
00163         db_dup_full_path( &(new->cts_p), pathp );
00164         return new;
00165 }
00166 
00167 /**
00168  *                      D B _ D U P _ C O M B I N E D _ T R E E _ S T A T E
00169  */
00170 struct combined_tree_state *
00171 db_dup_combined_tree_state(const struct combined_tree_state *old)
00172 {
00173         struct combined_tree_state      *new;
00174 
00175         RT_CK_CTS(old);
00176         BU_GETSTRUCT( new, combined_tree_state );
00177         new->magic = RT_CTS_MAGIC;
00178         db_dup_db_tree_state( &(new->cts_s), &(old->cts_s) );
00179         db_full_path_init( &(new->cts_p) );
00180         db_dup_full_path( &(new->cts_p), &(old->cts_p) );
00181         return new;
00182 }
00183 
00184 /**
00185  *                      D B _ F R E E _ C O M B I N E D _ T R E E _ S T A T E
00186  */
00187 void
00188 db_free_combined_tree_state(register struct combined_tree_state *ctsp)
00189 {
00190         RT_CK_CTS(ctsp);
00191         db_free_full_path( &(ctsp->cts_p) );
00192         db_free_db_tree_state( &(ctsp->cts_s) );
00193         bzero( (char *)ctsp, sizeof(*ctsp) );           /* sanity */
00194         bu_free( (char *)ctsp, "combined_tree_state");
00195 }
00196 
00197 /**
00198  *                      D B _ P R _ T R E E _ S T A T E
00199  */
00200 void
00201 db_pr_tree_state(register const struct db_tree_state *tsp)
00202 {
00203         int i;
00204 
00205         RT_CK_DBTS(tsp);
00206 
00207         bu_log("db_pr_tree_state(x%x):\n", tsp);
00208         bu_log(" ts_dbip=x%x\n", tsp->ts_dbip);
00209         bu_printb(" ts_sofar", tsp->ts_sofar, "\020\3REGION\2INTER\1MINUS" );
00210         bu_log("\n");
00211         bu_log(" ts_regionid=%d\n", tsp->ts_regionid);
00212         bu_log(" ts_aircode=%d\n", tsp->ts_aircode);
00213         bu_log(" ts_gmater=%d\n", tsp->ts_gmater);
00214         bu_log(" ts_los=%d\n", tsp->ts_los);
00215         bu_log(" ts_mater.ma_color=%g,%g,%g\n",
00216                 tsp->ts_mater.ma_color[0],
00217                 tsp->ts_mater.ma_color[1],
00218                 tsp->ts_mater.ma_color[2] );
00219         bu_log(" ts_mater.ma_temperature=%g K\n", tsp->ts_mater.ma_temperature);
00220         bu_log(" ts_mater.ma_shader=%s\n", tsp->ts_mater.ma_shader ? tsp->ts_mater.ma_shader : "" );
00221         for( i=0 ; i<tsp->ts_attrs.count ; i++ ) {
00222                 bu_log( "\t%s = %s\n", tsp->ts_attrs.avp[i].name, tsp->ts_attrs.avp[i].value );
00223         }
00224         bn_mat_print("ts_mat", tsp->ts_mat );
00225         bu_log(" ts_resp=x%x\n", tsp->ts_resp );
00226 }
00227 
00228 /**
00229  *                      D B _ P R _ C O M B I N E D _ T R E E _ S T A T E
00230  */
00231 void
00232 db_pr_combined_tree_state(register const struct combined_tree_state *ctsp)
00233 {
00234         char    *str;
00235 
00236         RT_CK_CTS(ctsp);
00237         bu_log("db_pr_combined_tree_state(x%x):\n", ctsp);
00238         db_pr_tree_state( &(ctsp->cts_s) );
00239         str = db_path_to_string( &(ctsp->cts_p) );
00240         bu_log(" path='%s'\n", str);
00241         bu_free( str, "path string" );
00242 }
00243 
00244 /**
00245  *                      D B _ A P P L Y _ S T A T E _ F R O M _ C O M B
00246  *
00247  *  Handle inheritance of material property found in combination record.
00248  *  Color and the material property have separate inheritance interlocks.
00249  *
00250  *  Returns -
00251  *      -1      failure
00252  *       0      success
00253  *       1      success, this is the top of a new region.
00254  */
00255 int
00256 db_apply_state_from_comb(struct db_tree_state *tsp, const struct db_full_path *pathp, register const struct rt_comb_internal *comb)
00257 {
00258         RT_CK_DBTS(tsp);
00259         RT_CK_COMB(comb);
00260 
00261         if( comb->rgb_valid == 1 )  {
00262                 if( tsp->ts_sofar & TS_SOFAR_REGION )  {
00263                         if( (tsp->ts_sofar&(TS_SOFAR_MINUS|TS_SOFAR_INTER)) == 0 )  {
00264                                 /* This combination is within a region */
00265                                 char    *sofar = db_path_to_string(pathp);
00266 
00267                                 bu_log("db_apply_state_from_comb(): WARNING: color override in combination within region '%s', ignored\n",
00268                                         sofar );
00269                                 bu_free(sofar, "path string");
00270                         }
00271                         /* Just quietly ignore it -- it's being subtracted off */
00272                 } else if( tsp->ts_mater.ma_cinherit == 0 )  {
00273                         /* DB_INH_LOWER was set -- lower nodes in tree override */
00274                         tsp->ts_mater.ma_color_valid = 1;
00275                         tsp->ts_mater.ma_color[0] =
00276                                 (((float)(comb->rgb[0]))*bn_inv255);
00277                         tsp->ts_mater.ma_color[1] =
00278                                 (((float)(comb->rgb[1]))*bn_inv255);
00279                         tsp->ts_mater.ma_color[2] =
00280                                 (((float)(comb->rgb[2]))*bn_inv255);
00281                         /* Track further inheritance as specified by this combination */
00282                         tsp->ts_mater.ma_cinherit = comb->inherit;
00283                 }
00284         }
00285         if( comb->temperature > 0 )  {
00286                 if( tsp->ts_sofar & TS_SOFAR_REGION )  {
00287                         if( (tsp->ts_sofar&(TS_SOFAR_MINUS|TS_SOFAR_INTER)) == 0 )  {
00288                                 /* This combination is within a region */
00289                                 char    *sofar = db_path_to_string(pathp);
00290 
00291                                 bu_log("db_apply_state_from_comb(): WARNING: temperature in combination below region '%s', ignored\n",
00292                                         sofar );
00293                                 bu_free(sofar, "path string");
00294                         }
00295                         /* Just quietly ignore it -- it's being subtracted off */
00296                 } else if( tsp->ts_mater.ma_minherit == 0 )  {
00297                         /* DB_INH_LOWER -- lower nodes in tree override */
00298                         tsp->ts_mater.ma_temperature = comb->temperature;
00299                 }
00300         }
00301         if( bu_vls_strlen( &comb->shader ) > 0 )  {
00302                 if( tsp->ts_sofar & TS_SOFAR_REGION )  {
00303                         if( (tsp->ts_sofar&(TS_SOFAR_MINUS|TS_SOFAR_INTER)) == 0 )  {
00304                                 /* This combination is within a region */
00305                                 char    *sofar = db_path_to_string(pathp);
00306 
00307                                 bu_log("db_apply_state_from_comb(): WARNING: material property spec in combination below region '%s', ignored\n",
00308                                         sofar );
00309                                 bu_free(sofar, "path string");
00310                         }
00311                         /* Just quietly ignore it -- it's being subtracted off */
00312                 } else if( tsp->ts_mater.ma_minherit == 0 )  {
00313                         struct bu_vls tmp_vls;
00314 
00315                         /* DB_INH_LOWER -- lower nodes in tree override */
00316                         if( tsp->ts_mater.ma_shader )
00317                                 bu_free( (genptr_t)tsp->ts_mater.ma_shader, "ma_shader" );
00318 
00319                         bu_vls_init( &tmp_vls );
00320                         if( bu_shader_to_key_eq( bu_vls_addr( &comb->shader ), &tmp_vls ) )
00321                         {
00322                                 char *sofar = db_path_to_string(pathp);
00323 
00324                                 bu_log( "db_apply_state_from_comb: Warning: bad shader in %s (ignored):\n", sofar );
00325                                 bu_vls_free( &tmp_vls );
00326                                 bu_free(sofar, "path string");
00327                                 tsp->ts_mater.ma_shader = (char *)NULL;
00328                         }
00329                         else
00330                         {
00331                                 tsp->ts_mater.ma_shader = bu_vls_strdup( &tmp_vls );
00332                                 bu_vls_free( &tmp_vls );
00333                         }
00334                         tsp->ts_mater.ma_minherit = comb->inherit;
00335                 }
00336         }
00337 
00338         /* Handle combinations which are the top of a "region" */
00339         if( comb->region_flag )  {
00340                 if( tsp->ts_sofar & TS_SOFAR_REGION )  {
00341                         if( (tsp->ts_sofar&(TS_SOFAR_MINUS|TS_SOFAR_INTER)) == 0 )  {
00342                                 char    *sofar = db_path_to_string(pathp);
00343                                 bu_log("Warning:  region unioned into region at '%s', lower region info ignored\n",
00344                                         sofar);
00345                                 bu_free(sofar, "path string");
00346                         }
00347                         /* Go on as if it was not a region */
00348                 } else {
00349                         /* This starts a new region */
00350                         tsp->ts_sofar |= TS_SOFAR_REGION;
00351                         tsp->ts_regionid = comb->region_id;
00352                         tsp->ts_is_fastgen = comb->is_fastgen;
00353                         tsp->ts_aircode = comb->aircode;
00354                         tsp->ts_gmater = comb->GIFTmater;
00355                         tsp->ts_los = comb->los;
00356                         return(1);      /* Success, this starts new region */
00357                 }
00358         }
00359         return(0);      /* Success */
00360 }
00361 
00362 /**
00363  *                      D B _ A P P L Y _ S T A T E _ F R O M _ M E M B
00364  *
00365  *  Updates state via *tsp, pushes member's directory entry on *pathp.
00366  *  (Caller is responsible for popping it).
00367  *
00368  *  Returns -
00369  *      -1      failure
00370  *       0      success, member pushed on path
00371  */
00372 int
00373 db_apply_state_from_memb(struct db_tree_state *tsp, struct db_full_path *pathp, const union tree *tp)
00374 {
00375         register struct directory *mdp;
00376         mat_t                   xmat;
00377         mat_t                   old_xlate;
00378 
00379         RT_CK_DBTS(tsp);
00380         RT_CK_FULL_PATH(pathp);
00381         RT_CK_TREE(tp);
00382 
00383         if( (mdp = db_lookup( tsp->ts_dbip, tp->tr_l.tl_name, LOOKUP_QUIET )) == DIR_NULL )  {
00384                 char    *sofar = db_path_to_string(pathp);
00385                 bu_log("db_lookup(%s) failed in %s\n", tp->tr_l.tl_name, sofar);
00386                 bu_free(sofar, "path string");
00387                 return -1;
00388         }
00389 
00390         db_add_node_to_full_path( pathp, mdp );
00391 
00392         MAT_COPY( old_xlate, tsp->ts_mat );
00393         if( tp->tr_l.tl_mat ) {
00394                 MAT_COPY( xmat, tp->tr_l.tl_mat );
00395         }
00396         else {
00397                 MAT_IDN( xmat );
00398         }
00399 
00400         /*  If the owning region it above this node in the tree,
00401          *  it is not possible to animation region-material properties
00402          *  lower down in the arc.  So note by sending a NULL pointer.
00403          */
00404         db_apply_anims( pathp, mdp, old_xlate, xmat,
00405                 (tsp->ts_sofar & TS_SOFAR_REGION) ?
00406                         (struct mater_info *)NULL :
00407                         &tsp->ts_mater );
00408 
00409         bn_mat_mul(tsp->ts_mat, old_xlate, xmat);
00410 
00411         return(0);              /* Success */
00412 }
00413 
00414 /**
00415  *              D B _ A P P L Y _ S T A T E _ F R O M _ O N E _ M E M B E R
00416  *
00417  *  Returns -
00418  *      -1      found member, failed to apply state
00419  *       0      unable to find member 'cp'
00420  *       1      state applied OK
00421  */
00422 int
00423 db_apply_state_from_one_member(
00424         struct db_tree_state *tsp,
00425         struct db_full_path *pathp,
00426         const char *cp,
00427         int sofar,
00428         const union tree *tp )
00429 {
00430         int     ret;
00431 
00432         RT_CK_DBTS(tsp);
00433         RT_CHECK_DBI( tsp->ts_dbip );
00434         RT_CK_FULL_PATH( pathp );
00435         RT_CK_TREE(tp);
00436 
00437         switch( tp->tr_op )  {
00438 
00439         case OP_DB_LEAF:
00440                 if( strcmp( cp, tp->tr_l.tl_name ) != 0 )
00441                         return 0;               /* NO-OP */
00442                 tsp->ts_sofar |= sofar;
00443                 if( db_apply_state_from_memb( tsp, pathp, tp ) < 0 )
00444                         return -1;              /* FAIL */
00445                 return 1;                       /* success */
00446 
00447         case OP_UNION:
00448         case OP_INTERSECT:
00449         case OP_SUBTRACT:
00450         case OP_XOR:
00451                 ret = db_apply_state_from_one_member( tsp, pathp, cp, sofar,
00452                         tp->tr_b.tb_left );
00453                 if( ret != 0 )  return ret;
00454                 if( tp->tr_op == OP_SUBTRACT )
00455                         sofar |= TS_SOFAR_MINUS;
00456                 else if( tp->tr_op == OP_INTERSECT )
00457                         sofar |= TS_SOFAR_INTER;
00458                 return db_apply_state_from_one_member( tsp, pathp, cp, sofar,
00459                         tp->tr_b.tb_right );
00460 
00461         default:
00462                 bu_log("db_apply_state_from_one_member: bad op %d\n", tp->tr_op);
00463                 bu_bomb("db_apply_state_from_one_member\n");
00464         }
00465         return -1;
00466 }
00467 
00468 /**
00469  *                      D B _ F I N D _ N A M E D _ L E A F
00470  *
00471  *  The search stops on the first match.
00472  *
00473  *  Returns -
00474  *      tp              if found
00475  *      TREE_NULL       if not found in this tree
00476  */
00477 union tree *
00478 db_find_named_leaf( union tree *tp, const char *cp )
00479 {
00480         union tree      *ret;
00481 
00482         RT_CK_TREE(tp);
00483 
00484         switch( tp->tr_op )  {
00485 
00486         case OP_DB_LEAF:
00487                 if( strcmp( cp, tp->tr_l.tl_name )  )
00488                         return TREE_NULL;
00489                 return tp;
00490 
00491         case OP_UNION:
00492         case OP_INTERSECT:
00493         case OP_SUBTRACT:
00494         case OP_XOR:
00495                 ret = db_find_named_leaf( tp->tr_b.tb_left, cp );
00496                 if( ret != TREE_NULL )  return ret;
00497                 return db_find_named_leaf( tp->tr_b.tb_right, cp );
00498 
00499         default:
00500                 bu_log("db_find_named_leaf: bad op %d\n", tp->tr_op);
00501                 bu_bomb("db_find_named_leaf\n");
00502         }
00503         return TREE_NULL;
00504 }
00505 
00506 /**
00507  *                      D B _ F I N D _ N A M E D _ L E A F S _ P A R E N T
00508  *
00509  *  The search stops on the first match.
00510  *
00511  *  Returns -
00512  *      TREE_NULL       if not found in this tree
00513  *      tp              if found
00514  *                      *side == 1 if leaf is on lhs.
00515  *                      *side == 2 if leaf is on rhs.
00516  *
00517  */
00518 union tree *
00519 db_find_named_leafs_parent( int *side, union tree *tp, const char *cp )
00520 {
00521         union tree      *ret;
00522 
00523         RT_CK_TREE(tp);
00524 
00525         switch( tp->tr_op )  {
00526 
00527         case OP_DB_LEAF:
00528                 /* Always return NULL -- we are seeking parent, not leaf */
00529                 return TREE_NULL;
00530 
00531         case OP_UNION:
00532         case OP_INTERSECT:
00533         case OP_SUBTRACT:
00534         case OP_XOR:
00535                 if( tp->tr_b.tb_left->tr_op == OP_DB_LEAF )  {
00536                         if( strcmp( cp, tp->tr_b.tb_left->tr_l.tl_name ) == 0 )  {
00537                                 *side = 1;
00538                                 return tp;
00539                         }
00540                 }
00541                 if( tp->tr_b.tb_right->tr_op == OP_DB_LEAF )  {
00542                         if( strcmp( cp, tp->tr_b.tb_right->tr_l.tl_name ) == 0 )  {
00543                                 *side = 2;
00544                                 return tp;
00545                         }
00546                 }
00547 
00548                 /* If target not on immediate children, descend down. */
00549                 ret = db_find_named_leafs_parent( side, tp->tr_b.tb_left, cp );
00550                 if( ret != TREE_NULL )  return ret;
00551                 return db_find_named_leafs_parent( side, tp->tr_b.tb_right, cp );
00552 
00553         default:
00554                 bu_log("db_find_named_leafs_parent: bad op %d\n", tp->tr_op);
00555                 bu_bomb("db_find_named_leafs_parent\n");
00556         }
00557         return TREE_NULL;
00558 }
00559 
00560 /**
00561  *                      D B _ T R E E _ D E L _ L H S
00562  */
00563 void
00564 db_tree_del_lhs( union tree *tp, struct resource *resp )
00565 {
00566         union tree      *subtree;
00567 
00568         RT_CK_TREE(tp);
00569 
00570         switch( tp->tr_op )  {
00571 
00572         default:
00573                 bu_bomb("db_tree_del_lhs() called with leaf node as parameter\n");
00574 
00575         case OP_UNION:
00576         case OP_INTERSECT:
00577         case OP_SUBTRACT:
00578         case OP_XOR:
00579                 switch( tp->tr_b.tb_left->tr_op )  {
00580                 case OP_NOP:
00581                 case OP_SOLID:
00582                 case OP_REGION:
00583                 case OP_NMG_TESS:
00584                 case OP_DB_LEAF:
00585                         /* lhs is indeed a leaf node */
00586                         db_free_tree( tp->tr_b.tb_left, resp );
00587                         tp->tr_b.tb_left = TREE_NULL;   /* sanity */
00588                         subtree = tp->tr_b.tb_right;
00589                         /*
00590                          *  Since we don't know what node has the downpointer
00591                          *  to 'tp', replicate 'subtree' data in 'tp' node,
00592                          *  then release memory of 'subtree' node
00593                          *  (but not the actual subtree).
00594                          */
00595                         *tp = *subtree;                 /* struct copy */
00596                         RT_FREE_TREE( subtree, resp );
00597                         return;
00598                 default:
00599                         bu_bomb("db_tree_del_lhs()  lhs is not a leaf node\n");
00600                 }
00601         }
00602 }
00603 
00604 /**
00605  *                      D B _ T R E E _ D E L _ R H S
00606  */
00607 void
00608 db_tree_del_rhs( union tree *tp, struct resource *resp )
00609 {
00610         union tree      *subtree;
00611 
00612         RT_CK_TREE(tp);
00613 
00614         switch( tp->tr_op )  {
00615 
00616         default:
00617                 bu_bomb("db_tree_del_rhs() called with leaf node as parameter\n");
00618 
00619         case OP_UNION:
00620         case OP_INTERSECT:
00621         case OP_SUBTRACT:
00622         case OP_XOR:
00623                 switch( tp->tr_b.tb_right->tr_op )  {
00624                 case OP_NOP:
00625                 case OP_SOLID:
00626                 case OP_REGION:
00627                 case OP_NMG_TESS:
00628                 case OP_DB_LEAF:
00629                         /* rhs is indeed a leaf node */
00630                         db_free_tree( tp->tr_b.tb_right, resp );
00631                         tp->tr_b.tb_right = TREE_NULL;  /* sanity */
00632                         subtree = tp->tr_b.tb_left;
00633                         /*
00634                          *  Since we don't know what node has the downpointer
00635                          *  to 'tp', replicate 'subtree' data in 'tp' node,
00636                          *  then release memory of 'subtree' node
00637                          *  (but not the actual subtree).
00638                          */
00639                         *tp = *subtree;                 /* struct copy */
00640                         RT_FREE_TREE( subtree, resp );
00641                         return;
00642                 default:
00643                         bu_bomb("db_tree_del_rhs()  rhs is not a leaf node\n");
00644                 }
00645         }
00646 }
00647 
00648 /**
00649  *                      D B _ T R E E _ D E L _ D B L E A F
00650  *
00651  *  Given a name presumably referenced in a OP_DB_LEAF node,
00652  *  delete that node, and the operation node that references it.
00653  *  Not that this may not produce an equivalant tree,
00654  *  for example when rewriting (A - subtree) as (subtree),
00655  *  but that will be up to the caller/user to adjust.
00656  *  This routine gets rid of exactly two nodes in the tree: leaf, and op.
00657  *  Use some other routine if you wish to kill the entire rhs
00658  *  below "-" and "intersect" nodes.
00659  *
00660  *  The two nodes deleted will have their memory freed.
00661  *
00662  *  If the tree is a single OP_DB_LEAF node, the leaf is freed and
00663  *  *tp is set to NULL.
00664  *
00665  *  Returns -
00666  *      -3      Internal error
00667  *      -2      Tree is empty
00668  *      -1      Unable to find OP_DB_LEAF node specified by 'cp'.
00669  *       0      OK
00670  */
00671 int
00672 db_tree_del_dbleaf(union tree **tp, const char *cp, struct resource *resp)
00673 {
00674         union tree      *parent;
00675         int             side = 0;
00676 
00677         if( *tp == TREE_NULL )  return -1;
00678 
00679         RT_CK_TREE(*tp);
00680         RT_CK_RESOURCE(resp);
00681 
00682         if( (parent = db_find_named_leafs_parent( &side, *tp, cp )) == TREE_NULL )  {
00683                 /* Perhaps the root of the tree is the named leaf? */
00684                 if( (*tp)->tr_op == OP_DB_LEAF &&
00685                     strcmp( cp, (*tp)->tr_l.tl_name ) == 0 )  {
00686                         db_free_tree( *tp, resp );
00687                         *tp = TREE_NULL;
00688                         return 0;
00689                 }
00690                 return -2;
00691         }
00692 
00693         switch( side )  {
00694         case 1:
00695                 db_tree_del_lhs( parent, resp );
00696                 (void)db_tree_del_dbleaf( tp, cp, resp );       /* recurse for extras */
00697                 return 0;
00698         case 2:
00699                 db_tree_del_rhs( parent, resp );
00700                 (void)db_tree_del_dbleaf( tp, cp, resp );       /* recurse for extras */
00701                 return 0;
00702         }
00703         bu_log("db_tree_del_dbleaf() unknown side=%d?\n", side);
00704         return -3;
00705 }
00706 
00707 /**
00708  *                      D B _ T R E E _ M U L _ D B L E A F
00709  *
00710  *  Multiply on the left every matrix found in a DB_LEAF node in a tree.
00711  */
00712 void
00713 db_tree_mul_dbleaf( union tree *tp, const mat_t mat )
00714 {
00715         mat_t   temp;
00716 
00717         RT_CK_TREE(tp);
00718 
00719         switch( tp->tr_op )  {
00720 
00721         case OP_DB_LEAF:
00722                 if( tp->tr_l.tl_mat == NULL )  {
00723                         tp->tr_l.tl_mat = bn_mat_dup(mat);
00724                         return;
00725                 }
00726                 bn_mat_mul( temp, mat, tp->tr_l.tl_mat );
00727                 MAT_COPY( tp->tr_l.tl_mat, temp );
00728                 break;
00729 
00730         case OP_UNION:
00731         case OP_INTERSECT:
00732         case OP_SUBTRACT:
00733         case OP_XOR:
00734                 db_tree_mul_dbleaf( tp->tr_b.tb_left, mat );
00735                 db_tree_mul_dbleaf( tp->tr_b.tb_right, mat );
00736                 break;
00737 
00738         default:
00739                 bu_log("db_tree_mul_dbleaf: bad op %d\n", tp->tr_op);
00740                 bu_bomb("db_tree_mul_dbleaf\n");
00741         }
00742 }
00743 
00744 /**
00745  *                      D B _ T R E E _ F U N C L E A F
00746  *
00747  *      This routine traverses a combination (union tree) in LNR order
00748  *      and calls the provided function for each OP_DB_LEAF node.
00749  *      Note that this routine does not go outside this one
00750  *      combination!!!!
00751  *      was comb_functree().
00752  */
00753 void
00754 db_tree_funcleaf(
00755         struct db_i             *dbip,
00756         struct rt_comb_internal *comb,
00757         union tree              *comb_tree,
00758         void                    (*leaf_func)(),
00759         genptr_t                user_ptr1,
00760         genptr_t                user_ptr2,
00761         genptr_t                user_ptr3 )
00762 {
00763         RT_CK_DBI( dbip );
00764 
00765         if( !comb_tree )
00766                 return;
00767 
00768         RT_CK_TREE( comb_tree );
00769 
00770         switch( comb_tree->tr_op )
00771         {
00772                 case OP_DB_LEAF:
00773                         (*leaf_func)( dbip, comb, comb_tree, user_ptr1, user_ptr2, user_ptr3 );
00774                         break;
00775                 case OP_UNION:
00776                 case OP_INTERSECT:
00777                 case OP_SUBTRACT:
00778                 case OP_XOR:
00779                         db_tree_funcleaf( dbip, comb, comb_tree->tr_b.tb_left, leaf_func, user_ptr1, user_ptr2, user_ptr3 );
00780                         db_tree_funcleaf( dbip, comb, comb_tree->tr_b.tb_right, leaf_func, user_ptr1, user_ptr2, user_ptr3 );
00781                         break;
00782                 default:
00783                         bu_log( "db_tree_funcleaf: bad op %d\n", comb_tree->tr_op );
00784                         bu_bomb( "db_tree_funcleaf: bad op\n" );
00785                         break;
00786         }
00787 }
00788 
00789 /**
00790  *                      D B _ F O L L O W _ P A T H
00791  *
00792  *  Starting with possible prior partial path and corresponding accumulated state,
00793  *  follow the path given by "new_path", updating
00794  *  *tsp and *total_path with full state information along the way.
00795  *  In a better world, there would have been a "combined_tree_state" arg.
00796  *
00797  *  Parameter 'depth' controls how much of 'new_path' is used:
00798  *      0       use all of new_path
00799  *      >0      use only this many of the first elements of the path
00800  *      <0      use all but this many path elements.
00801  *
00802  *  A much more complete version of rt_plookup() and pathHmat().
00803  *  There is also a TCL interface.
00804  *
00805  *  Returns -
00806  *       0      success (plus *tsp is updated)
00807  *      -1      error (*tsp values are not useful)
00808  */
00809 int
00810 db_follow_path(
00811         struct db_tree_state            *tsp,
00812         struct db_full_path             *total_path,
00813         const struct db_full_path       *new_path,
00814         int                             noisy,
00815         int                             depth )         /* # arcs in new_path to use */
00816 {
00817         struct rt_db_internal   intern;
00818         struct rt_comb_internal *comb;
00819         struct directory        *comb_dp;       /* combination's dp */
00820         struct directory        *dp;            /* element's dp */
00821         int                     j;
00822 
00823         RT_CK_DBTS(tsp);
00824         RT_CHECK_DBI( tsp->ts_dbip );
00825         RT_CK_FULL_PATH( total_path );
00826         RT_CK_FULL_PATH( new_path );
00827         RT_CK_RESOURCE( tsp->ts_resp );
00828 
00829         if(RT_G_DEBUG&DEBUG_TREEWALK)  {
00830                 char    *sofar = db_path_to_string(total_path);
00831                 char    *toofar = db_path_to_string(new_path);
00832                 bu_log("db_follow_path() total_path='%s', tsp=x%x, new_path='%s', noisy=%d, depth=%d\n",
00833                         sofar, tsp, toofar, noisy, depth );
00834                 bu_free(sofar, "path string");
00835                 bu_free(toofar, "path string");
00836         }
00837 
00838         if( depth < 0 )  {
00839                 depth = new_path->fp_len-1 + depth;
00840                 if( depth < 0 ) rt_bomb("db_follow_path() depth exceeded provided path\n");
00841         } else if( depth >= new_path->fp_len )  {
00842                 depth = new_path->fp_len-1;
00843         } else if( depth == 0 )  {
00844                 /* depth of zero means "do it all". */
00845                 depth = new_path->fp_len-1;
00846         }
00847 
00848         j = 0;
00849 
00850         /* Get the first combination */
00851         if( total_path->fp_len > 0 )  {
00852                 /* Some path has already been processed */
00853                 comb_dp = DB_FULL_PATH_CUR_DIR(total_path);
00854         }  else  {
00855                 /* No prior path. Process any animations located at the root */
00856                 comb_dp = DIR_NULL;
00857                 dp = new_path->fp_names[0];
00858                 RT_CK_DIR(dp);
00859 
00860                 if( tsp->ts_dbip->dbi_anroot )  {
00861                         register struct animate *anp;
00862                         mat_t   old_xlate, xmat;
00863 
00864                         for( anp=tsp->ts_dbip->dbi_anroot; anp != ANIM_NULL; anp = anp->an_forw ) {
00865                                 RT_CK_ANIMATE(anp);
00866                                 if( dp != anp->an_path.fp_names[0] )
00867                                         continue;
00868                                 MAT_COPY( old_xlate, tsp->ts_mat );
00869                                 MAT_IDN( xmat );
00870                                 db_do_anim( anp, old_xlate, xmat, &(tsp->ts_mater) );
00871                                 bn_mat_mul( tsp->ts_mat, old_xlate, xmat );
00872                         }
00873                 }
00874 
00875                 /* Put first element on output path, either way */
00876                 db_add_node_to_full_path( total_path, dp );
00877 
00878                 if( (dp->d_flags & DIR_COMB) == 0 )  goto is_leaf;
00879 
00880                 /* Advance to next path element */
00881                 j = 1;
00882                 comb_dp = dp;
00883         }
00884         /*
00885          *  Process two things at once:
00886          *  the combination at [j], and it's member at [j+1].
00887          */
00888         do  {
00889                 /* j == depth is the last one, presumably a leaf */
00890                 if( j > depth )  break;
00891                 dp = new_path->fp_names[j];
00892                 RT_CK_DIR(dp);
00893 
00894                 if( (comb_dp->d_flags & DIR_COMB) == 0 )  {
00895                         bu_log("db_follow_path() %s isn't combination\n", comb_dp->d_namep);
00896                         goto fail;
00897                 }
00898 
00899                 /* At this point, comb_db is the comb, dp is the member */
00900                 if(RT_G_DEBUG&DEBUG_TREEWALK)  {
00901                         bu_log("db_follow_path() at %s/%s\n", comb_dp->d_namep, dp->d_namep );
00902                 }
00903 
00904                 /* Load the combination object into memory */
00905                 if( rt_db_get_internal( &intern, comb_dp, tsp->ts_dbip, NULL, tsp->ts_resp ) < 0 )
00906                         goto fail;
00907                 comb = (struct rt_comb_internal *)intern.idb_ptr;
00908                 RT_CK_COMB(comb);
00909                 if( db_apply_state_from_comb( tsp, total_path, comb ) < 0 )
00910                         goto fail;
00911 
00912                 /* Crawl tree searching for specified leaf */
00913                 if( db_apply_state_from_one_member( tsp, total_path, dp->d_namep, 0, comb->tree ) <= 0 )  {
00914                         bu_log("db_follow_path() ERROR: unable to apply member %s state\n", dp->d_namep);
00915                         goto fail;
00916                 }
00917                 /* Found it, state has been applied, sofar applied,
00918                  * member's directory entry pushed onto total_path
00919                  */
00920                 rt_db_free_internal( &intern, tsp->ts_resp );
00921 
00922                 /* If member is a leaf, handle leaf processing too. */
00923                 if( (dp->d_flags & DIR_COMB) == 0 )  {
00924 is_leaf:
00925                         /* Object is a leaf */
00926                         if( j == new_path->fp_len-1 )  {
00927                                 /* No more path was given, all is well */
00928                                 goto out;
00929                         }
00930                         /* Additional path was given, this is wrong */
00931                         if( noisy )  {
00932                                 char    *sofar = db_path_to_string(total_path);
00933                                 char    *toofar = db_path_to_string(new_path);
00934                                 bu_log("db_follow_path() ERROR: path ended in leaf at '%s', additional path specified '%s'\n",
00935                                         sofar, toofar );
00936                                 bu_free(sofar, "path string");
00937                                 bu_free(toofar, "path string");
00938                         }
00939                         goto fail;
00940                 }
00941 
00942                 /* Advance to next path element */
00943                 j++;
00944                 comb_dp = dp;
00945         } while( j <= depth );
00946 
00947 out:
00948         if(RT_G_DEBUG&DEBUG_TREEWALK)  {
00949                 char    *sofar = db_path_to_string(total_path);
00950                 bu_log("db_follow_path() returns total_path='%s'\n",
00951                         sofar);
00952                 bu_free(sofar, "path string");
00953         }
00954         return 0;               /* SUCCESS */
00955 fail:
00956         return -1;              /* FAIL */
00957 }
00958 
00959 /**
00960  *                      D B _ F O L L O W _ P A T H _ F O R _ S T A T E
00961  *
00962  *  Follow the slash-separated path given by "cp", and update
00963  *  *tsp and *total_path with full state information along the way.
00964  *
00965  *  A much more complete version of rt_plookup().
00966  *
00967  *  Returns -
00968  *       0      success (plus *tsp is updated)
00969  *      -1      error (*tsp values are not useful)
00970  */
00971 int
00972 db_follow_path_for_state(struct db_tree_state *tsp, struct db_full_path *total_path, const char *orig_str, int noisy)
00973 {
00974         struct db_full_path     new_path;
00975         int                     ret;
00976 
00977         RT_CK_DBTS(tsp);
00978 
00979         if( *orig_str == '\0' )  return 0;              /* Null string */
00980 
00981         if( db_string_to_path( &new_path, tsp->ts_dbip, orig_str ) < 0 )
00982                 return -1;
00983 
00984         if( new_path.fp_len <= 0 )  return 0;           /* Null string */
00985 
00986         ret = db_follow_path( tsp, total_path, &new_path, noisy, 0 );
00987         db_free_full_path( &new_path );
00988 
00989         return ret;
00990 }
00991 
00992 /**
00993  *   D B _ D E T E C T _ C Y C L E
00994  *
00995  *  Helper routine to detect cyclic references
00996  */
00997 HIDDEN int
00998 db_detect_cycle( struct db_full_path *pathp, union tree *tp )
00999 {
01000     /* skip the last one added since it is currently being tested. */
01001     long int depth = pathp->fp_len - 1;
01002 
01003     /* check the path to see if it is groundhog day */
01004     while (--depth >= 0) {
01005         if (strcmp(tp->tr_l.tl_name, pathp->fp_names[depth]->d_namep) == 0) {
01006             return 1;
01007         }
01008     }
01009 
01010     /* not found */
01011     return 0;
01012 }
01013 
01014 /**
01015  *                      D B _ R E C U R S E _ S U B T R E E
01016  *
01017  *  Helper routine for db_recurse()
01018  */
01019 HIDDEN void
01020 db_recurse_subtree(union tree *tp, struct db_tree_state *msp, struct db_full_path *pathp, struct combined_tree_state **region_start_statepp, genptr_t client_data)
01021 {
01022         struct db_tree_state    memb_state;
01023         union tree              *subtree;
01024 
01025         RT_CK_TREE(tp);
01026         RT_CK_DBTS(msp);
01027         RT_CK_RESOURCE(msp->ts_resp);
01028         db_dup_db_tree_state( &memb_state, msp );
01029 
01030         switch( tp->tr_op )  {
01031 
01032         case OP_DB_LEAF:
01033                 if( db_apply_state_from_memb( &memb_state, pathp, tp ) < 0 )  {
01034                         /* Lookup of this leaf failed, NOP it out. */
01035                         if( tp->tr_l.tl_mat )  {
01036                                 bu_free( (char *)tp->tr_l.tl_mat, "tl_mat" );
01037                                 tp->tr_l.tl_mat = NULL;
01038                         }
01039                         bu_free( tp->tr_l.tl_name, "tl_name" );
01040                         tp->tr_l.tl_name = NULL;
01041                         tp->tr_op = OP_NOP;
01042                         goto out;
01043                 }
01044 
01045                 /* protect against cyclic geometry */
01046                 if ( db_detect_cycle( pathp, tp ) ) {
01047                     int depth = pathp->fp_len;
01048 
01049                     bu_log("Detected cyclic reference of %s\nPath stack is:\n", tp->tr_l.tl_name);
01050                     while (--depth >=0) {
01051                         bu_log("\tPath depth %d is %s\n", depth, pathp->fp_names[depth]->d_namep);
01052                     }
01053                     bu_log("WARNING: skipping the cyclic reference lookup\n");
01054 
01055                     /* Lookup of this leaf resulted in a cyclic reference, NOP it out */
01056                     if( tp->tr_l.tl_mat )  {
01057                         bu_free( (char *)tp->tr_l.tl_mat, "tl_mat" );
01058                         tp->tr_l.tl_mat = NULL;
01059                     }
01060                     bu_free( tp->tr_l.tl_name, "tl_name" );
01061                     tp->tr_l.tl_name = NULL;
01062                     tp->tr_op = OP_NOP;
01063                     goto out;
01064                 }
01065 
01066                 /* Recursive call */
01067                 if( (subtree = db_recurse( &memb_state, pathp, region_start_statepp, client_data )) != TREE_NULL )  {
01068                         union tree      *tmp;
01069 
01070                         /* graft subtree on in place of 'tp' leaf node */
01071                         /* exchange what subtree and tp point at */
01072                         RT_GET_TREE( tmp, msp->ts_resp );
01073                         RT_CK_TREE(subtree);
01074                         *tmp = *tp;     /* struct copy */
01075                         *tp = *subtree; /* struct copy */
01076                         RT_FREE_TREE( subtree, msp->ts_resp );
01077 
01078                         db_free_tree( tmp, msp->ts_resp );
01079                         RT_CK_TREE(tp);
01080                 } else {
01081                         /* Processing of this leaf failed, NOP it out. */
01082                         if( tp->tr_l.tl_mat )  {
01083                                 bu_free( (char *)tp->tr_l.tl_mat, "tl_mat" );
01084                                 tp->tr_l.tl_mat = NULL;
01085                         }
01086                         bu_free( tp->tr_l.tl_name, "tl_name" );
01087                         tp->tr_l.tl_name = NULL;
01088                         tp->tr_op = OP_NOP;
01089                 }
01090                 DB_FULL_PATH_POP(pathp);
01091                 break;
01092 
01093         case OP_UNION:
01094         case OP_INTERSECT:
01095         case OP_SUBTRACT:
01096         case OP_XOR:
01097                 db_recurse_subtree( tp->tr_b.tb_left, &memb_state, pathp, region_start_statepp, client_data );
01098                 if( tp->tr_op == OP_SUBTRACT )
01099                         memb_state.ts_sofar |= TS_SOFAR_MINUS;
01100                 else if( tp->tr_op == OP_INTERSECT )
01101                         memb_state.ts_sofar |= TS_SOFAR_INTER;
01102                 db_recurse_subtree( tp->tr_b.tb_right, &memb_state, pathp, region_start_statepp, client_data );
01103                 break;
01104 
01105         default:
01106                 bu_log("db_recurse_subtree: bad op %d\n", tp->tr_op);
01107                 rt_bomb("db_recurse_subtree\n");
01108         }
01109 out:
01110         db_free_db_tree_state( &memb_state );
01111         RT_CK_TREE(tp);
01112         return;
01113 }
01114 
01115 /**
01116  *                      D B _ R E C U R S E
01117  *
01118  *  Recurse down the tree, finding all the leaves
01119  *  (or finding just all the regions).
01120  *
01121  *  ts_region_start_func() is called to permit regions to be skipped.
01122  *  It is not intended to be used for collecting state.
01123  */
01124 union tree *
01125 db_recurse(struct db_tree_state *tsp, struct db_full_path *pathp, struct combined_tree_state **region_start_statepp, genptr_t client_data)
01126 {
01127         struct directory        *dp;
01128         struct rt_db_internal   intern;
01129         union tree              *curtree = TREE_NULL;
01130         static long int         depth = 0;
01131 
01132         RT_CK_DBTS(tsp);
01133         RT_CHECK_DBI( tsp->ts_dbip );
01134         RT_CK_RESOURCE(tsp->ts_resp);
01135         RT_CK_FULL_PATH(pathp);
01136         RT_INIT_DB_INTERNAL(&intern);
01137 
01138         if( pathp->fp_len <= 0 )  {
01139                 bu_log("db_recurse() null path?\n");
01140                 return(TREE_NULL);
01141         }
01142         dp = DB_FULL_PATH_CUR_DIR(pathp);
01143 
01144         if(RT_G_DEBUG&DEBUG_TREEWALK)  {
01145                 char    *sofar = db_path_to_string(pathp);
01146                 bu_log("db_recurse() pathp='%s', tsp=x%x, *statepp=x%x, tsp->ts_sofar=%d\n",
01147                         sofar, tsp,
01148                         *region_start_statepp, tsp->ts_sofar );
01149                 bu_free(sofar, "path string");
01150                 bn_mat_ck("db_recurse() tsp->ts_mat at start", tsp->ts_mat);
01151         }
01152 
01153         /*
01154          * Load the entire object into contiguous memory.
01155          * Note that this code depends on the d_flags being set properly.
01156          */
01157         if( dp->d_addr == RT_DIR_PHONY_ADDR )  return TREE_NULL;
01158 
01159         if( dp->d_flags & DIR_COMB )  {
01160                 struct rt_comb_internal *comb;
01161                 struct db_tree_state    nts;
01162                 int                     is_region;
01163 
01164                 if( rt_db_get_internal( &intern, dp, tsp->ts_dbip, NULL, tsp->ts_resp ) < 0 )  {
01165                         bu_log("db_recurse() rt_db_get_internal(%s) FAIL\n", dp->d_namep);
01166                         curtree = TREE_NULL;            /* FAIL */
01167                         goto out;
01168                 }
01169 
01170                 /*  Handle inheritance of material property. */
01171                 db_dup_db_tree_state( &nts, tsp );
01172 
01173                 comb = (struct rt_comb_internal *)intern.idb_ptr;
01174                 RT_CK_COMB(comb);
01175                 if( (is_region = db_apply_state_from_comb( &nts, pathp, comb )) < 0 )  {
01176                         db_free_db_tree_state( &nts );
01177                         curtree = TREE_NULL;            /* FAIL */
01178                         goto out;
01179                 }
01180 
01181                 if( is_region > 0 )  {
01182                         struct combined_tree_state      *ctsp;
01183 
01184                         /* get attribute/value structure */
01185                         bu_avs_merge( &nts.ts_attrs, &intern.idb_avs );
01186 
01187                         /*
01188                          *  This is the start of a new region.
01189                          *  If handler rejects this region, skip on.
01190                          *  This might be used for ignoring air regions.
01191                          */
01192                         if( tsp->ts_region_start_func &&
01193                             tsp->ts_region_start_func( &nts, pathp, comb, client_data ) < 0 )  {
01194                                 if(RT_G_DEBUG&DEBUG_TREEWALK)  {
01195                                         char    *sofar = db_path_to_string(pathp);
01196                                         bu_log("db_recurse() ts_region_start_func deletes %s\n",
01197                                                 sofar);
01198                                         bu_free(sofar, "path string");
01199                                 }
01200                                 db_free_db_tree_state( &nts );
01201                                 curtree = TREE_NULL;            /* FAIL */
01202                                 goto out;
01203                         }
01204 
01205                         if( tsp->ts_stop_at_regions )  {
01206                                 goto region_end;
01207                         }
01208 
01209                         /* Take note of full state here at region start */
01210                         if( *region_start_statepp != (struct combined_tree_state *)0 ) {
01211                                 bu_log("db_recurse() ERROR at start of a region, *region_start_statepp = x%x\n",
01212                                         *region_start_statepp );
01213                                 db_free_db_tree_state( &nts );
01214                                 curtree = TREE_NULL;            /* FAIL */
01215                                 goto out;
01216                         }
01217                         ctsp =  db_new_combined_tree_state( &nts, pathp );
01218                         *region_start_statepp = ctsp;
01219                         if(RT_G_DEBUG&DEBUG_TREEWALK)  {
01220                                 bu_log("setting *region_start_statepp to x%x\n", ctsp );
01221                                 db_pr_combined_tree_state(ctsp);
01222                         }
01223                 }
01224 
01225                 if( comb->tree )  {
01226                         /* Steal tree from combination, so it won't be freed */
01227                         curtree = comb->tree;
01228                         comb->tree = TREE_NULL;
01229                         if(curtree) RT_CK_TREE(curtree);
01230 
01231                         /* Release most of internal form before recursing */
01232                         rt_db_free_internal( &intern, tsp->ts_resp );
01233                         comb = NULL;
01234 
01235                         db_recurse_subtree( curtree, &nts, pathp, region_start_statepp, client_data );
01236                         if(curtree) RT_CK_TREE(curtree);
01237                 } else {
01238                         /* No subtrees in this combination, invent a NOP */
01239                         RT_GET_TREE( curtree, tsp->ts_resp );
01240                         curtree->magic = RT_TREE_MAGIC;
01241                         curtree->tr_op = OP_NOP;
01242                         if(curtree) RT_CK_TREE(curtree);
01243                 }
01244 
01245 region_end:
01246                 if( is_region > 0 )  {
01247                         /*
01248                          *  This is the end of processing for a region.
01249                          */
01250                         if( tsp->ts_region_end_func )  {
01251                                 curtree = tsp->ts_region_end_func(
01252                                         &nts, pathp, curtree, client_data );
01253                                 if(curtree) RT_CK_TREE(curtree);
01254                         }
01255                 }
01256                 db_free_db_tree_state( &nts );
01257                 if(curtree) RT_CK_TREE(curtree);
01258         } else if( dp->d_flags & DIR_SOLID )  {
01259 
01260                 if( bn_mat_ck( dp->d_namep, tsp->ts_mat ) < 0 )  {
01261                         bu_log("db_recurse(%s):  matrix does not preserve axis perpendicularity.\n",
01262                                 dp->d_namep );
01263                         bn_mat_print("bad matrix", tsp->ts_mat);
01264                         curtree = TREE_NULL;            /* FAIL */
01265                         goto out;
01266                 }
01267 
01268                 if(RT_G_DEBUG&DEBUG_TREEWALK)
01269                         bu_log("db_recurse() rt_db_get_internal(%s) solid\n", dp->d_namep);
01270 
01271                 RT_INIT_DB_INTERNAL(&intern);
01272                 if( rt_db_get_internal( &intern, dp, tsp->ts_dbip, tsp->ts_mat, tsp->ts_resp ) < 0 )  {
01273                         bu_log("db_recurse() rt_db_get_internal(%s) FAIL\n", dp->d_namep);
01274                         curtree = TREE_NULL;            /* FAIL */
01275                         goto out;
01276                 }
01277 
01278                 if( (tsp->ts_sofar & TS_SOFAR_REGION) == 0 &&
01279                     tsp->ts_stop_at_regions == 0 )  {
01280                         struct combined_tree_state      *ctsp;
01281                         char    *sofar = db_path_to_string(pathp);
01282                         /*
01283                          *  Solid is not contained in a region.
01284                          *  "Invent" region info.
01285                          *  Take note of full state here at "region start".
01286                          */
01287                         if( *region_start_statepp != (struct combined_tree_state *)0 ) {
01288                                 bu_log("db_recurse(%s) ERROR at start of a region (bare solid), *region_start_statepp = x%x\n",
01289                                         sofar, *region_start_statepp );
01290                                 curtree = TREE_NULL;            /* FAIL */
01291                                 goto out;
01292                         }
01293                         if( RT_G_DEBUG & DEBUG_REGIONS )  {
01294                                 bu_log("NOTICE: db_recurse(): solid '%s' not contained in a region, creating a region for it of the same name.\n",
01295                                         sofar );
01296                         }
01297 
01298                         ctsp = db_new_combined_tree_state( tsp, pathp );
01299                         ctsp->cts_s.ts_sofar |= TS_SOFAR_REGION;
01300                         *region_start_statepp = ctsp;
01301                         if(RT_G_DEBUG&DEBUG_TREEWALK)  {
01302                                 bu_log("db_recurse(%s): setting *region_start_statepp to x%x (bare solid)\n",
01303                                         sofar, ctsp );
01304                                 db_pr_combined_tree_state(ctsp);
01305                         }
01306                         bu_free( sofar, "path string" );
01307                 }
01308 
01309                 /* Hand the solid off for leaf processing */
01310                 if( !tsp->ts_leaf_func )  {
01311                         curtree = TREE_NULL;            /* FAIL */
01312                         goto out;
01313                 }
01314                 curtree = tsp->ts_leaf_func( tsp, pathp, &intern, client_data );
01315                 if(curtree) RT_CK_TREE(curtree);
01316         } else {
01317                 bu_log("%s is not a drawable database object\n",
01318                         dp->d_namep );
01319                 curtree = TREE_NULL;
01320                 return(curtree);
01321         }
01322 out:
01323         /* rt_db_get_internal() may not have been called yet, so do not try to free intern unless
01324          * we know there is something to free
01325          */
01326         if( intern.idb_ptr != NULL ) {
01327             rt_db_free_internal( &intern, tsp->ts_resp );
01328         }
01329         if(RT_G_DEBUG&DEBUG_TREEWALK)  {
01330                 char    *sofar = db_path_to_string(pathp);
01331                 bu_log("db_recurse() return curtree=x%x, pathp='%s', *statepp=x%x\n",
01332                         curtree, sofar,
01333                         *region_start_statepp );
01334                 bu_free(sofar, "path string");
01335         }
01336         if(curtree) RT_CK_TREE(curtree);
01337         return(curtree);
01338 }
01339 
01340 /**
01341  *                      D B _ D U P _ S U B T R E E
01342  */
01343 union tree *
01344 db_dup_subtree( const union tree *tp, struct resource *resp )
01345 {
01346         union tree      *new;
01347 
01348         RT_CK_TREE(tp);
01349         RT_CK_RESOURCE(resp);
01350 
01351         RT_GET_TREE( new, resp );
01352         *new = *tp;             /* struct copy */
01353 
01354         switch( tp->tr_op )  {
01355         case OP_NOP:
01356         case OP_SOLID:
01357                 /* If this is a simple leaf, done */
01358                 return(new);
01359 
01360         case OP_DB_LEAF:
01361                 if( tp->tr_l.tl_mat )
01362                         new->tr_l.tl_mat = bn_mat_dup( tp->tr_l.tl_mat );
01363                 new->tr_l.tl_name = bu_strdup( tp->tr_l.tl_name );
01364                 return new;
01365 
01366         case OP_REGION:
01367                 /* If this is a REGION leaf, dup combined_tree_state & path */
01368                 new->tr_c.tc_ctsp = db_dup_combined_tree_state(
01369                         tp->tr_c.tc_ctsp );
01370                 return(new);
01371 
01372         case OP_NOT:
01373         case OP_GUARD:
01374         case OP_XNOP:
01375                 new->tr_b.tb_left = db_dup_subtree( tp->tr_b.tb_left, resp );
01376                 return(new);
01377 
01378         case OP_UNION:
01379         case OP_INTERSECT:
01380         case OP_SUBTRACT:
01381         case OP_XOR:
01382                 /* This node is known to be a binary op */
01383                 new->tr_b.tb_left = db_dup_subtree( tp->tr_b.tb_left, resp );
01384                 new->tr_b.tb_right = db_dup_subtree( tp->tr_b.tb_right, resp );
01385                 return(new);
01386 
01387         default:
01388                 bu_log("db_dup_subtree: bad op %d\n", tp->tr_op);
01389                 rt_bomb("db_dup_subtree\n");
01390         }
01391         return( TREE_NULL );
01392 }
01393 
01394 /**
01395  *                      D B _ C K _ T R E E
01396  */
01397 void
01398 db_ck_tree( const union tree *tp )
01399 {
01400 
01401         RT_CK_TREE(tp);
01402 
01403         switch( tp->tr_op )  {
01404         case OP_NOP:
01405                 break;
01406         case OP_DB_LEAF:
01407                 BU_ASSERT_PTR( tp->tr_l.tl_name, !=, NULL );
01408                 break;
01409         case OP_SOLID:
01410                 if( tp->tr_a.tu_stp )
01411                         RT_CK_SOLTAB( tp->tr_a.tu_stp );
01412                 break;
01413         case OP_REGION:
01414                 RT_CK_CTS( tp->tr_c.tc_ctsp );
01415                 break;
01416 
01417         case OP_NOT:
01418         case OP_GUARD:
01419         case OP_XNOP:
01420                 db_ck_tree( tp->tr_b.tb_left );
01421                 break;
01422 
01423         case OP_UNION:
01424         case OP_INTERSECT:
01425         case OP_SUBTRACT:
01426         case OP_XOR:
01427                 /* This node is known to be a binary op */
01428                 db_ck_tree( tp->tr_b.tb_left );
01429                 db_ck_tree( tp->tr_b.tb_right );
01430                 break;
01431 
01432         default:
01433                 bu_log("db_ck_tree: bad op %d\n", tp->tr_op);
01434                 rt_bomb("db_ck_tree\n");
01435         }
01436 }
01437 
01438 /**
01439  *                      D B _ F R E E _ T R E E
01440  *
01441  *  Release all storage associated with node 'tp', including
01442  *  children nodes.
01443  */
01444 void
01445 db_free_tree( register union tree *tp, struct resource *resp )
01446 {
01447         RT_CK_TREE(tp);
01448         RT_CK_RESOURCE(resp);
01449 
01450         /*
01451          *  Before recursion, smash the magic number, so that if
01452          *  another thread tries to free this same tree, they will fail.
01453          */
01454         tp->magic = -3;         /* special bad flag */
01455 
01456         switch( tp->tr_op )  {
01457         case OP_FREE :
01458             tp->tr_op = 0;              /* sanity */
01459             return;
01460         case OP_NOP:
01461                 break;
01462 
01463         case OP_SOLID:
01464                 if( tp->tr_a.tu_stp )  {
01465                         register struct soltab  *stp = tp->tr_a.tu_stp;
01466                         RT_CK_SOLTAB(stp);
01467                         tp->tr_a.tu_stp = RT_SOLTAB_NULL;
01468                         rt_free_soltab(stp);
01469                 }
01470                 break;
01471         case OP_REGION:
01472                 /* REGION leaf, free combined_tree_state & path */
01473                 db_free_combined_tree_state( tp->tr_c.tc_ctsp );
01474                 tp->tr_c.tc_ctsp = (struct combined_tree_state *)0;
01475                 break;
01476 
01477         case OP_NMG_TESS:
01478                 {
01479                         struct nmgregion *r = tp->tr_d.td_r;
01480                         if( tp->tr_d.td_name )  {
01481                                 bu_free( (char *)tp->tr_d.td_name, "region name" );
01482                                 tp->tr_d.td_name = (const char *)NULL;
01483                         }
01484                         if( r == (struct nmgregion *)NULL )  {
01485                                 break;
01486                         }
01487                         /* Disposing of the nmg model structue is
01488                          * left to someone else.
01489                          * It would be rude to zap all the other regions here.
01490                          */
01491 #if 0
01492                         if( r->l.magic == (-1L) )  {
01493                                 bu_log("db_free_tree: OP_NMG_TESS, r = -1, skipping\n");
01494                         } else if( r->l.magic != NMG_REGION_MAGIC )  {
01495                                 /* It may have been freed, and the memory re-used */
01496                                 bu_log("db_free_tree: OP_NMG_TESS, bad magic x%x (s/b x%x), skipping\n",
01497                                         r->l.magic, NMG_REGION_MAGIC );
01498                         } else {
01499 #endif
01500                         if( r->l.magic == NMG_REGION_MAGIC )
01501                         {
01502                                 NMG_CK_REGION(r);
01503                                 nmg_kr(r);
01504                         }
01505                         tp->tr_d.td_r = (struct nmgregion *)NULL;
01506                 }
01507                 break;
01508 
01509         case OP_DB_LEAF:
01510                 if( tp->tr_l.tl_mat )  {
01511                         bu_free( (char *)tp->tr_l.tl_mat, "tl_mat" );
01512                         tp->tr_l.tl_mat = NULL;
01513                 }
01514                 bu_free( tp->tr_l.tl_name, "tl_name" );
01515                 tp->tr_l.tl_name = NULL;
01516                 break;
01517 
01518         case OP_NOT:
01519         case OP_GUARD:
01520         case OP_XNOP:
01521                 if( tp->tr_b.tb_left->magic == RT_TREE_MAGIC )
01522                         db_free_tree( tp->tr_b.tb_left, resp );
01523                 tp->tr_b.tb_left = TREE_NULL;
01524                 break;
01525 
01526         case OP_UNION:
01527         case OP_INTERSECT:
01528         case OP_SUBTRACT:
01529         case OP_XOR:
01530                 {
01531                         register union tree *fp;
01532 
01533                         /* This node is known to be a binary op */
01534                         fp = tp->tr_b.tb_left;
01535                         tp->tr_b.tb_left = TREE_NULL;
01536                         RT_CK_TREE(fp);
01537                         db_free_tree( fp, resp );
01538 
01539                         fp = tp->tr_b.tb_right;
01540                         tp->tr_b.tb_right = TREE_NULL;
01541                         RT_CK_TREE(fp);
01542                         db_free_tree( fp, resp );
01543                 }
01544                 break;
01545 
01546         default:
01547                 bu_log("db_free_tree: bad op %d\n", tp->tr_op);
01548                 rt_bomb("db_free_tree\n");
01549         }
01550         tp->tr_op = 0;          /* sanity */
01551         RT_FREE_TREE( tp, resp );
01552 }
01553 
01554 /**                     D B _ L E F T _ H V Y _ N O D E
01555  *
01556  *      Re-balance this node to make it left heavy.
01557  *      Unions operators will be moved to left side.
01558  *      when finished "tp" MUST still point to top node
01559  *      od this subtree.
01560  */
01561 void
01562 db_left_hvy_node( union tree *tp )
01563 {
01564         union tree *lhs, *rhs;
01565 
01566         RT_CK_TREE(tp);
01567 
01568         if( tp->tr_op != OP_UNION )
01569                 return;
01570 
01571         while( tp->tr_b.tb_right->tr_op == OP_UNION )
01572         {
01573                 lhs = tp->tr_b.tb_left;
01574                 rhs = tp->tr_b.tb_right;
01575 
01576                 tp->tr_b.tb_left = rhs;
01577                 tp->tr_b.tb_right = rhs->tr_b.tb_right;
01578                 rhs->tr_b.tb_right = rhs->tr_b.tb_left;
01579                 rhs->tr_b.tb_left = lhs;
01580         }
01581 }
01582 
01583 /**
01584  *                      D B _ N O N _ U N I O N _ P U S H
01585  *
01586  *  If there are non-union operations in the tree,
01587  *  above the region nodes, then rewrite the tree so that
01588  *  the entire tree top is nothing but union operations,
01589  *  and any non-union operations are clustered down near the region nodes.
01590  */
01591 void
01592 db_non_union_push( register union tree *tp, struct resource *resp )
01593 {
01594         union tree *A, *B, *C;
01595         union tree *tmp;
01596         int repush_child=0;
01597 
01598         RT_CK_TREE(tp);
01599         RT_CK_RESOURCE(resp);
01600 
01601         switch( tp->tr_op )  {
01602         case OP_REGION:
01603         case OP_SOLID:
01604         case OP_DB_LEAF:
01605                 /* If this is a leaf, done */
01606                 return;
01607 
01608         case OP_NOP:
01609                 /* This tree has nothing in it, done */
01610                 return;
01611 
01612         default:
01613                 db_non_union_push( tp->tr_b.tb_left, resp );
01614                 db_non_union_push( tp->tr_b.tb_right, resp );
01615                 break;
01616         }
01617         if( (tp->tr_op == OP_INTERSECT || tp->tr_op == OP_SUBTRACT) &&
01618             tp->tr_b.tb_left->tr_op == OP_UNION ) {
01619                 union tree      *lhs = tp->tr_b.tb_left;
01620                 union tree      *rhs;
01621 
01622                 A = lhs->tr_b.tb_left;
01623                 B = lhs->tr_b.tb_right;
01624 
01625                 if( A->tr_op == OP_NOP && B->tr_op == OP_NOP ) {
01626                   /* nothing here, eliminate entire subtree */
01627                   db_free_tree( tp->tr_b.tb_left, resp );
01628                   db_free_tree( tp->tr_b.tb_right, resp );
01629                   tp->tr_op = OP_NOP;
01630                   tp->tr_b.tb_left = NULL;
01631                   tp->tr_b.tb_right = NULL;
01632 
01633                 } else if( A->tr_op == OP_NOP ) {
01634                   db_tree_del_lhs( lhs, resp );
01635 
01636                   /* recurse */
01637                   db_non_union_push( tp, resp );
01638                 } else if( B->tr_op == OP_NOP ) {
01639                   db_tree_del_rhs( lhs, resp );
01640 
01641                   /* recurse */
01642                   db_non_union_push( tp, resp );
01643                 } else {
01644 
01645                   repush_child = 1;
01646 
01647                   /*  Rewrite intersect and subtraction nodes, such that
01648                    *  (A u B) - C  becomes (A - C) u (B - C)
01649                    *
01650                    * tp->    -
01651                    *       /   \
01652                    * lhs->  u     C
01653                    *     / \
01654                    *    A   B
01655                    */
01656                   RT_GET_TREE( rhs, resp );
01657 
01658                   /* duplicate top node into rhs */
01659                   *rhs = *tp;           /* struct copy */
01660                   tp->tr_b.tb_right = rhs;
01661                   /* rhs->tr_b.tb_right remains unchanged:
01662                    *
01663                    * tp->    -
01664                    *       /   \
01665                    * lhs->  u     -   <-rhs
01666                    *     / \   / \
01667                    *    A   B ?   C
01668                    */
01669 
01670                   rhs->tr_b.tb_left = lhs->tr_b.tb_right;
01671                   /*
01672                    * tp->    -
01673                    *       /   \
01674                    * lhs->  u     -   <-rhs
01675                    *     / \   / \
01676                    *    A   B B   C
01677                    */
01678 
01679                   /* exchange left and top operators */
01680                   tp->tr_op = lhs->tr_op;
01681                   lhs->tr_op = rhs->tr_op;
01682                   /*
01683                    * tp->    u
01684                    *       /   \
01685                    * lhs->  -     -   <-rhs
01686                    *     / \   / \
01687                    *    A   B B   C
01688                    */
01689 
01690                   /* Make a duplicate of rhs->tr_b.tb_right */
01691                   lhs->tr_b.tb_right = db_dup_subtree( rhs->tr_b.tb_right, resp );
01692                   /*
01693                    * tp->    u
01694                    *       /   \
01695                    * lhs->  -     -   <-rhs
01696                    *     / \   / \
01697                    *    A  C' B   C
01698                    */
01699                 }
01700 
01701         }
01702 
01703         else if( tp->tr_op == OP_INTERSECT &&
01704                 tp->tr_b.tb_right->tr_op == OP_UNION )
01705         {
01706                 /* C + (A u B) -> (C + A) u (C + B) */
01707                 union tree      *rhs = tp->tr_b.tb_right;
01708 
01709                 C = tp->tr_b.tb_left;
01710                 A = tp->tr_b.tb_right->tr_b.tb_left;
01711                 B = tp->tr_b.tb_right->tr_b.tb_right;
01712 
01713                 if( A->tr_op == OP_NOP && B->tr_op == OP_NOP ) {
01714                   /* nothing here, eliminate entire subtree */
01715                   tp->tr_op = OP_NOP;
01716                   db_free_tree( tp->tr_b.tb_left, resp );
01717                   db_free_tree( tp->tr_b.tb_right, resp );
01718                   tp->tr_b.tb_left = NULL;
01719                   tp->tr_b.tb_right = NULL;
01720                 } else if( A->tr_op == OP_NOP ) {
01721                   db_tree_del_lhs( rhs, resp );
01722 
01723                   /* recurse */
01724                   db_non_union_push( tp, resp );
01725                 } else if( B->tr_op == OP_NOP ) {
01726                   db_tree_del_rhs( rhs, resp );
01727 
01728                   /* recurse */
01729                   db_non_union_push( tp, resp );
01730                 } else {
01731                   repush_child = 1;
01732 
01733                   tp->tr_op = OP_UNION;
01734                   RT_GET_TREE( tmp, resp );
01735                   tmp->tr_regionp = tp->tr_regionp;
01736                   tmp->magic = RT_TREE_MAGIC;
01737                   tmp->tr_op = OP_INTERSECT;
01738                   tmp->tr_b.tb_left = C;
01739                   tmp->tr_b.tb_right = A;
01740                   tp->tr_b.tb_left = tmp;
01741                   tp->tr_b.tb_right->tr_op = OP_INTERSECT;
01742                   tp->tr_b.tb_right->tr_b.tb_left = db_dup_subtree( C, resp );
01743                 }
01744         }
01745         else if( tp->tr_op == OP_SUBTRACT &&
01746                 tp->tr_b.tb_right->tr_op == OP_UNION )
01747         {
01748                 /* C - (A u B) -> C - A - B */
01749                 union tree      *rhs = tp->tr_b.tb_right;
01750 
01751 
01752                 C = tp->tr_b.tb_left;
01753                 A = tp->tr_b.tb_right->tr_b.tb_left;
01754                 B = tp->tr_b.tb_right->tr_b.tb_right;
01755 
01756                 if( C->tr_op == OP_NOP ) {
01757                   /* nothing here, eliminate entire subtree */
01758                   tp->tr_op = OP_NOP;
01759                   db_free_tree( tp->tr_b.tb_left, resp );
01760                   db_free_tree( tp->tr_b.tb_right, resp );
01761                   tp->tr_b.tb_left = NULL;
01762                   tp->tr_b.tb_right = NULL;
01763                 } else if( A->tr_op == OP_NOP && B->tr_op == OP_NOP ) {
01764                   db_tree_del_rhs( tp, resp );
01765 
01766                   /* recurse */
01767                   db_non_union_push( tp, resp );
01768                 } else if( A->tr_op == OP_NOP ) {
01769                   db_tree_del_lhs( rhs, resp );
01770 
01771                   /* recurse */
01772                   db_non_union_push( tp, resp );
01773                 } else if( B->tr_op == OP_NOP ) {
01774                   db_tree_del_rhs( rhs, resp );
01775 
01776                   /* recurse */
01777                   db_non_union_push( tp, resp );
01778                 } else {
01779                   repush_child = 1;
01780 
01781                   tp->tr_b.tb_left = tp->tr_b.tb_right;
01782                   tp->tr_b.tb_left->tr_op = OP_SUBTRACT;
01783                   tp->tr_b.tb_right = B;
01784                   tmp = tp->tr_b.tb_left;
01785                   tmp->tr_b.tb_left = C;
01786                   tmp->tr_b.tb_right = A;
01787                 }
01788         }
01789 
01790         /* if this operation has moved a UNION operator towards the leaves
01791          * then the children must be processed again
01792          */
01793         if( repush_child )
01794         {
01795                 db_non_union_push( tp->tr_b.tb_left, resp );
01796                 db_non_union_push( tp->tr_b.tb_right, resp );
01797         }
01798 
01799         /* rebalance this node (moves UNIONs to left side) */
01800         db_left_hvy_node( tp );
01801 }
01802 
01803 /**
01804  *                      D B _ C O U N T _ T R E E _ N O D E S
01805  *
01806  *  Return a count of the number of "union tree" nodes below "tp",
01807  *  including tp.
01808  */
01809 int
01810 db_count_tree_nodes( const union tree *tp, int count )
01811 {
01812         RT_CK_TREE(tp);
01813         switch( tp->tr_op )  {
01814         case OP_NOP:
01815         case OP_SOLID:
01816         case OP_REGION:
01817         case OP_DB_LEAF:
01818                 /* A leaf node */
01819                 return(count+1);
01820 
01821         case OP_UNION:
01822         case OP_INTERSECT:
01823         case OP_SUBTRACT:
01824                 /* This node is known to be a binary op */
01825                 count = db_count_tree_nodes( tp->tr_b.tb_left, count );
01826                 count = db_count_tree_nodes( tp->tr_b.tb_right, count );
01827                 return(count);
01828 
01829         case OP_XOR:
01830         case OP_NOT:
01831         case OP_GUARD:
01832         case OP_XNOP:
01833                 /* This node is known to be a unary op */
01834                 count = db_count_tree_nodes( tp->tr_b.tb_left, count );
01835                 return(count);
01836 
01837         default:
01838                 bu_log("db_count_tree_nodes: bad op %d\n", tp->tr_op);
01839                 rt_bomb("db_count_tree_nodes\n");
01840         }
01841         return( 0 );
01842 }
01843 
01844 /**
01845  *                      D B _ I S _ T R E E _ A L L _ U N I O N S
01846  *
01847  *  Returns -
01848  *      1       if this tree contains nothing but union operations.
01849  *      0       if at least one subtraction or intersection op exists.
01850  */
01851 int
01852 db_is_tree_all_unions( const union tree *tp )
01853 {
01854         RT_CK_TREE(tp);
01855         switch( tp->tr_op )  {
01856         case OP_NOP:
01857         case OP_SOLID:
01858         case OP_REGION:
01859         case OP_DB_LEAF:
01860                 /* A leaf node */
01861                 return 1;               /* yep */
01862 
01863         case OP_UNION:
01864                 if( db_is_tree_all_unions( tp->tr_b.tb_left ) == 0 )
01865                         return 0;
01866                 return db_is_tree_all_unions( tp->tr_b.tb_right );
01867 
01868         case OP_INTERSECT:
01869         case OP_SUBTRACT:
01870                 return 0;               /* nope */
01871 
01872         case OP_XOR:
01873         case OP_NOT:
01874         case OP_GUARD:
01875         case OP_XNOP:
01876                 return 0;               /* nope */
01877 
01878         default:
01879                 bu_log("db_is_tree_all_unions: bad op %d\n", tp->tr_op);
01880                 rt_bomb("db_is_tree_all_unions\n");
01881         }
01882         return 0;
01883 }
01884 
01885 /**
01886  *                      D B _ C O U N T _ S U B T R E E _ R E G I O N S
01887  */
01888 int
01889 db_count_subtree_regions( const union tree *tp )
01890 {
01891         int     cnt;
01892 
01893         RT_CK_TREE(tp);
01894         switch( tp->tr_op )  {
01895         case OP_SOLID:
01896         case OP_REGION:
01897         case OP_DB_LEAF:
01898                 return(1);
01899 
01900         case OP_UNION:
01901                 /* This node is known to be a binary op */
01902                 cnt = db_count_subtree_regions( tp->tr_b.tb_left );
01903                 cnt += db_count_subtree_regions( tp->tr_b.tb_right );
01904                 return(cnt);
01905 
01906         case OP_INTERSECT:
01907         case OP_SUBTRACT:
01908         case OP_XOR:
01909         case OP_NOT:
01910         case OP_GUARD:
01911         case OP_XNOP:
01912         case OP_NOP:
01913                 /* This is as far down as we go -- this is a region top */
01914                 return(1);
01915 
01916         default:
01917                 bu_log("db_count_subtree_regions: bad op %d\n", tp->tr_op);
01918                 rt_bomb("db_count_subtree_regions\n");
01919         }
01920         return( 0 );
01921 }
01922 
01923 /**
01924  *                      D B _ T A L L Y _ S U B T R E E _ R E G I O N S
01925  */
01926 int
01927 db_tally_subtree_regions(
01928         union tree      *tp,
01929         union tree      **reg_trees,
01930         int             cur,
01931         int             lim,
01932         struct resource *resp)
01933 {
01934         union tree      *new;
01935 
01936         RT_CK_TREE(tp);
01937         RT_CK_RESOURCE(resp);
01938         if( cur >= lim )  rt_bomb("db_tally_subtree_regions: array overflow\n");
01939 
01940         switch( tp->tr_op )  {
01941         case OP_NOP:
01942                 return(cur);
01943 
01944         case OP_SOLID:
01945         case OP_REGION:
01946         case OP_DB_LEAF:
01947                 RT_GET_TREE( new, resp );
01948                 *new = *tp;             /* struct copy */
01949                 tp->tr_op = OP_NOP;     /* Zap original */
01950                 reg_trees[cur++] = new;
01951                 return(cur);
01952 
01953         case OP_UNION:
01954                 /* This node is known to be a binary op */
01955                 cur = db_tally_subtree_regions( tp->tr_b.tb_left, reg_trees, cur, lim, resp );
01956                 cur = db_tally_subtree_regions( tp->tr_b.tb_right, reg_trees, cur, lim, resp );
01957                 return(cur);
01958 
01959         case OP_INTERSECT:
01960         case OP_SUBTRACT:
01961         case OP_XOR:
01962         case OP_NOT:
01963         case OP_GUARD:
01964         case OP_XNOP:
01965                 /* This is as far down as we go -- this is a region top */
01966                 RT_GET_TREE( new, resp );
01967                 *new = *tp;             /* struct copy */
01968                 tp->tr_op = OP_NOP;     /* Zap original */
01969                 reg_trees[cur++] = new;
01970                 return(cur);
01971 
01972         default:
01973                 bu_log("db_tally_subtree_regions: bad op %d\n", tp->tr_op);
01974                 rt_bomb("db_tally_subtree_regions\n");
01975         }
01976         return( cur );
01977 }
01978 
01979 /* ============================== */
01980 
01981 HIDDEN union tree *db_gettree_region_end(register struct db_tree_state *tsp, struct db_full_path *pathp, union tree *curtree, genptr_t client_data)
01982 {
01983 
01984         RT_CK_DBTS(tsp);
01985         RT_CK_DBI(tsp->ts_dbip);
01986         RT_CK_FULL_PATH(pathp);
01987         RT_CK_RESOURCE(tsp->ts_resp);
01988 
01989         RT_GET_TREE( curtree, tsp->ts_resp );
01990         curtree->magic = RT_TREE_MAGIC;
01991         curtree->tr_op = OP_REGION;
01992         curtree->tr_c.tc_ctsp = db_new_combined_tree_state( tsp, pathp );
01993 
01994         return(curtree);
01995 }
01996 
01997 HIDDEN union tree *db_gettree_leaf(struct db_tree_state *tsp, struct db_full_path *pathp, struct rt_db_internal *ip, genptr_t client_data)
01998 {
01999         register union tree     *curtree;
02000 
02001         RT_CK_DBTS(tsp);
02002         RT_CK_DBI(tsp->ts_dbip);
02003         RT_CK_FULL_PATH(pathp);
02004         RT_CK_DB_INTERNAL(ip);
02005         RT_CK_RESOURCE(tsp->ts_resp);
02006 
02007         RT_GET_TREE( curtree, tsp->ts_resp );
02008         curtree->magic = RT_TREE_MAGIC;
02009         curtree->tr_op = OP_REGION;
02010         curtree->tr_c.tc_ctsp = db_new_combined_tree_state( tsp, pathp );
02011 
02012         return(curtree);
02013 }
02014 
02015 struct db_walk_parallel_state {
02016         long            magic;
02017         union tree      **reg_trees;
02018         int             reg_count;
02019         int             reg_current;            /* semaphored when parallel */
02020         union tree *    (*reg_end_func)();
02021         union tree *    (*reg_leaf_func)();
02022         struct rt_i     *rtip;
02023         genptr_t        client_data;
02024 };
02025 #define DB_WALK_PARALLEL_STATE_MAGIC    0x64777073      /* dwps */
02026 #define DB_CK_WPS(_p)   BU_CKMAG(_p, DB_WALK_PARALLEL_STATE_MAGIC, "db_walk_parallel_state")
02027 
02028 /**
02029  *                      D B _ W A L K _ S U B T R E E
02030  */
02031 HIDDEN void
02032 db_walk_subtree(
02033         register union tree     *tp,
02034         struct combined_tree_state      **region_start_statepp,
02035         union tree       *(*leaf_func) BU_ARGS((struct db_tree_state *, struct db_full_path *, struct rt_db_internal *, void *)),
02036         genptr_t        client_data,
02037         struct resource *resp )
02038 {
02039         struct combined_tree_state      *ctsp;
02040         union tree      *curtree;
02041 
02042         RT_CK_TREE(tp);
02043         RT_CK_RESOURCE(resp);
02044 
02045         switch( tp->tr_op )  {
02046         case OP_NOP:
02047                 return;
02048 
02049         /*  case OP_SOLID:*/
02050         case OP_REGION:
02051                 /* Flesh out remainder of subtree */
02052                 ctsp = tp->tr_c.tc_ctsp;
02053                 RT_CK_CTS(ctsp);
02054                 if( ctsp->cts_p.fp_len <= 0 )  {
02055                         bu_log("db_walk_subtree() REGION with null path?\n");
02056                         db_free_combined_tree_state( ctsp );
02057                         /* Result is an empty tree */
02058                         tp->tr_op = OP_NOP;
02059                         tp->tr_a.tu_stp = 0;
02060                         return;
02061                 }
02062                 RT_CK_DBI(ctsp->cts_s.ts_dbip);
02063                 ctsp->cts_s.ts_stop_at_regions = 0;
02064                 /* All regions will be accepted, in this 2nd pass */
02065                 ctsp->cts_s.ts_region_start_func = 0;
02066                 /* ts_region_end_func() will be called in db_walk_dispatcher() */
02067                 ctsp->cts_s.ts_region_end_func = 0;
02068                 /* Use user's leaf function */
02069                 ctsp->cts_s.ts_leaf_func = leaf_func;
02070                 ctsp->cts_s.ts_resp = resp;
02071 
02072                 /* If region already seen, force flag */
02073                 if( *region_start_statepp )
02074                         ctsp->cts_s.ts_sofar |= TS_SOFAR_REGION;
02075                 else
02076                         ctsp->cts_s.ts_sofar &= ~TS_SOFAR_REGION;
02077 
02078                 curtree = db_recurse( &ctsp->cts_s, &ctsp->cts_p, region_start_statepp, client_data );
02079                 if( curtree == TREE_NULL )  {
02080                         char    *str;
02081                         str = db_path_to_string( &(ctsp->cts_p) );
02082                         bu_log("db_walk_subtree() FAIL on '%s'\n", str);
02083                         bu_free( str, "path string" );
02084 
02085                         db_free_combined_tree_state( ctsp );
02086                         /* Result is an empty tree */
02087                         tp->tr_op = OP_NOP;
02088                         tp->tr_a.tu_stp = 0;
02089                         return;
02090                 }
02091                 /* replace *tp with new subtree */
02092                 *tp = *curtree;         /* struct copy */
02093                 db_free_combined_tree_state( ctsp );
02094                 RT_FREE_TREE( curtree, resp );
02095                 return;
02096 
02097         case OP_NOT:
02098         case OP_GUARD:
02099         case OP_XNOP:
02100                 db_walk_subtree( tp->tr_b.tb_left, region_start_statepp,
02101                         leaf_func, client_data, resp );
02102                 return;
02103 
02104         case OP_UNION:
02105         case OP_INTERSECT:
02106         case OP_SUBTRACT:
02107         case OP_XOR:
02108                 /* This node is known to be a binary op */
02109                 db_walk_subtree( tp->tr_b.tb_left, region_start_statepp,
02110                         leaf_func, client_data, resp );
02111                 db_walk_subtree( tp->tr_b.tb_right, region_start_statepp,
02112                         leaf_func, client_data, resp );
02113                 return;
02114 
02115         case OP_DB_LEAF:
02116                 rt_pr_tree( tp, 1 );
02117                 bu_bomb("db_walk_subtree() unexpected DB_LEAF\n");
02118 
02119         default:
02120                 bu_log("db_walk_subtree: bad op %d\n", tp->tr_op);
02121                 bu_bomb("db_walk_subtree() bad op\n");
02122         }
02123 }
02124 
02125 /**
02126  *                      D B _ W A L K _ D I S P A T C H E R
02127  *
02128  *  This routine handles the PARALLEL portion of db_walk_tree().
02129  *  There will be at least one, and possibly more, instances of
02130  *  this routine running simultaneously.
02131  *
02132  *  Uses the self-dispatcher pattern:
02133  *  Pick off the next region's tree, and walk it.
02134  */
02135 HIDDEN void
02136 db_walk_dispatcher(int cpu, genptr_t arg)
02137 {
02138         struct combined_tree_state      *region_start_statep;
02139         int             mine;
02140         union tree      *curtree;
02141         struct db_walk_parallel_state   *wps = (struct db_walk_parallel_state *)arg;
02142         struct resource *resp;
02143 
02144         DB_CK_WPS(wps);
02145 
02146         if( wps->rtip == NULL && cpu == 0 )  {
02147                 resp = &rt_uniresource;
02148         } else {
02149                 RT_CK_RTI(wps->rtip);
02150 
02151                 resp = (struct resource *)BU_PTBL_GET( &wps->rtip->rti_resources, cpu );
02152                 if( resp == NULL && cpu == 0 )  resp = &rt_uniresource;
02153         }
02154         RT_CK_RESOURCE(resp);
02155 
02156         while(1)  {
02157                 bu_semaphore_acquire( RT_SEM_WORKER );
02158                 mine = wps->reg_current++;
02159                 bu_semaphore_release( RT_SEM_WORKER );
02160 
02161                 if( mine >= wps->reg_count )
02162                         break;
02163 
02164                 if( RT_G_DEBUG&DEBUG_TREEWALK )
02165                         bu_log("\n\n***** db_walk_dispatcher() on item %d\n\n", mine );
02166 
02167                 if( (curtree = wps->reg_trees[mine]) == TREE_NULL )
02168                         continue;
02169                 RT_CK_TREE(curtree);
02170 
02171                 /* Walk the full subtree now */
02172                 region_start_statep = (struct combined_tree_state *)0;
02173                 db_walk_subtree( curtree, &region_start_statep,
02174                         wps->reg_leaf_func, wps->client_data, resp );
02175 
02176                 /*  curtree->tr_op may be OP_NOP here.
02177                  *  It is up to db_reg_end_func() to deal with this,
02178                  *  either by discarding it, or making a null region.
02179                  */
02180                 RT_CK_TREE(curtree);
02181                 if( !region_start_statep )  {
02182                         bu_log("ERROR: db_walk_dispatcher() region %d started with no state\n", mine);
02183                         if( RT_G_DEBUG&DEBUG_TREEWALK )
02184                                 rt_pr_tree( curtree, 0 );
02185                         continue;
02186                 }
02187                 RT_CK_CTS( region_start_statep );
02188 
02189                 /* This is a new region */
02190                 if( RT_G_DEBUG&DEBUG_TREEWALK )
02191                         db_pr_combined_tree_state(region_start_statep);
02192 
02193                 /*
02194                  *  reg_end_func() returns a pointer to any unused
02195                  *  subtree for freeing.
02196                  */
02197                 if( wps->reg_end_func )  {
02198                         wps->reg_trees[mine] = (*(wps->reg_end_func))(
02199                                 &(region_start_statep->cts_s),
02200                                 &(region_start_statep->cts_p),
02201                                 curtree, wps->client_data );
02202                 }
02203 
02204                 db_free_combined_tree_state( region_start_statep );
02205         }
02206 }
02207 
02208 /**
02209  *                      D B _ W A L K _ T R E E
02210  *
02211  *  This is the top interface to the "tree walker."
02212  *
02213  * Parameters:
02214  *      rtip            rt_i structure to database (open with rt_dirbuild())
02215  *      argc            # of tree-tops named
02216  *      argv            names of tree-tops to process
02217  *      init_state      Input parameter: initial state of the tree.
02218  *                      For example:  rt_initial_tree_state,
02219  *                      and mged_initial_tree_state.
02220  *
02221  * These parameters are pointers to callback routines.
02222  * If NULL, they won't be called.
02223  *
02224  *      reg_start_func  Called at beginning of each region, before visiting
02225  *                      any nodes within the region.
02226  *                      Return 0 if region should be skipped without recursing,
02227  *                       otherwise non-zero.  DO NOT USE FOR OTHER PURPOSES!
02228  *                      For example, can be used to quickly skip air regions.
02229  *
02230  *      reg_end_func    Called after all nodes within a region have been
02231  *                      recursively processed by leaf_func.
02232  *                      If it wants to retain 'curtree' then it may steal
02233  *                      that pointer and return TREE_NULL.
02234  *                      If it wants us to clean up some or all of that
02235  *                      tree, then it returns a non-null (union tree *)
02236  *                      pointer, and that tree is safely freed
02237  *                      in a non-parallel section before we return.
02238  *
02239  *      leaf_func       Function to process a leaf node.
02240  *                      It is actually invoked from db_recurse() from db_walk_subtree().
02241  *                      Returns (union tree *) representing the leaf, or
02242  *                      TREE_NULL if leaf does not exist or has an error.
02243  *
02244  *
02245  *  This routine will employ multiple CPUs if asked,
02246  *  but is not multiply-parallel-recursive.
02247  *  Call this routine with ncpu > 1 from serial code only.
02248  *  When called from within an existing thread, ncpu must be 1.
02249  *
02250  *  If ncpu > 1, the caller is responsible for making sure that
02251  *      rt_g.rtg_parallel is non-zero, and that the
02252  *      bu_semaphore_init() functions has been performed, first.
02253  *
02254  *  Plucks per-cpu resources out of rtip->rti_resources[].
02255  *  They need to have been initialized first.
02256  *
02257  *  Returns -
02258  *      -1      Failure to prepare even a single sub-tree
02259  *       0      OK
02260  */
02261 int
02262 db_walk_tree(struct db_i *dbip,
02263              int argc,
02264              const char **argv,
02265              int ncpu,
02266              const struct db_tree_state *init_state,
02267              int (*reg_start_func) (struct db_tree_state *, struct db_full_path *, const struct rt_comb_internal *, genptr_t),
02268              union tree *(*reg_end_func) (struct db_tree_state *, struct db_full_path *, union tree *, genptr_t),
02269              union tree *(*leaf_func) (struct db_tree_state *, struct db_full_path *, struct rt_db_internal *, genptr_t),
02270              genptr_t client_data)
02271 {
02272         union tree              *whole_tree = TREE_NULL;
02273         int                     new_reg_count;
02274         int                     i;
02275         int                     something_not_found = 0;
02276         union tree              **reg_trees;    /* (*reg_trees)[] */
02277         struct db_walk_parallel_state   wps;
02278         struct resource         *resp;
02279 
02280         RT_CK_DBTS(init_state);
02281         RT_CHECK_DBI(dbip);
02282 
02283         if( init_state->ts_rtip == NULL && ncpu == 1 )  {
02284                 resp = &rt_uniresource;
02285         } else {
02286                 RT_CK_RTI(init_state->ts_rtip);
02287                 resp = (struct resource *)BU_PTBL_GET(&init_state->ts_rtip->rti_resources, 0);
02288                 if( resp == NULL && ncpu == 1 )  {
02289                         resp = &rt_uniresource;
02290                 }
02291         }
02292         RT_CK_RESOURCE(resp);
02293 
02294         /* Walk each of the given path strings */
02295         for( i=0; i < argc; i++ )  {
02296                 register union tree     *curtree;
02297                 struct db_tree_state    ts;
02298                 struct db_full_path     path;
02299                 struct combined_tree_state      *region_start_statep;
02300 
02301                 ts = *init_state;       /* struct copy */
02302                 ts.ts_dbip = dbip;
02303                 ts.ts_resp = resp;
02304                 db_full_path_init( &path );
02305 
02306                 /* First, establish context from given path */
02307                 if( db_follow_path_for_state( &ts, &path, argv[i],
02308                                               LOOKUP_NOISY ) < 0 )
02309                   {
02310                     bu_log ("db_walk_tree: warning - %s not found.\n",
02311                             argv[i]);
02312                     ++ something_not_found;
02313                     continue;   /* ERROR */
02314                   }
02315                 if( path.fp_len <= 0 )  {
02316                         continue;       /* e.g., null combination */
02317                 }
02318 
02319                 /*
02320                  *  Second, walk tree from root to start of all regions.
02321                  *  Build a boolean tree of all regions.
02322                  *  Use user function to accept/reject each region here.
02323                  *  Use internal functions to process regions & leaves.
02324                  */
02325                 ts.ts_stop_at_regions = 1;
02326                 ts.ts_region_start_func = reg_start_func;
02327                 ts.ts_region_end_func = db_gettree_region_end;
02328                 ts.ts_leaf_func = db_gettree_leaf;
02329 
02330                 region_start_statep = (struct combined_tree_state *)0;
02331                 curtree = db_recurse( &ts, &path, &region_start_statep, client_data );
02332                 if( region_start_statep )
02333                         db_free_combined_tree_state( region_start_statep );
02334                 db_free_full_path( &path );
02335                 if( curtree == TREE_NULL )
02336                         continue;       /* ERROR */
02337 
02338                 RT_CK_TREE(curtree);
02339                 if( RT_G_DEBUG&DEBUG_TREEWALK )  {
02340                         bu_log("tree after db_recurse():\n");
02341                         rt_pr_tree( curtree, 0 );
02342                 }
02343 
02344                 if( whole_tree == TREE_NULL )  {
02345                         whole_tree = curtree;
02346                 } else {
02347                         union tree      *new;
02348 
02349                         RT_GET_TREE( new, ts.ts_resp );
02350                         new->magic = RT_TREE_MAGIC;
02351                         new->tr_op = OP_UNION;
02352                         new->tr_b.tb_left = whole_tree;
02353                         new->tr_b.tb_right = curtree;
02354                         whole_tree = new;
02355                 }
02356         }
02357 
02358         if( whole_tree == TREE_NULL )
02359                 return(-1);     /* ERROR, nothing worked */
02360 
02361 
02362         /*
02363          *  Third, push all non-union booleans down.
02364          */
02365         db_non_union_push( whole_tree, resp );
02366         if( RT_G_DEBUG&DEBUG_TREEWALK )  {
02367                 char *str;
02368 
02369                 bu_log("tree after db_non_union_push():\n");
02370                 rt_pr_tree( whole_tree, 0 );
02371                 bu_log( "Same tree in another form:\n" );
02372                 str = (char *)rt_pr_tree_str( whole_tree );
02373                 bu_log( "%s\n", str );
02374                 bu_free( str, "rturn from rt_pr_tree_str" );
02375         }
02376 
02377         /*
02378          *  Build array of sub-tree pointers, one per region,
02379          *  for parallel processing below.
02380          */
02381         new_reg_count = db_count_subtree_regions( whole_tree );
02382         reg_trees = (union tree **)bu_calloc( sizeof(union tree *),
02383                 (new_reg_count+1), "*reg_trees[]" );
02384         new_reg_count = db_tally_subtree_regions( whole_tree, reg_trees, 0,
02385                 new_reg_count, resp );
02386 
02387         /*  Release storage for tree from whole_tree to leaves.
02388          *  db_tally_subtree_regions() duplicated and OP_NOP'ed the original
02389          *  top of any sub-trees that it wanted to keep, so whole_tree
02390          *  is just the left-over part now.
02391          */
02392         db_free_tree( whole_tree, resp );
02393 
02394         /* As a debugging aid, print out the waiting region names */
02395         if( RT_G_DEBUG&DEBUG_TREEWALK )  {
02396                 bu_log("%d waiting regions:\n", new_reg_count);
02397                 for( i=0; i < new_reg_count; i++ )  {
02398                         union tree      *treep;
02399                         struct combined_tree_state      *ctsp;
02400                         char    *str;
02401 
02402                         if( (treep = reg_trees[i]) == TREE_NULL )  {
02403                                 bu_log("%d: NULL\n", i);
02404                                 continue;
02405                         }
02406                         RT_CK_TREE(treep);
02407                         if( treep->tr_op != OP_REGION )  {
02408                                 bu_log("%d: op=%d\n", i, treep->tr_op);
02409                                 rt_pr_tree( treep, 2 );
02410                                 continue;
02411                         }
02412                         ctsp = treep->tr_c.tc_ctsp;
02413                         RT_CK_CTS(ctsp);
02414                         str = db_path_to_string( &(ctsp->cts_p) );
02415                         bu_log("%d '%s'\n", i, str);
02416                         bu_free( str, "path string" );
02417                 }
02418                 bu_log("end of waiting regions\n");
02419         }
02420 
02421         /*
02422          *  Fourth, in parallel, for each region, walk the tree to the leaves.
02423          */
02424         if( bu_is_parallel() && ncpu != 1 )  {
02425                 bu_log("db_walk_tree() recursively invoked while inside parallel section with additional parallelism of ncpu=%d requested.  Running only in one thread.\n",
02426                         ncpu );
02427                 ncpu = 1;
02428         }
02429 
02430         /* Make state available to the threads */
02431         wps.magic = DB_WALK_PARALLEL_STATE_MAGIC;
02432         wps.reg_trees = reg_trees;
02433         wps.reg_count = new_reg_count;
02434         wps.reg_current = 0;                    /* Semaphored */
02435         wps.reg_end_func = reg_end_func;
02436         wps.reg_leaf_func = leaf_func;
02437         wps.client_data = client_data;
02438         wps.rtip = init_state->ts_rtip;
02439 
02440         if( ncpu <= 1 )  {
02441                 db_walk_dispatcher( 0, (genptr_t)&wps );
02442         } else {
02443                 bu_parallel( db_walk_dispatcher, ncpu, (genptr_t)&wps );
02444         }
02445 
02446         /* Clean up any remaining sub-trees still in reg_trees[] */
02447         for( i=0; i < new_reg_count; i++ )  {
02448                 if( reg_trees[i] != TREE_NULL )  {
02449                         db_free_tree( reg_trees[i], resp );
02450                 }
02451         }
02452         bu_free( (char *)reg_trees, "*reg_trees[]" );
02453 
02454         if (something_not_found)
02455           {
02456             bu_log ("db_walk_tree: %d %s not found.\n",
02457                     something_not_found,
02458                     (something_not_found > 1) ? "items" : "item" ) ;
02459             return -2;
02460           }
02461         else
02462           {
02463             return 0;   /* OK */
02464           }
02465 }
02466 
02467 /**
02468  *                      D B _ P A T H _ T O _ M A T
02469  *
02470  *  Returns -
02471  *      1       OK, path matrix written into 'mat'.
02472  *      0       FAIL
02473  *
02474  *  Called in librt/db_tree.c, mged/dodraw.c, and mged/animedit.c
02475  */
02476 int
02477 db_path_to_mat(
02478         struct db_i             *dbip,
02479         struct db_full_path     *pathp,
02480         mat_t                   mat,            /* result */
02481         int                     depth,          /* number of arcs */
02482         struct resource         *resp)
02483 {
02484         struct db_tree_state    ts;
02485         struct db_full_path     null_path;
02486         int                     ret;
02487 
02488         RT_CHECK_DBI(dbip);
02489         RT_CK_FULL_PATH(pathp);
02490         if( !mat ) rt_bomb("db_path_to_mat() NULL matrix pointer\n");
02491 
02492         db_full_path_init( &null_path );
02493         db_init_db_tree_state( &ts, dbip, resp );
02494 
02495         ret = db_follow_path( &ts, &null_path, pathp, LOOKUP_NOISY, depth );
02496         db_free_full_path( &null_path );
02497         MAT_COPY( mat, ts.ts_mat );     /* implicit return */
02498         db_free_db_tree_state( &ts );
02499 
02500         if( ret < 0 )  {
02501                 return 0;       /* FAIL */
02502         }
02503         return 1;               /* OK */
02504 }
02505 
02506 /**
02507  *                      D B _ A P P L Y _ A N I M S
02508  *
02509  *  'arc' may be a null pointer, signifying an identity matrix.
02510  *  'materp' may be a null pointer, signifying that
02511  *  the region has already been finalized above this point in the tree.
02512  */
02513 void
02514 db_apply_anims(struct db_full_path *pathp, struct directory *dp, mat_t stack, mat_t arc, struct mater_info *materp)
02515 {
02516         register struct animate *anp;
02517         register int i,j;
02518 
02519         /* Check here for animation to apply */
02520 
02521         if ((dp->d_animate != ANIM_NULL) && (RT_G_DEBUG & DEBUG_ANIM)) {
02522                 char    *sofar = db_path_to_string(pathp);
02523                 bu_log("Animate %s with...\n", sofar);
02524                 bu_free(sofar, "path string");
02525         }
02526 
02527         /*
02528          * For each of the animations attached to the
02529          * mentioned object,  see if the current accumulated
02530          * path matches the path  specified in the animation.
02531          * Comparison is performed right-to-left (from
02532          * leafward to rootward).
02533          */
02534         for( anp = dp->d_animate; anp != ANIM_NULL; anp = anp->an_forw ) {
02535                 register int anim_flag;
02536 
02537                 j = pathp->fp_len-1;
02538 
02539                 RT_CK_ANIMATE(anp);
02540                 i = anp->an_path.fp_len-1;
02541                 anim_flag = 1;
02542 
02543                 if (RT_G_DEBUG & DEBUG_ANIM) {
02544                         char    *str;
02545 
02546                         str = db_path_to_string( &(anp->an_path) );
02547                         bu_log( "\t%s\t", str );
02548                         bu_free( str, "path string" );
02549                         bu_log("an_path.fp_len-1:%d  pathp->fp_len-1:%d\n",
02550                                 i, j);
02551                 }
02552 
02553                 for( ; i>=0 && j>=0; i--, j-- )  {
02554                         if( anp->an_path.fp_names[i] != pathp->fp_names[j] ) {
02555                                 if (RT_G_DEBUG & DEBUG_ANIM) {
02556                                         bu_log("%s != %s\n",
02557                                              anp->an_path.fp_names[i]->d_namep,
02558                                              pathp->fp_names[j]->d_namep);
02559                                 }
02560                                 anim_flag = 0;
02561                                 break;
02562                         }
02563                 }
02564 
02565                                 /* anim, stack, arc, mater */
02566                 if (anim_flag)
02567                         db_do_anim( anp, stack, arc, materp);
02568         }
02569         return;
02570 }
02571 
02572 /**
02573  *                      D B _ R E G I O N _ M A T
02574  *
02575  *  Given the name of a region, return the matrix which maps model coordinates
02576  *  into "region" coordinates.
02577  *
02578  *  Returns:
02579  *      0       OK
02580  *      <0      Failure
02581  */
02582 int
02583 db_region_mat(
02584         mat_t           m,              /* result */
02585         struct db_i     *dbip,
02586         const char      *name,
02587         struct resource *resp)
02588 {
02589         struct db_full_path             full_path;
02590         mat_t   region_to_model;
02591 
02592         /* get transformation between world and "region" coordinates */
02593         if (db_string_to_path( &full_path, dbip, name) ) {
02594                 /* bad thing */
02595                 bu_log("db_region_mat: db_string_to_path(%s) error\n", name);
02596                 return -1;
02597         }
02598         if(! db_path_to_mat(dbip, &full_path, region_to_model, 0, resp)) {
02599                 /* bad thing */
02600                 bu_log("db_region_mat: db_path_to_mat(%s) error", name);
02601                 return -2;
02602         }
02603 
02604         /* get matrix to map points from model (world) space
02605          * to "region" space
02606          */
02607         bn_mat_inv(m, region_to_model);
02608         db_free_full_path( &full_path );
02609         return 0;
02610 }
02611 
02612 
02613 
02614 /**             D B _ S H A D E R _ M A T
02615  * XXX given that this routine depends on rtip, it should be called
02616  * XXX rt_shader_mat().
02617  *
02618  *  Given a region, return a matrix which maps model coordinates into
02619  *  region "shader space".  This is a space where points in the model
02620  *  within the bounding box of the region are mapped into "region"
02621  *  space (the coordinate system in which the region is defined).
02622  *  The area occupied by the region's bounding box (in region coordinates)
02623  *  are then mapped into the unit cube.  This unit cube defines
02624  *  "shader space".
02625  *
02626  *  Returns:
02627  *      0       OK
02628  *      <0      Failure
02629  */
02630 int
02631 db_shader_mat(
02632         mat_t                   model_to_shader,        /* result */
02633         const struct rt_i       *rtip,
02634         const struct region     *rp,
02635         point_t                 p_min,  /* input/output: shader/region min point */
02636         point_t                 p_max,  /* input/output: shader/region max point */
02637         struct resource         *resp)
02638 {
02639         mat_t   model_to_region;
02640         mat_t   m_xlate;
02641         mat_t   m_scale;
02642         mat_t   m_tmp;
02643         vect_t  v_tmp;
02644         struct  rt_i *my_rtip;
02645         const char      *reg_name;
02646 
02647         RT_CK_RTI(rtip);
02648         RT_CK_RESOURCE(resp);
02649 
02650         reg_name = rt_basename(rp->reg_name);
02651 #ifdef DEBUG_SHADER_MAT
02652         bu_log("db_shader_mat(%s)\n", rp->reg_name);
02653 #endif
02654         /* get model-to-region space mapping */
02655         if( db_region_mat(model_to_region, rtip->rti_dbip, rp->reg_name, resp) < 0 )
02656                 return -1;
02657 
02658 #ifdef DEBUG_SHADER_MAT
02659         bn_mat_print("model_to_region", model_to_region);
02660 #endif
02661         if (VEQUAL(p_min, p_max)) {
02662                 /* User/shader did not specify bounding box,
02663                  * obtain bounding box for un-transformed region
02664                  */
02665 
02666                 /* XXX This should really be handled by a special set of
02667                  * tree walker routines which just build up the RPP of the
02668                  * region.  For now we just re-use rt_rpp_region() with
02669                  * a scratch rtip.
02670                  */
02671                 my_rtip = rt_new_rti(rtip->rti_dbip);
02672                 my_rtip->useair = rtip->useair;
02673 
02674                 /* XXX Should have our own semaphore here */
02675                 bu_semaphore_acquire( RT_SEM_MODEL );
02676                 if (rt_gettree(my_rtip, reg_name)) bu_bomb(rp->reg_name);
02677                 bu_semaphore_release( RT_SEM_MODEL );
02678                 rt_rpp_region(my_rtip, reg_name, p_min, p_max);
02679                 rt_clean(my_rtip);
02680         }
02681 #ifdef DEBUG_SHADER_MAT
02682         bu_log("db_shader_mat(%s) min(%g %g %g) max(%g %g %g)\n", reg_name,
02683                         V3ARGS(p_min), V3ARGS(p_max));
02684 #endif
02685         /*
02686          * Translate bounding box to origin
02687          */
02688         MAT_IDN(m_xlate);
02689         VSCALE(v_tmp, p_min, -1);
02690         MAT_DELTAS_VEC(m_xlate, v_tmp);
02691         bn_mat_mul(m_tmp, m_xlate, model_to_region);
02692 
02693 
02694         /*
02695          * Scale the bounding box to unit cube
02696          */
02697         VSUB2(v_tmp, p_max, p_min);
02698         VINVDIR(v_tmp, v_tmp);
02699         MAT_IDN(m_scale);
02700         MAT_SCALE_VEC(m_scale, v_tmp);
02701         bn_mat_mul(model_to_shader, m_scale, m_tmp);
02702         return 0;
02703 }
02704 
02705 /*@}*/
02706 /*
02707  * Local Variables:
02708  * mode: C
02709  * tab-width: 8
02710  * c-basic-offset: 4
02711  * indent-tabs-mode: t
02712  * End:
02713  * ex: shiftwidth=4 tabstop=8
02714  */

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