diff --git a/README.txt b/README.txt
index 843ec44..7585bbf 100644
--- a/README.txt
+++ b/README.txt
@@ -1,523 +1,523 @@
Name
qhull, rbox 2012.1 2012/02/18
Convex hull, Delaunay triangulation, Voronoi diagrams, Halfspace intersection
Documentation:
html/index.htm
http://www.qhull.org/html
Available from:
Up: Qhull Home Page Qhull
computes the convex hull, Delaunay triangulation, Voronoi diagram, halfspace
intersection about a point, furthest-site Delaunay
triangulation, and furthest-site Voronoi diagram. It
runs in 2-d, 3-d, 4-d, and higher dimensions. It
implements the Quickhull algorithm for computing the
convex hull. Qhull handles roundoff errors from floating
point arithmetic. It can approximate a convex hull. Visit Qhull News
for news, bug reports, change history, and users.
If you use Qhull 2003.1 or 2009.1, please upgrade to 2012.1 or apply
poly.c-qh_gethash.patch. Type: console programs for
- Windows Type: console programs for Windows 7 (32- or 64-bit), XP, and NT Includes executables, documentation, and sources files. It runs in a
command window. Type: C/C++ source code for 32-bit and 64-bit architectures Includes documentation, source files, two Makefile's, CMakeLists.txt, DevStudio projects, and Qt projects.
Includes preliminary C++ support and a preliminary Debian/autoconf build. Debian, rpm, and Autoconf distributions are needed. Type: git repository for Qhull. See Changes.txt Includes documentation, source files, C++ interface, and test programs. It builds with gcc, mingw,
CMake, DevStudio, and Qt Creator.
Type: Rpm build for OpenSuse 12.1
+ Type: PDF on ACM Digital Library (from this page only) Barber, C.B., Dobkin, D.P., and Huhdanpaa, H.T.,
+ "The Quickhull algorithm for convex hulls," ACM
+ Transactions on Mathematical Software, 22(4):469-483, Dec 1996 [abstract]. Type: Out-of-date C source code for Unix systems. Requires patch
- poly.c-qh_gethash.patch
+ Type: Out-of-date C source code for Unix systems. It may segfault if run with more than 2G memory (fix with
+ poly.c-qh_gethash.patch)
Qhull 2009.1 for Octave with Autoconf/Automake/Libtool
support [R. Laboissiere]. If using gcc 4.1, 4.2, or 4.3, please compile qset.c with
-fno-strict-aliasing (otherwise qhull segfaults) [Karas, Krishnaswami].
See Bugs [Apr 2008].
Type: Out-of-date rpm build for Mandrake 9.2 and RedHat
- [qhull.spec].
- Requires patch poly.c-qh_gethash.patch
- For other Linux systems, use the out-of-date src rpm [L. Mazet].
- Requires patch poly.c-qh_gethash.patch
- Type: PDF on ACM Digital Library (from this page only) Barber, C.B., Dobkin, D.P., and Huhdanpaa, H.T.,
- "The Quickhull algorithm for convex hulls," ACM
- Transactions on Mathematical Software, 22(4):469-483, Dec 1996 [abstract]. Type: C source code for
32-bit architectures Version 1.0 is a fifth the size of version 2.4. It
computes convex hulls and Delaunay triangulations. If a
precision error occurs, it stops with an error message.
It reports an initialization error for inputs made with
0/1 coordinates. Version 1.0 compiles on a PC with Borland C++ 4.02 for
Win32 and DOS Power Pack. The options for rbox are
"bcc32 -WX -w- -O2-e -erbox -lc rbox.c". The
options for qhull are the same. [D. Zwick] Up: Qhull Home Page Comments to: qhull@qhull.org
Qhull Downloads
diff --git a/index.htm b/index.htm
index 5a8cc94..2053789 100644
--- a/index.htm
+++ b/index.htm
@@ -1,278 +1,279 @@
To:
News
Download
CiteSeer
Images
Manual
FAQ
Programs
Options
Qhull |
Qhull computes the convex hull, Delaunay triangulation, Voronoi diagram,
halfspace intersection about a point, furthest-site Delaunay
triangulation, and furthest-site Voronoi diagram. The source code runs in
2-d, 3-d, 4-d, and higher dimensions. Qhull implements the Quickhull
algorithm for computing the convex hull. It handles roundoff
errors from floating point arithmetic. It computes volumes,
surface areas, and approximations to the convex hull.
Qhull does not support triangulation of non-convex surfaces, mesh generation of non-convex objects, medium-sized inputs in 9-D and higher, alpha shapes, weighted Voronoi diagrams, Voronoi volumes, or constrained Delaunay triangulations, Qhull 2012.1 fixes qhull-go for Windows 64-bit. If you use Qhull 2003.1. please upgrade to 2012.1 or apply poly.c-qh_gethash.patch. |
Introduction
Qhull Documentation and Support
|
Related URLs
FAQs and Newsgroups
The program includes options for input transformations, randomization, tracing, multiple output formats, and execution statistics. The program can be called from within your application.
You can view the results in 2-d, 3-d and 4-d with Geomview. An alternative is VTK.
For an article about Qhull, download from ACM or CiteSeer:
Barber, C.B., Dobkin, D.P., and Huhdanpaa, H.T., "The Quickhull algorithm for convex hulls," ACM Trans. on Mathematical Software, 22(4):469-483, Dec 1996, http://www.qhull.org
Abstract:
The convex hull of a set of points is the smallest convex set that contains the points. This article presents a practical convex hull algorithm that combines the two-dimensional Quickhull Algorithm with the general dimension Beneath-Beyond Algorithm. It is similar to the randomized, incremental algorithms for convex hull and Delaunay triangulation. We provide empirical evidence that the algorithm runs faster when the input contains non-extreme points, and that it uses less memory.
Computational geometry algorithms have traditionally assumed that input sets are well behaved. When an algorithm is implemented with floating point arithmetic, this assumption can lead to serious errors. We briefly describe a solution to this problem when computing the convex hull in two, three, or four dimensions. The output is a set of "thick" facets that contain all possible exact convex hulls of the input. A variation is effective in five or more dimensions.
Up: Past Software
Projects of the Geometry Center
URL: http://www.qhull.org
To:
News
Download
CiteSeer
Images
Manual
FAQ
Programs
Options
Comments to: qhull@qhull.org
Created: May 17 1995 ---
diff --git a/src/Changes.txt b/src/Changes.txt
index 0e38be1..14c46bc 100644
--- a/src/Changes.txt
+++ b/src/Changes.txt
@@ -1,1626 +1,1629 @@
.............This file lists all changes to qhull and rbox.....................
------------
Need help
- Qhull needs RPM and Debian builds (CMake's CPackRMP and CPackDeb).
- Add an Autotools build for everything
- Set up debian build [octave-maintainers]
Please keep debian files in config/
- Qhull needs a mirror/archive site for old and new builds
- The C++ interface needs work. Give it a try and make it better.
http://gitorious.org/qhull/
- Document with Qt conventions using Doxygen (//! and //!<)
- Produce conformant triangulations for merged facets using option 'Qt'
To do
- Review email for doc changes before jun'10
- Git: Create signed tags for Qhull versions
- Wiki: Add FIXUP to QH11026 for known problems
- Notes to compgeom on conformant triangulation and Voronoi volume
- Set File Ver and Product Ver in dll
- For the C++ interface, make qh_qh a parameter instead of a global static
This replaces qh_QHpointer. It simplifies multithreading.
- Compute the convex hull of each Voronoi or Delaunay region
- Performance test of qset and qhull
+ - qh-get.htm: List the Window OSs for Qhull [T. Winkler]
+ - Reconsider Qhull version numbering (e.g., Traveler 4/5/12)
+
------------
Qhull 2012.1 2012/02/18 6.3.1.1494
- Fix CMakeLists for libqhull with MATCHES [P. Gajdos]
------------
Qhull 2012.1 2012/02/18 6.3.1.1490
Code changes
- Require option 'Qz' for Delaunay triangulation/Voronoi diagram
of cocircular/cospherical points [D. Sheehy]
- qh_errexit: Do not call qh_printsummary or qh_printstats on qh_ERRinput
- Change error QH6227 (all degenerate) from qh_ERRinput to qh_ERRprec
- Change error QH6159 (ID overflow) from qh_ERRinput to qh_ERRqhull
- eg/q_eg, q_egtest, q_test: Run if qconvex is in $PATH [M. Atzeri]
Build changes [M. Atzeri]
- Install to share/doc/qhull instead of share/doc/packages/qhull
- On Unix systems, install to share/man/man1 instead of man/man1
- CMakeLists: Remove the installation of user_eg* and testqset
- CMakeLists: Remove VERSION from qhull executables and libraries
- CMakeLists: Define qhull_SOVERSION instead of qhull_MAJOR
- CMakeLists: Set SOVERSION for shared libraries
- Rename libraries to qhull, qhull_d, qhull_p, and qhull_pd
libqhull6_p.vcproj is now libqhull_p.vcproj
mingw builds as libqhull.dll
cygwin builds as cygqhull-6.dll
linux builds as libqhull.so.6.3.1 with symbolic link as libqhull.so
- Developers using qhull 2011:
libqhull6.so is now libqhull_p.so. Do not use libqhull.so.
qhull6.dll is now qhull_p.dll. Do not use qhull.dll.
- Merged road/ into libqhullcpp/ and qhulltest/
Moved RoadLogEvent.* and RoadError.* to libqhullcpp
Moved RoadTest.* to qhulltest (requires Qt)
Installed RoadTest.h in libqhullcpp
Doc changes
- index.htm: Mathworks uses qhull for n-d
- qhull.htm: Fix qhull for qconvex
- qdelaun.htm/qvoronoi.htm: Use option 'Qz' for circular/cospherical inputs
- make help: Display targets
- Makefile: Better messaging
------------
Qhull 2012.1 2012/02/02 6.3.0.1483
Bug fixes
- Fixed qset.c for -fno-strict-aliasing. This gcc option is no longer needed
(disallow two pointers of differing types to the same memory location)
- Fixed error in qh_setappend_set if first set full and second set empty
- qh_setdelnth, qh_setdelnth_sorted: fixed wording of error message
- qh_setcheck: error message listed size and max backwards.
- qh_setequal: Allow NULL set as documented
- qh_setindex: Allow NULL set as documented
- qh_settemppush: report error if NULL
Code changes
- Add testqset: low level test of qset.c with mem.c
- qh_setendpointer: Implements QSet::endPointer()
- Assigned unique error code for qh_gethash
Build changes
- Added qhull.dll(.so) for Octave and other Debian builds
The global data structure qh_qh is statically defined (no qh_QHpointer)
Linked user_eg2 with qhull.dll (libqhull.so) instead of qhullstatic
Added qh_dllimport to libqhull.h for qhull.dll with MSVC
Changed qhull-app-shared.pri to use libqhull (without qh_QHpointer)
- Renamed libqhull6.so to libqhull6_p.so
Renamed qhull6.dll to qhull6_p.dll
The _p libraries (e.g., libqhull6_p.so) require -Dqh_QHpointer
Renamed qhull6.vcproj to libqhull6_p.vcproj
Added libqhullp/libqhullp.pro for shared library (libqhull6_p.so)
Added qhull-app-sharedp.pri for shared libraries with qh_QHpointer
- Install libqhull/*.htm files into include/libqhull
- Removed libqhull/qhull.h-deprecated [J. Eaton]
- Other changes to Makefile builds
Added 'make qtestall' as a smoketest of each qhull program
src/libqhull/Makefile: Use 'ar -rs ...' instead of ranlib
src/libqhull/Makefile: Fixed targets for cleanall
- Other changes to DevStudio builds
Moved pdb files for qhull libraries to lib/
AdditionalIncludeDirectories: Removed ../src/libqhullcpp
Use build-cmake/ for the DevStudio CMake build
- Other changes to Qt builds
Renamed qhull-libsrc.pri to qhull-libqhull-src.pri
- Added explicit d2u conversions to qhull-zip.sh
- Fixed \n vs. \r\n issues for Windows source files
Draft of Debian/AutoConf build (untested)
- Adjusted the Makefile.am's for the new directory structure.
- Added testqset to bin_PROGRAMS
- config/bootstrap.sh copies program sources into src/libqhull
- Kept qh_QHpointer=0 (static global data structure, qh_qh). It is faster.
Planning a new interface (qhull7?) which passes qh_qh as a parameter
- Added config/changelog from the 2003.1 Debian build
- Moved the debian/patches directory to config/
Optional patches to change smoketest message and turn on qh_QHpointer
- Deleted the debian directory. It was the old Debian build from 2003.1
Rafael Laboissiere's config directory replaced this build..
- Deleted Make-conf.sh (also the old Debian build)
Doc changes
- FAQ: Updated notes on computing volume of a Voronoi region
- Added direct link to ACM Digital Library for downloading the qhull paper
- Added link to Qhull in R
- qset.c: Updated notes about NULL sets
- qh_setappend: clarify qh_setappend for NULL newelem
- qh_setdellast: Fix head note
- Add build/README.txt
- Add uninstall instructions to README.txt and CMakeLists.txt
- Added instructions to create build/*.vcproj to CMakeLists.txt
- Update copyright to 2012
- Updated page links. Added Google books, patents, and blogs.
-----------
Qhull 2009.1.3 2011/12/06
configure: Add -fno-strict-aliasing if $GCC, Required for gcc 4.1+
------------
Qhull 2011.2 2011/11/29 6.2.1.1446
Bug fixes
- qh_new_qhull: Call qh_prepare_output if !outfile [A. Aldoma]
No effect on qhull users since qh_prepare_output is always called.
- Replace Qhull-go.pif with Qhull-go.lnk for Windows 7 64-bit [lots]
- Error if qh_newhashtable, qh_setnew, or qh_memalloc overflows [X. Cheng]
For example, 'rbox 64 D32' overflows hash table for qh_matchnewfacets
Qhull uses 32-bit ints for identifiers, counts, and sizes. See "WARN64"
- q_eg, q_test: change tail +3 to tail -n +3 [N. Dubray, M. Atzeri]
- Qhull-go.bat: Changed 'cmd' to '%comspec%'
Build changes
- Added src/libqhull/Makefile for simple gcc build of executables and lib
- qhulltest.vcproj: Replaced full path to QT with $QTDIR (e.g., c:/qt/4.7.4)
- Split userprintf_rbox.c from userprintf.c,
Otherwise qhull brings in rboxlib and rbox brings in libqhull
- Makefile: qhullx target must be after LIBQHULLS_OBJS
- Makefile: Explicitly list rbox dependencies for qhullx target
- MBorland: Fixed tabs
- Placed $LIBQHULLS_OBJS in same order. Frequently called ones together.
- Update file lists for Make-config.sh [O. Lahaye]
- CMakeLists: add README.txt,etc. to DOC_INSTALL_DIR [M. Atzeri]
- Restored qhull.h-deprecated.
qhull.h conflicts with Qhull.h on Windows systems [C. Abela]
- make-config.sh: Add warning that it is out-of-date
- Remove extra space in '#! /bin/sh' in q_eg, etc. [P. Cheeseman]
Source changes
- libqhull.h: Added qh_True and qh_False for True/False [A. Mutzel]
Did not remove or replace True/False since it is used everywhere
- Moved error message from qh_argv_to_command to caller. Avoids dependency.
- user_eg3.c: Use '10 D2' as default rbox (e.g., 'user_eg3 rbox qhull d')
- user.c, user_eg2.c: Add test of qh_qh as done in user_eg.c
- q_test: Removed duplicate test of qhull C-0.02
Documentation
- index.html: Added ACM Authorizer link to ACM Trans. Math. Software
- Split Delaunay and Voronoi FAQs
- FAQ: How to compute the volume of a Voronoi region [C, Brinch]
- Add 'FS' to qconvex prompt (total area and volume)
- Add clarification to 'Fv' about corner input sites [O. Can]
- Qhull-go.bat: Removed out-of-date advice. Added title.
- qh-code.htm: Updated the discussion of multi-threading for C++ [I. Pirwani]
Qhull 2009.1.2 2011/11/21
- Revert to LF line endings [P. Cheeseman]
- Remove out-of-date material from qhull-go.bat
- Replaced QHULL-GO with a lnk file
Qhull 2011.1 2011/05/23 6.2.0.1385 (exe/dll files unchanged)
- delaunay.vcproj: Fixed qhullstatic_d.lib for debug and minrelsize builds
- Did not redate the distribution
Qhull 2011.1 2011/05/18 6.2.0.1385 (exe/dll files unchanged)
- Add 'm' library to shared and static targets on Unix [A. Bouchard]
Qhull 2011.1 2011/05/14 6.2.0.1383 (exe/dll files unchanged)
- PointCoordinates.cpp: Add #include ---------------------------------
+
+
+ geom2.c
+ infrequently used geometric routines of qhull
+
+ see qh-geom.htm and geom.h
+
+ Copyright (c) 1993-2012 The Geometry Center.
+ $Id: //main/2011/qhull/src/libqhullr/geom2_r.c#1 $$Change: 1640 $
+ $DateTime: 2014/01/15 09:12:08 $$Author: bbarber $
+
+ frequently used code goes into geom.c
+*/
+
+#include "qhull_a.h"
+
+/*================== functions in alphabetic order ============*/
+
+/*---------------------------------
+
+ qh_copypoints( points, numpoints, dimension)
+ return qh_malloc'd copy of points
+*/
+coordT *qh_copypoints(coordT *points, int numpoints, int dimension) {
+ int size;
+ coordT *newpoints;
+
+ size= numpoints * dimension * (int)sizeof(coordT);
+ if (!(newpoints=(coordT*)qh_malloc((size_t)size))) {
+ qh_fprintf(qh ferr, 6004, "qhull error: insufficient memory to copy %d points\n",
+ numpoints);
+ qh_errexit(qh_ERRmem, NULL, NULL);
+ }
+ memcpy((char *)newpoints, (char *)points, (size_t)size);
+ return newpoints;
+} /* copypoints */
+
+/*---------------------------------
+
+ qh_crossproduct( dim, vecA, vecB, vecC )
+ crossproduct of 2 dim vectors
+ C= A x B
+
+ notes:
+ from Glasner, Graphics Gems I, p. 639
+ only defined for dim==3
+*/
+void qh_crossproduct(int dim, realT vecA[3], realT vecB[3], realT vecC[3]){
+
+ if (dim == 3) {
+ vecC[0]= det2_(vecA[1], vecA[2],
+ vecB[1], vecB[2]);
+ vecC[1]= - det2_(vecA[0], vecA[2],
+ vecB[0], vecB[2]);
+ vecC[2]= det2_(vecA[0], vecA[1],
+ vecB[0], vecB[1]);
+ }
+} /* vcross */
+
+/*---------------------------------
+
+ qh_determinant( rows, dim, nearzero )
+ compute signed determinant of a square matrix
+ uses qh.NEARzero to test for degenerate matrices
+
+ returns:
+ determinant
+ overwrites rows and the matrix
+ if dim == 2 or 3
+ nearzero iff determinant < qh NEARzero[dim-1]
+ (!quite correct, not critical)
+ if dim >= 4
+ nearzero iff diagonal[k] < qh NEARzero[k]
+*/
+realT qh_determinant(realT **rows, int dim, boolT *nearzero) {
+ realT det=0;
+ int i;
+ boolT sign= False;
+
+ *nearzero= False;
+ if (dim < 2) {
+ qh_fprintf(qh ferr, 6005, "qhull internal error (qh_determinate): only implemented for dimension >= 2\n");
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }else if (dim == 2) {
+ det= det2_(rows[0][0], rows[0][1],
+ rows[1][0], rows[1][1]);
+ if (fabs_(det) < qh NEARzero[1]) /* not really correct, what should this be? */
+ *nearzero= True;
+ }else if (dim == 3) {
+ det= det3_(rows[0][0], rows[0][1], rows[0][2],
+ rows[1][0], rows[1][1], rows[1][2],
+ rows[2][0], rows[2][1], rows[2][2]);
+ if (fabs_(det) < qh NEARzero[2]) /* not really correct, what should this be? */
+ *nearzero= True;
+ }else {
+ qh_gausselim(rows, dim, dim, &sign, nearzero); /* if nearzero, diagonal still ok*/
+ det= 1.0;
+ for (i=dim; i--; )
+ det *= (rows[i])[i];
+ if (sign)
+ det= -det;
+ }
+ return det;
+} /* determinant */
+
+/*---------------------------------
+
+ qh_detjoggle( points, numpoints, dimension )
+ determine default max joggle for point array
+ as qh_distround * qh_JOGGLEdefault
+
+ returns:
+ initial value for JOGGLEmax from points and REALepsilon
+
+ notes:
+ computes DISTround since qh_maxmin not called yet
+ if qh SCALElast, last dimension will be scaled later to MAXwidth
+
+ loop duplicated from qh_maxmin
+*/
+realT qh_detjoggle(pointT *points, int numpoints, int dimension) {
+ realT abscoord, distround, joggle, maxcoord, mincoord;
+ pointT *point, *pointtemp;
+ realT maxabs= -REALmax;
+ realT sumabs= 0;
+ realT maxwidth= 0;
+ int k;
+
+ for (k=0; k < dimension; k++) {
+ if (qh SCALElast && k == dimension-1)
+ abscoord= maxwidth;
+ else if (qh DELAUNAY && k == dimension-1) /* will qh_setdelaunay() */
+ abscoord= 2 * maxabs * maxabs; /* may be low by qh hull_dim/2 */
+ else {
+ maxcoord= -REALmax;
+ mincoord= REALmax;
+ FORALLpoint_(points, numpoints) {
+ maximize_(maxcoord, point[k]);
+ minimize_(mincoord, point[k]);
+ }
+ maximize_(maxwidth, maxcoord-mincoord);
+ abscoord= fmax_(maxcoord, -mincoord);
+ }
+ sumabs += abscoord;
+ maximize_(maxabs, abscoord);
+ } /* for k */
+ distround= qh_distround(qh hull_dim, maxabs, sumabs);
+ joggle= distround * qh_JOGGLEdefault;
+ maximize_(joggle, REALepsilon * qh_JOGGLEdefault);
+ trace2((qh ferr, 2001, "qh_detjoggle: joggle=%2.2g maxwidth=%2.2g\n", joggle, maxwidth));
+ return joggle;
+} /* detjoggle */
+
+/*---------------------------------
+
+ qh_detroundoff()
+ determine maximum roundoff errors from
+ REALepsilon, REALmax, REALmin, qh.hull_dim, qh.MAXabs_coord,
+ qh.MAXsumcoord, qh.MAXwidth, qh.MINdenom_1
+
+ accounts for qh.SETroundoff, qh.RANDOMdist, qh MERGEexact
+ qh.premerge_cos, qh.postmerge_cos, qh.premerge_centrum,
+ qh.postmerge_centrum, qh.MINoutside,
+ qh_RATIOnearinside, qh_COPLANARratio, qh_WIDEcoplanar
+
+ returns:
+ sets qh.DISTround, etc. (see below)
+ appends precision constants to qh.qhull_options
+
+ see:
+ qh_maxmin() for qh.NEARzero
+
+ design:
+ determine qh.DISTround for distance computations
+ determine minimum denominators for qh_divzero
+ determine qh.ANGLEround for angle computations
+ adjust qh.premerge_cos,... for roundoff error
+ determine qh.ONEmerge for maximum error due to a single merge
+ determine qh.NEARinside, qh.MAXcoplanar, qh.MINvisible,
+ qh.MINoutside, qh.WIDEfacet
+ initialize qh.max_vertex and qh.minvertex
+*/
+void qh_detroundoff(void) {
+
+ qh_option("_max-width", NULL, &qh MAXwidth);
+ if (!qh SETroundoff) {
+ qh DISTround= qh_distround(qh hull_dim, qh MAXabs_coord, qh MAXsumcoord);
+ if (qh RANDOMdist)
+ qh DISTround += qh RANDOMfactor * qh MAXabs_coord;
+ qh_option("Error-roundoff", NULL, &qh DISTround);
+ }
+ qh MINdenom= qh MINdenom_1 * qh MAXabs_coord;
+ qh MINdenom_1_2= sqrt(qh MINdenom_1 * qh hull_dim) ; /* if will be normalized */
+ qh MINdenom_2= qh MINdenom_1_2 * qh MAXabs_coord;
+ /* for inner product */
+ qh ANGLEround= 1.01 * qh hull_dim * REALepsilon;
+ if (qh RANDOMdist)
+ qh ANGLEround += qh RANDOMfactor;
+ if (qh premerge_cos < REALmax/2) {
+ qh premerge_cos -= qh ANGLEround;
+ if (qh RANDOMdist)
+ qh_option("Angle-premerge-with-random", NULL, &qh premerge_cos);
+ }
+ if (qh postmerge_cos < REALmax/2) {
+ qh postmerge_cos -= qh ANGLEround;
+ if (qh RANDOMdist)
+ qh_option("Angle-postmerge-with-random", NULL, &qh postmerge_cos);
+ }
+ qh premerge_centrum += 2 * qh DISTround; /*2 for centrum and distplane()*/
+ qh postmerge_centrum += 2 * qh DISTround;
+ if (qh RANDOMdist && (qh MERGEexact || qh PREmerge))
+ qh_option("Centrum-premerge-with-random", NULL, &qh premerge_centrum);
+ if (qh RANDOMdist && qh POSTmerge)
+ qh_option("Centrum-postmerge-with-random", NULL, &qh postmerge_centrum);
+ { /* compute ONEmerge, max vertex offset for merging simplicial facets */
+ realT maxangle= 1.0, maxrho;
+
+ minimize_(maxangle, qh premerge_cos);
+ minimize_(maxangle, qh postmerge_cos);
+ /* max diameter * sin theta + DISTround for vertex to its hyperplane */
+ qh ONEmerge= sqrt((realT)qh hull_dim) * qh MAXwidth *
+ sqrt(1.0 - maxangle * maxangle) + qh DISTround;
+ maxrho= qh hull_dim * qh premerge_centrum + qh DISTround;
+ maximize_(qh ONEmerge, maxrho);
+ maxrho= qh hull_dim * qh postmerge_centrum + qh DISTround;
+ maximize_(qh ONEmerge, maxrho);
+ if (qh MERGING)
+ qh_option("_one-merge", NULL, &qh ONEmerge);
+ }
+ qh NEARinside= qh ONEmerge * qh_RATIOnearinside; /* only used if qh KEEPnearinside */
+ if (qh JOGGLEmax < REALmax/2 && (qh KEEPcoplanar || qh KEEPinside)) {
+ realT maxdist; /* adjust qh.NEARinside for joggle */
+ qh KEEPnearinside= True;
+ maxdist= sqrt((realT)qh hull_dim) * qh JOGGLEmax + qh DISTround;
+ maxdist= 2*maxdist; /* vertex and coplanar point can joggle in opposite directions */
+ maximize_(qh NEARinside, maxdist); /* must agree with qh_nearcoplanar() */
+ }
+ if (qh KEEPnearinside)
+ qh_option("_near-inside", NULL, &qh NEARinside);
+ if (qh JOGGLEmax < qh DISTround) {
+ qh_fprintf(qh ferr, 6006, "qhull error: the joggle for 'QJn', %.2g, is below roundoff for distance computations, %.2g\n",
+ qh JOGGLEmax, qh DISTround);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ if (qh MINvisible > REALmax/2) {
+ if (!qh MERGING)
+ qh MINvisible= qh DISTround;
+ else if (qh hull_dim <= 3)
+ qh MINvisible= qh premerge_centrum;
+ else
+ qh MINvisible= qh_COPLANARratio * qh premerge_centrum;
+ if (qh APPROXhull && qh MINvisible > qh MINoutside)
+ qh MINvisible= qh MINoutside;
+ qh_option("Visible-distance", NULL, &qh MINvisible);
+ }
+ if (qh MAXcoplanar > REALmax/2) {
+ qh MAXcoplanar= qh MINvisible;
+ qh_option("U-coplanar-distance", NULL, &qh MAXcoplanar);
+ }
+ if (!qh APPROXhull) { /* user may specify qh MINoutside */
+ qh MINoutside= 2 * qh MINvisible;
+ if (qh premerge_cos < REALmax/2)
+ maximize_(qh MINoutside, (1- qh premerge_cos) * qh MAXabs_coord);
+ qh_option("Width-outside", NULL, &qh MINoutside);
+ }
+ qh WIDEfacet= qh MINoutside;
+ maximize_(qh WIDEfacet, qh_WIDEcoplanar * qh MAXcoplanar);
+ maximize_(qh WIDEfacet, qh_WIDEcoplanar * qh MINvisible);
+ qh_option("_wide-facet", NULL, &qh WIDEfacet);
+ if (qh MINvisible > qh MINoutside + 3 * REALepsilon
+ && !qh BESToutside && !qh FORCEoutput)
+ qh_fprintf(qh ferr, 7001, "qhull input warning: minimum visibility V%.2g is greater than \nminimum outside W%.2g. Flipped facets are likely.\n",
+ qh MINvisible, qh MINoutside);
+ qh max_vertex= qh DISTround;
+ qh min_vertex= -qh DISTround;
+ /* numeric constants reported in printsummary */
+} /* detroundoff */
+
+/*---------------------------------
+
+ qh_detsimplex( apex, points, dim, nearzero )
+ compute determinant of a simplex with point apex and base points
+
+ returns:
+ signed determinant and nearzero from qh_determinant
+
+ notes:
+ uses qh.gm_matrix/qh.gm_row (assumes they're big enough)
+
+ design:
+ construct qm_matrix by subtracting apex from points
+ compute determinate
+*/
+realT qh_detsimplex(pointT *apex, setT *points, int dim, boolT *nearzero) {
+ pointT *coorda, *coordp, *gmcoord, *point, **pointp;
+ coordT **rows;
+ int k, i=0;
+ realT det;
+
+ zinc_(Zdetsimplex);
+ gmcoord= qh gm_matrix;
+ rows= qh gm_row;
+ FOREACHpoint_(points) {
+ if (i == dim)
+ break;
+ rows[i++]= gmcoord;
+ coordp= point;
+ coorda= apex;
+ for (k=dim; k--; )
+ *(gmcoord++)= *coordp++ - *coorda++;
+ }
+ if (i < dim) {
+ qh_fprintf(qh ferr, 6007, "qhull internal error (qh_detsimplex): #points %d < dimension %d\n",
+ i, dim);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ det= qh_determinant(rows, dim, nearzero);
+ trace2((qh ferr, 2002, "qh_detsimplex: det=%2.2g for point p%d, dim %d, nearzero? %d\n",
+ det, qh_pointid(apex), dim, *nearzero));
+ return det;
+} /* detsimplex */
+
+/*---------------------------------
+
+ qh_distnorm( dim, point, normal, offset )
+ return distance from point to hyperplane at normal/offset
+
+ returns:
+ dist
+
+ notes:
+ dist > 0 if point is outside of hyperplane
+
+ see:
+ qh_distplane in geom.c
+*/
+realT qh_distnorm(int dim, pointT *point, pointT *normal, realT *offsetp) {
+ coordT *normalp= normal, *coordp= point;
+ realT dist;
+ int k;
+
+ dist= *offsetp;
+ for (k=dim; k--; )
+ dist += *(coordp++) * *(normalp++);
+ return dist;
+} /* distnorm */
+
+/*---------------------------------
+
+ qh_distround(dimension, maxabs, maxsumabs )
+ compute maximum round-off error for a distance computation
+ to a normalized hyperplane
+ maxabs is the maximum absolute value of a coordinate
+ maxsumabs is the maximum possible sum of absolute coordinate values
+
+ returns:
+ max dist round for REALepsilon
+
+ notes:
+ calculate roundoff error according to
+ Lemma 3.2-1 of Golub and van Loan "Matrix Computation"
+ use sqrt(dim) since one vector is normalized
+ or use maxsumabs since one vector is < 1
+*/
+realT qh_distround(int dimension, realT maxabs, realT maxsumabs) {
+ realT maxdistsum, maxround;
+
+ maxdistsum= sqrt((realT)dimension) * maxabs;
+ minimize_( maxdistsum, maxsumabs);
+ maxround= REALepsilon * (dimension * maxdistsum * 1.01 + maxabs);
+ /* adds maxabs for offset */
+ trace4((qh ferr, 4008, "qh_distround: %2.2g maxabs %2.2g maxsumabs %2.2g maxdistsum %2.2g\n",
+ maxround, maxabs, maxsumabs, maxdistsum));
+ return maxround;
+} /* distround */
+
+/*---------------------------------
+
+ qh_divzero( numer, denom, mindenom1, zerodiv )
+ divide by a number that's nearly zero
+ mindenom1= minimum denominator for dividing into 1.0
+
+ returns:
+ quotient
+ sets zerodiv and returns 0.0 if it would overflow
+
+ design:
+ if numer is nearly zero and abs(numer) < abs(denom)
+ return numer/denom
+ else if numer is nearly zero
+ return 0 and zerodiv
+ else if denom/numer non-zero
+ return numer/denom
+ else
+ return 0 and zerodiv
+*/
+realT qh_divzero(realT numer, realT denom, realT mindenom1, boolT *zerodiv) {
+ realT temp, numerx, denomx;
+
+
+ if (numer < mindenom1 && numer > -mindenom1) {
+ numerx= fabs_(numer);
+ denomx= fabs_(denom);
+ if (numerx < denomx) {
+ *zerodiv= False;
+ return numer/denom;
+ }else {
+ *zerodiv= True;
+ return 0.0;
+ }
+ }
+ temp= denom/numer;
+ if (temp > mindenom1 || temp < -mindenom1) {
+ *zerodiv= False;
+ return numer/denom;
+ }else {
+ *zerodiv= True;
+ return 0.0;
+ }
+} /* divzero */
+
+
+/*---------------------------------
+
+ qh_facetarea( facet )
+ return area for a facet
+
+ notes:
+ if non-simplicial,
+ uses centrum to triangulate facet and sums the projected areas.
+ if (qh DELAUNAY),
+ computes projected area instead for last coordinate
+ assumes facet->normal exists
+ projecting tricoplanar facets to the hyperplane does not appear to make a difference
+
+ design:
+ if simplicial
+ compute area
+ else
+ for each ridge
+ compute area from centrum to ridge
+ negate area if upper Delaunay facet
+*/
+realT qh_facetarea(facetT *facet) {
+ vertexT *apex;
+ pointT *centrum;
+ realT area= 0.0;
+ ridgeT *ridge, **ridgep;
+
+ if (facet->simplicial) {
+ apex= SETfirstt_(facet->vertices, vertexT);
+ area= qh_facetarea_simplex(qh hull_dim, apex->point, facet->vertices,
+ apex, facet->toporient, facet->normal, &facet->offset);
+ }else {
+ if (qh CENTERtype == qh_AScentrum)
+ centrum= facet->center;
+ else
+ centrum= qh_getcentrum(facet);
+ FOREACHridge_(facet->ridges)
+ area += qh_facetarea_simplex(qh hull_dim, centrum, ridge->vertices,
+ NULL, (boolT)(ridge->top == facet), facet->normal, &facet->offset);
+ if (qh CENTERtype != qh_AScentrum)
+ qh_memfree(centrum, qh normal_size);
+ }
+ if (facet->upperdelaunay && qh DELAUNAY)
+ area= -area; /* the normal should be [0,...,1] */
+ trace4((qh ferr, 4009, "qh_facetarea: f%d area %2.2g\n", facet->id, area));
+ return area;
+} /* facetarea */
+
+/*---------------------------------
+
+ qh_facetarea_simplex( dim, apex, vertices, notvertex, toporient, normal, offset )
+ return area for a simplex defined by
+ an apex, a base of vertices, an orientation, and a unit normal
+ if simplicial or tricoplanar facet,
+ notvertex is defined and it is skipped in vertices
+
+ returns:
+ computes area of simplex projected to plane [normal,offset]
+ returns 0 if vertex too far below plane (qh WIDEfacet)
+ vertex can't be apex of tricoplanar facet
+
+ notes:
+ if (qh DELAUNAY),
+ computes projected area instead for last coordinate
+ uses qh gm_matrix/gm_row and qh hull_dim
+ helper function for qh_facetarea
+
+ design:
+ if Notvertex
+ translate simplex to apex
+ else
+ project simplex to normal/offset
+ translate simplex to apex
+ if Delaunay
+ set last row/column to 0 with -1 on diagonal
+ else
+ set last row to Normal
+ compute determinate
+ scale and flip sign for area
+*/
+realT qh_facetarea_simplex(int dim, coordT *apex, setT *vertices,
+ vertexT *notvertex, boolT toporient, coordT *normal, realT *offset) {
+ pointT *coorda, *coordp, *gmcoord;
+ coordT **rows, *normalp;
+ int k, i=0;
+ realT area, dist;
+ vertexT *vertex, **vertexp;
+ boolT nearzero;
+
+ gmcoord= qh gm_matrix;
+ rows= qh gm_row;
+ FOREACHvertex_(vertices) {
+ if (vertex == notvertex)
+ continue;
+ rows[i++]= gmcoord;
+ coorda= apex;
+ coordp= vertex->point;
+ normalp= normal;
+ if (notvertex) {
+ for (k=dim; k--; )
+ *(gmcoord++)= *coordp++ - *coorda++;
+ }else {
+ dist= *offset;
+ for (k=dim; k--; )
+ dist += *coordp++ * *normalp++;
+ if (dist < -qh WIDEfacet) {
+ zinc_(Znoarea);
+ return 0.0;
+ }
+ coordp= vertex->point;
+ normalp= normal;
+ for (k=dim; k--; )
+ *(gmcoord++)= (*coordp++ - dist * *normalp++) - *coorda++;
+ }
+ }
+ if (i != dim-1) {
+ qh_fprintf(qh ferr, 6008, "qhull internal error (qh_facetarea_simplex): #points %d != dim %d -1\n",
+ i, dim);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ rows[i]= gmcoord;
+ if (qh DELAUNAY) {
+ for (i=0; i < dim-1; i++)
+ rows[i][dim-1]= 0.0;
+ for (k=dim; k--; )
+ *(gmcoord++)= 0.0;
+ rows[dim-1][dim-1]= -1.0;
+ }else {
+ normalp= normal;
+ for (k=dim; k--; )
+ *(gmcoord++)= *normalp++;
+ }
+ zinc_(Zdetsimplex);
+ area= qh_determinant(rows, dim, &nearzero);
+ if (toporient)
+ area= -area;
+ area *= qh AREAfactor;
+ trace4((qh ferr, 4010, "qh_facetarea_simplex: area=%2.2g for point p%d, toporient %d, nearzero? %d\n",
+ area, qh_pointid(apex), toporient, nearzero));
+ return area;
+} /* facetarea_simplex */
+
+/*---------------------------------
+
+ qh_facetcenter( vertices )
+ return Voronoi center (Voronoi vertex) for a facet's vertices
+
+ returns:
+ return temporary point equal to the center
+
+ see:
+ qh_voronoi_center()
+*/
+pointT *qh_facetcenter(setT *vertices) {
+ setT *points= qh_settemp(qh_setsize(vertices));
+ vertexT *vertex, **vertexp;
+ pointT *center;
+
+ FOREACHvertex_(vertices)
+ qh_setappend(&points, vertex->point);
+ center= qh_voronoi_center(qh hull_dim-1, points);
+ qh_settempfree(&points);
+ return center;
+} /* facetcenter */
+
+/*---------------------------------
+
+ qh_findgooddist( point, facetA, dist, facetlist )
+ find best good facet visible for point from facetA
+ assumes facetA is visible from point
+
+ returns:
+ best facet, i.e., good facet that is furthest from point
+ distance to best facet
+ NULL if none
+
+ moves good, visible facets (and some other visible facets)
+ to end of qh facet_list
+
+ notes:
+ uses qh visit_id
+
+ design:
+ initialize bestfacet if facetA is good
+ move facetA to end of facetlist
+ for each facet on facetlist
+ for each unvisited neighbor of facet
+ move visible neighbors to end of facetlist
+ update best good neighbor
+ if no good neighbors, update best facet
+*/
+facetT *qh_findgooddist(pointT *point, facetT *facetA, realT *distp,
+ facetT **facetlist) {
+ realT bestdist= -REALmax, dist;
+ facetT *neighbor, **neighborp, *bestfacet=NULL, *facet;
+ boolT goodseen= False;
+
+ if (facetA->good) {
+ zzinc_(Zcheckpart); /* calls from check_bestdist occur after print stats */
+ qh_distplane(point, facetA, &bestdist);
+ bestfacet= facetA;
+ goodseen= True;
+ }
+ qh_removefacet(facetA);
+ qh_appendfacet(facetA);
+ *facetlist= facetA;
+ facetA->visitid= ++qh visit_id;
+ FORALLfacet_(*facetlist) {
+ FOREACHneighbor_(facet) {
+ if (neighbor->visitid == qh visit_id)
+ continue;
+ neighbor->visitid= qh visit_id;
+ if (goodseen && !neighbor->good)
+ continue;
+ zzinc_(Zcheckpart);
+ qh_distplane(point, neighbor, &dist);
+ if (dist > 0) {
+ qh_removefacet(neighbor);
+ qh_appendfacet(neighbor);
+ if (neighbor->good) {
+ goodseen= True;
+ if (dist > bestdist) {
+ bestdist= dist;
+ bestfacet= neighbor;
+ }
+ }
+ }
+ }
+ }
+ if (bestfacet) {
+ *distp= bestdist;
+ trace2((qh ferr, 2003, "qh_findgooddist: p%d is %2.2g above good facet f%d\n",
+ qh_pointid(point), bestdist, bestfacet->id));
+ return bestfacet;
+ }
+ trace4((qh ferr, 4011, "qh_findgooddist: no good facet for p%d above f%d\n",
+ qh_pointid(point), facetA->id));
+ return NULL;
+} /* findgooddist */
+
+/*---------------------------------
+
+ qh_getarea( facetlist )
+ set area of all facets in facetlist
+ collect statistics
+ nop if hasAreaVolume
+
+ returns:
+ sets qh totarea/totvol to total area and volume of convex hull
+ for Delaunay triangulation, computes projected area of the lower or upper hull
+ ignores upper hull if qh ATinfinity
+
+ notes:
+ could compute outer volume by expanding facet area by rays from interior
+ the following attempt at perpendicular projection underestimated badly:
+ qh.totoutvol += (-dist + facet->maxoutside + qh DISTround)
+ * area/ qh hull_dim;
+ design:
+ for each facet on facetlist
+ compute facet->area
+ update qh.totarea and qh.totvol
+*/
+void qh_getarea(facetT *facetlist) {
+ realT area;
+ realT dist;
+ facetT *facet;
+
+ if (qh hasAreaVolume)
+ return;
+ if (qh REPORTfreq)
+ qh_fprintf(qh ferr, 8020, "computing area of each facet and volume of the convex hull\n");
+ else
+ trace1((qh ferr, 1001, "qh_getarea: computing volume and area for each facet\n"));
+ qh totarea= qh totvol= 0.0;
+ FORALLfacet_(facetlist) {
+ if (!facet->normal)
+ continue;
+ if (facet->upperdelaunay && qh ATinfinity)
+ continue;
+ if (!facet->isarea) {
+ facet->f.area= qh_facetarea(facet);
+ facet->isarea= True;
+ }
+ area= facet->f.area;
+ if (qh DELAUNAY) {
+ if (facet->upperdelaunay == qh UPPERdelaunay)
+ qh totarea += area;
+ }else {
+ qh totarea += area;
+ qh_distplane(qh interior_point, facet, &dist);
+ qh totvol += -dist * area/ qh hull_dim;
+ }
+ if (qh PRINTstatistics) {
+ wadd_(Wareatot, area);
+ wmax_(Wareamax, area);
+ wmin_(Wareamin, area);
+ }
+ }
+ qh hasAreaVolume= True;
+} /* getarea */
+
+/*---------------------------------
+
+ qh_gram_schmidt( dim, row )
+ implements Gram-Schmidt orthogonalization by rows
+
+ returns:
+ false if zero norm
+ overwrites rows[dim][dim]
+
+ notes:
+ see Golub & van Loan Algorithm 6.2-2
+ overflow due to small divisors not handled
+
+ design:
+ for each row
+ compute norm for row
+ if non-zero, normalize row
+ for each remaining rowA
+ compute inner product of row and rowA
+ reduce rowA by row * inner product
+*/
+boolT qh_gram_schmidt(int dim, realT **row) {
+ realT *rowi, *rowj, norm;
+ int i, j, k;
+
+ for (i=0; i < dim; i++) {
+ rowi= row[i];
+ for (norm= 0.0, k= dim; k--; rowi++)
+ norm += *rowi * *rowi;
+ norm= sqrt(norm);
+ wmin_(Wmindenom, norm);
+ if (norm == 0.0) /* either 0 or overflow due to sqrt */
+ return False;
+ for (k=dim; k--; )
+ *(--rowi) /= norm;
+ for (j=i+1; j < dim; j++) {
+ rowj= row[j];
+ for (norm= 0.0, k=dim; k--; )
+ norm += *rowi++ * *rowj++;
+ for (k=dim; k--; )
+ *(--rowj) -= *(--rowi) * norm;
+ }
+ }
+ return True;
+} /* gram_schmidt */
+
+
+/*---------------------------------
+
+ qh_inthresholds( normal, angle )
+ return True if normal within qh.lower_/upper_threshold
+
+ returns:
+ estimate of angle by summing of threshold diffs
+ angle may be NULL
+ smaller "angle" is better
+
+ notes:
+ invalid if qh.SPLITthresholds
+
+ see:
+ qh.lower_threshold in qh_initbuild()
+ qh_initthresholds()
+
+ design:
+ for each dimension
+ test threshold
+*/
+boolT qh_inthresholds(coordT *normal, realT *angle) {
+ boolT within= True;
+ int k;
+ realT threshold;
+
+ if (angle)
+ *angle= 0.0;
+ for (k=0; k < qh hull_dim; k++) {
+ threshold= qh lower_threshold[k];
+ if (threshold > -REALmax/2) {
+ if (normal[k] < threshold)
+ within= False;
+ if (angle) {
+ threshold -= normal[k];
+ *angle += fabs_(threshold);
+ }
+ }
+ if (qh upper_threshold[k] < REALmax/2) {
+ threshold= qh upper_threshold[k];
+ if (normal[k] > threshold)
+ within= False;
+ if (angle) {
+ threshold -= normal[k];
+ *angle += fabs_(threshold);
+ }
+ }
+ }
+ return within;
+} /* inthresholds */
+
+
+/*---------------------------------
+
+ qh_joggleinput()
+ randomly joggle input to Qhull by qh.JOGGLEmax
+ initial input is qh.first_point/qh.num_points of qh.hull_dim
+ repeated calls use qh.input_points/qh.num_points
+
+ returns:
+ joggles points at qh.first_point/qh.num_points
+ copies data to qh.input_points/qh.input_malloc if first time
+ determines qh.JOGGLEmax if it was zero
+ if qh.DELAUNAY
+ computes the Delaunay projection of the joggled points
+
+ notes:
+ if qh.DELAUNAY, unnecessarily joggles the last coordinate
+ the initial 'QJn' may be set larger than qh_JOGGLEmaxincrease
+
+ design:
+ if qh.DELAUNAY
+ set qh.SCALElast for reduced precision errors
+ if first call
+ initialize qh.input_points to the original input points
+ if qh.JOGGLEmax == 0
+ determine default qh.JOGGLEmax
+ else
+ increase qh.JOGGLEmax according to qh.build_cnt
+ joggle the input by adding a random number in [-qh.JOGGLEmax,qh.JOGGLEmax]
+ if qh.DELAUNAY
+ sets the Delaunay projection
+*/
+void qh_joggleinput(void) {
+ int i, seed, size;
+ coordT *coordp, *inputp;
+ realT randr, randa, randb;
+
+ if (!qh input_points) { /* first call */
+ qh input_points= qh first_point;
+ qh input_malloc= qh POINTSmalloc;
+ size= qh num_points * qh hull_dim * sizeof(coordT);
+ if (!(qh first_point=(coordT*)qh_malloc((size_t)size))) {
+ qh_fprintf(qh ferr, 6009, "qhull error: insufficient memory to joggle %d points\n",
+ qh num_points);
+ qh_errexit(qh_ERRmem, NULL, NULL);
+ }
+ qh POINTSmalloc= True;
+ if (qh JOGGLEmax == 0.0) {
+ qh JOGGLEmax= qh_detjoggle(qh input_points, qh num_points, qh hull_dim);
+ qh_option("QJoggle", NULL, &qh JOGGLEmax);
+ }
+ }else { /* repeated call */
+ if (!qh RERUN && qh build_cnt > qh_JOGGLEretry) {
+ if (((qh build_cnt-qh_JOGGLEretry-1) % qh_JOGGLEagain) == 0) {
+ realT maxjoggle= qh MAXwidth * qh_JOGGLEmaxincrease;
+ if (qh JOGGLEmax < maxjoggle) {
+ qh JOGGLEmax *= qh_JOGGLEincrease;
+ minimize_(qh JOGGLEmax, maxjoggle);
+ }
+ }
+ }
+ qh_option("QJoggle", NULL, &qh JOGGLEmax);
+ }
+ if (qh build_cnt > 1 && qh JOGGLEmax > fmax_(qh MAXwidth/4, 0.1)) {
+ qh_fprintf(qh ferr, 6010, "qhull error: the current joggle for 'QJn', %.2g, is too large for the width\nof the input. If possible, recompile Qhull with higher-precision reals.\n",
+ qh JOGGLEmax);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ /* for some reason, using qh ROTATErandom and qh_RANDOMseed does not repeat the run. Use 'TRn' instead */
+ seed= qh_RANDOMint;
+ qh_option("_joggle-seed", &seed, NULL);
+ trace0((qh ferr, 6, "qh_joggleinput: joggle input by %2.2g with seed %d\n",
+ qh JOGGLEmax, seed));
+ inputp= qh input_points;
+ coordp= qh first_point;
+ randa= 2.0 * qh JOGGLEmax/qh_RANDOMmax;
+ randb= -qh JOGGLEmax;
+ size= qh num_points * qh hull_dim;
+ for (i=size; i--; ) {
+ randr= qh_RANDOMint;
+ *(coordp++)= *(inputp++) + (randr * randa + randb);
+ }
+ if (qh DELAUNAY) {
+ qh last_low= qh last_high= qh last_newhigh= REALmax;
+ qh_setdelaunay(qh hull_dim, qh num_points, qh first_point);
+ }
+} /* joggleinput */
+
+/*---------------------------------
+
+ qh_maxabsval( normal, dim )
+ return pointer to maximum absolute value of a dim vector
+ returns NULL if dim=0
+*/
+realT *qh_maxabsval(realT *normal, int dim) {
+ realT maxval= -REALmax;
+ realT *maxp= NULL, *colp, absval;
+ int k;
+
+ for (k=dim, colp= normal; k--; colp++) {
+ absval= fabs_(*colp);
+ if (absval > maxval) {
+ maxval= absval;
+ maxp= colp;
+ }
+ }
+ return maxp;
+} /* maxabsval */
+
+
+/*---------------------------------
+
+ qh_maxmin( points, numpoints, dimension )
+ return max/min points for each dimension
+ determine max and min coordinates
+
+ returns:
+ returns a temporary set of max and min points
+ may include duplicate points. Does not include qh.GOODpoint
+ sets qh.NEARzero, qh.MAXabs_coord, qh.MAXsumcoord, qh.MAXwidth
+ qh.MAXlastcoord, qh.MINlastcoord
+ initializes qh.max_outside, qh.min_vertex, qh.WAScoplanar, qh.ZEROall_ok
+
+ notes:
+ loop duplicated in qh_detjoggle()
+
+ design:
+ initialize global precision variables
+ checks definition of REAL...
+ for each dimension
+ for each point
+ collect maximum and minimum point
+ collect maximum of maximums and minimum of minimums
+ determine qh.NEARzero for Gaussian Elimination
+*/
+setT *qh_maxmin(pointT *points, int numpoints, int dimension) {
+ int k;
+ realT maxcoord, temp;
+ pointT *minimum, *maximum, *point, *pointtemp;
+ setT *set;
+
+ qh max_outside= 0.0;
+ qh MAXabs_coord= 0.0;
+ qh MAXwidth= -REALmax;
+ qh MAXsumcoord= 0.0;
+ qh min_vertex= 0.0;
+ qh WAScoplanar= False;
+ if (qh ZEROcentrum)
+ qh ZEROall_ok= True;
+ if (REALmin < REALepsilon && REALmin < REALmax && REALmin > -REALmax
+ && REALmax > 0.0 && -REALmax < 0.0)
+ ; /* all ok */
+ else {
+ qh_fprintf(qh ferr, 6011, "qhull error: floating point constants in user.h are wrong\n\
+REALepsilon %g REALmin %g REALmax %g -REALmax %g\n",
+ REALepsilon, REALmin, REALmax, -REALmax);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ set= qh_settemp(2*dimension);
+ for (k=0; k < dimension; k++) {
+ if (points == qh GOODpointp)
+ minimum= maximum= points + dimension;
+ else
+ minimum= maximum= points;
+ FORALLpoint_(points, numpoints) {
+ if (point == qh GOODpointp)
+ continue;
+ if (maximum[k] < point[k])
+ maximum= point;
+ else if (minimum[k] > point[k])
+ minimum= point;
+ }
+ if (k == dimension-1) {
+ qh MINlastcoord= minimum[k];
+ qh MAXlastcoord= maximum[k];
+ }
+ if (qh SCALElast && k == dimension-1)
+ maxcoord= qh MAXwidth;
+ else {
+ maxcoord= fmax_(maximum[k], -minimum[k]);
+ if (qh GOODpointp) {
+ temp= fmax_(qh GOODpointp[k], -qh GOODpointp[k]);
+ maximize_(maxcoord, temp);
+ }
+ temp= maximum[k] - minimum[k];
+ maximize_(qh MAXwidth, temp);
+ }
+ maximize_(qh MAXabs_coord, maxcoord);
+ qh MAXsumcoord += maxcoord;
+ qh_setappend(&set, maximum);
+ qh_setappend(&set, minimum);
+ /* calculation of qh NEARzero is based on error formula 4.4-13 of
+ Golub & van Loan, authors say n^3 can be ignored and 10 be used in
+ place of rho */
+ qh NEARzero[k]= 80 * qh MAXsumcoord * REALepsilon;
+ }
+ if (qh IStracing >=1)
+ qh_printpoints(qh ferr, "qh_maxmin: found the max and min points(by dim):", set);
+ return(set);
+} /* maxmin */
+
+/*---------------------------------
+
+ qh_maxouter()
+ return maximum distance from facet to outer plane
+ normally this is qh.max_outside+qh.DISTround
+ does not include qh.JOGGLEmax
+
+ see:
+ qh_outerinner()
+
+ notes:
+ need to add another qh.DISTround if testing actual point with computation
+
+ for joggle:
+ qh_setfacetplane() updated qh.max_outer for Wnewvertexmax (max distance to vertex)
+ need to use Wnewvertexmax since could have a coplanar point for a high
+ facet that is replaced by a low facet
+ need to add qh.JOGGLEmax if testing input points
+*/
+realT qh_maxouter(void) {
+ realT dist;
+
+ dist= fmax_(qh max_outside, qh DISTround);
+ dist += qh DISTround;
+ trace4((qh ferr, 4012, "qh_maxouter: max distance from facet to outer plane is %2.2g max_outside is %2.2g\n", dist, qh max_outside));
+ return dist;
+} /* maxouter */
+
+/*---------------------------------
+
+ qh_maxsimplex( dim, maxpoints, points, numpoints, simplex )
+ determines maximum simplex for a set of points
+ starts from points already in simplex
+ skips qh.GOODpointp (assumes that it isn't in maxpoints)
+
+ returns:
+ simplex with dim+1 points
+
+ notes:
+ assumes at least pointsneeded points in points
+ maximizes determinate for x,y,z,w, etc.
+ uses maxpoints as long as determinate is clearly non-zero
+
+ design:
+ initialize simplex with at least two points
+ (find points with max or min x coordinate)
+ for each remaining dimension
+ add point that maximizes the determinate
+ (use points from maxpoints first)
+*/
+void qh_maxsimplex(int dim, setT *maxpoints, pointT *points, int numpoints, setT **simplex) {
+ pointT *point, **pointp, *pointtemp, *maxpoint, *minx=NULL, *maxx=NULL;
+ boolT nearzero, maxnearzero= False;
+ int k, sizinit;
+ realT maxdet= -REALmax, det, mincoord= REALmax, maxcoord= -REALmax;
+
+ sizinit= qh_setsize(*simplex);
+ if (sizinit < 2) {
+ if (qh_setsize(maxpoints) >= 2) {
+ FOREACHpoint_(maxpoints) {
+ if (maxcoord < point[0]) {
+ maxcoord= point[0];
+ maxx= point;
+ }
+ if (mincoord > point[0]) {
+ mincoord= point[0];
+ minx= point;
+ }
+ }
+ }else {
+ FORALLpoint_(points, numpoints) {
+ if (point == qh GOODpointp)
+ continue;
+ if (maxcoord < point[0]) {
+ maxcoord= point[0];
+ maxx= point;
+ }
+ if (mincoord > point[0]) {
+ mincoord= point[0];
+ minx= point;
+ }
+ }
+ }
+ qh_setunique(simplex, minx);
+ if (qh_setsize(*simplex) < 2)
+ qh_setunique(simplex, maxx);
+ sizinit= qh_setsize(*simplex);
+ if (sizinit < 2) {
+ qh_precision("input has same x coordinate");
+ if (zzval_(Zsetplane) > qh hull_dim+1) {
+ qh_fprintf(qh ferr, 6012, "qhull precision error (qh_maxsimplex for voronoi_center):\n%d points with the same x coordinate.\n",
+ qh_setsize(maxpoints)+numpoints);
+ qh_errexit(qh_ERRprec, NULL, NULL);
+ }else {
+ qh_fprintf(qh ferr, 6013, "qhull input error: input is less than %d-dimensional since it has the same x coordinate\n", qh hull_dim);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ }
+ }
+ for (k=sizinit; k < dim+1; k++) {
+ maxpoint= NULL;
+ maxdet= -REALmax;
+ FOREACHpoint_(maxpoints) {
+ if (!qh_setin(*simplex, point)) {
+ det= qh_detsimplex(point, *simplex, k, &nearzero);
+ if ((det= fabs_(det)) > maxdet) {
+ maxdet= det;
+ maxpoint= point;
+ maxnearzero= nearzero;
+ }
+ }
+ }
+ if (!maxpoint || maxnearzero) {
+ zinc_(Zsearchpoints);
+ if (!maxpoint) {
+ trace0((qh ferr, 7, "qh_maxsimplex: searching all points for %d-th initial vertex.\n", k+1));
+ }else {
+ trace0((qh ferr, 8, "qh_maxsimplex: searching all points for %d-th initial vertex, better than p%d det %2.2g\n",
+ k+1, qh_pointid(maxpoint), maxdet));
+ }
+ FORALLpoint_(points, numpoints) {
+ if (point == qh GOODpointp)
+ continue;
+ if (!qh_setin(*simplex, point)) {
+ det= qh_detsimplex(point, *simplex, k, &nearzero);
+ if ((det= fabs_(det)) > maxdet) {
+ maxdet= det;
+ maxpoint= point;
+ maxnearzero= nearzero;
+ }
+ }
+ }
+ } /* !maxpoint */
+ if (!maxpoint) {
+ qh_fprintf(qh ferr, 6014, "qhull internal error (qh_maxsimplex): not enough points available\n");
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ qh_setappend(simplex, maxpoint);
+ trace1((qh ferr, 1002, "qh_maxsimplex: selected point p%d for %d`th initial vertex, det=%2.2g\n",
+ qh_pointid(maxpoint), k+1, maxdet));
+ } /* k */
+} /* maxsimplex */
+
+/*---------------------------------
+
+ qh_minabsval( normal, dim )
+ return minimum absolute value of a dim vector
+*/
+realT qh_minabsval(realT *normal, int dim) {
+ realT minval= 0;
+ realT maxval= 0;
+ realT *colp;
+ int k;
+
+ for (k=dim, colp=normal; k--; colp++) {
+ maximize_(maxval, *colp);
+ minimize_(minval, *colp);
+ }
+ return fmax_(maxval, -minval);
+} /* minabsval */
+
+
+/*---------------------------------
+
+ qh_mindif ( vecA, vecB, dim )
+ return index of min abs. difference of two vectors
+*/
+int qh_mindiff(realT *vecA, realT *vecB, int dim) {
+ realT mindiff= REALmax, diff;
+ realT *vecAp= vecA, *vecBp= vecB;
+ int k, mink= 0;
+
+ for (k=0; k < dim; k++) {
+ diff= *vecAp++ - *vecBp++;
+ diff= fabs_(diff);
+ if (diff < mindiff) {
+ mindiff= diff;
+ mink= k;
+ }
+ }
+ return mink;
+} /* mindiff */
+
+
+
+/*---------------------------------
+
+ qh_orientoutside( facet )
+ make facet outside oriented via qh.interior_point
+
+ returns:
+ True if facet reversed orientation.
+*/
+boolT qh_orientoutside(facetT *facet) {
+ int k;
+ realT dist;
+
+ qh_distplane(qh interior_point, facet, &dist);
+ if (dist > 0) {
+ for (k=qh hull_dim; k--; )
+ facet->normal[k]= -facet->normal[k];
+ facet->offset= -facet->offset;
+ return True;
+ }
+ return False;
+} /* orientoutside */
+
+/*---------------------------------
+
+ qh_outerinner( facet, outerplane, innerplane )
+ if facet and qh.maxoutdone (i.e., qh_check_maxout)
+ returns outer and inner plane for facet
+ else
+ returns maximum outer and inner plane
+ accounts for qh.JOGGLEmax
+
+ see:
+ qh_maxouter(), qh_check_bestdist(), qh_check_points()
+
+ notes:
+ outerplaner or innerplane may be NULL
+ facet is const
+ Does not error (QhullFacet)
+
+ includes qh.DISTround for actual points
+ adds another qh.DISTround if testing with floating point arithmetic
+*/
+void qh_outerinner(facetT *facet, realT *outerplane, realT *innerplane) {
+ realT dist, mindist;
+ vertexT *vertex, **vertexp;
+
+ if (outerplane) {
+ if (!qh_MAXoutside || !facet || !qh maxoutdone) {
+ *outerplane= qh_maxouter(); /* includes qh.DISTround */
+ }else { /* qh_MAXoutside ... */
+#if qh_MAXoutside
+ *outerplane= facet->maxoutside + qh DISTround;
+#endif
+
+ }
+ if (qh JOGGLEmax < REALmax/2)
+ *outerplane += qh JOGGLEmax * sqrt((realT)qh hull_dim);
+ }
+ if (innerplane) {
+ if (facet) {
+ mindist= REALmax;
+ FOREACHvertex_(facet->vertices) {
+ zinc_(Zdistio);
+ qh_distplane(vertex->point, facet, &dist);
+ minimize_(mindist, dist);
+ }
+ *innerplane= mindist - qh DISTround;
+ }else
+ *innerplane= qh min_vertex - qh DISTround;
+ if (qh JOGGLEmax < REALmax/2)
+ *innerplane -= qh JOGGLEmax * sqrt((realT)qh hull_dim);
+ }
+} /* outerinner */
+
+/*---------------------------------
+
+ qh_pointdist( point1, point2, dim )
+ return distance between two points
+
+ notes:
+ returns distance squared if 'dim' is negative
+*/
+coordT qh_pointdist(pointT *point1, pointT *point2, int dim) {
+ coordT dist, diff;
+ int k;
+
+ dist= 0.0;
+ for (k= (dim > 0 ? dim : -dim); k--; ) {
+ diff= *point1++ - *point2++;
+ dist += diff * diff;
+ }
+ if (dim > 0)
+ return(sqrt(dist));
+ return dist;
+} /* pointdist */
+
+
+/*---------------------------------
+
+ qh_printmatrix( fp, string, rows, numrow, numcol )
+ print matrix to fp given by row vectors
+ print string as header
+
+ notes:
+ print a vector by qh_printmatrix(fp, "", &vect, 1, len)
+*/
+void qh_printmatrix(FILE *fp, const char *string, realT **rows, int numrow, int numcol) {
+ realT *rowp;
+ realT r; /*bug fix*/
+ int i,k;
+
+ qh_fprintf(fp, 9001, "%s\n", string);
+ for (i=0; i < numrow; i++) {
+ rowp= rows[i];
+ for (k=0; k < numcol; k++) {
+ r= *rowp++;
+ qh_fprintf(fp, 9002, "%6.3g ", r);
+ }
+ qh_fprintf(fp, 9003, "\n");
+ }
+} /* printmatrix */
+
+
+/*---------------------------------
+
+ qh_printpoints( fp, string, points )
+ print pointids to fp for a set of points
+ if string, prints string and 'p' point ids
+*/
+void qh_printpoints(FILE *fp, const char *string, setT *points) {
+ pointT *point, **pointp;
+
+ if (string) {
+ qh_fprintf(fp, 9004, "%s", string);
+ FOREACHpoint_(points)
+ qh_fprintf(fp, 9005, " p%d", qh_pointid(point));
+ qh_fprintf(fp, 9006, "\n");
+ }else {
+ FOREACHpoint_(points)
+ qh_fprintf(fp, 9007, " %d", qh_pointid(point));
+ qh_fprintf(fp, 9008, "\n");
+ }
+} /* printpoints */
+
+
+/*---------------------------------
+
+ qh_projectinput()
+ project input points using qh.lower_bound/upper_bound and qh DELAUNAY
+ if qh.lower_bound[k]=qh.upper_bound[k]= 0,
+ removes dimension k
+ if halfspace intersection
+ removes dimension k from qh.feasible_point
+ input points in qh first_point, num_points, input_dim
+
+ returns:
+ new point array in qh first_point of qh hull_dim coordinates
+ sets qh POINTSmalloc
+ if qh DELAUNAY
+ projects points to paraboloid
+ lowbound/highbound is also projected
+ if qh ATinfinity
+ adds point "at-infinity"
+ if qh POINTSmalloc
+ frees old point array
+
+ notes:
+ checks that qh.hull_dim agrees with qh.input_dim, PROJECTinput, and DELAUNAY
+
+
+ design:
+ sets project[k] to -1 (delete), 0 (keep), 1 (add for Delaunay)
+ determines newdim and newnum for qh hull_dim and qh num_points
+ projects points to newpoints
+ projects qh.lower_bound to itself
+ projects qh.upper_bound to itself
+ if qh DELAUNAY
+ if qh ATINFINITY
+ projects points to paraboloid
+ computes "infinity" point as vertex average and 10% above all points
+ else
+ uses qh_setdelaunay to project points to paraboloid
+*/
+void qh_projectinput(void) {
+ int k,i;
+ int newdim= qh input_dim, newnum= qh num_points;
+ signed char *project;
+ int size= (qh input_dim+1)*sizeof(*project);
+ pointT *newpoints, *coord, *infinity;
+ realT paraboloid, maxboloid= 0;
+
+ project= (signed char*)qh_memalloc(size);
+ memset((char*)project, 0, (size_t)size);
+ for (k=0; k < qh input_dim; k++) { /* skip Delaunay bound */
+ if (qh lower_bound[k] == 0 && qh upper_bound[k] == 0) {
+ project[k]= -1;
+ newdim--;
+ }
+ }
+ if (qh DELAUNAY) {
+ project[k]= 1;
+ newdim++;
+ if (qh ATinfinity)
+ newnum++;
+ }
+ if (newdim != qh hull_dim) {
+ qh_fprintf(qh ferr, 6015, "qhull internal error (qh_projectinput): dimension after projection %d != hull_dim %d\n", newdim, qh hull_dim);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ if (!(newpoints=(coordT*)qh_malloc(newnum*newdim*sizeof(coordT)))){
+ qh_fprintf(qh ferr, 6016, "qhull error: insufficient memory to project %d points\n",
+ qh num_points);
+ qh_errexit(qh_ERRmem, NULL, NULL);
+ }
+ qh_projectpoints(project, qh input_dim+1, qh first_point,
+ qh num_points, qh input_dim, newpoints, newdim);
+ trace1((qh ferr, 1003, "qh_projectinput: updating lower and upper_bound\n"));
+ qh_projectpoints(project, qh input_dim+1, qh lower_bound,
+ 1, qh input_dim+1, qh lower_bound, newdim+1);
+ qh_projectpoints(project, qh input_dim+1, qh upper_bound,
+ 1, qh input_dim+1, qh upper_bound, newdim+1);
+ if (qh HALFspace) {
+ if (!qh feasible_point) {
+ qh_fprintf(qh ferr, 6017, "qhull internal error (qh_projectinput): HALFspace defined without qh.feasible_point\n");
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ qh_projectpoints(project, qh input_dim, qh feasible_point,
+ 1, qh input_dim, qh feasible_point, newdim);
+ }
+ qh_memfree(project, (qh input_dim+1)*sizeof(*project));
+ if (qh POINTSmalloc)
+ qh_free(qh first_point);
+ qh first_point= newpoints;
+ qh POINTSmalloc= True;
+ if (qh DELAUNAY && qh ATinfinity) {
+ coord= qh first_point;
+ infinity= qh first_point + qh hull_dim * qh num_points;
+ for (k=qh hull_dim-1; k--; )
+ infinity[k]= 0.0;
+ for (i=qh num_points; i--; ) {
+ paraboloid= 0.0;
+ for (k=0; k < qh hull_dim-1; k++) {
+ paraboloid += *coord * *coord;
+ infinity[k] += *coord;
+ coord++;
+ }
+ *(coord++)= paraboloid;
+ maximize_(maxboloid, paraboloid);
+ }
+ /* coord == infinity */
+ for (k=qh hull_dim-1; k--; )
+ *(coord++) /= qh num_points;
+ *(coord++)= maxboloid * 1.1;
+ qh num_points++;
+ trace0((qh ferr, 9, "qh_projectinput: projected points to paraboloid for Delaunay\n"));
+ }else if (qh DELAUNAY) /* !qh ATinfinity */
+ qh_setdelaunay( qh hull_dim, qh num_points, qh first_point);
+} /* projectinput */
+
+
+/*---------------------------------
+
+ qh_projectpoints( project, n, points, numpoints, dim, newpoints, newdim )
+ project points/numpoints/dim to newpoints/newdim
+ if project[k] == -1
+ delete dimension k
+ if project[k] == 1
+ add dimension k by duplicating previous column
+ n is size of project
+
+ notes:
+ newpoints may be points if only adding dimension at end
+
+ design:
+ check that 'project' and 'newdim' agree
+ for each dimension
+ if project == -1
+ skip dimension
+ else
+ determine start of column in newpoints
+ determine start of column in points
+ if project == +1, duplicate previous column
+ copy dimension (column) from points to newpoints
+*/
+void qh_projectpoints(signed char *project, int n, realT *points,
+ int numpoints, int dim, realT *newpoints, int newdim) {
+ int testdim= dim, oldk=0, newk=0, i,j=0,k;
+ realT *newp, *oldp;
+
+ for (k=0; k < n; k++)
+ testdim += project[k];
+ if (testdim != newdim) {
+ qh_fprintf(qh ferr, 6018, "qhull internal error (qh_projectpoints): newdim %d should be %d after projection\n",
+ newdim, testdim);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ for (j=0; j
---------------------------------
+
+ geom.c
+ geometric routines of qhull
+
+ see qh-geom.htm and geom.h
+
+ Copyright (c) 1993-2012 The Geometry Center.
+ $Id: //main/2011/qhull/src/libqhullr/geom_r.c#1 $$Change: 1640 $
+ $DateTime: 2014/01/15 09:12:08 $$Author: bbarber $
+
+ infrequent code goes into geom2.c
+*/
+
+#include "qhull_a.h"
+
+/*---------------------------------
+
+ qh_distplane( point, facet, dist )
+ return distance from point to facet
+
+ returns:
+ dist
+ if qh.RANDOMdist, joggles result
+
+ notes:
+ dist > 0 if point is above facet (i.e., outside)
+ does not error (for qh_sortfacets, qh_outerinner)
+
+ see:
+ qh_distnorm in geom2.c
+ qh_distplane [geom.c], QhullFacet::distance, and QhullHyperplane::distance are copies
+*/
+void qh_distplane(pointT *point, facetT *facet, realT *dist) {
+ coordT *normal= facet->normal, *coordp, randr;
+ int k;
+
+ switch (qh hull_dim){
+ case 2:
+ *dist= facet->offset + point[0] * normal[0] + point[1] * normal[1];
+ break;
+ case 3:
+ *dist= facet->offset + point[0] * normal[0] + point[1] * normal[1] + point[2] * normal[2];
+ break;
+ case 4:
+ *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3];
+ break;
+ case 5:
+ *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4];
+ break;
+ case 6:
+ *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]+point[5]*normal[5];
+ break;
+ case 7:
+ *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]+point[5]*normal[5]+point[6]*normal[6];
+ break;
+ case 8:
+ *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]+point[5]*normal[5]+point[6]*normal[6]+point[7]*normal[7];
+ break;
+ default:
+ *dist= facet->offset;
+ coordp= point;
+ for (k=qh hull_dim; k--; )
+ *dist += *coordp++ * *normal++;
+ break;
+ }
+ zinc_(Zdistplane);
+ if (!qh RANDOMdist && qh IStracing < 4)
+ return;
+ if (qh RANDOMdist) {
+ randr= qh_RANDOMint;
+ *dist += (2.0 * randr / qh_RANDOMmax - 1.0) *
+ qh RANDOMfactor * qh MAXabs_coord;
+ }
+ if (qh IStracing >= 4) {
+ qh_fprintf(qh ferr, 8001, "qh_distplane: ");
+ qh_fprintf(qh ferr, 8002, qh_REAL_1, *dist);
+ qh_fprintf(qh ferr, 8003, "from p%d to f%d\n", qh_pointid(point), facet->id);
+ }
+ return;
+} /* distplane */
+
+
+/*---------------------------------
+
+ qh_findbest( point, startfacet, bestoutside, qh_ISnewfacets, qh_NOupper, dist, isoutside, numpart )
+ find facet that is furthest below a point
+ for upperDelaunay facets
+ returns facet only if !qh_NOupper and clearly above
+
+ input:
+ starts search at 'startfacet' (can not be flipped)
+ if !bestoutside(qh_ALL), stops at qh.MINoutside
+
+ returns:
+ best facet (reports error if NULL)
+ early out if isoutside defined and bestdist > qh.MINoutside
+ dist is distance to facet
+ isoutside is true if point is outside of facet
+ numpart counts the number of distance tests
+
+ see also:
+ qh_findbestnew()
+
+ notes:
+ If merging (testhorizon), searches horizon facets of coplanar best facets because
+ after qh_distplane, this and qh_partitionpoint are the most expensive in 3-d
+ avoid calls to distplane, function calls, and real number operations.
+ caller traces result
+ Optimized for outside points. Tried recording a search set for qh_findhorizon.
+ Made code more complicated.
+
+ when called by qh_partitionvisible():
+ indicated by qh_ISnewfacets
+ qh.newfacet_list is list of simplicial, new facets
+ qh_findbestnew set if qh_sharpnewfacets returns True (to use qh_findbestnew)
+ qh.bestfacet_notsharp set if qh_sharpnewfacets returns False
+
+ when called by qh_findfacet(), qh_partitionpoint(), qh_partitioncoplanar(),
+ qh_check_bestdist(), qh_addpoint()
+ indicated by !qh_ISnewfacets
+ returns best facet in neighborhood of given facet
+ this is best facet overall if dist > - qh.MAXcoplanar
+ or hull has at least a "spherical" curvature
+
+ design:
+ initialize and test for early exit
+ repeat while there are better facets
+ for each neighbor of facet
+ exit if outside facet found
+ test for better facet
+ if point is inside and partitioning
+ test for new facets with a "sharp" intersection
+ if so, future calls go to qh_findbestnew()
+ test horizon facets
+*/
+facetT *qh_findbest(pointT *point, facetT *startfacet,
+ boolT bestoutside, boolT isnewfacets, boolT noupper,
+ realT *dist, boolT *isoutside, int *numpart) {
+ realT bestdist= -REALmax/2 /* avoid underflow */;
+ facetT *facet, *neighbor, **neighborp;
+ facetT *bestfacet= NULL, *lastfacet= NULL;
+ int oldtrace= qh IStracing;
+ unsigned int visitid= ++qh visit_id;
+ int numpartnew=0;
+ boolT testhorizon = True; /* needed if precise, e.g., rbox c D6 | qhull Q0 Tv */
+
+ zinc_(Zfindbest);
+ if (qh IStracing >= 3 || (qh TRACElevel && qh TRACEpoint >= 0 && qh TRACEpoint == qh_pointid(point))) {
+ if (qh TRACElevel > qh IStracing)
+ qh IStracing= qh TRACElevel;
+ qh_fprintf(qh ferr, 8004, "qh_findbest: point p%d starting at f%d isnewfacets? %d, unless %d exit if > %2.2g\n",
+ qh_pointid(point), startfacet->id, isnewfacets, bestoutside, qh MINoutside);
+ qh_fprintf(qh ferr, 8005, " testhorizon? %d noupper? %d", testhorizon, noupper);
+ qh_fprintf(qh ferr, 8006, " Last point added was p%d.", qh furthest_id);
+ qh_fprintf(qh ferr, 8007, " Last merge was #%d. max_outside %2.2g\n", zzval_(Ztotmerge), qh max_outside);
+ }
+ if (isoutside)
+ *isoutside= True;
+ if (!startfacet->flipped) { /* test startfacet */
+ *numpart= 1;
+ qh_distplane(point, startfacet, dist); /* this code is duplicated below */
+ if (!bestoutside && *dist >= qh MINoutside
+ && (!startfacet->upperdelaunay || !noupper)) {
+ bestfacet= startfacet;
+ goto LABELreturn_best;
+ }
+ bestdist= *dist;
+ if (!startfacet->upperdelaunay) {
+ bestfacet= startfacet;
+ }
+ }else
+ *numpart= 0;
+ startfacet->visitid= visitid;
+ facet= startfacet;
+ while (facet) {
+ trace4((qh ferr, 4001, "qh_findbest: neighbors of f%d, bestdist %2.2g f%d\n",
+ facet->id, bestdist, getid_(bestfacet)));
+ lastfacet= facet;
+ FOREACHneighbor_(facet) {
+ if (!neighbor->newfacet && isnewfacets)
+ continue;
+ if (neighbor->visitid == visitid)
+ continue;
+ neighbor->visitid= visitid;
+ if (!neighbor->flipped) { /* code duplicated above */
+ (*numpart)++;
+ qh_distplane(point, neighbor, dist);
+ if (*dist > bestdist) {
+ if (!bestoutside && *dist >= qh MINoutside
+ && (!neighbor->upperdelaunay || !noupper)) {
+ bestfacet= neighbor;
+ goto LABELreturn_best;
+ }
+ if (!neighbor->upperdelaunay) {
+ bestfacet= neighbor;
+ bestdist= *dist;
+ break; /* switch to neighbor */
+ }else if (!bestfacet) {
+ bestdist= *dist;
+ break; /* switch to neighbor */
+ }
+ } /* end of *dist>bestdist */
+ } /* end of !flipped */
+ } /* end of FOREACHneighbor */
+ facet= neighbor; /* non-NULL only if *dist>bestdist */
+ } /* end of while facet (directed search) */
+ if (isnewfacets) {
+ if (!bestfacet) {
+ bestdist= -REALmax/2;
+ bestfacet= qh_findbestnew(point, startfacet->next, &bestdist, bestoutside, isoutside, &numpartnew);
+ testhorizon= False; /* qh_findbestnew calls qh_findbesthorizon */
+ }else if (!qh findbest_notsharp && bestdist < - qh DISTround) {
+ if (qh_sharpnewfacets()) {
+ /* seldom used, qh_findbestnew will retest all facets */
+ zinc_(Zfindnewsharp);
+ bestfacet= qh_findbestnew(point, bestfacet, &bestdist, bestoutside, isoutside, &numpartnew);
+ testhorizon= False; /* qh_findbestnew calls qh_findbesthorizon */
+ qh findbestnew= True;
+ }else
+ qh findbest_notsharp= True;
+ }
+ }
+ if (!bestfacet)
+ bestfacet= qh_findbestlower(lastfacet, point, &bestdist, numpart);
+ if (testhorizon)
+ bestfacet= qh_findbesthorizon(!qh_IScheckmax, point, bestfacet, noupper, &bestdist, &numpartnew);
+ *dist= bestdist;
+ if (isoutside && bestdist < qh MINoutside)
+ *isoutside= False;
+LABELreturn_best:
+ zadd_(Zfindbesttot, *numpart);
+ zmax_(Zfindbestmax, *numpart);
+ (*numpart) += numpartnew;
+ qh IStracing= oldtrace;
+ return bestfacet;
+} /* findbest */
+
+
+/*---------------------------------
+
+ qh_findbesthorizon( qh_IScheckmax, point, startfacet, qh_NOupper, &bestdist, &numpart )
+ search coplanar and better horizon facets from startfacet/bestdist
+ ischeckmax turns off statistics and minsearch update
+ all arguments must be initialized
+ returns(ischeckmax):
+ best facet
+ returns(!ischeckmax):
+ best facet that is not upperdelaunay
+ allows upperdelaunay that is clearly outside
+ returns:
+ bestdist is distance to bestfacet
+ numpart -- updates number of distance tests
+
+ notes:
+ no early out -- use qh_findbest() or qh_findbestnew()
+ Searches coplanar or better horizon facets
+
+ when called by qh_check_maxout() (qh_IScheckmax)
+ startfacet must be closest to the point
+ Otherwise, if point is beyond and below startfacet, startfacet may be a local minimum
+ even though other facets are below the point.
+ updates facet->maxoutside for good, visited facets
+ may return NULL
+
+ searchdist is qh.max_outside + 2 * DISTround
+ + max( MINvisible('Vn'), MAXcoplanar('Un'));
+ This setting is a guess. It must be at least max_outside + 2*DISTround
+ because a facet may have a geometric neighbor across a vertex
+
+ design:
+ for each horizon facet of coplanar best facets
+ continue if clearly inside
+ unless upperdelaunay or clearly outside
+ update best facet
+*/
+facetT *qh_findbesthorizon(boolT ischeckmax, pointT* point, facetT *startfacet, boolT noupper, realT *bestdist, int *numpart) {
+ facetT *bestfacet= startfacet;
+ realT dist;
+ facetT *neighbor, **neighborp, *facet;
+ facetT *nextfacet= NULL; /* optimize last facet of coplanarfacetset */
+ int numpartinit= *numpart, coplanarfacetset_size;
+ unsigned int visitid= ++qh visit_id;
+ boolT newbest= False; /* for tracing */
+ realT minsearch, searchdist; /* skip facets that are too far from point */
+
+ if (!ischeckmax) {
+ zinc_(Zfindhorizon);
+ }else {
+#if qh_MAXoutside
+ if ((!qh ONLYgood || startfacet->good) && *bestdist > startfacet->maxoutside)
+ startfacet->maxoutside= *bestdist;
+#endif
+ }
+ searchdist= qh_SEARCHdist; /* multiple of qh.max_outside and precision constants */
+ minsearch= *bestdist - searchdist;
+ if (ischeckmax) {
+ /* Always check coplanar facets. Needed for RBOX 1000 s Z1 G1e-13 t996564279 | QHULL Tv */
+ minimize_(minsearch, -searchdist);
+ }
+ coplanarfacetset_size= 0;
+ facet= startfacet;
+ while (True) {
+ trace4((qh ferr, 4002, "qh_findbesthorizon: neighbors of f%d bestdist %2.2g f%d ischeckmax? %d noupper? %d minsearch %2.2g searchdist %2.2g\n",
+ facet->id, *bestdist, getid_(bestfacet), ischeckmax, noupper,
+ minsearch, searchdist));
+ FOREACHneighbor_(facet) {
+ if (neighbor->visitid == visitid)
+ continue;
+ neighbor->visitid= visitid;
+ if (!neighbor->flipped) {
+ qh_distplane(point, neighbor, &dist);
+ (*numpart)++;
+ if (dist > *bestdist) {
+ if (!neighbor->upperdelaunay || ischeckmax || (!noupper && dist >= qh MINoutside)) {
+ bestfacet= neighbor;
+ *bestdist= dist;
+ newbest= True;
+ if (!ischeckmax) {
+ minsearch= dist - searchdist;
+ if (dist > *bestdist + searchdist) {
+ zinc_(Zfindjump); /* everything in qh.coplanarfacetset at least searchdist below */
+ coplanarfacetset_size= 0;
+ }
+ }
+ }
+ }else if (dist < minsearch)
+ continue; /* if ischeckmax, dist can't be positive */
+#if qh_MAXoutside
+ if (ischeckmax && dist > neighbor->maxoutside)
+ neighbor->maxoutside= dist;
+#endif
+ } /* end of !flipped */
+ if (nextfacet) {
+ if (!coplanarfacetset_size++) {
+ SETfirst_(qh coplanarfacetset)= nextfacet;
+ SETtruncate_(qh coplanarfacetset, 1);
+ }else
+ qh_setappend(&qh coplanarfacetset, nextfacet); /* Was needed for RBOX 1000 s W1e-13 P0 t996547055 | QHULL d Qbb Qc Tv
+ and RBOX 1000 s Z1 G1e-13 t996564279 | qhull Tv */
+ }
+ nextfacet= neighbor;
+ } /* end of EACHneighbor */
+ facet= nextfacet;
+ if (facet)
+ nextfacet= NULL;
+ else if (!coplanarfacetset_size)
+ break;
+ else if (!--coplanarfacetset_size) {
+ facet= SETfirstt_(qh coplanarfacetset, facetT);
+ SETtruncate_(qh coplanarfacetset, 0);
+ }else
+ facet= (facetT*)qh_setdellast(qh coplanarfacetset);
+ } /* while True, for each facet in qh.coplanarfacetset */
+ if (!ischeckmax) {
+ zadd_(Zfindhorizontot, *numpart - numpartinit);
+ zmax_(Zfindhorizonmax, *numpart - numpartinit);
+ if (newbest)
+ zinc_(Zparthorizon);
+ }
+ trace4((qh ferr, 4003, "qh_findbesthorizon: newbest? %d bestfacet f%d bestdist %2.2g\n", newbest, getid_(bestfacet), *bestdist));
+ return bestfacet;
+} /* findbesthorizon */
+
+/*---------------------------------
+
+ qh_findbestnew( point, startfacet, dist, isoutside, numpart )
+ find best newfacet for point
+ searches all of qh.newfacet_list starting at startfacet
+ searches horizon facets of coplanar best newfacets
+ searches all facets if startfacet == qh.facet_list
+ returns:
+ best new or horizon facet that is not upperdelaunay
+ early out if isoutside and not 'Qf'
+ dist is distance to facet
+ isoutside is true if point is outside of facet
+ numpart is number of distance tests
+
+ notes:
+ Always used for merged new facets (see qh_USEfindbestnew)
+ Avoids upperdelaunay facet unless (isoutside and outside)
+
+ Uses qh.visit_id, qh.coplanarfacetset.
+ If share visit_id with qh_findbest, coplanarfacetset is incorrect.
+
+ If merging (testhorizon), searches horizon facets of coplanar best facets because
+ a point maybe coplanar to the bestfacet, below its horizon facet,
+ and above a horizon facet of a coplanar newfacet. For example,
+ rbox 1000 s Z1 G1e-13 | qhull
+ rbox 1000 s W1e-13 P0 t992110337 | QHULL d Qbb Qc
+
+ qh_findbestnew() used if
+ qh_sharpnewfacets -- newfacets contains a sharp angle
+ if many merges, qh_premerge found a merge, or 'Qf' (qh.findbestnew)
+
+ see also:
+ qh_partitionall() and qh_findbest()
+
+ design:
+ for each new facet starting from startfacet
+ test distance from point to facet
+ return facet if clearly outside
+ unless upperdelaunay and a lowerdelaunay exists
+ update best facet
+ test horizon facets
+*/
+facetT *qh_findbestnew(pointT *point, facetT *startfacet,
+ realT *dist, boolT bestoutside, boolT *isoutside, int *numpart) {
+ realT bestdist= -REALmax/2;
+ facetT *bestfacet= NULL, *facet;
+ int oldtrace= qh IStracing, i;
+ unsigned int visitid= ++qh visit_id;
+ realT distoutside= 0.0;
+ boolT isdistoutside; /* True if distoutside is defined */
+ boolT testhorizon = True; /* needed if precise, e.g., rbox c D6 | qhull Q0 Tv */
+
+ if (!startfacet) {
+ if (qh MERGING)
+ qh_fprintf(qh ferr, 6001, "qhull precision error (qh_findbestnew): merging has formed and deleted a cone of new facets. Can not continue.\n");
+ else
+ qh_fprintf(qh ferr, 6002, "qhull internal error (qh_findbestnew): no new facets for point p%d\n",
+ qh furthest_id);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ zinc_(Zfindnew);
+ if (qh BESToutside || bestoutside)
+ isdistoutside= False;
+ else {
+ isdistoutside= True;
+ distoutside= qh_DISToutside; /* multiple of qh.MINoutside & qh.max_outside, see user.h */
+ }
+ if (isoutside)
+ *isoutside= True;
+ *numpart= 0;
+ if (qh IStracing >= 3 || (qh TRACElevel && qh TRACEpoint >= 0 && qh TRACEpoint == qh_pointid(point))) {
+ if (qh TRACElevel > qh IStracing)
+ qh IStracing= qh TRACElevel;
+ qh_fprintf(qh ferr, 8008, "qh_findbestnew: point p%d facet f%d. Stop? %d if dist > %2.2g\n",
+ qh_pointid(point), startfacet->id, isdistoutside, distoutside);
+ qh_fprintf(qh ferr, 8009, " Last point added p%d visitid %d.", qh furthest_id, visitid);
+ qh_fprintf(qh ferr, 8010, " Last merge was #%d.\n", zzval_(Ztotmerge));
+ }
+ /* visit all new facets starting with startfacet, maybe qh facet_list */
+ for (i=0, facet=startfacet; i < 2; i++, facet= qh newfacet_list) {
+ FORALLfacet_(facet) {
+ if (facet == startfacet && i)
+ break;
+ facet->visitid= visitid;
+ if (!facet->flipped) {
+ qh_distplane(point, facet, dist);
+ (*numpart)++;
+ if (*dist > bestdist) {
+ if (!facet->upperdelaunay || *dist >= qh MINoutside) {
+ bestfacet= facet;
+ if (isdistoutside && *dist >= distoutside)
+ goto LABELreturn_bestnew;
+ bestdist= *dist;
+ }
+ }
+ } /* end of !flipped */
+ } /* FORALLfacet from startfacet or qh newfacet_list */
+ }
+ if (testhorizon || !bestfacet)
+ bestfacet= qh_findbesthorizon(!qh_IScheckmax, point, bestfacet ? bestfacet : startfacet,
+ !qh_NOupper, &bestdist, numpart);
+ *dist= bestdist;
+ if (isoutside && *dist < qh MINoutside)
+ *isoutside= False;
+LABELreturn_bestnew:
+ zadd_(Zfindnewtot, *numpart);
+ zmax_(Zfindnewmax, *numpart);
+ trace4((qh ferr, 4004, "qh_findbestnew: bestfacet f%d bestdist %2.2g\n", getid_(bestfacet), *dist));
+ qh IStracing= oldtrace;
+ return bestfacet;
+} /* findbestnew */
+
+/* ============ hyperplane functions -- keep code together [?] ============ */
+
+/*---------------------------------
+
+ qh_backnormal( rows, numrow, numcol, sign, normal, nearzero )
+ given an upper-triangular rows array and a sign,
+ solve for normal equation x using back substitution over rows U
+
+ returns:
+ normal= x
+
+ if will not be able to divzero() when normalized(qh.MINdenom_2 and qh.MINdenom_1_2),
+ if fails on last row
+ this means that the hyperplane intersects [0,..,1]
+ sets last coordinate of normal to sign
+ otherwise
+ sets tail of normal to [...,sign,0,...], i.e., solves for b= [0...0]
+ sets nearzero
+
+ notes:
+ assumes numrow == numcol-1
+
+ see Golub & van Loan 4.4-9 for back substitution
+
+ solves Ux=b where Ax=b and PA=LU
+ b= [0,...,0,sign or 0] (sign is either -1 or +1)
+ last row of A= [0,...,0,1]
+
+ 1) Ly=Pb == y=b since P only permutes the 0's of b
+
+ design:
+ for each row from end
+ perform back substitution
+ if near zero
+ use qh_divzero for division
+ if zero divide and not last row
+ set tail of normal to 0
+*/
+void qh_backnormal(realT **rows, int numrow, int numcol, boolT sign,
+ coordT *normal, boolT *nearzero) {
+ int i, j;
+ coordT *normalp, *normal_tail, *ai, *ak;
+ realT diagonal;
+ boolT waszero;
+ int zerocol= -1;
+
+ normalp= normal + numcol - 1;
+ *normalp--= (sign ? -1.0 : 1.0);
+ for (i=numrow; i--; ) {
+ *normalp= 0.0;
+ ai= rows[i] + i + 1;
+ ak= normalp+1;
+ for (j=i+1; j < numcol; j++)
+ *normalp -= *ai++ * *ak++;
+ diagonal= (rows[i])[i];
+ if (fabs_(diagonal) > qh MINdenom_2)
+ *(normalp--) /= diagonal;
+ else {
+ waszero= False;
+ *normalp= qh_divzero(*normalp, diagonal, qh MINdenom_1_2, &waszero);
+ if (waszero) {
+ zerocol= i;
+ *(normalp--)= (sign ? -1.0 : 1.0);
+ for (normal_tail= normalp+2; normal_tail < normal + numcol; normal_tail++)
+ *normal_tail= 0.0;
+ }else
+ normalp--;
+ }
+ }
+ if (zerocol != -1) {
+ zzinc_(Zback0);
+ *nearzero= True;
+ trace4((qh ferr, 4005, "qh_backnormal: zero diagonal at column %d.\n", i));
+ qh_precision("zero diagonal on back substitution");
+ }
+} /* backnormal */
+
+/*---------------------------------
+
+ qh_gausselim( rows, numrow, numcol, sign )
+ Gaussian elimination with partial pivoting
+
+ returns:
+ rows is upper triangular (includes row exchanges)
+ flips sign for each row exchange
+ sets nearzero if pivot[k] < qh.NEARzero[k], else clears it
+
+ notes:
+ if nearzero, the determinant's sign may be incorrect.
+ assumes numrow <= numcol
+
+ design:
+ for each row
+ determine pivot and exchange rows if necessary
+ test for near zero
+ perform gaussian elimination step
+*/
+void qh_gausselim(realT **rows, int numrow, int numcol, boolT *sign, boolT *nearzero) {
+ realT *ai, *ak, *rowp, *pivotrow;
+ realT n, pivot, pivot_abs= 0.0, temp;
+ int i, j, k, pivoti, flip=0;
+
+ *nearzero= False;
+ for (k=0; k < numrow; k++) {
+ pivot_abs= fabs_((rows[k])[k]);
+ pivoti= k;
+ for (i=k+1; i < numrow; i++) {
+ if ((temp= fabs_((rows[i])[k])) > pivot_abs) {
+ pivot_abs= temp;
+ pivoti= i;
+ }
+ }
+ if (pivoti != k) {
+ rowp= rows[pivoti];
+ rows[pivoti]= rows[k];
+ rows[k]= rowp;
+ *sign ^= 1;
+ flip ^= 1;
+ }
+ if (pivot_abs <= qh NEARzero[k]) {
+ *nearzero= True;
+ if (pivot_abs == 0.0) { /* remainder of column == 0 */
+ if (qh IStracing >= 4) {
+ qh_fprintf(qh ferr, 8011, "qh_gausselim: 0 pivot at column %d. (%2.2g < %2.2g)\n", k, pivot_abs, qh DISTround);
+ qh_printmatrix(qh ferr, "Matrix:", rows, numrow, numcol);
+ }
+ zzinc_(Zgauss0);
+ qh_precision("zero pivot for Gaussian elimination");
+ goto LABELnextcol;
+ }
+ }
+ pivotrow= rows[k] + k;
+ pivot= *pivotrow++; /* signed value of pivot, and remainder of row */
+ for (i=k+1; i < numrow; i++) {
+ ai= rows[i] + k;
+ ak= pivotrow;
+ n= (*ai++)/pivot; /* divzero() not needed since |pivot| >= |*ai| */
+ for (j= numcol - (k+1); j--; )
+ *ai++ -= n * *ak++;
+ }
+ LABELnextcol:
+ ;
+ }
+ wmin_(Wmindenom, pivot_abs); /* last pivot element */
+ if (qh IStracing >= 5)
+ qh_printmatrix(qh ferr, "qh_gausselem: result", rows, numrow, numcol);
+} /* gausselim */
+
+
+/*---------------------------------
+
+ qh_getangle( vect1, vect2 )
+ returns the dot product of two vectors
+ if qh.RANDOMdist, joggles result
+
+ notes:
+ the angle may be > 1.0 or < -1.0 because of roundoff errors
+
+*/
+realT qh_getangle(pointT *vect1, pointT *vect2) {
+ realT angle= 0, randr;
+ int k;
+
+ for (k=qh hull_dim; k--; )
+ angle += *vect1++ * *vect2++;
+ if (qh RANDOMdist) {
+ randr= qh_RANDOMint;
+ angle += (2.0 * randr / qh_RANDOMmax - 1.0) *
+ qh RANDOMfactor;
+ }
+ trace4((qh ferr, 4006, "qh_getangle: %2.2g\n", angle));
+ return(angle);
+} /* getangle */
+
+
+/*---------------------------------
+
+ qh_getcenter( vertices )
+ returns arithmetic center of a set of vertices as a new point
+
+ notes:
+ allocates point array for center
+*/
+pointT *qh_getcenter(setT *vertices) {
+ int k;
+ pointT *center, *coord;
+ vertexT *vertex, **vertexp;
+ int count= qh_setsize(vertices);
+
+ if (count < 2) {
+ qh_fprintf(qh ferr, 6003, "qhull internal error (qh_getcenter): not defined for %d points\n", count);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ center= (pointT *)qh_memalloc(qh normal_size);
+ for (k=0; k < qh hull_dim; k++) {
+ coord= center+k;
+ *coord= 0.0;
+ FOREACHvertex_(vertices)
+ *coord += vertex->point[k];
+ *coord /= count;
+ }
+ return(center);
+} /* getcenter */
+
+
+/*---------------------------------
+
+ qh_getcentrum( facet )
+ returns the centrum for a facet as a new point
+
+ notes:
+ allocates the centrum
+*/
+pointT *qh_getcentrum(facetT *facet) {
+ realT dist;
+ pointT *centrum, *point;
+
+ point= qh_getcenter(facet->vertices);
+ zzinc_(Zcentrumtests);
+ qh_distplane(point, facet, &dist);
+ centrum= qh_projectpoint(point, facet, dist);
+ qh_memfree(point, qh normal_size);
+ trace4((qh ferr, 4007, "qh_getcentrum: for f%d, %d vertices dist= %2.2g\n",
+ facet->id, qh_setsize(facet->vertices), dist));
+ return centrum;
+} /* getcentrum */
+
+
+/*---------------------------------
+
+ qh_getdistance( facet, neighbor, mindist, maxdist )
+ returns the maxdist and mindist distance of any vertex from neighbor
+
+ returns:
+ the max absolute value
+
+ design:
+ for each vertex of facet that is not in neighbor
+ test the distance from vertex to neighbor
+*/
+realT qh_getdistance(facetT *facet, facetT *neighbor, realT *mindist, realT *maxdist) {
+ vertexT *vertex, **vertexp;
+ realT dist, maxd, mind;
+
+ FOREACHvertex_(facet->vertices)
+ vertex->seen= False;
+ FOREACHvertex_(neighbor->vertices)
+ vertex->seen= True;
+ mind= 0.0;
+ maxd= 0.0;
+ FOREACHvertex_(facet->vertices) {
+ if (!vertex->seen) {
+ zzinc_(Zbestdist);
+ qh_distplane(vertex->point, neighbor, &dist);
+ if (dist < mind)
+ mind= dist;
+ else if (dist > maxd)
+ maxd= dist;
+ }
+ }
+ *mindist= mind;
+ *maxdist= maxd;
+ mind= -mind;
+ if (maxd > mind)
+ return maxd;
+ else
+ return mind;
+} /* getdistance */
+
+
+/*---------------------------------
+
+ qh_normalize( normal, dim, toporient )
+ normalize a vector and report if too small
+ does not use min norm
+
+ see:
+ qh_normalize2
+*/
+void qh_normalize(coordT *normal, int dim, boolT toporient) {
+ qh_normalize2( normal, dim, toporient, NULL, NULL);
+} /* normalize */
+
+/*---------------------------------
+
+ qh_normalize2( normal, dim, toporient, minnorm, ismin )
+ normalize a vector and report if too small
+ qh.MINdenom/MINdenom1 are the upper limits for divide overflow
+
+ returns:
+ normalized vector
+ flips sign if !toporient
+ if minnorm non-NULL,
+ sets ismin if normal < minnorm
+
+ notes:
+ if zero norm
+ sets all elements to sqrt(1.0/dim)
+ if divide by zero (divzero())
+ sets largest element to +/-1
+ bumps Znearlysingular
+
+ design:
+ computes norm
+ test for minnorm
+ if not near zero
+ normalizes normal
+ else if zero norm
+ sets normal to standard value
+ else
+ uses qh_divzero to normalize
+ if nearzero
+ sets norm to direction of maximum value
+*/
+void qh_normalize2 (coordT *normal, int dim, boolT toporient,
+ realT *minnorm, boolT *ismin) {
+ int k;
+ realT *colp, *maxp, norm= 0, temp, *norm1, *norm2, *norm3;
+ boolT zerodiv;
+
+ norm1= normal+1;
+ norm2= normal+2;
+ norm3= normal+3;
+ if (dim == 2)
+ norm= sqrt((*normal)*(*normal) + (*norm1)*(*norm1));
+ else if (dim == 3)
+ norm= sqrt((*normal)*(*normal) + (*norm1)*(*norm1) + (*norm2)*(*norm2));
+ else if (dim == 4) {
+ norm= sqrt((*normal)*(*normal) + (*norm1)*(*norm1) + (*norm2)*(*norm2)
+ + (*norm3)*(*norm3));
+ }else if (dim > 4) {
+ norm= (*normal)*(*normal) + (*norm1)*(*norm1) + (*norm2)*(*norm2)
+ + (*norm3)*(*norm3);
+ for (k=dim-4, colp=normal+4; k--; colp++)
+ norm += (*colp) * (*colp);
+ norm= sqrt(norm);
+ }
+ if (minnorm) {
+ if (norm < *minnorm)
+ *ismin= True;
+ else
+ *ismin= False;
+ }
+ wmin_(Wmindenom, norm);
+ if (norm > qh MINdenom) {
+ if (!toporient)
+ norm= -norm;
+ *normal /= norm;
+ *norm1 /= norm;
+ if (dim == 2)
+ ; /* all done */
+ else if (dim == 3)
+ *norm2 /= norm;
+ else if (dim == 4) {
+ *norm2 /= norm;
+ *norm3 /= norm;
+ }else if (dim >4) {
+ *norm2 /= norm;
+ *norm3 /= norm;
+ for (k=dim-4, colp=normal+4; k--; )
+ *colp++ /= norm;
+ }
+ }else if (norm == 0.0) {
+ temp= sqrt(1.0/dim);
+ for (k=dim, colp=normal; k--; )
+ *colp++ = temp;
+ }else {
+ if (!toporient)
+ norm= -norm;
+ for (k=dim, colp=normal; k--; colp++) { /* k used below */
+ temp= qh_divzero(*colp, norm, qh MINdenom_1, &zerodiv);
+ if (!zerodiv)
+ *colp= temp;
+ else {
+ maxp= qh_maxabsval(normal, dim);
+ temp= ((*maxp * norm >= 0.0) ? 1.0 : -1.0);
+ for (k=dim, colp=normal; k--; colp++)
+ *colp= 0.0;
+ *maxp= temp;
+ zzinc_(Znearlysingular);
+ trace0((qh ferr, 1, "qh_normalize: norm=%2.2g too small during p%d\n",
+ norm, qh furthest_id));
+ return;
+ }
+ }
+ }
+} /* normalize */
+
+
+/*---------------------------------
+
+ qh_projectpoint( point, facet, dist )
+ project point onto a facet by dist
+
+ returns:
+ returns a new point
+
+ notes:
+ if dist= distplane(point,facet)
+ this projects point to hyperplane
+ assumes qh_memfree_() is valid for normal_size
+*/
+pointT *qh_projectpoint(pointT *point, facetT *facet, realT dist) {
+ pointT *newpoint, *np, *normal;
+ int normsize= qh normal_size;
+ int k;
+ void **freelistp; /* used !qh_NOmem */
+
+ qh_memalloc_(normsize, freelistp, newpoint, pointT);
+ np= newpoint;
+ normal= facet->normal;
+ for (k=qh hull_dim; k--; )
+ *(np++)= *point++ - dist * *normal++;
+ return(newpoint);
+} /* projectpoint */
+
+
+/*---------------------------------
+
+ qh_setfacetplane( facet )
+ sets the hyperplane for a facet
+ if qh.RANDOMdist, joggles hyperplane
+
+ notes:
+ uses global buffers qh.gm_matrix and qh.gm_row
+ overwrites facet->normal if already defined
+ updates Wnewvertex if PRINTstatistics
+ sets facet->upperdelaunay if upper envelope of Delaunay triangulation
+
+ design:
+ copy vertex coordinates to qh.gm_matrix/gm_row
+ compute determinate
+ if nearzero
+ recompute determinate with gaussian elimination
+ if nearzero
+ force outside orientation by testing interior point
+*/
+void qh_setfacetplane(facetT *facet) {
+ pointT *point;
+ vertexT *vertex, **vertexp;
+ int normsize= qh normal_size;
+ int k,i, oldtrace= 0;
+ realT dist;
+ void **freelistp; /* used !qh_NOmem */
+ coordT *coord, *gmcoord;
+ pointT *point0= SETfirstt_(facet->vertices, vertexT)->point;
+ boolT nearzero= False;
+
+ zzinc_(Zsetplane);
+ if (!facet->normal)
+ qh_memalloc_(normsize, freelistp, facet->normal, coordT);
+ if (facet == qh tracefacet) {
+ oldtrace= qh IStracing;
+ qh IStracing= 5;
+ qh_fprintf(qh ferr, 8012, "qh_setfacetplane: facet f%d created.\n", facet->id);
+ qh_fprintf(qh ferr, 8013, " Last point added to hull was p%d.", qh furthest_id);
+ if (zzval_(Ztotmerge))
+ qh_fprintf(qh ferr, 8014, " Last merge was #%d.", zzval_(Ztotmerge));
+ qh_fprintf(qh ferr, 8015, "\n\nCurrent summary is:\n");
+ qh_printsummary(qh ferr);
+ }
+ if (qh hull_dim <= 4) {
+ i= 0;
+ if (qh RANDOMdist) {
+ gmcoord= qh gm_matrix;
+ FOREACHvertex_(facet->vertices) {
+ qh gm_row[i++]= gmcoord;
+ coord= vertex->point;
+ for (k=qh hull_dim; k--; )
+ *(gmcoord++)= *coord++ * qh_randomfactor(qh RANDOMa, qh RANDOMb);
+ }
+ }else {
+ FOREACHvertex_(facet->vertices)
+ qh gm_row[i++]= vertex->point;
+ }
+ qh_sethyperplane_det(qh hull_dim, qh gm_row, point0, facet->toporient,
+ facet->normal, &facet->offset, &nearzero);
+ }
+ if (qh hull_dim > 4 || nearzero) {
+ i= 0;
+ gmcoord= qh gm_matrix;
+ FOREACHvertex_(facet->vertices) {
+ if (vertex->point != point0) {
+ qh gm_row[i++]= gmcoord;
+ coord= vertex->point;
+ point= point0;
+ for (k=qh hull_dim; k--; )
+ *(gmcoord++)= *coord++ - *point++;
+ }
+ }
+ qh gm_row[i]= gmcoord; /* for areasimplex */
+ if (qh RANDOMdist) {
+ gmcoord= qh gm_matrix;
+ for (i=qh hull_dim-1; i--; ) {
+ for (k=qh hull_dim; k--; )
+ *(gmcoord++) *= qh_randomfactor(qh RANDOMa, qh RANDOMb);
+ }
+ }
+ qh_sethyperplane_gauss(qh hull_dim, qh gm_row, point0, facet->toporient,
+ facet->normal, &facet->offset, &nearzero);
+ if (nearzero) {
+ if (qh_orientoutside(facet)) {
+ trace0((qh ferr, 2, "qh_setfacetplane: flipped orientation after testing interior_point during p%d\n", qh furthest_id));
+ /* this is part of using Gaussian Elimination. For example in 5-d
+ 1 1 1 1 0
+ 1 1 1 1 1
+ 0 0 0 1 0
+ 0 1 0 0 0
+ 1 0 0 0 0
+ norm= 0.38 0.38 -0.76 0.38 0
+ has a determinate of 1, but g.e. after subtracting pt. 0 has
+ 0's in the diagonal, even with full pivoting. It does work
+ if you subtract pt. 4 instead. */
+ }
+ }
+ }
+ facet->upperdelaunay= False;
+ if (qh DELAUNAY) {
+ if (qh UPPERdelaunay) { /* matches qh_triangulate_facet and qh.lower_threshold in qh_initbuild */
+ if (facet->normal[qh hull_dim -1] >= qh ANGLEround * qh_ZEROdelaunay)
+ facet->upperdelaunay= True;
+ }else {
+ if (facet->normal[qh hull_dim -1] > -qh ANGLEround * qh_ZEROdelaunay)
+ facet->upperdelaunay= True;
+ }
+ }
+ if (qh PRINTstatistics || qh IStracing || qh TRACElevel || qh JOGGLEmax < REALmax) {
+ qh old_randomdist= qh RANDOMdist;
+ qh RANDOMdist= False;
+ FOREACHvertex_(facet->vertices) {
+ if (vertex->point != point0) {
+ boolT istrace= False;
+ zinc_(Zdiststat);
+ qh_distplane(vertex->point, facet, &dist);
+ dist= fabs_(dist);
+ zinc_(Znewvertex);
+ wadd_(Wnewvertex, dist);
+ if (dist > wwval_(Wnewvertexmax)) {
+ wwval_(Wnewvertexmax)= dist;
+ if (dist > qh max_outside) {
+ qh max_outside= dist; /* used by qh_maxouter() */
+ if (dist > qh TRACEdist)
+ istrace= True;
+ }
+ }else if (-dist > qh TRACEdist)
+ istrace= True;
+ if (istrace) {
+ qh_fprintf(qh ferr, 8016, "qh_setfacetplane: ====== vertex p%d(v%d) increases max_outside to %2.2g for new facet f%d last p%d\n",
+ qh_pointid(vertex->point), vertex->id, dist, facet->id, qh furthest_id);
+ qh_errprint("DISTANT", facet, NULL, NULL, NULL);
+ }
+ }
+ }
+ qh RANDOMdist= qh old_randomdist;
+ }
+ if (qh IStracing >= 3) {
+ qh_fprintf(qh ferr, 8017, "qh_setfacetplane: f%d offset %2.2g normal: ",
+ facet->id, facet->offset);
+ for (k=0; k < qh hull_dim; k++)
+ qh_fprintf(qh ferr, 8018, "%2.2g ", facet->normal[k]);
+ qh_fprintf(qh ferr, 8019, "\n");
+ }
+ if (facet == qh tracefacet)
+ qh IStracing= oldtrace;
+} /* setfacetplane */
+
+
+/*---------------------------------
+
+ qh_sethyperplane_det( dim, rows, point0, toporient, normal, offset, nearzero )
+ given dim X dim array indexed by rows[], one row per point,
+ toporient(flips all signs),
+ and point0 (any row)
+ set normalized hyperplane equation from oriented simplex
+
+ returns:
+ normal (normalized)
+ offset (places point0 on the hyperplane)
+ sets nearzero if hyperplane not through points
+
+ notes:
+ only defined for dim == 2..4
+ rows[] is not modified
+ solves det(P-V_0, V_n-V_0, ..., V_1-V_0)=0, i.e. every point is on hyperplane
+ see Bower & Woodworth, A programmer's geometry, Butterworths 1983.
+
+ derivation of 3-d minnorm
+ Goal: all vertices V_i within qh.one_merge of hyperplane
+ Plan: exactly translate the facet so that V_0 is the origin
+ exactly rotate the facet so that V_1 is on the x-axis and y_2=0.
+ exactly rotate the effective perturbation to only effect n_0
+ this introduces a factor of sqrt(3)
+ n_0 = ((y_2-y_0)*(z_1-z_0) - (z_2-z_0)*(y_1-y_0)) / norm
+ Let M_d be the max coordinate difference
+ Let M_a be the greater of M_d and the max abs. coordinate
+ Let u be machine roundoff and distround be max error for distance computation
+ The max error for n_0 is sqrt(3) u M_a M_d / norm. n_1 is approx. 1 and n_2 is approx. 0
+ The max error for distance of V_1 is sqrt(3) u M_a M_d M_d / norm. Offset=0 at origin
+ Then minnorm = 1.8 u M_a M_d M_d / qh.ONEmerge
+ Note that qh.one_merge is approx. 45.5 u M_a and norm is usually about M_d M_d
+
+ derivation of 4-d minnorm
+ same as above except rotate the facet so that V_1 on x-axis and w_2, y_3, w_3=0
+ [if two vertices fixed on x-axis, can rotate the other two in yzw.]
+ n_0 = det3_(...) = y_2 det2_(z_1, w_1, z_3, w_3) = - y_2 w_1 z_3
+ [all other terms contain at least two factors nearly zero.]
+ The max error for n_0 is sqrt(4) u M_a M_d M_d / norm
+ Then minnorm = 2 u M_a M_d M_d M_d / qh.ONEmerge
+ Note that qh.one_merge is approx. 82 u M_a and norm is usually about M_d M_d M_d
+*/
+void qh_sethyperplane_det(int dim, coordT **rows, coordT *point0,
+ boolT toporient, coordT *normal, realT *offset, boolT *nearzero) {
+ realT maxround, dist;
+ int i;
+ pointT *point;
+
+
+ if (dim == 2) {
+ normal[0]= dY(1,0);
+ normal[1]= dX(0,1);
+ qh_normalize2 (normal, dim, toporient, NULL, NULL);
+ *offset= -(point0[0]*normal[0]+point0[1]*normal[1]);
+ *nearzero= False; /* since nearzero norm => incident points */
+ }else if (dim == 3) {
+ normal[0]= det2_(dY(2,0), dZ(2,0),
+ dY(1,0), dZ(1,0));
+ normal[1]= det2_(dX(1,0), dZ(1,0),
+ dX(2,0), dZ(2,0));
+ normal[2]= det2_(dX(2,0), dY(2,0),
+ dX(1,0), dY(1,0));
+ qh_normalize2 (normal, dim, toporient, NULL, NULL);
+ *offset= -(point0[0]*normal[0] + point0[1]*normal[1]
+ + point0[2]*normal[2]);
+ maxround= qh DISTround;
+ for (i=dim; i--; ) {
+ point= rows[i];
+ if (point != point0) {
+ dist= *offset + (point[0]*normal[0] + point[1]*normal[1]
+ + point[2]*normal[2]);
+ if (dist > maxround || dist < -maxround) {
+ *nearzero= True;
+ break;
+ }
+ }
+ }
+ }else if (dim == 4) {
+ normal[0]= - det3_(dY(2,0), dZ(2,0), dW(2,0),
+ dY(1,0), dZ(1,0), dW(1,0),
+ dY(3,0), dZ(3,0), dW(3,0));
+ normal[1]= det3_(dX(2,0), dZ(2,0), dW(2,0),
+ dX(1,0), dZ(1,0), dW(1,0),
+ dX(3,0), dZ(3,0), dW(3,0));
+ normal[2]= - det3_(dX(2,0), dY(2,0), dW(2,0),
+ dX(1,0), dY(1,0), dW(1,0),
+ dX(3,0), dY(3,0), dW(3,0));
+ normal[3]= det3_(dX(2,0), dY(2,0), dZ(2,0),
+ dX(1,0), dY(1,0), dZ(1,0),
+ dX(3,0), dY(3,0), dZ(3,0));
+ qh_normalize2 (normal, dim, toporient, NULL, NULL);
+ *offset= -(point0[0]*normal[0] + point0[1]*normal[1]
+ + point0[2]*normal[2] + point0[3]*normal[3]);
+ maxround= qh DISTround;
+ for (i=dim; i--; ) {
+ point= rows[i];
+ if (point != point0) {
+ dist= *offset + (point[0]*normal[0] + point[1]*normal[1]
+ + point[2]*normal[2] + point[3]*normal[3]);
+ if (dist > maxround || dist < -maxround) {
+ *nearzero= True;
+ break;
+ }
+ }
+ }
+ }
+ if (*nearzero) {
+ zzinc_(Zminnorm);
+ trace0((qh ferr, 3, "qh_sethyperplane_det: degenerate norm during p%d.\n", qh furthest_id));
+ zzinc_(Znearlysingular);
+ }
+} /* sethyperplane_det */
+
+
+/*---------------------------------
+
+ qh_sethyperplane_gauss( dim, rows, point0, toporient, normal, offset, nearzero )
+ given(dim-1) X dim array of rows[i]= V_{i+1} - V_0 (point0)
+ set normalized hyperplane equation from oriented simplex
+
+ returns:
+ normal (normalized)
+ offset (places point0 on the hyperplane)
+
+ notes:
+ if nearzero
+ orientation may be incorrect because of incorrect sign flips in gausselim
+ solves [V_n-V_0,...,V_1-V_0, 0 .. 0 1] * N == [0 .. 0 1]
+ or [V_n-V_0,...,V_1-V_0, 0 .. 0 1] * N == [0]
+ i.e., N is normal to the hyperplane, and the unnormalized
+ distance to [0 .. 1] is either 1 or 0
+
+ design:
+ perform gaussian elimination
+ flip sign for negative values
+ perform back substitution
+ normalize result
+ compute offset
+*/
+void qh_sethyperplane_gauss(int dim, coordT **rows, pointT *point0,
+ boolT toporient, coordT *normal, coordT *offset, boolT *nearzero) {
+ coordT *pointcoord, *normalcoef;
+ int k;
+ boolT sign= toporient, nearzero2= False;
+
+ qh_gausselim(rows, dim-1, dim, &sign, nearzero);
+ for (k=dim-1; k--; ) {
+ if ((rows[k])[k] < 0)
+ sign ^= 1;
+ }
+ if (*nearzero) {
+ zzinc_(Znearlysingular);
+ trace0((qh ferr, 4, "qh_sethyperplane_gauss: nearly singular or axis parallel hyperplane during p%d.\n", qh furthest_id));
+ qh_backnormal(rows, dim-1, dim, sign, normal, &nearzero2);
+ }else {
+ qh_backnormal(rows, dim-1, dim, sign, normal, &nearzero2);
+ if (nearzero2) {
+ zzinc_(Znearlysingular);
+ trace0((qh ferr, 5, "qh_sethyperplane_gauss: singular or axis parallel hyperplane at normalization during p%d.\n", qh furthest_id));
+ }
+ }
+ if (nearzero2)
+ *nearzero= True;
+ qh_normalize2(normal, dim, True, NULL, NULL);
+ pointcoord= point0;
+ normalcoef= normal;
+ *offset= -(*pointcoord++ * *normalcoef++);
+ for (k=dim-1; k--; )
+ *offset -= *pointcoord++ * *normalcoef++;
+} /* sethyperplane_gauss */
+
+
+
diff --git a/src/libqhullr/geom_r.h b/src/libqhullr/geom_r.h
new file mode 100644
index 0000000..bf10577
--- /dev/null
+++ b/src/libqhullr/geom_r.h
@@ -0,0 +1,176 @@
+/*
---------------------------------
+
+ geom.h
+ header file for geometric routines
+
+ see qh-geom.htm and geom.c
+
+ Copyright (c) 1993-2012 The Geometry Center.
+ $Id: //main/2011/qhull/src/libqhullr/geom_r.h#1 $$Change: 1640 $
+ $DateTime: 2014/01/15 09:12:08 $$Author: bbarber $
+*/
+
+#ifndef qhDEFgeom
+#define qhDEFgeom 1
+
+#include "libqhull.h"
+
+/* ============ -macros- ======================== */
+
+/*----------------------------------
+
+ fabs_(a)
+ returns the absolute value of a
+*/
+#define fabs_( a ) ((( a ) < 0 ) ? -( a ):( a ))
+
+/*----------------------------------
+
+ fmax_(a,b)
+ returns the maximum value of a and b
+*/
+#define fmax_( a,b ) ( ( a ) < ( b ) ? ( b ) : ( a ) )
+
+/*----------------------------------
+
+ fmin_(a,b)
+ returns the minimum value of a and b
+*/
+#define fmin_( a,b ) ( ( a ) > ( b ) ? ( b ) : ( a ) )
+
+/*----------------------------------
+
+ maximize_(maxval, val)
+ set maxval to val if val is greater than maxval
+*/
+#define maximize_( maxval, val ) { if (( maxval ) < ( val )) ( maxval )= ( val ); }
+
+/*----------------------------------
+
+ minimize_(minval, val)
+ set minval to val if val is less than minval
+*/
+#define minimize_( minval, val ) { if (( minval ) > ( val )) ( minval )= ( val ); }
+
+/*----------------------------------
+
+ det2_(a1, a2,
+ b1, b2)
+
+ compute a 2-d determinate
+*/
+#define det2_( a1,a2,b1,b2 ) (( a1 )*( b2 ) - ( a2 )*( b1 ))
+
+/*----------------------------------
+
+ det3_(a1, a2, a3,
+ b1, b2, b3,
+ c1, c2, c3)
+
+ compute a 3-d determinate
+*/
+#define det3_( a1,a2,a3,b1,b2,b3,c1,c2,c3 ) ( ( a1 )*det2_( b2,b3,c2,c3 ) \
+ - ( b1 )*det2_( a2,a3,c2,c3 ) + ( c1 )*det2_( a2,a3,b2,b3 ) )
+
+/*----------------------------------
+
+ dX( p1, p2 )
+ dY( p1, p2 )
+ dZ( p1, p2 )
+
+ given two indices into rows[],
+
+ compute the difference between X, Y, or Z coordinates
+*/
+#define dX( p1,p2 ) ( *( rows[p1] ) - *( rows[p2] ))
+#define dY( p1,p2 ) ( *( rows[p1]+1 ) - *( rows[p2]+1 ))
+#define dZ( p1,p2 ) ( *( rows[p1]+2 ) - *( rows[p2]+2 ))
+#define dW( p1,p2 ) ( *( rows[p1]+3 ) - *( rows[p2]+3 ))
+
+/*============= prototypes in alphabetical order, infrequent at end ======= */
+
+void qh_backnormal(realT **rows, int numrow, int numcol, boolT sign, coordT *normal, boolT *nearzero);
+void qh_distplane(pointT *point, facetT *facet, realT *dist);
+facetT *qh_findbest(pointT *point, facetT *startfacet,
+ boolT bestoutside, boolT isnewfacets, boolT noupper,
+ realT *dist, boolT *isoutside, int *numpart);
+facetT *qh_findbesthorizon(boolT ischeckmax, pointT *point,
+ facetT *startfacet, boolT noupper, realT *bestdist, int *numpart);
+facetT *qh_findbestnew(pointT *point, facetT *startfacet, realT *dist,
+ boolT bestoutside, boolT *isoutside, int *numpart);
+void qh_gausselim(realT **rows, int numrow, int numcol, boolT *sign, boolT *nearzero);
+realT qh_getangle(pointT *vect1, pointT *vect2);
+pointT *qh_getcenter(setT *vertices);
+pointT *qh_getcentrum(facetT *facet);
+realT qh_getdistance(facetT *facet, facetT *neighbor, realT *mindist, realT *maxdist);
+void qh_normalize(coordT *normal, int dim, boolT toporient);
+void qh_normalize2 (coordT *normal, int dim, boolT toporient,
+ realT *minnorm, boolT *ismin);
+pointT *qh_projectpoint(pointT *point, facetT *facet, realT dist);
+
+void qh_setfacetplane(facetT *newfacets);
+void qh_sethyperplane_det(int dim, coordT **rows, coordT *point0,
+ boolT toporient, coordT *normal, realT *offset, boolT *nearzero);
+void qh_sethyperplane_gauss(int dim, coordT **rows, pointT *point0,
+ boolT toporient, coordT *normal, coordT *offset, boolT *nearzero);
+boolT qh_sharpnewfacets(void);
+
+/*========= infrequently used code in geom2.c =============*/
+
+coordT *qh_copypoints(coordT *points, int numpoints, int dimension);
+void qh_crossproduct(int dim, realT vecA[3], realT vecB[3], realT vecC[3]);
+realT qh_determinant(realT **rows, int dim, boolT *nearzero);
+realT qh_detjoggle(pointT *points, int numpoints, int dimension);
+void qh_detroundoff(void);
+realT qh_detsimplex(pointT *apex, setT *points, int dim, boolT *nearzero);
+realT qh_distnorm(int dim, pointT *point, pointT *normal, realT *offsetp);
+realT qh_distround(int dimension, realT maxabs, realT maxsumabs);
+realT qh_divzero(realT numer, realT denom, realT mindenom1, boolT *zerodiv);
+realT qh_facetarea(facetT *facet);
+realT qh_facetarea_simplex(int dim, coordT *apex, setT *vertices,
+ vertexT *notvertex, boolT toporient, coordT *normal, realT *offset);
+pointT *qh_facetcenter(setT *vertices);
+facetT *qh_findgooddist(pointT *point, facetT *facetA, realT *distp, facetT **facetlist);
+void qh_getarea(facetT *facetlist);
+boolT qh_gram_schmidt(int dim, realT **rows);
+boolT qh_inthresholds(coordT *normal, realT *angle);
+void qh_joggleinput(void);
+realT *qh_maxabsval(realT *normal, int dim);
+setT *qh_maxmin(pointT *points, int numpoints, int dimension);
+realT qh_maxouter(void);
+void qh_maxsimplex(int dim, setT *maxpoints, pointT *points, int numpoints, setT **simplex);
+realT qh_minabsval(realT *normal, int dim);
+int qh_mindiff(realT *vecA, realT *vecB, int dim);
+boolT qh_orientoutside(facetT *facet);
+void qh_outerinner(facetT *facet, realT *outerplane, realT *innerplane);
+coordT qh_pointdist(pointT *point1, pointT *point2, int dim);
+void qh_printmatrix(FILE *fp, const char *string, realT **rows, int numrow, int numcol);
+void qh_printpoints(FILE *fp, const char *string, setT *points);
+void qh_projectinput(void);
+void qh_projectpoints(signed char *project, int n, realT *points,
+ int numpoints, int dim, realT *newpoints, int newdim);
+void qh_rotateinput(realT **rows);
+void qh_rotatepoints(realT *points, int numpoints, int dim, realT **rows);
+void qh_scaleinput(void);
+void qh_scalelast(coordT *points, int numpoints, int dim, coordT low,
+ coordT high, coordT newhigh);
+void qh_scalepoints(pointT *points, int numpoints, int dim,
+ realT *newlows, realT *newhighs);
+boolT qh_sethalfspace(int dim, coordT *coords, coordT **nextp,
+ coordT *normal, coordT *offset, coordT *feasible);
+coordT *qh_sethalfspace_all(int dim, int count, coordT *halfspaces, pointT *feasible);
+pointT *qh_voronoi_center(int dim, setT *points);
+
+#endif /* qhDEFgeom */
+
+
+
diff --git a/src/libqhullr/global_r.c b/src/libqhullr/global_r.c
new file mode 100644
index 0000000..66a4b70
--- /dev/null
+++ b/src/libqhullr/global_r.c
@@ -0,0 +1,2127 @@
+
+/*
---------------------------------
+
+ global.c
+ initializes all the globals of the qhull application
+
+ see README
+
+ see libqhull.h for qh.globals and function prototypes
+
+ see qhull_a.h for internal functions
+
+ Copyright (c) 1993-2012 The Geometry Center.
+ $Id: //main/2011/qhull/src/libqhullr/global_r.c#1 $$Change: 1640 $
+ $DateTime: 2014/01/15 09:12:08 $$Author: bbarber $
+ */
+
+#include "qhull_a.h"
+
+/*========= qh definition -- globals defined in libqhull.h =======================*/
+
+int qhull_inuse= 0; /* not used */
+
+#if qh_QHpointer
+qhT *qh_qh= NULL; /* pointer to all global variables */
+#else
+qhT qh_qh; /* all global variables.
+ Add "= {0}" if this causes a compiler error.
+ Also qh_qhstat in stat.c and qhmem in mem.c. */
+#endif
+
+/*----------------------------------
+
+ qh_version
+ version string by year and date
+
+ the revision increases on code changes only
+
+ notes:
+ change date: Changes.txt, Announce.txt, index.htm, README.txt,
+ qhull-news.html, Eudora signatures, CMakeLists.txt
+ change version: README.txt, qh-get.htm, File_id.diz, Makefile.txt
+ change year: Copying.txt
+ check download size
+ recompile user_eg.c, rbox.c, libqhull.c, qconvex.c, qdelaun.c qvoronoi.c, qhalf.c, testqset.c
+*/
+
+const char *qh_version = "2012.1 2012/02/18";
+
+/*---------------------------------
+
+ qh_appendprint( printFormat )
+ append printFormat to qh.PRINTout unless already defined
+*/
+void qh_appendprint(qh_PRINT format) {
+ int i;
+
+ for (i=0; i < qh_PRINTEND; i++) {
+ if (qh PRINTout[i] == format && format != qh_PRINTqhull)
+ break;
+ if (!qh PRINTout[i]) {
+ qh PRINTout[i]= format;
+ break;
+ }
+ }
+} /* appendprint */
+
+/*---------------------------------
+
+ qh_checkflags( commandStr, hiddenFlags )
+ errors if commandStr contains hiddenFlags
+ hiddenFlags starts and ends with a space and is space deliminated (checked)
+
+ notes:
+ ignores first word (e.g., "qconvex i")
+ use qh_strtol/strtod since strtol/strtod may or may not skip trailing spaces
+
+ see:
+ qh_initflags() initializes Qhull according to commandStr
+*/
+void qh_checkflags(char *command, char *hiddenflags) {
+ char *s= command, *t, *chkerr; /* qh_skipfilename is non-const */
+ char key, opt, prevopt;
+ char chkkey[]= " ";
+ char chkopt[]= " ";
+ char chkopt2[]= " ";
+ boolT waserr= False;
+
+ if (*hiddenflags != ' ' || hiddenflags[strlen(hiddenflags)-1] != ' ') {
+ qh_fprintf(qh ferr, 6026, "qhull error (qh_checkflags): hiddenflags must start and end with a space: \"%s\"", hiddenflags);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ if (strpbrk(hiddenflags, ",\n\r\t")) {
+ qh_fprintf(qh ferr, 6027, "qhull error (qh_checkflags): hiddenflags contains commas, newlines, or tabs: \"%s\"", hiddenflags);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ while (*s && !isspace(*s)) /* skip program name */
+ s++;
+ while (*s) {
+ while (*s && isspace(*s))
+ s++;
+ if (*s == '-')
+ s++;
+ if (!*s)
+ break;
+ key = *s++;
+ chkerr = NULL;
+ if (key == 'T' && (*s == 'I' || *s == 'O')) { /* TI or TO 'file name' */
+ s= qh_skipfilename(++s);
+ continue;
+ }
+ chkkey[1]= key;
+ if (strstr(hiddenflags, chkkey)) {
+ chkerr= chkkey;
+ }else if (isupper(key)) {
+ opt= ' ';
+ prevopt= ' ';
+ chkopt[1]= key;
+ chkopt2[1]= key;
+ while (!chkerr && *s && !isspace(*s)) {
+ opt= *s++;
+ if (isalpha(opt)) {
+ chkopt[2]= opt;
+ if (strstr(hiddenflags, chkopt))
+ chkerr= chkopt;
+ if (prevopt != ' ') {
+ chkopt2[2]= prevopt;
+ chkopt2[3]= opt;
+ if (strstr(hiddenflags, chkopt2))
+ chkerr= chkopt2;
+ }
+ }else if (key == 'Q' && isdigit(opt) && prevopt != 'b'
+ && (prevopt == ' ' || islower(prevopt))) {
+ chkopt[2]= opt;
+ if (strstr(hiddenflags, chkopt))
+ chkerr= chkopt;
+ }else {
+ qh_strtod(s-1, &t);
+ if (s < t)
+ s= t;
+ }
+ prevopt= opt;
+ }
+ }
+ if (chkerr) {
+ *chkerr= '\'';
+ chkerr[strlen(chkerr)-1]= '\'';
+ qh_fprintf(qh ferr, 6029, "qhull error: option %s is not used with this program.\n It may be used with qhull.\n", chkerr);
+ waserr= True;
+ }
+ }
+ if (waserr)
+ qh_errexit(qh_ERRinput, NULL, NULL);
+} /* checkflags */
+
+/*---------------------------------
+
+ qh_clear_outputflags()
+ Clear output flags for QhullPoints
+*/
+void qh_clear_outputflags(void) {
+ int i,k;
+
+ qh ANNOTATEoutput= False;
+ qh DOintersections= False;
+ qh DROPdim= -1;
+ qh FORCEoutput= False;
+ qh GETarea= False;
+ qh GOODpoint= 0;
+ qh GOODpointp= NULL;
+ qh GOODthreshold= False;
+ qh GOODvertex= 0;
+ qh GOODvertexp= NULL;
+ qh IStracing= 0;
+ qh KEEParea= False;
+ qh KEEPmerge= False;
+ qh KEEPminArea= REALmax;
+ qh PRINTcentrums= False;
+ qh PRINTcoplanar= False;
+ qh PRINTdots= False;
+ qh PRINTgood= False;
+ qh PRINTinner= False;
+ qh PRINTneighbors= False;
+ qh PRINTnoplanes= False;
+ qh PRINToptions1st= False;
+ qh PRINTouter= False;
+ qh PRINTprecision= True;
+ qh PRINTridges= False;
+ qh PRINTspheres= False;
+ qh PRINTstatistics= False;
+ qh PRINTsummary= False;
+ qh PRINTtransparent= False;
+ qh SPLITthresholds= False;
+ qh TRACElevel= 0;
+ qh TRInormals= False;
+ qh USEstdout= False;
+ qh VERIFYoutput= False;
+ for (k=qh input_dim+1; k--; ) { /* duplicated in qh_initqhull_buffers and qh_clear_ouputflags */
+ qh lower_threshold[k]= -REALmax;
+ qh upper_threshold[k]= REALmax;
+ qh lower_bound[k]= -REALmax;
+ qh upper_bound[k]= REALmax;
+ }
+
+ for (i=0; i < qh_PRINTEND; i++) {
+ qh PRINTout[i]= qh_PRINTnone;
+ }
+
+ if (!qh qhull_commandsiz2)
+ qh qhull_commandsiz2= (int)strlen(qh qhull_command); /* WARN64 */
+ else {
+ qh qhull_command[qh qhull_commandsiz2]= '\0';
+ }
+ if (!qh qhull_optionsiz2)
+ qh qhull_optionsiz2= (int)strlen(qh qhull_options); /* WARN64 */
+ else {
+ qh qhull_options[qh qhull_optionsiz2]= '\0';
+ qh qhull_optionlen= qh_OPTIONline; /* start a new line */
+ }
+} /* clear_outputflags */
+
+/*---------------------------------
+
+ qh_clock()
+ return user CPU time in 100ths (qh_SECtick)
+ only defined for qh_CLOCKtype == 2
+
+ notes:
+ use first value to determine time 0
+ from Stevens '92 8.15
+*/
+unsigned long qh_clock(void) {
+
+#if (qh_CLOCKtype == 2)
+ struct tms time;
+ static long clktck; /* initialized first call */
+ double ratio, cpu;
+ unsigned long ticks;
+
+ if (!clktck) {
+ if ((clktck= sysconf(_SC_CLK_TCK)) < 0) {
+ qh_fprintf(qh ferr, 6030, "qhull internal error (qh_clock): sysconf() failed. Use qh_CLOCKtype 1 in user.h\n");
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ }
+ if (times(&time) == -1) {
+ qh_fprintf(qh ferr, 6031, "qhull internal error (qh_clock): times() failed. Use qh_CLOCKtype 1 in user.h\n");
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ ratio= qh_SECticks / (double)clktck;
+ ticks= time.tms_utime * ratio;
+ return ticks;
+#else
+ qh_fprintf(qh ferr, 6032, "qhull internal error (qh_clock): use qh_CLOCKtype 2 in user.h\n");
+ qh_errexit(qh_ERRqhull, NULL, NULL); /* never returns */
+ return 0;
+#endif
+} /* clock */
+
+/*---------------------------------
+
+ qh_freebuffers()
+ free up global memory buffers
+
+ notes:
+ must match qh_initbuffers()
+*/
+void qh_freebuffers(void) {
+
+ trace5((qh ferr, 5001, "qh_freebuffers: freeing up global memory buffers\n"));
+ /* allocated by qh_initqhull_buffers */
+ qh_memfree(qh NEARzero, qh hull_dim * sizeof(realT));
+ qh_memfree(qh lower_threshold, (qh input_dim+1) * sizeof(realT));
+ qh_memfree(qh upper_threshold, (qh input_dim+1) * sizeof(realT));
+ qh_memfree(qh lower_bound, (qh input_dim+1) * sizeof(realT));
+ qh_memfree(qh upper_bound, (qh input_dim+1) * sizeof(realT));
+ qh_memfree(qh gm_matrix, (qh hull_dim+1) * qh hull_dim * sizeof(coordT));
+ qh_memfree(qh gm_row, (qh hull_dim+1) * sizeof(coordT *));
+ qh NEARzero= qh lower_threshold= qh upper_threshold= NULL;
+ qh lower_bound= qh upper_bound= NULL;
+ qh gm_matrix= NULL;
+ qh gm_row= NULL;
+ qh_setfree(&qh other_points);
+ qh_setfree(&qh del_vertices);
+ qh_setfree(&qh coplanarfacetset);
+ if (qh line) /* allocated by qh_readinput, freed if no error */
+ qh_free(qh line);
+ if (qh half_space)
+ qh_free(qh half_space);
+ if (qh temp_malloc)
+ qh_free(qh temp_malloc);
+ if (qh feasible_point) /* allocated by qh_readfeasible */
+ qh_free(qh feasible_point);
+ if (qh feasible_string) /* allocated by qh_initflags */
+ qh_free(qh feasible_string);
+ qh line= qh feasible_string= NULL;
+ qh half_space= qh feasible_point= qh temp_malloc= NULL;
+ /* usually allocated by qh_readinput */
+ if (qh first_point && qh POINTSmalloc) {
+ qh_free(qh first_point);
+ qh first_point= NULL;
+ }
+ if (qh input_points && qh input_malloc) { /* set by qh_joggleinput */
+ qh_free(qh input_points);
+ qh input_points= NULL;
+ }
+ trace5((qh ferr, 5002, "qh_freebuffers: finished\n"));
+} /* freebuffers */
+
+
+/*---------------------------------
+
+ qh_freebuild( allmem )
+ free global memory used by qh_initbuild and qh_buildhull
+ if !allmem,
+ does not free short memory (e.g., facetT, freed by qh_memfreeshort)
+
+ design:
+ free centrums
+ free each vertex
+ mark unattached ridges
+ for each facet
+ free ridges
+ free outside set, coplanar set, neighbor set, ridge set, vertex set
+ free facet
+ free hash table
+ free interior point
+ free merge set
+ free temporary sets
+*/
+void qh_freebuild(boolT allmem) {
+ facetT *facet;
+ vertexT *vertex;
+ ridgeT *ridge, **ridgep;
+ mergeT *merge, **mergep;
+
+ trace1((qh ferr, 1005, "qh_freebuild: free memory from qh_inithull and qh_buildhull\n"));
+ if (qh del_vertices)
+ qh_settruncate(qh del_vertices, 0);
+ if (allmem) {
+ while ((vertex= qh vertex_list)) {
+ if (vertex->next)
+ qh_delvertex(vertex);
+ else {
+ qh_memfree(vertex, (int)sizeof(vertexT));
+ qh newvertex_list= qh vertex_list= NULL;
+ }
+ }
+ }else if (qh VERTEXneighbors) {
+ FORALLvertices
+ qh_setfreelong(&(vertex->neighbors));
+ }
+ qh VERTEXneighbors= False;
+ qh GOODclosest= NULL;
+ if (allmem) {
+ FORALLfacets {
+ FOREACHridge_(facet->ridges)
+ ridge->seen= False;
+ }
+ FORALLfacets {
+ if (facet->visible) {
+ FOREACHridge_(facet->ridges) {
+ if (!otherfacet_(ridge, facet)->visible)
+ ridge->seen= True; /* an unattached ridge */
+ }
+ }
+ }
+ while ((facet= qh facet_list)) {
+ FOREACHridge_(facet->ridges) {
+ if (ridge->seen) {
+ qh_setfree(&(ridge->vertices));
+ qh_memfree(ridge, (int)sizeof(ridgeT));
+ }else
+ ridge->seen= True;
+ }
+ qh_setfree(&(facet->outsideset));
+ qh_setfree(&(facet->coplanarset));
+ qh_setfree(&(facet->neighbors));
+ qh_setfree(&(facet->ridges));
+ qh_setfree(&(facet->vertices));
+ if (facet->next)
+ qh_delfacet(facet);
+ else {
+ qh_memfree(facet, (int)sizeof(facetT));
+ qh visible_list= qh newfacet_list= qh facet_list= NULL;
+ }
+ }
+ }else {
+ FORALLfacets {
+ qh_setfreelong(&(facet->outsideset));
+ qh_setfreelong(&(facet->coplanarset));
+ if (!facet->simplicial) {
+ qh_setfreelong(&(facet->neighbors));
+ qh_setfreelong(&(facet->ridges));
+ qh_setfreelong(&(facet->vertices));
+ }
+ }
+ }
+ qh_setfree(&(qh hash_table));
+ qh_memfree(qh interior_point, qh normal_size);
+ qh interior_point= NULL;
+ FOREACHmerge_(qh facet_mergeset) /* usually empty */
+ qh_memfree(merge, (int)sizeof(mergeT));
+ qh facet_mergeset= NULL; /* temp set */
+ qh degen_mergeset= NULL; /* temp set */
+ qh_settempfree_all();
+} /* freebuild */
+
+/*---------------------------------
+
+ qh_freeqhull( allmem )
+ see qh_freeqhull2
+ if qh_QHpointer, frees qh_qh
+*/
+void qh_freeqhull(boolT allmem) {
+ qh_freeqhull2(allmem);
+#if qh_QHpointer
+ qh_free(qh_qh);
+ qh_qh= NULL;
+#endif
+}
+
+/*---------------------------------
+
+qh_freeqhull2( allmem )
+ free global memory
+ if !allmem,
+ does not free short memory (freed by qh_memfreeshort)
+
+notes:
+ sets qh.NOerrexit in case caller forgets to
+
+see:
+ see qh_initqhull_start2()
+
+design:
+ free global and temporary memory from qh_initbuild and qh_buildhull
+ free buffers
+ free statistics
+*/
+void qh_freeqhull2(boolT allmem) {
+
+ trace1((qh ferr, 1006, "qh_freeqhull2: free global memory\n"));
+ qh NOerrexit= True; /* no more setjmp since called at exit and ~QhullQh */
+ qh_freebuild(allmem);
+ qh_freebuffers();
+ qh_freestatistics();
+#if qh_QHpointer
+ memset((char *)qh_qh, 0, sizeof(qhT));
+ /* qh_qh freed by caller, qh_freeqhull() */
+#else
+ memset((char *)&qh_qh, 0, sizeof(qhT));
+#endif
+ qh NOerrexit= True;
+} /* freeqhull2 */
+
+/*---------------------------------
+
+ qh_init_A( infile, outfile, errfile, argc, argv )
+ initialize memory and stdio files
+ convert input options to option string (qh.qhull_command)
+
+ notes:
+ infile may be NULL if qh_readpoints() is not called
+
+ errfile should always be defined. It is used for reporting
+ errors. outfile is used for output and format options.
+
+ argc/argv may be 0/NULL
+
+ called before error handling initialized
+ qh_errexit() may not be used
+*/
+void qh_init_A(FILE *infile, FILE *outfile, FILE *errfile, int argc, char *argv[]) {
+ qh_meminit(errfile);
+ qh_initqhull_start(infile, outfile, errfile);
+ qh_init_qhull_command(argc, argv);
+} /* init_A */
+
+/*---------------------------------
+
+ qh_init_B( points, numpoints, dim, ismalloc )
+ initialize globals for points array
+
+ points has numpoints dim-dimensional points
+ points[0] is the first coordinate of the first point
+ points[1] is the second coordinate of the first point
+ points[dim] is the first coordinate of the second point
+
+ ismalloc=True
+ Qhull will call qh_free(points) on exit or input transformation
+ ismalloc=False
+ Qhull will allocate a new point array if needed for input transformation
+
+ qh.qhull_command
+ is the option string.
+ It is defined by qh_init_B(), qh_qhull_command(), or qh_initflags
+
+ returns:
+ if qh.PROJECTinput or (qh.DELAUNAY and qh.PROJECTdelaunay)
+ projects the input to a new point array
+
+ if qh.DELAUNAY,
+ qh.hull_dim is increased by one
+ if qh.ATinfinity,
+ qh_projectinput adds point-at-infinity for Delaunay tri.
+
+ if qh.SCALEinput
+ changes the upper and lower bounds of the input, see qh_scaleinput()
+
+ if qh.ROTATEinput
+ rotates the input by a random rotation, see qh_rotateinput()
+ if qh.DELAUNAY
+ rotates about the last coordinate
+
+ notes:
+ called after points are defined
+ qh_errexit() may be used
+*/
+void qh_init_B(coordT *points, int numpoints, int dim, boolT ismalloc) {
+ qh_initqhull_globals(points, numpoints, dim, ismalloc);
+ if (qhmem.LASTsize == 0)
+ qh_initqhull_mem();
+ /* mem.c and qset.c are initialized */
+ qh_initqhull_buffers();
+ qh_initthresholds(qh qhull_command);
+ if (qh PROJECTinput || (qh DELAUNAY && qh PROJECTdelaunay))
+ qh_projectinput();
+ if (qh SCALEinput)
+ qh_scaleinput();
+ if (qh ROTATErandom >= 0) {
+ qh_randommatrix(qh gm_matrix, qh hull_dim, qh gm_row);
+ if (qh DELAUNAY) {
+ int k, lastk= qh hull_dim-1;
+ for (k=0; k < lastk; k++) {
+ qh gm_row[k][lastk]= 0.0;
+ qh gm_row[lastk][k]= 0.0;
+ }
+ qh gm_row[lastk][lastk]= 1.0;
+ }
+ qh_gram_schmidt(qh hull_dim, qh gm_row);
+ qh_rotateinput(qh gm_row);
+ }
+} /* init_B */
+
+/*---------------------------------
+
+ qh_init_qhull_command( argc, argv )
+ build qh.qhull_command from argc/argv
+
+ returns:
+ a space-delimited string of options (just as typed)
+
+ notes:
+ makes option string easy to input and output
+
+ argc/argv may be 0/NULL
+*/
+void qh_init_qhull_command(int argc, char *argv[]) {
+
+ if (!qh_argv_to_command(argc, argv, qh qhull_command, (int)sizeof(qh qhull_command))){
+ /* Assumes qh.ferr is defined. */
+ qh_fprintf(qh ferr, 6033, "qhull input error: more than %d characters in command line\n",
+ (int)sizeof(qh qhull_command));
+ qh_exit(qh_ERRinput); /* error reported, can not use qh_errexit */
+ }
+} /* init_qhull_command */
+
+/*---------------------------------
+
+ qh_initflags( commandStr )
+ set flags and initialized constants from commandStr
+
+ returns:
+ sets qh.qhull_command to command if needed
+
+ notes:
+ ignores first word (e.g., "qhull d")
+ use qh_strtol/strtod since strtol/strtod may or may not skip trailing spaces
+
+ see:
+ qh_initthresholds() continues processing of 'Pdn' and 'PDn'
+ 'prompt' in unix.c for documentation
+
+ design:
+ for each space-deliminated option group
+ if top-level option
+ check syntax
+ append approriate option to option string
+ set appropriate global variable or append printFormat to print options
+ else
+ for each sub-option
+ check syntax
+ append approriate option to option string
+ set appropriate global variable or append printFormat to print options
+*/
+void qh_initflags(char *command) {
+ int k, i, lastproject;
+ char *s= command, *t, *prev_s, *start, key;
+ boolT isgeom= False, wasproject;
+ realT r;
+
+ if (command <= &qh qhull_command[0] || command > &qh qhull_command[0] + sizeof(qh qhull_command)) {
+ if (command != &qh qhull_command[0]) {
+ *qh qhull_command= '\0';
+ strncat(qh qhull_command, command, sizeof(qh qhull_command)-strlen(qh qhull_command)-1);
+ }
+ while (*s && !isspace(*s)) /* skip program name */
+ s++;
+ }
+ while (*s) {
+ while (*s && isspace(*s))
+ s++;
+ if (*s == '-')
+ s++;
+ if (!*s)
+ break;
+ prev_s= s;
+ switch (*s++) {
+ case 'd':
+ qh_option("delaunay", NULL, NULL);
+ qh DELAUNAY= True;
+ break;
+ case 'f':
+ qh_option("facets", NULL, NULL);
+ qh_appendprint(qh_PRINTfacets);
+ break;
+ case 'i':
+ qh_option("incidence", NULL, NULL);
+ qh_appendprint(qh_PRINTincidences);
+ break;
+ case 'm':
+ qh_option("mathematica", NULL, NULL);
+ qh_appendprint(qh_PRINTmathematica);
+ break;
+ case 'n':
+ qh_option("normals", NULL, NULL);
+ qh_appendprint(qh_PRINTnormals);
+ break;
+ case 'o':
+ qh_option("offFile", NULL, NULL);
+ qh_appendprint(qh_PRINToff);
+ break;
+ case 'p':
+ qh_option("points", NULL, NULL);
+ qh_appendprint(qh_PRINTpoints);
+ break;
+ case 's':
+ qh_option("summary", NULL, NULL);
+ qh PRINTsummary= True;
+ break;
+ case 'v':
+ qh_option("voronoi", NULL, NULL);
+ qh VORONOI= True;
+ qh DELAUNAY= True;
+ break;
+ case 'A':
+ if (!isdigit(*s) && *s != '.' && *s != '-')
+ qh_fprintf(qh ferr, 7002, "qhull warning: no maximum cosine angle given for option 'An'. Ignored.\n");
+ else {
+ if (*s == '-') {
+ qh premerge_cos= -qh_strtod(s, &s);
+ qh_option("Angle-premerge-", NULL, &qh premerge_cos);
+ qh PREmerge= True;
+ }else {
+ qh postmerge_cos= qh_strtod(s, &s);
+ qh_option("Angle-postmerge", NULL, &qh postmerge_cos);
+ qh POSTmerge= True;
+ }
+ qh MERGING= True;
+ }
+ break;
+ case 'C':
+ if (!isdigit(*s) && *s != '.' && *s != '-')
+ qh_fprintf(qh ferr, 7003, "qhull warning: no centrum radius given for option 'Cn'. Ignored.\n");
+ else {
+ if (*s == '-') {
+ qh premerge_centrum= -qh_strtod(s, &s);
+ qh_option("Centrum-premerge-", NULL, &qh premerge_centrum);
+ qh PREmerge= True;
+ }else {
+ qh postmerge_centrum= qh_strtod(s, &s);
+ qh_option("Centrum-postmerge", NULL, &qh postmerge_centrum);
+ qh POSTmerge= True;
+ }
+ qh MERGING= True;
+ }
+ break;
+ case 'E':
+ if (*s == '-')
+ qh_fprintf(qh ferr, 7004, "qhull warning: negative maximum roundoff given for option 'An'. Ignored.\n");
+ else if (!isdigit(*s))
+ qh_fprintf(qh ferr, 7005, "qhull warning: no maximum roundoff given for option 'En'. Ignored.\n");
+ else {
+ qh DISTround= qh_strtod(s, &s);
+ qh_option("Distance-roundoff", NULL, &qh DISTround);
+ qh SETroundoff= True;
+ }
+ break;
+ case 'H':
+ start= s;
+ qh HALFspace= True;
+ qh_strtod(s, &t);
+ while (t > s) {
+ if (*t && !isspace(*t)) {
+ if (*t == ',')
+ t++;
+ else
+ qh_fprintf(qh ferr, 7006, "qhull warning: origin for Halfspace intersection should be 'Hn,n,n,...'\n");
+ }
+ s= t;
+ qh_strtod(s, &t);
+ }
+ if (start < t) {
+ if (!(qh feasible_string= (char*)calloc((size_t)(t-start+1), (size_t)1))) {
+ qh_fprintf(qh ferr, 6034, "qhull error: insufficient memory for 'Hn,n,n'\n");
+ qh_errexit(qh_ERRmem, NULL, NULL);
+ }
+ strncpy(qh feasible_string, start, (size_t)(t-start));
+ qh_option("Halfspace-about", NULL, NULL);
+ qh_option(qh feasible_string, NULL, NULL);
+ }else
+ qh_option("Halfspace", NULL, NULL);
+ break;
+ case 'R':
+ if (!isdigit(*s))
+ qh_fprintf(qh ferr, 7007, "qhull warning: missing random perturbation for option 'Rn'. Ignored\n");
+ else {
+ qh RANDOMfactor= qh_strtod(s, &s);
+ qh_option("Random_perturb", NULL, &qh RANDOMfactor);
+ qh RANDOMdist= True;
+ }
+ break;
+ case 'V':
+ if (!isdigit(*s) && *s != '-')
+ qh_fprintf(qh ferr, 7008, "qhull warning: missing visible distance for option 'Vn'. Ignored\n");
+ else {
+ qh MINvisible= qh_strtod(s, &s);
+ qh_option("Visible", NULL, &qh MINvisible);
+ }
+ break;
+ case 'U':
+ if (!isdigit(*s) && *s != '-')
+ qh_fprintf(qh ferr, 7009, "qhull warning: missing coplanar distance for option 'Un'. Ignored\n");
+ else {
+ qh MAXcoplanar= qh_strtod(s, &s);
+ qh_option("U-coplanar", NULL, &qh MAXcoplanar);
+ }
+ break;
+ case 'W':
+ if (*s == '-')
+ qh_fprintf(qh ferr, 7010, "qhull warning: negative outside width for option 'Wn'. Ignored.\n");
+ else if (!isdigit(*s))
+ qh_fprintf(qh ferr, 7011, "qhull warning: missing outside width for option 'Wn'. Ignored\n");
+ else {
+ qh MINoutside= qh_strtod(s, &s);
+ qh_option("W-outside", NULL, &qh MINoutside);
+ qh APPROXhull= True;
+ }
+ break;
+ /************ sub menus ***************/
+ case 'F':
+ while (*s && !isspace(*s)) {
+ switch (*s++) {
+ case 'a':
+ qh_option("Farea", NULL, NULL);
+ qh_appendprint(qh_PRINTarea);
+ qh GETarea= True;
+ break;
+ case 'A':
+ qh_option("FArea-total", NULL, NULL);
+ qh GETarea= True;
+ break;
+ case 'c':
+ qh_option("Fcoplanars", NULL, NULL);
+ qh_appendprint(qh_PRINTcoplanars);
+ break;
+ case 'C':
+ qh_option("FCentrums", NULL, NULL);
+ qh_appendprint(qh_PRINTcentrums);
+ break;
+ case 'd':
+ qh_option("Fd-cdd-in", NULL, NULL);
+ qh CDDinput= True;
+ break;
+ case 'D':
+ qh_option("FD-cdd-out", NULL, NULL);
+ qh CDDoutput= True;
+ break;
+ case 'F':
+ qh_option("FFacets-xridge", NULL, NULL);
+ qh_appendprint(qh_PRINTfacets_xridge);
+ break;
+ case 'i':
+ qh_option("Finner", NULL, NULL);
+ qh_appendprint(qh_PRINTinner);
+ break;
+ case 'I':
+ qh_option("FIDs", NULL, NULL);
+ qh_appendprint(qh_PRINTids);
+ break;
+ case 'm':
+ qh_option("Fmerges", NULL, NULL);
+ qh_appendprint(qh_PRINTmerges);
+ break;
+ case 'M':
+ qh_option("FMaple", NULL, NULL);
+ qh_appendprint(qh_PRINTmaple);
+ break;
+ case 'n':
+ qh_option("Fneighbors", NULL, NULL);
+ qh_appendprint(qh_PRINTneighbors);
+ break;
+ case 'N':
+ qh_option("FNeighbors-vertex", NULL, NULL);
+ qh_appendprint(qh_PRINTvneighbors);
+ break;
+ case 'o':
+ qh_option("Fouter", NULL, NULL);
+ qh_appendprint(qh_PRINTouter);
+ break;
+ case 'O':
+ if (qh PRINToptions1st) {
+ qh_option("FOptions", NULL, NULL);
+ qh_appendprint(qh_PRINToptions);
+ }else
+ qh PRINToptions1st= True;
+ break;
+ case 'p':
+ qh_option("Fpoint-intersect", NULL, NULL);
+ qh_appendprint(qh_PRINTpointintersect);
+ break;
+ case 'P':
+ qh_option("FPoint-nearest", NULL, NULL);
+ qh_appendprint(qh_PRINTpointnearest);
+ break;
+ case 'Q':
+ qh_option("FQhull", NULL, NULL);
+ qh_appendprint(qh_PRINTqhull);
+ break;
+ case 's':
+ qh_option("Fsummary", NULL, NULL);
+ qh_appendprint(qh_PRINTsummary);
+ break;
+ case 'S':
+ qh_option("FSize", NULL, NULL);
+ qh_appendprint(qh_PRINTsize);
+ qh GETarea= True;
+ break;
+ case 't':
+ qh_option("Ftriangles", NULL, NULL);
+ qh_appendprint(qh_PRINTtriangles);
+ break;
+ case 'v':
+ /* option set in qh_initqhull_globals */
+ qh_appendprint(qh_PRINTvertices);
+ break;
+ case 'V':
+ qh_option("FVertex-average", NULL, NULL);
+ qh_appendprint(qh_PRINTaverage);
+ break;
+ case 'x':
+ qh_option("Fxtremes", NULL, NULL);
+ qh_appendprint(qh_PRINTextremes);
+ break;
+ default:
+ s--;
+ qh_fprintf(qh ferr, 7012, "qhull warning: unknown 'F' output option %c, rest ignored\n", (int)s[0]);
+ while (*++s && !isspace(*s));
+ break;
+ }
+ }
+ break;
+ case 'G':
+ isgeom= True;
+ qh_appendprint(qh_PRINTgeom);
+ while (*s && !isspace(*s)) {
+ switch (*s++) {
+ case 'a':
+ qh_option("Gall-points", NULL, NULL);
+ qh PRINTdots= True;
+ break;
+ case 'c':
+ qh_option("Gcentrums", NULL, NULL);
+ qh PRINTcentrums= True;
+ break;
+ case 'h':
+ qh_option("Gintersections", NULL, NULL);
+ qh DOintersections= True;
+ break;
+ case 'i':
+ qh_option("Ginner", NULL, NULL);
+ qh PRINTinner= True;
+ break;
+ case 'n':
+ qh_option("Gno-planes", NULL, NULL);
+ qh PRINTnoplanes= True;
+ break;
+ case 'o':
+ qh_option("Gouter", NULL, NULL);
+ qh PRINTouter= True;
+ break;
+ case 'p':
+ qh_option("Gpoints", NULL, NULL);
+ qh PRINTcoplanar= True;
+ break;
+ case 'r':
+ qh_option("Gridges", NULL, NULL);
+ qh PRINTridges= True;
+ break;
+ case 't':
+ qh_option("Gtransparent", NULL, NULL);
+ qh PRINTtransparent= True;
+ break;
+ case 'v':
+ qh_option("Gvertices", NULL, NULL);
+ qh PRINTspheres= True;
+ break;
+ case 'D':
+ if (!isdigit(*s))
+ qh_fprintf(qh ferr, 6035, "qhull input error: missing dimension for option 'GDn'\n");
+ else {
+ if (qh DROPdim >= 0)
+ qh_fprintf(qh ferr, 7013, "qhull warning: can only drop one dimension. Previous 'GD%d' ignored\n",
+ qh DROPdim);
+ qh DROPdim= qh_strtol(s, &s);
+ qh_option("GDrop-dim", &qh DROPdim, NULL);
+ }
+ break;
+ default:
+ s--;
+ qh_fprintf(qh ferr, 7014, "qhull warning: unknown 'G' print option %c, rest ignored\n", (int)s[0]);
+ while (*++s && !isspace(*s));
+ break;
+ }
+ }
+ break;
+ case 'P':
+ while (*s && !isspace(*s)) {
+ switch (*s++) {
+ case 'd': case 'D': /* see qh_initthresholds() */
+ key= s[-1];
+ i= qh_strtol(s, &s);
+ r= 0;
+ if (*s == ':') {
+ s++;
+ r= qh_strtod(s, &s);
+ }
+ if (key == 'd')
+ qh_option("Pdrop-facets-dim-less", &i, &r);
+ else
+ qh_option("PDrop-facets-dim-more", &i, &r);
+ break;
+ case 'g':
+ qh_option("Pgood-facets", NULL, NULL);
+ qh PRINTgood= True;
+ break;
+ case 'G':
+ qh_option("PGood-facet-neighbors", NULL, NULL);
+ qh PRINTneighbors= True;
+ break;
+ case 'o':
+ qh_option("Poutput-forced", NULL, NULL);
+ qh FORCEoutput= True;
+ break;
+ case 'p':
+ qh_option("Pprecision-ignore", NULL, NULL);
+ qh PRINTprecision= False;
+ break;
+ case 'A':
+ if (!isdigit(*s))
+ qh_fprintf(qh ferr, 6036, "qhull input error: missing facet count for keep area option 'PAn'\n");
+ else {
+ qh KEEParea= qh_strtol(s, &s);
+ qh_option("PArea-keep", &qh KEEParea, NULL);
+ qh GETarea= True;
+ }
+ break;
+ case 'F':
+ if (!isdigit(*s))
+ qh_fprintf(qh ferr, 6037, "qhull input error: missing facet area for option 'PFn'\n");
+ else {
+ qh KEEPminArea= qh_strtod(s, &s);
+ qh_option("PFacet-area-keep", NULL, &qh KEEPminArea);
+ qh GETarea= True;
+ }
+ break;
+ case 'M':
+ if (!isdigit(*s))
+ qh_fprintf(qh ferr, 6038, "qhull input error: missing merge count for option 'PMn'\n");
+ else {
+ qh KEEPmerge= qh_strtol(s, &s);
+ qh_option("PMerge-keep", &qh KEEPmerge, NULL);
+ }
+ break;
+ default:
+ s--;
+ qh_fprintf(qh ferr, 7015, "qhull warning: unknown 'P' print option %c, rest ignored\n", (int)s[0]);
+ while (*++s && !isspace(*s));
+ break;
+ }
+ }
+ break;
+ case 'Q':
+ lastproject= -1;
+ while (*s && !isspace(*s)) {
+ switch (*s++) {
+ case 'b': case 'B': /* handled by qh_initthresholds */
+ key= s[-1];
+ if (key == 'b' && *s == 'B') {
+ s++;
+ r= qh_DEFAULTbox;
+ qh SCALEinput= True;
+ qh_option("QbBound-unit-box", NULL, &r);
+ break;
+ }
+ if (key == 'b' && *s == 'b') {
+ s++;
+ qh SCALElast= True;
+ qh_option("Qbbound-last", NULL, NULL);
+ break;
+ }
+ k= qh_strtol(s, &s);
+ r= 0.0;
+ wasproject= False;
+ if (*s == ':') {
+ s++;
+ if ((r= qh_strtod(s, &s)) == 0.0) {
+ t= s; /* need true dimension for memory allocation */
+ while (*t && !isspace(*t)) {
+ if (toupper(*t++) == 'B'
+ && k == qh_strtol(t, &t)
+ && *t++ == ':'
+ && qh_strtod(t, &t) == 0.0) {
+ qh PROJECTinput++;
+ trace2((qh ferr, 2004, "qh_initflags: project dimension %d\n", k));
+ qh_option("Qb-project-dim", &k, NULL);
+ wasproject= True;
+ lastproject= k;
+ break;
+ }
+ }
+ }
+ }
+ if (!wasproject) {
+ if (lastproject == k && r == 0.0)
+ lastproject= -1; /* doesn't catch all possible sequences */
+ else if (key == 'b') {
+ qh SCALEinput= True;
+ if (r == 0.0)
+ r= -qh_DEFAULTbox;
+ qh_option("Qbound-dim-low", &k, &r);
+ }else {
+ qh SCALEinput= True;
+ if (r == 0.0)
+ r= qh_DEFAULTbox;
+ qh_option("QBound-dim-high", &k, &r);
+ }
+ }
+ break;
+ case 'c':
+ qh_option("Qcoplanar-keep", NULL, NULL);
+ qh KEEPcoplanar= True;
+ break;
+ case 'f':
+ qh_option("Qfurthest-outside", NULL, NULL);
+ qh BESToutside= True;
+ break;
+ case 'g':
+ qh_option("Qgood-facets-only", NULL, NULL);
+ qh ONLYgood= True;
+ break;
+ case 'i':
+ qh_option("Qinterior-keep", NULL, NULL);
+ qh KEEPinside= True;
+ break;
+ case 'm':
+ qh_option("Qmax-outside-only", NULL, NULL);
+ qh ONLYmax= True;
+ break;
+ case 'r':
+ qh_option("Qrandom-outside", NULL, NULL);
+ qh RANDOMoutside= True;
+ break;
+ case 's':
+ qh_option("Qsearch-initial-simplex", NULL, NULL);
+ qh ALLpoints= True;
+ break;
+ case 't':
+ qh_option("Qtriangulate", NULL, NULL);
+ qh TRIangulate= True;
+ break;
+ case 'T':
+ qh_option("QTestPoints", NULL, NULL);
+ if (!isdigit(*s))
+ qh_fprintf(qh ferr, 6039, "qhull input error: missing number of test points for option 'QTn'\n");
+ else {
+ qh TESTpoints= qh_strtol(s, &s);
+ qh_option("QTestPoints", &qh TESTpoints, NULL);
+ }
+ break;
+ case 'u':
+ qh_option("QupperDelaunay", NULL, NULL);
+ qh UPPERdelaunay= True;
+ break;
+ case 'v':
+ qh_option("Qvertex-neighbors-convex", NULL, NULL);
+ qh TESTvneighbors= True;
+ break;
+ case 'x':
+ qh_option("Qxact-merge", NULL, NULL);
+ qh MERGEexact= True;
+ break;
+ case 'z':
+ qh_option("Qz-infinity-point", NULL, NULL);
+ qh ATinfinity= True;
+ break;
+ case '0':
+ qh_option("Q0-no-premerge", NULL, NULL);
+ qh NOpremerge= True;
+ break;
+ case '1':
+ if (!isdigit(*s)) {
+ qh_option("Q1-no-angle-sort", NULL, NULL);
+ qh ANGLEmerge= False;
+ break;
+ }
+ switch (*s++) {
+ case '0':
+ qh_option("Q10-no-narrow", NULL, NULL);
+ qh NOnarrow= True;
+ break;
+ case '1':
+ qh_option("Q11-trinormals Qtriangulate", NULL, NULL);
+ qh TRInormals= True;
+ qh TRIangulate= True;
+ break;
+ default:
+ s--;
+ qh_fprintf(qh ferr, 7016, "qhull warning: unknown 'Q' qhull option 1%c, rest ignored\n", (int)s[0]);
+ while (*++s && !isspace(*s));
+ break;
+ }
+ break;
+ case '2':
+ qh_option("Q2-no-merge-independent", NULL, NULL);
+ qh MERGEindependent= False;
+ goto LABELcheckdigit;
+ break; /* no warnings */
+ case '3':
+ qh_option("Q3-no-merge-vertices", NULL, NULL);
+ qh MERGEvertices= False;
+ LABELcheckdigit:
+ if (isdigit(*s))
+ qh_fprintf(qh ferr, 7017, "qhull warning: can not follow '1', '2', or '3' with a digit. '%c' skipped.\n",
+ *s++);
+ break;
+ case '4':
+ qh_option("Q4-avoid-old-into-new", NULL, NULL);
+ qh AVOIDold= True;
+ break;
+ case '5':
+ qh_option("Q5-no-check-outer", NULL, NULL);
+ qh SKIPcheckmax= True;
+ break;
+ case '6':
+ qh_option("Q6-no-concave-merge", NULL, NULL);
+ qh SKIPconvex= True;
+ break;
+ case '7':
+ qh_option("Q7-no-breadth-first", NULL, NULL);
+ qh VIRTUALmemory= True;
+ break;
+ case '8':
+ qh_option("Q8-no-near-inside", NULL, NULL);
+ qh NOnearinside= True;
+ break;
+ case '9':
+ qh_option("Q9-pick-furthest", NULL, NULL);
+ qh PICKfurthest= True;
+ break;
+ case 'G':
+ i= qh_strtol(s, &t);
+ if (qh GOODpoint)
+ qh_fprintf(qh ferr, 7018, "qhull warning: good point already defined for option 'QGn'. Ignored\n");
+ else if (s == t)
+ qh_fprintf(qh ferr, 7019, "qhull warning: missing good point id for option 'QGn'. Ignored\n");
+ else if (i < 0 || *s == '-') {
+ qh GOODpoint= i-1;
+ qh_option("QGood-if-dont-see-point", &i, NULL);
+ }else {
+ qh GOODpoint= i+1;
+ qh_option("QGood-if-see-point", &i, NULL);
+ }
+ s= t;
+ break;
+ case 'J':
+ if (!isdigit(*s) && *s != '-')
+ qh JOGGLEmax= 0.0;
+ else {
+ qh JOGGLEmax= (realT) qh_strtod(s, &s);
+ qh_option("QJoggle", NULL, &qh JOGGLEmax);
+ }
+ break;
+ case 'R':
+ if (!isdigit(*s) && *s != '-')
+ qh_fprintf(qh ferr, 7020, "qhull warning: missing random seed for option 'QRn'. Ignored\n");
+ else {
+ qh ROTATErandom= i= qh_strtol(s, &s);
+ if (i > 0)
+ qh_option("QRotate-id", &i, NULL );
+ else if (i < -1)
+ qh_option("QRandom-seed", &i, NULL );
+ }
+ break;
+ case 'V':
+ i= qh_strtol(s, &t);
+ if (qh GOODvertex)
+ qh_fprintf(qh ferr, 7021, "qhull warning: good vertex already defined for option 'QVn'. Ignored\n");
+ else if (s == t)
+ qh_fprintf(qh ferr, 7022, "qhull warning: no good point id given for option 'QVn'. Ignored\n");
+ else if (i < 0) {
+ qh GOODvertex= i - 1;
+ qh_option("QV-good-facets-not-point", &i, NULL);
+ }else {
+ qh_option("QV-good-facets-point", &i, NULL);
+ qh GOODvertex= i + 1;
+ }
+ s= t;
+ break;
+ default:
+ s--;
+ qh_fprintf(qh ferr, 7023, "qhull warning: unknown 'Q' qhull option %c, rest ignored\n", (int)s[0]);
+ while (*++s && !isspace(*s));
+ break;
+ }
+ }
+ break;
+ case 'T':
+ while (*s && !isspace(*s)) {
+ if (isdigit(*s) || *s == '-')
+ qh IStracing= qh_strtol(s, &s);
+ else switch (*s++) {
+ case 'a':
+ qh_option("Tannotate-output", NULL, NULL);
+ qh ANNOTATEoutput= True;
+ break;
+ case 'c':
+ qh_option("Tcheck-frequently", NULL, NULL);
+ qh CHECKfrequently= True;
+ break;
+ case 's':
+ qh_option("Tstatistics", NULL, NULL);
+ qh PRINTstatistics= True;
+ break;
+ case 'v':
+ qh_option("Tverify", NULL, NULL);
+ qh VERIFYoutput= True;
+ break;
+ case 'z':
+ if (qh ferr == qh_FILEstderr) {
+ /* The C++ interface captures the output in qh_fprint_qhull() */
+ qh_option("Tz-stdout", NULL, NULL);
+ qh USEstdout= True;
+ }else if (!qh fout)
+ qh_fprintf(qh ferr, 7024, "qhull warning: output file undefined(stdout). Option 'Tz' ignored.\n");
+ else {
+ qh_option("Tz-stdout", NULL, NULL);
+ qh USEstdout= True;
+ qh ferr= qh fout;
+ qhmem.ferr= qh fout;
+ }
+ break;
+ case 'C':
+ if (!isdigit(*s))
+ qh_fprintf(qh ferr, 7025, "qhull warning: missing point id for cone for trace option 'TCn'. Ignored\n");
+ else {
+ i= qh_strtol(s, &s);
+ qh_option("TCone-stop", &i, NULL);
+ qh STOPcone= i + 1;
+ }
+ break;
+ case 'F':
+ if (!isdigit(*s))
+ qh_fprintf(qh ferr, 7026, "qhull warning: missing frequency count for trace option 'TFn'. Ignored\n");
+ else {
+ qh REPORTfreq= qh_strtol(s, &s);
+ qh_option("TFacet-log", &qh REPORTfreq, NULL);
+ qh REPORTfreq2= qh REPORTfreq/2; /* for tracemerging() */
+ }
+ break;
+ case 'I':
+ if (!isspace(*s))
+ qh_fprintf(qh ferr, 7027, "qhull warning: missing space between 'TI' and filename, %s\n", s);
+ while (isspace(*s))
+ s++;
+ t= qh_skipfilename(s);
+ {
+ char filename[qh_FILENAMElen];
+
+ qh_copyfilename(filename, (int)sizeof(filename), s, (int)(t-s)); /* WARN64 */
+ s= t;
+ if (!freopen(filename, "r", stdin)) {
+ qh_fprintf(qh ferr, 6041, "qhull error: could not open file \"%s\".", filename);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }else {
+ qh_option("TInput-file", NULL, NULL);
+ qh_option(filename, NULL, NULL);
+ }
+ }
+ break;
+ case 'O':
+ if (!isspace(*s))
+ qh_fprintf(qh ferr, 7028, "qhull warning: missing space between 'TO' and filename, %s\n", s);
+ while (isspace(*s))
+ s++;
+ t= qh_skipfilename(s);
+ {
+ char filename[qh_FILENAMElen];
+
+ qh_copyfilename(filename, (int)sizeof(filename), s, (int)(t-s)); /* WARN64 */
+ s= t;
+ if (!freopen(filename, "w", stdout)) {
+ qh_fprintf(qh ferr, 6044, "qhull error: could not open file \"%s\".", filename);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }else {
+ qh_option("TOutput-file", NULL, NULL);
+ qh_option(filename, NULL, NULL);
+ }
+ }
+ break;
+ case 'P':
+ if (!isdigit(*s))
+ qh_fprintf(qh ferr, 7029, "qhull warning: missing point id for trace option 'TPn'. Ignored\n");
+ else {
+ qh TRACEpoint= qh_strtol(s, &s);
+ qh_option("Trace-point", &qh TRACEpoint, NULL);
+ }
+ break;
+ case 'M':
+ if (!isdigit(*s))
+ qh_fprintf(qh ferr, 7030, "qhull warning: missing merge id for trace option 'TMn'. Ignored\n");
+ else {
+ qh TRACEmerge= qh_strtol(s, &s);
+ qh_option("Trace-merge", &qh TRACEmerge, NULL);
+ }
+ break;
+ case 'R':
+ if (!isdigit(*s))
+ qh_fprintf(qh ferr, 7031, "qhull warning: missing rerun count for trace option 'TRn'. Ignored\n");
+ else {
+ qh RERUN= qh_strtol(s, &s);
+ qh_option("TRerun", &qh RERUN, NULL);
+ }
+ break;
+ case 'V':
+ i= qh_strtol(s, &t);
+ if (s == t)
+ qh_fprintf(qh ferr, 7032, "qhull warning: missing furthest point id for trace option 'TVn'. Ignored\n");
+ else if (i < 0) {
+ qh STOPpoint= i - 1;
+ qh_option("TV-stop-before-point", &i, NULL);
+ }else {
+ qh STOPpoint= i + 1;
+ qh_option("TV-stop-after-point", &i, NULL);
+ }
+ s= t;
+ break;
+ case 'W':
+ if (!isdigit(*s))
+ qh_fprintf(qh ferr, 7033, "qhull warning: missing max width for trace option 'TWn'. Ignored\n");
+ else {
+ qh TRACEdist= (realT) qh_strtod(s, &s);
+ qh_option("TWide-trace", NULL, &qh TRACEdist);
+ }
+ break;
+ default:
+ s--;
+ qh_fprintf(qh ferr, 7034, "qhull warning: unknown 'T' trace option %c, rest ignored\n", (int)s[0]);
+ while (*++s && !isspace(*s));
+ break;
+ }
+ }
+ break;
+ default:
+ qh_fprintf(qh ferr, 7035, "qhull warning: unknown flag %c(%x)\n", (int)s[-1],
+ (int)s[-1]);
+ break;
+ }
+ if (s-1 == prev_s && *s && !isspace(*s)) {
+ qh_fprintf(qh ferr, 7036, "qhull warning: missing space after flag %c(%x); reserved for menu. Skipped.\n",
+ (int)*prev_s, (int)*prev_s);
+ while (*s && !isspace(*s))
+ s++;
+ }
+ }
+ if (qh STOPcone && qh JOGGLEmax < REALmax/2)
+ qh_fprintf(qh ferr, 7078, "qhull warning: 'TCn' (stopCone) ignored when used with 'QJn' (joggle)\n");
+ if (isgeom && !qh FORCEoutput && qh PRINTout[1])
+ qh_fprintf(qh ferr, 7037, "qhull warning: additional output formats are not compatible with Geomview\n");
+ /* set derived values in qh_initqhull_globals */
+} /* initflags */
+
+
+/*---------------------------------
+
+ qh_initqhull_buffers()
+ initialize global memory buffers
+
+ notes:
+ must match qh_freebuffers()
+*/
+void qh_initqhull_buffers(void) {
+ int k;
+
+ qh TEMPsize= (qhmem.LASTsize - sizeof(setT))/SETelemsize;
+ if (qh TEMPsize <= 0 || qh TEMPsize > qhmem.LASTsize)
+ qh TEMPsize= 8; /* e.g., if qh_NOmem */
+ qh other_points= qh_setnew(qh TEMPsize);
+ qh del_vertices= qh_setnew(qh TEMPsize);
+ qh coplanarfacetset= qh_setnew(qh TEMPsize);
+ qh NEARzero= (realT *)qh_memalloc(qh hull_dim * sizeof(realT));
+ qh lower_threshold= (realT *)qh_memalloc((qh input_dim+1) * sizeof(realT));
+ qh upper_threshold= (realT *)qh_memalloc((qh input_dim+1) * sizeof(realT));
+ qh lower_bound= (realT *)qh_memalloc((qh input_dim+1) * sizeof(realT));
+ qh upper_bound= (realT *)qh_memalloc((qh input_dim+1) * sizeof(realT));
+ for (k=qh input_dim+1; k--; ) { /* duplicated in qh_initqhull_buffers and qh_clear_ouputflags */
+ qh lower_threshold[k]= -REALmax;
+ qh upper_threshold[k]= REALmax;
+ qh lower_bound[k]= -REALmax;
+ qh upper_bound[k]= REALmax;
+ }
+ qh gm_matrix= (coordT *)qh_memalloc((qh hull_dim+1) * qh hull_dim * sizeof(coordT));
+ qh gm_row= (coordT **)qh_memalloc((qh hull_dim+1) * sizeof(coordT *));
+} /* initqhull_buffers */
+
+/*---------------------------------
+
+ qh_initqhull_globals( points, numpoints, dim, ismalloc )
+ initialize globals
+ if ismalloc
+ points were malloc'd and qhull should free at end
+
+ returns:
+ sets qh.first_point, num_points, input_dim, hull_dim and others
+ seeds random number generator (seed=1 if tracing)
+ modifies qh.hull_dim if ((qh.DELAUNAY and qh.PROJECTdelaunay) or qh.PROJECTinput)
+ adjust user flags as needed
+ also checks DIM3 dependencies and constants
+
+ notes:
+ do not use qh_point() since an input transformation may move them elsewhere
+
+ see:
+ qh_initqhull_start() sets default values for non-zero globals
+
+ design:
+ initialize points array from input arguments
+ test for qh.ZEROcentrum
+ (i.e., use opposite vertex instead of cetrum for convexity testing)
+ initialize qh.CENTERtype, qh.normal_size,
+ qh.center_size, qh.TRACEpoint/level,
+ initialize and test random numbers
+ qh_initqhull_outputflags() -- adjust and test output flags
+*/
+void qh_initqhull_globals(coordT *points, int numpoints, int dim, boolT ismalloc) {
+ int seed, pointsneeded, extra= 0, i, randi, k;
+ realT randr;
+ realT factorial;
+
+ time_t timedata;
+
+ trace0((qh ferr, 13, "qh_initqhull_globals: for %s | %s\n", qh rbox_command,
+ qh qhull_command));
+ qh POINTSmalloc= ismalloc;
+ qh first_point= points;
+ qh num_points= numpoints;
+ qh hull_dim= qh input_dim= dim;
+ if (!qh NOpremerge && !qh MERGEexact && !qh PREmerge && qh JOGGLEmax > REALmax/2) {
+ qh MERGING= True;
+ if (qh hull_dim <= 4) {
+ qh PREmerge= True;
+ qh_option("_pre-merge", NULL, NULL);
+ }else {
+ qh MERGEexact= True;
+ qh_option("Qxact_merge", NULL, NULL);
+ }
+ }else if (qh MERGEexact)
+ qh MERGING= True;
+ if (!qh NOpremerge && qh JOGGLEmax > REALmax/2) {
+#ifdef qh_NOmerge
+ qh JOGGLEmax= 0.0;
+#endif
+ }
+ if (qh TRIangulate && qh JOGGLEmax < REALmax/2 && qh PRINTprecision)
+ qh_fprintf(qh ferr, 7038, "qhull warning: joggle('QJ') always produces simplicial output. Triangulated output('Qt') does nothing.\n");
+ if (qh JOGGLEmax < REALmax/2 && qh DELAUNAY && !qh SCALEinput && !qh SCALElast) {
+ qh SCALElast= True;
+ qh_option("Qbbound-last-qj", NULL, NULL);
+ }
+ if (qh MERGING && !qh POSTmerge && qh premerge_cos > REALmax/2
+ && qh premerge_centrum == 0) {
+ qh ZEROcentrum= True;
+ qh ZEROall_ok= True;
+ qh_option("_zero-centrum", NULL, NULL);
+ }
+ if (qh JOGGLEmax < REALmax/2 && REALepsilon > 2e-8 && qh PRINTprecision)
+ qh_fprintf(qh ferr, 7039, "qhull warning: real epsilon, %2.2g, is probably too large for joggle('QJn')\nRecompile with double precision reals(see user.h).\n",
+ REALepsilon);
+#ifdef qh_NOmerge
+ if (qh MERGING) {
+ qh_fprintf(qh ferr, 6045, "qhull input error: merging not installed(qh_NOmerge + 'Qx', 'Cn' or 'An')\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+#endif
+ if (qh DELAUNAY && qh KEEPcoplanar && !qh KEEPinside) {
+ qh KEEPinside= True;
+ qh_option("Qinterior-keep", NULL, NULL);
+ }
+ if (qh DELAUNAY && qh HALFspace) {
+ qh_fprintf(qh ferr, 6046, "qhull input error: can not use Delaunay('d') or Voronoi('v') with halfspace intersection('H')\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ if (!qh DELAUNAY && (qh UPPERdelaunay || qh ATinfinity)) {
+ qh_fprintf(qh ferr, 6047, "qhull input error: use upper-Delaunay('Qu') or infinity-point('Qz') with Delaunay('d') or Voronoi('v')\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ if (qh UPPERdelaunay && qh ATinfinity) {
+ qh_fprintf(qh ferr, 6048, "qhull input error: can not use infinity-point('Qz') with upper-Delaunay('Qu')\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ if (qh SCALElast && !qh DELAUNAY && qh PRINTprecision)
+ qh_fprintf(qh ferr, 7040, "qhull input warning: option 'Qbb' (scale-last-coordinate) is normally used with 'd' or 'v'\n");
+ qh DOcheckmax= (!qh SKIPcheckmax && qh MERGING );
+ qh KEEPnearinside= (qh DOcheckmax && !(qh KEEPinside && qh KEEPcoplanar)
+ && !qh NOnearinside);
+ if (qh MERGING)
+ qh CENTERtype= qh_AScentrum;
+ else if (qh VORONOI)
+ qh CENTERtype= qh_ASvoronoi;
+ if (qh TESTvneighbors && !qh MERGING) {
+ qh_fprintf(qh ferr, 6049, "qhull input error: test vertex neighbors('Qv') needs a merge option\n");
+ qh_errexit(qh_ERRinput, NULL ,NULL);
+ }
+ if (qh PROJECTinput || (qh DELAUNAY && qh PROJECTdelaunay)) {
+ qh hull_dim -= qh PROJECTinput;
+ if (qh DELAUNAY) {
+ qh hull_dim++;
+ if (qh ATinfinity)
+ extra= 1;
+ }
+ }
+ if (qh hull_dim <= 1) {
+ qh_fprintf(qh ferr, 6050, "qhull error: dimension %d must be > 1\n", qh hull_dim);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ for (k=2, factorial=1.0; k < qh hull_dim; k++)
+ factorial *= k;
+ qh AREAfactor= 1.0 / factorial;
+ trace2((qh ferr, 2005, "qh_initqhull_globals: initialize globals. dim %d numpoints %d malloc? %d projected %d to hull_dim %d\n",
+ dim, numpoints, ismalloc, qh PROJECTinput, qh hull_dim));
+ qh normal_size= qh hull_dim * sizeof(coordT);
+ qh center_size= qh normal_size - sizeof(coordT);
+ pointsneeded= qh hull_dim+1;
+ if (qh hull_dim > qh_DIMmergeVertex) {
+ qh MERGEvertices= False;
+ qh_option("Q3-no-merge-vertices-dim-high", NULL, NULL);
+ }
+ if (qh GOODpoint)
+ pointsneeded++;
+#ifdef qh_NOtrace
+ if (qh IStracing) {
+ qh_fprintf(qh ferr, 6051, "qhull input error: tracing is not installed(qh_NOtrace in user.h)");
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+#endif
+ if (qh RERUN > 1) {
+ qh TRACElastrun= qh IStracing; /* qh_build_withrestart duplicates next conditional */
+ if (qh IStracing != -1)
+ qh IStracing= 0;
+ }else if (qh TRACEpoint != -1 || qh TRACEdist < REALmax/2 || qh TRACEmerge) {
+ qh TRACElevel= (qh IStracing? qh IStracing : 3);
+ qh IStracing= 0;
+ }
+ if (qh ROTATErandom == 0 || qh ROTATErandom == -1) {
+ seed= (int)time(&timedata);
+ if (qh ROTATErandom == -1) {
+ seed= -seed;
+ qh_option("QRandom-seed", &seed, NULL );
+ }else
+ qh_option("QRotate-random", &seed, NULL);
+ qh ROTATErandom= seed;
+ }
+ seed= qh ROTATErandom;
+ if (seed == INT_MIN) /* default value */
+ seed= 1;
+ else if (seed < 0)
+ seed= -seed;
+ qh_RANDOMseed_(seed);
+ randr= 0.0;
+ for (i=1000; i--; ) {
+ randi= qh_RANDOMint;
+ randr += randi;
+ if (randi > qh_RANDOMmax) {
+ qh_fprintf(qh ferr, 8036, "\
+qhull configuration error (qh_RANDOMmax in user.h):\n\
+ random integer %d > qh_RANDOMmax(%.8g)\n",
+ randi, qh_RANDOMmax);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ }
+ qh_RANDOMseed_(seed);
+ randr = randr/1000;
+ if (randr < qh_RANDOMmax * 0.1
+ || randr > qh_RANDOMmax * 0.9)
+ qh_fprintf(qh ferr, 8037, "\
+qhull configuration warning (qh_RANDOMmax in user.h):\n\
+ average of 1000 random integers (%.2g) is much different than expected (%.2g).\n\
+ Is qh_RANDOMmax (%.2g) wrong?\n",
+ randr, qh_RANDOMmax * 0.5, qh_RANDOMmax);
+ qh RANDOMa= 2.0 * qh RANDOMfactor/qh_RANDOMmax;
+ qh RANDOMb= 1.0 - qh RANDOMfactor;
+ if (qh_HASHfactor < 1.1) {
+ qh_fprintf(qh ferr, 6052, "qhull internal error (qh_initqhull_globals): qh_HASHfactor %d must be at least 1.1. Qhull uses linear hash probing\n",
+ qh_HASHfactor);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ if (numpoints+extra < pointsneeded) {
+ qh_fprintf(qh ferr, 6214, "qhull input error: not enough points(%d) to construct initial simplex (need %d)\n",
+ numpoints, pointsneeded);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ qh_initqhull_outputflags();
+} /* initqhull_globals */
+
+/*---------------------------------
+
+ qh_initqhull_mem( )
+ initialize mem.c for qhull
+ qh.hull_dim and qh.normal_size determine some of the allocation sizes
+ if qh.MERGING,
+ includes ridgeT
+ calls qh_user_memsizes() to add up to 10 additional sizes for quick allocation
+ (see numsizes below)
+
+ returns:
+ mem.c already for qh_memalloc/qh_memfree (errors if called beforehand)
+
+ notes:
+ qh_produceoutput() prints memsizes
+
+*/
+void qh_initqhull_mem(void) {
+ int numsizes;
+ int i;
+
+ numsizes= 8+10;
+ qh_meminitbuffers(qh IStracing, qh_MEMalign, numsizes,
+ qh_MEMbufsize,qh_MEMinitbuf);
+ qh_memsize((int)sizeof(vertexT));
+ if (qh MERGING) {
+ qh_memsize((int)sizeof(ridgeT));
+ qh_memsize((int)sizeof(mergeT));
+ }
+ qh_memsize((int)sizeof(facetT));
+ i= sizeof(setT) + (qh hull_dim - 1) * SETelemsize; /* ridge.vertices */
+ qh_memsize(i);
+ qh_memsize(qh normal_size); /* normal */
+ i += SETelemsize; /* facet.vertices, .ridges, .neighbors */
+ qh_memsize(i);
+ qh_user_memsizes();
+ qh_memsetup();
+} /* initqhull_mem */
+
+/*---------------------------------
+
+ qh_initqhull_outputflags
+ initialize flags concerned with output
+
+ returns:
+ adjust user flags as needed
+
+ see:
+ qh_clear_outputflags() resets the flags
+
+ design:
+ test for qh.PRINTgood (i.e., only print 'good' facets)
+ check for conflicting print output options
+*/
+void qh_initqhull_outputflags(void) {
+ boolT printgeom= False, printmath= False, printcoplanar= False;
+ int i;
+
+ trace3((qh ferr, 3024, "qh_initqhull_outputflags: %s\n", qh qhull_command));
+ if (!(qh PRINTgood || qh PRINTneighbors)) {
+ if (qh KEEParea || qh KEEPminArea < REALmax/2 || qh KEEPmerge || qh DELAUNAY
+ || (!qh ONLYgood && (qh GOODvertex || qh GOODpoint))) {
+ qh PRINTgood= True;
+ qh_option("Pgood", NULL, NULL);
+ }
+ }
+ if (qh PRINTtransparent) {
+ if (qh hull_dim != 4 || !qh DELAUNAY || qh VORONOI || qh DROPdim >= 0) {
+ qh_fprintf(qh ferr, 6215, "qhull input error: transparent Delaunay('Gt') needs 3-d Delaunay('d') w/o 'GDn'\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ qh DROPdim = 3;
+ qh PRINTridges = True;
+ }
+ for (i=qh_PRINTEND; i--; ) {
+ if (qh PRINTout[i] == qh_PRINTgeom)
+ printgeom= True;
+ else if (qh PRINTout[i] == qh_PRINTmathematica || qh PRINTout[i] == qh_PRINTmaple)
+ printmath= True;
+ else if (qh PRINTout[i] == qh_PRINTcoplanars)
+ printcoplanar= True;
+ else if (qh PRINTout[i] == qh_PRINTpointnearest)
+ printcoplanar= True;
+ else if (qh PRINTout[i] == qh_PRINTpointintersect && !qh HALFspace) {
+ qh_fprintf(qh ferr, 6053, "qhull input error: option 'Fp' is only used for \nhalfspace intersection('Hn,n,n').\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }else if (qh PRINTout[i] == qh_PRINTtriangles && (qh HALFspace || qh VORONOI)) {
+ qh_fprintf(qh ferr, 6054, "qhull input error: option 'Ft' is not available for Voronoi vertices or halfspace intersection\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }else if (qh PRINTout[i] == qh_PRINTcentrums && qh VORONOI) {
+ qh_fprintf(qh ferr, 6055, "qhull input error: option 'FC' is not available for Voronoi vertices('v')\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }else if (qh PRINTout[i] == qh_PRINTvertices) {
+ if (qh VORONOI)
+ qh_option("Fvoronoi", NULL, NULL);
+ else
+ qh_option("Fvertices", NULL, NULL);
+ }
+ }
+ if (printcoplanar && qh DELAUNAY && qh JOGGLEmax < REALmax/2) {
+ if (qh PRINTprecision)
+ qh_fprintf(qh ferr, 7041, "qhull input warning: 'QJ' (joggle) will usually prevent coincident input sites for options 'Fc' and 'FP'\n");
+ }
+ if (printmath && (qh hull_dim > 3 || qh VORONOI)) {
+ qh_fprintf(qh ferr, 6056, "qhull input error: Mathematica and Maple output is only available for 2-d and 3-d convex hulls and 2-d Delaunay triangulations\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ if (printgeom) {
+ if (qh hull_dim > 4) {
+ qh_fprintf(qh ferr, 6057, "qhull input error: Geomview output is only available for 2-d, 3-d and 4-d\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ if (qh PRINTnoplanes && !(qh PRINTcoplanar + qh PRINTcentrums
+ + qh PRINTdots + qh PRINTspheres + qh DOintersections + qh PRINTridges)) {
+ qh_fprintf(qh ferr, 6058, "qhull input error: no output specified for Geomview\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ if (qh VORONOI && (qh hull_dim > 3 || qh DROPdim >= 0)) {
+ qh_fprintf(qh ferr, 6059, "qhull input error: Geomview output for Voronoi diagrams only for 2-d\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ /* can not warn about furthest-site Geomview output: no lower_threshold */
+ if (qh hull_dim == 4 && qh DROPdim == -1 &&
+ (qh PRINTcoplanar || qh PRINTspheres || qh PRINTcentrums)) {
+ qh_fprintf(qh ferr, 7042, "qhull input warning: coplanars, vertices, and centrums output not\n\
+available for 4-d output(ignored). Could use 'GDn' instead.\n");
+ qh PRINTcoplanar= qh PRINTspheres= qh PRINTcentrums= False;
+ }
+ }
+ if (!qh KEEPcoplanar && !qh KEEPinside && !qh ONLYgood) {
+ if ((qh PRINTcoplanar && qh PRINTspheres) || printcoplanar) {
+ if (qh QHULLfinished) {
+ qh_fprintf(qh ferr, 7072, "qhull output warning: ignoring coplanar points, option 'Qc' was not set for the first run of qhull.\n");
+ }else {
+ qh KEEPcoplanar = True;
+ qh_option("Qcoplanar", NULL, NULL);
+ }
+ }
+ }
+ qh PRINTdim= qh hull_dim;
+ if (qh DROPdim >=0) { /* after Geomview checks */
+ if (qh DROPdim < qh hull_dim) {
+ qh PRINTdim--;
+ if (!printgeom || qh hull_dim < 3)
+ qh_fprintf(qh ferr, 7043, "qhull input warning: drop dimension 'GD%d' is only available for 3-d/4-d Geomview\n", qh DROPdim);
+ }else
+ qh DROPdim= -1;
+ }else if (qh VORONOI) {
+ qh DROPdim= qh hull_dim-1;
+ qh PRINTdim= qh hull_dim-1;
+ }
+} /* qh_initqhull_outputflags */
+
+/*---------------------------------
+
+ qh_initqhull_start( infile, outfile, errfile )
+ allocate memory if needed and call qh_initqhull_start2()
+*/
+void qh_initqhull_start(FILE *infile, FILE *outfile, FILE *errfile) {
+
+#if qh_QHpointer
+ if (qh_qh) {
+ qh_fprintf(errfile, 6205, "qhull error (qh_initqhull_start): qh_qh already defined. Call qh_save_qhull() first\n");
+ qh_exit(qh_ERRqhull); /* no error handler */
+ }
+ if (!(qh_qh= (qhT *)qh_malloc(sizeof(qhT)))) {
+ qh_fprintf(errfile, 6060, "qhull error (qh_initqhull_start): insufficient memory\n");
+ qh_exit(qh_ERRmem); /* no error handler */
+ }
+#endif
+ qh_initstatistics();
+ qh_initqhull_start2(infile, outfile, errfile);
+} /* initqhull_start */
+
+/*---------------------------------
+
+ qh_initqhull_start2( infile, outfile, errfile )
+ start initialization of qhull
+ initialize statistics, stdio, default values for global variables
+ assumes qh_qh is defined
+ notes:
+ report errors elsewhere, error handling and g_qhull_output [Qhull.cpp, QhullQh()] not in initialized
+ see:
+ qh_maxmin() determines the precision constants
+ qh_freeqhull2()
+*/
+void qh_initqhull_start2(FILE *infile, FILE *outfile, FILE *errfile) {
+ time_t timedata;
+ int seed;
+
+ qh_CPUclock; /* start the clock(for qh_clock). One-shot. */
+#if qh_QHpointer
+ memset((char *)qh_qh, 0, sizeof(qhT)); /* every field is 0, FALSE, NULL */
+#else
+ memset((char *)&qh_qh, 0, sizeof(qhT));
+#endif
+ qh ANGLEmerge= True;
+ qh DROPdim= -1;
+ qh ferr= errfile;
+ qh fin= infile;
+ qh fout= outfile;
+ qh furthest_id= -1;
+ qh JOGGLEmax= REALmax;
+ qh KEEPminArea = REALmax;
+ qh last_low= REALmax;
+ qh last_high= REALmax;
+ qh last_newhigh= REALmax;
+ qh max_outside= 0.0;
+ qh max_vertex= 0.0;
+ qh MAXabs_coord= 0.0;
+ qh MAXsumcoord= 0.0;
+ qh MAXwidth= -REALmax;
+ qh MERGEindependent= True;
+ qh MINdenom_1= fmax_(1.0/REALmax, REALmin); /* used by qh_scalepoints */
+ qh MINoutside= 0.0;
+ qh MINvisible= REALmax;
+ qh MAXcoplanar= REALmax;
+ qh outside_err= REALmax;
+ qh premerge_centrum= 0.0;
+ qh premerge_cos= REALmax;
+ qh PRINTprecision= True;
+ qh PRINTradius= 0.0;
+ qh postmerge_cos= REALmax;
+ qh postmerge_centrum= 0.0;
+ qh ROTATErandom= INT_MIN;
+ qh MERGEvertices= True;
+ qh totarea= 0.0;
+ qh totvol= 0.0;
+ qh TRACEdist= REALmax;
+ qh TRACEpoint= -1; /* recompile or use 'TPn' */
+ qh tracefacet_id= UINT_MAX; /* recompile to trace a facet */
+ qh tracevertex_id= UINT_MAX; /* recompile to trace a vertex */
+ seed= (int)time(&timedata);
+ qh_RANDOMseed_(seed);
+ qh run_id= qh_RANDOMint+1; /* disallow 0 [UsingLibQhull::NOqhRunId] */
+ qh_option("run-id", &qh run_id, NULL);
+ strcat(qh qhull, "qhull");
+} /* initqhull_start2 */
+
+/*---------------------------------
+
+ qh_initthresholds( commandString )
+ set thresholds for printing and scaling from commandString
+
+ returns:
+ sets qh.GOODthreshold or qh.SPLITthreshold if 'Pd0D1' used
+
+ see:
+ qh_initflags(), 'Qbk' 'QBk' 'Pdk' and 'PDk'
+ qh_inthresholds()
+
+ design:
+ for each 'Pdn' or 'PDn' option
+ check syntax
+ set qh.lower_threshold or qh.upper_threshold
+ set qh.GOODthreshold if an unbounded threshold is used
+ set qh.SPLITthreshold if a bounded threshold is used
+*/
+void qh_initthresholds(char *command) {
+ realT value;
+ int idx, maxdim, k;
+ char *s= command; /* non-const due to strtol */
+ char key;
+
+ maxdim= qh input_dim;
+ if (qh DELAUNAY && (qh PROJECTdelaunay || qh PROJECTinput))
+ maxdim++;
+ while (*s) {
+ if (*s == '-')
+ s++;
+ if (*s == 'P') {
+ s++;
+ while (*s && !isspace(key= *s++)) {
+ if (key == 'd' || key == 'D') {
+ if (!isdigit(*s)) {
+ qh_fprintf(qh ferr, 7044, "qhull warning: no dimension given for Print option '%c' at: %s. Ignored\n",
+ key, s-1);
+ continue;
+ }
+ idx= qh_strtol(s, &s);
+ if (idx >= qh hull_dim) {
+ qh_fprintf(qh ferr, 7045, "qhull warning: dimension %d for Print option '%c' is >= %d. Ignored\n",
+ idx, key, qh hull_dim);
+ continue;
+ }
+ if (*s == ':') {
+ s++;
+ value= qh_strtod(s, &s);
+ if (fabs((double)value) > 1.0) {
+ qh_fprintf(qh ferr, 7046, "qhull warning: value %2.4g for Print option %c is > +1 or < -1. Ignored\n",
+ value, key);
+ continue;
+ }
+ }else
+ value= 0.0;
+ if (key == 'd')
+ qh lower_threshold[idx]= value;
+ else
+ qh upper_threshold[idx]= value;
+ }
+ }
+ }else if (*s == 'Q') {
+ s++;
+ while (*s && !isspace(key= *s++)) {
+ if (key == 'b' && *s == 'B') {
+ s++;
+ for (k=maxdim; k--; ) {
+ qh lower_bound[k]= -qh_DEFAULTbox;
+ qh upper_bound[k]= qh_DEFAULTbox;
+ }
+ }else if (key == 'b' && *s == 'b')
+ s++;
+ else if (key == 'b' || key == 'B') {
+ if (!isdigit(*s)) {
+ qh_fprintf(qh ferr, 7047, "qhull warning: no dimension given for Qhull option %c. Ignored\n",
+ key);
+ continue;
+ }
+ idx= qh_strtol(s, &s);
+ if (idx >= maxdim) {
+ qh_fprintf(qh ferr, 7048, "qhull warning: dimension %d for Qhull option %c is >= %d. Ignored\n",
+ idx, key, maxdim);
+ continue;
+ }
+ if (*s == ':') {
+ s++;
+ value= qh_strtod(s, &s);
+ }else if (key == 'b')
+ value= -qh_DEFAULTbox;
+ else
+ value= qh_DEFAULTbox;
+ if (key == 'b')
+ qh lower_bound[idx]= value;
+ else
+ qh upper_bound[idx]= value;
+ }
+ }
+ }else {
+ while (*s && !isspace(*s))
+ s++;
+ }
+ while (isspace(*s))
+ s++;
+ }
+ for (k=qh hull_dim; k--; ) {
+ if (qh lower_threshold[k] > -REALmax/2) {
+ qh GOODthreshold= True;
+ if (qh upper_threshold[k] < REALmax/2) {
+ qh SPLITthresholds= True;
+ qh GOODthreshold= False;
+ break;
+ }
+ }else if (qh upper_threshold[k] < REALmax/2)
+ qh GOODthreshold= True;
+ }
+} /* initthresholds */
+
+/*---------------------------------
+
+ qh_option( option, intVal, realVal )
+ add an option description to qh.qhull_options
+
+ notes:
+ NOerrors -- qh_option can not call qh_errexit() [qh_initqhull_start2]
+ will be printed with statistics ('Ts') and errors
+ strlen(option) < 40
+*/
+void qh_option(const char *option, int *i, realT *r) {
+ char buf[200];
+ int len, maxlen;
+
+ sprintf(buf, " %s", option);
+ if (i)
+ sprintf(buf+strlen(buf), " %d", *i);
+ if (r)
+ sprintf(buf+strlen(buf), " %2.2g", *r);
+ len= (int)strlen(buf); /* WARN64 */
+ qh qhull_optionlen += len;
+ maxlen= sizeof(qh qhull_options) - len -1;
+ maximize_(maxlen, 0);
+ if (qh qhull_optionlen >= qh_OPTIONline && maxlen > 0) {
+ qh qhull_optionlen= len;
+ strncat(qh qhull_options, "\n", (size_t)(maxlen--));
+ }
+ strncat(qh qhull_options, buf, (size_t)maxlen);
+} /* option */
+
+#if qh_QHpointer
+/*---------------------------------
+
+ qh_restore_qhull( oldqh )
+ restores a previously saved qhull
+ also restores qh_qhstat and qhmem.tempstack
+ Sets *oldqh to NULL
+ notes:
+ errors if current qhull hasn't been saved or freed
+ uses qhmem for error reporting
+
+ NOTE 1998/5/11:
+ Freeing memory after qh_save_qhull and qh_restore_qhull
+ is complicated. The procedures will be redesigned.
+
+ see:
+ qh_save_qhull(), UsingLibQhull
+*/
+void qh_restore_qhull(qhT **oldqh) {
+
+ if (*oldqh && strcmp((*oldqh)->qhull, "qhull")) {
+ qh_fprintf(qhmem.ferr, 6061, "qhull internal error (qh_restore_qhull): %p is not a qhull data structure\n",
+ *oldqh);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ if (qh_qh) {
+ qh_fprintf(qhmem.ferr, 6062, "qhull internal error (qh_restore_qhull): did not save or free existing qhull\n");
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ if (!*oldqh || !(*oldqh)->old_qhstat) {
+ qh_fprintf(qhmem.ferr, 6063, "qhull internal error (qh_restore_qhull): did not previously save qhull %p\n",
+ *oldqh);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ qh_qh= *oldqh;
+ *oldqh= NULL;
+ qh_qhstat= qh old_qhstat;
+ qhmem.tempstack= qh old_tempstack;
+ qh old_qhstat= 0;
+ qh old_tempstack= 0;
+ trace1((qh ferr, 1007, "qh_restore_qhull: restored qhull from %p\n", *oldqh));
+} /* restore_qhull */
+
+/*---------------------------------
+
+ qh_save_qhull( )
+ saves qhull for a later qh_restore_qhull
+ also saves qh_qhstat and qhmem.tempstack
+
+ returns:
+ qh_qh=NULL
+
+ notes:
+ need to initialize qhull or call qh_restore_qhull before continuing
+
+ NOTE 1998/5/11:
+ Freeing memory after qh_save_qhull and qh_restore_qhull
+ is complicated. The procedures will be redesigned.
+
+ see:
+ qh_restore_qhull()
+*/
+qhT *qh_save_qhull(void) {
+ qhT *oldqh;
+
+ trace1((qhmem.ferr, 1045, "qh_save_qhull: save qhull %p\n", qh_qh));
+ if (!qh_qh) {
+ qh_fprintf(qhmem.ferr, 6064, "qhull internal error (qh_save_qhull): qhull not initialized\n");
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ qh old_qhstat= qh_qhstat;
+ qh_qhstat= NULL;
+ qh old_tempstack= qhmem.tempstack;
+ qhmem.tempstack= NULL;
+ oldqh= qh_qh;
+ qh_qh= NULL;
+ return oldqh;
+} /* save_qhull */
+
+#endif
+
diff --git a/src/libqhullr/io_r.c b/src/libqhullr/io_r.c
new file mode 100644
index 0000000..c552585
--- /dev/null
+++ b/src/libqhullr/io_r.c
@@ -0,0 +1,4060 @@
+/*
---------------------------------
+
+ io.c
+ Input/Output routines of qhull application
+
+ see qh-io.htm and io.h
+
+ see user.c for qh_errprint and qh_printfacetlist
+
+ unix.c calls qh_readpoints and qh_produce_output
+
+ unix.c and user.c are the only callers of io.c functions
+ This allows the user to avoid loading io.o from qhull.a
+
+ Copyright (c) 1993-2012 The Geometry Center.
+ $Id: //main/2011/qhull/src/libqhullr/io_r.c#1 $$Change: 1640 $
+ $DateTime: 2014/01/15 09:12:08 $$Author: bbarber $
+*/
+
+#include "qhull_a.h"
+
+/*========= -functions in alphabetical order after qh_produce_output() =====*/
+
+/*---------------------------------
+
+ qh_produce_output()
+ qh_produce_output2()
+ prints out the result of qhull in desired format
+ qh_produce_output2() does not call qh_prepare_output()
+ if qh.GETarea
+ computes and prints area and volume
+ qh.PRINTout[] is an array of output formats
+
+ notes:
+ prints output in qh.PRINTout order
+*/
+void qh_produce_output(void) {
+ int tempsize= qh_setsize(qhmem.tempstack);
+
+ qh_prepare_output();
+ qh_produce_output2();
+ if (qh_setsize(qhmem.tempstack) != tempsize) {
+ qh_fprintf(qh ferr, 6206, "qhull internal error (qh_produce_output): temporary sets not empty(%d)\n",
+ qh_setsize(qhmem.tempstack));
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+} /* produce_output */
+
+
+void qh_produce_output2(void) {
+ int i, tempsize= qh_setsize(qhmem.tempstack), d_1;
+
+ if (qh PRINTsummary)
+ qh_printsummary(qh ferr);
+ else if (qh PRINTout[0] == qh_PRINTnone)
+ qh_printsummary(qh fout);
+ for (i=0; i < qh_PRINTEND; i++)
+ qh_printfacets(qh fout, qh PRINTout[i], qh facet_list, NULL, !qh_ALL);
+ qh_allstatistics();
+ if (qh PRINTprecision && !qh MERGING && (qh JOGGLEmax > REALmax/2 || qh RERUN))
+ qh_printstats(qh ferr, qhstat precision, NULL);
+ if (qh VERIFYoutput && (zzval_(Zridge) > 0 || zzval_(Zridgemid) > 0))
+ qh_printstats(qh ferr, qhstat vridges, NULL);
+ if (qh PRINTstatistics) {
+ qh_printstatistics(qh ferr, "");
+ qh_memstatistics(qh ferr);
+ d_1= sizeof(setT) + (qh hull_dim - 1) * SETelemsize;
+ qh_fprintf(qh ferr, 8040, "\
+ size in bytes: merge %d ridge %d vertex %d facet %d\n\
+ normal %d ridge vertices %d facet vertices or neighbors %d\n",
+ (int)sizeof(mergeT), (int)sizeof(ridgeT),
+ (int)sizeof(vertexT), (int)sizeof(facetT),
+ qh normal_size, d_1, d_1 + SETelemsize);
+ }
+ if (qh_setsize(qhmem.tempstack) != tempsize) {
+ qh_fprintf(qh ferr, 6065, "qhull internal error (qh_produce_output2): temporary sets not empty(%d)\n",
+ qh_setsize(qhmem.tempstack));
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+} /* produce_output2 */
+
+/*---------------------------------
+
+ dfacet( id )
+ print facet by id, for debugging
+
+*/
+void dfacet(unsigned id) {
+ facetT *facet;
+
+ FORALLfacets {
+ if (facet->id == id) {
+ qh_printfacet(qh fout, facet);
+ break;
+ }
+ }
+} /* dfacet */
+
+
+/*---------------------------------
+
+ dvertex( id )
+ print vertex by id, for debugging
+*/
+void dvertex(unsigned id) {
+ vertexT *vertex;
+
+ FORALLvertices {
+ if (vertex->id == id) {
+ qh_printvertex(qh fout, vertex);
+ break;
+ }
+ }
+} /* dvertex */
+
+
+/*---------------------------------
+
+ qh_compare_vertexpoint( p1, p2 )
+ used by qsort() to order vertices by point id
+*/
+int qh_compare_vertexpoint(const void *p1, const void *p2) {
+ const vertexT *a= *((vertexT *const*)p1), *b= *((vertexT *const*)p2);
+
+ return((qh_pointid(a->point) > qh_pointid(b->point)?1:-1));
+} /* compare_vertexpoint */
+
+/*---------------------------------
+
+ qh_compare_facetarea( p1, p2 )
+ used by qsort() to order facets by area
+*/
+int qh_compare_facetarea(const void *p1, const void *p2) {
+ const facetT *a= *((facetT *const*)p1), *b= *((facetT *const*)p2);
+
+ if (!a->isarea)
+ return -1;
+ if (!b->isarea)
+ return 1;
+ if (a->f.area > b->f.area)
+ return 1;
+ else if (a->f.area == b->f.area)
+ return 0;
+ return -1;
+} /* compare_facetarea */
+
+/*---------------------------------
+
+ qh_compare_facetmerge( p1, p2 )
+ used by qsort() to order facets by number of merges
+*/
+int qh_compare_facetmerge(const void *p1, const void *p2) {
+ const facetT *a= *((facetT *const*)p1), *b= *((facetT *const*)p2);
+
+ return(a->nummerge - b->nummerge);
+} /* compare_facetvisit */
+
+/*---------------------------------
+
+ qh_compare_facetvisit( p1, p2 )
+ used by qsort() to order facets by visit id or id
+*/
+int qh_compare_facetvisit(const void *p1, const void *p2) {
+ const facetT *a= *((facetT *const*)p1), *b= *((facetT *const*)p2);
+ int i,j;
+
+ if (!(i= a->visitid))
+ i= 0 - a->id; /* do not convert to int, sign distinguishes id from visitid */
+ if (!(j= b->visitid))
+ j= 0 - b->id;
+ return(i - j);
+} /* compare_facetvisit */
+
+/*---------------------------------
+
+ qh_copyfilename( dest, size, source, length )
+ copy filename identified by qh_skipfilename()
+
+ notes:
+ see qh_skipfilename() for syntax
+*/
+void qh_copyfilename(char *filename, int size, const char* source, int length) {
+ char c= *source;
+
+ if (length > size + 1) {
+ qh_fprintf(qh ferr, 6040, "qhull error: filename is more than %d characters, %s\n", size-1, source);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ strncpy(filename, source, length);
+ filename[length]= '\0';
+ if (c == '\'' || c == '"') {
+ char *s= filename + 1;
+ char *t= filename;
+ while (*s) {
+ if (*s == c) {
+ if (s[-1] == '\\')
+ t[-1]= c;
+ }else
+ *t++= *s;
+ s++;
+ }
+ *t= '\0';
+ }
+} /* copyfilename */
+
+/*---------------------------------
+
+ qh_countfacets( facetlist, facets, printall,
+ numfacets, numsimplicial, totneighbors, numridges, numcoplanar, numtricoplanars )
+ count good facets for printing and set visitid
+ if allfacets, ignores qh_skipfacet()
+
+ notes:
+ qh_printsummary and qh_countfacets must match counts
+
+ returns:
+ numfacets, numsimplicial, total neighbors, numridges, coplanars
+ each facet with ->visitid indicating 1-relative position
+ ->visitid==0 indicates not good
+
+ notes
+ numfacets >= numsimplicial
+ if qh.NEWfacets,
+ does not count visible facets (matches qh_printafacet)
+
+ design:
+ for all facets on facetlist and in facets set
+ unless facet is skipped or visible (i.e., will be deleted)
+ mark facet->visitid
+ update counts
+*/
+void qh_countfacets(facetT *facetlist, setT *facets, boolT printall,
+ int *numfacetsp, int *numsimplicialp, int *totneighborsp, int *numridgesp, int *numcoplanarsp, int *numtricoplanarsp) {
+ facetT *facet, **facetp;
+ int numfacets= 0, numsimplicial= 0, numridges= 0, totneighbors= 0, numcoplanars= 0, numtricoplanars= 0;
+
+ FORALLfacet_(facetlist) {
+ if ((facet->visible && qh NEWfacets)
+ || (!printall && qh_skipfacet(facet)))
+ facet->visitid= 0;
+ else {
+ facet->visitid= ++numfacets;
+ totneighbors += qh_setsize(facet->neighbors);
+ if (facet->simplicial) {
+ numsimplicial++;
+ if (facet->keepcentrum && facet->tricoplanar)
+ numtricoplanars++;
+ }else
+ numridges += qh_setsize(facet->ridges);
+ if (facet->coplanarset)
+ numcoplanars += qh_setsize(facet->coplanarset);
+ }
+ }
+
+ FOREACHfacet_(facets) {
+ if ((facet->visible && qh NEWfacets)
+ || (!printall && qh_skipfacet(facet)))
+ facet->visitid= 0;
+ else {
+ facet->visitid= ++numfacets;
+ totneighbors += qh_setsize(facet->neighbors);
+ if (facet->simplicial){
+ numsimplicial++;
+ if (facet->keepcentrum && facet->tricoplanar)
+ numtricoplanars++;
+ }else
+ numridges += qh_setsize(facet->ridges);
+ if (facet->coplanarset)
+ numcoplanars += qh_setsize(facet->coplanarset);
+ }
+ }
+ qh visit_id += numfacets+1;
+ *numfacetsp= numfacets;
+ *numsimplicialp= numsimplicial;
+ *totneighborsp= totneighbors;
+ *numridgesp= numridges;
+ *numcoplanarsp= numcoplanars;
+ *numtricoplanarsp= numtricoplanars;
+} /* countfacets */
+
+/*---------------------------------
+
+ qh_detvnorm( vertex, vertexA, centers, offset )
+ compute separating plane of the Voronoi diagram for a pair of input sites
+ centers= set of facets (i.e., Voronoi vertices)
+ facet->visitid= 0 iff vertex-at-infinity (i.e., unbounded)
+
+ assumes:
+ qh_ASvoronoi and qh_vertexneighbors() already set
+
+ returns:
+ norm
+ a pointer into qh.gm_matrix to qh.hull_dim-1 reals
+ copy the data before reusing qh.gm_matrix
+ offset
+ if 'QVn'
+ sign adjusted so that qh.GOODvertexp is inside
+ else
+ sign adjusted so that vertex is inside
+
+ qh.gm_matrix= simplex of points from centers relative to first center
+
+ notes:
+ in io.c so that code for 'v Tv' can be removed by removing io.c
+ returns pointer into qh.gm_matrix to avoid tracking of temporary memory
+
+ design:
+ determine midpoint of input sites
+ build points as the set of Voronoi vertices
+ select a simplex from points (if necessary)
+ include midpoint if the Voronoi region is unbounded
+ relocate the first vertex of the simplex to the origin
+ compute the normalized hyperplane through the simplex
+ orient the hyperplane toward 'QVn' or 'vertex'
+ if 'Tv' or 'Ts'
+ if bounded
+ test that hyperplane is the perpendicular bisector of the input sites
+ test that Voronoi vertices not in the simplex are still on the hyperplane
+ free up temporary memory
+*/
+pointT *qh_detvnorm(vertexT *vertex, vertexT *vertexA, setT *centers, realT *offsetp) {
+ facetT *facet, **facetp;
+ int i, k, pointid, pointidA, point_i, point_n;
+ setT *simplex= NULL;
+ pointT *point, **pointp, *point0, *midpoint, *normal, *inpoint;
+ coordT *coord, *gmcoord, *normalp;
+ setT *points= qh_settemp(qh TEMPsize);
+ boolT nearzero= False;
+ boolT unbounded= False;
+ int numcenters= 0;
+ int dim= qh hull_dim - 1;
+ realT dist, offset, angle, zero= 0.0;
+
+ midpoint= qh gm_matrix + qh hull_dim * qh hull_dim; /* last row */
+ for (k=0; k < dim; k++)
+ midpoint[k]= (vertex->point[k] + vertexA->point[k])/2;
+ FOREACHfacet_(centers) {
+ numcenters++;
+ if (!facet->visitid)
+ unbounded= True;
+ else {
+ if (!facet->center)
+ facet->center= qh_facetcenter(facet->vertices);
+ qh_setappend(&points, facet->center);
+ }
+ }
+ if (numcenters > dim) {
+ simplex= qh_settemp(qh TEMPsize);
+ qh_setappend(&simplex, vertex->point);
+ if (unbounded)
+ qh_setappend(&simplex, midpoint);
+ qh_maxsimplex(dim, points, NULL, 0, &simplex);
+ qh_setdelnth(simplex, 0);
+ }else if (numcenters == dim) {
+ if (unbounded)
+ qh_setappend(&points, midpoint);
+ simplex= points;
+ }else {
+ qh_fprintf(qh ferr, 6216, "qhull internal error (qh_detvnorm): too few points(%d) to compute separating plane\n", numcenters);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ i= 0;
+ gmcoord= qh gm_matrix;
+ point0= SETfirstt_(simplex, pointT);
+ FOREACHpoint_(simplex) {
+ if (qh IStracing >= 4)
+ qh_printmatrix(qh ferr, "qh_detvnorm: Voronoi vertex or midpoint",
+ &point, 1, dim);
+ if (point != point0) {
+ qh gm_row[i++]= gmcoord;
+ coord= point0;
+ for (k=dim; k--; )
+ *(gmcoord++)= *point++ - *coord++;
+ }
+ }
+ qh gm_row[i]= gmcoord; /* does not overlap midpoint, may be used later for qh_areasimplex */
+ normal= gmcoord;
+ qh_sethyperplane_gauss(dim, qh gm_row, point0, True,
+ normal, &offset, &nearzero);
+ if (qh GOODvertexp == vertexA->point)
+ inpoint= vertexA->point;
+ else
+ inpoint= vertex->point;
+ zinc_(Zdistio);
+ dist= qh_distnorm(dim, inpoint, normal, &offset);
+ if (dist > 0) {
+ offset= -offset;
+ normalp= normal;
+ for (k=dim; k--; ) {
+ *normalp= -(*normalp);
+ normalp++;
+ }
+ }
+ if (qh VERIFYoutput || qh PRINTstatistics) {
+ pointid= qh_pointid(vertex->point);
+ pointidA= qh_pointid(vertexA->point);
+ if (!unbounded) {
+ zinc_(Zdiststat);
+ dist= qh_distnorm(dim, midpoint, normal, &offset);
+ if (dist < 0)
+ dist= -dist;
+ zzinc_(Zridgemid);
+ wwmax_(Wridgemidmax, dist);
+ wwadd_(Wridgemid, dist);
+ trace4((qh ferr, 4014, "qh_detvnorm: points %d %d midpoint dist %2.2g\n",
+ pointid, pointidA, dist));
+ for (k=0; k < dim; k++)
+ midpoint[k]= vertexA->point[k] - vertex->point[k]; /* overwrites midpoint! */
+ qh_normalize(midpoint, dim, False);
+ angle= qh_distnorm(dim, midpoint, normal, &zero); /* qh_detangle uses dim+1 */
+ if (angle < 0.0)
+ angle= angle + 1.0;
+ else
+ angle= angle - 1.0;
+ if (angle < 0.0)
+ angle -= angle;
+ trace4((qh ferr, 4015, "qh_detvnorm: points %d %d angle %2.2g nearzero %d\n",
+ pointid, pointidA, angle, nearzero));
+ if (nearzero) {
+ zzinc_(Zridge0);
+ wwmax_(Wridge0max, angle);
+ wwadd_(Wridge0, angle);
+ }else {
+ zzinc_(Zridgeok)
+ wwmax_(Wridgeokmax, angle);
+ wwadd_(Wridgeok, angle);
+ }
+ }
+ if (simplex != points) {
+ FOREACHpoint_i_(points) {
+ if (!qh_setin(simplex, point)) {
+ facet= SETelemt_(centers, point_i, facetT);
+ zinc_(Zdiststat);
+ dist= qh_distnorm(dim, point, normal, &offset);
+ if (dist < 0)
+ dist= -dist;
+ zzinc_(Zridge);
+ wwmax_(Wridgemax, dist);
+ wwadd_(Wridge, dist);
+ trace4((qh ferr, 4016, "qh_detvnorm: points %d %d Voronoi vertex %d dist %2.2g\n",
+ pointid, pointidA, facet->visitid, dist));
+ }
+ }
+ }
+ }
+ *offsetp= offset;
+ if (simplex != points)
+ qh_settempfree(&simplex);
+ qh_settempfree(&points);
+ return normal;
+} /* detvnorm */
+
+/*---------------------------------
+
+ qh_detvridge( vertexA )
+ determine Voronoi ridge from 'seen' neighbors of vertexA
+ include one vertex-at-infinite if an !neighbor->visitid
+
+ returns:
+ temporary set of centers (facets, i.e., Voronoi vertices)
+ sorted by center id
+*/
+setT *qh_detvridge(vertexT *vertex) {
+ setT *centers= qh_settemp(qh TEMPsize);
+ setT *tricenters= qh_settemp(qh TEMPsize);
+ facetT *neighbor, **neighborp;
+ boolT firstinf= True;
+
+ FOREACHneighbor_(vertex) {
+ if (neighbor->seen) {
+ if (neighbor->visitid) {
+ if (!neighbor->tricoplanar || qh_setunique(&tricenters, neighbor->center))
+ qh_setappend(¢ers, neighbor);
+ }else if (firstinf) {
+ firstinf= False;
+ qh_setappend(¢ers, neighbor);
+ }
+ }
+ }
+ qsort(SETaddr_(centers, facetT), (size_t)qh_setsize(centers),
+ sizeof(facetT *), qh_compare_facetvisit);
+ qh_settempfree(&tricenters);
+ return centers;
+} /* detvridge */
+
+/*---------------------------------
+
+ qh_detvridge3( atvertex, vertex )
+ determine 3-d Voronoi ridge from 'seen' neighbors of atvertex and vertex
+ include one vertex-at-infinite for !neighbor->visitid
+ assumes all facet->seen2= True
+
+ returns:
+ temporary set of centers (facets, i.e., Voronoi vertices)
+ listed in adjacency order (!oriented)
+ all facet->seen2= True
+
+ design:
+ mark all neighbors of atvertex
+ for each adjacent neighbor of both atvertex and vertex
+ if neighbor selected
+ add neighbor to set of Voronoi vertices
+*/
+setT *qh_detvridge3 (vertexT *atvertex, vertexT *vertex) {
+ setT *centers= qh_settemp(qh TEMPsize);
+ setT *tricenters= qh_settemp(qh TEMPsize);
+ facetT *neighbor, **neighborp, *facet= NULL;
+ boolT firstinf= True;
+
+ FOREACHneighbor_(atvertex)
+ neighbor->seen2= False;
+ FOREACHneighbor_(vertex) {
+ if (!neighbor->seen2) {
+ facet= neighbor;
+ break;
+ }
+ }
+ while (facet) {
+ facet->seen2= True;
+ if (neighbor->seen) {
+ if (facet->visitid) {
+ if (!facet->tricoplanar || qh_setunique(&tricenters, facet->center))
+ qh_setappend(¢ers, facet);
+ }else if (firstinf) {
+ firstinf= False;
+ qh_setappend(¢ers, facet);
+ }
+ }
+ FOREACHneighbor_(facet) {
+ if (!neighbor->seen2) {
+ if (qh_setin(vertex->neighbors, neighbor))
+ break;
+ else
+ neighbor->seen2= True;
+ }
+ }
+ facet= neighbor;
+ }
+ if (qh CHECKfrequently) {
+ FOREACHneighbor_(vertex) {
+ if (!neighbor->seen2) {
+ qh_fprintf(qh ferr, 6217, "qhull internal error (qh_detvridge3): neighbors of vertex p%d are not connected at facet %d\n",
+ qh_pointid(vertex->point), neighbor->id);
+ qh_errexit(qh_ERRqhull, neighbor, NULL);
+ }
+ }
+ }
+ FOREACHneighbor_(atvertex)
+ neighbor->seen2= True;
+ qh_settempfree(&tricenters);
+ return centers;
+} /* detvridge3 */
+
+/*---------------------------------
+
+ qh_eachvoronoi( fp, printvridge, vertex, visitall, innerouter, inorder )
+ if visitall,
+ visit all Voronoi ridges for vertex (i.e., an input site)
+ else
+ visit all unvisited Voronoi ridges for vertex
+ all vertex->seen= False if unvisited
+ assumes
+ all facet->seen= False
+ all facet->seen2= True (for qh_detvridge3)
+ all facet->visitid == 0 if vertex_at_infinity
+ == index of Voronoi vertex
+ >= qh.num_facets if ignored
+ innerouter:
+ qh_RIDGEall-- both inner (bounded) and outer(unbounded) ridges
+ qh_RIDGEinner- only inner
+ qh_RIDGEouter- only outer
+
+ if inorder
+ orders vertices for 3-d Voronoi diagrams
+
+ returns:
+ number of visited ridges (does not include previously visited ridges)
+
+ if printvridge,
+ calls printvridge( fp, vertex, vertexA, centers)
+ fp== any pointer (assumes FILE*)
+ vertex,vertexA= pair of input sites that define a Voronoi ridge
+ centers= set of facets (i.e., Voronoi vertices)
+ ->visitid == index or 0 if vertex_at_infinity
+ ordered for 3-d Voronoi diagram
+ notes:
+ uses qh.vertex_visit
+
+ see:
+ qh_eachvoronoi_all()
+
+ design:
+ mark selected neighbors of atvertex
+ for each selected neighbor (either Voronoi vertex or vertex-at-infinity)
+ for each unvisited vertex
+ if atvertex and vertex share more than d-1 neighbors
+ bump totalcount
+ if printvridge defined
+ build the set of shared neighbors (i.e., Voronoi vertices)
+ call printvridge
+*/
+int qh_eachvoronoi(FILE *fp, printvridgeT printvridge, vertexT *atvertex, boolT visitall, qh_RIDGE innerouter, boolT inorder) {
+ boolT unbounded;
+ int count;
+ facetT *neighbor, **neighborp, *neighborA, **neighborAp;
+ setT *centers;
+ setT *tricenters= qh_settemp(qh TEMPsize);
+
+ vertexT *vertex, **vertexp;
+ boolT firstinf;
+ unsigned int numfacets= (unsigned int)qh num_facets;
+ int totridges= 0;
+
+ qh vertex_visit++;
+ atvertex->seen= True;
+ if (visitall) {
+ FORALLvertices
+ vertex->seen= False;
+ }
+ FOREACHneighbor_(atvertex) {
+ if (neighbor->visitid < numfacets)
+ neighbor->seen= True;
+ }
+ FOREACHneighbor_(atvertex) {
+ if (neighbor->seen) {
+ FOREACHvertex_(neighbor->vertices) {
+ if (vertex->visitid != qh vertex_visit && !vertex->seen) {
+ vertex->visitid= qh vertex_visit;
+ count= 0;
+ firstinf= True;
+ qh_settruncate(tricenters, 0);
+ FOREACHneighborA_(vertex) {
+ if (neighborA->seen) {
+ if (neighborA->visitid) {
+ if (!neighborA->tricoplanar || qh_setunique(&tricenters, neighborA->center))
+ count++;
+ }else if (firstinf) {
+ count++;
+ firstinf= False;
+ }
+ }
+ }
+ if (count >= qh hull_dim - 1) { /* e.g., 3 for 3-d Voronoi */
+ if (firstinf) {
+ if (innerouter == qh_RIDGEouter)
+ continue;
+ unbounded= False;
+ }else {
+ if (innerouter == qh_RIDGEinner)
+ continue;
+ unbounded= True;
+ }
+ totridges++;
+ trace4((qh ferr, 4017, "qh_eachvoronoi: Voronoi ridge of %d vertices between sites %d and %d\n",
+ count, qh_pointid(atvertex->point), qh_pointid(vertex->point)));
+ if (printvridge && fp) {
+ if (inorder && qh hull_dim == 3+1) /* 3-d Voronoi diagram */
+ centers= qh_detvridge3 (atvertex, vertex);
+ else
+ centers= qh_detvridge(vertex);
+ (*printvridge) (fp, atvertex, vertex, centers, unbounded);
+ qh_settempfree(¢ers);
+ }
+ }
+ }
+ }
+ }
+ }
+ FOREACHneighbor_(atvertex)
+ neighbor->seen= False;
+ qh_settempfree(&tricenters);
+ return totridges;
+} /* eachvoronoi */
+
+
+/*---------------------------------
+
+ qh_eachvoronoi_all( fp, printvridge, isUpper, innerouter, inorder )
+ visit all Voronoi ridges
+
+ innerouter:
+ see qh_eachvoronoi()
+
+ if inorder
+ orders vertices for 3-d Voronoi diagrams
+
+ returns
+ total number of ridges
+
+ if isUpper == facet->upperdelaunay (i.e., a Vornoi vertex)
+ facet->visitid= Voronoi vertex index(same as 'o' format)
+ else
+ facet->visitid= 0
+
+ if printvridge,
+ calls printvridge( fp, vertex, vertexA, centers)
+ [see qh_eachvoronoi]
+
+ notes:
+ Not used for qhull.exe
+ same effect as qh_printvdiagram but ridges not sorted by point id
+*/
+int qh_eachvoronoi_all(FILE *fp, printvridgeT printvridge, boolT isUpper, qh_RIDGE innerouter, boolT inorder) {
+ facetT *facet;
+ vertexT *vertex;
+ int numcenters= 1; /* vertex 0 is vertex-at-infinity */
+ int totridges= 0;
+
+ qh_clearcenters(qh_ASvoronoi);
+ qh_vertexneighbors();
+ maximize_(qh visit_id, (unsigned) qh num_facets);
+ FORALLfacets {
+ facet->visitid= 0;
+ facet->seen= False;
+ facet->seen2= True;
+ }
+ FORALLfacets {
+ if (facet->upperdelaunay == isUpper)
+ facet->visitid= numcenters++;
+ }
+ FORALLvertices
+ vertex->seen= False;
+ FORALLvertices {
+ if (qh GOODvertex > 0 && qh_pointid(vertex->point)+1 != qh GOODvertex)
+ continue;
+ totridges += qh_eachvoronoi(fp, printvridge, vertex,
+ !qh_ALL, innerouter, inorder);
+ }
+ return totridges;
+} /* eachvoronoi_all */
+
+/*---------------------------------
+
+ qh_facet2point( facet, point0, point1, mindist )
+ return two projected temporary vertices for a 2-d facet
+ may be non-simplicial
+
+ returns:
+ point0 and point1 oriented and projected to the facet
+ returns mindist (maximum distance below plane)
+*/
+void qh_facet2point(facetT *facet, pointT **point0, pointT **point1, realT *mindist) {
+ vertexT *vertex0, *vertex1;
+ realT dist;
+
+ if (facet->toporient ^ qh_ORIENTclock) {
+ vertex0= SETfirstt_(facet->vertices, vertexT);
+ vertex1= SETsecondt_(facet->vertices, vertexT);
+ }else {
+ vertex1= SETfirstt_(facet->vertices, vertexT);
+ vertex0= SETsecondt_(facet->vertices, vertexT);
+ }
+ zadd_(Zdistio, 2);
+ qh_distplane(vertex0->point, facet, &dist);
+ *mindist= dist;
+ *point0= qh_projectpoint(vertex0->point, facet, dist);
+ qh_distplane(vertex1->point, facet, &dist);
+ minimize_(*mindist, dist);
+ *point1= qh_projectpoint(vertex1->point, facet, dist);
+} /* facet2point */
+
+
+/*---------------------------------
+
+ qh_facetvertices( facetlist, facets, allfacets )
+ returns temporary set of vertices in a set and/or list of facets
+ if allfacets, ignores qh_skipfacet()
+
+ returns:
+ vertices with qh.vertex_visit
+
+ notes:
+ optimized for allfacets of facet_list
+
+ design:
+ if allfacets of facet_list
+ create vertex set from vertex_list
+ else
+ for each selected facet in facets or facetlist
+ append unvisited vertices to vertex set
+*/
+setT *qh_facetvertices(facetT *facetlist, setT *facets, boolT allfacets) {
+ setT *vertices;
+ facetT *facet, **facetp;
+ vertexT *vertex, **vertexp;
+
+ qh vertex_visit++;
+ if (facetlist == qh facet_list && allfacets && !facets) {
+ vertices= qh_settemp(qh num_vertices);
+ FORALLvertices {
+ vertex->visitid= qh vertex_visit;
+ qh_setappend(&vertices, vertex);
+ }
+ }else {
+ vertices= qh_settemp(qh TEMPsize);
+ FORALLfacet_(facetlist) {
+ if (!allfacets && qh_skipfacet(facet))
+ continue;
+ FOREACHvertex_(facet->vertices) {
+ if (vertex->visitid != qh vertex_visit) {
+ vertex->visitid= qh vertex_visit;
+ qh_setappend(&vertices, vertex);
+ }
+ }
+ }
+ }
+ FOREACHfacet_(facets) {
+ if (!allfacets && qh_skipfacet(facet))
+ continue;
+ FOREACHvertex_(facet->vertices) {
+ if (vertex->visitid != qh vertex_visit) {
+ vertex->visitid= qh vertex_visit;
+ qh_setappend(&vertices, vertex);
+ }
+ }
+ }
+ return vertices;
+} /* facetvertices */
+
+/*---------------------------------
+
+ qh_geomplanes( facet, outerplane, innerplane )
+ return outer and inner planes for Geomview
+ qh.PRINTradius is size of vertices and points (includes qh.JOGGLEmax)
+
+ notes:
+ assume precise calculations in io.c with roundoff covered by qh_GEOMepsilon
+*/
+void qh_geomplanes(facetT *facet, realT *outerplane, realT *innerplane) {
+ realT radius;
+
+ if (qh MERGING || qh JOGGLEmax < REALmax/2) {
+ qh_outerinner(facet, outerplane, innerplane);
+ radius= qh PRINTradius;
+ if (qh JOGGLEmax < REALmax/2)
+ radius -= qh JOGGLEmax * sqrt((realT)qh hull_dim); /* already accounted for in qh_outerinner() */
+ *outerplane += radius;
+ *innerplane -= radius;
+ if (qh PRINTcoplanar || qh PRINTspheres) {
+ *outerplane += qh MAXabs_coord * qh_GEOMepsilon;
+ *innerplane -= qh MAXabs_coord * qh_GEOMepsilon;
+ }
+ }else
+ *innerplane= *outerplane= 0;
+} /* geomplanes */
+
+
+/*---------------------------------
+
+ qh_markkeep( facetlist )
+ mark good facets that meet qh.KEEParea, qh.KEEPmerge, and qh.KEEPminArea
+ ignores visible facets (!part of convex hull)
+
+ returns:
+ may clear facet->good
+ recomputes qh.num_good
+
+ design:
+ get set of good facets
+ if qh.KEEParea
+ sort facets by area
+ clear facet->good for all but n largest facets
+ if qh.KEEPmerge
+ sort facets by merge count
+ clear facet->good for all but n most merged facets
+ if qh.KEEPminarea
+ clear facet->good if area too small
+ update qh.num_good
+*/
+void qh_markkeep(facetT *facetlist) {
+ facetT *facet, **facetp;
+ setT *facets= qh_settemp(qh num_facets);
+ int size, count;
+
+ trace2((qh ferr, 2006, "qh_markkeep: only keep %d largest and/or %d most merged facets and/or min area %.2g\n",
+ qh KEEParea, qh KEEPmerge, qh KEEPminArea));
+ FORALLfacet_(facetlist) {
+ if (!facet->visible && facet->good)
+ qh_setappend(&facets, facet);
+ }
+ size= qh_setsize(facets);
+ if (qh KEEParea) {
+ qsort(SETaddr_(facets, facetT), (size_t)size,
+ sizeof(facetT *), qh_compare_facetarea);
+ if ((count= size - qh KEEParea) > 0) {
+ FOREACHfacet_(facets) {
+ facet->good= False;
+ if (--count == 0)
+ break;
+ }
+ }
+ }
+ if (qh KEEPmerge) {
+ qsort(SETaddr_(facets, facetT), (size_t)size,
+ sizeof(facetT *), qh_compare_facetmerge);
+ if ((count= size - qh KEEPmerge) > 0) {
+ FOREACHfacet_(facets) {
+ facet->good= False;
+ if (--count == 0)
+ break;
+ }
+ }
+ }
+ if (qh KEEPminArea < REALmax/2) {
+ FOREACHfacet_(facets) {
+ if (!facet->isarea || facet->f.area < qh KEEPminArea)
+ facet->good= False;
+ }
+ }
+ qh_settempfree(&facets);
+ count= 0;
+ FORALLfacet_(facetlist) {
+ if (facet->good)
+ count++;
+ }
+ qh num_good= count;
+} /* markkeep */
+
+
+/*---------------------------------
+
+ qh_markvoronoi( facetlist, facets, printall, isLower, numcenters )
+ mark voronoi vertices for printing by site pairs
+
+ returns:
+ temporary set of vertices indexed by pointid
+ isLower set if printing lower hull (i.e., at least one facet is lower hull)
+ numcenters= total number of Voronoi vertices
+ bumps qh.printoutnum for vertex-at-infinity
+ clears all facet->seen and sets facet->seen2
+
+ if selected
+ facet->visitid= Voronoi vertex id
+ else if upper hull (or 'Qu' and lower hull)
+ facet->visitid= 0
+ else
+ facet->visitid >= qh num_facets
+
+ notes:
+ ignores qh.ATinfinity, if defined
+*/
+setT *qh_markvoronoi(facetT *facetlist, setT *facets, boolT printall, boolT *isLowerp, int *numcentersp) {
+ int numcenters=0;
+ facetT *facet, **facetp;
+ setT *vertices;
+ boolT isLower= False;
+
+ qh printoutnum++;
+ qh_clearcenters(qh_ASvoronoi); /* in case, qh_printvdiagram2 called by user */
+ qh_vertexneighbors();
+ vertices= qh_pointvertex();
+ if (qh ATinfinity)
+ SETelem_(vertices, qh num_points-1)= NULL;
+ qh visit_id++;
+ maximize_(qh visit_id, (unsigned) qh num_facets);
+ FORALLfacet_(facetlist) {
+ if (printall || !qh_skipfacet(facet)) {
+ if (!facet->upperdelaunay) {
+ isLower= True;
+ break;
+ }
+ }
+ }
+ FOREACHfacet_(facets) {
+ if (printall || !qh_skipfacet(facet)) {
+ if (!facet->upperdelaunay) {
+ isLower= True;
+ break;
+ }
+ }
+ }
+ FORALLfacets {
+ if (facet->normal && (facet->upperdelaunay == isLower))
+ facet->visitid= 0; /* facetlist or facets may overwrite */
+ else
+ facet->visitid= qh visit_id;
+ facet->seen= False;
+ facet->seen2= True;
+ }
+ numcenters++; /* qh_INFINITE */
+ FORALLfacet_(facetlist) {
+ if (printall || !qh_skipfacet(facet))
+ facet->visitid= numcenters++;
+ }
+ FOREACHfacet_(facets) {
+ if (printall || !qh_skipfacet(facet))
+ facet->visitid= numcenters++;
+ }
+ *isLowerp= isLower;
+ *numcentersp= numcenters;
+ trace2((qh ferr, 2007, "qh_markvoronoi: isLower %d numcenters %d\n", isLower, numcenters));
+ return vertices;
+} /* markvoronoi */
+
+/*---------------------------------
+
+ qh_order_vertexneighbors( vertex )
+ order facet neighbors of a 2-d or 3-d vertex by adjacency
+
+ notes:
+ does not orient the neighbors
+
+ design:
+ initialize a new neighbor set with the first facet in vertex->neighbors
+ while vertex->neighbors non-empty
+ select next neighbor in the previous facet's neighbor set
+ set vertex->neighbors to the new neighbor set
+*/
+void qh_order_vertexneighbors(vertexT *vertex) {
+ setT *newset;
+ facetT *facet, *neighbor, **neighborp;
+
+ trace4((qh ferr, 4018, "qh_order_vertexneighbors: order neighbors of v%d for 3-d\n", vertex->id));
+ newset= qh_settemp(qh_setsize(vertex->neighbors));
+ facet= (facetT*)qh_setdellast(vertex->neighbors);
+ qh_setappend(&newset, facet);
+ while (qh_setsize(vertex->neighbors)) {
+ FOREACHneighbor_(vertex) {
+ if (qh_setin(facet->neighbors, neighbor)) {
+ qh_setdel(vertex->neighbors, neighbor);
+ qh_setappend(&newset, neighbor);
+ facet= neighbor;
+ break;
+ }
+ }
+ if (!neighbor) {
+ qh_fprintf(qh ferr, 6066, "qhull internal error (qh_order_vertexneighbors): no neighbor of v%d for f%d\n",
+ vertex->id, facet->id);
+ qh_errexit(qh_ERRqhull, facet, NULL);
+ }
+ }
+ qh_setfree(&vertex->neighbors);
+ qh_settemppop();
+ vertex->neighbors= newset;
+} /* order_vertexneighbors */
+
+/*---------------------------------
+
+ qh_prepare_output( )
+ prepare for qh_produce_output2() according to
+ qh.KEEPminArea, KEEParea, KEEPmerge, GOODvertex, GOODthreshold, GOODpoint, ONLYgood, SPLITthresholds
+ does not reset facet->good
+
+ notes
+ except for PRINTstatistics, no-op if previously called with same options
+*/
+void qh_prepare_output(void) {
+ if (qh VORONOI) {
+ qh_clearcenters (qh_ASvoronoi);
+ qh_vertexneighbors();
+ }
+ if (qh TRIangulate && !qh hasTriangulation) {
+ qh_triangulate();
+ if (qh VERIFYoutput && !qh CHECKfrequently)
+ qh_checkpolygon (qh facet_list);
+ }
+ qh_findgood_all (qh facet_list);
+ if (qh GETarea)
+ qh_getarea(qh facet_list);
+ if (qh KEEParea || qh KEEPmerge || qh KEEPminArea < REALmax/2)
+ qh_markkeep (qh facet_list);
+ if (qh PRINTstatistics)
+ qh_collectstatistics();
+}
+
+/*---------------------------------
+
+ qh_printafacet( fp, format, facet, printall )
+ print facet to fp in given output format (see qh.PRINTout)
+
+ returns:
+ nop if !printall and qh_skipfacet()
+ nop if visible facet and NEWfacets and format != PRINTfacets
+ must match qh_countfacets
+
+ notes
+ preserves qh.visit_id
+ facet->normal may be null if PREmerge/MERGEexact and STOPcone before merge
+
+ see
+ qh_printbegin() and qh_printend()
+
+ design:
+ test for printing facet
+ call appropriate routine for format
+ or output results directly
+*/
+void qh_printafacet(FILE *fp, qh_PRINT format, facetT *facet, boolT printall) {
+ realT color[4], offset, dist, outerplane, innerplane;
+ boolT zerodiv;
+ coordT *point, *normp, *coordp, **pointp, *feasiblep;
+ int k;
+ vertexT *vertex, **vertexp;
+ facetT *neighbor, **neighborp;
+
+ if (!printall && qh_skipfacet(facet))
+ return;
+ if (facet->visible && qh NEWfacets && format != qh_PRINTfacets)
+ return;
+ qh printoutnum++;
+ switch (format) {
+ case qh_PRINTarea:
+ if (facet->isarea) {
+ qh_fprintf(fp, 9009, qh_REAL_1, facet->f.area);
+ qh_fprintf(fp, 9010, "\n");
+ }else
+ qh_fprintf(fp, 9011, "0\n");
+ break;
+ case qh_PRINTcoplanars:
+ qh_fprintf(fp, 9012, "%d", qh_setsize(facet->coplanarset));
+ FOREACHpoint_(facet->coplanarset)
+ qh_fprintf(fp, 9013, " %d", qh_pointid(point));
+ qh_fprintf(fp, 9014, "\n");
+ break;
+ case qh_PRINTcentrums:
+ qh_printcenter(fp, format, NULL, facet);
+ break;
+ case qh_PRINTfacets:
+ qh_printfacet(fp, facet);
+ break;
+ case qh_PRINTfacets_xridge:
+ qh_printfacetheader(fp, facet);
+ break;
+ case qh_PRINTgeom: /* either 2 , 3, or 4-d by qh_printbegin */
+ if (!facet->normal)
+ break;
+ for (k=qh hull_dim; k--; ) {
+ color[k]= (facet->normal[k]+1.0)/2.0;
+ maximize_(color[k], -1.0);
+ minimize_(color[k], +1.0);
+ }
+ qh_projectdim3 (color, color);
+ if (qh PRINTdim != qh hull_dim)
+ qh_normalize2 (color, 3, True, NULL, NULL);
+ if (qh hull_dim <= 2)
+ qh_printfacet2geom(fp, facet, color);
+ else if (qh hull_dim == 3) {
+ if (facet->simplicial)
+ qh_printfacet3geom_simplicial(fp, facet, color);
+ else
+ qh_printfacet3geom_nonsimplicial(fp, facet, color);
+ }else {
+ if (facet->simplicial)
+ qh_printfacet4geom_simplicial(fp, facet, color);
+ else
+ qh_printfacet4geom_nonsimplicial(fp, facet, color);
+ }
+ break;
+ case qh_PRINTids:
+ qh_fprintf(fp, 9015, "%d\n", facet->id);
+ break;
+ case qh_PRINTincidences:
+ case qh_PRINToff:
+ case qh_PRINTtriangles:
+ if (qh hull_dim == 3 && format != qh_PRINTtriangles)
+ qh_printfacet3vertex(fp, facet, format);
+ else if (facet->simplicial || qh hull_dim == 2 || format == qh_PRINToff)
+ qh_printfacetNvertex_simplicial(fp, facet, format);
+ else
+ qh_printfacetNvertex_nonsimplicial(fp, facet, qh printoutvar++, format);
+ break;
+ case qh_PRINTinner:
+ qh_outerinner(facet, NULL, &innerplane);
+ offset= facet->offset - innerplane;
+ goto LABELprintnorm;
+ break; /* prevent warning */
+ case qh_PRINTmerges:
+ qh_fprintf(fp, 9016, "%d\n", facet->nummerge);
+ break;
+ case qh_PRINTnormals:
+ offset= facet->offset;
+ goto LABELprintnorm;
+ break; /* prevent warning */
+ case qh_PRINTouter:
+ qh_outerinner(facet, &outerplane, NULL);
+ offset= facet->offset - outerplane;
+ LABELprintnorm:
+ if (!facet->normal) {
+ qh_fprintf(fp, 9017, "no normal for facet f%d\n", facet->id);
+ break;
+ }
+ if (qh CDDoutput) {
+ qh_fprintf(fp, 9018, qh_REAL_1, -offset);
+ for (k=0; k < qh hull_dim; k++)
+ qh_fprintf(fp, 9019, qh_REAL_1, -facet->normal[k]);
+ }else {
+ for (k=0; k < qh hull_dim; k++)
+ qh_fprintf(fp, 9020, qh_REAL_1, facet->normal[k]);
+ qh_fprintf(fp, 9021, qh_REAL_1, offset);
+ }
+ qh_fprintf(fp, 9022, "\n");
+ break;
+ case qh_PRINTmathematica: /* either 2 or 3-d by qh_printbegin */
+ case qh_PRINTmaple:
+ if (qh hull_dim == 2)
+ qh_printfacet2math(fp, facet, format, qh printoutvar++);
+ else
+ qh_printfacet3math(fp, facet, format, qh printoutvar++);
+ break;
+ case qh_PRINTneighbors:
+ qh_fprintf(fp, 9023, "%d", qh_setsize(facet->neighbors));
+ FOREACHneighbor_(facet)
+ qh_fprintf(fp, 9024, " %d",
+ neighbor->visitid ? neighbor->visitid - 1: 0 - neighbor->id);
+ qh_fprintf(fp, 9025, "\n");
+ break;
+ case qh_PRINTpointintersect:
+ if (!qh feasible_point) {
+ qh_fprintf(qh ferr, 6067, "qhull input error (qh_printafacet): option 'Fp' needs qh feasible_point\n");
+ qh_errexit( qh_ERRinput, NULL, NULL);
+ }
+ if (facet->offset > 0)
+ goto LABELprintinfinite;
+ point= coordp= (coordT*)qh_memalloc(qh normal_size);
+ normp= facet->normal;
+ feasiblep= qh feasible_point;
+ if (facet->offset < -qh MINdenom) {
+ for (k=qh hull_dim; k--; )
+ *(coordp++)= (*(normp++) / - facet->offset) + *(feasiblep++);
+ }else {
+ for (k=qh hull_dim; k--; ) {
+ *(coordp++)= qh_divzero(*(normp++), facet->offset, qh MINdenom_1,
+ &zerodiv) + *(feasiblep++);
+ if (zerodiv) {
+ qh_memfree(point, qh normal_size);
+ goto LABELprintinfinite;
+ }
+ }
+ }
+ qh_printpoint(fp, NULL, point);
+ qh_memfree(point, qh normal_size);
+ break;
+ LABELprintinfinite:
+ for (k=qh hull_dim; k--; )
+ qh_fprintf(fp, 9026, qh_REAL_1, qh_INFINITE);
+ qh_fprintf(fp, 9027, "\n");
+ break;
+ case qh_PRINTpointnearest:
+ FOREACHpoint_(facet->coplanarset) {
+ int id, id2;
+ vertex= qh_nearvertex(facet, point, &dist);
+ id= qh_pointid(vertex->point);
+ id2= qh_pointid(point);
+ qh_fprintf(fp, 9028, "%d %d %d " qh_REAL_1 "\n", id, id2, facet->id, dist);
+ }
+ break;
+ case qh_PRINTpoints: /* VORONOI only by qh_printbegin */
+ if (qh CDDoutput)
+ qh_fprintf(fp, 9029, "1 ");
+ qh_printcenter(fp, format, NULL, facet);
+ break;
+ case qh_PRINTvertices:
+ qh_fprintf(fp, 9030, "%d", qh_setsize(facet->vertices));
+ FOREACHvertex_(facet->vertices)
+ qh_fprintf(fp, 9031, " %d", qh_pointid(vertex->point));
+ qh_fprintf(fp, 9032, "\n");
+ break;
+ default:
+ break;
+ }
+} /* printafacet */
+
+/*---------------------------------
+
+ qh_printbegin( )
+ prints header for all output formats
+
+ returns:
+ checks for valid format
+
+ notes:
+ uses qh.visit_id for 3/4off
+ changes qh.interior_point if printing centrums
+ qh_countfacets clears facet->visitid for non-good facets
+
+ see
+ qh_printend() and qh_printafacet()
+
+ design:
+ count facets and related statistics
+ print header for format
+*/
+void qh_printbegin(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {
+ int numfacets, numsimplicial, numridges, totneighbors, numcoplanars, numtricoplanars;
+ int i, num;
+ facetT *facet, **facetp;
+ vertexT *vertex, **vertexp;
+ setT *vertices;
+ pointT *point, **pointp, *pointtemp;
+
+ qh printoutnum= 0;
+ qh_countfacets(facetlist, facets, printall, &numfacets, &numsimplicial,
+ &totneighbors, &numridges, &numcoplanars, &numtricoplanars);
+ switch (format) {
+ case qh_PRINTnone:
+ break;
+ case qh_PRINTarea:
+ qh_fprintf(fp, 9033, "%d\n", numfacets);
+ break;
+ case qh_PRINTcoplanars:
+ qh_fprintf(fp, 9034, "%d\n", numfacets);
+ break;
+ case qh_PRINTcentrums:
+ if (qh CENTERtype == qh_ASnone)
+ qh_clearcenters(qh_AScentrum);
+ qh_fprintf(fp, 9035, "%d\n%d\n", qh hull_dim, numfacets);
+ break;
+ case qh_PRINTfacets:
+ case qh_PRINTfacets_xridge:
+ if (facetlist)
+ qh_printvertexlist(fp, "Vertices and facets:\n", facetlist, facets, printall);
+ break;
+ case qh_PRINTgeom:
+ if (qh hull_dim > 4) /* qh_initqhull_globals also checks */
+ goto LABELnoformat;
+ if (qh VORONOI && qh hull_dim > 3) /* PRINTdim == DROPdim == hull_dim-1 */
+ goto LABELnoformat;
+ if (qh hull_dim == 2 && (qh PRINTridges || qh DOintersections))
+ qh_fprintf(qh ferr, 7049, "qhull warning: output for ridges and intersections not implemented in 2-d\n");
+ if (qh hull_dim == 4 && (qh PRINTinner || qh PRINTouter ||
+ (qh PRINTdim == 4 && qh PRINTcentrums)))
+ qh_fprintf(qh ferr, 7050, "qhull warning: output for outer/inner planes and centrums not implemented in 4-d\n");
+ if (qh PRINTdim == 4 && (qh PRINTspheres))
+ qh_fprintf(qh ferr, 7051, "qhull warning: output for vertices not implemented in 4-d\n");
+ if (qh PRINTdim == 4 && qh DOintersections && qh PRINTnoplanes)
+ qh_fprintf(qh ferr, 7052, "qhull warning: 'Gnh' generates no output in 4-d\n");
+ if (qh PRINTdim == 2) {
+ qh_fprintf(fp, 9036, "{appearance {linewidth 3} LIST # %s | %s\n",
+ qh rbox_command, qh qhull_command);
+ }else if (qh PRINTdim == 3) {
+ qh_fprintf(fp, 9037, "{appearance {+edge -evert linewidth 2} LIST # %s | %s\n",
+ qh rbox_command, qh qhull_command);
+ }else if (qh PRINTdim == 4) {
+ qh visit_id++;
+ num= 0;
+ FORALLfacet_(facetlist) /* get number of ridges to be printed */
+ qh_printend4geom(NULL, facet, &num, printall);
+ FOREACHfacet_(facets)
+ qh_printend4geom(NULL, facet, &num, printall);
+ qh ridgeoutnum= num;
+ qh printoutvar= 0; /* counts number of ridges in output */
+ qh_fprintf(fp, 9038, "LIST # %s | %s\n", qh rbox_command, qh qhull_command);
+ }
+
+ if (qh PRINTdots) {
+ qh printoutnum++;
+ num= qh num_points + qh_setsize(qh other_points);
+ if (qh DELAUNAY && qh ATinfinity)
+ num--;
+ if (qh PRINTdim == 4)
+ qh_fprintf(fp, 9039, "4VECT %d %d 1\n", num, num);
+ else
+ qh_fprintf(fp, 9040, "VECT %d %d 1\n", num, num);
+
+ for (i=num; i--; ) {
+ if (i % 20 == 0)
+ qh_fprintf(fp, 9041, "\n");
+ qh_fprintf(fp, 9042, "1 ");
+ }
+ qh_fprintf(fp, 9043, "# 1 point per line\n1 ");
+ for (i=num-1; i--; ) { /* num at least 3 for D2 */
+ if (i % 20 == 0)
+ qh_fprintf(fp, 9044, "\n");
+ qh_fprintf(fp, 9045, "0 ");
+ }
+ qh_fprintf(fp, 9046, "# 1 color for all\n");
+ FORALLpoints {
+ if (!qh DELAUNAY || !qh ATinfinity || qh_pointid(point) != qh num_points-1) {
+ if (qh PRINTdim == 4)
+ qh_printpoint(fp, NULL, point);
+ else
+ qh_printpoint3 (fp, point);
+ }
+ }
+ FOREACHpoint_(qh other_points) {
+ if (qh PRINTdim == 4)
+ qh_printpoint(fp, NULL, point);
+ else
+ qh_printpoint3 (fp, point);
+ }
+ qh_fprintf(fp, 9047, "0 1 1 1 # color of points\n");
+ }
+
+ if (qh PRINTdim == 4 && !qh PRINTnoplanes)
+ /* 4dview loads up multiple 4OFF objects slowly */
+ qh_fprintf(fp, 9048, "4OFF %d %d 1\n", 3*qh ridgeoutnum, qh ridgeoutnum);
+ qh PRINTcradius= 2 * qh DISTround; /* include test DISTround */
+ if (qh PREmerge) {
+ maximize_(qh PRINTcradius, qh premerge_centrum + qh DISTround);
+ }else if (qh POSTmerge)
+ maximize_(qh PRINTcradius, qh postmerge_centrum + qh DISTround);
+ qh PRINTradius= qh PRINTcradius;
+ if (qh PRINTspheres + qh PRINTcoplanar)
+ maximize_(qh PRINTradius, qh MAXabs_coord * qh_MINradius);
+ if (qh premerge_cos < REALmax/2) {
+ maximize_(qh PRINTradius, (1- qh premerge_cos) * qh MAXabs_coord);
+ }else if (!qh PREmerge && qh POSTmerge && qh postmerge_cos < REALmax/2) {
+ maximize_(qh PRINTradius, (1- qh postmerge_cos) * qh MAXabs_coord);
+ }
+ maximize_(qh PRINTradius, qh MINvisible);
+ if (qh JOGGLEmax < REALmax/2)
+ qh PRINTradius += qh JOGGLEmax * sqrt((realT)qh hull_dim);
+ if (qh PRINTdim != 4 &&
+ (qh PRINTcoplanar || qh PRINTspheres || qh PRINTcentrums)) {
+ vertices= qh_facetvertices(facetlist, facets, printall);
+ if (qh PRINTspheres && qh PRINTdim <= 3)
+ qh_printspheres(fp, vertices, qh PRINTradius);
+ if (qh PRINTcoplanar || qh PRINTcentrums) {
+ qh firstcentrum= True;
+ if (qh PRINTcoplanar&& !qh PRINTspheres) {
+ FOREACHvertex_(vertices)
+ qh_printpointvect2 (fp, vertex->point, NULL, qh interior_point, qh PRINTradius);
+ }
+ FORALLfacet_(facetlist) {
+ if (!printall && qh_skipfacet(facet))
+ continue;
+ if (!facet->normal)
+ continue;
+ if (qh PRINTcentrums && qh PRINTdim <= 3)
+ qh_printcentrum(fp, facet, qh PRINTcradius);
+ if (!qh PRINTcoplanar)
+ continue;
+ FOREACHpoint_(facet->coplanarset)
+ qh_printpointvect2 (fp, point, facet->normal, NULL, qh PRINTradius);
+ FOREACHpoint_(facet->outsideset)
+ qh_printpointvect2 (fp, point, facet->normal, NULL, qh PRINTradius);
+ }
+ FOREACHfacet_(facets) {
+ if (!printall && qh_skipfacet(facet))
+ continue;
+ if (!facet->normal)
+ continue;
+ if (qh PRINTcentrums && qh PRINTdim <= 3)
+ qh_printcentrum(fp, facet, qh PRINTcradius);
+ if (!qh PRINTcoplanar)
+ continue;
+ FOREACHpoint_(facet->coplanarset)
+ qh_printpointvect2 (fp, point, facet->normal, NULL, qh PRINTradius);
+ FOREACHpoint_(facet->outsideset)
+ qh_printpointvect2 (fp, point, facet->normal, NULL, qh PRINTradius);
+ }
+ }
+ qh_settempfree(&vertices);
+ }
+ qh visit_id++; /* for printing hyperplane intersections */
+ break;
+ case qh_PRINTids:
+ qh_fprintf(fp, 9049, "%d\n", numfacets);
+ break;
+ case qh_PRINTincidences:
+ if (qh VORONOI && qh PRINTprecision)
+ qh_fprintf(qh ferr, 7053, "qhull warning: writing Delaunay. Use 'p' or 'o' for Voronoi centers\n");
+ qh printoutvar= qh vertex_id; /* centrum id for non-simplicial facets */
+ if (qh hull_dim <= 3)
+ qh_fprintf(fp, 9050, "%d\n", numfacets);
+ else
+ qh_fprintf(fp, 9051, "%d\n", numsimplicial+numridges);
+ break;
+ case qh_PRINTinner:
+ case qh_PRINTnormals:
+ case qh_PRINTouter:
+ if (qh CDDoutput)
+ qh_fprintf(fp, 9052, "%s | %s\nbegin\n %d %d real\n", qh rbox_command,
+ qh qhull_command, numfacets, qh hull_dim+1);
+ else
+ qh_fprintf(fp, 9053, "%d\n%d\n", qh hull_dim+1, numfacets);
+ break;
+ case qh_PRINTmathematica:
+ case qh_PRINTmaple:
+ if (qh hull_dim > 3) /* qh_initbuffers also checks */
+ goto LABELnoformat;
+ if (qh VORONOI)
+ qh_fprintf(qh ferr, 7054, "qhull warning: output is the Delaunay triangulation\n");
+ if (format == qh_PRINTmaple) {
+ if (qh hull_dim == 2)
+ qh_fprintf(fp, 9054, "PLOT(CURVES(\n");
+ else
+ qh_fprintf(fp, 9055, "PLOT3D(POLYGONS(\n");
+ }else
+ qh_fprintf(fp, 9056, "{\n");
+ qh printoutvar= 0; /* counts number of facets for notfirst */
+ break;
+ case qh_PRINTmerges:
+ qh_fprintf(fp, 9057, "%d\n", numfacets);
+ break;
+ case qh_PRINTpointintersect:
+ qh_fprintf(fp, 9058, "%d\n%d\n", qh hull_dim, numfacets);
+ break;
+ case qh_PRINTneighbors:
+ qh_fprintf(fp, 9059, "%d\n", numfacets);
+ break;
+ case qh_PRINToff:
+ case qh_PRINTtriangles:
+ if (qh VORONOI)
+ goto LABELnoformat;
+ num = qh hull_dim;
+ if (format == qh_PRINToff || qh hull_dim == 2)
+ qh_fprintf(fp, 9060, "%d\n%d %d %d\n", num,
+ qh num_points+qh_setsize(qh other_points), numfacets, totneighbors/2);
+ else { /* qh_PRINTtriangles */
+ qh printoutvar= qh num_points+qh_setsize(qh other_points); /* first centrum */
+ if (qh DELAUNAY)
+ num--; /* drop last dimension */
+ qh_fprintf(fp, 9061, "%d\n%d %d %d\n", num, qh printoutvar
+ + numfacets - numsimplicial, numsimplicial + numridges, totneighbors/2);
+ }
+ FORALLpoints
+ qh_printpointid(qh fout, NULL, num, point, -1);
+ FOREACHpoint_(qh other_points)
+ qh_printpointid(qh fout, NULL, num, point, -1);
+ if (format == qh_PRINTtriangles && qh hull_dim > 2) {
+ FORALLfacets {
+ if (!facet->simplicial && facet->visitid)
+ qh_printcenter(qh fout, format, NULL, facet);
+ }
+ }
+ break;
+ case qh_PRINTpointnearest:
+ qh_fprintf(fp, 9062, "%d\n", numcoplanars);
+ break;
+ case qh_PRINTpoints:
+ if (!qh VORONOI)
+ goto LABELnoformat;
+ if (qh CDDoutput)
+ qh_fprintf(fp, 9063, "%s | %s\nbegin\n%d %d real\n", qh rbox_command,
+ qh qhull_command, numfacets, qh hull_dim);
+ else
+ qh_fprintf(fp, 9064, "%d\n%d\n", qh hull_dim-1, numfacets);
+ break;
+ case qh_PRINTvertices:
+ qh_fprintf(fp, 9065, "%d\n", numfacets);
+ break;
+ case qh_PRINTsummary:
+ default:
+ LABELnoformat:
+ qh_fprintf(qh ferr, 6068, "qhull internal error (qh_printbegin): can not use this format for dimension %d\n",
+ qh hull_dim);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+} /* printbegin */
+
+/*---------------------------------
+
+ qh_printcenter( fp, string, facet )
+ print facet->center as centrum or Voronoi center
+ string may be NULL. Don't include '%' codes.
+ nop if qh CENTERtype neither CENTERvoronoi nor CENTERcentrum
+ if upper envelope of Delaunay triangulation and point at-infinity
+ prints qh_INFINITE instead;
+
+ notes:
+ defines facet->center if needed
+ if format=PRINTgeom, adds a 0 if would otherwise be 2-d
+ Same as QhullFacet::printCenter
+*/
+void qh_printcenter(FILE *fp, qh_PRINT format, const char *string, facetT *facet) {
+ int k, num;
+
+ if (qh CENTERtype != qh_ASvoronoi && qh CENTERtype != qh_AScentrum)
+ return;
+ if (string)
+ qh_fprintf(fp, 9066, string);
+ if (qh CENTERtype == qh_ASvoronoi) {
+ num= qh hull_dim-1;
+ if (!facet->normal || !facet->upperdelaunay || !qh ATinfinity) {
+ if (!facet->center)
+ facet->center= qh_facetcenter(facet->vertices);
+ for (k=0; k < num; k++)
+ qh_fprintf(fp, 9067, qh_REAL_1, facet->center[k]);
+ }else {
+ for (k=0; k < num; k++)
+ qh_fprintf(fp, 9068, qh_REAL_1, qh_INFINITE);
+ }
+ }else /* qh CENTERtype == qh_AScentrum */ {
+ num= qh hull_dim;
+ if (format == qh_PRINTtriangles && qh DELAUNAY)
+ num--;
+ if (!facet->center)
+ facet->center= qh_getcentrum(facet);
+ for (k=0; k < num; k++)
+ qh_fprintf(fp, 9069, qh_REAL_1, facet->center[k]);
+ }
+ if (format == qh_PRINTgeom && num == 2)
+ qh_fprintf(fp, 9070, " 0\n");
+ else
+ qh_fprintf(fp, 9071, "\n");
+} /* printcenter */
+
+/*---------------------------------
+
+ qh_printcentrum( fp, facet, radius )
+ print centrum for a facet in OOGL format
+ radius defines size of centrum
+ 2-d or 3-d only
+
+ returns:
+ defines facet->center if needed
+*/
+void qh_printcentrum(FILE *fp, facetT *facet, realT radius) {
+ pointT *centrum, *projpt;
+ boolT tempcentrum= False;
+ realT xaxis[4], yaxis[4], normal[4], dist;
+ realT green[3]={0, 1, 0};
+ vertexT *apex;
+ int k;
+
+ if (qh CENTERtype == qh_AScentrum) {
+ if (!facet->center)
+ facet->center= qh_getcentrum(facet);
+ centrum= facet->center;
+ }else {
+ centrum= qh_getcentrum(facet);
+ tempcentrum= True;
+ }
+ qh_fprintf(fp, 9072, "{appearance {-normal -edge normscale 0} ");
+ if (qh firstcentrum) {
+ qh firstcentrum= False;
+ qh_fprintf(fp, 9073, "{INST geom { define centrum CQUAD # f%d\n\
+-0.3 -0.3 0.0001 0 0 1 1\n\
+ 0.3 -0.3 0.0001 0 0 1 1\n\
+ 0.3 0.3 0.0001 0 0 1 1\n\
+-0.3 0.3 0.0001 0 0 1 1 } transform { \n", facet->id);
+ }else
+ qh_fprintf(fp, 9074, "{INST geom { : centrum } transform { # f%d\n", facet->id);
+ apex= SETfirstt_(facet->vertices, vertexT);
+ qh_distplane(apex->point, facet, &dist);
+ projpt= qh_projectpoint(apex->point, facet, dist);
+ for (k=qh hull_dim; k--; ) {
+ xaxis[k]= projpt[k] - centrum[k];
+ normal[k]= facet->normal[k];
+ }
+ if (qh hull_dim == 2) {
+ xaxis[2]= 0;
+ normal[2]= 0;
+ }else if (qh hull_dim == 4) {
+ qh_projectdim3 (xaxis, xaxis);
+ qh_projectdim3 (normal, normal);
+ qh_normalize2 (normal, qh PRINTdim, True, NULL, NULL);
+ }
+ qh_crossproduct(3, xaxis, normal, yaxis);
+ qh_fprintf(fp, 9075, "%8.4g %8.4g %8.4g 0\n", xaxis[0], xaxis[1], xaxis[2]);
+ qh_fprintf(fp, 9076, "%8.4g %8.4g %8.4g 0\n", yaxis[0], yaxis[1], yaxis[2]);
+ qh_fprintf(fp, 9077, "%8.4g %8.4g %8.4g 0\n", normal[0], normal[1], normal[2]);
+ qh_printpoint3 (fp, centrum);
+ qh_fprintf(fp, 9078, "1 }}}\n");
+ qh_memfree(projpt, qh normal_size);
+ qh_printpointvect(fp, centrum, facet->normal, NULL, radius, green);
+ if (tempcentrum)
+ qh_memfree(centrum, qh normal_size);
+} /* printcentrum */
+
+/*---------------------------------
+
+ qh_printend( fp, format )
+ prints trailer for all output formats
+
+ see:
+ qh_printbegin() and qh_printafacet()
+
+*/
+void qh_printend(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {
+ int num;
+ facetT *facet, **facetp;
+
+ if (!qh printoutnum)
+ qh_fprintf(qh ferr, 7055, "qhull warning: no facets printed\n");
+ switch (format) {
+ case qh_PRINTgeom:
+ if (qh hull_dim == 4 && qh DROPdim < 0 && !qh PRINTnoplanes) {
+ qh visit_id++;
+ num= 0;
+ FORALLfacet_(facetlist)
+ qh_printend4geom(fp, facet,&num, printall);
+ FOREACHfacet_(facets)
+ qh_printend4geom(fp, facet, &num, printall);
+ if (num != qh ridgeoutnum || qh printoutvar != qh ridgeoutnum) {
+ qh_fprintf(qh ferr, 6069, "qhull internal error (qh_printend): number of ridges %d != number printed %d and at end %d\n", qh ridgeoutnum, qh printoutvar, num);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ }else
+ qh_fprintf(fp, 9079, "}\n");
+ break;
+ case qh_PRINTinner:
+ case qh_PRINTnormals:
+ case qh_PRINTouter:
+ if (qh CDDoutput)
+ qh_fprintf(fp, 9080, "end\n");
+ break;
+ case qh_PRINTmaple:
+ qh_fprintf(fp, 9081, "));\n");
+ break;
+ case qh_PRINTmathematica:
+ qh_fprintf(fp, 9082, "}\n");
+ break;
+ case qh_PRINTpoints:
+ if (qh CDDoutput)
+ qh_fprintf(fp, 9083, "end\n");
+ break;
+ default:
+ break;
+ }
+} /* printend */
+
+/*---------------------------------
+
+ qh_printend4geom( fp, facet, numridges, printall )
+ helper function for qh_printbegin/printend
+
+ returns:
+ number of printed ridges
+
+ notes:
+ just counts printed ridges if fp=NULL
+ uses facet->visitid
+ must agree with qh_printfacet4geom...
+
+ design:
+ computes color for facet from its normal
+ prints each ridge of facet
+*/
+void qh_printend4geom(FILE *fp, facetT *facet, int *nump, boolT printall) {
+ realT color[3];
+ int i, num= *nump;
+ facetT *neighbor, **neighborp;
+ ridgeT *ridge, **ridgep;
+
+ if (!printall && qh_skipfacet(facet))
+ return;
+ if (qh PRINTnoplanes || (facet->visible && qh NEWfacets))
+ return;
+ if (!facet->normal)
+ return;
+ if (fp) {
+ for (i=0; i < 3; i++) {
+ color[i]= (facet->normal[i]+1.0)/2.0;
+ maximize_(color[i], -1.0);
+ minimize_(color[i], +1.0);
+ }
+ }
+ facet->visitid= qh visit_id;
+ if (facet->simplicial) {
+ FOREACHneighbor_(facet) {
+ if (neighbor->visitid != qh visit_id) {
+ if (fp)
+ qh_fprintf(fp, 9084, "3 %d %d %d %8.4g %8.4g %8.4g 1 # f%d f%d\n",
+ 3*num, 3*num+1, 3*num+2, color[0], color[1], color[2],
+ facet->id, neighbor->id);
+ num++;
+ }
+ }
+ }else {
+ FOREACHridge_(facet->ridges) {
+ neighbor= otherfacet_(ridge, facet);
+ if (neighbor->visitid != qh visit_id) {
+ if (fp)
+ qh_fprintf(fp, 9085, "3 %d %d %d %8.4g %8.4g %8.4g 1 #r%d f%d f%d\n",
+ 3*num, 3*num+1, 3*num+2, color[0], color[1], color[2],
+ ridge->id, facet->id, neighbor->id);
+ num++;
+ }
+ }
+ }
+ *nump= num;
+} /* printend4geom */
+
+/*---------------------------------
+
+ qh_printextremes( fp, facetlist, facets, printall )
+ print extreme points for convex hulls or halfspace intersections
+
+ notes:
+ #points, followed by ids, one per line
+
+ sorted by id
+ same order as qh_printpoints_out if no coplanar/interior points
+*/
+void qh_printextremes(FILE *fp, facetT *facetlist, setT *facets, boolT printall) {
+ setT *vertices, *points;
+ pointT *point;
+ vertexT *vertex, **vertexp;
+ int id;
+ int numpoints=0, point_i, point_n;
+ int allpoints= qh num_points + qh_setsize(qh other_points);
+
+ points= qh_settemp(allpoints);
+ qh_setzero(points, 0, allpoints);
+ vertices= qh_facetvertices(facetlist, facets, printall);
+ FOREACHvertex_(vertices) {
+ id= qh_pointid(vertex->point);
+ if (id >= 0) {
+ SETelem_(points, id)= vertex->point;
+ numpoints++;
+ }
+ }
+ qh_settempfree(&vertices);
+ qh_fprintf(fp, 9086, "%d\n", numpoints);
+ FOREACHpoint_i_(points) {
+ if (point)
+ qh_fprintf(fp, 9087, "%d\n", point_i);
+ }
+ qh_settempfree(&points);
+} /* printextremes */
+
+/*---------------------------------
+
+ qh_printextremes_2d( fp, facetlist, facets, printall )
+ prints point ids for facets in qh_ORIENTclock order
+
+ notes:
+ #points, followed by ids, one per line
+ if facetlist/facets are disjoint than the output includes skips
+ errors if facets form a loop
+ does not print coplanar points
+*/
+void qh_printextremes_2d(FILE *fp, facetT *facetlist, setT *facets, boolT printall) {
+ int numfacets, numridges, totneighbors, numcoplanars, numsimplicial, numtricoplanars;
+ setT *vertices;
+ facetT *facet, *startfacet, *nextfacet;
+ vertexT *vertexA, *vertexB;
+
+ qh_countfacets(facetlist, facets, printall, &numfacets, &numsimplicial,
+ &totneighbors, &numridges, &numcoplanars, &numtricoplanars); /* marks qh visit_id */
+ vertices= qh_facetvertices(facetlist, facets, printall);
+ qh_fprintf(fp, 9088, "%d\n", qh_setsize(vertices));
+ qh_settempfree(&vertices);
+ if (!numfacets)
+ return;
+ facet= startfacet= facetlist ? facetlist : SETfirstt_(facets, facetT);
+ qh vertex_visit++;
+ qh visit_id++;
+ do {
+ if (facet->toporient ^ qh_ORIENTclock) {
+ vertexA= SETfirstt_(facet->vertices, vertexT);
+ vertexB= SETsecondt_(facet->vertices, vertexT);
+ nextfacet= SETfirstt_(facet->neighbors, facetT);
+ }else {
+ vertexA= SETsecondt_(facet->vertices, vertexT);
+ vertexB= SETfirstt_(facet->vertices, vertexT);
+ nextfacet= SETsecondt_(facet->neighbors, facetT);
+ }
+ if (facet->visitid == qh visit_id) {
+ qh_fprintf(qh ferr, 6218, "Qhull internal error (qh_printextremes_2d): loop in facet list. facet %d nextfacet %d\n",
+ facet->id, nextfacet->id);
+ qh_errexit2 (qh_ERRqhull, facet, nextfacet);
+ }
+ if (facet->visitid) {
+ if (vertexA->visitid != qh vertex_visit) {
+ vertexA->visitid= qh vertex_visit;
+ qh_fprintf(fp, 9089, "%d\n", qh_pointid(vertexA->point));
+ }
+ if (vertexB->visitid != qh vertex_visit) {
+ vertexB->visitid= qh vertex_visit;
+ qh_fprintf(fp, 9090, "%d\n", qh_pointid(vertexB->point));
+ }
+ }
+ facet->visitid= qh visit_id;
+ facet= nextfacet;
+ }while (facet && facet != startfacet);
+} /* printextremes_2d */
+
+/*---------------------------------
+
+ qh_printextremes_d( fp, facetlist, facets, printall )
+ print extreme points of input sites for Delaunay triangulations
+
+ notes:
+ #points, followed by ids, one per line
+
+ unordered
+*/
+void qh_printextremes_d(FILE *fp, facetT *facetlist, setT *facets, boolT printall) {
+ setT *vertices;
+ vertexT *vertex, **vertexp;
+ boolT upperseen, lowerseen;
+ facetT *neighbor, **neighborp;
+ int numpoints=0;
+
+ vertices= qh_facetvertices(facetlist, facets, printall);
+ qh_vertexneighbors();
+ FOREACHvertex_(vertices) {
+ upperseen= lowerseen= False;
+ FOREACHneighbor_(vertex) {
+ if (neighbor->upperdelaunay)
+ upperseen= True;
+ else
+ lowerseen= True;
+ }
+ if (upperseen && lowerseen) {
+ vertex->seen= True;
+ numpoints++;
+ }else
+ vertex->seen= False;
+ }
+ qh_fprintf(fp, 9091, "%d\n", numpoints);
+ FOREACHvertex_(vertices) {
+ if (vertex->seen)
+ qh_fprintf(fp, 9092, "%d\n", qh_pointid(vertex->point));
+ }
+ qh_settempfree(&vertices);
+} /* printextremes_d */
+
+/*---------------------------------
+
+ qh_printfacet( fp, facet )
+ prints all fields of a facet to fp
+
+ notes:
+ ridges printed in neighbor order
+*/
+void qh_printfacet(FILE *fp, facetT *facet) {
+
+ qh_printfacetheader(fp, facet);
+ if (facet->ridges)
+ qh_printfacetridges(fp, facet);
+} /* printfacet */
+
+
+/*---------------------------------
+
+ qh_printfacet2geom( fp, facet, color )
+ print facet as part of a 2-d VECT for Geomview
+
+ notes:
+ assume precise calculations in io.c with roundoff covered by qh_GEOMepsilon
+ mindist is calculated within io.c. maxoutside is calculated elsewhere
+ so a DISTround error may have occured.
+*/
+void qh_printfacet2geom(FILE *fp, facetT *facet, realT color[3]) {
+ pointT *point0, *point1;
+ realT mindist, innerplane, outerplane;
+ int k;
+
+ qh_facet2point(facet, &point0, &point1, &mindist);
+ qh_geomplanes(facet, &outerplane, &innerplane);
+ if (qh PRINTouter || (!qh PRINTnoplanes && !qh PRINTinner))
+ qh_printfacet2geom_points(fp, point0, point1, facet, outerplane, color);
+ if (qh PRINTinner || (!qh PRINTnoplanes && !qh PRINTouter &&
+ outerplane - innerplane > 2 * qh MAXabs_coord * qh_GEOMepsilon)) {
+ for (k=3; k--; )
+ color[k]= 1.0 - color[k];
+ qh_printfacet2geom_points(fp, point0, point1, facet, innerplane, color);
+ }
+ qh_memfree(point1, qh normal_size);
+ qh_memfree(point0, qh normal_size);
+} /* printfacet2geom */
+
+/*---------------------------------
+
+ qh_printfacet2geom_points( fp, point1, point2, facet, offset, color )
+ prints a 2-d facet as a VECT with 2 points at some offset.
+ The points are on the facet's plane.
+*/
+void qh_printfacet2geom_points(FILE *fp, pointT *point1, pointT *point2,
+ facetT *facet, realT offset, realT color[3]) {
+ pointT *p1= point1, *p2= point2;
+
+ qh_fprintf(fp, 9093, "VECT 1 2 1 2 1 # f%d\n", facet->id);
+ if (offset != 0.0) {
+ p1= qh_projectpoint(p1, facet, -offset);
+ p2= qh_projectpoint(p2, facet, -offset);
+ }
+ qh_fprintf(fp, 9094, "%8.4g %8.4g %8.4g\n%8.4g %8.4g %8.4g\n",
+ p1[0], p1[1], 0.0, p2[0], p2[1], 0.0);
+ if (offset != 0.0) {
+ qh_memfree(p1, qh normal_size);
+ qh_memfree(p2, qh normal_size);
+ }
+ qh_fprintf(fp, 9095, "%8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]);
+} /* printfacet2geom_points */
+
+
+/*---------------------------------
+
+ qh_printfacet2math( fp, facet, format, notfirst )
+ print 2-d Maple or Mathematica output for a facet
+ may be non-simplicial
+
+ notes:
+ use %16.8f since Mathematica 2.2 does not handle exponential format
+ see qh_printfacet3math
+*/
+void qh_printfacet2math(FILE *fp, facetT *facet, qh_PRINT format, int notfirst) {
+ pointT *point0, *point1;
+ realT mindist;
+ const char *pointfmt;
+
+ qh_facet2point(facet, &point0, &point1, &mindist);
+ if (notfirst)
+ qh_fprintf(fp, 9096, ",");
+ if (format == qh_PRINTmaple)
+ pointfmt= "[[%16.8f, %16.8f], [%16.8f, %16.8f]]\n";
+ else
+ pointfmt= "Line[{{%16.8f, %16.8f}, {%16.8f, %16.8f}}]\n";
+ qh_fprintf(fp, 9097, pointfmt, point0[0], point0[1], point1[0], point1[1]);
+ qh_memfree(point1, qh normal_size);
+ qh_memfree(point0, qh normal_size);
+} /* printfacet2math */
+
+
+/*---------------------------------
+
+ qh_printfacet3geom_nonsimplicial( fp, facet, color )
+ print Geomview OFF for a 3-d nonsimplicial facet.
+ if DOintersections, prints ridges to unvisited neighbors(qh visit_id)
+
+ notes
+ uses facet->visitid for intersections and ridges
+*/
+void qh_printfacet3geom_nonsimplicial(FILE *fp, facetT *facet, realT color[3]) {
+ ridgeT *ridge, **ridgep;
+ setT *projectedpoints, *vertices;
+ vertexT *vertex, **vertexp, *vertexA, *vertexB;
+ pointT *projpt, *point, **pointp;
+ facetT *neighbor;
+ realT dist, outerplane, innerplane;
+ int cntvertices, k;
+ realT black[3]={0, 0, 0}, green[3]={0, 1, 0};
+
+ qh_geomplanes(facet, &outerplane, &innerplane);
+ vertices= qh_facet3vertex(facet); /* oriented */
+ cntvertices= qh_setsize(vertices);
+ projectedpoints= qh_settemp(cntvertices);
+ FOREACHvertex_(vertices) {
+ zinc_(Zdistio);
+ qh_distplane(vertex->point, facet, &dist);
+ projpt= qh_projectpoint(vertex->point, facet, dist);
+ qh_setappend(&projectedpoints, projpt);
+ }
+ if (qh PRINTouter || (!qh PRINTnoplanes && !qh PRINTinner))
+ qh_printfacet3geom_points(fp, projectedpoints, facet, outerplane, color);
+ if (qh PRINTinner || (!qh PRINTnoplanes && !qh PRINTouter &&
+ outerplane - innerplane > 2 * qh MAXabs_coord * qh_GEOMepsilon)) {
+ for (k=3; k--; )
+ color[k]= 1.0 - color[k];
+ qh_printfacet3geom_points(fp, projectedpoints, facet, innerplane, color);
+ }
+ FOREACHpoint_(projectedpoints)
+ qh_memfree(point, qh normal_size);
+ qh_settempfree(&projectedpoints);
+ qh_settempfree(&vertices);
+ if ((qh DOintersections || qh PRINTridges)
+ && (!facet->visible || !qh NEWfacets)) {
+ facet->visitid= qh visit_id;
+ FOREACHridge_(facet->ridges) {
+ neighbor= otherfacet_(ridge, facet);
+ if (neighbor->visitid != qh visit_id) {
+ if (qh DOintersections)
+ qh_printhyperplaneintersection(fp, facet, neighbor, ridge->vertices, black);
+ if (qh PRINTridges) {
+ vertexA= SETfirstt_(ridge->vertices, vertexT);
+ vertexB= SETsecondt_(ridge->vertices, vertexT);
+ qh_printline3geom(fp, vertexA->point, vertexB->point, green);
+ }
+ }
+ }
+ }
+} /* printfacet3geom_nonsimplicial */
+
+/*---------------------------------
+
+ qh_printfacet3geom_points( fp, points, facet, offset )
+ prints a 3-d facet as OFF Geomview object.
+ offset is relative to the facet's hyperplane
+ Facet is determined as a list of points
+*/
+void qh_printfacet3geom_points(FILE *fp, setT *points, facetT *facet, realT offset, realT color[3]) {
+ int k, n= qh_setsize(points), i;
+ pointT *point, **pointp;
+ setT *printpoints;
+
+ qh_fprintf(fp, 9098, "{ OFF %d 1 1 # f%d\n", n, facet->id);
+ if (offset != 0.0) {
+ printpoints= qh_settemp(n);
+ FOREACHpoint_(points)
+ qh_setappend(&printpoints, qh_projectpoint(point, facet, -offset));
+ }else
+ printpoints= points;
+ FOREACHpoint_(printpoints) {
+ for (k=0; k < qh hull_dim; k++) {
+ if (k == qh DROPdim)
+ qh_fprintf(fp, 9099, "0 ");
+ else
+ qh_fprintf(fp, 9100, "%8.4g ", point[k]);
+ }
+ if (printpoints != points)
+ qh_memfree(point, qh normal_size);
+ qh_fprintf(fp, 9101, "\n");
+ }
+ if (printpoints != points)
+ qh_settempfree(&printpoints);
+ qh_fprintf(fp, 9102, "%d ", n);
+ for (i=0; i < n; i++)
+ qh_fprintf(fp, 9103, "%d ", i);
+ qh_fprintf(fp, 9104, "%8.4g %8.4g %8.4g 1.0 }\n", color[0], color[1], color[2]);
+} /* printfacet3geom_points */
+
+
+/*---------------------------------
+
+ qh_printfacet3geom_simplicial( )
+ print Geomview OFF for a 3-d simplicial facet.
+
+ notes:
+ may flip color
+ uses facet->visitid for intersections and ridges
+
+ assume precise calculations in io.c with roundoff covered by qh_GEOMepsilon
+ innerplane may be off by qh DISTround. Maxoutside is calculated elsewhere
+ so a DISTround error may have occured.
+*/
+void qh_printfacet3geom_simplicial(FILE *fp, facetT *facet, realT color[3]) {
+ setT *points, *vertices;
+ vertexT *vertex, **vertexp, *vertexA, *vertexB;
+ facetT *neighbor, **neighborp;
+ realT outerplane, innerplane;
+ realT black[3]={0, 0, 0}, green[3]={0, 1, 0};
+ int k;
+
+ qh_geomplanes(facet, &outerplane, &innerplane);
+ vertices= qh_facet3vertex(facet);
+ points= qh_settemp(qh TEMPsize);
+ FOREACHvertex_(vertices)
+ qh_setappend(&points, vertex->point);
+ if (qh PRINTouter || (!qh PRINTnoplanes && !qh PRINTinner))
+ qh_printfacet3geom_points(fp, points, facet, outerplane, color);
+ if (qh PRINTinner || (!qh PRINTnoplanes && !qh PRINTouter &&
+ outerplane - innerplane > 2 * qh MAXabs_coord * qh_GEOMepsilon)) {
+ for (k=3; k--; )
+ color[k]= 1.0 - color[k];
+ qh_printfacet3geom_points(fp, points, facet, innerplane, color);
+ }
+ qh_settempfree(&points);
+ qh_settempfree(&vertices);
+ if ((qh DOintersections || qh PRINTridges)
+ && (!facet->visible || !qh NEWfacets)) {
+ facet->visitid= qh visit_id;
+ FOREACHneighbor_(facet) {
+ if (neighbor->visitid != qh visit_id) {
+ vertices= qh_setnew_delnthsorted(facet->vertices, qh hull_dim,
+ SETindex_(facet->neighbors, neighbor), 0);
+ if (qh DOintersections)
+ qh_printhyperplaneintersection(fp, facet, neighbor, vertices, black);
+ if (qh PRINTridges) {
+ vertexA= SETfirstt_(vertices, vertexT);
+ vertexB= SETsecondt_(vertices, vertexT);
+ qh_printline3geom(fp, vertexA->point, vertexB->point, green);
+ }
+ qh_setfree(&vertices);
+ }
+ }
+ }
+} /* printfacet3geom_simplicial */
+
+/*---------------------------------
+
+ qh_printfacet3math( fp, facet, notfirst )
+ print 3-d Maple or Mathematica output for a facet
+
+ notes:
+ may be non-simplicial
+ use %16.8f since Mathematica 2.2 does not handle exponential format
+ see qh_printfacet2math
+*/
+void qh_printfacet3math(FILE *fp, facetT *facet, qh_PRINT format, int notfirst) {
+ vertexT *vertex, **vertexp;
+ setT *points, *vertices;
+ pointT *point, **pointp;
+ boolT firstpoint= True;
+ realT dist;
+ const char *pointfmt, *endfmt;
+
+ if (notfirst)
+ qh_fprintf(fp, 9105, ",\n");
+ vertices= qh_facet3vertex(facet);
+ points= qh_settemp(qh_setsize(vertices));
+ FOREACHvertex_(vertices) {
+ zinc_(Zdistio);
+ qh_distplane(vertex->point, facet, &dist);
+ point= qh_projectpoint(vertex->point, facet, dist);
+ qh_setappend(&points, point);
+ }
+ if (format == qh_PRINTmaple) {
+ qh_fprintf(fp, 9106, "[");
+ pointfmt= "[%16.8f, %16.8f, %16.8f]";
+ endfmt= "]";
+ }else {
+ qh_fprintf(fp, 9107, "Polygon[{");
+ pointfmt= "{%16.8f, %16.8f, %16.8f}";
+ endfmt= "}]";
+ }
+ FOREACHpoint_(points) {
+ if (firstpoint)
+ firstpoint= False;
+ else
+ qh_fprintf(fp, 9108, ",\n");
+ qh_fprintf(fp, 9109, pointfmt, point[0], point[1], point[2]);
+ }
+ FOREACHpoint_(points)
+ qh_memfree(point, qh normal_size);
+ qh_settempfree(&points);
+ qh_settempfree(&vertices);
+ qh_fprintf(fp, 9110, endfmt);
+} /* printfacet3math */
+
+
+/*---------------------------------
+
+ qh_printfacet3vertex( fp, facet, format )
+ print vertices in a 3-d facet as point ids
+
+ notes:
+ prints number of vertices first if format == qh_PRINToff
+ the facet may be non-simplicial
+*/
+void qh_printfacet3vertex(FILE *fp, facetT *facet, qh_PRINT format) {
+ vertexT *vertex, **vertexp;
+ setT *vertices;
+
+ vertices= qh_facet3vertex(facet);
+ if (format == qh_PRINToff)
+ qh_fprintf(fp, 9111, "%d ", qh_setsize(vertices));
+ FOREACHvertex_(vertices)
+ qh_fprintf(fp, 9112, "%d ", qh_pointid(vertex->point));
+ qh_fprintf(fp, 9113, "\n");
+ qh_settempfree(&vertices);
+} /* printfacet3vertex */
+
+
+/*---------------------------------
+
+ qh_printfacet4geom_nonsimplicial( )
+ print Geomview 4OFF file for a 4d nonsimplicial facet
+ prints all ridges to unvisited neighbors (qh.visit_id)
+ if qh.DROPdim
+ prints in OFF format
+
+ notes:
+ must agree with printend4geom()
+*/
+void qh_printfacet4geom_nonsimplicial(FILE *fp, facetT *facet, realT color[3]) {
+ facetT *neighbor;
+ ridgeT *ridge, **ridgep;
+ vertexT *vertex, **vertexp;
+ pointT *point;
+ int k;
+ realT dist;
+
+ facet->visitid= qh visit_id;
+ if (qh PRINTnoplanes || (facet->visible && qh NEWfacets))
+ return;
+ FOREACHridge_(facet->ridges) {
+ neighbor= otherfacet_(ridge, facet);
+ if (neighbor->visitid == qh visit_id)
+ continue;
+ if (qh PRINTtransparent && !neighbor->good)
+ continue;
+ if (qh DOintersections)
+ qh_printhyperplaneintersection(fp, facet, neighbor, ridge->vertices, color);
+ else {
+ if (qh DROPdim >= 0)
+ qh_fprintf(fp, 9114, "OFF 3 1 1 # f%d\n", facet->id);
+ else {
+ qh printoutvar++;
+ qh_fprintf(fp, 9115, "# r%d between f%d f%d\n", ridge->id, facet->id, neighbor->id);
+ }
+ FOREACHvertex_(ridge->vertices) {
+ zinc_(Zdistio);
+ qh_distplane(vertex->point,facet, &dist);
+ point=qh_projectpoint(vertex->point,facet, dist);
+ for (k=0; k < qh hull_dim; k++) {
+ if (k != qh DROPdim)
+ qh_fprintf(fp, 9116, "%8.4g ", point[k]);
+ }
+ qh_fprintf(fp, 9117, "\n");
+ qh_memfree(point, qh normal_size);
+ }
+ if (qh DROPdim >= 0)
+ qh_fprintf(fp, 9118, "3 0 1 2 %8.4g %8.4g %8.4g\n", color[0], color[1], color[2]);
+ }
+ }
+} /* printfacet4geom_nonsimplicial */
+
+
+/*---------------------------------
+
+ qh_printfacet4geom_simplicial( fp, facet, color )
+ print Geomview 4OFF file for a 4d simplicial facet
+ prints triangles for unvisited neighbors (qh.visit_id)
+
+ notes:
+ must agree with printend4geom()
+*/
+void qh_printfacet4geom_simplicial(FILE *fp, facetT *facet, realT color[3]) {
+ setT *vertices;
+ facetT *neighbor, **neighborp;
+ vertexT *vertex, **vertexp;
+ int k;
+
+ facet->visitid= qh visit_id;
+ if (qh PRINTnoplanes || (facet->visible && qh NEWfacets))
+ return;
+ FOREACHneighbor_(facet) {
+ if (neighbor->visitid == qh visit_id)
+ continue;
+ if (qh PRINTtransparent && !neighbor->good)
+ continue;
+ vertices= qh_setnew_delnthsorted(facet->vertices, qh hull_dim,
+ SETindex_(facet->neighbors, neighbor), 0);
+ if (qh DOintersections)
+ qh_printhyperplaneintersection(fp, facet, neighbor, vertices, color);
+ else {
+ if (qh DROPdim >= 0)
+ qh_fprintf(fp, 9119, "OFF 3 1 1 # ridge between f%d f%d\n",
+ facet->id, neighbor->id);
+ else {
+ qh printoutvar++;
+ qh_fprintf(fp, 9120, "# ridge between f%d f%d\n", facet->id, neighbor->id);
+ }
+ FOREACHvertex_(vertices) {
+ for (k=0; k < qh hull_dim; k++) {
+ if (k != qh DROPdim)
+ qh_fprintf(fp, 9121, "%8.4g ", vertex->point[k]);
+ }
+ qh_fprintf(fp, 9122, "\n");
+ }
+ if (qh DROPdim >= 0)
+ qh_fprintf(fp, 9123, "3 0 1 2 %8.4g %8.4g %8.4g\n", color[0], color[1], color[2]);
+ }
+ qh_setfree(&vertices);
+ }
+} /* printfacet4geom_simplicial */
+
+
+/*---------------------------------
+
+ qh_printfacetNvertex_nonsimplicial( fp, facet, id, format )
+ print vertices for an N-d non-simplicial facet
+ triangulates each ridge to the id
+*/
+void qh_printfacetNvertex_nonsimplicial(FILE *fp, facetT *facet, int id, qh_PRINT format) {
+ vertexT *vertex, **vertexp;
+ ridgeT *ridge, **ridgep;
+
+ if (facet->visible && qh NEWfacets)
+ return;
+ FOREACHridge_(facet->ridges) {
+ if (format == qh_PRINTtriangles)
+ qh_fprintf(fp, 9124, "%d ", qh hull_dim);
+ qh_fprintf(fp, 9125, "%d ", id);
+ if ((ridge->top == facet) ^ qh_ORIENTclock) {
+ FOREACHvertex_(ridge->vertices)
+ qh_fprintf(fp, 9126, "%d ", qh_pointid(vertex->point));
+ }else {
+ FOREACHvertexreverse12_(ridge->vertices)
+ qh_fprintf(fp, 9127, "%d ", qh_pointid(vertex->point));
+ }
+ qh_fprintf(fp, 9128, "\n");
+ }
+} /* printfacetNvertex_nonsimplicial */
+
+
+/*---------------------------------
+
+ qh_printfacetNvertex_simplicial( fp, facet, format )
+ print vertices for an N-d simplicial facet
+ prints vertices for non-simplicial facets
+ 2-d facets (orientation preserved by qh_mergefacet2d)
+ PRINToff ('o') for 4-d and higher
+*/
+void qh_printfacetNvertex_simplicial(FILE *fp, facetT *facet, qh_PRINT format) {
+ vertexT *vertex, **vertexp;
+
+ if (format == qh_PRINToff || format == qh_PRINTtriangles)
+ qh_fprintf(fp, 9129, "%d ", qh_setsize(facet->vertices));
+ if ((facet->toporient ^ qh_ORIENTclock)
+ || (qh hull_dim > 2 && !facet->simplicial)) {
+ FOREACHvertex_(facet->vertices)
+ qh_fprintf(fp, 9130, "%d ", qh_pointid(vertex->point));
+ }else {
+ FOREACHvertexreverse12_(facet->vertices)
+ qh_fprintf(fp, 9131, "%d ", qh_pointid(vertex->point));
+ }
+ qh_fprintf(fp, 9132, "\n");
+} /* printfacetNvertex_simplicial */
+
+
+/*---------------------------------
+
+ qh_printfacetheader( fp, facet )
+ prints header fields of a facet to fp
+
+ notes:
+ for 'f' output and debugging
+ Same as QhullFacet::printHeader()
+*/
+void qh_printfacetheader(FILE *fp, facetT *facet) {
+ pointT *point, **pointp, *furthest;
+ facetT *neighbor, **neighborp;
+ realT dist;
+
+ if (facet == qh_MERGEridge) {
+ qh_fprintf(fp, 9133, " MERGEridge\n");
+ return;
+ }else if (facet == qh_DUPLICATEridge) {
+ qh_fprintf(fp, 9134, " DUPLICATEridge\n");
+ return;
+ }else if (!facet) {
+ qh_fprintf(fp, 9135, " NULLfacet\n");
+ return;
+ }
+ qh old_randomdist= qh RANDOMdist;
+ qh RANDOMdist= False;
+ qh_fprintf(fp, 9136, "- f%d\n", facet->id);
+ qh_fprintf(fp, 9137, " - flags:");
+ if (facet->toporient)
+ qh_fprintf(fp, 9138, " top");
+ else
+ qh_fprintf(fp, 9139, " bottom");
+ if (facet->simplicial)
+ qh_fprintf(fp, 9140, " simplicial");
+ if (facet->tricoplanar)
+ qh_fprintf(fp, 9141, " tricoplanar");
+ if (facet->upperdelaunay)
+ qh_fprintf(fp, 9142, " upperDelaunay");
+ if (facet->visible)
+ qh_fprintf(fp, 9143, " visible");
+ if (facet->newfacet)
+ qh_fprintf(fp, 9144, " new");
+ if (facet->tested)
+ qh_fprintf(fp, 9145, " tested");
+ if (!facet->good)
+ qh_fprintf(fp, 9146, " notG");
+ if (facet->seen)
+ qh_fprintf(fp, 9147, " seen");
+ if (facet->coplanar)
+ qh_fprintf(fp, 9148, " coplanar");
+ if (facet->mergehorizon)
+ qh_fprintf(fp, 9149, " mergehorizon");
+ if (facet->keepcentrum)
+ qh_fprintf(fp, 9150, " keepcentrum");
+ if (facet->dupridge)
+ qh_fprintf(fp, 9151, " dupridge");
+ if (facet->mergeridge && !facet->mergeridge2)
+ qh_fprintf(fp, 9152, " mergeridge1");
+ if (facet->mergeridge2)
+ qh_fprintf(fp, 9153, " mergeridge2");
+ if (facet->newmerge)
+ qh_fprintf(fp, 9154, " newmerge");
+ if (facet->flipped)
+ qh_fprintf(fp, 9155, " flipped");
+ if (facet->notfurthest)
+ qh_fprintf(fp, 9156, " notfurthest");
+ if (facet->degenerate)
+ qh_fprintf(fp, 9157, " degenerate");
+ if (facet->redundant)
+ qh_fprintf(fp, 9158, " redundant");
+ qh_fprintf(fp, 9159, "\n");
+ if (facet->isarea)
+ qh_fprintf(fp, 9160, " - area: %2.2g\n", facet->f.area);
+ else if (qh NEWfacets && facet->visible && facet->f.replace)
+ qh_fprintf(fp, 9161, " - replacement: f%d\n", facet->f.replace->id);
+ else if (facet->newfacet) {
+ if (facet->f.samecycle && facet->f.samecycle != facet)
+ qh_fprintf(fp, 9162, " - shares same visible/horizon as f%d\n", facet->f.samecycle->id);
+ }else if (facet->tricoplanar /* !isarea */) {
+ if (facet->f.triowner)
+ qh_fprintf(fp, 9163, " - owner of normal & centrum is facet f%d\n", facet->f.triowner->id);
+ }else if (facet->f.newcycle)
+ qh_fprintf(fp, 9164, " - was horizon to f%d\n", facet->f.newcycle->id);
+ if (facet->nummerge)
+ qh_fprintf(fp, 9165, " - merges: %d\n", facet->nummerge);
+ qh_printpointid(fp, " - normal: ", qh hull_dim, facet->normal, -1);
+ qh_fprintf(fp, 9166, " - offset: %10.7g\n", facet->offset);
+ if (qh CENTERtype == qh_ASvoronoi || facet->center)
+ qh_printcenter(fp, qh_PRINTfacets, " - center: ", facet);
+#if qh_MAXoutside
+ if (facet->maxoutside > qh DISTround)
+ qh_fprintf(fp, 9167, " - maxoutside: %10.7g\n", facet->maxoutside);
+#endif
+ if (!SETempty_(facet->outsideset)) {
+ furthest= (pointT*)qh_setlast(facet->outsideset);
+ if (qh_setsize(facet->outsideset) < 6) {
+ qh_fprintf(fp, 9168, " - outside set(furthest p%d):\n", qh_pointid(furthest));
+ FOREACHpoint_(facet->outsideset)
+ qh_printpoint(fp, " ", point);
+ }else if (qh_setsize(facet->outsideset) < 21) {
+ qh_printpoints(fp, " - outside set:", facet->outsideset);
+ }else {
+ qh_fprintf(fp, 9169, " - outside set: %d points.", qh_setsize(facet->outsideset));
+ qh_printpoint(fp, " Furthest", furthest);
+ }
+#if !qh_COMPUTEfurthest
+ qh_fprintf(fp, 9170, " - furthest distance= %2.2g\n", facet->furthestdist);
+#endif
+ }
+ if (!SETempty_(facet->coplanarset)) {
+ furthest= (pointT*)qh_setlast(facet->coplanarset);
+ if (qh_setsize(facet->coplanarset) < 6) {
+ qh_fprintf(fp, 9171, " - coplanar set(furthest p%d):\n", qh_pointid(furthest));
+ FOREACHpoint_(facet->coplanarset)
+ qh_printpoint(fp, " ", point);
+ }else if (qh_setsize(facet->coplanarset) < 21) {
+ qh_printpoints(fp, " - coplanar set:", facet->coplanarset);
+ }else {
+ qh_fprintf(fp, 9172, " - coplanar set: %d points.", qh_setsize(facet->coplanarset));
+ qh_printpoint(fp, " Furthest", furthest);
+ }
+ zinc_(Zdistio);
+ qh_distplane(furthest, facet, &dist);
+ qh_fprintf(fp, 9173, " furthest distance= %2.2g\n", dist);
+ }
+ qh_printvertices(fp, " - vertices:", facet->vertices);
+ qh_fprintf(fp, 9174, " - neighboring facets:");
+ FOREACHneighbor_(facet) {
+ if (neighbor == qh_MERGEridge)
+ qh_fprintf(fp, 9175, " MERGE");
+ else if (neighbor == qh_DUPLICATEridge)
+ qh_fprintf(fp, 9176, " DUP");
+ else
+ qh_fprintf(fp, 9177, " f%d", neighbor->id);
+ }
+ qh_fprintf(fp, 9178, "\n");
+ qh RANDOMdist= qh old_randomdist;
+} /* printfacetheader */
+
+
+/*---------------------------------
+
+ qh_printfacetridges( fp, facet )
+ prints ridges of a facet to fp
+
+ notes:
+ ridges printed in neighbor order
+ assumes the ridges exist
+ for 'f' output
+ same as QhullFacet::printRidges
+*/
+void qh_printfacetridges(FILE *fp, facetT *facet) {
+ facetT *neighbor, **neighborp;
+ ridgeT *ridge, **ridgep;
+ int numridges= 0;
+
+
+ if (facet->visible && qh NEWfacets) {
+ qh_fprintf(fp, 9179, " - ridges(ids may be garbage):");
+ FOREACHridge_(facet->ridges)
+ qh_fprintf(fp, 9180, " r%d", ridge->id);
+ qh_fprintf(fp, 9181, "\n");
+ }else {
+ qh_fprintf(fp, 9182, " - ridges:\n");
+ FOREACHridge_(facet->ridges)
+ ridge->seen= False;
+ if (qh hull_dim == 3) {
+ ridge= SETfirstt_(facet->ridges, ridgeT);
+ while (ridge && !ridge->seen) {
+ ridge->seen= True;
+ qh_printridge(fp, ridge);
+ numridges++;
+ ridge= qh_nextridge3d(ridge, facet, NULL);
+ }
+ }else {
+ FOREACHneighbor_(facet) {
+ FOREACHridge_(facet->ridges) {
+ if (otherfacet_(ridge,facet) == neighbor) {
+ ridge->seen= True;
+ qh_printridge(fp, ridge);
+ numridges++;
+ }
+ }
+ }
+ }
+ if (numridges != qh_setsize(facet->ridges)) {
+ qh_fprintf(fp, 9183, " - all ridges:");
+ FOREACHridge_(facet->ridges)
+ qh_fprintf(fp, 9184, " r%d", ridge->id);
+ qh_fprintf(fp, 9185, "\n");
+ }
+ FOREACHridge_(facet->ridges) {
+ if (!ridge->seen)
+ qh_printridge(fp, ridge);
+ }
+ }
+} /* printfacetridges */
+
+/*---------------------------------
+
+ qh_printfacets( fp, format, facetlist, facets, printall )
+ prints facetlist and/or facet set in output format
+
+ notes:
+ also used for specialized formats ('FO' and summary)
+ turns off 'Rn' option since want actual numbers
+*/
+void qh_printfacets(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {
+ int numfacets, numsimplicial, numridges, totneighbors, numcoplanars, numtricoplanars;
+ facetT *facet, **facetp;
+ setT *vertices;
+ coordT *center;
+ realT outerplane, innerplane;
+
+ qh old_randomdist= qh RANDOMdist;
+ qh RANDOMdist= False;
+ if (qh CDDoutput && (format == qh_PRINTcentrums || format == qh_PRINTpointintersect || format == qh_PRINToff))
+ qh_fprintf(qh ferr, 7056, "qhull warning: CDD format is not available for centrums, halfspace\nintersections, and OFF file format.\n");
+ if (format == qh_PRINTnone)
+ ; /* print nothing */
+ else if (format == qh_PRINTaverage) {
+ vertices= qh_facetvertices(facetlist, facets, printall);
+ center= qh_getcenter(vertices);
+ qh_fprintf(fp, 9186, "%d 1\n", qh hull_dim);
+ qh_printpointid(fp, NULL, qh hull_dim, center, -1);
+ qh_memfree(center, qh normal_size);
+ qh_settempfree(&vertices);
+ }else if (format == qh_PRINTextremes) {
+ if (qh DELAUNAY)
+ qh_printextremes_d(fp, facetlist, facets, printall);
+ else if (qh hull_dim == 2)
+ qh_printextremes_2d(fp, facetlist, facets, printall);
+ else
+ qh_printextremes(fp, facetlist, facets, printall);
+ }else if (format == qh_PRINToptions)
+ qh_fprintf(fp, 9187, "Options selected for Qhull %s:\n%s\n", qh_version, qh qhull_options);
+ else if (format == qh_PRINTpoints && !qh VORONOI)
+ qh_printpoints_out(fp, facetlist, facets, printall);
+ else if (format == qh_PRINTqhull)
+ qh_fprintf(fp, 9188, "%s | %s\n", qh rbox_command, qh qhull_command);
+ else if (format == qh_PRINTsize) {
+ qh_fprintf(fp, 9189, "0\n2 ");
+ qh_fprintf(fp, 9190, qh_REAL_1, qh totarea);
+ qh_fprintf(fp, 9191, qh_REAL_1, qh totvol);
+ qh_fprintf(fp, 9192, "\n");
+ }else if (format == qh_PRINTsummary) {
+ qh_countfacets(facetlist, facets, printall, &numfacets, &numsimplicial,
+ &totneighbors, &numridges, &numcoplanars, &numtricoplanars);
+ vertices= qh_facetvertices(facetlist, facets, printall);
+ qh_fprintf(fp, 9193, "10 %d %d %d %d %d %d %d %d %d %d\n2 ", qh hull_dim,
+ qh num_points + qh_setsize(qh other_points),
+ qh num_vertices, qh num_facets - qh num_visible,
+ qh_setsize(vertices), numfacets, numcoplanars,
+ numfacets - numsimplicial, zzval_(Zdelvertextot),
+ numtricoplanars);
+ qh_settempfree(&vertices);
+ qh_outerinner(NULL, &outerplane, &innerplane);
+ qh_fprintf(fp, 9194, qh_REAL_2n, outerplane, innerplane);
+ }else if (format == qh_PRINTvneighbors)
+ qh_printvneighbors(fp, facetlist, facets, printall);
+ else if (qh VORONOI && format == qh_PRINToff)
+ qh_printvoronoi(fp, format, facetlist, facets, printall);
+ else if (qh VORONOI && format == qh_PRINTgeom) {
+ qh_printbegin(fp, format, facetlist, facets, printall);
+ qh_printvoronoi(fp, format, facetlist, facets, printall);
+ qh_printend(fp, format, facetlist, facets, printall);
+ }else if (qh VORONOI
+ && (format == qh_PRINTvertices || format == qh_PRINTinner || format == qh_PRINTouter))
+ qh_printvdiagram(fp, format, facetlist, facets, printall);
+ else {
+ qh_printbegin(fp, format, facetlist, facets, printall);
+ FORALLfacet_(facetlist)
+ qh_printafacet(fp, format, facet, printall);
+ FOREACHfacet_(facets)
+ qh_printafacet(fp, format, facet, printall);
+ qh_printend(fp, format, facetlist, facets, printall);
+ }
+ qh RANDOMdist= qh old_randomdist;
+} /* printfacets */
+
+
+/*---------------------------------
+
+ qh_printhyperplaneintersection( fp, facet1, facet2, vertices, color )
+ print Geomview OFF or 4OFF for the intersection of two hyperplanes in 3-d or 4-d
+*/
+void qh_printhyperplaneintersection(FILE *fp, facetT *facet1, facetT *facet2,
+ setT *vertices, realT color[3]) {
+ realT costheta, denominator, dist1, dist2, s, t, mindenom, p[4];
+ vertexT *vertex, **vertexp;
+ int i, k;
+ boolT nearzero1, nearzero2;
+
+ costheta= qh_getangle(facet1->normal, facet2->normal);
+ denominator= 1 - costheta * costheta;
+ i= qh_setsize(vertices);
+ if (qh hull_dim == 3)
+ qh_fprintf(fp, 9195, "VECT 1 %d 1 %d 1 ", i, i);
+ else if (qh hull_dim == 4 && qh DROPdim >= 0)
+ qh_fprintf(fp, 9196, "OFF 3 1 1 ");
+ else
+ qh printoutvar++;
+ qh_fprintf(fp, 9197, "# intersect f%d f%d\n", facet1->id, facet2->id);
+ mindenom= 1 / (10.0 * qh MAXabs_coord);
+ FOREACHvertex_(vertices) {
+ zadd_(Zdistio, 2);
+ qh_distplane(vertex->point, facet1, &dist1);
+ qh_distplane(vertex->point, facet2, &dist2);
+ s= qh_divzero(-dist1 + costheta * dist2, denominator,mindenom,&nearzero1);
+ t= qh_divzero(-dist2 + costheta * dist1, denominator,mindenom,&nearzero2);
+ if (nearzero1 || nearzero2)
+ s= t= 0.0;
+ for (k=qh hull_dim; k--; )
+ p[k]= vertex->point[k] + facet1->normal[k] * s + facet2->normal[k] * t;
+ if (qh PRINTdim <= 3) {
+ qh_projectdim3 (p, p);
+ qh_fprintf(fp, 9198, "%8.4g %8.4g %8.4g # ", p[0], p[1], p[2]);
+ }else
+ qh_fprintf(fp, 9199, "%8.4g %8.4g %8.4g %8.4g # ", p[0], p[1], p[2], p[3]);
+ if (nearzero1+nearzero2)
+ qh_fprintf(fp, 9200, "p%d(coplanar facets)\n", qh_pointid(vertex->point));
+ else
+ qh_fprintf(fp, 9201, "projected p%d\n", qh_pointid(vertex->point));
+ }
+ if (qh hull_dim == 3)
+ qh_fprintf(fp, 9202, "%8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]);
+ else if (qh hull_dim == 4 && qh DROPdim >= 0)
+ qh_fprintf(fp, 9203, "3 0 1 2 %8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]);
+} /* printhyperplaneintersection */
+
+/*---------------------------------
+
+ qh_printline3geom( fp, pointA, pointB, color )
+ prints a line as a VECT
+ prints 0's for qh.DROPdim
+
+ notes:
+ if pointA == pointB,
+ it's a 1 point VECT
+*/
+void qh_printline3geom(FILE *fp, pointT *pointA, pointT *pointB, realT color[3]) {
+ int k;
+ realT pA[4], pB[4];
+
+ qh_projectdim3(pointA, pA);
+ qh_projectdim3(pointB, pB);
+ if ((fabs(pA[0] - pB[0]) > 1e-3) ||
+ (fabs(pA[1] - pB[1]) > 1e-3) ||
+ (fabs(pA[2] - pB[2]) > 1e-3)) {
+ qh_fprintf(fp, 9204, "VECT 1 2 1 2 1\n");
+ for (k=0; k < 3; k++)
+ qh_fprintf(fp, 9205, "%8.4g ", pB[k]);
+ qh_fprintf(fp, 9206, " # p%d\n", qh_pointid(pointB));
+ }else
+ qh_fprintf(fp, 9207, "VECT 1 1 1 1 1\n");
+ for (k=0; k < 3; k++)
+ qh_fprintf(fp, 9208, "%8.4g ", pA[k]);
+ qh_fprintf(fp, 9209, " # p%d\n", qh_pointid(pointA));
+ qh_fprintf(fp, 9210, "%8.4g %8.4g %8.4g 1\n", color[0], color[1], color[2]);
+}
+
+/*---------------------------------
+
+ qh_printneighborhood( fp, format, facetA, facetB, printall )
+ print neighborhood of one or two facets
+
+ notes:
+ calls qh_findgood_all()
+ bumps qh.visit_id
+*/
+void qh_printneighborhood(FILE *fp, qh_PRINT format, facetT *facetA, facetT *facetB, boolT printall) {
+ facetT *neighbor, **neighborp, *facet;
+ setT *facets;
+
+ if (format == qh_PRINTnone)
+ return;
+ qh_findgood_all(qh facet_list);
+ if (facetA == facetB)
+ facetB= NULL;
+ facets= qh_settemp(2*(qh_setsize(facetA->neighbors)+1));
+ qh visit_id++;
+ for (facet= facetA; facet; facet= ((facet == facetA) ? facetB : NULL)) {
+ if (facet->visitid != qh visit_id) {
+ facet->visitid= qh visit_id;
+ qh_setappend(&facets, facet);
+ }
+ FOREACHneighbor_(facet) {
+ if (neighbor->visitid == qh visit_id)
+ continue;
+ neighbor->visitid= qh visit_id;
+ if (printall || !qh_skipfacet(neighbor))
+ qh_setappend(&facets, neighbor);
+ }
+ }
+ qh_printfacets(fp, format, NULL, facets, printall);
+ qh_settempfree(&facets);
+} /* printneighborhood */
+
+/*---------------------------------
+
+ qh_printpoint( fp, string, point )
+ qh_printpointid( fp, string, dim, point, id )
+ prints the coordinates of a point
+
+ returns:
+ if string is defined
+ prints 'string p%d' (skips p%d if id=-1)
+
+ notes:
+ nop if point is NULL
+ prints id unless it is undefined (-1)
+ Same as QhullPoint's printPoint
+*/
+void qh_printpoint(FILE *fp, const char *string, pointT *point) {
+ int id= qh_pointid( point);
+
+ qh_printpointid( fp, string, qh hull_dim, point, id);
+} /* printpoint */
+
+void qh_printpointid(FILE *fp, const char *string, int dim, pointT *point, int id) {
+ int k;
+ realT r; /*bug fix*/
+
+ if (!point)
+ return;
+ if (string) {
+ qh_fprintf(fp, 9211, "%s", string);
+ if (id != -1)
+ qh_fprintf(fp, 9212, " p%d: ", id);
+ }
+ for (k=dim; k--; ) {
+ r= *point++;
+ if (string)
+ qh_fprintf(fp, 9213, " %8.4g", r);
+ else
+ qh_fprintf(fp, 9214, qh_REAL_1, r);
+ }
+ qh_fprintf(fp, 9215, "\n");
+} /* printpointid */
+
+/*---------------------------------
+
+ qh_printpoint3( fp, point )
+ prints 2-d, 3-d, or 4-d point as Geomview 3-d coordinates
+*/
+void qh_printpoint3 (FILE *fp, pointT *point) {
+ int k;
+ realT p[4];
+
+ qh_projectdim3 (point, p);
+ for (k=0; k < 3; k++)
+ qh_fprintf(fp, 9216, "%8.4g ", p[k]);
+ qh_fprintf(fp, 9217, " # p%d\n", qh_pointid(point));
+} /* printpoint3 */
+
+/*----------------------------------------
+-printpoints- print pointids for a set of points starting at index
+ see geom.c
+*/
+
+/*---------------------------------
+
+ qh_printpoints_out( fp, facetlist, facets, printall )
+ prints vertices, coplanar/inside points, for facets by their point coordinates
+ allows qh.CDDoutput
+
+ notes:
+ same format as qhull input
+ if no coplanar/interior points,
+ same order as qh_printextremes
+*/
+void qh_printpoints_out(FILE *fp, facetT *facetlist, setT *facets, boolT printall) {
+ int allpoints= qh num_points + qh_setsize(qh other_points);
+ int numpoints=0, point_i, point_n;
+ setT *vertices, *points;
+ facetT *facet, **facetp;
+ pointT *point, **pointp;
+ vertexT *vertex, **vertexp;
+ int id;
+
+ points= qh_settemp(allpoints);
+ qh_setzero(points, 0, allpoints);
+ vertices= qh_facetvertices(facetlist, facets, printall);
+ FOREACHvertex_(vertices) {
+ id= qh_pointid(vertex->point);
+ if (id >= 0)
+ SETelem_(points, id)= vertex->point;
+ }
+ if (qh KEEPinside || qh KEEPcoplanar || qh KEEPnearinside) {
+ FORALLfacet_(facetlist) {
+ if (!printall && qh_skipfacet(facet))
+ continue;
+ FOREACHpoint_(facet->coplanarset) {
+ id= qh_pointid(point);
+ if (id >= 0)
+ SETelem_(points, id)= point;
+ }
+ }
+ FOREACHfacet_(facets) {
+ if (!printall && qh_skipfacet(facet))
+ continue;
+ FOREACHpoint_(facet->coplanarset) {
+ id= qh_pointid(point);
+ if (id >= 0)
+ SETelem_(points, id)= point;
+ }
+ }
+ }
+ qh_settempfree(&vertices);
+ FOREACHpoint_i_(points) {
+ if (point)
+ numpoints++;
+ }
+ if (qh CDDoutput)
+ qh_fprintf(fp, 9218, "%s | %s\nbegin\n%d %d real\n", qh rbox_command,
+ qh qhull_command, numpoints, qh hull_dim + 1);
+ else
+ qh_fprintf(fp, 9219, "%d\n%d\n", qh hull_dim, numpoints);
+ FOREACHpoint_i_(points) {
+ if (point) {
+ if (qh CDDoutput)
+ qh_fprintf(fp, 9220, "1 ");
+ qh_printpoint(fp, NULL, point);
+ }
+ }
+ if (qh CDDoutput)
+ qh_fprintf(fp, 9221, "end\n");
+ qh_settempfree(&points);
+} /* printpoints_out */
+
+
+/*---------------------------------
+
+ qh_printpointvect( fp, point, normal, center, radius, color )
+ prints a 2-d, 3-d, or 4-d point as 3-d VECT's relative to normal or to center point
+*/
+void qh_printpointvect(FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius, realT color[3]) {
+ realT diff[4], pointA[4];
+ int k;
+
+ for (k=qh hull_dim; k--; ) {
+ if (center)
+ diff[k]= point[k]-center[k];
+ else if (normal)
+ diff[k]= normal[k];
+ else
+ diff[k]= 0;
+ }
+ if (center)
+ qh_normalize2 (diff, qh hull_dim, True, NULL, NULL);
+ for (k=qh hull_dim; k--; )
+ pointA[k]= point[k]+diff[k] * radius;
+ qh_printline3geom(fp, point, pointA, color);
+} /* printpointvect */
+
+/*---------------------------------
+
+ qh_printpointvect2( fp, point, normal, center, radius )
+ prints a 2-d, 3-d, or 4-d point as 2 3-d VECT's for an imprecise point
+*/
+void qh_printpointvect2 (FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius) {
+ realT red[3]={1, 0, 0}, yellow[3]={1, 1, 0};
+
+ qh_printpointvect(fp, point, normal, center, radius, red);
+ qh_printpointvect(fp, point, normal, center, -radius, yellow);
+} /* printpointvect2 */
+
+/*---------------------------------
+
+ qh_printridge( fp, ridge )
+ prints the information in a ridge
+
+ notes:
+ for qh_printfacetridges()
+ same as operator<< [QhullRidge.cpp]
+*/
+void qh_printridge(FILE *fp, ridgeT *ridge) {
+
+ qh_fprintf(fp, 9222, " - r%d", ridge->id);
+ if (ridge->tested)
+ qh_fprintf(fp, 9223, " tested");
+ if (ridge->nonconvex)
+ qh_fprintf(fp, 9224, " nonconvex");
+ qh_fprintf(fp, 9225, "\n");
+ qh_printvertices(fp, " vertices:", ridge->vertices);
+ if (ridge->top && ridge->bottom)
+ qh_fprintf(fp, 9226, " between f%d and f%d\n",
+ ridge->top->id, ridge->bottom->id);
+} /* printridge */
+
+/*---------------------------------
+
+ qh_printspheres( fp, vertices, radius )
+ prints 3-d vertices as OFF spheres
+
+ notes:
+ inflated octahedron from Stuart Levy earth/mksphere2
+*/
+void qh_printspheres(FILE *fp, setT *vertices, realT radius) {
+ vertexT *vertex, **vertexp;
+
+ qh printoutnum++;
+ qh_fprintf(fp, 9227, "{appearance {-edge -normal normscale 0} {\n\
+INST geom {define vsphere OFF\n\
+18 32 48\n\
+\n\
+0 0 1\n\
+1 0 0\n\
+0 1 0\n\
+-1 0 0\n\
+0 -1 0\n\
+0 0 -1\n\
+0.707107 0 0.707107\n\
+0 -0.707107 0.707107\n\
+0.707107 -0.707107 0\n\
+-0.707107 0 0.707107\n\
+-0.707107 -0.707107 0\n\
+0 0.707107 0.707107\n\
+-0.707107 0.707107 0\n\
+0.707107 0.707107 0\n\
+0.707107 0 -0.707107\n\
+0 0.707107 -0.707107\n\
+-0.707107 0 -0.707107\n\
+0 -0.707107 -0.707107\n\
+\n\
+3 0 6 11\n\
+3 0 7 6 \n\
+3 0 9 7 \n\
+3 0 11 9\n\
+3 1 6 8 \n\
+3 1 8 14\n\
+3 1 13 6\n\
+3 1 14 13\n\
+3 2 11 13\n\
+3 2 12 11\n\
+3 2 13 15\n\
+3 2 15 12\n\
+3 3 9 12\n\
+3 3 10 9\n\
+3 3 12 16\n\
+3 3 16 10\n\
+3 4 7 10\n\
+3 4 8 7\n\
+3 4 10 17\n\
+3 4 17 8\n\
+3 5 14 17\n\
+3 5 15 14\n\
+3 5 16 15\n\
+3 5 17 16\n\
+3 6 13 11\n\
+3 7 8 6\n\
+3 9 10 7\n\
+3 11 12 9\n\
+3 14 8 17\n\
+3 15 13 14\n\
+3 16 12 15\n\
+3 17 10 16\n} transforms { TLIST\n");
+ FOREACHvertex_(vertices) {
+ qh_fprintf(fp, 9228, "%8.4g 0 0 0 # v%d\n 0 %8.4g 0 0\n0 0 %8.4g 0\n",
+ radius, vertex->id, radius, radius);
+ qh_printpoint3 (fp, vertex->point);
+ qh_fprintf(fp, 9229, "1\n");
+ }
+ qh_fprintf(fp, 9230, "}}}\n");
+} /* printspheres */
+
+
+/*----------------------------------------------
+-printsummary-
+ see libqhull.c
+*/
+
+/*---------------------------------
+
+ qh_printvdiagram( fp, format, facetlist, facets, printall )
+ print voronoi diagram
+ # of pairs of input sites
+ #indices site1 site2 vertex1 ...
+
+ sites indexed by input point id
+ point 0 is the first input point
+ vertices indexed by 'o' and 'p' order
+ vertex 0 is the 'vertex-at-infinity'
+ vertex 1 is the first Voronoi vertex
+
+ see:
+ qh_printvoronoi()
+ qh_eachvoronoi_all()
+
+ notes:
+ if all facets are upperdelaunay,
+ prints upper hull (furthest-site Voronoi diagram)
+*/
+void qh_printvdiagram(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {
+ setT *vertices;
+ int totcount, numcenters;
+ boolT isLower;
+ qh_RIDGE innerouter= qh_RIDGEall;
+ printvridgeT printvridge= NULL;
+
+ if (format == qh_PRINTvertices) {
+ innerouter= qh_RIDGEall;
+ printvridge= qh_printvridge;
+ }else if (format == qh_PRINTinner) {
+ innerouter= qh_RIDGEinner;
+ printvridge= qh_printvnorm;
+ }else if (format == qh_PRINTouter) {
+ innerouter= qh_RIDGEouter;
+ printvridge= qh_printvnorm;
+ }else {
+ qh_fprintf(qh ferr, 6219, "Qhull internal error (qh_printvdiagram): unknown print format %d.\n", format);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ vertices= qh_markvoronoi(facetlist, facets, printall, &isLower, &numcenters);
+ totcount= qh_printvdiagram2 (NULL, NULL, vertices, innerouter, False);
+ qh_fprintf(fp, 9231, "%d\n", totcount);
+ totcount= qh_printvdiagram2 (fp, printvridge, vertices, innerouter, True /* inorder*/);
+ qh_settempfree(&vertices);
+#if 0 /* for testing qh_eachvoronoi_all */
+ qh_fprintf(fp, 9232, "\n");
+ totcount= qh_eachvoronoi_all(fp, printvridge, qh UPPERdelaunay, innerouter, True /* inorder*/);
+ qh_fprintf(fp, 9233, "%d\n", totcount);
+#endif
+} /* printvdiagram */
+
+/*---------------------------------
+
+ qh_printvdiagram2( fp, printvridge, vertices, innerouter, inorder )
+ visit all pairs of input sites (vertices) for selected Voronoi vertices
+ vertices may include NULLs
+
+ innerouter:
+ qh_RIDGEall print inner ridges(bounded) and outer ridges(unbounded)
+ qh_RIDGEinner print only inner ridges
+ qh_RIDGEouter print only outer ridges
+
+ inorder:
+ print 3-d Voronoi vertices in order
+
+ assumes:
+ qh_markvoronoi marked facet->visitid for Voronoi vertices
+ all facet->seen= False
+ all facet->seen2= True
+
+ returns:
+ total number of Voronoi ridges
+ if printvridge,
+ calls printvridge( fp, vertex, vertexA, centers) for each ridge
+ [see qh_eachvoronoi()]
+
+ see:
+ qh_eachvoronoi_all()
+*/
+int qh_printvdiagram2 (FILE *fp, printvridgeT printvridge, setT *vertices, qh_RIDGE innerouter, boolT inorder) {
+ int totcount= 0;
+ int vertex_i, vertex_n;
+ vertexT *vertex;
+
+ FORALLvertices
+ vertex->seen= False;
+ FOREACHvertex_i_(vertices) {
+ if (vertex) {
+ if (qh GOODvertex > 0 && qh_pointid(vertex->point)+1 != qh GOODvertex)
+ continue;
+ totcount += qh_eachvoronoi(fp, printvridge, vertex, !qh_ALL, innerouter, inorder);
+ }
+ }
+ return totcount;
+} /* printvdiagram2 */
+
+/*---------------------------------
+
+ qh_printvertex( fp, vertex )
+ prints the information in a vertex
+ Duplicated as operator<< [QhullVertex.cpp]
+*/
+void qh_printvertex(FILE *fp, vertexT *vertex) {
+ pointT *point;
+ int k, count= 0;
+ facetT *neighbor, **neighborp;
+ realT r; /*bug fix*/
+
+ if (!vertex) {
+ qh_fprintf(fp, 9234, " NULLvertex\n");
+ return;
+ }
+ qh_fprintf(fp, 9235, "- p%d(v%d):", qh_pointid(vertex->point), vertex->id);
+ point= vertex->point;
+ if (point) {
+ for (k=qh hull_dim; k--; ) {
+ r= *point++;
+ qh_fprintf(fp, 9236, " %5.2g", r);
+ }
+ }
+ if (vertex->deleted)
+ qh_fprintf(fp, 9237, " deleted");
+ if (vertex->delridge)
+ qh_fprintf(fp, 9238, " ridgedeleted");
+ qh_fprintf(fp, 9239, "\n");
+ if (vertex->neighbors) {
+ qh_fprintf(fp, 9240, " neighbors:");
+ FOREACHneighbor_(vertex) {
+ if (++count % 100 == 0)
+ qh_fprintf(fp, 9241, "\n ");
+ qh_fprintf(fp, 9242, " f%d", neighbor->id);
+ }
+ qh_fprintf(fp, 9243, "\n");
+ }
+} /* printvertex */
+
+
+/*---------------------------------
+
+ qh_printvertexlist( fp, string, facetlist, facets, printall )
+ prints vertices used by a facetlist or facet set
+ tests qh_skipfacet() if !printall
+*/
+void qh_printvertexlist(FILE *fp, const char* string, facetT *facetlist,
+ setT *facets, boolT printall) {
+ vertexT *vertex, **vertexp;
+ setT *vertices;
+
+ vertices= qh_facetvertices(facetlist, facets, printall);
+ qh_fprintf(fp, 9244, "%s", string);
+ FOREACHvertex_(vertices)
+ qh_printvertex(fp, vertex);
+ qh_settempfree(&vertices);
+} /* printvertexlist */
+
+
+/*---------------------------------
+
+ qh_printvertices( fp, string, vertices )
+ prints vertices in a set
+ duplicated as printVertexSet [QhullVertex.cpp]
+*/
+void qh_printvertices(FILE *fp, const char* string, setT *vertices) {
+ vertexT *vertex, **vertexp;
+
+ qh_fprintf(fp, 9245, "%s", string);
+ FOREACHvertex_(vertices)
+ qh_fprintf(fp, 9246, " p%d(v%d)", qh_pointid(vertex->point), vertex->id);
+ qh_fprintf(fp, 9247, "\n");
+} /* printvertices */
+
+/*---------------------------------
+
+ qh_printvneighbors( fp, facetlist, facets, printall )
+ print vertex neighbors of vertices in facetlist and facets ('FN')
+
+ notes:
+ qh_countfacets clears facet->visitid for non-printed facets
+
+ design:
+ collect facet count and related statistics
+ if necessary, build neighbor sets for each vertex
+ collect vertices in facetlist and facets
+ build a point array for point->vertex and point->coplanar facet
+ for each point
+ list vertex neighbors or coplanar facet
+*/
+void qh_printvneighbors(FILE *fp, facetT* facetlist, setT *facets, boolT printall) {
+ int numfacets, numsimplicial, numridges, totneighbors, numneighbors, numcoplanars, numtricoplanars;
+ setT *vertices, *vertex_points, *coplanar_points;
+ int numpoints= qh num_points + qh_setsize(qh other_points);
+ vertexT *vertex, **vertexp;
+ int vertex_i, vertex_n;
+ facetT *facet, **facetp, *neighbor, **neighborp;
+ pointT *point, **pointp;
+
+ qh_countfacets(facetlist, facets, printall, &numfacets, &numsimplicial,
+ &totneighbors, &numridges, &numcoplanars, &numtricoplanars); /* sets facet->visitid */
+ qh_fprintf(fp, 9248, "%d\n", numpoints);
+ qh_vertexneighbors();
+ vertices= qh_facetvertices(facetlist, facets, printall);
+ vertex_points= qh_settemp(numpoints);
+ coplanar_points= qh_settemp(numpoints);
+ qh_setzero(vertex_points, 0, numpoints);
+ qh_setzero(coplanar_points, 0, numpoints);
+ FOREACHvertex_(vertices)
+ qh_point_add(vertex_points, vertex->point, vertex);
+ FORALLfacet_(facetlist) {
+ FOREACHpoint_(facet->coplanarset)
+ qh_point_add(coplanar_points, point, facet);
+ }
+ FOREACHfacet_(facets) {
+ FOREACHpoint_(facet->coplanarset)
+ qh_point_add(coplanar_points, point, facet);
+ }
+ FOREACHvertex_i_(vertex_points) {
+ if (vertex) {
+ numneighbors= qh_setsize(vertex->neighbors);
+ qh_fprintf(fp, 9249, "%d", numneighbors);
+ if (qh hull_dim == 3)
+ qh_order_vertexneighbors(vertex);
+ else if (qh hull_dim >= 4)
+ qsort(SETaddr_(vertex->neighbors, facetT), (size_t)numneighbors,
+ sizeof(facetT *), qh_compare_facetvisit);
+ FOREACHneighbor_(vertex)
+ qh_fprintf(fp, 9250, " %d",
+ neighbor->visitid ? neighbor->visitid - 1 : 0 - neighbor->id);
+ qh_fprintf(fp, 9251, "\n");
+ }else if ((facet= SETelemt_(coplanar_points, vertex_i, facetT)))
+ qh_fprintf(fp, 9252, "1 %d\n",
+ facet->visitid ? facet->visitid - 1 : 0 - facet->id);
+ else
+ qh_fprintf(fp, 9253, "0\n");
+ }
+ qh_settempfree(&coplanar_points);
+ qh_settempfree(&vertex_points);
+ qh_settempfree(&vertices);
+} /* printvneighbors */
+
+/*---------------------------------
+
+ qh_printvoronoi( fp, format, facetlist, facets, printall )
+ print voronoi diagram in 'o' or 'G' format
+ for 'o' format
+ prints voronoi centers for each facet and for infinity
+ for each vertex, lists ids of printed facets or infinity
+ assumes facetlist and facets are disjoint
+ for 'G' format
+ prints an OFF object
+ adds a 0 coordinate to center
+ prints infinity but does not list in vertices
+
+ see:
+ qh_printvdiagram()
+
+ notes:
+ if 'o',
+ prints a line for each point except "at-infinity"
+ if all facets are upperdelaunay,
+ reverses lower and upper hull
+*/
+void qh_printvoronoi(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {
+ int k, numcenters, numvertices= 0, numneighbors, numinf, vid=1, vertex_i, vertex_n;
+ facetT *facet, **facetp, *neighbor, **neighborp;
+ setT *vertices;
+ vertexT *vertex;
+ boolT isLower;
+ unsigned int numfacets= (unsigned int) qh num_facets;
+
+ vertices= qh_markvoronoi(facetlist, facets, printall, &isLower, &numcenters);
+ FOREACHvertex_i_(vertices) {
+ if (vertex) {
+ numvertices++;
+ numneighbors = numinf = 0;
+ FOREACHneighbor_(vertex) {
+ if (neighbor->visitid == 0)
+ numinf= 1;
+ else if (neighbor->visitid < numfacets)
+ numneighbors++;
+ }
+ if (numinf && !numneighbors) {
+ SETelem_(vertices, vertex_i)= NULL;
+ numvertices--;
+ }
+ }
+ }
+ if (format == qh_PRINTgeom)
+ qh_fprintf(fp, 9254, "{appearance {+edge -face} OFF %d %d 1 # Voronoi centers and cells\n",
+ numcenters, numvertices);
+ else
+ qh_fprintf(fp, 9255, "%d\n%d %d 1\n", qh hull_dim-1, numcenters, qh_setsize(vertices));
+ if (format == qh_PRINTgeom) {
+ for (k=qh hull_dim-1; k--; )
+ qh_fprintf(fp, 9256, qh_REAL_1, 0.0);
+ qh_fprintf(fp, 9257, " 0 # infinity not used\n");
+ }else {
+ for (k=qh hull_dim-1; k--; )
+ qh_fprintf(fp, 9258, qh_REAL_1, qh_INFINITE);
+ qh_fprintf(fp, 9259, "\n");
+ }
+ FORALLfacet_(facetlist) {
+ if (facet->visitid && facet->visitid < numfacets) {
+ if (format == qh_PRINTgeom)
+ qh_fprintf(fp, 9260, "# %d f%d\n", vid++, facet->id);
+ qh_printcenter(fp, format, NULL, facet);
+ }
+ }
+ FOREACHfacet_(facets) {
+ if (facet->visitid && facet->visitid < numfacets) {
+ if (format == qh_PRINTgeom)
+ qh_fprintf(fp, 9261, "# %d f%d\n", vid++, facet->id);
+ qh_printcenter(fp, format, NULL, facet);
+ }
+ }
+ FOREACHvertex_i_(vertices) {
+ numneighbors= 0;
+ numinf=0;
+ if (vertex) {
+ if (qh hull_dim == 3)
+ qh_order_vertexneighbors(vertex);
+ else if (qh hull_dim >= 4)
+ qsort(SETaddr_(vertex->neighbors, facetT),
+ (size_t)qh_setsize(vertex->neighbors),
+ sizeof(facetT *), qh_compare_facetvisit);
+ FOREACHneighbor_(vertex) {
+ if (neighbor->visitid == 0)
+ numinf= 1;
+ else if (neighbor->visitid < numfacets)
+ numneighbors++;
+ }
+ }
+ if (format == qh_PRINTgeom) {
+ if (vertex) {
+ qh_fprintf(fp, 9262, "%d", numneighbors);
+ FOREACHneighbor_(vertex) {
+ if (neighbor->visitid && neighbor->visitid < numfacets)
+ qh_fprintf(fp, 9263, " %d", neighbor->visitid);
+ }
+ qh_fprintf(fp, 9264, " # p%d(v%d)\n", vertex_i, vertex->id);
+ }else
+ qh_fprintf(fp, 9265, " # p%d is coplanar or isolated\n", vertex_i);
+ }else {
+ if (numinf)
+ numneighbors++;
+ qh_fprintf(fp, 9266, "%d", numneighbors);
+ if (vertex) {
+ FOREACHneighbor_(vertex) {
+ if (neighbor->visitid == 0) {
+ if (numinf) {
+ numinf= 0;
+ qh_fprintf(fp, 9267, " %d", neighbor->visitid);
+ }
+ }else if (neighbor->visitid < numfacets)
+ qh_fprintf(fp, 9268, " %d", neighbor->visitid);
+ }
+ }
+ qh_fprintf(fp, 9269, "\n");
+ }
+ }
+ if (format == qh_PRINTgeom)
+ qh_fprintf(fp, 9270, "}\n");
+ qh_settempfree(&vertices);
+} /* printvoronoi */
+
+/*---------------------------------
+
+ qh_printvnorm( fp, vertex, vertexA, centers, unbounded )
+ print one separating plane of the Voronoi diagram for a pair of input sites
+ unbounded==True if centers includes vertex-at-infinity
+
+ assumes:
+ qh_ASvoronoi and qh_vertexneighbors() already set
+
+ note:
+ parameter unbounded is UNUSED by this callback
+
+ see:
+ qh_printvdiagram()
+ qh_eachvoronoi()
+*/
+void qh_printvnorm(FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded) {
+ pointT *normal;
+ realT offset;
+ int k;
+ QHULL_UNUSED(unbounded);
+
+ normal= qh_detvnorm(vertex, vertexA, centers, &offset);
+ qh_fprintf(fp, 9271, "%d %d %d ",
+ 2+qh hull_dim, qh_pointid(vertex->point), qh_pointid(vertexA->point));
+ for (k=0; k< qh hull_dim-1; k++)
+ qh_fprintf(fp, 9272, qh_REAL_1, normal[k]);
+ qh_fprintf(fp, 9273, qh_REAL_1, offset);
+ qh_fprintf(fp, 9274, "\n");
+} /* printvnorm */
+
+/*---------------------------------
+
+ qh_printvridge( fp, vertex, vertexA, centers, unbounded )
+ print one ridge of the Voronoi diagram for a pair of input sites
+ unbounded==True if centers includes vertex-at-infinity
+
+ see:
+ qh_printvdiagram()
+
+ notes:
+ the user may use a different function
+ parameter unbounded is UNUSED
+*/
+void qh_printvridge(FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded) {
+ facetT *facet, **facetp;
+ QHULL_UNUSED(unbounded);
+
+ qh_fprintf(fp, 9275, "%d %d %d", qh_setsize(centers)+2,
+ qh_pointid(vertex->point), qh_pointid(vertexA->point));
+ FOREACHfacet_(centers)
+ qh_fprintf(fp, 9276, " %d", facet->visitid);
+ qh_fprintf(fp, 9277, "\n");
+} /* printvridge */
+
+/*---------------------------------
+
+ qh_projectdim3( source, destination )
+ project 2-d 3-d or 4-d point to a 3-d point
+ uses qh.DROPdim and qh.hull_dim
+ source and destination may be the same
+
+ notes:
+ allocate 4 elements to destination just in case
+*/
+void qh_projectdim3 (pointT *source, pointT *destination) {
+ int i,k;
+
+ for (k=0, i=0; k < qh hull_dim; k++) {
+ if (qh hull_dim == 4) {
+ if (k != qh DROPdim)
+ destination[i++]= source[k];
+ }else if (k == qh DROPdim)
+ destination[i++]= 0;
+ else
+ destination[i++]= source[k];
+ }
+ while (i < 3)
+ destination[i++]= 0.0;
+} /* projectdim3 */
+
+/*---------------------------------
+
+ qh_readfeasible( dim, curline )
+ read feasible point from current line and qh.fin
+
+ returns:
+ number of lines read from qh.fin
+ sets qh.FEASIBLEpoint with malloc'd coordinates
+
+ notes:
+ checks for qh.HALFspace
+ assumes dim > 1
+
+ see:
+ qh_setfeasible
+*/
+int qh_readfeasible(int dim, const char *curline) {
+ boolT isfirst= True;
+ int linecount= 0, tokcount= 0;
+ const char *s;
+ char *t, firstline[qh_MAXfirst+1];
+ coordT *coords, value;
+
+ if (!qh HALFspace) {
+ qh_fprintf(qh ferr, 6070, "qhull input error: feasible point(dim 1 coords) is only valid for halfspace intersection\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ if (qh feasible_string)
+ qh_fprintf(qh ferr, 7057, "qhull input warning: feasible point(dim 1 coords) overrides 'Hn,n,n' feasible point for halfspace intersection\n");
+ if (!(qh feasible_point= (coordT*)qh_malloc(dim* sizeof(coordT)))) {
+ qh_fprintf(qh ferr, 6071, "qhull error: insufficient memory for feasible point\n");
+ qh_errexit(qh_ERRmem, NULL, NULL);
+ }
+ coords= qh feasible_point;
+ while ((s= (isfirst ? curline : fgets(firstline, qh_MAXfirst, qh fin)))) {
+ if (isfirst)
+ isfirst= False;
+ else
+ linecount++;
+ while (*s) {
+ while (isspace(*s))
+ s++;
+ value= qh_strtod(s, &t);
+ if (s == t)
+ break;
+ s= t;
+ *(coords++)= value;
+ if (++tokcount == dim) {
+ while (isspace(*s))
+ s++;
+ qh_strtod(s, &t);
+ if (s != t) {
+ qh_fprintf(qh ferr, 6072, "qhull input error: coordinates for feasible point do not finish out the line: %s\n",
+ s);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ return linecount;
+ }
+ }
+ }
+ qh_fprintf(qh ferr, 6073, "qhull input error: only %d coordinates. Could not read %d-d feasible point.\n",
+ tokcount, dim);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ return 0;
+} /* readfeasible */
+
+/*---------------------------------
+
+ qh_readpoints( numpoints, dimension, ismalloc )
+ read points from qh.fin into qh.first_point, qh.num_points
+ qh.fin is lines of coordinates, one per vertex, first line number of points
+ if 'rbox D4',
+ gives message
+ if qh.ATinfinity,
+ adds point-at-infinity for Delaunay triangulations
+
+ returns:
+ number of points, array of point coordinates, dimension, ismalloc True
+ if qh.DELAUNAY & !qh.PROJECTinput, projects points to paraboloid
+ and clears qh.PROJECTdelaunay
+ if qh.HALFspace, reads optional feasible point, reads halfspaces,
+ converts to dual.
+
+ for feasible point in "cdd format" in 3-d:
+ 3 1
+ coordinates
+ comments
+ begin
+ n 4 real/integer
+ ...
+ end
+
+ notes:
+ dimension will change in qh_initqhull_globals if qh.PROJECTinput
+ uses malloc() since qh_mem not initialized
+ FIXUP QH11012: qh_readpoints needs rewriting, too long
+*/
+coordT *qh_readpoints(int *numpoints, int *dimension, boolT *ismalloc) {
+ coordT *points, *coords, *infinity= NULL;
+ realT paraboloid, maxboloid= -REALmax, value;
+ realT *coordp= NULL, *offsetp= NULL, *normalp= NULL;
+ char *s= 0, *t, firstline[qh_MAXfirst+1];
+ int diminput=0, numinput=0, dimfeasible= 0, newnum, k, tempi;
+ int firsttext=0, firstshort=0, firstlong=0, firstpoint=0;
+ int tokcount= 0, linecount=0, maxcount, coordcount=0;
+ boolT islong, isfirst= True, wasbegin= False;
+ boolT isdelaunay= qh DELAUNAY && !qh PROJECTinput;
+
+ if (qh CDDinput) {
+ while ((s= fgets(firstline, qh_MAXfirst, qh fin))) {
+ linecount++;
+ if (qh HALFspace && linecount == 1 && isdigit(*s)) {
+ dimfeasible= qh_strtol(s, &s);
+ while (isspace(*s))
+ s++;
+ if (qh_strtol(s, &s) == 1)
+ linecount += qh_readfeasible(dimfeasible, s);
+ else
+ dimfeasible= 0;
+ }else if (!memcmp(firstline, "begin", (size_t)5) || !memcmp(firstline, "BEGIN", (size_t)5))
+ break;
+ else if (!*qh rbox_command)
+ strncat(qh rbox_command, s, sizeof(qh rbox_command)-1);
+ }
+ if (!s) {
+ qh_fprintf(qh ferr, 6074, "qhull input error: missing \"begin\" for cdd-formated input\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ }
+ while (!numinput && (s= fgets(firstline, qh_MAXfirst, qh fin))) {
+ linecount++;
+ if (!memcmp(s, "begin", (size_t)5) || !memcmp(s, "BEGIN", (size_t)5))
+ wasbegin= True;
+ while (*s) {
+ while (isspace(*s))
+ s++;
+ if (!*s)
+ break;
+ if (!isdigit(*s)) {
+ if (!*qh rbox_command) {
+ strncat(qh rbox_command, s, sizeof(qh rbox_command)-1);
+ firsttext= linecount;
+ }
+ break;
+ }
+ if (!diminput)
+ diminput= qh_strtol(s, &s);
+ else {
+ numinput= qh_strtol(s, &s);
+ if (numinput == 1 && diminput >= 2 && qh HALFspace && !qh CDDinput) {
+ linecount += qh_readfeasible(diminput, s); /* checks if ok */
+ dimfeasible= diminput;
+ diminput= numinput= 0;
+ }else
+ break;
+ }
+ }
+ }
+ if (!s) {
+ qh_fprintf(qh ferr, 6075, "qhull input error: short input file. Did not find dimension and number of points\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ if (diminput > numinput) {
+ tempi= diminput; /* exchange dim and n, e.g., for cdd input format */
+ diminput= numinput;
+ numinput= tempi;
+ }
+ if (diminput < 2) {
+ qh_fprintf(qh ferr, 6220,"qhull input error: dimension %d(first number) should be at least 2\n",
+ diminput);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ if (isdelaunay) {
+ qh PROJECTdelaunay= False;
+ if (qh CDDinput)
+ *dimension= diminput;
+ else
+ *dimension= diminput+1;
+ *numpoints= numinput;
+ if (qh ATinfinity)
+ (*numpoints)++;
+ }else if (qh HALFspace) {
+ *dimension= diminput - 1;
+ *numpoints= numinput;
+ if (diminput < 3) {
+ qh_fprintf(qh ferr, 6221,"qhull input error: dimension %d(first number, includes offset) should be at least 3 for halfspaces\n",
+ diminput);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ if (dimfeasible) {
+ if (dimfeasible != *dimension) {
+ qh_fprintf(qh ferr, 6222,"qhull input error: dimension %d of feasible point is not one less than dimension %d for halfspaces\n",
+ dimfeasible, diminput);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ }else
+ qh_setfeasible(*dimension);
+ }else {
+ if (qh CDDinput)
+ *dimension= diminput-1;
+ else
+ *dimension= diminput;
+ *numpoints= numinput;
+ }
+ qh normal_size= *dimension * sizeof(coordT); /* for tracing with qh_printpoint */
+ if (qh HALFspace) {
+ qh half_space= coordp= (coordT*)qh_malloc(qh normal_size + sizeof(coordT));
+ if (qh CDDinput) {
+ offsetp= qh half_space;
+ normalp= offsetp + 1;
+ }else {
+ normalp= qh half_space;
+ offsetp= normalp + *dimension;
+ }
+ }
+ qh maxline= diminput * (qh_REALdigits + 5);
+ maximize_(qh maxline, 500);
+ qh line= (char*)qh_malloc((qh maxline+1) * sizeof(char));
+ *ismalloc= True; /* use malloc since memory not setup */
+ coords= points= qh temp_malloc=
+ (coordT*)qh_malloc((*numpoints)*(*dimension)*sizeof(coordT));
+ if (!coords || !qh line || (qh HALFspace && !qh half_space)) {
+ qh_fprintf(qh ferr, 6076, "qhull error: insufficient memory to read %d points\n",
+ numinput);
+ qh_errexit(qh_ERRmem, NULL, NULL);
+ }
+ if (isdelaunay && qh ATinfinity) {
+ infinity= points + numinput * (*dimension);
+ for (k= (*dimension) - 1; k--; )
+ infinity[k]= 0.0;
+ }
+ maxcount= numinput * diminput;
+ paraboloid= 0.0;
+ while ((s= (isfirst ? s : fgets(qh line, qh maxline, qh fin)))) {
+ if (!isfirst) {
+ linecount++;
+ if (*s == 'e' || *s == 'E') {
+ if (!memcmp(s, "end", (size_t)3) || !memcmp(s, "END", (size_t)3)) {
+ if (qh CDDinput )
+ break;
+ else if (wasbegin)
+ qh_fprintf(qh ferr, 7058, "qhull input warning: the input appears to be in cdd format. If so, use 'Fd'\n");
+ }
+ }
+ }
+ islong= False;
+ while (*s) {
+ while (isspace(*s))
+ s++;
+ value= qh_strtod(s, &t);
+ if (s == t) {
+ if (!*qh rbox_command)
+ strncat(qh rbox_command, s, sizeof(qh rbox_command)-1);
+ if (*s && !firsttext)
+ firsttext= linecount;
+ if (!islong && !firstshort && coordcount)
+ firstshort= linecount;
+ break;
+ }
+ if (!firstpoint)
+ firstpoint= linecount;
+ s= t;
+ if (++tokcount > maxcount)
+ continue;
+ if (qh HALFspace) {
+ if (qh CDDinput)
+ *(coordp++)= -value; /* both coefficients and offset */
+ else
+ *(coordp++)= value;
+ }else {
+ *(coords++)= value;
+ if (qh CDDinput && !coordcount) {
+ if (value != 1.0) {
+ qh_fprintf(qh ferr, 6077, "qhull input error: for cdd format, point at line %d does not start with '1'\n",
+ linecount);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ coords--;
+ }else if (isdelaunay) {
+ paraboloid += value * value;
+ if (qh ATinfinity) {
+ if (qh CDDinput)
+ infinity[coordcount-1] += value;
+ else
+ infinity[coordcount] += value;
+ }
+ }
+ }
+ if (++coordcount == diminput) {
+ coordcount= 0;
+ if (isdelaunay) {
+ *(coords++)= paraboloid;
+ maximize_(maxboloid, paraboloid);
+ paraboloid= 0.0;
+ }else if (qh HALFspace) {
+ if (!qh_sethalfspace(*dimension, coords, &coords, normalp, offsetp, qh feasible_point)) {
+ qh_fprintf(qh ferr, 8048, "The halfspace was on line %d\n", linecount);
+ if (wasbegin)
+ qh_fprintf(qh ferr, 8049, "The input appears to be in cdd format. If so, you should use option 'Fd'\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ coordp= qh half_space;
+ }
+ while (isspace(*s))
+ s++;
+ if (*s) {
+ islong= True;
+ if (!firstlong)
+ firstlong= linecount;
+ }
+ }
+ }
+ if (!islong && !firstshort && coordcount)
+ firstshort= linecount;
+ if (!isfirst && s - qh line >= qh maxline) {
+ qh_fprintf(qh ferr, 6078, "qhull input error: line %d contained more than %d characters\n",
+ linecount, (int) (s - qh line)); /* WARN64 */
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ isfirst= False;
+ }
+ if (tokcount != maxcount) {
+ newnum= fmin_(numinput, tokcount/diminput);
+ qh_fprintf(qh ferr, 7073,"\
+qhull warning: instead of %d %d-dimensional points, input contains\n\
+%d points and %d extra coordinates. Line %d is the first\npoint",
+ numinput, diminput, tokcount/diminput, tokcount % diminput, firstpoint);
+ if (firsttext)
+ qh_fprintf(qh ferr, 8051, ", line %d is the first comment", firsttext);
+ if (firstshort)
+ qh_fprintf(qh ferr, 8052, ", line %d is the first short\nline", firstshort);
+ if (firstlong)
+ qh_fprintf(qh ferr, 8053, ", line %d is the first long line", firstlong);
+ qh_fprintf(qh ferr, 8054, ". Continue with %d points.\n", newnum);
+ numinput= newnum;
+ if (isdelaunay && qh ATinfinity) {
+ for (k= tokcount % diminput; k--; )
+ infinity[k] -= *(--coords);
+ *numpoints= newnum+1;
+ }else {
+ coords -= tokcount % diminput;
+ *numpoints= newnum;
+ }
+ }
+ if (isdelaunay && qh ATinfinity) {
+ for (k= (*dimension) -1; k--; )
+ infinity[k] /= numinput;
+ if (coords == infinity)
+ coords += (*dimension) -1;
+ else {
+ for (k=0; k < (*dimension) -1; k++)
+ *(coords++)= infinity[k];
+ }
+ *(coords++)= maxboloid * 1.1;
+ }
+ if (qh rbox_command[0]) {
+ qh rbox_command[strlen(qh rbox_command)-1]= '\0';
+ if (!strcmp(qh rbox_command, "./rbox D4"))
+ qh_fprintf(qh ferr, 8055, "\n\
+This is the qhull test case. If any errors or core dumps occur,\n\
+recompile qhull with 'make new'. If errors still occur, there is\n\
+an incompatibility. You should try a different compiler. You can also\n\
+change the choices in user.h. If you discover the source of the problem,\n\
+please send mail to qhull_bug@qhull.org.\n\
+\n\
+Type 'qhull' for a short list of options.\n");
+ }
+ qh_free(qh line);
+ qh line= NULL;
+ if (qh half_space) {
+ qh_free(qh half_space);
+ qh half_space= NULL;
+ }
+ qh temp_malloc= NULL;
+ trace1((qh ferr, 1008,"qh_readpoints: read in %d %d-dimensional points\n",
+ numinput, diminput));
+ return(points);
+} /* readpoints */
+
+
+/*---------------------------------
+
+ qh_setfeasible( dim )
+ set qh.FEASIBLEpoint from qh.feasible_string in "n,n,n" or "n n n" format
+
+ notes:
+ "n,n,n" already checked by qh_initflags()
+ see qh_readfeasible()
+*/
+void qh_setfeasible(int dim) {
+ int tokcount= 0;
+ char *s;
+ coordT *coords, value;
+
+ if (!(s= qh feasible_string)) {
+ qh_fprintf(qh ferr, 6223, "\
+qhull input error: halfspace intersection needs a feasible point.\n\
+Either prepend the input with 1 point or use 'Hn,n,n'. See manual.\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ if (!(qh feasible_point= (pointT*)qh_malloc(dim * sizeof(coordT)))) {
+ qh_fprintf(qh ferr, 6079, "qhull error: insufficient memory for 'Hn,n,n'\n");
+ qh_errexit(qh_ERRmem, NULL, NULL);
+ }
+ coords= qh feasible_point;
+ while (*s) {
+ value= qh_strtod(s, &s);
+ if (++tokcount > dim) {
+ qh_fprintf(qh ferr, 7059, "qhull input warning: more coordinates for 'H%s' than dimension %d\n",
+ qh feasible_string, dim);
+ break;
+ }
+ *(coords++)= value;
+ if (*s)
+ s++;
+ }
+ while (++tokcount <= dim)
+ *(coords++)= 0.0;
+} /* setfeasible */
+
+/*---------------------------------
+
+ qh_skipfacet( facet )
+ returns 'True' if this facet is not to be printed
+
+ notes:
+ based on the user provided slice thresholds and 'good' specifications
+*/
+boolT qh_skipfacet(facetT *facet) {
+ facetT *neighbor, **neighborp;
+
+ if (qh PRINTneighbors) {
+ if (facet->good)
+ return !qh PRINTgood;
+ FOREACHneighbor_(facet) {
+ if (neighbor->good)
+ return False;
+ }
+ return True;
+ }else if (qh PRINTgood)
+ return !facet->good;
+ else if (!facet->normal)
+ return True;
+ return(!qh_inthresholds(facet->normal, NULL));
+} /* skipfacet */
+
+/*---------------------------------
+
+ qh_skipfilename( string )
+ returns pointer to character after filename
+
+ notes:
+ skips leading spaces
+ ends with spacing or eol
+ if starts with ' or " ends with the same, skipping \' or \"
+ For qhull, qh_argv_to_command() only uses double quotes
+*/
+char *qh_skipfilename(char *filename) {
+ char *s= filename; /* non-const due to return */
+ char c;
+
+ while (*s && isspace(*s))
+ s++;
+ c= *s++;
+ if (c == '\0') {
+ qh_fprintf(qh ferr, 6204, "qhull input error: filename expected, none found.\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ if (c == '\'' || c == '"') {
+ while (*s !=c || s[-1] == '\\') {
+ if (!*s) {
+ qh_fprintf(qh ferr, 6203, "qhull input error: missing quote after filename -- %s\n", filename);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ s++;
+ }
+ s++;
+ }
+ else while (*s && !isspace(*s))
+ s++;
+ return s;
+} /* skipfilename */
+
diff --git a/src/libqhullr/io_r.h b/src/libqhullr/io_r.h
new file mode 100644
index 0000000..7549952
--- /dev/null
+++ b/src/libqhullr/io_r.h
@@ -0,0 +1,159 @@
+/*
---------------------------------
+
+ io.h
+ declarations of Input/Output functions
+
+ see README, libqhull.h and io.c
+
+ Copyright (c) 1993-2012 The Geometry Center.
+ $Id: //main/2011/qhull/src/libqhullr/io_r.h#1 $$Change: 1640 $
+ $DateTime: 2014/01/15 09:12:08 $$Author: bbarber $
+*/
+
+#ifndef qhDEFio
+#define qhDEFio 1
+
+#include "libqhull.h"
+
+/*============ constants and flags ==================*/
+
+/*----------------------------------
+
+ qh_MAXfirst
+ maximum length of first two lines of stdin
+*/
+#define qh_MAXfirst 200
+
+/*----------------------------------
+
+ qh_MINradius
+ min radius for Gp and Gv, fraction of maxcoord
+*/
+#define qh_MINradius 0.02
+
+/*----------------------------------
+
+ qh_GEOMepsilon
+ adjust outer planes for 'lines closer' and geomview roundoff.
+ This prevents bleed through.
+*/
+#define qh_GEOMepsilon 2e-3
+
+/*----------------------------------
+
+ qh_WHITESPACE
+ possible values of white space
+*/
+#define qh_WHITESPACE " \n\t\v\r\f"
+
+
+/*----------------------------------
+
+ qh_RIDGE
+ to select which ridges to print in qh_eachvoronoi
+*/
+typedef enum
+{
+ qh_RIDGEall = 0, qh_RIDGEinner, qh_RIDGEouter
+}
+qh_RIDGE;
+
+/*----------------------------------
+
+ printvridgeT
+ prints results of qh_printvdiagram
+
+ see:
+ qh_printvridge for an example
+*/
+typedef void (*printvridgeT)(FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded);
+
+/*============== -prototypes in alphabetical order =========*/
+
+void dfacet(unsigned id);
+void dvertex(unsigned id);
+int qh_compare_facetarea(const void *p1, const void *p2);
+int qh_compare_facetmerge(const void *p1, const void *p2);
+int qh_compare_facetvisit(const void *p1, const void *p2);
+int qh_compare_vertexpoint(const void *p1, const void *p2); /* not used */
+void qh_copyfilename(char *filename, int size, const char* source, int length);
+void qh_countfacets(facetT *facetlist, setT *facets, boolT printall,
+ int *numfacetsp, int *numsimplicialp, int *totneighborsp,
+ int *numridgesp, int *numcoplanarsp, int *numnumtricoplanarsp);
+pointT *qh_detvnorm(vertexT *vertex, vertexT *vertexA, setT *centers, realT *offsetp);
+setT *qh_detvridge(vertexT *vertex);
+setT *qh_detvridge3 (vertexT *atvertex, vertexT *vertex);
+int qh_eachvoronoi(FILE *fp, printvridgeT printvridge, vertexT *atvertex, boolT visitall, qh_RIDGE innerouter, boolT inorder);
+int qh_eachvoronoi_all(FILE *fp, printvridgeT printvridge, boolT isUpper, qh_RIDGE innerouter, boolT inorder);
+void qh_facet2point(facetT *facet, pointT **point0, pointT **point1, realT *mindist);
+setT *qh_facetvertices(facetT *facetlist, setT *facets, boolT allfacets);
+void qh_geomplanes(facetT *facet, realT *outerplane, realT *innerplane);
+void qh_markkeep(facetT *facetlist);
+setT *qh_markvoronoi(facetT *facetlist, setT *facets, boolT printall, boolT *isLowerp, int *numcentersp);
+void qh_order_vertexneighbors(vertexT *vertex);
+void qh_prepare_output(void);
+void qh_printafacet(FILE *fp, qh_PRINT format, facetT *facet, boolT printall);
+void qh_printbegin(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall);
+void qh_printcenter(FILE *fp, qh_PRINT format, const char *string, facetT *facet);
+void qh_printcentrum(FILE *fp, facetT *facet, realT radius);
+void qh_printend(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall);
+void qh_printend4geom(FILE *fp, facetT *facet, int *num, boolT printall);
+void qh_printextremes(FILE *fp, facetT *facetlist, setT *facets, boolT printall);
+void qh_printextremes_2d(FILE *fp, facetT *facetlist, setT *facets, boolT printall);
+void qh_printextremes_d(FILE *fp, facetT *facetlist, setT *facets, boolT printall);
+void qh_printfacet(FILE *fp, facetT *facet);
+void qh_printfacet2math(FILE *fp, facetT *facet, qh_PRINT format, int notfirst);
+void qh_printfacet2geom(FILE *fp, facetT *facet, realT color[3]);
+void qh_printfacet2geom_points(FILE *fp, pointT *point1, pointT *point2,
+ facetT *facet, realT offset, realT color[3]);
+void qh_printfacet3math(FILE *fp, facetT *facet, qh_PRINT format, int notfirst);
+void qh_printfacet3geom_nonsimplicial(FILE *fp, facetT *facet, realT color[3]);
+void qh_printfacet3geom_points(FILE *fp, setT *points, facetT *facet, realT offset, realT color[3]);
+void qh_printfacet3geom_simplicial(FILE *fp, facetT *facet, realT color[3]);
+void qh_printfacet3vertex(FILE *fp, facetT *facet, qh_PRINT format);
+void qh_printfacet4geom_nonsimplicial(FILE *fp, facetT *facet, realT color[3]);
+void qh_printfacet4geom_simplicial(FILE *fp, facetT *facet, realT color[3]);
+void qh_printfacetNvertex_nonsimplicial(FILE *fp, facetT *facet, int id, qh_PRINT format);
+void qh_printfacetNvertex_simplicial(FILE *fp, facetT *facet, qh_PRINT format);
+void qh_printfacetheader(FILE *fp, facetT *facet);
+void qh_printfacetridges(FILE *fp, facetT *facet);
+void qh_printfacets(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall);
+void qh_printhyperplaneintersection(FILE *fp, facetT *facet1, facetT *facet2,
+ setT *vertices, realT color[3]);
+void qh_printneighborhood(FILE *fp, qh_PRINT format, facetT *facetA, facetT *facetB, boolT printall);
+void qh_printline3geom(FILE *fp, pointT *pointA, pointT *pointB, realT color[3]);
+void qh_printpoint(FILE *fp, const char *string, pointT *point);
+void qh_printpointid(FILE *fp, const char *string, int dim, pointT *point, int id);
+void qh_printpoint3 (FILE *fp, pointT *point);
+void qh_printpoints_out(FILE *fp, facetT *facetlist, setT *facets, boolT printall);
+void qh_printpointvect(FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius, realT color[3]);
+void qh_printpointvect2 (FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius);
+void qh_printridge(FILE *fp, ridgeT *ridge);
+void qh_printspheres(FILE *fp, setT *vertices, realT radius);
+void qh_printvdiagram(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall);
+int qh_printvdiagram2 (FILE *fp, printvridgeT printvridge, setT *vertices, qh_RIDGE innerouter, boolT inorder);
+void qh_printvertex(FILE *fp, vertexT *vertex);
+void qh_printvertexlist(FILE *fp, const char* string, facetT *facetlist,
+ setT *facets, boolT printall);
+void qh_printvertices(FILE *fp, const char* string, setT *vertices);
+void qh_printvneighbors(FILE *fp, facetT* facetlist, setT *facets, boolT printall);
+void qh_printvoronoi(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall);
+void qh_printvnorm(FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded);
+void qh_printvridge(FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded);
+void qh_produce_output(void);
+void qh_produce_output2(void);
+void qh_projectdim3 (pointT *source, pointT *destination);
+int qh_readfeasible(int dim, const char *curline);
+coordT *qh_readpoints(int *numpoints, int *dimension, boolT *ismalloc);
+void qh_setfeasible(int dim);
+boolT qh_skipfacet(facetT *facet);
+char *qh_skipfilename(char *filename);
+
+#endif /* qhDEFio */
diff --git a/src/libqhullr/libqhull_r.c b/src/libqhullr/libqhull_r.c
new file mode 100644
index 0000000..2778f9c
--- /dev/null
+++ b/src/libqhullr/libqhull_r.c
@@ -0,0 +1,1401 @@
+/*
---------------------------------
+
+ libqhull.c
+ Quickhull algorithm for convex hulls
+
+ qhull() and top-level routines
+
+ see qh-qhull.htm, libqhull.h, unix.c
+
+ see qhull_a.h for internal functions
+
+ Copyright (c) 1993-2012 The Geometry Center.
+ $Id: //main/2011/qhull/src/libqhullr/libqhull_r.c#1 $$Change: 1640 $
+ $DateTime: 2014/01/15 09:12:08 $$Author: bbarber $
+*/
+
+#include "qhull_a.h"
+
+/*============= functions in alphabetic order after qhull() =======*/
+
+/*---------------------------------
+
+ qh_qhull()
+ compute DIM3 convex hull of qh.num_points starting at qh.first_point
+ qh contains all global options and variables
+
+ returns:
+ returns polyhedron
+ qh.facet_list, qh.num_facets, qh.vertex_list, qh.num_vertices,
+
+ returns global variables
+ qh.hulltime, qh.max_outside, qh.interior_point, qh.max_vertex, qh.min_vertex
+
+ returns precision constants
+ qh.ANGLEround, centrum_radius, cos_max, DISTround, MAXabs_coord, ONEmerge
+
+ notes:
+ unless needed for output
+ qh.max_vertex and qh.min_vertex are max/min due to merges
+
+ see:
+ to add individual points to either qh.num_points
+ use qh_addpoint()
+
+ if qh.GETarea
+ qh_produceoutput() returns qh.totarea and qh.totvol via qh_getarea()
+
+ design:
+ record starting time
+ initialize hull and partition points
+ build convex hull
+ unless early termination
+ update facet->maxoutside for vertices, coplanar, and near-inside points
+ error if temporary sets exist
+ record end time
+*/
+
+void qh_qhull(void) {
+ int numoutside;
+
+ qh hulltime= qh_CPUclock;
+ if (qh RERUN || qh JOGGLEmax < REALmax/2)
+ qh_build_withrestart();
+ else {
+ qh_initbuild();
+ qh_buildhull();
+ }
+ if (!qh STOPpoint && !qh STOPcone) {
+ if (qh ZEROall_ok && !qh TESTvneighbors && qh MERGEexact)
+ qh_checkzero( qh_ALL);
+ if (qh ZEROall_ok && !qh TESTvneighbors && !qh WAScoplanar) {
+ trace2((qh ferr, 2055, "qh_qhull: all facets are clearly convex and no coplanar points. Post-merging and check of maxout not needed.\n"));
+ qh DOcheckmax= False;
+ }else {
+ if (qh MERGEexact || (qh hull_dim > qh_DIMreduceBuild && qh PREmerge))
+ qh_postmerge("First post-merge", qh premerge_centrum, qh premerge_cos,
+ (qh POSTmerge ? False : qh TESTvneighbors));
+ else if (!qh POSTmerge && qh TESTvneighbors)
+ qh_postmerge("For testing vertex neighbors", qh premerge_centrum,
+ qh premerge_cos, True);
+ if (qh POSTmerge)
+ qh_postmerge("For post-merging", qh postmerge_centrum,
+ qh postmerge_cos, qh TESTvneighbors);
+ if (qh visible_list == qh facet_list) { /* i.e., merging done */
+ qh findbestnew= True;
+ qh_partitionvisible(/*visible_list, newfacet_list*/ !qh_ALL, &numoutside);
+ qh findbestnew= False;
+ qh_deletevisible(/*qh visible_list*/);
+ qh_resetlists(False, qh_RESETvisible /*qh visible_list newvertex_list newfacet_list */);
+ }
+ }
+ if (qh DOcheckmax){
+ if (qh REPORTfreq) {
+ qh_buildtracing(NULL, NULL);
+ qh_fprintf(qh ferr, 8115, "\nTesting all coplanar points.\n");
+ }
+ qh_check_maxout();
+ }
+ if (qh KEEPnearinside && !qh maxoutdone)
+ qh_nearcoplanar();
+ }
+ if (qh_setsize(qhmem.tempstack) != 0) {
+ qh_fprintf(qh ferr, 6164, "qhull internal error (qh_qhull): temporary sets not empty(%d)\n",
+ qh_setsize(qhmem.tempstack));
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ qh hulltime= qh_CPUclock - qh hulltime;
+ qh QHULLfinished= True;
+ trace1((qh ferr, 1036, "Qhull: algorithm completed\n"));
+} /* qhull */
+
+/*---------------------------------
+
+ qh_addpoint( furthest, facet, checkdist )
+ add point (usually furthest point) above facet to hull
+ if checkdist,
+ check that point is above facet.
+ if point is not outside of the hull, uses qh_partitioncoplanar()
+ assumes that facet is defined by qh_findbestfacet()
+ else if facet specified,
+ assumes that point is above facet (major damage if below)
+ for Delaunay triangulations,
+ Use qh_setdelaunay() to lift point to paraboloid and scale by 'Qbb' if needed
+ Do not use options 'Qbk', 'QBk', or 'QbB' since they scale the coordinates.
+
+ returns:
+ returns False if user requested an early termination
+ qh.visible_list, newfacet_list, delvertex_list, NEWfacets may be defined
+ updates qh.facet_list, qh.num_facets, qh.vertex_list, qh.num_vertices
+ clear qh.maxoutdone (will need to call qh_check_maxout() for facet->maxoutside)
+ if unknown point, adds a pointer to qh.other_points
+ do not deallocate the point's coordinates
+
+ notes:
+ assumes point is near its best facet and not at a local minimum of a lens
+ distributions. Use qh_findbestfacet to avoid this case.
+ uses qh.visible_list, qh.newfacet_list, qh.delvertex_list, qh.NEWfacets
+
+ see also:
+ qh_triangulate() -- triangulate non-simplicial facets
+
+ design:
+ add point to other_points if needed
+ if checkdist
+ if point not above facet
+ partition coplanar point
+ exit
+ exit if pre STOPpoint requested
+ find horizon and visible facets for point
+ make new facets for point to horizon
+ make hyperplanes for point
+ compute balance statistics
+ match neighboring new facets
+ update vertex neighbors and delete interior vertices
+ exit if STOPcone requested
+ merge non-convex new facets
+ if merge found, many merges, or 'Qf'
+ use qh_findbestnew() instead of qh_findbest()
+ partition outside points from visible facets
+ delete visible facets
+ check polyhedron if requested
+ exit if post STOPpoint requested
+ reset working lists of facets and vertices
+*/
+boolT qh_addpoint(pointT *furthest, facetT *facet, boolT checkdist) {
+ int goodvisible, goodhorizon;
+ vertexT *vertex;
+ facetT *newfacet;
+ realT dist, newbalance, pbalance;
+ boolT isoutside= False;
+ int numpart, numpoints, numnew, firstnew;
+
+ qh maxoutdone= False;
+ if (qh_pointid(furthest) == -1)
+ qh_setappend(&qh other_points, furthest);
+ if (!facet) {
+ qh_fprintf(qh ferr, 6213, "qhull internal error (qh_addpoint): NULL facet. Need to call qh_findbestfacet first\n");
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ if (checkdist) {
+ facet= qh_findbest(furthest, facet, !qh_ALL, !qh_ISnewfacets, !qh_NOupper,
+ &dist, &isoutside, &numpart);
+ zzadd_(Zpartition, numpart);
+ if (!isoutside) {
+ zinc_(Znotmax); /* last point of outsideset is no longer furthest. */
+ facet->notfurthest= True;
+ qh_partitioncoplanar(furthest, facet, &dist);
+ return True;
+ }
+ }
+ qh_buildtracing(furthest, facet);
+ if (qh STOPpoint < 0 && qh furthest_id == -qh STOPpoint-1) {
+ facet->notfurthest= True;
+ return False;
+ }
+ qh_findhorizon(furthest, facet, &goodvisible, &goodhorizon);
+ if (qh ONLYgood && !(goodvisible+goodhorizon) && !qh GOODclosest) {
+ zinc_(Znotgood);
+ facet->notfurthest= True;
+ /* last point of outsideset is no longer furthest. This is ok
+ since all points of the outside are likely to be bad */
+ qh_resetlists(False, qh_RESETvisible /*qh visible_list newvertex_list newfacet_list */);
+ return True;
+ }
+ zzinc_(Zprocessed);
+ firstnew= qh facet_id;
+ vertex= qh_makenewfacets(furthest /*visible_list, attaches if !ONLYgood */);
+ qh_makenewplanes(/* newfacet_list */);
+ numnew= qh facet_id - firstnew;
+ newbalance= numnew - (realT) (qh num_facets-qh num_visible)
+ * qh hull_dim/qh num_vertices;
+ wadd_(Wnewbalance, newbalance);
+ wadd_(Wnewbalance2, newbalance * newbalance);
+ if (qh ONLYgood
+ && !qh_findgood(qh newfacet_list, goodhorizon) && !qh GOODclosest) {
+ FORALLnew_facets
+ qh_delfacet(newfacet);
+ qh_delvertex(vertex);
+ qh_resetlists(True, qh_RESETvisible /*qh visible_list newvertex_list newfacet_list */);
+ zinc_(Znotgoodnew);
+ facet->notfurthest= True;
+ return True;
+ }
+ if (qh ONLYgood)
+ qh_attachnewfacets(/*visible_list*/);
+ qh_matchnewfacets();
+ qh_updatevertices();
+ if (qh STOPcone && qh furthest_id == qh STOPcone-1) {
+ facet->notfurthest= True;
+ return False; /* visible_list etc. still defined */
+ }
+ qh findbestnew= False;
+ if (qh PREmerge || qh MERGEexact) {
+ qh_premerge(vertex, qh premerge_centrum, qh premerge_cos);
+ if (qh_USEfindbestnew)
+ qh findbestnew= True;
+ else {
+ FORALLnew_facets {
+ if (!newfacet->simplicial) {
+ qh findbestnew= True; /* use qh_findbestnew instead of qh_findbest*/
+ break;
+ }
+ }
+ }
+ }else if (qh BESToutside)
+ qh findbestnew= True;
+ qh_partitionvisible(/*visible_list, newfacet_list*/ !qh_ALL, &numpoints);
+ qh findbestnew= False;
+ qh findbest_notsharp= False;
+ zinc_(Zpbalance);
+ pbalance= numpoints - (realT) qh hull_dim /* assumes all points extreme */
+ * (qh num_points - qh num_vertices)/qh num_vertices;
+ wadd_(Wpbalance, pbalance);
+ wadd_(Wpbalance2, pbalance * pbalance);
+ qh_deletevisible(/*qh visible_list*/);
+ zmax_(Zmaxvertex, qh num_vertices);
+ qh NEWfacets= False;
+ if (qh IStracing >= 4) {
+ if (qh num_facets < 2000)
+ qh_printlists();
+ qh_printfacetlist(qh newfacet_list, NULL, True);
+ qh_checkpolygon(qh facet_list);
+ }else if (qh CHECKfrequently) {
+ if (qh num_facets < 50)
+ qh_checkpolygon(qh facet_list);
+ else
+ qh_checkpolygon(qh newfacet_list);
+ }
+ if (qh STOPpoint > 0 && qh furthest_id == qh STOPpoint-1)
+ return False;
+ qh_resetlists(True, qh_RESETvisible /*qh visible_list newvertex_list newfacet_list */);
+ /* qh_triangulate(); to test qh.TRInormals */
+ trace2((qh ferr, 2056, "qh_addpoint: added p%d new facets %d new balance %2.2g point balance %2.2g\n",
+ qh_pointid(furthest), numnew, newbalance, pbalance));
+ return True;
+} /* addpoint */
+
+/*---------------------------------
+
+ qh_build_withrestart()
+ allow restarts due to qh.JOGGLEmax while calling qh_buildhull()
+ qh.FIRSTpoint/qh.NUMpoints is point array
+ it may be moved by qh_joggleinput()
+*/
+void qh_build_withrestart(void) {
+ int restart;
+
+ qh ALLOWrestart= True;
+ while (True) {
+ restart= setjmp(qh restartexit); /* simple statement for CRAY J916 */
+ if (restart) { /* only from qh_precision() */
+ zzinc_(Zretry);
+ wmax_(Wretrymax, qh JOGGLEmax);
+ /* QH7078 warns about using 'TCn' with 'QJn' */
+ qh STOPcone= -1; /* if break from joggle, prevents normal output */
+ }
+ if (!qh RERUN && qh JOGGLEmax < REALmax/2) {
+ if (qh build_cnt > qh_JOGGLEmaxretry) {
+ qh_fprintf(qh ferr, 6229, "qhull precision error: %d attempts to construct a convex hull\n\
+ with joggled input. Increase joggle above 'QJ%2.2g'\n\
+ or modify qh_JOGGLE... parameters in user.h\n",
+ qh build_cnt, qh JOGGLEmax);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ if (qh build_cnt && !restart)
+ break;
+ }else if (qh build_cnt && qh build_cnt >= qh RERUN)
+ break;
+ qh STOPcone= 0;
+ qh_freebuild(True); /* first call is a nop */
+ qh build_cnt++;
+ if (!qh qhull_optionsiz)
+ qh qhull_optionsiz= (int)strlen(qh qhull_options); /* WARN64 */
+ else {
+ qh qhull_options [qh qhull_optionsiz]= '\0';
+ qh qhull_optionlen= qh_OPTIONline; /* starts a new line */
+ }
+ qh_option("_run", &qh build_cnt, NULL);
+ if (qh build_cnt == qh RERUN) {
+ qh IStracing= qh TRACElastrun; /* duplicated from qh_initqhull_globals */
+ if (qh TRACEpoint != -1 || qh TRACEdist < REALmax/2 || qh TRACEmerge) {
+ qh TRACElevel= (qh IStracing? qh IStracing : 3);
+ qh IStracing= 0;
+ }
+ qhmem.IStracing= qh IStracing;
+ }
+ if (qh JOGGLEmax < REALmax/2)
+ qh_joggleinput();
+ qh_initbuild();
+ qh_buildhull();
+ if (qh JOGGLEmax < REALmax/2 && !qh MERGING)
+ qh_checkconvex(qh facet_list, qh_ALGORITHMfault);
+ }
+ qh ALLOWrestart= False;
+} /* qh_build_withrestart */
+
+/*---------------------------------
+
+ qh_buildhull()
+ construct a convex hull by adding outside points one at a time
+
+ returns:
+
+ notes:
+ may be called multiple times
+ checks facet and vertex lists for incorrect flags
+ to recover from STOPcone, call qh_deletevisible and qh_resetlists
+
+ design:
+ check visible facet and newfacet flags
+ check newlist vertex flags and qh.STOPcone/STOPpoint
+ for each facet with a furthest outside point
+ add point to facet
+ exit if qh.STOPcone or qh.STOPpoint requested
+ if qh.NARROWhull for initial simplex
+ partition remaining outside points to coplanar sets
+*/
+void qh_buildhull(void) {
+ facetT *facet;
+ pointT *furthest;
+ vertexT *vertex;
+ int id;
+
+ trace1((qh ferr, 1037, "qh_buildhull: start build hull\n"));
+ FORALLfacets {
+ if (facet->visible || facet->newfacet) {
+ qh_fprintf(qh ferr, 6165, "qhull internal error (qh_buildhull): visible or new facet f%d in facet list\n",
+ facet->id);
+ qh_errexit(qh_ERRqhull, facet, NULL);
+ }
+ }
+ FORALLvertices {
+ if (vertex->newlist) {
+ qh_fprintf(qh ferr, 6166, "qhull internal error (qh_buildhull): new vertex f%d in vertex list\n",
+ vertex->id);
+ qh_errprint("ERRONEOUS", NULL, NULL, NULL, vertex);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ id= qh_pointid(vertex->point);
+ if ((qh STOPpoint>0 && id == qh STOPpoint-1) ||
+ (qh STOPpoint<0 && id == -qh STOPpoint-1) ||
+ (qh STOPcone>0 && id == qh STOPcone-1)) {
+ trace1((qh ferr, 1038,"qh_buildhull: stop point or cone P%d in initial hull\n", id));
+ return;
+ }
+ }
+ qh facet_next= qh facet_list; /* advance facet when processed */
+ while ((furthest= qh_nextfurthest(&facet))) {
+ qh num_outside--; /* if ONLYmax, furthest may not be outside */
+ if (!qh_addpoint(furthest, facet, qh ONLYmax))
+ break;
+ }
+ if (qh NARROWhull) /* move points from outsideset to coplanarset */
+ qh_outcoplanar( /* facet_list */ );
+ if (qh num_outside && !furthest) {
+ qh_fprintf(qh ferr, 6167, "qhull internal error (qh_buildhull): %d outside points were never processed.\n", qh num_outside);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ trace1((qh ferr, 1039, "qh_buildhull: completed the hull construction\n"));
+} /* buildhull */
+
+
+/*---------------------------------
+
+ qh_buildtracing( furthest, facet )
+ trace an iteration of qh_buildhull() for furthest point and facet
+ if !furthest, prints progress message
+
+ returns:
+ tracks progress with qh.lastreport
+ updates qh.furthest_id (-3 if furthest is NULL)
+ also resets visit_id, vertext_visit on wrap around
+
+ see:
+ qh_tracemerging()
+
+ design:
+ if !furthest
+ print progress message
+ exit
+ if 'TFn' iteration
+ print progress message
+ else if tracing
+ trace furthest point and facet
+ reset qh.visit_id and qh.vertex_visit if overflow may occur
+ set qh.furthest_id for tracing
+*/
+void qh_buildtracing(pointT *furthest, facetT *facet) {
+ realT dist= 0;
+ float cpu;
+ int total, furthestid;
+ time_t timedata;
+ struct tm *tp;
+ vertexT *vertex;
+
+ qh old_randomdist= qh RANDOMdist;
+ qh RANDOMdist= False;
+ if (!furthest) {
+ time(&timedata);
+ tp= localtime(&timedata);
+ cpu= (float)qh_CPUclock - (float)qh hulltime;
+ cpu /= (float)qh_SECticks;
+ total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot);
+ qh_fprintf(qh ferr, 8118, "\n\
+At %02d:%02d:%02d & %2.5g CPU secs, qhull has created %d facets and merged %d.\n\
+ The current hull contains %d facets and %d vertices. Last point was p%d\n",
+ tp->tm_hour, tp->tm_min, tp->tm_sec, cpu, qh facet_id -1,
+ total, qh num_facets, qh num_vertices, qh furthest_id);
+ return;
+ }
+ furthestid= qh_pointid(furthest);
+ if (qh TRACEpoint == furthestid) {
+ qh IStracing= qh TRACElevel;
+ qhmem.IStracing= qh TRACElevel;
+ }else if (qh TRACEpoint != -1 && qh TRACEdist < REALmax/2) {
+ qh IStracing= 0;
+ qhmem.IStracing= 0;
+ }
+ if (qh REPORTfreq && (qh facet_id-1 > qh lastreport+qh REPORTfreq)) {
+ qh lastreport= qh facet_id-1;
+ time(&timedata);
+ tp= localtime(&timedata);
+ cpu= (float)qh_CPUclock - (float)qh hulltime;
+ cpu /= (float)qh_SECticks;
+ total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot);
+ zinc_(Zdistio);
+ qh_distplane(furthest, facet, &dist);
+ qh_fprintf(qh ferr, 8119, "\n\
+At %02d:%02d:%02d & %2.5g CPU secs, qhull has created %d facets and merged %d.\n\
+ The current hull contains %d facets and %d vertices. There are %d\n\
+ outside points. Next is point p%d(v%d), %2.2g above f%d.\n",
+ tp->tm_hour, tp->tm_min, tp->tm_sec, cpu, qh facet_id -1,
+ total, qh num_facets, qh num_vertices, qh num_outside+1,
+ furthestid, qh vertex_id, dist, getid_(facet));
+ }else if (qh IStracing >=1) {
+ cpu= (float)qh_CPUclock - (float)qh hulltime;
+ cpu /= (float)qh_SECticks;
+ qh_distplane(furthest, facet, &dist);
+ qh_fprintf(qh ferr, 8120, "qh_addpoint: add p%d(v%d) to hull of %d facets(%2.2g above f%d) and %d outside at %4.4g CPU secs. Previous was p%d.\n",
+ furthestid, qh vertex_id, qh num_facets, dist,
+ getid_(facet), qh num_outside+1, cpu, qh furthest_id);
+ }
+ zmax_(Zvisit2max, (int)qh visit_id/2);
+ if (qh visit_id > (unsigned) INT_MAX) {
+ zinc_(Zvisit);
+ qh visit_id= 0;
+ FORALLfacets
+ facet->visitid= 0;
+ }
+ zmax_(Zvvisit2max, (int)qh vertex_visit/2);
+ if (qh vertex_visit > (unsigned) INT_MAX/2) { /* 31 bits */
+ zinc_(Zvvisit);
+ qh vertex_visit= 0;
+ FORALLvertices
+ vertex->visitid= 0;
+ }
+ qh furthest_id= furthestid;
+ qh RANDOMdist= qh old_randomdist;
+} /* buildtracing */
+
+/*---------------------------------
+
+ qh_errexit2( exitcode, facet, otherfacet )
+ return exitcode to system after an error
+ report two facets
+
+ returns:
+ assumes exitcode non-zero
+
+ see:
+ normally use qh_errexit() in user.c(reports a facet and a ridge)
+*/
+void qh_errexit2(int exitcode, facetT *facet, facetT *otherfacet) {
+
+ qh_errprint("ERRONEOUS", facet, otherfacet, NULL, NULL);
+ qh_errexit(exitcode, NULL, NULL);
+} /* errexit2 */
+
+
+/*---------------------------------
+
+ qh_findhorizon( point, facet, goodvisible, goodhorizon )
+ given a visible facet, find the point's horizon and visible facets
+ for all facets, !facet-visible
+
+ returns:
+ returns qh.visible_list/num_visible with all visible facets
+ marks visible facets with ->visible
+ updates count of good visible and good horizon facets
+ updates qh.max_outside, qh.max_vertex, facet->maxoutside
+
+ see:
+ similar to qh_delpoint()
+
+ design:
+ move facet to qh.visible_list at end of qh.facet_list
+ for all visible facets
+ for each unvisited neighbor of a visible facet
+ compute distance of point to neighbor
+ if point above neighbor
+ move neighbor to end of qh.visible_list
+ else if point is coplanar with neighbor
+ update qh.max_outside, qh.max_vertex, neighbor->maxoutside
+ mark neighbor coplanar (will create a samecycle later)
+ update horizon statistics
+*/
+void qh_findhorizon(pointT *point, facetT *facet, int *goodvisible, int *goodhorizon) {
+ facetT *neighbor, **neighborp, *visible;
+ int numhorizon= 0, coplanar= 0;
+ realT dist;
+
+ trace1((qh ferr, 1040,"qh_findhorizon: find horizon for point p%d facet f%d\n",qh_pointid(point),facet->id));
+ *goodvisible= *goodhorizon= 0;
+ zinc_(Ztotvisible);
+ qh_removefacet(facet); /* visible_list at end of qh facet_list */
+ qh_appendfacet(facet);
+ qh num_visible= 1;
+ if (facet->good)
+ (*goodvisible)++;
+ qh visible_list= facet;
+ facet->visible= True;
+ facet->f.replace= NULL;
+ if (qh IStracing >=4)
+ qh_errprint("visible", facet, NULL, NULL, NULL);
+ qh visit_id++;
+ FORALLvisible_facets {
+ if (visible->tricoplanar && !qh TRInormals) {
+ qh_fprintf(qh ferr, 6230, "Qhull internal error (qh_findhorizon): does not work for tricoplanar facets. Use option 'Q11'\n");
+ qh_errexit(qh_ERRqhull, visible, NULL);
+ }
+ visible->visitid= qh visit_id;
+ FOREACHneighbor_(visible) {
+ if (neighbor->visitid == qh visit_id)
+ continue;
+ neighbor->visitid= qh visit_id;
+ zzinc_(Znumvisibility);
+ qh_distplane(point, neighbor, &dist);
+ if (dist > qh MINvisible) {
+ zinc_(Ztotvisible);
+ qh_removefacet(neighbor); /* append to end of qh visible_list */
+ qh_appendfacet(neighbor);
+ neighbor->visible= True;
+ neighbor->f.replace= NULL;
+ qh num_visible++;
+ if (neighbor->good)
+ (*goodvisible)++;
+ if (qh IStracing >=4)
+ qh_errprint("visible", neighbor, NULL, NULL, NULL);
+ }else {
+ if (dist > - qh MAXcoplanar) {
+ neighbor->coplanar= True;
+ zzinc_(Zcoplanarhorizon);
+ qh_precision("coplanar horizon");
+ coplanar++;
+ if (qh MERGING) {
+ if (dist > 0) {
+ maximize_(qh max_outside, dist);
+ maximize_(qh max_vertex, dist);
+#if qh_MAXoutside
+ maximize_(neighbor->maxoutside, dist);
+#endif
+ }else
+ minimize_(qh min_vertex, dist); /* due to merge later */
+ }
+ trace2((qh ferr, 2057, "qh_findhorizon: point p%d is coplanar to horizon f%d, dist=%2.7g < qh MINvisible(%2.7g)\n",
+ qh_pointid(point), neighbor->id, dist, qh MINvisible));
+ }else
+ neighbor->coplanar= False;
+ zinc_(Ztothorizon);
+ numhorizon++;
+ if (neighbor->good)
+ (*goodhorizon)++;
+ if (qh IStracing >=4)
+ qh_errprint("horizon", neighbor, NULL, NULL, NULL);
+ }
+ }
+ }
+ if (!numhorizon) {
+ qh_precision("empty horizon");
+ qh_fprintf(qh ferr, 6168, "qhull precision error (qh_findhorizon): empty horizon\n\
+QhullPoint p%d was above all facets.\n", qh_pointid(point));
+ qh_printfacetlist(qh facet_list, NULL, True);
+ qh_errexit(qh_ERRprec, NULL, NULL);
+ }
+ trace1((qh ferr, 1041, "qh_findhorizon: %d horizon facets(good %d), %d visible(good %d), %d coplanar\n",
+ numhorizon, *goodhorizon, qh num_visible, *goodvisible, coplanar));
+ if (qh IStracing >= 4 && qh num_facets < 50)
+ qh_printlists();
+} /* findhorizon */
+
+/*---------------------------------
+
+ qh_nextfurthest( visible )
+ returns next furthest point and visible facet for qh_addpoint()
+ starts search at qh.facet_next
+
+ returns:
+ removes furthest point from outside set
+ NULL if none available
+ advances qh.facet_next over facets with empty outside sets
+
+ design:
+ for each facet from qh.facet_next
+ if empty outside set
+ advance qh.facet_next
+ else if qh.NARROWhull
+ determine furthest outside point
+ if furthest point is not outside
+ advance qh.facet_next(point will be coplanar)
+ remove furthest point from outside set
+*/
+pointT *qh_nextfurthest(facetT **visible) {
+ facetT *facet;
+ int size, idx;
+ realT randr, dist;
+ pointT *furthest;
+
+ while ((facet= qh facet_next) != qh facet_tail) {
+ if (!facet->outsideset) {
+ qh facet_next= facet->next;
+ continue;
+ }
+ SETreturnsize_(facet->outsideset, size);
+ if (!size) {
+ qh_setfree(&facet->outsideset);
+ qh facet_next= facet->next;
+ continue;
+ }
+ if (qh NARROWhull) {
+ if (facet->notfurthest)
+ qh_furthestout(facet);
+ furthest= (pointT*)qh_setlast(facet->outsideset);
+#if qh_COMPUTEfurthest
+ qh_distplane(furthest, facet, &dist);
+ zinc_(Zcomputefurthest);
+#else
+ dist= facet->furthestdist;
+#endif
+ if (dist < qh MINoutside) { /* remainder of outside set is coplanar for qh_outcoplanar */
+ qh facet_next= facet->next;
+ continue;
+ }
+ }
+ if (!qh RANDOMoutside && !qh VIRTUALmemory) {
+ if (qh PICKfurthest) {
+ qh_furthestnext(/* qh facet_list */);
+ facet= qh facet_next;
+ }
+ *visible= facet;
+ return((pointT*)qh_setdellast(facet->outsideset));
+ }
+ if (qh RANDOMoutside) {
+ int outcoplanar = 0;
+ if (qh NARROWhull) {
+ FORALLfacets {
+ if (facet == qh facet_next)
+ break;
+ if (facet->outsideset)
+ outcoplanar += qh_setsize( facet->outsideset);
+ }
+ }
+ randr= qh_RANDOMint;
+ randr= randr/(qh_RANDOMmax+1);
+ idx= (int)floor((qh num_outside - outcoplanar) * randr);
+ FORALLfacet_(qh facet_next) {
+ if (facet->outsideset) {
+ SETreturnsize_(facet->outsideset, size);
+ if (!size)
+ qh_setfree(&facet->outsideset);
+ else if (size > idx) {
+ *visible= facet;
+ return((pointT*)qh_setdelnth(facet->outsideset, idx));
+ }else
+ idx -= size;
+ }
+ }
+ qh_fprintf(qh ferr, 6169, "qhull internal error (qh_nextfurthest): num_outside %d is too low\nby at least %d, or a random real %g >= 1.0\n",
+ qh num_outside, idx+1, randr);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }else { /* VIRTUALmemory */
+ facet= qh facet_tail->previous;
+ if (!(furthest= (pointT*)qh_setdellast(facet->outsideset))) {
+ if (facet->outsideset)
+ qh_setfree(&facet->outsideset);
+ qh_removefacet(facet);
+ qh_prependfacet(facet, &qh facet_list);
+ continue;
+ }
+ *visible= facet;
+ return furthest;
+ }
+ }
+ return NULL;
+} /* nextfurthest */
+
+/*---------------------------------
+
+ qh_partitionall( vertices, points, numpoints )
+ partitions all points in points/numpoints to the outsidesets of facets
+ vertices= vertices in qh.facet_list(!partitioned)
+
+ returns:
+ builds facet->outsideset
+ does not partition qh.GOODpoint
+ if qh.ONLYgood && !qh.MERGING,
+ does not partition qh.GOODvertex
+
+ notes:
+ faster if qh.facet_list sorted by anticipated size of outside set
+
+ design:
+ initialize pointset with all points
+ remove vertices from pointset
+ remove qh.GOODpointp from pointset (unless it's qh.STOPcone or qh.STOPpoint)
+ for all facets
+ for all remaining points in pointset
+ compute distance from point to facet
+ if point is outside facet
+ remove point from pointset (by not reappending)
+ update bestpoint
+ append point or old bestpoint to facet's outside set
+ append bestpoint to facet's outside set (furthest)
+ for all points remaining in pointset
+ partition point into facets' outside sets and coplanar sets
+*/
+void qh_partitionall(setT *vertices, pointT *points, int numpoints){
+ setT *pointset;
+ vertexT *vertex, **vertexp;
+ pointT *point, **pointp, *bestpoint;
+ int size, point_i, point_n, point_end, remaining, i, id;
+ facetT *facet;
+ realT bestdist= -REALmax, dist, distoutside;
+
+ trace1((qh ferr, 1042, "qh_partitionall: partition all points into outside sets\n"));
+ pointset= qh_settemp(numpoints);
+ qh num_outside= 0;
+ pointp= SETaddr_(pointset, pointT);
+ for (i=numpoints, point= points; i--; point += qh hull_dim)
+ *(pointp++)= point;
+ qh_settruncate(pointset, numpoints);
+ FOREACHvertex_(vertices) {
+ if ((id= qh_pointid(vertex->point)) >= 0)
+ SETelem_(pointset, id)= NULL;
+ }
+ id= qh_pointid(qh GOODpointp);
+ if (id >=0 && qh STOPcone-1 != id && -qh STOPpoint-1 != id)
+ SETelem_(pointset, id)= NULL;
+ if (qh GOODvertexp && qh ONLYgood && !qh MERGING) { /* matches qhull()*/
+ if ((id= qh_pointid(qh GOODvertexp)) >= 0)
+ SETelem_(pointset, id)= NULL;
+ }
+ if (!qh BESToutside) { /* matches conditional for qh_partitionpoint below */
+ distoutside= qh_DISToutside; /* multiple of qh.MINoutside & qh.max_outside, see user.h */
+ zval_(Ztotpartition)= qh num_points - qh hull_dim - 1; /*misses GOOD... */
+ remaining= qh num_facets;
+ point_end= numpoints;
+ FORALLfacets {
+ size= point_end/(remaining--) + 100;
+ facet->outsideset= qh_setnew(size);
+ bestpoint= NULL;
+ point_end= 0;
+ FOREACHpoint_i_(pointset) {
+ if (point) {
+ zzinc_(Zpartitionall);
+ qh_distplane(point, facet, &dist);
+ if (dist < distoutside)
+ SETelem_(pointset, point_end++)= point;
+ else {
+ qh num_outside++;
+ if (!bestpoint) {
+ bestpoint= point;
+ bestdist= dist;
+ }else if (dist > bestdist) {
+ qh_setappend(&facet->outsideset, bestpoint);
+ bestpoint= point;
+ bestdist= dist;
+ }else
+ qh_setappend(&facet->outsideset, point);
+ }
+ }
+ }
+ if (bestpoint) {
+ qh_setappend(&facet->outsideset, bestpoint);
+#if !qh_COMPUTEfurthest
+ facet->furthestdist= bestdist;
+#endif
+ }else
+ qh_setfree(&facet->outsideset);
+ qh_settruncate(pointset, point_end);
+ }
+ }
+ /* if !qh BESToutside, pointset contains points not assigned to outsideset */
+ if (qh BESToutside || qh MERGING || qh KEEPcoplanar || qh KEEPinside) {
+ qh findbestnew= True;
+ FOREACHpoint_i_(pointset) {
+ if (point)
+ qh_partitionpoint(point, qh facet_list);
+ }
+ qh findbestnew= False;
+ }
+ zzadd_(Zpartitionall, zzval_(Zpartition));
+ zzval_(Zpartition)= 0;
+ qh_settempfree(&pointset);
+ if (qh IStracing >= 4)
+ qh_printfacetlist(qh facet_list, NULL, True);
+} /* partitionall */
+
+
+/*---------------------------------
+
+ qh_partitioncoplanar( point, facet, dist )
+ partition coplanar point to a facet
+ dist is distance from point to facet
+ if dist NULL,
+ searches for bestfacet and does nothing if inside
+ if qh.findbestnew set,
+ searches new facets instead of using qh_findbest()
+
+ returns:
+ qh.max_ouside updated
+ if qh.KEEPcoplanar or qh.KEEPinside
+ point assigned to best coplanarset
+
+ notes:
+ facet->maxoutside is updated at end by qh_check_maxout
+
+ design:
+ if dist undefined
+ find best facet for point
+ if point sufficiently below facet (depends on qh.NEARinside and qh.KEEPinside)
+ exit
+ if keeping coplanar/nearinside/inside points
+ if point is above furthest coplanar point
+ append point to coplanar set (it is the new furthest)
+ update qh.max_outside
+ else
+ append point one before end of coplanar set
+ else if point is clearly outside of qh.max_outside and bestfacet->coplanarset
+ and bestfacet is more than perpendicular to facet
+ repartition the point using qh_findbest() -- it may be put on an outsideset
+ else
+ update qh.max_outside
+*/
+void qh_partitioncoplanar(pointT *point, facetT *facet, realT *dist) {
+ facetT *bestfacet;
+ pointT *oldfurthest;
+ realT bestdist, dist2= 0, angle;
+ int numpart= 0, oldfindbest;
+ boolT isoutside;
+
+ qh WAScoplanar= True;
+ if (!dist) {
+ if (qh findbestnew)
+ bestfacet= qh_findbestnew(point, facet, &bestdist, qh_ALL, &isoutside, &numpart);
+ else
+ bestfacet= qh_findbest(point, facet, qh_ALL, !qh_ISnewfacets, qh DELAUNAY,
+ &bestdist, &isoutside, &numpart);
+ zinc_(Ztotpartcoplanar);
+ zzadd_(Zpartcoplanar, numpart);
+ if (!qh DELAUNAY && !qh KEEPinside) { /* for 'd', bestdist skips upperDelaunay facets */
+ if (qh KEEPnearinside) {
+ if (bestdist < -qh NEARinside) {
+ zinc_(Zcoplanarinside);
+ trace4((qh ferr, 4062, "qh_partitioncoplanar: point p%d is more than near-inside facet f%d dist %2.2g findbestnew %d\n",
+ qh_pointid(point), bestfacet->id, bestdist, qh findbestnew));
+ return;
+ }
+ }else if (bestdist < -qh MAXcoplanar) {
+ trace4((qh ferr, 4063, "qh_partitioncoplanar: point p%d is inside facet f%d dist %2.2g findbestnew %d\n",
+ qh_pointid(point), bestfacet->id, bestdist, qh findbestnew));
+ zinc_(Zcoplanarinside);
+ return;
+ }
+ }
+ }else {
+ bestfacet= facet;
+ bestdist= *dist;
+ }
+ if (bestdist > qh max_outside) {
+ if (!dist && facet != bestfacet) {
+ zinc_(Zpartangle);
+ angle= qh_getangle(facet->normal, bestfacet->normal);
+ if (angle < 0) {
+ /* typically due to deleted vertex and coplanar facets, e.g.,
+ RBOX 1000 s Z1 G1e-13 t1001185205 | QHULL Tv */
+ zinc_(Zpartflip);
+ trace2((qh ferr, 2058, "qh_partitioncoplanar: repartition point p%d from f%d. It is above flipped facet f%d dist %2.2g\n",
+ qh_pointid(point), facet->id, bestfacet->id, bestdist));
+ oldfindbest= qh findbestnew;
+ qh findbestnew= False;
+ qh_partitionpoint(point, bestfacet);
+ qh findbestnew= oldfindbest;
+ return;
+ }
+ }
+ qh max_outside= bestdist;
+ if (bestdist > qh TRACEdist) {
+ qh_fprintf(qh ferr, 8122, "qh_partitioncoplanar: ====== p%d from f%d increases max_outside to %2.2g of f%d last p%d\n",
+ qh_pointid(point), facet->id, bestdist, bestfacet->id, qh furthest_id);
+ qh_errprint("DISTANT", facet, bestfacet, NULL, NULL);
+ }
+ }
+ if (qh KEEPcoplanar + qh KEEPinside + qh KEEPnearinside) {
+ oldfurthest= (pointT*)qh_setlast(bestfacet->coplanarset);
+ if (oldfurthest) {
+ zinc_(Zcomputefurthest);
+ qh_distplane(oldfurthest, bestfacet, &dist2);
+ }
+ if (!oldfurthest || dist2 < bestdist)
+ qh_setappend(&bestfacet->coplanarset, point);
+ else
+ qh_setappend2ndlast(&bestfacet->coplanarset, point);
+ }
+ trace4((qh ferr, 4064, "qh_partitioncoplanar: point p%d is coplanar with facet f%d(or inside) dist %2.2g\n",
+ qh_pointid(point), bestfacet->id, bestdist));
+} /* partitioncoplanar */
+
+/*---------------------------------
+
+ qh_partitionpoint( point, facet )
+ assigns point to an outside set, coplanar set, or inside set (i.e., dropt)
+ if qh.findbestnew
+ uses qh_findbestnew() to search all new facets
+ else
+ uses qh_findbest()
+
+ notes:
+ after qh_distplane(), this and qh_findbest() are most expensive in 3-d
+
+ design:
+ find best facet for point
+ (either exhaustive search of new facets or directed search from facet)
+ if qh.NARROWhull
+ retain coplanar and nearinside points as outside points
+ if point is outside bestfacet
+ if point above furthest point for bestfacet
+ append point to outside set (it becomes the new furthest)
+ if outside set was empty
+ move bestfacet to end of qh.facet_list (i.e., after qh.facet_next)
+ update bestfacet->furthestdist
+ else
+ append point one before end of outside set
+ else if point is coplanar to bestfacet
+ if keeping coplanar points or need to update qh.max_outside
+ partition coplanar point into bestfacet
+ else if near-inside point
+ partition as coplanar point into bestfacet
+ else is an inside point
+ if keeping inside points
+ partition as coplanar point into bestfacet
+*/
+void qh_partitionpoint(pointT *point, facetT *facet) {
+ realT bestdist;
+ boolT isoutside;
+ facetT *bestfacet;
+ int numpart;
+#if qh_COMPUTEfurthest
+ realT dist;
+#endif
+
+ if (qh findbestnew)
+ bestfacet= qh_findbestnew(point, facet, &bestdist, qh BESToutside, &isoutside, &numpart);
+ else
+ bestfacet= qh_findbest(point, facet, qh BESToutside, qh_ISnewfacets, !qh_NOupper,
+ &bestdist, &isoutside, &numpart);
+ zinc_(Ztotpartition);
+ zzadd_(Zpartition, numpart);
+ if (qh NARROWhull) {
+ if (qh DELAUNAY && !isoutside && bestdist >= -qh MAXcoplanar)
+ qh_precision("nearly incident point(narrow hull)");
+ if (qh KEEPnearinside) {
+ if (bestdist >= -qh NEARinside)
+ isoutside= True;
+ }else if (bestdist >= -qh MAXcoplanar)
+ isoutside= True;
+ }
+
+ if (isoutside) {
+ if (!bestfacet->outsideset
+ || !qh_setlast(bestfacet->outsideset)) {
+ qh_setappend(&(bestfacet->outsideset), point);
+ if (!bestfacet->newfacet) {
+ qh_removefacet(bestfacet); /* make sure it's after qh facet_next */
+ qh_appendfacet(bestfacet);
+ }
+#if !qh_COMPUTEfurthest
+ bestfacet->furthestdist= bestdist;
+#endif
+ }else {
+#if qh_COMPUTEfurthest
+ zinc_(Zcomputefurthest);
+ qh_distplane(oldfurthest, bestfacet, &dist);
+ if (dist < bestdist)
+ qh_setappend(&(bestfacet->outsideset), point);
+ else
+ qh_setappend2ndlast(&(bestfacet->outsideset), point);
+#else
+ if (bestfacet->furthestdist < bestdist) {
+ qh_setappend(&(bestfacet->outsideset), point);
+ bestfacet->furthestdist= bestdist;
+ }else
+ qh_setappend2ndlast(&(bestfacet->outsideset), point);
+#endif
+ }
+ qh num_outside++;
+ trace4((qh ferr, 4065, "qh_partitionpoint: point p%d is outside facet f%d new? %d (or narrowhull)\n",
+ qh_pointid(point), bestfacet->id, bestfacet->newfacet));
+ }else if (qh DELAUNAY || bestdist >= -qh MAXcoplanar) { /* for 'd', bestdist skips upperDelaunay facets */
+ zzinc_(Zcoplanarpart);
+ if (qh DELAUNAY)
+ qh_precision("nearly incident point");
+ if ((qh KEEPcoplanar + qh KEEPnearinside) || bestdist > qh max_outside)
+ qh_partitioncoplanar(point, bestfacet, &bestdist);
+ else {
+ trace4((qh ferr, 4066, "qh_partitionpoint: point p%d is coplanar to facet f%d (dropped)\n",
+ qh_pointid(point), bestfacet->id));
+ }
+ }else if (qh KEEPnearinside && bestdist > -qh NEARinside) {
+ zinc_(Zpartnear);
+ qh_partitioncoplanar(point, bestfacet, &bestdist);
+ }else {
+ zinc_(Zpartinside);
+ trace4((qh ferr, 4067, "qh_partitionpoint: point p%d is inside all facets, closest to f%d dist %2.2g\n",
+ qh_pointid(point), bestfacet->id, bestdist));
+ if (qh KEEPinside)
+ qh_partitioncoplanar(point, bestfacet, &bestdist);
+ }
+} /* partitionpoint */
+
+/*---------------------------------
+
+ qh_partitionvisible( allpoints, numoutside )
+ partitions points in visible facets to qh.newfacet_list
+ qh.visible_list= visible facets
+ for visible facets
+ 1st neighbor (if any) points to a horizon facet or a new facet
+ if allpoints(!used),
+ repartitions coplanar points
+
+ returns:
+ updates outside sets and coplanar sets of qh.newfacet_list
+ updates qh.num_outside (count of outside points)
+
+ notes:
+ qh.findbest_notsharp should be clear (extra work if set)
+
+ design:
+ for all visible facets with outside set or coplanar set
+ select a newfacet for visible facet
+ if outside set
+ partition outside set into new facets
+ if coplanar set and keeping coplanar/near-inside/inside points
+ if allpoints
+ partition coplanar set into new facets, may be assigned outside
+ else
+ partition coplanar set into coplanar sets of new facets
+ for each deleted vertex
+ if allpoints
+ partition vertex into new facets, may be assigned outside
+ else
+ partition vertex into coplanar sets of new facets
+*/
+void qh_partitionvisible(/*visible_list*/ boolT allpoints, int *numoutside) {
+ facetT *visible, *newfacet;
+ pointT *point, **pointp;
+ int coplanar=0, size;
+ unsigned count;
+ vertexT *vertex, **vertexp;
+
+ if (qh ONLYmax)
+ maximize_(qh MINoutside, qh max_vertex);
+ *numoutside= 0;
+ FORALLvisible_facets {
+ if (!visible->outsideset && !visible->coplanarset)
+ continue;
+ newfacet= visible->f.replace;
+ count= 0;
+ while (newfacet && newfacet->visible) {
+ newfacet= newfacet->f.replace;
+ if (count++ > qh facet_id)
+ qh_infiniteloop(visible);
+ }
+ if (!newfacet)
+ newfacet= qh newfacet_list;
+ if (newfacet == qh facet_tail) {
+ qh_fprintf(qh ferr, 6170, "qhull precision error (qh_partitionvisible): all new facets deleted as\n degenerate facets. Can not continue.\n");
+ qh_errexit(qh_ERRprec, NULL, NULL);
+ }
+ if (visible->outsideset) {
+ size= qh_setsize(visible->outsideset);
+ *numoutside += size;
+ qh num_outside -= size;
+ FOREACHpoint_(visible->outsideset)
+ qh_partitionpoint(point, newfacet);
+ }
+ if (visible->coplanarset && (qh KEEPcoplanar + qh KEEPinside + qh KEEPnearinside)) {
+ size= qh_setsize(visible->coplanarset);
+ coplanar += size;
+ FOREACHpoint_(visible->coplanarset) {
+ if (allpoints) /* not used */
+ qh_partitionpoint(point, newfacet);
+ else
+ qh_partitioncoplanar(point, newfacet, NULL);
+ }
+ }
+ }
+ FOREACHvertex_(qh del_vertices) {
+ if (vertex->point) {
+ if (allpoints) /* not used */
+ qh_partitionpoint(vertex->point, qh newfacet_list);
+ else
+ qh_partitioncoplanar(vertex->point, qh newfacet_list, NULL);
+ }
+ }
+ trace1((qh ferr, 1043,"qh_partitionvisible: partitioned %d points from outsidesets and %d points from coplanarsets\n", *numoutside, coplanar));
+} /* partitionvisible */
+
+
+
+/*---------------------------------
+
+ qh_precision( reason )
+ restart on precision errors if not merging and if 'QJn'
+*/
+void qh_precision(const char *reason) {
+
+ if (qh ALLOWrestart && !qh PREmerge && !qh MERGEexact) {
+ if (qh JOGGLEmax < REALmax/2) {
+ trace0((qh ferr, 26, "qh_precision: qhull restart because of %s\n", reason));
+ longjmp(qh restartexit, qh_ERRprec);
+ }
+ }
+} /* qh_precision */
+
+/*---------------------------------
+
+ qh_printsummary( fp )
+ prints summary to fp
+
+ notes:
+ not in io.c so that user_eg.c can prevent io.c from loading
+ qh_printsummary and qh_countfacets must match counts
+
+ design:
+ determine number of points, vertices, and coplanar points
+ print summary
+*/
+void qh_printsummary(FILE *fp) {
+ realT ratio, outerplane, innerplane;
+ float cpu;
+ int size, id, nummerged, numvertices, numcoplanars= 0, nonsimplicial=0;
+ int goodused;
+ facetT *facet;
+ const char *s;
+ int numdel= zzval_(Zdelvertextot);
+ int numtricoplanars= 0;
+
+ size= qh num_points + qh_setsize(qh other_points);
+ numvertices= qh num_vertices - qh_setsize(qh del_vertices);
+ id= qh_pointid(qh GOODpointp);
+ FORALLfacets {
+ if (facet->coplanarset)
+ numcoplanars += qh_setsize( facet->coplanarset);
+ if (facet->good) {
+ if (facet->simplicial) {
+ if (facet->keepcentrum && facet->tricoplanar)
+ numtricoplanars++;
+ }else if (qh_setsize(facet->vertices) != qh hull_dim)
+ nonsimplicial++;
+ }
+ }
+ if (id >=0 && qh STOPcone-1 != id && -qh STOPpoint-1 != id)
+ size--;
+ if (qh STOPcone || qh STOPpoint)
+ qh_fprintf(fp, 9288, "\nAt a premature exit due to 'TVn', 'TCn', 'TRn', or precision error with 'QJn'.");
+ if (qh UPPERdelaunay)
+ goodused= qh GOODvertex + qh GOODpoint + qh SPLITthresholds;
+ else if (qh DELAUNAY)
+ goodused= qh GOODvertex + qh GOODpoint + qh GOODthreshold;
+ else
+ goodused= qh num_good;
+ nummerged= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot);
+ if (qh VORONOI) {
+ if (qh UPPERdelaunay)
+ qh_fprintf(fp, 9289, "\n\
+Furthest-site Voronoi vertices by the convex hull of %d points in %d-d:\n\n", size, qh hull_dim);
+ else
+ qh_fprintf(fp, 9290, "\n\
+Voronoi diagram by the convex hull of %d points in %d-d:\n\n", size, qh hull_dim);
+ qh_fprintf(fp, 9291, " Number of Voronoi regions%s: %d\n",
+ qh ATinfinity ? " and at-infinity" : "", numvertices);
+ if (numdel)
+ qh_fprintf(fp, 9292, " Total number of deleted points due to merging: %d\n", numdel);
+ if (numcoplanars - numdel > 0)
+ qh_fprintf(fp, 9293, " Number of nearly incident points: %d\n", numcoplanars - numdel);
+ else if (size - numvertices - numdel > 0)
+ qh_fprintf(fp, 9294, " Total number of nearly incident points: %d\n", size - numvertices - numdel);
+ qh_fprintf(fp, 9295, " Number of%s Voronoi vertices: %d\n",
+ goodused ? " 'good'" : "", qh num_good);
+ if (nonsimplicial)
+ qh_fprintf(fp, 9296, " Number of%s non-simplicial Voronoi vertices: %d\n",
+ goodused ? " 'good'" : "", nonsimplicial);
+ }else if (qh DELAUNAY) {
+ if (qh UPPERdelaunay)
+ qh_fprintf(fp, 9297, "\n\
+Furthest-site Delaunay triangulation by the convex hull of %d points in %d-d:\n\n", size, qh hull_dim);
+ else
+ qh_fprintf(fp, 9298, "\n\
+Delaunay triangulation by the convex hull of %d points in %d-d:\n\n", size, qh hull_dim);
+ qh_fprintf(fp, 9299, " Number of input sites%s: %d\n",
+ qh ATinfinity ? " and at-infinity" : "", numvertices);
+ if (numdel)
+ qh_fprintf(fp, 9300, " Total number of deleted points due to merging: %d\n", numdel);
+ if (numcoplanars - numdel > 0)
+ qh_fprintf(fp, 9301, " Number of nearly incident points: %d\n", numcoplanars - numdel);
+ else if (size - numvertices - numdel > 0)
+ qh_fprintf(fp, 9302, " Total number of nearly incident points: %d\n", size - numvertices - numdel);
+ qh_fprintf(fp, 9303, " Number of%s Delaunay regions: %d\n",
+ goodused ? " 'good'" : "", qh num_good);
+ if (nonsimplicial)
+ qh_fprintf(fp, 9304, " Number of%s non-simplicial Delaunay regions: %d\n",
+ goodused ? " 'good'" : "", nonsimplicial);
+ }else if (qh HALFspace) {
+ qh_fprintf(fp, 9305, "\n\
+Halfspace intersection by the convex hull of %d points in %d-d:\n\n", size, qh hull_dim);
+ qh_fprintf(fp, 9306, " Number of halfspaces: %d\n", size);
+ qh_fprintf(fp, 9307, " Number of non-redundant halfspaces: %d\n", numvertices);
+ if (numcoplanars) {
+ if (qh KEEPinside && qh KEEPcoplanar)
+ s= "similar and redundant";
+ else if (qh KEEPinside)
+ s= "redundant";
+ else
+ s= "similar";
+ qh_fprintf(fp, 9308, " Number of %s halfspaces: %d\n", s, numcoplanars);
+ }
+ qh_fprintf(fp, 9309, " Number of intersection points: %d\n", qh num_facets - qh num_visible);
+ if (goodused)
+ qh_fprintf(fp, 9310, " Number of 'good' intersection points: %d\n", qh num_good);
+ if (nonsimplicial)
+ qh_fprintf(fp, 9311, " Number of%s non-simplicial intersection points: %d\n",
+ goodused ? " 'good'" : "", nonsimplicial);
+ }else {
+ qh_fprintf(fp, 9312, "\n\
+Convex hull of %d points in %d-d:\n\n", size, qh hull_dim);
+ qh_fprintf(fp, 9313, " Number of vertices: %d\n", numvertices);
+ if (numcoplanars) {
+ if (qh KEEPinside && qh KEEPcoplanar)
+ s= "coplanar and interior";
+ else if (qh KEEPinside)
+ s= "interior";
+ else
+ s= "coplanar";
+ qh_fprintf(fp, 9314, " Number of %s points: %d\n", s, numcoplanars);
+ }
+ qh_fprintf(fp, 9315, " Number of facets: %d\n", qh num_facets - qh num_visible);
+ if (goodused)
+ qh_fprintf(fp, 9316, " Number of 'good' facets: %d\n", qh num_good);
+ if (nonsimplicial)
+ qh_fprintf(fp, 9317, " Number of%s non-simplicial facets: %d\n",
+ goodused ? " 'good'" : "", nonsimplicial);
+ }
+ if (numtricoplanars)
+ qh_fprintf(fp, 9318, " Number of triangulated facets: %d\n", numtricoplanars);
+ qh_fprintf(fp, 9319, "\nStatistics for: %s | %s",
+ qh rbox_command, qh qhull_command);
+ if (qh ROTATErandom != INT_MIN)
+ qh_fprintf(fp, 9320, " QR%d\n\n", qh ROTATErandom);
+ else
+ qh_fprintf(fp, 9321, "\n\n");
+ qh_fprintf(fp, 9322, " Number of points processed: %d\n", zzval_(Zprocessed));
+ qh_fprintf(fp, 9323, " Number of hyperplanes created: %d\n", zzval_(Zsetplane));
+ if (qh DELAUNAY)
+ qh_fprintf(fp, 9324, " Number of facets in hull: %d\n", qh num_facets - qh num_visible);
+ qh_fprintf(fp, 9325, " Number of distance tests for qhull: %d\n", zzval_(Zpartition)+
+ zzval_(Zpartitionall)+zzval_(Znumvisibility)+zzval_(Zpartcoplanar));
+#if 0 /* NOTE: must print before printstatistics() */
+ {realT stddev, ave;
+ qh_fprintf(fp, 9326, " average new facet balance: %2.2g\n",
+ wval_(Wnewbalance)/zval_(Zprocessed));
+ stddev= qh_stddev(zval_(Zprocessed), wval_(Wnewbalance),
+ wval_(Wnewbalance2), &ave);
+ qh_fprintf(fp, 9327, " new facet standard deviation: %2.2g\n", stddev);
+ qh_fprintf(fp, 9328, " average partition balance: %2.2g\n",
+ wval_(Wpbalance)/zval_(Zpbalance));
+ stddev= qh_stddev(zval_(Zpbalance), wval_(Wpbalance),
+ wval_(Wpbalance2), &ave);
+ qh_fprintf(fp, 9329, " partition standard deviation: %2.2g\n", stddev);
+ }
+#endif
+ if (nummerged) {
+ qh_fprintf(fp, 9330," Number of distance tests for merging: %d\n",zzval_(Zbestdist)+
+ zzval_(Zcentrumtests)+zzval_(Zdistconvex)+zzval_(Zdistcheck)+
+ zzval_(Zdistzero));
+ qh_fprintf(fp, 9331," Number of distance tests for checking: %d\n",zzval_(Zcheckpart));
+ qh_fprintf(fp, 9332," Number of merged facets: %d\n", nummerged);
+ }
+ if (!qh RANDOMoutside && qh QHULLfinished) {
+ cpu= (float)qh hulltime;
+ cpu /= (float)qh_SECticks;
+ wval_(Wcpu)= cpu;
+ qh_fprintf(fp, 9333, " CPU seconds to compute hull (after input): %2.4g\n", cpu);
+ }
+ if (qh RERUN) {
+ if (!qh PREmerge && !qh MERGEexact)
+ qh_fprintf(fp, 9334, " Percentage of runs with precision errors: %4.1f\n",
+ zzval_(Zretry)*100.0/qh build_cnt); /* careful of order */
+ }else if (qh JOGGLEmax < REALmax/2) {
+ if (zzval_(Zretry))
+ qh_fprintf(fp, 9335, " After %d retries, input joggled by: %2.2g\n",
+ zzval_(Zretry), qh JOGGLEmax);
+ else
+ qh_fprintf(fp, 9336, " Input joggled by: %2.2g\n", qh JOGGLEmax);
+ }
+ if (qh totarea != 0.0)
+ qh_fprintf(fp, 9337, " %s facet area: %2.8g\n",
+ zzval_(Ztotmerge) ? "Approximate" : "Total", qh totarea);
+ if (qh totvol != 0.0)
+ qh_fprintf(fp, 9338, " %s volume: %2.8g\n",
+ zzval_(Ztotmerge) ? "Approximate" : "Total", qh totvol);
+ if (qh MERGING) {
+ qh_outerinner(NULL, &outerplane, &innerplane);
+ if (outerplane > 2 * qh DISTround) {
+ qh_fprintf(fp, 9339, " Maximum distance of %spoint above facet: %2.2g",
+ (qh QHULLfinished ? "" : "merged "), outerplane);
+ ratio= outerplane/(qh ONEmerge + qh DISTround);
+ /* don't report ratio if MINoutside is large */
+ if (ratio > 0.05 && 2* qh ONEmerge > qh MINoutside && qh JOGGLEmax > REALmax/2)
+ qh_fprintf(fp, 9340, " (%.1fx)\n", ratio);
+ else
+ qh_fprintf(fp, 9341, "\n");
+ }
+ if (innerplane < -2 * qh DISTround) {
+ qh_fprintf(fp, 9342, " Maximum distance of %svertex below facet: %2.2g",
+ (qh QHULLfinished ? "" : "merged "), innerplane);
+ ratio= -innerplane/(qh ONEmerge+qh DISTround);
+ if (ratio > 0.05 && qh JOGGLEmax > REALmax/2)
+ qh_fprintf(fp, 9343, " (%.1fx)\n", ratio);
+ else
+ qh_fprintf(fp, 9344, "\n");
+ }
+ }
+ qh_fprintf(fp, 9345, "\n");
+} /* printsummary */
+
+
diff --git a/src/libqhullr/libqhull_r.h b/src/libqhullr/libqhull_r.h
new file mode 100644
index 0000000..e2d61f2
--- /dev/null
+++ b/src/libqhullr/libqhull_r.h
@@ -0,0 +1,1100 @@
+/*
---------------------------------
+
+ libqhull.h
+ user-level header file for using qhull.a library
+
+ see qh-qhull.htm, qhull_a.h
+
+ Copyright (c) 1993-2012 The Geometry Center.
+ $Id: //main/2011/qhull/src/libqhullr/libqhull_r.h#1 $$Change: 1640 $
+ $DateTime: 2014/01/15 09:12:08 $$Author: bbarber $
+
+ NOTE: access to qh_qh is via the 'qh' macro. This allows
+ qh_qh to be either a pointer or a structure. An example
+ of using qh is "qh DROPdim" which accesses the DROPdim
+ field of qh_qh. Similarly, access to qh_qhstat is via
+ the 'qhstat' macro.
+
+ includes function prototypes for libqhull.c, geom.c, global.c, io.c, user.c
+
+ use mem.h for mem.c
+ use qset.h for qset.c
+
+ see unix.c for an example of using libqhull.h
+
+ recompile qhull if you change this file
+*/
+
+#ifndef qhDEFlibqhull
+#define qhDEFlibqhull 1
+
+/*=========================== -included files ==============*/
+
+#include "user.h" /* user definable constants (e.g., qh_QHpointer) */
+
+#include
---------------------------------
+
+ mem.c
+ memory management routines for qhull
+
+ This is a standalone program.
+
+ To initialize memory:
+
+ qh_meminit(stderr);
+ qh_meminitbuffers(qh IStracing, qh_MEMalign, 7, qh_MEMbufsize,qh_MEMinitbuf);
+ qh_memsize((int)sizeof(facetT));
+ qh_memsize((int)sizeof(facetT));
+ ...
+ qh_memsetup();
+
+ To free up all memory buffers:
+ qh_memfreeshort(&curlong, &totlong);
+
+ if qh_NOmem,
+ malloc/free is used instead of mem.c
+
+ notes:
+ uses Quickfit algorithm (freelists for commonly allocated sizes)
+ assumes small sizes for freelists (it discards the tail of memory buffers)
+
+ see:
+ qh-mem.htm and mem.h
+ global.c (qh_initbuffers) for an example of using mem.c
+
+ Copyright (c) 1993-2012 The Geometry Center.
+ $Id: //main/2011/qhull/src/libqhullr/mem_r.c#1 $$Change: 1640 $
+ $DateTime: 2014/01/15 09:12:08 $$Author: bbarber $
+*/
+
+#include "mem.h"
+#include
---------------------------------
+
+ mem.h
+ prototypes for memory management functions
+
+ see qh-mem.htm, mem.c and qset.h
+
+ for error handling, writes message and calls
+ qh_errexit(qhmem_ERRmem, NULL, NULL) if insufficient memory
+ and
+ qh_errexit(qhmem_ERRqhull, NULL, NULL) otherwise
+
+ Copyright (c) 1993-2012 The Geometry Center.
+ $Id: //main/2011/qhull/src/libqhullr/mem_r.h#1 $$Change: 1640 $
+ $DateTime: 2014/01/15 09:12:08 $$Author: bbarber $
+*/
+
+#ifndef qhDEFmem
+#define qhDEFmem 1
+
+#include
---------------------------------
+
+ merge.c
+ merges non-convex facets
+
+ see qh-merge.htm and merge.h
+
+ other modules call qh_premerge() and qh_postmerge()
+
+ the user may call qh_postmerge() to perform additional merges.
+
+ To remove deleted facets and vertices (qhull() in libqhull.c):
+ qh_partitionvisible(!qh_ALL, &numoutside); // visible_list, newfacet_list
+ qh_deletevisible(); // qh.visible_list
+ qh_resetlists(False, qh_RESETvisible); // qh.visible_list newvertex_list newfacet_list
+
+ assumes qh.CENTERtype= centrum
+
+ merges occur in qh_mergefacet and in qh_mergecycle
+ vertex->neighbors not set until the first merge occurs
+
+ Copyright (c) 1993-2012 C.B. Barber.
+ $Id: //main/2011/qhull/src/libqhullr/merge_r.c#1 $$Change: 1640 $
+ $DateTime: 2014/01/15 09:12:08 $$Author: bbarber $
+*/
+
+#include "qhull_a.h"
+
+#ifndef qh_NOmerge
+
+/*===== functions(alphabetical after premerge and postmerge) ======*/
+
+/*---------------------------------
+
+ qh_premerge( apex, maxcentrum )
+ pre-merge nonconvex facets in qh.newfacet_list for apex
+ maxcentrum defines coplanar and concave (qh_test_appendmerge)
+
+ returns:
+ deleted facets added to qh.visible_list with facet->visible set
+
+ notes:
+ uses globals, qh.MERGEexact, qh.PREmerge
+
+ design:
+ mark duplicate ridges in qh.newfacet_list
+ merge facet cycles in qh.newfacet_list
+ merge duplicate ridges and concave facets in qh.newfacet_list
+ check merged facet cycles for degenerate and redundant facets
+ merge degenerate and redundant facets
+ collect coplanar and concave facets
+ merge concave, coplanar, degenerate, and redundant facets
+*/
+void qh_premerge(vertexT *apex, realT maxcentrum, realT maxangle) {
+ boolT othermerge= False;
+ facetT *newfacet;
+
+ if (qh ZEROcentrum && qh_checkzero(!qh_ALL))
+ return;
+ trace2((qh ferr, 2008, "qh_premerge: premerge centrum %2.2g angle %2.2g for apex v%d facetlist f%d\n",
+ maxcentrum, maxangle, apex->id, getid_(qh newfacet_list)));
+ if (qh IStracing >= 4 && qh num_facets < 50)
+ qh_printlists();
+ qh centrum_radius= maxcentrum;
+ qh cos_max= maxangle;
+ qh degen_mergeset= qh_settemp(qh TEMPsize);
+ qh facet_mergeset= qh_settemp(qh TEMPsize);
+ if (qh hull_dim >=3) {
+ qh_mark_dupridges(qh newfacet_list); /* facet_mergeset */
+ qh_mergecycle_all(qh newfacet_list, &othermerge);
+ qh_forcedmerges(&othermerge /* qh facet_mergeset */);
+ FORALLnew_facets { /* test samecycle merges */
+ if (!newfacet->simplicial && !newfacet->mergeridge)
+ qh_degen_redundant_neighbors(newfacet, NULL);
+ }
+ if (qh_merge_degenredundant())
+ othermerge= True;
+ }else /* qh hull_dim == 2 */
+ qh_mergecycle_all(qh newfacet_list, &othermerge);
+ qh_flippedmerges(qh newfacet_list, &othermerge);
+ if (!qh MERGEexact || zzval_(Ztotmerge)) {
+ zinc_(Zpremergetot);
+ qh POSTmerging= False;
+ qh_getmergeset_initial(qh newfacet_list);
+ qh_all_merges(othermerge, False);
+ }
+ qh_settempfree(&qh facet_mergeset);
+ qh_settempfree(&qh degen_mergeset);
+} /* premerge */
+
+/*---------------------------------
+
+ qh_postmerge( reason, maxcentrum, maxangle, vneighbors )
+ post-merge nonconvex facets as defined by maxcentrum and maxangle
+ 'reason' is for reporting progress
+ if vneighbors,
+ calls qh_test_vneighbors at end of qh_all_merge
+ if firstmerge,
+ calls qh_reducevertices before qh_getmergeset
+
+ returns:
+ if first call (qh.visible_list != qh.facet_list),
+ builds qh.facet_newlist, qh.newvertex_list
+ deleted facets added to qh.visible_list with facet->visible
+ qh.visible_list == qh.facet_list
+
+ notes:
+
+
+ design:
+ if first call
+ set qh.visible_list and qh.newfacet_list to qh.facet_list
+ add all facets to qh.newfacet_list
+ mark non-simplicial facets, facet->newmerge
+ set qh.newvertext_list to qh.vertex_list
+ add all vertices to qh.newvertex_list
+ if a pre-merge occured
+ set vertex->delridge {will retest the ridge}
+ if qh.MERGEexact
+ call qh_reducevertices()
+ if no pre-merging
+ merge flipped facets
+ determine non-convex facets
+ merge all non-convex facets
+*/
+void qh_postmerge(const char *reason, realT maxcentrum, realT maxangle,
+ boolT vneighbors) {
+ facetT *newfacet;
+ boolT othermerges= False;
+ vertexT *vertex;
+
+ if (qh REPORTfreq || qh IStracing) {
+ qh_buildtracing(NULL, NULL);
+ qh_printsummary(qh ferr);
+ if (qh PRINTstatistics)
+ qh_printallstatistics(qh ferr, "reason");
+ qh_fprintf(qh ferr, 8062, "\n%s with 'C%.2g' and 'A%.2g'\n",
+ reason, maxcentrum, maxangle);
+ }
+ trace2((qh ferr, 2009, "qh_postmerge: postmerge. test vneighbors? %d\n",
+ vneighbors));
+ qh centrum_radius= maxcentrum;
+ qh cos_max= maxangle;
+ qh POSTmerging= True;
+ qh degen_mergeset= qh_settemp(qh TEMPsize);
+ qh facet_mergeset= qh_settemp(qh TEMPsize);
+ if (qh visible_list != qh facet_list) { /* first call */
+ qh NEWfacets= True;
+ qh visible_list= qh newfacet_list= qh facet_list;
+ FORALLnew_facets {
+ newfacet->newfacet= True;
+ if (!newfacet->simplicial)
+ newfacet->newmerge= True;
+ zinc_(Zpostfacets);
+ }
+ qh newvertex_list= qh vertex_list;
+ FORALLvertices
+ vertex->newlist= True;
+ if (qh VERTEXneighbors) { /* a merge has occurred */
+ FORALLvertices
+ vertex->delridge= True; /* test for redundant, needed? */
+ if (qh MERGEexact) {
+ if (qh hull_dim <= qh_DIMreduceBuild)
+ qh_reducevertices(); /* was skipped during pre-merging */
+ }
+ }
+ if (!qh PREmerge && !qh MERGEexact)
+ qh_flippedmerges(qh newfacet_list, &othermerges);
+ }
+ qh_getmergeset_initial(qh newfacet_list);
+ qh_all_merges(False, vneighbors);
+ qh_settempfree(&qh facet_mergeset);
+ qh_settempfree(&qh degen_mergeset);
+} /* post_merge */
+
+/*---------------------------------
+
+ qh_all_merges( othermerge, vneighbors )
+ merge all non-convex facets
+
+ set othermerge if already merged facets (for qh_reducevertices)
+ if vneighbors
+ tests vertex neighbors for convexity at end
+ qh.facet_mergeset lists the non-convex ridges in qh_newfacet_list
+ qh.degen_mergeset is defined
+ if qh.MERGEexact && !qh.POSTmerging,
+ does not merge coplanar facets
+
+ returns:
+ deleted facets added to qh.visible_list with facet->visible
+ deleted vertices added qh.delvertex_list with vertex->delvertex
+
+ notes:
+ unless !qh.MERGEindependent,
+ merges facets in independent sets
+ uses qh.newfacet_list as argument since merges call qh_removefacet()
+
+ design:
+ while merges occur
+ for each merge in qh.facet_mergeset
+ unless one of the facets was already merged in this pass
+ merge the facets
+ test merged facets for additional merges
+ add merges to qh.facet_mergeset
+ if vertices record neighboring facets
+ rename redundant vertices
+ update qh.facet_mergeset
+ if vneighbors ??
+ tests vertex neighbors for convexity at end
+*/
+void qh_all_merges(boolT othermerge, boolT vneighbors) {
+ facetT *facet1, *facet2;
+ mergeT *merge;
+ boolT wasmerge= True, isreduce;
+ void **freelistp; /* used !qh_NOmem */
+ vertexT *vertex;
+ mergeType mergetype;
+ int numcoplanar=0, numconcave=0, numdegenredun= 0, numnewmerges= 0;
+
+ trace2((qh ferr, 2010, "qh_all_merges: starting to merge facets beginning from f%d\n",
+ getid_(qh newfacet_list)));
+ while (True) {
+ wasmerge= False;
+ while (qh_setsize(qh facet_mergeset)) {
+ while ((merge= (mergeT*)qh_setdellast(qh facet_mergeset))) {
+ facet1= merge->facet1;
+ facet2= merge->facet2;
+ mergetype= merge->type;
+ qh_memfree_(merge, (int)sizeof(mergeT), freelistp);
+ if (facet1->visible || facet2->visible) /*deleted facet*/
+ continue;
+ if ((facet1->newfacet && !facet1->tested)
+ || (facet2->newfacet && !facet2->tested)) {
+ if (qh MERGEindependent && mergetype <= MRGanglecoplanar)
+ continue; /* perform independent sets of merges */
+ }
+ qh_merge_nonconvex(facet1, facet2, mergetype);
+ numdegenredun += qh_merge_degenredundant();
+ numnewmerges++;
+ wasmerge= True;
+ if (mergetype == MRGconcave)
+ numconcave++;
+ else /* MRGcoplanar or MRGanglecoplanar */
+ numcoplanar++;
+ } /* while setdellast */
+ if (qh POSTmerging && qh hull_dim <= qh_DIMreduceBuild
+ && numnewmerges > qh_MAXnewmerges) {
+ numnewmerges= 0;
+ qh_reducevertices(); /* otherwise large post merges too slow */
+ }
+ qh_getmergeset(qh newfacet_list); /* facet_mergeset */
+ } /* while mergeset */
+ if (qh VERTEXneighbors) {
+ isreduce= False;
+ if (qh hull_dim >=4 && qh POSTmerging) {
+ FORALLvertices
+ vertex->delridge= True;
+ isreduce= True;
+ }
+ if ((wasmerge || othermerge) && (!qh MERGEexact || qh POSTmerging)
+ && qh hull_dim <= qh_DIMreduceBuild) {
+ othermerge= False;
+ isreduce= True;
+ }
+ if (isreduce) {
+ if (qh_reducevertices()) {
+ qh_getmergeset(qh newfacet_list); /* facet_mergeset */
+ continue;
+ }
+ }
+ }
+ if (vneighbors && qh_test_vneighbors(/* qh newfacet_list */))
+ continue;
+ break;
+ } /* while (True) */
+ if (qh CHECKfrequently && !qh MERGEexact) {
+ qh old_randomdist= qh RANDOMdist;
+ qh RANDOMdist= False;
+ qh_checkconvex(qh newfacet_list, qh_ALGORITHMfault);
+ /* qh_checkconnect(); [this is slow and it changes the facet order] */
+ qh RANDOMdist= qh old_randomdist;
+ }
+ trace1((qh ferr, 1009, "qh_all_merges: merged %d coplanar facets %d concave facets and %d degen or redundant facets.\n",
+ numcoplanar, numconcave, numdegenredun));
+ if (qh IStracing >= 4 && qh num_facets < 50)
+ qh_printlists();
+} /* all_merges */
+
+
+/*---------------------------------
+
+ qh_appendmergeset( facet, neighbor, mergetype, angle )
+ appends an entry to qh.facet_mergeset or qh.degen_mergeset
+
+ angle ignored if NULL or !qh.ANGLEmerge
+
+ returns:
+ merge appended to facet_mergeset or degen_mergeset
+ sets ->degenerate or ->redundant if degen_mergeset
+
+ see:
+ qh_test_appendmerge()
+
+ design:
+ allocate merge entry
+ if regular merge
+ append to qh.facet_mergeset
+ else if degenerate merge and qh.facet_mergeset is all degenerate
+ append to qh.degen_mergeset
+ else if degenerate merge
+ prepend to qh.degen_mergeset
+ else if redundant merge
+ append to qh.degen_mergeset
+*/
+void qh_appendmergeset(facetT *facet, facetT *neighbor, mergeType mergetype, realT *angle) {
+ mergeT *merge, *lastmerge;
+ void **freelistp; /* used !qh_NOmem */
+
+ if (facet->redundant)
+ return;
+ if (facet->degenerate && mergetype == MRGdegen)
+ return;
+ qh_memalloc_((int)sizeof(mergeT), freelistp, merge, mergeT);
+ merge->facet1= facet;
+ merge->facet2= neighbor;
+ merge->type= mergetype;
+ if (angle && qh ANGLEmerge)
+ merge->angle= *angle;
+ if (mergetype < MRGdegen)
+ qh_setappend(&(qh facet_mergeset), merge);
+ else if (mergetype == MRGdegen) {
+ facet->degenerate= True;
+ if (!(lastmerge= (mergeT*)qh_setlast(qh degen_mergeset))
+ || lastmerge->type == MRGdegen)
+ qh_setappend(&(qh degen_mergeset), merge);
+ else
+ qh_setaddnth(&(qh degen_mergeset), 0, merge);
+ }else if (mergetype == MRGredundant) {
+ facet->redundant= True;
+ qh_setappend(&(qh degen_mergeset), merge);
+ }else /* mergetype == MRGmirror */ {
+ if (facet->redundant || neighbor->redundant) {
+ qh_fprintf(qh ferr, 6092, "qhull error (qh_appendmergeset): facet f%d or f%d is already a mirrored facet\n",
+ facet->id, neighbor->id);
+ qh_errexit2 (qh_ERRqhull, facet, neighbor);
+ }
+ if (!qh_setequal(facet->vertices, neighbor->vertices)) {
+ qh_fprintf(qh ferr, 6093, "qhull error (qh_appendmergeset): mirrored facets f%d and f%d do not have the same vertices\n",
+ facet->id, neighbor->id);
+ qh_errexit2 (qh_ERRqhull, facet, neighbor);
+ }
+ facet->redundant= True;
+ neighbor->redundant= True;
+ qh_setappend(&(qh degen_mergeset), merge);
+ }
+} /* appendmergeset */
+
+
+/*---------------------------------
+
+ qh_basevertices( samecycle )
+ return temporary set of base vertices for samecycle
+ samecycle is first facet in the cycle
+ assumes apex is SETfirst_( samecycle->vertices )
+
+ returns:
+ vertices(settemp)
+ all ->seen are cleared
+
+ notes:
+ uses qh_vertex_visit;
+
+ design:
+ for each facet in samecycle
+ for each unseen vertex in facet->vertices
+ append to result
+*/
+setT *qh_basevertices(facetT *samecycle) {
+ facetT *same;
+ vertexT *apex, *vertex, **vertexp;
+ setT *vertices= qh_settemp(qh TEMPsize);
+
+ apex= SETfirstt_(samecycle->vertices, vertexT);
+ apex->visitid= ++qh vertex_visit;
+ FORALLsame_cycle_(samecycle) {
+ if (same->mergeridge)
+ continue;
+ FOREACHvertex_(same->vertices) {
+ if (vertex->visitid != qh vertex_visit) {
+ qh_setappend(&vertices, vertex);
+ vertex->visitid= qh vertex_visit;
+ vertex->seen= False;
+ }
+ }
+ }
+ trace4((qh ferr, 4019, "qh_basevertices: found %d vertices\n",
+ qh_setsize(vertices)));
+ return vertices;
+} /* basevertices */
+
+/*---------------------------------
+
+ qh_checkconnect()
+ check that new facets are connected
+ new facets are on qh.newfacet_list
+
+ notes:
+ this is slow and it changes the order of the facets
+ uses qh.visit_id
+
+ design:
+ move first new facet to end of qh.facet_list
+ for all newly appended facets
+ append unvisited neighbors to end of qh.facet_list
+ for all new facets
+ report error if unvisited
+*/
+void qh_checkconnect(void /* qh newfacet_list */) {
+ facetT *facet, *newfacet, *errfacet= NULL, *neighbor, **neighborp;
+
+ facet= qh newfacet_list;
+ qh_removefacet(facet);
+ qh_appendfacet(facet);
+ facet->visitid= ++qh visit_id;
+ FORALLfacet_(facet) {
+ FOREACHneighbor_(facet) {
+ if (neighbor->visitid != qh visit_id) {
+ qh_removefacet(neighbor);
+ qh_appendfacet(neighbor);
+ neighbor->visitid= qh visit_id;
+ }
+ }
+ }
+ FORALLnew_facets {
+ if (newfacet->visitid == qh visit_id)
+ break;
+ qh_fprintf(qh ferr, 6094, "qhull error: f%d is not attached to the new facets\n",
+ newfacet->id);
+ errfacet= newfacet;
+ }
+ if (errfacet)
+ qh_errexit(qh_ERRqhull, errfacet, NULL);
+} /* checkconnect */
+
+/*---------------------------------
+
+ qh_checkzero( testall )
+ check that facets are clearly convex for qh.DISTround with qh.MERGEexact
+
+ if testall,
+ test all facets for qh.MERGEexact post-merging
+ else
+ test qh.newfacet_list
+
+ if qh.MERGEexact,
+ allows coplanar ridges
+ skips convexity test while qh.ZEROall_ok
+
+ returns:
+ True if all facets !flipped, !dupridge, normal
+ if all horizon facets are simplicial
+ if all vertices are clearly below neighbor
+ if all opposite vertices of horizon are below
+ clears qh.ZEROall_ok if any problems or coplanar facets
+
+ notes:
+ uses qh.vertex_visit
+ horizon facets may define multiple new facets
+
+ design:
+ for all facets in qh.newfacet_list or qh.facet_list
+ check for flagged faults (flipped, etc.)
+ for all facets in qh.newfacet_list or qh.facet_list
+ for each neighbor of facet
+ skip horizon facets for qh.newfacet_list
+ test the opposite vertex
+ if qh.newfacet_list
+ test the other vertices in the facet's horizon facet
+*/
+boolT qh_checkzero(boolT testall) {
+ facetT *facet, *neighbor, **neighborp;
+ facetT *horizon, *facetlist;
+ int neighbor_i;
+ vertexT *vertex, **vertexp;
+ realT dist;
+
+ if (testall)
+ facetlist= qh facet_list;
+ else {
+ facetlist= qh newfacet_list;
+ FORALLfacet_(facetlist) {
+ horizon= SETfirstt_(facet->neighbors, facetT);
+ if (!horizon->simplicial)
+ goto LABELproblem;
+ if (facet->flipped || facet->dupridge || !facet->normal)
+ goto LABELproblem;
+ }
+ if (qh MERGEexact && qh ZEROall_ok) {
+ trace2((qh ferr, 2011, "qh_checkzero: skip convexity check until first pre-merge\n"));
+ return True;
+ }
+ }
+ FORALLfacet_(facetlist) {
+ qh vertex_visit++;
+ neighbor_i= 0;
+ horizon= NULL;
+ FOREACHneighbor_(facet) {
+ if (!neighbor_i && !testall) {
+ horizon= neighbor;
+ neighbor_i++;
+ continue; /* horizon facet tested in qh_findhorizon */
+ }
+ vertex= SETelemt_(facet->vertices, neighbor_i++, vertexT);
+ vertex->visitid= qh vertex_visit;
+ zzinc_(Zdistzero);
+ qh_distplane(vertex->point, neighbor, &dist);
+ if (dist >= -qh DISTround) {
+ qh ZEROall_ok= False;
+ if (!qh MERGEexact || testall || dist > qh DISTround)
+ goto LABELnonconvex;
+ }
+ }
+ if (!testall) {
+ FOREACHvertex_(horizon->vertices) {
+ if (vertex->visitid != qh vertex_visit) {
+ zzinc_(Zdistzero);
+ qh_distplane(vertex->point, facet, &dist);
+ if (dist >= -qh DISTround) {
+ qh ZEROall_ok= False;
+ if (!qh MERGEexact || dist > qh DISTround)
+ goto LABELnonconvex;
+ }
+ break;
+ }
+ }
+ }
+ }
+ trace2((qh ferr, 2012, "qh_checkzero: testall %d, facets are %s\n", testall,
+ (qh MERGEexact && !testall) ?
+ "not concave, flipped, or duplicate ridged" : "clearly convex"));
+ return True;
+
+ LABELproblem:
+ qh ZEROall_ok= False;
+ trace2((qh ferr, 2013, "qh_checkzero: facet f%d needs pre-merging\n",
+ facet->id));
+ return False;
+
+ LABELnonconvex:
+ trace2((qh ferr, 2014, "qh_checkzero: facet f%d and f%d are not clearly convex. v%d dist %.2g\n",
+ facet->id, neighbor->id, vertex->id, dist));
+ return False;
+} /* checkzero */
+
+/*---------------------------------
+
+ qh_compareangle( angle1, angle2 )
+ used by qsort() to order merges by angle
+*/
+int qh_compareangle(const void *p1, const void *p2) {
+ const mergeT *a= *((mergeT *const*)p1), *b= *((mergeT *const*)p2);
+
+ return((a->angle > b->angle) ? 1 : -1);
+} /* compareangle */
+
+/*---------------------------------
+
+ qh_comparemerge( merge1, merge2 )
+ used by qsort() to order merges
+*/
+int qh_comparemerge(const void *p1, const void *p2) {
+ const mergeT *a= *((mergeT *const*)p1), *b= *((mergeT *const*)p2);
+
+ return(a->type - b->type);
+} /* comparemerge */
+
+/*---------------------------------
+
+ qh_comparevisit( vertex1, vertex2 )
+ used by qsort() to order vertices by their visitid
+*/
+int qh_comparevisit(const void *p1, const void *p2) {
+ const vertexT *a= *((vertexT *const*)p1), *b= *((vertexT *const*)p2);
+
+ return(a->visitid - b->visitid);
+} /* comparevisit */
+
+/*---------------------------------
+
+ qh_copynonconvex( atridge )
+ set non-convex flag on other ridges (if any) between same neighbors
+
+ notes:
+ may be faster if use smaller ridge set
+
+ design:
+ for each ridge of atridge's top facet
+ if ridge shares the same neighbor
+ set nonconvex flag
+*/
+void qh_copynonconvex(ridgeT *atridge) {
+ facetT *facet, *otherfacet;
+ ridgeT *ridge, **ridgep;
+
+ facet= atridge->top;
+ otherfacet= atridge->bottom;
+ FOREACHridge_(facet->ridges) {
+ if (otherfacet == otherfacet_(ridge, facet) && ridge != atridge) {
+ ridge->nonconvex= True;
+ trace4((qh ferr, 4020, "qh_copynonconvex: moved nonconvex flag from r%d to r%d\n",
+ atridge->id, ridge->id));
+ break;
+ }
+ }
+} /* copynonconvex */
+
+/*---------------------------------
+
+ qh_degen_redundant_facet( facet )
+ check facet for degen. or redundancy
+
+ notes:
+ bumps vertex_visit
+ called if a facet was redundant but no longer is (qh_merge_degenredundant)
+ qh_appendmergeset() only appends first reference to facet (i.e., redundant)
+
+ see:
+ qh_degen_redundant_neighbors()
+
+ design:
+ test for redundant neighbor
+ test for degenerate facet
+*/
+void qh_degen_redundant_facet(facetT *facet) {
+ vertexT *vertex, **vertexp;
+ facetT *neighbor, **neighborp;
+
+ trace4((qh ferr, 4021, "qh_degen_redundant_facet: test facet f%d for degen/redundant\n",
+ facet->id));
+ FOREACHneighbor_(facet) {
+ qh vertex_visit++;
+ FOREACHvertex_(neighbor->vertices)
+ vertex->visitid= qh vertex_visit;
+ FOREACHvertex_(facet->vertices) {
+ if (vertex->visitid != qh vertex_visit)
+ break;
+ }
+ if (!vertex) {
+ qh_appendmergeset(facet, neighbor, MRGredundant, NULL);
+ trace2((qh ferr, 2015, "qh_degen_redundant_facet: f%d is contained in f%d. merge\n", facet->id, neighbor->id));
+ return;
+ }
+ }
+ if (qh_setsize(facet->neighbors) < qh hull_dim) {
+ qh_appendmergeset(facet, facet, MRGdegen, NULL);
+ trace2((qh ferr, 2016, "qh_degen_redundant_neighbors: f%d is degenerate.\n", facet->id));
+ }
+} /* degen_redundant_facet */
+
+
+/*---------------------------------
+
+ qh_degen_redundant_neighbors( facet, delfacet, )
+ append degenerate and redundant neighbors to facet_mergeset
+ if delfacet,
+ only checks neighbors of both delfacet and facet
+ also checks current facet for degeneracy
+
+ notes:
+ bumps vertex_visit
+ called for each qh_mergefacet() and qh_mergecycle()
+ merge and statistics occur in merge_nonconvex
+ qh_appendmergeset() only appends first reference to facet (i.e., redundant)
+ it appends redundant facets after degenerate ones
+
+ a degenerate facet has fewer than hull_dim neighbors
+ a redundant facet's vertices is a subset of its neighbor's vertices
+ tests for redundant merges first (appendmergeset is nop for others)
+ in a merge, only needs to test neighbors of merged facet
+
+ see:
+ qh_merge_degenredundant() and qh_degen_redundant_facet()
+
+ design:
+ test for degenerate facet
+ test for redundant neighbor
+ test for degenerate neighbor
+*/
+void qh_degen_redundant_neighbors(facetT *facet, facetT *delfacet) {
+ vertexT *vertex, **vertexp;
+ facetT *neighbor, **neighborp;
+ int size;
+
+ trace4((qh ferr, 4022, "qh_degen_redundant_neighbors: test neighbors of f%d with delfacet f%d\n",
+ facet->id, getid_(delfacet)));
+ if ((size= qh_setsize(facet->neighbors)) < qh hull_dim) {
+ qh_appendmergeset(facet, facet, MRGdegen, NULL);
+ trace2((qh ferr, 2017, "qh_degen_redundant_neighbors: f%d is degenerate with %d neighbors.\n", facet->id, size));
+ }
+ if (!delfacet)
+ delfacet= facet;
+ qh vertex_visit++;
+ FOREACHvertex_(facet->vertices)
+ vertex->visitid= qh vertex_visit;
+ FOREACHneighbor_(delfacet) {
+ /* uses early out instead of checking vertex count */
+ if (neighbor == facet)
+ continue;
+ FOREACHvertex_(neighbor->vertices) {
+ if (vertex->visitid != qh vertex_visit)
+ break;
+ }
+ if (!vertex) {
+ qh_appendmergeset(neighbor, facet, MRGredundant, NULL);
+ trace2((qh ferr, 2018, "qh_degen_redundant_neighbors: f%d is contained in f%d. merge\n", neighbor->id, facet->id));
+ }
+ }
+ FOREACHneighbor_(delfacet) { /* redundant merges occur first */
+ if (neighbor == facet)
+ continue;
+ if ((size= qh_setsize(neighbor->neighbors)) < qh hull_dim) {
+ qh_appendmergeset(neighbor, neighbor, MRGdegen, NULL);
+ trace2((qh ferr, 2019, "qh_degen_redundant_neighbors: f%d is degenerate with %d neighbors. Neighbor of f%d.\n", neighbor->id, size, facet->id));
+ }
+ }
+} /* degen_redundant_neighbors */
+
+
+/*---------------------------------
+
+ qh_find_newvertex( oldvertex, vertices, ridges )
+ locate new vertex for renaming old vertex
+ vertices is a set of possible new vertices
+ vertices sorted by number of deleted ridges
+
+ returns:
+ newvertex or NULL
+ each ridge includes both vertex and oldvertex
+ vertices sorted by number of deleted ridges
+
+ notes:
+ modifies vertex->visitid
+ new vertex is in one of the ridges
+ renaming will not cause a duplicate ridge
+ renaming will minimize the number of deleted ridges
+ newvertex may not be adjacent in the dual (though unlikely)
+
+ design:
+ for each vertex in vertices
+ set vertex->visitid to number of references in ridges
+ remove unvisited vertices
+ set qh.vertex_visit above all possible values
+ sort vertices by number of references in ridges
+ add each ridge to qh.hash_table
+ for each vertex in vertices
+ look for a vertex that would not cause a duplicate ridge after a rename
+*/
+vertexT *qh_find_newvertex(vertexT *oldvertex, setT *vertices, setT *ridges) {
+ vertexT *vertex, **vertexp;
+ setT *newridges;
+ ridgeT *ridge, **ridgep;
+ int size, hashsize;
+ int hash;
+
+#ifndef qh_NOtrace
+ if (qh IStracing >= 4) {
+ qh_fprintf(qh ferr, 8063, "qh_find_newvertex: find new vertex for v%d from ",
+ oldvertex->id);
+ FOREACHvertex_(vertices)
+ qh_fprintf(qh ferr, 8064, "v%d ", vertex->id);
+ FOREACHridge_(ridges)
+ qh_fprintf(qh ferr, 8065, "r%d ", ridge->id);
+ qh_fprintf(qh ferr, 8066, "\n");
+ }
+#endif
+ FOREACHvertex_(vertices)
+ vertex->visitid= 0;
+ FOREACHridge_(ridges) {
+ FOREACHvertex_(ridge->vertices)
+ vertex->visitid++;
+ }
+ FOREACHvertex_(vertices) {
+ if (!vertex->visitid) {
+ qh_setdelnth(vertices, SETindex_(vertices,vertex));
+ vertexp--; /* repeat since deleted this vertex */
+ }
+ }
+ qh vertex_visit += (unsigned int)qh_setsize(ridges);
+ if (!qh_setsize(vertices)) {
+ trace4((qh ferr, 4023, "qh_find_newvertex: vertices not in ridges for v%d\n",
+ oldvertex->id));
+ return NULL;
+ }
+ qsort(SETaddr_(vertices, vertexT), (size_t)qh_setsize(vertices),
+ sizeof(vertexT *), qh_comparevisit);
+ /* can now use qh vertex_visit */
+ if (qh PRINTstatistics) {
+ size= qh_setsize(vertices);
+ zinc_(Zintersect);
+ zadd_(Zintersecttot, size);
+ zmax_(Zintersectmax, size);
+ }
+ hashsize= qh_newhashtable(qh_setsize(ridges));
+ FOREACHridge_(ridges)
+ qh_hashridge(qh hash_table, hashsize, ridge, oldvertex);
+ FOREACHvertex_(vertices) {
+ newridges= qh_vertexridges(vertex);
+ FOREACHridge_(newridges) {
+ if (qh_hashridge_find(qh hash_table, hashsize, ridge, vertex, oldvertex, &hash)) {
+ zinc_(Zdupridge);
+ break;
+ }
+ }
+ qh_settempfree(&newridges);
+ if (!ridge)
+ break; /* found a rename */
+ }
+ if (vertex) {
+ /* counted in qh_renamevertex */
+ trace2((qh ferr, 2020, "qh_find_newvertex: found v%d for old v%d from %d vertices and %d ridges.\n",
+ vertex->id, oldvertex->id, qh_setsize(vertices), qh_setsize(ridges)));
+ }else {
+ zinc_(Zfindfail);
+ trace0((qh ferr, 14, "qh_find_newvertex: no vertex for renaming v%d(all duplicated ridges) during p%d\n",
+ oldvertex->id, qh furthest_id));
+ }
+ qh_setfree(&qh hash_table);
+ return vertex;
+} /* find_newvertex */
+
+/*---------------------------------
+
+ qh_findbest_test( testcentrum, facet, neighbor, bestfacet, dist, mindist, maxdist )
+ test neighbor of facet for qh_findbestneighbor()
+ if testcentrum,
+ tests centrum (assumes it is defined)
+ else
+ tests vertices
+
+ returns:
+ if a better facet (i.e., vertices/centrum of facet closer to neighbor)
+ updates bestfacet, dist, mindist, and maxdist
+*/
+void qh_findbest_test(boolT testcentrum, facetT *facet, facetT *neighbor,
+ facetT **bestfacet, realT *distp, realT *mindistp, realT *maxdistp) {
+ realT dist, mindist, maxdist;
+
+ if (testcentrum) {
+ zzinc_(Zbestdist);
+ qh_distplane(facet->center, neighbor, &dist);
+ dist *= qh hull_dim; /* estimate furthest vertex */
+ if (dist < 0) {
+ maxdist= 0;
+ mindist= dist;
+ dist= -dist;
+ }else {
+ mindist= 0;
+ maxdist= dist;
+ }
+ }else
+ dist= qh_getdistance(facet, neighbor, &mindist, &maxdist);
+ if (dist < *distp) {
+ *bestfacet= neighbor;
+ *mindistp= mindist;
+ *maxdistp= maxdist;
+ *distp= dist;
+ }
+} /* findbest_test */
+
+/*---------------------------------
+
+ qh_findbestneighbor( facet, dist, mindist, maxdist )
+ finds best neighbor (least dist) of a facet for merging
+
+ returns:
+ returns min and max distances and their max absolute value
+
+ notes:
+ avoids merging old into new
+ assumes ridge->nonconvex only set on one ridge between a pair of facets
+ could use an early out predicate but not worth it
+
+ design:
+ if a large facet
+ will test centrum
+ else
+ will test vertices
+ if a large facet
+ test nonconvex neighbors for best merge
+ else
+ test all neighbors for the best merge
+ if testing centrum
+ get distance information
+*/
+facetT *qh_findbestneighbor(facetT *facet, realT *distp, realT *mindistp, realT *maxdistp) {
+ facetT *neighbor, **neighborp, *bestfacet= NULL;
+ ridgeT *ridge, **ridgep;
+ boolT nonconvex= True, testcentrum= False;
+ int size= qh_setsize(facet->vertices);
+
+ *distp= REALmax;
+ if (size > qh_BESTcentrum2 * qh hull_dim + qh_BESTcentrum) {
+ testcentrum= True;
+ zinc_(Zbestcentrum);
+ if (!facet->center)
+ facet->center= qh_getcentrum(facet);
+ }
+ if (size > qh hull_dim + qh_BESTnonconvex) {
+ FOREACHridge_(facet->ridges) {
+ if (ridge->nonconvex) {
+ neighbor= otherfacet_(ridge, facet);
+ qh_findbest_test(testcentrum, facet, neighbor,
+ &bestfacet, distp, mindistp, maxdistp);
+ }
+ }
+ }
+ if (!bestfacet) {
+ nonconvex= False;
+ FOREACHneighbor_(facet)
+ qh_findbest_test(testcentrum, facet, neighbor,
+ &bestfacet, distp, mindistp, maxdistp);
+ }
+ if (!bestfacet) {
+ qh_fprintf(qh ferr, 6095, "qhull internal error (qh_findbestneighbor): no neighbors for f%d\n", facet->id);
+
+ qh_errexit(qh_ERRqhull, facet, NULL);
+ }
+ if (testcentrum)
+ qh_getdistance(facet, bestfacet, mindistp, maxdistp);
+ trace3((qh ferr, 3002, "qh_findbestneighbor: f%d is best neighbor for f%d testcentrum? %d nonconvex? %d dist %2.2g min %2.2g max %2.2g\n",
+ bestfacet->id, facet->id, testcentrum, nonconvex, *distp, *mindistp, *maxdistp));
+ return(bestfacet);
+} /* findbestneighbor */
+
+
+/*---------------------------------
+
+ qh_flippedmerges( facetlist, wasmerge )
+ merge flipped facets into best neighbor
+ assumes qh.facet_mergeset at top of temporary stack
+
+ returns:
+ no flipped facets on facetlist
+ sets wasmerge if merge occurred
+ degen/redundant merges passed through
+
+ notes:
+ othermerges not needed since qh.facet_mergeset is empty before & after
+ keep it in case of change
+
+ design:
+ append flipped facets to qh.facetmergeset
+ for each flipped merge
+ find best neighbor
+ merge facet into neighbor
+ merge degenerate and redundant facets
+ remove flipped merges from qh.facet_mergeset
+*/
+void qh_flippedmerges(facetT *facetlist, boolT *wasmerge) {
+ facetT *facet, *neighbor, *facet1;
+ realT dist, mindist, maxdist;
+ mergeT *merge, **mergep;
+ setT *othermerges;
+ int nummerge=0;
+
+ trace4((qh ferr, 4024, "qh_flippedmerges: begin\n"));
+ FORALLfacet_(facetlist) {
+ if (facet->flipped && !facet->visible)
+ qh_appendmergeset(facet, facet, MRGflip, NULL);
+ }
+ othermerges= qh_settemppop(); /* was facet_mergeset */
+ qh facet_mergeset= qh_settemp(qh TEMPsize);
+ qh_settemppush(othermerges);
+ FOREACHmerge_(othermerges) {
+ facet1= merge->facet1;
+ if (merge->type != MRGflip || facet1->visible)
+ continue;
+ if (qh TRACEmerge-1 == zzval_(Ztotmerge))
+ qhmem.IStracing= qh IStracing= qh TRACElevel;
+ neighbor= qh_findbestneighbor(facet1, &dist, &mindist, &maxdist);
+ trace0((qh ferr, 15, "qh_flippedmerges: merge flipped f%d into f%d dist %2.2g during p%d\n",
+ facet1->id, neighbor->id, dist, qh furthest_id));
+ qh_mergefacet(facet1, neighbor, &mindist, &maxdist, !qh_MERGEapex);
+ nummerge++;
+ if (qh PRINTstatistics) {
+ zinc_(Zflipped);
+ wadd_(Wflippedtot, dist);
+ wmax_(Wflippedmax, dist);
+ }
+ qh_merge_degenredundant();
+ }
+ FOREACHmerge_(othermerges) {
+ if (merge->facet1->visible || merge->facet2->visible)
+ qh_memfree(merge, (int)sizeof(mergeT));
+ else
+ qh_setappend(&qh facet_mergeset, merge);
+ }
+ qh_settempfree(&othermerges);
+ if (nummerge)
+ *wasmerge= True;
+ trace1((qh ferr, 1010, "qh_flippedmerges: merged %d flipped facets into a good neighbor\n", nummerge));
+} /* flippedmerges */
+
+
+/*---------------------------------
+
+ qh_forcedmerges( wasmerge )
+ merge duplicated ridges
+
+ returns:
+ removes all duplicate ridges on facet_mergeset
+ wasmerge set if merge
+ qh.facet_mergeset may include non-forced merges(none for now)
+ qh.degen_mergeset includes degen/redun merges
+
+ notes:
+ duplicate ridges occur when the horizon is pinched,
+ i.e. a subridge occurs in more than two horizon ridges.
+ could rename vertices that pinch the horizon
+ assumes qh_merge_degenredundant() has not be called
+ othermerges isn't needed since facet_mergeset is empty afterwards
+ keep it in case of change
+
+ design:
+ for each duplicate ridge
+ find current facets by chasing f.replace links
+ determine best direction for facet
+ merge one facet into the other
+ remove duplicate ridges from qh.facet_mergeset
+*/
+void qh_forcedmerges(boolT *wasmerge) {
+ facetT *facet1, *facet2;
+ mergeT *merge, **mergep;
+ realT dist1, dist2, mindist1, mindist2, maxdist1, maxdist2;
+ setT *othermerges;
+ int nummerge=0, numflip=0;
+
+ if (qh TRACEmerge-1 == zzval_(Ztotmerge))
+ qhmem.IStracing= qh IStracing= qh TRACElevel;
+ trace4((qh ferr, 4025, "qh_forcedmerges: begin\n"));
+ othermerges= qh_settemppop(); /* was facet_mergeset */
+ qh facet_mergeset= qh_settemp(qh TEMPsize);
+ qh_settemppush(othermerges);
+ FOREACHmerge_(othermerges) {
+ if (merge->type != MRGridge)
+ continue;
+ facet1= merge->facet1;
+ facet2= merge->facet2;
+ while (facet1->visible) /* must exist, no qh_merge_degenredunant */
+ facet1= facet1->f.replace; /* previously merged facet */
+ while (facet2->visible)
+ facet2= facet2->f.replace; /* previously merged facet */
+ if (facet1 == facet2)
+ continue;
+ if (!qh_setin(facet2->neighbors, facet1)) {
+ qh_fprintf(qh ferr, 6096, "qhull internal error (qh_forcedmerges): f%d and f%d had a duplicate ridge but as f%d and f%d they are no longer neighbors\n",
+ merge->facet1->id, merge->facet2->id, facet1->id, facet2->id);
+ qh_errexit2 (qh_ERRqhull, facet1, facet2);
+ }
+ if (qh TRACEmerge-1 == zzval_(Ztotmerge))
+ qhmem.IStracing= qh IStracing= qh TRACElevel;
+ dist1= qh_getdistance(facet1, facet2, &mindist1, &maxdist1);
+ dist2= qh_getdistance(facet2, facet1, &mindist2, &maxdist2);
+ trace0((qh ferr, 16, "qh_forcedmerges: duplicate ridge between f%d and f%d, dist %2.2g and reverse dist %2.2g during p%d\n",
+ facet1->id, facet2->id, dist1, dist2, qh furthest_id));
+ if (dist1 < dist2)
+ qh_mergefacet(facet1, facet2, &mindist1, &maxdist1, !qh_MERGEapex);
+ else {
+ qh_mergefacet(facet2, facet1, &mindist2, &maxdist2, !qh_MERGEapex);
+ dist1= dist2;
+ facet1= facet2;
+ }
+ if (facet1->flipped) {
+ zinc_(Zmergeflipdup);
+ numflip++;
+ }else
+ nummerge++;
+ if (qh PRINTstatistics) {
+ zinc_(Zduplicate);
+ wadd_(Wduplicatetot, dist1);
+ wmax_(Wduplicatemax, dist1);
+ }
+ }
+ FOREACHmerge_(othermerges) {
+ if (merge->type == MRGridge)
+ qh_memfree(merge, (int)sizeof(mergeT));
+ else
+ qh_setappend(&qh facet_mergeset, merge);
+ }
+ qh_settempfree(&othermerges);
+ if (nummerge)
+ *wasmerge= True;
+ trace1((qh ferr, 1011, "qh_forcedmerges: merged %d facets and %d flipped facets across duplicated ridges\n",
+ nummerge, numflip));
+} /* forcedmerges */
+
+
+/*---------------------------------
+
+ qh_getmergeset( facetlist )
+ determines nonconvex facets on facetlist
+ tests !tested ridges and nonconvex ridges of !tested facets
+
+ returns:
+ returns sorted qh.facet_mergeset of facet-neighbor pairs to be merged
+ all ridges tested
+
+ notes:
+ assumes no nonconvex ridges with both facets tested
+ uses facet->tested/ridge->tested to prevent duplicate tests
+ can not limit tests to modified ridges since the centrum changed
+ uses qh.visit_id
+
+ see:
+ qh_getmergeset_initial()
+
+ design:
+ for each facet on facetlist
+ for each ridge of facet
+ if untested ridge
+ test ridge for convexity
+ if non-convex
+ append ridge to qh.facet_mergeset
+ sort qh.facet_mergeset by angle
+*/
+void qh_getmergeset(facetT *facetlist) {
+ facetT *facet, *neighbor, **neighborp;
+ ridgeT *ridge, **ridgep;
+ int nummerges;
+
+ nummerges= qh_setsize(qh facet_mergeset);
+ trace4((qh ferr, 4026, "qh_getmergeset: started.\n"));
+ qh visit_id++;
+ FORALLfacet_(facetlist) {
+ if (facet->tested)
+ continue;
+ facet->visitid= qh visit_id;
+ facet->tested= True; /* must be non-simplicial due to merge */
+ FOREACHneighbor_(facet)
+ neighbor->seen= False;
+ FOREACHridge_(facet->ridges) {
+ if (ridge->tested && !ridge->nonconvex)
+ continue;
+ /* if tested & nonconvex, need to append merge */
+ neighbor= otherfacet_(ridge, facet);
+ if (neighbor->seen) {
+ ridge->tested= True;
+ ridge->nonconvex= False;
+ }else if (neighbor->visitid != qh visit_id) {
+ ridge->tested= True;
+ ridge->nonconvex= False;
+ neighbor->seen= True; /* only one ridge is marked nonconvex */
+ if (qh_test_appendmerge(facet, neighbor))
+ ridge->nonconvex= True;
+ }
+ }
+ }
+ nummerges= qh_setsize(qh facet_mergeset);
+ if (qh ANGLEmerge)
+ qsort(SETaddr_(qh facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_compareangle);
+ else
+ qsort(SETaddr_(qh facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_comparemerge);
+ if (qh POSTmerging) {
+ zadd_(Zmergesettot2, nummerges);
+ }else {
+ zadd_(Zmergesettot, nummerges);
+ zmax_(Zmergesetmax, nummerges);
+ }
+ trace2((qh ferr, 2021, "qh_getmergeset: %d merges found\n", nummerges));
+} /* getmergeset */
+
+
+/*---------------------------------
+
+ qh_getmergeset_initial( facetlist )
+ determine initial qh.facet_mergeset for facets
+ tests all facet/neighbor pairs on facetlist
+
+ returns:
+ sorted qh.facet_mergeset with nonconvex ridges
+ sets facet->tested, ridge->tested, and ridge->nonconvex
+
+ notes:
+ uses visit_id, assumes ridge->nonconvex is False
+
+ see:
+ qh_getmergeset()
+
+ design:
+ for each facet on facetlist
+ for each untested neighbor of facet
+ test facet and neighbor for convexity
+ if non-convex
+ append merge to qh.facet_mergeset
+ mark one of the ridges as nonconvex
+ sort qh.facet_mergeset by angle
+*/
+void qh_getmergeset_initial(facetT *facetlist) {
+ facetT *facet, *neighbor, **neighborp;
+ ridgeT *ridge, **ridgep;
+ int nummerges;
+
+ qh visit_id++;
+ FORALLfacet_(facetlist) {
+ facet->visitid= qh visit_id;
+ facet->tested= True;
+ FOREACHneighbor_(facet) {
+ if (neighbor->visitid != qh visit_id) {
+ if (qh_test_appendmerge(facet, neighbor)) {
+ FOREACHridge_(neighbor->ridges) {
+ if (facet == otherfacet_(ridge, neighbor)) {
+ ridge->nonconvex= True;
+ break; /* only one ridge is marked nonconvex */
+ }
+ }
+ }
+ }
+ }
+ FOREACHridge_(facet->ridges)
+ ridge->tested= True;
+ }
+ nummerges= qh_setsize(qh facet_mergeset);
+ if (qh ANGLEmerge)
+ qsort(SETaddr_(qh facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_compareangle);
+ else
+ qsort(SETaddr_(qh facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_comparemerge);
+ if (qh POSTmerging) {
+ zadd_(Zmergeinittot2, nummerges);
+ }else {
+ zadd_(Zmergeinittot, nummerges);
+ zmax_(Zmergeinitmax, nummerges);
+ }
+ trace2((qh ferr, 2022, "qh_getmergeset_initial: %d merges found\n", nummerges));
+} /* getmergeset_initial */
+
+
+/*---------------------------------
+
+ qh_hashridge( hashtable, hashsize, ridge, oldvertex )
+ add ridge to hashtable without oldvertex
+
+ notes:
+ assumes hashtable is large enough
+
+ design:
+ determine hash value for ridge without oldvertex
+ find next empty slot for ridge
+*/
+void qh_hashridge(setT *hashtable, int hashsize, ridgeT *ridge, vertexT *oldvertex) {
+ int hash;
+ ridgeT *ridgeA;
+
+ hash= qh_gethash(hashsize, ridge->vertices, qh hull_dim-1, 0, oldvertex);
+ while (True) {
+ if (!(ridgeA= SETelemt_(hashtable, hash, ridgeT))) {
+ SETelem_(hashtable, hash)= ridge;
+ break;
+ }else if (ridgeA == ridge)
+ break;
+ if (++hash == hashsize)
+ hash= 0;
+ }
+} /* hashridge */
+
+
+/*---------------------------------
+
+ qh_hashridge_find( hashtable, hashsize, ridge, vertex, oldvertex, hashslot )
+ returns matching ridge without oldvertex in hashtable
+ for ridge without vertex
+ if oldvertex is NULL
+ matches with any one skip
+
+ returns:
+ matching ridge or NULL
+ if no match,
+ if ridge already in table
+ hashslot= -1
+ else
+ hashslot= next NULL index
+
+ notes:
+ assumes hashtable is large enough
+ can't match ridge to itself
+
+ design:
+ get hash value for ridge without vertex
+ for each hashslot
+ return match if ridge matches ridgeA without oldvertex
+*/
+ridgeT *qh_hashridge_find(setT *hashtable, int hashsize, ridgeT *ridge,
+ vertexT *vertex, vertexT *oldvertex, int *hashslot) {
+ int hash;
+ ridgeT *ridgeA;
+
+ *hashslot= 0;
+ zinc_(Zhashridge);
+ hash= qh_gethash(hashsize, ridge->vertices, qh hull_dim-1, 0, vertex);
+ while ((ridgeA= SETelemt_(hashtable, hash, ridgeT))) {
+ if (ridgeA == ridge)
+ *hashslot= -1;
+ else {
+ zinc_(Zhashridgetest);
+ if (qh_setequal_except(ridge->vertices, vertex, ridgeA->vertices, oldvertex))
+ return ridgeA;
+ }
+ if (++hash == hashsize)
+ hash= 0;
+ }
+ if (!*hashslot)
+ *hashslot= hash;
+ return NULL;
+} /* hashridge_find */
+
+
+/*---------------------------------
+
+ qh_makeridges( facet )
+ creates explicit ridges between simplicial facets
+
+ returns:
+ facet with ridges and without qh_MERGEridge
+ ->simplicial is False
+
+ notes:
+ allows qh_MERGEridge flag
+ uses existing ridges
+ duplicate neighbors ok if ridges already exist (qh_mergecycle_ridges)
+
+ see:
+ qh_mergecycle_ridges()
+
+ design:
+ look for qh_MERGEridge neighbors
+ mark neighbors that already have ridges
+ for each unprocessed neighbor of facet
+ create a ridge for neighbor and facet
+ if any qh_MERGEridge neighbors
+ delete qh_MERGEridge flags (already handled by qh_mark_dupridges)
+*/
+void qh_makeridges(facetT *facet) {
+ facetT *neighbor, **neighborp;
+ ridgeT *ridge, **ridgep;
+ int neighbor_i, neighbor_n;
+ boolT toporient, mergeridge= False;
+
+ if (!facet->simplicial)
+ return;
+ trace4((qh ferr, 4027, "qh_makeridges: make ridges for f%d\n", facet->id));
+ facet->simplicial= False;
+ FOREACHneighbor_(facet) {
+ if (neighbor == qh_MERGEridge)
+ mergeridge= True;
+ else
+ neighbor->seen= False;
+ }
+ FOREACHridge_(facet->ridges)
+ otherfacet_(ridge, facet)->seen= True;
+ FOREACHneighbor_i_(facet) {
+ if (neighbor == qh_MERGEridge)
+ continue; /* fixed by qh_mark_dupridges */
+ else if (!neighbor->seen) { /* no current ridges */
+ ridge= qh_newridge();
+ ridge->vertices= qh_setnew_delnthsorted(facet->vertices, qh hull_dim,
+ neighbor_i, 0);
+ toporient= facet->toporient ^ (neighbor_i & 0x1);
+ if (toporient) {
+ ridge->top= facet;
+ ridge->bottom= neighbor;
+ }else {
+ ridge->top= neighbor;
+ ridge->bottom= facet;
+ }
+#if 0 /* this also works */
+ flip= (facet->toporient ^ neighbor->toporient)^(skip1 & 0x1) ^ (skip2 & 0x1);
+ if (facet->toporient ^ (skip1 & 0x1) ^ flip) {
+ ridge->top= neighbor;
+ ridge->bottom= facet;
+ }else {
+ ridge->top= facet;
+ ridge->bottom= neighbor;
+ }
+#endif
+ qh_setappend(&(facet->ridges), ridge);
+ qh_setappend(&(neighbor->ridges), ridge);
+ }
+ }
+ if (mergeridge) {
+ while (qh_setdel(facet->neighbors, qh_MERGEridge))
+ ; /* delete each one */
+ }
+} /* makeridges */
+
+
+/*---------------------------------
+
+ qh_mark_dupridges( facetlist )
+ add duplicated ridges to qh.facet_mergeset
+ facet->dupridge is true
+
+ returns:
+ duplicate ridges on qh.facet_mergeset
+ ->mergeridge/->mergeridge2 set
+ duplicate ridges marked by qh_MERGEridge and both sides facet->dupridge
+ no MERGEridges in neighbor sets
+
+ notes:
+ duplicate ridges occur when the horizon is pinched,
+ i.e. a subridge occurs in more than two horizon ridges.
+ could rename vertices that pinch the horizon
+ uses qh.visit_id
+
+ design:
+ for all facets on facetlist
+ if facet contains a duplicate ridge
+ for each neighbor of facet
+ if neighbor marked qh_MERGEridge (one side of the merge)
+ set facet->mergeridge
+ else
+ if neighbor contains a duplicate ridge
+ and the back link is qh_MERGEridge
+ append duplicate ridge to qh.facet_mergeset
+ for each duplicate ridge
+ make ridge sets in preparation for merging
+ remove qh_MERGEridge from neighbor set
+ for each duplicate ridge
+ restore the missing neighbor from the neighbor set that was qh_MERGEridge
+ add the missing ridge for this neighbor
+*/
+void qh_mark_dupridges(facetT *facetlist) {
+ facetT *facet, *neighbor, **neighborp;
+ int nummerge=0;
+ mergeT *merge, **mergep;
+
+
+ trace4((qh ferr, 4028, "qh_mark_dupridges: identify duplicate ridges\n"));
+ FORALLfacet_(facetlist) {
+ if (facet->dupridge) {
+ FOREACHneighbor_(facet) {
+ if (neighbor == qh_MERGEridge) {
+ facet->mergeridge= True;
+ continue;
+ }
+ if (neighbor->dupridge
+ && !qh_setin(neighbor->neighbors, facet)) { /* qh_MERGEridge */
+ qh_appendmergeset(facet, neighbor, MRGridge, NULL);
+ facet->mergeridge2= True;
+ facet->mergeridge= True;
+ nummerge++;
+ }
+ }
+ }
+ }
+ if (!nummerge)
+ return;
+ FORALLfacet_(facetlist) { /* gets rid of qh_MERGEridge */
+ if (facet->mergeridge && !facet->mergeridge2)
+ qh_makeridges(facet);
+ }
+ FOREACHmerge_(qh facet_mergeset) { /* restore the missing neighbors */
+ if (merge->type == MRGridge) {
+ qh_setappend(&merge->facet2->neighbors, merge->facet1);
+ qh_makeridges(merge->facet1); /* and the missing ridges */
+ }
+ }
+ trace1((qh ferr, 1012, "qh_mark_dupridges: found %d duplicated ridges\n",
+ nummerge));
+} /* mark_dupridges */
+
+/*---------------------------------
+
+ qh_maydropneighbor( facet )
+ drop neighbor relationship if no ridge between facet and neighbor
+
+ returns:
+ neighbor sets updated
+ appends degenerate facets to qh.facet_mergeset
+
+ notes:
+ won't cause redundant facets since vertex inclusion is the same
+ may drop vertex and neighbor if no ridge
+ uses qh.visit_id
+
+ design:
+ visit all neighbors with ridges
+ for each unvisited neighbor of facet
+ delete neighbor and facet from the neighbor sets
+ if neighbor becomes degenerate
+ append neighbor to qh.degen_mergeset
+ if facet is degenerate
+ append facet to qh.degen_mergeset
+*/
+void qh_maydropneighbor(facetT *facet) {
+ ridgeT *ridge, **ridgep;
+ realT angledegen= qh_ANGLEdegen;
+ facetT *neighbor, **neighborp;
+
+ qh visit_id++;
+ trace4((qh ferr, 4029, "qh_maydropneighbor: test f%d for no ridges to a neighbor\n",
+ facet->id));
+ FOREACHridge_(facet->ridges) {
+ ridge->top->visitid= qh visit_id;
+ ridge->bottom->visitid= qh visit_id;
+ }
+ FOREACHneighbor_(facet) {
+ if (neighbor->visitid != qh visit_id) {
+ trace0((qh ferr, 17, "qh_maydropneighbor: facets f%d and f%d are no longer neighbors during p%d\n",
+ facet->id, neighbor->id, qh furthest_id));
+ zinc_(Zdropneighbor);
+ qh_setdel(facet->neighbors, neighbor);
+ neighborp--; /* repeat, deleted a neighbor */
+ qh_setdel(neighbor->neighbors, facet);
+ if (qh_setsize(neighbor->neighbors) < qh hull_dim) {
+ zinc_(Zdropdegen);
+ qh_appendmergeset(neighbor, neighbor, MRGdegen, &angledegen);
+ trace2((qh ferr, 2023, "qh_maydropneighbors: f%d is degenerate.\n", neighbor->id));
+ }
+ }
+ }
+ if (qh_setsize(facet->neighbors) < qh hull_dim) {
+ zinc_(Zdropdegen);
+ qh_appendmergeset(facet, facet, MRGdegen, &angledegen);
+ trace2((qh ferr, 2024, "qh_maydropneighbors: f%d is degenerate.\n", facet->id));
+ }
+} /* maydropneighbor */
+
+
+/*---------------------------------
+
+ qh_merge_degenredundant()
+ merge all degenerate and redundant facets
+ qh.degen_mergeset contains merges from qh_degen_redundant_neighbors()
+
+ returns:
+ number of merges performed
+ resets facet->degenerate/redundant
+ if deleted (visible) facet has no neighbors
+ sets ->f.replace to NULL
+
+ notes:
+ redundant merges happen before degenerate ones
+ merging and renaming vertices can result in degen/redundant facets
+
+ design:
+ for each merge on qh.degen_mergeset
+ if redundant merge
+ if non-redundant facet merged into redundant facet
+ recheck facet for redundancy
+ else
+ merge redundant facet into other facet
+*/
+int qh_merge_degenredundant(void) {
+ int size;
+ mergeT *merge;
+ facetT *bestneighbor, *facet1, *facet2;
+ realT dist, mindist, maxdist;
+ vertexT *vertex, **vertexp;
+ int nummerges= 0;
+ mergeType mergetype;
+
+ while ((merge= (mergeT*)qh_setdellast(qh degen_mergeset))) {
+ facet1= merge->facet1;
+ facet2= merge->facet2;
+ mergetype= merge->type;
+ qh_memfree(merge, (int)sizeof(mergeT));
+ if (facet1->visible)
+ continue;
+ facet1->degenerate= False;
+ facet1->redundant= False;
+ if (qh TRACEmerge-1 == zzval_(Ztotmerge))
+ qhmem.IStracing= qh IStracing= qh TRACElevel;
+ if (mergetype == MRGredundant) {
+ zinc_(Zneighbor);
+ while (facet2->visible) {
+ if (!facet2->f.replace) {
+ qh_fprintf(qh ferr, 6097, "qhull internal error (qh_merge_degenredunant): f%d redundant but f%d has no replacement\n",
+ facet1->id, facet2->id);
+ qh_errexit2 (qh_ERRqhull, facet1, facet2);
+ }
+ facet2= facet2->f.replace;
+ }
+ if (facet1 == facet2) {
+ qh_degen_redundant_facet(facet1); /* in case of others */
+ continue;
+ }
+ trace2((qh ferr, 2025, "qh_merge_degenredundant: facet f%d is contained in f%d, will merge\n",
+ facet1->id, facet2->id));
+ qh_mergefacet(facet1, facet2, NULL, NULL, !qh_MERGEapex);
+ /* merge distance is already accounted for */
+ nummerges++;
+ }else { /* mergetype == MRGdegen, other merges may have fixed */
+ if (!(size= qh_setsize(facet1->neighbors))) {
+ zinc_(Zdelfacetdup);
+ trace2((qh ferr, 2026, "qh_merge_degenredundant: facet f%d has no neighbors. Deleted\n", facet1->id));
+ qh_willdelete(facet1, NULL);
+ FOREACHvertex_(facet1->vertices) {
+ qh_setdel(vertex->neighbors, facet1);
+ if (!SETfirst_(vertex->neighbors)) {
+ zinc_(Zdegenvertex);
+ trace2((qh ferr, 2027, "qh_merge_degenredundant: deleted v%d because f%d has no neighbors\n",
+ vertex->id, facet1->id));
+ vertex->deleted= True;
+ qh_setappend(&qh del_vertices, vertex);
+ }
+ }
+ nummerges++;
+ }else if (size < qh hull_dim) {
+ bestneighbor= qh_findbestneighbor(facet1, &dist, &mindist, &maxdist);
+ trace2((qh ferr, 2028, "qh_merge_degenredundant: facet f%d has %d neighbors, merge into f%d dist %2.2g\n",
+ facet1->id, size, bestneighbor->id, dist));
+ qh_mergefacet(facet1, bestneighbor, &mindist, &maxdist, !qh_MERGEapex);
+ nummerges++;
+ if (qh PRINTstatistics) {
+ zinc_(Zdegen);
+ wadd_(Wdegentot, dist);
+ wmax_(Wdegenmax, dist);
+ }
+ } /* else, another merge fixed the degeneracy and redundancy tested */
+ }
+ }
+ return nummerges;
+} /* merge_degenredundant */
+
+/*---------------------------------
+
+ qh_merge_nonconvex( facet1, facet2, mergetype )
+ remove non-convex ridge between facet1 into facet2
+ mergetype gives why the facet's are non-convex
+
+ returns:
+ merges one of the facets into the best neighbor
+
+ design:
+ if one of the facets is a new facet
+ prefer merging new facet into old facet
+ find best neighbors for both facets
+ merge the nearest facet into its best neighbor
+ update the statistics
+*/
+void qh_merge_nonconvex(facetT *facet1, facetT *facet2, mergeType mergetype) {
+ facetT *bestfacet, *bestneighbor, *neighbor;
+ realT dist, dist2, mindist, mindist2, maxdist, maxdist2;
+
+ if (qh TRACEmerge-1 == zzval_(Ztotmerge))
+ qhmem.IStracing= qh IStracing= qh TRACElevel;
+ trace3((qh ferr, 3003, "qh_merge_nonconvex: merge #%d for f%d and f%d type %d\n",
+ zzval_(Ztotmerge) + 1, facet1->id, facet2->id, mergetype));
+ /* concave or coplanar */
+ if (!facet1->newfacet) {
+ bestfacet= facet2; /* avoid merging old facet if new is ok */
+ facet2= facet1;
+ facet1= bestfacet;
+ }else
+ bestfacet= facet1;
+ bestneighbor= qh_findbestneighbor(bestfacet, &dist, &mindist, &maxdist);
+ neighbor= qh_findbestneighbor(facet2, &dist2, &mindist2, &maxdist2);
+ if (dist < dist2) {
+ qh_mergefacet(bestfacet, bestneighbor, &mindist, &maxdist, !qh_MERGEapex);
+ }else if (qh AVOIDold && !facet2->newfacet
+ && ((mindist >= -qh MAXcoplanar && maxdist <= qh max_outside)
+ || dist * 1.5 < dist2)) {
+ zinc_(Zavoidold);
+ wadd_(Wavoidoldtot, dist);
+ wmax_(Wavoidoldmax, dist);
+ trace2((qh ferr, 2029, "qh_merge_nonconvex: avoid merging old facet f%d dist %2.2g. Use f%d dist %2.2g instead\n",
+ facet2->id, dist2, facet1->id, dist2));
+ qh_mergefacet(bestfacet, bestneighbor, &mindist, &maxdist, !qh_MERGEapex);
+ }else {
+ qh_mergefacet(facet2, neighbor, &mindist2, &maxdist2, !qh_MERGEapex);
+ dist= dist2;
+ }
+ if (qh PRINTstatistics) {
+ if (mergetype == MRGanglecoplanar) {
+ zinc_(Zacoplanar);
+ wadd_(Wacoplanartot, dist);
+ wmax_(Wacoplanarmax, dist);
+ }else if (mergetype == MRGconcave) {
+ zinc_(Zconcave);
+ wadd_(Wconcavetot, dist);
+ wmax_(Wconcavemax, dist);
+ }else { /* MRGcoplanar */
+ zinc_(Zcoplanar);
+ wadd_(Wcoplanartot, dist);
+ wmax_(Wcoplanarmax, dist);
+ }
+ }
+} /* merge_nonconvex */
+
+/*---------------------------------
+
+ qh_mergecycle( samecycle, newfacet )
+ merge a cycle of facets starting at samecycle into a newfacet
+ newfacet is a horizon facet with ->normal
+ samecycle facets are simplicial from an apex
+
+ returns:
+ initializes vertex neighbors on first merge
+ samecycle deleted (placed on qh.visible_list)
+ newfacet at end of qh.facet_list
+ deleted vertices on qh.del_vertices
+
+ see:
+ qh_mergefacet()
+ called by qh_mergecycle_all() for multiple, same cycle facets
+
+ design:
+ make vertex neighbors if necessary
+ make ridges for newfacet
+ merge neighbor sets of samecycle into newfacet
+ merge ridges of samecycle into newfacet
+ merge vertex neighbors of samecycle into newfacet
+ make apex of samecycle the apex of newfacet
+ if newfacet wasn't a new facet
+ add its vertices to qh.newvertex_list
+ delete samecycle facets a make newfacet a newfacet
+*/
+void qh_mergecycle(facetT *samecycle, facetT *newfacet) {
+ int traceonce= False, tracerestore= 0;
+ vertexT *apex;
+#ifndef qh_NOtrace
+ facetT *same;
+#endif
+
+ if (newfacet->tricoplanar) {
+ if (!qh TRInormals) {
+ qh_fprintf(qh ferr, 6224, "Qhull internal error (qh_mergecycle): does not work for tricoplanar facets. Use option 'Q11'\n");
+ qh_errexit(qh_ERRqhull, newfacet, NULL);
+ }
+ newfacet->tricoplanar= False;
+ newfacet->keepcentrum= False;
+ }
+ if (!qh VERTEXneighbors)
+ qh_vertexneighbors();
+ zzinc_(Ztotmerge);
+ if (qh REPORTfreq2 && qh POSTmerging) {
+ if (zzval_(Ztotmerge) > qh mergereport + qh REPORTfreq2)
+ qh_tracemerging();
+ }
+#ifndef qh_NOtrace
+ if (qh TRACEmerge == zzval_(Ztotmerge))
+ qhmem.IStracing= qh IStracing= qh TRACElevel;
+ trace2((qh ferr, 2030, "qh_mergecycle: merge #%d for facets from cycle f%d into coplanar horizon f%d\n",
+ zzval_(Ztotmerge), samecycle->id, newfacet->id));
+ if (newfacet == qh tracefacet) {
+ tracerestore= qh IStracing;
+ qh IStracing= 4;
+ qh_fprintf(qh ferr, 8068, "qh_mergecycle: ========= trace merge %d of samecycle %d into trace f%d, furthest is p%d\n",
+ zzval_(Ztotmerge), samecycle->id, newfacet->id, qh furthest_id);
+ traceonce= True;
+ }
+ if (qh IStracing >=4) {
+ qh_fprintf(qh ferr, 8069, " same cycle:");
+ FORALLsame_cycle_(samecycle)
+ qh_fprintf(qh ferr, 8070, " f%d", same->id);
+ qh_fprintf(qh ferr, 8071, "\n");
+ }
+ if (qh IStracing >=4)
+ qh_errprint("MERGING CYCLE", samecycle, newfacet, NULL, NULL);
+#endif /* !qh_NOtrace */
+ apex= SETfirstt_(samecycle->vertices, vertexT);
+ qh_makeridges(newfacet);
+ qh_mergecycle_neighbors(samecycle, newfacet);
+ qh_mergecycle_ridges(samecycle, newfacet);
+ qh_mergecycle_vneighbors(samecycle, newfacet);
+ if (SETfirstt_(newfacet->vertices, vertexT) != apex)
+ qh_setaddnth(&newfacet->vertices, 0, apex); /* apex has last id */
+ if (!newfacet->newfacet)
+ qh_newvertices(newfacet->vertices);
+ qh_mergecycle_facets(samecycle, newfacet);
+ qh_tracemerge(samecycle, newfacet);
+ /* check for degen_redundant_neighbors after qh_forcedmerges() */
+ if (traceonce) {
+ qh_fprintf(qh ferr, 8072, "qh_mergecycle: end of trace facet\n");
+ qh IStracing= tracerestore;
+ }
+} /* mergecycle */
+
+/*---------------------------------
+
+ qh_mergecycle_all( facetlist, wasmerge )
+ merge all samecycles of coplanar facets into horizon
+ don't merge facets with ->mergeridge (these already have ->normal)
+ all facets are simplicial from apex
+ all facet->cycledone == False
+
+ returns:
+ all newfacets merged into coplanar horizon facets
+ deleted vertices on qh.del_vertices
+ sets wasmerge if any merge
+
+ see:
+ calls qh_mergecycle for multiple, same cycle facets
+
+ design:
+ for each facet on facetlist
+ skip facets with duplicate ridges and normals
+ check that facet is in a samecycle (->mergehorizon)
+ if facet only member of samecycle
+ sets vertex->delridge for all vertices except apex
+ merge facet into horizon
+ else
+ mark all facets in samecycle
+ remove facets with duplicate ridges from samecycle
+ merge samecycle into horizon (deletes facets from facetlist)
+*/
+void qh_mergecycle_all(facetT *facetlist, boolT *wasmerge) {
+ facetT *facet, *same, *prev, *horizon;
+ facetT *samecycle= NULL, *nextfacet, *nextsame;
+ vertexT *apex, *vertex, **vertexp;
+ int cycles=0, total=0, facets, nummerge;
+
+ trace2((qh ferr, 2031, "qh_mergecycle_all: begin\n"));
+ for (facet= facetlist; facet && (nextfacet= facet->next); facet= nextfacet) {
+ if (facet->normal)
+ continue;
+ if (!facet->mergehorizon) {
+ qh_fprintf(qh ferr, 6225, "Qhull internal error (qh_mergecycle_all): f%d without normal\n", facet->id);
+ qh_errexit(qh_ERRqhull, facet, NULL);
+ }
+ horizon= SETfirstt_(facet->neighbors, facetT);
+ if (facet->f.samecycle == facet) {
+ zinc_(Zonehorizon);
+ /* merge distance done in qh_findhorizon */
+ apex= SETfirstt_(facet->vertices, vertexT);
+ FOREACHvertex_(facet->vertices) {
+ if (vertex != apex)
+ vertex->delridge= True;
+ }
+ horizon->f.newcycle= NULL;
+ qh_mergefacet(facet, horizon, NULL, NULL, qh_MERGEapex);
+ }else {
+ samecycle= facet;
+ facets= 0;
+ prev= facet;
+ for (same= facet->f.samecycle; same; /* FORALLsame_cycle_(facet) */
+ same= (same == facet ? NULL :nextsame)) { /* ends at facet */
+ nextsame= same->f.samecycle;
+ if (same->cycledone || same->visible)
+ qh_infiniteloop(same);
+ same->cycledone= True;
+ if (same->normal) {
+ prev->f.samecycle= same->f.samecycle; /* unlink ->mergeridge */
+ same->f.samecycle= NULL;
+ }else {
+ prev= same;
+ facets++;
+ }
+ }
+ while (nextfacet && nextfacet->cycledone) /* will delete samecycle */
+ nextfacet= nextfacet->next;
+ horizon->f.newcycle= NULL;
+ qh_mergecycle(samecycle, horizon);
+ nummerge= horizon->nummerge + facets;
+ if (nummerge > qh_MAXnummerge)
+ horizon->nummerge= qh_MAXnummerge;
+ else
+ horizon->nummerge= (short unsigned int)nummerge;
+ zzinc_(Zcyclehorizon);
+ total += facets;
+ zzadd_(Zcyclefacettot, facets);
+ zmax_(Zcyclefacetmax, facets);
+ }
+ cycles++;
+ }
+ if (cycles)
+ *wasmerge= True;
+ trace1((qh ferr, 1013, "qh_mergecycle_all: merged %d same cycles or facets into coplanar horizons\n", cycles));
+} /* mergecycle_all */
+
+/*---------------------------------
+
+ qh_mergecycle_facets( samecycle, newfacet )
+ finish merge of samecycle into newfacet
+
+ returns:
+ samecycle prepended to visible_list for later deletion and partitioning
+ each facet->f.replace == newfacet
+
+ newfacet moved to end of qh.facet_list
+ makes newfacet a newfacet (get's facet1->id if it was old)
+ sets newfacet->newmerge
+ clears newfacet->center (unless merging into a large facet)
+ clears newfacet->tested and ridge->tested for facet1
+
+ adds neighboring facets to facet_mergeset if redundant or degenerate
+
+ design:
+ make newfacet a new facet and set its flags
+ move samecycle facets to qh.visible_list for later deletion
+ unless newfacet is large
+ remove its centrum
+*/
+void qh_mergecycle_facets(facetT *samecycle, facetT *newfacet) {
+ facetT *same, *next;
+
+ trace4((qh ferr, 4030, "qh_mergecycle_facets: make newfacet new and samecycle deleted\n"));
+ qh_removefacet(newfacet); /* append as a newfacet to end of qh facet_list */
+ qh_appendfacet(newfacet);
+ newfacet->newfacet= True;
+ newfacet->simplicial= False;
+ newfacet->newmerge= True;
+
+ for (same= samecycle->f.samecycle; same; same= (same == samecycle ? NULL : next)) {
+ next= same->f.samecycle; /* reused by willdelete */
+ qh_willdelete(same, newfacet);
+ }
+ if (newfacet->center
+ && qh_setsize(newfacet->vertices) <= qh hull_dim + qh_MAXnewcentrum) {
+ qh_memfree(newfacet->center, qh normal_size);
+ newfacet->center= NULL;
+ }
+ trace3((qh ferr, 3004, "qh_mergecycle_facets: merged facets from cycle f%d into f%d\n",
+ samecycle->id, newfacet->id));
+} /* mergecycle_facets */
+
+/*---------------------------------
+
+ qh_mergecycle_neighbors( samecycle, newfacet )
+ add neighbors for samecycle facets to newfacet
+
+ returns:
+ newfacet with updated neighbors and vice-versa
+ newfacet has ridges
+ all neighbors of newfacet marked with qh.visit_id
+ samecycle facets marked with qh.visit_id-1
+ ridges updated for simplicial neighbors of samecycle with a ridge
+
+ notes:
+ assumes newfacet not in samecycle
+ usually, samecycle facets are new, simplicial facets without internal ridges
+ not so if horizon facet is coplanar to two different samecycles
+
+ see:
+ qh_mergeneighbors()
+
+ design:
+ check samecycle
+ delete neighbors from newfacet that are also in samecycle
+ for each neighbor of a facet in samecycle
+ if neighbor is simplicial
+ if first visit
+ move the neighbor relation to newfacet
+ update facet links for its ridges
+ else
+ make ridges for neighbor
+ remove samecycle reference
+ else
+ update neighbor sets
+*/
+void qh_mergecycle_neighbors(facetT *samecycle, facetT *newfacet) {
+ facetT *same, *neighbor, **neighborp;
+ int delneighbors= 0, newneighbors= 0;
+ unsigned int samevisitid;
+ ridgeT *ridge, **ridgep;
+
+ samevisitid= ++qh visit_id;
+ FORALLsame_cycle_(samecycle) {
+ if (same->visitid == samevisitid || same->visible)
+ qh_infiniteloop(samecycle);
+ same->visitid= samevisitid;
+ }
+ newfacet->visitid= ++qh visit_id;
+ trace4((qh ferr, 4031, "qh_mergecycle_neighbors: delete shared neighbors from newfacet\n"));
+ FOREACHneighbor_(newfacet) {
+ if (neighbor->visitid == samevisitid) {
+ SETref_(neighbor)= NULL; /* samecycle neighbors deleted */
+ delneighbors++;
+ }else
+ neighbor->visitid= qh visit_id;
+ }
+ qh_setcompact(newfacet->neighbors);
+
+ trace4((qh ferr, 4032, "qh_mergecycle_neighbors: update neighbors\n"));
+ FORALLsame_cycle_(samecycle) {
+ FOREACHneighbor_(same) {
+ if (neighbor->visitid == samevisitid)
+ continue;
+ if (neighbor->simplicial) {
+ if (neighbor->visitid != qh visit_id) {
+ qh_setappend(&newfacet->neighbors, neighbor);
+ qh_setreplace(neighbor->neighbors, same, newfacet);
+ newneighbors++;
+ neighbor->visitid= qh visit_id;
+ FOREACHridge_(neighbor->ridges) { /* update ridge in case of qh_makeridges */
+ if (ridge->top == same) {
+ ridge->top= newfacet;
+ break;
+ }else if (ridge->bottom == same) {
+ ridge->bottom= newfacet;
+ break;
+ }
+ }
+ }else {
+ qh_makeridges(neighbor);
+ qh_setdel(neighbor->neighbors, same);
+ /* same can't be horizon facet for neighbor */
+ }
+ }else { /* non-simplicial neighbor */
+ qh_setdel(neighbor->neighbors, same);
+ if (neighbor->visitid != qh visit_id) {
+ qh_setappend(&neighbor->neighbors, newfacet);
+ qh_setappend(&newfacet->neighbors, neighbor);
+ neighbor->visitid= qh visit_id;
+ newneighbors++;
+ }
+ }
+ }
+ }
+ trace2((qh ferr, 2032, "qh_mergecycle_neighbors: deleted %d neighbors and added %d\n",
+ delneighbors, newneighbors));
+} /* mergecycle_neighbors */
+
+/*---------------------------------
+
+ qh_mergecycle_ridges( samecycle, newfacet )
+ add ridges/neighbors for facets in samecycle to newfacet
+ all new/old neighbors of newfacet marked with qh.visit_id
+ facets in samecycle marked with qh.visit_id-1
+ newfacet marked with qh.visit_id
+
+ returns:
+ newfacet has merged ridges
+
+ notes:
+ ridge already updated for simplicial neighbors of samecycle with a ridge
+
+ see:
+ qh_mergeridges()
+ qh_makeridges()
+
+ design:
+ remove ridges between newfacet and samecycle
+ for each facet in samecycle
+ for each ridge in facet
+ update facet pointers in ridge
+ skip ridges processed in qh_mergecycle_neighors
+ free ridges between newfacet and samecycle
+ free ridges between facets of samecycle (on 2nd visit)
+ append remaining ridges to newfacet
+ if simpilicial facet
+ for each neighbor of facet
+ if simplicial facet
+ and not samecycle facet or newfacet
+ make ridge between neighbor and newfacet
+*/
+void qh_mergecycle_ridges(facetT *samecycle, facetT *newfacet) {
+ facetT *same, *neighbor= NULL;
+ int numold=0, numnew=0;
+ int neighbor_i, neighbor_n;
+ unsigned int samevisitid;
+ ridgeT *ridge, **ridgep;
+ boolT toporient;
+ void **freelistp; /* used !qh_NOmem */
+
+ trace4((qh ferr, 4033, "qh_mergecycle_ridges: delete shared ridges from newfacet\n"));
+ samevisitid= qh visit_id -1;
+ FOREACHridge_(newfacet->ridges) {
+ neighbor= otherfacet_(ridge, newfacet);
+ if (neighbor->visitid == samevisitid)
+ SETref_(ridge)= NULL; /* ridge free'd below */
+ }
+ qh_setcompact(newfacet->ridges);
+
+ trace4((qh ferr, 4034, "qh_mergecycle_ridges: add ridges to newfacet\n"));
+ FORALLsame_cycle_(samecycle) {
+ FOREACHridge_(same->ridges) {
+ if (ridge->top == same) {
+ ridge->top= newfacet;
+ neighbor= ridge->bottom;
+ }else if (ridge->bottom == same) {
+ ridge->bottom= newfacet;
+ neighbor= ridge->top;
+ }else if (ridge->top == newfacet || ridge->bottom == newfacet) {
+ qh_setappend(&newfacet->ridges, ridge);
+ numold++; /* already set by qh_mergecycle_neighbors */
+ continue;
+ }else {
+ qh_fprintf(qh ferr, 6098, "qhull internal error (qh_mergecycle_ridges): bad ridge r%d\n", ridge->id);
+ qh_errexit(qh_ERRqhull, NULL, ridge);
+ }
+ if (neighbor == newfacet) {
+ qh_setfree(&(ridge->vertices));
+ qh_memfree_(ridge, (int)sizeof(ridgeT), freelistp);
+ numold++;
+ }else if (neighbor->visitid == samevisitid) {
+ qh_setdel(neighbor->ridges, ridge);
+ qh_setfree(&(ridge->vertices));
+ qh_memfree_(ridge, (int)sizeof(ridgeT), freelistp);
+ numold++;
+ }else {
+ qh_setappend(&newfacet->ridges, ridge);
+ numold++;
+ }
+ }
+ if (same->ridges)
+ qh_settruncate(same->ridges, 0);
+ if (!same->simplicial)
+ continue;
+ FOREACHneighbor_i_(same) { /* note: !newfact->simplicial */
+ if (neighbor->visitid != samevisitid && neighbor->simplicial) {
+ ridge= qh_newridge();
+ ridge->vertices= qh_setnew_delnthsorted(same->vertices, qh hull_dim,
+ neighbor_i, 0);
+ toporient= same->toporient ^ (neighbor_i & 0x1);
+ if (toporient) {
+ ridge->top= newfacet;
+ ridge->bottom= neighbor;
+ }else {
+ ridge->top= neighbor;
+ ridge->bottom= newfacet;
+ }
+ qh_setappend(&(newfacet->ridges), ridge);
+ qh_setappend(&(neighbor->ridges), ridge);
+ numnew++;
+ }
+ }
+ }
+
+ trace2((qh ferr, 2033, "qh_mergecycle_ridges: found %d old ridges and %d new ones\n",
+ numold, numnew));
+} /* mergecycle_ridges */
+
+/*---------------------------------
+
+ qh_mergecycle_vneighbors( samecycle, newfacet )
+ create vertex neighbors for newfacet from vertices of facets in samecycle
+ samecycle marked with visitid == qh.visit_id - 1
+
+ returns:
+ newfacet vertices with updated neighbors
+ marks newfacet with qh.visit_id-1
+ deletes vertices that are merged away
+ sets delridge on all vertices (faster here than in mergecycle_ridges)
+
+ see:
+ qh_mergevertex_neighbors()
+
+ design:
+ for each vertex of samecycle facet
+ set vertex->delridge
+ delete samecycle facets from vertex neighbors
+ append newfacet to vertex neighbors
+ if vertex only in newfacet
+ delete it from newfacet
+ add it to qh.del_vertices for later deletion
+*/
+void qh_mergecycle_vneighbors(facetT *samecycle, facetT *newfacet) {
+ facetT *neighbor, **neighborp;
+ unsigned int mergeid;
+ vertexT *vertex, **vertexp, *apex;
+ setT *vertices;
+
+ trace4((qh ferr, 4035, "qh_mergecycle_vneighbors: update vertex neighbors for newfacet\n"));
+ mergeid= qh visit_id - 1;
+ newfacet->visitid= mergeid;
+ vertices= qh_basevertices(samecycle); /* temp */
+ apex= SETfirstt_(samecycle->vertices, vertexT);
+ qh_setappend(&vertices, apex);
+ FOREACHvertex_(vertices) {
+ vertex->delridge= True;
+ FOREACHneighbor_(vertex) {
+ if (neighbor->visitid == mergeid)
+ SETref_(neighbor)= NULL;
+ }
+ qh_setcompact(vertex->neighbors);
+ qh_setappend(&vertex->neighbors, newfacet);
+ if (!SETsecond_(vertex->neighbors)) {
+ zinc_(Zcyclevertex);
+ trace2((qh ferr, 2034, "qh_mergecycle_vneighbors: deleted v%d when merging cycle f%d into f%d\n",
+ vertex->id, samecycle->id, newfacet->id));
+ qh_setdelsorted(newfacet->vertices, vertex);
+ vertex->deleted= True;
+ qh_setappend(&qh del_vertices, vertex);
+ }
+ }
+ qh_settempfree(&vertices);
+ trace3((qh ferr, 3005, "qh_mergecycle_vneighbors: merged vertices from cycle f%d into f%d\n",
+ samecycle->id, newfacet->id));
+} /* mergecycle_vneighbors */
+
+/*---------------------------------
+
+ qh_mergefacet( facet1, facet2, mindist, maxdist, mergeapex )
+ merges facet1 into facet2
+ mergeapex==qh_MERGEapex if merging new facet into coplanar horizon
+
+ returns:
+ qh.max_outside and qh.min_vertex updated
+ initializes vertex neighbors on first merge
+
+ returns:
+ facet2 contains facet1's vertices, neighbors, and ridges
+ facet2 moved to end of qh.facet_list
+ makes facet2 a newfacet
+ sets facet2->newmerge set
+ clears facet2->center (unless merging into a large facet)
+ clears facet2->tested and ridge->tested for facet1
+
+ facet1 prepended to visible_list for later deletion and partitioning
+ facet1->f.replace == facet2
+
+ adds neighboring facets to facet_mergeset if redundant or degenerate
+
+ notes:
+ mindist/maxdist may be NULL (only if both NULL)
+ traces merge if fmax_(maxdist,-mindist) > TRACEdist
+
+ see:
+ qh_mergecycle()
+
+ design:
+ trace merge and check for degenerate simplex
+ make ridges for both facets
+ update qh.max_outside, qh.max_vertex, qh.min_vertex
+ update facet2->maxoutside and keepcentrum
+ update facet2->nummerge
+ update tested flags for facet2
+ if facet1 is simplicial
+ merge facet1 into facet2
+ else
+ merge facet1's neighbors into facet2
+ merge facet1's ridges into facet2
+ merge facet1's vertices into facet2
+ merge facet1's vertex neighbors into facet2
+ add facet2's vertices to qh.new_vertexlist
+ unless qh_MERGEapex
+ test facet2 for degenerate or redundant neighbors
+ move facet1 to qh.visible_list for later deletion
+ move facet2 to end of qh.newfacet_list
+*/
+void qh_mergefacet(facetT *facet1, facetT *facet2, realT *mindist, realT *maxdist, boolT mergeapex) {
+ boolT traceonce= False;
+ vertexT *vertex, **vertexp;
+ int tracerestore=0, nummerge;
+
+ if (facet1->tricoplanar || facet2->tricoplanar) {
+ if (!qh TRInormals) {
+ qh_fprintf(qh ferr, 6226, "Qhull internal error (qh_mergefacet): does not work for tricoplanar facets. Use option 'Q11'\n");
+ qh_errexit2 (qh_ERRqhull, facet1, facet2);
+ }
+ if (facet2->tricoplanar) {
+ facet2->tricoplanar= False;
+ facet2->keepcentrum= False;
+ }
+ }
+ zzinc_(Ztotmerge);
+ if (qh REPORTfreq2 && qh POSTmerging) {
+ if (zzval_(Ztotmerge) > qh mergereport + qh REPORTfreq2)
+ qh_tracemerging();
+ }
+#ifndef qh_NOtrace
+ if (qh build_cnt >= qh RERUN) {
+ if (mindist && (-*mindist > qh TRACEdist || *maxdist > qh TRACEdist)) {
+ tracerestore= 0;
+ qh IStracing= qh TRACElevel;
+ traceonce= True;
+ qh_fprintf(qh ferr, 8075, "qh_mergefacet: ========= trace wide merge #%d(%2.2g) for f%d into f%d, last point was p%d\n", zzval_(Ztotmerge),
+ fmax_(-*mindist, *maxdist), facet1->id, facet2->id, qh furthest_id);
+ }else if (facet1 == qh tracefacet || facet2 == qh tracefacet) {
+ tracerestore= qh IStracing;
+ qh IStracing= 4;
+ traceonce= True;
+ qh_fprintf(qh ferr, 8076, "qh_mergefacet: ========= trace merge #%d involving f%d, furthest is p%d\n",
+ zzval_(Ztotmerge), qh tracefacet_id, qh furthest_id);
+ }
+ }
+ if (qh IStracing >= 2) {
+ realT mergemin= -2;
+ realT mergemax= -2;
+
+ if (mindist) {
+ mergemin= *mindist;
+ mergemax= *maxdist;
+ }
+ qh_fprintf(qh ferr, 8077, "qh_mergefacet: #%d merge f%d into f%d, mindist= %2.2g, maxdist= %2.2g\n",
+ zzval_(Ztotmerge), facet1->id, facet2->id, mergemin, mergemax);
+ }
+#endif /* !qh_NOtrace */
+ if (facet1 == facet2 || facet1->visible || facet2->visible) {
+ qh_fprintf(qh ferr, 6099, "qhull internal error (qh_mergefacet): either f%d and f%d are the same or one is a visible facet\n",
+ facet1->id, facet2->id);
+ qh_errexit2 (qh_ERRqhull, facet1, facet2);
+ }
+ if (qh num_facets - qh num_visible <= qh hull_dim + 1) {
+ qh_fprintf(qh ferr, 6227, "\n\
+qhull precision error: Only %d facets remain. Can not merge another\n\
+pair. The input is too degenerate or the convexity constraints are\n\
+too strong.\n", qh hull_dim+1);
+ if (qh hull_dim >= 5 && !qh MERGEexact)
+ qh_fprintf(qh ferr, 8079, "Option 'Qx' may avoid this problem.\n");
+ qh_errexit(qh_ERRprec, NULL, NULL);
+ }
+ if (!qh VERTEXneighbors)
+ qh_vertexneighbors();
+ qh_makeridges(facet1);
+ qh_makeridges(facet2);
+ if (qh IStracing >=4)
+ qh_errprint("MERGING", facet1, facet2, NULL, NULL);
+ if (mindist) {
+ maximize_(qh max_outside, *maxdist);
+ maximize_(qh max_vertex, *maxdist);
+#if qh_MAXoutside
+ maximize_(facet2->maxoutside, *maxdist);
+#endif
+ minimize_(qh min_vertex, *mindist);
+ if (!facet2->keepcentrum
+ && (*maxdist > qh WIDEfacet || *mindist < -qh WIDEfacet)) {
+ facet2->keepcentrum= True;
+ zinc_(Zwidefacet);
+ }
+ }
+ nummerge= facet1->nummerge + facet2->nummerge + 1;
+ if (nummerge >= qh_MAXnummerge)
+ facet2->nummerge= qh_MAXnummerge;
+ else
+ facet2->nummerge= (short unsigned int)nummerge;
+ facet2->newmerge= True;
+ facet2->dupridge= False;
+ qh_updatetested (facet1, facet2);
+ if (qh hull_dim > 2 && qh_setsize(facet1->vertices) == qh hull_dim)
+ qh_mergesimplex(facet1, facet2, mergeapex);
+ else {
+ qh vertex_visit++;
+ FOREACHvertex_(facet2->vertices)
+ vertex->visitid= qh vertex_visit;
+ if (qh hull_dim == 2)
+ qh_mergefacet2d(facet1, facet2);
+ else {
+ qh_mergeneighbors(facet1, facet2);
+ qh_mergevertices(facet1->vertices, &facet2->vertices);
+ }
+ qh_mergeridges(facet1, facet2);
+ qh_mergevertex_neighbors(facet1, facet2);
+ if (!facet2->newfacet)
+ qh_newvertices(facet2->vertices);
+ }
+ if (!mergeapex)
+ qh_degen_redundant_neighbors(facet2, facet1);
+ if (facet2->coplanar || !facet2->newfacet) {
+ zinc_(Zmergeintohorizon);
+ }else if (!facet1->newfacet && facet2->newfacet) {
+ zinc_(Zmergehorizon);
+ }else {
+ zinc_(Zmergenew);
+ }
+ qh_willdelete(facet1, facet2);
+ qh_removefacet(facet2); /* append as a newfacet to end of qh facet_list */
+ qh_appendfacet(facet2);
+ facet2->newfacet= True;
+ facet2->tested= False;
+ qh_tracemerge(facet1, facet2);
+ if (traceonce) {
+ qh_fprintf(qh ferr, 8080, "qh_mergefacet: end of wide tracing\n");
+ qh IStracing= tracerestore;
+ }
+} /* mergefacet */
+
+
+/*---------------------------------
+
+ qh_mergefacet2d( facet1, facet2 )
+ in 2d, merges neighbors and vertices of facet1 into facet2
+
+ returns:
+ build ridges for neighbors if necessary
+ facet2 looks like a simplicial facet except for centrum, ridges
+ neighbors are opposite the corresponding vertex
+ maintains orientation of facet2
+
+ notes:
+ qh_mergefacet() retains non-simplicial structures
+ they are not needed in 2d, but later routines may use them
+ preserves qh.vertex_visit for qh_mergevertex_neighbors()
+
+ design:
+ get vertices and neighbors
+ determine new vertices and neighbors
+ set new vertices and neighbors and adjust orientation
+ make ridges for new neighbor if needed
+*/
+void qh_mergefacet2d(facetT *facet1, facetT *facet2) {
+ vertexT *vertex1A, *vertex1B, *vertex2A, *vertex2B, *vertexA, *vertexB;
+ facetT *neighbor1A, *neighbor1B, *neighbor2A, *neighbor2B, *neighborA, *neighborB;
+
+ vertex1A= SETfirstt_(facet1->vertices, vertexT);
+ vertex1B= SETsecondt_(facet1->vertices, vertexT);
+ vertex2A= SETfirstt_(facet2->vertices, vertexT);
+ vertex2B= SETsecondt_(facet2->vertices, vertexT);
+ neighbor1A= SETfirstt_(facet1->neighbors, facetT);
+ neighbor1B= SETsecondt_(facet1->neighbors, facetT);
+ neighbor2A= SETfirstt_(facet2->neighbors, facetT);
+ neighbor2B= SETsecondt_(facet2->neighbors, facetT);
+ if (vertex1A == vertex2A) {
+ vertexA= vertex1B;
+ vertexB= vertex2B;
+ neighborA= neighbor2A;
+ neighborB= neighbor1A;
+ }else if (vertex1A == vertex2B) {
+ vertexA= vertex1B;
+ vertexB= vertex2A;
+ neighborA= neighbor2B;
+ neighborB= neighbor1A;
+ }else if (vertex1B == vertex2A) {
+ vertexA= vertex1A;
+ vertexB= vertex2B;
+ neighborA= neighbor2A;
+ neighborB= neighbor1B;
+ }else { /* 1B == 2B */
+ vertexA= vertex1A;
+ vertexB= vertex2A;
+ neighborA= neighbor2B;
+ neighborB= neighbor1B;
+ }
+ /* vertexB always from facet2, neighborB always from facet1 */
+ if (vertexA->id > vertexB->id) {
+ SETfirst_(facet2->vertices)= vertexA;
+ SETsecond_(facet2->vertices)= vertexB;
+ if (vertexB == vertex2A)
+ facet2->toporient= !facet2->toporient;
+ SETfirst_(facet2->neighbors)= neighborA;
+ SETsecond_(facet2->neighbors)= neighborB;
+ }else {
+ SETfirst_(facet2->vertices)= vertexB;
+ SETsecond_(facet2->vertices)= vertexA;
+ if (vertexB == vertex2B)
+ facet2->toporient= !facet2->toporient;
+ SETfirst_(facet2->neighbors)= neighborB;
+ SETsecond_(facet2->neighbors)= neighborA;
+ }
+ qh_makeridges(neighborB);
+ qh_setreplace(neighborB->neighbors, facet1, facet2);
+ trace4((qh ferr, 4036, "qh_mergefacet2d: merged v%d and neighbor f%d of f%d into f%d\n",
+ vertexA->id, neighborB->id, facet1->id, facet2->id));
+} /* mergefacet2d */
+
+
+/*---------------------------------
+
+ qh_mergeneighbors( facet1, facet2 )
+ merges the neighbors of facet1 into facet2
+
+ see:
+ qh_mergecycle_neighbors()
+
+ design:
+ for each neighbor of facet1
+ if neighbor is also a neighbor of facet2
+ if neighbor is simpilicial
+ make ridges for later deletion as a degenerate facet
+ update its neighbor set
+ else
+ move the neighbor relation to facet2
+ remove the neighbor relation for facet1 and facet2
+*/
+void qh_mergeneighbors(facetT *facet1, facetT *facet2) {
+ facetT *neighbor, **neighborp;
+
+ trace4((qh ferr, 4037, "qh_mergeneighbors: merge neighbors of f%d and f%d\n",
+ facet1->id, facet2->id));
+ qh visit_id++;
+ FOREACHneighbor_(facet2) {
+ neighbor->visitid= qh visit_id;
+ }
+ FOREACHneighbor_(facet1) {
+ if (neighbor->visitid == qh visit_id) {
+ if (neighbor->simplicial) /* is degen, needs ridges */
+ qh_makeridges(neighbor);
+ if (SETfirstt_(neighbor->neighbors, facetT) != facet1) /*keep newfacet->horizon*/
+ qh_setdel(neighbor->neighbors, facet1);
+ else {
+ qh_setdel(neighbor->neighbors, facet2);
+ qh_setreplace(neighbor->neighbors, facet1, facet2);
+ }
+ }else if (neighbor != facet2) {
+ qh_setappend(&(facet2->neighbors), neighbor);
+ qh_setreplace(neighbor->neighbors, facet1, facet2);
+ }
+ }
+ qh_setdel(facet1->neighbors, facet2); /* here for makeridges */
+ qh_setdel(facet2->neighbors, facet1);
+} /* mergeneighbors */
+
+
+/*---------------------------------
+
+ qh_mergeridges( facet1, facet2 )
+ merges the ridge set of facet1 into facet2
+
+ returns:
+ may delete all ridges for a vertex
+ sets vertex->delridge on deleted ridges
+
+ see:
+ qh_mergecycle_ridges()
+
+ design:
+ delete ridges between facet1 and facet2
+ mark (delridge) vertices on these ridges for later testing
+ for each remaining ridge
+ rename facet1 to facet2
+*/
+void qh_mergeridges(facetT *facet1, facetT *facet2) {
+ ridgeT *ridge, **ridgep;
+ vertexT *vertex, **vertexp;
+
+ trace4((qh ferr, 4038, "qh_mergeridges: merge ridges of f%d and f%d\n",
+ facet1->id, facet2->id));
+ FOREACHridge_(facet2->ridges) {
+ if ((ridge->top == facet1) || (ridge->bottom == facet1)) {
+ FOREACHvertex_(ridge->vertices)
+ vertex->delridge= True;
+ qh_delridge(ridge); /* expensive in high-d, could rebuild */
+ ridgep--; /*repeat*/
+ }
+ }
+ FOREACHridge_(facet1->ridges) {
+ if (ridge->top == facet1)
+ ridge->top= facet2;
+ else
+ ridge->bottom= facet2;
+ qh_setappend(&(facet2->ridges), ridge);
+ }
+} /* mergeridges */
+
+
+/*---------------------------------
+
+ qh_mergesimplex( facet1, facet2, mergeapex )
+ merge simplicial facet1 into facet2
+ mergeapex==qh_MERGEapex if merging samecycle into horizon facet
+ vertex id is latest (most recently created)
+ facet1 may be contained in facet2
+ ridges exist for both facets
+
+ returns:
+ facet2 with updated vertices, ridges, neighbors
+ updated neighbors for facet1's vertices
+ facet1 not deleted
+ sets vertex->delridge on deleted ridges
+
+ notes:
+ special case code since this is the most common merge
+ called from qh_mergefacet()
+
+ design:
+ if qh_MERGEapex
+ add vertices of facet2 to qh.new_vertexlist if necessary
+ add apex to facet2
+ else
+ for each ridge between facet1 and facet2
+ set vertex->delridge
+ determine the apex for facet1 (i.e., vertex to be merged)
+ unless apex already in facet2
+ insert apex into vertices for facet2
+ add vertices of facet2 to qh.new_vertexlist if necessary
+ add apex to qh.new_vertexlist if necessary
+ for each vertex of facet1
+ if apex
+ rename facet1 to facet2 in its vertex neighbors
+ else
+ delete facet1 from vertex neighors
+ if only in facet2
+ add vertex to qh.del_vertices for later deletion
+ for each ridge of facet1
+ delete ridges between facet1 and facet2
+ append other ridges to facet2 after renaming facet to facet2
+*/
+void qh_mergesimplex(facetT *facet1, facetT *facet2, boolT mergeapex) {
+ vertexT *vertex, **vertexp, *apex;
+ ridgeT *ridge, **ridgep;
+ boolT issubset= False;
+ int vertex_i= -1, vertex_n;
+ facetT *neighbor, **neighborp, *otherfacet;
+
+ if (mergeapex) {
+ if (!facet2->newfacet)
+ qh_newvertices(facet2->vertices); /* apex is new */
+ apex= SETfirstt_(facet1->vertices, vertexT);
+ if (SETfirstt_(facet2->vertices, vertexT) != apex)
+ qh_setaddnth(&facet2->vertices, 0, apex); /* apex has last id */
+ else
+ issubset= True;
+ }else {
+ zinc_(Zmergesimplex);
+ FOREACHvertex_(facet1->vertices)
+ vertex->seen= False;
+ FOREACHridge_(facet1->ridges) {
+ if (otherfacet_(ridge, facet1) == facet2) {
+ FOREACHvertex_(ridge->vertices) {
+ vertex->seen= True;
+ vertex->delridge= True;
+ }
+ break;
+ }
+ }
+ FOREACHvertex_(facet1->vertices) {
+ if (!vertex->seen)
+ break; /* must occur */
+ }
+ apex= vertex;
+ trace4((qh ferr, 4039, "qh_mergesimplex: merge apex v%d of f%d into facet f%d\n",
+ apex->id, facet1->id, facet2->id));
+ FOREACHvertex_i_(facet2->vertices) {
+ if (vertex->id < apex->id) {
+ break;
+ }else if (vertex->id == apex->id) {
+ issubset= True;
+ break;
+ }
+ }
+ if (!issubset)
+ qh_setaddnth(&facet2->vertices, vertex_i, apex);
+ if (!facet2->newfacet)
+ qh_newvertices(facet2->vertices);
+ else if (!apex->newlist) {
+ qh_removevertex(apex);
+ qh_appendvertex(apex);
+ }
+ }
+ trace4((qh ferr, 4040, "qh_mergesimplex: update vertex neighbors of f%d\n",
+ facet1->id));
+ FOREACHvertex_(facet1->vertices) {
+ if (vertex == apex && !issubset)
+ qh_setreplace(vertex->neighbors, facet1, facet2);
+ else {
+ qh_setdel(vertex->neighbors, facet1);
+ if (!SETsecond_(vertex->neighbors))
+ qh_mergevertex_del(vertex, facet1, facet2);
+ }
+ }
+ trace4((qh ferr, 4041, "qh_mergesimplex: merge ridges and neighbors of f%d into f%d\n",
+ facet1->id, facet2->id));
+ qh visit_id++;
+ FOREACHneighbor_(facet2)
+ neighbor->visitid= qh visit_id;
+ FOREACHridge_(facet1->ridges) {
+ otherfacet= otherfacet_(ridge, facet1);
+ if (otherfacet == facet2) {
+ qh_setdel(facet2->ridges, ridge);
+ qh_setfree(&(ridge->vertices));
+ qh_memfree(ridge, (int)sizeof(ridgeT));
+ qh_setdel(facet2->neighbors, facet1);
+ }else {
+ qh_setappend(&facet2->ridges, ridge);
+ if (otherfacet->visitid != qh visit_id) {
+ qh_setappend(&facet2->neighbors, otherfacet);
+ qh_setreplace(otherfacet->neighbors, facet1, facet2);
+ otherfacet->visitid= qh visit_id;
+ }else {
+ if (otherfacet->simplicial) /* is degen, needs ridges */
+ qh_makeridges(otherfacet);
+ if (SETfirstt_(otherfacet->neighbors, facetT) != facet1)
+ qh_setdel(otherfacet->neighbors, facet1);
+ else { /*keep newfacet->neighbors->horizon*/
+ qh_setdel(otherfacet->neighbors, facet2);
+ qh_setreplace(otherfacet->neighbors, facet1, facet2);
+ }
+ }
+ if (ridge->top == facet1) /* wait until after qh_makeridges */
+ ridge->top= facet2;
+ else
+ ridge->bottom= facet2;
+ }
+ }
+ SETfirst_(facet1->ridges)= NULL; /* it will be deleted */
+ trace3((qh ferr, 3006, "qh_mergesimplex: merged simplex f%d apex v%d into facet f%d\n",
+ facet1->id, getid_(apex), facet2->id));
+} /* mergesimplex */
+
+/*---------------------------------
+
+ qh_mergevertex_del( vertex, facet1, facet2 )
+ delete a vertex because of merging facet1 into facet2
+
+ returns:
+ deletes vertex from facet2
+ adds vertex to qh.del_vertices for later deletion
+*/
+void qh_mergevertex_del(vertexT *vertex, facetT *facet1, facetT *facet2) {
+
+ zinc_(Zmergevertex);
+ trace2((qh ferr, 2035, "qh_mergevertex_del: deleted v%d when merging f%d into f%d\n",
+ vertex->id, facet1->id, facet2->id));
+ qh_setdelsorted(facet2->vertices, vertex);
+ vertex->deleted= True;
+ qh_setappend(&qh del_vertices, vertex);
+} /* mergevertex_del */
+
+/*---------------------------------
+
+ qh_mergevertex_neighbors( facet1, facet2 )
+ merge the vertex neighbors of facet1 to facet2
+
+ returns:
+ if vertex is current qh.vertex_visit
+ deletes facet1 from vertex->neighbors
+ else
+ renames facet1 to facet2 in vertex->neighbors
+ deletes vertices if only one neighbor
+
+ notes:
+ assumes vertex neighbor sets are good
+*/
+void qh_mergevertex_neighbors(facetT *facet1, facetT *facet2) {
+ vertexT *vertex, **vertexp;
+
+ trace4((qh ferr, 4042, "qh_mergevertex_neighbors: merge vertex neighbors of f%d and f%d\n",
+ facet1->id, facet2->id));
+ if (qh tracevertex) {
+ qh_fprintf(qh ferr, 8081, "qh_mergevertex_neighbors: of f%d and f%d at furthest p%d f0= %p\n",
+ facet1->id, facet2->id, qh furthest_id, qh tracevertex->neighbors->e[0].p);
+ qh_errprint("TRACE", NULL, NULL, NULL, qh tracevertex);
+ }
+ FOREACHvertex_(facet1->vertices) {
+ if (vertex->visitid != qh vertex_visit)
+ qh_setreplace(vertex->neighbors, facet1, facet2);
+ else {
+ qh_setdel(vertex->neighbors, facet1);
+ if (!SETsecond_(vertex->neighbors))
+ qh_mergevertex_del(vertex, facet1, facet2);
+ }
+ }
+ if (qh tracevertex)
+ qh_errprint("TRACE", NULL, NULL, NULL, qh tracevertex);
+} /* mergevertex_neighbors */
+
+
+/*---------------------------------
+
+ qh_mergevertices( vertices1, vertices2 )
+ merges the vertex set of facet1 into facet2
+
+ returns:
+ replaces vertices2 with merged set
+ preserves vertex_visit for qh_mergevertex_neighbors
+ updates qh.newvertex_list
+
+ design:
+ create a merged set of both vertices (in inverse id order)
+*/
+void qh_mergevertices(setT *vertices1, setT **vertices2) {
+ int newsize= qh_setsize(vertices1)+qh_setsize(*vertices2) - qh hull_dim + 1;
+ setT *mergedvertices;
+ vertexT *vertex, **vertexp, **vertex2= SETaddr_(*vertices2, vertexT);
+
+ mergedvertices= qh_settemp(newsize);
+ FOREACHvertex_(vertices1) {
+ if (!*vertex2 || vertex->id > (*vertex2)->id)
+ qh_setappend(&mergedvertices, vertex);
+ else {
+ while (*vertex2 && (*vertex2)->id > vertex->id)
+ qh_setappend(&mergedvertices, *vertex2++);
+ if (!*vertex2 || (*vertex2)->id < vertex->id)
+ qh_setappend(&mergedvertices, vertex);
+ else
+ qh_setappend(&mergedvertices, *vertex2++);
+ }
+ }
+ while (*vertex2)
+ qh_setappend(&mergedvertices, *vertex2++);
+ if (newsize < qh_setsize(mergedvertices)) {
+ qh_fprintf(qh ferr, 6100, "qhull internal error (qh_mergevertices): facets did not share a ridge\n");
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ qh_setfree(vertices2);
+ *vertices2= mergedvertices;
+ qh_settemppop();
+} /* mergevertices */
+
+
+/*---------------------------------
+
+ qh_neighbor_intersections( vertex )
+ return intersection of all vertices in vertex->neighbors except for vertex
+
+ returns:
+ returns temporary set of vertices
+ does not include vertex
+ NULL if a neighbor is simplicial
+ NULL if empty set
+
+ notes:
+ used for renaming vertices
+
+ design:
+ initialize the intersection set with vertices of the first two neighbors
+ delete vertex from the intersection
+ for each remaining neighbor
+ intersect its vertex set with the intersection set
+ return NULL if empty
+ return the intersection set
+*/
+setT *qh_neighbor_intersections(vertexT *vertex) {
+ facetT *neighbor, **neighborp, *neighborA, *neighborB;
+ setT *intersect;
+ int neighbor_i, neighbor_n;
+
+ FOREACHneighbor_(vertex) {
+ if (neighbor->simplicial)
+ return NULL;
+ }
+ neighborA= SETfirstt_(vertex->neighbors, facetT);
+ neighborB= SETsecondt_(vertex->neighbors, facetT);
+ zinc_(Zintersectnum);
+ if (!neighborA)
+ return NULL;
+ if (!neighborB)
+ intersect= qh_setcopy(neighborA->vertices, 0);
+ else
+ intersect= qh_vertexintersect_new(neighborA->vertices, neighborB->vertices);
+ qh_settemppush(intersect);
+ qh_setdelsorted(intersect, vertex);
+ FOREACHneighbor_i_(vertex) {
+ if (neighbor_i >= 2) {
+ zinc_(Zintersectnum);
+ qh_vertexintersect(&intersect, neighbor->vertices);
+ if (!SETfirst_(intersect)) {
+ zinc_(Zintersectfail);
+ qh_settempfree(&intersect);
+ return NULL;
+ }
+ }
+ }
+ trace3((qh ferr, 3007, "qh_neighbor_intersections: %d vertices in neighbor intersection of v%d\n",
+ qh_setsize(intersect), vertex->id));
+ return intersect;
+} /* neighbor_intersections */
+
+/*---------------------------------
+
+ qh_newvertices( vertices )
+ add vertices to end of qh.vertex_list (marks as new vertices)
+
+ returns:
+ vertices on qh.newvertex_list
+ vertex->newlist set
+*/
+void qh_newvertices(setT *vertices) {
+ vertexT *vertex, **vertexp;
+
+ FOREACHvertex_(vertices) {
+ if (!vertex->newlist) {
+ qh_removevertex(vertex);
+ qh_appendvertex(vertex);
+ }
+ }
+} /* newvertices */
+
+/*---------------------------------
+
+ qh_reducevertices()
+ reduce extra vertices, shared vertices, and redundant vertices
+ facet->newmerge is set if merged since last call
+ if !qh.MERGEvertices, only removes extra vertices
+
+ returns:
+ True if also merged degen_redundant facets
+ vertices are renamed if possible
+ clears facet->newmerge and vertex->delridge
+
+ notes:
+ ignored if 2-d
+
+ design:
+ merge any degenerate or redundant facets
+ for each newly merged facet
+ remove extra vertices
+ if qh.MERGEvertices
+ for each newly merged facet
+ for each vertex
+ if vertex was on a deleted ridge
+ rename vertex if it is shared
+ remove delridge flag from new vertices
+*/
+boolT qh_reducevertices(void) {
+ int numshare=0, numrename= 0;
+ boolT degenredun= False;
+ facetT *newfacet;
+ vertexT *vertex, **vertexp;
+
+ if (qh hull_dim == 2)
+ return False;
+ if (qh_merge_degenredundant())
+ degenredun= True;
+ LABELrestart:
+ FORALLnew_facets {
+ if (newfacet->newmerge) {
+ if (!qh MERGEvertices)
+ newfacet->newmerge= False;
+ qh_remove_extravertices(newfacet);
+ }
+ }
+ if (!qh MERGEvertices)
+ return False;
+ FORALLnew_facets {
+ if (newfacet->newmerge) {
+ newfacet->newmerge= False;
+ FOREACHvertex_(newfacet->vertices) {
+ if (vertex->delridge) {
+ if (qh_rename_sharedvertex(vertex, newfacet)) {
+ numshare++;
+ vertexp--; /* repeat since deleted vertex */
+ }
+ }
+ }
+ }
+ }
+ FORALLvertex_(qh newvertex_list) {
+ if (vertex->delridge && !vertex->deleted) {
+ vertex->delridge= False;
+ if (qh hull_dim >= 4 && qh_redundant_vertex(vertex)) {
+ numrename++;
+ if (qh_merge_degenredundant()) {
+ degenredun= True;
+ goto LABELrestart;
+ }
+ }
+ }
+ }
+ trace1((qh ferr, 1014, "qh_reducevertices: renamed %d shared vertices and %d redundant vertices. Degen? %d\n",
+ numshare, numrename, degenredun));
+ return degenredun;
+} /* reducevertices */
+
+/*---------------------------------
+
+ qh_redundant_vertex( vertex )
+ detect and rename a redundant vertex
+ vertices have full vertex->neighbors
+
+ returns:
+ returns true if find a redundant vertex
+ deletes vertex(vertex->deleted)
+
+ notes:
+ only needed if vertex->delridge and hull_dim >= 4
+ may add degenerate facets to qh.facet_mergeset
+ doesn't change vertex->neighbors or create redundant facets
+
+ design:
+ intersect vertices of all facet neighbors of vertex
+ determine ridges for these vertices
+ if find a new vertex for vertex amoung these ridges and vertices
+ rename vertex to the new vertex
+*/
+vertexT *qh_redundant_vertex(vertexT *vertex) {
+ vertexT *newvertex= NULL;
+ setT *vertices, *ridges;
+
+ trace3((qh ferr, 3008, "qh_redundant_vertex: check if v%d can be renamed\n", vertex->id));
+ if ((vertices= qh_neighbor_intersections(vertex))) {
+ ridges= qh_vertexridges(vertex);
+ if ((newvertex= qh_find_newvertex(vertex, vertices, ridges)))
+ qh_renamevertex(vertex, newvertex, ridges, NULL, NULL);
+ qh_settempfree(&ridges);
+ qh_settempfree(&vertices);
+ }
+ return newvertex;
+} /* redundant_vertex */
+
+/*---------------------------------
+
+ qh_remove_extravertices( facet )
+ remove extra vertices from non-simplicial facets
+
+ returns:
+ returns True if it finds them
+
+ design:
+ for each vertex in facet
+ if vertex not in a ridge (i.e., no longer used)
+ delete vertex from facet
+ delete facet from vertice's neighbors
+ unless vertex in another facet
+ add vertex to qh.del_vertices for later deletion
+*/
+boolT qh_remove_extravertices(facetT *facet) {
+ ridgeT *ridge, **ridgep;
+ vertexT *vertex, **vertexp;
+ boolT foundrem= False;
+
+ trace4((qh ferr, 4043, "qh_remove_extravertices: test f%d for extra vertices\n",
+ facet->id));
+ FOREACHvertex_(facet->vertices)
+ vertex->seen= False;
+ FOREACHridge_(facet->ridges) {
+ FOREACHvertex_(ridge->vertices)
+ vertex->seen= True;
+ }
+ FOREACHvertex_(facet->vertices) {
+ if (!vertex->seen) {
+ foundrem= True;
+ zinc_(Zremvertex);
+ qh_setdelsorted(facet->vertices, vertex);
+ qh_setdel(vertex->neighbors, facet);
+ if (!qh_setsize(vertex->neighbors)) {
+ vertex->deleted= True;
+ qh_setappend(&qh del_vertices, vertex);
+ zinc_(Zremvertexdel);
+ trace2((qh ferr, 2036, "qh_remove_extravertices: v%d deleted because it's lost all ridges\n", vertex->id));
+ }else
+ trace3((qh ferr, 3009, "qh_remove_extravertices: v%d removed from f%d because it's lost all ridges\n", vertex->id, facet->id));
+ vertexp--; /*repeat*/
+ }
+ }
+ return foundrem;
+} /* remove_extravertices */
+
+/*---------------------------------
+
+ qh_rename_sharedvertex( vertex, facet )
+ detect and rename if shared vertex in facet
+ vertices have full ->neighbors
+
+ returns:
+ newvertex or NULL
+ the vertex may still exist in other facets (i.e., a neighbor was pinched)
+ does not change facet->neighbors
+ updates vertex->neighbors
+
+ notes:
+ a shared vertex for a facet is only in ridges to one neighbor
+ this may undo a pinched facet
+
+ it does not catch pinches involving multiple facets. These appear
+ to be difficult to detect, since an exhaustive search is too expensive.
+
+ design:
+ if vertex only has two neighbors
+ determine the ridges that contain the vertex
+ determine the vertices shared by both neighbors
+ if can find a new vertex in this set
+ rename the vertex to the new vertex
+*/
+vertexT *qh_rename_sharedvertex(vertexT *vertex, facetT *facet) {
+ facetT *neighbor, **neighborp, *neighborA= NULL;
+ setT *vertices, *ridges;
+ vertexT *newvertex;
+
+ if (qh_setsize(vertex->neighbors) == 2) {
+ neighborA= SETfirstt_(vertex->neighbors, facetT);
+ if (neighborA == facet)
+ neighborA= SETsecondt_(vertex->neighbors, facetT);
+ }else if (qh hull_dim == 3)
+ return NULL;
+ else {
+ qh visit_id++;
+ FOREACHneighbor_(facet)
+ neighbor->visitid= qh visit_id;
+ FOREACHneighbor_(vertex) {
+ if (neighbor->visitid == qh visit_id) {
+ if (neighborA)
+ return NULL;
+ neighborA= neighbor;
+ }
+ }
+ if (!neighborA) {
+ qh_fprintf(qh ferr, 6101, "qhull internal error (qh_rename_sharedvertex): v%d's neighbors not in f%d\n",
+ vertex->id, facet->id);
+ qh_errprint("ERRONEOUS", facet, NULL, NULL, vertex);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ }
+ /* the vertex is shared by facet and neighborA */
+ ridges= qh_settemp(qh TEMPsize);
+ neighborA->visitid= ++qh visit_id;
+ qh_vertexridges_facet(vertex, facet, &ridges);
+ trace2((qh ferr, 2037, "qh_rename_sharedvertex: p%d(v%d) is shared by f%d(%d ridges) and f%d\n",
+ qh_pointid(vertex->point), vertex->id, facet->id, qh_setsize(ridges), neighborA->id));
+ zinc_(Zintersectnum);
+ vertices= qh_vertexintersect_new(facet->vertices, neighborA->vertices);
+ qh_setdel(vertices, vertex);
+ qh_settemppush(vertices);
+ if ((newvertex= qh_find_newvertex(vertex, vertices, ridges)))
+ qh_renamevertex(vertex, newvertex, ridges, facet, neighborA);
+ qh_settempfree(&vertices);
+ qh_settempfree(&ridges);
+ return newvertex;
+} /* rename_sharedvertex */
+
+/*---------------------------------
+
+ qh_renameridgevertex( ridge, oldvertex, newvertex )
+ renames oldvertex as newvertex in ridge
+
+ returns:
+
+ design:
+ delete oldvertex from ridge
+ if newvertex already in ridge
+ copy ridge->noconvex to another ridge if possible
+ delete the ridge
+ else
+ insert newvertex into the ridge
+ adjust the ridge's orientation
+*/
+void qh_renameridgevertex(ridgeT *ridge, vertexT *oldvertex, vertexT *newvertex) {
+ int nth= 0, oldnth;
+ facetT *temp;
+ vertexT *vertex, **vertexp;
+
+ oldnth= qh_setindex(ridge->vertices, oldvertex);
+ qh_setdelnthsorted(ridge->vertices, oldnth);
+ FOREACHvertex_(ridge->vertices) {
+ if (vertex == newvertex) {
+ zinc_(Zdelridge);
+ if (ridge->nonconvex) /* only one ridge has nonconvex set */
+ qh_copynonconvex(ridge);
+ qh_delridge(ridge);
+ trace2((qh ferr, 2038, "qh_renameridgevertex: ridge r%d deleted. It contained both v%d and v%d\n",
+ ridge->id, oldvertex->id, newvertex->id));
+ return;
+ }
+ if (vertex->id < newvertex->id)
+ break;
+ nth++;
+ }
+ qh_setaddnth(&ridge->vertices, nth, newvertex);
+ if (abs(oldnth - nth)%2) {
+ trace3((qh ferr, 3010, "qh_renameridgevertex: swapped the top and bottom of ridge r%d\n",
+ ridge->id));
+ temp= ridge->top;
+ ridge->top= ridge->bottom;
+ ridge->bottom= temp;
+ }
+} /* renameridgevertex */
+
+
+/*---------------------------------
+
+ qh_renamevertex( oldvertex, newvertex, ridges, oldfacet, neighborA )
+ renames oldvertex as newvertex in ridges
+ gives oldfacet/neighborA if oldvertex is shared between two facets
+
+ returns:
+ oldvertex may still exist afterwards
+
+
+ notes:
+ can not change neighbors of newvertex (since it's a subset)
+
+ design:
+ for each ridge in ridges
+ rename oldvertex to newvertex and delete degenerate ridges
+ if oldfacet not defined
+ for each neighbor of oldvertex
+ delete oldvertex from neighbor's vertices
+ remove extra vertices from neighbor
+ add oldvertex to qh.del_vertices
+ else if oldvertex only between oldfacet and neighborA
+ delete oldvertex from oldfacet and neighborA
+ add oldvertex to qh.del_vertices
+ else oldvertex is in oldfacet and neighborA and other facets (i.e., pinched)
+ delete oldvertex from oldfacet
+ delete oldfacet from oldvertice's neighbors
+ remove extra vertices (e.g., oldvertex) from neighborA
+*/
+void qh_renamevertex(vertexT *oldvertex, vertexT *newvertex, setT *ridges, facetT *oldfacet, facetT *neighborA) {
+ facetT *neighbor, **neighborp;
+ ridgeT *ridge, **ridgep;
+ boolT istrace= False;
+
+ if (qh IStracing >= 2 || oldvertex->id == qh tracevertex_id ||
+ newvertex->id == qh tracevertex_id)
+ istrace= True;
+ FOREACHridge_(ridges)
+ qh_renameridgevertex(ridge, oldvertex, newvertex);
+ if (!oldfacet) {
+ zinc_(Zrenameall);
+ if (istrace)
+ qh_fprintf(qh ferr, 8082, "qh_renamevertex: renamed v%d to v%d in several facets\n",
+ oldvertex->id, newvertex->id);
+ FOREACHneighbor_(oldvertex) {
+ qh_maydropneighbor(neighbor);
+ qh_setdelsorted(neighbor->vertices, oldvertex);
+ if (qh_remove_extravertices(neighbor))
+ neighborp--; /* neighbor may be deleted */
+ }
+ if (!oldvertex->deleted) {
+ oldvertex->deleted= True;
+ qh_setappend(&qh del_vertices, oldvertex);
+ }
+ }else if (qh_setsize(oldvertex->neighbors) == 2) {
+ zinc_(Zrenameshare);
+ if (istrace)
+ qh_fprintf(qh ferr, 8083, "qh_renamevertex: renamed v%d to v%d in oldfacet f%d\n",
+ oldvertex->id, newvertex->id, oldfacet->id);
+ FOREACHneighbor_(oldvertex)
+ qh_setdelsorted(neighbor->vertices, oldvertex);
+ oldvertex->deleted= True;
+ qh_setappend(&qh del_vertices, oldvertex);
+ }else {
+ zinc_(Zrenamepinch);
+ if (istrace || qh IStracing)
+ qh_fprintf(qh ferr, 8084, "qh_renamevertex: renamed pinched v%d to v%d between f%d and f%d\n",
+ oldvertex->id, newvertex->id, oldfacet->id, neighborA->id);
+ qh_setdelsorted(oldfacet->vertices, oldvertex);
+ qh_setdel(oldvertex->neighbors, oldfacet);
+ qh_remove_extravertices(neighborA);
+ }
+} /* renamevertex */
+
+
+/*---------------------------------
+
+ qh_test_appendmerge( facet, neighbor )
+ tests facet/neighbor for convexity
+ appends to mergeset if non-convex
+ if pre-merging,
+ nop if qh.SKIPconvex, or qh.MERGEexact and coplanar
+
+ returns:
+ true if appends facet/neighbor to mergeset
+ sets facet->center as needed
+ does not change facet->seen
+
+ design:
+ if qh.cos_max is defined
+ if the angle between facet normals is too shallow
+ append an angle-coplanar merge to qh.mergeset
+ return True
+ make facet's centrum if needed
+ if facet's centrum is above the neighbor
+ set isconcave
+ else
+ if facet's centrum is not below the neighbor
+ set iscoplanar
+ make neighbor's centrum if needed
+ if neighbor's centrum is above the facet
+ set isconcave
+ else if neighbor's centrum is not below the facet
+ set iscoplanar
+ if isconcave or iscoplanar
+ get angle if needed
+ append concave or coplanar merge to qh.mergeset
+*/
+boolT qh_test_appendmerge(facetT *facet, facetT *neighbor) {
+ realT dist, dist2= -REALmax, angle= -REALmax;
+ boolT isconcave= False, iscoplanar= False, okangle= False;
+
+ if (qh SKIPconvex && !qh POSTmerging)
+ return False;
+ if ((!qh MERGEexact || qh POSTmerging) && qh cos_max < REALmax/2) {
+ angle= qh_getangle(facet->normal, neighbor->normal);
+ zinc_(Zangletests);
+ if (angle > qh cos_max) {
+ zinc_(Zcoplanarangle);
+ qh_appendmergeset(facet, neighbor, MRGanglecoplanar, &angle);
+ trace2((qh ferr, 2039, "qh_test_appendmerge: coplanar angle %4.4g between f%d and f%d\n",
+ angle, facet->id, neighbor->id));
+ return True;
+ }else
+ okangle= True;
+ }
+ if (!facet->center)
+ facet->center= qh_getcentrum(facet);
+ zzinc_(Zcentrumtests);
+ qh_distplane(facet->center, neighbor, &dist);
+ if (dist > qh centrum_radius)
+ isconcave= True;
+ else {
+ if (dist > -qh centrum_radius)
+ iscoplanar= True;
+ if (!neighbor->center)
+ neighbor->center= qh_getcentrum(neighbor);
+ zzinc_(Zcentrumtests);
+ qh_distplane(neighbor->center, facet, &dist2);
+ if (dist2 > qh centrum_radius)
+ isconcave= True;
+ else if (!iscoplanar && dist2 > -qh centrum_radius)
+ iscoplanar= True;
+ }
+ if (!isconcave && (!iscoplanar || (qh MERGEexact && !qh POSTmerging)))
+ return False;
+ if (!okangle && qh ANGLEmerge) {
+ angle= qh_getangle(facet->normal, neighbor->normal);
+ zinc_(Zangletests);
+ }
+ if (isconcave) {
+ zinc_(Zconcaveridge);
+ if (qh ANGLEmerge)
+ angle += qh_ANGLEconcave + 0.5;
+ qh_appendmergeset(facet, neighbor, MRGconcave, &angle);
+ trace0((qh ferr, 18, "qh_test_appendmerge: concave f%d to f%d dist %4.4g and reverse dist %4.4g angle %4.4g during p%d\n",
+ facet->id, neighbor->id, dist, dist2, angle, qh furthest_id));
+ }else /* iscoplanar */ {
+ zinc_(Zcoplanarcentrum);
+ qh_appendmergeset(facet, neighbor, MRGcoplanar, &angle);
+ trace2((qh ferr, 2040, "qh_test_appendmerge: coplanar f%d to f%d dist %4.4g, reverse dist %4.4g angle %4.4g\n",
+ facet->id, neighbor->id, dist, dist2, angle));
+ }
+ return True;
+} /* test_appendmerge */
+
+/*---------------------------------
+
+ qh_test_vneighbors()
+ test vertex neighbors for convexity
+ tests all facets on qh.newfacet_list
+
+ returns:
+ true if non-convex vneighbors appended to qh.facet_mergeset
+ initializes vertex neighbors if needed
+
+ notes:
+ assumes all facet neighbors have been tested
+ this can be expensive
+ this does not guarantee that a centrum is below all facets
+ but it is unlikely
+ uses qh.visit_id
+
+ design:
+ build vertex neighbors if necessary
+ for all new facets
+ for all vertices
+ for each unvisited facet neighbor of the vertex
+ test new facet and neighbor for convexity
+*/
+boolT qh_test_vneighbors(void /* qh newfacet_list */) {
+ facetT *newfacet, *neighbor, **neighborp;
+ vertexT *vertex, **vertexp;
+ int nummerges= 0;
+
+ trace1((qh ferr, 1015, "qh_test_vneighbors: testing vertex neighbors for convexity\n"));
+ if (!qh VERTEXneighbors)
+ qh_vertexneighbors();
+ FORALLnew_facets
+ newfacet->seen= False;
+ FORALLnew_facets {
+ newfacet->seen= True;
+ newfacet->visitid= qh visit_id++;
+ FOREACHneighbor_(newfacet)
+ newfacet->visitid= qh visit_id;
+ FOREACHvertex_(newfacet->vertices) {
+ FOREACHneighbor_(vertex) {
+ if (neighbor->seen || neighbor->visitid == qh visit_id)
+ continue;
+ if (qh_test_appendmerge(newfacet, neighbor))
+ nummerges++;
+ }
+ }
+ }
+ zadd_(Ztestvneighbor, nummerges);
+ trace1((qh ferr, 1016, "qh_test_vneighbors: found %d non-convex, vertex neighbors\n",
+ nummerges));
+ return (nummerges > 0);
+} /* test_vneighbors */
+
+/*---------------------------------
+
+ qh_tracemerge( facet1, facet2 )
+ print trace message after merge
+*/
+void qh_tracemerge(facetT *facet1, facetT *facet2) {
+ boolT waserror= False;
+
+#ifndef qh_NOtrace
+ if (qh IStracing >= 4)
+ qh_errprint("MERGED", facet2, NULL, NULL, NULL);
+ if (facet2 == qh tracefacet || (qh tracevertex && qh tracevertex->newlist)) {
+ qh_fprintf(qh ferr, 8085, "qh_tracemerge: trace facet and vertex after merge of f%d and f%d, furthest p%d\n", facet1->id, facet2->id, qh furthest_id);
+ if (facet2 != qh tracefacet)
+ qh_errprint("TRACE", qh tracefacet,
+ (qh tracevertex && qh tracevertex->neighbors) ?
+ SETfirstt_(qh tracevertex->neighbors, facetT) : NULL,
+ NULL, qh tracevertex);
+ }
+ if (qh tracevertex) {
+ if (qh tracevertex->deleted)
+ qh_fprintf(qh ferr, 8086, "qh_tracemerge: trace vertex deleted at furthest p%d\n",
+ qh furthest_id);
+ else
+ qh_checkvertex(qh tracevertex);
+ }
+ if (qh tracefacet) {
+ qh_checkfacet(qh tracefacet, True, &waserror);
+ if (waserror)
+ qh_errexit(qh_ERRqhull, qh tracefacet, NULL);
+ }
+#endif /* !qh_NOtrace */
+ if (qh CHECKfrequently || qh IStracing >= 4) { /* can't check polygon here */
+ qh_checkfacet(facet2, True, &waserror);
+ if (waserror)
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+} /* tracemerge */
+
+/*---------------------------------
+
+ qh_tracemerging()
+ print trace message during POSTmerging
+
+ returns:
+ updates qh.mergereport
+
+ notes:
+ called from qh_mergecycle() and qh_mergefacet()
+
+ see:
+ qh_buildtracing()
+*/
+void qh_tracemerging(void) {
+ realT cpu;
+ int total;
+ time_t timedata;
+ struct tm *tp;
+
+ qh mergereport= zzval_(Ztotmerge);
+ time(&timedata);
+ tp= localtime(&timedata);
+ cpu= qh_CPUclock;
+ cpu /= qh_SECticks;
+ total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot);
+ qh_fprintf(qh ferr, 8087, "\n\
+At %d:%d:%d & %2.5g CPU secs, qhull has merged %d facets. The hull\n\
+ contains %d facets and %d vertices.\n",
+ tp->tm_hour, tp->tm_min, tp->tm_sec, cpu,
+ total, qh num_facets - qh num_visible,
+ qh num_vertices-qh_setsize(qh del_vertices));
+} /* tracemerging */
+
+/*---------------------------------
+
+ qh_updatetested( facet1, facet2 )
+ clear facet2->tested and facet1->ridge->tested for merge
+
+ returns:
+ deletes facet2->center unless it's already large
+ if so, clears facet2->ridge->tested
+
+ design:
+ clear facet2->tested
+ clear ridge->tested for facet1's ridges
+ if facet2 has a centrum
+ if facet2 is large
+ set facet2->keepcentrum
+ else if facet2 has 3 vertices due to many merges, or not large and post merging
+ clear facet2->keepcentrum
+ unless facet2->keepcentrum
+ clear facet2->center to recompute centrum later
+ clear ridge->tested for facet2's ridges
+*/
+void qh_updatetested(facetT *facet1, facetT *facet2) {
+ ridgeT *ridge, **ridgep;
+ int size;
+
+ facet2->tested= False;
+ FOREACHridge_(facet1->ridges)
+ ridge->tested= False;
+ if (!facet2->center)
+ return;
+ size= qh_setsize(facet2->vertices);
+ if (!facet2->keepcentrum) {
+ if (size > qh hull_dim + qh_MAXnewcentrum) {
+ facet2->keepcentrum= True;
+ zinc_(Zwidevertices);
+ }
+ }else if (size <= qh hull_dim + qh_MAXnewcentrum) {
+ /* center and keepcentrum was set */
+ if (size == qh hull_dim || qh POSTmerging)
+ facet2->keepcentrum= False; /* if many merges need to recompute centrum */
+ }
+ if (!facet2->keepcentrum) {
+ qh_memfree(facet2->center, qh normal_size);
+ facet2->center= NULL;
+ FOREACHridge_(facet2->ridges)
+ ridge->tested= False;
+ }
+} /* updatetested */
+
+/*---------------------------------
+
+ qh_vertexridges( vertex )
+ return temporary set of ridges adjacent to a vertex
+ vertex->neighbors defined
+
+ ntoes:
+ uses qh.visit_id
+ does not include implicit ridges for simplicial facets
+
+ design:
+ for each neighbor of vertex
+ add ridges that include the vertex to ridges
+*/
+setT *qh_vertexridges(vertexT *vertex) {
+ facetT *neighbor, **neighborp;
+ setT *ridges= qh_settemp(qh TEMPsize);
+ int size;
+
+ qh visit_id++;
+ FOREACHneighbor_(vertex)
+ neighbor->visitid= qh visit_id;
+ FOREACHneighbor_(vertex) {
+ if (*neighborp) /* no new ridges in last neighbor */
+ qh_vertexridges_facet(vertex, neighbor, &ridges);
+ }
+ if (qh PRINTstatistics || qh IStracing) {
+ size= qh_setsize(ridges);
+ zinc_(Zvertexridge);
+ zadd_(Zvertexridgetot, size);
+ zmax_(Zvertexridgemax, size);
+ trace3((qh ferr, 3011, "qh_vertexridges: found %d ridges for v%d\n",
+ size, vertex->id));
+ }
+ return ridges;
+} /* vertexridges */
+
+/*---------------------------------
+
+ qh_vertexridges_facet( vertex, facet, ridges )
+ add adjacent ridges for vertex in facet
+ neighbor->visitid==qh.visit_id if it hasn't been visited
+
+ returns:
+ ridges updated
+ sets facet->visitid to qh.visit_id-1
+
+ design:
+ for each ridge of facet
+ if ridge of visited neighbor (i.e., unprocessed)
+ if vertex in ridge
+ append ridge to vertex
+ mark facet processed
+*/
+void qh_vertexridges_facet(vertexT *vertex, facetT *facet, setT **ridges) {
+ ridgeT *ridge, **ridgep;
+ facetT *neighbor;
+
+ FOREACHridge_(facet->ridges) {
+ neighbor= otherfacet_(ridge, facet);
+ if (neighbor->visitid == qh visit_id
+ && qh_setin(ridge->vertices, vertex))
+ qh_setappend(ridges, ridge);
+ }
+ facet->visitid= qh visit_id-1;
+} /* vertexridges_facet */
+
+/*---------------------------------
+
+ qh_willdelete( facet, replace )
+ moves facet to visible list
+ sets facet->f.replace to replace (may be NULL)
+
+ returns:
+ bumps qh.num_visible
+*/
+void qh_willdelete(facetT *facet, facetT *replace) {
+
+ qh_removefacet(facet);
+ qh_prependfacet(facet, &qh visible_list);
+ qh num_visible++;
+ facet->visible= True;
+ facet->f.replace= replace;
+} /* willdelete */
+
+#else /* qh_NOmerge */
+void qh_premerge(vertexT *apex, realT maxcentrum, realT maxangle) {
+}
+void qh_postmerge(const char *reason, realT maxcentrum, realT maxangle,
+ boolT vneighbors) {
+}
+boolT qh_checkzero(boolT testall) {
+ }
+#endif /* qh_NOmerge */
+
diff --git a/src/libqhullr/merge_r.h b/src/libqhullr/merge_r.h
new file mode 100644
index 0000000..ddb90cd
--- /dev/null
+++ b/src/libqhullr/merge_r.h
@@ -0,0 +1,178 @@
+/*
---------------------------------
+
+ merge.h
+ header file for merge.c
+
+ see qh-merge.htm and merge.c
+
+ Copyright (c) 1993-2012 C.B. Barber.
+ $Id: //main/2011/qhull/src/libqhullr/merge_r.h#1 $$Change: 1640 $
+ $DateTime: 2014/01/15 09:12:08 $$Author: bbarber $
+*/
+
+#ifndef qhDEFmerge
+#define qhDEFmerge 1
+
+#include "libqhull.h"
+
+
+/*============ -constants- ==============*/
+
+/*----------------------------------
+
+ qh_ANGLEredundant
+ indicates redundant merge in mergeT->angle
+*/
+#define qh_ANGLEredundant 6.0
+
+/*----------------------------------
+
+ qh_ANGLEdegen
+ indicates degenerate facet in mergeT->angle
+*/
+#define qh_ANGLEdegen 5.0
+
+/*----------------------------------
+
+ qh_ANGLEconcave
+ offset to indicate concave facets in mergeT->angle
+
+ notes:
+ concave facets are assigned the range of [2,4] in mergeT->angle
+ roundoff error may make the angle less than 2
+*/
+#define qh_ANGLEconcave 1.5
+
+/*----------------------------------
+
+ MRG... (mergeType)
+ indicates the type of a merge (mergeT->type)
+*/
+typedef enum { /* in sort order for facet_mergeset */
+ MRGnone= 0,
+ MRGcoplanar, /* centrum coplanar */
+ MRGanglecoplanar, /* angle coplanar */
+ /* could detect half concave ridges */
+ MRGconcave, /* concave ridge */
+ MRGflip, /* flipped facet. facet1 == facet2 */
+ MRGridge, /* duplicate ridge (qh_MERGEridge) */
+ /* degen and redundant go onto degen_mergeset */
+ MRGdegen, /* degenerate facet (!enough neighbors) facet1 == facet2 */
+ MRGredundant, /* redundant facet (vertex subset) */
+ /* merge_degenredundant assumes degen < redundant */
+ MRGmirror, /* mirror facet from qh_triangulate */
+ ENDmrg
+} mergeType;
+
+/*----------------------------------
+
+ qh_MERGEapex
+ flag for qh_mergefacet() to indicate an apex merge
+*/
+#define qh_MERGEapex True
+
+/*============ -structures- ====================*/
+
+/*----------------------------------
+
+ mergeT
+ structure used to merge facets
+*/
+
+typedef struct mergeT mergeT;
+struct mergeT { /* initialize in qh_appendmergeset */
+ realT angle; /* angle between normals of facet1 and facet2 */
+ facetT *facet1; /* will merge facet1 into facet2 */
+ facetT *facet2;
+ mergeType type;
+};
+
+
+/*=========== -macros- =========================*/
+
+/*----------------------------------
+
+ FOREACHmerge_( merges ) {...}
+ assign 'merge' to each merge in merges
+
+ notes:
+ uses 'mergeT *merge, **mergep;'
+ if qh_mergefacet(),
+ restart since qh.facet_mergeset may change
+ see FOREACHsetelement_
+*/
+#define FOREACHmerge_( merges ) FOREACHsetelement_(mergeT, merges, merge)
+
+/*============ prototypes in alphabetical order after pre/postmerge =======*/
+
+void qh_premerge(vertexT *apex, realT maxcentrum, realT maxangle);
+void qh_postmerge(const char *reason, realT maxcentrum, realT maxangle,
+ boolT vneighbors);
+void qh_all_merges(boolT othermerge, boolT vneighbors);
+void qh_appendmergeset(facetT *facet, facetT *neighbor, mergeType mergetype, realT *angle);
+setT *qh_basevertices( facetT *samecycle);
+void qh_checkconnect(void /* qh new_facets */);
+boolT qh_checkzero(boolT testall);
+int qh_compareangle(const void *p1, const void *p2);
+int qh_comparemerge(const void *p1, const void *p2);
+int qh_comparevisit(const void *p1, const void *p2);
+void qh_copynonconvex(ridgeT *atridge);
+void qh_degen_redundant_facet(facetT *facet);
+void qh_degen_redundant_neighbors(facetT *facet, facetT *delfacet);
+vertexT *qh_find_newvertex(vertexT *oldvertex, setT *vertices, setT *ridges);
+void qh_findbest_test(boolT testcentrum, facetT *facet, facetT *neighbor,
+ facetT **bestfacet, realT *distp, realT *mindistp, realT *maxdistp);
+facetT *qh_findbestneighbor(facetT *facet, realT *distp, realT *mindistp, realT *maxdistp);
+void qh_flippedmerges(facetT *facetlist, boolT *wasmerge);
+void qh_forcedmerges( boolT *wasmerge);
+void qh_getmergeset(facetT *facetlist);
+void qh_getmergeset_initial(facetT *facetlist);
+void qh_hashridge(setT *hashtable, int hashsize, ridgeT *ridge, vertexT *oldvertex);
+ridgeT *qh_hashridge_find(setT *hashtable, int hashsize, ridgeT *ridge,
+ vertexT *vertex, vertexT *oldvertex, int *hashslot);
+void qh_makeridges(facetT *facet);
+void qh_mark_dupridges(facetT *facetlist);
+void qh_maydropneighbor(facetT *facet);
+int qh_merge_degenredundant(void);
+void qh_merge_nonconvex( facetT *facet1, facetT *facet2, mergeType mergetype);
+void qh_mergecycle(facetT *samecycle, facetT *newfacet);
+void qh_mergecycle_all(facetT *facetlist, boolT *wasmerge);
+void qh_mergecycle_facets( facetT *samecycle, facetT *newfacet);
+void qh_mergecycle_neighbors(facetT *samecycle, facetT *newfacet);
+void qh_mergecycle_ridges(facetT *samecycle, facetT *newfacet);
+void qh_mergecycle_vneighbors( facetT *samecycle, facetT *newfacet);
+void qh_mergefacet(facetT *facet1, facetT *facet2, realT *mindist, realT *maxdist, boolT mergeapex);
+void qh_mergefacet2d(facetT *facet1, facetT *facet2);
+void qh_mergeneighbors(facetT *facet1, facetT *facet2);
+void qh_mergeridges(facetT *facet1, facetT *facet2);
+void qh_mergesimplex(facetT *facet1, facetT *facet2, boolT mergeapex);
+void qh_mergevertex_del(vertexT *vertex, facetT *facet1, facetT *facet2);
+void qh_mergevertex_neighbors(facetT *facet1, facetT *facet2);
+void qh_mergevertices(setT *vertices1, setT **vertices);
+setT *qh_neighbor_intersections(vertexT *vertex);
+void qh_newvertices(setT *vertices);
+boolT qh_reducevertices(void);
+vertexT *qh_redundant_vertex(vertexT *vertex);
+boolT qh_remove_extravertices(facetT *facet);
+vertexT *qh_rename_sharedvertex(vertexT *vertex, facetT *facet);
+void qh_renameridgevertex(ridgeT *ridge, vertexT *oldvertex, vertexT *newvertex);
+void qh_renamevertex(vertexT *oldvertex, vertexT *newvertex, setT *ridges,
+ facetT *oldfacet, facetT *neighborA);
+boolT qh_test_appendmerge(facetT *facet, facetT *neighbor);
+boolT qh_test_vneighbors(void /* qh newfacet_list */);
+void qh_tracemerge(facetT *facet1, facetT *facet2);
+void qh_tracemerging(void);
+void qh_updatetested( facetT *facet1, facetT *facet2);
+setT *qh_vertexridges(vertexT *vertex);
+void qh_vertexridges_facet(vertexT *vertex, facetT *facet, setT **ridges);
+void qh_willdelete(facetT *facet, facetT *replace);
+
+#endif /* qhDEFmerge */
diff --git a/src/libqhullr/poly2_r.c b/src/libqhullr/poly2_r.c
new file mode 100644
index 0000000..5267239
--- /dev/null
+++ b/src/libqhullr/poly2_r.c
@@ -0,0 +1,3154 @@
+/*
---------------------------------
+
+ poly2.c
+ implements polygons and simplices
+
+ see qh-poly.htm, poly.h and libqhull.h
+
+ frequently used code is in poly.c
+
+ Copyright (c) 1993-2012 The Geometry Center.
+ $Id: //main/2011/qhull/src/libqhullr/poly2_r.c#1 $$Change: 1640 $
+ $DateTime: 2014/01/15 09:12:08 $$Author: bbarber $
+*/
+
+#include "qhull_a.h"
+
+/*======== functions in alphabetical order ==========*/
+
+/*---------------------------------
+
+ qh_addhash( newelem, hashtable, hashsize, hash )
+ add newelem to linear hash table at hash if not already there
+*/
+void qh_addhash(void* newelem, setT *hashtable, int hashsize, int hash) {
+ int scan;
+ void *elem;
+
+ for (scan= (int)hash; (elem= SETelem_(hashtable, scan));
+ scan= (++scan >= hashsize ? 0 : scan)) {
+ if (elem == newelem)
+ break;
+ }
+ /* loop terminates because qh_HASHfactor >= 1.1 by qh_initbuffers */
+ if (!elem)
+ SETelem_(hashtable, scan)= newelem;
+} /* addhash */
+
+/*---------------------------------
+
+ qh_check_bestdist()
+ check that all points are within max_outside of the nearest facet
+ if qh.ONLYgood,
+ ignores !good facets
+
+ see:
+ qh_check_maxout(), qh_outerinner()
+
+ notes:
+ only called from qh_check_points()
+ seldom used since qh.MERGING is almost always set
+ if notverified>0 at end of routine
+ some points were well inside the hull. If the hull contains
+ a lens-shaped component, these points were not verified. Use
+ options 'Qi Tv' to verify all points. (Exhaustive check also verifies)
+
+ design:
+ determine facet for each point (if any)
+ for each point
+ start with the assigned facet or with the first facet
+ find the best facet for the point and check all coplanar facets
+ error if point is outside of facet
+*/
+void qh_check_bestdist(void) {
+ boolT waserror= False, unassigned;
+ facetT *facet, *bestfacet, *errfacet1= NULL, *errfacet2= NULL;
+ facetT *facetlist;
+ realT dist, maxoutside, maxdist= -REALmax;
+ pointT *point;
+ int numpart= 0, facet_i, facet_n, notgood= 0, notverified= 0;
+ setT *facets;
+
+ trace1((qh ferr, 1020, "qh_check_bestdist: check points below nearest facet. Facet_list f%d\n",
+ qh facet_list->id));
+ maxoutside= qh_maxouter();
+ maxoutside += qh DISTround;
+ /* one more qh.DISTround for check computation */
+ trace1((qh ferr, 1021, "qh_check_bestdist: check that all points are within %2.2g of best facet\n", maxoutside));
+ facets= qh_pointfacet(/*qh facet_list*/);
+ if (!qh_QUICKhelp && qh PRINTprecision)
+ qh_fprintf(qh ferr, 8091, "\n\
+qhull output completed. Verifying that %d points are\n\
+below %2.2g of the nearest %sfacet.\n",
+ qh_setsize(facets), maxoutside, (qh ONLYgood ? "good " : ""));
+ FOREACHfacet_i_(facets) { /* for each point with facet assignment */
+ if (facet)
+ unassigned= False;
+ else {
+ unassigned= True;
+ facet= qh facet_list;
+ }
+ point= qh_point(facet_i);
+ if (point == qh GOODpointp)
+ continue;
+ qh_distplane(point, facet, &dist);
+ numpart++;
+ bestfacet= qh_findbesthorizon(!qh_IScheckmax, point, facet, qh_NOupper, &dist, &numpart);
+ /* occurs after statistics reported */
+ maximize_(maxdist, dist);
+ if (dist > maxoutside) {
+ if (qh ONLYgood && !bestfacet->good
+ && !((bestfacet= qh_findgooddist(point, bestfacet, &dist, &facetlist))
+ && dist > maxoutside))
+ notgood++;
+ else {
+ waserror= True;
+ qh_fprintf(qh ferr, 6109, "qhull precision error: point p%d is outside facet f%d, distance= %6.8g maxoutside= %6.8g\n",
+ facet_i, bestfacet->id, dist, maxoutside);
+ if (errfacet1 != bestfacet) {
+ errfacet2= errfacet1;
+ errfacet1= bestfacet;
+ }
+ }
+ }else if (unassigned && dist < -qh MAXcoplanar)
+ notverified++;
+ }
+ qh_settempfree(&facets);
+ if (notverified && !qh DELAUNAY && !qh_QUICKhelp && qh PRINTprecision)
+ qh_fprintf(qh ferr, 8092, "\n%d points were well inside the hull. If the hull contains\n\
+a lens-shaped component, these points were not verified. Use\n\
+options 'Qci Tv' to verify all points.\n", notverified);
+ if (maxdist > qh outside_err) {
+ qh_fprintf(qh ferr, 6110, "qhull precision error (qh_check_bestdist): a coplanar point is %6.2g from convex hull. The maximum value(qh.outside_err) is %6.2g\n",
+ maxdist, qh outside_err);
+ qh_errexit2 (qh_ERRprec, errfacet1, errfacet2);
+ }else if (waserror && qh outside_err > REALmax/2)
+ qh_errexit2 (qh_ERRprec, errfacet1, errfacet2);
+ /* else if waserror, the error was logged to qh.ferr but does not effect the output */
+ trace0((qh ferr, 20, "qh_check_bestdist: max distance outside %2.2g\n", maxdist));
+} /* check_bestdist */
+
+/*---------------------------------
+
+ qh_check_maxout()
+ updates qh.max_outside by checking all points against bestfacet
+ if qh.ONLYgood, ignores !good facets
+
+ returns:
+ updates facet->maxoutside via qh_findbesthorizon()
+ sets qh.maxoutdone
+ if printing qh.min_vertex (qh_outerinner),
+ it is updated to the current vertices
+ removes inside/coplanar points from coplanarset as needed
+
+ notes:
+ defines coplanar as min_vertex instead of MAXcoplanar
+ may not need to check near-inside points because of qh.MAXcoplanar
+ and qh.KEEPnearinside (before it was -DISTround)
+
+ see also:
+ qh_check_bestdist()
+
+ design:
+ if qh.min_vertex is needed
+ for all neighbors of all vertices
+ test distance from vertex to neighbor
+ determine facet for each point (if any)
+ for each point with an assigned facet
+ find the best facet for the point and check all coplanar facets
+ (updates outer planes)
+ remove near-inside points from coplanar sets
+*/
+#ifndef qh_NOmerge
+void qh_check_maxout(void) {
+ facetT *facet, *bestfacet, *neighbor, **neighborp, *facetlist;
+ realT dist, maxoutside, minvertex, old_maxoutside;
+ pointT *point;
+ int numpart= 0, facet_i, facet_n, notgood= 0;
+ setT *facets, *vertices;
+ vertexT *vertex;
+
+ trace1((qh ferr, 1022, "qh_check_maxout: check and update maxoutside for each facet.\n"));
+ maxoutside= minvertex= 0;
+ if (qh VERTEXneighbors
+ && (qh PRINTsummary || qh KEEPinside || qh KEEPcoplanar
+ || qh TRACElevel || qh PRINTstatistics
+ || qh PRINTout[0] == qh_PRINTsummary || qh PRINTout[0] == qh_PRINTnone)) {
+ trace1((qh ferr, 1023, "qh_check_maxout: determine actual maxoutside and minvertex\n"));
+ vertices= qh_pointvertex(/*qh facet_list*/);
+ FORALLvertices {
+ FOREACHneighbor_(vertex) {
+ zinc_(Zdistvertex); /* distance also computed by main loop below */
+ qh_distplane(vertex->point, neighbor, &dist);
+ minimize_(minvertex, dist);
+ if (-dist > qh TRACEdist || dist > qh TRACEdist
+ || neighbor == qh tracefacet || vertex == qh tracevertex)
+ qh_fprintf(qh ferr, 8093, "qh_check_maxout: p%d(v%d) is %.2g from f%d\n",
+ qh_pointid(vertex->point), vertex->id, dist, neighbor->id);
+ }
+ }
+ if (qh MERGING) {
+ wmin_(Wminvertex, qh min_vertex);
+ }
+ qh min_vertex= minvertex;
+ qh_settempfree(&vertices);
+ }
+ facets= qh_pointfacet(/*qh facet_list*/);
+ do {
+ old_maxoutside= fmax_(qh max_outside, maxoutside);
+ FOREACHfacet_i_(facets) { /* for each point with facet assignment */
+ if (facet) {
+ point= qh_point(facet_i);
+ if (point == qh GOODpointp)
+ continue;
+ zzinc_(Ztotcheck);
+ qh_distplane(point, facet, &dist);
+ numpart++;
+ bestfacet= qh_findbesthorizon(qh_IScheckmax, point, facet, !qh_NOupper, &dist, &numpart);
+ if (bestfacet && dist > maxoutside) {
+ if (qh ONLYgood && !bestfacet->good
+ && !((bestfacet= qh_findgooddist(point, bestfacet, &dist, &facetlist))
+ && dist > maxoutside))
+ notgood++;
+ else
+ maxoutside= dist;
+ }
+ if (dist > qh TRACEdist || (bestfacet && bestfacet == qh tracefacet))
+ qh_fprintf(qh ferr, 8094, "qh_check_maxout: p%d is %.2g above f%d\n",
+ qh_pointid(point), dist, bestfacet->id);
+ }
+ }
+ }while
+ (maxoutside > 2*old_maxoutside);
+ /* if qh.maxoutside increases substantially, qh_SEARCHdist is not valid
+ e.g., RBOX 5000 s Z1 G1e-13 t1001200614 | qhull */
+ zzadd_(Zcheckpart, numpart);
+ qh_settempfree(&facets);
+ wval_(Wmaxout)= maxoutside - qh max_outside;
+ wmax_(Wmaxoutside, qh max_outside);
+ qh max_outside= maxoutside;
+ qh_nearcoplanar(/*qh.facet_list*/);
+ qh maxoutdone= True;
+ trace1((qh ferr, 1024, "qh_check_maxout: maxoutside %2.2g, min_vertex %2.2g, outside of not good %d\n",
+ maxoutside, qh min_vertex, notgood));
+} /* check_maxout */
+#else /* qh_NOmerge */
+void qh_check_maxout(void) {
+}
+#endif
+
+/*---------------------------------
+
+ qh_check_output()
+ performs the checks at the end of qhull algorithm
+ Maybe called after voronoi output. Will recompute otherwise centrums are Voronoi centers instead
+*/
+void qh_check_output(void) {
+ int i;
+
+ if (qh STOPcone)
+ return;
+ if (qh VERIFYoutput | qh IStracing | qh CHECKfrequently) {
+ qh_checkpolygon(qh facet_list);
+ qh_checkflipped_all(qh facet_list);
+ qh_checkconvex(qh facet_list, qh_ALGORITHMfault);
+ }else if (!qh MERGING && qh_newstats(qhstat precision, &i)) {
+ qh_checkflipped_all(qh facet_list);
+ qh_checkconvex(qh facet_list, qh_ALGORITHMfault);
+ }
+} /* check_output */
+
+
+
+/*---------------------------------
+
+ qh_check_point( point, facet, maxoutside, maxdist, errfacet1, errfacet2 )
+ check that point is less than maxoutside from facet
+*/
+void qh_check_point(pointT *point, facetT *facet, realT *maxoutside, realT *maxdist, facetT **errfacet1, facetT **errfacet2) {
+ realT dist;
+
+ /* occurs after statistics reported */
+ qh_distplane(point, facet, &dist);
+ if (dist > *maxoutside) {
+ if (*errfacet1 != facet) {
+ *errfacet2= *errfacet1;
+ *errfacet1= facet;
+ }
+ qh_fprintf(qh ferr, 6111, "qhull precision error: point p%d is outside facet f%d, distance= %6.8g maxoutside= %6.8g\n",
+ qh_pointid(point), facet->id, dist, *maxoutside);
+ }
+ maximize_(*maxdist, dist);
+} /* qh_check_point */
+
+
+/*---------------------------------
+
+ qh_check_points()
+ checks that all points are inside all facets
+
+ notes:
+ if many points and qh_check_maxout not called (i.e., !qh.MERGING),
+ calls qh_findbesthorizon (seldom done).
+ ignores flipped facets
+ maxoutside includes 2 qh.DISTrounds
+ one qh.DISTround for the computed distances in qh_check_points
+ qh_printafacet and qh_printsummary needs only one qh.DISTround
+ the computation for qh.VERIFYdirect does not account for qh.other_points
+
+ design:
+ if many points
+ use qh_check_bestdist()
+ else
+ for all facets
+ for all points
+ check that point is inside facet
+*/
+void qh_check_points(void) {
+ facetT *facet, *errfacet1= NULL, *errfacet2= NULL;
+ realT total, maxoutside, maxdist= -REALmax;
+ pointT *point, **pointp, *pointtemp;
+ boolT testouter;
+
+ maxoutside= qh_maxouter();
+ maxoutside += qh DISTround;
+ /* one more qh.DISTround for check computation */
+ trace1((qh ferr, 1025, "qh_check_points: check all points below %2.2g of all facet planes\n",
+ maxoutside));
+ if (qh num_good) /* miss counts other_points and !good facets */
+ total= (float)qh num_good * (float)qh num_points;
+ else
+ total= (float)qh num_facets * (float)qh num_points;
+ if (total >= qh_VERIFYdirect && !qh maxoutdone) {
+ if (!qh_QUICKhelp && qh SKIPcheckmax && qh MERGING)
+ qh_fprintf(qh ferr, 7075, "qhull input warning: merging without checking outer planes('Q5' or 'Po').\n\
+Verify may report that a point is outside of a facet.\n");
+ qh_check_bestdist();
+ }else {
+ if (qh_MAXoutside && qh maxoutdone)
+ testouter= True;
+ else
+ testouter= False;
+ if (!qh_QUICKhelp) {
+ if (qh MERGEexact)
+ qh_fprintf(qh ferr, 7076, "qhull input warning: exact merge ('Qx'). Verify may report that a point\n\
+is outside of a facet. See qh-optq.htm#Qx\n");
+ else if (qh SKIPcheckmax || qh NOnearinside)
+ qh_fprintf(qh ferr, 7077, "qhull input warning: no outer plane check ('Q5') or no processing of\n\
+near-inside points ('Q8'). Verify may report that a point is outside\n\
+of a facet.\n");
+ }
+ if (qh PRINTprecision) {
+ if (testouter)
+ qh_fprintf(qh ferr, 8098, "\n\
+Output completed. Verifying that all points are below outer planes of\n\
+all %sfacets. Will make %2.0f distance computations.\n",
+ (qh ONLYgood ? "good " : ""), total);
+ else
+ qh_fprintf(qh ferr, 8099, "\n\
+Output completed. Verifying that all points are below %2.2g of\n\
+all %sfacets. Will make %2.0f distance computations.\n",
+ maxoutside, (qh ONLYgood ? "good " : ""), total);
+ }
+ FORALLfacets {
+ if (!facet->good && qh ONLYgood)
+ continue;
+ if (facet->flipped)
+ continue;
+ if (!facet->normal) {
+ qh_fprintf(qh ferr, 7061, "qhull warning (qh_check_points): missing normal for facet f%d\n", facet->id);
+ continue;
+ }
+ if (testouter) {
+#if qh_MAXoutside
+ maxoutside= facet->maxoutside + 2* qh DISTround;
+ /* one DISTround to actual point and another to computed point */
+#endif
+ }
+ FORALLpoints {
+ if (point != qh GOODpointp)
+ qh_check_point(point, facet, &maxoutside, &maxdist, &errfacet1, &errfacet2);
+ }
+ FOREACHpoint_(qh other_points) {
+ if (point != qh GOODpointp)
+ qh_check_point(point, facet, &maxoutside, &maxdist, &errfacet1, &errfacet2);
+ }
+ }
+ if (maxdist > qh outside_err) {
+ qh_fprintf(qh ferr, 6112, "qhull precision error (qh_check_points): a coplanar point is %6.2g from convex hull. The maximum value(qh.outside_err) is %6.2g\n",
+ maxdist, qh outside_err );
+ qh_errexit2( qh_ERRprec, errfacet1, errfacet2 );
+ }else if (errfacet1 && qh outside_err > REALmax/2)
+ qh_errexit2( qh_ERRprec, errfacet1, errfacet2 );
+ /* else if errfacet1, the error was logged to qh.ferr but does not effect the output */
+ trace0((qh ferr, 21, "qh_check_points: max distance outside %2.2g\n", maxdist));
+ }
+} /* check_points */
+
+
+/*---------------------------------
+
+ qh_checkconvex( facetlist, fault )
+ check that each ridge in facetlist is convex
+ fault = qh_DATAfault if reporting errors
+ = qh_ALGORITHMfault otherwise
+
+ returns:
+ counts Zconcaveridges and Zcoplanarridges
+ errors if concaveridge or if merging an coplanar ridge
+
+ note:
+ if not merging,
+ tests vertices for neighboring simplicial facets
+ else if ZEROcentrum,
+ tests vertices for neighboring simplicial facets
+ else
+ tests centrums of neighboring facets
+
+ design:
+ for all facets
+ report flipped facets
+ if ZEROcentrum and simplicial neighbors
+ test vertices for neighboring simplicial facets
+ else
+ test centrum against all neighbors
+*/
+void qh_checkconvex(facetT *facetlist, int fault) {
+ facetT *facet, *neighbor, **neighborp, *errfacet1=NULL, *errfacet2=NULL;
+ vertexT *vertex;
+ realT dist;
+ pointT *centrum;
+ boolT waserror= False, centrum_warning= False, tempcentrum= False, allsimplicial;
+ int neighbor_i;
+
+ trace1((qh ferr, 1026, "qh_checkconvex: check all ridges are convex\n"));
+ if (!qh RERUN) {
+ zzval_(Zconcaveridges)= 0;
+ zzval_(Zcoplanarridges)= 0;
+ }
+ FORALLfacet_(facetlist) {
+ if (facet->flipped) {
+ qh_precision("flipped facet");
+ qh_fprintf(qh ferr, 6113, "qhull precision error: f%d is flipped(interior point is outside)\n",
+ facet->id);
+ errfacet1= facet;
+ waserror= True;
+ continue;
+ }
+ if (qh MERGING && (!qh ZEROcentrum || !facet->simplicial || facet->tricoplanar))
+ allsimplicial= False;
+ else {
+ allsimplicial= True;
+ neighbor_i= 0;
+ FOREACHneighbor_(facet) {
+ vertex= SETelemt_(facet->vertices, neighbor_i++, vertexT);
+ if (!neighbor->simplicial || neighbor->tricoplanar) {
+ allsimplicial= False;
+ continue;
+ }
+ qh_distplane(vertex->point, neighbor, &dist);
+ if (dist > -qh DISTround) {
+ if (fault == qh_DATAfault) {
+ qh_precision("coplanar or concave ridge");
+ qh_fprintf(qh ferr, 6114, "qhull precision error: initial simplex is not convex. Distance=%.2g\n", dist);
+ qh_errexit(qh_ERRsingular, NULL, NULL);
+ }
+ if (dist > qh DISTround) {
+ zzinc_(Zconcaveridges);
+ qh_precision("concave ridge");
+ qh_fprintf(qh ferr, 6115, "qhull precision error: f%d is concave to f%d, since p%d(v%d) is %6.4g above\n",
+ facet->id, neighbor->id, qh_pointid(vertex->point), vertex->id, dist);
+ errfacet1= facet;
+ errfacet2= neighbor;
+ waserror= True;
+ }else if (qh ZEROcentrum) {
+ if (dist > 0) { /* qh_checkzero checks that dist < - qh DISTround */
+ zzinc_(Zcoplanarridges);
+ qh_precision("coplanar ridge");
+ qh_fprintf(qh ferr, 6116, "qhull precision error: f%d is clearly not convex to f%d, since p%d(v%d) is %6.4g above\n",
+ facet->id, neighbor->id, qh_pointid(vertex->point), vertex->id, dist);
+ errfacet1= facet;
+ errfacet2= neighbor;
+ waserror= True;
+ }
+ }else {
+ zzinc_(Zcoplanarridges);
+ qh_precision("coplanar ridge");
+ trace0((qh ferr, 22, "qhull precision error: f%d may be coplanar to f%d, since p%d(v%d) is within %6.4g during p%d\n",
+ facet->id, neighbor->id, qh_pointid(vertex->point), vertex->id, dist, qh furthest_id));
+ }
+ }
+ }
+ }
+ if (!allsimplicial) {
+ if (qh CENTERtype == qh_AScentrum) {
+ if (!facet->center)
+ facet->center= qh_getcentrum(facet);
+ centrum= facet->center;
+ }else {
+ if (!centrum_warning && (!facet->simplicial || facet->tricoplanar)) {
+ centrum_warning= True;
+ qh_fprintf(qh ferr, 7062, "qhull warning: recomputing centrums for convexity test. This may lead to false, precision errors.\n");
+ }
+ centrum= qh_getcentrum(facet);
+ tempcentrum= True;
+ }
+ FOREACHneighbor_(facet) {
+ if (qh ZEROcentrum && facet->simplicial && neighbor->simplicial)
+ continue;
+ if (facet->tricoplanar || neighbor->tricoplanar)
+ continue;
+ zzinc_(Zdistconvex);
+ qh_distplane(centrum, neighbor, &dist);
+ if (dist > qh DISTround) {
+ zzinc_(Zconcaveridges);
+ qh_precision("concave ridge");
+ qh_fprintf(qh ferr, 6117, "qhull precision error: f%d is concave to f%d. Centrum of f%d is %6.4g above f%d\n",
+ facet->id, neighbor->id, facet->id, dist, neighbor->id);
+ errfacet1= facet;
+ errfacet2= neighbor;
+ waserror= True;
+ }else if (dist >= 0.0) { /* if arithmetic always rounds the same,
+ can test against centrum radius instead */
+ zzinc_(Zcoplanarridges);
+ qh_precision("coplanar ridge");
+ qh_fprintf(qh ferr, 6118, "qhull precision error: f%d is coplanar or concave to f%d. Centrum of f%d is %6.4g above f%d\n",
+ facet->id, neighbor->id, facet->id, dist, neighbor->id);
+ errfacet1= facet;
+ errfacet2= neighbor;
+ waserror= True;
+ }
+ }
+ if (tempcentrum)
+ qh_memfree(centrum, qh normal_size);
+ }
+ }
+ if (waserror && !qh FORCEoutput)
+ qh_errexit2 (qh_ERRprec, errfacet1, errfacet2);
+} /* checkconvex */
+
+
+/*---------------------------------
+
+ qh_checkfacet( facet, newmerge, waserror )
+ checks for consistency errors in facet
+ newmerge set if from merge.c
+
+ returns:
+ sets waserror if any error occurs
+
+ checks:
+ vertex ids are inverse sorted
+ unless newmerge, at least hull_dim neighbors and vertices (exactly if simplicial)
+ if non-simplicial, at least as many ridges as neighbors
+ neighbors are not duplicated
+ ridges are not duplicated
+ in 3-d, ridges=verticies
+ (qh.hull_dim-1) ridge vertices
+ neighbors are reciprocated
+ ridge neighbors are facet neighbors and a ridge for every neighbor
+ simplicial neighbors match facetintersect
+ vertex intersection matches vertices of common ridges
+ vertex neighbors and facet vertices agree
+ all ridges have distinct vertex sets
+
+ notes:
+ uses neighbor->seen
+
+ design:
+ check sets
+ check vertices
+ check sizes of neighbors and vertices
+ check for qh_MERGEridge and qh_DUPLICATEridge flags
+ check neighbor set
+ check ridge set
+ check ridges, neighbors, and vertices
+*/
+void qh_checkfacet(facetT *facet, boolT newmerge, boolT *waserrorp) {
+ facetT *neighbor, **neighborp, *errother=NULL;
+ ridgeT *ridge, **ridgep, *errridge= NULL, *ridge2;
+ vertexT *vertex, **vertexp;
+ unsigned previousid= INT_MAX;
+ int numneighbors, numvertices, numridges=0, numRvertices=0;
+ boolT waserror= False;
+ int skipA, skipB, ridge_i, ridge_n, i;
+ setT *intersection;
+
+ if (facet->visible) {
+ qh_fprintf(qh ferr, 6119, "qhull internal error (qh_checkfacet): facet f%d is on the visible_list\n",
+ facet->id);
+ qh_errexit(qh_ERRqhull, facet, NULL);
+ }
+ if (!facet->normal) {
+ qh_fprintf(qh ferr, 6120, "qhull internal error (qh_checkfacet): facet f%d does not have a normal\n",
+ facet->id);
+ waserror= True;
+ }
+ qh_setcheck(facet->vertices, "vertices for f", facet->id);
+ qh_setcheck(facet->ridges, "ridges for f", facet->id);
+ qh_setcheck(facet->outsideset, "outsideset for f", facet->id);
+ qh_setcheck(facet->coplanarset, "coplanarset for f", facet->id);
+ qh_setcheck(facet->neighbors, "neighbors for f", facet->id);
+ FOREACHvertex_(facet->vertices) {
+ if (vertex->deleted) {
+ qh_fprintf(qh ferr, 6121, "qhull internal error (qh_checkfacet): deleted vertex v%d in f%d\n", vertex->id, facet->id);
+ qh_errprint("ERRONEOUS", NULL, NULL, NULL, vertex);
+ waserror= True;
+ }
+ if (vertex->id >= previousid) {
+ qh_fprintf(qh ferr, 6122, "qhull internal error (qh_checkfacet): vertices of f%d are not in descending id order at v%d\n", facet->id, vertex->id);
+ waserror= True;
+ break;
+ }
+ previousid= vertex->id;
+ }
+ numneighbors= qh_setsize(facet->neighbors);
+ numvertices= qh_setsize(facet->vertices);
+ numridges= qh_setsize(facet->ridges);
+ if (facet->simplicial) {
+ if (numvertices+numneighbors != 2*qh hull_dim
+ && !facet->degenerate && !facet->redundant) {
+ qh_fprintf(qh ferr, 6123, "qhull internal error (qh_checkfacet): for simplicial facet f%d, #vertices %d + #neighbors %d != 2*qh hull_dim\n",
+ facet->id, numvertices, numneighbors);
+ qh_setprint(qh ferr, "", facet->neighbors);
+ waserror= True;
+ }
+ }else { /* non-simplicial */
+ if (!newmerge
+ &&(numvertices < qh hull_dim || numneighbors < qh hull_dim)
+ && !facet->degenerate && !facet->redundant) {
+ qh_fprintf(qh ferr, 6124, "qhull internal error (qh_checkfacet): for facet f%d, #vertices %d or #neighbors %d < qh hull_dim\n",
+ facet->id, numvertices, numneighbors);
+ waserror= True;
+ }
+ /* in 3-d, can get a vertex twice in an edge list, e.g., RBOX 1000 s W1e-13 t995849315 D2 | QHULL d Tc Tv TP624 TW1e-13 T4 */
+ if (numridges < numneighbors
+ ||(qh hull_dim == 3 && numvertices > numridges && !qh NEWfacets)
+ ||(qh hull_dim == 2 && numridges + numvertices + numneighbors != 6)) {
+ if (!facet->degenerate && !facet->redundant) {
+ qh_fprintf(qh ferr, 6125, "qhull internal error (qh_checkfacet): for facet f%d, #ridges %d < #neighbors %d or(3-d) > #vertices %d or(2-d) not all 2\n",
+ facet->id, numridges, numneighbors, numvertices);
+ waserror= True;
+ }
+ }
+ }
+ FOREACHneighbor_(facet) {
+ if (neighbor == qh_MERGEridge || neighbor == qh_DUPLICATEridge) {
+ qh_fprintf(qh ferr, 6126, "qhull internal error (qh_checkfacet): facet f%d still has a MERGE or DUP neighbor\n", facet->id);
+ qh_errexit(qh_ERRqhull, facet, NULL);
+ }
+ neighbor->seen= True;
+ }
+ FOREACHneighbor_(facet) {
+ if (!qh_setin(neighbor->neighbors, facet)) {
+ qh_fprintf(qh ferr, 6127, "qhull internal error (qh_checkfacet): facet f%d has neighbor f%d, but f%d does not have neighbor f%d\n",
+ facet->id, neighbor->id, neighbor->id, facet->id);
+ errother= neighbor;
+ waserror= True;
+ }
+ if (!neighbor->seen) {
+ qh_fprintf(qh ferr, 6128, "qhull internal error (qh_checkfacet): facet f%d has a duplicate neighbor f%d\n",
+ facet->id, neighbor->id);
+ errother= neighbor;
+ waserror= True;
+ }
+ neighbor->seen= False;
+ }
+ FOREACHridge_(facet->ridges) {
+ qh_setcheck(ridge->vertices, "vertices for r", ridge->id);
+ ridge->seen= False;
+ }
+ FOREACHridge_(facet->ridges) {
+ if (ridge->seen) {
+ qh_fprintf(qh ferr, 6129, "qhull internal error (qh_checkfacet): facet f%d has a duplicate ridge r%d\n",
+ facet->id, ridge->id);
+ errridge= ridge;
+ waserror= True;
+ }
+ ridge->seen= True;
+ numRvertices= qh_setsize(ridge->vertices);
+ if (numRvertices != qh hull_dim - 1) {
+ qh_fprintf(qh ferr, 6130, "qhull internal error (qh_checkfacet): ridge between f%d and f%d has %d vertices\n",
+ ridge->top->id, ridge->bottom->id, numRvertices);
+ errridge= ridge;
+ waserror= True;
+ }
+ neighbor= otherfacet_(ridge, facet);
+ neighbor->seen= True;
+ if (!qh_setin(facet->neighbors, neighbor)) {
+ qh_fprintf(qh ferr, 6131, "qhull internal error (qh_checkfacet): for facet f%d, neighbor f%d of ridge r%d not in facet\n",
+ facet->id, neighbor->id, ridge->id);
+ errridge= ridge;
+ waserror= True;
+ }
+ }
+ if (!facet->simplicial) {
+ FOREACHneighbor_(facet) {
+ if (!neighbor->seen) {
+ qh_fprintf(qh ferr, 6132, "qhull internal error (qh_checkfacet): facet f%d does not have a ridge for neighbor f%d\n",
+ facet->id, neighbor->id);
+ errother= neighbor;
+ waserror= True;
+ }
+ intersection= qh_vertexintersect_new(facet->vertices, neighbor->vertices);
+ qh_settemppush(intersection);
+ FOREACHvertex_(facet->vertices) {
+ vertex->seen= False;
+ vertex->seen2= False;
+ }
+ FOREACHvertex_(intersection)
+ vertex->seen= True;
+ FOREACHridge_(facet->ridges) {
+ if (neighbor != otherfacet_(ridge, facet))
+ continue;
+ FOREACHvertex_(ridge->vertices) {
+ if (!vertex->seen) {
+ qh_fprintf(qh ferr, 6133, "qhull internal error (qh_checkfacet): vertex v%d in r%d not in f%d intersect f%d\n",
+ vertex->id, ridge->id, facet->id, neighbor->id);
+ qh_errexit(qh_ERRqhull, facet, ridge);
+ }
+ vertex->seen2= True;
+ }
+ }
+ if (!newmerge) {
+ FOREACHvertex_(intersection) {
+ if (!vertex->seen2) {
+ if (qh IStracing >=3 || !qh MERGING) {
+ qh_fprintf(qh ferr, 6134, "qhull precision error (qh_checkfacet): vertex v%d in f%d intersect f%d but\n\
+ not in a ridge. This is ok under merging. Last point was p%d\n",
+ vertex->id, facet->id, neighbor->id, qh furthest_id);
+ if (!qh FORCEoutput && !qh MERGING) {
+ qh_errprint("ERRONEOUS", facet, neighbor, NULL, vertex);
+ if (!qh MERGING)
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ }
+ }
+ }
+ }
+ qh_settempfree(&intersection);
+ }
+ }else { /* simplicial */
+ FOREACHneighbor_(facet) {
+ if (neighbor->simplicial) {
+ skipA= SETindex_(facet->neighbors, neighbor);
+ skipB= qh_setindex(neighbor->neighbors, facet);
+ if (!qh_setequal_skip(facet->vertices, skipA, neighbor->vertices, skipB)) {
+ qh_fprintf(qh ferr, 6135, "qhull internal error (qh_checkfacet): facet f%d skip %d and neighbor f%d skip %d do not match \n",
+ facet->id, skipA, neighbor->id, skipB);
+ errother= neighbor;
+ waserror= True;
+ }
+ }
+ }
+ }
+ if (qh hull_dim < 5 && (qh IStracing > 2 || qh CHECKfrequently)) {
+ FOREACHridge_i_(facet->ridges) { /* expensive */
+ for (i=ridge_i+1; i < ridge_n; i++) {
+ ridge2= SETelemt_(facet->ridges, i, ridgeT);
+ if (qh_setequal(ridge->vertices, ridge2->vertices)) {
+ qh_fprintf(qh ferr, 6227, "Qhull internal error (qh_checkfacet): ridges r%d and r%d have the same vertices\n",
+ ridge->id, ridge2->id);
+ errridge= ridge;
+ waserror= True;
+ }
+ }
+ }
+ }
+ if (waserror) {
+ qh_errprint("ERRONEOUS", facet, errother, errridge, NULL);
+ *waserrorp= True;
+ }
+} /* checkfacet */
+
+
+/*---------------------------------
+
+ qh_checkflipped_all( facetlist )
+ checks orientation of facets in list against interior point
+*/
+void qh_checkflipped_all(facetT *facetlist) {
+ facetT *facet;
+ boolT waserror= False;
+ realT dist;
+
+ if (facetlist == qh facet_list)
+ zzval_(Zflippedfacets)= 0;
+ FORALLfacet_(facetlist) {
+ if (facet->normal && !qh_checkflipped(facet, &dist, !qh_ALL)) {
+ qh_fprintf(qh ferr, 6136, "qhull precision error: facet f%d is flipped, distance= %6.12g\n",
+ facet->id, dist);
+ if (!qh FORCEoutput) {
+ qh_errprint("ERRONEOUS", facet, NULL, NULL, NULL);
+ waserror= True;
+ }
+ }
+ }
+ if (waserror) {
+ qh_fprintf(qh ferr, 8101, "\n\
+A flipped facet occurs when its distance to the interior point is\n\
+greater than %2.2g, the maximum roundoff error.\n", -qh DISTround);
+ qh_errexit(qh_ERRprec, NULL, NULL);
+ }
+} /* checkflipped_all */
+
+/*---------------------------------
+
+ qh_checkpolygon( facetlist )
+ checks the correctness of the structure
+
+ notes:
+ call with either qh.facet_list or qh.newfacet_list
+ checks num_facets and num_vertices if qh.facet_list
+
+ design:
+ for each facet
+ checks facet and outside set
+ initializes vertexlist
+ for each facet
+ checks vertex set
+ if checking all facets(qh.facetlist)
+ check facet count
+ if qh.VERTEXneighbors
+ check vertex neighbors and count
+ check vertex count
+*/
+void qh_checkpolygon(facetT *facetlist) {
+ facetT *facet;
+ vertexT *vertex, **vertexp, *vertexlist;
+ int numfacets= 0, numvertices= 0, numridges= 0;
+ int totvneighbors= 0, totvertices= 0;
+ boolT waserror= False, nextseen= False, visibleseen= False;
+
+ trace1((qh ferr, 1027, "qh_checkpolygon: check all facets from f%d\n", facetlist->id));
+ if (facetlist != qh facet_list || qh ONLYgood)
+ nextseen= True;
+ FORALLfacet_(facetlist) {
+ if (facet == qh visible_list)
+ visibleseen= True;
+ if (!facet->visible) {
+ if (!nextseen) {
+ if (facet == qh facet_next)
+ nextseen= True;
+ else if (qh_setsize(facet->outsideset)) {
+ if (!qh NARROWhull
+#if !qh_COMPUTEfurthest
+ || facet->furthestdist >= qh MINoutside
+#endif
+ ) {
+ qh_fprintf(qh ferr, 6137, "qhull internal error (qh_checkpolygon): f%d has outside points before qh facet_next\n",
+ facet->id);
+ qh_errexit(qh_ERRqhull, facet, NULL);
+ }
+ }
+ }
+ numfacets++;
+ qh_checkfacet(facet, False, &waserror);
+ }
+ }
+ if (qh visible_list && !visibleseen && facetlist == qh facet_list) {
+ qh_fprintf(qh ferr, 6138, "qhull internal error (qh_checkpolygon): visible list f%d no longer on facet list\n", qh visible_list->id);
+ qh_printlists();
+ qh_errexit(qh_ERRqhull, qh visible_list, NULL);
+ }
+ if (facetlist == qh facet_list)
+ vertexlist= qh vertex_list;
+ else if (facetlist == qh newfacet_list)
+ vertexlist= qh newvertex_list;
+ else
+ vertexlist= NULL;
+ FORALLvertex_(vertexlist) {
+ vertex->seen= False;
+ vertex->visitid= 0;
+ }
+ FORALLfacet_(facetlist) {
+ if (facet->visible)
+ continue;
+ if (facet->simplicial)
+ numridges += qh hull_dim;
+ else
+ numridges += qh_setsize(facet->ridges);
+ FOREACHvertex_(facet->vertices) {
+ vertex->visitid++;
+ if (!vertex->seen) {
+ vertex->seen= True;
+ numvertices++;
+ if (qh_pointid(vertex->point) == -1) {
+ qh_fprintf(qh ferr, 6139, "qhull internal error (qh_checkpolygon): unknown point %p for vertex v%d first_point %p\n",
+ vertex->point, vertex->id, qh first_point);
+ waserror= True;
+ }
+ }
+ }
+ }
+ qh vertex_visit += (unsigned int)numfacets;
+ if (facetlist == qh facet_list) {
+ if (numfacets != qh num_facets - qh num_visible) {
+ qh_fprintf(qh ferr, 6140, "qhull internal error (qh_checkpolygon): actual number of facets is %d, cumulative facet count is %d - %d visible facets\n",
+ numfacets, qh num_facets, qh num_visible);
+ waserror= True;
+ }
+ qh vertex_visit++;
+ if (qh VERTEXneighbors) {
+ FORALLvertices {
+ qh_setcheck(vertex->neighbors, "neighbors for v", vertex->id);
+ if (vertex->deleted)
+ continue;
+ totvneighbors += qh_setsize(vertex->neighbors);
+ }
+ FORALLfacet_(facetlist)
+ totvertices += qh_setsize(facet->vertices);
+ if (totvneighbors != totvertices) {
+ qh_fprintf(qh ferr, 6141, "qhull internal error (qh_checkpolygon): vertex neighbors inconsistent. Totvneighbors %d, totvertices %d\n",
+ totvneighbors, totvertices);
+ waserror= True;
+ }
+ }
+ if (numvertices != qh num_vertices - qh_setsize(qh del_vertices)) {
+ qh_fprintf(qh ferr, 6142, "qhull internal error (qh_checkpolygon): actual number of vertices is %d, cumulative vertex count is %d\n",
+ numvertices, qh num_vertices - qh_setsize(qh del_vertices));
+ waserror= True;
+ }
+ if (qh hull_dim == 2 && numvertices != numfacets) {
+ qh_fprintf(qh ferr, 6143, "qhull internal error (qh_checkpolygon): #vertices %d != #facets %d\n",
+ numvertices, numfacets);
+ waserror= True;
+ }
+ if (qh hull_dim == 3 && numvertices + numfacets - numridges/2 != 2) {
+ qh_fprintf(qh ferr, 7063, "qhull warning: #vertices %d + #facets %d - #edges %d != 2\n\
+ A vertex appears twice in a edge list. May occur during merging.",
+ numvertices, numfacets, numridges/2);
+ /* occurs if lots of merging and a vertex ends up twice in an edge list. e.g., RBOX 1000 s W1e-13 t995849315 D2 | QHULL d Tc Tv */
+ }
+ }
+ if (waserror)
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+} /* checkpolygon */
+
+
+/*---------------------------------
+
+ qh_checkvertex( vertex )
+ check vertex for consistency
+ checks vertex->neighbors
+
+ notes:
+ neighbors checked efficiently in checkpolygon
+*/
+void qh_checkvertex(vertexT *vertex) {
+ boolT waserror= False;
+ facetT *neighbor, **neighborp, *errfacet=NULL;
+
+ if (qh_pointid(vertex->point) == -1) {
+ qh_fprintf(qh ferr, 6144, "qhull internal error (qh_checkvertex): unknown point id %p\n", vertex->point);
+ waserror= True;
+ }
+ if (vertex->id >= qh vertex_id) {
+ qh_fprintf(qh ferr, 6145, "qhull internal error (qh_checkvertex): unknown vertex id %d\n", vertex->id);
+ waserror= True;
+ }
+ if (!waserror && !vertex->deleted) {
+ if (qh_setsize(vertex->neighbors)) {
+ FOREACHneighbor_(vertex) {
+ if (!qh_setin(neighbor->vertices, vertex)) {
+ qh_fprintf(qh ferr, 6146, "qhull internal error (qh_checkvertex): neighbor f%d does not contain v%d\n", neighbor->id, vertex->id);
+ errfacet= neighbor;
+ waserror= True;
+ }
+ }
+ }
+ }
+ if (waserror) {
+ qh_errprint("ERRONEOUS", NULL, NULL, NULL, vertex);
+ qh_errexit(qh_ERRqhull, errfacet, NULL);
+ }
+} /* checkvertex */
+
+/*---------------------------------
+
+ qh_clearcenters( type )
+ clear old data from facet->center
+
+ notes:
+ sets new centertype
+ nop if CENTERtype is the same
+*/
+void qh_clearcenters(qh_CENTER type) {
+ facetT *facet;
+
+ if (qh CENTERtype != type) {
+ FORALLfacets {
+ if (facet->tricoplanar && !facet->keepcentrum)
+ facet->center= NULL;
+ else if (qh CENTERtype == qh_ASvoronoi){
+ if (facet->center) {
+ qh_memfree(facet->center, qh center_size);
+ facet->center= NULL;
+ }
+ }else /* qh CENTERtype == qh_AScentrum */ {
+ if (facet->center) {
+ qh_memfree(facet->center, qh normal_size);
+ facet->center= NULL;
+ }
+ }
+ }
+ qh CENTERtype= type;
+ }
+ trace2((qh ferr, 2043, "qh_clearcenters: switched to center type %d\n", type));
+} /* clearcenters */
+
+/*---------------------------------
+
+ qh_createsimplex( vertices )
+ creates a simplex from a set of vertices
+
+ returns:
+ initializes qh.facet_list to the simplex
+ initializes qh.newfacet_list, .facet_tail
+ initializes qh.vertex_list, .newvertex_list, .vertex_tail
+
+ design:
+ initializes lists
+ for each vertex
+ create a new facet
+ for each new facet
+ create its neighbor set
+*/
+void qh_createsimplex(setT *vertices) {
+ facetT *facet= NULL, *newfacet;
+ boolT toporient= True;
+ int vertex_i, vertex_n, nth;
+ setT *newfacets= qh_settemp(qh hull_dim+1);
+ vertexT *vertex;
+
+ qh facet_list= qh newfacet_list= qh facet_tail= qh_newfacet();
+ qh num_facets= qh num_vertices= qh num_visible= 0;
+ qh vertex_list= qh newvertex_list= qh vertex_tail= qh_newvertex(NULL);
+ FOREACHvertex_i_(vertices) {
+ newfacet= qh_newfacet();
+ newfacet->vertices= qh_setnew_delnthsorted(vertices, vertex_n,
+ vertex_i, 0);
+ newfacet->toporient= (unsigned char)toporient;
+ qh_appendfacet(newfacet);
+ newfacet->newfacet= True;
+ qh_appendvertex(vertex);
+ qh_setappend(&newfacets, newfacet);
+ toporient ^= True;
+ }
+ FORALLnew_facets {
+ nth= 0;
+ FORALLfacet_(qh newfacet_list) {
+ if (facet != newfacet)
+ SETelem_(newfacet->neighbors, nth++)= facet;
+ }
+ qh_settruncate(newfacet->neighbors, qh hull_dim);
+ }
+ qh_settempfree(&newfacets);
+ trace1((qh ferr, 1028, "qh_createsimplex: created simplex\n"));
+} /* createsimplex */
+
+/*---------------------------------
+
+ qh_delridge( ridge )
+ deletes ridge from data structures it belongs to
+ frees up its memory
+
+ notes:
+ in merge.c, caller sets vertex->delridge for each vertex
+ ridges also freed in qh_freeqhull
+*/
+void qh_delridge(ridgeT *ridge) {
+ void **freelistp; /* used !qh_NOmem */
+
+ qh_setdel(ridge->top->ridges, ridge);
+ qh_setdel(ridge->bottom->ridges, ridge);
+ qh_setfree(&(ridge->vertices));
+ qh_memfree_(ridge, (int)sizeof(ridgeT), freelistp);
+} /* delridge */
+
+
+/*---------------------------------
+
+ qh_delvertex( vertex )
+ deletes a vertex and frees its memory
+
+ notes:
+ assumes vertex->adjacencies have been updated if needed
+ unlinks from vertex_list
+*/
+void qh_delvertex(vertexT *vertex) {
+
+ if (vertex == qh tracevertex)
+ qh tracevertex= NULL;
+ qh_removevertex(vertex);
+ qh_setfree(&vertex->neighbors);
+ qh_memfree(vertex, (int)sizeof(vertexT));
+} /* delvertex */
+
+
+/*---------------------------------
+
+ qh_facet3vertex( )
+ return temporary set of 3-d vertices in qh_ORIENTclock order
+
+ design:
+ if simplicial facet
+ build set from facet->vertices with facet->toporient
+ else
+ for each ridge in order
+ build set from ridge's vertices
+*/
+setT *qh_facet3vertex(facetT *facet) {
+ ridgeT *ridge, *firstridge;
+ vertexT *vertex;
+ int cntvertices, cntprojected=0;
+ setT *vertices;
+
+ cntvertices= qh_setsize(facet->vertices);
+ vertices= qh_settemp(cntvertices);
+ if (facet->simplicial) {
+ if (cntvertices != 3) {
+ qh_fprintf(qh ferr, 6147, "qhull internal error (qh_facet3vertex): only %d vertices for simplicial facet f%d\n",
+ cntvertices, facet->id);
+ qh_errexit(qh_ERRqhull, facet, NULL);
+ }
+ qh_setappend(&vertices, SETfirst_(facet->vertices));
+ if (facet->toporient ^ qh_ORIENTclock)
+ qh_setappend(&vertices, SETsecond_(facet->vertices));
+ else
+ qh_setaddnth(&vertices, 0, SETsecond_(facet->vertices));
+ qh_setappend(&vertices, SETelem_(facet->vertices, 2));
+ }else {
+ ridge= firstridge= SETfirstt_(facet->ridges, ridgeT); /* no infinite */
+ while ((ridge= qh_nextridge3d(ridge, facet, &vertex))) {
+ qh_setappend(&vertices, vertex);
+ if (++cntprojected > cntvertices || ridge == firstridge)
+ break;
+ }
+ if (!ridge || cntprojected != cntvertices) {
+ qh_fprintf(qh ferr, 6148, "qhull internal error (qh_facet3vertex): ridges for facet %d don't match up. got at least %d\n",
+ facet->id, cntprojected);
+ qh_errexit(qh_ERRqhull, facet, ridge);
+ }
+ }
+ return vertices;
+} /* facet3vertex */
+
+/*---------------------------------
+
+ qh_findbestfacet( point, bestoutside, bestdist, isoutside )
+ find facet that is furthest below a point
+
+ for Delaunay triangulations,
+ Use qh_setdelaunay() to lift point to paraboloid and scale by 'Qbb' if needed
+ Do not use options 'Qbk', 'QBk', or 'QbB' since they scale the coordinates.
+
+ returns:
+ if bestoutside is set (e.g., qh_ALL)
+ returns best facet that is not upperdelaunay
+ if Delaunay and inside, point is outside circumsphere of bestfacet
+ else
+ returns first facet below point
+ if point is inside, returns nearest, !upperdelaunay facet
+ distance to facet
+ isoutside set if outside of facet
+
+ notes:
+ For tricoplanar facets, this finds one of the tricoplanar facets closest
+ to the point. For Delaunay triangulations, the point may be inside a
+ different tricoplanar facet. See locate a facet with qh_findbestfacet()
+
+ If inside, qh_findbestfacet performs an exhaustive search
+ this may be too conservative. Sometimes it is clearly required.
+
+ qh_findbestfacet is not used by qhull.
+ uses qh.visit_id and qh.coplanarset
+
+ see:
+ qh_findbest
+*/
+facetT *qh_findbestfacet(pointT *point, boolT bestoutside,
+ realT *bestdist, boolT *isoutside) {
+ facetT *bestfacet= NULL;
+ int numpart, totpart= 0;
+
+ bestfacet= qh_findbest(point, qh facet_list,
+ bestoutside, !qh_ISnewfacets, bestoutside /* qh_NOupper */,
+ bestdist, isoutside, &totpart);
+ if (*bestdist < -qh DISTround) {
+ bestfacet= qh_findfacet_all(point, bestdist, isoutside, &numpart);
+ totpart += numpart;
+ if ((isoutside && bestoutside)
+ || (!isoutside && bestfacet->upperdelaunay)) {
+ bestfacet= qh_findbest(point, bestfacet,
+ bestoutside, False, bestoutside,
+ bestdist, isoutside, &totpart);
+ totpart += numpart;
+ }
+ }
+ trace3((qh ferr, 3014, "qh_findbestfacet: f%d dist %2.2g isoutside %d totpart %d\n",
+ bestfacet->id, *bestdist, *isoutside, totpart));
+ return bestfacet;
+} /* findbestfacet */
+
+/*---------------------------------
+
+ qh_findbestlower( facet, point, bestdist, numpart )
+ returns best non-upper, non-flipped neighbor of facet for point
+ if needed, searches vertex neighbors
+
+ returns:
+ returns bestdist and updates numpart
+
+ notes:
+ if Delaunay and inside, point is outside of circumsphere of bestfacet
+ called by qh_findbest() for points above an upperdelaunay facet
+
+*/
+facetT *qh_findbestlower(facetT *upperfacet, pointT *point, realT *bestdistp, int *numpart) {
+ facetT *neighbor, **neighborp, *bestfacet= NULL;
+ realT bestdist= -REALmax/2 /* avoid underflow */;
+ realT dist;
+ vertexT *vertex;
+
+ zinc_(Zbestlower);
+ FOREACHneighbor_(upperfacet) {
+ if (neighbor->upperdelaunay || neighbor->flipped)
+ continue;
+ (*numpart)++;
+ qh_distplane(point, neighbor, &dist);
+ if (dist > bestdist) {
+ bestfacet= neighbor;
+ bestdist= dist;
+ }
+ }
+ if (!bestfacet) {
+ zinc_(Zbestlowerv);
+ /* rarely called, numpart does not count nearvertex computations */
+ vertex= qh_nearvertex(upperfacet, point, &dist);
+ qh_vertexneighbors();
+ FOREACHneighbor_(vertex) {
+ if (neighbor->upperdelaunay || neighbor->flipped)
+ continue;
+ (*numpart)++;
+ qh_distplane(point, neighbor, &dist);
+ if (dist > bestdist) {
+ bestfacet= neighbor;
+ bestdist= dist;
+ }
+ }
+ }
+ if (!bestfacet) {
+ qh_fprintf(qh ferr, 6228, "\n\
+Qhull internal error (qh_findbestlower): all neighbors of facet %d are flipped or upper Delaunay.\n\
+Please report this error to qhull_bug@qhull.org with the input and all of the output.\n",
+ upperfacet->id);
+ qh_errexit(qh_ERRqhull, upperfacet, NULL);
+ }
+ *bestdistp= bestdist;
+ trace3((qh ferr, 3015, "qh_findbestlower: f%d dist %2.2g for f%d p%d\n",
+ bestfacet->id, bestdist, upperfacet->id, qh_pointid(point)));
+ return bestfacet;
+} /* findbestlower */
+
+/*---------------------------------
+
+ qh_findfacet_all( point, bestdist, isoutside, numpart )
+ exhaustive search for facet below a point
+
+ for Delaunay triangulations,
+ Use qh_setdelaunay() to lift point to paraboloid and scale by 'Qbb' if needed
+ Do not use options 'Qbk', 'QBk', or 'QbB' since they scale the coordinates.
+
+ returns:
+ returns first facet below point
+ if point is inside,
+ returns nearest facet
+ distance to facet
+ isoutside if point is outside of the hull
+ number of distance tests
+
+ notes:
+ for library users, not used by Qhull
+*/
+facetT *qh_findfacet_all(pointT *point, realT *bestdist, boolT *isoutside,
+ int *numpart) {
+ facetT *bestfacet= NULL, *facet;
+ realT dist;
+ int totpart= 0;
+
+ *bestdist= -REALmax;
+ *isoutside= False;
+ FORALLfacets {
+ if (facet->flipped || !facet->normal)
+ continue;
+ totpart++;
+ qh_distplane(point, facet, &dist);
+ if (dist > *bestdist) {
+ *bestdist= dist;
+ bestfacet= facet;
+ if (dist > qh MINoutside) {
+ *isoutside= True;
+ break;
+ }
+ }
+ }
+ *numpart= totpart;
+ trace3((qh ferr, 3016, "qh_findfacet_all: f%d dist %2.2g isoutside %d totpart %d\n",
+ getid_(bestfacet), *bestdist, *isoutside, totpart));
+ return bestfacet;
+} /* findfacet_all */
+
+/*---------------------------------
+
+ qh_findgood( facetlist, goodhorizon )
+ identify good facets for qh.PRINTgood
+ if qh.GOODvertex>0
+ facet includes point as vertex
+ if !match, returns goodhorizon
+ inactive if qh.MERGING
+ if qh.GOODpoint
+ facet is visible or coplanar (>0) or not visible (<0)
+ if qh.GOODthreshold
+ facet->normal matches threshold
+ if !goodhorizon and !match,
+ selects facet with closest angle
+ sets GOODclosest
+
+ returns:
+ number of new, good facets found
+ determines facet->good
+ may update qh.GOODclosest
+
+ notes:
+ qh_findgood_all further reduces the good region
+
+ design:
+ count good facets
+ mark good facets for qh.GOODpoint
+ mark good facets for qh.GOODthreshold
+ if necessary
+ update qh.GOODclosest
+*/
+int qh_findgood(facetT *facetlist, int goodhorizon) {
+ facetT *facet, *bestfacet= NULL;
+ realT angle, bestangle= REALmax, dist;
+ int numgood=0;
+
+ FORALLfacet_(facetlist) {
+ if (facet->good)
+ numgood++;
+ }
+ if (qh GOODvertex>0 && !qh MERGING) {
+ FORALLfacet_(facetlist) {
+ if (!qh_isvertex(qh GOODvertexp, facet->vertices)) {
+ facet->good= False;
+ numgood--;
+ }
+ }
+ }
+ if (qh GOODpoint && numgood) {
+ FORALLfacet_(facetlist) {
+ if (facet->good && facet->normal) {
+ zinc_(Zdistgood);
+ qh_distplane(qh GOODpointp, facet, &dist);
+ if ((qh GOODpoint > 0) ^ (dist > 0.0)) {
+ facet->good= False;
+ numgood--;
+ }
+ }
+ }
+ }
+ if (qh GOODthreshold && (numgood || goodhorizon || qh GOODclosest)) {
+ FORALLfacet_(facetlist) {
+ if (facet->good && facet->normal) {
+ if (!qh_inthresholds(facet->normal, &angle)) {
+ facet->good= False;
+ numgood--;
+ if (angle < bestangle) {
+ bestangle= angle;
+ bestfacet= facet;
+ }
+ }
+ }
+ }
+ if (!numgood && (!goodhorizon || qh GOODclosest)) {
+ if (qh GOODclosest) {
+ if (qh GOODclosest->visible)
+ qh GOODclosest= NULL;
+ else {
+ qh_inthresholds(qh GOODclosest->normal, &angle);
+ if (angle < bestangle)
+ bestfacet= qh GOODclosest;
+ }
+ }
+ if (bestfacet && bestfacet != qh GOODclosest) {
+ if (qh GOODclosest)
+ qh GOODclosest->good= False;
+ qh GOODclosest= bestfacet;
+ bestfacet->good= True;
+ numgood++;
+ trace2((qh ferr, 2044, "qh_findgood: f%d is closest(%2.2g) to thresholds\n",
+ bestfacet->id, bestangle));
+ return numgood;
+ }
+ }else if (qh GOODclosest) { /* numgood > 0 */
+ qh GOODclosest->good= False;
+ qh GOODclosest= NULL;
+ }
+ }
+ zadd_(Zgoodfacet, numgood);
+ trace2((qh ferr, 2045, "qh_findgood: found %d good facets with %d good horizon\n",
+ numgood, goodhorizon));
+ if (!numgood && qh GOODvertex>0 && !qh MERGING)
+ return goodhorizon;
+ return numgood;
+} /* findgood */
+
+/*---------------------------------
+
+ qh_findgood_all( facetlist )
+ apply other constraints for good facets (used by qh.PRINTgood)
+ if qh.GOODvertex
+ facet includes (>0) or doesn't include (<0) point as vertex
+ if last good facet and ONLYgood, prints warning and continues
+ if qh.SPLITthresholds
+ facet->normal matches threshold, or if none, the closest one
+ calls qh_findgood
+ nop if good not used
+
+ returns:
+ clears facet->good if not good
+ sets qh.num_good
+
+ notes:
+ this is like qh_findgood but more restrictive
+
+ design:
+ uses qh_findgood to mark good facets
+ marks facets for qh.GOODvertex
+ marks facets for qh.SPLITthreholds
+*/
+void qh_findgood_all(facetT *facetlist) {
+ facetT *facet, *bestfacet=NULL;
+ realT angle, bestangle= REALmax;
+ int numgood=0, startgood;
+
+ if (!qh GOODvertex && !qh GOODthreshold && !qh GOODpoint
+ && !qh SPLITthresholds)
+ return;
+ if (!qh ONLYgood)
+ qh_findgood(qh facet_list, 0);
+ FORALLfacet_(facetlist) {
+ if (facet->good)
+ numgood++;
+ }
+ if (qh GOODvertex <0 || (qh GOODvertex > 0 && qh MERGING)) {
+ FORALLfacet_(facetlist) {
+ if (facet->good && ((qh GOODvertex > 0) ^ !!qh_isvertex(qh GOODvertexp, facet->vertices))) {
+ if (!--numgood) {
+ if (qh ONLYgood) {
+ qh_fprintf(qh ferr, 7064, "qhull warning: good vertex p%d does not match last good facet f%d. Ignored.\n",
+ qh_pointid(qh GOODvertexp), facet->id);
+ return;
+ }else if (qh GOODvertex > 0)
+ qh_fprintf(qh ferr, 7065, "qhull warning: point p%d is not a vertex('QV%d').\n",
+ qh GOODvertex-1, qh GOODvertex-1);
+ else
+ qh_fprintf(qh ferr, 7066, "qhull warning: point p%d is a vertex for every facet('QV-%d').\n",
+ -qh GOODvertex - 1, -qh GOODvertex - 1);
+ }
+ facet->good= False;
+ }
+ }
+ }
+ startgood= numgood;
+ if (qh SPLITthresholds) {
+ FORALLfacet_(facetlist) {
+ if (facet->good) {
+ if (!qh_inthresholds(facet->normal, &angle)) {
+ facet->good= False;
+ numgood--;
+ if (angle < bestangle) {
+ bestangle= angle;
+ bestfacet= facet;
+ }
+ }
+ }
+ }
+ if (!numgood && bestfacet) {
+ bestfacet->good= True;
+ numgood++;
+ trace0((qh ferr, 23, "qh_findgood_all: f%d is closest(%2.2g) to thresholds\n",
+ bestfacet->id, bestangle));
+ return;
+ }
+ }
+ qh num_good= numgood;
+ trace0((qh ferr, 24, "qh_findgood_all: %d good facets remain out of %d facets\n",
+ numgood, startgood));
+} /* findgood_all */
+
+/*---------------------------------
+
+ qh_furthestnext()
+ set qh.facet_next to facet with furthest of all furthest points
+ searches all facets on qh.facet_list
+
+ notes:
+ this may help avoid precision problems
+*/
+void qh_furthestnext(void /* qh facet_list */) {
+ facetT *facet, *bestfacet= NULL;
+ realT dist, bestdist= -REALmax;
+
+ FORALLfacets {
+ if (facet->outsideset) {
+#if qh_COMPUTEfurthest
+ pointT *furthest;
+ furthest= (pointT*)qh_setlast(facet->outsideset);
+ zinc_(Zcomputefurthest);
+ qh_distplane(furthest, facet, &dist);
+#else
+ dist= facet->furthestdist;
+#endif
+ if (dist > bestdist) {
+ bestfacet= facet;
+ bestdist= dist;
+ }
+ }
+ }
+ if (bestfacet) {
+ qh_removefacet(bestfacet);
+ qh_prependfacet(bestfacet, &qh facet_next);
+ trace1((qh ferr, 1029, "qh_furthestnext: made f%d next facet(dist %.2g)\n",
+ bestfacet->id, bestdist));
+ }
+} /* furthestnext */
+
+/*---------------------------------
+
+ qh_furthestout( facet )
+ make furthest outside point the last point of outsideset
+
+ returns:
+ updates facet->outsideset
+ clears facet->notfurthest
+ sets facet->furthestdist
+
+ design:
+ determine best point of outsideset
+ make it the last point of outsideset
+*/
+void qh_furthestout(facetT *facet) {
+ pointT *point, **pointp, *bestpoint= NULL;
+ realT dist, bestdist= -REALmax;
+
+ FOREACHpoint_(facet->outsideset) {
+ qh_distplane(point, facet, &dist);
+ zinc_(Zcomputefurthest);
+ if (dist > bestdist) {
+ bestpoint= point;
+ bestdist= dist;
+ }
+ }
+ if (bestpoint) {
+ qh_setdel(facet->outsideset, point);
+ qh_setappend(&facet->outsideset, point);
+#if !qh_COMPUTEfurthest
+ facet->furthestdist= bestdist;
+#endif
+ }
+ facet->notfurthest= False;
+ trace3((qh ferr, 3017, "qh_furthestout: p%d is furthest outside point of f%d\n",
+ qh_pointid(point), facet->id));
+} /* furthestout */
+
+
+/*---------------------------------
+
+ qh_infiniteloop( facet )
+ report infinite loop error due to facet
+*/
+void qh_infiniteloop(facetT *facet) {
+
+ qh_fprintf(qh ferr, 6149, "qhull internal error (qh_infiniteloop): potential infinite loop detected\n");
+ qh_errexit(qh_ERRqhull, facet, NULL);
+} /* qh_infiniteloop */
+
+/*---------------------------------
+
+ qh_initbuild()
+ initialize hull and outside sets with point array
+ qh.FIRSTpoint/qh.NUMpoints is point array
+ if qh.GOODpoint
+ adds qh.GOODpoint to initial hull
+
+ returns:
+ qh_facetlist with initial hull
+ points partioned into outside sets, coplanar sets, or inside
+ initializes qh.GOODpointp, qh.GOODvertexp,
+
+ design:
+ initialize global variables used during qh_buildhull
+ determine precision constants and points with max/min coordinate values
+ if qh.SCALElast, scale last coordinate(for 'd')
+ build initial simplex
+ partition input points into facets of initial simplex
+ set up lists
+ if qh.ONLYgood
+ check consistency
+ add qh.GOODvertex if defined
+*/
+void qh_initbuild( void) {
+ setT *maxpoints, *vertices;
+ facetT *facet;
+ int i, numpart;
+ realT dist;
+ boolT isoutside;
+
+ qh furthest_id= -1;
+ qh lastreport= 0;
+ qh facet_id= qh vertex_id= qh ridge_id= 0;
+ qh visit_id= qh vertex_visit= 0;
+ qh maxoutdone= False;
+
+ if (qh GOODpoint > 0)
+ qh GOODpointp= qh_point(qh GOODpoint-1);
+ else if (qh GOODpoint < 0)
+ qh GOODpointp= qh_point(-qh GOODpoint-1);
+ if (qh GOODvertex > 0)
+ qh GOODvertexp= qh_point(qh GOODvertex-1);
+ else if (qh GOODvertex < 0)
+ qh GOODvertexp= qh_point(-qh GOODvertex-1);
+ if ((qh GOODpoint
+ && (qh GOODpointp < qh first_point /* also catches !GOODpointp */
+ || qh GOODpointp > qh_point(qh num_points-1)))
+ || (qh GOODvertex
+ && (qh GOODvertexp < qh first_point /* also catches !GOODvertexp */
+ || qh GOODvertexp > qh_point(qh num_points-1)))) {
+ qh_fprintf(qh ferr, 6150, "qhull input error: either QGn or QVn point is > p%d\n",
+ qh num_points-1);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ maxpoints= qh_maxmin(qh first_point, qh num_points, qh hull_dim);
+ if (qh SCALElast)
+ qh_scalelast(qh first_point, qh num_points, qh hull_dim,
+ qh MINlastcoord, qh MAXlastcoord, qh MAXwidth);
+ qh_detroundoff();
+ if (qh DELAUNAY && qh upper_threshold[qh hull_dim-1] > REALmax/2
+ && qh lower_threshold[qh hull_dim-1] < -REALmax/2) {
+ for (i=qh_PRINTEND; i--; ) {
+ if (qh PRINTout[i] == qh_PRINTgeom && qh DROPdim < 0
+ && !qh GOODthreshold && !qh SPLITthresholds)
+ break; /* in this case, don't set upper_threshold */
+ }
+ if (i < 0) {
+ if (qh UPPERdelaunay) { /* matches qh.upperdelaunay in qh_setfacetplane */
+ qh lower_threshold[qh hull_dim-1]= qh ANGLEround * qh_ZEROdelaunay;
+ qh GOODthreshold= True;
+ }else {
+ qh upper_threshold[qh hull_dim-1]= -qh ANGLEround * qh_ZEROdelaunay;
+ if (!qh GOODthreshold)
+ qh SPLITthresholds= True; /* build upper-convex hull even if Qg */
+ /* qh_initqhull_globals errors if Qg without Pdk/etc. */
+ }
+ }
+ }
+ vertices= qh_initialvertices(qh hull_dim, maxpoints, qh first_point, qh num_points);
+ qh_initialhull(vertices); /* initial qh facet_list */
+ qh_partitionall(vertices, qh first_point, qh num_points);
+ if (qh PRINToptions1st || qh TRACElevel || qh IStracing) {
+ if (qh TRACElevel || qh IStracing)
+ qh_fprintf(qh ferr, 8103, "\nTrace level %d for %s | %s\n",
+ qh IStracing ? qh IStracing : qh TRACElevel, qh rbox_command, qh qhull_command);
+ qh_fprintf(qh ferr, 8104, "Options selected for Qhull %s:\n%s\n", qh_version, qh qhull_options);
+ }
+ qh_resetlists(False, qh_RESETvisible /*qh visible_list newvertex_list newfacet_list */);
+ qh facet_next= qh facet_list;
+ qh_furthestnext(/* qh facet_list */);
+ if (qh PREmerge) {
+ qh cos_max= qh premerge_cos;
+ qh centrum_radius= qh premerge_centrum;
+ }
+ if (qh ONLYgood) {
+ if (qh GOODvertex > 0 && qh MERGING) {
+ qh_fprintf(qh ferr, 6151, "qhull input error: 'Qg QVn' (only good vertex) does not work with merging.\nUse 'QJ' to joggle the input or 'Q0' to turn off merging.\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ if (!(qh GOODthreshold || qh GOODpoint
+ || (!qh MERGEexact && !qh PREmerge && qh GOODvertexp))) {
+ qh_fprintf(qh ferr, 6152, "qhull input error: 'Qg' (ONLYgood) needs a good threshold('Pd0D0'), a\n\
+good point(QGn or QG-n), or a good vertex with 'QJ' or 'Q0' (QVn).\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ if (qh GOODvertex > 0 && !qh MERGING /* matches qh_partitionall */
+ && !qh_isvertex(qh GOODvertexp, vertices)) {
+ facet= qh_findbestnew(qh GOODvertexp, qh facet_list,
+ &dist, !qh_ALL, &isoutside, &numpart);
+ zadd_(Zdistgood, numpart);
+ if (!isoutside) {
+ qh_fprintf(qh ferr, 6153, "qhull input error: point for QV%d is inside initial simplex. It can not be made a vertex.\n",
+ qh_pointid(qh GOODvertexp));
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ if (!qh_addpoint(qh GOODvertexp, facet, False)) {
+ qh_settempfree(&vertices);
+ qh_settempfree(&maxpoints);
+ return;
+ }
+ }
+ qh_findgood(qh facet_list, 0);
+ }
+ qh_settempfree(&vertices);
+ qh_settempfree(&maxpoints);
+ trace1((qh ferr, 1030, "qh_initbuild: initial hull created and points partitioned\n"));
+} /* initbuild */
+
+/*---------------------------------
+
+ qh_initialhull( vertices )
+ constructs the initial hull as a DIM3 simplex of vertices
+
+ design:
+ creates a simplex (initializes lists)
+ determines orientation of simplex
+ sets hyperplanes for facets
+ doubles checks orientation (in case of axis-parallel facets with Gaussian elimination)
+ checks for flipped facets and qh.NARROWhull
+ checks the result
+*/
+void qh_initialhull(setT *vertices) {
+ facetT *facet, *firstfacet, *neighbor, **neighborp;
+ realT dist, angle, minangle= REALmax;
+#ifndef qh_NOtrace
+ int k;
+#endif
+
+ qh_createsimplex(vertices); /* qh facet_list */
+ qh_resetlists(False, qh_RESETvisible);
+ qh facet_next= qh facet_list; /* advance facet when processed */
+ qh interior_point= qh_getcenter(vertices);
+ firstfacet= qh facet_list;
+ qh_setfacetplane(firstfacet);
+ zinc_(Znumvisibility); /* needs to be in printsummary */
+ qh_distplane(qh interior_point, firstfacet, &dist);
+ if (dist > 0) {
+ FORALLfacets
+ facet->toporient ^= (unsigned char)True;
+ }
+ FORALLfacets
+ qh_setfacetplane(facet);
+ FORALLfacets {
+ if (!qh_checkflipped(facet, NULL, qh_ALL)) {/* due to axis-parallel facet */
+ trace1((qh ferr, 1031, "qh_initialhull: initial orientation incorrect. Correct all facets\n"));
+ facet->flipped= False;
+ FORALLfacets {
+ facet->toporient ^= (unsigned char)True;
+ qh_orientoutside(facet);
+ }
+ break;
+ }
+ }
+ FORALLfacets {
+ if (!qh_checkflipped(facet, NULL, !qh_ALL)) { /* can happen with 'R0.1' */
+ if (qh DELAUNAY && ! qh ATinfinity) {
+ if (qh UPPERdelaunay)
+ qh_fprintf(qh ferr, 6240, "Qhull input error: Can not compute the upper Delaunay triangulation or upper Voronoi diagram of cocircular/cospherical points.\n");
+ else
+ qh_fprintf(qh ferr, 6239, "Qhull input error: Use option 'Qz' for the Delaunay triangulation or Voronoi diagram of cocircular/cospherical points. Option 'Qz' adds a point \"at infinity\" (above the corresponding paraboloid).\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ qh_precision("initial facet is coplanar with interior point");
+ qh_fprintf(qh ferr, 6154, "qhull precision error: initial facet %d is coplanar with the interior point\n",
+ facet->id);
+ qh_errexit(qh_ERRsingular, facet, NULL);
+ }
+ FOREACHneighbor_(facet) {
+ angle= qh_getangle(facet->normal, neighbor->normal);
+ minimize_( minangle, angle);
+ }
+ }
+ if (minangle < qh_MAXnarrow && !qh NOnarrow) {
+ realT diff= 1.0 + minangle;
+
+ qh NARROWhull= True;
+ qh_option("_narrow-hull", NULL, &diff);
+ if (minangle < qh_WARNnarrow && !qh RERUN && qh PRINTprecision)
+ qh_printhelp_narrowhull(qh ferr, minangle);
+ }
+ zzval_(Zprocessed)= qh hull_dim+1;
+ qh_checkpolygon(qh facet_list);
+ qh_checkconvex(qh facet_list, qh_DATAfault);
+#ifndef qh_NOtrace
+ if (qh IStracing >= 1) {
+ qh_fprintf(qh ferr, 8105, "qh_initialhull: simplex constructed, interior point:");
+ for (k=0; k < qh hull_dim; k++)
+ qh_fprintf(qh ferr, 8106, " %6.4g", qh interior_point[k]);
+ qh_fprintf(qh ferr, 8107, "\n");
+ }
+#endif
+} /* initialhull */
+
+/*---------------------------------
+
+ qh_initialvertices( dim, maxpoints, points, numpoints )
+ determines a non-singular set of initial vertices
+ maxpoints may include duplicate points
+
+ returns:
+ temporary set of dim+1 vertices in descending order by vertex id
+ if qh.RANDOMoutside && !qh.ALLpoints
+ picks random points
+ if dim >= qh_INITIALmax,
+ uses min/max x and max points with non-zero determinants
+
+ notes:
+ unless qh.ALLpoints,
+ uses maxpoints as long as determinate is non-zero
+*/
+setT *qh_initialvertices(int dim, setT *maxpoints, pointT *points, int numpoints) {
+ pointT *point, **pointp;
+ setT *vertices, *simplex, *tested;
+ realT randr;
+ int idx, point_i, point_n, k;
+ boolT nearzero= False;
+
+ vertices= qh_settemp(dim + 1);
+ simplex= qh_settemp(dim+1);
+ if (qh ALLpoints)
+ qh_maxsimplex(dim, NULL, points, numpoints, &simplex);
+ else if (qh RANDOMoutside) {
+ while (qh_setsize(simplex) != dim+1) {
+ randr= qh_RANDOMint;
+ randr= randr/(qh_RANDOMmax+1);
+ idx= (int)floor(qh num_points * randr);
+ while (qh_setin(simplex, qh_point(idx))) {
+ idx++; /* in case qh_RANDOMint always returns the same value */
+ idx= idx < qh num_points ? idx : 0;
+ }
+ qh_setappend(&simplex, qh_point(idx));
+ }
+ }else if (qh hull_dim >= qh_INITIALmax) {
+ tested= qh_settemp(dim+1);
+ qh_setappend(&simplex, SETfirst_(maxpoints)); /* max and min X coord */
+ qh_setappend(&simplex, SETsecond_(maxpoints));
+ qh_maxsimplex(fmin_(qh_INITIALsearch, dim), maxpoints, points, numpoints, &simplex);
+ k= qh_setsize(simplex);
+ FOREACHpoint_i_(maxpoints) {
+ if (point_i & 0x1) { /* first pick up max. coord. points */
+ if (!qh_setin(simplex, point) && !qh_setin(tested, point)){
+ qh_detsimplex(point, simplex, k, &nearzero);
+ if (nearzero)
+ qh_setappend(&tested, point);
+ else {
+ qh_setappend(&simplex, point);
+ if (++k == dim) /* use search for last point */
+ break;
+ }
+ }
+ }
+ }
+ while (k != dim && (point= (pointT*)qh_setdellast(maxpoints))) {
+ if (!qh_setin(simplex, point) && !qh_setin(tested, point)){
+ qh_detsimplex(point, simplex, k, &nearzero);
+ if (nearzero)
+ qh_setappend(&tested, point);
+ else {
+ qh_setappend(&simplex, point);
+ k++;
+ }
+ }
+ }
+ idx= 0;
+ while (k != dim && (point= qh_point(idx++))) {
+ if (!qh_setin(simplex, point) && !qh_setin(tested, point)){
+ qh_detsimplex(point, simplex, k, &nearzero);
+ if (!nearzero){
+ qh_setappend(&simplex, point);
+ k++;
+ }
+ }
+ }
+ qh_settempfree(&tested);
+ qh_maxsimplex(dim, maxpoints, points, numpoints, &simplex);
+ }else
+ qh_maxsimplex(dim, maxpoints, points, numpoints, &simplex);
+ FOREACHpoint_(simplex)
+ qh_setaddnth(&vertices, 0, qh_newvertex(point)); /* descending order */
+ qh_settempfree(&simplex);
+ return vertices;
+} /* initialvertices */
+
+
+/*---------------------------------
+
+ qh_isvertex( )
+ returns vertex if point is in vertex set, else returns NULL
+
+ notes:
+ for qh.GOODvertex
+*/
+vertexT *qh_isvertex(pointT *point, setT *vertices) {
+ vertexT *vertex, **vertexp;
+
+ FOREACHvertex_(vertices) {
+ if (vertex->point == point)
+ return vertex;
+ }
+ return NULL;
+} /* isvertex */
+
+/*---------------------------------
+
+ qh_makenewfacets( point )
+ make new facets from point and qh.visible_list
+
+ returns:
+ qh.newfacet_list= list of new facets with hyperplanes and ->newfacet
+ qh.newvertex_list= list of vertices in new facets with ->newlist set
+
+ if (qh.ONLYgood)
+ newfacets reference horizon facets, but not vice versa
+ ridges reference non-simplicial horizon ridges, but not vice versa
+ does not change existing facets
+ else
+ sets qh.NEWfacets
+ new facets attached to horizon facets and ridges
+ for visible facets,
+ visible->r.replace is corresponding new facet
+
+ see also:
+ qh_makenewplanes() -- make hyperplanes for facets
+ qh_attachnewfacets() -- attachnewfacets if not done here(qh ONLYgood)
+ qh_matchnewfacets() -- match up neighbors
+ qh_updatevertices() -- update vertex neighbors and delvertices
+ qh_deletevisible() -- delete visible facets
+ qh_checkpolygon() --check the result
+ qh_triangulate() -- triangulate a non-simplicial facet
+
+ design:
+ for each visible facet
+ make new facets to its horizon facets
+ update its f.replace
+ clear its neighbor set
+*/
+vertexT *qh_makenewfacets(pointT *point /*visible_list*/) {
+ facetT *visible, *newfacet= NULL, *newfacet2= NULL, *neighbor, **neighborp;
+ vertexT *apex;
+ int numnew=0;
+
+ qh newfacet_list= qh facet_tail;
+ qh newvertex_list= qh vertex_tail;
+ apex= qh_newvertex(point);
+ qh_appendvertex(apex);
+ qh visit_id++;
+ if (!qh ONLYgood)
+ qh NEWfacets= True;
+ FORALLvisible_facets {
+ FOREACHneighbor_(visible)
+ neighbor->seen= False;
+ if (visible->ridges) {
+ visible->visitid= qh visit_id;
+ newfacet2= qh_makenew_nonsimplicial(visible, apex, &numnew);
+ }
+ if (visible->simplicial)
+ newfacet= qh_makenew_simplicial(visible, apex, &numnew);
+ if (!qh ONLYgood) {
+ if (newfacet2) /* newfacet is null if all ridges defined */
+ newfacet= newfacet2;
+ if (newfacet)
+ visible->f.replace= newfacet;
+ else
+ zinc_(Zinsidevisible);
+ SETfirst_(visible->neighbors)= NULL;
+ }
+ }
+ trace1((qh ferr, 1032, "qh_makenewfacets: created %d new facets from point p%d to horizon\n",
+ numnew, qh_pointid(point)));
+ if (qh IStracing >= 4)
+ qh_printfacetlist(qh newfacet_list, NULL, qh_ALL);
+ return apex;
+} /* makenewfacets */
+
+/*---------------------------------
+
+ qh_matchduplicates( atfacet, atskip, hashsize, hashcount )
+ match duplicate ridges in qh.hash_table for atfacet/atskip
+ duplicates marked with ->dupridge and qh_DUPLICATEridge
+
+ returns:
+ picks match with worst merge (min distance apart)
+ updates hashcount
+
+ see also:
+ qh_matchneighbor
+
+ notes:
+
+ design:
+ compute hash value for atfacet and atskip
+ repeat twice -- once to make best matches, once to match the rest
+ for each possible facet in qh.hash_table
+ if it is a matching facet and pass 2
+ make match
+ unless tricoplanar, mark match for merging (qh_MERGEridge)
+ [e.g., tricoplanar RBOX s 1000 t993602376 | QHULL C-1e-3 d Qbb FA Qt]
+ if it is a matching facet and pass 1
+ test if this is a better match
+ if pass 1,
+ make best match (it will not be merged)
+*/
+#ifndef qh_NOmerge
+void qh_matchduplicates(facetT *atfacet, int atskip, int hashsize, int *hashcount) {
+ boolT same, ismatch;
+ int hash, scan;
+ facetT *facet, *newfacet, *maxmatch= NULL, *maxmatch2= NULL, *nextfacet;
+ int skip, newskip, nextskip= 0, maxskip= 0, maxskip2= 0, makematch;
+ realT maxdist= -REALmax, mindist, dist2, low, high;
+
+ hash= qh_gethash(hashsize, atfacet->vertices, qh hull_dim, 1,
+ SETelem_(atfacet->vertices, atskip));
+ trace2((qh ferr, 2046, "qh_matchduplicates: find duplicate matches for f%d skip %d hash %d hashcount %d\n",
+ atfacet->id, atskip, hash, *hashcount));
+ for (makematch= 0; makematch < 2; makematch++) {
+ qh visit_id++;
+ for (newfacet= atfacet, newskip= atskip; newfacet; newfacet= nextfacet, newskip= nextskip) {
+ zinc_(Zhashlookup);
+ nextfacet= NULL;
+ newfacet->visitid= qh visit_id;
+ for (scan= hash; (facet= SETelemt_(qh hash_table, scan, facetT));
+ scan= (++scan >= hashsize ? 0 : scan)) {
+ if (!facet->dupridge || facet->visitid == qh visit_id)
+ continue;
+ zinc_(Zhashtests);
+ if (qh_matchvertices(1, newfacet->vertices, newskip, facet->vertices, &skip, &same)) {
+ ismatch= (same == (boolT)(newfacet->toporient ^ facet->toporient));
+ if (SETelemt_(facet->neighbors, skip, facetT) != qh_DUPLICATEridge) {
+ if (!makematch) {
+ qh_fprintf(qh ferr, 6155, "qhull internal error (qh_matchduplicates): missing dupridge at f%d skip %d for new f%d skip %d hash %d\n",
+ facet->id, skip, newfacet->id, newskip, hash);
+ qh_errexit2 (qh_ERRqhull, facet, newfacet);
+ }
+ }else if (ismatch && makematch) {
+ if (SETelemt_(newfacet->neighbors, newskip, facetT) == qh_DUPLICATEridge) {
+ SETelem_(facet->neighbors, skip)= newfacet;
+ if (newfacet->tricoplanar)
+ SETelem_(newfacet->neighbors, newskip)= facet;
+ else
+ SETelem_(newfacet->neighbors, newskip)= qh_MERGEridge;
+ *hashcount -= 2; /* removed two unmatched facets */
+ trace4((qh ferr, 4059, "qh_matchduplicates: duplicate f%d skip %d matched with new f%d skip %d merge\n",
+ facet->id, skip, newfacet->id, newskip));
+ }
+ }else if (ismatch) {
+ mindist= qh_getdistance(facet, newfacet, &low, &high);
+ dist2= qh_getdistance(newfacet, facet, &low, &high);
+ minimize_(mindist, dist2);
+ if (mindist > maxdist) {
+ maxdist= mindist;
+ maxmatch= facet;
+ maxskip= skip;
+ maxmatch2= newfacet;
+ maxskip2= newskip;
+ }
+ trace3((qh ferr, 3018, "qh_matchduplicates: duplicate f%d skip %d new f%d skip %d at dist %2.2g, max is now f%d f%d\n",
+ facet->id, skip, newfacet->id, newskip, mindist,
+ maxmatch->id, maxmatch2->id));
+ }else { /* !ismatch */
+ nextfacet= facet;
+ nextskip= skip;
+ }
+ }
+ if (makematch && !facet
+ && SETelemt_(facet->neighbors, skip, facetT) == qh_DUPLICATEridge) {
+ qh_fprintf(qh ferr, 6156, "qhull internal error (qh_matchduplicates): no MERGEridge match for duplicate f%d skip %d at hash %d\n",
+ newfacet->id, newskip, hash);
+ qh_errexit(qh_ERRqhull, newfacet, NULL);
+ }
+ }
+ } /* end of for each new facet at hash */
+ if (!makematch) {
+ if (!maxmatch) {
+ qh_fprintf(qh ferr, 6157, "qhull internal error (qh_matchduplicates): no maximum match at duplicate f%d skip %d at hash %d\n",
+ atfacet->id, atskip, hash);
+ qh_errexit(qh_ERRqhull, atfacet, NULL);
+ }
+ SETelem_(maxmatch->neighbors, maxskip)= maxmatch2;
+ SETelem_(maxmatch2->neighbors, maxskip2)= maxmatch;
+ *hashcount -= 2; /* removed two unmatched facets */
+ zzinc_(Zmultiridge);
+ trace0((qh ferr, 25, "qh_matchduplicates: duplicate f%d skip %d matched with new f%d skip %d keep\n",
+ maxmatch->id, maxskip, maxmatch2->id, maxskip2));
+ qh_precision("ridge with multiple neighbors");
+ if (qh IStracing >= 4)
+ qh_errprint("DUPLICATED/MATCH", maxmatch, maxmatch2, NULL, NULL);
+ }
+ }
+} /* matchduplicates */
+
+/*---------------------------------
+
+ qh_nearcoplanar()
+ for all facets, remove near-inside points from facet->coplanarset
+ coplanar points defined by innerplane from qh_outerinner()
+
+ returns:
+ if qh KEEPcoplanar && !qh KEEPinside
+ facet->coplanarset only contains coplanar points
+ if qh.JOGGLEmax
+ drops inner plane by another qh.JOGGLEmax diagonal since a
+ vertex could shift out while a coplanar point shifts in
+
+ notes:
+ used for qh.PREmerge and qh.JOGGLEmax
+ must agree with computation of qh.NEARcoplanar in qh_detroundoff()
+ design:
+ if not keeping coplanar or inside points
+ free all coplanar sets
+ else if not keeping both coplanar and inside points
+ remove !coplanar or !inside points from coplanar sets
+*/
+void qh_nearcoplanar(void /* qh.facet_list */) {
+ facetT *facet;
+ pointT *point, **pointp;
+ int numpart;
+ realT dist, innerplane;
+
+ if (!qh KEEPcoplanar && !qh KEEPinside) {
+ FORALLfacets {
+ if (facet->coplanarset)
+ qh_setfree( &facet->coplanarset);
+ }
+ }else if (!qh KEEPcoplanar || !qh KEEPinside) {
+ qh_outerinner(NULL, NULL, &innerplane);
+ if (qh JOGGLEmax < REALmax/2)
+ innerplane -= qh JOGGLEmax * sqrt((realT)qh hull_dim);
+ numpart= 0;
+ FORALLfacets {
+ if (facet->coplanarset) {
+ FOREACHpoint_(facet->coplanarset) {
+ numpart++;
+ qh_distplane(point, facet, &dist);
+ if (dist < innerplane) {
+ if (!qh KEEPinside)
+ SETref_(point)= NULL;
+ }else if (!qh KEEPcoplanar)
+ SETref_(point)= NULL;
+ }
+ qh_setcompact(facet->coplanarset);
+ }
+ }
+ zzadd_(Zcheckpart, numpart);
+ }
+} /* nearcoplanar */
+
+/*---------------------------------
+
+ qh_nearvertex( facet, point, bestdist )
+ return nearest vertex in facet to point
+
+ returns:
+ vertex and its distance
+
+ notes:
+ if qh.DELAUNAY
+ distance is measured in the input set
+ searches neighboring tricoplanar facets (requires vertexneighbors)
+ Slow implementation. Recomputes vertex set for each point.
+ The vertex set could be stored in the qh.keepcentrum facet.
+*/
+vertexT *qh_nearvertex(facetT *facet, pointT *point, realT *bestdistp) {
+ realT bestdist= REALmax, dist;
+ vertexT *bestvertex= NULL, *vertex, **vertexp, *apex;
+ coordT *center;
+ facetT *neighbor, **neighborp;
+ setT *vertices;
+ int dim= qh hull_dim;
+
+ if (qh DELAUNAY)
+ dim--;
+ if (facet->tricoplanar) {
+ if (!qh VERTEXneighbors || !facet->center) {
+ qh_fprintf(qh ferr, 6158, "qhull internal error (qh_nearvertex): qh.VERTEXneighbors and facet->center required for tricoplanar facets\n");
+ qh_errexit(qh_ERRqhull, facet, NULL);
+ }
+ vertices= qh_settemp(qh TEMPsize);
+ apex= SETfirstt_(facet->vertices, vertexT);
+ center= facet->center;
+ FOREACHneighbor_(apex) {
+ if (neighbor->center == center) {
+ FOREACHvertex_(neighbor->vertices)
+ qh_setappend(&vertices, vertex);
+ }
+ }
+ }else
+ vertices= facet->vertices;
+ FOREACHvertex_(vertices) {
+ dist= qh_pointdist(vertex->point, point, -dim);
+ if (dist < bestdist) {
+ bestdist= dist;
+ bestvertex= vertex;
+ }
+ }
+ if (facet->tricoplanar)
+ qh_settempfree(&vertices);
+ *bestdistp= sqrt(bestdist);
+ trace3((qh ferr, 3019, "qh_nearvertex: v%d dist %2.2g for f%d p%d\n",
+ bestvertex->id, *bestdistp, facet->id, qh_pointid(point)));
+ return bestvertex;
+} /* nearvertex */
+
+/*---------------------------------
+
+ qh_newhashtable( newsize )
+ returns size of qh.hash_table of at least newsize slots
+
+ notes:
+ assumes qh.hash_table is NULL
+ qh_HASHfactor determines the number of extra slots
+ size is not divisible by 2, 3, or 5
+*/
+int qh_newhashtable(int newsize) {
+ int size;
+
+ size= ((newsize+1)*qh_HASHfactor) | 0x1; /* odd number */
+ while (True) {
+ if (newsize<0 || size<0) {
+ qh_fprintf(qhmem.ferr, 6236, "qhull error (qh_newhashtable): negative request (%d) or size (%d). Did int overflow due to high-D?\n", newsize, size); /* WARN64 */
+ qh_errexit(qhmem_ERRmem, NULL, NULL);
+ }
+ if ((size%3) && (size%5))
+ break;
+ size += 2;
+ /* loop terminates because there is an infinite number of primes */
+ }
+ qh hash_table= qh_setnew(size);
+ qh_setzero(qh hash_table, 0, size);
+ return size;
+} /* newhashtable */
+
+/*---------------------------------
+
+ qh_newvertex( point )
+ returns a new vertex for point
+*/
+vertexT *qh_newvertex(pointT *point) {
+ vertexT *vertex;
+
+ zinc_(Ztotvertices);
+ vertex= (vertexT *)qh_memalloc((int)sizeof(vertexT));
+ memset((char *) vertex, (size_t)0, sizeof(vertexT));
+ if (qh vertex_id == 0xFFFFFF) {
+ qh_fprintf(qh ferr, 6159, "qhull error: more than %d vertices. ID field overflows and two vertices\n\
+may have the same identifier. Vertices will not be sorted correctly.\n", 0xFFFFFF);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ if (qh vertex_id == qh tracevertex_id)
+ qh tracevertex= vertex;
+ vertex->id= qh vertex_id++;
+ vertex->point= point;
+ vertex->dim= (unsigned char)(qh hull_dim <= MAX_vdim ? qh hull_dim : 0);
+ trace4((qh ferr, 4060, "qh_newvertex: vertex p%d(v%d) created\n", qh_pointid(vertex->point),
+ vertex->id));
+ return(vertex);
+} /* newvertex */
+
+/*---------------------------------
+
+ qh_nextridge3d( atridge, facet, vertex )
+ return next ridge and vertex for a 3d facet
+ returns NULL on error
+ [for QhullFacet::nextRidge3d] Does not call qh_errexit nor access qh_qh.
+
+ notes:
+ in qh_ORIENTclock order
+ this is a O(n^2) implementation to trace all ridges
+ be sure to stop on any 2nd visit
+ same as QhullRidge::nextRidge3d
+ does not use qh_qh or qh_errexit [QhullFacet.cpp]
+
+ design:
+ for each ridge
+ exit if it is the ridge after atridge
+*/
+ridgeT *qh_nextridge3d(ridgeT *atridge, facetT *facet, vertexT **vertexp) {
+ vertexT *atvertex, *vertex, *othervertex;
+ ridgeT *ridge, **ridgep;
+
+ if ((atridge->top == facet) ^ qh_ORIENTclock)
+ atvertex= SETsecondt_(atridge->vertices, vertexT);
+ else
+ atvertex= SETfirstt_(atridge->vertices, vertexT);
+ FOREACHridge_(facet->ridges) {
+ if (ridge == atridge)
+ continue;
+ if ((ridge->top == facet) ^ qh_ORIENTclock) {
+ othervertex= SETsecondt_(ridge->vertices, vertexT);
+ vertex= SETfirstt_(ridge->vertices, vertexT);
+ }else {
+ vertex= SETsecondt_(ridge->vertices, vertexT);
+ othervertex= SETfirstt_(ridge->vertices, vertexT);
+ }
+ if (vertex == atvertex) {
+ if (vertexp)
+ *vertexp= othervertex;
+ return ridge;
+ }
+ }
+ return NULL;
+} /* nextridge3d */
+#else /* qh_NOmerge */
+void qh_matchduplicates(facetT *atfacet, int atskip, int hashsize, int *hashcount) {
+}
+ridgeT *qh_nextridge3d(ridgeT *atridge, facetT *facet, vertexT **vertexp) {
+
+ return NULL;
+}
+#endif /* qh_NOmerge */
+
+/*---------------------------------
+
+ qh_outcoplanar()
+ move points from all facets' outsidesets to their coplanarsets
+
+ notes:
+ for post-processing under qh.NARROWhull
+
+ design:
+ for each facet
+ for each outside point for facet
+ partition point into coplanar set
+*/
+void qh_outcoplanar(void /* facet_list */) {
+ pointT *point, **pointp;
+ facetT *facet;
+ realT dist;
+
+ trace1((qh ferr, 1033, "qh_outcoplanar: move outsideset to coplanarset for qh NARROWhull\n"));
+ FORALLfacets {
+ FOREACHpoint_(facet->outsideset) {
+ qh num_outside--;
+ if (qh KEEPcoplanar || qh KEEPnearinside) {
+ qh_distplane(point, facet, &dist);
+ zinc_(Zpartition);
+ qh_partitioncoplanar(point, facet, &dist);
+ }
+ }
+ qh_setfree(&facet->outsideset);
+ }
+} /* outcoplanar */
+
+/*---------------------------------
+
+ qh_point( id )
+ return point for a point id, or NULL if unknown
+
+ alternative code:
+ return((pointT *)((unsigned long)qh.first_point
+ + (unsigned long)((id)*qh.normal_size)));
+*/
+pointT *qh_point(int id) {
+
+ if (id < 0)
+ return NULL;
+ if (id < qh num_points)
+ return qh first_point + id * qh hull_dim;
+ id -= qh num_points;
+ if (id < qh_setsize(qh other_points))
+ return SETelemt_(qh other_points, id, pointT);
+ return NULL;
+} /* point */
+
+/*---------------------------------
+
+ qh_point_add( set, point, elem )
+ stores elem at set[point.id]
+
+ returns:
+ access function for qh_pointfacet and qh_pointvertex
+
+ notes:
+ checks point.id
+*/
+void qh_point_add(setT *set, pointT *point, void *elem) {
+ int id, size;
+
+ SETreturnsize_(set, size);
+ if ((id= qh_pointid(point)) < 0)
+ qh_fprintf(qh ferr, 7067, "qhull internal warning (point_add): unknown point %p id %d\n",
+ point, id);
+ else if (id >= size) {
+ qh_fprintf(qh ferr, 6160, "qhull internal errror(point_add): point p%d is out of bounds(%d)\n",
+ id, size);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }else
+ SETelem_(set, id)= elem;
+} /* point_add */
+
+
+/*---------------------------------
+
+ qh_pointfacet()
+ return temporary set of facet for each point
+ the set is indexed by point id
+
+ notes:
+ vertices assigned to one of the facets
+ coplanarset assigned to the facet
+ outside set assigned to the facet
+ NULL if no facet for point (inside)
+ includes qh.GOODpointp
+
+ access:
+ FOREACHfacet_i_(facets) { ... }
+ SETelem_(facets, i)
+
+ design:
+ for each facet
+ add each vertex
+ add each coplanar point
+ add each outside point
+*/
+setT *qh_pointfacet(void /*qh facet_list*/) {
+ int numpoints= qh num_points + qh_setsize(qh other_points);
+ setT *facets;
+ facetT *facet;
+ vertexT *vertex, **vertexp;
+ pointT *point, **pointp;
+
+ facets= qh_settemp(numpoints);
+ qh_setzero(facets, 0, numpoints);
+ qh vertex_visit++;
+ FORALLfacets {
+ FOREACHvertex_(facet->vertices) {
+ if (vertex->visitid != qh vertex_visit) {
+ vertex->visitid= qh vertex_visit;
+ qh_point_add(facets, vertex->point, facet);
+ }
+ }
+ FOREACHpoint_(facet->coplanarset)
+ qh_point_add(facets, point, facet);
+ FOREACHpoint_(facet->outsideset)
+ qh_point_add(facets, point, facet);
+ }
+ return facets;
+} /* pointfacet */
+
+/*---------------------------------
+
+ qh_pointvertex( )
+ return temporary set of vertices indexed by point id
+ entry is NULL if no vertex for a point
+ this will include qh.GOODpointp
+
+ access:
+ FOREACHvertex_i_(vertices) { ... }
+ SETelem_(vertices, i)
+*/
+setT *qh_pointvertex(void /*qh facet_list*/) {
+ int numpoints= qh num_points + qh_setsize(qh other_points);
+ setT *vertices;
+ vertexT *vertex;
+
+ vertices= qh_settemp(numpoints);
+ qh_setzero(vertices, 0, numpoints);
+ FORALLvertices
+ qh_point_add(vertices, vertex->point, vertex);
+ return vertices;
+} /* pointvertex */
+
+
+/*---------------------------------
+
+ qh_prependfacet( facet, facetlist )
+ prepend facet to the start of a facetlist
+
+ returns:
+ increments qh.numfacets
+ updates facetlist, qh.facet_list, facet_next
+
+ notes:
+ be careful of prepending since it can lose a pointer.
+ e.g., can lose _next by deleting and then prepending before _next
+*/
+void qh_prependfacet(facetT *facet, facetT **facetlist) {
+ facetT *prevfacet, *list;
+
+
+ trace4((qh ferr, 4061, "qh_prependfacet: prepend f%d before f%d\n",
+ facet->id, getid_(*facetlist)));
+ if (!*facetlist)
+ (*facetlist)= qh facet_tail;
+ list= *facetlist;
+ prevfacet= list->previous;
+ facet->previous= prevfacet;
+ if (prevfacet)
+ prevfacet->next= facet;
+ list->previous= facet;
+ facet->next= *facetlist;
+ if (qh facet_list == list) /* this may change *facetlist */
+ qh facet_list= facet;
+ if (qh facet_next == list)
+ qh facet_next= facet;
+ *facetlist= facet;
+ qh num_facets++;
+} /* prependfacet */
+
+
+/*---------------------------------
+
+ qh_printhashtable( fp )
+ print hash table to fp
+
+ notes:
+ not in I/O to avoid bringing io.c in
+
+ design:
+ for each hash entry
+ if defined
+ if unmatched or will merge (NULL, qh_MERGEridge, qh_DUPLICATEridge)
+ print entry and neighbors
+*/
+void qh_printhashtable(FILE *fp) {
+ facetT *facet, *neighbor;
+ int id, facet_i, facet_n, neighbor_i= 0, neighbor_n= 0;
+ vertexT *vertex, **vertexp;
+
+ FOREACHfacet_i_(qh hash_table) {
+ if (facet) {
+ FOREACHneighbor_i_(facet) {
+ if (!neighbor || neighbor == qh_MERGEridge || neighbor == qh_DUPLICATEridge)
+ break;
+ }
+ if (neighbor_i == neighbor_n)
+ continue;
+ qh_fprintf(fp, 9283, "hash %d f%d ", facet_i, facet->id);
+ FOREACHvertex_(facet->vertices)
+ qh_fprintf(fp, 9284, "v%d ", vertex->id);
+ qh_fprintf(fp, 9285, "\n neighbors:");
+ FOREACHneighbor_i_(facet) {
+ if (neighbor == qh_MERGEridge)
+ id= -3;
+ else if (neighbor == qh_DUPLICATEridge)
+ id= -2;
+ else
+ id= getid_(neighbor);
+ qh_fprintf(fp, 9286, " %d", id);
+ }
+ qh_fprintf(fp, 9287, "\n");
+ }
+ }
+} /* printhashtable */
+
+
+/*---------------------------------
+
+ qh_printlists( fp )
+ print out facet and vertex list for debugging (without 'f/v' tags)
+*/
+void qh_printlists(void) {
+ facetT *facet;
+ vertexT *vertex;
+ int count= 0;
+
+ qh_fprintf(qh ferr, 8108, "qh_printlists: facets:");
+ FORALLfacets {
+ if (++count % 100 == 0)
+ qh_fprintf(qh ferr, 8109, "\n ");
+ qh_fprintf(qh ferr, 8110, " %d", facet->id);
+ }
+ qh_fprintf(qh ferr, 8111, "\n new facets %d visible facets %d next facet for qh_addpoint %d\n vertices(new %d):",
+ getid_(qh newfacet_list), getid_(qh visible_list), getid_(qh facet_next),
+ getid_(qh newvertex_list));
+ count = 0;
+ FORALLvertices {
+ if (++count % 100 == 0)
+ qh_fprintf(qh ferr, 8112, "\n ");
+ qh_fprintf(qh ferr, 8113, " %d", vertex->id);
+ }
+ qh_fprintf(qh ferr, 8114, "\n");
+} /* printlists */
+
+/*---------------------------------
+
+ qh_resetlists( stats, qh_RESETvisible )
+ reset newvertex_list, newfacet_list, visible_list
+ if stats,
+ maintains statistics
+
+ returns:
+ visible_list is empty if qh_deletevisible was called
+*/
+void qh_resetlists(boolT stats, boolT resetVisible /*qh newvertex_list newfacet_list visible_list*/) {
+ vertexT *vertex;
+ facetT *newfacet, *visible;
+ int totnew=0, totver=0;
+
+ if (stats) {
+ FORALLvertex_(qh newvertex_list)
+ totver++;
+ FORALLnew_facets
+ totnew++;
+ zadd_(Zvisvertextot, totver);
+ zmax_(Zvisvertexmax, totver);
+ zadd_(Znewfacettot, totnew);
+ zmax_(Znewfacetmax, totnew);
+ }
+ FORALLvertex_(qh newvertex_list)
+ vertex->newlist= False;
+ qh newvertex_list= NULL;
+ FORALLnew_facets
+ newfacet->newfacet= False;
+ qh newfacet_list= NULL;
+ if (resetVisible) {
+ FORALLvisible_facets {
+ visible->f.replace= NULL;
+ visible->visible= False;
+ }
+ qh num_visible= 0;
+ }
+ qh visible_list= NULL; /* may still have visible facets via qh_triangulate */
+ qh NEWfacets= False;
+} /* resetlists */
+
+/*---------------------------------
+
+ qh_setvoronoi_all()
+ compute Voronoi centers for all facets
+ includes upperDelaunay facets if qh.UPPERdelaunay ('Qu')
+
+ returns:
+ facet->center is the Voronoi center
+
+ notes:
+ this is unused/untested code
+ please email bradb@shore.net if this works ok for you
+
+ use:
+ FORALLvertices {...} to locate the vertex for a point.
+ FOREACHneighbor_(vertex) {...} to visit the Voronoi centers for a Voronoi cell.
+*/
+void qh_setvoronoi_all(void) {
+ facetT *facet;
+
+ qh_clearcenters(qh_ASvoronoi);
+ qh_vertexneighbors();
+
+ FORALLfacets {
+ if (!facet->normal || !facet->upperdelaunay || qh UPPERdelaunay) {
+ if (!facet->center)
+ facet->center= qh_facetcenter(facet->vertices);
+ }
+ }
+} /* setvoronoi_all */
+
+#ifndef qh_NOmerge
+
+/*---------------------------------
+
+ qh_triangulate()
+ triangulate non-simplicial facets on qh.facet_list,
+ if qh VORONOI, sets Voronoi centers of non-simplicial facets
+ nop if hasTriangulation
+
+ returns:
+ all facets simplicial
+ each tricoplanar facet has ->f.triowner == owner of ->center,normal,etc.
+
+ notes:
+ call after qh_check_output since may switch to Voronoi centers
+ Output may overwrite ->f.triowner with ->f.area
+*/
+void qh_triangulate(void /*qh facet_list*/) {
+ facetT *facet, *nextfacet, *owner;
+ int onlygood= qh ONLYgood;
+ facetT *neighbor, *visible= NULL, *facet1, *facet2, *new_facet_list= NULL;
+ facetT *orig_neighbor= NULL, *otherfacet;
+ vertexT *new_vertex_list= NULL;
+ mergeT *merge;
+ mergeType mergetype;
+ int neighbor_i, neighbor_n;
+
+ if (qh hasTriangulation)
+ return;
+ trace1((qh ferr, 1034, "qh_triangulate: triangulate non-simplicial facets\n"));
+ if (qh hull_dim == 2)
+ return;
+ if (qh VORONOI) { /* otherwise lose Voronoi centers [could rebuild vertex set from tricoplanar] */
+ qh_clearcenters(qh_ASvoronoi);
+ qh_vertexneighbors();
+ }
+ qh ONLYgood= False; /* for makenew_nonsimplicial */
+ qh visit_id++;
+ qh NEWfacets= True;
+ qh degen_mergeset= qh_settemp(qh TEMPsize);
+ qh newvertex_list= qh vertex_tail;
+ for (facet= qh facet_list; facet && facet->next; facet= nextfacet) { /* non-simplicial facets moved to end */
+ nextfacet= facet->next;
+ if (facet->visible || facet->simplicial)
+ continue;
+ /* triangulate all non-simplicial facets, otherwise merging does not work, e.g., RBOX c P-0.1 P+0.1 P+0.1 D3 | QHULL d Qt Tv */
+ if (!new_facet_list)
+ new_facet_list= facet; /* will be moved to end */
+ qh_triangulate_facet(facet, &new_vertex_list);
+ }
+ trace2((qh ferr, 2047, "qh_triangulate: delete null facets from f%d -- apex same as second vertex\n", getid_(new_facet_list)));
+ for (facet= new_facet_list; facet && facet->next; facet= nextfacet) { /* null facets moved to end */
+ nextfacet= facet->next;
+ if (facet->visible)
+ continue;
+ if (facet->ridges) {
+ if (qh_setsize(facet->ridges) > 0) {
+ qh_fprintf(qh ferr, 6161, "qhull error (qh_triangulate): ridges still defined for f%d\n", facet->id);
+ qh_errexit(qh_ERRqhull, facet, NULL);
+ }
+ qh_setfree(&facet->ridges);
+ }
+ if (SETfirst_(facet->vertices) == SETsecond_(facet->vertices)) {
+ zinc_(Ztrinull);
+ qh_triangulate_null(facet);
+ }
+ }
+ trace2((qh ferr, 2048, "qh_triangulate: delete %d or more mirror facets -- same vertices and neighbors\n", qh_setsize(qh degen_mergeset)));
+ qh visible_list= qh facet_tail;
+ while ((merge= (mergeT*)qh_setdellast(qh degen_mergeset))) {
+ facet1= merge->facet1;
+ facet2= merge->facet2;
+ mergetype= merge->type;
+ qh_memfree(merge, (int)sizeof(mergeT));
+ if (mergetype == MRGmirror) {
+ zinc_(Ztrimirror);
+ qh_triangulate_mirror(facet1, facet2);
+ }
+ }
+ qh_settempfree(&qh degen_mergeset);
+ trace2((qh ferr, 2049, "qh_triangulate: update neighbor lists for vertices from v%d\n", getid_(new_vertex_list)));
+ qh newvertex_list= new_vertex_list; /* all vertices of new facets */
+ qh visible_list= NULL;
+ qh_updatevertices(/*qh newvertex_list, empty newfacet_list and visible_list*/);
+ qh_resetlists(False, !qh_RESETvisible /*qh newvertex_list, empty newfacet_list and visible_list*/);
+
+ trace2((qh ferr, 2050, "qh_triangulate: identify degenerate tricoplanar facets from f%d\n", getid_(new_facet_list)));
+ trace2((qh ferr, 2051, "qh_triangulate: and replace facet->f.triowner with tricoplanar facets that own center, normal, etc.\n"));
+ FORALLfacet_(new_facet_list) {
+ if (facet->tricoplanar && !facet->visible) {
+ FOREACHneighbor_i_(facet) {
+ if (neighbor_i == 0) { /* first iteration */
+ if (neighbor->tricoplanar)
+ orig_neighbor= neighbor->f.triowner;
+ else
+ orig_neighbor= neighbor;
+ }else {
+ if (neighbor->tricoplanar)
+ otherfacet= neighbor->f.triowner;
+ else
+ otherfacet= neighbor;
+ if (orig_neighbor == otherfacet) {
+ zinc_(Ztridegen);
+ facet->degenerate= True;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ trace2((qh ferr, 2052, "qh_triangulate: delete visible facets -- non-simplicial, null, and mirrored facets\n"));
+ owner= NULL;
+ visible= NULL;
+ for (facet= new_facet_list; facet && facet->next; facet= nextfacet) { /* may delete facet */
+ nextfacet= facet->next;
+ if (facet->visible) {
+ if (facet->tricoplanar) { /* a null or mirrored facet */
+ qh_delfacet(facet);
+ qh num_visible--;
+ }else { /* a non-simplicial facet followed by its tricoplanars */
+ if (visible && !owner) {
+ /* RBOX 200 s D5 t1001471447 | QHULL Qt C-0.01 Qx Qc Tv Qt -- f4483 had 6 vertices/neighbors and 8 ridges */
+ trace2((qh ferr, 2053, "qh_triangulate: all tricoplanar facets degenerate for non-simplicial facet f%d\n",
+ visible->id));
+ qh_delfacet(visible);
+ qh num_visible--;
+ }
+ visible= facet;
+ owner= NULL;
+ }
+ }else if (facet->tricoplanar) {
+ if (facet->f.triowner != visible) {
+ qh_fprintf(qh ferr, 6162, "qhull error (qh_triangulate): tricoplanar facet f%d not owned by its visible, non-simplicial facet f%d\n", facet->id, getid_(visible));
+ qh_errexit2 (qh_ERRqhull, facet, visible);
+ }
+ if (owner)
+ facet->f.triowner= owner;
+ else if (!facet->degenerate) {
+ owner= facet;
+ nextfacet= visible->next; /* rescan tricoplanar facets with owner */
+ facet->keepcentrum= True; /* one facet owns ->normal, etc. */
+ facet->coplanarset= visible->coplanarset;
+ facet->outsideset= visible->outsideset;
+ visible->coplanarset= NULL;
+ visible->outsideset= NULL;
+ if (!qh TRInormals) { /* center and normal copied to tricoplanar facets */
+ visible->center= NULL;
+ visible->normal= NULL;
+ }
+ qh_delfacet(visible);
+ qh num_visible--;
+ }
+ }
+ }
+ if (visible && !owner) {
+ trace2((qh ferr, 2054, "qh_triangulate: all tricoplanar facets degenerate for last non-simplicial facet f%d\n",
+ visible->id));
+ qh_delfacet(visible);
+ qh num_visible--;
+ }
+ qh NEWfacets= False;
+ qh ONLYgood= onlygood; /* restore value */
+ if (qh CHECKfrequently)
+ qh_checkpolygon(qh facet_list);
+ qh hasTriangulation= True;
+} /* triangulate */
+
+
+/*---------------------------------
+
+ qh_triangulate_facet(facetA)
+ triangulate a non-simplicial facet
+ if qh.CENTERtype=qh_ASvoronoi, sets its Voronoi center
+ returns:
+ qh.newfacet_list == simplicial facets
+ facet->tricoplanar set and ->keepcentrum false
+ facet->degenerate set if duplicated apex
+ facet->f.trivisible set to facetA
+ facet->center copied from facetA (created if qh_ASvoronoi)
+ qh_eachvoronoi, qh_detvridge, qh_detvridge3 assume centers copied
+ facet->normal,offset,maxoutside copied from facetA
+
+ notes:
+ qh_makenew_nonsimplicial uses neighbor->seen for the same
+
+ see also:
+ qh_addpoint() -- add a point
+ qh_makenewfacets() -- construct a cone of facets for a new vertex
+
+ design:
+ if qh_ASvoronoi,
+ compute Voronoi center (facet->center)
+ select first vertex (highest ID to preserve ID ordering of ->vertices)
+ triangulate from vertex to ridges
+ copy facet->center, normal, offset
+ update vertex neighbors
+*/
+void qh_triangulate_facet(facetT *facetA, vertexT **first_vertex) {
+ facetT *newfacet;
+ facetT *neighbor, **neighborp;
+ vertexT *apex;
+ int numnew=0;
+
+ trace3((qh ferr, 3020, "qh_triangulate_facet: triangulate facet f%d\n", facetA->id));
+
+ if (qh IStracing >= 4)
+ qh_printfacet(qh ferr, facetA);
+ FOREACHneighbor_(facetA) {
+ neighbor->seen= False;
+ neighbor->coplanar= False;
+ }
+ if (qh CENTERtype == qh_ASvoronoi && !facetA->center /* matches upperdelaunay in qh_setfacetplane() */
+ && fabs_(facetA->normal[qh hull_dim -1]) >= qh ANGLEround * qh_ZEROdelaunay) {
+ facetA->center= qh_facetcenter(facetA->vertices);
+ }
+ qh_willdelete(facetA, NULL);
+ qh newfacet_list= qh facet_tail;
+ facetA->visitid= qh visit_id;
+ apex= SETfirstt_(facetA->vertices, vertexT);
+ qh_makenew_nonsimplicial(facetA, apex, &numnew);
+ SETfirst_(facetA->neighbors)= NULL;
+ FORALLnew_facets {
+ newfacet->tricoplanar= True;
+ newfacet->f.trivisible= facetA;
+ newfacet->degenerate= False;
+ newfacet->upperdelaunay= facetA->upperdelaunay;
+ newfacet->good= facetA->good;
+ if (qh TRInormals) {
+ newfacet->keepcentrum= True;
+ newfacet->normal= qh_copypoints(facetA->normal, 1, qh hull_dim);
+ if (qh CENTERtype == qh_AScentrum)
+ newfacet->center= qh_getcentrum(newfacet);
+ else
+ newfacet->center= qh_copypoints(facetA->center, 1, qh hull_dim);
+ }else {
+ newfacet->keepcentrum= False;
+ newfacet->normal= facetA->normal;
+ newfacet->center= facetA->center;
+ }
+ newfacet->offset= facetA->offset;
+#if qh_MAXoutside
+ newfacet->maxoutside= facetA->maxoutside;
+#endif
+ }
+ qh_matchnewfacets(/*qh newfacet_list*/);
+ zinc_(Ztricoplanar);
+ zadd_(Ztricoplanartot, numnew);
+ zmax_(Ztricoplanarmax, numnew);
+ qh visible_list= NULL;
+ if (!(*first_vertex))
+ (*first_vertex)= qh newvertex_list;
+ qh newvertex_list= NULL;
+ qh_updatevertices(/*qh newfacet_list, empty visible_list and newvertex_list*/);
+ qh_resetlists(False, !qh_RESETvisible /*qh newfacet_list, empty visible_list and newvertex_list*/);
+} /* triangulate_facet */
+
+/*---------------------------------
+
+ qh_triangulate_link(oldfacetA, facetA, oldfacetB, facetB)
+ relink facetA to facetB via oldfacets
+ returns:
+ adds mirror facets to qh degen_mergeset (4-d and up only)
+ design:
+ if they are already neighbors, the opposing neighbors become MRGmirror facets
+*/
+void qh_triangulate_link(facetT *oldfacetA, facetT *facetA, facetT *oldfacetB, facetT *facetB) {
+ int errmirror= False;
+
+ trace3((qh ferr, 3021, "qh_triangulate_link: relink old facets f%d and f%d between neighbors f%d and f%d\n",
+ oldfacetA->id, oldfacetB->id, facetA->id, facetB->id));
+ if (qh_setin(facetA->neighbors, facetB)) {
+ if (!qh_setin(facetB->neighbors, facetA))
+ errmirror= True;
+ else
+ qh_appendmergeset(facetA, facetB, MRGmirror, NULL);
+ }else if (qh_setin(facetB->neighbors, facetA))
+ errmirror= True;
+ if (errmirror) {
+ qh_fprintf(qh ferr, 6163, "qhull error (qh_triangulate_link): mirror facets f%d and f%d do not match for old facets f%d and f%d\n",
+ facetA->id, facetB->id, oldfacetA->id, oldfacetB->id);
+ qh_errexit2 (qh_ERRqhull, facetA, facetB);
+ }
+ qh_setreplace(facetB->neighbors, oldfacetB, facetA);
+ qh_setreplace(facetA->neighbors, oldfacetA, facetB);
+} /* triangulate_link */
+
+/*---------------------------------
+
+ qh_triangulate_mirror(facetA, facetB)
+ delete mirrored facets from qh_triangulate_null() and qh_triangulate_mirror
+ a mirrored facet shares the same vertices of a logical ridge
+ design:
+ since a null facet duplicates the first two vertices, the opposing neighbors absorb the null facet
+ if they are already neighbors, the opposing neighbors become MRGmirror facets
+*/
+void qh_triangulate_mirror(facetT *facetA, facetT *facetB) {
+ facetT *neighbor, *neighborB;
+ int neighbor_i, neighbor_n;
+
+ trace3((qh ferr, 3022, "qh_triangulate_mirror: delete mirrored facets f%d and f%d\n",
+ facetA->id, facetB->id));
+ FOREACHneighbor_i_(facetA) {
+ neighborB= SETelemt_(facetB->neighbors, neighbor_i, facetT);
+ if (neighbor == neighborB)
+ continue; /* occurs twice */
+ qh_triangulate_link(facetA, neighbor, facetB, neighborB);
+ }
+ qh_willdelete(facetA, NULL);
+ qh_willdelete(facetB, NULL);
+} /* triangulate_mirror */
+
+/*---------------------------------
+
+ qh_triangulate_null(facetA)
+ remove null facetA from qh_triangulate_facet()
+ a null facet has vertex #1 (apex) == vertex #2
+ returns:
+ adds facetA to ->visible for deletion after qh_updatevertices
+ qh degen_mergeset contains mirror facets (4-d and up only)
+ design:
+ since a null facet duplicates the first two vertices, the opposing neighbors absorb the null facet
+ if they are already neighbors, the opposing neighbors become MRGmirror facets
+*/
+void qh_triangulate_null(facetT *facetA) {
+ facetT *neighbor, *otherfacet;
+
+ trace3((qh ferr, 3023, "qh_triangulate_null: delete null facet f%d\n", facetA->id));
+ neighbor= SETfirstt_(facetA->neighbors, facetT);
+ otherfacet= SETsecondt_(facetA->neighbors, facetT);
+ qh_triangulate_link(facetA, neighbor, facetA, otherfacet);
+ qh_willdelete(facetA, NULL);
+} /* triangulate_null */
+
+#else /* qh_NOmerge */
+void qh_triangulate(void) {
+}
+#endif /* qh_NOmerge */
+
+ /*---------------------------------
+
+ qh_vertexintersect( vertexsetA, vertexsetB )
+ intersects two vertex sets (inverse id ordered)
+ vertexsetA is a temporary set at the top of qhmem.tempstack
+
+ returns:
+ replaces vertexsetA with the intersection
+
+ notes:
+ could overwrite vertexsetA if currently too slow
+*/
+void qh_vertexintersect(setT **vertexsetA,setT *vertexsetB) {
+ setT *intersection;
+
+ intersection= qh_vertexintersect_new(*vertexsetA, vertexsetB);
+ qh_settempfree(vertexsetA);
+ *vertexsetA= intersection;
+ qh_settemppush(intersection);
+} /* vertexintersect */
+
+/*---------------------------------
+
+ qh_vertexintersect_new( )
+ intersects two vertex sets (inverse id ordered)
+
+ returns:
+ a new set
+*/
+setT *qh_vertexintersect_new(setT *vertexsetA,setT *vertexsetB) {
+ setT *intersection= qh_setnew(qh hull_dim - 1);
+ vertexT **vertexA= SETaddr_(vertexsetA, vertexT);
+ vertexT **vertexB= SETaddr_(vertexsetB, vertexT);
+
+ while (*vertexA && *vertexB) {
+ if (*vertexA == *vertexB) {
+ qh_setappend(&intersection, *vertexA);
+ vertexA++; vertexB++;
+ }else {
+ if ((*vertexA)->id > (*vertexB)->id)
+ vertexA++;
+ else
+ vertexB++;
+ }
+ }
+ return intersection;
+} /* vertexintersect_new */
+
+/*---------------------------------
+
+ qh_vertexneighbors()
+ for each vertex in qh.facet_list,
+ determine its neighboring facets
+
+ returns:
+ sets qh.VERTEXneighbors
+ nop if qh.VERTEXneighbors already set
+ qh_addpoint() will maintain them
+
+ notes:
+ assumes all vertex->neighbors are NULL
+
+ design:
+ for each facet
+ for each vertex
+ append facet to vertex->neighbors
+*/
+void qh_vertexneighbors(void /*qh facet_list*/) {
+ facetT *facet;
+ vertexT *vertex, **vertexp;
+
+ if (qh VERTEXneighbors)
+ return;
+ trace1((qh ferr, 1035, "qh_vertexneighbors: determing neighboring facets for each vertex\n"));
+ qh vertex_visit++;
+ FORALLfacets {
+ if (facet->visible)
+ continue;
+ FOREACHvertex_(facet->vertices) {
+ if (vertex->visitid != qh vertex_visit) {
+ vertex->visitid= qh vertex_visit;
+ vertex->neighbors= qh_setnew(qh hull_dim);
+ }
+ qh_setappend(&vertex->neighbors, facet);
+ }
+ }
+ qh VERTEXneighbors= True;
+} /* vertexneighbors */
+
+/*---------------------------------
+
+ qh_vertexsubset( vertexsetA, vertexsetB )
+ returns True if vertexsetA is a subset of vertexsetB
+ assumes vertexsets are sorted
+
+ note:
+ empty set is a subset of any other set
+*/
+boolT qh_vertexsubset(setT *vertexsetA, setT *vertexsetB) {
+ vertexT **vertexA= (vertexT **) SETaddr_(vertexsetA, vertexT);
+ vertexT **vertexB= (vertexT **) SETaddr_(vertexsetB, vertexT);
+
+ while (True) {
+ if (!*vertexA)
+ return True;
+ if (!*vertexB)
+ return False;
+ if ((*vertexA)->id > (*vertexB)->id)
+ return False;
+ if (*vertexA == *vertexB)
+ vertexA++;
+ vertexB++;
+ }
+ return False; /* avoid warnings */
+} /* vertexsubset */
diff --git a/src/libqhullr/poly_r.c b/src/libqhullr/poly_r.c
new file mode 100644
index 0000000..a066ae4
--- /dev/null
+++ b/src/libqhullr/poly_r.c
@@ -0,0 +1,1199 @@
+/*
---------------------------------
+
+ poly.c
+ implements polygons and simplices
+
+ see qh-poly.htm, poly.h and libqhull.h
+
+ infrequent code is in poly2.c
+ (all but top 50 and their callers 12/3/95)
+
+ Copyright (c) 1993-2012 The Geometry Center.
+ $Id: //main/2011/qhull/src/libqhullr/poly_r.c#1 $$Change: 1640 $
+ $DateTime: 2014/01/15 09:12:08 $$Author: bbarber $
+*/
+
+#include "qhull_a.h"
+
+/*======== functions in alphabetical order ==========*/
+
+/*---------------------------------
+
+ qh_appendfacet( facet )
+ appends facet to end of qh.facet_list,
+
+ returns:
+ updates qh.newfacet_list, facet_next, facet_list
+ increments qh.numfacets
+
+ notes:
+ assumes qh.facet_list/facet_tail is defined (createsimplex)
+
+ see:
+ qh_removefacet()
+
+*/
+void qh_appendfacet(facetT *facet) {
+ facetT *tail= qh facet_tail;
+
+ if (tail == qh newfacet_list)
+ qh newfacet_list= facet;
+ if (tail == qh facet_next)
+ qh facet_next= facet;
+ facet->previous= tail->previous;
+ facet->next= tail;
+ if (tail->previous)
+ tail->previous->next= facet;
+ else
+ qh facet_list= facet;
+ tail->previous= facet;
+ qh num_facets++;
+ trace4((qh ferr, 4044, "qh_appendfacet: append f%d to facet_list\n", facet->id));
+} /* appendfacet */
+
+
+/*---------------------------------
+
+ qh_appendvertex( vertex )
+ appends vertex to end of qh.vertex_list,
+
+ returns:
+ sets vertex->newlist
+ updates qh.vertex_list, newvertex_list
+ increments qh.num_vertices
+
+ notes:
+ assumes qh.vertex_list/vertex_tail is defined (createsimplex)
+
+*/
+void qh_appendvertex(vertexT *vertex) {
+ vertexT *tail= qh vertex_tail;
+
+ if (tail == qh newvertex_list)
+ qh newvertex_list= vertex;
+ vertex->newlist= True;
+ vertex->previous= tail->previous;
+ vertex->next= tail;
+ if (tail->previous)
+ tail->previous->next= vertex;
+ else
+ qh vertex_list= vertex;
+ tail->previous= vertex;
+ qh num_vertices++;
+ trace4((qh ferr, 4045, "qh_appendvertex: append v%d to vertex_list\n", vertex->id));
+} /* appendvertex */
+
+
+/*---------------------------------
+
+ qh_attachnewfacets( )
+ attach horizon facets to new facets in qh.newfacet_list
+ newfacets have neighbor and ridge links to horizon but not vice versa
+ only needed for qh.ONLYgood
+
+ returns:
+ set qh.NEWfacets
+ horizon facets linked to new facets
+ ridges changed from visible facets to new facets
+ simplicial ridges deleted
+ qh.visible_list, no ridges valid
+ facet->f.replace is a newfacet (if any)
+
+ design:
+ delete interior ridges and neighbor sets by
+ for each visible, non-simplicial facet
+ for each ridge
+ if last visit or if neighbor is simplicial
+ if horizon neighbor
+ delete ridge for horizon's ridge set
+ delete ridge
+ erase neighbor set
+ attach horizon facets and new facets by
+ for all new facets
+ if corresponding horizon facet is simplicial
+ locate corresponding visible facet {may be more than one}
+ link visible facet to new facet
+ replace visible facet with new facet in horizon
+ else it's non-simplicial
+ for all visible neighbors of the horizon facet
+ link visible neighbor to new facet
+ delete visible neighbor from horizon facet
+ append new facet to horizon's neighbors
+ the first ridge of the new facet is the horizon ridge
+ link the new facet into the horizon ridge
+*/
+void qh_attachnewfacets(void ) {
+ facetT *newfacet= NULL, *neighbor, **neighborp, *horizon, *visible;
+ ridgeT *ridge, **ridgep;
+
+ qh NEWfacets= True;
+ trace3((qh ferr, 3012, "qh_attachnewfacets: delete interior ridges\n"));
+ qh visit_id++;
+ FORALLvisible_facets {
+ visible->visitid= qh visit_id;
+ if (visible->ridges) {
+ FOREACHridge_(visible->ridges) {
+ neighbor= otherfacet_(ridge, visible);
+ if (neighbor->visitid == qh visit_id
+ || (!neighbor->visible && neighbor->simplicial)) {
+ if (!neighbor->visible) /* delete ridge for simplicial horizon */
+ qh_setdel(neighbor->ridges, ridge);
+ qh_setfree(&(ridge->vertices)); /* delete on 2nd visit */
+ qh_memfree(ridge, (int)sizeof(ridgeT));
+ }
+ }
+ SETfirst_(visible->ridges)= NULL;
+ }
+ SETfirst_(visible->neighbors)= NULL;
+ }
+ trace1((qh ferr, 1017, "qh_attachnewfacets: attach horizon facets to new facets\n"));
+ FORALLnew_facets {
+ horizon= SETfirstt_(newfacet->neighbors, facetT);
+ if (horizon->simplicial) {
+ visible= NULL;
+ FOREACHneighbor_(horizon) { /* may have more than one horizon ridge */
+ if (neighbor->visible) {
+ if (visible) {
+ if (qh_setequal_skip(newfacet->vertices, 0, horizon->vertices,
+ SETindex_(horizon->neighbors, neighbor))) {
+ visible= neighbor;
+ break;
+ }
+ }else
+ visible= neighbor;
+ }
+ }
+ if (visible) {
+ visible->f.replace= newfacet;
+ qh_setreplace(horizon->neighbors, visible, newfacet);
+ }else {
+ qh_fprintf(qh ferr, 6102, "qhull internal error (qh_attachnewfacets): couldn't find visible facet for horizon f%d of newfacet f%d\n",
+ horizon->id, newfacet->id);
+ qh_errexit2 (qh_ERRqhull, horizon, newfacet);
+ }
+ }else { /* non-simplicial, with a ridge for newfacet */
+ FOREACHneighbor_(horizon) { /* may hold for many new facets */
+ if (neighbor->visible) {
+ neighbor->f.replace= newfacet;
+ qh_setdelnth(horizon->neighbors,
+ SETindex_(horizon->neighbors, neighbor));
+ neighborp--; /* repeat */
+ }
+ }
+ qh_setappend(&horizon->neighbors, newfacet);
+ ridge= SETfirstt_(newfacet->ridges, ridgeT);
+ if (ridge->top == horizon)
+ ridge->bottom= newfacet;
+ else
+ ridge->top= newfacet;
+ }
+ } /* newfacets */
+ if (qh PRINTstatistics) {
+ FORALLvisible_facets {
+ if (!visible->f.replace)
+ zinc_(Zinsidevisible);
+ }
+ }
+} /* attachnewfacets */
+
+/*---------------------------------
+
+ qh_checkflipped( facet, dist, allerror )
+ checks facet orientation to interior point
+
+ if allerror set,
+ tests against qh.DISTround
+ else
+ tests against 0 since tested against DISTround before
+
+ returns:
+ False if it flipped orientation (sets facet->flipped)
+ distance if non-NULL
+*/
+boolT qh_checkflipped(facetT *facet, realT *distp, boolT allerror) {
+ realT dist;
+
+ if (facet->flipped && !distp)
+ return False;
+ zzinc_(Zdistcheck);
+ qh_distplane(qh interior_point, facet, &dist);
+ if (distp)
+ *distp= dist;
+ if ((allerror && dist > -qh DISTround)|| (!allerror && dist >= 0.0)) {
+ facet->flipped= True;
+ zzinc_(Zflippedfacets);
+ trace0((qh ferr, 19, "qh_checkflipped: facet f%d is flipped, distance= %6.12g during p%d\n",
+ facet->id, dist, qh furthest_id));
+ qh_precision("flipped facet");
+ return False;
+ }
+ return True;
+} /* checkflipped */
+
+/*---------------------------------
+
+ qh_delfacet( facet )
+ removes facet from facet_list and frees up its memory
+
+ notes:
+ assumes vertices and ridges already freed
+*/
+void qh_delfacet(facetT *facet) {
+ void **freelistp; /* used !qh_NOmem */
+
+ trace4((qh ferr, 4046, "qh_delfacet: delete f%d\n", facet->id));
+ if (facet == qh tracefacet)
+ qh tracefacet= NULL;
+ if (facet == qh GOODclosest)
+ qh GOODclosest= NULL;
+ qh_removefacet(facet);
+ if (!facet->tricoplanar || facet->keepcentrum) {
+ qh_memfree_(facet->normal, qh normal_size, freelistp);
+ if (qh CENTERtype == qh_ASvoronoi) { /* uses macro calls */
+ qh_memfree_(facet->center, qh center_size, freelistp);
+ }else /* AScentrum */ {
+ qh_memfree_(facet->center, qh normal_size, freelistp);
+ }
+ }
+ qh_setfree(&(facet->neighbors));
+ if (facet->ridges)
+ qh_setfree(&(facet->ridges));
+ qh_setfree(&(facet->vertices));
+ if (facet->outsideset)
+ qh_setfree(&(facet->outsideset));
+ if (facet->coplanarset)
+ qh_setfree(&(facet->coplanarset));
+ qh_memfree_(facet, (int)sizeof(facetT), freelistp);
+} /* delfacet */
+
+
+/*---------------------------------
+
+ qh_deletevisible()
+ delete visible facets and vertices
+
+ returns:
+ deletes each facet and removes from facetlist
+ at exit, qh.visible_list empty (== qh.newfacet_list)
+
+ notes:
+ ridges already deleted
+ horizon facets do not reference facets on qh.visible_list
+ new facets in qh.newfacet_list
+ uses qh.visit_id;
+*/
+void qh_deletevisible(void /*qh visible_list*/) {
+ facetT *visible, *nextfacet;
+ vertexT *vertex, **vertexp;
+ int numvisible= 0, numdel= qh_setsize(qh del_vertices);
+
+ trace1((qh ferr, 1018, "qh_deletevisible: delete %d visible facets and %d vertices\n",
+ qh num_visible, numdel));
+ for (visible= qh visible_list; visible && visible->visible;
+ visible= nextfacet) { /* deleting current */
+ nextfacet= visible->next;
+ numvisible++;
+ qh_delfacet(visible);
+ }
+ if (numvisible != qh num_visible) {
+ qh_fprintf(qh ferr, 6103, "qhull internal error (qh_deletevisible): qh num_visible %d is not number of visible facets %d\n",
+ qh num_visible, numvisible);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ qh num_visible= 0;
+ zadd_(Zvisfacettot, numvisible);
+ zmax_(Zvisfacetmax, numvisible);
+ zzadd_(Zdelvertextot, numdel);
+ zmax_(Zdelvertexmax, numdel);
+ FOREACHvertex_(qh del_vertices)
+ qh_delvertex(vertex);
+ qh_settruncate(qh del_vertices, 0);
+} /* deletevisible */
+
+/*---------------------------------
+
+ qh_facetintersect( facetA, facetB, skipa, skipB, prepend )
+ return vertices for intersection of two simplicial facets
+ may include 1 prepended entry (if more, need to settemppush)
+
+ returns:
+ returns set of qh.hull_dim-1 + prepend vertices
+ returns skipped index for each test and checks for exactly one
+
+ notes:
+ does not need settemp since set in quick memory
+
+ see also:
+ qh_vertexintersect and qh_vertexintersect_new
+ use qh_setnew_delnthsorted to get nth ridge (no skip information)
+
+ design:
+ locate skipped vertex by scanning facet A's neighbors
+ locate skipped vertex by scanning facet B's neighbors
+ intersect the vertex sets
+*/
+setT *qh_facetintersect(facetT *facetA, facetT *facetB,
+ int *skipA,int *skipB, int prepend) {
+ setT *intersect;
+ int dim= qh hull_dim, i, j;
+ facetT **neighborsA, **neighborsB;
+
+ neighborsA= SETaddr_(facetA->neighbors, facetT);
+ neighborsB= SETaddr_(facetB->neighbors, facetT);
+ i= j= 0;
+ if (facetB == *neighborsA++)
+ *skipA= 0;
+ else if (facetB == *neighborsA++)
+ *skipA= 1;
+ else if (facetB == *neighborsA++)
+ *skipA= 2;
+ else {
+ for (i=3; i < dim; i++) {
+ if (facetB == *neighborsA++) {
+ *skipA= i;
+ break;
+ }
+ }
+ }
+ if (facetA == *neighborsB++)
+ *skipB= 0;
+ else if (facetA == *neighborsB++)
+ *skipB= 1;
+ else if (facetA == *neighborsB++)
+ *skipB= 2;
+ else {
+ for (j=3; j < dim; j++) {
+ if (facetA == *neighborsB++) {
+ *skipB= j;
+ break;
+ }
+ }
+ }
+ if (i >= dim || j >= dim) {
+ qh_fprintf(qh ferr, 6104, "qhull internal error (qh_facetintersect): f%d or f%d not in others neighbors\n",
+ facetA->id, facetB->id);
+ qh_errexit2 (qh_ERRqhull, facetA, facetB);
+ }
+ intersect= qh_setnew_delnthsorted(facetA->vertices, qh hull_dim, *skipA, prepend);
+ trace4((qh ferr, 4047, "qh_facetintersect: f%d skip %d matches f%d skip %d\n",
+ facetA->id, *skipA, facetB->id, *skipB));
+ return(intersect);
+} /* facetintersect */
+
+/*---------------------------------
+
+ qh_gethash( hashsize, set, size, firstindex, skipelem )
+ return hashvalue for a set with firstindex and skipelem
+
+ notes:
+ returned hash is in [0,hashsize)
+ assumes at least firstindex+1 elements
+ assumes skipelem is NULL, in set, or part of hash
+
+ hashes memory addresses which may change over different runs of the same data
+ using sum for hash does badly in high d
+*/
+int qh_gethash(int hashsize, setT *set, int size, int firstindex, void *skipelem) {
+ void **elemp= SETelemaddr_(set, firstindex, void);
+ ptr_intT hash = 0, elem;
+ unsigned result;
+ int i;
+#ifdef _MSC_VER /* Microsoft Visual C++ -- warn about 64-bit issues */
+#pragma warning( push) /* WARN64 -- ptr_intT holds a 64-bit pointer */
+#pragma warning( disable : 4311) /* 'type cast': pointer truncation from 'void*' to 'ptr_intT' */
+#endif
+
+ switch (size-firstindex) {
+ case 1:
+ hash= (ptr_intT)(*elemp) - (ptr_intT) skipelem;
+ break;
+ case 2:
+ hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] - (ptr_intT) skipelem;
+ break;
+ case 3:
+ hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] + (ptr_intT)elemp[2]
+ - (ptr_intT) skipelem;
+ break;
+ case 4:
+ hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] + (ptr_intT)elemp[2]
+ + (ptr_intT)elemp[3] - (ptr_intT) skipelem;
+ break;
+ case 5:
+ hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] + (ptr_intT)elemp[2]
+ + (ptr_intT)elemp[3] + (ptr_intT)elemp[4] - (ptr_intT) skipelem;
+ break;
+ case 6:
+ hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] + (ptr_intT)elemp[2]
+ + (ptr_intT)elemp[3] + (ptr_intT)elemp[4]+ (ptr_intT)elemp[5]
+ - (ptr_intT) skipelem;
+ break;
+ default:
+ hash= 0;
+ i= 3;
+ do { /* this is about 10% in 10-d */
+ if ((elem= (ptr_intT)*elemp++) != (ptr_intT)skipelem) {
+ hash ^= (elem << i) + (elem >> (32-i));
+ i += 3;
+ if (i >= 32)
+ i -= 32;
+ }
+ }while (*elemp);
+ break;
+ }
+ if (hashsize<0) {
+ qh_fprintf(qh ferr, 6202, "qhull internal error: negative hashsize %d passed to qh_gethash [poly.c]\n", hashsize);
+ qh_errexit2 (qh_ERRqhull, NULL, NULL);
+ }
+ result= (unsigned)hash;
+ result %= (unsigned)hashsize;
+ /* result= 0; for debugging */
+ return result;
+#ifdef _MSC_VER
+#pragma warning( pop)
+#endif
+} /* gethash */
+
+/*---------------------------------
+
+ qh_makenewfacet( vertices, toporient, horizon )
+ creates a toporient? facet from vertices
+
+ returns:
+ returns newfacet
+ adds newfacet to qh.facet_list
+ newfacet->vertices= vertices
+ if horizon
+ newfacet->neighbor= horizon, but not vice versa
+ newvertex_list updated with vertices
+*/
+facetT *qh_makenewfacet(setT *vertices, boolT toporient,facetT *horizon) {
+ facetT *newfacet;
+ vertexT *vertex, **vertexp;
+
+ FOREACHvertex_(vertices) {
+ if (!vertex->newlist) {
+ qh_removevertex(vertex);
+ qh_appendvertex(vertex);
+ }
+ }
+ newfacet= qh_newfacet();
+ newfacet->vertices= vertices;
+ newfacet->toporient= (unsigned char)toporient;
+ if (horizon)
+ qh_setappend(&(newfacet->neighbors), horizon);
+ qh_appendfacet(newfacet);
+ return(newfacet);
+} /* makenewfacet */
+
+
+/*---------------------------------
+
+ qh_makenewplanes()
+ make new hyperplanes for facets on qh.newfacet_list
+
+ returns:
+ all facets have hyperplanes or are marked for merging
+ doesn't create hyperplane if horizon is coplanar (will merge)
+ updates qh.min_vertex if qh.JOGGLEmax
+
+ notes:
+ facet->f.samecycle is defined for facet->mergehorizon facets
+*/
+void qh_makenewplanes(void /* newfacet_list */) {
+ facetT *newfacet;
+
+ FORALLnew_facets {
+ if (!newfacet->mergehorizon)
+ qh_setfacetplane(newfacet);
+ }
+ if (qh JOGGLEmax < REALmax/2)
+ minimize_(qh min_vertex, -wwval_(Wnewvertexmax));
+} /* makenewplanes */
+
+/*---------------------------------
+
+ qh_makenew_nonsimplicial( visible, apex, numnew )
+ make new facets for ridges of a visible facet
+
+ returns:
+ first newfacet, bumps numnew as needed
+ attaches new facets if !qh.ONLYgood
+ marks ridge neighbors for simplicial visible
+ if (qh.ONLYgood)
+ ridges on newfacet, horizon, and visible
+ else
+ ridge and neighbors between newfacet and horizon
+ visible facet's ridges are deleted
+
+ notes:
+ qh.visit_id if visible has already been processed
+ sets neighbor->seen for building f.samecycle
+ assumes all 'seen' flags initially false
+
+ design:
+ for each ridge of visible facet
+ get neighbor of visible facet
+ if neighbor was already processed
+ delete the ridge (will delete all visible facets later)
+ if neighbor is a horizon facet
+ create a new facet
+ if neighbor coplanar
+ adds newfacet to f.samecycle for later merging
+ else
+ updates neighbor's neighbor set
+ (checks for non-simplicial facet with multiple ridges to visible facet)
+ updates neighbor's ridge set
+ (checks for simplicial neighbor to non-simplicial visible facet)
+ (deletes ridge if neighbor is simplicial)
+
+*/
+#ifndef qh_NOmerge
+facetT *qh_makenew_nonsimplicial(facetT *visible, vertexT *apex, int *numnew) {
+ void **freelistp; /* used !qh_NOmem */
+ ridgeT *ridge, **ridgep;
+ facetT *neighbor, *newfacet= NULL, *samecycle;
+ setT *vertices;
+ boolT toporient;
+ int ridgeid;
+
+ FOREACHridge_(visible->ridges) {
+ ridgeid= ridge->id;
+ neighbor= otherfacet_(ridge, visible);
+ if (neighbor->visible) {
+ if (!qh ONLYgood) {
+ if (neighbor->visitid == qh visit_id) {
+ qh_setfree(&(ridge->vertices)); /* delete on 2nd visit */
+ qh_memfree_(ridge, (int)sizeof(ridgeT), freelistp);
+ }
+ }
+ }else { /* neighbor is an horizon facet */
+ toporient= (ridge->top == visible);
+ vertices= qh_setnew(qh hull_dim); /* makes sure this is quick */
+ qh_setappend(&vertices, apex);
+ qh_setappend_set(&vertices, ridge->vertices);
+ newfacet= qh_makenewfacet(vertices, toporient, neighbor);
+ (*numnew)++;
+ if (neighbor->coplanar) {
+ newfacet->mergehorizon= True;
+ if (!neighbor->seen) {
+ newfacet->f.samecycle= newfacet;
+ neighbor->f.newcycle= newfacet;
+ }else {
+ samecycle= neighbor->f.newcycle;
+ newfacet->f.samecycle= samecycle->f.samecycle;
+ samecycle->f.samecycle= newfacet;
+ }
+ }
+ if (qh ONLYgood) {
+ if (!neighbor->simplicial)
+ qh_setappend(&(newfacet->ridges), ridge);
+ }else { /* qh_attachnewfacets */
+ if (neighbor->seen) {
+ if (neighbor->simplicial) {
+ qh_fprintf(qh ferr, 6105, "qhull internal error (qh_makenew_nonsimplicial): simplicial f%d sharing two ridges with f%d\n",
+ neighbor->id, visible->id);
+ qh_errexit2 (qh_ERRqhull, neighbor, visible);
+ }
+ qh_setappend(&(neighbor->neighbors), newfacet);
+ }else
+ qh_setreplace(neighbor->neighbors, visible, newfacet);
+ if (neighbor->simplicial) {
+ qh_setdel(neighbor->ridges, ridge);
+ qh_setfree(&(ridge->vertices));
+ qh_memfree(ridge, (int)sizeof(ridgeT));
+ }else {
+ qh_setappend(&(newfacet->ridges), ridge);
+ if (toporient)
+ ridge->top= newfacet;
+ else
+ ridge->bottom= newfacet;
+ }
+ trace4((qh ferr, 4048, "qh_makenew_nonsimplicial: created facet f%d from v%d and r%d of horizon f%d\n",
+ newfacet->id, apex->id, ridgeid, neighbor->id));
+ }
+ }
+ neighbor->seen= True;
+ } /* for each ridge */
+ if (!qh ONLYgood)
+ SETfirst_(visible->ridges)= NULL;
+ return newfacet;
+} /* makenew_nonsimplicial */
+#else /* qh_NOmerge */
+facetT *qh_makenew_nonsimplicial(facetT *visible, vertexT *apex, int *numnew) {
+ return NULL;
+}
+#endif /* qh_NOmerge */
+
+/*---------------------------------
+
+ qh_makenew_simplicial( visible, apex, numnew )
+ make new facets for simplicial visible facet and apex
+
+ returns:
+ attaches new facets if (!qh.ONLYgood)
+ neighbors between newfacet and horizon
+
+ notes:
+ nop if neighbor->seen or neighbor->visible(see qh_makenew_nonsimplicial)
+
+ design:
+ locate neighboring horizon facet for visible facet
+ determine vertices and orientation
+ create new facet
+ if coplanar,
+ add new facet to f.samecycle
+ update horizon facet's neighbor list
+*/
+facetT *qh_makenew_simplicial(facetT *visible, vertexT *apex, int *numnew) {
+ facetT *neighbor, **neighborp, *newfacet= NULL;
+ setT *vertices;
+ boolT flip, toporient;
+ int horizonskip, visibleskip;
+
+ FOREACHneighbor_(visible) {
+ if (!neighbor->seen && !neighbor->visible) {
+ vertices= qh_facetintersect(neighbor,visible, &horizonskip, &visibleskip, 1);
+ SETfirst_(vertices)= apex;
+ flip= ((horizonskip & 0x1) ^ (visibleskip & 0x1));
+ if (neighbor->toporient)
+ toporient= horizonskip & 0x1;
+ else
+ toporient= (horizonskip & 0x1) ^ 0x1;
+ newfacet= qh_makenewfacet(vertices, toporient, neighbor);
+ (*numnew)++;
+ if (neighbor->coplanar && (qh PREmerge || qh MERGEexact)) {
+#ifndef qh_NOmerge
+ newfacet->f.samecycle= newfacet;
+ newfacet->mergehorizon= True;
+#endif
+ }
+ if (!qh ONLYgood)
+ SETelem_(neighbor->neighbors, horizonskip)= newfacet;
+ trace4((qh ferr, 4049, "qh_makenew_simplicial: create facet f%d top %d from v%d and horizon f%d skip %d top %d and visible f%d skip %d, flip? %d\n",
+ newfacet->id, toporient, apex->id, neighbor->id, horizonskip,
+ neighbor->toporient, visible->id, visibleskip, flip));
+ }
+ }
+ return newfacet;
+} /* makenew_simplicial */
+
+/*---------------------------------
+
+ qh_matchneighbor( newfacet, newskip, hashsize, hashcount )
+ either match subridge of newfacet with neighbor or add to hash_table
+
+ returns:
+ duplicate ridges are unmatched and marked by qh_DUPLICATEridge
+
+ notes:
+ ridge is newfacet->vertices w/o newskip vertex
+ do not allocate memory (need to free hash_table cleanly)
+ uses linear hash chains
+
+ see also:
+ qh_matchduplicates
+
+ design:
+ for each possible matching facet in qh.hash_table
+ if vertices match
+ set ismatch, if facets have opposite orientation
+ if ismatch and matching facet doesn't have a match
+ match the facets by updating their neighbor sets
+ else
+ indicate a duplicate ridge
+ set facet hyperplane for later testing
+ add facet to hashtable
+ unless the other facet was already a duplicate ridge
+ mark both facets with a duplicate ridge
+ add other facet (if defined) to hash table
+*/
+void qh_matchneighbor(facetT *newfacet, int newskip, int hashsize, int *hashcount) {
+ boolT newfound= False; /* True, if new facet is already in hash chain */
+ boolT same, ismatch;
+ int hash, scan;
+ facetT *facet, *matchfacet;
+ int skip, matchskip;
+
+ hash= qh_gethash(hashsize, newfacet->vertices, qh hull_dim, 1,
+ SETelem_(newfacet->vertices, newskip));
+ trace4((qh ferr, 4050, "qh_matchneighbor: newfacet f%d skip %d hash %d hashcount %d\n",
+ newfacet->id, newskip, hash, *hashcount));
+ zinc_(Zhashlookup);
+ for (scan= hash; (facet= SETelemt_(qh hash_table, scan, facetT));
+ scan= (++scan >= hashsize ? 0 : scan)) {
+ if (facet == newfacet) {
+ newfound= True;
+ continue;
+ }
+ zinc_(Zhashtests);
+ if (qh_matchvertices(1, newfacet->vertices, newskip, facet->vertices, &skip, &same)) {
+ if (SETelem_(newfacet->vertices, newskip) ==
+ SETelem_(facet->vertices, skip)) {
+ qh_precision("two facets with the same vertices");
+ qh_fprintf(qh ferr, 6106, "qhull precision error: Vertex sets are the same for f%d and f%d. Can not force output.\n",
+ facet->id, newfacet->id);
+ qh_errexit2 (qh_ERRprec, facet, newfacet);
+ }
+ ismatch= (same == (boolT)((newfacet->toporient ^ facet->toporient)));
+ matchfacet= SETelemt_(facet->neighbors, skip, facetT);
+ if (ismatch && !matchfacet) {
+ SETelem_(facet->neighbors, skip)= newfacet;
+ SETelem_(newfacet->neighbors, newskip)= facet;
+ (*hashcount)--;
+ trace4((qh ferr, 4051, "qh_matchneighbor: f%d skip %d matched with new f%d skip %d\n",
+ facet->id, skip, newfacet->id, newskip));
+ return;
+ }
+ if (!qh PREmerge && !qh MERGEexact) {
+ qh_precision("a ridge with more than two neighbors");
+ qh_fprintf(qh ferr, 6107, "qhull precision error: facets f%d, f%d and f%d meet at a ridge with more than 2 neighbors. Can not continue.\n",
+ facet->id, newfacet->id, getid_(matchfacet));
+ qh_errexit2 (qh_ERRprec, facet, newfacet);
+ }
+ SETelem_(newfacet->neighbors, newskip)= qh_DUPLICATEridge;
+ newfacet->dupridge= True;
+ if (!newfacet->normal)
+ qh_setfacetplane(newfacet);
+ qh_addhash(newfacet, qh hash_table, hashsize, hash);
+ (*hashcount)++;
+ if (!facet->normal)
+ qh_setfacetplane(facet);
+ if (matchfacet != qh_DUPLICATEridge) {
+ SETelem_(facet->neighbors, skip)= qh_DUPLICATEridge;
+ facet->dupridge= True;
+ if (!facet->normal)
+ qh_setfacetplane(facet);
+ if (matchfacet) {
+ matchskip= qh_setindex(matchfacet->neighbors, facet);
+ SETelem_(matchfacet->neighbors, matchskip)= qh_DUPLICATEridge;
+ matchfacet->dupridge= True;
+ if (!matchfacet->normal)
+ qh_setfacetplane(matchfacet);
+ qh_addhash(matchfacet, qh hash_table, hashsize, hash);
+ *hashcount += 2;
+ }
+ }
+ trace4((qh ferr, 4052, "qh_matchneighbor: new f%d skip %d duplicates ridge for f%d skip %d matching f%d ismatch %d at hash %d\n",
+ newfacet->id, newskip, facet->id, skip,
+ (matchfacet == qh_DUPLICATEridge ? -2 : getid_(matchfacet)),
+ ismatch, hash));
+ return; /* end of duplicate ridge */
+ }
+ }
+ if (!newfound)
+ SETelem_(qh hash_table, scan)= newfacet; /* same as qh_addhash */
+ (*hashcount)++;
+ trace4((qh ferr, 4053, "qh_matchneighbor: no match for f%d skip %d at hash %d\n",
+ newfacet->id, newskip, hash));
+} /* matchneighbor */
+
+
+/*---------------------------------
+
+ qh_matchnewfacets()
+ match newfacets in qh.newfacet_list to their newfacet neighbors
+
+ returns:
+ qh.newfacet_list with full neighbor sets
+ get vertices with nth neighbor by deleting nth vertex
+ if qh.PREmerge/MERGEexact or qh.FORCEoutput
+ sets facet->flippped if flipped normal (also prevents point partitioning)
+ if duplicate ridges and qh.PREmerge/MERGEexact
+ sets facet->dupridge
+ missing neighbor links identifies extra ridges to be merging (qh_MERGEridge)
+
+ notes:
+ newfacets already have neighbor[0] (horizon facet)
+ assumes qh.hash_table is NULL
+ vertex->neighbors has not been updated yet
+ do not allocate memory after qh.hash_table (need to free it cleanly)
+
+ design:
+ delete neighbor sets for all new facets
+ initialize a hash table
+ for all new facets
+ match facet with neighbors
+ if unmatched facets (due to duplicate ridges)
+ for each new facet with a duplicate ridge
+ match it with a facet
+ check for flipped facets
+*/
+void qh_matchnewfacets(void /* qh newfacet_list */) {
+ int numnew=0, hashcount=0, newskip;
+ facetT *newfacet, *neighbor;
+ int dim= qh hull_dim, hashsize, neighbor_i, neighbor_n;
+ setT *neighbors;
+#ifndef qh_NOtrace
+ int facet_i, facet_n, numfree= 0;
+ facetT *facet;
+#endif
+
+ trace1((qh ferr, 1019, "qh_matchnewfacets: match neighbors for new facets.\n"));
+ FORALLnew_facets {
+ numnew++;
+ { /* inline qh_setzero(newfacet->neighbors, 1, qh hull_dim); */
+ neighbors= newfacet->neighbors;
+ neighbors->e[neighbors->maxsize].i= dim+1; /*may be overwritten*/
+ memset((char *)SETelemaddr_(neighbors, 1, void), 0, dim * SETelemsize);
+ }
+ }
+
+ qh_newhashtable(numnew*(qh hull_dim-1)); /* twice what is normally needed,
+ but every ridge could be DUPLICATEridge */
+ hashsize= qh_setsize(qh hash_table);
+ FORALLnew_facets {
+ for (newskip=1; newskip
---------------------------------
+
+ poly.h
+ header file for poly.c and poly2.c
+
+ see qh-poly.htm, libqhull.h and poly.c
+
+ Copyright (c) 1993-2012 The Geometry Center.
+ $Id: //main/2011/qhull/src/libqhullr/poly_r.h#1 $$Change: 1640 $
+ $DateTime: 2014/01/15 09:12:08 $$Author: bbarber $
+*/
+
+#ifndef qhDEFpoly
+#define qhDEFpoly 1
+
+#include "libqhull.h"
+
+/*=============== constants ========================== */
+
+/*----------------------------------
+
+ ALGORITHMfault
+ use as argument to checkconvex() to report errors during buildhull
+*/
+#define qh_ALGORITHMfault 0
+
+/*----------------------------------
+
+ DATAfault
+ use as argument to checkconvex() to report errors during initialhull
+*/
+#define qh_DATAfault 1
+
+/*----------------------------------
+
+ DUPLICATEridge
+ special value for facet->neighbor to indicate a duplicate ridge
+
+ notes:
+ set by matchneighbor, used by matchmatch and mark_dupridge
+*/
+#define qh_DUPLICATEridge (facetT *)1L
+
+/*----------------------------------
+
+ MERGEridge flag in facet
+ special value for facet->neighbor to indicate a merged ridge
+
+ notes:
+ set by matchneighbor, used by matchmatch and mark_dupridge
+*/
+#define qh_MERGEridge (facetT *)2L
+
+
+/*============ -structures- ====================*/
+
+/*=========== -macros- =========================*/
+
+/*----------------------------------
+
+ FORALLfacet_( facetlist ) { ... }
+ assign 'facet' to each facet in facetlist
+
+ notes:
+ uses 'facetT *facet;'
+ assumes last facet is a sentinel
+
+ see:
+ FORALLfacets
+*/
+#define FORALLfacet_( facetlist ) if (facetlist ) for ( facet=( facetlist ); facet && facet->next; facet= facet->next )
+
+/*----------------------------------
+
+ FORALLnew_facets { ... }
+ assign 'newfacet' to each facet in qh.newfacet_list
+
+ notes:
+ uses 'facetT *newfacet;'
+ at exit, newfacet==NULL
+*/
+#define FORALLnew_facets for ( newfacet=qh newfacet_list;newfacet && newfacet->next;newfacet=newfacet->next )
+
+/*----------------------------------
+
+ FORALLvertex_( vertexlist ) { ... }
+ assign 'vertex' to each vertex in vertexlist
+
+ notes:
+ uses 'vertexT *vertex;'
+ at exit, vertex==NULL
+*/
+#define FORALLvertex_( vertexlist ) for (vertex=( vertexlist );vertex && vertex->next;vertex= vertex->next )
+
+/*----------------------------------
+
+ FORALLvisible_facets { ... }
+ assign 'visible' to each visible facet in qh.visible_list
+
+ notes:
+ uses 'vacetT *visible;'
+ at exit, visible==NULL
+*/
+#define FORALLvisible_facets for (visible=qh visible_list; visible && visible->visible; visible= visible->next)
+
+/*----------------------------------
+
+ FORALLsame_( newfacet ) { ... }
+ assign 'same' to each facet in newfacet->f.samecycle
+
+ notes:
+ uses 'facetT *same;'
+ stops when it returns to newfacet
+*/
+#define FORALLsame_(newfacet) for (same= newfacet->f.samecycle; same != newfacet; same= same->f.samecycle)
+
+/*----------------------------------
+
+ FORALLsame_cycle_( newfacet ) { ... }
+ assign 'same' to each facet in newfacet->f.samecycle
+
+ notes:
+ uses 'facetT *same;'
+ at exit, same == NULL
+*/
+#define FORALLsame_cycle_(newfacet) \
+ for (same= newfacet->f.samecycle; \
+ same; same= (same == newfacet ? NULL : same->f.samecycle))
+
+/*----------------------------------
+
+ FOREACHneighborA_( facet ) { ... }
+ assign 'neighborA' to each neighbor in facet->neighbors
+
+ FOREACHneighborA_( vertex ) { ... }
+ assign 'neighborA' to each neighbor in vertex->neighbors
+
+ declare:
+ facetT *neighborA, **neighborAp;
+
+ see:
+ FOREACHsetelement_
+*/
+#define FOREACHneighborA_(facet) FOREACHsetelement_(facetT, facet->neighbors, neighborA)
+
+/*----------------------------------
+
+ FOREACHvisible_( facets ) { ... }
+ assign 'visible' to each facet in facets
+
+ notes:
+ uses 'facetT *facet, *facetp;'
+ see FOREACHsetelement_
+*/
+#define FOREACHvisible_(facets) FOREACHsetelement_(facetT, facets, visible)
+
+/*----------------------------------
+
+ FOREACHnewfacet_( facets ) { ... }
+ assign 'newfacet' to each facet in facets
+
+ notes:
+ uses 'facetT *newfacet, *newfacetp;'
+ see FOREACHsetelement_
+*/
+#define FOREACHnewfacet_(facets) FOREACHsetelement_(facetT, facets, newfacet)
+
+/*----------------------------------
+
+ FOREACHvertexA_( vertices ) { ... }
+ assign 'vertexA' to each vertex in vertices
+
+ notes:
+ uses 'vertexT *vertexA, *vertexAp;'
+ see FOREACHsetelement_
+*/
+#define FOREACHvertexA_(vertices) FOREACHsetelement_(vertexT, vertices, vertexA)
+
+/*----------------------------------
+
+ FOREACHvertexreverse12_( vertices ) { ... }
+ assign 'vertex' to each vertex in vertices
+ reverse order of first two vertices
+
+ notes:
+ uses 'vertexT *vertex, *vertexp;'
+ see FOREACHsetelement_
+*/
+#define FOREACHvertexreverse12_(vertices) FOREACHsetelementreverse12_(vertexT, vertices, vertex)
+
+
+/*=============== prototypes poly.c in alphabetical order ================*/
+
+void qh_appendfacet(facetT *facet);
+void qh_appendvertex(vertexT *vertex);
+void qh_attachnewfacets(void);
+boolT qh_checkflipped(facetT *facet, realT *dist, boolT allerror);
+void qh_delfacet(facetT *facet);
+void qh_deletevisible(void /*qh visible_list, qh horizon_list*/);
+setT *qh_facetintersect(facetT *facetA, facetT *facetB, int *skipAp,int *skipBp, int extra);
+int qh_gethash(int hashsize, setT *set, int size, int firstindex, void *skipelem);
+facetT *qh_makenewfacet(setT *vertices, boolT toporient, facetT *facet);
+void qh_makenewplanes(void /* newfacet_list */);
+facetT *qh_makenew_nonsimplicial(facetT *visible, vertexT *apex, int *numnew);
+facetT *qh_makenew_simplicial(facetT *visible, vertexT *apex, int *numnew);
+void qh_matchneighbor(facetT *newfacet, int newskip, int hashsize,
+ int *hashcount);
+void qh_matchnewfacets(void);
+boolT qh_matchvertices(int firstindex, setT *verticesA, int skipA,
+ setT *verticesB, int *skipB, boolT *same);
+facetT *qh_newfacet(void);
+ridgeT *qh_newridge(void);
+int qh_pointid(pointT *point);
+void qh_removefacet(facetT *facet);
+void qh_removevertex(vertexT *vertex);
+void qh_updatevertices(void);
+
+
+/*========== -prototypes poly2.c in alphabetical order ===========*/
+
+void qh_addhash(void* newelem, setT *hashtable, int hashsize, int hash);
+void qh_check_bestdist(void);
+void qh_check_maxout(void);
+void qh_check_output(void);
+void qh_check_point(pointT *point, facetT *facet, realT *maxoutside, realT *maxdist, facetT **errfacet1, facetT **errfacet2);
+void qh_check_points(void);
+void qh_checkconvex(facetT *facetlist, int fault);
+void qh_checkfacet(facetT *facet, boolT newmerge, boolT *waserrorp);
+void qh_checkflipped_all(facetT *facetlist);
+void qh_checkpolygon(facetT *facetlist);
+void qh_checkvertex(vertexT *vertex);
+void qh_clearcenters(qh_CENTER type);
+void qh_createsimplex(setT *vertices);
+void qh_delridge(ridgeT *ridge);
+void qh_delvertex(vertexT *vertex);
+setT *qh_facet3vertex(facetT *facet);
+facetT *qh_findbestfacet(pointT *point, boolT bestoutside,
+ realT *bestdist, boolT *isoutside);
+facetT *qh_findbestlower(facetT *upperfacet, pointT *point, realT *bestdistp, int *numpart);
+facetT *qh_findfacet_all(pointT *point, realT *bestdist, boolT *isoutside,
+ int *numpart);
+int qh_findgood(facetT *facetlist, int goodhorizon);
+void qh_findgood_all(facetT *facetlist);
+void qh_furthestnext(void /* qh facet_list */);
+void qh_furthestout(facetT *facet);
+void qh_infiniteloop(facetT *facet);
+void qh_initbuild(void);
+void qh_initialhull(setT *vertices);
+setT *qh_initialvertices(int dim, setT *maxpoints, pointT *points, int numpoints);
+vertexT *qh_isvertex(pointT *point, setT *vertices);
+vertexT *qh_makenewfacets(pointT *point /*horizon_list, visible_list*/);
+void qh_matchduplicates(facetT *atfacet, int atskip, int hashsize, int *hashcount);
+void qh_nearcoplanar(void /* qh.facet_list */);
+vertexT *qh_nearvertex(facetT *facet, pointT *point, realT *bestdistp);
+int qh_newhashtable(int newsize);
+vertexT *qh_newvertex(pointT *point);
+ridgeT *qh_nextridge3d(ridgeT *atridge, facetT *facet, vertexT **vertexp);
+void qh_outcoplanar(void /* facet_list */);
+pointT *qh_point(int id);
+void qh_point_add(setT *set, pointT *point, void *elem);
+setT *qh_pointfacet(void /*qh facet_list*/);
+setT *qh_pointvertex(void /*qh facet_list*/);
+void qh_prependfacet(facetT *facet, facetT **facetlist);
+void qh_printhashtable(FILE *fp);
+void qh_printlists(void);
+void qh_resetlists(boolT stats, boolT resetVisible /*qh newvertex_list newfacet_list visible_list*/);
+void qh_setvoronoi_all(void);
+void qh_triangulate(void /*qh facet_list*/);
+void qh_triangulate_facet(facetT *facetA, vertexT **first_vertex);
+void qh_triangulate_link(facetT *oldfacetA, facetT *facetA, facetT *oldfacetB, facetT *facetB);
+void qh_triangulate_mirror(facetT *facetA, facetT *facetB);
+void qh_triangulate_null(facetT *facetA);
+void qh_vertexintersect(setT **vertexsetA,setT *vertexsetB);
+setT *qh_vertexintersect_new(setT *vertexsetA,setT *vertexsetB);
+void qh_vertexneighbors(void /*qh facet_list*/);
+boolT qh_vertexsubset(setT *vertexsetA, setT *vertexsetB);
+
+
+#endif /* qhDEFpoly */
diff --git a/src/libqhullr/qhull-exports_r.def b/src/libqhullr/qhull-exports_r.def
new file mode 100644
index 0000000..8f9cca9
--- /dev/null
+++ b/src/libqhullr/qhull-exports_r.def
@@ -0,0 +1,412 @@
+; qhull-exports.def -- msvc module-definition file
+;
+; Generated from depends.exe by cut-and-paste of exported symbols by mingw gcc
+; [mar'11] 399 symbols
+; Annotate as DATA qh_qh qh_qhstat qhmem qhull_inuse rbox_inuse
+; Same as ../libqhullp/qhull_p-exports.def without qh_save_qhull and qh_restore_qhull
+;
+; $Id: //main/2011/qhull/src/libqhullr/qhull-exports_r.def#1 $$Change: 1640 $
+; $DateTime: 2014/01/15 09:12:08 $$Author: bbarber $
+;
+; Define qhull_VERSION in CMakeLists.txt, Makefile, qhull-exports.def, qhull_p-exports.def, and qhull-warn.pri
+VERSION 6.3
+EXPORTS
+dfacet
+dvertex
+out1
+out2n
+out3n
+qh_addhash
+qh_addpoint
+qh_all_merges
+qh_allstatA
+qh_allstatB
+qh_allstatC
+qh_allstatD
+qh_allstatE
+qh_allstatE2
+qh_allstatF
+qh_allstatG
+qh_allstatH
+qh_allstatI
+qh_allstatistics
+qh_appendfacet
+qh_appendmergeset
+qh_appendprint
+qh_appendvertex
+qh_argv_to_command
+qh_argv_to_command_size
+qh_attachnewfacets
+qh_backnormal
+qh_basevertices
+qh_build_withrestart
+qh_buildhull
+qh_buildtracing
+qh_check_bestdist
+qh_check_maxout
+qh_check_output
+qh_check_point
+qh_check_points
+qh_checkconnect
+qh_checkconvex
+qh_checkfacet
+qh_checkflags
+qh_checkflipped
+qh_checkflipped_all
+qh_checkpolygon
+qh_checkvertex
+qh_checkzero
+qh_clear_outputflags
+qh_clearcenters
+qh_clock
+qh_collectstatistics
+qh_compare_facetarea
+qh_compare_facetmerge
+qh_compare_facetvisit
+qh_compare_vertexpoint
+qh_compareangle
+qh_comparemerge
+qh_comparevisit
+qh_copyfilename
+qh_copynonconvex
+qh_copypoints
+qh_countfacets
+qh_createsimplex
+qh_crossproduct
+qh_degen_redundant_facet
+qh_degen_redundant_neighbors
+qh_deletevisible
+qh_delfacet
+qh_delridge
+qh_delvertex
+qh_determinant
+qh_detjoggle
+qh_detroundoff
+qh_detsimplex
+qh_detvnorm
+qh_detvridge
+qh_detvridge3
+qh_distnorm
+qh_distplane
+qh_distround
+qh_divzero
+qh_eachvoronoi
+qh_eachvoronoi_all
+qh_errexit
+qh_errexit2
+qh_errexit_rbox
+qh_errprint
+qh_exit
+qh_facet2point
+qh_facet3vertex
+qh_facetarea
+qh_facetarea_simplex
+qh_facetcenter
+qh_facetintersect
+qh_facetvertices
+qh_find_newvertex
+qh_findbest
+qh_findbest_test
+qh_findbestfacet
+qh_findbesthorizon
+qh_findbestlower
+qh_findbestneighbor
+qh_findbestnew
+qh_findfacet_all
+qh_findgood
+qh_findgood_all
+qh_findgooddist
+qh_findhorizon
+qh_flippedmerges
+qh_forcedmerges
+qh_fprintf
+qh_fprintf_rbox
+qh_free
+qh_freebuffers
+qh_freebuild
+qh_freeqhull
+qh_freeqhull2
+qh_freestatistics
+qh_furthestnext
+qh_furthestout
+qh_gausselim
+qh_geomplanes
+qh_getangle
+qh_getarea
+qh_getcenter
+qh_getcentrum
+qh_getdistance
+qh_gethash
+qh_getmergeset
+qh_getmergeset_initial
+qh_gram_schmidt
+qh_hashridge
+qh_hashridge_find
+qh_infiniteloop
+qh_init_A
+qh_init_B
+qh_init_qhull_command
+qh_initbuild
+qh_initflags
+qh_initialhull
+qh_initialvertices
+qh_initqhull_buffers
+qh_initqhull_globals
+qh_initqhull_mem
+qh_initqhull_outputflags
+qh_initqhull_start
+qh_initqhull_start2
+qh_initstatistics
+qh_initthresholds
+qh_inthresholds
+qh_isvertex
+qh_joggleinput
+qh_makenew_nonsimplicial
+qh_makenew_simplicial
+qh_makenewfacet
+qh_makenewfacets
+qh_makenewplanes
+qh_makeridges
+qh_malloc
+qh_mark_dupridges
+qh_markkeep
+qh_markvoronoi
+qh_matchduplicates
+qh_matchneighbor
+qh_matchnewfacets
+qh_matchvertices
+qh_maxabsval
+qh_maxmin
+qh_maxouter
+qh_maxsimplex
+qh_maydropneighbor
+qh_memalloc
+qh_memfree
+qh_memfreeshort
+qh_meminit
+qh_meminitbuffers
+qh_memsetup
+qh_memsize
+qh_memstatistics
+qh_memtotal
+qh_merge_degenredundant
+qh_merge_nonconvex
+qh_mergecycle
+qh_mergecycle_all
+qh_mergecycle_facets
+qh_mergecycle_neighbors
+qh_mergecycle_ridges
+qh_mergecycle_vneighbors
+qh_mergefacet
+qh_mergefacet2d
+qh_mergeneighbors
+qh_mergeridges
+qh_mergesimplex
+qh_mergevertex_del
+qh_mergevertex_neighbors
+qh_mergevertices
+qh_minabsval
+qh_mindiff
+qh_nearcoplanar
+qh_nearvertex
+qh_neighbor_intersections
+qh_new_qhull
+qh_newfacet
+qh_newhashtable
+qh_newridge
+qh_newstats
+qh_newvertex
+qh_newvertices
+qh_nextfurthest
+qh_nextridge3d
+qh_normalize
+qh_normalize2
+qh_nostatistic
+qh_option
+qh_order_vertexneighbors
+qh_orientoutside
+qh_outcoplanar
+qh_outerinner
+qh_partitionall
+qh_partitioncoplanar
+qh_partitionpoint
+qh_partitionvisible
+qh_point
+qh_point_add
+qh_pointdist
+qh_pointfacet
+qh_pointid
+qh_pointvertex
+qh_postmerge
+qh_precision
+qh_premerge
+qh_prepare_output
+qh_prependfacet
+qh_printafacet
+qh_printallstatistics
+qh_printbegin
+qh_printcenter
+qh_printcentrum
+qh_printend
+qh_printend4geom
+qh_printextremes
+qh_printextremes_2d
+qh_printextremes_d
+qh_printfacet
+qh_printfacet2geom
+qh_printfacet2geom_points
+qh_printfacet2math
+qh_printfacet3geom_nonsimplicial
+qh_printfacet3geom_points
+qh_printfacet3geom_simplicial
+qh_printfacet3math
+qh_printfacet3vertex
+qh_printfacet4geom_nonsimplicial
+qh_printfacet4geom_simplicial
+qh_printfacetNvertex_nonsimplicial
+qh_printfacetNvertex_simplicial
+qh_printfacetheader
+qh_printfacetlist
+qh_printfacetridges
+qh_printfacets
+qh_printhashtable
+qh_printhelp_degenerate
+qh_printhelp_narrowhull
+qh_printhelp_singular
+qh_printhyperplaneintersection
+qh_printline3geom
+qh_printlists
+qh_printmatrix
+qh_printneighborhood
+qh_printpoint
+qh_printpoint3
+qh_printpointid
+qh_printpoints
+qh_printpoints_out
+qh_printpointvect
+qh_printpointvect2
+qh_printridge
+qh_printspheres
+qh_printstatistics
+qh_printstatlevel
+qh_printstats
+qh_printsummary
+qh_printvdiagram
+qh_printvdiagram2
+qh_printvertex
+qh_printvertexlist
+qh_printvertices
+qh_printvneighbors
+qh_printvnorm
+qh_printvoronoi
+qh_printvridge
+qh_produce_output
+qh_produce_output2
+qh_projectdim3
+qh_projectinput
+qh_projectpoint
+qh_projectpoints
+; Mark as DATA, otherwise links a separate qh_qh. Requires __dcelspec
+qh_qh DATA
+qh_qhstat DATA
+qh_qhull
+qh_rand
+qh_rand_seed
+qh_randomfactor
+qh_randommatrix
+qh_rboxpoints
+qh_readfeasible
+qh_readpoints
+qh_reducevertices
+qh_redundant_vertex
+qh_remove_extravertices
+qh_removefacet
+qh_removevertex
+qh_rename_sharedvertex
+qh_renameridgevertex
+qh_renamevertex
+qh_resetlists
+qh_rotateinput
+qh_rotatepoints
+qh_scaleinput
+qh_scalelast
+qh_scalepoints
+qh_setaddnth
+qh_setaddsorted
+qh_setappend
+qh_setappend2ndlast
+qh_setappend_set
+qh_setcheck
+qh_setcompact
+qh_setcopy
+qh_setdel
+qh_setdelaunay
+qh_setdellast
+qh_setdelnth
+qh_setdelnthsorted
+qh_setdelsorted
+qh_setduplicate
+qh_setequal
+qh_setequal_except
+qh_setequal_skip
+qh_setfacetplane
+qh_setfeasible
+qh_setfree
+qh_setfree2
+qh_setfreelong
+qh_sethalfspace
+qh_sethalfspace_all
+qh_sethyperplane_det
+qh_sethyperplane_gauss
+qh_setin
+qh_setindex
+qh_setlarger
+qh_setlast
+qh_setnew
+qh_setnew_delnthsorted
+qh_setprint
+qh_setreplace
+qh_setsize
+qh_settemp
+qh_settempfree
+qh_settempfree_all
+qh_settemppop
+qh_settemppush
+qh_settruncate
+qh_setunique
+qh_setvoronoi_all
+qh_setzero
+qh_sharpnewfacets
+qh_skipfacet
+qh_skipfilename
+qh_srand
+qh_stddev
+qh_strtod
+qh_strtol
+qh_test_appendmerge
+qh_test_vneighbors
+qh_tracemerge
+qh_tracemerging
+qh_triangulate
+qh_triangulate_facet
+qh_triangulate_link
+qh_triangulate_mirror
+qh_triangulate_null
+qh_updatetested
+qh_updatevertices
+qh_user_memsizes
+qh_version
+qh_vertexintersect
+qh_vertexintersect_new
+qh_vertexneighbors
+qh_vertexridges
+qh_vertexridges_facet
+qh_vertexsubset
+qh_voronoi_center
+qh_willdelete
+; Mark as DATA, otherwise links a separate qhmem. Requires __dcelspec
+qhmem DATA
+qhull_inuse DATA
+rbox
+rbox_inuse DATA
+roundi
diff --git a/src/libqhullr/qhull_a_r.h b/src/libqhullr/qhull_a_r.h
new file mode 100644
index 0000000..207dba9
--- /dev/null
+++ b/src/libqhullr/qhull_a_r.h
@@ -0,0 +1,151 @@
+/*
---------------------------------
+
+ qhull_a.h
+ all header files for compiling qhull
+
+ see qh-qhull.htm
+
+ see libqhull.h for user-level definitions
+
+ see user.h for user-defineable constants
+
+ defines internal functions for libqhull.c global.c
+
+ Copyright (c) 1993-2012 The Geometry Center.
+ $Id: //main/2011/qhull/src/libqhullr/qhull_a_r.h#1 $$Change: 1640 $
+ $DateTime: 2014/01/15 09:12:08 $$Author: bbarber $
+
+ Notes: grep for ((" and (" to catch fprintf("lkasdjf");
+ full parens around (x?y:z)
+ use '#include qhull/qhull_a.h' to avoid name clashes
+*/
+
+#ifndef qhDEFqhulla
+#define qhDEFqhulla 1
+
+#include "libqhull.h" /* Defines data types */
+
+#include "stat.h"
+#include "random.h"
+#include "mem.h"
+#include "qset.h"
+#include "geom.h"
+#include "merge.h"
+#include "poly.h"
+#include "io.h"
+
+#include
---------------------------------
+
+ qset.c
+ implements set manipulations needed for quickhull
+
+ see qh-set.htm and qset.h
+
+ Be careful of strict aliasing (two pointers of different types
+ that reference the same location). The last slot of a set is
+ either the actual size of the set plus 1, or the NULL terminator
+ of the set (i.e., setelemT).
+
+ Copyright (c) 1993-2012 The Geometry Center.
+ $Id: //main/2011/qhull/src/libqhullr/qset_r.c#1 $$Change: 1640 $
+ $DateTime: 2014/01/15 09:12:08 $$Author: bbarber $
+*/
+
+#include "qset.h"
+#include "mem.h"
+#include
---------------------------------
+
+ qset.h
+ header file for qset.c that implements set
+
+ see qh-set.htm and qset.c
+
+ only uses mem.c, malloc/free
+
+ for error handling, writes message and calls
+ qh_errexit(qhmem_ERRqhull, NULL, NULL);
+
+ set operations satisfy the following properties:
+ - sets have a max size, the actual size (if different) is stored at the end
+ - every set is NULL terminated
+ - sets may be sorted or unsorted, the caller must distinguish this
+
+ Copyright (c) 1993-2012 The Geometry Center.
+ $Id: //main/2011/qhull/src/libqhullr/qset_r.h#1 $$Change: 1640 $
+ $DateTime: 2014/01/15 09:12:08 $$Author: bbarber $
+*/
+
+#ifndef qhDEFset
+#define qhDEFset 1
+
+#include
---------------------------------
+
+ random.c -- utilities
+ Park & Miller's minimimal standard random number generator
+ argc/argv conversion
+
+ Used by rbox. Do not use 'qh'
+*/
+
+#include "libqhull.h"
+#include
---------------------------------
+
+ random.h
+ header file for random routines
+
+ see qh-geom.htm and random.c
+
+ Copyright (c) 1993-2012 The Geometry Center.
+ $Id: //main/2011/qhull/src/libqhullr/random_r.h#1 $$Change: 1640 $
+ $DateTime: 2014/01/15 09:12:08 $$Author: bbarber $
+*/
+
+#ifndef qhDEFrandom
+#define qhDEFrandom 1
+
+#include "libqhull.h"
+
+/*============= prototypes in alphabetical order ======= */
+
+
+int qh_argv_to_command(int argc, char *argv[], char* command, int max_size);
+int qh_argv_to_command_size(int argc, char *argv[]);
+int qh_rand( void);
+void qh_srand( int seed);
+realT qh_randomfactor(realT scale, realT offset);
+void qh_randommatrix(realT *buffer, int dim, realT **row);
+int qh_strtol(const char *s, char **endp);
+double qh_strtod(const char *s, char **endp);
+
+#endif /* qhDEFrandom */
+
+
+
diff --git a/src/libqhullr/rboxlib_r.c b/src/libqhullr/rboxlib_r.c
new file mode 100644
index 0000000..0fafeec
--- /dev/null
+++ b/src/libqhullr/rboxlib_r.c
@@ -0,0 +1,795 @@
+/*
---------------------------------
+
+ rboxlib.c
+ Generate input points
+
+ notes:
+ For documentation, see prompt[] of rbox.c
+ 50 points generated for 'rbox D4'
+
+ WARNING:
+ incorrect range if qh_RANDOMmax is defined wrong (user.h)
+*/
+
+#include "random.h"
+#include "libqhull.h"
+
+#include
---------------------------------
+
+ stat.c
+ contains all statistics that are collected for qhull
+
+ see qh-stat.htm and stat.h
+
+ Copyright (c) 1993-2012 The Geometry Center.
+ $Id: //main/2011/qhull/src/libqhullr/stat_r.c#1 $$Change: 1640 $
+ $DateTime: 2014/01/15 09:12:08 $$Author: bbarber $
+*/
+
+#include "qhull_a.h"
+
+/*============ global data structure ==========*/
+
+#if qh_QHpointer
+qhstatT *qh_qhstat=NULL; /* global data structure */
+#else
+qhstatT qh_qhstat; /* add "={0}" if this causes a compiler error */
+#endif
+
+/*========== functions in alphabetic order ================*/
+
+/*---------------------------------
+
+ qh_allstatA()
+ define statistics in groups of 20
+
+ notes:
+ (otherwise, 'gcc -O2' uses too much memory)
+ uses qhstat.next
+*/
+void qh_allstatA(void) {
+
+ /* zdef_(type,name,doc,average) */
+ zzdef_(zdoc, Zdoc2, "precision statistics", -1);
+ zdef_(zinc, Znewvertex, NULL, -1);
+ zdef_(wadd, Wnewvertex, "ave. distance of a new vertex to a facet(!0s)", Znewvertex);
+ zzdef_(wmax, Wnewvertexmax, "max. distance of a new vertex to a facet", -1);
+ zdef_(wmax, Wvertexmax, "max. distance of an output vertex to a facet", -1);
+ zdef_(wmin, Wvertexmin, "min. distance of an output vertex to a facet", -1);
+ zdef_(wmin, Wmindenom, "min. denominator in hyperplane computation", -1);
+
+ qhstat precision= qhstat next; /* call qh_precision for each of these */
+ zzdef_(zdoc, Zdoc3, "precision problems (corrected unless 'Q0' or an error)", -1);
+ zzdef_(zinc, Zcoplanarridges, "coplanar half ridges in output", -1);
+ zzdef_(zinc, Zconcaveridges, "concave half ridges in output", -1);
+ zzdef_(zinc, Zflippedfacets, "flipped facets", -1);
+ zzdef_(zinc, Zcoplanarhorizon, "coplanar horizon facets for new vertices", -1);
+ zzdef_(zinc, Zcoplanarpart, "coplanar points during partitioning", -1);
+ zzdef_(zinc, Zminnorm, "degenerate hyperplanes recomputed with gaussian elimination", -1);
+ zzdef_(zinc, Znearlysingular, "nearly singular or axis-parallel hyperplanes", -1);
+ zzdef_(zinc, Zback0, "zero divisors during back substitute", -1);
+ zzdef_(zinc, Zgauss0, "zero divisors during gaussian elimination", -1);
+ zzdef_(zinc, Zmultiridge, "ridges with multiple neighbors", -1);
+}
+void qh_allstatB(void) {
+ zzdef_(zdoc, Zdoc1, "summary information", -1);
+ zdef_(zinc, Zvertices, "number of vertices in output", -1);
+ zdef_(zinc, Znumfacets, "number of facets in output", -1);
+ zdef_(zinc, Znonsimplicial, "number of non-simplicial facets in output", -1);
+ zdef_(zinc, Znowsimplicial, "number of simplicial facets that were merged", -1);
+ zdef_(zinc, Znumridges, "number of ridges in output", -1);
+ zdef_(zadd, Znumridges, "average number of ridges per facet", Znumfacets);
+ zdef_(zmax, Zmaxridges, "maximum number of ridges", -1);
+ zdef_(zadd, Znumneighbors, "average number of neighbors per facet", Znumfacets);
+ zdef_(zmax, Zmaxneighbors, "maximum number of neighbors", -1);
+ zdef_(zadd, Znumvertices, "average number of vertices per facet", Znumfacets);
+ zdef_(zmax, Zmaxvertices, "maximum number of vertices", -1);
+ zdef_(zadd, Znumvneighbors, "average number of neighbors per vertex", Zvertices);
+ zdef_(zmax, Zmaxvneighbors, "maximum number of neighbors", -1);
+ zdef_(wadd, Wcpu, "cpu seconds for qhull after input", -1);
+ zdef_(zinc, Ztotvertices, "vertices created altogether", -1);
+ zzdef_(zinc, Zsetplane, "facets created altogether", -1);
+ zdef_(zinc, Ztotridges, "ridges created altogether", -1);
+ zdef_(zinc, Zpostfacets, "facets before post merge", -1);
+ zdef_(zadd, Znummergetot, "average merges per facet(at most 511)", Znumfacets);
+ zdef_(zmax, Znummergemax, " maximum merges for a facet(at most 511)", -1);
+ zdef_(zinc, Zangle, NULL, -1);
+ zdef_(wadd, Wangle, "average angle(cosine) of facet normals for all ridges", Zangle);
+ zdef_(wmax, Wanglemax, " maximum angle(cosine) of facet normals across a ridge", -1);
+ zdef_(wmin, Wanglemin, " minimum angle(cosine) of facet normals across a ridge", -1);
+ zdef_(wadd, Wareatot, "total area of facets", -1);
+ zdef_(wmax, Wareamax, " maximum facet area", -1);
+ zdef_(wmin, Wareamin, " minimum facet area", -1);
+}
+void qh_allstatC(void) {
+ zdef_(zdoc, Zdoc9, "build hull statistics", -1);
+ zzdef_(zinc, Zprocessed, "points processed", -1);
+ zzdef_(zinc, Zretry, "retries due to precision problems", -1);
+ zdef_(wmax, Wretrymax, " max. random joggle", -1);
+ zdef_(zmax, Zmaxvertex, "max. vertices at any one time", -1);
+ zdef_(zinc, Ztotvisible, "ave. visible facets per iteration", Zprocessed);
+ zdef_(zinc, Zinsidevisible, " ave. visible facets without an horizon neighbor", Zprocessed);
+ zdef_(zadd, Zvisfacettot, " ave. facets deleted per iteration", Zprocessed);
+ zdef_(zmax, Zvisfacetmax, " maximum", -1);
+ zdef_(zadd, Zvisvertextot, "ave. visible vertices per iteration", Zprocessed);
+ zdef_(zmax, Zvisvertexmax, " maximum", -1);
+ zdef_(zinc, Ztothorizon, "ave. horizon facets per iteration", Zprocessed);
+ zdef_(zadd, Znewfacettot, "ave. new or merged facets per iteration", Zprocessed);
+ zdef_(zmax, Znewfacetmax, " maximum(includes initial simplex)", -1);
+ zdef_(wadd, Wnewbalance, "average new facet balance", Zprocessed);
+ zdef_(wadd, Wnewbalance2, " standard deviation", -1);
+ zdef_(wadd, Wpbalance, "average partition balance", Zpbalance);
+ zdef_(wadd, Wpbalance2, " standard deviation", -1);
+ zdef_(zinc, Zpbalance, " number of trials", -1);
+ zdef_(zinc, Zsearchpoints, "searches of all points for initial simplex", -1);
+ zdef_(zinc, Zdetsimplex, "determinants computed(area & initial hull)", -1);
+ zdef_(zinc, Znoarea, "determinants not computed because vertex too low", -1);
+ zdef_(zinc, Znotmax, "points ignored(!above max_outside)", -1);
+ zdef_(zinc, Znotgood, "points ignored(!above a good facet)", -1);
+ zdef_(zinc, Znotgoodnew, "points ignored(didn't create a good new facet)", -1);
+ zdef_(zinc, Zgoodfacet, "good facets found", -1);
+ zzdef_(zinc, Znumvisibility, "distance tests for facet visibility", -1);
+ zdef_(zinc, Zdistvertex, "distance tests to report minimum vertex", -1);
+ zzdef_(zinc, Ztotcheck, "points checked for facets' outer planes", -1);
+ zzdef_(zinc, Zcheckpart, " ave. distance tests per check", Ztotcheck);
+}
+void qh_allstatD(void) {
+ zdef_(zinc, Zvisit, "resets of visit_id", -1);
+ zdef_(zinc, Zvvisit, " resets of vertex_visit", -1);
+ zdef_(zmax, Zvisit2max, " max visit_id/2", -1);
+ zdef_(zmax, Zvvisit2max, " max vertex_visit/2", -1);
+
+ zdef_(zdoc, Zdoc4, "partitioning statistics(see previous for outer planes)", -1);
+ zzdef_(zadd, Zdelvertextot, "total vertices deleted", -1);
+ zdef_(zmax, Zdelvertexmax, " maximum vertices deleted per iteration", -1);
+ zdef_(zinc, Zfindbest, "calls to findbest", -1);
+ zdef_(zadd, Zfindbesttot, " ave. facets tested", Zfindbest);
+ zdef_(zmax, Zfindbestmax, " max. facets tested", -1);
+ zdef_(zadd, Zfindcoplanar, " ave. coplanar search", Zfindbest);
+ zdef_(zinc, Zfindnew, "calls to findbestnew", -1);
+ zdef_(zadd, Zfindnewtot, " ave. facets tested", Zfindnew);
+ zdef_(zmax, Zfindnewmax, " max. facets tested", -1);
+ zdef_(zinc, Zfindnewjump, " ave. clearly better", Zfindnew);
+ zdef_(zinc, Zfindnewsharp, " calls due to qh_sharpnewfacets", -1);
+ zdef_(zinc, Zfindhorizon, "calls to findhorizon", -1);
+ zdef_(zadd, Zfindhorizontot, " ave. facets tested", Zfindhorizon);
+ zdef_(zmax, Zfindhorizonmax, " max. facets tested", -1);
+ zdef_(zinc, Zfindjump, " ave. clearly better", Zfindhorizon);
+ zdef_(zinc, Zparthorizon, " horizon facets better than bestfacet", -1);
+ zdef_(zinc, Zpartangle, "angle tests for repartitioned coplanar points", -1);
+ zdef_(zinc, Zpartflip, " repartitioned coplanar points for flipped orientation", -1);
+}
+void qh_allstatE(void) {
+ zdef_(zinc, Zpartinside, "inside points", -1);
+ zdef_(zinc, Zpartnear, " inside points kept with a facet", -1);
+ zdef_(zinc, Zcoplanarinside, " inside points that were coplanar with a facet", -1);
+ zdef_(zinc, Zbestlower, "calls to findbestlower", -1);
+ zdef_(zinc, Zbestlowerv, " with search of vertex neighbors", -1);
+ zdef_(wadd, Wmaxout, "difference in max_outside at final check", -1);
+ zzdef_(zinc, Zpartitionall, "distance tests for initial partition", -1);
+ zdef_(zinc, Ztotpartition, "partitions of a point", -1);
+ zzdef_(zinc, Zpartition, "distance tests for partitioning", -1);
+ zzdef_(zinc, Zdistcheck, "distance tests for checking flipped facets", -1);
+ zzdef_(zinc, Zdistconvex, "distance tests for checking convexity", -1);
+ zdef_(zinc, Zdistgood, "distance tests for checking good point", -1);
+ zdef_(zinc, Zdistio, "distance tests for output", -1);
+ zdef_(zinc, Zdiststat, "distance tests for statistics", -1);
+ zdef_(zinc, Zdistplane, "total number of distance tests", -1);
+ zdef_(zinc, Ztotpartcoplanar, "partitions of coplanar points or deleted vertices", -1);
+ zzdef_(zinc, Zpartcoplanar, " distance tests for these partitions", -1);
+ zdef_(zinc, Zcomputefurthest, "distance tests for computing furthest", -1);
+}
+void qh_allstatE2(void) {
+ zdef_(zdoc, Zdoc5, "statistics for matching ridges", -1);
+ zdef_(zinc, Zhashlookup, "total lookups for matching ridges of new facets", -1);
+ zdef_(zinc, Zhashtests, "average number of tests to match a ridge", Zhashlookup);
+ zdef_(zinc, Zhashridge, "total lookups of subridges(duplicates and boundary)", -1);
+ zdef_(zinc, Zhashridgetest, "average number of tests per subridge", Zhashridge);
+ zdef_(zinc, Zdupsame, "duplicated ridges in same merge cycle", -1);
+ zdef_(zinc, Zdupflip, "duplicated ridges with flipped facets", -1);
+
+ zdef_(zdoc, Zdoc6, "statistics for determining merges", -1);
+ zdef_(zinc, Zangletests, "angles computed for ridge convexity", -1);
+ zdef_(zinc, Zbestcentrum, "best merges used centrum instead of vertices",-1);
+ zzdef_(zinc, Zbestdist, "distance tests for best merge", -1);
+ zzdef_(zinc, Zcentrumtests, "distance tests for centrum convexity", -1);
+ zzdef_(zinc, Zdistzero, "distance tests for checking simplicial convexity", -1);
+ zdef_(zinc, Zcoplanarangle, "coplanar angles in getmergeset", -1);
+ zdef_(zinc, Zcoplanarcentrum, "coplanar centrums in getmergeset", -1);
+ zdef_(zinc, Zconcaveridge, "concave ridges in getmergeset", -1);
+}
+void qh_allstatF(void) {
+ zdef_(zdoc, Zdoc7, "statistics for merging", -1);
+ zdef_(zinc, Zpremergetot, "merge iterations", -1);
+ zdef_(zadd, Zmergeinittot, "ave. initial non-convex ridges per iteration", Zpremergetot);
+ zdef_(zadd, Zmergeinitmax, " maximum", -1);
+ zdef_(zadd, Zmergesettot, " ave. additional non-convex ridges per iteration", Zpremergetot);
+ zdef_(zadd, Zmergesetmax, " maximum additional in one pass", -1);
+ zdef_(zadd, Zmergeinittot2, "initial non-convex ridges for post merging", -1);
+ zdef_(zadd, Zmergesettot2, " additional non-convex ridges", -1);
+ zdef_(wmax, Wmaxoutside, "max distance of vertex or coplanar point above facet(w/roundoff)", -1);
+ zdef_(wmin, Wminvertex, "max distance of merged vertex below facet(or roundoff)", -1);
+ zdef_(zinc, Zwidefacet, "centrums frozen due to a wide merge", -1);
+ zdef_(zinc, Zwidevertices, "centrums frozen due to extra vertices", -1);
+ zzdef_(zinc, Ztotmerge, "total number of facets or cycles of facets merged", -1);
+ zdef_(zinc, Zmergesimplex, "merged a simplex", -1);
+ zdef_(zinc, Zonehorizon, "simplices merged into coplanar horizon", -1);
+ zzdef_(zinc, Zcyclehorizon, "cycles of facets merged into coplanar horizon", -1);
+ zzdef_(zadd, Zcyclefacettot, " ave. facets per cycle", Zcyclehorizon);
+ zdef_(zmax, Zcyclefacetmax, " max. facets", -1);
+ zdef_(zinc, Zmergeintohorizon, "new facets merged into horizon", -1);
+ zdef_(zinc, Zmergenew, "new facets merged", -1);
+ zdef_(zinc, Zmergehorizon, "horizon facets merged into new facets", -1);
+ zdef_(zinc, Zmergevertex, "vertices deleted by merging", -1);
+ zdef_(zinc, Zcyclevertex, "vertices deleted by merging into coplanar horizon", -1);
+ zdef_(zinc, Zdegenvertex, "vertices deleted by degenerate facet", -1);
+ zdef_(zinc, Zmergeflipdup, "merges due to flipped facets in duplicated ridge", -1);
+ zdef_(zinc, Zneighbor, "merges due to redundant neighbors", -1);
+ zdef_(zadd, Ztestvneighbor, "non-convex vertex neighbors", -1);
+}
+void qh_allstatG(void) {
+ zdef_(zinc, Zacoplanar, "merges due to angle coplanar facets", -1);
+ zdef_(wadd, Wacoplanartot, " average merge distance", Zacoplanar);
+ zdef_(wmax, Wacoplanarmax, " maximum merge distance", -1);
+ zdef_(zinc, Zcoplanar, "merges due to coplanar facets", -1);
+ zdef_(wadd, Wcoplanartot, " average merge distance", Zcoplanar);
+ zdef_(wmax, Wcoplanarmax, " maximum merge distance", -1);
+ zdef_(zinc, Zconcave, "merges due to concave facets", -1);
+ zdef_(wadd, Wconcavetot, " average merge distance", Zconcave);
+ zdef_(wmax, Wconcavemax, " maximum merge distance", -1);
+ zdef_(zinc, Zavoidold, "coplanar/concave merges due to avoiding old merge", -1);
+ zdef_(wadd, Wavoidoldtot, " average merge distance", Zavoidold);
+ zdef_(wmax, Wavoidoldmax, " maximum merge distance", -1);
+ zdef_(zinc, Zdegen, "merges due to degenerate facets", -1);
+ zdef_(wadd, Wdegentot, " average merge distance", Zdegen);
+ zdef_(wmax, Wdegenmax, " maximum merge distance", -1);
+ zdef_(zinc, Zflipped, "merges due to removing flipped facets", -1);
+ zdef_(wadd, Wflippedtot, " average merge distance", Zflipped);
+ zdef_(wmax, Wflippedmax, " maximum merge distance", -1);
+ zdef_(zinc, Zduplicate, "merges due to duplicated ridges", -1);
+ zdef_(wadd, Wduplicatetot, " average merge distance", Zduplicate);
+ zdef_(wmax, Wduplicatemax, " maximum merge distance", -1);
+}
+void qh_allstatH(void) {
+ zdef_(zdoc, Zdoc8, "renamed vertex statistics", -1);
+ zdef_(zinc, Zrenameshare, "renamed vertices shared by two facets", -1);
+ zdef_(zinc, Zrenamepinch, "renamed vertices in a pinched facet", -1);
+ zdef_(zinc, Zrenameall, "renamed vertices shared by multiple facets", -1);
+ zdef_(zinc, Zfindfail, "rename failures due to duplicated ridges", -1);
+ zdef_(zinc, Zdupridge, " duplicate ridges detected", -1);
+ zdef_(zinc, Zdelridge, "deleted ridges due to renamed vertices", -1);
+ zdef_(zinc, Zdropneighbor, "dropped neighbors due to renamed vertices", -1);
+ zdef_(zinc, Zdropdegen, "degenerate facets due to dropped neighbors", -1);
+ zdef_(zinc, Zdelfacetdup, " facets deleted because of no neighbors", -1);
+ zdef_(zinc, Zremvertex, "vertices removed from facets due to no ridges", -1);
+ zdef_(zinc, Zremvertexdel, " deleted", -1);
+ zdef_(zinc, Zintersectnum, "vertex intersections for locating redundant vertices", -1);
+ zdef_(zinc, Zintersectfail, "intersections failed to find a redundant vertex", -1);
+ zdef_(zinc, Zintersect, "intersections found redundant vertices", -1);
+ zdef_(zadd, Zintersecttot, " ave. number found per vertex", Zintersect);
+ zdef_(zmax, Zintersectmax, " max. found for a vertex", -1);
+ zdef_(zinc, Zvertexridge, NULL, -1);
+ zdef_(zadd, Zvertexridgetot, " ave. number of ridges per tested vertex", Zvertexridge);
+ zdef_(zmax, Zvertexridgemax, " max. number of ridges per tested vertex", -1);
+
+ zdef_(zdoc, Zdoc10, "memory usage statistics(in bytes)", -1);
+ zdef_(zadd, Zmemfacets, "for facets and their normals, neighbor and vertex sets", -1);
+ zdef_(zadd, Zmemvertices, "for vertices and their neighbor sets", -1);
+ zdef_(zadd, Zmempoints, "for input points and outside and coplanar sets",-1);
+ zdef_(zadd, Zmemridges, "for ridges and their vertex sets", -1);
+} /* allstat */
+
+void qh_allstatI(void) {
+ qhstat vridges= qhstat next;
+ zzdef_(zdoc, Zdoc11, "Voronoi ridge statistics", -1);
+ zzdef_(zinc, Zridge, "non-simplicial Voronoi vertices for all ridges", -1);
+ zzdef_(wadd, Wridge, " ave. distance to ridge", Zridge);
+ zzdef_(wmax, Wridgemax, " max. distance to ridge", -1);
+ zzdef_(zinc, Zridgemid, "bounded ridges", -1);
+ zzdef_(wadd, Wridgemid, " ave. distance of midpoint to ridge", Zridgemid);
+ zzdef_(wmax, Wridgemidmax, " max. distance of midpoint to ridge", -1);
+ zzdef_(zinc, Zridgeok, "bounded ridges with ok normal", -1);
+ zzdef_(wadd, Wridgeok, " ave. angle to ridge", Zridgeok);
+ zzdef_(wmax, Wridgeokmax, " max. angle to ridge", -1);
+ zzdef_(zinc, Zridge0, "bounded ridges with near-zero normal", -1);
+ zzdef_(wadd, Wridge0, " ave. angle to ridge", Zridge0);
+ zzdef_(wmax, Wridge0max, " max. angle to ridge", -1);
+
+ zdef_(zdoc, Zdoc12, "Triangulation statistics(Qt)", -1);
+ zdef_(zinc, Ztricoplanar, "non-simplicial facets triangulated", -1);
+ zdef_(zadd, Ztricoplanartot, " ave. new facets created(may be deleted)", Ztricoplanar);
+ zdef_(zmax, Ztricoplanarmax, " max. new facets created", -1);
+ zdef_(zinc, Ztrinull, "null new facets deleted(duplicated vertex)", -1);
+ zdef_(zinc, Ztrimirror, "mirrored pairs of new facets deleted(same vertices)", -1);
+ zdef_(zinc, Ztridegen, "degenerate new facets in output(same ridge)", -1);
+} /* allstat */
+
+/*---------------------------------
+
+ qh_allstatistics()
+ reset printed flag for all statistics
+*/
+void qh_allstatistics(void) {
+ int i;
+
+ for(i=ZEND; i--; )
+ qhstat printed[i]= False;
+} /* allstatistics */
+
+#if qh_KEEPstatistics
+/*---------------------------------
+
+ qh_collectstatistics()
+ collect statistics for qh.facet_list
+
+*/
+void qh_collectstatistics(void) {
+ facetT *facet, *neighbor, **neighborp;
+ vertexT *vertex, **vertexp;
+ realT dotproduct, dist;
+ int sizneighbors, sizridges, sizvertices, i;
+
+ qh old_randomdist= qh RANDOMdist;
+ qh RANDOMdist= False;
+ zval_(Zmempoints)= qh num_points * qh normal_size +
+ sizeof(qhT) + sizeof(qhstatT);
+ zval_(Zmemfacets)= 0;
+ zval_(Zmemridges)= 0;
+ zval_(Zmemvertices)= 0;
+ zval_(Zangle)= 0;
+ wval_(Wangle)= 0.0;
+ zval_(Znumridges)= 0;
+ zval_(Znumfacets)= 0;
+ zval_(Znumneighbors)= 0;
+ zval_(Znumvertices)= 0;
+ zval_(Znumvneighbors)= 0;
+ zval_(Znummergetot)= 0;
+ zval_(Znummergemax)= 0;
+ zval_(Zvertices)= qh num_vertices - qh_setsize(qh del_vertices);
+ if (qh MERGING || qh APPROXhull || qh JOGGLEmax < REALmax/2)
+ wmax_(Wmaxoutside, qh max_outside);
+ if (qh MERGING)
+ wmin_(Wminvertex, qh min_vertex);
+ FORALLfacets
+ facet->seen= False;
+ if (qh DELAUNAY) {
+ FORALLfacets {
+ if (facet->upperdelaunay != qh UPPERdelaunay)
+ facet->seen= True; /* remove from angle statistics */
+ }
+ }
+ FORALLfacets {
+ if (facet->visible && qh NEWfacets)
+ continue;
+ sizvertices= qh_setsize(facet->vertices);
+ sizneighbors= qh_setsize(facet->neighbors);
+ sizridges= qh_setsize(facet->ridges);
+ zinc_(Znumfacets);
+ zadd_(Znumvertices, sizvertices);
+ zmax_(Zmaxvertices, sizvertices);
+ zadd_(Znumneighbors, sizneighbors);
+ zmax_(Zmaxneighbors, sizneighbors);
+ zadd_(Znummergetot, facet->nummerge);
+ i= facet->nummerge; /* avoid warnings */
+ zmax_(Znummergemax, i);
+ if (!facet->simplicial) {
+ if (sizvertices == qh hull_dim) {
+ zinc_(Znowsimplicial);
+ }else {
+ zinc_(Znonsimplicial);
+ }
+ }
+ if (sizridges) {
+ zadd_(Znumridges, sizridges);
+ zmax_(Zmaxridges, sizridges);
+ }
+ zadd_(Zmemfacets, sizeof(facetT) + qh normal_size + 2*sizeof(setT)
+ + SETelemsize * (sizneighbors + sizvertices));
+ if (facet->ridges) {
+ zadd_(Zmemridges,
+ sizeof(setT) + SETelemsize * sizridges + sizridges *
+ (sizeof(ridgeT) + sizeof(setT) + SETelemsize * (qh hull_dim-1))/2);
+ }
+ if (facet->outsideset)
+ zadd_(Zmempoints, sizeof(setT) + SETelemsize * qh_setsize(facet->outsideset));
+ if (facet->coplanarset)
+ zadd_(Zmempoints, sizeof(setT) + SETelemsize * qh_setsize(facet->coplanarset));
+ if (facet->seen) /* Delaunay upper envelope */
+ continue;
+ facet->seen= True;
+ FOREACHneighbor_(facet) {
+ if (neighbor == qh_DUPLICATEridge || neighbor == qh_MERGEridge
+ || neighbor->seen || !facet->normal || !neighbor->normal)
+ continue;
+ dotproduct= qh_getangle(facet->normal, neighbor->normal);
+ zinc_(Zangle);
+ wadd_(Wangle, dotproduct);
+ wmax_(Wanglemax, dotproduct)
+ wmin_(Wanglemin, dotproduct)
+ }
+ if (facet->normal) {
+ FOREACHvertex_(facet->vertices) {
+ zinc_(Zdiststat);
+ qh_distplane(vertex->point, facet, &dist);
+ wmax_(Wvertexmax, dist);
+ wmin_(Wvertexmin, dist);
+ }
+ }
+ }
+ FORALLvertices {
+ if (vertex->deleted)
+ continue;
+ zadd_(Zmemvertices, sizeof(vertexT));
+ if (vertex->neighbors) {
+ sizneighbors= qh_setsize(vertex->neighbors);
+ zadd_(Znumvneighbors, sizneighbors);
+ zmax_(Zmaxvneighbors, sizneighbors);
+ zadd_(Zmemvertices, sizeof(vertexT) + SETelemsize * sizneighbors);
+ }
+ }
+ qh RANDOMdist= qh old_randomdist;
+} /* collectstatistics */
+#endif /* qh_KEEPstatistics */
+
+/*---------------------------------
+
+ qh_freestatistics( )
+ free memory used for statistics
+*/
+void qh_freestatistics(void) {
+
+#if qh_QHpointer
+ qh_free(qh_qhstat);
+ qh_qhstat= NULL;
+#endif
+} /* freestatistics */
+
+/*---------------------------------
+
+ qh_initstatistics( )
+ allocate and initialize statistics
+
+ notes:
+ uses qh_malloc() instead of qh_memalloc() since mem.c not set up yet
+ NOerrors -- qh_initstatistics can not use qh_errexit(). One first call, qh_memalloc is not initialized. Also invoked by QhullQh().
+*/
+void qh_initstatistics(void) {
+ int i;
+ realT realx;
+ int intx;
+
+#if qh_QHpointer
+ if(qh_qhstat){ /* qh_initstatistics may be called from Qhull::resetStatistics() */
+ qh_free(qh_qhstat);
+ qh_qhstat= 0;
+ }
+ if (!(qh_qhstat= (qhstatT *)qh_malloc(sizeof(qhstatT)))) {
+ qh_fprintf(qhmem.ferr, 6183, "qhull error (qh_initstatistics): insufficient memory\n");
+ qh_exit(qh_ERRmem); /* can not use qh_errexit() */
+ }
+#endif
+
+ qhstat next= 0;
+ qh_allstatA();
+ qh_allstatB();
+ qh_allstatC();
+ qh_allstatD();
+ qh_allstatE();
+ qh_allstatE2();
+ qh_allstatF();
+ qh_allstatG();
+ qh_allstatH();
+ qh_allstatI();
+ if (qhstat next > (int)sizeof(qhstat id)) {
+ qh_fprintf(qhmem.ferr, 6184, "qhull error (qh_initstatistics): increase size of qhstat.id[].\n\
+ qhstat.next %d should be <= sizeof(qhstat id) %d\n", qhstat next, (int)sizeof(qhstat id));
+#if 0 /* for locating error, Znumridges should be duplicated */
+ for(i=0; i < ZEND; i++) {
+ int j;
+ for(j=i+1; j < ZEND; j++) {
+ if (qhstat id[i] == qhstat id[j]) {
+ qh_fprintf(qhmem.ferr, 6185, "qhull error (qh_initstatistics): duplicated statistic %d at indices %d and %d\n",
+ qhstat id[i], i, j);
+ }
+ }
+ }
+#endif
+ qh_exit(qh_ERRqhull); /* can not use qh_errexit() */
+ }
+ qhstat init[zinc].i= 0;
+ qhstat init[zadd].i= 0;
+ qhstat init[zmin].i= INT_MAX;
+ qhstat init[zmax].i= INT_MIN;
+ qhstat init[wadd].r= 0;
+ qhstat init[wmin].r= REALmax;
+ qhstat init[wmax].r= -REALmax;
+ for(i=0; i < ZEND; i++) {
+ if (qhstat type[i] > ZTYPEreal) {
+ realx= qhstat init[(unsigned char)(qhstat type[i])].r;
+ qhstat stats[i].r= realx;
+ }else if (qhstat type[i] != zdoc) {
+ intx= qhstat init[(unsigned char)(qhstat type[i])].i;
+ qhstat stats[i].i= intx;
+ }
+ }
+} /* initstatistics */
+
+/*---------------------------------
+
+ qh_newstats( )
+ returns True if statistics for zdoc
+
+ returns:
+ next zdoc
+*/
+boolT qh_newstats(int idx, int *nextindex) {
+ boolT isnew= False;
+ int start, i;
+
+ if (qhstat type[qhstat id[idx]] == zdoc)
+ start= idx+1;
+ else
+ start= idx;
+ for(i= start; i < qhstat next && qhstat type[qhstat id[i]] != zdoc; i++) {
+ if (!qh_nostatistic(qhstat id[i]) && !qhstat printed[qhstat id[i]])
+ isnew= True;
+ }
+ *nextindex= i;
+ return isnew;
+} /* newstats */
+
+/*---------------------------------
+
+ qh_nostatistic( index )
+ true if no statistic to print
+*/
+boolT qh_nostatistic(int i) {
+
+ if ((qhstat type[i] > ZTYPEreal
+ &&qhstat stats[i].r == qhstat init[(unsigned char)(qhstat type[i])].r)
+ || (qhstat type[i] < ZTYPEreal
+ &&qhstat stats[i].i == qhstat init[(unsigned char)(qhstat type[i])].i))
+ return True;
+ return False;
+} /* nostatistic */
+
+#if qh_KEEPstatistics
+/*---------------------------------
+
+ qh_printallstatistics( fp, string )
+ print all statistics with header 'string'
+*/
+void qh_printallstatistics(FILE *fp, const char *string) {
+
+ qh_allstatistics();
+ qh_collectstatistics();
+ qh_printstatistics(fp, string);
+ qh_memstatistics(fp);
+}
+
+
+/*---------------------------------
+
+ qh_printstatistics( fp, string )
+ print statistics to a file with header 'string'
+ skips statistics with qhstat.printed[] (reset with qh_allstatistics)
+
+ see:
+ qh_printallstatistics()
+*/
+void qh_printstatistics(FILE *fp, const char *string) {
+ int i, k;
+ realT ave;
+
+ if (qh num_points != qh num_vertices) {
+ wval_(Wpbalance)= 0;
+ wval_(Wpbalance2)= 0;
+ }else
+ wval_(Wpbalance2)= qh_stddev(zval_(Zpbalance), wval_(Wpbalance),
+ wval_(Wpbalance2), &ave);
+ wval_(Wnewbalance2)= qh_stddev(zval_(Zprocessed), wval_(Wnewbalance),
+ wval_(Wnewbalance2), &ave);
+ qh_fprintf(fp, 9350, "\n\
+%s\n\
+ qhull invoked by: %s | %s\n%s with options:\n%s\n", string, qh rbox_command,
+ qh qhull_command, qh_version, qh qhull_options);
+ qh_fprintf(fp, 9351, "\nprecision constants:\n\
+ %6.2g max. abs. coordinate in the (transformed) input('Qbd:n')\n\
+ %6.2g max. roundoff error for distance computation('En')\n\
+ %6.2g max. roundoff error for angle computations\n\
+ %6.2g min. distance for outside points ('Wn')\n\
+ %6.2g min. distance for visible facets ('Vn')\n\
+ %6.2g max. distance for coplanar facets ('Un')\n\
+ %6.2g max. facet width for recomputing centrum and area\n\
+",
+ qh MAXabs_coord, qh DISTround, qh ANGLEround, qh MINoutside,
+ qh MINvisible, qh MAXcoplanar, qh WIDEfacet);
+ if (qh KEEPnearinside)
+ qh_fprintf(fp, 9352, "\
+ %6.2g max. distance for near-inside points\n", qh NEARinside);
+ if (qh premerge_cos < REALmax/2) qh_fprintf(fp, 9353, "\
+ %6.2g max. cosine for pre-merge angle\n", qh premerge_cos);
+ if (qh PREmerge) qh_fprintf(fp, 9354, "\
+ %6.2g radius of pre-merge centrum\n", qh premerge_centrum);
+ if (qh postmerge_cos < REALmax/2) qh_fprintf(fp, 9355, "\
+ %6.2g max. cosine for post-merge angle\n", qh postmerge_cos);
+ if (qh POSTmerge) qh_fprintf(fp, 9356, "\
+ %6.2g radius of post-merge centrum\n", qh postmerge_centrum);
+ qh_fprintf(fp, 9357, "\
+ %6.2g max. distance for merging two simplicial facets\n\
+ %6.2g max. roundoff error for arithmetic operations\n\
+ %6.2g min. denominator for divisions\n\
+ zero diagonal for Gauss: ", qh ONEmerge, REALepsilon, qh MINdenom);
+ for(k=0; k < qh hull_dim; k++)
+ qh_fprintf(fp, 9358, "%6.2e ", qh NEARzero[k]);
+ qh_fprintf(fp, 9359, "\n\n");
+ for(i=0 ; i < qhstat next; )
+ qh_printstats(fp, i, &i);
+} /* printstatistics */
+#endif /* qh_KEEPstatistics */
+
+/*---------------------------------
+
+ qh_printstatlevel( fp, id )
+ print level information for a statistic
+
+ notes:
+ nop if id >= ZEND, printed, or same as initial value
+*/
+void qh_printstatlevel(FILE *fp, int id, int start) {
+#define NULLfield " "
+
+ if (id >= ZEND || qhstat printed[id])
+ return;
+ if (qhstat type[id] == zdoc) {
+ qh_fprintf(fp, 9360, "%s\n", qhstat doc[id]);
+ return;
+ }
+ start= 0; /* not used */
+ if (qh_nostatistic(id) || !qhstat doc[id])
+ return;
+ qhstat printed[id]= True;
+ if (qhstat count[id] != -1
+ && qhstat stats[(unsigned char)(qhstat count[id])].i == 0)
+ qh_fprintf(fp, 9361, " *0 cnt*");
+ else if (qhstat type[id] >= ZTYPEreal && qhstat count[id] == -1)
+ qh_fprintf(fp, 9362, "%7.2g", qhstat stats[id].r);
+ else if (qhstat type[id] >= ZTYPEreal && qhstat count[id] != -1)
+ qh_fprintf(fp, 9363, "%7.2g", qhstat stats[id].r/ qhstat stats[(unsigned char)(qhstat count[id])].i);
+ else if (qhstat type[id] < ZTYPEreal && qhstat count[id] == -1)
+ qh_fprintf(fp, 9364, "%7d", qhstat stats[id].i);
+ else if (qhstat type[id] < ZTYPEreal && qhstat count[id] != -1)
+ qh_fprintf(fp, 9365, "%7.3g", (realT) qhstat stats[id].i / qhstat stats[(unsigned char)(qhstat count[id])].i);
+ qh_fprintf(fp, 9366, " %s\n", qhstat doc[id]);
+} /* printstatlevel */
+
+
+/*---------------------------------
+
+ qh_printstats( fp, index, nextindex )
+ print statistics for a zdoc group
+
+ returns:
+ next zdoc if non-null
+*/
+void qh_printstats(FILE *fp, int idx, int *nextindex) {
+ int j, nexti;
+
+ if (qh_newstats(idx, &nexti)) {
+ qh_fprintf(fp, 9367, "\n");
+ for (j=idx; j
---------------------------------
+
+ stat.h
+ contains all statistics that are collected for qhull
+
+ see qh-stat.htm and stat.c
+
+ Copyright (c) 1993-2012 The Geometry Center.
+ $Id: //main/2011/qhull/src/libqhullr/stat_r.h#1 $$Change: 1640 $
+ $DateTime: 2014/01/15 09:12:08 $$Author: bbarber $
+
+ recompile qhull if you change this file
+
+ Integer statistics are Z* while real statistics are W*.
+
+ define maydebugx to call a routine at every statistic event
+
+*/
+
+#ifndef qhDEFstat
+#define qhDEFstat 1
+
+#include "libqhull.h"
+
+/*---------------------------------
+
+ qh_KEEPstatistics
+ 0 turns off statistic gathering (except zzdef/zzinc/zzadd/zzval/wwval)
+*/
+#ifndef qh_KEEPstatistics
+#define qh_KEEPstatistics 1
+#endif
+
+/*---------------------------------
+
+ Zxxx for integers, Wxxx for reals
+
+ notes:
+ be sure that all statistics are defined in stat.c
+ otherwise initialization may core dump
+ can pick up all statistics by:
+ grep '[zw].*_[(][ZW]' *.c >z.x
+ remove trailers with query">-
+ remove leaders with query-replace-regexp [ ^I]+ (
+*/
+#if qh_KEEPstatistics
+enum statistics { /* alphabetical after Z/W */
+ Zacoplanar,
+ Wacoplanarmax,
+ Wacoplanartot,
+ Zangle,
+ Wangle,
+ Wanglemax,
+ Wanglemin,
+ Zangletests,
+ Wareatot,
+ Wareamax,
+ Wareamin,
+ Zavoidold,
+ Wavoidoldmax,
+ Wavoidoldtot,
+ Zback0,
+ Zbestcentrum,
+ Zbestdist,
+ Zbestlower,
+ Zbestlowerv,
+ Zcentrumtests,
+ Zcheckpart,
+ Zcomputefurthest,
+ Zconcave,
+ Wconcavemax,
+ Wconcavetot,
+ Zconcaveridges,
+ Zconcaveridge,
+ Zcoplanar,
+ Wcoplanarmax,
+ Wcoplanartot,
+ Zcoplanarangle,
+ Zcoplanarcentrum,
+ Zcoplanarhorizon,
+ Zcoplanarinside,
+ Zcoplanarpart,
+ Zcoplanarridges,
+ Wcpu,
+ Zcyclefacetmax,
+ Zcyclefacettot,
+ Zcyclehorizon,
+ Zcyclevertex,
+ Zdegen,
+ Wdegenmax,
+ Wdegentot,
+ Zdegenvertex,
+ Zdelfacetdup,
+ Zdelridge,
+ Zdelvertextot,
+ Zdelvertexmax,
+ Zdetsimplex,
+ Zdistcheck,
+ Zdistconvex,
+ Zdistgood,
+ Zdistio,
+ Zdistplane,
+ Zdiststat,
+ Zdistvertex,
+ Zdistzero,
+ Zdoc1,
+ Zdoc2,
+ Zdoc3,
+ Zdoc4,
+ Zdoc5,
+ Zdoc6,
+ Zdoc7,
+ Zdoc8,
+ Zdoc9,
+ Zdoc10,
+ Zdoc11,
+ Zdoc12,
+ Zdropdegen,
+ Zdropneighbor,
+ Zdupflip,
+ Zduplicate,
+ Wduplicatemax,
+ Wduplicatetot,
+ Zdupridge,
+ Zdupsame,
+ Zflipped,
+ Wflippedmax,
+ Wflippedtot,
+ Zflippedfacets,
+ Zfindbest,
+ Zfindbestmax,
+ Zfindbesttot,
+ Zfindcoplanar,
+ Zfindfail,
+ Zfindhorizon,
+ Zfindhorizonmax,
+ Zfindhorizontot,
+ Zfindjump,
+ Zfindnew,
+ Zfindnewmax,
+ Zfindnewtot,
+ Zfindnewjump,
+ Zfindnewsharp,
+ Zgauss0,
+ Zgoodfacet,
+ Zhashlookup,
+ Zhashridge,
+ Zhashridgetest,
+ Zhashtests,
+ Zinsidevisible,
+ Zintersect,
+ Zintersectfail,
+ Zintersectmax,
+ Zintersectnum,
+ Zintersecttot,
+ Zmaxneighbors,
+ Wmaxout,
+ Wmaxoutside,
+ Zmaxridges,
+ Zmaxvertex,
+ Zmaxvertices,
+ Zmaxvneighbors,
+ Zmemfacets,
+ Zmempoints,
+ Zmemridges,
+ Zmemvertices,
+ Zmergeflipdup,
+ Zmergehorizon,
+ Zmergeinittot,
+ Zmergeinitmax,
+ Zmergeinittot2,
+ Zmergeintohorizon,
+ Zmergenew,
+ Zmergesettot,
+ Zmergesetmax,
+ Zmergesettot2,
+ Zmergesimplex,
+ Zmergevertex,
+ Wmindenom,
+ Wminvertex,
+ Zminnorm,
+ Zmultiridge,
+ Znearlysingular,
+ Zneighbor,
+ Wnewbalance,
+ Wnewbalance2,
+ Znewfacettot,
+ Znewfacetmax,
+ Znewvertex,
+ Wnewvertex,
+ Wnewvertexmax,
+ Znoarea,
+ Znonsimplicial,
+ Znowsimplicial,
+ Znotgood,
+ Znotgoodnew,
+ Znotmax,
+ Znumfacets,
+ Znummergemax,
+ Znummergetot,
+ Znumneighbors,
+ Znumridges,
+ Znumvertices,
+ Znumvisibility,
+ Znumvneighbors,
+ Zonehorizon,
+ Zpartangle,
+ Zpartcoplanar,
+ Zpartflip,
+ Zparthorizon,
+ Zpartinside,
+ Zpartition,
+ Zpartitionall,
+ Zpartnear,
+ Zpbalance,
+ Wpbalance,
+ Wpbalance2,
+ Zpostfacets,
+ Zpremergetot,
+ Zprocessed,
+ Zremvertex,
+ Zremvertexdel,
+ Zrenameall,
+ Zrenamepinch,
+ Zrenameshare,
+ Zretry,
+ Wretrymax,
+ Zridge,
+ Wridge,
+ Wridgemax,
+ Zridge0,
+ Wridge0,
+ Wridge0max,
+ Zridgemid,
+ Wridgemid,
+ Wridgemidmax,
+ Zridgeok,
+ Wridgeok,
+ Wridgeokmax,
+ Zsearchpoints,
+ Zsetplane,
+ Ztestvneighbor,
+ Ztotcheck,
+ Ztothorizon,
+ Ztotmerge,
+ Ztotpartcoplanar,
+ Ztotpartition,
+ Ztotridges,
+ Ztotvertices,
+ Ztotvisible,
+ Ztricoplanar,
+ Ztricoplanarmax,
+ Ztricoplanartot,
+ Ztridegen,
+ Ztrimirror,
+ Ztrinull,
+ Wvertexmax,
+ Wvertexmin,
+ Zvertexridge,
+ Zvertexridgetot,
+ Zvertexridgemax,
+ Zvertices,
+ Zvisfacettot,
+ Zvisfacetmax,
+ Zvisit,
+ Zvisit2max,
+ Zvisvertextot,
+ Zvisvertexmax,
+ Zvvisit,
+ Zvvisit2max,
+ Zwidefacet,
+ Zwidevertices,
+ ZEND};
+
+/*---------------------------------
+
+ Zxxx/Wxxx statistics that remain defined if qh_KEEPstatistics=0
+
+ notes:
+ be sure to use zzdef, zzinc, etc. with these statistics (no double checking!)
+*/
+#else
+enum statistics { /* for zzdef etc. macros */
+ Zback0,
+ Zbestdist,
+ Zcentrumtests,
+ Zcheckpart,
+ Zconcaveridges,
+ Zcoplanarhorizon,
+ Zcoplanarpart,
+ Zcoplanarridges,
+ Zcyclefacettot,
+ Zcyclehorizon,
+ Zdelvertextot,
+ Zdistcheck,
+ Zdistconvex,
+ Zdistzero,
+ Zdoc1,
+ Zdoc2,
+ Zdoc3,
+ Zdoc11,
+ Zflippedfacets,
+ Zgauss0,
+ Zminnorm,
+ Zmultiridge,
+ Znearlysingular,
+ Wnewvertexmax,
+ Znumvisibility,
+ Zpartcoplanar,
+ Zpartition,
+ Zpartitionall,
+ Zprocessed,
+ Zretry,
+ Zridge,
+ Wridge,
+ Wridgemax,
+ Zridge0,
+ Wridge0,
+ Wridge0max,
+ Zridgemid,
+ Wridgemid,
+ Wridgemidmax,
+ Zridgeok,
+ Wridgeok,
+ Wridgeokmax,
+ Zsetplane,
+ Ztotcheck,
+ Ztotmerge,
+ ZEND};
+#endif
+
+/*---------------------------------
+
+ ztype
+ the type of a statistic sets its initial value.
+
+ notes:
+ The type should be the same as the macro for collecting the statistic
+*/
+enum ztypes {zdoc,zinc,zadd,zmax,zmin,ZTYPEreal,wadd,wmax,wmin,ZTYPEend};
+
+/*========== macros and constants =============*/
+
+/*----------------------------------
+
+ MAYdebugx
+ define as maydebug() to be called frequently for error trapping
+*/
+#define MAYdebugx
+
+/*----------------------------------
+
+ zzdef_, zdef_( type, name, doc, -1)
+ define a statistic (assumes 'qhstat.next= 0;')
+
+ zdef_( type, name, doc, count)
+ define an averaged statistic
+ printed as name/count
+*/
+#define zzdef_(stype,name,string,cnt) qhstat id[qhstat next++]=name; \
+ qhstat doc[name]= string; qhstat count[name]= cnt; qhstat type[name]= stype
+#if qh_KEEPstatistics
+#define zdef_(stype,name,string,cnt) qhstat id[qhstat next++]=name; \
+ qhstat doc[name]= string; qhstat count[name]= cnt; qhstat type[name]= stype
+#else
+#define zdef_(type,name,doc,count)
+#endif
+
+/*----------------------------------
+
+ zzinc_( name ), zinc_( name)
+ increment an integer statistic
+*/
+#define zzinc_(id) {MAYdebugx; qhstat stats[id].i++;}
+#if qh_KEEPstatistics
+#define zinc_(id) {MAYdebugx; qhstat stats[id].i++;}
+#else
+#define zinc_(id) {}
+#endif
+
+/*----------------------------------
+
+ zzadd_( name, value ), zadd_( name, value ), wadd_( name, value )
+ add value to an integer or real statistic
+*/
+#define zzadd_(id, val) {MAYdebugx; qhstat stats[id].i += (val);}
+#define wwadd_(id, val) {MAYdebugx; qhstat stats[id].r += (val);}
+#if qh_KEEPstatistics
+#define zadd_(id, val) {MAYdebugx; qhstat stats[id].i += (val);}
+#define wadd_(id, val) {MAYdebugx; qhstat stats[id].r += (val);}
+#else
+#define zadd_(id, val) {}
+#define wadd_(id, val) {}
+#endif
+
+/*----------------------------------
+
+ zzval_( name ), zval_( name ), wwval_( name )
+ set or return value of a statistic
+*/
+#define zzval_(id) ((qhstat stats[id]).i)
+#define wwval_(id) ((qhstat stats[id]).r)
+#if qh_KEEPstatistics
+#define zval_(id) ((qhstat stats[id]).i)
+#define wval_(id) ((qhstat stats[id]).r)
+#else
+#define zval_(id) qhstat tempi
+#define wval_(id) qhstat tempr
+#endif
+
+/*----------------------------------
+
+ zmax_( id, val ), wmax_( id, value )
+ maximize id with val
+*/
+#define wwmax_(id, val) {MAYdebugx; maximize_(qhstat stats[id].r,(val));}
+#if qh_KEEPstatistics
+#define zmax_(id, val) {MAYdebugx; maximize_(qhstat stats[id].i,(val));}
+#define wmax_(id, val) {MAYdebugx; maximize_(qhstat stats[id].r,(val));}
+#else
+#define zmax_(id, val) {}
+#define wmax_(id, val) {}
+#endif
+
+/*----------------------------------
+
+ zmin_( id, val ), wmin_( id, value )
+ minimize id with val
+*/
+#if qh_KEEPstatistics
+#define zmin_(id, val) {MAYdebugx; minimize_(qhstat stats[id].i,(val));}
+#define wmin_(id, val) {MAYdebugx; minimize_(qhstat stats[id].r,(val));}
+#else
+#define zmin_(id, val) {}
+#define wmin_(id, val) {}
+#endif
+
+/*================== stat.h types ==============*/
+
+
+/*----------------------------------
+
+ intrealT
+ union of integer and real, used for statistics
+*/
+typedef union intrealT intrealT; /* union of int and realT */
+union intrealT {
+ int i;
+ realT r;
+};
+
+/*----------------------------------
+
+ qhstat
+ global data structure for statistics, similar to qh and qhrbox
+
+ notes:
+ access to qh_qhstat is via the "qhstat" macro. There are two choices
+ qh_QHpointer = 1 access globals via a pointer
+ enables qh_saveqhull() and qh_restoreqhull()
+ = 0 qh_qhstat is a static data structure
+ only one instance of qhull() can be active at a time
+ default value
+ qh_QHpointer is defined in libqhull.h
+ qh_QHpointer_dllimport and qh_dllimport define qh_qh as __declspec(dllimport) [libqhull.h]
+
+ allocated in stat.c using qh_malloc()
+*/
+#ifndef DEFqhstatT
+#define DEFqhstatT 1
+typedef struct qhstatT qhstatT;
+#endif
+
+#if qh_QHpointer_dllimport
+#define qhstat qh_qhstat->
+__declspec(dllimport) extern qhstatT *qh_qhstat;
+#elif qh_QHpointer
+#define qhstat qh_qhstat->
+extern qhstatT *qh_qhstat;
+#elif qh_dllimport
+#define qhstat qh_qhstat.
+__declspec(dllimport) extern qhstatT qh_qhstat;
+#else
+#define qhstat qh_qhstat.
+extern qhstatT qh_qhstat;
+#endif
+struct qhstatT {
+ intrealT stats[ZEND]; /* integer and real statistics */
+ unsigned char id[ZEND+10]; /* id's in print order */
+ const char *doc[ZEND]; /* array of documentation strings */
+ short int count[ZEND]; /* -1 if none, else index of count to use */
+ char type[ZEND]; /* type, see ztypes above */
+ char printed[ZEND]; /* true, if statistic has been printed */
+ intrealT init[ZTYPEend]; /* initial values by types, set initstatistics */
+
+ int next; /* next index for zdef_ */
+ int precision; /* index for precision problems */
+ int vridges; /* index for Voronoi ridges */
+ int tempi;
+ realT tempr;
+};
+
+/*========== function prototypes ===========*/
+
+void qh_allstatA(void);
+void qh_allstatB(void);
+void qh_allstatC(void);
+void qh_allstatD(void);
+void qh_allstatE(void);
+void qh_allstatE2(void);
+void qh_allstatF(void);
+void qh_allstatG(void);
+void qh_allstatH(void);
+void qh_allstatI(void);
+void qh_allstatistics(void);
+void qh_collectstatistics(void);
+void qh_freestatistics(void);
+void qh_initstatistics(void);
+boolT qh_newstats(int idx, int *nextindex);
+boolT qh_nostatistic(int i);
+void qh_printallstatistics(FILE *fp, const char *string);
+void qh_printstatistics(FILE *fp, const char *string);
+void qh_printstatlevel(FILE *fp, int id, int start);
+void qh_printstats(FILE *fp, int idx, int *nextindex);
+realT qh_stddev(int num, realT tot, realT tot2, realT *ave);
+
+#endif /* qhDEFstat */
diff --git a/src/libqhullr/user_r.c b/src/libqhullr/user_r.c
new file mode 100644
index 0000000..653c5a4
--- /dev/null
+++ b/src/libqhullr/user_r.c
@@ -0,0 +1,527 @@
+/*
---------------------------------
+
+ user.c
+ user redefinable functions
+
+ see user2.c for qh_fprintf, qh_malloc, qh_free
+
+ see README.txt see COPYING.txt for copyright information.
+
+ see libqhull.h for data structures, macros, and user-callable functions.
+
+ see user_eg.c, unix.c, and qhull_interface.cpp for examples.
+
+ see user.h for user-definable constants
+
+ use qh_NOmem in mem.h to turn off memory management
+ use qh_NOmerge in user.h to turn off facet merging
+ set qh_KEEPstatistics in user.h to 0 to turn off statistics
+
+ This is unsupported software. You're welcome to make changes,
+ but you're on your own if something goes wrong. Use 'Tc' to
+ check frequently. Usually qhull will report an error if
+ a data structure becomes inconsistent. If so, it also reports
+ the last point added to the hull, e.g., 102. You can then trace
+ the execution of qhull with "T4P102".
+
+ Please report any errors that you fix to qhull@qhull.org
+
+ call_qhull is a template for calling qhull from within your application
+
+ if you recompile and load this module, then user.o will not be loaded
+ from qhull.a
+
+ you can add additional quick allocation sizes in qh_user_memsizes
+
+ if the other functions here are redefined to not use qh_print...,
+ then io.o will not be loaded from qhull.a. See user_eg.c for an
+ example. We recommend keeping io.o for the extra debugging
+ information it supplies.
+*/
+
+#include "qhull_a.h"
+
+#include
---------------------------------
+
+ user.h
+ user redefinable constants
+
+ see qh-user.htm. see COPYING for copyright information.
+
+ before reading any code, review libqhull.h for data structure definitions and
+ the "qh" macro.
+
+Sections:
+ ============= qhull library constants ======================
+ ============= data types and configuration macros ==========
+ ============= performance related constants ================
+ ============= memory constants =============================
+ ============= joggle constants =============================
+ ============= conditional compilation ======================
+ ============= -merge constants- ============================
+
+Code flags --
+ NOerrors -- the code does not call qh_errexit()
+ WARN64 -- the code may be incompatible with 64-bit pointers
+
+*/
+
+#include
---------------------------------
+
+ usermem.c
+ qh_exit(), qh_free(), and qh_malloc()
+
+ See README.txt.
+
+ If you redefine one of these functions you must redefine all of them.
+ If you recompile and load this file, then usermem.o will not be loaded
+ from qhull.a or qhull.lib
+
+ See libqhull.h for data structures, macros, and user-callable functions.
+ See user.c for qhull-related, redefinable functions
+ see user.h for user-definable constants
+ See userprintf.c for qh_fprintf and userprintf_rbox,c for qh_fprintf_rbox
+
+ Please report any errors that you fix to qhull@qhull.org
+*/
+
+#include "libqhull.h"
+
+#include
---------------------------------
+
+ userprintf.c
+ qh_fprintf()
+
+ see README.txt see COPYING.txt for copyright information.
+
+ If you recompile and load this file, then userprintf.o will not be loaded
+ from qhull.a or qhull.lib
+
+ See libqhull.h for data structures, macros, and user-callable functions.
+ See user.c for qhull-related, redefinable functions
+ see user.h for user-definable constants
+ See usermem.c for qh_exit(), qh_free(), and qh_malloc()
+ see Qhull.cpp and RboxPoints.cpp for examples.
+
+ Please report any errors that you fix to qhull@qhull.org
+*/
+
+#include "libqhull.h"
+
+#include
---------------------------------
+
+ userprintf_rbox.c
+ qh_fprintf_rbox()
+
+ see README.txt see COPYING.txt for copyright information.
+
+ If you recompile and load this file, then userprintf_rbox.o will not be loaded
+ from qhull.a or qhull.lib
+
+ See libqhull.h for data structures, macros, and user-callable functions.
+ See user.c for qhull-related, redefinable functions
+ see user.h for user-definable constants
+ See usermem.c for qh_exit(), qh_free(), and qh_malloc()
+ see Qhull.cpp and RboxPoints.cpp for examples.
+
+ Please report any errors that you fix to qhull@qhull.org
+*/
+
+#include "libqhull.h"
+
+#include