Revision: 72373
          http://sourceforge.net/p/brlcad/code/72373
Author:   starseeker
Date:     2019-02-08 18:28:41 +0000 (Fri, 08 Feb 2019)
Log Message:
-----------
Probably breaks gqa and mater (definitely breaks rtweight) but have reached a 
point where things build and the new analyze density unit tests are passing so 
need to checkpoint.

Modified Paths:
--------------
    brlcad/trunk/include/analyze.h
    brlcad/trunk/src/libanalyze/analyze_private.h
    brlcad/trunk/src/libanalyze/api.c
    brlcad/trunk/src/libanalyze/check_options.c
    brlcad/trunk/src/libanalyze/density.cxx
    brlcad/trunk/src/libanalyze/tests/CMakeLists.txt
    brlcad/trunk/src/libged/gqa.c
    brlcad/trunk/src/libged/mater.cxx
    brlcad/trunk/src/rt/CMakeLists.txt

Added Paths:
-----------
    brlcad/trunk/src/libanalyze/tests/density.cxx

Removed Paths:
-------------
    brlcad/trunk/src/libanalyze/tests/density.c

Modified: brlcad/trunk/include/analyze.h
===================================================================
--- brlcad/trunk/include/analyze.h      2019-02-08 00:43:55 UTC (rev 72372)
+++ brlcad/trunk/include/analyze.h      2019-02-08 18:28:41 UTC (rev 72373)
@@ -146,14 +146,76 @@
 };
 
 /**
- *     Routine to parse a .density file
+ *     Loading information from a .density file and querying it.
  */
-ANALYZE_EXPORT extern int parse_densities_buffer(char *buf,
-                                                size_t len,
-                                                struct density_entry 
**densities,
-                                                struct bu_vls *result_str,
-                                                int *num_densities);
+#if defined(__cplusplus)
+extern "C" {
+#endif
+struct analyze_densities_impl;
+struct analyze_densities {
+    struct analyze_densities_impl *i;
+};
+#if defined(__cplusplus)
+}
+#endif
 
+ANALYZE_EXPORT extern int analyze_densities_create(struct analyze_densities 
**a);
+ANALYZE_EXPORT extern void analyze_densities_destroy(struct analyze_densities 
*a);
+ANALYZE_EXPORT extern int analyze_densities_init(struct analyze_densities *a);
+ANALYZE_EXPORT extern void analyze_densities_clear(struct analyze_densities 
*a);
+
+/* Update an entry if one matching id already exists, else create a new entry.
+ * Unlike the loading and writing functions, this command uses g/mm^3 */
+ANALYZE_EXPORT extern int analyze_densities_set(struct analyze_densities *a, 
long int id, fastf_t density, const char *name, struct bu_vls *msgs);
+
+/* Accepts a buffer, typically read from a .density file.  Expects units of 
g/cm^3.
+ * (TODO - Ugh - need to come up with a way to define the units in the file so 
we aren't
+ * tied to that forever...) */
+ANALYZE_EXPORT extern int analyze_densities_load(struct analyze_densities *a, 
const char *buff, struct bu_vls *msgs);
+
+/* Creates a .density buffer from a, writing units of g/cm^3.  Returns length 
of buff
+ * (TODO - Ugh - need to come up with a way to define the units so we aren't
+ * tied to g/cm^3 that forever...) */
+ANALYZE_EXPORT extern long int analyze_densities_write(char **buff, struct 
analyze_densities *a);
+
+/* This mapping will be unique - each id may have at most one entry in the 
database. Caller
+ * is responsible for freeing the resulting string. */
+ANALYZE_EXPORT extern char *analyze_densities_name(struct analyze_densities 
*a, long int id);
+
+/* Because this mapping is not guaranteed to be unique, this function has to be
+ * a bit more sophisticated in what it does.
+ *
+ * If ids is NULL or max_ids == 0, it will return the count of the ids
+ * associated with name in the database.
+ *
+ * If an ids array is available, it returns two pieces of data - the id array
+ * of length max_ids (defined by the caller) will hold up to max_ids material
+ * IDs that have the specified name.  In this mode, the return code will either
+ * be the number of IDs written to ids (if positive) or the number of
+ * additional ids found that would not fit in the ids array (if negative).
+ */
+ANALYZE_EXPORT extern long int analyze_densities_id(long int *ids, long int 
max_ids, struct analyze_densities *a, const char *name);
+
+/* Note: to use a name for density lookup, supply one of the outputs of
+ * analyze_densities_id to this function.
+ *
+ * Density is returned in g/mm^3 */
+ANALYZE_EXPORT extern fastf_t analyze_densities_density(struct 
analyze_densities *a, long int id);
+
+/* Provide a means to iterate over all defined densities.  To get the lowest 
valid id,
+ * supply -1 to curr_id.  Given a valid id, supply it to curr_id to get the 
next highest
+ * id.  When there isn't a valid next id, -1 is returned. To count the number 
of density
+ * definitions active, do the following:
+ *
+ * long int curr_id = -1;
+ * long int dcnt = 0;
+ * while ((curr_id = analyze_densities_next(a, curr_id)) != -1) dcnt++;
+ *
+ */
+ANALYZE_EXPORT extern long int analyze_densities_next(struct analyze_densities 
*a, long int curr_id);
+
+
+
 /**
  *     region_pair for gqa
  */

Modified: brlcad/trunk/src/libanalyze/analyze_private.h
===================================================================
--- brlcad/trunk/src/libanalyze/analyze_private.h       2019-02-08 00:43:55 UTC 
(rev 72372)
+++ brlcad/trunk/src/libanalyze/analyze_private.h       2019-02-08 18:28:41 UTC 
(rev 72373)
@@ -120,8 +120,7 @@
     struct per_obj_data *objs;
     struct per_region_data *reg_tbl;
 
-    struct density_entry *densities;
-    int num_densities;
+    struct analyze_densities *densities;
 
     /* the parameters */
     int num_views;

Modified: brlcad/trunk/src/libanalyze/api.c
===================================================================
--- brlcad/trunk/src/libanalyze/api.c   2019-02-08 00:43:55 UTC (rev 72372)
+++ brlcad/trunk/src/libanalyze/api.c   2019-02-08 18:28:41 UTC (rev 72373)
@@ -87,8 +87,20 @@
 
     /* examine each partition until we get back to the head */
     for (pp=PartHeadp->pt_forw; pp != PartHeadp; pp = pp->pt_forw) {
-       struct density_entry *de;
 
+       fastf_t grams_per_cu_mm;
+       long int material_id = pp->pt_regionp->reg_gmater;
+
+       if (state->default_den) {
+           /* Aluminium 7xxx series as default material */
+           fastf_t default_density = 2.74; /* Aluminium, 7079-T6 */
+           material_id = -1;
+           grams_per_cu_mm = default_density;
+       } else {
+           grams_per_cu_mm = analyze_densities_density(state->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);
@@ -139,30 +151,16 @@
            }
 
            /* make sure mater index is within range of densities */
-           if (pp->pt_regionp->reg_gmater >= state->num_densities) {
-               if(state->default_den == 0) {
-                   bu_semaphore_acquire(ANALYZE_SEM_WORKER);
-                   bu_vls_printf(state->log_str, "Density index %d on region 
%s is outside of range of table [1..%d]\nSet GIFTmater on region or add entry 
to density table\n",
-                                 pp->pt_regionp->reg_gmater,
-                                 pp->pt_regionp->reg_name,
-                                 state->num_densities); /* XXX this should do 
something else */
-                   bu_semaphore_release(ANALYZE_SEM_WORKER);
-                   return ANALYZE_ERROR;
-               }
+           if (material_id < 0 && state->default_den == 0) {
+               bu_semaphore_acquire(ANALYZE_SEM_WORKER);
+               bu_vls_printf(state->log_str, "Density index %d on region %s is 
not in density table.\nSet GIFTmater on region or add entry to density table\n",
+                       pp->pt_regionp->reg_gmater,
+                       pp->pt_regionp->reg_name); /* XXX this should do 
something else */
+               bu_semaphore_release(ANALYZE_SEM_WORKER);
+               return ANALYZE_ERROR;
            }
 
-           /* make sure the density index has been set */
-           bu_semaphore_acquire(ANALYZE_SEM_WORKER);
-           if(state->default_den || 
state->densities[pp->pt_regionp->reg_gmater].magic != DENSITY_MAGIC) {
-               BU_GET(de, struct density_entry);               /* Aluminium 
7xxx series as default material */
-               de->magic = DENSITY_MAGIC;
-               de->grams_per_cu_mm = 2.74;
-               de->name = "Aluminium, 7079-T6";
-           } else {
-               de = state->densities + pp->pt_regionp->reg_gmater;
-           }
-           bu_semaphore_release(ANALYZE_SEM_WORKER);
-           if (de->magic == DENSITY_MAGIC) {
+           {
                struct per_region_data *prd;
                vect_t cmass;
                vect_t lenTorque;
@@ -213,7 +211,7 @@
                }
 
                /* accumulate the total mass values */
-               val = de->grams_per_cu_mm * dist * (pp->pt_regionp->reg_los * 
0.01);
+               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);
@@ -269,14 +267,6 @@
                }
                bu_semaphore_release(ANALYZE_SEM_STATS);
 
-           } else {
-               bu_semaphore_acquire(ANALYZE_SEM_WORKER);
-               bu_vls_printf(state->log_str, "Density index %d from region %s 
is not set.\nAdd entry to density table\n",
-                             pp->pt_regionp->reg_gmater, 
pp->pt_regionp->reg_name);
-               bu_semaphore_release(ANALYZE_SEM_WORKER);
-
-               state->aborted = 1;
-               return ANALYZE_ERROR;
            }
        }
 
@@ -495,38 +485,36 @@
 HIDDEN int
 densities_from_file(struct current_state *state, char *name)
 {
-    struct stat sb;
-
-    FILE *fp = (FILE *)NULL;
+    struct bu_vls msgs = BU_VLS_INIT_ZERO;
+    struct bu_mapped_file *dfile = NULL;
     char *buf = NULL;
     int ret = 0;
-    size_t sret = 0;
 
-    fp = fopen(name, "rb");
-    if (fp == (FILE *)NULL) {
-       bu_log("Could not open file - %s\n", name);
+    if (!bu_file_exists(name, NULL)) {
+       bu_log("Could not find density file - %s\n", name);
        return ANALYZE_ERROR;
     }
 
-    if (stat(name, &sb)) {
-       bu_log("Could not read file - %s\n", name);
-       fclose(fp);
+    dfile = bu_open_mapped_file(name, "densities file");
+    if (!dfile) {
+       bu_log("Could not open density file - %s\n", name);
        return ANALYZE_ERROR;
     }
 
-    state->densities = (struct density_entry *)bu_calloc(128, sizeof(struct 
density_entry), "density entries");
-    state->num_densities = 128;
+    buf = (char *)(dfile->buf);
 
-    /* a mapped file would make more sense here */
-    buf = (char *)bu_malloc(sb.st_size+1, "density buffer");
-    sret = fread(buf, sb.st_size, 1, fp);
-    if (sret != 1)
-       perror("fread");
-    ret = parse_densities_buffer(buf, (unsigned long)sb.st_size, 
&(state->densities), NULL, &state->num_densities);
-    bu_free(buf, "density buffer");
-    fclose(fp);
+    (void)analyze_densities_create(&(state->densities));
 
-    return ret;
+    ret = analyze_densities_load(state->densities, buf, &msgs);
+
+    if (bu_vls_strlen(&msgs)) {
+       bu_log("Problem reading densities file:\n%s\n", bu_vls_cstr(&msgs));
+    }
+    bu_vls_free(&msgs);
+
+    bu_close_mapped_file(dfile);
+
+    return (ret <= 0) ? -1 : 0;
 }
 
 
@@ -538,6 +526,7 @@
 HIDDEN int
 densities_from_database(struct current_state *state, struct rt_i *rtip)
 {
+    struct bu_vls msgs = BU_VLS_INIT_ZERO;
     struct directory *dp;
     struct rt_db_internal intern;
     struct rt_binunif_internal *bu;
@@ -562,15 +551,18 @@
 
     RT_CHECK_BINUNIF (bu);
 
-    state->densities = (struct density_entry *)bu_calloc(128, sizeof(struct 
density_entry), "density entries");
-    state->num_densities = 128;
+    (void)analyze_densities_create(&(state->densities));
 
-    /* Acquire one extra byte to accommodate parse_densities_buffer()
-     * (i.e. it wants to write an EOS in buf[bu->count]).
-     */
-    buf = (char *)bu_malloc(bu->count+1, "density buffer");
+    buf = (char *)bu_calloc(bu->count+1, sizeof(char), "density buffer");
     memcpy(buf, bu->u.int8, bu->count);
-    ret = parse_densities_buffer(buf, bu->count, &(state->densities), NULL, 
&state->num_densities);
+
+    ret = analyze_densities_load(state->densities, buf, &msgs);
+
+    if (bu_vls_strlen(&msgs)) {
+       bu_log("Problem reading densities file:\n%s\n", bu_vls_cstr(&msgs));
+    }
+    bu_vls_free(&msgs);
+
     bu_free((void *)buf, "density buffer");
 
     return ret;
@@ -908,10 +900,10 @@
     if (state->analysis_flags & ANALYSIS_MASS) {
        if (state->mass_tolerance < 0.0) {
            double max_den = 0.0;
-           int i;
-           for (i = 0; i < state->num_densities; i++) {
-               if (state->densities[i].grams_per_cu_mm > max_den)
-                   max_den = state->densities[i].grams_per_cu_mm;
+           long int curr_id = -1;
+           while ((curr_id = analyze_densities_next(state->densities, 
curr_id)) != -1) {
+               if (analyze_densities_density(state->densities, curr_id) > 
max_den)
+                   max_den = analyze_densities_density(state->densities, 
curr_id);
            }
            state->mass_tolerance = state->span[X] * state->span[Y] * 
state->span[Z] * 0.1 * max_den;
            bu_log("Setting mass tolerance to %g gram\n", 
state->mass_tolerance);

Modified: brlcad/trunk/src/libanalyze/check_options.c
===================================================================
--- brlcad/trunk/src/libanalyze/check_options.c 2019-02-08 00:43:55 UTC (rev 
72372)
+++ brlcad/trunk/src/libanalyze/check_options.c 2019-02-08 18:28:41 UTC (rev 
72373)
@@ -101,14 +101,8 @@
     bu_vls_free(state->log_str);
 
     if (state->densities != NULL) {
-       for (i = 0; i < state->num_densities; ++i) {
-           if (state->densities[i].name != 0)
-               bu_free(state->densities[i].name, "density name");
-       }
-
-       bu_free(state->densities, "densities");
+       analyze_densities_destroy(state->densities);    
        state->densities = NULL;
-       state->num_densities = 0;
     }
 
     if (state->m_lenTorque != NULL)

Modified: brlcad/trunk/src/libanalyze/density.cxx
===================================================================
--- brlcad/trunk/src/libanalyze/density.cxx     2019-02-08 00:43:55 UTC (rev 
72372)
+++ brlcad/trunk/src/libanalyze/density.cxx     2019-02-08 18:28:41 UTC (rev 
72373)
@@ -1,4 +1,4 @@
-/*                    D E N S I T Y . C
+/*                   D E N S I T Y . C X X
  * BRL-CAD
  *
  * Copyright (c) 2009-2019 United States Government as represented by
@@ -22,14 +22,91 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <map>
+#include <string>
+#include <sstream>
+
 #include "analyze.h"
 
-int
-parse_densities_buffer(char *obuf, size_t len, struct density_entry 
**densities, struct bu_vls *result_str, int *num_densities)
+#define MAX_MATERIAL_ID   9999999999
+#define MAX_MATERIAL_CNT 10000000000
+
+struct analyze_densities_impl {
+    std::map<long int,std::string> id2name;
+    std::map<long int,fastf_t> id2density;
+    std::multimap<std::string, long int> name2id;
+};
+
+extern "C" int
+analyze_densities_init(struct analyze_densities *a)
 {
+    if (!a) return -1;
+    a->i = new analyze_densities_impl;
+    return (!a->i) ? -1 : 0;
+}
+
+extern "C" void
+analyze_densities_clear(struct analyze_densities *a)
+{
+    if (!a) return;
+    if (!a->i) return;
+    a->i->id2name.clear();
+    a->i->id2density.clear();
+    a->i->name2id.clear();
+    delete a->i;
+}
+
+extern "C" int
+analyze_densities_create(struct analyze_densities **a)
+{
+    if (!a) return -1;
+    (*a) = new analyze_densities;
+    return analyze_densities_init(*a);
+}
+extern "C" void
+analyze_densities_destroy(struct analyze_densities *a)
+{
+    if (!a) return;
+    analyze_densities_clear(a);
+    delete a;
+}
+
+extern "C" int
+analyze_densities_set(struct analyze_densities *a, long int id, fastf_t 
density, const char *name, struct bu_vls *msgs)
+{
+    if (!a || !a->i) return -1;
+    if (id < 0 || density < 0 || !name) return -1;
+
+    if (id > MAX_MATERIAL_ID) {
+       if (msgs) {
+           bu_vls_printf(msgs, "Error: material id %ld is large than 
MAX_MATERIAL_ID (%ld)\n", id, MAX_MATERIAL_ID);
+       }
+       return -1;
+    }
+
+    if (a->i->id2density.size() == MAX_MATERIAL_CNT) {
+       if (msgs) {
+           bu_vls_printf(msgs, "Error: analyze densities database is full - 
maximum density count of %ld has been reached\n", MAX_MATERIAL_CNT);
+       }
+       return -1;
+    }
+
+    a->i->id2name[id] = std::string(name);
+    a->i->id2density[id] = density;
+    a->i->name2id.insert(std::pair<std::string, long int>(std::string(name), 
id));
+    return 0;
+}
+
+static int
+parse_densities_line(struct analyze_densities *a, const char *obuf, size_t 
len, struct bu_vls *result_str)
+{
+    double density = -1;
+    char *name = NULL;
+    long int material_id = -1;
+
+    int have_density = 0;
     char *p, *q, *last;
     long idx;
-    double density;
     char *buf = (char *)bu_malloc(sizeof(char)*len+1, "buffer copy");
     memcpy(buf, obuf, len);
 
@@ -62,30 +139,47 @@
            continue;
        }
 
+       if (have_density) {
+           bu_free(buf, "free buf copy");
+           bu_free(name, "free name copy");
+           if (result_str) {
+               bu_vls_printf(result_str, "Error processing line: \"%s\"\nExtra 
content after density entry\n", obuf);
+           }
+           return -1;
+       }
+
        idx = strtol(p, &q, 10);
        if (q == (char *)NULL) {
            bu_free(buf, "free buf copy");
-           bu_vls_printf(result_str, "could not convert idx\n");
-           return ANALYZE_ERROR;
+           if (result_str) {
+               bu_vls_printf(result_str, "Error processing line: \"%s\"\ncould 
not convert idx\n", obuf);
+           }
+           return -1;
        }
 
        if (idx < 0) {
            bu_free(buf, "free buf copy");
-           bu_vls_printf(result_str, "bad density index (%ld < 0)\n", idx);
-           return ANALYZE_ERROR;
+           if (result_str) {
+               bu_vls_printf(result_str, "Error processing line: \"%s\"\nbad 
density index (%ld < 0)\n", obuf, idx);
+           }
+           return -1;
        }
 
        density = strtod(q, &p);
        if (q == p) {
            bu_free(buf, "free buf copy");
-           bu_vls_printf(result_str, "could not convert density\n");
-           return ANALYZE_ERROR;
+           if (result_str) {
+               bu_vls_printf(result_str, "Error processing line: \"%s\"\ncould 
not convert density\n", obuf);
+           }
+           return -1;
        }
 
        if (density < 0.0) {
            bu_free(buf, "free buf copy");
-           bu_vls_printf(result_str, "bad density (%lf < 0)\n", density);
-           return ANALYZE_ERROR;
+           if (result_str) {
+               bu_vls_printf(result_str, "Error processing line: \"%s\"\nbad 
density (%lf < 0)\n", obuf, density);
+           }
+           return -1;
        }
 
        /* Skip tabs and spaces */
@@ -99,18 +193,23 @@
        else
            q = last;
 
-       while (idx >= *num_densities) {
-           *densities = (struct density_entry *)bu_realloc(*densities, 
sizeof(struct density_entry)*(*num_densities)*2,
-                                                          "density entries");
-           *num_densities *= 2;
+
+       /* Trim off comments and ws, if any. */
+       std::string wstr(p);
+       std::string nstr;
+       size_t ep = wstr.find_first_of("#");
+       if (ep != std::string::npos) {
+           wstr = wstr.substr(0, ep);
        }
-
-       (*densities)[idx].magic = DENSITY_MAGIC;
+       ep = wstr.find_last_not_of(" \t\n\v\f\r");
+       size_t sp = wstr.find_first_not_of(" \t\n\v\f\r");
+       nstr = wstr.substr(sp, ep-sp+1);
        /* since BRL-CAD does computation in mm, but the table is in
         * grams / (cm^3) we convert the table on input
         */
-       (*densities)[idx].grams_per_cu_mm = density / 1000.0;
-       (*densities)[idx].name = bu_strdup(p);
+       density = density / 1000.0;
+       name = bu_strdup(nstr.c_str());
+       material_id = idx;
 
        p = q;
 
@@ -118,20 +217,147 @@
        while (*p && (*p == '\t' || *p == ' ' || *p == '\n')) p++;
     }
 
-#ifdef PRINT_DENSITIES
-    for (idx = 0; idx < *num_densities; idx++)
-       if ((*densities)[idx].magic == DENSITY_MAGIC)
-           bu_vls_printf(&_ged_current_gedp->ged_result_str, "%4d %6g %s\n",
-                         idx,
-                         (*densities)[idx].grams_per_cu_mm,
-                         (*densities)[idx].name);
-#endif
+    /* Whatever we saw, if it wasn't a valid definition bail now */
+    if (!name || material_id == -1 || density < 0) {
+       return -1;
+    }
 
+    /* If we have a valid definition, try to insert it */
+    if (analyze_densities_set(a, material_id, density, name, result_str) < 0) {
+       bu_free(buf, "free buf copy");
+       if (result_str) {
+           bu_vls_printf(result_str, "Error inserting density %ld,%g,%s\n", 
material_id, density, name);
+       }
+       bu_free(name, "free name copy");
+       return -1;
+
+    }
+
+    bu_free(name, "free name copy");
     bu_free(buf, "free buf copy");
-    return ANALYZE_OK;
+    return 0;
 }
 
+extern "C" int
+analyze_densities_load(struct analyze_densities *a, const char *buff, struct 
bu_vls *msgs)
+{
+    if (!a || !a->i || !buff) return 0;
+    std::string dbuff(buff);
+    std::istringstream ss(dbuff);
+    std::string line;
+    int dcnt = 0;
+    while (std::getline(ss, line)) {
+       struct bu_vls lmsg = BU_VLS_INIT_ZERO;
+       int ret = parse_densities_line(a, line.c_str(), line.length(), &lmsg);
+       if (ret < 0) {
+          if (msgs && bu_vls_strlen(&lmsg)) {
+           bu_vls_printf(msgs, "%s", bu_vls_cstr(&lmsg));
+          }
+       } else {
+           dcnt++;
+       }
+       bu_vls_free(&lmsg);
+    }
+    return dcnt;
+}
 
+
+extern "C" long int
+analyze_densities_write(char **buff, struct analyze_densities *a)
+{
+    if (!a || !a->i || !buff) return 0;
+    std::string obuff;
+    std::ostringstream os(obuff);
+
+    std::map<long int,std::string>::iterator id_it;
+    for (id_it = a->i->id2name.begin(); id_it != a->i->id2name.end(); id_it++) 
{
+       long int nid = id_it->first;
+       std::string nname = id_it->second;
+       fastf_t ndensity = analyze_densities_density(a, nid);
+       os << nid << "\t" << ndensity * 1000 << "\t" << nname << "\n";
+    }
+
+    std::string ostring = os.str();
+    (*buff) = bu_strdup(ostring.c_str());
+
+    return (long int)ostring.length();
+}
+
+
+extern "C" char *
+analyze_densities_name(struct analyze_densities *a, long int id)
+{
+    if (!a || !a->i || id < 0) return NULL;
+    if (a->i->id2name.find(id) == a->i->id2name.end()) {
+       return NULL;
+    }
+    return bu_strdup(a->i->id2name[id].c_str());
+}
+
+extern "C" long int
+analyze_densities_id(long int *ids, long int max_ids, struct analyze_densities 
*a, const char *name)
+{
+    int mcnt = 0;
+    std::multimap<std::string, long int>::iterator m_it;
+    std::pair<std::multimap<std::string, long int>::iterator, 
std::multimap<std::string, long int>::iterator> eret;
+    if (!a || !a->i || !name) return -1;
+    eret = a->i->name2id.equal_range(std::string(name));
+    for (m_it = eret.first; m_it != eret.second; m_it++) {
+       mcnt++;
+    }
+
+    if (!ids || max_ids == 0) {
+       return mcnt;
+    }
+
+    /* Have an id buffer, return what we can */
+    if (mcnt > 0) {
+       int ccnt = 0;
+       for (m_it = eret.first; m_it != eret.second; m_it++) {
+           if (ccnt < max_ids) {
+               ids[ccnt] = m_it->second;
+               ccnt++;
+           }
+       }
+    }
+
+    return (mcnt > max_ids) ? (max_ids - mcnt) : mcnt;
+}
+
+extern "C" fastf_t
+analyze_densities_density(struct analyze_densities *a, long int id)
+{
+    if (!a || !a->i || id < 0) return -1.0;
+    if (a->i->id2density.find(id) == a->i->id2density.end()) {
+       return -1.0;
+    }
+    return a->i->id2density[id];
+}
+
+extern "C" long int
+analyze_densities_next(struct analyze_densities *a, long int id)
+{
+    if (!a || !a->i) return -1;
+    if (id < 0) {
+       if (a->i->id2density.size()) {
+           std::map<long int,fastf_t>::iterator i = a->i->id2density.begin();
+           return i->first;
+       } else {
+           return -1;
+       }
+    }
+    std::map<long int,fastf_t>::iterator idc = a->i->id2density.find(id);
+    if (idc != a->i->id2density.end()) {
+       std::map<long int,fastf_t>::iterator idn = std::next(idc);
+       if (idn != a->i->id2density.end()) {
+           return idn->first;
+       } else {
+           return -1;
+       }
+    }
+    return -1;
+}
+
 /*
  * Local Variables:
  * tab-width: 8

Modified: brlcad/trunk/src/libanalyze/tests/CMakeLists.txt
===================================================================
--- brlcad/trunk/src/libanalyze/tests/CMakeLists.txt    2019-02-08 00:43:55 UTC 
(rev 72372)
+++ brlcad/trunk/src/libanalyze/tests/CMakeLists.txt    2019-02-08 18:28:41 UTC 
(rev 72373)
@@ -1,7 +1,15 @@
-BRLCAD_ADDEXEC(tester_density density.c "libanalyze;libbu" NO_INSTALL)
 BRLCAD_ADDEXEC(tester_raydiff raydiff.c "libanalyze;libbu" NO_INSTALL)
 BRLCAD_ADDEXEC(tester_sp solid_partitions.c "libanalyze;libbu" NO_INSTALL)
 
+#####################################
+#      analyze_densities testing    #
+#####################################
+BRLCAD_ADDEXEC(analyze_densities density.cxx "libanalyze;libbu" NO_INSTALL)
+
+add_test(NAME analyze_densities_null        COMMAND analyze_densities)
+add_test(NAME analyze_densities_std        COMMAND analyze_densities std)
+
+
 CMAKEFILES(raydiff.g)
 CMAKEFILES(CMakeLists.txt)
 

Deleted: brlcad/trunk/src/libanalyze/tests/density.c
===================================================================
--- brlcad/trunk/src/libanalyze/tests/density.c 2019-02-08 00:43:55 UTC (rev 
72372)
+++ brlcad/trunk/src/libanalyze/tests/density.c 2019-02-08 18:28:41 UTC (rev 
72373)
@@ -1,71 +0,0 @@
-/*                    T E S T _ D E N S I T Y . C
- * BRL-CAD
- *
- * Copyright (c) 2011-2019 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.
- */
-
-#include "common.h"
-
-#include <sys/stat.h>
-#include "analyze.h"
-
-int
-main(int argc, char **argv)
-{
-  struct density_entry *densities = NULL;
-  static int num_densities = 1028;
-
-  struct stat sb;
-  FILE *fp = (FILE *)NULL;
-  char *buf = NULL;
-  int ret = 0;
-  int i;
-
-  if (argc < 2) {
-      bu_log("Error - please supply density file\n");
-      bu_exit(EXIT_FAILURE, NULL);
-  }
-
-  fp = fopen(argv[1], "rb");
-  if (fp == (FILE *)NULL) {
-      bu_log("Error - file %s not opened\n", argv[1]);
-      bu_exit(EXIT_FAILURE, NULL);
-  }
-
-  if (stat(argv[1], &sb)) {
-      bu_log("Error - file %s not stat successfully\n", argv[1]);
-      bu_exit(EXIT_FAILURE, NULL);
-  }
-
-  buf = (char *)bu_malloc(sb.st_size+1, "density buffer");
-  ret = fread(buf, sb.st_size, 1, fp);
-  if (ret != 1) {
-    bu_log("Error reading file %s\n", argv[1]);
-    bu_exit(EXIT_FAILURE, NULL);
-  }
-
-  densities = (struct density_entry *)bu_calloc(num_densities, sizeof(struct 
density_entry), "density entries");
-
-  ret = parse_densities_buffer(buf, (unsigned long)sb.st_size, &densities, 
NULL, &num_densities);
-
-  for (i = 0; i < num_densities; i++) {
-      if (densities[i].name)
-         bu_log("densities[%i]: %s, %f\n", i, densities[i].name, 
densities[i].grams_per_cu_mm);
-  }
-
-  return 0;
-}

Added: brlcad/trunk/src/libanalyze/tests/density.cxx
===================================================================
--- brlcad/trunk/src/libanalyze/tests/density.cxx                               
(rev 0)
+++ brlcad/trunk/src/libanalyze/tests/density.cxx       2019-02-08 18:28:41 UTC 
(rev 72373)
@@ -0,0 +1,324 @@
+/*                      D E N S I T Y . C X X
+ * BRL-CAD
+ *
+ * Copyright (c) 2011-2019 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.
+ */
+
+#include "common.h"
+
+#include <set>
+
+#include "analyze.h"
+
+const char *simple_buff = "1 7.8295        steel";
+
+const char *test_buff =
+"#  Test density file with comments and bad input\n"
+"2    7.82      Carbon Tool Steel\n"
+"3    2.7       Aluminum, 6061-T6\n"
+"4    2.74      Aluminum, 7079-T6\n"
+"#  Incomplete line following\n"
+"               Copper, pure\n"
+"               6    19.32     Gold, pure\n"
+"               7    8.03      Stainless, 18Cr-8Ni\n"
+"#  Comment\n"
+"               8    7.47      Stainless 27Cr\n"
+"\n"
+"               9    7.715     Steel, tool\n"
+"\n"
+"#  Blank line above\n"
+"#  Comment following valid data on the line below\n"
+"               10   7.84      Carbon Steel # used for widgets\n"
+"               12   3.00      Gunner\n"
+"               14   10.00     Fuel\n"
+"#  Material ID too high \n"
+"99999999999 70.84    Kryptonite\n";
+
+const char *tbuff_out_ctrl =
+"2     7.82    Carbon Tool Steel\n"
+"3     2.7     Aluminum, 6061-T6\n"
+"4     2.74    Aluminum, 7079-T6\n"
+"6     19.32   Gold, pure\n"
+"7     8.03    Stainless, 18Cr-8Ni\n"
+"8     7.47    Stainless 27Cr\n"
+"9     7.715   Steel, tool\n"
+"10    7.84    Carbon Steel\n"
+"12    3       Gunner\n"
+"14    10      Fuel\n";
+
+int
+dtest_validate(struct analyze_densities *a, long int id, fastf_t density, 
const char *name)
+{
+    char *db_name = analyze_densities_name(a, id);
+    if (!db_name) {
+       bu_log("Error - could not find name for id %ld\n", id);
+       return -1;
+    }
+    if (!BU_STR_EQUAL(name, db_name)) {
+       bu_log("Error - found incorrect name for %ld (%s instead of %s)\n", id, 
db_name, name);
+       bu_free(db_name, "free name");
+       return -1;
+    }
+    bu_free(db_name, "free name");
+    
+    fastf_t db_density = analyze_densities_density(a, id);
+    if (db_density < 0) {
+       bu_log("Error - could not find density for id %ld\n", id);
+       return -1;
+    }
+    if (!NEAR_EQUAL(density, db_density, SMALL_FASTF)) {
+       bu_log("Error - found incorrect density for %ld (%g instead of %g)\n", 
id, db_density, density);
+       return -1;
+    }
+
+    long int max_ids = 3;
+    long int ids[3] = {-1, -1, -1};
+    long int idcnt = analyze_densities_id((long int *)ids, max_ids, a, name);
+    if (idcnt == 0) {
+       bu_log("Error - could not find any ids associated with name %s\n", 
name);
+       return -1;
+    }
+    int have_correct_id = 0;
+    for (long int i = 0; i < max_ids; i++) {
+       if (ids[i] >= 0 && ids[i] == id) {
+           have_correct_id = 1;
+           break;
+       }
+    }
+    if (!have_correct_id) {
+       bu_log("Error - could not find correct id (%ld) associated with name 
%s\n", id, name);
+       return -1;
+    }
+
+    return 0;
+}
+
+int
+dtest_insert(struct analyze_densities *a, long int id, fastf_t density, const 
char *name, struct bu_vls *msgs)
+{
+    if (analyze_densities_set(a, id, density, name, msgs)) {
+       bu_log("Error inserting density: %s\n", bu_vls_cstr(msgs));
+       return -1;
+    }
+    if (dtest_validate(a, id, density, name)) {
+       return -1;
+    }
+    return 0;
+}
+
+int
+main(int argc, char **argv)
+{
+    struct bu_vls msgs = BU_VLS_INIT_ZERO;
+    struct analyze_densities *a;
+
+    if (!argc || !argv) {
+       return -1;
+    }
+
+    /* Test behaviors with an empty database */
+    if (argc == 1) {
+       long int max_ids = 2;
+       long int ids[2];
+
+       analyze_densities_create(&a);
+
+       if (analyze_densities_name(a, 0)) {
+           bu_log("Error - empty database returned a non-NULL name on id 
lookup??\n");
+           goto analyze_density_fail;
+       }
+       if (analyze_densities_id((long int *)ids, max_ids, a, "Aluminum") != 0) 
{
+           bu_log("Error - empty database returned a non-NULL id set on name 
lookup??\n");
+           goto analyze_density_fail;
+       }
+       if (analyze_densities_density(a, 0) >= 0) {
+           bu_log("Error - empty database returned a density on id 
lookup??\n");
+           goto analyze_density_fail;
+       }
+       if (analyze_densities_load(a, NULL, &msgs) != 0) {
+           bu_log("Error - loading NULL buffer returned a non-zero density 
count??\nMsg: %s", bu_vls_cstr(&msgs));
+           goto analyze_density_fail;
+       }
+       if (analyze_densities_load(a, "", &msgs) != 0) {
+           bu_log("Error - loading empty buffer returned a non-zero density 
count??Msg: %s\n", bu_vls_cstr(&msgs));
+           goto analyze_density_fail;
+       }
+       if (analyze_densities_next(a, -1) != -1) {
+           bu_log("Error - found initial \"next\" iterator in empty 
database??\n");
+           goto analyze_density_fail;
+       }
+       if (analyze_densities_next(a, 0) != -1) {
+           bu_log("Error - found \"next\" iterator in empty database with 
invalid id??\n");
+           goto analyze_density_fail;
+       }
+
+       analyze_densities_destroy(a);
+       bu_vls_free(&msgs);
+       return 0;
+    }
+
+    if (BU_STR_EQUAL(argv[1], "std")) {
+
+       std::set<long int> valid_ids = {0,2,15,4,31};
+
+       analyze_densities_create(&a);
+
+       /* Initialize with several values */
+       if (dtest_insert(a, 0, 0.007715, "Steel", &msgs)) {
+           goto analyze_density_fail;
+       }
+       if (dtest_insert(a, 2, 0.00782, "Carbon Tool Steel", &msgs)) {
+           goto analyze_density_fail;
+       }
+       if (dtest_insert(a, 15, 0.0027, "Aluminum, 6061-T6", &msgs)) {
+           goto analyze_density_fail;
+       }
+       if (dtest_insert(a, 4,  0.00274, "Aluminum, 7079-T6", &msgs)) {
+           goto analyze_density_fail;
+       }
+
+       /* Check assignment and duplicate id behaviors given existing entries */
+
+       /* Redefine id 0 */
+       if (dtest_insert(a, 0, 0.001, "Light Steel", &msgs)) {
+           goto analyze_density_fail;
+       }
+
+       /* Rename id 15 */
+       if (dtest_insert(a, 15, 0.0027, "Aluminum", &msgs)) {
+           goto analyze_density_fail;
+       }
+
+       /* Add another id for Carbon Tool Steel */
+       if (dtest_insert(a, 31, analyze_densities_density(a, 2), "Carbon Tool 
Steel", &msgs)) {
+           goto analyze_density_fail;
+       }
+
+       /* Check that we can retrieve both IDs for Carbon Tool Steel */
+       long int max_ids = 2;
+       long int ids[2] = {-1, -1};
+       long int idcnt = analyze_densities_id((long int *)ids, max_ids, a, 
"Carbon Tool Steel");
+       if (idcnt != 2) {
+           bu_log("Error - could not find both ids for Carbon Tool Steel\n");
+           goto analyze_density_fail;
+       }
+       std::set<long int> exp_cts = {2,31};
+       std::set<long int> found_cts;
+       for (long int i = 0; i < 2; i++) {
+           found_cts.insert(ids[i]);
+       }
+       if (found_cts != exp_cts) {
+           bu_log("Error - could not find both ids for Carbon Tool Steel\n");
+           goto analyze_density_fail;
+       }
+
+       /* Check that we can iterate over the database */
+       std::set<long int> found_ids;
+       long int curr_id = -1;
+       while ((curr_id = analyze_densities_next(a, curr_id)) != -1) {
+           if (valid_ids.find(curr_id) == valid_ids.end()) {
+               bu_log("Error - found invalid id %ld while iterating\n", 
curr_id);
+               goto analyze_density_fail;
+           } else {
+               found_ids.insert(curr_id);
+           }
+       }
+       if (found_ids != valid_ids) {
+           std::set<long int>::iterator f_it;
+           bu_log("Error - did not find the exact set of all valid ids while 
iterating\nFound: ");
+           for (f_it = found_ids.begin(); f_it != found_ids.end(); f_it++) {
+               bu_log("%ld ", *f_it);
+           }
+           bu_log("\nExpecting: ");
+           for (f_it = valid_ids.begin(); f_it != valid_ids.end(); f_it++) {
+               bu_log("%ld ", *f_it);
+           }
+           bu_log("\n");
+           goto analyze_density_fail;
+       }
+
+       /* Clear the database */
+       analyze_densities_clear(a);
+       analyze_densities_init(a);
+       curr_id = -1;
+       long int id_cnt = 0;
+       while ((curr_id = analyze_densities_next(a, curr_id)) != -1) id_cnt++;
+       if (id_cnt) {
+           bu_log("Error - analyze_densities_clear + analyze_densities_init 
failed to rest the database: found %ld entities\n", id_cnt);
+           goto analyze_density_fail;
+       }
+
+       /* Test loading from buffers */
+       long int sb_cnt = analyze_densities_load(a, simple_buff, &msgs);
+       if (!sb_cnt) {
+           bu_log("Error - could not find id in simple test buffer: %s\n", 
bu_vls_cstr(&msgs));
+           goto analyze_density_fail;
+       }
+       if (sb_cnt != 1) {
+           bu_log("Error - found %ld ids in test buffer, but expected 1\n", 
sb_cnt);
+           goto analyze_density_fail;
+       }
+
+       analyze_densities_clear(a);
+       analyze_densities_init(a);
+
+       long int tb_cnt = analyze_densities_load(a, test_buff, &msgs);
+       if (!tb_cnt) {
+           bu_log("Error - could not find ids in test buffer: %s\n", 
bu_vls_cstr(&msgs));
+           goto analyze_density_fail;
+       }
+       if (tb_cnt != 10) {
+           bu_log("Error - found %ld ids in test buffer, but expected 10\n", 
tb_cnt);
+           goto analyze_density_fail;
+       }
+
+       /* Test writing out a buffer */
+       char *tbuff_out;
+       long int tbuff_len = analyze_densities_write(&tbuff_out, a);
+       if (!tbuff_len) {
+           bu_log("Error - unable to write out buffer!\n");
+           goto analyze_density_fail;
+       }
+
+       if (!BU_STR_EQUAL(tbuff_out, tbuff_out_ctrl)) {
+           bu_log("Error - buffer does not match!\n");
+           bu_log("Expected:\n%s", tbuff_out_ctrl);
+           bu_log("Got:\n%s", tbuff_out);
+           goto analyze_density_fail;
+       }
+
+       analyze_densities_destroy(a);
+       bu_vls_free(&msgs);
+       return 0;
+    }
+
+analyze_density_fail:
+    analyze_densities_destroy(a);
+    bu_vls_free(&msgs);
+    return -1;
+}
+
+/*
+ * Local Variables:
+ * mode: C
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * c-file-style: "stroustrup"
+ * End:
+ * ex: shiftwidth=4 tabstop=8
+ */
+


Property changes on: brlcad/trunk/src/libanalyze/tests/density.cxx
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Modified: brlcad/trunk/src/libged/gqa.c
===================================================================
--- brlcad/trunk/src/libged/gqa.c       2019-02-08 00:43:55 UTC (rev 72372)
+++ brlcad/trunk/src/libged/gqa.c       2019-02-08 18:28:41 UTC (rev 72373)
@@ -163,8 +163,7 @@
 } ged_gqa_plot;
 
 /* the entries in the density table */
-struct density_entry *densities = NULL;
-static int num_densities;
+struct analyze_densities *densities = NULL;
 
 /* summary data structure for objects specified on command line */
 static struct per_obj_data {
@@ -749,44 +748,41 @@
  * 0 on success
  * !0 on failure
  */
-int
+HIDDEN int
 get_densities_from_file(char *name)
 {
-    struct stat sb;
-
-    FILE *fp = (FILE *)NULL;
+    struct bu_vls msgs = BU_VLS_INIT_ZERO;
+    struct bu_mapped_file *dfile = NULL;
     char *buf = NULL;
     int ret = 0;
-    size_t sret = 0;
 
-    fp = fopen(name, "rb");
-    if (fp == (FILE *)NULL) {
-       bu_vls_printf(_ged_current_gedp->ged_result_str, "Could not open file - 
%s\n", name);
+    if (!bu_file_exists(name, NULL)) {
+       bu_vls_printf(_ged_current_gedp->ged_result_str, "Could not find 
density file - %s\n", name);
        return GED_ERROR;
     }
 
-    if (stat(name, &sb)) {
-       bu_vls_printf(_ged_current_gedp->ged_result_str, "Could not read file - 
%s\n", name);
-       fclose(fp);
+    dfile = bu_open_mapped_file(name, "densities file");
+    if (!dfile) {
+       bu_vls_printf(_ged_current_gedp->ged_result_str, "Could not open 
density file - %s\n", name);
        return GED_ERROR;
     }
 
-    densities = (struct density_entry *)bu_calloc(128, sizeof(struct 
density_entry), "density entries");
-    num_densities = 128;
+    buf = (char *)(dfile->buf);
 
-    /* a mapped file would make more sense here */
-    buf = (char *)bu_malloc(sb.st_size+1, "density buffer");
-    sret = fread(buf, sb.st_size, 1, fp);
-    if (sret != 1)
-       perror("fread");
-    ret = parse_densities_buffer(buf, (unsigned long)sb.st_size, &densities, 
_ged_current_gedp->ged_result_str, &num_densities);
-    bu_free(buf, "density buffer");
-    fclose(fp);
+    (void)analyze_densities_create(&densities);
 
-    return ret;
+    ret = analyze_densities_load(densities, buf, &msgs);
+
+    if (bu_vls_strlen(&msgs)) {
+       bu_vls_printf(_ged_current_gedp->ged_result_str, "Problem reading 
densities file:\n%s\n", bu_vls_cstr(&msgs));
+    }
+    bu_vls_free(&msgs);
+
+    bu_close_mapped_file(dfile);
+
+    return (ret <= 0) ? GED_ERROR : GED_OK;
 }
 
-
 /**
  * Returns
  * 0 on success
@@ -795,6 +791,7 @@
 int
 get_densities_from_database(struct rt_i *rtip)
 {
+    struct bu_vls msgs = BU_VLS_INIT_ZERO;
     struct directory *dp;
     struct rt_db_internal intern;
     struct rt_binunif_internal *bu;
@@ -820,15 +817,18 @@
 
     RT_CHECK_BINUNIF (bu);
 
-    densities = (struct density_entry *)bu_calloc(128, sizeof(struct 
density_entry), "density entries");
-    num_densities = 128;
+    (void)analyze_densities_create(&densities);
 
-    /* Acquire one extra byte to accommodate parse_densities_buffer()
-     * (i.e. it wants to write an EOS in buf[bu->count]).
-     */
     buf = (char *)bu_malloc(bu->count+1, "density buffer");
     memcpy(buf, bu->u.int8, bu->count);
-    ret = parse_densities_buffer(buf, bu->count, &densities, 
_ged_current_gedp->ged_result_str, &num_densities);
+
+    ret = analyze_densities_load(densities, buf, &msgs);
+
+    if (bu_vls_strlen(&msgs)) {
+       bu_vls_printf(_ged_current_gedp->ged_result_str, "Problem reading 
densities file:\n%s\n", bu_vls_cstr(&msgs));
+    }
+    bu_vls_free(&msgs);
+
     bu_free((void *)buf, "density buffer");
 
     return ret;
@@ -993,8 +993,10 @@
 
     /* examine each partition until we get back to the head */
     for (pp=PartHeadp->pt_forw; pp != PartHeadp; pp = pp->pt_forw) {
-       struct density_entry *de;
 
+       long int material_id = pp->pt_regionp->reg_gmater;
+       fastf_t grams_per_cu_mm = analyze_densities_density(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);
@@ -1068,135 +1070,121 @@
            }
 
            /* make sure mater index is within range of densities */
-           if (pp->pt_regionp->reg_gmater >= num_densities) {
+           if (pp->pt_regionp->reg_gmater < 0) {
                bu_semaphore_acquire(GED_SEM_WORKER);
-               bu_vls_printf(_ged_current_gedp->ged_result_str, "density index 
%d on region %s is outside of range of table [1..%d]\nSet GIFTmater on region 
or add entry to density table\n",
+               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,
-                             num_densities); /* XXX this should do something 
else */
+                             pp->pt_regionp->reg_name);
                bu_semaphore_release(GED_SEM_WORKER);
                return GED_ERROR;
            } else {
 
-               /* make sure the density index has been set */
-               de = &densities[pp->pt_regionp->reg_gmater];
-               if (de->magic == DENSITY_MAGIC) {
-                   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;
+               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;
-                   }
+               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;
+               /* 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(GED_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(GED_SEM_WORKER);
-                   }
+               if (los < 1) {
+                   bu_semaphore_acquire(GED_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(GED_SEM_WORKER);
+               }
 
-                   /* accumulate the total weight values */
-                   val = de->grams_per_cu_mm * dist * (pp->pt_regionp->reg_los 
* 0.01);
-                   ap->A_LENDEN += val;
+               /* 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(GED_SEM_STATS);
-                   prd->r_lenDensity[state->i_axis] += val;
+               prd = ((struct per_region_data *)pp->pt_regionp->reg_udata);
+               /* accumulate the per-region per-view weight values */
+               bu_semaphore_acquire(GED_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;
+               /* 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);
+               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);
+                   /* 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 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);
+                   /* 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;
+                   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 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];
-                       }
+                       /* 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(GED_SEM_STATS);
-
-               } else {
-                   bu_semaphore_acquire(GED_SEM_WORKER);
-                   bu_vls_printf(_ged_current_gedp->ged_result_str, "density 
index %d from region %s is not set.\nAdd entry to density table\n",
-                                 pp->pt_regionp->reg_gmater, 
pp->pt_regionp->reg_name);
-                   bu_semaphore_release(GED_SEM_WORKER);
-
-                   aborted = 1;
-                   return GED_ERROR;
-               }
+               bu_semaphore_release(GED_SEM_STATS);
            }
        }
 
@@ -1658,10 +1646,10 @@
     if (analysis_flags & ANALYSIS_WEIGHTS) {
        if (weight_tolerance < 0.0) {
            double max_den = 0.0;
-           int i;
-           for (i = 0; i < num_densities; i++) {
-               if (densities[i].grams_per_cu_mm > max_den)
-                   max_den = densities[i].grams_per_cu_mm;
+           long int curr_id = -1;
+           while ((curr_id = analyze_densities_next(densities, curr_id)) != 
-1) {
+               if (analyze_densities_density(densities, curr_id) > max_den)
+                   max_den = analyze_densities_density(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",

Modified: brlcad/trunk/src/libged/mater.cxx
===================================================================
--- brlcad/trunk/src/libged/mater.cxx   2019-02-08 00:43:55 UTC (rev 72372)
+++ brlcad/trunk/src/libged/mater.cxx   2019-02-08 18:28:41 UTC (rev 72373)
@@ -311,17 +311,13 @@
 _ged_mater_mat_id(struct ged *gedp, int argc, const char *argv[])
 {
     int names_from_ids = 0;
-    int num_densities = 128;
-    struct density_entry *densities = (struct density_entry 
*)bu_calloc(num_densities, sizeof(struct density_entry), "density entries");
+    struct analyze_densities *densities;
     struct bu_mapped_file *dfile = NULL;
     char *dbuff = NULL;
     struct bu_mapped_file *mfile = NULL;
     char *mbuff = NULL;
-    std::map<std::string, int> n2d;
-    std::map<int, std::string> d2n;
     std::set<std::string> listed_materials;
     std::set<std::string> defined_materials;
-    std::set<std::string> g_materials;
     std::map<std::string,std::string> listed_to_defined;
 
     if (BU_STR_EQUAL(argv[1], "--names-from-ids")) {
@@ -348,6 +344,8 @@
 
     /* OK, we know what we're doing - start reading inputs */
 
+    analyze_densities_create(&densities);
+
     dfile = bu_open_mapped_file(argv[1], "densities file");
     if (!dfile) {
        bu_vls_printf(gedp->ged_result_str, "could not open density file %s", 
argv[1]);
@@ -355,39 +353,23 @@
     }
 
     dbuff = (char *)(dfile->buf);
-    if (parse_densities_buffer(dbuff, dfile->buflen, &densities, NULL, 
&num_densities) !=  ANALYZE_OK) {
-       bu_vls_printf(gedp->ged_result_str, "could not parse density file %s", 
argv[1]);
+    struct bu_vls pbuff_msgs = BU_VLS_INIT_ZERO;
+    if (analyze_densities_load(densities, dbuff, &pbuff_msgs) ==  0) {
+       bu_vls_printf(gedp->ged_result_str, "could not parse density file %s: 
%s", argv[1], bu_vls_cstr(&pbuff_msgs));
        bu_close_mapped_file(dfile);
+       bu_vls_free(&pbuff_msgs);
        return GED_ERROR;
-    } else {
-       /* Populate the name->material_id map from the density file */
-       int found_duplicate = 0;
-       for (int idx = 0; idx < num_densities; idx++) {
-           if (densities[idx].magic == DENSITY_MAGIC) {
-               if (n2d.find(std::string(densities[idx].name)) != n2d.end()) {
-                   found_duplicate = 1;
-                   bu_vls_printf(gedp->ged_result_str, "density file contains 
multiple densities for %s", densities[idx].name);
-               }
-               n2d[std::string(densities[idx].name)] = idx;
-               defined_materials.insert(std::string(densities[idx].name));
-               bu_free(densities[idx].name, "free density name");
-           }
-       }
-       std::map<std::string, int>::iterator n2d_it;
-       for (n2d_it = n2d.begin(); n2d_it != n2d.end(); n2d_it++) {
-           d2n[n2d_it->second] = n2d_it->first;
-       }
-       bu_free(densities, "density_entry array");
-       bu_close_mapped_file(dfile);
-       if (found_duplicate) {
-           return GED_ERROR;
-       }
     }
 
     // For simplicity, let the complex -> simple map know to map known 
materials to themselves
-    std::set<std::string>::iterator s_it;
-    for (s_it = defined_materials.begin(); s_it != defined_materials.end(); 
s_it++) {
-       listed_to_defined.insert(std::pair<std::string, std::string>(*s_it, 
*s_it));
+    long int curr_id = -1;
+    long int id_cnt = 0;
+    while ((curr_id = analyze_densities_next(densities, curr_id)) != -1) {
+       id_cnt++;
+       char *cname = analyze_densities_name(densities, curr_id);
+       listed_to_defined.insert(std::pair<std::string, 
std::string>(std::string(cname), std::string(cname)));
+       defined_materials.insert(std::string(cname));
+       bu_free(cname, "free name copy");
     }
 
     if (argc == 3 && !names_from_ids) {
@@ -439,11 +421,12 @@
            }
            const char *mat_id = bu_avs_get(&avs, "material_id");
            const char *oname = bu_avs_get(&avs, "material_name");
-           if (d2n.find(std::stoi(mat_id)) == d2n.end()) {
+           if (analyze_densities_density(densities, std::stol(mat_id)) < 0) {
                bu_vls_printf(gedp->ged_result_str, "Warning: no name found in 
density file for material_id %s on object %s, skipping\n", mat_id, dp->d_namep);
                if (oname) {
-                   if (n2d.find(std::string(oname)) != n2d.end()) {
-                       bu_vls_printf(gedp->ged_result_str, "Material id 
collision: object %s has material_name \"%s\" and material_id %s, but the 
specified density file defines \"%s\" as material_id %s.\n", dp->d_namep, 
oname, mat_id, oname, 
std::to_string(n2d.find(std::string(oname))->second).c_str());
+                   long int found_id_cnt = analyze_densities_id(NULL, 0, 
densities, oname);
+                   if (found_id_cnt) {
+                       bu_vls_printf(gedp->ged_result_str, "Material id 
collision: object %s has material_name \"%s\" and material_id %s, but the 
specified density file defines \"%s\" as a different material id.\n", 
dp->d_namep, oname, mat_id, oname);
                    }  else {
                        bu_vls_printf(gedp->ged_result_str, "Unknown material: 
object %s has material_name %s, which is not a material defined in the 
specified density file.\n", dp->d_namep, oname);
                    }
@@ -452,9 +435,9 @@
                continue;
            }
            // Found a name, assign it if it doesn't match
-           std::string nname = d2n[std::stoi(mat_id)];
-           if (!oname || nname.compare(std::string(oname))) {
-               (void)bu_avs_add(&avs, "material_name", nname.c_str());
+           char *nname = analyze_densities_name(densities, std::stol(mat_id));
+           if (!oname || !BU_STR_EQUAL(nname,oname)) {
+               (void)bu_avs_add(&avs, "material_name", nname);
                if (db5_update_attributes(dp, &avs, gedp->ged_wdbp->dbip)) {
                    bu_vls_printf(gedp->ged_result_str, "Error: failed to 
update object %s attributes\n", dp->d_namep);
                    bu_avs_free(&avs);
@@ -464,6 +447,7 @@
                // Already has correct name, no need to update
                bu_avs_free(&avs);
            }
+           bu_free(nname, "free name");
        }
        return GED_OK;
     } else {
@@ -498,7 +482,13 @@
            continue;
        }
 
-       int nid = n2d[listed_to_defined[std::string(oname)]];
+       long int wids[1];
+       int id_found_cnt = analyze_densities_id((long int *)wids, 1, densities, 
listed_to_defined[std::string(oname)].c_str());
+       if (!id_found_cnt) {
+           bu_vls_printf(gedp->ged_result_str, "Error: failed to find ID for 
%s\n", oname);
+           return GED_ERROR;
+       }
+       int nid = wids[0];
        if (!mat_id || std::stoi(mat_id) != nid) {
            (void)bu_avs_add(&avs, "material_id", std::to_string(nid).c_str());
            if (db5_update_attributes(dp, &avs, gedp->ged_wdbp->dbip)) {

Modified: brlcad/trunk/src/rt/CMakeLists.txt
===================================================================
--- brlcad/trunk/src/rt/CMakeLists.txt  2019-02-08 00:43:55 UTC (rev 72372)
+++ brlcad/trunk/src/rt/CMakeLists.txt  2019-02-08 18:28:41 UTC (rev 72373)
@@ -81,7 +81,7 @@
 
 BRLCAD_ADDEXEC(rtsil "${LIBRTUIF_SOURCES};viewsil.c" 
"${CMAKE_THREAD_LIBS_INIT};librt;libnmg;libfb;liboptical;libicv;${M_LIBRARY}")
 
-BRLCAD_ADDEXEC(rtweight "${LIBRTUIF_SOURCES};viewweight.c" 
"${CMAKE_THREAD_LIBS_INIT};librt;libnmg;libfb;liboptical;libicv;${M_LIBRARY}")
+BRLCAD_ADDEXEC(rtweight "${LIBRTUIF_SOURCES};viewweight.c" 
"${CMAKE_THREAD_LIBS_INIT};librt;libnmg;libfb;liboptical;libicv;libanalyze;${M_LIBRARY}")
 set_property(TARGET rtweight APPEND PROPERTY COMPILE_DEFINITIONS 
"RT_TXT_OUTPUT")
 
 BRLCAD_ADDEXEC(rtxray "${LIBRTUIF_SOURCES};viewxray.c" 
"${CMAKE_THREAD_LIBS_INIT};librt;libnmg;libfb;liboptical;libicv;${M_LIBRARY}")

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



_______________________________________________
BRL-CAD Source Commits mailing list
brlcad-commits@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/brlcad-commits

Reply via email to