db_comb.c

Go to the documentation of this file.
00001 /*                       D B _ C O M B . C
00002  * BRL-CAD
00003  *
00004  * Copyright (c) 1996-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 db4 */
00023 
00024 /*@{*/
00025 /** @file db_comb.c
00026  *  This module contains the import/export routines for "Combinations",
00027  *  the non-leaf nodes in the directed acyclic graphs (DAGs) in the
00028  *  BRL-CAD ".g" database.
00029  *
00030  *  This parallels the function of the geometry (leaf-node) import/export
00031  *  routines found in the g_xxx.c routines.
00032  *
00033  *  As a reminder, some combinations are special, when marked with
00034  *  the "Region" flag, everything from that node down is considered to
00035  *  be made of uniform material.
00036  *
00037  *  Authors -
00038  *      Michael John Muuss
00039  *      John R. Anderson
00040  *
00041  *  Source -
00042  *      The U. S. Army Research Laboratory
00043  *      Aberdeen Proving Ground, Maryland  21005-5068  USA
00044  */
00045 
00046 #ifndef lint
00047 static const char RCSid[] = "@(#)$Header: /cvsroot/brlcad/brlcad/src/librt/db_comb.c,v 14.14 2006/09/16 02:04:24 lbutler Exp $ (ARL)";
00048 #endif
00049 
00050 #include "common.h"
00051 
00052 
00053 
00054 #include <stdio.h>
00055 #include <math.h>
00056 #ifdef HAVE_STRING_H
00057 #include <string.h>
00058 #else
00059 #include <strings.h>
00060 #endif
00061 
00062 #include "machine.h"
00063 #include "bu.h"
00064 #include "vmath.h"
00065 #include "bn.h"
00066 #include "db.h"
00067 #include "raytrace.h"
00068 
00069 #include "./debug.h"
00070 
00071 #define STAT_ROT        1
00072 #define STAT_XLATE      2
00073 #define STAT_PERSP      4
00074 #define STAT_SCALE      8
00075 
00076 /**
00077  *                      M A T _ C A T E G O R I Z E
00078  *
00079  *  Describe with a bit vector the effects this matrix will have.
00080 XXX Should have different name prefix.
00081  */
00082 static int
00083 mat_categorize(const fastf_t *matp)
00084 {
00085         int     status = 0;
00086 
00087         if( !matp )  return 0;
00088 
00089         if( matp[0] != 1.0 || matp[5] != 1.0 || matp[10] != 1.0 )
00090                 status |= STAT_ROT;
00091 
00092         if( matp[MDX] != 0.0 ||
00093             matp[MDY] != 0.0 ||
00094             matp[MDZ] != 0.0 )
00095                 status |= STAT_XLATE;
00096 
00097         if( matp[12] != 0.0 ||
00098             matp[13] != 0.0 ||
00099             matp[14] != 0.0 )
00100                 status |= STAT_PERSP;
00101 
00102         if( matp[15] != 1.0 )  status |= STAT_SCALE;
00103 
00104         return status;
00105 }
00106 
00107 /**
00108  *                      D B _ T R E E _ N L E A V E S
00109  *
00110  *  Return count of number of leaf nodes in this tree.
00111  */
00112 int
00113 db_tree_nleaves( const union tree *tp )
00114 {
00115         if( tp == TREE_NULL )  return 0;
00116 
00117         RT_CK_TREE(tp);
00118 
00119         switch( tp->tr_op )  {
00120         case OP_NOP:
00121                 return 0;
00122         case OP_DB_LEAF:
00123                 return 1;
00124         case OP_SOLID:
00125                 return 1;
00126         case OP_REGION:
00127                 return 1;
00128 
00129         case OP_NOT:
00130         case OP_GUARD:
00131         case OP_XNOP:
00132                 /* Unary ops */
00133                 return db_tree_nleaves( tp->tr_b.tb_left );
00134 
00135         case OP_UNION:
00136         case OP_INTERSECT:
00137         case OP_SUBTRACT:
00138         case OP_XOR:
00139                 /* This node is known to be a binary op */
00140                 return  db_tree_nleaves( tp->tr_b.tb_left ) +
00141                         db_tree_nleaves( tp->tr_b.tb_right );
00142 
00143         default:
00144                 bu_log("db_tree_nleaves: bad op %d\n", tp->tr_op);
00145                 rt_bomb("db_tree_nleaves\n");
00146         }
00147         return( -1 );   /* for the compiler */
00148 }
00149 
00150 /**
00151  *                      D B _ F L A T T E N _ T R E E
00152  *
00153  *  Take a binary tree in "V4-ready" layout (non-unions pushed below unions,
00154  *  left-heavy), and flatten it into an array layout, ready for conversion
00155  *  back to the GIFT-inspired V4 database format.
00156  *
00157  *  This is done using the db_non_union_push() routine.
00158  *
00159  *  If argument 'free' is non-zero, then
00160  *  the non-leaf nodes are freed along the way, to prevent memory leaks.
00161  *  In this case, the caller's copy of 'tp' will be invalid upon return.
00162  *
00163  *  When invoked at the very top of the tree, the op argument must be OP_UNION.
00164  */
00165 struct rt_tree_array *
00166 db_flatten_tree(
00167         struct rt_tree_array    *rt_tree_array,
00168         union tree              *tp,
00169         int                     op,
00170         int                     free,
00171         struct resource         *resp)
00172 {
00173 
00174         RT_CK_TREE(tp);
00175         RT_CK_RESOURCE(resp);
00176 
00177         switch( tp->tr_op )  {
00178         case OP_DB_LEAF:
00179                 rt_tree_array->tl_op = op;
00180                 rt_tree_array->tl_tree = tp;
00181                 return rt_tree_array+1;
00182 
00183         case OP_UNION:
00184         case OP_INTERSECT:
00185         case OP_SUBTRACT:
00186                 /* This node is known to be a binary op */
00187                 rt_tree_array = db_flatten_tree( rt_tree_array, tp->tr_b.tb_left, op, free, resp );
00188                 rt_tree_array = db_flatten_tree( rt_tree_array, tp->tr_b.tb_right, tp->tr_op, free, resp );
00189                 if(free)  {
00190                         /* The leaves have been stolen, free the binary op */
00191                         tp->tr_b.tb_left = TREE_NULL;
00192                         tp->tr_b.tb_right = TREE_NULL;
00193                         RT_FREE_TREE( tp, resp )
00194                 }
00195                 return rt_tree_array;
00196 
00197         default:
00198                 bu_log("db_flatten_tree: bad op %d\n", tp->tr_op);
00199                 bu_bomb("db_flatten_tree\n");
00200         }
00201 
00202         return( (struct rt_tree_array *)NULL ); /* for the compiler */
00203 }
00204 
00205 /**
00206  *                      R T _ C O M B _ I M P O R T 4
00207  *
00208  *  Import a combination record from a V4 database into internal form.
00209  */
00210 int
00211 rt_comb_import4(
00212         struct rt_db_internal           *ip,
00213         const struct bu_external        *ep,
00214         const mat_t                     matrix,         /* NULL if identity */
00215         const struct db_i               *dbip,
00216         struct resource                 *resp)
00217 {
00218         union record            *rp;
00219         struct rt_tree_array    *rt_tree_array;
00220         union tree              *tree;
00221         struct rt_comb_internal *comb;
00222         int                     j;
00223         int                     node_count;
00224 
00225         BU_CK_EXTERNAL( ep );
00226         rp = (union record *)ep->ext_buf;
00227 
00228         if( rp[0].u_id != ID_COMB )
00229         {
00230                 bu_log( "rt_comb_import4: Attempt to import a non-combination\n" );
00231                 return( -1 );
00232         }
00233 
00234         /* Compute how many granules of MEMBER records follow */
00235         node_count = ep->ext_nbytes/sizeof( union record ) - 1;
00236 
00237         if( node_count )
00238                 rt_tree_array = (struct rt_tree_array *)bu_calloc( node_count , sizeof( struct rt_tree_array ) , "rt_tree_array" );
00239         else
00240                 rt_tree_array = (struct rt_tree_array *)NULL;
00241 
00242         for( j=0 ; j<node_count ; j++ )
00243         {
00244                 if( rp[j+1].u_id != ID_MEMB )
00245                 {
00246                         bu_free( (genptr_t)rt_tree_array , "rt_comb_import4: rt_tree_array" );
00247                         bu_log( "rt_comb_import4(): granule in external buffer is not ID_MEMB, id=%d\n", rp[j+1].u_id );
00248                         return( -1 );
00249                 }
00250 
00251                 switch( rp[j+1].M.m_relation )
00252                 {
00253                         case '+':
00254                                 rt_tree_array[j].tl_op = OP_INTERSECT;
00255                                 break;
00256                         case '-':
00257                                 rt_tree_array[j].tl_op = OP_SUBTRACT;
00258                                 break;
00259                         default:
00260                                 bu_log("rt_comb_import4() unknown op=x%x, assuming UNION\n", rp[j+1].M.m_relation );
00261                                 /* Fall through */
00262                         case 'u':
00263                                 rt_tree_array[j].tl_op = OP_UNION;
00264                                 break;
00265                 }
00266                 /* Build leaf node for in-memory tree */
00267                 {
00268                         union tree              *tp;
00269                         mat_t                   diskmat;
00270                         char                    namebuf[NAMESIZE+2];
00271 
00272                         RT_GET_TREE( tp, resp );
00273                         rt_tree_array[j].tl_tree = tp;
00274                         tp->tr_l.magic = RT_TREE_MAGIC;
00275                         tp->tr_l.tl_op = OP_DB_LEAF;
00276                         strncpy( namebuf, rp[j+1].M.m_instname, NAMESIZE );
00277                         namebuf[NAMESIZE] = '\0';       /* ensure null term */
00278                         tp->tr_l.tl_name = bu_strdup( namebuf );
00279 
00280                         rt_mat_dbmat( diskmat, rp[j+1].M.m_mat );
00281 
00282                         /* Verify that rotation part is pure rotation */
00283                         if( fabs(diskmat[0]) > 1 || fabs(diskmat[1]) > 1 ||
00284                             fabs(diskmat[2]) > 1 ||
00285                             fabs(diskmat[4]) > 1 || fabs(diskmat[5]) > 1 ||
00286                             fabs(diskmat[6]) > 1 ||
00287                             fabs(diskmat[8]) > 1 || fabs(diskmat[9]) > 1 ||
00288                             fabs(diskmat[10]) > 1 )  {
00289                                 bu_log("ERROR: %s/%s improper scaling, rotation matrix elements > 1\n",
00290                                         rp[0].c.c_name, namebuf );
00291                         }
00292 
00293                         /* Verify that perspective isn't used as a modeling transform */
00294                         if( diskmat[12] != 0 || diskmat[13] != 0 || diskmat[14] != 0 )  {
00295                                 bu_log("ERROR: %s/%s has perspective transform\n",
00296                                         rp[0].c.c_name, namebuf );
00297                         }
00298 
00299                         /* See if disk record is identity matrix */
00300                         if( bn_mat_is_identity( diskmat ) )  {
00301                                 if( matrix == NULL )  {
00302                                         tp->tr_l.tl_mat = NULL; /* identity */
00303                                 } else {
00304                                         tp->tr_l.tl_mat = bn_mat_dup( matrix );
00305                                 }
00306                         } else {
00307                                 if( matrix == NULL )  {
00308                                         tp->tr_l.tl_mat = bn_mat_dup( diskmat );
00309                                 } else {
00310                                         mat_t   prod;
00311                                         bn_mat_mul( prod, matrix, diskmat );
00312                                         tp->tr_l.tl_mat = bn_mat_dup( prod );
00313                                 }
00314                         }
00315 /* bu_log("M_name=%s, matp=x%x\n", tp->tr_l.tl_name, tp->tr_l.tl_mat ); */
00316                 }
00317         }
00318         if( node_count )
00319                 tree = db_mkgift_tree( rt_tree_array, node_count, &rt_uniresource );
00320         else
00321                 tree = (union tree *)NULL;
00322 
00323         RT_INIT_DB_INTERNAL( ip );
00324         ip->idb_major_type = DB5_MAJORTYPE_BRLCAD;
00325         ip->idb_type = ID_COMBINATION;
00326         ip->idb_meth = &rt_functab[ID_COMBINATION];
00327         comb = (struct rt_comb_internal *)bu_malloc( sizeof( struct rt_comb_internal ) , "rt_comb_import4: rt_comb_internal" );
00328         ip->idb_ptr = (genptr_t)comb;
00329         comb->magic = RT_COMB_MAGIC;
00330         bu_vls_init( &comb->shader );
00331         bu_vls_init( &comb->material );
00332         comb->tree = tree;
00333         comb->temperature = -1;
00334         switch( rp[0].c.c_flags )  {
00335         case DBV4_NON_REGION_NULL:
00336         case DBV4_NON_REGION:
00337                 comb->region_flag = 0;
00338                 break;
00339         case DBV4_REGION:
00340                 comb->region_flag = 1;
00341                 comb->is_fastgen = REGION_NON_FASTGEN;
00342                 break;
00343         case DBV4_REGION_FASTGEN_PLATE:
00344                 comb->region_flag = 1;
00345                 comb->is_fastgen = REGION_FASTGEN_PLATE;
00346                 break;
00347         case DBV4_REGION_FASTGEN_VOLUME:
00348                 comb->region_flag = 1;
00349                 comb->is_fastgen = REGION_FASTGEN_VOLUME;
00350                 break;
00351         default:
00352                 bu_log("WARNING: combination %s has illegal c_flag=x%x\n",
00353                         rp[0].c.c_name, rp[0].c.c_flags );
00354                 break;
00355         }
00356 
00357         if( comb->region_flag )  {
00358                 comb->region_id = rp[0].c.c_regionid;
00359                 comb->aircode = rp[0].c.c_aircode;
00360                 comb->GIFTmater = rp[0].c.c_material;
00361                 comb->los = rp[0].c.c_los;
00362 #if 0
00363                 if( comb->region_id && comb->aircode )
00364                 {
00365                         bu_log( "NOTICE: region %s has both id=%d and aircode=%d, ignoring aircode!!!\n",
00366                                 rp[0].c.c_name, comb->region_id, comb->aircode );
00367                         comb->aircode = 0;
00368                 }
00369 #endif
00370         }
00371         else {  /* set some reasonable defaults */
00372                 comb->region_id = 0;
00373                 comb->aircode = 0;
00374                 comb->GIFTmater = 0;
00375                 comb->los = 0;
00376         }
00377 
00378         comb->rgb_valid = rp[0].c.c_override;
00379         if ( comb->rgb_valid )  {
00380                 comb->rgb[0] = rp[0].c.c_rgb[0];
00381                 comb->rgb[1] = rp[0].c.c_rgb[1];
00382                 comb->rgb[2] = rp[0].c.c_rgb[2];
00383         }
00384         if( rp[0].c.c_matname[0] != '\0' )
00385         {
00386                 char shader_str[94];
00387 
00388                 /* copy shader info to a static string */
00389                 strncpy( shader_str,  rp[0].c.c_matname, 32 );
00390                 shader_str[33] = '\0';
00391                 strcat( shader_str, " " );
00392                 strncat( shader_str, rp[0].c.c_matparm, 60 );
00393                 shader_str[93] = '\0';
00394 
00395                 /* convert to TCL format and place into comb->shader */
00396                 if( bu_shader_to_tcl_list( shader_str, &comb->shader ) )
00397                 {
00398                         bu_log( "rt_comb_import4: Error: Cannot convert following shader to TCL format:\n" );
00399                         bu_log( "\t%s\n", shader_str );
00400                         bu_vls_free( &comb->shader );
00401                         return -1;
00402                 }
00403         }
00404         /* XXX Separate flags for color inherit, shader inherit, (new) material inherit? */
00405         /* XXX cf: ma_cinherit, ma_minherit */
00406         /* This ? is necessary to clean up old databases with grunge here */
00407         comb->inherit = (rp[0].c.c_inherit == DB_INH_HIGHER) ? 1 : 0;
00408         /* Automatic material table lookup here? */
00409         if( comb->region_flag )
00410                 bu_vls_printf( &comb->material, "gift%d", comb->GIFTmater );
00411 
00412         if( rt_tree_array )  bu_free( (genptr_t)rt_tree_array, "rt_tree_array" );
00413 
00414         return( 0 );
00415 }
00416 
00417 /**
00418  *                      R T _ C O M B _ E X P O R T 4
00419  */
00420 int
00421 rt_comb_export4(
00422         struct bu_external              *ep,
00423         const struct rt_db_internal     *ip,
00424         double                          local2mm,
00425         const struct db_i               *dbip,
00426         struct resource                 *resp)
00427 {
00428         struct rt_comb_internal *comb;
00429         int                     node_count;
00430         int                     actual_count;
00431         struct rt_tree_array    *rt_tree_array;
00432         union tree              *tp;
00433         union record            *rp;
00434         int                     j;
00435         char                    *endp;
00436         struct bu_vls           tmp_vls;
00437 
00438         RT_CK_DB_INTERNAL( ip );
00439         RT_CK_RESOURCE(resp);
00440         if( ip->idb_type != ID_COMBINATION ) bu_bomb("rt_comb_export4() type not ID_COMBINATION");
00441         comb = (struct rt_comb_internal *)ip->idb_ptr;
00442         RT_CK_COMB(comb);
00443 
00444         if( comb->tree && db_ck_v4gift_tree( comb->tree ) < 0 )  {
00445                 db_non_union_push( comb->tree, resp );
00446                 if( db_ck_v4gift_tree( comb->tree ) < 0 )  {
00447                         /* Need to further modify tree */
00448                         bu_log("rt_comb_export4() Unable to V4-ify tree, aborting.\n");
00449                         rt_pr_tree( comb->tree, 0 );
00450                         return -1;
00451                 }
00452         }
00453 
00454         /* Count # leaves in tree -- that's how many Member records needed. */
00455         node_count = db_tree_nleaves( comb->tree );
00456         if( node_count > 0 )  {
00457                 rt_tree_array = (struct rt_tree_array *)bu_calloc( node_count , sizeof( struct rt_tree_array ) , "rt_tree_array" );
00458 
00459                 /* Convert tree into array form */
00460                 actual_count = db_flatten_tree( rt_tree_array, comb->tree,
00461                         OP_UNION, 1, resp ) - rt_tree_array;
00462                 BU_ASSERT_LONG( actual_count, ==, node_count );
00463                 comb->tree = TREE_NULL;
00464         } else {
00465                 rt_tree_array = (struct rt_tree_array *)NULL;
00466                 actual_count = 0;
00467         }
00468 
00469         /* Reformat the data into the necessary V4 granules */
00470         BU_INIT_EXTERNAL(ep);
00471         ep->ext_nbytes = sizeof(union record) * ( 1 + node_count );
00472         ep->ext_buf = bu_calloc( 1, ep->ext_nbytes, "v4 comb external" );
00473         rp = (union record *)ep->ext_buf;
00474 
00475         /* Convert the member records */
00476         for( j = 0; j < node_count; j++ )  {
00477                 tp = rt_tree_array[j].tl_tree;
00478                 RT_CK_TREE(tp);
00479                 if( tp->tr_op != OP_DB_LEAF )  bu_bomb("rt_comb_export4() tree not OP_DB_LEAF");
00480 
00481                 rp[j+1].u_id = ID_MEMB;
00482                 switch( rt_tree_array[j].tl_op )  {
00483                 case OP_INTERSECT:
00484                         rp[j+1].M.m_relation = '+';
00485                         break;
00486                 case OP_SUBTRACT:
00487                         rp[j+1].M.m_relation = '-';
00488                         break;
00489                 case OP_UNION:
00490                         rp[j+1].M.m_relation = 'u';
00491                         break;
00492                 default:
00493                         bu_bomb("rt_comb_export4() corrupt rt_tree_array");
00494                 }
00495                 strncpy( rp[j+1].M.m_instname, tp->tr_l.tl_name, NAMESIZE );
00496                 if( tp->tr_l.tl_mat )  {
00497                         rt_dbmat_mat( rp[j+1].M.m_mat, tp->tr_l.tl_mat );
00498                 } else {
00499                         rt_dbmat_mat( rp[j+1].M.m_mat, bn_mat_identity );
00500                 }
00501                 db_free_tree( tp, resp );
00502         }
00503 
00504         /* Build the Combination record, on the front */
00505         rp[0].u_id = ID_COMB;
00506         /* c_name[] filled in by db_wrap_v4_external() */
00507         if( comb->region_flag )  {
00508                 rp[0].c.c_regionid = (short)comb->region_id;
00509                 rp[0].c.c_aircode = (short)comb->aircode;
00510                 rp[0].c.c_material = (short)comb->GIFTmater;
00511                 rp[0].c.c_los = (short)comb->los;
00512                 switch( comb->is_fastgen )  {
00513                 case REGION_FASTGEN_PLATE:
00514                         rp[0].c.c_flags = DBV4_REGION_FASTGEN_PLATE;
00515                         break;
00516                 case REGION_FASTGEN_VOLUME:
00517                         rp[0].c.c_flags = DBV4_REGION_FASTGEN_VOLUME;
00518                         break;
00519                 default:
00520                 case REGION_NON_FASTGEN:
00521                         rp[0].c.c_flags = DBV4_REGION;
00522                         break;
00523                 }
00524         } else {
00525                 rp[0].c.c_flags = DBV4_NON_REGION;
00526         }
00527         if( comb->rgb_valid )  {
00528                 rp[0].c.c_override = 1;
00529                 rp[0].c.c_rgb[0] = comb->rgb[0];
00530                 rp[0].c.c_rgb[1] = comb->rgb[1];
00531                 rp[0].c.c_rgb[2] = comb->rgb[2];
00532         }
00533 
00534         bu_vls_init( &tmp_vls );
00535 
00536         /* convert TCL list format shader to keyword=value format */
00537         if( bu_shader_to_key_eq( bu_vls_addr(&comb->shader), &tmp_vls ) )
00538         {
00539 
00540                 bu_log( "rt_comb_export4: Cannot convert following shader string to keyword=value format:\n" );
00541                 bu_log( "\t%s\n", bu_vls_addr(&comb->shader) );
00542                 rp[0].c.c_matparm[0] = '\0';
00543                 rp[0].c.c_matname[0] = '\0';
00544                 return -1;
00545         }
00546         else
00547         {
00548                 endp = strchr( bu_vls_addr(&tmp_vls), ' ' );
00549                 if( endp )  {
00550                         int     len;
00551                         len = endp - bu_vls_addr(&tmp_vls);
00552                         if( len <= 0 && bu_vls_strlen(&tmp_vls) > 0 )  {
00553                                 bu_log("WARNING: leading spaces on shader '%s' implies NULL shader\n",
00554                                         bu_vls_addr(&tmp_vls) );
00555                         }
00556 
00557                         if( len >= sizeof(rp[0].c.c_matname) )  {
00558                                 bu_log("ERROR:  Shader name '%s' exceeds v4 database field, aborting.\n",
00559                                         bu_vls_addr(&tmp_vls) );
00560                                 return -1;
00561                         }
00562                         if( strlen(endp+1) >= sizeof(rp[0].c.c_matparm) )  {
00563                                 bu_log("ERROR:  Shader parameters '%s' exceed database field, aborting.\nUse \"dbupgrade\" to enable unlimited length strings.\n",
00564                                         endp+1);
00565                                 return -1;
00566                         }
00567                         strncpy( rp[0].c.c_matname, bu_vls_addr(&tmp_vls), len );
00568                         strncpy( rp[0].c.c_matparm, endp+1, sizeof(rp[0].c.c_matparm) );
00569                 } else {
00570                         if( bu_vls_strlen(&tmp_vls) >= sizeof(rp[0].c.c_matname) )  {
00571                                 bu_log("ERROR:  Shader name '%s' exceeds v4 database field, aborting.\n",
00572                                         bu_vls_addr(&tmp_vls) );
00573                                 return -1;
00574                         }
00575                         strncpy( rp[0].c.c_matname, bu_vls_addr(&tmp_vls), sizeof(rp[0].c.c_matname) );
00576                         rp[0].c.c_matparm[0] = '\0';
00577                 }
00578         }
00579         bu_vls_free( &tmp_vls );
00580 
00581         rp[0].c.c_inherit = comb->inherit;
00582 
00583         if( rt_tree_array )  bu_free( (char *)rt_tree_array, "rt_tree_array" );
00584 
00585         return 0;               /* OK */
00586 }
00587 
00588 /**
00589  *                      D B _ T R E E _ F L A T T E N _ D E S C R I B E
00590  *
00591  *  Produce a GIFT-compatible listing, one "member" per line,
00592  *  regardless of the structure of the tree we've been given.
00593  */
00594 void
00595 db_tree_flatten_describe(
00596         struct bu_vls           *vls,
00597         const union tree        *tp,
00598         int                     indented,
00599         int                     lvl,
00600         double                  mm2local,
00601         struct resource         *resp)
00602 {
00603         int node_count;
00604         struct rt_tree_array    *rt_tree_array;
00605         int i;
00606         char op = OP_NOP;
00607         int status;
00608         union tree *ntp;
00609 
00610         BU_CK_VLS(vls);
00611         RT_CK_RESOURCE(resp);
00612 
00613         if( !tp )
00614         {
00615                 /* no tree, probably an empty combination */
00616                 bu_vls_strcat( vls, "-empty-\n" );
00617                 return;
00618         }
00619         RT_CK_TREE(tp);
00620 
00621         node_count = db_tree_nleaves( tp );
00622         if( node_count <= 0 )  {
00623                 if( !indented )  bu_vls_spaces( vls, 2*lvl );
00624                 bu_vls_strcat( vls, "-empty-\n" );
00625                 return;
00626         }
00627 
00628         /*
00629          *  We're going to whack the heck out of the tree, but our
00630          *  argument is 'const'.  Before getting started, make a
00631          *  private copy just for us.
00632          */
00633         ntp = db_dup_subtree( tp, resp );
00634         RT_CK_TREE(ntp);
00635 
00636         /* Convert to "v4 / GIFT style", so that the flatten makes sense. */
00637         if( db_ck_v4gift_tree( ntp ) < 0 )
00638                 db_non_union_push( ntp, resp );
00639         RT_CK_TREE(ntp);
00640 
00641         node_count = db_tree_nleaves( ntp );
00642         rt_tree_array = (struct rt_tree_array *)bu_calloc( node_count , sizeof( struct rt_tree_array ) , "rt_tree_array" );
00643 
00644         /*
00645          * free=0 means that the tree won't have any leaf nodes freed.
00646          */
00647         (void)db_flatten_tree( rt_tree_array, ntp, OP_UNION, 0, resp );
00648 
00649         for( i=0 ; i<node_count ; i++ )
00650         {
00651                 union tree      *itp = rt_tree_array[i].tl_tree;
00652 
00653                 RT_CK_TREE(itp);
00654                 BU_ASSERT_LONG( itp->tr_op, ==, OP_DB_LEAF );
00655                 BU_ASSERT_PTR( itp->tr_l.tl_name, !=, NULL );
00656 
00657                 switch (rt_tree_array[i].tl_op)
00658                 {
00659                         case OP_INTERSECT:
00660                                 op = '+';
00661                                 break;
00662                         case OP_SUBTRACT:
00663                                 op = '-';
00664                                 break;
00665                         case OP_UNION:
00666                                 op = 'u';
00667                                 break;
00668                         default:
00669                                 bu_bomb("db_tree_flatten_describe() corrupt rt_tree_array");
00670                 }
00671 
00672                 status = mat_categorize( itp->tr_l.tl_mat );
00673                 if( !indented )  bu_vls_spaces( vls, 2*lvl );
00674                 bu_vls_printf( vls, " %c %s", op, itp->tr_l.tl_name );
00675                 if( status & STAT_ROT ) {
00676                         fastf_t az, el;
00677                         bn_ae_vec( &az, &el, itp->tr_l.tl_mat ?
00678                                 itp->tr_l.tl_mat : bn_mat_identity );
00679                         bu_vls_printf( vls,
00680                                 " az=%g, el=%g, ",
00681                                 az, el );
00682                 }
00683                 if( status & STAT_XLATE ) {
00684                         bu_vls_printf( vls, " [%g,%g,%g]",
00685                                 itp->tr_l.tl_mat[MDX]*mm2local,
00686                                 itp->tr_l.tl_mat[MDY]*mm2local,
00687                                 itp->tr_l.tl_mat[MDZ]*mm2local);
00688                 }
00689                 if( status & STAT_SCALE ) {
00690                         bu_vls_printf( vls, " scale %g",
00691                                 1.0/itp->tr_l.tl_mat[15] );
00692                 }
00693                 if( status & STAT_PERSP ) {
00694                         bu_vls_printf( vls,
00695                                 " Perspective=[%g,%g,%g]??",
00696                                 itp->tr_l.tl_mat[12],
00697                                 itp->tr_l.tl_mat[13],
00698                                 itp->tr_l.tl_mat[14] );
00699                 }
00700                 bu_vls_printf( vls, "\n" );
00701         }
00702 
00703         if( rt_tree_array ) bu_free( (genptr_t)rt_tree_array, "rt_tree_array" );
00704         db_free_tree( ntp, resp );
00705 }
00706 
00707 /**
00708  *                      D B _ T R E E _ D E S C R I B E
00709  */
00710 void
00711 db_tree_describe(
00712         struct bu_vls           *vls,
00713         const union tree        *tp,
00714         int                     indented,
00715         int                     lvl,
00716         double                  mm2local)
00717 {
00718         int     status;
00719 
00720         BU_CK_VLS(vls);
00721 
00722         if( !tp )
00723         {
00724                 /* no tree, probably an empty combination */
00725                 bu_vls_strcat( vls, "-empty-\n" );
00726                 return;
00727         }
00728         RT_CK_TREE(tp);
00729         switch( tp->tr_op )  {
00730 
00731         case OP_DB_LEAF:
00732                 status = mat_categorize( tp->tr_l.tl_mat );
00733 
00734                 /* One per line, out onto the vls */
00735                 if( !indented )  bu_vls_spaces( vls, 2*lvl );
00736                 bu_vls_strcat( vls, tp->tr_l.tl_name );
00737                 if( status & STAT_ROT ) {
00738                         fastf_t az, el;
00739                         bn_ae_vec( &az, &el, tp->tr_l.tl_mat ?
00740                                 tp->tr_l.tl_mat : bn_mat_identity );
00741                         bu_vls_printf( vls,
00742                                 " az=%g, el=%g, ",
00743                                 az, el );
00744                 }
00745                 if( status & STAT_XLATE ) {
00746                         bu_vls_printf( vls, " [%g,%g,%g]",
00747                                 tp->tr_l.tl_mat[MDX]*mm2local,
00748                                 tp->tr_l.tl_mat[MDY]*mm2local,
00749                                 tp->tr_l.tl_mat[MDZ]*mm2local);
00750                 }
00751                 if( status & STAT_SCALE ) {
00752                         bu_vls_printf( vls, " scale %g",
00753                                 1.0/tp->tr_l.tl_mat[15] );
00754                 }
00755                 if( status & STAT_PERSP ) {
00756                         bu_vls_printf( vls,
00757                                 " Perspective=[%g,%g,%g]??",
00758                                 tp->tr_l.tl_mat[12],
00759                                 tp->tr_l.tl_mat[13],
00760                                 tp->tr_l.tl_mat[14] );
00761                 }
00762                 bu_vls_printf( vls, "\n" );
00763                 return;
00764 
00765                 /* This node is known to be a binary op */
00766         case OP_UNION:
00767                 if(!indented) bu_vls_spaces( vls, 2*lvl );
00768                 bu_vls_strcat( vls, "u " );
00769                 goto bin;
00770         case OP_INTERSECT:
00771                 if(!indented) bu_vls_spaces( vls, 2*lvl );
00772                 bu_vls_strcat( vls, "+ " );
00773                 goto bin;
00774         case OP_SUBTRACT:
00775                 if(!indented) bu_vls_spaces( vls, 2*lvl );
00776                 bu_vls_strcat( vls, "- " );
00777                 goto bin;
00778         case OP_XOR:
00779                 if(!indented) bu_vls_spaces( vls, 2*lvl );
00780                 bu_vls_strcat( vls, "^ " );
00781 bin:
00782                 db_tree_describe( vls, tp->tr_b.tb_left, 1, lvl+1, mm2local );
00783                 db_tree_describe( vls, tp->tr_b.tb_right, 0, lvl+1, mm2local );
00784                 return;
00785 
00786                 /* This node is known to be a unary op */
00787         case OP_NOT:
00788                 if(!indented) bu_vls_spaces( vls, 2*lvl );
00789                 bu_vls_strcat( vls, "! " );
00790                 goto unary;
00791         case OP_GUARD:
00792                 if(!indented) bu_vls_spaces( vls, 2*lvl );
00793                 bu_vls_strcat( vls, "G " );
00794                 goto unary;
00795         case OP_XNOP:
00796                 if(!indented) bu_vls_spaces( vls, 2*lvl );
00797                 bu_vls_strcat( vls, "X " );
00798 unary:
00799                 db_tree_describe( vls, tp->tr_b.tb_left, 1, lvl+1, mm2local );
00800                 return;
00801 
00802         case OP_NOP:
00803                 if(!indented) bu_vls_spaces( vls, 2*lvl );
00804                 bu_vls_strcat( vls, "NOP\n" );
00805                 return;
00806 
00807         default:
00808                 bu_log("db_tree_describe: bad op %d\n", tp->tr_op);
00809                 bu_bomb("db_tree_describe\n");
00810         }
00811 }
00812 
00813 /**
00814  *                      D B _ C O M B _ D E S C R I B E
00815  */
00816 void
00817 db_comb_describe(
00818         struct bu_vls   *str,
00819         const struct rt_comb_internal   *comb,
00820         int             verbose,
00821         double          mm2local,
00822         struct resource *resp)
00823 {
00824         RT_CK_COMB(comb);
00825         RT_CK_RESOURCE(resp);
00826 
00827         if( comb->region_flag ) {
00828                 bu_vls_printf( str,
00829                        "REGION id=%d  (air=%d, los=%d, GIFTmater=%d) ",
00830                         comb->region_id,
00831                         comb->aircode,
00832                         comb->los,
00833                         comb->GIFTmater );
00834 
00835                 if( comb->is_fastgen == REGION_FASTGEN_PLATE )
00836                         bu_vls_printf( str, "(FASTGEN plate mode) " );
00837                 else if( comb->is_fastgen == REGION_FASTGEN_VOLUME )
00838                         bu_vls_printf( str, "(FASTGEN volume mode) " );
00839         }
00840 
00841 
00842         bu_vls_strcat( str, "--\n" );
00843         if( bu_vls_strlen(&comb->shader) > 0 ) {
00844                 bu_vls_printf( str,
00845                         "Shader '%s'\n",
00846                         bu_vls_addr(&comb->shader) );
00847         }
00848 
00849         if( comb->rgb_valid ) {
00850                 bu_vls_printf( str,
00851                         "Color %d %d %d\n",
00852                         comb->rgb[0],
00853                         comb->rgb[1],
00854                         comb->rgb[2]);
00855         }
00856 
00857         if( bu_vls_strlen(&comb->shader) > 0 || comb->rgb_valid )  {
00858                 if( comb->inherit ) {
00859                         bu_vls_strcat( str,
00860         "(These material properties override all lower ones in the tree)\n");
00861                 }
00862         }
00863 
00864         if( comb->tree )  {
00865                 if( verbose )  {
00866                         db_tree_flatten_describe( str, comb->tree, 0, 1, mm2local, resp );
00867                 } else {
00868                         rt_pr_tree_vls( str, comb->tree );
00869                 }
00870         } else {
00871                 bu_vls_strcat( str, "(empty tree)\n");
00872         }
00873 }
00874 
00875 /**
00876  *                      R T _ C O M B _ I F R E E
00877  *
00878  *  Free the storage associated with the rt_db_internal version of this combination.
00879  */
00880 void
00881 rt_comb_ifree( struct rt_db_internal *ip, struct resource *resp )
00882 {
00883         register struct rt_comb_internal        *comb;
00884 
00885         RT_CK_DB_INTERNAL(ip);
00886         RT_CK_RESOURCE(resp);
00887         comb = (struct rt_comb_internal *)ip->idb_ptr;
00888 
00889         if (comb) {
00890             /* If tree hasn't been stolen, release it */
00891             if(comb->tree) db_free_tree( comb->tree, resp );
00892             comb->tree = NULL;
00893             
00894             bu_vls_free( &comb->shader );
00895             bu_vls_free( &comb->material );
00896             
00897             comb->magic = 0;                    /* sanity */
00898             bu_free( (genptr_t)comb, "comb ifree" );
00899         }
00900         ip->idb_ptr = GENPTR_NULL;      /* sanity */
00901 }
00902 
00903 
00904 /**
00905  *                      R T _ C O M B _ D E S C R I B E
00906  *
00907  *  rt_functab[ID_COMBINATION].ft_describe() method
00908  */
00909 int
00910 rt_comb_describe(
00911         struct bu_vls   *str,
00912         const struct rt_db_internal *ip,
00913         int             verbose,
00914         double          mm2local,
00915         struct resource *resp,
00916         struct db_i *db_i)
00917 {
00918         const struct rt_comb_internal   *comb;
00919 
00920         RT_CK_DB_INTERNAL(ip);
00921         RT_CK_RESOURCE(resp);
00922 
00923         comb = (struct rt_comb_internal *)ip->idb_ptr;
00924         RT_CK_COMB(comb);
00925 
00926         db_comb_describe( str, comb, verbose, mm2local, resp );
00927         return 0;
00928 }
00929 
00930 /*==================== END g_comb.c / table.c interface ========== */
00931 
00932 /**
00933  *                      D B _ W R A P _ V 4 _ E X T E R N A L
00934  *
00935  *  As the v4 database does not really have the notion of "wrapping",
00936  *  this function writes the object name into the
00937  *  proper place (a standard location in all granules).
00938  */
00939 void
00940 db_wrap_v4_external( struct bu_external *op, const char *name )
00941 {
00942         union record    *rec;
00943 
00944         BU_CK_EXTERNAL(op);
00945 
00946         rec = (union record *)op->ext_buf;
00947         NAMEMOVE( name, rec->s.s_name );
00948 }
00949 
00950 /* Some export support routines */
00951 
00952 /**
00953  *                      D B _ C K _ L E F T _ H E A V Y _ T R E E
00954  *
00955  *  Support routine for db_ck_v4gift_tree().
00956  *  Ensure that the tree below 'tp' is left-heavy, i.e. that there are
00957  *  nothing but solids on the right side of any binary operations.
00958  *
00959  *  Returns -
00960  *      -1      ERROR
00961  *       0      OK
00962  */
00963 int
00964 db_ck_left_heavy_tree(
00965         const union tree        *tp,
00966         int                     no_unions)
00967 {
00968         RT_CK_TREE(tp);
00969         switch( tp->tr_op )  {
00970 
00971         case OP_DB_LEAF:
00972                 break;
00973 
00974         case OP_UNION:
00975                 if( no_unions )  return -1;
00976                 /* else fall through */
00977         case OP_INTERSECT:
00978         case OP_SUBTRACT:
00979         case OP_XOR:
00980                 if( db_ck_left_heavy_tree( tp->tr_b.tb_right, no_unions ) < 0 )
00981                         return -1;
00982                 return db_ck_left_heavy_tree( tp->tr_b.tb_left, no_unions );
00983 
00984         default:
00985                 bu_log("db_ck_left_heavy_tree: bad op %d\n", tp->tr_op);
00986                 bu_bomb("db_ck_left_heavy_tree\n");
00987         }
00988         return 0;
00989 }
00990 
00991 
00992 /**
00993  *                      D B _ C K _ V 4 G I F T _ T R E E
00994  *
00995  *  Look a gift-tree in the mouth.
00996  *  Ensure that this boolean tree conforms to the GIFT convention that
00997  *  union operations must bind the loosest.
00998  *  There are two stages to this check:
00999  *  1)  Ensure that if unions are present they are all at the root of tree,
01000  *  2)  Ensure non-union children of union nodes are all left-heavy
01001  *      (nothing but solid nodes permitted on rhs of binary operators).
01002  *
01003  *  Returns -
01004  *      -1      ERROR
01005  *       0      OK
01006  */
01007 int
01008 db_ck_v4gift_tree( const union tree *tp )
01009 {
01010         RT_CK_TREE(tp);
01011         switch( tp->tr_op )  {
01012 
01013         case OP_DB_LEAF:
01014                 break;
01015 
01016         case OP_UNION:
01017                 if( db_ck_v4gift_tree( tp->tr_b.tb_left ) < 0 )
01018                         return -1;
01019                 return db_ck_v4gift_tree( tp->tr_b.tb_right );
01020 
01021         case OP_INTERSECT:
01022         case OP_SUBTRACT:
01023         case OP_XOR:
01024                 return db_ck_left_heavy_tree( tp, 1 );
01025 
01026         default:
01027                 bu_log("db_ck_v4gift_tree: bad op %d\n", tp->tr_op);
01028                 bu_bomb("db_ck_v4gift_tree\n");
01029         }
01030         return 0;
01031 }
01032 
01033 
01034 /**
01035  *                      D B _ M K B O O L _ T R E E
01036  *
01037  *  Given a rt_tree_array array, build a tree of "union tree" nodes
01038  *  appropriately connected together.  Every element of the
01039  *  rt_tree_array array used is replaced with a TREE_NULL.
01040  *  Elements which are already TREE_NULL are ignored.
01041  *  Returns a pointer to the top of the tree.
01042  */
01043 union tree *
01044 db_mkbool_tree(
01045         struct rt_tree_array *rt_tree_array,
01046         int             howfar,
01047         struct resource *resp)
01048 {
01049         register struct rt_tree_array *tlp;
01050         register int            i;
01051         register struct rt_tree_array *first_tlp = (struct rt_tree_array *)0;
01052         register union tree     *xtp;
01053         register union tree     *curtree;
01054         register int            inuse;
01055 
01056         RT_CK_RESOURCE(resp);
01057 
01058         if( howfar <= 0 )
01059                 return(TREE_NULL);
01060 
01061         /* Count number of non-null sub-trees to do */
01062         for( i=howfar, inuse=0, tlp=rt_tree_array; i>0; i--, tlp++ )  {
01063                 if( tlp->tl_tree == TREE_NULL )
01064                         continue;
01065                 if( inuse++ == 0 )
01066                         first_tlp = tlp;
01067         }
01068 
01069         /* Handle trivial cases */
01070         if( inuse <= 0 )
01071                 return(TREE_NULL);
01072         if( inuse == 1 )  {
01073                 curtree = first_tlp->tl_tree;
01074                 first_tlp->tl_tree = TREE_NULL;
01075                 return( curtree );
01076         }
01077 
01078         if( first_tlp->tl_op != OP_UNION )  {
01079                 first_tlp->tl_op = OP_UNION;    /* Fix it */
01080                 if( RT_G_DEBUG & DEBUG_TREEWALK )  {
01081                         bu_log("db_mkbool_tree() WARNING: non-union (%c) first operation ignored\n",
01082                                 first_tlp->tl_op );
01083                 }
01084         }
01085 
01086         curtree = first_tlp->tl_tree;
01087         first_tlp->tl_tree = TREE_NULL;
01088         tlp=first_tlp+1;
01089         for( i=howfar-(tlp-rt_tree_array); i>0; i--, tlp++ )  {
01090                 if( tlp->tl_tree == TREE_NULL )
01091                         continue;
01092 
01093                 RT_GET_TREE( xtp, resp );
01094                 xtp->magic = RT_TREE_MAGIC;
01095                 xtp->tr_b.tb_left = curtree;
01096                 xtp->tr_b.tb_right = tlp->tl_tree;
01097                 xtp->tr_b.tb_regionp = (struct region *)0;
01098                 xtp->tr_op = tlp->tl_op;
01099                 curtree = xtp;
01100                 tlp->tl_tree = TREE_NULL;       /* empty the input slot */
01101         }
01102         return(curtree);
01103 }
01104 
01105 /**
01106  *                      D B _ M K G I F T _ T R E E
01107  */
01108 union tree *
01109 db_mkgift_tree(
01110         struct rt_tree_array    *trees,
01111         int                     subtreecount,
01112         struct resource         *resp)
01113 {
01114         register struct rt_tree_array *tstart;
01115         register struct rt_tree_array *tnext;
01116         union tree              *curtree;
01117         int     i;
01118         int     j;
01119 
01120         RT_CK_RESOURCE(resp);
01121 
01122         /*
01123          * This is how GIFT interpreted equations, so it is duplicated here.
01124          * Any expressions between UNIONs are evaluated first.  For example:
01125          *              A - B - C u D - E - F
01126          * becomes      (A - B - C) u (D - E - F)
01127          * so first do the parenthesised parts, and then go
01128          * back and glue the unions together.
01129          * As always, unions are the downfall of free enterprise!
01130          */
01131         tstart = trees;
01132         tnext = trees+1;
01133         for( i=subtreecount-1; i>=0; i--, tnext++ )  {
01134                 /* If we went off end, or hit a union, do it */
01135                 if( i>0 && tnext->tl_op != OP_UNION )
01136                         continue;
01137                 if( (j = tnext-tstart) <= 0 )
01138                         continue;
01139                 curtree = db_mkbool_tree( tstart, j, resp );
01140                 /* db_mkbool_tree() has side effect of zapping tree array,
01141                  * so build new first node in array.
01142                  */
01143                 tstart->tl_op = OP_UNION;
01144                 tstart->tl_tree = curtree;
01145 
01146                 if(RT_G_DEBUG&DEBUG_TREEWALK)  {
01147                         bu_log("db_mkgift_tree() intermediate term:\n");
01148                         rt_pr_tree(tstart->tl_tree, 0);
01149                 }
01150 
01151                 /* tstart here at union */
01152                 tstart = tnext;
01153         }
01154 
01155         curtree = db_mkbool_tree( trees, subtreecount, resp );
01156         if(RT_G_DEBUG&DEBUG_TREEWALK)  {
01157                 bu_log("db_mkgift_tree() returns:\n");
01158                 rt_pr_tree(curtree, 0);
01159         }
01160         return( curtree );
01161 }
01162 
01163 /*@}*/
01164 /*
01165  * Local Variables:
01166  * mode: C
01167  * tab-width: 8
01168  * c-basic-offset: 4
01169  * indent-tabs-mode: t
01170  * End:
01171  * ex: shiftwidth=4 tabstop=8
01172  */

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