wdb_obj.c

Go to the documentation of this file.
00001 /*                       W D B _ O B J . C
00002  * BRL-CAD
00003  *
00004  * Copyright (c) 2000-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 wdb */
00023 /*@{*/
00024 /** @file wdb_obj.c
00025  *  A database object contains the attributes and
00026  *  methods for controlling a BRL-CAD database.
00027  *
00028  *  Authors -
00029  *      Michael John Muuss
00030  *      Glenn Durfee
00031  *      Robert G. Parker
00032  *
00033  *  Source -
00034  *      The U. S. Army Research Laboratory
00035  *      Aberdeen Proving Ground, Maryland  21005-5068  USA
00036  */
00037 /*@}*/
00038 
00039 #ifndef lint
00040 static const char RCSid[] = "@(#)$Header: /cvsroot/brlcad/brlcad/src/librt/wdb_obj.c,v 14.37 2006/09/16 02:04:26 lbutler Exp $ (ARL)";
00041 #endif
00042 
00043 #include "common.h"
00044 
00045 #include <stdlib.h>
00046 #include <ctype.h>
00047 #ifdef HAVE_STRING_H
00048 #  include <string.h>
00049 #else
00050 #  include <strings.h>
00051 #endif
00052 #include <math.h>
00053 #if defined(HAVE_FCNTL_H)
00054 #  include <fcntl.h>
00055 #endif
00056 #if defined(HAVE_ERRNO_H)
00057 #  include <errno.h>
00058 #else
00059 #  if defined(HAVE_SYS_ERRNO_H)
00060 #    include <sys/errno.h>
00061 #  endif
00062 #endif
00063 #if defined(HAVE_UNISTD_H)
00064 #  include <unistd.h>
00065 #endif
00066 
00067 #include "tcl.h"
00068 #include "machine.h"
00069 #include "cmd.h"                /* this includes bu.h */
00070 #include "vmath.h"
00071 #include "bn.h"
00072 #include "db.h"
00073 #include "mater.h"
00074 #include "rtgeom.h"
00075 #include "raytrace.h"
00076 #include "wdb.h"
00077 
00078 #include "./debug.h"
00079 
00080 /*
00081  * rt_comb_ifree() should NOT be used here because
00082  * it doesn't know how to free attributes.
00083  * rt_db_free_internal() should be used instead.
00084  */
00085 #define USE_RT_COMB_IFREE 0
00086 
00087 /* defined in mater.c */
00088 extern void rt_insert_color( struct mater *newp );
00089 
00090 #define WDB_TCL_READ_ERR { \
00091         Tcl_AppendResult(interp, "Database read error, aborting.\n", (char *)NULL); \
00092         }
00093 
00094 #define WDB_TCL_READ_ERR_return { \
00095         WDB_TCL_READ_ERR; \
00096         return TCL_ERROR; }
00097 
00098 #define WDB_TCL_WRITE_ERR { \
00099         Tcl_AppendResult(interp, "Database write error, aborting.\n", (char *)NULL); \
00100         WDB_TCL_ERROR_RECOVERY_SUGGESTION; }
00101 
00102 #define WDB_TCL_WRITE_ERR_return { \
00103         WDB_TCL_WRITE_ERR; \
00104         return TCL_ERROR; }
00105 
00106 #define WDB_TCL_ALLOC_ERR { \
00107         Tcl_AppendResult(interp, "\
00108 An error has occured while adding a new object to the database.\n", (char *)NULL); \
00109         WDB_TCL_ERROR_RECOVERY_SUGGESTION; }
00110 
00111 #define WDB_TCL_ALLOC_ERR_return { \
00112         WDB_TCL_ALLOC_ERR; \
00113         return TCL_ERROR; }
00114 
00115 #define WDB_TCL_DELETE_ERR(_name){ \
00116         Tcl_AppendResult(interp, "An error has occurred while deleting '", _name,\
00117         "' from the database.\n", (char *)NULL);\
00118         WDB_TCL_ERROR_RECOVERY_SUGGESTION; }
00119 
00120 #define WDB_TCL_DELETE_ERR_return(_name){  \
00121         WDB_TCL_DELETE_ERR(_name); \
00122         return TCL_ERROR;  }
00123 
00124 /* A verbose message to attempt to soothe and advise the user */
00125 #define WDB_TCL_ERROR_RECOVERY_SUGGESTION\
00126         Tcl_AppendResult(interp, "\
00127 The in-memory table of contents may not match the status of the on-disk\n\
00128 database.  The on-disk database should still be intact.  For safety,\n\
00129 you should exit now, and resolve the I/O problem, before continuing.\n", (char *)NULL)
00130 
00131 #define WDB_READ_ERR { \
00132         bu_log("Database read error, aborting\n"); }
00133 
00134 #define WDB_READ_ERR_return { \
00135         WDB_READ_ERR; \
00136         return;  }
00137 
00138 #define WDB_WRITE_ERR { \
00139         bu_log("Database write error, aborting.\n"); \
00140         WDB_ERROR_RECOVERY_SUGGESTION; }
00141 
00142 #define WDB_WRITE_ERR_return { \
00143         WDB_WRITE_ERR; \
00144         return;  }
00145 
00146 /* For errors from db_diradd() or db_alloc() */
00147 #define WDB_ALLOC_ERR { \
00148         bu_log("\nAn error has occured while adding a new object to the database.\n"); \
00149         WDB_ERROR_RECOVERY_SUGGESTION; }
00150 
00151 #define WDB_ALLOC_ERR_return { \
00152         WDB_ALLOC_ERR; \
00153         return;  }
00154 
00155 /* A verbose message to attempt to soothe and advise the user */
00156 #define WDB_ERROR_RECOVERY_SUGGESTION\
00157         bu_log(WDB_ERROR_RECOVERY_MESSAGE)
00158 
00159 #define WDB_ERROR_RECOVERY_MESSAGE "\
00160 The in-memory table of contents may not match the status of the on-disk\n\
00161 database.  The on-disk database should still be intact.  For safety,\n\
00162 you should exit now, and resolve the I/O problem, before continuing.\n"
00163 
00164 #define WDB_TCL_CHECK_READ_ONLY \
00165         if (wdbp->dbip->dbi_read_only) {\
00166                 Tcl_AppendResult(interp, "Sorry, this database is READ-ONLY\n", (char *)NULL); \
00167                 return TCL_ERROR; \
00168         }
00169 
00170 #define WDB_MAX_LEVELS 12
00171 #define WDB_CPEVAL      0
00172 #define WDB_LISTPATH    1
00173 #define WDB_LISTEVAL    2
00174 #define WDB_EVAL_ONLY   3
00175 
00176 struct wdb_trace_data {
00177         Tcl_Interp              *wtd_interp;
00178         struct db_i             *wtd_dbip;
00179         struct directory        *wtd_path[WDB_MAX_LEVELS];
00180         struct directory        *wtd_obj[WDB_MAX_LEVELS];
00181         mat_t                   wtd_xform;
00182         int                     wtd_objpos;
00183         int                     wtd_prflag;
00184         int                     wtd_flag;
00185 };
00186 
00187 struct wdb_killtree_data {
00188   Tcl_Interp    *interp;
00189   int           notify;
00190 };
00191 
00192 /* from librt/tcl.c */
00193 extern int rt_tcl_rt(ClientData clientData, Tcl_Interp *interp, int argc, const char **argv);
00194 extern int rt_tcl_import_from_path(Tcl_Interp *interp, struct rt_db_internal *ip, const char *path, struct rt_wdb *wdb);
00195 extern void rt_generic_make(const struct rt_functab *ftp, struct rt_db_internal *intern, double diameter);
00196 
00197 /* from librt/wdb_comb_std.c */
00198 extern int wdb_comb_std_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00199 
00200 /* from librt/g_bot.c */
00201 extern int rt_bot_sort_faces( struct rt_bot_internal *bot, int tris_per_piece );
00202 extern int rt_bot_decimate( struct rt_bot_internal *bot, fastf_t max_chord_error, fastf_t max_normal_error, fastf_t min_edge_length );
00203 
00204 /* from db5_scan.c */
00205 HIDDEN int db5_scan(struct db_i *dbip, void (*handler) (struct db_i *, const struct db5_raw_internal *, long int, genptr_t), genptr_t client_data);
00206 
00207 int wdb_init_obj(Tcl_Interp *interp, struct rt_wdb *wdbp, const char *oname);
00208 int wdb_get_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00209 int wdb_get_type_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00210 int wdb_attr_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00211 int wdb_pathsum_cmd(struct rt_wdb *wdbp, Tcl_Interp *interp, int argc, char **argv);
00212 
00213 static int wdb_open_tcl(ClientData clientData, Tcl_Interp *interp, int argc, const char **argv);
00214 #if 0
00215 static int wdb_close_tcl();
00216 #endif
00217 static int wdb_decode_dbip(Tcl_Interp *interp, const char *dbip_string, struct db_i **dbipp);
00218 struct db_i *wdb_prep_dbip(Tcl_Interp *interp, const char *filename);
00219 
00220 static int wdb_cmd(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00221 static int wdb_match_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00222 static int wdb_put_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00223 static int wdb_adjust_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00224 static int wdb_form_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00225 static int wdb_tops_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00226 static int wdb_rt_gettrees_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00227 static int wdb_shells_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00228 static int wdb_dump_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00229 static int wdb_dbip_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00230 static int wdb_ls_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00231 static int wdb_list_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00232 static int wdb_pathsum_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00233 static int wdb_expand_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00234 static int wdb_kill_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00235 static int wdb_killall_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00236 static int wdb_killtree_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00237 static void wdb_killtree_callback(struct db_i *dbip, register struct directory *dp, genptr_t ptr);
00238 static int wdb_copy_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00239 static int wdb_move_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00240 static int wdb_move_all_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00241 static int wdb_concat_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00242 static int wdb_copyeval_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00243 static int wdb_dup_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00244 static int wdb_group_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00245 static int wdb_remove_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00246 static int wdb_region_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00247 static int wdb_comb_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00248 static int wdb_facetize_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00249 static int wdb_find_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00250 static int wdb_which_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00251 static int wdb_title_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00252 static int wdb_track_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00253 static int wdb_tree_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00254 static int wdb_color_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00255 static int wdb_prcolor_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00256 static int wdb_tol_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00257 static int wdb_push_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00258 static int wdb_whatid_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00259 static int wdb_keep_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00260 static int wdb_cat_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00261 static int wdb_instance_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00262 static int wdb_observer_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00263 static int wdb_reopen_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00264 static int wdb_make_bb_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00265 static int wdb_make_name_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00266 static int wdb_units_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00267 static int wdb_hide_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00268 static int wdb_unhide_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00269 static int wdb_xpush_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00270 static int wdb_smooth_bot_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00271 static int wdb_showmats_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00272 static int wdb_nmg_collapse_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00273 static int wdb_nmg_simplify_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00274 static int wdb_summary_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00275 static int wdb_pathlist_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00276 static int wdb_lt_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00277 static int wdb_version_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00278 static int wdb_binary_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00279 static int wdb_bot_face_sort_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00280 static int wdb_bot_decimate_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00281 static int wdb_move_arb_edge_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00282 static int wdb_move_arb_face_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00283 static int wdb_rotate_arb_face_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00284 static int wdb_rmap_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00285 static int wdb_importFg4Section_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00286 static int wdb_orotate_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00287 static int wdb_oscale_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00288 static int wdb_otranslate_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00289 static int wdb_ocenter_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
00290 
00291 void wdb_deleteProc(ClientData clientData);
00292 static void wdb_deleteProc_rt(ClientData clientData);
00293 
00294 static void wdb_do_trace(struct db_i *dbip, struct rt_comb_internal *comb, union tree *comb_leaf, genptr_t user_ptr1, genptr_t user_ptr2, genptr_t user_ptr3);
00295 static void wdb_trace(register struct directory *dp, int pathpos, const fastf_t *old_xlate, struct wdb_trace_data *wtdp);
00296 
00297 int wdb_cmpdirname(const genptr_t a, const genptr_t b);
00298 void wdb_vls_col_item(struct bu_vls *str, register char *cp, int *ccp, int *clp);
00299 void wdb_vls_col_eol(struct bu_vls *str, int *ccp, int *clp);
00300 void wdb_vls_col_pr4v(struct bu_vls *vls, struct directory **list_of_names, int num_in_list, int no_decorate);
00301 void wdb_vls_long_dpp(struct bu_vls *vls, struct directory **list_of_names, int num_in_list, int aflag, int cflag, int rflag, int sflag);
00302 void wdb_vls_line_dpp(struct bu_vls *vls, struct directory **list_of_names, int num_in_list, int aflag, int cflag, int rflag, int sflag);
00303 void wdb_do_list(struct db_i *dbip, Tcl_Interp *interp, struct bu_vls *outstrp, register struct directory *dp, int verbose);
00304 struct directory ** wdb_getspace(struct db_i *dbip, register int num_entries);
00305 struct directory *wdb_combadd(Tcl_Interp *interp, struct db_i *dbip, register struct directory *objp, char *combname, int region_flag, int relation, int ident, int air, struct rt_wdb *wdbp);
00306 void wdb_identitize(struct directory *dp, struct db_i *dbip, Tcl_Interp *interp);
00307 static void wdb_dir_summary(struct db_i *dbip, Tcl_Interp *interp, int flag);
00308 static struct directory ** wdb_dir_getspace(struct db_i *dbip, register int num_entries);
00309 static union tree *wdb_pathlist_leaf_func(struct db_tree_state *tsp, struct db_full_path *pathp, struct rt_db_internal *ip, genptr_t client_data);
00310 HIDDEN union tree *facetize_region_end(struct db_tree_state *tsp, struct db_full_path *pathp, union tree *curtree, genptr_t client_data);
00311 static int pathListNoLeaf = 0;
00312 
00313 
00314 static struct bu_cmdtab wdb_cmds[] = {
00315         {"adjust",      wdb_adjust_tcl},
00316         {"attr",        wdb_attr_tcl},
00317         {"binary",      wdb_binary_tcl},
00318         {"bot_face_sort", wdb_bot_face_sort_tcl},
00319         {"bot_decimate", wdb_bot_decimate_tcl},
00320         {"c",           wdb_comb_std_tcl},
00321         {"cat",         wdb_cat_tcl},
00322 #if 0
00323         {"close",       wdb_close_tcl},
00324 #endif
00325         {"color",       wdb_color_tcl},
00326         {"comb",        wdb_comb_tcl},
00327         {"concat",      wdb_concat_tcl},
00328         {"copyeval",    wdb_copyeval_tcl},
00329         {"cp",          wdb_copy_tcl},
00330         {"dbip",        wdb_dbip_tcl},
00331         {"dump",        wdb_dump_tcl},
00332         {"dup",         wdb_dup_tcl},
00333         {"expand",      wdb_expand_tcl},
00334         {"facetize",    wdb_facetize_tcl},
00335         {"find",        wdb_find_tcl},
00336         {"form",        wdb_form_tcl},
00337         {"g",           wdb_group_tcl},
00338         {"get",         wdb_get_tcl},
00339         {"get_type",    wdb_get_type_tcl},
00340         {"hide",        wdb_hide_tcl},
00341         {"i",           wdb_instance_tcl},
00342         {"importFg4Section",            wdb_importFg4Section_tcl},
00343         {"keep",        wdb_keep_tcl},
00344         {"kill",        wdb_kill_tcl},
00345         {"killall",     wdb_killall_tcl},
00346         {"killtree",    wdb_killtree_tcl},
00347         {"l",           wdb_list_tcl},
00348         {"listeval",    wdb_pathsum_tcl},
00349         {"ls",          wdb_ls_tcl},
00350         {"lt",          wdb_lt_tcl},
00351         {"make_bb",     wdb_make_bb_tcl},
00352         {"make_name",   wdb_make_name_tcl},
00353         {"match",       wdb_match_tcl},
00354         {"move_arb_edge",       wdb_move_arb_edge_tcl},
00355         {"move_arb_face",       wdb_move_arb_face_tcl},
00356         {"mv",          wdb_move_tcl},
00357         {"mvall",       wdb_move_all_tcl},
00358         {"nmg_collapse",wdb_nmg_collapse_tcl},
00359         {"nmg_simplify",wdb_nmg_simplify_tcl},
00360         {"observer",    wdb_observer_tcl},
00361         {"ocenter",     wdb_ocenter_tcl},
00362         {"orotate",     wdb_orotate_tcl},
00363         {"oscale",      wdb_oscale_tcl},
00364         {"otranslate",  wdb_otranslate_tcl},
00365         {"open",        wdb_reopen_tcl},
00366         {"pathlist",    wdb_pathlist_tcl},
00367         {"paths",       wdb_pathsum_tcl},
00368         {"prcolor",     wdb_prcolor_tcl},
00369         {"push",        wdb_push_tcl},
00370         {"put",         wdb_put_tcl},
00371         {"r",           wdb_region_tcl},
00372         {"rm",          wdb_remove_tcl},
00373         {"rmap",        wdb_rmap_tcl},
00374         {"rotate_arb_face",     wdb_rotate_arb_face_tcl},
00375         {"rt_gettrees", wdb_rt_gettrees_tcl},
00376         {"shells",      wdb_shells_tcl},
00377         {"showmats",    wdb_showmats_tcl},
00378         {"smooth_bot",  wdb_smooth_bot_tcl},
00379         {"summary",     wdb_summary_tcl},
00380         {"title",       wdb_title_tcl},
00381         {"tol",         wdb_tol_tcl},
00382         {"tops",        wdb_tops_tcl},
00383         {"track",       wdb_track_tcl},
00384         {"tree",        wdb_tree_tcl},
00385         {"unhide",      wdb_unhide_tcl},
00386         {"units",       wdb_units_tcl},
00387         {"version",     wdb_version_tcl},
00388         {"whatid",      wdb_whatid_tcl},
00389         {"whichair",    wdb_which_tcl},
00390         {"whichid",     wdb_which_tcl},
00391         {"xpush",       wdb_xpush_tcl},
00392 #if 0
00393         /* Commands to be added */
00394         {"comb_color",  wdb_comb_color_tcl},
00395         {"copymat",     wdb_copymat_tcl},
00396         {"getmat",      wdb_getmat_tcl},
00397         {"putmat",      wdb_putmat_tcl},
00398         {"which_shader",        wdb_which_shader_tcl},
00399         {"rcodes",      wdb_rcodes_tcl},
00400         {"wcodes",      wdb_wcodes_tcl},
00401         {"rmater",      wdb_rmater_tcl},
00402         {"wmater",      wdb_wmater_tcl},
00403         {"analyze",     wdb_analyze_tcl},
00404         {"inside",      wdb_inside_tcl},
00405 #endif
00406         {(char *)NULL,  (int (*)())0 }
00407 };
00408 
00409 int
00410 Wdb_Init(Tcl_Interp *interp)
00411 {
00412         (void)Tcl_CreateCommand(interp, (const char *)"wdb_open", wdb_open_tcl,
00413                                 (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
00414 
00415         return TCL_OK;
00416 }
00417 
00418 /*
00419  *                      W D B _ C M D
00420  *
00421  * Generic interface for database commands.
00422  * Usage:
00423  *        procname cmd ?args?
00424  *
00425  * Returns: result of wdb command.
00426  */
00427 static int
00428 wdb_cmd(ClientData clientData, Tcl_Interp *interp, int argc, char **argv)
00429 {
00430         return bu_cmd(clientData, interp, argc, argv, wdb_cmds, 1);
00431 }
00432 
00433 /*
00434  * Called by Tcl when the object is destroyed.
00435  */
00436 void
00437 wdb_deleteProc(ClientData clientData)
00438 {
00439         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
00440 
00441         /* free observers */
00442         bu_observer_free(&wdbp->wdb_observers);
00443 
00444         /* notify drawable geometry objects of the impending close */
00445         dgo_impending_wdb_close(wdbp, wdbp->wdb_interp);
00446 
00447         RT_CK_WDB(wdbp);
00448         BU_LIST_DEQUEUE(&wdbp->l);
00449         bu_vls_free(&wdbp->wdb_name);
00450         wdb_close(wdbp);
00451 }
00452 
00453 /*
00454  * Create a command named "oname" in "interp"
00455  * using "wdbp" as its state.
00456  */
00457 int
00458 wdb_create_cmd(Tcl_Interp       *interp,
00459                struct rt_wdb    *wdbp,  /* pointer to object */
00460                const char       *oname) /* object name */
00461 {
00462     if (wdbp == RT_WDB_NULL) {
00463         Tcl_AppendResult(interp, "wdb_init_cmd ", oname, " failed", NULL);
00464         return TCL_ERROR;
00465     }
00466 
00467     /* Instantiate the newprocname, with clientData of wdbp */
00468     /* Beware, returns a "token", not TCL_OK. */
00469     (void)Tcl_CreateCommand(interp, oname, (Tcl_CmdProc *)wdb_cmd,
00470                             (ClientData)wdbp, wdb_deleteProc);
00471 
00472     /* Return new function name as result */
00473     Tcl_AppendResult(interp, oname, (char *)NULL);
00474 
00475     return TCL_OK;
00476 }
00477 
00478 /*
00479  * Create an command/object named "oname" in "interp" using "wdbp" as
00480  * its state.  It is presumed that the wdbp has already been opened.
00481  */
00482 int
00483 wdb_init_obj(Tcl_Interp         *interp,
00484              struct rt_wdb      *wdbp,  /* pointer to object */
00485              const char         *oname) /* object name */
00486 {
00487         if (wdbp == RT_WDB_NULL) {
00488                 Tcl_AppendResult(interp, "wdb_open ", oname, " failed (wdb_init_obj)", NULL);
00489                 return TCL_ERROR;
00490         }
00491 
00492         /* initialize rt_wdb */
00493         bu_vls_init(&wdbp->wdb_name);
00494         bu_vls_strcpy(&wdbp->wdb_name, oname);
00495 
00496 #if 0
00497         /*XXXX already initialize by wdb_dbopen */
00498         /* initilize tolerance structures */
00499         wdbp->wdb_ttol.magic = RT_TESS_TOL_MAGIC;
00500         wdbp->wdb_ttol.abs = 0.0;               /* disabled */
00501         wdbp->wdb_ttol.rel = 0.01;
00502         wdbp->wdb_ttol.norm = 0.0;              /* disabled */
00503         
00504         wdbp->wdb_tol.magic = BN_TOL_MAGIC;
00505         wdbp->wdb_tol.dist = 0.005;
00506         wdbp->wdb_tol.dist_sq = wdbp->wdb_tol.dist * wdbp->wdb_tol.dist;
00507         wdbp->wdb_tol.perp = 1e-6;
00508         wdbp->wdb_tol.para = 1 - wdbp->wdb_tol.perp;
00509 #endif
00510  
00511         /* initialize tree state */
00512         wdbp->wdb_initial_tree_state = rt_initial_tree_state;  /* struct copy */
00513         wdbp->wdb_initial_tree_state.ts_ttol = &wdbp->wdb_ttol;
00514         wdbp->wdb_initial_tree_state.ts_tol = &wdbp->wdb_tol;
00515 
00516         /* default region ident codes */
00517         wdbp->wdb_item_default = 1000;
00518         wdbp->wdb_air_default = 0;
00519         wdbp->wdb_mat_default = 1;
00520         wdbp->wdb_los_default = 100;
00521 
00522         /* resource structure */
00523         wdbp->wdb_resp = &rt_uniresource;
00524 
00525         BU_LIST_INIT(&wdbp->wdb_observers.l);
00526         wdbp->wdb_interp = interp;
00527 
00528         /* append to list of rt_wdb's */
00529         BU_LIST_APPEND(&rt_g.rtg_headwdb.l,&wdbp->l);
00530 
00531         return TCL_OK;
00532 }
00533 
00534 /*
00535  *                      W D B _ O P E N _ T C L
00536  *
00537  *  A TCL interface to wdb_fopen() and wdb_dbopen().
00538  *
00539  *  Implicit return -
00540  *      Creates a new TCL proc which responds to get/put/etc. arguments
00541  *      when invoked.  clientData of that proc will be rt_wdb pointer
00542  *      for this instance of the database.
00543  *      Easily allows keeping track of multiple databases.
00544  *
00545  *  Explicit return -
00546  *      wdb pointer, for more traditional C-style interfacing.
00547  *
00548  *  Example -
00549  *      set wdbp [wdb_open .inmem inmem $dbip]
00550  *      .inmem get box.s
00551  *      .inmem close
00552  *
00553  *      wdb_open db file "bob.g"
00554  *      db get white.r
00555  *      db close
00556  */
00557 static int
00558 wdb_open_tcl(ClientData clientData,
00559              Tcl_Interp *interp,
00560              int        argc,
00561              const char **argv)
00562 {
00563         struct rt_wdb *wdbp;
00564         int ret;
00565 
00566         if (argc == 1) {
00567                 /* get list of database objects */
00568                 for (BU_LIST_FOR(wdbp, rt_wdb, &rt_g.rtg_headwdb.l))
00569                         Tcl_AppendResult(interp, bu_vls_addr(&wdbp->wdb_name), " ", (char *)NULL);
00570 
00571                 return TCL_OK;
00572         }
00573 
00574         if (argc < 3 || 4 < argc) {
00575 #if 0
00576                 bu_vls_init(&vls);
00577                 bu_vls_printf(&vls, "helplib wdb_open");
00578                 Tcl_Eval(interp, bu_vls_addr(&vls));
00579                 bu_vls_free(&vls);
00580                 return TCL_ERROR;
00581 #else
00582                 Tcl_AppendResult(interp, "\
00583 Usage: wdb_open\n\
00584        wdb_open newprocname file filename\n\
00585        wdb_open newprocname disk $dbip\n\
00586        wdb_open newprocname disk_append $dbip\n\
00587        wdb_open newprocname inmem $dbip\n\
00588        wdb_open newprocname inmem_append $dbip\n\
00589        wdb_open newprocname db filename\n\
00590        wdb_open newprocname filename\n",
00591                                  NULL);
00592                 return TCL_ERROR;
00593 #endif
00594         }
00595 
00596         /* Delete previous proc (if any) to release all that memory, first */
00597         (void)Tcl_DeleteCommand(interp, argv[1]);
00598 
00599         if (argc == 3 || strcmp(argv[2], "db") == 0) {
00600                 struct db_i     *dbip;
00601                 int i;
00602 
00603                 if (argc == 3)
00604                         i = 2;
00605                 else
00606                         i = 3;
00607 
00608                 if ((dbip = wdb_prep_dbip(interp, argv[i])) == DBI_NULL)
00609                         return TCL_ERROR;
00610                 RT_CK_DBI_TCL(interp,dbip);
00611 
00612                 wdbp = wdb_dbopen(dbip, RT_WDB_TYPE_DB_DISK);
00613         } else if (strcmp(argv[2], "file") == 0) {
00614                 wdbp = wdb_fopen( argv[3] );
00615         } else {
00616                 struct db_i     *dbip;
00617 
00618                 if (wdb_decode_dbip(interp, argv[3], &dbip) != TCL_OK)
00619                         return TCL_ERROR;
00620 
00621                 if (strcmp( argv[2], "disk" ) == 0)
00622                         wdbp = wdb_dbopen(dbip, RT_WDB_TYPE_DB_DISK);
00623                 else if (strcmp(argv[2], "disk_append") == 0)
00624                         wdbp = wdb_dbopen(dbip, RT_WDB_TYPE_DB_DISK_APPEND_ONLY);
00625                 else if (strcmp( argv[2], "inmem" ) == 0)
00626                         wdbp = wdb_dbopen(dbip, RT_WDB_TYPE_DB_INMEM);
00627                 else if (strcmp( argv[2], "inmem_append" ) == 0)
00628                         wdbp = wdb_dbopen(dbip, RT_WDB_TYPE_DB_INMEM_APPEND_ONLY);
00629                 else {
00630                         Tcl_AppendResult(interp, "wdb_open ", argv[2],
00631                                          " target type not recognized", NULL);
00632                         return TCL_ERROR;
00633                 }
00634         }
00635 
00636         if ((ret = wdb_init_obj(interp, wdbp, argv[1])) != TCL_OK)
00637             return ret;
00638 
00639         return wdb_create_cmd(interp, wdbp, argv[1]);
00640 }
00641 
00642 int
00643 wdb_decode_dbip(Tcl_Interp *interp, const char *dbip_string, struct db_i **dbipp)
00644 {
00645 
00646         *dbipp = (struct db_i *)atol(dbip_string);
00647 
00648         /* Could core dump */
00649         RT_CK_DBI_TCL(interp,*dbipp);
00650 
00651         return TCL_OK;
00652 }
00653 
00654 /*
00655  * Open/Create the database and build the in memory directory.
00656  */
00657 struct db_i *
00658 wdb_prep_dbip(Tcl_Interp *interp, const char *filename)
00659 {
00660         struct db_i *dbip;
00661 
00662         /* open database */
00663         if (((dbip = db_open(filename, "r+w")) == DBI_NULL) &&
00664             ((dbip = db_open(filename, "r"  )) == DBI_NULL)) {
00665 
00666 #if defined(HAVE_ACCESS)
00667                 /*
00668                  * Check to see if we can access the database
00669                  */
00670                 if (access(filename, R_OK|W_OK) != 0 && errno != ENOENT) {
00671                         perror(filename);
00672                         return DBI_NULL;
00673                 }
00674 #endif
00675 
00676                 /* db_create does a db_dirbuild */
00677                 if ((dbip = db_create(filename, 5)) == DBI_NULL) {
00678                         Tcl_AppendResult(interp,
00679                                          "wdb_open: failed to create ", filename,
00680                                          (char *)NULL);
00681                         if (dbip == DBI_NULL)
00682                                 Tcl_AppendResult(interp,
00683                                                  "opendb: no database is currently opened!", \
00684                                                  (char *)NULL);
00685 
00686                         return DBI_NULL;
00687                 }
00688         } else
00689                 /* --- Scan geometry database and build in-memory directory --- */
00690                 db_dirbuild(dbip);
00691 
00692 
00693         return dbip;
00694 }
00695 
00696 /****************** Database Object Methods ********************/
00697 #if 0
00698 int
00699 wdb_close_cmd(struct rt_wdb     *wdbp,
00700               Tcl_Interp        *interp,
00701               int               argc,
00702               char              **argv)
00703 {
00704         struct bu_vls vls;
00705 
00706         if (argc != 1) {
00707                 bu_vls_init(&vls);
00708                 bu_vls_printf(&vls, "helplib wdb_close");
00709                 Tcl_Eval(interp, bu_vls_addr(&vls));
00710                 bu_vls_free(&vls);
00711                 return TCL_ERROR;
00712         }
00713 
00714         /*
00715          * Among other things, this will call wdb_deleteProc.
00716          * Note - wdb_deleteProc is being passed clientdata.
00717          *        It ought to get interp as well.
00718          */
00719         Tcl_DeleteCommand(interp, bu_vls_addr(&wdbp->wdb_name));
00720 
00721         return TCL_OK;
00722 }
00723 
00724 /*
00725  * Close a BRL-CAD database object.
00726  *
00727  * USAGE:
00728  *        procname close
00729  */
00730 static int
00731 wdb_close_tcl(ClientData        clientData,
00732               Tcl_Interp        *interp,
00733               int               argc,
00734               char              **argv)
00735 {
00736         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
00737 
00738         return wdb_close_cmd(wdbp, interp, argc-1, argv+1);
00739 }
00740 #endif
00741 
00742 int
00743 wdb_reopen_cmd(struct rt_wdb    *wdbp,
00744                Tcl_Interp       *interp,
00745                int              argc,
00746                char             **argv)
00747 {
00748         struct db_i *dbip;
00749         struct bu_vls vls;
00750 
00751         /* get database filename */
00752         if (argc == 1) {
00753                 Tcl_AppendResult(interp, wdbp->dbip->dbi_filename, (char *)NULL);
00754                 return TCL_OK;
00755         }
00756 
00757         /* set database filename */
00758         if (argc == 2) {
00759                 if ((dbip = wdb_prep_dbip(interp, argv[1])) == DBI_NULL) {
00760                         return TCL_ERROR;
00761                 }
00762 
00763                 /* XXXnotify observers */
00764                 /* notify drawable geometry objects associated with this database */
00765                 dgo_zapall(wdbp, interp);
00766 
00767                 /* close current database */
00768                 db_close(wdbp->dbip);
00769 
00770                 wdbp->dbip = dbip;
00771 
00772                 Tcl_AppendResult(interp, wdbp->dbip->dbi_filename, (char *)NULL);
00773                 return TCL_OK;
00774         }
00775 
00776         bu_vls_init(&vls);
00777         bu_vls_printf(&vls, "helplib_alias wdb_reopen %s", argv[0]);
00778         Tcl_Eval(interp, bu_vls_addr(&vls));
00779         bu_vls_free(&vls);
00780         return TCL_ERROR;
00781 }
00782 
00783 /*
00784  *
00785  * Usage:
00786  *        procname open [filename]
00787  */
00788 static int
00789 wdb_reopen_tcl(ClientData       clientData,
00790                Tcl_Interp       *interp,
00791                int              argc,
00792                char             **argv)
00793 {
00794         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
00795 
00796         return wdb_reopen_cmd(wdbp, interp, argc-1, argv+1);
00797 }
00798 
00799 int
00800 wdb_match_cmd(struct rt_wdb     *wdbp,
00801               Tcl_Interp        *interp,
00802               int               argc,
00803               char              **argv)
00804 {
00805         struct bu_vls   matches;
00806 
00807         RT_CK_WDB_TCL(interp,wdbp);
00808 
00809         /* Verify that this wdb supports lookup operations
00810            (non-null dbip) */
00811         if (wdbp->dbip == 0) {
00812                 Tcl_AppendResult( interp, "this database does not support lookup operations" );
00813                 return TCL_ERROR;
00814         }
00815 
00816         bu_vls_init(&matches);
00817         for (++argv; *argv != NULL; ++argv) {
00818                 if (db_regexp_match_all(&matches, wdbp->dbip, *argv) > 0)
00819                         bu_vls_strcat(&matches, " ");
00820         }
00821         bu_vls_trimspace(&matches);
00822         Tcl_AppendResult(interp, bu_vls_addr(&matches), (char *)NULL);
00823         bu_vls_free(&matches);
00824         return TCL_OK;
00825 }
00826 
00827 /*
00828  *                      W D B _ M A T C H _ T C L
00829  *
00830  * Returns (in interp->result) a list (possibly empty) of all matches to
00831  * the (possibly wildcard-containing) arguments given.
00832  * Does *NOT* return tokens that do not match anything, unlike the
00833  * "expand" command.
00834  */
00835 
00836 static int
00837 wdb_match_tcl(ClientData        clientData,
00838               Tcl_Interp        *interp,
00839               int               argc,
00840               char              **argv)
00841 {
00842         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
00843 
00844         return wdb_match_cmd(wdbp, interp, argc-1, argv+1);
00845 }
00846 
00847 int
00848 wdb_get_cmd(struct rt_wdb       *wdbp,
00849             Tcl_Interp          *interp,
00850             int                 argc,
00851             char                **argv)
00852 {
00853         int                     status;
00854         struct rt_db_internal   intern;
00855 
00856         if (argc < 2 || argc > 3) {
00857                 struct bu_vls vls;
00858 
00859                 bu_vls_init(&vls);
00860                 bu_vls_printf(&vls, "helplib_alias wdb_get %s", argv[0]);
00861                 Tcl_Eval(interp, bu_vls_addr(&vls));
00862                 bu_vls_free(&vls);
00863                 return TCL_ERROR;
00864         }
00865 
00866         /* Verify that this wdb supports lookup operations
00867            (non-null dbip) */
00868         if (wdbp->dbip == 0) {
00869                 Tcl_AppendResult(interp,
00870                                  "db does not support lookup operations",
00871                                  (char *)NULL);
00872                 return TCL_ERROR;
00873         }
00874 
00875         if (rt_tcl_import_from_path(interp, &intern, argv[1], wdbp) == TCL_ERROR)
00876                 return TCL_ERROR;
00877 
00878         status = intern.idb_meth->ft_tclget(interp, &intern, argv[2]);
00879         rt_db_free_internal(&intern, &rt_uniresource);
00880         return status;
00881 }
00882 
00883 /*
00884  *                      W D B _ G E T_ T C L
00885  *
00886  **
00887  ** For use with Tcl, this routine accepts as its first argument the name
00888  ** of an object in the database.  If only one argument is given, this routine
00889  ** then fills the result string with the (minimal) attributes of the item.
00890  ** If a second, optional, argument is provided, this function looks up the
00891  ** property with that name of the item given, and returns it as the result
00892  ** string.
00893  **/
00894 /* NOTE: This is called directly by gdiff/g_diff.c */
00895 
00896 int
00897 wdb_get_tcl(ClientData  clientData,
00898             Tcl_Interp  *interp,
00899             int         argc,
00900             char        **argv)
00901 {
00902         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
00903 
00904         return wdb_get_cmd(wdbp, interp, argc-1, argv+1);
00905 }
00906 
00907 int
00908 wdb_get_type_cmd(struct rt_wdb  *wdbp,
00909                  Tcl_Interp     *interp,
00910                  int            argc,
00911                  char           **argv)
00912 {
00913     struct rt_db_internal       intern;
00914     int type;
00915 
00916     if (argc != 2) {
00917         struct bu_vls vls;
00918 
00919         bu_vls_init(&vls);
00920         bu_vls_printf(&vls, "helplib_alias wdb_get_type %s", argv[0]);
00921         Tcl_Eval(interp, bu_vls_addr(&vls));
00922         bu_vls_free(&vls);
00923 
00924         return TCL_ERROR;
00925     }
00926 
00927     if (wdbp->dbip == 0) {
00928         Tcl_AppendResult(interp,
00929                          "db does not support lookup operations",
00930                          (char *)NULL);
00931         return TCL_ERROR;
00932     }
00933 
00934     if (rt_tcl_import_from_path(interp, &intern, argv[1], wdbp) == TCL_ERROR)
00935         return TCL_ERROR;
00936 
00937     if (intern.idb_major_type != DB5_MAJORTYPE_BRLCAD) {
00938         Tcl_AppendResult(interp, "unknown", (char *)NULL);
00939         rt_db_free_internal(&intern, &rt_uniresource);
00940 
00941         return TCL_OK;
00942     }
00943 
00944     switch (intern.idb_minor_type) {
00945     case DB5_MINORTYPE_BRLCAD_TOR:
00946         Tcl_AppendResult(interp, "tor", (char *)NULL);
00947         break;
00948     case DB5_MINORTYPE_BRLCAD_TGC:
00949         Tcl_AppendResult(interp, "tgc", (char *)NULL);
00950         break;
00951     case DB5_MINORTYPE_BRLCAD_ELL:
00952         Tcl_AppendResult(interp, "ell", (char *)NULL);
00953         break;
00954     case DB5_MINORTYPE_BRLCAD_ARB8:
00955         type = rt_arb_std_type(&intern, &wdbp->wdb_tol);
00956 
00957         switch (type) {
00958         case 4:
00959             Tcl_AppendResult(interp, "arb4", (char *)NULL);
00960             break;
00961         case 5:
00962             Tcl_AppendResult(interp, "arb5", (char *)NULL);
00963             break;
00964         case 6:
00965             Tcl_AppendResult(interp, "arb6", (char *)NULL);
00966             break;
00967         case 7:
00968             Tcl_AppendResult(interp, "arb7", (char *)NULL);
00969             break;
00970         case 8:
00971             Tcl_AppendResult(interp, "arb8", (char *)NULL);
00972             break;
00973         default:
00974             Tcl_AppendResult(interp, "invalid", (char *)NULL);
00975             break;
00976         }
00977 
00978         break;
00979     case DB5_MINORTYPE_BRLCAD_ARS:
00980         Tcl_AppendResult(interp, "ars", (char *)NULL);
00981         break;
00982     case DB5_MINORTYPE_BRLCAD_HALF:
00983         Tcl_AppendResult(interp, "half", (char *)NULL);
00984         break;
00985     case DB5_MINORTYPE_BRLCAD_REC:
00986         Tcl_AppendResult(interp, "rec", (char *)NULL);
00987         break;
00988     case DB5_MINORTYPE_BRLCAD_POLY:
00989         Tcl_AppendResult(interp, "poly", (char *)NULL);
00990         break;
00991     case DB5_MINORTYPE_BRLCAD_BSPLINE:
00992         Tcl_AppendResult(interp, "spline", (char *)NULL);
00993         break;
00994     case DB5_MINORTYPE_BRLCAD_SPH:
00995         Tcl_AppendResult(interp, "sph", (char *)NULL);
00996         break;
00997     case DB5_MINORTYPE_BRLCAD_NMG:
00998         Tcl_AppendResult(interp, "nmg", (char *)NULL);
00999         break;
01000     case DB5_MINORTYPE_BRLCAD_EBM:
01001         Tcl_AppendResult(interp, "ebm", (char *)NULL);
01002         break;
01003     case DB5_MINORTYPE_BRLCAD_VOL:
01004         Tcl_AppendResult(interp, "vol", (char *)NULL);
01005         break;
01006     case DB5_MINORTYPE_BRLCAD_ARBN:
01007         Tcl_AppendResult(interp, "arbn", (char *)NULL);
01008         break;
01009     case DB5_MINORTYPE_BRLCAD_PIPE:
01010         Tcl_AppendResult(interp, "pipe", (char *)NULL);
01011         break;
01012     case DB5_MINORTYPE_BRLCAD_PARTICLE:
01013         Tcl_AppendResult(interp, "part", (char *)NULL);
01014         break;
01015     case DB5_MINORTYPE_BRLCAD_RPC:
01016         Tcl_AppendResult(interp, "rpc", (char *)NULL);
01017         break;
01018     case DB5_MINORTYPE_BRLCAD_RHC:
01019         Tcl_AppendResult(interp, "rhc", (char *)NULL);
01020         break;
01021     case DB5_MINORTYPE_BRLCAD_EPA:
01022         Tcl_AppendResult(interp, "epa", (char *)NULL);
01023         break;
01024     case DB5_MINORTYPE_BRLCAD_EHY:
01025         Tcl_AppendResult(interp, "ehy", (char *)NULL);
01026         break;
01027     case DB5_MINORTYPE_BRLCAD_ETO:
01028         Tcl_AppendResult(interp, "eto", (char *)NULL);
01029         break;
01030     case DB5_MINORTYPE_BRLCAD_GRIP:
01031         Tcl_AppendResult(interp, "grip", (char *)NULL);
01032         break;
01033     case DB5_MINORTYPE_BRLCAD_JOINT:
01034         Tcl_AppendResult(interp, "joint", (char *)NULL);
01035         break;
01036     case DB5_MINORTYPE_BRLCAD_HF:
01037         Tcl_AppendResult(interp, "hf", (char *)NULL);
01038         break;
01039     case DB5_MINORTYPE_BRLCAD_DSP:
01040         Tcl_AppendResult(interp, "dsp", (char *)NULL);
01041         break;
01042     case DB5_MINORTYPE_BRLCAD_SKETCH:
01043         Tcl_AppendResult(interp, "sketch", (char *)NULL);
01044         break;
01045     case DB5_MINORTYPE_BRLCAD_EXTRUDE:
01046         Tcl_AppendResult(interp, "extrude", (char *)NULL);
01047         break;
01048     case DB5_MINORTYPE_BRLCAD_SUBMODEL:
01049         Tcl_AppendResult(interp, "submodel", (char *)NULL);
01050         break;
01051     case DB5_MINORTYPE_BRLCAD_CLINE:
01052         Tcl_AppendResult(interp, "cline", (char *)NULL);
01053         break;
01054     case DB5_MINORTYPE_BRLCAD_BOT:
01055         Tcl_AppendResult(interp, "bot", (char *)NULL);
01056         break;
01057     case DB5_MINORTYPE_BRLCAD_COMBINATION:
01058         Tcl_AppendResult(interp, "comb", (char *)NULL);
01059         break;
01060     default:
01061         Tcl_AppendResult(interp, "other", (char *)NULL);
01062         break;
01063     }
01064 
01065     rt_db_free_internal(&intern, &rt_uniresource);
01066     return TCL_OK;
01067 }
01068 
01069 int
01070 wdb_get_type_tcl(ClientData     clientData,
01071                  Tcl_Interp     *interp,
01072                  int            argc,
01073                  char           **argv)
01074 {
01075     struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
01076 
01077     return wdb_get_type_cmd(wdbp, interp, argc-1, argv+1);
01078 }
01079 
01080 int
01081 wdb_put_cmd(struct rt_wdb       *wdbp,
01082             Tcl_Interp          *interp,
01083             int                 argc,
01084             char                **argv)
01085 {
01086         struct rt_db_internal                   intern;
01087         register const struct rt_functab        *ftp;
01088         int                                     i;
01089         char                                   *name;
01090         char                                    type[16];
01091 
01092         if (argc < 3) {
01093                 struct bu_vls vls;
01094 
01095                 bu_vls_init(&vls);
01096                 bu_vls_printf(&vls, "helplib_alias wdb_put %s", argv[0]);
01097                 Tcl_Eval(interp, bu_vls_addr(&vls));
01098                 bu_vls_free(&vls);
01099                 return TCL_ERROR;
01100         }
01101 
01102         name = argv[1];
01103 
01104         /* Verify that this wdb supports lookup operations (non-null dbip).
01105          * stdout/file wdb objects don't, but can still be written to.
01106          * If not, just skip the lookup test and write the object
01107          */
01108         if (wdbp->dbip && db_lookup(wdbp->dbip, argv[1], LOOKUP_QUIET) != DIR_NULL ) {
01109                 Tcl_AppendResult(interp, argv[1], " already exists",
01110                                  (char *)NULL);
01111                 return TCL_ERROR;
01112         }
01113 
01114         RT_INIT_DB_INTERNAL(&intern);
01115 
01116         for (i = 0; argv[2][i] != 0 && i < 16; i++) {
01117                 type[i] = isupper(argv[2][i]) ? tolower(argv[2][i]) :
01118                         argv[2][i];
01119         }
01120         type[i] = 0;
01121 
01122         ftp = rt_get_functab_by_label(type);
01123         if (ftp == NULL) {
01124                 Tcl_AppendResult(interp, type,
01125                                  " is an unknown object type.",
01126                                  (char *)NULL);
01127                 return TCL_ERROR;
01128         }
01129 
01130         RT_CK_FUNCTAB(ftp);
01131 
01132         if (ftp->ft_make) {
01133             if (ftp->ft_make == rt_nul_make) {
01134                 Tcl_AppendResult(interp, "wdb_put_internal(", argv[1],
01135                                  ") cannot put a ", type, (char *)NULL);
01136 
01137                 return TCL_ERROR;
01138             }
01139             ftp->ft_make(ftp, &intern, 0.0);
01140         } else {
01141             rt_generic_make(ftp, &intern, 0.0);
01142         }
01143 
01144         if (ftp->ft_tcladjust(interp, &intern, argc-3, argv+3, &rt_uniresource) == TCL_ERROR) {
01145                 rt_db_free_internal(&intern, &rt_uniresource);
01146                 return TCL_ERROR;
01147         }
01148 
01149         if (wdb_put_internal(wdbp, name, &intern, 1.0) < 0)  {
01150                 Tcl_AppendResult(interp, "wdb_put_internal(", argv[1],
01151                                  ") failure", (char *)NULL);
01152                 rt_db_free_internal(&intern, &rt_uniresource);
01153                 return TCL_ERROR;
01154         }
01155 
01156         rt_db_free_internal( &intern, &rt_uniresource );
01157         return TCL_OK;
01158 }
01159 
01160 /*
01161  *                      W D B _ P U T _ T C L
01162  **
01163  ** Creates an object and stuffs it into the databse.
01164  ** All arguments must be specified.  Object cannot already exist.
01165  **/
01166 
01167 static int
01168 wdb_put_tcl(ClientData  clientData,
01169             Tcl_Interp  *interp,
01170             int         argc,
01171             char        **argv)
01172 {
01173         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
01174 
01175         return wdb_put_cmd(wdbp, interp, argc-1, argv+1);
01176 }
01177 
01178 int
01179 wdb_adjust_cmd(struct rt_wdb    *wdbp,
01180                Tcl_Interp       *interp,
01181                int              argc,
01182                char             **argv)
01183 {
01184         register struct directory       *dp;
01185         int                              status;
01186         char                            *name;
01187         struct rt_db_internal            intern;
01188 
01189         if (argc < 4) {
01190                 struct bu_vls vls;
01191 
01192                 bu_vls_init(&vls);
01193                 bu_vls_printf(&vls, "helplib_alias wdb_adjust %s", argv[0]);
01194                 Tcl_Eval(interp, bu_vls_addr(&vls));
01195                 bu_vls_free(&vls);
01196                 return TCL_ERROR;
01197         }
01198         name = argv[1];
01199 
01200         /* Verify that this wdb supports lookup operations (non-null dbip) */
01201         RT_CK_DBI_TCL(interp,wdbp->dbip);
01202 
01203         dp = db_lookup(wdbp->dbip, name, LOOKUP_QUIET);
01204         if (dp == DIR_NULL) {
01205                 Tcl_AppendResult(interp, name, ": not found",
01206                                  (char *)NULL );
01207                 return TCL_ERROR;
01208         }
01209 
01210         status = rt_db_get_internal(&intern, dp, wdbp->dbip, (matp_t)NULL, &rt_uniresource);
01211         if (status < 0) {
01212                 Tcl_AppendResult(interp, "rt_db_get_internal(", name,
01213                                  ") failure", (char *)NULL );
01214                 return TCL_ERROR;
01215         }
01216         RT_CK_DB_INTERNAL(&intern);
01217 
01218         /* Find out what type of object we are dealing with and tweak it. */
01219         RT_CK_FUNCTAB(intern.idb_meth);
01220 
01221         status = intern.idb_meth->ft_tcladjust(interp, &intern, argc-2, argv+2, &rt_uniresource);
01222         if( status == TCL_OK && wdb_put_internal(wdbp, name, &intern, 1.0) < 0)  {
01223                 Tcl_AppendResult(interp, "wdb_export(", name,
01224                                  ") failure", (char *)NULL);
01225                 rt_db_free_internal(&intern, &rt_uniresource);
01226                 return TCL_ERROR;
01227         }
01228 
01229         /* notify observers */
01230         bu_observer_notify(interp, &wdbp->wdb_observers, bu_vls_addr(&wdbp->wdb_name));
01231 
01232         return status;
01233 }
01234 
01235 /*
01236  *                      W D B _ A D J U S T _ T C L
01237  *
01238  **
01239  ** For use with Tcl, this routine accepts as its first argument an item in
01240  ** the database; as its remaining arguments it takes the properties that
01241  ** need to be changed and their values.
01242  *
01243  *  Example of adjust operation on a solid:
01244  *      .inmem adjust LIGHT V { -46 -13 5 }
01245  *
01246  *  Example of adjust operation on a combination:
01247  *      .inmem adjust light.r rgb { 255 255 255 }
01248  */
01249 
01250 static int
01251 wdb_adjust_tcl(ClientData       clientData,
01252                Tcl_Interp       *interp,
01253                int              argc,
01254                char             **argv)
01255 {
01256         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
01257 
01258         return wdb_adjust_cmd(wdbp, interp, argc-1, argv+1);
01259 }
01260 
01261 int
01262 wdb_form_cmd(struct rt_wdb      *wdbp,
01263              Tcl_Interp         *interp,
01264              int                argc,
01265              char               **argv)
01266 {
01267         const struct rt_functab         *ftp;
01268 
01269         if (argc != 2) {
01270                 struct bu_vls vls;
01271 
01272                 bu_vls_init(&vls);
01273                 bu_vls_printf(&vls, "helplib_alias wdb_form %s", argv[0]);
01274                 Tcl_Eval(interp, bu_vls_addr(&vls));
01275                 bu_vls_free(&vls);
01276                 return TCL_ERROR;
01277         }
01278 
01279         if ((ftp = rt_get_functab_by_label(argv[1])) == NULL) {
01280                 Tcl_AppendResult(interp, "There is no geometric object type \"",
01281                                  argv[1], "\".", (char *)NULL);
01282                 return TCL_ERROR;
01283         }
01284         return ftp->ft_tclform(ftp, interp);
01285 }
01286 
01287 /*
01288  *                      W D B _ F O R M _ T C L
01289  */
01290 static int
01291 wdb_form_tcl(ClientData clientData,
01292              Tcl_Interp *interp,
01293              int        argc,
01294              char       **argv)
01295 {
01296         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
01297 
01298         return wdb_form_cmd(wdbp, interp, argc-1, argv+1);
01299 }
01300 
01301 int
01302 wdb_tops_cmd(struct rt_wdb      *wdbp,
01303              Tcl_Interp         *interp,
01304              int                argc,
01305              char               **argv)
01306 {
01307         register struct directory       *dp;
01308         register int                    i;
01309         struct directory                **dirp;
01310         struct directory                **dirp0 = (struct directory **)NULL;
01311         struct bu_vls                   vls;
01312         int                             c;
01313 #ifdef NEW_TOPS_BEHAVIOR
01314         int                             aflag = 0;
01315         int                             hflag = 0;
01316         int                             pflag = 0;
01317 #else
01318         int                             gflag = 0;
01319         int                             uflag = 0;
01320 #endif
01321         int                             no_decorate = 0;
01322 
01323         RT_CK_WDB_TCL(interp, wdbp);
01324         RT_CK_DBI_TCL(interp, wdbp->dbip);
01325 
01326         /* process any options */
01327         bu_optind = 1;  /* re-init bu_getopt() */
01328 #ifdef NEW_TOPS_BEHAVIOR
01329         while ((c = bu_getopt(argc, argv, "ahnp")) != EOF) {
01330                 switch (c) {
01331                 case 'a':
01332                         aflag = 1;
01333                         break;
01334                 case 'h':
01335                         hflag = 1;
01336                         break;
01337                 case 'n':
01338                         no_decorate = 1;
01339                         break;
01340                 case 'p':
01341                         pflag = 1;
01342                         break;
01343                 default:
01344                         break;
01345                 }
01346         }
01347 #else
01348         while ((c = bu_getopt(argc, argv, "gun")) != EOF) {
01349                 switch (c) {
01350                 case 'g':
01351                         gflag = 1;
01352                         break;
01353                 case 'u':
01354                         uflag = 1;
01355                         break;
01356                 case 'n':
01357                         no_decorate = 1;
01358                         break;
01359                 default:
01360                         break;
01361                 }
01362         }
01363 #endif
01364 
01365         argc -= (bu_optind - 1);
01366         argv += (bu_optind - 1);
01367 
01368         /* Can this be executed only sometimes?
01369            Perhaps a "dirty bit" on the database? */
01370         db_update_nref(wdbp->dbip, &rt_uniresource);
01371 
01372         /*
01373          * Find number of possible entries and allocate memory
01374          */
01375         dirp = wdb_dir_getspace(wdbp->dbip, 0);
01376         dirp0 = dirp;
01377 
01378         if (wdbp->dbip->dbi_version < 5) {
01379                 for (i = 0; i < RT_DBNHASH; i++)
01380                         for (dp = wdbp->dbip->dbi_Head[i];
01381                              dp != DIR_NULL;
01382                              dp = dp->d_forw)  {
01383                                 if (dp->d_nref == 0)
01384                                         *dirp++ = dp;
01385                         }
01386         } else {
01387                 for (i = 0; i < RT_DBNHASH; i++)
01388                         for (dp = wdbp->dbip->dbi_Head[i];
01389                              dp != DIR_NULL;
01390                              dp = dp->d_forw)  {
01391 #ifdef NEW_TOPS_BEHAVIOR
01392                                 if (dp->d_nref == 0 &&
01393                                     (aflag ||
01394                                      (hflag && (dp->d_flags & DIR_HIDDEN)) ||
01395                                      (pflag && dp->d_addr == RT_DIR_PHONY_ADDR) ||
01396                                      (!aflag && !hflag && !pflag &&
01397                                       !(dp->d_flags & DIR_HIDDEN) &&
01398                                       (dp->d_addr != RT_DIR_PHONY_ADDR))))
01399                                         *dirp++ = dp;
01400 #else
01401                                 if (dp->d_nref == 0 &&
01402                                     ((!gflag || (gflag && dp->d_major_type == DB5_MAJORTYPE_BRLCAD)) &&
01403                                      (!uflag || (uflag && !(dp->d_flags & DIR_HIDDEN)))))
01404                                         *dirp++ = dp;
01405 #endif
01406                         }
01407         }
01408 
01409         bu_vls_init(&vls);
01410         wdb_vls_col_pr4v(&vls, dirp0, (int)(dirp - dirp0), no_decorate);
01411         Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)0);
01412         bu_vls_free(&vls);
01413         bu_free((genptr_t)dirp0, "wdb_tops_cmd: wdb_dir_getspace");
01414 
01415         return TCL_OK;
01416 }
01417 
01418 /*
01419  *                      W D B _ T O P S _ T C L
01420  *
01421  *  NON-PARALLEL because of rt_uniresource
01422  */
01423 static int
01424 wdb_tops_tcl(ClientData clientData,
01425              Tcl_Interp *interp,
01426              int        argc,
01427              char       **argv)
01428 {
01429         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
01430 
01431         return wdb_tops_cmd(wdbp, interp, argc-1, argv+1);
01432 }
01433 
01434 /*
01435  *                      R T _ T C L _ D E L E T E P R O C _ R T
01436  *
01437  *  Called when the named proc created by rt_gettrees() is destroyed.
01438  */
01439 static void
01440 wdb_deleteProc_rt(ClientData clientData)
01441 {
01442         struct application      *ap = (struct application *)clientData;
01443         struct rt_i             *rtip;
01444 
01445         RT_AP_CHECK(ap);
01446         rtip = ap->a_rt_i;
01447         RT_CK_RTI(rtip);
01448 
01449         rt_free_rti(rtip);
01450         ap->a_rt_i = (struct rt_i *)NULL;
01451 
01452         bu_free( (genptr_t)ap, "struct application" );
01453 }
01454 
01455 int
01456 wdb_rt_gettrees_cmd(struct rt_wdb       *wdbp,
01457                     Tcl_Interp          *interp,
01458                     int                 argc,
01459                     char                **argv)
01460 {
01461         struct rt_i             *rtip;
01462         struct application      *ap;
01463         struct resource         *resp;
01464         char                    *newprocname;
01465 
01466         RT_CK_WDB_TCL(interp, wdbp);
01467         RT_CK_DBI_TCL(interp, wdbp->dbip);
01468 
01469         if (argc < 3) {
01470                 struct bu_vls vls;
01471 
01472                 bu_vls_init(&vls);
01473                 bu_vls_printf(&vls, "helplib_alias wdb_rt_gettrees %s", argv[0]);
01474                 Tcl_Eval(interp, bu_vls_addr(&vls));
01475                 bu_vls_free(&vls);
01476                 return TCL_ERROR;
01477         }
01478 
01479         rtip = rt_new_rti(wdbp->dbip);
01480         newprocname = argv[1];
01481 
01482         /* Delete previous proc (if any) to release all that memory, first */
01483         (void)Tcl_DeleteCommand(interp, newprocname);
01484 
01485         while (argv[2][0] == '-') {
01486                 if (strcmp( argv[2], "-i") == 0) {
01487                         rtip->rti_dont_instance = 1;
01488                         argc--;
01489                         argv++;
01490                         continue;
01491                 }
01492                 if (strcmp(argv[2], "-u") == 0) {
01493                         rtip->useair = 1;
01494                         argc--;
01495                         argv++;
01496                         continue;
01497                 }
01498                 break;
01499         }
01500 
01501         if (rt_gettrees(rtip, argc-2, (const char **)&argv[2], 1) < 0) {
01502                 Tcl_AppendResult(interp,
01503                                  "rt_gettrees() returned error", (char *)NULL);
01504                 rt_free_rti(rtip);
01505                 return TCL_ERROR;
01506         }
01507 
01508         /* Establish defaults for this rt_i */
01509         rtip->rti_hasty_prep = 1;       /* Tcl isn't going to fire many rays */
01510 
01511         /*
01512          *  In case of multiple instances of the library, make sure that
01513          *  each instance has a separate resource structure,
01514          *  because the bit vector lengths depend on # of solids.
01515          *  And the "overwrite" sequence in Tcl is to create the new
01516          *  proc before running the Tcl_CmdDeleteProc on the old one,
01517          *  which in this case would trash rt_uniresource.
01518          *  Once on the rti_resources list, rt_clean() will clean 'em up.
01519          */
01520         BU_GETSTRUCT(resp, resource);
01521         rt_init_resource(resp, 0, rtip);
01522         BU_ASSERT_PTR( BU_PTBL_GET(&rtip->rti_resources, 0), !=, NULL );
01523 
01524         ap = (struct application *)bu_malloc(sizeof(struct application), "wdb_rt_gettrees_cmd: ap");
01525         RT_APPLICATION_INIT(ap);
01526         ap->a_magic = RT_AP_MAGIC;
01527         ap->a_resource = resp;
01528         ap->a_rt_i = rtip;
01529         ap->a_purpose = "Conquest!";
01530 
01531         rt_ck(rtip);
01532 
01533         /* Instantiate the proc, with clientData of wdb */
01534         /* Beware, returns a "token", not TCL_OK. */
01535         (void)Tcl_CreateCommand(interp, newprocname, rt_tcl_rt,
01536                                 (ClientData)ap, wdb_deleteProc_rt);
01537 
01538         /* Return new function name as result */
01539         Tcl_AppendResult(interp, newprocname, (char *)NULL);
01540 
01541         return TCL_OK;
01542 }
01543 
01544 /*
01545  *                      W D B _ R T _ G E T T R E E S _ T C L
01546  *
01547  *  Given an instance of a database and the name of some treetops,
01548  *  create a named "ray-tracing" object (proc) which will respond to
01549  *  subsequent operations.
01550  *  Returns new proc name as result.
01551  *
01552  *  Example:
01553  *      .inmem rt_gettrees .rt all.g light.r
01554  */
01555 static int
01556 wdb_rt_gettrees_tcl(ClientData  clientData,
01557                     Tcl_Interp     *interp,
01558                     int         argc,
01559                     char              **argv)
01560 {
01561         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
01562 
01563         return wdb_rt_gettrees_cmd(wdbp, interp, argc-1, argv+1);
01564 }
01565 
01566 struct showmats_data {
01567         Tcl_Interp      *smd_interp;
01568         int             smd_count;
01569         char            *smd_child;
01570         mat_t           smd_mat;
01571 };
01572 
01573 static void
01574 Do_showmats(struct db_i                 *dbip,
01575             struct rt_comb_internal     *comb,
01576             union tree                  *comb_leaf,
01577             genptr_t                    user_ptr1,
01578             genptr_t                    user_ptr2,
01579             genptr_t                    user_ptr3)
01580 {
01581         struct showmats_data    *smdp;
01582 
01583         RT_CK_DBI(dbip);
01584         RT_CK_TREE(comb_leaf);
01585 
01586         smdp = (struct showmats_data *)user_ptr1;
01587 
01588         if (strcmp(comb_leaf->tr_l.tl_name, smdp->smd_child))
01589                 return;
01590 
01591         smdp->smd_count++;
01592         if (smdp->smd_count > 1) {
01593                 struct bu_vls vls;
01594 
01595                 bu_vls_init(&vls);
01596                 bu_vls_printf(&vls, "\n\tOccurrence #%d:\n", smdp->smd_count);
01597                 Tcl_AppendResult(smdp->smd_interp, bu_vls_addr(&vls), (char *)NULL);
01598                 bu_vls_free(&vls);
01599         }
01600 
01601         bn_tcl_mat_print(smdp->smd_interp, "", comb_leaf->tr_l.tl_mat);
01602         if (smdp->smd_count == 1) {
01603                 mat_t tmp_mat;
01604                 if (comb_leaf->tr_l.tl_mat) {
01605                         bn_mat_mul(tmp_mat, smdp->smd_mat, comb_leaf->tr_l.tl_mat);
01606                         MAT_COPY(smdp->smd_mat, tmp_mat);
01607                 }
01608         }
01609 }
01610 
01611 int
01612 wdb_showmats_cmd(struct rt_wdb  *wdbp,
01613                  Tcl_Interp     *interp,
01614                  int            argc,
01615                  char           **argv)
01616 {
01617         struct showmats_data sm_data;
01618         char *parent;
01619         struct directory *dp;
01620         int max_count=1;
01621 
01622         if (argc != 2) {
01623                 struct bu_vls vls;
01624 
01625                 bu_vls_init(&vls);
01626                 bu_vls_printf(&vls, "helplib_alias wdb_showmats %s", argv[0]);
01627                 Tcl_Eval(interp, bu_vls_addr(&vls));
01628                 bu_vls_free(&vls);
01629                 return TCL_ERROR;
01630         }
01631 
01632         sm_data.smd_interp = interp;
01633         MAT_IDN(sm_data.smd_mat);
01634 
01635         parent = strtok(argv[1], "/");
01636         while ((sm_data.smd_child = strtok((char *)NULL, "/")) != NULL) {
01637                 struct rt_db_internal   intern;
01638                 struct rt_comb_internal *comb;
01639 
01640                 if ((dp = db_lookup(wdbp->dbip, parent, LOOKUP_NOISY)) == DIR_NULL)
01641                         return TCL_ERROR;
01642 
01643                 Tcl_AppendResult(interp, parent, "\n", (char *)NULL);
01644 
01645                 if (!(dp->d_flags & DIR_COMB)) {
01646                         Tcl_AppendResult(interp, "\tThis is not a combination\n", (char *)NULL);
01647                         break;
01648                 }
01649 
01650                 if (rt_db_get_internal(&intern, dp, wdbp->dbip, (fastf_t *)NULL, &rt_uniresource) < 0)
01651                         WDB_TCL_READ_ERR_return;
01652                 comb = (struct rt_comb_internal *)intern.idb_ptr;
01653 
01654                 sm_data.smd_count = 0;
01655 
01656                 if (comb->tree)
01657                         db_tree_funcleaf(wdbp->dbip, comb, comb->tree, Do_showmats,
01658                                          (genptr_t)&sm_data, (genptr_t)NULL, (genptr_t)NULL);
01659 #if USE_RT_COMB_IFREE
01660                 rt_comb_ifree(&intern, &rt_uniresource);
01661 #else
01662                 rt_db_free_internal(&intern, &rt_uniresource);
01663 #endif
01664 
01665                 if (!sm_data.smd_count) {
01666                         Tcl_AppendResult(interp, sm_data.smd_child, " is not a member of ",
01667                                          parent, "\n", (char *)NULL);
01668                         return TCL_ERROR;
01669                 }
01670                 if (sm_data.smd_count > max_count)
01671                         max_count = sm_data.smd_count;
01672 
01673                 parent = sm_data.smd_child;
01674         }
01675         Tcl_AppendResult(interp, parent, "\n", (char *)NULL);
01676 
01677         if (max_count > 1)
01678                 Tcl_AppendResult(interp, "\nAccumulated matrix (using first occurrence of each object):\n", (char *)NULL);
01679         else
01680                 Tcl_AppendResult(interp, "\nAccumulated matrix:\n", (char *)NULL);
01681 
01682         bn_tcl_mat_print(interp, "", sm_data.smd_mat);
01683 
01684         return TCL_OK;
01685 }
01686 
01687 static int
01688 wdb_showmats_tcl(ClientData     clientData,
01689                  Tcl_Interp     *interp,
01690                  int            argc,
01691                  char           **argv)
01692 {
01693         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
01694 
01695         return wdb_showmats_cmd(wdbp, interp, argc-1, argv+1);
01696 }
01697 
01698 int
01699 wdb_shells_cmd(struct rt_wdb    *wdbp,
01700              Tcl_Interp         *interp,
01701              int                argc,
01702              char               **argv)
01703 {
01704         struct directory *old_dp,*new_dp;
01705         struct rt_db_internal old_intern,new_intern;
01706         struct model *m_tmp,*m;
01707         struct nmgregion *r_tmp,*r;
01708         struct shell *s_tmp,*s;
01709         int shell_count=0;
01710         struct bu_vls shell_name;
01711         long **trans_tbl;
01712 
01713         WDB_TCL_CHECK_READ_ONLY;
01714 
01715         if (argc != 2) {
01716                 struct bu_vls vls;
01717 
01718                 bu_vls_init(&vls);
01719                 bu_vls_printf(&vls, "helplib_alias wdb_shells %s", argv[0]);
01720                 Tcl_Eval(interp, bu_vls_addr(&vls));
01721                 bu_vls_free(&vls);
01722                 return TCL_ERROR;
01723         }
01724 
01725         if ((old_dp = db_lookup(wdbp->dbip,  argv[1], LOOKUP_NOISY)) == DIR_NULL)
01726                 return TCL_ERROR;
01727 
01728         if (rt_db_get_internal(&old_intern, old_dp, wdbp->dbip, bn_mat_identity, &rt_uniresource) < 0) {
01729                 Tcl_AppendResult(interp, "rt_db_get_internal() error\n", (char *)NULL);
01730                 return TCL_ERROR;
01731         }
01732 
01733         if (old_intern.idb_type != ID_NMG) {
01734                 Tcl_AppendResult(interp, "Object is not an NMG!!!\n", (char *)NULL);
01735                 return TCL_ERROR;
01736         }
01737 
01738         m = (struct model *)old_intern.idb_ptr;
01739         NMG_CK_MODEL(m);
01740 
01741         bu_vls_init(&shell_name);
01742         for (BU_LIST_FOR(r, nmgregion, &m->r_hd)) {
01743                 for (BU_LIST_FOR(s, shell, &r->s_hd)) {
01744                         s_tmp = nmg_dup_shell(s, &trans_tbl, &wdbp->wdb_tol);
01745                         bu_free((genptr_t)trans_tbl, "trans_tbl");
01746 
01747                         m_tmp = nmg_mmr();
01748                         r_tmp = BU_LIST_FIRST(nmgregion, &m_tmp->r_hd);
01749 
01750                         BU_LIST_DEQUEUE(&s_tmp->l);
01751                         BU_LIST_APPEND(&r_tmp->s_hd, &s_tmp->l);
01752                         s_tmp->r_p = r_tmp;
01753                         nmg_m_reindex(m_tmp, 0);
01754                         nmg_m_reindex(m, 0);
01755 
01756                         bu_vls_printf(&shell_name, "shell.%d", shell_count);
01757                         while (db_lookup(wdbp->dbip, bu_vls_addr( &shell_name), 0) != DIR_NULL) {
01758                                 bu_vls_trunc(&shell_name, 0);
01759                                 shell_count++;
01760                                 bu_vls_printf(&shell_name, "shell.%d", shell_count);
01761                         }
01762 
01763                         /* Export NMG as a new solid */
01764                         RT_INIT_DB_INTERNAL(&new_intern);
01765                         new_intern.idb_major_type = DB5_MAJORTYPE_BRLCAD;
01766                         new_intern.idb_type = ID_NMG;
01767                         new_intern.idb_meth = &rt_functab[ID_NMG];
01768                         new_intern.idb_ptr = (genptr_t)m_tmp;
01769 
01770                         if ((new_dp=db_diradd(wdbp->dbip, bu_vls_addr(&shell_name), -1, 0,
01771                                               DIR_SOLID, (genptr_t)&new_intern.idb_type)) == DIR_NULL) {
01772                                 WDB_TCL_ALLOC_ERR_return;
01773                         }
01774 
01775                         /* make sure the geometry/bounding boxes are up to date */
01776                         nmg_rebound(m_tmp, &wdbp->wdb_tol);
01777 
01778 
01779                         if (rt_db_put_internal(new_dp, wdbp->dbip, &new_intern, &rt_uniresource) < 0) {
01780                                 /* Free memory */
01781                                 nmg_km(m_tmp);
01782                                 Tcl_AppendResult(interp, "rt_db_put_internal() failure\n", (char *)NULL);
01783                                 return TCL_ERROR;
01784                         }
01785                         /* Internal representation has been freed by rt_db_put_internal */
01786                         new_intern.idb_ptr = (genptr_t)NULL;
01787                 }
01788         }
01789         bu_vls_free(&shell_name);
01790 
01791         return TCL_OK;
01792 }
01793 
01794 static int
01795 wdb_shells_tcl(ClientData       clientData,
01796                Tcl_Interp       *interp,
01797                int              argc,
01798                char             **argv)
01799 {
01800         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
01801 
01802         return wdb_shells_cmd(wdbp, interp, argc-1, argv+1);
01803 }
01804 
01805 int
01806 wdb_dump_cmd(struct rt_wdb      *wdbp,
01807              Tcl_Interp         *interp,
01808              int                argc,
01809              char               **argv)
01810 {
01811         struct rt_wdb   *op;
01812         int             ret;
01813 
01814         RT_CK_WDB_TCL(interp, wdbp);
01815         RT_CK_DBI_TCL(interp, wdbp->dbip);
01816 
01817         if (argc != 2) {
01818                 struct bu_vls vls;
01819 
01820                 bu_vls_init(&vls);
01821                 bu_vls_printf(&vls, "helplib_alias wdb_dump %s", argv[0]);
01822                 Tcl_Eval(interp, bu_vls_addr(&vls));
01823                 bu_vls_free(&vls);
01824                 return TCL_ERROR;
01825         }
01826 
01827         if ((op = wdb_fopen(argv[1])) == RT_WDB_NULL) {
01828                 Tcl_AppendResult(interp, "dump:  ", argv[1],
01829                                  ": cannot create", (char *)NULL);
01830                 return TCL_ERROR;
01831         }
01832 
01833         ret = db_dump(op, wdbp->dbip);
01834         wdb_close(op);
01835 
01836         if (ret < 0) {
01837                 Tcl_AppendResult(interp, "dump ", argv[1],
01838                                  ": db_dump() error", (char *)NULL);
01839                 return TCL_ERROR;
01840         }
01841 
01842         return TCL_OK;
01843 }
01844 
01845 /*
01846  *                      W D B _ D U M P _ T C L
01847  *
01848  *  Write the current state of a database object out to a file.
01849  *
01850  *  Example:
01851  *      .inmem dump "/tmp/foo.g"
01852  */
01853 static int
01854 wdb_dump_tcl(ClientData clientData,
01855              Tcl_Interp *interp,
01856              int        argc,
01857              char       **argv)
01858 {
01859         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
01860 
01861         return wdb_dump_cmd(wdbp, interp, argc-1, argv+1);
01862 }
01863 
01864 int
01865 wdb_dbip_cmd(struct rt_wdb      *wdbp,
01866              Tcl_Interp         *interp,
01867              int                argc,
01868              char               **argv)
01869 {
01870         struct bu_vls vls;
01871 
01872         bu_vls_init(&vls);
01873 
01874         if (argc != 1) {
01875                 bu_vls_printf(&vls, "helplib_alias wdb_dbip %s", argv[0]);
01876                 Tcl_Eval(interp, bu_vls_addr(&vls));
01877                 bu_vls_free(&vls);
01878                 return TCL_ERROR;
01879         }
01880 
01881         bu_vls_printf(&vls, "%lu", (unsigned long)wdbp->dbip);
01882         Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
01883         bu_vls_free(&vls);
01884         return TCL_OK;
01885 }
01886 
01887 /*
01888  *
01889  * Usage:
01890  *        procname dbip
01891  *
01892  * Returns: database objects dbip.
01893  */
01894 static int
01895 wdb_dbip_tcl(ClientData clientData,
01896              Tcl_Interp *interp,
01897              int        argc,
01898              char       **argv)
01899 {
01900         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
01901 
01902         return wdb_dbip_cmd(wdbp, interp, argc-1, argv+1);
01903 }
01904 
01905 int
01906 wdb_ls_cmd(struct rt_wdb        *wdbp,
01907            Tcl_Interp           *interp,
01908            int                  argc,
01909            char                 **argv)
01910 {
01911         struct bu_vls vls;
01912         register struct directory *dp;
01913         register int i;
01914         int c;
01915         int aflag = 0;          /* print all objects without formatting */
01916         int cflag = 0;          /* print combinations */
01917         int rflag = 0;          /* print regions */
01918         int sflag = 0;          /* print solids */
01919         int lflag = 0;          /* use long format */
01920         int attr_flag = 0;      /* arguments are attribute name/value pairs */
01921         int or_flag = 0;        /* flag indicating that any one attribute match is sufficient
01922                                  * default is all attributes must match.
01923                                  */
01924         struct directory **dirp;
01925         struct directory **dirp0 = (struct directory **)NULL;
01926 
01927         bu_vls_init(&vls);
01928 
01929         if (argc < 1 || MAXARGS < argc) {
01930                 bu_vls_printf(&vls, "helplib_alias wdb_ls %s", argv[0]);
01931                 Tcl_Eval(interp, bu_vls_addr(&vls));
01932                 bu_vls_free(&vls);
01933                 return TCL_ERROR;
01934         }
01935 
01936         bu_optind = 1;  /* re-init bu_getopt() */
01937         while ((c = bu_getopt(argc, argv, "acrslpAo")) != EOF) {
01938                 switch (c) {
01939                 case 'A':
01940                         attr_flag = 1;
01941                         break;
01942                 case 'o':
01943                         or_flag = 1;
01944                         break;
01945                 case 'a':
01946                         aflag = 1;
01947                         break;
01948                 case 'c':
01949                         cflag = 1;
01950                         break;
01951                 case 'r':
01952                         rflag = 1;
01953                         break;
01954                 case 's':
01955                 case 'p':
01956                         sflag = 1;
01957                         break;
01958                 case 'l':
01959                         lflag = 1;
01960                         break;
01961                 default:
01962                         bu_vls_printf(&vls, "Unrecognized option - %c", c);
01963                         Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
01964                         bu_vls_free(&vls);
01965                         return TCL_ERROR;
01966                 }
01967         }
01968         argc -= (bu_optind - 1);
01969         argv += (bu_optind - 1);
01970 
01971         /* create list of selected objects from database */
01972         if( attr_flag ) {
01973                 /* select objects based on attributes */
01974                 struct bu_ptbl *tbl;
01975                 struct bu_attribute_value_set avs;
01976                 int dir_flags;
01977                 int op;
01978 
01979                 if( argc < 3 || argc%2 != 1 ) {
01980                         /* should be odd number of args name/value pairs plus argv[0] */
01981                         bu_vls_printf(&vls, "helplib_alias wdb_ls %s", argv[0]);
01982                         Tcl_Eval(interp, bu_vls_addr(&vls));
01983                         bu_vls_free(&vls);
01984                         return TCL_ERROR;
01985                 }
01986 
01987                 if( or_flag ) {
01988                         op = 2;
01989                 } else {
01990                         op = 1;
01991                 }
01992 
01993                 dir_flags = 0;
01994                 if( aflag ) dir_flags = -1;
01995                 if( cflag ) dir_flags = DIR_COMB;
01996                 if( sflag ) dir_flags = DIR_SOLID;
01997                 if( rflag ) dir_flags = DIR_REGION;
01998                 if( !dir_flags ) dir_flags = -1 ^ DIR_HIDDEN;
01999 
02000                 bu_avs_init( &avs, argc-1, "wdb_ls_cmd avs" );
02001                 for (i = 1; i < argc; i += 2) {
02002                         if( or_flag ) {
02003                                 bu_avs_add_nonunique( &avs, argv[i], argv[i+1] );
02004                         } else {
02005                                 bu_avs_add( &avs, argv[i], argv[i+1] );
02006                         }
02007                 }
02008                 tbl = db_lookup_by_attr( wdbp->dbip, dir_flags, &avs, op );
02009                 bu_avs_free( &avs );
02010                 dirp = wdb_getspace(wdbp->dbip, BU_PTBL_LEN( tbl ));
02011                 dirp0 = dirp;
02012                 for( i=0 ; i<BU_PTBL_LEN( tbl ) ; i++ ) {
02013                         *dirp++ = (struct directory *)BU_PTBL_GET( tbl, i );
02014                 }
02015                 bu_ptbl_free( tbl );
02016                 bu_free( (char *)tbl, "wdb_ls_cmd ptbl" );
02017         } else if (argc > 1) {
02018                 /* Just list specified names */
02019                 dirp = wdb_getspace(wdbp->dbip, argc-1);
02020                 dirp0 = dirp;
02021                 /*
02022                  * Verify the names, and add pointers to them to the array.
02023                  */
02024                 for (i = 1; i < argc; i++) {
02025                         if ((dp = db_lookup(wdbp->dbip, argv[i], LOOKUP_NOISY)) == DIR_NULL)
02026                                 continue;
02027                         *dirp++ = dp;
02028                 }
02029         } else {
02030                 /* Full table of contents */
02031                 dirp = wdb_getspace(wdbp->dbip, 0);     /* Enough for all */
02032                 dirp0 = dirp;
02033                 /*
02034                  * Walk the directory list adding pointers (to the directory
02035                  * entries) to the array.
02036                  */
02037                 for (i = 0; i < RT_DBNHASH; i++)
02038                         for (dp = wdbp->dbip->dbi_Head[i]; dp != DIR_NULL; dp = dp->d_forw) {
02039                                 if( !aflag && (dp->d_flags & DIR_HIDDEN) )
02040                                         continue;
02041                                 *dirp++ = dp;
02042                         }
02043         }
02044 
02045         if (lflag)
02046                 wdb_vls_long_dpp(&vls, dirp0, (int)(dirp - dirp0),
02047                                  aflag, cflag, rflag, sflag);
02048         else if (aflag || cflag || rflag || sflag)
02049                 wdb_vls_line_dpp(&vls, dirp0, (int)(dirp - dirp0),
02050                                  aflag, cflag, rflag, sflag);
02051         else
02052                 wdb_vls_col_pr4v(&vls, dirp0, (int)(dirp - dirp0), 0);
02053 
02054         Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
02055         bu_vls_free(&vls);
02056         bu_free((genptr_t)dirp0, "wdb_getspace dp[]");
02057 
02058         return TCL_OK;
02059 }
02060 
02061 /*
02062  *
02063  * Usage:
02064  *        procname ls [args]
02065  *
02066  * Returns: list objects in this database object.
02067  */
02068 static int
02069 wdb_ls_tcl(ClientData   clientData,
02070            Tcl_Interp   *interp,
02071            int          argc,
02072            char         **argv)
02073 {
02074         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
02075 
02076         return wdb_ls_cmd(wdbp, interp, argc-1, argv+1);
02077 }
02078 
02079 int
02080 wdb_list_cmd(struct rt_wdb      *wdbp,
02081              Tcl_Interp         *interp,
02082              int                argc,
02083              char               **argv)
02084 {
02085         register struct directory       *dp;
02086         register int                    arg;
02087         struct bu_vls                   str;
02088         int                             id;
02089         int                             recurse = 0;
02090         char                            *listeval = "listeval";
02091         struct rt_db_internal           intern;
02092 
02093         if (argc < 2 || MAXARGS < argc) {
02094                 struct bu_vls vls;
02095 
02096                 bu_vls_init(&vls);
02097                 bu_vls_printf(&vls, "helplib_alias wdb_list %s", argv[0]);
02098                 Tcl_Eval(interp, bu_vls_addr(&vls));
02099                 bu_vls_free(&vls);
02100                 return TCL_ERROR;
02101         }
02102 
02103         if (argc > 1 && strcmp(argv[1], "-r") == 0) {
02104                 recurse = 1;
02105 
02106                 /* skip past used args */
02107                 --argc;
02108                 ++argv;
02109         }
02110 
02111         /* skip past used args */
02112         --argc;
02113         ++argv;
02114 
02115         bu_vls_init(&str);
02116 
02117         for (arg = 0; arg < argc; arg++) {
02118                 if (recurse) {
02119                         char *tmp_argv[3];
02120 
02121                         tmp_argv[0] = listeval;
02122                         tmp_argv[1] = argv[arg];
02123                         tmp_argv[2] = (char *)NULL;
02124 
02125                         wdb_pathsum_cmd(wdbp, interp, 2, tmp_argv);
02126                 } else if (strchr(argv[arg], '/')) {
02127                         struct db_tree_state ts;
02128                         struct db_full_path path;
02129 
02130                         db_full_path_init( &path );
02131                         ts = wdbp->wdb_initial_tree_state;     /* struct copy */
02132                         ts.ts_dbip = wdbp->dbip;
02133                         ts.ts_resp = &rt_uniresource;
02134                         MAT_IDN(ts.ts_mat);
02135 
02136                         if (db_follow_path_for_state(&ts, &path, argv[arg], 1))
02137                                 continue;
02138 
02139                         dp = DB_FULL_PATH_CUR_DIR( &path );
02140 
02141                         if ((id = rt_db_get_internal(&intern, dp, wdbp->dbip, ts.ts_mat, &rt_uniresource)) < 0) {
02142                                 Tcl_AppendResult(interp, "rt_db_get_internal(", dp->d_namep,
02143                                                  ") failure", (char *)NULL );
02144                                 continue;
02145                         }
02146 
02147                         db_free_full_path( &path );
02148 
02149                         bu_vls_printf( &str, "%s:  ", argv[arg] );
02150 
02151                         if (rt_functab[id].ft_describe(&str, &intern, 99, wdbp->dbip->dbi_base2local, &rt_uniresource, wdbp->dbip) < 0)
02152                                 Tcl_AppendResult(interp, dp->d_namep, ": describe error", (char *)NULL);
02153 
02154                         rt_db_free_internal(&intern, &rt_uniresource);
02155                 } else {
02156                         if ((dp = db_lookup(wdbp->dbip, argv[arg], LOOKUP_NOISY)) == DIR_NULL)
02157                                 continue;
02158 
02159                         wdb_do_list(wdbp->dbip, interp, &str, dp, 99);  /* very verbose */
02160                 }
02161         }
02162 
02163         Tcl_AppendResult(interp, bu_vls_addr(&str), (char *)NULL);
02164         bu_vls_free(&str);
02165 
02166         return TCL_OK;
02167 }
02168 
02169 /*
02170  *
02171  *  Usage:
02172  *        procname l [-r] arg(s)
02173  *
02174  *  List object information, verbose.
02175  */
02176 static int
02177 wdb_list_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv)
02178 {
02179         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
02180 
02181         return wdb_list_cmd(wdbp, interp, argc-1, argv+1);
02182 }
02183 
02184 static void
02185 wdb_do_trace(struct db_i                *dbip,
02186              struct rt_comb_internal    *comb,
02187              union tree                 *comb_leaf,
02188              genptr_t                   user_ptr1,
02189              genptr_t                   user_ptr2,
02190              genptr_t                   user_ptr3)
02191 {
02192         int                     *pathpos;
02193         matp_t                  old_xlate;
02194         mat_t                   new_xlate;
02195         struct directory        *nextdp;
02196         struct wdb_trace_data   *wtdp;
02197 
02198         RT_CK_DBI(dbip);
02199         RT_CK_TREE(comb_leaf);
02200 
02201         if ((nextdp = db_lookup(dbip, comb_leaf->tr_l.tl_name, LOOKUP_NOISY)) == DIR_NULL)
02202                 return;
02203 
02204         pathpos = (int *)user_ptr1;
02205         old_xlate = (matp_t)user_ptr2;
02206         wtdp = (struct wdb_trace_data *)user_ptr3;
02207 
02208         /* 
02209          * In WDB_EVAL_ONLY mode we're collecting the matrices along
02210          * the path in order to perform some type of edit where the object
02211          * lives (i.e. after applying the accumulated transforms). So, if
02212          * we're doing a matrix edit (i.e. the last object in the path is
02213          * a combination), we skip its leaf matrices because those are the
02214          * one's we'll be editing.
02215          */
02216 #if 0
02217         /*XXX Remove this section of code */
02218         if (comb_leaf->tr_l.tl_mat) {
02219             bn_mat_mul(new_xlate, old_xlate, comb_leaf->tr_l.tl_mat);
02220         } else {
02221             MAT_COPY(new_xlate, old_xlate);
02222         }
02223 #else
02224         if (wtdp->wtd_flag != WDB_EVAL_ONLY ||
02225             (*pathpos)+1 < wtdp->wtd_objpos) {
02226             if (comb_leaf->tr_l.tl_mat) {
02227                 bn_mat_mul(new_xlate, old_xlate, comb_leaf->tr_l.tl_mat);
02228             } else {
02229                 MAT_COPY(new_xlate, old_xlate);
02230             }
02231         } else {
02232             MAT_COPY(new_xlate, old_xlate);
02233         }
02234 #endif
02235 
02236         wdb_trace(nextdp, (*pathpos)+1, new_xlate, wtdp);
02237 }
02238 
02239 static void
02240 wdb_trace(register struct directory     *dp,
02241           int                           pathpos,
02242           const mat_t                   old_xlate,
02243           struct wdb_trace_data         *wtdp)
02244 {
02245         struct rt_db_internal   intern;
02246         struct rt_comb_internal *comb;
02247         int                     i;
02248         int                     id;
02249         struct bu_vls           str;
02250 
02251 #if 0
02252         if (dbip == DBI_NULL)
02253                 return;
02254 #endif
02255 
02256         bu_vls_init(&str);
02257 
02258         if (pathpos >= WDB_MAX_LEVELS) {
02259                 struct bu_vls tmp_vls;
02260 
02261                 bu_vls_init(&tmp_vls);
02262                 bu_vls_printf(&tmp_vls, "nesting exceeds %d levels\n", WDB_MAX_LEVELS);
02263                 Tcl_AppendResult(wtdp->wtd_interp, bu_vls_addr(&tmp_vls), (char *)NULL);
02264                 bu_vls_free(&tmp_vls);
02265 
02266                 for (i=0; i<WDB_MAX_LEVELS; i++)
02267                         Tcl_AppendResult(wtdp->wtd_interp, "/", wtdp->wtd_path[i]->d_namep, (char *)NULL);
02268 
02269                 Tcl_AppendResult(wtdp->wtd_interp, "\n", (char *)NULL);
02270                 return;
02271         }
02272 
02273         if (dp->d_flags & DIR_COMB) {
02274                 if (rt_db_get_internal(&intern, dp, wtdp->wtd_dbip, (fastf_t *)NULL, &rt_uniresource) < 0)
02275                         WDB_READ_ERR_return;
02276 
02277                 wtdp->wtd_path[pathpos] = dp;
02278                 comb = (struct rt_comb_internal *)intern.idb_ptr;
02279                 if (comb->tree)
02280                         db_tree_funcleaf(wtdp->wtd_dbip, comb, comb->tree, wdb_do_trace,
02281                                 (genptr_t)&pathpos, (genptr_t)old_xlate, (genptr_t)wtdp);
02282 #if USE_RT_COMB_IFREE
02283                 rt_comb_ifree(&intern, &rt_uniresource);
02284 #else
02285                 rt_db_free_internal(&intern, &rt_uniresource);
02286 #endif
02287                 return;
02288         }
02289 
02290         /* not a combination  -  should have a solid */
02291 
02292         /* last (bottom) position */
02293         wtdp->wtd_path[pathpos] = dp;
02294 
02295         /* check for desired path */
02296         if( wtdp->wtd_flag == WDB_CPEVAL ) {
02297                 for (i=0; i<=pathpos; i++) {
02298                         if (wtdp->wtd_path[i]->d_addr != wtdp->wtd_obj[i]->d_addr) {
02299                                 /* not the desired path */
02300                                 return;
02301                         }
02302                 }
02303         } else {
02304                 for (i=0; i<wtdp->wtd_objpos; i++) {
02305                         if (wtdp->wtd_path[i]->d_addr != wtdp->wtd_obj[i]->d_addr) {
02306                                 /* not the desired path */
02307                                 return;
02308                         }
02309                 }
02310         }
02311 
02312         /* have the desired path up to objpos */
02313         MAT_COPY(wtdp->wtd_xform, old_xlate);
02314         wtdp->wtd_prflag = 1;
02315 
02316         if (wtdp->wtd_flag == WDB_CPEVAL ||
02317             wtdp->wtd_flag == WDB_EVAL_ONLY)
02318                 return;
02319 
02320         /* print the path */
02321         for (i=0; i<pathpos; i++)
02322                 Tcl_AppendResult(wtdp->wtd_interp, "/", wtdp->wtd_path[i]->d_namep, (char *)NULL);
02323 
02324         if (wtdp->wtd_flag == WDB_LISTPATH) {
02325                 bu_vls_printf( &str, "/%s:\n", dp->d_namep );
02326                 Tcl_AppendResult(wtdp->wtd_interp, bu_vls_addr(&str), (char *)NULL);
02327                 bu_vls_free(&str);
02328                 return;
02329         }
02330 
02331         /* NOTE - only reach here if wtd_flag == WDB_LISTEVAL */
02332         Tcl_AppendResult(wtdp->wtd_interp, "/", (char *)NULL);
02333         if ((id=rt_db_get_internal(&intern, dp, wtdp->wtd_dbip, wtdp->wtd_xform, &rt_uniresource)) < 0) {
02334                 Tcl_AppendResult(wtdp->wtd_interp, "rt_db_get_internal(", dp->d_namep,
02335                                  ") failure", (char *)NULL );
02336                 return;
02337         }
02338         bu_vls_printf(&str, "%s:\n", dp->d_namep);
02339         if (rt_functab[id].ft_describe(&str, &intern, 1, wtdp->wtd_dbip->dbi_base2local, &rt_uniresource, wtdp->wtd_dbip) < 0)
02340                 Tcl_AppendResult(wtdp->wtd_interp, dp->d_namep, ": describe error\n", (char *)NULL);
02341         rt_db_free_internal(&intern, &rt_uniresource);
02342         Tcl_AppendResult(wtdp->wtd_interp, bu_vls_addr(&str), (char *)NULL);
02343         bu_vls_free(&str);
02344 }
02345 
02346 int
02347 wdb_pathsum_cmd(struct rt_wdb   *wdbp,
02348                 Tcl_Interp      *interp,
02349                 int             argc,
02350                 char            **argv)
02351 {
02352         int                     i, pos_in;
02353         struct wdb_trace_data   wtd;
02354 
02355         if (argc < 2 || MAXARGS < argc) {
02356                 struct bu_vls vls;
02357 
02358                 bu_vls_init(&vls);
02359                 bu_vls_printf(&vls, "helplib_alias %s%s %s", "wdb_", argv[0], argv[0]);
02360                 Tcl_Eval(interp, bu_vls_addr(&vls));
02361                 bu_vls_free(&vls);
02362                 return TCL_ERROR;
02363         }
02364 
02365         /*
02366          *      paths are matched up to last input member
02367          *      ANY path the same up to this point is considered as matching
02368          */
02369 
02370         /* initialize wtd */
02371         wtd.wtd_interp = interp;
02372         wtd.wtd_dbip = wdbp->dbip;
02373         wtd.wtd_flag = WDB_CPEVAL;
02374         wtd.wtd_prflag = 0;
02375 
02376         pos_in = 1;
02377 
02378         /* find out which command was entered */
02379         if (strcmp(argv[0], "paths") == 0) {
02380                 /* want to list all matching paths */
02381                 wtd.wtd_flag = WDB_LISTPATH;
02382         }
02383         if (strcmp(argv[0], "listeval") == 0) {
02384                 /* want to list evaluated solid[s] */
02385                 wtd.wtd_flag = WDB_LISTEVAL;
02386         }
02387 
02388         if (argc == 2 && strchr(argv[1], '/')) {
02389                 char *tok;
02390                 wtd.wtd_objpos = 0;
02391 
02392                 tok = strtok(argv[1], "/");
02393                 while (tok) {
02394                         if ((wtd.wtd_obj[wtd.wtd_objpos++] = db_lookup(wdbp->dbip, tok, LOOKUP_NOISY)) == DIR_NULL)
02395                                 return TCL_ERROR;
02396                         tok = strtok((char *)NULL, "/");
02397                 }
02398         } else {
02399                 wtd.wtd_objpos = argc-1;
02400 
02401                 /* build directory pointer array for desired path */
02402                 for (i=0; i<wtd.wtd_objpos; i++) {
02403                         if ((wtd.wtd_obj[i] = db_lookup(wdbp->dbip, argv[pos_in+i], LOOKUP_NOISY)) == DIR_NULL)
02404                                 return TCL_ERROR;
02405                 }
02406         }
02407 
02408         MAT_IDN(wtd.wtd_xform);
02409 
02410         wdb_trace(wtd.wtd_obj[0], 0, bn_mat_identity, &wtd);
02411 
02412         if (wtd.wtd_prflag == 0) {
02413                 /* path not found */
02414                 Tcl_AppendResult(interp, "PATH:  ", (char *)NULL);
02415                 for (i=0; i<wtd.wtd_objpos; i++)
02416                         Tcl_AppendResult(interp, "/", wtd.wtd_obj[i]->d_namep, (char *)NULL);
02417 
02418                 Tcl_AppendResult(interp, "  NOT FOUND\n", (char *)NULL);
02419         }
02420 
02421         return TCL_OK;
02422 }
02423 
02424 
02425 /*
02426  *                      W D B _ P A T H S U M _ T C L
02427  *
02428  *  Common code for several direct db methods: listeval, paths
02429  *  Also used as support routine for "l" (list) command.
02430  *
02431  *  1.  produces path for purposes of matching
02432  *  2.  gives all paths matching the input path OR
02433  *  3.  gives a summary of all paths matching the input path
02434  *      including the final parameters of the solids at the bottom
02435  *      of the matching paths
02436  *
02437  * Usage:
02438  *        procname (WDB_LISTEVAL|paths) args(s)
02439  */
02440 static int
02441 wdb_pathsum_tcl(ClientData      clientData,
02442                 Tcl_Interp      *interp,
02443                 int             argc,
02444                 char            **argv)
02445 {
02446         struct rt_wdb   *wdbp = (struct rt_wdb *)clientData;
02447 
02448         return wdb_pathsum_cmd(wdbp, interp, argc-1, argv+1);
02449 }
02450 
02451 
02452 static void
02453 wdb_scrape_escapes_AppendResult(Tcl_Interp      *interp,
02454                                 char            *str)
02455 {
02456         char buf[2];
02457         buf[1] = '\0';
02458 
02459         while (*str) {
02460                 buf[0] = *str;
02461                 if (*str != '\\') {
02462                         Tcl_AppendResult(interp, buf, NULL);
02463                 } else if (*(str+1) == '\\') {
02464                         Tcl_AppendResult(interp, buf, NULL);
02465                         ++str;
02466                 }
02467                 if (*str == '\0')
02468                         break;
02469                 ++str;
02470         }
02471 }
02472 
02473 int
02474 wdb_expand_cmd(struct rt_wdb    *wdbp,
02475                Tcl_Interp       *interp,
02476                int              argc,
02477                char             **argv)
02478 {
02479         register char *pattern;
02480         register struct directory *dp;
02481         register int i, whicharg;
02482         int regexp, nummatch, thismatch, backslashed;
02483 
02484         if (argc < 1 || MAXARGS < argc) {
02485                 struct bu_vls vls;
02486 
02487                 bu_vls_init(&vls);
02488                 bu_vls_printf(&vls, "helplib_alias wdb_expand %s", argv[0]);
02489                 Tcl_Eval(interp, bu_vls_addr(&vls));
02490                 bu_vls_free(&vls);
02491                 return TCL_ERROR;
02492         }
02493 
02494         nummatch = 0;
02495         backslashed = 0;
02496         for (whicharg = 1; whicharg < argc; whicharg++) {
02497                 /* If * ? or [ are present, this is a regular expression */
02498                 pattern = argv[whicharg];
02499                 regexp = 0;
02500                 do {
02501                         if ((*pattern == '*' || *pattern == '?' || *pattern == '[') &&
02502                             !backslashed) {
02503                                 regexp = 1;
02504                                 break;
02505                         }
02506                         if (*pattern == '\\' && !backslashed)
02507                                 backslashed = 1;
02508                         else
02509                                 backslashed = 0;
02510                 } while (*pattern++);
02511 
02512                 /* If it isn't a regexp, copy directly and continue */
02513                 if (regexp == 0) {
02514                         if (nummatch > 0)
02515                                 Tcl_AppendResult(interp, " ", NULL);
02516                         wdb_scrape_escapes_AppendResult(interp, argv[whicharg]);
02517                         ++nummatch;
02518                         continue;
02519                 }
02520 
02521                 /* Search for pattern matches.
02522                  * If any matches are found, we do not have to worry about
02523                  * '\' escapes since the match coming from dp->d_namep will be
02524                  * clean. In the case of no matches, just copy the argument
02525                  * directly.
02526                  */
02527 
02528                 pattern = argv[whicharg];
02529                 thismatch = 0;
02530                 for (i = 0; i < RT_DBNHASH; i++) {
02531                         for (dp = wdbp->dbip->dbi_Head[i]; dp != DIR_NULL; dp = dp->d_forw) {
02532                                 if (!db_regexp_match(pattern, dp->d_namep))
02533                                         continue;
02534                                 /* Successful match */
02535                                 if (nummatch == 0)
02536                                         Tcl_AppendResult(interp, dp->d_namep, NULL);
02537                                 else
02538                                         Tcl_AppendResult(interp, " ", dp->d_namep, NULL);
02539                                 ++nummatch;
02540                                 ++thismatch;
02541                         }
02542                 }
02543                 if (thismatch == 0) {
02544                         if (nummatch > 0)
02545                                 Tcl_AppendResult(interp, " ", NULL);
02546                         wdb_scrape_escapes_AppendResult(interp, argv[whicharg]);
02547                 }
02548         }
02549 
02550         return TCL_OK;
02551 }
02552 
02553 /*
02554  * Performs wildcard expansion (matched to the database elements)
02555  * on its given arguments.  The result is returned in interp->result.
02556  *
02557  * Usage:
02558  *        procname expand [args]
02559  */
02560 static int
02561 wdb_expand_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv)
02562 {
02563         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
02564 
02565         return wdb_expand_cmd(wdbp, interp, argc-1, argv+1);
02566 }
02567 
02568 int
02569 wdb_kill_cmd(struct rt_wdb      *wdbp,
02570              Tcl_Interp         *interp,
02571              int                argc,
02572              char               **argv)
02573 {
02574         register struct directory *dp;
02575         register int i;
02576         int     is_phony;
02577         int     verbose = LOOKUP_NOISY;
02578 
02579         WDB_TCL_CHECK_READ_ONLY;
02580 
02581         if (argc < 2 || MAXARGS < argc) {
02582                 struct bu_vls vls;
02583 
02584                 bu_vls_init(&vls);
02585                 bu_vls_printf(&vls, "helplib_alias wdb_kill %s", argv[0]);
02586                 if (interp) {
02587                     Tcl_Eval(interp, bu_vls_addr(&vls));
02588                 }
02589                 bu_vls_free(&vls);
02590                 return TCL_ERROR;
02591         }
02592 
02593         /* skip past "-f" */
02594         if (argc > 1 && strcmp(argv[1], "-f") == 0) {
02595                 verbose = LOOKUP_QUIET;
02596                 argc--;
02597                 argv++;
02598         }
02599 
02600         for (i = 1; i < argc; i++) {
02601                 if ((dp = db_lookup(wdbp->dbip,  argv[i], verbose)) != DIR_NULL) {
02602                         is_phony = (dp->d_addr == RT_DIR_PHONY_ADDR);
02603 
02604                         /* don't worry about phony objects */
02605                         if (is_phony)
02606                                 continue;
02607 
02608                         /* notify drawable geometry objects associated with this database object */
02609                         if (i == argc-1)
02610                             dgo_eraseobjall_callback(wdbp->dbip, interp, dp, 1 /* notify other interested observers */);
02611                         else
02612                             dgo_eraseobjall_callback(wdbp->dbip, interp, dp, 0);
02613 
02614                         if (db_delete(wdbp->dbip, dp) < 0 ||
02615                             db_dirdelete(wdbp->dbip, dp) < 0) {
02616                                 /* Abort kill processing on first error */
02617                                 Tcl_AppendResult(interp,
02618                                                  "an error occurred while deleting ",
02619                                                  argv[i], (char *)NULL);
02620                                 return TCL_ERROR;
02621                         }
02622                 }
02623         }
02624 
02625         return TCL_OK;
02626 }
02627 
02628 /*
02629  * Usage:
02630  *        procname kill arg(s)
02631  */
02632 static int
02633 wdb_kill_tcl(ClientData clientData,
02634              Tcl_Interp *interp,
02635              int        argc,
02636              char       **argv)
02637 {
02638         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
02639 
02640         return wdb_kill_cmd(wdbp, interp, argc-1, argv+1);
02641 }
02642 
02643 int
02644 wdb_killall_cmd(struct rt_wdb   *wdbp,
02645                 Tcl_Interp      *interp,
02646                 int             argc,
02647                 char            **argv)
02648 {
02649         register int                    i,k;
02650         register struct directory       *dp;
02651         struct rt_db_internal           intern;
02652         struct rt_comb_internal         *comb;
02653         int                             ret;
02654 
02655         WDB_TCL_CHECK_READ_ONLY;
02656 
02657         if (argc < 2 || MAXARGS < argc) {
02658                 struct bu_vls vls;
02659 
02660                 bu_vls_init(&vls);
02661                 bu_vls_printf(&vls, "helplib_alias  wdb_killall %s", argv[0]);
02662                 Tcl_Eval(interp, bu_vls_addr(&vls));
02663                 bu_vls_free(&vls);
02664                 return TCL_ERROR;
02665         }
02666 
02667         ret = TCL_OK;
02668 
02669         /* Examine all COMB nodes */
02670         for (i = 0; i < RT_DBNHASH; i++) {
02671                 for (dp = wdbp->dbip->dbi_Head[i]; dp != DIR_NULL; dp = dp->d_forw) {
02672                         if (!(dp->d_flags & DIR_COMB))
02673                                 continue;
02674 
02675                         if (rt_db_get_internal(&intern, dp, wdbp->dbip, (fastf_t *)NULL, &rt_uniresource) < 0) {
02676                                 Tcl_AppendResult(interp, "rt_db_get_internal(", dp->d_namep,
02677                                                  ") failure", (char *)NULL );
02678                                 ret = TCL_ERROR;
02679                                 continue;
02680                         }
02681                         comb = (struct rt_comb_internal *)intern.idb_ptr;
02682                         RT_CK_COMB(comb);
02683 
02684                         for (k=1; k<argc; k++) {
02685                                 int     code;
02686 
02687                                 code = db_tree_del_dbleaf(&(comb->tree), argv[k], &rt_uniresource);
02688                                 if (code == -1)
02689                                         continue;       /* not found */
02690                                 if (code == -2)
02691                                         continue;       /* empty tree */
02692                                 if (code < 0) {
02693                                         Tcl_AppendResult(interp, "  ERROR_deleting ",
02694                                                          dp->d_namep, "/", argv[k],
02695                                                          "\n", (char *)NULL);
02696                                         ret = TCL_ERROR;
02697                                 } else {
02698                                         Tcl_AppendResult(interp, "deleted ",
02699                                                          dp->d_namep, "/", argv[k],
02700                                                          "\n", (char *)NULL);
02701                                 }
02702                         }
02703 
02704                         if (rt_db_put_internal(dp, wdbp->dbip, &intern, &rt_uniresource) < 0) {
02705                                 Tcl_AppendResult(interp,
02706                                                  "ERROR: Unable to write new combination into database.\n",
02707                                                  (char *)NULL);
02708                                 ret = TCL_ERROR;
02709                                 continue;
02710                         }
02711                 }
02712         }
02713 
02714         if (ret != TCL_OK) {
02715                 Tcl_AppendResult(interp,
02716                                  "KILL skipped because of earlier errors.\n",
02717                                  (char *)NULL);
02718                 return ret;
02719         }
02720 
02721         /* ALL references removed...now KILL the object[s] */
02722         /* reuse argv[] */
02723         argv[0] = "kill";
02724         return wdb_kill_cmd(wdbp, interp, argc, argv);
02725 }
02726 
02727 /*
02728  * Kill object[s] and remove all references to the object[s].
02729  *
02730  * Usage:
02731  *        procname killall arg(s)
02732  */
02733 static int
02734 wdb_killall_tcl(ClientData      clientData,
02735                 Tcl_Interp      *interp,
02736                 int             argc,
02737                 char            **argv)
02738 {
02739         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
02740 
02741         return wdb_killall_cmd(wdbp, interp, argc-1, argv+1);
02742 }
02743 
02744 int
02745 wdb_killtree_cmd(struct rt_wdb  *wdbp,
02746                  Tcl_Interp     *interp,
02747                  int            argc,
02748                  char           **argv)
02749 {
02750         register struct directory *dp;
02751         register int i;
02752         struct wdb_killtree_data ktd;
02753 
02754         WDB_TCL_CHECK_READ_ONLY;
02755 
02756         if (argc < 2 || MAXARGS < argc) {
02757                 struct bu_vls vls;
02758 
02759                 bu_vls_init(&vls);
02760                 bu_vls_printf(&vls, "helplib_alias wdb_killtree %s", argv[0]);
02761                 Tcl_Eval(interp, bu_vls_addr(&vls));
02762                 bu_vls_free(&vls);
02763                 return TCL_ERROR;
02764         }
02765 
02766         ktd.interp = interp;
02767         ktd.notify = 0;
02768 
02769         for (i=1; i<argc; i++) {
02770                 if ((dp = db_lookup(wdbp->dbip, argv[i], LOOKUP_NOISY)) == DIR_NULL)
02771                         continue;
02772 
02773                 /* ignore phony objects */
02774                 if (dp->d_addr == RT_DIR_PHONY_ADDR)
02775                         continue;
02776 #if 0
02777                 if (i == argc-1)
02778                   ktd.notify = 1;
02779 #endif
02780 
02781                 db_functree(wdbp->dbip, dp,
02782                             wdb_killtree_callback, wdb_killtree_callback,
02783                             wdbp->wdb_resp, (genptr_t)&ktd);
02784         }
02785 
02786         dgo_notifyWdb(wdbp, interp);
02787 
02788         return TCL_OK;
02789 }
02790 
02791 /*
02792  * Kill all paths belonging to an object.
02793  *
02794  * Usage:
02795  *        procname killtree arg(s)
02796  */
02797 static int
02798 wdb_killtree_tcl(ClientData     clientData,
02799                  Tcl_Interp     *interp,
02800                  int            argc,
02801                  char           **argv)
02802 {
02803         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
02804 
02805         return wdb_killtree_cmd(wdbp, interp, argc-1, argv+1);
02806 }
02807 
02808 /*
02809  *                      K I L L T R E E
02810  */
02811 static void
02812 wdb_killtree_callback(struct db_i               *dbip,
02813                       register struct directory *dp,
02814                       genptr_t                  ptr) {
02815         struct wdb_killtree_data *ktdp = (struct wdb_killtree_data *)ptr;
02816         Tcl_Interp *interp = ktdp->interp;
02817 
02818         if (dbip == DBI_NULL)
02819                 return;
02820 
02821         Tcl_AppendResult(interp, "KILL ", (dp->d_flags & DIR_COMB) ? "COMB" : "Solid",
02822                          ":  ", dp->d_namep, "\n", (char *)NULL);
02823 
02824         /* notify drawable geometry objects associated with this database object */
02825         dgo_eraseobjall_callback(dbip, interp, dp, ktdp->notify);
02826 
02827         if (db_delete(dbip, dp) < 0 || db_dirdelete(dbip, dp) < 0) {
02828                 Tcl_AppendResult(interp,
02829                                  "an error occurred while deleting ",
02830                                  dp->d_namep, "\n", (char *)NULL);
02831         }
02832 }
02833 
02834 int
02835 wdb_copy_cmd(struct rt_wdb      *wdbp,
02836              Tcl_Interp         *interp,
02837              int                argc,
02838              char               **argv)
02839 {
02840         register struct directory *proto;
02841         register struct directory *dp;
02842         struct bu_external external;
02843 
02844         WDB_TCL_CHECK_READ_ONLY;
02845 
02846         if (argc != 3) {
02847                 struct bu_vls vls;
02848 
02849                 bu_vls_init(&vls);
02850                 bu_vls_printf(&vls, "helplib_alias wdb_copy %s", argv[0]);
02851                 Tcl_Eval(interp, bu_vls_addr(&vls));
02852                 bu_vls_free(&vls);
02853                 return TCL_ERROR;
02854         }
02855 
02856         if ((proto = db_lookup(wdbp->dbip,  argv[1], LOOKUP_NOISY)) == DIR_NULL)
02857                 return TCL_ERROR;
02858 
02859         if (db_lookup(wdbp->dbip, argv[2], LOOKUP_QUIET) != DIR_NULL) {
02860                 Tcl_AppendResult(interp, argv[2], ":  already exists", (char *)NULL);
02861                 return TCL_ERROR;
02862         }
02863 
02864         if (db_get_external(&external , proto , wdbp->dbip)) {
02865                 Tcl_AppendResult(interp, "Database read error, aborting", (char *)NULL);
02866                 return TCL_ERROR;
02867         }
02868 
02869         if ((dp=db_diradd(wdbp->dbip, argv[2], -1, 0, proto->d_flags, (genptr_t)&proto->d_minor_type)) == DIR_NULL ) {
02870                 Tcl_AppendResult(interp,
02871                                  "An error has occured while adding a new object to the database.",
02872                                  (char *)NULL);
02873                 return TCL_ERROR;
02874         }
02875 
02876         if (db_put_external(&external, dp, wdbp->dbip) < 0) {
02877                 bu_free_external(&external);
02878                 Tcl_AppendResult(interp, "Database write error, aborting", (char *)NULL);
02879                 return TCL_ERROR;
02880         }
02881         bu_free_external(&external);
02882 
02883         return TCL_OK;
02884 }
02885 
02886 /*
02887  * Usage:
02888  *        procname cp from to
02889  */
02890 static int
02891 wdb_copy_tcl(ClientData clientData,
02892              Tcl_Interp *interp,
02893              int        argc,
02894              char       **argv)
02895 {
02896         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
02897 
02898         return wdb_copy_cmd(wdbp, interp, argc-1, argv+1);
02899 }
02900 
02901 int
02902 wdb_move_cmd(struct rt_wdb      *wdbp,
02903              Tcl_Interp         *interp,
02904              int                argc,
02905              char               **argv)
02906 {
02907         register struct directory       *dp;
02908         struct rt_db_internal           intern;
02909 
02910         WDB_TCL_CHECK_READ_ONLY;
02911 
02912         if (argc != 3) {
02913                 struct bu_vls vls;
02914 
02915                 bu_vls_init(&vls);
02916                 bu_vls_printf(&vls, "helplib_alias wdb_move %s", argv[0]);
02917                 Tcl_Eval(interp, bu_vls_addr(&vls));
02918                 bu_vls_free(&vls);
02919                 return TCL_ERROR;
02920         }
02921 
02922         if ((dp = db_lookup(wdbp->dbip,  argv[1], LOOKUP_NOISY)) == DIR_NULL)
02923                 return TCL_ERROR;
02924 
02925         if (db_lookup(wdbp->dbip, argv[2], LOOKUP_QUIET) != DIR_NULL) {
02926                 Tcl_AppendResult(interp, argv[2], ":  already exists", (char *)NULL);
02927                 return TCL_ERROR;
02928         }
02929 
02930         if (rt_db_get_internal(&intern, dp, wdbp->dbip, (fastf_t *)NULL, &rt_uniresource) < 0) {
02931                 Tcl_AppendResult(interp, "Database read error, aborting", (char *)NULL);
02932                 return TCL_ERROR;
02933         }
02934 
02935         /*  Change object name in the in-memory directory. */
02936         if (db_rename(wdbp->dbip, dp, argv[2]) < 0) {
02937                 rt_db_free_internal(&intern, &rt_uniresource);
02938                 Tcl_AppendResult(interp, "error in db_rename to ", argv[2],
02939                                  ", aborting", (char *)NULL);
02940                 return TCL_ERROR;
02941         }
02942 
02943         /* Re-write to the database.  New name is applied on the way out. */
02944         if (rt_db_put_internal(dp, wdbp->dbip, &intern, &rt_uniresource) < 0) {
02945                 Tcl_AppendResult(interp, "Database write error, aborting", (char *)NULL);
02946                 return TCL_ERROR;
02947         }
02948 
02949         return TCL_OK;
02950 }
02951 
02952 /*
02953  * Rename an object.
02954  *
02955  * Usage:
02956  *        procname mv from to
02957  */
02958 static int
02959 wdb_move_tcl(ClientData clientData,
02960              Tcl_Interp *interp,
02961              int        argc,
02962              char       **argv)
02963 {
02964         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
02965 
02966         return wdb_move_cmd(wdbp, interp, argc-1, argv+1);
02967 }
02968 
02969 /*
02970  *
02971  */
02972 int
02973 wdb_move_all_cmd(struct rt_wdb  *wdbp,
02974                  Tcl_Interp     *interp,
02975                  int            argc,
02976                  char           **argv)
02977 {
02978         register int    i;
02979         register struct directory *dp;
02980         struct rt_db_internal   intern;
02981         struct rt_comb_internal *comb;
02982         struct bu_ptbl          stack;
02983 
02984         WDB_TCL_CHECK_READ_ONLY;
02985 
02986         if (argc != 3) {
02987                 struct bu_vls vls;
02988 
02989                 bu_vls_init(&vls);
02990                 bu_vls_printf(&vls, "helplib_alias wdb_moveall %s", argv[0]);
02991                 Tcl_Eval(interp, bu_vls_addr(&vls));
02992                 bu_vls_free(&vls);
02993                 return TCL_ERROR;
02994         }
02995 
02996         if (wdbp->dbip->dbi_version < 5 && (int)strlen(argv[2]) > NAMESIZE) {
02997                 struct bu_vls tmp_vls;
02998 
02999                 bu_vls_init(&tmp_vls);
03000                 bu_vls_printf(&tmp_vls, "ERROR: name length limited to %d characters in v4 databases\n", NAMESIZE);
03001                 Tcl_AppendResult(interp, bu_vls_addr(&tmp_vls), (char *)NULL);
03002                 bu_vls_free(&tmp_vls);
03003                 return TCL_ERROR;
03004         }
03005 
03006 
03007 
03008         /* rename the record itself */
03009         if ((dp = db_lookup(wdbp->dbip, argv[1], LOOKUP_NOISY )) == DIR_NULL)
03010                 return TCL_ERROR;
03011 
03012         if (db_lookup(wdbp->dbip, argv[2], LOOKUP_QUIET) != DIR_NULL) {
03013                 Tcl_AppendResult(interp, argv[2], ":  already exists", (char *)NULL);
03014                 return TCL_ERROR;
03015         }
03016 
03017         /* if this was a sketch, we need to look for all the extrude 
03018          * objects that might use it.
03019          *
03020          * This has to be done here, before we rename the (possible) sketch object
03021          * because the extrude will do a rt_db_get on the sketch when we call
03022          * rt_db_get_internal on it.
03023          */
03024         if (dp->d_major_type == DB5_MAJORTYPE_BRLCAD && \
03025             dp->d_minor_type == DB5_MINORTYPE_BRLCAD_SKETCH) {
03026 
03027             struct directory *dirp;
03028 
03029             for (i = 0; i < RT_DBNHASH; i++) {
03030                 for (dirp = wdbp->dbip->dbi_Head[i]; dirp != DIR_NULL; dirp = dirp->d_forw) {
03031 
03032                     if (dirp->d_major_type == DB5_MAJORTYPE_BRLCAD && \
03033                         dirp->d_minor_type == DB5_MINORTYPE_BRLCAD_EXTRUDE) {
03034                         struct rt_extrude_internal *extrude;
03035 
03036                         if (rt_db_get_internal(&intern, dirp, wdbp->dbip, (fastf_t *)NULL, &rt_uniresource) < 0) {
03037                             bu_log("Can't get extrude %s?\n", dirp->d_namep);
03038                             continue;
03039                         }
03040                         extrude = (struct rt_extrude_internal *)intern.idb_ptr;
03041                         RT_EXTRUDE_CK_MAGIC(extrude);
03042 
03043                         if (! strcmp(extrude->sketch_name, argv[1]) ) {
03044                             bu_free(extrude->sketch_name, "sketch name");
03045                             extrude->sketch_name = bu_strdup(argv[2]);
03046 
03047                             if (rt_db_put_internal(dirp, wdbp->dbip, &intern, &rt_uniresource) < 0) {
03048                                 bu_log("oops\n");
03049                             }
03050                         }
03051                     }
03052                 }
03053             }
03054         }
03055 
03056         /*  Change object name in the directory. */
03057         if (db_rename(wdbp->dbip, dp, argv[2]) < 0) {
03058                 Tcl_AppendResult(interp, "error in rename to ", argv[2],
03059                                  ", aborting", (char *)NULL);
03060                 return TCL_ERROR;
03061         }
03062 
03063         /* Change name in the file */
03064         if (rt_db_get_internal(&intern, dp, wdbp->dbip, (fastf_t *)NULL, &rt_uniresource) < 0) {
03065                 Tcl_AppendResult(interp, "Database read error, aborting", (char *)NULL);
03066                 return TCL_ERROR;
03067         }
03068 
03069         if (rt_db_put_internal(dp, wdbp->dbip, &intern, &rt_uniresource) < 0) {
03070                 Tcl_AppendResult(interp, "Database write error, aborting", (char *)NULL);
03071                 return TCL_ERROR;
03072         }
03073 
03074         bu_ptbl_init(&stack, 64, "combination stack for wdb_mvall_cmd");
03075 
03076 
03077         /* Examine all COMB nodes */
03078         for (i = 0; i < RT_DBNHASH; i++) {
03079                 for (dp = wdbp->dbip->dbi_Head[i]; dp != DIR_NULL; dp = dp->d_forw) {
03080                         union tree      *comb_leaf;
03081                         int             done=0;
03082                         int             changed=0;
03083 
03084                         
03085 
03086                         if (!(dp->d_flags & DIR_COMB))
03087                                 continue;
03088 
03089                         if (rt_db_get_internal(&intern, dp, wdbp->dbip, (fastf_t *)NULL, &rt_uniresource) < 0)
03090                                 continue;
03091                         comb = (struct rt_comb_internal *)intern.idb_ptr;
03092 
03093                         bu_ptbl_reset(&stack);
03094                         /* visit each leaf in the combination */
03095                         comb_leaf = comb->tree;
03096                         if (comb_leaf) {
03097                                 while (!done) {
03098                                         while(comb_leaf->tr_op != OP_DB_LEAF) {
03099                                                 bu_ptbl_ins(&stack, (long *)comb_leaf);
03100                                                 comb_leaf = comb_leaf->tr_b.tb_left;
03101                                         }
03102 
03103                                         if (!strcmp(comb_leaf->tr_l.tl_name, argv[1])) {
03104                                                 bu_free(comb_leaf->tr_l.tl_name, "comb_leaf->tr_l.tl_name");
03105                                                 comb_leaf->tr_l.tl_name = bu_strdup(argv[2]);
03106                                                 changed = 1;
03107                                         }
03108 
03109                                         if (BU_PTBL_END(&stack) < 1) {
03110                                                 done = 1;
03111                                                 break;
03112                                         }
03113                                         comb_leaf = (union tree *)BU_PTBL_GET(&stack, BU_PTBL_END(&stack)-1);
03114                                         if (comb_leaf->tr_op != OP_DB_LEAF) {
03115                                                 bu_ptbl_rm( &stack, (long *)comb_leaf );
03116                                                 comb_leaf = comb_leaf->tr_b.tb_right;
03117                                         }
03118                                 }
03119                         }
03120 
03121                         if (changed) {
03122                                 if (rt_db_put_internal(dp, wdbp->dbip, &intern, &rt_uniresource)) {
03123                                         bu_ptbl_free( &stack );
03124                                         rt_db_free_internal( &intern, &rt_uniresource );
03125                                         Tcl_AppendResult(interp,
03126                                                          "Database write error, aborting",
03127                                                          (char *)NULL);
03128                                         return TCL_ERROR;
03129                                 }
03130                         }
03131                         else
03132                                 rt_db_free_internal(&intern, &rt_uniresource);
03133                 }
03134         }
03135 
03136         bu_ptbl_free(&stack);
03137         return TCL_OK;
03138 }
03139 
03140 /*
03141  * Rename all occurences of an object
03142  *
03143  * Usage:
03144  *        procname mvall from to
03145  */
03146 static int
03147 wdb_move_all_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv)
03148 {
03149         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
03150 
03151         return wdb_move_all_cmd(wdbp, interp, argc-1, argv+1);
03152 }
03153 
03154 struct concat_data {
03155         int unique_mode;
03156         struct db_i *old_dbip;
03157         struct db_i *new_dbip;
03158         struct bu_vls prestr;
03159 };
03160 
03161 #define ADD_PREFIX 1
03162 #define ADD_SUFFIX 2
03163 #define OLD_PREFIX 3
03164 #define V4_MAXNAME 16
03165 
03166 static char *
03167 get_new_name(
03168              const char *name,
03169              struct db_i *dbip,
03170              Tcl_HashTable *name_tbl,
03171              Tcl_HashTable *used_names_tbl,
03172              struct concat_data *cc_data )
03173 {
03174         int new=0;
03175         Tcl_HashEntry *ptr;
03176         struct bu_vls new_name;
03177         int num=0;
03178         char *aname;
03179         char *ret_name;
03180 
03181         ptr = Tcl_CreateHashEntry( name_tbl, name, &new );
03182 
03183         if( !new ) {
03184                 return( (char *)Tcl_GetHashValue( ptr ) );
03185         }
03186 
03187         /* need to create a unique name for this item */
03188         bu_vls_init( &new_name );
03189         if( cc_data->unique_mode != OLD_PREFIX ) {
03190                 bu_vls_strcpy( &new_name, name );
03191                 aname = bu_vls_addr( &new_name );
03192                 while(  db_lookup( dbip, aname, LOOKUP_QUIET ) != DIR_NULL ||
03193                         Tcl_FindHashEntry( used_names_tbl, aname ) != NULL ) {
03194                         bu_vls_trunc( &new_name, 0 );
03195                         num++;
03196                         if( cc_data->unique_mode == ADD_PREFIX ) {
03197                                 bu_vls_printf( &new_name, "%d_", num);
03198                         }
03199                         bu_vls_strcat( &new_name, name );
03200                         if( cc_data->unique_mode == ADD_SUFFIX ) {
03201                                 bu_vls_printf( &new_name, "_%d", num );
03202                         }
03203                         aname = bu_vls_addr( &new_name );
03204                 }
03205         } else {
03206                 bu_vls_vlscat( &new_name, &cc_data->prestr );
03207                 bu_vls_strcat( &new_name, name );
03208                 if( cc_data->old_dbip->dbi_version < 5 ) {
03209                         bu_vls_trunc( &new_name, V4_MAXNAME );
03210                 }
03211         }
03212 
03213         /* now have a unique name, make entries for it in both hash tables */
03214 
03215         ret_name = bu_vls_strgrab( &new_name );
03216         Tcl_SetHashValue( ptr, (ClientData)ret_name );
03217         (void)Tcl_CreateHashEntry( used_names_tbl, ret_name, &new );
03218 
03219         return( ret_name );
03220 }
03221 
03222 static void
03223 adjust_names(
03224              Tcl_Interp *interp,
03225              union tree *trp,
03226              struct db_i *dbip,
03227              Tcl_HashTable *name_tbl,
03228              Tcl_HashTable *used_names_tbl,
03229              struct concat_data *cc_data )
03230 {
03231         char *new_name;
03232 
03233         switch( trp->tr_op ) {
03234                 case OP_DB_LEAF:
03235                         new_name = get_new_name( trp->tr_l.tl_name, dbip,
03236                                                  name_tbl, used_names_tbl, cc_data );
03237                         if( new_name ) {
03238                                 bu_free( trp->tr_l.tl_name, "leaf name" );
03239                                 trp->tr_l.tl_name = bu_strdup( new_name );
03240                         }
03241                         break;
03242                 case OP_UNION:
03243                 case OP_INTERSECT:
03244                 case OP_SUBTRACT:
03245                 case OP_XOR:
03246                         adjust_names( interp, trp->tr_b.tb_left, dbip,
03247                                       name_tbl, used_names_tbl, cc_data );
03248                         adjust_names( interp, trp->tr_b.tb_right, dbip,
03249                                       name_tbl, used_names_tbl, cc_data );
03250                         break;
03251                 case OP_NOT:
03252                 case OP_GUARD:
03253                 case OP_XNOP:
03254                         adjust_names( interp, trp->tr_b.tb_left, dbip,
03255                                       name_tbl, used_names_tbl, cc_data );
03256                         break;
03257         }
03258 }
03259 
03260 static int
03261 copy_object(
03262         Tcl_Interp *interp,
03263         struct directory *input_dp,
03264         struct db_i *input_dbip,
03265         struct db_i *curr_dbip,
03266         Tcl_HashTable *name_tbl,
03267         Tcl_HashTable *used_names_tbl,
03268         struct concat_data *cc_data )
03269 {
03270         struct rt_db_internal ip;
03271         struct rt_extrude_internal *extr;
03272         struct rt_dsp_internal *dsp;
03273         struct rt_comb_internal *comb;
03274         struct directory *new_dp;
03275         char *new_name;
03276 
03277         if( rt_db_get_internal( &ip, input_dp, input_dbip, NULL, &rt_uniresource) < 0 ) {
03278                 Tcl_AppendResult(interp, "Failed to get internal form of object (", input_dp->d_namep,
03279                                  ") - aborting!!!\n", (char *)NULL );
03280                 return TCL_ERROR;
03281         }
03282 
03283         if( ip.idb_major_type == DB5_MAJORTYPE_BRLCAD ) {
03284                 /* adjust names of referenced object in any object that reference other objects */
03285                 switch( ip.idb_minor_type ) {
03286                         case DB5_MINORTYPE_BRLCAD_COMBINATION:
03287                                 comb = (struct rt_comb_internal *)ip.idb_ptr;
03288                                 RT_CK_COMB_TCL( interp, comb );
03289                                 adjust_names( interp, comb->tree, curr_dbip, name_tbl, used_names_tbl, cc_data );
03290                                 break;
03291                         case DB5_MINORTYPE_BRLCAD_EXTRUDE:
03292                                 extr = (struct rt_extrude_internal *)ip.idb_ptr;
03293                                 RT_EXTRUDE_CK_MAGIC( extr );
03294 
03295                                 new_name = get_new_name( extr->sketch_name, curr_dbip, name_tbl, used_names_tbl, cc_data );
03296                                 if( new_name ) {
03297                                         bu_free( extr->sketch_name, "sketch name" );
03298                                         extr->sketch_name = bu_strdup( new_name );
03299                                 }
03300                                 break;
03301                         case DB5_MINORTYPE_BRLCAD_DSP:
03302                                 dsp = (struct rt_dsp_internal *)ip.idb_ptr;
03303                                 RT_DSP_CK_MAGIC( dsp );
03304 
03305                                 if( dsp->dsp_datasrc == RT_DSP_SRC_OBJ ) {
03306                                         /* This dsp references a database object, may need to change its name */
03307                                         new_name = get_new_name( bu_vls_addr( &dsp->dsp_name ), curr_dbip,
03308                                                                  name_tbl, used_names_tbl, cc_data );
03309                                         if( new_name ) {
03310                                                 bu_vls_free( &dsp->dsp_name );
03311                                                 bu_vls_strcpy( &dsp->dsp_name, new_name );
03312                                         }
03313                                 }
03314                                 break;
03315                 }
03316         }
03317 
03318         new_name = get_new_name(input_dp->d_namep, curr_dbip, name_tbl, used_names_tbl , cc_data );
03319         if( !new_name ) {
03320                 new_name = input_dp->d_namep;
03321         }
03322         if( (new_dp = db_diradd( curr_dbip, new_name, -1L, 0, input_dp->d_flags,
03323                                  (genptr_t)&input_dp->d_minor_type ) ) == DIR_NULL ) {
03324                 Tcl_AppendResult(interp, "Failed to add new object name (", new_name,
03325                                  ") to directory - aborting!!\n", (char *)NULL );
03326                 return TCL_ERROR;
03327         }
03328 
03329         if( rt_db_put_internal( new_dp, curr_dbip, &ip, &rt_uniresource ) < 0 )  {
03330                 Tcl_AppendResult(interp, "Failed to write new object (", new_name,
03331                                  ") to database - aborting!!\n", (char *)NULL );
03332                 return TCL_ERROR;
03333         }
03334 
03335         return TCL_OK;
03336 }
03337 
03338 int
03339 wdb_concat_cmd(struct rt_wdb    *wdbp,
03340                Tcl_Interp       *interp,
03341                int              argc,
03342                char             **argv)
03343 {
03344         struct db_i             *newdbp;
03345         int                     bad = 0;
03346         int                     file_index;
03347         struct directory        *dp;
03348         Tcl_HashTable           name_tbl;
03349         Tcl_HashTable           used_names_tbl;
03350         Tcl_HashEntry           *ptr;
03351         Tcl_HashSearch          search;
03352         struct concat_data      cc_data;
03353 
03354         WDB_TCL_CHECK_READ_ONLY;
03355 
03356         if (argc != 3 ) {
03357                 struct bu_vls vls;
03358 
03359                 bu_vls_init(&vls);
03360                 bu_vls_printf(&vls, "helplib_alias wdb_concat %s", argv[0]);
03361                 Tcl_Eval(interp, bu_vls_addr(&vls));
03362                 bu_vls_free(&vls);
03363                 return TCL_ERROR;
03364         }
03365 
03366         bu_vls_init( &cc_data.prestr );
03367 
03368         if( argv[1][0] == '-' ) {
03369 
03370                 file_index = 2;
03371 
03372                 if( argv[1][1] == 'p' ) {
03373                         cc_data.unique_mode = ADD_PREFIX;
03374                 } else if( argv[1][1] == 's' ) {
03375                         cc_data.unique_mode = ADD_SUFFIX;
03376                 } else {
03377                         struct bu_vls vls;
03378 
03379                         bu_vls_init(&vls);
03380                         bu_vls_printf(&vls, "helplib_alias wdb_concat %s", argv[0]);
03381                         Tcl_Eval(interp, bu_vls_addr(&vls));
03382                         bu_vls_free(&vls);
03383                         return TCL_ERROR;
03384                 }
03385         } else {
03386                 file_index = 1;
03387                 cc_data.unique_mode = OLD_PREFIX;
03388 
03389                 if (strcmp(argv[2], "/") != 0) {
03390                         (void)bu_vls_strcpy(&cc_data.prestr, argv[2]);
03391                 }
03392 
03393                 if( wdbp->dbip->dbi_version < 5 ) {
03394                         if ( bu_vls_strlen(&cc_data.prestr) > 12) {
03395                                 bu_vls_trunc( &cc_data.prestr, 12 );
03396                         }
03397                 }
03398         }
03399 
03400         /* open the input file */
03401         if ((newdbp = db_open(argv[file_index], "r")) == DBI_NULL) {
03402                 perror(argv[file_index]);
03403                 Tcl_AppendResult(interp, "concat: Can't open ",
03404                                  argv[file_index], (char *)NULL);
03405                 return TCL_ERROR;
03406         }
03407 
03408         if( newdbp->dbi_version > 4 && wdbp->dbip->dbi_version < 5 ) {
03409                 Tcl_AppendResult(interp, "concat: databases are incompatible, convert ",
03410                                  wdbp->dbip->dbi_filename, " to version 5 first",
03411                                  (char *)NULL );
03412                 return TCL_ERROR;
03413         }
03414 
03415         db_dirbuild( newdbp );
03416 
03417         cc_data.new_dbip = newdbp;
03418         cc_data.old_dbip = wdbp->dbip;
03419 
03420         /* visit each directory pointer in the input database */
03421         Tcl_InitHashTable( &name_tbl, TCL_STRING_KEYS );
03422         Tcl_InitHashTable( &used_names_tbl, TCL_STRING_KEYS );
03423         FOR_ALL_DIRECTORY_START( dp, newdbp )
03424                 if( dp->d_major_type == DB5_MAJORTYPE_ATTRIBUTE_ONLY ) {
03425                         /* skip GLOBAL object */
03426                         continue;
03427                 }
03428 
03429                 copy_object( interp, dp, newdbp, wdbp->dbip, &name_tbl,
03430                                      &used_names_tbl, &cc_data );
03431         FOR_ALL_DIRECTORY_END;
03432 
03433         bu_vls_free( &cc_data.prestr );
03434         rt_mempurge(&(newdbp->dbi_freep));
03435 
03436         /* Free all the directory entries, and close the input database */
03437         db_close(newdbp);
03438 
03439         db_sync(wdbp->dbip);    /* force changes to disk */
03440 
03441         /* Free the Hash tables */
03442         ptr = Tcl_FirstHashEntry( &name_tbl, &search );
03443         while( ptr ) {
03444                 bu_free( (char *)Tcl_GetHashValue( ptr ), "new name" );
03445                 ptr = Tcl_NextHashEntry( &search );
03446         }
03447         Tcl_DeleteHashTable( &name_tbl );
03448         Tcl_DeleteHashTable( &used_names_tbl );
03449 
03450         return bad ? TCL_ERROR : TCL_OK;
03451 }
03452 
03453 /*
03454  *  Concatenate another GED file into the current file.
03455  *
03456  * Usage:
03457  *        procname concat file.g prefix
03458  */
03459 static int
03460 wdb_concat_tcl(ClientData       clientData,
03461                Tcl_Interp       *interp,
03462                int              argc,
03463                char             **argv)
03464 {
03465         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
03466 
03467         return wdb_concat_cmd(wdbp, interp, argc-1, argv+1);
03468 }
03469 
03470 int
03471 wdb_copyeval_cmd(struct rt_wdb  *wdbp,
03472                  Tcl_Interp     *interp,
03473                  int            argc,
03474                  char           **argv)
03475 {
03476         struct directory        *dp;
03477         struct rt_db_internal   internal, new_int;
03478         mat_t                   start_mat;
03479         int                     id;
03480         int                     i;
03481         int                     endpos;
03482         struct wdb_trace_data   wtd;
03483 
03484         WDB_TCL_CHECK_READ_ONLY;
03485 
03486         if (argc < 3 || 27 < argc) {
03487                 struct bu_vls vls;
03488 
03489                 bu_vls_init(&vls);
03490                 bu_vls_printf(&vls, "helplib_alias wdb_copyeval %s", argv[0]);
03491                 Tcl_Eval(interp, bu_vls_addr(&vls));
03492                 bu_vls_free(&vls);
03493                 return TCL_ERROR;
03494         }
03495 
03496         /* initialize wtd */
03497         wtd.wtd_interp = interp;
03498         wtd.wtd_dbip = wdbp->dbip;
03499         wtd.wtd_flag = WDB_CPEVAL;
03500         wtd.wtd_prflag = 0;
03501 
03502         /* check if new solid name already exists in description */
03503         if (db_lookup(wdbp->dbip, argv[1], LOOKUP_QUIET) != DIR_NULL) {
03504                 Tcl_AppendResult(interp, argv[1], ": already exists\n", (char *)NULL);
03505                 return TCL_ERROR;
03506         }
03507 
03508         MAT_IDN(start_mat);
03509 
03510         /* build directory pointer array for desired path */
03511         if (argc == 3 && strchr(argv[2], '/')) {
03512                 char *tok;
03513 
03514                 endpos = 0;
03515 
03516                 tok = strtok(argv[2], "/");
03517                 while (tok) {
03518                         if ((wtd.wtd_obj[endpos++] = db_lookup(wdbp->dbip, tok, LOOKUP_NOISY)) == DIR_NULL)
03519                                 return TCL_ERROR;
03520                         tok = strtok((char *)NULL, "/");
03521                 }
03522         } else {
03523                 for (i=2; i<argc; i++) {
03524                         if ((wtd.wtd_obj[i-2] = db_lookup(wdbp->dbip, argv[i], LOOKUP_NOISY)) == DIR_NULL)
03525                                 return TCL_ERROR;
03526                 }
03527                 endpos = argc - 2;
03528         }
03529 
03530         wtd.wtd_objpos = endpos - 1;
03531 
03532         /* Make sure that final component in path is a solid */
03533         if ((id = rt_db_get_internal(&internal, wtd.wtd_obj[endpos - 1], wdbp->dbip, bn_mat_identity, &rt_uniresource)) < 0) {
03534                 Tcl_AppendResult(interp, "import failure on ",
03535                                  argv[argc-1], "\n", (char *)NULL);
03536                 return TCL_ERROR;
03537         }
03538 
03539         if (id >= ID_COMBINATION) {
03540                 rt_db_free_internal(&internal, &rt_uniresource);
03541                 Tcl_AppendResult(interp, "final component on path must be a solid!!!\n", (char *)NULL );
03542                 return TCL_ERROR;
03543         }
03544 
03545         wdb_trace(wtd.wtd_obj[0], 0, start_mat, &wtd);
03546 
03547         if (wtd.wtd_prflag == 0) {
03548                 Tcl_AppendResult(interp, "PATH:  ", (char *)NULL);
03549 
03550                 for (i=0; i<wtd.wtd_objpos; i++)
03551                         Tcl_AppendResult(interp, "/", wtd.wtd_obj[i]->d_namep, (char *)NULL);
03552 
03553                 Tcl_AppendResult(interp, "  NOT FOUND\n", (char *)NULL);
03554                 rt_db_free_internal(&internal, &rt_uniresource);
03555                 return TCL_ERROR;
03556         }
03557 
03558         /* Have found the desired path - wdb_xform is the transformation matrix */
03559         /* wdb_xform matrix calculated in wdb_trace() */
03560 
03561         /* create the new solid */
03562         RT_INIT_DB_INTERNAL(&new_int);
03563         if (rt_generic_xform(&new_int, wtd.wtd_xform,
03564                              &internal, 0, wdbp->dbip, &rt_uniresource)) {
03565                 rt_db_free_internal(&internal, &rt_uniresource);
03566                 Tcl_AppendResult(interp, "wdb_copyeval_cmd: rt_generic_xform failed\n", (char *)NULL);
03567                 return TCL_ERROR;
03568         }
03569 
03570         if ((dp=db_diradd(wdbp->dbip, argv[1], -1L, 0,
03571                           wtd.wtd_obj[endpos-1]->d_flags,
03572                           (genptr_t)&new_int.idb_type)) == DIR_NULL) {
03573                 rt_db_free_internal(&internal, &rt_uniresource);
03574                 rt_db_free_internal(&new_int, &rt_uniresource);
03575                 WDB_TCL_ALLOC_ERR_return;
03576         }
03577 
03578         if (rt_db_put_internal(dp, wdbp->dbip, &new_int, &rt_uniresource) < 0) {
03579                 rt_db_free_internal(&internal, &rt_uniresource);
03580                 rt_db_free_internal(&new_int, &rt_uniresource);
03581                 WDB_TCL_WRITE_ERR_return;
03582         }
03583         rt_db_free_internal(&internal, &rt_uniresource);
03584         rt_db_free_internal(&new_int, &rt_uniresource);
03585 
03586         return TCL_OK;
03587 }
03588 
03589 /*
03590  *
03591  *
03592  * Usage:
03593  *        procname copyeval new_solid path_to_solid
03594  */
03595 static int
03596 wdb_copyeval_tcl(ClientData     clientData,
03597                  Tcl_Interp     *interp,
03598                  int            argc,
03599                  char           **argv)
03600 {
03601         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
03602 
03603         return wdb_copyeval_cmd(wdbp, interp, argc-1, argv+1);
03604 }
03605 
03606 BU_EXTERN(int wdb_dir_check, ( struct
03607 db_i *input_dbip, const char *name, long laddr, int len, int flags,
03608 genptr_t ptr));
03609 
03610 struct dir_check_stuff {
03611         struct db_i     *main_dbip;
03612         struct rt_wdb   *wdbp;
03613         struct directory **dup_dirp;
03614 };
03615 
03616 BU_EXTERN(void wdb_dir_check5, ( struct db_i *input_dbip, const struct db5_raw_internal *rip, long addr, genptr_t ptr));
03617 
03618 void
03619 wdb_dir_check5(register struct db_i             *input_dbip,
03620                const struct db5_raw_internal    *rip,
03621                long                             addr,
03622                genptr_t                         ptr)
03623 {
03624         char                    *name;
03625         struct directory        *dupdp;
03626         struct bu_vls           local;
03627         struct dir_check_stuff  *dcsp = (struct dir_check_stuff *)ptr;
03628 
03629         if (dcsp->main_dbip == DBI_NULL)
03630                 return;
03631 
03632         RT_CK_DBI(input_dbip);
03633         RT_CK_RIP( rip );
03634 
03635         if( rip->h_dli == DB5HDR_HFLAGS_DLI_HEADER_OBJECT ) return;
03636         if( rip->h_dli == DB5HDR_HFLAGS_DLI_FREE_STORAGE ) return;
03637 
03638         name = (char *)rip->name.ext_buf;
03639 
03640         if( name == (char *)NULL ) return;
03641 
03642         /* do not compare _GLOBAL */
03643         if( rip->major_type == DB5_MAJORTYPE_ATTRIBUTE_ONLY &&
03644             rip->minor_type == 0 )
03645                 return;
03646 
03647         /* Add the prefix, if any */
03648         bu_vls_init( &local );
03649         if( dcsp->main_dbip->dbi_version < 5 ) {
03650                 if (dcsp->wdbp->wdb_ncharadd > 0) {
03651                         bu_vls_strncpy( &local, bu_vls_addr( &dcsp->wdbp->wdb_prestr ), dcsp->wdbp->wdb_ncharadd );
03652                         bu_vls_strcat( &local, name );
03653                 } else {
03654                         bu_vls_strncpy( &local, name, V4_MAXNAME );
03655                 }
03656                 bu_vls_trunc( &local, V4_MAXNAME );
03657         } else {
03658                 if (dcsp->wdbp->wdb_ncharadd > 0) {
03659                         (void)bu_vls_vlscat( &local, &dcsp->wdbp->wdb_prestr );
03660                         (void)bu_vls_strcat( &local, name );
03661                 } else {
03662                         (void)bu_vls_strcat( &local, name );
03663                 }
03664         }
03665 
03666         /* Look up this new name in the existing (main) database */
03667         if ((dupdp = db_lookup(dcsp->main_dbip, bu_vls_addr( &local ), LOOKUP_QUIET)) != DIR_NULL) {
03668                 /* Duplicate found, add it to the list */
03669                 dcsp->wdbp->wdb_num_dups++;
03670                 *dcsp->dup_dirp++ = dupdp;
03671         }
03672         return;
03673 }
03674 
03675 /*
03676  *                      W D B _ D I R _ C H E C K
03677  *
03678  * Check a name against the global directory.
03679  */
03680 int
03681 wdb_dir_check(register struct db_i *input_dbip, register const char *name, long int laddr, int len, int flags, genptr_t ptr)
03682 {
03683         struct directory        *dupdp;
03684         struct bu_vls           local;
03685         struct dir_check_stuff  *dcsp = (struct dir_check_stuff *)ptr;
03686 
03687         if (dcsp->main_dbip == DBI_NULL)
03688                 return 0;
03689 
03690         RT_CK_DBI(input_dbip);
03691 
03692         /* Add the prefix, if any */
03693         bu_vls_init( &local );
03694         if( dcsp->main_dbip->dbi_version < 5 ) {
03695                 if (dcsp->wdbp->wdb_ncharadd > 0) {
03696                         bu_vls_strncpy( &local, bu_vls_addr( &dcsp->wdbp->wdb_prestr ), dcsp->wdbp->wdb_ncharadd );
03697                         bu_vls_strcat( &local, name );
03698                 } else {
03699                         bu_vls_strncpy( &local, name, V4_MAXNAME );
03700                 }
03701                 bu_vls_trunc( &local, V4_MAXNAME );
03702         } else {
03703                 if (dcsp->wdbp->wdb_ncharadd > 0) {
03704                         bu_vls_vlscat( &local, &dcsp->wdbp->wdb_prestr );
03705                         bu_vls_strcat( &local, name );
03706                 } else {
03707                         bu_vls_strcat( &local, name );
03708                 }
03709         }
03710 
03711         /* Look up this new name in the existing (main) database */
03712         if ((dupdp = db_lookup(dcsp->main_dbip, bu_vls_addr( &local ), LOOKUP_QUIET)) != DIR_NULL) {
03713                 /* Duplicate found, add it to the list */
03714                 dcsp->wdbp->wdb_num_dups++;
03715                 *dcsp->dup_dirp++ = dupdp;
03716         }
03717         bu_vls_free( &local );
03718         return 0;
03719 }
03720 
03721 int
03722 wdb_dup_cmd(struct rt_wdb       *wdbp,
03723             Tcl_Interp          *interp,
03724             int                 argc,
03725             char                **argv)
03726 {
03727         struct db_i             *newdbp = DBI_NULL;
03728         struct directory        **dirp0 = (struct directory **)NULL;
03729         struct bu_vls vls;
03730         struct dir_check_stuff  dcs;
03731 
03732         if (argc < 2 || 3 < argc) {
03733                 struct bu_vls vls;
03734 
03735                 bu_vls_init(&vls);
03736                 bu_vls_printf(&vls, "helplib_alias wdb_dup %s", argv[0]);
03737                 Tcl_Eval(interp, bu_vls_addr(&vls));
03738                 bu_vls_free(&vls);
03739                 return TCL_ERROR;
03740         }
03741 
03742         bu_vls_trunc( &wdbp->wdb_prestr, 0 );
03743         if (argc == 3)
03744                 (void)bu_vls_strcpy(&wdbp->wdb_prestr, argv[2]);
03745 
03746         wdbp->wdb_num_dups = 0;
03747         if( wdbp->dbip->dbi_version < 5 ) {
03748                 if ((wdbp->wdb_ncharadd = bu_vls_strlen(&wdbp->wdb_prestr)) > 12) {
03749                         wdbp->wdb_ncharadd = 12;
03750                         bu_vls_trunc( &wdbp->wdb_prestr, 12 );
03751                 }
03752         } else {
03753                 wdbp->wdb_ncharadd = bu_vls_strlen(&wdbp->wdb_prestr);
03754         }
03755 
03756         /* open the input file */
03757         if ((newdbp = db_open(argv[1], "r")) == DBI_NULL) {
03758                 perror(argv[1]);
03759                 Tcl_AppendResult(interp, "dup: Can't open ", argv[1], (char *)NULL);
03760                 return TCL_ERROR;
03761         }
03762 
03763         Tcl_AppendResult(interp, "\n*** Comparing ",
03764                         wdbp->dbip->dbi_filename,
03765                          "  with ", argv[1], " for duplicate names\n", (char *)NULL);
03766         if (wdbp->wdb_ncharadd) {
03767                 Tcl_AppendResult(interp, "  For comparison, all names in ",
03768                                  argv[1], " were prefixed with:  ",
03769                                  bu_vls_addr( &wdbp->wdb_prestr ), "\n", (char *)NULL);
03770         }
03771 
03772         /* Get array to hold names of duplicates */
03773         if ((dirp0 = wdb_getspace(wdbp->dbip, 0)) == (struct directory **) 0) {
03774                 Tcl_AppendResult(interp, "f_dup: unable to get memory\n", (char *)NULL);
03775                 db_close( newdbp );
03776                 return TCL_ERROR;
03777         }
03778 
03779         /* Scan new database for overlaps */
03780         dcs.main_dbip = wdbp->dbip;
03781         dcs.wdbp = wdbp;
03782         dcs.dup_dirp = dirp0;
03783         if( newdbp->dbi_version < 5 ) {
03784                 if (db_scan(newdbp, wdb_dir_check, 0, (genptr_t)&dcs) < 0) {
03785                         Tcl_AppendResult(interp, "dup: db_scan failure", (char *)NULL);
03786                         bu_free((genptr_t)dirp0, "wdb_getspace array");
03787                         db_close(newdbp);
03788                         return TCL_ERROR;
03789                 }
03790         } else {
03791                 if( db5_scan( newdbp, wdb_dir_check5, (genptr_t)&dcs) < 0) {
03792                         Tcl_AppendResult(interp, "dup: db_scan failure", (char *)NULL);
03793                         bu_free((genptr_t)dirp0, "wdb_getspace array");
03794                         db_close(newdbp);
03795                         return TCL_ERROR;
03796                 }
03797         }
03798         rt_mempurge( &(newdbp->dbi_freep) );        /* didn't really build a directory */
03799 
03800         bu_vls_init(&vls);
03801         wdb_vls_col_pr4v(&vls, dirp0, (int)(dcs.dup_dirp - dirp0), 0);
03802         bu_vls_printf(&vls, "\n -----  %d duplicate names found  -----", wdbp->wdb_num_dups);
03803         Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
03804         bu_vls_free(&vls);
03805         bu_free((genptr_t)dirp0, "wdb_getspace array");
03806         db_close(newdbp);
03807 
03808         return TCL_OK;
03809 }
03810 
03811 /*
03812  * Usage:
03813  *        procname dup file.g [prefix]
03814  */
03815 static int
03816 wdb_dup_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv)
03817 {
03818         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
03819 
03820         return wdb_dup_cmd(wdbp, interp, argc-1, argv+1);
03821 }
03822 
03823 int
03824 wdb_group_cmd(struct rt_wdb     *wdbp,
03825               Tcl_Interp        *interp,
03826               int               argc,
03827               char              **argv)
03828 {
03829         register struct directory *dp;
03830         register int i;
03831 
03832         WDB_TCL_CHECK_READ_ONLY;
03833 
03834         if (argc < 3 || MAXARGS < argc) {
03835                 struct bu_vls vls;
03836 
03837                 bu_vls_init(&vls);
03838                 bu_vls_printf(&vls, "helplib_alias wdb_group %s", argv[0]);
03839                 Tcl_Eval(interp, bu_vls_addr(&vls));
03840                 bu_vls_free(&vls);
03841                 return TCL_ERROR;
03842         }
03843 
03844         /* get objects to add to group */
03845         for (i = 2; i < argc; i++) {
03846                 if ((dp = db_lookup(wdbp->dbip, argv[i], LOOKUP_NOISY)) != DIR_NULL) {
03847                         if (wdb_combadd(interp, wdbp->dbip, dp, argv[1], 0,
03848                                         WMOP_UNION, 0, 0, wdbp) == DIR_NULL)
03849                                 return TCL_ERROR;
03850                 }  else
03851                         Tcl_AppendResult(interp, "skip member ", argv[i], "\n", (char *)NULL);
03852         }
03853         return TCL_OK;
03854 }
03855 
03856 /*
03857  * Usage:
03858  *        procname g groupname object1 object2 .... objectn
03859  */
03860 static int
03861 wdb_group_tcl(ClientData        clientData,
03862               Tcl_Interp        *interp,
03863               int               argc,
03864               char              **argv)
03865 {
03866         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
03867 
03868         return wdb_group_cmd(wdbp, interp, argc-1, argv+1);
03869 }
03870 
03871 int
03872 wdb_remove_cmd(struct rt_wdb    *wdbp,
03873                Tcl_Interp       *interp,
03874                int              argc,
03875                char             **argv)
03876 {
03877         register struct directory       *dp;
03878         register int                    i;
03879         int                             num_deleted;
03880         struct rt_db_internal           intern;
03881         struct rt_comb_internal         *comb;
03882         int                             ret;
03883 
03884         WDB_TCL_CHECK_READ_ONLY;
03885 
03886         if (argc < 3 || MAXARGS < argc) {
03887                 struct bu_vls vls;
03888 
03889                 bu_vls_init(&vls);
03890                 bu_vls_printf(&vls, "helplib_alias wdb_remove %s", argv[0]);
03891                 Tcl_Eval(interp, bu_vls_addr(&vls));
03892                 bu_vls_free(&vls);
03893                 return TCL_ERROR;
03894         }
03895 
03896         if ((dp = db_lookup(wdbp->dbip,  argv[1], LOOKUP_NOISY)) == DIR_NULL)
03897                 return TCL_ERROR;
03898 
03899         if ((dp->d_flags & DIR_COMB) == 0) {
03900                 Tcl_AppendResult(interp, "rm: ", dp->d_namep,
03901                                  " is not a combination", (char *)NULL );
03902                 return TCL_ERROR;
03903         }
03904 
03905         if (rt_db_get_internal(&intern, dp, wdbp->dbip, (fastf_t *)NULL, &rt_uniresource) < 0) {
03906                 Tcl_AppendResult(interp, "Database read error, aborting", (char *)NULL);
03907                 return TCL_ERROR;
03908         }
03909 
03910         comb = (struct rt_comb_internal *)intern.idb_ptr;
03911         RT_CK_COMB(comb);
03912 
03913         /* Process each argument */
03914         num_deleted = 0;
03915         ret = TCL_OK;
03916         for (i = 2; i < argc; i++) {
03917                 if (db_tree_del_dbleaf( &(comb->tree), argv[i], &rt_uniresource ) < 0) {
03918                         Tcl_AppendResult(interp, "  ERROR_deleting ",
03919                                          dp->d_namep, "/", argv[i],
03920                                          "\n", (char *)NULL);
03921                         ret = TCL_ERROR;
03922                 } else {
03923                         Tcl_AppendResult(interp, "deleted ",
03924                                          dp->d_namep, "/", argv[i],
03925                                          "\n", (char *)NULL);
03926                         num_deleted++;
03927                 }
03928         }
03929 
03930         if (rt_db_put_internal(dp, wdbp->dbip, &intern, &rt_uniresource) < 0) {
03931                 Tcl_AppendResult(interp, "Database write error, aborting", (char *)NULL);
03932                 return TCL_ERROR;
03933         }
03934 
03935         return ret;
03936 }
03937 
03938 /*
03939  * Remove members from a combination.
03940  *
03941  * Usage:
03942  *        procname remove comb object(s)
03943  */
03944 static int
03945 wdb_remove_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv)
03946 {
03947         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
03948 
03949         return wdb_remove_cmd(wdbp, interp, argc-1, argv+1);
03950 }
03951 
03952 int
03953 wdb_region_cmd(struct rt_wdb    *wdbp,
03954                Tcl_Interp       *interp,
03955                int              argc,
03956                char             **argv)
03957 {
03958         register struct directory       *dp;
03959         int                             i;
03960         int                             ident, air;
03961         char                            oper;
03962 
03963         WDB_TCL_CHECK_READ_ONLY;
03964 
03965         if (argc < 4 || MAXARGS < argc) {
03966                 struct bu_vls vls;
03967 
03968                 bu_vls_init(&vls);
03969                 bu_vls_printf(&vls, "helplib_alias wdb_region %s", argv[0]);
03970                 Tcl_Eval(interp, bu_vls_addr(&vls));
03971                 bu_vls_free(&vls);
03972                 return TCL_ERROR;
03973         }
03974 
03975         ident = wdbp->wdb_item_default;
03976         air = wdbp->wdb_air_default;
03977 
03978         /* Check for even number of arguments */
03979         if (argc & 01) {
03980                 Tcl_AppendResult(interp, "error in number of args!", (char *)NULL);
03981                 return TCL_ERROR;
03982         }
03983 
03984         if (db_lookup(wdbp->dbip, argv[1], LOOKUP_QUIET) == DIR_NULL) {
03985                 /* will attempt to create the region */
03986                 if (wdbp->wdb_item_default) {
03987                         struct bu_vls tmp_vls;
03988 
03989                         wdbp->wdb_item_default++;
03990                         bu_vls_init(&tmp_vls);
03991                         bu_vls_printf(&tmp_vls, "Defaulting item number to %d\n",
03992                                 wdbp->wdb_item_default);
03993                         Tcl_AppendResult(interp, bu_vls_addr(&tmp_vls), (char *)NULL);
03994                         bu_vls_free(&tmp_vls);
03995                 }
03996         }
03997 
03998         /* Get operation and solid name for each solid */
03999         for (i = 2; i < argc; i += 2) {
04000                 if (argv[i][1] != '\0') {
04001                         Tcl_AppendResult(interp, "bad operation: ", argv[i],
04002                                          " skip member: ", argv[i+1], "\n", (char *)NULL);
04003                         continue;
04004                 }
04005                 oper = argv[i][0];
04006                 if ((dp = db_lookup(wdbp->dbip,  argv[i+1], LOOKUP_NOISY )) == DIR_NULL) {
04007                         Tcl_AppendResult(interp, "skipping ", argv[i+1], "\n", (char *)NULL);
04008                         continue;
04009                 }
04010 
04011                 if (oper != WMOP_UNION && oper != WMOP_SUBTRACT && oper != WMOP_INTERSECT) {
04012                         struct bu_vls tmp_vls;
04013 
04014                         bu_vls_init(&tmp_vls);
04015                         bu_vls_printf(&tmp_vls, "bad operation: %c skip member: %s\n",
04016                                       oper, dp->d_namep );
04017                         Tcl_AppendResult(interp, bu_vls_addr(&tmp_vls), (char *)NULL);
04018                         bu_vls_free(&tmp_vls);
04019                         continue;
04020                 }
04021 
04022                 /* Adding region to region */
04023                 if (dp->d_flags & DIR_REGION) {
04024                         Tcl_AppendResult(interp, "Note: ", dp->d_namep,
04025                                          " is a region\n", (char *)NULL);
04026                 }
04027 
04028                 if (wdb_combadd(interp, wdbp->dbip, dp,
04029                                 argv[1], 1, oper, ident, air, wdbp) == DIR_NULL) {
04030                         Tcl_AppendResult(interp, "error in combadd", (char *)NULL);
04031                         return TCL_ERROR;
04032                 }
04033         }
04034 
04035         if (db_lookup(wdbp->dbip, argv[1], LOOKUP_QUIET) == DIR_NULL) {
04036                 /* failed to create region */
04037                 if (wdbp->wdb_item_default > 1)
04038                         wdbp->wdb_item_default--;
04039                 return TCL_ERROR;
04040         }
04041 
04042         return TCL_OK;
04043 }
04044 
04045 /*
04046  * Usage:
04047  *        procname r rname object(s)
04048  */
04049 static int
04050 wdb_region_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv)
04051 {
04052         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
04053 
04054         return wdb_region_cmd(wdbp, interp, argc-1, argv+1);
04055 }
04056 
04057 int
04058 wdb_comb_cmd(struct rt_wdb      *wdbp,
04059              Tcl_Interp         *interp,
04060              int                argc,
04061              char               **argv)
04062 {
04063         register struct directory *dp;
04064         char    *comb_name;
04065         register int    i;
04066         char    oper;
04067 
04068         WDB_TCL_CHECK_READ_ONLY;
04069 
04070         if (argc < 4 || MAXARGS < argc) {
04071                 struct bu_vls vls;
04072 
04073                 bu_vls_init(&vls);
04074                 bu_vls_printf(&vls, "helplib_alias wdb_comb %s", argv[0]);
04075                 Tcl_Eval(interp, bu_vls_addr(&vls));
04076                 bu_vls_free(&vls);
04077                 return TCL_ERROR;
04078         }
04079 
04080         /* Check for odd number of arguments */
04081         if (argc & 01) {
04082                 Tcl_AppendResult(interp, "error in number of args!", (char *)NULL);
04083                 return TCL_ERROR;
04084         }
04085 
04086         /* Save combination name, for use inside loop */
04087         comb_name = argv[1];
04088         if ((dp=db_lookup(wdbp->dbip, comb_name, LOOKUP_QUIET)) != DIR_NULL) {
04089                 if (!(dp->d_flags & DIR_COMB)) {
04090                         Tcl_AppendResult(interp,
04091                                          "ERROR: ", comb_name,
04092                                          " is not a combination", (char *)0 );
04093                         return TCL_ERROR;
04094                 }
04095         }
04096 
04097         /* Get operation and solid name for each solid */
04098         for (i = 2; i < argc; i += 2) {
04099                 if (argv[i][1] != '\0') {
04100                         Tcl_AppendResult(interp, "bad operation: ", argv[i],
04101                                          " skip member: ", argv[i+1], "\n", (char *)NULL);
04102                         continue;
04103                 }
04104                 oper = argv[i][0];
04105                 if ((dp = db_lookup(wdbp->dbip,  argv[i+1], LOOKUP_NOISY)) == DIR_NULL) {
04106                         Tcl_AppendResult(interp, "skipping ", argv[i+1], "\n", (char *)NULL);
04107                         continue;
04108                 }
04109 
04110                 if (oper != WMOP_UNION && oper != WMOP_SUBTRACT && oper != WMOP_INTERSECT) {
04111                         struct bu_vls tmp_vls;
04112 
04113                         bu_vls_init(&tmp_vls);
04114                         bu_vls_printf(&tmp_vls, "bad operation: %c skip member: %s\n",
04115                                       oper, dp->d_namep);
04116                         Tcl_AppendResult(interp, bu_vls_addr(&tmp_vls), (char *)NULL);
04117                         continue;
04118                 }
04119 
04120                 if (wdb_combadd(interp, wdbp->dbip, dp, comb_name, 0, oper, 0, 0, wdbp) == DIR_NULL) {
04121                         Tcl_AppendResult(interp, "error in combadd", (char *)NULL);
04122                         return TCL_ERROR;
04123                 }
04124         }
04125 
04126         if (db_lookup(wdbp->dbip, comb_name, LOOKUP_QUIET) == DIR_NULL) {
04127                 Tcl_AppendResult(interp, "Error:  ", comb_name,
04128                                  " not created", (char *)NULL);
04129                 return TCL_ERROR;
04130         }
04131 
04132         return TCL_OK;
04133 }
04134 
04135 /*
04136  * Create or add to the end of a combination, with one or more solids,
04137  * with explicitly specified operations.
04138  *
04139  * Usage:
04140  *        procname comb comb_name opr1 sol1 opr2 sol2 ... oprN solN
04141  */
04142 static int
04143 wdb_comb_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv)
04144 {
04145         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
04146 
04147         return wdb_comb_cmd(wdbp, interp, argc-1, argv+1);
04148 }
04149 
04150 static void
04151 wdb_find_ref(struct db_i                *dbip,
04152              struct rt_comb_internal    *comb,
04153              union tree                 *comb_leaf,
04154              genptr_t                   object,
04155              genptr_t                   comb_name_ptr,
04156              genptr_t                   user_ptr3)
04157 {
04158         char *obj_name;
04159         char *comb_name;
04160         Tcl_Interp *interp = (Tcl_Interp *)user_ptr3;
04161 
04162         RT_CK_TREE(comb_leaf);
04163 
04164         obj_name = (char *)object;
04165         if (strcmp(comb_leaf->tr_l.tl_name, obj_name))
04166                 return;
04167 
04168         comb_name = (char *)comb_name_ptr;
04169 
04170         Tcl_AppendElement(interp, comb_name);
04171 }
04172 
04173 HIDDEN union tree *
04174 facetize_region_end( tsp, pathp, curtree, client_data )
04175 register struct db_tree_state   *tsp;
04176 struct db_full_path     *pathp;
04177 union tree              *curtree;
04178 genptr_t                client_data;
04179 {
04180         struct bu_list          vhead;
04181         union tree              **facetize_tree;
04182 
04183         facetize_tree = (union tree **)client_data;
04184         BU_LIST_INIT( &vhead );
04185 
04186         if( curtree->tr_op == OP_NOP )  return  curtree;
04187 
04188         if( *facetize_tree )  {
04189                 union tree      *tr;
04190                 tr = (union tree *)bu_calloc(1, sizeof(union tree), "union tree");
04191                 tr->magic = RT_TREE_MAGIC;
04192                 tr->tr_op = OP_UNION;
04193                 tr->tr_b.tb_regionp = REGION_NULL;
04194                 tr->tr_b.tb_left = *facetize_tree;
04195                 tr->tr_b.tb_right = curtree;
04196                 *facetize_tree = tr;
04197         } else {
04198                 *facetize_tree = curtree;
04199         }
04200 
04201         /* Tree has been saved, and will be freed later */
04202         return( TREE_NULL );
04203 }
04204 
04205 int
04206 wdb_facetize_cmd(struct rt_wdb  *wdbp,
04207              Tcl_Interp         *interp,
04208              int                argc,
04209              char               **argv)
04210 {
04211         int                     i;
04212         register int            c;
04213         int                     triangulate;
04214         char                    *newname;
04215         struct rt_db_internal   intern;
04216         struct directory        *dp;
04217         int                     failed;
04218         int                     nmg_use_tnurbs = 0;
04219         int                     make_bot;
04220         struct db_tree_state    init_state;
04221         struct db_i             *dbip;
04222         union tree              *facetize_tree;
04223         struct model            *nmg_model;
04224 
04225         if(argc < 3){
04226                 Tcl_AppendResult(interp,
04227                                  "Usage: ",
04228                                  argv[0],
04229                                  " new_object old_object [old_object2 old_object3 ...]\n",
04230                                  (char *)NULL );
04231           return TCL_ERROR;
04232         }
04233 
04234         dbip = wdbp->dbip;
04235         RT_CHECK_DBI(dbip);
04236 
04237         db_init_db_tree_state( &init_state, dbip, wdbp->wdb_resp );
04238 
04239         /* Establish tolerances */
04240         init_state.ts_ttol = &wdbp->wdb_ttol;
04241         init_state.ts_tol = &wdbp->wdb_tol;
04242 
04243         /* Initial vaues for options, must be reset each time */
04244         triangulate = 0;
04245         make_bot = 1;
04246 
04247         /* Parse options. */
04248         bu_optind = 1;          /* re-init bu_getopt() */
04249         while( (c=bu_getopt(argc,argv,"ntT")) != EOF )  {
04250                 switch(c)  {
04251                 case 'n':
04252                         make_bot = 0;
04253                         break;
04254                 case 'T':
04255                         triangulate = 1;
04256                         break;
04257                 case 't':
04258                         nmg_use_tnurbs = 1;
04259                         break;
04260                 default:
04261                   {
04262                     struct bu_vls tmp_vls;
04263 
04264                     bu_vls_init(&tmp_vls);
04265                     bu_vls_printf(&tmp_vls, "option '%c' unknown\n", c);
04266                     Tcl_AppendResult(interp, bu_vls_addr(&tmp_vls),
04267                                      "Usage: facetize [-ntT] object(s)\n",
04268                                      "\t-n make NMG primitives rather than BOT's\n",
04269                                      "\t-t Perform CSG-to-tNURBS conversion\n",
04270                                      "\t-T enable triangulator\n", (char *)NULL);
04271                     bu_vls_free(&tmp_vls);
04272                   }
04273                   break;
04274                 }
04275         }
04276         argc -= bu_optind;
04277         argv += bu_optind;
04278         if( argc < 0 ){
04279           Tcl_AppendResult(interp, "facetize: missing argument\n", (char *)NULL);
04280           return TCL_ERROR;
04281         }
04282 
04283         newname = argv[0];
04284         argv++;
04285         argc--;
04286         if( argc < 0 ){
04287           Tcl_AppendResult(interp, "facetize: missing argument\n", (char *)NULL);
04288           return TCL_ERROR;
04289         }
04290 
04291         if( db_lookup( dbip, newname, LOOKUP_QUIET ) != DIR_NULL )  {
04292           Tcl_AppendResult(interp, "error: solid '", newname,
04293                            "' already exists, aborting\n", (char *)NULL);
04294           return TCL_ERROR;
04295         }
04296 
04297         {
04298           struct bu_vls tmp_vls;
04299 
04300           bu_vls_init(&tmp_vls);
04301           bu_vls_printf(&tmp_vls,
04302                         "facetize:  tessellating primitives with tolerances a=%g, r=%g, n=%g\n",
04303                         wdbp->wdb_ttol.abs, wdbp->wdb_ttol.rel, wdbp->wdb_ttol.norm );
04304           Tcl_AppendResult(interp, bu_vls_addr(&tmp_vls), (char *)NULL);
04305           bu_vls_free(&tmp_vls);
04306         }
04307         facetize_tree = (union tree *)0;
04308         nmg_model = nmg_mm();
04309         init_state.ts_m = &nmg_model;
04310 
04311         i = db_walk_tree( dbip, argc, (const char **)argv,
04312                 1,
04313                 &init_state,
04314                 0,                      /* take all regions */
04315                 facetize_region_end,
04316                 nmg_use_tnurbs ?
04317                         nmg_booltree_leaf_tnurb :
04318                         nmg_booltree_leaf_tess,
04319                 (genptr_t)&facetize_tree
04320                 );
04321 
04322 
04323         if( i < 0 )  {
04324           Tcl_AppendResult(interp, "facetize: error in db_walk_tree()\n", (char *)NULL);
04325           /* Destroy NMG */
04326           nmg_km( nmg_model );
04327           return TCL_ERROR;
04328         }
04329 
04330         if( facetize_tree )
04331         {
04332                 /* Now, evaluate the boolean tree into ONE region */
04333                 Tcl_AppendResult(interp, "facetize:  evaluating boolean expressions\n", (char *)NULL);
04334 
04335                 if( BU_SETJUMP )
04336                 {
04337                         BU_UNSETJUMP;
04338                         Tcl_AppendResult(interp, "WARNING: facetization failed!!!\n", (char *)NULL );
04339                         if( facetize_tree )
04340                                 db_free_tree( facetize_tree, &rt_uniresource );
04341                         facetize_tree = (union tree *)NULL;
04342                         nmg_km( nmg_model );
04343                         nmg_model = (struct model *)NULL;
04344                         return TCL_ERROR;
04345                 }
04346 
04347                 failed = nmg_boolean( facetize_tree, nmg_model, &wdbp->wdb_tol, &rt_uniresource );
04348                 BU_UNSETJUMP;
04349         }
04350         else
04351                 failed = 1;
04352 
04353         if( failed )  {
04354           Tcl_AppendResult(interp, "facetize:  no resulting region, aborting\n", (char *)NULL);
04355           if( facetize_tree )
04356                 db_free_tree( facetize_tree, &rt_uniresource );
04357           facetize_tree = (union tree *)NULL;
04358           nmg_km( nmg_model );
04359           nmg_model = (struct model *)NULL;
04360           return TCL_ERROR;
04361         }
04362         /* New region remains part of this nmg "model" */
04363         NMG_CK_REGION( facetize_tree->tr_d.td_r );
04364         Tcl_AppendResult(interp, "facetize:  ", facetize_tree->tr_d.td_name,
04365                          "\n", (char *)NULL);
04366 
04367         /* Triangulate model, if requested */
04368         if( triangulate && !make_bot )
04369         {
04370                 Tcl_AppendResult(interp, "facetize:  triangulating resulting object\n", (char *)NULL);
04371                 if( BU_SETJUMP )
04372                 {
04373                         BU_UNSETJUMP;
04374                         Tcl_AppendResult(interp, "WARNING: triangulation failed!!!\n", (char *)NULL );
04375                         if( facetize_tree )
04376                                 db_free_tree( facetize_tree, &rt_uniresource );
04377                         facetize_tree = (union tree *)NULL;
04378                         nmg_km( nmg_model );
04379                         nmg_model = (struct model *)NULL;
04380                         return TCL_ERROR;
04381                 }
04382                 nmg_triangulate_model( nmg_model , &wdbp->wdb_tol );
04383                 BU_UNSETJUMP;
04384         }
04385 
04386         if( make_bot )
04387         {
04388                 struct rt_bot_internal *bot;
04389                 struct nmgregion *r;
04390                 struct shell *s;
04391 
04392                 Tcl_AppendResult(interp, "facetize:  converting to BOT format\n", (char *)NULL);
04393 
04394                 r = BU_LIST_FIRST( nmgregion, &nmg_model->r_hd );
04395                 s = BU_LIST_FIRST( shell, &r->s_hd );
04396                 bot = (struct rt_bot_internal *)nmg_bot( s, &wdbp->wdb_tol );
04397                 nmg_km( nmg_model );
04398                 nmg_model = (struct model *)NULL;
04399 
04400                 /* Export BOT as a new solid */
04401                 RT_INIT_DB_INTERNAL(&intern);
04402                 intern.idb_major_type = DB5_MAJORTYPE_BRLCAD;
04403                 intern.idb_type = ID_BOT;
04404                 intern.idb_meth = &rt_functab[ID_BOT];
04405                 intern.idb_ptr = (genptr_t) bot;
04406         }
04407         else
04408         {
04409 
04410                 Tcl_AppendResult(interp, "facetize:  converting NMG to database format\n", (char *)NULL);
04411 
04412                 /* Export NMG as a new solid */
04413                 RT_INIT_DB_INTERNAL(&intern);
04414                 intern.idb_major_type = DB5_MAJORTYPE_BRLCAD;
04415                 intern.idb_type = ID_NMG;
04416                 intern.idb_meth = &rt_functab[ID_NMG];
04417                 intern.idb_ptr = (genptr_t)nmg_model;
04418                 nmg_model = (struct model *)NULL;
04419         }
04420 
04421         if( (dp=db_diradd( dbip, newname, -1L, 0, DIR_SOLID, (genptr_t)&intern.idb_type)) == DIR_NULL )
04422         {
04423                 Tcl_AppendResult(interp, "Cannot add ", newname, " to directory\n", (char *)NULL );
04424                 return TCL_ERROR;
04425         }
04426 
04427         if( rt_db_put_internal( dp, dbip, &intern, &rt_uniresource ) < 0 )
04428         {
04429                 Tcl_AppendResult(interp, "Failed to write ", newname, " to database\n", (char *)NULL );
04430                 rt_db_free_internal( &intern, &rt_uniresource );
04431                 return TCL_ERROR;
04432         }
04433 
04434         facetize_tree->tr_d.td_r = (struct nmgregion *)NULL;
04435 
04436         /* Free boolean tree, and the regions in it */
04437         db_free_tree( facetize_tree, &rt_uniresource );
04438         facetize_tree = (union tree *)NULL;
04439 
04440         return TCL_OK;
04441 }
04442 
04443 int
04444 wdb_find_cmd(struct rt_wdb      *wdbp,
04445              Tcl_Interp         *interp,
04446              int                argc,
04447              char               **argv)
04448 {
04449     register int                                i,k;
04450     register struct directory           *dp;
04451     struct rt_db_internal                       intern;
04452     register struct rt_comb_internal    *comb=(struct rt_comb_internal *)NULL;
04453     struct bu_vls vls;
04454     int c;
04455     int aflag = 0;              /* look at all objects */
04456 
04457     if (argc < 2 || MAXARGS < argc) {
04458         bu_vls_init(&vls);
04459         bu_vls_printf(&vls, "helplib_alias wdb_find %s", argv[0]);
04460         Tcl_Eval(interp, bu_vls_addr(&vls));
04461         bu_vls_free(&vls);
04462         return TCL_ERROR;
04463     }
04464 
04465     bu_optind = 1;      /* re-init bu_getopt() */
04466     while ((c = bu_getopt(argc, argv, "a")) != EOF) {
04467         switch (c) {
04468         case 'a':
04469             aflag = 1;
04470             break;
04471         default:
04472             bu_vls_init(&vls);
04473             bu_vls_printf(&vls, "Unrecognized option - %c", c);
04474             Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
04475             bu_vls_free(&vls);
04476             return TCL_ERROR;
04477         }
04478     }
04479     argc -= (bu_optind - 1);
04480     argv += (bu_optind - 1);
04481 
04482     /* Examine all COMB nodes */
04483     for (i = 0; i < RT_DBNHASH; i++) {
04484         for (dp = wdbp->dbip->dbi_Head[i]; dp != DIR_NULL; dp = dp->d_forw) {
04485             if (!(dp->d_flags & DIR_COMB) ||
04486                 (!aflag && (dp->d_flags & DIR_HIDDEN)))
04487                 continue;
04488 
04489             if (rt_db_get_internal(&intern,
04490                                    dp,
04491                                    wdbp->dbip,
04492                                    (fastf_t *)NULL,
04493                                    &rt_uniresource) < 0) {
04494                 Tcl_AppendResult(interp, "Database read error, aborting", (char *)NULL);
04495                 return TCL_ERROR;
04496             }
04497 
04498             comb = (struct rt_comb_internal *)intern.idb_ptr;
04499             for (k=1; k<argc; k++)
04500                 db_tree_funcleaf(wdbp->dbip,
04501                                  comb,
04502                                  comb->tree,
04503                                  wdb_find_ref,
04504                                  (genptr_t)argv[k],
04505                                  (genptr_t)dp->d_namep,
04506                                  (genptr_t)interp);
04507 
04508             rt_db_free_internal(&intern, &rt_uniresource);
04509         }
04510     }
04511 
04512     return TCL_OK;
04513 }
04514 
04515 static int
04516 wdb_facetize_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv)
04517 {
04518         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
04519 
04520         return wdb_facetize_cmd(wdbp, interp, argc-1, argv+1);
04521 }
04522 
04523 
04524 /*
04525  * Usage:
04526  *        procname find object(s)
04527  */
04528 static int
04529 wdb_find_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv)
04530 {
04531         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
04532 
04533         return wdb_find_cmd(wdbp, interp, argc-1, argv+1);
04534 }
04535 
04536 struct wdb_id_names {
04537         struct bu_list l;
04538         struct bu_vls name;             /* name associated with region id */
04539 };
04540 
04541 struct wdb_id_to_names {
04542         struct bu_list l;
04543         int id;                         /* starting id (i.e. region id or air code) */
04544         struct wdb_id_names headName;   /* head of list of names */
04545 };
04546 
04547 int
04548 wdb_rmap_cmd(struct rt_wdb      *wdbp,
04549              Tcl_Interp         *interp,
04550              int                argc,
04551              char               **argv)
04552 {
04553     register int i;
04554     register struct directory *dp;
04555     struct rt_db_internal intern;
04556     struct rt_comb_internal *comb;
04557     struct wdb_id_to_names headIdName;
04558     struct wdb_id_to_names *itnp;
04559     struct wdb_id_names *inp;
04560     Tcl_DString ds;
04561 
04562 
04563     if (argc != 1) {
04564         struct bu_vls vls;
04565 
04566         bu_vls_init(&vls);
04567         bu_vls_printf(&vls, "helplib_alias wdb_rmap %s", argv[0]);
04568         Tcl_Eval(interp, bu_vls_addr(&vls));
04569         bu_vls_free(&vls);
04570         return TCL_ERROR;
04571     }
04572 
04573     Tcl_DStringInit(&ds);
04574 
04575     if (wdbp->dbip->dbi_version < 5) {
04576         Tcl_DStringAppend(&ds, argv[0], -1);
04577         Tcl_DStringAppend(&ds, " is not available prior to dbversion 5\n", -1);
04578         Tcl_DStringResult(interp, &ds);
04579         return TCL_ERROR;
04580     }
04581 
04582     BU_LIST_INIT(&headIdName.l);
04583 
04584     /* For all regions not hidden */
04585     for (i = 0; i < RT_DBNHASH; i++) {
04586         for (dp = wdbp->dbip->dbi_Head[i]; dp != DIR_NULL; dp = dp->d_forw) {
04587             int found = 0;
04588 
04589             if (!(dp->d_flags & DIR_REGION) ||
04590                 (dp->d_flags & DIR_HIDDEN))
04591                 continue;
04592 
04593             if (rt_db_get_internal(&intern,
04594                                    dp,
04595                                    wdbp->dbip,
04596                                    (fastf_t *)NULL,
04597                                    &rt_uniresource) < 0) {
04598                 Tcl_DStringAppend(&ds, "Database read error, aborting", -1);
04599                 Tcl_DStringResult(interp, &ds);
04600                 return TCL_ERROR;
04601             }
04602 
04603             comb = (struct rt_comb_internal *)intern.idb_ptr;
04604             /* check to see if the region id or air code matches one in our list */
04605             for (BU_LIST_FOR(itnp,wdb_id_to_names,&headIdName.l)) {
04606                 if ((comb->region_id == itnp->id) ||
04607                     (comb->aircode != 0 && -comb->aircode == itnp->id)) {
04608                     /* add region name to our name list for this region */
04609                     BU_GETSTRUCT(inp,wdb_id_names);
04610                     bu_vls_init(&inp->name);
04611                     bu_vls_strcpy(&inp->name, dp->d_namep);
04612                     BU_LIST_INSERT(&itnp->headName.l,&inp->l);
04613                     found = 1;
04614                     break;
04615                 }
04616             }
04617 
04618             if (!found) {
04619                 /* create new id_to_names node */
04620                 BU_GETSTRUCT(itnp,wdb_id_to_names);
04621                 if (0 < comb->region_id)
04622                     itnp->id = comb->region_id;
04623                 else
04624                     itnp->id = -comb->aircode;
04625                 BU_LIST_INSERT(&headIdName.l,&itnp->l);
04626                 BU_LIST_INIT(&itnp->headName.l);
04627 
04628                 /* add region name to our name list for this region */
04629                 BU_GETSTRUCT(inp,wdb_id_names);
04630                 bu_vls_init(&inp->name);
04631                 bu_vls_strcpy(&inp->name, dp->d_namep);
04632                 BU_LIST_INSERT(&itnp->headName.l,&inp->l);
04633             }
04634 
04635 #if USE_RT_COMB_IFREE
04636             rt_comb_ifree(&intern, &rt_uniresource);
04637 #else
04638             rt_db_free_internal(&intern, &rt_uniresource);
04639 #endif
04640         }
04641     }
04642 
04643     /* place data in a dynamic tcl string */
04644     while (BU_LIST_WHILE(itnp,wdb_id_to_names,&headIdName.l)) {
04645         char buf[32];
04646 
04647         /* add this id to the list */
04648         sprintf(buf, "%d", itnp->id);
04649         Tcl_DStringAppendElement(&ds, buf);
04650 
04651         /* start sublist of names associated with this id */
04652         Tcl_DStringStartSublist(&ds);
04653         while (BU_LIST_WHILE(inp,wdb_id_names,&itnp->headName.l)) {
04654             /* add the this name to this sublist */
04655             Tcl_DStringAppendElement(&ds, bu_vls_addr(&inp->name));
04656 
04657             BU_LIST_DEQUEUE(&inp->l);
04658             bu_vls_free(&inp->name);
04659             bu_free((genptr_t)inp, "rmap: inp");
04660         }
04661         Tcl_DStringEndSublist(&ds);
04662 
04663         BU_LIST_DEQUEUE(&itnp->l);
04664         bu_free((genptr_t)itnp, "rmap: itnp");
04665     }
04666 
04667     Tcl_DStringResult(interp, &ds);
04668     return TCL_OK;
04669 }
04670 
04671 /*
04672  * Usage:
04673  *        procname rmap
04674  */
04675 static int
04676 wdb_rmap_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv)
04677 {
04678         struct rt_wdb   *wdbp = (struct rt_wdb *)clientData;
04679 
04680         return wdb_rmap_cmd(wdbp, interp, argc-1, argv+1);
04681 }
04682 
04683 int
04684 wdb_which_cmd(struct rt_wdb     *wdbp,
04685               Tcl_Interp        *interp,
04686               int               argc,
04687               char              **argv)
04688 {
04689         register int    i,j;
04690         register struct directory *dp;
04691         struct rt_db_internal intern;
04692         struct rt_comb_internal *comb;
04693         struct wdb_id_to_names headIdName;
04694         struct wdb_id_to_names *itnp;
04695         struct wdb_id_names *inp;
04696         int isAir;
04697         int sflag;
04698 
04699 
04700         if (argc < 2 || MAXARGS < argc) {
04701                 struct bu_vls vls;
04702 
04703                 bu_vls_init(&vls);
04704                 bu_vls_printf(&vls, "helplib_alias wdb_%s %s", argv[0], argv[0]);
04705                 Tcl_Eval(interp, bu_vls_addr(&vls));
04706                 bu_vls_free(&vls);
04707                 return TCL_ERROR;
04708         }
04709 
04710         if (!strcmp(argv[0], "whichair"))
04711                 isAir = 1;
04712         else
04713                 isAir = 0;
04714 
04715         if (strcmp(argv[1], "-s") == 0) {
04716                 --argc;
04717                 ++argv;
04718 
04719                 if (argc < 2) {
04720                         struct bu_vls vls;
04721 
04722                         bu_vls_init(&vls);
04723                         bu_vls_printf(&vls, "helplib_alias wdb_%s %s", argv[-1], argv[-1]);
04724                         Tcl_Eval(interp, bu_vls_addr(&vls));
04725                         bu_vls_free(&vls);
04726                         return TCL_ERROR;
04727                 }
04728 
04729                 sflag = 1;
04730         } else {
04731                 sflag = 0;
04732         }
04733 
04734         BU_LIST_INIT(&headIdName.l);
04735 
04736         /* Build list of id_to_names */
04737         for (j=1; j<argc; j++) {
04738                 int n;
04739                 int start, end;
04740                 int range;
04741                 int k;
04742 
04743                 n = sscanf(argv[j], "%d%*[:-]%d", &start, &end);
04744                 switch (n) {
04745                 case 1:
04746                         for (BU_LIST_FOR(itnp,wdb_id_to_names,&headIdName.l))
04747                                 if (itnp->id == start)
04748                                         break;
04749 
04750                         /* id not found */
04751                         if (BU_LIST_IS_HEAD(itnp,&headIdName.l)) {
04752                                 BU_GETSTRUCT(itnp,wdb_id_to_names);
04753                                 itnp->id = start;
04754                                 BU_LIST_INSERT(&headIdName.l,&itnp->l);
04755                                 BU_LIST_INIT(&itnp->headName.l);
04756                         }
04757 
04758                         break;
04759                 case 2:
04760                         if (start < end)
04761                                 range = end - start + 1;
04762                         else if (end < start) {
04763                                 range = start - end + 1;
04764                                 start = end;
04765                         } else
04766                                 range = 1;
04767 
04768                         for (k = 0; k < range; ++k) {
04769                                 int id = start + k;
04770 
04771                                 for (BU_LIST_FOR(itnp,wdb_id_to_names,&headIdName.l))
04772                                         if (itnp->id == id)
04773                                                 break;
04774 
04775                                 /* id not found */
04776                                 if (BU_LIST_IS_HEAD(itnp,&headIdName.l)) {
04777                                         BU_GETSTRUCT(itnp,wdb_id_to_names);
04778                                         itnp->id = id;
04779                                         BU_LIST_INSERT(&headIdName.l,&itnp->l);
04780                                         BU_LIST_INIT(&itnp->headName.l);
04781                                 }
04782                         }
04783 
04784                         break;
04785                 }
04786         }
04787 
04788         /* Examine all COMB nodes */
04789         for (i = 0; i < RT_DBNHASH; i++) {
04790                 for (dp = wdbp->dbip->dbi_Head[i]; dp != DIR_NULL; dp = dp->d_forw) {
04791                         if (!(dp->d_flags & DIR_REGION))
04792                                 continue;
04793 
04794                         if (rt_db_get_internal( &intern, dp, wdbp->dbip, (fastf_t *)NULL, &rt_uniresource ) < 0) {
04795                                 Tcl_AppendResult(interp, "Database read error, aborting", (char *)NULL);
04796                                 return TCL_ERROR;
04797                         }
04798                         comb = (struct rt_comb_internal *)intern.idb_ptr;
04799                         /* check to see if the region id or air code matches one in our list */
04800                         for (BU_LIST_FOR(itnp,wdb_id_to_names,&headIdName.l)) {
04801                                 if ((!isAir && comb->region_id == itnp->id) ||
04802                                     (isAir && comb->aircode == itnp->id)) {
04803                                         /* add region name to our name list for this region */
04804                                         BU_GETSTRUCT(inp,wdb_id_names);
04805                                         bu_vls_init(&inp->name);
04806                                         bu_vls_strcpy(&inp->name, dp->d_namep);
04807                                         BU_LIST_INSERT(&itnp->headName.l,&inp->l);
04808                                         break;
04809                                 }
04810                         }
04811 
04812 #if USE_RT_COMB_IFREE
04813                         rt_comb_ifree( &intern, &rt_uniresource );
04814 #else
04815                         rt_db_free_internal(&intern, &rt_uniresource);
04816 #endif
04817                 }
04818         }
04819 
04820         /* place data in interp and free memory */
04821          while (BU_LIST_WHILE(itnp,wdb_id_to_names,&headIdName.l)) {
04822                 if (!sflag) {
04823                         struct bu_vls vls;
04824 
04825                         bu_vls_init(&vls);
04826                         bu_vls_printf(&vls, "Region[s] with %s %d:\n",
04827                                       isAir ? "air code" : "ident", itnp->id);
04828                         Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
04829                         bu_vls_free(&vls);
04830                 }
04831 
04832                 while (BU_LIST_WHILE(inp,wdb_id_names,&itnp->headName.l)) {
04833                         if (sflag)
04834                                 Tcl_AppendElement(interp, bu_vls_addr(&inp->name));
04835                         else
04836                                 Tcl_AppendResult(interp, "   ", bu_vls_addr(&inp->name),
04837                                                  "\n", (char *)NULL);
04838 
04839                         BU_LIST_DEQUEUE(&inp->l);
04840                         bu_vls_free(&inp->name);
04841                         bu_free((genptr_t)inp, "which: inp");
04842                 }
04843 
04844                 BU_LIST_DEQUEUE(&itnp->l);
04845                 bu_free((genptr_t)itnp, "which: itnp");
04846         }
04847 
04848         return TCL_OK;
04849 }
04850 
04851 /*
04852  * Usage:
04853  *        procname whichair/whichid [-s] id(s)
04854  */
04855 static int
04856 wdb_which_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv)
04857 {
04858         struct rt_wdb   *wdbp = (struct rt_wdb *)clientData;
04859 
04860         return wdb_which_cmd(wdbp, interp, argc-1, argv+1);
04861 }
04862 
04863 int
04864 wdb_title_cmd(struct rt_wdb     *wdbp,
04865               Tcl_Interp        *interp,
04866               int               argc,
04867               char              **argv)
04868 {
04869         struct bu_vls   title;
04870         int             bad = 0;
04871 
04872         RT_CK_WDB(wdbp);
04873         RT_CK_DBI(wdbp->dbip);
04874 
04875         if (argc < 1 || MAXARGS < argc) {
04876                 struct bu_vls vls;
04877 
04878                 bu_vls_init(&vls);
04879                 bu_vls_printf(&vls, "helplib_alias wdb_title %s", argv[0]);
04880                 Tcl_Eval(interp, bu_vls_addr(&vls));
04881                 bu_vls_free(&vls);
04882                 return TCL_ERROR;
04883         }
04884 
04885         /* get title */
04886         if (argc == 1) {
04887                 Tcl_AppendResult(interp, wdbp->dbip->dbi_title, (char *)NULL);
04888                 return TCL_OK;
04889         }
04890 
04891         WDB_TCL_CHECK_READ_ONLY;
04892 
04893         /* set title */
04894         bu_vls_init(&title);
04895         bu_vls_from_argv(&title, argc-1, (const char **)argv+1);
04896 
04897         if (db_update_ident(wdbp->dbip, bu_vls_addr(&title), wdbp->dbip->dbi_base2local) < 0) {
04898                 Tcl_AppendResult(interp, "Error: unable to change database title");
04899                 bad = 1;
04900         }
04901 
04902         bu_vls_free(&title);
04903         return bad ? TCL_ERROR : TCL_OK;
04904 }
04905 
04906 /*
04907  * Change or return the database title.
04908  *
04909  * Usage:
04910  *        procname title [description]
04911  */
04912 static int
04913 wdb_title_tcl(ClientData        clientData,
04914               Tcl_Interp        *interp,
04915               int               argc,
04916               char              **argv)
04917 {
04918         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
04919 
04920         return wdb_title_cmd(wdbp, interp, argc-1, argv+1);
04921 }
04922 
04923 static int
04924 wdb_list_children(struct rt_wdb         *wdbp,
04925                   Tcl_Interp            *interp,
04926                   register struct directory *dp)
04927 {
04928         register int                    i;
04929         struct rt_db_internal           intern;
04930         struct rt_comb_internal         *comb;
04931 
04932         if (!(dp->d_flags & DIR_COMB))
04933                 return TCL_OK;
04934 
04935         if (rt_db_get_internal(&intern, dp, wdbp->dbip, (fastf_t *)NULL, &rt_uniresource) < 0) {
04936                 Tcl_AppendResult(interp, "Database read error, aborting", (char *)NULL);
04937                 return TCL_ERROR;
04938         }
04939         comb = (struct rt_comb_internal *)intern.idb_ptr;
04940 
04941         if (comb->tree) {
04942                 struct bu_vls vls;
04943                 int node_count;
04944                 int actual_count;
04945                 struct rt_tree_array *rt_tree_array;
04946 
04947                 if (comb->tree && db_ck_v4gift_tree(comb->tree) < 0) {
04948                         db_non_union_push(comb->tree, &rt_uniresource);
04949                         if (db_ck_v4gift_tree(comb->tree) < 0) {
04950                                 Tcl_AppendResult(interp, "Cannot flatten tree for listing", (char *)NULL);
04951                                 return TCL_ERROR;
04952                         }
04953                 }
04954                 node_count = db_tree_nleaves(comb->tree);
04955                 if (node_count > 0) {
04956                         rt_tree_array = (struct rt_tree_array *)bu_calloc( node_count,
04957                                                                            sizeof( struct rt_tree_array ), "tree list" );
04958                         actual_count = (struct rt_tree_array *)db_flatten_tree(
04959                                 rt_tree_array, comb->tree, OP_UNION,
04960                                 1, &rt_uniresource ) - rt_tree_array;
04961                         BU_ASSERT_PTR( actual_count, ==, node_count );
04962                         comb->tree = TREE_NULL;
04963                 } else {
04964                         actual_count = 0;
04965                         rt_tree_array = NULL;
04966                 }
04967 
04968                 bu_vls_init(&vls);
04969                 for (i=0 ; i<actual_count ; i++) {
04970                         char op;
04971 
04972                         switch (rt_tree_array[i].tl_op) {
04973                         case OP_UNION:
04974                                 op = 'u';
04975                                 break;
04976                         case OP_INTERSECT:
04977                                 op = '+';
04978                                 break;
04979                         case OP_SUBTRACT:
04980                                 op = '-';
04981                                 break;
04982                         default:
04983                                 op = '?';
04984                                 break;
04985                         }
04986 
04987                         bu_vls_printf(&vls, "{%c %s} ", op, rt_tree_array[i].tl_tree->tr_l.tl_name);
04988                         db_free_tree( rt_tree_array[i].tl_tree, &rt_uniresource );
04989                 }
04990                 Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)0);
04991                 bu_vls_free(&vls);
04992 
04993                 if (rt_tree_array)
04994                         bu_free((char *)rt_tree_array, "printnode: rt_tree_array");
04995         }
04996         rt_db_free_internal(&intern, &rt_uniresource);
04997 
04998         return TCL_OK;
04999 }
05000 
05001 int
05002 wdb_lt_cmd(struct rt_wdb        *wdbp,
05003            Tcl_Interp           *interp,
05004            int                  argc,
05005            char                 **argv)
05006 {
05007         register struct directory       *dp;
05008         struct bu_vls                   vls;
05009 
05010         if (argc != 2)
05011                 goto bad;
05012 
05013         if ((dp = db_lookup(wdbp->dbip, argv[1], LOOKUP_NOISY)) == DIR_NULL)
05014                 goto bad;
05015 
05016         return wdb_list_children(wdbp, interp, dp);
05017 
05018  bad:
05019         bu_vls_init(&vls);
05020         bu_vls_printf(&vls, "helplib_alias wdb_lt %s", argv[0]);
05021         Tcl_Eval(interp, bu_vls_addr(&vls));
05022         bu_vls_free(&vls);
05023         return TCL_ERROR;
05024 }
05025 
05026 /*
05027  * Usage:
05028  *        procname lt object
05029  */
05030 static int
05031 wdb_lt_tcl(ClientData   clientData,
05032            Tcl_Interp   *interp,
05033            int          argc,
05034            char         **argv)
05035 {
05036         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
05037 
05038         return wdb_lt_cmd(wdbp, interp, argc-1, argv+1);
05039 }
05040 
05041 int
05042 wdb_version_cmd(struct rt_wdb   *wdbp,
05043                 Tcl_Interp      *interp,
05044                 int             argc,
05045                 char            **argv)
05046 {
05047         struct bu_vls   vls;
05048 
05049         bu_vls_init(&vls);
05050 
05051         if (argc != 1) {
05052                 bu_vls_printf(&vls, "helplib_alias wdb_version %s", argv[0]);
05053                 Tcl_Eval(interp, bu_vls_addr(&vls));
05054                 bu_vls_free(&vls);
05055                 return TCL_ERROR;
05056         }
05057 
05058         bu_vls_printf(&vls, "%d", wdbp->dbip->dbi_version);
05059         Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)0);
05060         bu_vls_free(&vls);
05061 
05062         return TCL_OK;
05063 }
05064 
05065 /*
05066  * Usage:
05067  *        procname version
05068  */
05069 static int
05070 wdb_version_tcl(ClientData      clientData,
05071                 Tcl_Interp      *interp,
05072                 int             argc,
05073                 char            **argv)
05074 {
05075         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
05076 
05077         return wdb_version_cmd(wdbp, interp, argc-1, argv+1);
05078 }
05079 
05080 /*
05081  *                      W D B _ P R I N T _ N O D E
05082  *
05083  *  NON-PARALLEL due to rt_uniresource
05084  */
05085 static void
05086 wdb_print_node(struct rt_wdb            *wdbp,
05087                Tcl_Interp               *interp,
05088                register struct directory *dp,
05089                int                      pathpos,
05090                int                      indentSize,
05091                char                     prefix,
05092                int                      cflag)
05093 {
05094         register int                    i;
05095         register struct directory       *nextdp;
05096         struct rt_db_internal           intern;
05097         struct rt_comb_internal         *comb;
05098 
05099         if (cflag && !(dp->d_flags & DIR_COMB))
05100                 return;
05101 
05102         for (i=0; i<pathpos; i++)
05103             if( indentSize < 0 ) {
05104                 Tcl_AppendResult(interp, "\t", (char *)NULL);
05105             } else {
05106                 int j;
05107                 for( j=0 ; j<indentSize ; j++ ) {
05108                     Tcl_AppendResult(interp, " ", (char *)NULL);
05109                 }
05110             }
05111 
05112         if (prefix) {
05113                 struct bu_vls tmp_vls;
05114 
05115                 bu_vls_init(&tmp_vls);
05116                 bu_vls_printf(&tmp_vls, "%c ", prefix);
05117                 Tcl_AppendResult(interp, bu_vls_addr(&tmp_vls), (char *)NULL);
05118                 bu_vls_free(&tmp_vls);
05119         }
05120 
05121         Tcl_AppendResult(interp, dp->d_namep, (char *)NULL);
05122         /* Output Comb and Region flags (-F?) */
05123         if(dp->d_flags & DIR_COMB)
05124                 Tcl_AppendResult(interp, "/", (char *)NULL);
05125         if(dp->d_flags & DIR_REGION)
05126                 Tcl_AppendResult(interp, "R", (char *)NULL);
05127 
05128         Tcl_AppendResult(interp, "\n", (char *)NULL);
05129 
05130         if(!(dp->d_flags & DIR_COMB))
05131                 return;
05132 
05133         /*
05134          *  This node is a combination (eg, a directory).
05135          *  Process all the arcs (eg, directory members).
05136          */
05137 
05138         if (rt_db_get_internal(&intern, dp, wdbp->dbip, (fastf_t *)NULL, &rt_uniresource) < 0) {
05139                 Tcl_AppendResult(interp, "Database read error, aborting", (char *)NULL);
05140                 return;
05141         }
05142         comb = (struct rt_comb_internal *)intern.idb_ptr;
05143 
05144         if (comb->tree) {
05145                 int node_count;
05146                 int actual_count;
05147                 struct rt_tree_array *rt_tree_array;
05148 
05149                 if (comb->tree && db_ck_v4gift_tree(comb->tree) < 0) {
05150                         db_non_union_push(comb->tree, &rt_uniresource);
05151                         if (db_ck_v4gift_tree(comb->tree) < 0) {
05152                                 Tcl_AppendResult(interp, "Cannot flatten tree for listing", (char *)NULL);
05153                                 return;
05154                         }
05155                 }
05156                 node_count = db_tree_nleaves(comb->tree);
05157                 if (node_count > 0) {
05158                         rt_tree_array = (struct rt_tree_array *)bu_calloc( node_count,
05159                                                                            sizeof( struct rt_tree_array ), "tree list" );
05160                         actual_count = (struct rt_tree_array *)db_flatten_tree(
05161                                 rt_tree_array, comb->tree, OP_UNION,
05162                                 1, &rt_uniresource ) - rt_tree_array;
05163                         BU_ASSERT_PTR( actual_count, ==, node_count );
05164                         comb->tree = TREE_NULL;
05165                 } else {
05166                         actual_count = 0;
05167                         rt_tree_array = NULL;
05168                 }
05169 
05170                 for (i=0 ; i<actual_count ; i++) {
05171                         char op;
05172 
05173                         switch (rt_tree_array[i].tl_op) {
05174                         case OP_UNION:
05175                                 op = 'u';
05176                                 break;
05177                         case OP_INTERSECT:
05178                                 op = '+';
05179                                 break;
05180                         case OP_SUBTRACT:
05181                                 op = '-';
05182                                 break;
05183                         default:
05184                                 op = '?';
05185                                 break;
05186                         }
05187 
05188                         if ((nextdp = db_lookup(wdbp->dbip, rt_tree_array[i].tl_tree->tr_l.tl_name, LOOKUP_NOISY)) == DIR_NULL) {
05189                                 int j;
05190                                 struct bu_vls tmp_vls;
05191 
05192                                 for (j=0; j<pathpos+1; j++)
05193                                         Tcl_AppendResult(interp, "\t", (char *)NULL);
05194 
05195                                 bu_vls_init(&tmp_vls);
05196                                 bu_vls_printf(&tmp_vls, "%c ", op);
05197                                 Tcl_AppendResult(interp, bu_vls_addr(&tmp_vls), (char *)NULL);
05198                                 bu_vls_free(&tmp_vls);
05199 
05200                                 Tcl_AppendResult(interp, rt_tree_array[i].tl_tree->tr_l.tl_name, "\n", (char *)NULL);
05201                         } else
05202                                 wdb_print_node(wdbp, interp, nextdp, pathpos+1, indentSize, op, cflag);
05203                         db_free_tree( rt_tree_array[i].tl_tree, &rt_uniresource );
05204                 }
05205                 if(rt_tree_array) bu_free((char *)rt_tree_array, "printnode: rt_tree_array");
05206         }
05207         rt_db_free_internal(&intern, &rt_uniresource);
05208 }
05209 
05210 /*
05211  * Usage:
05212  *        procname track args
05213  */
05214 static int
05215 wdb_track_tcl(ClientData clientData,
05216               Tcl_Interp *interp,
05217               int        argc,
05218               char       **argv) {
05219   struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
05220 
05221   return wdb_track_cmd(wdbp, interp, argc-1, argv+1);
05222 }
05223 
05224 int
05225 wdb_tree_cmd(struct rt_wdb      *wdbp,
05226              Tcl_Interp         *interp,
05227              int                argc,
05228              char               **argv)
05229 {
05230         register struct directory       *dp;
05231         register int                    j;
05232         int                             cflag = 0;
05233         int                             indentSize = -1;
05234         int                             c;
05235         struct bu_vls                   vls;
05236         FILE                            *fdout = NULL;
05237 
05238         if (argc < 2 || MAXARGS < argc) {
05239 
05240                 bu_vls_init(&vls);
05241                 bu_vls_printf(&vls, "helplib_alias wdb_tree %s", argv[0]);
05242                 Tcl_Eval(interp, bu_vls_addr(&vls));
05243                 bu_vls_free(&vls);
05244                 return TCL_ERROR;
05245         }
05246 
05247         /* Parse options */
05248         bu_optind = 1;  /* re-init bu_getopt() */
05249         while ((c=bu_getopt(argc, argv, "i:o:c")) != EOF) {
05250                 switch (c) {
05251                 case 'i':
05252                         indentSize = atoi(bu_optarg);
05253                         break;
05254                 case 'c':
05255                     cflag = 1;
05256                         break;
05257                 case 'o':
05258                     if( (fdout = fopen( bu_optarg, "w+" )) == NULL ) {
05259                         Tcl_SetErrno( errno );
05260                         Tcl_AppendResult( interp, "Failed to open output file, ",
05261                                           strerror( errno ), (char *)NULL );
05262                         return TCL_ERROR;
05263                     }
05264                     break;
05265                 case '?':
05266                 default:
05267                     bu_vls_init(&vls);
05268                     bu_vls_printf(&vls, "helplib_alias wdb_tree %s", argv[0]);
05269                     Tcl_Eval(interp, bu_vls_addr(&vls));
05270                     bu_vls_free(&vls);
05271                     return TCL_ERROR;
05272                     break;
05273                 }
05274         }
05275 
05276         argc -= (bu_optind - 1);
05277         argv += (bu_optind - 1);
05278 
05279         for (j = 1; j < argc; j++) {
05280                 if (j > 1)
05281                         Tcl_AppendResult(interp, "\n", (char *)NULL);
05282                 if ((dp = db_lookup(wdbp->dbip, argv[j], LOOKUP_NOISY)) == DIR_NULL)
05283                         continue;
05284                 wdb_print_node(wdbp, interp, dp, 0, indentSize, 0, cflag);
05285         }
05286 
05287         if( fdout != NULL ) {
05288             fprintf( fdout, "%s", Tcl_GetStringResult( interp ) );
05289             Tcl_ResetResult( interp );
05290             fclose( fdout );
05291         }
05292 
05293         return TCL_OK;
05294 }
05295 
05296 /*
05297  * Usage:
05298  *        procname tree object(s)
05299  */
05300 static int
05301 wdb_tree_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv)
05302 {
05303         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
05304 
05305         return wdb_tree_cmd(wdbp, interp, argc-1, argv+1);
05306 }
05307 
05308 /*
05309  *                      W D B _ C O L O R _ P U T R E C
05310  *
05311  *  Used to create a database record and get it written out to a granule.
05312  *  In some cases, storage will need to be allocated.
05313  */
05314 static void
05315 wdb_color_putrec(register struct mater  *mp,
05316                  Tcl_Interp             *interp,
05317                  struct db_i            *dbip)
05318 {
05319         struct directory dir;
05320         union record rec;
05321 
05322         /* we get here only if database is NOT read-only */
05323 
05324         rec.md.md_id = ID_MATERIAL;
05325         rec.md.md_low = mp->mt_low;
05326         rec.md.md_hi = mp->mt_high;
05327         rec.md.md_r = mp->mt_r;
05328         rec.md.md_g = mp->mt_g;
05329         rec.md.md_b = mp->mt_b;
05330 
05331         /* Fake up a directory entry for db_* routines */
05332         RT_DIR_SET_NAMEP( &dir, "color_putrec" );
05333         dir.d_magic = RT_DIR_MAGIC;
05334         dir.d_flags = 0;
05335 
05336         if (mp->mt_daddr == MATER_NO_ADDR) {
05337                 /* Need to allocate new database space */
05338                 if (db_alloc(dbip, &dir, 1) < 0) {
05339                         Tcl_AppendResult(interp,
05340                                          "Database alloc error, aborting",
05341                                          (char *)NULL);
05342                         return;
05343                 }
05344                 mp->mt_daddr = dir.d_addr;
05345         } else {
05346                 dir.d_addr = mp->mt_daddr;
05347                 dir.d_len = 1;
05348         }
05349 
05350         if (db_put(dbip, &dir, &rec, 0, 1) < 0) {
05351                 Tcl_AppendResult(interp,
05352                                  "Database write error, aborting",
05353                                  (char *)NULL);
05354                 return;
05355         }
05356 }
05357 
05358 /*
05359  *                      W D B _ C O L O R _ Z A P R E C
05360  *
05361  *  Used to release database resources occupied by a material record.
05362  */
05363 static void
05364 wdb_color_zaprec(register struct mater  *mp,
05365                  Tcl_Interp             *interp,
05366                  struct db_i            *dbip)
05367 {
05368         struct directory dir;
05369 
05370         /* we get here only if database is NOT read-only */
05371         if (mp->mt_daddr == MATER_NO_ADDR)
05372                 return;
05373 
05374         dir.d_magic = RT_DIR_MAGIC;
05375         RT_DIR_SET_NAMEP( &dir, "color_zaprec" );
05376         dir.d_len = 1;
05377         dir.d_addr = mp->mt_daddr;
05378         dir.d_flags = 0;
05379 
05380         if (db_delete(dbip, &dir) < 0) {
05381                 Tcl_AppendResult(interp,
05382                                  "Database delete error, aborting",
05383                                  (char *)NULL);
05384                 return;
05385         }
05386         mp->mt_daddr = MATER_NO_ADDR;
05387 }
05388 
05389 int
05390 wdb_color_cmd(struct rt_wdb     *wdbp,
05391               Tcl_Interp        *interp,
05392               int               argc,
05393               char              **argv)
05394 {
05395         register struct mater *newp,*next_mater;
05396 
05397         WDB_TCL_CHECK_READ_ONLY;
05398 
05399         if (argc != 6) {
05400                 struct bu_vls vls;
05401 
05402                 bu_vls_init(&vls);
05403                 bu_vls_printf(&vls, "helplib_alias wdb_color %s", argv[0]);
05404                 Tcl_Eval(interp, bu_vls_addr(&vls));
05405                 bu_vls_free(&vls);
05406                 return TCL_ERROR;
05407         }
05408 
05409         if (wdbp->dbip->dbi_version < 5) {
05410                 /* Delete all color records from the database */
05411                 newp = rt_material_head;
05412                 while (newp != MATER_NULL) {
05413                         next_mater = newp->mt_forw;
05414                         wdb_color_zaprec(newp, interp, wdbp->dbip);
05415                         newp = next_mater;
05416                 }
05417 
05418                 /* construct the new color record */
05419                 BU_GETSTRUCT(newp, mater);
05420                 newp->mt_low = atoi(argv[1]);
05421                 newp->mt_high = atoi(argv[2]);
05422                 newp->mt_r = atoi(argv[3]);
05423                 newp->mt_g = atoi(argv[4]);
05424                 newp->mt_b = atoi(argv[5]);
05425                 newp->mt_daddr = MATER_NO_ADDR;         /* not in database yet */
05426 
05427                 /* Insert new color record in the in-memory list */
05428                 rt_insert_color(newp);
05429 
05430                 /* Write new color records for all colors in the list */
05431                 newp = rt_material_head;
05432                 while (newp != MATER_NULL) {
05433                         next_mater = newp->mt_forw;
05434                         wdb_color_putrec(newp, interp, wdbp->dbip);
05435                         newp = next_mater;
05436                 }
05437         } else {
05438                 struct bu_vls colors;
05439 
05440                 /* construct the new color record */
05441                 BU_GETSTRUCT(newp, mater);
05442                 newp->mt_low = atoi(argv[1]);
05443                 newp->mt_high = atoi(argv[2]);
05444                 newp->mt_r = atoi(argv[3]);
05445                 newp->mt_g = atoi(argv[4]);
05446                 newp->mt_b = atoi(argv[5]);
05447                 newp->mt_daddr = MATER_NO_ADDR;         /* not in database yet */
05448 
05449                 /* Insert new color record in the in-memory list */
05450                 rt_insert_color(newp);
05451 
05452                 /*
05453                  * Gather color records from the in-memory list to build
05454                  * the _GLOBAL objects regionid_colortable attribute.
05455                  */
05456                 newp = rt_material_head;
05457                 bu_vls_init(&colors);
05458                 while (newp != MATER_NULL) {
05459                         next_mater = newp->mt_forw;
05460                         bu_vls_printf(&colors, "{%d %d %d %d %d} ", newp->mt_low, newp->mt_high,
05461                                       newp->mt_r, newp->mt_g, newp->mt_b);
05462                         newp = next_mater;
05463                 }
05464 
05465                 db5_update_attribute("_GLOBAL", "regionid_colortable", bu_vls_addr(&colors), wdbp->dbip);
05466                 bu_vls_free(&colors);
05467         }
05468 
05469         return TCL_OK;
05470 }
05471 
05472 /*
05473  * Usage:
05474  *        procname color low high r g b
05475  */
05476 static int
05477 wdb_color_tcl(ClientData        clientData,
05478               Tcl_Interp        *interp,
05479               int               argc,
05480               char              **argv)
05481 {
05482         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
05483 
05484         return wdb_color_cmd(wdbp, interp, argc-1, argv+1);
05485 }
05486 
05487 static void
05488 wdb_pr_mater(register struct mater      *mp,
05489              Tcl_Interp                 *interp,
05490              int                        *ccp,
05491              int                        *clp)
05492 {
05493         char buf[128];
05494         struct bu_vls vls;
05495 
05496         bu_vls_init(&vls);
05497 
05498         (void)sprintf(buf, "%5d..%d", mp->mt_low, mp->mt_high );
05499         wdb_vls_col_item(&vls, buf, ccp, clp);
05500         (void)sprintf( buf, "%3d,%3d,%3d", mp->mt_r, mp->mt_g, mp->mt_b);
05501         wdb_vls_col_item(&vls, buf, ccp, clp);
05502         wdb_vls_col_eol(&vls, ccp, clp);
05503 
05504         Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
05505         bu_vls_free(&vls);
05506 }
05507 
05508 int
05509 wdb_prcolor_cmd(struct rt_wdb   *wdbp,
05510                 Tcl_Interp      *interp,
05511                 int             argc,
05512                 char            **argv)
05513 {
05514         register struct mater *mp;
05515         int col_count = 0;
05516         int col_len = 0;
05517 
05518         if (argc != 1) {
05519                 struct bu_vls vls;
05520 
05521                 bu_vls_init(&vls);
05522                 bu_vls_printf(&vls, "helplib_alias wdb_prcolor %s", argv[0]);
05523                 Tcl_Eval(interp, bu_vls_addr(&vls));
05524                 bu_vls_free(&vls);
05525                 return TCL_ERROR;
05526         }
05527 
05528         if (rt_material_head == MATER_NULL) {
05529                 Tcl_AppendResult(interp, "none", (char *)NULL);
05530                 return TCL_OK;
05531         }
05532 
05533         for (mp = rt_material_head; mp != MATER_NULL; mp = mp->mt_forw)
05534                 wdb_pr_mater(mp, interp, &col_count, &col_len);
05535 
05536         return TCL_OK;
05537 }
05538 
05539 /*
05540  * Usage:
05541  *        procname prcolor
05542  */
05543 static int
05544 wdb_prcolor_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char **argv)
05545 {
05546         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
05547 
05548         return wdb_prcolor_cmd(wdbp, interp, argc-1, argv+1);
05549 }
05550 
05551 int
05552 wdb_tol_cmd(struct rt_wdb       *wdbp,
05553             Tcl_Interp          *interp,
05554             int                 argc,
05555             char                **argv)
05556 {
05557         struct bu_vls vls;
05558         double  f;
05559 
05560         if (argc < 1 || 3 < argc){
05561                 bu_vls_init(&vls);
05562                 bu_vls_printf(&vls, "helplib_alias wdb_tol %s", argv[0]);
05563                 Tcl_Eval(interp, bu_vls_addr(&vls));
05564                 bu_vls_free(&vls);
05565                 return TCL_ERROR;
05566         }
05567 
05568         /* print all tolerance settings */
05569         if (argc == 1) {
05570                 Tcl_AppendResult(interp, "Current tolerance settings are:\n", (char *)NULL);
05571                 Tcl_AppendResult(interp, "Tesselation tolerances:\n", (char *)NULL );
05572 
05573                 if (wdbp->wdb_ttol.abs > 0.0) {
05574                         bu_vls_init(&vls);
05575                         bu_vls_printf(&vls, "\tabs %g mm\n", wdbp->wdb_ttol.abs);
05576                         Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
05577                         bu_vls_free(&vls);
05578                 } else {
05579                         Tcl_AppendResult(interp, "\tabs None\n", (char *)NULL);
05580                 }
05581 
05582                 if (wdbp->wdb_ttol.rel > 0.0) {
05583                         bu_vls_init(&vls);
05584                         bu_vls_printf(&vls, "\trel %g (%g%%)\n",
05585                                       wdbp->wdb_ttol.rel, wdbp->wdb_ttol.rel * 100.0 );
05586                         Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
05587                         bu_vls_free(&vls);
05588                 } else {
05589                         Tcl_AppendResult(interp, "\trel None\n", (char *)NULL);
05590                 }
05591 
05592                 if (wdbp->wdb_ttol.norm > 0.0) {
05593                         int     deg, min;
05594                         double  sec;
05595 
05596                         bu_vls_init(&vls);
05597                         sec = wdbp->wdb_ttol.norm * bn_radtodeg;
05598                         deg = (int)(sec);
05599                         sec = (sec - (double)deg) * 60;
05600                         min = (int)(sec);
05601                         sec = (sec - (double)min) * 60;
05602 
05603                         bu_vls_printf(&vls, "\tnorm %g degrees (%d deg %d min %g sec)\n",
05604                                       wdbp->wdb_ttol.norm * bn_radtodeg, deg, min, sec);
05605                         Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
05606                         bu_vls_free(&vls);
05607                 } else {
05608                         Tcl_AppendResult(interp, "\tnorm None\n", (char *)NULL);
05609                 }
05610 
05611                 bu_vls_init(&vls);
05612                 bu_vls_printf(&vls,"Calculational tolerances:\n");
05613                 bu_vls_printf(&vls,
05614                               "\tdistance = %g mm\n\tperpendicularity = %g (cosine of %g degrees)",
05615                               wdbp->wdb_tol.dist, wdbp->wdb_tol.perp,
05616                               acos(wdbp->wdb_tol.perp)*bn_radtodeg);
05617                 Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
05618                 bu_vls_free(&vls);
05619 
05620                 return TCL_OK;
05621         }
05622 
05623         /* get the specified tolerance */
05624         if (argc == 2) {
05625                 int status = TCL_OK;
05626 
05627                 bu_vls_init(&vls);
05628 
05629                 switch (argv[1][0]) {
05630                 case 'a':
05631                         if (wdbp->wdb_ttol.abs > 0.0)
05632                                 bu_vls_printf(&vls, "%g", wdbp->wdb_ttol.abs);
05633                         else
05634                                 bu_vls_printf(&vls, "None");
05635                         break;
05636                 case 'r':
05637                         if (wdbp->wdb_ttol.rel > 0.0)
05638                                 bu_vls_printf(&vls, "%g", wdbp->wdb_ttol.rel);
05639                         else
05640                                 bu_vls_printf(&vls, "None");
05641                         break;
05642                 case 'n':
05643                         if (wdbp->wdb_ttol.norm > 0.0)
05644                                 bu_vls_printf(&vls, "%g", wdbp->wdb_ttol.norm);
05645                         else
05646                                 bu_vls_printf(&vls, "None");
05647                         break;
05648                 case 'd':
05649                         bu_vls_printf(&vls, "%g", wdbp->wdb_tol.dist);
05650                         break;
05651                 case 'p':
05652                         bu_vls_printf(&vls, "%g", wdbp->wdb_tol.perp);
05653                         break;
05654                 default:
05655                         bu_vls_printf(&vls, "unrecognized tolerance type - %s", argv[1]);
05656                         status = TCL_ERROR;
05657                         break;
05658                 }
05659 
05660                 Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
05661                 bu_vls_free(&vls);
05662                 return status;
05663         }
05664 
05665         /* set the specified tolerance */
05666         if (sscanf(argv[2], "%lf", &f) != 1) {
05667                 bu_vls_init(&vls);
05668                 bu_vls_printf(&vls, "bad tolerance - %s", argv[2]);
05669                 Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
05670                 bu_vls_free(&vls);
05671 
05672                 return TCL_ERROR;
05673         }
05674 
05675         /* clamp negative to zero */
05676         if (f < 0.0) {
05677             Tcl_AppendResult(interp, "negative tolerance clamped to 0.0\n", (char *)NULL);
05678             f = 0.0;
05679         }
05680 
05681         switch (argv[1][0]) {
05682         case 'a':
05683                 /* Absolute tol */
05684             if (f < wdbp->wdb_tol.dist) {
05685                 bu_vls_init(&vls);
05686                 bu_vls_printf(&vls, "absolute tolerance cannot be less than distance tolerance, clamped to %f\n", wdbp->wdb_tol.dist);
05687                 Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
05688             }
05689             wdbp->wdb_ttol.abs = f;
05690             break;
05691         case 'r':
05692             if (f >= 1.0) {
05693                 Tcl_AppendResult(interp,
05694                                  "relative tolerance must be between 0 and 1, not changed\n",
05695                                  (char *)NULL);
05696                 return TCL_ERROR;
05697             }
05698             /* Note that a value of 0.0 will disable relative tolerance */
05699             wdbp->wdb_ttol.rel = f;
05700             break;
05701         case 'n':
05702             /* Normal tolerance, in degrees */
05703             if (f > 90.0) {
05704                 Tcl_AppendResult(interp,
05705                                  "Normal tolerance must be less than 90.0 degrees\n",
05706                                  (char *)NULL);
05707                 return TCL_ERROR;
05708             }
05709             /* Note that a value of 0.0 or 360.0 will disable this tol */
05710             wdbp->wdb_ttol.norm = f * bn_degtorad;
05711             break;
05712         case 'd':
05713             /* Calculational distance tolerance */
05714             wdbp->wdb_tol.dist = f;
05715             wdbp->wdb_tol.dist_sq = wdbp->wdb_tol.dist * wdbp->wdb_tol.dist;
05716             break;
05717         case 'p':
05718             /* Calculational perpendicularity tolerance */
05719             if (f > 1.0) {
05720                 Tcl_AppendResult(interp,
05721                                  "Calculational perpendicular tolerance must be from 0 to 1\n",
05722                                  (char *)NULL);
05723                 return TCL_ERROR;
05724             }
05725             wdbp->wdb_tol.perp = f;
05726             wdbp->wdb_tol.para = 1.0 - f;
05727             break;
05728         default:
05729             bu_vls_init(&vls);
05730             bu_vls_printf(&vls, "unrecognized tolerance type - %s", argv[1]);
05731             Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
05732             bu_vls_free(&vls);
05733             
05734             return TCL_ERROR;
05735         }
05736 
05737         return TCL_OK;
05738 }
05739 
05740 /*
05741  * Usage:
05742  *        procname tol [abs|rel|norm|dist|perp [#]]
05743  *
05744  *  abs #       sets absolute tolerance.  # > 0.0
05745  *  rel #       sets relative tolerance.  0.0 < # < 1.0
05746  *  norm #      sets normal tolerance, in degrees.
05747  *  dist #      sets calculational distance tolerance
05748  *  perp #      sets calculational normal tolerance.
05749  *
05750  */
05751 static int
05752 wdb_tol_tcl(ClientData  clientData,
05753             Tcl_Interp  *interp,
05754             int         argc,
05755             char        **argv)
05756 {
05757         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
05758 
05759         return wdb_tol_cmd(wdbp, interp, argc-1, argv+1);
05760 }
05761 
05762 /* structure to hold all solids that have been pushed. */
05763 struct wdb_push_id {
05764         long    magic;
05765         struct wdb_push_id *forw, *back;
05766         struct directory *pi_dir;
05767         mat_t   pi_mat;
05768 };
05769 
05770 #define WDB_MAGIC_PUSH_ID       0x50495323
05771 #define FOR_ALL_WDB_PUSH_SOLIDS(_p,_phead) \
05772         for(_p=_phead.forw; _p!=&_phead; _p=_p->forw)
05773 struct wdb_push_data {
05774         Tcl_Interp              *interp;
05775         struct wdb_push_id      pi_head;
05776         int                     push_error;
05777 };
05778 
05779 /*
05780  *              P U S H _ L E A F
05781  *
05782  * This routine must be prepared to run in parallel.
05783  *
05784  * This routine is called once for eas leaf (solid) that is to
05785  * be pushed.  All it does is build at push_id linked list.  The
05786  * linked list could be handled by bu_list macros but it is simple
05787  * enough to do hear with out them.
05788  */
05789 static union tree *
05790 wdb_push_leaf(struct db_tree_state      *tsp,
05791               struct db_full_path       *pathp,
05792               struct rt_db_internal     *ip,
05793               genptr_t                  client_data)
05794 {
05795         union tree      *curtree;
05796         struct directory *dp;
05797         register struct wdb_push_id *pip;
05798         struct wdb_push_data *wpdp = (struct wdb_push_data *)client_data;
05799 
05800         RT_CK_TESS_TOL(tsp->ts_ttol);
05801         BN_CK_TOL(tsp->ts_tol);
05802         RT_CK_RESOURCE(tsp->ts_resp);
05803 
05804         dp = pathp->fp_names[pathp->fp_len-1];
05805 
05806         if (RT_G_DEBUG&DEBUG_TREEWALK) {
05807                 char *sofar = db_path_to_string(pathp);
05808 
05809                 Tcl_AppendResult(wpdp->interp, "wdb_push_leaf(",
05810                                 ip->idb_meth->ft_name,
05811                                  ") path='", sofar, "'\n", (char *)NULL);
05812                 bu_free((genptr_t)sofar, "path string");
05813         }
05814 /*
05815  * XXX - This will work but is not the best method.  dp->d_uses tells us
05816  * if this solid (leaf) has been seen before.  If it hasn't just add
05817  * it to the list.  If it has, search the list to see if the matricies
05818  * match and do the "right" thing.
05819  *
05820  * (There is a question as to whether dp->d_uses is reset to zero
05821  *  for each tree walk.  If it is not, then d_uses is NOT a safe
05822  *  way to check and this method will always work.)
05823  */
05824         bu_semaphore_acquire(RT_SEM_WORKER);
05825         FOR_ALL_WDB_PUSH_SOLIDS(pip,wpdp->pi_head) {
05826                 if (pip->pi_dir == dp ) {
05827                         if (!bn_mat_is_equal(pip->pi_mat,
05828                                              tsp->ts_mat, tsp->ts_tol)) {
05829                                 char *sofar = db_path_to_string(pathp);
05830 
05831                                 Tcl_AppendResult(wpdp->interp, "wdb_push_leaf: matrix mismatch between '", sofar,
05832                                                  "' and prior reference.\n", (char *)NULL);
05833                                 bu_free((genptr_t)sofar, "path string");
05834                                 wpdp->push_error = 1;
05835                         }
05836 
05837                         bu_semaphore_release(RT_SEM_WORKER);
05838                         RT_GET_TREE(curtree, tsp->ts_resp);
05839                         curtree->magic = RT_TREE_MAGIC;
05840                         curtree->tr_op = OP_NOP;
05841                         return curtree;
05842                 }
05843         }
05844 /*
05845  * This is the first time we have seen this solid.
05846  */
05847         pip = (struct wdb_push_id *) bu_malloc(sizeof(struct wdb_push_id), "Push ident");
05848         pip->magic = WDB_MAGIC_PUSH_ID;
05849         pip->pi_dir = dp;
05850         MAT_COPY(pip->pi_mat, tsp->ts_mat);
05851         pip->back = wpdp->pi_head.back;
05852         wpdp->pi_head.back = pip;
05853         pip->forw = &wpdp->pi_head;
05854         pip->back->forw = pip;
05855         bu_semaphore_release(RT_SEM_WORKER);
05856         RT_GET_TREE( curtree, tsp->ts_resp );
05857         curtree->magic = RT_TREE_MAGIC;
05858         curtree->tr_op = OP_NOP;
05859         return curtree;
05860 }
05861 /*
05862  * A null routine that does nothing.
05863  */
05864 static union tree *
05865 wdb_push_region_end(register struct db_tree_state *tsp,
05866                     struct db_full_path         *pathp,
05867                     union tree                  *curtree,
05868                     genptr_t                    client_data)
05869 {
05870         return curtree;
05871 }
05872 
05873 int
05874 wdb_push_cmd(struct rt_wdb      *wdbp,
05875              Tcl_Interp         *interp,
05876              int                argc,
05877              char               **argv)
05878 {
05879         struct wdb_push_data    *wpdp;
05880         struct wdb_push_id      *pip;
05881         struct rt_db_internal   es_int;
05882         int                     i;
05883         int                     ncpu;
05884         int                     c;
05885         int                     old_debug;
05886         int                     push_error;
05887         extern  int             bu_optind;
05888         extern  char            *bu_optarg;
05889 
05890         WDB_TCL_CHECK_READ_ONLY;
05891 
05892         if (argc < 2 || MAXARGS < argc) {
05893                 struct bu_vls vls;
05894 
05895                 bu_vls_init(&vls);
05896                 bu_vls_printf(&vls, "helplib_alias wdb_push %s", argv[0]);
05897                 Tcl_Eval(interp, bu_vls_addr(&vls));
05898                 bu_vls_free(&vls);
05899                 return TCL_ERROR;
05900         }
05901 
05902         RT_CHECK_DBI(wdbp->dbip);
05903 
05904         BU_GETSTRUCT(wpdp,wdb_push_data);
05905         wpdp->interp = interp;
05906         wpdp->push_error = 0;
05907         wpdp->pi_head.magic = WDB_MAGIC_PUSH_ID;
05908         wpdp->pi_head.forw = wpdp->pi_head.back = &wpdp->pi_head;
05909         wpdp->pi_head.pi_dir = (struct directory *) 0;
05910 
05911         old_debug = RT_G_DEBUG;
05912 
05913         /* Initial values for options, must be reset each time */
05914         ncpu = 1;
05915 
05916         /* Parse options */
05917         bu_optind = 1;  /* re-init bu_getopt() */
05918         while ((c=bu_getopt(argc, argv, "P:d")) != EOF) {
05919                 switch (c) {
05920                 case 'P':
05921                         ncpu = atoi(bu_optarg);
05922                         if (ncpu<1) ncpu = 1;
05923                         break;
05924                 case 'd':
05925                         rt_g.debug |= DEBUG_TREEWALK;
05926                         break;
05927                 case '?':
05928                 default:
05929                   Tcl_AppendResult(interp, "push: usage push [-P processors] [-d] root [root2 ...]\n", (char *)NULL);
05930                         break;
05931                 }
05932         }
05933 
05934         argc -= bu_optind;
05935         argv += bu_optind;
05936 
05937         /*
05938          * build a linked list of solids with the correct
05939          * matrix to apply to each solid.  This will also
05940          * check to make sure that a solid is not pushed in two
05941          * different directions at the same time.
05942          */
05943         i = db_walk_tree(wdbp->dbip, argc, (const char **)argv,
05944                          ncpu,
05945                          &wdbp->wdb_initial_tree_state,
05946                          0,                             /* take all regions */
05947                          wdb_push_region_end,
05948                          wdb_push_leaf, (genptr_t)wpdp);
05949 
05950         /*
05951          * If there was any error, then just free up the solid
05952          * list we just built.
05953          */
05954         if (i < 0 || wpdp->push_error) {
05955                 while (wpdp->pi_head.forw != &wpdp->pi_head) {
05956                         pip = wpdp->pi_head.forw;
05957                         pip->forw->back = pip->back;
05958                         pip->back->forw = pip->forw;
05959                         bu_free((genptr_t)pip, "Push ident");
05960                 }
05961                 rt_g.debug = old_debug;
05962                 bu_free((genptr_t)wpdp, "wdb_push_tcl: wpdp");
05963                 Tcl_AppendResult(interp,
05964                                  "push:\tdb_walk_tree failed or there was a solid moving\n\tin two or more directions",
05965                                  (char *)NULL);
05966                 return TCL_ERROR;
05967         }
05968 /*
05969  * We've built the push solid list, now all we need to do is apply
05970  * the matrix we've stored for each solid.
05971  */
05972         FOR_ALL_WDB_PUSH_SOLIDS(pip,wpdp->pi_head) {
05973                 if (rt_db_get_internal(&es_int, pip->pi_dir, wdbp->dbip, pip->pi_mat, &rt_uniresource) < 0) {
05974                         Tcl_AppendResult(interp, "f_push: Read error fetching '",
05975                                    pip->pi_dir->d_namep, "'\n", (char *)NULL);
05976                         wpdp->push_error = -1;
05977                         continue;
05978                 }
05979                 RT_CK_DB_INTERNAL(&es_int);
05980 
05981                 if (rt_db_put_internal(pip->pi_dir, wdbp->dbip, &es_int, &rt_uniresource) < 0) {
05982                         Tcl_AppendResult(interp, "push(", pip->pi_dir->d_namep,
05983                                          "): solid export failure\n", (char *)NULL);
05984                 }
05985                 rt_db_free_internal(&es_int, &rt_uniresource);
05986         }
05987 
05988         /*
05989          * Now use the wdb_identitize() tree walker to turn all the
05990          * matricies in a combination to the identity matrix.
05991          * It would be nice to use db_tree_walker() but the tree
05992          * walker does not give us all combinations, just regions.
05993          * This would work if we just processed all matricies backwards
05994          * from the leaf (solid) towards the root, but all in all it
05995          * seems that this is a better method.
05996          */
05997 
05998         while (argc > 0) {
05999                 struct directory *db;
06000                 db = db_lookup(wdbp->dbip, *argv++, 0);
06001                 if (db)
06002                         wdb_identitize(db, wdbp->dbip, interp);
06003                 --argc;
06004         }
06005 
06006         /*
06007          * Free up the solid table we built.
06008          */
06009         while (wpdp->pi_head.forw != &wpdp->pi_head) {
06010                 pip = wpdp->pi_head.forw;
06011                 pip->forw->back = pip->back;
06012                 pip->back->forw = pip->forw;
06013                 bu_free((genptr_t)pip, "Push ident");
06014         }
06015 
06016         rt_g.debug = old_debug;
06017         push_error = wpdp->push_error;
06018         bu_free((genptr_t)wpdp, "wdb_push_tcl: wpdp");
06019 
06020         return push_error ? TCL_ERROR : TCL_OK;
06021 }
06022 
06023 /*
06024  * The push command is used to move matrices from combinations
06025  * down to the solids. At some point, it is worth while thinking
06026  * about adding a limit to have the push go only N levels down.
06027  *
06028  * the -d flag turns on the treewalker debugging output.
06029  * the -P flag allows for multi-processor tree walking (not useful)
06030  *
06031  * Usage:
06032  *        procname push object(s)
06033  */
06034 static int
06035 wdb_push_tcl(ClientData clientData,
06036              Tcl_Interp *interp,
06037              int        argc,
06038              char       **argv)
06039 {
06040         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
06041 
06042         return wdb_push_cmd(wdbp, interp, argc-1, argv+1);
06043 }
06044 
06045 static void
06046 increment_uses(struct db_i      *db_ip,
06047                struct directory *dp,
06048                genptr_t         ptr)
06049 {
06050         RT_CK_DIR(dp);
06051 
06052         dp->d_uses++;
06053 }
06054 
06055 static void
06056 increment_nrefs(struct db_i             *db_ip,
06057                 struct directory        *dp,
06058                 genptr_t                ptr)
06059 {
06060         RT_CK_DIR(dp);
06061 
06062         dp->d_nref++;
06063 }
06064 
06065 struct object_use
06066 {
06067         struct bu_list          l;
06068         struct directory        *dp;
06069         mat_t                   xform;
06070         int                     used;
06071 };
06072 
06073 static void
06074 Free_uses( struct db_i *dbip )
06075 {
06076         int i;
06077 
06078         for (i=0 ; i<RT_DBNHASH ; i++) {
06079                 struct directory *dp;
06080                 struct object_use *use;
06081 
06082                 for (dp=dbip->dbi_Head[i]; dp!=DIR_NULL; dp=dp->d_forw) {
06083                         if (!(dp->d_flags & (DIR_SOLID | DIR_COMB)))
06084                                 continue;
06085 
06086                         while (BU_LIST_NON_EMPTY(&dp->d_use_hd)) {
06087                                 use = BU_LIST_FIRST(object_use, &dp->d_use_hd);
06088                                 if( !use->used ) {
06089                                         if( use->dp->d_un.file_offset >= 0 ) {
06090                                                 /* was written to disk */
06091                                                 db_delete( dbip, use->dp );
06092                                         }
06093                                         db_dirdelete(dbip, use->dp);
06094                                 }
06095                                 BU_LIST_DEQUEUE(&use->l);
06096                                 bu_free((genptr_t)use, "Free_uses: use");
06097                         }
06098 
06099                 }
06100         }
06101 
06102 }
06103 
06104 static void
06105 Make_new_name(struct db_i       *dbip,
06106               struct directory  *dp,
06107               genptr_t          ptr)
06108 {
06109         struct object_use *use;
06110         int use_no;
06111         int digits;
06112         int suffix_start;
06113         int name_length;
06114         int j;
06115         char format_v4[25], format_v5[25];
06116         struct bu_vls name_v5;
06117         char name_v4[NAMESIZE];
06118         char *name;
06119 
06120         /* only one use and not referenced elsewhere, nothing to do */
06121         if (dp->d_uses < 2 && dp->d_uses == dp->d_nref)
06122                 return;
06123 
06124         /* check if already done */
06125         if (BU_LIST_NON_EMPTY(&dp->d_use_hd))
06126                 return;
06127 
06128         digits = log10((double)dp->d_uses) + 2.0;
06129         sprintf(format_v5, "%%s_%%0%dd", digits);
06130         sprintf(format_v4, "_%%0%dd", digits);
06131 
06132         name_length = strlen(dp->d_namep);
06133         if (name_length + digits + 1 > NAMESIZE - 1)
06134                 suffix_start = NAMESIZE - digits - 2;
06135         else
06136                 suffix_start = name_length;
06137 
06138         if (dbip->dbi_version >= 5)
06139                 bu_vls_init(&name_v5);
06140         j = 0;
06141         for (use_no=0 ; use_no<dp->d_uses ; use_no++) {
06142                 j++;
06143                 use = (struct object_use *)bu_malloc( sizeof( struct object_use ), "Make_new_name: use" );
06144 
06145                 /* set xform for this object_use to all zeros */
06146                 MAT_ZERO(use->xform);
06147                 use->used = 0;
06148                 if (dbip->dbi_version < 5) {
06149                         NAMEMOVE(dp->d_namep, name_v4);
06150                         name_v4[NAMESIZE-1] = '\0';                /* ensure null termination */
06151                 }
06152 
06153                 /* Add an entry for the original at the end of the list
06154                  * This insures that the original will be last to be modified
06155                  * If original were modified earlier, copies would be screwed-up
06156                  */
06157                 if (use_no == dp->d_uses-1 && dp->d_uses == dp->d_nref)
06158                         use->dp = dp;
06159                 else {
06160                         if (dbip->dbi_version < 5) {
06161                                 sprintf(&name_v4[suffix_start], format_v4, j);
06162                                 name = name_v4;
06163                         } else {
06164                                 bu_vls_trunc(&name_v5, 0);
06165                                 bu_vls_printf(&name_v5, format_v5, dp->d_namep, j);
06166                                 name = bu_vls_addr(&name_v5);
06167                         }
06168 
06169                         /* Insure that new name is unique */
06170                         while (db_lookup( dbip, name, 0 ) != DIR_NULL) {
06171                                 j++;
06172                                 if (dbip->dbi_version < 5) {
06173                                         sprintf(&name_v4[suffix_start], format_v4, j);
06174                                         name = name_v4;
06175                                 } else {
06176                                         bu_vls_trunc(&name_v5, 0);
06177                                         bu_vls_printf(&name_v5, format_v5, dp->d_namep, j);
06178                                         name = bu_vls_addr(&name_v5);
06179                                 }
06180                         }
06181 
06182                         /* Add new name to directory */
06183                         if ((use->dp = db_diradd(dbip, name, -1, 0, dp->d_flags,
06184                                                  (genptr_t)&dp->d_minor_type)) == DIR_NULL) {
06185                                 WDB_ALLOC_ERR_return;
06186                         }
06187                 }
06188 
06189                 /* Add new directory pointer to use list for this object */
06190                 BU_LIST_INSERT(&dp->d_use_hd, &use->l);
06191         }
06192 
06193         if (dbip->dbi_version >= 5)
06194                 bu_vls_free(&name_v5);
06195 }
06196 
06197 static struct directory *
06198 Copy_solid(struct db_i          *dbip,
06199            struct directory     *dp,
06200            mat_t                xform,
06201            Tcl_Interp           *interp,
06202            struct rt_wdb        *wdbp)
06203 {
06204         struct directory *found;
06205         struct rt_db_internal sol_int;
06206         struct object_use *use;
06207 
06208         RT_CK_DIR(dp);
06209 
06210         if (!(dp->d_flags & DIR_SOLID)) {
06211                 Tcl_AppendResult(interp, "Copy_solid: ", dp->d_namep,
06212                                  " is not a solid!!!!\n", (char *)NULL);
06213                 return (DIR_NULL);
06214         }
06215 
06216         /* If no transformation is to be applied, just use the original */
06217         if (bn_mat_is_identity(xform)) {
06218                 /* find original in the list */
06219                 for (BU_LIST_FOR(use, object_use, &dp->d_use_hd)) {
06220                         if (use->dp == dp && use->used == 0) {
06221                                 use->used = 1;
06222                                 return (dp);
06223                         }
06224                 }
06225         }
06226 
06227         /* Look for a copy that already has this transform matrix */
06228         for (BU_LIST_FOR(use, object_use, &dp->d_use_hd)) {
06229                 if (bn_mat_is_equal(xform, use->xform, &wdbp->wdb_tol)) {
06230                         /* found a match, no need to make another copy */
06231                         use->used = 1;
06232                         return(use->dp);
06233                 }
06234         }
06235 
06236         /* get a fresh use */
06237         found = DIR_NULL;
06238         for (BU_LIST_FOR(use, object_use, &dp->d_use_hd)) {
06239                 if (use->used)
06240                         continue;
06241 
06242                 found = use->dp;
06243                 use->used = 1;
06244                 MAT_COPY(use->xform, xform);
06245                 break;
06246         }
06247 
06248         if (found == DIR_NULL && dp->d_nref == 1 && dp->d_uses == 1) {
06249                 /* only one use, take it */
06250                 found = dp;
06251         }
06252 
06253         if (found == DIR_NULL) {
06254                 Tcl_AppendResult(interp, "Ran out of uses for solid ",
06255                                  dp->d_namep, "\n", (char *)NULL);
06256                 return (DIR_NULL);
06257         }
06258 
06259         if (rt_db_get_internal(&sol_int, dp, dbip, xform, &rt_uniresource) < 0) {
06260                 Tcl_AppendResult(interp, "Cannot import solid ",
06261                                  dp->d_namep, "\n", (char *)NULL);
06262                 return (DIR_NULL);
06263         }
06264 
06265         RT_CK_DB_INTERNAL(&sol_int);
06266         if (rt_db_put_internal(found, dbip, &sol_int, &rt_uniresource) < 0) {
06267                 Tcl_AppendResult(interp, "Cannot write copy solid (", found->d_namep,
06268                                  ") to database\n", (char *)NULL);
06269                 return (DIR_NULL);
06270         }
06271 
06272         return (found);
06273 }
06274 
06275 static struct directory *Copy_object(struct db_i *dbip, struct directory *dp, fastf_t *xform, Tcl_Interp *interp, struct rt_wdb *wdbp);
06276 
06277 HIDDEN void
06278 Do_copy_membs(struct db_i               *dbip,
06279               struct rt_comb_internal   *comb,
06280               union tree                *comb_leaf,
06281               genptr_t                  user_ptr1,
06282               genptr_t                  user_ptr2,
06283               genptr_t                  user_ptr3)
06284 {
06285         struct directory        *dp;
06286         struct directory        *dp_new;
06287         mat_t                   new_xform;
06288         matp_t                  xform;
06289         Tcl_Interp              *interp;
06290         struct rt_wdb           *wdbp;
06291 
06292         RT_CK_DBI(dbip);
06293         RT_CK_TREE(comb_leaf);
06294 
06295         if ((dp=db_lookup(dbip, comb_leaf->tr_l.tl_name, LOOKUP_QUIET)) == DIR_NULL)
06296                 return;
06297 
06298         xform = (matp_t)user_ptr1;
06299         interp = (Tcl_Interp *)user_ptr2;
06300         wdbp = (struct rt_wdb *)user_ptr3;
06301 
06302         /* apply transform matrix for this arc */
06303         if (comb_leaf->tr_l.tl_mat) {
06304                 bn_mat_mul(new_xform, xform, comb_leaf->tr_l.tl_mat);
06305         } else {
06306                 MAT_COPY(new_xform, xform);
06307         }
06308 
06309         /* Copy member with current tranform matrix */
06310         if ((dp_new=Copy_object(dbip, dp, new_xform, interp, wdbp)) == DIR_NULL) {
06311                 Tcl_AppendResult(interp, "Failed to copy object ",
06312                                  dp->d_namep, "\n", (char *)NULL);
06313                 return;
06314         }
06315 
06316         /* replace member name with new copy */
06317         bu_free(comb_leaf->tr_l.tl_name, "comb_leaf->tr_l.tl_name");
06318         comb_leaf->tr_l.tl_name = bu_strdup(dp_new->d_namep);
06319 
06320         /* make transform for this arc the identity matrix */
06321         if (!comb_leaf->tr_l.tl_mat) {
06322                 comb_leaf->tr_l.tl_mat = (matp_t)bu_malloc(sizeof(mat_t), "tl_mat");
06323         }
06324         MAT_IDN(comb_leaf->tr_l.tl_mat);
06325 }
06326 
06327 static struct directory *
06328 Copy_comb(struct db_i           *dbip,
06329           struct directory      *dp,
06330           mat_t                 xform,
06331           Tcl_Interp            *interp,
06332           struct rt_wdb         *wdbp)
06333 {
06334         struct object_use *use;
06335         struct directory *found;
06336         struct rt_db_internal intern;
06337         struct rt_comb_internal *comb;
06338 
06339         RT_CK_DIR(dp);
06340 
06341         /* Look for a copy that already has this transform matrix */
06342         for (BU_LIST_FOR(use, object_use, &dp->d_use_hd)) {
06343                 if (bn_mat_is_equal(xform, use->xform, &wdbp->wdb_tol)) {
06344                         /* found a match, no need to make another copy */
06345                         use->used = 1;
06346                         return (use->dp);
06347                 }
06348         }
06349 
06350         /* if we can't get records for this combination, just leave it alone */
06351         if (rt_db_get_internal(&intern, dp, dbip, (fastf_t *)NULL, &rt_uniresource) < 0)
06352                 return (dp);
06353         comb = (struct rt_comb_internal *)intern.idb_ptr;
06354 
06355         /* copy members */
06356         if (comb->tree)
06357                 db_tree_funcleaf(dbip, comb, comb->tree, Do_copy_membs,
06358                                  (genptr_t)xform, (genptr_t)interp, (genptr_t)wdbp);
06359 
06360         /* Get a use of this object */
06361         found = DIR_NULL;
06362         for (BU_LIST_FOR(use, object_use, &dp->d_use_hd)) {
06363                 /* Get a fresh use of this object */
06364                 if (use->used)
06365                         continue;       /* already used */
06366                 found = use->dp;
06367                 use->used = 1;
06368                 MAT_COPY(use->xform, xform);
06369                 break;
06370         }
06371 
06372         if (found == DIR_NULL && dp->d_nref == 1 && dp->d_uses == 1) {
06373                 /* only one use, so take original */
06374                 found = dp;
06375         }
06376 
06377         if (found == DIR_NULL) {
06378                 Tcl_AppendResult(interp, "Ran out of uses for combination ",
06379                                  dp->d_namep, "\n", (char *)NULL);
06380                 return (DIR_NULL);
06381         }
06382 
06383         if (rt_db_put_internal(found, dbip, &intern, &rt_uniresource) < 0) {
06384                 Tcl_AppendResult(interp, "rt_db_put_internal failed for ", dp->d_namep,
06385                                  "\n", (char *)NULL);
06386 #if USE_RT_COMB_IFREE
06387                 rt_comb_ifree(&intern, &rt_uniresource);
06388 #else
06389                 rt_db_free_internal(&intern, &rt_uniresource);
06390 #endif
06391                 return(DIR_NULL);
06392         }
06393 
06394         return(found);
06395 }
06396 
06397 static struct directory *
06398 Copy_object(struct db_i         *dbip,
06399             struct directory    *dp,
06400             mat_t               xform,
06401             Tcl_Interp          *interp,
06402             struct rt_wdb       *wdbp)
06403 {
06404         RT_CK_DIR(dp);
06405 
06406         if (dp->d_flags & DIR_SOLID)
06407                 return (Copy_solid(dbip, dp, xform, interp, wdbp));
06408         else
06409                 return (Copy_comb(dbip, dp, xform, interp, wdbp));
06410 }
06411 
06412 HIDDEN void
06413 Do_ref_incr(struct db_i                 *dbip,
06414             struct rt_comb_internal     *comb,
06415             union tree                  *comb_leaf,
06416             genptr_t                    user_ptr1,
06417             genptr_t                    user_ptr2,
06418             genptr_t                    user_ptr3)
06419 {
06420         struct directory *dp;
06421 
06422         RT_CK_DBI(dbip);
06423         RT_CK_TREE(comb_leaf);
06424 
06425         if ((dp = db_lookup(dbip, comb_leaf->tr_l.tl_name, LOOKUP_QUIET)) == DIR_NULL)
06426                 return;
06427 
06428         dp->d_nref++;
06429 }
06430 
06431 int
06432 wdb_xpush_cmd(struct rt_wdb     *wdbp,
06433              Tcl_Interp         *interp,
06434              int                argc,
06435              char               **argv)
06436 {
06437         struct directory *old_dp;
06438         struct rt_db_internal intern;
06439         struct rt_comb_internal *comb;
06440         struct bu_ptbl tops;
06441         mat_t xform;
06442         int i;
06443 
06444         WDB_TCL_CHECK_READ_ONLY;
06445 
06446         if (argc != 2) {
06447                 struct bu_vls vls;
06448 
06449                 bu_vls_init(&vls);
06450                 bu_vls_printf(&vls, "helplib_alias wdb_xpush %s", argv[0]);
06451                 Tcl_Eval(interp, bu_vls_addr(&vls));
06452                 bu_vls_free(&vls);
06453                 return TCL_ERROR;
06454         }
06455 
06456         /* get directory pointer for arg */
06457         if ((old_dp = db_lookup(wdbp->dbip,  argv[1], LOOKUP_NOISY)) == DIR_NULL)
06458                 return TCL_ERROR;
06459 
06460         /* Initialize use and reference counts of all directory entries */
06461         for (i=0 ; i<RT_DBNHASH ; i++) {
06462                 struct directory *dp;
06463 
06464                 for (dp=wdbp->dbip->dbi_Head[i]; dp!=DIR_NULL; dp=dp->d_forw) {
06465                         if (!(dp->d_flags & (DIR_SOLID | DIR_COMB)))
06466                                 continue;
06467 
06468                         dp->d_uses = 0;
06469                         dp->d_nref = 0;
06470                 }
06471         }
06472 
06473         /* Count uses in the tree being pushed (updates dp->d_uses) */
06474         db_functree(wdbp->dbip, old_dp, increment_uses, increment_uses, &rt_uniresource, NULL);
06475 
06476         /* Do a simple reference count to find top level objects */
06477         for (i=0 ; i<RT_DBNHASH ; i++) {
06478                 struct directory *dp;
06479 
06480                 for (dp=wdbp->dbip->dbi_Head[i] ; dp!=DIR_NULL ; dp=dp->d_forw) {
06481                         struct rt_db_internal intern;
06482                         struct rt_comb_internal *comb;
06483 
06484                         if (dp->d_flags & DIR_SOLID)
06485                                 continue;
06486 
06487                         if (!(dp->d_flags & (DIR_SOLID | DIR_COMB)))
06488                                 continue;
06489 
06490                         if (rt_db_get_internal(&intern, dp, wdbp->dbip, (fastf_t *)NULL, &rt_uniresource) < 0)
06491                                 WDB_TCL_READ_ERR_return;
06492                         comb = (struct rt_comb_internal *)intern.idb_ptr;
06493                         if (comb->tree)
06494                                 db_tree_funcleaf(wdbp->dbip, comb, comb->tree, Do_ref_incr,
06495                                                  (genptr_t )NULL, (genptr_t )NULL, (genptr_t )NULL);
06496 #if USE_RT_COMB_IFREE
06497                         rt_comb_ifree(&intern, &rt_uniresource);
06498 #else
06499                         rt_db_free_internal(&intern, &rt_uniresource);
06500 #endif
06501                 }
06502         }
06503 
06504         /* anything with zero references is a tree top */
06505         bu_ptbl_init(&tops, 0, "tops for xpush");
06506         for (i=0; i<RT_DBNHASH; i++) {
06507                 struct directory *dp;
06508 
06509                 for (dp=wdbp->dbip->dbi_Head[i]; dp!=DIR_NULL; dp=dp->d_forw) {
06510                         if (dp->d_flags & DIR_SOLID)
06511                                 continue;
06512 
06513                         if (!(dp->d_flags & (DIR_SOLID | DIR_COMB )))
06514                                 continue;
06515 
06516                         if (dp->d_nref == 0)
06517                                 bu_ptbl(&tops, BU_PTBL_INS, (long *)dp);
06518                 }
06519         }
06520 
06521         /* now re-zero the reference counts */
06522         for (i=0 ; i<RT_DBNHASH ; i++) {
06523                 struct directory *dp;
06524 
06525                 for (dp=wdbp->dbip->dbi_Head[i]; dp!=DIR_NULL; dp=dp->d_forw) {
06526                         if (!(dp->d_flags & (DIR_SOLID | DIR_COMB)))
06527                                 continue;
06528 
06529                         dp->d_nref = 0;
06530                 }
06531         }
06532 
06533         /* accurately count references in entire model */
06534         for (i=0; i<BU_PTBL_END(&tops); i++) {
06535                 struct directory *dp;
06536 
06537                 dp = (struct directory *)BU_PTBL_GET(&tops, i);
06538                 db_functree(wdbp->dbip, dp, increment_nrefs, increment_nrefs, &rt_uniresource, NULL);
06539         }
06540 
06541         /* Free list of tree-tops */
06542         bu_ptbl(&tops, BU_PTBL_FREE, (long *)NULL);
06543 
06544         /* Make new names */
06545         db_functree(wdbp->dbip, old_dp, Make_new_name, Make_new_name, &rt_uniresource, NULL);
06546 
06547         MAT_IDN(xform);
06548 
06549         /* Make new objects */
06550         if (rt_db_get_internal(&intern, old_dp, wdbp->dbip, (fastf_t *)NULL, &rt_uniresource) < 0) {
06551                 bu_log("ERROR: cannot load %s feom the database!!!\n", old_dp->d_namep);
06552                 bu_log("\tNothing has been changed!!\n");
06553                 Free_uses( wdbp->dbip );
06554                 return TCL_ERROR;
06555         }
06556 
06557         comb = (struct rt_comb_internal *)intern.idb_ptr;
06558         if (!comb->tree) {
06559                 Free_uses( wdbp->dbip );
06560                 return TCL_OK;
06561         }
06562 
06563         db_tree_funcleaf(wdbp->dbip, comb, comb->tree, Do_copy_membs,
06564                          (genptr_t)xform, (genptr_t)interp, (genptr_t)wdbp);
06565 
06566         if (rt_db_put_internal(old_dp, wdbp->dbip, &intern, &rt_uniresource) < 0) {
06567                 Tcl_AppendResult(interp, "rt_db_put_internal failed for ", old_dp->d_namep,
06568                                  "\n", (char *)NULL);
06569 #if USE_RT_COMB_IFREE
06570                 rt_comb_ifree(&intern, &rt_uniresource);
06571 #else
06572                 rt_db_free_internal(&intern, &rt_uniresource);
06573 #endif
06574                 Free_uses( wdbp->dbip );
06575                 return TCL_ERROR;
06576         }
06577 
06578         /* Free use lists and delete unused directory entries */
06579         Free_uses( wdbp->dbip );
06580         return TCL_OK;
06581 }
06582 
06583 static int
06584 wdb_xpush_tcl(ClientData        clientData,
06585              Tcl_Interp         *interp,
06586              int                argc,
06587              char               **argv)
06588 {
06589         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
06590 
06591         return wdb_xpush_cmd(wdbp, interp, argc-1, argv+1);
06592 }
06593 
06594 int
06595 wdb_whatid_cmd(struct rt_wdb    *wdbp,
06596                Tcl_Interp       *interp,
06597                int              argc,
06598                char             **argv)
06599 {
06600         struct directory        *dp;
06601         struct rt_db_internal   intern;
06602         struct rt_comb_internal *comb;
06603         struct bu_vls           vls;
06604 
06605         if (argc != 2) {
06606                 bu_vls_init(&vls);
06607                 bu_vls_printf(&vls, "helplib_alias wdb_whatid %s", argv[0]);
06608                 Tcl_Eval(interp, bu_vls_addr(&vls));
06609                 bu_vls_free(&vls);
06610                 return TCL_ERROR;
06611         }
06612 
06613         if ((dp=db_lookup(wdbp->dbip, argv[1], LOOKUP_NOISY )) == DIR_NULL )
06614                 return TCL_ERROR;
06615 
06616         if (!(dp->d_flags & DIR_REGION)) {
06617                 Tcl_AppendResult(interp, argv[1], " is not a region", (char *)NULL );
06618                 return TCL_ERROR;
06619         }
06620 
06621         if (rt_db_get_internal(&intern, dp, wdbp->dbip, (fastf_t *)NULL, &rt_uniresource) < 0)
06622                 return TCL_ERROR;
06623         comb = (struct rt_comb_internal *)intern.idb_ptr;
06624 
06625         bu_vls_init(&vls);
06626         bu_vls_printf(&vls, "%d", comb->region_id);
06627 #if USE_RT_COMB_IFREE
06628         rt_comb_ifree(&intern, &rt_uniresource);
06629 #else
06630         rt_db_free_internal(&intern, &rt_uniresource);
06631 #endif
06632         Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
06633         bu_vls_free(&vls);
06634 
06635         return TCL_OK;
06636 }
06637 
06638 /*
06639  * Usage:
06640  *        procname whatid object
06641  */
06642 static int
06643 wdb_whatid_tcl(ClientData       clientData,
06644                Tcl_Interp       *interp,
06645                int              argc,
06646                char             **argv)
06647 {
06648         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
06649 
06650         return wdb_whatid_cmd(wdbp, interp, argc-1, argv+1);
06651 }
06652 
06653 struct wdb_node_data {
06654         FILE         *fp;
06655         Tcl_Interp   *interp;
06656 };
06657 
06658 /*
06659  *                      W D B _ N O D E _ W R I T E
06660  *
06661  *  Support for the 'keep' method.
06662  *  Write each node encountered exactly once.
06663  */
06664 void
06665 wdb_node_write(struct db_i              *dbip,
06666                register struct directory *dp,
06667                genptr_t                 ptr)
06668 {
06669         struct rt_wdb           *keepfp = (struct rt_wdb *)ptr;
06670         struct rt_db_internal   intern;
06671 
06672         RT_CK_WDB(keepfp);
06673 
06674         if (dp->d_nref++ > 0)
06675                 return;         /* already written */
06676 
06677         if (rt_db_get_internal(&intern, dp, dbip, NULL, &rt_uniresource) < 0)
06678                 WDB_READ_ERR_return;
06679 
06680         /* if this is an extrusion, keep the referenced sketch */
06681         if( dp->d_major_type == DB5_MAJORTYPE_BRLCAD && dp->d_minor_type == DB5_MINORTYPE_BRLCAD_EXTRUDE ) {
06682                 struct rt_extrude_internal *extr;
06683                 struct directory *dp2;
06684 
06685                 extr = (struct rt_extrude_internal *)intern.idb_ptr;
06686                 RT_EXTRUDE_CK_MAGIC( extr );
06687 
06688                 if( (dp2 = db_lookup( dbip, extr->sketch_name, LOOKUP_QUIET )) != DIR_NULL ) {
06689                         wdb_node_write( dbip, dp2, ptr );
06690                 }
06691         } else if ( dp->d_major_type == DB5_MAJORTYPE_BRLCAD && dp->d_minor_type == DB5_MINORTYPE_BRLCAD_DSP ) {
06692                 struct rt_dsp_internal *dsp;
06693                 struct directory *dp2;
06694 
06695                 /* this is a DSP, if it uses a binary object, keep it also */
06696                 dsp = (struct rt_dsp_internal *)intern.idb_ptr;
06697                 RT_DSP_CK_MAGIC( dsp );
06698 
06699                 if( dsp->dsp_datasrc == RT_DSP_SRC_OBJ ) {
06700                         /* need to keep this object */
06701                         if( (dp2 = db_lookup( dbip, bu_vls_addr(&dsp->dsp_name),  LOOKUP_QUIET )) != DIR_NULL ) {
06702                                 wdb_node_write( dbip, dp2, ptr );
06703                         }
06704                 }
06705         }
06706 
06707         if (wdb_put_internal(keepfp, dp->d_namep, &intern, 1.0) < 0)
06708                 WDB_WRITE_ERR_return;
06709 }
06710 
06711 int
06712 wdb_keep_cmd(struct rt_wdb      *wdbp,
06713              Tcl_Interp         *interp,
06714              int                argc,
06715              char               **argv)
06716 {
06717         struct rt_wdb           *keepfp;
06718         register struct directory *dp;
06719         struct bu_vls           title;
06720         register int            i;
06721         struct db_i             *new_dbip;
06722 
06723         if (argc < 3 || MAXARGS < argc) {
06724                 struct bu_vls vls;
06725 
06726                 bu_vls_init(&vls);
06727                 bu_vls_printf(&vls, "helplib_alias wdb_keep %s", argv[0]);
06728                 Tcl_Eval(interp, bu_vls_addr(&vls));
06729                 bu_vls_free(&vls);
06730                 return TCL_ERROR;
06731         }
06732 
06733         /* First, clear any existing counts */
06734         for (i = 0; i < RT_DBNHASH; i++) {
06735                 for (dp = wdbp->dbip->dbi_Head[i]; dp != DIR_NULL; dp = dp->d_forw)
06736                         dp->d_nref = 0;
06737         }
06738 
06739         /* Alert user if named file already exists */
06740 
06741         new_dbip = db_open(argv[1], "w");
06742 
06743 
06744         if (new_dbip != DBI_NULL) {
06745             if (new_dbip->dbi_version != wdbp->dbip->dbi_version) {
06746                 Tcl_AppendResult(interp,
06747                                  "keep: File format mismatch between '",
06748                                  argv[1], "' and '",
06749                                  wdbp->dbip->dbi_filename, "'\n",
06750                                  (char *)NULL);
06751                 return TCL_ERROR;
06752             }
06753 
06754             if ((keepfp = wdb_dbopen(new_dbip, RT_WDB_TYPE_DB_DISK)) == NULL) {
06755                 Tcl_AppendResult(interp, "keep:  Error opening '", argv[1],
06756                                  "'\n", (char *)NULL);
06757                 return TCL_ERROR;
06758             } else {
06759                 Tcl_AppendResult(interp, "keep:  appending to '", argv[1],
06760                                  "'\n", (char *)NULL);
06761 
06762                 /* --- Scan geometry database and build in-memory directory --- */
06763                 db_dirbuild(new_dbip);
06764             }
06765         } else {
06766             /* Create a new database */
06767             keepfp = wdb_fopen_v(argv[1], wdbp->dbip->dbi_version);
06768 
06769             if (keepfp == NULL) {
06770                 perror(argv[1]);
06771                 return TCL_ERROR;
06772             }
06773         }
06774 
06775         /* ident record */
06776         bu_vls_init(&title);
06777         if (strncmp(wdbp->dbip->dbi_title, "Parts of: ", 10) != 0) {
06778           bu_vls_strcat(&title, "Parts of: ");
06779         }
06780         bu_vls_strcat(&title, wdbp->dbip->dbi_title);
06781 
06782         if (db_update_ident(keepfp->dbip, bu_vls_addr(&title), wdbp->dbip->dbi_local2base) < 0) {
06783                 perror("fwrite");
06784                 Tcl_AppendResult(interp, "db_update_ident() failed\n", (char *)NULL);
06785                 wdb_close(keepfp);
06786                 bu_vls_free(&title);
06787                 return TCL_ERROR;
06788         }
06789         bu_vls_free(&title);
06790 
06791         for (i = 2; i < argc; i++) {
06792                 if ((dp = db_lookup(wdbp->dbip, argv[i], LOOKUP_NOISY)) == DIR_NULL)
06793                         continue;
06794                 db_functree(wdbp->dbip, dp, wdb_node_write, wdb_node_write, &rt_uniresource, (genptr_t)keepfp);
06795         }
06796 
06797         wdb_close(keepfp);
06798         return TCL_OK;
06799 }
06800 
06801 /*
06802  * Usage:
06803  *        procname keep file object(s)
06804  */
06805 static int
06806 wdb_keep_tcl(ClientData clientData,
06807              Tcl_Interp *interp,
06808              int        argc,
06809              char       **argv)
06810 {
06811         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
06812 
06813         return wdb_keep_cmd(wdbp, interp, argc-1, argv+1);
06814 }
06815 
06816 int
06817 wdb_cat_cmd(struct rt_wdb       *wdbp,
06818             Tcl_Interp          *interp,
06819             int                 argc,
06820             char                **argv)
06821 {
06822         register struct directory       *dp;
06823         register int                    arg;
06824         struct bu_vls                   str;
06825 
06826         if (argc < 2 || MAXARGS < argc) {
06827                 struct bu_vls vls;
06828 
06829                 bu_vls_init(&vls);
06830                 bu_vls_printf(&vls, "helplib_alias wdb_cat %s", argv[0]);
06831                 Tcl_Eval(interp, bu_vls_addr(&vls));
06832                 bu_vls_free(&vls);
06833                 return TCL_ERROR;
06834         }
06835 
06836         bu_vls_init(&str);
06837         for (arg = 1; arg < argc; arg++) {
06838                 if ((dp = db_lookup(wdbp->dbip, argv[arg], LOOKUP_NOISY)) == DIR_NULL)
06839                         continue;
06840 
06841                 bu_vls_trunc(&str, 0);
06842                 wdb_do_list(wdbp->dbip, interp, &str, dp, 0);   /* non-verbose */
06843                 Tcl_AppendResult(interp, bu_vls_addr(&str), "\n", (char *)NULL);
06844         }
06845         bu_vls_free(&str);
06846 
06847         return TCL_OK;
06848 }
06849 
06850 /*
06851  * Usage:
06852  *        procname cat object(s)
06853  */
06854 static int
06855 wdb_cat_tcl(ClientData  clientData,
06856             Tcl_Interp  *interp,
06857             int         argc,
06858             char        **argv)
06859 {
06860         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
06861 
06862         return wdb_cat_cmd(wdbp, interp, argc-1, argv+1);
06863 }
06864 
06865 int
06866 wdb_instance_cmd(struct rt_wdb  *wdbp,
06867                  Tcl_Interp     *interp,
06868                  int            argc,
06869                  char           **argv)
06870 {
06871         register struct directory       *dp;
06872         char                            oper;
06873 
06874         WDB_TCL_CHECK_READ_ONLY;
06875 
06876         if (argc < 3 || 4 < argc) {
06877                 struct bu_vls vls;
06878 
06879                 bu_vls_init(&vls);
06880                 bu_vls_printf(&vls, "helplib_alias wdb_instance %s", argv[0]);
06881                 Tcl_Eval(interp, bu_vls_addr(&vls));
06882                 bu_vls_free(&vls);
06883                 return TCL_ERROR;
06884         }
06885 
06886         if ((dp = db_lookup(wdbp->dbip,  argv[1], LOOKUP_NOISY)) == DIR_NULL)
06887                 return TCL_ERROR;
06888 
06889         oper = WMOP_UNION;
06890         if (argc == 4)
06891                 oper = argv[3][0];
06892 
06893         if (oper != WMOP_UNION &&
06894             oper != WMOP_SUBTRACT &&
06895             oper != WMOP_INTERSECT) {
06896                 struct bu_vls tmp_vls;
06897 
06898                 bu_vls_init(&tmp_vls);
06899                 bu_vls_printf(&tmp_vls, "bad operation: %c\n", oper);
06900                 Tcl_AppendResult(interp, bu_vls_addr(&tmp_vls), (char *)NULL);
06901                 bu_vls_free(&tmp_vls);
06902                 return TCL_ERROR;
06903         }
06904 
06905         if (wdb_combadd(interp, wdbp->dbip, dp, argv[2], 0, oper, 0, 0, wdbp) == DIR_NULL)
06906                 return TCL_ERROR;
06907 
06908         return TCL_OK;
06909 }
06910 
06911 /*
06912  * Add instance of obj to comb.
06913  *
06914  * Usage:
06915  *        procname i obj comb [op]
06916  */
06917 static int
06918 wdb_instance_tcl(ClientData     clientData,
06919                  Tcl_Interp     *interp,
06920                  int            argc,
06921                  char           **argv)
06922 {
06923         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
06924 
06925         return wdb_instance_cmd(wdbp, interp, argc-1, argv+1);
06926 }
06927 
06928 int
06929 wdb_observer_cmd(struct rt_wdb  *wdbp,
06930                  Tcl_Interp     *interp,
06931                  int            argc,
06932                  char           **argv)
06933 {
06934         if (argc < 2) {
06935                 struct bu_vls vls;
06936 
06937                 /* return help message */
06938                 bu_vls_init(&vls);
06939                 bu_vls_printf(&vls, "helplib_alias wdb_observer %s", argv[0]);
06940                 Tcl_Eval(interp, bu_vls_addr(&vls));
06941                 bu_vls_free(&vls);
06942                 return TCL_ERROR;
06943         }
06944 
06945         return bu_cmd((ClientData)&wdbp->wdb_observers,
06946                       interp, argc - 1, argv + 1, bu_observer_cmds, 0);
06947 }
06948 
06949 /*
06950  * Attach/detach observers to/from list.
06951  *
06952  * Usage:
06953  *        procname observer cmd [args]
06954  *
06955  */
06956 static int
06957 wdb_observer_tcl(ClientData     clientData,
06958                  Tcl_Interp     *interp,
06959                  int            argc,
06960                  char           **argv)
06961 {
06962         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
06963 
06964         return wdb_observer_cmd(wdbp, interp, argc-1, argv+1);
06965 }
06966 
06967 int
06968 wdb_get_objpath_mat(struct rt_wdb               *wdbp,
06969                     Tcl_Interp                  *interp,
06970                     int                         argc,
06971                     char                        **argv,
06972                     struct wdb_trace_data       *wtdp)
06973 {
06974     int i, pos_in;
06975 
06976     /*
06977      *  paths are matched up to last input member
06978      *      ANY path the same up to this point is considered as matching
06979      */
06980 
06981     /* initialize wtd */
06982     wtdp->wtd_interp = interp;
06983     wtdp->wtd_dbip = wdbp->dbip;
06984     wtdp->wtd_flag = WDB_EVAL_ONLY;
06985     wtdp->wtd_prflag = 0;
06986 
06987     pos_in = 0;
06988 
06989     if (argc == 1 && strchr(argv[0], '/')) {
06990         char *tok;
06991         char *av0;
06992         wtdp->wtd_objpos = 0;
06993 
06994         av0 = strdup(argv[0]);
06995         tok = strtok(av0, "/");
06996         while (tok) {
06997             if ((wtdp->wtd_obj[wtdp->wtd_objpos++] =
06998                  db_lookup(wdbp->dbip, tok, LOOKUP_NOISY)) == DIR_NULL) {
06999                 Tcl_AppendResult(interp,
07000                                  "wdb_get_objpath_mat: Failed to find ",
07001                                  tok,
07002                                  "\n",
07003                                  (char *)0);
07004                 free(av0);
07005                 return TCL_ERROR;
07006             }
07007 
07008             tok = strtok((char *)0, "/");
07009         }
07010 
07011         free(av0);
07012     } else {
07013         wtdp->wtd_objpos = argc;
07014 
07015         /* build directory pointer array for desired path */
07016         for (i=0; i<wtdp->wtd_objpos; i++) {
07017             if ((wtdp->wtd_obj[i] =
07018                  db_lookup(wdbp->dbip, argv[pos_in+i], LOOKUP_NOISY)) == DIR_NULL) {
07019                 Tcl_AppendResult(interp,
07020                                  "wdb_get_objpath_mat: Failed to find ",
07021                                  argv[pos_in+i],
07022                                  "\n",
07023                                  (char *)0);
07024                 return TCL_ERROR;
07025             }
07026         }
07027     }
07028 
07029     MAT_IDN(wtdp->wtd_xform);
07030     wdb_trace(wtdp->wtd_obj[0], 0, bn_mat_identity, wtdp);
07031 
07032     return TCL_OK;
07033 }
07034 
07035 /*
07036  * This version works if the last member of the path is a primitive.
07037  */
07038 int
07039 wdb_get_obj_bounds2(struct rt_wdb               *wdbp,
07040                     Tcl_Interp                  *interp,
07041                     int                         argc,
07042                     char                        **argv,
07043                     struct wdb_trace_data       *wtdp,
07044                     point_t                     rpp_min,
07045                     point_t                     rpp_max)
07046 {
07047     register struct directory *dp;
07048     struct rt_db_internal intern;
07049     struct rt_i *rtip;
07050     struct soltab *stp;
07051     mat_t imat;
07052 
07053     /* initialize RPP bounds */
07054     VSETALL(rpp_min, MAX_FASTF);
07055     VREVERSE(rpp_max, rpp_min);
07056 
07057     if (wdb_get_objpath_mat(wdbp, interp, argc, argv, wtdp) == TCL_ERROR)
07058         return TCL_ERROR;
07059 
07060     dp = wtdp->wtd_obj[wtdp->wtd_objpos-1];
07061     if (rt_db_get_internal(&intern,
07062                            dp,
07063                            wdbp->dbip,
07064                            wtdp->wtd_xform,
07065                            &rt_uniresource) < 0) {
07066         Tcl_AppendResult(interp,
07067                          "rt_db_get_internal(",
07068                          dp->d_namep,
07069                          ") failure",
07070                          (char *)0);
07071         return TCL_ERROR;
07072     }
07073 
07074     /* Make a new rt_i instance from the existing db_i structure */
07075     if ((rtip=rt_new_rti(wdbp->dbip)) == RTI_NULL) {
07076         Tcl_AppendResult(interp,
07077                          "rt_new_rti failure for ",
07078                          wdbp->dbip->dbi_filename,
07079                          "\n",
07080                          (char *)0);
07081         return TCL_ERROR;
07082     }
07083 
07084     BU_GETSTRUCT(stp, soltab);
07085     stp->l.magic = RT_SOLTAB_MAGIC;
07086     stp->l2.magic = RT_SOLTAB2_MAGIC;
07087     stp->st_dp = dp;
07088     MAT_IDN(imat);
07089     stp->st_matp = imat;
07090 
07091     /* Get bounds from internal object */
07092     VMOVE(stp->st_min, rpp_min);
07093     VMOVE(stp->st_max, rpp_max);
07094     intern.idb_meth->ft_prep(stp, &intern, rtip);
07095     VMOVE(rpp_min, stp->st_min);
07096     VMOVE(rpp_max, stp->st_max);
07097 
07098     rt_free_rti(rtip);
07099     rt_db_free_internal(&intern, &rt_uniresource);
07100     bu_free( (char *)stp, "struct soltab" );
07101 
07102     return TCL_OK;
07103 }
07104 
07105 
07106 int
07107 wdb_get_obj_bounds(struct rt_wdb        *wdbp,
07108                    Tcl_Interp           *interp,
07109                    int                  argc,
07110                    char                 **argv,
07111                    int                  use_air,
07112                    point_t              rpp_min,
07113                    point_t              rpp_max)
07114 {
07115     register int        i;
07116     struct rt_i         *rtip;
07117     struct db_full_path path;
07118     struct region       *regp;
07119 
07120     /* Make a new rt_i instance from the existing db_i sructure */
07121     if ((rtip=rt_new_rti(wdbp->dbip)) == RTI_NULL) {
07122         Tcl_AppendResult(interp, "rt_new_rti failure for ", wdbp->dbip->dbi_filename,
07123                          "\n", (char *)NULL);
07124         return TCL_ERROR;
07125     }
07126 
07127     rtip->useair = use_air;
07128 
07129     /* Get trees for list of objects/paths */
07130     for (i = 0; i < argc; i++) {
07131         int gottree;
07132 
07133         /* Get full_path structure for argument */
07134         db_full_path_init(&path);
07135         if (db_string_to_path(&path,  rtip->rti_dbip, argv[i])) {
07136             Tcl_AppendResult(interp, "db_string_to_path failed for ",
07137                              argv[i], "\n", (char *)NULL );
07138             rt_free_rti(rtip);
07139             return TCL_ERROR;
07140         }
07141 
07142         /* check if we already got this tree */
07143         gottree = 0;
07144         for (BU_LIST_FOR(regp, region, &(rtip->HeadRegion))) {
07145             struct db_full_path tmp_path;
07146 
07147             db_full_path_init(&tmp_path);
07148             if (db_string_to_path(&tmp_path, rtip->rti_dbip, regp->reg_name)) {
07149                 Tcl_AppendResult(interp, "db_string_to_path failed for ",
07150                                  regp->reg_name, "\n", (char *)NULL);
07151                 rt_free_rti(rtip);
07152                 return TCL_ERROR;
07153             }
07154             if (path.fp_names[0] == tmp_path.fp_names[0])
07155                 gottree = 1;
07156             db_free_full_path(&tmp_path);
07157             if (gottree)
07158                 break;
07159         }
07160 
07161         /* if we don't already have it, get it */
07162         if (!gottree && rt_gettree(rtip, path.fp_names[0]->d_namep)) {
07163             Tcl_AppendResult(interp, "rt_gettree failed for ",
07164                              argv[i], "\n", (char *)NULL );
07165             rt_free_rti(rtip);
07166             return TCL_ERROR;
07167         }
07168         db_free_full_path(&path);
07169     }
07170 
07171     /* prep calculates bounding boxes of solids */
07172     rt_prep(rtip);
07173 
07174     /* initialize RPP bounds */
07175     VSETALL(rpp_min, MAX_FASTF);
07176     VREVERSE(rpp_max, rpp_min);
07177     for (i = 0; i < argc; i++) {
07178         vect_t reg_min, reg_max;
07179         struct region *regp;
07180         const char *reg_name;
07181 
07182         /* check if input name is a region */
07183         for (BU_LIST_FOR(regp, region, &(rtip->HeadRegion))) {
07184             reg_name = regp->reg_name;
07185             if (*argv[i] != '/' && *reg_name == '/')
07186                 reg_name++;
07187 
07188             if (!strcmp( reg_name, argv[i]))
07189                 goto found;
07190         }
07191         goto not_found;
07192 
07193 found:
07194         if (regp != REGION_NULL) {
07195             /* input name was a region  */
07196             if (rt_bound_tree(regp->reg_treetop, reg_min, reg_max)) {
07197                 Tcl_AppendResult(interp, "rt_bound_tree failed for ",
07198                                  regp->reg_name, "\n", (char *)NULL);
07199                 rt_free_rti(rtip);
07200                 return TCL_ERROR;
07201             }
07202             VMINMAX(rpp_min, rpp_max, reg_min);
07203             VMINMAX(rpp_min, rpp_max, reg_max);
07204         } else {
07205             int name_len;
07206 not_found:
07207 
07208             /* input name may be a group, need to check all regions under
07209              * that group
07210              */
07211             name_len = strlen( argv[i] );
07212             for (BU_LIST_FOR( regp, region, &(rtip->HeadRegion))) {
07213                 reg_name = regp->reg_name;
07214                 if (*argv[i] != '/' && *reg_name == '/')
07215                     reg_name++;
07216 
07217                 if (strncmp(argv[i], reg_name, name_len))
07218                     continue;
07219 
07220                 /* This is part of the group */
07221                 if (rt_bound_tree(regp->reg_treetop, reg_min, reg_max)) {
07222                     Tcl_AppendResult(interp, "rt_bound_tree failed for ",
07223                                      regp->reg_name, "\n", (char *)NULL);
07224                     rt_free_rti(rtip);
07225                     return TCL_ERROR;
07226                 }
07227                 VMINMAX(rpp_min, rpp_max, reg_min);
07228                 VMINMAX(rpp_min, rpp_max, reg_max);
07229             }
07230         }
07231     }
07232 
07233     rt_free_rti(rtip);
07234 
07235     return TCL_OK;
07236 }
07237 
07238 int
07239 wdb_make_bb_cmd(struct rt_wdb   *wdbp,
07240                 Tcl_Interp      *interp,
07241                 int             argc,
07242                 char            **argv)
07243 {
07244         register int            i;
07245         point_t                 rpp_min,rpp_max;
07246         struct directory        *dp;
07247         struct rt_arb_internal  *arb;
07248         struct rt_db_internal   new_intern;
07249         char                    *new_name;
07250         int                     use_air = 0;
07251 
07252         WDB_TCL_CHECK_READ_ONLY;
07253 
07254         if (argc < 3 || MAXARGS < argc) {
07255                 struct bu_vls vls;
07256 
07257                 bu_vls_init(&vls);
07258                 bu_vls_printf(&vls, "helplib_alias wdb_make_bb %s", argv[0]);
07259                 Tcl_Eval(interp, bu_vls_addr(&vls));
07260                 bu_vls_free(&vls);
07261                 return TCL_ERROR;
07262         }
07263 
07264         i = 1;
07265 
07266         /* look for a USEAIR option */
07267         if ( ! strcmp(argv[i], "-u") ) {
07268             use_air = 1;
07269             i++;
07270         }
07271 
07272         /* Since arguments may be paths, make sure first argument isn't */
07273         if (strchr(argv[i], '/')) {
07274                 Tcl_AppendResult(interp, "Do not use '/' in solid names: ", argv[i], "\n", (char *)NULL);
07275                 return TCL_ERROR;
07276         }
07277 
07278         new_name = argv[i++];
07279         if (db_lookup(wdbp->dbip, new_name, LOOKUP_QUIET) != DIR_NULL) {
07280                 Tcl_AppendResult(interp, new_name, " already exists\n", (char *)NULL);
07281                 return TCL_ERROR;
07282         }
07283 
07284         if (wdb_get_obj_bounds(wdbp, interp, argc-2, argv+2, use_air, rpp_min, rpp_max) == TCL_ERROR)
07285             return TCL_ERROR;
07286 
07287         /* build bounding RPP */
07288         arb = (struct rt_arb_internal *)bu_malloc(sizeof(struct rt_arb_internal), "arb");
07289         VMOVE(arb->pt[0], rpp_min);
07290         VSET(arb->pt[1], rpp_min[X], rpp_min[Y], rpp_max[Z]);
07291         VSET(arb->pt[2], rpp_min[X], rpp_max[Y], rpp_max[Z]);
07292         VSET(arb->pt[3], rpp_min[X], rpp_max[Y], rpp_min[Z]);
07293         VSET(arb->pt[4], rpp_max[X], rpp_min[Y], rpp_min[Z]);
07294         VSET(arb->pt[5], rpp_max[X], rpp_min[Y], rpp_max[Z]);
07295         VMOVE(arb->pt[6], rpp_max);
07296         VSET(arb->pt[7], rpp_max[X], rpp_max[Y], rpp_min[Z]);
07297         arb->magic = RT_ARB_INTERNAL_MAGIC;
07298 
07299         /* set up internal structure */
07300         RT_INIT_DB_INTERNAL(&new_intern);
07301         new_intern.idb_major_type = DB5_MAJORTYPE_BRLCAD;
07302         new_intern.idb_type = ID_ARB8;
07303         new_intern.idb_meth = &rt_functab[ID_ARB8];
07304         new_intern.idb_ptr = (genptr_t)arb;
07305 
07306         if ((dp=db_diradd( wdbp->dbip, new_name, -1L, 0, DIR_SOLID, (genptr_t)&new_intern.idb_type)) == DIR_NULL) {
07307                 Tcl_AppendResult(interp, "Cannot add ", new_name, " to directory\n", (char *)NULL);
07308                 return TCL_ERROR;
07309         }
07310 
07311         if (rt_db_put_internal(dp, wdbp->dbip, &new_intern, wdbp->wdb_resp) < 0) {
07312                 rt_db_free_internal(&new_intern, wdbp->wdb_resp);
07313                 Tcl_AppendResult(interp, "Database write error, aborting.\n", (char *)NULL);
07314                 return TCL_ERROR;
07315         }
07316 
07317         return TCL_OK;
07318 }
07319 
07320 /*
07321  *      Build an RPP bounding box for the list of objects
07322  *      and/or paths passed to this routine
07323  *
07324  *      Usage:
07325  *              dbobjname make_bb bbname obj(s)
07326  */
07327 static int
07328 wdb_make_bb_tcl(ClientData      clientData,
07329                 Tcl_Interp      *interp,
07330                 int             argc,
07331                 char            **argv)
07332 {
07333         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
07334 
07335         return wdb_make_bb_cmd(wdbp, interp, argc-1, argv+1);
07336 }
07337 
07338 int
07339 wdb_make_name_cmd(struct rt_wdb *wdbp,
07340                   Tcl_Interp    *interp,
07341                   int           argc,
07342                   char          **argv)
07343 {
07344         struct bu_vls   obj_name;
07345         char            *cp, *tp;
07346         static int      i = 0;
07347         int             len;
07348 
07349         switch (argc) {
07350         case 2:
07351                 if (strcmp(argv[1], "-s") != 0)
07352                         break;
07353                 else {
07354                         i = 0;
07355                         return TCL_OK;
07356                 }
07357         case 3:
07358                 {
07359                         int     new_i;
07360 
07361                         if ((strcmp(argv[1], "-s") == 0)
07362                             && (sscanf(argv[2], "%d", &new_i) == 1)) {
07363                                 i = new_i;
07364                                 return TCL_OK;
07365                         }
07366                 }
07367         default:
07368                 {
07369                         struct bu_vls   vls;
07370 
07371                         bu_vls_init(&vls);
07372                         bu_vls_printf(&vls, "helplib_alias wdb_make_name %s", argv[0]);
07373                         Tcl_Eval(interp, bu_vls_addr(&vls));
07374                         bu_vls_free(&vls);
07375                         return TCL_ERROR;
07376                 }
07377         }
07378 
07379         bu_vls_init(&obj_name);
07380         for (cp = argv[1], len = 0; *cp != '\0'; ++cp, ++len) {
07381                 if (*cp == '@') {
07382                         if (*(cp + 1) == '@')
07383                                 ++cp;
07384                         else
07385                                 break;
07386                 }
07387                 bu_vls_putc(&obj_name, *cp);
07388         }
07389         bu_vls_putc(&obj_name, '\0');
07390         tp = (*cp == '\0') ? "" : cp + 1;
07391 
07392         do {
07393                 bu_vls_trunc(&obj_name, len);
07394                 bu_vls_printf(&obj_name, "%d", i++);
07395                 bu_vls_strcat(&obj_name, tp);
07396         }
07397         while (db_lookup(wdbp->dbip, bu_vls_addr(&obj_name), LOOKUP_QUIET) != DIR_NULL);
07398         Tcl_AppendResult(interp, bu_vls_addr(&obj_name), (char *) NULL);
07399         bu_vls_free(&obj_name);
07400         return TCL_OK;
07401 }
07402 
07403 /*
07404  *
07405  * Generate an identifier that is guaranteed not to be the name
07406  * of any object currently in the database.
07407  *
07408  * Usage:
07409  *      dbobjname make_name (template | -s [num])
07410  *
07411  */
07412 static int
07413 wdb_make_name_tcl(ClientData    clientData,
07414                   Tcl_Interp    *interp,
07415                   int           argc,
07416                   char          **argv)
07417 
07418 {
07419         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
07420 
07421         return wdb_make_name_cmd(wdbp, interp, argc-1, argv+1);
07422 }
07423 
07424 int
07425 wdb_units_cmd(struct rt_wdb     *wdbp,
07426               Tcl_Interp        *interp,
07427               int               argc,
07428               char              **argv)
07429 {
07430         double          loc2mm;
07431         struct bu_vls   vls;
07432         const char      *str;
07433         int             sflag = 0;
07434 
07435         bu_vls_init(&vls);
07436         if (argc < 1 || 2 < argc) {
07437                 bu_vls_printf(&vls, "helplib_alias wdb_units %s", argv[0]);
07438                 Tcl_Eval(interp, bu_vls_addr(&vls));
07439                 bu_vls_free(&vls);
07440                 return TCL_ERROR;
07441         }
07442 
07443         if (argc == 2 && strcmp(argv[1], "-s") == 0) {
07444                 --argc;
07445                 ++argv;
07446 
07447                 sflag = 1;
07448         }
07449 
07450         if (argc < 2) {
07451                 str = bu_units_string(wdbp->dbip->dbi_local2base);
07452                 if (!str) str = "Unknown_unit";
07453 
07454                 if (sflag)
07455                         bu_vls_printf(&vls, "%s", str);
07456                 else
07457                         bu_vls_printf(&vls, "You are editing in '%s'.  1 %s = %g mm \n",
07458                                       str, str, wdbp->dbip->dbi_local2base );
07459 
07460                 Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
07461                 bu_vls_free(&vls);
07462                 return TCL_OK;
07463         }
07464 
07465         /* Allow inputs of the form "25cm" or "3ft" */
07466         if ((loc2mm = bu_mm_value(argv[1]) ) <= 0) {
07467                 Tcl_AppendResult(interp, argv[1], ": unrecognized unit\n",
07468                                  "valid units: <um|mm|cm|m|km|in|ft|yd|mi>\n", (char *)NULL);
07469                 bu_vls_free(&vls);
07470                 return TCL_ERROR;
07471         }
07472 
07473         if (db_update_ident(wdbp->dbip, wdbp->dbip->dbi_title, loc2mm) < 0) {
07474                 Tcl_AppendResult(interp,
07475                                  "Warning: unable to stash working units into database\n",
07476                                  (char *)NULL);
07477         }
07478 
07479         wdbp->dbip->dbi_local2base = loc2mm;
07480         wdbp->dbip->dbi_base2local = 1.0 / loc2mm;
07481 
07482         str = bu_units_string(wdbp->dbip->dbi_local2base);
07483         if (!str) str = "Unknown_unit";
07484         bu_vls_printf(&vls, "You are now editing in '%s'.  1 %s = %g mm \n",
07485                       str, str, wdbp->dbip->dbi_local2base );
07486         Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
07487         bu_vls_free(&vls);
07488 
07489         return TCL_OK;
07490 }
07491 
07492 /*
07493  * Set/get the database units.
07494  *
07495  * Usage:
07496  *        dbobjname units [str]
07497  */
07498 static int
07499 wdb_units_tcl(ClientData        clientData,
07500               Tcl_Interp        *interp,
07501               int               argc,
07502               char              **argv)
07503 {
07504         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
07505 
07506         return wdb_units_cmd(wdbp, interp, argc-1, argv+1);
07507 }
07508 
07509 int
07510 wdb_hide_cmd(struct rt_wdb      *wdbp,
07511              Tcl_Interp         *interp,
07512              int                argc,
07513              char               **argv)
07514 {
07515         struct directory                *dp;
07516         struct db_i                     *dbip;
07517         struct bu_external              ext;
07518         struct bu_external              tmp;
07519         struct db5_raw_internal         raw;
07520         int                             i;
07521 
07522         WDB_TCL_CHECK_READ_ONLY;
07523 
07524         if( argc < 2 ) {
07525                 struct bu_vls vls;
07526 
07527                 bu_vls_init( &vls );
07528                 bu_vls_printf(&vls, "helplib_alias wdb_hide %s", argv[0]);
07529                 Tcl_Eval(interp, bu_vls_addr(&vls));
07530                 bu_vls_free(&vls);
07531                 return TCL_ERROR;
07532         }
07533 
07534         RT_CK_WDB( wdbp );
07535 
07536         dbip = wdbp->dbip;
07537 
07538         RT_CK_DBI( dbip );
07539         if( dbip->dbi_version < 5 ) {
07540           Tcl_AppendResult(interp,
07541                            "Database was created with a previous release of BRL-CAD.\nSelect \"Tools->Upgrade Database...\" to enable support for this feature.",
07542                            (char *)NULL );
07543                 return TCL_ERROR;
07544         }
07545 
07546         for( i=1 ; i<argc ; i++ ) {
07547                 if( (dp = db_lookup( dbip, argv[i], LOOKUP_NOISY )) == DIR_NULL ) {
07548                         continue;
07549                 }
07550 
07551                 RT_CK_DIR( dp );
07552 
07553                 if( dp->d_major_type == DB5_MAJORTYPE_BRLCAD ) {
07554                         int no_hide=0;
07555 
07556                         /* warn the user that this might be a bad idea */
07557                         if( isatty(fileno(stdin)) && isatty(fileno(stdout))) {
07558                                 char line[80];
07559 
07560 /*XXX Ditto on the message below. Besides, it screws with the cadwidgets. */
07561 #if 0
07562                                 /* classic interactive MGED */
07563                                 while( 1 ) {
07564                                         bu_log( "Hiding BRL-CAD geometry (%s) is generaly a bad idea.\n", dp->d_namep );
07565                                         bu_log( "This may cause unexpected problems with other commands.\n" );
07566                                         bu_log( "Are you sure you want to do this?? (y/n)\n" );
07567                                         (void)fgets( line, sizeof( line ), stdin );
07568                                         if( line[0] == 'y' || line[0] == 'Y' ) break;
07569                                         if( line[0] == 'n' || line[0] == 'N' ) {
07570                                                 no_hide = 1;
07571                                                 break;
07572                                         }
07573                                 }
07574 #endif
07575                         } else if( Tcl_GetVar2Ex( interp, "tk_version", NULL, TCL_GLOBAL_ONLY ) ) {
07576 #if 0
07577                                 struct bu_vls vls;
07578 
07579 /*
07580  * We should give the user some credit here
07581  * and not annoy them with a message dialog.
07582 */
07583                                 /* Tk is active, we can pop-up a window */
07584                                 bu_vls_init( &vls );
07585                                 bu_vls_printf( &vls, "Hiding BRL-CAD geometry (%s) is generaly a bad idea.\n", dp->d_namep );
07586                                 bu_vls_strcat( &vls, "This may cause unexpected problems with other commands.\n" );
07587                                 bu_vls_strcat( &vls, "Are you sure you want to do this??" );
07588                                 (void)Tcl_ResetResult( interp );
07589                                 if( Tcl_VarEval( interp, "tk_messageBox -type yesno ",
07590                                                  "-title Warning -icon question -message {",
07591                                                  bu_vls_addr( &vls ), "}",
07592                                                  (char *)NULL ) != TCL_OK ) {
07593                                         bu_log( "Unable to post question!!!\n" );
07594                                 } else {
07595                                         const char *result;
07596 
07597                                         result = Tcl_GetStringResult( interp );
07598                                         if( !strcmp( result, "no" ) ) {
07599                                                 no_hide = 1;
07600                                         }
07601                                         (void)Tcl_ResetResult( interp );
07602                                 }
07603 #endif
07604                         }
07605                         if( no_hide )
07606                                 continue;
07607                 }
07608 
07609                 BU_INIT_EXTERNAL(&ext);
07610 
07611                 if( db_get_external( &ext, dp, dbip ) < 0 ) {
07612                         Tcl_AppendResult(interp, "db_get_external failed for ",
07613                                          dp->d_namep, " \n", (char *)NULL );
07614                         continue;
07615                 }
07616 
07617                 if (db5_get_raw_internal_ptr(&raw, ext.ext_buf) == NULL) {
07618                         Tcl_AppendResult(interp, "db5_get_raw_internal_ptr() failed for ",
07619                                          dp->d_namep, " \n", (char *)NULL );
07620                         bu_free_external( &ext );
07621                         continue;
07622                 }
07623 
07624                 raw.h_name_hidden = (unsigned char)(0x1);
07625 
07626                 BU_INIT_EXTERNAL( &tmp );
07627                 db5_export_object3( &tmp, DB5HDR_HFLAGS_DLI_APPLICATION_DATA_OBJECT,
07628                         dp->d_namep,
07629                         raw.h_name_hidden,
07630                         &raw.attributes,
07631                         &raw.body,
07632                         raw.major_type, raw.minor_type,
07633                         raw.a_zzz, raw.b_zzz );
07634                 bu_free_external( &ext );
07635 
07636                 if( db_put_external( &tmp, dp, dbip ) ) {
07637                         Tcl_AppendResult(interp, "db_put_external() failed for ",
07638                                          dp->d_namep, " \n", (char *)NULL );
07639                         bu_free_external( &tmp );
07640                         continue;
07641                 }
07642                 bu_free_external( &tmp );
07643                 dp->d_flags |= DIR_HIDDEN;
07644         }
07645 
07646         return TCL_OK;
07647 }
07648 
07649 static int
07650 wdb_hide_tcl(ClientData clientData,
07651              Tcl_Interp *interp,
07652              int        argc,
07653              char       **argv)
07654 {
07655         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
07656 
07657         return wdb_hide_cmd(wdbp, interp, argc-1, argv+1);
07658 }
07659 
07660 int
07661 wdb_unhide_cmd(struct rt_wdb    *wdbp,
07662                Tcl_Interp       *interp,
07663                int              argc,
07664                char             **argv)
07665 {
07666         struct directory                *dp;
07667         struct db_i                     *dbip;
07668         struct bu_external              ext;
07669         struct bu_external              tmp;
07670         struct db5_raw_internal         raw;
07671         int                             i;
07672 
07673         WDB_TCL_CHECK_READ_ONLY;
07674 
07675         if( argc < 2 ) {
07676                 struct bu_vls vls;
07677 
07678                 bu_vls_init( &vls );
07679                 bu_vls_printf(&vls, "helplib_alias wdb_unhide %s", argv[0]);
07680                 Tcl_Eval(interp, bu_vls_addr(&vls));
07681                 bu_vls_free(&vls);
07682                 return TCL_ERROR;
07683         }
07684 
07685         RT_CK_WDB( wdbp );
07686 
07687         dbip = wdbp->dbip;
07688 
07689         RT_CK_DBI( dbip );
07690         if( dbip->dbi_version < 5 ) {
07691           Tcl_AppendResult(interp,
07692                            "Database was created with a previous release of BRL-CAD.\nSelect \"Tools->Upgrade Database...\" to enable support for this feature.",
07693                            (char *)NULL );
07694                 return TCL_ERROR;
07695         }
07696 
07697         for( i=1 ; i<argc ; i++ ) {
07698                 if( (dp = db_lookup( dbip, argv[i], LOOKUP_NOISY )) == DIR_NULL ) {
07699                         continue;
07700                 }
07701 
07702                 RT_CK_DIR( dp );
07703 
07704                 BU_INIT_EXTERNAL(&ext);
07705 
07706                 if( db_get_external( &ext, dp, dbip ) < 0 ) {
07707                         Tcl_AppendResult(interp, "db_get_external failed for ",
07708                                          dp->d_namep, " \n", (char *)NULL );
07709                         continue;
07710                 }
07711 
07712                 if (db5_get_raw_internal_ptr(&raw, ext.ext_buf) == NULL) {
07713                         Tcl_AppendResult(interp, "db5_get_raw_internal_ptr() failed for ",
07714                                          dp->d_namep, " \n", (char *)NULL );
07715                         bu_free_external( &ext );
07716                         continue;
07717                 }
07718 
07719                 raw.h_name_hidden = (unsigned char)(0x0);
07720 
07721                 BU_INIT_EXTERNAL( &tmp );
07722                 db5_export_object3( &tmp, DB5HDR_HFLAGS_DLI_APPLICATION_DATA_OBJECT,
07723                         dp->d_namep,
07724                         raw.h_name_hidden,
07725                         &raw.attributes,
07726                         &raw.body,
07727                         raw.major_type, raw.minor_type,
07728                         raw.a_zzz, raw.b_zzz );
07729                 bu_free_external( &ext );
07730 
07731                 if( db_put_external( &tmp, dp, dbip ) ) {
07732                         Tcl_AppendResult(interp, "db_put_external() failed for ",
07733                                          dp->d_namep, " \n", (char *)NULL );
07734                         bu_free_external( &tmp );
07735                         continue;
07736                 }
07737                 bu_free_external( &tmp );
07738                 dp->d_flags &= (~DIR_HIDDEN);
07739         }
07740 
07741         return TCL_OK;
07742 }
07743 
07744 static int
07745 wdb_unhide_tcl(ClientData       clientData,
07746                Tcl_Interp       *interp,
07747                int              argc,
07748                char             **argv)
07749 {
07750         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
07751 
07752         return wdb_unhide_cmd(wdbp, interp, argc-1, argv+1);
07753 }
07754 
07755 /*              W D B _ A T T R _ C M D
07756  *
07757  *      implements the "attr" command
07758  *
07759  *      argv[1] is a sub-command:
07760  *              get - get attributes
07761  *              set - add a new attribute or replace an existing one
07762  *              rm  - remove an attribute
07763  *              append - append to an existing attribute
07764  *              edit - invoke an editor to edit all attributes
07765  *
07766  *      argv[2] is the name of the object
07767  *
07768  *      for "get" or "show", remaining args are attribute names (or none for all)
07769  *
07770  *      for "set", remaining args are attribute name, attribute value..
07771  *
07772  *      for "rm", remaining args are all attribute names
07773  *
07774  *      for "append", remaining args are attribute name, value to append, ...
07775  *
07776  *      for "edit", remaining args are attribute names
07777  */
07778 int
07779 wdb_attr_cmd(struct rt_wdb      *wdbp,
07780              Tcl_Interp         *interp,
07781              int                argc,
07782              char               **argv)
07783 {
07784         int                     i;
07785         struct directory        *dp;
07786         struct bu_attribute_value_set avs;
07787         struct bu_attribute_value_pair  *avpp;
07788 
07789         /* this is only valid for v5 databases */
07790         if( wdbp->dbip->dbi_version < 5 ) {
07791                 Tcl_AppendResult(interp, "Attributes are not available for this database format.\nPlease upgrade your database format using \"dbupgrade\" to enable attributes.", (char *)NULL );
07792                 return TCL_ERROR;
07793         }
07794 
07795         if (argc < 3 ) {
07796                 struct bu_vls vls;
07797 
07798                 bu_vls_init(&vls);
07799                 bu_vls_printf(&vls, "helplib_alias wdb_attr %s", argv[0]);
07800                 Tcl_Eval(interp, bu_vls_addr(&vls));
07801                 bu_vls_free(&vls);
07802                 return TCL_ERROR;
07803         }
07804 
07805         /* Verify that this wdb supports lookup operations
07806            (non-null dbip) */
07807         if (wdbp->dbip == 0) {
07808                 Tcl_AppendResult(interp,
07809                                  "db does not support lookup operations",
07810                                  (char *)NULL);
07811                 return TCL_ERROR;
07812         }
07813 
07814         if( (dp=db_lookup( wdbp->dbip, argv[2], LOOKUP_QUIET)) == DIR_NULL ) {
07815                 Tcl_AppendResult(interp,
07816                                  argv[2],
07817                                  " does not exist\n",
07818                                  (char *)NULL );
07819                 return TCL_ERROR;
07820         }
07821 
07822         bu_avs_init_empty(&avs);
07823         if( db5_get_attributes( wdbp->dbip, &avs, dp ) ) {
07824                 Tcl_AppendResult(interp,
07825                                  "Cannot get attributes for object ", dp->d_namep, "\n", (char *)NULL );
07826                 return TCL_ERROR;
07827         }
07828 
07829         if( strcmp( argv[1], "get" ) == 0 ) {
07830                 if( argc == 3 ) {
07831                         /* just list all the attributes */
07832                         avpp = avs.avp;
07833                         for( i=0 ; i < avs.count ; i++, avpp++ ) {
07834                                 Tcl_AppendResult(interp, avpp->name, " {",
07835                                          avpp->value, "} ", (char *)NULL );
07836                         }
07837                 } else {
07838                         const char *val;
07839                         int do_separators=argc-4; /* if more than one attribute */
07840 
07841                         for( i=3 ; i<argc ; i++ ) {
07842                                 val = bu_avs_get( &avs, argv[i] );
07843                                 if( !val ) {
07844                                         Tcl_ResetResult( interp );
07845                                         Tcl_AppendResult(interp, "Object ",
07846                                               dp->d_namep, " does not have a ",
07847                                               argv[i], " attribute\n",
07848                                               (char *)NULL );
07849                                         bu_avs_free( &avs );
07850                                         return TCL_ERROR;
07851                                 }
07852                                 if( do_separators ) {
07853                                         Tcl_AppendResult(interp,
07854                                                          "{",
07855                                                          val,
07856                                                          "} ",
07857                                                  (char *)NULL );
07858                                 } else {
07859                                         Tcl_AppendResult(interp, val,
07860                                                  (char *)NULL );
07861                                 }
07862                         }
07863                 }
07864 
07865                 bu_avs_free( &avs );
07866                 return TCL_OK;
07867 
07868         } else if( strcmp( argv[1], "set" ) == 0 ) {
07869                 /* setting attribute/value pairs */
07870                 if( (argc - 3) % 2 ) {
07871                         Tcl_AppendResult(interp,
07872                           "Error: attribute names and values must be in pairs!!!\n",
07873                           (char *)NULL );
07874                         bu_avs_free( &avs );
07875                         return TCL_ERROR;
07876                 }
07877 
07878                 i = 3;
07879                 while( i < argc ) {
07880                         (void)bu_avs_add( &avs, argv[i], argv[i+1] );
07881                         i += 2;
07882                 }
07883                 if( db5_update_attributes( dp, &avs, wdbp->dbip ) ) {
07884                         Tcl_AppendResult(interp,
07885                                       "Error: failed to update attributes\n",
07886                                       (char *)NULL );
07887                         bu_avs_free( &avs );
07888                         return TCL_ERROR;
07889                 }
07890 
07891                 /* avs is freed by db5_update_attributes() */
07892                 return TCL_OK;
07893         } else if( strcmp( argv[1], "rm" ) == 0 ) {
07894                 i = 3;
07895                 while( i < argc ) {
07896                         (void)bu_avs_remove( &avs, argv[i] );
07897                         i++;
07898                 }
07899                 if( db5_replace_attributes( dp, &avs, wdbp->dbip ) ) {
07900                         Tcl_AppendResult(interp,
07901                                  "Error: failed to update attributes\n",
07902                                   (char *)NULL );
07903                         bu_avs_free( &avs );
07904                         return TCL_ERROR;
07905                 }
07906 
07907                 /* avs is freed by db5_replace_attributes() */
07908                 return TCL_OK;
07909         } else if( strcmp( argv[1], "append" ) == 0 ) {
07910                 if( (argc-3)%2 ) {
07911                         Tcl_AppendResult(interp,
07912                           "Error: attribute names and values must be in pairs!!!\n",
07913                           (char *)NULL );
07914                         bu_avs_free( &avs );
07915                         return TCL_ERROR;
07916                 }
07917                 i = 3;
07918                 while( i < argc ) {
07919                         const char *old_val;
07920 
07921                         old_val = bu_avs_get( &avs, argv[i] );
07922                         if( !old_val ) {
07923                                 (void)bu_avs_add( &avs, argv[i], argv[i+1] );
07924                         } else {
07925                                 struct bu_vls vls;
07926 
07927                                 bu_vls_init( &vls );
07928                                 bu_vls_strcat( &vls, old_val );
07929                                 bu_vls_strcat( &vls, argv[i+1] );
07930                                 bu_avs_add_vls( &avs, argv[i], &vls );
07931                                 bu_vls_free( &vls );
07932                         }
07933 
07934                         i += 2;
07935                 }
07936                 if( db5_replace_attributes( dp, &avs, wdbp->dbip ) ) {
07937                         Tcl_AppendResult(interp,
07938                                  "Error: failed to update attributes\n",
07939                                   (char *)NULL );
07940                         bu_avs_free( &avs );
07941                         return TCL_ERROR;
07942                 }
07943 
07944                 /* avs is freed by db5_replace_attributes() */
07945                 return TCL_OK;
07946         } else if( strcmp( argv[1], "show" ) == 0 ) {
07947                 struct bu_vls vls;
07948                 int max_attr_name_len=0;
07949                 int tabs1=0;
07950 
07951                 /* pretty print */
07952                 bu_vls_init( &vls );
07953                 if( dp->d_flags & DIR_COMB ) {
07954                         if( dp->d_flags & DIR_REGION ) {
07955                                 bu_vls_printf( &vls, "%s region:\n", argv[2] );
07956                         } else {
07957                                 bu_vls_printf( &vls, "%s combination:\n", argv[2] );
07958                         }
07959                 } else if( dp->d_flags & DIR_SOLID ) {
07960                         bu_vls_printf( &vls, "%s %s:\n", argv[2],
07961                                        rt_functab[dp->d_minor_type].ft_label );
07962                 } else {
07963                     switch( dp->d_major_type ) {
07964                         case DB5_MAJORTYPE_ATTRIBUTE_ONLY:
07965                                 bu_vls_printf( &vls, "%s global:\n", argv[2] );
07966                                 break;
07967                         case DB5_MAJORTYPE_BINARY_EXPM:
07968                                 bu_vls_printf( &vls, "%s binary(expm):\n", argv[2] );
07969                                 break;
07970                         case DB5_MAJORTYPE_BINARY_MIME:
07971                                 bu_vls_printf( &vls, "%s binary(mime):\n", argv[2] );
07972                                 break;
07973                         case DB5_MAJORTYPE_BINARY_UNIF:
07974                                 bu_vls_printf( &vls, "%s %s:\n", argv[2],
07975                                                binu_types[dp->d_minor_type] );
07976                                 break;
07977                         }
07978                 }
07979                 if( argc == 3 ) {
07980                         /* just display all attributes */
07981                         avpp = avs.avp;
07982                         for( i=0 ; i < avs.count ; i++, avpp++ ) {
07983                                 int len;
07984 
07985                                 len = strlen( avpp->name );
07986                                 if( len > max_attr_name_len ) {
07987                                         max_attr_name_len = len;
07988                                 }
07989                         }
07990                         tabs1 = 2 + max_attr_name_len/8;
07991                         avpp = avs.avp;
07992                         for( i=0 ; i < avs.count ; i++, avpp++ ) {
07993                                 const char *c;
07994                                 int tabs2;
07995                                 int k;
07996                                 int len;
07997 
07998                                 bu_vls_printf( &vls, "\t%s", avpp->name );
07999                                 len = strlen( avpp->name );
08000                                 tabs2 = tabs1 - 1 - len/8;
08001                                 for( k=0 ; k<tabs2 ; k++ ) {
08002                                         bu_vls_putc( &vls, '\t' );
08003                                 }
08004                                 c = avpp->value;
08005                                 while( *c ) {
08006                                         bu_vls_putc( &vls, *c );
08007                                         if( *c == '\n' ) {
08008                                                 for( k=0 ; k<tabs1 ; k++ ) {
08009                                                         bu_vls_putc( &vls, '\t' );
08010                                                 }
08011                                         }
08012                                         c++;
08013                                 }
08014                                 bu_vls_putc( &vls, '\n' );
08015                         }
08016                 } else {
08017                         const char *val;
08018                         int len;
08019 
08020                         /* show just the specified attributes */
08021                         for( i=0 ; i<argc ; i++ ) {
08022                                 len = strlen( argv[i] );
08023                                 if( len > max_attr_name_len ) {
08024                                         max_attr_name_len = len;
08025                                 }
08026                         }
08027                         tabs1 = 2 + max_attr_name_len/8;
08028                         for( i=3 ; i<argc ; i++ ) {
08029                                 int tabs2;
08030                                 int k;
08031                                 const char *c;
08032 
08033                                 val = bu_avs_get( &avs, argv[i] );
08034                                 if( !val ) {
08035                                         Tcl_ResetResult( interp );
08036                                         Tcl_AppendResult(interp, "Object ",
08037                                               dp->d_namep, " does not have a ",
08038                                               argv[i], " attribute\n",
08039                                               (char *)NULL );
08040                                         bu_avs_free( &avs );
08041                                         return TCL_ERROR;
08042                                 }
08043                                 bu_vls_printf( &vls, "\t%s", argv[i] );
08044                                 len = strlen( val );
08045                                 tabs2 = tabs1 - 1 - len/8;
08046                                 for( k=0 ; k<tabs2 ; k++ ) {
08047                                         bu_vls_putc( &vls, '\t' );
08048                                 }
08049                                 c = val;
08050                                 while( *c ) {
08051                                         bu_vls_putc( &vls, *c );
08052                                         if( *c == '\n' ) {
08053                                                 for( k=0 ; k<tabs1 ; k++ ) {
08054                                                         bu_vls_putc( &vls, '\t' );
08055                                                 }
08056                                         }
08057                                         c++;
08058                                 }
08059                                 bu_vls_putc( &vls, '\n' );
08060                         }
08061                 }
08062                 Tcl_AppendResult(interp, bu_vls_addr( &vls ), (char *)NULL );
08063                 bu_vls_free( &vls );
08064                 return TCL_OK;
08065         } else {
08066                 struct bu_vls vls;
08067 
08068                 Tcl_AppendResult(interp,
08069                                  "ERROR: unrecognized attr subcommand ",
08070                                  argv[1], "\n",
08071                                  (char *)NULL );
08072                 bu_vls_init(&vls);
08073                 bu_vls_printf(&vls, "helplib_alias wdb_attr %s", argv[0]);
08074                 Tcl_Eval(interp, bu_vls_addr(&vls));
08075                 bu_vls_free(&vls);
08076                 return TCL_ERROR;
08077         }
08078 
08079 }
08080 
08081 int
08082 wdb_attr_tcl(ClientData clientData,
08083              Tcl_Interp     *interp,
08084              int                argc,
08085              char             **argv)
08086 {
08087         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
08088 
08089         return wdb_attr_cmd(wdbp, interp, argc-1, argv+1);
08090 }
08091 
08092 int
08093 wdb_nmg_simplify_cmd(struct rt_wdb      *wdbp,
08094                      Tcl_Interp         *interp,
08095                      int                argc,
08096                      char               **argv)
08097 {
08098         struct directory *dp;
08099         struct rt_db_internal nmg_intern;
08100         struct rt_db_internal new_intern;
08101         struct model *m;
08102         struct nmgregion *r;
08103         struct shell *s;
08104         int do_all=1;
08105         int do_arb=0;
08106         int do_tgc=0;
08107         int do_poly=0;
08108         char *new_name;
08109         char *nmg_name;
08110         int success = 0;
08111         int shell_count=0;
08112 
08113         WDB_TCL_CHECK_READ_ONLY;
08114 
08115         if (argc < 3 || 4 < argc) {
08116                 struct bu_vls vls;
08117 
08118                 bu_vls_init(&vls);
08119                 bu_vls_printf(&vls, "helplib_alias wdb_nmg_simplify %s", argv[0]);
08120                 Tcl_Eval(interp, bu_vls_addr(&vls));
08121                 bu_vls_free(&vls);
08122                 return TCL_ERROR;
08123         }
08124 
08125         RT_INIT_DB_INTERNAL(&new_intern);
08126 
08127         if (argc == 4) {
08128                 do_all = 0;
08129                 if (!strncmp(argv[1], "arb", 3))
08130                         do_arb = 1;
08131                 else if (!strncmp(argv[1], "tgc", 3))
08132                         do_tgc = 1;
08133                 else if (!strncmp(argv[1], "poly", 4))
08134                         do_poly = 1;
08135                 else {
08136                         struct bu_vls vls;
08137 
08138                         bu_vls_init(&vls);
08139                         bu_vls_printf(&vls, "helplib_alias wdb_nmg_simplify %s", argv[0]);
08140                         Tcl_Eval(interp, bu_vls_addr(&vls));
08141                         bu_vls_free(&vls);
08142                         return TCL_ERROR;
08143                 }
08144 
08145                 new_name = argv[2];
08146                 nmg_name = argv[3];
08147         } else {
08148                 new_name = argv[1];
08149                 nmg_name = argv[2];
08150         }
08151 
08152         if (db_lookup(wdbp->dbip, new_name, LOOKUP_QUIET) != DIR_NULL) {
08153                 Tcl_AppendResult(interp, new_name, " already exists\n", (char *)NULL);
08154                 return TCL_ERROR;
08155         }
08156 
08157         if ((dp=db_lookup(wdbp->dbip, nmg_name, LOOKUP_QUIET)) == DIR_NULL) {
08158                 Tcl_AppendResult(interp, nmg_name, " does not exist\n", (char *)NULL);
08159                 return TCL_ERROR;
08160         }
08161 
08162         if (rt_db_get_internal(&nmg_intern, dp, wdbp->dbip, bn_mat_identity, &rt_uniresource) < 0) {
08163                 Tcl_AppendResult(interp, "rt_db_get_internal() error\n", (char *)NULL);
08164                 return TCL_ERROR;
08165         }
08166 
08167         if (nmg_intern.idb_type != ID_NMG) {
08168                 Tcl_AppendResult(interp, nmg_name, " is not an NMG solid\n", (char *)NULL);
08169                 rt_db_free_internal(&nmg_intern, &rt_uniresource);
08170                 return TCL_ERROR;
08171         }
08172 
08173         m = (struct model *)nmg_intern.idb_ptr;
08174         NMG_CK_MODEL(m);
08175 
08176         /* count shells */
08177         for (BU_LIST_FOR(r, nmgregion, &m->r_hd)) {
08178                 for (BU_LIST_FOR(s, shell, &r->s_hd))
08179                         shell_count++;
08180         }
08181 
08182         if ((do_arb || do_all) && shell_count == 1) {
08183                 struct rt_arb_internal *arb_int;
08184 
08185                 BU_GETSTRUCT( arb_int, rt_arb_internal );
08186 
08187                 if (nmg_to_arb(m, arb_int)) {
08188                         new_intern.idb_ptr = (genptr_t)(arb_int);
08189                         new_intern.idb_major_type = DB5_MAJORTYPE_BRLCAD;
08190                         new_intern.idb_type = ID_ARB8;
08191                         new_intern.idb_meth = &rt_functab[ID_ARB8];
08192                         success = 1;
08193                 } else if (do_arb) {
08194                         /* see if we can get an arb by simplifying the NMG */
08195 
08196                         r = BU_LIST_FIRST( nmgregion, &m->r_hd );
08197                         s = BU_LIST_FIRST( shell, &r->s_hd );
08198                         nmg_shell_coplanar_face_merge( s, &wdbp->wdb_tol, 1 );
08199                         if (!nmg_kill_cracks(s)) {
08200                                 (void) nmg_model_edge_fuse( m, &wdbp->wdb_tol );
08201                                 (void) nmg_model_edge_g_fuse( m, &wdbp->wdb_tol );
08202                                 (void) nmg_unbreak_region_edges( &r->l.magic );
08203                                 if (nmg_to_arb(m, arb_int)) {
08204                                         new_intern.idb_ptr = (genptr_t)(arb_int);
08205                                         new_intern.idb_major_type = DB5_MAJORTYPE_BRLCAD;
08206                                         new_intern.idb_type = ID_ARB8;
08207                                         new_intern.idb_meth = &rt_functab[ID_ARB8];
08208                                         success = 1;
08209                                 }
08210                         }
08211                         if (!success) {
08212                                 rt_db_free_internal( &nmg_intern, &rt_uniresource );
08213                                 Tcl_AppendResult(interp, "Failed to construct an ARB equivalent to ",
08214                                                  nmg_name, "\n", (char *)NULL);
08215                                 return TCL_OK;
08216                         }
08217                 }
08218         }
08219 
08220         if ((do_tgc || do_all) && !success && shell_count == 1) {
08221                 struct rt_tgc_internal *tgc_int;
08222 
08223                 BU_GETSTRUCT( tgc_int, rt_tgc_internal );
08224 
08225                 if (nmg_to_tgc(m, tgc_int, &wdbp->wdb_tol)) {
08226                         new_intern.idb_ptr = (genptr_t)(tgc_int);
08227                         new_intern.idb_major_type = DB5_MAJORTYPE_BRLCAD;
08228                         new_intern.idb_type = ID_TGC;
08229                         new_intern.idb_meth = &rt_functab[ID_TGC];
08230                         success = 1;
08231                 } else if (do_tgc) {
08232                         rt_db_free_internal( &nmg_intern, &rt_uniresource );
08233                         Tcl_AppendResult(interp, "Failed to construct a TGC equivalent to ",
08234                                          nmg_name, "\n", (char *)NULL);
08235                         return TCL_OK;
08236                 }
08237         }
08238 
08239         /* see if we can get an arb by simplifying the NMG */
08240         if ((do_arb || do_all) && !success && shell_count == 1) {
08241                 struct rt_arb_internal *arb_int;
08242 
08243                 BU_GETSTRUCT( arb_int, rt_arb_internal );
08244 
08245                 r = BU_LIST_FIRST( nmgregion, &m->r_hd );
08246                 s = BU_LIST_FIRST( shell, &r->s_hd );
08247                 nmg_shell_coplanar_face_merge( s, &wdbp->wdb_tol, 1 );
08248                 if (!nmg_kill_cracks(s)) {
08249                         (void) nmg_model_edge_fuse( m, &wdbp->wdb_tol );
08250                         (void) nmg_model_edge_g_fuse( m, &wdbp->wdb_tol );
08251                         (void) nmg_unbreak_region_edges( &r->l.magic );
08252                         if (nmg_to_arb(m, arb_int )) {
08253                                 new_intern.idb_ptr = (genptr_t)(arb_int);
08254                                 new_intern.idb_major_type = DB5_MAJORTYPE_BRLCAD;
08255                                 new_intern.idb_type = ID_ARB8;
08256                                 new_intern.idb_meth = &rt_functab[ID_ARB8];
08257                                 success = 1;
08258                         }
08259                         else if (do_arb) {
08260                                 rt_db_free_internal( &nmg_intern, &rt_uniresource );
08261                                 Tcl_AppendResult(interp, "Failed to construct an ARB equivalent to ",
08262                                                  nmg_name, "\n", (char *)NULL);
08263                                 return TCL_OK;
08264                         }
08265                 }
08266         }
08267 
08268         if ((do_poly || do_all) && !success) {
08269                 struct rt_pg_internal *poly_int;
08270 
08271                 poly_int = (struct rt_pg_internal *)bu_malloc( sizeof( struct rt_pg_internal ), "f_nmg_simplify: poly_int" );
08272 
08273                 if (nmg_to_poly( m, poly_int, &wdbp->wdb_tol)) {
08274                         new_intern.idb_ptr = (genptr_t)(poly_int);
08275                         new_intern.idb_major_type = DB5_MAJORTYPE_BRLCAD;
08276                         new_intern.idb_type = ID_POLY;
08277                         new_intern.idb_meth = &rt_functab[ID_POLY];
08278                         success = 1;
08279                 }
08280                 else if (do_poly) {
08281                         rt_db_free_internal( &nmg_intern, &rt_uniresource );
08282                         Tcl_AppendResult(interp, nmg_name, " is not a closed surface, cannot make a polysolid\n", (char *)NULL);
08283                         return TCL_OK;
08284                 }
08285         }
08286 
08287         if (success) {
08288                 r = BU_LIST_FIRST( nmgregion, &m->r_hd );
08289                 s = BU_LIST_FIRST( shell, &r->s_hd );
08290 
08291                 if (BU_LIST_NON_EMPTY( &s->lu_hd))
08292                         Tcl_AppendResult(interp, "wire loops in ", nmg_name,
08293                                          " have been ignored in conversion\n", (char *)NULL);
08294 
08295                 if (BU_LIST_NON_EMPTY(&s->eu_hd))
08296                         Tcl_AppendResult(interp, "wire edges in ", nmg_name,
08297                                          " have been ignored in conversion\n", (char *)NULL);
08298 
08299                 if (s->vu_p)
08300                         Tcl_AppendResult(interp, "Single vertexuse in shell of ", nmg_name,
08301                                          " has been ignored in conversion\n", (char *)NULL);
08302 
08303                 rt_db_free_internal( &nmg_intern, &rt_uniresource );
08304 
08305                 if ((dp=db_diradd(wdbp->dbip, new_name, -1L, 0, DIR_SOLID, (genptr_t)&new_intern.idb_type)) == DIR_NULL) {
08306                         Tcl_AppendResult(interp, "Cannot add ", new_name, " to directory\n", (char *)NULL );
08307                         return TCL_ERROR;
08308                 }
08309 
08310                 if (rt_db_put_internal(dp, wdbp->dbip, &new_intern, &rt_uniresource) < 0) {
08311                         rt_db_free_internal( &new_intern, &rt_uniresource );
08312                         WDB_TCL_WRITE_ERR_return;
08313                 }
08314                 return TCL_OK;
08315         }
08316 
08317         Tcl_AppendResult(interp, "simplification to ", argv[1],
08318                          " is not yet supported\n", (char *)NULL);
08319         return TCL_ERROR;
08320 }
08321 
08322 /*
08323  * Usage:
08324  *        procname nmg_simplify [arb|tgc|ell|poly] new_solid nmg_solid
08325  */
08326 static int
08327 wdb_nmg_simplify_tcl(ClientData         clientData,
08328                      Tcl_Interp         *interp,
08329                      int                argc,
08330                      char               **argv)
08331 {
08332         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
08333 
08334         return wdb_nmg_simplify_cmd(wdbp, interp, argc-1, argv+1);
08335 }
08336 
08337 int
08338 wdb_nmg_collapse_cmd(struct rt_wdb      *wdbp,
08339                       Tcl_Interp        *interp,
08340                       int               argc,
08341                       char              **argv)
08342 {
08343         char *new_name;
08344         struct model *m;
08345         struct rt_db_internal intern;
08346         struct directory *dp;
08347         long count;
08348         char count_str[32];
08349         fastf_t tol_coll;
08350         fastf_t min_angle;
08351 
08352         WDB_TCL_CHECK_READ_ONLY;
08353 
08354         if (argc < 4) {
08355                 struct bu_vls vls;
08356 
08357                 bu_vls_init(&vls);
08358                 bu_vls_printf(&vls, "helplib_alias wdb_nmg_collapse %s", argv[0]);
08359                 Tcl_Eval(interp, bu_vls_addr(&vls));
08360                 bu_vls_free(&vls);
08361                 return TCL_ERROR;
08362         }
08363 
08364         if (strchr(argv[2], '/')) {
08365                 Tcl_AppendResult(interp, "Do not use '/' in solid names: ", argv[2], "\n", (char *)NULL);
08366                 return TCL_ERROR;
08367         }
08368 
08369         new_name = argv[2];
08370 
08371         if (db_lookup(wdbp->dbip, new_name, LOOKUP_QUIET) != DIR_NULL) {
08372                 Tcl_AppendResult(interp, new_name, " already exists\n", (char *)NULL);
08373                 return TCL_ERROR;
08374         }
08375 
08376         if ((dp=db_lookup(wdbp->dbip, argv[1], LOOKUP_NOISY)) == DIR_NULL)
08377                 return TCL_ERROR;
08378 
08379         if (dp->d_flags & DIR_COMB) {
08380                 Tcl_AppendResult(interp, argv[1], " is a combination, only NMG primitives are allowed here\n", (char *)NULL );
08381                 return TCL_ERROR;
08382         }
08383 
08384         if (rt_db_get_internal(&intern, dp, wdbp->dbip, (matp_t)NULL, &rt_uniresource) < 0) {
08385                 Tcl_AppendResult(interp, "Failed to get internal form of ", argv[1], "!!!!\n", (char *)NULL);
08386                 return TCL_ERROR;
08387         }
08388 
08389         if (intern.idb_type != ID_NMG) {
08390                 Tcl_AppendResult(interp, argv[1], " is not an NMG solid!!!!\n", (char *)NULL);
08391                 rt_db_free_internal(&intern, &rt_uniresource);
08392                 return TCL_ERROR;
08393         }
08394 
08395         tol_coll = atof(argv[3]) * wdbp->dbip->dbi_local2base;
08396         if (tol_coll <= 0.0) {
08397                 Tcl_AppendResult(interp, "tolerance distance too small\n", (char *)NULL);
08398                 return TCL_ERROR;
08399         }
08400 
08401         if (argc == 5) {
08402                 min_angle = atof(argv[4]);
08403                 if (min_angle < 0.0) {
08404                         Tcl_AppendResult(interp, "Minimum angle cannot be less than zero\n", (char *)NULL);
08405                         return TCL_ERROR;
08406                 }
08407         } else
08408                 min_angle = 0.0;
08409 
08410         m = (struct model *)intern.idb_ptr;
08411         NMG_CK_MODEL(m);
08412 
08413         /* triangulate model */
08414         nmg_triangulate_model(m, &wdbp->wdb_tol);
08415 
08416         count = nmg_edge_collapse(m, &wdbp->wdb_tol, tol_coll, min_angle);
08417 
08418         if ((dp=db_diradd(wdbp->dbip, new_name, -1L, 0, DIR_SOLID, (genptr_t)&intern.idb_type)) == DIR_NULL) {
08419                 Tcl_AppendResult(interp, "Cannot add ", new_name, " to directory\n", (char *)NULL);
08420                 rt_db_free_internal(&intern, &rt_uniresource);
08421                 return TCL_ERROR;
08422         }
08423 
08424         if (rt_db_put_internal(dp, wdbp->dbip, &intern, &rt_uniresource) < 0) {
08425                 rt_db_free_internal(&intern, &rt_uniresource);
08426                 WDB_TCL_WRITE_ERR_return;
08427         }
08428 
08429         rt_db_free_internal(&intern, &rt_uniresource);
08430 
08431         sprintf(count_str, "%ld", count);
08432         Tcl_AppendResult(interp, count_str, " edges collapsed\n", (char *)NULL);
08433 
08434         return TCL_OK;
08435 }
08436 
08437 /*
08438  * Usage:
08439  *        procname nmg_collapse nmg_solid new_solid maximum_error_distance [minimum_allowed_angle]
08440  */
08441 static int
08442 wdb_nmg_collapse_tcl(ClientData clientData,
08443                       Tcl_Interp        *interp,
08444                       int               argc,
08445                       char              **argv)
08446 {
08447         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
08448 
08449         return wdb_nmg_collapse_cmd(wdbp, interp, argc-1, argv+1);
08450 }
08451 
08452 int
08453 wdb_summary_cmd(struct rt_wdb   *wdbp,
08454                 Tcl_Interp      *interp,
08455                 int             argc,
08456                 char            **argv)
08457 {
08458         register char *cp;
08459         int flags = 0;
08460         int bad = 0;
08461 
08462         if (argc < 1 || 2 < argc) {
08463                 struct bu_vls vls;
08464 
08465                 bu_vls_init(&vls);
08466                 bu_vls_printf(&vls, "helplib_alias wdb_summary %s", argv[0]);
08467                 Tcl_Eval(interp, bu_vls_addr(&vls));
08468                 bu_vls_free(&vls);
08469                 return TCL_ERROR;
08470         }
08471 
08472         if (argc <= 1) {
08473                 wdb_dir_summary(wdbp->dbip, interp, 0);
08474                 return TCL_OK;
08475         }
08476 
08477         cp = argv[1];
08478         while (*cp)  switch(*cp++) {
08479         case 'p':
08480                 flags |= DIR_SOLID;
08481                 break;
08482         case 'r':
08483                 flags |= DIR_REGION;
08484                 break;
08485         case 'g':
08486                 flags |= DIR_COMB;
08487                 break;
08488         default:
08489                 Tcl_AppendResult(interp, "summary:  P R or G are only valid parmaters\n",
08490                                  (char *)NULL);
08491                 bad = 1;
08492                 break;
08493         }
08494 
08495         wdb_dir_summary(wdbp->dbip, interp, flags);
08496         return bad ? TCL_ERROR : TCL_OK;
08497 }
08498 
08499 /*
08500  * Usage:
08501  *        procname
08502  */
08503 static int
08504 wdb_summary_tcl(ClientData      clientData,
08505                 Tcl_Interp      *interp,
08506                 int             argc,
08507                 char            **argv)
08508 {
08509         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
08510 
08511         return wdb_summary_cmd(wdbp, interp, argc-1, argv+1);
08512 }
08513 
08514 int
08515 wdb_pathlist_cmd(struct rt_wdb  *wdbp,
08516                  Tcl_Interp     *interp,
08517                  int            argc,
08518                  char           **argv)
08519 {
08520         if (argc < 2 || 3 < argc) {
08521                 struct bu_vls vls;
08522 
08523                 bu_vls_init(&vls);
08524                 bu_vls_printf(&vls, "helplib_alias wdb_pathlist %s", argv[0]);
08525                 Tcl_Eval(interp, bu_vls_addr(&vls));
08526                 bu_vls_free(&vls);
08527                 return TCL_ERROR;
08528         }
08529 
08530         pathListNoLeaf = 0;
08531 
08532         if (argc == 3) {
08533             if (!strcmp(argv[1], "-noleaf"))
08534                 pathListNoLeaf = 1;
08535 
08536             ++argv;
08537             --argc;
08538         }
08539 
08540         if (db_walk_tree(wdbp->dbip, argc-1, (const char **)argv+1, 1,
08541                          &wdbp->wdb_initial_tree_state,
08542                          0, 0, wdb_pathlist_leaf_func, (genptr_t)interp) < 0) {
08543                 Tcl_AppendResult(interp, "wdb_pathlist: db_walk_tree() error", (char *)NULL);
08544                 return TCL_ERROR;
08545         }
08546 
08547         return TCL_OK;
08548 }
08549 
08550 /*
08551  * Usage:
08552  *        procname
08553  */
08554 static int
08555 wdb_pathlist_tcl(ClientData     clientData,
08556                  Tcl_Interp     *interp,
08557                  int            argc,
08558                  char           **argv)
08559 {
08560         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
08561 
08562         return wdb_pathlist_cmd(wdbp, interp, argc-1, argv+1);
08563 }
08564 
08565 int
08566 wdb_smooth_bot_cmd(struct rt_wdb        *wdbp,
08567                Tcl_Interp       *interp,
08568                int              argc,
08569                char             **argv)
08570 {
08571         char *new_bot_name, *old_bot_name;
08572         struct directory *dp_old, *dp_new;
08573         struct rt_bot_internal *old_bot;
08574         struct rt_db_internal intern;
08575         fastf_t tolerance_angle=180.0;
08576         int arg_index=1;
08577         int id;
08578 
08579         /* check that we are using a version 5 database */
08580         if( wdbp->dbip->dbi_version < 5 ) {
08581                 Tcl_AppendResult(interp, "This is an older database version.\n",
08582                         "It does not support BOT surface normals.\n",
08583                         "Use \"dbupgrade\" to upgrade this database to the current version.\n",
08584                         (char *)NULL );
08585                 return TCL_ERROR;
08586         }
08587 
08588         if( argc < 3 ) {
08589                 struct bu_vls vls;
08590 
08591                 bu_vls_init(&vls);
08592                 bu_vls_printf(&vls, "helplib_alias wdb_smooth_bot %s", argv[0]);
08593                 Tcl_Eval(interp, bu_vls_addr(&vls));
08594                 bu_vls_free(&vls);
08595                 return TCL_ERROR;
08596         }
08597 
08598         while( *argv[arg_index] == '-' ) {
08599                 /* this is an option */
08600                 if( !strcmp( argv[arg_index], "-t" ) ) {
08601                         arg_index++;
08602                         tolerance_angle = atof( argv[arg_index] );
08603                 } else {
08604                         struct bu_vls vls;
08605 
08606                         bu_vls_init(&vls);
08607                         bu_vls_printf(&vls, "helplib_alias wdb_smooth_bot %s", argv[0]);
08608                         Tcl_Eval(interp, bu_vls_addr(&vls));
08609                         bu_vls_free(&vls);
08610                         return TCL_ERROR;
08611                 }
08612                 arg_index++;
08613         }
08614 
08615         if( arg_index >= argc ) {
08616                 struct bu_vls vls;
08617 
08618                 bu_vls_init(&vls);
08619                 bu_vls_printf(&vls, "helplib_alias wdb_smooth_bot %s", argv[0]);
08620                 Tcl_Eval(interp, bu_vls_addr(&vls));
08621                 bu_vls_free(&vls);
08622                 return TCL_ERROR;
08623         }
08624 
08625         new_bot_name = argv[arg_index++];
08626         old_bot_name = argv[arg_index];
08627 
08628         if( (dp_old=db_lookup( wdbp->dbip, old_bot_name, LOOKUP_QUIET ) ) == DIR_NULL ) {
08629                 Tcl_AppendResult(interp, old_bot_name, " does not exist!!\n", (char *)NULL );
08630                 return TCL_ERROR;
08631         }
08632 
08633         if( strcmp( old_bot_name, new_bot_name ) ) {
08634 
08635                 if( (dp_new=db_lookup( wdbp->dbip, new_bot_name, LOOKUP_QUIET ) ) != DIR_NULL ) {
08636                         Tcl_AppendResult(interp, new_bot_name, " already exists!!\n", (char *)NULL );
08637                         return TCL_ERROR;
08638                 }
08639         } else {
08640                 dp_new = dp_old;
08641         }
08642 
08643         if( (id=rt_db_get_internal( &intern, dp_old, wdbp->dbip, NULL, wdbp->wdb_resp ) ) < 0 ) {
08644                 Tcl_AppendResult(interp, "Failed to get internal form of ", old_bot_name, "\n", (char *)NULL );
08645                 return TCL_ERROR;
08646         }
08647 
08648         if( id != ID_BOT ) {
08649                 Tcl_AppendResult(interp, old_bot_name, " is not a BOT primitive\n", (char *)NULL );
08650                 rt_db_free_internal( &intern, wdbp->wdb_resp );
08651                 return TCL_ERROR;
08652         }
08653 
08654         old_bot = (struct rt_bot_internal *)intern.idb_ptr;
08655         RT_BOT_CK_MAGIC( old_bot );
08656 
08657         if( rt_smooth_bot( old_bot, old_bot_name, wdbp->dbip, tolerance_angle*M_PI/180.0 ) ) {
08658                 Tcl_AppendResult(interp, "Failed to smooth ", old_bot_name, "\n", (char *)NULL );
08659                 rt_db_free_internal( &intern, wdbp->wdb_resp );
08660                 return TCL_ERROR;
08661         }
08662 
08663         if( dp_new == DIR_NULL ) {
08664                 if( (dp_new=db_diradd( wdbp->dbip, new_bot_name, -1L, 0, DIR_SOLID,
08665                                    (genptr_t)&intern.idb_type)) == DIR_NULL ) {
08666                         rt_db_free_internal(&intern, wdbp->wdb_resp);
08667                         Tcl_AppendResult(interp, "Cannot add ", new_bot_name, " to directory\n", (char *)NULL);
08668                         return TCL_ERROR;
08669                 }
08670         }
08671 
08672         if( rt_db_put_internal( dp_new, wdbp->dbip, &intern, wdbp->wdb_resp ) < 0 ) {
08673                 rt_db_free_internal(&intern, wdbp->wdb_resp);
08674                 Tcl_AppendResult(interp, "Database write error, aborting.\n", (char *)NULL);
08675                 return TCL_ERROR;
08676         }
08677 
08678         rt_db_free_internal( &intern, wdbp->wdb_resp );
08679 
08680         return TCL_OK;
08681 }
08682 
08683 static int
08684 wdb_smooth_bot_tcl(ClientData   clientData,
08685                  Tcl_Interp     *interp,
08686                  int            argc,
08687                  char           **argv)
08688 {
08689         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
08690 
08691         return wdb_smooth_bot_cmd(wdbp, interp, argc-1, argv+1);
08692 }
08693 
08694 int
08695 wdb_binary_cmd(struct rt_wdb    *wdbp,
08696                Tcl_Interp       *interp,
08697                int              argc,
08698                char             **argv)
08699 {
08700         int c;
08701         struct bu_vls   vls;
08702         unsigned int minor_type=0;
08703         char *obj_name;
08704         char *file_name;
08705         int input_mode=0;
08706         int output_mode=0;
08707         struct rt_binunif_internal *bip;
08708         struct rt_db_internal intern;
08709         struct directory *dp;
08710         char *cname;
08711 
08712         /* check that we are using a version 5 database */
08713         if( wdbp->dbip->dbi_version < 5 ) {
08714                 Tcl_AppendResult(interp, "This is an older database version.\n",
08715                         "It does not support binary objects.\n",
08716                         "Use \"dbupgrade\" to upgrade this database to the current version.\n",
08717                         (char *)NULL );
08718                 return TCL_ERROR;
08719         }
08720 
08721         bu_optind = 1;          /* re-init bu_getopt() */
08722         bu_opterr = 0;          /* suppress bu_getopt()'s error message */
08723         while ((c=bu_getopt(argc, argv, "iou:")) != EOF) {
08724                 switch (c) {
08725                         case 'i':
08726                                 input_mode = 1;
08727                                 break;
08728                         case 'o':
08729                                 output_mode = 1;
08730                                 break;
08731                         default:
08732                                 bu_vls_init( &vls );
08733                                 bu_vls_printf(&vls, "Unrecognized option - %c", c);
08734                                 Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
08735                                 bu_vls_free(&vls);
08736                                 return TCL_ERROR;
08737 
08738                 }
08739         }
08740 
08741         cname = argv[0];
08742 
08743         if( input_mode + output_mode != 1 ) {
08744                 bu_vls_init(&vls);
08745                 bu_vls_printf(&vls, "helplib_alias wdb_binary %s", cname);
08746                 Tcl_Eval(interp, bu_vls_addr(&vls));
08747                 bu_vls_free(&vls);
08748                 return TCL_ERROR;
08749         }
08750 
08751         argc -= bu_optind;
08752         argv += bu_optind;
08753 
08754         if ( (input_mode && argc != 4) || (output_mode && argc != 2) ) {
08755                 bu_vls_init(&vls);
08756                 bu_vls_printf(&vls, "helplib_alias wdb_binary %s", cname);
08757                 Tcl_Eval(interp, bu_vls_addr(&vls));
08758                 bu_vls_free(&vls);
08759                 return TCL_ERROR;
08760         }
08761 
08762 
08763         if( input_mode ) {
08764                 if (argv[0][0] == 'u') {
08765 
08766                         if (argv[1][1] != '\0') {
08767                                 bu_vls_init(&vls);
08768                                 bu_vls_printf(&vls, "Unrecognized minor type: %s", argv[1]);
08769                                 Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
08770                                 bu_vls_free(&vls);
08771                                 return TCL_ERROR;
08772                         }
08773 
08774                         switch ((int)argv[1][0]) {
08775                         case 'f':
08776                                 minor_type = DB5_MINORTYPE_BINU_FLOAT;
08777                                 break;
08778                         case 'd':
08779                                 minor_type = DB5_MINORTYPE_BINU_DOUBLE;
08780                                 break;
08781                         case 'c':
08782                                 minor_type = DB5_MINORTYPE_BINU_8BITINT;
08783                                 break;
08784                         case 's':
08785                                 minor_type = DB5_MINORTYPE_BINU_16BITINT;
08786                                 break;
08787                         case 'i':
08788                                 minor_type = DB5_MINORTYPE_BINU_32BITINT;
08789                                 break;
08790                         case 'l':
08791                                 minor_type = DB5_MINORTYPE_BINU_64BITINT;
08792                                 break;
08793                         case 'C':
08794                                 minor_type = DB5_MINORTYPE_BINU_8BITINT_U;
08795                                 break;
08796                         case 'S':
08797                                 minor_type = DB5_MINORTYPE_BINU_16BITINT_U;
08798                                 break;
08799                         case 'I':
08800                                 minor_type = DB5_MINORTYPE_BINU_32BITINT_U;
08801                                 break;
08802                         case 'L':
08803                                 minor_type = DB5_MINORTYPE_BINU_64BITINT_U;
08804                                 break;
08805                         default:
08806                                 bu_vls_init(&vls);
08807                                 bu_vls_printf(&vls, "Unrecognized minor type: %s", argv[1]);
08808                                 Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
08809                                 bu_vls_free(&vls);
08810                                 return TCL_ERROR;
08811                         }
08812                 } else {
08813                         bu_vls_init(&vls);
08814                         bu_vls_printf(&vls, "Unrecognized major type: %s", argv[0]);
08815                         Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
08816                         bu_vls_free(&vls);
08817                         return TCL_ERROR;
08818                 }
08819 
08820                 /* skip past major_type and minor_type */
08821                 argc -= 2;
08822                 argv += 2;
08823 
08824                 if( minor_type == 0 ) {
08825                         bu_vls_init(&vls);
08826                         bu_vls_printf(&vls, "helplib_alias wdb_binary %s", cname);
08827                         Tcl_Eval(interp, bu_vls_addr(&vls));
08828                         bu_vls_free(&vls);
08829                         return TCL_ERROR;
08830                 }
08831 
08832                 obj_name = *argv;
08833                 if( db_lookup( wdbp->dbip, obj_name, LOOKUP_QUIET ) != DIR_NULL ) {
08834                         bu_vls_init( &vls );
08835                         bu_vls_printf( &vls, "Object %s already exists", obj_name );
08836                         Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
08837                         bu_vls_free( &vls );
08838                         return TCL_ERROR;
08839                 }
08840 
08841                 argc--;
08842                 argv++;
08843 
08844                 file_name = *argv;
08845 
08846                 /* make a binunif of the entire file */
08847                 if( rt_mk_binunif( wdbp, obj_name, file_name, minor_type, -1 ) ) {
08848                         Tcl_AppendResult(interp, "Error creating ", obj_name,
08849                                          (char *)NULL );
08850                         return TCL_ERROR;
08851                 }
08852 
08853                 return TCL_OK;
08854 
08855         } else if( output_mode ) {
08856                 FILE *fd;
08857 
08858                 file_name = *argv;
08859 
08860                 argc--;
08861                 argv++;
08862 
08863                 obj_name = *argv;
08864 
08865                 if( (dp=db_lookup(wdbp->dbip, obj_name, LOOKUP_NOISY )) == DIR_NULL ) {
08866                         return TCL_ERROR;
08867                 }
08868                 if( !( dp->d_major_type & DB5_MAJORTYPE_BINARY_MASK) ) {
08869                         Tcl_AppendResult(interp, obj_name, " is not a binary object", (char *)NULL );
08870                         return TCL_ERROR;
08871                 }
08872 
08873                 if( dp->d_major_type != DB5_MAJORTYPE_BINARY_UNIF ) {
08874                         Tcl_AppendResult(interp, "source must be a uniform binary object",
08875                                          (char *)NULL );
08876                         return TCL_ERROR;
08877                 }
08878 
08879 #if defined(_WIN32) && !defined(__CYGWIN__)
08880                 if( (fd=fopen( file_name, "w+b")) == NULL ) {
08881 #else
08882                 if( (fd=fopen( file_name, "w+")) == NULL ) {
08883 #endif
08884                         Tcl_AppendResult(interp, "Error: cannot open file ", file_name,
08885                                          " for writing", (char *)NULL );
08886                         return TCL_ERROR;
08887                 }
08888 
08889                 if( rt_db_get_internal( &intern, dp, wdbp->dbip, NULL,
08890                                          &rt_uniresource ) < 0 ) {
08891                         Tcl_AppendResult(interp, "Error reading ", dp->d_namep,
08892                                          " from database", (char *)NULL );
08893                         fclose( fd );
08894                         return TCL_ERROR;
08895                 }
08896 
08897                 RT_CK_DB_INTERNAL( &intern );
08898 
08899                 bip = (struct rt_binunif_internal *)intern.idb_ptr;
08900                 if( bip->count < 1 ) {
08901                         Tcl_AppendResult(interp, obj_name, " has no contents", (char *)NULL );
08902                         fclose( fd );
08903                         rt_db_free_internal( &intern, &rt_uniresource );
08904                         return TCL_ERROR;
08905                 }
08906 
08907                 if( fwrite( bip->u.int8, bip->count * db5_type_sizeof_h_binu( bip->type ),
08908                             1, fd) != 1 ) {
08909                         Tcl_AppendResult(interp, "Error writing contents to file",
08910                                          (char *)NULL );
08911                         fclose( fd );
08912                         rt_db_free_internal( &intern, &rt_uniresource );
08913                         return TCL_ERROR;
08914                 }
08915 
08916                 fclose( fd );
08917                 rt_db_free_internal( &intern, &rt_uniresource );
08918                 return TCL_OK;
08919         } else {
08920                 bu_vls_init(&vls);
08921                 bu_vls_printf(&vls, "helplib_alias wdb_binary %s", cname);
08922                 Tcl_Eval(interp, bu_vls_addr(&vls));
08923                 bu_vls_free(&vls);
08924                 return TCL_ERROR;
08925         }
08926 
08927         /* should never get here */
08928         /* return TCL_ERROR; */
08929 }
08930 
08931 /*
08932  * Usage:
08933  *        procname binary args
08934  */
08935 static int
08936 wdb_binary_tcl(ClientData       clientData,
08937                Tcl_Interp       *interp,
08938                int              argc,
08939                char             **argv)
08940 {
08941         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
08942 
08943         return wdb_binary_cmd(wdbp, interp, argc-1, argv+1);
08944 }
08945 
08946 int wdb_bot_face_sort_cmd(struct rt_wdb *wdbp,
08947              Tcl_Interp         *interp,
08948              int                argc,
08949              char               **argv)
08950 {
08951         int i;
08952         int tris_per_piece=0;
08953         struct bu_vls vls;
08954         int warnings=0;
08955 
08956         if( argc < 3 ) {
08957                 bu_vls_init(&vls);
08958                 bu_vls_printf(&vls, "helplib_alias wdb_bot_face_sort %s", argv[0]);
08959                 Tcl_Eval(interp, bu_vls_addr(&vls));
08960                 bu_vls_free(&vls);
08961                 return TCL_ERROR;
08962         }
08963 
08964         tris_per_piece = atoi( argv[1] );
08965         if( tris_per_piece < 1 ) {
08966                 Tcl_AppendResult(interp, "Illegal value for triangle per piece (",
08967                                  argv[1],
08968                                  ")\n",
08969                                  (char *)NULL );
08970                 bu_vls_init(&vls);
08971                 bu_vls_printf(&vls, "helplib_alias wdb_bot_face_sort %s", argv[0]);
08972                 Tcl_Eval(interp, bu_vls_addr(&vls));
08973                 bu_vls_free(&vls);
08974                 return TCL_ERROR;
08975         }
08976 
08977         bu_vls_init( &vls );
08978         for( i=2 ; i<argc ; i++ ) {
08979                 struct directory *dp;
08980                 struct rt_db_internal intern;
08981                 struct rt_bot_internal *bot;
08982                 int id;
08983 
08984                 if( (dp=db_lookup( wdbp->dbip, argv[i], LOOKUP_NOISY ) ) == DIR_NULL ) {
08985                         continue;
08986                 }
08987 
08988                 if( (id=rt_db_get_internal( &intern, dp, wdbp->dbip, bn_mat_identity, wdbp->wdb_resp )) < 0 ) {
08989                         bu_vls_printf( &vls,
08990                            "Failed to get internal form of %s, not sorting this one\n",
08991                             dp->d_namep );
08992                         warnings++;
08993                         continue;
08994                 }
08995 
08996                 if( id != ID_BOT ) {
08997                         rt_db_free_internal( &intern, wdbp->wdb_resp );
08998                         bu_vls_printf( &vls,
08999                                        "%s is not a BOT primitive, skipped\n",
09000                                        dp->d_namep );
09001                         warnings++;
09002                         continue;
09003                 }
09004 
09005                 bot = (struct rt_bot_internal *)intern.idb_ptr;
09006                 RT_BOT_CK_MAGIC( bot );
09007 
09008                 bu_log( "processing %s (%d triangles)\n", dp->d_namep, bot->num_faces );
09009                 while( Tcl_DoOneEvent( TCL_DONT_WAIT | TCL_FILE_EVENTS ) );
09010                 if( rt_bot_sort_faces( bot, tris_per_piece ) ) {
09011                         rt_db_free_internal( &intern, wdbp->wdb_resp );
09012                         bu_vls_printf( &vls,
09013                                        "Face sort failed for %s, this BOT not sorted\n",
09014                                        dp->d_namep );
09015                         warnings++;
09016                         continue;
09017                 }
09018 
09019                 if( rt_db_put_internal( dp, wdbp->dbip, &intern, wdbp->wdb_resp ) ) {
09020                         if( warnings ) {
09021                                 Tcl_AppendResult(interp, bu_vls_addr( &vls ),
09022                                                  (char *)NULL );
09023                         }
09024                         Tcl_AppendResult(interp, "Failed to write sorted BOT (",
09025                                          dp->d_namep,
09026                                          ") to database!!! (This is very bad)\n" );
09027                         rt_db_free_internal( &intern, wdbp->wdb_resp );
09028                         bu_vls_free( &vls );
09029                         return( TCL_ERROR );
09030                 }
09031         }
09032 
09033         if( warnings ) {
09034                 Tcl_AppendResult(interp, bu_vls_addr( &vls ), (char *)NULL );
09035         }
09036         bu_vls_free( &vls );
09037         return( TCL_OK );
09038 }
09039 
09040 /*
09041  * Usage:
09042  *        procname
09043  */
09044 static int
09045 wdb_bot_face_sort_tcl(ClientData        clientData,
09046          Tcl_Interp     *interp,
09047          int            argc,
09048          char           **argv)
09049 {
09050         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
09051 
09052         return wdb_bot_face_sort_cmd(wdbp, interp, argc-1, argv+1);
09053 }
09054 
09055 
09056 /*
09057  * Usage:
09058  *        importFg4Section name sdata
09059  */
09060 static int
09061 wdb_importFg4Section_tcl(ClientData     clientData,
09062                          Tcl_Interp     *interp,
09063                          int            argc,
09064                          char           **argv)
09065 {
09066     struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
09067 
09068     return wdb_importFg4Section_cmd(wdbp, interp, argc-1, argv+1);
09069 }
09070 
09071 #if 0
09072 /* skeleton functions for wdb_obj methods */
09073 int
09074 wdb__cmd(struct rt_wdb  *wdbp,
09075              Tcl_Interp         *interp,
09076              int                argc,
09077              char               **argv)
09078 {
09079 }
09080 
09081 /*
09082  * Usage:
09083  *        procname
09084  */
09085 static int
09086 wdb__tcl(ClientData     clientData,
09087          Tcl_Interp     *interp,
09088          int            argc,
09089          char           **argv)
09090 {
09091         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
09092 
09093         return wdb__cmd(wdbp, interp, argc-1, argv+1);
09094 }
09095 #endif
09096 
09097 /****************** utility routines ********************/
09098 
09099 /*
09100  *                      W D B _ C M P D I R N A M E
09101  *
09102  * Given two pointers to pointers to directory entries, do a string compare
09103  * on the respective names and return that value.
09104  *  This routine was lifted from mged/columns.c.
09105  */
09106 int
09107 wdb_cmpdirname(const genptr_t a,
09108                const genptr_t b)
09109 {
09110         register struct directory **dp1, **dp2;
09111 
09112         dp1 = (struct directory **)a;
09113         dp2 = (struct directory **)b;
09114         return( strcmp( (*dp1)->d_namep, (*dp2)->d_namep));
09115 }
09116 
09117 #define RT_TERMINAL_WIDTH 80
09118 #define RT_COLUMNS ((RT_TERMINAL_WIDTH + V4_MAXNAME - 1) / V4_MAXNAME)
09119 
09120 /*
09121  *                      V L S _ C O L _ I T E M
09122  */
09123 void
09124 wdb_vls_col_item(struct bu_vls  *str,
09125                  register char  *cp,
09126                  int            *ccp,           /* column count pointer */
09127                  int            *clp)           /* column length pointer */
09128 {
09129         /* Output newline if last column printed. */
09130         if (*ccp >= RT_COLUMNS || (*clp+V4_MAXNAME-1) >= RT_TERMINAL_WIDTH) {
09131                 /* line now full */
09132                 bu_vls_putc(str, '\n');
09133                 *ccp = 0;
09134         } else if (*ccp != 0) {
09135                 /* Space over before starting new column */
09136                 do {
09137                         bu_vls_putc(str, ' ');
09138                         ++*clp;
09139                 }  while ((*clp % V4_MAXNAME) != 0);
09140         }
09141         /* Output string and save length for next tab. */
09142         *clp = 0;
09143         while (*cp != '\0') {
09144                 bu_vls_putc(str, *cp);
09145                 ++cp;
09146                 ++*clp;
09147         }
09148         ++*ccp;
09149 }
09150 
09151 /*
09152  */
09153 void
09154 wdb_vls_col_eol(struct bu_vls   *str,
09155                 int             *ccp,
09156                 int             *clp)
09157 {
09158         if (*ccp != 0)          /* partial line */
09159                 bu_vls_putc(str, '\n');
09160         *ccp = 0;
09161         *clp = 0;
09162 }
09163 
09164 /*
09165  *                      W D B _ V L S _ C O L _ P R 4 V
09166  *
09167  *  Given a pointer to a list of pointers to names and the number of names
09168  *  in that list, sort and print that list in column order over four columns.
09169  *  This routine was lifted from mged/columns.c.
09170  */
09171 void
09172 wdb_vls_col_pr4v(struct bu_vls          *vls,
09173                  struct directory       **list_of_names,
09174                  int                    num_in_list,
09175                  int                    no_decorate)
09176 {
09177 #if 0
09178         int lines, i, j, namelen, this_one;
09179 
09180         qsort((genptr_t)list_of_names,
09181               (unsigned)num_in_list, (unsigned)sizeof(struct directory *),
09182               (int (*)())wdb_cmpdirname);
09183 
09184         /*
09185          * For the number of (full and partial) lines that will be needed,
09186          * print in vertical format.
09187          */
09188         lines = (num_in_list + 3) / 4;
09189         for (i=0; i < lines; i++) {
09190                 for (j=0; j < 4; j++) {
09191                         this_one = j * lines + i;
09192                         /* Restrict the print to 16 chars per spec. */
09193                         bu_vls_printf(vls,  "%.16s", list_of_names[this_one]->d_namep);
09194                         namelen = strlen(list_of_names[this_one]->d_namep);
09195                         if (namelen > 16)
09196                                 namelen = 16;
09197                         /*
09198                          * Region and ident checks here....  Since the code
09199                          * has been modified to push and sort on pointers,
09200                          * the printing of the region and ident flags must
09201                          * be delayed until now.  There is no way to make the
09202                          * decision on where to place them before now.
09203                          */
09204                         if (list_of_names[this_one]->d_flags & DIR_COMB) {
09205                                 bu_vls_putc(vls, '/');
09206                                 namelen++;
09207                         }
09208                         if (list_of_names[this_one]->d_flags & DIR_REGION) {
09209                                 bu_vls_putc(vls, 'R');
09210                                 namelen++;
09211                         }
09212                         /*
09213                          * Size check (partial lines), and line termination.
09214                          * Note that this will catch the end of the lines
09215                          * that are full too.
09216                          */
09217                         if (this_one + lines >= num_in_list) {
09218                                 bu_vls_putc(vls, '\n');
09219                                 break;
09220                         } else {
09221                                 /*
09222                                  * Pad to next boundary as there will be
09223                                  * another entry to the right of this one.
09224                                  */
09225                                 while (namelen++ < 20)
09226                                         bu_vls_putc(vls, ' ');
09227                         }
09228                 }
09229         }
09230 #else
09231         int lines, i, j, k, namelen, this_one;
09232         int     maxnamelen;     /* longest name in list */
09233         int     cwidth;         /* column width */
09234         int     numcol;         /* number of columns */
09235 
09236         qsort((genptr_t)list_of_names,
09237               (unsigned)num_in_list, (unsigned)sizeof(struct directory *),
09238               (int (*)())wdb_cmpdirname);
09239 
09240         /*
09241          * Traverse the list of names, find the longest name and set the
09242          * the column width and number of columns accordingly.
09243          * If the longest name is greater than 80 characters, the number of columns
09244          * will be one.
09245          */
09246         maxnamelen = 0;
09247         for (k=0; k < num_in_list; k++) {
09248                 namelen = strlen(list_of_names[k]->d_namep);
09249                 if (namelen > maxnamelen)
09250                         maxnamelen = namelen;
09251         }
09252 
09253         if (maxnamelen <= 16)
09254                 maxnamelen = 16;
09255         cwidth = maxnamelen + 4;
09256 
09257         if (cwidth > 80)
09258                 cwidth = 80;
09259         numcol = RT_TERMINAL_WIDTH / cwidth;
09260 
09261         /*
09262          * For the number of (full and partial) lines that will be needed,
09263          * print in vertical format.
09264          */
09265         lines = (num_in_list + (numcol - 1)) / numcol;
09266         for (i=0; i < lines; i++) {
09267                 for (j=0; j < numcol; j++) {
09268                         this_one = j * lines + i;
09269                         bu_vls_printf(vls, "%s", list_of_names[this_one]->d_namep);
09270                         namelen = strlen( list_of_names[this_one]->d_namep);
09271 
09272                         /*
09273                          * Region and ident checks here....  Since the code
09274                          * has been modified to push and sort on pointers,
09275                          * the printing of the region and ident flags must
09276                          * be delayed until now.  There is no way to make the
09277                          * decision on where to place them before now.
09278                          */
09279                         if ( !no_decorate && list_of_names[this_one]->d_flags & DIR_COMB) {
09280                                 bu_vls_putc(vls, '/');
09281                                 namelen++;
09282                         }
09283 
09284                         if ( !no_decorate && list_of_names[this_one]->d_flags & DIR_REGION) {
09285                                 bu_vls_putc(vls, 'R');
09286                                 namelen++;
09287                         }
09288 
09289                         /*
09290                          * Size check (partial lines), and line termination.
09291                          * Note that this will catch the end of the lines
09292                          * that are full too.
09293                          */
09294                         if (this_one + lines >= num_in_list) {
09295                                 bu_vls_putc(vls, '\n');
09296                                 break;
09297                         } else {
09298                                 /*
09299                                  * Pad to next boundary as there will be
09300                                  * another entry to the right of this one.
09301                                  */
09302                                 while( namelen++ < cwidth)
09303                                         bu_vls_putc(vls, ' ');
09304                         }
09305                 }
09306         }
09307 #endif
09308 }
09309 
09310 void
09311 wdb_vls_long_dpp(struct bu_vls          *vls,
09312                  struct directory       **list_of_names,
09313                  int                    num_in_list,
09314                  int                    aflag,          /* print all objects */
09315                  int                    cflag,          /* print combinations */
09316                  int                    rflag,          /* print regions */
09317                  int                    sflag)          /* print solids */
09318 {
09319         int i;
09320         int isComb=0, isRegion=0;
09321         int isSolid=0;
09322         const char *type=NULL;
09323         int max_nam_len = 0;
09324         int max_type_len = 0;
09325         struct directory *dp;
09326 
09327         qsort((genptr_t)list_of_names,
09328               (unsigned)num_in_list, (unsigned)sizeof(struct directory *),
09329               (int (*)())wdb_cmpdirname);
09330 
09331         for (i=0 ; i < num_in_list ; i++) {
09332                 int len;
09333 
09334                 dp = list_of_names[i];
09335                 len = strlen(dp->d_namep);
09336                 if (len > max_nam_len)
09337                         max_nam_len = len;
09338 
09339                 if (dp->d_flags & DIR_REGION)
09340                         len = 6;
09341                 else if (dp->d_flags & DIR_COMB)
09342                         len = 4;
09343                 else if( dp->d_flags & DIR_SOLID )
09344                         len = strlen(rt_functab[dp->d_minor_type].ft_label);
09345                 else {
09346                         switch(list_of_names[i]->d_major_type) {
09347                         case DB5_MAJORTYPE_ATTRIBUTE_ONLY:
09348                                 len = 6;
09349                                 break;
09350                         case DB5_MAJORTYPE_BINARY_MIME:
09351                                 len = strlen( "binary (mime)" );
09352                                 break;
09353                         case DB5_MAJORTYPE_BINARY_UNIF:
09354                                 len = strlen( binu_types[list_of_names[i]->d_minor_type] );
09355                                 break;
09356                         case DB5_MAJORTYPE_BINARY_EXPM:
09357                                 len = strlen( "binary(expm)" );
09358                                 break;
09359                         }
09360                 }
09361 
09362                 if (len > max_type_len)
09363                         max_type_len = len;
09364         }
09365 
09366         /*
09367          * i - tracks the list item
09368          */
09369         for (i=0; i < num_in_list; ++i) {
09370                 if (list_of_names[i]->d_flags & DIR_COMB) {
09371                         isComb = 1;
09372                         isSolid = 0;
09373                         type = "comb";
09374 
09375                         if (list_of_names[i]->d_flags & DIR_REGION) {
09376                                 isRegion = 1;
09377                                 type = "region";
09378                         } else
09379                                 isRegion = 0;
09380                 } else if( list_of_names[i]->d_flags & DIR_SOLID )  {
09381                         isComb = isRegion = 0;
09382                         isSolid = 1;
09383                         type = rt_functab[list_of_names[i]->d_minor_type].ft_label;
09384                 } else {
09385                         switch(list_of_names[i]->d_major_type) {
09386                         case DB5_MAJORTYPE_ATTRIBUTE_ONLY:
09387                                 isSolid = 0;
09388                                 type = "global";
09389                                 break;
09390                         case DB5_MAJORTYPE_BINARY_EXPM:
09391                                 isSolid = 0;
09392                                 isRegion = 0;
09393                                 type = "binary(expm)";
09394                                 break;
09395                         case DB5_MAJORTYPE_BINARY_MIME:
09396                                 isSolid = 0;
09397                                 isRegion = 0;
09398                                 type = "binary(mime)";
09399                                 break;
09400                         case DB5_MAJORTYPE_BINARY_UNIF:
09401                                 isSolid = 0;
09402                                 isRegion = 0;
09403                                 type = binu_types[list_of_names[i]->d_minor_type];
09404                                 break;
09405                         }
09406                 }
09407 
09408                 /* print list item i */
09409                 dp = list_of_names[i];
09410                 if (aflag ||
09411                     (!cflag && !rflag && !sflag) ||
09412                     (cflag && isComb) ||
09413                     (rflag && isRegion) ||
09414                     (sflag && isSolid)) {
09415                         bu_vls_printf(vls, "%s", dp->d_namep );
09416                         bu_vls_spaces(vls, max_nam_len - strlen( dp->d_namep ) );
09417                         bu_vls_printf(vls, " %s", type );
09418                         bu_vls_spaces(vls, max_type_len - strlen( type ) );
09419                         bu_vls_printf(vls,  " %2d %2d %ld\n",
09420                                       dp->d_major_type, dp->d_minor_type, (long)(dp->d_len));
09421                 }
09422         }
09423 }
09424 
09425 /*
09426  *                      W D B _ V L S _ L I N E _ D P P
09427  *
09428  *  Given a pointer to a list of pointers to names and the number of names
09429  *  in that list, sort and print that list on the same line.
09430  *  This routine was lifted from mged/columns.c.
09431  */
09432 void
09433 wdb_vls_line_dpp(struct bu_vls  *vls,
09434                  struct directory **list_of_names,
09435                  int            num_in_list,
09436                  int            aflag,  /* print all objects */
09437                  int            cflag,  /* print combinations */
09438                  int            rflag,  /* print regions */
09439                  int            sflag)  /* print solids */
09440 {
09441         int i;
09442         int isComb, isRegion;
09443         int isSolid;
09444 
09445         qsort( (genptr_t)list_of_names,
09446                (unsigned)num_in_list, (unsigned)sizeof(struct directory *),
09447                (int (*)())wdb_cmpdirname);
09448 
09449         /*
09450          * i - tracks the list item
09451          */
09452         for (i=0; i < num_in_list; ++i) {
09453                 if (list_of_names[i]->d_flags & DIR_COMB) {
09454                         isComb = 1;
09455                         isSolid = 0;
09456 
09457                         if (list_of_names[i]->d_flags & DIR_REGION)
09458                                 isRegion = 1;
09459                         else
09460                                 isRegion = 0;
09461                 } else {
09462                         isComb = isRegion = 0;
09463                         isSolid = 1;
09464                 }
09465 
09466                 /* print list item i */
09467                 if (aflag ||
09468                     (!cflag && !rflag && !sflag) ||
09469                     (cflag && isComb) ||
09470                     (rflag && isRegion) ||
09471                     (sflag && isSolid)) {
09472                         bu_vls_printf(vls,  "%s ", list_of_names[i]->d_namep);
09473                 }
09474         }
09475 }
09476 
09477 /*
09478  *                      W D B _ G E T S P A C E
09479  *
09480  * This routine walks through the directory entry list and mallocs enough
09481  * space for pointers to hold:
09482  *  a) all of the entries if called with an argument of 0, or
09483  *  b) the number of entries specified by the argument if > 0.
09484  *  This routine was lifted from mged/dir.c.
09485  */
09486 struct directory **
09487 wdb_getspace(struct db_i        *dbip,
09488              register int       num_entries)
09489 {
09490         register struct directory **dir_basep;
09491 
09492         if (num_entries < 0) {
09493                 bu_log("wdb_getspace: was passed %d, used 0\n",
09494                        num_entries);
09495                 num_entries = 0;
09496         }
09497 
09498         if (num_entries == 0)  num_entries = db_get_directory_size(dbip);
09499 
09500         /* Allocate and cast num_entries worth of pointers */
09501         dir_basep = (struct directory **) bu_malloc((num_entries+1) * sizeof(struct directory *),
09502                                                     "wdb_getspace *dir[]" );
09503         return(dir_basep);
09504 }
09505 
09506 /*
09507  *                      W D B _ D O _ L I S T
09508  */
09509 void
09510 wdb_do_list(struct db_i         *dbip,
09511             Tcl_Interp          *interp,
09512             struct bu_vls       *outstrp,
09513             register struct directory *dp,
09514             int                 verbose)
09515 {
09516         int                     id;
09517         struct rt_db_internal   intern;
09518 
09519         RT_CK_DBI(dbip);
09520 
09521         if( dp->d_major_type == DB5_MAJORTYPE_ATTRIBUTE_ONLY ) {
09522                 /* this is the _GLOBAL object */
09523                 struct bu_attribute_value_set avs;
09524                 struct bu_attribute_value_pair  *avp;
09525 
09526                 bu_vls_strcat( outstrp, dp->d_namep );
09527                 bu_vls_strcat( outstrp, ": global attributes object\n" );
09528                 bu_avs_init_empty(&avs);
09529                 if( db5_get_attributes( dbip, &avs, dp ) ) {
09530                         Tcl_AppendResult(interp, "Cannot get attributes for ", dp->d_namep,
09531                                          "\n", (char *)NULL );
09532                         return;
09533                 }
09534                 for( BU_AVS_FOR( avp, &avs ) ) {
09535                         if( !strcmp( avp->name, "units" ) ) {
09536                                 double conv;
09537                                 const char *str;
09538 
09539                                 conv = atof( avp->value );
09540                                 bu_vls_strcat( outstrp, "\tunits: " );
09541                                 if( (str=bu_units_string( conv ) ) == NULL ) {
09542                                         bu_vls_strcat( outstrp, "Unrecognized units\n" );
09543                                 } else {
09544                                         bu_vls_strcat( outstrp, str );
09545                                         bu_vls_putc( outstrp, '\n' );
09546                                 }
09547                         } else {
09548                                 bu_vls_putc( outstrp, '\t' );
09549                                 bu_vls_strcat( outstrp, avp->name );
09550                                 bu_vls_strcat( outstrp, ": " );
09551                                 bu_vls_strcat( outstrp, avp->value );
09552                                 bu_vls_putc( outstrp, '\n' );
09553                         }
09554                 }
09555         } else {
09556 
09557                 if ((id = rt_db_get_internal(&intern, dp, dbip,
09558                                              (fastf_t *)NULL, &rt_uniresource)) < 0) {
09559                         Tcl_AppendResult(interp, "rt_db_get_internal(", dp->d_namep,
09560                                          ") failure\n", (char *)NULL);
09561                         return;
09562                 }
09563 
09564                 bu_vls_printf(outstrp, "%s:  ", dp->d_namep);
09565 
09566                 if (rt_functab[id].ft_describe(outstrp, &intern,
09567                                                verbose, dbip->dbi_base2local, &rt_uniresource, dbip) < 0)
09568                         Tcl_AppendResult(interp, dp->d_namep, ": describe error\n", (char *)NULL);
09569                 rt_db_free_internal(&intern, &rt_uniresource);
09570         }
09571 }
09572 
09573 /*
09574  *                      W D B _ C O M B A D D
09575  *
09576  * Add an instance of object 'objp' to combination 'name'.
09577  * If the combination does not exist, it is created.
09578  * region_flag is 1 (region), or 0 (group).
09579  *
09580  *  Preserves the GIFT semantics.
09581  */
09582 struct directory *
09583 wdb_combadd(Tcl_Interp                  *interp,
09584             struct db_i                 *dbip,
09585             register struct directory   *objp,
09586             char                        *combname,
09587             int                         region_flag,    /* true if adding region */
09588             int                         relation,       /* = UNION, SUBTRACT, INTERSECT */
09589             int                         ident,          /* "Region ID" */
09590             int                         air,            /* Air code */
09591             struct rt_wdb               *wdbp)
09592 {
09593         register struct directory *dp;
09594         struct rt_db_internal intern;
09595         struct rt_comb_internal *comb;
09596         union tree *tp;
09597         struct rt_tree_array *tree_list;
09598         int node_count;
09599         int actual_count;
09600 
09601         /*
09602          * Check to see if we have to create a new combination
09603          */
09604         if ((dp = db_lookup(dbip,  combname, LOOKUP_QUIET)) == DIR_NULL) {
09605                 int flags;
09606 
09607                 if (region_flag)
09608                         flags = DIR_REGION | DIR_COMB;
09609                 else
09610                         flags = DIR_COMB;
09611 
09612                 RT_INIT_DB_INTERNAL(&intern);
09613                 intern.idb_major_type = DB5_MAJORTYPE_BRLCAD;
09614                 intern.idb_type = ID_COMBINATION;
09615                 intern.idb_meth = &rt_functab[ID_COMBINATION];
09616 
09617                 /* Update the in-core directory */
09618                 if ((dp = db_diradd(dbip, combname, -1, 0, flags, (genptr_t)&intern.idb_type)) == DIR_NULL)  {
09619                         Tcl_AppendResult(interp, "An error has occured while adding '",
09620                                          combname, "' to the database.\n", (char *)NULL);
09621                         return DIR_NULL;
09622                 }
09623 
09624                 BU_GETSTRUCT(comb, rt_comb_internal);
09625                 intern.idb_ptr = (genptr_t)comb;
09626                 comb->magic = RT_COMB_MAGIC;
09627                 bu_vls_init(&comb->shader);
09628                 bu_vls_init(&comb->material);
09629                 comb->region_id = 0;  /* This makes a comb/group by default */
09630                 comb->tree = TREE_NULL;
09631 
09632                 if (region_flag) {
09633                         struct bu_vls tmp_vls;
09634 
09635                         comb->region_flag = 1;
09636                         comb->region_id = ident;
09637                         comb->aircode = air;
09638                         comb->los = wdbp->wdb_los_default;
09639                         comb->GIFTmater = wdbp->wdb_mat_default;
09640                         bu_vls_init(&tmp_vls);
09641                         bu_vls_printf(&tmp_vls,
09642                                       "Creating region id=%d, air=%d, GIFTmaterial=%d, los=%d\n",
09643                                       ident, air,
09644                                         wdbp->wdb_mat_default,
09645                                         wdbp->wdb_los_default);
09646                         Tcl_AppendResult(interp, bu_vls_addr(&tmp_vls), (char *)NULL);
09647                         bu_vls_free(&tmp_vls);
09648                 } else {
09649                         comb->region_flag = 0;
09650                 }
09651                 RT_GET_TREE( tp, &rt_uniresource );
09652                 tp->magic = RT_TREE_MAGIC;
09653                 tp->tr_l.tl_op = OP_DB_LEAF;
09654                 tp->tr_l.tl_name = bu_strdup( objp->d_namep );
09655                 tp->tr_l.tl_mat = (matp_t)NULL;
09656                 comb->tree = tp;
09657 
09658                 if (rt_db_put_internal(dp, dbip, &intern, &rt_uniresource) < 0) {
09659                         Tcl_AppendResult(interp, "Failed to write ", dp->d_namep, (char *)NULL );
09660                         return DIR_NULL;
09661                 }
09662                 return dp;
09663         } else if (!(dp->d_flags & DIR_COMB)) {
09664                 Tcl_AppendResult(interp, combname, " exists, but is not a combination\n", (char *)NULL);
09665                 return DIR_NULL;
09666         }
09667 
09668         /* combination exists, add a new member */
09669         if (rt_db_get_internal(&intern, dp, dbip, (fastf_t *)NULL, &rt_uniresource) < 0) {
09670                 Tcl_AppendResult(interp, "read error, aborting\n", (char *)NULL);
09671                 return DIR_NULL;
09672         }
09673 
09674         comb = (struct rt_comb_internal *)intern.idb_ptr;
09675         RT_CK_COMB(comb);
09676 
09677         if (region_flag && !comb->region_flag) {
09678                 Tcl_AppendResult(interp, combname, ": not a region\n", (char *)NULL);
09679                 return DIR_NULL;
09680         }
09681 
09682         if (comb->tree && db_ck_v4gift_tree(comb->tree) < 0) {
09683                 db_non_union_push(comb->tree, &rt_uniresource);
09684                 if (db_ck_v4gift_tree(comb->tree) < 0) {
09685                         Tcl_AppendResult(interp, "Cannot flatten tree for editing\n", (char *)NULL);
09686                         rt_db_free_internal(&intern, &rt_uniresource);
09687                         return DIR_NULL;
09688                 }
09689         }
09690 
09691         /* make space for an extra leaf */
09692         node_count = db_tree_nleaves( comb->tree ) + 1;
09693         tree_list = (struct rt_tree_array *)bu_calloc( node_count,
09694                                                        sizeof( struct rt_tree_array ), "tree list" );
09695 
09696         /* flatten tree */
09697         if (comb->tree) {
09698                 actual_count = 1 + (struct rt_tree_array *)db_flatten_tree(
09699                         tree_list, comb->tree, OP_UNION, 1, &rt_uniresource )
09700                         - tree_list;
09701                 BU_ASSERT_LONG( actual_count, ==, node_count );
09702                 comb->tree = TREE_NULL;
09703         }
09704 
09705         /* insert new member at end */
09706         switch (relation) {
09707         case '+':
09708                 tree_list[node_count - 1].tl_op = OP_INTERSECT;
09709                 break;
09710         case '-':
09711                 tree_list[node_count - 1].tl_op = OP_SUBTRACT;
09712                 break;
09713         default:
09714                 Tcl_AppendResult(interp, "unrecognized relation (assume UNION)\n",
09715                                  (char *)NULL );
09716         case 'u':
09717                 tree_list[node_count - 1].tl_op = OP_UNION;
09718                 break;
09719         }
09720 
09721         /* make new leaf node, and insert at end of list */
09722         RT_GET_TREE( tp, &rt_uniresource );
09723         tree_list[node_count-1].tl_tree = tp;
09724         tp->tr_l.magic = RT_TREE_MAGIC;
09725         tp->tr_l.tl_op = OP_DB_LEAF;
09726         tp->tr_l.tl_name = bu_strdup( objp->d_namep );
09727         tp->tr_l.tl_mat = (matp_t)NULL;
09728 
09729         /* rebuild the tree */
09730         comb->tree = (union tree *)db_mkgift_tree( tree_list, node_count, &rt_uniresource );
09731 
09732         /* and finally, write it out */
09733         if (rt_db_put_internal(dp, dbip, &intern, &rt_uniresource) < 0) {
09734                 Tcl_AppendResult(interp, "Failed to write ", dp->d_namep, (char *)NULL);
09735                 return DIR_NULL;
09736         }
09737 
09738         bu_free((char *)tree_list, "combadd: tree_list");
09739 
09740         return (dp);
09741 }
09742 
09743 static void
09744 wdb_do_identitize(struct db_i           *dbip,
09745                   struct rt_comb_internal *comb,
09746                   union tree            *comb_leaf,
09747                   genptr_t              user_ptr1,
09748                   genptr_t              user_ptr2,
09749                   genptr_t              user_ptr3)
09750 {
09751         struct directory *dp;
09752         Tcl_Interp *interp = (Tcl_Interp *)user_ptr1;
09753 
09754         RT_CK_DBI(dbip);
09755         RT_CK_TREE(comb_leaf);
09756 
09757         if (!comb_leaf->tr_l.tl_mat) {
09758                 comb_leaf->tr_l.tl_mat = (matp_t)bu_malloc(sizeof(mat_t), "tl_mat");
09759         }
09760         MAT_IDN(comb_leaf->tr_l.tl_mat);
09761         if ((dp = db_lookup(dbip, comb_leaf->tr_l.tl_name, LOOKUP_NOISY)) == DIR_NULL)
09762                 return;
09763 
09764         wdb_identitize(dp, dbip, interp);
09765 }
09766 
09767 /*
09768  *                      W D B _ I D E N T I T I Z E ( )
09769  *
09770  *      Traverses an objects paths, setting all member matrices == identity
09771  *
09772  */
09773 void
09774 wdb_identitize(struct directory *dp,
09775                struct db_i      *dbip,
09776                Tcl_Interp       *interp)
09777 {
09778         struct rt_db_internal intern;
09779         struct rt_comb_internal *comb;
09780 
09781         if (dp->d_flags & DIR_SOLID)
09782                 return;
09783         if (rt_db_get_internal(&intern, dp, dbip, (fastf_t *)NULL, &rt_uniresource) < 0) {
09784                 Tcl_AppendResult(interp, "Database read error, aborting\n", (char *)NULL);
09785                 return;
09786         }
09787         comb = (struct rt_comb_internal *)intern.idb_ptr;
09788         if (comb->tree) {
09789                 db_tree_funcleaf(dbip, comb, comb->tree, wdb_do_identitize,
09790                                  (genptr_t)interp, (genptr_t)NULL, (genptr_t)NULL);
09791                 if (rt_db_put_internal(dp, dbip, &intern, &rt_uniresource) < 0) {
09792                         Tcl_AppendResult(interp, "Cannot write modified combination (", dp->d_namep,
09793                                          ") to database\n", (char *)NULL );
09794                         return;
09795                 }
09796         }
09797 }
09798 
09799 /*
09800  *                      W D B _ D I R _ S U M M A R Y
09801  *
09802  * Summarize the contents of the directory by categories
09803  * (solid, comb, region).  If flag is != 0, it is interpreted
09804  * as a request to print all the names in that category (eg, DIR_SOLID).
09805  */
09806 static void
09807 wdb_dir_summary(struct db_i     *dbip,
09808                 Tcl_Interp      *interp,
09809                 int             flag)
09810 {
09811         register struct directory *dp;
09812         register int i;
09813         static int sol, comb, reg;
09814         struct directory **dirp;
09815         struct directory **dirp0 = (struct directory **)NULL;
09816         struct bu_vls vls;
09817 
09818         bu_vls_init(&vls);
09819 
09820         sol = comb = reg = 0;
09821         for (i = 0; i < RT_DBNHASH; i++)  {
09822                 for (dp = dbip->dbi_Head[i]; dp != DIR_NULL; dp = dp->d_forw) {
09823                         if (dp->d_flags & DIR_SOLID)
09824                                 sol++;
09825                         if (dp->d_flags & DIR_COMB) {
09826                                 if (dp->d_flags & DIR_REGION)
09827                                         reg++;
09828                                 else
09829                                         comb++;
09830                         }
09831                 }
09832         }
09833 
09834         bu_vls_printf(&vls, "Summary:\n");
09835         bu_vls_printf(&vls, "  %5d primitives\n", sol);
09836         bu_vls_printf(&vls, "  %5d region; %d non-region combinations\n", reg, comb);
09837         bu_vls_printf(&vls, "  %5d total objects\n\n", sol+reg+comb );
09838 
09839         if (flag == 0) {
09840                 Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
09841                 bu_vls_free(&vls);
09842                 return;
09843         }
09844 
09845         /* Print all names matching the flags parameter */
09846         /* THIS MIGHT WANT TO BE SEPARATED OUT BY CATEGORY */
09847 
09848         dirp = wdb_dir_getspace(dbip, 0);
09849         dirp0 = dirp;
09850         /*
09851          * Walk the directory list adding pointers (to the directory entries
09852          * of interest) to the array
09853          */
09854         for (i = 0; i < RT_DBNHASH; i++)
09855                 for(dp = dbip->dbi_Head[i]; dp != DIR_NULL; dp = dp->d_forw)
09856                         if (dp->d_flags & flag)
09857                                 *dirp++ = dp;
09858 
09859         wdb_vls_col_pr4v(&vls, dirp0, (int)(dirp - dirp0), 0);
09860         Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
09861         bu_vls_free(&vls);
09862         bu_free((genptr_t)dirp0, "dir_getspace");
09863 }
09864 
09865 /*
09866  *                      W D B _ D I R _ G E T S P A C E
09867  *
09868  * This routine walks through the directory entry list and mallocs enough
09869  * space for pointers to hold:
09870  *  a) all of the entries if called with an argument of 0, or
09871  *  b) the number of entries specified by the argument if > 0.
09872  */
09873 static struct directory **
09874 wdb_dir_getspace(struct db_i    *dbip,
09875                  register int   num_entries)
09876 {
09877         register struct directory *dp;
09878         register int i;
09879         register struct directory **dir_basep;
09880 
09881         if (num_entries < 0) {
09882                 bu_log( "dir_getspace: was passed %d, used 0\n",
09883                         num_entries);
09884                 num_entries = 0;
09885         }
09886         if (num_entries == 0) {
09887                 /* Set num_entries to the number of entries */
09888                 for (i = 0; i < RT_DBNHASH; i++)
09889                         for(dp = dbip->dbi_Head[i]; dp != DIR_NULL; dp = dp->d_forw)
09890                                 num_entries++;
09891         }
09892 
09893         /* Allocate and cast num_entries worth of pointers */
09894         dir_basep = (struct directory **) bu_malloc((num_entries+1) * sizeof(struct directory *),
09895                                                     "dir_getspace *dir[]");
09896         return dir_basep;
09897 }
09898 
09899 /*
09900  *                      P A T H L I S T _ L E A F _ F U N C
09901  */
09902 static union tree *
09903 wdb_pathlist_leaf_func(struct db_tree_state     *tsp,
09904                        struct db_full_path      *pathp,
09905                        struct rt_db_internal    *ip,
09906                        genptr_t                 client_data)
09907 {
09908         Tcl_Interp      *interp = (Tcl_Interp *)client_data;
09909         char            *str;
09910 
09911         RT_CK_FULL_PATH(pathp);
09912         RT_CK_DB_INTERNAL(ip);
09913 
09914         if (pathListNoLeaf) {
09915             --pathp->fp_len;
09916             str = db_path_to_string(pathp);
09917             ++pathp->fp_len;
09918         } else
09919             str = db_path_to_string(pathp);
09920 
09921         Tcl_AppendElement(interp, str);
09922 
09923         bu_free((genptr_t)str, "path string");
09924         return TREE_NULL;
09925 }
09926 
09927 /*
09928  *                      W D B _ B O T _ D E C I M A T E _ C M D
09929  */
09930 
09931 int
09932 wdb_bot_decimate_cmd(struct rt_wdb      *wdbp,
09933              Tcl_Interp         *interp,
09934              int                argc,
09935              char               **argv)
09936 {
09937         int c;
09938         struct rt_db_internal intern;
09939         struct rt_bot_internal *bot;
09940         struct directory *dp;
09941         fastf_t max_chord_error=-1.0;
09942         fastf_t max_normal_error=-1.0;
09943         fastf_t min_edge_length=-1.0;
09944 
09945         if( argc < 5 || argc > 9 ) {
09946                 struct bu_vls vls;
09947 
09948                 bu_vls_init(&vls);
09949                 bu_vls_printf(&vls, "helplib_alias wdb_bot_decimate %s", argv[0]);
09950                 Tcl_Eval(interp, bu_vls_addr(&vls));
09951                 bu_vls_free(&vls);
09952                 return TCL_ERROR;
09953         }
09954 
09955         /* process args */
09956         bu_optind = 1;
09957         bu_opterr = 0;
09958         while( (c=bu_getopt(argc,argv,"c:n:e:")) != EOF )  {
09959                 switch(c) {
09960                         case 'c':
09961                                 max_chord_error = atof( bu_optarg );
09962                                 if( max_chord_error < 0.0 ) {
09963                                         Tcl_AppendResult(interp,
09964                                                          "Maximum chord error cannot be less than zero",
09965                                                          (char *)NULL );
09966                                         return TCL_ERROR;
09967                                 }
09968                                 break;
09969                         case 'n':
09970                                 max_normal_error = atof( bu_optarg );
09971                                 if( max_normal_error < 0.0 ) {
09972                                         Tcl_AppendResult(interp,
09973                                                          "Maximum normal error cannot be less than zero",
09974                                                          (char *)NULL );
09975                                         return TCL_ERROR;
09976                                 }
09977                                 break;
09978                         case 'e':
09979                                 min_edge_length = atof( bu_optarg );
09980                                 if( min_edge_length < 0.0 ) {
09981                                         Tcl_AppendResult(interp,
09982                                                          "minumum edge length cannot be less than zero",
09983                                                          (char *)NULL );
09984                                         return TCL_ERROR;
09985                                 }
09986                                 break;
09987                         default:
09988                                 {
09989                                         struct bu_vls vls;
09990 
09991                                         bu_vls_init(&vls);
09992                                         bu_vls_printf(&vls, "helplib_alias wdb_bot_decimate %s",
09993                                                       argv[0]);
09994                                         Tcl_Eval(interp, bu_vls_addr(&vls));
09995                                         bu_vls_free(&vls);
09996                                         return TCL_ERROR;
09997                                 }
09998                 }
09999         }
10000 
10001         argc -= bu_optind;
10002         argv += bu_optind;
10003 
10004         /* make sure new solid does not already exist */
10005         if( (dp=db_lookup( wdbp->dbip, argv[0], LOOKUP_QUIET ) ) != DIR_NULL ) {
10006           Tcl_AppendResult(interp, argv[0], " already exists!!\n", (char *)NULL );
10007           return TCL_ERROR;
10008         }
10009 
10010         /* make sure current solid does exist */
10011         if( (dp=db_lookup( wdbp->dbip, argv[1], LOOKUP_QUIET ) ) == DIR_NULL ) {
10012                 Tcl_AppendResult(interp, argv[1], " Does not exist\n", (char *)NULL );
10013                 return TCL_ERROR;
10014         }
10015 
10016         /* import the current solid */
10017         RT_INIT_DB_INTERNAL( &intern );
10018         if( rt_db_get_internal( &intern, dp, wdbp->dbip, NULL, wdbp->wdb_resp ) < 0 ) {
10019                 Tcl_AppendResult(interp, "Failed to get internal form of ", argv[1],
10020                                  "\n", (char *)NULL );
10021                 return TCL_ERROR;
10022         }
10023 
10024         /* make sure this is a BOT solid */
10025         if( intern.idb_major_type != DB5_MAJORTYPE_BRLCAD ||
10026             intern.idb_minor_type != DB5_MINORTYPE_BRLCAD_BOT ) {
10027                 Tcl_AppendResult(interp, argv[1], " is not a BOT solid\n", (char *)NULL );
10028                 rt_db_free_internal( &intern, wdbp->wdb_resp );
10029                 return TCL_ERROR;
10030         }
10031 
10032         bot = (struct rt_bot_internal *)intern.idb_ptr;
10033 
10034         RT_BOT_CK_MAGIC( bot );
10035 
10036         /* convert maximum error and edge length to mm */
10037         max_chord_error = max_chord_error * wdbp->dbip->dbi_local2base;
10038         min_edge_length = min_edge_length * wdbp->dbip->dbi_local2base;
10039 
10040         /* do the decimation */
10041         if( rt_bot_decimate( bot, max_chord_error, max_normal_error, min_edge_length) < 0 ) {
10042                 Tcl_AppendResult(interp, "Decimation Error\n", (char *)NULL );
10043                 rt_db_free_internal( &intern, wdbp->wdb_resp );
10044                 return TCL_ERROR;
10045         }
10046 
10047         /* save the result to the database */
10048         if( wdb_put_internal( wdbp, argv[0], &intern, 1.0 ) < 0 ) {
10049                 Tcl_AppendResult(interp, "Failed to write decimated BOT back to database\n", (char *)NULL );
10050                 return TCL_ERROR;
10051         }
10052 
10053         return TCL_OK;
10054 }
10055 
10056 /*
10057  * Usage:
10058  *        procname
10059  */
10060 static int
10061 wdb_bot_decimate_tcl(ClientData clientData,
10062          Tcl_Interp     *interp,
10063          int            argc,
10064          char           **argv)
10065 {
10066         struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
10067 
10068         return wdb_bot_decimate_cmd(wdbp, interp, argc-1, argv+1);
10069 }
10070 
10071 
10072 int
10073 wdb_move_arb_edge_cmd(struct rt_wdb     *wdbp,
10074                       Tcl_Interp        *interp,
10075                       int               argc,
10076                       char              **argv)
10077 {
10078     struct rt_db_internal intern;
10079     struct rt_arb_internal *arb;
10080     fastf_t planes[7][4];               /* ARBs defining plane equations */
10081     int arb_type;
10082     int edge;
10083     int bad_edge_id = 0;
10084     point_t pt;
10085 
10086     if (argc != 4) {
10087         struct bu_vls vls;
10088 
10089         bu_vls_init(&vls);
10090         bu_vls_printf(&vls, "helplib_alias wdb_move_arb_edge %s", argv[0]);
10091         Tcl_Eval(interp, bu_vls_addr(&vls));
10092         bu_vls_free(&vls);
10093 
10094         return TCL_ERROR;
10095     }
10096 
10097     if (wdbp->dbip == 0) {
10098         Tcl_AppendResult(interp,
10099                          "db does not support lookup operations",
10100                          (char *)NULL);
10101         return TCL_ERROR;
10102     }
10103 
10104     if (rt_tcl_import_from_path(interp, &intern, argv[1], wdbp) == TCL_ERROR)
10105         return TCL_ERROR;
10106 
10107     if (intern.idb_major_type != DB5_MAJORTYPE_BRLCAD ||
10108         intern.idb_minor_type != DB5_MINORTYPE_BRLCAD_ARB8) {
10109         Tcl_AppendResult(interp, "Object not an ARB", (char *)NULL);
10110         rt_db_free_internal(&intern, &rt_uniresource);
10111 
10112         return TCL_ERROR;
10113     }
10114 
10115     if (sscanf(argv[2], "%d", &edge) != 1) {
10116         struct bu_vls vls;
10117 
10118         bu_vls_init(&vls);
10119         bu_vls_printf(&vls, "bad edge - %s", argv[2]);
10120         Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
10121         bu_vls_free(&vls);
10122         rt_db_free_internal(&intern, &rt_uniresource);
10123 
10124         return TCL_ERROR;
10125     }
10126     edge -= 1;
10127 
10128     if (sscanf(argv[3], "%lf %lf %lf", &pt[X], &pt[Y], &pt[Z]) != 3) {
10129         struct bu_vls vls;
10130 
10131         bu_vls_init(&vls);
10132         bu_vls_printf(&vls, "bad point - %s", argv[3]);
10133         Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
10134         bu_vls_free(&vls);
10135         rt_db_free_internal(&intern, &rt_uniresource);
10136 
10137         return TCL_ERROR;
10138     }
10139 
10140     arb = (struct rt_arb_internal *)intern.idb_ptr;
10141     RT_ARB_CK_MAGIC(arb);
10142 
10143     arb_type = rt_arb_std_type(&intern, &wdbp->wdb_tol);
10144 
10145     /* check the arb type */
10146     switch (arb_type) {
10147     case ARB4:
10148         if (edge < 0 || 4 < edge)
10149             bad_edge_id = 1;
10150         break;
10151     case ARB5:
10152         if (edge < 0 || 8 < edge)
10153             bad_edge_id = 1;
10154         break;
10155     case ARB6:
10156         if (edge < 0 || 9 < edge)
10157             bad_edge_id = 1;
10158         break;
10159     case ARB7:
10160         if (edge < 0 || 11 < edge)
10161             bad_edge_id = 1;
10162         break;
10163     case ARB8:
10164         if (edge < 0 || 11 < edge)
10165             bad_edge_id = 1;
10166         break;
10167     default:
10168         Tcl_AppendResult(interp, "unrecognized arb type", (char *)NULL);
10169         rt_db_free_internal(&intern, &rt_uniresource);
10170 
10171         return TCL_ERROR;
10172     }
10173 
10174     /* check the edge id */
10175     if (bad_edge_id) {
10176         struct bu_vls vls;
10177 
10178         bu_vls_init(&vls);
10179         bu_vls_printf(&vls, "bad edge - %s", argv[2]);
10180         Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
10181         bu_vls_free(&vls);
10182         rt_db_free_internal(&intern, &rt_uniresource);
10183 
10184         return TCL_ERROR;
10185     }
10186 
10187     if (rt_arb_calc_planes(interp, arb, arb_type, planes, &wdbp->wdb_tol)) {
10188         rt_db_free_internal(&intern, &rt_uniresource);
10189 
10190         return TCL_ERROR;
10191     }
10192 
10193     if (rt_arb_edit(interp, arb, arb_type, edge, pt, planes, &wdbp->wdb_tol)) {
10194         rt_db_free_internal(&intern, &rt_uniresource);
10195 
10196         return TCL_ERROR;
10197     }
10198 
10199     {
10200         register int i;
10201         struct bu_vls vls;
10202 
10203         bu_vls_init(&vls);
10204 
10205         for (i = 0; i < 8; ++i) {
10206             bu_vls_printf(&vls, "V%d {%g %g %g} ",
10207                           i + 1,
10208                           arb->pt[i][X],
10209                           arb->pt[i][Y],
10210                           arb->pt[i][Z]);
10211         }
10212 
10213         Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
10214         bu_vls_free(&vls);
10215     }
10216 
10217     rt_db_free_internal(&intern, &rt_uniresource);
10218     return TCL_OK;
10219 }
10220 
10221 /*
10222  * Move an arb's edge so that it intersects the
10223  * given point. The new vertices are returned
10224  * in interp->result.
10225  *
10226  * Usage:
10227  *        procname move_arb_face arb face pt
10228  */
10229 static int
10230 wdb_move_arb_edge_tcl(ClientData        clientData,
10231                       Tcl_Interp        *interp,
10232                       int               argc,
10233                       char              **argv)
10234 {
10235     struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
10236 
10237     return wdb_move_arb_edge_cmd(wdbp, interp, argc-1, argv+1);
10238 }
10239 
10240 int
10241 wdb_move_arb_face_cmd(struct rt_wdb     *wdbp,
10242                       Tcl_Interp        *interp,
10243                       int               argc,
10244                       char              **argv)
10245 {
10246     struct rt_db_internal intern;
10247     struct rt_arb_internal *arb;
10248     fastf_t planes[7][4];               /* ARBs defining plane equations */
10249     int arb_type;
10250     int face;
10251     point_t pt;
10252 
10253     if (argc != 4) {
10254         struct bu_vls vls;
10255 
10256         bu_vls_init(&vls);
10257         bu_vls_printf(&vls, "helplib_alias wdb_move_arb_face %s", argv[0]);
10258         Tcl_Eval(interp, bu_vls_addr(&vls));
10259         bu_vls_free(&vls);
10260 
10261         return TCL_ERROR;
10262     }
10263 
10264     if (wdbp->dbip == 0) {
10265         Tcl_AppendResult(interp,
10266                          "db does not support lookup operations",
10267                          (char *)NULL);
10268         return TCL_ERROR;
10269     }
10270 
10271     if (rt_tcl_import_from_path(interp, &intern, argv[1], wdbp) == TCL_ERROR)
10272         return TCL_ERROR;
10273 
10274     if (intern.idb_major_type != DB5_MAJORTYPE_BRLCAD ||
10275         intern.idb_minor_type != DB5_MINORTYPE_BRLCAD_ARB8) {
10276         Tcl_AppendResult(interp, "Object not an ARB", (char *)NULL);
10277         rt_db_free_internal(&intern, &rt_uniresource);
10278 
10279         return TCL_OK;
10280     }
10281 
10282     if (sscanf(argv[2], "%d", &face) != 1) {
10283         struct bu_vls vls;
10284 
10285         bu_vls_init(&vls);
10286         bu_vls_printf(&vls, "bad face - %s", argv[2]);
10287         Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
10288         bu_vls_free(&vls);
10289         rt_db_free_internal(&intern, &rt_uniresource);
10290 
10291         return TCL_ERROR;
10292     }
10293 
10294     /*XXX need better checking of the face */
10295     face -= 1;
10296     if (face < 0 || 5 < face) {
10297         struct bu_vls vls;
10298 
10299         bu_vls_init(&vls);
10300         bu_vls_printf(&vls, "bad face - %s", argv[2]);
10301         Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
10302         bu_vls_free(&vls);
10303         rt_db_free_internal(&intern, &rt_uniresource);
10304 
10305         return TCL_ERROR;
10306     }
10307 
10308     if (sscanf(argv[3], "%lf %lf %lf", &pt[X], &pt[Y], &pt[Z]) != 3) {
10309         struct bu_vls vls;
10310 
10311         bu_vls_init(&vls);
10312         bu_vls_printf(&vls, "bad point - %s", argv[3]);
10313         Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
10314         bu_vls_free(&vls);
10315         rt_db_free_internal(&intern, &rt_uniresource);
10316 
10317         return TCL_ERROR;
10318     }
10319 
10320     arb = (struct rt_arb_internal *)intern.idb_ptr;
10321     RT_ARB_CK_MAGIC(arb);
10322 
10323     arb_type = rt_arb_std_type(&intern, &wdbp->wdb_tol);
10324 
10325     if (rt_arb_calc_planes(interp, arb, arb_type, planes, &wdbp->wdb_tol)) {
10326         rt_db_free_internal(&intern, &rt_uniresource);
10327 
10328         return TCL_ERROR;
10329     }
10330 
10331     /* change D of planar equation */
10332     planes[face][3] = VDOT(&planes[face][0], pt);
10333 
10334     /* calculate new points for the arb */
10335     (void)rt_arb_calc_points(arb, arb_type, planes, &wdbp->wdb_tol);
10336 
10337     {
10338         register int i;
10339         struct bu_vls vls;
10340 
10341         bu_vls_init(&vls);
10342 
10343         for (i = 0; i < 8; ++i) {
10344             bu_vls_printf(&vls, "V%d {%g %g %g} ",
10345                           i + 1,
10346                           arb->pt[i][X],
10347                           arb->pt[i][Y],
10348                           arb->pt[i][Z]);
10349         }
10350 
10351         Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
10352         bu_vls_free(&vls);
10353     }
10354 
10355     rt_db_free_internal(&intern, &rt_uniresource);
10356     return TCL_OK;
10357 }
10358 
10359 /*
10360  * Move an arb's face so that its plane intersects
10361  * the given point. The new vertices are returned
10362  * in interp->result.
10363  *
10364  * Usage:
10365  *        procname move_arb_face arb face pt
10366  */
10367 static int
10368 wdb_move_arb_face_tcl(ClientData        clientData,
10369                       Tcl_Interp        *interp,
10370                       int               argc,
10371                       char              **argv)
10372 {
10373     struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
10374 
10375     return wdb_move_arb_face_cmd(wdbp, interp, argc-1, argv+1);
10376 }
10377 
10378 
10379 static short int rt_arb_vertices[5][24] = {
10380         { 1,2,3,0, 1,2,4,0, 2,3,4,0, 1,3,4,0, 0,0,0,0, 0,0,0,0 },       /* arb4 */
10381         { 1,2,3,4, 1,2,5,0, 2,3,5,0, 3,4,5,0, 1,4,5,0, 0,0,0,0 },       /* arb5 */
10382         { 1,2,3,4, 2,3,6,5, 1,5,6,4, 1,2,5,0, 3,4,6,0, 0,0,0,0 },       /* arb6 */
10383         { 1,2,3,4, 5,6,7,0, 1,4,5,0, 2,3,7,6, 1,2,6,5, 4,3,7,5 },       /* arb7 */
10384         { 1,2,3,4, 5,6,7,8, 1,5,8,4, 2,3,7,6, 1,2,6,5, 4,3,7,8 }        /* arb8 */
10385 };
10386 
10387 int
10388 wdb_rotate_arb_face_cmd(struct rt_wdb   *wdbp,
10389                         Tcl_Interp      *interp,
10390                         int             argc,
10391                         char            **argv)
10392 {
10393     struct rt_db_internal intern;
10394     struct rt_arb_internal *arb;
10395     fastf_t planes[7][4];               /* ARBs defining plane equations */
10396     int arb_type;
10397     int face;
10398     int vi;
10399     point_t pt;
10400     register int i;
10401     int pnt5;           /* special arb7 case */
10402 
10403     if (argc != 5) {
10404         struct bu_vls vls;
10405 
10406         bu_vls_init(&vls);
10407         bu_vls_printf(&vls, "helplib_alias wdb_move_arb_face %s", argv[0]);
10408         Tcl_Eval(interp, bu_vls_addr(&vls));
10409         bu_vls_free(&vls);
10410 
10411         return TCL_ERROR;
10412     }
10413 
10414     if (wdbp->dbip == 0) {
10415         Tcl_AppendResult(interp,
10416                          "db does not support lookup operations",
10417                          (char *)NULL);
10418         return TCL_ERROR;
10419     }
10420 
10421     if (rt_tcl_import_from_path(interp, &intern, argv[1], wdbp) == TCL_ERROR)
10422         return TCL_ERROR;
10423 
10424     if (intern.idb_major_type != DB5_MAJORTYPE_BRLCAD ||
10425         intern.idb_minor_type != DB5_MINORTYPE_BRLCAD_ARB8) {
10426         Tcl_AppendResult(interp, "Object not an ARB", (char *)NULL);
10427         rt_db_free_internal(&intern, &rt_uniresource);
10428 
10429         return TCL_OK;
10430     }
10431 
10432     if (sscanf(argv[2], "%d", &face) != 1) {
10433         struct bu_vls vls;
10434 
10435         bu_vls_init(&vls);
10436         bu_vls_printf(&vls, "bad face - %s", argv[2]);
10437         Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
10438         bu_vls_free(&vls);
10439         rt_db_free_internal(&intern, &rt_uniresource);
10440 
10441         return TCL_ERROR;
10442     }
10443 
10444     /*XXX need better checking of the face */
10445     face -= 1;
10446     if (face < 0 || 5 < face) {
10447         struct bu_vls vls;
10448 
10449         bu_vls_init(&vls);
10450         bu_vls_printf(&vls, "bad face - %s", argv[2]);
10451         Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
10452         bu_vls_free(&vls);
10453         rt_db_free_internal(&intern, &rt_uniresource);
10454 
10455         return TCL_ERROR;
10456     }
10457 
10458     if (sscanf(argv[3], "%d", &vi) != 1) {
10459         struct bu_vls vls;
10460 
10461         bu_vls_init(&vls);
10462         bu_vls_printf(&vls, "bad vertex index - %s", argv[2]);
10463         Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
10464         bu_vls_free(&vls);
10465         rt_db_free_internal(&intern, &rt_uniresource);
10466 
10467         return TCL_ERROR;
10468     }
10469 
10470 
10471     /*XXX need better checking of the vertex index */
10472     vi -= 1;
10473     if (vi < 0 || 7 < vi) {
10474         struct bu_vls vls;
10475 
10476         bu_vls_init(&vls);
10477         bu_vls_printf(&vls, "bad vertex - %s", argv[2]);
10478         Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
10479         bu_vls_free(&vls);
10480         rt_db_free_internal(&intern, &rt_uniresource);
10481 
10482         return TCL_ERROR;
10483     }
10484 
10485     if (sscanf(argv[4], "%lf %lf %lf", &pt[X], &pt[Y], &pt[Z]) != 3) {
10486         struct bu_vls vls;
10487 
10488         bu_vls_init(&vls);
10489         bu_vls_printf(&vls, "bad point - %s", argv[3]);
10490         Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
10491         bu_vls_free(&vls);
10492         rt_db_free_internal(&intern, &rt_uniresource);
10493 
10494         return TCL_ERROR;
10495     }
10496 
10497     arb = (struct rt_arb_internal *)intern.idb_ptr;
10498     RT_ARB_CK_MAGIC(arb);
10499 
10500     arb_type = rt_arb_std_type(&intern, &wdbp->wdb_tol);
10501 
10502     if (rt_arb_calc_planes(interp, arb, arb_type, planes, &wdbp->wdb_tol)) {
10503         rt_db_free_internal(&intern, &rt_uniresource);
10504 
10505         return TCL_ERROR;
10506     }
10507 
10508     /* check if point 5 is in the face */
10509     pnt5 = 0;
10510     for(i=0; i<4; i++)  {
10511         if (rt_arb_vertices[arb_type-4][face*4+i]==5)
10512             pnt5=1;
10513     }
10514 
10515     /* special case for arb7 */
10516     if (arb_type == ARB7  && pnt5)
10517         vi = 4;
10518 
10519     {
10520         /* Apply incremental changes */
10521         vect_t tempvec;
10522         vect_t work;
10523         fastf_t *plane;
10524         mat_t rmat;
10525 
10526         bn_mat_angles(rmat, pt[X], pt[Y], pt[Z]);
10527 
10528         plane = &planes[face][0];
10529         VMOVE(work, plane);
10530         MAT4X3VEC(plane, rmat, work);
10531 
10532         /* point notation of fixed vertex */
10533         VMOVE(tempvec, arb->pt[vi]);
10534 
10535         /* set D of planar equation to anchor at fixed vertex */
10536         planes[face][3]=VDOT(plane, tempvec);
10537     }
10538 
10539     /* calculate new points for the arb */
10540     (void)rt_arb_calc_points(arb, arb_type, planes, &wdbp->wdb_tol);
10541 
10542     {
10543         register int i;
10544         struct bu_vls vls;
10545 
10546         bu_vls_init(&vls);
10547 
10548         for (i = 0; i < 8; ++i) {
10549             bu_vls_printf(&vls, "V%d {%g %g %g} ",
10550                           i + 1,
10551                           arb->pt[i][X],
10552                           arb->pt[i][Y],
10553                           arb->pt[i][Z]);
10554         }
10555 
10556         Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)NULL);
10557         bu_vls_free(&vls);
10558     }
10559 
10560     rt_db_free_internal(&intern, &rt_uniresource);
10561     return TCL_OK;
10562 }
10563 
10564 /*
10565  * Rotate an arb's face to the given point. The new
10566  * vertices are returned in interp->result.
10567  *
10568  * Usage:
10569  *        procname rotate_arb_face arb face pt
10570  */
10571 static int
10572 wdb_rotate_arb_face_tcl(ClientData      clientData,
10573                         Tcl_Interp      *interp,
10574                         int             argc,
10575                         char            **argv)
10576 {
10577     struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
10578 
10579     return wdb_rotate_arb_face_cmd(wdbp, interp, argc-1, argv+1);
10580 }
10581 
10582 int
10583 wdb_orotate_cmd(struct rt_wdb   *wdbp,
10584                 Tcl_Interp      *interp,
10585                 int             argc,
10586                 char            **argv)
10587 {
10588     register struct directory *dp;
10589     struct wdb_trace_data wtd;
10590     struct rt_db_internal intern;
10591     fastf_t xrot, yrot, zrot;
10592     mat_t rmat;
10593     mat_t pmat;
10594     mat_t emat;
10595     mat_t tmpMat;
10596     mat_t invXform;
10597     point_t rpp_min;
10598     point_t rpp_max;
10599     point_t keypoint;
10600     Tcl_DString ds;
10601 
10602     WDB_TCL_CHECK_READ_ONLY;
10603 
10604     if (argc != 5 && argc != 8) {
10605         struct bu_vls vls;
10606 
10607         bu_vls_init(&vls);
10608         bu_vls_printf(&vls, "helplib wdb_orotate");
10609         Tcl_Eval(interp, bu_vls_addr(&vls));
10610         bu_vls_free(&vls);
10611 
10612         return TCL_ERROR;
10613     }
10614 
10615     if (sscanf(argv[2], "%lf", &xrot) != 1) {
10616         Tcl_DStringInit(&ds);
10617         Tcl_DStringAppend(&ds, argv[0], -1);
10618         Tcl_DStringAppend(&ds, ": bad xrot value - ", -1);
10619         Tcl_DStringAppend(&ds, argv[2], -1);
10620         Tcl_DStringResult(interp, &ds);
10621 
10622         return TCL_ERROR;
10623     }
10624 
10625     if (sscanf(argv[3], "%lf", &yrot) != 1) {
10626         Tcl_DStringInit(&ds);
10627         Tcl_DStringAppend(&ds, argv[0], -1);
10628         Tcl_DStringAppend(&ds, ": bad yrot value - ", -1);
10629         Tcl_DStringAppend(&ds, argv[3], -1);
10630         Tcl_DStringResult(interp, &ds);
10631 
10632         return TCL_ERROR;
10633     }
10634 
10635     if (sscanf(argv[4], "%lf", &zrot) != 1) {
10636         Tcl_DStringInit(&ds);
10637         Tcl_DStringAppend(&ds, argv[0], -1);
10638         Tcl_DStringAppend(&ds, ": bad zrot value - ", -1);
10639         Tcl_DStringAppend(&ds, argv[4], -1);
10640         Tcl_DStringResult(interp, &ds);
10641 
10642         return TCL_ERROR;
10643     }
10644 
10645     if (argc == 5) {
10646         /* Use the object's center as the keypoint. */
10647 
10648         if (wdb_get_obj_bounds2(wdbp, interp, 1, argv+1, &wtd, rpp_min, rpp_max) == TCL_ERROR)
10649             return TCL_ERROR;
10650 
10651         dp = wtd.wtd_obj[wtd.wtd_objpos-1];
10652         if (!(dp->d_flags & DIR_SOLID)) {
10653             if (wdb_get_obj_bounds(wdbp, interp, 1, argv+1, 1, rpp_min, rpp_max) == TCL_ERROR)
10654                 return TCL_ERROR;
10655         }
10656 
10657         VADD2(keypoint, rpp_min, rpp_max);
10658         VSCALE(keypoint, keypoint, 0.5);
10659     } else {
10660         /* The user has provided the keypoint. */
10661         MAT_IDN(wtd.wtd_xform);
10662 
10663         if (sscanf(argv[5], "%lf", &keypoint[X]) != 1) {
10664             Tcl_DStringInit(&ds);
10665             Tcl_DStringAppend(&ds, argv[0], -1);
10666             Tcl_DStringAppend(&ds, ": bad kx value - ", -1);
10667             Tcl_DStringAppend(&ds, argv[5], -1);
10668             Tcl_DStringResult(interp, &ds);
10669 
10670             return TCL_ERROR;
10671         }
10672 
10673         if (sscanf(argv[6], "%lf", &keypoint[Y]) != 1) {
10674             Tcl_DStringInit(&ds);
10675             Tcl_DStringAppend(&ds, argv[0], -1);
10676             Tcl_DStringAppend(&ds, ": bad ky value - ", -1);
10677             Tcl_DStringAppend(&ds, argv[6], -1);
10678             Tcl_DStringResult(interp, &ds);
10679 
10680             return TCL_ERROR;
10681         }
10682 
10683         if (sscanf(argv[7], "%lf", &keypoint[Z]) != 1) {
10684             Tcl_DStringInit(&ds);
10685             Tcl_DStringAppend(&ds, argv[0], -1);
10686             Tcl_DStringAppend(&ds, ": bad kz value - ", -1);
10687             Tcl_DStringAppend(&ds, argv[7], -1);
10688             Tcl_DStringResult(interp, &ds);
10689 
10690             return TCL_ERROR;
10691         }
10692 
10693         VSCALE(keypoint, keypoint, wdbp->dbip->dbi_local2base);
10694 
10695         if ((dp = db_lookup(wdbp->dbip,  argv[1],  LOOKUP_QUIET)) == DIR_NULL) {
10696             Tcl_DStringInit(&ds);
10697             Tcl_DStringAppend(&ds, argv[0], -1);
10698             Tcl_DStringAppend(&ds, ": ", -1);
10699             Tcl_DStringAppend(&ds, argv[1], -1);
10700             Tcl_DStringAppend(&ds, " not found", -1);
10701             Tcl_DStringResult(interp, &ds);
10702 
10703             return TCL_ERROR;
10704         }
10705     }
10706 
10707 #if 0
10708     MAT_IDN(rmat);
10709     MAT_IDN(mat);
10710 #endif
10711     bn_mat_angles(rmat, xrot, yrot, zrot);
10712     bn_mat_xform_about_pt(pmat, rmat, keypoint);
10713 
10714     bn_mat_inv(invXform, wtd.wtd_xform);
10715     bn_mat_mul(tmpMat, invXform, pmat);
10716     bn_mat_mul(emat, tmpMat, wtd.wtd_xform);
10717 
10718     if (rt_db_get_internal(&intern, dp, wdbp->dbip, emat, &rt_uniresource) < 0) {
10719         Tcl_DStringInit(&ds);
10720         Tcl_DStringAppend(&ds, argv[0], -1);
10721         Tcl_DStringAppend(&ds, ": ", -1);
10722         Tcl_DStringAppend(&ds, " rt_db_get_internal(", -1);
10723         Tcl_DStringAppend(&ds, argv[1], -1);
10724         Tcl_DStringAppend(&ds, ") failure", -1);
10725         Tcl_DStringResult(interp, &ds);
10726 
10727         return TCL_ERROR;
10728     }
10729     RT_CK_DB_INTERNAL(&intern);
10730 
10731     if (rt_db_put_internal(dp,
10732                            wdbp->dbip,
10733                            &intern,
10734                            &rt_uniresource) < 0) {
10735         rt_db_free_internal(&intern, &rt_uniresource);
10736 
10737         Tcl_DStringInit(&ds);
10738         Tcl_DStringAppend(&ds, argv[0], -1);
10739         Tcl_DStringAppend(&ds, ": Database write error, aborting", -1);
10740         Tcl_DStringResult(interp, &ds);
10741 
10742         return TCL_ERROR;
10743     }
10744     rt_db_free_internal(&intern, &rt_uniresource);
10745 
10746     /* notify observers */
10747     bu_observer_notify(interp, &wdbp->wdb_observers, bu_vls_addr(&wdbp->wdb_name));
10748 
10749     return TCL_OK;
10750 }
10751 
10752 /*
10753  * Rotate obj about the keypoint by xrot yrot zrot.
10754  *
10755  * Usage:
10756  *        procname orotate obj xrot yrot zrot [kx ky kz]
10757  */
10758 static int
10759 wdb_orotate_tcl(ClientData      clientData,
10760                 Tcl_Interp      *interp,
10761                 int             argc,
10762                 char            **argv)
10763 {
10764     struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
10765 
10766     return wdb_orotate_cmd(wdbp, interp, argc-1, argv+1);
10767 }
10768 
10769 int
10770 wdb_oscale_cmd(struct rt_wdb    *wdbp,
10771                Tcl_Interp       *interp,
10772                int              argc,
10773                char             **argv)
10774 {
10775     register struct directory *dp;
10776     struct wdb_trace_data wtd;
10777     struct rt_db_internal intern;
10778     mat_t smat;
10779     mat_t emat;
10780     mat_t tmpMat;
10781     mat_t invXform;
10782     point_t rpp_min;
10783     point_t rpp_max;
10784     fastf_t sf;
10785     point_t keypoint;
10786     Tcl_DString ds;
10787 
10788     WDB_TCL_CHECK_READ_ONLY;
10789 
10790     if (argc != 3 && argc != 6) {
10791         struct bu_vls vls;
10792 
10793         bu_vls_init(&vls);
10794         bu_vls_printf(&vls, "helplib wdb_oscale");
10795         Tcl_Eval(interp, bu_vls_addr(&vls));
10796         bu_vls_free(&vls);
10797 
10798         return TCL_ERROR;
10799     }
10800 
10801     if (sscanf(argv[2], "%lf", &sf) != 1) {
10802         Tcl_DStringInit(&ds);
10803         Tcl_DStringAppend(&ds, argv[0], -1);
10804         Tcl_DStringAppend(&ds, ": bad sf value - ", -1);
10805         Tcl_DStringAppend(&ds, argv[2], -1);
10806         Tcl_DStringResult(interp, &ds);
10807 
10808         return TCL_ERROR;
10809     }
10810 
10811     if (argc == 3) {
10812         if (wdb_get_obj_bounds2(wdbp, interp, 1, argv+1, &wtd, rpp_min, rpp_max) == TCL_ERROR)
10813             return TCL_ERROR;
10814 
10815         dp = wtd.wtd_obj[wtd.wtd_objpos-1];
10816         if (!(dp->d_flags & DIR_SOLID)) {
10817             if (wdb_get_obj_bounds(wdbp, interp, 1, argv+1, 1, rpp_min, rpp_max) == TCL_ERROR)
10818                 return TCL_ERROR;
10819         }
10820 
10821         VADD2(keypoint, rpp_min, rpp_max);
10822         VSCALE(keypoint, keypoint, 0.5);
10823     } else {
10824         /* The user has provided the keypoint. */
10825         MAT_IDN(wtd.wtd_xform);
10826 
10827         if (sscanf(argv[3], "%lf", &keypoint[X]) != 1) {
10828             Tcl_DStringInit(&ds);
10829             Tcl_DStringAppend(&ds, argv[0], -1);
10830             Tcl_DStringAppend(&ds, ": bad kx value - ", -1);
10831             Tcl_DStringAppend(&ds, argv[3], -1);
10832             Tcl_DStringResult(interp, &ds);
10833 
10834             return TCL_ERROR;
10835         }
10836 
10837         if (sscanf(argv[4], "%lf", &keypoint[Y]) != 1) {
10838             Tcl_DStringInit(&ds);
10839             Tcl_DStringAppend(&ds, argv[0], -1);
10840             Tcl_DStringAppend(&ds, ": bad ky value - ", -1);
10841             Tcl_DStringAppend(&ds, argv[4], -1);
10842             Tcl_DStringResult(interp, &ds);
10843 
10844             return TCL_ERROR;
10845         }
10846 
10847         if (sscanf(argv[5], "%lf", &keypoint[Z]) != 1) {
10848             Tcl_DStringInit(&ds);
10849             Tcl_DStringAppend(&ds, argv[0], -1);
10850             Tcl_DStringAppend(&ds, ": bad kz value - ", -1);
10851             Tcl_DStringAppend(&ds, argv[5], -1);
10852             Tcl_DStringResult(interp, &ds);
10853 
10854             return TCL_ERROR;
10855         }
10856 
10857         VSCALE(keypoint, keypoint, wdbp->dbip->dbi_local2base);
10858 
10859         if ((dp = db_lookup(wdbp->dbip,  argv[1],  LOOKUP_QUIET)) == DIR_NULL) {
10860             Tcl_DStringInit(&ds);
10861             Tcl_DStringAppend(&ds, argv[0], -1);
10862             Tcl_DStringAppend(&ds, ": ", -1);
10863             Tcl_DStringAppend(&ds, argv[1], -1);
10864             Tcl_DStringAppend(&ds, " not found", -1);
10865             Tcl_DStringResult(interp, &ds);
10866 
10867             return TCL_ERROR;
10868         }
10869     }
10870 
10871     MAT_IDN(smat);
10872     bn_mat_scale_about_pt(smat, keypoint, sf);
10873 
10874     bn_mat_inv(invXform, wtd.wtd_xform);
10875     bn_mat_mul(tmpMat, invXform, smat);
10876     bn_mat_mul(emat, tmpMat, wtd.wtd_xform);
10877 
10878     if (rt_db_get_internal(&intern, dp, wdbp->dbip, emat, &rt_uniresource) < 0) {
10879         Tcl_DStringInit(&ds);
10880         Tcl_DStringAppend(&ds, argv[0], -1);
10881         Tcl_DStringAppend(&ds, ": ", -1);
10882         Tcl_DStringAppend(&ds, " rt_db_get_internal(", -1);
10883         Tcl_DStringAppend(&ds, argv[1], -1);
10884         Tcl_DStringAppend(&ds, ") failure", -1);
10885         Tcl_DStringResult(interp, &ds);
10886 
10887         return TCL_ERROR;
10888     }
10889     RT_CK_DB_INTERNAL(&intern);
10890 
10891     if (rt_db_put_internal(dp,
10892                            wdbp->dbip,
10893                            &intern,
10894                            &rt_uniresource) < 0) {
10895         rt_db_free_internal(&intern, &rt_uniresource);
10896 
10897         Tcl_DStringInit(&ds);
10898         Tcl_DStringAppend(&ds, argv[0], -1);
10899         Tcl_DStringAppend(&ds, ": Database write error, aborting", -1);
10900         Tcl_DStringResult(interp, &ds);
10901 
10902         return TCL_ERROR;
10903     }
10904     rt_db_free_internal(&intern, &rt_uniresource);
10905 
10906     /* notify observers */
10907     bu_observer_notify(interp, &wdbp->wdb_observers, bu_vls_addr(&wdbp->wdb_name));
10908 
10909     return TCL_OK;
10910 }
10911 
10912 /*
10913  * Scale obj about the keypoint by sf.
10914  *
10915  * Usage:
10916  *        procname oscale obj sf [kx ky kz]
10917  */
10918 static int
10919 wdb_oscale_tcl(ClientData       clientData,
10920                Tcl_Interp       *interp,
10921                int              argc,
10922                char             **argv)
10923 {
10924     struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
10925 
10926     return wdb_oscale_cmd(wdbp, interp, argc-1, argv+1);
10927 }
10928 
10929 int
10930 wdb_otranslate_cmd(struct rt_wdb        *wdbp,
10931                    Tcl_Interp           *interp,
10932                    int                  argc,
10933                    char                 **argv)
10934 {
10935     register struct directory *dp;
10936     struct wdb_trace_data wtd;
10937     struct rt_db_internal intern;
10938     vect_t delta;
10939     mat_t dmat;
10940     mat_t emat;
10941     mat_t tmpMat;
10942     mat_t invXform;
10943     point_t rpp_min;
10944     point_t rpp_max;
10945     Tcl_DString ds;
10946 
10947     WDB_TCL_CHECK_READ_ONLY;
10948 
10949     if (argc != 5) {
10950         struct bu_vls vls;
10951 
10952         bu_vls_init(&vls);
10953         bu_vls_printf(&vls, "helplib wdb_otranslate");
10954         Tcl_Eval(interp, bu_vls_addr(&vls));
10955         bu_vls_free(&vls);
10956 
10957         return TCL_ERROR;
10958     }
10959 
10960     if (wdb_get_obj_bounds2(wdbp, interp, 1, argv+1, &wtd, rpp_min, rpp_max) == TCL_ERROR)
10961         return TCL_ERROR;
10962 
10963     dp = wtd.wtd_obj[wtd.wtd_objpos-1];
10964     if (!(dp->d_flags & DIR_SOLID)) {
10965         if (wdb_get_obj_bounds(wdbp, interp, 1, argv+1, 1, rpp_min, rpp_max) == TCL_ERROR)
10966             return TCL_ERROR;
10967     }
10968 
10969     if (sscanf(argv[2], "%lf", &delta[X]) != 1) {
10970         Tcl_DStringInit(&ds);
10971         Tcl_DStringAppend(&ds, argv[0], -1);
10972         Tcl_DStringAppend(&ds, ": bad x value - ", -1);
10973         Tcl_DStringAppend(&ds, argv[2], -1);
10974         Tcl_DStringResult(interp, &ds);
10975 
10976         return TCL_ERROR;
10977     }
10978 
10979     if (sscanf(argv[3], "%lf", &delta[Y]) != 1) {
10980         Tcl_DStringInit(&ds);
10981         Tcl_DStringAppend(&ds, argv[0], -1);
10982         Tcl_DStringAppend(&ds, ": bad y value - ", -1);
10983         Tcl_DStringAppend(&ds, argv[3], -1);
10984         Tcl_DStringResult(interp, &ds);
10985 
10986         return TCL_ERROR;
10987     }
10988 
10989     if (sscanf(argv[4], "%lf", &delta[Z]) != 1) {
10990         Tcl_DStringInit(&ds);
10991         Tcl_DStringAppend(&ds, argv[0], -1);
10992         Tcl_DStringAppend(&ds, ": bad z value - ", -1);
10993         Tcl_DStringAppend(&ds, argv[4], -1);
10994         Tcl_DStringResult(interp, &ds);
10995 
10996         return TCL_ERROR;
10997     }
10998 
10999     MAT_IDN(dmat);
11000     VSCALE(delta, delta, wdbp->dbip->dbi_local2base);
11001     MAT_DELTAS_VEC(dmat, delta);
11002 
11003     bn_mat_inv(invXform, wtd.wtd_xform);
11004     bn_mat_mul(tmpMat, invXform, dmat);
11005     bn_mat_mul(emat, tmpMat, wtd.wtd_xform);
11006 
11007     if (rt_db_get_internal(&intern, dp, wdbp->dbip, emat, &rt_uniresource) < 0) {
11008         Tcl_DStringInit(&ds);
11009         Tcl_DStringAppend(&ds, argv[0], -1);
11010         Tcl_DStringAppend(&ds, ": ", -1);
11011         Tcl_DStringAppend(&ds, " rt_db_get_internal(", -1);
11012         Tcl_DStringAppend(&ds, argv[1], -1);
11013         Tcl_DStringAppend(&ds, ") failure", -1);
11014         Tcl_DStringResult(interp, &ds);
11015 
11016         return TCL_ERROR;
11017     }
11018     RT_CK_DB_INTERNAL(&intern);
11019 
11020     if (rt_db_put_internal(dp,
11021                            wdbp->dbip,
11022                            &intern,
11023                            &rt_uniresource) < 0) {
11024         rt_db_free_internal(&intern, &rt_uniresource);
11025 
11026         Tcl_DStringInit(&ds);
11027         Tcl_DStringAppend(&ds, argv[0], -1);
11028         Tcl_DStringAppend(&ds, ": Database write error, aborting", -1);
11029         Tcl_DStringResult(interp, &ds);
11030 
11031         return TCL_ERROR;
11032     }
11033     rt_db_free_internal(&intern, &rt_uniresource);
11034 
11035     /* notify observers */
11036     bu_observer_notify(interp, &wdbp->wdb_observers, bu_vls_addr(&wdbp->wdb_name));
11037 
11038     return TCL_OK;
11039 }
11040 
11041 /*
11042  * Translate obj by dx dy dz.
11043  *
11044  * Usage:
11045  *        procname otranslate obj dx dy dz
11046  */
11047 static int
11048 wdb_otranslate_tcl(ClientData   clientData,
11049                 Tcl_Interp      *interp,
11050                 int             argc,
11051                 char            **argv)
11052 {
11053     struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
11054 
11055     return wdb_otranslate_cmd(wdbp, interp, argc-1, argv+1);
11056 }
11057 
11058 int
11059 wdb_ocenter_cmd(struct rt_wdb   *wdbp,
11060                 Tcl_Interp      *interp,
11061                 int             argc,
11062                 char            **argv)
11063 {
11064     register struct directory *dp;
11065     struct wdb_trace_data wtd;
11066     struct rt_db_internal intern;
11067     mat_t dmat;
11068     mat_t emat;
11069     mat_t tmpMat;
11070     mat_t invXform;
11071     point_t rpp_min;
11072     point_t rpp_max;
11073     point_t oldCenter;
11074     point_t center;
11075     point_t delta;
11076     struct bu_vls vls;
11077     Tcl_DString ds;
11078 
11079     if (argc != 2 && argc !=5) {
11080         bu_vls_init(&vls);
11081         bu_vls_printf(&vls, "helplib wdb_ocenter");
11082         Tcl_Eval(interp, bu_vls_addr(&vls));
11083         bu_vls_free(&vls);
11084 
11085         return TCL_ERROR;
11086     }
11087 
11088     /*
11089      * One of the get bounds routines needs to be fixed to
11090      * work with all cases. In the meantime...
11091      */
11092     if (wdb_get_obj_bounds2(wdbp, interp, 1, argv+1, &wtd, rpp_min, rpp_max) == TCL_ERROR)
11093         return TCL_ERROR;
11094 
11095     dp = wtd.wtd_obj[wtd.wtd_objpos-1];
11096     if (!(dp->d_flags & DIR_SOLID)) {
11097         if (wdb_get_obj_bounds(wdbp, interp, 1, argv+1, 1, rpp_min, rpp_max) == TCL_ERROR)
11098             return TCL_ERROR;
11099     }
11100 
11101     VADD2(oldCenter, rpp_min, rpp_max);
11102     VSCALE(oldCenter, oldCenter, 0.5);
11103 
11104     if (argc == 2) {
11105         VSCALE(center, oldCenter, wdbp->dbip->dbi_base2local);
11106 
11107         bu_vls_init(&vls);
11108         bn_encode_vect(&vls, center);
11109         Tcl_AppendResult(interp, bu_vls_addr(&vls), (char *)0);
11110         bu_vls_free(&vls);
11111 
11112         return TCL_OK;
11113     }
11114 
11115     WDB_TCL_CHECK_READ_ONLY;
11116 
11117     /* Read in the new center */
11118     if (sscanf(argv[2], "%lf", &center[X]) != 1) {
11119         Tcl_DStringInit(&ds);
11120         Tcl_DStringAppend(&ds, argv[0], -1);
11121         Tcl_DStringAppend(&ds, ": bad x value - ", -1);
11122         Tcl_DStringAppend(&ds, argv[2], -1);
11123         Tcl_DStringResult(interp, &ds);
11124 
11125         return TCL_ERROR;
11126     }
11127 
11128     if (sscanf(argv[3], "%lf", &center[Y]) != 1) {
11129         Tcl_DStringInit(&ds);
11130         Tcl_DStringAppend(&ds, argv[0], -1);
11131         Tcl_DStringAppend(&ds, ": bad y value - ", -1);
11132         Tcl_DStringAppend(&ds, argv[3], -1);
11133         Tcl_DStringResult(interp, &ds);
11134 
11135         return TCL_ERROR;
11136     }
11137 
11138     if (sscanf(argv[4], "%lf", &center[Z]) != 1) {
11139         Tcl_DStringInit(&ds);
11140         Tcl_DStringAppend(&ds, argv[0], -1);
11141         Tcl_DStringAppend(&ds, ": bad x value - ", -1);
11142         Tcl_DStringAppend(&ds, argv[4], -1);
11143         Tcl_DStringResult(interp, &ds);
11144 
11145         return TCL_ERROR;
11146     }
11147 
11148     VSCALE(center, center, wdbp->dbip->dbi_local2base);
11149     VSUB2(delta, center, oldCenter);
11150     MAT_IDN(dmat);
11151     MAT_DELTAS_VEC(dmat, delta);
11152 
11153     bn_mat_inv(invXform, wtd.wtd_xform);
11154     bn_mat_mul(tmpMat, invXform, dmat);
11155     bn_mat_mul(emat, tmpMat, wtd.wtd_xform);
11156 
11157     if (rt_db_get_internal(&intern, dp, wdbp->dbip, emat, &rt_uniresource) < 0) {
11158         Tcl_DStringInit(&ds);
11159         Tcl_DStringAppend(&ds, argv[0], -1);
11160         Tcl_DStringAppend(&ds, ": ", -1);
11161         Tcl_DStringAppend(&ds, " rt_db_get_internal(", -1);
11162         Tcl_DStringAppend(&ds, argv[1], -1);
11163         Tcl_DStringAppend(&ds, ") failure", -1);
11164         Tcl_DStringResult(interp, &ds);
11165 
11166         return TCL_ERROR;
11167     }
11168     RT_CK_DB_INTERNAL(&intern);
11169 
11170     if (rt_db_put_internal(dp,
11171                            wdbp->dbip,
11172                            &intern,
11173                            &rt_uniresource) < 0) {
11174         rt_db_free_internal(&intern, &rt_uniresource);
11175 
11176         Tcl_DStringInit(&ds);
11177         Tcl_DStringAppend(&ds, argv[0], -1);
11178         Tcl_DStringAppend(&ds, ": Database write error, aborting", -1);
11179         Tcl_DStringResult(interp, &ds);
11180 
11181         return TCL_ERROR;
11182     }
11183     rt_db_free_internal(&intern, &rt_uniresource);
11184 
11185     /* notify observers */
11186     bu_observer_notify(interp, &wdbp->wdb_observers, bu_vls_addr(&wdbp->wdb_name));
11187 
11188     return TCL_OK;
11189 }
11190 
11191 /*
11192  * Set/get the center of the specified object.
11193  *
11194  * Usage:
11195  *        procname ocenter object [x y z]
11196  */
11197 static int
11198 wdb_ocenter_tcl(ClientData      clientData,
11199                 Tcl_Interp      *interp,
11200                 int             argc,
11201                 char            **argv)
11202 {
11203     struct rt_wdb *wdbp = (struct rt_wdb *)clientData;
11204 
11205     return wdb_ocenter_cmd(wdbp, interp, argc-1, argv+1);
11206 }
11207 
11208 /*
11209  * Local Variables:
11210  * mode: C
11211  * tab-width: 8
11212  * c-basic-offset: 4
11213  * indent-tabs-mode: t
11214  * End:
11215  * ex: shiftwidth=4 tabstop=8
11216  */

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