Revision: 77357
          http://sourceforge.net/p/brlcad/code/77357
Author:   starseeker
Date:     2020-10-07 16:14:36 +0000 (Wed, 07 Oct 2020)
Log Message:
-----------
C->C++

Modified Paths:
--------------
    brlcad/trunk/include/analyze/info.h
    brlcad/trunk/src/libged/gqa/CMakeLists.txt

Added Paths:
-----------
    brlcad/trunk/src/libged/gqa/gqa.cpp

Removed Paths:
-------------
    brlcad/trunk/src/libged/gqa/gqa.c

Modified: brlcad/trunk/include/analyze/info.h
===================================================================
--- brlcad/trunk/include/analyze/info.h 2020-10-07 15:17:27 UTC (rev 77356)
+++ brlcad/trunk/include/analyze/info.h 2020-10-07 16:14:36 UTC (rev 77357)
@@ -42,7 +42,7 @@
 struct region_pair {
     struct bu_list l;
     union {
-       char *name;
+       const char *name;
        struct region *r1;
     } r;
     struct region *r2;

Modified: brlcad/trunk/src/libged/gqa/CMakeLists.txt
===================================================================
--- brlcad/trunk/src/libged/gqa/CMakeLists.txt  2020-10-07 15:17:27 UTC (rev 
77356)
+++ brlcad/trunk/src/libged/gqa/CMakeLists.txt  2020-10-07 16:14:36 UTC (rev 
77357)
@@ -7,15 +7,15 @@
   )
 
 add_definitions(-DGED_PLUGIN)
-ged_plugin_library(ged-gqa SHARED gqa.c)
+ged_plugin_library(ged-gqa SHARED gqa.cpp)
 target_link_libraries(ged-gqa libged libbu)
 set_property(TARGET ged-gqa APPEND PROPERTY COMPILE_DEFINITIONS BRLCADBUILD 
HAVE_CONFIG_H)
-VALIDATE_STYLE(ged-gqa gqa.c)
+VALIDATE_STYLE(ged-gqa gqa.cpp)
 PLUGIN_SETUP(ged-gqa ged)
 
 CMAKEFILES(
   CMakeLists.txt
-  gqa.c
+  gqa.cpp
   )
 
 # Local Variables:

Deleted: brlcad/trunk/src/libged/gqa/gqa.c
===================================================================
--- brlcad/trunk/src/libged/gqa/gqa.c   2020-10-07 15:17:27 UTC (rev 77356)
+++ brlcad/trunk/src/libged/gqa/gqa.c   2020-10-07 16:14:36 UTC (rev 77357)
@@ -1,2582 +0,0 @@
-/*                         G Q A . 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/gqa.c
- *
- * performs a set of quantitative analyses on geometry.
- *
- * XXX need to look at gap computation
- *
- * plot the points where overlaps start/stop
- *
- * Designed to be a framework for 3d sampling of the geometry volume.
- * TODO: Need to move the sample pattern logic into LIBRT.
- *
- */
-
-#include "common.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <math.h>
-#include <limits.h>                    /* home of INT_MAX aka MAXINT */
-
-
-#include "bu/parallel.h"
-#include "bu/getopt.h"
-#include "vmath.h"
-#include "raytrace.h"
-#include "bn/plot3.h"
-#include "analyze.h"
-
-#include "../ged_private.h"
-
-struct analyze_densities *_gd_densities;
-char *_gd_densities_source;
-
-/* bu_getopt() options */
-char *options = "A:a:de:f:g:Gn:N:p:P:qrS:s:t:U:u:vV:W:h?";
-char *options_str = "[-A A|a|b|c|e|g|m|o|v|w] [-a az] [-d] [-e el] [-f 
densityFile] [-g spacing|upper,lower|upper-lower] [-G] [-n nhits] [-N nviews] 
[-p plotPrefix] [-P ncpus] [-q] [-r] [-S nsamples] [-t overlap_tol] [-U useair] 
[-u len_units vol_units wt_units] [-v] [-V volume_tol] [-W weight_tol]";
-
-#define ANALYSIS_VOLUMES          1
-#define ANALYSIS_WEIGHTS          2
-#define ANALYSIS_OVERLAPS         4
-#define ANALYSIS_ADJ_AIR          8 /* adjacent air */
-#define ANALYSIS_GAPS            16 /* space between regions */
-#define ANALYSIS_EXP_AIR         32 /* exposed air */
-#define ANALYSIS_BBOX            64 /* overall bounding box */
-#define ANALYSIS_INTERFACES     128
-#define ANALYSIS_CENTROIDS      256
-#define ANALYSIS_MOMENTS        512
-#define ANALYSIS_PLOT_OVERLAPS 1024
-
-/* Note: struct parsing requires no space after the commas.  take care
- * when formatting this file.  if the compile breaks here, it means
- * that spaces got inserted incorrectly.
- */
-#define COMMA ','
-
-static int analysis_flags;
-static int multiple_analyses;
-
-static double azimuth_deg;
-static double elevation_deg;
-static char *densityFileName;
-static double gridSpacing;
-static double gridSpacingLimit;
-static const double GRIDSPACING_STEP = 1.0 / 2.0;
-
-static char makeOverlapAssemblies;
-static size_t require_num_hits;
-static int ncpu;
-static double Samples_per_model_axis;
-static double overlap_tolerance;
-static double volume_tolerance;
-static double weight_tolerance;
-static int aborted = 0;
-
-static int print_per_region_stats;
-static int max_region_name_len;
-static int use_air;
-static int num_objects; /* number of objects specified on command line */
-static int max_cpus;
-static int num_views;
-static int verbose;
-static int quiet_missed_report;
-
-static const char *plot_prefix = NULL; /* non-NULL means produce plot files */
-static FILE *plot_weight;
-static FILE *plot_volume;
-static FILE *plot_overlaps;
-static FILE *plot_adjair;
-static FILE *plot_gaps;
-static FILE *plot_expair;
-
-static int overlap_color[3] = { 255, 255, 0 }; /* yellow */
-static int gap_color[3] = { 128, 192, 255 };    /* cyan */
-static int adjAir_color[3] = { 128, 255, 192 }; /* pale green */
-static int expAir_color[3] = { 255, 128, 255 }; /* magenta */
-
-static int debug = 0;
-#define DLOG if (debug) bu_vls_printf
-
-/* Some defines for re-using the values from the application structure
- * for other purposes
- */
-#define A_LENDEN a_color[0]
-#define A_LEN a_color[1]
-#define A_STATE a_uptr
-
-
-struct cstate {
-    int curr_view; /* the "view" number we are shooting */
-    int u_axis;    /* these 3 are in the range 0..2 inclusive and indicate 
which axis (X, Y, or Z) */
-    int v_axis;    /* is being used for the U, V, or invariant vector 
direction */
-    int i_axis;
-
-    int sem_lists;
-    int sem_worker;
-
-    /* sem_worker protects this */
-    int v;         /* indicates how many "grid_size" steps in the v direction 
have been taken */
-
-    int sem_stats;
-
-    /* sem_stats protects this */
-    double *m_lenDensity;
-    double *m_len;
-    double *m_volume;
-    double *m_weight;
-    unsigned long *shots;
-    int first;     /* this is the first time we've computed a set of views */
-
-    vect_t u_dir;  /* direction of U vector for "current view" */
-    vect_t v_dir;  /* direction of V vector for "current view" */
-    struct rt_i *rtip;
-    long steps[3]; /* this is per-dimension, not per-view */
-    vect_t span;   /* How much space does the geometry span in each of X, Y, Z 
directions */
-    vect_t area;   /* area of the view for view with invariant at index */
-
-    fastf_t *m_lenTorque; /* torque vector for each view */
-    fastf_t *m_moi;       /* one vector per view for collecting the partial 
moments of inertia calculation */
-    fastf_t *m_poi;       /* one vector per view for collecting the partial 
products of inertia calculation */
-
-    struct resource *resp;
-};
-
-
-struct ged_gqa_plot {
-    struct bn_vlblock *vbp;
-    struct bu_list *vhead;
-} ged_gqa_plot;
-
-/* summary data structure for objects specified on command line */
-static struct per_obj_data {
-    char *o_name;
-    double *o_len;
-    double *o_lenDensity;
-    double *o_volume;
-    double *o_weight;
-    fastf_t *o_lenTorque; /* torque vector for each view */
-    fastf_t *o_moi;       /* one vector per view for collecting the partial 
moments of inertia calculation */
-    fastf_t *o_poi;       /* one vector per view for collecting the partial 
products of inertia calculation */
-} *obj_tbl;
-
-/**
- * this is the data we track for each region
- */
-static struct per_region_data {
-    unsigned long hits;
-    double *r_lenDensity; /* for per-region per-view weight computation */
-    double *r_len;        /* for per-region, per-view computation */
-    double *r_weight;
-    double *r_volume;
-    struct per_obj_data *optr;
-} *reg_tbl;
-
-
-/* Access to these lists should be in sections
- * of code protected by state->sem_lists
- */
-
-/**
- * list of gaps
- */
-static struct region_pair gapList = {
-    {
-       BU_LIST_HEAD_MAGIC,
-       (struct bu_list *)&gapList,
-       (struct bu_list *)&gapList
-    },
-    { "Gaps" },
-    (struct region *)NULL,
-    (unsigned long)0,
-    (double)0.0,
-    {0.0, 0.0, 0.0, }
-};
-
-
-/**
- * list of adjacent air
- */
-static struct region_pair adjAirList = {
-    {
-       BU_LIST_HEAD_MAGIC,
-       (struct bu_list *)&adjAirList,
-       (struct bu_list *)&adjAirList
-    },
-    { (char *)"Adjacent Air" },
-    (struct region *)NULL,
-    (unsigned long)0,
-    (double)0.0,
-    {0.0, 0.0, 0.0, }
-};
-
-
-/**
- * list of exposed air
- */
-static struct region_pair exposedAirList = {
-    {
-       BU_LIST_HEAD_MAGIC,
-       (struct bu_list *)&exposedAirList,
-       (struct bu_list *)&exposedAirList
-    },
-    { "Exposed Air" },
-    (struct region *)NULL,
-    (unsigned long)0,
-    (double)0.0,
-    {0.0, 0.0, 0.0, }
-};
-
-
-/**
- * list of overlaps
- */
-static struct region_pair overlapList = {
-    {
-       BU_LIST_HEAD_MAGIC,
-       (struct bu_list *)&overlapList,
-       (struct bu_list *)&overlapList
-    },
-    { "Overlaps" },
-    (struct region *)NULL,
-    (unsigned long)0,
-    (double)0.0,
-    {0.0, 0.0, 0.0, }
-};
-
-
-/**
- * This structure holds the name of a unit value, and the conversion
- * factor necessary to convert from/to BRL-CAD standard units.
- *
- * The standard units are millimeters, cubic millimeters, and grams.
- *
- * XXX this section should be extracted to libbu/units.c
- */
-struct cvt_tab {
-    double val;
-    char name[32];
-};
-
-
-static const struct cvt_tab units_tab[3][40] = {
-    {
-       /* length, stolen from bu/units.c with the "none" value
-        * removed Values for converting from given units to mm
-        */
-       {1.0,           "mm"}, /* default */
-       /* {0.0,                "none"}, */ /* this is removed to force a 
certain
-                                            * amount of error checking for the 
user
-                                            */
-       {1.0e-7,        "angstrom"},
-       {1.0e-7,        "decinanometer"},
-       {1.0e-6,        "nm"},
-       {1.0e-6,        "nanometer"},
-       {1.0e-3,        "um"},
-       {1.0e-3,        "micrometer"},
-       {1.0e-3,        "micron"},
-       {1.0,           "millimeter"},
-       {10.0,          "cm"},
-       {10.0,          "centimeter"},
-       {1000.0,        "m"},
-       {1000.0,        "meter"},
-       {1000000.0,     "km"},
-       {1000000.0,     "kilometer"},
-       {25.4,          "in"},
-       {25.4,          "inch"},
-       {25.4,          "inches"},              /* for plural */
-       {304.8,         "ft"},
-       {304.8,         "foot"},
-       {304.8,         "feet"},
-       {456.2,         "cubit"},
-       {914.4,         "yd"},
-       {914.4,         "yard"},
-       {5029.2,        "rd"},
-       {5029.2,        "rod"},
-       {1609344.0,     "mi"},
-       {1609344.0,     "mile"},
-       {1852000.0,     "nmile"},
-       {1852000.0,     "nautical mile"},
-       {1.495979e+14,  "AU"},
-       {1.495979e+14,  "astronomical unit"},
-       {9.460730e+18,  "lightyear"},
-       {3.085678e+19,  "pc"},
-       {3.085678e+19,  "parsec"},
-       {0.0,           ""}                     /* LAST ENTRY */
-    },
-    {
-       /* volume
-        * Values for converting from given units to mm^3
-        */
-       {1.0, "cu mm"}, /* default */
-
-       {1.0, "mm"},
-       {1.0, "mm^3"},
-
-       {1.0e3, "cm"},
-       {1.0e3, "cm^3"},
-       {1.0e3, "cu cm"},
-       {1.0e3, "cc"},
-
-       {1.0e6, "l"},
-       {1.0e6, "liter"},
-       {1.0e6, "litre"},
-
-       {1.0e9, "m"},
-       {1.0e9, "m^3"},
-       {1.0e9, "cu m"},
-
-       {16387.064, "in"},
-       {16387.064, "in^3"},
-       {16387.064, "cu in"},
-
-       {28316846.592, "ft"},
-
-       {28316846.592, "ft^3"},
-       {28316846.592, "cu ft"},
-
-       {764554857.984, "yds"},
-       {764554857.984, "yards"},
-       {764554857.984, "cu yards"},
-
-       {0.0,           ""}                     /* LAST ENTRY */
-    },
-    {
-       /* weight
-        * Values for converting given units to grams
-        */
-       {1.0, "grams"}, /* default */
-
-       {1.0, "g"},
-       {0.0648, "gr"},
-       {0.0648, "grains"},
-
-       {1.0e3, "kg"},
-       {1.0e3, "kilos"},
-       {1.0e3, "kilograms"},
-
-       {28.35, "oz"},
-       {28.35, "ounce"},
-
-       {453.6, "lb"},
-       {453.6, "lbs"},
-       {0.0,           ""}                     /* LAST ENTRY */
-    }
-};
-
-
-/* this table keeps track of the "current" or "user selected units and
- * the associated conversion values
- */
-#define LINE 0
-#define VOL 1
-#define WGT 2
-static const struct cvt_tab *units[3] = {
-    &units_tab[0][0],  /* linear */
-    &units_tab[1][0],  /* volume */
-    &units_tab[2][0]   /* weight */
-};
-
-
-/**
- * _gqa_read_units_double
- *
- * Read a non-negative floating point value with optional units
- *
- * Return
- * 1 Failure
- * 0 Success
- */
-int
-_gqa_read_units_double(double *val, char *buf, const struct cvt_tab *cvt)
-{
-    double a;
-#define UNITS_STRING_SZ 256
-    char units_string[UNITS_STRING_SZ+1] = {0};
-    int i;
-
-
-    i = sscanf(buf, "%lg" CPP_SCAN(UNITS_STRING_SZ), &a, units_string);
-
-    if (i < 0) return 1;
-
-    if (i == 1) {
-       *val = a;
-
-       return 0;
-    }
-    if (i == 2) {
-       *val = a;
-       for (; cvt->name[0] != '\0';) {
-           if (!bu_strncmp(cvt->name, units_string, sizeof(units_string))) {
-               goto found_units;
-           } else {
-               cvt++;
-           }
-       }
-       bu_vls_printf(_ged_current_gedp->ged_result_str, "Bad units specifier 
\"%s\" on value \"%s\"\n", units_string, buf);
-       return 1;
-
-    found_units:
-       *val = a * cvt->val;
-       return 0;
-    }
-    bu_vls_printf(_ged_current_gedp->ged_result_str, "%s sscanf problem on 
\"%s\" got %d\n", CPP_FILELINE, buf, i);
-    return 1;
-}
-
-
-/* the above should be extracted to libbu/units.c */
-
-
-/**
- * Parse through command line flags
- */
-static int
-parse_args(int ac, char *av[])
-{
-    int c;
-    int i;
-    double a;
-    char *p;
-
-    /* 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(ac, av, options)) != -1) {
-       switch (c) {
-           case 'A':
-               {
-                   analysis_flags = 0;
-                   multiple_analyses = 0;
-                   for (p = bu_optarg; *p; p++) {
-                       switch (*p) {
-                           case 'A' :
-                               multiple_analyses = 1;
-                               analysis_flags = analysis_flags \
-                                   | ANALYSIS_ADJ_AIR \
-                                   | ANALYSIS_BBOX \
-                                   | ANALYSIS_CENTROIDS \
-                                   | ANALYSIS_EXP_AIR \
-                                   | ANALYSIS_GAPS \
-                                   | ANALYSIS_MOMENTS \
-                                   | ANALYSIS_OVERLAPS \
-                                   | ANALYSIS_VOLUMES \
-                                   | ANALYSIS_WEIGHTS;
-                               break;
-                           case 'a' :
-                               if (analysis_flags)
-                                   multiple_analyses = 1;
-
-                               analysis_flags |= ANALYSIS_ADJ_AIR;
-
-                               break;
-                           case 'b' :
-                               if (analysis_flags)
-                                   multiple_analyses = 1;
-
-                               analysis_flags |= ANALYSIS_BBOX;
-
-                               break;
-                           case 'c' :
-                               if (analysis_flags)
-                                   multiple_analyses = 1;
-
-                               analysis_flags |= ANALYSIS_WEIGHTS;
-                               analysis_flags |= ANALYSIS_CENTROIDS;
-
-                               break;
-                           case 'e' :
-                               if (analysis_flags)
-                                   multiple_analyses = 1;
-
-                               analysis_flags |= ANALYSIS_EXP_AIR;
-                               break;
-                           case 'g' :
-                               if (analysis_flags)
-                                   multiple_analyses = 1;
-
-                               analysis_flags |= ANALYSIS_GAPS;
-                               break;
-                           case 'm' :
-                               if (analysis_flags)
-                                   multiple_analyses = 1;
-
-                               analysis_flags |= ANALYSIS_WEIGHTS;
-                               analysis_flags |= ANALYSIS_CENTROIDS;
-                               analysis_flags |= ANALYSIS_MOMENTS;
-
-                               break;
-                           case 'o' :
-                               if (analysis_flags)
-                                   multiple_analyses = 1;
-
-                               analysis_flags |= ANALYSIS_OVERLAPS;
-                               break;
-                           case 'p' :
-                               if (analysis_flags)
-                                   multiple_analyses = 1;
-
-                               analysis_flags |= ANALYSIS_OVERLAPS;
-                               analysis_flags |= ANALYSIS_PLOT_OVERLAPS;
-                               break;
-                           case 'v' :
-                               if (analysis_flags)
-                                   multiple_analyses = 1;
-
-                               analysis_flags |= ANALYSIS_VOLUMES;
-                               break;
-                           case 'w' :
-                               if (analysis_flags)
-                                   multiple_analyses = 1;
-
-                               analysis_flags |= ANALYSIS_WEIGHTS;
-                               break;
-                           default:
-                               
bu_vls_printf(_ged_current_gedp->ged_result_str, "Unknown analysis type \"%c\" 
requested.\n", *p);
-                               return -1;
-                       }
-                   }
-                   break;
-               }
-           case 'a':
-               bu_vls_printf(_ged_current_gedp->ged_result_str, "azimuth not 
implemented\n");
-               if (bn_decode_angle(&azimuth_deg,bu_optarg) == 0) {
-                   bu_vls_printf(_ged_current_gedp->ged_result_str, "error 
parsing azimuth \"%s\"\n", bu_optarg);
-                   return -1;
-               }
-               break;
-           case 'e':
-               bu_vls_printf(_ged_current_gedp->ged_result_str, "elevation not 
implemented\n");
-               if (bn_decode_angle(&elevation_deg,bu_optarg) == 0) {
-                   bu_vls_printf(_ged_current_gedp->ged_result_str, "error 
parsing elevation \"%s\"\n", bu_optarg);
-                   return -1;
-               }
-               break;
-           case 'd': debug = 1; break;
-
-           case 'f': densityFileName = bu_optarg; break;
-
-           case 'g':
-               {
-                   double value1, value2;
-                   i = 0;
-
-                   /* find out if we have two or one args; user can
-                    * separate them with , or - delimiter
-                    */
-                   p = strchr(bu_optarg, COMMA);
-                   if (p)
-                       *p++ = '\0';
-                   else {
-                       p = strchr(bu_optarg, '-');
-                       if (p)
-                           *p++ = '\0';
-                   }
-
-
-                   if (_gqa_read_units_double(&value1, bu_optarg, 
units_tab[0])) {
-                       bu_vls_printf(_ged_current_gedp->ged_result_str, "error 
parsing grid spacing value \"%s\"\n", bu_optarg);
-                       return -1;
-                   }
-
-                   if (p) {
-                       /* we've got 2 values, they are upper limit
-                        * and lower limit.
-                        */
-                       if (_gqa_read_units_double(&value2, p, units_tab[0])) {
-                           bu_vls_printf(_ged_current_gedp->ged_result_str, 
"error parsing grid spacing limit value \"%s\"\n", p);
-                           return -1;
-                       }
-
-                       gridSpacing = value1;
-                       gridSpacingLimit = value2;
-                   } else {
-                       gridSpacingLimit = value1;
-
-                       gridSpacing = 0.0; /* flag it */
-                   }
-                   break;
-               }
-           case 'G':
-               makeOverlapAssemblies = 1;
-               bu_vls_printf(_ged_current_gedp->ged_result_str, "-G option 
unimplemented\n");
-               return -1;
-           case 'n':
-               if (sscanf(bu_optarg, "%d", &c) != 1 || c < 0) {
-                   bu_vls_printf(_ged_current_gedp->ged_result_str, "num_hits 
must be integer value >= 0, not \"%s\"\n", bu_optarg);
-                   return -1;
-               }
-
-               require_num_hits = (size_t)c;
-               break;
-
-           case 'N':
-               num_views = atoi(bu_optarg);
-               break;
-           case 'p':
-               plot_prefix = bu_optarg;
-               break;
-           case 'P':
-               /* cannot ask for more cpu's than the machine has */
-               c = atoi(bu_optarg);
-               if (c > 0 && c <= max_cpus)
-                   ncpu = c;
-               break;
-           case 'q':
-               quiet_missed_report = 1;
-               break;
-           case 'r':
-               print_per_region_stats = 1;
-               break;
-           case 'S':
-               if (sscanf(bu_optarg, "%lg", &a) != 1 || a <= 1.0) {
-                   bu_vls_printf(_ged_current_gedp->ged_result_str, "error in 
specifying minimum samples per model axis: \"%s\"\n", bu_optarg);
-                   break;
-               }
-               Samples_per_model_axis = a + 1;
-               break;
-           case 't':
-               if (_gqa_read_units_double(&overlap_tolerance, bu_optarg, 
units_tab[0])) {
-                   bu_vls_printf(_ged_current_gedp->ged_result_str, "error in 
overlap tolerance distance \"%s\"\n", bu_optarg);
-                   return -1;
-               }
-               break;
-           case 'v':
-               verbose = 1;
-               break;
-           case 'V':
-               if (_gqa_read_units_double(&volume_tolerance, bu_optarg, 
units_tab[1])) {
-                   bu_vls_printf(_ged_current_gedp->ged_result_str, "error in 
volume tolerance \"%s\"\n", bu_optarg);
-                   return -1;
-               }
-               break;
-           case 'W':
-               if (_gqa_read_units_double(&weight_tolerance, bu_optarg, 
units_tab[2])) {
-                   bu_vls_printf(_ged_current_gedp->ged_result_str, "error in 
weight tolerance \"%s\"\n", bu_optarg);
-                   return -1;
-               }
-               break;
-
-           case 'U':
-               errno = 0;
-               use_air = strtol(bu_optarg, (char **)NULL, 10);
-               if (errno == ERANGE || errno == EINVAL) {
-                   bu_vls_printf(_ged_current_gedp->ged_result_str, "error in 
air argument %s\n", bu_optarg);
-                   return -1;
-               }
-               break;
-           case 'u':
-               {
-                   char *ptr = bu_optarg;
-                   const struct cvt_tab *cv;
-                   static const char *dim[3] = {"length", "volume", "weight"};
-                   char *units_name[3] = {NULL, NULL, NULL};
-                   char **units_ap;
-
-                   /* fill in units_name with the names we parse out */
-                   units_ap = units_name;
-
-                   /* acquire unit names */
-                   for (i = 0; i < 3 && ptr; i++) {
-                       int found_unit;
-
-                       if (i == 0) {
-                           *units_ap = strtok(ptr, CPP_XSTR(COMMA));
-                       } else {
-                           *units_ap = strtok(NULL, CPP_XSTR(COMMA));
-                       }
-
-                       /* got something? */
-                       if (*units_ap == NULL)
-                           break;
-
-                       /* got something valid? */
-                       found_unit = 0;
-                       for (cv = &units_tab[i][0]; cv->name[0] != '\0'; cv++) {
-                           if (units_name[i] && BU_STR_EQUAL(cv->name, 
units_name[i])) {
-                               units[i] = cv;
-                               found_unit = 1;
-                               break;
-                           }
-                       }
-
-                       if (!found_unit) {
-                           bu_vls_printf(_ged_current_gedp->ged_result_str, 
"Units \"%s\" not found in conversion table\n", units_name[i]);
-                           return -1;
-                       }
-
-                       ++units_ap;
-                   }
-
-                   bu_vls_printf(_ged_current_gedp->ged_result_str, "Units: ");
-                   for (i = 0; i < 3; i++) {
-                       bu_vls_printf(_ged_current_gedp->ged_result_str, " %s: 
%s", dim[i], units[i]->name);
-                   }
-                   bu_vls_printf(_ged_current_gedp->ged_result_str, "\n");
-               }
-               break;
-
-           default: /* '?' 'h' */
-               return -1;
-       }
-    }
-
-    return bu_optind;
-}
-
-/**
- * Write end points of partition to the standard output.  If this
- * routine return !0, this partition will be dropped from the boolean
- * evaluation.
- *
- * Returns:
- * 0 to eliminate partition with overlap entirely
- * 1 to retain partition in output list, claimed by reg1
- * 2 to retain partition in output list, claimed by reg2
- *
- * This routine must be prepared to run in parallel
- */
-int
-_gqa_overlap(struct application *ap,
-       struct partition *pp,
-       struct region *reg1,
-       struct region *reg2,
-       struct partition *hp)
-{
-    struct cstate *state = (struct cstate *)ap->A_STATE;
-    struct xray *rp = &ap->a_ray;
-    struct hit *ihitp = pp->pt_inhit;
-    struct hit *ohitp = pp->pt_outhit;
-    point_t ihit;
-    point_t ohit;
-    double depth;
-
-    if (!hp) /* unexpected */
-       return 0;
-
-    /* if one of the regions is air, let it loose */
-    if (reg1->reg_aircode && ! reg2->reg_aircode)
-       return 2;
-    if (reg2->reg_aircode && ! reg1->reg_aircode)
-       return 1;
-
-    depth = ohitp->hit_dist - ihitp->hit_dist;
-
-    if (depth < overlap_tolerance)
-       /* too small to matter, pick one or none */
-       return 1;
-
-    VJOIN1(ihit, rp->r_pt, ihitp->hit_dist, rp->r_dir);
-    VJOIN1(ohit, rp->r_pt, ohitp->hit_dist, rp->r_dir);
-
-    if (plot_overlaps) {
-       pl_color(plot_overlaps, V3ARGS(overlap_color));
-       pdv_3line(plot_overlaps, ihit, ohit);
-    }
-
-    if (analysis_flags & ANALYSIS_PLOT_OVERLAPS) {
-       bu_semaphore_acquire(state->sem_worker);
-       BN_ADD_VLIST(ged_gqa_plot.vbp->free_vlist_hd, ged_gqa_plot.vhead, ihit, 
BN_VLIST_LINE_MOVE);
-       BN_ADD_VLIST(ged_gqa_plot.vbp->free_vlist_hd, ged_gqa_plot.vhead, ohit, 
BN_VLIST_LINE_DRAW);
-       bu_semaphore_release(state->sem_worker);
-    }
-
-    if (analysis_flags & ANALYSIS_OVERLAPS) {
-       bu_semaphore_acquire(state->sem_lists);
-       add_unique_pair(&overlapList, reg1, reg2, depth, ihit);
-       bu_semaphore_release(state->sem_lists);
-
-       if (plot_overlaps) {
-           pl_color(plot_overlaps, V3ARGS(overlap_color));
-           pdv_3line(plot_overlaps, ihit, ohit);
-       }
-    } else {
-       bu_semaphore_acquire(state->sem_worker);
-       bu_vls_printf(_ged_current_gedp->ged_result_str, "overlap %s %s\n", 
reg1->reg_name, reg2->reg_name);
-       bu_semaphore_release(state->sem_worker);
-    }
-
-    /* XXX We should somehow flag the volume/weight calculations as invalid */
-
-    /* since we have no basis to pick one over the other, just pick */
-    return 1;  /* No further consideration to this partition */
-}
-
-
-/**
- * Does nothing.
- */
-void
-logoverlap(struct application *ap,
-          const struct partition *pp,
-          const struct bu_ptbl *regiontable,
-          const struct partition *InputHdp)
-{
-    RT_CK_AP(ap);
-    RT_CK_PT(pp);
-    BU_CK_PTBL(regiontable);
-    if (!InputHdp)
-       return;
-
-    /* do nothing */
-
-    return;
-}
-
-
-void _gqa_exposed_air(struct application *ap,
-                     struct partition *pp,
-                     point_t last_out_point,
-                     point_t pt,
-                     point_t opt)
-{
-    struct cstate *state = (struct cstate *)ap->A_STATE;
-
-    /* this shouldn't be air */
-
-    bu_semaphore_acquire(state->sem_lists);
-    add_unique_pair(&exposedAirList,
-           pp->pt_regionp,
-           (struct region *)NULL,
-           pp->pt_outhit->hit_dist - pp->pt_inhit->hit_dist, /* thickness */
-           last_out_point); /* location */
-    bu_semaphore_release(state->sem_lists);
-
-    if (plot_expair) {
-       pl_color(plot_expair, V3ARGS(expAir_color));
-       pdv_3line(plot_expair, pt, opt);
-    }
-}
-
-
-/**
- * rt_shootray() was told to call this on a hit.  It passes the
- * application structure which describes the state of the world (see
- * raytrace.h), and a circular linked list of partitions, each one
- * describing one in and out segment of one region.
- *
- * this routine must be prepared to run in parallel
- */
-int
-hit(struct application *ap, struct partition *PartHeadp, struct seg *segs)
-{
-    /* see raytrace.h for all of these guys */
-    struct partition *pp;
-    point_t pt, opt, last_out_point;
-    int last_air = 0;  /* what was the aircode of the last item */
-    int air_first = 1; /* are we in an air before a solid */
-    double dist;       /* the thickness of the partition */
-    double gap_dist;
-    double last_out_dist = -1.0;
-    double val;
-    struct cstate *state = (struct cstate *)ap->A_STATE;
-
-    if (!segs) /* unexpected */
-       return 0;
-
-    if (PartHeadp->pt_forw == PartHeadp) return 1;
-
-
-    /* examine each partition until we get back to the head */
-    for (pp=PartHeadp->pt_forw; pp != PartHeadp; pp = pp->pt_forw) {
-
-       long int material_id = pp->pt_regionp->reg_gmater;
-       fastf_t grams_per_cu_mm = analyze_densities_density(_gd_densities, 
material_id);
-
-       /* inhit info */
-       dist = pp->pt_outhit->hit_dist - pp->pt_inhit->hit_dist;
-       VJOIN1(pt, ap->a_ray.r_pt, pp->pt_inhit->hit_dist, ap->a_ray.r_dir);
-       VJOIN1(opt, ap->a_ray.r_pt, pp->pt_outhit->hit_dist, ap->a_ray.r_dir);
-
-       if (debug) {
-           bu_semaphore_acquire(state->sem_worker);
-           bu_vls_printf(_ged_current_gedp->ged_result_str, "%s %g->%g\n", 
pp->pt_regionp->reg_name,
-                         pp->pt_inhit->hit_dist, pp->pt_outhit->hit_dist);
-           bu_semaphore_release(state->sem_worker);
-       }
-
-       /* checking for air sticking out of the model.  This is done
-        * here because there may be any number of air regions
-        * sticking out of the model along the ray.
-        */
-       if (analysis_flags & ANALYSIS_EXP_AIR) {
-
-           gap_dist = (pp->pt_inhit->hit_dist - last_out_dist);
-
-           /* if air is first on the ray, or we're moving from void/gap to air
-            * then this is exposed air
-            */
-           if (pp->pt_regionp->reg_aircode &&
-               (air_first || gap_dist > overlap_tolerance)) {
-               _gqa_exposed_air(ap, pp, last_out_point, pt, opt);
-           } else {
-               air_first = 0;
-           }
-       }
-
-       /* looking for voids in the model */
-       if (analysis_flags & ANALYSIS_GAPS) {
-           if (pp->pt_back != PartHeadp) {
-               /* if this entry point is further than the previous
-                * exit point then we have a void
-                */
-               gap_dist = pp->pt_inhit->hit_dist - last_out_dist;
-
-               if (gap_dist > overlap_tolerance) {
-
-                   /* like overlaps, we only want to report unique pairs */
-                   bu_semaphore_acquire(state->sem_lists);
-                   add_unique_pair(&gapList,
-                           pp->pt_regionp,
-                           pp->pt_back->pt_regionp,
-                           gap_dist,
-                           pt);
-                   bu_semaphore_release(state->sem_lists);
-
-                   /* like overlaps, let's plot */
-                   if (plot_gaps) {
-                       vect_t gapEnd;
-                       VJOIN1(gapEnd, pt, -gap_dist, ap->a_ray.r_dir);
-
-                       pl_color(plot_gaps, V3ARGS(gap_color));
-                       pdv_3line(plot_gaps, pt, gapEnd);
-                   }
-               }
-           }
-       }
-
-       /* computing the weight of the objects */
-       if (analysis_flags & ANALYSIS_WEIGHTS) {
-           if (debug) {
-               bu_semaphore_acquire(state->sem_worker);
-               bu_vls_printf(_ged_current_gedp->ged_result_str, "Hit %s doing 
weight\n", pp->pt_regionp->reg_name);
-               bu_semaphore_release(state->sem_worker);
-           }
-
-           /* make sure mater index is within range of densities */
-           if (pp->pt_regionp->reg_gmater < 0) {
-               bu_semaphore_acquire(state->sem_worker);
-               bu_vls_printf(_ged_current_gedp->ged_result_str, "density index 
%d on region %s is outside of range\nSet GIFTmater on region or add entry to 
density table\n",
-                             pp->pt_regionp->reg_gmater,
-                             pp->pt_regionp->reg_name);
-               bu_semaphore_release(state->sem_worker);
-               return GED_ERROR;
-           } else {
-
-               struct per_region_data *prd;
-               vect_t cmass;
-               vect_t lenTorque;
-               fastf_t Lx = state->span[0]/state->steps[0];
-               fastf_t Ly = state->span[1]/state->steps[1];
-               fastf_t Lz = state->span[2]/state->steps[2];
-               fastf_t Lx_sq;
-               fastf_t Ly_sq;
-               fastf_t Lz_sq;
-               fastf_t cell_area;
-               int los;
-
-               switch (state->i_axis) {
-                   case 0:
-                       Lx_sq = dist*pp->pt_regionp->reg_los*0.01;
-                       Lx_sq *= Lx_sq;
-                       Ly_sq = Ly*Ly;
-                       Lz_sq = Lz*Lz;
-                       cell_area = Ly_sq;
-                       break;
-                   case 1:
-                       Lx_sq = Lx*Lx;
-                       Ly_sq = dist*pp->pt_regionp->reg_los*0.01;
-                       Ly_sq *= Ly_sq;
-                       Lz_sq = Lz*Lz;
-                       cell_area = Lx_sq;
-                       break;
-                   case 2:
-                   default:
-                       Lx_sq = Lx*Lx;
-                       Ly_sq = Ly*Ly;
-                       Lz_sq = dist*pp->pt_regionp->reg_los*0.01;
-                       Lz_sq *= Lz_sq;
-                       cell_area = Lx_sq;
-                       break;
-               }
-
-               /* factor in the density of this object weight
-                * computation, factoring in the LOS percentage
-                * material of the object
-                */
-               los = pp->pt_regionp->reg_los;
-
-               if (los < 1) {
-                   bu_semaphore_acquire(state->sem_worker);
-                   bu_vls_printf(_ged_current_gedp->ged_result_str, "bad LOS 
(%d) on %s\n", los, pp->pt_regionp->reg_name);
-                   bu_semaphore_release(state->sem_worker);
-               }
-
-               /* accumulate the total weight values */
-               val = grams_per_cu_mm * dist * (pp->pt_regionp->reg_los * 0.01);
-               ap->A_LENDEN += val;
-
-               prd = ((struct per_region_data *)pp->pt_regionp->reg_udata);
-               /* accumulate the per-region per-view weight values */
-               bu_semaphore_acquire(state->sem_stats);
-               prd->r_lenDensity[state->i_axis] += val;
-
-               /* accumulate the per-object per-view weight values */
-               prd->optr->o_lenDensity[state->i_axis] += val;
-
-               if (analysis_flags & ANALYSIS_CENTROIDS) {
-                   /* calculate the center of mass for this partition */
-                   VJOIN1(cmass, pt, dist*0.5, ap->a_ray.r_dir);
-
-                   /* calculate the lenTorque for this partition (i.e. 
centerOfMass * lenDensity) */
-                   VSCALE(lenTorque, cmass, val);
-
-                   /* accumulate per-object per-view torque values */
-                   VADD2(&prd->optr->o_lenTorque[state->i_axis*3], 
&prd->optr->o_lenTorque[state->i_axis*3], lenTorque);
-
-                   /* accumulate the total lenTorque */
-                   VADD2(&state->m_lenTorque[state->i_axis*3], 
&state->m_lenTorque[state->i_axis*3], lenTorque);
-
-                   if (analysis_flags & ANALYSIS_MOMENTS) {
-                       vectp_t moi;
-                       vectp_t poi = &state->m_poi[state->i_axis*3];
-                       fastf_t dx_sq = cmass[X]*cmass[X];
-                       fastf_t dy_sq = cmass[Y]*cmass[Y];
-                       fastf_t dz_sq = cmass[Z]*cmass[Z];
-                       fastf_t mass = val * cell_area;
-                       static const fastf_t ONE_TWELFTH = 1.0 / 12.0;
-
-                       /* Collect moments and products of inertia for the 
current object */
-                       moi = &prd->optr->o_moi[state->i_axis*3];
-                       moi[X] += ONE_TWELFTH*mass*(Ly_sq + Lz_sq) + 
mass*(dy_sq + dz_sq);
-                       moi[Y] += ONE_TWELFTH*mass*(Lx_sq + Lz_sq) + 
mass*(dx_sq + dz_sq);
-                       moi[Z] += ONE_TWELFTH*mass*(Lx_sq + Ly_sq) + 
mass*(dx_sq + dy_sq);
-                       poi = &prd->optr->o_poi[state->i_axis*3];
-                       poi[X] -= mass*cmass[X]*cmass[Y];
-                       poi[Y] -= mass*cmass[X]*cmass[Z];
-                       poi[Z] -= mass*cmass[Y]*cmass[Z];
-
-                       /* Collect moments and products of inertia for all 
objects */
-                       moi = &state->m_moi[state->i_axis*3];
-                       moi[X] += ONE_TWELFTH*mass*(Ly_sq + Lz_sq) + 
mass*(dy_sq + dz_sq);
-                       moi[Y] += ONE_TWELFTH*mass*(Lx_sq + Lz_sq) + 
mass*(dx_sq + dz_sq);
-                       moi[Z] += ONE_TWELFTH*mass*(Lx_sq + Ly_sq) + 
mass*(dx_sq + dy_sq);
-                       poi = &state->m_poi[state->i_axis*3];
-                       poi[X] -= mass*cmass[X]*cmass[Y];
-                       poi[Y] -= mass*cmass[X]*cmass[Z];
-                       poi[Z] -= mass*cmass[Y]*cmass[Z];
-                   }
-               }
-
-               bu_semaphore_release(state->sem_stats);
-           }
-       }
-
-       /* compute the volume of the object */
-       if (analysis_flags & ANALYSIS_VOLUMES) {
-           struct per_region_data *prd = ((struct per_region_data 
*)pp->pt_regionp->reg_udata);
-           ap->A_LEN += dist; /* add to total volume */
-           {
-               bu_semaphore_acquire(state->sem_stats);
-
-               /* add to region volume */
-               prd->r_len[state->curr_view] += dist;
-
-               /* add to object volume */
-               prd->optr->o_len[state->curr_view] += dist;
-
-               bu_semaphore_release(state->sem_stats);
-           }
-           if (debug) {
-               bu_semaphore_acquire(state->sem_worker);
-               bu_vls_printf(_ged_current_gedp->ged_result_str, "\t\tvol hit 
%s oDist:%g objVol:%g %s\n",
-                             pp->pt_regionp->reg_name, dist, 
prd->optr->o_len[state->curr_view], prd->optr->o_name);
-               bu_semaphore_release(state->sem_worker);
-           }
-
-           if (plot_volume) {
-               VJOIN1(opt, ap->a_ray.r_pt, pp->pt_outhit->hit_dist, 
ap->a_ray.r_dir);
-
-               if (ap->a_user & 1) {
-                   pl_color(plot_volume, V3ARGS(gap_color));
-               } else {
-                   pl_color(plot_volume, V3ARGS(adjAir_color));
-               }
-
-               pdv_3line(plot_volume, pt, opt);
-           }
-       }
-
-
-       /* look for two adjacent air regions */
-       if (analysis_flags & ANALYSIS_ADJ_AIR) {
-           if (last_air && pp->pt_regionp->reg_aircode &&
-               pp->pt_regionp->reg_aircode != last_air) {
-
-               double d = pp->pt_outhit->hit_dist - pp->pt_inhit->hit_dist;
-               point_t aapt;
-
-               bu_semaphore_acquire(state->sem_lists);
-               add_unique_pair(&adjAirList, pp->pt_back->pt_regionp, 
pp->pt_regionp, 0.0, pt);
-               bu_semaphore_release(state->sem_lists);
-
-
-               d *= 0.25;
-               VJOIN1(aapt, pt, d, ap->a_ray.r_dir);
-
-               pl_color(plot_adjair, V3ARGS(adjAir_color));
-               pdv_3line(plot_adjair, pt, aapt);
-           }
-       }
-
-       /* note that this region has been seen */
-       ((struct per_region_data *)pp->pt_regionp->reg_udata)->hits++;
-
-       last_air = pp->pt_regionp->reg_aircode;
-       last_out_dist = pp->pt_outhit->hit_dist;
-       VJOIN1(last_out_point, ap->a_ray.r_pt, pp->pt_outhit->hit_dist, 
ap->a_ray.r_dir);
-    }
-
-
-    if (analysis_flags & ANALYSIS_EXP_AIR && last_air) {
-       /* the last thing we hit was air.  Make a note of that */
-       pp = PartHeadp->pt_back;
-
-       _gqa_exposed_air(ap, pp, last_out_point, pt, opt);
-    }
-
-
-    /* This value is returned by rt_shootray a hit usually returns 1,
-     * miss 0.
-     */
-    return 1;
-}
-
-
-/**
- * rt_shootray() was told to call this on a miss.
- *
- * This routine must be prepared to run in parallel
- */
-int
-miss(struct application *ap)
-{
-    RT_CK_APPLICATION(ap);
-
-    return 0;
-}
-
-
-/**
- * This routine must be prepared to run in parallel
- */
-int
-get_next_row(struct cstate *state)
-{
-    int v;
-    /* look for more work */
-    bu_semaphore_acquire(state->sem_worker);
-
-    if (state->v < state->steps[state->v_axis])
-       v = state->v++; /* get a row to work on */
-    else
-       v = 0; /* signal end of work */
-
-    bu_semaphore_release(state->sem_worker);
-
-    return v;
-}
-
-
-/**
- * This routine must be prepared to run in parallel
- */
-void
-plane_worker(int cpu, void *ptr)
-{
-    struct application ap;
-    int u, v;
-    double v_coord;
-    struct cstate *state = (struct cstate *)ptr;
-    unsigned long shot_cnt;
-
-    if (aborted)
-       return;
-
-    RT_APPLICATION_INIT(&ap);
-    ap.a_rt_i = (struct rt_i *)state->rtip;    /* application uses this 
instance */
-    ap.a_hit = hit;    /* where to go on a hit */
-    ap.a_miss = miss;  /* where to go on a miss */
-    ap.a_logoverlap = logoverlap;
-    ap.a_overlap = _gqa_overlap;
-    ap.a_resource = &state->resp[cpu];
-    ap.A_LENDEN = 0.0; /* really the cumulative length*density for weight 
computation*/
-    ap.A_LEN = 0.0;    /* really the cumulative length for volume computation 
*/
-
-    /* gross hack */
-    ap.a_ray.r_dir[state->u_axis] = ap.a_ray.r_dir[state->v_axis] = 0.0;
-    ap.a_ray.r_dir[state->i_axis] = 1.0;
-
-    ap.A_STATE = ptr; /* really copying the state ptr to the a_uptr */
-
-    u = -1;
-
-    v = get_next_row(state);
-
-    shot_cnt = 0;
-    while (v) {
-
-       v_coord = v * gridSpacing;
-       if (debug) {
-           bu_semaphore_acquire(state->sem_worker);
-           bu_vls_printf(_ged_current_gedp->ged_result_str, "  v = %d 
v_coord=%g\n", v, v_coord);
-           bu_semaphore_release(state->sem_worker);
-       }
-
-       if ((v&1) || state->first) {
-           /* shoot all the rays in this row.  This is either the
-            * first time a view has been computed or it is an odd
-            * numbered row in a grid refinement
-            */
-           for (u=1; u < state->steps[state->u_axis]; u++) {
-               ap.a_ray.r_pt[state->u_axis] = 
ap.a_rt_i->mdl_min[state->u_axis] + u*gridSpacing;
-               ap.a_ray.r_pt[state->v_axis] = 
ap.a_rt_i->mdl_min[state->v_axis] + v*gridSpacing;
-               ap.a_ray.r_pt[state->i_axis] = 
ap.a_rt_i->mdl_min[state->i_axis];
-
-               if (debug) {
-                   bu_semaphore_acquire(state->sem_worker);
-                   bu_vls_printf(_ged_current_gedp->ged_result_str, "%5g %5g 
%5g -> %g %g %g\n", V3ARGS(ap.a_ray.r_pt),
-                                 V3ARGS(ap.a_ray.r_dir));
-                   bu_semaphore_release(state->sem_worker);
-               }
-               ap.a_user = v;
-               (void)rt_shootray(&ap);
-
-               if (aborted)
-                   return;
-
-               shot_cnt++;
-           }
-       } else {
-           /* shoot only the rays we need to on this row.  Some of
-            * them have been computed in a previous iteration.
-            */
-           for (u=1; u < state->steps[state->u_axis]; u+=2) {
-               ap.a_ray.r_pt[state->u_axis] = 
ap.a_rt_i->mdl_min[state->u_axis] + u*gridSpacing;
-               ap.a_ray.r_pt[state->v_axis] = 
ap.a_rt_i->mdl_min[state->v_axis] + v*gridSpacing;
-               ap.a_ray.r_pt[state->i_axis] = 
ap.a_rt_i->mdl_min[state->i_axis];
-
-               if (debug) {
-                   bu_semaphore_acquire(state->sem_worker);
-                   bu_vls_printf(_ged_current_gedp->ged_result_str, "%5g %5g 
%5g -> %g %g %g\n", V3ARGS(ap.a_ray.r_pt),
-                                 V3ARGS(ap.a_ray.r_dir));
-                   bu_semaphore_release(state->sem_worker);
-               }
-               ap.a_user = v;
-               (void)rt_shootray(&ap);
-
-               if (aborted)
-                   return;
-
-               shot_cnt++;
-
-               if (debug) {
-                   if (u+1 < state->steps[state->u_axis]) {
-                       bu_semaphore_acquire(state->sem_worker);
-                       bu_vls_printf(_ged_current_gedp->ged_result_str, "  
---\n");
-                       bu_semaphore_release(state->sem_worker);
-                   }
-               }
-           }
-       }
-
-       /* iterate */
-       v = get_next_row(state);
-    }
-
-    if (debug && (u == -1)) {
-       bu_semaphore_acquire(state->sem_worker);
-       bu_vls_printf(_ged_current_gedp->ged_result_str, "didn't shoot any 
rays\n");
-       bu_semaphore_release(state->sem_worker);
-    }
-
-    /* There's nothing else left to work on in this view.  It's time
-     * to add the values we have accumulated to the totals for the
-     * view and return.  When all threads have been through here,
-     * we'll have returned to serial computation.
-     */
-    bu_semaphore_acquire(state->sem_stats);
-    state->shots[state->curr_view] += shot_cnt;
-    state->m_lenDensity[state->curr_view] += ap.A_LENDEN; /* add our 
length*density value */
-    state->m_len[state->curr_view] += ap.A_LEN; /* add our volume value */
-    bu_semaphore_release(state->sem_stats);
-}
-
-
-int
-find_cmd_line_obj(struct per_obj_data *obj_rpt, const char *name)
-{
-    int i;
-    char *str = bu_strdup(name);
-    char *p;
-
-    p = strchr(str, '/');
-    if (p) {
-       *p = '\0';
-    }
-
-    for (i = 0; i < num_objects; i++) {
-       if (BU_STR_EQUAL(obj_rpt[i].o_name, str)) {
-           bu_free(str, "");
-           return i;
-       }
-    }
-
-    bu_vls_printf(_ged_current_gedp->ged_result_str, "%s Didn't find object 
named \"%s\" in %d entries\n", CPP_FILELINE, name, num_objects);
-
-    return -1;
-}
-
-
-/**
- * Allocate data structures for tracking statistics on a per-view
- * basis for each of the view, object and region levels.
- */
-void
-allocate_per_region_data(struct cstate *state, int start, int ac, const char 
*av[])
-{
-    struct region *regp;
-    struct rt_i *rtip = state->rtip;
-    int i;
-    int m;
-    int index;
-
-    if (start > ac) {
-       /* what? */
-       bu_log("WARNING: Internal error (start:%d > ac:%d).\n", start, ac);
-       return;
-    }
-
-    if (num_objects < 1) {
-       /* what?? */
-       bu_log("WARNING: No objects remaining.\n");
-       return;
-    }
-
-    if (num_views == 0) {
-       /* crap. */
-       bu_log("WARNING: No views specified.\n");
-       return;
-    }
-
-    if (rtip->nregions == 0) {
-       /* dammit! */
-       bu_log("WARNING: No regions remaining.\n");
-       return;
-    }
-
-    state->m_lenDensity = (double *)bu_calloc(num_views, sizeof(double), 
"densityLen");
-    state->m_len = (double *)bu_calloc(num_views, sizeof(double), "volume");
-    state->m_volume = (double *)bu_calloc(num_views, sizeof(double), "volume");
-    state->m_weight = (double *)bu_calloc(num_views, sizeof(double), "volume");
-    state->shots = (unsigned long *)bu_calloc(num_views, sizeof(unsigned 
long), "volume");
-    state->m_lenTorque = (fastf_t *)bu_calloc(num_views, sizeof(vect_t), 
"lenTorque");
-    state->m_moi = (fastf_t *)bu_calloc(num_views, sizeof(vect_t), "moments of 
inertia");
-    state->m_poi = (fastf_t *)bu_calloc(num_views, sizeof(vect_t), "products 
of inertia");
-
-    /* build data structures for the list of objects the user
-     * specified on the command line
-     */
-    obj_tbl = (struct per_obj_data *)bu_calloc(num_objects, sizeof(struct 
per_obj_data), "report tables");
-    for (i = 0; i < num_objects; i++) {
-       obj_tbl[i].o_name = (char *)av[start+i];
-       obj_tbl[i].o_len = (double *)bu_calloc(num_views, sizeof(double), 
"o_len");
-       obj_tbl[i].o_lenDensity = (double *)bu_calloc(num_views, 
sizeof(double), "o_lenDensity");
-       obj_tbl[i].o_volume = (double *)bu_calloc(num_views, sizeof(double), 
"o_volume");
-       obj_tbl[i].o_weight = (double *)bu_calloc(num_views, sizeof(double), 
"o_weight");
-       obj_tbl[i].o_lenTorque = (fastf_t *)bu_calloc(num_views, 
sizeof(vect_t), "lenTorque");
-       obj_tbl[i].o_moi = (fastf_t *)bu_calloc(num_views, sizeof(vect_t), 
"moments of inertia");
-       obj_tbl[i].o_poi = (fastf_t *)bu_calloc(num_views, sizeof(vect_t), 
"products of inertia");
-    }
-
-    /* build objects for each region */
-    reg_tbl = (struct per_region_data *)bu_calloc(rtip->nregions, 
sizeof(struct per_region_data), "per_region_data");
-
-
-    for (i = 0, BU_LIST_FOR (regp, region, &(rtip->HeadRegion)), i++) {
-       regp->reg_udata = &reg_tbl[i];
-
-       reg_tbl[i].r_lenDensity = (double *)bu_calloc(num_views, 
sizeof(double), "r_lenDensity");
-       reg_tbl[i].r_len = (double *)bu_calloc(num_views, sizeof(double), 
"r_len");
-       reg_tbl[i].r_volume = (double *)bu_calloc(num_views, sizeof(double), 
"len");
-       reg_tbl[i].r_weight = (double *)bu_calloc(num_views, sizeof(double), 
"len");
-
-       m = (int)strlen(regp->reg_name);
-       if (m > max_region_name_len) max_region_name_len = m;
-       index = find_cmd_line_obj(obj_tbl, &regp->reg_name[1]);
-       if (index == -1)
-           reg_tbl[i].optr = NULL;
-       else
-           reg_tbl[i].optr = &obj_tbl[index];
-    }
-}
-
-
-/**
- * list_report
- */
-void
-list_report(struct region_pair *list)
-{
-    struct region_pair *rp;
-
-    if (BU_LIST_IS_EMPTY(&list->l)) {
-       bu_vls_printf(_ged_current_gedp->ged_result_str, "No %s\n", (char 
*)list->r.name);
-
-       return;
-    }
-
-    bu_vls_printf(_ged_current_gedp->ged_result_str, "list %s:\n", (char 
*)list->r.name);
-
-    for (BU_LIST_FOR (rp, region_pair, &(list->l))) {
-       if (rp->r2) {
-           bu_vls_printf(_ged_current_gedp->ged_result_str, "%s %s count:%lu 
dist:%g%s @ (%g %g %g)\n",
-                         rp->r.r1->reg_name, rp->r2->reg_name, rp->count,
-                         rp->max_dist / units[LINE]->val, units[LINE]->name, 
V3ARGS(rp->coord));
-       } else {
-           bu_vls_printf(_ged_current_gedp->ged_result_str, "%s count:%lu 
dist:%g%s @ (%g %g %g)\n",
-                         rp->r.r1->reg_name, rp->count,
-                         rp->max_dist / units[LINE]->val, units[LINE]->name, 
V3ARGS(rp->coord));
-       }
-    }
-}
-
-
-/**
- * Do some computations prior to raytracing based upon options the
- * user has specified
- *
- * Returns:
- * 0 continue, ready to go
- * !0 error encountered, terminate processing
- */
-int
-options_prep(struct rt_i *UNUSED(rtip), vect_t span)
-{
-    double newGridSpacing = gridSpacing;
-    int axis;
-
-    /* figure out where the density values are coming from and get
-     * them.
-     */
-    if (analysis_flags & ANALYSIS_WEIGHTS) {
-       if (densityFileName) {
-           DLOG(_ged_current_gedp->ged_result_str, "density from file\n");
-           if (_ged_read_densities(&_gd_densities, &_gd_densities_source, 
_ged_current_gedp, densityFileName, 0) != GED_OK) {
-               return GED_ERROR;
-           }
-       } else {
-           DLOG(_ged_current_gedp->ged_result_str, "density from db\n");
-           if (_ged_read_densities(&_gd_densities, &_gd_densities_source, 
_ged_current_gedp, NULL, 0) != GED_OK) {
-               return GED_ERROR;
-           }
-       }
-    }
-    /* refine the grid spacing if the user has set a lower bound on
-     * the number of rays per model axis
-     */
-    for (axis=0; axis < 3; axis++) {
-       if (span[axis] < newGridSpacing*Samples_per_model_axis) {
-           /* along this axis, the gridSpacing is larger than the
-            * model span.  We need to refine.
-            */
-           newGridSpacing = span[axis] / Samples_per_model_axis;
-       }
-    }
-
-    if (!ZERO(newGridSpacing - gridSpacing)) {
-       bu_log("Initial grid spacing %g %s does not allow %g samples per 
axis.\n",
-                     gridSpacing / units[LINE]->val, units[LINE]->name, 
Samples_per_model_axis - 1);
-
-       bu_log("Adjusted initial grid spacing to %g %s to get %g samples per 
model axis.\n",
-                     newGridSpacing / units[LINE]->val, units[LINE]->name, 
Samples_per_model_axis);
-
-       gridSpacing = newGridSpacing;
-    }
-
-    /* if the vol/weight tolerances are not set, pick something */
-    if (analysis_flags & ANALYSIS_VOLUMES) {
-       if (volume_tolerance < 0.0) {
-           /* using 1/1000th the volume as a default tolerance, no particular 
reason */
-           volume_tolerance = span[X] * span[Y] * span[Z] * 0.001;
-           bu_log("Using estimated volume tolerance %g %s\n", volume_tolerance 
/ units[VOL]->val, units[VOL]->name);
-       } else
-           bu_log("Using volume tolerance %g %s\n", volume_tolerance / 
units[VOL]->val, units[VOL]->name);
-       if (plot_prefix) {
-           struct bu_vls vp = BU_VLS_INIT_ZERO;
-           bu_vls_printf(&vp, "%svolume.plot3", plot_prefix);
-           bu_log("Plotting volumes to %s\n", bu_vls_cstr(&vp));
-           plot_volume = fopen(bu_vls_cstr(&vp), "wb");
-           if (plot_volume == (FILE *)NULL) {
-               bu_vls_printf(_ged_current_gedp->ged_result_str, "cannot open 
plot file %s\n", bu_vls_cstr(&vp));
-               /* not a critical failure */
-           }
-           bu_vls_free(&vp);
-       }
-    }
-    if (analysis_flags & ANALYSIS_WEIGHTS) {
-       if (weight_tolerance < 0.0) {
-           double max_den = 0.0;
-           long int curr_id = -1;
-           while ((curr_id = analyze_densities_next(_gd_densities, curr_id)) 
!= -1) {
-               if (analyze_densities_density(_gd_densities, curr_id) > max_den)
-                   max_den = analyze_densities_density(_gd_densities, curr_id);
-           }
-           weight_tolerance = span[X] * span[Y] * span[Z] * 0.1 * max_den;
-           bu_vls_printf(_ged_current_gedp->ged_result_str, "setting weight 
tolerance to %g %s\n",
-                         weight_tolerance / units[WGT]->val,
-                         units[WGT]->name);
-       } else {
-           bu_vls_printf(_ged_current_gedp->ged_result_str, "weight tolerance  
 %g\n", weight_tolerance);
-       }
-    }
-    if (analysis_flags & ANALYSIS_GAPS) {
-       if (plot_prefix) {
-           struct bu_vls vp = BU_VLS_INIT_ZERO;
-           bu_vls_printf(&vp, "%sgaps.plot3", plot_prefix);
-           bu_log("Plotting gaps to %s\n", bu_vls_cstr(&vp));
-           plot_gaps = fopen(bu_vls_cstr(&vp), "wb");
-           if (plot_gaps == (FILE *)NULL) {
-               bu_vls_printf(_ged_current_gedp->ged_result_str, "cannot open 
plot file %s\n", bu_vls_cstr(&vp));
-               /* not a critical failure */
-           }
-           bu_vls_free(&vp);
-       }
-    }
-    if (analysis_flags & ANALYSIS_OVERLAPS) {
-       if (!ZERO(overlap_tolerance))
-           bu_vls_printf(_ged_current_gedp->ged_result_str, "overlap tolerance 
to %g\n", overlap_tolerance);
-       if (plot_prefix) {
-           struct bu_vls vp = BU_VLS_INIT_ZERO;
-           bu_vls_printf(&vp, "%soverlaps.plot3", plot_prefix);
-           bu_log("Plotting overlaps to %s\n", bu_vls_cstr(&vp));
-           plot_overlaps = fopen(bu_vls_cstr(&vp), "wb");
-           if (plot_overlaps == (FILE *)NULL) {
-               bu_vls_printf(_ged_current_gedp->ged_result_str, "cannot open 
plot file %s\n", bu_vls_cstr(&vp));
-               /* not a critical failure */
-           }
-           bu_vls_free(&vp);
-       }
-    }
-
-    if (print_per_region_stats)
-       if ((analysis_flags & (ANALYSIS_VOLUMES|ANALYSIS_WEIGHTS)) == 0)
-           bu_vls_printf(_ged_current_gedp->ged_result_str, "Note: -r option 
ignored: neither volume or weight options requested\n");
-
-    if (analysis_flags & ANALYSIS_ADJ_AIR)
-       if (plot_prefix) {
-           struct bu_vls vp = BU_VLS_INIT_ZERO;
-           bu_vls_printf(&vp, "%sadj_air.plot3", plot_prefix);
-           bu_log("Plotting adjacent air to %s\n", bu_vls_cstr(&vp));
-           plot_adjair = fopen(bu_vls_cstr(&vp), "wb");
-           if (plot_adjair == (FILE *)NULL) {
-               bu_vls_printf(_ged_current_gedp->ged_result_str, "cannot open 
plot file %s\n", bu_vls_cstr(&vp));
-               /* not a critical failure */
-           }
-           bu_vls_free(&vp);
-       }
-
-    if (analysis_flags & ANALYSIS_EXP_AIR)
-       if (plot_prefix) {
-           struct bu_vls vp = BU_VLS_INIT_ZERO;
-           bu_vls_printf(&vp, "%sexp_air.plot3", plot_prefix);
-           bu_log("Plotting exposed air to %s\n", bu_vls_cstr(&vp));
-           plot_expair = fopen(bu_vls_cstr(&vp), "wb");
-           if (plot_expair == (FILE *)NULL) {
-               bu_vls_printf(_ged_current_gedp->ged_result_str, "cannot open 
plot file %s\n", bu_vls_cstr(&vp));
-               /* not a critical failure */
-           }
-           bu_vls_free(&vp);
-       }
-
-
-    if ((analysis_flags & (ANALYSIS_ADJ_AIR|ANALYSIS_EXP_AIR)) && ! use_air) {
-       bu_vls_printf(_ged_current_gedp->ged_result_str, "Error:  Air regions 
discarded but air analysis requested!\nSet use_air non-zero or eliminate air 
analysis\n");
-       return GED_ERROR;
-    }
-
-    return GED_OK;
-}
-
-
-void
-view_reports(struct cstate *state)
-{
-    if (analysis_flags & ANALYSIS_VOLUMES) {
-       int obj;
-       int view;
-
-       /* for each object, compute the volume for all views */
-       for (obj = 0; obj < num_objects; obj++) {
-           double val;
-           /* compute volume of object for given view */
-           view = state->curr_view;
-
-           /* compute the per-view volume of this object */
-
-           if (state->shots[view] > 0) {
-               val = obj_tbl[obj].o_volume[view] =
-                   obj_tbl[obj].o_len[view] * (state->area[view] / 
state->shots[view]);
-
-               if (verbose)
-                   bu_vls_printf(_ged_current_gedp->ged_result_str, "\t%s 
volume %g %s\n",
-                                 obj_tbl[obj].o_name,
-                                 val / units[VOL]->val,
-                                 units[VOL]->name);
-           }
-       }
-    }
-    if (analysis_flags & ANALYSIS_WEIGHTS) {
-       int obj;
-       int view = state->curr_view;
-
-       for (obj = 0; obj < num_objects; obj++) {
-           double grams_per_cu_mm = obj_tbl[obj].o_lenDensity[view] *
-               (state->area[view] / state->shots[view]);
-
-
-           if (verbose)
-               bu_vls_printf(_ged_current_gedp->ged_result_str, "\t%s %g %s\n",
-                             obj_tbl[obj].o_name,
-                             grams_per_cu_mm / units[WGT]->val,
-                             units[WGT]->name);
-       }
-    }
-}
-
-
-/**
- * These checks are unique because they must both be completed.  Early
- * termination before they are done is not an option.  The results
- * computed here are used later.
- *
- * Returns:
- * 0 terminate
- * 1 continue processing
- */
-static int
-weight_volume_terminate(struct cstate *state)
-{
-    /* Both weight and volume computations rely on this routine to
-     * compute values that are printed in summaries.  Hence, both
-     * checks must always be done before this routine exits.  So we
-     * store the status (can we terminate processing?) in this
-     * variable and act on it once both volume and weight computations
-     * are done.
-     */
-    int can_terminate = 1;
-
-    double low, hi, val, delta;
-
-    if (analysis_flags & ANALYSIS_WEIGHTS) {
-       /* for each object, compute the weight for all views */
-       int obj;
-
-       for (obj = 0; obj < num_objects; obj++) {
-           int view;
-           double tmp;
-
-           if (verbose)
-               bu_vls_printf(_ged_current_gedp->ged_result_str, "object %d\n", 
obj);
-
-           /* compute weight of object for given view */
-           low = INFINITY;
-           hi = -INFINITY;
-           tmp = 0.0;
-           for (view = 0; view < num_views; view++) {
-               val = obj_tbl[obj].o_weight[view] =
-                   obj_tbl[obj].o_lenDensity[view] * (state->area[view] / 
state->shots[view]);
-               V_MIN(low, val);
-               V_MAX(hi, val);
-               tmp += val;
-           }
-           delta = hi - low;
-
-           if (verbose)
-               bu_vls_printf(_ged_current_gedp->ged_result_str,
-                   "\t%s running avg weight %g %s hi=(%g) low=(%g)\n",
-                   obj_tbl[obj].o_name,
-                   (tmp / num_views) / units[WGT]->val,
-                   units[WGT]->name,
-                   hi / units[WGT]->val,
-                   low / units[WGT]->val);
-
-           if (delta > weight_tolerance) {
-               /* this object differs too much in each view, so we
-                * need to refine the grid. signal that we cannot
-                * terminate.
-                */
-               can_terminate = 0;
-               if (verbose)
-                   bu_vls_printf(_ged_current_gedp->ged_result_str, "\t%s 
differs too much in weight per view.\n",
-                                 obj_tbl[obj].o_name);
-           }
-       }
-       if (can_terminate) {
-           if (verbose)
-               bu_vls_printf(_ged_current_gedp->ged_result_str, "all objects 
within tolerance on weight calculation\n");
-       }
-    }
-
-    if (analysis_flags & ANALYSIS_VOLUMES) {
-       /* find the range of values for object volumes */
-       int obj;
-
-       /* for each object, compute the volume for all views */
-       for (obj = 0; obj < num_objects; obj++) {
-           int view;
-           double tmp;
-
-           /* compute volume of object for given view */
-           low = INFINITY;
-           hi = -INFINITY;
-           tmp = 0.0;
-           for (view = 0; view < num_views; view++) {
-               val = obj_tbl[obj].o_volume[view] =
-                   obj_tbl[obj].o_len[view] * (state->area[view] / 
state->shots[view]);
-               V_MIN(low, val);
-               V_MAX(hi, val);
-               tmp += val;
-           }
-           delta = hi - low;
-
-           if (verbose)
-               bu_vls_printf(_ged_current_gedp->ged_result_str,
-                   "\t%s running avg volume %g %s hi=(%g) low=(%g)\n",
-                   obj_tbl[obj].o_name,
-                   (tmp / num_views) / units[VOL]->val, units[VOL]->name,
-                   hi / units[VOL]->val,
-                   low / units[VOL]->val);
-
-           if (delta > volume_tolerance) {
-               /* this object differs too much in each view, so we
-                * need to refine the grid.
-                */
-               can_terminate = 0;
-               if (verbose)
-                   bu_vls_printf(_ged_current_gedp->ged_result_str, "\tvolume 
tol not met on %s.  Refine grid\n",
-                                 obj_tbl[obj].o_name);
-               break;
-           }
-       }
-    }
-
-    if (can_terminate) {
-       return 0; /* signal we don't want to go onward */
-    }
-    return 1;
-}
-
-
-/**
- * Check to see if we are done processing due to some user specified
- * limit being achieved.
- *
- * Returns:
- * 0 Terminate
- * 1 Continue processing
- */
-int
-terminate_check(struct cstate *state)
-{
-    int wv_status;
-    int view;
-    int obj;
-
-    DLOG(_ged_current_gedp->ged_result_str, "terminate_check\n");
-    RT_CK_RTI(state->rtip);
-
-    if (plot_overlaps) fflush(plot_overlaps);
-    if (plot_weight) fflush(plot_weight);
-    if (plot_volume) fflush(plot_volume);
-    if (plot_adjair) fflush(plot_adjair);
-    if (plot_gaps) fflush(plot_gaps);
-    if (plot_expair) fflush(plot_expair);
-
-    /* this computation is done first, because there are side effects
-     * that must be obtained whether we terminate or not
-     */
-    wv_status = weight_volume_terminate(state);
-
-
-    /* if we've reached the grid limit, we're done, no matter what */
-    if (gridSpacing < gridSpacingLimit) {
-       bu_vls_printf(_ged_current_gedp->ged_result_str, "NOTE: Stopped, grid 
spacing refined to %g (below lower limit %g).\n",
-           gridSpacing, gridSpacingLimit);
-       return 0;
-    }
-
-    /* if we are doing one of the "Error" checking operations:
-     * Overlap, gap, adj_air, exp_air, then we ALWAYS go to the grid
-     * spacing limit and we ALWAYS terminate on first error/list-entry
-     */
-    if ((analysis_flags & ANALYSIS_OVERLAPS)) {
-       if (BU_LIST_NON_EMPTY(&overlapList.l)) {
-           /* since we've found an overlap, we can quit */
-           return 0;
-       } else {
-           bu_vls_printf(_ged_current_gedp->ged_result_str, "overlaps list at 
%gmm is empty\n", gridSpacing / GRIDSPACING_STEP);
-       }
-    }
-    if ((analysis_flags & ANALYSIS_GAPS)) {
-       if (BU_LIST_NON_EMPTY(&gapList.l)) {
-           /* since we've found a gap, we can quit */
-           return 0;
-       }
-    }
-    if ((analysis_flags & ANALYSIS_ADJ_AIR)) {
-       if (BU_LIST_NON_EMPTY(&adjAirList.l)) {
-           /* since we've found adjacent air, we can quit */
-           return 0;
-       }
-    }
-    if ((analysis_flags & ANALYSIS_EXP_AIR)) {
-       if (BU_LIST_NON_EMPTY(&exposedAirList.l)) {
-           /* since we've found exposed air, we can quit */
-           return 0;
-       }
-    }
-
-
-    if (analysis_flags & (ANALYSIS_WEIGHTS|ANALYSIS_VOLUMES)) {
-       /* volume/weight checks only get to terminate processing if
-        * there are no "error" check computations being done
-        */
-       if (analysis_flags & 
(ANALYSIS_GAPS|ANALYSIS_ADJ_AIR|ANALYSIS_OVERLAPS|ANALYSIS_EXP_AIR)) {
-           if (verbose)
-               bu_vls_printf(_ged_current_gedp->ged_result_str, "Volume/Weight 
tolerance met.  Cannot terminate calculation due to error computations\n");
-       } else {
-           struct region *regp;
-           int all_hit = 1;
-           size_t hits;
-
-           if (require_num_hits > 0) {
-               /* check to make sure every region was hit at least once */
-               for (BU_LIST_FOR (regp, region, &(state->rtip->HeadRegion))) {
-                   RT_CK_REGION(regp);
-
-                   hits = (size_t)((struct per_region_data 
*)regp->reg_udata)->hits;
-                   if (hits < require_num_hits) {
-                       all_hit = 0;
-                       if (verbose) {
-                           if (hits == 0 && !quiet_missed_report) {
-                               
bu_vls_printf(_ged_current_gedp->ged_result_str, "%s was not hit\n", 
regp->reg_name);
-                           } else if (hits) {
-                               
bu_vls_printf(_ged_current_gedp->ged_result_str, "%s hit only %zu times (< 
%zu)\n",
-                                             regp->reg_name, hits, 
require_num_hits);
-                           }
-                       }
-                   }
-               }
-
-               if (all_hit && wv_status == 0) {
-                   if (verbose)
-                       bu_vls_printf(_ged_current_gedp->ged_result_str, "%s: 
Volume/Weight tolerance met. Terminate\n", CPP_FILELINE);
-                   return 0; /* terminate */
-               }
-           } else {
-               if (wv_status == 0) {
-                   if (verbose)
-                       bu_vls_printf(_ged_current_gedp->ged_result_str, "%s: 
Volume/Weight tolerance met. Terminate\n", CPP_FILELINE);
-                   return 0; /* terminate */
-               }
-           }
-       }
-    }
-
-    for (view=0; view < num_views; view++) {
-       for (obj = 0; obj < num_objects; obj++) {
-           VSCALE(&obj_tbl[obj].o_moi[view*3], &obj_tbl[obj].o_moi[view*3], 
0.25);
-           VSCALE(&obj_tbl[obj].o_poi[view*3], &obj_tbl[obj].o_poi[view*3], 
0.25);
-       }
-
-       VSCALE(&state->m_moi[view*3], &state->m_moi[view*3], 0.25);
-       VSCALE(&state->m_poi[view*3], &state->m_poi[view*3], 0.25);
-    }
-
-    return 1;
-}
-
-
-/**
- * summary_reports
- */
-void
-summary_reports(struct cstate *state)
-{
-    int view;
-    int obj;
-    double avg_mass;
-    struct region *regp;
-
-    if (multiple_analyses)
-       bu_vls_printf(_ged_current_gedp->ged_result_str, "Summaries (%gmm grid 
spacing):\n", gridSpacing / GRIDSPACING_STEP);
-    else
-       bu_vls_printf(_ged_current_gedp->ged_result_str, "Summary (%gmm grid 
spacing):\n", gridSpacing / GRIDSPACING_STEP);
-
-    if (analysis_flags & ANALYSIS_WEIGHTS) {
-       bu_vls_printf(_ged_current_gedp->ged_result_str, "Weight:\n");
-       for (obj = 0; obj < num_objects; obj++) {
-           avg_mass = 0.0;
-
-           for (view=0; view < num_views; view++) {
-               /* computed in terminate_check() */
-               avg_mass += obj_tbl[obj].o_weight[view];
-           }
-           avg_mass /= num_views;
-           bu_vls_printf(_ged_current_gedp->ged_result_str, "\t%*s %g %s\n", 
-max_region_name_len, obj_tbl[obj].o_name,
-                         avg_mass / units[WGT]->val, units[WGT]->name);
-
-           if (analysis_flags & ANALYSIS_CENTROIDS &&
-               !ZERO(avg_mass)) {
-               vect_t centroid = VINIT_ZERO;
-               fastf_t Dx_sq, Dy_sq, Dz_sq;
-               fastf_t inv_total_mass = 1.0/avg_mass;
-
-               for (view=0; view < num_views; view++) {
-                   vect_t torque;
-                   fastf_t cell_area = state->area[view] / state->shots[view];
-
-                   VSCALE(torque, &obj_tbl[obj].o_lenTorque[view*3], 
cell_area);
-                   VADD2(centroid, centroid, torque);
-               }
-
-               VSCALE(centroid, centroid, 1.0/(fastf_t)num_views);
-               VSCALE(centroid, centroid, inv_total_mass);
-               bu_vls_printf(_ged_current_gedp->ged_result_str,
-                             "\t\tcentroid: (%g %g %g) mm\n", 
V3ARGS(centroid));
-
-               /* Do the final calculations for the moments of
-                * inertia for the current object.
-                */
-               if (analysis_flags & ANALYSIS_MOMENTS) {
-                   struct bu_vls title = BU_VLS_INIT_ZERO;
-                   mat_t tmat; /* total mat */
-
-                   MAT_ZERO(tmat);
-                   for (view=0; view < num_views; view++) {
-                       vectp_t moi = &obj_tbl[obj].o_moi[view*3];
-                       vectp_t poi = &obj_tbl[obj].o_poi[view*3];
-
-                       tmat[MSX] += moi[X];
-                       tmat[MSY] += moi[Y];
-                       tmat[MSZ] += moi[Z];
-                       tmat[1] += poi[X];
-                       tmat[2] += poi[Y];
-                       tmat[6] += poi[Z];
-                   }
-
-                   tmat[MSX] /= (fastf_t)num_views;
-                   tmat[MSY] /= (fastf_t)num_views;
-                   tmat[MSZ] /= (fastf_t)num_views;
-                   tmat[1] /= (fastf_t)num_views;
-                   tmat[2] /= (fastf_t)num_views;
-                   tmat[6] /= (fastf_t)num_views;
-
-                   /* Lastly, apply the parallel axis theorem */
-                   Dx_sq = centroid[X]*centroid[X];
-                   Dy_sq = centroid[Y]*centroid[Y];
-                   Dz_sq = centroid[Z]*centroid[Z];
-                   tmat[MSX] -= avg_mass*(Dy_sq + Dz_sq);
-                   tmat[MSY] -= avg_mass*(Dx_sq + Dz_sq);
-                   tmat[MSZ] -= avg_mass*(Dx_sq + Dy_sq);
-                   tmat[1] += avg_mass*centroid[X]*centroid[Y];
-                   tmat[2] += avg_mass*centroid[X]*centroid[Z];
-                   tmat[6] += avg_mass*centroid[Y]*centroid[Z];
-
-                   tmat[4] = tmat[1];
-                   tmat[8] = tmat[2];
-                   tmat[9] = tmat[6];
-
-                   bu_vls_printf(&title, "For the Moments and Products of 
Inertia For %s", obj_tbl[obj].o_name);
-                   bn_mat_print_vls(bu_vls_addr(&title), tmat, 
_ged_current_gedp->ged_result_str);
-                   bu_vls_free(&title);
-               }
-           }
-       }
-
-
-       if (print_per_region_stats) {
-           double *wv;
-           bu_vls_printf(_ged_current_gedp->ged_result_str, "\tregions:\n");
-           for (BU_LIST_FOR (regp, region, &(state->rtip->HeadRegion))) {
-               double low = INFINITY;
-               double hi = -INFINITY;
-
-               avg_mass = 0.0;
-
-               for (view=0; view < num_views; view++) {
-                   wv = &((struct per_region_data 
*)regp->reg_udata)->r_weight[view];
-
-                   *wv = ((struct per_region_data 
*)regp->reg_udata)->r_lenDensity[view] *
-                       (state->area[view]/state->shots[view]);
-
-                   *wv /= units[WGT]->val;
-
-                   avg_mass += *wv;
-
-                   if (*wv < low) low = *wv;
-                   if (*wv > hi) hi = *wv;
-               }
-
-               avg_mass /= num_views;
-               bu_vls_printf(_ged_current_gedp->ged_result_str, "\t%s %g %s 
+(%g) -(%g)\n",
-                             regp->reg_name,
-                             avg_mass,
-                             units[WGT]->name,
-                             hi - avg_mass,
-                             avg_mass - low);
-           }
-       }
-
-       /* print grand totals */
-       avg_mass = 0.0;
-       for (view=0; view < num_views; view++) {
-           avg_mass += state->m_weight[view] =
-               state->m_lenDensity[view] *
-               (state->area[view] / state->shots[view]);
-       }
-
-       avg_mass /= num_views;
-       bu_vls_printf(_ged_current_gedp->ged_result_str, "  Average total 
weight: %g %s\n", avg_mass / units[WGT]->val, units[WGT]->name);
-
-       if (analysis_flags & ANALYSIS_CENTROIDS &&
-           !ZERO(avg_mass)) {
-           vect_t centroid = VINIT_ZERO;
-           fastf_t Dx_sq, Dy_sq, Dz_sq;
-           fastf_t inv_total_mass = 1.0/avg_mass;
-
-           for (view=0; view < num_views; view++) {
-               vect_t torque;
-               fastf_t cell_area = state->area[view] / state->shots[view];
-
-               VSCALE(torque, &state->m_lenTorque[view*3], cell_area);
-               VADD2(centroid, centroid, torque);
-           }
-
-           VSCALE(centroid, centroid, 1.0/(fastf_t)num_views);
-           VSCALE(centroid, centroid, inv_total_mass);
-           bu_vls_printf(_ged_current_gedp->ged_result_str,
-                         "  Average centroid: (%g %g %g) mm\n", 
V3ARGS(centroid));
-
-           /* Do the final calculations for the moments of inertia
-            * for the current object.
-            */
-           if (analysis_flags & ANALYSIS_MOMENTS) {
-               mat_t tmat; /* total mat */
-
-               MAT_ZERO(tmat);
-               for (view=0; view < num_views; view++) {
-                   vectp_t moi = &state->m_moi[view*3];
-                   vectp_t poi = &state->m_poi[view*3];
-
-                   tmat[MSX] += moi[X];
-                   tmat[MSY] += moi[Y];
-                   tmat[MSZ] += moi[Z];
-                   tmat[1] += poi[X];
-                   tmat[2] += poi[Y];
-                   tmat[6] += poi[Z];
-               }
-
-               tmat[MSX] /= (fastf_t)num_views;
-               tmat[MSY] /= (fastf_t)num_views;
-               tmat[MSZ] /= (fastf_t)num_views;
-               tmat[1] /= (fastf_t)num_views;
-               tmat[2] /= (fastf_t)num_views;
-               tmat[6] /= (fastf_t)num_views;
-
-               /* Lastly, apply the parallel axis theorem */
-               Dx_sq = centroid[X]*centroid[X];
-               Dy_sq = centroid[Y]*centroid[Y];
-               Dz_sq = centroid[Z]*centroid[Z];
-               tmat[MSX] -= avg_mass*(Dy_sq + Dz_sq);
-               tmat[MSY] -= avg_mass*(Dx_sq + Dz_sq);
-               tmat[MSZ] -= avg_mass*(Dx_sq + Dy_sq);
-               tmat[1] += avg_mass*centroid[X]*centroid[Y];
-               tmat[2] += avg_mass*centroid[X]*centroid[Z];
-               tmat[6] += avg_mass*centroid[Y]*centroid[Z];
-
-               tmat[4] = tmat[1];
-               tmat[8] = tmat[2];
-               tmat[9] = tmat[6];
-
-               bn_mat_print_vls("For the Moments and Products of Inertia 
For\n\tAll Specified Objects",
-                                tmat, _ged_current_gedp->ged_result_str);
-           }
-       }
-    }
-
-
-    if (analysis_flags & ANALYSIS_VOLUMES) {
-       bu_vls_printf(_ged_current_gedp->ged_result_str, "Volume:\n");
-
-       /* print per-object */
-       for (obj = 0; obj < num_objects; obj++) {
-           avg_mass = 0.0;
-
-           for (view=0; view < num_views; view++)
-               avg_mass += obj_tbl[obj].o_volume[view];
-
-           avg_mass /= num_views;
-           bu_vls_printf(_ged_current_gedp->ged_result_str, "\t%*s %g %s\n", 
-max_region_name_len, obj_tbl[obj].o_name,
-                         avg_mass / units[VOL]->val, units[VOL]->name);
-       }
-
-       if (print_per_region_stats) {
-           double *vv;
-
-           bu_vls_printf(_ged_current_gedp->ged_result_str, "\tregions:\n");
-           for (BU_LIST_FOR (regp, region, &(state->rtip->HeadRegion))) {
-               double low = INFINITY;
-               double hi = -INFINITY;
-               avg_mass = 0.0;
-
-               for (view=0; view < num_views; view++) {
-                   vv = &((struct per_region_data 
*)regp->reg_udata)->r_volume[view];
-
-                   /* convert view length to a volume */
-                   *vv = ((struct per_region_data 
*)regp->reg_udata)->r_len[view] *
-                       (state->area[view] / state->shots[view]);
-
-                   /* convert to user's units */
-                   *vv /= units[VOL]->val;
-
-                   /* find limits of values */
-                   if (*vv < low) low = *vv;
-                   if (*vv > hi) hi = *vv;
-
-                   avg_mass += *vv;
-               }
-
-               avg_mass /= num_views;
-
-               bu_vls_printf(_ged_current_gedp->ged_result_str, "\t%s 
volume:%g %s +(%g) -(%g)\n",
-                             regp->reg_name,
-                             avg_mass,
-                             units[VOL]->name,
-                             hi - avg_mass,
-                             avg_mass - low);
-           }
-       }
-
-
-       /* print grand totals */
-       avg_mass = 0.0;
-       for (view=0; view < num_views; view++) {
-           avg_mass += state->m_volume[view] =
-               state->m_len[view] * (state->area[view] / state->shots[view]);
-       }
-
-       avg_mass /= num_views;
-       bu_vls_printf(_ged_current_gedp->ged_result_str, "  Average total 
volume: %g %s\n", avg_mass / units[VOL]->val, units[VOL]->name);
-    }
-    if (analysis_flags & ANALYSIS_OVERLAPS) list_report(&overlapList);
-    if (analysis_flags & ANALYSIS_ADJ_AIR) list_report(&adjAirList);
-    if (analysis_flags & ANALYSIS_GAPS) list_report(&gapList);
-    if (analysis_flags & ANALYSIS_EXP_AIR) list_report(&exposedAirList);
-
-    for (BU_LIST_FOR (regp, region, &(state->rtip->HeadRegion))) {
-       size_t hits;
-       struct region_pair *rp;
-       int is_overlap_only_hit;
-
-       RT_CK_REGION(regp);
-       hits = (size_t)((struct per_region_data *)regp->reg_udata)->hits;
-       if (hits < require_num_hits) {
-           if (hits == 0 && !quiet_missed_report) {
-               is_overlap_only_hit = 0;
-               if (analysis_flags & ANALYSIS_OVERLAPS) {
-                   /* If the region is in the overlap list, it has
-                    * been hit even though the hit count is zero.
-                    * Do not report zero hit regions if they are in
-                    * the overlap list.
-                    */
-                   for (BU_LIST_FOR (rp, region_pair, &(overlapList.l))) {
-                       if (rp->r.r1->reg_name == regp->reg_name) {
-                           is_overlap_only_hit = 1;
-                           break;
-                       } else if (rp->r2) {
-                           if (rp->r2->reg_name == regp->reg_name) {
-                               is_overlap_only_hit = 1;
-                               break;
-                           }
-                       }
-                   }
-               }
-               if (!is_overlap_only_hit) {
-                   bu_vls_printf(_ged_current_gedp->ged_result_str, "%s was 
not hit\n", regp->reg_name);
-               }
-           } else if (hits) {
-               bu_vls_printf(_ged_current_gedp->ged_result_str, "%s hit only 
%zu times (< %zu)\n",
-                             regp->reg_name, hits, require_num_hits);
-           }
-       }
-    }
-}
-
-
-int
-ged_gqa_core(struct ged *gedp, int argc, const char *argv[])
-{
-    int arg_count;
-    struct rt_i *rtip;
-    int i;
-    struct cstate state;
-    int start_objs; /* index in command line args where geom object list 
starts */
-    struct region_pair *rp;
-    struct region *regp;
-    static const char *usage = "object [object ...]";
-    struct resource resp[MAX_PSW];     /* memory resources for multi-cpu 
processing */
-
-    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 %s", argv[0], 
options_str, usage);
-       return GED_HELP;
-    }
-
-    _ged_current_gedp = gedp;
-
-    analysis_flags = ANALYSIS_VOLUMES | ANALYSIS_OVERLAPS | ANALYSIS_WEIGHTS |
-       ANALYSIS_EXP_AIR | ANALYSIS_ADJ_AIR | ANALYSIS_GAPS | 
ANALYSIS_CENTROIDS | ANALYSIS_MOMENTS;
-    multiple_analyses = 1;
-    azimuth_deg = 0.0;
-    elevation_deg = 0.0;
-    densityFileName = (char *)0;
-
-    /* FIXME: this is completely arbitrary, should probably be based
-     * on the model size.
-     */
-    gridSpacing = 50.0;
-
-    /* default grid spacing limit is based on the current distance
-     * tolerance, one order of magnitude greater.
-     *
-     * FIXME: should probably be based on the model size.
-     */
-    gridSpacingLimit = 10.0 * gedp->ged_wdbp->wdb_tol.dist;
-
-    makeOverlapAssemblies = 0;
-    require_num_hits = 1;
-    max_cpus = ncpu = bu_avail_cpus();
-    Samples_per_model_axis = 2.0;
-    overlap_tolerance = 0.0;
-    volume_tolerance = -1.0;
-    weight_tolerance = -1.0;
-    print_per_region_stats = 0;
-    max_region_name_len = 0;
-    use_air = 1;
-    num_objects = 0;
-    num_views = 3;
-    verbose = 0;
-    quiet_missed_report = 0;
-    plot_prefix = NULL;
-    plot_weight = (FILE *)0;
-    plot_volume = (FILE *)0;
-    plot_overlaps = (FILE *)0;
-    plot_adjair = (FILE *)0;
-    plot_gaps = (FILE *)0;
-    plot_expair = (FILE *)0;
-    debug = 0;
-
-    /* parse command line arguments */
-    arg_count = parse_args(argc, (char **)argv);
-
-    if (arg_count < 0 || (argc-arg_count) < 1) {
-       bu_vls_printf(gedp->ged_result_str, "Usage: %s %s %s", argv[0], 
options_str, usage);
-       return GED_ERROR;
-    }
-
-    if (analysis_flags & ANALYSIS_PLOT_OVERLAPS) {
-       ged_gqa_plot.vbp = rt_vlblock_init();
-       ged_gqa_plot.vhead = bn_vlblock_find(ged_gqa_plot.vbp, 0xFF, 0xFF, 
0x00);
-    }
-
-    rtip = rt_new_rti(gedp->ged_wdbp->dbip);
-    rtip->useair = use_air;
-
-    start_objs = arg_count;
-    num_objects = argc - arg_count;
-
-    /* Initialize all the per-CPU memory resources.  The number of
-     * processors can change at runtime, init them all.
-     */
-    memset(resp, 0, sizeof(resp));
-    for (i = 0; i < MAX_PSW; i++) {
-       rt_init_resource(&resp[i], i, rtip);
-    }
-    state.resp = resp;
-
-    /* Walk trees.  Here we identify any object trees in the database
-     * that the user wants included in the ray trace.
-     */
-    for (; arg_count < argc; arg_count++) {
-       if (rt_gettree(rtip, argv[arg_count]) < 0) {
-           fprintf(stderr, "rt_gettree(%s) FAILED\n", argv[arg_count]);
-           return GED_ERROR;
-       }
-    }
-
-    /* This gets the database ready for ray tracing.  (it precomputes
-     * some values, sets up space partitioning, etc.)
-     */
-    rt_prep_parallel(rtip, ncpu);
-
-    /* we now have to subdivide space */
-    VSUB2(state.span, rtip->mdl_max, rtip->mdl_min);
-    state.area[0] = state.span[1] * state.span[2];
-    state.area[1] = state.span[2] * state.span[0];
-    state.area[2] = state.span[0] * state.span[1];
-
-    if (analysis_flags & ANALYSIS_BBOX) {
-       bu_vls_printf(gedp->ged_result_str, "bounding box: %g %g %g  %g %g 
%g\n",
-                     V3ARGS(rtip->mdl_min), V3ARGS(rtip->mdl_max));
-
-       bu_vls_printf(gedp->ged_result_str, "Area: (%g, %g, %g)\n", 
state.area[X], state.area[Y], state.area[Z]);
-    }
-    if (verbose) bu_vls_printf(gedp->ged_result_str, "ncpu: %d\n", ncpu);
-
-    /* if the user did not specify the initial grid spacing limit, we
-     * need to compute a reasonable one for them.
-     */
-    if (ZERO(gridSpacing)) {
-       double min_span = MAX_FASTF;
-       VPRINT("span", state.span);
-
-       V_MIN(min_span, state.span[X]);
-       V_MIN(min_span, state.span[Y]);
-       V_MIN(min_span, state.span[Z]);
-
-       gridSpacing = gridSpacingLimit;
-       do {
-           gridSpacing *= 2.0;
-       } while (gridSpacing < min_span);
-
-       /* dial it back a little bit */
-       gridSpacing *= 0.25;
-       V_MAX(gridSpacing, gridSpacingLimit);
-
-       bu_log("Trying estimated initial grid spacing: %g %s\n",
-           gridSpacing / units[LINE]->val, units[LINE]->name);
-    } else {
-       bu_log("Trying initial grid spacing: %g %s\n",
-           gridSpacing / units[LINE]->val, units[LINE]->name);
-    }
-
-    bu_log("Using grid spacing lower limit: %g %s\n",
-           gridSpacingLimit / units[LINE]->val, units[LINE]->name);
-
-    if (options_prep(rtip, state.span) != GED_OK) return GED_ERROR;
-
-    /* initialize some stuff */
-    state.sem_worker = bu_semaphore_register("gqa_sem_worker");
-    state.sem_stats = bu_semaphore_register("gqa_sem_stats");
-    state.rtip = rtip;
-    state.first = 1;
-    allocate_per_region_data(&state, start_objs, argc, argv);
-
-    /* compute */
-    do {
-       double inv_spacing = 1.0/gridSpacing;
-       int view;
-
-       VSCALE(state.steps, state.span, inv_spacing);
-
-       bu_log("Processing with grid spacing %g %s %ld x %ld x %ld\n",
-                     gridSpacing / units[LINE]->val,
-                     units[LINE]->name,
-                     state.steps[0]-1,
-                     state.steps[1]-1,
-                     state.steps[2]-1);
-
-
-       for (view=0; view < num_views; view++) {
-
-           if (verbose)
-               bu_vls_printf(gedp->ged_result_str, "  view %d\n", view);
-
-           /* gross hack.  By assuming we have <= 3 views, we can let
-            * the view # indicate a coordinate axis.  Note this is
-            * used as an index into state.area[]
-            */
-           state.i_axis = state.curr_view = view;
-           state.u_axis = (state.curr_view+1) % 3;
-           state.v_axis = (state.curr_view+2) % 3;
-
-           state.u_dir[state.u_axis] = 1;
-           state.u_dir[state.v_axis] = 0;
-           state.u_dir[state.i_axis] = 0;
-
-           state.v_dir[state.u_axis] = 0;
-           state.v_dir[state.v_axis] = 1;
-           state.v_dir[state.i_axis] = 0;
-           state.v = 1;
-
-           bu_parallel(plane_worker, ncpu, (void *)&state);
-
-           if (aborted)
-               goto aborted;
-
-           view_reports(&state);
-       }
-
-       state.first = 0;
-       gridSpacing *= GRIDSPACING_STEP;
-
-    } while (terminate_check(&state));
-
-aborted:
-    if (plot_overlaps) fclose(plot_overlaps);
-    if (plot_weight) fclose(plot_weight);
-    if (plot_volume) fclose(plot_volume);
-    if (plot_adjair) fclose(plot_adjair);
-    if (plot_gaps) fclose(plot_gaps);
-    if (plot_expair) fclose(plot_expair);
-
-
-    if (verbose)
-       bu_vls_printf(gedp->ged_result_str, "Computation Done\n");
-
-    if (!aborted) {
-       summary_reports(&state);
-
-       if (analysis_flags & ANALYSIS_PLOT_OVERLAPS)
-           _ged_cvt_vlblock_to_solids(gedp, ged_gqa_plot.vbp, "OVERLAPS", 0);
-    } else
-       aborted = 0; /* reset flag */
-
-    if (analysis_flags & ANALYSIS_PLOT_OVERLAPS)
-       bn_vlblock_free(ged_gqa_plot.vbp);
-
-    /* Clear out the lists */
-    while (BU_LIST_WHILE (rp, region_pair, &overlapList.l)) {
-       BU_LIST_DEQUEUE(&rp->l);
-       bu_free(rp, "overlapList items");
-    }
-    while (BU_LIST_WHILE (rp, region_pair, &adjAirList.l)) {
-       BU_LIST_DEQUEUE(&rp->l);
-       bu_free(rp, "adjAirList items");
-    }
-    while (BU_LIST_WHILE (rp, region_pair, &gapList.l)) {
-       BU_LIST_DEQUEUE(&rp->l);
-       bu_free(rp, "gapList items");
-    }
-    while (BU_LIST_WHILE (rp, region_pair, &exposedAirList.l)) {
-       BU_LIST_DEQUEUE(&rp->l);
-       bu_free(rp, "exposedAirList items");
-    }
-
-    /* Free dynamically allocated state */
-    bu_free(state.m_lenDensity, "m_lenDensity");
-    bu_free(state.m_len, "m_len");
-    bu_free(state.m_volume, "m_volume");
-    bu_free(state.m_weight, "m_weight");
-    bu_free(state.shots, "m_shots");
-    bu_free(state.m_lenTorque, "m_lenTorque");
-    bu_free(state.m_moi, "m_moi");
-    bu_free(state.m_poi, "m_poi");
-
-    for (i = 0; i < num_objects; i++) {
-       bu_free(obj_tbl[i].o_len, "o_len");
-       bu_free(obj_tbl[i].o_lenDensity, "o_lenDensity");
-       bu_free(obj_tbl[i].o_volume, "o_volume");
-       bu_free(obj_tbl[i].o_weight, "o_weight");
-       bu_free(obj_tbl[i].o_lenTorque, "o_lenTorque");
-       bu_free(obj_tbl[i].o_moi, "o_moi");
-       bu_free(obj_tbl[i].o_poi, "o_poi");
-    }
-    bu_free(obj_tbl, "object table");
-    obj_tbl = NULL;
-
-    for (i = 0, BU_LIST_FOR (regp, region, &(rtip->HeadRegion)), i++) {
-       bu_free(reg_tbl[i].r_lenDensity, "r_lenDensity");
-       bu_free(reg_tbl[i].r_len, "r_len");
-       bu_free(reg_tbl[i].r_volume, "r_volume");
-       bu_free(reg_tbl[i].r_weight, "r_weight");
-    }
-    bu_free(reg_tbl, "object table");
-    reg_tbl = NULL;
-
-    if (_gd_densities) {
-       analyze_densities_destroy(_gd_densities);
-       _gd_densities = NULL;
-    }
-
-    if (_gd_densities_source) {
-       bu_free(_gd_densities_source, "free densities source string");
-       _gd_densities_source = NULL;
-    }
-
-    rt_free_rti(rtip);
-
-    return GED_OK;
-}
-
-
-#ifdef GED_PLUGIN
-#include "../include/plugin.h"
-struct ged_cmd_impl gqa_cmd_impl = {
-    "gqa",
-    ged_gqa_core,
-    GED_CMD_DEFAULT
-};
-
-const struct ged_cmd gqa_cmd = { &gqa_cmd_impl };
-const struct ged_cmd *gqa_cmds[] = { &gqa_cmd, NULL };
-
-static const struct ged_plugin pinfo = { GED_API,  gqa_cmds, 1 };
-
-COMPILER_DLLEXPORT const struct ged_plugin *ged_plugin_info()
-{
-    return &pinfo;
-}
-#endif /* GED_PLUGIN */
-
-/*
- * Local Variables:
- * mode: C
- * tab-width: 8
- * indent-tabs-mode: t
- * c-file-style: "stroustrup"
- * End:
- * ex: shiftwidth=4 tabstop=8
- */

Copied: brlcad/trunk/src/libged/gqa/gqa.cpp (from rev 77356, 
brlcad/trunk/src/libged/gqa/gqa.c)
===================================================================
--- brlcad/trunk/src/libged/gqa/gqa.cpp                         (rev 0)
+++ brlcad/trunk/src/libged/gqa/gqa.cpp 2020-10-07 16:14:36 UTC (rev 77357)
@@ -0,0 +1,2584 @@
+/*                         G Q A . 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/gqa.c
+ *
+ * performs a set of quantitative analyses on geometry.
+ *
+ * XXX need to look at gap computation
+ *
+ * plot the points where overlaps start/stop
+ *
+ * Designed to be a framework for 3d sampling of the geometry volume.
+ * TODO: Need to move the sample pattern logic into LIBRT.
+ *
+ */
+
+#include "common.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <math.h>
+#include <limits.h>                    /* home of INT_MAX aka MAXINT */
+
+
+#include "bu/parallel.h"
+#include "bu/getopt.h"
+#include "vmath.h"
+#include "raytrace.h"
+#include "bn/plot3.h"
+#include "analyze.h"
+
+#include "../ged_private.h"
+
+struct analyze_densities *_gd_densities;
+char *_gd_densities_source;
+
+/* bu_getopt() options */
+const char *options = "A:a:de:f:g:Gn:N:p:P:qrS:s:t:U:u:vV:W:h?";
+const char *options_str = "[-A A|a|b|c|e|g|m|o|v|w] [-a az] [-d] [-e el] [-f 
densityFile] [-g spacing|upper,lower|upper-lower] [-G] [-n nhits] [-N nviews] 
[-p plotPrefix] [-P ncpus] [-q] [-r] [-S nsamples] [-t overlap_tol] [-U useair] 
[-u len_units vol_units wt_units] [-v] [-V volume_tol] [-W weight_tol]";
+
+#define ANALYSIS_VOLUMES          1
+#define ANALYSIS_WEIGHTS          2
+#define ANALYSIS_OVERLAPS         4
+#define ANALYSIS_ADJ_AIR          8 /* adjacent air */
+#define ANALYSIS_GAPS            16 /* space between regions */
+#define ANALYSIS_EXP_AIR         32 /* exposed air */
+#define ANALYSIS_BBOX            64 /* overall bounding box */
+#define ANALYSIS_INTERFACES     128
+#define ANALYSIS_CENTROIDS      256
+#define ANALYSIS_MOMENTS        512
+#define ANALYSIS_PLOT_OVERLAPS 1024
+
+/* Note: struct parsing requires no space after the commas.  take care
+ * when formatting this file.  if the compile breaks here, it means
+ * that spaces got inserted incorrectly.
+ */
+#define COMMA ','
+
+static int analysis_flags;
+static int multiple_analyses;
+
+static double azimuth_deg;
+static double elevation_deg;
+static char *densityFileName;
+static double gridSpacing;
+static double gridSpacingLimit;
+static const double GRIDSPACING_STEP = 1.0 / 2.0;
+
+static char makeOverlapAssemblies;
+static size_t require_num_hits;
+static int ncpu;
+static double Samples_per_model_axis;
+static double overlap_tolerance;
+static double volume_tolerance;
+static double weight_tolerance;
+static int aborted = 0;
+
+static int print_per_region_stats;
+static int max_region_name_len;
+static int use_air;
+static int num_objects; /* number of objects specified on command line */
+static int max_cpus;
+static int num_views;
+static int verbose;
+static int quiet_missed_report;
+
+static const char *plot_prefix = NULL; /* non-NULL means produce plot files */
+static FILE *plot_weight;
+static FILE *plot_volume;
+static FILE *plot_overlaps;
+static FILE *plot_adjair;
+static FILE *plot_gaps;
+static FILE *plot_expair;
+
+static int overlap_color[3] = { 255, 255, 0 }; /* yellow */
+static int gap_color[3] = { 128, 192, 255 };    /* cyan */
+static int adjAir_color[3] = { 128, 255, 192 }; /* pale green */
+static int expAir_color[3] = { 255, 128, 255 }; /* magenta */
+
+static int debug = 0;
+#define DLOG if (debug) bu_vls_printf
+
+/* Some defines for re-using the values from the application structure
+ * for other purposes
+ */
+#define A_LENDEN a_color[0]
+#define A_LEN a_color[1]
+#define A_STATE a_uptr
+
+
+struct cstate {
+    int curr_view; /* the "view" number we are shooting */
+    int u_axis;    /* these 3 are in the range 0..2 inclusive and indicate 
which axis (X, Y, or Z) */
+    int v_axis;    /* is being used for the U, V, or invariant vector 
direction */
+    int i_axis;
+
+    int sem_lists;
+    int sem_worker;
+
+    /* sem_worker protects this */
+    int v;         /* indicates how many "grid_size" steps in the v direction 
have been taken */
+
+    int sem_stats;
+
+    /* sem_stats protects this */
+    double *m_lenDensity;
+    double *m_len;
+    double *m_volume;
+    double *m_weight;
+    unsigned long *shots;
+    int first;     /* this is the first time we've computed a set of views */
+
+    vect_t u_dir;  /* direction of U vector for "current view" */
+    vect_t v_dir;  /* direction of V vector for "current view" */
+    struct rt_i *rtip;
+    long steps[3]; /* this is per-dimension, not per-view */
+    vect_t span;   /* How much space does the geometry span in each of X, Y, Z 
directions */
+    vect_t area;   /* area of the view for view with invariant at index */
+
+    fastf_t *m_lenTorque; /* torque vector for each view */
+    fastf_t *m_moi;       /* one vector per view for collecting the partial 
moments of inertia calculation */
+    fastf_t *m_poi;       /* one vector per view for collecting the partial 
products of inertia calculation */
+
+    struct resource *resp;
+};
+
+
+struct ged_gqa_plot {
+    struct bn_vlblock *vbp;
+    struct bu_list *vhead;
+} ged_gqa_plot;
+
+/* summary data structure for objects specified on command line */
+static struct per_obj_data {
+    char *o_name;
+    double *o_len;
+    double *o_lenDensity;
+    double *o_volume;
+    double *o_weight;
+    fastf_t *o_lenTorque; /* torque vector for each view */
+    fastf_t *o_moi;       /* one vector per view for collecting the partial 
moments of inertia calculation */
+    fastf_t *o_poi;       /* one vector per view for collecting the partial 
products of inertia calculation */
+} *obj_tbl;
+
+/**
+ * this is the data we track for each region
+ */
+static struct per_region_data {
+    unsigned long hits;
+    double *r_lenDensity; /* for per-region per-view weight computation */
+    double *r_len;        /* for per-region, per-view computation */
+    double *r_weight;
+    double *r_volume;
+    struct per_obj_data *optr;
+} *reg_tbl;
+
+
+/* Access to these lists should be in sections
+ * of code protected by state->sem_lists
+ */
+
+/**
+ * list of gaps
+ */
+static struct region_pair gapList = {
+    {
+       BU_LIST_HEAD_MAGIC,
+       (struct bu_list *)&gapList,
+       (struct bu_list *)&gapList
+    },
+    { "Gaps" },
+    (struct region *)NULL,
+    (unsigned long)0,
+    (double)0.0,
+    {0.0, 0.0, 0.0, }
+};
+
+
+/**
+ * list of adjacent air
+ */
+static struct region_pair adjAirList = {
+    {
+       BU_LIST_HEAD_MAGIC,
+       (struct bu_list *)&adjAirList,
+       (struct bu_list *)&adjAirList
+    },
+    { (char *)"Adjacent Air" },
+    (struct region *)NULL,
+    (unsigned long)0,
+    (double)0.0,
+    {0.0, 0.0, 0.0, }
+};
+
+
+/**
+ * list of exposed air
+ */
+static struct region_pair exposedAirList = {
+    {
+       BU_LIST_HEAD_MAGIC,
+       (struct bu_list *)&exposedAirList,
+       (struct bu_list *)&exposedAirList
+    },
+    { "Exposed Air" },
+    (struct region *)NULL,
+    (unsigned long)0,
+    (double)0.0,
+    {0.0, 0.0, 0.0, }
+};
+
+
+/**
+ * list of overlaps
+ */
+static struct region_pair overlapList = {
+    {
+       BU_LIST_HEAD_MAGIC,
+       (struct bu_list *)&overlapList,
+       (struct bu_list *)&overlapList
+    },
+    { "Overlaps" },
+    (struct region *)NULL,
+    (unsigned long)0,
+    (double)0.0,
+    {0.0, 0.0, 0.0, }
+};
+
+
+/**
+ * This structure holds the name of a unit value, and the conversion
+ * factor necessary to convert from/to BRL-CAD standard units.
+ *
+ * The standard units are millimeters, cubic millimeters, and grams.
+ *
+ * XXX this section should be extracted to libbu/units.c
+ */
+struct cvt_tab {
+    double val;
+    char name[32];
+};
+
+
+static const struct cvt_tab units_tab[3][40] = {
+    {
+       /* length, stolen from bu/units.c with the "none" value
+        * removed Values for converting from given units to mm
+        */
+       {1.0,           "mm"}, /* default */
+       /* {0.0,                "none"}, */ /* this is removed to force a 
certain
+                                            * amount of error checking for the 
user
+                                            */
+       {1.0e-7,        "angstrom"},
+       {1.0e-7,        "decinanometer"},
+       {1.0e-6,        "nm"},
+       {1.0e-6,        "nanometer"},
+       {1.0e-3,        "um"},
+       {1.0e-3,        "micrometer"},
+       {1.0e-3,        "micron"},
+       {1.0,           "millimeter"},
+       {10.0,          "cm"},
+       {10.0,          "centimeter"},
+       {1000.0,        "m"},
+       {1000.0,        "meter"},
+       {1000000.0,     "km"},
+       {1000000.0,     "kilometer"},
+       {25.4,          "in"},
+       {25.4,          "inch"},
+       {25.4,          "inches"},              /* for plural */
+       {304.8,         "ft"},
+       {304.8,         "foot"},
+       {304.8,         "feet"},
+       {456.2,         "cubit"},
+       {914.4,         "yd"},
+       {914.4,         "yard"},
+       {5029.2,        "rd"},
+       {5029.2,        "rod"},
+       {1609344.0,     "mi"},
+       {1609344.0,     "mile"},
+       {1852000.0,     "nmile"},
+       {1852000.0,     "nautical mile"},
+       {1.495979e+14,  "AU"},
+       {1.495979e+14,  "astronomical unit"},
+       {9.460730e+18,  "lightyear"},
+       {3.085678e+19,  "pc"},
+       {3.085678e+19,  "parsec"},
+       {0.0,           ""}                     /* LAST ENTRY */
+    },
+    {
+       /* volume
+        * Values for converting from given units to mm^3
+        */
+       {1.0, "cu mm"}, /* default */
+
+       {1.0, "mm"},
+       {1.0, "mm^3"},
+
+       {1.0e3, "cm"},
+       {1.0e3, "cm^3"},
+       {1.0e3, "cu cm"},
+       {1.0e3, "cc"},
+
+       {1.0e6, "l"},
+       {1.0e6, "liter"},
+       {1.0e6, "litre"},
+
+       {1.0e9, "m"},
+       {1.0e9, "m^3"},
+       {1.0e9, "cu m"},
+
+       {16387.064, "in"},
+       {16387.064, "in^3"},
+       {16387.064, "cu in"},
+
+       {28316846.592, "ft"},
+
+       {28316846.592, "ft^3"},
+       {28316846.592, "cu ft"},
+
+       {764554857.984, "yds"},
+       {764554857.984, "yards"},
+       {764554857.984, "cu yards"},
+
+       {0.0,           ""}                     /* LAST ENTRY */
+    },
+    {
+       /* weight
+        * Values for converting given units to grams
+        */
+       {1.0, "grams"}, /* default */
+
+       {1.0, "g"},
+       {0.0648, "gr"},
+       {0.0648, "grains"},
+
+       {1.0e3, "kg"},
+       {1.0e3, "kilos"},
+       {1.0e3, "kilograms"},
+
+       {28.35, "oz"},
+       {28.35, "ounce"},
+
+       {453.6, "lb"},
+       {453.6, "lbs"},
+       {0.0,           ""}                     /* LAST ENTRY */
+    }
+};
+
+
+/* this table keeps track of the "current" or "user selected units and
+ * the associated conversion values
+ */
+#define LINE 0
+#define VOL 1
+#define WGT 2
+static const struct cvt_tab *units[3] = {
+    &units_tab[0][0],  /* linear */
+    &units_tab[1][0],  /* volume */
+    &units_tab[2][0]   /* weight */
+};
+
+
+/**
+ * _gqa_read_units_double
+ *
+ * Read a non-negative floating point value with optional units
+ *
+ * Return
+ * 1 Failure
+ * 0 Success
+ */
+int
+_gqa_read_units_double(double *val, char *buf, const struct cvt_tab *cvt)
+{
+    double a;
+#define UNITS_STRING_SZ 256
+    char units_string[UNITS_STRING_SZ+1] = {0};
+    int i;
+
+
+    i = sscanf(buf, "%lg" CPP_SCAN(UNITS_STRING_SZ), &a, units_string);
+
+    if (i < 0) return 1;
+
+    if (i == 1) {
+       *val = a;
+
+       return 0;
+    }
+    if (i == 2) {
+       *val = a;
+       for (; cvt->name[0] != '\0';) {
+           if (!bu_strncmp(cvt->name, units_string, sizeof(units_string))) {
+               goto found_units;
+           } else {
+               cvt++;
+           }
+       }
+       bu_vls_printf(_ged_current_gedp->ged_result_str, "Bad units specifier 
\"%s\" on value \"%s\"\n", units_string, buf);
+       return 1;
+
+    found_units:
+       *val = a * cvt->val;
+       return 0;
+    }
+    bu_vls_printf(_ged_current_gedp->ged_result_str, "%s sscanf problem on 
\"%s\" got %d\n", CPP_FILELINE, buf, i);
+    return 1;
+}
+
+
+/* the above should be extracted to libbu/units.c */
+
+
+/**
+ * Parse through command line flags
+ */
+static int
+parse_args(int ac, char *av[])
+{
+    int c;
+    int i;
+    double a;
+    char *p;
+
+    /* 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(ac, av, options)) != -1) {
+       switch (c) {
+           case 'A':
+               {
+                   analysis_flags = 0;
+                   multiple_analyses = 0;
+                   for (p = bu_optarg; *p; p++) {
+                       switch (*p) {
+                           case 'A' :
+                               multiple_analyses = 1;
+                               analysis_flags = analysis_flags \
+                                   | ANALYSIS_ADJ_AIR \
+                                   | ANALYSIS_BBOX \
+                                   | ANALYSIS_CENTROIDS \
+                                   | ANALYSIS_EXP_AIR \
+                                   | ANALYSIS_GAPS \
+                                   | ANALYSIS_MOMENTS \
+                                   | ANALYSIS_OVERLAPS \
+                                   | ANALYSIS_VOLUMES \
+                                   | ANALYSIS_WEIGHTS;
+                               break;
+                           case 'a' :
+                               if (analysis_flags)
+                                   multiple_analyses = 1;
+
+                               analysis_flags |= ANALYSIS_ADJ_AIR;
+
+                               break;
+                           case 'b' :
+                               if (analysis_flags)
+                                   multiple_analyses = 1;
+
+                               analysis_flags |= ANALYSIS_BBOX;
+
+                               break;
+                           case 'c' :
+                               if (analysis_flags)
+                                   multiple_analyses = 1;
+
+                               analysis_flags |= ANALYSIS_WEIGHTS;
+                               analysis_flags |= ANALYSIS_CENTROIDS;
+
+                               break;
+                           case 'e' :
+                               if (analysis_flags)
+                                   multiple_analyses = 1;
+
+                               analysis_flags |= ANALYSIS_EXP_AIR;
+                               break;
+                           case 'g' :
+                               if (analysis_flags)
+                                   multiple_analyses = 1;
+
+                               analysis_flags |= ANALYSIS_GAPS;
+                               break;
+                           case 'm' :
+                               if (analysis_flags)
+                                   multiple_analyses = 1;
+
+                               analysis_flags |= ANALYSIS_WEIGHTS;
+                               analysis_flags |= ANALYSIS_CENTROIDS;
+                               analysis_flags |= ANALYSIS_MOMENTS;
+
+                               break;
+                           case 'o' :
+                               if (analysis_flags)
+                                   multiple_analyses = 1;
+
+                               analysis_flags |= ANALYSIS_OVERLAPS;
+                               break;
+                           case 'p' :
+                               if (analysis_flags)
+                                   multiple_analyses = 1;
+
+                               analysis_flags |= ANALYSIS_OVERLAPS;
+                               analysis_flags |= ANALYSIS_PLOT_OVERLAPS;
+                               break;
+                           case 'v' :
+                               if (analysis_flags)
+                                   multiple_analyses = 1;
+
+                               analysis_flags |= ANALYSIS_VOLUMES;
+                               break;
+                           case 'w' :
+                               if (analysis_flags)
+                                   multiple_analyses = 1;
+
+                               analysis_flags |= ANALYSIS_WEIGHTS;
+                               break;
+                           default:
+                               
bu_vls_printf(_ged_current_gedp->ged_result_str, "Unknown analysis type \"%c\" 
requested.\n", *p);
+                               return -1;
+                       }
+                   }
+                   break;
+               }
+           case 'a':
+               bu_vls_printf(_ged_current_gedp->ged_result_str, "azimuth not 
implemented\n");
+               if (bn_decode_angle(&azimuth_deg,bu_optarg) == 0) {
+                   bu_vls_printf(_ged_current_gedp->ged_result_str, "error 
parsing azimuth \"%s\"\n", bu_optarg);
+                   return -1;
+               }
+               break;
+           case 'e':
+               bu_vls_printf(_ged_current_gedp->ged_result_str, "elevation not 
implemented\n");
+               if (bn_decode_angle(&elevation_deg,bu_optarg) == 0) {
+                   bu_vls_printf(_ged_current_gedp->ged_result_str, "error 
parsing elevation \"%s\"\n", bu_optarg);
+                   return -1;
+               }
+               break;
+           case 'd': debug = 1; break;
+
+           case 'f': densityFileName = bu_optarg; break;
+
+           case 'g':
+               {
+                   double value1, value2;
+                   i = 0;
+
+                   /* find out if we have two or one args; user can
+                    * separate them with , or - delimiter
+                    */
+                   p = strchr(bu_optarg, COMMA);
+                   if (p)
+                       *p++ = '\0';
+                   else {
+                       p = strchr(bu_optarg, '-');
+                       if (p)
+                           *p++ = '\0';
+                   }
+
+
+                   if (_gqa_read_units_double(&value1, bu_optarg, 
units_tab[0])) {
+                       bu_vls_printf(_ged_current_gedp->ged_result_str, "error 
parsing grid spacing value \"%s\"\n", bu_optarg);
+                       return -1;
+                   }
+
+                   if (p) {
+                       /* we've got 2 values, they are upper limit
+                        * and lower limit.
+                        */
+                       if (_gqa_read_units_double(&value2, p, units_tab[0])) {
+                           bu_vls_printf(_ged_current_gedp->ged_result_str, 
"error parsing grid spacing limit value \"%s\"\n", p);
+                           return -1;
+                       }
+
+                       gridSpacing = value1;
+                       gridSpacingLimit = value2;
+                   } else {
+                       gridSpacingLimit = value1;
+
+                       gridSpacing = 0.0; /* flag it */
+                   }
+                   break;
+               }
+           case 'G':
+               makeOverlapAssemblies = 1;
+               bu_vls_printf(_ged_current_gedp->ged_result_str, "-G option 
unimplemented\n");
+               return -1;
+           case 'n':
+               if (sscanf(bu_optarg, "%d", &c) != 1 || c < 0) {
+                   bu_vls_printf(_ged_current_gedp->ged_result_str, "num_hits 
must be integer value >= 0, not \"%s\"\n", bu_optarg);
+                   return -1;
+               }
+
+               require_num_hits = (size_t)c;
+               break;
+
+           case 'N':
+               num_views = atoi(bu_optarg);
+               break;
+           case 'p':
+               plot_prefix = bu_optarg;
+               break;
+           case 'P':
+               /* cannot ask for more cpu's than the machine has */
+               c = atoi(bu_optarg);
+               if (c > 0 && c <= max_cpus)
+                   ncpu = c;
+               break;
+           case 'q':
+               quiet_missed_report = 1;
+               break;
+           case 'r':
+               print_per_region_stats = 1;
+               break;
+           case 'S':
+               if (sscanf(bu_optarg, "%lg", &a) != 1 || a <= 1.0) {
+                   bu_vls_printf(_ged_current_gedp->ged_result_str, "error in 
specifying minimum samples per model axis: \"%s\"\n", bu_optarg);
+                   break;
+               }
+               Samples_per_model_axis = a + 1;
+               break;
+           case 't':
+               if (_gqa_read_units_double(&overlap_tolerance, bu_optarg, 
units_tab[0])) {
+                   bu_vls_printf(_ged_current_gedp->ged_result_str, "error in 
overlap tolerance distance \"%s\"\n", bu_optarg);
+                   return -1;
+               }
+               break;
+           case 'v':
+               verbose = 1;
+               break;
+           case 'V':
+               if (_gqa_read_units_double(&volume_tolerance, bu_optarg, 
units_tab[1])) {
+                   bu_vls_printf(_ged_current_gedp->ged_result_str, "error in 
volume tolerance \"%s\"\n", bu_optarg);
+                   return -1;
+               }
+               break;
+           case 'W':
+               if (_gqa_read_units_double(&weight_tolerance, bu_optarg, 
units_tab[2])) {
+                   bu_vls_printf(_ged_current_gedp->ged_result_str, "error in 
weight tolerance \"%s\"\n", bu_optarg);
+                   return -1;
+               }
+               break;
+
+           case 'U':
+               errno = 0;
+               use_air = strtol(bu_optarg, (char **)NULL, 10);
+               if (errno == ERANGE || errno == EINVAL) {
+                   bu_vls_printf(_ged_current_gedp->ged_result_str, "error in 
air argument %s\n", bu_optarg);
+                   return -1;
+               }
+               break;
+           case 'u':
+               {
+                   char *ptr = bu_optarg;
+                   const struct cvt_tab *cv;
+                   static const char *dim[3] = {"length", "volume", "weight"};
+                   char *units_name[3] = {NULL, NULL, NULL};
+                   char **units_ap;
+
+                   /* fill in units_name with the names we parse out */
+                   units_ap = units_name;
+
+                   /* acquire unit names */
+                   for (i = 0; i < 3 && ptr; i++) {
+                       int found_unit;
+
+                       if (i == 0) {
+                           *units_ap = strtok(ptr, CPP_XSTR(COMMA));
+                       } else {
+                           *units_ap = strtok(NULL, CPP_XSTR(COMMA));
+                       }
+
+                       /* got something? */
+                       if (*units_ap == NULL)
+                           break;
+
+                       /* got something valid? */
+                       found_unit = 0;
+                       for (cv = &units_tab[i][0]; cv->name[0] != '\0'; cv++) {
+                           if (units_name[i] && BU_STR_EQUAL(cv->name, 
units_name[i])) {
+                               units[i] = cv;
+                               found_unit = 1;
+                               break;
+                           }
+                       }
+
+                       if (!found_unit) {
+                           bu_vls_printf(_ged_current_gedp->ged_result_str, 
"Units \"%s\" not found in conversion table\n", units_name[i]);
+                           return -1;
+                       }
+
+                       ++units_ap;
+                   }
+
+                   bu_vls_printf(_ged_current_gedp->ged_result_str, "Units: ");
+                   for (i = 0; i < 3; i++) {
+                       bu_vls_printf(_ged_current_gedp->ged_result_str, " %s: 
%s", dim[i], units[i]->name);
+                   }
+                   bu_vls_printf(_ged_current_gedp->ged_result_str, "\n");
+               }
+               break;
+
+           default: /* '?' 'h' */
+               return -1;
+       }
+    }
+
+    return bu_optind;
+}
+
+/**
+ * Write end points of partition to the standard output.  If this
+ * routine return !0, this partition will be dropped from the boolean
+ * evaluation.
+ *
+ * Returns:
+ * 0 to eliminate partition with overlap entirely
+ * 1 to retain partition in output list, claimed by reg1
+ * 2 to retain partition in output list, claimed by reg2
+ *
+ * This routine must be prepared to run in parallel
+ */
+int
+_gqa_overlap(struct application *ap,
+       struct partition *pp,
+       struct region *reg1,
+       struct region *reg2,
+       struct partition *hp)
+{
+    struct cstate *state = (struct cstate *)ap->A_STATE;
+    struct xray *rp = &ap->a_ray;
+    struct hit *ihitp = pp->pt_inhit;
+    struct hit *ohitp = pp->pt_outhit;
+    point_t ihit;
+    point_t ohit;
+    double depth;
+
+    if (!hp) /* unexpected */
+       return 0;
+
+    /* if one of the regions is air, let it loose */
+    if (reg1->reg_aircode && ! reg2->reg_aircode)
+       return 2;
+    if (reg2->reg_aircode && ! reg1->reg_aircode)
+       return 1;
+
+    depth = ohitp->hit_dist - ihitp->hit_dist;
+
+    if (depth < overlap_tolerance)
+       /* too small to matter, pick one or none */
+       return 1;
+
+    VJOIN1(ihit, rp->r_pt, ihitp->hit_dist, rp->r_dir);
+    VJOIN1(ohit, rp->r_pt, ohitp->hit_dist, rp->r_dir);
+
+    if (plot_overlaps) {
+       pl_color(plot_overlaps, V3ARGS(overlap_color));
+       pdv_3line(plot_overlaps, ihit, ohit);
+    }
+
+    if (analysis_flags & ANALYSIS_PLOT_OVERLAPS) {
+       bu_semaphore_acquire(state->sem_worker);
+       BN_ADD_VLIST(ged_gqa_plot.vbp->free_vlist_hd, ged_gqa_plot.vhead, ihit, 
BN_VLIST_LINE_MOVE);
+       BN_ADD_VLIST(ged_gqa_plot.vbp->free_vlist_hd, ged_gqa_plot.vhead, ohit, 
BN_VLIST_LINE_DRAW);
+       bu_semaphore_release(state->sem_worker);
+    }
+
+    if (analysis_flags & ANALYSIS_OVERLAPS) {
+       bu_semaphore_acquire(state->sem_lists);
+       add_unique_pair(&overlapList, reg1, reg2, depth, ihit);
+       bu_semaphore_release(state->sem_lists);
+
+       if (plot_overlaps) {
+           pl_color(plot_overlaps, V3ARGS(overlap_color));
+           pdv_3line(plot_overlaps, ihit, ohit);
+       }
+    } else {
+       bu_semaphore_acquire(state->sem_worker);
+       bu_vls_printf(_ged_current_gedp->ged_result_str, "overlap %s %s\n", 
reg1->reg_name, reg2->reg_name);
+       bu_semaphore_release(state->sem_worker);
+    }
+
+    /* XXX We should somehow flag the volume/weight calculations as invalid */
+
+    /* since we have no basis to pick one over the other, just pick */
+    return 1;  /* No further consideration to this partition */
+}
+
+
+/**
+ * Does nothing.
+ */
+void
+logoverlap(struct application *ap,
+          const struct partition *pp,
+          const struct bu_ptbl *regiontable,
+          const struct partition *InputHdp)
+{
+    RT_CK_AP(ap);
+    RT_CK_PT(pp);
+    BU_CK_PTBL(regiontable);
+    if (!InputHdp)
+       return;
+
+    /* do nothing */
+
+    return;
+}
+
+
+void _gqa_exposed_air(struct application *ap,
+                     struct partition *pp,
+                     point_t last_out_point,
+                     point_t pt,
+                     point_t opt)
+{
+    struct cstate *state = (struct cstate *)ap->A_STATE;
+
+    /* this shouldn't be air */
+
+    bu_semaphore_acquire(state->sem_lists);
+    add_unique_pair(&exposedAirList,
+           pp->pt_regionp,
+           (struct region *)NULL,
+           pp->pt_outhit->hit_dist - pp->pt_inhit->hit_dist, /* thickness */
+           last_out_point); /* location */
+    bu_semaphore_release(state->sem_lists);
+
+    if (plot_expair) {
+       pl_color(plot_expair, V3ARGS(expAir_color));
+       pdv_3line(plot_expair, pt, opt);
+    }
+}
+
+
+/**
+ * rt_shootray() was told to call this on a hit.  It passes the
+ * application structure which describes the state of the world (see
+ * raytrace.h), and a circular linked list of partitions, each one
+ * describing one in and out segment of one region.
+ *
+ * this routine must be prepared to run in parallel
+ */
+int
+_gqa_hit(struct application *ap, struct partition *PartHeadp, struct seg *segs)
+{
+    /* see raytrace.h for all of these guys */
+    struct partition *pp;
+    point_t pt, opt, last_out_point;
+    int last_air = 0;  /* what was the aircode of the last item */
+    int air_first = 1; /* are we in an air before a solid */
+    double dist;       /* the thickness of the partition */
+    double gap_dist;
+    double last_out_dist = -1.0;
+    double val;
+    struct cstate *state = (struct cstate *)ap->A_STATE;
+
+    if (!segs) /* unexpected */
+       return 0;
+
+    if (PartHeadp->pt_forw == PartHeadp) return 1;
+
+
+    /* examine each partition until we get back to the head */
+    for (pp=PartHeadp->pt_forw; pp != PartHeadp; pp = pp->pt_forw) {
+
+       long int material_id = pp->pt_regionp->reg_gmater;
+       fastf_t grams_per_cu_mm = analyze_densities_density(_gd_densities, 
material_id);
+
+       /* inhit info */
+       dist = pp->pt_outhit->hit_dist - pp->pt_inhit->hit_dist;
+       VJOIN1(pt, ap->a_ray.r_pt, pp->pt_inhit->hit_dist, ap->a_ray.r_dir);
+       VJOIN1(opt, ap->a_ray.r_pt, pp->pt_outhit->hit_dist, ap->a_ray.r_dir);
+
+       if (debug) {
+           bu_semaphore_acquire(state->sem_worker);
+           bu_vls_printf(_ged_current_gedp->ged_result_str, "%s %g->%g\n", 
pp->pt_regionp->reg_name,
+                         pp->pt_inhit->hit_dist, pp->pt_outhit->hit_dist);
+           bu_semaphore_release(state->sem_worker);
+       }
+
+       /* checking for air sticking out of the model.  This is done
+        * here because there may be any number of air regions
+        * sticking out of the model along the ray.
+        */
+       if (analysis_flags & ANALYSIS_EXP_AIR) {
+
+           gap_dist = (pp->pt_inhit->hit_dist - last_out_dist);
+

@@ Diff output truncated at 100000 characters. @@
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