Revision: 76396
          http://sourceforge.net/p/brlcad/code/76396
Author:   starseeker
Date:     2020-07-21 19:08:41 +0000 (Tue, 21 Jul 2020)
Log Message:
-----------
Consolidate bot related commands into one plugin

Modified Paths:
--------------
    brlcad/branches/gedplugins/src/libged/CMakeLists.txt
    brlcad/branches/gedplugins/src/libged/bot/bot.cpp

Added Paths:
-----------
    brlcad/branches/gedplugins/src/libged/bot/bot_condense.c
    brlcad/branches/gedplugins/src/libged/bot/bot_decimate.c
    brlcad/branches/gedplugins/src/libged/bot/bot_dump.c
    brlcad/branches/gedplugins/src/libged/bot/bot_face_fuse.c
    brlcad/branches/gedplugins/src/libged/bot/bot_face_sort.c
    brlcad/branches/gedplugins/src/libged/bot/bot_flip.c
    brlcad/branches/gedplugins/src/libged/bot/bot_fuse.c
    brlcad/branches/gedplugins/src/libged/bot/bot_merge.c
    brlcad/branches/gedplugins/src/libged/bot/bot_smooth.c
    brlcad/branches/gedplugins/src/libged/bot/bot_split.c
    brlcad/branches/gedplugins/src/libged/bot/bot_sync.c
    brlcad/branches/gedplugins/src/libged/bot/bot_vertex_fuse.c

Removed Paths:
-------------
    brlcad/branches/gedplugins/src/libged/bot_condense/
    brlcad/branches/gedplugins/src/libged/bot_decimate/
    brlcad/branches/gedplugins/src/libged/bot_dump/
    brlcad/branches/gedplugins/src/libged/bot_face_fuse/
    brlcad/branches/gedplugins/src/libged/bot_face_sort/
    brlcad/branches/gedplugins/src/libged/bot_flip/
    brlcad/branches/gedplugins/src/libged/bot_fuse/
    brlcad/branches/gedplugins/src/libged/bot_merge/
    brlcad/branches/gedplugins/src/libged/bot_smooth/
    brlcad/branches/gedplugins/src/libged/bot_split/
    brlcad/branches/gedplugins/src/libged/bot_sync/
    brlcad/branches/gedplugins/src/libged/bot_vertex_fuse/

Modified: brlcad/branches/gedplugins/src/libged/CMakeLists.txt
===================================================================
--- brlcad/branches/gedplugins/src/libged/CMakeLists.txt        2020-07-21 
18:53:10 UTC (rev 76395)
+++ brlcad/branches/gedplugins/src/libged/CMakeLists.txt        2020-07-21 
19:08:41 UTC (rev 76396)
@@ -95,18 +95,18 @@
   bot/check.cpp
   bot/extrude.cpp
   bot/remesh.cpp
-  bot_condense/bot_condense.c
-  bot_decimate/bot_decimate.c
-  bot_dump/bot_dump.c
-  bot_face_fuse/bot_face_fuse.c
-  bot_face_sort/bot_face_sort.c
-  bot_flip/bot_flip.c
-  bot_fuse/bot_fuse.c
-  bot_merge/bot_merge.c
-  bot_smooth/bot_smooth.c
-  bot_split/bot_split.c
-  bot_sync/bot_sync.c
-  bot_vertex_fuse/bot_vertex_fuse.c
+  bot/bot_condense.c
+  bot/bot_decimate.c
+  bot/bot_dump.c
+  bot/bot_face_fuse.c
+  bot/bot_face_sort.c
+  bot/bot_flip.c
+  bot/bot_fuse.c
+  bot/bot_merge.c
+  bot/bot_smooth.c
+  bot/bot_split.c
+  bot/bot_sync.c
+  bot/bot_vertex_fuse.c
   brep/brep.cpp
   brep/conversion.cpp
   brep/csg.cpp
@@ -469,18 +469,6 @@
 add_subdirectory(blast)
 add_subdirectory(bo)
 add_subdirectory(bot)
-add_subdirectory(bot_condense)
-add_subdirectory(bot_decimate)
-add_subdirectory(bot_dump)
-add_subdirectory(bot_face_fuse)
-add_subdirectory(bot_face_sort)
-add_subdirectory(bot_flip)
-add_subdirectory(bot_fuse)
-add_subdirectory(bot_merge)
-add_subdirectory(bot_smooth)
-add_subdirectory(bot_split)
-add_subdirectory(bot_sync)
-add_subdirectory(bot_vertex_fuse)
 add_subdirectory(brep)
 add_subdirectory(cat)
 add_subdirectory(cc)

Modified: brlcad/branches/gedplugins/src/libged/bot/bot.cpp
===================================================================
--- brlcad/branches/gedplugins/src/libged/bot/bot.cpp   2020-07-21 18:53:10 UTC 
(rev 76395)
+++ brlcad/branches/gedplugins/src/libged/bot/bot.cpp   2020-07-21 19:08:41 UTC 
(rev 76396)
@@ -440,10 +440,62 @@
 extern "C" {
     struct ged_cmd_impl bot_cmd_impl = { "bot", ged_bot, GED_CMD_DEFAULT };
     const struct ged_cmd bot_cmd = { &bot_cmd_impl };
-    const struct ged_cmd *bot_cmds[] = { &bot_cmd,  NULL };
 
-    static const struct ged_plugin pinfo = { bot_cmds, 1 };
+    struct ged_cmd_impl bot_condense_cmd_impl = {"bot_condense", 
ged_bot_condense, GED_CMD_DEFAULT};
+    const struct ged_cmd bot_condense_cmd = { &bot_condense_cmd_impl };
 
+    struct ged_cmd_impl bot_decimate_cmd_impl = {"bot_decimate", 
ged_bot_decimate, GED_CMD_DEFAULT};
+    const struct ged_cmd bot_decimate_cmd = { &bot_decimate_cmd_impl };
+
+    struct ged_cmd_impl bot_dump_cmd_impl = {"bot_dump", ged_bot_dump, 
GED_CMD_DEFAULT};
+    const struct ged_cmd bot_dump_cmd = { &bot_dump_cmd_impl };
+
+    struct ged_cmd_impl bot_face_fuse_cmd_impl = {"bot_face_fuse", 
ged_bot_face_fuse, GED_CMD_DEFAULT};
+    const struct ged_cmd bot_face_fuse_cmd = { &bot_face_fuse_cmd_impl };
+
+    struct ged_cmd_impl bot_face_sort_cmd_impl = {"bot_face_sort", 
ged_bot_face_sort, GED_CMD_DEFAULT};
+    const struct ged_cmd bot_face_sort_cmd = { &bot_face_sort_cmd_impl };
+
+    struct ged_cmd_impl bot_flip_cmd_impl = {"bot_flip", ged_bot_flip, 
GED_CMD_DEFAULT};
+    const struct ged_cmd bot_flip_cmd = { &bot_flip_cmd_impl };
+
+    struct ged_cmd_impl bot_fuse_cmd_impl = {"bot_fuse", ged_bot_fuse, 
GED_CMD_DEFAULT};
+    const struct ged_cmd bot_fuse_cmd = { &bot_fuse_cmd_impl };
+
+    struct ged_cmd_impl bot_merge_cmd_impl = {"bot_merge", ged_bot_merge, 
GED_CMD_DEFAULT};
+    const struct ged_cmd bot_merge_cmd = { &bot_merge_cmd_impl };
+
+    struct ged_cmd_impl bot_smooth_cmd_impl = {"bot_smooth", ged_bot_smooth, 
GED_CMD_DEFAULT};
+    const struct ged_cmd bot_smooth_cmd = { &bot_smooth_cmd_impl };
+
+    struct ged_cmd_impl bot_split_cmd_impl = {"bot_split", ged_bot_split, 
GED_CMD_DEFAULT};
+    const struct ged_cmd bot_split_cmd = { &bot_split_cmd_impl };
+
+    struct ged_cmd_impl bot_sync_cmd_impl = {"bot_sync", ged_bot_sync, 
GED_CMD_DEFAULT};
+    const struct ged_cmd bot_sync_cmd = { &bot_sync_cmd_impl };
+
+    struct ged_cmd_impl bot_vertex_fuse_cmd_impl = {"bot_vertex_fuse", 
ged_bot_vertex_fuse, GED_CMD_DEFAULT};
+    const struct ged_cmd bot_vertex_fuse_cmd = { &bot_vertex_fuse_cmd_impl };
+
+    const struct ged_cmd *bot_cmds[] = {
+               &bot_cmd,
+       &bot_condense_cmd,
+       &bot_decimate_cmd,
+       &bot_dump_cmd,
+       &bot_face_fuse_cmd,
+       &bot_face_sort_cmd,
+       &bot_flip_cmd,
+       &bot_fuse_cmd,
+       &bot_merge_cmd,
+       &bot_smooth_cmd,
+       &bot_split_cmd,
+       &bot_sync_cmd,
+       &bot_vertex_fuse_cmd,
+       NULL
+    };
+
+    static const struct ged_plugin pinfo = { bot_cmds, 13 };
+
     COMPILER_DLLEXPORT const struct ged_plugin *ged_plugin_info()
     {
        return &pinfo;

Copied: brlcad/branches/gedplugins/src/libged/bot/bot_condense.c (from rev 
76395, brlcad/branches/gedplugins/src/libged/bot_condense/bot_condense.c)
===================================================================
--- brlcad/branches/gedplugins/src/libged/bot/bot_condense.c                    
        (rev 0)
+++ brlcad/branches/gedplugins/src/libged/bot/bot_condense.c    2020-07-21 
19:08:41 UTC (rev 76396)
@@ -0,0 +1,92 @@
+/*                         B O T _ C O N D E N S E . C
+ * BRL-CAD
+ *
+ * Copyright (c) 2008-2020 United States Government as represented by
+ * the U.S. Army Research Laboratory.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this file; see the file named COPYING for more
+ * information.
+ */
+/** @file libged/bot_condense.c
+ *
+ * The bot_condense command.
+ *
+ */
+
+#include "common.h"
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "rt/geom.h"
+
+#include "../ged_private.h"
+
+
+int
+ged_bot_condense(struct ged *gedp, int argc, const char *argv[])
+{
+    struct directory *old_dp, *new_dp;
+    struct rt_db_internal intern;
+    struct rt_bot_internal *bot;
+    int count2=0;
+    static const char *usage = "new_bot old_bot";
+
+    GED_CHECK_DATABASE_OPEN(gedp, GED_ERROR);
+    GED_CHECK_READ_ONLY(gedp, GED_ERROR);
+    GED_CHECK_ARGC_GT_0(gedp, argc, GED_ERROR);
+
+    /* initialize result */
+    bu_vls_trunc(gedp->ged_result_str, 0);
+
+    /* must be wanting help */
+    if (argc == 1) {
+       bu_vls_printf(gedp->ged_result_str, "Usage: %s %s", argv[0], usage);
+       return GED_HELP;
+    }
+
+    if (argc != 3) {
+       bu_vls_printf(gedp->ged_result_str, "Usage: %s %s", argv[0], usage);
+       return GED_ERROR;
+    }
+
+    GED_DB_LOOKUP(gedp, old_dp, argv[2], LOOKUP_NOISY, GED_ERROR & GED_QUIET);
+    GED_DB_GET_INTERNAL(gedp, &intern,  old_dp, bn_mat_identity, 
&rt_uniresource, GED_ERROR);
+
+    if (intern.idb_major_type != DB5_MAJORTYPE_BRLCAD || intern.idb_minor_type 
!= DB5_MINORTYPE_BRLCAD_BOT) {
+       bu_vls_printf(gedp->ged_result_str, "%s: %s is not a BOT solid!\n", 
argv[0], argv[2]);
+       return GED_ERROR;
+    }
+
+    bot = (struct rt_bot_internal *)intern.idb_ptr;
+    RT_BOT_CK_MAGIC(bot);
+
+    count2 = rt_bot_condense(bot);
+    bu_vls_printf(gedp->ged_result_str, "%s: %d dead vertices eliminated\n", 
argv[0], count2);
+
+    GED_DB_DIRADD(gedp, new_dp, argv[1], RT_DIR_PHONY_ADDR, 0, RT_DIR_SOLID, 
(void *)&intern.idb_type, GED_ERROR);
+    GED_DB_PUT_INTERNAL(gedp, new_dp, &intern, &rt_uniresource, GED_ERROR);
+
+    return GED_OK;
+}
+
+/*
+ * Local Variables:
+ * mode: C
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * c-file-style: "stroustrup"
+ * End:
+ * ex: shiftwidth=4 tabstop=8
+ */

Copied: brlcad/branches/gedplugins/src/libged/bot/bot_decimate.c (from rev 
76395, brlcad/branches/gedplugins/src/libged/bot_decimate/bot_decimate.c)
===================================================================
--- brlcad/branches/gedplugins/src/libged/bot/bot_decimate.c                    
        (rev 0)
+++ brlcad/branches/gedplugins/src/libged/bot/bot_decimate.c    2020-07-21 
19:08:41 UTC (rev 76396)
@@ -0,0 +1,201 @@
+/*                         B O T _ D E C I M A T E . C
+ * BRL-CAD
+ *
+ * Copyright (c) 2008-2020 United States Government as represented by
+ * the U.S. Army Research Laboratory.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this file; see the file named COPYING for more
+ * information.
+ */
+/** @file libged/bot_decimate.c
+ *
+ * The bot_decimate command.
+ *
+ */
+
+#include "common.h"
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "bu/getopt.h"
+#include "rt/geom.h"
+#include "ged.h"
+
+
+int
+ged_bot_decimate(struct ged *gedp, int argc, const char *argv[])
+{
+    int c;
+    struct rt_db_internal intern;
+    struct rt_bot_internal *bot;
+    struct directory *dp;
+    fastf_t max_chord_error = -1.0;
+    fastf_t max_normal_error = -1.0;
+    fastf_t min_edge_length = -1.0;
+    fastf_t feature_size = -1.0;
+    static const char *usage = "-f feature_size (to use the newer GCT 
decimator)"
+                              "\nOR: -c maximum_chord_error -n 
maximum_normal_error -e minimum_edge_length new_bot_name current_bot_name";
+
+    GED_CHECK_DATABASE_OPEN(gedp, GED_ERROR);
+    GED_CHECK_READ_ONLY(gedp, GED_ERROR);
+    GED_CHECK_ARGC_GT_0(gedp, argc, GED_ERROR);
+
+    /* initialize result */
+    bu_vls_trunc(gedp->ged_result_str, 0);
+
+    /* must be wanting help */
+    if (argc == 1) {
+       bu_vls_printf(gedp->ged_result_str, "Usage: %s %s", argv[0], usage);
+       return GED_HELP;
+    }
+
+    if (argc < 5 || argc > 9) {
+       bu_vls_printf(gedp->ged_result_str, "Usage: %s %s", argv[0], usage);
+       return GED_ERROR;
+    }
+
+    /* process args */
+    bu_optind = 1;
+    bu_opterr = 0;
+
+    while ((c = bu_getopt(argc, (char * const *)argv, "c:n:e:f:")) != -1) {
+       switch (c) {
+           case 'c':
+               max_chord_error = atof(bu_optarg);
+
+               if (max_chord_error < 0.0) {
+                   bu_vls_printf(gedp->ged_result_str,
+                                 "Maximum chord error cannot be less than 
zero");
+                   return GED_ERROR;
+               }
+
+               break;
+
+           case 'n':
+               max_normal_error = atof(bu_optarg);
+
+               if (max_normal_error < 0.0) {
+                   bu_vls_printf(gedp->ged_result_str,
+                                 "Maximum normal error cannot be less than 
zero");
+                   return GED_ERROR;
+               }
+
+               break;
+
+           case 'e':
+               min_edge_length = atof(bu_optarg);
+
+               if (min_edge_length < 0.0) {
+                   bu_vls_printf(gedp->ged_result_str,
+                                 "minimum edge length cannot be less than 
zero");
+                   return GED_ERROR;
+               }
+
+               break;
+
+           case 'f':
+               feature_size = atof(bu_optarg);
+
+               if (feature_size < 0.0) {
+                   bu_vls_printf(gedp->ged_result_str,
+                                 "minimum feature size cannot be less than 
zero");
+                   return GED_ERROR;
+               }
+
+               break;
+
+           default: {
+               bu_vls_printf(gedp->ged_result_str, "Usage: %s %s", argv[0], 
usage);
+               return GED_ERROR;
+           }
+       }
+    }
+
+    if (feature_size >= 0.0 && (max_chord_error >= 0.0 || max_normal_error >= 
0.0 ||  min_edge_length >= 0.0)) {
+       bu_vls_printf(gedp->ged_result_str, "-f may not be used with -c, -n, or 
-e");
+       return GED_ERROR;
+    }
+
+    argc -= bu_optind;
+    argv += bu_optind;
+
+    /* make sure new solid does not already exist */
+    GED_CHECK_EXISTS(gedp, argv[0], LOOKUP_QUIET, GED_ERROR);
+
+    /* make sure current solid does exist */
+    GED_DB_LOOKUP(gedp, dp, argv[1], LOOKUP_QUIET, GED_ERROR);
+
+    /* import the current solid */
+    RT_DB_INTERNAL_INIT(&intern);
+    GED_DB_GET_INTERNAL(gedp, &intern, dp, NULL, gedp->ged_wdbp->wdb_resp, 
GED_ERROR);
+
+    /* make sure this is a BOT solid */
+    if (intern.idb_major_type != DB5_MAJORTYPE_BRLCAD || intern.idb_minor_type 
!= DB5_MINORTYPE_BRLCAD_BOT) {
+       bu_vls_printf(gedp->ged_result_str, "%s is not a BOT solid\n", argv[1]);
+       rt_db_free_internal(&intern);
+       return GED_ERROR;
+    }
+
+    bot = (struct rt_bot_internal *)intern.idb_ptr;
+
+    RT_BOT_CK_MAGIC(bot);
+
+    /* convert maximum error, edge length, and feature size to mm */
+    if (max_chord_error > 0.0) {
+       max_chord_error = max_chord_error * 
gedp->ged_wdbp->dbip->dbi_local2base;
+    }
+
+    if (min_edge_length > 0.0) {
+       min_edge_length = min_edge_length * 
gedp->ged_wdbp->dbip->dbi_local2base;
+    }
+
+    if (feature_size >= 0.0) {
+       /* use the new GCT decimator */
+       const size_t orig_num_faces = bot->num_faces;
+       size_t edges_removed;
+       feature_size *= gedp->ged_wdbp->dbip->dbi_local2base;
+       edges_removed = rt_bot_decimate_gct(bot, feature_size);
+       bu_log("original face count = %zu\n", orig_num_faces);
+       bu_log("\tedges removed = %zu\n", edges_removed);
+       bu_log("\tnew face count = %zu\n", bot->num_faces);
+    } else {
+       /* use the old decimator */
+       if (rt_bot_decimate(bot, max_chord_error, max_normal_error, 
min_edge_length) < 0) {
+           bu_vls_printf(gedp->ged_result_str, "Decimation Error\n");
+           rt_db_free_internal(&intern);
+           return GED_ERROR;
+       }
+    }
+
+    /* save the result to the database */
+    /* XXX - should this be rt_db_put_internal() instead? */
+    if (wdb_put_internal(gedp->ged_wdbp, argv[0], &intern, 1.0) < 0) {
+       bu_vls_printf(gedp->ged_result_str,
+                     "Failed to write decimated BOT back to database\n");
+       return GED_ERROR;
+    }
+
+    return GED_OK;
+}
+
+/*
+ * Local Variables:
+ * mode: C
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * c-file-style: "stroustrup"
+ * End:
+ * ex: shiftwidth=4 tabstop=8
+ */

Copied: brlcad/branches/gedplugins/src/libged/bot/bot_dump.c (from rev 76395, 
brlcad/branches/gedplugins/src/libged/bot_dump/bot_dump.c)
===================================================================
--- brlcad/branches/gedplugins/src/libged/bot/bot_dump.c                        
        (rev 0)
+++ brlcad/branches/gedplugins/src/libged/bot/bot_dump.c        2020-07-21 
19:08:41 UTC (rev 76396)
@@ -0,0 +1,1566 @@
+/*                         B O T _ D U M P . C
+ * BRL-CAD
+ *
+ * Copyright (c) 2008-2020 United States Government as represented by
+ * the U.S. Army Research Laboratory.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this file; see the file named COPYING for more
+ * information.
+ */
+/** @file libged/bot_dump.c
+ *
+ * The bot_dump command.
+ *
+ */
+
+#include "common.h"
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <ctype.h>
+#include "bio.h"
+#include "bnetwork.h"
+
+#include "bu/cv.h"
+#include "bu/getopt.h"
+#include "bu/units.h"
+#include "vmath.h"
+#include "nmg.h"
+#include "rt/geom.h"
+
+#include "raytrace.h"
+#include "wdb.h"
+
+#include "brlcad_version.h"
+
+#include "raytrace.h"
+
+#include "dm/bview.h"
+#include "dm.h"
+
+#include "../ged_private.h"
+
+
+#define V3ARGS_SCALE(_a) (_a)[X]*cfactor, (_a)[Y]*cfactor, (_a)[Z]*cfactor
+
+static char usage[] = "[-b] [-n] [-m directory] [-o file] [-t dxf|obj|sat|stl] 
[-u units] [bot1 bot2 ...]";
+
+
+struct _ged_bot_dump_client_data {
+    struct ged *gedp;
+    FILE *fp;
+    int fd;
+    char *file_ext;
+};
+
+
+struct _ged_obj_material {
+    struct bu_list l;
+    struct bu_vls name;
+    unsigned char r;
+    unsigned char g;
+    unsigned char b;
+    fastf_t a;
+};
+
+
+static int using_dbot_dump;
+struct bu_list HeadObjMaterials = BU_LIST_INIT_ZERO;
+struct bu_vls obj_materials_file = BU_VLS_INIT_ZERO;
+FILE *obj_materials_fp;
+int num_obj_materials;
+int curr_obj_red;
+int curr_obj_green;
+int curr_obj_blue;
+fastf_t curr_obj_alpha;
+
+static enum otype output_type;
+static int binary;
+static int normals;
+static fastf_t cfactor;
+static char *output_file;      /* output filename */
+static char *output_directory; /* directory name to hold output files */
+static unsigned int total_faces;
+static int v_offset;
+static int curr_line_num;
+
+static int curr_body_id;
+static int curr_lump_id;
+static int curr_shell_id;
+static int curr_face_id;
+static int curr_loop_id;
+static int curr_edge_id;
+
+/* Byte swaps a four byte value */
+static void
+lswap(unsigned int *v)
+{
+    unsigned int r;
+
+    r =*v;
+    *v = ((r & 0xff) << 24) | ((r & 0xff00) << 8) | ((r & 0xff0000) >> 8)
+       | ((r & 0xff000000) >> 24);
+}
+
+
+static struct _ged_obj_material *
+obj_get_material(int red, int green, int blue, fastf_t transparency)
+{
+    struct _ged_obj_material *gomp;
+
+    for (BU_LIST_FOR(gomp, _ged_obj_material, &HeadObjMaterials)) {
+       if (gomp->r == red &&
+           gomp->g == green &&
+           gomp->b == blue &&
+           ZERO(gomp->a - transparency)) {
+           return gomp;
+       }
+    }
+
+    BU_GET(gomp, struct _ged_obj_material);
+    BU_LIST_APPEND(&HeadObjMaterials, &gomp->l);
+    gomp->r = red;
+    gomp->g = green;
+    gomp->b = blue;
+    gomp->a = transparency;
+    bu_vls_init(&gomp->name);
+    bu_vls_printf(&gomp->name, "matl_%d", ++num_obj_materials);
+
+    /* Write out newmtl to mtl file */
+    fprintf(obj_materials_fp, "newmtl %s\n", bu_vls_addr(&gomp->name));
+    fprintf(obj_materials_fp, "Kd %f %f %f\n",
+           (fastf_t)gomp->r / 255.0,
+           (fastf_t)gomp->g / 255.0,
+           (fastf_t)gomp->b / 255.0);
+    fprintf(obj_materials_fp, "d %f\n", gomp->a);
+    fprintf(obj_materials_fp, "illum 1\n");
+
+    return gomp;
+}
+
+
+static void
+obj_free_materials() {
+    struct _ged_obj_material *gomp;
+
+    while (BU_LIST_WHILE(gomp, _ged_obj_material, &HeadObjMaterials)) {
+       BU_LIST_DEQUEUE(&gomp->l);
+       bu_vls_free(&gomp->name);
+       BU_PUT(gomp, struct _ged_obj_material);
+    }
+}
+
+
+static void
+sat_write_header(FILE *fp)
+{
+    time_t now;
+
+    /* SAT header consists of three lines:
+     *
+     * 1: SAT_version num_records num_objects history_boolean
+     * 2: strlen product_id_str strlen version_str strlen date_str
+     * 3: cnv_to_mm resabs_value resnor_value
+     *
+     * When num_records is zero, it looks for an end marker.
+     */
+    fprintf(fp, "400 0 1 0\n");
+
+    time(&now);
+    fprintf(fp, "%ld BRL-CAD(%s)-bot_dump 16 ACIS 8.0 Unknown %ld %s",
+           (long)strlen(brlcad_version())+18, brlcad_version(), 
(long)strlen(ctime(&now)) - 1, ctime(&now));
+
+    /* FIXME: this includes abs tolerance info, should probably output ours */
+    fprintf(fp, "1 9.9999999999999995e-007 1e-010\n");
+}
+
+
+static void
+sat_write_bot(struct rt_bot_internal *bot, FILE *fp, char *UNUSED(name))
+{
+    int i, j;
+    fastf_t *vertices;
+    int *faces;
+    int first_vertex;
+    int first_coedge;
+    int first_face;
+    int num_vertices = bot->num_vertices;
+    int num_faces = bot->num_faces;
+
+    vertices = bot->vertices;
+    faces = bot->faces;
+
+    curr_body_id = curr_line_num;
+    curr_lump_id = curr_body_id + 1;
+    curr_shell_id = curr_lump_id + 1;
+    curr_face_id = curr_shell_id + 1 + num_vertices*2 + num_faces*6;
+
+    fprintf(fp, "-%d body $-1 $%d $-1 $-1 #\n", curr_body_id, curr_lump_id);
+    fprintf(fp, "-%d lump $-1 $-1 $%d $%d #\n", curr_lump_id, curr_shell_id, 
curr_body_id);
+    fprintf(fp, "-%d shell $-1 $-1 $-1 $%d $-1 $%d #\n", curr_shell_id, 
curr_face_id, curr_lump_id);
+
+    curr_line_num += 3;
+
+    /* Dump out vertices */
+    first_vertex = curr_line_num;
+    for (i = 0; i < num_vertices; i++) {
+       curr_edge_id = -1;
+       for (j = 0; j < num_faces; j++) {
+           if (faces[3*j]+first_vertex == curr_line_num) {
+               curr_edge_id = first_vertex + num_vertices*2 + num_faces*3 + 
j*3;
+               break;
+           } else if (faces[3*j+1]+first_vertex == curr_line_num) {
+               curr_edge_id = first_vertex + num_vertices*2 + num_faces*3 + 
j*3 + 1;
+               break;
+           } else if (faces[3*j+2]+first_vertex == curr_line_num) {
+               curr_edge_id = first_vertex + num_vertices*2 + num_faces*3 + 
j*3 + 2;
+               break;
+           }
+       }
+
+       fprintf(fp, "-%d vertex $-1 $%d $%d #\n", curr_line_num, curr_edge_id, 
curr_line_num+num_vertices);
+       ++curr_line_num;
+    }
+
+    /* Dump out points */
+    for (i = 0; i < num_vertices; i++) {
+       fprintf(fp, "-%d point $-1 %f %f %f #\n", curr_line_num, 
V3ARGS_SCALE(&vertices[3*i]));
+       ++curr_line_num;
+    }
+
+    /* Dump out coedges */
+    first_coedge = curr_line_num;
+    curr_loop_id = first_coedge+num_faces*7;
+    for (i = 0; i < num_faces; i++) {
+       fprintf(fp, "-%d coedge $-1 $%d $%d $%d $%d forward $%d $-1 #\n",
+               curr_line_num, curr_line_num+1, curr_line_num+2, curr_line_num,
+               curr_line_num+num_faces*3, curr_loop_id);
+       ++curr_line_num;
+       fprintf(fp, "-%d coedge $-1 $%d $%d $%d $%d forward $%d $-1 #\n",
+               curr_line_num, curr_line_num+1, curr_line_num-1, curr_line_num,
+               curr_line_num+num_faces*3, curr_loop_id);
+       ++curr_line_num;
+       fprintf(fp, "-%d coedge $-1 $%d $%d $%d $%d forward $%d $-1 #\n",
+               curr_line_num, curr_line_num-2, curr_line_num-1, curr_line_num,
+               curr_line_num+num_faces*3,  curr_loop_id);
+       ++curr_line_num;
+       ++curr_loop_id;
+    }
+
+    /* Dump out edges */
+    for (i = 0; i < num_faces; i++) {
+       fprintf(fp, "-%d edge $-1 $%d $%d $%d $%d forward #\n", curr_line_num,
+               faces[3*i]+first_vertex, faces[3*i+1]+first_vertex,
+               first_coedge + i*3, curr_line_num + num_faces*5);
+       ++curr_line_num;
+       fprintf(fp, "-%d edge $-1 $%d $%d $%d $%d forward #\n", curr_line_num,
+               faces[3*i+1]+first_vertex, faces[3*i+2]+first_vertex,
+               first_coedge + i*3 + 1, curr_line_num + num_faces*5);
+       ++curr_line_num;
+       fprintf(fp, "-%d edge $-1 $%d $%d $%d $%d forward #\n", curr_line_num,
+               faces[3*i+2]+first_vertex, faces[3*i]+first_vertex,
+               first_coedge + i*3 + 2, curr_line_num + num_faces*5);
+       ++curr_line_num;
+    }
+
+    /* Dump out faces */
+    first_face = curr_line_num;
+    for (i = 0; i < num_faces-1; i++) {
+       fprintf(fp, "-%d face $-1 $%d $%d $%d $-1 $%d forward single #\n",
+               curr_line_num, curr_line_num+1, curr_line_num+num_faces,
+               curr_shell_id, curr_line_num + num_faces*5);
+       ++curr_line_num;
+    }
+    fprintf(fp, "-%d face $-1 $-1 $%d $%d $-1 $%d forward single #\n",
+           curr_line_num, curr_line_num+num_faces, curr_shell_id,
+           curr_line_num + num_faces*5);
+    ++curr_line_num;
+
+    /* Dump out loops */
+    for (i = 0; i < num_faces; i++) {
+       fprintf(fp, "-%d loop $-1 $-1 $%d $%d #\n",
+               curr_line_num, first_coedge+i*3, first_face+i);
+       ++curr_line_num;
+    }
+
+    /* Dump out straight-curves for each edge */
+    for (i = 0; i < num_faces; i++) {
+       point_t A;
+       point_t B;
+       point_t C;
+       vect_t BmA;
+       vect_t CmB;
+       vect_t AmC;
+       int vi;
+
+       vi = 3*faces[3*i];
+       VSET(A, vertices[vi], vertices[vi+1], vertices[vi+2]);
+       vi = 3*faces[3*i+1];
+       VSET(B, vertices[vi], vertices[vi+1], vertices[vi+2]);
+       vi = 3*faces[3*i+2];
+       VSET(C, vertices[vi], vertices[vi+1], vertices[vi+2]);
+       VSUB2(BmA, B, A);
+       VSUB2(CmB, C, B);
+       VSUB2(AmC, A, C);
+       VUNITIZE(BmA);
+       VUNITIZE(CmB);
+       VUNITIZE(AmC);
+
+       fprintf(fp, "-%d straight-curve $-1 %f %f %f %f %f %f I I #\n", 
curr_line_num, V3ARGS_SCALE(A), V3ARGS(BmA));
+       ++curr_line_num;
+       fprintf(fp, "-%d straight-curve $-1 %f %f %f %f %f %f I I #\n", 
curr_line_num, V3ARGS_SCALE(B), V3ARGS(CmB));
+       ++curr_line_num;
+       fprintf(fp, "-%d straight-curve $-1 %f %f %f %f %f %f I I #\n", 
curr_line_num, V3ARGS_SCALE(C), V3ARGS(AmC));
+       ++curr_line_num;
+    }
+
+    /* Dump out plane-surfaces for each face */
+    for (i = 0; i < num_faces; i++) {
+       point_t A;
+       point_t B;
+       point_t C;
+       point_t center;
+       vect_t BmA;
+       vect_t CmA;
+       vect_t norm;
+       int vi;
+       fastf_t sf = 1.0/3.0;
+
+       vi = 3*faces[3*i];
+       VSET(A, vertices[vi], vertices[vi+1], vertices[vi+2]);
+       vi = 3*faces[3*i+1];
+       VSET(B, vertices[vi], vertices[vi+1], vertices[vi+2]);
+       vi = 3*faces[3*i+2];
+       VSET(C, vertices[vi], vertices[vi+1], vertices[vi+2]);
+
+       VADD3(center, A, B, C);
+       VSCALE(center, center, sf);
+
+       VSUB2(BmA, B, A);
+       VSUB2(CmA, C, A);
+       if (bot->orientation != RT_BOT_CW) {
+           VCROSS(norm, BmA, CmA);
+       } else {
+           VCROSS(norm, CmA, BmA);
+       }
+       VUNITIZE(norm);
+
+       VUNITIZE(BmA);
+
+       fprintf(fp, "-%d plane-surface $-1 %f %f %f %f %f %f %f %f %f forward_v 
I I I I #\n",
+               curr_line_num, V3ARGS_SCALE(A), V3ARGS(norm), V3ARGS(BmA));
+
+       ++curr_line_num;
+    }
+}
+
+
+static void
+dxf_write_bot(struct rt_bot_internal *bot, FILE *fp, char *name)
+{
+    fastf_t *vertices;
+    int num_faces, *faces;
+    point_t A;
+    point_t B;
+    point_t C;
+    int i, vi;
+
+    vertices = bot->vertices;
+    num_faces = bot->num_faces;
+    faces = bot->faces;
+
+    for (i = 0; i < num_faces; i++) {
+       vi = 3*faces[3*i];
+       VSET(A, vertices[vi], vertices[vi+1], vertices[vi+2]);
+       vi = 3*faces[3*i+1];
+       VSET(B, vertices[vi], vertices[vi+1], vertices[vi+2]);
+       vi = 3*faces[3*i+2];
+       VSET(C, vertices[vi], vertices[vi+1], vertices[vi+2]);
+
+       VSCALE(A, A, cfactor);
+       VSCALE(B, B, cfactor);
+       VSCALE(C, C, cfactor);
+
+       fprintf(fp, "0\n3DFACE\n8\n%s\n62\n7\n", name);
+       fprintf(fp, "%d\n%f\n%d\n%f\n%d\n%f\n",
+               10, A[X], 20, A[Y], 30, A[Z]);
+       fprintf(fp, "%d\n%f\n%d\n%f\n%d\n%f\n",
+               11, B[X], 21, B[Y], 31, B[Z]);
+       fprintf(fp, "%d\n%f\n%d\n%f\n%d\n%f\n",
+               12, C[X], 22, C[Y], 32, C[Z]);
+       fprintf(fp, "%d\n%f\n%d\n%f\n%d\n%f\n",
+               12, C[X], 22, C[Y], 32, C[Z]);
+    }
+
+}
+
+
+static void
+obj_write_bot(struct rt_bot_internal *bot, FILE *fp, char *name)
+{
+    int num_vertices;
+    fastf_t *vertices;
+    int num_faces, *faces;
+    point_t A;
+    point_t B;
+    point_t C;
+    vect_t BmA;
+    vect_t CmA;
+    vect_t norm;
+    int i, vi;
+    struct _ged_obj_material *gomp;
+
+    if (using_dbot_dump) {
+       gomp = obj_get_material(curr_obj_red,
+                                   curr_obj_green,
+                                   curr_obj_blue,
+                                   curr_obj_alpha);
+       fprintf(fp, "usemtl %s\n", bu_vls_addr(&gomp->name));
+    }
+
+    num_vertices = bot->num_vertices;
+    vertices = bot->vertices;
+    num_faces = bot->num_faces;
+    faces = bot->faces;
+
+    fprintf(fp, "g %s\n", name);
+
+    for (i = 0; i < num_vertices; i++) {
+       fprintf(fp, "v %f %f %f\n", V3ARGS_SCALE(&vertices[3*i]));
+    }
+
+    if (normals) {
+       for (i = 0; i < num_faces; i++) {
+           vi = 3*faces[3*i];
+           VSET(A, vertices[vi], vertices[vi+1], vertices[vi+2]);
+           vi = 3*faces[3*i+1];
+           VSET(B, vertices[vi], vertices[vi+1], vertices[vi+2]);
+           vi = 3*faces[3*i+2];
+           VSET(C, vertices[vi], vertices[vi+1], vertices[vi+2]);
+
+           VSUB2(BmA, B, A);
+           VSUB2(CmA, C, A);
+           if (bot->orientation != RT_BOT_CW) {
+               VCROSS(norm, BmA, CmA);
+           } else {
+               VCROSS(norm, CmA, BmA);
+           }
+           VUNITIZE(norm);
+
+           fprintf(fp, "vn %f %f %f\n", V3ARGS(norm));
+       }
+    }
+
+    if (normals) {
+       for (i = 0; i < num_faces; i++) {
+           fprintf(fp, "f %d//%d %d//%d %d//%d\n", faces[3*i]+v_offset, i+1, 
faces[3*i+1]+v_offset, i+1, faces[3*i+2]+v_offset, i+1);
+       }
+    } else {
+       for (i = 0; i < num_faces; i++) {
+           fprintf(fp, "f %d %d %d\n", faces[3*i]+v_offset, 
faces[3*i+1]+v_offset, faces[3*i+2]+v_offset);
+       }
+    }
+
+    v_offset += num_vertices;
+}
+
+
+static void
+stl_write_bot(struct rt_bot_internal *bot, FILE *fp, char *name)
+{
+    fastf_t *vertices;
+    int num_faces, *faces;
+    point_t A;
+    point_t B;
+    point_t C;
+    vect_t BmA;
+    vect_t CmA;
+    vect_t norm;
+    int i, vi;
+
+    vertices = bot->vertices;
+    num_faces = bot->num_faces;
+    faces = bot->faces;
+
+    fprintf(fp, "solid %s\n", name);
+    for (i = 0; i < num_faces; i++) {
+       vi = 3*faces[3*i];
+       VSET(A, vertices[vi], vertices[vi+1], vertices[vi+2]);
+       vi = 3*faces[3*i+1];
+       VSET(B, vertices[vi], vertices[vi+1], vertices[vi+2]);
+       vi = 3*faces[3*i+2];
+       VSET(C, vertices[vi], vertices[vi+1], vertices[vi+2]);
+
+       VSUB2(BmA, B, A);
+       VSUB2(CmA, C, A);
+       if (bot->orientation != RT_BOT_CW) {
+           VCROSS(norm, BmA, CmA);
+       } else {
+           VCROSS(norm, CmA, BmA);
+       }
+       VUNITIZE(norm);
+
+       fprintf(fp, "  facet normal %f %f %f\n", V3ARGS(norm));
+       fprintf(fp, "    outer loop\n");
+       fprintf(fp, "      vertex %f %f %f\n", V3ARGS_SCALE(A));
+       fprintf(fp, "      vertex %f %f %f\n", V3ARGS_SCALE(B));
+       fprintf(fp, "      vertex %f %f %f\n", V3ARGS_SCALE(C));
+       fprintf(fp, "    endloop\n");
+       fprintf(fp, "  endfacet\n");
+    }
+    fprintf(fp, "endsolid %s\n", name);
+}
+
+
+static void
+stl_write_bot_binary(struct rt_bot_internal *bot, int fd, char *UNUSED(name))
+{
+    fastf_t *vertices;
+    size_t num_faces;
+    int *faces;
+    point_t A;
+    point_t B;
+    point_t C;
+    vect_t BmA;
+    vect_t CmA;
+    vect_t norm;
+    unsigned long i, j, vi;
+
+    vertices = bot->vertices;
+    num_faces = bot->num_faces;
+    faces = bot->faces;
+
+    /* Write out the vertex data for each triangle */
+    for (i = 0; (size_t)i < num_faces; i++) {
+       float flts[12];
+       float *flt_ptr;
+       unsigned char vert_buffer[50];
+       int ret;
+
+       vi = 3*faces[3*i];
+       VSET(A, vertices[vi], vertices[vi+1], vertices[vi+2]);
+       vi = 3*faces[3*i+1];
+       VSET(B, vertices[vi], vertices[vi+1], vertices[vi+2]);
+       vi = 3*faces[3*i+2];
+       VSET(C, vertices[vi], vertices[vi+1], vertices[vi+2]);
+
+       VSUB2(BmA, B, A);
+       VSUB2(CmA, C, A);
+       if (bot->orientation != RT_BOT_CW) {
+           VCROSS(norm, BmA, CmA);
+       } else {
+           VCROSS(norm, CmA, BmA);
+       }
+       VUNITIZE(norm);
+
+       VSCALE(A, A, cfactor);
+       VSCALE(B, B, cfactor);
+       VSCALE(C, C, cfactor);
+
+       memset(vert_buffer, 0, sizeof(vert_buffer));
+
+       flt_ptr = flts;
+       VMOVE(flt_ptr, norm);
+       flt_ptr += 3;
+       VMOVE(flt_ptr, A);
+       flt_ptr += 3;
+       VMOVE(flt_ptr, B);
+       flt_ptr += 3;
+       VMOVE(flt_ptr, C);
+       flt_ptr += 3;
+
+       bu_cv_htonf(vert_buffer, (const unsigned char *)flts, 12);
+       for (j = 0; j < 12; j++) {
+           lswap((unsigned int *)&vert_buffer[j*4]);
+       }
+       ret = write(fd, vert_buffer, 50);
+       if (ret < 0) {
+           perror("write");
+       }
+    }
+}
+
+
+void
+_ged_bot_dump(struct directory *dp, const struct db_full_path *pathp, struct 
rt_bot_internal *bot, FILE *fp, int fd, const char *file_ext, const char 
*db_g_name)
+{
+    int ret;
+
+    if (output_directory) {
+       char *cp;
+       struct bu_vls file_name = BU_VLS_INIT_ZERO;
+
+       bu_vls_strcpy(&file_name, output_directory);
+       bu_vls_putc(&file_name, '/');
+       cp = dp->d_namep;
+       while (*cp != '\0') {
+           if (*cp == '/') {
+               bu_vls_putc(&file_name, '@');
+           } else if (*cp == '.' || isspace((int)*cp)) {
+               bu_vls_putc(&file_name, '_');
+           } else {
+               bu_vls_putc(&file_name, *cp);
+           }
+           cp++;
+       }
+       bu_vls_strcat(&file_name, file_ext);
+
+       if (binary && output_type == OTYPE_STL) {
+           char buf[81];       /* need exactly 80 chars for header */
+           unsigned char tot_buffer[4];
+
+           if ((fd=open(bu_vls_addr(&file_name), 
O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0) {
+               perror(bu_vls_addr(&file_name));
+               bu_log("Cannot open binary output file (%s) for writing\n", 
bu_vls_addr(&file_name));
+               bu_vls_free(&file_name);
+               return;
+           }
+
+           /* Write out STL header */
+           memset(buf, 0, sizeof(buf));
+           bu_strlcpy(buf, "BRL-CAD generated STL FILE", sizeof(buf));
+           ret = write(fd, &buf, 80);
+           if (ret < 0) {
+               perror("write");
+           }
+
+           /* write a place keeper for the number of triangles */
+           memset(buf, 0, 4);
+           ret = write(fd, &buf, 4);
+           if (ret < 0) {
+               perror("write");
+           }
+
+           stl_write_bot_binary(bot, fd, dp->d_namep);
+
+           /* Re-position pointer to 80th byte */
+           bu_lseek(fd, 80, SEEK_SET);
+
+           /* Write out number of triangles */
+           *(uint32_t *)tot_buffer = htonl((unsigned long)total_faces);
+           lswap((unsigned int *)tot_buffer);
+           ret = write(fd, tot_buffer, 4);
+           if (ret < 0) {
+               perror("write");
+           }
+
+           close(fd);
+       } else {
+           if ((fp=fopen(bu_vls_addr(&file_name), "wb+")) == NULL) {
+               perror(bu_vls_addr(&file_name));
+               bu_log("Cannot open ASCII output file (%s) for writing\n", 
bu_vls_addr(&file_name));
+               bu_vls_free(&file_name);
+               return;
+           }
+
+           switch (output_type) {
+               case OTYPE_DXF:
+                   fprintf(fp,
+                           "0\nSECTION\n2\nHEADER\n999\n%s (BOT from 
%s)\n0\nENDSEC\n0\nSECTION\n2\nENTITIES\n",
+                           dp->d_namep, db_g_name);
+                   dxf_write_bot(bot, fp, dp->d_namep);
+                   fprintf(fp, "0\nENDSEC\n0\nEOF\n");
+                   break;
+               case OTYPE_OBJ:
+                   v_offset = 1;
+                   fprintf(fp, "mtllib %s\n", 
bu_vls_addr(&obj_materials_file));
+                   if (!pathp) {
+                       obj_write_bot(bot, fp, dp->d_namep);
+                   } else {
+                       char *pathstr = db_path_to_string(pathp);
+                       obj_write_bot(bot, fp, pathstr);
+                       bu_free(pathstr, "free path");
+                   }
+                   break;
+               case OTYPE_SAT:
+                   curr_line_num = 0;
+
+                   sat_write_header(fp);
+
+                   sat_write_bot(bot, fp, dp->d_namep);
+                   fprintf(fp, "End-of-ACIS-data\n");
+                   break;
+               case OTYPE_STL:
+               default:
+                   stl_write_bot(bot, fp, dp->d_namep);
+                   break;
+           }
+
+           fclose(fp);
+       }
+
+       bu_vls_free(&file_name);
+    } else {
+      if (binary && output_type == OTYPE_STL) {
+       total_faces += bot->num_faces;
+       stl_write_bot_binary(bot, fd, dp->d_namep);
+      } else if (binary && output_type != OTYPE_STL) {
+       bu_log("Unsupported binary file type - only STL is currently 
supported\n");
+       return;
+      } else {
+       /* If we get to this point, we need fp - check for it */
+       if (fp) {
+           char *pathstr;
+       switch (output_type) {
+         case OTYPE_DXF:
+           dxf_write_bot(bot, fp, dp->d_namep);
+           break;
+         case OTYPE_OBJ:
+           if (!pathp) {
+               obj_write_bot(bot, fp, dp->d_namep);
+           } else {
+               pathstr = db_path_to_string(pathp);
+               obj_write_bot(bot, fp, pathstr);
+               bu_free(pathstr, "free path");
+           }
+           break;
+         case OTYPE_SAT:
+           sat_write_bot(bot, fp, dp->d_namep);
+           break;
+         case OTYPE_STL:
+         default:
+           stl_write_bot(bot, fp, dp->d_namep);
+           break;
+       }
+       } else {
+         bu_log("_ged_bot_dump: non-binay file requested but fp is NULL!\n");
+       }
+      }
+    }
+}
+
+
+static union tree *
+bot_dump_leaf(struct db_tree_state *UNUSED(tsp),
+             const struct db_full_path *pathp,
+             struct rt_db_internal *UNUSED(ip),
+             void *client_data)
+{
+    int ret;
+    union tree *curtree;
+    mat_t mat;
+    struct directory *dp;
+    struct rt_db_internal intern;
+    struct rt_bot_internal *bot;
+    struct _ged_bot_dump_client_data *gbdcdp = (struct 
_ged_bot_dump_client_data *)client_data;
+
+    /* Indicate success by returning something other than TREE_NULL */
+    BU_GET(curtree, union tree);
+    RT_TREE_INIT(curtree);
+    curtree->tr_op = OP_NOP;
+
+    dp = pathp->fp_names[pathp->fp_len-1];
+
+    /* we only dump BOT primitives, so skip some obvious exceptions */
+    if (dp->d_major_type != DB5_MAJORTYPE_BRLCAD || dp->d_flags & RT_DIR_COMB)
+       return curtree;
+
+    MAT_IDN(mat);
+
+    /* get the internal form */
+    ret=rt_db_get_internal(&intern, dp, gbdcdp->gedp->ged_wdbp->dbip, mat, 
&rt_uniresource);
+
+    if (ret < 0) {
+       bu_log("ged_bot_leaf: rt_get_internal failure %d on %s\n", ret, 
dp->d_namep);
+       return curtree;
+    }
+
+    if (ret != ID_BOT) {
+       bu_log("ged_bot_leaf: %s is not a bot (ignored)\n", dp->d_namep);
+       rt_db_free_internal(&intern);
+       return curtree;
+    }
+
+    bot = (struct rt_bot_internal *)intern.idb_ptr;
+    _ged_bot_dump(dp, pathp, bot, gbdcdp->fp, gbdcdp->fd, gbdcdp->file_ext, 
gbdcdp->gedp->ged_wdbp->dbip->dbi_filename);
+    rt_db_free_internal(&intern);
+
+    return curtree;
+}
+
+
+static int
+bot_dump_get_args(struct ged *gedp, int argc, const char *argv[])
+{
+    int c;
+
+    output_type = OTYPE_STL;
+    binary = 0;
+    normals = 0;
+    cfactor = 1.0;
+    output_file = NULL;
+    output_directory = NULL;
+    total_faces = 0;
+    v_offset = 1;
+    curr_line_num = 0;
+    bu_optind = 1;
+
+    /* Get command line options. */
+    while ((c = bu_getopt(argc, (char * const *)argv, "bno:m:t:u:")) != -1) {
+       switch (c) {
+           case 'b':           /* Binary output file */
+               binary=1;
+               break;
+           case 'n':           /* Binary output file */
+               normals=1;
+               break;
+           case 'm':
+               output_directory = bu_optarg;
+               break;
+           case 'o':           /* Output file name. */
+               output_file = bu_optarg;
+               break;
+           case 't':
+               if (BU_STR_EQUAL("dxf", bu_optarg))
+                   output_type = OTYPE_DXF;
+               else if (BU_STR_EQUAL("obj", bu_optarg))
+                   output_type = OTYPE_OBJ;
+               else if (BU_STR_EQUAL("sat", bu_optarg))
+                   output_type = OTYPE_SAT;
+               else if (BU_STR_EQUAL("stl", bu_optarg))
+                   output_type = OTYPE_STL;
+               else {
+                   bu_vls_printf(gedp->ged_result_str, "Usage: %s %s\n", 
argv[0], usage);
+                   return GED_ERROR;
+               }
+               break;
+           case 'u':
+               cfactor = bu_units_conversion(bu_optarg);
+               if (ZERO(cfactor))
+                   cfactor = 1.0;
+               else
+                   cfactor = 1.0 / cfactor;
+
+               break;
+           default:
+               bu_vls_printf(gedp->ged_result_str, "Usage: %s %s\n", argv[0], 
usage);
+               return GED_ERROR;
+       }
+    }
+
+    return GED_OK;
+}
+
+
+int
+ged_bot_dump(struct ged *gedp, int argc, const char *argv[])
+{
+    int ret;
+    struct rt_db_internal intern;
+    struct rt_bot_internal *bot;
+    struct directory *dp;
+    char *file_ext = NULL;
+    FILE *fp = (FILE *)0;
+    int fd = -1;
+    mat_t mat;
+    int i;
+    const char *cmd_name;
+
+    GED_CHECK_DATABASE_OPEN(gedp, GED_ERROR);
+    GED_CHECK_ARGC_GT_0(gedp, argc, GED_ERROR);
+
+    /* initialize result */
+    bu_vls_trunc(gedp->ged_result_str, 0);
+
+    /* must be wanting help */
+    if (argc == 1) {
+       bu_vls_printf(gedp->ged_result_str, "Usage: %s %s\n", argv[0], usage);
+       return GED_HELP;
+    }
+
+    using_dbot_dump = 0;
+
+    if (bot_dump_get_args(gedp, argc, argv) == GED_ERROR)
+       return GED_ERROR;
+
+    if (bu_optind > argc) {
+       bu_vls_printf(gedp->ged_result_str, "Usage: %s %s", argv[0], usage);
+       return GED_ERROR;
+    }
+
+    if (output_file && output_directory) {
+       fprintf(stderr, "ERROR: options \"-o\" and \"-m\" are mutually 
exclusive\n");
+       return GED_ERROR;
+    }
+
+    if (!output_file && !output_directory) {
+       if (binary) {
+           bu_vls_printf(gedp->ged_result_str, "Can't output binary to 
stdout\nUsage: %s %s\n", argv[0], usage);
+           return GED_ERROR;
+       }
+       fp = stdout;
+
+       /* Set this to something non-null in order to possibly write eof */
+       output_file = "stdout";
+    }
+
+    if (output_file) {
+       if (binary && output_type == OTYPE_STL) {
+           char buf[81];       /* need exactly 80 chars for header */
+
+           /* Open binary output file */
+           if ((fd=open(output_file, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 
S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0) {
+               perror(argv[0]);
+               bu_vls_printf(gedp->ged_result_str, "Cannot open binary output 
file (%s) for writing\n", output_file);
+               return GED_ERROR;
+           }
+
+           /* Write out STL header if output file is binary */
+           memset(buf, 0, sizeof(buf));
+           bu_strlcpy(buf, "BRL-CAD generated STL FILE", sizeof(buf));
+           ret = write(fd, &buf, 80);
+           if (ret < 0) {
+               perror("write");
+           }
+
+           /* write a place keeper for the number of triangles */
+           memset(buf, 0, 4);
+           ret = write(fd, &buf, 4);
+           if (ret < 0) {
+               perror("write");
+           }
+       } else {
+           /* Open ASCII output file */
+           if ((fp=fopen(output_file, "wb+")) == NULL) {
+               perror(argv[0]);
+               bu_vls_printf(gedp->ged_result_str, "Cannot open ascii output 
file (%s) for writing\n", output_file);
+               return GED_ERROR;
+           }
+
+           switch (output_type) {
+               case OTYPE_DXF:
+                   /* output DXF header and start of TABLES section */
+                   fprintf(fp,
+                           "0\nSECTION\n2\nHEADER\n999\n%s (All 
Bots)\n0\nENDSEC\n0\nSECTION\n2\nENTITIES\n",
+                           argv[argc-1]);
+                   break;
+               case OTYPE_SAT:
+                   sat_write_header(fp);
+                   break;
+               default:
+                   break;
+           }
+       }
+    }
+
+    /* save the command name */
+    cmd_name = argv[0];
+
+    /* skip past the command name and optional args */
+    argc -= bu_optind;
+    argv += bu_optind;
+
+    if (output_directory) {
+       switch (output_type) {
+           case OTYPE_DXF:
+               file_ext = ".dxf";
+               break;
+           case OTYPE_OBJ:
+               file_ext = ".obj";
+               break;
+           case OTYPE_SAT:
+               file_ext = ".sat";
+               break;
+           case OTYPE_STL:
+           default:
+               file_ext = ".stl";
+               break;
+       }
+    }
+
+    MAT_IDN(mat);
+
+    if (argc < 1) {
+       /* dump all the bots */
+       FOR_ALL_DIRECTORY_START(dp, gedp->ged_wdbp->dbip) {
+
+           /* we only dump BOT primitives, so skip some obvious exceptions */
+           if (dp->d_major_type != DB5_MAJORTYPE_BRLCAD) continue;
+           if (dp->d_flags & RT_DIR_COMB) continue;
+
+           /* get the internal form */
+           i = rt_db_get_internal(&intern, dp, gedp->ged_wdbp->dbip, mat, 
&rt_uniresource);
+           if (i < 0) {
+               fprintf(stderr, "%s: rt_get_internal failure %d on %s\n", 
cmd_name, i, dp->d_namep);
+               continue;
+           }
+
+           if (i != ID_BOT) {
+               continue;
+           }
+
+           bot = (struct rt_bot_internal *)intern.idb_ptr;
+           _ged_bot_dump(dp, NULL, bot, fp, fd, file_ext, 
gedp->ged_wdbp->dbip->dbi_filename);
+           rt_db_free_internal(&intern);
+
+       } FOR_ALL_DIRECTORY_END;
+    } else {
+       int ac = 1;
+       int ncpu = 1;
+       char *av[2];
+       struct _ged_bot_dump_client_data gbdcdp = {NULL, NULL, 0, NULL};
+
+       av[1] = (char *)0;
+       gbdcdp.gedp = gedp;
+       gbdcdp.fp = fp;
+       gbdcdp.fd = fd;
+       gbdcdp.file_ext = file_ext;
+
+       for (i = 0; i < argc; ++i) {
+           av[0] = (char *)argv[i];
+           ret = db_walk_tree(gedp->ged_wdbp->dbip,
+                              ac,
+                              (const char **)av,
+                              ncpu,
+                              &gedp->ged_wdbp->wdb_initial_tree_state,
+                              0,
+                              0,
+                              bot_dump_leaf,
+                              (void *)&gbdcdp);
+       }
+    }
+
+
+    if (output_file) {
+       if (binary && output_type == OTYPE_STL) {
+           unsigned char tot_buffer[4];
+
+           /* Re-position pointer to 80th byte */
+           bu_lseek(fd, 80, SEEK_SET);
+
+           /* Write out number of triangles */
+           *(uint32_t *)tot_buffer = htonl((unsigned long)total_faces);
+           lswap((unsigned int *)tot_buffer);
+           ret = write(fd, tot_buffer, 4);
+           if (ret < 0) {
+               perror("write");
+           }
+
+           close(fd);
+       } else {
+           /* end of layers section, start of ENTITIES SECTION */
+           switch (output_type) {
+               case OTYPE_DXF:
+                   fprintf(fp, "0\nENDSEC\n0\nEOF\n");
+                   break;
+               case OTYPE_SAT:
+                   fprintf(fp, "End-of-ACIS-data\n");
+                   break;
+               default:
+                   break;
+           }
+
+           fclose(fp);
+       }
+    }
+
+    return GED_OK;
+}
+
+
+static void
+write_data_arrows(struct bview_data_arrow_state *gdasp, FILE *fp, int sflag)
+{
+    register int i;
+
+    if (gdasp->gdas_draw) {
+       struct _ged_obj_material *gomp;
+
+       gomp = obj_get_material(gdasp->gdas_color[0],
+                                   gdasp->gdas_color[1],
+                                   gdasp->gdas_color[2],
+                                   1);
+       fprintf(fp, "usemtl %s\n", bu_vls_addr(&gomp->name));
+
+       if (sflag)
+           fprintf(fp, "g sdata_arrows\n");
+       else
+           fprintf(fp, "g data_arrows\n");
+
+       for (i = 0; i < gdasp->gdas_num_points; i += 2) {
+           point_t A, B;
+           point_t BmA;
+           point_t offset;
+           point_t perp1, perp2;
+           point_t a_base;
+           point_t a_pt1, a_pt2, a_pt3, a_pt4;
+
+           VMOVE(A, gdasp->gdas_points[i]);
+           VMOVE(B, gdasp->gdas_points[i+1]);
+
+           VSUB2(BmA, B, A);
+
+           VUNITIZE(BmA);
+           VSCALE(offset, BmA, -gdasp->gdas_tip_length);
+
+           bn_vec_perp(perp1, BmA);
+           VUNITIZE(perp1);
+
+           VCROSS(perp2, BmA, perp1);
+           VUNITIZE(perp2);
+
+           VSCALE(perp1, perp1, gdasp->gdas_tip_width);
+           VSCALE(perp2, perp2, gdasp->gdas_tip_width);
+
+           VADD2(a_base, B, offset);
+           VADD2(a_pt1, a_base, perp1);
+           VADD2(a_pt2, a_base, perp2);
+           VSUB2(a_pt3, a_base, perp1);
+           VSUB2(a_pt4, a_base, perp2);
+
+           fprintf(fp, "v %f %f %f\n", V3ARGS_SCALE(A));
+           fprintf(fp, "v %f %f %f\n", V3ARGS_SCALE(B));
+           fprintf(fp, "v %f %f %f\n", V3ARGS_SCALE(a_pt1));
+           fprintf(fp, "v %f %f %f\n", V3ARGS_SCALE(a_pt2));
+           fprintf(fp, "v %f %f %f\n", V3ARGS_SCALE(a_pt3));
+           fprintf(fp, "v %f %f %f\n", V3ARGS_SCALE(a_pt4));
+       }
+
+       for (i = 0; i < gdasp->gdas_num_points; i += 2) {
+           fprintf(fp, "l %d %d\n", (i/2*6)+v_offset, (i/2*6)+v_offset+1);
+           fprintf(fp, "l %d %d\n", (i/2*6)+v_offset+1, (i/2*6)+v_offset+2);
+           fprintf(fp, "l %d %d\n", (i/2*6)+v_offset+1, (i/2*6)+v_offset+3);
+           fprintf(fp, "l %d %d\n", (i/2*6)+v_offset+1, (i/2*6)+v_offset+4);
+           fprintf(fp, "l %d %d\n", (i/2*6)+v_offset+1, (i/2*6)+v_offset+5);
+           fprintf(fp, "l %d %d\n", (i/2*6)+v_offset+2, (i/2*6)+v_offset+3);
+           fprintf(fp, "l %d %d\n", (i/2*6)+v_offset+3, (i/2*6)+v_offset+4);
+           fprintf(fp, "l %d %d\n", (i/2*6)+v_offset+4, (i/2*6)+v_offset+5);
+           fprintf(fp, "l %d %d\n", (i/2*6)+v_offset+5, (i/2*6)+v_offset+2);
+       }
+
+       v_offset += ((gdasp->gdas_num_points/2)*6);
+    }
+}
+
+
+static void
+write_data_axes(struct bview_data_axes_state *bndasp, FILE *fp, int sflag)
+{
+    register int i;
+
+    if (bndasp->draw) {
+       fastf_t halfAxesSize;
+       struct _ged_obj_material *gomp;
+
+       halfAxesSize = bndasp->size * 0.5;
+
+       gomp = obj_get_material(bndasp->color[0],
+                                   bndasp->color[1],
+                                   bndasp->color[2],
+                                   1);
+       fprintf(fp, "usemtl %s\n", bu_vls_addr(&gomp->name));
+
+       if (sflag)
+           fprintf(fp, "g sdata_axes\n");
+       else
+           fprintf(fp, "g data_axes\n");
+
+       for (i = 0; i < bndasp->num_points; ++i) {
+           point_t A, B;
+
+           /* draw X axis with x/y offsets */
+           VSET(A,
+                bndasp->points[i][X] - halfAxesSize,
+                bndasp->points[i][Y],
+                bndasp->points[i][Z]);
+           VSET(B,
+                bndasp->points[i][X] + halfAxesSize,
+                bndasp->points[i][Y],
+                bndasp->points[i][Z]);
+
+           fprintf(fp, "v %f %f %f\n", V3ARGS_SCALE(A));
+           fprintf(fp, "v %f %f %f\n", V3ARGS_SCALE(B));
+
+           /* draw Y axis with x/y offsets */
+           VSET(A,
+                bndasp->points[i][X],
+                bndasp->points[i][Y] - halfAxesSize,
+                bndasp->points[i][Z]);
+           VSET(B,
+                bndasp->points[i][X],
+                bndasp->points[i][Y] + halfAxesSize,
+                bndasp->points[i][Z]);
+
+           fprintf(fp, "v %f %f %f\n", V3ARGS_SCALE(A));
+           fprintf(fp, "v %f %f %f\n", V3ARGS_SCALE(B));
+
+           /* draw Z axis with x/y offsets */
+           VSET(A,
+                bndasp->points[i][X],
+                bndasp->points[i][Y],
+                bndasp->points[i][Z] - halfAxesSize);
+           VSET(B,
+                bndasp->points[i][X],
+                bndasp->points[i][Y],
+                bndasp->points[i][Z] + halfAxesSize);
+
+           fprintf(fp, "v %f %f %f\n", V3ARGS_SCALE(A));
+           fprintf(fp, "v %f %f %f\n", V3ARGS_SCALE(B));
+       }
+
+       for (i = 0; i < bndasp->num_points; ++i) {
+           fprintf(fp, "l %d %d\n", (i*6)+v_offset, (i*6)+v_offset+1);
+           fprintf(fp, "l %d %d\n", (i*6)+v_offset+2, (i*6)+v_offset+3);
+           fprintf(fp, "l %d %d\n", (i*6)+v_offset+4, (i*6)+v_offset+5);
+       }
+
+
+       v_offset += (bndasp->num_points*6);
+    }
+}
+
+
+static void
+write_data_lines(struct bview_data_line_state *gdlsp, FILE *fp, int sflag)
+{
+    register int i;
+
+    if (gdlsp->gdls_draw) {
+       struct _ged_obj_material *gomp;
+
+       gomp = obj_get_material(gdlsp->gdls_color[0],
+                                   gdlsp->gdls_color[1],
+                                   gdlsp->gdls_color[2],
+                                   1);
+       fprintf(fp, "usemtl %s\n", bu_vls_addr(&gomp->name));
+
+       if (sflag)
+           fprintf(fp, "g sdata_lines\n");
+       else
+           fprintf(fp, "g data_lines\n");
+
+       for (i = 0; i < gdlsp->gdls_num_points; i += 2) {
+           point_t A, B;
+
+           VMOVE(A, gdlsp->gdls_points[i]);
+           VMOVE(B, gdlsp->gdls_points[i+1]);
+
+           fprintf(fp, "v %f %f %f\n", V3ARGS_SCALE(A));
+           fprintf(fp, "v %f %f %f\n", V3ARGS_SCALE(B));
+       }
+
+       for (i = 0; i < gdlsp->gdls_num_points; i += 2) {
+           fprintf(fp, "l %d %d\n", i+v_offset, i+v_offset+1);
+       }
+
+       v_offset += gdlsp->gdls_num_points;
+    }
+}
+
+
+static void
+obj_write_data(struct ged *gedp, FILE *fp)
+{
+    write_data_arrows(&gedp->ged_gvp->gv_data_arrows, fp, 0);
+    write_data_arrows(&gedp->ged_gvp->gv_sdata_arrows, fp, 1);
+
+    write_data_axes(&gedp->ged_gvp->gv_data_axes, fp, 0);
+    write_data_axes(&gedp->ged_gvp->gv_sdata_axes, fp, 1);
+
+    write_data_lines(&gedp->ged_gvp->gv_data_lines, fp, 0);
+    write_data_lines(&gedp->ged_gvp->gv_sdata_lines, fp, 1);
+}
+
+
+static int
+data_dump(struct ged *gedp, FILE *fp)
+{
+    switch (output_type) {
+       case OTYPE_DXF:
+           break;
+       case OTYPE_OBJ:
+           if (output_directory) {
+               char *cp;
+               struct bu_vls filepath = BU_VLS_INIT_ZERO;
+               FILE *data_fp;
+
+               cp = strrchr(output_directory, '/');
+               if (!cp)
+                   cp = (char *)output_directory;
+               else
+                   ++cp;
+
+               if (*cp == '\0') {
+                   bu_vls_printf(gedp->ged_result_str, "data_dump: bad dirname 
- %s\n", output_directory);
+                   return GED_ERROR;
+               }
+
+               bu_vls_printf(&filepath, "%s/%s_data.obj", output_directory, 
cp);
+
+               if ((data_fp=fopen(bu_vls_addr(&filepath), "wb+")) == NULL) {
+                   bu_vls_printf(gedp->ged_result_str, "data_dump: failed to 
open %s\n", bu_vls_addr(&filepath));
+                   bu_vls_free(&filepath);
+                   return GED_ERROR;
+               }
+
+               bu_vls_free(&filepath);
+               obj_write_data(gedp, data_fp);
+               fclose(data_fp);
+           } else
+               if (fp) {
+                 obj_write_data(gedp, fp);
+               } else {
+                 bu_vls_printf(gedp->ged_result_str, "data_dump: bad FILE 
fp\n");
+                 return GED_ERROR;
+               }
+
+           break;
+       case OTYPE_SAT:
+           break;
+       case OTYPE_STL:
+       default:
+           break;
+    }
+
+    return GED_OK;
+}
+
+
+int
+ged_dbot_dump(struct ged *gedp, int argc, const char *argv[])
+{
+    int ret;
+    char *file_ext = NULL;
+    FILE *fp = (FILE *)0;
+    int fd = -1;
+    const char *cmd_name;
+
+    GED_CHECK_DATABASE_OPEN(gedp, GED_ERROR);
+    GED_CHECK_DRAWABLE(gedp, GED_ERROR);
+    GED_CHECK_VIEW(gedp, GED_ERROR);
+    GED_CHECK_ARGC_GT_0(gedp, argc, GED_ERROR);
+
+    /* initialize result */
+    bu_vls_trunc(gedp->ged_result_str, 0);
+
+    /* must be wanting help */
+    if (argc == 1) {
+       bu_vls_printf(gedp->ged_result_str, usage, argv[0]);
+       return GED_HELP;
+    }
+
+    using_dbot_dump = 1;
+
+    if (bot_dump_get_args(gedp, argc, argv) == GED_ERROR)
+       return GED_ERROR;
+
+    if (bu_optind != argc) {
+       bu_vls_printf(gedp->ged_result_str, "Usage: %s %s", argv[0], usage);
+       return GED_ERROR;
+    }
+
+    if (output_file && output_directory) {
+       fprintf(stderr, "ERROR: options \"-o\" and \"-m\" are mutually 
exclusive\n");
+       return GED_ERROR;
+    }
+
+    if (!output_file && !output_directory) {
+       if (binary) {
+           bu_vls_printf(gedp->ged_result_str, "Can't output binary to 
stdout\nUsage: %s %s", argv[0], usage);
+           return GED_ERROR;
+       }
+       fp = stdout;
+
+       /* Set this to something non-null in order to possibly write eof */
+       output_file = "stdout";
+    }
+
+    if (output_file) {
+       if (binary && output_type == OTYPE_STL) {
+           char buf[81];       /* need exactly 80 chars for header */
+
+           /* Open binary output file */
+           if ((fd=open(output_file, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 
S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0) {
+               perror(argv[0]);
+               bu_vls_printf(gedp->ged_result_str, "Cannot open binary output 
file (%s) for writing\n", output_file);
+               return GED_ERROR;
+           }
+
+           /* Write out STL header if output file is binary */
+           memset(buf, 0, sizeof(buf));
+           bu_strlcpy(buf, "BRL-CAD generated STL FILE", sizeof(buf));
+           ret = write(fd, &buf, 80);
+           if (ret < 0) {
+               perror("write");
+           }
+
+           /* write a place keeper for the number of triangles */
+           memset(buf, 0, 4);
+           ret = write(fd, &buf, 4);
+           if (ret < 0) {
+               perror("write");
+           }
+       } else {
+           /* Open ASCII output file */
+           if ((fp=fopen(output_file, "wb+")) == NULL) {
+               perror(argv[0]);
+               bu_vls_printf(gedp->ged_result_str, "Cannot open ascii output 
file (%s) for writing\n", output_file);
+               return GED_ERROR;
+           }
+
+           switch (output_type) {
+               case OTYPE_DXF:
+                   /* output DXF header and start of TABLES section */
+                   fprintf(fp,
+                           "0\nSECTION\n2\nHEADER\n999\n%s (All 
Bots)\n0\nENDSEC\n0\nSECTION\n2\nENTITIES\n",
+                           argv[argc-1]);
+                   break;
+               case OTYPE_SAT:
+                   sat_write_header(fp);
+                   break;
+               default:
+                   break;
+           }
+       }
+    }
+
+    /* save the command name */
+    cmd_name = argv[0];
+
+    if (output_directory) {
+       switch (output_type) {
+           case OTYPE_DXF:
+               file_ext = ".dxf";
+               break;
+           case OTYPE_OBJ:
+               file_ext = ".obj";
+
+               BU_LIST_INIT(&HeadObjMaterials);
+
+               {
+                   char *cp;
+                   struct bu_vls filepath = BU_VLS_INIT_ZERO;
+
+                   cp = strrchr(output_directory, '/');
+                   if (!cp)
+                       cp = (char *)output_directory;
+                   else
+                       ++cp;
+
+                   if (*cp == '\0') {
+                       bu_vls_printf(gedp->ged_result_str, "%s: bad dirname - 
%s\n", cmd_name, output_directory);
+                       return GED_ERROR;
+                   }
+
+                   bu_vls_trunc(&obj_materials_file, 0);
+                   bu_vls_printf(&obj_materials_file, "%s.mtl", cp);
+
+                   bu_vls_printf(&filepath, "%s/%s", output_directory, 
bu_vls_addr(&obj_materials_file));
+
+                   if ((obj_materials_fp=fopen(bu_vls_addr(&filepath), "wb+")) 
== NULL) {
+                       bu_vls_printf(gedp->ged_result_str, "%s: failed to open 
%s\n", cmd_name, bu_vls_addr(&filepath));
+                       bu_vls_free(&obj_materials_file);
+                       bu_vls_free(&filepath);
+                       return GED_ERROR;
+                   }
+
+                   bu_vls_free(&filepath);
+               }
+
+               num_obj_materials = 0;
+
+               break;
+           case OTYPE_SAT:
+               file_ext = ".sat";
+               break;
+           case OTYPE_STL:
+           default:
+               file_ext = ".stl";
+               break;
+       }
+    } else if (output_type == OTYPE_OBJ) {
+       char *cp;
+
+       bu_vls_trunc(&obj_materials_file, 0);
+
+       cp = strrchr(output_file, '.');
+       if (!cp)
+           bu_vls_printf(&obj_materials_file, "%s.mtl", output_file);
+       else {
+           /* ignore everything after the last '.' */
+           *cp = '\0';
+           bu_vls_printf(&obj_materials_file, "%s.mtl", output_file);
+           *cp = '.';
+       }
+
+       BU_LIST_INIT(&HeadObjMaterials);
+
+       if ((obj_materials_fp=fopen(bu_vls_addr(&obj_materials_file), "wb+")) 
== NULL) {
+           bu_vls_printf(gedp->ged_result_str, "%s: failed to open %s\n", 
cmd_name, bu_vls_addr(&obj_materials_file));
+           bu_vls_free(&obj_materials_file);
+           fclose(fp);
+           return GED_ERROR;
+       }
+
+       num_obj_materials = 0;
+
+       fprintf(fp, "mtllib %s\n", bu_vls_addr(&obj_materials_file));
+    }
+
+    dl_botdump(gedp->ged_gdp->gd_headDisplay, gedp->ged_wdbp->dbip, fp, fd, 
file_ext, output_type, &curr_obj_red, &curr_obj_green, &curr_obj_blue, 
&curr_obj_alpha);
+
+    data_dump(gedp, fp);
+
+    if (output_file) {
+       if (binary && output_type == OTYPE_STL) {
+           unsigned char tot_buffer[4];
+
+           /* Re-position pointer to 80th byte */
+           bu_lseek(fd, 80, SEEK_SET);
+
+           /* Write out number of triangles */
+           *(uint32_t *)tot_buffer = htonl((unsigned long)total_faces);
+           lswap((unsigned int *)tot_buffer);
+           ret = write(fd, tot_buffer, 4);
+           if (ret < 0) {
+               perror("write");
+           }
+
+           close(fd);
+       } else {
+           /* end of layers section, start of ENTITIES SECTION */
+           switch (output_type) {
+               case OTYPE_DXF:
+                   fprintf(fp, "0\nENDSEC\n0\nEOF\n");
+                   break;
+               case OTYPE_SAT:
+                   fprintf(fp, "End-of-ACIS-data\n");
+                   break;
+               default:
+                   break;
+           }
+
+           fclose(fp);
+       }
+    }
+
+    if (output_type == OTYPE_OBJ) {
+       bu_vls_free(&obj_materials_file);
+       obj_free_materials();
+       fclose(obj_materials_fp);
+    }
+
+    return GED_OK;
+}
+
+/*
+ * Local Variables:
+ * mode: C
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * c-file-style: "stroustrup"
+ * End:
+ * ex: shiftwidth=4 tabstop=8
+ */

Copied: brlcad/branches/gedplugins/src/libged/bot/bot_face_fuse.c (from rev 
76395, brlcad/branches/gedplugins/src/libged/bot_face_fuse/bot_face_fuse.c)
===================================================================
--- brlcad/branches/gedplugins/src/libged/bot/bot_face_fuse.c                   
        (rev 0)
+++ brlcad/branches/gedplugins/src/libged/bot/bot_face_fuse.c   2020-07-21 
19:08:41 UTC (rev 76396)
@@ -0,0 +1,90 @@
+/*                    B O T _ F A C E _ F U S E . C
+ * BRL-CAD
+ *
+ * Copyright (c) 2008-2020 United States Government as represented by
+ * the U.S. Army Research Laboratory.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this file; see the file named COPYING for more
+ * information.
+ */
+/** @file libged/bot_face_fuse.c
+ *
+ * The bot_face_fuse command.
+ *
+ */
+
+#include "common.h"
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "rt/geom.h"
+
+#include "../ged_private.h"
+
+
+int
+ged_bot_face_fuse(struct ged *gedp, int argc, const char *argv[])
+{
+    struct directory *old_dp, *new_dp;
+    struct rt_db_internal intern;
+    struct rt_bot_internal *bot;
+    static const char *usage = "new_bot old_bot";
+
+    GED_CHECK_DATABASE_OPEN(gedp, GED_ERROR);
+    GED_CHECK_READ_ONLY(gedp, GED_ERROR);
+    GED_CHECK_ARGC_GT_0(gedp, argc, GED_ERROR);
+
+    /* initialize result */
+    bu_vls_trunc(gedp->ged_result_str, 0);
+
+    /* must be wanting help */
+    if (argc == 1) {
+       bu_vls_printf(gedp->ged_result_str, "Usage: %s %s", argv[0], usage);
+       return GED_HELP;
+    }
+
+    if (argc != 3) {
+       bu_vls_printf(gedp->ged_result_str, "Usage: %s %s", argv[0], usage);
+       return GED_ERROR;
+    }
+
+    GED_DB_LOOKUP(gedp, old_dp, argv[2], LOOKUP_NOISY, GED_ERROR & GED_QUIET);
+    GED_DB_GET_INTERNAL(gedp, &intern, old_dp, bn_mat_identity, 
&rt_uniresource, GED_ERROR);
+
+    if (intern.idb_type != ID_BOT) {
+       bu_vls_printf(gedp->ged_result_str, "%s: %s is not a BOT solid!\n", 
argv[0], argv[2]);
+       return GED_ERROR;
+    }
+
+    bot = (struct rt_bot_internal *)intern.idb_ptr;
+    RT_BOT_CK_MAGIC(bot);
+
+    (void) rt_bot_face_fuse(bot);
+
+    GED_DB_DIRADD(gedp, new_dp, argv[1], RT_DIR_PHONY_ADDR, 0, RT_DIR_SOLID, 
(void *)&intern.idb_type, GED_ERROR);
+    GED_DB_PUT_INTERNAL(gedp, new_dp, &intern, &rt_uniresource, GED_ERROR);
+
+    return GED_OK;
+}
+
+/*
+ * Local Variables:
+ * mode: C
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * c-file-style: "stroustrup"
+ * End:
+ * ex: shiftwidth=4 tabstop=8
+ */

Copied: brlcad/branches/gedplugins/src/libged/bot/bot_face_sort.c (from rev 
76395, brlcad/branches/gedplugins/src/libged/bot_face_sort/bot_face_sort.c)
===================================================================
--- brlcad/branches/gedplugins/src/libged/bot/bot_face_sort.c                   
        (rev 0)
+++ brlcad/branches/gedplugins/src/libged/bot/bot_face_sort.c   2020-07-21 
19:08:41 UTC (rev 76396)
@@ -0,0 +1,116 @@
+/*                   B O T _ F A C E _ S O R T . C
+ * BRL-CAD
+ *
+ * Copyright (c) 2008-2020 United States Government as represented by
+ * the U.S. Army Research Laboratory.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this file; see the file named COPYING for more
+ * information.
+ */
+/** @file libged/bot_face_sort.c
+ *
+ * The bot_face_sort command.
+ *
+ */
+
+#include "common.h"
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "rt/geom.h"
+#include "ged.h"
+
+
+int
+ged_bot_face_sort(struct ged *gedp, int argc, const char *argv[])
+{
+    int i;
+    int tris_per_piece=0;
+    static const char *usage = "triangles_per_piece bot_solid1 [bot_solid2 
bot_solid3 ...]";
+
+    GED_CHECK_DATABASE_OPEN(gedp, GED_ERROR);
+    GED_CHECK_READ_ONLY(gedp, GED_ERROR);
+    GED_CHECK_ARGC_GT_0(gedp, argc, GED_ERROR);
+
+    /* initialize result */
+    bu_vls_trunc(gedp->ged_result_str, 0);
+
+    /* must be wanting help */
+    if (argc == 1) {
+       bu_vls_printf(gedp->ged_result_str, "Usage: %s %s", argv[0], usage);
+       return GED_HELP;
+    }
+
+    if (argc < 3) {
+       bu_vls_printf(gedp->ged_result_str, "Usage: %s %s", argv[0], usage);
+       return GED_ERROR;
+    }
+
+    tris_per_piece = atoi(argv[1]);
+    if (tris_per_piece < 1) {
+       bu_vls_printf(gedp->ged_result_str,
+                     "Illegal value for triangle per piece (%s)\n",
+                     argv[1]);
+       bu_vls_printf(gedp->ged_result_str, "Usage: %s %s", argv[0], usage);
+       return GED_ERROR;
+    }
+
+    for (i = 2; i < argc; i++) {
+       struct directory *dp;
+       struct rt_db_internal intern;
+       struct rt_bot_internal *bot;
+
+       if ((dp=db_lookup(gedp->ged_wdbp->dbip, argv[i], LOOKUP_NOISY)) == 
RT_DIR_NULL) {
+           continue;
+       }
+
+       GED_DB_GET_INTERNAL(gedp, &intern, dp, bn_mat_identity, 
gedp->ged_wdbp->wdb_resp, GED_ERROR);
+
+       if (intern.idb_major_type != DB5_MAJORTYPE_BRLCAD || 
intern.idb_minor_type != DB5_MINORTYPE_BRLCAD_BOT) {
+           rt_db_free_internal(&intern);
+           bu_vls_printf(gedp->ged_result_str,
+                         "%s is not a BOT primitive, skipped\n",
+                         dp->d_namep);
+           continue;
+       }
+
+       bot = (struct rt_bot_internal *)intern.idb_ptr;
+       RT_BOT_CK_MAGIC(bot);
+
+       bu_log("processing %s (%zu triangles)\n", dp->d_namep, bot->num_faces);
+
+       if (rt_bot_sort_faces(bot, tris_per_piece)) {
+           rt_db_free_internal(&intern);
+           bu_vls_printf(gedp->ged_result_str,
+                         "Face sort failed for %s, this BOT not sorted\n",
+                         dp->d_namep);
+           continue;
+       }
+
+       GED_DB_PUT_INTERNAL(gedp, dp, &intern, gedp->ged_wdbp->wdb_resp, 
GED_ERROR);
+    }
+
+    return GED_OK;
+}
+
+/*
+ * Local Variables:
+ * mode: C
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * c-file-style: "stroustrup"
+ * End:
+ * ex: shiftwidth=4 tabstop=8
+ */

Copied: brlcad/branches/gedplugins/src/libged/bot/bot_flip.c (from rev 76395, 
brlcad/branches/gedplugins/src/libged/bot_flip/bot_flip.c)
===================================================================
--- brlcad/branches/gedplugins/src/libged/bot/bot_flip.c                        
        (rev 0)
+++ brlcad/branches/gedplugins/src/libged/bot/bot_flip.c        2020-07-21 
19:08:41 UTC (rev 76396)
@@ -0,0 +1,99 @@
+/*                         B O T _ F L I P . C
+ * BRL-CAD
+ *
+ * Copyright (c) 2008-2020 United States Government as represented by
+ * the U.S. Army Research Laboratory.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this file; see the file named COPYING for more
+ * information.
+ */
+/** @file libged/bot_flip.c
+ *
+ * The bot_flip command.
+ *
+ */
+
+#include "common.h"
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "bu/path.h"
+#include "rt/geom.h"
+
+#include "../ged_private.h"
+
+
+int
+ged_bot_flip(struct ged *gedp, int argc, const char *argv[])
+{
+    int i;
+    struct directory *dp;
+    struct rt_db_internal intern;
+    struct rt_bot_internal *bot;
+    static const char *usage = "bot [bot2 bot3 ...]";
+
+    GED_CHECK_DATABASE_OPEN(gedp, GED_ERROR);
+    GED_CHECK_READ_ONLY(gedp, GED_ERROR);
+    GED_CHECK_ARGC_GT_0(gedp, argc, GED_ERROR);
+
+    /* initialize result */
+    bu_vls_trunc(gedp->ged_result_str, 0);
+
+    /* must be wanting help */
+    if (argc == 1) {
+       bu_vls_printf(gedp->ged_result_str, "Usage: %s %s", argv[0], usage);
+       return GED_HELP;
+    }
+
+    for (i = 1; i < argc; ++i) {
+       /* Skip past any path elements */
+       char *obj = (char *)bu_calloc(strlen(argv[i]), sizeof(char), 
"ged_bot_flip obj");
+       bu_path_basename(argv[i], obj);
+
+       if (BU_STR_EQUAL(obj, ".")) {
+           /* malformed path, lookup using exactly what was provided */
+           bu_free(obj, "free bu_path_basename");
+           obj = bu_strdup(argv[i]);
+       }
+
+       if ((dp = db_lookup(gedp->ged_wdbp->dbip, obj, LOOKUP_QUIET)) == 
RT_DIR_NULL) {
+           bu_vls_printf(gedp->ged_result_str, "%s: db_lookup(%s) error\n", 
argv[0], obj);
+       } else {
+           GED_DB_GET_INTERNAL(gedp, &intern, dp, bn_mat_identity, 
&rt_uniresource, GED_ERROR);
+
+           if (intern.idb_major_type != DB5_MAJORTYPE_BRLCAD || 
intern.idb_minor_type != DB5_MINORTYPE_BRLCAD_BOT) {
+               bu_vls_printf(gedp->ged_result_str, "%s: %s is not a BOT 
solid!\n", argv[0], obj);
+           } else {
+               bot = (struct rt_bot_internal *)intern.idb_ptr;
+               rt_bot_flip(bot);
+
+               GED_DB_PUT_INTERNAL(gedp, dp, &intern, 
gedp->ged_wdbp->wdb_resp, GED_ERROR);
+           }
+       }
+       bu_free(obj, "free obj");
+    }
+
+    return GED_OK;
+}
+
+/*
+ * Local Variables:
+ * mode: C
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * c-file-style: "stroustrup"
+ * End:
+ * ex: shiftwidth=4 tabstop=8
+ */

Copied: brlcad/branches/gedplugins/src/libged/bot/bot_fuse.c (from rev 76395, 
brlcad/branches/gedplugins/src/libged/bot_fuse/bot_fuse.c)
===================================================================
--- brlcad/branches/gedplugins/src/libged/bot/bot_fuse.c                        
        (rev 0)
+++ brlcad/branches/gedplugins/src/libged/bot/bot_fuse.c        2020-07-21 
19:08:41 UTC (rev 76396)
@@ -0,0 +1,323 @@
+/*                         B O T _ F U S E . C
+ * BRL-CAD
+ *
+ * Copyright (c) 2008-2020 United States Government as represented by
+ * the U.S. Army Research Laboratory.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this file; see the file named COPYING for more
+ * information.
+ */
+/** @file libged/bot_fuse.c
+ *
+ * The bot_fuse command.
+ *
+ */
+
+#include "common.h"
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "bu/getopt.h"
+#include "bu/parallel.h"
+#include "rt/geom.h"
+#include "bn/plot3.h"
+
+#include "../ged_private.h"
+
+
+static size_t
+show_dangling_edges(struct ged *gedp, const uint32_t *magic_p, const char 
*name, int out_type)
+{
+    FILE *plotfp = NULL;
+    const char *manifolds = NULL;
+    const struct edgeuse *eur;
+    int done;
+    point_t pt1, pt2;
+    size_t i, cnt;
+    struct bn_vlblock *vbp = NULL;
+    struct bu_list *vhead = NULL;
+    struct bu_ptbl faces;
+    struct bu_vls plot_file_name = BU_VLS_INIT_ZERO;
+    struct edgeuse *eu = NULL;
+    struct face *fp = NULL;
+    struct faceuse *fu, *fu1, *fu2;
+    struct faceuse *newfu = NULL;
+    struct loopuse *lu = NULL;
+
+    /* out_type: 0 = none, 1 = show, 2 = plot */
+    if (out_type < 0 || out_type > 2) {
+       bu_log("Internal error, open edge test failed.\n");
+       return 0;
+    }
+
+    if (out_type == 1) {
+       vbp = rt_vlblock_init();
+       vhead = bn_vlblock_find(vbp, 0xFF, 0xFF, 0x00);
+    }
+
+    bu_ptbl_init(&faces, 64, "faces buffer");
+    nmg_face_tabulate(&faces, magic_p, &RTG.rtg_vlfree);
+
+    cnt = 0;
+    for (i = 0; i < (size_t)BU_PTBL_LEN(&faces) ; i++) {
+       fp = (struct face *)BU_PTBL_GET(&faces, i);
+       NMG_CK_FACE(fp);
+       fu = fu1 = fp->fu_p;
+       NMG_CK_FACEUSE(fu1);
+       fu2 = fp->fu_p->fumate_p;
+       NMG_CK_FACEUSE(fu2);
+       done = 0;
+       while (!done) {
+           NMG_CK_FACEUSE(fu);
+           for (BU_LIST_FOR(lu, loopuse, &fu->lu_hd)) {
+               NMG_CK_LOOPUSE(lu);
+               if (BU_LIST_FIRST_MAGIC(&lu->down_hd) == NMG_EDGEUSE_MAGIC) {
+                   for (BU_LIST_FOR(eu, edgeuse, &lu->down_hd)) {
+                       NMG_CK_EDGEUSE(eu);
+                       eur = nmg_radial_face_edge_in_shell(eu);
+                       newfu = eur->up.lu_p->up.fu_p;
+                       while (manifolds &&
+                              NMG_MANIFOLDS(manifolds, newfu) &
+                              NMG_2MANIFOLD &&
+                              eur != eu->eumate_p) {
+                           eur = nmg_radial_face_edge_in_shell(eur->eumate_p);
+                           newfu = eur->up.lu_p->up.fu_p;
+                       }
+                       if (eur == eu->eumate_p) {
+                           VMOVE(pt1, eu->vu_p->v_p->vg_p->coord);
+                           VMOVE(pt2, eu->eumate_p->vu_p->v_p->vg_p->coord);
+                           if (out_type == 1) {
+                               BN_ADD_VLIST(vbp->free_vlist_hd, vhead, pt1, 
BN_VLIST_LINE_MOVE);
+                               BN_ADD_VLIST(vbp->free_vlist_hd, vhead, pt2, 
BN_VLIST_LINE_DRAW);
+                           } else if (out_type == 2) {
+                               if (!plotfp) {
+                                   bu_vls_sprintf(&plot_file_name, "%s.%p.pl", 
name, (void *)magic_p);
+                                   if ((plotfp = 
fopen(bu_vls_addr(&plot_file_name), "wb")) == (FILE *)NULL) {
+                                       bu_vls_free(&plot_file_name);
+                                       bu_log("Error, unable to create plot 
file (%s), open edge test failed.\n",
+                                              bu_vls_addr(&plot_file_name));
+                                       return 0;
+                                   }
+                               }
+                               pdv_3line(plotfp, pt1, pt2);
+                           }
+                           cnt++;
+                       }
+                   }
+               }
+           }
+           if (fu == fu1) fu = fu2;
+           if (fu == fu2) done = 1;
+       };
+
+    }
+
+    if (out_type == 1) {
+       /* Add overlay */
+       _ged_cvt_vlblock_to_solids(gedp, vbp, name, 0);
+       bn_vlblock_free(vbp);
+       bu_log("Showing open edges...\n");
+    } else if (out_type == 2) {
+       if (plotfp) {
+           (void)fclose(plotfp);
+           bu_log("Wrote plot file (%s)\n", bu_vls_addr(&plot_file_name));
+           bu_vls_free(&plot_file_name);
+       }
+    }
+    bu_ptbl_free(&faces);
+
+    return cnt;
+}
+
+
+int
+ged_bot_fuse(struct ged *gedp, int argc, const char **argv)
+{
+    struct directory *old_dp, *new_dp;
+    struct rt_db_internal intern, intern2;
+    struct rt_bot_internal *bot;
+    int count=0;
+    static const char *usage = "new_bot old_bot";
+
+    struct model *m;
+    struct nmgregion *r;
+    int ret, c, i;
+    struct bn_tol *tol = &gedp->ged_wdbp->wdb_tol;
+    int total = 0;
+    volatile int out_type = 0; /* open edge output type: 0 = none, 1 = show, 2 
= plot */
+    size_t open_cnt;
+    struct bu_vls name_prefix = BU_VLS_INIT_ZERO;
+
+    /* bu_getopt() options */
+    static const char *bot_fuse_options = "sp";
+    static const char *bot_fuse_options_str = "[-s|-p]";
+
+    GED_CHECK_DATABASE_OPEN(gedp, GED_ERROR);
+    GED_CHECK_READ_ONLY(gedp, GED_ERROR);
+    GED_CHECK_ARGC_GT_0(gedp, argc, GED_ERROR);
+
+    /* initialize result */
+    bu_vls_trunc(gedp->ged_result_str, 0);
+
+    /* must be wanting help */
+    if (argc != 3 && argc != 4) {
+       bu_vls_printf(gedp->ged_result_str, "Usage: %s %s %s", argv[0], 
bot_fuse_options_str, usage);
+       return GED_HELP;
+    }
+
+    /* Turn off getopt's error messages */
+    bu_opterr = 0;
+    bu_optind = 1;
+
+    /* get all the option flags from the command line */
+    while ((c=bu_getopt(argc, (char **)argv, bot_fuse_options)) != -1) {
+       switch (c) {
+           case 's':
+               {
+                   out_type = 1; /* show open edges */
+                   break;
+               }
+           case 'p':
+               {
+                   out_type = 2; /* plot open edges */
+                   break;
+               }
+           default :
+               {
+                   bu_vls_printf(gedp->ged_result_str, "Unknown option: '%c'", 
c);
+                   return GED_HELP;
+               }
+       }
+    }
+
+    i = argc - 2;
+
+    bu_log("%s: start\n", argv[0]);
+
+    GED_DB_LOOKUP(gedp, old_dp, argv[i+1], LOOKUP_NOISY, GED_ERROR & 
GED_QUIET);
+    GED_DB_GET_INTERNAL(gedp, &intern, old_dp, bn_mat_identity, 
&rt_uniresource, GED_ERROR);
+
+    if (intern.idb_major_type != DB5_MAJORTYPE_BRLCAD || intern.idb_minor_type 
!= DB5_MINORTYPE_BRLCAD_BOT) {
+       bu_vls_printf(gedp->ged_result_str, "%s: %s is not a BOT solid!\n", 
argv[0], argv[i+1]);
+       return GED_ERROR;
+    }
+
+    /* create nmg model structure */
+    m = nmg_mm();
+
+    /* place bot in nmg structure */
+    bu_log("%s: running rt_bot_tess\n", argv[0]);
+    ret = rt_bot_tess(&r, m, &intern, &gedp->ged_wdbp->wdb_ttol, tol);
+
+    /* free internal representation of original bot */
+    rt_db_free_internal(&intern);
+
+    if (ret != 0) {
+       bu_vls_printf(gedp->ged_result_str, "%s: %s fuse failed (1).\n", 
argv[0], argv[i+1]);
+       nmg_km(m);
+       return GED_ERROR;
+    }
+
+    total = 0;
+
+    /* Step 1 -- the vertices. */
+    bu_log("%s: running nmg_vertex_fuse\n", argv[0]);
+    count = nmg_vertex_fuse(&m->magic, &RTG.rtg_vlfree, tol);
+    total += count;
+    bu_log("%s: %s, %d vertex fused\n", argv[0], argv[i+1], count);
+
+    /* Step 1.5 -- break edges on vertices, before fusing edges */
+    bu_log("%s: running nmg_break_e_on_v\n", argv[0]);
+    count = nmg_break_e_on_v(&m->magic, &RTG.rtg_vlfree, tol);
+    total += count;
+    bu_log("%s: %s, %d broke 'e' on 'v'\n", argv[0], argv[i+1], count);
+
+    if (total) {
+       struct nmgregion *r2;
+       struct shell *s;
+
+       bu_log("%s: running nmg_make_faces_within_tol\n", argv[0]);
+
+       /* vertices and/or edges have been moved,
+        * may have created out-of-tolerance faces
+        */
+
+       for (BU_LIST_FOR(r2, nmgregion, &m->r_hd)) {
+           for (BU_LIST_FOR(s, shell, &r2->s_hd))
+               nmg_make_faces_within_tol(s, &RTG.rtg_vlfree, tol);
+       }
+    }
+
+    /* Step 2 -- the face geometry */
+    bu_log("%s: running nmg_model_face_fuse\n", argv[0]);
+    count = nmg_model_face_fuse(m, &RTG.rtg_vlfree, tol);
+    total += count;
+    bu_log("%s: %s, %d faces fused\n", argv[0], argv[i+1], count);
+
+    /* Step 3 -- edges */
+    bu_log("%s: running nmg_edge_fuse\n", argv[0]);
+    count = nmg_edge_fuse(&m->magic, &RTG.rtg_vlfree, tol);
+    total += count;
+
+    bu_log("%s: %s, %d edges fused\n", argv[0], argv[i+1], count);
+
+    bu_log("%s: %s, %d total fused\n", argv[0], argv[i+1], total);
+
+    if (!BU_SETJUMP) {
+       /* try */
+
+       /* convert the nmg model back into a bot */
+       bot = nmg_bot(BU_LIST_FIRST(shell, &r->s_hd), &RTG.rtg_vlfree, tol);
+
+       bu_vls_sprintf(&name_prefix, "open_edges.%s", argv[i]);
+       bu_log("%s: running show_dangling_edges\n", argv[0]);
+       open_cnt = show_dangling_edges(gedp, &m->magic, 
bu_vls_addr(&name_prefix), out_type);
+       bu_log("%s: WARNING %ld open edges, new BOT may be invalid!!!\n", 
argv[0], open_cnt);
+       bu_vls_free(&name_prefix);
+
+       /* free the nmg model structure */
+       nmg_km(m);
+    } else {
+       /* catch */
+       BU_UNSETJUMP;
+       bu_vls_printf(gedp->ged_result_str, "%s: %s fuse failed (2).\n", 
argv[0], argv[i+1]);
+       return GED_ERROR;
+    } BU_UNSETJUMP;
+
+    RT_DB_INTERNAL_INIT(&intern2);
+    intern2.idb_major_type = DB5_MAJORTYPE_BRLCAD;
+    intern2.idb_type = ID_BOT;
+    intern2.idb_meth = &OBJ[ID_BOT];
+    intern2.idb_ptr = (void *)bot;
+
+    GED_DB_DIRADD(gedp, new_dp, argv[i], RT_DIR_PHONY_ADDR, 0, RT_DIR_SOLID, 
(void *)&intern2.idb_type, GED_ERROR);
+    GED_DB_PUT_INTERNAL(gedp, new_dp, &intern2, &rt_uniresource, GED_ERROR);
+
+    bu_log("%s: Created new BOT (%s)\n", argv[0], argv[i]);
+    bu_log("%s: Done.\n", argv[0]);
+
+    return GED_OK;
+}
+
+
+/*
+ * Local Variables:
+ * mode: C
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * c-file-style: "stroustrup"
+ * End:
+ * ex: shiftwidth=4 tabstop=8
+ */

Copied: brlcad/branches/gedplugins/src/libged/bot/bot_merge.c (from rev 76395, 
brlcad/branches/gedplugins/src/libged/bot_merge/bot_merge.c)
===================================================================
--- brlcad/branches/gedplugins/src/libged/bot/bot_merge.c                       
        (rev 0)
+++ brlcad/branches/gedplugins/src/libged/bot/bot_merge.c       2020-07-21 
19:08:41 UTC (rev 76396)
@@ -0,0 +1,125 @@
+/*                         B O T _ M E R G E . C
+ * BRL-CAD
+ *
+ * Copyright (c) 2008-2020 United States Government as represented by
+ * the U.S. Army Research Laboratory.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this file; see the file named COPYING for more
+ * information.
+ */
+/** @file libged/bot_merge.c
+ *
+ * The bot_merge command.
+ *
+ */
+
+#include "common.h"
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "rt/geom.h"
+
+#include "../ged_private.h"
+
+
+int
+ged_bot_merge(struct ged *gedp, int argc, const char *argv[])
+{
+    struct directory *dp, *new_dp;
+    struct rt_db_internal intern;
+    struct rt_bot_internal **bots;
+    int i, idx;
+    static const char *usage = "bot_dest bot1_src [botn_src]";
+
+    GED_CHECK_DATABASE_OPEN(gedp, GED_ERROR);
+    GED_CHECK_READ_ONLY(gedp, GED_ERROR);
+    GED_CHECK_ARGC_GT_0(gedp, argc, GED_ERROR);
+
+    /* initialize result */
+    bu_vls_trunc(gedp->ged_result_str, 0);
+
+    /* must be wanting help */
+    if (argc == 1) {
+       bu_vls_printf(gedp->ged_result_str, "Usage: %s %s", argv[0], usage);
+       return GED_HELP;
+    }
+
+    bots = (struct rt_bot_internal **)bu_calloc(argc - 1, sizeof(struct 
rt_bot_internal *), "bot internal");
+
+    /* read in all the bots */
+    for (idx = 0, i = 2; i < argc; ++i) {
+       if ((dp = db_lookup(gedp->ged_wdbp->dbip, argv[i], LOOKUP_NOISY)) == 
RT_DIR_NULL) {
+           continue;
+       }
+
+       GED_DB_GET_INTERNAL(gedp, &intern, dp, bn_mat_identity, 
&rt_uniresource, GED_ERROR);
+
+       if (intern.idb_major_type != DB5_MAJORTYPE_BRLCAD || 
intern.idb_minor_type != DB5_MINORTYPE_BRLCAD_BOT) {
+           bu_vls_printf(gedp->ged_result_str, "%s: %s is not a BOT solid!  
Skipping.\n", argv[0], argv[i]);
+           rt_db_free_internal(&intern);
+           continue;
+       }
+
+       bots[idx] = (struct rt_bot_internal *)intern.idb_ptr;
+
+       intern.idb_ptr = (void *)0;
+       rt_db_free_internal(&intern);
+
+       RT_BOT_CK_MAGIC(bots[idx]);
+       idx++;
+    }
+
+    if (idx == 0) {
+       bu_vls_printf(gedp->ged_result_str, "%s: No BOT solids given.\n", 
argv[0]);
+       bu_free(bots, "bots");
+       return GED_ERROR;
+    }
+
+    RT_DB_INTERNAL_INIT(&intern);
+    intern.idb_type = ID_BOT;
+    intern.idb_major_type = DB5_MAJORTYPE_BRLCAD;
+    intern.idb_minor_type = DB5_MINORTYPE_BRLCAD_BOT;
+    intern.idb_meth = &OBJ[ID_BOT];
+    intern.idb_ptr = rt_bot_merge(idx, (const struct rt_bot_internal * const 
*)(bots));
+
+    GED_DB_DIRADD(gedp, new_dp, argv[1], RT_DIR_PHONY_ADDR, 0, RT_DIR_SOLID, 
(void *)&intern.idb_type, GED_ERROR);
+    GED_DB_PUT_INTERNAL(gedp, new_dp, &intern, &rt_uniresource, GED_ERROR);
+
+    for (i = 0; i < idx; ++i) {
+       /* fill in an rt_db_internal so we can free it */
+       struct rt_db_internal internal;
+       RT_DB_INTERNAL_INIT(&internal);
+       internal.idb_major_type = DB5_MAJORTYPE_BRLCAD;
+       internal.idb_minor_type = ID_BOT;
+       internal.idb_meth = &OBJ[ID_BOT];
+       internal.idb_ptr = bots[i];
+
+       rt_db_free_internal(&internal);
+    }
+
+    bu_free(bots, "bots");
+
+    return GED_OK;
+}
+
+/*
+ * Local Variables:
+ * mode: C
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * c-file-style: "stroustrup"
+ * End:
+ * ex: shiftwidth=4 tabstop=8
+ */

Copied: brlcad/branches/gedplugins/src/libged/bot/bot_smooth.c (from rev 76395, 
brlcad/branches/gedplugins/src/libged/bot_smooth/bot_smooth.c)
===================================================================
--- brlcad/branches/gedplugins/src/libged/bot/bot_smooth.c                      
        (rev 0)
+++ brlcad/branches/gedplugins/src/libged/bot/bot_smooth.c      2020-07-21 
19:08:41 UTC (rev 76396)
@@ -0,0 +1,137 @@
+/*                         B O T _ S M O O T H . C
+ * BRL-CAD
+ *
+ * Copyright (c) 2008-2020 United States Government as represented by
+ * the U.S. Army Research Laboratory.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this file; see the file named COPYING for more
+ * information.
+ */
+/** @file libged/bot_smooth.c
+ *
+ * The bot_smooth command.
+ *
+ */
+
+#include "common.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "bu/cmd.h"
+#include "rt/geom.h"
+
+#include "../ged_private.h"
+
+
+int
+ged_bot_smooth(struct ged *gedp, int argc, const char *argv[])
+{
+    char *new_bot_name, *old_bot_name;
+    struct directory *dp_old, *dp_new;
+    struct rt_bot_internal *old_bot;
+    struct rt_db_internal intern;
+    fastf_t tolerance_angle=180.0;
+    int arg_index=1;
+    static const char *usage = "[-t angle_tolerance] new_bot old_bot";
+
+    GED_CHECK_DATABASE_OPEN(gedp, GED_ERROR);
+    GED_CHECK_READ_ONLY(gedp, GED_ERROR);
+    GED_CHECK_ARGC_GT_0(gedp, argc, GED_ERROR);
+
+    dp_old = dp_new = RT_DIR_NULL;
+
+    /* initialize result */
+    bu_vls_trunc(gedp->ged_result_str, 0);
+
+    /* must be wanting help */
+    if (argc == 1) {
+       bu_vls_printf(gedp->ged_result_str, "Usage: %s %s", argv[0], usage);
+       return GED_HELP;
+    }
+
+    /* check that we are using a version 5 database */
+    if (db_version(gedp->ged_wdbp->dbip) < 5) {
+       bu_vls_printf(gedp->ged_result_str, "This is an older database 
version.\nIt does not support BOT surface normals.\nUse \"dbupgrade\" to 
upgrade this database to the current version.\n");
+       return GED_ERROR;
+    }
+
+    if (argc < 3) {
+       bu_vls_printf(gedp->ged_result_str, "Usage: %s %s", argv[0], usage);
+       return GED_ERROR;
+    }
+
+    while (*argv[arg_index] == '-') {
+       /* this is an option */
+       if (BU_STR_EQUAL(argv[arg_index], "-t")) {
+           arg_index++;
+           tolerance_angle = atof(argv[arg_index]);
+       } else {
+           bu_vls_printf(gedp->ged_result_str, "Usage: %s %s", argv[0], usage);
+           return GED_ERROR;
+       }
+       arg_index++;
+    }
+
+    if (arg_index >= argc) {
+       bu_vls_printf(gedp->ged_result_str, "Usage: %s %s", argv[0], usage);
+       return GED_ERROR;
+    }
+
+    new_bot_name = (char *)argv[arg_index++];
+    old_bot_name = (char *)argv[arg_index];
+
+    GED_DB_LOOKUP(gedp, dp_old, old_bot_name, LOOKUP_QUIET, GED_ERROR);
+
+    if (!BU_STR_EQUAL(old_bot_name, new_bot_name)) {
+       GED_CHECK_EXISTS(gedp, new_bot_name, LOOKUP_QUIET, GED_ERROR);
+    } else {
+       dp_new = dp_old;
+    }
+
+    GED_DB_GET_INTERNAL(gedp, &intern, dp_old, NULL, gedp->ged_wdbp->wdb_resp, 
GED_ERROR);
+
+    if (intern.idb_major_type != DB5_MAJORTYPE_BRLCAD || intern.idb_minor_type 
!= DB5_MINORTYPE_BRLCAD_BOT) {
+       bu_vls_printf(gedp->ged_result_str, "%s is not a BOT primitive\n", 
old_bot_name);
+       rt_db_free_internal(&intern);
+       return GED_ERROR;
+    }
+
+    old_bot = (struct rt_bot_internal *)intern.idb_ptr;
+    RT_BOT_CK_MAGIC(old_bot);
+
+    if (rt_bot_smooth(old_bot, old_bot_name, gedp->ged_wdbp->dbip, 
tolerance_angle*DEG2RAD)) {
+       bu_vls_printf(gedp->ged_result_str, "Failed to smooth %s\n", 
old_bot_name);
+       rt_db_free_internal(&intern);
+       return GED_ERROR;
+    }
+
+    if (dp_new == RT_DIR_NULL) {
+       GED_DB_DIRADD(gedp, dp_new, new_bot_name, RT_DIR_PHONY_ADDR, 0, 
RT_DIR_SOLID, (void *)&intern.idb_type, GED_ERROR);
+    }
+
+    GED_DB_PUT_INTERNAL(gedp, dp_new, &intern, gedp->ged_wdbp->wdb_resp, 
GED_ERROR);
+    rt_db_free_internal(&intern);
+
+    return GED_OK;
+}
+
+/*
+ * Local Variables:
+ * mode: C
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * c-file-style: "stroustrup"
+ * End:
+ * ex: shiftwidth=4 tabstop=8
+ */

Copied: brlcad/branches/gedplugins/src/libged/bot/bot_split.c (from rev 76395, 
brlcad/branches/gedplugins/src/libged/bot_split/bot_split.c)
===================================================================
--- brlcad/branches/gedplugins/src/libged/bot/bot_split.c                       
        (rev 0)
+++ brlcad/branches/gedplugins/src/libged/bot/bot_split.c       2020-07-21 
19:08:41 UTC (rev 76396)
@@ -0,0 +1,168 @@
+/*                         B O T _ S P L I T . C
+ * BRL-CAD
+ *
+ * Copyright (c) 2008-2020 United States Government as represented by
+ * the U.S. Army Research Laboratory.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this file; see the file named COPYING for more
+ * information.
+ */
+/** @file libged/bot_split.c
+ *
+ * The bot_split command.
+ *
+ */
+
+#include "common.h"
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "bu/path.h"
+#include "rt/geom.h"   /* for rt_bot_split (in raytrace.h) */
+#include "../ged_private.h"
+
+
+int
+ged_bot_split(struct ged *gedp, int argc, const char *argv[])
+{
+    static const char *usage = "bot [bot2 bot3 ...]";
+
+    int i;
+    struct bu_vls bot_result_list = BU_VLS_INIT_ZERO;
+    struct bu_vls error_str = BU_VLS_INIT_ZERO;
+    struct bu_vls new_bots = BU_VLS_INIT_ZERO;
+    struct directory *dp;
+    struct rt_bot_internal *bot;
+    struct rt_db_internal intern;
+
+    GED_CHECK_DATABASE_OPEN(gedp, GED_ERROR);
+    GED_CHECK_READ_ONLY(gedp, GED_ERROR);
+    GED_CHECK_ARGC_GT_0(gedp, argc, GED_ERROR);
+
+    /* initialize result */
+    bu_vls_trunc(gedp->ged_result_str, 0);
+
+    /* must be wanting help */
+    if (argc == 1) {
+       bu_vls_printf(gedp->ged_result_str, "Usage: %s %s", argv[0], usage);
+       return GED_HELP;
+    }
+
+    for (i = 1; i < argc; ++i) {
+       struct rt_bot_list *headRblp = NULL;
+       /* Skip past any path elements */
+       char *obj = bu_path_basename(argv[i], NULL);
+
+       if (BU_STR_EQUAL(obj, ".")) {
+           /* malformed path, lookup using exactly what was provided */
+           bu_free(obj, "free bu_path_basename");
+           obj = bu_strdup(argv[i]);
+       }
+
+       if ((dp = db_lookup(gedp->ged_wdbp->dbip, obj, LOOKUP_QUIET)) == 
RT_DIR_NULL) {
+           bu_vls_printf(&error_str, "%s: db_lookup(%s) error\n", argv[0], 
obj);
+           bu_free(obj, "free obj");
+           continue;
+       }
+
+       GED_DB_GET_INTERNAL(gedp, &intern, dp, bn_mat_identity, 
&rt_uniresource, GED_ERROR);
+
+       if (intern.idb_major_type != DB5_MAJORTYPE_BRLCAD || 
intern.idb_minor_type != DB5_MINORTYPE_BRLCAD_BOT) {
+           rt_db_free_internal(&intern);
+           bu_vls_printf(&error_str, "%s: %s is not a BOT solid!\n", argv[0], 
obj);
+           bu_free(obj, "free obj");
+           continue;
+       }
+
+       bot = (struct rt_bot_internal *)intern.idb_ptr;
+       headRblp = rt_bot_split(bot);
+
+       {
+           int ac = 3;
+           const char *av[4];
+           struct rt_db_internal bot_intern;
+           struct rt_bot_list *rblp;
+
+           av[0] = "make_name";
+           av[1] = "-s";
+           av[2] = "0";
+           av[3] = (char *)0;
+
+           /* Set make_name's count to 0 */
+           ged_make_name(gedp, ac, av);
+
+           ac = 2;
+           av[2] = (char *)0;
+
+           for (BU_LIST_FOR(rblp, rt_bot_list, &headRblp->l)) {
+               /* Get a unique name based on the original name */
+               av[1] = obj;
+               ged_make_name(gedp, ac, av);
+
+               /* Create the bot */
+               RT_DB_INTERNAL_INIT(&bot_intern);
+               bot_intern.idb_major_type = DB5_MAJORTYPE_BRLCAD;
+               bot_intern.idb_type = ID_BOT;
+               bot_intern.idb_meth = &OBJ[ID_BOT];
+               bot_intern.idb_ptr = (void *)rblp->bot;
+
+               /* Save new bot name for later use */
+               bu_vls_printf(&new_bots, "%s ", 
bu_vls_addr(gedp->ged_result_str));
+
+               dp = db_diradd(gedp->ged_wdbp->dbip, 
bu_vls_addr(gedp->ged_result_str), RT_DIR_PHONY_ADDR, 0, RT_DIR_SOLID, (void 
*)&bot_intern.idb_type);
+               if (dp == RT_DIR_NULL) {
+                   bu_vls_printf(&error_str, " failed to be added to the 
database.\n");
+                   rt_bot_list_free(headRblp, 0);
+                   rt_db_free_internal(&intern);
+               } else {
+                 if (rt_db_put_internal(dp, gedp->ged_wdbp->dbip, &bot_intern, 
&rt_uniresource) < 0) {
+                   bu_vls_printf(&error_str, " failed to be added to the 
database.\n");
+                   rt_bot_list_free(headRblp, 0);
+                   rt_db_free_internal(&intern);
+                 }
+               }
+           }
+
+           /* Save the name of the original bot and the new bots as a sublist 
*/
+           bu_vls_printf(&bot_result_list, "{%s {%s}} ", obj, 
bu_vls_addr(&new_bots));
+
+           bu_vls_trunc(gedp->ged_result_str, 0);
+           bu_vls_trunc(&new_bots, 0);
+       }
+
+       rt_bot_list_free(headRblp, 0);
+       rt_db_free_internal(&intern);
+       bu_free(obj, "free obj");
+    }
+
+    bu_vls_trunc(gedp->ged_result_str, 0);
+    bu_vls_printf(gedp->ged_result_str, "%s {%s}", 
bu_vls_addr(&bot_result_list), bu_vls_addr(&error_str));
+
+    bu_vls_free(&bot_result_list);
+    bu_vls_free(&new_bots);
+    bu_vls_free(&error_str);
+
+    return GED_OK;
+}
+
+/*
+ * Local Variables:
+ * mode: C
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * c-file-style: "stroustrup"
+ * End:
+ * ex: shiftwidth=4 tabstop=8
+ */

Copied: brlcad/branches/gedplugins/src/libged/bot/bot_sync.c (from rev 76395, 
brlcad/branches/gedplugins/src/libged/bot_sync/bot_sync.c)
===================================================================
--- brlcad/branches/gedplugins/src/libged/bot/bot_sync.c                        
        (rev 0)
+++ brlcad/branches/gedplugins/src/libged/bot/bot_sync.c        2020-07-21 
19:08:41 UTC (rev 76396)
@@ -0,0 +1,101 @@
+/*                         B O T _ S Y N C . C
+ * BRL-CAD
+ *
+ * Copyright (c) 2008-2020 United States Government as represented by
+ * the U.S. Army Research Laboratory.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this file; see the file named COPYING for more
+ * information.
+ */
+/** @file libged/bot_sync.c
+ *
+ * The bot_sync command.
+ *
+ */
+
+#include "common.h"
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "bu/path.h"
+#include "raytrace.h"
+
+#include "../ged_private.h"
+
+
+int
+ged_bot_sync(struct ged *gedp, int argc, const char *argv[])
+{
+    int i;
+    struct directory *dp;
+    struct rt_db_internal intern;
+    struct rt_bot_internal *bot;
+    static const char *usage = "bot [bot2 bot3 ...]";
+
+    GED_CHECK_DATABASE_OPEN(gedp, GED_ERROR);
+    GED_CHECK_READ_ONLY(gedp, GED_ERROR);
+    GED_CHECK_ARGC_GT_0(gedp, argc, GED_ERROR);
+
+    /* initialize result */
+    bu_vls_trunc(gedp->ged_result_str, 0);
+
+    /* must be wanting help */
+    if (argc == 1) {
+       bu_vls_printf(gedp->ged_result_str, "Usage: %s %s", argv[0], usage);
+       return GED_HELP;
+    }
+
+    for (i = 1; i < argc; ++i) {
+       /* Skip past any path elements */
+       char *obj = bu_path_basename(argv[i], NULL);
+
+       if (BU_STR_EQUAL(obj, ".")) {
+           /* malformed path, lookup using exactly what was provided */
+           bu_free(obj, "free bu_path_basename");
+           obj = bu_strdup(argv[i]);
+       }
+
+       if ((dp = db_lookup(gedp->ged_wdbp->dbip, obj, LOOKUP_QUIET)) == 
RT_DIR_NULL) {
+           bu_vls_printf(gedp->ged_result_str, "%s: db_lookup(%s) error\n", 
argv[0], obj);
+       } else {
+
+           GED_DB_GET_INTERNAL(gedp, &intern, dp, bn_mat_identity, 
&rt_uniresource, GED_ERROR);
+
+           if (intern.idb_major_type != DB5_MAJORTYPE_BRLCAD || 
intern.idb_minor_type != DB5_MINORTYPE_BRLCAD_BOT) {
+               rt_db_free_internal(&intern);
+               bu_vls_printf(gedp->ged_result_str, "%s: %s is not a BOT 
solid!\n", argv[0], obj);
+           } else {
+
+               bot = (struct rt_bot_internal *)intern.idb_ptr;
+               rt_bot_sync(bot);
+
+               GED_DB_PUT_INTERNAL(gedp, dp, &intern, 
gedp->ged_wdbp->wdb_resp, GED_ERROR);
+           }
+       }
+       bu_free(obj, "free obj");
+    }
+
+    return GED_OK;
+}
+
+/*
+ * Local Variables:
+ * mode: C
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * c-file-style: "stroustrup"
+ * End:
+ * ex: shiftwidth=4 tabstop=8
+ */

Copied: brlcad/branches/gedplugins/src/libged/bot/bot_vertex_fuse.c (from rev 
76395, brlcad/branches/gedplugins/src/libged/bot_vertex_fuse/bot_vertex_fuse.c)
===================================================================
--- brlcad/branches/gedplugins/src/libged/bot/bot_vertex_fuse.c                 
        (rev 0)
+++ brlcad/branches/gedplugins/src/libged/bot/bot_vertex_fuse.c 2020-07-21 
19:08:41 UTC (rev 76396)
@@ -0,0 +1,92 @@
+/*                         B O T _ V E R T E X _ F U S E . C
+ * BRL-CAD
+ *
+ * Copyright (c) 2008-2020 United States Government as represented by
+ * the U.S. Army Research Laboratory.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this file; see the file named COPYING for more
+ * information.
+ */
+/** @file libged/bot_vertex_fuse.c
+ *
+ * The bot_vertex_fuse command.
+ *
+ */
+
+#include "common.h"
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "rt/geom.h"
+
+#include "../ged_private.h"
+
+
+int
+ged_bot_vertex_fuse(struct ged *gedp, int argc, const char *argv[])
+{
+    struct directory *old_dp, *new_dp;
+    struct rt_db_internal intern;
+    struct rt_bot_internal *bot;
+    int count=0;
+    static const char *usage = "new_bot old_bot";
+
+    GED_CHECK_DATABASE_OPEN(gedp, GED_ERROR);
+    GED_CHECK_READ_ONLY(gedp, GED_ERROR);
+    GED_CHECK_ARGC_GT_0(gedp, argc, GED_ERROR);
+
+    /* initialize result */
+    bu_vls_trunc(gedp->ged_result_str, 0);
+
+    /* must be wanting help */
+    if (argc == 1) {
+       bu_vls_printf(gedp->ged_result_str, "Usage: %s %s", argv[0], usage);
+       return GED_HELP;
+    }
+
+    if (argc != 3) {
+       bu_vls_printf(gedp->ged_result_str, "Usage: %s %s", argv[0], usage);
+       return GED_ERROR;
+    }
+
+    GED_DB_LOOKUP(gedp, old_dp, argv[2], LOOKUP_NOISY, GED_ERROR & GED_QUIET);
+    GED_DB_GET_INTERNAL(gedp, &intern, old_dp, bn_mat_identity, 
&rt_uniresource, GED_ERROR);
+
+    if (intern.idb_major_type != DB5_MAJORTYPE_BRLCAD || intern.idb_minor_type 
!= DB5_MINORTYPE_BRLCAD_BOT) {
+       bu_vls_printf(gedp->ged_result_str, "%s: %s is not a BOT solid!\n", 
argv[0], argv[2]);
+       return GED_ERROR;
+    }
+
+    bot = (struct rt_bot_internal *)intern.idb_ptr;
+    RT_BOT_CK_MAGIC(bot);
+
+    count = rt_bot_vertex_fuse(bot, &gedp->ged_wdbp->wdb_tol);
+    bu_vls_printf(gedp->ged_result_str, "Fused %d vertices\n", count);
+
+    GED_DB_DIRADD(gedp, new_dp, argv[1], RT_DIR_PHONY_ADDR, 0, RT_DIR_SOLID, 
(void *)&intern.idb_type, GED_ERROR);
+    GED_DB_PUT_INTERNAL(gedp, new_dp, &intern, &rt_uniresource, GED_ERROR);
+
+    return GED_OK;
+}
+
+/*
+ * Local Variables:
+ * mode: C
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * c-file-style: "stroustrup"
+ * End:
+ * ex: shiftwidth=4 tabstop=8
+ */

This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.



_______________________________________________
BRL-CAD Source Commits mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/brlcad-commits

Reply via email to