Revision: 77222
          http://sourceforge.net/p/brlcad/code/77222
Author:   starseeker
Date:     2020-09-25 13:41:24 +0000 (Fri, 25 Sep 2020)
Log Message:
-----------
Merge analyze command rework from branch, add doc update and NEWS items.  
Immediate functionality is to use geometric inside/outside testing to peform 
boolean ops on point clouds and generate new point sets, but should be 
implemented in such a way as to allow for expansion of additional capabilities.

Modified Paths:
--------------
    brlcad/trunk/NEWS
    brlcad/trunk/doc/docbook/system/mann/analyze.xml
    brlcad/trunk/doc/docbook/system/mann/kill.xml
    brlcad/trunk/src/libged/analyze/CMakeLists.txt
    brlcad/trunk/src/libged/kill/kill.c
    brlcad/trunk/src/libged/pnts_util.c
    brlcad/trunk/src/libged/pnts_util.h
    brlcad/trunk/src/libged/tests/CMakeLists.txt

Added Paths:
-----------
    brlcad/trunk/src/libged/analyze/analyze.cpp
    brlcad/trunk/src/libged/analyze/arb8.cpp
    brlcad/trunk/src/libged/analyze/arbn.cpp
    brlcad/trunk/src/libged/analyze/ars.cpp
    brlcad/trunk/src/libged/analyze/ged_analyze.h
    brlcad/trunk/src/libged/analyze/op_pnts_vol.cpp
    brlcad/trunk/src/libged/analyze/sketch.cpp
    brlcad/trunk/src/libged/analyze/superell.cpp
    brlcad/trunk/src/libged/analyze/util.cpp
    brlcad/trunk/src/libged/tests/pnts_in_out.g

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

Property Changed:
----------------
    brlcad/trunk/
    brlcad/trunk/src/libged/kill/kill.c

Index: brlcad/trunk
===================================================================
--- brlcad/trunk        2020-09-25 12:41:33 UTC (rev 77221)
+++ brlcad/trunk        2020-09-25 13:41:24 UTC (rev 77222)

Property changes on: brlcad/trunk
___________________________________________________________________
Modified: svn:mergeinfo
## -1,4 +1,5 ##
 
/brlcad/branches/RELEASE:57439,57447-57860,69901-69913,70323-70333,71915-72242,72525-72534,72826-72858,74376-74454,74964-75140,75372-75685,76001-76451,76693-76768,77107-77132,77145-77155
+/brlcad/branches/analyze_cmd:76836-77221
 
/brlcad/branches/bioh:75720-75736,75740-75742,75860-75891,75894-75986,76088-76153,76354-76506,76577
 
/brlcad/branches/brep-debug:61373,61375,61404,61427,61429,61470,61544,61567,61576,61999,62018,62094,62098,62107,62117,62406,62416-62519,62521-62584,62593-62614,62623,62658,62660-62674,62681-62771,62876,62901,62907,62910,62925,62928,62931-63025,63027,63051,63054-63056,63069,63071-63073,63122,63160-63161,63165,63171,63184,63187,63189-63190,63193-63196,63200,63202,63205-63210,63213,63219-63225,63232-63233,63236,63238,63338,63350-63353,63481,63618,63669,64173-64174,64176-64177,64229-64233,64242,64244,64360-64362,65165,65245,65249,65334,65833-65834,66370-66375,66931-66932,66934,67012-67015,67018-67019,67021-67022,67406,67740,67746-67748,67950,67952,68144-68145,68636,68640-68643,68820,69081,69109,69168,69206,69289,69346,69460-69461,69582-69583,69719-69721,69857-69859,69927,69995-69996,70148-70149,70347-70349,70377,70526-70527,71006-71007,71009-71022,71046-71047,71049,71096-71100
 /brlcad/branches/bullet:62518
Modified: brlcad/trunk/NEWS
===================================================================
--- brlcad/trunk/NEWS   2020-09-25 12:41:33 UTC (rev 77221)
+++ brlcad/trunk/NEWS   2020-09-25 13:41:24 UTC (rev 77222)
@@ -13,6 +13,10 @@
 --- 2020-xx-xx  Release 7.32.2                                     ---
 ----------------------------------------------------------------------
 
+* added subtract subcommand to MGED analyze command - Cliff Yapp
+* added intersect subcommand to MGED analyze command - Cliff Yapp
+* added summarize subcommand to MGED analyze command - Cliff Yapp
+* added quiet (-q) flag to MGED kill command - Cliff Yapp
 * added MGED stat command for reporting of object info - Cliff Yapp
 * corrected examples in gdiff man page - Cliff Yapp
 * fixed help message for -F option on gdiff command - Cliff Yapp

Modified: brlcad/trunk/doc/docbook/system/mann/analyze.xml
===================================================================
--- brlcad/trunk/doc/docbook/system/mann/analyze.xml    2020-09-25 12:41:33 UTC 
(rev 77221)
+++ brlcad/trunk/doc/docbook/system/mann/analyze.xml    2020-09-25 13:41:24 UTC 
(rev 77222)
@@ -10,9 +10,7 @@
   <refnamediv xml:id="name">
     <refname>analyze</refname>
     <refpurpose>
-      Displays the rotation and fallback angles, surface area, and
-      plane equation for each face of the ARB specified on the command line. 
The total
-      surface area, volume, and the length of each edge are also displayed.
+      Report and/or compute analytical information about the listed objects.
     </refpurpose>
   </refnamediv>
 
@@ -20,39 +18,243 @@
   <refsynopsisdiv xml:id="synopsis">
     <cmdsynopsis sepchar=" ">
       <command>analyze</command>
-      <arg choice="req" 
rep="norepeat"><replaceable>arb_name</replaceable></arg>
+      <arg choice="opt" rep="repeat"><replaceable>options</replaceable></arg>
+      <arg choice="opt" 
rep="norepeat"><replaceable>subcommand</replaceable></arg>
     </cmdsynopsis>
+    <variablelist>
+      
+      <varlistentry><term></term><listitem>
+      <cmdsynopsis sepchar=" ">
+       <command>summarize</command>
+       <arg choice="req" rep="repeat">obj</arg>
+      </cmdsynopsis>
+      </listitem></varlistentry>
+      
+      <varlistentry><term></term><listitem>
+      <cmdsynopsis sepchar=" ">
+       <command>intersect</command>
+       <arg choice="opt" rep="repeat"><replaceable>options</replaceable></arg>
+       <arg choice="req" rep="repeat">obj1 obj2 [...]</arg>
+      </cmdsynopsis>
+      </listitem></varlistentry>
+      
+      <varlistentry><term></term><listitem>
+      <cmdsynopsis sepchar=" ">
+       <command>subtract</command>
+       <arg choice="opt" rep="repeat"><replaceable>options</replaceable></arg>
+       <arg choice="req" rep="repeat">obj1 obj2 [...]</arg>
+      </cmdsynopsis>
+      </listitem></varlistentry>
+    </variablelist>
   </refsynopsisdiv>
 
   <refsection xml:id="description"><title>DESCRIPTION</title>
 
     <para>
-      The <command>analyze</command> command displays the rotation and fallback
-      angles, surface area, and plane equation for each face of the ARB 
specified
-      on the command line. The total surface area, volume, and the length of 
each
-      edge are also displayed. If executed while editing an ARB, the arb_name 
may
-      be omitted, and the ARB being edited will be analyzed.
+      The <command>analyze</command> command provides an array of reporting and
+      shape generation capabilities.  The classic behavior (and the behavior 
users
+      will see if calling without specifying a subcommand) is a report 
summarizing
+      geometric information about the specified solid(s).  This behavior may 
also
+      be explicitly invoked by the <command>summarize</command> subcommand.
     </para>
+
+    <para>
+      The <command>intersect</command> will take the first object specified and
+      intersect it with any subsequently specified objects - in effect, 
building
+      up a new object with the geoemtry common to all objects specified.
+    </para>
+
+    <para>
+      The <command>subtract</command> will take the first object specified and
+      subtract from it any subsequently specified objects.
+    </para>
+    
   </refsection>
 
   <refsection xml:id="examples"><title>EXAMPLES</title>
 
-    <para>
-      The example shows how to display information about an ARB's rotation and
-      fallback angles, surface area, and plane equation for its face.
-      Edge information is also displayed.
-    </para>
     <example><title>Analyze a particular <emphasis>ARB</emphasis></title>
+      <para>
+       <prompt>mged&gt;</prompt> <userinput>analyze box.s</userinput>
+      </para>
+      <literallayout>
+box.s:  ARB8
+       1 (30.0283, -5.21153, -16.3791)
+       2 (30.0283, 21.5812, -16.3791)
+       3 (30.0283, 21.5812, 10.4137)
+       4 (30.0283, -5.21153, 10.4137)
+       5 (3.23558, -5.21153, -16.3791)
+       6 (3.23558, 21.5812, -16.3791)
+       7 (3.23558, 21.5812, 10.4137)
+       8 (3.23558, -5.21153, 10.4137)
 
++------+---------------------------+-------------------------------------------------+--------------+
+| FACE |     ROT          FB       |                 PLANE EQUATION            
      | SURFACE AREA |
++------+---------------------------+-------------------------------------------------+--------------+
+| 1234 |   0.00000000   0.00000000 |  1.00000000  0.00000000  0.00000000 
30.02833557 | 717.85172729 |
+| 5678 | 180.00000000  -0.00000000 | -1.00000000 -0.00000000 -0.00000000 
-3.23558044 | 717.85172729 |
+| 1584 | 270.00000000  -0.00000000 | -0.00000000 -1.00000000 -0.00000000  
5.21152973 | 717.85172729 |
+| 2376 |  90.00000000  -0.00000000 | -0.00000000  1.00000000 -0.00000000 
21.58122540 | 717.85172729 |
+| 1265 |   0.00000000 -90.00000000 | -0.00000000  0.00000000 -1.00000000 
16.37908936 | 717.85172729 |
+| 4378 |   0.00000000  90.00000000 |  0.00000000 -0.00000000  1.00000000 
10.41366577 | 717.85172729 |
++------+---------------------------+-------------------------------------------------+--------------+
+
+  
+--------------------+--------------------+--------------------+--------------------+
+  | EDGE        LENGTH | EDGE        LENGTH | EDGE        LENGTH | EDGE        
LENGTH |
+  
+--------------------+--------------------+--------------------+--------------------+
+  | 12     26.79275513 | 23     26.79275513 | 34     26.79275513 | 14     
26.79275513 |
+  | 15     26.79275513 | 56     26.79275513 | 26     26.79275513 | 67     
26.79275513 |
+  | 78     26.79275513 | 58     26.79275513 | 48     26.79275513 | 37     
26.79275513 |
+  
+--------------------+--------------------+--------------------+--------------------+
+
+    +-------------------------------+
+    | Volume       = 19233.22554681 |
+    | Surface Area =  4307.11036376 |
+    | Gallons      =     0.00508088 |
+    +-------------------------------+
+      </literallayout>
+    </example>
+
+     <example><title>Analyze a particular <emphasis>TOR</emphasis></title>
       <para>
-       <prompt>mged&gt;</prompt> <userinput>analyze arb_name</userinput>
+       <prompt>mged&gt;</prompt> <userinput>analyze tor</userinput>
       </para>
+      <literallayout>
+tor:  torus (TOR)
+       V (4.91624, -32.8022, 31.7118), r1=25.4 (A), r2=5.08 (H)
+       N=(0, 1, 0)
+       A=(0, -0, 1)
+       B=(1, 0, -0)
+       vector to inner edge = (0, -0, 20.32)
+       vector to outer edge = (0, -0, 30.48)
+
+    Centroid: (4.91624, -32.8022, 31.7118)
+
+    +-------------------------------+
+    | Volume       = 12938.70529707 |
+    | Surface Area =  5093.97853992 |
+    | Gallons      =     0.00341804 |
+    +-------------------------------+
+      </literallayout>
+    </example>
+    
+    <example><title>Points inside a sphere</title>
+    
       <para>
-       Rotation and fallback angles, surface area, and plane equation for
-       each face of the ARB specified on the command line are displayed along
-       with edge information.
+       <prompt>mged&gt;</prompt> <userinput>l sph.s</userinput>
       </para>
+      <literallayout>
+sph.s:  ellipsoid (ELL)
+       V (0, 0, 0)
+       A (10, 0, 0) mag=10
+       B (0, 10, 0) mag=10
+       C (0, 0, 10) mag=10
+       A direction cosines=(0, 90, 90)
+       A rotation angle=0, fallback angle=0
+       B direction cosines=(90, 0, 90)
+       B rotation angle=90, fallback angle=0
+       C direction cosines=(90, 90, 0)
+       C rotation angle=0, fallback angle=90
+      </literallayout>
+      
+      <para>
+       <prompt>mged&gt;</prompt> <userinput>l pnts.s</userinput>
+      </para>
+      <literallayout>
+pnts.s:  Point Cloud (PNTS)
+Total number of points: 6
+Default scale: 0.000000
+point#, (point)
+1,      (2.000000 2.000000 2.000000)
+2,      (0.000000 0.000000 -5.000000)
+3,      (0.000000 0.000000 10.000000)
+4,      (0.000000 0.000000 20.000000)
+5,      (10.000000 0.000000 10.000000)
+6,      (0.000000 10.000000 20.000000)
+      </literallayout>
+      
+      <para>
+       <prompt>mged&gt;</prompt> <userinput>analyze intersect -o inside.s 
pnts.s sph.s</userinput>
+      </para>
+      <literallayout>
+3
+      </literallayout>
+      
+      <para>
+       <prompt>mged&gt;</prompt> <userinput>l inside.s</userinput>
+      </para>
+      <literallayout>
+inside.s:  Point Cloud (PNTS)
+Total number of points: 3
+Default scale: 0.000000
+point#, (point)
+1,      (0.000000 0.000000 10.000000)
+2,      (0.000000 0.000000 -5.000000)
+3,      (2.000000 2.000000 2.000000)
+      </literallayout>
+
     </example>
+
+
+    <example><title>Points outside a sphere</title>
+    
+      <para>
+       <prompt>mged&gt;</prompt> <userinput>l sph.s</userinput>
+      </para>
+      <literallayout>
+sph.s:  ellipsoid (ELL)
+       V (0, 0, 0)
+       A (10, 0, 0) mag=10
+       B (0, 10, 0) mag=10
+       C (0, 0, 10) mag=10
+       A direction cosines=(0, 90, 90)
+       A rotation angle=0, fallback angle=0
+       B direction cosines=(90, 0, 90)
+       B rotation angle=90, fallback angle=0
+       C direction cosines=(90, 90, 0)
+       C rotation angle=0, fallback angle=90
+      </literallayout>
+      
+      <para>
+       <prompt>mged&gt;</prompt> <userinput>l pnts.s</userinput>
+      </para>
+      <literallayout>
+pnts.s:  Point Cloud (PNTS)
+Total number of points: 6
+Default scale: 0.000000
+point#, (point)
+1,      (2.000000 2.000000 2.000000)
+2,      (0.000000 0.000000 -5.000000)
+3,      (0.000000 0.000000 10.000000)
+4,      (0.000000 0.000000 20.000000)
+5,      (10.000000 0.000000 10.000000)
+6,      (0.000000 10.000000 20.000000)
+      </literallayout>
+      
+      <para>
+       <prompt>mged&gt;</prompt> <userinput>analyze subtract -o outside.s 
pnts.s sph</userinput>
+      </para>
+      <literallayout>
+3
+      </literallayout>
+
+      <para>
+       <prompt>mged&gt;</prompt> <userinput>l outside.s</userinput>
+      </para>
+      <literallayout>
+outside.s:  Point Cloud (PNTS)
+Total number of points: 3
+Default scale: 0.000000
+point#, (point)
+1,      (0.000000 10.000000 20.000000)
+2,      (10.000000 0.000000 10.000000)
+3,      (0.000000 0.000000 20.000000)
+      </literallayout>
+
+      
+    </example>
+
+    
   </refsection>
 
   <refsection xml:id="author"><title>AUTHOR</title>

Modified: brlcad/trunk/doc/docbook/system/mann/kill.xml
===================================================================
--- brlcad/trunk/doc/docbook/system/mann/kill.xml       2020-09-25 12:41:33 UTC 
(rev 77221)
+++ brlcad/trunk/doc/docbook/system/mann/kill.xml       2020-09-25 13:41:24 UTC 
(rev 77222)
@@ -1,61 +1,79 @@
 <refentry xmlns="http://docbook.org/ns/docbook"; version="5.0" xml:id="kill">
 
-<refmeta>
-  <refentrytitle>KILL</refentrytitle>
-  <manvolnum>nged</manvolnum>
-  <refmiscinfo class="source">BRL-CAD</refmiscinfo>
-  <refmiscinfo class="manual">BRL-CAD User Commands</refmiscinfo>
-</refmeta>
+  <refmeta>
+    <refentrytitle>KILL</refentrytitle>
+    <manvolnum>nged</manvolnum>
+    <refmiscinfo class="source">BRL-CAD</refmiscinfo>
+    <refmiscinfo class="manual">BRL-CAD User Commands</refmiscinfo>
+  </refmeta>
 
-<refnamediv xml:id="name">
-  <refname>kill</refname>
-  <refpurpose>Deletes specified <emphasis>objects</emphasis> from the current 
database.
-   </refpurpose>
-</refnamediv>
+  <refnamediv xml:id="name">
+    <refname>kill</refname>
+    <refpurpose>Deletes specified <emphasis>objects</emphasis> from the 
current database.
+    </refpurpose>
+  </refnamediv>
 
-<!-- body begins here -->
-<refsynopsisdiv xml:id="synopsis">
-  <cmdsynopsis sepchar=" ">
-    <command>kill</command>
-     <arg choice="opt" rep="norepeat">-f</arg>
-     <arg choice="req" rep="norepeat"><replaceable>objects</replaceable></arg>
-  </cmdsynopsis>
-</refsynopsisdiv>
+  <!-- body begins here -->
+  <refsynopsisdiv xml:id="synopsis">
+    <cmdsynopsis sepchar=" ">
+      <command>kill</command>
+      <arg choice="opt" rep="norepeat">-f</arg>
+      <arg choice="req" rep="norepeat"><replaceable>objects</replaceable></arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
 
-<refsection xml:id="description"><title>DESCRIPTION</title>
+  <refsection xml:id="description"><title>DESCRIPTION</title>
 
   <para>
-    Deletes the specified <emphasis>objects</emphasis> from the current 
database.
-       This command affects only the <emphasis>objects</emphasis> actually 
listed on the command       line. If a combination is killed, its members are 
not affected. If the <emphasis>-f     </emphasis>option is specified, then kill 
will not complain if some, or all, of the     <emphasis>objects</emphasis> 
specified do not actually exist in the database. Note that         the 
<emphasis>objects</emphasis> are killed immediately. There is no need for a 
<command>       write file</command> command in MGED, and there is no 
<command>undo</command> command.  <emphasis remap="B" role="bold">Use this 
command with caution.</emphasis> Other commands        that remove objects from 
the database are <command>killall</command> and <command>killtree      
</command>.
+    Deletes the specified <emphasis>objects</emphasis> from the current 
database.  This command
+    affects only the <emphasis>objects</emphasis> actually listed on the 
command line. If a
+    combination is killed, its members are not affected. If the 
<emphasis>-f</emphasis>option
+    is specified, then kill will not complain if some, or all, of the 
<emphasis>objects</emphasis>
+    specified do not actually exist in the database. Note that         the 
<emphasis>objects</emphasis>
+    are killed immediately. There is no need for a <command>write 
file</command> command in MGED,
+    and there is no <command>undo</command> command. <emphasis remap="B" 
role="bold">Use this
+    command with caution.</emphasis> Other commands that remove objects from 
the database are
+    <command>killall</command> and <command>killtree</command>.
   </para>
-</refsection>
+  <para>
+    The <option>-q</option> option may be added to suppress database object 
lookup messages
+    reporting failure, which will otherwise let the user know an invalid 
object was specified.
+  </para>
+  </refsection>
 
-<refsection xml:id="examples"><title>EXAMPLES</title>
-
-  <para>The example shows the use of the <command>kill</command> command to 
delete specified   objects from the database.
+  <refsection xml:id="examples"><title>EXAMPLES</title>
+  <para>
+    The example shows the use of the <command>kill</command> command to delete 
specified objects
+    from the database.
   </para>
   <example><title>Delete objects from the database.</title>
 
-    <variablelist>
-      <varlistentry>
-          <term><prompt>mged&gt;</prompt> <userinput>kill group1 region2 
shapeb</userinput></term>
-          <listitem>
-            <para>Deletes <emphasis>group1, region2</emphasis>, and 
<emphasis>shapeb</emphasis>                                from the database.
-             </para>
-          </listitem>
-      </varlistentry>
-    </variablelist>
+  <variablelist>
+    <varlistentry>
+      <term><prompt>mged&gt;</prompt> <userinput>kill group1 region2 
shapeb</userinput></term>
+      <listitem>
+       <para>
+         Deletes <emphasis>group1, region2</emphasis>, and 
<emphasis>shapeb</emphasis> from
+         the database.
+       </para>
+      </listitem>
+    </varlistentry>
+  </variablelist>
   </example>
 
-</refsection>
+  </refsection>
 
-<refsection xml:id="author"><title>AUTHOR</title><para>BRL-CAD 
Team</para></refsection>
+  <refsection xml:id="author"><title>AUTHOR</title>
+  <para>
+    BRL-CAD Team
+  </para>
+  </refsection>
 
-<refsection xml:id="bug_reports"><title>BUG REPORTS</title>
+  <refsection xml:id="bug_reports"><title>BUG REPORTS</title>
 
   <para>
     Reports of bugs or problems should be submitted via electronic
     mail to <email>[email protected]</email>
   </para>
-</refsection>
+  </refsection>
 </refentry>

Modified: brlcad/trunk/src/libged/analyze/CMakeLists.txt
===================================================================
--- brlcad/trunk/src/libged/analyze/CMakeLists.txt      2020-09-25 12:41:33 UTC 
(rev 77221)
+++ brlcad/trunk/src/libged/analyze/CMakeLists.txt      2020-09-25 13:41:24 UTC 
(rev 77222)
@@ -6,9 +6,20 @@
   ${GED_INCLUDE_DIRS}
   )
 
+set(analyze_srcs
+  analyze.cpp
+  arb8.cpp
+  arbn.cpp
+  ars.cpp
+  superell.cpp
+  sketch.cpp
+  util.cpp
+  op_pnts_vol.cpp
+  )
+
 add_definitions(-DGED_PLUGIN)
-ged_plugin_library(ged-analyze SHARED analyze.c)
-target_link_libraries(ged-analyze libged libbu)
+ged_plugin_library(ged-analyze SHARED ${analyze_srcs})
+target_link_libraries(ged-analyze libged libanalyze libbu)
 set_property(TARGET ged-analyze APPEND PROPERTY COMPILE_DEFINITIONS 
BRLCADBUILD HAVE_CONFIG_H)
 VALIDATE_STYLE(ged-analyze analyze.c)
 PLUGIN_SETUP(ged-analyze ged)
@@ -15,7 +26,8 @@
 
 CMAKEFILES(
   CMakeLists.txt
-  analyze.c
+  ${analyze_srcs}
+  ged_analyze.h
   )
 
 # Local Variables:

Deleted: brlcad/trunk/src/libged/analyze/analyze.c
===================================================================
--- brlcad/trunk/src/libged/analyze/analyze.c   2020-09-25 12:41:33 UTC (rev 
77221)
+++ brlcad/trunk/src/libged/analyze/analyze.c   2020-09-25 13:41:24 UTC (rev 
77222)
@@ -1,1341 +0,0 @@
-/*                          A N A L Y Z E . C
- * BRL-CAD
- *
- * Copyright (c) 1985-2020 United States Government as represented by
- * the U.S. Army Research Laboratory.
- *
- * This program 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 program 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/analyze.c
- *
- * The analyze command.
- *
- */
-
-#include "common.h"
-
-#include <math.h>
-#include <string.h>
-#include <assert.h>
-
-#include "vmath.h"
-#include "bn.h"
-#include "bg/polygon.h"
-#include "rt/arb_edit.h"
-#include "raytrace.h"
-#include "rt/geom.h"
-
-#include "../ged_private.h"
-
-/**
- * TODO: primitives that still need implementing
- * ehy
- * metaball
- * nmg
- */
-
-/* Conversion factor for Gallons to cubic millimeters */
-#define GALLONS_TO_MM3 3785411.784
-
-
-/* ARB face printout array */
-static const int prface[5][6] = {
-    {123, 124, 234, 134, -111, -111},          /* ARB4 */
-    {1234, 125, 235, 345, 145, -111},          /* ARB5 */
-    {1234, 2365, 1564, 512, 634, -111},                /* ARB6 */
-    {1234, 567, 145, 2376, 1265, 4375},                /* ARB7 */
-    {1234, 5678, 1584, 2376, 1265, 4378},      /* ARB8 */
-};
-
-
-/* edge definition array */
-static const int nedge[5][24] = {
-    {0, 1, 1, 2, 2, 0, 0, 3, 3, 2, 1, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
-1, -1, -1},   /* ARB4 */
-    {0, 1, 1, 2, 2, 3, 0, 3, 0, 4, 1, 4, 2, 4, 3, 4, -1, -1, -1, -1, -1, -1, 
-1, -1},       /* ARB5 */
-    {0, 1, 1, 2, 2, 3, 0, 3, 0, 4, 1, 4, 2, 5, 3, 5, 4, 5, -1, -1, -1, -1, -1, 
-1},         /* ARB6 */
-    {0, 1, 1, 2, 2, 3, 0, 3, 0, 4, 3, 4, 1, 5, 2, 6, 4, 5, 5, 6, 4, 6, -1, 
-1},             /* ARB7 */
-    {0, 1, 1, 2, 2, 3, 0, 3, 0, 4, 4, 5, 1, 5, 5, 6, 6, 7, 4, 7, 3, 7, 2, 6},  
             /* ARB8 */
-};
-
-
-/* contains information used to analyze a polygonal face */
-struct poly_face
-{
-    char label[5];
-    size_t npts;
-    point_t *pts;
-    plane_t plane_eqn;
-    fastf_t area;
-};
-
-
-#define POLY_FACE_INIT_ZERO { { 0, 0, 0, 0, 0 }, 0, NULL, HINIT_ZERO, 0.0 }
-
-#define ADD_PT(face, pt) do { VMOVE((face).pts[(face).npts], (pt)); 
(face).npts++; } while (0)
-
-/* structures and subroutines for analyze pretty printing */
-
-#define FBUFSIZ 100
-#define NFIELDS 9
-#define NOT_A_PLANE -1
-typedef struct row_field
-{
-    int nchars;
-    char buf[FBUFSIZ];
-} field_t;
-
-typedef struct table_row
-{
-    int nfields;
-    field_t fields[NFIELDS];
-} row_t;
-
-typedef struct table
-{
-    int nrows;
-    row_t *rows;
-} table_t;
-
-void get_dashes(field_t *f, const int ndashes)
-{
-    int i;
-    f->buf[0] = '\0';
-    for (i = 0; i < ndashes; ++i) {
-       bu_strlcat(f->buf, "-", FBUFSIZ);
-    }
-    f->nchars = ndashes;
-}
-
-
-void print_volume_table(struct ged *gedp
-                       , const fastf_t tot_vol
-                       , const fastf_t tot_area
-                       , const fastf_t tot_gallons
-    )
-{
-
-/* table format
-
-   +------------------------------------+
-   | Volume       = 7999999999.99999905 |
-   | Surface Area =   24000000.00000000 |
-   | Gallons      =       2113.37641887 |
-   +------------------------------------+
-
-*/
-    /* track actual table column widths */
-    /* this table has 1 column (plus a name column) */
-    int maxwidth[2] = {0, 0};
-    field_t dashes;
-    char* fnames[3] = {"Volume",
-                      "Surface Area",
-                      "Gallons"};
-    int indent = 4; /* number spaces to indent the table */
-    int table_width_chars;
-    table_t table;
-    int i, nd, field;
-
-    table.nrows = 3;
-    table.rows = (row_t *)bu_calloc(3, sizeof(row_t), "print_volume_table: 
rows");
-    for (i = 0; i < table.nrows; ++i) {
-       fastf_t val = 0.0;
-
-       /* field 0 */
-       field = 0;
-       table.rows[i].fields[0].nchars = 
snprintf(table.rows[i].fields[field].buf, FBUFSIZ, "%s",
-                                                 fnames[i]);
-       if (maxwidth[field] < table.rows[i].fields[field].nchars)
-           maxwidth[field] = table.rows[i].fields[field].nchars;
-
-       if (i == 0) {
-           val = tot_vol;
-       } else if (i == 1) {
-           val = tot_area;
-       } else if (i == 2) {
-           val = tot_gallons;
-       }
-
-       /* field 1 */
-       field = 1;
-       if (val < 0) {
-           table.rows[i].fields[1].nchars = 
snprintf(table.rows[i].fields[field].buf, FBUFSIZ, "COULD NOT DETERMINE");
-       } else {
-           table.rows[i].fields[1].nchars = 
snprintf(table.rows[i].fields[field].buf, FBUFSIZ, "%10.8f", val);
-       }
-       if (maxwidth[field] < table.rows[i].fields[field].nchars)
-           maxwidth[field] = table.rows[i].fields[field].nchars;
-    }
-
-    /* get total table width */
-    table_width_chars  = maxwidth[0] + maxwidth[1];
-    table_width_chars += 2 + 2; /* 2 chars at each end of a row */
-    table_width_chars += 3; /* ' = ' between the two fields of a row */
-
-    /* newline following previous table */
-    bu_vls_printf(gedp->ged_result_str, "\n");
-
-    /* header row 1 */
-    nd = table_width_chars - 4;
-    get_dashes(&dashes, nd);
-    bu_vls_printf(gedp->ged_result_str, "%-*.*s+-%-*.*s-+\n",
-                 indent, indent, " ",
-                 nd, nd, dashes.buf);
-
-    /* the three data rows */
-    for (i = 0; i < table.nrows; ++i) {
-       bu_vls_printf(gedp->ged_result_str, "%-*.*s| %-*.*s = %*.*s |\n",
-                     indent, indent, " ",
-                     maxwidth[0], maxwidth[0], table.rows[i].fields[0].buf,
-                     maxwidth[1], maxwidth[1], table.rows[i].fields[1].buf);
-    }
-
-    /* closing table row */
-    bu_vls_printf(gedp->ged_result_str, "%-*.*s+-%-*.*s-+\n",
-                 indent, indent, " ",
-                 nd, nd, dashes.buf);
-    bu_free((char *)table.rows, "print_volume_table: rows");
-}
-
-
-void print_edges_table(struct ged *gedp, table_t *table)
-{
-
-/* table header
-
-   
+--------------------+--------------------+--------------------+--------------------+
-   | EDGE          LEN  | EDGE          LEN  | EDGE          LEN  | EDGE       
   LEN  |
-   
+--------------------+--------------------+--------------------+--------------------+
-
-*/
-
-    int i;
-    int tcol, nd, nrow, nrows;
-    int maxwidth[] = {0, 0, 0,
-                     0, 0, 0,
-                     0, 0};
-    int indent = 2;
-    field_t dashes;
-    char EDGE[] = {"EDGE"};
-    int elen    = strlen(EDGE);
-    char LEN[]  = {"LENGTH"};
-    int llen    = strlen(LEN);
-    char buf[FBUFSIZ];
-
-    /* put four edges per row making 8 columns */
-    /* this table has 8 columns per row: 2 columns per edge; 4 edges per row */
-
-    /* collect max table column widths */
-    tcol = 0;
-    for (i = 0; i < table->nrows; ++i) {
-       /* field 0 */
-       int field = 0;
-       if (maxwidth[tcol] < table->rows[i].fields[field].nchars)
-           maxwidth[tcol] = table->rows[i].fields[field].nchars;
-       if (maxwidth[tcol] < elen)
-           maxwidth[tcol] = elen;
-
-       /* field 1 */
-       field = 1;
-       if (maxwidth[tcol+1] < table->rows[i].fields[field].nchars)
-           maxwidth[tcol+1] = table->rows[i].fields[field].nchars;
-       if (maxwidth[tcol] < llen)
-           maxwidth[tcol] = llen;
-
-       /* iterate on columns */
-       tcol += 2;
-       tcol = tcol > 6 ? 0 : tcol;
-    }
-
-    /* header row 1 */
-    /* print dashes in 4 sets */
-    nd = maxwidth[0] + maxwidth[1] + 3; /* 1 space between numbers and one at 
each end */
-    get_dashes(&dashes, nd);
-    bu_vls_printf(gedp->ged_result_str, "%-*.*s+%-*.*s",
-                 indent, indent, " ",
-                 nd, nd, dashes.buf);
-    nd = maxwidth[2] + maxwidth[3] + 3; /* 1 space between numbers and one at 
each end */
-    get_dashes(&dashes, nd);
-    bu_vls_printf(gedp->ged_result_str, "+%-*.*s",
-                 nd, nd, dashes.buf);
-    nd = maxwidth[4] + maxwidth[5] + 3; /* 1 space between numbers and one at 
each end */
-    get_dashes(&dashes, nd);
-    bu_vls_printf(gedp->ged_result_str, "+%-*.*s",
-                 nd, nd, dashes.buf);
-    nd = maxwidth[6] + maxwidth[7] + 3; /* 1 space between numbers and one at 
each end */
-    get_dashes(&dashes, nd);
-    bu_vls_printf(gedp->ged_result_str, "+%-*.*s+\n",
-                 nd, nd, dashes.buf);
-
-    /* header row 2 */
-    /* print titles in 4 sets */
-
-    /* bu_vls_printf can't handle this at the moment */
-    bu_vls_printf(gedp->ged_result_str, "%-*.*s| %-*.*s %*.*s ",
-                 indent, indent, " ",
-                 maxwidth[0], maxwidth[0], EDGE,
-                 maxwidth[1], maxwidth[1], LEN);
-    bu_vls_printf(gedp->ged_result_str, "| %-*.*s %*.*s ",
-                 maxwidth[2], maxwidth[2], EDGE,
-                 maxwidth[3], maxwidth[3], LEN);
-    bu_vls_printf(gedp->ged_result_str, "| %-*.*s %*.*s ",
-                 maxwidth[4], maxwidth[4], EDGE,
-                 maxwidth[5], maxwidth[5], LEN);
-    bu_vls_printf(gedp->ged_result_str, "| %-*.*s %*.*s |\n",
-                 maxwidth[6], maxwidth[6], EDGE,
-                 maxwidth[7], maxwidth[7], LEN);
-
-    /* header row 3 */
-    /* print dashes in 4 sets */
-    nd = maxwidth[0] + maxwidth[1] + 3; /* 1 space between numbers and one at 
each end */
-    get_dashes(&dashes, nd);
-    bu_vls_printf(gedp->ged_result_str, "%-*.*s+%-*.*s",
-                 indent, indent, " ",
-                 nd, nd, dashes.buf);
-    nd = maxwidth[2] + maxwidth[3] + 3; /* 1 space between numbers and one at 
each end */
-    get_dashes(&dashes, nd);
-    bu_vls_printf(gedp->ged_result_str, "+%-*.*s",
-                 nd, nd, dashes.buf);
-    nd = maxwidth[4] + maxwidth[5] + 3; /* 1 space between numbers and one at 
each end */
-    get_dashes(&dashes, nd);
-    bu_vls_printf(gedp->ged_result_str, "+%-*.*s",
-                 nd, nd, dashes.buf);
-    nd = maxwidth[6] + maxwidth[7] + 3; /* 1 space between numbers and one at 
each end */
-    get_dashes(&dashes, nd);
-    bu_vls_printf(gedp->ged_result_str, "+%-*.*s+\n",
-                 nd, nd, dashes.buf);
-
-    /* print the data lines */
-    /* collect max table column widths */
-    tcol = 0;
-    nrow = 0;
-    for (i = 0; i < table->nrows; ++i) {
-       int field;
-
-       if (tcol == 0) {
-           /* need to start a row */
-           snprintf(buf, FBUFSIZ, "%-*.*s|",
-                    indent, indent, " ");
-           bu_vls_printf(gedp->ged_result_str, "%s", buf);
-       }
-
-       /* data in sets of two */
-       /* field 0 */
-       field = 0;
-       /* FIXME: using snprintf because bu_vls_printf is broken for complex 
formats */
-       snprintf(buf, FBUFSIZ, " %-*.*s",
-                maxwidth[tcol], maxwidth[tcol], 
table->rows[i].fields[field].buf);
-       bu_vls_printf(gedp->ged_result_str, "%s", buf);
-
-       /* field 1 */
-       field = 1;
-       /* FIXME: using snprintf because bu_vls_printf is broken for complex 
formats */
-       snprintf(buf, FBUFSIZ, " %-*.*s |",
-                maxwidth[tcol+1], maxwidth[tcol+1], 
table->rows[i].fields[field].buf);
-       bu_vls_printf(gedp->ged_result_str, "%s", buf);
-
-       /* iterate on columns */
-       tcol += 2;
-
-       if (tcol > 6) {
-           /* time for a newline to end the row */
-           bu_vls_printf(gedp->ged_result_str, "\n");
-           tcol = 0;
-           ++nrow;
-       }
-    }
-
-    /* we may have a row to finish */
-    nrows = table->nrows % 4;
-    if (nrows) {
-       assert(tcol < 8);
-
-       /* write blanks */
-       while (tcol < 7) {
-
-           /* data in sets of two */
-           /* this is field 0 */
-           /* FIXME: using snprintf because bu_vls_printf is broken for 
complex formats */
-           snprintf(buf, FBUFSIZ, " %-*.*s",
-                    maxwidth[tcol], maxwidth[tcol], " ");
-           bu_vls_printf(gedp->ged_result_str, "%s", buf);
-
-           /* this is field 1 */
-           /* FIXME: using snprintf because bu_vls_printf is broken for 
complex formats */
-           snprintf(buf, FBUFSIZ, " %-*.*s |",
-                    maxwidth[tcol+1], maxwidth[tcol+1], " ");
-           bu_vls_printf(gedp->ged_result_str, "%s", buf);
-
-           /* iterate on columns */
-           tcol += 2;
-
-           if (tcol > 6) {
-               /* time for a newline to end the row */
-               bu_vls_printf(gedp->ged_result_str, "\n");
-           }
-       }
-    }
-
-    /* close the table */
-    /* print dashes in 4 sets */
-    nd = maxwidth[0] + maxwidth[1] + 3; /* 1 space between numbers and one at 
each end */
-    get_dashes(&dashes, nd);
-    bu_vls_printf(gedp->ged_result_str, "%-*.*s+%-*.*s",
-                 indent, indent, " ",
-                 nd, nd, dashes.buf);
-    nd = maxwidth[2] + maxwidth[3] + 3; /* 1 space between numbers and one at 
each end */
-    get_dashes(&dashes, nd);
-    bu_vls_printf(gedp->ged_result_str, "+%-*.*s",
-                 nd, nd, dashes.buf);
-    nd = maxwidth[4] + maxwidth[5] + 3; /* 1 space between numbers and one at 
each end */
-    get_dashes(&dashes, nd);
-    bu_vls_printf(gedp->ged_result_str, "+%-*.*s",
-                 nd, nd, dashes.buf);
-    nd = maxwidth[6] + maxwidth[7] + 3; /* 1 space between numbers and one at 
each end */
-    get_dashes(&dashes, nd);
-    bu_vls_printf(gedp->ged_result_str, "+%-*.*s+\n",
-                 nd, nd, dashes.buf);
-}
-
-
-void print_faces_table(struct ged *gedp, table_t *table)
-{
-
-/* table header
-
-   
+------+-----------------------------+--------------------------------------------------+-----------------+
-   | FACE |      ROT           FB       |                  PLANE EQUATION      
            |   SURFACE AREA  |
-   
+------+-----------------------------+--------------------------------------------------+-----------------+
-
-*/
-
-    /* track actual table column widths */
-    /* this table has 8 columns */
-    int maxwidth[8] = {0, 0, 0,
-                      0, 0, 0,
-                      0, 0};
-    int i, j;
-    int c0, h1a, h1b, h1c;
-    int h2a, h2b, h2c;
-    int c2, c2a, c2b, c2c;
-    int f7, f7a, f7b, f7c;
-    int nd, tnd;
-    field_t dashes;
-    char ROT[] = {"ROT"};
-    char FB[]  = {"FB"};
-    char PA[]  = {"PLANE EQUATION"};
-    char SA[]  = {"SURFACE AREA"};
-
-    /* get max fields widths */
-    for (i = 0; i < table->nrows; ++i) {
-       for (j = 0; j < table->rows[i].nfields; ++j) {
-           if (table->rows[i].fields[j].nchars > maxwidth[j])
-               maxwidth[j] = table->rows[i].fields[j].nchars;
-       }
-    }
-
-    /* blank line following previous table */
-    bu_vls_printf(gedp->ged_result_str, "\n");
-
-    /* get max width of header columns (not counting single space on either 
side) */
-    c0 = maxwidth[0] > 4 ? maxwidth[0] : 4;
-
-    /* print "ROT" in center of field 1 space */
-    h1b = strlen(ROT);
-    h1a = (maxwidth[1] - h1b)/2;
-    h1c = (maxwidth[1] - h1b - h1a);
-
-    /* print "FB" in center of field 2 space */
-    h2b = strlen(FB);
-    h2a = (maxwidth[2] - h2b)/2;
-    h2c = (maxwidth[2] - h2b - h2a);
-
-    /* get width of subcolumns of header column 2 */
-    /* print "PLANE EQUATION" in center of columns 2 space */
-    c2 = maxwidth[3] + maxwidth[4] + maxwidth[5] + maxwidth[6] + 3; /* 3 
spaces between fields */
-    c2b = strlen(PA);
-    c2a = (c2 - c2b)/2;
-    c2c = (c2 - c2b - c2a);
-
-    /* print "SURFACE AREA" in center of field 7 space */
-    f7b = strlen(SA);
-    f7  = maxwidth[7] > f7b ? maxwidth[7] : f7b;
-    f7a = (f7 - f7b)/2;
-    f7c = (f7 - f7b - f7a);
-
-    /* print the pieces */
-
-    /* header row 1 */
-    bu_vls_printf(gedp->ged_result_str, "+-");
-    nd = c0; tnd = nd;
-    get_dashes(&dashes, nd);
-    bu_vls_printf(gedp->ged_result_str, "%-*.*s",
-                 nd, nd, dashes.buf);
-    bu_vls_printf(gedp->ged_result_str, "-+-");
-    nd = h1a + h1b + h1c + 1 + h2a + h2b + h2c; tnd += nd + 3;
-    get_dashes(&dashes, nd);
-    bu_vls_printf(gedp->ged_result_str, "%*.*s",
-                 nd, nd, dashes.buf);
-    bu_vls_printf(gedp->ged_result_str, "-+-");
-    nd = c2a + c2b + c2c; tnd += nd + 3;
-    get_dashes(&dashes, nd);
-    bu_vls_printf(gedp->ged_result_str, "%*.*s",
-                 nd, nd, dashes.buf);
-    bu_vls_printf(gedp->ged_result_str, "-+-");
-    nd = f7a + f7b + f7c; tnd += nd + 3;
-    get_dashes(&dashes, nd);
-    bu_vls_printf(gedp->ged_result_str, "%*.*s",
-                 nd, nd, dashes.buf);
-    bu_vls_printf(gedp->ged_result_str, "-+\n");
-
-    /* header row 2 */
-    bu_vls_printf(gedp->ged_result_str, "| ");
-
-    bu_vls_printf(gedp->ged_result_str, "%-*.*s", c0, c0, "FACE");
-
-    bu_vls_printf(gedp->ged_result_str, " | ");
-
-
-    bu_vls_printf(gedp->ged_result_str, "%*.*s", h1a, h1a, " ");
-
-    bu_vls_printf(gedp->ged_result_str, "%*.*s", h1b, h1b, ROT);
-
-    bu_vls_printf(gedp->ged_result_str, "%*.*s", h1c+h2a, h1c+h2a, " ");
-
-    bu_vls_printf(gedp->ged_result_str, "%*.*s", h2b, h2b, FB);
-
-    bu_vls_printf(gedp->ged_result_str, "%*.*s ", h2c, h2c, " ");
-
-
-    bu_vls_printf(gedp->ged_result_str, " | ");
-
-
-    bu_vls_printf(gedp->ged_result_str, "%*.*s", c2a, c2a, " ");
-
-    bu_vls_printf(gedp->ged_result_str, "%*.*s", c2b, c2b, PA);
-
-    bu_vls_printf(gedp->ged_result_str, "%*.*s", c2c, c2c, " ");
-
-
-    bu_vls_printf(gedp->ged_result_str, " | ");
-
-    bu_vls_printf(gedp->ged_result_str, "%*.*s", f7a, f7a, " ");
-
-    bu_vls_printf(gedp->ged_result_str, "%*.*s", f7b, f7b, SA);
-
-    bu_vls_printf(gedp->ged_result_str, "%*.*s", f7c, f7c, " ");
-
-    bu_vls_printf(gedp->ged_result_str, " |\n");
-
-    /* header row 3 */
-    bu_vls_printf(gedp->ged_result_str, "+-");
-    nd = c0; tnd = nd;
-    get_dashes(&dashes, nd);
-    bu_vls_printf(gedp->ged_result_str, "%-*.*s",
-                 nd, nd, dashes.buf);
-    bu_vls_printf(gedp->ged_result_str, "-+-");
-    nd = h1a + h1b + h1c + 1 + h2a + h2b + h2c; tnd += nd + 3;
-    get_dashes(&dashes, nd);
-    bu_vls_printf(gedp->ged_result_str, "%*.*s",
-                 nd, nd, dashes.buf);
-    bu_vls_printf(gedp->ged_result_str, "-+-");
-    nd = c2a + c2b + c2c; tnd += nd + 3;
-    get_dashes(&dashes, nd);
-    bu_vls_printf(gedp->ged_result_str, "%*.*s",
-                 nd, nd, dashes.buf);
-    bu_vls_printf(gedp->ged_result_str, "-+-");
-    nd = f7a + f7b + f7c; tnd += nd + 3;
-    get_dashes(&dashes, nd);
-    bu_vls_printf(gedp->ged_result_str, "%*.*s",
-                 nd, nd, dashes.buf);
-    bu_vls_printf(gedp->ged_result_str, "-+\n");
-
-    /* output table data rows */
-    for (i = 0; i < table->nrows; ++i) {
-       /* may not have a row with data */
-       if (table->rows[i].nfields == 0)
-           continue;
-       if (table->rows[i].nfields == NOT_A_PLANE)
-           bu_vls_printf(gedp->ged_result_str, "***NOT A PLANE ***");
-
-       bu_vls_printf(gedp->ged_result_str, "|");
-       for (j = 0; j < table->rows[i].nfields; ++j) {
-           assert(table->rows[i].fields[j].buf);
-           bu_vls_printf(gedp->ged_result_str, " %*.*s",
-                         maxwidth[j], maxwidth[j],
-                         table->rows[i].fields[j].buf);
-           /* do we need a separator? */
-           if (j == 0 || j == 2 || j == 6 || j == 7)
-               bu_vls_printf(gedp->ged_result_str, " |");
-       }
-       /* close the row */
-       bu_vls_printf(gedp->ged_result_str, "\n");
-    }
-
-    /* close the table with the ender row */
-    bu_vls_printf(gedp->ged_result_str, "+-");
-    nd = c0; tnd = nd;
-    get_dashes(&dashes, nd);
-    bu_vls_printf(gedp->ged_result_str, "%-*.*s",
-                 nd, nd, dashes.buf);
-    bu_vls_printf(gedp->ged_result_str, "-+-");
-    nd = h1a + h1b + h1c + 1 + h2a + h2b + h2c; tnd += nd + 3;
-    get_dashes(&dashes, nd);
-    bu_vls_printf(gedp->ged_result_str, "%*.*s",
-                 nd, nd, dashes.buf);
-    bu_vls_printf(gedp->ged_result_str, "-+-");
-    nd = c2a + c2b + c2c; tnd += nd + 3;
-    get_dashes(&dashes, nd);
-    bu_vls_printf(gedp->ged_result_str, "%*.*s",
-                 nd, nd, dashes.buf);
-    bu_vls_printf(gedp->ged_result_str, "-+-");
-    nd = f7a + f7b + f7c; tnd += nd + 3;
-    get_dashes(&dashes, nd);
-    bu_vls_printf(gedp->ged_result_str, "%*.*s",
-                 nd, nd, dashes.buf);
-    bu_vls_printf(gedp->ged_result_str, "-+\n");
-}
-
-
-/**
- * general analyze function for primitives that can be analyzed using volume
- * and surface area functions from the rt_functab.
- * Currently used for:
- * - ell
- * - tor
- * - tgc
- * - rpc
- * - eto
- * - epa
- * - part
- * - rhc
- */
-HIDDEN void
-analyze_general(struct ged *gedp, const struct rt_db_internal *ip)
-{
-    fastf_t vol, area;
-    point_t centroid;
-
-    vol = area = -1.0;
-
-    if (OBJ[ip->idb_minor_type].ft_volume) {
-       OBJ[ip->idb_minor_type].ft_volume(&vol, ip);
-    }
-    if (OBJ[ip->idb_minor_type].ft_surf_area) {
-       OBJ[ip->idb_minor_type].ft_surf_area(&area, ip);
-    }
-
-    if (OBJ[ip->idb_minor_type].ft_centroid) {
-       OBJ[ip->idb_minor_type].ft_centroid(&centroid, ip);
-       bu_vls_printf(gedp->ged_result_str, "\n    Centroid: (%g, %g, %g)\n",
-                     centroid[X] * gedp->ged_wdbp->dbip->dbi_base2local,
-                     centroid[Y] * gedp->ged_wdbp->dbip->dbi_base2local,
-                     centroid[Z] * gedp->ged_wdbp->dbip->dbi_base2local);
-    }
-
-    print_volume_table(gedp,
-                      vol
-                      * gedp->ged_wdbp->dbip->dbi_base2local
-                      * gedp->ged_wdbp->dbip->dbi_base2local
-                      * gedp->ged_wdbp->dbip->dbi_base2local,
-                      area
-                      * gedp->ged_wdbp->dbip->dbi_base2local
-                      * gedp->ged_wdbp->dbip->dbi_base2local,
-                      vol/GALLONS_TO_MM3
-       );
-}
-
-
-/**
- * finds direction cosines and rotation, fallback angles of a unit vector
- * angles = pointer to 5 fastf_t's to store angles
- * unitv = pointer to the unit vector (previously computed)
- */
-HIDDEN void
-findang(fastf_t *angles, fastf_t *unitv)
-{
-    int i;
-    fastf_t f;
-
-    /* convert direction cosines into axis angles */
-    for (i = X; i <= Z; i++) {
-       if (unitv[i] <= -1.0)
-           angles[i] = -90.0;
-       else if (unitv[i] >= 1.0)
-           angles[i] = 90.0;
-       else
-           angles[i] = acos(unitv[i]) * RAD2DEG;
-    }
-
-    /* fallback angle */
-    if (unitv[Z] <= -1.0)
-       unitv[Z] = -1.0;
-    else if (unitv[Z] >= 1.0)
-       unitv[Z] = 1.0;
-    angles[4] = asin(unitv[Z]);
-
-    /* rotation angle */
-    /* For the tolerance below, on an SGI 4D/70, cos(asin(1.0)) != 0.0
-     * with an epsilon of +/- 1.0e-17, so the tolerance below was
-     * substituted for the original +/- 1.0e-20.
-     */
-    if ((f = cos(angles[4])) > 1.0e-16 || f < -1.0e-16) {
-       f = unitv[X]/f;
-       if (f <= -1.0)
-           angles[3] = 180.0;
-       else if (f >= 1.0)
-           angles[3] = 0.0;
-       else
-           angles[3] = RAD2DEG * acos(f);
-    } else
-       angles[3] = 0.0;
-
-    if (unitv[Y] < 0)
-       angles[3] = 360.0 - angles[3];
-
-    angles[4] *= RAD2DEG;
-}
-
-
-/**
- * general analyze function for polygonal faces.
- * Currently used for:
- * - arb8
- * - arbn
- * - ars
- *
- * returns:
- * - area in face->area
- * - print_faces_table() information in row
- * - sorts vertices in face->pts into ccw order
- */
-HIDDEN void
-analyze_poly_face(struct ged *gedp, struct poly_face *face, row_t *row)
-{
-    fastf_t angles[5];
-
-    findang(angles, face->plane_eqn);
-
-    /* sort points */
-    bg_3d_polygon_sort_ccw(face->npts, face->pts, face->plane_eqn);
-    bg_3d_polygon_area(&face->area, face->npts, (const point_t *)face->pts);
-
-    /* store face information for pretty printing */
-    row->nfields = 8;
-    row->fields[0].nchars = sprintf(row->fields[0].buf, "%4s", face->label);
-    row->fields[1].nchars = sprintf(row->fields[1].buf, "%10.8f", angles[3]);
-    row->fields[2].nchars = sprintf(row->fields[2].buf, "%10.8f", angles[4]);
-    row->fields[3].nchars = sprintf(row->fields[3].buf, "%10.8f", 
face->plane_eqn[X]);
-    row->fields[4].nchars = sprintf(row->fields[4].buf, "%10.8f", 
face->plane_eqn[Y]);
-    row->fields[5].nchars = sprintf(row->fields[5].buf, "%10.8f", 
face->plane_eqn[Z]);
-    row->fields[6].nchars = sprintf(row->fields[6].buf, "%10.8f",
-                                   
face->plane_eqn[W]*gedp->ged_wdbp->dbip->dbi_base2local);
-    row->fields[7].nchars = sprintf(row->fields[7].buf, "%10.8f",
-                                   
face->area*gedp->ged_wdbp->dbip->dbi_base2local*gedp->ged_wdbp->dbip->dbi_base2local);
-}
-
-
-HIDDEN void
-analyze_edge(struct ged *gedp, const int edge, const struct rt_arb_internal 
*arb,
-            const int type, row_t *row)
-{
-    int a = nedge[type][edge*2];
-    int b = nedge[type][edge*2+1];
-
-    if (b == -1) {
-       row->nfields = 0;
-       return;
-    }
-
-    row->nfields = 2;
-    row->fields[0].nchars = sprintf(row->fields[0].buf, "%d%d", a + 1, b + 1);
-    row->fields[1].nchars = sprintf(row->fields[1].buf, "%10.8f",
-                                   DIST_PNT_PNT(arb->pt[a], 
arb->pt[b])*gedp->ged_wdbp->dbip->dbi_base2local);
-}
-
-
-HIDDEN void
-analyze_arb8(struct ged *gedp, const struct rt_db_internal *ip)
-{
-    int i, type;
-    int cgtype;     /* COMGEOM arb type: # of vertices */
-    table_t table;  /* holds table data from child functions */
-    fastf_t tot_vol = 0.0, tot_area = 0.0;
-    point_t center_pt = VINIT_ZERO;
-    struct poly_face face = POLY_FACE_INIT_ZERO;
-    struct rt_arb_internal earb;
-    struct rt_arb_internal *arb = (struct rt_arb_internal *)ip->idb_ptr;
-    const int arb_faces[5][24] = rt_arb_faces;
-    RT_ARB_CK_MAGIC(arb);
-
-    /* find the specific arb type, in GIFT order. */
-    if ((cgtype = rt_arb_std_type(ip, &gedp->ged_wdbp->wdb_tol)) == 0) {
-       bu_vls_printf(gedp->ged_result_str, "analyze_arb: bad ARB\n");
-       return;
-    }
-
-    type = cgtype - 4;
-
-    /* to get formatting correct, we need to collect the actual string
-     * lengths for each field BEFORE we start printing a table (fields
-     * are allowed to overflow the stated printf field width) */
-
-    /* TABLE 1 =========================================== */
-    /* analyze each face, use center point of arb for reference */
-    rt_arb_centroid(&center_pt, ip);
-
-    /* allocate pts array, maximum 4 verts per arb8 face */
-    face.pts = (point_t *)bu_calloc(4, sizeof(point_t), "analyze_arb8: pts");
-    /* allocate table rows, 12 rows needed for arb8 edges */
-    table.rows = (row_t *)bu_calloc(12, sizeof(row_t), "analyze_arb8: rows");
-
-    table.nrows = 0;
-    for (face.npts = 0, i = 0; i < 6; face.npts = 0, i++) {
-       int a, b, c, d; /* 4 indices to face vertices */
-
-       a = arb_faces[type][i*4+0];
-       b = arb_faces[type][i*4+1];
-       c = arb_faces[type][i*4+2];
-       d = arb_faces[type][i*4+3];
-
-       if (a == -1) {
-           table.rows[i].nfields = 0;
-           continue;
-       }
-
-       /* find plane eqn for this face */
-       if (bn_make_plane_3pnts(face.plane_eqn, arb->pt[a], arb->pt[b], 
arb->pt[c], &gedp->ged_wdbp->wdb_tol) < 0) {
-           bu_vls_printf(gedp->ged_result_str, "| %d%d%d%d |         ***NOT A 
PLANE***                                          |\n",
-                         a+1, b+1, c+1, d+1);
-           /* this row has 1 special fields */
-           table.rows[i].nfields = NOT_A_PLANE;
-           continue;
-       }
-
-       ADD_PT(face, arb->pt[a]);
-       ADD_PT(face, arb->pt[b]);
-       ADD_PT(face, arb->pt[c]);
-       ADD_PT(face, arb->pt[d]);
-
-       /* The plane equations returned by bn_make_plane_3pnts above do
-        * not necessarily point outward. Use the reference center
-        * point for the arb and reverse direction for any errant planes.
-        * This corrects the output rotation, fallback angles so that
-        * they always give the outward pointing normal vector. */
-       if (DIST_PNT_PLANE(center_pt, face.plane_eqn) > 0.0) {
-           HREVERSE(face.plane_eqn, face.plane_eqn);
-       }
-
-       snprintf(face.label, sizeof(face.label), "%d", prface[type][i]);
-
-       analyze_poly_face(gedp, &face, &(table.rows[i]));
-       tot_area += face.area;
-       table.nrows++;
-    }
-
-    /* and print it */
-    print_faces_table(gedp, &table);
-
-    /* TABLE 2 =========================================== */
-    /* analyze each edge */
-
-    /* blank line following previous table */
-    bu_vls_printf(gedp->ged_result_str, "\n");
-
-    /* set up the records for arb4's and arb6's */
-    earb = *arb; /* struct copy */
-    if (cgtype == 4) {
-       VMOVE(earb.pt[3], earb.pt[4]);
-    } else if (cgtype == 6) {
-       VMOVE(earb.pt[5], earb.pt[6]);
-    }
-
-    table.nrows = 0;
-    for (i = 0; i < 12; i++) {
-       analyze_edge(gedp, i, &earb, type, &(table.rows[i]));
-       if (nedge[type][i*2] == -1) {
-           break;
-       }
-       table.nrows += 1;
-    }
-
-    print_edges_table(gedp, &table);
-
-    /* TABLE 3 =========================================== */
-    /* find the volume - break arb8 into 6 arb4s */
-
-    if (OBJ[ID_ARB8].ft_volume)
-       OBJ[ID_ARB8].ft_volume(&tot_vol, ip);
-
-    print_volume_table(gedp,
-                      tot_vol
-                      * gedp->ged_wdbp->dbip->dbi_base2local
-                      * gedp->ged_wdbp->dbip->dbi_base2local
-                      * gedp->ged_wdbp->dbip->dbi_base2local,
-                      tot_area
-                      * gedp->ged_wdbp->dbip->dbi_base2local
-                      * gedp->ged_wdbp->dbip->dbi_base2local,
-                      tot_vol/GALLONS_TO_MM3
-       );
-
-    bu_free((char *)face.pts, "analyze_arb8: pts");
-    bu_free((char *)table.rows, "analyze_arb8: rows");
-}
-
-
-HIDDEN void
-analyze_arbn(struct ged *gedp, const struct rt_db_internal *ip)
-{
-    size_t i;
-    fastf_t tot_vol = 0.0, tot_area = 0.0;
-    table_t table;
-    struct poly_face *faces;
-    struct bu_vls tmpstr = BU_VLS_INIT_ZERO;
-    struct rt_arbn_internal *aip = (struct rt_arbn_internal *)ip->idb_ptr;
-    size_t *npts = (size_t *)bu_calloc(aip->neqn, sizeof(size_t), 
"analyze_arbn: npts");
-    point_t **tmp_pts = (point_t **)bu_calloc(aip->neqn, sizeof(point_t *), 
"analyze_arbn: tmp_pts");
-    plane_t *eqs= (plane_t *)bu_calloc(aip->neqn, sizeof(plane_t), 
"analyze_arbn: eqs");
-
-    /* allocate array of face structs */
-    faces = (struct poly_face *)bu_calloc(aip->neqn, sizeof(struct poly_face), 
"analyze_arbn: faces");
-    for (i = 0; i < aip->neqn; i++) {
-       HMOVE(faces[i].plane_eqn, aip->eqn[i]);
-       VUNITIZE(faces[i].plane_eqn);
-       /* allocate array of pt structs, max number of verts per faces = (# of 
faces) - 1 */
-       faces[i].pts = (point_t *)bu_calloc(aip->neqn - 1, sizeof(point_t), 
"analyze_arbn: pts");
-       tmp_pts[i] = faces[i].pts;
-       HMOVE(eqs[i], faces[i].plane_eqn);
-    }
-    /* allocate table rows, 1 row per plane eqn */
-    table.rows = (row_t *)bu_calloc(aip->neqn, sizeof(row_t), "analyze_arbn: 
rows");
-    table.nrows = aip->neqn;
-
-    bg_3d_polygon_make_pnts_planes(npts, tmp_pts, aip->neqn, (const plane_t 
*)eqs);
-
-    for (i = 0; i < aip->neqn; i++) {
-       vect_t tmp;
-       bu_vls_sprintf(&tmpstr, "%4zu", i);
-       snprintf(faces[i].label, sizeof(faces[i].label), "%s", 
bu_vls_addr(&tmpstr));
-
-       faces[i].npts = npts[i];
-
-       /* calculate surface area */
-       analyze_poly_face(gedp, &faces[i], &table.rows[i]);
-       tot_area += faces[i].area;
-
-       /* calculate volume */
-       VSCALE(tmp, faces[i].plane_eqn, faces[i].area);
-       tot_vol += VDOT(faces[i].pts[0], tmp);
-    }
-    tot_vol /= 3.0;
-
-    print_faces_table(gedp, &table);
-    print_volume_table(gedp,
-                      tot_vol
-                      * gedp->ged_wdbp->dbip->dbi_base2local
-                      * gedp->ged_wdbp->dbip->dbi_base2local
-                      * gedp->ged_wdbp->dbip->dbi_base2local,
-                      tot_area
-                      * gedp->ged_wdbp->dbip->dbi_base2local
-                      * gedp->ged_wdbp->dbip->dbi_base2local,
-                      tot_vol/GALLONS_TO_MM3
-       );
-
-    for (i = 0; i < aip->neqn; i++) {
-       bu_free((char *)faces[i].pts, "analyze_arbn: pts");
-    }
-    bu_free((char *)faces, "analyze_arbn: faces");
-    bu_free((char *)table.rows, "analyze_arbn: rows");
-    bu_free((char *)tmp_pts, "analyze_arbn: tmp_pts");
-    bu_free((char *)npts, "analyze_arbn: npts");
-    bu_free((char *)eqs, "analyze_arbn: eqs");
-    bu_vls_free(&tmpstr);
-}
-
-
-#define ARS_PT(ii, jj) (&arip->curves[i+(ii)][(j+(jj))*ELEMENTS_PER_VECT])
-
-HIDDEN void
-analyze_ars(struct ged *gedp, const struct rt_db_internal *ip)
-{
-    size_t i, j, k;
-    size_t nfaces = 0;
-    fastf_t tot_area = 0.0, tot_vol = 0.0;
-    table_t table;
-    plane_t old_plane = HINIT_ZERO;
-    struct bu_vls tmpstr = BU_VLS_INIT_ZERO;
-    struct poly_face face = POLY_FACE_INIT_ZERO;
-    struct rt_ars_internal *arip = (struct rt_ars_internal *)ip->idb_ptr;
-    RT_ARS_CK_MAGIC(arip);
-
-    /* allocate pts array, max 3 pts per triangular face */
-    face.pts = (point_t *)bu_calloc(3, sizeof(point_t), "analyze_ars: pts");
-    /* allocate table rows, probably overestimating the number of rows needed 
*/
-    table.rows = (row_t *)bu_calloc((arip->ncurves - 1) * 2 * 
arip->pts_per_curve, sizeof(row_t), "analyze_ars: rows");
-
-    k = arip->pts_per_curve - 2;
-    for (i = 0; i < arip->ncurves - 1; i++) {
-       int double_ended = k != 1 && 
VEQUAL(&arip->curves[i][ELEMENTS_PER_VECT], &arip->curves[i][k * 
ELEMENTS_PER_VECT]);
-
-       for (j = 0; j < arip->pts_per_curve; j++) {
-           vect_t tmp;
-
-           if (double_ended && i != 0 && (j == 0 || j == k || j == 
arip->pts_per_curve - 1)) continue;
-
-           /* first triangular face, make sure it's not a duplicate */
-           if (bn_make_plane_3pnts(face.plane_eqn, ARS_PT(0, 0), ARS_PT(1, 1), 
ARS_PT(0, 1), &gedp->ged_wdbp->wdb_tol) == 0
-               && !HEQUAL(old_plane, face.plane_eqn)) {
-               HMOVE(old_plane, face.plane_eqn);
-               ADD_PT(face, ARS_PT(0, 1));
-               ADD_PT(face, ARS_PT(0, 0));
-               ADD_PT(face, ARS_PT(1, 1));
-
-               bu_vls_sprintf(&tmpstr, "%zu%zu", i, j);
-               snprintf(face.label, sizeof(face.label), "%s", 
bu_vls_addr(&tmpstr));
-
-               /* surface area */
-               analyze_poly_face(gedp, &face, &(table.rows[nfaces]));
-               tot_area += face.area;
-
-               /* volume */
-               VSCALE(tmp, face.plane_eqn, face.area);
-               tot_vol += fabs(VDOT(face.pts[0], tmp));
-
-               face.npts = 0;
-               nfaces++;
-           }
-
-           /* second triangular face, make sure it's not a duplicate */
-           if (bn_make_plane_3pnts(face.plane_eqn, ARS_PT(1, 0), ARS_PT(1, 1), 
ARS_PT(0, 0), &gedp->ged_wdbp->wdb_tol) == 0
-               && !HEQUAL(old_plane, face.plane_eqn)) {
-               HMOVE(old_plane, face.plane_eqn);
-               ADD_PT(face, ARS_PT(1, 0));
-               ADD_PT(face, ARS_PT(0, 0));
-               ADD_PT(face, ARS_PT(1, 1));
-
-               bu_vls_sprintf(&tmpstr, "%zu%zu", i, j);
-               snprintf(face.label, sizeof(face.label), "%s", 
bu_vls_addr(&tmpstr));
-
-               analyze_poly_face(gedp, &face, &table.rows[nfaces]);
-               tot_area += face.area;
-
-               VSCALE(tmp, face.plane_eqn, face.area);
-               tot_vol += fabs(VDOT(face.pts[0], tmp));
-
-               face.npts = 0;
-               nfaces++;
-           }
-       }
-    }
-    tot_vol /= 3.0;
-    table.nrows = nfaces;
-
-    print_faces_table(gedp, &table);
-    print_volume_table(gedp,
-                      tot_vol
-                      * gedp->ged_wdbp->dbip->dbi_base2local
-                      * gedp->ged_wdbp->dbip->dbi_base2local
-                      * gedp->ged_wdbp->dbip->dbi_base2local,
-                      tot_area
-                      * gedp->ged_wdbp->dbip->dbi_base2local
-                      * gedp->ged_wdbp->dbip->dbi_base2local,
-                      tot_vol/GALLONS_TO_MM3
-       );
-
-    bu_free((char *)face.pts, "analyze_ars: pts");
-    bu_free((char *)table.rows, "analyze_ars: rows");
-    bu_vls_free(&tmpstr);
-}
-
-
-#define PROLATE 1
-#define OBLATE 2
-
-HIDDEN void
-analyze_superell(struct ged *gedp, const struct rt_db_internal *ip)
-{
-    struct rt_superell_internal *superell = (struct rt_superell_internal 
*)ip->idb_ptr;
-    fastf_t ma, mb, mc;
-    fastf_t ecc, major_mag, minor_mag;
-    fastf_t vol, sur_area;
-    int type;
-
-    RT_SUPERELL_CK_MAGIC(superell);
-
-    ma = MAGNITUDE(superell->a);
-    mb = MAGNITUDE(superell->b);
-    mc = MAGNITUDE(superell->c);
-
-    type = 0;
-
-    vol = 4.0 * M_PI * ma * mb * mc / 3.0;
-
-    if (fabs(ma-mb) < .00001 && fabs(mb-mc) < .00001) {
-       /* have a sphere */
-       sur_area = 4.0 * M_PI * ma * ma;
-       goto print_results;
-    }
-    if (fabs(ma-mb) < .00001) {
-       /* A == B */
-       if (mc > ma) {
-           /* oblate spheroid */
-           type = OBLATE;
-           major_mag = mc;
-           minor_mag = ma;
-       } else {
-           /* prolate spheroid */
-           type = PROLATE;
-           major_mag = ma;
-           minor_mag = mc;
-       }
-    } else
-       if (fabs(ma-mc) < .00001) {
-           /* A == C */
-           if (mb > ma) {
-               /* oblate spheroid */
-               type = OBLATE;
-               major_mag = mb;
-               minor_mag = ma;
-           } else {
-               /* prolate spheroid */
-               type = PROLATE;
-               major_mag = ma;
-               minor_mag = mb;
-           }
-       } else
-           if (fabs(mb-mc) < .00001) {
-               /* B == C */
-               if (ma > mb) {
-                   /* oblate spheroid */
-                   type = OBLATE;
-                   major_mag = ma;
-                   minor_mag = mb;
-               } else {
-                   /* prolate spheroid */
-                   type = PROLATE;
-                   major_mag = mb;
-                   minor_mag = ma;
-               }
-           } else {
-               bu_vls_printf(gedp->ged_result_str, "   Cannot find surface 
area\n");
-               return;
-           }
-    ecc = sqrt(major_mag*major_mag - minor_mag*minor_mag) / major_mag;
-    if (type == PROLATE) {
-       sur_area = M_2PI * minor_mag * minor_mag +
-           (M_2PI * (major_mag*minor_mag/ecc) * asin(ecc));
-    } else {
-       /* type == OBLATE */
-       sur_area = M_2PI * major_mag * major_mag +
-           (M_PI * (minor_mag*minor_mag/ecc) * log((1.0+ecc)/(1.0-ecc)));
-    }
-
-print_results:
-    print_volume_table(gedp,
-                      vol
-                      * gedp->ged_wdbp->dbip->dbi_base2local
-                      * gedp->ged_wdbp->dbip->dbi_base2local
-                      * gedp->ged_wdbp->dbip->dbi_base2local,
-                      sur_area
-                      * gedp->ged_wdbp->dbip->dbi_base2local
-                      * gedp->ged_wdbp->dbip->dbi_base2local,
-                      vol/GALLONS_TO_MM3
-       );
-}
-
-
-HIDDEN void
-analyze_sketch(struct ged *gedp, const struct rt_db_internal *ip)
-{
-    fastf_t area = -1;
-    point_t centroid;
-
-    if (OBJ[ID_SKETCH].ft_surf_area)
-       OBJ[ID_SKETCH].ft_surf_area(&area, ip);
-
-    if (area > 0.0) {
-       bu_vls_printf(gedp->ged_result_str, "\nTotal Area: %10.8f",
-                     area
-                     * gedp->ged_wdbp->dbip->dbi_local2base
-                     * gedp->ged_wdbp->dbip->dbi_local2base
-           );
-    }
-
-    if (OBJ[ID_SKETCH].ft_centroid) {
-       OBJ[ID_SKETCH].ft_centroid(&centroid, ip);
-       bu_vls_printf(gedp->ged_result_str, "\n    Centroid: (%g, %g, %g)\n",
-                     centroid[X] * gedp->ged_wdbp->dbip->dbi_base2local,
-                     centroid[Y] * gedp->ged_wdbp->dbip->dbi_base2local,
-                     centroid[Z] * gedp->ged_wdbp->dbip->dbi_base2local);
-    }
-}
-
-
-/**
- * Analyze command - prints loads of info about a solid
- * Format:     analyze [name]
- * if 'name' is missing use solid being edited
- */
-
-/* Analyze solid in internal form */
-HIDDEN void
-analyze_do(struct ged *gedp, const struct rt_db_internal *ip)
-{
-    /* XXX Could give solid name, and current units, here */
-
-    switch (ip->idb_type) {
-
-       case ID_ARB8:
-           analyze_arb8(gedp, ip);
-           break;
-
-       case ID_BOT:
-           analyze_general(gedp, ip);
-           break;
-
-       case ID_ARBN:
-           analyze_arbn(gedp, ip);
-           break;
-
-       case ID_ARS:
-           analyze_ars(gedp, ip);
-           break;
-
-       case ID_TGC:
-           analyze_general(gedp, ip);
-           break;
-
-       case ID_ELL:
-           analyze_general(gedp, ip);
-           break;
-
-       case ID_TOR:
-           analyze_general(gedp, ip);
-           break;
-
-       case ID_RPC:
-           analyze_general(gedp, ip);
-           break;
-
-       case ID_ETO:
-           analyze_general(gedp, ip);
-           break;
-
-       case ID_EPA:
-           analyze_general(gedp, ip);
-           break;
-
-       case ID_PARTICLE:
-           analyze_general(gedp, ip);
-           break;
-
-       case ID_SUPERELL:
-           analyze_superell(gedp, ip);
-           break;
-
-       case ID_SKETCH:
-           analyze_sketch(gedp, ip);
-           break;
-
-       case ID_HYP:
-           analyze_general(gedp, ip);
-           break;
-
-       case ID_PIPE:
-           analyze_general(gedp, ip);
-           break;
-
-       case ID_VOL:
-           analyze_general(gedp, ip);
-           break;
-
-        case ID_EXTRUDE:
-           analyze_general(gedp, ip);
-           break;
-
-        case ID_RHC:
-           analyze_general(gedp, ip);
-           break;
-
-       default:
-           bu_vls_printf(gedp->ged_result_str, "\nanalyze: unable to process 
%s solid\n",
-                         OBJ[ip->idb_type].ft_name);
-           break;
-    }
-}
-
-
-int
-ged_analyze_core(struct ged *gedp, int argc, const char *argv[])
-{
-    struct directory *ndp;
-    int i;
-    struct rt_db_internal intern;
-    static const char *usage = "object(s)";
-
-    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", argv[0], usage);
-       return GED_HELP;
-    }
-
-    /* use the names that were input */
-    for (i = 1; i < argc; i++) {
-       if ((ndp = db_lookup(gedp->ged_wdbp->dbip,  argv[i], LOOKUP_NOISY)) == 
RT_DIR_NULL)
-           continue;
-
-       GED_DB_GET_INTERNAL(gedp, &intern, ndp, bn_mat_identity, 
&rt_uniresource, GED_ERROR);
-
-       _ged_do_list(gedp, ndp, 1);
-       analyze_do(gedp, &intern);
-       rt_db_free_internal(&intern);
-    }
-
-    return GED_OK;
-}
-
-
-#ifdef GED_PLUGIN
-#include "../include/plugin.h"
-struct ged_cmd_impl analyze_cmd_impl = {
-    "analyze",
-    ged_analyze_core,
-    GED_CMD_DEFAULT
-};
-
-const struct ged_cmd analyze_cmd = { &analyze_cmd_impl };
-const struct ged_cmd *analyze_cmds[] = { &analyze_cmd, NULL };
-
-static const struct ged_plugin pinfo = { GED_API,  analyze_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/analyze/analyze.cpp (from rev 77221, 
brlcad/branches/analyze_cmd/src/libged/analyze/analyze.cpp)
===================================================================
--- brlcad/trunk/src/libged/analyze/analyze.cpp                         (rev 0)
+++ brlcad/trunk/src/libged/analyze/analyze.cpp 2020-09-25 13:41:24 UTC (rev 
77222)
@@ -0,0 +1,748 @@
+/*                      A N A L Y Z E . C P P
+ * BRL-CAD
+ *
+ * Copyright (c) 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 analyze.cpp
+ *
+ * The analyze command
+ *
+ */
+
+#include "common.h"
+
+extern "C" {
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+}
+
+#include <limits>
+
+extern "C" {
+#include "bu/cmd.h"
+#include "bu/opt.h"
+#include "../ged_private.h"
+#include "./ged_analyze.h"
+}
+
+#define DB_SOLID INT_MAX
+#define DB_NON_SOLID INT_MAX - 1
+
+
+#define HELPFLAG "--print-help"
+#define PURPOSEFLAG "--print-purpose"
+
+struct _ged_analyze_info {
+    struct ged *gedp = NULL;
+    const struct bu_cmdtab *cmds = NULL;
+    struct bu_opt_desc *gopts = NULL;
+    int verbosity = 0;
+    std::map<std::pair<int, int>, op_func_ptr> *union_map;
+    std::map<std::pair<int, int>, op_func_ptr> *isect_map;
+    std::map<std::pair<int, int>, op_func_ptr> *subtr_map;
+};
+
+
+static struct _ged_analyze_info *
+_analyze_info_create()
+{
+    struct _ged_analyze_info *gc = new struct _ged_analyze_info;
+    gc->verbosity = 0;
+    gc->union_map = new std::map<std::pair<int, int>, op_func_ptr>;
+    gc->isect_map = new std::map<std::pair<int, int>, op_func_ptr>;
+    gc->subtr_map = new std::map<std::pair<int, int>, op_func_ptr>;
+
+
+    // Populate the maps with known pair analysis functions
+    (*gc->union_map)[std::make_pair(DB5_MINORTYPE_BRLCAD_PNTS, DB_SOLID)] = 
op_pnts_vol;
+    (*gc->isect_map)[std::make_pair(DB5_MINORTYPE_BRLCAD_PNTS, DB_SOLID)] = 
op_pnts_vol;
+    (*gc->subtr_map)[std::make_pair(DB5_MINORTYPE_BRLCAD_PNTS, DB_SOLID)] = 
op_pnts_vol;
+
+    return gc;
+}
+
+static void
+_analyze_info_destroy(struct _ged_analyze_info *s)
+{
+    delete s->union_map;
+    delete s->isect_map;
+    delete s->subtr_map;
+    delete s;
+}
+
+static bool
+db_solid_type(int type)
+{
+    switch (type) {
+       case DB5_MINORTYPE_BRLCAD_ARB8:
+       case DB5_MINORTYPE_BRLCAD_ARBN:
+       case DB5_MINORTYPE_BRLCAD_ARS:
+       case DB5_MINORTYPE_BRLCAD_BOT:
+       case DB5_MINORTYPE_BRLCAD_BREP:
+       case DB5_MINORTYPE_BRLCAD_BSPLINE:
+       case DB5_MINORTYPE_BRLCAD_CLINE:
+       case DB5_MINORTYPE_BRLCAD_COMBINATION:
+       case DB5_MINORTYPE_BRLCAD_DSP:
+       case DB5_MINORTYPE_BRLCAD_EBM:
+       case DB5_MINORTYPE_BRLCAD_EHY:
+       case DB5_MINORTYPE_BRLCAD_ELL:
+       case DB5_MINORTYPE_BRLCAD_EPA:
+       case DB5_MINORTYPE_BRLCAD_ETO:
+       case DB5_MINORTYPE_BRLCAD_EXTRUDE:
+       case DB5_MINORTYPE_BRLCAD_HALF:
+       case DB5_MINORTYPE_BRLCAD_HF:
+       case DB5_MINORTYPE_BRLCAD_HRT:
+       case DB5_MINORTYPE_BRLCAD_HYP:
+       case DB5_MINORTYPE_BRLCAD_METABALL:
+       case DB5_MINORTYPE_BRLCAD_NMG:
+       case DB5_MINORTYPE_BRLCAD_PARTICLE:
+       case DB5_MINORTYPE_BRLCAD_PIPE:
+       case DB5_MINORTYPE_BRLCAD_POLY:
+       case DB5_MINORTYPE_BRLCAD_REC:
+       case DB5_MINORTYPE_BRLCAD_REVOLVE:
+       case DB5_MINORTYPE_BRLCAD_RHC:
+       case DB5_MINORTYPE_BRLCAD_RPC:
+       case DB5_MINORTYPE_BRLCAD_SKETCH:
+       case DB5_MINORTYPE_BRLCAD_SPH:
+       case DB5_MINORTYPE_BRLCAD_SUBMODEL:
+       case DB5_MINORTYPE_BRLCAD_SUPERELL:
+       case DB5_MINORTYPE_BRLCAD_TGC:
+       case DB5_MINORTYPE_BRLCAD_TOR:
+       case DB5_MINORTYPE_BRLCAD_VOL:
+           return true;
+       case DB5_MINORTYPE_BRLCAD_ANNOT:
+       case DB5_MINORTYPE_BRLCAD_CONSTRAINT:
+       case DB5_MINORTYPE_BRLCAD_DATUM:
+       case DB5_MINORTYPE_BRLCAD_GRIP:
+       case DB5_MINORTYPE_BRLCAD_JOINT:
+       case DB5_MINORTYPE_BRLCAD_PNTS:
+       case DB5_MINORTYPE_BRLCAD_SCRIPT:
+       default:
+           return false;
+    };
+}
+
+static op_func_ptr
+_analyze_find_processor(struct _ged_analyze_info *s, db_op_t op, int t1, int 
t2)
+{
+    int type1 = t1;
+    int type2 = t2;
+    std::map<std::pair<int, int>, op_func_ptr> *omap;
+    switch (op) {
+       case DB_OP_UNION:
+           omap = s->union_map;
+           break;
+       case DB_OP_INTERSECT:
+           omap = s->isect_map;
+           break;
+       case DB_OP_SUBTRACT:
+           omap = s->subtr_map;
+           break;
+       default:
+           return NULL;
+    }
+
+    if (omap->find(std::make_pair(type1, type2)) != omap->end()) {
+       return (*omap)[std::make_pair(t1, t2)];
+    }
+
+    // If there isn't a specific type, see if there's a generic match for t2
+    type2 = (db_solid_type(t2)) ? DB_SOLID : DB_NON_SOLID;
+    if (omap->find(std::make_pair(type1, type2)) != omap->end()) {
+       return (*omap)[std::make_pair(type1, type2)];
+    }
+
+    // If there isn't a specific type, see if there's a generic match for t1
+    type1 = (db_solid_type(t1)) ? DB_SOLID : DB_NON_SOLID;
+    type2 = t2;
+    if (omap->find(std::make_pair(type1, type2)) != omap->end()) {
+       return (*omap)[std::make_pair(type1, type2)];
+    }
+    // If there isn't a match, see if there's a generic match for t1 and t2
+    type1 = (db_solid_type(t1)) ? DB_SOLID : DB_NON_SOLID;
+    type2 = (db_solid_type(t2)) ? DB_SOLID : DB_NON_SOLID;
+    if (omap->find(std::make_pair(type1, type2)) != omap->end()) {
+       return (*omap)[std::make_pair(type1, type2)];
+    }
+
+    // Nope, nothing
+    return NULL;
+}
+
+static int
+_analyze_cmd_msgs(void *cs, int argc, const char **argv, const char *us, const 
char *ps)
+{
+    struct _ged_analyze_info *gc = (struct _ged_analyze_info *)cs;
+    if (argc == 2 && BU_STR_EQUAL(argv[1], HELPFLAG)) {
+       bu_vls_printf(gc->gedp->ged_result_str, "%s\n%s\n", us, ps);
+       return 1;
+    }
+    if (argc == 2 && BU_STR_EQUAL(argv[1], PURPOSEFLAG)) {
+       bu_vls_printf(gc->gedp->ged_result_str, "%s\n", ps);
+       return 1;
+    }
+    return 0;
+}
+
+/* Analyze solid in internal form.
+ * TODO - this switch table probably indicates this should
+ * be a functab callback... */
+/**
+ * TODO: primitives that still need implementing
+ * ehy
+ * metaball
+ * nmg
+ */
+static void
+analyze_do_summary(struct ged *gedp, const struct rt_db_internal *ip)
+{
+    /* XXX Could give solid name, and current units, here */
+
+    switch (ip->idb_type) {
+
+       case ID_ARB8:
+           analyze_arb8(gedp, ip);
+           break;
+
+       case ID_BOT:
+           analyze_general(gedp, ip);
+           break;
+
+       case ID_ARBN:
+           analyze_arbn(gedp, ip);
+           break;
+
+       case ID_ARS:
+           analyze_ars(gedp, ip);
+           break;
+
+       case ID_TGC:
+           analyze_general(gedp, ip);
+           break;
+
+       case ID_ELL:
+           analyze_general(gedp, ip);
+           break;
+
+       case ID_TOR:
+           analyze_general(gedp, ip);
+           break;
+
+       case ID_RPC:
+           analyze_general(gedp, ip);
+           break;
+
+       case ID_ETO:
+           analyze_general(gedp, ip);
+           break;
+
+       case ID_EPA:
+           analyze_general(gedp, ip);
+           break;
+
+       case ID_PARTICLE:
+           analyze_general(gedp, ip);
+           break;
+
+       case ID_SUPERELL:
+           analyze_superell(gedp, ip);
+           break;
+
+       case ID_SKETCH:
+           analyze_sketch(gedp, ip);
+           break;
+
+       case ID_HYP:
+           analyze_general(gedp, ip);
+           break;
+
+       case ID_PIPE:
+           analyze_general(gedp, ip);
+           break;
+
+       case ID_VOL:
+           analyze_general(gedp, ip);
+           break;
+
+        case ID_EXTRUDE:
+           analyze_general(gedp, ip);
+           break;
+
+        case ID_RHC:
+           analyze_general(gedp, ip);
+           break;
+
+       default:
+           bu_vls_printf(gedp->ged_result_str, "\nanalyze: unable to process 
%s solid\n",
+                         OBJ[ip->idb_type].ft_name);
+           break;
+    }
+}
+
+extern "C" int
+_analyze_cmd_summarize(void *bs, int argc, const char **argv)
+{
+    const char *usage_string = "analyze [options] summarize obj1 <obj2 ...>";
+    const char *purpose_string = "Summary of analytical information about 
listed objects";
+    if (_analyze_cmd_msgs(bs, argc, argv, usage_string, purpose_string)) {
+       return GED_OK;
+    }
+
+    struct _ged_analyze_info *gc = (struct _ged_analyze_info *)bs;
+    struct ged *gedp = gc->gedp;
+    struct rt_db_internal intern;
+
+    argc--; argv++;
+    if (!argc) {
+       bu_vls_printf(gc->gedp->ged_result_str, "%s\n", usage_string);
+       return GED_ERROR;
+    }
+
+    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);
+
+    /* use the names that were input */
+    for (int i = 0; i < argc; i++) {
+       struct directory *ndp = db_lookup(gedp->ged_wdbp->dbip,  argv[i], 
LOOKUP_NOISY);
+       if (ndp == RT_DIR_NULL)
+           continue;
+
+       GED_DB_GET_INTERNAL(gedp, &intern, ndp, bn_mat_identity, 
&rt_uniresource, GED_ERROR);
+
+       _ged_do_list(gedp, ndp, 1);
+       analyze_do_summary(gedp, &intern);
+       rt_db_free_internal(&intern);
+    }
+
+    return GED_OK;
+}
+
+static void
+clear_obj(struct ged *gedp, const char *name)
+{
+    struct bu_vls tmpstr = BU_VLS_INIT_ZERO;
+    bu_vls_sprintf(&tmpstr, "%s", bu_vls_cstr(gedp->ged_result_str));
+    const char *av[4];
+    av[0] = "kill";
+    av[1] = "-f";
+    av[2] = "-q";
+    av[3] = name;
+    ged_kill(gedp, 4, (const char **)av);
+    bu_vls_sprintf(gedp->ged_result_str, "%s", bu_vls_cstr(&tmpstr));
+}
+
+
+static void
+mv_obj(struct ged *gedp, const char *n1, const char *n2)
+{
+    clear_obj(gedp, n2);
+    struct bu_vls tmpstr = BU_VLS_INIT_ZERO;
+    bu_vls_sprintf(&tmpstr, "%s", bu_vls_cstr(gedp->ged_result_str));
+    const char *av[3];
+    av[0] = "mv";
+    av[1] = n1;
+    av[2] = n2;
+    ged_move(gedp, 3, (const char **)av);
+    bu_vls_sprintf(gedp->ged_result_str, "%s", bu_vls_cstr(&tmpstr));
+}
+
+extern "C" int
+_analyze_cmd_intersect(void *bs, int argc, const char **argv)
+{
+    const char *usage_string = "analyze [options] intersect [-o out_obj] obj1 
obj2 <...>";
+    const char *purpose_string = "Intersect obj1 with obj2 and any subsequent 
objs";
+    if (_analyze_cmd_msgs(bs, argc, argv, usage_string, purpose_string)) {
+       return GED_OK;
+    }
+
+    struct _ged_analyze_info *gc = (struct _ged_analyze_info *)bs;
+    struct ged *gedp = gc->gedp;
+
+    argc--; argv++;
+    if (!argc) {
+       bu_vls_printf(gc->gedp->ged_result_str, "%s\n", usage_string);
+       return GED_ERROR;
+    }
+
+    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);
+
+    // See if we are going to output an object
+    int help = 0;
+    struct bu_vls oname = BU_VLS_INIT_ZERO;
+    struct bu_opt_desc d[3];
+    BU_OPT(d[0], "h", "help",    "",      NULL,        &help,  "Print help");
+    BU_OPT(d[1], "o", "output",  "name",  &bu_opt_vls, &oname, "Specify output 
object");
+    BU_OPT_NULL(d[2]);
+
+    int ac = bu_opt_parse(NULL, argc, argv, d);
+    if (help) {
+       bu_vls_printf(gc->gedp->ged_result_str, "%s\n", usage_string);
+       return GED_HELP;
+    }
+    if (ac < 2) {
+       bu_vls_printf(gc->gedp->ged_result_str, "%s\n", usage_string);
+       return GED_HELP;
+    }
+    argc = ac;
+
+    if (bu_vls_strlen(&oname)) {
+       struct directory *dp_out = db_lookup(gedp->ged_wdbp->dbip, 
bu_vls_cstr(&oname), LOOKUP_QUIET);
+       if (dp_out != RT_DIR_NULL) {
+           bu_vls_sprintf(gedp->ged_result_str, "specified output object %s 
already exists.\n", bu_vls_cstr(&oname));
+           bu_vls_free(&oname);
+           return GED_ERROR;
+       }
+    }
+
+    long ret = 0;
+    const char *tmpname = "___analyze_cmd_intersect_tmp_obj__";
+    struct directory *dp1 = db_lookup(gedp->ged_wdbp->dbip, argv[0], 
LOOKUP_NOISY);
+    struct directory *dp2 = db_lookup(gedp->ged_wdbp->dbip, argv[1], 
LOOKUP_NOISY);
+    op_func_ptr of = _analyze_find_processor(gc, DB_OP_INTERSECT, 
dp1->d_minor_type, dp2->d_minor_type);
+    if (!of) {
+       bu_vls_sprintf(gedp->ged_result_str, "Unsupported type pairing\n");
+       bu_vls_free(&oname);
+       return GED_ERROR;
+    }
+    clear_obj(gc->gedp, tmpname);
+    ret = (*of)(tmpname, gc->gedp,  DB_OP_INTERSECT, argv[0], argv[1]);
+    if (ret == -1) {
+       clear_obj(gc->gedp, tmpname);
+       bu_vls_free(&oname);
+       return GED_ERROR;
+    }
+
+    if (argc > 2) {
+       const char *tmpname2 = "___analyze_cmd_intersect_tmp_obj_2__";
+       for (int i = 2; i < argc; i++) {
+           const char *n1 = tmpname;
+           const char *n2 = argv[i];
+           dp1 = db_lookup(gedp->ged_wdbp->dbip, n1, LOOKUP_NOISY);
+           dp2 = db_lookup(gedp->ged_wdbp->dbip, n2, LOOKUP_NOISY);
+           of = _analyze_find_processor(gc, DB_OP_INTERSECT, 
dp1->d_minor_type, dp2->d_minor_type);
+           if (!of) {
+               bu_vls_sprintf(gedp->ged_result_str, "Unsupported type 
pairing\n");
+               clear_obj(gc->gedp, tmpname);
+               clear_obj(gc->gedp, tmpname2);
+               bu_vls_free(&oname);
+               return GED_ERROR;
+           }
+           ret = (*of)(tmpname2, gc->gedp,  DB_OP_INTERSECT, n1, n2);
+           mv_obj(gc->gedp, tmpname2, tmpname);
+           if (ret == -1) {
+               clear_obj(gc->gedp, tmpname);
+               clear_obj(gc->gedp, tmpname2);
+               bu_vls_free(&oname);
+               return GED_ERROR;
+           }
+       }
+    }
+
+    if (bu_vls_strlen(&oname)) {
+       mv_obj(gc->gedp, tmpname, bu_vls_cstr(&oname));
+    }
+
+    clear_obj(gc->gedp, tmpname);
+
+    bu_vls_sprintf(gedp->ged_result_str, "%ld\n", ret);
+    bu_vls_free(&oname);
+
+    return GED_OK;
+}
+
+extern "C" int
+_analyze_cmd_subtract(void *bs, int argc, const char **argv)
+{
+    const char *usage_string = "analyze [options] subtract [-o out_obj] obj1 
obj2 <...>";
+    const char *purpose_string = "Subtract obj2 (and any subsequent objects) 
from obj1";
+    if (_analyze_cmd_msgs(bs, argc, argv, usage_string, purpose_string)) {
+       return GED_OK;
+    }
+
+    struct _ged_analyze_info *gc = (struct _ged_analyze_info *)bs;
+    struct ged *gedp = gc->gedp;
+
+    argc--; argv++;
+    if (!argc) {
+       bu_vls_printf(gc->gedp->ged_result_str, "%s\n", usage_string);
+       return GED_ERROR;
+    }
+
+    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);
+
+    // See if we are going to output an object
+    int help = 0;
+    struct bu_vls oname = BU_VLS_INIT_ZERO;
+    struct bu_opt_desc d[3];
+    BU_OPT(d[0], "h", "help",    "",      NULL,        &help,  "Print help");
+    BU_OPT(d[1], "o", "output",  "name",  &bu_opt_vls, &oname, "Specify output 
object");
+    BU_OPT_NULL(d[2]);
+
+    int ac = bu_opt_parse(NULL, argc, argv, d);
+    if (help) {
+       bu_vls_printf(gc->gedp->ged_result_str, "%s\n", usage_string);
+       return GED_HELP;
+    }
+    if (ac < 2) {
+       bu_vls_printf(gc->gedp->ged_result_str, "%s\n", usage_string);
+       return GED_HELP;
+    }
+    argc = ac;
+
+    if (bu_vls_strlen(&oname)) {
+       struct directory *dp_out = db_lookup(gedp->ged_wdbp->dbip, 
bu_vls_cstr(&oname), LOOKUP_QUIET);
+       if (dp_out != RT_DIR_NULL) {
+           bu_vls_sprintf(gedp->ged_result_str, "specified output object %s 
already exists.\n", bu_vls_cstr(&oname));
+           bu_vls_free(&oname);
+           return GED_ERROR;
+       }
+    }
+
+    long ret = 0;
+    const char *tmpname = "___analyze_cmd_subtract_tmp_obj__";
+    struct directory *dp1 = db_lookup(gedp->ged_wdbp->dbip, argv[0], 
LOOKUP_NOISY);
+    struct directory *dp2 = db_lookup(gedp->ged_wdbp->dbip, argv[1], 
LOOKUP_NOISY);
+    op_func_ptr of = _analyze_find_processor(gc, DB_OP_SUBTRACT, 
dp1->d_minor_type, dp2->d_minor_type);
+    if (!of) {
+       bu_vls_sprintf(gedp->ged_result_str, "Unsupported type pairing\n");
+       bu_vls_free(&oname);
+       return GED_ERROR;
+    }
+    clear_obj(gc->gedp, tmpname);
+    ret = (*of)(tmpname, gc->gedp,  DB_OP_SUBTRACT, argv[0], argv[1]);
+    if (ret == -1) {
+       clear_obj(gc->gedp, tmpname);
+       bu_vls_free(&oname);
+       return GED_ERROR;
+    }
+
+    if (argc > 2) {
+       const char *tmpname2 = "___analyze_cmd_subtract_tmp_obj_2__";
+       for (int i = 2; i < argc; i++) {
+           const char *n1 = tmpname;
+           const char *n2 = argv[i];
+           dp1 = db_lookup(gedp->ged_wdbp->dbip, n1, LOOKUP_NOISY);
+           dp2 = db_lookup(gedp->ged_wdbp->dbip, n2, LOOKUP_NOISY);
+           of = _analyze_find_processor(gc, DB_OP_SUBTRACT, dp1->d_minor_type, 
dp2->d_minor_type);
+           if (!of) {
+               bu_vls_sprintf(gedp->ged_result_str, "Unsupported type 
pairing\n");
+               clear_obj(gc->gedp, tmpname);
+               clear_obj(gc->gedp, tmpname2);
+               bu_vls_free(&oname);
+               return GED_ERROR;
+           }
+           ret = (*of)(tmpname2, gc->gedp,  DB_OP_SUBTRACT, n1, n2);
+           mv_obj(gc->gedp, tmpname2, tmpname);
+           if (ret == -1) {
+               clear_obj(gc->gedp, tmpname);
+               clear_obj(gc->gedp, tmpname2);
+               bu_vls_free(&oname);
+               return GED_ERROR;
+           }
+       }
+    }
+
+    if (bu_vls_strlen(&oname)) {
+       mv_obj(gc->gedp, tmpname, bu_vls_cstr(&oname));
+    }
+
+    clear_obj(gc->gedp, tmpname);
+
+    bu_vls_sprintf(gedp->ged_result_str, "%ld\n", ret);
+    bu_vls_free(&oname);
+
+    return GED_OK;
+}
+
+extern "C" int
+_analyze_cmd_help(void *bs, int argc, const char **argv)
+{
+    struct _ged_analyze_info *gc = (struct _ged_analyze_info *)bs;
+    if (!argc || !argv || BU_STR_EQUAL(argv[0], "help")) {
+       bu_vls_printf(gc->gedp->ged_result_str, "analyze [options] subcommand 
[args]\n");
+       if (gc->gopts) {
+           char *option_help = bu_opt_describe(gc->gopts, NULL);
+           if (option_help) {
+               bu_vls_printf(gc->gedp->ged_result_str, "Options:\n%s\n", 
option_help);
+               bu_free(option_help, "help str");
+           }
+       }
+       bu_vls_printf(gc->gedp->ged_result_str, "Available subcommands:\n");
+       const struct bu_cmdtab *ctp = NULL;
+       int ret;
+       const char *helpflag[2];
+       helpflag[1] = PURPOSEFLAG;
+       size_t maxcmdlen = 0;
+       for (ctp = gc->cmds; ctp->ct_name != (char *)NULL; ctp++) {
+           maxcmdlen = (maxcmdlen > strlen(ctp->ct_name)) ? maxcmdlen : 
strlen(ctp->ct_name);
+       }
+       for (ctp = gc->cmds; ctp->ct_name != (char *)NULL; ctp++) {
+           bu_vls_printf(gc->gedp->ged_result_str, "  %s%*s", ctp->ct_name, 
(int)(maxcmdlen - strlen(ctp->ct_name)) +   2, " ");
+           if (!BU_STR_EQUAL(ctp->ct_name, "help")) {
+               helpflag[0] = ctp->ct_name;
+               bu_cmd(gc->cmds, 2, helpflag, 0, (void *)gc, &ret);
+           } else {
+               bu_vls_printf(gc->gedp->ged_result_str, "print help and 
exit\n");
+           }
+       }
+    } else {
+       int ret;
+       const char **helpargv = (const char **)bu_calloc(argc+1, sizeof(char 
*), "help argv");
+       helpargv[0] = argv[0];
+       helpargv[1] = HELPFLAG;
+       for (int i = 1; i < argc; i++) {
+           helpargv[i+1] = argv[i];
+       }
+       bu_cmd(gc->cmds, argc+1, helpargv, 0, (void *)gc, &ret);
+       bu_free(helpargv, "help argv");
+       return ret;
+    }
+
+    return GED_OK;
+}
+
+
+const struct bu_cmdtab _analyze_cmds[] = {
+      { "summarize",           _analyze_cmd_summarize},
+      { "intersect",           _analyze_cmd_intersect},
+      { "subtract",            _analyze_cmd_subtract},
+      { (char *)NULL,      NULL}
+  };
+
+
+extern "C" int
+ged_analyze_core(struct ged *gedp, int argc, const char *argv[])
+{
+    int help = 0;
+    struct _ged_analyze_info *gc = _analyze_info_create();
+    gc->gedp = gedp;
+    gc->cmds = _analyze_cmds;
+
+    // Sanity
+    if (UNLIKELY(!gedp || !argc || !argv)) {
+       _analyze_info_destroy(gc);
+       return GED_ERROR;
+    }
+
+    // Clear results
+    bu_vls_trunc(gedp->ged_result_str, 0);
+
+    // See if we have any high level options set
+    struct bu_opt_desc d[3];
+    BU_OPT(d[0], "h", "help",    "",  NULL, &help,          "Print help");
+    BU_OPT(d[1], "v", "verbose", "",  NULL, &gc->verbosity, "Verbose output");
+    BU_OPT_NULL(d[2]);
+
+    gc->gopts = d;
+
+    if (argc == 1) {
+       _analyze_cmd_help(gc, 0, NULL);
+       _analyze_info_destroy(gc);
+       return GED_OK;
+    }
+
+    // High level options are only defined prior to the subcommand
+    int cmd_pos = -1;
+    for (int i = 1; i < argc; i++) {
+       if (bu_cmd_valid(_analyze_cmds, argv[i]) == BRLCAD_OK) {
+           cmd_pos = i;
+           break;
+       }
+    }
+
+    int acnt = (cmd_pos >= 0) ? cmd_pos : argc;
+
+    bu_opt_parse(NULL, acnt, argv, d);
+
+    if (help) {
+       if (cmd_pos >= 0) {
+           argc = argc - cmd_pos;
+           argv = &argv[cmd_pos];
+           _analyze_cmd_help(gc, argc, argv);
+       } else {
+           _analyze_cmd_help(gc, 0, NULL);
+       }
+       _analyze_info_destroy(gc);
+       return GED_OK;
+    }
+
+
+    // Jump the processing past any options specified. If we don't have a
+    // subcommand, assume all args are geometry objects and the command mode is
+    // summarize. This will get us the old behavior, except in the case where
+    // we happen to have an object with a name that matches a subcommand of
+    // analyze.  In that case, the full "analyze summarize objname" is needed.
+    const char *scmd = "summarize";
+    if (cmd_pos != -1) {
+       argc = argc - cmd_pos;
+       argv = &argv[cmd_pos];
+    } else {
+       argv[0] = scmd;
+    }
+
+    int ret;
+    if (bu_cmd(_analyze_cmds, argc, argv, 0, (void *)gc, &ret) == BRLCAD_OK) {
+       _analyze_info_destroy(gc);
+       return ret;
+    } else {
+       bu_vls_printf(gedp->ged_result_str, "subcommand %s not defined", 
argv[0]);
+    }
+
+    _analyze_info_destroy(gc);
+    return GED_ERROR;
+}
+
+// Local Variables:
+
+#ifdef GED_PLUGIN
+#include "../include/plugin.h"
+extern "C" {
+    struct ged_cmd_impl analyze_cmd_impl = { "analyze", ged_analyze_core, 
GED_CMD_DEFAULT };
+    const struct ged_cmd analyze_cmd = { &analyze_cmd_impl };
+
+    const struct ged_cmd *analyze_cmds[] = { &analyze_cmd, NULL };
+
+    static const struct ged_plugin pinfo = { GED_API,  analyze_cmds, 1 };
+
+    COMPILER_DLLEXPORT const struct ged_plugin *ged_plugin_info()
+    {
+       return &pinfo;
+    }
+}
+#endif
+
+// Local Variables:
+// tab-width: 8
+// mode: C++
+// c-basic-offset: 4
+// indent-tabs-mode: t
+// c-file-style: "stroustrup"
+// End:
+// ex: shiftwidth=4 tabstop=8
+

Copied: brlcad/trunk/src/libged/analyze/arb8.cpp (from rev 77221, 
brlcad/branches/analyze_cmd/src/libged/analyze/arb8.cpp)
===================================================================
--- brlcad/trunk/src/libged/analyze/arb8.cpp                            (rev 0)
+++ brlcad/trunk/src/libged/analyze/arb8.cpp    2020-09-25 13:41:24 UTC (rev 
77222)
@@ -0,0 +1,209 @@
+/*                        A R B 8 . C P P
+ * BRL-CAD
+ *
+ * Copyright (c) 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 arb8.cpp
+ *
+ * Brief description
+ *
+ */
+
+#include "common.h"
+
+#include "vmath.h"
+
+#include "bg/polygon.h"
+#include "rt/arb_edit.h"
+#include "rt/geom.h"
+#include "ged/defines.h"
+#include "../ged_private.h"
+#include "./ged_analyze.h"
+
+/* edge definition array */
+static const int nedge[5][24] = {
+    {0, 1, 1, 2, 2, 0, 0, 3, 3, 2, 1, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
-1, -1, -1},   /* ARB4 */
+    {0, 1, 1, 2, 2, 3, 0, 3, 0, 4, 1, 4, 2, 4, 3, 4, -1, -1, -1, -1, -1, -1, 
-1, -1},       /* ARB5 */
+    {0, 1, 1, 2, 2, 3, 0, 3, 0, 4, 1, 4, 2, 5, 3, 5, 4, 5, -1, -1, -1, -1, -1, 
-1},         /* ARB6 */
+    {0, 1, 1, 2, 2, 3, 0, 3, 0, 4, 3, 4, 1, 5, 2, 6, 4, 5, 5, 6, 4, 6, -1, 
-1},             /* ARB7 */
+    {0, 1, 1, 2, 2, 3, 0, 3, 0, 4, 4, 5, 1, 5, 5, 6, 6, 7, 4, 7, 3, 7, 2, 6},  
             /* ARB8 */
+};
+
+/* ARB face printout array */
+static const int prface[5][6] = {
+    {123, 124, 234, 134, -111, -111},          /* ARB4 */
+    {1234, 125, 235, 345, 145, -111},          /* ARB5 */
+    {1234, 2365, 1564, 512, 634, -111},                /* ARB6 */
+    {1234, 567, 145, 2376, 1265, 4375},                /* ARB7 */
+    {1234, 5678, 1584, 2376, 1265, 4378},      /* ARB8 */
+};
+
+void
+analyze_edge(struct ged *gedp, const int edge, const struct rt_arb_internal 
*arb,
+            const int type, row_t *row)
+{
+    int a = nedge[type][edge*2];
+    int b = nedge[type][edge*2+1];
+
+    if (b == -1) {
+       row->nfields = 0;
+       return;
+    }
+
+    row->nfields = 2;
+    row->fields[0].nchars = sprintf(row->fields[0].buf, "%d%d", a + 1, b + 1);
+    row->fields[1].nchars = sprintf(row->fields[1].buf, "%10.8f",
+                                   DIST_PNT_PNT(arb->pt[a], 
arb->pt[b])*gedp->ged_wdbp->dbip->dbi_base2local);
+}
+
+
+
+void
+analyze_arb8(struct ged *gedp, const struct rt_db_internal *ip)
+{
+    int i, type;
+    int cgtype;     /* COMGEOM arb type: # of vertices */
+    table_t table;  /* holds table data from child functions */
+    fastf_t tot_vol = 0.0, tot_area = 0.0;
+    point_t center_pt = VINIT_ZERO;
+    struct poly_face face = POLY_FACE_INIT_ZERO;
+    struct rt_arb_internal earb;
+    struct rt_arb_internal *arb = (struct rt_arb_internal *)ip->idb_ptr;
+    const int arb_faces[5][24] = rt_arb_faces;
+    RT_ARB_CK_MAGIC(arb);
+
+    /* find the specific arb type, in GIFT order. */
+    if ((cgtype = rt_arb_std_type(ip, &gedp->ged_wdbp->wdb_tol)) == 0) {
+       bu_vls_printf(gedp->ged_result_str, "analyze_arb: bad ARB\n");
+       return;
+    }
+
+    type = cgtype - 4;
+
+    /* to get formatting correct, we need to collect the actual string
+     * lengths for each field BEFORE we start printing a table (fields
+     * are allowed to overflow the stated printf field width) */
+
+    /* TABLE 1 =========================================== */
+    /* analyze each face, use center point of arb for reference */
+    rt_arb_centroid(&center_pt, ip);
+
+    /* allocate pts array, maximum 4 verts per arb8 face */
+    face.pts = (point_t *)bu_calloc(4, sizeof(point_t), "analyze_arb8: pts");
+    /* allocate table rows, 12 rows needed for arb8 edges */
+    table.rows = (row_t *)bu_calloc(12, sizeof(row_t), "analyze_arb8: rows");
+
+    table.nrows = 0;
+    for (face.npts = 0, i = 0; i < 6; face.npts = 0, i++) {
+       int a, b, c, d; /* 4 indices to face vertices */
+
+       a = arb_faces[type][i*4+0];
+       b = arb_faces[type][i*4+1];
+       c = arb_faces[type][i*4+2];
+       d = arb_faces[type][i*4+3];
+
+       if (a == -1) {
+           table.rows[i].nfields = 0;
+           continue;
+       }
+
+       /* find plane eqn for this face */
+       if (bn_make_plane_3pnts(face.plane_eqn, arb->pt[a], arb->pt[b], 
arb->pt[c], &gedp->ged_wdbp->wdb_tol) < 0) {
+           bu_vls_printf(gedp->ged_result_str, "| %d%d%d%d |         ***NOT A 
PLANE***                                          |\n",
+                         a+1, b+1, c+1, d+1);
+           /* this row has 1 special fields */
+           table.rows[i].nfields = NOT_A_PLANE;
+           continue;
+       }
+
+       ADD_PT(face, arb->pt[a]);
+       ADD_PT(face, arb->pt[b]);
+       ADD_PT(face, arb->pt[c]);
+       ADD_PT(face, arb->pt[d]);
+
+       /* The plane equations returned by bn_make_plane_3pnts above do
+        * not necessarily point outward. Use the reference center
+        * point for the arb and reverse direction for any errant planes.
+        * This corrects the output rotation, fallback angles so that
+        * they always give the outward pointing normal vector. */
+       if (DIST_PNT_PLANE(center_pt, face.plane_eqn) > 0.0) {
+           HREVERSE(face.plane_eqn, face.plane_eqn);
+       }
+
+       snprintf(face.label, sizeof(face.label), "%d", prface[type][i]);
+
+       analyze_poly_face(gedp, &face, &(table.rows[i]));
+       tot_area += face.area;
+       table.nrows++;
+    }
+
+    /* and print it */
+    print_faces_table(gedp, &table);
+
+    /* TABLE 2 =========================================== */
+    /* analyze each edge */
+
+    /* blank line following previous table */
+    bu_vls_printf(gedp->ged_result_str, "\n");
+
+    /* set up the records for arb4's and arb6's */
+    earb = *arb; /* struct copy */
+    if (cgtype == 4) {
+       VMOVE(earb.pt[3], earb.pt[4]);
+    } else if (cgtype == 6) {
+       VMOVE(earb.pt[5], earb.pt[6]);
+    }
+
+    table.nrows = 0;
+    for (i = 0; i < 12; i++) {
+       analyze_edge(gedp, i, &earb, type, &(table.rows[i]));
+       if (nedge[type][i*2] == -1) {
+           break;
+       }
+       table.nrows += 1;
+    }
+
+    print_edges_table(gedp, &table);
+
+    /* TABLE 3 =========================================== */
+    /* find the volume - break arb8 into 6 arb4s */
+
+    if (OBJ[ID_ARB8].ft_volume)
+       OBJ[ID_ARB8].ft_volume(&tot_vol, ip);
+
+    print_volume_table(gedp,
+                      tot_vol
+                      * gedp->ged_wdbp->dbip->dbi_base2local
+                      * gedp->ged_wdbp->dbip->dbi_base2local
+                      * gedp->ged_wdbp->dbip->dbi_base2local,
+                      tot_area
+                      * gedp->ged_wdbp->dbip->dbi_base2local
+                      * gedp->ged_wdbp->dbip->dbi_base2local,
+                      tot_vol/GALLONS_TO_MM3
+       );
+
+    bu_free((char *)face.pts, "analyze_arb8: pts");
+    bu_free((char *)table.rows, "analyze_arb8: rows");
+}
+// Local Variables:
+// tab-width: 8
+// mode: C++
+// c-basic-offset: 4
+// indent-tabs-mode: t
+// c-file-style: "stroustrup"
+// End:
+// ex: shiftwidth=4 tabstop=8

Copied: brlcad/trunk/src/libged/analyze/arbn.cpp (from rev 77221, 
brlcad/branches/analyze_cmd/src/libged/analyze/arbn.cpp)
===================================================================
--- brlcad/trunk/src/libged/analyze/arbn.cpp                            (rev 0)
+++ brlcad/trunk/src/libged/analyze/arbn.cpp    2020-09-25 13:41:24 UTC (rev 
77222)
@@ -0,0 +1,112 @@
+/*                        A R B N . C P P
+ * BRL-CAD
+ *
+ * Copyright (c) 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 arbn.cpp
+ *
+ * Brief description
+ *
+ */
+
+#include "common.h"
+
+#include "vmath.h"
+
+#include "rt/geom.h"
+#include "ged/defines.h"
+#include "../ged_private.h"
+#include "./ged_analyze.h"
+
+void
+analyze_arbn(struct ged *gedp, const struct rt_db_internal *ip)
+{
+    size_t i;
+    fastf_t tot_vol = 0.0, tot_area = 0.0;
+    table_t table;
+    struct poly_face *faces;
+    struct bu_vls tmpstr = BU_VLS_INIT_ZERO;
+    struct rt_arbn_internal *aip = (struct rt_arbn_internal *)ip->idb_ptr;
+    size_t *npts = (size_t *)bu_calloc(aip->neqn, sizeof(size_t), 
"analyze_arbn: npts");
+    point_t **tmp_pts = (point_t **)bu_calloc(aip->neqn, sizeof(point_t *), 
"analyze_arbn: tmp_pts");
+    plane_t *eqs= (plane_t *)bu_calloc(aip->neqn, sizeof(plane_t), 
"analyze_arbn: eqs");
+
+    /* allocate array of face structs */
+    faces = (struct poly_face *)bu_calloc(aip->neqn, sizeof(struct poly_face), 
"analyze_arbn: faces");
+    for (i = 0; i < aip->neqn; i++) {
+       HMOVE(faces[i].plane_eqn, aip->eqn[i]);
+       VUNITIZE(faces[i].plane_eqn);
+       /* allocate array of pt structs, max number of verts per faces = (# of 
faces) - 1 */
+       faces[i].pts = (point_t *)bu_calloc(aip->neqn - 1, sizeof(point_t), 
"analyze_arbn: pts");
+       tmp_pts[i] = faces[i].pts;
+       HMOVE(eqs[i], faces[i].plane_eqn);
+    }
+    /* allocate table rows, 1 row per plane eqn */
+    table.rows = (row_t *)bu_calloc(aip->neqn, sizeof(row_t), "analyze_arbn: 
rows");
+    table.nrows = aip->neqn;
+
+    bg_3d_polygon_make_pnts_planes(npts, tmp_pts, aip->neqn, (const plane_t 
*)eqs);
+
+    for (i = 0; i < aip->neqn; i++) {
+       vect_t tmp;
+       bu_vls_sprintf(&tmpstr, "%4zu", i);
+       snprintf(faces[i].label, sizeof(faces[i].label), "%s", 
bu_vls_addr(&tmpstr));
+
+       faces[i].npts = npts[i];
+
+       /* calculate surface area */
+       analyze_poly_face(gedp, &faces[i], &table.rows[i]);
+       tot_area += faces[i].area;
+
+       /* calculate volume */
+       VSCALE(tmp, faces[i].plane_eqn, faces[i].area);
+       tot_vol += VDOT(faces[i].pts[0], tmp);
+    }
+    tot_vol /= 3.0;
+
+    print_faces_table(gedp, &table);
+    print_volume_table(gedp,
+                      tot_vol
+                      * gedp->ged_wdbp->dbip->dbi_base2local
+                      * gedp->ged_wdbp->dbip->dbi_base2local
+                      * gedp->ged_wdbp->dbip->dbi_base2local,
+                      tot_area
+                      * gedp->ged_wdbp->dbip->dbi_base2local
+                      * gedp->ged_wdbp->dbip->dbi_base2local,
+                      tot_vol/GALLONS_TO_MM3
+       );
+
+    for (i = 0; i < aip->neqn; i++) {
+       bu_free((char *)faces[i].pts, "analyze_arbn: pts");
+    }
+    bu_free((char *)faces, "analyze_arbn: faces");
+    bu_free((char *)table.rows, "analyze_arbn: rows");
+    bu_free((char *)tmp_pts, "analyze_arbn: tmp_pts");
+    bu_free((char *)npts, "analyze_arbn: npts");
+    bu_free((char *)eqs, "analyze_arbn: eqs");
+    bu_vls_free(&tmpstr);
+}
+
+
+// Local Variables:
+// tab-width: 8
+// mode: C++
+// c-basic-offset: 4
+// indent-tabs-mode: t
+// c-file-style: "stroustrup"
+// End:
+// ex: shiftwidth=4 tabstop=8

Copied: brlcad/trunk/src/libged/analyze/ars.cpp (from rev 77221, 
brlcad/branches/analyze_cmd/src/libged/analyze/ars.cpp)
===================================================================
--- brlcad/trunk/src/libged/analyze/ars.cpp                             (rev 0)
+++ brlcad/trunk/src/libged/analyze/ars.cpp     2020-09-25 13:41:24 UTC (rev 
77222)
@@ -0,0 +1,138 @@
+/*                        A R S . C P P
+ * BRL-CAD
+ *
+ * Copyright (c) 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 ars.cpp
+ *
+ * Brief description
+ *
+ */
+
+#include "common.h"
+
+#include "vmath.h"
+
+#include "rt/geom.h"
+#include "ged/defines.h"
+#include "../ged_private.h"
+#include "./ged_analyze.h"
+
+#define ARS_PT(ii, jj) (&arip->curves[i+(ii)][(j+(jj))*ELEMENTS_PER_VECT])
+
+void
+analyze_ars(struct ged *gedp, const struct rt_db_internal *ip)
+{
+    size_t i, j, k;
+    size_t nfaces = 0;
+    fastf_t tot_area = 0.0, tot_vol = 0.0;
+    table_t table;
+    plane_t old_plane = HINIT_ZERO;
+    struct bu_vls tmpstr = BU_VLS_INIT_ZERO;
+    struct poly_face face = POLY_FACE_INIT_ZERO;
+    struct rt_ars_internal *arip = (struct rt_ars_internal *)ip->idb_ptr;
+    RT_ARS_CK_MAGIC(arip);
+
+    /* allocate pts array, max 3 pts per triangular face */
+    face.pts = (point_t *)bu_calloc(3, sizeof(point_t), "analyze_ars: pts");
+    /* allocate table rows, probably overestimating the number of rows needed 
*/
+    table.rows = (row_t *)bu_calloc((arip->ncurves - 1) * 2 * 
arip->pts_per_curve, sizeof(row_t), "analyze_ars: rows");
+
+    k = arip->pts_per_curve - 2;
+    for (i = 0; i < arip->ncurves - 1; i++) {
+       int double_ended = k != 1 && 
VEQUAL(&arip->curves[i][ELEMENTS_PER_VECT], &arip->curves[i][k * 
ELEMENTS_PER_VECT]);
+
+       for (j = 0; j < arip->pts_per_curve; j++) {
+           vect_t tmp;
+
+           if (double_ended && i != 0 && (j == 0 || j == k || j == 
arip->pts_per_curve - 1)) continue;
+
+           /* first triangular face, make sure it's not a duplicate */
+           if (bn_make_plane_3pnts(face.plane_eqn, ARS_PT(0, 0), ARS_PT(1, 1), 
ARS_PT(0, 1), &gedp->ged_wdbp->wdb_tol) == 0
+               && !HEQUAL(old_plane, face.plane_eqn)) {
+               HMOVE(old_plane, face.plane_eqn);
+               ADD_PT(face, ARS_PT(0, 1));
+               ADD_PT(face, ARS_PT(0, 0));
+               ADD_PT(face, ARS_PT(1, 1));
+
+               bu_vls_sprintf(&tmpstr, "%zu%zu", i, j);
+               snprintf(face.label, sizeof(face.label), "%s", 
bu_vls_addr(&tmpstr));
+
+               /* surface area */
+               analyze_poly_face(gedp, &face, &(table.rows[nfaces]));
+               tot_area += face.area;
+
+               /* volume */
+               VSCALE(tmp, face.plane_eqn, face.area);
+               tot_vol += fabs(VDOT(face.pts[0], tmp));
+
+               face.npts = 0;
+               nfaces++;
+           }
+
+           /* second triangular face, make sure it's not a duplicate */
+           if (bn_make_plane_3pnts(face.plane_eqn, ARS_PT(1, 0), ARS_PT(1, 1), 
ARS_PT(0, 0), &gedp->ged_wdbp->wdb_tol) == 0
+               && !HEQUAL(old_plane, face.plane_eqn)) {
+               HMOVE(old_plane, face.plane_eqn);
+               ADD_PT(face, ARS_PT(1, 0));
+               ADD_PT(face, ARS_PT(0, 0));
+               ADD_PT(face, ARS_PT(1, 1));
+
+               bu_vls_sprintf(&tmpstr, "%zu%zu", i, j);
+               snprintf(face.label, sizeof(face.label), "%s", 
bu_vls_addr(&tmpstr));
+
+               analyze_poly_face(gedp, &face, &table.rows[nfaces]);
+               tot_area += face.area;
+
+               VSCALE(tmp, face.plane_eqn, face.area);
+               tot_vol += fabs(VDOT(face.pts[0], tmp));
+
+               face.npts = 0;
+               nfaces++;
+           }
+       }
+    }
+    tot_vol /= 3.0;
+    table.nrows = nfaces;
+
+    print_faces_table(gedp, &table);
+    print_volume_table(gedp,
+                      tot_vol
+                      * gedp->ged_wdbp->dbip->dbi_base2local
+                      * gedp->ged_wdbp->dbip->dbi_base2local
+                      * gedp->ged_wdbp->dbip->dbi_base2local,
+                      tot_area
+                      * gedp->ged_wdbp->dbip->dbi_base2local
+                      * gedp->ged_wdbp->dbip->dbi_base2local,
+                      tot_vol/GALLONS_TO_MM3
+       );
+
+    bu_free((char *)face.pts, "analyze_ars: pts");
+    bu_free((char *)table.rows, "analyze_ars: rows");
+    bu_vls_free(&tmpstr);
+}
+
+
+
+// Local Variables:
+// tab-width: 8
+// mode: C++
+// c-basic-offset: 4
+// indent-tabs-mode: t
+// c-file-style: "stroustrup"
+// End:
+// ex: shiftwidth=4 tabstop=8

Copied: brlcad/trunk/src/libged/analyze/ged_analyze.h (from rev 77221, 
brlcad/branches/analyze_cmd/src/libged/analyze/ged_analyze.h)
===================================================================
--- brlcad/trunk/src/libged/analyze/ged_analyze.h                               
(rev 0)
+++ brlcad/trunk/src/libged/analyze/ged_analyze.h       2020-09-25 13:41:24 UTC 
(rev 77222)
@@ -0,0 +1,157 @@
+/*                    G E D _ A N A L Y Z E . H
+ * BRL-CAD
+ *
+ * Copyright (c) 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 ged_cg.h
+ *
+ * Brief description
+ *
+ */
+
+#ifndef LIBGED_ANALYZE_GED_PRIVATE_H
+#define LIBGED_ANALYZE_GED_PRIVATE_H
+
+#include "common.h"
+
+#include "vmath.h"
+#include "rt/op.h"
+#include "./ged/defines.h"
+
+__BEGIN_DECLS
+
+/* Conversion factor for Gallons to cubic millimeters */
+#define GALLONS_TO_MM3 3785411.784
+#define NOT_A_PLANE -1
+
+#define FBUFSIZ 100
+#define NFIELDS 9
+
+typedef struct row_field
+{
+    int nchars;
+    char buf[FBUFSIZ];
+} field_t;
+
+typedef struct table_row
+{
+    int nfields;
+    field_t fields[NFIELDS];
+} row_t;
+
+typedef struct table
+{
+    int nrows;
+    row_t *rows;
+} table_t;
+
+#define ADD_PT(face, pt) do { VMOVE((face).pts[(face).npts], (pt)); 
(face).npts++; } while (0)
+
+/* contains information used to analyze a polygonal face */
+struct poly_face
+{
+    char label[5];
+    size_t npts;
+    point_t *pts;
+    plane_t plane_eqn;
+    fastf_t area;
+};
+
+GED_EXPORT extern void
+print_edges_table(struct ged *gedp, table_t *table);
+
+GED_EXPORT extern void
+print_faces_table(struct ged *gedp, table_t *table);
+
+GED_EXPORT extern void
+print_volume_table(
+       struct ged *gedp,
+               const fastf_t tot_vol,
+               const fastf_t tot_area,
+               const fastf_t tot_gallons
+       );
+
+#define POLY_FACE_INIT_ZERO { { 0, 0, 0, 0, 0 }, 0, NULL, HINIT_ZERO, 0.0 }
+
+GED_EXPORT extern void
+analyze_poly_face(struct ged *gedp, struct poly_face *face, row_t *row);
+
+GED_EXPORT extern void
+print_faces_table(struct ged *gedp, table_t *table);
+
+
+GED_EXPORT extern void
+analyze_arb8(struct ged *gedp, const struct rt_db_internal *ip);
+
+GED_EXPORT extern void
+analyze_arbn(struct ged *gedp, const struct rt_db_internal *ip);
+
+GED_EXPORT extern void
+analyze_ars(struct ged *gedp, const struct rt_db_internal *ip);
+
+GED_EXPORT extern void
+analyze_superell(struct ged *gedp, const struct rt_db_internal *ip);
+
+GED_EXPORT extern void
+analyze_sketch(struct ged *gedp, const struct rt_db_internal *ip);
+
+GED_EXPORT extern void
+analyze_general(struct ged *gedp, const struct rt_db_internal *ip);
+
+
+
+/**
+ * Functions for performing union/intersection/subtraction analysis operations
+ * on various type combinations.
+ *
+ * output_obj is optional.  If non-NULL, it is the name used to produce a new
+ * geometry database object with the results of the operation (if any).
+ *
+ * The trivial case would be an op_vol_vol that takes two csg solids or combs
+ * and returns a new implicit comb with them unioned (always), intersected (if
+ * there is overlap) or subtracted (if there is overlap).
+ *
+ * More interesting are cases like op_pnts_vol that will produce new pnts
+ * objects based on boolean set operation analysis.
+ */
+
+typedef long (*op_func_ptr)(const char *, struct ged *, db_op_t, const char *, 
const char *);
+
+GED_EXPORT extern long
+op_pnts_vol(
+       const char *output_obj,
+       struct ged *gedp,
+       db_op_t op,
+       const char *pnt_obj,
+       const char *vol_obj
+       );
+
+
+__END_DECLS
+
+#endif /* LIBGED_ANALYZE_GED_PRIVATE_H */
+
+/** @} */
+/*
+ * 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/analyze/op_pnts_vol.cpp (from rev 77221, 
brlcad/branches/analyze_cmd/src/libged/analyze/op_pnts_vol.cpp)
===================================================================
--- brlcad/trunk/src/libged/analyze/op_pnts_vol.cpp                             
(rev 0)
+++ brlcad/trunk/src/libged/analyze/op_pnts_vol.cpp     2020-09-25 13:41:24 UTC 
(rev 77222)
@@ -0,0 +1,427 @@
+/*                 O P _ P N T S _ V O L . C P P
+ * BRL-CAD
+ *
+ * Copyright (c) 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

@@ 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