Revision: 77305
          http://sourceforge.net/p/brlcad/code/77305
Author:   starseeker
Date:     2020-10-01 13:21:15 +0000 (Thu, 01 Oct 2020)
Log Message:
-----------
Merge from trunk - r77224 through r77302

Modified Paths:
--------------
    brlcad/branches/swrast/NEWS
    brlcad/branches/swrast/TODO
    brlcad/branches/swrast/bench/pixcmp.c
    brlcad/branches/swrast/bench/run.sh
    brlcad/branches/swrast/doc/docbook/CMakeLists.txt
    brlcad/branches/swrast/doc/legal/embedded/SPSR.txt
    brlcad/branches/swrast/doc/legal/other/CMakeLists.txt
    brlcad/branches/swrast/include/ged/commands.h
    brlcad/branches/swrast/src/conv/fast4-g.c
    brlcad/branches/swrast/src/libbrep/CMakeLists.txt
    brlcad/branches/swrast/src/libbrep/boolean.cpp
    brlcad/branches/swrast/src/libbrep/intersect.cpp
    brlcad/branches/swrast/src/libbrep/tests/CMakeLists.txt
    brlcad/branches/swrast/src/libged/analyze/util.cpp
    brlcad/branches/swrast/src/libged/brep/CMakeLists.txt
    brlcad/branches/swrast/src/libged/brep/brep.cpp
    brlcad/branches/swrast/src/libged/brep/ged_brep.h
    brlcad/branches/swrast/src/libged/exec_mapping.cpp
    brlcad/branches/swrast/src/libtclcad/commands.c
    brlcad/branches/swrast/src/other/libspsr/Src/PointStream.h
    brlcad/branches/swrast/src/other/libspsr/Src/PointStream.inl
    brlcad/branches/swrast/src/other/libspsr/Src/PoissonRecon.cpp
    brlcad/branches/swrast/src/other/libspsr/Src/SPSR.cpp
    brlcad/branches/swrast/src/other/libspsr/Src/SurfaceTrimmer.cpp
    brlcad/branches/swrast/src/other/libspsr.dist
    brlcad/branches/swrast/src/tclscripts/lib/Ged.tcl

Added Paths:
-----------
    brlcad/branches/swrast/doc/docbook/devguides/
    brlcad/branches/swrast/src/libbrep/brep_defines.h
    brlcad/branches/swrast/src/libbrep/debug_plot.cpp
    brlcad/branches/swrast/src/libbrep/debug_plot.h
    brlcad/branches/swrast/src/libbrep/tests/ppx.cpp
    brlcad/branches/swrast/src/libged/brep/dplot.c
    brlcad/branches/swrast/src/libged/brep/dplot_parser.lemon
    brlcad/branches/swrast/src/libged/brep/dplot_reader.c
    brlcad/branches/swrast/src/libged/brep/dplot_reader.h
    brlcad/branches/swrast/src/libged/brep/dplot_scanner.perplex
    brlcad/branches/swrast/src/other/libspsr/Src/PlyVertexMini.h

Removed Paths:
-------------
    brlcad/branches/swrast/doc/legal/other/SPSR.txt
    brlcad/branches/swrast/src/other/libspsr/Src/Ply.h
    brlcad/branches/swrast/src/other/libspsr/Src/PlyFile.cpp

Property Changed:
----------------
    brlcad/branches/swrast/
    brlcad/branches/swrast/NEWS
    brlcad/branches/swrast/bench/
    brlcad/branches/swrast/doc/
    brlcad/branches/swrast/include/
    brlcad/branches/swrast/src/other/
    brlcad/branches/swrast/src/other/libspsr/

Index: brlcad/branches/swrast
===================================================================
--- brlcad/branches/swrast      2020-10-01 13:18:29 UTC (rev 77304)
+++ brlcad/branches/swrast      2020-10-01 13:21:15 UTC (rev 77305)

Property changes on: brlcad/branches/swrast
___________________________________________________________________
Modified: svn:mergeinfo
## -1,7 +1,7 ##
 
/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/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,77226-77229
 /brlcad/branches/bullet:62518
 /brlcad/branches/cmake:43219
 /brlcad/branches/dm-fb-merge:75426-76198
## -11,4 +11,4 ##
 /brlcad/branches/osg:62110-62113
 /brlcad/branches/prep-cache:68236-68933
 /brlcad/branches/tcltk86:68300-75257
-/brlcad/trunk:76973-77223
\ No newline at end of property
+/brlcad/trunk:76973-77302
\ No newline at end of property
Modified: brlcad/branches/swrast/NEWS
===================================================================
--- brlcad/branches/swrast/NEWS 2020-10-01 13:18:29 UTC (rev 77304)
+++ brlcad/branches/swrast/NEWS 2020-10-01 13:21:15 UTC (rev 77305)
@@ -13,6 +13,8 @@
 --- 2020-xx-xx  Release 7.32.2                                     ---
 ----------------------------------------------------------------------
 
+* applied shorter default title in fast4-g conversions - Cliff Yapp
+* improved pixcmp support for subset image comparisons - Sean Morrison
 * 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


Property changes on: brlcad/branches/swrast/NEWS
___________________________________________________________________
Modified: svn:mergeinfo
## -9,4 +9,4 ##
 /brlcad/branches/osg/NEWS:62110-62113
 /brlcad/branches/prep-cache/NEWS:68236-68933
 /brlcad/branches/tcltk86/NEWS:68300-75257
-/brlcad/trunk/NEWS:76973-77223
\ No newline at end of property
+/brlcad/trunk/NEWS:76973-77302
\ No newline at end of property
Modified: brlcad/branches/swrast/TODO
===================================================================
--- brlcad/branches/swrast/TODO 2020-10-01 13:18:29 UTC (rev 77304)
+++ brlcad/branches/swrast/TODO 2020-10-01 13:21:15 UTC (rev 77305)
@@ -18,12 +18,9 @@
 THESE TASKS SHOULD HAPPEN BEFORE THE NEXT RELEASE
 -------------------------------------------------
 
-* Release Testing, final commit reviews
+* Update libgcv plugin installation location - now that its
+  available, they belong with the other plugins in libexec
 
-
-THESE TASKS SHOULD HAPPEN WITHIN TWO RELEASE ITERATIONS
--------------------------------------------------------
-
 * The following will be removed from MGED's menu bar (unused
   features, can be accomplished another way, or will be exposed
   only as lower level settings when there are sensible defaults
@@ -169,6 +166,11 @@
   allocation, not just do some work
 
 
+THESE TASKS SHOULD HAPPEN WITHIN TWO RELEASE ITERATIONS
+-------------------------------------------------------
+
+
+
 THESE ARE UNSCHEDULED BACKLOG TASKS
 -----------------------------------
 

Index: brlcad/branches/swrast/bench
===================================================================
--- brlcad/branches/swrast/bench        2020-10-01 13:18:29 UTC (rev 77304)
+++ brlcad/branches/swrast/bench        2020-10-01 13:21:15 UTC (rev 77305)

Property changes on: brlcad/branches/swrast/bench
___________________________________________________________________
Modified: svn:mergeinfo
## -8,4 +8,4 ##
 /brlcad/branches/osg/bench:62110-62113
 /brlcad/branches/prep-cache/bench:68236-68933
 /brlcad/branches/tcltk86/bench:68300-75257
-/brlcad/trunk/bench:77084-77223
\ No newline at end of property
+/brlcad/trunk/bench:77084-77302
\ No newline at end of property
Modified: brlcad/branches/swrast/bench/pixcmp.c
===================================================================
--- brlcad/branches/swrast/bench/pixcmp.c       2020-10-01 13:18:29 UTC (rev 
77304)
+++ brlcad/branches/swrast/bench/pixcmp.c       2020-10-01 13:21:15 UTC (rev 
77305)
@@ -121,7 +121,8 @@
 {
     FILE *f1 = NULL;
     FILE *f2 = NULL;
-    struct stat sf1, sf2;
+    struct stat sf1 = {0};
+    struct stat sf2 = {0};
 
     size_t matching = 0;
     size_t off1 = 0;
@@ -190,6 +191,10 @@
     }
 
     /* printf("Skip from FILE1: %ld and from FILE2: %ld\n", f1_skip, f2_skip); 
*/
+    if (!print_bytes) {
+       f1_skip *= 3;
+       f2_skip *= 3;
+    }
 
     if (BU_STR_EQUAL(argv[0], "-")) {
        f1 = stdin;
@@ -204,18 +209,25 @@
        exit(FILE_ERROR);
     }
 
-    stat(argv[0], &sf1);
-    stat(argv[1], &sf2);
+    fstat(fileno(f1), &sf1);
+    fstat(fileno(f2), &sf2);
 
-    if (sf1.st_size != sf2.st_size) {
-       bu_exit(FILE_ERROR, "Different file sizes found: %s(%d) and %s(%d).  
Cannot perform pixcmp.\n", argv[0], (int)sf1.st_size, argv[1], 
(int)sf2.st_size);
+    if ((sf1.st_size - f1_skip) != (sf2.st_size - f2_skip)) {
+       bu_log("WARNING: Different image sizes detected\n");
+       if (print_bytes) {
+           bu_log("\t%s: %7zu bytes (%8zu bytes, skipping %7zu)\n",
+                  argv[0], sf1.st_size - f1_skip, sf1.st_size, f1_skip);
+           bu_log("\t%s: %7zu bytes (%8zu bytes, skipping %7zu)\n",
+                  argv[1], sf2.st_size - f2_skip, sf2.st_size, f2_skip);
+       } else {
+           bu_log("\t%s: %7zu pixels (%8zu bytes, skipping %7zu)\n",
+                  argv[0], (sf1.st_size - f1_skip)/3, sf1.st_size, f1_skip);
+           bu_log("\t%s: %7zu pixels (%8zu bytes, skipping %7zu)\n",
+                  argv[1], (sf2.st_size - f2_skip)/3, sf2.st_size, f2_skip);
+       }
+       bu_exit(1, "ERROR: Cannot pixcmp due to different images sizes 
(unimplemented).\n");
     }
 
-    if (!print_bytes) {
-       f1_skip *= 3;
-       f2_skip *= 3;
-    }
-
     /* skip requested pixels/bytes in FILE1 */
     if (f1_skip && fseek(f1, f1_skip, SEEK_SET)) {
        bu_log("ERROR: Unable to seek %zd %s%s in FILE1\n",

Modified: brlcad/branches/swrast/bench/run.sh
===================================================================
--- brlcad/branches/swrast/bench/run.sh 2020-10-01 13:18:29 UTC (rev 77304)
+++ brlcad/branches/swrast/bench/run.sh 2020-10-01 13:21:15 UTC (rev 77305)
@@ -613,18 +613,14 @@
 fi
 
 # sanity check: make sure $CMP runs
-echo "" > tmp1
-echo "" > tmp2
-out=`echo "" | eval \"$CMP\" tmp1 tmp2 2>&1`
+out=`echo "" | eval \"$CMP\" - - 2>&1`
 ret=$?
 if test ! "x${ret}" = "x0" ; then
     $ECHO
     $ECHO "ERROR:  CMP does not seem to work as expected"
     $ECHO "        (output was [$out])"
-    rm -f tmp1 tmp2
     exit 2
 fi
-rm -f tmp1 tmp2
 
 # sanity check: make sure $ELP runs
 out=`eval \"$ELP\" 0 2>&1`

Index: brlcad/branches/swrast/doc
===================================================================
--- brlcad/branches/swrast/doc  2020-10-01 13:18:29 UTC (rev 77304)
+++ brlcad/branches/swrast/doc  2020-10-01 13:21:15 UTC (rev 77305)

Property changes on: brlcad/branches/swrast/doc
___________________________________________________________________
Modified: svn:mergeinfo
## -1,6 +1,6 ##
 
/brlcad/branches/RELEASE/doc:57439,57447-57860,69901-69913,71917-72242,72525-72534,72826-72858,74376-74454,74964-75140,77145-77155
 /brlcad/branches/bioh/doc:75894-75986,76088-76153,76354-76506
-/brlcad/branches/brep-debug/doc: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,69206,69289,69346,69460-69461,69582-69583,69719-69721,69857-69859,69927
+/brlcad/branches/brep-debug/doc: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,69206,69289,69346,69460-69461,69582-69583,69719-69721,69857-69859,69927,77226-77229
 /brlcad/branches/bullet/doc:62518
 /brlcad/branches/cmake/doc:43219
 /brlcad/branches/dm-fb-merge/doc:75426-76198
## -9,4 +9,4 ##
 /brlcad/branches/osg/doc:62110-62113
 /brlcad/branches/prep-cache/doc:68236-68933
 /brlcad/branches/tcltk86/doc:68300-75257
-/brlcad/trunk/doc:76973-77223
\ No newline at end of property
+/brlcad/trunk/doc:76973-77302
\ No newline at end of property
Modified: brlcad/branches/swrast/doc/docbook/CMakeLists.txt
===================================================================
--- brlcad/branches/swrast/doc/docbook/CMakeLists.txt   2020-10-01 13:18:29 UTC 
(rev 77304)
+++ brlcad/branches/swrast/doc/docbook/CMakeLists.txt   2020-10-01 13:21:15 UTC 
(rev 77305)
@@ -36,6 +36,7 @@
 
 add_subdirectory(articles)
 add_subdirectory(books)
+add_subdirectory(devguides)
 add_subdirectory(lessons)
 add_subdirectory(presentations)
 add_subdirectory(specifications)

Modified: brlcad/branches/swrast/doc/legal/embedded/SPSR.txt
===================================================================
--- brlcad/branches/swrast/doc/legal/embedded/SPSR.txt  2020-10-01 13:18:29 UTC 
(rev 77304)
+++ brlcad/branches/swrast/doc/legal/embedded/SPSR.txt  2020-10-01 13:21:15 UTC 
(rev 77305)
@@ -48,6 +48,42 @@
 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGE.
 
+
+Uses PlyVertexMini.h from geogram:
+https://github.com/alicevision/geogram/blob/master/src/lib/geogram/third_party/PoissonRecon/PlyVertexMini.h
+
+Geogram is licensed under the 3-clauses BSD License (also called "Revised
+BSD License", "New BSD License", or "Modified BSD License"):
+
+Copyright (c) 2012-2014, Bruno Levy All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer. Redistributions in binary
+form must reproduce the above copyright notice, this list of conditions and
+the following disclaimer in the documentation and/or other materials
+provided with the distribution. Neither the name of the ALICE Project-Team
+nor the names of its contributors may be used to endorse or promote products
+derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+If you modify this software, you should include a notice giving the name of
+the person performing the modification, the date of modification, and the
+reason for such modification.
+
 file:/include/bg/spsr.h
 file:/src/other/libspsr/Src/Allocator.h
 file:/src/other/libspsr/Src/Array.h
@@ -80,8 +116,7 @@
 file:/src/other/libspsr/Src/Octree.inl
 file:/src/other/libspsr/Src/PPolynomial.h
 file:/src/other/libspsr/Src/PPolynomial.inl
-file:/src/other/libspsr/Src/Ply.h
-file:/src/other/libspsr/Src/PlyFile.cpp
+file:/src/other/libspsr/Src/PlyVertexMini.h
 file:/src/other/libspsr/Src/PointStream.h
 file:/src/other/libspsr/Src/PointStream.inl
 file:/src/other/libspsr/Src/PoissonRecon.cpp

Modified: brlcad/branches/swrast/doc/legal/other/CMakeLists.txt
===================================================================
--- brlcad/branches/swrast/doc/legal/other/CMakeLists.txt       2020-10-01 
13:18:29 UTC (rev 77304)
+++ brlcad/branches/swrast/doc/legal/other/CMakeLists.txt       2020-10-01 
13:21:15 UTC (rev 77305)
@@ -16,7 +16,6 @@
   openscenegraph.txt
   poly2tri.txt
   proj-4.txt
-  SPSR.txt
   stepcode.txt
   tcl.txt
   tkhtml.txt

Deleted: brlcad/branches/swrast/doc/legal/other/SPSR.txt
===================================================================
--- brlcad/branches/swrast/doc/legal/other/SPSR.txt     2020-10-01 13:18:29 UTC 
(rev 77304)
+++ brlcad/branches/swrast/doc/legal/other/SPSR.txt     2020-10-01 13:21:15 UTC 
(rev 77305)
@@ -1,25 +0,0 @@
-Copyright (c) 2006, Michael Kazhdan and Matthew Bolitho
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without 
modification,
-are permitted provided that the following conditions are met:
-
-Redistributions of source code must retain the above copyright notice, this 
list of
-conditions and the following disclaimer. Redistributions in binary form must 
reproduce
-the above copyright notice, this list of conditions and the following 
disclaimer
-in the documentation and/or other materials provided with the distribution.
-
-Neither the name of the Johns Hopkins University nor the names of its 
contributors
-may be used to endorse or promote products derived from this software without 
specific
-prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
AND ANY
-EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED 
WARRANTIES
-OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 
EVENT
-SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
LIMITED
-TO, PROCUREMENT OF SUBSTITUTE  GOODS OR SERVICES; LOSS OF USE, DATA, OR 
PROFITS; OR
-BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 
IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
ARISING IN
-ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
SUCH
-DAMAGE.

Index: brlcad/branches/swrast/include
===================================================================
--- brlcad/branches/swrast/include      2020-10-01 13:18:29 UTC (rev 77304)
+++ brlcad/branches/swrast/include      2020-10-01 13:21:15 UTC (rev 77305)

Property changes on: brlcad/branches/swrast/include
___________________________________________________________________
Modified: svn:mergeinfo
## -1,6 +1,6 ##
 
/brlcad/branches/RELEASE/include:57439,57447-57860,69901-69913,71915-72242,72525-72534,72826-72858,74376-74454,74964-75140,75372-75681,76726-76768
 
/brlcad/branches/bioh/include:75720-75736,75740-75742,75860-75891,75894-75986,76088-76153,76354-76506
-/brlcad/branches/brep-debug/include: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,69206,69289,69346,69460-69461,69582-69583,69719-69721,69857-69859,69927
+/brlcad/branches/brep-debug/include: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,69206,69289,69346,69460-69461,69582-69583,69719-69721,69857-69859,69927,77226-77229
 /brlcad/branches/bullet/include:62518
 /brlcad/branches/cmake/include:43219
 /brlcad/branches/dm-fb-merge/include:75426-76198
## -10,4 +10,4 ##
 /brlcad/branches/osg/include:62110-62113
 /brlcad/branches/prep-cache/include:68236-68933
 /brlcad/branches/tcltk86/include:68300-75257
-/brlcad/trunk/include:76973-77223
\ No newline at end of property
+/brlcad/trunk/include:76973-77302
\ No newline at end of property
Modified: brlcad/branches/swrast/include/ged/commands.h
===================================================================
--- brlcad/branches/swrast/include/ged/commands.h       2020-10-01 13:18:29 UTC 
(rev 77304)
+++ brlcad/branches/swrast/include/ged/commands.h       2020-10-01 13:21:15 UTC 
(rev 77305)
@@ -860,6 +860,11 @@
  */
 GED_EXPORT extern int ged_stat(struct ged *gedp, int argc, const char *argv[]);
 
+
+
+/* Debugging command for brep plotting */
+GED_EXPORT extern int ged_dplot(struct ged *gedp, int argc, const char 
*argv[]);
+
 /** @} */
 
 

Modified: brlcad/branches/swrast/src/conv/fast4-g.c
===================================================================
--- brlcad/branches/swrast/src/conv/fast4-g.c   2020-10-01 13:18:29 UTC (rev 
77304)
+++ brlcad/branches/swrast/src/conv/fast4-g.c   2020-10-01 13:21:15 UTC (rev 
77305)
@@ -38,6 +38,7 @@
 #include "bu/app.h"
 #include "bu/debug.h"
 #include "bu/getopt.h"
+#include "bu/path.h"
 #include "rt/db4.h"
 #include "vmath.h"
 #include "nmg.h"
@@ -2839,8 +2840,12 @@
     rewind(fpin);
 
     /* Make an ID record if no vehicle card was found */
-    if (!vehicle[0])
-       mk_id_units(fpout, argv[bu_optind], "in");
+    if (!vehicle[0]) {
+       struct bu_vls fname = BU_VLS_INIT_ZERO;
+       bu_path_component(&fname, argv[bu_optind], BU_PATH_BASENAME);
+       mk_id_units(fpout, bu_vls_cstr(&fname), "in");
+       bu_vls_free(&fname);
+    }
 
     if (!quiet)
        bu_log("Building components....\n");

Modified: brlcad/branches/swrast/src/libbrep/CMakeLists.txt
===================================================================
--- brlcad/branches/swrast/src/libbrep/CMakeLists.txt   2020-10-01 13:18:29 UTC 
(rev 77304)
+++ brlcad/branches/swrast/src/libbrep/CMakeLists.txt   2020-10-01 13:21:15 UTC 
(rev 77305)
@@ -33,6 +33,7 @@
   cdt/mesh.cpp
   cdt/ovlps_simple.cpp
   cdt/tri_isect.cpp
+  debug_plot.cpp
   intersect.cpp
   tools/tools.cpp
   opennurbs_ext.cpp

Modified: brlcad/branches/swrast/src/libbrep/boolean.cpp
===================================================================
--- brlcad/branches/swrast/src/libbrep/boolean.cpp      2020-10-01 13:18:29 UTC 
(rev 77304)
+++ brlcad/branches/swrast/src/libbrep/boolean.cpp      2020-10-01 13:21:15 UTC 
(rev 77305)
@@ -21,6 +21,8 @@
  *
  * Evaluate NURBS booleans (union, intersection and difference).
  *
+ * Additional documentation can be found in the "NURBS Boolean Evaluation
+ * Development Guide" docbook article (bool_eval_development.html).
  */
 
 #include "common.h"
@@ -31,6 +33,7 @@
 #include <queue>
 #include <set>
 #include <map>
+#include <sstream>
 
 #include "bio.h"
 
@@ -43,21 +46,16 @@
 #include "brep/ray.h"
 #include "brep/util.h"
 
+#include "debug_plot.h"
 #include "brep_except.h"
+#include "brep_defines.h"
 
+DebugPlot *dplot = NULL;
 
 // Whether to output the debug messages about b-rep booleans.
 #define DEBUG_BREP_BOOLEAN 0
 
-// tol value used in ON_Intersect()s. We use a smaller tolerance than the
-// default one 0.001.
-#define INTERSECTION_TOL 1e-4
 
-// tol value used in ON_3dVector::IsParallelTo(). We use a smaller tolerance
-// than the default one ON_PI/180.
-#define ANGLE_TOL ON_PI/1800.0
-
-
 struct IntersectPoint {
     ON_3dPoint m_pt;   // 3D intersection point
     double m_seg_t;    // param on the loop curve
@@ -104,7 +102,7 @@
 };
 
 
-HIDDEN void
+void
 append_to_polycurve(ON_Curve *curve, ON_PolyCurve &polycurve);
 // We link the SSICurves that share an endpoint, and form this new structure,
 // which has many similar behaviors as ON_Curve, e.g. PointAt(), Reverse().
@@ -344,7 +342,7 @@
 }
 
 
-HIDDEN void
+void
 append_to_polycurve(ON_Curve *curve, ON_PolyCurve &polycurve)
 {
     // use this function rather than ON_PolyCurve::Append() to avoid
@@ -3445,7 +3443,7 @@
        std::set<int> *unused = i < face_count1 ? &unused1 : &unused2;
        std::set<int> *intact = i < face_count1 ? &finalform1 : &finalform2;
        int curr_index = i < face_count1 ? i : i - face_count1;
-       if (face.BoundingBox().MinimumDistanceTo(brep->BoundingBox()) > 
ON_ZERO_TOLERANCE) {
+       if (face.BoundingBox().MinimumDistanceTo(brep->BoundingBox()) > 
INTERSECTION_TOL) {
            switch (operation) {
                case BOOLEAN_UNION:
                    intact->insert(curr_index);
@@ -3483,8 +3481,8 @@
                if (unused2.find(j) == unused2.end() &&  finalform2.find(j) == 
finalform2.end()) {
                    // If the two faces don't interact according to their 
bounding boxes,
                    // they won't be a source of events - otherwise, they must 
be checked.
-                   fastf_t disjoint = 
brep1->m_F[i].BoundingBox().MinimumDistanceTo(brep2->m_F[j].BoundingBox());
-                   if (!(disjoint > ON_ZERO_TOLERANCE)) {
+                   fastf_t face_dist = 
brep1->m_F[i].BoundingBox().MinimumDistanceTo(brep2->m_F[j].BoundingBox());
+                   if (face_dist <= INTERSECTION_TOL) {
                        intersection_candidates.insert(std::pair<int, int>(i, 
j));
                    }
                }
@@ -3563,6 +3561,10 @@
                if (results <= 0) {
                    continue;
                }
+
+               dplot->SSX(events, brep1, brep1->m_F[i].m_si, brep2, 
brep2->m_F[j].m_si);
+               dplot->WriteLog();
+
                ON_SimpleArray<ON_Curve *> face1_curves, face2_curves;
                for (int k = 0; k < events.Count(); k++) {
                    if (events[k].m_type == ON_SSX_EVENT::ssx_tangent ||
@@ -3590,6 +3592,8 @@
                        }
                    }
                }
+               dplot->ClippedFaceCurves(surf1, surf2, face1_curves, 
face2_curves);
+               dplot->WriteLog();
 
                if (DEBUG_BREP_BOOLEAN) {
                    // Look for coplanar faces
@@ -3603,7 +3607,6 @@
                }
 
            }
-
        }
     }
 
@@ -3694,6 +3697,7 @@
            }
 
            splitted[j]->m_rev = false;
+           splitted[j]->m_belong_to_final = TrimmedFace::NOT_BELONG;
            switch (face_location) {
                case INSIDE_BREP:
                    if (operation == BOOLEAN_INTERSECT ||
@@ -3798,6 +3802,8 @@
     for (int i = 0; i < original_faces.Count(); i++) {
        TrimmedFace *first = original_faces[i];
        ON_ClassArray<LinkedCurve> linked_curves = link_curves(curves_array[i]);
+       dplot->LinkedCurves(first->m_face->SurfaceOf(), linked_curves);
+       dplot->WriteLog();
 
        ON_SimpleArray<TrimmedFace *> splitted = split_trimmed_face(first, 
linked_curves);
        trimmed_faces.Append(splitted);
@@ -3826,6 +3832,9 @@
 
     categorize_trimmed_faces(trimmed_faces, brep1, brep2, surf_tree1, 
surf_tree2, operation);
 
+    dplot->SplitFaces(trimmed_faces);
+    dplot->WriteLog();
+
     for (int i = 0; i < surf_tree1.Count(); i++) {
        delete surf_tree1[i];
     }
@@ -3914,6 +3923,14 @@
 int
 ON_Boolean(ON_Brep *evaluated_brep, const ON_Brep *brep1, const ON_Brep 
*brep2, op_type operation)
 {
+    static int calls = 0;
+    ++calls;
+    std::ostringstream prefix;
+    prefix << "bool" << calls;
+    dplot = new DebugPlot(prefix.str().c_str());
+    dplot->Surfaces(brep1, brep2);
+    dplot->WriteLog();
+
     ON_ClassArray<ON_SimpleArray<TrimmedFace *> > trimmed_faces;
     try {
        /* Deal with the trivial cases up front */
@@ -3934,14 +3951,17 @@
            }
            evaluated_brep->ShrinkSurfaces();
            evaluated_brep->Compact();
+           dplot->WriteLog();
            return 0;
        }
        trimmed_faces = get_evaluated_faces(brep1, brep2, operation);
     } catch (InvalidBooleanOperation &e) {
        bu_log("%s", e.what());
+       dplot->WriteLog();
        return -1;
     } catch (GeometryGenerationError &e) {
        bu_log("%s", e.what());
+       dplot->WriteLog();
        return -1;
     }
 
@@ -3999,6 +4019,10 @@
        bu_log("%s", ON_String(ws).Array());
     }
 
+    dplot->WriteLog();
+    delete dplot;
+    dplot = NULL;
+
     return 0;
 }
 

Copied: brlcad/branches/swrast/src/libbrep/brep_defines.h (from rev 77302, 
brlcad/trunk/src/libbrep/brep_defines.h)
===================================================================
--- brlcad/branches/swrast/src/libbrep/brep_defines.h                           
(rev 0)
+++ brlcad/branches/swrast/src/libbrep/brep_defines.h   2020-10-01 13:21:15 UTC 
(rev 77305)
@@ -0,0 +1,71 @@
+/*                  B R E P _ D E F I N E S . H
+ * BRL-CAD
+ *
+ * Copyright (c) 2014 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 brep_defines.h
+ *
+ * Private defines.
+ *
+ */
+
+// The maximal depth for subdivision - trade-off between accuracy and
+// performance.
+#define NR_MAX_DEPTH 8
+#define MAX_PCI_DEPTH NR_MAX_DEPTH
+#define MAX_PSI_DEPTH NR_MAX_DEPTH
+#define MAX_CCI_DEPTH NR_MAX_DEPTH
+#define MAX_CSI_DEPTH NR_MAX_DEPTH
+#define MAX_SSI_DEPTH NR_MAX_DEPTH
+
+// Used to prevent an infinite loop in the unlikely event that we
+// can't provide a good starting point for the Newton-Raphson
+// Iteration.
+#define NR_MAX_ITERATIONS 100
+#define PCI_MAX_ITERATIONS NR_MAX_ITERATIONS
+#define PSI_MAX_ITERATIONS NR_MAX_ITERATIONS
+#define CCI_MAX_ITERATIONS NR_MAX_ITERATIONS
+#define CSI_MAX_ITERATIONS NR_MAX_ITERATIONS
+#define SSI_MAX_ITERATIONS NR_MAX_ITERATIONS
+
+// We make the default tolerance for PSI the same as that of curve and
+// surface intersections defined by openNURBS (see opennurbs_curve.h
+// and opennurbs_surface.h).
+#define NR_DEFAULT_TOLERANCE 0.001
+#define PCI_DEFAULT_TOLERANCE NR_DEFAULT_TOLERANCE
+#define PSI_DEFAULT_TOLERANCE NR_DEFAULT_TOLERANCE
+#define CCI_DEFAULT_TOLERANCE NR_DEFAULT_TOLERANCE
+#define CSI_DEFAULT_TOLERANCE NR_DEFAULT_TOLERANCE
+#define SSI_DEFAULT_TOLERANCE NR_DEFAULT_TOLERANCE
+
+// tol value used in ON_Intersect()s. We use a smaller tolerance than the
+// default one 0.001.
+#define INTERSECTION_TOL 1e-4
+
+// tol value used in ON_3dVector::IsParallelTo(). We use a smaller tolerance
+// than the default one ON_PI/180.
+#define ANGLE_TOL ON_PI/1800.0
+
+/*
+ * Local Variables:
+ * tab-width: 8
+ * mode: C
+ * indent-tabs-mode: t
+ * c-file-style: "stroustrup"
+ * End:
+ * ex: shiftwidth=4 tabstop=8
+ */

Copied: brlcad/branches/swrast/src/libbrep/debug_plot.cpp (from rev 77302, 
brlcad/trunk/src/libbrep/debug_plot.cpp)
===================================================================
--- brlcad/branches/swrast/src/libbrep/debug_plot.cpp                           
(rev 0)
+++ brlcad/branches/swrast/src/libbrep/debug_plot.cpp   2020-10-01 13:21:15 UTC 
(rev 77305)
@@ -0,0 +1,990 @@
+/*                  D E B U G _ P L O T . C P P
+ * BRL-CAD
+ *
+ * Copyright (c) 2014 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 debug_plot.cpp
+ *
+ * DebugPlot implementation. Currently borrows code from librt to
+ * handle the creation of vlists for brep geometry and conversion of
+ * those vlists to unix plot files.
+ */
+
+#include <iostream>
+#include <sstream>
+#include <string>
+#include "bu/log.h"
+#include "bn.h"
+#include "raytrace.h"
+#include "vmath.h"
+#include "debug_plot.h"
+#include "brep_except.h"
+
+static unsigned char surface1_color[] = {0, 0, 62};
+static unsigned char surface2_color[] = {62, 0, 0};
+static unsigned char surface1_highlight_color[] = {56, 56, 255};
+static unsigned char surface2_highlight_color[] = {255, 56, 56};
+
+static unsigned char tangent_color[] = {255, 255, 255};
+static unsigned char transverse_color[] = {255, 255, 0};
+static unsigned char overlap_color[] = {0, 255, 0};
+
+static unsigned char accepted_outerloop_color[] = {0, 255, 0};
+static unsigned char accepted_innerloop_color[] = {255, 0, 0};
+static unsigned char unknown_outerloop_color[] = {158, 158, 0};
+static unsigned char unknown_innerloop_color[] = {158, 158, 0};
+static unsigned char rejected_outerloop_color[] = {0, 62, 0};
+static unsigned char rejected_innerloop_color[] = {62, 0, 0};
+
+DebugPlot::DebugPlot(const char *basename) :
+    prefix(basename),
+    have_surfaces(false),
+    brep1_surf_count(0),
+    brep2_surf_count(0),
+    linked_curve_count(0)
+{
+    BU_LIST_INIT(&vlist_free_list);
+}
+
+DebugPlot::~DebugPlot()
+{
+    struct bn_vlist *vp;
+    while (BU_LIST_WHILE(vp, bn_vlist, &vlist_free_list)) {
+       BU_LIST_DEQUEUE(&(vp->l));
+       bu_free((char *)vp, "bn_vlist");
+    }
+}
+
+int
+DebugPlot::SurfacePairs(void)
+{
+    return (int)intersecting_surfaces.size();
+}
+
+int
+DebugPlot::IntersectingIsocurves(int ssx_idx)
+{
+    int max_isocsx_idx = (int)ssx_isocsx_events.size() - 1;
+    if (ssx_idx < 0 || ssx_idx > max_isocsx_idx) {
+       std::cerr << "DebugPlot::IntersectingIsocurves passed invalid ssx 
index.\n";
+       return 0;
+    }
+    return (int)ssx_isocsx_events[ssx_idx].size();
+}
+
+int
+DebugPlot::LinkedCurves(void)
+{
+    return linked_curve_count;
+}
+
+HIDDEN void
+rt_vlist_to_uplot(FILE *fp, const struct bu_list *vhead)
+{
+    struct bn_vlist *vp;
+
+    for (BU_LIST_FOR(vp, bn_vlist, vhead)) {
+        int i;
+        int nused = vp->nused;
+        const int *cmd = vp->cmd;
+        point_t *pt = vp->pt;
+
+       for (i = 0; i < nused; i++, cmd++, pt++) {
+           switch (*cmd) {
+               case BN_VLIST_POLY_START:
+               case BN_VLIST_TRI_START:
+                   break;
+               case BN_VLIST_POLY_MOVE:
+               case BN_VLIST_LINE_MOVE:
+               case BN_VLIST_TRI_MOVE:
+                   pdv_3move(fp, *pt);
+                   break;
+               case BN_VLIST_POLY_DRAW:
+               case BN_VLIST_POLY_END:
+               case BN_VLIST_LINE_DRAW:
+               case BN_VLIST_TRI_DRAW:
+               case BN_VLIST_TRI_END:
+                   pdv_3cont(fp, *pt);
+                   break;
+               default:
+                   bu_log("rt_vlist_to_uplot: unknown vlist cmd x%x\n",
+                          *cmd);
+           }
+       }
+    }
+}
+
+HIDDEN void
+write_plot_to_file(
+    const char *filename,
+    const struct bu_list *vhead,
+    const unsigned char *color)
+{
+    FILE *fp = fopen(filename, "w");
+
+    if (!color) {
+       unsigned char clr[] = {255, 0, 0};
+       color = clr;
+    }
+
+    pl_linmod(fp, "solid");
+    pl_color(fp, color[0], color[1], color[2]);
+    rt_vlist_to_uplot(fp, vhead);
+
+    fclose(fp);
+}
+
+void
+DebugPlot::WriteLog()
+{
+    std::ostringstream filename;
+
+    // First write out empty plots of different colors, we use this to
+    // "erase" unwanted overlays when running the dplot command.
+    struct bu_list vhead;
+    BU_LIST_INIT(&vhead);
+    point_t origin = {0.0, 0.0, 0.0};
+    BN_ADD_VLIST(&vlist_free_list, &vhead, origin, BN_VLIST_LINE_MOVE);
+
+    filename << prefix << "_empty0.plot3";
+    write_plot_to_file(filename.str().c_str(), &vhead, tangent_color);
+    filename.str("");
+    filename << prefix << "_empty1.plot3";
+    write_plot_to_file(filename.str().c_str(), &vhead, transverse_color);
+    filename.str("");
+    filename << prefix << "_empty2.plot3";
+    write_plot_to_file(filename.str().c_str(), &vhead, overlap_color);
+    filename.str("");
+    filename << prefix << "_empty3.plot3";
+    write_plot_to_file(filename.str().c_str(), &vhead, surface1_color);
+    filename.str("");
+    filename << prefix << "_empty4.plot3";
+    write_plot_to_file(filename.str().c_str(), &vhead, surface2_color);
+    filename.str("");
+    filename << prefix << "_empty5.plot3";
+    write_plot_to_file(filename.str().c_str(), &vhead, 
surface1_highlight_color);
+    filename.str("");
+    filename << prefix << "_empty6.plot3";
+    write_plot_to_file(filename.str().c_str(), &vhead, 
surface2_highlight_color);
+    filename.str("");
+    filename << prefix << "_empty7.plot3";
+    write_plot_to_file(filename.str().c_str(), &vhead, 
accepted_outerloop_color);
+    filename.str("");
+    filename << prefix << "_empty8.plot3";
+    write_plot_to_file(filename.str().c_str(), &vhead, 
accepted_innerloop_color);
+    filename.str("");
+    filename << prefix << "_empty9.plot3";
+    write_plot_to_file(filename.str().c_str(), &vhead, 
unknown_outerloop_color);
+    filename.str("");
+    filename << prefix << "_empty10.plot3";
+    write_plot_to_file(filename.str().c_str(), &vhead, 
unknown_innerloop_color);
+    filename.str("");
+    filename << prefix << "_empty11.plot3";
+    write_plot_to_file(filename.str().c_str(), &vhead, 
rejected_outerloop_color);
+    filename.str("");
+    filename << prefix << "_empty12.plot3";
+    write_plot_to_file(filename.str().c_str(), &vhead, 
rejected_innerloop_color);
+
+    // create dplot log file
+    filename.str("");
+    filename << prefix << ".dplot";
+    FILE *fp = fopen(filename.str().c_str(), "w");
+
+    // write out surface-surface intersections
+    fprintf(fp, "surfaces %d %d\n", brep1_surf_count, brep2_surf_count);
+    for (size_t i = 0; i < intersecting_surfaces.size(); ++i) {
+       std::pair<int, int> intersecting = intersecting_surfaces[i];
+
+       int events, b1_isocurves, intersecting_isocurves, b1_clipped, 
b2_clipped;
+
+       try {
+           events = ssx_events.at(i);
+       } catch (std::out_of_range &e) {
+           events = 0;
+       }
+       try {
+           std::pair<int, int> ccount = ssx_clipped_curves.at(i);
+           b1_clipped = ccount.first;
+           b2_clipped = ccount.second;
+       } catch (std::out_of_range &e) {
+           b1_clipped = b2_clipped = 0;
+       }
+       try {
+           b1_isocurves = ssx_isocsx_brep1_curves.at(i);
+       } catch (std::out_of_range &e) {
+           b1_isocurves = 0;
+       }
+       try {
+           intersecting_isocurves = (int)ssx_isocsx_events.at(i).size();
+       } catch (std::out_of_range &e) {
+           intersecting_isocurves = 0;
+       }
+
+       // b1si b2si finalevents b1ccurves b2ccurves b1_isocurve_xs 
total_isocurve_xs isocsx0_event0 ...
+       fprintf(fp, "ssx %d %d %d %d %d %d %d", intersecting.first,
+               intersecting.second, events, b1_clipped, b2_clipped,
+               b1_isocurves, intersecting_isocurves);
+
+       if (ssx_isocsx_events.size() > i) {
+           for (size_t j = 0; j < ssx_isocsx_events[i].size(); ++j) {
+               fprintf(fp, " %d", ssx_isocsx_events[i][j]);
+           }
+       }
+       fprintf(fp, "\n");
+    }
+
+    // write out linked curve count
+    if (linked_curve_count > 0) {
+       fprintf(fp, "linkedcurves %d\n", linked_curve_count);
+    }
+
+    // write out split faces
+    size_t split_faces = split_face_outerloop_curves.size();
+    if (split_faces > 0) {
+       fprintf(fp, "splitfaces %d\n", (int)split_faces);
+    }
+    for (size_t i = 0; i < split_faces; ++i) {
+       fprintf(fp, "splitface %d %d %d\n", (int)i,
+               split_face_outerloop_curves[i],
+               split_face_innerloop_curves[i]);
+    }
+    fclose(fp);
+    BN_FREE_VLIST(&vlist_free_list, &vhead);
+}
+
+HIDDEN double
+find_next_t(const ON_Curve* crv, double start_t, double step, double max_dist)
+{
+    ON_Interval dom = crv->Domain();
+    ON_3dPoint prev_pt = crv->PointAt(dom.ParameterAt(start_t));
+    ON_3dPoint next_pt;
+
+    // ensure that (start + step) < 1.0
+    if (start_t + step > 1.0) {
+       step = 1.0 - start_t - BN_TOL_DIST;
+    }
+    
+    // reduce step until next point is within tolerance
+    while (step > BN_TOL_DIST) {
+       next_pt = crv->PointAt(dom.ParameterAt(start_t + step));
+
+       if (prev_pt.DistanceTo(next_pt) <= max_dist) {
+           return start_t + step;
+       }
+       step /= 2.0;
+    }
+    // if we couldn't find a point within tolerance, give up and jump
+    // to end of domain
+    return 1.0;
+}
+
+void
+DebugPlot::Plot3DCurve(
+    const ON_Curve *crv,
+    const char *filename,
+    unsigned char *color,
+    struct bu_list *vlist /* = NULL */)
+{
+    struct bu_list vhead_tmp;
+    BU_LIST_INIT(&vhead_tmp);
+
+    struct bu_list *vhead = &vhead_tmp;
+    if (vlist) {
+       vhead = vlist;
+    }
+
+    ON_Interval crv_dom = crv->Domain();
+
+    // Insert first point.
+    point_t pt1;
+    ON_3dPoint p;
+    p = crv->PointAt(crv_dom.ParameterAt(0.0));
+    VMOVE(pt1, p);
+    BN_ADD_VLIST(&vlist_free_list, vhead, pt1, BN_VLIST_LINE_MOVE);
+
+    /* Dynamic sampling approach - start with an initial guess
+     * for the next point of one tenth of the domain length
+     * further down the domain from the previous value.  Set a
+     * maximum physical distance between points of 100 times
+     * the model tolerance.  Reduce the increment until the
+     * tolerance is satisfied, then add the point and use it
+     * as the starting point for the next calculation until
+     * the whole domain is finished.  Perhaps it would be more
+     * ideal to base the tolerance on some fraction of the
+     * curve bounding box dimensions?
+     */
+    double t = 0.0;
+    while (t < 1.0) {
+       t = find_next_t(crv, t, 0.1, BN_TOL_DIST * 100);
+       p = crv->PointAt(crv_dom.ParameterAt(t));
+       VMOVE(pt1, p);
+
+       BN_ADD_VLIST(&vlist_free_list, vhead, pt1, BN_VLIST_LINE_DRAW);
+    }
+
+    if (!vlist) {
+       write_plot_to_file(filename, vhead, color);
+       BN_FREE_VLIST(&vlist_free_list, vhead);
+    }
+}
+
+void
+DebugPlot::Plot3DCurveFrom2D(
+    const ON_Surface *surf,
+    const ON_Curve *crv,
+    const char *filename,
+    unsigned char *color,
+    bool decorate /* = false */)
+{
+    struct bu_list vhead;
+    BU_LIST_INIT(&vhead);
+
+    ON_Interval crv_dom = crv->Domain();
+
+    ON_3dPoint p, uv;
+
+    // Insert first point.
+    point_t pt1, first_pt, last_pt, prev_pt;
+    ON_3dVector normal;
+    uv = crv->PointAt(crv_dom.ParameterAt(0.0));
+    surf->EvNormal(uv.x, uv.y, p, normal);
+    VMOVE(first_pt, p);
+
+    uv = crv->PointAt(crv_dom.ParameterAt(1.0));
+    surf->EvNormal(uv.x, uv.y, p, normal);
+    VMOVE(last_pt, p);
+
+    bool closed = false;
+    if (VNEAR_EQUAL(first_pt, last_pt, BN_TOL_DIST)) {
+       closed = true;
+    }
+
+    VMOVE(pt1, first_pt);
+    BN_ADD_VLIST(&vlist_free_list, &vhead, pt1, BN_VLIST_LINE_MOVE);
+
+    /* Dynamic sampling approach - start with an initial guess
+     * for the next point of one tenth of the domain length
+     * further down the domain from the previous value.  Set a
+     * maximum physical distance between points of 100 times
+     * the model tolerance.  Reduce the increment until the
+     * tolerance is satisfied, then add the point and use it
+     * as the starting point for the next calculation until
+     * the whole domain is finished.  Perhaps it would be more
+     * ideal to base the tolerance on some fraction of the
+     * curve bounding box dimensions?
+     */
+    double t = 0.0;
+    bool first = true;
+    double min_mag = BN_TOL_DIST * 10.0;
+    double mag_tan = min_mag;
+    vect_t tangent = {0.0, 0.0, min_mag};
+    vect_t perp, barb;
+    while (t < 1.0) {
+       t = find_next_t(crv, t, 0.1, BN_TOL_DIST * 100);
+
+       uv = crv->PointAt(crv_dom.ParameterAt(t));
+       surf->EvNormal(uv.x, uv.y, p, normal);
+       VMOVE(prev_pt, pt1);
+       VMOVE(pt1, p);
+
+       if (first || !VNEAR_EQUAL(pt1, prev_pt, BN_TOL_DIST)) {
+           VSUB2(tangent, pt1, prev_pt);
+       }
+
+       if (decorate && first) {
+           first = false;
+
+           mag_tan = DIST_PNT_PNT(prev_pt, pt1);
+           mag_tan = FMAX(mag_tan, min_mag);
+
+           VUNITIZE(tangent);
+           VCROSS(perp, tangent, normal);
+
+           if (!closed) {
+               VSCALE(tangent, tangent, mag_tan);
+               VUNITIZE(perp);
+               VSCALE(perp, perp, mag_tan);
+
+               VADD3(barb, prev_pt, tangent, perp);
+               BN_ADD_VLIST(&vlist_free_list, &vhead, barb, 
BN_VLIST_LINE_DRAW);
+               BN_ADD_VLIST(&vlist_free_list, &vhead, prev_pt, 
BN_VLIST_LINE_MOVE);
+
+               VSCALE(perp, perp, -1.0);
+               VADD3(barb, prev_pt, tangent, perp);
+               BN_ADD_VLIST(&vlist_free_list, &vhead, barb, 
BN_VLIST_LINE_DRAW);
+               BN_ADD_VLIST(&vlist_free_list, &vhead, prev_pt, 
BN_VLIST_LINE_MOVE);
+           }
+       }
+       BN_ADD_VLIST(&vlist_free_list, &vhead, pt1, BN_VLIST_LINE_DRAW);
+    }
+    if (decorate) {
+       VUNITIZE(tangent);
+       VSCALE(tangent, tangent, -mag_tan);
+
+       VCROSS(perp, tangent, normal);
+       VUNITIZE(perp);
+       VSCALE(perp, perp, mag_tan);
+
+       VADD2(barb, pt1, perp);
+       if (!closed) {
+           VADD2(barb, barb, tangent);
+       }
+       BN_ADD_VLIST(&vlist_free_list, &vhead, barb, BN_VLIST_LINE_DRAW);
+       BN_ADD_VLIST(&vlist_free_list, &vhead, pt1, BN_VLIST_LINE_MOVE);
+
+       VSCALE(perp, perp, -1.0);
+       VADD2(barb, pt1, perp);
+       if (!closed) {
+           VADD2(barb, barb, tangent);
+       }
+       BN_ADD_VLIST(&vlist_free_list, &vhead, barb, BN_VLIST_LINE_DRAW);
+    }
+
+    write_plot_to_file(filename, &vhead, color);
+    BN_FREE_VLIST(&vlist_free_list, &vhead);
+}
+
+void
+DebugPlot::PlotBoundaryIsocurves(
+    struct bu_list *vlist,
+    const ON_Surface &surf,
+    int knot_dir)
+{
+    int surf_dir = 1 - knot_dir;
+    int knot_count = surf.SpanCount(surf_dir) + 1;
+
+    double *surf_knots = new double[knot_count];
+    surf.GetSpanVector(surf_dir, surf_knots);
+
+    // knots that can be boundaries of Bezier patches
+    ON_SimpleArray<double> surf_bknots;
+    surf_bknots.Append(surf_knots[0]);
+    for (int i = 1; i < knot_count; i++) {
+       if (surf_knots[i] > *(surf_bknots.Last())) {
+           surf_bknots.Append(surf_knots[i]);
+       }
+    }
+    delete[] surf_knots;
+
+    if (surf.IsClosed(surf_dir)) {
+       surf_bknots.Remove();
+    }
+
+    for (int i = 0; i < surf_bknots.Count(); i++) {
+       ON_Curve *surf_boundary_iso = surf.IsoCurve(knot_dir, surf_bknots[i]);
+       Plot3DCurve(surf_boundary_iso, NULL, NULL, vlist);
+       delete surf_boundary_iso;
+    }
+}
+
+void
+DebugPlot::PlotSurface(
+    const ON_Surface &surf,
+    const char *filename,
+    unsigned char *color)
+{
+    struct bu_list vhead;
+    BU_LIST_INIT(&vhead);
+
+    PlotBoundaryIsocurves(&vhead, surf, 0);
+    PlotBoundaryIsocurves(&vhead, surf, 1);
+
+    write_plot_to_file(filename, &vhead, color);
+
+    BN_FREE_VLIST(&vlist_free_list, &vhead);
+}
+
+
+void
+DebugPlot::Surfaces(const ON_Brep *brep1, const ON_Brep *brep2)
+{
+    if (!brep1 || !brep2) {
+       std::cerr << "error: dplot_surfaces: NULL args\n";
+       return;
+    }
+
+    brep1_surf_count = brep1->m_S.Count();
+    for (int i = 0; i < brep1->m_S.Count(); i++) {
+       ON_Surface *surf = brep1->m_S[i];
+       std::ostringstream filename;
+       filename << prefix << "_brep1_surface" << i << ".plot3";
+       PlotSurface(*surf, filename.str().c_str(), surface1_color);
+    }
+
+    brep2_surf_count = brep2->m_S.Count();
+    for (int i = 0; i < brep2->m_S.Count(); i++) {
+       ON_Surface *surf = brep2->m_S[i];
+       std::ostringstream filename;
+       filename << prefix << "_brep2_surface" << i << ".plot3";
+       PlotSurface(*surf, filename.str().c_str(), surface2_color);
+    }
+    have_surfaces = true;
+}
+
+int get_subcurve_inside_faces(const ON_Brep *brep1, const ON_Brep *brep2, int 
face_i1, int face_i2, ON_SSX_EVENT *event);
+
+void
+DebugPlot::SSX(
+    const ON_ClassArray<ON_SSX_EVENT> &events,
+    const ON_Brep *brep1, int brep1_surf,
+    const ON_Brep *brep2, int brep2_surf)
+{
+    ON_Surface *surf;
+    std::ostringstream filename;
+
+    // create highlighted plot of brep1 surface if it doesn't exist
+    filename << prefix << "_highlight_brep1_surface" << brep1_surf << ".plot3";
+    if (!bu_file_exists(filename.str().c_str(), NULL)) {
+       surf = brep1->m_S[brep1_surf];
+       PlotSurface(*surf, filename.str().c_str(), surface1_highlight_color);
+    }
+
+    // create highlighted plot of brep2 surface if it doesn't exist
+    filename.str("");
+    filename << prefix << "_highlight_brep2_surface" << brep2_surf << ".plot3";
+    if (!bu_file_exists(filename.str().c_str(), NULL)) {
+       surf = brep2->m_S[brep2_surf];
+       PlotSurface(*surf, filename.str().c_str(), surface2_highlight_color);
+    }
+
+    // create plot of the intersections between these surfaces
+    surf = brep1->m_S[brep1_surf];
+    size_t ssx_idx = intersecting_surfaces.size();
+    int plot_count = 0;
+    for (int i = 0; i < events.Count(); ++i) {
+       filename.str("");
+       filename << prefix << "_ssx" << ssx_idx << "_event" << plot_count <<
+           ".plot3";
+
+       if (events[i].m_type == ON_SSX_EVENT::ssx_tangent) {
+           Plot3DCurveFrom2D(surf, events[i].m_curveA,
+                   filename.str().c_str(), tangent_color, true);
+           ++plot_count;
+       } else if (events[i].m_type == ON_SSX_EVENT::ssx_transverse) {
+           Plot3DCurveFrom2D(surf, events[i].m_curveA,
+                   filename.str().c_str(), transverse_color, true);
+           ++plot_count;
+       } else if (events[i].m_type == ON_SSX_EVENT::ssx_overlap) {
+           Plot3DCurveFrom2D(surf, events[i].m_curveA,
+                   filename.str().c_str(), overlap_color, true);
+           ++plot_count;
+       }
+    }
+    // stash surface indices and event count
+    std::pair<int, int> ssx_pair(brep1_surf, brep2_surf);
+    intersecting_surfaces.push_back(ssx_pair);
+    ssx_events.push_back(plot_count);
+}
+
+void
+DebugPlot::IsoCSX(
+    const ON_SimpleArray<ON_X_EVENT> &events,
+    const ON_Curve *isocurve,
+    bool is_brep1_iso) // is the isocurve from brep1?
+{
+    size_t ssx_idx = intersecting_surfaces.size();
+
+    // create plot of the intersections between the curve and surface
+    while (ssx_isocsx_events.size() < (ssx_idx + 1)) {
+       ssx_isocsx_events.push_back(std::vector<int>());
+    }
+    size_t isocsx_idx = ssx_isocsx_events[ssx_idx].size();
+    int plot_count = 0;
+    for (int i = 0; i < events.Count(); ++i) {
+       if (events[i].m_type == ON_X_EVENT::csx_overlap) {
+           std::ostringstream filename;
+           filename << prefix << "_ssx" << ssx_idx << "_isocsx" << isocsx_idx
+               << "_event" << plot_count++ << ".plot3";
+
+           try {
+               ON_Curve *event_curve = sub_curve(isocurve, events[i].m_a[0],
+                       events[i].m_a[1]);
+               Plot3DCurve(event_curve, filename.str().c_str(), overlap_color);
+           } catch (InvalidInterval &e) {
+               std::cerr << "error: IsoCSX event contains degenerate 
interval\n";
+           }
+       }
+    }
+    if (plot_count) {
+       // create highlighted plot of isocurve if it doesn't already exist
+       std::ostringstream filename;
+       filename << prefix << "_highlight_ssx" << ssx_idx << "_isocurve" <<
+           isocsx_idx << ".plot3";
+       if (!bu_file_exists(filename.str().c_str(), NULL)) {
+           if (is_brep1_iso) {
+               Plot3DCurve(isocurve, filename.str().c_str(),
+                       surface1_highlight_color);
+           } else {
+               Plot3DCurve(isocurve, filename.str().c_str(),
+                       surface2_highlight_color);
+           }
+       }
+
+       // remember event count for this isocsx
+       ssx_isocsx_events[ssx_idx].push_back(plot_count);
+
+       // remember how many events are for brep1 isocurve and brep2 surface,
+       if (is_brep1_iso) {
+           while (ssx_isocsx_brep1_curves.size() < (ssx_idx + 1)) {
+               ssx_isocsx_brep1_curves.push_back(0);
+           }
+           ++ssx_isocsx_brep1_curves[ssx_idx];
+       }
+    }
+}
+
+void
+DebugPlot::ClippedFaceCurves(
+    const ON_Surface *surf1,
+    const ON_Surface *surf2,
+    const ON_SimpleArray<ON_Curve *> &face1_curves,
+    const ON_SimpleArray<ON_Curve *> &face2_curves)
+{
+    // plot clipped tangent/transverse/overlap curves
+    size_t ssx_idx = intersecting_surfaces.size() - 1;
+    for (int i = 0; i < face1_curves.Count(); ++i) {
+       std::ostringstream filename;
+       filename << prefix << "_ssx" << ssx_idx << "_brep1face_clipped_curve" 
<< i << ".plot3";
+       Plot3DCurveFrom2D(surf1, face1_curves[i], filename.str().c_str(),
+               surface1_highlight_color, true);
+    }
+    for (int i = 0; i < face2_curves.Count(); ++i) {
+       std::ostringstream filename;
+       filename << prefix << "_ssx" << ssx_idx << "_brep2face_clipped_curve" 
<< i << ".plot3";
+       Plot3DCurveFrom2D(surf2, face2_curves[i], filename.str().c_str(),
+               surface2_highlight_color, true);
+    }
+
+    while (ssx_clipped_curves.size() < (ssx_idx + 1)) {
+       ssx_clipped_curves.push_back(std::pair<int, int>(0, 0));
+    }
+    std::pair<int, int> counts(face1_curves.Count(), face2_curves.Count());
+    ssx_clipped_curves[ssx_idx] = counts;
+}
+
+struct TrimmedFace {
+    // curve segments in the face's outer loop
+    ON_SimpleArray<ON_Curve *> m_outerloop;
+    // several inner loops, each has some curves
+    std::vector<ON_SimpleArray<ON_Curve *> > m_innerloop;
+    const ON_BrepFace *m_face;
+    enum {
+       UNKNOWN = -1,
+       NOT_BELONG = 0,
+       BELONG = 1
+    } m_belong_to_final;
+    bool m_rev;
+
+    // Default constructor
+    TrimmedFace()
+    {
+       m_face = NULL;
+       m_belong_to_final = UNKNOWN;
+       m_rev = false;
+    }
+
+    // Destructor
+    ~TrimmedFace()
+    {
+       // Delete the curve segments if it's not belong to the result.
+       if (m_belong_to_final != BELONG) {
+           for (int i = 0; i < m_outerloop.Count(); i++) {
+               if (m_outerloop[i]) {
+                   delete m_outerloop[i];
+                   m_outerloop[i] = NULL;
+               }
+           }
+           for (unsigned int i = 0; i < m_innerloop.size(); i++) {
+               for (int j = 0; j < m_innerloop[i].Count(); j++) {
+                   if (m_innerloop[i][j]) {
+                       delete m_innerloop[i][j];
+                       m_innerloop[i][j] = NULL;
+                   }
+               }
+           }
+       }
+    }
+
+    TrimmedFace *Duplicate() const
+    {
+       TrimmedFace *out = new TrimmedFace();
+       out->m_face = m_face;
+       for (int i = 0; i < m_outerloop.Count(); i++) {
+           if (m_outerloop[i]) {
+               out->m_outerloop.Append(m_outerloop[i]->Duplicate());
+           }
+       }
+       out->m_innerloop = m_innerloop;
+       for (unsigned int i = 0; i < m_innerloop.size(); i++) {
+           for (int j = 0; j < m_innerloop[i].Count(); j++) {
+               if (m_innerloop[i][j]) {
+                   out->m_innerloop[i][j] = m_innerloop[i][j]->Duplicate();
+               }
+           }
+       }
+       return out;
+    }
+};
+
+void
+DebugPlot::SplitFaces(
+    const ON_ClassArray<ON_SimpleArray<TrimmedFace *> > &split_faces)
+{
+    for (int i = 0; i < split_faces.Count(); ++i) {
+       for (int j = 0; j < split_faces[i].Count(); ++j) {
+           TrimmedFace *face = split_faces[i][j];
+
+           unsigned char *outerloop_color = unknown_outerloop_color;
+           unsigned char *innerloop_color = unknown_innerloop_color;
+           switch (face->m_belong_to_final) {
+               case TrimmedFace::NOT_BELONG:
+                   outerloop_color = rejected_outerloop_color;
+                   innerloop_color = rejected_innerloop_color;
+                   break;
+               case TrimmedFace::BELONG:
+                   outerloop_color = accepted_outerloop_color;
+                   innerloop_color = accepted_innerloop_color;
+                   break;
+               default:
+                   outerloop_color = unknown_outerloop_color;
+                   innerloop_color = unknown_innerloop_color;
+           }
+
+           int split_face_count = split_face_outerloop_curves.size();
+           for (int k = 0; k < face->m_outerloop.Count(); ++k) {
+               std::ostringstream filename;
+               filename << prefix << "_split_face" << split_face_count <<
+                   "_outerloop_curve" << k << ".plot3";
+
+               Plot3DCurveFrom2D(face->m_face->SurfaceOf(),
+                       face->m_outerloop[k], filename.str().c_str(),
+                       outerloop_color);
+           }
+           split_face_outerloop_curves.push_back(face->m_outerloop.Count());
+
+           int innerloop_count = 0;
+           for (size_t k = 0; k < face->m_innerloop.size(); ++k) {
+               for (int l = 0; l < face->m_innerloop[k].Count(); ++l) {
+                   std::ostringstream filename;
+                   filename << prefix << "_split_face" << split_face_count <<
+                       "_innerloop_curve" << innerloop_count++ << ".plot3";
+
+                   Plot3DCurveFrom2D(face->m_face->SurfaceOf(),
+                           face->m_innerloop[k][l], filename.str().c_str(),
+                           innerloop_color);
+               }
+           }
+           split_face_innerloop_curves.push_back(innerloop_count);
+       }
+    }
+}
+
+void append_to_polycurve(ON_Curve *curve, ON_PolyCurve &polycurve);
+
+struct SSICurve {
+    ON_Curve *m_curve;
+
+    SSICurve()
+    {
+       m_curve = NULL;
+    }
+
+    SSICurve(ON_Curve *curve)
+    {
+       m_curve = curve;
+    }
+
+    SSICurve *Duplicate() const
+    {
+       SSICurve *out = new SSICurve();
+       if (out != NULL) {
+           *out = *this;
+           out->m_curve = m_curve->Duplicate();
+       }
+       return out;
+    }
+};
+
+struct LinkedCurve {
+private:
+    ON_Curve *m_curve; // an explicit storage of the whole curve
+public:
+    // The curves contained in this LinkedCurve, including
+    // the information needed by the connectivity graph
+    ON_SimpleArray<SSICurve> m_ssi_curves;
+
+    // Default constructor
+    LinkedCurve()
+    {
+       m_curve = NULL;
+    }
+
+    ~LinkedCurve()
+    {
+       if (m_curve) {
+           delete m_curve;
+       }
+       m_curve = NULL;
+    }
+
+    LinkedCurve &operator= (const LinkedCurve &_lc)
+    {
+       m_curve = _lc.m_curve ? _lc.m_curve->Duplicate() : NULL;
+       m_ssi_curves = _lc.m_ssi_curves;
+       return *this;
+    }
+
+    ON_3dPoint PointAtStart() const
+    {
+       if (m_ssi_curves.Count()) {
+           return m_ssi_curves[0].m_curve->PointAtStart();
+       } else {
+           return ON_3dPoint::UnsetPoint;
+       }
+    }
+
+    ON_3dPoint PointAtEnd() const
+    {
+       if (m_ssi_curves.Count()) {
+           return m_ssi_curves.Last()->m_curve->PointAtEnd();
+       } else {
+           return ON_3dPoint::UnsetPoint;
+       }
+    }
+
+    bool IsClosed() const
+    {
+       if (m_ssi_curves.Count() == 0) {
+           return false;
+       }
+       return PointAtStart().DistanceTo(PointAtEnd()) < ON_ZERO_TOLERANCE;
+    }
+
+    bool IsValid() const
+    {
+       // Check whether the curve has "gaps".
+       for (int i = 1; i < m_ssi_curves.Count(); i++) {
+           if 
(m_ssi_curves[i].m_curve->PointAtStart().DistanceTo(m_ssi_curves[i - 
1].m_curve->PointAtEnd()) >= ON_ZERO_TOLERANCE) {
+               bu_log("The LinkedCurve is not valid.\n");
+               return false;
+           }
+       }
+       return true;
+    }
+
+    bool Reverse()
+    {
+       ON_SimpleArray<SSICurve> new_array;
+       for (int i = m_ssi_curves.Count() - 1; i >= 0; i--) {
+           if (!m_ssi_curves[i].m_curve->Reverse()) {
+               return false;
+           }
+           new_array.Append(m_ssi_curves[i]);
+       }
+       m_ssi_curves = new_array;
+       return true;
+    }
+
+    void Append(const LinkedCurve &lc)
+    {
+       m_ssi_curves.Append(lc.m_ssi_curves.Count(), lc.m_ssi_curves.Array());
+    }
+
+    void Append(const SSICurve &sc)
+    {
+       m_ssi_curves.Append(sc);
+    }
+
+    void AppendCurvesToArray(ON_SimpleArray<ON_Curve *> &arr) const
+    {
+       for (int i = 0; i < m_ssi_curves.Count(); i++) {
+           arr.Append(m_ssi_curves[i].m_curve->Duplicate());
+       }
+    }
+
+    const ON_Curve *Curve()
+    {
+       if (m_curve != NULL) {
+           return m_curve;
+       }
+       if (m_ssi_curves.Count() == 0 || !IsValid()) {
+           return NULL;
+       }
+       ON_PolyCurve *polycurve = new ON_PolyCurve;
+       for (int i = 0; i < m_ssi_curves.Count(); i++) {
+           append_to_polycurve(m_ssi_curves[i].m_curve->Duplicate(), 
*polycurve);
+       }
+       m_curve = polycurve;
+       return m_curve;
+    }
+
+    const ON_3dPoint PointAt(double t)
+    {
+       const ON_Curve *c = Curve();
+       if (c == NULL) {
+           return ON_3dPoint::UnsetPoint;
+       }
+       return c->PointAt(t);
+    }
+
+    const ON_Interval Domain()
+    {
+       const ON_Curve *c = Curve();
+       if (c == NULL) {
+           return ON_Interval::EmptyInterval;
+       }
+       return c->Domain();
+    }
+
+    ON_Curve *SubCurve(double t1, double t2)
+    {
+       const ON_Curve *c = Curve();
+       if (c == NULL) {
+           return NULL;
+       }
+       try {
+           return sub_curve(c, t1, t2);
+       } catch (InvalidInterval &e) {
+           bu_log("%s", e.what());
+           return NULL;
+       }
+    }
+};
+
+void
+DebugPlot::LinkedCurves(
+    const ON_Surface *surf,
+    ON_ClassArray<LinkedCurve> &linked_curves)
+{
+    for (int i = 0; i < linked_curves.Count(); ++i) {
+       const ON_Curve *linked_curve = linked_curves[i].Curve();
+
+       std::ostringstream filename;
+       filename << prefix << "_linked_curve" << linked_curve_count++ << 
".plot3";
+
+       Plot3DCurveFrom2D(surf, linked_curve, filename.str().c_str(), 
transverse_color);
+    }
+}
+
+// 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/branches/swrast/src/libbrep/debug_plot.h (from rev 77302, 
brlcad/trunk/src/libbrep/debug_plot.h)
===================================================================
--- brlcad/branches/swrast/src/libbrep/debug_plot.h                             
(rev 0)
+++ brlcad/branches/swrast/src/libbrep/debug_plot.h     2020-10-01 13:21:15 UTC 
(rev 77305)
@@ -0,0 +1,141 @@
+/*                    D E B U G _ P L O T . H
+ * BRL-CAD
+ *
+ * Copyright (c) 2014 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 debug_plot.h
+ *
+ * Class used to write out unix plot files representing brep geometry
+ * and intersection events from various stages of brep boolean
+ * evaluation.
+ *
+ * Also outputs a parsable log file with implicit information about
+ * the output plot files.
+ */
+
+#ifndef DEBUG_PLOT_H
+#define DEBUG_PLOT_H
+#include "bu/list.h"
+#include "raytrace.h"
+
+struct TrimmedFace;
+struct LinkedCurve;
+
+class DebugPlot {
+public:
+    // prefix all dplot output files with 'basename'
+    DebugPlot(const char *basename);
+
+    ~DebugPlot();
+
+    // write plot files for all surfaces of two breps
+    void Surfaces(const ON_Brep *brep1, const ON_Brep *brep2);
+
+    // record a surface surface intersection
+    void SSX(
+       const ON_ClassArray<ON_SSX_EVENT> &events,
+       const ON_Brep *brep1, int brep1_surf,
+       const ON_Brep *brep2, int brep2_surf);
+
+    // record surface surface isocurve intersections
+    void IsoCSX(
+       const ON_SimpleArray<ON_X_EVENT> &events,
+       const ON_Curve *isocurve,
+       bool is_brep1_iso);
+
+    // record clipped surface-surface intersection curves
+    void ClippedFaceCurves(
+       const ON_Surface *surf1,
+       const ON_Surface *surf2,
+       const ON_SimpleArray<ON_Curve *> &face1_curves,
+       const ON_SimpleArray<ON_Curve *> &face2_curves);
+
+    // write out the log file that the dplot command references to
+    // navigate the generated plot files
+    void WriteLog();
+
+    // get the number of intersecting surface pairs recorded
+    int SurfacePairs();
+
+    // get the number of isocurves that intersect either surface in
+    // given surface pair
+    int IntersectingIsocurves(int ssx_idx);
+
+    // get the number of linked curves recorded
+    int LinkedCurves();
+
+    void SplitFaces(
+       const ON_ClassArray<ON_SimpleArray<TrimmedFace *> > &split_faces);
+
+    void LinkedCurves(
+       const ON_Surface *surf,
+       ON_ClassArray<LinkedCurve> &linked_curves);
+
+    void
+    Plot3DCurveFrom2D(
+       const ON_Surface *surf,
+       const ON_Curve *crv,
+       const char *filename,
+       unsigned char *color,
+       bool decorate = false
+       );
+
+    void
+    Plot3DCurve(
+       const ON_Curve *crv,
+       const char *filename,
+       unsigned char *color,
+       struct bu_list *vlist = NULL);
+
+private:
+    struct bu_list vlist_free_list;
+    std::string prefix;
+    bool have_surfaces;
+    int brep1_surf_count;
+    int brep2_surf_count;
+    int linked_curve_count;
+    std::vector< std::pair<int, int> > intersecting_surfaces; // ssx surface 
index pairs
+    std::vector<int> ssx_events; // num final events of each ssx
+    std::vector< std::vector<int> > ssx_isocsx_events; // num events for each 
isocsx of each ssx
+    std::vector<int> ssx_isocsx_brep1_curves; // num ssx isocsx events using 
brep1 isocurves
+    std::vector< std::pair<int, int> > ssx_clipped_curves; // num clipped 
intersection curves
+    std::vector<int> split_face_outerloop_curves;
+    std::vector<int> split_face_innerloop_curves;
+
+    void PlotSurface(
+       const ON_Surface &surf,
+       const char *filename,
+       unsigned char *color);
+
+    void
+    PlotBoundaryIsocurves(
+       struct bu_list *vlist,
+       const ON_Surface &surf,
+       int knot_dir);
+};
+
+#endif
+
+/*
+ * Local Variables:
+ * tab-width: 8
+ * mode: C
+ * indent-tabs-mode: t
+ * c-file-style: "stroustrup"
+ * End:
+ * ex: shiftwidth=4 tabstop=8
+ */

Modified: brlcad/branches/swrast/src/libbrep/intersect.cpp
===================================================================
--- brlcad/branches/swrast/src/libbrep/intersect.cpp    2020-10-01 13:18:29 UTC 
(rev 77304)
+++ brlcad/branches/swrast/src/libbrep/intersect.cpp    2020-10-01 13:21:15 UTC 
(rev 77305)
@@ -21,6 +21,8 @@
  *
  * Implementation of intersection routines openNURBS left out.
  *
+ * Additional documentation can be found in the "NURBS Boolean Evaluation
+ * Development Guide" docbook article (bool_eval_development.html).
  */
 
 #include "common.h"
@@ -38,42 +40,15 @@
 #include "brep/defines.h"
 #include "brep/intersect.h"
 #include "brep/util.h"
+#include "debug_plot.h"
 #include "brep_except.h"
+#include "brep_defines.h"
 
+extern DebugPlot *dplot;
 
 // Whether to output the debug messages about b-rep intersections.
 static int DEBUG_BREP_INTERSECT = 0;
 
-// The maximal depth for subdivision - trade-off between accuracy and
-// performance.
-#define NR_MAX_DEPTH 8
-#define MAX_PCI_DEPTH NR_MAX_DEPTH
-#define MAX_PSI_DEPTH NR_MAX_DEPTH
-#define MAX_CCI_DEPTH NR_MAX_DEPTH
-#define MAX_CSI_DEPTH NR_MAX_DEPTH
-#define MAX_SSI_DEPTH NR_MAX_DEPTH
-
-
-// We make the default tolerance for PSI the same as that of curve and
-// surface intersections defined by openNURBS (see opennurbs_curve.h
-// and opennurbs_surface.h).
-#define NR_DEFAULT_TOLERANCE 0.001
-#define PCI_DEFAULT_TOLERANCE NR_DEFAULT_TOLERANCE
-#define PSI_DEFAULT_TOLERANCE NR_DEFAULT_TOLERANCE
-#define CCI_DEFAULT_TOLERANCE NR_DEFAULT_TOLERANCE
-#define CSI_DEFAULT_TOLERANCE NR_DEFAULT_TOLERANCE
-#define SSI_DEFAULT_TOLERANCE NR_DEFAULT_TOLERANCE
-
-// Used to prevent an infinite loop in the unlikely event that we
-// can't provide a good starting point for the Newton-Raphson
-// Iteration.
-#define NR_MAX_ITERATIONS 100
-#define PCI_MAX_ITERATIONS NR_MAX_ITERATIONS
-#define PSI_MAX_ITERATIONS NR_MAX_ITERATIONS
-#define CCI_MAX_ITERATIONS NR_MAX_ITERATIONS
-#define CSI_MAX_ITERATIONS NR_MAX_ITERATIONS
-#define SSI_MAX_ITERATIONS NR_MAX_ITERATIONS
-
 class XEventProxy {
 public:
     XEventProxy(ON_X_EVENT::TYPE type);
@@ -2556,10 +2531,29 @@
     return (pointA.DistanceTo(pointB) < isect_tol) && !std::isnan(uA) && 
!std::isnan(vA) && !std::isnan(uB) & !std::isnan(vB);
 }
 
+// if curve end is greater than tol distance from pt, append a linear
+// segment to the curve so it extends to the pt
+HIDDEN void
+extend_curve_end_to_pt(ON_Curve *&curve, ON_3dPoint pt, double tol)
+{
+    ON_NurbsCurve *nc = curve->NurbsCurve();
+    if (nc->PointAtEnd().DistanceTo(pt) > tol) {
+       ON_LineCurve line_bridge(nc->PointAtEnd(), pt);
 
+       ON_NurbsCurve bridge;
+       if (line_bridge.GetNurbForm(bridge)) {
+           nc->Append(bridge);
+       }
+       delete curve;
+       curve = nc;
+    }
+}
+
 HIDDEN ON_Curve *
 link_curves(ON_Curve *&c1, ON_Curve *&c2)
 {
+    extend_curve_end_to_pt(c1, c2->PointAtStart(), ON_ZERO_TOLERANCE);
+
     ON_NurbsCurve *nc1 = c1->NurbsCurve();
     ON_NurbsCurve *nc2 = c2->NurbsCurve();
     if (nc1 && nc2) {
@@ -2578,6 +2572,16 @@
     return NULL;
 }
 
+// if curve start and end are within tolerance, append a linear
+// segment to the curve to close it completely so it passes
+// IsClosed() tests
+HIDDEN void
+fill_gap_if_closed(ON_Curve *&curve, double tol)
+{
+    if (curve->PointAtStart().DistanceTo(curve->PointAtEnd()) <= tol) {
+       extend_curve_end_to_pt(curve, curve->PointAtStart(), ON_ZERO_TOLERANCE);
+    }
+}
 
 struct OverlapSegment {
     ON_Curve *m_curve3d, *m_curveA, *m_curveB;
@@ -2969,17 +2973,10 @@
     bool ret = false;
 
     if (iso.overlap_t[0] < iso.overlap_t[1]) {
-       bool at_first_knot = iso.src.knot.IsFirst();
-       bool at_last_knot = iso.src.knot.IsLast();
-       bool closed_at_iso = surf1->IsClosed(1 - iso.src.knot.dir) != 0;
+       int location = isocurve_surface_overlap_location(iso, surf1, surf2,
+               surf2_tree, isect_tol, isect_tol1);
 
-       ret = true;
-       if (closed_at_iso || (!at_first_knot && !at_last_knot)) {
-           int location = isocurve_surface_overlap_location(iso, surf1,
-                                                            surf2, surf2_tree, 
isect_tol, isect_tol1);
-
-           ret = (location == ON_OVERLAP_BOUNDARY);
-       }
+       ret = (location == ON_OVERLAP_BOUNDARY);
     }
     return ret;
 }
@@ -3513,6 +3510,7 @@
 HIDDEN void
 find_overlap_boundary_curves(
     ON_SimpleArray<OverlapSegment *> &overlaps,
+    ON_ClassArray<ON_SSX_EVENT> &csx_events,
     ON_3dPointArray &isocurve_3d,
     ON_2dPointArray &isocurveA_2d,
     ON_2dPointArray &isocurveB_2d,
@@ -3554,6 +3552,9 @@
            ON_Intersect(surf1_isocurve, surf2, events, isect_tol,
                         overlap_tol, 0, 0, 0, &overlap2d);
 
+           dplot->IsoCSX(events, surf1_isocurve, is_surfA_iso);
+           dplot->WriteLog();
+
            append_csx_event_points(isocurve_3d, isocurve2_2d, events);
 
            for (int k = 0; k < events.Count(); k++) {
@@ -3575,6 +3576,36 @@
                    if (curve_on_overlap_boundary) {
                        append_overlap_segments(overlaps, iso, overlap2d[k],
                                                surf1);
+                   } else {
+                       // the intersection isn't part of a
+                       // surface-surface overlap, but we want it all
+                       // the same
+                       ON_SimpleArray<OverlapSegment *> tmp_overlaps;
+
+                       append_overlap_segments(tmp_overlaps, iso, 
overlap2d[k], surf1);
+
+                       for (int l = 0; l < tmp_overlaps.Count(); ++l) {
+                           ON_SSX_EVENT ssx_event;
+
+                           int ret = set_ssx_event_from_curves(ssx_event,
+                                   tmp_overlaps[l]->m_curve3d,
+                                   tmp_overlaps[l]->m_curveA,
+                                   tmp_overlaps[l]->m_curveB, surfA, surfB);
+                           if (ret != 0) {
+                               bu_log("warning: reverse failed.");
+                           }
+                           csx_events.Append(ssx_event);
+
+                           // set the curves to NULL so they aren't
+                           // deleted by destructors
+                           tmp_overlaps[l]->m_curve3d = NULL;
+                           tmp_overlaps[l]->m_curveA = NULL;
+                           tmp_overlaps[l]->m_curveB = NULL;
+
+                           ssx_event.m_curve3d = NULL;
+                           ssx_event.m_curveA = NULL;
+                           ssx_event.m_curveB = NULL;
+                       }
                    }
                }
            }
@@ -3735,12 +3766,25 @@
     ON_2dPointArray curve_uvA, curve_uvB, tmp_curve_uvA, tmp_curve_uvB;
 
     ON_SimpleArray<OverlapSegment *> overlaps;
-    find_overlap_boundary_curves(overlaps, tmp_curvept, tmp_curve_uvA,
+    ON_ClassArray<ON_SSX_EVENT> csx_events;
+
+    find_overlap_boundary_curves(overlaps, csx_events, tmp_curvept, 
tmp_curve_uvA,
                                 tmp_curve_uvB, surfA, surfB, treeA, treeB, 
isect_tol, isect_tolA,
                                 isect_tolB, overlap_tol);
+
     split_overlaps_at_intersections(overlaps, surfA, surfB, treeA, treeB,
                                    isect_tol, isect_tolA, isect_tolB);
 
+    // add csx_events
+    for (int i = 0; i < csx_events.Count(); ++i) {
+       x.Append(csx_events[i]);
+
+       // set the curves to NULL so they aren't deleted by destructor
+       csx_events[i].m_curve3d = NULL;
+       csx_events[i].m_curveA = NULL;
+       csx_events[i].m_curveB = NULL;
+    }
+
     // find the neighbors for every overlap segment
     ON_SimpleArray<bool> start_linked(overlaps.Count()), 
end_linked(overlaps.Count());
     for (int i = 0; i < overlaps.Count(); i++) {
@@ -3875,6 +3919,9 @@
                delete overlaps[j];
                overlaps[j] = NULL;
            }
+           fill_gap_if_closed(overlaps[i]->m_curve3d, isect_tol);
+           fill_gap_if_closed(overlaps[i]->m_curveA, isect_tolA);
+           fill_gap_if_closed(overlaps[i]->m_curveB, isect_tolB);
        }
     }
 

Modified: brlcad/branches/swrast/src/libbrep/tests/CMakeLists.txt
===================================================================
--- brlcad/branches/swrast/src/libbrep/tests/CMakeLists.txt     2020-10-01 
13:18:29 UTC (rev 77304)
+++ brlcad/branches/swrast/src/libbrep/tests/CMakeLists.txt     2020-10-01 
13:21:15 UTC (rev 77305)
@@ -14,15 +14,9 @@
   vsp.stp
   )
 
-BRLCAD_ADDEXEC(brep_cdt_mesh brep_cdt_mesh.cpp "libbrep;libbg;libbn;libbu" 
TEST)
+BRLCAD_ADDEXEC(test_brep_ppx ppx.cpp "libbrep"  NO_INSTALL)
+add_test(NAME brep_ppx COMMAND test_brep_ppx)
 
-BRLCAD_ADDEXEC(test_point_intersect test_point_intersect.cpp "libbrep" 
NO_STRICT NO_INSTALL)
-
-BRLCAD_ADDEXEC(test_curve_intersect test_curve_intersect.cpp "libbrep" 
NO_STRICT NO_INSTALL)
-
-CMAKEFILES(${distcheck_files})
-CMAKEFILES(CMakeLists.txt)
-
 # Local Variables:
 # tab-width: 8
 # mode: cmake

Copied: brlcad/branches/swrast/src/libbrep/tests/ppx.cpp (from rev 77302, 
brlcad/trunk/src/libbrep/tests/ppx.cpp)
===================================================================
--- brlcad/branches/swrast/src/libbrep/tests/ppx.cpp                            
(rev 0)
+++ brlcad/branches/swrast/src/libbrep/tests/ppx.cpp    2020-10-01 13:21:15 UTC 
(rev 77305)
@@ -0,0 +1,235 @@
+/*                         P P X . C P P
+ * BRL-CAD
+ *
+ * Copyright (c) 2014 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 ppx.cpp
+ *
+ * Point-point intersection tests.
+ *
+ */
+
+#define NOMINMAX
+
+#include "common.h"
+#include "bu.h"
+#include "brep.h"
+#include <limits>
+#include <sstream>
+#include <string>
+#include "../brep_defines.h"
+
+// Here we determine an upper limit on the magnitude of coordinate
+// values for test points.
+//
+// We need to be able to calculate point to point distance as:
+//     sqrt((x2 - x1)^2 + (y2 - y1)^2 + (z2 - z1)^2)
+//
+// To prevent overflow, we must restrict the coordinate values such
+// that:
+//     (x2 - x1)^2 + (y2 - y1)^2 + (z2 - z1)^2 <= DBL_MAX
+//
+// If we want to choose an upper limit M on the magnitude of a
+// coordinate value to enforce this constraint, the restriction
+// simplifies to:
+//     coord1, coord2 in [-M, M] s.t.
+//     (coord2 - coord1)^2 <= DBL_MAX / 3.0
+//  => (coord2 - coord1) <= sqrt(DBL_MAX / 3.0)
+//
+// Knowing that the maximum value of (coord2 - coord1) is 2M, we can
+// solve for M:
+//     2M <= sqrt(DBL_MAX / 3.0)
+//  => M <= sqrt(DBL_MAX / 3.0) / 2.0
+//
+// We choose to use a value slightly less than the true DBL_MAX for
+// the calculation to avoid the possibility that floating point error
+// causes [3.0 * sqrt(DBL_MAX / 3.0)^2 <= DBL_MAX] to be false.
+static const double NEAR_DBL_MAX =
+    std::numeric_limits<double>::max() -
+    std::numeric_limits<double>::round_error();
+
+static const double COORD_MAG_MAX = sqrt(NEAR_DBL_MAX / 3.0) / 2.0;
+
+class PPX_Input {
+public:
+    PPX_Input()
+       : tol(0.0)
+    {
+    }
+    ON_3dPoint ptA;
+    ON_3dPoint ptB;
+    ON_ClassArray<ON_PX_EVENT> events;
+    bool default_tol;
+    double tol;
+};
+
+class PPX_Output {
+public:
+    bool status;
+    ON_ClassArray<ON_PX_EVENT> events;
+
+    bool Equals(std::string &log, const PPX_Output &other);
+};
+
+static const double EVENT_EQUAL_TOL =
+    std::numeric_limits<double>::round_error();
+
+static bool
+point_events_equal(ON_PX_EVENT a, ON_PX_EVENT b)
+{
+    if (a.m_type != b.m_type) {
+       return false;
+    }
+    if (a.m_A.DistanceTo(b.m_A) >= EVENT_EQUAL_TOL) {
+       return false;
+    }
+    if (a.m_B.DistanceTo(b.m_B) >= EVENT_EQUAL_TOL) {
+       return false;
+    }
+    if (a.m_b.DistanceTo(b.m_b) >= EVENT_EQUAL_TOL) {
+       return false;
+    }
+    if (a.m_Mid.DistanceTo(b.m_Mid) >= EVENT_EQUAL_TOL) {
+       return false;
+    }
+    if (!ON_NearZero(a.m_radius - b.m_radius, EVENT_EQUAL_TOL)) {
+       return false;
+    }
+    return true;
+}
+
+bool
+PPX_Output::Equals(std::string &log, const PPX_Output &other)
+{
+    bool ret = true;
+    std::stringstream out;
+
+    if (status != other.status) {
+       out << "return status: " << std::boolalpha << status << " vs " <<
+           other.status << "\n";
+       ret = false;
+    }
+
+    if (events.Count() != other.events.Count()) {
+       out << "event count: " << events.Count() << " vs " <<
+           other.events.Count() << "\n";
+       ret = false;
+    } else {
+       for (int i = 0; i < events.Count(); ++i) {
+           if (!point_events_equal(other.events[i], events[i])) {
+               out << "event arrays don't match\n";
+               ret = false;
+               break;
+           }
+       }
+    }
+
+    log = out.str();
+
+    return ret;
+}
+
+void
+test_intersection(PPX_Input in, PPX_Output expected_out)
+{
+    PPX_Output out;
+
+    out.events = in.events;
+
+    if (in.default_tol) {
+       out.status = ON_Intersect(in.ptA, in.ptB, out.events);
+    } else {
+       out.status = ON_Intersect(in.ptA, in.ptB, out.events, in.tol);
+    }
+
+    std::string err_msg;
+    if (!expected_out.Equals(err_msg, out)) {
+       bu_exit(1, "Unexpected intersection result. Expected vs actual:\n%s",
+               err_msg.c_str());
+    }
+}
+
+static ON_PX_EVENT
+ppx_point_event(ON_3dPoint ptA, ON_3dPoint ptB)
+{
+    ON_PX_EVENT event;
+    event.m_type = ON_PX_EVENT::ppx_point;
+    event.m_A = ptA;
+    event.m_B = ptB;
+    event.m_b = ON_2dPoint(0.0, 0.0);
+    event.m_Mid = (ptA + ptB) * 0.5;
+    event.m_radius = ptA.DistanceTo(ptB) * 0.5;
+    return event;
+}
+
+static void
+test_equal_points(ON_3dPoint pt)
+{
+    ON_ClassArray<ON_PX_EVENT> events;
+    events.Append(ppx_point_event(pt, pt));
+
+    PPX_Output expected_output;
+    expected_output.status = true;
+    expected_output.events = events;
+
+    PPX_Input input;
+    input.ptA = input.ptB = pt;
+    input.default_tol = true;
+
+    test_intersection(input, expected_output);
+}
+
+static void
+do_equal_point_tests(void)
+{
+    std::vector<double> coord_vals;
+    coord_vals.push_back(-COORD_MAG_MAX);
+    coord_vals.push_back(-INTERSECTION_TOL);
+    coord_vals.push_back(0.0);
+    coord_vals.push_back(INTERSECTION_TOL);
+    coord_vals.push_back(COORD_MAG_MAX);
+
+    for (size_t i = 0; i < coord_vals.size(); ++i) {
+       for (size_t j = 0; j < coord_vals.size(); ++j) {
+           for (size_t k = 0; k < coord_vals.size(); ++k) {
+               ON_3dPoint test_pt(coord_vals[i], coord_vals[j],
+                       coord_vals[k]);
+
+               test_equal_points(test_pt);
+           }
+       }
+    }
+}
+
+int
+main(int UNUSED(argc), const char *argv[])
+{
+    bu_setprogname(argv[0]);
+
+    do_equal_point_tests();
+
+    return 0;
+}
+
+// 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

Modified: brlcad/branches/swrast/src/libged/analyze/util.cpp
===================================================================
--- brlcad/branches/swrast/src/libged/analyze/util.cpp  2020-10-01 13:18:29 UTC 
(rev 77304)
+++ brlcad/branches/swrast/src/libged/analyze/util.cpp  2020-10-01 13:21:15 UTC 
(rev 77305)
@@ -1,3 +1,28 @@
+/*                        U T I 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
+ * 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 util.cpp
+ *
+ * Brief description
+ *
+ */
+
 #include "common.h"
 
 extern "C" {

Modified: brlcad/branches/swrast/src/libged/brep/CMakeLists.txt
===================================================================
--- brlcad/branches/swrast/src/libged/brep/CMakeLists.txt       2020-10-01 
13:18:29 UTC (rev 77304)
+++ brlcad/branches/swrast/src/libged/brep/CMakeLists.txt       2020-10-01 
13:21:15 UTC (rev 77305)
@@ -1,3 +1,18 @@
+
+set(DPLOT_READER dplot)
+
+PERPLEX_TARGET(
+  ${DPLOT_READER}_scanner
+  ${DPLOT_READER}_scanner.perplex
+  OUT_SRC_FILE ${DPLOT_READER}_scanner.c
+  OUT_HDR_FILE ${DPLOT_READER}_scanner.h)
+LEMON_TARGET(
+  ${DPLOT_READER}_parser
+  ${DPLOT_READER}_parser.lemon
+  OUT_SRC_FILE ${DPLOT_READER}_parser.c
+  OUT_HDR_FILE ${DPLOT_READER}_parser.h)
+ADD_PERPLEX_LEMON_DEPENDENCY(${DPLOT_READER}_scanner ${DPLOT_READER}_parser)
+
 include_directories(
   ${CMAKE_CURRENT_SOURCE_DIR}
   ${BRLCAD_BINARY_DIR}/include
@@ -4,9 +19,14 @@
   ${BRLCAD_SOURCE_DIR}/include
   ${BU_INCLUDE_DIRS}
   ${GED_INCLUDE_DIRS}
+  ${PERPLEX_${DPLOT_READER}_scanner_INCLUDE_DIR}
+  ${LEMON_${DPLOT_READER}_parser_INCLUDE_DIR}
   )
 
 set(BREP_SRCS
+  ${PERPLEX_${DPLOT_READER}_scanner_SRC}
+  ${LEMON_${DPLOT_READER}_parser_SRC}
+  dplot.c
   brep.cpp
   conversion.cpp
   csg.cpp

Modified: brlcad/branches/swrast/src/libged/brep/brep.cpp
===================================================================
--- brlcad/branches/swrast/src/libged/brep/brep.cpp     2020-10-01 13:18:29 UTC 
(rev 77304)
+++ brlcad/branches/swrast/src/libged/brep/brep.cpp     2020-10-01 13:21:15 UTC 
(rev 77305)
@@ -1480,9 +1480,11 @@
 extern "C" {
     struct ged_cmd_impl brep_cmd_impl = { "brep", ged_brep_core, 
GED_CMD_DEFAULT };
     const struct ged_cmd brep_cmd = { &brep_cmd_impl };
-    const struct ged_cmd *brep_cmds[] = { &brep_cmd,  NULL };
+    struct ged_cmd_impl dplot_cmd_impl = { "dplot", ged_dplot_core, 
GED_CMD_DEFAULT };
+    const struct ged_cmd dplot_cmd = { &dplot_cmd_impl };
+    const struct ged_cmd *brep_cmds[] = { &brep_cmd, &dplot_cmd, NULL };
 
-    static const struct ged_plugin pinfo = { GED_API,  brep_cmds, 1 };
+    static const struct ged_plugin pinfo = { GED_API,  brep_cmds, 2 };
 
     COMPILER_DLLEXPORT const struct ged_plugin *ged_plugin_info()
     {

Copied: brlcad/branches/swrast/src/libged/brep/dplot.c (from rev 77302, 
brlcad/trunk/src/libged/brep/dplot.c)
===================================================================
--- brlcad/branches/swrast/src/libged/brep/dplot.c                              
(rev 0)
+++ brlcad/branches/swrast/src/libged/brep/dplot.c      2020-10-01 13:21:15 UTC 
(rev 77305)
@@ -0,0 +1,799 @@
+/*                         D P L O T . C
+ * BRL-CAD
+ *
+ * Copyright (c) 2014-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 dplot.c
+ *
+ * Brief description
+ *
+ */
+
+#include "common.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "bu/color.h"
+#include "bu/opt.h"
+#include "raytrace.h"
+#include "rt/geom.h"
+#include "wdb.h"
+
+#include "../ged_private.h"
+#include "./dplot_reader.h"
+
+enum {
+    DPLOT_INITIAL,
+    DPLOT_SSX_FIRST,
+    DPLOT_SSX,
+    DPLOT_SSX_EVENTS,
+    DPLOT_ISOCSX_FIRST,
+    DPLOT_ISOCSX,
+    DPLOT_ISOCSX_EVENTS,
+    DPLOT_FACE_CURVES,
+    DPLOT_LINKED_CURVES,
+    DPLOT_SPLIT_FACES
+};
+
+struct dplot_info {
+    struct dplot_data fdata;
+    int mode;
+    struct ged *gedp;
+    FILE *logfile;
+    char *prefix;
+    int ssx_idx;
+    int isocsx_idx;
+    int brep1_surf_idx;
+    int brep2_surf_idx;
+    int brep1_surf_count;
+    int brep2_surf_count;
+    int event_idx;
+    int event_count;
+    int brep1_isocsx_count;
+    int isocsx_count;
+};
+
+#define CLEANUP \
+    fclose(info.logfile); \
+    bu_free(info.prefix, "prefix"); \
+    info.prefix = NULL; \
+    if (info.fdata.ssx) bu_free(info.fdata.ssx, "ssx array");
+
+#define RETURN_MORE \
+    CLEANUP \
+    return GED_MORE;
+
+#define RETURN_ERROR \
+    CLEANUP \
+    info.mode = DPLOT_INITIAL; \
+    return GED_ERROR;
+
+HIDDEN int
+dplot_overlay(
+       struct ged *gedp,
+       const char *prefix,
+       const char *infix,
+       int idx,
+       const char *name)
+{
+    const char *cmd_av[] = {"overlay", "[filename]", "1.0", "[name]"};
+    int ret, cmd_ac = sizeof(cmd_av) / sizeof(char *);
+    struct bu_vls overlay_name = BU_VLS_INIT_ZERO;
+
+    bu_vls_printf(&overlay_name, "%s%s%d.plot3", prefix, infix, idx);
+    cmd_av[1] = cmd_av[3] = bu_vls_cstr(&overlay_name);
+    if (name) {
+       cmd_av[3] = name;
+    }
+    ret = ged_overlay(gedp, cmd_ac, cmd_av);
+    bu_vls_free(&overlay_name);
+
+    if (ret != GED_OK) {
+       bu_vls_printf(gedp->ged_result_str, "error overlaying plot\n");
+       return GED_ERROR;
+    }
+    return GED_OK;
+}
+
+HIDDEN int
+dplot_erase_overlay(
+       struct dplot_info *info,
+       const char *name)
+{
+    const int NUM_EMPTY_PLOTS = 13;
+    int i;
+
+    /* We can't actually erase the old plot without its real name,
+     * which is unknown. Instead, we'll write a plot with the same
+     * base name and color, which will overwrite the old one. We
+     * don't actually know the color either, so we resort to writing
+     * an empty plot with the given name using every color we created
+     * plots with.
+     */
+    for (i = 0; i < NUM_EMPTY_PLOTS; ++i) {
+       int ret = dplot_overlay(info->gedp, info->prefix, "_empty", i, name);
+       if (ret != GED_OK) {
+           return ret;
+       }
+    }
+    return GED_OK;
+}
+
+HIDDEN int
+dplot_ssx(
+       struct dplot_info *info)
+{
+    int i, ret;
+
+    /* draw surfaces, skipping intersecting surfaces if in SSI mode */
+    /* TODO: need to name these overlays so I can selectively erase them */
+    for (i = 0; i < info->brep1_surf_count; ++i) {
+       struct bu_vls name = BU_VLS_INIT_ZERO;
+       bu_vls_printf(&name, "%s_brep1_surface%d", info->prefix, i);
+       dplot_erase_overlay(info, bu_vls_cstr(&name));
+       bu_vls_free(&name);
+    }
+    for (i = 0; i < info->brep2_surf_count; ++i) {
+       struct bu_vls name = BU_VLS_INIT_ZERO;
+       bu_vls_printf(&name, "%s_brep2_surface%d", info->prefix, i);
+       dplot_erase_overlay(info, bu_vls_cstr(&name));
+       bu_vls_free(&name);
+    }
+    if (info->mode == DPLOT_SSX_FIRST || info->mode == DPLOT_SSX || info->mode 
== DPLOT_SSX_EVENTS) {
+       for (i = 0; i < info->brep1_surf_count; ++i) {
+           if (info->mode != DPLOT_SSX_FIRST && i == info->brep1_surf_idx) {
+               continue;
+           }
+           ret = dplot_overlay(info->gedp, info->prefix, "_brep1_surface", i, 
NULL);
+           if (ret != GED_OK) {
+               return GED_ERROR;
+           }
+       }
+       for (i = 0; i < info->brep2_surf_count; ++i) {
+           if (info->mode != DPLOT_SSX_FIRST && i == info->brep2_surf_idx) {
+               continue;
+           }
+           ret = dplot_overlay(info->gedp, info->prefix, "_brep2_surface", i, 
NULL);
+           if (ret != GED_OK) {
+               return GED_ERROR;
+           }
+       }
+    }
+
+    /* draw highlighted surface-surface intersection pair */
+    if (info->mode == DPLOT_SSX || info->mode == DPLOT_SSX_EVENTS) {
+       ret = dplot_overlay(info->gedp, info->prefix, 
"_highlight_brep1_surface",
+               info->brep1_surf_idx, "dplot_ssx1");
+       if (ret != GED_OK) {
+           return GED_ERROR;
+       }
+       ret = dplot_overlay(info->gedp, info->prefix, 
"_highlight_brep2_surface",
+               info->brep2_surf_idx, "dplot_ssx2");
+       if (ret != GED_OK) {
+           return GED_ERROR;
+       }
+       if (info->mode == DPLOT_SSX) {
+           /* advance past the completed pair */
+           ++info->ssx_idx;
+       }
+    }
+    if (info->mode == DPLOT_SSX_FIRST) {
+       info->mode = DPLOT_SSX;
+    }
+    if (info->mode == DPLOT_SSX && info->ssx_idx < info->fdata.ssx_count) {
+       bu_vls_printf(info->gedp->ged_result_str, "Press [Enter] to show 
surface-"
+               "surface intersection %d", info->ssx_idx);
+       return GED_MORE;
+    }
+    return GED_OK;
+}
+
+void
+dplot_print_event_legend(struct dplot_info *info)
+{
+    bu_vls_printf(info->gedp->ged_result_str, "yellow = transverse\n");
+    bu_vls_printf(info->gedp->ged_result_str, "white  = tangent\n");
+    bu_vls_printf(info->gedp->ged_result_str, "green  = overlap\n");
+}
+
+HIDDEN int
+dplot_ssx_events(
+       struct dplot_info *info)
+{
+    int ret;
+
+    /* erase old event plots */
+    ret = dplot_erase_overlay(info, "curr_event");
+    if (ret != GED_OK) {
+       return ret;
+    }
+
+    if (info->mode != DPLOT_SSX_EVENTS) {
+       return GED_OK;
+    }
+
+    if (info->event_count > 0) {
+       /* convert event ssx_idx to string */
+       struct bu_vls infix = BU_VLS_INIT_ZERO;
+       bu_vls_printf(&infix, "_ssx%d_event", info->ssx_idx);
+
+       /* plot overlay */
+       ret = dplot_overlay(info->gedp, info->prefix, bu_vls_cstr(&infix),
+               info->event_idx, "curr_event");
+       bu_vls_free(&infix);
+
+       if (ret != GED_OK) {
+           return ret;
+       }
+       if (info->event_idx == 0) {
+           dplot_print_event_legend(info);
+       }
+    }
+    /* advance to next event, or return to initial state */
+    if (++info->event_idx < info->event_count) {
+       bu_vls_printf(info->gedp->ged_result_str, "Press [Enter] to show next 
event\n");
+       return GED_MORE;
+    }
+    info->mode = DPLOT_INITIAL;
+    return GED_OK;
+}
+
+HIDDEN int
+dplot_isocsx(
+       struct dplot_info *info)
+{
+    if (info->mode != DPLOT_ISOCSX &&
+           info->mode != DPLOT_ISOCSX_FIRST &&
+           info->mode != DPLOT_ISOCSX_EVENTS)
+    {
+       return GED_OK;
+    }
+
+    if (info->fdata.ssx[info->ssx_idx].isocsx_events == NULL) {
+       bu_vls_printf(info->gedp->ged_result_str, "The isocurves of neither "
+               "surface intersected the opposing surface in surface-surface"
+               " intersection %d.\n", info->ssx_idx);
+       info->mode = DPLOT_INITIAL;
+       return GED_OK;
+    }
+
+    dplot_overlay(info->gedp, info->prefix, "_brep1_surface",
+           info->brep1_surf_idx, "isocsx_b1");
+    dplot_overlay(info->gedp, info->prefix, "_brep2_surface",
+           info->brep2_surf_idx, "isocsx_b2");
+
+    if (info->mode == DPLOT_ISOCSX) {
+       struct bu_vls infix = BU_VLS_INIT_ZERO;
+       /* plot surface and the isocurve that intersects it */
+       if (info->isocsx_idx < info->brep1_isocsx_count) {
+           dplot_overlay(info->gedp, info->prefix, "_highlight_brep2_surface",
+                   info->brep2_surf_idx, "isocsx_b2");
+       } else {
+           dplot_overlay(info->gedp, info->prefix, "_highlight_brep1_surface",
+                   info->brep1_surf_idx, "isocsx_b1");
+       }
+       bu_vls_printf(&infix, "_highlight_ssx%d_isocurve", info->ssx_idx);
+       dplot_overlay(info->gedp, info->prefix, bu_vls_cstr(&infix), 
info->isocsx_idx, "isocsx_isocurve");
+       bu_vls_free(&infix);
+    }
+
+    if (info->mode == DPLOT_ISOCSX_FIRST ||
+           info->mode == DPLOT_ISOCSX)
+    {
+       if (info->mode == DPLOT_ISOCSX_FIRST ||
+               ++info->isocsx_idx < info->isocsx_count)
+       {
+           bu_vls_printf(info->gedp->ged_result_str, "Press [Enter] to show "
+                   "isocurve-surface intersection %d", info->isocsx_idx);
+           info->mode = DPLOT_ISOCSX;
+           return GED_MORE;
+       } else {
+           info->mode = DPLOT_INITIAL;
+       }
+    }
+    return GED_OK;
+}
+
+HIDDEN int
+dplot_isocsx_events(struct dplot_info *info)
+{
+    int ret;
+
+    if (info->mode != DPLOT_ISOCSX_EVENTS) {
+       return GED_OK;
+    }
+    ret = dplot_erase_overlay(info, "curr_event");
+    if (ret != GED_OK) {
+       return ret;
+    }
+    if (info->event_count > 0) {
+       /* convert event ssx_idx to string */
+       struct bu_vls infix = BU_VLS_INIT_ZERO;
+       bu_vls_printf(&infix, "_ssx%d_isocsx%d_event", info->ssx_idx,
+               info->isocsx_idx);
+
+       /* plot overlay */
+       ret = dplot_overlay(info->gedp, info->prefix, bu_vls_cstr(&infix),
+               info->event_idx, "curr_event");
+       bu_vls_free(&infix);
+
+       if (ret != GED_OK) {
+           bu_vls_printf(info->gedp->ged_result_str,
+                   "error overlaying plot\n");
+           return ret;
+       }
+       if (info->event_idx == 0) {
+           dplot_print_event_legend(info);
+       }
+    }
+    /* advance to next event, or return to initial state */
+    if (++info->event_idx < info->event_count) {
+       bu_vls_printf(info->gedp->ged_result_str,
+               "Press [Enter] to show next event\n");
+       return GED_MORE;
+    }
+
+    info->mode = DPLOT_INITIAL;
+    return GED_OK;
+}
+
+HIDDEN int
+dplot_face_curves(struct dplot_info *info)
+{
+    int f1_curves, f2_curves;
+    if (info->mode != DPLOT_FACE_CURVES) {
+       return GED_OK;
+    }
+
+    f1_curves = info->fdata.ssx[info->ssx_idx].face1_clipped_curves;
+    f2_curves = info->fdata.ssx[info->ssx_idx].face2_clipped_curves;
+    info->event_count = f1_curves + f2_curves;
+
+    if (info->event_count == 0) {
+       bu_vls_printf(info->gedp->ged_result_str, "No clipped curves for ssx"
+               " pair %d.\n", info->ssx_idx);
+       return GED_OK;
+    }
+
+    if (info->event_idx < info->event_count) {
+       struct bu_vls prefix;
+
+       dplot_overlay(info->gedp, info->prefix, "_brep1_surface",
+               info->brep1_surf_idx, "face_b1");
+       dplot_overlay(info->gedp, info->prefix, "_brep2_surface",
+               info->brep2_surf_idx, "face_b2");
+
+       BU_VLS_INIT(&prefix);
+       bu_vls_printf(&prefix, "%s_ssx%d", info->prefix, info->ssx_idx);
+       dplot_erase_overlay(info, "clipped_fcurve");
+       if (info->event_idx < f1_curves) {
+           bu_vls_printf(&prefix, "_brep1face_clipped_curve");
+           dplot_overlay(info->gedp, bu_vls_cstr(&prefix), "",
+                   info->event_idx, "clipped_fcurve");
+       } else {
+           bu_vls_printf(&prefix, "_brep2face_clipped_curve");
+           dplot_overlay(info->gedp, bu_vls_cstr(&prefix), "",
+                   info->event_idx - f1_curves, "clipped_fcurve");
+       }
+       ++info->event_idx;
+       if (info->event_idx < info->event_count) {
+           bu_vls_printf(info->gedp->ged_result_str, "Press [Enter] to show 
the"
+                   " next curve.");
+           return GED_MORE;
+       }
+    }
+
+    info->mode = DPLOT_INITIAL;
+    return GED_OK;
+}
+
+HIDDEN int
+dplot_split_faces(
+       struct dplot_info *info)
+{
+    int i, j;
+    struct bu_vls name = BU_VLS_INIT_ZERO;
+    struct bu_vls short_name = BU_VLS_INIT_ZERO;
+    struct split_face split_face;
+
+    if (info->mode != DPLOT_SPLIT_FACES) {
+       return GED_OK;
+    }
+
+    if (info->event_idx >= info->fdata.split_face_count) {
+       for (i = 0; i < info->fdata.split_face_count; ++i) {
+           split_face = info->fdata.face[i];
+
+           bu_vls_trunc(&name, 0);
+           bu_vls_printf(&name, "_split_face%d_outerloop_curve", i);
+           for (j = 0; j < split_face.outerloop_curves; ++j) {
+               bu_vls_trunc(&short_name, 0);
+               bu_vls_printf(&short_name, "sf%do%d", i, j);
+               dplot_overlay(info->gedp, info->prefix, bu_vls_cstr(&name), j,
+                       bu_vls_cstr(&short_name));
+           }
+
+           bu_vls_trunc(&name, 0);
+           bu_vls_printf(&name, "_split_face%d_innerloop_curve", i);
+           for (j = 0; j < split_face.innerloop_curves; ++j) {
+               bu_vls_trunc(&short_name, 0);
+               bu_vls_printf(&short_name, "sf%di%d", i, j);
+               dplot_overlay(info->gedp, info->prefix, bu_vls_cstr(&name), j,
+                       bu_vls_cstr(&short_name));
+           }
+       }
+    } else {
+       if (info->event_idx > 0) {
+           /* erase curves of previous split face */
+           split_face = info->fdata.face[info->event_idx - 1];
+           for (i = 0; i < split_face.outerloop_curves; ++i) {
+               bu_vls_trunc(&short_name, 0);
+               bu_vls_printf(&short_name, "sfo%d", i);
+               dplot_erase_overlay(info, bu_vls_cstr(&short_name));
+           }
+           for (i = 0; i < split_face.innerloop_curves; ++i) {
+               bu_vls_trunc(&short_name, 0);
+               bu_vls_printf(&short_name, "sfi%d", i);
+               dplot_erase_overlay(info, bu_vls_cstr(&short_name));
+           }
+       }
+
+       split_face = info->fdata.face[info->event_idx];
+       bu_vls_printf(&name, "_split_face%d_outerloop_curve", info->event_idx);
+       for (i = 0; i < info->fdata.face[info->event_idx].outerloop_curves; 
++i) {
+           bu_vls_trunc(&short_name, 0);
+           bu_vls_printf(&short_name, "sfo%d", i);
+
+           dplot_erase_overlay(info, bu_vls_cstr(&short_name));
+           dplot_overlay(info->gedp, info->prefix, bu_vls_cstr(&name), i,
+                   bu_vls_cstr(&short_name));
+       }
+
+       bu_vls_trunc(&name, 0);
+       bu_vls_printf(&name, "_split_face%d_innerloop_curve", info->event_idx);
+       for (i = 0; i < info->fdata.face[info->event_idx].innerloop_curves; 
++i) {
+           bu_vls_trunc(&short_name, 0);
+           bu_vls_printf(&short_name, "sfi%d", i);
+
+           dplot_erase_overlay(info, bu_vls_cstr(&short_name));
+           dplot_overlay(info->gedp, info->prefix, bu_vls_cstr(&name), i,
+                   bu_vls_cstr(&short_name));
+       }
+
+       bu_vls_printf(info->gedp->ged_result_str, "Press [Enter] to show "
+               "split face %d", ++info->event_idx);
+       return GED_MORE;
+    }
+    return GED_OK;
+}
+
+HIDDEN int
+dplot_linked_curves(
+       struct dplot_info *info)
+{
+    int i;
+    if (info->mode != DPLOT_LINKED_CURVES) {
+       return GED_OK;
+    }
+
+    if (info->event_idx >= info->fdata.linked_curve_count) {
+       for (i = 0; i < info->fdata.linked_curve_count; ++i) {
+           dplot_overlay(info->gedp, info->prefix, "_linked_curve", i, NULL);
+       }
+    } else {
+       dplot_overlay(info->gedp, info->prefix, "_linked_curve",
+               info->event_idx, "linked_curve");
+       bu_vls_printf(info->gedp->ged_result_str, "Press [Enter] to show "
+               "linked curve %d", ++info->event_idx);
+       return GED_MORE;
+    }
+    return GED_OK;
+}
+
+HIDDEN void *
+dplot_malloc(size_t s) {
+    return bu_malloc(s, "dplot_malloc");
+}
+
+HIDDEN void
+dplot_free(void *p) {
+    bu_free(p, "dplot_free");
+}
+
+HIDDEN void
+dplot_load_file_data(struct dplot_info *info)
+{
+    int i, j;
+    struct ssx *curr_ssx;
+    struct isocsx *curr_isocsx;
+    int token_id;
+    perplex_t scanner;
+    void *parser;
+
+    /* initialize scanner and parser */
+    parser = ParseAlloc(dplot_malloc);
+    scanner = perplexFileScanner(info->logfile);
+
+    info->fdata.brep1_surface_count = info->fdata.brep2_surface_count = 0;
+    info->fdata.ssx_count = 0;
+    BU_LIST_INIT(&info->fdata.ssx_list);
+    BU_LIST_INIT(&info->fdata.isocsx_list);
+    perplexSetExtra(scanner, (void *)&info->fdata);
+
+    /* parse */
+    while ((token_id = yylex(scanner)) != YYEOF) {
+       Parse(parser, token_id, info->fdata.token_data, &info->fdata);
+    }
+    Parse(parser, 0, info->fdata.token_data, &info->fdata);
+
+    /* clean up */
+    ParseFree(parser, dplot_free);
+    perplexFree(scanner);
+
+    /* move ssx to dynamic array for easy access */
+    info->fdata.ssx = NULL;
+    if (info->fdata.ssx_count > 0) {
+
+       info->fdata.ssx = (struct ssx *)bu_malloc(
+               sizeof(struct ssx) * info->fdata.ssx_count, "ssx array");
+
+       i = info->fdata.ssx_count - 1;
+       while (BU_LIST_WHILE(curr_ssx, ssx, &info->fdata.ssx_list)) {
+           BU_LIST_DEQUEUE(&curr_ssx->l);
+
+           curr_ssx->isocsx_events = NULL;
+           if (curr_ssx->intersecting_isocurves > 0) {
+               curr_ssx->isocsx_events = (int *)bu_malloc(sizeof(int) *
+                       curr_ssx->intersecting_isocurves, "isocsx array");
+
+               j = curr_ssx->intersecting_isocurves - 1;
+               while (BU_LIST_WHILE(curr_isocsx, isocsx, 
&curr_ssx->isocsx_list)) {
+                   BU_LIST_DEQUEUE(&curr_isocsx->l);
+                   curr_ssx->isocsx_events[j--] = curr_isocsx->events;
+                   BU_PUT(curr_isocsx, struct isocsx);
+               }
+           }
+           info->fdata.ssx[i--] = *curr_ssx;
+           BU_PUT(curr_ssx, struct ssx);
+       }
+    }
+}
+
+int
+ged_dplot_core(struct ged *gedp, int argc, const char *argv[])
+{
+    static struct dplot_info info;
+    int ret;
+    const char *filename, *cmd;
+    char *dot;
+
+    info.gedp = gedp;
+    bu_vls_trunc(gedp->ged_result_str, 0);
+
+    if (argc < 3) {
+       bu_vls_printf(gedp->ged_result_str, "usage: %s logfile cmd\n",
+               argv[0]);
+       bu_vls_printf(gedp->ged_result_str, "  where cmd is one of:\n");
+       bu_vls_printf(gedp->ged_result_str,
+               "      ssx     (show intersecting surface pairs)\n");
+       bu_vls_printf(gedp->ged_result_str,
+               "      ssx N   (show intersections of ssx pair N)\n");
+       bu_vls_printf(gedp->ged_result_str,
+               "   isocsx N   (show intersecting isocurve-surface pairs of ssx 
pair N)\n");
+       bu_vls_printf(gedp->ged_result_str,
+               "   isocsx N M (show intersections of ssx pair N, isocsx pair 
M)\n");
+       bu_vls_printf(gedp->ged_result_str,
+               "  fcurves N   (show clipped face curves of ssx pair N)\n");
+       bu_vls_printf(gedp->ged_result_str,
+               "  lcurves     (show linked ssx curves used to split faces)\n");
+       bu_vls_printf(gedp->ged_result_str,
+               "    faces     (show split faces used to construct result)\n");
+       return GED_HELP;
+    }
+    filename = argv[1];
+    cmd = argv[2];
+
+    if (info.mode == DPLOT_INITIAL) {
+       if (BU_STR_EQUAL(cmd, "ssx") && argc == 3) {
+           info.mode = DPLOT_SSX_FIRST;
+           info.ssx_idx = 0;
+       } else if (BU_STR_EQUAL(cmd, "ssx") && argc == 4) {
+           /* parse surface pair index */
+           const char *idx_str = argv[3];
+           ret = bu_sscanf(idx_str, "%d", &info.ssx_idx);
+           if (ret != 1) {
+               bu_vls_printf(gedp->ged_result_str, "%s is not a valid "
+                       "surface pair (must be a non-negative integer)\n", 
idx_str);
+               return GED_ERROR;
+           }
+           info.mode = DPLOT_SSX_EVENTS;
+           info.event_idx = 0;
+       } else if (BU_STR_EQUAL(cmd, "isocsx") && argc == 4) {
+           const char *idx_str = argv[3];
+           ret = bu_sscanf(idx_str, "%d", &info.ssx_idx);
+           if (ret != 1) {
+               bu_vls_printf(gedp->ged_result_str, "%s is not a valid "
+                       "surface pair (must be a non-negative integer)\n", 
idx_str);
+               return GED_ERROR;
+           }
+           info.mode = DPLOT_ISOCSX_FIRST;
+           info.isocsx_idx = 0;
+       } else if (BU_STR_EQUAL(cmd, "isocsx") && argc == 5) {
+           /* parse surface pair index */
+           const char *idx_str = argv[3];
+           ret = bu_sscanf(idx_str, "%d", &info.ssx_idx);
+           if (ret != 1) {
+               bu_vls_printf(gedp->ged_result_str, "%s is not a valid "
+                       "surface pair (must be a non-negative integer)\n", 
idx_str);
+               return GED_ERROR;
+           }
+           idx_str = argv[4];
+           ret = bu_sscanf(idx_str, "%d", &info.isocsx_idx);
+           if (ret != 1) {
+               bu_vls_printf(gedp->ged_result_str, "%s is not a valid "
+                       "isocurve-surface pair (must be a non-negative 
integer)\n", idx_str);
+               return GED_ERROR;
+           }
+           info.mode = DPLOT_ISOCSX_EVENTS;
+           info.event_idx = 0;
+       } else if (BU_STR_EQUAL(cmd, "fcurves") && argc == 4) {
+           const char *idx_str = argv[3];
+           ret = bu_sscanf(idx_str, "%d", &info.ssx_idx);
+           if (ret != 1) {
+               bu_vls_printf(gedp->ged_result_str, "%s is not a valid "
+                       "surface pair (must be a non-negative integer)\n", 
idx_str);
+               return GED_ERROR;
+           }
+           info.mode = DPLOT_FACE_CURVES;
+           info.event_idx = 0;
+       } else if (BU_STR_EQUAL(cmd, "lcurves") && argc == 3) {
+           info.mode = DPLOT_LINKED_CURVES;
+           info.event_idx = 0;
+       } else if (BU_STR_EQUAL(cmd, "faces") && argc == 3) {
+           info.mode = DPLOT_SPLIT_FACES;
+           info.event_idx = 0;
+       } else {
+           bu_vls_printf(gedp->ged_result_str, "%s is not a recognized "
+                   "command or was given the wrong number of arguments\n",
+                   cmd);
+           return GED_ERROR;
+       }
+    }
+
+    /* open dplot log file */
+    info.logfile = fopen(filename, "r");
+    if (!info.logfile) {
+       bu_vls_printf(gedp->ged_result_str, "couldn't open log file \"%s\"\n", 
filename);
+       return GED_ERROR;
+    }
+
+    /* filename before '.' is assumed to be the prefix for all
+     * plot-file names
+     */
+    info.prefix = bu_strdup(filename);
+    dot = strchr(info.prefix, '.');
+    if (dot) {
+       *dot = '\0';
+    }
+
+    dplot_load_file_data(&info);
+
+    if (info.mode == DPLOT_SSX_FIRST   ||
+           info.mode == DPLOT_SSX              ||
+           info.mode == DPLOT_SSX_EVENTS       ||
+           info.mode == DPLOT_ISOCSX_FIRST     ||
+           info.mode == DPLOT_ISOCSX   ||
+           info.mode == DPLOT_ISOCSX_EVENTS)
+    {
+       if (info.fdata.ssx_count == 0) {
+           bu_vls_printf(info.gedp->ged_result_str, "no surface surface"
+                   "intersections");
+           RETURN_ERROR;
+       } else if (info.ssx_idx < 0 ||
+               info.ssx_idx > (info.fdata.ssx_count - 1))
+       {
+           bu_vls_printf(info.gedp->ged_result_str, "no surface pair %d (valid"

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