diff options
author | yuryalekseev <yuryalekseev@yandex-team.com> | 2022-12-28 18:34:10 +0300 |
---|---|---|
committer | yuryalekseev <yuryalekseev@yandex-team.com> | 2022-12-28 18:34:10 +0300 |
commit | 48aaf60ffad50a3cd1dd55d80d8d2fde282f9f26 (patch) | |
tree | c13dfc679b3ba52d0b2e3b49048f3c6d4529edf1 /contrib/libs | |
parent | 7ee4233c4eea5893888ea9657229587d508662c4 (diff) | |
download | ydb-48aaf60ffad50a3cd1dd55d80d8d2fde282f9f26.tar.gz |
Move dstool_oss to ydb/apps/dstool.
Diffstat (limited to 'contrib/libs')
32 files changed, 32890 insertions, 0 deletions
diff --git a/contrib/libs/python/Include/cStringIO.h b/contrib/libs/python/Include/cStringIO.h new file mode 100644 index 0000000000..3a8a908d56 --- /dev/null +++ b/contrib/libs/python/Include/cStringIO.h @@ -0,0 +1,7 @@ +#pragma once + +#ifdef USE_PYTHON3 +#error "No <cStringIO.h> in Python3" +#else +#include <contrib/tools/python/src/Include/cStringIO.h> +#endif diff --git a/contrib/libs/qhull/COPYING.txt b/contrib/libs/qhull/COPYING.txt new file mode 100644 index 0000000000..122a00a4fa --- /dev/null +++ b/contrib/libs/qhull/COPYING.txt @@ -0,0 +1,39 @@ + Qhull, Copyright (c) 1993-2020 + + C.B. Barber + Arlington, MA + + and + + The National Science and Technology Research Center for + Computation and Visualization of Geometric Structures + (The Geometry Center) + University of Minnesota + + email: qhull@qhull.org + +This software includes Qhull from C.B. Barber and The Geometry Center. +Files derived from Qhull 1.0 are copyrighted by the Geometry Center. The +remaining files are copyrighted by C.B. Barber. Qhull is free software +and may be obtained via http from www.qhull.org. It may be freely copied, +modified, and redistributed under the following conditions: + +1. All copyright notices must remain intact in all files. + +2. A copy of this text file must be distributed along with any copies + of Qhull that you redistribute; this includes copies that you have + modified, or copies of programs or other software products that + include Qhull. + +3. If you modify Qhull, you must include a notice giving the + name of the person performing the modification, the date of + modification, and the reason for such modification. + +4. When distributing modified versions of Qhull, or other software + products that include Qhull, you must provide notice that the original + source code may be obtained as noted above. + +5. There is no warranty or other guarantee of fitness for Qhull, it is + provided solely "as is". Bug reports or fixes may be sent to + qhull_bug@qhull.org; the authors may or may not act on them as + they desire. diff --git a/contrib/libs/qhull/README.txt b/contrib/libs/qhull/README.txt new file mode 100644 index 0000000000..d5f6c05900 --- /dev/null +++ b/contrib/libs/qhull/README.txt @@ -0,0 +1,720 @@ +Name + + qhull, rbox 2020.2 2020/08/31 (8.0.2) + +Convex hull, Delaunay triangulation, Voronoi diagrams, Halfspace intersection + + Documentation: + html/index.htm + <http://www.qhull.org/html> + + Available from: + <http://www.qhull.org> + <http://www.qhull.org/download> + <http://github.com/qhull/qhull/wiki> (git@github.com:qhull/qhull.git) + + News and a paper: + <http://www.qhull.org/news> + <http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.117.405> + + Version 1 (simplicial only): + <http://www.qhull.org/download/qhull-1.0.tar.gz> + +Purpose + + Qhull is a general dimension convex hull program that reads a set + of points from stdin, and outputs the smallest convex set that contains + the points to stdout. It also generates Delaunay triangulations, Voronoi + diagrams, furthest-site Voronoi diagrams, and halfspace intersections + about a point. + + Rbox is a useful tool in generating input for Qhull; it generates + hypercubes, diamonds, cones, circles, simplices, spirals, + lattices, and random points. + + Qhull produces graphical output for Geomview. This helps with + understanding the output. <http://www.geomview.org> + +Environment requirements + + Qhull and rbox should run on all 32-bit and 64-bit computers. Use + an ANSI C or C++ compiler to compile the program. The software is + self-contained. It comes with examples and test scripts. + + Qhull's C++ interface uses the STL. The C++ test program uses QTestLib + from the Qt Framework. + + Qhull is copyrighted software. Please read COPYING.txt and REGISTER.txt + before using or distributing Qhull. + +To cite Qhull, please use + + 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. + +To modify Qhull, particularly the C++ interface + + Qhull is on GitHub + (http://github.com/qhull/qhull/wiki, git@github.com:qhull/qhull.git) + + For internal documentation, see html/qh-code.htm + +To install Qhull + + Qhull is precompiled for Windows 32-bit, otherwise it needs compilation. + + Qhull includes Makefiles for gcc and other targets, CMakeLists.txt for CMake, + .sln/.vcproj/.vcxproj files for Microsoft Visual Studio, and .pro files + for Qt Creator. It compiles under Windows with mingw. + (<https://github.com/qhull/qhull/wiki/Qhull-build-systems>) + + Install and build instructions follow. + + See the end of this document for a list of distributed files. + +------------------ +Index + +Installing Qhull on Windows 10, 8, 7 (32- or 64-bit), Windows XP, and Windows NT +Installing Qhull on Unix with gcc +Installing Qhull with CMake 2.6 or later +Installing Qhull with Qt +Working with Qhull's C++ interface +Calling Qhull from C programs +Compiling Qhull with Microsoft Visual C++ +Compiling Qhull with Qt Creator +Compiling Qhull with mingw/gcc on Windows +Compiling Qhull with cygwin on Windows +Compiling from Makfile without gcc +Compiling on other machines and compilers +Distributed files +Authors + +------------------ +Installing Qhull on Windows 10, 8, 7 (32- or 64-bit), Windows XP, and Windows NT + + The zip file contains rbox.exe, qhull.exe, qconvex.exe, qdelaunay.exe, + qhalf.exe, qvoronoi.exe, testqset.exe, user_eg*.exe, documentation files, + and source files. Qhull.exe and user-eg3.exe are compiled with the reentrant + library while the other executables use the non-reentrant library. + + To install Qhull: + - Unzip the files into a directory (e.g., named 'qhull') + - Click on QHULL-GO or open a command window into Qhull's bin directory. + - Test with 'rbox D4 | qhull' + + To uninstall Qhull + - Delete the qhull directory + + To learn about Qhull: + - Execute 'qconvex' for a synopsis and examples. + Or 'qconvex --help' or 'qconvex -?' + - Execute 'rbox 10 | qconvex' to compute the convex hull of 10 random points. + - Execute 'rbox 10 | qconvex i TO file' to write results to 'file'. + - Browse the documentation: qhull\html\index.htm + - If an error occurs, Windows sends the error to stdout instead of stderr. + Use 'TO xxx' to send normal output to xxx + + To improve the command window + - Double-click the window bar to increase the size of the window + - Right-click the window bar + - Select Properties + - Check QuickEdit Mode + Select text with right-click or Enter + Paste text with right-click + - Change Font to Lucinda Console + - Change Layout to Screen Buffer Height 999, Window Size Height 55 + - Change Colors to Screen Background White, Screen Text Black + - Click OK + - Select 'Modify shortcut that started this window', then OK + + If you regularly use qhull on a Windows host, install a bash shell such as + https://gitforwindows.org/ # based on MSYS2 + https://github.com/git-for-windows/git/wiki + http://www.msys2.org/ + https://github.com/msys2/msys2/wiki + [mar'19] Git for Windows v2.21 requires 'qhull --help' + Install in C:\Git\... # Not 'Program Files\...' otherwise './configure && make' fails + www.cygwin.com + www.mingw.org/wiki/msys # for Windows XP + Road Bash (www.qhull.org/bash) # based on MSYS + +------------------ +Installing Qhull on Unix with gcc + + To build Qhull, static libraries, shared library, and C++ interface + - Download and extract Qhull (either GitHub, .tgz file, or .zip file) + - make + - export LD_LIBRARY_PATH=$PWD/lib:$LD_LIBRARY_PATH + - make test + + 'make install' installs Qhull at '/usr/local/'. It installs pkg-config files + at '/usr/local/lib/pkgconfig'. Change the install directory with DESTDIR and PREFIX. + + To build 32-bit Qhull on a 64-bit host (uses 33% less memory in 4-d) + - make new M32=-m32 + + To build 32-bit Qhull without -fpic (may be faster, but shared library may fail) + - make new M32=-m32 FPIC= + + The Makefiles may be edited for other compilers. + If 'testqset' exits with an error, qhull is broken + + A simple Makefile for Qhull is in src/libqhull and src/libqhull_r. + To build the Qhull executables and libqhullstatic + - Extract Qhull from qhull...tgz or qhull...zip + - cd src/libqhull_r # cd src/libqhull + - make + + +------------------ +Installing Qhull with CMake 2.6 or later + + See CMakeLists.txt for examples and further build instructions + + To build Qhull, static libraries, shared library, and C++ interface + - Download and extract Qhull (either GitHub, .tgz file, or .zip file) + - cd build + - cmake --help # List build generators + - cmake -G "<generator>" .. # e.g., for MINGW-w64 -- cmake -G "MSYS Makefiles" .. + - cmake .. + - make + - ctest + - make install # If MSYS or UNIX, default CMAKE_INSTALL_PREFIX is '/usr/local' + # otherwise if WINDOWS, installs to ../bin, ../include, and ../lib + - make uninstall # Delete the files in install_manifest.txt + + The ".." is important. It refers to the parent directory (i.e., qhull/) + + CMake installs lib/pkgconfig/qhull*.pc for use with pkg-config + + If CMAKE_INSTALL_PREFIX is C:/Program Files/qhull, you may need to give 'Users' "full control" + to qhull's sub-directories: bin, doc, include, lib, and man (folder > Properties > Security > Edit > Users). + + On Windows, CMake's 64-bit generators have a "Win64" tag. Qhull's data structures + are substantial larger as 64-bit code than as 32-bit code. This may slow down Qhull. + + If cmake fails with "No CMAKE_C_COMPILER could be found" + - cmake was not able to find the build environment specified by -G "..." + + If cmake's gcc smoketest fails after a Windows update + - Reinstall MINGW-w64 and delete CMakeCache.txt. A Windows update can break gcc process creation for cc1. + +------------------ +Installing Qhull with Qt + + To build Qhull, including its C++ test program (qhulltest) + - Download and extract Qhull (either GitHub, .tgz file, or .zip file) + - Load src/qhull-all.pro into QtCreator + - Configure the project to use a Shadow build at the same level as 'src', 'bin', and 'lib' + If, instead, the shadow build is a subdirectory of 'build', Qt Creator will install Qhull in 'build/bin' and 'build/lib' + - Build + + - Build qhulltest with a C++11 or later compiler + - qhulltest depends on shared libraries QtCore.a and QtTest.a. They may need to be copied + into the bin directory. On Windows, copy Qt5Core.dll and Qt5Test.dll, e.g., /qt/5.11.2/msvc2017_64/bin + - If qhulltest fails with exit status 127 and no error message, + check for missing Q5Core.dll and Qt5Test.dll + +------------------ +Working with Qhull's C++ interface + + See html/qh-code.htm#cpp for calling Qhull from C++ programs + + Class and method documentation is limited + + See html/qh-code.htm#reentrant for converting from Qhull-2012 + + Examples of using the C++ interface + user_eg3_r.cpp + qhulltest/*_test.cpp + + Qhull's C++ interface is likely to change. Stay current with GitHub. + + To clone Qhull's next branch from http://github.com/qhull/qhull/wiki + git init + git clone git@github.com:qhull/qhull.git + cd qhull + git checkout next + ... + git pull origin next + + Compile qhullcpp and libqhullstatic_r with the same compiler. Both libraries + use the C routines setjmp() and longjmp() for error handling. They must + be compiled with the same compiler. + + Qhull provides pkg-config support with build/qhull.pc.in and lib/pkgconfig/qhull*.pc + With back-ticks, you can compile your C++ program with the Qhull libraries: + g++ `pkg-config --cflags --libs qhullcpp qhullstatic_r` -o my_app my_app.cpp + or + g++ `pkg-config --cflags --libs qhullcpp qhull_r` -o my_app my_app.cpp + + qhullcpp must be linked before qhull_r, otherwise the linker reports + an error -- "QhullUser ... multiple definition of `qh_fprintf'" + +------------------ +Calling Qhull from C programs + + See html/qh-code.htm#library for calling Qhull from C programs + + Qhull provides pkg-config support with build/qhull.pc.in and lib/pkgconfig/qhull*.pc + With back-ticks, you can compile your C program with the Qhull library + gcc `pkg-config --cflags --libs qhull_r` -o my_app my_app.c + + See html/qh-code.htm#reentrant for converting from Qhull-2012 + + Warning: You will need to understand Qhull's data structures and read the + code. Most users will find it easier to call Qhull as an external command. + + The reentrant 'C' code (src/libqhull_r), passes a pointer to qhT + to most Qhull routines. This allows multiple instances of Qhull to run + at the same time. It simplifies the C++ interface. + + The non-reentrant 'C' code (src/libqhull) looks unusual. It refers to + Qhull's global data structure, qhT, through a 'qh' macro (e.g., 'qh ferr'). + This allows the same code to use static memory or heap memory. + If qh_QHpointer is defined, qh_qh is a pointer to an allocated qhT; + otherwise qh_qh is a global static data structure of type qhT. + +------------------ +Compiling Qhull with Microsoft Visual C++ + + To compile 32-bit Qhull with Microsoft Visual C++ 2010 and later + - Download and extract Qhull (either GitHub, .tgz file, or .zip file) + - Load solution build/qhull-32.sln + - Right-click 'Retarget solution' from toolset v110 to your Platform Toolset + File > Save All + - Build target 'Win32' + - Project qhulltest requires Qt for DevStudio (http://www.qt.io) + Set the QTDIR environment variable to your Qt directory (e.g., c:/qt/5.2.0/5.2.0/msvc2012) + If QTDIR is incorrect, precompile will fail with 'Can not locate the file specified' + - Copy Qt shared libraries, QtCore.dll and QtTest.dll, into the bin directory + + To compile 64-bit Qhull with Microsoft Visual C++ 2010 and later + - 64-bit Qhull has larger data structures due to 64-bit pointers. This may slow down Qhull. + - Download and extract Qhull (either GitHub, .tgz file, or .zip file) + - Load solution build/qhull-64.sln + - Right-click 'Retarget solution' from toolset v110 to your Platform Toolset + File > Save All + - Build target 'x64' + - If build as 32-bit fails, use solution build/qhull-32.sln + - Project qhulltest requires Qt for DevStudio (http://www.qt.io) + Set the QTDIR environment variable to your Qt directory (e.g., c:/qt/5.2.0/5.2.0/msvc2012_64) + If QTDIR is incorrect, precompile will fail with 'Can not locate the file specified' + + If error -- MSB8020: The build tools for Visual Studio 2012 (Platform Toolset = 'v110') cannot be found. + - 'Project > Retarget solution' for both qhull-32.sln and qhull-64.sln + - 'File > Open' your preferred solution (qhull-32.sln or qhull-64.sln) + - 'Save All' both projects + - DevStudio may need a restart + + To compile Qhull with Microsoft Visual C++ 2005 (vcproj files) + - Download and extract Qhull (either GitHub, .tgz file, or .zip file) + - Load solution build/qhull.sln + - Build target 'win32' (not 'x64') + - Project qhulltest requires Qt for DevStudio (http://www.qt.io) + Set the QTDIR environment variable to your Qt directory (e.g., c:/qt/4.7.4) + If QTDIR is incorrect, precompile will fail with 'Can not locate the file specified' + +------------------ +Compiling Qhull with Qt Creator + + Qt (http://www.qt.io) is a C++ framework for Windows, Linux, and Macintosh + + Qhull uses QTestLib to test qhull's C++ interface (see src/qhulltest/) + + To compile Qhull with Qt Creator + - Download and extract Qhull (either GitHub, .tgz file, or .zip file) + - Download the Qt SDK + - Start Qt Creator + - Load src/qhull-all.pro + - Configure the project to use a Shadow build at the same level as 'src', 'bin', and 'lib' + If, instead, the shadow build is a subdirectory of 'build', Qt Creator will install Qhull in 'build/bin' and 'build/lib' + - Build + + - Build qhulltest with a C++11 or later compiler + - qhulltest depends on shared libraries QtCore.a and QtTest.a. They may need to be copied + into the bin directory. On Windows, copy Qt5Core.dll and Qt5Test.dll, e.g., /qt/5.11.2/msvc2017_64/bin + - If qhulltest fails with exit status 127 and no error message, + check for missing Q5Core.dll and Qt5Test.dll + +------------------ +Compiling Qhull with mingw/gcc on Windows + + To compile Qhull with MINGW + - Download and extract Qhull (either GitHub, .tgz file, or .zip file) + - Install GitForWindows (https://gitforwindows.org/) + or MSYS2 (http://www.msys2.org/) + Install in C:\Git\... # Not 'Program Files\...' otherwise './configure && make' will not work + - Install MINGW-w64 with gcc (https://mingw-w64.org/) + 1) Goto sourceforge -- https://sourceforge.net/projects/mingw-w64/files/ + 2) in folder -- mingw-w64 + 3) download installer -- MinGW-W64-install.exe + Run the installer + 1) Select i686/posix/dwarf + 2) Install in 'C:\mingw-w64' # Not 'Program Files\...' + Rename /c/mingw-w64/mingw32/bin/mingw32-make.exe to make.exe + Add the 'C:\mingw-w64\mingw32\bin' directory to your $PATH environment variable + Execute 'which make' to check that 'make' is mingw-w64's make + - Compile Qhull from the home directory + make help + make + + Notes + - Mingw is included with Qt SDK in qt/Tools/mingw53_32 + - If you use Windows XP + Install Road Bash (http://www.qhull.org/bash) or MSYS (http://www.mingw.org/wiki/msys) + Install MINGW (http://mingw.org/) + +------------------ +Compiling Qhull with cygwin on Windows + + To compile Qhull with cygwin + - Download and extract Qhull (either GitHub, .tgz file, or .zip file) + - Install cygwin (http://www.cygwin.com) + - Include packages for gcc, make, ar, and ln + - make + +------------------ +Compiling from Makfile without gcc + + The file, qhull-src.tgz, contains documentation and source files for + qhull and rbox. + + To unpack the tgz file + - tar zxf qhull-src.tgz + - cd qhull + - Use qhull/Makefile + Simpler Makefiles are qhull/src/libqhull/Makefile and qhull/src/libqhull_r/Makefile + + Compiling qhull and rbox with Makefile + - in Makefile, check the CC, CCOPTS1, PRINTMAN, and PRINTC defines + - the defaults are gcc and enscript + - CCOPTS1 should include the ANSI flag. It defines __STDC__ + - in user.h, check the definitions of qh_SECticks and qh_CPUclock. + - use '#define qh_CLOCKtype 2' for timing runs longer than 1 hour + - type: make + - this builds: qhull qconvex qdelaunay qhalf qvoronoi rbox libqhull.a libqhull_r.a + - type: make doc + - this prints the man page + - See also qhull/html/index.htm + - if your compiler reports many errors, it is probably not a ANSI C compiler + - you will need to set the -ansi switch or find another compiler + - if your compiler warns about missing prototypes for fprintf() etc. + - this is ok, your compiler should have these in stdio.h + - if your compiler warns about missing prototypes for memset() etc. + - include memory.h in qhull_a.h + - if your compiler reports "global.c: storage size of 'qh_qh' isn't known" + - delete the initializer "={0}" in global.c, stat.c and mem.c + - if your compiler warns about "stat.c: improper initializer" + - this is ok, the initializer is not used + - if you have trouble building libqhull.a with 'ar' + - try 'make -f Makefile.txt qhullx' + - if the code compiles, the qhull test case will automatically execute + - if an error occurs, there's an incompatibility between machines + - If you can, try a different compiler + - You can turn off the Qhull memory manager with qh_NOmem in mem.h + - You can turn off compiler optimization (-O2 in Makefile) + - If you find the source of the problem, please let us know + - to install the programs and their man pages: + - define MANDIR and BINDIR + - type 'make install' + + - if you have Geomview (www.geomview.org) + - try 'rbox 100 | qconvex G >a' and load 'a' into Geomview + - run 'q_eg' for Geomview examples of Qhull output (see qh-eg.htm) + +------------------ +Compiling on other machines and compilers + + Qhull may compile with Borland C++ 5.0 bcc32. A Makefile is included. + Execute 'cd src/libqhull; make -f Mborland'. If you use the Borland IDE, set + the ANSI option in Options:Project:Compiler:Source:Language-compliance. + + Qhull may compile with Borland C++ 4.02 for Win32 and DOS Power Pack. + Use 'cd src/libqhull; make -f Mborland -D_DPMI'. Qhull 1.0 compiles with + Borland C++ 4.02. For rbox 1.0, use "bcc32 -WX -w- -O2-e -erbox -lc rbox.c". + Use the same options for Qhull 1.0. [D. Zwick] + + If you have troubles with the memory manager, you can turn it off by + defining qh_NOmem in mem.h. + +------------------ +Distributed files + + README.txt // Instructions for installing Qhull + REGISTER.txt // Qhull registration + COPYING.txt // Copyright notice + QHULL-GO.lnk // Windows icon for eg/qhull-go.bat + Announce.txt // Announcement + CMakeLists.txt // CMake build file (2.6 or later) + File_id.diz // Package descriptor + index.htm // Home page + Makefile // Makefile for gcc and other compilers + qhull*.md5sum // md5sum for all files + + bin/* // Qhull executables and dll (.zip only) + build/CMakeModules/CheckLFS.cmake // enables Large File Support in CMake + build/config.cmake.in // extract target variables + build/qhull.pc.in // pkg-config template for creating lib/pkgconfig/qhull*.pc + build/qhull-32.sln // 32-bit DevStudio solution and project files (2010 and later) + build/*-32.vcxproj + build/qhull-64.sln // 64-bit DevStudio solution and project files (2010 and later) + build/*-64.vcxproj + build/qhull.sln // DevStudio solution and project files (2005 and 2009) + build/*.vcproj + build/qhulltest/ // DevStudio project files for qhulltest (C++ and Qt) + build/README-build.txt // Contents of build/ + eg/* // Test scripts and geomview files from q_eg + html/index.htm // Manual + html/qh-faq.htm // Frequently asked questions + html/qh-get.htm // Download page + html/qhull-cpp.xml // C++ style notes as a Road FAQ (www.qhull.org/road) + src/Changes.txt // Change history for Qhull and rbox + src/qhull-all.pro // Qt project + +eg/ + q_benchmark // shell script for precision and performance benchmark + q_benchmark-ok.txt // reviewed output from q_benchmark + q_eg // shell script for Geomview examples (eg.01.cube) + q_egtest // shell script for Geomview test examples + q_test // shell script to test qhull + q_test.bat // Windows batch test for QHULL-GO.bat + // cd bin; ..\eg\q_test.bat >q_test.x 2>&1 + q_test-ok.txt // reviewed output from q_test + qhulltest-ok.txt // reviewed output from qhulltest (Qt only) + make-qhull_qh.sh // shell script to create non-reentrant qhull_qh from reentrant Qhull + make-vcproj.sh // shell script to create vcproj and vcxprog files + qhull-zip.sh // shell script to create distribution files + qtest.sh // shell script for testing and logging qhull + +rbox consists of (bin, html): + rbox.exe // Win32 executable (.zip only) + rbox.htm // html manual + rbox.man // Unix man page + rbox.txt + +qhull consists of (bin, html): + qconvex.exe // Win32 executables and dlls (.zip download only) + qhull.exe // Built with the reentrant library (about 2% slower) + qdelaunay.exe + qhalf.exe + qvoronoi.exe + qhull_r.dll + qhull-go.bat // command window + qconvex.htm // html manual + qdelaun.htm + qdelau_f.htm + qhalf.htm + qvoronoi.htm + qvoron_f.htm + qh-eg.htm + qh-code.htm + qh-impre.htm + index.htm + qh-opt*.htm + qh-quick.htm + qh--*.gif // images for manual + normal_voronoi_knauss_oesterle.jpg + qh_findbestfacet-drielsma.pdf + qhull.man // Unix man page + qhull.txt + +bin/ + msvcr80.dll // Visual C++ redistributable file (.zip download only) + +src/ + qhull/unix.c // Qhull and rbox applications using non-reentrant libqhullstatic.a + rbox/rbox.c + qconvex/qconvex.c + qhalf/qhalf.c + qdelaunay/qdelaunay.c + qvoronoi/qvoronoi.c + + qhull/unix_r.c // Qhull and rbox applications using reentrant libqhullstatic_r.a + rbox/rbox_r.c + qconvex/qconvex_r.c // Qhull applications built with reentrant libqhull_r/Makefile + qhalf/qhalf_r.c + qdelaunay/qdelaun_r.c + qvoronoi/qvoronoi_r.c + + user_eg/user_eg_r.c // example of using qhull_r.dll from a user program + user_eg2/user_eg2_r.c // example of using libqhullstatic_r.a from a user program + user_eg3/user_eg3_r.cpp // example of Qhull's C++ interface libqhullcpp with libqhullstatic_r.a + qhulltest/qhulltest.cpp // Test of Qhull's C++ interface using Qt's QTestLib + qhull-*.pri // Include files for Qt projects + testqset_r/testqset_r.c // Test of reentrant qset_r.c and mem_r.c + testqset/testqset.c // Test of non-rentrant qset.c and mem.c + +src/libqhull + libqhull.pro // Qt project for non-rentrant, shared library (qhull.dll) + index.htm // design documentation for libqhull + qh-*.htm + qhull-exports.def // Export Definition files for Visual C++ + qhull-nomerge-exports.def + qhull_p-exports.def + qhull_p-nomerge-exports.def + Makefile // Simple gcc Makefile for qhull and libqhullstatic.a + Mborland // Makefile for Borland C++ 5.0 + + libqhull.h // header file for qhull + user.h // header file of user definable constants + libqhull.c // Quickhull algorithm with partitioning + user.c // user re-definable functions + usermem.c + userprintf.c + userprintf_rbox.c + + qhull_a.h // include files for libqhull/*.c + geom.c // geometric routines + geom2.c + geom.h + global.c // global variables + io.c // input-output routines + io.h + mem.c // memory routines, this is stand-alone code + mem.h + merge.c // merging of non-convex facets + merge.h + poly.c // polyhedron routines + poly2.c + poly.h + qset.c // set routines, this only depends on mem.c + qset.h + random.c // utilities w/ Park & Miller's random number generator + random.h + rboxlib.c // point set generator for rbox + stat.c // statistics + stat.h + +src/libqhull_r + libqhull_r.pro // Qt project for rentrant, shared library (qhull_r.dll) + index.htm // design documentation for libqhull_r + qh-*_r.htm + qhull_r-exports.def // Export Definition files for Visual C++ + qhull_r-nomerge-exports.def + Makefile // Simple gcc Makefile for qhull and libqhullstatic.a + + libqhull_r.h // header file for qhull + user_r.h // header file of user definable constants + libqhull_r.c // Quickhull algorithm wi_r.hpartitioning + user_r.c // user re-definable functions + usermem.c + userprintf.c + userprintf_rbox.c + qhull_ra.h // include files for libqhull/*_r.c + geom_r.c // geometric routines + geom2.c + geom_r.h + global_r.c // global variables + io_r.c // input-output routines + io_r.h + mem_r.c // memory routines, this is stand-alone code + mem.h + merge_r.c // merging of non-convex facets + merge.h + poly_r.c // polyhedron routines + poly2.c + poly_r.h + qset_r.c // set routines, this only depends on mem_r.c + qset.h + random_r.c // utilities w/ Park & Miller's random number generator + random.h + rboxlib_r.c // point set generator for rbox + stat_r.c // statistics + stat.h + +src/libqhullcpp/ + libqhullcpp.pro // Qt project for renentrant, static C++ library + Qhull.cpp // Calls libqhull_r.c from C++ + Qhull.h + qt-qhull.cpp // Supporting methods for Qt + + Coordinates.cpp // input classes + Coordinates.h + + PointCoordinates.cpp + PointCoordinates.h + RboxPoints.cpp // call rboxlib.c from C++ + RboxPoints.h + + QhullFacet.cpp // data structure classes + QhullFacet.h + QhullHyperplane.cpp + QhullHyperplane.h + QhullPoint.cpp + QhullPoint.h + QhullQh.cpp + QhullRidge.cpp + QhullRidge.h + QhullVertex.cpp + QhullVertex.h + + QhullFacetList.cpp // collection classes + QhullFacetList.h + QhullFacetSet.cpp + QhullFacetSet.h + QhullIterator.h + QhullLinkedList.h + QhullPoints.cpp + QhullPoints.h + QhullPointSet.cpp + QhullPointSet.h + QhullSet.cpp + QhullSet.h + QhullSets.h + QhullVertexSet.cpp + QhullVertexSet.h + + functionObjects.h // supporting classes + QhullError.cpp + QhullError.h + QhullQh.cpp + QhullQh.h + QhullStat.cpp + QhullStat.h + QhullUser.cpp + QhullUser.h + RoadError.cpp // Supporting base classes + RoadError.h + RoadLogEvent.cpp + RoadLogEvent.h + usermem_r-cpp.cpp // Optional override for qh_exit() to throw an error + +src/libqhullstatic/ + libqhullstatic.pro // Qt project for non-reentrant, static library + +src/libqhullstatic_r/ + libqhullstatic_r.pro // Qt project for reentrant, static library + +src/qhulltest/ + qhulltest.pro // Qt project for test of C++ interface + Coordinates_test.cpp // Test of each class + PointCoordinates_test.cpp + Qhull_test.cpp + QhullFacet_test.cpp + QhullFacetList_test.cpp + QhullFacetSet_test.cpp + QhullHyperplane_test.cpp + QhullLinkedList_test.cpp + QhullPoint_test.cpp + QhullPoints_test.cpp + QhullPointSet_test.cpp + QhullRidge_test.cpp + QhullSet_test.cpp + QhullVertex_test.cpp + QhullVertexSet_test.cpp + RboxPoints_test.cpp + RoadTest.cpp // Run multiple test files with QTestLib + RoadTest.h + +------------------ +Authors + + C. Bradford Barber Hannu Huhdanpaa (Version 1.0) + bradb@shore.net hannu@qhull.org + + Qhull 1.0 and 2.0 were developed under NSF grants NSF/DMS-8920161 + and NSF-CCR-91-15793 750-7504 at the Geometry Center and Harvard + University. If you find Qhull useful, please let us know. diff --git a/contrib/libs/qhull/REGISTER.txt b/contrib/libs/qhull/REGISTER.txt new file mode 100644 index 0000000000..16ccb1a58d --- /dev/null +++ b/contrib/libs/qhull/REGISTER.txt @@ -0,0 +1,32 @@ +Dear Qhull User + +We would like to find out how you are using our software. Think of +Qhull as a new kind of shareware: you share your science and successes +with us, and we share our software and support with you. + +If you use Qhull, please send us a note telling +us what you are doing with it. + +We need to know: + + (1) What you are working on - an abstract of your work would be + fine. + + (2) How Qhull has helped you, for example, by increasing your + productivity or allowing you to do things you could not do + before. If Qhull had a direct bearing on your work, please + tell us about this. + +We encourage you to cite Qhull in your publications. + +To cite Qhull, please use + + 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. + +Please send e-mail to + + bradb@shore.net + +Thank you! diff --git a/contrib/libs/qhull/libqhull_r/geom2_r.c b/contrib/libs/qhull/libqhull_r/geom2_r.c new file mode 100644 index 0000000000..9e0f997f63 --- /dev/null +++ b/contrib/libs/qhull/libqhull_r/geom2_r.c @@ -0,0 +1,2302 @@ +/*<html><pre> -<a href="qh-geom_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + + geom2_r.c + infrequently used geometric routines of qhull + + see qh-geom_r.htm and geom_r.h + + Copyright (c) 1993-2020 The Geometry Center. + $Id: //main/2019/qhull/src/libqhull_r/geom2_r.c#17 $$Change: 3037 $ + $DateTime: 2020/09/03 17:28:32 $$Author: bbarber $ + + frequently used code goes into geom_r.c +*/ + +#include "qhull_ra.h" + +/*================== functions in alphabetic order ============*/ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="copypoints">-</a> + + qh_copypoints(qh, points, numpoints, dimension ) + return qh_malloc'd copy of points + + notes: + qh_free the returned points to avoid a memory leak +*/ +coordT *qh_copypoints(qhT *qh, 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, qh->ferr, 6004, "qhull error: insufficient memory to copy %d points\n", + numpoints); + qh_errexit(qh, qh_ERRmem, NULL, NULL); + } + memcpy((char *)newpoints, (char *)points, (size_t)size); /* newpoints!=0 by QH6004 */ + return newpoints; +} /* copypoints */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="crossproduct">-</a> + + 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 */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="determinant">-</a> + + qh_determinant(qh, 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(qhT *qh, realT **rows, int dim, boolT *nearzero) { + realT det=0; + int i; + boolT sign= False; + + *nearzero= False; + if (dim < 2) { + qh_fprintf(qh, qh->ferr, 6005, "qhull internal error (qh_determinate): only implemented for dimension >= 2\n"); + qh_errexit(qh, 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) < 10*qh->NEARzero[1]) /* QH11031 FIX: 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) < 10*qh->NEARzero[2]) /* QH11031 FIX: what should this be? det 5.5e-12 was flat for qh_maxsimplex of qdelaunay 0,0 27,27 -36,36 -9,63 */ + *nearzero= True; + }else { + qh_gausselim(qh, 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 */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="detjoggle">-</a> + + qh_detjoggle(qh, 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(qhT *qh, 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; + + if (qh->SETroundoff) + distround= qh->DISTround; /* 'En' */ + else{ + 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_(qh, 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, qh->hull_dim, maxabs, sumabs); + } + joggle= distround * qh_JOGGLEdefault; + maximize_(joggle, REALepsilon * qh_JOGGLEdefault); + trace2((qh, qh->ferr, 2001, "qh_detjoggle: joggle=%2.2g maxwidth=%2.2g\n", joggle, maxwidth)); + return joggle; +} /* detjoggle */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="detmaxoutside">-</a> + + qh_detmaxoutside(qh); + determine qh.MAXoutside target for qh_RATIO... tests of distance + updates option '_max-outside' + + notes: + called from qh_addpoint and qh_detroundoff + accounts for qh.ONEmerge, qh.DISTround, qh.MINoutside ('Wn'), qh.max_outside + see qh_maxout for qh.max_outside with qh.DISTround +*/ + +void qh_detmaxoutside(qhT *qh) { + realT maxoutside; + + maxoutside= fmax_(qh->max_outside, qh->ONEmerge + qh->DISTround); + maximize_(maxoutside, qh->MINoutside); + qh->MAXoutside= maxoutside; + trace3((qh, qh->ferr, 3056, "qh_detmaxoutside: MAXoutside %2.2g from qh.max_outside %2.2g, ONEmerge %2.2g, MINoutside %2.2g, DISTround %2.2g\n", + qh->MAXoutside, qh->max_outside, qh->ONEmerge, qh->MINoutside, qh->DISTround)); +} /* detmaxoutside */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="detroundoff">-</a> + + qh_detroundoff(qh) + 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(qhT *qh) { + + qh_option(qh, "_max-width", NULL, &qh->MAXwidth); + if (!qh->SETroundoff) { + qh->DISTround= qh_distround(qh, qh->hull_dim, qh->MAXabs_coord, qh->MAXsumcoord); + qh_option(qh, "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; + trace4((qh, qh->ferr, 4096, "qh_detroundoff: increase qh.ANGLEround by option 'R%2.2g'\n", qh->RANDOMfactor)); + } + if (qh->premerge_cos < REALmax/2) { + qh->premerge_cos -= qh->ANGLEround; + if (qh->RANDOMdist) + qh_option(qh, "Angle-premerge-with-random", NULL, &qh->premerge_cos); + } + if (qh->postmerge_cos < REALmax/2) { + qh->postmerge_cos -= qh->ANGLEround; + if (qh->RANDOMdist) + qh_option(qh, "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(qh, "Centrum-premerge-with-random", NULL, &qh->premerge_centrum); + if (qh->RANDOMdist && qh->POSTmerge) + qh_option(qh, "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(qh, "_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(qh, "_near-inside", NULL, &qh->NEARinside); + if (qh->JOGGLEmax < qh->DISTround) { + qh_fprintf(qh, qh->ferr, 6006, "qhull option error: the joggle for 'QJn', %.2g, is below roundoff for distance computations, %.2g\n", + qh->JOGGLEmax, qh->DISTround); + qh_errexit(qh, 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(qh, "Visible-distance", NULL, &qh->MINvisible); + } + if (qh->MAXcoplanar > REALmax/2) { + qh->MAXcoplanar= qh->MINvisible; + qh_option(qh, "U-max-coplanar", 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(qh, "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(qh, "_wide-facet", NULL, &qh->WIDEfacet); + if (qh->MINvisible > qh->MINoutside + 3 * REALepsilon + && !qh->BESToutside && !qh->FORCEoutput) + qh_fprintf(qh, 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 */ + qh_detmaxoutside(qh); +} /* detroundoff */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="detsimplex">-</a> + + qh_detsimplex(qh, apex, points, dim, nearzero ) + compute determinant of a simplex with point apex and base points + + returns: + signed determinant and nearzero from qh_determinant + + notes: + called by qh_maxsimplex and qh_initialvertices + 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(qhT *qh, 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, qh->ferr, 6007, "qhull internal error (qh_detsimplex): #points %d < dimension %d\n", + i, dim); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + det= qh_determinant(qh, rows, dim, nearzero); + trace2((qh, qh->ferr, 2002, "qh_detsimplex: det=%2.2g for point p%d, dim %d, nearzero? %d\n", + det, qh_pointid(qh, apex), dim, *nearzero)); + return det; +} /* detsimplex */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="distnorm">-</a> + + 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_r.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 */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="distround">-</a> + + qh_distround(qh, 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 + if qh.RANDOMdist ('Qr'), adjusts qh_distround + + returns: + max dist round for qh.REALepsilon and qh.RANDOMdist + + notes: + calculate roundoff error according to Golub & van Loan, 1983, Lemma 3.2-1, "Rounding Errors" + use sqrt(dim) since one vector is normalized + or use maxsumabs since one vector is < 1 +*/ +realT qh_distround(qhT *qh, int dimension, realT maxabs, realT maxsumabs) { + realT maxdistsum, maxround, delta; + + maxdistsum= sqrt((realT)dimension) * maxabs; + minimize_( maxdistsum, maxsumabs); + maxround= REALepsilon * (dimension * maxdistsum * 1.01 + maxabs); + /* adds maxabs for offset */ + if (qh->RANDOMdist) { + delta= qh->RANDOMfactor * maxabs; + maxround += delta; + trace4((qh, qh->ferr, 4092, "qh_distround: increase roundoff by random delta %2.2g for option 'R%2.2g'\n", delta, qh->RANDOMfactor)); + } + trace4((qh, qh->ferr, 4008, "qh_distround: %2.2g, maxabs %2.2g, maxsumabs %2.2g, maxdistsum %2.2g\n", + maxround, maxabs, maxsumabs, maxdistsum)); + return maxround; +} /* distround */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="divzero">-</a> + + 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 */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="facetarea">-</a> + + qh_facetarea(qh, 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(qhT *qh, 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, 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(qh, facet); + FOREACHridge_(facet->ridges) + area += qh_facetarea_simplex(qh, qh->hull_dim, centrum, ridge->vertices, + NULL, (boolT)(ridge->top == facet), facet->normal, &facet->offset); + if (qh->CENTERtype != qh_AScentrum) + qh_memfree(qh, centrum, qh->normal_size); + } + if (facet->upperdelaunay && qh->DELAUNAY) + area= -area; /* the normal should be [0,...,1] */ + trace4((qh, qh->ferr, 4009, "qh_facetarea: f%d area %2.2g\n", facet->id, area)); + return area; +} /* facetarea */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="facetarea_simplex">-</a> + + qh_facetarea_simplex(qh, 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(qhT *qh, 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, qh->ferr, 6008, "qhull internal error (qh_facetarea_simplex): #points %d != dim %d -1\n", + i, dim); + qh_errexit(qh, 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_(Zdetfacetarea); + area= qh_determinant(qh, rows, dim, &nearzero); + if (toporient) + area= -area; + area *= qh->AREAfactor; + trace4((qh, qh->ferr, 4010, "qh_facetarea_simplex: area=%2.2g for point p%d, toporient %d, nearzero? %d\n", + area, qh_pointid(qh, apex), toporient, nearzero)); + return area; +} /* facetarea_simplex */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="facetcenter">-</a> + + qh_facetcenter(qh, 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(qhT *qh, setT *vertices) { + setT *points= qh_settemp(qh, qh_setsize(qh, vertices)); + vertexT *vertex, **vertexp; + pointT *center; + + FOREACHvertex_(vertices) + qh_setappend(qh, &points, vertex->point); + center= qh_voronoi_center(qh, qh->hull_dim-1, points); + qh_settempfree(qh, &points); + return center; +} /* facetcenter */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="findgooddist">-</a> + + qh_findgooddist(qh, 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(qhT *qh, 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(qh, point, facetA, &bestdist); + bestfacet= facetA; + goodseen= True; + } + qh_removefacet(qh, facetA); + qh_appendfacet(qh, 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(qh, point, neighbor, &dist); + if (dist > 0) { + qh_removefacet(qh, neighbor); + qh_appendfacet(qh, neighbor); + if (neighbor->good) { + goodseen= True; + if (dist > bestdist) { + bestdist= dist; + bestfacet= neighbor; + } + } + } + } + } + if (bestfacet) { + *distp= bestdist; + trace2((qh, qh->ferr, 2003, "qh_findgooddist: p%d is %2.2g above good facet f%d\n", + qh_pointid(qh, point), bestdist, bestfacet->id)); + return bestfacet; + } + trace4((qh, qh->ferr, 4011, "qh_findgooddist: no good facet for p%d above f%d\n", + qh_pointid(qh, point), facetA->id)); + return NULL; +} /* findgooddist */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="furthestnewvertex">-</a> + + qh_furthestnewvertex(qh, unvisited, facet, &maxdist ) + return furthest unvisited, new vertex to a facet + + return: + NULL if no vertex is above facet + maxdist to facet + updates v.visitid + + notes: + Ignores vertices in facetB + Does not change qh.vertex_visit. Use in conjunction with qh_furthestvertex +*/ +vertexT *qh_furthestnewvertex(qhT *qh, unsigned int unvisited, facetT *facet, realT *maxdistp /* qh.newvertex_list */) { + vertexT *maxvertex= NULL, *vertex; + coordT dist, maxdist= 0.0; + + FORALLvertex_(qh->newvertex_list) { + if (vertex->newfacet && vertex->visitid <= unvisited) { + vertex->visitid= qh->vertex_visit; + qh_distplane(qh, vertex->point, facet, &dist); + if (dist > maxdist) { + maxdist= dist; + maxvertex= vertex; + } + } + } + trace4((qh, qh->ferr, 4085, "qh_furthestnewvertex: v%d dist %2.2g is furthest new vertex for f%d\n", + getid_(maxvertex), maxdist, facet->id)); + *maxdistp= maxdist; + return maxvertex; +} /* furthestnewvertex */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="furthestvertex">-</a> + + qh_furthestvertex(qh, facetA, facetB, &maxdist, &mindist ) + return furthest vertex in facetA from facetB, or NULL if none + + return: + maxdist and mindist to facetB or 0.0 if none + updates qh.vertex_visit + + notes: + Ignores vertices in facetB +*/ +vertexT *qh_furthestvertex(qhT *qh, facetT *facetA, facetT *facetB, realT *maxdistp, realT *mindistp) { + vertexT *maxvertex= NULL, *vertex, **vertexp; + coordT dist, maxdist= -REALmax, mindist= REALmax; + + qh->vertex_visit++; + FOREACHvertex_(facetB->vertices) + vertex->visitid= qh->vertex_visit; + FOREACHvertex_(facetA->vertices) { + if (vertex->visitid != qh->vertex_visit) { + vertex->visitid= qh->vertex_visit; + zzinc_(Zvertextests); + qh_distplane(qh, vertex->point, facetB, &dist); + if (!maxvertex) { + maxdist= dist; + mindist= dist; + maxvertex= vertex; + }else if (dist > maxdist) { + maxdist= dist; + maxvertex= vertex; + }else if (dist < mindist) + mindist= dist; + } + } + if (!maxvertex) { + trace3((qh, qh->ferr, 3067, "qh_furthestvertex: all vertices of f%d are in f%d. Returning 0.0 for max and mindist\n", + facetA->id, facetB->id)); + maxdist= mindist= 0.0; + }else { + trace4((qh, qh->ferr, 4084, "qh_furthestvertex: v%d dist %2.2g is furthest (mindist %2.2g) of f%d above f%d\n", + maxvertex->id, maxdist, mindist, facetA->id, facetB->id)); + } + *maxdistp= maxdist; + *mindistp= mindist; + return maxvertex; +} /* furthestvertex */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="getarea">-</a> + + qh_getarea(qh, 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(qhT *qh, facetT *facetlist) { + realT area; + realT dist; + facetT *facet; + + if (qh->hasAreaVolume) + return; + if (qh->REPORTfreq) + qh_fprintf(qh, qh->ferr, 8020, "computing area of each facet and volume of the convex hull\n"); + else + trace1((qh, qh->ferr, 1001, "qh_getarea: computing area for each facet and its volume to qh.interior_point (dist*area/dim)\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(qh, 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, 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 */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="gram_schmidt">-</a> + + qh_gram_schmidt(qh, dim, row ) + implements Gram-Schmidt orthogonalization by rows + + returns: + false if zero norm + overwrites rows[dim][dim] + + notes: + see Golub & van Loan, 1983, Algorithm 6.2-2, "Modified Gram-Schmidt" + 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(qhT *qh, 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 */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="inthresholds">-</a> + + qh_inthresholds(qh, 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(qhT *qh, 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 */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="joggleinput">-</a> + + qh_joggleinput(qh) + 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(qhT *qh) { + 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 * (int)sizeof(coordT); + if (!(qh->first_point= (coordT *)qh_malloc((size_t)size))) { + qh_fprintf(qh, qh->ferr, 6009, "qhull error: insufficient memory to joggle %d points\n", + qh->num_points); + qh_errexit(qh, qh_ERRmem, NULL, NULL); + } + qh->POINTSmalloc= True; + if (qh->JOGGLEmax == 0.0) { + qh->JOGGLEmax= qh_detjoggle(qh, qh->input_points, qh->num_points, qh->hull_dim); + qh_option(qh, "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(qh, "QJoggle", NULL, &qh->JOGGLEmax); + } + if (qh->build_cnt > 1 && qh->JOGGLEmax > fmax_(qh->MAXwidth/4, 0.1)) { + qh_fprintf(qh, qh->ferr, 6010, "qhull input error (qh_joggleinput): 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, qh_ERRinput, NULL, NULL); + } + /* for some reason, using qh->ROTATErandom and qh_RANDOMseed does not repeat the run. Use 'TRn' instead */ + seed= qh_RANDOMint; + qh_option(qh, "_joggle-seed", &seed, NULL); + trace0((qh, qh->ferr, 6, "qh_joggleinput: joggle input by %4.4g 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, qh->hull_dim, qh->num_points, qh->first_point); + } +} /* joggleinput */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="maxabsval">-</a> + + 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 */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="maxmin">-</a> + + qh_maxmin(qh, 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(qhT *qh, 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, qh->ferr, 6011, "qhull error: one or more floating point constants in user_r.h are inconsistent. REALmin %g, -REALmax %g, 0.0, REALepsilon %g, REALmax %g\n", + REALmin, -REALmax, REALepsilon, REALmax); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + set= qh_settemp(qh, 2*dimension); + trace1((qh, qh->ferr, 8082, "qh_maxmin: dim min max width nearzero min-point max-point\n")); + for (k=0; k < dimension; k++) { + if (points == qh->GOODpointp) + minimum= maximum= points + dimension; + else + minimum= maximum= points; + FORALLpoint_(qh, 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->MAXabs_coord; + 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(qh, &set, minimum); + qh_setappend(qh, &set, maximum); + /* calculation of qh NEARzero is based on Golub & van Loan, 1983, + Eq. 4.4-13 for "Gaussian elimination with complete pivoting". + Golub & van Loan say that n^3 can be ignored and 10 be used in + place of rho */ + qh->NEARzero[k]= 80 * qh->MAXsumcoord * REALepsilon; + trace1((qh, qh->ferr, 8106, " %3d % 14.8e % 14.8e % 14.8e %4.4e p%-9d p%-d\n", + k, minimum[k], maximum[k], maximum[k]-minimum[k], qh->NEARzero[k], qh_pointid(qh, minimum), qh_pointid(qh, maximum))); + if (qh->SCALElast && k == dimension-1) + trace1((qh, qh->ferr, 8107, " last coordinate scaled to (%4.4g, %4.4g), width %4.4e for option 'Qbb'\n", + qh->MAXabs_coord - qh->MAXwidth, qh->MAXabs_coord, qh->MAXwidth)); + } + if (qh->IStracing >= 1) + qh_printpoints(qh, qh->ferr, "qh_maxmin: found the max and min points (by dim):", set); + return(set); +} /* maxmin */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="maxouter">-</a> + + qh_maxouter(qh) + 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 + see qh_detmaxoutside for a qh_RATIO... target + + 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(qhT *qh) { + realT dist; + + dist= fmax_(qh->max_outside, qh->DISTround); + dist += qh->DISTround; + trace4((qh, qh->ferr, 4012, "qh_maxouter: max distance from facet to outer plane is %4.4g, qh.max_outside is %4.4g\n", dist, qh->max_outside)); + return dist; +} /* maxouter */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="maxsimplex">-</a> + + qh_maxsimplex(qh, dim, maxpoints, points, numpoints, simplex ) + determines maximum simplex for a set of points + maxpoints is the subset of points with a min or max coordinate + may start with points already in simplex + skips qh.GOODpointp (assumes that it isn't in maxpoints) + + returns: + simplex with dim+1 points + + notes: + called by qh_initialvertices, qh_detvnorm, and qh_voronoi_center + requires qh.MAXwidth to estimate determinate for each vertex + assumes at least needed 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) + create a simplex of dim+1 vertices as follows + add point from maxpoints that maximizes the determinate of the point and the simplex vertices + if last point and maxdet/prevdet < qh_RATIOmaxsimplex (3.0e-2) + flag maybe_falsenarrow + if no maxpoint or maxnearzero or maybe_falsenarrow + search all points for maximum determinate + early exit if maybe_falsenarrow and !maxnearzero and maxdet > prevdet +*/ +void qh_maxsimplex(qhT *qh, int dim, setT *maxpoints, pointT *points, int numpoints, setT **simplex) { + pointT *point, **pointp, *pointtemp, *maxpoint, *minx=NULL, *maxx=NULL; + boolT nearzero, maxnearzero= False, maybe_falsenarrow; + int i, sizinit; + realT maxdet= -1.0, prevdet= -1.0, det, mincoord= REALmax, maxcoord= -REALmax, mindet, ratio, targetdet; + + if (qh->MAXwidth <= 0.0) { + qh_fprintf(qh, qh->ferr, 6421, "qhull internal error (qh_maxsimplex): qh.MAXwidth required for qh_maxsimplex. Used to estimate determinate\n"); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + sizinit= qh_setsize(qh, *simplex); + if (sizinit >= 2) { + maxdet= pow(qh->MAXwidth, sizinit - 1); + }else { + if (qh_setsize(qh, maxpoints) >= 2) { + FOREACHpoint_(maxpoints) { + if (maxcoord < point[0]) { + maxcoord= point[0]; + maxx= point; + } + if (mincoord > point[0]) { + mincoord= point[0]; + minx= point; + } + } + }else { + FORALLpoint_(qh, 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; + } + } + } + maxdet= maxcoord - mincoord; + qh_setunique(qh, simplex, minx); + if (qh_setsize(qh, *simplex) < 2) + qh_setunique(qh, simplex, maxx); + sizinit= qh_setsize(qh, *simplex); + if (sizinit < 2) { + qh_joggle_restart(qh, "input has same x coordinate"); + if (zzval_(Zsetplane) > qh->hull_dim+1) { + qh_fprintf(qh, qh->ferr, 6012, "qhull precision error (qh_maxsimplex for voronoi_center): %d points with the same x coordinate %4.4g\n", + qh_setsize(qh, maxpoints)+numpoints, mincoord); + qh_errexit(qh, qh_ERRprec, NULL, NULL); + }else { + qh_fprintf(qh, qh->ferr, 6013, "qhull input error: input is less than %d-dimensional since all points have the same x coordinate %4.4g\n", + qh->hull_dim, mincoord); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + } + } + for (i=sizinit; i < dim+1; i++) { + prevdet= maxdet; + maxpoint= NULL; + maxdet= -1.0; + FOREACHpoint_(maxpoints) { + if (!qh_setin(*simplex, point) && point != maxpoint) { + det= qh_detsimplex(qh, point, *simplex, i, &nearzero); /* retests maxpoints if duplicate or multiple iterations */ + if ((det= fabs_(det)) > maxdet) { + maxdet= det; + maxpoint= point; + maxnearzero= nearzero; + } + } + } + maybe_falsenarrow= False; + ratio= 1.0; + targetdet= prevdet * qh->MAXwidth; + mindet= 10 * qh_RATIOmaxsimplex * targetdet; + if (maxdet > 0.0) { + ratio= maxdet / targetdet; + if (ratio < qh_RATIOmaxsimplex) + maybe_falsenarrow= True; + } + if (!maxpoint || maxnearzero || maybe_falsenarrow) { + zinc_(Zsearchpoints); + if (!maxpoint) { + trace0((qh, qh->ferr, 7, "qh_maxsimplex: searching all points for %d-th initial vertex, better than mindet %4.4g, targetdet %4.4g\n", + i+1, mindet, targetdet)); + }else if (qh->ALLpoints) { + trace0((qh, qh->ferr, 30, "qh_maxsimplex: searching all points ('Qs') for %d-th initial vertex, better than p%d det %4.4g, targetdet %4.4g, ratio %4.4g\n", + i+1, qh_pointid(qh, maxpoint), maxdet, targetdet, ratio)); + }else if (maybe_falsenarrow) { + trace0((qh, qh->ferr, 17, "qh_maxsimplex: searching all points for %d-th initial vertex, better than p%d det %4.4g and mindet %4.4g, ratio %4.4g\n", + i+1, qh_pointid(qh, maxpoint), maxdet, mindet, ratio)); + }else { + trace0((qh, qh->ferr, 8, "qh_maxsimplex: searching all points for %d-th initial vertex, better than p%d det %2.2g and mindet %4.4g, targetdet %4.4g\n", + i+1, qh_pointid(qh, maxpoint), maxdet, mindet, targetdet)); + } + FORALLpoint_(qh, points, numpoints) { + if (point == qh->GOODpointp) + continue; + if (!qh_setin(maxpoints, point) && !qh_setin(*simplex, point)) { + det= qh_detsimplex(qh, point, *simplex, i, &nearzero); + if ((det= fabs_(det)) > maxdet) { + maxdet= det; + maxpoint= point; + maxnearzero= nearzero; + if (!maxnearzero && !qh->ALLpoints && maxdet > mindet) + break; + } + } + } + } /* !maxpoint */ + if (!maxpoint) { + qh_fprintf(qh, qh->ferr, 6014, "qhull internal error (qh_maxsimplex): not enough points available\n"); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + qh_setappend(qh, simplex, maxpoint); + trace1((qh, qh->ferr, 1002, "qh_maxsimplex: selected point p%d for %d`th initial vertex, det=%4.4g, targetdet=%4.4g, mindet=%4.4g\n", + qh_pointid(qh, maxpoint), i+1, maxdet, prevdet * qh->MAXwidth, mindet)); + } /* i */ +} /* maxsimplex */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="minabsval">-</a> + + 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 */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="mindiff">-</a> + + qh_mindif(qh, 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 */ + + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="orientoutside">-</a> + + qh_orientoutside(qh, facet ) + make facet outside oriented via qh.interior_point + + returns: + True if facet reversed orientation. +*/ +boolT qh_orientoutside(qhT *qh, facetT *facet) { + int k; + realT dist; + + qh_distplane(qh, 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 */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="outerinner">-</a> + + qh_outerinner(qh, 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), 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(qhT *qh, facetT *facet, realT *outerplane, realT *innerplane) { + realT dist, mindist; + vertexT *vertex, **vertexp; + + if (outerplane) { + if (!qh_MAXoutside || !facet || !qh->maxoutdone) { + *outerplane= qh_maxouter(qh); /* 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(qh, 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 */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="pointdist">-</a> + + 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 */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="printmatrix">-</a> + + qh_printmatrix(qh, fp, string, rows, numrow, numcol ) + print matrix to fp given by row vectors + print string as header + qh may be NULL if fp is defined + + notes: + print a vector by qh_printmatrix(qh, fp, "", &vect, 1, len) +*/ +void qh_printmatrix(qhT *qh, FILE *fp, const char *string, realT **rows, int numrow, int numcol) { + realT *rowp; + realT r; /*bug fix*/ + int i,k; + + qh_fprintf(qh, fp, 9001, "%s\n", string); + for (i=0; i < numrow; i++) { + rowp= rows[i]; + for (k=0; k < numcol; k++) { + r= *rowp++; + qh_fprintf(qh, fp, 9002, "%6.3g ", r); + } + qh_fprintf(qh, fp, 9003, "\n"); + } +} /* printmatrix */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="printpoints">-</a> + + qh_printpoints(qh, fp, string, points ) + print pointids to fp for a set of points + if string, prints string and 'p' point ids +*/ +void qh_printpoints(qhT *qh, FILE *fp, const char *string, setT *points) { + pointT *point, **pointp; + + if (string) { + qh_fprintf(qh, fp, 9004, "%s", string); + FOREACHpoint_(points) + qh_fprintf(qh, fp, 9005, " p%d", qh_pointid(qh, point)); + qh_fprintf(qh, fp, 9006, "\n"); + }else { + FOREACHpoint_(points) + qh_fprintf(qh, fp, 9007, " %d", qh_pointid(qh, point)); + qh_fprintf(qh, fp, 9008, "\n"); + } +} /* printpoints */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="projectinput">-</a> + + qh_projectinput(qh) + 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(qhT *qh) { + int k,i; + int newdim= qh->input_dim, newnum= qh->num_points; + signed char *project; + int projectsize= (qh->input_dim + 1) * (int)sizeof(*project); + pointT *newpoints, *coord, *infinity; + realT paraboloid, maxboloid= 0; + + project= (signed char *)qh_memalloc(qh, projectsize); + memset((char *)project, 0, (size_t)projectsize); + for (k=0; k < qh->input_dim; k++) { /* skip Delaunay bound */ + if (qh->lower_bound[k] == 0.0 && qh->upper_bound[k] == 0.0) { + project[k]= -1; + newdim--; + } + } + if (qh->DELAUNAY) { + project[k]= 1; + newdim++; + if (qh->ATinfinity) + newnum++; + } + if (newdim != qh->hull_dim) { + qh_memfree(qh, project, projectsize); + qh_fprintf(qh, qh->ferr, 6015, "qhull internal error (qh_projectinput): dimension after projection %d != hull_dim %d\n", newdim, qh->hull_dim); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + if (!(newpoints= qh->temp_malloc= (coordT *)qh_malloc((size_t)(newnum * newdim) * sizeof(coordT)))) { + qh_memfree(qh, project, projectsize); + qh_fprintf(qh, qh->ferr, 6016, "qhull error: insufficient memory to project %d points\n", + qh->num_points); + qh_errexit(qh, qh_ERRmem, NULL, NULL); + } + /* qh_projectpoints throws error if mismatched dimensions */ + qh_projectpoints(qh, project, qh->input_dim+1, qh->first_point, + qh->num_points, qh->input_dim, newpoints, newdim); + trace1((qh, qh->ferr, 1003, "qh_projectinput: updating lower and upper_bound\n")); + qh_projectpoints(qh, project, qh->input_dim+1, qh->lower_bound, + 1, qh->input_dim+1, qh->lower_bound, newdim+1); + qh_projectpoints(qh, 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_memfree(qh, project, projectsize); + qh_fprintf(qh, qh->ferr, 6017, "qhull internal error (qh_projectinput): HALFspace defined without qh.feasible_point\n"); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + qh_projectpoints(qh, project, qh->input_dim, qh->feasible_point, + 1, qh->input_dim, qh->feasible_point, newdim); + } + qh_memfree(qh, project, projectsize); + if (qh->POINTSmalloc) + qh_free(qh->first_point); + qh->first_point= newpoints; + qh->POINTSmalloc= True; + qh->temp_malloc= NULL; + 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, qh->ferr, 9, "qh_projectinput: projected points to paraboloid for Delaunay\n")); + }else if (qh->DELAUNAY) /* !qh->ATinfinity */ + qh_setdelaunay(qh, qh->hull_dim, qh->num_points, qh->first_point); +} /* projectinput */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="projectpoints">-</a> + + qh_projectpoints(qh, 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(qhT *qh, 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, qh->ferr, 6018, "qhull internal error (qh_projectpoints): newdim %d should be %d after projection\n", + newdim, testdim); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + for (j=0; j<n; j++) { + if (project[j] == -1) + oldk++; + else { + newp= newpoints+newk++; + if (project[j] == +1) { + if (oldk >= dim) + continue; + oldp= points+oldk; + }else + oldp= points+oldk++; + for (i=numpoints; i--; ) { + *newp= *oldp; + newp += newdim; + oldp += dim; + } + } + if (oldk >= dim) + break; + } + trace1((qh, qh->ferr, 1004, "qh_projectpoints: projected %d points from dim %d to dim %d\n", + numpoints, dim, newdim)); +} /* projectpoints */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="rotateinput">-</a> + + qh_rotateinput(qh, rows ) + rotate input using row matrix + input points given by qh->first_point, num_points, hull_dim + assumes rows[dim] is a scratch buffer + if qh->POINTSmalloc, overwrites input points, else mallocs a new array + + returns: + rotated input + sets qh->POINTSmalloc + + design: + see qh_rotatepoints +*/ +void qh_rotateinput(qhT *qh, realT **rows) { + + if (!qh->POINTSmalloc) { + qh->first_point= qh_copypoints(qh, qh->first_point, qh->num_points, qh->hull_dim); + qh->POINTSmalloc= True; + } + qh_rotatepoints(qh, qh->first_point, qh->num_points, qh->hull_dim, rows); +} /* rotateinput */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="rotatepoints">-</a> + + qh_rotatepoints(qh, points, numpoints, dim, row ) + rotate numpoints points by a d-dim row matrix + assumes rows[dim] is a scratch buffer + + returns: + rotated points in place + + design: + for each point + for each coordinate + use row[dim] to compute partial inner product + for each coordinate + rotate by partial inner product +*/ +void qh_rotatepoints(qhT *qh, realT *points, int numpoints, int dim, realT **row) { + realT *point, *rowi, *coord= NULL, sum, *newval; + int i,j,k; + + if (qh->IStracing >= 1) + qh_printmatrix(qh, qh->ferr, "qh_rotatepoints: rotate points by", row, dim, dim); + for (point=points, j=numpoints; j--; point += dim) { + newval= row[dim]; + for (i=0; i < dim; i++) { + rowi= row[i]; + coord= point; + for (sum=0.0, k=dim; k--; ) + sum += *rowi++ * *coord++; + *(newval++)= sum; + } + for (k=dim; k--; ) + *(--coord)= *(--newval); + } +} /* rotatepoints */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="scaleinput">-</a> + + qh_scaleinput(qh) + scale input points using qh->low_bound/high_bound + input points given by qh->first_point, num_points, hull_dim + if qh->POINTSmalloc, overwrites input points, else mallocs a new array + + returns: + scales coordinates of points to low_bound[k], high_bound[k] + sets qh->POINTSmalloc + + design: + see qh_scalepoints +*/ +void qh_scaleinput(qhT *qh) { + + if (!qh->POINTSmalloc) { + qh->first_point= qh_copypoints(qh, qh->first_point, qh->num_points, qh->hull_dim); + qh->POINTSmalloc= True; + } + qh_scalepoints(qh, qh->first_point, qh->num_points, qh->hull_dim, + qh->lower_bound, qh->upper_bound); +} /* scaleinput */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="scalelast">-</a> + + qh_scalelast(qh, points, numpoints, dim, low, high, newhigh ) + scale last coordinate to [0.0, newhigh], for Delaunay triangulation + input points given by points, numpoints, dim + + returns: + changes scale of last coordinate from [low, high] to [0.0, newhigh] + overwrites last coordinate of each point + saves low/high/newhigh in qh.last_low, etc. for qh_setdelaunay() + + notes: + to reduce precision issues, qh_scalelast makes the last coordinate similar to other coordinates + the last coordinate for Delaunay triangulation is the sum of squares of input coordinates + note that the range [0.0, newwidth] is wrong for narrow distributions with large positive coordinates (e.g., [995933.64, 995963.48]) + + when called by qh_setdelaunay, low/high may not match the data passed to qh_setdelaunay + + design: + compute scale and shift factors + apply to last coordinate of each point +*/ +void qh_scalelast(qhT *qh, coordT *points, int numpoints, int dim, coordT low, + coordT high, coordT newhigh) { + realT scale, shift; + coordT *coord, newlow; + int i; + boolT nearzero= False; + + newlow= 0.0; + trace4((qh, qh->ferr, 4013, "qh_scalelast: scale last coordinate from [%2.2g, %2.2g] to [%2.2g, %2.2g]\n", + low, high, newlow, newhigh)); + qh->last_low= low; + qh->last_high= high; + qh->last_newhigh= newhigh; + scale= qh_divzero(newhigh - newlow, high - low, + qh->MINdenom_1, &nearzero); + if (nearzero) { + if (qh->DELAUNAY) + qh_fprintf(qh, qh->ferr, 6019, "qhull input error (qh_scalelast): can not scale last coordinate to [%4.4g, %4.4g]. Input is cocircular or cospherical. Use option 'Qz' to add a point at infinity.\n", + newlow, newhigh); + else + qh_fprintf(qh, qh->ferr, 6020, "qhull input error (qh_scalelast): can not scale last coordinate to [%4.4g, %4.4g]. New bounds are too wide for compared to existing bounds [%4.4g, %4.4g] (width %4.4g)\n", + newlow, newhigh, low, high, high-low); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + shift= newlow - low * scale; + coord= points + dim - 1; + for (i=numpoints; i--; coord += dim) + *coord= *coord * scale + shift; +} /* scalelast */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="scalepoints">-</a> + + qh_scalepoints(qh, points, numpoints, dim, newlows, newhighs ) + scale points to new lowbound and highbound + retains old bound when newlow= -REALmax or newhigh= +REALmax + + returns: + scaled points + overwrites old points + + design: + for each coordinate + compute current low and high bound + compute scale and shift factors + scale all points + enforce new low and high bound for all points +*/ +void qh_scalepoints(qhT *qh, pointT *points, int numpoints, int dim, + realT *newlows, realT *newhighs) { + int i,k; + realT shift, scale, *coord, low, high, newlow, newhigh, mincoord, maxcoord; + boolT nearzero= False; + + for (k=0; k < dim; k++) { + newhigh= newhighs[k]; + newlow= newlows[k]; + if (newhigh > REALmax/2 && newlow < -REALmax/2) + continue; + low= REALmax; + high= -REALmax; + for (i=numpoints, coord=points+k; i--; coord += dim) { + minimize_(low, *coord); + maximize_(high, *coord); + } + if (newhigh > REALmax/2) + newhigh= high; + if (newlow < -REALmax/2) + newlow= low; + if (qh->DELAUNAY && k == dim-1 && newhigh < newlow) { + qh_fprintf(qh, qh->ferr, 6021, "qhull input error: 'Qb%d' or 'QB%d' inverts paraboloid since high bound %.2g < low bound %.2g\n", + k, k, newhigh, newlow); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + scale= qh_divzero(newhigh - newlow, high - low, + qh->MINdenom_1, &nearzero); + if (nearzero) { + qh_fprintf(qh, qh->ferr, 6022, "qhull input error: %d'th dimension's new bounds [%2.2g, %2.2g] too wide for\nexisting bounds [%2.2g, %2.2g]\n", + k, newlow, newhigh, low, high); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + shift= (newlow * high - low * newhigh)/(high-low); + coord= points+k; + for (i=numpoints; i--; coord += dim) + *coord= *coord * scale + shift; + coord= points+k; + if (newlow < newhigh) { + mincoord= newlow; + maxcoord= newhigh; + }else { + mincoord= newhigh; + maxcoord= newlow; + } + for (i=numpoints; i--; coord += dim) { + minimize_(*coord, maxcoord); /* because of roundoff error */ + maximize_(*coord, mincoord); + } + trace0((qh, qh->ferr, 10, "qh_scalepoints: scaled %d'th coordinate [%2.2g, %2.2g] to [%.2g, %.2g] for %d points by %2.2g and shifted %2.2g\n", + k, low, high, newlow, newhigh, numpoints, scale, shift)); + } +} /* scalepoints */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="setdelaunay">-</a> + + qh_setdelaunay(qh, dim, count, points ) + project count points to dim-d paraboloid for Delaunay triangulation + + dim is one more than the dimension of the input set + assumes dim is at least 3 (i.e., at least a 2-d Delaunay triangulation) + + points is a dim*count realT array. The first dim-1 coordinates + are the coordinates of the first input point. array[dim] is + the first coordinate of the second input point. array[2*dim] is + the first coordinate of the third input point. + + if qh.last_low defined (i.e., 'Qbb' called qh_scalelast) + calls qh_scalelast to scale the last coordinate the same as the other points + + returns: + for each point + sets point[dim-1] to sum of squares of coordinates + scale points to 'Qbb' if needed + + notes: + to project one point, use + qh_setdelaunay(qh, qh->hull_dim, 1, point) + + Do not use options 'Qbk', 'QBk', or 'QbB' since they scale + the coordinates after the original projection. + +*/ +void qh_setdelaunay(qhT *qh, int dim, int count, pointT *points) { + int i, k; + coordT *coordp, coord; + realT paraboloid; + + trace0((qh, qh->ferr, 11, "qh_setdelaunay: project %d points to paraboloid for Delaunay triangulation\n", count)); + coordp= points; + for (i=0; i < count; i++) { + coord= *coordp++; + paraboloid= coord*coord; + for (k=dim-2; k--; ) { + coord= *coordp++; + paraboloid += coord*coord; + } + *coordp++= paraboloid; + } + if (qh->last_low < REALmax/2) + qh_scalelast(qh, points, count, dim, qh->last_low, qh->last_high, qh->last_newhigh); +} /* setdelaunay */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="sethalfspace">-</a> + + qh_sethalfspace(qh, dim, coords, nextp, normal, offset, feasible ) + set point to dual of halfspace relative to feasible point + halfspace is normal coefficients and offset. + + returns: + false and prints error if feasible point is outside of hull + overwrites coordinates for point at dim coords + nextp= next point (coords) + does not call qh_errexit + + design: + compute distance from feasible point to halfspace + divide each normal coefficient by -dist +*/ +boolT qh_sethalfspace(qhT *qh, int dim, coordT *coords, coordT **nextp, + coordT *normal, coordT *offset, coordT *feasible) { + coordT *normp= normal, *feasiblep= feasible, *coordp= coords; + realT dist; + realT r; /*bug fix*/ + int k; + boolT zerodiv; + + dist= *offset; + for (k=dim; k--; ) + dist += *(normp++) * *(feasiblep++); + if (dist > 0) + goto LABELerroroutside; + normp= normal; + if (dist < -qh->MINdenom) { + for (k=dim; k--; ) + *(coordp++)= *(normp++) / -dist; + }else { + for (k=dim; k--; ) { + *(coordp++)= qh_divzero(*(normp++), -dist, qh->MINdenom_1, &zerodiv); + if (zerodiv) + goto LABELerroroutside; + } + } + *nextp= coordp; +#ifndef qh_NOtrace + if (qh->IStracing >= 4) { + qh_fprintf(qh, qh->ferr, 8021, "qh_sethalfspace: halfspace at offset %6.2g to point: ", *offset); + for (k=dim, coordp=coords; k--; ) { + r= *coordp++; + qh_fprintf(qh, qh->ferr, 8022, " %6.2g", r); + } + qh_fprintf(qh, qh->ferr, 8023, "\n"); + } +#endif + return True; +LABELerroroutside: + feasiblep= feasible; + normp= normal; + qh_fprintf(qh, qh->ferr, 6023, "qhull input error: feasible point is not clearly inside halfspace\nfeasible point: "); + for (k=dim; k--; ) + qh_fprintf(qh, qh->ferr, 8024, qh_REAL_1, r=*(feasiblep++)); + qh_fprintf(qh, qh->ferr, 8025, "\n halfspace: "); + for (k=dim; k--; ) + qh_fprintf(qh, qh->ferr, 8026, qh_REAL_1, r=*(normp++)); + qh_fprintf(qh, qh->ferr, 8027, "\n at offset: "); + qh_fprintf(qh, qh->ferr, 8028, qh_REAL_1, *offset); + qh_fprintf(qh, qh->ferr, 8029, " and distance: "); + qh_fprintf(qh, qh->ferr, 8030, qh_REAL_1, dist); + qh_fprintf(qh, qh->ferr, 8031, "\n"); + return False; +} /* sethalfspace */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="sethalfspace_all">-</a> + + qh_sethalfspace_all(qh, dim, count, halfspaces, feasible ) + generate dual for halfspace intersection with feasible point + array of count halfspaces + each halfspace is normal coefficients followed by offset + the origin is inside the halfspace if the offset is negative + feasible is a point inside all halfspaces (http://www.qhull.org/html/qhalf.htm#notes) + + returns: + malloc'd array of count X dim-1 points + + notes: + call before qh_init_B or qh_initqhull_globals + free memory when done + unused/untested code: please email bradb@shore.net if this works ok for you + if using option 'Fp', qh.feasible_point must be set (e.g., to 'feasible') + qh->feasible_point is a malloc'd array that is freed by qh_freebuffers. + + design: + see qh_sethalfspace +*/ +coordT *qh_sethalfspace_all(qhT *qh, int dim, int count, coordT *halfspaces, pointT *feasible) { + int i, newdim; + pointT *newpoints; + coordT *coordp, *normalp, *offsetp; + + trace0((qh, qh->ferr, 12, "qh_sethalfspace_all: compute dual for halfspace intersection\n")); + newdim= dim - 1; + if (!(newpoints= (coordT *)qh_malloc((size_t)(count * newdim) * sizeof(coordT)))){ + qh_fprintf(qh, qh->ferr, 6024, "qhull error: insufficient memory to compute dual of %d halfspaces\n", + count); + qh_errexit(qh, qh_ERRmem, NULL, NULL); + } + coordp= newpoints; + normalp= halfspaces; + for (i=0; i < count; i++) { + offsetp= normalp + newdim; + if (!qh_sethalfspace(qh, newdim, coordp, &coordp, normalp, offsetp, feasible)) { + qh_free(newpoints); /* feasible is not inside halfspace as reported by qh_sethalfspace */ + qh_fprintf(qh, qh->ferr, 8032, "The halfspace was at index %d\n", i); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + normalp= offsetp + 1; + } + return newpoints; +} /* sethalfspace_all */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="sharpnewfacets">-</a> + + qh_sharpnewfacets(qh) + + returns: + true if could be an acute angle (facets in different quadrants) + + notes: + for qh_findbest + + design: + for all facets on qh.newfacet_list + if two facets are in different quadrants + set issharp +*/ +boolT qh_sharpnewfacets(qhT *qh) { + facetT *facet; + boolT issharp= False; + int *quadrant, k; + + quadrant= (int *)qh_memalloc(qh, qh->hull_dim * (int)sizeof(int)); + FORALLfacet_(qh->newfacet_list) { + if (facet == qh->newfacet_list) { + for (k=qh->hull_dim; k--; ) + quadrant[ k]= (facet->normal[ k] > 0); + }else { + for (k=qh->hull_dim; k--; ) { + if (quadrant[ k] != (facet->normal[ k] > 0)) { + issharp= True; + break; + } + } + } + if (issharp) + break; + } + qh_memfree(qh, quadrant, qh->hull_dim * (int)sizeof(int)); + trace3((qh, qh->ferr, 3001, "qh_sharpnewfacets: %d\n", issharp)); + return issharp; +} /* sharpnewfacets */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="vertex_bestdist">-</a> + + qh_vertex_bestdist(qh, vertices ) + qh_vertex_bestdist2(qh, vertices, vertexp, vertexp2 ) + return nearest distance between vertices + optionally returns vertex and vertex2 + + notes: + called by qh_partitioncoplanar, qh_mergefacet, qh_check_maxout, qh_checkpoint +*/ +coordT qh_vertex_bestdist(qhT *qh, setT *vertices) { + vertexT *vertex, *vertex2; + + return qh_vertex_bestdist2(qh, vertices, &vertex, &vertex2); +} /* vertex_bestdist */ + +coordT qh_vertex_bestdist2(qhT *qh, setT *vertices, vertexT **vertexp/*= NULL*/, vertexT **vertexp2/*= NULL*/) { + vertexT *vertex, *vertexA, *bestvertex= NULL, *bestvertex2= NULL; + coordT dist, bestdist= REALmax; + int k, vertex_i, vertex_n; + + FOREACHvertex_i_(qh, vertices) { + for (k= vertex_i+1; k < vertex_n; k++) { + vertexA= SETelemt_(vertices, k, vertexT); + dist= qh_pointdist(vertex->point, vertexA->point, -qh->hull_dim); + if (dist < bestdist) { + bestdist= dist; + bestvertex= vertex; + bestvertex2= vertexA; + } + } + } + *vertexp= bestvertex; + *vertexp2= bestvertex2; + return sqrt(bestdist); +} /* vertex_bestdist */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="voronoi_center">-</a> + + qh_voronoi_center(qh, dim, points ) + return Voronoi center for a set of points + dim is the orginal dimension of the points + gh.gm_matrix/qh.gm_row are scratch buffers + + returns: + center as a temporary point (qh_memalloc) + if non-simplicial, + returns center for max simplex of points + + notes: + only called by qh_facetcenter + from Bowyer & Woodwark, A Programmer's Geometry, 1983, p. 65 + + design: + if non-simplicial + determine max simplex for points + translate point0 of simplex to origin + compute sum of squares of diagonal + compute determinate + compute Voronoi center (see Bowyer & Woodwark) +*/ +pointT *qh_voronoi_center(qhT *qh, int dim, setT *points) { + pointT *point, **pointp, *point0; + pointT *center= (pointT *)qh_memalloc(qh, qh->center_size); + setT *simplex; + int i, j, k, size= qh_setsize(qh, points); + coordT *gmcoord; + realT *diffp, sum2, *sum2row, *sum2p, det, factor; + boolT nearzero, infinite; + + if (size == dim+1) + simplex= points; + else if (size < dim+1) { + qh_memfree(qh, center, qh->center_size); + qh_fprintf(qh, qh->ferr, 6025, "qhull internal error (qh_voronoi_center): need at least %d points to construct a Voronoi center\n", + dim+1); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + simplex= points; /* never executed -- avoids warning */ + }else { + simplex= qh_settemp(qh, dim+1); + qh_maxsimplex(qh, dim, points, NULL, 0, &simplex); + } + point0= SETfirstt_(simplex, pointT); + gmcoord= qh->gm_matrix; + for (k=0; k < dim; k++) { + qh->gm_row[k]= gmcoord; + FOREACHpoint_(simplex) { + if (point != point0) + *(gmcoord++)= point[k] - point0[k]; + } + } + sum2row= gmcoord; + for (i=0; i < dim; i++) { + sum2= 0.0; + for (k=0; k < dim; k++) { + diffp= qh->gm_row[k] + i; + sum2 += *diffp * *diffp; + } + *(gmcoord++)= sum2; + } + det= qh_determinant(qh, qh->gm_row, dim, &nearzero); + factor= qh_divzero(0.5, det, qh->MINdenom, &infinite); + if (infinite) { + for (k=dim; k--; ) + center[k]= qh_INFINITE; + if (qh->IStracing) + qh_printpoints(qh, qh->ferr, "qh_voronoi_center: at infinity for ", simplex); + }else { + for (i=0; i < dim; i++) { + gmcoord= qh->gm_matrix; + sum2p= sum2row; + for (k=0; k < dim; k++) { + qh->gm_row[k]= gmcoord; + if (k == i) { + for (j=dim; j--; ) + *(gmcoord++)= *sum2p++; + }else { + FOREACHpoint_(simplex) { + if (point != point0) + *(gmcoord++)= point[k] - point0[k]; + } + } + } + center[i]= qh_determinant(qh, qh->gm_row, dim, &nearzero)*factor + point0[i]; + } +#ifndef qh_NOtrace + if (qh->IStracing >= 3) { + qh_fprintf(qh, qh->ferr, 3061, "qh_voronoi_center: det %2.2g factor %2.2g ", det, factor); + qh_printmatrix(qh, qh->ferr, "center:", ¢er, 1, dim); + if (qh->IStracing >= 5) { + qh_printpoints(qh, qh->ferr, "points", simplex); + FOREACHpoint_(simplex) + qh_fprintf(qh, qh->ferr, 8034, "p%d dist %.2g, ", qh_pointid(qh, point), + qh_pointdist(point, center, dim)); + qh_fprintf(qh, qh->ferr, 8035, "\n"); + } + } +#endif + } + if (simplex != points) + qh_settempfree(qh, &simplex); + return center; +} /* voronoi_center */ + diff --git a/contrib/libs/qhull/libqhull_r/geom_r.c b/contrib/libs/qhull/libqhull_r/geom_r.c new file mode 100644 index 0000000000..22faead499 --- /dev/null +++ b/contrib/libs/qhull/libqhull_r/geom_r.c @@ -0,0 +1,1284 @@ +/*<html><pre> -<a href="qh-geom_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + geom_r.c + geometric routines of qhull + + see qh-geom_r.htm and geom_r.h + + Copyright (c) 1993-2020 The Geometry Center. + $Id: //main/2019/qhull/src/libqhull_r/geom_r.c#5 $$Change: 2953 $ + $DateTime: 2020/05/21 22:05:32 $$Author: bbarber $ + + infrequent code goes into geom2_r.c +*/ + +#include "qhull_ra.h" + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="distplane">-</a> + + qh_distplane(qh, 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) + for nearly coplanar points, the returned values may be duplicates + for example pairs of nearly incident points, rbox 175 C1,2e-13 t1538759579 | qhull d T4 + 622 qh_distplane: e-014 # count of two or more duplicate values for unique calls + 258 qh_distplane: e-015 + 38 qh_distplane: e-016 + 40 qh_distplane: e-017 + 6 qh_distplane: e-018 + 5 qh_distplane: -e-018 + 33 qh_distplane: -e-017 + 3153 qh_distplane: -2.775557561562891e-017 # duplicated value for 3153 unique calls + 42 qh_distplane: -e-016 + 307 qh_distplane: -e-015 + 1271 qh_distplane: -e-014 + 13 qh_distplane: -e-013 + + see: + qh_distnorm in geom2_r.c + qh_distplane [geom_r.c], QhullFacet::distance, and QhullHyperplane::distance are copies +*/ +void qh_distplane(qhT *qh, 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; + } + zzinc_(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; + } +#ifndef qh_NOtrace + if (qh->IStracing >= 4) { + qh_fprintf(qh, qh->ferr, 8001, "qh_distplane: "); + qh_fprintf(qh, qh->ferr, 8002, qh_REAL_1, *dist); + qh_fprintf(qh, qh->ferr, 8003, "from p%d to f%d\n", qh_pointid(qh, point), facet->id); + } +#endif + return; +} /* distplane */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="findbest">-</a> + + qh_findbest(qh, 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(qhT *qh, 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); +#ifndef qh_NOtrace + if (qh->IStracing >= 4 || (qh->TRACElevel && qh->TRACEpoint >= 0 && qh->TRACEpoint == qh_pointid(qh, point))) { + if (qh->TRACElevel > qh->IStracing) + qh->IStracing= qh->TRACElevel; + qh_fprintf(qh, qh->ferr, 8004, "qh_findbest: point p%d starting at f%d isnewfacets? %d, unless %d exit if > %2.2g,", + qh_pointid(qh, point), startfacet->id, isnewfacets, bestoutside, qh->MINoutside); + qh_fprintf(qh, qh->ferr, 8005, " testhorizon? %d, noupper? %d,", testhorizon, noupper); + qh_fprintf(qh, qh->ferr, 8006, " Last qh_addpoint p%d,", qh->furthest_id); + qh_fprintf(qh, qh->ferr, 8007, " Last merge #%d, max_outside %2.2g\n", zzval_(Ztotmerge), qh->max_outside); + } +#endif + if (isoutside) + *isoutside= True; + if (!startfacet->flipped) { /* test startfacet before testing its neighbors */ + *numpart= 1; + qh_distplane(qh, 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, 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(qh, 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) { /* startfacet is upperdelaunay (or flipped) w/o !flipped newfacet neighbors */ + bestdist= -REALmax/2; + bestfacet= qh_findbestnew(qh, point, qh->newfacet_list, &bestdist, bestoutside, isoutside, &numpartnew); + testhorizon= False; /* qh_findbestnew calls qh_findbesthorizon */ + }else if (!qh->findbest_notsharp && bestdist < -qh->DISTround) { + if (qh_sharpnewfacets(qh)) { + /* seldom used, qh_findbestnew will retest all facets */ + zinc_(Zfindnewsharp); + bestfacet= qh_findbestnew(qh, 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(qh, lastfacet, point, &bestdist, numpart); /* lastfacet is non-NULL because startfacet is non-NULL */ + if (testhorizon) /* qh_findbestnew not called */ + bestfacet= qh_findbesthorizon(qh, !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 */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="findbesthorizon">-</a> + + qh_findbesthorizon(qh, 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, including *bestdist and *numpart + qh.coplanarfacetset used to maintain current search set, reset whenever best facet is substantially better + returns(ischeckmax): + best facet + updates f.maxoutside for neighbors of searched facets (if qh_MAXoutside) + returns(!ischeckmax): + best facet that is not upperdelaunay or newfacet (qh.first_newfacet) + allows upperdelaunay that is clearly outside + returns: + bestdist is distance to bestfacet + numpart -- updates number of distance tests + + notes: + called by qh_findbest if point is not outside a facet (directed search) + called by qh_findbestnew if point is not outside a new facet + called by qh_check_maxout for each point in hull + called by qh_check_bestdist for each point in hull (rarely used) + + 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(qhT *qh, 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, numcoplanar= 0, numfacet= 0; + unsigned int visitid= ++qh->visit_id; + boolT newbest= False; /* for tracing */ + realT minsearch, searchdist; /* skip facets that are too far from point */ + boolT is_5x_minsearch; + + if (!ischeckmax) { + zinc_(Zfindhorizon); + }else { +#if qh_MAXoutside + if ((!qh->ONLYgood || startfacet->good) && *bestdist > startfacet->maxoutside) + startfacet->maxoutside= *bestdist; +#endif + } + searchdist= qh_SEARCHdist; /* an expression, a 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; + startfacet->visitid= visitid; + facet= startfacet; + while (True) { + numfacet++; + is_5x_minsearch= (ischeckmax && facet->nummerge > 10 && qh_setsize(qh, facet->neighbors) > 100); /* QH11033 FIX: qh_findbesthorizon: many tests for facets with many merges and neighbors. Can hide coplanar facets, e.g., 'rbox 1000 s Z1 G1e-13' with 4400+ neighbors */ + trace4((qh, qh->ferr, 4002, "qh_findbesthorizon: test neighbors of f%d bestdist %2.2g f%d ischeckmax? %d noupper? %d minsearch %2.2g is_5x? %d searchdist %2.2g\n", + facet->id, *bestdist, getid_(bestfacet), ischeckmax, noupper, + minsearch, is_5x_minsearch, searchdist)); + FOREACHneighbor_(facet) { + if (neighbor->visitid == visitid) + continue; + neighbor->visitid= visitid; + if (!neighbor->flipped) { /* neighbors of flipped facets always searched via nextfacet */ + qh_distplane(qh, point, neighbor, &dist); /* duplicate qh_distpane for new facets, they may be coplanar */ + (*numpart)++; + if (dist > *bestdist) { + if (!neighbor->upperdelaunay || ischeckmax || (!noupper && dist >= qh->MINoutside)) { + if (!ischeckmax) { + minsearch= dist - searchdist; + if (dist > *bestdist + searchdist) { + zinc_(Zfindjump); /* everything in qh.coplanarfacetset at least searchdist below */ + coplanarfacetset_size= 0; + } + } + bestfacet= neighbor; + *bestdist= dist; + newbest= True; + } + }else if (is_5x_minsearch) { + if (dist < 5 * minsearch) + continue; /* skip this neighbor, do not set nextfacet. dist is negative */ + }else if (dist < minsearch) + continue; /* skip this neighbor, do not set nextfacet. If ischeckmax, dist can't be positive */ +#if qh_MAXoutside + if (ischeckmax && dist > neighbor->maxoutside) + neighbor->maxoutside= dist; +#endif + } /* end of !flipped, need to search neighbor */ + if (nextfacet) { + numcoplanar++; + if (!coplanarfacetset_size++) { + SETfirst_(qh->coplanarfacetset)= nextfacet; + SETtruncate_(qh->coplanarfacetset, 1); + }else + qh_setappend(qh, &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, i.e., "for each facet in qh.coplanarfacetset" */ + if (!ischeckmax) { + zadd_(Zfindhorizontot, *numpart - numpartinit); + zmax_(Zfindhorizonmax, *numpart - numpartinit); + if (newbest) + zinc_(Znewbesthorizon); + } + trace4((qh, qh->ferr, 4003, "qh_findbesthorizon: p%d, newbest? %d, bestfacet f%d, bestdist %2.2g, numfacet %d, coplanarfacets %d, numdist %d\n", + qh_pointid(qh, point), newbest, getid_(bestfacet), *bestdist, numfacet, numcoplanar, *numpart - numpartinit)); + return bestfacet; +} /* findbesthorizon */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="findbestnew">-</a> + + qh_findbestnew(qh, 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(qhT *qh, 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 || !startfacet->next) { + if (qh->MERGING) { + qh_fprintf(qh, qh->ferr, 6001, "qhull topology error (qh_findbestnew): merging has formed and deleted a cone of new facets. Can not continue.\n"); + qh_errexit(qh, qh_ERRtopology, NULL, NULL); + }else { + qh_fprintf(qh, qh->ferr, 6002, "qhull internal error (qh_findbestnew): no new facets for point p%d\n", + qh->furthest_id); + qh_errexit(qh, 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_r.h */ + } + if (isoutside) + *isoutside= True; + *numpart= 0; +#ifndef qh_NOtrace + if (qh->IStracing >= 4 || (qh->TRACElevel && qh->TRACEpoint >= 0 && qh->TRACEpoint == qh_pointid(qh, point))) { + if (qh->TRACElevel > qh->IStracing) + qh->IStracing= qh->TRACElevel; + qh_fprintf(qh, qh->ferr, 8008, "qh_findbestnew: point p%d facet f%d. Stop? %d if dist > %2.2g,", + qh_pointid(qh, point), startfacet->id, isdistoutside, distoutside); + qh_fprintf(qh, qh->ferr, 8009, " Last qh_addpoint p%d, qh.visit_id %d, vertex_visit %d,", qh->furthest_id, visitid, qh->vertex_visit); + qh_fprintf(qh, qh->ferr, 8010, " Last merge #%d\n", zzval_(Ztotmerge)); + } +#endif + /* 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(qh, 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) /* testhorizon is always True. Keep the same code as qh_findbest */ + bestfacet= qh_findbesthorizon(qh, !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, qh->ferr, 4004, "qh_findbestnew: bestfacet f%d bestdist %2.2g for p%d f%d bestoutside? %d \n", + getid_(bestfacet), *dist, qh_pointid(qh, point), startfacet->id, bestoutside)); + qh->IStracing= oldtrace; + return bestfacet; +} /* findbestnew */ + +/* ============ hyperplane functions -- keep code together [?] ============ */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="backnormal">-</a> + + qh_backnormal(qh, 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, 1983, Eq. 4.4-9 for "Gaussian elimination with complete pivoting" + + 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(qhT *qh, 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) { + *nearzero= True; + trace4((qh, qh->ferr, 4005, "qh_backnormal: zero diagonal at column %d.\n", i)); + zzinc_(Zback0); + qh_joggle_restart(qh, "zero diagonal on back substitution"); + } +} /* backnormal */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="gausselim">-</a> + + qh_gausselim(qh, 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(qhT *qh, 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 */ +#ifndef qh_NOtrace + if (qh->IStracing >= 4) { + qh_fprintf(qh, qh->ferr, 8011, "qh_gausselim: 0 pivot at column %d. (%2.2g < %2.2g)\n", k, pivot_abs, qh->DISTround); + qh_printmatrix(qh, qh->ferr, "Matrix:", rows, numrow, numcol); + } +#endif + zzinc_(Zgauss0); + qh_joggle_restart(qh, "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, qh->ferr, "qh_gausselem: result", rows, numrow, numcol); +} /* gausselim */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="getangle">-</a> + + qh_getangle(qh, 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(qhT *qh, 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, qh->ferr, 4006, "qh_getangle: %4.4g\n", angle)); + return(angle); +} /* getangle */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="getcenter">-</a> + + qh_getcenter(qh, vertices ) + returns arithmetic center of a set of vertices as a new point + + notes: + allocates point array for center +*/ +pointT *qh_getcenter(qhT *qh, setT *vertices) { + int k; + pointT *center, *coord; + vertexT *vertex, **vertexp; + int count= qh_setsize(qh, vertices); + + if (count < 2) { + qh_fprintf(qh, qh->ferr, 6003, "qhull internal error (qh_getcenter): not defined for %d points\n", count); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + center= (pointT *)qh_memalloc(qh, 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; /* count>=2 by QH6003 */ + } + return(center); +} /* getcenter */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="getcentrum">-</a> + + qh_getcentrum(qh, facet ) + returns the centrum for a facet as a new point + + notes: + allocates the centrum +*/ +pointT *qh_getcentrum(qhT *qh, facetT *facet) { + realT dist; + pointT *centrum, *point; + + point= qh_getcenter(qh, facet->vertices); + zzinc_(Zcentrumtests); + qh_distplane(qh, point, facet, &dist); + centrum= qh_projectpoint(qh, point, facet, dist); + qh_memfree(qh, point, qh->normal_size); + trace4((qh, qh->ferr, 4007, "qh_getcentrum: for f%d, %d vertices dist= %2.2g\n", + facet->id, qh_setsize(qh, facet->vertices), dist)); + return centrum; +} /* getcentrum */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="getdistance">-</a> + + qh_getdistance(qh, facet, neighbor, mindist, maxdist ) + returns the min and max distance to neighbor of non-neighbor vertices in facet + + returns: + the max absolute value + + design: + for each vertex of facet that is not in neighbor + test the distance from vertex to neighbor +*/ +coordT qh_getdistance(qhT *qh, facetT *facet, facetT *neighbor, coordT *mindist, coordT *maxdist) { + vertexT *vertex, **vertexp; + coordT 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(qh, 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 */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="normalize">-</a> + + qh_normalize(qh, normal, dim, toporient ) + normalize a vector and report if too small + does not use min norm + + see: + qh_normalize2 +*/ +void qh_normalize(qhT *qh, coordT *normal, int dim, boolT toporient) { + qh_normalize2(qh, normal, dim, toporient, NULL, NULL); +} /* normalize */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="normalize2">-</a> + + qh_normalize2(qh, 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(qhT *qh, 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); + /* qh_joggle_restart ignored for Znearlysingular, normal part of qh_sethyperplane_gauss */ + trace0((qh, qh->ferr, 1, "qh_normalize: norm=%2.2g too small during p%d\n", + norm, qh->furthest_id)); + return; + } + } + } +} /* normalize */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="projectpoint">-</a> + + qh_projectpoint(qh, 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(qhT *qh, pointT *point, facetT *facet, realT dist) { + pointT *newpoint, *np, *normal; + int normsize= qh->normal_size; + int k; + void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */ + + qh_memalloc_(qh, normsize, freelistp, newpoint, pointT); + np= newpoint; + normal= facet->normal; + for (k=qh->hull_dim; k--; ) + *(np++)= *point++ - dist * *normal++; + return(newpoint); +} /* projectpoint */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="setfacetplane">-</a> + + qh_setfacetplane(qh, 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(qhT *qh, facetT *facet) { + pointT *point; + vertexT *vertex, **vertexp; + int normsize= qh->normal_size; + int k,i, oldtrace= 0; + realT dist; + void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */ + coordT *coord, *gmcoord; + pointT *point0= SETfirstt_(facet->vertices, vertexT)->point; + boolT nearzero= False; + + zzinc_(Zsetplane); + if (!facet->normal) + qh_memalloc_(qh, normsize, freelistp, facet->normal, coordT); +#ifndef qh_NOtrace + if (facet == qh->tracefacet) { + oldtrace= qh->IStracing; + qh->IStracing= 5; + qh_fprintf(qh, qh->ferr, 8012, "qh_setfacetplane: facet f%d created.\n", facet->id); + qh_fprintf(qh, qh->ferr, 8013, " Last point added to hull was p%d.", qh->furthest_id); + if (zzval_(Ztotmerge)) + qh_fprintf(qh, qh->ferr, 8014, " Last merge was #%d.", zzval_(Ztotmerge)); + qh_fprintf(qh, qh->ferr, 8015, "\n\nCurrent summary is:\n"); + qh_printsummary(qh, qh->ferr); + } +#endif + 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, qh->RANDOMa, qh->RANDOMb); + } + }else { + FOREACHvertex_(facet->vertices) + qh->gm_row[i++]= vertex->point; + } + qh_sethyperplane_det(qh, 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, qh->RANDOMa, qh->RANDOMb); + } + } + qh_sethyperplane_gauss(qh, qh->hull_dim, qh->gm_row, point0, facet->toporient, + facet->normal, &facet->offset, &nearzero); + if (nearzero) { + if (qh_orientoutside(qh, facet)) { + trace0((qh, qh->ferr, 2, "qh_setfacetplane: flipped orientation due to nearzero gauss and interior_point test. 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(qh, 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(qh) */ + if (dist > qh->TRACEdist) + istrace= True; + } + }else if (-dist > qh->TRACEdist) + istrace= True; + if (istrace) { + qh_fprintf(qh, qh->ferr, 3060, "qh_setfacetplane: ====== vertex p%d(v%d) increases max_outside to %2.2g for new facet f%d last p%d\n", + qh_pointid(qh, vertex->point), vertex->id, dist, facet->id, qh->furthest_id); + qh_errprint(qh, "DISTANT", facet, NULL, NULL, NULL); + } + } + } + qh->RANDOMdist= qh->old_randomdist; + } +#ifndef qh_NOtrace + if (qh->IStracing >= 4) { + qh_fprintf(qh, 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, qh->ferr, 8018, "%2.2g ", facet->normal[k]); + qh_fprintf(qh, qh->ferr, 8019, "\n"); + } +#endif + qh_checkflipped(qh, facet, NULL, qh_ALL); + if (facet == qh->tracefacet) { + qh->IStracing= oldtrace; + qh_printfacet(qh, qh->ferr, facet); + } +} /* setfacetplane */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="sethyperplane_det">-</a> + + qh_sethyperplane_det(qh, 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(qhT *qh, 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(qh, 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(qh, 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(qh, 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); + /* qh_joggle_restart not needed, will call qh_sethyperplane_gauss instead */ + trace0((qh, qh->ferr, 3, "qh_sethyperplane_det: degenerate norm during p%d, use qh_sethyperplane_gauss instead.\n", qh->furthest_id)); + } +} /* sethyperplane_det */ + + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="sethyperplane_gauss">-</a> + + qh_sethyperplane_gauss(qh, 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(qhT *qh, 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(qh, rows, dim-1, dim, &sign, nearzero); + for (k=dim-1; k--; ) { + if ((rows[k])[k] < 0) + sign ^= 1; + } + if (*nearzero) { + zzinc_(Znearlysingular); + /* qh_joggle_restart ignored for Znearlysingular, normal part of qh_sethyperplane_gauss */ + trace0((qh, qh->ferr, 4, "qh_sethyperplane_gauss: nearly singular or axis parallel hyperplane during p%d.\n", qh->furthest_id)); + qh_backnormal(qh, rows, dim-1, dim, sign, normal, &nearzero2); + }else { + qh_backnormal(qh, rows, dim-1, dim, sign, normal, &nearzero2); + if (nearzero2) { + zzinc_(Znearlysingular); + trace0((qh, 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(qh, 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/contrib/libs/qhull/libqhull_r/geom_r.h b/contrib/libs/qhull/libqhull_r/geom_r.h new file mode 100644 index 0000000000..f3f8ee8140 --- /dev/null +++ b/contrib/libs/qhull/libqhull_r/geom_r.h @@ -0,0 +1,189 @@ +/*<html><pre> -<a href="qh-geom_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + geom_r.h + header file for geometric routines + + see qh-geom_r.htm and geom_r.c + + Copyright (c) 1993-2020 The Geometry Center. + $Id: //main/2019/qhull/src/libqhull_r/geom_r.h#2 $$Change: 2953 $ + $DateTime: 2020/05/21 22:05:32 $$Author: bbarber $ +*/ + +#ifndef qhDEFgeom +#define qhDEFgeom 1 + +#include "libqhull_r.h" + +/* ============ -macros- ======================== */ + +/*-<a href="qh-geom_r.htm#TOC" + >--------------------------------</a><a name="fabs_">-</a> + + fabs_(a) + returns the absolute value of a +*/ +#define fabs_( a ) ((( a ) < 0 ) ? -( a ):( a )) + +/*-<a href="qh-geom_r.htm#TOC" + >--------------------------------</a><a name="fmax_">-</a> + + fmax_(a,b) + returns the maximum value of a and b +*/ +#define fmax_( a,b ) ( ( a ) < ( b ) ? ( b ) : ( a ) ) + +/*-<a href="qh-geom_r.htm#TOC" + >--------------------------------</a><a name="fmin_">-</a> + + fmin_(a,b) + returns the minimum value of a and b +*/ +#define fmin_( a,b ) ( ( a ) > ( b ) ? ( b ) : ( a ) ) + +/*-<a href="qh-geom_r.htm#TOC" + >--------------------------------</a><a name="maximize_">-</a> + + maximize_(maxval, val) + set maxval to val if val is greater than maxval +*/ +#define maximize_( maxval, val ) { if (( maxval ) < ( val )) ( maxval )= ( val ); } + +/*-<a href="qh-geom_r.htm#TOC" + >--------------------------------</a><a name="minimize_">-</a> + + minimize_(minval, val) + set minval to val if val is less than minval +*/ +#define minimize_( minval, val ) { if (( minval ) > ( val )) ( minval )= ( val ); } + +/*-<a href="qh-geom_r.htm#TOC" + >--------------------------------</a><a name="det2_">-</a> + + det2_(a1, a2, + b1, b2) + + compute a 2-d determinate +*/ +#define det2_( a1,a2,b1,b2 ) (( a1 )*( b2 ) - ( a2 )*( b1 )) + +/*-<a href="qh-geom_r.htm#TOC" + >--------------------------------</a><a name="det3_">-</a> + + 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 ) ) + +/*-<a href="qh-geom_r.htm#TOC" + >--------------------------------</a><a name="dX">-</a> + + 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 ======= */ + +#ifdef __cplusplus +extern "C" { +#endif + +void qh_backnormal(qhT *qh, realT **rows, int numrow, int numcol, boolT sign, coordT *normal, boolT *nearzero); +void qh_distplane(qhT *qh, pointT *point, facetT *facet, realT *dist); +facetT *qh_findbest(qhT *qh, pointT *point, facetT *startfacet, + boolT bestoutside, boolT isnewfacets, boolT noupper, + realT *dist, boolT *isoutside, int *numpart); +facetT *qh_findbesthorizon(qhT *qh, boolT ischeckmax, pointT *point, + facetT *startfacet, boolT noupper, realT *bestdist, int *numpart); +facetT *qh_findbestnew(qhT *qh, pointT *point, facetT *startfacet, realT *dist, + boolT bestoutside, boolT *isoutside, int *numpart); +void qh_gausselim(qhT *qh, realT **rows, int numrow, int numcol, boolT *sign, boolT *nearzero); +realT qh_getangle(qhT *qh, pointT *vect1, pointT *vect2); +pointT *qh_getcenter(qhT *qh, setT *vertices); +pointT *qh_getcentrum(qhT *qh, facetT *facet); +coordT qh_getdistance(qhT *qh, facetT *facet, facetT *neighbor, coordT *mindist, coordT *maxdist); +void qh_normalize(qhT *qh, coordT *normal, int dim, boolT toporient); +void qh_normalize2(qhT *qh, coordT *normal, int dim, boolT toporient, + realT *minnorm, boolT *ismin); +pointT *qh_projectpoint(qhT *qh, pointT *point, facetT *facet, realT dist); + +void qh_setfacetplane(qhT *qh, facetT *newfacets); +void qh_sethyperplane_det(qhT *qh, int dim, coordT **rows, coordT *point0, + boolT toporient, coordT *normal, realT *offset, boolT *nearzero); +void qh_sethyperplane_gauss(qhT *qh, int dim, coordT **rows, pointT *point0, + boolT toporient, coordT *normal, coordT *offset, boolT *nearzero); +boolT qh_sharpnewfacets(qhT *qh); + +/*========= infrequently used code in geom2_r.c =============*/ + +coordT *qh_copypoints(qhT *qh, coordT *points, int numpoints, int dimension); +void qh_crossproduct(int dim, realT vecA[3], realT vecB[3], realT vecC[3]); +realT qh_determinant(qhT *qh, realT **rows, int dim, boolT *nearzero); +realT qh_detjoggle(qhT *qh, pointT *points, int numpoints, int dimension); +void qh_detmaxoutside(qhT *qh); +void qh_detroundoff(qhT *qh); +realT qh_detsimplex(qhT *qh, pointT *apex, setT *points, int dim, boolT *nearzero); +realT qh_distnorm(int dim, pointT *point, pointT *normal, realT *offsetp); +realT qh_distround(qhT *qh, int dimension, realT maxabs, realT maxsumabs); +realT qh_divzero(realT numer, realT denom, realT mindenom1, boolT *zerodiv); +realT qh_facetarea(qhT *qh, facetT *facet); +realT qh_facetarea_simplex(qhT *qh, int dim, coordT *apex, setT *vertices, + vertexT *notvertex, boolT toporient, coordT *normal, realT *offset); +pointT *qh_facetcenter(qhT *qh, setT *vertices); +facetT *qh_findgooddist(qhT *qh, pointT *point, facetT *facetA, realT *distp, facetT **facetlist); +vertexT *qh_furthestnewvertex(qhT *qh, unsigned int unvisited, facetT *facet, realT *maxdistp /* qh.newvertex_list */); +vertexT *qh_furthestvertex(qhT *qh, facetT *facetA, facetT *facetB, realT *maxdistp, realT *mindistp); +void qh_getarea(qhT *qh, facetT *facetlist); +boolT qh_gram_schmidt(qhT *qh, int dim, realT **rows); +boolT qh_inthresholds(qhT *qh, coordT *normal, realT *angle); +void qh_joggleinput(qhT *qh); +realT *qh_maxabsval(realT *normal, int dim); +setT *qh_maxmin(qhT *qh, pointT *points, int numpoints, int dimension); +realT qh_maxouter(qhT *qh); +void qh_maxsimplex(qhT *qh, 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(qhT *qh, facetT *facet); +void qh_outerinner(qhT *qh, facetT *facet, realT *outerplane, realT *innerplane); +coordT qh_pointdist(pointT *point1, pointT *point2, int dim); +void qh_printmatrix(qhT *qh, FILE *fp, const char *string, realT **rows, int numrow, int numcol); +void qh_printpoints(qhT *qh, FILE *fp, const char *string, setT *points); +void qh_projectinput(qhT *qh); +void qh_projectpoints(qhT *qh, signed char *project, int n, realT *points, + int numpoints, int dim, realT *newpoints, int newdim); +void qh_rotateinput(qhT *qh, realT **rows); +void qh_rotatepoints(qhT *qh, realT *points, int numpoints, int dim, realT **rows); +void qh_scaleinput(qhT *qh); +void qh_scalelast(qhT *qh, coordT *points, int numpoints, int dim, coordT low, + coordT high, coordT newhigh); +void qh_scalepoints(qhT *qh, pointT *points, int numpoints, int dim, + realT *newlows, realT *newhighs); +boolT qh_sethalfspace(qhT *qh, int dim, coordT *coords, coordT **nextp, + coordT *normal, coordT *offset, coordT *feasible); +coordT *qh_sethalfspace_all(qhT *qh, int dim, int count, coordT *halfspaces, pointT *feasible); +coordT qh_vertex_bestdist(qhT *qh, setT *vertices); +coordT qh_vertex_bestdist2(qhT *qh, setT *vertices, vertexT **vertexp, vertexT **vertexp2); +pointT *qh_voronoi_center(qhT *qh, int dim, setT *points); + +#ifdef __cplusplus +} /* extern "C"*/ +#endif + +#endif /* qhDEFgeom */ + + + diff --git a/contrib/libs/qhull/libqhull_r/global_r.c b/contrib/libs/qhull/libqhull_r/global_r.c new file mode 100644 index 0000000000..04b9b4d74e --- /dev/null +++ b/contrib/libs/qhull/libqhull_r/global_r.c @@ -0,0 +1,2268 @@ + +/*<html><pre> -<a href="qh-globa_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + global_r.c + initializes all the globals of the qhull application + + see README + + see libqhull_r.h for qh.globals and function prototypes + + see qhull_ra.h for internal functions + + Copyright (c) 1993-2020 The Geometry Center. + $Id: //main/2019/qhull/src/libqhull_r/global_r.c#19 $$Change: 3037 $ + $DateTime: 2020/09/03 17:28:32 $$Author: bbarber $ + */ + +#include "qhull_ra.h" + +/*========= qh->definition -- globals defined in libqhull_r.h =======================*/ + +/*-<a href ="qh-globa_r.htm#TOC" + >--------------------------------</a><a name="version">-</a> + + qh_version + version string by year and date + qh_version2 for Unix users and -V + + 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, CMakeLists.txt + check that CMakeLists.txt @version is the same as qh_version2 + change year: Copying.txt + check download size + recompile user_eg_r.c, rbox_r.c, libqhull_r.c, qconvex_r.c, qdelaun_r.c qvoronoi_r.c, qhalf_r.c, testqset_r.c +*/ + +const char qh_version[]= "2020.2.r 2020/08/31"; +const char qh_version2[]= "qhull_r 8.0.2 (2020.2.r 2020/08/31)"; + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="appendprint">-</a> + + qh_appendprint(qh, printFormat ) + append printFormat to qh.PRINTout unless already defined +*/ +void qh_appendprint(qhT *qh, 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 */ + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="checkflags">-</a> + + qh_checkflags(qh, commandStr, hiddenFlags ) + errors if commandStr contains hiddenFlags + hiddenFlags starts and ends with a space and is space delimited (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(qhT *qh, char *command, char *hiddenflags) { + char *s= command, *t, *chkerr; /* qh_skipfilename is non-const */ + char key, opt, prevopt; + char chkkey[]= " "; /* check one character options ('s') */ + char chkopt[]= " "; /* check two character options ('Ta') */ + char chkopt2[]= " "; /* check three character options ('Q12') */ + boolT waserr= False; + + if (*hiddenflags != ' ' || hiddenflags[strlen(hiddenflags)-1] != ' ') { + qh_fprintf(qh, qh->ferr, 6026, "qhull internal error (qh_checkflags): hiddenflags must start and end with a space: \"%s\"\n", hiddenflags); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + if (strpbrk(hiddenflags, ",\n\r\t")) { + qh_fprintf(qh, qh->ferr, 6027, "qhull internal error (qh_checkflags): hiddenflags contains commas, newlines, or tabs: \"%s\"\n", hiddenflags); + qh_errexit(qh, qh_ERRqhull, 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(qh, ++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))) { + if (isdigit(*s)) { /* Q12 */ + chkopt2[2]= opt; + chkopt2[3]= *s++; + if (strstr(hiddenflags, chkopt2)) + chkerr= chkopt2; + }else { + 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, qh->ferr, 6029, "qhull option 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, qh_ERRinput, NULL, NULL); +} /* checkflags */ + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="clear_outputflags">-</a> + + qh_clear_outputflags(qh) + Clear output flags for QhullPoints +*/ +void qh_clear_outputflags(qhT *qh) { + 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_outputflags */ + 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 */ + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="clock">-</a> + + 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(qhT *qh) { + +#if (qh_CLOCKtype == 2) + struct tms time; + static long clktck; /* initialized first call and never updated */ + double ratio, cpu; + unsigned long ticks; + + if (!clktck) { + if ((clktck= sysconf(_SC_CLK_TCK)) < 0) { + qh_fprintf(qh, qh->ferr, 6030, "qhull internal error (qh_clock): sysconf() failed. Use qh_CLOCKtype 1 in user_r.h\n"); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + } + if (times(&time) == -1) { + qh_fprintf(qh, qh->ferr, 6031, "qhull internal error (qh_clock): times() failed. Use qh_CLOCKtype 1 in user_r.h\n"); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + ratio= qh_SECticks / (double)clktck; + ticks= time.tms_utime * ratio; + return ticks; +#else + qh_fprintf(qh, qh->ferr, 6032, "qhull internal error (qh_clock): use qh_CLOCKtype 2 in user_r.h\n"); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); /* never returns */ + return 0; +#endif +} /* clock */ + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="freebuffers">-</a> + + qh_freebuffers() + free up global memory buffers + + notes: + must match qh_initbuffers() +*/ +void qh_freebuffers(qhT *qh) { + + trace5((qh, qh->ferr, 5001, "qh_freebuffers: freeing up global memory buffers\n")); + /* allocated by qh_initqhull_buffers */ + qh_setfree(qh, &qh->other_points); + qh_setfree(qh, &qh->del_vertices); + qh_setfree(qh, &qh->coplanarfacetset); + qh_memfree(qh, qh->NEARzero, qh->hull_dim * (int)sizeof(realT)); + qh_memfree(qh, qh->lower_threshold, (qh->input_dim+1) * (int)sizeof(realT)); + qh_memfree(qh, qh->upper_threshold, (qh->input_dim+1) * (int)sizeof(realT)); + qh_memfree(qh, qh->lower_bound, (qh->input_dim+1) * (int)sizeof(realT)); + qh_memfree(qh, qh->upper_bound, (qh->input_dim+1) * (int)sizeof(realT)); + qh_memfree(qh, qh->gm_matrix, (qh->hull_dim+1) * qh->hull_dim * (int)sizeof(coordT)); + qh_memfree(qh, qh->gm_row, (qh->hull_dim+1) * (int)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; + + 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, qh->ferr, 5002, "qh_freebuffers: finished\n")); +} /* freebuffers */ + + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="freebuild">-</a> + + qh_freebuild(qh, 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 + 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 sets + free temporary sets +*/ +void qh_freebuild(qhT *qh, boolT allmem) { + facetT *facet, *previousfacet= NULL; + vertexT *vertex, *previousvertex= NULL; + ridgeT *ridge, **ridgep, *previousridge= NULL; + mergeT *merge, **mergep; + int newsize; + boolT freeall; + + /* free qhT global sets first, includes references from qh_buildhull */ + trace5((qh, qh->ferr, 5004, "qh_freebuild: free global sets\n")); + FOREACHmerge_(qh->facet_mergeset) /* usually empty */ + qh_memfree(qh, merge, (int)sizeof(mergeT)); + FOREACHmerge_(qh->degen_mergeset) /* usually empty */ + qh_memfree(qh, merge, (int)sizeof(mergeT)); + FOREACHmerge_(qh->vertex_mergeset) /* usually empty */ + qh_memfree(qh, merge, (int)sizeof(mergeT)); + qh->facet_mergeset= NULL; /* temp set freed by qh_settempfree_all */ + qh->degen_mergeset= NULL; /* temp set freed by qh_settempfree_all */ + qh->vertex_mergeset= NULL; /* temp set freed by qh_settempfree_all */ + qh_setfree(qh, &(qh->hash_table)); + trace5((qh, qh->ferr, 5003, "qh_freebuild: free temporary sets (qh_settempfree_all)\n")); + qh_settempfree_all(qh); + trace1((qh, qh->ferr, 1005, "qh_freebuild: free memory from qh_inithull and qh_buildhull\n")); + if (qh->del_vertices) + qh_settruncate(qh, qh->del_vertices, 0); + if (allmem) { + while ((vertex= qh->vertex_list)) { + if (vertex->next) + qh_delvertex(qh, vertex); + else { + qh_memfree(qh, vertex, (int)sizeof(vertexT)); /* sentinel */ + qh->newvertex_list= qh->vertex_list= NULL; + break; + } + previousvertex= vertex; /* in case of memory fault */ + QHULL_UNUSED(previousvertex) + } + }else if (qh->VERTEXneighbors) { + FORALLvertices + qh_setfreelong(qh, &(vertex->neighbors)); + } + qh->VERTEXneighbors= False; + qh->GOODclosest= NULL; + if (allmem) { + FORALLfacets { + FOREACHridge_(facet->ridges) + ridge->seen= False; + } + while ((facet= qh->facet_list)) { + if (!facet->newfacet || !qh->NEWtentative || qh_setsize(qh, facet->ridges) > 1) { /* skip tentative horizon ridges */ + trace4((qh, qh->ferr, 4095, "qh_freebuild: delete the previously-seen ridges of f%d\n", facet->id)); + FOREACHridge_(facet->ridges) { + if (ridge->seen) + qh_delridge(qh, ridge); + else + ridge->seen= True; + previousridge= ridge; /* in case of memory fault */ + QHULL_UNUSED(previousridge) + } + } + qh_setfree(qh, &(facet->outsideset)); + qh_setfree(qh, &(facet->coplanarset)); + qh_setfree(qh, &(facet->neighbors)); + qh_setfree(qh, &(facet->ridges)); + qh_setfree(qh, &(facet->vertices)); + if (facet->next) + qh_delfacet(qh, facet); + else { + qh_memfree(qh, facet, (int)sizeof(facetT)); + qh->visible_list= qh->newfacet_list= qh->facet_list= NULL; + } + previousfacet= facet; /* in case of memory fault */ + QHULL_UNUSED(previousfacet) + } + }else { + freeall= True; + if (qh_setlarger_quick(qh, qh->hull_dim + 1, &newsize)) + freeall= False; + FORALLfacets { + qh_setfreelong(qh, &(facet->outsideset)); + qh_setfreelong(qh, &(facet->coplanarset)); + if (!facet->simplicial || freeall) { + qh_setfreelong(qh, &(facet->neighbors)); + qh_setfreelong(qh, &(facet->ridges)); + qh_setfreelong(qh, &(facet->vertices)); + } + } + } + /* qh internal constants */ + qh_memfree(qh, qh->interior_point, qh->normal_size); + qh->interior_point= NULL; +} /* freebuild */ + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="freeqhull">-</a> + + qh_freeqhull(qh, allmem ) + + free global memory and set qhT to 0 + if !allmem, + does not free short memory (freed by qh_memfreeshort unless qh_NOmem) + +notes: + sets qh.NOerrexit in case caller forgets to + Does not throw errors + +see: + see qh_initqhull_start2() + For libqhull_r, qhstatT is part of qhT + +design: + free global and temporary memory from qh_initbuild and qh_buildhull + free buffers +*/ +void qh_freeqhull(qhT *qh, boolT allmem) { + + qh->NOerrexit= True; /* no more setjmp since called at exit and ~QhullQh */ + trace1((qh, qh->ferr, 1006, "qh_freeqhull: free global memory\n")); + qh_freebuild(qh, allmem); + qh_freebuffers(qh); + trace1((qh, qh->ferr, 1061, "qh_freeqhull: clear qhT except for qh.qhmem and qh.qhstat\n")); + /* memset is the same in qh_freeqhull() and qh_initqhull_start2() */ + memset((char *)qh, 0, sizeof(qhT)-sizeof(qhmemT)-sizeof(qhstatT)); + qh->NOerrexit= True; +} /* freeqhull */ + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="init_A">-</a> + + qh_init_A(qh, 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(qhT *qh, FILE *infile, FILE *outfile, FILE *errfile, int argc, char *argv[]) { + qh_meminit(qh, errfile); + qh_initqhull_start(qh, infile, outfile, errfile); + qh_init_qhull_command(qh, argc, argv); +} /* init_A */ + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="init_B">-</a> + + qh_init_B(qh, 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(qhT *qh, coordT *points, int numpoints, int dim, boolT ismalloc) { + qh_initqhull_globals(qh, points, numpoints, dim, ismalloc); + if (qh->qhmem.LASTsize == 0) + qh_initqhull_mem(qh); + /* mem_r.c and qset_r.c are initialized */ + qh_initqhull_buffers(qh); + qh_initthresholds(qh, qh->qhull_command); + if (qh->PROJECTinput || (qh->DELAUNAY && qh->PROJECTdelaunay)) + qh_projectinput(qh); + if (qh->SCALEinput) + qh_scaleinput(qh); + if (qh->ROTATErandom >= 0) { + qh_randommatrix(qh, 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, qh->hull_dim, qh->gm_row); + qh_rotateinput(qh, qh->gm_row); + } +} /* init_B */ + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="init_qhull_command">-</a> + + qh_init_qhull_command(qh, argc, argv ) + build qh.qhull_command from argc/argv + Calls qh_exit if qhull_command is too short + + 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(qhT *qh, 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, 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 */ + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="initflags">-</a> + + qh_initflags(qh, commandStr ) + set flags and initialized constants from commandStr + calls qh_exit() if qh.NOerrexit + + returns: + sets qh.qhull_command to command if needed + + notes: + ignores first word (e.g., 'qhull' in "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_r.c for documentation + + design: + for each space-delimited option group + if top-level option + check syntax + append appropriate option to option string + set appropriate global variable or append printFormat to print options + else + for each sub-option + check syntax + append appropriate option to option string + set appropriate global variable or append printFormat to print options +*/ +void qh_initflags(qhT *qh, char *command) { + int k, i, lastproject; + char *s= command, *t, *prev_s, *start, key, *lastwarning= NULL; + boolT isgeom= False, wasproject; + realT r; + + if(qh->NOerrexit){ + qh_fprintf(qh, qh->ferr, 6245, "qhull internal error (qh_initflags): qh.NOerrexit was not cleared before calling qh_initflags(). It should be cleared after setjmp(). Exit qhull.\n"); + qh_exit(qh_ERRqhull); + } +#ifdef qh_RANDOMdist + qh->RANDOMfactor= qh_RANDOMdist; + qh_option(qh, "Random-qh_RANDOMdist", NULL, &qh->RANDOMfactor); + qh->RANDOMdist= True; +#endif + 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(qh, "delaunay", NULL, NULL); + qh->DELAUNAY= True; + break; + case 'f': + qh_option(qh, "facets", NULL, NULL); + qh_appendprint(qh, qh_PRINTfacets); + break; + case 'i': + qh_option(qh, "incidence", NULL, NULL); + qh_appendprint(qh, qh_PRINTincidences); + break; + case 'm': + qh_option(qh, "mathematica", NULL, NULL); + qh_appendprint(qh, qh_PRINTmathematica); + break; + case 'n': + qh_option(qh, "normals", NULL, NULL); + qh_appendprint(qh, qh_PRINTnormals); + break; + case 'o': + qh_option(qh, "offFile", NULL, NULL); + qh_appendprint(qh, qh_PRINToff); + break; + case 'p': + qh_option(qh, "points", NULL, NULL); + qh_appendprint(qh, qh_PRINTpoints); + break; + case 's': + qh_option(qh, "summary", NULL, NULL); + qh->PRINTsummary= True; + break; + case 'v': + qh_option(qh, "voronoi", NULL, NULL); + qh->VORONOI= True; + qh->DELAUNAY= True; + break; + case 'A': + if (!isdigit(*s) && *s != '.' && *s != '-') { + qh_fprintf(qh, qh->ferr, 7002, "qhull input warning: no maximum cosine angle given for option 'An'. A1.0 is coplanar\n"); + lastwarning= s-1; + }else { + if (*s == '-') { + qh->premerge_cos= -qh_strtod(s, &s); + qh_option(qh, "Angle-premerge-", NULL, &qh->premerge_cos); + qh->PREmerge= True; + }else { + qh->postmerge_cos= qh_strtod(s, &s); + qh_option(qh, "Angle-postmerge", NULL, &qh->postmerge_cos); + qh->POSTmerge= True; + } + qh->MERGING= True; + } + break; + case 'C': + if (!isdigit(*s) && *s != '.' && *s != '-') { + qh_fprintf(qh, qh->ferr, 7003, "qhull input warning: no centrum radius given for option 'Cn'\n"); + lastwarning= s-1; + }else { + if (*s == '-') { + qh->premerge_centrum= -qh_strtod(s, &s); + qh_option(qh, "Centrum-premerge-", NULL, &qh->premerge_centrum); + qh->PREmerge= True; + }else { + qh->postmerge_centrum= qh_strtod(s, &s); + qh_option(qh, "Centrum-postmerge", NULL, &qh->postmerge_centrum); + qh->POSTmerge= True; + } + qh->MERGING= True; + } + break; + case 'E': + if (*s == '-') { + qh_fprintf(qh, qh->ferr, 6363, "qhull option error: expecting a positive number for maximum roundoff 'En'. Got '%s'\n", s-1); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + }else if (!isdigit(*s)) { + qh_fprintf(qh, qh->ferr, 7005, "qhull option warning: no maximum roundoff given for option 'En'\n"); + lastwarning= s-1; + }else { + qh->DISTround= qh_strtod(s, &s); + qh_option(qh, "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, qh->ferr, 6364, "qhull option error: expecting 'Hn,n,n,...' for feasible point of halfspace intersection. Got '%s'\n", start-1); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + } + 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, qh->ferr, 6034, "qhull error: insufficient memory for 'Hn,n,n'\n"); + qh_errexit(qh, qh_ERRmem, NULL, NULL); + } + strncpy(qh->feasible_string, start, (size_t)(t-start)); + qh_option(qh, "Halfspace-about", NULL, NULL); + qh_option(qh, qh->feasible_string, NULL, NULL); + }else + qh_option(qh, "Halfspace", NULL, NULL); + break; + case 'R': + if (!isdigit(*s)) { + qh_fprintf(qh, qh->ferr, 7007, "qhull option warning: missing random perturbation for option 'Rn'\n"); + lastwarning= s-1; + }else { + qh->RANDOMfactor= qh_strtod(s, &s); + qh_option(qh, "Random-perturb", NULL, &qh->RANDOMfactor); + qh->RANDOMdist= True; + } + break; + case 'V': + if (!isdigit(*s) && *s != '-') { + qh_fprintf(qh, qh->ferr, 7008, "qhull option warning: missing visible distance for option 'Vn'\n"); + lastwarning= s-1; + }else { + qh->MINvisible= qh_strtod(s, &s); + qh_option(qh, "Visible", NULL, &qh->MINvisible); + } + break; + case 'U': + if (!isdigit(*s) && *s != '-') { + qh_fprintf(qh, qh->ferr, 7009, "qhull option warning: missing coplanar distance for option 'Un'\n"); + lastwarning= s-1; + }else { + qh->MAXcoplanar= qh_strtod(s, &s); + qh_option(qh, "U-coplanar", NULL, &qh->MAXcoplanar); + } + break; + case 'W': + if (*s == '-') { + qh_fprintf(qh, qh->ferr, 6365, "qhull option error: expecting a positive number for outside width 'Wn'. Got '%s'\n", s-1); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + }else if (!isdigit(*s)) { + qh_fprintf(qh, qh->ferr, 7011, "qhull option warning: missing outside width for option 'Wn'\n"); + lastwarning= s-1; + }else { + qh->MINoutside= qh_strtod(s, &s); + qh_option(qh, "W-outside", NULL, &qh->MINoutside); + qh->APPROXhull= True; + } + break; + /************ sub menus ***************/ + case 'F': + while (*s && !isspace(*s)) { + switch (*s++) { + case 'a': + qh_option(qh, "Farea", NULL, NULL); + qh_appendprint(qh, qh_PRINTarea); + qh->GETarea= True; + break; + case 'A': + qh_option(qh, "FArea-total", NULL, NULL); + qh->GETarea= True; + break; + case 'c': + qh_option(qh, "Fcoplanars", NULL, NULL); + qh_appendprint(qh, qh_PRINTcoplanars); + break; + case 'C': + qh_option(qh, "FCentrums", NULL, NULL); + qh_appendprint(qh, qh_PRINTcentrums); + break; + case 'd': + qh_option(qh, "Fd-cdd-in", NULL, NULL); + qh->CDDinput= True; + break; + case 'D': + qh_option(qh, "FD-cdd-out", NULL, NULL); + qh->CDDoutput= True; + break; + case 'F': + qh_option(qh, "FFacets-xridge", NULL, NULL); + qh_appendprint(qh, qh_PRINTfacets_xridge); + break; + case 'i': + qh_option(qh, "Finner", NULL, NULL); + qh_appendprint(qh, qh_PRINTinner); + break; + case 'I': + qh_option(qh, "FIDs", NULL, NULL); + qh_appendprint(qh, qh_PRINTids); + break; + case 'm': + qh_option(qh, "Fmerges", NULL, NULL); + qh_appendprint(qh, qh_PRINTmerges); + break; + case 'M': + qh_option(qh, "FMaple", NULL, NULL); + qh_appendprint(qh, qh_PRINTmaple); + break; + case 'n': + qh_option(qh, "Fneighbors", NULL, NULL); + qh_appendprint(qh, qh_PRINTneighbors); + break; + case 'N': + qh_option(qh, "FNeighbors-vertex", NULL, NULL); + qh_appendprint(qh, qh_PRINTvneighbors); + break; + case 'o': + qh_option(qh, "Fouter", NULL, NULL); + qh_appendprint(qh, qh_PRINTouter); + break; + case 'O': + if (qh->PRINToptions1st) { + qh_option(qh, "FOptions", NULL, NULL); + qh_appendprint(qh, qh_PRINToptions); + }else + qh->PRINToptions1st= True; + break; + case 'p': + qh_option(qh, "Fpoint-intersect", NULL, NULL); + qh_appendprint(qh, qh_PRINTpointintersect); + break; + case 'P': + qh_option(qh, "FPoint-nearest", NULL, NULL); + qh_appendprint(qh, qh_PRINTpointnearest); + break; + case 'Q': + qh_option(qh, "FQhull", NULL, NULL); + qh_appendprint(qh, qh_PRINTqhull); + break; + case 's': + qh_option(qh, "Fsummary", NULL, NULL); + qh_appendprint(qh, qh_PRINTsummary); + break; + case 'S': + qh_option(qh, "FSize", NULL, NULL); + qh_appendprint(qh, qh_PRINTsize); + qh->GETarea= True; + break; + case 't': + qh_option(qh, "Ftriangles", NULL, NULL); + qh_appendprint(qh, qh_PRINTtriangles); + break; + case 'v': + /* option set in qh_initqhull_globals */ + qh_appendprint(qh, qh_PRINTvertices); + break; + case 'V': + qh_option(qh, "FVertex-average", NULL, NULL); + qh_appendprint(qh, qh_PRINTaverage); + break; + case 'x': + qh_option(qh, "Fxtremes", NULL, NULL); + qh_appendprint(qh, qh_PRINTextremes); + break; + default: + s--; + qh_fprintf(qh, qh->ferr, 7012, "qhull option warning: unknown 'F' output option 'F%c', skip to next space\n", (int)s[0]); + lastwarning= s-1; + while (*++s && !isspace(*s)); + break; + } + } + break; + case 'G': + isgeom= True; + qh_appendprint(qh, qh_PRINTgeom); + while (*s && !isspace(*s)) { + switch (*s++) { + case 'a': + qh_option(qh, "Gall-points", NULL, NULL); + qh->PRINTdots= True; + break; + case 'c': + qh_option(qh, "Gcentrums", NULL, NULL); + qh->PRINTcentrums= True; + break; + case 'h': + qh_option(qh, "Gintersections", NULL, NULL); + qh->DOintersections= True; + break; + case 'i': + qh_option(qh, "Ginner", NULL, NULL); + qh->PRINTinner= True; + break; + case 'n': + qh_option(qh, "Gno-planes", NULL, NULL); + qh->PRINTnoplanes= True; + break; + case 'o': + qh_option(qh, "Gouter", NULL, NULL); + qh->PRINTouter= True; + break; + case 'p': + qh_option(qh, "Gpoints", NULL, NULL); + qh->PRINTcoplanar= True; + break; + case 'r': + qh_option(qh, "Gridges", NULL, NULL); + qh->PRINTridges= True; + break; + case 't': + qh_option(qh, "Gtransparent", NULL, NULL); + qh->PRINTtransparent= True; + break; + case 'v': + qh_option(qh, "Gvertices", NULL, NULL); + qh->PRINTspheres= True; + break; + case 'D': + if (!isdigit(*s)) { + qh_fprintf(qh, qh->ferr, 7004, "qhull option warning: missing dimension for option 'GDn'\n"); + lastwarning= s-2; + }else { + if (qh->DROPdim >= 0) { + qh_fprintf(qh, qh->ferr, 7013, "qhull option warning: can only drop one dimension. Previous 'GD%d' ignored\n", + qh->DROPdim); + lastwarning= s-2; + } + qh->DROPdim= qh_strtol(s, &s); + qh_option(qh, "GDrop-dim", &qh->DROPdim, NULL); + } + break; + default: + s--; + qh_fprintf(qh, qh->ferr, 7014, "qhull option warning: unknown 'G' geomview option 'G%c', skip to next space\n", (int)s[0]); + lastwarning= s-1; + 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(qh, "Pdrop-facets-dim-less", &i, &r); + else + qh_option(qh, "PDrop-facets-dim-more", &i, &r); + break; + case 'g': + qh_option(qh, "Pgood-facets", NULL, NULL); + qh->PRINTgood= True; + break; + case 'G': + qh_option(qh, "PGood-facet-neighbors", NULL, NULL); + qh->PRINTneighbors= True; + break; + case 'o': + qh_option(qh, "Poutput-forced", NULL, NULL); + qh->FORCEoutput= True; + break; + case 'p': + qh_option(qh, "Pprecision-ignore", NULL, NULL); + qh->PRINTprecision= False; + break; + case 'A': + if (!isdigit(*s)) { + qh_fprintf(qh, qh->ferr, 7006, "qhull option warning: missing facet count for keep area option 'PAn'\n"); + lastwarning= s-2; + }else { + qh->KEEParea= qh_strtol(s, &s); + qh_option(qh, "PArea-keep", &qh->KEEParea, NULL); + qh->GETarea= True; + } + break; + case 'F': + if (!isdigit(*s)) { + qh_fprintf(qh, qh->ferr, 7010, "qhull option warning: missing facet area for option 'PFn'\n"); + lastwarning= s-2; + }else { + qh->KEEPminArea= qh_strtod(s, &s); + qh_option(qh, "PFacet-area-keep", NULL, &qh->KEEPminArea); + qh->GETarea= True; + } + break; + case 'M': + if (!isdigit(*s)) { + qh_fprintf(qh, qh->ferr, 7090, "qhull option warning: missing merge count for option 'PMn'\n"); + lastwarning= s-2; + }else { + qh->KEEPmerge= qh_strtol(s, &s); + qh_option(qh, "PMerge-keep", &qh->KEEPmerge, NULL); + } + break; + default: + s--; + qh_fprintf(qh, qh->ferr, 7015, "qhull option warning: unknown 'P' print option 'P%c', skip to next space\n", (int)s[0]); + lastwarning= s-1; + while (*++s && !isspace(*s)); + break; + } + } + break; + case 'Q': + lastproject= -1; + while (*s && !isspace(*s)) { + switch (*s++) { + case 'a': + qh_option(qh, "Qallow-short", NULL, NULL); + qh->ALLOWshort= True; + break; + 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(qh, "QbBound-unit-box", NULL, &r); + break; + } + if (key == 'b' && *s == 'b') { + s++; + qh->SCALElast= True; + qh_option(qh, "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, qh->ferr, 2004, "qh_initflags: project dimension %d\n", k)); + qh_option(qh, "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(qh, "Qbound-dim-low", &k, &r); + }else { + qh->SCALEinput= True; + if (r == 0.0) + r= qh_DEFAULTbox; + qh_option(qh, "QBound-dim-high", &k, &r); + } + } + break; + case 'c': + qh_option(qh, "Qcoplanar-keep", NULL, NULL); + qh->KEEPcoplanar= True; + break; + case 'f': + qh_option(qh, "Qfurthest-outside", NULL, NULL); + qh->BESToutside= True; + break; + case 'g': + qh_option(qh, "Qgood-facets-only", NULL, NULL); + qh->ONLYgood= True; + break; + case 'i': + qh_option(qh, "Qinterior-keep", NULL, NULL); + qh->KEEPinside= True; + break; + case 'm': + qh_option(qh, "Qmax-outside-only", NULL, NULL); + qh->ONLYmax= True; + break; + case 'r': + qh_option(qh, "Qrandom-outside", NULL, NULL); + qh->RANDOMoutside= True; + break; + case 's': + qh_option(qh, "Qsearch-initial-simplex", NULL, NULL); + qh->ALLpoints= True; + break; + case 't': + qh_option(qh, "Qtriangulate", NULL, NULL); + qh->TRIangulate= True; + break; + case 'T': + qh_option(qh, "QTestPoints", NULL, NULL); + if (!isdigit(*s)) { + qh_fprintf(qh, qh->ferr, 7091, "qhull option warning: missing number of test points for option 'QTn'\n"); + lastwarning= s-2; + }else { + qh->TESTpoints= qh_strtol(s, &s); + qh_option(qh, "QTestPoints", &qh->TESTpoints, NULL); + } + break; + case 'u': + qh_option(qh, "QupperDelaunay", NULL, NULL); + qh->UPPERdelaunay= True; + break; + case 'v': + qh_option(qh, "Qvertex-neighbors-convex", NULL, NULL); + qh->TESTvneighbors= True; + break; + case 'x': + qh_option(qh, "Qxact-merge", NULL, NULL); + qh->MERGEexact= True; + break; + case 'z': + qh_option(qh, "Qz-infinity-point", NULL, NULL); + qh->ATinfinity= True; + break; + case '0': + qh_option(qh, "Q0-no-premerge", NULL, NULL); + qh->NOpremerge= True; + break; + case '1': + if (!isdigit(*s)) { + qh_option(qh, "Q1-angle-merge", NULL, NULL); + qh->ANGLEmerge= True; + break; + } + switch (*s++) { + case '0': + qh_option(qh, "Q10-no-narrow", NULL, NULL); + qh->NOnarrow= True; + break; + case '1': + qh_option(qh, "Q11-trinormals Qtriangulate", NULL, NULL); + qh->TRInormals= True; + qh->TRIangulate= True; + break; + case '2': + qh_option(qh, "Q12-allow-wide", NULL, NULL); + qh->ALLOWwide= True; + break; + case '4': +#ifndef qh_NOmerge + qh_option(qh, "Q14-merge-pinched-vertices", NULL, NULL); + qh->MERGEpinched= True; +#else + /* ignore 'Q14' for q_benchmark testing of difficult cases for Qhull */ + qh_fprintf(qh, qh->ferr, 7099, "qhull option warning: option 'Q14-merge-pinched' disabled due to qh_NOmerge\n"); +#endif + break; + case '7': + qh_option(qh, "Q15-check-duplicates", NULL, NULL); + qh->CHECKduplicates= True; + break; + default: + s--; + qh_fprintf(qh, qh->ferr, 7016, "qhull option warning: unknown 'Q' qhull option 'Q1%c', skip to next space\n", (int)s[0]); + lastwarning= s-1; + while (*++s && !isspace(*s)); + break; + } + break; + case '2': + qh_option(qh, "Q2-no-merge-independent", NULL, NULL); + qh->MERGEindependent= False; + goto LABELcheckdigit; + break; /* no gcc warnings */ + case '3': + qh_option(qh, "Q3-no-merge-vertices", NULL, NULL); + qh->MERGEvertices= False; + LABELcheckdigit: + if (isdigit(*s)) { + qh_fprintf(qh, qh->ferr, 7017, "qhull option warning: can not follow '1', '2', or '3' with a digit. 'Q%c%c' skipped\n", *(s-1), *s); + lastwarning= s-2; + s++; + } + break; + case '4': + qh_option(qh, "Q4-avoid-old-into-new", NULL, NULL); + qh->AVOIDold= True; + break; + case '5': + qh_option(qh, "Q5-no-check-outer", NULL, NULL); + qh->SKIPcheckmax= True; + break; + case '6': + qh_option(qh, "Q6-no-concave-merge", NULL, NULL); + qh->SKIPconvex= True; + break; + case '7': + qh_option(qh, "Q7-no-breadth-first", NULL, NULL); + qh->VIRTUALmemory= True; + break; + case '8': + qh_option(qh, "Q8-no-near-inside", NULL, NULL); + qh->NOnearinside= True; + break; + case '9': + qh_option(qh, "Q9-pick-furthest", NULL, NULL); + qh->PICKfurthest= True; + break; + case 'G': + i= qh_strtol(s, &t); + if (qh->GOODpoint) { + qh_fprintf(qh, qh->ferr, 7018, "qhull option warning: good point already defined for option 'QGn'. Ignored\n"); + lastwarning= s-2; + }else if (s == t) { + qh_fprintf(qh, qh->ferr, 7019, "qhull option warning: missing good point id for option 'QGn'. Ignored\n"); + lastwarning= s-2; + }else if (i < 0 || *s == '-') { + qh->GOODpoint= i-1; + qh_option(qh, "QGood-if-dont-see-point", &i, NULL); + }else { + qh->GOODpoint= i+1; + qh_option(qh, "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(qh, "QJoggle", NULL, &qh->JOGGLEmax); + } + break; + case 'R': + if (!isdigit(*s) && *s != '-') { + qh_fprintf(qh, qh->ferr, 7020, "qhull option warning: missing random seed for option 'QRn'\n"); + lastwarning= s-2; + }else { + qh->ROTATErandom= i= qh_strtol(s, &s); + if (i > 0) + qh_option(qh, "QRotate-id", &i, NULL ); + else if (i < -1) + qh_option(qh, "QRandom-seed", &i, NULL ); + } + break; + case 'V': + i= qh_strtol(s, &t); + if (qh->GOODvertex) { + qh_fprintf(qh, qh->ferr, 7021, "qhull option warning: good vertex already defined for option 'QVn'. Ignored\n"); + lastwarning= s-2; + }else if (s == t) { + qh_fprintf(qh, qh->ferr, 7022, "qhull option warning: no good point id given for option 'QVn'. Ignored\n"); + lastwarning= s-2; + }else if (i < 0) { + qh->GOODvertex= i - 1; + qh_option(qh, "QV-good-facets-not-point", &i, NULL); + }else { + qh_option(qh, "QV-good-facets-point", &i, NULL); + qh->GOODvertex= i + 1; + } + s= t; + break; + case 'w': + qh_option(qh, "Qwarn-allow", NULL, NULL); + qh->ALLOWwarning= True; + break; + default: + s--; + qh_fprintf(qh, qh->ferr, 7023, "qhull option warning: unknown 'Q' qhull option 'Q%c', skip to next space\n", (int)s[0]); + lastwarning= s-1; + 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(qh, "Tannotate-output", NULL, NULL); + qh->ANNOTATEoutput= True; + break; + case 'c': + qh_option(qh, "Tcheck-frequently", NULL, NULL); + qh->CHECKfrequently= True; + break; + case 'f': + qh_option(qh, "Tflush", NULL, NULL); + qh->FLUSHprint= True; + break; + case 's': + qh_option(qh, "Tstatistics", NULL, NULL); + qh->PRINTstatistics= True; + break; + case 'v': + qh_option(qh, "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(qh, "Tz-stdout", NULL, NULL); + qh->USEstdout= True; + }else if (!qh->fout) { + qh_fprintf(qh, qh->ferr, 7024, "qhull option warning: output file undefined(stdout). Option 'Tz' ignored.\n"); + lastwarning= s-2; + }else { + qh_option(qh, "Tz-stdout", NULL, NULL); + qh->USEstdout= True; + qh->ferr= qh->fout; + qh->qhmem.ferr= qh->fout; + } + break; + case 'C': + if (!isdigit(*s)) { + qh_fprintf(qh, qh->ferr, 7025, "qhull option warning: missing point id for cone for trace option 'TCn'\n"); + lastwarning= s-2; + }else { + i= qh_strtol(s, &s); + qh_option(qh, "TCone-stop", &i, NULL); + qh->STOPcone= i + 1; + } + break; + case 'F': + if (!isdigit(*s)) { + qh_fprintf(qh, qh->ferr, 7026, "qhull option warning: missing frequency count for trace option 'TFn'\n"); + lastwarning= s-2; + }else { + qh->REPORTfreq= qh_strtol(s, &s); + qh_option(qh, "TFacet-log", &qh->REPORTfreq, NULL); + qh->REPORTfreq2= qh->REPORTfreq/2; /* for tracemerging() */ + } + break; + case 'I': + while (isspace(*s)) + s++; + t= qh_skipfilename(qh, s); + { + char filename[qh_FILENAMElen]; + + qh_copyfilename(qh, filename, (int)sizeof(filename), s, (int)(t-s)); /* WARN64 */ + s= t; + if (!freopen(filename, "r", stdin)) { + qh_fprintf(qh, qh->ferr, 6041, "qhull option error: cannot open 'TI' file \"%s\"\n", filename); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + }else { + qh_option(qh, "TInput-file", NULL, NULL); + qh_option(qh, filename, NULL, NULL); + } + } + break; + case 'O': + while (isspace(*s)) + s++; + t= qh_skipfilename(qh, s); + { + char filename[qh_FILENAMElen]; + + qh_copyfilename(qh, filename, (int)sizeof(filename), s, (int)(t-s)); /* WARN64 */ + if (!qh->fout) { + qh_fprintf(qh, qh->ferr, 7092, "qhull option warning: qh.fout was not set by caller of qh_initflags. Cannot use option 'TO' to redirect output. Ignoring option 'TO'\n"); + lastwarning= s-2; + }else if (!freopen(filename, "w", qh->fout)) { + qh_fprintf(qh, qh->ferr, 6044, "qhull option error: cannot open file \"%s\" for writing as option 'TO'. It is already in use or read-only\n", filename); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + }else { + qh_option(qh, "TOutput-file", NULL, NULL); + qh_option(qh, filename, NULL, NULL); + } + s= t; + } + break; + case 'A': + if (!isdigit(*s)) { + qh_fprintf(qh, qh->ferr, 7093, "qhull option warning: missing count of added points for trace option 'TAn'\n"); + lastwarning= s-2; + }else { + i= qh_strtol(s, &t); + qh->STOPadd= i + 1; + qh_option(qh, "TA-stop-add", &i, NULL); + } + s= t; + break; + case 'P': + if (*s == '-') { + if (s[1] == '1' && !isdigit(s[2])) { + s += 2; + qh->TRACEpoint= qh_IDunknown; /* qh_buildhull done */ + qh_option(qh, "Trace-point", &qh->TRACEpoint, NULL); + }else { + qh_fprintf(qh, qh->ferr, 7100, "qhull option warning: negative point id for trace option 'TPn'. Expecting 'TP-1' for tracing after qh_buildhull and qh_postmerge\n"); + lastwarning= s-2; + while (isdigit(*(++s))) + ; /* skip digits */ + } + }else if (!isdigit(*s)) { + qh_fprintf(qh, qh->ferr, 7029, "qhull option warning: missing point id or -1 for trace option 'TPn'\n"); + lastwarning= s-2; + }else { + qh->TRACEpoint= qh_strtol(s, &s); + qh_option(qh, "Trace-point", &qh->TRACEpoint, NULL); + } + break; + case 'M': + if (!isdigit(*s)) { + qh_fprintf(qh, qh->ferr, 7030, "qhull option warning: missing merge id for trace option 'TMn'\n"); + lastwarning= s-2; + }else { + qh->TRACEmerge= qh_strtol(s, &s); + qh_option(qh, "Trace-merge", &qh->TRACEmerge, NULL); + } + break; + case 'R': + if (!isdigit(*s)) { + qh_fprintf(qh, qh->ferr, 7031, "qhull option warning: missing rerun count for trace option 'TRn'\n"); + lastwarning= s-2; + }else { + qh->RERUN= qh_strtol(s, &s); + qh_option(qh, "TRerun", &qh->RERUN, NULL); + } + break; + case 'V': + i= qh_strtol(s, &t); + if (s == t) { + qh_fprintf(qh, qh->ferr, 7032, "qhull option warning: missing furthest point id for trace option 'TVn'\n"); + lastwarning= s-2; + }else if (i < 0) { + qh->STOPpoint= i - 1; + qh_option(qh, "TV-stop-before-point", &i, NULL); + }else { + qh->STOPpoint= i + 1; + qh_option(qh, "TV-stop-after-point", &i, NULL); + } + s= t; + break; + case 'W': + if (!isdigit(*s)) { + qh_fprintf(qh, qh->ferr, 7033, "qhull option warning: missing max width for trace option 'TWn'\n"); + lastwarning= s-2; + }else { + qh->TRACEdist= (realT) qh_strtod(s, &s); + qh_option(qh, "TWide-trace", NULL, &qh->TRACEdist); + } + break; + default: + s--; + qh_fprintf(qh, qh->ferr, 7034, "qhull option warning: unknown 'T' trace option 'T%c', skip to next space\n", (int)s[0]); + lastwarning= s-2; + while (*++s && !isspace(*s)); + break; + } + } + break; + default: + qh_fprintf(qh, qh->ferr, 7094, "qhull option warning: unknown option '%c'(%x)\n", + (int)s[-1], (int)s[-1]); + lastwarning= s-2; + break; + } + if (s-1 == prev_s && *s && !isspace(*s)) { + qh_fprintf(qh, qh->ferr, 7036, "qhull option warning: missing space after option '%c'(%x), reserved for sub-options, ignoring '%c' options to next space\n", + (int)*prev_s, (int)*prev_s, (int)*prev_s); + lastwarning= s-1; + while (*s && !isspace(*s)) + s++; + } + } + if (qh->STOPcone && qh->JOGGLEmax < REALmax/2) { + qh_fprintf(qh, qh->ferr, 7078, "qhull option warning: 'TCn' (stopCone) ignored when used with 'QJn' (joggle)\n"); + lastwarning= command; + } + if (isgeom && !qh->FORCEoutput && qh->PRINTout[1]) { + qh_fprintf(qh, qh->ferr, 7037, "qhull option warning: additional output formats ('Fc',etc.) are not compatible with Geomview ('G'). Use option 'Po' to override\n"); + lastwarning= command; + } + if (lastwarning && !qh->ALLOWwarning) { + qh_fprintf(qh, qh->ferr, 6035, "qhull option error: see previous warnings, use 'Qw' to override: '%s' (last offset %d)\n", + command, (int)(lastwarning-command)); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + trace4((qh, qh->ferr, 4093, "qh_initflags: option flags initialized\n")); + /* set derived values in qh_initqhull_globals */ +} /* initflags */ + + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="initqhull_buffers">-</a> + + qh_initqhull_buffers(qh) + initialize global memory buffers + + notes: + must match qh_freebuffers() +*/ +void qh_initqhull_buffers(qhT *qh) { + int k; + + qh->TEMPsize= (qh->qhmem.LASTsize - (int)sizeof(setT))/SETelemsize; + if (qh->TEMPsize <= 0 || qh->TEMPsize > qh->qhmem.LASTsize) + qh->TEMPsize= 8; /* e.g., if qh_NOmem */ + qh->other_points= qh_setnew(qh, qh->TEMPsize); + qh->del_vertices= qh_setnew(qh, qh->TEMPsize); + qh->coplanarfacetset= qh_setnew(qh, qh->TEMPsize); + qh->NEARzero= (realT *)qh_memalloc(qh, qh->hull_dim * (int)sizeof(realT)); + qh->lower_threshold= (realT *)qh_memalloc(qh, (qh->input_dim+1) * (int)sizeof(realT)); + qh->upper_threshold= (realT *)qh_memalloc(qh, (qh->input_dim+1) * (int)sizeof(realT)); + qh->lower_bound= (realT *)qh_memalloc(qh, (qh->input_dim+1) * (int)sizeof(realT)); + qh->upper_bound= (realT *)qh_memalloc(qh, (qh->input_dim+1) * (int)sizeof(realT)); + for (k=qh->input_dim+1; k--; ) { /* duplicated in qh_initqhull_buffers and qh_clear_outputflags */ + 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, (qh->hull_dim+1) * qh->hull_dim * (int)sizeof(coordT)); + qh->gm_row= (coordT **)qh_memalloc(qh, (qh->hull_dim+1) * (int)sizeof(coordT *)); +} /* initqhull_buffers */ + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="initqhull_globals">-</a> + + qh_initqhull_globals(qh, 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 + qh_initqhull_start() sets default values for non-zero globals + consider duplicate error checks in qh_readpoints. It is called before qh_initqhull_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(qhT *qh, 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, qh->ferr, 13, "qh_initqhull_globals: for %s | %s\n", qh->rbox_command, + qh->qhull_command)); + if (numpoints < 1 || numpoints > qh_POINTSmax) { + qh_fprintf(qh, qh->ferr, 6412, "qhull input error (qh_initqhull_globals): expecting between 1 and %d points. Got %d %d-d points\n", + qh_POINTSmax, numpoints, dim); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + /* same error message in qh_readpoints */ + } + 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(qh, "_pre-merge", NULL, NULL); + }else { + qh->MERGEexact= True; + qh_option(qh, "Qxact-merge", NULL, NULL); + } + }else if (qh->MERGEexact) + qh->MERGING= True; + if (qh->NOpremerge && (qh->MERGEexact || qh->PREmerge)) + qh_fprintf(qh, qh->ferr, 7095, "qhull option warning: 'Q0-no-premerge' ignored due to exact merge ('Qx') or pre-merge ('C-n' or 'A-n')\n"); + if (!qh->NOpremerge && qh->JOGGLEmax > REALmax/2) { +#ifdef qh_NOmerge + qh->JOGGLEmax= 0.0; +#endif + } + if (qh->TRIangulate && qh->JOGGLEmax < REALmax/2 && !qh->PREmerge && !qh->POSTmerge && qh->PRINTprecision) + qh_fprintf(qh, qh->ferr, 7038, "qhull option warning: joggle ('QJ') produces simplicial output (i.e., triangles in 2-D). Unless merging is requested, option 'Qt' has no effect\n"); + if (qh->JOGGLEmax < REALmax/2 && qh->DELAUNAY && !qh->SCALEinput && !qh->SCALElast) { + qh->SCALElast= True; + qh_option(qh, "Qbbound-last-qj", NULL, NULL); + } + if (qh->MERGING && !qh->POSTmerge && qh->premerge_cos > REALmax/2 + && qh->premerge_centrum == 0.0) { + qh->ZEROcentrum= True; + qh->ZEROall_ok= True; + qh_option(qh, "_zero-centrum", NULL, NULL); + } + if (qh->JOGGLEmax < REALmax/2 && REALepsilon > 2e-8 && qh->PRINTprecision) + qh_fprintf(qh, qh->ferr, 7039, "qhull warning: real epsilon, %2.2g, is probably too large for joggle('QJn')\nRecompile with double precision reals(see user_r.h).\n", + REALepsilon); +#ifdef qh_NOmerge + if (qh->MERGING) { + qh_fprintf(qh, qh->ferr, 6045, "qhull option error: merging not installed (qh_NOmerge) for 'Qx', 'Cn' or 'An')\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } +#endif + if (qh->DELAUNAY && qh->KEEPcoplanar && !qh->KEEPinside) { + qh->KEEPinside= True; + qh_option(qh, "Qinterior-keep", NULL, NULL); + } + if (qh->VORONOI && !qh->DELAUNAY) { + qh_fprintf(qh, qh->ferr, 6038, "qhull internal error (qh_initqhull_globals): if qh.VORONOI is set, qh.DELAUNAY must be set. Qhull constructs the Delaunay triangulation in order to compute the Voronoi diagram\n"); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + if (qh->DELAUNAY && qh->HALFspace) { + qh_fprintf(qh, qh->ferr, 6046, "qhull option error: can not use Delaunay('d') or Voronoi('v') with halfspace intersection('H')\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + /* same error message in qh_readpoints */ + } + if (!qh->DELAUNAY && (qh->UPPERdelaunay || qh->ATinfinity)) { + qh_fprintf(qh, qh->ferr, 6047, "qhull option error: use upper-Delaunay('Qu') or infinity-point('Qz') with Delaunay('d') or Voronoi('v')\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + if (qh->UPPERdelaunay && qh->ATinfinity) { + qh_fprintf(qh, qh->ferr, 6048, "qhull option error: can not use infinity-point('Qz') with upper-Delaunay('Qu')\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + if (qh->MERGEpinched && qh->ONLYgood) { + qh_fprintf(qh, qh->ferr, 6362, "qhull option error: can not use merge-pinched-vertices ('Q14') with good-facets-only ('Qg')\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + if (qh->MERGEpinched && qh->hull_dim == 2) { + trace2((qh, qh->ferr, 2108, "qh_initqhull_globals: disable qh.MERGEpinched for 2-d. It has no effect")) + qh->MERGEpinched= False; + } + if (qh->SCALElast && !qh->DELAUNAY && qh->PRINTprecision) + qh_fprintf(qh, qh->ferr, 7040, "qhull option warning: option 'Qbb' (scale-last-coordinate) is normally used with 'd' or 'v'\n"); + qh->DOcheckmax= (!qh->SKIPcheckmax && (qh->MERGING || qh->APPROXhull)); + 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, qh->ferr, 6049, "qhull option error: test vertex neighbors('Qv') needs a merge option\n"); + qh_errexit(qh, 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, qh->ferr, 6050, "qhull error: dimension %d must be > 1\n", qh->hull_dim); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + for (k=2, factorial=1.0; k < qh->hull_dim; k++) + factorial *= k; + qh->AREAfactor= 1.0 / factorial; + trace2((qh, qh->ferr, 2005, "qh_initqhull_globals: initialize globals. input_dim %d, numpoints %d, malloc? %d, projected %d to hull_dim %d\n", + qh->input_dim, numpoints, ismalloc, qh->PROJECTinput, qh->hull_dim)); + qh->normal_size= qh->hull_dim * (int)sizeof(coordT); + qh->center_size= qh->normal_size - (int)sizeof(coordT); + pointsneeded= qh->hull_dim+1; + if (qh->hull_dim > qh_DIMmergeVertex) { + qh->MERGEvertices= False; + qh_option(qh, "Q3-no-merge-vertices-dim-high", NULL, NULL); + } + if (qh->GOODpoint) + pointsneeded++; +#ifdef qh_NOtrace + if (qh->IStracing || qh->TRACEmerge || qh->TRACEpoint != qh_IDnone || qh->TRACEdist < REALmax/2) { + qh_fprintf(qh, qh->ferr, 6051, "qhull option error: tracing is not installed (qh_NOtrace in user_r.h). Trace options 'Tn', 'TMn', 'TPn' and 'TWn' mostly removed. Continue with 'Qw' (allow warning)\n"); + if (!qh->ALLOWwarning) + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } +#endif + if (qh->RERUN > 1) { + qh->TRACElastrun= qh->IStracing; /* qh_build_withrestart duplicates next conditional */ + if (qh->IStracing && qh->IStracing != -1) { + qh_fprintf(qh, qh->ferr, 8162, "qh_initqhull_globals: trace last of TR%d runs at level %d\n", qh->RERUN, qh->IStracing); + qh->IStracing= 0; + } + }else if (qh->TRACEpoint != qh_IDnone || 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(qh, "QRandom-seed", &seed, NULL ); + }else + qh_option(qh, "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_(qh, seed); + randr= 0.0; + for (i=1000; i--; ) { + randi= qh_RANDOMint; + randr += randi; + if (randi > qh_RANDOMmax) { + qh_fprintf(qh, qh->ferr, 8036, "\ +qhull configuration error (qh_RANDOMmax in user_r.h): random integer %d > qh_RANDOMmax (%.8g)\n", + randi, qh_RANDOMmax); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + } + qh_RANDOMseed_(qh, seed); + randr= randr/1000; + if (randr < qh_RANDOMmax * 0.1 + || randr > qh_RANDOMmax * 0.9) + qh_fprintf(qh, qh->ferr, 8037, "\ +qhull configuration warning (qh_RANDOMmax in user_r.h): average of 1000 random integers (%.2g) is much different than expected (%.2g). 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, 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, qh_ERRqhull, NULL, NULL); + } + if (numpoints+extra < pointsneeded) { + qh_fprintf(qh, qh->ferr, 6214, "qhull input error: not enough points(%d) to construct initial simplex (need %d)\n", + numpoints, pointsneeded); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + qh_initqhull_outputflags(qh); +} /* initqhull_globals */ + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="initqhull_mem">-</a> + + qh_initqhull_mem(qh ) + initialize mem_r.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 (user_r.c) to add up to 10 additional sizes for quick allocation + (see numsizes below) + + returns: + mem_r.c already for qh_memalloc/qh_memfree (errors if called beforehand) + + notes: + qh_produceoutput() prints memsizes + +*/ +void qh_initqhull_mem(qhT *qh) { + int numsizes; + int i; + + numsizes= 8+10; + qh_meminitbuffers(qh, qh->IStracing, qh_MEMalign, numsizes, + qh_MEMbufsize, qh_MEMinitbuf); + qh_memsize(qh, (int)sizeof(vertexT)); + if (qh->MERGING) { + qh_memsize(qh, (int)sizeof(ridgeT)); + qh_memsize(qh, (int)sizeof(mergeT)); + } + qh_memsize(qh, (int)sizeof(facetT)); + i= (int)sizeof(setT) + (qh->hull_dim - 1) * SETelemsize; /* ridge.vertices */ + qh_memsize(qh, i); + qh_memsize(qh, qh->normal_size); /* normal */ + i += SETelemsize; /* facet.vertices, .ridges, .neighbors */ + qh_memsize(qh, i); + qh_user_memsizes(qh); + qh_memsetup(qh); +} /* initqhull_mem */ + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="initqhull_outputflags">-</a> + + 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(qhT *qh) { + boolT printgeom= False, printmath= False, printcoplanar= False; + int i; + + trace3((qh, qh->ferr, 3024, "qh_initqhull_outputflags: %s\n", qh->qhull_command)); + if (!(qh->PRINTgood || qh->PRINTneighbors)) { + if (qh->DELAUNAY || qh->KEEParea || qh->KEEPminArea < REALmax/2 || qh->KEEPmerge + || (!qh->ONLYgood && (qh->GOODvertex || qh->GOODpoint))) { + qh->PRINTgood= True; + qh_option(qh, "Pgood", NULL, NULL); + } + } + if (qh->PRINTtransparent) { + if (qh->hull_dim != 4 || !qh->DELAUNAY || qh->VORONOI || qh->DROPdim >= 0) { + qh_fprintf(qh, qh->ferr, 6215, "qhull option error: transparent Delaunay('Gt') needs 3-d Delaunay('d') w/o 'GDn'\n"); + qh_errexit(qh, 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, qh->ferr, 6053, "qhull option error: option 'Fp' is only used for \nhalfspace intersection('Hn,n,n').\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + }else if (qh->PRINTout[i] == qh_PRINTtriangles && (qh->HALFspace || qh->VORONOI)) { + qh_fprintf(qh, qh->ferr, 6054, "qhull option error: option 'Ft' is not available for Voronoi vertices ('v') or halfspace intersection ('H')\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + }else if (qh->PRINTout[i] == qh_PRINTcentrums && qh->VORONOI) { + qh_fprintf(qh, qh->ferr, 6055, "qhull option error: option 'FC' is not available for Voronoi vertices('v')\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + }else if (qh->PRINTout[i] == qh_PRINTvertices) { + if (qh->VORONOI) + qh_option(qh, "Fvoronoi", NULL, NULL); + else + qh_option(qh, "Fvertices", NULL, NULL); + } + } + if (printcoplanar && qh->DELAUNAY && qh->JOGGLEmax < REALmax/2) { + if (qh->PRINTprecision) + qh_fprintf(qh, qh->ferr, 7041, "qhull option 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, qh->ferr, 6056, "qhull option 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, qh_ERRinput, NULL, NULL); + } + if (printgeom) { + if (qh->hull_dim > 4) { + qh_fprintf(qh, qh->ferr, 6057, "qhull option error: Geomview output is only available for 2-d, 3-d and 4-d\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + if (qh->PRINTnoplanes && !(qh->PRINTcoplanar + qh->PRINTcentrums + + qh->PRINTdots + qh->PRINTspheres + qh->DOintersections + qh->PRINTridges)) { + qh_fprintf(qh, qh->ferr, 6058, "qhull option error: no output specified for Geomview\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + if (qh->VORONOI && (qh->hull_dim > 3 || qh->DROPdim >= 0)) { + qh_fprintf(qh, qh->ferr, 6059, "qhull option error: Geomview output for Voronoi diagrams only for 2-d\n"); + qh_errexit(qh, 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, qh->ferr, 7042, "qhull option warning: coplanars, vertices, and centrums output not 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, 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(qh, "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, qh->ferr, 7043, "qhull option 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 */ + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="initqhull_start">-</a> + + qh_initqhull_start(qh, infile, outfile, errfile ) + allocate memory if needed and call qh_initqhull_start2() +*/ +void qh_initqhull_start(qhT *qh, FILE *infile, FILE *outfile, FILE *errfile) { + + qh_initstatistics(qh); + qh_initqhull_start2(qh, infile, outfile, errfile); +} /* initqhull_start */ + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="initqhull_start2">-</a> + + qh_initqhull_start2(qh, infile, outfile, errfile ) + start initialization of qhull + initialize statistics, stdio, default values for global variables + assumes qh is allocated + notes: + report errors elsewhere, error handling and g_qhull_output [Qhull.cpp, QhullQh()] not in initialized + see: + qh_maxmin() determines the precision constants + qh_freeqhull() +*/ +void qh_initqhull_start2(qhT *qh, FILE *infile, FILE *outfile, FILE *errfile) { + time_t timedata; + int seed; + + qh_CPUclock; /* start the clock(for qh_clock). One-shot. */ + /* memset is the same in qh_freeqhull() and qh_initqhull_start2() */ + memset((char *)qh, 0, sizeof(qhT)-sizeof(qhmemT)-sizeof(qhstatT)); /* every field is 0, FALSE, NULL */ + qh->NOerrexit= True; + qh->DROPdim= -1; + qh->ferr= errfile; + qh->fin= infile; + qh->fout= outfile; + qh->furthest_id= qh_IDunknown; +#ifndef qh_NOmerge + qh->JOGGLEmax= REALmax; +#else + qh->JOGGLEmax= 0.0; /* Joggle ('QJ') if qh_NOmerge */ +#endif + qh->KEEPminArea= REALmax; + qh->last_low= REALmax; + qh->last_high= REALmax; + qh->last_newhigh= REALmax; + qh->last_random= 1; /* reentrant only */ + qh->lastcpu= 0.0; + 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= qh_IDnone; /* recompile to trace a point, or use 'TPn' */ + qh->tracefacet_id= UINT_MAX; /* recompile to trace a facet, set to UINT_MAX when done, see userprintf_r.c/qh_fprintf */ + qh->traceridge_id= UINT_MAX; /* recompile to trace a ridge, set to UINT_MAX when done, see userprintf_r.c/qh_fprintf */ + qh->tracevertex_id= UINT_MAX; /* recompile to trace a vertex, set to UINT_MAX when done, see userprintf_r.c/qh_fprintf */ + seed= (int)time(&timedata); + qh_RANDOMseed_(qh, seed); + qh->run_id= qh_RANDOMint; + if(!qh->run_id) + qh->run_id++; /* guarantee non-zero */ + qh_option(qh, "run-id", &qh->run_id, NULL); + strcat(qh->qhull, "qhull"); +} /* initqhull_start2 */ + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="initthresholds">-</a> + + qh_initthresholds(qh, 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(qhT *qh, char *command) { + realT value; + int idx, maxdim, k; + char *s= command; /* non-const due to strtol */ + char *lastoption, *lastwarning= NULL; + char key; + + maxdim= qh->input_dim; + if (qh->DELAUNAY && (qh->PROJECTdelaunay || qh->PROJECTinput)) + maxdim++; + while (*s) { + if (*s == '-') + s++; + if (*s == 'P') { + lastoption= s++; + while (*s && !isspace(key= *s++)) { + if (key == 'd' || key == 'D') { + if (!isdigit(*s)) { + qh_fprintf(qh, qh->ferr, 7044, "qhull option warning: no dimension given for Print option 'P%c' at: %s. Ignored\n", + key, s-1); + lastwarning= lastoption; + continue; + } + idx= qh_strtol(s, &s); + if (idx >= qh->hull_dim) { + qh_fprintf(qh, qh->ferr, 7045, "qhull option warning: dimension %d for Print option 'P%c' is >= %d. Ignored\n", + idx, key, qh->hull_dim); + lastwarning= lastoption; + continue; + } + if (*s == ':') { + s++; + value= qh_strtod(s, &s); + if (fabs((double)value) > 1.0) { + qh_fprintf(qh, qh->ferr, 7046, "qhull option warning: value %2.4g for Print option 'P%c' is > +1 or < -1. Ignored\n", + value, key); + lastwarning= lastoption; + continue; + } + }else + value= 0.0; + if (key == 'd') + qh->lower_threshold[idx]= value; + else + qh->upper_threshold[idx]= value; + } + } + }else if (*s == 'Q') { + lastoption= 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, qh->ferr, 7047, "qhull option warning: no dimension given for Qhull option 'Q%c'\n", + key); + lastwarning= lastoption; + continue; + } + idx= qh_strtol(s, &s); + if (idx >= maxdim) { + qh_fprintf(qh, qh->ferr, 7048, "qhull option warning: dimension %d for Qhull option 'Q%c' is >= %d. Ignored\n", + idx, key, maxdim); + lastwarning= lastoption; + 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; + } + if (lastwarning && !qh->ALLOWwarning) { + qh_fprintf(qh, qh->ferr, 6036, "qhull option error: see previous warnings, use 'Qw' to override: '%s' (last offset %d)\n", + command, (int)(lastwarning-command)); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } +} /* initthresholds */ + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="lib_check">-</a> + + qh_lib_check( qhullLibraryType, qhTsize, vertexTsize, ridgeTsize, facetTsize, setTsize, qhmemTsize ) + Report error if library does not agree with caller + + notes: + NOerrors -- qh_lib_check can not call qh_errexit() +*/ +void qh_lib_check(int qhullLibraryType, int qhTsize, int vertexTsize, int ridgeTsize, int facetTsize, int setTsize, int qhmemTsize) { + int last_errcode= qh_ERRnone; + +#if defined(_MSC_VER) && defined(_DEBUG) && defined(QHULL_CRTDBG) /* user_r.h */ + /*_CrtSetBreakAlloc(744);*/ /* Break at memalloc {744}, or 'watch' _crtBreakAlloc */ + _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_DELAY_FREE_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) ); + _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG ); + _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDERR ); + _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG ); + _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDERR ); + _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG ); + _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDERR ); +#endif + + if (qhullLibraryType==QHULL_NON_REENTRANT) { /* 0 */ + qh_fprintf_stderr(6257, "qh_lib_check: Incorrect qhull library called. Caller uses non-reentrant Qhull with a static qhT. Qhull library is reentrant.\n"); + last_errcode= 6257; + }else if (qhullLibraryType==QHULL_QH_POINTER) { /* 1 */ + qh_fprintf_stderr(6258, "qh_lib_check: Incorrect qhull library called. Caller uses non-reentrant Qhull with a dynamic qhT via qh_QHpointer. Qhull library is reentrant.\n"); + last_errcode= 6258; + }else if (qhullLibraryType != QHULL_REENTRANT) { /* 2 */ + qh_fprintf_stderr(6262, "qh_lib_check: Expecting qhullLibraryType QHULL_NON_REENTRANT(0), QHULL_QH_POINTER(1), or QHULL_REENTRANT(2). Got %d\n", qhullLibraryType); + last_errcode= 6262; + } + if (qhTsize != (int)sizeof(qhT)) { + qh_fprintf_stderr(6249, "qh_lib_check: Incorrect qhull library called. Size of qhT for caller is %d, but for qhull library is %d.\n", qhTsize, (int)sizeof(qhT)); + last_errcode= 6249; + } + if (vertexTsize != (int)sizeof(vertexT)) { + qh_fprintf_stderr(6250, "qh_lib_check: Incorrect qhull library called. Size of vertexT for caller is %d, but for qhull library is %d.\n", vertexTsize, (int)sizeof(vertexT)); + last_errcode= 6250; + } + if (ridgeTsize != (int)sizeof(ridgeT)) { + qh_fprintf_stderr(6251, "qh_lib_check: Incorrect qhull library called. Size of ridgeT for caller is %d, but for qhull library is %d.\n", ridgeTsize, (int)sizeof(ridgeT)); + last_errcode= 6251; + } + if (facetTsize != (int)sizeof(facetT)) { + qh_fprintf_stderr(6252, "qh_lib_check: Incorrect qhull library called. Size of facetT for caller is %d, but for qhull library is %d.\n", facetTsize, (int)sizeof(facetT)); + last_errcode= 6252; + } + if (setTsize && setTsize != (int)sizeof(setT)) { + qh_fprintf_stderr(6253, "qh_lib_check: Incorrect qhull library called. Size of setT for caller is %d, but for qhull library is %d.\n", setTsize, (int)sizeof(setT)); + last_errcode= 6253; + } + if (qhmemTsize && qhmemTsize != sizeof(qhmemT)) { + qh_fprintf_stderr(6254, "qh_lib_check: Incorrect qhull library called. Size of qhmemT for caller is %d, but for qhull library is %d.\n", qhmemTsize, sizeof(qhmemT)); + last_errcode= 6254; + } + if (last_errcode) { + qh_fprintf_stderr(6259, "qhull internal error (qh_lib_check): Cannot continue due to QH%d. '%s' is not reentrant (e.g., qhull.so) or out-of-date. Exit with %d\n", + last_errcode, qh_version2, last_errcode - 6200); + qh_exit(last_errcode - 6200); /* can not use qh_errexit(), must be less than 255 */ + } +} /* lib_check */ + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="option">-</a> + + qh_option(qh, 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(qhT *qh, const char *option, int *i, realT *r) { + char buf[200]; + int buflen, remainder; + + if (strlen(option) > sizeof(buf)-30-30) { + qh_fprintf(qh, qh->ferr, 6408, "qhull internal error (qh_option): option (%d chars) has more than %d chars. May overflow temporary buffer. Option '%s'\n", + (int)strlen(option), (int)sizeof(buf)-30-30, option); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + sprintf(buf, " %s", option); + if (i) + sprintf(buf+strlen(buf), " %d", *i); + if (r) + sprintf(buf+strlen(buf), " %2.2g", *r); + buflen= (int)strlen(buf); /* WARN64 */ + qh->qhull_optionlen += buflen; + remainder= (int)(sizeof(qh->qhull_options) - strlen(qh->qhull_options)) - 1; /* WARN64 */ + maximize_(remainder, 0); + if (qh->qhull_optionlen >= qh_OPTIONline && remainder > 0) { + strncat(qh->qhull_options, "\n", (unsigned int)remainder); + --remainder; + qh->qhull_optionlen= buflen; + } + if (buflen > remainder) { + trace1((qh, qh->ferr, 1058, "qh_option: option would overflow qh.qhull_options. Truncated '%s'\n", buf)); + } + strncat(qh->qhull_options, buf, (unsigned int)remainder); +} /* option */ + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="zero">-</a> + + qh_zero( qh, errfile ) + Initialize and zero Qhull's memory for qh_new_qhull() + + notes: + Not needed in global_r.c because static variables are initialized to zero +*/ +void qh_zero(qhT *qh, FILE *errfile) { + memset((char *)qh, 0, sizeof(qhT)); /* every field is 0, FALSE, NULL */ + qh->NOerrexit= True; + qh_meminit(qh, errfile); +} /* zero */ + diff --git a/contrib/libs/qhull/libqhull_r/io_r.c b/contrib/libs/qhull/libqhull_r/io_r.c new file mode 100644 index 0000000000..a80a5b14a4 --- /dev/null +++ b/contrib/libs/qhull/libqhull_r/io_r.c @@ -0,0 +1,4128 @@ +/*<html><pre> -<a href="qh-io_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + io_r.c + Input/Output routines of qhull application + + see qh-io_r.htm and io_r.h + + see user_r.c for qh_errprint and qh_printfacetlist + + unix_r.c calls qh_readpoints and qh_produce_output + + unix_r.c and user_r.c are the only callers of io_r.c functions + This allows the user to avoid loading io_r.o from qhull.a + + Copyright (c) 1993-2020 The Geometry Center. + $Id: //main/2019/qhull/src/libqhull_r/io_r.c#12 $$Change: 2965 $ + $DateTime: 2020/06/04 15:37:41 $$Author: bbarber $ +*/ + +#include "qhull_ra.h" + +/*========= -functions in alphabetical order after qh_produce_output(qh) =====*/ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="produce_output">-</a> + + qh_produce_output(qh ) + qh_produce_output2(qh ) + prints out the result of qhull in desired format + qh_produce_output2 does not call qh_prepare_output + qh_checkpolygon is valid for 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(qhT *qh) { + int tempsize= qh_setsize(qh, qh->qhmem.tempstack); + + qh_prepare_output(qh); + qh_produce_output2(qh); + if (qh_setsize(qh, qh->qhmem.tempstack) != tempsize) { + qh_fprintf(qh, qh->ferr, 6206, "qhull internal error (qh_produce_output): temporary sets not empty(%d)\n", + qh_setsize(qh, qh->qhmem.tempstack)); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } +} /* produce_output */ + + +void qh_produce_output2(qhT *qh) { + int i, tempsize= qh_setsize(qh, qh->qhmem.tempstack), d_1; + + fflush(NULL); + if (qh->PRINTsummary) + qh_printsummary(qh, qh->ferr); + else if (qh->PRINTout[0] == qh_PRINTnone) + qh_printsummary(qh, qh->fout); + for (i=0; i < qh_PRINTEND; i++) + qh_printfacets(qh, qh->fout, qh->PRINTout[i], qh->facet_list, NULL, !qh_ALL); + fflush(NULL); + + qh_allstatistics(qh); + if (qh->PRINTprecision && !qh->MERGING && (qh->JOGGLEmax > REALmax/2 || qh->RERUN)) + qh_printstats(qh, qh->ferr, qh->qhstat.precision, NULL); + if (qh->VERIFYoutput && (zzval_(Zridge) > 0 || zzval_(Zridgemid) > 0)) + qh_printstats(qh, qh->ferr, qh->qhstat.vridges, NULL); + if (qh->PRINTstatistics) { + qh_printstatistics(qh, qh->ferr, ""); + qh_memstatistics(qh, qh->ferr); + d_1= (int)sizeof(setT) + (qh->hull_dim - 1) * SETelemsize; + qh_fprintf(qh, 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(qh, qh->qhmem.tempstack) != tempsize) { + qh_fprintf(qh, qh->ferr, 6065, "qhull internal error (qh_produce_output2): temporary sets not empty(%d)\n", + qh_setsize(qh, qh->qhmem.tempstack)); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } +} /* produce_output2 */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="dfacet">-</a> + + qh_dfacet(qh, id ) + print facet by id, for debugging + +*/ +void qh_dfacet(qhT *qh, unsigned int id) { + facetT *facet; + + FORALLfacets { + if (facet->id == id) { + qh_printfacet(qh, qh->fout, facet); + break; + } + } +} /* dfacet */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="dvertex">-</a> + + qh_dvertex(qh, id ) + print vertex by id, for debugging +*/ +void qh_dvertex(qhT *qh, unsigned int id) { + vertexT *vertex; + + FORALLvertices { + if (vertex->id == id) { + qh_printvertex(qh, qh->fout, vertex); + break; + } + } +} /* dvertex */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="compare_facetarea">-</a> + + 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 */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="compare_facetvisit">-</a> + + 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= (int)a->visitid)) + i= (int)(0 - a->id); /* sign distinguishes id from visitid */ + if (!(j= (int)b->visitid)) + j= (int)(0 - b->id); + return(i - j); +} /* compare_facetvisit */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="compare_nummerge">-</a> + + qh_compare_nummerge( p1, p2 ) + used by qsort() to order facets by number of merges + +notes: + called by qh_markkeep ('PMerge-keep') +*/ +int qh_compare_nummerge(const void *p1, const void *p2) { + const facetT *a= *((facetT *const*)p1), *b= *((facetT *const*)p2); + + return(a->nummerge - b->nummerge); +} /* compare_nummerge */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="copyfilename">-</a> + + qh_copyfilename(qh, dest, size, source, length ) + copy filename identified by qh_skipfilename() + + notes: + see qh_skipfilename() for syntax +*/ +void qh_copyfilename(qhT *qh, char *filename, int size, const char* source, int length) { + char c= *source; + + if (length > size + 1) { + qh_fprintf(qh, qh->ferr, 6040, "qhull error: filename is more than %d characters, %s\n", size-1, source); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + strncpy(filename, source, (size_t)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 */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="countfacets">-</a> + + qh_countfacets(qh, 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(qhT *qh, 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(qh, facet))) + facet->visitid= 0; + else { + facet->visitid= (unsigned int)(++numfacets); + totneighbors += qh_setsize(qh, facet->neighbors); + if (facet->simplicial) { + numsimplicial++; + if (facet->keepcentrum && facet->tricoplanar) + numtricoplanars++; + }else + numridges += qh_setsize(qh, facet->ridges); + if (facet->coplanarset) + numcoplanars += qh_setsize(qh, facet->coplanarset); + } + } + + FOREACHfacet_(facets) { + if ((facet->visible && qh->NEWfacets) + || (!printall && qh_skipfacet(qh, facet))) + facet->visitid= 0; + else { + facet->visitid= (unsigned int)(++numfacets); + totneighbors += qh_setsize(qh, facet->neighbors); + if (facet->simplicial){ + numsimplicial++; + if (facet->keepcentrum && facet->tricoplanar) + numtricoplanars++; + }else + numridges += qh_setsize(qh, facet->ridges); + if (facet->coplanarset) + numcoplanars += qh_setsize(qh, facet->coplanarset); + } + } + qh->visit_id += (unsigned int)numfacets + 1; + *numfacetsp= numfacets; + *numsimplicialp= numsimplicial; + *totneighborsp= totneighbors; + *numridgesp= numridges; + *numcoplanarsp= numcoplanars; + *numtricoplanarsp= numtricoplanars; +} /* countfacets */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="detvnorm">-</a> + + qh_detvnorm(qh, 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_r.c so that code for 'v Tv' can be removed by removing io_r.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(qhT *qh, 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, 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(qh, facet->vertices); + qh_setappend(qh, &points, facet->center); + } + } + if (numcenters > dim) { + simplex= qh_settemp(qh, qh->TEMPsize); + qh_setappend(qh, &simplex, vertex->point); + if (unbounded) + qh_setappend(qh, &simplex, midpoint); + qh_maxsimplex(qh, dim, points, NULL, 0, &simplex); + qh_setdelnth(qh, simplex, 0); + }else if (numcenters == dim) { + if (unbounded) + qh_setappend(qh, &points, midpoint); + simplex= points; + }else { + qh_fprintf(qh, qh->ferr, 6216, "qhull internal error (qh_detvnorm): too few points(%d) to compute separating plane\n", numcenters); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + i= 0; + gmcoord= qh->gm_matrix; + point0= SETfirstt_(simplex, pointT); + FOREACHpoint_(simplex) { + if (qh->IStracing >= 4) + qh_printmatrix(qh, 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(qh, dim, qh->gm_row, point0, True, + normal, &offset, &nearzero); + /* nearzero is true for axis-parallel hyperplanes (e.g., a bounding box). Should detect degenerate hyperplanes. See 'Tv' check following */ + 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(qh, vertex->point); + pointidA= qh_pointid(qh, 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, 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(qh, 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, 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_(qh, 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, 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(qh, &simplex); + qh_settempfree(qh, &points); + return normal; +} /* detvnorm */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="detvridge">-</a> + + qh_detvridge(qh, 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(qhT *qh, vertexT *vertex) { + setT *centers= qh_settemp(qh, qh->TEMPsize); + setT *tricenters= qh_settemp(qh, qh->TEMPsize); + facetT *neighbor, **neighborp; + boolT firstinf= True; + + FOREACHneighbor_(vertex) { + if (neighbor->seen) { + if (neighbor->visitid) { + if (!neighbor->tricoplanar || qh_setunique(qh, &tricenters, neighbor->center)) + qh_setappend(qh, ¢ers, neighbor); + }else if (firstinf) { + firstinf= False; + qh_setappend(qh, ¢ers, neighbor); + } + } + } + qsort(SETaddr_(centers, facetT), (size_t)qh_setsize(qh, centers), + sizeof(facetT *), qh_compare_facetvisit); + qh_settempfree(qh, &tricenters); + return centers; +} /* detvridge */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="detvridge3">-</a> + + qh_detvridge3(qh, 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(qhT *qh, vertexT *atvertex, vertexT *vertex) { + setT *centers= qh_settemp(qh, qh->TEMPsize); + setT *tricenters= qh_settemp(qh, 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(qh, &tricenters, facet->center)) + qh_setappend(qh, ¢ers, facet); + }else if (firstinf) { + firstinf= False; + qh_setappend(qh, ¢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, qh->ferr, 6217, "qhull internal error (qh_detvridge3): neighbors of vertex p%d are not connected at facet %d\n", + qh_pointid(qh, vertex->point), neighbor->id); + qh_errexit(qh, qh_ERRqhull, neighbor, NULL); + } + } + } + FOREACHneighbor_(atvertex) + neighbor->seen2= True; + qh_settempfree(qh, &tricenters); + return centers; +} /* detvridge3 */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="eachvoronoi">-</a> + + qh_eachvoronoi(qh, 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*) + fp may be NULL for QhullQh::qh_fprintf which calls appendQhullMessage + 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(qhT *qh, 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, 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(qh, tricenters, 0); + FOREACHneighborA_(vertex) { + if (neighborA->seen) { + if (neighborA->visitid) { + if (!neighborA->tricoplanar || qh_setunique(qh, &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, qh->ferr, 4017, "qh_eachvoronoi: Voronoi ridge of %d vertices between sites %d and %d\n", + count, qh_pointid(qh, atvertex->point), qh_pointid(qh, vertex->point))); + if (printvridge) { + if (inorder && qh->hull_dim == 3+1) /* 3-d Voronoi diagram */ + centers= qh_detvridge3(qh, atvertex, vertex); + else + centers= qh_detvridge(qh, vertex); + (*printvridge)(qh, fp, atvertex, vertex, centers, unbounded); + qh_settempfree(qh, ¢ers); + } + } + } + } + } + } + FOREACHneighbor_(atvertex) + neighbor->seen= False; + qh_settempfree(qh, &tricenters); + return totridges; +} /* eachvoronoi */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="eachvoronoi_all">-</a> + + qh_eachvoronoi_all(qh, 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(qhT *qh, 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, qh_ASvoronoi); + qh_vertexneighbors(qh); + maximize_(qh->visit_id, (unsigned int)qh->num_facets); + FORALLfacets { + facet->visitid= 0; + facet->seen= False; + facet->seen2= True; + } + FORALLfacets { + if (facet->upperdelaunay == isUpper) + facet->visitid= (unsigned int)(numcenters++); + } + FORALLvertices + vertex->seen= False; + FORALLvertices { + if (qh->GOODvertex > 0 && qh_pointid(qh, vertex->point)+1 != qh->GOODvertex) + continue; + totridges += qh_eachvoronoi(qh, fp, printvridge, vertex, + !qh_ALL, innerouter, inorder); + } + return totridges; +} /* eachvoronoi_all */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="facet2point">-</a> + + qh_facet2point(qh, 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(qhT *qh, 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(qh, vertex0->point, facet, &dist); + *mindist= dist; + *point0= qh_projectpoint(qh, vertex0->point, facet, dist); + qh_distplane(qh, vertex1->point, facet, &dist); + minimize_(*mindist, dist); + *point1= qh_projectpoint(qh, vertex1->point, facet, dist); +} /* facet2point */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="facetvertices">-</a> + + qh_facetvertices(qh, 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(qhT *qh, 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, qh->num_vertices); + FORALLvertices { + vertex->visitid= qh->vertex_visit; + qh_setappend(qh, &vertices, vertex); + } + }else { + vertices= qh_settemp(qh, qh->TEMPsize); + FORALLfacet_(facetlist) { + if (!allfacets && qh_skipfacet(qh, facet)) + continue; + FOREACHvertex_(facet->vertices) { + if (vertex->visitid != qh->vertex_visit) { + vertex->visitid= qh->vertex_visit; + qh_setappend(qh, &vertices, vertex); + } + } + } + } + FOREACHfacet_(facets) { + if (!allfacets && qh_skipfacet(qh, facet)) + continue; + FOREACHvertex_(facet->vertices) { + if (vertex->visitid != qh->vertex_visit) { + vertex->visitid= qh->vertex_visit; + qh_setappend(qh, &vertices, vertex); + } + } + } + return vertices; +} /* facetvertices */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="geomplanes">-</a> + + qh_geomplanes(qh, 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_r.c with roundoff covered by qh_GEOMepsilon +*/ +void qh_geomplanes(qhT *qh, facetT *facet, realT *outerplane, realT *innerplane) { + realT radius; + + if (qh->MERGING || qh->JOGGLEmax < REALmax/2) { + qh_outerinner(qh, 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 */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="markkeep">-</a> + + qh_markkeep(qh, facetlist ) + restrict good facets for qh.KEEParea, qh.KEEPmerge, and qh.KEEPminArea + ignores visible facets (!part of convex hull) + + returns: + may clear facet->good + recomputes qh.num_good + + notes: + only called by qh_prepare_output after qh_findgood_all + does not throw errors except memory/corruption of qset_r.c + + 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(qhT *qh, facetT *facetlist) { + facetT *facet, **facetp; + setT *facets= qh_settemp(qh, qh->num_facets); + int size, count; + + trace2((qh, 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(qh, &facets, facet); + } + size= qh_setsize(qh, 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_nummerge); + 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(qh, &facets); + count= 0; + FORALLfacet_(facetlist) { + if (facet->good) + count++; + } + qh->num_good= count; +} /* markkeep */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="markvoronoi">-</a> + + qh_markvoronoi(qh, 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(qhT *qh, 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, qh_ASvoronoi); /* in case, qh_printvdiagram2 called by user */ + qh_vertexneighbors(qh); + vertices= qh_pointvertex(qh); + if (qh->ATinfinity) + SETelem_(vertices, qh->num_points-1)= NULL; + qh->visit_id++; + maximize_(qh->visit_id, (unsigned int)qh->num_facets); + FORALLfacet_(facetlist) { + if (printall || !qh_skipfacet(qh, facet)) { + if (!facet->upperdelaunay) { + isLower= True; + break; + } + } + } + FOREACHfacet_(facets) { + if (printall || !qh_skipfacet(qh, 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(qh, facet)) + facet->visitid= (unsigned int)(numcenters++); + } + FOREACHfacet_(facets) { + if (printall || !qh_skipfacet(qh, facet)) + facet->visitid= (unsigned int)(numcenters++); + } + *isLowerp= isLower; + *numcentersp= numcenters; + trace2((qh, qh->ferr, 2007, "qh_markvoronoi: isLower %d numcenters %d\n", isLower, numcenters)); + return vertices; +} /* markvoronoi */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="order_vertexneighbors">-</a> + + qh_order_vertexneighbors(qh, vertex ) + order facet neighbors of vertex by 2-d (orientation), 3-d (adjacency), or n-d (f.visitid,id) + + notes: + error if qh_vertexneighbors not called beforehand + only 2-d orients the neighbors + for 4-d and higher + set or clear f.visitid for qh_compare_facetvisit + for example, use qh_markvoronoi (e.g., qh_printvornoi) or qh_countfacets (e.g., qh_printvneighbors) + + design (2-d): + see qh_printextremes_2d + design (3-d): + 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 + design (n-d): + qsort by f.visitid, or f.facetid (qh_compare_facetvisit) + facet_id is negated (sorted before visit_id facets) +*/ +void qh_order_vertexneighbors(qhT *qh, vertexT *vertex) { + setT *newset; + facetT *facet, *facetA, *facetB, *neighbor, **neighborp; + vertexT *vertexA; + int numneighbors; + + trace4((qh, qh->ferr, 4018, "qh_order_vertexneighbors: order facet neighbors of v%d by 2-d (orientation), 3-d (adjacency), or n-d (f.visitid,id)\n", vertex->id)); + if (!qh->VERTEXneighbors) { + qh_fprintf(qh, qh->ferr, 6428, "qhull internal error (qh_order_vertexneighbors): call qh_vertexneighbors before calling qh_order_vertexneighbors\n"); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + if (qh->hull_dim == 2) { + facetA= SETfirstt_(vertex->neighbors, facetT); + if (facetA->toporient ^ qh_ORIENTclock) + vertexA= SETfirstt_(facetA->vertices, vertexT); + else + vertexA= SETsecondt_(facetA->vertices, vertexT); + if (vertexA!=vertex) { + facetB= SETsecondt_(vertex->neighbors, facetT); + SETfirst_(vertex->neighbors)= facetB; + SETsecond_(vertex->neighbors)= facetA; + } + }else if (qh->hull_dim == 3) { + newset= qh_settemp(qh, qh_setsize(qh, vertex->neighbors)); + facet= (facetT *)qh_setdellast(vertex->neighbors); + qh_setappend(qh, &newset, facet); + while (qh_setsize(qh, vertex->neighbors)) { + FOREACHneighbor_(vertex) { + if (qh_setin(facet->neighbors, neighbor)) { + qh_setdel(vertex->neighbors, neighbor); + qh_setappend(qh, &newset, neighbor); + facet= neighbor; + break; + } + } + if (!neighbor) { + qh_fprintf(qh, 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, qh_ERRqhull, facet, NULL); + } + } + qh_setfree(qh, &vertex->neighbors); + qh_settemppop(qh); + vertex->neighbors= newset; + }else { /* qh.hull_dim >= 4 */ + numneighbors= qh_setsize(qh, vertex->neighbors); + qsort(SETaddr_(vertex->neighbors, facetT), (size_t)numneighbors, + sizeof(facetT *), qh_compare_facetvisit); + } +} /* order_vertexneighbors */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="prepare_output">-</a> + + qh_prepare_output(qh ) + prepare for qh_produce_output2(qh) according to + qh.KEEPminArea, KEEParea, KEEPmerge, GOODvertex, GOODthreshold, GOODpoint, ONLYgood, SPLITthresholds + does not reset facet->good + + notes + called by qh_produce_output, qh_new_qhull, Qhull.outputQhull + except for PRINTstatistics, no-op if previously called with same options +*/ +void qh_prepare_output(qhT *qh) { + if (qh->VORONOI) { + qh_clearcenters(qh, qh_ASvoronoi); /* must be before qh_triangulate */ + qh_vertexneighbors(qh); + } + if (qh->TRIangulate && !qh->hasTriangulation) { + qh_triangulate(qh); + if (qh->VERIFYoutput && !qh->CHECKfrequently) + qh_checkpolygon(qh, qh->facet_list); + } + qh_findgood_all(qh, qh->facet_list); + if (qh->GETarea) + qh_getarea(qh, qh->facet_list); + if (qh->KEEParea || qh->KEEPmerge || qh->KEEPminArea < REALmax/2) + qh_markkeep(qh, qh->facet_list); + if (qh->PRINTstatistics) + qh_collectstatistics(qh); +} + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printafacet">-</a> + + qh_printafacet(qh, 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(qhT *qh, 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(qh, facet)) + return; + if (facet->visible && qh->NEWfacets && format != qh_PRINTfacets) + return; + qh->printoutnum++; + switch (format) { + case qh_PRINTarea: + if (facet->isarea) { + qh_fprintf(qh, fp, 9009, qh_REAL_1, facet->f.area); + qh_fprintf(qh, fp, 9010, "\n"); + }else + qh_fprintf(qh, fp, 9011, "0\n"); + break; + case qh_PRINTcoplanars: + qh_fprintf(qh, fp, 9012, "%d", qh_setsize(qh, facet->coplanarset)); + FOREACHpoint_(facet->coplanarset) + qh_fprintf(qh, fp, 9013, " %d", qh_pointid(qh, point)); + qh_fprintf(qh, fp, 9014, "\n"); + break; + case qh_PRINTcentrums: + qh_printcenter(qh, fp, format, NULL, facet); + break; + case qh_PRINTfacets: + qh_printfacet(qh, fp, facet); + break; + case qh_PRINTfacets_xridge: + qh_printfacetheader(qh, 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(qh, color, color); + if (qh->PRINTdim != qh->hull_dim) + qh_normalize2(qh, color, 3, True, NULL, NULL); + if (qh->hull_dim <= 2) + qh_printfacet2geom(qh, fp, facet, color); + else if (qh->hull_dim == 3) { + if (facet->simplicial) + qh_printfacet3geom_simplicial(qh, fp, facet, color); + else + qh_printfacet3geom_nonsimplicial(qh, fp, facet, color); + }else { + if (facet->simplicial) + qh_printfacet4geom_simplicial(qh, fp, facet, color); + else + qh_printfacet4geom_nonsimplicial(qh, fp, facet, color); + } + break; + case qh_PRINTids: + qh_fprintf(qh, 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(qh, fp, facet, format); + else if (facet->simplicial || qh->hull_dim == 2 || format == qh_PRINToff) + qh_printfacetNvertex_simplicial(qh, fp, facet, format); + else + qh_printfacetNvertex_nonsimplicial(qh, fp, facet, qh->printoutvar++, format); + break; + case qh_PRINTinner: + qh_outerinner(qh, facet, NULL, &innerplane); + offset= facet->offset - innerplane; + goto LABELprintnorm; + break; /* prevent warning */ + case qh_PRINTmerges: + qh_fprintf(qh, fp, 9016, "%d\n", facet->nummerge); + break; + case qh_PRINTnormals: + offset= facet->offset; + goto LABELprintnorm; + break; /* prevent warning */ + case qh_PRINTouter: + qh_outerinner(qh, facet, &outerplane, NULL); + offset= facet->offset - outerplane; + LABELprintnorm: + if (!facet->normal) { + qh_fprintf(qh, fp, 9017, "no normal for facet f%d\n", facet->id); + break; + } + if (qh->CDDoutput) { + qh_fprintf(qh, fp, 9018, qh_REAL_1, -offset); + for (k=0; k < qh->hull_dim; k++) + qh_fprintf(qh, fp, 9019, qh_REAL_1, -facet->normal[k]); + }else { + for (k=0; k < qh->hull_dim; k++) + qh_fprintf(qh, fp, 9020, qh_REAL_1, facet->normal[k]); + qh_fprintf(qh, fp, 9021, qh_REAL_1, offset); + } + qh_fprintf(qh, 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(qh, fp, facet, format, qh->printoutvar++); + else + qh_printfacet3math(qh, fp, facet, format, qh->printoutvar++); + break; + case qh_PRINTneighbors: + qh_fprintf(qh, fp, 9023, "%d", qh_setsize(qh, facet->neighbors)); + FOREACHneighbor_(facet) + qh_fprintf(qh, fp, 9024, " %d", + neighbor->visitid ? neighbor->visitid - 1: 0 - neighbor->id); + qh_fprintf(qh, fp, 9025, "\n"); + break; + case qh_PRINTpointintersect: + if (!qh->feasible_point) { + qh_fprintf(qh, qh->ferr, 6067, "qhull input error (qh_printafacet): option 'Fp' needs qh->feasible_point\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + if (facet->offset > 0) + goto LABELprintinfinite; + point= coordp= (coordT *)qh_memalloc(qh, 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(qh, point, qh->normal_size); + goto LABELprintinfinite; + } + } + } + qh_printpoint(qh, fp, NULL, point); + qh_memfree(qh, point, qh->normal_size); + break; + LABELprintinfinite: + for (k=qh->hull_dim; k--; ) + qh_fprintf(qh, fp, 9026, qh_REAL_1, qh_INFINITE); + qh_fprintf(qh, fp, 9027, "\n"); + break; + case qh_PRINTpointnearest: + FOREACHpoint_(facet->coplanarset) { + int id, id2; + vertex= qh_nearvertex(qh, facet, point, &dist); + id= qh_pointid(qh, vertex->point); + id2= qh_pointid(qh, point); + qh_fprintf(qh, 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(qh, fp, 9029, "1 "); + qh_printcenter(qh, fp, format, NULL, facet); + break; + case qh_PRINTvertices: + qh_fprintf(qh, fp, 9030, "%d", qh_setsize(qh, facet->vertices)); + FOREACHvertex_(facet->vertices) + qh_fprintf(qh, fp, 9031, " %d", qh_pointid(qh, vertex->point)); + qh_fprintf(qh, fp, 9032, "\n"); + break; + default: + break; + } +} /* printafacet */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printbegin">-</a> + + qh_printbegin(qh ) + 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(qhT *qh, 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(qh, facetlist, facets, printall, &numfacets, &numsimplicial, + &totneighbors, &numridges, &numcoplanars, &numtricoplanars); + switch (format) { + case qh_PRINTnone: + break; + case qh_PRINTarea: + qh_fprintf(qh, fp, 9033, "%d\n", numfacets); + break; + case qh_PRINTcoplanars: + qh_fprintf(qh, fp, 9034, "%d\n", numfacets); + break; + case qh_PRINTcentrums: + if (qh->CENTERtype == qh_ASnone) + qh_clearcenters(qh, qh_AScentrum); + qh_fprintf(qh, fp, 9035, "%d\n%d\n", qh->hull_dim, numfacets); + break; + case qh_PRINTfacets: + case qh_PRINTfacets_xridge: + if (facetlist) + qh_printvertexlist(qh, 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, 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, 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, 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, qh->ferr, 7052, "qhull warning: 'Gnh' generates no output in 4-d\n"); + if (qh->PRINTdim == 2) { + qh_fprintf(qh, fp, 9036, "{appearance {linewidth 3} LIST # %s | %s\n", + qh->rbox_command, qh->qhull_command); + }else if (qh->PRINTdim == 3) { + qh_fprintf(qh, 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(qh, NULL, facet, &num, printall); + FOREACHfacet_(facets) + qh_printend4geom(qh, NULL, facet, &num, printall); + qh->ridgeoutnum= num; + qh->printoutvar= 0; /* counts number of ridges in output */ + qh_fprintf(qh, fp, 9038, "LIST # %s | %s\n", qh->rbox_command, qh->qhull_command); + } + + if (qh->PRINTdots) { + qh->printoutnum++; + num= qh->num_points + qh_setsize(qh, qh->other_points); + if (qh->DELAUNAY && qh->ATinfinity) + num--; + if (qh->PRINTdim == 4) + qh_fprintf(qh, fp, 9039, "4VECT %d %d 1\n", num, num); + else + qh_fprintf(qh, fp, 9040, "VECT %d %d 1\n", num, num); + + for (i=num; i--; ) { + if (i % 20 == 0) + qh_fprintf(qh, fp, 9041, "\n"); + qh_fprintf(qh, fp, 9042, "1 "); + } + qh_fprintf(qh, fp, 9043, "# 1 point per line\n1 "); + for (i=num-1; i--; ) { /* num at least 3 for D2 */ + if (i % 20 == 0) + qh_fprintf(qh, fp, 9044, "\n"); + qh_fprintf(qh, fp, 9045, "0 "); + } + qh_fprintf(qh, fp, 9046, "# 1 color for all\n"); + FORALLpoints { + if (!qh->DELAUNAY || !qh->ATinfinity || qh_pointid(qh, point) != qh->num_points-1) { + if (qh->PRINTdim == 4) + qh_printpoint(qh, fp, NULL, point); + else + qh_printpoint3(qh, fp, point); + } + } + FOREACHpoint_(qh->other_points) { + if (qh->PRINTdim == 4) + qh_printpoint(qh, fp, NULL, point); + else + qh_printpoint3(qh, fp, point); + } + qh_fprintf(qh, 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(qh, 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(qh, facetlist, facets, printall); + if (qh->PRINTspheres && qh->PRINTdim <= 3) + qh_printspheres(qh, fp, vertices, qh->PRINTradius); + if (qh->PRINTcoplanar || qh->PRINTcentrums) { + qh->firstcentrum= True; + if (qh->PRINTcoplanar&& !qh->PRINTspheres) { + FOREACHvertex_(vertices) + qh_printpointvect2(qh, fp, vertex->point, NULL, qh->interior_point, qh->PRINTradius); + } + FORALLfacet_(facetlist) { + if (!printall && qh_skipfacet(qh, facet)) + continue; + if (!facet->normal) + continue; + if (qh->PRINTcentrums && qh->PRINTdim <= 3) + qh_printcentrum(qh, fp, facet, qh->PRINTcradius); + if (!qh->PRINTcoplanar) + continue; + FOREACHpoint_(facet->coplanarset) + qh_printpointvect2(qh, fp, point, facet->normal, NULL, qh->PRINTradius); + FOREACHpoint_(facet->outsideset) + qh_printpointvect2(qh, fp, point, facet->normal, NULL, qh->PRINTradius); + } + FOREACHfacet_(facets) { + if (!printall && qh_skipfacet(qh, facet)) + continue; + if (!facet->normal) + continue; + if (qh->PRINTcentrums && qh->PRINTdim <= 3) + qh_printcentrum(qh, fp, facet, qh->PRINTcradius); + if (!qh->PRINTcoplanar) + continue; + FOREACHpoint_(facet->coplanarset) + qh_printpointvect2(qh, fp, point, facet->normal, NULL, qh->PRINTradius); + FOREACHpoint_(facet->outsideset) + qh_printpointvect2(qh, fp, point, facet->normal, NULL, qh->PRINTradius); + } + } + qh_settempfree(qh, &vertices); + } + qh->visit_id++; /* for printing hyperplane intersections */ + break; + case qh_PRINTids: + qh_fprintf(qh, fp, 9049, "%d\n", numfacets); + break; + case qh_PRINTincidences: + if (qh->VORONOI && qh->PRINTprecision) + qh_fprintf(qh, qh->ferr, 7053, "qhull warning: input sites of Delaunay regions (option 'i'). Use option 'p' or 'o' for Voronoi centers. Disable warning with option 'Pp'\n"); + qh->printoutvar= (int)qh->vertex_id; /* centrum id for 4-d+, non-simplicial facets */ + if (qh->hull_dim <= 3) + qh_fprintf(qh, fp, 9050, "%d\n", numfacets); + else + qh_fprintf(qh, fp, 9051, "%d\n", numsimplicial+numridges); + break; + case qh_PRINTinner: + case qh_PRINTnormals: + case qh_PRINTouter: + if (qh->CDDoutput) + qh_fprintf(qh, fp, 9052, "%s | %s\nbegin\n %d %d real\n", qh->rbox_command, + qh->qhull_command, numfacets, qh->hull_dim+1); + else + qh_fprintf(qh, 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, qh->ferr, 7054, "qhull warning: output is the Delaunay triangulation\n"); + if (format == qh_PRINTmaple) { + if (qh->hull_dim == 2) + qh_fprintf(qh, fp, 9054, "PLOT(CURVES(\n"); + else + qh_fprintf(qh, fp, 9055, "PLOT3D(POLYGONS(\n"); + }else + qh_fprintf(qh, fp, 9056, "{\n"); + qh->printoutvar= 0; /* counts number of facets for notfirst */ + break; + case qh_PRINTmerges: + qh_fprintf(qh, fp, 9057, "%d\n", numfacets); + break; + case qh_PRINTpointintersect: + qh_fprintf(qh, fp, 9058, "%d\n%d\n", qh->hull_dim, numfacets); + break; + case qh_PRINTneighbors: + qh_fprintf(qh, 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(qh, fp, 9060, "%d\n%d %d %d\n", num, + qh->num_points+qh_setsize(qh, qh->other_points), numfacets, totneighbors/2); + else { /* qh_PRINTtriangles */ + qh->printoutvar= qh->num_points+qh_setsize(qh, qh->other_points); /* first centrum */ + if (qh->DELAUNAY) + num--; /* drop last dimension */ + qh_fprintf(qh, fp, 9061, "%d\n%d %d %d\n", num, qh->printoutvar + + numfacets - numsimplicial, numsimplicial + numridges, totneighbors/2); + } + FORALLpoints + qh_printpointid(qh, qh->fout, NULL, num, point, qh_IDunknown); + FOREACHpoint_(qh->other_points) + qh_printpointid(qh, qh->fout, NULL, num, point, qh_IDunknown); + if (format == qh_PRINTtriangles && qh->hull_dim > 2) { + FORALLfacets { + if (!facet->simplicial && facet->visitid) + qh_printcenter(qh, qh->fout, format, NULL, facet); + } + } + break; + case qh_PRINTpointnearest: + qh_fprintf(qh, fp, 9062, "%d\n", numcoplanars); + break; + case qh_PRINTpoints: + if (!qh->VORONOI) + goto LABELnoformat; + if (qh->CDDoutput) + qh_fprintf(qh, fp, 9063, "%s | %s\nbegin\n%d %d real\n", qh->rbox_command, + qh->qhull_command, numfacets, qh->hull_dim); + else + qh_fprintf(qh, fp, 9064, "%d\n%d\n", qh->hull_dim-1, numfacets); + break; + case qh_PRINTvertices: + qh_fprintf(qh, fp, 9065, "%d\n", numfacets); + break; + case qh_PRINTsummary: + default: + LABELnoformat: + qh_fprintf(qh, qh->ferr, 6068, "qhull internal error (qh_printbegin): can not use this format for dimension %d\n", + qh->hull_dim); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } +} /* printbegin */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printcenter">-</a> + + qh_printcenter(qh, 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(qhT *qh, 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(qh, 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(qh, facet->vertices); + for (k=0; k < num; k++) + qh_fprintf(qh, fp, 9067, qh_REAL_1, facet->center[k]); + }else { + for (k=0; k < num; k++) + qh_fprintf(qh, 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(qh, facet); + for (k=0; k < num; k++) + qh_fprintf(qh, fp, 9069, qh_REAL_1, facet->center[k]); + } + if (format == qh_PRINTgeom && num == 2) + qh_fprintf(qh, fp, 9070, " 0\n"); + else + qh_fprintf(qh, fp, 9071, "\n"); +} /* printcenter */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printcentrum">-</a> + + qh_printcentrum(qh, 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(qhT *qh, 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(qh, facet); + centrum= facet->center; + }else { + centrum= qh_getcentrum(qh, facet); + tempcentrum= True; + } + qh_fprintf(qh, fp, 9072, "{appearance {-normal -edge normscale 0} "); + if (qh->firstcentrum) { + qh->firstcentrum= False; + qh_fprintf(qh, 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(qh, fp, 9074, "{INST geom { : centrum } transform { # f%d\n", facet->id); + apex= SETfirstt_(facet->vertices, vertexT); + qh_distplane(qh, apex->point, facet, &dist); + projpt= qh_projectpoint(qh, 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(qh, xaxis, xaxis); + qh_projectdim3(qh, normal, normal); + qh_normalize2(qh, normal, qh->PRINTdim, True, NULL, NULL); + } + qh_crossproduct(3, xaxis, normal, yaxis); + qh_fprintf(qh, fp, 9075, "%8.4g %8.4g %8.4g 0\n", xaxis[0], xaxis[1], xaxis[2]); + qh_fprintf(qh, fp, 9076, "%8.4g %8.4g %8.4g 0\n", yaxis[0], yaxis[1], yaxis[2]); + qh_fprintf(qh, fp, 9077, "%8.4g %8.4g %8.4g 0\n", normal[0], normal[1], normal[2]); + qh_printpoint3(qh, fp, centrum); + qh_fprintf(qh, fp, 9078, "1 }}}\n"); + qh_memfree(qh, projpt, qh->normal_size); + qh_printpointvect(qh, fp, centrum, facet->normal, NULL, radius, green); + if (tempcentrum) + qh_memfree(qh, centrum, qh->normal_size); +} /* printcentrum */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printend">-</a> + + qh_printend(qh, fp, format ) + prints trailer for all output formats + + see: + qh_printbegin() and qh_printafacet() + +*/ +void qh_printend(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) { + int num; + facetT *facet, **facetp; + + if (!qh->printoutnum) + qh_fprintf(qh, 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(qh, fp, facet,&num, printall); + FOREACHfacet_(facets) + qh_printend4geom(qh, fp, facet, &num, printall); + if (num != qh->ridgeoutnum || qh->printoutvar != qh->ridgeoutnum) { + qh_fprintf(qh, 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, qh_ERRqhull, NULL, NULL); + } + }else + qh_fprintf(qh, fp, 9079, "}\n"); + break; + case qh_PRINTinner: + case qh_PRINTnormals: + case qh_PRINTouter: + if (qh->CDDoutput) + qh_fprintf(qh, fp, 9080, "end\n"); + break; + case qh_PRINTmaple: + qh_fprintf(qh, fp, 9081, "));\n"); + break; + case qh_PRINTmathematica: + qh_fprintf(qh, fp, 9082, "}\n"); + break; + case qh_PRINTpoints: + if (qh->CDDoutput) + qh_fprintf(qh, fp, 9083, "end\n"); + break; + default: + break; + } +} /* printend */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printend4geom">-</a> + + qh_printend4geom(qh, 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(qhT *qh, 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(qh, 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(qh, 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(qh, 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 */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printextremes">-</a> + + qh_printextremes(qh, 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(qhT *qh, 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, qh->other_points); + + points= qh_settemp(qh, allpoints); + qh_setzero(qh, points, 0, allpoints); + vertices= qh_facetvertices(qh, facetlist, facets, printall); + FOREACHvertex_(vertices) { + id= qh_pointid(qh, vertex->point); + if (id >= 0) { + SETelem_(points, id)= vertex->point; + numpoints++; + } + } + qh_settempfree(qh, &vertices); + qh_fprintf(qh, fp, 9086, "%d\n", numpoints); + FOREACHpoint_i_(qh, points) { + if (point) + qh_fprintf(qh, fp, 9087, "%d\n", point_i); + } + qh_settempfree(qh, &points); +} /* printextremes */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printextremes_2d">-</a> + + qh_printextremes_2d(qh, 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(qhT *qh, 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(qh, facetlist, facets, printall, &numfacets, &numsimplicial, + &totneighbors, &numridges, &numcoplanars, &numtricoplanars); /* marks qh->visit_id */ + vertices= qh_facetvertices(qh, facetlist, facets, printall); + qh_fprintf(qh, fp, 9088, "%d\n", qh_setsize(qh, vertices)); + qh_settempfree(qh, &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, 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, qh_ERRqhull, facet, nextfacet); + } + if (facet->visitid) { + if (vertexA->visitid != qh->vertex_visit) { + vertexA->visitid= qh->vertex_visit; + qh_fprintf(qh, fp, 9089, "%d\n", qh_pointid(qh, vertexA->point)); + } + if (vertexB->visitid != qh->vertex_visit) { + vertexB->visitid= qh->vertex_visit; + qh_fprintf(qh, fp, 9090, "%d\n", qh_pointid(qh, vertexB->point)); + } + } + facet->visitid= qh->visit_id; + facet= nextfacet; + }while (facet && facet != startfacet); +} /* printextremes_2d */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printextremes_d">-</a> + + qh_printextremes_d(qh, 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(qhT *qh, 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(qh, facetlist, facets, printall); + qh_vertexneighbors(qh); + 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(qh, fp, 9091, "%d\n", numpoints); + FOREACHvertex_(vertices) { + if (vertex->seen) + qh_fprintf(qh, fp, 9092, "%d\n", qh_pointid(qh, vertex->point)); + } + qh_settempfree(qh, &vertices); +} /* printextremes_d */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printfacet">-</a> + + qh_printfacet(qh, fp, facet ) + prints all fields of a facet to fp + + notes: + ridges printed in neighbor order +*/ +void qh_printfacet(qhT *qh, FILE *fp, facetT *facet) { + + qh_printfacetheader(qh, fp, facet); + if (facet->ridges) + qh_printfacetridges(qh, fp, facet); +} /* printfacet */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printfacet2geom">-</a> + + qh_printfacet2geom(qh, fp, facet, color ) + print facet as part of a 2-d VECT for Geomview + + notes: + assume precise calculations in io_r.c with roundoff covered by qh_GEOMepsilon + mindist is calculated within io_r.c. maxoutside is calculated elsewhere + so a DISTround error may have occurred. +*/ +void qh_printfacet2geom(qhT *qh, FILE *fp, facetT *facet, realT color[3]) { + pointT *point0, *point1; + realT mindist, innerplane, outerplane; + int k; + + qh_facet2point(qh, facet, &point0, &point1, &mindist); + qh_geomplanes(qh, facet, &outerplane, &innerplane); + if (qh->PRINTouter || (!qh->PRINTnoplanes && !qh->PRINTinner)) + qh_printfacet2geom_points(qh, 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(qh, fp, point0, point1, facet, innerplane, color); + } + qh_memfree(qh, point1, qh->normal_size); + qh_memfree(qh, point0, qh->normal_size); +} /* printfacet2geom */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printfacet2geom_points">-</a> + + qh_printfacet2geom_points(qh, 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(qhT *qh, FILE *fp, pointT *point1, pointT *point2, + facetT *facet, realT offset, realT color[3]) { + pointT *p1= point1, *p2= point2; + + qh_fprintf(qh, fp, 9093, "VECT 1 2 1 2 1 # f%d\n", facet->id); + if (offset != 0.0) { + p1= qh_projectpoint(qh, p1, facet, -offset); + p2= qh_projectpoint(qh, p2, facet, -offset); + } + qh_fprintf(qh, 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(qh, p1, qh->normal_size); + qh_memfree(qh, p2, qh->normal_size); + } + qh_fprintf(qh, fp, 9095, "%8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]); +} /* printfacet2geom_points */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printfacet2math">-</a> + + qh_printfacet2math(qh, 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(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format, int notfirst) { + pointT *point0, *point1; + realT mindist; + const char *pointfmt; + + qh_facet2point(qh, facet, &point0, &point1, &mindist); + if (notfirst) + qh_fprintf(qh, 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(qh, fp, 9097, pointfmt, point0[0], point0[1], point1[0], point1[1]); + qh_memfree(qh, point1, qh->normal_size); + qh_memfree(qh, point0, qh->normal_size); +} /* printfacet2math */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printfacet3geom_nonsimplicial">-</a> + + qh_printfacet3geom_nonsimplicial(qh, 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(qhT *qh, 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(qh, facet, &outerplane, &innerplane); + vertices= qh_facet3vertex(qh, facet); /* oriented */ + cntvertices= qh_setsize(qh, vertices); + projectedpoints= qh_settemp(qh, cntvertices); + FOREACHvertex_(vertices) { + zinc_(Zdistio); + qh_distplane(qh, vertex->point, facet, &dist); + projpt= qh_projectpoint(qh, vertex->point, facet, dist); + qh_setappend(qh, &projectedpoints, projpt); + } + if (qh->PRINTouter || (!qh->PRINTnoplanes && !qh->PRINTinner)) + qh_printfacet3geom_points(qh, 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(qh, fp, projectedpoints, facet, innerplane, color); + } + FOREACHpoint_(projectedpoints) + qh_memfree(qh, point, qh->normal_size); + qh_settempfree(qh, &projectedpoints); + qh_settempfree(qh, &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(qh, fp, facet, neighbor, ridge->vertices, black); + if (qh->PRINTridges) { + vertexA= SETfirstt_(ridge->vertices, vertexT); + vertexB= SETsecondt_(ridge->vertices, vertexT); + qh_printline3geom(qh, fp, vertexA->point, vertexB->point, green); + } + } + } + } +} /* printfacet3geom_nonsimplicial */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printfacet3geom_points">-</a> + + qh_printfacet3geom_points(qh, 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(qhT *qh, FILE *fp, setT *points, facetT *facet, realT offset, realT color[3]) { + int k, n= qh_setsize(qh, points), i; + pointT *point, **pointp; + setT *printpoints; + + qh_fprintf(qh, fp, 9098, "{ OFF %d 1 1 # f%d\n", n, facet->id); + if (offset != 0.0) { + printpoints= qh_settemp(qh, n); + FOREACHpoint_(points) + qh_setappend(qh, &printpoints, qh_projectpoint(qh, point, facet, -offset)); + }else + printpoints= points; + FOREACHpoint_(printpoints) { + for (k=0; k < qh->hull_dim; k++) { + if (k == qh->DROPdim) + qh_fprintf(qh, fp, 9099, "0 "); + else + qh_fprintf(qh, fp, 9100, "%8.4g ", point[k]); + } + if (printpoints != points) + qh_memfree(qh, point, qh->normal_size); + qh_fprintf(qh, fp, 9101, "\n"); + } + if (printpoints != points) + qh_settempfree(qh, &printpoints); + qh_fprintf(qh, fp, 9102, "%d ", n); + for (i=0; i < n; i++) + qh_fprintf(qh, fp, 9103, "%d ", i); + qh_fprintf(qh, fp, 9104, "%8.4g %8.4g %8.4g 1.0 }\n", color[0], color[1], color[2]); +} /* printfacet3geom_points */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printfacet3geom_simplicial">-</a> + + qh_printfacet3geom_simplicial(qh ) + 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_r.c with roundoff covered by qh_GEOMepsilon + innerplane may be off by qh->DISTround. Maxoutside is calculated elsewhere + so a DISTround error may have occurred. +*/ +void qh_printfacet3geom_simplicial(qhT *qh, 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(qh, facet, &outerplane, &innerplane); + vertices= qh_facet3vertex(qh, facet); + points= qh_settemp(qh, qh->TEMPsize); + FOREACHvertex_(vertices) + qh_setappend(qh, &points, vertex->point); + if (qh->PRINTouter || (!qh->PRINTnoplanes && !qh->PRINTinner)) + qh_printfacet3geom_points(qh, 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(qh, fp, points, facet, innerplane, color); + } + qh_settempfree(qh, &points); + qh_settempfree(qh, &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(qh, facet->vertices, qh->hull_dim, + SETindex_(facet->neighbors, neighbor), 0); + if (qh->DOintersections) + qh_printhyperplaneintersection(qh, fp, facet, neighbor, vertices, black); + if (qh->PRINTridges) { + vertexA= SETfirstt_(vertices, vertexT); + vertexB= SETsecondt_(vertices, vertexT); + qh_printline3geom(qh, fp, vertexA->point, vertexB->point, green); + } + qh_setfree(qh, &vertices); + } + } + } +} /* printfacet3geom_simplicial */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printfacet3math">-</a> + + qh_printfacet3math(qh, 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(qhT *qh, 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(qh, fp, 9105, ",\n"); + vertices= qh_facet3vertex(qh, facet); + points= qh_settemp(qh, qh_setsize(qh, vertices)); + FOREACHvertex_(vertices) { + zinc_(Zdistio); + qh_distplane(qh, vertex->point, facet, &dist); + point= qh_projectpoint(qh, vertex->point, facet, dist); + qh_setappend(qh, &points, point); + } + if (format == qh_PRINTmaple) { + qh_fprintf(qh, fp, 9106, "["); + pointfmt= "[%16.8f, %16.8f, %16.8f]"; + endfmt= "]"; + }else { + qh_fprintf(qh, fp, 9107, "Polygon[{"); + pointfmt= "{%16.8f, %16.8f, %16.8f}"; + endfmt= "}]"; + } + FOREACHpoint_(points) { + if (firstpoint) + firstpoint= False; + else + qh_fprintf(qh, fp, 9108, ",\n"); + qh_fprintf(qh, fp, 9109, pointfmt, point[0], point[1], point[2]); + } + FOREACHpoint_(points) + qh_memfree(qh, point, qh->normal_size); + qh_settempfree(qh, &points); + qh_settempfree(qh, &vertices); + qh_fprintf(qh, fp, 9110, "%s", endfmt); +} /* printfacet3math */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printfacet3vertex">-</a> + + qh_printfacet3vertex(qh, 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(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format) { + vertexT *vertex, **vertexp; + setT *vertices; + + vertices= qh_facet3vertex(qh, facet); + if (format == qh_PRINToff) + qh_fprintf(qh, fp, 9111, "%d ", qh_setsize(qh, vertices)); + FOREACHvertex_(vertices) + qh_fprintf(qh, fp, 9112, "%d ", qh_pointid(qh, vertex->point)); + qh_fprintf(qh, fp, 9113, "\n"); + qh_settempfree(qh, &vertices); +} /* printfacet3vertex */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printfacet4geom_nonsimplicial">-</a> + + qh_printfacet4geom_nonsimplicial(qh ) + 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(qhT *qh, 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(qh, fp, facet, neighbor, ridge->vertices, color); + else { + if (qh->DROPdim >= 0) + qh_fprintf(qh, fp, 9114, "OFF 3 1 1 # f%d\n", facet->id); + else { + qh->printoutvar++; + qh_fprintf(qh, fp, 9115, "# r%d between f%d f%d\n", ridge->id, facet->id, neighbor->id); + } + FOREACHvertex_(ridge->vertices) { + zinc_(Zdistio); + qh_distplane(qh, vertex->point,facet, &dist); + point=qh_projectpoint(qh, vertex->point,facet, dist); + for (k=0; k < qh->hull_dim; k++) { + if (k != qh->DROPdim) + qh_fprintf(qh, fp, 9116, "%8.4g ", point[k]); + } + qh_fprintf(qh, fp, 9117, "\n"); + qh_memfree(qh, point, qh->normal_size); + } + if (qh->DROPdim >= 0) + qh_fprintf(qh, fp, 9118, "3 0 1 2 %8.4g %8.4g %8.4g\n", color[0], color[1], color[2]); + } + } +} /* printfacet4geom_nonsimplicial */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printfacet4geom_simplicial">-</a> + + qh_printfacet4geom_simplicial(qh, 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(qhT *qh, 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(qh, facet->vertices, qh->hull_dim, + SETindex_(facet->neighbors, neighbor), 0); + if (qh->DOintersections) + qh_printhyperplaneintersection(qh, fp, facet, neighbor, vertices, color); + else { + if (qh->DROPdim >= 0) + qh_fprintf(qh, fp, 9119, "OFF 3 1 1 # ridge between f%d f%d\n", + facet->id, neighbor->id); + else { + qh->printoutvar++; + qh_fprintf(qh, 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(qh, fp, 9121, "%8.4g ", vertex->point[k]); + } + qh_fprintf(qh, fp, 9122, "\n"); + } + if (qh->DROPdim >= 0) + qh_fprintf(qh, fp, 9123, "3 0 1 2 %8.4g %8.4g %8.4g\n", color[0], color[1], color[2]); + } + qh_setfree(qh, &vertices); + } +} /* printfacet4geom_simplicial */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printfacetNvertex_nonsimplicial">-</a> + + qh_printfacetNvertex_nonsimplicial(qh, fp, facet, id, format ) + print vertices for an N-d non-simplicial facet + triangulates each ridge to the id +*/ +void qh_printfacetNvertex_nonsimplicial(qhT *qh, 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(qh, fp, 9124, "%d ", qh->hull_dim); + qh_fprintf(qh, fp, 9125, "%d ", id); + if ((ridge->top == facet) ^ qh_ORIENTclock) { + FOREACHvertex_(ridge->vertices) + qh_fprintf(qh, fp, 9126, "%d ", qh_pointid(qh, vertex->point)); + }else { + FOREACHvertexreverse12_(ridge->vertices) + qh_fprintf(qh, fp, 9127, "%d ", qh_pointid(qh, vertex->point)); + } + qh_fprintf(qh, fp, 9128, "\n"); + } +} /* printfacetNvertex_nonsimplicial */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printfacetNvertex_simplicial">-</a> + + qh_printfacetNvertex_simplicial(qh, 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(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format) { + vertexT *vertex, **vertexp; + + if (format == qh_PRINToff || format == qh_PRINTtriangles) + qh_fprintf(qh, fp, 9129, "%d ", qh_setsize(qh, facet->vertices)); + if ((facet->toporient ^ qh_ORIENTclock) + || (qh->hull_dim > 2 && !facet->simplicial)) { + FOREACHvertex_(facet->vertices) + qh_fprintf(qh, fp, 9130, "%d ", qh_pointid(qh, vertex->point)); + }else { + FOREACHvertexreverse12_(facet->vertices) + qh_fprintf(qh, fp, 9131, "%d ", qh_pointid(qh, vertex->point)); + } + qh_fprintf(qh, fp, 9132, "\n"); +} /* printfacetNvertex_simplicial */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printfacetheader">-</a> + + qh_printfacetheader(qh, fp, facet ) + prints header fields of a facet to fp + + notes: + for 'f' output and debugging + Same as QhullFacet::printHeader() +*/ +void qh_printfacetheader(qhT *qh, FILE *fp, facetT *facet) { + pointT *point, **pointp, *furthest; + facetT *neighbor, **neighborp; + realT dist; + + if (facet == qh_MERGEridge) { + qh_fprintf(qh, fp, 9133, " MERGEridge\n"); + return; + }else if (facet == qh_DUPLICATEridge) { + qh_fprintf(qh, fp, 9134, " DUPLICATEridge\n"); + return; + }else if (!facet) { + qh_fprintf(qh, fp, 9135, " NULLfacet\n"); + return; + } + qh->old_randomdist= qh->RANDOMdist; + qh->RANDOMdist= False; + qh_fprintf(qh, fp, 9136, "- f%d\n", facet->id); + qh_fprintf(qh, fp, 9137, " - flags:"); + if (facet->toporient) + qh_fprintf(qh, fp, 9138, " top"); + else + qh_fprintf(qh, fp, 9139, " bottom"); + if (facet->simplicial) + qh_fprintf(qh, fp, 9140, " simplicial"); + if (facet->tricoplanar) + qh_fprintf(qh, fp, 9141, " tricoplanar"); + if (facet->upperdelaunay) + qh_fprintf(qh, fp, 9142, " upperDelaunay"); + if (facet->visible) + qh_fprintf(qh, fp, 9143, " visible"); + if (facet->newfacet) + qh_fprintf(qh, fp, 9144, " newfacet"); + if (facet->tested) + qh_fprintf(qh, fp, 9145, " tested"); + if (!facet->good) + qh_fprintf(qh, fp, 9146, " notG"); + if (facet->seen && qh->IStracing) + qh_fprintf(qh, fp, 9147, " seen"); + if (facet->seen2 && qh->IStracing) + qh_fprintf(qh, fp, 9418, " seen2"); + if (facet->isarea) + qh_fprintf(qh, fp, 9419, " isarea"); + if (facet->coplanarhorizon) + qh_fprintf(qh, fp, 9148, " coplanarhorizon"); + if (facet->mergehorizon) + qh_fprintf(qh, fp, 9149, " mergehorizon"); + if (facet->cycledone) + qh_fprintf(qh, fp, 9420, " cycledone"); + if (facet->keepcentrum) + qh_fprintf(qh, fp, 9150, " keepcentrum"); + if (facet->dupridge) + qh_fprintf(qh, fp, 9151, " dupridge"); + if (facet->mergeridge && !facet->mergeridge2) + qh_fprintf(qh, fp, 9152, " mergeridge1"); + if (facet->mergeridge2) + qh_fprintf(qh, fp, 9153, " mergeridge2"); + if (facet->newmerge) + qh_fprintf(qh, fp, 9154, " newmerge"); + if (facet->flipped) + qh_fprintf(qh, fp, 9155, " flipped"); + if (facet->notfurthest) + qh_fprintf(qh, fp, 9156, " notfurthest"); + if (facet->degenerate) + qh_fprintf(qh, fp, 9157, " degenerate"); + if (facet->redundant) + qh_fprintf(qh, fp, 9158, " redundant"); + qh_fprintf(qh, fp, 9159, "\n"); + if (facet->isarea) + qh_fprintf(qh, fp, 9160, " - area: %2.2g\n", facet->f.area); + else if (qh->NEWfacets && facet->visible && facet->f.replace) + qh_fprintf(qh, fp, 9161, " - replacement: f%d\n", facet->f.replace->id); + else if (facet->newfacet) { + if (facet->f.samecycle && facet->f.samecycle != facet) + qh_fprintf(qh, 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(qh, fp, 9163, " - owner of normal & centrum is facet f%d\n", facet->f.triowner->id); + }else if (facet->f.newcycle) + qh_fprintf(qh, fp, 9164, " - was horizon to f%d\n", facet->f.newcycle->id); + if (facet->nummerge == qh_MAXnummerge) + qh_fprintf(qh, fp, 9427, " - merges: %dmax\n", qh_MAXnummerge); + else if (facet->nummerge) + qh_fprintf(qh, fp, 9165, " - merges: %d\n", facet->nummerge); + qh_printpointid(qh, fp, " - normal: ", qh->hull_dim, facet->normal, qh_IDunknown); + qh_fprintf(qh, fp, 9166, " - offset: %10.7g\n", facet->offset); + if (qh->CENTERtype == qh_ASvoronoi || facet->center) + qh_printcenter(qh, fp, qh_PRINTfacets, " - center: ", facet); +#if qh_MAXoutside + if (facet->maxoutside > qh->DISTround) /* initial value */ + qh_fprintf(qh, fp, 9167, " - maxoutside: %10.7g\n", facet->maxoutside); +#endif + if (!SETempty_(facet->outsideset)) { + furthest= (pointT *)qh_setlast(facet->outsideset); + if (qh_setsize(qh, facet->outsideset) < 6) { + qh_fprintf(qh, fp, 9168, " - outside set(furthest p%d):\n", qh_pointid(qh, furthest)); + FOREACHpoint_(facet->outsideset) + qh_printpoint(qh, fp, " ", point); + }else if (qh_setsize(qh, facet->outsideset) < 21) { + qh_printpoints(qh, fp, " - outside set:", facet->outsideset); + }else { + qh_fprintf(qh, fp, 9169, " - outside set: %d points.", qh_setsize(qh, facet->outsideset)); + qh_printpoint(qh, fp, " Furthest", furthest); + } +#if !qh_COMPUTEfurthest + qh_fprintf(qh, fp, 9170, " - furthest distance= %2.2g\n", facet->furthestdist); +#endif + } + if (!SETempty_(facet->coplanarset)) { + furthest= (pointT *)qh_setlast(facet->coplanarset); + if (qh_setsize(qh, facet->coplanarset) < 6) { + qh_fprintf(qh, fp, 9171, " - coplanar set(furthest p%d):\n", qh_pointid(qh, furthest)); + FOREACHpoint_(facet->coplanarset) + qh_printpoint(qh, fp, " ", point); + }else if (qh_setsize(qh, facet->coplanarset) < 21) { + qh_printpoints(qh, fp, " - coplanar set:", facet->coplanarset); + }else { + qh_fprintf(qh, fp, 9172, " - coplanar set: %d points.", qh_setsize(qh, facet->coplanarset)); + qh_printpoint(qh, fp, " Furthest", furthest); + } + zinc_(Zdistio); + qh_distplane(qh, furthest, facet, &dist); + qh_fprintf(qh, fp, 9173, " furthest distance= %2.2g\n", dist); + } + qh_printvertices(qh, fp, " - vertices:", facet->vertices); + qh_fprintf(qh, fp, 9174, " - neighboring facets:"); + FOREACHneighbor_(facet) { + if (neighbor == qh_MERGEridge) + qh_fprintf(qh, fp, 9175, " MERGEridge"); + else if (neighbor == qh_DUPLICATEridge) + qh_fprintf(qh, fp, 9176, " DUPLICATEridge"); + else + qh_fprintf(qh, fp, 9177, " f%d", neighbor->id); + } + qh_fprintf(qh, fp, 9178, "\n"); + qh->RANDOMdist= qh->old_randomdist; +} /* printfacetheader */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printfacetridges">-</a> + + qh_printfacetridges(qh, 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(qhT *qh, FILE *fp, facetT *facet) { + facetT *neighbor, **neighborp; + ridgeT *ridge, **ridgep; + int numridges= 0; + int n; + + if (facet->visible && qh->NEWfacets) { + qh_fprintf(qh, fp, 9179, " - ridges (tentative ids):"); + FOREACHridge_(facet->ridges) + qh_fprintf(qh, fp, 9180, " r%d", ridge->id); + qh_fprintf(qh, fp, 9181, "\n"); + }else { + qh_fprintf(qh, 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(qh, fp, ridge); + numridges++; + ridge= qh_nextridge3d(ridge, facet, NULL); + } + }else { + FOREACHneighbor_(facet) { + FOREACHridge_(facet->ridges) { + if (otherfacet_(ridge, facet) == neighbor && !ridge->seen) { + ridge->seen= True; + qh_printridge(qh, fp, ridge); + numridges++; + } + } + } + } + n= qh_setsize(qh, facet->ridges); + if (n == 1 && facet->newfacet && qh->NEWtentative) { + qh_fprintf(qh, fp, 9411, " - horizon ridge to visible facet\n"); + } + if (numridges != n) { + qh_fprintf(qh, fp, 9183, " - all ridges:"); + FOREACHridge_(facet->ridges) + qh_fprintf(qh, fp, 9184, " r%d", ridge->id); + qh_fprintf(qh, fp, 9185, "\n"); + } + /* non-3d ridges w/o non-simplicial neighbors */ + FOREACHridge_(facet->ridges) { + if (!ridge->seen) + qh_printridge(qh, fp, ridge); + } + } +} /* printfacetridges */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printfacets">-</a> + + qh_printfacets(qh, 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(qhT *qh, 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, 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(qh, facetlist, facets, printall); + center= qh_getcenter(qh, vertices); + qh_fprintf(qh, fp, 9186, "%d 1\n", qh->hull_dim); + qh_printpointid(qh, fp, NULL, qh->hull_dim, center, qh_IDunknown); + qh_memfree(qh, center, qh->normal_size); + qh_settempfree(qh, &vertices); + }else if (format == qh_PRINTextremes) { + if (qh->DELAUNAY) + qh_printextremes_d(qh, fp, facetlist, facets, printall); + else if (qh->hull_dim == 2) + qh_printextremes_2d(qh, fp, facetlist, facets, printall); + else + qh_printextremes(qh, fp, facetlist, facets, printall); + }else if (format == qh_PRINToptions) + qh_fprintf(qh, 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(qh, fp, facetlist, facets, printall); + else if (format == qh_PRINTqhull) + qh_fprintf(qh, fp, 9188, "%s | %s\n", qh->rbox_command, qh->qhull_command); + else if (format == qh_PRINTsize) { + qh_fprintf(qh, fp, 9189, "0\n2 "); + qh_fprintf(qh, fp, 9190, qh_REAL_1, qh->totarea); + qh_fprintf(qh, fp, 9191, qh_REAL_1, qh->totvol); + qh_fprintf(qh, fp, 9192, "\n"); + }else if (format == qh_PRINTsummary) { + qh_countfacets(qh, facetlist, facets, printall, &numfacets, &numsimplicial, + &totneighbors, &numridges, &numcoplanars, &numtricoplanars); + vertices= qh_facetvertices(qh, facetlist, facets, printall); + qh_fprintf(qh, fp, 9193, "10 %d %d %d %d %d %d %d %d %d %d\n2 ", qh->hull_dim, + qh->num_points + qh_setsize(qh, qh->other_points), + qh->num_vertices, qh->num_facets - qh->num_visible, + qh_setsize(qh, vertices), numfacets, numcoplanars, + numfacets - numsimplicial, zzval_(Zdelvertextot), + numtricoplanars); + qh_settempfree(qh, &vertices); + qh_outerinner(qh, NULL, &outerplane, &innerplane); + qh_fprintf(qh, fp, 9194, qh_REAL_2n, outerplane, innerplane); + }else if (format == qh_PRINTvneighbors) + qh_printvneighbors(qh, fp, facetlist, facets, printall); + else if (qh->VORONOI && format == qh_PRINToff) + qh_printvoronoi(qh, fp, format, facetlist, facets, printall); + else if (qh->VORONOI && format == qh_PRINTgeom) { + qh_printbegin(qh, fp, format, facetlist, facets, printall); + qh_printvoronoi(qh, fp, format, facetlist, facets, printall); + qh_printend(qh, fp, format, facetlist, facets, printall); + }else if (qh->VORONOI + && (format == qh_PRINTvertices || format == qh_PRINTinner || format == qh_PRINTouter)) + qh_printvdiagram(qh, fp, format, facetlist, facets, printall); + else { + qh_printbegin(qh, fp, format, facetlist, facets, printall); + FORALLfacet_(facetlist) + qh_printafacet(qh, fp, format, facet, printall); + FOREACHfacet_(facets) + qh_printafacet(qh, fp, format, facet, printall); + qh_printend(qh, fp, format, facetlist, facets, printall); + } + qh->RANDOMdist= qh->old_randomdist; +} /* printfacets */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printhyperplaneintersection">-</a> + + qh_printhyperplaneintersection(qh, 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(qhT *qh, 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(qh, facet1->normal, facet2->normal); + denominator= 1 - costheta * costheta; + i= qh_setsize(qh, vertices); + if (qh->hull_dim == 3) + qh_fprintf(qh, fp, 9195, "VECT 1 %d 1 %d 1 ", i, i); + else if (qh->hull_dim == 4 && qh->DROPdim >= 0) + qh_fprintf(qh, fp, 9196, "OFF 3 1 1 "); + else + qh->printoutvar++; + qh_fprintf(qh, 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(qh, vertex->point, facet1, &dist1); + qh_distplane(qh, 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(qh, p, p); + qh_fprintf(qh, fp, 9198, "%8.4g %8.4g %8.4g # ", p[0], p[1], p[2]); + }else + qh_fprintf(qh, fp, 9199, "%8.4g %8.4g %8.4g %8.4g # ", p[0], p[1], p[2], p[3]); + if (nearzero1+nearzero2) + qh_fprintf(qh, fp, 9200, "p%d(coplanar facets)\n", qh_pointid(qh, vertex->point)); + else + qh_fprintf(qh, fp, 9201, "projected p%d\n", qh_pointid(qh, vertex->point)); + } + if (qh->hull_dim == 3) + qh_fprintf(qh, 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(qh, fp, 9203, "3 0 1 2 %8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]); +} /* printhyperplaneintersection */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printline3geom">-</a> + + qh_printline3geom(qh, 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(qhT *qh, FILE *fp, pointT *pointA, pointT *pointB, realT color[3]) { + int k; + realT pA[4], pB[4]; + + qh_projectdim3(qh, pointA, pA); + qh_projectdim3(qh, 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(qh, fp, 9204, "VECT 1 2 1 2 1\n"); + for (k=0; k < 3; k++) + qh_fprintf(qh, fp, 9205, "%8.4g ", pB[k]); + qh_fprintf(qh, fp, 9206, " # p%d\n", qh_pointid(qh, pointB)); + }else + qh_fprintf(qh, fp, 9207, "VECT 1 1 1 1 1\n"); + for (k=0; k < 3; k++) + qh_fprintf(qh, fp, 9208, "%8.4g ", pA[k]); + qh_fprintf(qh, fp, 9209, " # p%d\n", qh_pointid(qh, pointA)); + qh_fprintf(qh, fp, 9210, "%8.4g %8.4g %8.4g 1\n", color[0], color[1], color[2]); +} + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printneighborhood">-</a> + + qh_printneighborhood(qh, fp, format, facetA, facetB, printall ) + print neighborhood of one or two facets + + notes: + calls qh_findgood_all() + bumps qh.visit_id +*/ +void qh_printneighborhood(qhT *qh, 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, qh->facet_list); + if (facetA == facetB) + facetB= NULL; + facets= qh_settemp(qh, 2*(qh_setsize(qh, 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(qh, &facets, facet); + } + FOREACHneighbor_(facet) { + if (neighbor->visitid == qh->visit_id) + continue; + neighbor->visitid= qh->visit_id; + if (printall || !qh_skipfacet(qh, neighbor)) + qh_setappend(qh, &facets, neighbor); + } + } + qh_printfacets(qh, fp, format, NULL, facets, printall); + qh_settempfree(qh, &facets); +} /* printneighborhood */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printpoint">-</a> + + qh_printpoint(qh, fp, string, point ) + qh_printpointid(qh, 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=qh_IDunknown(-1) or qh_IDnone(-3) + + notes: + nop if point is NULL + Same as QhullPoint's printPoint +*/ +void qh_printpoint(qhT *qh, FILE *fp, const char *string, pointT *point) { + int id= qh_pointid(qh, point); + + qh_printpointid(qh, fp, string, qh->hull_dim, point, id); +} /* printpoint */ + +void qh_printpointid(qhT *qh, FILE *fp, const char *string, int dim, pointT *point, int id) { + int k; + realT r; /*bug fix*/ + + if (!point) + return; + if (string) { + qh_fprintf(qh, fp, 9211, "%s", string); + if (id != qh_IDunknown && id != qh_IDnone) + qh_fprintf(qh, fp, 9212, " p%d: ", id); + } + for (k=dim; k--; ) { + r= *point++; + if (string) + qh_fprintf(qh, fp, 9213, " %8.4g", r); + else + qh_fprintf(qh, fp, 9214, qh_REAL_1, r); + } + qh_fprintf(qh, fp, 9215, "\n"); +} /* printpointid */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printpoint3">-</a> + + qh_printpoint3(qh, fp, point ) + prints 2-d, 3-d, or 4-d point as Geomview 3-d coordinates +*/ +void qh_printpoint3(qhT *qh, FILE *fp, pointT *point) { + int k; + realT p[4]; + + qh_projectdim3(qh, point, p); + for (k=0; k < 3; k++) + qh_fprintf(qh, fp, 9216, "%8.4g ", p[k]); + qh_fprintf(qh, fp, 9217, " # p%d\n", qh_pointid(qh, point)); +} /* printpoint3 */ + +/*---------------------------------------- +-printpoints- print pointids for a set of points starting at index + see geom_r.c +*/ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printpoints_out">-</a> + + qh_printpoints_out(qh, 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(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall) { + int allpoints= qh->num_points + qh_setsize(qh, 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(qh, allpoints); + qh_setzero(qh, points, 0, allpoints); + vertices= qh_facetvertices(qh, facetlist, facets, printall); + FOREACHvertex_(vertices) { + id= qh_pointid(qh, vertex->point); + if (id >= 0) + SETelem_(points, id)= vertex->point; + } + if (qh->KEEPinside || qh->KEEPcoplanar || qh->KEEPnearinside) { + FORALLfacet_(facetlist) { + if (!printall && qh_skipfacet(qh, facet)) + continue; + FOREACHpoint_(facet->coplanarset) { + id= qh_pointid(qh, point); + if (id >= 0) + SETelem_(points, id)= point; + } + } + FOREACHfacet_(facets) { + if (!printall && qh_skipfacet(qh, facet)) + continue; + FOREACHpoint_(facet->coplanarset) { + id= qh_pointid(qh, point); + if (id >= 0) + SETelem_(points, id)= point; + } + } + } + qh_settempfree(qh, &vertices); + FOREACHpoint_i_(qh, points) { + if (point) + numpoints++; + } + if (qh->CDDoutput) + qh_fprintf(qh, fp, 9218, "%s | %s\nbegin\n%d %d real\n", qh->rbox_command, + qh->qhull_command, numpoints, qh->hull_dim + 1); + else + qh_fprintf(qh, fp, 9219, "%d\n%d\n", qh->hull_dim, numpoints); + FOREACHpoint_i_(qh, points) { + if (point) { + if (qh->CDDoutput) + qh_fprintf(qh, fp, 9220, "1 "); + qh_printpoint(qh, fp, NULL, point); + } + } + if (qh->CDDoutput) + qh_fprintf(qh, fp, 9221, "end\n"); + qh_settempfree(qh, &points); +} /* printpoints_out */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printpointvect">-</a> + + qh_printpointvect(qh, 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(qhT *qh, 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(qh, diff, qh->hull_dim, True, NULL, NULL); + for (k=qh->hull_dim; k--; ) + pointA[k]= point[k]+diff[k] * radius; + qh_printline3geom(qh, fp, point, pointA, color); +} /* printpointvect */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printpointvect2">-</a> + + qh_printpointvect2(qh, 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(qhT *qh, FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius) { + realT red[3]={1, 0, 0}, yellow[3]={1, 1, 0}; + + qh_printpointvect(qh, fp, point, normal, center, radius, red); + qh_printpointvect(qh, fp, point, normal, center, -radius, yellow); +} /* printpointvect2 */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printridge">-</a> + + qh_printridge(qh, fp, ridge ) + prints the information in a ridge + + notes: + for qh_printfacetridges() + same as operator<< [QhullRidge.cpp] +*/ +void qh_printridge(qhT *qh, FILE *fp, ridgeT *ridge) { + + qh_fprintf(qh, fp, 9222, " - r%d", ridge->id); + if (ridge->tested) + qh_fprintf(qh, fp, 9223, " tested"); + if (ridge->nonconvex) + qh_fprintf(qh, fp, 9224, " nonconvex"); + if (ridge->mergevertex) + qh_fprintf(qh, fp, 9421, " mergevertex"); + if (ridge->mergevertex2) + qh_fprintf(qh, fp, 9422, " mergevertex2"); + if (ridge->simplicialtop) + qh_fprintf(qh, fp, 9425, " simplicialtop"); + if (ridge->simplicialbot) + qh_fprintf(qh, fp, 9423, " simplicialbot"); + qh_fprintf(qh, fp, 9225, "\n"); + qh_printvertices(qh, fp, " vertices:", ridge->vertices); + if (ridge->top && ridge->bottom) + qh_fprintf(qh, fp, 9226, " between f%d and f%d\n", + ridge->top->id, ridge->bottom->id); +} /* printridge */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printspheres">-</a> + + qh_printspheres(qh, fp, vertices, radius ) + prints 3-d vertices as OFF spheres + + notes: + inflated octahedron from Stuart Levy earth/mksphere2 +*/ +void qh_printspheres(qhT *qh, FILE *fp, setT *vertices, realT radius) { + vertexT *vertex, **vertexp; + + qh->printoutnum++; + qh_fprintf(qh, 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(qh, 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(qh, fp, vertex->point); + qh_fprintf(qh, fp, 9229, "1\n"); + } + qh_fprintf(qh, fp, 9230, "}}}\n"); +} /* printspheres */ + + +/*---------------------------------------------- +-printsummary- + see libqhull_r.c +*/ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printvdiagram">-</a> + + qh_printvdiagram(qh, 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(qhT *qh, 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, qh->ferr, 6219, "qhull internal error (qh_printvdiagram): unknown print format %d.\n", format); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + vertices= qh_markvoronoi(qh, facetlist, facets, printall, &isLower, &numcenters); + totcount= qh_printvdiagram2(qh, NULL, NULL, vertices, innerouter, False); + qh_fprintf(qh, fp, 9231, "%d\n", totcount); + totcount= qh_printvdiagram2(qh, fp, printvridge, vertices, innerouter, True /* inorder*/); + qh_settempfree(qh, &vertices); +#if 0 /* for testing qh_eachvoronoi_all */ + qh_fprintf(qh, fp, 9232, "\n"); + totcount= qh_eachvoronoi_all(qh, fp, printvridge, qh->UPPERdelaunay, innerouter, True /* inorder*/); + qh_fprintf(qh, fp, 9233, "%d\n", totcount); +#endif +} /* printvdiagram */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printvdiagram2">-</a> + + qh_printvdiagram2(qh, 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(qhT *qh, 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_(qh, vertices) { + if (vertex) { + if (qh->GOODvertex > 0 && qh_pointid(qh, vertex->point)+1 != qh->GOODvertex) + continue; + totcount += qh_eachvoronoi(qh, fp, printvridge, vertex, !qh_ALL, innerouter, inorder); + } + } + return totcount; +} /* printvdiagram2 */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printvertex">-</a> + + qh_printvertex(qh, fp, vertex ) + prints the information in a vertex + Duplicated as operator<< [QhullVertex.cpp] +*/ +void qh_printvertex(qhT *qh, FILE *fp, vertexT *vertex) { + pointT *point; + int k, count= 0; + facetT *neighbor, **neighborp; + realT r; /*bug fix*/ + + if (!vertex) { + qh_fprintf(qh, fp, 9234, " NULLvertex\n"); + return; + } + qh_fprintf(qh, fp, 9235, "- p%d(v%d):", qh_pointid(qh, vertex->point), vertex->id); + point= vertex->point; + if (point) { + for (k=qh->hull_dim; k--; ) { + r= *point++; + qh_fprintf(qh, fp, 9236, " %5.2g", r); + } + } + if (vertex->deleted) + qh_fprintf(qh, fp, 9237, " deleted"); + if (vertex->delridge) + qh_fprintf(qh, fp, 9238, " delridge"); + if (vertex->newfacet) + qh_fprintf(qh, fp, 9415, " newfacet"); + if (vertex->seen && qh->IStracing) + qh_fprintf(qh, fp, 9416, " seen"); + if (vertex->seen2 && qh->IStracing) + qh_fprintf(qh, fp, 9417, " seen2"); + qh_fprintf(qh, fp, 9239, "\n"); + if (vertex->neighbors) { + qh_fprintf(qh, fp, 9240, " neighbors:"); + FOREACHneighbor_(vertex) { + if (++count % 100 == 0) + qh_fprintf(qh, fp, 9241, "\n "); + qh_fprintf(qh, fp, 9242, " f%d", neighbor->id); + } + qh_fprintf(qh, fp, 9243, "\n"); + } +} /* printvertex */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printvertexlist">-</a> + + qh_printvertexlist(qh, fp, string, facetlist, facets, printall ) + prints vertices used by a facetlist or facet set + tests qh_skipfacet() if !printall +*/ +void qh_printvertexlist(qhT *qh, FILE *fp, const char* string, facetT *facetlist, + setT *facets, boolT printall) { + vertexT *vertex, **vertexp; + setT *vertices; + + vertices= qh_facetvertices(qh, facetlist, facets, printall); + qh_fprintf(qh, fp, 9244, "%s", string); + FOREACHvertex_(vertices) + qh_printvertex(qh, fp, vertex); + qh_settempfree(qh, &vertices); +} /* printvertexlist */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printvertices">-</a> + + qh_printvertices(qh, fp, string, vertices ) + prints vertices in a set + duplicated as printVertexSet [QhullVertex.cpp] +*/ +void qh_printvertices(qhT *qh, FILE *fp, const char* string, setT *vertices) { + vertexT *vertex, **vertexp; + + qh_fprintf(qh, fp, 9245, "%s", string); + FOREACHvertex_(vertices) + qh_fprintf(qh, fp, 9246, " p%d(v%d)", qh_pointid(qh, vertex->point), vertex->id); + qh_fprintf(qh, fp, 9247, "\n"); +} /* printvertices */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printvneighbors">-</a> + + qh_printvneighbors(qh, 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(qhT *qh, 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, qh->other_points); + vertexT *vertex, **vertexp; + int vertex_i, vertex_n; + facetT *facet, **facetp, *neighbor, **neighborp; + pointT *point, **pointp; + + qh_countfacets(qh, facetlist, facets, printall, &numfacets, &numsimplicial, + &totneighbors, &numridges, &numcoplanars, &numtricoplanars); /* sets facet->visitid */ + qh_fprintf(qh, fp, 9248, "%d\n", numpoints); + qh_vertexneighbors(qh); + vertices= qh_facetvertices(qh, facetlist, facets, printall); + vertex_points= qh_settemp(qh, numpoints); + coplanar_points= qh_settemp(qh, numpoints); + qh_setzero(qh, vertex_points, 0, numpoints); + qh_setzero(qh, coplanar_points, 0, numpoints); + FOREACHvertex_(vertices) + qh_point_add(qh, vertex_points, vertex->point, vertex); + FORALLfacet_(facetlist) { + FOREACHpoint_(facet->coplanarset) + qh_point_add(qh, coplanar_points, point, facet); + } + FOREACHfacet_(facets) { + FOREACHpoint_(facet->coplanarset) + qh_point_add(qh, coplanar_points, point, facet); + } + FOREACHvertex_i_(qh, vertex_points) { + if (vertex) { + numneighbors= qh_setsize(qh, vertex->neighbors); + qh_fprintf(qh, fp, 9249, "%d", numneighbors); + qh_order_vertexneighbors(qh, vertex); + FOREACHneighbor_(vertex) + qh_fprintf(qh, fp, 9250, " %d", + neighbor->visitid ? neighbor->visitid - 1 : 0 - neighbor->id); + qh_fprintf(qh, fp, 9251, "\n"); + }else if ((facet= SETelemt_(coplanar_points, vertex_i, facetT))) + qh_fprintf(qh, fp, 9252, "1 %d\n", + facet->visitid ? facet->visitid - 1 : 0 - facet->id); + else + qh_fprintf(qh, fp, 9253, "0\n"); + } + qh_settempfree(qh, &coplanar_points); + qh_settempfree(qh, &vertex_points); + qh_settempfree(qh, &vertices); +} /* printvneighbors */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printvoronoi">-</a> + + qh_printvoronoi(qh, 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(qhT *qh, 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(qh, facetlist, facets, printall, &isLower, &numcenters); + FOREACHvertex_i_(qh, 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(qh, fp, 9254, "{appearance {+edge -face} OFF %d %d 1 # Voronoi centers and cells\n", + numcenters, numvertices); + else + qh_fprintf(qh, fp, 9255, "%d\n%d %d 1\n", qh->hull_dim-1, numcenters, qh_setsize(qh, vertices)); + if (format == qh_PRINTgeom) { + for (k=qh->hull_dim-1; k--; ) + qh_fprintf(qh, fp, 9256, qh_REAL_1, 0.0); + qh_fprintf(qh, fp, 9257, " 0 # infinity not used\n"); + }else { + for (k=qh->hull_dim-1; k--; ) + qh_fprintf(qh, fp, 9258, qh_REAL_1, qh_INFINITE); + qh_fprintf(qh, fp, 9259, "\n"); + } + FORALLfacet_(facetlist) { + if (facet->visitid && facet->visitid < numfacets) { + if (format == qh_PRINTgeom) + qh_fprintf(qh, fp, 9260, "# %d f%d\n", vid++, facet->id); + qh_printcenter(qh, fp, format, NULL, facet); + } + } + FOREACHfacet_(facets) { + if (facet->visitid && facet->visitid < numfacets) { + if (format == qh_PRINTgeom) + qh_fprintf(qh, fp, 9261, "# %d f%d\n", vid++, facet->id); + qh_printcenter(qh, fp, format, NULL, facet); + } + } + FOREACHvertex_i_(qh, vertices) { + numneighbors= 0; + numinf=0; + if (vertex) { + qh_order_vertexneighbors(qh, vertex); + FOREACHneighbor_(vertex) { + if (neighbor->visitid == 0) + numinf= 1; + else if (neighbor->visitid < numfacets) + numneighbors++; + } + } + if (format == qh_PRINTgeom) { + if (vertex) { + qh_fprintf(qh, fp, 9262, "%d", numneighbors); + FOREACHneighbor_(vertex) { + if (neighbor->visitid && neighbor->visitid < numfacets) + qh_fprintf(qh, fp, 9263, " %d", neighbor->visitid); + } + qh_fprintf(qh, fp, 9264, " # p%d(v%d)\n", vertex_i, vertex->id); + }else + qh_fprintf(qh, fp, 9265, " # p%d is coplanar or isolated\n", vertex_i); + }else { + if (numinf) + numneighbors++; + qh_fprintf(qh, fp, 9266, "%d", numneighbors); + if (vertex) { + FOREACHneighbor_(vertex) { + if (neighbor->visitid == 0) { + if (numinf) { + numinf= 0; + qh_fprintf(qh, fp, 9267, " %d", neighbor->visitid); + } + }else if (neighbor->visitid < numfacets) + qh_fprintf(qh, fp, 9268, " %d", neighbor->visitid); + } + } + qh_fprintf(qh, fp, 9269, "\n"); + } + } + if (format == qh_PRINTgeom) + qh_fprintf(qh, fp, 9270, "}\n"); + qh_settempfree(qh, &vertices); +} /* printvoronoi */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printvnorm">-</a> + + qh_printvnorm(qh, 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(qhT *qh, FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded) { + pointT *normal; + realT offset; + int k; + QHULL_UNUSED(unbounded); + + normal= qh_detvnorm(qh, vertex, vertexA, centers, &offset); + qh_fprintf(qh, fp, 9271, "%d %d %d ", + 2+qh->hull_dim, qh_pointid(qh, vertex->point), qh_pointid(qh, vertexA->point)); + for (k=0; k< qh->hull_dim-1; k++) + qh_fprintf(qh, fp, 9272, qh_REAL_1, normal[k]); + qh_fprintf(qh, fp, 9273, qh_REAL_1, offset); + qh_fprintf(qh, fp, 9274, "\n"); +} /* printvnorm */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="printvridge">-</a> + + qh_printvridge(qh, 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(qhT *qh, FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded) { + facetT *facet, **facetp; + QHULL_UNUSED(unbounded); + + qh_fprintf(qh, fp, 9275, "%d %d %d", qh_setsize(qh, centers)+2, + qh_pointid(qh, vertex->point), qh_pointid(qh, vertexA->point)); + FOREACHfacet_(centers) + qh_fprintf(qh, fp, 9276, " %d", facet->visitid); + qh_fprintf(qh, fp, 9277, "\n"); +} /* printvridge */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="projectdim3">-</a> + + qh_projectdim3(qh, 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(qhT *qh, 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 */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="readfeasible">-</a> + + qh_readfeasible(qh, dim, curline ) + read feasible point from current line and qh.fin + + returns: + number of lines read from qh.fin + sets qh.feasible_point with malloc'd coordinates + + notes: + checks for qh.HALFspace + assumes dim > 1 + + see: + qh_setfeasible +*/ +int qh_readfeasible(qhT *qh, 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, qh->ferr, 6070, "qhull input error: feasible point(dim 1 coords) is only valid for halfspace intersection\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + if (qh->feasible_string) + qh_fprintf(qh, 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((size_t)dim * sizeof(coordT)))) { + qh_fprintf(qh, qh->ferr, 6071, "qhull error: insufficient memory for feasible point\n"); + qh_errexit(qh, 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, qh->ferr, 6072, "qhull input error: coordinates for feasible point do not finish out the line: %s\n", + s); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + return linecount; + } + } + } + qh_fprintf(qh, qh->ferr, 6073, "qhull input error: only %d coordinates. Could not read %d-d feasible point.\n", + tokcount, dim); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + return 0; +} /* readfeasible */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="readpoints">-</a> + + qh_readpoints(qh, 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 + QH11012 FIX: qh_readpoints needs rewriting, too long +*/ +coordT *qh_readpoints(qhT *qh, 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(qh, 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, qh->ferr, 6074, "qhull input error: missing \"begin\" for cdd-formated input\n"); + qh_errexit(qh, 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(qh, diminput, s); /* checks if ok */ + dimfeasible= diminput; + diminput= numinput= 0; + }else + break; + } + } + } + if (!s) { + qh_fprintf(qh, qh->ferr, 6075, "qhull input error: short input file. Did not find dimension and number of points\n"); + qh_errexit(qh, 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, qh->ferr, 6220, "qhull input error: dimension %d (first or smaller number) should be at least 2\n", + diminput); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + if (numinput < 1 || numinput > qh_POINTSmax) { + qh_fprintf(qh, qh->ferr, 6411, "qhull input error: expecting between 1 and %d points. Got %d %d-d points\n", + qh_POINTSmax, numinput, diminput); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + /* same error message in qh_initqhull_globals */ + } + + if (isdelaunay && qh->HALFspace) { + qh_fprintf(qh, qh->ferr, 6037, "qhull option error (qh_readpoints): can not use Delaunay('d') or Voronoi('v') with halfspace intersection('H')\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + /* otherwise corrupted memory allocations, same error message as in qh_initqhull_globals */ + }else 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, qh->ferr, 6221, "qhull input error: dimension %d (first number, includes offset) should be at least 3 for halfspaces\n", + diminput); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + if (dimfeasible) { + if (dimfeasible != *dimension) { + qh_fprintf(qh, 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, qh_ERRinput, NULL, NULL); + } + }else + qh_setfeasible(qh, *dimension); + }else { + if (qh->CDDinput) + *dimension= diminput-1; + else + *dimension= diminput; + *numpoints= numinput; + } + qh->normal_size= *dimension * (int)sizeof(coordT); /* for tracing with qh_printpoint */ + if (qh->HALFspace) { + qh->half_space= coordp= (coordT *)qh_malloc((size_t)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((size_t)(qh->maxline+1) * sizeof(char)); + *ismalloc= True; /* use malloc since memory not setup */ + coords= points= qh->temp_malloc= /* numinput and diminput >=2 by QH6220 */ + (coordT *)qh_malloc((size_t)((*numpoints)*(*dimension))*sizeof(coordT)); + if (!coords || !qh->line || (qh->HALFspace && !qh->half_space)) { + qh_fprintf(qh, qh->ferr, 6076, "qhull error: insufficient memory to read %d points\n", + numinput); + qh_errexit(qh, 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, 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, qh->ferr, 6077, "qhull input error: for cdd format, point at line %d does not start with '1'\n", + linecount); + qh_errexit(qh, 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(qh, *dimension, coords, &coords, normalp, offsetp, qh->feasible_point)) { + qh_fprintf(qh, qh->ferr, 8048, "The halfspace was on line %d\n", linecount); + if (wasbegin) + qh_fprintf(qh, qh->ferr, 8049, "The input appears to be in cdd format. If so, you should use option 'Fd'\n"); + qh_errexit(qh, 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, qh->ferr, 6078, "qhull input error: line %d contained more than %d characters\n", + linecount, (int) (s - qh->line)); /* WARN64 */ + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + isfirst= False; + } + if (qh->rbox_command[0]) + qh->rbox_command[strlen(qh->rbox_command)-1]= '\0'; /* remove \n, previous qh_errexit's display command as two lines */ + if (tokcount != maxcount) { + newnum= fmin_(numinput, tokcount/diminput); + if (qh->ALLOWshort) + qh_fprintf(qh, qh->ferr, 7073, "qhull warning: instead of %d points in %d-d, input contains %d points and %d extra coordinates.\n", + numinput, diminput, tokcount/diminput, tokcount % diminput); + else + qh_fprintf(qh, qh->ferr, 6410, "qhull error: instead of %d points in %d-d, input contains %d points and %d extra coordinates.\n", + numinput, diminput, tokcount/diminput, tokcount % diminput); + if (firsttext) + qh_fprintf(qh, qh->ferr, 8051, " Line %d is the first comment.\n", firsttext); + qh_fprintf(qh, qh->ferr, 8033, " Line %d is the first point.\n", firstpoint); + if (firstshort) + qh_fprintf(qh, qh->ferr, 8052, " Line %d is the first short line.\n", firstshort); + if (firstlong) + qh_fprintf(qh, qh->ferr, 8053, " Line %d is the first long line.\n", firstlong); + if (qh->ALLOWshort) + qh_fprintf(qh, qh->ferr, 8054, " Continuing with %d points.\n", newnum); + else { + qh_fprintf(qh, qh->ferr, 8077, " Override with option 'Qa' (allow-short)\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + 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 (!strcmp(qh->rbox_command, "./rbox D4")) + qh_fprintf(qh, 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_r.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, qh->ferr, 1008,"qh_readpoints: read in %d %d-dimensional points\n", + numinput, diminput)); + return(points); +} /* readpoints */ + + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="setfeasible">-</a> + + qh_setfeasible(qh, dim ) + set qh.feasible_point 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() + called only once from qh_new_qhull, otherwise leaks memory +*/ +void qh_setfeasible(qhT *qh, int dim) { + int tokcount= 0; + char *s; + coordT *coords, value; + + if (!(s= qh->feasible_string)) { + qh_fprintf(qh, qh->ferr, 6223, "qhull input error: halfspace intersection needs a feasible point. Either prepend the input with 1 point or use 'Hn,n,n'. See manual.\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + if (!(qh->feasible_point= (pointT *)qh_malloc((size_t)dim * sizeof(coordT)))) { + qh_fprintf(qh, qh->ferr, 6079, "qhull error: insufficient memory for 'Hn,n,n'\n"); + qh_errexit(qh, qh_ERRmem, NULL, NULL); + } + coords= qh->feasible_point; + while (*s) { + value= qh_strtod(s, &s); + if (++tokcount > dim) { + qh_fprintf(qh, 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 */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="skipfacet">-</a> + + qh_skipfacet(qh, 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(qhT *qh, 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(qh, facet->normal, NULL)); +} /* skipfacet */ + +/*-<a href="qh-io_r.htm#TOC" + >-------------------------------</a><a name="skipfilename">-</a> + + qh_skipfilename(qh, 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(qhT *qh, 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, qh->ferr, 6204, "qhull input error: filename expected, none found.\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + if (c == '\'' || c == '"') { + while (*s !=c || s[-1] == '\\') { + if (!*s) { + qh_fprintf(qh, qh->ferr, 6203, "qhull input error: missing quote after filename -- %s\n", filename); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + s++; + } + s++; + } + else while (*s && !isspace(*s)) + s++; + return s; +} /* skipfilename */ + diff --git a/contrib/libs/qhull/libqhull_r/io_r.h b/contrib/libs/qhull/libqhull_r/io_r.h new file mode 100644 index 0000000000..eb3c751492 --- /dev/null +++ b/contrib/libs/qhull/libqhull_r/io_r.h @@ -0,0 +1,166 @@ +/*<html><pre> -<a href="qh-io_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + io_r.h + declarations of Input/Output functions + + see README, libqhull_r.h and io_r.c + + Copyright (c) 1993-2020 The Geometry Center. + $Id: //main/2019/qhull/src/libqhull_r/io_r.h#3 $$Change: 2953 $ + $DateTime: 2020/05/21 22:05:32 $$Author: bbarber $ +*/ + +#ifndef qhDEFio +#define qhDEFio 1 + +#include "libqhull_r.h" + +/*============ constants and flags ==================*/ + +/*-<a href="qh-io_r.htm#TOC" + >--------------------------------</a><a name="qh_MAXfirst">-</a> + + qh_MAXfirst + maximum length of first two lines of stdin +*/ +#define qh_MAXfirst 200 + +/*-<a href="qh-io_r.htm#TOC" + >--------------------------------</a><a name="qh_MINradius">-</a> + + qh_MINradius + min radius for Gp and Gv, fraction of maxcoord +*/ +#define qh_MINradius 0.02 + +/*-<a href="qh-io_r.htm#TOC" + >--------------------------------</a><a name="qh_GEOMepsilon">-</a> + + qh_GEOMepsilon + adjust outer planes for 'lines closer' and geomview roundoff. + This prevents bleed through. +*/ +#define qh_GEOMepsilon 2e-3 + +/*-<a href="qh-io_r.htm#TOC" + >--------------------------------</a><a name="qh_WHITESPACE">-</a> + + qh_WHITESPACE + possible values of white space +*/ +#define qh_WHITESPACE " \n\t\v\r\f" + + +/*-<a href="qh-io_r.htm#TOC" + >--------------------------------</a><a name="RIDGE">-</a> + + qh_RIDGE + to select which ridges to print in qh_eachvoronoi +*/ +typedef enum +{ + qh_RIDGEall= 0, qh_RIDGEinner, qh_RIDGEouter +} +qh_RIDGE; + +/*-<a href="qh-io_r.htm#TOC" + >--------------------------------</a><a name="printvridgeT">-</a> + + printvridgeT + prints results of qh_printvdiagram + + see: + <a href="io_r.c#printvridge">qh_printvridge</a> for an example +*/ +typedef void (*printvridgeT)(qhT *qh, FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded); + +/*============== -prototypes in alphabetical order =========*/ + +#ifdef __cplusplus +extern "C" { +#endif + +void qh_dfacet(qhT *qh, unsigned int id); +void qh_dvertex(qhT *qh, unsigned int id); +int qh_compare_facetarea(const void *p1, const void *p2); +int qh_compare_facetvisit(const void *p1, const void *p2); +int qh_compare_nummerge(const void *p1, const void *p2); +void qh_copyfilename(qhT *qh, char *filename, int size, const char* source, int length); +void qh_countfacets(qhT *qh, facetT *facetlist, setT *facets, boolT printall, + int *numfacetsp, int *numsimplicialp, int *totneighborsp, + int *numridgesp, int *numcoplanarsp, int *numnumtricoplanarsp); +pointT *qh_detvnorm(qhT *qh, vertexT *vertex, vertexT *vertexA, setT *centers, realT *offsetp); +setT *qh_detvridge(qhT *qh, vertexT *vertex); +setT *qh_detvridge3(qhT *qh, vertexT *atvertex, vertexT *vertex); +int qh_eachvoronoi(qhT *qh, FILE *fp, printvridgeT printvridge, vertexT *atvertex, boolT visitall, qh_RIDGE innerouter, boolT inorder); +int qh_eachvoronoi_all(qhT *qh, FILE *fp, printvridgeT printvridge, boolT isUpper, qh_RIDGE innerouter, boolT inorder); +void qh_facet2point(qhT *qh, facetT *facet, pointT **point0, pointT **point1, realT *mindist); +setT *qh_facetvertices(qhT *qh, facetT *facetlist, setT *facets, boolT allfacets); +void qh_geomplanes(qhT *qh, facetT *facet, realT *outerplane, realT *innerplane); +void qh_markkeep(qhT *qh, facetT *facetlist); +setT *qh_markvoronoi(qhT *qh, facetT *facetlist, setT *facets, boolT printall, boolT *isLowerp, int *numcentersp); +void qh_order_vertexneighbors(qhT *qh, vertexT *vertex); +void qh_prepare_output(qhT *qh); +void qh_printafacet(qhT *qh, FILE *fp, qh_PRINT format, facetT *facet, boolT printall); +void qh_printbegin(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall); +void qh_printcenter(qhT *qh, FILE *fp, qh_PRINT format, const char *string, facetT *facet); +void qh_printcentrum(qhT *qh, FILE *fp, facetT *facet, realT radius); +void qh_printend(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall); +void qh_printend4geom(qhT *qh, FILE *fp, facetT *facet, int *num, boolT printall); +void qh_printextremes(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall); +void qh_printextremes_2d(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall); +void qh_printextremes_d(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall); +void qh_printfacet(qhT *qh, FILE *fp, facetT *facet); +void qh_printfacet2math(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format, int notfirst); +void qh_printfacet2geom(qhT *qh, FILE *fp, facetT *facet, realT color[3]); +void qh_printfacet2geom_points(qhT *qh, FILE *fp, pointT *point1, pointT *point2, + facetT *facet, realT offset, realT color[3]); +void qh_printfacet3math(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format, int notfirst); +void qh_printfacet3geom_nonsimplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]); +void qh_printfacet3geom_points(qhT *qh, FILE *fp, setT *points, facetT *facet, realT offset, realT color[3]); +void qh_printfacet3geom_simplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]); +void qh_printfacet3vertex(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format); +void qh_printfacet4geom_nonsimplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]); +void qh_printfacet4geom_simplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]); +void qh_printfacetNvertex_nonsimplicial(qhT *qh, FILE *fp, facetT *facet, int id, qh_PRINT format); +void qh_printfacetNvertex_simplicial(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format); +void qh_printfacetheader(qhT *qh, FILE *fp, facetT *facet); +void qh_printfacetridges(qhT *qh, FILE *fp, facetT *facet); +void qh_printfacets(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall); +void qh_printhyperplaneintersection(qhT *qh, FILE *fp, facetT *facet1, facetT *facet2, + setT *vertices, realT color[3]); +void qh_printline3geom(qhT *qh, FILE *fp, pointT *pointA, pointT *pointB, realT color[3]); +void qh_printneighborhood(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetA, facetT *facetB, boolT printall); +void qh_printpoint(qhT *qh, FILE *fp, const char *string, pointT *point); +void qh_printpointid(qhT *qh, FILE *fp, const char *string, int dim, pointT *point, int id); +void qh_printpoint3(qhT *qh, FILE *fp, pointT *point); +void qh_printpoints_out(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall); +void qh_printpointvect(qhT *qh, FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius, realT color[3]); +void qh_printpointvect2(qhT *qh, FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius); +void qh_printridge(qhT *qh, FILE *fp, ridgeT *ridge); +void qh_printspheres(qhT *qh, FILE *fp, setT *vertices, realT radius); +void qh_printvdiagram(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall); +int qh_printvdiagram2(qhT *qh, FILE *fp, printvridgeT printvridge, setT *vertices, qh_RIDGE innerouter, boolT inorder); +void qh_printvertex(qhT *qh, FILE *fp, vertexT *vertex); +void qh_printvertexlist(qhT *qh, FILE *fp, const char* string, facetT *facetlist, + setT *facets, boolT printall); +void qh_printvertices(qhT *qh, FILE *fp, const char* string, setT *vertices); +void qh_printvneighbors(qhT *qh, FILE *fp, facetT* facetlist, setT *facets, boolT printall); +void qh_printvoronoi(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall); +void qh_printvnorm(qhT *qh, FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded); +void qh_printvridge(qhT *qh, FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded); +void qh_produce_output(qhT *qh); +void qh_produce_output2(qhT *qh); +void qh_projectdim3(qhT *qh, pointT *source, pointT *destination); +int qh_readfeasible(qhT *qh, int dim, const char *curline); +coordT *qh_readpoints(qhT *qh, int *numpoints, int *dimension, boolT *ismalloc); +void qh_setfeasible(qhT *qh, int dim); +boolT qh_skipfacet(qhT *qh, facetT *facet); +char *qh_skipfilename(qhT *qh, char *filename); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* qhDEFio */ diff --git a/contrib/libs/qhull/libqhull_r/libqhull_r.c b/contrib/libs/qhull/libqhull_r/libqhull_r.c new file mode 100644 index 0000000000..0d41d7be05 --- /dev/null +++ b/contrib/libs/qhull/libqhull_r/libqhull_r.c @@ -0,0 +1,1754 @@ +/*<html><pre> -<a href="qh-qhull_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + libqhull_r.c + Quickhull algorithm for convex hulls + + qhull() and top-level routines + + see qh-qhull_r.htm, libqhull_r.h, unix_r.c + + see qhull_ra.h for internal functions + + Copyright (c) 1993-2020 The Geometry Center. + $Id: //main/2019/qhull/src/libqhull_r/libqhull_r.c#17 $$Change: 2953 $ + $DateTime: 2020/05/21 22:05:32 $$Author: bbarber $ +*/ + +#include "qhull_ra.h" + +/*============= functions in alphabetic order after qhull() =======*/ + +/*-<a href="qh-qhull_r.htm#TOC" + >-------------------------------</a><a name="qhull">-</a> + + qh_qhull(qh) + 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(qhT *qh) { + int numoutside; + + qh->hulltime= qh_CPUclock; + if (qh->RERUN || qh->JOGGLEmax < REALmax/2) + qh_build_withrestart(qh); + else { + qh_initbuild(qh); + qh_buildhull(qh); + } + if (!qh->STOPadd && !qh->STOPcone && !qh->STOPpoint) { + if (qh->ZEROall_ok && !qh->TESTvneighbors && qh->MERGEexact) + qh_checkzero(qh, qh_ALL); + if (qh->ZEROall_ok && !qh->TESTvneighbors && !qh->WAScoplanar) { + trace2((qh, 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 { + qh_initmergesets(qh /* qh.facet_mergeset,degen_mergeset,vertex_mergeset */); + if (qh->MERGEexact || (qh->hull_dim > qh_DIMreduceBuild && qh->PREmerge)) + qh_postmerge(qh, "First post-merge", qh->premerge_centrum, qh->premerge_cos, + (qh->POSTmerge ? False : qh->TESTvneighbors)); /* calls qh_reducevertices */ + else if (!qh->POSTmerge && qh->TESTvneighbors) + qh_postmerge(qh, "For testing vertex neighbors", qh->premerge_centrum, + qh->premerge_cos, True); /* calls qh_test_vneighbors */ + if (qh->POSTmerge) + qh_postmerge(qh, "For post-merging", qh->postmerge_centrum, + qh->postmerge_cos, qh->TESTvneighbors); + if (qh->visible_list == qh->facet_list) { /* qh_postmerge was called */ + qh->findbestnew= True; + qh_partitionvisible(qh, !qh_ALL, &numoutside /* qh.visible_list */); + qh->findbestnew= False; + qh_deletevisible(qh /* qh.visible_list */); /* stops at first !f.visible */ + qh_resetlists(qh, False, qh_RESETvisible /* qh.visible_list newvertex_list qh.newfacet_list */); + } + qh_all_vertexmerges(qh, -1, NULL, NULL); + qh_freemergesets(qh); + } + if (qh->TRACEpoint == qh_IDunknown && qh->TRACElevel > qh->IStracing) { + qh->IStracing= qh->TRACElevel; + qh_fprintf(qh, qh->ferr, 2112, "qh_qhull: finished qh_buildhull and qh_postmerge, start tracing (TP-1)\n"); + } + if (qh->DOcheckmax){ + if (qh->REPORTfreq) { + qh_buildtracing(qh, NULL, NULL); + qh_fprintf(qh, qh->ferr, 8115, "\nTesting all coplanar points.\n"); + } + qh_check_maxout(qh); + } + if (qh->KEEPnearinside && !qh->maxoutdone) + qh_nearcoplanar(qh); + } + if (qh_setsize(qh, qh->qhmem.tempstack) != 0) { + qh_fprintf(qh, qh->ferr, 6164, "qhull internal error (qh_qhull): temporary sets not empty(%d) at end of Qhull\n", + qh_setsize(qh, qh->qhmem.tempstack)); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + qh->hulltime= qh_CPUclock - qh->hulltime; + qh->QHULLfinished= True; + trace1((qh, qh->ferr, 1036, "Qhull: algorithm completed\n")); +} /* qhull */ + +/*-<a href="qh-qhull_r.htm#TOC" + >-------------------------------</a><a name="addpoint">-</a> + + qh_addpoint(qh, 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: + called from qh_initbuild, qh_buildhull, and qh_addpoint + tail recursive call if merged a pinchedvertex due to a duplicated ridge + no more than qh.num_vertices calls (QH6296) + 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 + if called from a user application after qh_qhull and 'QJ' (joggle), + facet merging for precision problems is disabled by default + + design: + exit if qh.STOPadd vertices 'TAn' + 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 + build cone of new facets to the horizon + exit if build cone fails due to qh.ONLYgood + tail recursive call if build cone fails due to pinched 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(qhT *qh, pointT *furthest, facetT *facet, boolT checkdist) { + realT dist, pbalance; + facetT *replacefacet, *newfacet; + vertexT *apex; + boolT isoutside= False; + int numpart, numpoints, goodvisible, goodhorizon, apexpointid; + + qh->maxoutdone= False; + if (qh_pointid(qh, furthest) == qh_IDunknown) + qh_setappend(qh, &qh->other_points, furthest); + if (!facet) { + qh_fprintf(qh, qh->ferr, 6213, "qhull internal error (qh_addpoint): NULL facet. Need to call qh_findbestfacet first\n"); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + qh_detmaxoutside(qh); + if (checkdist) { + facet= qh_findbest(qh, 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(qh, furthest, facet, &dist, qh->findbestnew); + return True; + } + } + qh_buildtracing(qh, furthest, facet); + if (qh->STOPpoint < 0 && qh->furthest_id == -qh->STOPpoint-1) { + facet->notfurthest= True; + return False; + } + qh_findhorizon(qh, furthest, facet, &goodvisible, &goodhorizon); + if (qh->ONLYgood && !qh->GOODclosest && !(goodvisible+goodhorizon)) { + 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(qh, False, qh_RESETvisible /* qh.visible_list newvertex_list qh.newfacet_list */); + return True; + } + apex= qh_buildcone(qh, furthest, facet, goodhorizon, &replacefacet); + /* qh.newfacet_list, visible_list, newvertex_list */ + if (!apex) { + if (qh->ONLYgood) + return True; /* ignore this furthest point, a good new facet was not found */ + if (replacefacet) { + if (qh->retry_addpoint++ >= qh->num_vertices) { + qh_fprintf(qh, qh->ferr, 6296, "qhull internal error (qh_addpoint): infinite loop (%d retries) of merging pinched vertices due to dupridge for point p%d, facet f%d, and %d vertices\n", + qh->retry_addpoint, qh_pointid(qh, furthest), facet->id, qh->num_vertices); + qh_errexit(qh, qh_ERRqhull, facet, NULL); + } + /* retry qh_addpoint after resolving a dupridge via qh_merge_pinchedvertices */ + return qh_addpoint(qh, furthest, replacefacet, True /* checkdisk */); + } + qh->retry_addpoint= 0; + return True; /* ignore this furthest point, resolved a dupridge by making furthest a coplanar point */ + } + if (qh->retry_addpoint) { + zinc_(Zretryadd); + zadd_(Zretryaddtot, qh->retry_addpoint); + zmax_(Zretryaddmax, qh->retry_addpoint); + qh->retry_addpoint= 0; + } + apexpointid= qh_pointid(qh, apex->point); + zzinc_(Zprocessed); + 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_initmergesets(qh /* qh.facet_mergeset,degen_mergeset,vertex_mergeset */); + qh_premerge(qh, apexpointid, qh->premerge_centrum, qh->premerge_cos /* qh.newfacet_list */); + 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; + if (qh->IStracing >= 4) + qh_checkpolygon(qh, qh->visible_list); + qh_partitionvisible(qh, !qh_ALL, &numpoints /* qh.visible_list */); + 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 /* qh.visible_list */); + zmax_(Zmaxvertex, qh->num_vertices); + qh->NEWfacets= False; + if (qh->IStracing >= 4) { + if (qh->num_facets < 200) + qh_printlists(qh); + qh_printfacetlist(qh, qh->newfacet_list, NULL, True); + qh_checkpolygon(qh, qh->facet_list); + }else if (qh->CHECKfrequently) { + if (qh->num_facets < 1000) + qh_checkpolygon(qh, qh->facet_list); + else + qh_checkpolygon(qh, qh->newfacet_list); + } + if (qh->STOPpoint > 0 && qh->furthest_id == qh->STOPpoint-1 && qh_setsize(qh, qh->vertex_mergeset) > 0) + return False; + qh_resetlists(qh, True, qh_RESETvisible /* qh.visible_list newvertex_list qh.newfacet_list */); + if (qh->facet_mergeset) { + /* vertex merges occur after facet merges (qh_premerge) and qh_resetlists */ + qh_all_vertexmerges(qh, apexpointid, NULL, NULL); + qh_freemergesets(qh); + } + /* qh_triangulate(qh); to test qh.TRInormals */ + if (qh->STOPpoint > 0 && qh->furthest_id == qh->STOPpoint-1) + return False; + trace2((qh, qh->ferr, 2056, "qh_addpoint: added p%d to convex hull with point balance %2.2g\n", + qh_pointid(qh, furthest), pbalance)); + return True; +} /* addpoint */ + +/*-<a href="qh-qhull_r.htm#TOC" + >-------------------------------</a><a name="build_withrestart">-</a> + + qh_build_withrestart(qh) + allow restarts due to qh.JOGGLEmax while calling qh_buildhull() + qh_errexit always undoes qh_build_withrestart() + qh.FIRSTpoint/qh.NUMpoints is point array + it may be moved by qh_joggleinput +*/ +void qh_build_withrestart(qhT *qh) { + int restart; + vertexT *vertex, **vertexp; + + qh->ALLOWrestart= True; + while (True) { + restart= setjmp(qh->restartexit); /* simple statement for CRAY J916 */ + if (restart) { /* only from qh_joggle_restart() */ + qh->last_errcode= qh_ERRnone; + zzinc_(Zretry); + wmax_(Wretrymax, qh->JOGGLEmax); + /* QH7078 warns about using 'TCn' with 'QJn' */ + qh->STOPcone= qh_IDunknown; /* if break from joggle, prevents normal output */ + FOREACHvertex_(qh->del_vertices) { + if (vertex->point && !vertex->partitioned) + vertex->partitioned= True; /* avoid error in qh_freebuild -> qh_delvertex */ + } + } + if (!qh->RERUN && qh->JOGGLEmax < REALmax/2) { + if (qh->build_cnt > qh_JOGGLEmaxretry) { + qh_fprintf(qh, qh->ferr, 6229, "qhull input error: %d attempts to construct a convex hull with joggled input. Increase joggle above 'QJ%2.2g' or modify qh_JOGGLE... parameters in user_r.h\n", + qh->build_cnt, qh->JOGGLEmax); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + if (qh->build_cnt && !restart) + break; + }else if (qh->build_cnt && qh->build_cnt >= qh->RERUN) + break; + qh->STOPcone= 0; + qh_freebuild(qh, 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(qh, "_run", &qh->build_cnt, NULL); + if (qh->build_cnt == qh->RERUN) { + qh->IStracing= qh->TRACElastrun; /* duplicated from qh_initqhull_globals */ + if (qh->TRACEpoint != qh_IDnone || qh->TRACEdist < REALmax/2 || qh->TRACEmerge) { + qh->TRACElevel= (qh->IStracing? qh->IStracing : 3); + qh->IStracing= 0; + } + qh->qhmem.IStracing= qh->IStracing; + } + if (qh->JOGGLEmax < REALmax/2) + qh_joggleinput(qh); + qh_initbuild(qh); + qh_buildhull(qh); + if (qh->JOGGLEmax < REALmax/2 && !qh->MERGING) + qh_checkconvex(qh, qh->facet_list, qh_ALGORITHMfault); + } + qh->ALLOWrestart= False; +} /* qh_build_withrestart */ + +/*-<a href="qh-qhull_r.htm#TOC" + >-------------------------------</a><a name="buildcone">-</a> + + qh_buildcone(qh, furthest, facet, goodhorizon, &replacefacet ) + build cone of new facets from furthest to the horizon + goodhorizon is count of good, horizon facets from qh_find_horizon + + returns: + returns apex of cone with qh.newfacet_list and qh.first_newfacet (f.id) + returns NULL if qh.ONLYgood and no good facets + returns NULL and retryfacet if merging pinched vertices will resolve a dupridge + a horizon vertex was nearly adjacent to another vertex + will retry qh_addpoint + returns NULL if resolve a dupridge by making furthest a coplanar point + furthest was nearly adjacent to an existing vertex + updates qh.degen_mergeset (MRGridge) if resolve a dupridge by merging facets + updates qh.newfacet_list, visible_list, newvertex_list + updates qh.facet_list, vertex_list, num_facets, num_vertices + + notes: + called by qh_addpoint + see qh_triangulate, it triangulates non-simplicial facets in post-processing + + design: + make new facets for point to horizon + compute balance statistics + make hyperplanes for point + exit if qh.ONLYgood and not good (qh_buildcone_onlygood) + match neighboring new facets + if dupridges + exit if !qh.IGNOREpinched and dupridge resolved by coplanar furthest + retry qh_buildcone if !qh.IGNOREpinched and dupridge resolved by qh_buildcone_mergepinched + otherwise dupridges resolved by merging facets + update vertex neighbors and delete interior vertices +*/ +vertexT *qh_buildcone(qhT *qh, pointT *furthest, facetT *facet, int goodhorizon, facetT **retryfacet) { + vertexT *apex; + realT newbalance; + int numnew; + + *retryfacet= NULL; + qh->first_newfacet= qh->facet_id; + qh->NEWtentative= (qh->MERGEpinched || qh->ONLYgood); /* cleared by qh_attachnewfacets or qh_resetlists */ + apex= qh_makenewfacets(qh, furthest /* qh.newfacet_list visible_list, attaches new facets if !qh.NEWtentative */); + numnew= (int)(qh->facet_id - qh->first_newfacet); + newbalance= numnew - (realT)(qh->num_facets - qh->num_visible) * qh->hull_dim / qh->num_vertices; + /* newbalance statistics updated below if the new facets are accepted */ + if (qh->ONLYgood) { /* qh.MERGEpinched is false by QH6362 */ + if (!qh_buildcone_onlygood(qh, apex, goodhorizon /* qh.newfacet_list */)) { + facet->notfurthest= True; + return NULL; + } + }else if(qh->MERGEpinched) { +#ifndef qh_NOmerge + if (qh_buildcone_mergepinched(qh, apex, facet, retryfacet /* qh.newfacet_list */)) + return NULL; +#else + qh_fprintf(qh, qh->ferr, 6375, "qhull option error (qh_buildcone): option 'Q14' (qh.MERGEpinched) is not available due to qh_NOmerge\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); +#endif + }else { + /* qh_makenewfacets attached new facets to the horizon */ + qh_matchnewfacets(qh); /* ignore returned value. qh_forcedmerges will merge dupridges if any */ + qh_makenewplanes(qh /* qh.newfacet_list */); + qh_update_vertexneighbors_cone(qh); + } + wadd_(Wnewbalance, newbalance); + wadd_(Wnewbalance2, newbalance * newbalance); + trace2((qh, qh->ferr, 2067, "qh_buildcone: created %d newfacets for p%d(v%d) new facet balance %2.2g\n", + numnew, qh_pointid(qh, furthest), apex->id, newbalance)); + return apex; +} /* buildcone */ + +#ifndef qh_NOmerge +/*-<a href="qh-qhull_r.htm#TOC" + >-------------------------------</a><a name="buildcone_mergepinched">-</a> + + qh_buildcone_mergepinched(qh, apex, facet, maxdupdist, &retryfacet ) + build cone of new facets from furthest to the horizon + maxdupdist>0.0 for merging dupridges (qh_matchdupridge) + + returns: + returns True if merged a pinched vertex and deleted the cone of new facets + if retryfacet is set + a dupridge was resolved by qh_merge_pinchedvertices + retry qh_addpoint + otherwise + apex/furthest was partitioned as a coplanar point + ignore this furthest point + returns False if no dupridges or if dupridges will be resolved by MRGridge + updates qh.facet_list, qh.num_facets, qh.vertex_list, qh.num_vertices + + notes: + only called from qh_buildcone with qh.MERGEpinched + + design: + match neighboring new facets + if matching detected dupridges with a wide merge (qh_RATIOtrypinched) + if pinched vertices (i.e., nearly adjacent) + delete the cone of new facets + delete the apex and reset the facet lists + if coplanar, pinched apex + partition the apex as a coplanar point + else + repeatedly merge the nearest pair of pinched vertices and subsequent facet merges + return True + otherwise + MRGridge are better than vertex merge, but may report an error + attach new facets + make hyperplanes for point + update vertex neighbors and delete interior vertices +*/ +boolT qh_buildcone_mergepinched(qhT *qh, vertexT *apex, facetT *facet, facetT **retryfacet) { + facetT *newfacet, *nextfacet; + pointT *apexpoint; + coordT maxdupdist; + int apexpointid; + boolT iscoplanar; + + *retryfacet= NULL; + maxdupdist= qh_matchnewfacets(qh); + if (maxdupdist > qh_RATIOtrypinched * qh->ONEmerge) { /* one or more dupridges with a wide merge */ + if (qh->IStracing >= 4 && qh->num_facets < 1000) + qh_printlists(qh); + qh_initmergesets(qh /* qh.facet_mergeset,degen_mergeset,vertex_mergeset */); + if (qh_getpinchedmerges(qh, apex, maxdupdist, &iscoplanar /* qh.newfacet_list, qh.vertex_mergeset */)) { + for (newfacet=qh->newfacet_list; newfacet && newfacet->next; newfacet= nextfacet) { + nextfacet= newfacet->next; + qh_delfacet(qh, newfacet); + } + apexpoint= apex->point; + apexpointid= qh_pointid(qh, apexpoint); + qh_delvertex(qh, apex); + qh_resetlists(qh, False, qh_RESETvisible /* qh.visible_list newvertex_list qh.newfacet_list */); + if (iscoplanar) { + zinc_(Zpinchedapex); + facet->notfurthest= True; + qh_partitioncoplanar(qh, apexpoint, facet, NULL, qh->findbestnew); + }else { + qh_all_vertexmerges(qh, apexpointid, facet, retryfacet); + } + qh_freemergesets(qh); /* errors if not empty */ + return True; + } + /* MRGridge are better than vertex merge, but may report an error */ + qh_freemergesets(qh); + } + qh_attachnewfacets(qh /* qh.visible_list */); + qh_makenewplanes(qh /* qh.newfacet_list */); + qh_update_vertexneighbors_cone(qh); + return False; +} /* buildcone_mergepinched */ +#endif /* !qh_NOmerge */ + +/*-<a href="qh-qhull_r.htm#TOC" + >-------------------------------</a><a name="buildcone_onlygood">-</a> + + qh_buildcone_onlygood(qh, apex, goodhorizon ) + build cone of good, new facets from apex and its qh.newfacet_list to the horizon + goodhorizon is count of good, horizon facets from qh_find_horizon + + returns: + False if a f.good facet or a qh.GOODclosest facet is not found + updates qh.facet_list, qh.num_facets, qh.vertex_list, qh.num_vertices + + notes: + called from qh_buildcone + QH11030 FIX: Review effect of qh.GOODclosest on qh_buildcone_onlygood ('Qg'). qh_findgood preserves old value if didn't find a good facet. See qh_findgood_all for disabling + + design: + make hyperplanes for point + if qh_findgood fails to find a f.good facet or a qh.GOODclosest facet + delete cone of new facets + return NULL (ignores apex) + else + attach cone to horizon + match neighboring new facets +*/ +boolT qh_buildcone_onlygood(qhT *qh, vertexT *apex, int goodhorizon) { + facetT *newfacet, *nextfacet; + + qh_makenewplanes(qh /* qh.newfacet_list */); + if(qh_findgood(qh, qh->newfacet_list, goodhorizon) == 0) { + if (!qh->GOODclosest) { + for (newfacet=qh->newfacet_list; newfacet && newfacet->next; newfacet= nextfacet) { + nextfacet= newfacet->next; + qh_delfacet(qh, newfacet); + } + qh_delvertex(qh, apex); + qh_resetlists(qh, False /*no stats*/, qh_RESETvisible /* qh.visible_list newvertex_list qh.newfacet_list */); + zinc_(Znotgoodnew); + /* !good outside points dropped from hull */ + return False; + } + } + qh_attachnewfacets(qh /* qh.visible_list */); + qh_matchnewfacets(qh); /* ignore returned value. qh_forcedmerges will merge dupridges if any */ + qh_update_vertexneighbors_cone(qh); + return True; +} /* buildcone_onlygood */ + +/*-<a href="qh-qhull_r.htm#TOC" + >-------------------------------</a><a name="buildhull">-</a> + + qh_buildhull(qh) + 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 newfacet 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(qhT *qh) { + facetT *facet; + pointT *furthest; + vertexT *vertex; + int id; + + trace1((qh, qh->ferr, 1037, "qh_buildhull: start build hull\n")); + FORALLfacets { + if (facet->visible || facet->newfacet) { + qh_fprintf(qh, qh->ferr, 6165, "qhull internal error (qh_buildhull): visible or new facet f%d in facet list\n", + facet->id); + qh_errexit(qh, qh_ERRqhull, facet, NULL); + } + } + FORALLvertices { + if (vertex->newfacet) { + qh_fprintf(qh, qh->ferr, 6166, "qhull internal error (qh_buildhull): new vertex f%d in vertex list\n", + vertex->id); + qh_errprint(qh, "ERRONEOUS", NULL, NULL, NULL, vertex); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + id= qh_pointid(qh, 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, 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(qh, &facet))) { + qh->num_outside--; /* if ONLYmax, furthest may not be outside */ + if (qh->STOPadd>0 && (qh->num_vertices - qh->hull_dim - 1 >= qh->STOPadd - 1)) { + trace1((qh, qh->ferr, 1059, "qh_buildhull: stop after adding %d vertices\n", qh->STOPadd-1)); + return; + } + if (!qh_addpoint(qh, furthest, facet, qh->ONLYmax)) + break; + } + if (qh->NARROWhull) /* move points from outsideset to coplanarset */ + qh_outcoplanar(qh /* facet_list */ ); + if (qh->num_outside && !furthest) { + qh_fprintf(qh, qh->ferr, 6167, "qhull internal error (qh_buildhull): %d outside points were never processed.\n", qh->num_outside); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + trace1((qh, qh->ferr, 1039, "qh_buildhull: completed the hull construction\n")); +} /* buildhull */ + + +/*-<a href="qh-qhull_r.htm#TOC" + >-------------------------------</a><a name="buildtracing">-</a> + + qh_buildtracing(qh, furthest, facet ) + trace an iteration of qh_buildhull() for furthest point and facet + if !furthest, prints progress message + + returns: + tracks progress with qh.lastreport, lastcpu, lastfacets, lastmerges, lastplanes, lastdist + 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(qhT *qh, pointT *furthest, facetT *facet) { + realT dist= 0; + double 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= (double)qh_CPUclock - (double)qh->hulltime; + cpu /= (double)qh_SECticks; + total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot); + qh_fprintf(qh, 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(qh, furthest); +#ifndef qh_NOtrace + if (qh->TRACEpoint == furthestid) { + trace1((qh, qh->ferr, 1053, "qh_buildtracing: start trace T%d for point TP%d above facet f%d\n", qh->TRACElevel, furthestid, facet->id)); + qh->IStracing= qh->TRACElevel; + qh->qhmem.IStracing= qh->TRACElevel; + }else if (qh->TRACEpoint != qh_IDnone && qh->TRACEdist < REALmax/2) { + qh->IStracing= 0; + qh->qhmem.IStracing= 0; + } +#endif + if (qh->REPORTfreq && (qh->facet_id-1 > qh->lastreport + (unsigned int)qh->REPORTfreq)) { + qh->lastreport= qh->facet_id-1; + time(&timedata); + tp= localtime(&timedata); + cpu= (double)qh_CPUclock - (double)qh->hulltime; + cpu /= (double)qh_SECticks; + total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot); + zinc_(Zdistio); + qh_distplane(qh, furthest, facet, &dist); + qh_fprintf(qh, 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= (double)qh_CPUclock - (double)qh->hulltime; + cpu /= (double)qh_SECticks; + qh_distplane(qh, furthest, facet, &dist); + qh_fprintf(qh, qh->ferr, 1049, "qh_addpoint: add p%d(v%d) %2.2g above f%d to hull of %d facets, %d merges, %d outside at %4.4g CPU secs. Previous p%d(v%d) delta %4.4g CPU, %d facets, %d merges, %d hyperplanes, %d distplanes, %d retries\n", + furthestid, qh->vertex_id, dist, getid_(facet), qh->num_facets, zzval_(Ztotmerge), qh->num_outside+1, cpu, qh->furthest_id, qh->vertex_id - 1, + cpu - qh->lastcpu, qh->num_facets - qh->lastfacets, zzval_(Ztotmerge) - qh->lastmerges, zzval_(Zsetplane) - qh->lastplanes, zzval_(Zdistplane) - qh->lastdist, qh->retry_addpoint); + qh->lastcpu= cpu; + qh->lastfacets= qh->num_facets; + qh->lastmerges= zzval_(Ztotmerge); + qh->lastplanes= zzval_(Zsetplane); + qh->lastdist= zzval_(Zdistplane); + } + zmax_(Zvisit2max, (int)qh->visit_id/2); + if (qh->visit_id > (unsigned int) INT_MAX) { /* 31 bits */ + zinc_(Zvisit); + if (!qh_checklists(qh, qh->facet_list)) { + qh_fprintf(qh, qh->ferr, 6370, "qhull internal error: qh_checklists failed on reset of qh.visit_id %u\n", qh->visit_id); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + qh->visit_id= 0; + FORALLfacets + facet->visitid= 0; + } + zmax_(Zvvisit2max, (int)qh->vertex_visit/2); + if (qh->vertex_visit > (unsigned int) INT_MAX) { /* 31 bits */ + zinc_(Zvvisit); + if (qh->visit_id && !qh_checklists(qh, qh->facet_list)) { + qh_fprintf(qh, qh->ferr, 6371, "qhull internal error: qh_checklists failed on reset of qh.vertex_visit %u\n", qh->vertex_visit); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + qh->vertex_visit= 0; + FORALLvertices + vertex->visitid= 0; + } + qh->furthest_id= furthestid; + qh->RANDOMdist= qh->old_randomdist; +} /* buildtracing */ + +/*-<a href="qh-qhull_r.htm#TOC" + >-------------------------------</a><a name="errexit2">-</a> + + qh_errexit2(qh, 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_r.c(reports a facet and a ridge) +*/ +void qh_errexit2(qhT *qh, int exitcode, facetT *facet, facetT *otherfacet) { + qh->tracefacet= NULL; /* avoid infinite recursion through qh_fprintf */ + qh->traceridge= NULL; + qh->tracevertex= NULL; + qh_errprint(qh, "ERRONEOUS", facet, otherfacet, NULL, NULL); + qh_errexit(qh, exitcode, NULL, NULL); +} /* errexit2 */ + + +/*-<a href="qh-qhull_r.htm#TOC" + >-------------------------------</a><a name="findhorizon">-</a> + + qh_findhorizon(qh, 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(qhT *qh, pointT *point, facetT *facet, int *goodvisible, int *goodhorizon) { + facetT *neighbor, **neighborp, *visible; + int numhorizon= 0, coplanar= 0; + realT dist; + + trace1((qh, qh->ferr, 1040, "qh_findhorizon: find horizon for point p%d facet f%d\n",qh_pointid(qh, point),facet->id)); + *goodvisible= *goodhorizon= 0; + zinc_(Ztotvisible); + qh_removefacet(qh, facet); /* visible_list at end of qh->facet_list */ + qh_appendfacet(qh, 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(qh, "visible", facet, NULL, NULL, NULL); + qh->visit_id++; + FORALLvisible_facets { + if (visible->tricoplanar && !qh->TRInormals) { + qh_fprintf(qh, qh->ferr, 6230, "qhull internal error (qh_findhorizon): does not work for tricoplanar facets. Use option 'Q11'\n"); + qh_errexit(qh, qh_ERRqhull, visible, NULL); + } + if (qh_setsize(qh, visible->neighbors) == 0) { + qh_fprintf(qh, qh->ferr, 6295, "qhull internal error (qh_findhorizon): visible facet f%d does not have neighbors\n", visible->id); + qh_errexit(qh, 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(qh, point, neighbor, &dist); + if (dist > qh->MINvisible) { + zinc_(Ztotvisible); + qh_removefacet(qh, neighbor); /* append to end of qh->visible_list */ + qh_appendfacet(qh, neighbor); + neighbor->visible= True; + neighbor->f.replace= NULL; + qh->num_visible++; + if (neighbor->good) + (*goodvisible)++; + if (qh->IStracing >=4) + qh_errprint(qh, "visible", neighbor, NULL, NULL, NULL); + }else { + if (dist >= -qh->MAXcoplanar) { + neighbor->coplanarhorizon= True; + zzinc_(Zcoplanarhorizon); + qh_joggle_restart(qh, "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, qh->ferr, 2057, "qh_findhorizon: point p%d is coplanar to horizon f%d, dist=%2.7g < qh->MINvisible(%2.7g)\n", + qh_pointid(qh, point), neighbor->id, dist, qh->MINvisible)); + }else + neighbor->coplanarhorizon= False; + zinc_(Ztothorizon); + numhorizon++; + if (neighbor->good) + (*goodhorizon)++; + if (qh->IStracing >=4) + qh_errprint(qh, "horizon", neighbor, NULL, NULL, NULL); + } + } + } + if (!numhorizon) { + qh_joggle_restart(qh, "empty horizon"); + qh_fprintf(qh, qh->ferr, 6168, "qhull topology error (qh_findhorizon): empty horizon for p%d. It was above all facets.\n", qh_pointid(qh, point)); + if (qh->num_facets < 100) { + qh_printfacetlist(qh, qh->facet_list, NULL, True); + } + qh_errexit(qh, qh_ERRtopology, NULL, NULL); + } + trace1((qh, 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 < 100) + qh_printlists(qh); +} /* findhorizon */ + +/*-<a href="qh-qhull_r.htm#TOC" + >-------------------------------</a><a name="joggle_restart">-</a> + + qh_joggle_restart(qh, reason ) + if joggle ('QJn') and not merging, restart on precision and topology errors +*/ +void qh_joggle_restart(qhT *qh, const char *reason) { + + if (qh->JOGGLEmax < REALmax/2) { + if (qh->ALLOWrestart && !qh->PREmerge && !qh->MERGEexact) { + trace0((qh, qh->ferr, 26, "qh_joggle_restart: qhull restart because of %s\n", reason)); + /* May be called repeatedly if qh->ALLOWrestart */ + longjmp(qh->restartexit, qh_ERRprec); + } + } +} /* qh_joggle_restart */ + +/*-<a href="qh-qhull_r.htm#TOC" + >-------------------------------</a><a name="nextfurthest">-</a> + + qh_nextfurthest(qh, 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(qhT *qh, facetT **visible) { + facetT *facet; + int size, idx, loopcount= 0; + realT randr, dist; + pointT *furthest; + + while ((facet= qh->facet_next) != qh->facet_tail) { + if (!facet || loopcount++ > qh->num_facets) { + qh_fprintf(qh, qh->ferr, 6406, "qhull internal error (qh_nextfurthest): null facet or infinite loop detected for qh.facet_next f%d facet_tail f%d\n", + getid_(facet), getid_(qh->facet_tail)); + qh_printlists(qh); + qh_errexit2(qh, qh_ERRqhull, facet, qh->facet_tail); + } + if (!facet->outsideset) { + qh->facet_next= facet->next; + continue; + } + SETreturnsize_(facet->outsideset, size); + if (!size) { + qh_setfree(qh, &facet->outsideset); + qh->facet_next= facet->next; + continue; + } + if (qh->NARROWhull) { + if (facet->notfurthest) + qh_furthestout(qh, facet); + furthest= (pointT *)qh_setlast(facet->outsideset); +#if qh_COMPUTEfurthest + qh_distplane(qh, 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 /* 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(qh, facet->outsideset); + } + } + randr= qh_RANDOMint; + randr= randr/(qh_RANDOMmax+1); + randr= floor((qh->num_outside - outcoplanar) * randr); + idx= (int)randr; + FORALLfacet_(qh->facet_next) { + if (facet->outsideset) { + SETreturnsize_(facet->outsideset, size); + if (!size) + qh_setfree(qh, &facet->outsideset); + else if (size > idx) { + *visible= facet; + return ((pointT *)qh_setdelnth(qh, facet->outsideset, idx)); + }else + idx -= size; + } + } + qh_fprintf(qh, 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, qh_ERRqhull, NULL, NULL); + }else { /* VIRTUALmemory */ + facet= qh->facet_tail->previous; + if (!(furthest= (pointT *)qh_setdellast(facet->outsideset))) { + if (facet->outsideset) + qh_setfree(qh, &facet->outsideset); + qh_removefacet(qh, facet); + qh_prependfacet(qh, facet, &qh->facet_list); + continue; + } + *visible= facet; + return furthest; + } + } + return NULL; +} /* nextfurthest */ + +/*-<a href="qh-qhull_r.htm#TOC" + >-------------------------------</a><a name="partitionall">-</a> + + qh_partitionall(qh, 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(qhT *qh, 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, qh->ferr, 1042, "qh_partitionall: partition all points into outside sets\n")); + pointset= qh_settemp(qh, numpoints); + qh->num_outside= 0; + pointp= SETaddr_(pointset, pointT); + for (i=numpoints, point= points; i--; point += qh->hull_dim) + *(pointp++)= point; + qh_settruncate(qh, pointset, numpoints); + FOREACHvertex_(vertices) { + if ((id= qh_pointid(qh, vertex->point)) >= 0) + SETelem_(pointset, id)= NULL; + } + id= qh_pointid(qh, 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, 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_r.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(qh, size); + bestpoint= NULL; + point_end= 0; + FOREACHpoint_i_(qh, pointset) { + if (point) { + zzinc_(Zpartitionall); + qh_distplane(qh, 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(qh, &facet->outsideset, bestpoint); + bestpoint= point; + bestdist= dist; + }else + qh_setappend(qh, &facet->outsideset, point); + } + } + } + if (bestpoint) { + qh_setappend(qh, &facet->outsideset, bestpoint); +#if !qh_COMPUTEfurthest + facet->furthestdist= bestdist; +#endif + }else + qh_setfree(qh, &facet->outsideset); + qh_settruncate(qh, pointset, point_end); + } + } + /* if !qh->BESToutside, pointset contains points not assigned to outsideset */ + if (qh->BESToutside || qh->MERGING || qh->KEEPcoplanar || qh->KEEPinside || qh->KEEPnearinside) { + qh->findbestnew= True; + FOREACHpoint_i_(qh, pointset) { + if (point) + qh_partitionpoint(qh, point, qh->facet_list); + } + qh->findbestnew= False; + } + zzadd_(Zpartitionall, zzval_(Zpartition)); + zzval_(Zpartition)= 0; + qh_settempfree(qh, &pointset); + if (qh->IStracing >= 4) + qh_printfacetlist(qh, qh->facet_list, NULL, True); +} /* partitionall */ + + +/*-<a href="qh-qhull_r.htm#TOC" + >-------------------------------</a><a name="partitioncoplanar">-</a> + + qh_partitioncoplanar(qh, point, facet, dist, allnew ) + 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 allnew (qh.findbestnew) + searches new facets instead of using qh_findbest() + + returns: + qh.max_ouside updated + if qh.KEEPcoplanar or qh.KEEPinside + point assigned to best coplanarset + qh.repart_facetid==0 (for detecting infinite recursion via qh_partitionpoint) + + 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(qhT *qh, pointT *point, facetT *facet, realT *dist, boolT allnew) { + facetT *bestfacet; + pointT *oldfurthest; + realT bestdist, angle, nearest, dist2= 0.0; + int numpart= 0; + boolT isoutside, oldfindbest, repartition= False; + + trace4((qh, qh->ferr, 4090, "qh_partitioncoplanar: partition coplanar point p%d starting with f%d dist? %2.2g, allnew? %d, gh.repart_facetid f%d\n", + qh_pointid(qh, point), facet->id, (dist ? *dist : 0.0), allnew, qh->repart_facetid)); + qh->WAScoplanar= True; + if (!dist) { + if (allnew) + bestfacet= qh_findbestnew(qh, point, facet, &bestdist, qh_ALL, &isoutside, &numpart); + else + bestfacet= qh_findbest(qh, 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, qh->ferr, 4062, "qh_partitioncoplanar: point p%d is more than near-inside facet f%d dist %2.2g allnew? %d\n", + qh_pointid(qh, point), bestfacet->id, bestdist, allnew)); + qh->repart_facetid= 0; + return; + } + }else if (bestdist < -qh->MAXcoplanar) { + trace4((qh, qh->ferr, 4063, "qh_partitioncoplanar: point p%d is inside facet f%d dist %2.2g allnew? %d\n", + qh_pointid(qh, point), bestfacet->id, bestdist, allnew)); + zinc_(Zcoplanarinside); + qh->repart_facetid= 0; + return; + } + } + }else { + bestfacet= facet; + bestdist= *dist; + } + if(bestfacet->visible){ + qh_fprintf(qh, qh->ferr, 6405, "qhull internal error (qh_partitioncoplanar): cannot partition coplanar p%d of f%d into visible facet f%d\n", + qh_pointid(qh, point), facet->id, bestfacet->id); + qh_errexit2(qh, qh_ERRqhull, facet, bestfacet); + } + if (bestdist > qh->max_outside) { + if (!dist && facet != bestfacet) { /* can't be recursive from qh_partitionpoint since facet != bestfacet */ + zinc_(Zpartangle); + angle= qh_getangle(qh, facet->normal, bestfacet->normal); + if (angle < 0) { + nearest= qh_vertex_bestdist(qh, bestfacet->vertices); + /* typically due to deleted vertex and coplanar facets, e.g., + RBOX 1000 s Z1 G1e-13 t1001185205 | QHULL Tv */ + zinc_(Zpartcorner); + trace2((qh, qh->ferr, 2058, "qh_partitioncoplanar: repartition coplanar point p%d from f%d as an outside point above corner facet f%d dist %2.2g with angle %2.2g\n", + qh_pointid(qh, point), facet->id, bestfacet->id, bestdist, angle)); + repartition= True; + } + } + if (!repartition) { + if (bestdist > qh->MAXoutside * qh_RATIOcoplanaroutside) { + nearest= qh_vertex_bestdist(qh, bestfacet->vertices); + if (facet->id == bestfacet->id) { + if (facet->id == qh->repart_facetid) { + qh_fprintf(qh, qh->ferr, 6404, "Qhull internal error (qh_partitioncoplanar): infinite loop due to recursive call to qh_partitionpoint. Repartition point p%d from f%d as a outside point dist %2.2g nearest vertices %2.2g\n", + qh_pointid(qh, point), facet->id, bestdist, nearest); + qh_errexit(qh, qh_ERRqhull, facet, NULL); + } + qh->repart_facetid= facet->id; /* reset after call to qh_partitionpoint */ + } + if (point == qh->coplanar_apex) { + /* otherwise may loop indefinitely, the point is well above a facet, yet near a vertex */ + qh_fprintf(qh, qh->ferr, 6425, "Qhull topology error (qh_partitioncoplanar): can not repartition coplanar point p%d from f%d as outside point above f%d. It previously failed to form a cone of facets, dist %2.2g, nearest vertices %2.2g\n", + qh_pointid(qh, point), facet->id, bestfacet->id, bestdist, nearest); + qh_errexit(qh, qh_ERRtopology, facet, NULL); + } + if (nearest < 2 * qh->MAXoutside * qh_RATIOcoplanaroutside) { + zinc_(Zparttwisted); + qh_fprintf(qh, qh->ferr, 7085, "Qhull precision warning: repartition coplanar point p%d from f%d as an outside point above twisted facet f%d dist %2.2g nearest vertices %2.2g\n", + qh_pointid(qh, point), facet->id, bestfacet->id, bestdist, nearest); + }else { + zinc_(Zparthidden); + qh_fprintf(qh, qh->ferr, 7086, "Qhull precision warning: repartition coplanar point p%d from f%d as an outside point above hidden facet f%d dist %2.2g nearest vertices %2.2g\n", + qh_pointid(qh, point), facet->id, bestfacet->id, bestdist, nearest); + } + repartition= True; + } + } + if (repartition) { + oldfindbest= qh->findbestnew; + qh->findbestnew= False; + qh_partitionpoint(qh, point, bestfacet); + qh->findbestnew= oldfindbest; + qh->repart_facetid= 0; + return; + } + qh->repart_facetid= 0; + qh->max_outside= bestdist; + if (bestdist > qh->TRACEdist || qh->IStracing >= 3) { + qh_fprintf(qh, qh->ferr, 3041, "qh_partitioncoplanar: == p%d from f%d increases qh.max_outside to %2.2g of f%d last p%d\n", + qh_pointid(qh, point), facet->id, bestdist, bestfacet->id, qh->furthest_id); + qh_errprint(qh, "DISTANT", facet, bestfacet, NULL, NULL); + } + } + if (qh->KEEPcoplanar + qh->KEEPinside + qh->KEEPnearinside) { + oldfurthest= (pointT *)qh_setlast(bestfacet->coplanarset); + if (oldfurthest) { + zinc_(Zcomputefurthest); + qh_distplane(qh, oldfurthest, bestfacet, &dist2); + } + if (!oldfurthest || dist2 < bestdist) + qh_setappend(qh, &bestfacet->coplanarset, point); + else + qh_setappend2ndlast(qh, &bestfacet->coplanarset, point); + } + trace4((qh, qh->ferr, 4064, "qh_partitioncoplanar: point p%d is coplanar with facet f%d (or inside) dist %2.2g\n", + qh_pointid(qh, point), bestfacet->id, bestdist)); +} /* partitioncoplanar */ + +/*-<a href="qh-qhull_r.htm#TOC" + >-------------------------------</a><a name="partitionpoint">-</a> + + qh_partitionpoint(qh, 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(qhT *qh, pointT *point, facetT *facet) { + realT bestdist, previousdist; + boolT isoutside, isnewoutside= False; + facetT *bestfacet; + int numpart; + + if (qh->findbestnew) + bestfacet= qh_findbestnew(qh, point, facet, &bestdist, qh->BESToutside, &isoutside, &numpart); + else + bestfacet= qh_findbest(qh, point, facet, qh->BESToutside, qh_ISnewfacets, !qh_NOupper, + &bestdist, &isoutside, &numpart); + zinc_(Ztotpartition); + zzadd_(Zpartition, numpart); + if(bestfacet->visible){ + qh_fprintf(qh, qh->ferr, 6293, "qhull internal error (qh_partitionpoint): cannot partition p%d of f%d into visible facet f%d\n", + qh_pointid(qh, point), facet->id, bestfacet->id); + qh_errexit2(qh, qh_ERRqhull, facet, bestfacet); + } + if (qh->NARROWhull) { + if (qh->DELAUNAY && !isoutside && bestdist >= -qh->MAXcoplanar) + qh_joggle_restart(qh, "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)) { /* empty outside set */ + qh_setappend(qh, &(bestfacet->outsideset), point); + if (!qh->NARROWhull || bestdist > qh->MINoutside) + isnewoutside= True; +#if !qh_COMPUTEfurthest + bestfacet->furthestdist= bestdist; +#endif + }else { +#if qh_COMPUTEfurthest + zinc_(Zcomputefurthest); + qh_distplane(qh, oldfurthest, bestfacet, &previousdist); + if (previousdist < bestdist) + qh_setappend(qh, &(bestfacet->outsideset), point); + else + qh_setappend2ndlast(qh, &(bestfacet->outsideset), point); +#else + previousdist= bestfacet->furthestdist; + if (previousdist < bestdist) { + qh_setappend(qh, &(bestfacet->outsideset), point); + bestfacet->furthestdist= bestdist; + if (qh->NARROWhull && previousdist < qh->MINoutside && bestdist >= qh->MINoutside) + isnewoutside= True; + }else + qh_setappend2ndlast(qh, &(bestfacet->outsideset), point); +#endif + } + if (isnewoutside && qh->facet_next != bestfacet) { + if (bestfacet->newfacet) { + if (qh->facet_next->newfacet) + qh->facet_next= qh->newfacet_list; /* make sure it's after qh.facet_next */ + }else { + qh_removefacet(qh, bestfacet); /* make sure it's after qh.facet_next */ + qh_appendfacet(qh, bestfacet); + if(qh->newfacet_list){ + bestfacet->newfacet= True; + } + } + } + qh->num_outside++; + trace4((qh, qh->ferr, 4065, "qh_partitionpoint: point p%d is outside facet f%d newfacet? %d, newoutside? %d (or narrowhull)\n", + qh_pointid(qh, point), bestfacet->id, bestfacet->newfacet, isnewoutside)); + }else if (qh->DELAUNAY || bestdist >= -qh->MAXcoplanar) { /* for 'd', bestdist skips upperDelaunay facets */ + if (qh->DELAUNAY) + qh_joggle_restart(qh, "nearly incident point"); + /* allow coplanar points with joggle, may be interior */ + zzinc_(Zcoplanarpart); + if ((qh->KEEPcoplanar + qh->KEEPnearinside) || bestdist > qh->max_outside) + qh_partitioncoplanar(qh, point, bestfacet, &bestdist, qh->findbestnew); + else { + trace4((qh, qh->ferr, 4066, "qh_partitionpoint: point p%d is coplanar to facet f%d (dropped)\n", + qh_pointid(qh, point), bestfacet->id)); + } + }else if (qh->KEEPnearinside && bestdist >= -qh->NEARinside) { + zinc_(Zpartnear); + qh_partitioncoplanar(qh, point, bestfacet, &bestdist, qh->findbestnew); + }else { + zinc_(Zpartinside); + trace4((qh, qh->ferr, 4067, "qh_partitionpoint: point p%d is inside all facets, closest to f%d dist %2.2g\n", + qh_pointid(qh, point), bestfacet->id, bestdist)); + if (qh->KEEPinside) + qh_partitioncoplanar(qh, point, bestfacet, &bestdist, qh->findbestnew); + } +} /* partitionpoint */ + +/*-<a href="qh-qhull_r.htm#TOC" + >-------------------------------</a><a name="partitionvisible">-</a> + + qh_partitionvisible(qh, allpoints, numoutside ) + partitions outside points in visible facets (qh.visible_list) to qh.newfacet_list + if keeping coplanar/near-inside/inside points + partitions coplanar points; repartitions if 'allpoints' (not used) + 1st neighbor (if any) of visible facets points to a horizon facet or a new facet + + returns: + updates outside sets and coplanar sets of qh.newfacet_list + updates qh.num_outside (count of outside points) + does not truncate f.outsideset, f.coplanarset, or qh.del_vertices (see qh_deletevisible) + + notes: + called by qh_qhull, qh_addpoint, and qh_all_vertexmerges + 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(qhT *qh, boolT allpoints, int *numoutside /* qh.visible_list */) { + facetT *visible, *newfacet; + pointT *point, **pointp; + int delsize, coplanar=0, size; + vertexT *vertex, **vertexp; + + trace3((qh, qh->ferr, 3042, "qh_partitionvisible: partition outside and coplanar points of visible and merged facets f%d into new facets f%d\n", + qh->visible_list->id, qh->newfacet_list->id)); + if (qh->ONLYmax) + maximize_(qh->MINoutside, qh->max_vertex); + *numoutside= 0; + FORALLvisible_facets { + if (!visible->outsideset && !visible->coplanarset) + continue; + newfacet= qh_getreplacement(qh, visible); + if (!newfacet) + newfacet= qh->newfacet_list; + if (!newfacet->next) { + qh_fprintf(qh, qh->ferr, 6170, "qhull topology error (qh_partitionvisible): all new facets deleted as\n degenerate facets. Can not continue.\n"); + qh_errexit(qh, qh_ERRtopology, NULL, NULL); + } + if (visible->outsideset) { + size= qh_setsize(qh, visible->outsideset); + *numoutside += size; + qh->num_outside -= size; + FOREACHpoint_(visible->outsideset) + qh_partitionpoint(qh, point, newfacet); + } + if (visible->coplanarset && (qh->KEEPcoplanar + qh->KEEPinside + qh->KEEPnearinside)) { + size= qh_setsize(qh, visible->coplanarset); + coplanar += size; + FOREACHpoint_(visible->coplanarset) { + if (allpoints) /* not used */ + qh_partitionpoint(qh, point, newfacet); + else + qh_partitioncoplanar(qh, point, newfacet, NULL, qh->findbestnew); + } + } + } + delsize= qh_setsize(qh, qh->del_vertices); + if (delsize > 0) { + trace3((qh, qh->ferr, 3049, "qh_partitionvisible: partition %d deleted vertices as coplanar? %d points into new facets f%d\n", + delsize, !allpoints, qh->newfacet_list->id)); + FOREACHvertex_(qh->del_vertices) { + if (vertex->point && !vertex->partitioned) { + if (!qh->newfacet_list || qh->newfacet_list == qh->facet_tail) { + qh_fprintf(qh, qh->ferr, 6284, "qhull internal error (qh_partitionvisible): all new facets deleted or none defined. Can not partition deleted v%d.\n", vertex->id); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + if (allpoints) /* not used */ + /* [apr'2019] infinite loop if vertex recreates the same facets from the same horizon + e.g., qh_partitionpoint if qh.DELAUNAY with qh.MERGEindependent for all mergetype, ../eg/qtest.sh t427764 '1000 s W1e-13 D3' 'd' */ + qh_partitionpoint(qh, vertex->point, qh->newfacet_list); + else + qh_partitioncoplanar(qh, vertex->point, qh->newfacet_list, NULL, qh_ALL); /* search all new facets */ + vertex->partitioned= True; + } + } + } + trace1((qh, qh->ferr, 1043,"qh_partitionvisible: partitioned %d points from outsidesets, %d points from coplanarsets, and %d deleted vertices\n", *numoutside, coplanar, delsize)); +} /* partitionvisible */ + +/*-<a href="qh-qhull_r.htm#TOC" + >-------------------------------</a><a name="printsummary">-</a> + + qh_printsummary(qh, fp ) + prints summary to fp + + notes: + not in io_r.c so that user_eg.c can prevent io_r.c from loading + qh_printsummary and qh_countfacets must match counts + updates qh.facet_visit to detect infinite loop + + design: + determine number of points, vertices, and coplanar points + print summary +*/ +void qh_printsummary(qhT *qh, FILE *fp) { + realT ratio, outerplane, innerplane; + double cpu; + int size, id, nummerged, numpinched, numvertices, numcoplanars= 0, nonsimplicial=0, numdelaunay= 0; + facetT *facet; + const char *s; + int numdel= zzval_(Zdelvertextot); + int numtricoplanars= 0; + boolT goodused; + + size= qh->num_points + qh_setsize(qh, qh->other_points); + numvertices= qh->num_vertices - qh_setsize(qh, qh->del_vertices); + id= qh_pointid(qh, qh->GOODpointp); + if (!qh_checklists(qh, qh->facet_list) && !qh->ERREXITcalled) { + qh_fprintf(qh, fp, 6372, "qhull internal error: qh_checklists failed at qh_printsummary\n"); + if (qh->num_facets < 4000) + qh_printlists(qh); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + if (qh->DELAUNAY && qh->ERREXITcalled) { + /* update f.good and determine qh.num_good as in qh_findgood_all */ + FORALLfacets { + if (facet->visible) + facet->good= False; /* will be deleted */ + else if (facet->good) { + if (facet->normal && !qh_inthresholds(qh, facet->normal, NULL)) + facet->good= False; + else + numdelaunay++; + } + } + qh->num_good= numdelaunay; + } + FORALLfacets { + if (facet->coplanarset) + numcoplanars += qh_setsize(qh, facet->coplanarset); + if (facet->good) { + if (facet->simplicial) { + if (facet->keepcentrum && facet->tricoplanar) + numtricoplanars++; + }else if (qh_setsize(qh, facet->vertices) != qh->hull_dim) + nonsimplicial++; + } + } + if (id >=0 && qh->STOPcone-1 != id && -qh->STOPpoint-1 != id) + size--; + if (qh->STOPadd || qh->STOPcone || qh->STOPpoint) + qh_fprintf(qh, fp, 9288, "\nEarly exit due to 'TAn', 'TVn', 'TCn', 'TRn', or precision error with 'QJn'."); + goodused= False; + if (qh->ERREXITcalled) + ; /* qh_findgood_all not called */ + else if (qh->UPPERdelaunay) { + if (qh->GOODvertex || qh->GOODpoint || qh->SPLITthresholds) + goodused= True; + }else if (qh->DELAUNAY) { + if (qh->GOODvertex || qh->GOODpoint || qh->GOODthreshold) + goodused= True; + }else if (qh->num_good > 0 || qh->GOODthreshold) + goodused= True; + nummerged= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot); + if (qh->VORONOI) { + if (qh->UPPERdelaunay) + qh_fprintf(qh, 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(qh, fp, 9290, "\n\ +Voronoi diagram by the convex hull of %d points in %d-d:\n\n", size, qh->hull_dim); + qh_fprintf(qh, fp, 9291, " Number of Voronoi regions%s: %d\n", + qh->ATinfinity ? " and at-infinity" : "", numvertices); + if (numdel) + qh_fprintf(qh, fp, 9292, " Total number of deleted points due to merging: %d\n", numdel); + if (numcoplanars - numdel > 0) + qh_fprintf(qh, fp, 9293, " Number of nearly incident points: %d\n", numcoplanars - numdel); + else if (size - numvertices - numdel > 0) + qh_fprintf(qh, fp, 9294, " Total number of nearly incident points: %d\n", size - numvertices - numdel); + qh_fprintf(qh, fp, 9295, " Number of%s Voronoi vertices: %d\n", + goodused ? " 'good'" : "", qh->num_good); + if (nonsimplicial) + qh_fprintf(qh, fp, 9296, " Number of%s non-simplicial Voronoi vertices: %d\n", + goodused ? " 'good'" : "", nonsimplicial); + }else if (qh->DELAUNAY) { + if (qh->UPPERdelaunay) + qh_fprintf(qh, 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(qh, fp, 9298, "\n\ +Delaunay triangulation by the convex hull of %d points in %d-d:\n\n", size, qh->hull_dim); + qh_fprintf(qh, fp, 9299, " Number of input sites%s: %d\n", + qh->ATinfinity ? " and at-infinity" : "", numvertices); + if (numdel) + qh_fprintf(qh, fp, 9300, " Total number of deleted points due to merging: %d\n", numdel); + if (numcoplanars - numdel > 0) + qh_fprintf(qh, fp, 9301, " Number of nearly incident points: %d\n", numcoplanars - numdel); + else if (size - numvertices - numdel > 0) + qh_fprintf(qh, fp, 9302, " Total number of nearly incident points: %d\n", size - numvertices - numdel); + qh_fprintf(qh, fp, 9303, " Number of%s Delaunay regions: %d\n", + goodused ? " 'good'" : "", qh->num_good); + if (nonsimplicial) + qh_fprintf(qh, fp, 9304, " Number of%s non-simplicial Delaunay regions: %d\n", + goodused ? " 'good'" : "", nonsimplicial); + }else if (qh->HALFspace) { + qh_fprintf(qh, fp, 9305, "\n\ +Halfspace intersection by the convex hull of %d points in %d-d:\n\n", size, qh->hull_dim); + qh_fprintf(qh, fp, 9306, " Number of halfspaces: %d\n", size); + qh_fprintf(qh, 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(qh, fp, 9308, " Number of %s halfspaces: %d\n", s, numcoplanars); + } + qh_fprintf(qh, fp, 9309, " Number of intersection points: %d\n", qh->num_facets - qh->num_visible); + if (goodused) + qh_fprintf(qh, fp, 9310, " Number of 'good' intersection points: %d\n", qh->num_good); + if (nonsimplicial) + qh_fprintf(qh, fp, 9311, " Number of%s non-simplicial intersection points: %d\n", + goodused ? " 'good'" : "", nonsimplicial); + }else { + qh_fprintf(qh, fp, 9312, "\n\ +Convex hull of %d points in %d-d:\n\n", size, qh->hull_dim); + qh_fprintf(qh, 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(qh, fp, 9314, " Number of %s points: %d\n", s, numcoplanars); + } + qh_fprintf(qh, fp, 9315, " Number of facets: %d\n", qh->num_facets - qh->num_visible); + if (goodused) + qh_fprintf(qh, fp, 9316, " Number of 'good' facets: %d\n", qh->num_good); + if (nonsimplicial) + qh_fprintf(qh, fp, 9317, " Number of%s non-simplicial facets: %d\n", + goodused ? " 'good'" : "", nonsimplicial); + } + if (numtricoplanars) + qh_fprintf(qh, fp, 9318, " Number of triangulated facets: %d\n", numtricoplanars); + qh_fprintf(qh, fp, 9319, "\nStatistics for: %s | %s", + qh->rbox_command, qh->qhull_command); + if (qh->ROTATErandom != INT_MIN) + qh_fprintf(qh, fp, 9320, " QR%d\n\n", qh->ROTATErandom); + else + qh_fprintf(qh, fp, 9321, "\n\n"); + qh_fprintf(qh, fp, 9322, " Number of points processed: %d\n", zzval_(Zprocessed)); + qh_fprintf(qh, fp, 9323, " Number of hyperplanes created: %d\n", zzval_(Zsetplane)); + if (qh->DELAUNAY) + qh_fprintf(qh, fp, 9324, " Number of facets in hull: %d\n", qh->num_facets - qh->num_visible); + qh_fprintf(qh, 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(qh, 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(qh, fp, 9327, " new facet standard deviation: %2.2g\n", stddev); + qh_fprintf(qh, fp, 9328, " average partition balance: %2.2g\n", + wval_(Wpbalance)/zval_(Zpbalance)); + stddev= qh_stddev(zval_(Zpbalance), wval_(Wpbalance), + wval_(Wpbalance2), &ave); + qh_fprintf(qh, fp, 9329, " partition standard deviation: %2.2g\n", stddev); + } +#endif + if (nummerged) { + qh_fprintf(qh, fp, 9330," Number of distance tests for merging: %d\n",zzval_(Zbestdist)+ + zzval_(Zcentrumtests)+zzval_(Zvertextests)+zzval_(Zdistcheck)+zzval_(Zdistzero)); + qh_fprintf(qh, fp, 9331," Number of distance tests for checking: %d\n",zzval_(Zcheckpart)+zzval_(Zdistconvex)); + qh_fprintf(qh, fp, 9332," Number of merged facets: %d\n", nummerged); + } + numpinched= zzval_(Zpinchduplicate) + zzval_(Zpinchedvertex); + if (numpinched) + qh_fprintf(qh, fp, 9375," Number of merged pinched vertices: %d\n", numpinched); + if (!qh->RANDOMoutside && qh->QHULLfinished) { + cpu= (double)qh->hulltime; + cpu /= (double)qh_SECticks; + wval_(Wcpu)= cpu; + qh_fprintf(qh, fp, 9333, " CPU seconds to compute hull (after input): %2.4g\n", cpu); + } + if (qh->RERUN) { + if (!qh->PREmerge && !qh->MERGEexact) + qh_fprintf(qh, 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(qh, fp, 9335, " After %d retries, input joggled by: %2.2g\n", + zzval_(Zretry), qh->JOGGLEmax); + else + qh_fprintf(qh, fp, 9336, " Input joggled by: %2.2g\n", qh->JOGGLEmax); + } + if (qh->totarea != 0.0) + qh_fprintf(qh, fp, 9337, " %s facet area: %2.8g\n", + zzval_(Ztotmerge) ? "Approximate" : "Total", qh->totarea); + if (qh->totvol != 0.0) + qh_fprintf(qh, fp, 9338, " %s volume: %2.8g\n", + zzval_(Ztotmerge) ? "Approximate" : "Total", qh->totvol); + if (qh->MERGING) { + qh_outerinner(qh, NULL, &outerplane, &innerplane); + if (outerplane > 2 * qh->DISTround) { + qh_fprintf(qh, fp, 9339, " Maximum distance of point above facet: %2.2g", 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(qh, fp, 9340, " (%.1fx)\n", ratio); + else + qh_fprintf(qh, fp, 9341, "\n"); + } + if (innerplane < -2 * qh->DISTround) { + qh_fprintf(qh, fp, 9342, " Maximum distance of vertex below facet: %2.2g", innerplane); + ratio= -innerplane/(qh->ONEmerge+qh->DISTround); + if (ratio > 0.05 && qh->JOGGLEmax > REALmax/2) + qh_fprintf(qh, fp, 9343, " (%.1fx)\n", ratio); + else + qh_fprintf(qh, fp, 9344, "\n"); + } + } + qh_fprintf(qh, fp, 9345, "\n"); +} /* printsummary */ + + diff --git a/contrib/libs/qhull/libqhull_r/libqhull_r.h b/contrib/libs/qhull/libqhull_r/libqhull_r.h new file mode 100644 index 0000000000..1f476b33ea --- /dev/null +++ b/contrib/libs/qhull/libqhull_r/libqhull_r.h @@ -0,0 +1,1227 @@ +/*<html><pre> -<a href="qh-qhull_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + libqhull_r.h + user-level header file for using qhull.a library + + see qh-qhull_r.htm, qhull_ra.h + + Copyright (c) 1993-2020 The Geometry Center. + $Id: //main/2019/qhull/src/libqhull_r/libqhull_r.h#16 $$Change: 3037 $ + $DateTime: 2020/09/03 17:28:32 $$Author: bbarber $ + + includes function prototypes for libqhull_r.c, geom_r.c, global_r.c, io_r.c, user_r.c + + use mem_r.h for mem_r.c + use qset_r.h for qset_r.c + + see unix_r.c for an example of using libqhull_r.h + + recompile qhull if you change this file +*/ + +#ifndef qhDEFlibqhull +#define qhDEFlibqhull 1 + +/*=========================== -included files ==============*/ + +/* user_r.h first for QHULL_CRTDBG */ +#include "user_r.h" /* user definable constants (e.g., realT). */ + +#include "mem_r.h" /* Needed for qhT in libqhull_r.h */ +#include "qset_r.h" /* Needed for QHULL_LIB_CHECK */ +/* include stat_r.h after defining boolT. Needed for qhT in libqhull_r.h */ + +#include <setjmp.h> +#include <float.h> +#include <limits.h> +#include <time.h> +#include <stdio.h> + +#ifndef __STDC__ +#ifndef __cplusplus +#if !defined(_MSC_VER) +#error Neither __STDC__ nor __cplusplus is defined. Please use strict ANSI C or C++ to compile +#error Qhull. You may need to turn off compiler extensions in your project configuration. If +#error your compiler is a standard C compiler, you can delete this warning from libqhull_r.h +#endif +#endif +#endif + +/*============ constants and basic types ====================*/ + +extern const char qh_version[]; /* defined in global_r.c */ +extern const char qh_version2[]; /* defined in global_r.c */ + +/*-<a href="qh-geom_r.htm#TOC" + >--------------------------------</a><a name="coordT">-</a> + + coordT + coordinates and coefficients are stored as realT (i.e., double) + + notes: + Qhull works well if realT is 'float'. If so joggle (QJ) is not effective. + + Could use 'float' for data and 'double' for calculations (realT vs. coordT) + This requires many type casts, and adjusted error bounds. + Also C compilers may do expressions in double anyway. +*/ +#define coordT realT + +/*-<a href="qh-geom_r.htm#TOC" + >--------------------------------</a><a name="pointT">-</a> + + pointT + a point is an array of coordinates, usually qh.hull_dim + qh_pointid returns + qh_IDnone if point==0 or qh is undefined + qh_IDinterior for qh.interior_point + qh_IDunknown if point is neither in qh.first_point... nor qh.other_points + + notes: + qh.STOPcone and qh.STOPpoint assume that qh_IDunknown==-1 (other negative numbers indicate points) + qh_IDunknown is also returned by getid_() for unknown facet, ridge, or vertex +*/ +#define pointT coordT +typedef enum +{ + qh_IDnone= -3, qh_IDinterior= -2, qh_IDunknown= -1 +} +qh_pointT; + +/*-<a href="qh-qhull_r.htm#TOC" + >--------------------------------</a><a name="flagT">-</a> + + flagT + Boolean flag as a bit +*/ +#define flagT unsigned int + +/*-<a href="qh-qhull_r.htm#TOC" + >--------------------------------</a><a name="boolT">-</a> + + boolT + boolean value, either True or False + + notes: + needed for portability + Use qh_False/qh_True as synonyms +*/ +#define boolT unsigned int +#ifdef False +#undef False +#endif +#ifdef True +#undef True +#endif +#define False 0 +#define True 1 +#define qh_False 0 +#define qh_True 1 + +#include "stat_r.h" /* needs boolT */ + +/*-<a href="qh-qhull_r.htm#TOC" + >--------------------------------</a><a name="CENTERtype">-</a> + + qh_CENTER + to distinguish facet->center +*/ +typedef enum +{ + qh_ASnone= 0, /* If not MERGING and not VORONOI */ + qh_ASvoronoi, /* Set by qh_clearcenters on qh_prepare_output, or if not MERGING and VORONOI */ + qh_AScentrum /* If MERGING (assumed during merging) */ +} +qh_CENTER; + +/*-<a href="qh-qhull_r.htm#TOC" + >--------------------------------</a><a name="qh_PRINT">-</a> + + qh_PRINT + output formats for printing (qh.PRINTout). + 'Fa' 'FV' 'Fc' 'FC' + + + notes: + some of these names are similar to qhT names. The similar names are only + used in switch statements in qh_printbegin() etc. +*/ +typedef enum {qh_PRINTnone= 0, + qh_PRINTarea, qh_PRINTaverage, /* 'Fa' 'FV' 'Fc' 'FC' */ + qh_PRINTcoplanars, qh_PRINTcentrums, + qh_PRINTfacets, qh_PRINTfacets_xridge, /* 'f' 'FF' 'G' 'FI' 'Fi' 'Fn' */ + qh_PRINTgeom, qh_PRINTids, qh_PRINTinner, qh_PRINTneighbors, + qh_PRINTnormals, qh_PRINTouter, qh_PRINTmaple, /* 'n' 'Fo' 'i' 'm' 'Fm' 'FM', 'o' */ + qh_PRINTincidences, qh_PRINTmathematica, qh_PRINTmerges, qh_PRINToff, + qh_PRINToptions, qh_PRINTpointintersect, /* 'FO' 'Fp' 'FP' 'p' 'FQ' 'FS' */ + qh_PRINTpointnearest, qh_PRINTpoints, qh_PRINTqhull, qh_PRINTsize, + qh_PRINTsummary, qh_PRINTtriangles, /* 'Fs' 'Ft' 'Fv' 'FN' 'Fx' */ + qh_PRINTvertices, qh_PRINTvneighbors, qh_PRINTextremes, + qh_PRINTEND} qh_PRINT; + +/*-<a href="qh-qhull_r.htm#TOC" + >--------------------------------</a><a name="qh_ALL">-</a> + + qh_ALL + argument flag for selecting everything +*/ +#define qh_ALL True +#define qh_NOupper True /* argument for qh_findbest */ +#define qh_IScheckmax True /* argument for qh_findbesthorizon */ +#define qh_ISnewfacets True /* argument for qh_findbest */ +#define qh_RESETvisible True /* argument for qh_resetlists */ + +/*-<a href="qh-qhull_r.htm#TOC" + >--------------------------------</a><a name="qh_ERR">-</a> + + qh_ERR... + Qhull exit status codes, for indicating errors + See: MSG_ERROR (6000) and MSG_WARNING (7000) [user_r.h] +*/ +#define qh_ERRnone 0 /* no error occurred during qhull */ +#define qh_ERRinput 1 /* input inconsistency */ +#define qh_ERRsingular 2 /* singular input data, calls qh_printhelp_singular */ +#define qh_ERRprec 3 /* precision error, calls qh_printhelp_degenerate */ +#define qh_ERRmem 4 /* insufficient memory, matches mem_r.h */ +#define qh_ERRqhull 5 /* internal error detected, matches mem_r.h, calls qh_printhelp_internal */ +#define qh_ERRother 6 /* other error detected */ +#define qh_ERRtopology 7 /* topology error, maybe due to nearly adjacent vertices, calls qh_printhelp_topology */ +#define qh_ERRwide 8 /* wide facet error, maybe due to nearly adjacent vertices, calls qh_printhelp_wide */ +#define qh_ERRdebug 9 /* qh_errexit from debugging code */ + +/*-<a href="qh-qhull_r.htm#TOC" +>--------------------------------</a><a name="qh_FILEstderr">-</a> + +qh_FILEstderr +Fake stderr to distinguish error output from normal output +For C++ interface. Must redefine qh_fprintf_qhull +*/ +#define qh_FILEstderr ((FILE *)1) + +/* ============ -structures- ==================== + each of the following structures is defined by a typedef + all realT and coordT fields occur at the beginning of a structure + (otherwise space may be wasted due to alignment) + define all flags together and pack into 32-bit number + + DEFqhT and DEFsetT are likewise defined in mem_r.h, qset_r.h, and stat_r.h +*/ + +typedef struct vertexT vertexT; +typedef struct ridgeT ridgeT; +typedef struct facetT facetT; + +#ifndef DEFqhT +#define DEFqhT 1 +typedef struct qhT qhT; /* defined below */ +#endif + +#ifndef DEFsetT +#define DEFsetT 1 +typedef struct setT setT; /* defined in qset_r.h */ +#endif + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="facetT">-</a> + + facetT + defines a facet + + notes: + qhull() generates the hull as a list of facets. + + topological information: + f.previous,next doubly-linked list of facets, next is always defined + f.vertices set of vertices + f.ridges set of ridges + f.neighbors set of neighbors + f.toporient True if facet has top-orientation (else bottom) + + geometric information: + f.offset,normal hyperplane equation + f.maxoutside offset to outer plane -- all points inside + f.center centrum for testing convexity or Voronoi center for output + f.simplicial True if facet is simplicial + f.flipped True if facet does not include qh.interior_point + + for constructing hull: + f.visible True if facet on list of visible facets (will be deleted) + f.newfacet True if facet on list of newly created facets + f.coplanarset set of points coplanar with this facet + (includes near-inside points for later testing) + f.outsideset set of points outside of this facet + f.furthestdist distance to furthest point of outside set + f.visitid marks visited facets during a loop + f.replace replacement facet for to-be-deleted, visible facets + f.samecycle,newcycle cycle of facets for merging into horizon facet + + see below for other flags and fields +*/ +/* QhullFacet.cpp -- Update static initializer list for s_empty_facet if add or remove fields */ +struct facetT { +#if !qh_COMPUTEfurthest + coordT furthestdist;/* distance to furthest point of outsideset */ +#endif +#if qh_MAXoutside + coordT maxoutside; /* max computed distance of point to facet + Before QHULLfinished this is an approximation + since maxdist not always set for qh_mergefacet + Actual outer plane is +DISTround and + computed outer plane is +2*DISTround. + Initial maxoutside is qh.DISTround, otherwise distance tests need to account for DISTround */ +#endif + coordT offset; /* exact offset of hyperplane from origin */ + coordT *normal; /* normal of hyperplane, hull_dim coefficients */ + /* if f.tricoplanar, shared with a neighbor */ + union { /* in order of testing */ + realT area; /* area of facet, only in io_r.c if f.isarea */ + facetT *replace; /* replacement facet for qh.NEWfacets with f.visible + NULL if qh_mergedegen_redundant, interior, or !NEWfacets */ + facetT *samecycle; /* cycle of facets from the same visible/horizon intersection, + if ->newfacet */ + facetT *newcycle; /* in horizon facet, current samecycle of new facets */ + facetT *trivisible; /* visible facet for ->tricoplanar facets during qh_triangulate() */ + facetT *triowner; /* owner facet for ->tricoplanar, !isarea facets w/ ->keepcentrum */ + }f; + coordT *center; /* set according to qh.CENTERtype */ + /* qh_ASnone: no center (not MERGING) */ + /* qh_AScentrum: centrum for testing convexity (qh_getcentrum) */ + /* assumed qh_AScentrum while merging */ + /* qh_ASvoronoi: Voronoi center (qh_facetcenter) */ + /* after constructing the hull, it may be changed (qh_clearcenter) */ + /* if tricoplanar and !keepcentrum, shared with a neighbor */ + facetT *previous; /* previous facet in the facet_list or NULL, for C++ interface */ + facetT *next; /* next facet in the facet_list or facet_tail */ + setT *vertices; /* vertices for this facet, inverse sorted by ID + if simplicial, 1st vertex was apex/furthest + qh_reduce_vertices removes extraneous vertices via qh_remove_extravertices + if f.visible, vertices may be on qh.del_vertices */ + setT *ridges; /* explicit ridges for nonsimplicial facets or nonsimplicial neighbors. + For simplicial facets, neighbors define the ridges + qh_makeridges() converts simplicial facets by creating ridges prior to merging + If qh.NEWtentative, new facets have horizon ridge, but not vice versa + if f.visible && qh.NEWfacets, ridges is empty */ + setT *neighbors; /* neighbors of the facet. Neighbors may be f.visible + If simplicial, the kth neighbor is opposite the kth vertex and the + first neighbor is the horizon facet for the first vertex. + dupridges marked by qh_DUPLICATEridge (0x01) and qh_MERGEridge (0x02) + if f.visible && qh.NEWfacets, neighbors is empty */ + setT *outsideset; /* set of points outside this facet + if non-empty, last point is furthest + if NARROWhull, includes coplanars (less than qh.MINoutside) for partitioning*/ + setT *coplanarset; /* set of points coplanar with this facet + >= qh.min_vertex and <= facet->max_outside + a point is assigned to the furthest facet + if non-empty, last point is furthest away */ + unsigned int visitid; /* visit_id, for visiting all neighbors, + all uses are independent */ + unsigned int id; /* unique identifier from qh.facet_id, 1..qh.facet_id, 0 is sentinel, printed as 'f%d' */ + unsigned int nummerge:9; /* number of merges */ +#define qh_MAXnummerge 511 /* 2^9-1 */ + /* 23 flags (at most 23 due to nummerge), printed by "flags:" in io_r.c */ + flagT tricoplanar:1; /* True if TRIangulate and simplicial and coplanar with a neighbor */ + /* all tricoplanars share the same apex */ + /* all tricoplanars share the same ->center, ->normal, ->offset, ->maxoutside */ + /* ->keepcentrum is true for the owner. It has the ->coplanareset */ + /* if ->degenerate, does not span facet (one logical ridge) */ + /* during qh_triangulate, f.trivisible points to original facet */ + flagT newfacet:1; /* True if facet on qh.newfacet_list (new/qh.first_newfacet or merged) */ + flagT visible:1; /* True if visible facet (will be deleted) */ + flagT toporient:1; /* True if created with top orientation + after merging, use ridge orientation */ + flagT simplicial:1;/* True if simplicial facet, ->ridges may be implicit */ + flagT seen:1; /* used to perform operations only once, like visitid */ + flagT seen2:1; /* used to perform operations only once, like visitid */ + flagT flipped:1; /* True if facet is flipped */ + flagT upperdelaunay:1; /* True if facet is upper envelope of Delaunay triangulation */ + flagT notfurthest:1; /* True if last point of outsideset is not furthest */ + +/*-------- flags primarily for output ---------*/ + flagT good:1; /* True if a facet marked good for output */ + flagT isarea:1; /* True if facet->f.area is defined */ + +/*-------- flags for merging ------------------*/ + flagT dupridge:1; /* True if facet has one or more dupridge in a new facet (qh_matchneighbor), + a dupridge has a subridge shared by more than one new facet */ + flagT mergeridge:1; /* True if facet or neighbor has a qh_MERGEridge (qh_mark_dupridges) + ->normal defined for mergeridge and mergeridge2 */ + flagT mergeridge2:1; /* True if neighbor has a qh_MERGEridge (qh_mark_dupridges) */ + flagT coplanarhorizon:1; /* True if horizon facet is coplanar at last use */ + flagT mergehorizon:1; /* True if will merge into horizon (its first neighbor w/ f.coplanarhorizon). */ + flagT cycledone:1;/* True if mergecycle_all already done */ + flagT tested:1; /* True if facet convexity has been tested (false after merge */ + flagT keepcentrum:1; /* True if keep old centrum after a merge, or marks owner for ->tricoplanar + Set by qh_updatetested if more than qh_MAXnewcentrum extra vertices + Set by qh_mergefacet if |maxdist| > qh.WIDEfacet */ + flagT newmerge:1; /* True if facet is newly merged for reducevertices */ + flagT degenerate:1; /* True if facet is degenerate (degen_mergeset or ->tricoplanar) */ + flagT redundant:1; /* True if facet is redundant (degen_mergeset) + Maybe merge degenerate and redundant to gain another flag */ +}; + + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="ridgeT">-</a> + + ridgeT + defines a ridge + + notes: + a ridge is hull_dim-1 simplex between two neighboring facets. If the + facets are non-simplicial, there may be more than one ridge between + two facets. E.G. a 4-d hypercube has two triangles between each pair + of neighboring facets. + + topological information: + vertices a set of vertices + top,bottom neighboring facets with orientation + + geometric information: + tested True if ridge is clearly convex + nonconvex True if ridge is non-convex +*/ +/* QhullRidge.cpp -- Update static initializer list for s_empty_ridge if add or remove fields */ +struct ridgeT { + setT *vertices; /* vertices belonging to this ridge, inverse sorted by ID + NULL if a degen ridge (matchsame) */ + facetT *top; /* top facet for this ridge */ + facetT *bottom; /* bottom facet for this ridge + ridge oriented by odd/even vertex order and top/bottom */ + unsigned int id; /* unique identifier. Same size as vertex_id, printed as 'r%d' */ + flagT seen:1; /* used to perform operations only once */ + flagT tested:1; /* True when ridge is tested for convexity by centrum or opposite vertices */ + flagT nonconvex:1; /* True if getmergeset detected a non-convex neighbor + only one ridge between neighbors may have nonconvex */ + flagT mergevertex:1; /* True if pending qh_appendvertexmerge due to + qh_maybe_duplicateridge or qh_maybe_duplicateridges + disables check for duplicate vertices in qh_checkfacet */ + flagT mergevertex2:1; /* True if qh_drop_mergevertex of MRGvertices, printed but not used */ + flagT simplicialtop:1; /* True if top was simplicial (original vertices) */ + flagT simplicialbot:1; /* True if bottom was simplicial (original vertices) + use qh_test_centrum_merge if top and bot, need to retest since centrum may change */ +}; + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="vertexT">-</a> + + vertexT + defines a vertex + + topological information: + next,previous doubly-linked list of all vertices + neighbors set of adjacent facets (only if qh.VERTEXneighbors) + + geometric information: + point array of DIM3 coordinates +*/ +/* QhullVertex.cpp -- Update static initializer list for s_empty_vertex if add or remove fields */ +struct vertexT { + vertexT *next; /* next vertex in vertex_list or vertex_tail */ + vertexT *previous; /* previous vertex in vertex_list or NULL, for C++ interface */ + pointT *point; /* hull_dim coordinates (coordT) */ + setT *neighbors; /* neighboring facets of vertex, qh_vertexneighbors() + initialized in io_r.c or after first merge + qh_update_vertices for qh_addpoint or qh_triangulate + updated by merges + qh_order_vertexneighbors by 2-d (orientation) 3-d (adjacency), n-d (f.visitid,id) */ + unsigned int id; /* unique identifier, 1..qh.vertex_id, 0 for sentinel, printed as 'r%d' */ + unsigned int visitid; /* for use with qh.vertex_visit, size must match */ + flagT seen:1; /* used to perform operations only once */ + flagT seen2:1; /* another seen flag */ + flagT deleted:1; /* vertex will be deleted via qh.del_vertices */ + flagT delridge:1; /* vertex belonged to a deleted ridge, cleared by qh_reducevertices */ + flagT newfacet:1; /* true if vertex is in a new facet + vertex is on qh.newvertex_list and it has a facet on qh.newfacet_list + or vertex is on qh.newvertex_list due to qh_newvertices while merging + cleared by qh_resetlists */ + flagT partitioned:1; /* true if deleted vertex has been partitioned */ +}; + +/*======= -global variables -qh ============================*/ + +/*-<a href="qh-globa_r.htm#TOC" + >--------------------------------</a><a name="qh">-</a> + + qhT + All global variables for qhull are in qhT. It includes qhmemT, qhstatT, and rbox globals + + This version of Qhull is reentrant, but it is not thread-safe. + + Do not run separate threads on the same instance of qhT. + + QHULL_LIB_CHECK checks that a program and the corresponding + qhull library were built with the same type of header files. + + QHULL_LIB_TYPE is QHULL_NON_REENTRANT, QHULL_QH_POINTER, or QHULL_REENTRANT +*/ + +#define QHULL_NON_REENTRANT 0 +#define QHULL_QH_POINTER 1 +#define QHULL_REENTRANT 2 + +#define QHULL_LIB_TYPE QHULL_REENTRANT + +#define QHULL_LIB_CHECK qh_lib_check(QHULL_LIB_TYPE, sizeof(qhT), sizeof(vertexT), sizeof(ridgeT), sizeof(facetT), sizeof(setT), sizeof(qhmemT)); +#define QHULL_LIB_CHECK_RBOX qh_lib_check(QHULL_LIB_TYPE, sizeof(qhT), sizeof(vertexT), sizeof(ridgeT), sizeof(facetT), 0, 0); + +struct qhT { + +/*-<a href="qh-globa_r.htm#TOC" + >--------------------------------</a><a name="qh-const">-</a> + + qh constants + configuration flags and constants for Qhull + + notes: + The user configures Qhull by defining flags. They are + copied into qh by qh_setflags(). qh-quick_r.htm#options defines the flags. +*/ + boolT ALLpoints; /* true 'Qs' if search all points for initial simplex */ + boolT ALLOWshort; /* true 'Qa' allow input with fewer or more points than coordinates */ + boolT ALLOWwarning; /* true 'Qw' if allow option warnings */ + boolT ALLOWwide; /* true 'Q12' if allow wide facets and wide dupridges, c.f. qh_WIDEmaxoutside */ + boolT ANGLEmerge; /* true 'Q1' if sort potential merges by type/angle instead of type/distance */ + boolT APPROXhull; /* true 'Wn' if MINoutside set */ + realT MINoutside; /* Minimum distance for an outside point ('Wn' or 2*qh.MINvisible) */ + boolT ANNOTATEoutput; /* true 'Ta' if annotate output with message codes */ + boolT ATinfinity; /* true 'Qz' if point num_points-1 is "at-infinity" + for improving precision in Delaunay triangulations */ + boolT AVOIDold; /* true 'Q4' if avoid old->new merges */ + boolT BESToutside; /* true 'Qf' if partition points into best outsideset */ + boolT CDDinput; /* true 'Pc' if input uses CDD format (1.0/offset first) */ + boolT CDDoutput; /* true 'PC' if print normals in CDD format (offset first) */ + boolT CHECKduplicates; /* true 'Q15' if qh_maybe_duplicateridges after each qh_mergefacet */ + boolT CHECKfrequently; /* true 'Tc' if checking frequently */ + realT premerge_cos; /* 'A-n' cos_max when pre merging */ + realT postmerge_cos; /* 'An' cos_max when post merging */ + boolT DELAUNAY; /* true 'd' or 'v' if computing DELAUNAY triangulation */ + boolT DOintersections; /* true 'Gh' if print hyperplane intersections */ + int DROPdim; /* drops dim 'GDn' for 4-d -> 3-d output */ + boolT FLUSHprint; /* true 'Tf' if flush after qh_fprintf for segfaults */ + boolT FORCEoutput; /* true 'Po' if forcing output despite degeneracies */ + int GOODpoint; /* 'QGn' or 'QG-n' (n+1, n-1), good facet if visible from point n (or not) */ + pointT *GOODpointp; /* the actual point */ + boolT GOODthreshold; /* true 'Pd/PD' if qh.lower_threshold/upper_threshold defined + set if qh.UPPERdelaunay (qh_initbuild) + false if qh.SPLITthreshold */ + int GOODvertex; /* 'QVn' or 'QV-n' (n+1, n-1), good facet if vertex for point n (or not) */ + pointT *GOODvertexp; /* the actual point */ + boolT HALFspace; /* true 'Hn,n,n' if halfspace intersection */ + boolT ISqhullQh; /* Set by Qhull.cpp on initialization */ + int IStracing; /* 'Tn' trace execution, 0=none, 1=least, 4=most, -1=events */ + int KEEParea; /* 'PAn' number of largest facets to keep */ + boolT KEEPcoplanar; /* true 'Qc' if keeping nearest facet for coplanar points */ + boolT KEEPinside; /* true 'Qi' if keeping nearest facet for inside points + set automatically if 'd Qc' */ + int KEEPmerge; /* 'PMn' number of facets to keep with most merges */ + realT KEEPminArea; /* 'PFn' minimum facet area to keep */ + realT MAXcoplanar; /* 'Un' max distance below a facet to be coplanar*/ + int MAXwide; /* 'QWn' max ratio for wide facet, otherwise error unless Q12-allow-wide */ + boolT MERGEexact; /* true 'Qx' if exact merges (concave, degen, dupridge, flipped) + tested by qh_checkzero and qh_test_*_merge */ + boolT MERGEindependent; /* true if merging independent sets of coplanar facets. 'Q2' disables */ + boolT MERGING; /* true if exact-, pre- or post-merging, with angle and centrum tests */ + realT premerge_centrum; /* 'C-n' centrum_radius when pre merging. Default is round-off */ + realT postmerge_centrum; /* 'Cn' centrum_radius when post merging. Default is round-off */ + boolT MERGEpinched; /* true 'Q14' if merging pinched vertices due to dupridge */ + boolT MERGEvertices; /* true if merging redundant vertices, 'Q3' disables or qh.hull_dim > qh_DIMmergeVertex */ + realT MINvisible; /* 'Vn' min. distance for a facet to be visible */ + boolT NOnarrow; /* true 'Q10' if no special processing for narrow distributions */ + boolT NOnearinside; /* true 'Q8' if ignore near-inside points when partitioning, qh_check_points may fail */ + boolT NOpremerge; /* true 'Q0' if no defaults for C-0 or Qx */ + boolT ONLYgood; /* true 'Qg' if process points with good visible or horizon facets */ + boolT ONLYmax; /* true 'Qm' if only process points that increase max_outside */ + boolT PICKfurthest; /* true 'Q9' if process furthest of furthest points*/ + boolT POSTmerge; /* true if merging after buildhull ('Cn' or 'An') */ + boolT PREmerge; /* true if merging during buildhull ('C-n' or 'A-n') */ + /* NOTE: some of these names are similar to qh_PRINT names */ + boolT PRINTcentrums; /* true 'Gc' if printing centrums */ + boolT PRINTcoplanar; /* true 'Gp' if printing coplanar points */ + int PRINTdim; /* print dimension for Geomview output */ + boolT PRINTdots; /* true 'Ga' if printing all points as dots */ + boolT PRINTgood; /* true 'Pg' if printing good facets + PGood set if 'd', 'PAn', 'PFn', 'PMn', 'QGn', 'QG-n', 'QVn', or 'QV-n' */ + boolT PRINTinner; /* true 'Gi' if printing inner planes */ + boolT PRINTneighbors; /* true 'PG' if printing neighbors of good facets */ + boolT PRINTnoplanes; /* true 'Gn' if printing no planes */ + boolT PRINToptions1st; /* true 'FO' if printing options to stderr */ + boolT PRINTouter; /* true 'Go' if printing outer planes */ + boolT PRINTprecision; /* false 'Pp' if not reporting precision problems */ + qh_PRINT PRINTout[qh_PRINTEND]; /* list of output formats to print */ + boolT PRINTridges; /* true 'Gr' if print ridges */ + boolT PRINTspheres; /* true 'Gv' if print vertices as spheres */ + boolT PRINTstatistics; /* true 'Ts' if printing statistics to stderr */ + boolT PRINTsummary; /* true 's' if printing summary to stderr */ + boolT PRINTtransparent; /* true 'Gt' if print transparent outer ridges */ + boolT PROJECTdelaunay; /* true if DELAUNAY, no readpoints() and + need projectinput() for Delaunay in qh_init_B */ + int PROJECTinput; /* number of projected dimensions 'bn:0Bn:0' */ + boolT RANDOMdist; /* true 'Rn' if randomly change distplane and setfacetplane */ + realT RANDOMfactor; /* maximum random perturbation */ + realT RANDOMa; /* qh_randomfactor is randr * RANDOMa + RANDOMb */ + realT RANDOMb; + boolT RANDOMoutside; /* true 'Qr' if select a random outside point */ + int REPORTfreq; /* 'TFn' buildtracing reports every n facets */ + int REPORTfreq2; /* tracemerging reports every REPORTfreq/2 facets */ + int RERUN; /* 'TRn' rerun qhull n times (qh.build_cnt) */ + int ROTATErandom; /* 'QRn' n<-1 random seed, n==-1 time is seed, n==0 random rotation by time, n>0 rotate input */ + boolT SCALEinput; /* true 'Qbk' if scaling input */ + boolT SCALElast; /* true 'Qbb' if scale last coord to max prev coord */ + boolT SETroundoff; /* true 'En' if qh.DISTround is predefined */ + boolT SKIPcheckmax; /* true 'Q5' if skip qh_check_maxout, qh_check_points may fail */ + boolT SKIPconvex; /* true 'Q6' if skip convexity testing during pre-merge */ + boolT SPLITthresholds; /* true 'Pd/PD' if upper_/lower_threshold defines a region + else qh.GOODthresholds + set if qh.DELAUNAY (qh_initbuild) + used only for printing (!for qh.ONLYgood) */ + int STOPadd; /* 'TAn' 1+n for stop after adding n vertices */ + int STOPcone; /* 'TCn' 1+n for stopping after cone for point n */ + /* also used by qh_build_withresart for err exit*/ + int STOPpoint; /* 'TVn' 'TV-n' 1+n for stopping after/before(-) + adding point n */ + int TESTpoints; /* 'QTn' num of test points after qh.num_points. Test points always coplanar. */ + boolT TESTvneighbors; /* true 'Qv' if test vertex neighbors at end */ + int TRACElevel; /* 'Tn' conditional IStracing level */ + int TRACElastrun; /* qh.TRACElevel applies to last qh.RERUN */ + int TRACEpoint; /* 'TPn' start tracing when point n is a vertex, use qh_IDunknown (-1) after qh_buildhull and qh_postmerge */ + realT TRACEdist; /* 'TWn' start tracing when merge distance too big */ + int TRACEmerge; /* 'TMn' start tracing before this merge */ + boolT TRIangulate; /* true 'Qt' if triangulate non-simplicial facets */ + boolT TRInormals; /* true 'Q11' if triangulate duplicates ->normal and ->center (sets Qt) */ + boolT UPPERdelaunay; /* true 'Qu' if computing furthest-site Delaunay */ + boolT USEstdout; /* true 'Tz' if using stdout instead of stderr */ + boolT VERIFYoutput; /* true 'Tv' if verify output at end of qhull */ + boolT VIRTUALmemory; /* true 'Q7' if depth-first processing in buildhull */ + boolT VORONOI; /* true 'v' if computing Voronoi diagram, also sets qh.DELAUNAY */ + + /*--------input constants ---------*/ + realT AREAfactor; /* 1/(hull_dim-1)! for converting det's to area */ + boolT DOcheckmax; /* true if calling qh_check_maxout (!qh.SKIPcheckmax && qh.MERGING) */ + char *feasible_string; /* feasible point 'Hn,n,n' for halfspace intersection */ + coordT *feasible_point; /* as coordinates, both malloc'd */ + boolT GETarea; /* true 'Fa', 'FA', 'FS', 'PAn', 'PFn' if compute facet area/Voronoi volume in io_r.c */ + boolT KEEPnearinside; /* true if near-inside points in coplanarset */ + int hull_dim; /* dimension of hull, set by initbuffers */ + int input_dim; /* dimension of input, set by initbuffers */ + int num_points; /* number of input points */ + pointT *first_point; /* array of input points, see POINTSmalloc */ + boolT POINTSmalloc; /* true if qh.first_point/num_points allocated */ + pointT *input_points; /* copy of original qh.first_point for input points for qh_joggleinput */ + boolT input_malloc; /* true if qh.input_points malloc'd */ + char qhull_command[256];/* command line that invoked this program */ + int qhull_commandsiz2; /* size of qhull_command at qh_clear_outputflags */ + char rbox_command[256]; /* command line that produced the input points */ + char qhull_options[512];/* descriptive list of options */ + int qhull_optionlen; /* length of last line */ + int qhull_optionsiz; /* size of qhull_options at qh_build_withrestart */ + int qhull_optionsiz2; /* size of qhull_options at qh_clear_outputflags */ + int run_id; /* non-zero, random identifier for this instance of qhull */ + boolT VERTEXneighbors; /* true if maintaining vertex neighbors */ + boolT ZEROcentrum; /* true if 'C-0' or 'C-0 Qx' and not post-merging or 'A-n'. Sets ZEROall_ok */ + realT *upper_threshold; /* don't print if facet->normal[k]>=upper_threshold[k] + must set either GOODthreshold or SPLITthreshold + if qh.DELAUNAY, default is 0.0 for upper envelope (qh_initbuild) */ + realT *lower_threshold; /* don't print if facet->normal[k] <=lower_threshold[k] */ + realT *upper_bound; /* scale point[k] to new upper bound */ + realT *lower_bound; /* scale point[k] to new lower bound + project if both upper_ and lower_bound == 0 */ + +/*-<a href="qh-globa_r.htm#TOC" + >--------------------------------</a><a name="qh-prec">-</a> + + qh precision constants + precision constants for Qhull + + notes: + qh_detroundoff [geom2_r.c] computes the maximum roundoff error for distance + and other computations. It also sets default values for the + qh constants above. +*/ + realT ANGLEround; /* max round off error for angles */ + realT centrum_radius; /* max centrum radius for convexity ('Cn' + 2*qh.DISTround) */ + realT cos_max; /* max cosine for convexity (roundoff added) */ + realT DISTround; /* max round off error for distances, qh.SETroundoff ('En') overrides qh_distround */ + realT MAXabs_coord; /* max absolute coordinate */ + realT MAXlastcoord; /* max last coordinate for qh_scalelast */ + realT MAXoutside; /* max target for qh.max_outside/f.maxoutside, base for qh_RATIO... + recomputed at qh_addpoint, unrelated to qh_MAXoutside */ + realT MAXsumcoord; /* max sum of coordinates */ + realT MAXwidth; /* max rectilinear width of point coordinates */ + realT MINdenom_1; /* min. abs. value for 1/x */ + realT MINdenom; /* use divzero if denominator < MINdenom */ + realT MINdenom_1_2; /* min. abs. val for 1/x that allows normalization */ + realT MINdenom_2; /* use divzero if denominator < MINdenom_2 */ + realT MINlastcoord; /* min. last coordinate for qh_scalelast */ + realT *NEARzero; /* hull_dim array for near zero in gausselim */ + realT NEARinside; /* keep points for qh_check_maxout if close to facet */ + realT ONEmerge; /* max distance for merging simplicial facets */ + realT outside_err; /* application's epsilon for coplanar points + qh_check_bestdist() qh_check_points() reports error if point outside */ + realT WIDEfacet; /* size of wide facet for skipping ridge in + area computation and locking centrum */ + boolT NARROWhull; /* set in qh_initialhull if angle < qh_MAXnarrow */ + +/*-<a href="qh-globa_r.htm#TOC" + >--------------------------------</a><a name="qh-codetern">-</a> + + qh internal constants + internal constants for Qhull +*/ + char qhull[sizeof("qhull")]; /* "qhull" for checking ownership while debugging */ + jmp_buf errexit; /* exit label for qh_errexit, defined by setjmp() and NOerrexit */ + char jmpXtra[40]; /* extra bytes in case jmp_buf is defined wrong by compiler */ + jmp_buf restartexit; /* restart label for qh_errexit, defined by setjmp() and ALLOWrestart */ + char jmpXtra2[40]; /* extra bytes in case jmp_buf is defined wrong by compiler*/ + FILE * fin; /* pointer to input file, init by qh_initqhull_start2 */ + FILE * fout; /* pointer to output file */ + FILE * ferr; /* pointer to error file */ + pointT *interior_point; /* center point of the initial simplex*/ + int normal_size; /* size in bytes for facet normals and point coords */ + int center_size; /* size in bytes for Voronoi centers */ + int TEMPsize; /* size for small, temporary sets (in quick mem) */ + +/*-<a href="qh-globa_r.htm#TOC" + >--------------------------------</a><a name="qh-lists">-</a> + + qh facet and vertex lists + defines lists of facets, new facets, visible facets, vertices, and + new vertices. Includes counts, next ids, and trace ids. + see: + qh_resetlists() +*/ + facetT *facet_list; /* first facet */ + facetT *facet_tail; /* end of facet_list (dummy facet with id 0 and next==NULL) */ + facetT *facet_next; /* next facet for buildhull() + previous facets do not have outside sets + NARROWhull: previous facets may have coplanar outside sets for qh_outcoplanar */ + facetT *newfacet_list; /* list of new facets to end of facet_list + qh_postmerge sets newfacet_list to facet_list */ + facetT *visible_list; /* list of visible facets preceding newfacet_list, + end of visible list if !facet->visible, same as newfacet_list + qh_findhorizon sets visible_list at end of facet_list + qh_willdelete prepends to visible_list + qh_triangulate appends mirror facets to visible_list at end of facet_list + qh_postmerge sets visible_list to facet_list + qh_deletevisible deletes the visible facets */ + int num_visible; /* current number of visible facets */ + unsigned int tracefacet_id; /* set at init, then can print whenever */ + facetT *tracefacet; /* set in newfacet/mergefacet, undone in delfacet and qh_errexit */ + unsigned int traceridge_id; /* set at init, then can print whenever */ + ridgeT *traceridge; /* set in newridge, undone in delridge, errexit, errexit2, makenew_nonsimplicial, mergecycle_ridges */ + unsigned int tracevertex_id; /* set at buildtracing, can print whenever */ + vertexT *tracevertex; /* set in newvertex, undone in delvertex and qh_errexit */ + vertexT *vertex_list; /* list of all vertices, to vertex_tail */ + vertexT *vertex_tail; /* end of vertex_list (dummy vertex with ID 0, next NULL) */ + vertexT *newvertex_list; /* list of vertices in newfacet_list, to vertex_tail + all vertices have 'newfacet' set */ + int num_facets; /* number of facets in facet_list + includes visible faces (num_visible) */ + int num_vertices; /* number of vertices in facet_list */ + int num_outside; /* number of points in outsidesets (for tracing and RANDOMoutside) + includes coplanar outsideset points for NARROWhull/qh_outcoplanar() */ + int num_good; /* number of good facets (after qh_findgood_all or qh_markkeep) */ + unsigned int facet_id; /* ID of next, new facet from newfacet() */ + unsigned int ridge_id; /* ID of next, new ridge from newridge() */ + unsigned int vertex_id; /* ID of next, new vertex from newvertex() */ + unsigned int first_newfacet; /* ID of first_newfacet for qh_buildcone, or 0 if none */ + +/*-<a href="qh-globa_r.htm#TOC" + >--------------------------------</a><a name="qh-var">-</a> + + qh global variables + defines minimum and maximum distances, next visit ids, several flags, + and other global variables. + initialize in qh_initbuild or qh_maxmin if used in qh_buildhull +*/ + unsigned long hulltime; /* ignore time to set up input and randomize */ + /* use 'unsigned long' to avoid wrap-around errors */ + boolT ALLOWrestart; /* true if qh_joggle_restart can use qh.restartexit */ + int build_cnt; /* number of calls to qh_initbuild */ + qh_CENTER CENTERtype; /* current type of facet->center, qh_CENTER */ + int furthest_id; /* pointid of furthest point, for tracing */ + int last_errcode; /* last errcode from qh_fprintf, reset in qh_build_withrestart */ + facetT *GOODclosest; /* closest facet to GOODthreshold in qh_findgood */ + pointT *coplanar_apex; /* last apex declared a coplanar point by qh_getpinchedmerges, prevents infinite loop */ + boolT hasAreaVolume; /* true if totarea, totvol was defined by qh_getarea */ + boolT hasTriangulation; /* true if triangulation created by qh_triangulate */ + boolT isRenameVertex; /* true during qh_merge_pinchedvertices, disables duplicate ridge vertices in qh_checkfacet */ + realT JOGGLEmax; /* set 'QJn' if randomly joggle input. 'QJ'/'QJ0.0' sets default (qh_detjoggle) */ + boolT maxoutdone; /* set qh_check_maxout(), cleared by qh_addpoint() */ + realT max_outside; /* maximum distance from a point to a facet, + before roundoff, not simplicial vertices + actual outer plane is +DISTround and + computed outer plane is +2*DISTround */ + realT max_vertex; /* maximum distance (>0) from vertex to a facet, + before roundoff, due to a merge */ + realT min_vertex; /* minimum distance (<0) from vertex to a facet, + before roundoff, due to a merge + if qh.JOGGLEmax, qh_makenewplanes sets it + recomputed if qh.DOcheckmax, default -qh.DISTround */ + boolT NEWfacets; /* true while visible facets invalid due to new or merge + from qh_makecone/qh_attachnewfacets to qh_resetlists */ + boolT NEWtentative; /* true while new facets are tentative due to !qh.IGNOREpinched or qh.ONLYgood + from qh_makecone to qh_attachnewfacets */ + boolT findbestnew; /* true if partitioning calls qh_findbestnew */ + boolT findbest_notsharp; /* true if new facets are at least 90 degrees */ + boolT NOerrexit; /* true if qh.errexit is not available, cleared after setjmp. See qh.ERREXITcalled */ + realT PRINTcradius; /* radius for printing centrums */ + realT PRINTradius; /* radius for printing vertex spheres and points */ + boolT POSTmerging; /* true when post merging */ + int printoutvar; /* temporary variable for qh_printbegin, etc. */ + int printoutnum; /* number of facets printed */ + unsigned int repart_facetid; /* previous facetid to prevent recursive qh_partitioncoplanar+qh_partitionpoint */ + int retry_addpoint; /* number of retries of qh_addpoint due to merging pinched vertices */ + boolT QHULLfinished; /* True after qhull() is finished */ + realT totarea; /* 'FA': total facet area computed by qh_getarea, hasAreaVolume */ + realT totvol; /* 'FA': total volume computed by qh_getarea, hasAreaVolume */ + unsigned int visit_id; /* unique ID for searching neighborhoods, */ + unsigned int vertex_visit; /* unique ID for searching vertices, reset with qh_buildtracing */ + boolT WAScoplanar; /* True if qh_partitioncoplanar (qh_check_maxout) */ + boolT ZEROall_ok; /* True if qh_checkzero always succeeds */ + +/*-<a href="qh-globa_r.htm#TOC" + >--------------------------------</a><a name="qh-set">-</a> + + qh global sets + defines sets for merging, initial simplex, hashing, extra input points, + and deleted vertices +*/ + setT *facet_mergeset; /* temporary set of merges to be done */ + setT *degen_mergeset; /* temporary set of degenerate and redundant merges */ + setT *vertex_mergeset; /* temporary set of vertex merges */ + setT *hash_table; /* hash table for matching ridges in qh_matchfacets + size is setsize() */ + setT *other_points; /* additional points */ + setT *del_vertices; /* vertices to partition and delete with visible + facets. v.deleted is set for checkfacet */ + +/*-<a href="qh-globa_r.htm#TOC" + >--------------------------------</a><a name="qh-buf">-</a> + + qh global buffers + defines buffers for maxtrix operations, input, and error messages +*/ + coordT *gm_matrix; /* (dim+1)Xdim matrix for geom_r.c */ + coordT **gm_row; /* array of gm_matrix rows */ + char* line; /* malloc'd input line of maxline+1 chars */ + int maxline; + coordT *half_space; /* malloc'd input array for halfspace (qh.normal_size+coordT) */ + coordT *temp_malloc; /* malloc'd input array for points */ + +/*-<a href="qh-globa_r.htm#TOC" + >--------------------------------</a><a name="qh-static">-</a> + + qh static variables + defines static variables for individual functions + + notes: + do not use 'static' within a function. Multiple instances of qhull + may exist. + + do not assume zero initialization, 'QPn' may cause a restart +*/ + boolT ERREXITcalled; /* true during qh_errexit (prevents duplicate calls). see qh.NOerrexit */ + boolT firstcentrum; /* for qh_printcentrum */ + boolT old_randomdist; /* save RANDOMdist flag during io, tracing, or statistics */ + setT *coplanarfacetset; /* set of coplanar facets for searching qh_findbesthorizon() */ + realT last_low; /* qh_scalelast parameters for qh_setdelaunay */ + realT last_high; + realT last_newhigh; + realT lastcpu; /* for qh_buildtracing */ + int lastfacets; /* last qh.num_facets */ + int lastmerges; /* last zzval_(Ztotmerge) */ + int lastplanes; /* last zzval_(Zsetplane) */ + int lastdist; /* last zzval_(Zdistplane) */ + unsigned int lastreport; /* last qh.facet_id */ + int mergereport; /* for qh_tracemerging */ + setT *old_tempstack; /* for saving qh->qhmem.tempstack in save_qhull */ + int ridgeoutnum; /* number of ridges for 4OFF output (qh_printbegin,etc) */ + +/*-<a href="qh-globa_r.htm#TOC" + >--------------------------------</a><a name="qh-const">-</a> + + qh memory management, rbox globals, and statistics + + Replaces global data structures defined for libqhull +*/ + int last_random; /* Last random number from qh_rand (random_r.c) */ + jmp_buf rbox_errexit; /* errexit from rboxlib_r.c, defined by qh_rboxpoints() only */ + char jmpXtra3[40]; /* extra bytes in case jmp_buf is defined wrong by compiler */ + int rbox_isinteger; + double rbox_out_offset; + void * cpp_object; /* C++ pointer. Currently used by RboxPoints.qh_fprintf_rbox */ + void * cpp_other; /* C++ pointer. Reserved for other users */ + void * cpp_user; /* C++ pointer. Currently used by QhullUser.qh_fprintf */ + + /* Last, otherwise zero'd by qh_initqhull_start2 (global_r.c */ + qhmemT qhmem; /* Qhull managed memory (mem_r.h) */ + /* After qhmem because its size depends on the number of statistics */ + qhstatT qhstat; /* Qhull statistics (stat_r.h) */ +}; + +/*=========== -macros- =========================*/ + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="otherfacet_">-</a> + + otherfacet_(ridge, facet) + return neighboring facet for a ridge in facet +*/ +#define otherfacet_(ridge, facet) \ + (((ridge)->top == (facet)) ? (ridge)->bottom : (ridge)->top) + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="getid_">-</a> + + getid_(p) + return int ID for facet, ridge, or vertex + return qh_IDunknown(-1) if NULL + return 0 if facet_tail or vertex_tail +*/ +#define getid_(p) ((p) ? (int)((p)->id) : qh_IDunknown) + +/*============== FORALL macros ===================*/ + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="FORALLfacets">-</a> + + FORALLfacets { ... } + assign 'facet' to each facet in qh.facet_list + + notes: + uses 'facetT *facet;' + assumes last facet is a sentinel + assumes qh defined + + see: + FORALLfacet_( facetlist ) +*/ +#define FORALLfacets for (facet=qh->facet_list;facet && facet->next;facet=facet->next) + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="FORALLpoints">-</a> + + FORALLpoints { ... } + assign 'point' to each point in qh.first_point, qh.num_points + + notes: + assumes qh defined + + declare: + coordT *point, *pointtemp; +*/ +#define FORALLpoints FORALLpoint_(qh, qh->first_point, qh->num_points) + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="FORALLpoint_">-</a> + + FORALLpoint_(qh, points, num) { ... } + assign 'point' to each point in points array of num points + + declare: + coordT *point, *pointtemp; +*/ +#define FORALLpoint_(qh, points, num) for (point=(points), \ + pointtemp= (points)+qh->hull_dim*(num); point < pointtemp; point += qh->hull_dim) + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="FORALLvertices">-</a> + + FORALLvertices { ... } + assign 'vertex' to each vertex in qh.vertex_list + + declare: + vertexT *vertex; + + notes: + assumes qh.vertex_list terminated by NULL or a sentinel (v.next==NULL) + assumes qh defined +*/ +#define FORALLvertices for (vertex=qh->vertex_list;vertex && vertex->next;vertex= vertex->next) + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="FOREACHfacet_">-</a> + + FOREACHfacet_( facets ) { ... } + assign 'facet' to each facet in facets + + declare: + facetT *facet, **facetp; + + notes: + assumes set is not modified + + see: + <a href="qset_r.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHfacet_(facets) FOREACHsetelement_(facetT, facets, facet) + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="FOREACHneighbor_">-</a> + + FOREACHneighbor_( facet ) { ... } + assign 'neighbor' to each neighbor in facet->neighbors + + FOREACHneighbor_( vertex ) { ... } + assign 'neighbor' to each neighbor in vertex->neighbors + + declare: + facetT *neighbor, **neighborp; + + notes: + assumes set is not modified + + see: + <a href="qset_r.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHneighbor_(facet) FOREACHsetelement_(facetT, facet->neighbors, neighbor) + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="FOREACHpoint_">-</a> + + FOREACHpoint_( points ) { ... } + assign 'point' to each point in points set + + declare: + pointT *point, **pointp; + + notes: + assumes set is not modified + + see: + <a href="qset_r.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHpoint_(points) FOREACHsetelement_(pointT, points, point) + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="FOREACHridge_">-</a> + + FOREACHridge_( ridges ) { ... } + assign 'ridge' to each ridge in ridges set + + declare: + ridgeT *ridge, **ridgep; + + notes: + assumes set is not modified + + see: + <a href="qset_r.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHridge_(ridges) FOREACHsetelement_(ridgeT, ridges, ridge) + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="FOREACHvertex_">-</a> + + FOREACHvertex_( vertices ) { ... } + assign 'vertex' to each vertex in vertices set + + declare: + vertexT *vertex, **vertexp; + + notes: + assumes set is not modified + + see: + <a href="qset_r.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHvertex_(vertices) FOREACHsetelement_(vertexT, vertices,vertex) + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="FOREACHfacet_i_">-</a> + + FOREACHfacet_i_(qh, facets ) { ... } + assign 'facet' and 'facet_i' for each facet in facets set + + declare: + facetT *facet; + int facet_n, facet_i; + + see: + <a href="qset_r.h#FOREACHsetelement_i_">FOREACHsetelement_i_</a> +*/ +#define FOREACHfacet_i_(qh, facets) FOREACHsetelement_i_(qh, facetT, facets, facet) + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="FOREACHneighbor_i_">-</a> + + FOREACHneighbor_i_(qh, facet ) { ... } + assign 'neighbor' and 'neighbor_i' for each neighbor in facet->neighbors + + declare: + facetT *neighbor; + int neighbor_n, neighbor_i; + + notes: + see <a href="qset_r.h#FOREACHsetelement_i_">FOREACHsetelement_i_</a> + for facet neighbors of vertex, need to define a new macro +*/ +#define FOREACHneighbor_i_(qh, facet) FOREACHsetelement_i_(qh, facetT, facet->neighbors, neighbor) + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="FOREACHpoint_i_">-</a> + + FOREACHpoint_i_(qh, points ) { ... } + assign 'point' and 'point_i' for each point in points set + + declare: + pointT *point; + int point_n, point_i; + + see: + <a href="qset_r.h#FOREACHsetelement_i_">FOREACHsetelement_i_</a> +*/ +#define FOREACHpoint_i_(qh, points) FOREACHsetelement_i_(qh, pointT, points, point) + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="FOREACHridge_i_">-</a> + + FOREACHridge_i_(qh, ridges ) { ... } + assign 'ridge' and 'ridge_i' for each ridge in ridges set + + declare: + ridgeT *ridge; + int ridge_n, ridge_i; + + see: + <a href="qset_r.h#FOREACHsetelement_i_">FOREACHsetelement_i_</a> +*/ +#define FOREACHridge_i_(qh, ridges) FOREACHsetelement_i_(qh, ridgeT, ridges, ridge) + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="FOREACHvertex_i_">-</a> + + FOREACHvertex_i_(qh, vertices ) { ... } + assign 'vertex' and 'vertex_i' for each vertex in vertices set + + declare: + vertexT *vertex; + int vertex_n, vertex_i; + + see: + <a href="qset_r.h#FOREACHsetelement_i_">FOREACHsetelement_i_</a> +*/ +#define FOREACHvertex_i_(qh, vertices) FOREACHsetelement_i_(qh, vertexT, vertices, vertex) + +#ifdef __cplusplus +extern "C" { +#endif + +/********* -libqhull_r.c prototypes (duplicated from qhull_ra.h) **********************/ + +void qh_qhull(qhT *qh); +boolT qh_addpoint(qhT *qh, pointT *furthest, facetT *facet, boolT checkdist); +void qh_errexit2(qhT *qh, int exitcode, facetT *facet, facetT *otherfacet); +void qh_printsummary(qhT *qh, FILE *fp); + +/********* -user_r.c prototypes (alphabetical) **********************/ + +void qh_errexit(qhT *qh, int exitcode, facetT *facet, ridgeT *ridge); +void qh_errprint(qhT *qh, const char* string, facetT *atfacet, facetT *otherfacet, ridgeT *atridge, vertexT *atvertex); +int qh_new_qhull(qhT *qh, int dim, int numpoints, coordT *points, boolT ismalloc, + char *qhull_cmd, FILE *outfile, FILE *errfile); +int qh_new_qhull_feaspoint(qhT *qh, int dim, int numpoints, coordT *points, boolT ismalloc, + char *qhull_cmd, FILE *outfile, FILE *errfile, coordT* feaspoint); +void qh_printfacetlist(qhT *qh, facetT *facetlist, setT *facets, boolT printall); +void qh_printhelp_degenerate(qhT *qh, FILE *fp); +void qh_printhelp_internal(qhT *qh, FILE *fp); +void qh_printhelp_narrowhull(qhT *qh, FILE *fp, realT minangle); +void qh_printhelp_singular(qhT *qh, FILE *fp); +void qh_printhelp_topology(qhT *qh, FILE *fp); +void qh_printhelp_wide(qhT *qh, FILE *fp); +void qh_user_memsizes(qhT *qh); + +/********* -usermem_r.c prototypes (alphabetical) **********************/ +void qh_exit(int exitcode); +void qh_fprintf_stderr(int msgcode, const char *fmt, ... ); +void qh_free(void *mem); +void *qh_malloc(size_t size); + +/********* -userprintf_r.c and userprintf_rbox_r.c prototypes **********************/ +void qh_fprintf(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... ); +void qh_fprintf_rbox(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... ); + +/***** -geom_r.c/geom2_r.c/random_r.c prototypes (duplicated from geom_r.h, random_r.h) ****************/ + +facetT *qh_findbest(qhT *qh, pointT *point, facetT *startfacet, + boolT bestoutside, boolT newfacets, boolT noupper, + realT *dist, boolT *isoutside, int *numpart); +facetT *qh_findbestnew(qhT *qh, pointT *point, facetT *startfacet, + realT *dist, boolT bestoutside, boolT *isoutside, int *numpart); +boolT qh_gram_schmidt(qhT *qh, int dim, realT **rows); +void qh_outerinner(qhT *qh, facetT *facet, realT *outerplane, realT *innerplane); +void qh_printsummary(qhT *qh, FILE *fp); +void qh_projectinput(qhT *qh); +void qh_randommatrix(qhT *qh, realT *buffer, int dim, realT **row); +void qh_rotateinput(qhT *qh, realT **rows); +void qh_scaleinput(qhT *qh); +void qh_setdelaunay(qhT *qh, int dim, int count, pointT *points); +coordT *qh_sethalfspace_all(qhT *qh, int dim, int count, coordT *halfspaces, pointT *feasible); + +/***** -global_r.c prototypes (alphabetical) ***********************/ + +unsigned long qh_clock(qhT *qh); +void qh_checkflags(qhT *qh, char *command, char *hiddenflags); +void qh_clear_outputflags(qhT *qh); +void qh_freebuffers(qhT *qh); +void qh_freeqhull(qhT *qh, boolT allmem); +void qh_init_A(qhT *qh, FILE *infile, FILE *outfile, FILE *errfile, int argc, char *argv[]); +void qh_init_B(qhT *qh, coordT *points, int numpoints, int dim, boolT ismalloc); +void qh_init_qhull_command(qhT *qh, int argc, char *argv[]); +void qh_initbuffers(qhT *qh, coordT *points, int numpoints, int dim, boolT ismalloc); +void qh_initflags(qhT *qh, char *command); +void qh_initqhull_buffers(qhT *qh); +void qh_initqhull_globals(qhT *qh, coordT *points, int numpoints, int dim, boolT ismalloc); +void qh_initqhull_mem(qhT *qh); +void qh_initqhull_outputflags(qhT *qh); +void qh_initqhull_start(qhT *qh, FILE *infile, FILE *outfile, FILE *errfile); +void qh_initqhull_start2(qhT *qh, FILE *infile, FILE *outfile, FILE *errfile); +void qh_initthresholds(qhT *qh, char *command); +void qh_lib_check(int qhullLibraryType, int qhTsize, int vertexTsize, int ridgeTsize, int facetTsize, int setTsize, int qhmemTsize); +void qh_option(qhT *qh, const char *option, int *i, realT *r); +void qh_zero(qhT *qh, FILE *errfile); + +/***** -io_r.c prototypes (duplicated from io_r.h) ***********************/ + +void qh_dfacet(qhT *qh, unsigned int id); +void qh_dvertex(qhT *qh, unsigned int id); +void qh_printneighborhood(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetA, facetT *facetB, boolT printall); +void qh_produce_output(qhT *qh); +coordT *qh_readpoints(qhT *qh, int *numpoints, int *dimension, boolT *ismalloc); + + +/********* -mem_r.c prototypes (duplicated from mem_r.h) **********************/ + +void qh_meminit(qhT *qh, FILE *ferr); +void qh_memfreeshort(qhT *qh, int *curlong, int *totlong); + +/********* -poly_r.c/poly2_r.c prototypes (duplicated from poly_r.h) **********************/ + +void qh_check_output(qhT *qh); +void qh_check_points(qhT *qh); +setT *qh_facetvertices(qhT *qh, facetT *facetlist, setT *facets, boolT allfacets); +facetT *qh_findbestfacet(qhT *qh, pointT *point, boolT bestoutside, + realT *bestdist, boolT *isoutside); +vertexT *qh_nearvertex(qhT *qh, facetT *facet, pointT *point, realT *bestdistp); +pointT *qh_point(qhT *qh, int id); +setT *qh_pointfacet(qhT *qh /* qh.facet_list */); +int qh_pointid(qhT *qh, pointT *point); +setT *qh_pointvertex(qhT *qh /* qh.facet_list */); +void qh_setvoronoi_all(qhT *qh); +void qh_triangulate(qhT *qh /* qh.facet_list */); + +/********* -rboxlib_r.c prototypes **********************/ +int qh_rboxpoints(qhT *qh, char* rbox_command); +void qh_errexit_rbox(qhT *qh, int exitcode); + +/********* -stat_r.c prototypes (duplicated from stat_r.h) **********************/ + +void qh_collectstatistics(qhT *qh); +void qh_printallstatistics(qhT *qh, FILE *fp, const char *string); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* qhDEFlibqhull */ diff --git a/contrib/libs/qhull/libqhull_r/mem_r.c b/contrib/libs/qhull/libqhull_r/mem_r.c new file mode 100644 index 0000000000..7d5509eb4f --- /dev/null +++ b/contrib/libs/qhull/libqhull_r/mem_r.c @@ -0,0 +1,566 @@ +/*<html><pre> -<a href="qh-mem_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + mem_r.c + memory management routines for qhull + + See libqhull/mem.c for a standalone program. + + To initialize memory: + + qh_meminit(qh, stderr); + qh_meminitbuffers(qh, qh->IStracing, qh_MEMalign, 7, qh_MEMbufsize,qh_MEMinitbuf); + qh_memsize(qh, (int)sizeof(facetT)); + qh_memsize(qh, (int)sizeof(facetT)); + ... + qh_memsetup(qh); + + To free up all memory buffers: + qh_memfreeshort(qh, &curlong, &totlong); + + if qh_NOmem, + malloc/free is used instead of mem_r.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_r.htm and mem_r.h + global_r.c (qh_initbuffers) for an example of using mem_r.c + + Copyright (c) 1993-2020 The Geometry Center. + $Id: //main/2019/qhull/src/libqhull_r/mem_r.c#7 $$Change: 2953 $ + $DateTime: 2020/05/21 22:05:32 $$Author: bbarber $ +*/ + +#include "libqhull_r.h" /* includes user_r.h and mem_r.h */ + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +#ifndef qh_NOmem + +/*============= internal functions ==============*/ + +static int qh_intcompare(const void *i, const void *j); + +/*========== functions in alphabetical order ======== */ + +/*-<a href="qh-mem_r.htm#TOC" + >-------------------------------</a><a name="intcompare">-</a> + + qh_intcompare( i, j ) + used by qsort and bsearch to compare two integers +*/ +static int qh_intcompare(const void *i, const void *j) { + return(*((const int *)i) - *((const int *)j)); +} /* intcompare */ + + +/*-<a href="qh-mem_r.htm#TOC" + >--------------------------------</a><a name="memalloc">-</a> + + qh_memalloc(qh, insize ) + returns object of insize bytes + qhmem is the global memory structure + + returns: + pointer to allocated memory + errors if insufficient memory + + notes: + use explicit type conversion to avoid type warnings on some compilers + actual object may be larger than insize + use qh_memalloc_() for inline code for quick allocations + logs allocations if 'T5' + caller is responsible for freeing the memory. + short memory is freed on shutdown by qh_memfreeshort unless qh_NOmem + + design: + if size < qh->qhmem.LASTsize + if qh->qhmem.freelists[size] non-empty + return first object on freelist + else + round up request to size of qh->qhmem.freelists[size] + allocate new allocation buffer if necessary + allocate object from allocation buffer + else + allocate object with qh_malloc() in user_r.c +*/ +void *qh_memalloc(qhT *qh, int insize) { + void **freelistp, *newbuffer; + int idx, size, n; + int outsize, bufsize; + void *object; + + if (insize<0) { + qh_fprintf(qh, qh->qhmem.ferr, 6235, "qhull error (qh_memalloc): negative request size (%d). Did int overflow due to high-D?\n", insize); /* WARN64 */ + qh_errexit(qh, qhmem_ERRmem, NULL, NULL); + } + if (insize>=0 && insize <= qh->qhmem.LASTsize) { + idx= qh->qhmem.indextable[insize]; + outsize= qh->qhmem.sizetable[idx]; + qh->qhmem.totshort += outsize; + freelistp= qh->qhmem.freelists+idx; + if ((object= *freelistp)) { + qh->qhmem.cntquick++; + qh->qhmem.totfree -= outsize; + *freelistp= *((void **)*freelistp); /* replace freelist with next object */ +#ifdef qh_TRACEshort + n= qh->qhmem.cntshort+qh->qhmem.cntquick+qh->qhmem.freeshort; + if (qh->qhmem.IStracing >= 5) + qh_fprintf(qh, qh->qhmem.ferr, 8141, "qh_mem %p n %8d alloc quick: %d bytes (tot %d cnt %d)\n", object, n, outsize, qh->qhmem.totshort, qh->qhmem.cntshort+qh->qhmem.cntquick-qh->qhmem.freeshort); +#endif + return(object); + }else { + qh->qhmem.cntshort++; + if (outsize > qh->qhmem.freesize) { + qh->qhmem.totdropped += qh->qhmem.freesize; + if (!qh->qhmem.curbuffer) + bufsize= qh->qhmem.BUFinit; + else + bufsize= qh->qhmem.BUFsize; + if (!(newbuffer= qh_malloc((size_t)bufsize))) { + qh_fprintf(qh, qh->qhmem.ferr, 6080, "qhull error (qh_memalloc): insufficient memory to allocate short memory buffer (%d bytes)\n", bufsize); + qh_errexit(qh, qhmem_ERRmem, NULL, NULL); + } + *((void **)newbuffer)= qh->qhmem.curbuffer; /* prepend newbuffer to curbuffer + list. newbuffer!=0 by QH6080 */ + qh->qhmem.curbuffer= newbuffer; + size= ((int)sizeof(void **) + qh->qhmem.ALIGNmask) & ~qh->qhmem.ALIGNmask; + qh->qhmem.freemem= (void *)((char *)newbuffer+size); + qh->qhmem.freesize= bufsize - size; + qh->qhmem.totbuffer += bufsize - size; /* easier to check */ + /* Periodically test totbuffer. It matches at beginning and exit of every call */ + n= qh->qhmem.totshort + qh->qhmem.totfree + qh->qhmem.totdropped + qh->qhmem.freesize - outsize; + if (qh->qhmem.totbuffer != n) { + qh_fprintf(qh, qh->qhmem.ferr, 6212, "qhull internal error (qh_memalloc): short totbuffer %d != totshort+totfree... %d\n", qh->qhmem.totbuffer, n); + qh_errexit(qh, qhmem_ERRmem, NULL, NULL); + } + } + object= qh->qhmem.freemem; + qh->qhmem.freemem= (void *)((char *)qh->qhmem.freemem + outsize); + qh->qhmem.freesize -= outsize; + qh->qhmem.totunused += outsize - insize; +#ifdef qh_TRACEshort + n= qh->qhmem.cntshort+qh->qhmem.cntquick+qh->qhmem.freeshort; + if (qh->qhmem.IStracing >= 5) + qh_fprintf(qh, qh->qhmem.ferr, 8140, "qh_mem %p n %8d alloc short: %d bytes (tot %d cnt %d)\n", object, n, outsize, qh->qhmem.totshort, qh->qhmem.cntshort+qh->qhmem.cntquick-qh->qhmem.freeshort); +#endif + return object; + } + }else { /* long allocation */ + if (!qh->qhmem.indextable) { + qh_fprintf(qh, qh->qhmem.ferr, 6081, "qhull internal error (qh_memalloc): qhmem has not been initialized.\n"); + qh_errexit(qh, qhmem_ERRqhull, NULL, NULL); + } + outsize= insize; + qh->qhmem.cntlong++; + qh->qhmem.totlong += outsize; + if (qh->qhmem.maxlong < qh->qhmem.totlong) + qh->qhmem.maxlong= qh->qhmem.totlong; + if (!(object= qh_malloc((size_t)outsize))) { + qh_fprintf(qh, qh->qhmem.ferr, 6082, "qhull error (qh_memalloc): insufficient memory to allocate %d bytes\n", outsize); + qh_errexit(qh, qhmem_ERRmem, NULL, NULL); + } + if (qh->qhmem.IStracing >= 5) + qh_fprintf(qh, qh->qhmem.ferr, 8057, "qh_mem %p n %8d alloc long: %d bytes (tot %d cnt %d)\n", object, qh->qhmem.cntlong+qh->qhmem.freelong, outsize, qh->qhmem.totlong, qh->qhmem.cntlong-qh->qhmem.freelong); + } + return(object); +} /* memalloc */ + + +/*-<a href="qh-mem_r.htm#TOC" + >--------------------------------</a><a name="memcheck">-</a> + + qh_memcheck(qh) +*/ +void qh_memcheck(qhT *qh) { + int i, count, totfree= 0; + void *object; + + if (!qh) { + qh_fprintf_stderr(6243, "qhull internal error (qh_memcheck): qh is 0. It does not point to a qhT\n"); + qh_exit(qhmem_ERRqhull); /* can not use qh_errexit() */ + } + if (qh->qhmem.ferr == 0 || qh->qhmem.IStracing < 0 || qh->qhmem.IStracing > 10 || (((qh->qhmem.ALIGNmask+1) & qh->qhmem.ALIGNmask) != 0)) { + qh_fprintf_stderr(6244, "qhull internal error (qh_memcheck): either qh->qhmem is overwritten or qh->qhmem is not initialized. Call qh_meminit or qh_new_qhull before calling qh_mem routines. ferr 0x%x, IsTracing %d, ALIGNmask 0x%x\n", + qh->qhmem.ferr, qh->qhmem.IStracing, qh->qhmem.ALIGNmask); + qh_exit(qhmem_ERRqhull); /* can not use qh_errexit() */ + } + if (qh->qhmem.IStracing != 0) + qh_fprintf(qh, qh->qhmem.ferr, 8143, "qh_memcheck: check size of freelists on qh->qhmem\nqh_memcheck: A segmentation fault indicates an overwrite of qh->qhmem\n"); + for (i=0; i < qh->qhmem.TABLEsize; i++) { + count=0; + for (object= qh->qhmem.freelists[i]; object; object= *((void **)object)) + count++; + totfree += qh->qhmem.sizetable[i] * count; + } + if (totfree != qh->qhmem.totfree) { + qh_fprintf(qh, qh->qhmem.ferr, 6211, "qhull internal error (qh_memcheck): totfree %d not equal to freelist total %d\n", qh->qhmem.totfree, totfree); + qh_errexit(qh, qhmem_ERRqhull, NULL, NULL); + } + if (qh->qhmem.IStracing != 0) + qh_fprintf(qh, qh->qhmem.ferr, 8144, "qh_memcheck: total size of freelists totfree is the same as qh->qhmem.totfree\n", totfree); +} /* memcheck */ + +/*-<a href="qh-mem_r.htm#TOC" + >--------------------------------</a><a name="memfree">-</a> + + qh_memfree(qh, object, insize ) + free up an object of size bytes + size is insize from qh_memalloc + + notes: + object may be NULL + type checking warns if using (void **)object + use qh_memfree_() for quick free's of small objects + + design: + if size <= qh->qhmem.LASTsize + append object to corresponding freelist + else + call qh_free(object) +*/ +void qh_memfree(qhT *qh, void *object, int insize) { + void **freelistp; + int idx, outsize; + + if (!object) + return; + if (insize <= qh->qhmem.LASTsize) { + qh->qhmem.freeshort++; + idx= qh->qhmem.indextable[insize]; + outsize= qh->qhmem.sizetable[idx]; + qh->qhmem.totfree += outsize; + qh->qhmem.totshort -= outsize; + freelistp= qh->qhmem.freelists + idx; + *((void **)object)= *freelistp; + *freelistp= object; +#ifdef qh_TRACEshort + idx= qh->qhmem.cntshort+qh->qhmem.cntquick+qh->qhmem.freeshort; + if (qh->qhmem.IStracing >= 5) + qh_fprintf(qh, qh->qhmem.ferr, 8142, "qh_mem %p n %8d free short: %d bytes (tot %d cnt %d)\n", object, idx, outsize, qh->qhmem.totshort, qh->qhmem.cntshort+qh->qhmem.cntquick-qh->qhmem.freeshort); +#endif + }else { + qh->qhmem.freelong++; + qh->qhmem.totlong -= insize; + if (qh->qhmem.IStracing >= 5) + qh_fprintf(qh, qh->qhmem.ferr, 8058, "qh_mem %p n %8d free long: %d bytes (tot %d cnt %d)\n", object, qh->qhmem.cntlong+qh->qhmem.freelong, insize, qh->qhmem.totlong, qh->qhmem.cntlong-qh->qhmem.freelong); + qh_free(object); + } +} /* memfree */ + + +/*-<a href="qh-mem_r.htm#TOC" + >-------------------------------</a><a name="memfreeshort">-</a> + + qh_memfreeshort(qh, curlong, totlong ) + frees up all short and qhmem memory allocations + + returns: + number and size of current long allocations + + notes: + if qh_NOmem (qh_malloc() for all allocations), + short objects (e.g., facetT) are not recovered. + use qh_freeqhull(qh, qh_ALL) instead. + + see: + qh_freeqhull(qh, allMem) + qh_memtotal(qh, curlong, totlong, curshort, totshort, maxlong, totbuffer); +*/ +void qh_memfreeshort(qhT *qh, int *curlong, int *totlong) { + void *buffer, *nextbuffer; + FILE *ferr; + + *curlong= qh->qhmem.cntlong - qh->qhmem.freelong; + *totlong= qh->qhmem.totlong; + for (buffer=qh->qhmem.curbuffer; buffer; buffer= nextbuffer) { + nextbuffer= *((void **) buffer); + qh_free(buffer); + } + qh->qhmem.curbuffer= NULL; + if (qh->qhmem.LASTsize) { + qh_free(qh->qhmem.indextable); + qh_free(qh->qhmem.freelists); + qh_free(qh->qhmem.sizetable); + } + ferr= qh->qhmem.ferr; + memset((char *)&qh->qhmem, 0, sizeof(qh->qhmem)); /* every field is 0, FALSE, NULL */ + qh->qhmem.ferr= ferr; +} /* memfreeshort */ + + +/*-<a href="qh-mem_r.htm#TOC" + >--------------------------------</a><a name="meminit">-</a> + + qh_meminit(qh, ferr ) + initialize qhmem and test sizeof(void *) + Does not throw errors. qh_exit on failure +*/ +void qh_meminit(qhT *qh, FILE *ferr) { + + memset((char *)&qh->qhmem, 0, sizeof(qh->qhmem)); /* every field is 0, FALSE, NULL */ + if (ferr) + qh->qhmem.ferr= ferr; + else + qh->qhmem.ferr= stderr; + if (sizeof(void *) < sizeof(int)) { + qh_fprintf(qh, qh->qhmem.ferr, 6083, "qhull internal error (qh_meminit): sizeof(void *) %d < sizeof(int) %d. qset_r.c will not work\n", (int)sizeof(void*), (int)sizeof(int)); + qh_exit(qhmem_ERRqhull); /* can not use qh_errexit() */ + } + if (sizeof(void *) > sizeof(ptr_intT)) { + qh_fprintf(qh, qh->qhmem.ferr, 6084, "qhull internal error (qh_meminit): sizeof(void *) %d > sizeof(ptr_intT) %d. Change ptr_intT in mem_r.h to 'long long'\n", (int)sizeof(void*), (int)sizeof(ptr_intT)); + qh_exit(qhmem_ERRqhull); /* can not use qh_errexit() */ + } + qh_memcheck(qh); +} /* meminit */ + +/*-<a href="qh-mem_r.htm#TOC" + >-------------------------------</a><a name="meminitbuffers">-</a> + + qh_meminitbuffers(qh, tracelevel, alignment, numsizes, bufsize, bufinit ) + initialize qhmem + if tracelevel >= 5, trace memory allocations + alignment= desired address alignment for memory allocations + numsizes= number of freelists + bufsize= size of additional memory buffers for short allocations + bufinit= size of initial memory buffer for short allocations +*/ +void qh_meminitbuffers(qhT *qh, int tracelevel, int alignment, int numsizes, int bufsize, int bufinit) { + + qh->qhmem.IStracing= tracelevel; + qh->qhmem.NUMsizes= numsizes; + qh->qhmem.BUFsize= bufsize; + qh->qhmem.BUFinit= bufinit; + qh->qhmem.ALIGNmask= alignment-1; + if (qh->qhmem.ALIGNmask & ~qh->qhmem.ALIGNmask) { + qh_fprintf(qh, qh->qhmem.ferr, 6085, "qhull internal error (qh_meminit): memory alignment %d is not a power of 2\n", alignment); + qh_errexit(qh, qhmem_ERRqhull, NULL, NULL); + } + qh->qhmem.sizetable= (int *) calloc((size_t)numsizes, sizeof(int)); + qh->qhmem.freelists= (void **) calloc((size_t)numsizes, sizeof(void *)); + if (!qh->qhmem.sizetable || !qh->qhmem.freelists) { + qh_fprintf(qh, qh->qhmem.ferr, 6086, "qhull error (qh_meminit): insufficient memory\n"); + qh_errexit(qh, qhmem_ERRmem, NULL, NULL); + } + if (qh->qhmem.IStracing >= 1) + qh_fprintf(qh, qh->qhmem.ferr, 8059, "qh_meminitbuffers: memory initialized with alignment %d\n", alignment); +} /* meminitbuffers */ + +/*-<a href="qh-mem_r.htm#TOC" + >-------------------------------</a><a name="memsetup">-</a> + + qh_memsetup(qh) + set up memory after running memsize() +*/ +void qh_memsetup(qhT *qh) { + int k,i; + + qsort(qh->qhmem.sizetable, (size_t)qh->qhmem.TABLEsize, sizeof(int), qh_intcompare); + qh->qhmem.LASTsize= qh->qhmem.sizetable[qh->qhmem.TABLEsize-1]; + if (qh->qhmem.LASTsize >= qh->qhmem.BUFsize || qh->qhmem.LASTsize >= qh->qhmem.BUFinit) { + qh_fprintf(qh, qh->qhmem.ferr, 6087, "qhull error (qh_memsetup): largest mem size %d is >= buffer size %d or initial buffer size %d\n", + qh->qhmem.LASTsize, qh->qhmem.BUFsize, qh->qhmem.BUFinit); + qh_errexit(qh, qhmem_ERRmem, NULL, NULL); + } + if (!(qh->qhmem.indextable= (int *)qh_malloc((size_t)(qh->qhmem.LASTsize+1) * sizeof(int)))) { + qh_fprintf(qh, qh->qhmem.ferr, 6088, "qhull error (qh_memsetup): insufficient memory\n"); + qh_errexit(qh, qhmem_ERRmem, NULL, NULL); + } + for (k=qh->qhmem.LASTsize+1; k--; ) + qh->qhmem.indextable[k]= k; + i= 0; + for (k=0; k <= qh->qhmem.LASTsize; k++) { + if (qh->qhmem.indextable[k] <= qh->qhmem.sizetable[i]) + qh->qhmem.indextable[k]= i; + else + qh->qhmem.indextable[k]= ++i; + } +} /* memsetup */ + +/*-<a href="qh-mem_r.htm#TOC" + >-------------------------------</a><a name="memsize">-</a> + + qh_memsize(qh, size ) + define a free list for this size +*/ +void qh_memsize(qhT *qh, int size) { + int k; + + if (qh->qhmem.LASTsize) { + qh_fprintf(qh, qh->qhmem.ferr, 6089, "qhull internal error (qh_memsize): qh_memsize called after qh_memsetup\n"); + qh_errexit(qh, qhmem_ERRqhull, NULL, NULL); + } + size= (size + qh->qhmem.ALIGNmask) & ~qh->qhmem.ALIGNmask; + if (qh->qhmem.IStracing >= 3) + qh_fprintf(qh, qh->qhmem.ferr, 3078, "qh_memsize: quick memory of %d bytes\n", size); + for (k=qh->qhmem.TABLEsize; k--; ) { + if (qh->qhmem.sizetable[k] == size) + return; + } + if (qh->qhmem.TABLEsize < qh->qhmem.NUMsizes) + qh->qhmem.sizetable[qh->qhmem.TABLEsize++]= size; + else + qh_fprintf(qh, qh->qhmem.ferr, 7060, "qhull warning (qh_memsize): free list table has room for only %d sizes\n", qh->qhmem.NUMsizes); +} /* memsize */ + + +/*-<a href="qh-mem_r.htm#TOC" + >-------------------------------</a><a name="memstatistics">-</a> + + qh_memstatistics(qh, fp ) + print out memory statistics + + Verifies that qh->qhmem.totfree == sum of freelists +*/ +void qh_memstatistics(qhT *qh, FILE *fp) { + int i; + int count; + void *object; + + qh_memcheck(qh); + qh_fprintf(qh, fp, 9278, "\nmemory statistics:\n\ +%7d quick allocations\n\ +%7d short allocations\n\ +%7d long allocations\n\ +%7d short frees\n\ +%7d long frees\n\ +%7d bytes of short memory in use\n\ +%7d bytes of short memory in freelists\n\ +%7d bytes of dropped short memory\n\ +%7d bytes of unused short memory (estimated)\n\ +%7d bytes of long memory allocated (max, except for input)\n\ +%7d bytes of long memory in use (in %d pieces)\n\ +%7d bytes of short memory buffers (minus links)\n\ +%7d bytes per short memory buffer (initially %d bytes)\n", + qh->qhmem.cntquick, qh->qhmem.cntshort, qh->qhmem.cntlong, + qh->qhmem.freeshort, qh->qhmem.freelong, + qh->qhmem.totshort, qh->qhmem.totfree, + qh->qhmem.totdropped + qh->qhmem.freesize, qh->qhmem.totunused, + qh->qhmem.maxlong, qh->qhmem.totlong, qh->qhmem.cntlong - qh->qhmem.freelong, + qh->qhmem.totbuffer, qh->qhmem.BUFsize, qh->qhmem.BUFinit); + if (qh->qhmem.cntlarger) { + qh_fprintf(qh, fp, 9279, "%7d calls to qh_setlarger\n%7.2g average copy size\n", + qh->qhmem.cntlarger, ((double)qh->qhmem.totlarger)/(double)qh->qhmem.cntlarger); + qh_fprintf(qh, fp, 9280, " freelists(bytes->count):"); + } + for (i=0; i < qh->qhmem.TABLEsize; i++) { + count=0; + for (object= qh->qhmem.freelists[i]; object; object= *((void **)object)) + count++; + qh_fprintf(qh, fp, 9281, " %d->%d", qh->qhmem.sizetable[i], count); + } + qh_fprintf(qh, fp, 9282, "\n\n"); +} /* memstatistics */ + + +/*-<a href="qh-mem_r.htm#TOC" + >-------------------------------</a><a name="NOmem">-</a> + + qh_NOmem + turn off quick-fit memory allocation + + notes: + uses qh_malloc() and qh_free() instead +*/ +#else /* qh_NOmem */ + +void *qh_memalloc(qhT *qh, int insize) { + void *object; + + if (!(object= qh_malloc((size_t)insize))) { + qh_fprintf(qh, qh->qhmem.ferr, 6090, "qhull error (qh_memalloc): insufficient memory\n"); + qh_errexit(qh, qhmem_ERRmem, NULL, NULL); + } + qh->qhmem.cntlong++; + qh->qhmem.totlong += insize; + if (qh->qhmem.maxlong < qh->qhmem.totlong) + qh->qhmem.maxlong= qh->qhmem.totlong; + if (qh->qhmem.IStracing >= 5) + qh_fprintf(qh, qh->qhmem.ferr, 8060, "qh_mem %p n %8d alloc long: %d bytes (tot %d cnt %d)\n", object, qh->qhmem.cntlong+qh->qhmem.freelong, insize, qh->qhmem.totlong, qh->qhmem.cntlong-qh->qhmem.freelong); + return object; +} + +void qh_memcheck(qhT *qh) { +} + +void qh_memfree(qhT *qh, void *object, int insize) { + + if (!object) + return; + qh_free(object); + qh->qhmem.freelong++; + qh->qhmem.totlong -= insize; + if (qh->qhmem.IStracing >= 5) + qh_fprintf(qh, qh->qhmem.ferr, 8061, "qh_mem %p n %8d free long: %d bytes (tot %d cnt %d)\n", object, qh->qhmem.cntlong+qh->qhmem.freelong, insize, qh->qhmem.totlong, qh->qhmem.cntlong-qh->qhmem.freelong); +} + +void qh_memfreeshort(qhT *qh, int *curlong, int *totlong) { + *totlong= qh->qhmem.totlong; + *curlong= qh->qhmem.cntlong - qh->qhmem.freelong; + memset((char *)&qh->qhmem, 0, sizeof(qh->qhmem)); /* every field is 0, FALSE, NULL */ +} + +void qh_meminit(qhT *qh, FILE *ferr) { + + memset((char *)&qh->qhmem, 0, sizeof(qh->qhmem)); /* every field is 0, FALSE, NULL */ + if (ferr) + qh->qhmem.ferr= ferr; + else + qh->qhmem.ferr= stderr; + if (sizeof(void *) < sizeof(int)) { + qh_fprintf(qh, qh->qhmem.ferr, 6091, "qhull internal error (qh_meminit): sizeof(void *) %d < sizeof(int) %d. qset_r.c will not work\n", (int)sizeof(void*), (int)sizeof(int)); + qh_errexit(qh, qhmem_ERRqhull, NULL, NULL); + } +} + +void qh_meminitbuffers(qhT *qh, int tracelevel, int alignment, int numsizes, int bufsize, int bufinit) { + + qh->qhmem.IStracing= tracelevel; +} + +void qh_memsetup(qhT *qh) { +} + +void qh_memsize(qhT *qh, int size) { +} + +void qh_memstatistics(qhT *qh, FILE *fp) { + + qh_fprintf(qh, fp, 9409, "\nmemory statistics:\n\ +%7d long allocations\n\ +%7d long frees\n\ +%7d bytes of long memory allocated (max, except for input)\n\ +%7d bytes of long memory in use (in %d pieces)\n", + qh->qhmem.cntlong, + qh->qhmem.freelong, + qh->qhmem.maxlong, qh->qhmem.totlong, qh->qhmem.cntlong - qh->qhmem.freelong); +} + +#endif /* qh_NOmem */ + +/*-<a href="qh-mem_r.htm#TOC" +>-------------------------------</a><a name="memtotlong">-</a> + + qh_memtotal(qh, totlong, curlong, totshort, curshort, maxlong, totbuffer ) + Return the total, allocated long and short memory + + returns: + Returns the total current bytes of long and short allocations + Returns the current count of long and short allocations + Returns the maximum long memory and total short buffer (minus one link per buffer) + Does not error (for deprecated UsingLibQhull.cpp in libqhullpcpp) +*/ +void qh_memtotal(qhT *qh, int *totlong, int *curlong, int *totshort, int *curshort, int *maxlong, int *totbuffer) { + *totlong= qh->qhmem.totlong; + *curlong= qh->qhmem.cntlong - qh->qhmem.freelong; + *totshort= qh->qhmem.totshort; + *curshort= qh->qhmem.cntshort + qh->qhmem.cntquick - qh->qhmem.freeshort; + *maxlong= qh->qhmem.maxlong; + *totbuffer= qh->qhmem.totbuffer; +} /* memtotlong */ + diff --git a/contrib/libs/qhull/libqhull_r/mem_r.h b/contrib/libs/qhull/libqhull_r/mem_r.h new file mode 100644 index 0000000000..aeb761b100 --- /dev/null +++ b/contrib/libs/qhull/libqhull_r/mem_r.h @@ -0,0 +1,235 @@ +/*<html><pre> -<a href="qh-mem_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + mem_r.h + prototypes for memory management functions + + see qh-mem_r.htm, mem_r.c and qset_r.h + + for error handling, writes message and calls + qh_errexit(qhT *qh, qhmem_ERRmem, NULL, NULL) if insufficient memory + and + qh_errexit(qhT *qh, qhmem_ERRqhull, NULL, NULL) otherwise + + Copyright (c) 1993-2020 The Geometry Center. + $Id: //main/2019/qhull/src/libqhull_r/mem_r.h#6 $$Change: 2953 $ + $DateTime: 2020/05/21 22:05:32 $$Author: bbarber $ +*/ + +#ifndef qhDEFmem +#define qhDEFmem 1 + +#include <stdio.h> + +#ifndef DEFsetT +#define DEFsetT 1 +typedef struct setT setT; /* defined in qset_r.h */ +#endif + +#ifndef DEFqhT +#define DEFqhT 1 +typedef struct qhT qhT; /* defined in libqhull_r.h */ +#endif + +/*-<a href="qh-mem_r.htm#TOC" + >-------------------------------</a><a name="NOmem">-</a> + + qh_NOmem + turn off quick-fit memory allocation + + notes: + mem_r.c implements Quickfit memory allocation for about 20% time + savings. If it fails on your machine, try to locate the + problem, and send the answer to qhull@qhull.org. If this can + not be done, define qh_NOmem to use malloc/free instead. + + #define qh_NOmem +*/ + +/*-<a href="qh-mem_r.htm#TOC" +>-------------------------------</a><a name="TRACEshort">-</a> + +qh_TRACEshort +Trace short and quick memory allocations at T5 + +*/ +#define qh_TRACEshort + +/*------------------------------------------- + to avoid bus errors, memory allocation must consider alignment requirements. + malloc() automatically takes care of alignment. Since mem_r.c manages + its own memory, we need to explicitly specify alignment in + qh_meminitbuffers(). + + A safe choice is sizeof(double). sizeof(float) may be used if doubles + do not occur in data structures and pointers are the same size. Be careful + of machines (e.g., DEC Alpha) with large pointers. If gcc is available, + use __alignof__(double) or fmax_(__alignof__(float), __alignof__(void *)). + + see <a href="user_r.h#MEMalign">qh_MEMalign</a> in user_r.h for qhull's alignment +*/ + +#define qhmem_ERRmem 4 /* matches qh_ERRmem in libqhull_r.h */ +#define qhmem_ERRqhull 5 /* matches qh_ERRqhull in libqhull_r.h */ + +/*-<a href="qh-mem_r.htm#TOC" + >--------------------------------</a><a name="ptr_intT">-</a> + + ptr_intT + for casting a void * to an integer-type that holds a pointer + Used for integer expressions (e.g., computing qh_gethash() in poly_r.c) + + notes: + WARN64 -- these notes indicate 64-bit issues + On 64-bit machines, a pointer may be larger than an 'int'. + qh_meminit()/mem_r.c checks that 'ptr_intT' holds a 'void*' + ptr_intT is typically a signed value, but not necessarily so + size_t is typically unsigned, but should match the parameter type + Qhull uses int instead of size_t except for system calls such as malloc, qsort, qh_malloc, etc. + This matches Qt convention and is easier to work with. +*/ +#if (defined(__MINGW64__)) && defined(_WIN64) +typedef long long ptr_intT; +#elif defined(_MSC_VER) && defined(_WIN64) +typedef long long ptr_intT; +#else +typedef long ptr_intT; +#endif + +/*-<a href="qh-mem_r.htm#TOC" + >--------------------------------</a><a name="qhmemT">-</a> + + qhmemT + global memory structure for mem_r.c + + notes: + users should ignore qhmem except for writing extensions + qhmem is allocated in mem_r.c + + qhmem could be swapable like qh and qhstat, but then + multiple qh's and qhmem's would need to keep in synch. + A swapable qhmem would also waste memory buffers. As long + as memory operations are atomic, there is no problem with + multiple qh structures being active at the same time. + If you need separate address spaces, you can swap the + contents of qh->qhmem. +*/ +typedef struct qhmemT qhmemT; + +struct qhmemT { /* global memory management variables */ + int BUFsize; /* size of memory allocation buffer */ + int BUFinit; /* initial size of memory allocation buffer */ + int TABLEsize; /* actual number of sizes in free list table */ + int NUMsizes; /* maximum number of sizes in free list table */ + int LASTsize; /* last size in free list table */ + int ALIGNmask; /* worst-case alignment, must be 2^n-1 */ + void **freelists; /* free list table, linked by offset 0 */ + int *sizetable; /* size of each freelist */ + int *indextable; /* size->index table */ + void *curbuffer; /* current buffer, linked by offset 0 */ + void *freemem; /* free memory in curbuffer */ + int freesize; /* size of freemem in bytes */ + setT *tempstack; /* stack of temporary memory, managed by users */ + FILE *ferr; /* file for reporting errors when 'qh' may be undefined */ + int IStracing; /* =5 if tracing memory allocations */ + int cntquick; /* count of quick allocations */ + /* Note: removing statistics doesn't effect speed */ + int cntshort; /* count of short allocations */ + int cntlong; /* count of long allocations */ + int freeshort; /* count of short memfrees */ + int freelong; /* count of long memfrees */ + int totbuffer; /* total short memory buffers minus buffer links */ + int totdropped; /* total dropped memory at end of short memory buffers (e.g., freesize) */ + int totfree; /* total size of free, short memory on freelists */ + int totlong; /* total size of long memory in use */ + int maxlong; /* maximum totlong */ + int totshort; /* total size of short memory in use */ + int totunused; /* total unused short memory (estimated, short size - request size of first allocations) */ + int cntlarger; /* count of setlarger's */ + int totlarger; /* total copied by setlarger */ +}; + + +/*==================== -macros ====================*/ + +/*-<a href="qh-mem_r.htm#TOC" + >--------------------------------</a><a name="memalloc_">-</a> + + qh_memalloc_(qh, insize, freelistp, object, type) + returns object of size bytes + assumes size<=qh->qhmem.LASTsize and void **freelistp is a temp +*/ + +#if defined qh_NOmem +#define qh_memalloc_(qh, insize, freelistp, object, type) {\ + (void)freelistp; /* Avoid warnings */ \ + object= (type *)qh_memalloc(qh, insize); } +#elif defined qh_TRACEshort +#define qh_memalloc_(qh, insize, freelistp, object, type) {\ + (void)freelistp; /* Avoid warnings */ \ + object= (type *)qh_memalloc(qh, insize); } +#else /* !qh_NOmem */ + +#define qh_memalloc_(qh, insize, freelistp, object, type) {\ + freelistp= qh->qhmem.freelists + qh->qhmem.indextable[insize];\ + if ((object= (type *)*freelistp)) {\ + qh->qhmem.totshort += qh->qhmem.sizetable[qh->qhmem.indextable[insize]]; \ + qh->qhmem.totfree -= qh->qhmem.sizetable[qh->qhmem.indextable[insize]]; \ + qh->qhmem.cntquick++; \ + *freelistp= *((void **)*freelistp);\ + }else object= (type *)qh_memalloc(qh, insize);} +#endif + +/*-<a href="qh-mem_r.htm#TOC" + >--------------------------------</a><a name="memfree_">-</a> + + qh_memfree_(qh, object, insize, freelistp) + free up an object + + notes: + object may be NULL + assumes size<=qh->qhmem.LASTsize and void **freelistp is a temp +*/ +#if defined qh_NOmem +#define qh_memfree_(qh, object, insize, freelistp) {\ + (void)freelistp; /* Avoid warnings */ \ + qh_memfree(qh, object, insize); } +#elif defined qh_TRACEshort +#define qh_memfree_(qh, object, insize, freelistp) {\ + (void)freelistp; /* Avoid warnings */ \ + qh_memfree(qh, object, insize); } +#else /* !qh_NOmem */ + +#define qh_memfree_(qh, object, insize, freelistp) {\ + if (object) { \ + qh->qhmem.freeshort++;\ + freelistp= qh->qhmem.freelists + qh->qhmem.indextable[insize];\ + qh->qhmem.totshort -= qh->qhmem.sizetable[qh->qhmem.indextable[insize]]; \ + qh->qhmem.totfree += qh->qhmem.sizetable[qh->qhmem.indextable[insize]]; \ + *((void **)object)= *freelistp;\ + *freelistp= object;}} +#endif + +/*=============== prototypes in alphabetical order ============*/ + +#ifdef __cplusplus +extern "C" { +#endif + +void *qh_memalloc(qhT *qh, int insize); +void qh_memcheck(qhT *qh); +void qh_memfree(qhT *qh, void *object, int insize); +void qh_memfreeshort(qhT *qh, int *curlong, int *totlong); +void qh_meminit(qhT *qh, FILE *ferr); +void qh_meminitbuffers(qhT *qh, int tracelevel, int alignment, int numsizes, + int bufsize, int bufinit); +void qh_memsetup(qhT *qh); +void qh_memsize(qhT *qh, int size); +void qh_memstatistics(qhT *qh, FILE *fp); +void qh_memtotal(qhT *qh, int *totlong, int *curlong, int *totshort, int *curshort, int *maxlong, int *totbuffer); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* qhDEFmem */ diff --git a/contrib/libs/qhull/libqhull_r/merge_r.c b/contrib/libs/qhull/libqhull_r/merge_r.c new file mode 100644 index 0000000000..f3c899cd6e --- /dev/null +++ b/contrib/libs/qhull/libqhull_r/merge_r.c @@ -0,0 +1,5590 @@ +/*<html><pre> -<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="TOP">-</a> + + merge_r.c + merges non-convex facets + + see qh-merge_r.htm and merge_r.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_r.c): + qh_partitionvisible(qh, !qh_ALL, &numoutside); // visible_list, newfacet_list + qh_deletevisible(); // qh.visible_list + qh_resetlists(qh, 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-2020 C.B. Barber. + $Id: //main/2019/qhull/src/libqhull_r/merge_r.c#14 $$Change: 2953 $ + $DateTime: 2020/05/21 22:05:32 $$Author: bbarber $ +*/ + +#include "qhull_ra.h" + +#ifndef qh_NOmerge + +/* MRGnone, etc. */ +const char *mergetypes[]= { + "none", + "coplanar", + "anglecoplanar", + "concave", + "concavecoplanar", + "twisted", + "flip", + "dupridge", + "subridge", + "vertices", + "degen", + "redundant", + "mirror", + "coplanarhorizon", +}; + +/*===== functions(alphabetical after premerge and postmerge) ======*/ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="premerge">-</a> + + qh_premerge(qh, apexpointid, maxcentrum ) + pre-merge nonconvex facets in qh.newfacet_list for apexpointid + maxcentrum defines coplanar and concave (qh_test_appendmerge) + + returns: + deleted facets added to qh.visible_list with facet->visible set + + notes: + only called by qh_addpoint + uses globals, qh.MERGEexact, qh.PREmerge + + design: + mark dupridges in qh.newfacet_list + merge facet cycles in qh.newfacet_list + merge dupridges 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(qhT *qh, int apexpointid, realT maxcentrum, realT maxangle /* qh.newfacet_list */) { + boolT othermerge= False; + + if (qh->ZEROcentrum && qh_checkzero(qh, !qh_ALL)) + return; + trace2((qh, qh->ferr, 2008, "qh_premerge: premerge centrum %2.2g angle %4.4g for apex p%d newfacet_list f%d\n", + maxcentrum, maxangle, apexpointid, getid_(qh->newfacet_list))); + if (qh->IStracing >= 4 && qh->num_facets < 100) + qh_printlists(qh); + qh->centrum_radius= maxcentrum; + qh->cos_max= maxangle; + if (qh->hull_dim >=3) { + qh_mark_dupridges(qh, qh->newfacet_list, qh_ALL); /* facet_mergeset */ + qh_mergecycle_all(qh, qh->newfacet_list, &othermerge); + qh_forcedmerges(qh, &othermerge /* qh.facet_mergeset */); + }else /* qh.hull_dim == 2 */ + qh_mergecycle_all(qh, qh->newfacet_list, &othermerge); + qh_flippedmerges(qh, qh->newfacet_list, &othermerge); + if (!qh->MERGEexact || zzval_(Ztotmerge)) { + zinc_(Zpremergetot); + qh->POSTmerging= False; + qh_getmergeset_initial(qh, qh->newfacet_list); + qh_all_merges(qh, othermerge, False); + } +} /* premerge */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="postmerge">-</a> + + qh_postmerge(qh, reason, maxcentrum, maxangle, vneighbors ) + post-merge nonconvex facets as defined by maxcentrum and maxangle + 'reason' is for reporting progress + if vneighbors ('Qv'), + calls qh_test_vneighbors at end of qh_all_merge from qh_postmerge + + 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: + called by qh_qhull after qh_buildhull + called if a merge may be needed due to + qh.MERGEexact ('Qx'), qh_DIMreduceBuild, POSTmerge (e.g., 'Cn'), or TESTvneighbors ('Qv') + if firstmerge, + calls qh_reducevertices before qh_getmergeset + + 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 occurred + 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(qhT *qh, const char *reason, realT maxcentrum, realT maxangle, + boolT vneighbors) { + facetT *newfacet; + boolT othermerges= False; + vertexT *vertex; + + if (qh->REPORTfreq || qh->IStracing) { + qh_buildtracing(qh, NULL, NULL); + qh_printsummary(qh, qh->ferr); + if (qh->PRINTstatistics) + qh_printallstatistics(qh, qh->ferr, "reason"); + qh_fprintf(qh, qh->ferr, 8062, "\n%s with 'C%.2g' and 'A%.2g'\n", + reason, maxcentrum, maxangle); + } + trace2((qh, qh->ferr, 2009, "qh_postmerge: postmerge. test vneighbors? %d\n", + vneighbors)); + qh->centrum_radius= maxcentrum; + qh->cos_max= maxangle; + qh->POSTmerging= True; + if (qh->visible_list != qh->facet_list) { /* first call due to qh_buildhull, multiple calls if qh.POSTmerge */ + qh->NEWfacets= True; + qh->visible_list= qh->newfacet_list= qh->facet_list; + FORALLnew_facets { /* all facets are new facets for qh_postmerge */ + newfacet->newfacet= True; + if (!newfacet->simplicial) + newfacet->newmerge= True; /* test f.vertices for 'delridge'. 'newmerge' was cleared at end of qh_all_merges */ + zinc_(Zpostfacets); + } + qh->newvertex_list= qh->vertex_list; + FORALLvertices + vertex->newfacet= True; + if (qh->VERTEXneighbors) { /* a merge has occurred */ + if (qh->MERGEexact && qh->hull_dim <= qh_DIMreduceBuild) + qh_reducevertices(qh); /* qh_all_merges did not call qh_reducevertices for v.delridge */ + } + if (!qh->PREmerge && !qh->MERGEexact) + qh_flippedmerges(qh, qh->newfacet_list, &othermerges); + } + qh_getmergeset_initial(qh, qh->newfacet_list); + qh_all_merges(qh, False, vneighbors); /* calls qh_reducevertices before exiting */ + FORALLnew_facets + newfacet->newmerge= False; /* Was True if no vertex in f.vertices was 'delridge' */ +} /* post_merge */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="all_merges">-</a> + + qh_all_merges(qh, othermerge, vneighbors ) + merge all non-convex facets + + set othermerge if already merged facets (calls qh_reducevertices) + if vneighbors ('Qv' at qh.POSTmerge) + tests vertex neighbors for convexity at end (qh_test_vneighbors) + 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 implicit argument since merges call qh_removefacet() + [apr'19] restored qh_setdellast in place of qh_next_facetmerge. Much faster for post-merge + + 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 qh.POSTmerging + periodically call qh_reducevertices to reduce extra vertices and redundant vertices + after each pass, if qh.VERTEXneighbors + if qh.POSTmerging or was a merge with qh.hull_dim<=5 + call qh_reducevertices + update qh.facet_mergeset if degenredundant merges + if 'Qv' and qh.POSTmerging + test vertex neighbors for convexity +*/ +void qh_all_merges(qhT *qh, boolT othermerge, boolT vneighbors) { + facetT *facet1, *facet2, *newfacet; + mergeT *merge; + boolT wasmerge= False, isreduce; + void **freelistp; /* used if !qh_NOmem by qh_memfree_() */ + vertexT *vertex; + realT angle, distance; + mergeType mergetype; + int numcoplanar=0, numconcave=0, numconcavecoplanar= 0, numdegenredun= 0, numnewmerges= 0, numtwisted= 0; + + trace2((qh, qh->ferr, 2010, "qh_all_merges: starting to merge %d facet and %d degenerate merges for new facets f%d, othermerge? %d\n", + qh_setsize(qh, qh->facet_mergeset), qh_setsize(qh, qh->degen_mergeset), getid_(qh->newfacet_list), othermerge)); + + while (True) { + wasmerge= False; + while (qh_setsize(qh, qh->facet_mergeset) > 0 || qh_setsize(qh, qh->degen_mergeset) > 0) { + if (qh_setsize(qh, qh->degen_mergeset) > 0) { + numdegenredun += qh_merge_degenredundant(qh); + wasmerge= True; + } + while ((merge= (mergeT *)qh_setdellast(qh->facet_mergeset))) { + facet1= merge->facet1; + facet2= merge->facet2; + vertex= merge->vertex1; /* not used for qh.facet_mergeset*/ + mergetype= merge->mergetype; + angle= merge->angle; + distance= merge->distance; + qh_memfree_(qh, merge, (int)sizeof(mergeT), freelistp); /* 'merge' is invalid */ + if (facet1->visible || facet2->visible) { + trace3((qh, qh->ferr, 3045, "qh_all_merges: drop merge of f%d (del? %d) into f%d (del? %d) mergetype %d, dist %4.4g, angle %4.4g. One or both facets is deleted\n", + facet1->id, facet1->visible, facet2->id, facet2->visible, mergetype, distance, angle)); + continue; + }else if (mergetype == MRGcoplanar || mergetype == MRGanglecoplanar) { + if (qh->MERGEindependent) { + if ((!facet1->tested && facet1->newfacet) + || (!facet2->tested && facet2->newfacet)) { + trace3((qh, qh->ferr, 3064, "qh_all_merges: drop merge of f%d (tested? %d) into f%d (tested? %d) mergetype %d, dist %2.2g, angle %4.4g. Merge independent sets of coplanar merges\n", + facet1->id, facet1->visible, facet2->id, facet2->visible, mergetype, distance, angle)); + continue; + } + } + } + trace3((qh, qh->ferr, 3047, "qh_all_merges: merge f%d and f%d type %d dist %2.2g angle %4.4g\n", + facet1->id, facet2->id, mergetype, distance, angle)); + if (mergetype == MRGtwisted) + qh_merge_twisted(qh, facet1, facet2); + else + qh_merge_nonconvex(qh, facet1, facet2, mergetype); + numnewmerges++; + numdegenredun += qh_merge_degenredundant(qh); + wasmerge= True; + if (mergetype == MRGconcave) + numconcave++; + else if (mergetype == MRGconcavecoplanar) + numconcavecoplanar++; + else if (mergetype == MRGtwisted) + numtwisted++; + else if (mergetype == MRGcoplanar || mergetype == MRGanglecoplanar) + numcoplanar++; + else { + qh_fprintf(qh, qh->ferr, 6394, "qhull internal error (qh_all_merges): expecting concave, coplanar, or twisted merge. Got merge f%d f%d v%d mergetype %d\n", + getid_(facet1), getid_(facet2), getid_(vertex), mergetype); + qh_errexit2(qh, qh_ERRqhull, facet1, facet2); + } + } /* while qh_setdellast */ + if (qh->POSTmerging && qh->hull_dim <= qh_DIMreduceBuild + && numnewmerges > qh_MAXnewmerges) { + numnewmerges= 0; + wasmerge= othermerge= False; + qh_reducevertices(qh); /* otherwise large post merges too slow */ + } + qh_getmergeset(qh, qh->newfacet_list); /* qh.facet_mergeset */ + } /* while facet_mergeset or degen_mergeset */ + if (qh->VERTEXneighbors) { /* at least one merge */ + isreduce= False; + if (qh->POSTmerging && qh->hull_dim >= 4) { + isreduce= True; + }else if (qh->POSTmerging || !qh->MERGEexact) { + if ((wasmerge || othermerge) && qh->hull_dim > 2 && qh->hull_dim <= qh_DIMreduceBuild) + isreduce= True; + } + if (isreduce) { + wasmerge= othermerge= False; + if (qh_reducevertices(qh)) { + qh_getmergeset(qh, qh->newfacet_list); /* facet_mergeset */ + continue; + } + } + } + if (vneighbors && qh_test_vneighbors(qh /* qh.newfacet_list */)) + continue; + break; + } /* while (True) */ + if (wasmerge || othermerge) { + trace3((qh, qh->ferr, 3033, "qh_all_merges: skip qh_reducevertices due to post-merging, no qh.VERTEXneighbors (%d), or hull_dim %d ==2 or >%d\n", qh->VERTEXneighbors, qh->hull_dim, qh_DIMreduceBuild)) + FORALLnew_facets { + newfacet->newmerge= False; + } + } + if (qh->CHECKfrequently && !qh->MERGEexact) { + qh->old_randomdist= qh->RANDOMdist; + qh->RANDOMdist= False; + qh_checkconvex(qh, qh->newfacet_list, qh_ALGORITHMfault); + /* qh_checkconnect(qh); [this is slow and it changes the facet order] */ + qh->RANDOMdist= qh->old_randomdist; + } + trace1((qh, qh->ferr, 1009, "qh_all_merges: merged %d coplanar %d concave %d concavecoplanar %d twisted facets and %d degen or redundant facets.\n", + numcoplanar, numconcave, numconcavecoplanar, numtwisted, numdegenredun)); + if (qh->IStracing >= 4 && qh->num_facets < 500) + qh_printlists(qh); +} /* all_merges */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="all_vertexmerges">-</a> + + qh_all_vertexmerges(qh, apexpointid, facet, &retryfacet ) + merge vertices in qh.vertex_mergeset and subsequent merges + + returns: + returns retryfacet for facet (if defined) + updates qh.facet_list, qh.num_facets, qh.vertex_list, qh.num_vertices + mergesets are empty + if merges, resets facet lists + + notes: + called from qh_qhull, qh_addpoint, and qh_buildcone_mergepinched + vertex merges occur after facet merges and qh_resetlists + + design: + while merges in vertex_mergeset (MRGvertices) + merge a pair of pinched vertices + update vertex neighbors + merge non-convex and degenerate facets and check for ridges with duplicate vertices + partition outside points of deleted, "visible" facets +*/ +void qh_all_vertexmerges(qhT *qh, int apexpointid, facetT *facet, facetT **retryfacet) { + int numpoints; /* ignore count of partitioned points. Used by qh_addpoint for Zpbalance */ + + if (retryfacet) + *retryfacet= facet; + while (qh_setsize(qh, qh->vertex_mergeset) > 0) { + trace1((qh, qh->ferr, 1057, "qh_all_vertexmerges: starting to merge %d vertex merges for apex p%d facet f%d\n", + qh_setsize(qh, qh->vertex_mergeset), apexpointid, getid_(facet))); + if (qh->IStracing >= 4 && qh->num_facets < 1000) + qh_printlists(qh); + qh_merge_pinchedvertices(qh, apexpointid /* qh.vertex_mergeset, visible_list, newvertex_list, newfacet_list */); + qh_update_vertexneighbors(qh); /* update neighbors of qh.newvertex_list from qh_newvertices for deleted facets on qh.visible_list */ + /* test ridges and merge non-convex facets */ + qh_getmergeset(qh, qh->newfacet_list); + qh_all_merges(qh, True, False); /* calls qh_reducevertices */ + if (qh->CHECKfrequently) + qh_checkpolygon(qh, qh->facet_list); + qh_partitionvisible(qh, !qh_ALL, &numpoints /* qh.visible_list qh.del_vertices*/); + if (retryfacet) + *retryfacet= qh_getreplacement(qh, *retryfacet); + qh_deletevisible(qh /* qh.visible_list qh.del_vertices*/); + qh_resetlists(qh, False, qh_RESETvisible /* qh.visible_list newvertex_list qh.newfacet_list */); + if (qh->IStracing >= 4 && qh->num_facets < 1000) { + qh_printlists(qh); + qh_checkpolygon(qh, qh->facet_list); + } + } +} /* all_vertexmerges */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="appendmergeset">-</a> + + qh_appendmergeset(qh, facet, vertex, neighbor, mergetype, dist, angle ) + appends an entry to qh.facet_mergeset or qh.degen_mergeset + if 'dist' is unknown, set it to 0.0 + if 'angle' is unknown, set it to 1.0 (coplanar) + + returns: + merge appended to facet_mergeset or degen_mergeset + sets ->degenerate or ->redundant if degen_mergeset + + notes: + caller collects statistics and/or caller of qh_mergefacet + 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 (merged last) + else if redundant merge + append to qh.degen_mergeset +*/ +void qh_appendmergeset(qhT *qh, facetT *facet, facetT *neighbor, mergeType mergetype, coordT dist, realT angle) { + mergeT *merge, *lastmerge; + void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */ + const char *mergename; + + if ((facet->redundant && mergetype != MRGmirror) || neighbor->redundant) { + trace3((qh, qh->ferr, 3051, "qh_appendmergeset: f%d is already redundant (%d) or f%d is already redundant (%d). Ignore merge f%d and f%d type %d\n", + facet->id, facet->redundant, neighbor->id, neighbor->redundant, facet->id, neighbor->id, mergetype)); + return; + } + if (facet->degenerate && mergetype == MRGdegen) { + trace3((qh, qh->ferr, 3077, "qh_appendmergeset: f%d is already degenerate. Ignore merge f%d type %d (MRGdegen)\n", + facet->id, facet->id, mergetype)); + return; + } + if (!qh->facet_mergeset || !qh->degen_mergeset) { + qh_fprintf(qh, qh->ferr, 6403, "qhull internal error (qh_appendmergeset): expecting temp set defined for qh.facet_mergeset (0x%x) and qh.degen_mergeset (0x%x). Got NULL\n", + qh->facet_mergeset, qh->degen_mergeset); + /* otherwise qh_setappend creates a new set that is not freed by qh_freebuild() */ + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + if (neighbor->flipped && !facet->flipped) { + if (mergetype != MRGdupridge) { + qh_fprintf(qh, qh->ferr, 6355, "qhull internal error (qh_appendmergeset): except for MRGdupridge, cannot merge a non-flipped facet f%d into flipped f%d, mergetype %d, dist %4.4g\n", + facet->id, neighbor->id, mergetype, dist); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + }else { + trace2((qh, qh->ferr, 2106, "qh_appendmergeset: dupridge will merge a non-flipped facet f%d into flipped f%d, dist %4.4g\n", + facet->id, neighbor->id, dist)); + } + } + qh_memalloc_(qh, (int)sizeof(mergeT), freelistp, merge, mergeT); + merge->angle= angle; + merge->distance= dist; + merge->facet1= facet; + merge->facet2= neighbor; + merge->vertex1= NULL; + merge->vertex2= NULL; + merge->ridge1= NULL; + merge->ridge2= NULL; + merge->mergetype= mergetype; + if(mergetype > 0 && mergetype < sizeof(mergetypes)/sizeof(char *)) + mergename= mergetypes[mergetype]; + else + mergename= mergetypes[MRGnone]; + if (mergetype < MRGdegen) + qh_setappend(qh, &(qh->facet_mergeset), merge); + else if (mergetype == MRGdegen) { + facet->degenerate= True; + if (!(lastmerge= (mergeT *)qh_setlast(qh->degen_mergeset)) + || lastmerge->mergetype == MRGdegen) + qh_setappend(qh, &(qh->degen_mergeset), merge); + else + qh_setaddnth(qh, &(qh->degen_mergeset), 0, merge); /* merged last */ + }else if (mergetype == MRGredundant) { + facet->redundant= True; + qh_setappend(qh, &(qh->degen_mergeset), merge); + }else /* mergetype == MRGmirror */ { + if (facet->redundant || neighbor->redundant) { + qh_fprintf(qh, qh->ferr, 6092, "qhull internal error (qh_appendmergeset): facet f%d or f%d is already a mirrored facet (i.e., 'redundant')\n", + facet->id, neighbor->id); + qh_errexit2(qh, qh_ERRqhull, facet, neighbor); + } + if (!qh_setequal(facet->vertices, neighbor->vertices)) { + qh_fprintf(qh, qh->ferr, 6093, "qhull internal error (qh_appendmergeset): mirrored facets f%d and f%d do not have the same vertices\n", + facet->id, neighbor->id); + qh_errexit2(qh, qh_ERRqhull, facet, neighbor); + } + facet->redundant= True; + neighbor->redundant= True; + qh_setappend(qh, &(qh->degen_mergeset), merge); + } + if (merge->mergetype >= MRGdegen) { + trace3((qh, qh->ferr, 3044, "qh_appendmergeset: append merge f%d and f%d type %d (%s) to qh.degen_mergeset (size %d)\n", + merge->facet1->id, merge->facet2->id, merge->mergetype, mergename, qh_setsize(qh, qh->degen_mergeset))); + }else { + trace3((qh, qh->ferr, 3027, "qh_appendmergeset: append merge f%d and f%d type %d (%s) dist %2.2g angle %4.4g to qh.facet_mergeset (size %d)\n", + merge->facet1->id, merge->facet2->id, merge->mergetype, mergename, merge->distance, merge->angle, qh_setsize(qh, qh->facet_mergeset))); + } +} /* appendmergeset */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="appendvertexmerge">-</a> + + qh_appendvertexmerge(qh, vertex, vertex2, mergetype, distance, ridge1, ridge2 ) + appends a vertex merge to qh.vertex_mergeset + MRGsubridge includes two ridges (from MRGdupridge) + MRGvertices includes two ridges + + notes: + called by qh_getpinchedmerges for MRGsubridge + called by qh_maybe_duplicateridge and qh_maybe_duplicateridges for MRGvertices + only way to add a vertex merge to qh.vertex_mergeset + checked by qh_next_vertexmerge +*/ +void qh_appendvertexmerge(qhT *qh, vertexT *vertex, vertexT *destination, mergeType mergetype, realT distance, ridgeT *ridge1, ridgeT *ridge2) { + mergeT *merge; + void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */ + const char *mergename; + + if (!qh->vertex_mergeset) { + qh_fprintf(qh, qh->ferr, 6387, "qhull internal error (qh_appendvertexmerge): expecting temp set defined for qh.vertex_mergeset (0x%x). Got NULL\n", + qh->vertex_mergeset); + /* otherwise qh_setappend creates a new set that is not freed by qh_freebuild() */ + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + qh_memalloc_(qh, (int)sizeof(mergeT), freelistp, merge, mergeT); + merge->angle= qh_ANGLEnone; + merge->distance= distance; + merge->facet1= NULL; + merge->facet2= NULL; + merge->vertex1= vertex; + merge->vertex2= destination; + merge->ridge1= ridge1; + merge->ridge2= ridge2; + merge->mergetype= mergetype; + if(mergetype > 0 && mergetype < sizeof(mergetypes)/sizeof(char *)) + mergename= mergetypes[mergetype]; + else + mergename= mergetypes[MRGnone]; + if (mergetype == MRGvertices) { + if (!ridge1 || !ridge2 || ridge1 == ridge2) { + qh_fprintf(qh, qh->ferr, 6106, "qhull internal error (qh_appendvertexmerge): expecting two distinct ridges for MRGvertices. Got r%d r%d\n", + getid_(ridge1), getid_(ridge2)); + qh_errexit(qh, qh_ERRqhull, NULL, ridge1); + } + } + qh_setappend(qh, &(qh->vertex_mergeset), merge); + trace3((qh, qh->ferr, 3034, "qh_appendvertexmerge: append merge v%d into v%d r%d r%d dist %2.2g type %d (%s)\n", + vertex->id, destination->id, getid_(ridge1), getid_(ridge2), distance, merge->mergetype, mergename)); +} /* appendvertexmerge */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="basevertices">-</a> + + qh_basevertices(qh, 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(qhT *qh, facetT *samecycle) { + facetT *same; + vertexT *apex, *vertex, **vertexp; + setT *vertices= qh_settemp(qh, 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(qh, &vertices, vertex); + vertex->visitid= qh->vertex_visit; + vertex->seen= False; + } + } + } + trace4((qh, qh->ferr, 4019, "qh_basevertices: found %d vertices\n", + qh_setsize(qh, vertices))); + return vertices; +} /* basevertices */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="check_dupridge">-</a> + + qh_check_dupridge(qh, facet1, dist1, facet2, dist2 ) + Check dupridge between facet1 and facet2 for wide merge + dist1 is the maximum distance of facet1's vertices to facet2 + dist2 is the maximum distance of facet2's vertices to facet1 + + returns + Level 1 log of the dupridge with the minimum distance between vertices + Throws error if the merge will increase the maximum facet width by qh_WIDEduplicate (100x) + + notes: + only called from qh_forcedmerges +*/ +void qh_check_dupridge(qhT *qh, facetT *facet1, realT dist1, facetT *facet2, realT dist2) { + vertexT *vertex, **vertexp, *vertexA, **vertexAp; + realT dist, innerplane, mergedist, outerplane, prevdist, ratio, vertexratio; + realT minvertex= REALmax; + + mergedist= fmin_(dist1, dist2); + qh_outerinner(qh, NULL, &outerplane, &innerplane); /* ratio from qh_printsummary */ + FOREACHvertex_(facet1->vertices) { /* The dupridge is between facet1 and facet2, so either facet can be tested */ + FOREACHvertexA_(facet1->vertices) { + if (vertex > vertexA){ /* Test each pair once */ + dist= qh_pointdist(vertex->point, vertexA->point, qh->hull_dim); + minimize_(minvertex, dist); + /* Not quite correct. A facet may have a dupridge and another pair of nearly adjacent vertices. */ + } + } + } + prevdist= fmax_(outerplane, innerplane); + maximize_(prevdist, qh->ONEmerge + qh->DISTround); + maximize_(prevdist, qh->MINoutside + qh->DISTround); + ratio= mergedist/prevdist; + vertexratio= minvertex/prevdist; + trace0((qh, qh->ferr, 16, "qh_check_dupridge: dupridge between f%d and f%d (vertex dist %2.2g), dist %2.2g, reverse dist %2.2g, ratio %2.2g while processing p%d\n", + facet1->id, facet2->id, minvertex, dist1, dist2, ratio, qh->furthest_id)); + if (ratio > qh_WIDEduplicate) { + qh_fprintf(qh, qh->ferr, 6271, "qhull topology error (qh_check_dupridge): wide merge (%.1fx wider) due to dupridge between f%d and f%d (vertex dist %2.2g), merge dist %2.2g, while processing p%d\n- Allow error with option 'Q12'\n", + ratio, facet1->id, facet2->id, minvertex, mergedist, qh->furthest_id); + if (vertexratio < qh_WIDEpinched) + qh_fprintf(qh, qh->ferr, 8145, "- Experimental option merge-pinched-vertices ('Q14') may avoid this error. It merges nearly adjacent vertices.\n"); + if (qh->DELAUNAY) + qh_fprintf(qh, qh->ferr, 8145, "- A bounding box for the input sites may alleviate this error.\n"); + if (!qh->ALLOWwide) + qh_errexit2(qh, qh_ERRwide, facet1, facet2); + } +} /* check_dupridge */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="checkconnect">-</a> + + qh_checkconnect(qh) + 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(qhT *qh /* qh.newfacet_list */) { + facetT *facet, *newfacet, *errfacet= NULL, *neighbor, **neighborp; + + facet= qh->newfacet_list; + qh_removefacet(qh, facet); + qh_appendfacet(qh, facet); + facet->visitid= ++qh->visit_id; + FORALLfacet_(facet) { + FOREACHneighbor_(facet) { + if (neighbor->visitid != qh->visit_id) { + qh_removefacet(qh, neighbor); + qh_appendfacet(qh, neighbor); + neighbor->visitid= qh->visit_id; + } + } + } + FORALLnew_facets { + if (newfacet->visitid == qh->visit_id) + break; + qh_fprintf(qh, qh->ferr, 6094, "qhull internal error (qh_checkconnect): f%d is not attached to the new facets\n", + newfacet->id); + errfacet= newfacet; + } + if (errfacet) + qh_errexit(qh, qh_ERRqhull, errfacet, NULL); +} /* checkconnect */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="checkdelfacet">-</a> + + qh_checkdelfacet(qh, facet, mergeset ) + check that mergeset does not reference facet + +*/ +void qh_checkdelfacet(qhT *qh, facetT *facet, setT *mergeset) { + mergeT *merge, **mergep; + + FOREACHmerge_(mergeset) { + if (merge->facet1 == facet || merge->facet2 == facet) { + qh_fprintf(qh, qh->ferr, 6390, "qhull internal error (qh_checkdelfacet): cannot delete f%d. It is referenced by merge f%d f%d mergetype %d\n", + facet->id, merge->facet1->id, getid_(merge->facet2), merge->mergetype); + qh_errexit2(qh, qh_ERRqhull, merge->facet1, merge->facet2); + } + } +} /* checkdelfacet */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="checkdelridge">-</a> + + qh_checkdelridge(qh) + check that qh_delridge_merge is not needed for deleted ridges + + notes: + called from qh_mergecycle, qh_makenewfacets, qh_attachnewfacets + errors if qh.vertex_mergeset is non-empty + errors if any visible or new facet has a ridge with r.nonconvex set + assumes that vertex.delfacet is not needed +*/ +void qh_checkdelridge(qhT *qh /* qh.visible_facets, vertex_mergeset */) { + facetT *newfacet, *visible; + ridgeT *ridge, **ridgep; + + if (!SETempty_(qh->vertex_mergeset)) { + qh_fprintf(qh, qh->ferr, 6382, "qhull internal error (qh_checkdelridge): expecting empty qh.vertex_mergeset in order to avoid calling qh_delridge_merge. Got %d merges\n", qh_setsize(qh, qh->vertex_mergeset)); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + + FORALLnew_facets { + FOREACHridge_(newfacet->ridges) { + if (ridge->nonconvex) { + qh_fprintf(qh, qh->ferr, 6313, "qhull internal error (qh_checkdelridge): unexpected 'nonconvex' flag for ridge r%d in newfacet f%d. Otherwise need to call qh_delridge_merge\n", + ridge->id, newfacet->id); + qh_errexit(qh, qh_ERRqhull, newfacet, ridge); + } + } + } + + FORALLvisible_facets { + FOREACHridge_(visible->ridges) { + if (ridge->nonconvex) { + qh_fprintf(qh, qh->ferr, 6385, "qhull internal error (qh_checkdelridge): unexpected 'nonconvex' flag for ridge r%d in visible facet f%d. Otherwise need to call qh_delridge_merge\n", + ridge->id, visible->id); + qh_errexit(qh, qh_ERRqhull, visible, ridge); + } + } + } +} /* checkdelridge */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="checkzero">-</a> + + qh_checkzero(qh, 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: + called by qh_premerge (qh.CHECKzero, 'C-0') and qh_qhull ('Qx') + 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(qhT *qh, boolT testall) { + facetT *facet, *neighbor; + facetT *horizon, *facetlist; + int neighbor_i, neighbor_n; + 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, qh->ferr, 2011, "qh_checkzero: skip convexity check until first pre-merge\n")); + return True; + } + } + FORALLfacet_(facetlist) { + qh->vertex_visit++; + horizon= NULL; + FOREACHneighbor_i_(qh, facet) { + if (!neighbor_i && !testall) { + horizon= neighbor; + continue; /* horizon facet tested in qh_findhorizon */ + } + vertex= SETelemt_(facet->vertices, neighbor_i, vertexT); + vertex->visitid= qh->vertex_visit; + zzinc_(Zdistzero); + qh_distplane(qh, vertex->point, neighbor, &dist); + if (dist >= -2 * qh->DISTround) { /* need 2x for qh_distround and 'Rn' for qh_checkconvex, same as qh.premerge_centrum */ + qh->ZEROall_ok= False; + if (!qh->MERGEexact || testall || dist > qh->DISTround) + goto LABELnonconvex; + } + } + if (!testall && horizon) { + FOREACHvertex_(horizon->vertices) { + if (vertex->visitid != qh->vertex_visit) { + zzinc_(Zdistzero); + qh_distplane(qh, vertex->point, facet, &dist); + if (dist >= -2 * qh->DISTround) { + qh->ZEROall_ok= False; + if (!qh->MERGEexact || dist > qh->DISTround) + goto LABELnonconvexhorizon; + } + break; + } + } + } + } + trace2((qh, qh->ferr, 2012, "qh_checkzero: testall %d, facets are %s\n", testall, + (qh->MERGEexact && !testall) ? + "not concave, flipped, or dupridge" : "clearly convex")); + return True; + + LABELproblem: + qh->ZEROall_ok= False; + trace2((qh, qh->ferr, 2013, "qh_checkzero: qh_premerge is needed. New facet f%d or its horizon f%d is non-simplicial, flipped, dupridge, or mergehorizon\n", + facet->id, horizon->id)); + return False; + + LABELnonconvex: + trace2((qh, 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; + + LABELnonconvexhorizon: + trace2((qh, qh->ferr, 2060, "qh_checkzero: facet f%d and horizon f%d are not clearly convex. v%d dist %.2g\n", + facet->id, horizon->id, vertex->id, dist)); + return False; +} /* checkzero */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="compare_anglemerge">-</a> + + qh_compare_anglemerge( mergeA, mergeB ) + used by qsort() to order qh.facet_mergeset by mergetype and angle (qh.ANGLEmerge, 'Q1') + lower numbered mergetypes done first (MRGcoplanar before MRGconcave) + + notes: + qh_all_merges processes qh.facet_mergeset by qh_setdellast + [mar'19] evaluated various options with eg/q_benchmark and merging of pinched vertices (Q14) +*/ +int qh_compare_anglemerge(const void *p1, const void *p2) { + const mergeT *a= *((mergeT *const*)p1), *b= *((mergeT *const*)p2); + + if (a->mergetype != b->mergetype) + return (a->mergetype < b->mergetype ? 1 : -1); /* select MRGcoplanar (1) before MRGconcave (3) */ + else + return (a->angle > b->angle ? 1 : -1); /* select coplanar merge (1.0) before sharp merge (-0.5) */ +} /* compare_anglemerge */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="compare_facetmerge">-</a> + + qh_compare_facetmerge( mergeA, mergeB ) + used by qsort() to order merges by mergetype, first merge, first + lower numbered mergetypes done first (MRGcoplanar before MRGconcave) + if same merge type, flat merges are first + + notes: + qh_all_merges processes qh.facet_mergeset by qh_setdellast + [mar'19] evaluated various options with eg/q_benchmark and merging of pinched vertices (Q14) +*/ +int qh_compare_facetmerge(const void *p1, const void *p2) { + const mergeT *a= *((mergeT *const*)p1), *b= *((mergeT *const*)p2); + + if (a->mergetype != b->mergetype) + return (a->mergetype < b->mergetype ? 1 : -1); /* select MRGcoplanar (1) before MRGconcave (3) */ + else if (a->mergetype == MRGanglecoplanar) + return (a->angle > b->angle ? 1 : -1); /* if MRGanglecoplanar, select coplanar merge (1.0) before sharp merge (-0.5) */ + else + return (a->distance < b->distance ? 1 : -1); /* select flat (0.0) merge before wide (1e-10) merge */ +} /* compare_facetmerge */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="comparevisit">-</a> + + qh_comparevisit( vertexA, vertexB ) + used by qsort() to order vertices by their visitid + + notes: + only called by qh_find_newvertex +*/ +int qh_comparevisit(const void *p1, const void *p2) { + const vertexT *a= *((vertexT *const*)p1), *b= *((vertexT *const*)p2); + + if (a->visitid > b->visitid) + return 1; + return -1; +} /* comparevisit */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="copynonconvex">-</a> + + qh_copynonconvex(qh, 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(qhT *qh, ridgeT *atridge) { + facetT *facet, *otherfacet; + ridgeT *ridge, **ridgep; + + facet= atridge->top; + otherfacet= atridge->bottom; + atridge->nonconvex= False; + FOREACHridge_(facet->ridges) { + if (otherfacet == ridge->top || otherfacet == ridge->bottom) { + if (ridge != atridge) { + ridge->nonconvex= True; + trace4((qh, qh->ferr, 4020, "qh_copynonconvex: moved nonconvex flag from r%d to r%d between f%d and f%d\n", + atridge->id, ridge->id, facet->id, otherfacet->id)); + break; + } + } + } +} /* copynonconvex */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="degen_redundant_facet">-</a> + + qh_degen_redundant_facet(qh, facet ) + check for a degenerate (too few neighbors) or redundant (subset of vertices) facet + + notes: + called at end of qh_mergefacet, qh_renamevertex, and qh_reducevertices + 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_test_redundant_neighbors, qh_maydropneighbor + + design: + test for redundant neighbor + test for degenerate facet +*/ +void qh_degen_redundant_facet(qhT *qh, facetT *facet) { + vertexT *vertex, **vertexp; + facetT *neighbor, **neighborp; + + trace3((qh, qh->ferr, 3028, "qh_degen_redundant_facet: test facet f%d for degen/redundant\n", + facet->id)); + if (facet->flipped) { + trace2((qh, qh->ferr, 3074, "qh_degen_redundant_facet: f%d is flipped, will merge later\n", facet->id)); + return; + } + FOREACHneighbor_(facet) { + if (neighbor->flipped) /* disallow merge of non-flipped into flipped, neighbor will be merged later */ + continue; + if (neighbor->visible) { + qh_fprintf(qh, qh->ferr, 6357, "qhull internal error (qh_degen_redundant_facet): facet f%d has deleted neighbor f%d (qh.visible_list)\n", + facet->id, neighbor->id); + qh_errexit2(qh, qh_ERRqhull, facet, neighbor); + } + qh->vertex_visit++; + FOREACHvertex_(neighbor->vertices) + vertex->visitid= qh->vertex_visit; + FOREACHvertex_(facet->vertices) { + if (vertex->visitid != qh->vertex_visit) + break; + } + if (!vertex) { + trace2((qh, qh->ferr, 2015, "qh_degen_redundant_facet: f%d is contained in f%d. merge\n", facet->id, neighbor->id)); + qh_appendmergeset(qh, facet, neighbor, MRGredundant, 0.0, 1.0); + return; + } + } + if (qh_setsize(qh, facet->neighbors) < qh->hull_dim) { + qh_appendmergeset(qh, facet, facet, MRGdegen, 0.0, 1.0); + trace2((qh, qh->ferr, 2016, "qh_degen_redundant_facet: f%d is degenerate.\n", facet->id)); + } +} /* degen_redundant_facet */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="delridge_merge">-</a> + + qh_delridge_merge(qh, ridge ) + delete ridge due to a merge + + notes: + only called by merge_r.c (qh_mergeridges, qh_renameridgevertex) + ridges also freed in qh_freeqhull and qh_mergecycle_ridges + + design: + if needed, moves ridge.nonconvex to another ridge + sets vertex.delridge for qh_reducevertices + deletes ridge from qh.vertex_mergeset + deletes ridge from its neighboring facets + frees up its memory +*/ +void qh_delridge_merge(qhT *qh, ridgeT *ridge) { + vertexT *vertex, **vertexp; + mergeT *merge; + int merge_i, merge_n; + + trace3((qh, qh->ferr, 3036, "qh_delridge_merge: delete ridge r%d between f%d and f%d\n", + ridge->id, ridge->top->id, ridge->bottom->id)); + if (ridge->nonconvex) + qh_copynonconvex(qh, ridge); + FOREACHvertex_(ridge->vertices) + vertex->delridge= True; + FOREACHmerge_i_(qh, qh->vertex_mergeset) { + if (merge->ridge1 == ridge || merge->ridge2 == ridge) { + trace3((qh, qh->ferr, 3029, "qh_delridge_merge: drop merge of v%d into v%d (dist %2.2g r%d r%d) due to deleted, duplicated ridge r%d\n", + merge->vertex1->id, merge->vertex2->id, merge->distance, merge->ridge1->id, merge->ridge2->id, ridge->id)); + if (merge->ridge1 == ridge) + merge->ridge2->mergevertex= False; + else + merge->ridge1->mergevertex= False; + qh_setdelnth(qh, qh->vertex_mergeset, merge_i); + merge_i--; merge_n--; /* next merge after deleted */ + } + } + qh_setdel(ridge->top->ridges, ridge); + qh_setdel(ridge->bottom->ridges, ridge); + qh_delridge(qh, ridge); +} /* delridge_merge */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="drop_mergevertex">-</a> + + qh_drop_mergevertex(qh, merge ) + + clear mergevertex flags for ridges of a vertex merge +*/ +void qh_drop_mergevertex(qhT *qh, mergeT *merge) +{ + if (merge->mergetype == MRGvertices) { + merge->ridge1->mergevertex= False; + merge->ridge1->mergevertex2= True; + merge->ridge2->mergevertex= False; + merge->ridge2->mergevertex2= True; + trace3((qh, qh->ferr, 3032, "qh_drop_mergevertex: unset mergevertex for r%d and r%d due to dropped vertex merge v%d to v%d. Sets mergevertex2\n", + merge->ridge1->id, merge->ridge2->id, merge->vertex1->id, merge->vertex2->id)); + } +} /* drop_mergevertex */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="find_newvertex">-</a> + + qh_find_newvertex(qh, 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 newvertex and oldvertex + vertices without oldvertex sorted by number of deleted ridges + qh.vertex_visit updated + sets v.seen + + notes: + called by qh_redundant_vertex due to vertex->delridge and qh_rename_sharedvertex + sets vertex->visitid to 0..setsize() for vertices + 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 ridges + remove unvisited vertices + set qh.vertex_visit above all possible values + sort vertices by number of ridges (minimize ridges that need renaming + add each ridge to qh.hash_table + for each vertex in vertices + find the first vertex that would not cause a duplicate ridge after a rename +*/ +vertexT *qh_find_newvertex(qhT *qh, vertexT *oldvertex, setT *vertices, setT *ridges) { + vertexT *vertex, **vertexp; + setT *newridges; + ridgeT *ridge, **ridgep; + int size, hashsize; + int hash; + unsigned int maxvisit; + +#ifndef qh_NOtrace + if (qh->IStracing >= 4) { + qh_fprintf(qh, qh->ferr, 8063, "qh_find_newvertex: find new vertex for v%d from ", + oldvertex->id); + FOREACHvertex_(vertices) + qh_fprintf(qh, qh->ferr, 8064, "v%d ", vertex->id); + FOREACHridge_(ridges) + qh_fprintf(qh, qh->ferr, 8065, "r%d ", ridge->id); + qh_fprintf(qh, qh->ferr, 8066, "\n"); + } +#endif + FOREACHridge_(ridges) { + FOREACHvertex_(ridge->vertices) + vertex->seen= False; + } + FOREACHvertex_(vertices) { + vertex->visitid= 0; /* v.visitid will be number of ridges */ + vertex->seen= True; + } + FOREACHridge_(ridges) { + FOREACHvertex_(ridge->vertices) { + if (vertex->seen) + vertex->visitid++; + } + } + FOREACHvertex_(vertices) { + if (!vertex->visitid) { + qh_setdelnth(qh, vertices, SETindex_(vertices,vertex)); + vertexp--; /* repeat since deleted this vertex */ + } + } + maxvisit= (unsigned int)qh_setsize(qh, ridges); + maximize_(qh->vertex_visit, maxvisit); + if (!qh_setsize(qh, vertices)) { + trace4((qh, 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(qh, vertices), + sizeof(vertexT *), qh_comparevisit); + /* can now use qh->vertex_visit */ + if (qh->PRINTstatistics) { + size= qh_setsize(qh, vertices); + zinc_(Zintersect); + zadd_(Zintersecttot, size); + zmax_(Zintersectmax, size); + } + hashsize= qh_newhashtable(qh, qh_setsize(qh, ridges)); + FOREACHridge_(ridges) + qh_hashridge(qh, qh->hash_table, hashsize, ridge, oldvertex); + FOREACHvertex_(vertices) { + newridges= qh_vertexridges(qh, vertex, !qh_ALL); + FOREACHridge_(newridges) { + if (qh_hashridge_find(qh, qh->hash_table, hashsize, ridge, vertex, oldvertex, &hash)) { + zinc_(Zvertexridge); + break; + } + } + qh_settempfree(qh, &newridges); + if (!ridge) + break; /* found a rename */ + } + if (vertex) { + /* counted in qh_renamevertex */ + trace2((qh, 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(qh, vertices), qh_setsize(qh, ridges))); + }else { + zinc_(Zfindfail); + trace0((qh, 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, &qh->hash_table); + return vertex; +} /* find_newvertex */ + +/*-<a href="qh-geom2_r.htm#TOC" + >-------------------------------</a><a name="findbest_pinchedvertex">-</a> + + qh_findbest_pinchedvertex(qh, merge, apex, nearestp, distp ) + Determine the best pinched vertex to rename as its nearest neighboring vertex + Renaming will remove a duplicate MRGdupridge in newfacet_list + + returns: + pinched vertex (either apex or subridge), nearest vertex (subridge or neighbor vertex), and the distance between them + + notes: + only called by qh_getpinchedmerges + assumes qh.VERTEXneighbors + see qh_findbest_ridgevertex + + design: + if the facets have the same vertices + return the nearest vertex pair + else + the subridge is the intersection of the two new facets minus the apex + the subridge consists of qh.hull_dim-2 horizon vertices + the subridge is also a matched ridge for the new facets (its duplicate) + determine the nearest vertex to the apex + determine the nearest pair of subridge vertices + for each vertex in the subridge + determine the nearest neighbor vertex (not in the subridge) +*/ +vertexT *qh_findbest_pinchedvertex(qhT *qh, mergeT *merge, vertexT *apex, vertexT **nearestp, coordT *distp /* qh.newfacet_list */) { + vertexT *vertex, **vertexp, *vertexA, **vertexAp; + vertexT *bestvertex= NULL, *bestpinched= NULL; + setT *subridge, *maybepinched; + coordT dist, bestdist= REALmax; + coordT pincheddist= (qh->ONEmerge+qh->DISTround)*qh_RATIOpinchedsubridge; + + if (!merge->facet1->simplicial || !merge->facet2->simplicial) { + qh_fprintf(qh, qh->ferr, 6351, "qhull internal error (qh_findbest_pinchedvertex): expecting merge of adjacent, simplicial new facets. f%d or f%d is not simplicial\n", + merge->facet1->id, merge->facet2->id); + qh_errexit2(qh, qh_ERRqhull, merge->facet1, merge->facet2); + } + subridge= qh_vertexintersect_new(qh, merge->facet1->vertices, merge->facet2->vertices); /* new setT. No error_exit() */ + if (qh_setsize(qh, subridge) == qh->hull_dim) { /* duplicate vertices */ + bestdist= qh_vertex_bestdist2(qh, subridge, &bestvertex, &bestpinched); + if(bestvertex == apex) { + bestvertex= bestpinched; + bestpinched= apex; + } + }else { + qh_setdel(subridge, apex); + if (qh_setsize(qh, subridge) != qh->hull_dim - 2) { + qh_fprintf(qh, qh->ferr, 6409, "qhull internal error (qh_findbest_pinchedvertex): expecting subridge of qh.hull_dim-2 vertices for the intersection of new facets f%d and f%d minus their apex. Got %d vertices\n", + merge->facet1->id, merge->facet2->id, qh_setsize(qh, subridge)); + qh_errexit2(qh, qh_ERRqhull, merge->facet1, merge->facet2); + } + FOREACHvertex_(subridge) { + dist= qh_pointdist(vertex->point, apex->point, qh->hull_dim); + if (dist < bestdist) { + bestpinched= apex; + bestvertex= vertex; + bestdist= dist; + } + } + if (bestdist > pincheddist) { + FOREACHvertex_(subridge) { + FOREACHvertexA_(subridge) { + if (vertexA->id > vertex->id) { /* once per vertex pair, do not compare addresses */ + dist= qh_pointdist(vertexA->point, vertex->point, qh->hull_dim); + if (dist < bestdist) { + bestpinched= vertexA; + bestvertex= vertex; + bestdist= dist; + } + } + } + } + } + if (bestdist > pincheddist) { + FOREACHvertexA_(subridge) { + maybepinched= qh_neighbor_vertices(qh, vertexA, subridge); /* subridge and apex tested above */ + FOREACHvertex_(maybepinched) { + dist= qh_pointdist(vertex->point, vertexA->point, qh->hull_dim); + if (dist < bestdist) { + bestvertex= vertex; + bestpinched= vertexA; + bestdist= dist; + } + } + qh_settempfree(qh, &maybepinched); + } + } + } + *distp= bestdist; + qh_setfree(qh, &subridge); /* qh_err_exit not called since allocated */ + if (!bestvertex) { /* should never happen if qh.hull_dim > 2 */ + qh_fprintf(qh, qh->ferr, 6274, "qhull internal error (qh_findbest_pinchedvertex): did not find best vertex for subridge of dupridge between f%d and f%d, while processing p%d\n", merge->facet1->id, merge->facet2->id, qh->furthest_id); + qh_errexit2(qh, qh_ERRqhull, merge->facet1, merge->facet2); + } + *nearestp= bestvertex; + trace2((qh, qh->ferr, 2061, "qh_findbest_pinchedvertex: best pinched p%d(v%d) and vertex p%d(v%d) are closest (%2.2g) for duplicate subridge between f%d and f%d\n", + qh_pointid(qh, bestpinched->point), bestpinched->id, qh_pointid(qh, bestvertex->point), bestvertex->id, bestdist, merge->facet1->id, merge->facet2->id)); + return bestpinched; +} /* findbest_pinchedvertex */ + +/*-<a href="qh-geom2_r.htm#TOC" + >-------------------------------</a><a name="findbest_ridgevertex">-</a> + + qh_findbest_ridgevertex(qh, ridge, pinchedp, distp ) + Determine the best vertex/pinched-vertex to merge for ridges with the same vertices + + returns: + vertex, pinched vertex, and the distance between them + + notes: + assumes qh.hull_dim>=3 + see qh_findbest_pinchedvertex + +*/ +vertexT *qh_findbest_ridgevertex(qhT *qh, ridgeT *ridge, vertexT **pinchedp, coordT *distp) { + vertexT *bestvertex; + + *distp= qh_vertex_bestdist2(qh, ridge->vertices, &bestvertex, pinchedp); + trace4((qh, qh->ferr, 4069, "qh_findbest_ridgevertex: best pinched p%d(v%d) and vertex p%d(v%d) are closest (%2.2g) for duplicated ridge r%d (same vertices) between f%d and f%d\n", + qh_pointid(qh, (*pinchedp)->point), (*pinchedp)->id, qh_pointid(qh, bestvertex->point), bestvertex->id, *distp, ridge->id, ridge->top->id, ridge->bottom->id)); + return bestvertex; +} /* findbest_ridgevertex */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="findbest_test">-</a> + + qh_findbest_test(qh, 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 + initially *bestfacet==NULL and *dist==REALmax + + returns: + if a better facet (i.e., vertices/centrum of facet closer to neighbor) + updates bestfacet, dist, mindist, and maxdist + + notes: + called by qh_findbestneighbor + ignores pairs of flipped facets, unless that's all there is +*/ +void qh_findbest_test(qhT *qh, boolT testcentrum, facetT *facet, facetT *neighbor, + facetT **bestfacet, realT *distp, realT *mindistp, realT *maxdistp) { + realT dist, mindist, maxdist; + + if (facet->flipped && neighbor->flipped && *bestfacet && !(*bestfacet)->flipped) + return; /* do not merge flipped into flipped facets */ + if (testcentrum) { + zzinc_(Zbestdist); + qh_distplane(qh, 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(qh, facet, neighbor, &mindist, &maxdist); + if (dist < *distp) { + *bestfacet= neighbor; + *mindistp= mindist; + *maxdistp= maxdist; + *distp= dist; + } +} /* findbest_test */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="findbestneighbor">-</a> + + qh_findbestneighbor(qh, 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: + error if qh_ASvoronoi + 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(qhT *qh, 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(qh, facet->vertices); + + if(qh->CENTERtype==qh_ASvoronoi){ + qh_fprintf(qh, qh->ferr, 6272, "qhull internal error: cannot call qh_findbestneighor for f%d while qh.CENTERtype is qh_ASvoronoi\n", facet->id); + qh_errexit(qh, qh_ERRqhull, facet, NULL); + } + *distp= REALmax; + if (size > qh_BESTcentrum2 * qh->hull_dim + qh_BESTcentrum) { + testcentrum= True; + zinc_(Zbestcentrum); + if (!facet->center) + facet->center= qh_getcentrum(qh, facet); + } + if (size > qh->hull_dim + qh_BESTnonconvex) { + FOREACHridge_(facet->ridges) { + if (ridge->nonconvex) { + neighbor= otherfacet_(ridge, facet); + qh_findbest_test(qh, testcentrum, facet, neighbor, + &bestfacet, distp, mindistp, maxdistp); + } + } + } + if (!bestfacet) { + nonconvex= False; + FOREACHneighbor_(facet) + qh_findbest_test(qh, testcentrum, facet, neighbor, + &bestfacet, distp, mindistp, maxdistp); + } + if (!bestfacet) { + qh_fprintf(qh, qh->ferr, 6095, "qhull internal error (qh_findbestneighbor): no neighbors for f%d\n", facet->id); + qh_errexit(qh, qh_ERRqhull, facet, NULL); + } + if (testcentrum) + qh_getdistance(qh, facet, bestfacet, mindistp, maxdistp); + trace3((qh, 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 */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="flippedmerges">-</a> + + qh_flippedmerges(qh, 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(qhT *qh, facetT *facetlist, boolT *wasmerge) { + facetT *facet, *neighbor, *facet1; + realT dist, mindist, maxdist; + mergeT *merge, **mergep; + setT *othermerges; + int nummerge= 0, numdegen= 0; + + trace4((qh, qh->ferr, 4024, "qh_flippedmerges: begin\n")); + FORALLfacet_(facetlist) { + if (facet->flipped && !facet->visible) + qh_appendmergeset(qh, facet, facet, MRGflip, 0.0, 1.0); + } + othermerges= qh_settemppop(qh); + if(othermerges != qh->facet_mergeset) { + qh_fprintf(qh, qh->ferr, 6392, "qhull internal error (qh_flippedmerges): facet_mergeset (%d merges) not at top of tempstack (%d merges)\n", + qh_setsize(qh, qh->facet_mergeset), qh_setsize(qh, othermerges)); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + qh->facet_mergeset= qh_settemp(qh, qh->TEMPsize); + qh_settemppush(qh, othermerges); + FOREACHmerge_(othermerges) { + facet1= merge->facet1; + if (merge->mergetype != MRGflip || facet1->visible) + continue; + if (qh->TRACEmerge-1 == zzval_(Ztotmerge)) + qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel; + neighbor= qh_findbestneighbor(qh, facet1, &dist, &mindist, &maxdist); + trace0((qh, 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(qh, facet1, neighbor, merge->mergetype, &mindist, &maxdist, !qh_MERGEapex); + nummerge++; + if (qh->PRINTstatistics) { + zinc_(Zflipped); + wadd_(Wflippedtot, dist); + wmax_(Wflippedmax, dist); + } + } + FOREACHmerge_(othermerges) { + if (merge->facet1->visible || merge->facet2->visible) + qh_memfree(qh, merge, (int)sizeof(mergeT)); /* invalidates merge and othermerges */ + else + qh_setappend(qh, &qh->facet_mergeset, merge); + } + qh_settempfree(qh, &othermerges); + numdegen += qh_merge_degenredundant(qh); /* somewhat better here than after each flipped merge -- qtest.sh 10 '500 C1,2e-13 D4' 'd Qbb' */ + if (nummerge) + *wasmerge= True; + trace1((qh, qh->ferr, 1010, "qh_flippedmerges: merged %d flipped and %d degenredundant facets into a good neighbor\n", + nummerge, numdegen)); +} /* flippedmerges */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="forcedmerges">-</a> + + qh_forcedmerges(qh, wasmerge ) + merge dupridges + calls qh_check_dupridge to report an error on wide merges + assumes qh_settemppop is qh.facet_mergeset + + returns: + removes all dupridges 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: + called by qh_premerge + dupridges 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 dupridge + find current facets by chasing f.replace links + check for wide merge due to dupridge + determine best direction for facet + merge one facet into the other + remove dupridges from qh.facet_mergeset +*/ +void qh_forcedmerges(qhT *qh, boolT *wasmerge) { + facetT *facet1, *facet2, *merging, *merged, *newfacet; + mergeT *merge, **mergep; + realT dist, mindist, maxdist, dist2, mindist2, maxdist2; + setT *othermerges; + int nummerge=0, numflip=0, numdegen= 0; + boolT wasdupridge= False; + + if (qh->TRACEmerge-1 == zzval_(Ztotmerge)) + qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel; + trace3((qh, qh->ferr, 3054, "qh_forcedmerges: merge dupridges\n")); + othermerges= qh_settemppop(qh); /* was facet_mergeset */ + if (qh->facet_mergeset != othermerges ) { + qh_fprintf(qh, qh->ferr, 6279, "qhull internal error (qh_forcedmerges): qh_settemppop (size %d) is not qh->facet_mergeset (size %d)\n", + qh_setsize(qh, othermerges), qh_setsize(qh, qh->facet_mergeset)); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + qh->facet_mergeset= qh_settemp(qh, qh->TEMPsize); + qh_settemppush(qh, othermerges); + FOREACHmerge_(othermerges) { + if (merge->mergetype != MRGdupridge) + continue; + wasdupridge= True; + if (qh->TRACEmerge-1 == zzval_(Ztotmerge)) + qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel; + facet1= qh_getreplacement(qh, merge->facet1); /* must exist, no qh_merge_degenredunant */ + facet2= qh_getreplacement(qh, merge->facet2); /* previously merged facet, if any */ + if (facet1 == facet2) + continue; + if (!qh_setin(facet2->neighbors, facet1)) { + qh_fprintf(qh, qh->ferr, 6096, "qhull internal error (qh_forcedmerges): f%d and f%d had a dupridge 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, qh_ERRqhull, facet1, facet2); + } + dist= qh_getdistance(qh, facet1, facet2, &mindist, &maxdist); + dist2= qh_getdistance(qh, facet2, facet1, &mindist2, &maxdist2); + qh_check_dupridge(qh, facet1, dist, facet2, dist2); + if (dist < dist2) { + if (facet2->flipped && !facet1->flipped && dist2 < qh_WIDEdupridge*(qh->ONEmerge+qh->DISTround)) { /* prefer merge of flipped facet */ + merging= facet2; + merged= facet1; + dist= dist2; + mindist= mindist2; + maxdist= maxdist2; + }else { + merging= facet1; + merged= facet2; + } + }else { + if (facet1->flipped && !facet2->flipped && dist < qh_WIDEdupridge*(qh->ONEmerge+qh->DISTround)) { /* prefer merge of flipped facet */ + merging= facet1; + merged= facet2; + }else { + merging= facet2; + merged= facet1; + dist= dist2; + mindist= mindist2; + maxdist= maxdist2; + } + } + qh_mergefacet(qh, merging, merged, merge->mergetype, &mindist, &maxdist, !qh_MERGEapex); + numdegen += qh_merge_degenredundant(qh); /* better here than at end -- qtest.sh 10 '500 C1,2e-13 D4' 'd Qbb' */ + if (facet1->flipped) { + zinc_(Zmergeflipdup); + numflip++; + }else + nummerge++; + if (qh->PRINTstatistics) { + zinc_(Zduplicate); + wadd_(Wduplicatetot, dist); + wmax_(Wduplicatemax, dist); + } + } + FOREACHmerge_(othermerges) { + if (merge->mergetype == MRGdupridge) + qh_memfree(qh, merge, (int)sizeof(mergeT)); /* invalidates merge and othermerges */ + else + qh_setappend(qh, &qh->facet_mergeset, merge); + } + qh_settempfree(qh, &othermerges); + if (wasdupridge) { + FORALLnew_facets { + if (newfacet->dupridge) { + newfacet->dupridge= False; + newfacet->mergeridge= False; + newfacet->mergeridge2= False; + if (qh_setsize(qh, newfacet->neighbors) < qh->hull_dim) { /* not tested for MRGdupridge */ + qh_appendmergeset(qh, newfacet, newfacet, MRGdegen, 0.0, 1.0); + trace2((qh, qh->ferr, 2107, "qh_forcedmerges: dupridge f%d is degenerate with fewer than %d neighbors\n", + newfacet->id, qh->hull_dim)); + } + } + } + numdegen += qh_merge_degenredundant(qh); + } + if (nummerge || numflip) { + *wasmerge= True; + trace1((qh, qh->ferr, 1011, "qh_forcedmerges: merged %d facets, %d flipped facets, and %d degenredundant facets across dupridges\n", + nummerge, numflip, numdegen)); + } +} /* forcedmerges */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="freemergesets">-</a> + + qh_freemergesets(qh ) + free the merge sets + + notes: + matches qh_initmergesets +*/ +void qh_freemergesets(qhT *qh) { + + if (!qh->facet_mergeset || !qh->degen_mergeset || !qh->vertex_mergeset) { + qh_fprintf(qh, qh->ferr, 6388, "qhull internal error (qh_freemergesets): expecting mergesets. Got a NULL mergeset, qh.facet_mergeset (0x%x), qh.degen_mergeset (0x%x), qh.vertex_mergeset (0x%x)\n", + qh->facet_mergeset, qh->degen_mergeset, qh->vertex_mergeset); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + if (!SETempty_(qh->facet_mergeset) || !SETempty_(qh->degen_mergeset) || !SETempty_(qh->vertex_mergeset)) { + qh_fprintf(qh, qh->ferr, 6389, "qhull internal error (qh_freemergesets): expecting empty mergesets. Got qh.facet_mergeset (%d merges), qh.degen_mergeset (%d merges), qh.vertex_mergeset (%d merges)\n", + qh_setsize(qh, qh->facet_mergeset), qh_setsize(qh, qh->degen_mergeset), qh_setsize(qh, qh->vertex_mergeset)); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + qh_settempfree(qh, &qh->facet_mergeset); + qh_settempfree(qh, &qh->vertex_mergeset); + qh_settempfree(qh, &qh->degen_mergeset); +} /* freemergesets */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="getmergeset">-</a> + + qh_getmergeset(qh, 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: + facetlist is qh.facet_newlist, use qh_getmergeset_initial for all facets + 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 + + 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 mergetype and angle or distance +*/ +void qh_getmergeset(qhT *qh, facetT *facetlist) { + facetT *facet, *neighbor, **neighborp; + ridgeT *ridge, **ridgep; + int nummerges; + boolT simplicial; + + nummerges= qh_setsize(qh, qh->facet_mergeset); + trace4((qh, qh->ferr, 4026, "qh_getmergeset: started.\n")); + qh->visit_id++; + FORALLfacet_(facetlist) { + if (facet->tested) + continue; + facet->visitid= qh->visit_id; + FOREACHneighbor_(facet) + neighbor->seen= False; + /* facet must be non-simplicial due to merge to qh.facet_newlist */ + FOREACHridge_(facet->ridges) { + if (ridge->tested && !ridge->nonconvex) + continue; + /* if r.tested & r.nonconvex, need to retest and append merge */ + neighbor= otherfacet_(ridge, facet); + if (neighbor->seen) { /* another ridge for this facet-neighbor pair was already tested in this loop */ + ridge->tested= True; + ridge->nonconvex= False; /* only one ridge is marked nonconvex per facet-neighbor pair */ + }else if (neighbor->visitid != qh->visit_id) { + neighbor->seen= True; + ridge->nonconvex= False; + simplicial= False; + if (ridge->simplicialbot && ridge->simplicialtop) + simplicial= True; + if (qh_test_appendmerge(qh, facet, neighbor, simplicial)) + ridge->nonconvex= True; + ridge->tested= True; + } + } + facet->tested= True; + } + nummerges= qh_setsize(qh, qh->facet_mergeset); + if (qh->ANGLEmerge) + qsort(SETaddr_(qh->facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_compare_anglemerge); + else + qsort(SETaddr_(qh->facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_compare_facetmerge); + nummerges += qh_setsize(qh, qh->degen_mergeset); + if (qh->POSTmerging) { + zadd_(Zmergesettot2, nummerges); + }else { + zadd_(Zmergesettot, nummerges); + zmax_(Zmergesetmax, nummerges); + } + trace2((qh, qh->ferr, 2021, "qh_getmergeset: %d merges found\n", nummerges)); +} /* getmergeset */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="getmergeset_initial">-</a> + + qh_getmergeset_initial(qh, 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 mergetype and angle or distance +*/ +void qh_getmergeset_initial(qhT *qh, facetT *facetlist) { + facetT *facet, *neighbor, **neighborp; + ridgeT *ridge, **ridgep; + int nummerges; + boolT simplicial; + + qh->visit_id++; + FORALLfacet_(facetlist) { + facet->visitid= qh->visit_id; + FOREACHneighbor_(facet) { + if (neighbor->visitid != qh->visit_id) { + simplicial= False; /* ignores r.simplicialtop/simplicialbot. Need to test horizon facets */ + if (facet->simplicial && neighbor->simplicial) + simplicial= True; + if (qh_test_appendmerge(qh, facet, neighbor, simplicial)) { + FOREACHridge_(neighbor->ridges) { + if (facet == otherfacet_(ridge, neighbor)) { + ridge->nonconvex= True; + break; /* only one ridge is marked nonconvex */ + } + } + } + } + } + facet->tested= True; + FOREACHridge_(facet->ridges) + ridge->tested= True; + } + nummerges= qh_setsize(qh, qh->facet_mergeset); + if (qh->ANGLEmerge) + qsort(SETaddr_(qh->facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_compare_anglemerge); + else + qsort(SETaddr_(qh->facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_compare_facetmerge); + nummerges += qh_setsize(qh, qh->degen_mergeset); + if (qh->POSTmerging) { + zadd_(Zmergeinittot2, nummerges); + }else { + zadd_(Zmergeinittot, nummerges); + zmax_(Zmergeinitmax, nummerges); + } + trace2((qh, qh->ferr, 2022, "qh_getmergeset_initial: %d merges found\n", nummerges)); +} /* getmergeset_initial */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="getpinchedmerges">-</a> + + qh_getpinchedmerges(qh, apex, maxdist, iscoplanar ) + get pinched merges for dupridges in qh.facet_mergeset + qh.NEWtentative==True + qh.newfacet_list with apex + qh.horizon_list is attached to qh.visible_list instead of qh.newfacet_list + maxdist for vertex-facet of a dupridge + qh.facet_mergeset is empty + qh.vertex_mergeset is a temporary set + + returns: + False if nearest vertex would increase facet width by more than maxdist or qh_WIDEpinched + True and iscoplanar, if the pinched vertex is the apex (i.e., make the apex a coplanar point) + True and !iscoplanar, if should merge a pinched vertex of a dupridge + qh.vertex_mergeset contains one or more MRGsubridge with a pinched vertex and a nearby, neighboring vertex + qh.facet_mergeset is empty + + notes: + called by qh_buildcone_mergepinched + hull_dim >= 3 + a pinched vertex is in a dupridge and the horizon + selects the pinched vertex that is closest to its neighbor + + design: + for each dupridge + determine the best pinched vertex to be merged into a neighboring vertex + if merging the pinched vertex would produce a wide merge (qh_WIDEpinched) + ignore pinched vertex with a warning, and use qh_merge_degenredundant instead + else + append the pinched vertex to vertex_mergeset for merging +*/ +boolT qh_getpinchedmerges(qhT *qh, vertexT *apex, coordT maxdupdist, boolT *iscoplanar /* qh.newfacet_list, qh.vertex_mergeset */) { + mergeT *merge, **mergep, *bestmerge= NULL; + vertexT *nearest, *pinched, *bestvertex= NULL, *bestpinched= NULL; + boolT result; + coordT dist, prevdist, bestdist= REALmax/(qh_RATIOcoplanarapex+1.0); /* allow *3.0 */ + realT ratio; + + trace2((qh, qh->ferr, 2062, "qh_getpinchedmerges: try to merge pinched vertices for dupridges in new facets with apex p%d(v%d) max dupdist %2.2g\n", + qh_pointid(qh, apex->point), apex->id, maxdupdist)); + *iscoplanar= False; + prevdist= fmax_(qh->ONEmerge + qh->DISTround, qh->MINoutside + qh->DISTround); + maximize_(prevdist, qh->max_outside); + maximize_(prevdist, -qh->min_vertex); + qh_mark_dupridges(qh, qh->newfacet_list, !qh_ALL); /* qh.facet_mergeset, creates ridges */ + /* qh_mark_dupridges is called a second time in qh_premerge */ + FOREACHmerge_(qh->facet_mergeset) { /* read-only */ + if (merge->mergetype != MRGdupridge) { + qh_fprintf(qh, qh->ferr, 6393, "qhull internal error (qh_getpinchedmerges): expecting MRGdupridge from qh_mark_dupridges. Got merge f%d f%d type %d\n", + getid_(merge->facet1), getid_(merge->facet2), merge->mergetype); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + /* dist is distance between vertices */ + pinched= qh_findbest_pinchedvertex(qh, merge, apex, &nearest, &dist /* qh.newfacet_list */); + if (pinched == apex && dist < qh_RATIOcoplanarapex*bestdist) { /* prefer coplanar apex since it always works */ + bestdist= dist/qh_RATIOcoplanarapex; + bestmerge= merge; + bestpinched= pinched; + bestvertex= nearest; + }else if (dist < bestdist) { + bestdist= dist; + bestmerge= merge; + bestpinched= pinched; + bestvertex= nearest; + } + } + result= False; + if (bestmerge && bestdist < maxdupdist) { + ratio= bestdist / prevdist; + if (ratio > qh_WIDEpinched) { + if (bestmerge->facet1->mergehorizon || bestmerge->facet2->mergehorizon) { /* e.g., rbox 175 C3,2e-13 t1539182828 | qhull d */ + trace1((qh, qh->ferr, 1051, "qh_getpinchedmerges: dupridge (MRGdupridge) of coplanar horizon would produce a wide merge (%.0fx) due to pinched vertices v%d and v%d (dist %2.2g) for f%d and f%d. qh_mergecycle_all will merge one or both facets\n", + ratio, bestpinched->id, bestvertex->id, bestdist, bestmerge->facet1->id, bestmerge->facet2->id)); + }else { + qh_fprintf(qh, qh->ferr, 7081, "qhull precision warning (qh_getpinchedmerges): pinched vertices v%d and v%d (dist %2.2g, %.0fx) would produce a wide merge for f%d and f%d. Will merge dupridge instead\n", + bestpinched->id, bestvertex->id, bestdist, ratio, bestmerge->facet1->id, bestmerge->facet2->id); + } + }else { + if (bestpinched == apex) { + trace2((qh, qh->ferr, 2063, "qh_getpinchedmerges: will make the apex a coplanar point. apex p%d(v%d) is the nearest vertex to v%d on dupridge. Dist %2.2g\n", + qh_pointid(qh, apex->point), apex->id, bestvertex->id, bestdist*qh_RATIOcoplanarapex)); + qh->coplanar_apex= apex->point; + *iscoplanar= True; + result= True; + }else if (qh_setin(bestmerge->facet1->vertices, bestpinched) != qh_setin(bestmerge->facet2->vertices, bestpinched)) { /* pinched in one facet but not the other facet */ + trace2((qh, qh->ferr, 2064, "qh_getpinchedmerges: will merge new facets to resolve dupridge between f%d and f%d with pinched v%d and v%d\n", + bestmerge->facet1->id, bestmerge->facet2->id, bestpinched->id, bestvertex->id)); + qh_appendvertexmerge(qh, bestpinched, bestvertex, MRGsubridge, bestdist, NULL, NULL); + result= True; + }else { + trace2((qh, qh->ferr, 2065, "qh_getpinchedmerges: will merge pinched v%d into v%d to resolve dupridge between f%d and f%d\n", + bestpinched->id, bestvertex->id, bestmerge->facet1->id, bestmerge->facet2->id)); + qh_appendvertexmerge(qh, bestpinched, bestvertex, MRGsubridge, bestdist, NULL, NULL); + result= True; + } + } + } + /* delete MRGdupridge, qh_mark_dupridges is called a second time in qh_premerge */ + while ((merge= (mergeT *)qh_setdellast(qh->facet_mergeset))) + qh_memfree(qh, merge, (int)sizeof(mergeT)); + return result; +}/* getpinchedmerges */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="hasmerge">-</a> + + qh_hasmerge( mergeset, mergetype, facetA, facetB ) + True if mergeset has mergetype for facetA and facetB +*/ +boolT qh_hasmerge(setT *mergeset, mergeType type, facetT *facetA, facetT *facetB) { + mergeT *merge, **mergep; + + FOREACHmerge_(mergeset) { + if (merge->mergetype == type) { + if (merge->facet1 == facetA && merge->facet2 == facetB) + return True; + if (merge->facet1 == facetB && merge->facet2 == facetA) + return True; + } + } + return False; +}/* hasmerge */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="hashridge">-</a> + + qh_hashridge(qh, 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(qhT *qh, setT *hashtable, int hashsize, ridgeT *ridge, vertexT *oldvertex) { + int hash; + ridgeT *ridgeA; + + hash= qh_gethash(qh, 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 */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="hashridge_find">-</a> + + qh_hashridge_find(qh, 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(qhT *qh, setT *hashtable, int hashsize, ridgeT *ridge, + vertexT *vertex, vertexT *oldvertex, int *hashslot) { + int hash; + ridgeT *ridgeA; + + *hashslot= 0; + zinc_(Zhashridge); + hash= qh_gethash(qh, 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 */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="initmergesets">-</a> + + qh_initmergesets(qh ) + initialize the merge sets + if 'all', include qh.degen_mergeset + + notes: + matches qh_freemergesets +*/ +void qh_initmergesets(qhT *qh /* qh.facet_mergeset,degen_mergeset,vertex_mergeset */) { + + if (qh->facet_mergeset || qh->degen_mergeset || qh->vertex_mergeset) { + qh_fprintf(qh, qh->ferr, 6386, "qhull internal error (qh_initmergesets): expecting NULL mergesets. Got qh.facet_mergeset (0x%x), qh.degen_mergeset (0x%x), qh.vertex_mergeset (0x%x)\n", + qh->facet_mergeset, qh->degen_mergeset, qh->vertex_mergeset); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + qh->degen_mergeset= qh_settemp(qh, qh->TEMPsize); + qh->vertex_mergeset= qh_settemp(qh, qh->TEMPsize); + qh->facet_mergeset= qh_settemp(qh, qh->TEMPsize); /* last temporary set for qh_forcedmerges */ +} /* initmergesets */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="makeridges">-</a> + + qh_makeridges(qh, facet ) + creates explicit ridges between simplicial facets + + returns: + facet with ridges and without qh_MERGEridge + ->simplicial is False + if facet was tested, new ridges are tested + + notes: + allows qh_MERGEridge flag + uses existing ridges + duplicate neighbors ok if ridges already exist (qh_mergecycle_ridges) + + see: + qh_mergecycle_ridges() + qh_rename_adjacentvertex for qh_merge_pinchedvertices + + 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 (previously processed by qh_mark_dupridges) +*/ +void qh_makeridges(qhT *qh, facetT *facet) { + facetT *neighbor, **neighborp; + ridgeT *ridge, **ridgep; + int neighbor_i, neighbor_n; + boolT toporient, mergeridge= False; + + if (!facet->simplicial) + return; + trace4((qh, 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_(qh, facet) { + if (neighbor == qh_MERGEridge) + continue; /* fixed by qh_mark_dupridges */ + else if (!neighbor->seen) { /* no current ridges */ + ridge= qh_newridge(qh); + ridge->vertices= qh_setnew_delnthsorted(qh, facet->vertices, qh->hull_dim, + neighbor_i, 0); + toporient= (boolT)(facet->toporient ^ (neighbor_i & 0x1)); + if (toporient) { + ridge->top= facet; + ridge->bottom= neighbor; + ridge->simplicialtop= True; + ridge->simplicialbot= neighbor->simplicial; + }else { + ridge->top= neighbor; + ridge->bottom= facet; + ridge->simplicialtop= neighbor->simplicial; + ridge->simplicialbot= True; + } + if (facet->tested && !mergeridge) + ridge->tested= True; +#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; + ridge->simplicialtop= True; + ridge->simplicialbot= neighbor->simplicial; + }else { + ridge->top= facet; + ridge->bottom= neighbor; + ridge->simplicialtop= neighbor->simplicial; + ridge->simplicialbot= True; + } +#endif + qh_setappend(qh, &(facet->ridges), ridge); + trace5((qh, qh->ferr, 5005, "makeridges: appended r%d to ridges for f%d. Next is ridges for neighbor f%d\n", + ridge->id, facet->id, neighbor->id)); + qh_setappend(qh, &(neighbor->ridges), ridge); + if (qh->ridge_id == qh->traceridge_id) + qh->traceridge= ridge; + } + } + if (mergeridge) { + while (qh_setdel(facet->neighbors, qh_MERGEridge)) + ; /* delete each one */ + } +} /* makeridges */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="mark_dupridges">-</a> + + qh_mark_dupridges(qh, facetlist, allmerges ) + add duplicated ridges to qh.facet_mergeset + facet-dupridge is true if it contains a subridge shared by more than one new facet + for each such facet, one has a neighbor marked qh_MERGEridge + allmerges is true if merging dupridges + allmerges is false if merging pinched vertices followed by retry addpoint + qh_mark_dupridges will be called again if pinched vertices not found + + returns: + dupridges on qh.facet_mergeset (MRGdupridge) + f.mergeridge and f.mergeridge2 set for facet + f.mergeridge set for neighbor + if allmerges is true + make ridges for facets with dupridges as marked by qh_MERGEridge and both sides facet->dupridge + removes qh_MERGEridge from neighbor sets + + notes: + called by qh_premerge and qh_getpinchedmerges + dupridges are due to duplicate subridges + i.e. a subridge occurs in more than two horizon ridges. + i.e., a ridge has more than two neighboring facets + dupridges occur in at least two cases + 1) a pinched horizon with nearly adjacent vertices -> merge the vertices (qh_getpinchedmerges) + 2) more than one newfacet for a horizon face -> merge coplanar facets (qh_premerge) + qh_matchdupridge previously identified the furthest apart pair of facets to retain + they must have a matching subridge and the same orientation + only way to set facet->mergeridge and mergeridge2 + uses qh.visit_id + + design: + for all facets on facetlist + if facet contains a dupridge + for each neighbor of facet + if neighbor marked qh_MERGEridge (one side of the merge) + set facet->mergeridge + else + if neighbor contains a dupridge + and the back link is qh_MERGEridge + append dupridge to qh.facet_mergeset + exit if !allmerges for repeating qh_mark_dupridges later + for each dupridge + make ridge sets in preparation for merging + remove qh_MERGEridge from neighbor set + for each dupridge + restore the missing neighbor from the neighbor set that was qh_MERGEridge + add the missing ridge for this neighbor +*/ +void qh_mark_dupridges(qhT *qh, facetT *facetlist, boolT allmerges) { + facetT *facet, *neighbor, **neighborp; + int nummerge=0; + mergeT *merge, **mergep; + + trace4((qh, qh->ferr, 4028, "qh_mark_dupridges: identify dupridges in facetlist f%d, allmerges? %d\n", + facetlist->id, allmerges)); + FORALLfacet_(facetlist) { /* not necessary for first call */ + facet->mergeridge2= False; + facet->mergeridge= False; + } + FORALLfacet_(facetlist) { + if (facet->dupridge) { + FOREACHneighbor_(facet) { + if (neighbor == qh_MERGEridge) { + facet->mergeridge= True; + continue; + } + if (neighbor->dupridge) { + if (!qh_setin(neighbor->neighbors, facet)) { /* i.e., it is qh_MERGEridge, neighbors are distinct */ + qh_appendmergeset(qh, facet, neighbor, MRGdupridge, 0.0, 1.0); + facet->mergeridge2= True; + facet->mergeridge= True; + nummerge++; + }else if (qh_setequal(facet->vertices, neighbor->vertices)) { /* neighbors are the same except for horizon and qh_MERGEridge, see QH7085 */ + trace3((qh, qh->ferr, 3043, "qh_mark_dupridges): dupridge due to duplicate vertices for subridges f%d and f%d\n", + facet->id, neighbor->id)); + qh_appendmergeset(qh, facet, neighbor, MRGdupridge, 0.0, 1.0); + facet->mergeridge2= True; + facet->mergeridge= True; + nummerge++; + break; /* same for all neighbors */ + } + } + } + } + } + if (!nummerge) + return; + if (!allmerges) { + trace1((qh, qh->ferr, 1012, "qh_mark_dupridges: found %d duplicated ridges (MRGdupridge) for qh_getpinchedmerges\n", nummerge)); + return; + } + trace1((qh, qh->ferr, 1048, "qh_mark_dupridges: found %d duplicated ridges (MRGdupridge) for qh_premerge. Prepare facets for merging\n", nummerge)); + /* make ridges in preparation for merging */ + FORALLfacet_(facetlist) { + if (facet->mergeridge && !facet->mergeridge2) + qh_makeridges(qh, facet); + } + trace3((qh, qh->ferr, 3075, "qh_mark_dupridges: restore missing neighbors and ridges due to qh_MERGEridge\n")); + FOREACHmerge_(qh->facet_mergeset) { /* restore the missing neighbors */ + if (merge->mergetype == MRGdupridge) { /* only between simplicial facets */ + if (merge->facet2->mergeridge2 && qh_setin(merge->facet2->neighbors, merge->facet1)) { + /* Due to duplicate or multiple subridges, e.g., ../eg/qtest.sh t712682 '200 s W1e-13 C1,1e-13 D5' 'd' + merge->facet1: - neighboring facets: f27779 f59186 f59186 f59186 MERGEridge f59186 + merge->facet2: - neighboring facets: f27779 f59100 f59100 f59100 f59100 f59100 + or, ../eg/qtest.sh 100 '500 s W1e-13 C1,1e-13 D4' 'd' + both facets will be degenerate after merge, consider for special case handling + */ + qh_fprintf(qh, qh->ferr, 6361, "qhull topological error (qh_mark_dupridges): multiple dupridges for f%d and f%d, including reverse\n", + merge->facet1->id, merge->facet2->id); + qh_errexit2(qh, qh_ERRtopology, merge->facet1, merge->facet2); + }else + qh_setappend(qh, &merge->facet2->neighbors, merge->facet1); + qh_makeridges(qh, merge->facet1); /* and the missing ridges */ + } + } +} /* mark_dupridges */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="maybe_duplicateridge">-</a> + + qh_maybe_duplicateridge(qh, ridge ) + add MRGvertices if neighboring facet has another ridge with the same vertices + + returns: + adds rename requests to qh.vertex_mergeset + + notes: + called by qh_renamevertex + nop if 2-D + expensive test + Duplicate ridges may lead to new facets with same vertex set (QH7084), will try merging vertices + same as qh_maybe_duplicateridges + + design: + for the two neighbors + if non-simplicial + for each ridge with the same first and last vertices (max id and min id) + if the remaining vertices are the same + get the closest pair of vertices + add to vertex_mergeset for merging +*/ +void qh_maybe_duplicateridge(qhT *qh, ridgeT *ridgeA) { + ridgeT *ridge, **ridgep; + vertexT *vertex, *pinched; + facetT *neighbor; + coordT dist; + int i, k, last= qh->hull_dim-2; + + if (qh->hull_dim < 3 ) + return; + + for (neighbor= ridgeA->top, i=0; i<2; neighbor= ridgeA->bottom, i++) { + if (!neighbor->simplicial && neighbor->nummerge > 0) { /* skip degenerate neighbors with both new and old vertices that will be merged */ + FOREACHridge_(neighbor->ridges) { + if (ridge != ridgeA && SETfirst_(ridge->vertices) == SETfirst_(ridgeA->vertices)) { + if (SETelem_(ridge->vertices, last) == SETelem_(ridgeA->vertices, last)) { + for (k=1; k<last; k++) { + if (SETelem_(ridge->vertices, k) != SETelem_(ridgeA->vertices, k)) + break; + } + if (k == last) { + vertex= qh_findbest_ridgevertex(qh, ridge, &pinched, &dist); + trace2((qh, qh->ferr, 2069, "qh_maybe_duplicateridge: will merge v%d into v%d (dist %2.2g) due to duplicate ridges r%d/r%d with the same vertices. mergevertex set\n", + pinched->id, vertex->id, dist, ridgeA->id, ridge->id, ridgeA->top->id, ridgeA->bottom->id, ridge->top->id, ridge->bottom->id)); + qh_appendvertexmerge(qh, pinched, vertex, MRGvertices, dist, ridgeA, ridge); + ridge->mergevertex= True; /* disables check for duplicate vertices in qh_checkfacet */ + ridgeA->mergevertex= True; + } + } + } + } + } + } +} /* maybe_duplicateridge */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="maybe_duplicateridges">-</a> + + qh_maybe_duplicateridges(qh, facet ) + if Q15, add MRGvertices if facet has ridges with the same vertices + + returns: + adds rename requests to qh.vertex_mergeset + + notes: + called at end of qh_mergefacet and qh_mergecycle_all + only enabled if qh.CHECKduplicates ('Q15') and 3-D or more + expensive test, not worth it + same as qh_maybe_duplicateridge + + design: + for all ridge pairs in facet + if the same first and last vertices (max id and min id) + if the remaining vertices are the same + get the closest pair of vertices + add to vertex_mergeset for merging +*/ +void qh_maybe_duplicateridges(qhT *qh, facetT *facet) { + facetT *otherfacet; + ridgeT *ridge, *ridge2; + vertexT *vertex, *pinched; + coordT dist; + int ridge_i, ridge_n, i, k, last_v= qh->hull_dim-2; + + if (qh->hull_dim < 3 || !qh->CHECKduplicates) + return; + + FOREACHridge_i_(qh, facet->ridges) { + otherfacet= otherfacet_(ridge, facet); + if (otherfacet->degenerate || otherfacet->redundant || otherfacet->dupridge || otherfacet->flipped) /* will merge */ + continue; + for (i=ridge_i+1; i < ridge_n; i++) { + ridge2= SETelemt_(facet->ridges, i, ridgeT); + otherfacet= otherfacet_(ridge2, facet); + if (otherfacet->degenerate || otherfacet->redundant || otherfacet->dupridge || otherfacet->flipped) /* will merge */ + continue; + /* optimize qh_setequal(ridge->vertices, ridge2->vertices) */ + if (SETelem_(ridge->vertices, last_v) == SETelem_(ridge2->vertices, last_v)) { /* SETfirst is likely to be the same */ + if (SETfirst_(ridge->vertices) == SETfirst_(ridge2->vertices)) { + for (k=1; k<last_v; k++) { + if (SETelem_(ridge->vertices, k) != SETelem_(ridge2->vertices, k)) + break; + } + if (k == last_v) { + vertex= qh_findbest_ridgevertex(qh, ridge, &pinched, &dist); + if (ridge->top == ridge2->bottom && ridge->bottom == ridge2->top) { + /* proof that ridges may have opposite orientation */ + trace2((qh, qh->ferr, 2088, "qh_maybe_duplicateridges: will merge v%d into v%d (dist %2.2g) due to opposite oriented ridges r%d/r%d for f%d and f%d\n", + pinched->id, vertex->id, dist, ridge->id, ridge2->id, ridge->top->id, ridge->bottom->id)); + }else { + trace2((qh, qh->ferr, 2083, "qh_maybe_duplicateridges: will merge v%d into v%d (dist %2.2g) due to duplicate ridges with the same vertices r%d/r%d in merged facet f%d\n", + pinched->id, vertex->id, dist, ridge->id, ridge2->id, facet->id)); + } + qh_appendvertexmerge(qh, pinched, vertex, MRGvertices, dist, ridge, ridge2); + ridge->mergevertex= True; /* disables check for duplicate vertices in qh_checkfacet */ + ridge2->mergevertex= True; + } + } + } + } + } +} /* maybe_duplicateridges */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="maydropneighbor">-</a> + + qh_maydropneighbor(qh, facet ) + drop neighbor relationship if ridge was deleted between a non-simplicial facet and its neighbors + + returns: + for deleted ridges + ridges made for simplicial neighbors + neighbor sets updated + appends degenerate facets to qh.facet_mergeset + + notes: + called by qh_renamevertex + assumes neighbors do not include qh_MERGEridge (qh_makeridges) + 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 non-simplicial 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(qhT *qh, facetT *facet) { + ridgeT *ridge, **ridgep; + facetT *neighbor, **neighborp; + + qh->visit_id++; + trace4((qh, qh->ferr, 4029, "qh_maydropneighbor: test f%d for no ridges to a neighbor\n", + facet->id)); + if (facet->simplicial) { + qh_fprintf(qh, qh->ferr, 6278, "qhull internal error (qh_maydropneighbor): not valid for simplicial f%d while adding furthest p%d\n", + facet->id, qh->furthest_id); + qh_errexit(qh, qh_ERRqhull, facet, NULL); + } + FOREACHridge_(facet->ridges) { + ridge->top->visitid= qh->visit_id; + ridge->bottom->visitid= qh->visit_id; + } + FOREACHneighbor_(facet) { + if (neighbor->visible) { + qh_fprintf(qh, qh->ferr, 6358, "qhull internal error (qh_maydropneighbor): facet f%d has deleted neighbor f%d (qh.visible_list)\n", + facet->id, neighbor->id); + qh_errexit2(qh, qh_ERRqhull, facet, neighbor); + } + if (neighbor->visitid != qh->visit_id) { + trace2((qh, qh->ferr, 2104, "qh_maydropneighbor: facets f%d and f%d are no longer neighbors while adding furthest p%d\n", + facet->id, neighbor->id, qh->furthest_id)); + if (neighbor->simplicial) { + qh_fprintf(qh, qh->ferr, 6280, "qhull internal error (qh_maydropneighbor): not valid for simplicial neighbor f%d of f%d while adding furthest p%d\n", + neighbor->id, facet->id, qh->furthest_id); + qh_errexit2(qh, qh_ERRqhull, neighbor, facet); + } + zinc_(Zdropneighbor); + qh_setdel(neighbor->neighbors, facet); + if (qh_setsize(qh, neighbor->neighbors) < qh->hull_dim) { + zinc_(Zdropdegen); + qh_appendmergeset(qh, neighbor, neighbor, MRGdegen, 0.0, qh_ANGLEnone); + trace2((qh, qh->ferr, 2023, "qh_maydropneighbors: f%d is degenerate.\n", neighbor->id)); + } + qh_setdel(facet->neighbors, neighbor); + neighborp--; /* repeat, deleted a neighbor */ + } + } + if (qh_setsize(qh, facet->neighbors) < qh->hull_dim) { + zinc_(Zdropdegen); + qh_appendmergeset(qh, facet, facet, MRGdegen, 0.0, qh_ANGLEnone); + trace2((qh, qh->ferr, 2024, "qh_maydropneighbors: f%d is degenerate.\n", facet->id)); + } +} /* maydropneighbor */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="merge_degenredundant">-</a> + + qh_merge_degenredundant(qh) + merge all degenerate and redundant facets + qh.degen_mergeset contains merges from qh_test_degen_neighbors, qh_test_redundant_neighbors, and qh_degen_redundant_facet + + 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 + check for coplanar and convex neighbors afterwards + + 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(qhT *qh) { + int size; + mergeT *merge; + facetT *bestneighbor, *facet1, *facet2, *facet3; + realT dist, mindist, maxdist; + vertexT *vertex, **vertexp; + int nummerges= 0; + mergeType mergetype; + setT *mergedfacets; + + trace2((qh, qh->ferr, 2095, "qh_merge_degenredundant: merge %d degenerate, redundant, and mirror facets\n", + qh_setsize(qh, qh->degen_mergeset))); + mergedfacets= qh_settemp(qh, qh->TEMPsize); + while ((merge= (mergeT *)qh_setdellast(qh->degen_mergeset))) { + facet1= merge->facet1; + facet2= merge->facet2; + mergetype= merge->mergetype; + qh_memfree(qh, merge, (int)sizeof(mergeT)); /* 'merge' is invalidated */ + if (facet1->visible) + continue; + facet1->degenerate= False; + facet1->redundant= False; + if (qh->TRACEmerge-1 == zzval_(Ztotmerge)) + qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel; + if (mergetype == MRGredundant) { + zinc_(Zredundant); + facet3= qh_getreplacement(qh, facet2); /* the same facet if !facet2.visible */ + if (!facet3) { + qh_fprintf(qh, qh->ferr, 6097, "qhull internal error (qh_merge_degenredunant): f%d is redundant but visible f%d has no replacement\n", + facet1->id, getid_(facet2)); + qh_errexit2(qh, qh_ERRqhull, facet1, facet2); + } + qh_setunique(qh, &mergedfacets, facet3); + if (facet1 == facet3) { + continue; + } + trace2((qh, qh->ferr, 2025, "qh_merge_degenredundant: merge redundant f%d into f%d (arg f%d)\n", + facet1->id, facet3->id, facet2->id)); + qh_mergefacet(qh, facet1, facet3, mergetype, NULL, NULL, !qh_MERGEapex); + /* merge distance is already accounted for */ + nummerges++; + }else { /* mergetype == MRGdegen or MRGmirror, other merges may have fixed */ + if (!(size= qh_setsize(qh, facet1->neighbors))) { + zinc_(Zdelfacetdup); + trace2((qh, qh->ferr, 2026, "qh_merge_degenredundant: facet f%d has no neighbors. Deleted\n", facet1->id)); + qh_willdelete(qh, facet1, NULL); + FOREACHvertex_(facet1->vertices) { + qh_setdel(vertex->neighbors, facet1); + if (!SETfirst_(vertex->neighbors)) { + zinc_(Zdegenvertex); + trace2((qh, 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, &qh->del_vertices, vertex); + } + } + nummerges++; + }else if (size < qh->hull_dim) { + bestneighbor= qh_findbestneighbor(qh, facet1, &dist, &mindist, &maxdist); + trace2((qh, 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(qh, facet1, bestneighbor, mergetype, &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 */ + } + } + qh_settempfree(qh, &mergedfacets); + return nummerges; +} /* merge_degenredundant */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="merge_nonconvex">-</a> + + qh_merge_nonconvex(qh, 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 + + notes: + mergetype is MRGcoplanar..MRGconvex + + 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(qhT *qh, facetT *facet1, facetT *facet2, mergeType mergetype) { + facetT *bestfacet, *bestneighbor, *neighbor, *merging, *merged; + realT dist, dist2, mindist, mindist2, maxdist, maxdist2; + + if (mergetype < MRGcoplanar || mergetype > MRGconcavecoplanar) { + qh_fprintf(qh, qh->ferr, 6398, "qhull internal error (qh_merge_nonconvex): expecting mergetype MRGcoplanar..MRGconcavecoplanar. Got merge f%d and f%d type %d\n", + facet1->id, facet2->id, mergetype); + qh_errexit2(qh, qh_ERRqhull, facet1, facet2); + } + if (qh->TRACEmerge-1 == zzval_(Ztotmerge)) + qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel; + trace3((qh, 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(qh, bestfacet, &dist, &mindist, &maxdist); + neighbor= qh_findbestneighbor(qh, facet2, &dist2, &mindist2, &maxdist2); + if (dist < dist2) { + merging= bestfacet; + merged= bestneighbor; + }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, 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)); + merging= bestfacet; + merged= bestneighbor; + }else { + merging= facet2; + merged= neighbor; + dist= dist2; + mindist= mindist2; + maxdist= maxdist2; + } + qh_mergefacet(qh, merging, merged, mergetype, &mindist, &maxdist, !qh_MERGEapex); + /* caller merges qh_degenredundant */ + 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 if (mergetype == MRGconcavecoplanar) { + zinc_(Zconcavecoplanar); + wadd_(Wconcavecoplanartot, dist); + wmax_(Wconcavecoplanarmax, dist); + }else { /* MRGcoplanar */ + zinc_(Zcoplanar); + wadd_(Wcoplanartot, dist); + wmax_(Wcoplanarmax, dist); + } + } +} /* merge_nonconvex */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="merge_pinchedvertices">-</a> + + qh_merge_pinchedvertices(qh, apex ) + merge pinched vertices in qh.vertex_mergeset to avoid qh_forcedmerges of dupridges + + notes: + only called by qh_all_vertexmerges + hull_dim >= 3 + + design: + make vertex neighbors if necessary + for each pinched vertex + determine the ridges for the pinched vertex (make ridges as needed) + merge the pinched vertex into the horizon vertex + merge the degenerate and redundant facets that result + check and resolve new dupridges +*/ +void qh_merge_pinchedvertices(qhT *qh, int apexpointid /* qh.newfacet_list */) { + mergeT *merge, *mergeA, **mergeAp; + vertexT *vertex, *vertex2; + realT dist; + boolT firstmerge= True; + + qh_vertexneighbors(qh); + if (qh->visible_list || qh->newfacet_list || qh->newvertex_list) { + qh_fprintf(qh, qh->ferr, 6402, "qhull internal error (qh_merge_pinchedvertices): qh.visible_list (f%d), newfacet_list (f%d), or newvertex_list (v%d) not empty\n", + getid_(qh->visible_list), getid_(qh->newfacet_list), getid_(qh->newvertex_list)); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + qh->visible_list= qh->newfacet_list= qh->facet_tail; + qh->newvertex_list= qh->vertex_tail; + qh->isRenameVertex= True; /* disable duplicate ridge vertices check in qh_checkfacet */ + while ((merge= qh_next_vertexmerge(qh /* qh.vertex_mergeset */))) { /* only one at a time from qh_getpinchedmerges */ + if (qh->TRACEmerge-1 == zzval_(Ztotmerge)) + qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel; + if (merge->mergetype == MRGsubridge) { + zzinc_(Zpinchedvertex); + trace1((qh, qh->ferr, 1050, "qh_merge_pinchedvertices: merge one of %d pinched vertices before adding apex p%d. Try to resolve duplicate ridges in newfacets\n", + qh_setsize(qh, qh->vertex_mergeset)+1, apexpointid)); + qh_remove_mergetype(qh, qh->vertex_mergeset, MRGsubridge); + }else { + zzinc_(Zpinchduplicate); + if (firstmerge) + trace1((qh, qh->ferr, 1056, "qh_merge_pinchedvertices: merge %d pinched vertices from dupridges in merged facets, apex p%d\n", + qh_setsize(qh, qh->vertex_mergeset)+1, apexpointid)); + firstmerge= False; + } + vertex= merge->vertex1; + vertex2= merge->vertex2; + dist= merge->distance; + qh_memfree(qh, merge, (int)sizeof(mergeT)); /* merge is invalidated */ + qh_rename_adjacentvertex(qh, vertex, vertex2, dist); +#ifndef qh_NOtrace + if (qh->IStracing >= 2) { + FOREACHmergeA_(qh->degen_mergeset) { + if (mergeA->mergetype== MRGdegen) { + qh_fprintf(qh, qh->ferr, 2072, "qh_merge_pinchedvertices: merge degenerate f%d into an adjacent facet\n", mergeA->facet1->id); + }else { + qh_fprintf(qh, qh->ferr, 2084, "qh_merge_pinchedvertices: merge f%d into f%d mergeType %d\n", mergeA->facet1->id, mergeA->facet2->id, mergeA->mergetype); + } + } + } +#endif + qh_merge_degenredundant(qh); /* simplicial facets with both old and new vertices */ + } + qh->isRenameVertex= False; +}/* merge_pinchedvertices */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="merge_twisted">-</a> + + qh_merge_twisted(qh, facet1, facet2 ) + remove twisted ridge between facet1 into facet2 or report error + + returns: + merges one of the facets into the best neighbor + + notes: + a twisted ridge has opposite vertices that are convex and concave + + design: + find best neighbors for both facets + error if wide merge + merge the nearest facet into its best neighbor + update statistics +*/ +void qh_merge_twisted(qhT *qh, facetT *facet1, facetT *facet2) { + facetT *neighbor2, *neighbor, *merging, *merged; + vertexT *bestvertex, *bestpinched; + realT dist, dist2, mindist, mindist2, maxdist, maxdist2, mintwisted, bestdist; + + if (qh->TRACEmerge-1 == zzval_(Ztotmerge)) + qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel; + trace3((qh, qh->ferr, 3050, "qh_merge_twisted: merge #%d for twisted f%d and f%d\n", + zzval_(Ztotmerge) + 1, facet1->id, facet2->id)); + /* twisted */ + neighbor= qh_findbestneighbor(qh, facet1, &dist, &mindist, &maxdist); + neighbor2= qh_findbestneighbor(qh, facet2, &dist2, &mindist2, &maxdist2); + mintwisted= qh_RATIOtwisted * qh->ONEmerge; + maximize_(mintwisted, facet1->maxoutside); + maximize_(mintwisted, facet2->maxoutside); + if (dist > mintwisted && dist2 > mintwisted) { + bestdist= qh_vertex_bestdist2(qh, facet1->vertices, &bestvertex, &bestpinched); + if (bestdist > mintwisted) { + qh_fprintf(qh, qh->ferr, 6417, "qhull precision error (qh_merge_twisted): twisted facet f%d does not contain pinched vertices. Too wide to merge into neighbor. mindist %2.2g maxdist %2.2g vertexdist %2.2g maxpinched %2.2g neighbor f%d mindist %2.2g maxdist %2.2g\n", + facet1->id, mindist, maxdist, bestdist, mintwisted, facet2->id, mindist2, maxdist2); + }else { + qh_fprintf(qh, qh->ferr, 6418, "qhull precision error (qh_merge_twisted): twisted facet f%d with pinched vertices. Could merge vertices, but too wide to merge into neighbor. mindist %2.2g maxdist %2.2g vertexdist %2.2g neighbor f%d mindist %2.2g maxdist %2.2g\n", + facet1->id, mindist, maxdist, bestdist, facet2->id, mindist2, maxdist2); + } + qh_errexit2(qh, qh_ERRwide, facet1, facet2); + } + if (dist < dist2) { + merging= facet1; + merged= neighbor; + }else { + /* ignores qh.AVOIDold ('Q4') */ + merging= facet2; + merged= neighbor2; + dist= dist2; + mindist= mindist2; + maxdist= maxdist2; + } + qh_mergefacet(qh, merging, merged, MRGtwisted, &mindist, &maxdist, !qh_MERGEapex); + /* caller merges qh_degenredundant */ + zinc_(Ztwisted); + wadd_(Wtwistedtot, dist); + wmax_(Wtwistedmax, dist); +} /* merge_twisted */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="mergecycle">-</a> + + qh_mergecycle(qh, 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 + + notes: + only called by qh_mergecycle_all for multiple, same cycle facets + see qh_mergefacet + + 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(qhT *qh, facetT *samecycle, facetT *newfacet) { + int traceonce= False, tracerestore= 0; + vertexT *apex; +#ifndef qh_NOtrace + facetT *same; +#endif + + zzinc_(Ztotmerge); + if (qh->REPORTfreq2 && qh->POSTmerging) { + if (zzval_(Ztotmerge) > qh->mergereport + qh->REPORTfreq2) + qh_tracemerging(qh); + } +#ifndef qh_NOtrace + if (qh->TRACEmerge == zzval_(Ztotmerge)) + qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel; + trace2((qh, 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, 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, qh->ferr, 8069, " same cycle:"); + FORALLsame_cycle_(samecycle) + qh_fprintf(qh, qh->ferr, 8070, " f%d", same->id); + qh_fprintf(qh, qh->ferr, 8071, "\n"); + } + if (qh->IStracing >=4) + qh_errprint(qh, "MERGING CYCLE", samecycle, newfacet, NULL, NULL); +#endif /* !qh_NOtrace */ + if (newfacet->tricoplanar) { + if (!qh->TRInormals) { + qh_fprintf(qh, qh->ferr, 6224, "qhull internal error (qh_mergecycle): does not work for tricoplanar facets. Use option 'Q11'\n"); + qh_errexit(qh, qh_ERRqhull, newfacet, NULL); + } + newfacet->tricoplanar= False; + newfacet->keepcentrum= False; + } + if (qh->CHECKfrequently) + qh_checkdelridge(qh); + if (!qh->VERTEXneighbors) + qh_vertexneighbors(qh); + apex= SETfirstt_(samecycle->vertices, vertexT); + qh_makeridges(qh, newfacet); + qh_mergecycle_neighbors(qh, samecycle, newfacet); + qh_mergecycle_ridges(qh, samecycle, newfacet); + qh_mergecycle_vneighbors(qh, samecycle, newfacet); + if (SETfirstt_(newfacet->vertices, vertexT) != apex) + qh_setaddnth(qh, &newfacet->vertices, 0, apex); /* apex has last id */ + if (!newfacet->newfacet) + qh_newvertices(qh, newfacet->vertices); + qh_mergecycle_facets(qh, samecycle, newfacet); + qh_tracemerge(qh, samecycle, newfacet, MRGcoplanarhorizon); + /* check for degen_redundant_neighbors after qh_forcedmerges() */ + if (traceonce) { + qh_fprintf(qh, qh->ferr, 8072, "qh_mergecycle: end of trace facet\n"); + qh->IStracing= tracerestore; + } +} /* mergecycle */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="mergecycle_all">-</a> + + qh_mergecycle_all(qh, 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 + + notes: + called by qh_premerge + calls qh_mergecycle for multiple, same cycle facets + + design: + for each facet on facetlist + skip facets with dupridges 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 dupridges from samecycle + merge samecycle into horizon (deletes facets from facetlist) +*/ +void qh_mergecycle_all(qhT *qh, facetT *facetlist, boolT *wasmerge) { + facetT *facet, *same, *prev, *horizon, *newfacet; + facetT *samecycle= NULL, *nextfacet, *nextsame; + vertexT *apex, *vertex, **vertexp; + int cycles=0, total=0, facets, nummerge, numdegen= 0; + + trace2((qh, qh->ferr, 2031, "qh_mergecycle_all: merge new facets into coplanar horizon facets. Bulk merge a cycle of facets with the same horizon facet\n")); + for (facet=facetlist; facet && (nextfacet= facet->next); facet= nextfacet) { + if (facet->normal) + continue; + if (!facet->mergehorizon) { + qh_fprintf(qh, qh->ferr, 6225, "qhull internal error (qh_mergecycle_all): f%d without normal\n", facet->id); + qh_errexit(qh, qh_ERRqhull, facet, NULL); + } + horizon= SETfirstt_(facet->neighbors, facetT); + if (facet->f.samecycle == facet) { + if (qh->TRACEmerge-1 == zzval_(Ztotmerge)) + qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel; + 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(qh, facet, horizon, MRGcoplanarhorizon, 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(qh, 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(qh, samecycle, horizon); + nummerge= horizon->nummerge + facets; + if (nummerge > qh_MAXnummerge) + horizon->nummerge= qh_MAXnummerge; + else + horizon->nummerge= (short unsigned int)nummerge; /* limited to 9 bits by qh_MAXnummerge, -Wconversion */ + zzinc_(Zcyclehorizon); + total += facets; + zzadd_(Zcyclefacettot, facets); + zmax_(Zcyclefacetmax, facets); + } + cycles++; + } + if (cycles) { + FORALLnew_facets { + /* qh_maybe_duplicateridges postponed since qh_mergecycle_ridges deletes ridges without calling qh_delridge_merge */ + if (newfacet->coplanarhorizon) { + qh_test_redundant_neighbors(qh, newfacet); + qh_maybe_duplicateridges(qh, newfacet); + newfacet->coplanarhorizon= False; + } + } + numdegen += qh_merge_degenredundant(qh); + *wasmerge= True; + trace1((qh, qh->ferr, 1013, "qh_mergecycle_all: merged %d same cycles or facets into coplanar horizons and %d degenredundant facets\n", + cycles, numdegen)); + } +} /* mergecycle_all */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="mergecycle_facets">-</a> + + qh_mergecycle_facets(qh, 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(qhT *qh, facetT *samecycle, facetT *newfacet) { + facetT *same, *next; + + trace4((qh, qh->ferr, 4030, "qh_mergecycle_facets: make newfacet new and samecycle deleted\n")); + qh_removefacet(qh, newfacet); /* append as a newfacet to end of qh->facet_list */ + qh_appendfacet(qh, 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(qh, same, newfacet); + } + if (newfacet->center + && qh_setsize(qh, newfacet->vertices) <= qh->hull_dim + qh_MAXnewcentrum) { + qh_memfree(qh, newfacet->center, qh->normal_size); + newfacet->center= NULL; + } + trace3((qh, qh->ferr, 3004, "qh_mergecycle_facets: merged facets from cycle f%d into f%d\n", + samecycle->id, newfacet->id)); +} /* mergecycle_facets */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="mergecycle_neighbors">-</a> + + qh_mergecycle_neighbors(qh, 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(qhT *qh, 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(qh, samecycle); + same->visitid= samevisitid; + } + newfacet->visitid= ++qh->visit_id; + trace4((qh, 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(qh, newfacet->neighbors); + + trace4((qh, 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(qh, &newfacet->neighbors, neighbor); + qh_setreplace(qh, 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(qh, 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(qh, &neighbor->neighbors, newfacet); + qh_setappend(qh, &newfacet->neighbors, neighbor); + neighbor->visitid= qh->visit_id; + newneighbors++; + } + } + } + } + trace2((qh, qh->ferr, 2032, "qh_mergecycle_neighbors: deleted %d neighbors and added %d\n", + delneighbors, newneighbors)); +} /* mergecycle_neighbors */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="mergecycle_ridges">-</a> + + qh_mergecycle_ridges(qh, 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 + qh_checkdelridge called by qh_mergecycle + + 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 simplicial 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(qhT *qh, 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 if !qh_NOmem by qh_memfree_() */ + + trace4((qh, 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(qh, newfacet->ridges); + + trace4((qh, 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(qh, &newfacet->ridges, ridge); + numold++; /* already set by qh_mergecycle_neighbors */ + continue; + }else { + qh_fprintf(qh, qh->ferr, 6098, "qhull internal error (qh_mergecycle_ridges): bad ridge r%d\n", ridge->id); + qh_errexit(qh, qh_ERRqhull, NULL, ridge); + } + if (neighbor == newfacet) { + if (qh->traceridge == ridge) + qh->traceridge= NULL; + qh_setfree(qh, &(ridge->vertices)); + qh_memfree_(qh, ridge, (int)sizeof(ridgeT), freelistp); + numold++; + }else if (neighbor->visitid == samevisitid) { + qh_setdel(neighbor->ridges, ridge); + if (qh->traceridge == ridge) + qh->traceridge= NULL; + qh_setfree(qh, &(ridge->vertices)); + qh_memfree_(qh, ridge, (int)sizeof(ridgeT), freelistp); + numold++; + }else { + qh_setappend(qh, &newfacet->ridges, ridge); + numold++; + } + } + if (same->ridges) + qh_settruncate(qh, same->ridges, 0); + if (!same->simplicial) + continue; + FOREACHneighbor_i_(qh, same) { /* note: !newfact->simplicial */ + if (neighbor->visitid != samevisitid && neighbor->simplicial) { + ridge= qh_newridge(qh); + ridge->vertices= qh_setnew_delnthsorted(qh, same->vertices, qh->hull_dim, + neighbor_i, 0); + toporient= (boolT)(same->toporient ^ (neighbor_i & 0x1)); + if (toporient) { + ridge->top= newfacet; + ridge->bottom= neighbor; + ridge->simplicialbot= True; + }else { + ridge->top= neighbor; + ridge->bottom= newfacet; + ridge->simplicialtop= True; + } + qh_setappend(qh, &(newfacet->ridges), ridge); + qh_setappend(qh, &(neighbor->ridges), ridge); + if (qh->ridge_id == qh->traceridge_id) + qh->traceridge= ridge; + numnew++; + } + } + } + + trace2((qh, qh->ferr, 2033, "qh_mergecycle_ridges: found %d old ridges and %d new ones\n", + numold, numnew)); +} /* mergecycle_ridges */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="mergecycle_vneighbors">-</a> + + qh_mergecycle_vneighbors(qh, 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(qhT *qh, facetT *samecycle, facetT *newfacet) { + facetT *neighbor, **neighborp; + unsigned int mergeid; + vertexT *vertex, **vertexp, *apex; + setT *vertices; + + trace4((qh, qh->ferr, 4035, "qh_mergecycle_vneighbors: update vertex neighbors for newfacet\n")); + mergeid= qh->visit_id - 1; + newfacet->visitid= mergeid; + vertices= qh_basevertices(qh, samecycle); /* temp */ + apex= SETfirstt_(samecycle->vertices, vertexT); + qh_setappend(qh, &vertices, apex); + FOREACHvertex_(vertices) { + vertex->delridge= True; + FOREACHneighbor_(vertex) { + if (neighbor->visitid == mergeid) + SETref_(neighbor)= NULL; + } + qh_setcompact(qh, vertex->neighbors); + qh_setappend(qh, &vertex->neighbors, newfacet); + if (!SETsecond_(vertex->neighbors)) { + zinc_(Zcyclevertex); + trace2((qh, 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, &qh->del_vertices, vertex); + } + } + qh_settempfree(qh, &vertices); + trace3((qh, qh->ferr, 3005, "qh_mergecycle_vneighbors: merged vertices from cycle f%d into f%d\n", + samecycle->id, newfacet->id)); +} /* mergecycle_vneighbors */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="mergefacet">-</a> + + qh_mergefacet(qh, facet1, facet2, mergetype, mindist, maxdist, mergeapex ) + merges facet1 into facet2 + mergeapex==qh_MERGEapex if merging new facet into coplanar horizon (optimizes qh_mergesimplex) + + returns: + qh.max_outside and qh.min_vertex updated + initializes vertex neighbors on first merge + + note: + mergetype only used for logging and error reporting + + 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: + when done, tests facet1 and facet2 for degenerate or redundant neighbors and dupridges + 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 + move facet2 to end of qh.newfacet_list + unless MRGcoplanarhorizon + test facet2 for redundant neighbors + test facet1 for degenerate neighbors + test for redundant facet2 + maybe test for duplicate ridges ('Q15') + move facet1 to qh.visible_list for later deletion +*/ +void qh_mergefacet(qhT *qh, facetT *facet1, facetT *facet2, mergeType mergetype, realT *mindist, realT *maxdist, boolT mergeapex) { + boolT traceonce= False; + vertexT *vertex, **vertexp; + realT mintwisted, vertexdist; + realT onemerge; + int tracerestore=0, nummerge; + const char *mergename; + + if(mergetype > 0 && mergetype < sizeof(mergetypes)/sizeof(char *)) + mergename= mergetypes[mergetype]; + else + mergename= mergetypes[MRGnone]; + if (facet1->tricoplanar || facet2->tricoplanar) { + if (!qh->TRInormals) { + qh_fprintf(qh, qh->ferr, 6226, "qhull internal error (qh_mergefacet): merge f%d into f%d for mergetype %d (%s) does not work for tricoplanar facets. Use option 'Q11'\n", + facet1->id, facet2->id, mergetype, mergename); + qh_errexit2(qh, 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(qh); + } +#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, qh->ferr, 8075, "qh_mergefacet: ========= trace wide merge #%d(%2.2g) for f%d into f%d for mergetype %d (%s), last point was p%d\n", + zzval_(Ztotmerge), fmax_(-*mindist, *maxdist), facet1->id, facet2->id, mergetype, mergename, qh->furthest_id); + }else if (facet1 == qh->tracefacet || facet2 == qh->tracefacet) { + tracerestore= qh->IStracing; + qh->IStracing= 4; + traceonce= True; + qh_fprintf(qh, qh->ferr, 8076, "qh_mergefacet: ========= trace merge #%d for f%d into f%d for mergetype %d (%s), furthest is p%d\n", + zzval_(Ztotmerge), facet1->id, facet2->id, mergetype, mergename, qh->furthest_id); + } + } + if (qh->IStracing >= 2) { + realT mergemin= -2; + realT mergemax= -2; + + if (mindist) { + mergemin= *mindist; + mergemax= *maxdist; + } + qh_fprintf(qh, qh->ferr, 2081, "qh_mergefacet: #%d merge f%d into f%d for merge for mergetype %d (%s), mindist= %2.2g, maxdist= %2.2g, max_outside %2.2g\n", + zzval_(Ztotmerge), facet1->id, facet2->id, mergetype, mergename, mergemin, mergemax, qh->max_outside); + } +#endif /* !qh_NOtrace */ + if(!qh->ALLOWwide && mindist) { + mintwisted= qh_WIDEmaxoutside * qh->ONEmerge; /* same as qh_merge_twisted and qh_check_maxout (poly2) */ + maximize_(mintwisted, facet1->maxoutside); + maximize_(mintwisted, facet2->maxoutside); + if (*maxdist > mintwisted || -*mindist > mintwisted) { + vertexdist= qh_vertex_bestdist(qh, facet1->vertices); + onemerge= qh->ONEmerge + qh->DISTround; + if (vertexdist > mintwisted) { + qh_fprintf(qh, qh->ferr, 6347, "qhull precision error (qh_mergefacet): wide merge for facet f%d into f%d for mergetype %d (%s). maxdist %2.2g (%.1fx) mindist %2.2g (%.1fx) vertexdist %2.2g Allow with 'Q12' (allow-wide)\n", + facet1->id, facet2->id, mergetype, mergename, *maxdist, *maxdist/onemerge, *mindist, -*mindist/onemerge, vertexdist); + }else { + qh_fprintf(qh, qh->ferr, 6348, "qhull precision error (qh_mergefacet): wide merge for pinched facet f%d into f%d for mergetype %d (%s). maxdist %2.2g (%.fx) mindist %2.2g (%.1fx) vertexdist %2.2g Allow with 'Q12' (allow-wide)\n", + facet1->id, facet2->id, mergetype, mergename, *maxdist, *maxdist/onemerge, *mindist, -*mindist/onemerge, vertexdist); + } + qh_errexit2(qh, qh_ERRwide, facet1, facet2); + } + } + if (facet1 == facet2 || facet1->visible || facet2->visible) { + qh_fprintf(qh, qh->ferr, 6099, "qhull internal error (qh_mergefacet): either f%d and f%d are the same or one is a visible facet, mergetype %d (%s)\n", + facet1->id, facet2->id, mergetype, mergename); + qh_errexit2(qh, qh_ERRqhull, facet1, facet2); + } + if (qh->num_facets - qh->num_visible <= qh->hull_dim + 1) { + qh_fprintf(qh, qh->ferr, 6227, "qhull topology error: Only %d facets remain. The input is too degenerate or the convexity constraints are too strong.\n", + qh->hull_dim+1); + if (qh->hull_dim >= 5 && !qh->MERGEexact) + qh_fprintf(qh, qh->ferr, 8079, " Option 'Qx' may avoid this problem.\n"); + qh_errexit(qh, qh_ERRtopology, NULL, NULL); + } + if (!qh->VERTEXneighbors) + qh_vertexneighbors(qh); + qh_makeridges(qh, facet1); + qh_makeridges(qh, facet2); + if (qh->IStracing >=4) + qh_errprint(qh, "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; /* limited to 9 bits by qh_MAXnummerge, -Wconversion */ + facet2->newmerge= True; + facet2->dupridge= False; + qh_updatetested(qh, facet1, facet2); + if (qh->hull_dim > 2 && qh_setsize(qh, facet1->vertices) == qh->hull_dim) + qh_mergesimplex(qh, facet1, facet2, mergeapex); + else { + qh->vertex_visit++; + FOREACHvertex_(facet2->vertices) + vertex->visitid= qh->vertex_visit; + if (qh->hull_dim == 2) + qh_mergefacet2d(qh, facet1, facet2); + else { + qh_mergeneighbors(qh, facet1, facet2); + qh_mergevertices(qh, facet1->vertices, &facet2->vertices); + } + qh_mergeridges(qh, facet1, facet2); + qh_mergevertex_neighbors(qh, facet1, facet2); + if (!facet2->newfacet) + qh_newvertices(qh, facet2->vertices); + } + if (facet2->coplanarhorizon) { + zinc_(Zmergeintocoplanar); + }else if (!facet2->newfacet) { + zinc_(Zmergeintohorizon); + }else if (!facet1->newfacet && facet2->newfacet) { + zinc_(Zmergehorizon); + }else { + zinc_(Zmergenew); + } + qh_removefacet(qh, facet2); /* append as a newfacet to end of qh->facet_list */ + qh_appendfacet(qh, facet2); + facet2->newfacet= True; + facet2->tested= False; + qh_tracemerge(qh, facet1, facet2, mergetype); + if (traceonce) { + qh_fprintf(qh, qh->ferr, 8080, "qh_mergefacet: end of wide tracing\n"); + qh->IStracing= tracerestore; + } + if (mergetype != MRGcoplanarhorizon) { + trace3((qh, qh->ferr, 3076, "qh_mergefacet: check f%d and f%d for redundant and degenerate neighbors\n", + facet1->id, facet2->id)); + qh_test_redundant_neighbors(qh, facet2); + qh_test_degen_neighbors(qh, facet1); /* after qh_test_redundant_neighbors since MRGdegen more difficult than MRGredundant + and before qh_willdelete which clears facet1.neighbors */ + qh_degen_redundant_facet(qh, facet2); /* may occur in qh_merge_pinchedvertices, e.g., rbox 175 C3,2e-13 D4 t1545228104 | qhull d */ + qh_maybe_duplicateridges(qh, facet2); + } + qh_willdelete(qh, facet1, facet2); +} /* mergefacet */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="mergefacet2d">-</a> + + qh_mergefacet2d(qh, 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(qhT *qh, 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 not needed since neighborB is not degenerate */ + qh_setreplace(qh, neighborB->neighbors, facet1, facet2); + trace4((qh, 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 */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="mergeneighbors">-</a> + + qh_mergeneighbors(qh, facet1, facet2 ) + merges the neighbors of facet1 into facet2 + + notes: + only called by qh_mergefacet + qh.hull_dim >= 3 + see qh_mergecycle_neighbors + + design: + for each neighbor of facet1 + if neighbor is also a neighbor of facet2 + if neighbor is simplicial + 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(qhT *qh, facetT *facet1, facetT *facet2) { + facetT *neighbor, **neighborp; + + trace4((qh, 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(qh, neighbor); + if (SETfirstt_(neighbor->neighbors, facetT) != facet1) /*keep newfacet->horizon*/ + qh_setdel(neighbor->neighbors, facet1); + else { + qh_setdel(neighbor->neighbors, facet2); + qh_setreplace(qh, neighbor->neighbors, facet1, facet2); + } + }else if (neighbor != facet2) { + qh_setappend(qh, &(facet2->neighbors), neighbor); + qh_setreplace(qh, neighbor->neighbors, facet1, facet2); + } + } + qh_setdel(facet1->neighbors, facet2); /* here for makeridges */ + qh_setdel(facet2->neighbors, facet1); +} /* mergeneighbors */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="mergeridges">-</a> + + qh_mergeridges(qh, 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(qhT *qh, facetT *facet1, facetT *facet2) { + ridgeT *ridge, **ridgep; + + trace4((qh, qh->ferr, 4038, "qh_mergeridges: merge ridges of f%d into f%d\n", + facet1->id, facet2->id)); + FOREACHridge_(facet2->ridges) { + if ((ridge->top == facet1) || (ridge->bottom == facet1)) { + /* ridge.nonconvex is irrelevant due to merge */ + qh_delridge_merge(qh, ridge); /* expensive in high-d, could rebuild */ + ridgep--; /* deleted this ridge, repeat with next ridge*/ + } + } + FOREACHridge_(facet1->ridges) { + if (ridge->top == facet1) { + ridge->top= facet2; + ridge->simplicialtop= False; + }else { /* ridge.bottom is facet1 */ + ridge->bottom= facet2; + ridge->simplicialbot= False; + } + qh_setappend(qh, &(facet2->ridges), ridge); + } +} /* mergeridges */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="mergesimplex">-</a> + + qh_mergesimplex(qh, 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 neighbors + 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(qhT *qh, facetT *facet1, facetT *facet2, boolT mergeapex) { + vertexT *vertex, **vertexp, *opposite; + ridgeT *ridge, **ridgep; + boolT isnew= False; + facetT *neighbor, **neighborp, *otherfacet; + + if (mergeapex) { + opposite= SETfirstt_(facet1->vertices, vertexT); /* apex is opposite facet2. It has the last vertex id */ + trace4((qh, qh->ferr, 4086, "qh_mergesimplex: merge apex v%d of f%d into facet f%d\n", + opposite->id, facet1->id, facet2->id)); + if (!facet2->newfacet) + qh_newvertices(qh, facet2->vertices); /* apex, the first vertex, is already new */ + if (SETfirstt_(facet2->vertices, vertexT) != opposite) { + qh_setaddnth(qh, &facet2->vertices, 0, opposite); + isnew= 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 */ + } + opposite= vertex; + trace4((qh, qh->ferr, 4039, "qh_mergesimplex: merge opposite v%d of f%d into facet f%d\n", + opposite->id, facet1->id, facet2->id)); + isnew= qh_addfacetvertex(qh, facet2, opposite); + if (!facet2->newfacet) + qh_newvertices(qh, facet2->vertices); + else if (!opposite->newfacet) { + qh_removevertex(qh, opposite); + qh_appendvertex(qh, opposite); + } + } + trace4((qh, qh->ferr, 4040, "qh_mergesimplex: update vertex neighbors of f%d\n", + facet1->id)); + FOREACHvertex_(facet1->vertices) { + if (vertex == opposite && isnew) + qh_setreplace(qh, vertex->neighbors, facet1, facet2); + else { + qh_setdel(vertex->neighbors, facet1); + if (!SETsecond_(vertex->neighbors)) + qh_mergevertex_del(qh, vertex, facet1, facet2); + } + } + trace4((qh, 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) { + /* ridge.nonconvex is irrelevant due to merge */ + qh_delridge_merge(qh, ridge); /* expensive in high-d, could rebuild */ + ridgep--; /* deleted this ridge, repeat with next ridge*/ + qh_setdel(facet2->neighbors, facet1); /* a simplicial facet may have duplicate neighbors, need to delete each one */ + }else if (otherfacet->dupridge && !qh_setin(otherfacet->neighbors, facet1)) { + qh_fprintf(qh, qh->ferr, 6356, "qhull topology error (qh_mergesimplex): f%d is a dupridge of f%d, cannot merge f%d into f%d\n", + facet1->id, otherfacet->id, facet1->id, facet2->id); + qh_errexit2(qh, qh_ERRqhull, facet1, otherfacet); + }else { + trace4((qh, qh->ferr, 4059, "qh_mergesimplex: move r%d with f%d to f%d, new neighbor? %d, maybe horizon? %d\n", + ridge->id, otherfacet->id, facet2->id, (otherfacet->visitid != qh->visit_id), (SETfirstt_(otherfacet->neighbors, facetT) == facet1))); + qh_setappend(qh, &facet2->ridges, ridge); + if (otherfacet->visitid != qh->visit_id) { + qh_setappend(qh, &facet2->neighbors, otherfacet); + qh_setreplace(qh, otherfacet->neighbors, facet1, facet2); + otherfacet->visitid= qh->visit_id; + }else { + if (otherfacet->simplicial) /* is degen, needs ridges */ + qh_makeridges(qh, otherfacet); + if (SETfirstt_(otherfacet->neighbors, facetT) == facet1) { + /* keep new, otherfacet->neighbors->horizon */ + qh_setdel(otherfacet->neighbors, facet2); + qh_setreplace(qh, otherfacet->neighbors, facet1, facet2); + }else { + /* facet2 is already a neighbor of otherfacet, by f.visitid */ + qh_setdel(otherfacet->neighbors, facet1); + } + } + if (ridge->top == facet1) { /* wait until after qh_makeridges */ + ridge->top= facet2; + ridge->simplicialtop= False; + }else { + ridge->bottom= facet2; + ridge->simplicialbot= False; + } + } + } + trace3((qh, qh->ferr, 3006, "qh_mergesimplex: merged simplex f%d v%d into facet f%d\n", + facet1->id, opposite->id, facet2->id)); +} /* mergesimplex */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="mergevertex_del">-</a> + + qh_mergevertex_del(qh, 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(qhT *qh, vertexT *vertex, facetT *facet1, facetT *facet2) { + + zinc_(Zmergevertex); + trace2((qh, 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, &qh->del_vertices, vertex); +} /* mergevertex_del */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="mergevertex_neighbors">-</a> + + qh_mergevertex_neighbors(qh, 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(qhT *qh, facetT *facet1, facetT *facet2) { + vertexT *vertex, **vertexp; + + trace4((qh, qh->ferr, 4042, "qh_mergevertex_neighbors: merge vertex neighborset for f%d into f%d\n", + facet1->id, facet2->id)); + if (qh->tracevertex) { + qh_fprintf(qh, qh->ferr, 8081, "qh_mergevertex_neighbors: of f%d into f%d at furthest p%d f0= %p\n", + facet1->id, facet2->id, qh->furthest_id, qh->tracevertex->neighbors->e[0].p); + qh_errprint(qh, "TRACE", NULL, NULL, NULL, qh->tracevertex); + } + FOREACHvertex_(facet1->vertices) { + if (vertex->visitid != qh->vertex_visit) + qh_setreplace(qh, vertex->neighbors, facet1, facet2); + else { + qh_setdel(vertex->neighbors, facet1); + if (!SETsecond_(vertex->neighbors)) + qh_mergevertex_del(qh, vertex, facet1, facet2); + } + } + if (qh->tracevertex) + qh_errprint(qh, "TRACE", NULL, NULL, NULL, qh->tracevertex); +} /* mergevertex_neighbors */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="mergevertices">-</a> + + qh_mergevertices(qh, 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(qhT *qh, setT *vertices1, setT **vertices2) { + int newsize= qh_setsize(qh, vertices1)+qh_setsize(qh, *vertices2) - qh->hull_dim + 1; + setT *mergedvertices; + vertexT *vertex, **vertexp, **vertex2= SETaddr_(*vertices2, vertexT); + + mergedvertices= qh_settemp(qh, newsize); + FOREACHvertex_(vertices1) { + if (!*vertex2 || vertex->id > (*vertex2)->id) + qh_setappend(qh, &mergedvertices, vertex); + else { + while (*vertex2 && (*vertex2)->id > vertex->id) + qh_setappend(qh, &mergedvertices, *vertex2++); + if (!*vertex2 || (*vertex2)->id < vertex->id) + qh_setappend(qh, &mergedvertices, vertex); + else + qh_setappend(qh, &mergedvertices, *vertex2++); + } + } + while (*vertex2) + qh_setappend(qh, &mergedvertices, *vertex2++); + if (newsize < qh_setsize(qh, mergedvertices)) { + qh_fprintf(qh, qh->ferr, 6100, "qhull internal error (qh_mergevertices): facets did not share a ridge\n"); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + qh_setfree(qh, vertices2); + *vertices2= mergedvertices; + qh_settemppop(qh); +} /* mergevertices */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="neighbor_intersections">-</a> + + qh_neighbor_intersections(qh, 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: + only called by qh_redundant_vertex for qh_reducevertices + so f.vertices does not contain extraneous vertices that are not in f.ridges + 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(qhT *qh, 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(qh, neighborA->vertices, 0); + else + intersect= qh_vertexintersect_new(qh, neighborA->vertices, neighborB->vertices); + qh_settemppush(qh, intersect); + qh_setdelsorted(intersect, vertex); + FOREACHneighbor_i_(qh, vertex) { + if (neighbor_i >= 2) { + zinc_(Zintersectnum); + qh_vertexintersect(qh, &intersect, neighbor->vertices); + if (!SETfirst_(intersect)) { + zinc_(Zintersectfail); + qh_settempfree(qh, &intersect); + return NULL; + } + } + } + trace3((qh, qh->ferr, 3007, "qh_neighbor_intersections: %d vertices in neighbor intersection of v%d\n", + qh_setsize(qh, intersect), vertex->id)); + return intersect; +} /* neighbor_intersections */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="neighbor_vertices">-</a> + + qh_neighbor_vertices(qh, vertex ) + return neighboring vertices for a vertex (not in subridge) + assumes vertices have full vertex->neighbors + + returns: + temporary set of vertices + + notes: + updates qh.visit_id and qh.vertex_visit + similar to qh_vertexridges + +*/ +setT *qh_neighbor_vertices(qhT *qh, vertexT *vertexA, setT *subridge) { + facetT *neighbor, **neighborp; + vertexT *vertex, **vertexp; + setT *vertices= qh_settemp(qh, qh->TEMPsize); + + qh->visit_id++; + FOREACHneighbor_(vertexA) + neighbor->visitid= qh->visit_id; + qh->vertex_visit++; + vertexA->visitid= qh->vertex_visit; + FOREACHvertex_(subridge) { + vertex->visitid= qh->vertex_visit; + } + FOREACHneighbor_(vertexA) { + if (*neighborp) /* no new ridges in last neighbor */ + qh_neighbor_vertices_facet(qh, vertexA, neighbor, &vertices); + } + trace3((qh, qh->ferr, 3035, "qh_neighbor_vertices: %d non-subridge, vertex neighbors for v%d\n", + qh_setsize(qh, vertices), vertexA->id)); + return vertices; +} /* neighbor_vertices */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="neighbor_vertices_facet">-</a> + + qh_neighbor_vertices_facet(qh, vertex, facet, vertices ) + add neighboring vertices on ridges for vertex in facet + neighbor->visitid==qh.visit_id if it hasn't been visited + v.visitid==qh.vertex_visit if it is already in vertices + + returns: + vertices updated + sets facet->visitid to qh.visit_id-1 + + notes: + only called by qh_neighbor_vertices + similar to qh_vertexridges_facet + + design: + for each ridge of facet + if ridge of visited neighbor (i.e., unprocessed) + if vertex in ridge + append unprocessed vertices of ridge + mark facet processed +*/ +void qh_neighbor_vertices_facet(qhT *qh, vertexT *vertexA, facetT *facet, setT **vertices) { + ridgeT *ridge, **ridgep; + facetT *neighbor; + vertexT *second, *last, *vertex, **vertexp; + int last_i= qh->hull_dim-2, count= 0; + boolT isridge; + + if (facet->simplicial) { + FOREACHvertex_(facet->vertices) { + if (vertex->visitid != qh->vertex_visit) { + vertex->visitid= qh->vertex_visit; + qh_setappend(qh, vertices, vertex); + count++; + } + } + }else { + FOREACHridge_(facet->ridges) { + neighbor= otherfacet_(ridge, facet); + if (neighbor->visitid == qh->visit_id) { + isridge= False; + if (SETfirst_(ridge->vertices) == vertexA) { + isridge= True; + }else if (last_i > 2) { + second= SETsecondt_(ridge->vertices, vertexT); + last= SETelemt_(ridge->vertices, last_i, vertexT); + if (second->id >= vertexA->id && last->id <= vertexA->id) { /* vertices inverse sorted by id */ + if (second == vertexA || last == vertexA) + isridge= True; + else if (qh_setin(ridge->vertices, vertexA)) + isridge= True; + } + }else if (SETelem_(ridge->vertices, last_i) == vertexA) { + isridge= True; + }else if (last_i > 1 && SETsecond_(ridge->vertices) == vertexA) { + isridge= True; + } + if (isridge) { + FOREACHvertex_(ridge->vertices) { + if (vertex->visitid != qh->vertex_visit) { + vertex->visitid= qh->vertex_visit; + qh_setappend(qh, vertices, vertex); + count++; + } + } + } + } + } + } + facet->visitid= qh->visit_id-1; + if (count) { + trace4((qh, qh->ferr, 4079, "qh_neighbor_vertices_facet: found %d vertex neighbors for v%d in f%d (simplicial? %d)\n", + count, vertexA->id, facet->id, facet->simplicial)); + } +} /* neighbor_vertices_facet */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="newvertices">-</a> + + qh_newvertices(qh, vertices ) + add vertices to end of qh.vertex_list (marks as new vertices) + + returns: + vertices on qh.newvertex_list + vertex->newfacet set +*/ +void qh_newvertices(qhT *qh, setT *vertices) { + vertexT *vertex, **vertexp; + + FOREACHvertex_(vertices) { + if (!vertex->newfacet) { + qh_removevertex(qh, vertex); + qh_appendvertex(qh, vertex); + } + } +} /* newvertices */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="next_vertexmerge">-</a> + + qh_next_vertexmerge(qh ) + return next vertex merge from qh.vertex_mergeset + + returns: + vertex merge either MRGvertices or MRGsubridge + drops merges of deleted vertices + + notes: + called from qh_merge_pinchedvertices +*/ +mergeT *qh_next_vertexmerge(qhT *qh /* qh.vertex_mergeset */) { + mergeT *merge; + int merge_i, merge_n, best_i= -1; + realT bestdist= REALmax; + + FOREACHmerge_i_(qh, qh->vertex_mergeset) { + if (!merge->vertex1 || !merge->vertex2) { + qh_fprintf(qh, qh->ferr, 6299, "qhull internal error (qh_next_vertexmerge): expecting two vertices for vertex merge. Got v%d v%d and optional f%d\n", + getid_(merge->vertex1), getid_(merge->vertex2), getid_(merge->facet1)); + qh_errexit(qh, qh_ERRqhull, merge->facet1, NULL); + } + if (merge->vertex1->deleted || merge->vertex2->deleted) { + trace3((qh, qh->ferr, 3030, "qh_next_vertexmerge: drop merge of v%d (del? %d) into v%d (del? %d) due to deleted vertex of r%d and r%d\n", + merge->vertex1->id, merge->vertex1->deleted, merge->vertex2->id, merge->vertex2->deleted, getid_(merge->ridge1), getid_(merge->ridge2))); + qh_drop_mergevertex(qh, merge); + qh_setdelnth(qh, qh->vertex_mergeset, merge_i); + merge_i--; merge_n--; + qh_memfree(qh, merge, (int)sizeof(mergeT)); + }else if (merge->distance < bestdist) { + bestdist= merge->distance; + best_i= merge_i; + } + } + merge= NULL; + if (best_i >= 0) { + merge= SETelemt_(qh->vertex_mergeset, best_i, mergeT); + if (bestdist/qh->ONEmerge > qh_WIDEpinched) { + if (merge->mergetype==MRGvertices) { + if (merge->ridge1->top == merge->ridge2->bottom && merge->ridge1->bottom == merge->ridge2->top) + qh_fprintf(qh, qh->ferr, 6391, "qhull topology error (qh_next_vertexmerge): no nearly adjacent vertices to resolve opposite oriented ridges r%d and r%d in f%d and f%d. Nearest v%d and v%d dist %2.2g (%.1fx)\n", + merge->ridge1->id, merge->ridge2->id, merge->ridge1->top->id, merge->ridge1->bottom->id, merge->vertex1->id, merge->vertex2->id, bestdist, bestdist/qh->ONEmerge); + else + qh_fprintf(qh, qh->ferr, 6381, "qhull topology error (qh_next_vertexmerge): no nearly adjacent vertices to resolve duplicate ridges r%d and r%d. Nearest v%d and v%d dist %2.2g (%.1fx)\n", + merge->ridge1->id, merge->ridge2->id, merge->vertex1->id, merge->vertex2->id, bestdist, bestdist/qh->ONEmerge); + }else { + qh_fprintf(qh, qh->ferr, 6208, "qhull topology error (qh_next_vertexmerge): no nearly adjacent vertices to resolve dupridge. Nearest v%d and v%d dist %2.2g (%.1fx)\n", + merge->vertex1->id, merge->vertex2->id, bestdist, bestdist/qh->ONEmerge); + } + /* it may be possible to find a different vertex, after other vertex merges have occurred */ + qh_errexit(qh, qh_ERRtopology, NULL, merge->ridge1); + } + qh_setdelnth(qh, qh->vertex_mergeset, best_i); + } + return merge; +} /* next_vertexmerge */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="opposite_horizonfacet">-</a> + + qh_opposite_horizonfacet(qh, merge, opposite ) + return horizon facet for one of the merge facets, and its opposite vertex across the ridge + assumes either facet1 or facet2 of merge is 'mergehorizon' + assumes both facets are simplicial facets on qh.new_facetlist + + returns: + horizon facet and opposite vertex + + notes: + called by qh_getpinchedmerges +*/ +facetT *qh_opposite_horizonfacet(qhT *qh, mergeT *merge, vertexT **opposite) { + facetT *facet, *horizon, *otherfacet; + int neighbor_i; + + if (!merge->facet1->simplicial || !merge->facet2->simplicial || (!merge->facet1->mergehorizon && !merge->facet2->mergehorizon)) { + qh_fprintf(qh, qh->ferr, 6273, "qhull internal error (qh_opposite_horizonfacet): expecting merge of simplicial facets, at least one of which is mergehorizon. Either simplicial or mergehorizon is wrong\n"); + qh_errexit2(qh, qh_ERRqhull, merge->facet1, merge->facet2); + } + if (merge->facet1->mergehorizon) { + facet= merge->facet1; + otherfacet= merge->facet2; + }else { + facet= merge->facet2; + otherfacet= merge->facet1; + } + horizon= SETfirstt_(facet->neighbors, facetT); + neighbor_i= qh_setindex(otherfacet->neighbors, facet); + if (neighbor_i==-1) + neighbor_i= qh_setindex(otherfacet->neighbors, qh_MERGEridge); + if (neighbor_i==-1) { + qh_fprintf(qh, qh->ferr, 6238, "qhull internal error (qh_opposite_horizonfacet): merge facet f%d not connected to mergehorizon f%d\n", + otherfacet->id, facet->id); + qh_errexit2(qh, qh_ERRqhull, otherfacet, facet); + } + *opposite= SETelemt_(otherfacet->vertices, neighbor_i, vertexT); + return horizon; +} /* opposite_horizonfacet */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="reducevertices">-</a> + + qh_reducevertices(qh) + reduce extra vertices, shared vertices, and redundant vertices + facet->newmerge is set if merged since last call + vertex->delridge is set if vertex was on a deleted ridge + 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: + called by qh_all_merges and qh_postmerge + ignored if 2-d + + design: + merge any degenerate or redundant facets + repeat until no more 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 + for each new, undeleted vertex + remove delridge flag + if vertex is redundant + merge degenerate or redundant facets +*/ +boolT qh_reducevertices(qhT *qh) { + int numshare=0, numrename= 0; + boolT degenredun= False; + facetT *newfacet; + vertexT *vertex, **vertexp; + + if (qh->hull_dim == 2) + return False; + trace2((qh, qh->ferr, 2101, "qh_reducevertices: reduce extra vertices, shared vertices, and redundant vertices\n")); + if (qh_merge_degenredundant(qh)) + degenredun= True; +LABELrestart: + FORALLnew_facets { + if (newfacet->newmerge) { + if (!qh->MERGEvertices) + newfacet->newmerge= False; + if (qh_remove_extravertices(qh, newfacet)) { + qh_degen_redundant_facet(qh, newfacet); + if (qh_merge_degenredundant(qh)) { + degenredun= True; + goto LABELrestart; + } + } + } + } + if (!qh->MERGEvertices) + return False; + FORALLnew_facets { + if (newfacet->newmerge) { + newfacet->newmerge= False; + FOREACHvertex_(newfacet->vertices) { + if (vertex->delridge) { + if (qh_rename_sharedvertex(qh, vertex, newfacet)) { + numshare++; + if (qh_merge_degenredundant(qh)) { + degenredun= True; + goto LABELrestart; + } + 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(qh, vertex)) { + numrename++; + if (qh_merge_degenredundant(qh)) { + degenredun= True; + goto LABELrestart; + } + } + } + } + trace1((qh, qh->ferr, 1014, "qh_reducevertices: renamed %d shared vertices and %d redundant vertices. Degen? %d\n", + numshare, numrename, degenredun)); + return degenredun; +} /* reducevertices */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="redundant_vertex">-</a> + + qh_redundant_vertex(qh, vertex ) + rename a redundant vertex if qh_find_newvertex succeeds + assumes vertices have full vertex->neighbors + + returns: + if find a replacement vertex + returns new vertex + qh_renamevertex sets vertex->deleted for redundant vertex + + notes: + only called by qh_reducevertices for 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 among these ridges and vertices + rename vertex to the new vertex +*/ +vertexT *qh_redundant_vertex(qhT *qh, vertexT *vertex) { + vertexT *newvertex= NULL; + setT *vertices, *ridges; + + trace3((qh, qh->ferr, 3008, "qh_redundant_vertex: check if v%d from a deleted ridge can be renamed\n", vertex->id)); + if ((vertices= qh_neighbor_intersections(qh, vertex))) { + ridges= qh_vertexridges(qh, vertex, !qh_ALL); + if ((newvertex= qh_find_newvertex(qh, vertex, vertices, ridges))) { + zinc_(Zrenameall); + qh_renamevertex(qh, vertex, newvertex, ridges, NULL, NULL); /* ridges invalidated */ + } + qh_settempfree(qh, &ridges); + qh_settempfree(qh, &vertices); + } + return newvertex; +} /* redundant_vertex */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="remove_extravertices">-</a> + + qh_remove_extravertices(qh, facet ) + remove extra vertices from non-simplicial facets + + returns: + returns True if it finds them + deletes facet from vertex neighbors + facet may be redundant (test with qh_degen_redundant) + + notes: + called by qh_renamevertex and qh_reducevertices + a merge (qh_reducevertices) or qh_renamevertex may drop all ridges for a vertex in a facet + + design: + for each vertex in facet + if vertex not in a ridge (i.e., no longer used) + delete vertex from facet + delete facet from vertex's neighbors + unless vertex in another facet + add vertex to qh.del_vertices for later deletion +*/ +boolT qh_remove_extravertices(qhT *qh, facetT *facet) { + ridgeT *ridge, **ridgep; + vertexT *vertex, **vertexp; + boolT foundrem= False; + + if (facet->simplicial) { + return False; + } + trace4((qh, qh->ferr, 4043, "qh_remove_extravertices: test non-simplicial 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(qh, vertex->neighbors)) { + vertex->deleted= True; + qh_setappend(qh, &qh->del_vertices, vertex); + zinc_(Zremvertexdel); + trace2((qh, qh->ferr, 2036, "qh_remove_extravertices: v%d deleted because it's lost all ridges\n", vertex->id)); + }else + trace3((qh, 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 */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="remove_mergetype">-</a> + + qh_remove_mergetype(qh, mergeset, mergetype ) + Remove mergetype merges from mergeset + + notes: + Does not preserve order +*/ +void qh_remove_mergetype(qhT *qh, setT *mergeset, mergeType type) { + mergeT *merge; + int merge_i, merge_n; + + FOREACHmerge_i_(qh, mergeset) { + if (merge->mergetype == type) { + trace3((qh, qh->ferr, 3037, "qh_remove_mergetype: remove merge f%d f%d v%d v%d r%d r%d dist %2.2g type %d", + getid_(merge->facet1), getid_(merge->facet2), getid_(merge->vertex1), getid_(merge->vertex2), getid_(merge->ridge1), getid_(merge->ridge2), merge->distance, type)); + qh_setdelnth(qh, mergeset, merge_i); + merge_i--; merge_n--; /* repeat with next merge */ + } + } +} /* remove_mergetype */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="rename_adjacentvertex">-</a> + + qh_rename_adjacentvertex(qh, oldvertex, newvertex ) + renames oldvertex as newvertex. Must be adjacent (i.e., in the same subridge) + no-op if either vertex is deleted + + notes: + called from qh_merge_pinchedvertices + + design: + for all neighbors of oldvertex + if simplicial, rename oldvertex to newvertex and drop if degenerate + if needed, add oldvertex neighbor to newvertex + determine ridges for oldvertex + rename oldvertex as newvertex in ridges (qh_renamevertex) +*/ +void qh_rename_adjacentvertex(qhT *qh, vertexT *oldvertex, vertexT *newvertex, realT dist) { + setT *ridges; + facetT *neighbor, **neighborp, *maxfacet= NULL; + ridgeT *ridge, **ridgep; + boolT istrace= False; + int oldsize= qh_setsize(qh, oldvertex->neighbors); + int newsize= qh_setsize(qh, newvertex->neighbors); + coordT maxdist2= -REALmax, dist2; + + if (qh->IStracing >= 4 || oldvertex->id == qh->tracevertex_id || newvertex->id == qh->tracevertex_id) { + istrace= True; + } + zzinc_(Ztotmerge); + if (istrace) { + qh_fprintf(qh, qh->ferr, 2071, "qh_rename_adjacentvertex: merge #%d rename v%d (%d neighbors) to v%d (%d neighbors) dist %2.2g\n", + zzval_(Ztotmerge), oldvertex->id, oldsize, newvertex->id, newsize, dist); + } + if (oldvertex->deleted || newvertex->deleted) { + if (istrace || qh->IStracing >= 2) { + qh_fprintf(qh, qh->ferr, 2072, "qh_rename_adjacentvertex: ignore rename. Either v%d (%d) or v%d (%d) is deleted\n", + oldvertex->id, oldvertex->deleted, newvertex->id, newvertex->deleted); + } + return; + } + if (oldsize == 0 || newsize == 0) { + qh_fprintf(qh, qh->ferr, 2072, "qhull internal error (qh_rename_adjacentvertex): expecting neighbor facets for v%d and v%d. Got %d and %d neighbors resp.\n", + oldvertex->id, newvertex->id, oldsize, newsize); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + FOREACHneighbor_(oldvertex) { + if (neighbor->simplicial) { + if (qh_setin(neighbor->vertices, newvertex)) { + if (istrace || qh->IStracing >= 2) { + qh_fprintf(qh, qh->ferr, 2070, "qh_rename_adjacentvertex: simplicial f%d contains old v%d and new v%d. Will be marked degenerate by qh_renamevertex\n", + neighbor->id, oldvertex->id, newvertex->id); + } + qh_makeridges(qh, neighbor); /* no longer simplicial, nummerge==0, skipped by qh_maybe_duplicateridge */ + }else { + qh_replacefacetvertex(qh, neighbor, oldvertex, newvertex); + qh_setunique(qh, &newvertex->neighbors, neighbor); + qh_newvertices(qh, neighbor->vertices); /* for qh_update_vertexneighbors of vertex neighbors */ + } + } + } + ridges= qh_vertexridges(qh, oldvertex, qh_ALL); + if (istrace) { + FOREACHridge_(ridges) { + qh_printridge(qh, qh->ferr, ridge); + } + } + FOREACHneighbor_(oldvertex) { + if (!neighbor->simplicial){ + qh_addfacetvertex(qh, neighbor, newvertex); + qh_setunique(qh, &newvertex->neighbors, neighbor); + qh_newvertices(qh, neighbor->vertices); /* for qh_update_vertexneighbors of vertex neighbors */ + if (qh->newfacet_list == qh->facet_tail) { + qh_removefacet(qh, neighbor); /* add a neighbor to newfacet_list so that qh_partitionvisible has a newfacet */ + qh_appendfacet(qh, neighbor); + neighbor->newfacet= True; + } + } + } + qh_renamevertex(qh, oldvertex, newvertex, ridges, NULL, NULL); /* ridges invalidated */ + if (oldvertex->deleted && !oldvertex->partitioned) { + FOREACHneighbor_(newvertex) { + if (!neighbor->visible) { + qh_distplane(qh, oldvertex->point, neighbor, &dist2); + if (dist2>maxdist2) { + maxdist2= dist2; + maxfacet= neighbor; + } + } + } + trace2((qh, qh->ferr, 2096, "qh_rename_adjacentvertex: partition old p%d(v%d) as a coplanar point for furthest f%d dist %2.2g. Maybe repartition later (QH0031)\n", + qh_pointid(qh, oldvertex->point), oldvertex->id, maxfacet->id, maxdist2)) + qh_partitioncoplanar(qh, oldvertex->point, maxfacet, NULL, !qh_ALL); /* faster with maxdist2, otherwise duplicates distance tests from maxdist2/dist2 */ + oldvertex->partitioned= True; + } + qh_settempfree(qh, &ridges); +} /* rename_adjacentvertex */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="rename_sharedvertex">-</a> + + qh_rename_sharedvertex(qh, 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: + only called by qh_reducevertices after qh_remove_extravertices + so f.vertices does not contain extraneous vertices + 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(qhT *qh, vertexT *vertex, facetT *facet) { + facetT *neighbor, **neighborp, *neighborA= NULL; + setT *vertices, *ridges; + vertexT *newvertex= NULL; + + if (qh_setsize(qh, 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, qh->ferr, 6101, "qhull internal error (qh_rename_sharedvertex): v%d's neighbors not in f%d\n", + vertex->id, facet->id); + qh_errprint(qh, "ERRONEOUS", facet, NULL, NULL, vertex); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + if (neighborA) { /* avoid warning */ + /* the vertex is shared by facet and neighborA */ + ridges= qh_settemp(qh, qh->TEMPsize); + neighborA->visitid= ++qh->visit_id; + qh_vertexridges_facet(qh, vertex, facet, &ridges); + trace2((qh, qh->ferr, 2037, "qh_rename_sharedvertex: p%d(v%d) is shared by f%d(%d ridges) and f%d\n", + qh_pointid(qh, vertex->point), vertex->id, facet->id, qh_setsize(qh, ridges), neighborA->id)); + zinc_(Zintersectnum); + vertices= qh_vertexintersect_new(qh, facet->vertices, neighborA->vertices); + qh_setdel(vertices, vertex); + qh_settemppush(qh, vertices); + if ((newvertex= qh_find_newvertex(qh, vertex, vertices, ridges))) + qh_renamevertex(qh, vertex, newvertex, ridges, facet, neighborA); /* ridges invalidated */ + qh_settempfree(qh, &vertices); + qh_settempfree(qh, &ridges); + } + return newvertex; +} /* rename_sharedvertex */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="renameridgevertex">-</a> + + qh_renameridgevertex(qh, ridge, oldvertex, newvertex ) + renames oldvertex as newvertex in ridge + + returns: + True if renames oldvertex + False if deleted the ridge + + notes: + called by qh_renamevertex + caller sets newvertex->delridge for deleted ridges (qh_reducevertices) + + 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 +*/ +boolT qh_renameridgevertex(qhT *qh, ridgeT *ridge, vertexT *oldvertex, vertexT *newvertex) { + int nth= 0, oldnth; + facetT *temp; + vertexT *vertex, **vertexp; + + oldnth= qh_setindex(ridge->vertices, oldvertex); + if (oldnth < 0) { + qh_fprintf(qh, qh->ferr, 6424, "qhull internal error (qh_renameridgevertex): oldvertex v%d not found in r%d. Cannot rename to v%d\n", + oldvertex->id, ridge->id, newvertex->id); + qh_errexit(qh, qh_ERRqhull, NULL, ridge); + } + qh_setdelnthsorted(qh, ridge->vertices, oldnth); + FOREACHvertex_(ridge->vertices) { + if (vertex == newvertex) { + zinc_(Zdelridge); + if (ridge->nonconvex) /* only one ridge has nonconvex set */ + qh_copynonconvex(qh, ridge); + trace2((qh, qh->ferr, 2038, "qh_renameridgevertex: ridge r%d deleted. It contained both v%d and v%d\n", + ridge->id, oldvertex->id, newvertex->id)); + qh_delridge_merge(qh, ridge); /* ridge.vertices deleted */ + return False; + } + if (vertex->id < newvertex->id) + break; + nth++; + } + qh_setaddnth(qh, &ridge->vertices, nth, newvertex); + ridge->simplicialtop= False; + ridge->simplicialbot= False; + if (abs(oldnth - nth)%2) { + trace3((qh, 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; + } + return True; +} /* renameridgevertex */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="renamevertex">-</a> + + qh_renamevertex(qh, oldvertex, newvertex, ridges, oldfacet, neighborA ) + renames oldvertex as newvertex in ridges of non-simplicial neighbors + set oldfacet/neighborA if oldvertex is shared between two facets (qh_rename_sharedvertex) + otherwise qh_redundant_vertex or qh_rename_adjacentvertex + + returns: + if oldfacet and multiple neighbors, oldvertex may still exist afterwards + otherwise sets oldvertex->deleted for later deletion + one or more ridges maybe deleted + ridges is invalidated + merges may be added to degen_mergeset via qh_maydropneighbor or qh_degen_redundant_facet + + notes: + qh_rename_sharedvertex can not change neighbors of newvertex (since it's a subset) + qh_redundant_vertex due to vertex->delridge for qh_reducevertices + qh_rename_adjacentvertex for complete renames + + design: + for each ridge in ridges + rename oldvertex to newvertex and delete degenerate ridges + if oldfacet not defined + for each non-simplicial 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 old vertex's neighbors + remove extra vertices (e.g., oldvertex) from neighborA +*/ +void qh_renamevertex(qhT *qh, vertexT *oldvertex, vertexT *newvertex, setT *ridges, facetT *oldfacet, facetT *neighborA) { + facetT *neighbor, **neighborp; + ridgeT *ridge, **ridgep; + int topsize, bottomsize; + boolT istrace= False; + +#ifndef qh_NOtrace + if (qh->IStracing >= 2 || oldvertex->id == qh->tracevertex_id || + newvertex->id == qh->tracevertex_id) { + istrace= True; + qh_fprintf(qh, qh->ferr, 2086, "qh_renamevertex: rename v%d to v%d in %d ridges with old f%d and neighbor f%d\n", + oldvertex->id, newvertex->id, qh_setsize(qh, ridges), getid_(oldfacet), getid_(neighborA)); + } +#endif + FOREACHridge_(ridges) { + if (qh_renameridgevertex(qh, ridge, oldvertex, newvertex)) { /* ridge is deleted if False, invalidating ridges */ + topsize= qh_setsize(qh, ridge->top->vertices); + bottomsize= qh_setsize(qh, ridge->bottom->vertices); + if (topsize < qh->hull_dim || (topsize == qh->hull_dim && !ridge->top->simplicial && qh_setin(ridge->top->vertices, newvertex))) { + trace4((qh, qh->ferr, 4070, "qh_renamevertex: ignore duplicate check for r%d. top f%d (size %d) will be degenerate after rename v%d to v%d\n", + ridge->id, ridge->top->id, topsize, oldvertex->id, newvertex->id)); + }else if (bottomsize < qh->hull_dim || (bottomsize == qh->hull_dim && !ridge->bottom->simplicial && qh_setin(ridge->bottom->vertices, newvertex))) { + trace4((qh, qh->ferr, 4071, "qh_renamevertex: ignore duplicate check for r%d. bottom f%d (size %d) will be degenerate after rename v%d to v%d\n", + ridge->id, ridge->bottom->id, bottomsize, oldvertex->id, newvertex->id)); + }else + qh_maybe_duplicateridge(qh, ridge); + } + } + if (!oldfacet) { + /* stat Zrenameall or Zpinchduplicate */ + if (istrace) + qh_fprintf(qh, qh->ferr, 2087, "qh_renamevertex: renaming v%d to v%d in several facets for qh_redundant_vertex or MRGsubridge\n", + oldvertex->id, newvertex->id); + FOREACHneighbor_(oldvertex) { + if (neighbor->simplicial) { + qh_degen_redundant_facet(qh, neighbor); /* e.g., rbox 175 C3,2e-13 D4 t1545235541 | qhull d */ + }else { + if (istrace) + qh_fprintf(qh, qh->ferr, 4080, "qh_renamevertex: rename vertices in non-simplicial neighbor f%d of v%d\n", neighbor->id, oldvertex->id); + qh_maydropneighbor(qh, neighbor); + qh_setdelsorted(neighbor->vertices, oldvertex); /* if degenerate, qh_degen_redundant_facet will add to mergeset */ + if (qh_remove_extravertices(qh, neighbor)) + neighborp--; /* neighbor deleted from oldvertex neighborset */ + qh_degen_redundant_facet(qh, neighbor); /* either direction may be redundant, faster if combine? */ + qh_test_redundant_neighbors(qh, neighbor); + qh_test_degen_neighbors(qh, neighbor); + } + } + if (!oldvertex->deleted) { + oldvertex->deleted= True; + qh_setappend(qh, &qh->del_vertices, oldvertex); + } + }else if (qh_setsize(qh, oldvertex->neighbors) == 2) { + zinc_(Zrenameshare); + if (istrace) + qh_fprintf(qh, qh->ferr, 3039, "qh_renamevertex: renaming v%d to v%d in oldfacet f%d for qh_rename_sharedvertex\n", + oldvertex->id, newvertex->id, oldfacet->id); + FOREACHneighbor_(oldvertex) { + qh_setdelsorted(neighbor->vertices, oldvertex); + qh_degen_redundant_facet(qh, neighbor); + } + oldvertex->deleted= True; + qh_setappend(qh, &qh->del_vertices, oldvertex); + }else { + zinc_(Zrenamepinch); + if (istrace || qh->IStracing >= 1) + qh_fprintf(qh, qh->ferr, 3040, "qh_renamevertex: renaming 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); + if (qh_remove_extravertices(qh, neighborA)) + qh_degen_redundant_facet(qh, neighborA); + } + if (oldfacet) + qh_degen_redundant_facet(qh, oldfacet); +} /* renamevertex */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="test_appendmerge">-</a> + + qh_test_appendmerge(qh, facet, neighbor, simplicial ) + test convexity and append to qh.facet_mergeset if non-convex + if pre-merging, + no-op if qh.SKIPconvex, or qh.MERGEexact and coplanar + if simplicial, assumes centrum test is valid (e.g., adjacent, simplicial new facets) + + returns: + true if appends facet/neighbor to qh.facet_mergeset + sets facet->center as needed + does not change facet->seen + + notes: + called from qh_getmergeset_initial, qh_getmergeset, and qh_test_vneighbors + must be at least as strong as qh_checkconvex (poly2_r.c) + assumes !f.flipped + + design: + exit if qh.SKIPconvex ('Q0') and !qh.POSTmerging + if qh.cos_max ('An') is defined and merging coplanars + if the angle between facet normals is too shallow + append an angle-coplanar merge to qh.mergeset + return True + test convexity of facet and neighbor +*/ +boolT qh_test_appendmerge(qhT *qh, facetT *facet, facetT *neighbor, boolT simplicial) { + realT angle= -REALmax; + boolT okangle= False; + + if (qh->SKIPconvex && !qh->POSTmerging) + return False; + if (qh->cos_max < REALmax/2 && (!qh->MERGEexact || qh->POSTmerging)) { + angle= qh_getangle(qh, facet->normal, neighbor->normal); + okangle= True; + zinc_(Zangletests); + if (angle > qh->cos_max) { + zinc_(Zcoplanarangle); + qh_appendmergeset(qh, facet, neighbor, MRGanglecoplanar, 0.0, angle); + trace2((qh, qh->ferr, 2039, "qh_test_appendmerge: coplanar angle %4.4g between f%d and f%d\n", + angle, facet->id, neighbor->id)); + return True; + } + } + if (simplicial || qh->hull_dim <= 3) + return qh_test_centrum_merge(qh, facet, neighbor, angle, okangle); + else + return qh_test_nonsimplicial_merge(qh, facet, neighbor, angle, okangle); +} /* test_appendmerge */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="test_centrum_merge">-</a> + + qh_test_centrum_merge(qh, facet, neighbor, angle, okangle ) + test centrum convexity and append non-convex facets to qh.facet_mergeset + 'angle' is angle between facets if okangle is true, otherwise use 0.0 + + returns: + true if append facet/neighbor to qh.facet_mergeset + sets facet->center as needed + does not change facet->seen + + notes: + called from test_appendmerge if adjacent simplicial facets or 2-d/3-d + at least as strict as qh_checkconvex, including qh.DISTround ('En' and 'Rn') + + design: + make facet's centrum if needed + if facet's centrum is above the neighbor (qh.centrum_radius) + set isconcave + + if facet's centrum is not below the neighbor (-qh.centrum_radius) + 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 and merging coplanars + get angle if needed (qh.ANGLEmerge 'An') + append concave-coplanar, concave ,or coplanar merge to qh.mergeset +*/ +boolT qh_test_centrum_merge(qhT *qh, facetT *facet, facetT *neighbor, realT angle, boolT okangle) { + coordT dist, dist2, mergedist; + boolT isconcave= False, iscoplanar= False; + + if (!facet->center) + facet->center= qh_getcentrum(qh, facet); + zzinc_(Zcentrumtests); + qh_distplane(qh, 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(qh, neighbor); + zzinc_(Zcentrumtests); + qh_distplane(qh, 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(qh, facet->normal, neighbor->normal); + zinc_(Zangletests); + } + if (isconcave && iscoplanar) { + zinc_(Zconcavecoplanarridge); + if (dist > dist2) + qh_appendmergeset(qh, facet, neighbor, MRGconcavecoplanar, dist, angle); + else + qh_appendmergeset(qh, neighbor, facet, MRGconcavecoplanar, dist2, angle); + trace0((qh, qh->ferr, 36, "qh_test_centrum_merge: concave f%d to coplanar 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 if (isconcave) { + mergedist= fmax_(dist, dist2); + zinc_(Zconcaveridge); + qh_appendmergeset(qh, facet, neighbor, MRGconcave, mergedist, angle); + trace0((qh, qh->ferr, 37, "qh_test_centrum_merge: 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 */ { + mergedist= fmin_(fabs_(dist), fabs_(dist2)); + zinc_(Zcoplanarcentrum); + qh_appendmergeset(qh, facet, neighbor, MRGcoplanar, mergedist, angle); + trace2((qh, qh->ferr, 2097, "qh_test_centrum_merge: 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_centrum_merge */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="test_degen_neighbors">-</a> + + qh_test_degen_neighbors(qh, facet ) + append degenerate neighbors to qh.degen_mergeset + + notes: + called at end of qh_mergefacet() and qh_renamevertex() + call after test_redundant_facet() since MRGredundant is less expensive then MRGdegen + a degenerate facet has fewer than hull_dim neighbors + see: qh_merge_degenredundant() + +*/ +void qh_test_degen_neighbors(qhT *qh, facetT *facet) { + facetT *neighbor, **neighborp; + int size; + + trace4((qh, qh->ferr, 4073, "qh_test_degen_neighbors: test for degenerate neighbors of f%d\n", facet->id)); + FOREACHneighbor_(facet) { + if (neighbor->visible) { + qh_fprintf(qh, qh->ferr, 6359, "qhull internal error (qh_test_degen_neighbors): facet f%d has deleted neighbor f%d (qh.visible_list)\n", + facet->id, neighbor->id); + qh_errexit2(qh, qh_ERRqhull, facet, neighbor); + } + if (neighbor->degenerate || neighbor->redundant || neighbor->dupridge) /* will merge or delete */ + continue; + /* merge flipped-degenerate facet before flipped facets */ + if ((size= qh_setsize(qh, neighbor->neighbors)) < qh->hull_dim) { + qh_appendmergeset(qh, neighbor, neighbor, MRGdegen, 0.0, 1.0); + trace2((qh, qh->ferr, 2019, "qh_test_degen_neighbors: f%d is degenerate with %d neighbors. Neighbor of f%d.\n", neighbor->id, size, facet->id)); + } + } +} /* test_degen_neighbors */ + + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="test_nonsimplicial_merge">-</a> + + qh_test_nonsimplicial_merge(qh, facet, neighbor, angle, okangle ) + test centrum and vertex convexity and append non-convex or redundant facets to qh.facet_mergeset + 'angle' is angle between facets if okangle is true, otherwise use 0.0 + skips coplanar merges if pre-merging with qh.MERGEexact ('Qx') + + returns: + true if appends facet/neighbor to qh.facet_mergeset + sets facet->center as needed + does not change facet->seen + + notes: + only called from test_appendmerge if a non-simplicial facet and at least 4-d + at least as strict as qh_checkconvex, including qh.DISTround ('En' and 'Rn') + centrums must be < -qh.centrum_radius + tests vertices as well as centrums since a facet may be twisted relative to its neighbor + + design: + set precision constants for maxoutside, clearlyconcave, minvertex, and coplanarcentrum + use maxoutside for coplanarcentrum if premerging with 'Qx' and qh_MAXcoplanarcentrum merges + otherwise use qh.centrum_radious for coplanarcentrum + make facet and neighbor centrums if needed + isconcave if a centrum is above neighbor (coplanarcentrum) + iscoplanar if a centrum is not below neighbor (-qh.centrum_radius) + maybeconvex if a centrum is clearly below neighbor (-clearyconvex) + return False if both centrums clearly below neighbor (-clearyconvex) + return MRGconcave if isconcave + + facets are neither clearly convex nor clearly concave + test vertices as well as centrums + if maybeconvex + determine mindist and maxdist for vertices of the other facet + maybe MRGredundant + otherwise + determine mindist and maxdist for vertices of either facet + maybe MRGredundant + maybeconvex if a vertex is clearly below neighbor (-clearconvex) + + vertices are concave if dist > clearlyconcave + vertices are twisted if dist > maxoutside (isconcave and maybeconvex) + return False if not concave and pre-merge of 'Qx' (qh.MERGEexact) + vertices are coplanar if dist in -minvertex..maxoutside + if !isconcave, vertices are coplanar if dist >= -qh.MAXcoplanar (n*qh.premerge_centrum) + + return False if neither concave nor coplanar + return MRGtwisted if isconcave and maybeconvex + return MRGconcavecoplanar if isconcave and isconvex + return MRGconcave if isconcave + return MRGcoplanar if iscoplanar +*/ +boolT qh_test_nonsimplicial_merge(qhT *qh, facetT *facet, facetT *neighbor, realT angle, boolT okangle) { + coordT dist, mindist, maxdist, mindist2, maxdist2, dist2, maxoutside, clearlyconcave, minvertex, clearlyconvex, mergedist, coplanarcentrum; + boolT isconcave= False, iscoplanar= False, maybeconvex= False, isredundant= False; + vertexT *maxvertex= NULL, *maxvertex2= NULL; + + maxoutside= fmax_(neighbor->maxoutside, qh->ONEmerge + qh->DISTround); + maxoutside= fmax_(maxoutside, facet->maxoutside); + clearlyconcave= qh_RATIOconcavehorizon * maxoutside; + minvertex= fmax_(-qh->min_vertex, qh->MAXcoplanar); /* non-negative, not available per facet, not used for iscoplanar */ + clearlyconvex= qh_RATIOconvexmerge * minvertex; /* must be convex for MRGtwisted */ + if (qh->MERGEexact && !qh->POSTmerging && (facet->nummerge > qh_MAXcoplanarcentrum || neighbor->nummerge > qh_MAXcoplanarcentrum)) + coplanarcentrum= maxoutside; + else + coplanarcentrum= qh->centrum_radius; + + if (!facet->center) + facet->center= qh_getcentrum(qh, facet); + zzinc_(Zcentrumtests); + qh_distplane(qh, facet->center, neighbor, &dist); + if (dist > coplanarcentrum) + isconcave= True; + else if (dist >= -qh->centrum_radius) + iscoplanar= True; + else if (dist < -clearlyconvex) + maybeconvex= True; + if (!neighbor->center) + neighbor->center= qh_getcentrum(qh, neighbor); + zzinc_(Zcentrumtests); + qh_distplane(qh, neighbor->center, facet, &dist2); + if (dist2 > coplanarcentrum) + isconcave= True; + else if (dist2 >= -qh->centrum_radius) + iscoplanar= True; + else if (dist2 < -clearlyconvex) { + if (maybeconvex) + return False; /* both centrums clearly convex */ + maybeconvex= True; + } + if (isconcave) { + if (!okangle && qh->ANGLEmerge) { + angle= qh_getangle(qh, facet->normal, neighbor->normal); + zinc_(Zangletests); + } + mergedist= fmax_(dist, dist2); + zinc_(Zconcaveridge); + qh_appendmergeset(qh, facet, neighbor, MRGconcave, mergedist, angle); + trace0((qh, qh->ferr, 18, "qh_test_nonsimplicial_merge: concave centrum for f%d or 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)); + return True; + } + /* neither clearly convex nor clearly concave, test vertices as well as centrums */ + if (maybeconvex) { + if (dist < -clearlyconvex) { + maxdist= dist; /* facet centrum clearly convex, no need to test its vertex distance */ + mindist= dist; + maxvertex2= qh_furthestvertex(qh, neighbor, facet, &maxdist2, &mindist2); + if (!maxvertex2) { + qh_appendmergeset(qh, neighbor, facet, MRGredundant, maxdist2, qh_ANGLEnone); + isredundant= True; + } + }else { /* dist2 < -clearlyconvex */ + maxdist2= dist2; /* neighbor centrum clearly convex, no need to test its vertex distance */ + mindist2= dist2; + maxvertex= qh_furthestvertex(qh, facet, neighbor, &maxdist, &mindist); + if (!maxvertex) { + qh_appendmergeset(qh, facet, neighbor, MRGredundant, maxdist, qh_ANGLEnone); + isredundant= True; + } + } + }else { + maxvertex= qh_furthestvertex(qh, facet, neighbor, &maxdist, &mindist); + if (maxvertex) { + maxvertex2= qh_furthestvertex(qh, neighbor, facet, &maxdist2, &mindist2); + if (!maxvertex2) { + qh_appendmergeset(qh, neighbor, facet, MRGredundant, maxdist2, qh_ANGLEnone); + isredundant= True; + }else if (mindist < -clearlyconvex || mindist2 < -clearlyconvex) + maybeconvex= True; + }else { /* !maxvertex */ + qh_appendmergeset(qh, facet, neighbor, MRGredundant, maxdist, qh_ANGLEnone); + isredundant= True; + } + } + if (isredundant) { + zinc_(Zredundantmerge); + return True; + } + + if (maxdist > clearlyconcave || maxdist2 > clearlyconcave) + isconcave= True; + else if (maybeconvex) { + if (maxdist > maxoutside || maxdist2 > maxoutside) + isconcave= True; /* MRGtwisted */ + } + if (!isconcave && qh->MERGEexact && !qh->POSTmerging) + return False; + if (isconcave && !iscoplanar) { + if (maxdist < maxoutside && (-qh->MAXcoplanar || (maxdist2 < maxoutside && mindist2 >= -qh->MAXcoplanar))) + iscoplanar= True; /* MRGconcavecoplanar */ + }else if (!iscoplanar) { + if (mindist >= -qh->MAXcoplanar || mindist2 >= -qh->MAXcoplanar) + iscoplanar= True; /* MRGcoplanar */ + } + if (!isconcave && !iscoplanar) + return False; + if (!okangle && qh->ANGLEmerge) { + angle= qh_getangle(qh, facet->normal, neighbor->normal); + zinc_(Zangletests); + } + if (isconcave && maybeconvex) { + zinc_(Ztwistedridge); + if (maxdist > maxdist2) + qh_appendmergeset(qh, facet, neighbor, MRGtwisted, maxdist, angle); + else + qh_appendmergeset(qh, neighbor, facet, MRGtwisted, maxdist2, angle); + trace0((qh, qh->ferr, 27, "qh_test_nonsimplicial_merge: twisted concave f%d v%d to f%d v%d, dist %4.4g and reverse dist %4.4g, angle %4.4g during p%d\n", + facet->id, getid_(maxvertex), neighbor->id, getid_(maxvertex2), maxdist, maxdist2, angle, qh->furthest_id)); + }else if (isconcave && iscoplanar) { + zinc_(Zconcavecoplanarridge); + if (maxdist > maxdist2) + qh_appendmergeset(qh, facet, neighbor, MRGconcavecoplanar, maxdist, angle); + else + qh_appendmergeset(qh, neighbor, facet, MRGconcavecoplanar, maxdist2, angle); + trace0((qh, qh->ferr, 28, "qh_test_nonsimplicial_merge: concave coplanar f%d v%d to f%d v%d, dist %4.4g and reverse dist %4.4g, angle %4.4g during p%d\n", + facet->id, getid_(maxvertex), neighbor->id, getid_(maxvertex2), maxdist, maxdist2, angle, qh->furthest_id)); + }else if (isconcave) { + mergedist= fmax_(maxdist, maxdist2); + zinc_(Zconcaveridge); + qh_appendmergeset(qh, facet, neighbor, MRGconcave, mergedist, angle); + trace0((qh, qh->ferr, 29, "qh_test_nonsimplicial_merge: concave f%d v%d to f%d v%d, dist %4.4g and reverse dist %4.4g, angle %4.4g during p%d\n", + facet->id, getid_(maxvertex), neighbor->id, getid_(maxvertex2), maxdist, maxdist2, angle, qh->furthest_id)); + }else /* iscoplanar */ { + mergedist= fmax_(fmax_(maxdist, maxdist2), fmax_(-mindist, -mindist2)); + zinc_(Zcoplanarcentrum); + qh_appendmergeset(qh, facet, neighbor, MRGcoplanar, mergedist, angle); + trace2((qh, qh->ferr, 2099, "qh_test_nonsimplicial_merge: coplanar f%d v%d to f%d v%d, dist %4.4g and reverse dist %4.4g, angle %4.4g during p%d\n", + facet->id, getid_(maxvertex), neighbor->id, getid_(maxvertex2), maxdist, maxdist2, angle, qh->furthest_id)); + } + return True; +} /* test_nonsimplicial_merge */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="test_redundant_neighbors">-</a> + + qh_test_redundant_neighbors(qh, facet ) + append degenerate facet or its redundant neighbors to qh.degen_mergeset + + returns: + bumps vertex_visit + + notes: + called at end of qh_mergefacet(), qh_mergecycle_all(), and qh_renamevertex + call before qh_test_degen_neighbors (MRGdegen are more likely to cause problems) + a redundant neighbor's vertices is a subset of the facet's vertices + with pinched and flipped facets, a redundant neighbor may have a wildly different normal + + see qh_merge_degenredundant() and qh_-_facet() + + design: + if facet is degenerate + appends facet to degen_mergeset + else + appends redundant neighbors of facet to degen_mergeset +*/ +void qh_test_redundant_neighbors(qhT *qh, facetT *facet) { + vertexT *vertex, **vertexp; + facetT *neighbor, **neighborp; + int size; + + trace4((qh, qh->ferr, 4022, "qh_test_redundant_neighbors: test neighbors of f%d vertex_visit %d\n", + facet->id, qh->vertex_visit+1)); + if ((size= qh_setsize(qh, facet->neighbors)) < qh->hull_dim) { + qh_appendmergeset(qh, facet, facet, MRGdegen, 0.0, 1.0); + trace2((qh, qh->ferr, 2017, "qh_test_redundant_neighbors: f%d is degenerate with %d neighbors.\n", facet->id, size)); + }else { + qh->vertex_visit++; + FOREACHvertex_(facet->vertices) + vertex->visitid= qh->vertex_visit; + FOREACHneighbor_(facet) { + if (neighbor->visible) { + qh_fprintf(qh, qh->ferr, 6360, "qhull internal error (qh_test_redundant_neighbors): facet f%d has deleted neighbor f%d (qh.visible_list)\n", + facet->id, neighbor->id); + qh_errexit2(qh, qh_ERRqhull, facet, neighbor); + } + if (neighbor->degenerate || neighbor->redundant || neighbor->dupridge) /* will merge or delete */ + continue; + if (facet->flipped && !neighbor->flipped) /* do not merge non-flipped into flipped */ + continue; + /* merge redundant-flipped facet first */ + /* uses early out instead of checking vertex count */ + FOREACHvertex_(neighbor->vertices) { + if (vertex->visitid != qh->vertex_visit) + break; + } + if (!vertex) { + qh_appendmergeset(qh, neighbor, facet, MRGredundant, 0.0, 1.0); + trace2((qh, qh->ferr, 2018, "qh_test_redundant_neighbors: f%d is contained in f%d. merge\n", neighbor->id, facet->id)); + } + } + } +} /* test_redundant_neighbors */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="test_vneighbors">-</a> + + qh_test_vneighbors(qh) + 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: + called by qh_all_merges from qh_postmerge if qh.TESTvneighbors ('Qv') + 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(qhT *qh /* qh.newfacet_list */) { + facetT *newfacet, *neighbor, **neighborp; + vertexT *vertex, **vertexp; + int nummerges= 0; + + trace1((qh, qh->ferr, 1015, "qh_test_vneighbors: testing vertex neighbors for convexity\n")); + if (!qh->VERTEXneighbors) + qh_vertexneighbors(qh); + 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(qh, newfacet, neighbor, False)) /* ignores optimization for simplicial ridges */ + nummerges++; + } + } + } + zadd_(Ztestvneighbor, nummerges); + trace1((qh, qh->ferr, 1016, "qh_test_vneighbors: found %d non-convex, vertex neighbors\n", + nummerges)); + return (nummerges > 0); +} /* test_vneighbors */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="tracemerge">-</a> + + qh_tracemerge(qh, facet1, facet2 ) + print trace message after merge +*/ +void qh_tracemerge(qhT *qh, facetT *facet1, facetT *facet2, mergeType mergetype) { + boolT waserror= False; + const char *mergename; + +#ifndef qh_NOtrace + if(mergetype > 0 && mergetype < sizeof(mergetypes)/sizeof(char *)) + mergename= mergetypes[mergetype]; + else + mergename= mergetypes[MRGnone]; + if (qh->IStracing >= 4) + qh_errprint(qh, "MERGED", facet2, NULL, NULL, NULL); + if (facet2 == qh->tracefacet || (qh->tracevertex && qh->tracevertex->newfacet)) { + qh_fprintf(qh, qh->ferr, 8085, "qh_tracemerge: trace facet and vertex after merge of f%d into f%d type %d (%s), furthest p%d\n", + facet1->id, facet2->id, mergetype, mergename, qh->furthest_id); + if (facet2 != qh->tracefacet) + qh_errprint(qh, "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, qh->ferr, 8086, "qh_tracemerge: trace vertex deleted at furthest p%d\n", + qh->furthest_id); + else + qh_checkvertex(qh, qh->tracevertex, qh_ALL, &waserror); + } + if (qh->tracefacet && qh->tracefacet->normal && !qh->tracefacet->visible) + qh_checkfacet(qh, qh->tracefacet, True /* newmerge */, &waserror); +#endif /* !qh_NOtrace */ + if (qh->CHECKfrequently || qh->IStracing >= 4) { /* can't check polygon here */ + if (qh->IStracing >= 4 && qh->num_facets < 500) { + qh_printlists(qh); + } + qh_checkfacet(qh, facet2, True /* newmerge */, &waserror); + } + if (waserror) + qh_errexit(qh, qh_ERRqhull, NULL, NULL); /* erroneous facet logged by qh_checkfacet */ +} /* tracemerge */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="tracemerging">-</a> + + qh_tracemerging(qh) + print trace message during POSTmerging + + returns: + updates qh.mergereport + + notes: + called from qh_mergecycle() and qh_mergefacet() + + see: + qh_buildtracing() +*/ +void qh_tracemerging(qhT *qh) { + 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, qh->ferr, 8087, "\n\ +At %d:%d:%d & %2.5g CPU secs, qhull has merged %d facets with max_outside %2.2g, min_vertex %2.2g.\n\ + The hull contains %d facets and %d vertices.\n", + tp->tm_hour, tp->tm_min, tp->tm_sec, cpu, total, qh->max_outside, qh->min_vertex, + qh->num_facets - qh->num_visible, + qh->num_vertices-qh_setsize(qh, qh->del_vertices)); +} /* tracemerging */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="updatetested">-</a> + + qh_updatetested(qh, 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 + + notes: + only called by qh_mergefacet + + 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(qhT *qh, 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(qh, 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(qh, facet2->center, qh->normal_size); + facet2->center= NULL; + FOREACHridge_(facet2->ridges) + ridge->tested= False; + } +} /* updatetested */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="vertexridges">-</a> + + qh_vertexridges(qh, vertex, allneighbors ) + return temporary set of ridges adjacent to a vertex + vertex->neighbors defined (qh_vertexneighbors) + + notes: + uses qh.visit_id + does not include implicit ridges for simplicial facets + skips last neighbor, unless allneighbors. For new facets, the last neighbor shares ridges with adjacent neighbors + if the last neighbor is not simplicial, it will have ridges for its simplicial neighbors + Use allneighbors when a new cone is attached to an existing convex hull + similar to qh_neighbor_vertices + + design: + for each neighbor of vertex + add ridges that include the vertex to ridges +*/ +setT *qh_vertexridges(qhT *qh, vertexT *vertex, boolT allneighbors) { + facetT *neighbor, **neighborp; + setT *ridges= qh_settemp(qh, qh->TEMPsize); + int size; + + qh->visit_id += 2; /* visit_id for vertex neighbors, visit_id-1 for facets of visited ridges */ + FOREACHneighbor_(vertex) + neighbor->visitid= qh->visit_id; + FOREACHneighbor_(vertex) { + if (*neighborp || allneighbors) /* no new ridges in last neighbor */ + qh_vertexridges_facet(qh, vertex, neighbor, &ridges); + } + if (qh->PRINTstatistics || qh->IStracing) { + size= qh_setsize(qh, ridges); + zinc_(Zvertexridge); + zadd_(Zvertexridgetot, size); + zmax_(Zvertexridgemax, size); + trace3((qh, qh->ferr, 3011, "qh_vertexridges: found %d ridges for v%d\n", + size, vertex->id)); + } + return ridges; +} /* vertexridges */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="vertexridges_facet">-</a> + + qh_vertexridges_facet(qh, 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 + mark facet processed +*/ +void qh_vertexridges_facet(qhT *qh, vertexT *vertex, facetT *facet, setT **ridges) { + ridgeT *ridge, **ridgep; + facetT *neighbor; + int last_i= qh->hull_dim-2; + vertexT *second, *last; + + FOREACHridge_(facet->ridges) { + neighbor= otherfacet_(ridge, facet); + if (neighbor->visitid == qh->visit_id) { + if (SETfirst_(ridge->vertices) == vertex) { + qh_setappend(qh, ridges, ridge); + }else if (last_i > 2) { + second= SETsecondt_(ridge->vertices, vertexT); + last= SETelemt_(ridge->vertices, last_i, vertexT); + if (second->id >= vertex->id && last->id <= vertex->id) { /* vertices inverse sorted by id */ + if (second == vertex || last == vertex) + qh_setappend(qh, ridges, ridge); + else if (qh_setin(ridge->vertices, vertex)) + qh_setappend(qh, ridges, ridge); + } + }else if (SETelem_(ridge->vertices, last_i) == vertex + || (last_i > 1 && SETsecond_(ridge->vertices) == vertex)) { + qh_setappend(qh, ridges, ridge); + } + } + } + facet->visitid= qh->visit_id-1; +} /* vertexridges_facet */ + +/*-<a href="qh-merge_r.htm#TOC" + >-------------------------------</a><a name="willdelete">-</a> + + qh_willdelete(qh, facet, replace ) + moves facet to visible list for qh_deletevisible + sets facet->f.replace to replace (may be NULL) + clears f.ridges and f.neighbors -- no longer valid + + returns: + bumps qh.num_visible +*/ +void qh_willdelete(qhT *qh, facetT *facet, facetT *replace) { + + trace4((qh, qh->ferr, 4081, "qh_willdelete: move f%d to visible list, set its replacement as f%d, and clear f.neighbors and f.ridges\n", facet->id, getid_(replace))); + if (!qh->visible_list && qh->newfacet_list) { + qh_fprintf(qh, qh->ferr, 6378, "qhull internal error (qh_willdelete): expecting qh.visible_list at before qh.newfacet_list f%d. Got NULL\n", + qh->newfacet_list->id); + qh_errexit2(qh, qh_ERRqhull, NULL, NULL); + } + qh_removefacet(qh, facet); + qh_prependfacet(qh, facet, &qh->visible_list); + qh->num_visible++; + facet->visible= True; + facet->f.replace= replace; + if (facet->ridges) + SETfirst_(facet->ridges)= NULL; + if (facet->neighbors) + SETfirst_(facet->neighbors)= NULL; +} /* willdelete */ + +#else /* qh_NOmerge */ + +void qh_all_vertexmerges(qhT *qh, int apexpointid, facetT *facet, facetT **retryfacet) { + QHULL_UNUSED(qh) + QHULL_UNUSED(apexpointid) + QHULL_UNUSED(facet) + QHULL_UNUSED(retryfacet) +} +void qh_premerge(qhT *qh, int apexpointid, realT maxcentrum, realT maxangle) { + QHULL_UNUSED(qh) + QHULL_UNUSED(apexpointid) + QHULL_UNUSED(maxcentrum) + QHULL_UNUSED(maxangle) +} +void qh_postmerge(qhT *qh, const char *reason, realT maxcentrum, realT maxangle, + boolT vneighbors) { + QHULL_UNUSED(qh) + QHULL_UNUSED(reason) + QHULL_UNUSED(maxcentrum) + QHULL_UNUSED(maxangle) + QHULL_UNUSED(vneighbors) +} +void qh_checkdelfacet(qhT *qh, facetT *facet, setT *mergeset) { + QHULL_UNUSED(qh) + QHULL_UNUSED(facet) + QHULL_UNUSED(mergeset) +} +void qh_checkdelridge(qhT *qh /* qh.visible_facets, vertex_mergeset */) { + QHULL_UNUSED(qh) +} +boolT qh_checkzero(qhT *qh, boolT testall) { + QHULL_UNUSED(qh) + QHULL_UNUSED(testall) + + return True; +} +void qh_freemergesets(qhT *qh) { + QHULL_UNUSED(qh) +} +void qh_initmergesets(qhT *qh) { + QHULL_UNUSED(qh) +} +void qh_merge_pinchedvertices(qhT *qh, int apexpointid /* qh.newfacet_list */) { + QHULL_UNUSED(qh) + QHULL_UNUSED(apexpointid) +} +#endif /* qh_NOmerge */ + diff --git a/contrib/libs/qhull/libqhull_r/merge_r.h b/contrib/libs/qhull/libqhull_r/merge_r.h new file mode 100644 index 0000000000..a0d4091ec8 --- /dev/null +++ b/contrib/libs/qhull/libqhull_r/merge_r.h @@ -0,0 +1,238 @@ +/*<html><pre> -<a href="qh-merge_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + merge_r.h + header file for merge_r.c + + see qh-merge_r.htm and merge_r.c + + Copyright (c) 1993-2020 C.B. Barber. + $Id: //main/2019/qhull/src/libqhull_r/merge_r.h#2 $$Change: 2953 $ + $DateTime: 2020/05/21 22:05:32 $$Author: bbarber $ +*/ + +#ifndef qhDEFmerge +#define qhDEFmerge 1 + +#include "libqhull_r.h" + + +/*============ -constants- ==============*/ + +/*-<a href="qh-merge_r.htm#TOC" + >--------------------------------</a><a name="qh_ANGLEnone">-</a> + + qh_ANGLEnone + indicates missing angle for mergeT->angle +*/ +#define qh_ANGLEnone 2.0 + +/*-<a href="qh-merge_r.htm#TOC" + >--------------------------------</a><a name="MRG">-</a> + + MRG... (mergeType) + indicates the type of a merge (mergeT->type) + MRGcoplanar...MRGtwisted set by qh_test_centrum_merge, qh_test_nonsimplicial_merge +*/ +typedef enum { /* must match mergetypes[] */ + MRGnone= 0, + /* MRGcoplanar..MRGtwisted go into qh.facet_mergeset for qh_all_merges + qh_compare_facetmerge selects lower mergetypes for merging first */ + MRGcoplanar, /* (1) centrum coplanar if centrum ('Cn') or vertex not clearly above or below neighbor */ + MRGanglecoplanar, /* (2) angle coplanar if angle ('An') is coplanar */ + MRGconcave, /* (3) concave ridge */ + MRGconcavecoplanar, /* (4) concave and coplanar ridge, one side concave, other side coplanar */ + MRGtwisted, /* (5) twisted ridge, both concave and convex, facet1 is wider */ + /* MRGflip go into qh.facet_mergeset for qh_flipped_merges */ + MRGflip, /* (6) flipped facet if qh.interior_point is above facet, w/ facet1 == facet2 */ + /* MRGdupridge go into qh.facet_mergeset for qh_forcedmerges */ + MRGdupridge, /* (7) dupridge if more than two neighbors. Set by qh_mark_dupridges for qh_MERGEridge */ + /* MRGsubridge and MRGvertices go into vertex_mergeset */ + MRGsubridge, /* (8) merge pinched vertex to remove the subridge of a MRGdupridge */ + MRGvertices, /* (9) merge pinched vertex to remove a facet's ridges with the same vertices */ + /* MRGdegen, MRGredundant, and MRGmirror go into qh.degen_mergeset */ + MRGdegen, /* (10) degenerate facet (!enough neighbors) facet1 == facet2 */ + MRGredundant, /* (11) redundant facet (vertex subset) */ + /* merge_degenredundant assumes degen < redundant */ + MRGmirror, /* (12) mirror facets: same vertices due to null facets in qh_triangulate + f.redundant for both facets*/ + /* MRGcoplanarhorizon for qh_mergecycle_all only */ + MRGcoplanarhorizon, /* (13) new facet coplanar with the horizon (qh_mergecycle_all) */ + ENDmrg +} mergeType; + +/*-<a href="qh-merge_r.htm#TOC" + >--------------------------------</a><a name="qh_MERGEapex">-</a> + + qh_MERGEapex + flag for qh_mergefacet() to indicate an apex merge +*/ +#define qh_MERGEapex True + +/*============ -structures- ====================*/ + +/*-<a href="qh-merge_r.htm#TOC" + >--------------------------------</a><a name="mergeT">-</a> + + mergeT + structure used to merge facets +*/ + +typedef struct mergeT mergeT; +struct mergeT { /* initialize in qh_appendmergeset */ + realT angle; /* cosine of angle between normals of facet1 and facet2, + null value and right angle is 0.0, coplanar is 1.0, narrow is -1.0 */ + realT distance; /* absolute value of distance between vertices, centrum and facet, or vertex and facet */ + facetT *facet1; /* will merge facet1 into facet2 */ + facetT *facet2; + vertexT *vertex1; /* will merge vertext1 into vertex2 for MRGsubridge or MRGvertices */ + vertexT *vertex2; + ridgeT *ridge1; /* the duplicate ridges resolved by MRGvertices */ + ridgeT *ridge2; /* merge is deleted if either ridge is deleted (qh_delridge) */ + mergeType mergetype; +}; + + +/*=========== -macros- =========================*/ + +/*-<a href="qh-merge_r.htm#TOC" + >--------------------------------</a><a name="FOREACHmerge_">-</a> + + FOREACHmerge_( merges ) {...} + assign 'merge' to each merge in merges + + notes: + uses 'mergeT *merge, **mergep;' + if qh_mergefacet(), + restart or use qh_setdellast() since qh.facet_mergeset may change + see <a href="qset_r.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHmerge_(merges) FOREACHsetelement_(mergeT, merges, merge) + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="FOREACHmergeA_">-</a> + + FOREACHmergeA_( vertices ) { ... } + assign 'mergeA' to each merge in merges + + notes: + uses 'mergeT *mergeA, *mergeAp;' + see <a href="qset_r.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHmergeA_(merges) FOREACHsetelement_(mergeT, merges, mergeA) + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="FOREACHmerge_i_">-</a> + + FOREACHmerge_i_(qh, vertices ) { ... } + assign 'merge' and 'merge_i' for each merge in mergeset + + declare: + mergeT *merge; + int merge_n, merge_i; + + see: + <a href="qset_r.h#FOREACHsetelement_i_">FOREACHsetelement_i_</a> +*/ +#define FOREACHmerge_i_(qh, mergeset) FOREACHsetelement_i_(qh, mergeT, mergeset, merge) + +/*============ prototypes in alphabetical order after pre/postmerge =======*/ + +#ifdef __cplusplus +extern "C" { +#endif + +void qh_premerge(qhT *qh, int apexpointid, realT maxcentrum, realT maxangle); +void qh_postmerge(qhT *qh, const char *reason, realT maxcentrum, realT maxangle, + boolT vneighbors); +void qh_all_merges(qhT *qh, boolT othermerge, boolT vneighbors); +void qh_all_vertexmerges(qhT *qh, int apexpointid, facetT *facet, facetT **retryfacet); +void qh_appendmergeset(qhT *qh, facetT *facet, facetT *neighbor, mergeType mergetype, coordT dist, realT angle); +void qh_appendvertexmerge(qhT *qh, vertexT *vertex, vertexT *destination, mergeType mergetype, realT distance, ridgeT *ridge1, ridgeT *ridge2); +setT *qh_basevertices(qhT *qh, facetT *samecycle); +void qh_check_dupridge(qhT *qh, facetT *facet1, realT dist1, facetT *facet2, realT dist2); +void qh_checkconnect(qhT *qh /* qh.new_facets */); +void qh_checkdelfacet(qhT *qh, facetT *facet, setT *mergeset); +void qh_checkdelridge(qhT *qh /* qh.visible_facets, vertex_mergeset */); +boolT qh_checkzero(qhT *qh, boolT testall); +int qh_compare_anglemerge(const void *p1, const void *p2); +int qh_compare_facetmerge(const void *p1, const void *p2); +int qh_comparevisit(const void *p1, const void *p2); +void qh_copynonconvex(qhT *qh, ridgeT *atridge); +void qh_degen_redundant_facet(qhT *qh, facetT *facet); +void qh_drop_mergevertex(qhT *qh, mergeT *merge); +void qh_delridge_merge(qhT *qh, ridgeT *ridge); +vertexT *qh_find_newvertex(qhT *qh, vertexT *oldvertex, setT *vertices, setT *ridges); +vertexT *qh_findbest_pinchedvertex(qhT *qh, mergeT *merge, vertexT *apex, vertexT **pinchedp, realT *distp /* qh.newfacet_list */); +vertexT *qh_findbest_ridgevertex(qhT *qh, ridgeT *ridge, vertexT **pinchedp, coordT *distp); +void qh_findbest_test(qhT *qh, boolT testcentrum, facetT *facet, facetT *neighbor, + facetT **bestfacet, realT *distp, realT *mindistp, realT *maxdistp); +facetT *qh_findbestneighbor(qhT *qh, facetT *facet, realT *distp, realT *mindistp, realT *maxdistp); +void qh_flippedmerges(qhT *qh, facetT *facetlist, boolT *wasmerge); +void qh_forcedmerges(qhT *qh, boolT *wasmerge); +void qh_freemergesets(qhT *qh); +void qh_getmergeset(qhT *qh, facetT *facetlist); +void qh_getmergeset_initial(qhT *qh, facetT *facetlist); +boolT qh_getpinchedmerges(qhT *qh, vertexT *apex, coordT maxdupdist, boolT *iscoplanar /* qh.newfacet_list, vertex_mergeset */); +boolT qh_hasmerge(setT *mergeset, mergeType type, facetT *facetA, facetT *facetB); +void qh_hashridge(qhT *qh, setT *hashtable, int hashsize, ridgeT *ridge, vertexT *oldvertex); +ridgeT *qh_hashridge_find(qhT *qh, setT *hashtable, int hashsize, ridgeT *ridge, + vertexT *vertex, vertexT *oldvertex, int *hashslot); +void qh_initmergesets(qhT *qh); +void qh_makeridges(qhT *qh, facetT *facet); +void qh_mark_dupridges(qhT *qh, facetT *facetlist, boolT allmerges); +void qh_maybe_duplicateridge(qhT *qh, ridgeT *ridge); +void qh_maybe_duplicateridges(qhT *qh, facetT *facet); +void qh_maydropneighbor(qhT *qh, facetT *facet); +int qh_merge_degenredundant(qhT *qh); +void qh_merge_nonconvex(qhT *qh, facetT *facet1, facetT *facet2, mergeType mergetype); +void qh_merge_pinchedvertices(qhT *qh, int apexpointid /* qh.newfacet_list */); +void qh_merge_twisted(qhT *qh, facetT *facet1, facetT *facet2); +void qh_mergecycle(qhT *qh, facetT *samecycle, facetT *newfacet); +void qh_mergecycle_all(qhT *qh, facetT *facetlist, boolT *wasmerge); +void qh_mergecycle_facets(qhT *qh, facetT *samecycle, facetT *newfacet); +void qh_mergecycle_neighbors(qhT *qh, facetT *samecycle, facetT *newfacet); +void qh_mergecycle_ridges(qhT *qh, facetT *samecycle, facetT *newfacet); +void qh_mergecycle_vneighbors(qhT *qh, facetT *samecycle, facetT *newfacet); +void qh_mergefacet(qhT *qh, facetT *facet1, facetT *facet2, mergeType mergetype, realT *mindist, realT *maxdist, boolT mergeapex); +void qh_mergefacet2d(qhT *qh, facetT *facet1, facetT *facet2); +void qh_mergeneighbors(qhT *qh, facetT *facet1, facetT *facet2); +void qh_mergeridges(qhT *qh, facetT *facet1, facetT *facet2); +void qh_mergesimplex(qhT *qh, facetT *facet1, facetT *facet2, boolT mergeapex); +void qh_mergevertex_del(qhT *qh, vertexT *vertex, facetT *facet1, facetT *facet2); +void qh_mergevertex_neighbors(qhT *qh, facetT *facet1, facetT *facet2); +void qh_mergevertices(qhT *qh, setT *vertices1, setT **vertices); +setT *qh_neighbor_intersections(qhT *qh, vertexT *vertex); +setT *qh_neighbor_vertices(qhT *qh, vertexT *vertex, setT *subridge); +void qh_neighbor_vertices_facet(qhT *qh, vertexT *vertexA, facetT *facet, setT **vertices); +void qh_newvertices(qhT *qh, setT *vertices); +mergeT *qh_next_vertexmerge(qhT *qh); +facetT *qh_opposite_horizonfacet(qhT *qh, mergeT *merge, vertexT **vertex); +boolT qh_reducevertices(qhT *qh); +vertexT *qh_redundant_vertex(qhT *qh, vertexT *vertex); +boolT qh_remove_extravertices(qhT *qh, facetT *facet); +void qh_remove_mergetype(qhT *qh, setT *mergeset, mergeType type); +void qh_rename_adjacentvertex(qhT *qh, vertexT *oldvertex, vertexT *newvertex, realT dist); +vertexT *qh_rename_sharedvertex(qhT *qh, vertexT *vertex, facetT *facet); +boolT qh_renameridgevertex(qhT *qh, ridgeT *ridge, vertexT *oldvertex, vertexT *newvertex); +void qh_renamevertex(qhT *qh, vertexT *oldvertex, vertexT *newvertex, setT *ridges, + facetT *oldfacet, facetT *neighborA); +boolT qh_test_appendmerge(qhT *qh, facetT *facet, facetT *neighbor, boolT simplicial); +void qh_test_degen_neighbors(qhT *qh, facetT *facet); +boolT qh_test_centrum_merge(qhT *qh, facetT *facet, facetT *neighbor, realT angle, boolT okangle); +boolT qh_test_nonsimplicial_merge(qhT *qh, facetT *facet, facetT *neighbor, realT angle, boolT okangle); +void qh_test_redundant_neighbors(qhT *qh, facetT *facet); +boolT qh_test_vneighbors(qhT *qh /* qh.newfacet_list */); +void qh_tracemerge(qhT *qh, facetT *facet1, facetT *facet2, mergeType mergetype); +void qh_tracemerging(qhT *qh); +void qh_undo_newfacets(qhT *qh); +void qh_updatetested(qhT *qh, facetT *facet1, facetT *facet2); +setT *qh_vertexridges(qhT *qh, vertexT *vertex, boolT allneighbors); +void qh_vertexridges_facet(qhT *qh, vertexT *vertex, facetT *facet, setT **ridges); +void qh_willdelete(qhT *qh, facetT *facet, facetT *replace); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* qhDEFmerge */ diff --git a/contrib/libs/qhull/libqhull_r/poly2_r.c b/contrib/libs/qhull/libqhull_r/poly2_r.c new file mode 100644 index 0000000000..1ab52444ff --- /dev/null +++ b/contrib/libs/qhull/libqhull_r/poly2_r.c @@ -0,0 +1,3958 @@ +/*<html><pre> -<a href="qh-poly_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + poly2_r.c + implements polygons and simplicies + + see qh-poly_r.htm, poly_r.h and libqhull_r.h + + frequently used code is in poly_r.c + + Copyright (c) 1993-2020 The Geometry Center. + $Id: //main/2019/qhull/src/libqhull_r/poly2_r.c#20 $$Change: 2953 $ + $DateTime: 2020/05/21 22:05:32 $$Author: bbarber $ +*/ + +#include "qhull_ra.h" + +/*======== functions in alphabetical order ==========*/ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="addfacetvertex">-</a> + + qh_addfacetvertex(qh, facet, newvertex ) + add newvertex to facet.vertices if not already there + vertices are inverse sorted by vertex->id + + returns: + True if new vertex for facet + + notes: + see qh_replacefacetvertex +*/ +boolT qh_addfacetvertex(qhT *qh, facetT *facet, vertexT *newvertex) { + vertexT *vertex; + int vertex_i= 0, vertex_n; + boolT isnew= True; + + FOREACHvertex_i_(qh, facet->vertices) { + if (vertex->id < newvertex->id) { + break; + }else if (vertex->id == newvertex->id) { + isnew= False; + break; + } + } + if (isnew) + qh_setaddnth(qh, &facet->vertices, vertex_i, newvertex); + return isnew; +} /* addfacetvertex */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="addhash">-</a> + + 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 */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="check_bestdist">-</a> + + qh_check_bestdist(qh) + 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(qhT *qh) { + 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, qh->ferr, 1020, "qh_check_bestdist: check points below nearest facet. Facet_list f%d\n", + qh->facet_list->id)); + maxoutside= qh_maxouter(qh); + maxoutside += qh->DISTround; + /* one more qh.DISTround for check computation */ + trace1((qh, qh->ferr, 1021, "qh_check_bestdist: check that all points are within %2.2g of best facet\n", maxoutside)); + facets= qh_pointfacet(qh /* qh.facet_list */); + if (!qh_QUICKhelp && qh->PRINTprecision) + qh_fprintf(qh, qh->ferr, 8091, "\n\ +qhull output completed. Verifying that %d points are\n\ +below %2.2g of the nearest %sfacet.\n", + qh_setsize(qh, facets), maxoutside, (qh->ONLYgood ? "good " : "")); + FOREACHfacet_i_(qh, facets) { /* for each point with facet assignment */ + if (facet) + unassigned= False; + else { + unassigned= True; + facet= qh->facet_list; + } + point= qh_point(qh, facet_i); + if (point == qh->GOODpointp) + continue; + qh_distplane(qh, point, facet, &dist); + numpart++; + bestfacet= qh_findbesthorizon(qh, !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(qh, point, bestfacet, &dist, &facetlist)) + && dist > maxoutside)) + notgood++; + else { + waserror= True; + qh_fprintf(qh, qh->ferr, 6109, "qhull precision error (qh_check_bestdist): 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(qh, &facets); + if (notverified && !qh->DELAUNAY && !qh_QUICKhelp && qh->PRINTprecision) + qh_fprintf(qh, 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, qh->ferr, 6110, "qhull precision error (qh_check_bestdist): a coplanar point is %6.2g from convex hull. The maximum value is qh.outside_err (%6.2g)\n", + maxdist, qh->outside_err); + qh_errexit2(qh, qh_ERRprec, errfacet1, errfacet2); + }else if (waserror && qh->outside_err > REALmax/2) + qh_errexit2(qh, qh_ERRprec, errfacet1, errfacet2); + /* else if waserror, the error was logged to qh.ferr but does not effect the output */ + trace0((qh, qh->ferr, 20, "qh_check_bestdist: max distance outside %2.2g\n", maxdist)); +} /* check_bestdist */ + +#ifndef qh_NOmerge +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="check_maxout">-</a> + + qh_check_maxout(qh) + 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 qh.min_vertex instead of qh.MAXcoplanar + may not need to check near-inside points because of qh.MAXcoplanar + and qh.KEEPnearinside (before it was -qh.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 +*/ +void qh_check_maxout(qhT *qh) { + facetT *facet, *bestfacet, *neighbor, **neighborp, *facetlist, *maxbestfacet= NULL, *minfacet, *maxfacet, *maxpointfacet; + realT dist, maxoutside, mindist, nearest; + realT maxoutside_base, minvertex_base; + pointT *point, *maxpoint= NULL; + int numpart= 0, facet_i, facet_n, notgood= 0; + setT *facets, *vertices; + vertexT *vertex, *minvertex; + + trace1((qh, qh->ferr, 1022, "qh_check_maxout: check and update qh.min_vertex %2.2g and qh.max_outside %2.2g\n", qh->min_vertex, qh->max_outside)); + minvertex_base= fmin_(qh->min_vertex, -(qh->ONEmerge+qh->DISTround)); + maxoutside= mindist= 0.0; + minvertex= qh->vertex_list; + maxfacet= minfacet= maxpointfacet= qh->facet_list; + if (qh->VERTEXneighbors + && (qh->PRINTsummary || qh->KEEPinside || qh->KEEPcoplanar + || qh->TRACElevel || qh->PRINTstatistics || qh->VERIFYoutput || qh->CHECKfrequently + || qh->PRINTout[0] == qh_PRINTsummary || qh->PRINTout[0] == qh_PRINTnone)) { + trace1((qh, qh->ferr, 1023, "qh_check_maxout: determine actual minvertex\n")); + vertices= qh_pointvertex(qh /* qh.facet_list */); + FORALLvertices { + FOREACHneighbor_(vertex) { + zinc_(Zdistvertex); /* distance also computed by main loop below */ + qh_distplane(qh, vertex->point, neighbor, &dist); + if (dist < mindist) { + if (qh->min_vertex/minvertex_base > qh_WIDEmaxoutside && (qh->PRINTprecision || !qh->ALLOWwide)) { + nearest= qh_vertex_bestdist(qh, neighbor->vertices); + /* should be caught in qh_mergefacet */ + qh_fprintf(qh, qh->ferr, 7083, "Qhull precision warning: in post-processing (qh_check_maxout) p%d(v%d) is %2.2g below f%d nearest vertices %2.2g\n", + qh_pointid(qh, vertex->point), vertex->id, dist, neighbor->id, nearest); + } + mindist= dist; + minvertex= vertex; + minfacet= neighbor; + } +#ifndef qh_NOtrace + if (-dist > qh->TRACEdist || dist > qh->TRACEdist + || neighbor == qh->tracefacet || vertex == qh->tracevertex) { + nearest= qh_vertex_bestdist(qh, neighbor->vertices); + qh_fprintf(qh, qh->ferr, 8093, "qh_check_maxout: p%d(v%d) is %.2g from f%d nearest vertices %2.2g\n", + qh_pointid(qh, vertex->point), vertex->id, dist, neighbor->id, nearest); + } +#endif + } + } + if (qh->MERGING) { + wmin_(Wminvertex, qh->min_vertex); + } + qh->min_vertex= mindist; + qh_settempfree(qh, &vertices); + } + trace1((qh, qh->ferr, 1055, "qh_check_maxout: determine actual maxoutside\n")); + maxoutside_base= fmax_(qh->max_outside, qh->ONEmerge+qh->DISTround); + /* maxoutside_base is same as qh.MAXoutside without qh.MINoutside (qh_detmaxoutside) */ + facets= qh_pointfacet(qh /* qh.facet_list */); + FOREACHfacet_i_(qh, facets) { /* for each point with facet assignment */ + if (facet) { + point= qh_point(qh, facet_i); + if (point == qh->GOODpointp) + continue; + zzinc_(Ztotcheck); + qh_distplane(qh, point, facet, &dist); + numpart++; + bestfacet= qh_findbesthorizon(qh, qh_IScheckmax, point, facet, !qh_NOupper, &dist, &numpart); + if (bestfacet && dist >= maxoutside) { + if (qh->ONLYgood && !bestfacet->good + && !((bestfacet= qh_findgooddist(qh, point, bestfacet, &dist, &facetlist)) + && dist > maxoutside)) { + notgood++; + }else if (dist/maxoutside_base > qh_WIDEmaxoutside && (qh->PRINTprecision || !qh->ALLOWwide)) { + nearest= qh_vertex_bestdist(qh, bestfacet->vertices); + if (nearest < fmax_(qh->ONEmerge, qh->max_outside) * qh_RATIOcoplanaroutside * 2) { + qh_fprintf(qh, qh->ferr, 7087, "Qhull precision warning: in post-processing (qh_check_maxout) p%d for f%d is %2.2g above twisted facet f%d nearest vertices %2.2g\n", + qh_pointid(qh, point), facet->id, dist, bestfacet->id, nearest); + }else { + qh_fprintf(qh, qh->ferr, 7088, "Qhull precision warning: in post-processing (qh_check_maxout) p%d for f%d is %2.2g above hidden facet f%d nearest vertices %2.2g\n", + qh_pointid(qh, point), facet->id, dist, bestfacet->id, nearest); + } + maxbestfacet= bestfacet; + } + maxoutside= dist; + maxfacet= bestfacet; + maxpoint= point; + maxpointfacet= facet; + } + if (dist > qh->TRACEdist || (bestfacet && bestfacet == qh->tracefacet)) + qh_fprintf(qh, qh->ferr, 8094, "qh_check_maxout: p%d is %.2g above f%d\n", + qh_pointid(qh, point), dist, (bestfacet ? bestfacet->id : UINT_MAX)); + } + } + zzadd_(Zcheckpart, numpart); + qh_settempfree(qh, &facets); + wval_(Wmaxout)= maxoutside - qh->max_outside; + wmax_(Wmaxoutside, qh->max_outside); + if (!qh->APPROXhull && maxoutside > qh->DISTround) { /* initial value for f.maxoutside */ + FORALLfacets { + if (maxoutside < facet->maxoutside) { + if (!qh->KEEPcoplanar) { + maxoutside= facet->maxoutside; + }else if (maxoutside + qh->DISTround < facet->maxoutside) { /* maxoutside is computed distance, e.g., rbox 100 s D3 t1547136913 | qhull R1e-3 Tcv Qc */ + qh_fprintf(qh, qh->ferr, 7082, "Qhull precision warning (qh_check_maxout): f%d.maxoutside (%4.4g) is greater than computed qh.max_outside (%2.2g) + qh.DISTround (%2.2g). It should be less than or equal\n", + facet->id, facet->maxoutside, maxoutside, qh->DISTround); + } + } + } + } + qh->max_outside= maxoutside; + qh_nearcoplanar(qh /* qh.facet_list */); + qh->maxoutdone= True; + trace1((qh, qh->ferr, 1024, "qh_check_maxout: p%d(v%d) is qh.min_vertex %2.2g below facet f%d. Point p%d for f%d is qh.max_outside %2.2g above f%d. %d points are outside of not-good facets\n", + qh_pointid(qh, minvertex->point), minvertex->id, qh->min_vertex, minfacet->id, qh_pointid(qh, maxpoint), maxpointfacet->id, qh->max_outside, maxfacet->id, notgood)); + if(!qh->ALLOWwide) { + if (maxoutside/maxoutside_base > qh_WIDEmaxoutside) { + qh_fprintf(qh, qh->ferr, 6297, "Qhull precision error (qh_check_maxout): large increase in qh.max_outside during post-processing dist %2.2g (%.1fx). See warning QH0032/QH0033. Allow with 'Q12' (allow-wide) and 'Pp'\n", + maxoutside, maxoutside/maxoutside_base); + qh_errexit(qh, qh_ERRwide, maxbestfacet, NULL); + }else if (!qh->APPROXhull && maxoutside_base > (qh->ONEmerge * qh_WIDEmaxoutside2)) { + if (maxoutside > (qh->ONEmerge * qh_WIDEmaxoutside2)) { /* wide facets may have been deleted */ + qh_fprintf(qh, qh->ferr, 6298, "Qhull precision error (qh_check_maxout): a facet merge, vertex merge, vertex, or coplanar point produced a wide facet %2.2g (%.1fx). Trace with option 'TWn' to identify the merge. Allow with 'Q12' (allow-wide)\n", + maxoutside_base, maxoutside_base/(qh->ONEmerge + qh->DISTround)); + qh_errexit(qh, qh_ERRwide, maxbestfacet, NULL); + } + }else if (qh->min_vertex/minvertex_base > qh_WIDEmaxoutside) { + qh_fprintf(qh, qh->ferr, 6354, "Qhull precision error (qh_check_maxout): large increase in qh.min_vertex during post-processing dist %2.2g (%.1fx). See warning QH7083. Allow with 'Q12' (allow-wide) and 'Pp'\n", + qh->min_vertex, qh->min_vertex/minvertex_base); + qh_errexit(qh, qh_ERRwide, minfacet, NULL); + }else if (minvertex_base < -(qh->ONEmerge * qh_WIDEmaxoutside2)) { + if (qh->min_vertex < -(qh->ONEmerge * qh_WIDEmaxoutside2)) { /* wide facets may have been deleted */ + qh_fprintf(qh, qh->ferr, 6380, "Qhull precision error (qh_check_maxout): a facet or vertex merge produced a wide facet: v%d below f%d distance %2.2g (%.1fx). Trace with option 'TWn' to identify the merge. Allow with 'Q12' (allow-wide)\n", + minvertex->id, minfacet->id, mindist, -qh->min_vertex/(qh->ONEmerge + qh->DISTround)); + qh_errexit(qh, qh_ERRwide, minfacet, NULL); + } + } + } +} /* check_maxout */ +#else /* qh_NOmerge */ +void qh_check_maxout(qhT *qh) { + QHULL_UNUSED(qh) +} +#endif + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="check_output">-</a> + + qh_check_output(qh) + performs the checks at the end of qhull algorithm + Maybe called after Voronoi output. If so, it recomputes centrums since they are Voronoi centers instead. +*/ +void qh_check_output(qhT *qh) { + int i; + + if (qh->STOPcone) + return; + if (qh->VERIFYoutput || qh->IStracing || qh->CHECKfrequently) { + qh_checkpolygon(qh, qh->facet_list); + qh_checkflipped_all(qh, qh->facet_list); + qh_checkconvex(qh, qh->facet_list, qh_ALGORITHMfault); + }else if (!qh->MERGING && qh_newstats(qh, qh->qhstat.precision, &i)) { + qh_checkflipped_all(qh, qh->facet_list); + qh_checkconvex(qh, qh->facet_list, qh_ALGORITHMfault); + } +} /* check_output */ + + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="check_point">-</a> + + qh_check_point(qh, point, facet, maxoutside, maxdist, errfacet1, errfacet2, errcount ) + check that point is less than maxoutside from facet + + notes: + only called from qh_checkpoints + reports up to qh_MAXcheckpoint-1 errors per facet +*/ +void qh_check_point(qhT *qh, pointT *point, facetT *facet, realT *maxoutside, realT *maxdist, facetT **errfacet1, facetT **errfacet2, int *errcount) { + realT dist, nearest; + + /* occurs after statistics reported */ + qh_distplane(qh, point, facet, &dist); + maximize_(*maxdist, dist); + if (dist > *maxoutside) { + (*errcount)++; + if (*errfacet1 != facet) { + *errfacet2= *errfacet1; + *errfacet1= facet; + } + if (*errcount < qh_MAXcheckpoint) { + nearest= qh_vertex_bestdist(qh, facet->vertices); + qh_fprintf(qh, qh->ferr, 6111, "qhull precision error: point p%d is outside facet f%d, distance= %6.8g maxoutside= %6.8g nearest vertices %2.2g\n", + qh_pointid(qh, point), facet->id, dist, *maxoutside, nearest); + } + } +} /* qh_check_point */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="check_points">-</a> + + qh_check_points(qh) + 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 via qh_check_bestdist (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(qhT *qh) { + facetT *facet, *errfacet1= NULL, *errfacet2= NULL; + realT total, maxoutside, maxdist= -REALmax; + pointT *point, **pointp, *pointtemp; + int errcount; + boolT testouter; + + maxoutside= qh_maxouter(qh); + maxoutside += qh->DISTround; + /* one more qh.DISTround for check computation */ + trace1((qh, 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, qh->ferr, 7075, "qhull input warning: merging without checking outer planes('Q5' or 'Po'). Verify may report that a point is outside of a facet.\n"); + qh_check_bestdist(qh); + }else { + if (qh_MAXoutside && qh->maxoutdone) + testouter= True; + else + testouter= False; + if (!qh_QUICKhelp) { + if (qh->MERGEexact) + qh_fprintf(qh, qh->ferr, 7076, "qhull input warning: exact merge ('Qx'). Verify may report that a point is outside of a facet. See qh-optq.htm#Qx\n"); + else if (qh->SKIPcheckmax || qh->NOnearinside) + qh_fprintf(qh, qh->ferr, 7077, "qhull input warning: no outer plane check ('Q5') or no processing of near-inside points ('Q8'). Verify may report that a point is outside of a facet.\n"); + } + if (qh->PRINTprecision) { + if (testouter) + qh_fprintf(qh, 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, 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, qh->ferr, 7061, "qhull warning (qh_check_points): missing normal for facet f%d\n", facet->id); + if (!errfacet1) + errfacet1= facet; + continue; + } + if (testouter) { +#if qh_MAXoutside + maxoutside= facet->maxoutside + 2 * qh->DISTround; + /* one DISTround to actual point and another to computed point */ +#endif + } + errcount= 0; + FORALLpoints { + if (point != qh->GOODpointp) + qh_check_point(qh, point, facet, &maxoutside, &maxdist, &errfacet1, &errfacet2, &errcount); + } + FOREACHpoint_(qh->other_points) { + if (point != qh->GOODpointp) + qh_check_point(qh, point, facet, &maxoutside, &maxdist, &errfacet1, &errfacet2, &errcount); + } + if (errcount >= qh_MAXcheckpoint) { + qh_fprintf(qh, qh->ferr, 6422, "qhull precision error (qh_check_points): %d additional points outside facet f%d, maxdist= %6.8g\n", + errcount-qh_MAXcheckpoint+1, facet->id, maxdist); + } + } + if (maxdist > qh->outside_err) { + qh_fprintf(qh, 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, qh_ERRprec, errfacet1, errfacet2 ); + }else if (errfacet1 && qh->outside_err > REALmax/2) + qh_errexit2(qh, qh_ERRprec, errfacet1, errfacet2 ); + /* else if errfacet1, the error was logged to qh.ferr but does not effect the output */ + trace0((qh, qh->ferr, 21, "qh_check_points: max distance outside %2.2g\n", maxdist)); + } +} /* check_points */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="checkconvex">-</a> + + qh_checkconvex(qh, facetlist, fault ) + check that each ridge in facetlist is convex + fault = qh_DATAfault if reporting errors from qh_initialhull with qh.ZEROcentrum + = qh_ALGORITHMfault otherwise + + returns: + counts Zconcaveridges and Zcoplanarridges + errors if !qh.FORCEoutput ('Fo') and concaveridge or if merging a coplanar ridge + overwrites Voronoi centers if set by qh_setvoronoi_all/qh_ASvoronoi + + notes: + called by qh_initial_hull, qh_check_output, qh_all_merges ('Tc'), qh_build_withrestart ('QJ') + does not test f.tricoplanar facets (qh_triangulate) + must be no stronger than qh_test_appendmerge + if not merging, + tests vertices for neighboring simplicial facets < -qh.DISTround + else if ZEROcentrum and simplicial facet, + tests vertices for neighboring simplicial facets < 0.0 + tests centrums of neighboring nonsimplicial facets < 0.0 + else if ZEROcentrum + tests centrums of neighboring facets < 0.0 + else + tests centrums of neighboring facets < -qh.DISTround ('En' 'Rn') + Does not test against -qh.centrum_radius since repeated computations may have different round-off errors (e.g., 'Rn') + + design: + for all facets + report flipped facets + if ZEROcentrum and simplicial neighbors + test vertices against neighbor + else + test centrum against neighbor +*/ +void qh_checkconvex(qhT *qh, 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, first_nonsimplicial= False, tested_simplicial, allsimplicial; + int neighbor_i, neighbor_n; + + if (qh->ZEROcentrum) { + trace1((qh, qh->ferr, 1064, "qh_checkconvex: check that facets are not-flipped and for qh.ZEROcentrum that simplicial vertices are below their neighbor (dist<0.0)\n")); + first_nonsimplicial= True; + }else if (!qh->MERGING) { + trace1((qh, qh->ferr, 1026, "qh_checkconvex: check that facets are not-flipped and that simplicial vertices are convex by qh.DISTround ('En', 'Rn')\n")); + first_nonsimplicial= True; + }else + trace1((qh, qh->ferr, 1062, "qh_checkconvex: check that facets are not-flipped and that their centrums are convex by qh.DISTround ('En', 'Rn') \n")); + if (!qh->RERUN) { + zzval_(Zconcaveridges)= 0; + zzval_(Zcoplanarridges)= 0; + } + FORALLfacet_(facetlist) { + if (facet->flipped) { + qh_joggle_restart(qh, "flipped facet"); /* also tested by qh_checkflipped */ + qh_fprintf(qh, qh->ferr, 6113, "qhull precision error: f%d is flipped (interior point is outside)\n", + facet->id); + errfacet1= facet; + waserror= True; + continue; + } + if (facet->tricoplanar) + continue; + if (qh->MERGING && (!qh->ZEROcentrum || !facet->simplicial)) { + allsimplicial= False; + tested_simplicial= False; + }else { + allsimplicial= True; + tested_simplicial= True; + FOREACHneighbor_i_(qh, facet) { + if (neighbor->tricoplanar) + continue; + if (!neighbor->simplicial) { + allsimplicial= False; + continue; + } + vertex= SETelemt_(facet->vertices, neighbor_i, vertexT); + qh_distplane(qh, vertex->point, neighbor, &dist); + if (dist >= -qh->DISTround) { + if (fault == qh_DATAfault) { + qh_joggle_restart(qh, "non-convex initial simplex"); + if (dist > qh->DISTround) + qh_fprintf(qh, qh->ferr, 6114, "qhull precision error: initial simplex is not convex, since p%d(v%d) is %6.4g above opposite f%d\n", + qh_pointid(qh, vertex->point), vertex->id, dist, neighbor->id); + else + qh_fprintf(qh, qh->ferr, 6379, "qhull precision error: initial simplex is not convex, since p%d(v%d) is within roundoff of opposite facet f%d (dist %6.4g)\n", + qh_pointid(qh, vertex->point), vertex->id, neighbor->id, dist); + qh_errexit(qh, qh_ERRsingular, neighbor, NULL); + } + if (dist > qh->DISTround) { + zzinc_(Zconcaveridges); + qh_joggle_restart(qh, "concave ridge"); + qh_fprintf(qh, qh->ferr, 6115, "qhull precision error: f%d is concave to f%d, since p%d(v%d) is %6.4g above f%d\n", + facet->id, neighbor->id, qh_pointid(qh, vertex->point), vertex->id, dist, neighbor->id); + errfacet1= facet; + errfacet2= neighbor; + waserror= True; + }else if (qh->ZEROcentrum) { + if (dist > 0.0) { /* qh_checkzero checked convex (dist < (- 2*qh->DISTround)), computation may differ e.g. 'Rn' */ + zzinc_(Zcoplanarridges); + qh_joggle_restart(qh, "coplanar ridge"); + qh_fprintf(qh, qh->ferr, 6116, "qhull precision error: f%d is clearly not convex to f%d, since p%d(v%d) is %6.4g above or coplanar with f%d with qh.ZEROcentrum\n", + facet->id, neighbor->id, qh_pointid(qh, vertex->point), vertex->id, dist, neighbor->id); + errfacet1= facet; + errfacet2= neighbor; + waserror= True; + } + }else { + zzinc_(Zcoplanarridges); + qh_joggle_restart(qh, "coplanar ridge"); + trace0((qh, qh->ferr, 22, "qhull precision error: f%d is coplanar to f%d, since p%d(v%d) is within %6.4g of f%d, during p%d\n", + facet->id, neighbor->id, qh_pointid(qh, vertex->point), vertex->id, dist, neighbor->id, qh->furthest_id)); + } + } + } + } + if (!allsimplicial) { + if (first_nonsimplicial) { + trace1((qh, qh->ferr, 1063, "qh_checkconvex: starting with f%d, also check that centrums of non-simplicial ridges are below their neighbors (dist<0.0)\n", + facet->id)); + first_nonsimplicial= False; + } + if (qh->CENTERtype == qh_AScentrum) { + if (!facet->center) + facet->center= qh_getcentrum(qh, facet); + centrum= facet->center; + }else { + if (!centrum_warning && !facet->simplicial) { /* recomputed centrum correct for simplicial facets */ + centrum_warning= True; + qh_fprintf(qh, qh->ferr, 7062, "qhull warning: recomputing centrums for convexity test. This may lead to false, precision errors.\n"); + } + centrum= qh_getcentrum(qh, facet); + tempcentrum= True; + } + FOREACHneighbor_(facet) { + if (neighbor->simplicial && tested_simplicial) /* tested above since f.simplicial */ + continue; + if (neighbor->tricoplanar) + continue; + zzinc_(Zdistconvex); + qh_distplane(qh, centrum, neighbor, &dist); + if (dist > qh->DISTround) { + zzinc_(Zconcaveridges); + qh_joggle_restart(qh, "concave ridge"); + qh_fprintf(qh, 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_joggle_restart(qh, "coplanar ridge"); + qh_fprintf(qh, 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(qh, centrum, qh->normal_size); + } + } + if (waserror && !qh->FORCEoutput) + qh_errexit2(qh, qh_ERRprec, errfacet1, errfacet2); +} /* checkconvex */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="checkfacet">-</a> + + qh_checkfacet(qh, facet, newmerge, waserror ) + checks for consistency errors in facet + newmerge set if from merge_r.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: + called by qh_tracemerge and qh_checkpolygon + 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(qhT *qh, facetT *facet, boolT newmerge, boolT *waserrorp) { + facetT *neighbor, **neighborp, *errother=NULL; + ridgeT *ridge, **ridgep, *errridge= NULL, *ridge2; + vertexT *vertex, **vertexp; + unsigned int previousid= INT_MAX; + int numneighbors, numvertices, numridges=0, numRvertices=0; + boolT waserror= False; + int skipA, skipB, ridge_i, ridge_n, i, last_v= qh->hull_dim-2; + setT *intersection; + + trace4((qh, qh->ferr, 4088, "qh_checkfacet: check f%d newmerge? %d\n", facet->id, newmerge)); + if (facet->id >= qh->facet_id) { + qh_fprintf(qh, qh->ferr, 6414, "qhull internal error (qh_checkfacet): unknown facet id f%d >= qh.facet_id (%d)\n", facet->id, qh->facet_id); + waserror= True; + } + if (facet->visitid > qh->visit_id) { + qh_fprintf(qh, qh->ferr, 6415, "qhull internal error (qh_checkfacet): expecting f%d.visitid <= qh.visit_id (%d). Got visitid %d\n", facet->id, qh->visit_id, facet->visitid); + waserror= True; + } + if (facet->visible && !qh->NEWtentative) { + qh_fprintf(qh, qh->ferr, 6119, "qhull internal error (qh_checkfacet): facet f%d is on qh.visible_list\n", + facet->id); + qh_errexit(qh, qh_ERRqhull, facet, NULL); + } + if (facet->redundant && !facet->visible && qh_setsize(qh, qh->degen_mergeset)==0) { + qh_fprintf(qh, qh->ferr, 6399, "qhull internal error (qh_checkfacet): redundant facet f%d not on qh.visible_list\n", + facet->id); + waserror= True; + } + if (facet->degenerate && !facet->visible && qh_setsize(qh, qh->degen_mergeset)==0) { + qh_fprintf(qh, qh->ferr, 6400, "qhull internal error (qh_checkfacet): degenerate facet f%d is not on qh.visible_list and qh.degen_mergeset is empty\n", + facet->id); + waserror= True; + } + if (!facet->normal) { + qh_fprintf(qh, qh->ferr, 6120, "qhull internal error (qh_checkfacet): facet f%d does not have a normal\n", + facet->id); + waserror= True; + } + if (!facet->newfacet) { + if (facet->dupridge) { + qh_fprintf(qh, qh->ferr, 6349, "qhull internal error (qh_checkfacet): f%d is 'dupridge' but it is not a newfacet on qh.newfacet_list f%d\n", + facet->id, getid_(qh->newfacet_list)); + waserror= True; + } + if (facet->newmerge) { + qh_fprintf(qh, qh->ferr, 6383, "qhull internal error (qh_checkfacet): f%d is 'newmerge' but it is not a newfacet on qh.newfacet_list f%d. Missing call to qh_reducevertices\n", + facet->id, getid_(qh->newfacet_list)); + waserror= True; + } + } + qh_setcheck(qh, facet->vertices, "vertices for f", facet->id); + qh_setcheck(qh, facet->ridges, "ridges for f", facet->id); + qh_setcheck(qh, facet->outsideset, "outsideset for f", facet->id); + qh_setcheck(qh, facet->coplanarset, "coplanarset for f", facet->id); + qh_setcheck(qh, facet->neighbors, "neighbors for f", facet->id); + FOREACHvertex_(facet->vertices) { + if (vertex->deleted) { + qh_fprintf(qh, qh->ferr, 6121, "qhull internal error (qh_checkfacet): deleted vertex v%d in f%d\n", vertex->id, facet->id); + qh_errprint(qh, "ERRONEOUS", NULL, NULL, NULL, vertex); + waserror= True; + } + if (vertex->id >= previousid) { + qh_fprintf(qh, 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(qh, facet->neighbors); + numvertices= qh_setsize(qh, facet->vertices); + numridges= qh_setsize(qh, facet->ridges); + if (facet->simplicial) { + if (numvertices+numneighbors != 2*qh->hull_dim + && !facet->degenerate && !facet->redundant) { + qh_fprintf(qh, 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, 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, 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, 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, qh->ferr, 6126, "qhull internal error (qh_checkfacet): facet f%d still has a MERGEridge or DUPLICATEridge neighbor\n", facet->id); + qh_errexit(qh, qh_ERRqhull, facet, NULL); + } + if (neighbor->visible) { + qh_fprintf(qh, qh->ferr, 6401, "qhull internal error (qh_checkfacet): facet f%d has deleted neighbor f%d (qh.visible_list)\n", + facet->id, neighbor->id); + errother= neighbor; + waserror= True; + } + neighbor->seen= True; + } + FOREACHneighbor_(facet) { + if (!qh_setin(neighbor->neighbors, facet)) { + qh_fprintf(qh, 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, 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(qh, ridge->vertices, "vertices for r", ridge->id); + ridge->seen= False; + } + FOREACHridge_(facet->ridges) { + if (ridge->seen) { + qh_fprintf(qh, 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(qh, ridge->vertices); + if (numRvertices != qh->hull_dim - 1) { + qh_fprintf(qh, 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, 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->newfacet && !neighbor->newfacet) { + if ((!ridge->tested) | ridge->nonconvex | ridge->mergevertex) { + qh_fprintf(qh, qh->ferr, 6384, "qhull internal error (qh_checkfacet): ridge r%d is nonconvex (%d), mergevertex (%d) or not tested (%d) for facet f%d, neighbor f%d\n", + ridge->id, ridge->nonconvex, ridge->mergevertex, ridge->tested, facet->id, neighbor->id); + errridge= ridge; + waserror= True; + } + } + } + if (!facet->simplicial) { + FOREACHneighbor_(facet) { + if (!neighbor->seen) { + qh_fprintf(qh, 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(qh, facet->vertices, neighbor->vertices); + qh_settemppush(qh, 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, 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, qh_ERRqhull, facet, ridge); + } + vertex->seen2= True; + } + } + if (!newmerge) { + FOREACHvertex_(intersection) { + if (!vertex->seen2) { + if (!qh->MERGING) { + qh_fprintf(qh, qh->ferr, 6420, "qhull topology error (qh_checkfacet): vertex v%d in f%d intersect f%d but not in a ridge. Last point was p%d\n", + vertex->id, facet->id, neighbor->id, qh->furthest_id); + if (!qh->FORCEoutput) { + qh_errprint(qh, "ERRONEOUS", facet, neighbor, NULL, vertex); + qh_errexit(qh, qh_ERRtopology, NULL, NULL); + } + }else { + trace4((qh, qh->ferr, 4025, "qh_checkfacet: vertex v%d in f%d intersect f%d but not in a ridge. Repaired by qh_remove_extravertices in qh_reducevertices\n", + vertex->id, facet->id, neighbor->id)); + } + } + } + } + qh_settempfree(qh, &intersection); + } + }else { /* simplicial */ + FOREACHneighbor_(facet) { + if (neighbor->simplicial && !facet->degenerate && !neighbor->degenerate) { + skipA= SETindex_(facet->neighbors, neighbor); + skipB= qh_setindex(neighbor->neighbors, facet); + if (skipA<0 || skipB<0 || !qh_setequal_skip(facet->vertices, skipA, neighbor->vertices, skipB)) { + qh_fprintf(qh, 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 (!newmerge && qh->CHECKduplicates && qh->hull_dim < 5 && (qh->IStracing > 2 || qh->CHECKfrequently)) { + FOREACHridge_i_(qh, facet->ridges) { /* expensive, if was merge and qh_maybe_duplicateridges hasn't been called yet */ + if (!ridge->mergevertex) { + for (i=ridge_i+1; i < ridge_n; i++) { + ridge2= SETelemt_(facet->ridges, i, ridgeT); + if (SETelem_(ridge->vertices, last_v) == SETelem_(ridge2->vertices, last_v)) { /* SETfirst is likely to be the same */ + if (SETfirst_(ridge->vertices) == SETfirst_(ridge2->vertices)) { + if (qh_setequal(ridge->vertices, ridge2->vertices)) { + qh_fprintf(qh, qh->ferr, 6294, "qhull internal error (qh_checkfacet): ridges r%d and r%d (f%d) have the same vertices\n", /* same as duplicate ridge */ + ridge->id, ridge2->id, facet->id); + errridge= ridge; + waserror= True; + } + } + } + } + } + } + } + if (waserror) { + qh_errprint(qh, "ERRONEOUS", facet, errother, errridge, NULL); + *waserrorp= True; + } +} /* checkfacet */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="checkflipped_all">-</a> + + qh_checkflipped_all(qh, facetlist ) + checks orientation of facets in list against interior point + + notes: + called by qh_checkoutput +*/ +void qh_checkflipped_all(qhT *qh, facetT *facetlist) { + facetT *facet; + boolT waserror= False; + realT dist; + + if (facetlist == qh->facet_list) + zzval_(Zflippedfacets)= 0; + FORALLfacet_(facetlist) { + if (facet->normal && !qh_checkflipped(qh, facet, &dist, !qh_ALL)) { + qh_fprintf(qh, qh->ferr, 6136, "qhull precision error: facet f%d is flipped, distance= %6.12g\n", + facet->id, dist); + if (!qh->FORCEoutput) { + qh_errprint(qh, "ERRONEOUS", facet, NULL, NULL, NULL); + waserror= True; + } + } + } + if (waserror) { + qh_fprintf(qh, qh->ferr, 8101, "\n\ +A flipped facet occurs when its distance to the interior point is\n\ +greater than or equal to %2.2g, the maximum roundoff error.\n", -qh->DISTround); + qh_errexit(qh, qh_ERRprec, NULL, NULL); + } +} /* checkflipped_all */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="checklists">-</a> + + qh_checklists(qh, facetlist ) + Check and repair facetlist and qh.vertex_list for infinite loops or overwritten facets + Checks that qh.newvertex_list is on qh.vertex_list + if facetlist is qh.facet_list + Checks that qh.visible_list and qh.newfacet_list are on qh.facet_list + Updates qh.facetvisit and qh.vertexvisit + + returns: + True if no errors found + If false, repairs erroneous lists to prevent infinite loops by FORALL macros + + notes: + called by qh_buildtracing, qh_checkpolygon, qh_collectstatistics, qh_printfacetlist, qh_printsummary + not called by qh_printlists + + design: + if facetlist + check qh.facet_tail + for each facet + check for infinite loop or overwritten facet + check previous facet + if facetlist is qh.facet_list + check qh.next_facet, qh.visible_list and qh.newfacet_list + if vertexlist + check qh.vertex_tail + for each vertex + check for infinite loop or overwritten vertex + check previous vertex + check qh.newvertex_list +*/ +boolT qh_checklists(qhT *qh, facetT *facetlist) { + facetT *facet, *errorfacet= NULL, *errorfacet2= NULL, *previousfacet; + vertexT *vertex, *vertexlist, *previousvertex, *errorvertex= NULL; + boolT waserror= False, newseen= False, nextseen= False, newvertexseen= False, visibleseen= False; + + if (facetlist == qh->newfacet_list || facetlist == qh->visible_list) { + vertexlist= qh->vertex_list; + previousvertex= NULL; + trace2((qh, qh->ferr, 2110, "qh_checklists: check qh.%s_list f%d and qh.vertex_list v%d\n", + (facetlist == qh->newfacet_list ? "newfacet" : "visible"), facetlist->id, getid_(vertexlist))); + }else { + vertexlist= qh->vertex_list; + previousvertex= NULL; + trace2((qh, qh->ferr, 2111, "qh_checklists: check %slist f%d and qh.vertex_list v%d\n", + (facetlist == qh->facet_list ? "qh.facet_" : "facet"), getid_(facetlist), getid_(vertexlist))); + } + if (facetlist) { + if (qh->facet_tail == NULL || qh->facet_tail->id != 0 || qh->facet_tail->next != NULL) { + qh_fprintf(qh, qh->ferr, 6397, "qhull internal error (qh_checklists): either qh.facet_tail f%d is NULL, or its id is not 0, or its next is not NULL\n", + getid_(qh->facet_tail)); + qh_errexit(qh, qh_ERRqhull, qh->facet_tail, NULL); + } + previousfacet= (facetlist == qh->facet_list ? NULL : facetlist->previous); + qh->visit_id++; + FORALLfacet_(facetlist) { + if (facet->visitid >= qh->visit_id || facet->id >= qh->facet_id) { + waserror= True; + errorfacet= facet; + errorfacet2= previousfacet; + if (facet->visitid == qh->visit_id) + qh_fprintf(qh, qh->ferr, 6039, "qhull internal error (qh_checklists): f%d already in facetlist causing an infinite loop ... f%d > f%d ... > f%d > f%d. Truncate facetlist at f%d\n", + facet->id, facet->id, facet->next->id, getid_(previousfacet), facet->id, getid_(previousfacet)); + else + qh_fprintf(qh, qh->ferr, 6350, "qhull internal error (qh_checklists): unknown or overwritten facet f%d, either id >= qh.facet_id (%d) or f.visitid %u > qh.visit_id %u. Facetlist terminated at previous facet f%d\n", + facet->id, qh->facet_id, facet->visitid, qh->visit_id, getid_(previousfacet)); + if (previousfacet) + previousfacet->next= qh->facet_tail; + else + facetlist= qh->facet_tail; + break; + } + facet->visitid= qh->visit_id; + if (facet->previous != previousfacet) { + qh_fprintf(qh, qh->ferr, 6416, "qhull internal error (qh_checklists): expecting f%d.previous == f%d. Got f%d\n", + facet->id, getid_(previousfacet), getid_(facet->previous)); + waserror= True; + errorfacet= facet; + errorfacet2= facet->previous; + } + previousfacet= facet; + if (facetlist == qh->facet_list) { + if (facet == qh->visible_list) { + if(newseen){ + qh_fprintf(qh, qh->ferr, 6285, "qhull internal error (qh_checklists): qh.visible_list f%d is after qh.newfacet_list f%d. It should be at, before, or NULL\n", + facet->id, getid_(qh->newfacet_list)); + waserror= True; + errorfacet= facet; + errorfacet2= qh->newfacet_list; + } + visibleseen= True; + } + if (facet == qh->newfacet_list) + newseen= True; + if (facet == qh->facet_next) + nextseen= True; + } + } + if (facetlist == qh->facet_list) { + if (!nextseen && qh->facet_next && qh->facet_next->next) { + qh_fprintf(qh, qh->ferr, 6369, "qhull internal error (qh_checklists): qh.facet_next f%d for qh_addpoint is not on qh.facet_list f%d\n", + qh->facet_next->id, facetlist->id); + waserror= True; + errorfacet= qh->facet_next; + errorfacet2= facetlist; + } + if (!newseen && qh->newfacet_list && qh->newfacet_list->next) { + qh_fprintf(qh, qh->ferr, 6286, "qhull internal error (qh_checklists): qh.newfacet_list f%d is not on qh.facet_list f%d\n", + qh->newfacet_list->id, facetlist->id); + waserror= True; + errorfacet= qh->newfacet_list; + errorfacet2= facetlist; + } + if (!visibleseen && qh->visible_list && qh->visible_list->next) { + qh_fprintf(qh, qh->ferr, 6138, "qhull internal error (qh_checklists): qh.visible_list f%d is not on qh.facet_list f%d\n", + qh->visible_list->id, facetlist->id); + waserror= True; + errorfacet= qh->visible_list; + errorfacet2= facetlist; + } + } + } + if (vertexlist) { + if (qh->vertex_tail == NULL || qh->vertex_tail->id != 0 || qh->vertex_tail->next != NULL) { + qh_fprintf(qh, qh->ferr, 6366, "qhull internal error (qh_checklists): either qh.vertex_tail v%d is NULL, or its id is not 0, or its next is not NULL\n", + getid_(qh->vertex_tail)); + qh_errprint(qh, "ERRONEOUS", errorfacet, errorfacet2, NULL, qh->vertex_tail); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + qh->vertex_visit++; + FORALLvertex_(vertexlist) { + if (vertex->visitid >= qh->vertex_visit || vertex->id >= qh->vertex_id) { + waserror= True; + errorvertex= vertex; + if (vertex->visitid == qh->visit_id) + qh_fprintf(qh, qh->ferr, 6367, "qhull internal error (qh_checklists): v%d already in vertexlist causing an infinite loop ... v%d > v%d ... > v%d > v%d. Truncate vertexlist at v%d\n", + vertex->id, vertex->id, vertex->next->id, getid_(previousvertex), vertex->id, getid_(previousvertex)); + else + qh_fprintf(qh, qh->ferr, 6368, "qhull internal error (qh_checklists): unknown or overwritten vertex v%d, either id >= qh.vertex_id (%d) or v.visitid %u > qh.visit_id %u. vertexlist terminated at previous vertex v%d\n", + vertex->id, qh->vertex_id, vertex->visitid, qh->visit_id, getid_(previousvertex)); + if (previousvertex) + previousvertex->next= qh->vertex_tail; + else + vertexlist= qh->vertex_tail; + break; + } + vertex->visitid= qh->vertex_visit; + if (vertex->previous != previousvertex) { + qh_fprintf(qh, qh->ferr, 6427, "qhull internal error (qh_checklists): expecting v%d.previous == v%d. Got v%d\n", + vertex->id, previousvertex, getid_(vertex->previous)); + waserror= True; + errorvertex= vertex; + } + previousvertex= vertex; + if(vertex == qh->newvertex_list) + newvertexseen= True; + } + if(!newvertexseen && qh->newvertex_list && qh->newvertex_list->next) { + qh_fprintf(qh, qh->ferr, 6287, "qhull internal error (qh_checklists): new vertex list v%d is not on vertex list\n", qh->newvertex_list->id); + waserror= True; + errorvertex= qh->newvertex_list; + } + } + if (waserror) { + qh_errprint(qh, "ERRONEOUS", errorfacet, errorfacet2, NULL, errorvertex); + return False; + } + return True; +} /* checklists */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="checkpolygon">-</a> + + qh_checkpolygon(qh, facetlist ) + checks the correctness of the structure + + notes: + called by qh_addpoint, qh_all_vertexmerge, qh_check_output, qh_initialhull, qh_prepare_output, qh_triangulate + call with qh.facet_list or qh.newfacet_list or another list + checks num_facets and num_vertices if qh.facet_list + + design: + check and repair lists for infinite loop + for each facet + check f.newfacet and f.visible + check facet and outside set if qh.NEWtentative and not f.newfacet, or not f.visible + initializes vertexlist for qh.facet_list or qh.newfacet_list + for each vertex + check vertex + check v.newfacet + for each facet + count f.ridges + check and count f.vertices + if checking qh.facet_list + check facet count + if qh.VERTEXneighbors + check and count v.neighbors for all vertices + check v.neighbors count and report possible causes of mismatch + check that facets are in their v.neighbors + check vertex count +*/ +void qh_checkpolygon(qhT *qh, facetT *facetlist) { + facetT *facet, *neighbor, **neighborp; + facetT *errorfacet= NULL, *errorfacet2= NULL; + vertexT *vertex, **vertexp, *vertexlist; + int numfacets= 0, numvertices= 0, numridges= 0; + int totvneighbors= 0, totfacetvertices= 0; + boolT waserror= False, newseen= False, newvertexseen= False, nextseen= False, visibleseen= False; + boolT checkfacet; + + trace1((qh, qh->ferr, 1027, "qh_checkpolygon: check all facets from f%d, qh.NEWtentative? %d\n", facetlist->id, qh->NEWtentative)); + if (!qh_checklists(qh, facetlist)) { + waserror= True; + qh_fprintf(qh, qh->ferr, 6374, "qhull internal error: qh_checklists failed in qh_checkpolygon\n"); + if (qh->num_facets < 4000) + qh_printlists(qh); + } + if (facetlist != qh->facet_list || qh->ONLYgood) + nextseen= True; /* allow f.outsideset */ + FORALLfacet_(facetlist) { + if (facet == qh->visible_list) + visibleseen= True; + if (facet == qh->newfacet_list) + newseen= True; + if (facet->newfacet && !newseen && !visibleseen) { + qh_fprintf(qh, qh->ferr, 6289, "qhull internal error (qh_checkpolygon): f%d is 'newfacet' but it is not on qh.newfacet_list f%d or visible_list f%d\n", facet->id, getid_(qh->newfacet_list), getid_(qh->visible_list)); + qh_errexit(qh, qh_ERRqhull, facet, NULL); + } + if (!facet->newfacet && newseen) { + qh_fprintf(qh, qh->ferr, 6292, "qhull internal error (qh_checkpolygon): f%d is on qh.newfacet_list f%d but it is not 'newfacet'\n", facet->id, getid_(qh->newfacet_list)); + qh_errexit(qh, qh_ERRqhull, facet, NULL); + } + if (facet->visible != (visibleseen & !newseen)) { + if(facet->visible) + qh_fprintf(qh, qh->ferr, 6290, "qhull internal error (qh_checkpolygon): f%d is 'visible' but it is not on qh.visible_list f%d\n", facet->id, getid_(qh->visible_list)); + else + qh_fprintf(qh, qh->ferr, 6291, "qhull internal error (qh_checkpolygon): f%d is on qh.visible_list f%d but it is not 'visible'\n", facet->id, qh->newfacet_list->id); + qh_errexit(qh, qh_ERRqhull, facet, NULL); + } + if (qh->NEWtentative) { + checkfacet= !facet->newfacet; + }else { + checkfacet= !facet->visible; + } + if(checkfacet) { + if (!nextseen) { + if (facet == qh->facet_next) /* previous facets do not have outsideset */ + nextseen= True; + else if (qh_setsize(qh, facet->outsideset)) { + if (!qh->NARROWhull +#if !qh_COMPUTEfurthest + || facet->furthestdist >= qh->MINoutside +#endif + ) { + qh_fprintf(qh, qh->ferr, 6137, "qhull internal error (qh_checkpolygon): f%d has outside points before qh.facet_next f%d\n", + facet->id, getid_(qh->facet_next)); + qh_errexit2(qh, qh_ERRqhull, facet, qh->facet_next); + } + } + } + numfacets++; + qh_checkfacet(qh, facet, False, &waserror); + }else if (facet->visible && qh->NEWfacets) { + if (!SETempty_(facet->neighbors) || !SETempty_(facet->ridges)) { + qh_fprintf(qh, qh->ferr, 6376, "qhull internal error (qh_checkpolygon): expecting empty f.neighbors and f.ridges for visible facet f%d. Got %d neighbors and %d ridges\n", + facet->id, qh_setsize(qh, facet->neighbors), qh_setsize(qh, facet->ridges)); + qh_errexit(qh, qh_ERRqhull, facet, 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) { + qh_checkvertex(qh, vertex, !qh_ALL, &waserror); + if(vertex == qh->newvertex_list) + newvertexseen= True; + vertex->seen= False; + vertex->visitid= 0; + if(vertex->newfacet && !newvertexseen && !vertex->deleted) { + qh_fprintf(qh, qh->ferr, 6288, "qhull internal error (qh_checkpolygon): v%d is 'newfacet' but it is not on new vertex list v%d\n", vertex->id, getid_(qh->newvertex_list)); + qh_errexit(qh, qh_ERRqhull, qh->visible_list, NULL); + } + } + FORALLfacet_(facetlist) { + if (facet->visible) + continue; + if (facet->simplicial) + numridges += qh->hull_dim; + else + numridges += qh_setsize(qh, facet->ridges); + FOREACHvertex_(facet->vertices) { + vertex->visitid++; + if (!vertex->seen) { + vertex->seen= True; + numvertices++; + if (qh_pointid(qh, vertex->point) == qh_IDunknown) { + qh_fprintf(qh, 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, 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 { + if (!vertex->neighbors) { + qh_fprintf(qh, qh->ferr, 6407, "qhull internal error (qh_checkpolygon): missing vertex neighbors for v%d\n", vertex->id); + waserror= True; + } + qh_setcheck(qh, vertex->neighbors, "neighbors for v", vertex->id); + if (vertex->deleted) + continue; + totvneighbors += qh_setsize(qh, vertex->neighbors); + } + FORALLfacet_(facetlist) { + if (!facet->visible) + totfacetvertices += qh_setsize(qh, facet->vertices); + } + if (totvneighbors != totfacetvertices) { + qh_fprintf(qh, qh->ferr, 6141, "qhull internal error (qh_checkpolygon): vertex neighbors inconsistent (tot_vneighbors %d != tot_facetvertices %d). Maybe duplicate or missing vertex\n", + totvneighbors, totfacetvertices); + waserror= True; + FORALLvertices { + if (vertex->deleted) + continue; + qh->visit_id++; + FOREACHneighbor_(vertex) { + if (neighbor->visitid==qh->visit_id) { + qh_fprintf(qh, qh->ferr, 6275, "qhull internal error (qh_checkpolygon): facet f%d occurs twice in neighbors of vertex v%d\n", + neighbor->id, vertex->id); + errorfacet2= errorfacet; + errorfacet= neighbor; + } + neighbor->visitid= qh->visit_id; + if (!qh_setin(neighbor->vertices, vertex)) { + qh_fprintf(qh, qh->ferr, 6276, "qhull internal error (qh_checkpolygon): facet f%d is a neighbor of vertex v%d but v%d is not a vertex of f%d\n", + neighbor->id, vertex->id, vertex->id, neighbor->id); + errorfacet2= errorfacet; + errorfacet= neighbor; + } + } + } + FORALLfacet_(facetlist){ + if (!facet->visible) { + /* vertices are inverse sorted and are unlikely to be duplicated */ + FOREACHvertex_(facet->vertices){ + if (!qh_setin(vertex->neighbors, facet)) { + qh_fprintf(qh, qh->ferr, 6277, "qhull internal error (qh_checkpolygon): v%d is a vertex of facet f%d but f%d is not a neighbor of v%d\n", + vertex->id, facet->id, facet->id, vertex->id); + errorfacet2= errorfacet; + errorfacet= facet; + } + } + } + } + } + } + if (numvertices != qh->num_vertices - qh_setsize(qh, qh->del_vertices)) { + qh_fprintf(qh, 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, qh->del_vertices)); + waserror= True; + } + if (qh->hull_dim == 2 && numvertices != numfacets) { + qh_fprintf(qh, 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, qh->ferr, 7063, "qhull warning: #vertices %d + #facets %d - #edges %d != 2. A vertex appears twice in a edge list. May occur during merging.\n", + 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_errexit2(qh, qh_ERRqhull, errorfacet, errorfacet2); +} /* checkpolygon */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="checkvertex">-</a> + + qh_checkvertex(qh, vertex, allchecks, &waserror ) + check vertex for consistency + if allchecks, checks vertex->neighbors + + returns: + sets waserror if any error occurs + + notes: + called by qh_tracemerge and qh_checkpolygon + neighbors checked efficiently in qh_checkpolygon +*/ +void qh_checkvertex(qhT *qh, vertexT *vertex, boolT allchecks, boolT *waserrorp) { + boolT waserror= False; + facetT *neighbor, **neighborp, *errfacet=NULL; + + if (qh_pointid(qh, vertex->point) == qh_IDunknown) { + qh_fprintf(qh, 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, qh->ferr, 6145, "qhull internal error (qh_checkvertex): unknown vertex id v%d >= qh.vertex_id (%d)\n", vertex->id, qh->vertex_id); + waserror= True; + } + if (vertex->visitid > qh->vertex_visit) { + qh_fprintf(qh, qh->ferr, 6413, "qhull internal error (qh_checkvertex): expecting v%d.visitid <= qh.vertex_visit (%d). Got visitid %d\n", vertex->id, qh->vertex_visit, vertex->visitid); + waserror= True; + } + if (allchecks && !waserror && !vertex->deleted) { + if (qh_setsize(qh, vertex->neighbors)) { + FOREACHneighbor_(vertex) { + if (!qh_setin(neighbor->vertices, vertex)) { + qh_fprintf(qh, 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(qh, "ERRONEOUS", NULL, NULL, NULL, vertex); + if (errfacet) + qh_errexit(qh, qh_ERRqhull, errfacet, NULL); + *waserrorp= True; + } +} /* checkvertex */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="clearcenters">-</a> + + qh_clearcenters(qh, type ) + clear old data from facet->center + + notes: + sets new centertype + nop if CENTERtype is the same +*/ +void qh_clearcenters(qhT *qh, qh_CENTER type) { + facetT *facet; + + if (qh->CENTERtype != type) { + FORALLfacets { + if (facet->tricoplanar && !facet->keepcentrum) + facet->center= NULL; /* center is owned by the ->keepcentrum facet */ + else if (qh->CENTERtype == qh_ASvoronoi){ + if (facet->center) { + qh_memfree(qh, facet->center, qh->center_size); + facet->center= NULL; + } + }else /* qh.CENTERtype == qh_AScentrum */ { + if (facet->center) { + qh_memfree(qh, facet->center, qh->normal_size); + facet->center= NULL; + } + } + } + qh->CENTERtype= type; + } + trace2((qh, qh->ferr, 2043, "qh_clearcenters: switched to center type %d\n", type)); +} /* clearcenters */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="createsimplex">-</a> + + qh_createsimplex(qh, vertices ) + creates a simplex from a set of vertices + + returns: + initializes qh.facet_list to the simplex + + notes: + only called by qh_initialhull + + design: + for each vertex + create a new facet + for each new facet + create its neighbor set +*/ +void qh_createsimplex(qhT *qh, setT *vertices /* qh.facet_list */) { + facetT *facet= NULL, *newfacet; + boolT toporient= True; + int vertex_i, vertex_n, nth; + setT *newfacets= qh_settemp(qh, qh->hull_dim+1); + vertexT *vertex; + + FOREACHvertex_i_(qh, vertices) { + newfacet= qh_newfacet(qh); + newfacet->vertices= qh_setnew_delnthsorted(qh, vertices, vertex_n, vertex_i, 0); + if (toporient) + newfacet->toporient= True; + qh_appendfacet(qh, newfacet); + newfacet->newfacet= True; + qh_appendvertex(qh, vertex); + qh_setappend(qh, &newfacets, newfacet); + toporient ^= True; + } + FORALLnew_facets { + nth= 0; + FORALLfacet_(qh->newfacet_list) { + if (facet != newfacet) + SETelem_(newfacet->neighbors, nth++)= facet; + } + qh_settruncate(qh, newfacet->neighbors, qh->hull_dim); + } + qh_settempfree(qh, &newfacets); + trace1((qh, qh->ferr, 1028, "qh_createsimplex: created simplex\n")); +} /* createsimplex */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="delridge">-</a> + + qh_delridge(qh, ridge ) + delete a ridge's vertices and frees its memory + + notes: + assumes r.top->ridges and r.bottom->ridges have been updated +*/ +void qh_delridge(qhT *qh, ridgeT *ridge) { + + if (ridge == qh->traceridge) + qh->traceridge= NULL; + qh_setfree(qh, &(ridge->vertices)); + qh_memfree(qh, ridge, (int)sizeof(ridgeT)); +} /* delridge */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="delvertex">-</a> + + qh_delvertex(qh, vertex ) + deletes a vertex and frees its memory + + notes: + assumes vertex->adjacencies have been updated if needed + unlinks from vertex_list +*/ +void qh_delvertex(qhT *qh, vertexT *vertex) { + + if (vertex->deleted && !vertex->partitioned && !qh->NOerrexit) { + qh_fprintf(qh, qh->ferr, 6395, "qhull internal error (qh_delvertex): vertex v%d was deleted but it was not partitioned as a coplanar point\n", + vertex->id); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + if (vertex == qh->tracevertex) + qh->tracevertex= NULL; + qh_removevertex(qh, vertex); + qh_setfree(qh, &vertex->neighbors); + qh_memfree(qh, vertex, (int)sizeof(vertexT)); +} /* delvertex */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="facet3vertex">-</a> + + qh_facet3vertex(qh ) + 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(qhT *qh, facetT *facet) { + ridgeT *ridge, *firstridge; + vertexT *vertex; + int cntvertices, cntprojected=0; + setT *vertices; + + cntvertices= qh_setsize(qh, facet->vertices); + vertices= qh_settemp(qh, cntvertices); + if (facet->simplicial) { + if (cntvertices != 3) { + qh_fprintf(qh, qh->ferr, 6147, "qhull internal error (qh_facet3vertex): only %d vertices for simplicial facet f%d\n", + cntvertices, facet->id); + qh_errexit(qh, qh_ERRqhull, facet, NULL); + } + qh_setappend(qh, &vertices, SETfirst_(facet->vertices)); + if (facet->toporient ^ qh_ORIENTclock) + qh_setappend(qh, &vertices, SETsecond_(facet->vertices)); + else + qh_setaddnth(qh, &vertices, 0, SETsecond_(facet->vertices)); + qh_setappend(qh, &vertices, SETelem_(facet->vertices, 2)); + }else { + ridge= firstridge= SETfirstt_(facet->ridges, ridgeT); /* no infinite */ + while ((ridge= qh_nextridge3d(ridge, facet, &vertex))) { + qh_setappend(qh, &vertices, vertex); + if (++cntprojected > cntvertices || ridge == firstridge) + break; + } + if (!ridge || cntprojected != cntvertices) { + qh_fprintf(qh, 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, qh_ERRqhull, facet, ridge); + } + } + return vertices; +} /* facet3vertex */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="findbestfacet">-</a> + + qh_findbestfacet(qh, 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: + Distance is measured by distance to the facet's hyperplane. For + Delaunay facets, this is not the same as the containing facet. It may + be an adjacent facet or a different tricoplanar facet. See + <a href="../html/qh-code.htm#findfacet">locate a facet with qh_findbestfacet()</a> + + For tricoplanar facets, this finds one of the tricoplanar facets closest + to the point. + + 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: + <a href="geom_r.c#findbest">qh_findbest</a> +*/ +facetT *qh_findbestfacet(qhT *qh, pointT *point, boolT bestoutside, + realT *bestdist, boolT *isoutside) { + facetT *bestfacet= NULL; + int numpart, totpart= 0; + + bestfacet= qh_findbest(qh, point, qh->facet_list, + bestoutside, !qh_ISnewfacets, bestoutside /* qh_NOupper */, + bestdist, isoutside, &totpart); + if (*bestdist < -qh->DISTround) { + bestfacet= qh_findfacet_all(qh, point, !qh_NOupper, bestdist, isoutside, &numpart); + totpart += numpart; + if ((isoutside && *isoutside && bestoutside) + || (isoutside && !*isoutside && bestfacet->upperdelaunay)) { + bestfacet= qh_findbest(qh, point, bestfacet, + bestoutside, False, bestoutside, + bestdist, isoutside, &totpart); + totpart += numpart; + } + } + trace3((qh, qh->ferr, 3014, "qh_findbestfacet: f%d dist %2.2g isoutside %d totpart %d\n", + bestfacet->id, *bestdist, (isoutside ? *isoutside : UINT_MAX), totpart)); + return bestfacet; +} /* findbestfacet */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="findbestlower">-</a> + + qh_findbestlower(qh, 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: + called by qh_findbest() for points above an upperdelaunay facet + if Delaunay and inside, point is outside of circumsphere of bestfacet + +*/ +facetT *qh_findbestlower(qhT *qh, facetT *upperfacet, pointT *point, realT *bestdistp, int *numpart) { + facetT *neighbor, **neighborp, *bestfacet= NULL; + realT bestdist= -REALmax/2 /* avoid underflow */; + realT dist; + vertexT *vertex; + boolT isoutside= False; /* not used */ + + zinc_(Zbestlower); + FOREACHneighbor_(upperfacet) { + if (neighbor->upperdelaunay || neighbor->flipped) + continue; + (*numpart)++; + qh_distplane(qh, 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(qh, upperfacet, point, &dist); + qh_vertexneighbors(qh); + FOREACHneighbor_(vertex) { + if (neighbor->upperdelaunay || neighbor->flipped) + continue; + (*numpart)++; + qh_distplane(qh, point, neighbor, &dist); + if (dist > bestdist) { + bestfacet= neighbor; + bestdist= dist; + } + } + } + if (!bestfacet) { + zinc_(Zbestlowerall); /* invoked once per point in outsideset */ + zmax_(Zbestloweralln, qh->num_facets); + /* [dec'15] Previously reported as QH6228 */ + trace3((qh, qh->ferr, 3025, "qh_findbestlower: all neighbors of facet %d are flipped or upper Delaunay. Search all facets\n", + upperfacet->id)); + /* rarely called */ + bestfacet= qh_findfacet_all(qh, point, qh_NOupper, &bestdist, &isoutside, numpart); + } + *bestdistp= bestdist; + trace3((qh, qh->ferr, 3015, "qh_findbestlower: f%d dist %2.2g for f%d p%d\n", + bestfacet->id, bestdist, upperfacet->id, qh_pointid(qh, point))); + return bestfacet; +} /* findbestlower */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="findfacet_all">-</a> + + qh_findfacet_all(qh, point, noupper, bestdist, isoutside, numpart ) + exhaustive search for facet below a point + ignore flipped and visible facets, f.normal==NULL, and if noupper, f.upperdelaunay facets + + 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: + called by qh_findbestlower if all neighbors are flipped or upper Delaunay (QH3025) + primarily for library users (qh_findbestfacet), rarely used by Qhull +*/ +facetT *qh_findfacet_all(qhT *qh, pointT *point, boolT noupper, 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 || facet->visible) + continue; + if (noupper && facet->upperdelaunay) + continue; + totpart++; + qh_distplane(qh, point, facet, &dist); + if (dist > *bestdist) { + *bestdist= dist; + bestfacet= facet; + if (dist > qh->MINoutside) { + *isoutside= True; + break; + } + } + } + *numpart= totpart; + trace3((qh, qh->ferr, 3016, "qh_findfacet_all: p%d, noupper? %d, f%d, dist %2.2g, isoutside %d, totpart %d\n", + qh_pointid(qh, point), noupper, getid_(bestfacet), *bestdist, *isoutside, totpart)); + return bestfacet; +} /* findfacet_all */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="findgood">-</a> + + qh_findgood(qh, facetlist, goodhorizon ) + identify good facets for qh.PRINTgood and qh_buildcone_onlygood + goodhorizon is count of good, horizon facets from qh_find_horizon, otherwise 0 from qh_findgood_all + if not qh.MERGING and qh.GOODvertex>0 + facet includes point as vertex + if !match, returns goodhorizon + 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 to thresholds + sets GOODclosest + + returns: + number of new, good facets found + determines facet->good + may update qh.GOODclosest + + notes: + called from qh_initbuild, qh_buildcone_onlygood, and qh_findgood_all + qh_findgood_all (called from qh_prepare_output) further reduces the good region + + design: + count good facets + if not merging, clear good facets that fail qh.GOODvertex ('QVn', but not 'QV-n') + clear good facets that fail qh.GOODpoint ('QGn' or 'QG-n') + clear good facets that fail qh.GOODthreshold + if !goodhorizon and !find f.good, + sets GOODclosest to facet with closest angle to thresholds +*/ +int qh_findgood(qhT *qh, 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 (facet->good && !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, 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(qh, facet->normal, &angle)) { + facet->good= False; + numgood--; + if (angle < bestangle) { + bestangle= angle; + bestfacet= facet; + } + } + } + } + if (numgood == 0 && (goodhorizon == 0 || qh->GOODclosest)) { + if (qh->GOODclosest) { + if (qh->GOODclosest->visible) + qh->GOODclosest= NULL; + else { + qh_inthresholds(qh, qh->GOODclosest->normal, &angle); + if (angle < bestangle) + bestfacet= qh->GOODclosest; + } + } + if (bestfacet && bestfacet != qh->GOODclosest) { /* numgood == 0 */ + if (qh->GOODclosest) + qh->GOODclosest->good= False; + qh->GOODclosest= bestfacet; + bestfacet->good= True; + numgood++; + trace2((qh, 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, qh->ferr, 2045, "qh_findgood: found %d good facets with %d good horizon and qh.GOODclosest f%d\n", + numgood, goodhorizon, getid_(qh->GOODclosest))); + if (!numgood && qh->GOODvertex>0 && !qh->MERGING) + return goodhorizon; + return numgood; +} /* findgood */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="findgood_all">-</a> + + qh_findgood_all(qh, 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 (e.g., qh.DELAUNAY) + 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: + called by qh_prepare_output and qh_printneighborhood + unless qh.ONLYgood, calls qh_findgood first + + design: + uses qh_findgood to mark good facets + clear f.good for failed qh.GOODvertex + clear f.good for failed qh.SPLITthreholds + if no more good facets, select best of qh.SPLITthresholds +*/ +void qh_findgood_all(qhT *qh, 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, 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))) { /* convert to bool */ + if (!--numgood) { + if (qh->ONLYgood) { + qh_fprintf(qh, qh->ferr, 7064, "qhull warning: good vertex p%d does not match last good facet f%d. Ignored.\n", + qh_pointid(qh, qh->GOODvertexp), facet->id); + return; + }else if (qh->GOODvertex > 0) + qh_fprintf(qh, 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, 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(qh, facet->normal, &angle)) { + facet->good= False; + numgood--; + if (angle < bestangle) { + bestangle= angle; + bestfacet= facet; + } + } + } + } + if (!numgood && bestfacet) { + bestfacet->good= True; + numgood++; + trace0((qh, qh->ferr, 23, "qh_findgood_all: f%d is closest(%2.2g) to split thresholds\n", + bestfacet->id, bestangle)); + return; + } + } + if (numgood == 1 && !qh->PRINTgood && qh->GOODclosest && qh->GOODclosest->good) { + trace2((qh, qh->ferr, 2109, "qh_findgood_all: undo selection of qh.GOODclosest f%d since it would fail qh_inthresholds in qh_skipfacet\n", + qh->GOODclosest->id)); + qh->GOODclosest->good= False; + numgood= 0; + } + qh->num_good= numgood; + trace0((qh, qh->ferr, 24, "qh_findgood_all: %d good facets remain out of %d facets\n", + numgood, startgood)); +} /* findgood_all */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="furthestnext">-</a> + + 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(qhT *qh /* 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(qh, furthest, facet, &dist); +#else + dist= facet->furthestdist; +#endif + if (dist > bestdist) { + bestfacet= facet; + bestdist= dist; + } + } + } + if (bestfacet) { + qh_removefacet(qh, bestfacet); + qh_prependfacet(qh, bestfacet, &qh->facet_next); + trace1((qh, qh->ferr, 1029, "qh_furthestnext: made f%d next facet(dist %.2g)\n", + bestfacet->id, bestdist)); + } +} /* furthestnext */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="furthestout">-</a> + + qh_furthestout(qh, 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(qhT *qh, facetT *facet) { + pointT *point, **pointp, *bestpoint= NULL; + realT dist, bestdist= -REALmax; + + FOREACHpoint_(facet->outsideset) { + qh_distplane(qh, point, facet, &dist); + zinc_(Zcomputefurthest); + if (dist > bestdist) { + bestpoint= point; + bestdist= dist; + } + } + if (bestpoint) { + qh_setdel(facet->outsideset, point); + qh_setappend(qh, &facet->outsideset, point); +#if !qh_COMPUTEfurthest + facet->furthestdist= bestdist; +#endif + } + facet->notfurthest= False; + trace3((qh, qh->ferr, 3017, "qh_furthestout: p%d is furthest outside point of f%d\n", + qh_pointid(qh, point), facet->id)); +} /* furthestout */ + + +/*-<a href="qh-qhull_r.htm#TOC" + >-------------------------------</a><a name="infiniteloop">-</a> + + qh_infiniteloop(qh, facet ) + report infinite loop error due to facet +*/ +void qh_infiniteloop(qhT *qh, facetT *facet) { + + qh_fprintf(qh, qh->ferr, 6149, "qhull internal error (qh_infiniteloop): potential infinite loop detected. If visible, f.replace. If newfacet, f.samecycle\n"); + qh_errexit(qh, qh_ERRqhull, facet, NULL); +} /* qh_infiniteloop */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="initbuild">-</a> + + 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') + initialize qh.newfacet_list, qh.facet_tail + initialize qh.vertex_list, qh.newvertex_list, qh.vertex_tail + determine initial vertices + 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(qhT *qh) { + setT *maxpoints, *vertices; + facetT *facet; + int i, numpart; + realT dist; + boolT isoutside; + + if (qh->PRINTstatistics) { + qh_fprintf(qh, qh->ferr, 9350, "qhull %s Statistics: %s | %s\n", + qh_version, qh->rbox_command, qh->qhull_command); + fflush(NULL); + } + qh->furthest_id= qh_IDunknown; + qh->lastreport= 0; + qh->lastfacets= 0; + qh->lastmerges= 0; + qh->lastplanes= 0; + qh->lastdist= 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, qh->GOODpoint-1); + else if (qh->GOODpoint < 0) + qh->GOODpointp= qh_point(qh, -qh->GOODpoint-1); + if (qh->GOODvertex > 0) + qh->GOODvertexp= qh_point(qh, qh->GOODvertex-1); + else if (qh->GOODvertex < 0) + qh->GOODvertexp= qh_point(qh, -qh->GOODvertex-1); + if ((qh->GOODpoint + && (qh->GOODpointp < qh->first_point /* also catches !GOODpointp */ + || qh->GOODpointp > qh_point(qh, qh->num_points-1))) + || (qh->GOODvertex + && (qh->GOODvertexp < qh->first_point /* also catches !GOODvertexp */ + || qh->GOODvertexp > qh_point(qh, qh->num_points-1)))) { + qh_fprintf(qh, qh->ferr, 6150, "qhull input error: either QGn or QVn point is > p%d\n", + qh->num_points-1); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + maxpoints= qh_maxmin(qh, qh->first_point, qh->num_points, qh->hull_dim); + if (qh->SCALElast) + qh_scalelast(qh, qh->first_point, qh->num_points, qh->hull_dim, qh->MINlastcoord, qh->MAXlastcoord, qh->MAXabs_coord); + qh_detroundoff(qh); + 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. */ + } + } + } + trace4((qh, qh->ferr, 4091, "qh_initbuild: create sentinels for qh.facet_tail and qh.vertex_tail\n")); + qh->facet_list= qh->newfacet_list= qh->facet_tail= qh_newfacet(qh); + qh->num_facets= qh->num_vertices= qh->num_visible= 0; + qh->vertex_list= qh->newvertex_list= qh->vertex_tail= qh_newvertex(qh, NULL); + vertices= qh_initialvertices(qh, qh->hull_dim, maxpoints, qh->first_point, qh->num_points); + qh_initialhull(qh, vertices); /* initial qh->facet_list */ + qh_partitionall(qh, vertices, qh->first_point, qh->num_points); + if (qh->PRINToptions1st || qh->TRACElevel || qh->IStracing) { + if (qh->TRACElevel || qh->IStracing) + qh_fprintf(qh, qh->ferr, 8103, "\nTrace level T%d, IStracing %d, point TP%d, merge TM%d, dist TW%2.2g, qh.tracefacet_id %d, traceridge_id %d, tracevertex_id %d, last qh.RERUN %d, %s | %s\n", + qh->TRACElevel, qh->IStracing, qh->TRACEpoint, qh->TRACEmerge, qh->TRACEdist, qh->tracefacet_id, qh->traceridge_id, qh->tracevertex_id, qh->TRACElastrun, qh->rbox_command, qh->qhull_command); + qh_fprintf(qh, qh->ferr, 8104, "Options selected for Qhull %s:\n%s\n", qh_version, qh->qhull_options); + } + qh_resetlists(qh, False, qh_RESETvisible /* qh.visible_list newvertex_list qh.newfacet_list */); + qh->facet_next= qh->facet_list; + qh_furthestnext(qh /* qh.facet_list */); + if (qh->PREmerge) { + qh->cos_max= qh->premerge_cos; + qh->centrum_radius= qh->premerge_centrum; /* overwritten by qh_premerge */ + } + if (qh->ONLYgood) { + if (qh->GOODvertex > 0 && qh->MERGING) { + qh_fprintf(qh, 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, qh_ERRinput, NULL, NULL); + } + if (!(qh->GOODthreshold || qh->GOODpoint + || (!qh->MERGEexact && !qh->PREmerge && qh->GOODvertexp))) { + qh_fprintf(qh, qh->ferr, 6152, "qhull input error: 'Qg' (ONLYgood) needs a good threshold('Pd0D0'), a good point(QGn or QG-n), or a good vertex with 'QJ' or 'Q0' (QVn).\n"); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + if (qh->GOODvertex > 0 && !qh->MERGING /* matches qh_partitionall */ + && !qh_isvertex(qh->GOODvertexp, vertices)) { + facet= qh_findbestnew(qh, qh->GOODvertexp, qh->facet_list, + &dist, !qh_ALL, &isoutside, &numpart); + zadd_(Zdistgood, numpart); + if (!isoutside) { + qh_fprintf(qh, 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, qh->GOODvertexp)); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + } + if (!qh_addpoint(qh, qh->GOODvertexp, facet, False)) { + qh_settempfree(qh, &vertices); + qh_settempfree(qh, &maxpoints); + return; + } + } + qh_findgood(qh, qh->facet_list, 0); + } + qh_settempfree(qh, &vertices); + qh_settempfree(qh, &maxpoints); + trace1((qh, qh->ferr, 1030, "qh_initbuild: initial hull created and points partitioned\n")); +} /* initbuild */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="initialhull">-</a> + + qh_initialhull(qh, vertices ) + constructs the initial hull as a DIM3 simplex of vertices + + notes: + only called by qh_initbuild + + 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(qhT *qh, setT *vertices) { + facetT *facet, *firstfacet, *neighbor, **neighborp; + realT angle, minangle= REALmax, dist; + + qh_createsimplex(qh, vertices /* qh.facet_list */); + qh_resetlists(qh, False, qh_RESETvisible); + qh->facet_next= qh->facet_list; /* advance facet when processed */ + qh->interior_point= qh_getcenter(qh, vertices); + if (qh->IStracing) { + qh_fprintf(qh, qh->ferr, 8105, "qh_initialhull: "); + qh_printpoint(qh, qh->ferr, "qh.interior_point", qh->interior_point); + } + firstfacet= qh->facet_list; + qh_setfacetplane(qh, firstfacet); /* qh_joggle_restart if flipped */ + if (firstfacet->flipped) { + trace1((qh, qh->ferr, 1065, "qh_initialhull: ignore f%d flipped. Test qh.interior_point (p-2) for clearly flipped\n", firstfacet->id)); + firstfacet->flipped= False; + } + zzinc_(Zdistcheck); + qh_distplane(qh, qh->interior_point, firstfacet, &dist); + if (dist > qh->DISTround) { /* clearly flipped */ + trace1((qh, qh->ferr, 1060, "qh_initialhull: initial orientation incorrect, qh.interior_point is %2.2g from f%d. Reversing orientation of all facets\n", + dist, firstfacet->id)); + FORALLfacets + facet->toporient ^= (unsigned char)True; + qh_setfacetplane(qh, firstfacet); + } + FORALLfacets { + if (facet != firstfacet) + qh_setfacetplane(qh, facet); /* qh_joggle_restart if flipped */ + } + FORALLfacets { + if (facet->flipped) { + trace1((qh, qh->ferr, 1066, "qh_initialhull: ignore f%d flipped. Test qh.interior_point (p-2) for clearly flipped\n", facet->id)); + facet->flipped= False; + } + zzinc_(Zdistcheck); + qh_distplane(qh, qh->interior_point, facet, &dist); /* duplicates qh_setfacetplane */ + if (dist > qh->DISTround) { /* clearly flipped, due to axis-parallel facet or coplanar firstfacet */ + trace1((qh, qh->ferr, 1031, "qh_initialhull: initial orientation incorrect, qh.interior_point is %2.2g from f%d. Either axis-parallel facet or coplanar firstfacet f%d. Force outside orientation of all facets\n")); + FORALLfacets { /* reuse facet, then 'break' */ + facet->flipped= False; + facet->toporient ^= (unsigned char)True; + qh_orientoutside(qh, facet); /* force outside orientation for f.normal */ + } + break; + } + } + FORALLfacets { + if (!qh_checkflipped(qh, facet, NULL, qh_ALL)) { + if (qh->DELAUNAY && ! qh->ATinfinity) { + qh_joggle_restart(qh, "initial Delaunay cocircular or cospherical"); + if (qh->UPPERdelaunay) + qh_fprintf(qh, qh->ferr, 6240, "Qhull precision error: initial Delaunay input sites are cocircular or cospherical. Option 'Qs' searches all points. Use option 'QJ' to joggle the input, otherwise cannot compute the upper Delaunay triangulation or upper Voronoi diagram of cocircular/cospherical points.\n"); + else + qh_fprintf(qh, qh->ferr, 6239, "Qhull precision error: initial Delaunay input sites are cocircular or cospherical. Use option 'Qz' for the Delaunay triangulation or Voronoi diagram of cocircular/cospherical points; it adds a point \"at infinity\". Alternatively use option 'QJ' to joggle the input. Use option 'Qs' to search all points for the initial simplex.\n"); + qh_printvertexlist(qh, qh->ferr, "\ninput sites with last coordinate projected to a paraboloid\n", qh->facet_list, NULL, qh_ALL); + qh_errexit(qh, qh_ERRinput, NULL, NULL); + }else { + qh_joggle_restart(qh, "initial simplex is flat"); + qh_fprintf(qh, qh->ferr, 6154, "Qhull precision error: Initial simplex is flat (facet %d is coplanar with the interior point)\n", + facet->id); + qh_errexit(qh, qh_ERRsingular, NULL, NULL); /* calls qh_printhelp_singular */ + } + } + FOREACHneighbor_(facet) { + angle= qh_getangle(qh, facet->normal, neighbor->normal); + minimize_( minangle, angle); + } + } + if (minangle < qh_MAXnarrow && !qh->NOnarrow) { + realT diff= 1.0 + minangle; + + qh->NARROWhull= True; + qh_option(qh, "_narrow-hull", NULL, &diff); + if (minangle < qh_WARNnarrow && !qh->RERUN && qh->PRINTprecision) + qh_printhelp_narrowhull(qh, qh->ferr, minangle); + } + zzval_(Zprocessed)= qh->hull_dim+1; + qh_checkpolygon(qh, qh->facet_list); + qh_checkconvex(qh, qh->facet_list, qh_DATAfault); + if (qh->IStracing >= 1) { + qh_fprintf(qh, qh->ferr, 8105, "qh_initialhull: simplex constructed\n"); + } +} /* initialhull */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="initialvertices">-</a> + + qh_initialvertices(qh, 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(qhT *qh, 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(qh, dim + 1); + simplex= qh_settemp(qh, dim + 1); + if (qh->ALLpoints) + qh_maxsimplex(qh, dim, NULL, points, numpoints, &simplex); + else if (qh->RANDOMoutside) { + while (qh_setsize(qh, simplex) != dim+1) { + randr= qh_RANDOMint; + randr= randr/(qh_RANDOMmax+1); + randr= floor(qh->num_points * randr); + idx= (int)randr; + while (qh_setin(simplex, qh_point(qh, idx))) { + idx++; /* in case qh_RANDOMint always returns the same value */ + idx= idx < qh->num_points ? idx : 0; + } + qh_setappend(qh, &simplex, qh_point(qh, idx)); + } + }else if (qh->hull_dim >= qh_INITIALmax) { + tested= qh_settemp(qh, dim+1); + qh_setappend(qh, &simplex, SETfirst_(maxpoints)); /* max and min X coord */ + qh_setappend(qh, &simplex, SETsecond_(maxpoints)); + qh_maxsimplex(qh, fmin_(qh_INITIALsearch, dim), maxpoints, points, numpoints, &simplex); + k= qh_setsize(qh, simplex); + FOREACHpoint_i_(qh, maxpoints) { + if (k >= dim) /* qh_maxsimplex for last point */ + break; + if (point_i & 0x1) { /* first try up to dim, max. coord. points */ + if (!qh_setin(simplex, point) && !qh_setin(tested, point)){ + qh_detsimplex(qh, point, simplex, k, &nearzero); + if (nearzero) + qh_setappend(qh, &tested, point); + else { + qh_setappend(qh, &simplex, point); + k++; + } + } + } + } + FOREACHpoint_i_(qh, maxpoints) { + if (k >= dim) /* qh_maxsimplex for last point */ + break; + if ((point_i & 0x1) == 0) { /* then test min. coord points */ + if (!qh_setin(simplex, point) && !qh_setin(tested, point)){ + qh_detsimplex(qh, point, simplex, k, &nearzero); + if (nearzero) + qh_setappend(qh, &tested, point); + else { + qh_setappend(qh, &simplex, point); + k++; + } + } + } + } + /* remove tested points from maxpoints */ + FOREACHpoint_i_(qh, maxpoints) { + if (qh_setin(simplex, point) || qh_setin(tested, point)) + SETelem_(maxpoints, point_i)= NULL; + } + qh_setcompact(qh, maxpoints); + idx= 0; + while (k < dim && (point= qh_point(qh, idx++))) { + if (!qh_setin(simplex, point) && !qh_setin(tested, point)){ + qh_detsimplex(qh, point, simplex, k, &nearzero); + if (!nearzero){ + qh_setappend(qh, &simplex, point); + k++; + } + } + } + qh_settempfree(qh, &tested); + qh_maxsimplex(qh, dim, maxpoints, points, numpoints, &simplex); + }else /* qh.hull_dim < qh_INITIALmax */ + qh_maxsimplex(qh, dim, maxpoints, points, numpoints, &simplex); + FOREACHpoint_(simplex) + qh_setaddnth(qh, &vertices, 0, qh_newvertex(qh, point)); /* descending order */ + qh_settempfree(qh, &simplex); + return vertices; +} /* initialvertices */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="isvertex">-</a> + + qh_isvertex( point, vertices ) + 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 */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="makenewfacets">-</a> + + qh_makenewfacets(qh, point ) + make new facets from point and qh.visible_list + + returns: + apex (point) of the new facets + qh.newfacet_list= list of new facets with hyperplanes and ->newfacet + qh.newvertex_list= list of vertices in new facets with ->newfacet set + + if (qh.NEWtentative) + 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->NEWtentative + qh_matchnewfacets() -- match up neighbors + qh_update_vertexneighbors() -- 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(qhT *qh, pointT *point /* qh.visible_list */) { + facetT *visible, *newfacet= NULL, *newfacet2= NULL, *neighbor, **neighborp; + vertexT *apex; + int numnew=0; + + if (qh->CHECKfrequently) { + qh_checkdelridge(qh); + } + qh->newfacet_list= qh->facet_tail; + qh->newvertex_list= qh->vertex_tail; + apex= qh_newvertex(qh, point); + qh_appendvertex(qh, apex); + qh->visit_id++; + FORALLvisible_facets { + FOREACHneighbor_(visible) + neighbor->seen= False; + if (visible->ridges) { + visible->visitid= qh->visit_id; + newfacet2= qh_makenew_nonsimplicial(qh, visible, apex, &numnew); + } + if (visible->simplicial) + newfacet= qh_makenew_simplicial(qh, visible, apex, &numnew); + if (!qh->NEWtentative) { + if (newfacet2) /* newfacet is null if all ridges defined */ + newfacet= newfacet2; + if (newfacet) + visible->f.replace= newfacet; + else + zinc_(Zinsidevisible); + if (visible->ridges) /* ridges and neighbors are no longer valid for visible facet */ + SETfirst_(visible->ridges)= NULL; + SETfirst_(visible->neighbors)= NULL; + } + } + if (!qh->NEWtentative) + qh->NEWfacets= True; + trace1((qh, qh->ferr, 1032, "qh_makenewfacets: created %d new facets f%d..f%d from point p%d to horizon\n", + numnew, qh->first_newfacet, qh->facet_id-1, qh_pointid(qh, point))); + if (qh->IStracing >= 4) + qh_printfacetlist(qh, qh->newfacet_list, NULL, qh_ALL); + return apex; +} /* makenewfacets */ + +#ifndef qh_NOmerge +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="matchdupridge">-</a> + + qh_matchdupridge(qh, atfacet, atskip, hashsize, hashcount ) + match duplicate ridges in qh.hash_table for atfacet@atskip + duplicates marked with ->dupridge and qh_DUPLICATEridge + + returns: + vertex-facet distance (>0.0) for qh_MERGEridge ridge + updates hashcount + set newfacet, facet, matchfacet's hyperplane (removes from mergecycle of coplanarhorizon facets) + + see also: + qh_matchneighbor + + notes: + only called by qh_matchnewfacets for qh_buildcone and qh_triangulate_facet + assumes atfacet is simplicial + assumes atfacet->neighbors @ atskip == qh_DUPLICATEridge + usually keeps ridge with the widest merge + both MRGdupridge and MRGflipped are required merges -- rbox 100 C1,2e-13 D4 t1 | qhull d Qbb + can merge flipped f11842 skip 3 into f11862 skip 2 and vice versa (forced by goodmatch/goodmatch2) + blocks -- cannot merge f11862 skip 2 and f11863 skip2 (the widest merge) + must block -- can merge f11843 skip 3 into f11842 flipped skip 3, but not vice versa + can merge f11843 skip 3 into f11863 skip 2, but not vice versa + working/unused.h: [jan'19] Dropped qh_matchdupridge_coplanarhorizon, it was the same or slightly worse. Complex addition, rarely occurs + + 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 with the same orientation 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 with the same orientation and pass 1 + test if this is a better match + if pass 1, + make best match (it will not be merged) + set newfacet, facet, matchfacet's hyperplane (removes from mergecycle of coplanarhorizon facets) + +*/ +coordT qh_matchdupridge(qhT *qh, facetT *atfacet, int atskip, int hashsize, int *hashcount) { + boolT same, ismatch, isduplicate= False; + int hash, scan; + facetT *facet, *newfacet, *nextfacet; + facetT *maxmatch= NULL, *maxmatch2= NULL, *goodmatch= NULL, *goodmatch2= NULL; + int skip, newskip, nextskip= 0, makematch; + int maxskip= 0, maxskip2= 0, goodskip= 0, goodskip2= 0; + coordT maxdist= -REALmax, maxdist2= 0.0, dupdist, dupdist2, low, high, maxgood, gooddist= 0.0; + + maxgood= qh_WIDEdupridge * (qh->ONEmerge + qh->DISTround); + hash= qh_gethash(qh, hashsize, atfacet->vertices, qh->hull_dim, 1, + SETelem_(atfacet->vertices, atskip)); + trace2((qh, qh->ferr, 2046, "qh_matchdupridge: find dupridge matches for f%d skip %d hash %d hashcount %d\n", + atfacet->id, atskip, hash, *hashcount)); + for (makematch=0; makematch < 2; makematch++) { /* makematch is false on the first pass and 1 on the second */ + qh->visit_id++; + for (newfacet=atfacet, newskip=atskip; newfacet; newfacet= nextfacet, newskip= nextskip) { + zinc_(Zhashlookup); + nextfacet= NULL; /* exit when ismatch found */ + 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(qh, 1, newfacet->vertices, newskip, facet->vertices, &skip, &same)) { + if (SETelem_(newfacet->vertices, newskip) == SETelem_(facet->vertices, skip)) { + trace3((qh, qh->ferr, 3053, "qh_matchdupridge: duplicate ridge due to duplicate facets (f%d skip %d and f%d skip %d) previously reported as QH7084. Maximize dupdist to force vertex merge\n", + newfacet->id, newskip, facet->id, skip)); + isduplicate= True; + } + ismatch= (same == (boolT)(newfacet->toporient ^ facet->toporient)); + if (SETelemt_(facet->neighbors, skip, facetT) != qh_DUPLICATEridge) { + if (!makematch) { /* occurs if many merges, e.g., rbox 100 W0 C2,1e-13 D6 t1546872462 | qhull C0 Qt Tcv */ + qh_fprintf(qh, qh->ferr, 6155, "qhull topology error (qh_matchdupridge): missing qh_DUPLICATEridge at f%d skip %d for new f%d skip %d hash %d ismatch %d. Set by qh_matchneighbor\n", + facet->id, skip, newfacet->id, newskip, hash, ismatch); + qh_errexit2(qh, qh_ERRtopology, facet, newfacet); + } + }else if (!ismatch) { + nextfacet= facet; + nextskip= skip; + }else if (SETelemt_(newfacet->neighbors, newskip, facetT) == qh_DUPLICATEridge) { + if (makematch) { + if (newfacet->tricoplanar) { + SETelem_(facet->neighbors, skip)= newfacet; + SETelem_(newfacet->neighbors, newskip)= facet; + *hashcount -= 2; /* removed two unmatched facets */ + trace2((qh, qh->ferr, 2075, "qh_matchdupridge: allow tricoplanar dupridge for new f%d skip %d and f%d skip %d\n", + newfacet->id, newskip, facet->id, skip)); + }else if (goodmatch && goodmatch2) { + SETelem_(goodmatch2->neighbors, goodskip2)= qh_MERGEridge; /* undo selection of goodmatch */ + SETelem_(facet->neighbors, skip)= newfacet; + SETelem_(newfacet->neighbors, newskip)= facet; + *hashcount -= 2; /* removed two unmatched facets */ + trace2((qh, qh->ferr, 2105, "qh_matchdupridge: make good forced merge of dupridge f%d skip %d into f%d skip %d, keep new f%d skip %d and f%d skip %d, dist %4.4g\n", + goodmatch->id, goodskip, goodmatch2->id, goodskip2, newfacet->id, newskip, facet->id, skip, gooddist)); + goodmatch2= NULL; + }else { + SETelem_(facet->neighbors, skip)= newfacet; + SETelem_(newfacet->neighbors, newskip)= qh_MERGEridge; /* resolved by qh_mark_dupridges */ + *hashcount -= 2; /* removed two unmatched facets */ + trace3((qh, qh->ferr, 3073, "qh_matchdupridge: make forced merge of dupridge for new f%d skip %d and f%d skip %d, maxdist %4.4g in qh_forcedmerges\n", + newfacet->id, newskip, facet->id, skip, maxdist2)); + } + }else { /* !makematch */ + if (!facet->normal) + qh_setfacetplane(qh, facet); /* qh_mergecycle will ignore 'mergehorizon' facets with normals, too many cases otherwise */ + if (!newfacet->normal) + qh_setfacetplane(qh, newfacet); + dupdist= qh_getdistance(qh, facet, newfacet, &low, &high); /* ignore low/high */ + dupdist2= qh_getdistance(qh, newfacet, facet, &low, &high); + if (isduplicate) { + goodmatch= NULL; + minimize_(dupdist, dupdist2); + maxdist= dupdist; + maxdist2= REALmax/2; + maxmatch= facet; + maxskip= skip; + maxmatch2= newfacet; + maxskip2= newskip; + break; /* force maxmatch */ + }else if (facet->flipped && !newfacet->flipped && dupdist < maxgood) { + if (!goodmatch || !goodmatch->flipped || dupdist < gooddist) { + goodmatch= facet; + goodskip= skip; + goodmatch2= newfacet; + goodskip2= newskip; + gooddist= dupdist; + trace3((qh, qh->ferr, 3070, "qh_matchdupridge: try good dupridge flipped f%d skip %d into new f%d skip %d at dist %2.2g otherdist %2.2g\n", + goodmatch->id, goodskip, goodmatch2->id, goodskip2, gooddist, dupdist2)); + } + }else if (newfacet->flipped && !facet->flipped && dupdist2 < maxgood) { + if (!goodmatch || !goodmatch->flipped || dupdist2 < gooddist) { + goodmatch= newfacet; + goodskip= newskip; + goodmatch2= facet; + goodskip2= skip; + gooddist= dupdist2; + trace3((qh, qh->ferr, 3071, "qh_matchdupridge: try good dupridge flipped new f%d skip %d into f%d skip %d at dist %2.2g otherdist %2.2g\n", + goodmatch->id, goodskip, goodmatch2->id, goodskip2, gooddist, dupdist)); + } + }else if (dupdist < maxgood && (!newfacet->flipped || facet->flipped)) { /* disallow not-flipped->flipped */ + if (!goodmatch || (!goodmatch->flipped && dupdist < gooddist)) { + goodmatch= facet; + goodskip= skip; + goodmatch2= newfacet; + goodskip2= newskip; + gooddist= dupdist; + trace3((qh, qh->ferr, 3072, "qh_matchdupridge: try good dupridge f%d skip %d into new f%d skip %d at dist %2.2g otherdist %2.2g\n", + goodmatch->id, goodskip, goodmatch2->id, goodskip2, gooddist, dupdist2)); + } + }else if (dupdist2 < maxgood && (!facet->flipped || newfacet->flipped)) { /* disallow not-flipped->flipped */ + if (!goodmatch || (!goodmatch->flipped && dupdist2 < gooddist)) { + goodmatch= newfacet; + goodskip= newskip; + goodmatch2= facet; + goodskip2= skip; + gooddist= dupdist2; + trace3((qh, qh->ferr, 3018, "qh_matchdupridge: try good dupridge new f%d skip %d into f%d skip %d at dist %2.2g otherdist %2.2g\n", + goodmatch->id, goodskip, goodmatch2->id, goodskip2, gooddist, dupdist)); + } + }else if (!goodmatch) { /* otherwise match the furthest apart facets */ + if (!newfacet->flipped || facet->flipped) { + minimize_(dupdist, dupdist2); + } + if (dupdist > maxdist) { /* could keep !flipped->flipped, but probably lost anyway */ + maxdist2= maxdist; + maxdist= dupdist; + maxmatch= facet; + maxskip= skip; + maxmatch2= newfacet; + maxskip2= newskip; + trace3((qh, qh->ferr, 3055, "qh_matchdupridge: try furthest dupridge f%d skip %d new f%d skip %d at dist %2.2g\n", + maxmatch->id, maxskip, maxmatch2->id, maxskip2, maxdist)); + }else if (dupdist > maxdist2) + maxdist2= dupdist; + } + } + } + } + } /* end of foreach entry in qh.hash_table starting at 'hash' */ + if (makematch && SETelemt_(newfacet->neighbors, newskip, facetT) == qh_DUPLICATEridge) { + qh_fprintf(qh, qh->ferr, 6156, "qhull internal error (qh_matchdupridge): no MERGEridge match for dupridge new f%d skip %d at hash %d..%d\n", + newfacet->id, newskip, hash, scan); + qh_errexit(qh, qh_ERRqhull, newfacet, NULL); + } + } /* end of foreach newfacet at 'hash' */ + if (!makematch) { + if (!maxmatch && !goodmatch) { + qh_fprintf(qh, qh->ferr, 6157, "qhull internal error (qh_matchdupridge): no maximum or good match for dupridge new f%d skip %d at hash %d..%d\n", + atfacet->id, atskip, hash, scan); + qh_errexit(qh, qh_ERRqhull, atfacet, NULL); + } + if (goodmatch) { + SETelem_(goodmatch->neighbors, goodskip)= goodmatch2; + SETelem_(goodmatch2->neighbors, goodskip2)= goodmatch; + *hashcount -= 2; /* removed two unmatched facets */ + if (goodmatch->flipped) { + if (!goodmatch2->flipped) { + zzinc_(Zflipridge); + }else { + zzinc_(Zflipridge2); + /* qh_joggle_restart called by qh_matchneighbor if qh_DUPLICATEridge */ + } + } + /* previously traced */ + }else { + SETelem_(maxmatch->neighbors, maxskip)= maxmatch2; /* maxmatch!=NULL by QH6157 */ + SETelem_(maxmatch2->neighbors, maxskip2)= maxmatch; + *hashcount -= 2; /* removed two unmatched facets */ + zzinc_(Zmultiridge); + /* qh_joggle_restart called by qh_matchneighbor if qh_DUPLICATEridge */ + trace0((qh, qh->ferr, 25, "qh_matchdupridge: keep dupridge f%d skip %d and f%d skip %d, dist %4.4g\n", + maxmatch2->id, maxskip2, maxmatch->id, maxskip, maxdist)); + } + } + } + if (goodmatch) + return gooddist; + return maxdist2; +} /* matchdupridge */ + +#else /* qh_NOmerge */ +coordT qh_matchdupridge(qhT *qh, facetT *atfacet, int atskip, int hashsize, int *hashcount) { + QHULL_UNUSED(qh) + QHULL_UNUSED(atfacet) + QHULL_UNUSED(atskip) + QHULL_UNUSED(hashsize) + QHULL_UNUSED(hashcount) + + return 0.0; +} +#endif /* qh_NOmerge */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="nearcoplanar">-</a> + + qh_nearcoplanar() + for all facets, remove near-inside points from facet->coplanarset</li> + 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(qhT *qh /* qh.facet_list */) { + facetT *facet; + pointT *point, **pointp; + int numpart; + realT dist, innerplane; + + if (!qh->KEEPcoplanar && !qh->KEEPinside) { + FORALLfacets { + if (facet->coplanarset) + qh_setfree(qh, &facet->coplanarset); + } + }else if (!qh->KEEPcoplanar || !qh->KEEPinside) { + qh_outerinner(qh, 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(qh, point, facet, &dist); + if (dist < innerplane) { + if (!qh->KEEPinside) + SETref_(point)= NULL; + }else if (!qh->KEEPcoplanar) + SETref_(point)= NULL; + } + qh_setcompact(qh, facet->coplanarset); + } + } + zzadd_(Zcheckpart, numpart); + } +} /* nearcoplanar */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="nearvertex">-</a> + + qh_nearvertex(qh, 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(qhT *qh, 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, qh->ferr, 6158, "qhull internal error (qh_nearvertex): qh.VERTEXneighbors and facet->center required for tricoplanar facets\n"); + qh_errexit(qh, qh_ERRqhull, facet, NULL); + } + vertices= qh_settemp(qh, qh->TEMPsize); + apex= SETfirstt_(facet->vertices, vertexT); + center= facet->center; + FOREACHneighbor_(apex) { + if (neighbor->center == center) { + FOREACHvertex_(neighbor->vertices) + qh_setappend(qh, &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(qh, &vertices); + *bestdistp= sqrt(bestdist); + if (!bestvertex) { + qh_fprintf(qh, qh->ferr, 6261, "qhull internal error (qh_nearvertex): did not find bestvertex for f%d p%d\n", facet->id, qh_pointid(qh, point)); + qh_errexit(qh, qh_ERRqhull, facet, NULL); + } + trace3((qh, qh->ferr, 3019, "qh_nearvertex: v%d dist %2.2g for f%d p%d\n", + bestvertex->id, *bestdistp, facet->id, qh_pointid(qh, point))); /* bestvertex!=0 by QH2161 */ + return bestvertex; +} /* nearvertex */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="newhashtable">-</a> + + qh_newhashtable(qh, 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(qhT *qh, int newsize) { + int size; + + size= ((newsize+1)*qh_HASHfactor) | 0x1; /* odd number */ + while (True) { + if (newsize<0 || size<0) { + qh_fprintf(qh, qh->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(qh, 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(qh, size); + qh_setzero(qh, qh->hash_table, 0, size); + return size; +} /* newhashtable */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="newvertex">-</a> + + qh_newvertex(qh, point ) + returns a new vertex for point +*/ +vertexT *qh_newvertex(qhT *qh, pointT *point) { + vertexT *vertex; + + zinc_(Ztotvertices); + vertex= (vertexT *)qh_memalloc(qh, (int)sizeof(vertexT)); + memset((char *) vertex, (size_t)0, sizeof(vertexT)); + if (qh->vertex_id == UINT_MAX) { + qh_memfree(qh, vertex, (int)sizeof(vertexT)); + qh_fprintf(qh, qh->ferr, 6159, "qhull error: 2^32 or more vertices. vertexT.id field overflows. Vertices would not be sorted correctly.\n"); + qh_errexit(qh, qh_ERRother, NULL, NULL); + } + if (qh->vertex_id == qh->tracevertex_id) + qh->tracevertex= vertex; + vertex->id= qh->vertex_id++; + vertex->point= point; + trace4((qh, qh->ferr, 4060, "qh_newvertex: vertex p%d(v%d) created\n", qh_pointid(qh, vertex->point), + vertex->id)); + return(vertex); +} /* newvertex */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="nextfacet2d">-</a> + + qh_nextfacet2d( facet, &nextvertex ) + return next facet and vertex for a 2d facet in qh_ORIENTclock order + returns NULL on error + + notes: + in qh_ORIENTclock order (default counter-clockwise) + nextvertex is in between the two facets + does not use qhT or qh_errexit [QhullFacet.cpp] + + design: + see io_r.c/qh_printextremes_2d +*/ +facetT *qh_nextfacet2d(facetT *facet, vertexT **nextvertexp) { + facetT *nextfacet; + + if (facet->toporient ^ qh_ORIENTclock) { + *nextvertexp= SETfirstt_(facet->vertices, vertexT); + nextfacet= SETfirstt_(facet->neighbors, facetT); + }else { + *nextvertexp= SETsecondt_(facet->vertices, vertexT); + nextfacet= SETsecondt_(facet->neighbors, facetT); + } + return nextfacet; +} /* nextfacet2d */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="nextridge3d">-</a> + + 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 qhT. + + 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 qhT 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 */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="opposite_vertex">-</a> + + qh_opposite_vertex(qh, facetA, neighbor ) + return the opposite vertex in facetA to neighbor + +*/ +vertexT *qh_opposite_vertex(qhT *qh, facetT *facetA, facetT *neighbor) { + vertexT *opposite= NULL; + facetT *facet; + int facet_i, facet_n; + + if (facetA->simplicial) { + FOREACHfacet_i_(qh, facetA->neighbors) { + if (facet == neighbor) { + opposite= SETelemt_(facetA->vertices, facet_i, vertexT); + break; + } + } + } + if (!opposite) { + qh_fprintf(qh, qh->ferr, 6396, "qhull internal error (qh_opposite_vertex): opposite vertex in facet f%d to neighbor f%d is not defined. Either is facet is not simplicial or neighbor not found\n", + facetA->id, neighbor->id); + qh_errexit2(qh, qh_ERRqhull, facetA, neighbor); + } + return opposite; +} /* opposite_vertex */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="outcoplanar">-</a> + + 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(qhT *qh /* facet_list */) { + pointT *point, **pointp; + facetT *facet; + realT dist; + + trace1((qh, 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(qh, point, facet, &dist); + zinc_(Zpartition); + qh_partitioncoplanar(qh, point, facet, &dist, qh->findbestnew); + } + } + qh_setfree(qh, &facet->outsideset); + } +} /* outcoplanar */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="point">-</a> + + qh_point(qh, 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(qhT *qh, 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, qh->other_points)) + return SETelemt_(qh->other_points, id, pointT); + return NULL; +} /* point */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="point_add">-</a> + + qh_point_add(qh, 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(qhT *qh, setT *set, pointT *point, void *elem) { + int id, size; + + SETreturnsize_(set, size); + if ((id= qh_pointid(qh, point)) < 0) + qh_fprintf(qh, qh->ferr, 7067, "qhull internal warning (point_add): unknown point %p id %d\n", + point, id); + else if (id >= size) { + qh_fprintf(qh, qh->ferr, 6160, "qhull internal error (point_add): point p%d is out of bounds(%d)\n", + id, size); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + }else + SETelem_(set, id)= elem; +} /* point_add */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="pointfacet">-</a> + + qh_pointfacet() + return temporary set of facet for each point + the set is indexed by point id + at most one facet per point, arbitrary selection + + notes: + each point is assigned to at most one of vertices, coplanarset, or outsideset + unassigned points are interior points or + vertices assigned to one of its 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_(qh, facets) { ... } + SETelem_(facets, i) + + design: + for each facet + add each vertex + add each coplanar point + add each outside point +*/ +setT *qh_pointfacet(qhT *qh /* qh.facet_list */) { + int numpoints= qh->num_points + qh_setsize(qh, qh->other_points); + setT *facets; + facetT *facet; + vertexT *vertex, **vertexp; + pointT *point, **pointp; + + facets= qh_settemp(qh, numpoints); + qh_setzero(qh, facets, 0, numpoints); + qh->vertex_visit++; + FORALLfacets { + FOREACHvertex_(facet->vertices) { + if (vertex->visitid != qh->vertex_visit) { + vertex->visitid= qh->vertex_visit; + qh_point_add(qh, facets, vertex->point, facet); + } + } + FOREACHpoint_(facet->coplanarset) + qh_point_add(qh, facets, point, facet); + FOREACHpoint_(facet->outsideset) + qh_point_add(qh, facets, point, facet); + } + return facets; +} /* pointfacet */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="pointvertex">-</a> + + qh_pointvertex(qh ) + 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_(qh, vertices) { ... } + SETelem_(vertices, i) +*/ +setT *qh_pointvertex(qhT *qh /* qh.facet_list */) { + int numpoints= qh->num_points + qh_setsize(qh, qh->other_points); + setT *vertices; + vertexT *vertex; + + vertices= qh_settemp(qh, numpoints); + qh_setzero(qh, vertices, 0, numpoints); + FORALLvertices + qh_point_add(qh, vertices, vertex->point, vertex); + return vertices; +} /* pointvertex */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="prependfacet">-</a> + + qh_prependfacet(qh, 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(qhT *qh, facetT *facet, facetT **facetlist) { + facetT *prevfacet, *list; + + trace4((qh, 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 */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="printhashtable">-</a> + + qh_printhashtable(qh, fp ) + print hash table to fp + + notes: + not in I/O to avoid bringing io_r.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(qhT *qh, FILE *fp) { + facetT *facet, *neighbor; + int id, facet_i, facet_n, neighbor_i= 0, neighbor_n= 0; + vertexT *vertex, **vertexp; + + FOREACHfacet_i_(qh, qh->hash_table) { + if (facet) { + FOREACHneighbor_i_(qh, facet) { + if (!neighbor || neighbor == qh_MERGEridge || neighbor == qh_DUPLICATEridge) + break; + } + if (neighbor_i == neighbor_n) + continue; + qh_fprintf(qh, fp, 9283, "hash %d f%d ", facet_i, facet->id); + FOREACHvertex_(facet->vertices) + qh_fprintf(qh, fp, 9284, "v%d ", vertex->id); + qh_fprintf(qh, fp, 9285, "\n neighbors:"); + FOREACHneighbor_i_(qh, facet) { + if (neighbor == qh_MERGEridge) + id= -3; + else if (neighbor == qh_DUPLICATEridge) + id= -2; + else + id= getid_(neighbor); + qh_fprintf(qh, fp, 9286, " %d", id); + } + qh_fprintf(qh, fp, 9287, "\n"); + } + } +} /* printhashtable */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="printlists">-</a> + + qh_printlists(qh) + print out facet and vertex lists for debugging (without 'f/v' tags) + + notes: + not in I/O to avoid bringing io_r.c in +*/ +void qh_printlists(qhT *qh) { + facetT *facet; + vertexT *vertex; + int count= 0; + + qh_fprintf(qh, qh->ferr, 3062, "qh_printlists: max_outside %2.2g all facets:", qh->max_outside); + FORALLfacets{ + if (++count % 100 == 0) + qh_fprintf(qh, qh->ferr, 8109, "\n "); + qh_fprintf(qh, qh->ferr, 8110, " %d", facet->id); + } + qh_fprintf(qh, qh->ferr, 8111, "\n qh.visible_list f%d, newfacet_list f%d, facet_next f%d for qh_addpoint\n qh.newvertex_list v%d all vertices:", + getid_(qh->visible_list), getid_(qh->newfacet_list), getid_(qh->facet_next), getid_(qh->newvertex_list)); + count= 0; + FORALLvertices{ + if (++count % 100 == 0) + qh_fprintf(qh, qh->ferr, 8112, "\n "); + qh_fprintf(qh, qh->ferr, 8113, " %d", vertex->id); + } + qh_fprintf(qh, qh->ferr, 8114, "\n"); +} /* printlists */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="addfacetvertex">-</a> + + qh_replacefacetvertex(qh, facet, oldvertex, newvertex ) + replace oldvertex with newvertex in f.vertices + vertices are inverse sorted by vertex->id + + returns: + toporient is flipped if an odd parity, position change + + notes: + for simplicial facets in qh_rename_adjacentvertex + see qh_addfacetvertex +*/ +void qh_replacefacetvertex(qhT *qh, facetT *facet, vertexT *oldvertex, vertexT *newvertex) { + vertexT *vertex; + facetT *neighbor; + int vertex_i, vertex_n= 0; + int old_i= -1, new_i= -1; + + trace3((qh, qh->ferr, 3038, "qh_replacefacetvertex: replace v%d with v%d in f%d\n", oldvertex->id, newvertex->id, facet->id)); + if (!facet->simplicial) { + qh_fprintf(qh, qh->ferr, 6283, "qhull internal error (qh_replacefacetvertex): f%d is not simplicial\n", facet->id); + qh_errexit(qh, qh_ERRqhull, facet, NULL); + } + FOREACHvertex_i_(qh, facet->vertices) { + if (new_i == -1 && vertex->id < newvertex->id) { + new_i= vertex_i; + }else if (vertex->id == newvertex->id) { + qh_fprintf(qh, qh->ferr, 6281, "qhull internal error (qh_replacefacetvertex): f%d already contains new v%d\n", facet->id, newvertex->id); + qh_errexit(qh, qh_ERRqhull, facet, NULL); + } + if (vertex->id == oldvertex->id) { + old_i= vertex_i; + } + } + if (old_i == -1) { + qh_fprintf(qh, qh->ferr, 6282, "qhull internal error (qh_replacefacetvertex): f%d does not contain old v%d\n", facet->id, oldvertex->id); + qh_errexit(qh, qh_ERRqhull, facet, NULL); + } + if (new_i == -1) { + new_i= vertex_n; + } + if (old_i < new_i) + new_i--; + if ((old_i & 0x1) != (new_i & 0x1)) + facet->toporient ^= 1; + qh_setdelnthsorted(qh, facet->vertices, old_i); + qh_setaddnth(qh, &facet->vertices, new_i, newvertex); + neighbor= SETelemt_(facet->neighbors, old_i, facetT); + qh_setdelnthsorted(qh, facet->neighbors, old_i); + qh_setaddnth(qh, &facet->neighbors, new_i, neighbor); +} /* replacefacetvertex */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="resetlists">-</a> + + qh_resetlists(qh, stats, qh_RESETvisible ) + reset newvertex_list, newfacet_list, visible_list, NEWfacets, NEWtentative + if stats, + maintains statistics + if resetVisible, + visible_list is restored to facet_list + otherwise, f.visible/f.replace is retained + + returns: + newvertex_list, newfacet_list, visible_list are NULL + + notes: + To delete visible facets, call qh_deletevisible before qh_resetlists +*/ +void qh_resetlists(qhT *qh, boolT stats, boolT resetVisible /* qh.newvertex_list newfacet_list visible_list */) { + vertexT *vertex; + facetT *newfacet, *visible; + int totnew=0, totver=0; + + trace2((qh, qh->ferr, 2066, "qh_resetlists: reset newvertex_list v%d, newfacet_list f%d, visible_list f%d, facet_list f%d next f%d vertex_list v%d -- NEWfacets? %d, NEWtentative? %d, stats? %d\n", + getid_(qh->newvertex_list), getid_(qh->newfacet_list), getid_(qh->visible_list), getid_(qh->facet_list), getid_(qh->facet_next), getid_(qh->vertex_list), qh->NEWfacets, qh->NEWtentative, stats)); + 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->newfacet= False; + qh->newvertex_list= NULL; + qh->first_newfacet= 0; + FORALLnew_facets { + newfacet->newfacet= False; + newfacet->dupridge= False; + } + qh->newfacet_list= NULL; + if (resetVisible) { + FORALLvisible_facets { + visible->f.replace= NULL; + visible->visible= False; + } + qh->num_visible= 0; + } + qh->visible_list= NULL; + qh->NEWfacets= False; + qh->NEWtentative= False; +} /* resetlists */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="setvoronoi_all">-</a> + + qh_setvoronoi_all(qh) + compute Voronoi centers for all facets + includes upperDelaunay facets if qh.UPPERdelaunay ('Qu') + + returns: + facet->center is the Voronoi center + + notes: + 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(qhT *qh) { + facetT *facet; + + qh_clearcenters(qh, qh_ASvoronoi); + qh_vertexneighbors(qh); + + FORALLfacets { + if (!facet->normal || !facet->upperdelaunay || qh->UPPERdelaunay) { + if (!facet->center) + facet->center= qh_facetcenter(qh, facet->vertices); + } + } +} /* setvoronoi_all */ + +#ifndef qh_NOmerge +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="triangulate">-</a> + + 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. + resets qh.newfacet_list and visible_list + + notes: + called by qh_prepare_output and user_eg2_r.c + call after qh_check_output since may switch to Voronoi centers, and qh_checkconvex skips f.tricoplanar facets + Output may overwrite ->f.triowner with ->f.area + while running, 'triangulated_facet_list' is a list of + one non-simplicial facet followed by its 'f.tricoplanar' triangulated facets + See qh_buildcone +*/ +void qh_triangulate(qhT *qh /* qh.facet_list */) { + facetT *facet, *nextfacet, *owner; + facetT *neighbor, *visible= NULL, *facet1, *facet2, *triangulated_facet_list= NULL; + facetT *orig_neighbor= NULL, *otherfacet; + vertexT *triangulated_vertex_list= NULL; + mergeT *merge; + mergeType mergetype; + int neighbor_i, neighbor_n; + boolT onlygood= qh->ONLYgood; + + if (qh->hasTriangulation) + return; + trace1((qh, 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, qh_ASvoronoi); + qh_vertexneighbors(qh); + } + qh->ONLYgood= False; /* for makenew_nonsimplicial */ + qh->visit_id++; + qh_initmergesets(qh /* qh.facet_mergeset,degen_mergeset,vertex_mergeset */); + 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 (!triangulated_facet_list) + triangulated_facet_list= facet; /* will be first triangulated facet */ + qh_triangulate_facet(qh, facet, &triangulated_vertex_list); /* qh_resetlists ! */ + } + /* qh_checkpolygon invalid due to f.visible without qh.visible_list */ + trace2((qh, qh->ferr, 2047, "qh_triangulate: delete null facets from facetlist f%d. A null facet has the same first (apex) and second vertices\n", getid_(triangulated_facet_list))); + for (facet=triangulated_facet_list; facet && facet->next; facet= nextfacet) { + nextfacet= facet->next; + if (facet->visible) + continue; + if (facet->ridges) { + if (qh_setsize(qh, facet->ridges) > 0) { + qh_fprintf(qh, qh->ferr, 6161, "qhull internal error (qh_triangulate): ridges still defined for f%d\n", facet->id); + qh_errexit(qh, qh_ERRqhull, facet, NULL); + } + qh_setfree(qh, &facet->ridges); + } + if (SETfirst_(facet->vertices) == SETsecond_(facet->vertices)) { + zinc_(Ztrinull); + qh_triangulate_null(qh, facet); /* will delete facet */ + } + } + trace2((qh, qh->ferr, 2048, "qh_triangulate: delete %d or more mirrored facets. Mirrored facets have the same vertices due to a null facet\n", qh_setsize(qh, 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->mergetype; + qh_memfree(qh, merge, (int)sizeof(mergeT)); + if (mergetype == MRGmirror) { + zinc_(Ztrimirror); + qh_triangulate_mirror(qh, facet1, facet2); /* will delete both facets */ + } + } + qh_freemergesets(qh); + trace2((qh, qh->ferr, 2049, "qh_triangulate: update neighbor lists for vertices from v%d\n", getid_(triangulated_vertex_list))); + qh->newvertex_list= triangulated_vertex_list; /* all vertices of triangulated facets */ + qh->visible_list= NULL; + qh_update_vertexneighbors(qh /* qh.newvertex_list, empty newfacet_list and visible_list */); + qh_resetlists(qh, False, !qh_RESETvisible /* qh.newvertex_list, empty newfacet_list and visible_list */); + + trace2((qh, qh->ferr, 2050, "qh_triangulate: identify degenerate tricoplanar facets from f%d\n", getid_(triangulated_facet_list))); + trace2((qh, qh->ferr, 2051, "qh_triangulate: and replace facet->f.triowner with tricoplanar facets that own center, normal, etc.\n")); + FORALLfacet_(triangulated_facet_list) { + if (facet->tricoplanar && !facet->visible) { + FOREACHneighbor_i_(qh, 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; + } + } + } + } + } + if (qh->IStracing >= 4) + qh_printlists(qh); + trace2((qh, qh->ferr, 2052, "qh_triangulate: delete visible facets -- non-simplicial, null, and mirrored facets\n")); + owner= NULL; + visible= NULL; + for (facet=triangulated_facet_list; facet && facet->next; facet= nextfacet) { + /* deleting facets, triangulated_facet_list is no longer valid */ + nextfacet= facet->next; + if (facet->visible) { + if (facet->tricoplanar) { /* a null or mirrored facet */ + qh_delfacet(qh, 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, qh->ferr, 2053, "qh_triangulate: delete f%d. All tricoplanar facets degenerate for non-simplicial facet\n", + visible->id)); + qh_delfacet(qh, visible); + qh->num_visible--; + } + visible= facet; + owner= NULL; + } + }else if (facet->tricoplanar) { + if (facet->f.triowner != visible || visible==NULL) { + qh_fprintf(qh, qh->ferr, 6162, "qhull internal 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, qh_ERRqhull, facet, visible); + } + if (owner) + facet->f.triowner= owner; + else if (!facet->degenerate) { + owner= facet; + nextfacet= visible->next; /* rescan tricoplanar facets with owner, visible!=0 by QH6162 */ + 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(qh, visible); + qh->num_visible--; + } + } + facet->degenerate= False; /* reset f.degenerate set by qh_triangulate*/ + } + if (visible && !owner) { + trace2((qh, qh->ferr, 2054, "qh_triangulate: all tricoplanar facets degenerate for last non-simplicial facet f%d\n", + visible->id)); + qh_delfacet(qh, visible); + qh->num_visible--; + } + qh->ONLYgood= onlygood; /* restore value */ + if (qh->CHECKfrequently) + qh_checkpolygon(qh, qh->facet_list); + qh->hasTriangulation= True; +} /* triangulate */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="triangulate_facet">-</a> + + qh_triangulate_facet(qh, facetA, &firstVertex ) + 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: + only called by qh_triangulate + qh_makenew_nonsimplicial uses neighbor->seen for the same + if qh.TRInormals, newfacet->normal will need qh_free + if qh.TRInormals and qh_AScentrum, newfacet->center will need qh_free + keepcentrum is also set on Zwidefacet in qh_mergefacet + freed by qh_clearcenters + + 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(qhT *qh, facetT *facetA, vertexT **first_vertex) { + facetT *newfacet; + facetT *neighbor, **neighborp; + vertexT *apex; + int numnew=0; + + trace3((qh, qh->ferr, 3020, "qh_triangulate_facet: triangulate facet f%d\n", facetA->id)); + + qh->first_newfacet= qh->facet_id; + if (qh->IStracing >= 4) + qh_printfacet(qh, qh->ferr, facetA); + FOREACHneighbor_(facetA) { + neighbor->seen= False; + neighbor->coplanarhorizon= 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(qh, facetA->vertices); + } + qh->visible_list= qh->newfacet_list= qh->facet_tail; + facetA->visitid= qh->visit_id; + apex= SETfirstt_(facetA->vertices, vertexT); + qh_makenew_nonsimplicial(qh, facetA, apex, &numnew); + qh_willdelete(qh, facetA, NULL); + FORALLnew_facets { + newfacet->tricoplanar= True; + newfacet->f.trivisible= facetA; + newfacet->degenerate= False; + newfacet->upperdelaunay= facetA->upperdelaunay; + newfacet->good= facetA->good; + if (qh->TRInormals) { /* 'Q11' triangulate duplicates ->normal and ->center */ + newfacet->keepcentrum= True; + if(facetA->normal){ + newfacet->normal= (double *)qh_memalloc(qh, qh->normal_size); + memcpy((char *)newfacet->normal, facetA->normal, (size_t)qh->normal_size); + } + if (qh->CENTERtype == qh_AScentrum) + newfacet->center= qh_getcentrum(qh, newfacet); + else if (qh->CENTERtype == qh_ASvoronoi && facetA->center){ + newfacet->center= (double *)qh_memalloc(qh, qh->center_size); + memcpy((char *)newfacet->center, facetA->center, (size_t)qh->center_size); + } + }else { + newfacet->keepcentrum= False; + /* one facet will have keepcentrum=True at end of qh_triangulate */ + newfacet->normal= facetA->normal; + newfacet->center= facetA->center; + } + newfacet->offset= facetA->offset; +#if qh_MAXoutside + newfacet->maxoutside= facetA->maxoutside; +#endif + } + qh_matchnewfacets(qh /* qh.newfacet_list */); /* ignore returned value, maxdupdist */ + zinc_(Ztricoplanar); + zadd_(Ztricoplanartot, numnew); + zmax_(Ztricoplanarmax, numnew); + if (!(*first_vertex)) + (*first_vertex)= qh->newvertex_list; + qh->newvertex_list= NULL; + qh->visible_list= NULL; + /* only update v.neighbors for qh.newfacet_list. qh.visible_list and qh.newvertex_list are NULL */ + qh_update_vertexneighbors(qh /* qh.newfacet_list */); + qh_resetlists(qh, False, !qh_RESETvisible /* qh.newfacet_list */); +} /* triangulate_facet */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="triangulate_link">-</a> + + qh_triangulate_link(qh, oldfacetA, facetA, oldfacetB, facetB) + relink facetA to facetB via null oldfacetA or mirrored oldfacetA and oldfacetB + returns: + if neighbors are already linked, will merge as MRGmirror (qh.degen_mergeset, 4-d and up) +*/ +void qh_triangulate_link(qhT *qh, facetT *oldfacetA, facetT *facetA, facetT *oldfacetB, facetT *facetB) { + int errmirror= False; + + if (oldfacetA == oldfacetB) { + trace3((qh, qh->ferr, 3052, "qh_triangulate_link: relink neighbors f%d and f%d of null facet f%d\n", + facetA->id, facetB->id, oldfacetA->id)); + }else { + trace3((qh, qh->ferr, 3021, "qh_triangulate_link: relink neighbors f%d and f%d of mirrored facets f%d and f%d\n", + facetA->id, facetB->id, oldfacetA->id, oldfacetB->id)); + } + if (qh_setin(facetA->neighbors, facetB)) { + if (!qh_setin(facetB->neighbors, facetA)) + errmirror= True; + else if (!facetA->redundant || !facetB->redundant || !qh_hasmerge(qh->degen_mergeset, MRGmirror, facetA, facetB)) + qh_appendmergeset(qh, facetA, facetB, MRGmirror, 0.0, 1.0); + }else if (qh_setin(facetB->neighbors, facetA)) + errmirror= True; + if (errmirror) { + qh_fprintf(qh, qh->ferr, 6163, "qhull internal error (qh_triangulate_link): neighbors f%d and f%d do not match for null facet or mirrored facets f%d and f%d\n", + facetA->id, facetB->id, oldfacetA->id, oldfacetB->id); + qh_errexit2(qh, qh_ERRqhull, facetA, facetB); + } + qh_setreplace(qh, facetB->neighbors, oldfacetB, facetA); + qh_setreplace(qh, facetA->neighbors, oldfacetA, facetB); +} /* triangulate_link */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="triangulate_mirror">-</a> + + qh_triangulate_mirror(qh, facetA, facetB) + delete two mirrored facets identified by qh_triangulate_null() and itself + 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(qhT *qh, facetT *facetA, facetT *facetB) { + facetT *neighbor, *neighborB; + int neighbor_i, neighbor_n; + + trace3((qh, qh->ferr, 3022, "qh_triangulate_mirror: delete mirrored facets f%d and f%d and link their neighbors\n", + facetA->id, facetB->id)); + FOREACHneighbor_i_(qh, facetA) { + neighborB= SETelemt_(facetB->neighbors, neighbor_i, facetT); + if (neighbor == facetB && neighborB == facetA) + continue; /* occurs twice */ + else if (neighbor->redundant && neighborB->redundant) { /* also mirrored facets (D5+) */ + if (qh_hasmerge(qh->degen_mergeset, MRGmirror, neighbor, neighborB)) + continue; + } + if (neighbor->visible && neighborB->visible) /* previously deleted as mirrored facets */ + continue; + qh_triangulate_link(qh, facetA, neighbor, facetB, neighborB); + } + qh_willdelete(qh, facetA, NULL); + qh_willdelete(qh, facetB, NULL); +} /* triangulate_mirror */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="triangulate_null">-</a> + + qh_triangulate_null(qh, 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_update_vertexneighbors + 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 will be merged (MRGmirror) +*/ +void qh_triangulate_null(qhT *qh, facetT *facetA) { + facetT *neighbor, *otherfacet; + + trace3((qh, 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(qh, facetA, neighbor, facetA, otherfacet); + qh_willdelete(qh, facetA, NULL); +} /* triangulate_null */ + +#else /* qh_NOmerge */ +void qh_triangulate(qhT *qh) { + QHULL_UNUSED(qh) +} +#endif /* qh_NOmerge */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="vertexintersect">-</a> + + qh_vertexintersect(qh, verticesA, verticesB ) + intersects two vertex sets (inverse id ordered) + vertexsetA is a temporary set at the top of qh->qhmem.tempstack + + returns: + replaces vertexsetA with the intersection + + notes: + only called by qh_neighbor_intersections + if !qh.QHULLfinished, non-simplicial facets may have f.vertices with extraneous vertices + cleaned by qh_remove_extravertices in qh_reduce_vertices + could optimize by overwriting vertexsetA +*/ +void qh_vertexintersect(qhT *qh, setT **vertexsetA, setT *vertexsetB) { + setT *intersection; + + intersection= qh_vertexintersect_new(qh, *vertexsetA, vertexsetB); + qh_settempfree(qh, vertexsetA); + *vertexsetA= intersection; + qh_settemppush(qh, intersection); +} /* vertexintersect */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="vertexintersect_new">-</a> + + qh_vertexintersect_new(qh, verticesA, verticesB ) + intersects two vertex sets (inverse id ordered) + + returns: + a new set + + notes: + called by qh_checkfacet, qh_vertexintersect, qh_rename_sharedvertex, qh_findbest_pinchedvertex, qh_neighbor_intersections + if !qh.QHULLfinished, non-simplicial facets may have f.vertices with extraneous vertices + cleaned by qh_remove_extravertices in qh_reduce_vertices +*/ +setT *qh_vertexintersect_new(qhT *qh, setT *vertexsetA, setT *vertexsetB) { + setT *intersection= qh_setnew(qh, qh->hull_dim - 1); + vertexT **vertexA= SETaddr_(vertexsetA, vertexT); + vertexT **vertexB= SETaddr_(vertexsetB, vertexT); + + while (*vertexA && *vertexB) { + if (*vertexA == *vertexB) { + qh_setappend(qh, &intersection, *vertexA); + vertexA++; vertexB++; + }else { + if ((*vertexA)->id > (*vertexB)->id) + vertexA++; + else + vertexB++; + } + } + return intersection; +} /* vertexintersect_new */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="vertexneighbors">-</a> + + qh_vertexneighbors(qh) + 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(qhT *qh /* qh.facet_list */) { + facetT *facet; + vertexT *vertex, **vertexp; + + if (qh->VERTEXneighbors) + return; + trace1((qh, qh->ferr, 1035, "qh_vertexneighbors: determining 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, qh->hull_dim); + } + qh_setappend(qh, &vertex->neighbors, facet); + } + } + qh->VERTEXneighbors= True; +} /* vertexneighbors */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="vertexsubset">-</a> + + 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/contrib/libs/qhull/libqhull_r/poly_r.c b/contrib/libs/qhull/libqhull_r/poly_r.c new file mode 100644 index 0000000000..d6a5e7a3d8 --- /dev/null +++ b/contrib/libs/qhull/libqhull_r/poly_r.c @@ -0,0 +1,1448 @@ +/*<html><pre> -<a href="qh-poly_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + poly_r.c + implements polygons and simplices + + see qh-poly_r.htm, poly_r.h and libqhull_r.h + + infrequent code is in poly2_r.c + (all but top 50 and their callers 12/3/95) + + Copyright (c) 1993-2020 The Geometry Center. + $Id: //main/2019/qhull/src/libqhull_r/poly_r.c#8 $$Change: 2953 $ + $DateTime: 2020/05/21 22:05:32 $$Author: bbarber $ +*/ + +#include "qhull_ra.h" + +/*======== functions in alphabetical order ==========*/ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="appendfacet">-</a> + + qh_appendfacet(qh, 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(qhT *qh, facetT *facet) { + facetT *tail= qh->facet_tail; + + if (tail == qh->newfacet_list) { + qh->newfacet_list= facet; + if (tail == qh->visible_list) /* visible_list is at or before newfacet_list */ + qh->visible_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, qh->ferr, 4044, "qh_appendfacet: append f%d to facet_list\n", facet->id)); +} /* appendfacet */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="appendvertex">-</a> + + qh_appendvertex(qh, vertex ) + appends vertex to end of qh.vertex_list, + + returns: + sets vertex->newfacet + updates qh.vertex_list, newvertex_list + increments qh.num_vertices + + notes: + assumes qh.vertex_list/vertex_tail is defined (createsimplex) + +*/ +void qh_appendvertex(qhT *qh, vertexT *vertex) { + vertexT *tail= qh->vertex_tail; + + if (tail == qh->newvertex_list) + qh->newvertex_list= vertex; + vertex->newfacet= 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, qh->ferr, 4045, "qh_appendvertex: append v%d to qh.newvertex_list and set v.newfacet\n", vertex->id)); +} /* appendvertex */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="attachnewfacets">-</a> + + qh_attachnewfacets(qh) + attach horizon facets to new facets in qh.newfacet_list + newfacets have neighbor and ridge links to horizon but not vice versa + + returns: + clears qh.NEWtentative + 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) + + notes: + used for qh.NEWtentative, otherwise see qh_makenew_nonsimplicial and qh_makenew_simplicial + qh_delridge_merge not needed (as tested by qh_checkdelridge) + + 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 is 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(qhT *qh /* qh.visible_list, qh.newfacet_list */) { + facetT *newfacet= NULL, *neighbor, **neighborp, *horizon, *visible; + ridgeT *ridge, **ridgep; + + trace3((qh, qh->ferr, 3012, "qh_attachnewfacets: delete interior ridges\n")); + if (qh->CHECKfrequently) { + qh_checkdelridge(qh); + } + 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_delridge(qh, ridge); /* delete on second visit */ + } + } + } + } + trace1((qh, 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(qh, horizon->neighbors, visible, newfacet); + }else { + qh_fprintf(qh, qh->ferr, 6102, "qhull internal error (qh_attachnewfacets): could not find visible facet for horizon f%d of newfacet f%d\n", + horizon->id, newfacet->id); + qh_errexit2(qh, 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(qh, horizon->neighbors, SETindex_(horizon->neighbors, neighbor)); + neighborp--; /* repeat */ + } + } + qh_setappend(qh, &horizon->neighbors, newfacet); + ridge= SETfirstt_(newfacet->ridges, ridgeT); + if (ridge->top == horizon) { + ridge->bottom= newfacet; + ridge->simplicialbot= True; + }else { + ridge->top= newfacet; + ridge->simplicialtop= True; + } + } + } /* newfacets */ + trace4((qh, qh->ferr, 4094, "qh_attachnewfacets: clear f.ridges and f.neighbors for visible facets, may become invalid before qh_deletevisible\n")); + FORALLvisible_facets { + if (visible->ridges) + SETfirst_(visible->ridges)= NULL; + SETfirst_(visible->neighbors)= NULL; + } + qh->NEWtentative= False; + qh->NEWfacets= True; + if (qh->PRINTstatistics) { + FORALLvisible_facets { + if (!visible->f.replace) + zinc_(Zinsidevisible); + } + } +} /* attachnewfacets */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="checkflipped">-</a> + + qh_checkflipped(qh, facet, dist, allerror ) + checks facet orientation to interior point + + if allerror set, + tests against -qh.DISTround + else + tests against 0.0 since tested against -qh.DISTround before + + returns: + False if it flipped orientation (sets facet->flipped) + distance if non-NULL + + notes: + called by qh_setfacetplane, qh_initialhull, and qh_checkflipped_all +*/ +boolT qh_checkflipped(qhT *qh, facetT *facet, realT *distp, boolT allerror) { + realT dist; + + if (facet->flipped && !distp) + return False; + zzinc_(Zdistcheck); + qh_distplane(qh, qh->interior_point, facet, &dist); + if (distp) + *distp= dist; + if ((allerror && dist >= -qh->DISTround) || (!allerror && dist > 0.0)) { + facet->flipped= True; + trace0((qh, qh->ferr, 19, "qh_checkflipped: facet f%d flipped, allerror? %d, distance= %6.12g during p%d\n", + facet->id, allerror, dist, qh->furthest_id)); + if (qh->num_facets > qh->hull_dim+1) { /* qh_initialhull reverses orientation if !qh_checkflipped */ + zzinc_(Zflippedfacets); + qh_joggle_restart(qh, "flipped facet"); + } + return False; + } + return True; +} /* checkflipped */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="delfacet">-</a> + + qh_delfacet(qh, facet ) + removes facet from facet_list and frees up its memory + + notes: + assumes vertices and ridges already freed or referenced elsewhere +*/ +void qh_delfacet(qhT *qh, facetT *facet) { + void **freelistp; /* used if !qh_NOmem by qh_memfree_() */ + + trace3((qh, qh->ferr, 3057, "qh_delfacet: delete f%d\n", facet->id)); + if (qh->CHECKfrequently || qh->VERIFYoutput) { + if (!qh->NOerrexit) { + qh_checkdelfacet(qh, facet, qh->facet_mergeset); + qh_checkdelfacet(qh, facet, qh->degen_mergeset); + qh_checkdelfacet(qh, facet, qh->vertex_mergeset); + } + } + if (facet == qh->tracefacet) + qh->tracefacet= NULL; + if (facet == qh->GOODclosest) + qh->GOODclosest= NULL; + qh_removefacet(qh, facet); + if (!facet->tricoplanar || facet->keepcentrum) { + qh_memfree_(qh, facet->normal, qh->normal_size, freelistp); + if (qh->CENTERtype == qh_ASvoronoi) { /* braces for macro calls */ + qh_memfree_(qh, facet->center, qh->center_size, freelistp); + }else /* AScentrum */ { + qh_memfree_(qh, facet->center, qh->normal_size, freelistp); + } + } + qh_setfree(qh, &(facet->neighbors)); + if (facet->ridges) + qh_setfree(qh, &(facet->ridges)); + qh_setfree(qh, &(facet->vertices)); + if (facet->outsideset) + qh_setfree(qh, &(facet->outsideset)); + if (facet->coplanarset) + qh_setfree(qh, &(facet->coplanarset)); + qh_memfree_(qh, facet, (int)sizeof(facetT), freelistp); +} /* delfacet */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="deletevisible">-</a> + + qh_deletevisible() + delete visible facets and vertices + + returns: + deletes each facet and removes from facetlist + deletes vertices on qh.del_vertices and ridges in qh.del_ridges + at exit, qh.visible_list empty (== qh.newfacet_list) + + notes: + called by qh_all_vertexmerges, qh_addpoint, and qh_qhull + ridges already deleted or moved elsewhere + deleted vertices on qh.del_vertices + horizon facets do not reference facets on qh.visible_list + new facets in qh.newfacet_list + uses qh.visit_id; +*/ +void qh_deletevisible(qhT *qh /* qh.visible_list */) { + facetT *visible, *nextfacet; + vertexT *vertex, **vertexp; + int numvisible= 0, numdel= qh_setsize(qh, qh->del_vertices); + + trace1((qh, 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(qh, visible); /* f.ridges deleted or moved elsewhere, deleted f.vertices on qh.del_vertices */ + } + if (numvisible != qh->num_visible) { + qh_fprintf(qh, 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, 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(qh, vertex); + qh_settruncate(qh, qh->del_vertices, 0); +} /* deletevisible */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="facetintersect">-</a> + + qh_facetintersect(qh, 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(qhT *qh, 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, qh->ferr, 6104, "qhull internal error (qh_facetintersect): f%d or f%d not in other's neighbors\n", + facetA->id, facetB->id); + qh_errexit2(qh, qh_ERRqhull, facetA, facetB); + } + intersect= qh_setnew_delnthsorted(qh, facetA->vertices, qh->hull_dim, *skipA, prepend); + trace4((qh, qh->ferr, 4047, "qh_facetintersect: f%d skip %d matches f%d skip %d\n", + facetA->id, *skipA, facetB->id, *skipB)); + return(intersect); +} /* facetintersect */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="gethash">-</a> + + qh_gethash(qh, 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(qhT *qh, int hashsize, setT *set, int size, int firstindex, void *skipelem) { + void **elemp= SETelemaddr_(set, firstindex, void); + ptr_intT hash= 0, elem; + unsigned int uresult; + 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, qh->ferr, 6202, "qhull internal error: negative hashsize %d passed to qh_gethash [poly_r.c]\n", hashsize); + qh_errexit2(qh, qh_ERRqhull, NULL, NULL); + } + uresult= (unsigned int)hash; + uresult %= (unsigned int)hashsize; + /* result= 0; for debugging */ + return (int)uresult; +#ifdef _MSC_VER +#pragma warning( pop) +#endif +} /* gethash */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="getreplacement">-</a> + + qh_getreplacement(qh, visible ) + get replacement for visible facet + + returns: + valid facet from visible.replace (may be chained) +*/ +facetT *qh_getreplacement(qhT *qh, facetT *visible) { + unsigned int count= 0; + + facetT *result= visible; + while (result && result->visible) { + result= result->f.replace; + if (count++ > qh->facet_id) + qh_infiniteloop(qh, visible); + } + return result; +} + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="makenewfacet">-</a> + + qh_makenewfacet(qh, 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(qhT *qh, setT *vertices, boolT toporient, facetT *horizon) { + facetT *newfacet; + vertexT *vertex, **vertexp; + + FOREACHvertex_(vertices) { + if (!vertex->newfacet) { + qh_removevertex(qh, vertex); + qh_appendvertex(qh, vertex); + } + } + newfacet= qh_newfacet(qh); + newfacet->vertices= vertices; + if (toporient) + newfacet->toporient= True; + if (horizon) + qh_setappend(qh, &(newfacet->neighbors), horizon); + qh_appendfacet(qh, newfacet); + return(newfacet); +} /* makenewfacet */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="makenewplanes">-</a> + + 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(qhT *qh /* qh.newfacet_list */) { + facetT *newfacet; + + trace4((qh, qh->ferr, 4074, "qh_makenewplanes: make new hyperplanes for facets on qh.newfacet_list f%d\n", + qh->newfacet_list->id)); + FORALLnew_facets { + if (!newfacet->mergehorizon) + qh_setfacetplane(qh, newfacet); /* updates Wnewvertexmax */ + } + if (qh->JOGGLEmax < REALmax/2) + minimize_(qh->min_vertex, -wwval_(Wnewvertexmax)); +} /* makenewplanes */ + +#ifndef qh_NOmerge +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="makenew_nonsimplicial">-</a> + + qh_makenew_nonsimplicial(qh, visible, apex, numnew ) + make new facets for ridges of a visible facet + + returns: + first newfacet, bumps numnew as needed + attaches new facets if !qh->NEWtentative + marks ridge neighbors for simplicial visible + if (qh.NEWtentative) + ridges on newfacet, horizon, and visible + else + ridge and neighbors between newfacet and horizon + visible facet's ridges are deleted + visible facet's f.neighbors is empty + + notes: + called by qh_makenewfacets and qh_triangulatefacet + qh.visit_id if visible has already been processed + sets neighbor->seen for building f.samecycle + assumes all 'seen' flags initially false + qh_delridge_merge not needed (as tested by qh_checkdelridge in qh_makenewfacets) + + 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) + +*/ +facetT *qh_makenew_nonsimplicial(qhT *qh, facetT *visible, vertexT *apex, int *numnew) { + void **freelistp; /* used if !qh_NOmem by qh_memfree_() */ + ridgeT *ridge, **ridgep; + facetT *neighbor, *newfacet= NULL, *samecycle; + setT *vertices; + boolT toporient; + unsigned int ridgeid; + + FOREACHridge_(visible->ridges) { + ridgeid= ridge->id; + neighbor= otherfacet_(ridge, visible); + if (neighbor->visible) { + if (!qh->NEWtentative) { + if (neighbor->visitid == qh->visit_id) { + if (qh->traceridge == ridge) + qh->traceridge= NULL; + qh_setfree(qh, &(ridge->vertices)); /* delete on 2nd visit */ + qh_memfree_(qh, ridge, (int)sizeof(ridgeT), freelistp); + } + } + }else { /* neighbor is an horizon facet */ + toporient= (ridge->top == visible); + vertices= qh_setnew(qh, qh->hull_dim); /* makes sure this is quick */ + qh_setappend(qh, &vertices, apex); + qh_setappend_set(qh, &vertices, ridge->vertices); + newfacet= qh_makenewfacet(qh, vertices, toporient, neighbor); + (*numnew)++; + if (neighbor->coplanarhorizon) { + 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->NEWtentative) { + if (!neighbor->simplicial) + qh_setappend(qh, &(newfacet->ridges), ridge); + }else { /* qh_attachnewfacets */ + if (neighbor->seen) { + if (neighbor->simplicial) { + qh_fprintf(qh, 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, qh_ERRqhull, neighbor, visible); + } + qh_setappend(qh, &(neighbor->neighbors), newfacet); + }else + qh_setreplace(qh, neighbor->neighbors, visible, newfacet); + if (neighbor->simplicial) { + qh_setdel(neighbor->ridges, ridge); + qh_delridge(qh, ridge); + }else { + qh_setappend(qh, &(newfacet->ridges), ridge); + if (toporient) { + ridge->top= newfacet; + ridge->simplicialtop= True; + }else { + ridge->bottom= newfacet; + ridge->simplicialbot= True; + } + } + } + trace4((qh, 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 */ + return newfacet; +} /* makenew_nonsimplicial */ + +#else /* qh_NOmerge */ +facetT *qh_makenew_nonsimplicial(qhT *qh, facetT *visible, vertexT *apex, int *numnew) { + QHULL_UNUSED(qh) + QHULL_UNUSED(visible) + QHULL_UNUSED(apex) + QHULL_UNUSED(numnew) + + return NULL; +} +#endif /* qh_NOmerge */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="makenew_simplicial">-</a> + + qh_makenew_simplicial(qh, visible, apex, numnew ) + make new facets for simplicial visible facet and apex + + returns: + attaches new facets if !qh.NEWtentative + 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(qhT *qh, facetT *visible, vertexT *apex, int *numnew) { + facetT *neighbor, **neighborp, *newfacet= NULL; + setT *vertices; + boolT flip, toporient; + int horizonskip= 0, visibleskip= 0; + + FOREACHneighbor_(visible) { + if (!neighbor->seen && !neighbor->visible) { + vertices= qh_facetintersect(qh, 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(qh, vertices, toporient, neighbor); + (*numnew)++; + if (neighbor->coplanarhorizon && (qh->PREmerge || qh->MERGEexact)) { +#ifndef qh_NOmerge + newfacet->f.samecycle= newfacet; + newfacet->mergehorizon= True; +#endif + } + if (!qh->NEWtentative) + SETelem_(neighbor->neighbors, horizonskip)= newfacet; + trace4((qh, 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 */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="matchneighbor">-</a> + + qh_matchneighbor(qh, newfacet, newskip, hashsize, hashcount ) + either match subridge of newfacet with neighbor or add to hash_table + + returns: + matched ridges of newfacet, except for duplicate ridges + duplicate ridges marked by qh_DUPLICATEridge for qh_matchdupridge + + notes: + called by qh_matchnewfacets + assumes newfacet is simplicial + ridge is newfacet->vertices w/o newskip vertex + do not allocate memory (need to free hash_table cleanly) + uses linear hash chains + see qh_matchdupridge (poly2_r.c) + + 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 + note: dupridge detected when a match 'f&d skip %d' has already been seen + need to mark all of the dupridges for qh_matchdupridge + indicate a duplicate ridge by qh_DUPLICATEridge and f.dupridge + 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 + + state at "indicate a duplicate ridge": + newfacet@newskip= the argument + facet= the hashed facet@skip that has the same vertices as newfacet@newskip + same= true if matched vertices have the same orientation + matchfacet= neighbor at facet@skip + matchfacet=qh_DUPLICATEridge, matchfacet was previously detected as a dupridge of facet@skip + ismatch if 'vertex orientation (same) matches facet/newfacet orientation (toporient) + unknown facet will match later + + details at "indicate a duplicate ridge": + if !ismatch and matchfacet, + dupridge is between hashed facet@skip/matchfacet@matchskip and arg newfacet@newskip/unknown + set newfacet@newskip, facet@skip, and matchfacet@matchskip to qh_DUPLICATEridge + add newfacet and matchfacet to hash_table + if ismatch and matchfacet, + same as !ismatch and matchfacet -- it matches facet instead of matchfacet + if !ismatch and !matchfacet + dupridge between hashed facet@skip/unknown and arg newfacet@newskip/unknown + set newfacet@newskip and facet@skip to qh_DUPLICATEridge + add newfacet to hash_table + if ismatch and matchfacet==qh_DUPLICATEridge + dupridge with already duplicated hashed facet@skip and arg newfacet@newskip/unknown + set newfacet@newskip to qh_DUPLICATEridge + add newfacet to hash_table + facet's hyperplane already set +*/ +void qh_matchneighbor(qhT *qh, 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(qh, hashsize, newfacet->vertices, qh->hull_dim, 1, + SETelem_(newfacet->vertices, newskip)); + trace4((qh, 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(qh, 1, newfacet->vertices, newskip, facet->vertices, &skip, &same)) { + if (SETelem_(newfacet->vertices, newskip) == SETelem_(facet->vertices, skip)) { + qh_joggle_restart(qh, "two new facets with the same vertices"); + /* duplicated for multiple skips, not easily avoided */ + qh_fprintf(qh, qh->ferr, 7084, "qhull topology warning (qh_matchneighbor): will merge vertices to undo new facets -- f%d and f%d have the same vertices (skip %d, skip %d) and same horizon ridges to f%d and f%d\n", + facet->id, newfacet->id, skip, newskip, SETfirstt_(facet->neighbors, facetT)->id, SETfirstt_(newfacet->neighbors, facetT)->id); + /* will rename a vertex (QH3053). The fault was duplicate ridges (same vertices) in different facets due to a previous rename. Expensive to detect beforehand */ + } + 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, 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_joggle_restart(qh, "a ridge with more than two neighbors"); + qh_fprintf(qh, qh->ferr, 6107, "qhull topology error: facets f%d, f%d and f%d meet at a ridge with more than 2 neighbors. Can not continue due to no qh.PREmerge and no 'Qx' (MERGEexact)\n", + facet->id, newfacet->id, getid_(matchfacet)); + qh_errexit2(qh, qh_ERRtopology, facet, newfacet); + } + SETelem_(newfacet->neighbors, newskip)= qh_DUPLICATEridge; + newfacet->dupridge= True; + qh_addhash(newfacet, qh->hash_table, hashsize, hash); + (*hashcount)++; + if (matchfacet != qh_DUPLICATEridge) { + SETelem_(facet->neighbors, skip)= qh_DUPLICATEridge; + facet->dupridge= True; + if (matchfacet) { + matchskip= qh_setindex(matchfacet->neighbors, facet); + if (matchskip<0) { + qh_fprintf(qh, qh->ferr, 6260, "qhull topology error (qh_matchneighbor): matchfacet f%d is in f%d neighbors but not vice versa. Can not continue.\n", + matchfacet->id, facet->id); + qh_errexit2(qh, qh_ERRtopology, matchfacet, facet); + } + SETelem_(matchfacet->neighbors, matchskip)= qh_DUPLICATEridge; /* matchskip>=0 by QH6260 */ + matchfacet->dupridge= True; + qh_addhash(matchfacet, qh->hash_table, hashsize, hash); + *hashcount += 2; + } + } + trace4((qh, 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, qh->ferr, 4053, "qh_matchneighbor: no match for f%d skip %d at hash %d\n", + newfacet->id, newskip, hash)); +} /* matchneighbor */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="matchnewfacets">-</a> + + qh_matchnewfacets(qh ) + match new facets in qh.newfacet_list to their newfacet neighbors + all facets are simplicial + + returns: + if dupridges and merging + returns maxdupdist (>=0.0) from vertex to opposite facet + sets facet->dupridge + missing neighbor links identify dupridges to be merged (qh_DUPLICATEridge) + else + qh.newfacet_list with full neighbor sets + vertices for the nth neighbor match all but the nth vertex + if not merging and qh.FORCEoutput + for facets with normals (i.e., with dupridges) + sets facet->flippped for flipped normals, also prevents point partitioning + + notes: + called by qh_buildcone* and qh_triangulate_facet + neighbor[0] of new facets is the horizon facet + if NEWtentative, new facets not attached to the horizon + 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: + truncate neighbor sets to horizon facet 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 + try to match facets with the same coplanar horizon + if not all matched + for each new facet with a duplicate ridge + match it with a coplanar facet, or identify a pinched vertex + if not merging and qh.FORCEoutput + check for flipped facets +*/ +coordT qh_matchnewfacets(qhT *qh /* qh.newfacet_list */) { + int numnew=0, hashcount=0, newskip; + facetT *newfacet, *neighbor; + coordT maxdupdist= 0.0, maxdist2; + int dim= qh->hull_dim, hashsize, neighbor_i, neighbor_n; + setT *neighbors; +#ifndef qh_NOtrace + int facet_i, facet_n, numunused= 0; + facetT *facet; +#endif + + trace1((qh, qh->ferr, 1019, "qh_matchnewfacets: match neighbors for new facets.\n")); + FORALLnew_facets { + numnew++; + { /* inline qh_setzero(qh, 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, (size_t)(dim * SETelemsize)); + } + } + + qh_newhashtable(qh, numnew*(qh->hull_dim-1)); /* twice what is normally needed, + but every ridge could be DUPLICATEridge */ + hashsize= qh_setsize(qh, qh->hash_table); + FORALLnew_facets { + if (!newfacet->simplicial) { + qh_fprintf(qh, qh->ferr, 6377, "qhull internal error (qh_matchnewfacets): expecting simplicial facets on qh.newfacet_list f%d for qh_matchneighbors, qh_matchneighbor, and qh_matchdupridge. Got non-simplicial f%d\n", + qh->newfacet_list->id, newfacet->id); + qh_errexit2(qh, qh_ERRqhull, newfacet, qh->newfacet_list); + } + for (newskip=1; newskip<qh->hull_dim; newskip++) /* furthest/horizon already matched */ + /* hashsize>0 because hull_dim>1 and numnew>0 */ + qh_matchneighbor(qh, newfacet, newskip, hashsize, &hashcount); +#if 0 /* use the following to trap hashcount errors */ + { + int count= 0, k; + facetT *facet, *neighbor; + + count= 0; + FORALLfacet_(qh->newfacet_list) { /* newfacet already in use */ + for (k=1; k < qh->hull_dim; k++) { + neighbor= SETelemt_(facet->neighbors, k, facetT); + if (!neighbor || neighbor == qh_DUPLICATEridge) + count++; + } + if (facet == newfacet) + break; + } + if (count != hashcount) { + qh_fprintf(qh, qh->ferr, 6266, "qhull error (qh_matchnewfacets): after adding facet %d, hashcount %d != count %d\n", + newfacet->id, hashcount, count); + qh_errexit(qh, qh_ERRdebug, newfacet, NULL); + } + } +#endif /* end of trap code */ + } /* end FORALLnew_facets */ + if (hashcount) { /* all neighbors matched, except for qh_DUPLICATEridge neighbors */ + qh_joggle_restart(qh, "ridge with multiple neighbors"); + if (hashcount) { + FORALLnew_facets { + if (newfacet->dupridge) { + FOREACHneighbor_i_(qh, newfacet) { + if (neighbor == qh_DUPLICATEridge) { + maxdist2= qh_matchdupridge(qh, newfacet, neighbor_i, hashsize, &hashcount); + maximize_(maxdupdist, maxdist2); + } + } + } + } + } + } + if (hashcount) { + qh_fprintf(qh, qh->ferr, 6108, "qhull internal error (qh_matchnewfacets): %d neighbors did not match up\n", + hashcount); + qh_printhashtable(qh, qh->ferr); + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } +#ifndef qh_NOtrace + if (qh->IStracing >= 3) { + FOREACHfacet_i_(qh, qh->hash_table) { + if (!facet) + numunused++; + } + qh_fprintf(qh, qh->ferr, 3063, "qh_matchnewfacets: maxdupdist %2.2g, new facets %d, unused hash entries %d, hashsize %d\n", + maxdupdist, numnew, numunused, qh_setsize(qh, qh->hash_table)); + } +#endif /* !qh_NOtrace */ + qh_setfree(qh, &qh->hash_table); + if (qh->PREmerge || qh->MERGEexact) { + if (qh->IStracing >= 4) + qh_printfacetlist(qh, qh->newfacet_list, NULL, qh_ALL); + } + return maxdupdist; +} /* matchnewfacets */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="matchvertices">-</a> + + qh_matchvertices(qh, firstindex, verticesA, skipA, verticesB, skipB, same ) + tests whether vertices match with a single skip + starts match at firstindex since all new facets have a common vertex + + returns: + true if matched vertices + skip index for skipB + sets same iff vertices have the same orientation + + notes: + called by qh_matchneighbor and qh_matchdupridge + assumes skipA is in A and both sets are the same size + + design: + set up pointers + scan both sets checking for a match + test orientation +*/ +boolT qh_matchvertices(qhT *qh, int firstindex, setT *verticesA, int skipA, + setT *verticesB, int *skipB, boolT *same) { + vertexT **elemAp, **elemBp, **skipBp=NULL, **skipAp; + + elemAp= SETelemaddr_(verticesA, firstindex, vertexT); + elemBp= SETelemaddr_(verticesB, firstindex, vertexT); + skipAp= SETelemaddr_(verticesA, skipA, vertexT); + do if (elemAp != skipAp) { + while (*elemAp != *elemBp++) { + if (skipBp) + return False; + skipBp= elemBp; /* one extra like FOREACH */ + } + }while (*(++elemAp)); + if (!skipBp) + skipBp= ++elemBp; + *skipB= SETindex_(verticesB, skipB); /* i.e., skipBp - verticesB + verticesA and verticesB are the same size, otherwise trace4 may segfault */ + *same= !((skipA & 0x1) ^ (*skipB & 0x1)); /* result is 0 or 1 */ + trace4((qh, qh->ferr, 4054, "qh_matchvertices: matched by skip %d(v%d) and skip %d(v%d) same? %d\n", + skipA, (*skipAp)->id, *skipB, (*(skipBp-1))->id, *same)); + return(True); +} /* matchvertices */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="newfacet">-</a> + + qh_newfacet(qh) + return a new facet + + returns: + all fields initialized or cleared (NULL) + preallocates neighbors set +*/ +facetT *qh_newfacet(qhT *qh) { + facetT *facet; + void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */ + + qh_memalloc_(qh, (int)sizeof(facetT), freelistp, facet, facetT); + memset((char *)facet, (size_t)0, sizeof(facetT)); + if (qh->facet_id == qh->tracefacet_id) + qh->tracefacet= facet; + facet->id= qh->facet_id++; + facet->neighbors= qh_setnew(qh, qh->hull_dim); +#if !qh_COMPUTEfurthest + facet->furthestdist= 0.0; +#endif +#if qh_MAXoutside + if (qh->FORCEoutput && qh->APPROXhull) + facet->maxoutside= qh->MINoutside; + else + facet->maxoutside= qh->DISTround; /* same value as test for QH7082 */ +#endif + facet->simplicial= True; + facet->good= True; + facet->newfacet= True; + trace4((qh, qh->ferr, 4055, "qh_newfacet: created facet f%d\n", facet->id)); + return(facet); +} /* newfacet */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="newridge">-</a> + + qh_newridge() + return a new ridge + notes: + caller sets qh.traceridge +*/ +ridgeT *qh_newridge(qhT *qh) { + ridgeT *ridge; + void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */ + + qh_memalloc_(qh, (int)sizeof(ridgeT), freelistp, ridge, ridgeT); + memset((char *)ridge, (size_t)0, sizeof(ridgeT)); + zinc_(Ztotridges); + if (qh->ridge_id == UINT_MAX) { + qh_fprintf(qh, qh->ferr, 7074, "qhull warning: more than 2^32 ridges. Qhull results are OK. Since the ridge ID wraps around to 0, two ridges may have the same identifier.\n"); + } + ridge->id= qh->ridge_id++; + trace4((qh, qh->ferr, 4056, "qh_newridge: created ridge r%d\n", ridge->id)); + return(ridge); +} /* newridge */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="pointid">-</a> + + qh_pointid(qh, point ) + return id for a point, + returns qh_IDnone(-3) if null, qh_IDinterior(-2) if interior, or qh_IDunknown(-1) if not known + + alternative code if point is in qh.first_point... + unsigned long id; + id= ((unsigned long)point - (unsigned long)qh.first_point)/qh.normal_size; + + notes: + Valid points are non-negative + WARN64 -- id truncated to 32-bits, at most 2G points + NOerrors returned (QhullPoint::id) + if point not in point array + the code does a comparison of unrelated pointers. +*/ +int qh_pointid(qhT *qh, pointT *point) { + ptr_intT offset, id; + + if (!point || !qh) + return qh_IDnone; + else if (point == qh->interior_point) + return qh_IDinterior; + else if (point >= qh->first_point + && point < qh->first_point + qh->num_points * qh->hull_dim) { + offset= (ptr_intT)(point - qh->first_point); + id= offset / qh->hull_dim; + }else if ((id= qh_setindex(qh->other_points, point)) != -1) + id += qh->num_points; + else + return qh_IDunknown; + return (int)id; +} /* pointid */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="removefacet">-</a> + + qh_removefacet(qh, facet ) + unlinks facet from qh.facet_list, + + returns: + updates qh.facet_list .newfacet_list .facet_next visible_list + decrements qh.num_facets + + see: + qh_appendfacet +*/ +void qh_removefacet(qhT *qh, facetT *facet) { + facetT *next= facet->next, *previous= facet->previous; /* next is always defined */ + + if (facet == qh->newfacet_list) + qh->newfacet_list= next; + if (facet == qh->facet_next) + qh->facet_next= next; + if (facet == qh->visible_list) + qh->visible_list= next; + if (previous) { + previous->next= next; + next->previous= previous; + }else { /* 1st facet in qh->facet_list */ + qh->facet_list= next; + qh->facet_list->previous= NULL; + } + qh->num_facets--; + trace4((qh, qh->ferr, 4057, "qh_removefacet: removed f%d from facet_list, newfacet_list, and visible_list\n", facet->id)); +} /* removefacet */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="removevertex">-</a> + + qh_removevertex(qh, vertex ) + unlinks vertex from qh.vertex_list, + + returns: + updates qh.vertex_list .newvertex_list + decrements qh.num_vertices +*/ +void qh_removevertex(qhT *qh, vertexT *vertex) { + vertexT *next= vertex->next, *previous= vertex->previous; /* next is always defined */ + + trace4((qh, qh->ferr, 4058, "qh_removevertex: remove v%d from qh.vertex_list\n", vertex->id)); + if (vertex == qh->newvertex_list) + qh->newvertex_list= next; + if (previous) { + previous->next= next; + next->previous= previous; + }else { /* 1st vertex in qh->vertex_list */ + qh->vertex_list= next; + qh->vertex_list->previous= NULL; + } + qh->num_vertices--; +} /* removevertex */ + + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="update_vertexneighbors">-</a> + + qh_update_vertexneighbors(qh ) + update vertex neighbors and delete interior vertices + + returns: + if qh.VERTEXneighbors, + if qh.newvertex_list, + removes visible neighbors from vertex neighbors + if qh.newfacet_list + adds new facets to vertex neighbors + if qh.visible_list + interior vertices added to qh.del_vertices for later partitioning as coplanar points + if not qh.VERTEXneighbors (not merging) + interior vertices of visible facets added to qh.del_vertices for later partitioning as coplanar points + + notes + [jan'19] split off qh_update_vertexneighbors_cone. Optimize the remaining cases in a future release + called by qh_triangulate_facet after triangulating a non-simplicial facet, followed by reset_lists + called by qh_triangulate after triangulating null and mirror facets + called by qh_all_vertexmerges after calling qh_merge_pinchedvertices + + design: + if qh.VERTEXneighbors + for each vertex on newvertex_list (i.e., new vertices and vertices of new facets) + delete visible facets from vertex neighbors + for each new facet on newfacet_list + for each vertex of facet + append facet to vertex neighbors + for each visible facet on qh.visible_list + for each vertex of facet + if the vertex is not on a new facet and not itself deleted + if the vertex has a not-visible neighbor (due to merging) + remove the visible facet from the vertex's neighbors + otherwise + add the vertex to qh.del_vertices for later deletion + + if not qh.VERTEXneighbors (not merging) + for each vertex of a visible facet + if the vertex is not on a new facet and not itself deleted + add the vertex to qh.del_vertices for later deletion +*/ +void qh_update_vertexneighbors(qhT *qh /* qh.newvertex_list, newfacet_list, visible_list */) { + facetT *newfacet= NULL, *neighbor, **neighborp, *visible; + vertexT *vertex, **vertexp; + int neighborcount= 0; + + if (qh->VERTEXneighbors) { + trace3((qh, qh->ferr, 3013, "qh_update_vertexneighbors: update v.neighbors for qh.newvertex_list (v%d) and qh.newfacet_list (f%d)\n", + getid_(qh->newvertex_list), getid_(qh->newfacet_list))); + FORALLvertex_(qh->newvertex_list) { + neighborcount= 0; + FOREACHneighbor_(vertex) { + if (neighbor->visible) { + neighborcount++; + SETref_(neighbor)= NULL; + } + } + if (neighborcount) { + trace4((qh, qh->ferr, 4046, "qh_update_vertexneighbors: delete %d of %d vertex neighbors for v%d. Removes to-be-deleted, visible facets\n", + neighborcount, qh_setsize(qh, vertex->neighbors), vertex->id)); + qh_setcompact(qh, vertex->neighbors); + } + } + FORALLnew_facets { + if (qh->first_newfacet && newfacet->id >= qh->first_newfacet) { + FOREACHvertex_(newfacet->vertices) + qh_setappend(qh, &vertex->neighbors, newfacet); + }else { /* called after qh_merge_pinchedvertices. In 7-D, many more neighbors than new facets. qh_setin is expensive */ + FOREACHvertex_(newfacet->vertices) + qh_setunique(qh, &vertex->neighbors, newfacet); + } + } + trace3((qh, qh->ferr, 3058, "qh_update_vertexneighbors: delete interior vertices for qh.visible_list (f%d)\n", + getid_(qh->visible_list))); + FORALLvisible_facets { + FOREACHvertex_(visible->vertices) { + if (!vertex->newfacet && !vertex->deleted) { + FOREACHneighbor_(vertex) { /* this can happen under merging */ + if (!neighbor->visible) + break; + } + if (neighbor) + qh_setdel(vertex->neighbors, visible); + else { + vertex->deleted= True; + qh_setappend(qh, &qh->del_vertices, vertex); + trace2((qh, qh->ferr, 2041, "qh_update_vertexneighbors: delete interior vertex p%d(v%d) of visible f%d\n", + qh_pointid(qh, vertex->point), vertex->id, visible->id)); + } + } + } + } + }else { /* !VERTEXneighbors */ + trace3((qh, qh->ferr, 3058, "qh_update_vertexneighbors: delete old vertices for qh.visible_list (f%d)\n", + getid_(qh->visible_list))); + FORALLvisible_facets { + FOREACHvertex_(visible->vertices) { + if (!vertex->newfacet && !vertex->deleted) { + vertex->deleted= True; + qh_setappend(qh, &qh->del_vertices, vertex); + trace2((qh, qh->ferr, 2042, "qh_update_vertexneighbors: will delete interior vertex p%d(v%d) of visible f%d\n", + qh_pointid(qh, vertex->point), vertex->id, visible->id)); + } + } + } + } +} /* update_vertexneighbors */ + +/*-<a href="qh-poly_r.htm#TOC" + >-------------------------------</a><a name="update_vertexneighbors_cone">-</a> + + qh_update_vertexneighbors_cone(qh ) + update vertex neighbors for a cone of new facets and delete interior vertices + + returns: + if qh.VERTEXneighbors, + if qh.newvertex_list, + removes visible neighbors from vertex neighbors + if qh.newfacet_list + adds new facets to vertex neighbors + if qh.visible_list + interior vertices added to qh.del_vertices for later partitioning as coplanar points + if not qh.VERTEXneighbors (not merging) + interior vertices of visible facets added to qh.del_vertices for later partitioning as coplanar points + + notes + called by qh_addpoint after create cone and before premerge + + design: + if qh.VERTEXneighbors + for each vertex on newvertex_list (i.e., new vertices and vertices of new facets) + delete visible facets from vertex neighbors + for each new facet on newfacet_list + for each vertex of facet + append facet to vertex neighbors + for each visible facet on qh.visible_list + for each vertex of facet + if the vertex is not on a new facet and not itself deleted + if the vertex has a not-visible neighbor (due to merging) + remove the visible facet from the vertex's neighbors + otherwise + add the vertex to qh.del_vertices for later deletion + + if not qh.VERTEXneighbors (not merging) + for each vertex of a visible facet + if the vertex is not on a new facet and not itself deleted + add the vertex to qh.del_vertices for later deletion + +*/ +void qh_update_vertexneighbors_cone(qhT *qh /* qh.newvertex_list, newfacet_list, visible_list */) { + facetT *newfacet= NULL, *neighbor, **neighborp, *visible; + vertexT *vertex, **vertexp; + int delcount= 0; + + if (qh->VERTEXneighbors) { + trace3((qh, qh->ferr, 3059, "qh_update_vertexneighbors_cone: update v.neighbors for qh.newvertex_list (v%d) and qh.newfacet_list (f%d)\n", + getid_(qh->newvertex_list), getid_(qh->newfacet_list))); + FORALLvertex_(qh->newvertex_list) { + delcount= 0; + FOREACHneighbor_(vertex) { + if (neighbor->visible) { /* alternative design is a loop over visible facets, but needs qh_setdel() */ + delcount++; + qh_setdelnth(qh, vertex->neighbors, SETindex_(vertex->neighbors, neighbor)); + neighborp--; /* repeat */ + } + } + if (delcount) { + trace4((qh, qh->ferr, 4021, "qh_update_vertexneighbors_cone: deleted %d visible vertexneighbors of v%d\n", + delcount, vertex->id)); + } + } + FORALLnew_facets { + FOREACHvertex_(newfacet->vertices) + qh_setappend(qh, &vertex->neighbors, newfacet); + } + trace3((qh, qh->ferr, 3065, "qh_update_vertexneighbors_cone: delete interior vertices, if any, for qh.visible_list (f%d)\n", + getid_(qh->visible_list))); + FORALLvisible_facets { + FOREACHvertex_(visible->vertices) { + if (!vertex->newfacet && !vertex->deleted) { + FOREACHneighbor_(vertex) { /* this can happen under merging, qh_checkfacet QH4025 */ + if (!neighbor->visible) + break; + } + if (neighbor) + qh_setdel(vertex->neighbors, visible); + else { + vertex->deleted= True; + qh_setappend(qh, &qh->del_vertices, vertex); + trace2((qh, qh->ferr, 2102, "qh_update_vertexneighbors_cone: will delete interior vertex p%d(v%d) of visible f%d\n", + qh_pointid(qh, vertex->point), vertex->id, visible->id)); + } + } + } + } + }else { /* !VERTEXneighbors */ + trace3((qh, qh->ferr, 3066, "qh_update_vertexneighbors_cone: delete interior vertices for qh.visible_list (f%d)\n", + getid_(qh->visible_list))); + FORALLvisible_facets { + FOREACHvertex_(visible->vertices) { + if (!vertex->newfacet && !vertex->deleted) { + vertex->deleted= True; + qh_setappend(qh, &qh->del_vertices, vertex); + trace2((qh, qh->ferr, 2059, "qh_update_vertexneighbors_cone: will delete interior vertex p%d(v%d) of visible f%d\n", + qh_pointid(qh, vertex->point), vertex->id, visible->id)); + } + } + } + } +} /* update_vertexneighbors_cone */ + diff --git a/contrib/libs/qhull/libqhull_r/poly_r.h b/contrib/libs/qhull/libqhull_r/poly_r.h new file mode 100644 index 0000000000..83c59140de --- /dev/null +++ b/contrib/libs/qhull/libqhull_r/poly_r.h @@ -0,0 +1,310 @@ +/*<html><pre> -<a href="qh-poly_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + poly_r.h + header file for poly_r.c and poly2_r.c + + see qh-poly_r.htm, libqhull_r.h and poly_r.c + + Copyright (c) 1993-2020 The Geometry Center. + $Id: //main/2019/qhull/src/libqhull_r/poly_r.h#5 $$Change: 2963 $ + $DateTime: 2020/06/03 19:31:01 $$Author: bbarber $ +*/ + +#ifndef qhDEFpoly +#define qhDEFpoly 1 + +#include "libqhull_r.h" + +/*=============== constants ========================== */ + +/*-<a href="qh-geom_r.htm#TOC" + >--------------------------------</a><a name="ALGORITHMfault">-</a> + + qh_ALGORITHMfault + use as argument to checkconvex() to report errors during buildhull +*/ +#define qh_ALGORITHMfault 0 + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="DATAfault">-</a> + + qh_DATAfault + use as argument to checkconvex() to report errors during initialhull +*/ +#define qh_DATAfault 1 + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="DUPLICATEridge">-</a> + + qh_DUPLICATEridge + special value for facet->neighbor to indicate a duplicate ridge + + notes: + set by qh_matchneighbor for qh_matchdupridge +*/ +#define qh_DUPLICATEridge (facetT *)1L + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="MERGEridge">-</a> + + qh_MERGEridge flag in facet + special value for facet->neighbor to indicate a duplicate ridge that needs merging + + notes: + set by qh_matchnewfacets..qh_matchdupridge from qh_DUPLICATEridge + used by qh_mark_dupridges to set facet->mergeridge, facet->mergeridge2 from facet->dupridge +*/ +#define qh_MERGEridge (facetT *)2L + + +/*============ -structures- ====================*/ + +/*=========== -macros- =========================*/ + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="FORALLfacet_">-</a> + + 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 ) + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="FORALLnew_facets">-</a> + + 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 ) + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="FORALLvertex_">-</a> + + 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 ) + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="FORALLvisible_facets">-</a> + + 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) + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="FORALLsame_">-</a> + + 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) + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="FORALLsame_cycle_">-</a> + + 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)) + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="FOREACHneighborA_">-</a> + + 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: + <a href="qset_r.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHneighborA_(facet) FOREACHsetelement_(facetT, facet->neighbors, neighborA) + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="FOREACHvisible_">-</a> + + FOREACHvisible_( facets ) { ... } + assign 'visible' to each facet in facets + + notes: + uses 'facetT *facet, *facetp;' + see <a href="qset_r.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHvisible_(facets) FOREACHsetelement_(facetT, facets, visible) + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="FOREACHnewfacet_">-</a> + + FOREACHnewfacet_( facets ) { ... } + assign 'newfacet' to each facet in facets + + notes: + uses 'facetT *newfacet, *newfacetp;' + see <a href="qset_r.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHnewfacet_(facets) FOREACHsetelement_(facetT, facets, newfacet) + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="FOREACHvertexA_">-</a> + + FOREACHvertexA_( vertices ) { ... } + assign 'vertexA' to each vertex in vertices + + notes: + uses 'vertexT *vertexA, *vertexAp;' + see <a href="qset_r.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHvertexA_(vertices) FOREACHsetelement_(vertexT, vertices, vertexA) + +/*-<a href="qh-poly_r.htm#TOC" + >--------------------------------</a><a name="FOREACHvertexreverse12_">-</a> + + FOREACHvertexreverse12_( vertices ) { ... } + assign 'vertex' to each vertex in vertices + reverse order of first two vertices + + notes: + uses 'vertexT *vertex, *vertexp;' + see <a href="qset_r.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHvertexreverse12_(vertices) FOREACHsetelementreverse12_(vertexT, vertices, vertex) + + +/*=============== prototypes poly_r.c in alphabetical order ================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +void qh_appendfacet(qhT *qh, facetT *facet); +void qh_appendvertex(qhT *qh, vertexT *vertex); +void qh_attachnewfacets(qhT *qh /* qh.visible_list, qh.newfacet_list */); +boolT qh_checkflipped(qhT *qh, facetT *facet, realT *dist, boolT allerror); +void qh_delfacet(qhT *qh, facetT *facet); +void qh_deletevisible(qhT *qh /* qh.visible_list, qh.horizon_list */); +setT *qh_facetintersect(qhT *qh, facetT *facetA, facetT *facetB, int *skipAp,int *skipBp, int extra); +int qh_gethash(qhT *qh, int hashsize, setT *set, int size, int firstindex, void *skipelem); +facetT *qh_getreplacement(qhT *qh, facetT *visible); +facetT *qh_makenewfacet(qhT *qh, setT *vertices, boolT toporient, facetT *facet); +void qh_makenewplanes(qhT *qh /* qh.newfacet_list */); +facetT *qh_makenew_nonsimplicial(qhT *qh, facetT *visible, vertexT *apex, int *numnew); +facetT *qh_makenew_simplicial(qhT *qh, facetT *visible, vertexT *apex, int *numnew); +void qh_matchneighbor(qhT *qh, facetT *newfacet, int newskip, int hashsize, + int *hashcount); +coordT qh_matchnewfacets(qhT *qh); +boolT qh_matchvertices(qhT *qh, int firstindex, setT *verticesA, int skipA, + setT *verticesB, int *skipB, boolT *same); +facetT *qh_newfacet(qhT *qh); +ridgeT *qh_newridge(qhT *qh); +int qh_pointid(qhT *qh, pointT *point); +void qh_removefacet(qhT *qh, facetT *facet); +void qh_removevertex(qhT *qh, vertexT *vertex); +void qh_update_vertexneighbors(qhT *qh); +void qh_update_vertexneighbors_cone(qhT *qh); + + +/*========== -prototypes poly2_r.c in alphabetical order ===========*/ + +boolT qh_addfacetvertex(qhT *qh, facetT *facet, vertexT *newvertex); +void qh_addhash(void *newelem, setT *hashtable, int hashsize, int hash); +void qh_check_bestdist(qhT *qh); +void qh_check_maxout(qhT *qh); +void qh_check_output(qhT *qh); +void qh_check_point(qhT *qh, pointT *point, facetT *facet, realT *maxoutside, realT *maxdist, facetT **errfacet1, facetT **errfacet2, int *errcount); +void qh_check_points(qhT *qh); +void qh_checkconvex(qhT *qh, facetT *facetlist, int fault); +void qh_checkfacet(qhT *qh, facetT *facet, boolT newmerge, boolT *waserrorp); +void qh_checkflipped_all(qhT *qh, facetT *facetlist); +boolT qh_checklists(qhT *qh, facetT *facetlist); +void qh_checkpolygon(qhT *qh, facetT *facetlist); +void qh_checkvertex(qhT *qh, vertexT *vertex, boolT allchecks, boolT *waserrorp); +void qh_clearcenters(qhT *qh, qh_CENTER type); +void qh_createsimplex(qhT *qh, setT *vertices); +void qh_delridge(qhT *qh, ridgeT *ridge); +void qh_delvertex(qhT *qh, vertexT *vertex); +setT *qh_facet3vertex(qhT *qh, facetT *facet); +facetT *qh_findbestfacet(qhT *qh, pointT *point, boolT bestoutside, + realT *bestdist, boolT *isoutside); +facetT *qh_findbestlower(qhT *qh, facetT *upperfacet, pointT *point, realT *bestdistp, int *numpart); +facetT *qh_findfacet_all(qhT *qh, pointT *point, boolT noupper, realT *bestdist, boolT *isoutside, + int *numpart); +int qh_findgood(qhT *qh, facetT *facetlist, int goodhorizon); +void qh_findgood_all(qhT *qh, facetT *facetlist); +void qh_furthestnext(qhT *qh /* qh.facet_list */); +void qh_furthestout(qhT *qh, facetT *facet); +void qh_infiniteloop(qhT *qh, facetT *facet); +void qh_initbuild(qhT *qh); +void qh_initialhull(qhT *qh, setT *vertices); +setT *qh_initialvertices(qhT *qh, int dim, setT *maxpoints, pointT *points, int numpoints); +vertexT *qh_isvertex(pointT *point, setT *vertices); +vertexT *qh_makenewfacets(qhT *qh, pointT *point /* qh.horizon_list, visible_list */); +coordT qh_matchdupridge(qhT *qh, facetT *atfacet, int atskip, int hashsize, int *hashcount); +void qh_nearcoplanar(qhT *qh /* qh.facet_list */); +vertexT *qh_nearvertex(qhT *qh, facetT *facet, pointT *point, realT *bestdistp); +int qh_newhashtable(qhT *qh, int newsize); +vertexT *qh_newvertex(qhT *qh, pointT *point); +facetT *qh_nextfacet2d(facetT *facet, vertexT **nextvertexp); +ridgeT *qh_nextridge3d(ridgeT *atridge, facetT *facet, vertexT **vertexp); +vertexT *qh_opposite_vertex(qhT *qh, facetT *facetA, facetT *neighbor); +void qh_outcoplanar(qhT *qh /* qh.facet_list */); +pointT *qh_point(qhT *qh, int id); +void qh_point_add(qhT *qh, setT *set, pointT *point, void *elem); +setT *qh_pointfacet(qhT *qh /* qh.facet_list */); +setT *qh_pointvertex(qhT *qh /* qh.facet_list */); +void qh_prependfacet(qhT *qh, facetT *facet, facetT **facetlist); +void qh_printhashtable(qhT *qh, FILE *fp); +void qh_printlists(qhT *qh); +void qh_replacefacetvertex(qhT *qh, facetT *facet, vertexT *oldvertex, vertexT *newvertex); +void qh_resetlists(qhT *qh, boolT stats, boolT resetVisible /* qh.newvertex_list qh.newfacet_list qh.visible_list */); +void qh_setvoronoi_all(qhT *qh); +void qh_triangulate(qhT *qh /* qh.facet_list */); +void qh_triangulate_facet(qhT *qh, facetT *facetA, vertexT **first_vertex); +void qh_triangulate_link(qhT *qh, facetT *oldfacetA, facetT *facetA, facetT *oldfacetB, facetT *facetB); +void qh_triangulate_mirror(qhT *qh, facetT *facetA, facetT *facetB); +void qh_triangulate_null(qhT *qh, facetT *facetA); +void qh_vertexintersect(qhT *qh, setT **vertexsetA,setT *vertexsetB); +setT *qh_vertexintersect_new(qhT *qh, setT *vertexsetA,setT *vertexsetB); +void qh_vertexneighbors(qhT *qh /* qh.facet_list */); +boolT qh_vertexsubset(setT *vertexsetA, setT *vertexsetB); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* qhDEFpoly */ diff --git a/contrib/libs/qhull/libqhull_r/qhull_ra.h b/contrib/libs/qhull/libqhull_r/qhull_ra.h new file mode 100644 index 0000000000..52ccd85a02 --- /dev/null +++ b/contrib/libs/qhull/libqhull_r/qhull_ra.h @@ -0,0 +1,161 @@ +/*<html><pre> -<a href="qh-qhull_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + qhull_ra.h + all header files for compiling qhull with reentrant code + included before C++ headers for user_r.h:QHULL_CRTDBG + + see qh-qhull.htm + + see libqhull_r.h for user-level definitions + + see user_r.h for user-definable constants + + defines internal functions for libqhull_r.c global_r.c + + Copyright (c) 1993-2020 The Geometry Center. + $Id: //main/2019/qhull/src/libqhull_r/qhull_ra.h#2 $$Change: 2953 $ + $DateTime: 2020/05/21 22:05:32 $$Author: bbarber $ + + Notes: grep for ((" and (" to catch fprintf("lkasdjf"); + full parens around (x?y:z) + use '#include "libqhull_r/qhull_ra.h"' to avoid name clashes +*/ + +#ifndef qhDEFqhulla +#define qhDEFqhulla 1 + +#include "libqhull_r.h" /* Includes user_r.h and data types */ + +#include "stat_r.h" +#include "random_r.h" +#include "mem_r.h" +#include "qset_r.h" +#include "geom_r.h" +#include "merge_r.h" +#include "poly_r.h" +#include "io_r.h" + +#include <setjmp.h> +#include <string.h> +#include <math.h> +#include <float.h> /* some compilers will not need float.h */ +#include <limits.h> +#include <time.h> +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +/*** uncomment here and qset_r.c + if string.h does not define memcpy() +#include <memory.h> +*/ + +#if qh_CLOCKtype == 2 /* defined in user_r.h from libqhull_r.h */ +#include <sys/types.h> +#include <sys/times.h> +#include <unistd.h> +#endif + +#ifdef _MSC_VER /* Microsoft Visual C++ -- warning level 4 */ +#pragma warning( disable : 4100) /* unreferenced formal parameter */ +#pragma warning( disable : 4127) /* conditional expression is constant */ +#pragma warning( disable : 4706) /* assignment within conditional function */ +#pragma warning( disable : 4996) /* function was declared deprecated(strcpy, localtime, etc.) */ +#endif + +/* ======= -macros- =========== */ + +/*-<a href="qh-qhull_r.htm#TOC" + >--------------------------------</a><a name="traceN">-</a> + + traceN((qh, qh->ferr, 0Nnnn, "format\n", vars)); + calls qh_fprintf if qh.IStracing >= N + + Add debugging traps to the end of qh_fprintf + + notes: + removing tracing reduces code size but doesn't change execution speed +*/ +#ifndef qh_NOtrace +#define trace0(args) {if (qh->IStracing) qh_fprintf args;} +#define trace1(args) {if (qh->IStracing >= 1) qh_fprintf args;} +#define trace2(args) {if (qh->IStracing >= 2) qh_fprintf args;} +#define trace3(args) {if (qh->IStracing >= 3) qh_fprintf args;} +#define trace4(args) {if (qh->IStracing >= 4) qh_fprintf args;} +#define trace5(args) {if (qh->IStracing >= 5) qh_fprintf args;} +#else /* qh_NOtrace */ +#define trace0(args) {} +#define trace1(args) {} +#define trace2(args) {} +#define trace3(args) {} +#define trace4(args) {} +#define trace5(args) {} +#endif /* qh_NOtrace */ + +/*-<a href="qh-qhull_r.htm#TOC" + >--------------------------------</a><a name="QHULL_UNUSED">-</a> + + Define an unused variable to avoid compiler warnings + + Derived from Qt's corelib/global/qglobal.h + +*/ + +#if defined(__cplusplus) && defined(__INTEL_COMPILER) && !defined(QHULL_OS_WIN) +template <typename T> +inline void qhullUnused(T &x) { (void)x; } +# define QHULL_UNUSED(x) qhullUnused(x); +#else +# define QHULL_UNUSED(x) (void)x; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/***** -libqhull_r.c prototypes (alphabetical after qhull) ********************/ + +void qh_qhull(qhT *qh); +boolT qh_addpoint(qhT *qh, pointT *furthest, facetT *facet, boolT checkdist); +void qh_build_withrestart(qhT *qh); +vertexT *qh_buildcone(qhT *qh, pointT *furthest, facetT *facet, int goodhorizon, facetT **retryfacet); +boolT qh_buildcone_mergepinched(qhT *qh, vertexT *apex, facetT *facet, facetT **retryfacet); +boolT qh_buildcone_onlygood(qhT *qh, vertexT *apex, int goodhorizon); +void qh_buildhull(qhT *qh); +void qh_buildtracing(qhT *qh, pointT *furthest, facetT *facet); +void qh_errexit2(qhT *qh, int exitcode, facetT *facet, facetT *otherfacet); +void qh_findhorizon(qhT *qh, pointT *point, facetT *facet, int *goodvisible,int *goodhorizon); +pointT *qh_nextfurthest(qhT *qh, facetT **visible); +void qh_partitionall(qhT *qh, setT *vertices, pointT *points,int npoints); +void qh_partitioncoplanar(qhT *qh, pointT *point, facetT *facet, realT *dist, boolT allnew); +void qh_partitionpoint(qhT *qh, pointT *point, facetT *facet); +void qh_partitionvisible(qhT *qh, boolT allpoints, int *numpoints); +void qh_joggle_restart(qhT *qh, const char *reason); +void qh_printsummary(qhT *qh, FILE *fp); + +/***** -global_r.c internal prototypes (alphabetical) ***********************/ + +void qh_appendprint(qhT *qh, qh_PRINT format); +void qh_freebuild(qhT *qh, boolT allmem); +void qh_freebuffers(qhT *qh); +void qh_initbuffers(qhT *qh, coordT *points, int numpoints, int dim, boolT ismalloc); + +/***** -stat_r.c internal prototypes (alphabetical) ***********************/ + +void qh_allstatA(qhT *qh); +void qh_allstatB(qhT *qh); +void qh_allstatC(qhT *qh); +void qh_allstatD(qhT *qh); +void qh_allstatE(qhT *qh); +void qh_allstatE2(qhT *qh); +void qh_allstatF(qhT *qh); +void qh_allstatG(qhT *qh); +void qh_allstatH(qhT *qh); +void qh_freebuffers(qhT *qh); +void qh_initbuffers(qhT *qh, coordT *points, int numpoints, int dim, boolT ismalloc); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* qhDEFqhulla */ diff --git a/contrib/libs/qhull/libqhull_r/qset_r.c b/contrib/libs/qhull/libqhull_r/qset_r.c new file mode 100644 index 0000000000..c3bec5ffa4 --- /dev/null +++ b/contrib/libs/qhull/libqhull_r/qset_r.c @@ -0,0 +1,1383 @@ +/*<html><pre> -<a href="qh-set_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + qset_r.c + implements set manipulations needed for quickhull + + see qh-set_r.htm and qset_r.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). + + Only reference qh for qhmem or qhstat. Otherwise the matching code in qset.c will bring in qhT + + Copyright (c) 1993-2020 The Geometry Center. + $Id: //main/2019/qhull/src/libqhull_r/qset_r.c#8 $$Change: 2953 $ + $DateTime: 2020/05/21 22:05:32 $$Author: bbarber $ +*/ + +#include "libqhull_r.h" /* for qhT and QHULL_CRTDBG */ +#include "qset_r.h" +#include "mem_r.h" +#include <stdio.h> +#include <string.h> +/*** uncomment here and qhull_ra.h + if string.h does not define memcpy() +#include <memory.h> +*/ + +#ifndef qhDEFlibqhull +typedef struct ridgeT ridgeT; +typedef struct facetT facetT; +void qh_errexit(qhT *qh, int exitcode, facetT *, ridgeT *); +void qh_fprintf(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... ); +# ifdef _MSC_VER /* Microsoft Visual C++ -- warning level 4 */ +# pragma warning( disable : 4127) /* conditional expression is constant */ +# pragma warning( disable : 4706) /* assignment within conditional function */ +# endif +#endif + +/*=============== internal macros ===========================*/ + +/*============ functions in alphabetical order ===================*/ + +/*-<a href="qh-set_r.htm#TOC" + >--------------------------------<a name="setaddnth">-</a> + + qh_setaddnth(qh, setp, nth, newelem ) + adds newelem as n'th element of sorted or unsorted *setp + + notes: + *setp and newelem must be defined + *setp may be a temp set + nth=0 is first element + errors if nth is out of bounds + + design: + expand *setp if empty or full + move tail of *setp up one + insert newelem +*/ +void qh_setaddnth(qhT *qh, setT **setp, int nth, void *newelem) { + int oldsize, i; + setelemT *sizep; /* avoid strict aliasing */ + setelemT *oldp, *newp; + + if (!*setp || (sizep= SETsizeaddr_(*setp))->i==0) { + qh_setlarger(qh, setp); + sizep= SETsizeaddr_(*setp); + } + oldsize= sizep->i - 1; + if (nth < 0 || nth > oldsize) { + qh_fprintf(qh, qh->qhmem.ferr, 6171, "qhull internal error (qh_setaddnth): nth %d is out-of-bounds for set:\n", nth); + qh_setprint(qh, qh->qhmem.ferr, "", *setp); + qh_errexit(qh, qhmem_ERRqhull, NULL, NULL); + } + sizep->i++; + oldp= (setelemT *)SETelemaddr_(*setp, oldsize, void); /* NULL */ + newp= oldp+1; + for (i=oldsize-nth+1; i--; ) /* move at least NULL */ + (newp--)->p= (oldp--)->p; /* may overwrite *sizep */ + newp->p= newelem; +} /* setaddnth */ + + +/*-<a href="qh-set_r.htm#TOC" + >--------------------------------<a name="setaddsorted">-</a> + + setaddsorted( setp, newelem ) + adds an newelem into sorted *setp + + notes: + *setp and newelem must be defined + *setp may be a temp set + nop if newelem already in set + + design: + find newelem's position in *setp + insert newelem +*/ +void qh_setaddsorted(qhT *qh, setT **setp, void *newelem) { + int newindex=0; + void *elem, **elemp; + + FOREACHelem_(*setp) { /* could use binary search instead */ + if (elem < newelem) + newindex++; + else if (elem == newelem) + return; + else + break; + } + qh_setaddnth(qh, setp, newindex, newelem); +} /* setaddsorted */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setappend">-</a> + + qh_setappend(qh, setp, newelem ) + append newelem to *setp + + notes: + *setp may be a temp set + *setp and newelem may be NULL + + design: + expand *setp if empty or full + append newelem to *setp + +*/ +void qh_setappend(qhT *qh, setT **setp, void *newelem) { + setelemT *sizep; /* Avoid strict aliasing. Writing to *endp may overwrite *sizep */ + setelemT *endp; + int count; + + if (!newelem) + return; + if (!*setp || (sizep= SETsizeaddr_(*setp))->i==0) { + qh_setlarger(qh, setp); + sizep= SETsizeaddr_(*setp); + } + count= (sizep->i)++ - 1; + endp= (setelemT *)SETelemaddr_(*setp, count, void); + (endp++)->p= newelem; + endp->p= NULL; +} /* setappend */ + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setappend_set">-</a> + + qh_setappend_set(qh, setp, setA ) + appends setA to *setp + + notes: + *setp can not be a temp set + *setp and setA may be NULL + + design: + setup for copy + expand *setp if it is too small + append all elements of setA to *setp +*/ +void qh_setappend_set(qhT *qh, setT **setp, setT *setA) { + int sizeA, size; + setT *oldset; + setelemT *sizep; + + if (!setA) + return; + SETreturnsize_(setA, sizeA); + if (!*setp) + *setp= qh_setnew(qh, sizeA); + sizep= SETsizeaddr_(*setp); + if (!(size= sizep->i)) + size= (*setp)->maxsize; + else + size--; + if (size + sizeA > (*setp)->maxsize) { + oldset= *setp; + *setp= qh_setcopy(qh, oldset, sizeA); + qh_setfree(qh, &oldset); + sizep= SETsizeaddr_(*setp); + } + if (sizeA > 0) { + sizep->i= size+sizeA+1; /* memcpy may overwrite */ + memcpy((char *)&((*setp)->e[size].p), (char *)&(setA->e[0].p), (size_t)(sizeA+1) * SETelemsize); + } +} /* setappend_set */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setappend2ndlast">-</a> + + qh_setappend2ndlast(qh, setp, newelem ) + makes newelem the next to the last element in *setp + + notes: + *setp must have at least one element + newelem must be defined + *setp may be a temp set + + design: + expand *setp if empty or full + move last element of *setp up one + insert newelem +*/ +void qh_setappend2ndlast(qhT *qh, setT **setp, void *newelem) { + setelemT *sizep; /* Avoid strict aliasing. Writing to *endp may overwrite *sizep */ + setelemT *endp, *lastp; + int count; + + if (!*setp || (sizep= SETsizeaddr_(*setp))->i==0) { + qh_setlarger(qh, setp); + sizep= SETsizeaddr_(*setp); + } + count= (sizep->i)++ - 1; + endp= (setelemT *)SETelemaddr_(*setp, count, void); /* NULL */ + lastp= endp-1; + *(endp++)= *lastp; + endp->p= NULL; /* may overwrite *sizep */ + lastp->p= newelem; +} /* setappend2ndlast */ + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setcheck">-</a> + + qh_setcheck(qh, set, typename, id ) + check set for validity + report errors with typename and id + + design: + checks that maxsize, actual size, and NULL terminator agree +*/ +void qh_setcheck(qhT *qh, setT *set, const char *tname, unsigned int id) { + int maxsize, size; + int waserr= 0; + + if (!set) + return; + SETreturnsize_(set, size); + maxsize= set->maxsize; + if (size > maxsize || !maxsize) { + qh_fprintf(qh, qh->qhmem.ferr, 6172, "qhull internal error (qh_setcheck): actual size %d of %s%d is greater than max size %d\n", + size, tname, id, maxsize); + waserr= 1; + }else if (set->e[size].p) { + qh_fprintf(qh, qh->qhmem.ferr, 6173, "qhull internal error (qh_setcheck): %s%d(size %d max %d) is not null terminated.\n", + tname, id, size-1, maxsize); + waserr= 1; + } + if (waserr) { + qh_setprint(qh, qh->qhmem.ferr, "ERRONEOUS", set); + qh_errexit(qh, qhmem_ERRqhull, NULL, NULL); + } +} /* setcheck */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setcompact">-</a> + + qh_setcompact(qh, set ) + remove internal NULLs from an unsorted set + + returns: + updated set + + notes: + set may be NULL + it would be faster to swap tail of set into holes, like qh_setdel + + design: + setup pointers into set + skip NULLs while copying elements to start of set + update the actual size +*/ +void qh_setcompact(qhT *qh, setT *set) { + int size; + void **destp, **elemp, **endp, **firstp; + + if (!set) + return; + SETreturnsize_(set, size); + destp= elemp= firstp= SETaddr_(set, void); + endp= destp + size; + while (1) { + if (!(*destp++= *elemp++)) { + destp--; + if (elemp > endp) + break; + } + } + qh_settruncate(qh, set, (int)(destp-firstp)); /* WARN64 */ +} /* setcompact */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setcopy">-</a> + + qh_setcopy(qh, set, extra ) + make a copy of a sorted or unsorted set with extra slots + + returns: + new set + + design: + create a newset with extra slots + copy the elements to the newset + +*/ +setT *qh_setcopy(qhT *qh, setT *set, int extra) { + setT *newset; + int size; + + if (extra < 0) + extra= 0; + SETreturnsize_(set, size); + newset= qh_setnew(qh, size+extra); + SETsizeaddr_(newset)->i= size+1; /* memcpy may overwrite */ + memcpy((char *)&(newset->e[0].p), (char *)&(set->e[0].p), (size_t)(size+1) * SETelemsize); + return(newset); +} /* setcopy */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setdel">-</a> + + qh_setdel(set, oldelem ) + delete oldelem from an unsorted set + + returns: + returns oldelem if found + returns NULL otherwise + + notes: + set may be NULL + oldelem must not be NULL; + only deletes one copy of oldelem in set + + design: + locate oldelem + update actual size if it was full + move the last element to the oldelem's location +*/ +void *qh_setdel(setT *set, void *oldelem) { + setelemT *sizep; + setelemT *elemp; + setelemT *lastp; + + if (!set) + return NULL; + elemp= (setelemT *)SETaddr_(set, void); + while (elemp->p != oldelem && elemp->p) + elemp++; + if (elemp->p) { + sizep= SETsizeaddr_(set); + if (!(sizep->i)--) /* if was a full set */ + sizep->i= set->maxsize; /* *sizep= (maxsize-1)+ 1 */ + lastp= (setelemT *)SETelemaddr_(set, sizep->i-1, void); + elemp->p= lastp->p; /* may overwrite itself */ + lastp->p= NULL; + return oldelem; + } + return NULL; +} /* setdel */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setdellast">-</a> + + qh_setdellast( set ) + return last element of set or NULL + + notes: + deletes element from set + set may be NULL + + design: + return NULL if empty + if full set + delete last element and set actual size + else + delete last element and update actual size +*/ +void *qh_setdellast(setT *set) { + int setsize; /* actually, actual_size + 1 */ + int maxsize; + setelemT *sizep; + void *returnvalue; + + if (!set || !(set->e[0].p)) + return NULL; + sizep= SETsizeaddr_(set); + if ((setsize= sizep->i)) { + returnvalue= set->e[setsize - 2].p; + set->e[setsize - 2].p= NULL; + sizep->i--; + }else { + maxsize= set->maxsize; + returnvalue= set->e[maxsize - 1].p; + set->e[maxsize - 1].p= NULL; + sizep->i= maxsize; + } + return returnvalue; +} /* setdellast */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setdelnth">-</a> + + qh_setdelnth(qh, set, nth ) + deletes nth element from unsorted set + 0 is first element + + returns: + returns the element (needs type conversion) + + notes: + errors if nth invalid + + design: + setup points and check nth + delete nth element and overwrite with last element +*/ +void *qh_setdelnth(qhT *qh, setT *set, int nth) { + void *elem; + setelemT *sizep; + setelemT *elemp, *lastp; + + sizep= SETsizeaddr_(set); + if ((sizep->i--)==0) /* if was a full set */ + sizep->i= set->maxsize; /* *sizep= (maxsize-1)+ 1 */ + if (nth < 0 || nth >= sizep->i) { + qh_fprintf(qh, qh->qhmem.ferr, 6174, "qhull internal error (qh_setdelnth): nth %d is out-of-bounds for set:\n", nth); + qh_setprint(qh, qh->qhmem.ferr, "", set); + qh_errexit(qh, qhmem_ERRqhull, NULL, NULL); + } + elemp= (setelemT *)SETelemaddr_(set, nth, void); /* nth valid by QH6174 */ + lastp= (setelemT *)SETelemaddr_(set, sizep->i-1, void); + elem= elemp->p; + elemp->p= lastp->p; /* may overwrite itself */ + lastp->p= NULL; + return elem; +} /* setdelnth */ + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setdelnthsorted">-</a> + + qh_setdelnthsorted(qh, set, nth ) + deletes nth element from sorted set + + returns: + returns the element (use type conversion) + + notes: + errors if nth invalid + + see also: + setnew_delnthsorted + + design: + setup points and check nth + copy remaining elements down one + update actual size +*/ +void *qh_setdelnthsorted(qhT *qh, setT *set, int nth) { + void *elem; + setelemT *sizep; + setelemT *newp, *oldp; + + sizep= SETsizeaddr_(set); + if (nth < 0 || (sizep->i && nth >= sizep->i-1) || nth >= set->maxsize) { + qh_fprintf(qh, qh->qhmem.ferr, 6175, "qhull internal error (qh_setdelnthsorted): nth %d is out-of-bounds for set:\n", nth); + qh_setprint(qh, qh->qhmem.ferr, "", set); + qh_errexit(qh, qhmem_ERRqhull, NULL, NULL); + } + newp= (setelemT *)SETelemaddr_(set, nth, void); + elem= newp->p; + oldp= newp+1; + while (((newp++)->p= (oldp++)->p)) + ; /* copy remaining elements and NULL */ + if ((sizep->i--)==0) /* if was a full set */ + sizep->i= set->maxsize; /* *sizep= (max size-1)+ 1 */ + return elem; +} /* setdelnthsorted */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setdelsorted">-</a> + + qh_setdelsorted( set, oldelem ) + deletes oldelem from sorted set + + returns: + returns oldelem if it was deleted + + notes: + set may be NULL + + design: + locate oldelem in set + copy remaining elements down one + update actual size +*/ +void *qh_setdelsorted(setT *set, void *oldelem) { + setelemT *sizep; + setelemT *newp, *oldp; + + if (!set) + return NULL; + newp= (setelemT *)SETaddr_(set, void); + while(newp->p != oldelem && newp->p) + newp++; + if (newp->p) { + oldp= newp+1; + while (((newp++)->p= (oldp++)->p)) + ; /* copy remaining elements */ + sizep= SETsizeaddr_(set); + if ((sizep->i--)==0) /* if was a full set */ + sizep->i= set->maxsize; /* *sizep= (max size-1)+ 1 */ + return oldelem; + } + return NULL; +} /* setdelsorted */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setduplicate">-</a> + + qh_setduplicate(qh, set, elemsize ) + duplicate a set of elemsize elements + + notes: + use setcopy if retaining old elements + + design: + create a new set + for each elem of the old set + create a newelem + append newelem to newset +*/ +setT *qh_setduplicate(qhT *qh, setT *set, int elemsize) { + void *elem, **elemp, *newElem; + setT *newSet; + int size; + + if (!(size= qh_setsize(qh, set))) + return NULL; + newSet= qh_setnew(qh, size); + FOREACHelem_(set) { + newElem= qh_memalloc(qh, elemsize); + memcpy(newElem, elem, (size_t)elemsize); + qh_setappend(qh, &newSet, newElem); + } + return newSet; +} /* setduplicate */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setendpointer">-</a> + + qh_setendpointer( set ) + Returns pointer to NULL terminator of a set's elements + set can not be NULL + +*/ +void **qh_setendpointer(setT *set) { + + setelemT *sizep= SETsizeaddr_(set); + int n= sizep->i; + return (n ? &set->e[n-1].p : &sizep->p); +} /* qh_setendpointer */ + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setequal">-</a> + + qh_setequal( setA, setB ) + returns 1 if two sorted sets are equal, otherwise returns 0 + + notes: + either set may be NULL + + design: + check size of each set + setup pointers + compare elements of each set +*/ +int qh_setequal(setT *setA, setT *setB) { + void **elemAp, **elemBp; + int sizeA= 0, sizeB= 0; + + if (setA) { + SETreturnsize_(setA, sizeA); + } + if (setB) { + SETreturnsize_(setB, sizeB); + } + if (sizeA != sizeB) + return 0; + if (!sizeA) + return 1; + elemAp= SETaddr_(setA, void); + elemBp= SETaddr_(setB, void); + if (!memcmp((char *)elemAp, (char *)elemBp, (size_t)(sizeA * SETelemsize))) + return 1; + return 0; +} /* setequal */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setequal_except">-</a> + + qh_setequal_except( setA, skipelemA, setB, skipelemB ) + returns 1 if sorted setA and setB are equal except for skipelemA & B + + returns: + false if either skipelemA or skipelemB are missing + + notes: + neither set may be NULL + + if skipelemB is NULL, + can skip any one element of setB + + design: + setup pointers + search for skipelemA, skipelemB, and mismatches + check results +*/ +int qh_setequal_except(setT *setA, void *skipelemA, setT *setB, void *skipelemB) { + void **elemA, **elemB; + int skip=0; + + elemA= SETaddr_(setA, void); + elemB= SETaddr_(setB, void); + while (1) { + if (*elemA == skipelemA) { + skip++; + elemA++; + } + if (skipelemB) { + if (*elemB == skipelemB) { + skip++; + elemB++; + } + }else if (*elemA != *elemB) { + skip++; + if (!(skipelemB= *elemB++)) + return 0; + } + if (!*elemA) + break; + if (*elemA++ != *elemB++) + return 0; + } + if (skip != 2 || *elemB) + return 0; + return 1; +} /* setequal_except */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setequal_skip">-</a> + + qh_setequal_skip( setA, skipA, setB, skipB ) + returns 1 if sorted setA and setB are equal except for elements skipA & B + + returns: + false if different size + + notes: + neither set may be NULL + + design: + setup pointers + search for mismatches while skipping skipA and skipB +*/ +int qh_setequal_skip(setT *setA, int skipA, setT *setB, int skipB) { + void **elemA, **elemB, **skipAp, **skipBp; + + elemA= SETaddr_(setA, void); + elemB= SETaddr_(setB, void); + skipAp= SETelemaddr_(setA, skipA, void); + skipBp= SETelemaddr_(setB, skipB, void); + while (1) { + if (elemA == skipAp) + elemA++; + if (elemB == skipBp) + elemB++; + if (!*elemA) + break; + if (*elemA++ != *elemB++) + return 0; + } + if (*elemB) + return 0; + return 1; +} /* setequal_skip */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setfree">-</a> + + qh_setfree(qh, setp ) + frees the space occupied by a sorted or unsorted set + + returns: + sets setp to NULL + + notes: + set may be NULL + + design: + free array + free set +*/ +void qh_setfree(qhT *qh, setT **setp) { + int size; + void **freelistp; /* used if !qh_NOmem by qh_memfree_() */ + + if (*setp) { + size= (int)sizeof(setT) + ((*setp)->maxsize)*SETelemsize; + if (size <= qh->qhmem.LASTsize) { + qh_memfree_(qh, *setp, size, freelistp); + }else + qh_memfree(qh, *setp, size); + *setp= NULL; + } +} /* setfree */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setfree2">-</a> + + qh_setfree2(qh, setp, elemsize ) + frees the space occupied by a set and its elements + + notes: + set may be NULL + + design: + free each element + free set +*/ +void qh_setfree2(qhT *qh, setT **setp, int elemsize) { + void *elem, **elemp; + + FOREACHelem_(*setp) + qh_memfree(qh, elem, elemsize); + qh_setfree(qh, setp); +} /* setfree2 */ + + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setfreelong">-</a> + + qh_setfreelong(qh, setp ) + frees a set only if it's in long memory + + returns: + sets setp to NULL if it is freed + + notes: + set may be NULL + + design: + if set is large + free it +*/ +void qh_setfreelong(qhT *qh, setT **setp) { + int size; + + if (*setp) { + size= (int)sizeof(setT) + ((*setp)->maxsize)*SETelemsize; + if (size > qh->qhmem.LASTsize) { + qh_memfree(qh, *setp, size); + *setp= NULL; + } + } +} /* setfreelong */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setin">-</a> + + qh_setin( set, setelem ) + returns 1 if setelem is in a set, 0 otherwise + + notes: + set may be NULL or unsorted + + design: + scans set for setelem +*/ +int qh_setin(setT *set, void *setelem) { + void *elem, **elemp; + + FOREACHelem_(set) { + if (elem == setelem) + return 1; + } + return 0; +} /* setin */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setindex">-</a> + + qh_setindex(set, atelem ) + returns the index of atelem in set. + returns -1, if not in set or maxsize wrong + + notes: + set may be NULL and may contain nulls. + NOerrors returned (qh_pointid, QhullPoint::id) + + design: + checks maxsize + scans set for atelem +*/ +int qh_setindex(setT *set, void *atelem) { + void **elem; + int size, i; + + if (!set) + return -1; + SETreturnsize_(set, size); + if (size > set->maxsize) + return -1; + elem= SETaddr_(set, void); + for (i=0; i < size; i++) { + if (*elem++ == atelem) + return i; + } + return -1; +} /* setindex */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setlarger">-</a> + + qh_setlarger(qh, oldsetp ) + returns a larger set that contains all elements of *oldsetp + + notes: + if long memory, + the new set is 2x larger + if qhmem.LASTsize is between 1.5x and 2x + the new set is qhmem.LASTsize + otherwise use quick memory, + the new set is 2x larger, rounded up to next qh_memsize + + if temp set, updates qh->qhmem.tempstack + + design: + creates a new set + copies the old set to the new set + updates pointers in tempstack + deletes the old set +*/ +void qh_setlarger(qhT *qh, setT **oldsetp) { + int setsize= 1, newsize; + setT *newset, *set, **setp, *oldset; + setelemT *sizep; + setelemT *newp, *oldp; + + if (*oldsetp) { + oldset= *oldsetp; + SETreturnsize_(oldset, setsize); + qh->qhmem.cntlarger++; + qh->qhmem.totlarger += setsize+1; + qh_setlarger_quick(qh, setsize, &newsize); + newset= qh_setnew(qh, newsize); + oldp= (setelemT *)SETaddr_(oldset, void); + newp= (setelemT *)SETaddr_(newset, void); + memcpy((char *)newp, (char *)oldp, (size_t)(setsize+1) * SETelemsize); + sizep= SETsizeaddr_(newset); + sizep->i= setsize+1; + FOREACHset_((setT *)qh->qhmem.tempstack) { + if (set == oldset) + *(setp-1)= newset; + } + qh_setfree(qh, oldsetp); + }else + newset= qh_setnew(qh, 3); + *oldsetp= newset; +} /* setlarger */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setlarger_quick">-</a> + + qh_setlarger_quick(qh, setsize, newsize ) + determine newsize for setsize + returns True if newsize fits in quick memory + + design: + if 2x fits into quick memory + return True, 2x + if x+4 does not fit into quick memory + return False, 2x + if x+x/3 fits into quick memory + return True, the last quick set + otherwise + return False, 2x +*/ +int qh_setlarger_quick(qhT *qh, int setsize, int *newsize) { + int lastquickset; + + *newsize= 2 * setsize; + lastquickset= (qh->qhmem.LASTsize - (int)sizeof(setT)) / SETelemsize; /* matches size computation in qh_setnew */ + if (*newsize <= lastquickset) + return 1; + if (setsize + 4 > lastquickset) + return 0; + if (setsize + setsize/3 <= lastquickset) { + *newsize= lastquickset; + return 1; + } + return 0; +} /* setlarger_quick */ + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setlast">-</a> + + qh_setlast( set ) + return last element of set or NULL (use type conversion) + + notes: + set may be NULL + + design: + return last element +*/ +void *qh_setlast(setT *set) { + int size; + + if (set) { + size= SETsizeaddr_(set)->i; + if (!size) + return SETelem_(set, set->maxsize - 1); + else if (size > 1) + return SETelem_(set, size - 2); + } + return NULL; +} /* setlast */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setnew">-</a> + + qh_setnew(qh, setsize ) + creates and allocates space for a set + + notes: + setsize means the number of elements (!including the NULL terminator) + use qh_settemp/qh_setfreetemp if set is temporary + + design: + allocate memory for set + roundup memory if small set + initialize as empty set +*/ +setT *qh_setnew(qhT *qh, int setsize) { + setT *set; + int sizereceived; /* used if !qh_NOmem */ + int size; + void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */ + + if (!setsize) + setsize++; + size= (int)sizeof(setT) + setsize * SETelemsize; /* setT includes NULL terminator, see qh.LASTquickset */ + if (size>0 && size <= qh->qhmem.LASTsize) { + qh_memalloc_(qh, size, freelistp, set, setT); +#ifndef qh_NOmem + sizereceived= qh->qhmem.sizetable[ qh->qhmem.indextable[size]]; + if (sizereceived > size) + setsize += (sizereceived - size)/SETelemsize; +#endif + }else + set= (setT *)qh_memalloc(qh, size); + set->maxsize= setsize; + set->e[setsize].i= 1; + set->e[0].p= NULL; + return(set); +} /* setnew */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setnew_delnthsorted">-</a> + + qh_setnew_delnthsorted(qh, set, size, nth, prepend ) + creates a sorted set not containing nth element + if prepend, the first prepend elements are undefined + + notes: + set must be defined + checks nth + see also: setdelnthsorted + + design: + create new set + setup pointers and allocate room for prepend'ed entries + append head of old set to new set + append tail of old set to new set +*/ +setT *qh_setnew_delnthsorted(qhT *qh, setT *set, int size, int nth, int prepend) { + setT *newset; + void **oldp, **newp; + int tailsize= size - nth -1, newsize; + + if (tailsize < 0) { + qh_fprintf(qh, qh->qhmem.ferr, 6176, "qhull internal error (qh_setnew_delnthsorted): nth %d is out-of-bounds for set:\n", nth); + qh_setprint(qh, qh->qhmem.ferr, "", set); + qh_errexit(qh, qhmem_ERRqhull, NULL, NULL); + } + newsize= size-1 + prepend; + newset= qh_setnew(qh, newsize); + newset->e[newset->maxsize].i= newsize+1; /* may be overwritten */ + oldp= SETaddr_(set, void); + newp= SETaddr_(newset, void) + prepend; + switch (nth) { + case 0: + break; + case 1: + *(newp++)= *oldp++; + break; + case 2: + *(newp++)= *oldp++; + *(newp++)= *oldp++; + break; + case 3: + *(newp++)= *oldp++; + *(newp++)= *oldp++; + *(newp++)= *oldp++; + break; + case 4: + *(newp++)= *oldp++; + *(newp++)= *oldp++; + *(newp++)= *oldp++; + *(newp++)= *oldp++; + break; + default: + memcpy((char *)newp, (char *)oldp, (size_t)nth * SETelemsize); + newp += nth; + oldp += nth; + break; + } + oldp++; + switch (tailsize) { + case 0: + break; + case 1: + *(newp++)= *oldp++; + break; + case 2: + *(newp++)= *oldp++; + *(newp++)= *oldp++; + break; + case 3: + *(newp++)= *oldp++; + *(newp++)= *oldp++; + *(newp++)= *oldp++; + break; + case 4: + *(newp++)= *oldp++; + *(newp++)= *oldp++; + *(newp++)= *oldp++; + *(newp++)= *oldp++; + break; + default: + memcpy((char *)newp, (char *)oldp, (size_t)tailsize * SETelemsize); + newp += tailsize; + } + *newp= NULL; + return(newset); +} /* setnew_delnthsorted */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setprint">-</a> + + qh_setprint(qh, fp, string, set ) + print set elements to fp with identifying string + + notes: + never errors +*/ +void qh_setprint(qhT *qh, FILE *fp, const char* string, setT *set) { + int size, k; + + if (!set) + qh_fprintf(qh, fp, 9346, "%s set is null\n", string); + else { + SETreturnsize_(set, size); + qh_fprintf(qh, fp, 9347, "%s set=%p maxsize=%d size=%d elems=", + string, set, set->maxsize, size); + if (size > set->maxsize) + size= set->maxsize+1; + for (k=0; k < size; k++) + qh_fprintf(qh, fp, 9348, " %p", set->e[k].p); + qh_fprintf(qh, fp, 9349, "\n"); + } +} /* setprint */ + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setreplace">-</a> + + qh_setreplace(qh, set, oldelem, newelem ) + replaces oldelem in set with newelem + + notes: + errors if oldelem not in the set + newelem may be NULL, but it turns the set into an indexed set (no FOREACH) + + design: + find oldelem + replace with newelem +*/ +void qh_setreplace(qhT *qh, setT *set, void *oldelem, void *newelem) { + void **elemp; + + elemp= SETaddr_(set, void); + while (*elemp != oldelem && *elemp) + elemp++; + if (*elemp) + *elemp= newelem; + else { + qh_fprintf(qh, qh->qhmem.ferr, 6177, "qhull internal error (qh_setreplace): elem %p not found in set\n", + oldelem); + qh_setprint(qh, qh->qhmem.ferr, "", set); + qh_errexit(qh, qhmem_ERRqhull, NULL, NULL); + } +} /* setreplace */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setsize">-</a> + + qh_setsize(qh, set ) + returns the size of a set + + notes: + errors if set's maxsize is incorrect + same as SETreturnsize_(set) + same code for qh_setsize [qset_r.c] and QhullSetBase::count + if first element is NULL, SETempty_() is True but qh_setsize may be greater than 0 + + design: + determine actual size of set from maxsize +*/ +int qh_setsize(qhT *qh, setT *set) { + int size; + setelemT *sizep; + + if (!set) + return(0); + sizep= SETsizeaddr_(set); + if ((size= sizep->i)) { + size--; + if (size > set->maxsize) { + qh_fprintf(qh, qh->qhmem.ferr, 6178, "qhull internal error (qh_setsize): current set size %d is greater than maximum size %d\n", + size, set->maxsize); + qh_setprint(qh, qh->qhmem.ferr, "set: ", set); + qh_errexit(qh, qhmem_ERRqhull, NULL, NULL); + } + }else + size= set->maxsize; + return size; +} /* setsize */ + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="settemp">-</a> + + qh_settemp(qh, setsize ) + return a stacked, temporary set of up to setsize elements + + notes: + use settempfree or settempfree_all to release from qh->qhmem.tempstack + see also qh_setnew + + design: + allocate set + append to qh->qhmem.tempstack + +*/ +setT *qh_settemp(qhT *qh, int setsize) { + setT *newset; + + newset= qh_setnew(qh, setsize); + qh_setappend(qh, &qh->qhmem.tempstack, newset); + if (qh->qhmem.IStracing >= 5) + qh_fprintf(qh, qh->qhmem.ferr, 8123, "qh_settemp: temp set %p of %d elements, depth %d\n", + newset, newset->maxsize, qh_setsize(qh, qh->qhmem.tempstack)); + return newset; +} /* settemp */ + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="settempfree">-</a> + + qh_settempfree(qh, set ) + free temporary set at top of qh->qhmem.tempstack + + notes: + nop if set is NULL + errors if set not from previous qh_settemp + + to locate errors: + use 'T2' to find source and then find mis-matching qh_settemp + + design: + check top of qh->qhmem.tempstack + free it +*/ +void qh_settempfree(qhT *qh, setT **set) { + setT *stackedset; + + if (!*set) + return; + stackedset= qh_settemppop(qh); + if (stackedset != *set) { + qh_settemppush(qh, stackedset); + qh_fprintf(qh, qh->qhmem.ferr, 6179, "qhull internal error (qh_settempfree): set %p(size %d) was not last temporary allocated(depth %d, set %p, size %d)\n", + *set, qh_setsize(qh, *set), qh_setsize(qh, qh->qhmem.tempstack)+1, + stackedset, qh_setsize(qh, stackedset)); + qh_errexit(qh, qhmem_ERRqhull, NULL, NULL); + } + qh_setfree(qh, set); +} /* settempfree */ + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="settempfree_all">-</a> + + qh_settempfree_all(qh) + free all temporary sets in qh->qhmem.tempstack + + design: + for each set in tempstack + free set + free qh->qhmem.tempstack +*/ +void qh_settempfree_all(qhT *qh) { + setT *set, **setp; + + FOREACHset_(qh->qhmem.tempstack) + qh_setfree(qh, &set); + qh_setfree(qh, &qh->qhmem.tempstack); +} /* settempfree_all */ + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="settemppop">-</a> + + qh_settemppop(qh) + pop and return temporary set from qh->qhmem.tempstack + + notes: + the returned set is permanent + + design: + pop and check top of qh->qhmem.tempstack +*/ +setT *qh_settemppop(qhT *qh) { + setT *stackedset; + + stackedset= (setT *)qh_setdellast(qh->qhmem.tempstack); + if (!stackedset) { + qh_fprintf(qh, qh->qhmem.ferr, 6180, "qhull internal error (qh_settemppop): pop from empty temporary stack\n"); + qh_errexit(qh, qhmem_ERRqhull, NULL, NULL); + } + if (qh->qhmem.IStracing >= 5) + qh_fprintf(qh, qh->qhmem.ferr, 8124, "qh_settemppop: depth %d temp set %p of %d elements\n", + qh_setsize(qh, qh->qhmem.tempstack)+1, stackedset, qh_setsize(qh, stackedset)); + return stackedset; +} /* settemppop */ + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="settemppush">-</a> + + qh_settemppush(qh, set ) + push temporary set unto qh->qhmem.tempstack (makes it temporary) + + notes: + duplicates settemp() for tracing + + design: + append set to tempstack +*/ +void qh_settemppush(qhT *qh, setT *set) { + if (!set) { + qh_fprintf(qh, qh->qhmem.ferr, 6267, "qhull error (qh_settemppush): can not push a NULL temp\n"); + qh_errexit(qh, qhmem_ERRqhull, NULL, NULL); + } + qh_setappend(qh, &qh->qhmem.tempstack, set); + if (qh->qhmem.IStracing >= 5) + qh_fprintf(qh, qh->qhmem.ferr, 8125, "qh_settemppush: depth %d temp set %p of %d elements\n", + qh_setsize(qh, qh->qhmem.tempstack), set, qh_setsize(qh, set)); +} /* settemppush */ + + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="settruncate">-</a> + + qh_settruncate(qh, set, size ) + truncate set to size elements + + notes: + set must be defined + + see: + SETtruncate_ + + design: + check size + update actual size of set +*/ +void qh_settruncate(qhT *qh, setT *set, int size) { + + if (size < 0 || size > set->maxsize) { + qh_fprintf(qh, qh->qhmem.ferr, 6181, "qhull internal error (qh_settruncate): size %d out of bounds for set:\n", size); + qh_setprint(qh, qh->qhmem.ferr, "", set); + qh_errexit(qh, qhmem_ERRqhull, NULL, NULL); + } + set->e[set->maxsize].i= size+1; /* maybe overwritten */ + set->e[size].p= NULL; +} /* settruncate */ + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setunique">-</a> + + qh_setunique(qh, set, elem ) + add elem to unsorted set unless it is already in set + + notes: + returns 1 if it is appended + + design: + if elem not in set + append elem to set +*/ +int qh_setunique(qhT *qh, setT **set, void *elem) { + + if (!qh_setin(*set, elem)) { + qh_setappend(qh, set, elem); + return 1; + } + return 0; +} /* setunique */ + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="setzero">-</a> + + qh_setzero(qh, set, index, size ) + zero elements from index on + set actual size of set to size + + notes: + set must be defined + the set becomes an indexed set (can not use FOREACH...) + + see also: + qh_settruncate + + design: + check index and size + update actual size + zero elements starting at e[index] +*/ +void qh_setzero(qhT *qh, setT *set, int idx, int size) { + int count; + + if (idx < 0 || idx >= size || size > set->maxsize) { + qh_fprintf(qh, qh->qhmem.ferr, 6182, "qhull internal error (qh_setzero): index %d or size %d out of bounds for set:\n", idx, size); + qh_setprint(qh, qh->qhmem.ferr, "", set); + qh_errexit(qh, qhmem_ERRqhull, NULL, NULL); + } + set->e[set->maxsize].i= size+1; /* may be overwritten */ + count= size - idx + 1; /* +1 for NULL terminator */ + memset((char *)SETelemaddr_(set, idx, void), 0, (size_t)count * SETelemsize); +} /* setzero */ + + diff --git a/contrib/libs/qhull/libqhull_r/qset_r.h b/contrib/libs/qhull/libqhull_r/qset_r.h new file mode 100644 index 0000000000..b41dac0084 --- /dev/null +++ b/contrib/libs/qhull/libqhull_r/qset_r.h @@ -0,0 +1,506 @@ +/*<html><pre> -<a href="qh-set_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + qset_r.h + header file for qset_r.c that implements set + + see qh-set_r.htm and qset_r.c + + only uses mem_r.c, malloc/free + + for error handling, writes message and calls + qh_errexit(qhT *qh, 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-2020 The Geometry Center. + $Id: //main/2019/qhull/src/libqhull_r/qset_r.h#4 $$Change: 2953 $ + $DateTime: 2020/05/21 22:05:32 $$Author: bbarber $ +*/ + +#ifndef qhDEFset +#define qhDEFset 1 + +#include <stdio.h> + +/*================= -structures- ===============*/ + +#ifndef DEFsetT +#define DEFsetT 1 +typedef struct setT setT; /* a set is a sorted or unsorted array of pointers */ +#endif + +#ifndef DEFqhT +#define DEFqhT 1 +typedef struct qhT qhT; /* defined in libqhull_r.h */ +#endif + +/* [jan'15] Decided not to use countT. Most sets are small. The code uses signed tests */ + +/*-<a href="qh-set_r.htm#TOC" +>----------------------------------------</a><a name="setT">-</a> + +setT + a set or list of pointers with maximum size and actual size. + +variations: + unsorted, unique -- a list of unique pointers with NULL terminator + user guarantees uniqueness + sorted -- a sorted list of unique pointers with NULL terminator + qset_r.c guarantees uniqueness + unsorted -- a list of pointers terminated with NULL + indexed -- an array of pointers with NULL elements + +structure for set of n elements: + + -------------- + | maxsize + -------------- + | e[0] - a pointer, may be NULL for indexed sets + -------------- + | e[1] + + -------------- + | ... + -------------- + | e[n-1] + -------------- + | e[n] = NULL + -------------- + | ... + -------------- + | e[maxsize] - n+1 or NULL (determines actual size of set) + -------------- + +*/ + +/*-- setelemT -- internal type to allow both pointers and indices +*/ +typedef union setelemT setelemT; +union setelemT { + void *p; + int i; /* integer used for e[maxSize] */ +}; + +struct setT { + int maxsize; /* maximum number of elements (except NULL) */ + setelemT e[1]; /* array of pointers, tail is NULL */ + /* last slot (unless NULL) is actual size+1 + e[maxsize]==NULL or e[e[maxsize]-1]==NULL */ + /* this may generate a warning since e[] contains + maxsize elements */ +}; + +/*=========== -constants- =========================*/ + +/*-<a href="qh-set_r.htm#TOC" + >-----------------------------------</a><a name="SETelemsize">-</a> + + SETelemsize + size of a set element in bytes +*/ +#define SETelemsize ((int)sizeof(setelemT)) + + +/*=========== -macros- =========================*/ + +/*-<a href="qh-set_r.htm#TOC" + >-----------------------------------</a><a name="FOREACHsetelement_">-</a> + + FOREACHsetelement_(type, set, variable) + define FOREACH iterator + + declare: + assumes *variable and **variablep are declared + no space in "variable)" [DEC Alpha cc compiler] + + each iteration: + variable is set element + variablep is one beyond variable. + + to repeat an element: + variablep--; / *repeat* / + + at exit: + variable is NULL at end of loop + + example: + #define FOREACHfacet_(facets) FOREACHsetelement_(facetT, facets, facet) + + notes: + use FOREACHsetelement_i_() if need index or include NULLs + assumes set is not modified + + WARNING: + nested loops can't use the same variable (define another FOREACH) + + needs braces if nested inside another FOREACH + this includes intervening blocks, e.g. FOREACH...{ if () FOREACH...} ) +*/ +#define FOREACHsetelement_(type, set, variable) \ + if (((variable= NULL), set)) for (\ + variable##p= (type **)&((set)->e[0].p); \ + (variable= *variable##p++);) + +/*-<a href="qh-set_r.htm#TOC" + >----------------------------------------</a><a name="FOREACHsetelement_i_">-</a> + + FOREACHsetelement_i_(qh, type, set, variable) + define indexed FOREACH iterator + + declare: + type *variable, variable_n, variable_i; + + each iteration: + variable is set element, may be NULL + variable_i is index, variable_n is qh_setsize() + + to repeat an element: + variable_i--; variable_n-- repeats for deleted element + + at exit: + variable==NULL and variable_i==variable_n + + example: + #define FOREACHfacet_i_(qh, facets) FOREACHsetelement_i_(qh, facetT, facets, facet) + + WARNING: + nested loops can't use the same variable (define another FOREACH) + + needs braces if nested inside another FOREACH + this includes intervening blocks, e.g. FOREACH...{ if () FOREACH...} ) +*/ +#define FOREACHsetelement_i_(qh, type, set, variable) \ + if (((variable= NULL), set)) for (\ + variable##_i= 0, variable= (type *)((set)->e[0].p), \ + variable##_n= qh_setsize(qh, set);\ + variable##_i < variable##_n;\ + variable= (type *)((set)->e[++variable##_i].p) ) + +/*-<a href="qh-set_r.htm#TOC" + >--------------------------------------</a><a name="FOREACHsetelementreverse_">-</a> + + FOREACHsetelementreverse_(qh, type, set, variable)- + define FOREACH iterator in reverse order + + declare: + assumes *variable and **variablep are declared + also declare 'int variabletemp' + + each iteration: + variable is set element + + to repeat an element: + variabletemp++; / *repeat* / + + at exit: + variable is NULL + + example: + #define FOREACHvertexreverse_(vertices) FOREACHsetelementreverse_(vertexT, vertices, vertex) + + notes: + use FOREACHsetelementreverse12_() to reverse first two elements + WARNING: needs braces if nested inside another FOREACH +*/ +#define FOREACHsetelementreverse_(qh, type, set, variable) \ + if (((variable= NULL), set)) for (\ + variable##temp= qh_setsize(qh, set)-1, variable= qh_setlast(qh, set);\ + variable; variable= \ + ((--variable##temp >= 0) ? SETelemt_(set, variable##temp, type) : NULL)) + +/*-<a href="qh-set_r.htm#TOC" + >-----------------------------------</a><a name="FOREACHsetelementreverse12_">-</a> + + FOREACHsetelementreverse12_(type, set, variable)- + define FOREACH iterator with e[1] and e[0] reversed + + declare: + assumes *variable and **variablep are declared + + each iteration: + variable is set element + variablep is one after variable. + + to repeat an element: + variablep--; / *repeat* / + + at exit: + variable is NULL at end of loop + + example + #define FOREACHvertexreverse12_(vertices) FOREACHsetelementreverse12_(vertexT, vertices, vertex) + + notes: + WARNING: needs braces if nested inside another FOREACH +*/ +#define FOREACHsetelementreverse12_(type, set, variable) \ + if (((variable= NULL), set)) for (\ + variable##p= (type **)&((set)->e[1].p); \ + (variable= *variable##p); \ + variable##p == ((type **)&((set)->e[0].p))?variable##p += 2: \ + (variable##p == ((type **)&((set)->e[1].p))?variable##p--:variable##p++)) + +/*-<a href="qh-set_r.htm#TOC" + >-----------------------------------</a><a name="FOREACHelem_">-</a> + + FOREACHelem_( set )- + iterate elements in a set + + declare: + void *elem, *elemp; + + each iteration: + elem is set element + elemp is one beyond + + to repeat an element: + elemp--; / *repeat* / + + at exit: + elem == NULL at end of loop + + example: + FOREACHelem_(set) { + + notes: + assumes set is not modified + WARNING: needs braces if nested inside another FOREACH +*/ +#define FOREACHelem_(set) FOREACHsetelement_(void, set, elem) + +/*-<a href="qh-set_r.htm#TOC" + >-----------------------------------</a><a name="FOREACHset_">-</a> + + FOREACHset_( set )- + iterate a set of sets + + declare: + setT *set, **setp; + + each iteration: + set is set element + setp is one beyond + + to repeat an element: + setp--; / *repeat* / + + at exit: + set == NULL at end of loop + + example + FOREACHset_(sets) { + + notes: + WARNING: needs braces if nested inside another FOREACH +*/ +#define FOREACHset_(sets) FOREACHsetelement_(setT, sets, set) + +/*-<a href="qh-set_r.htm#TOC" + >-----------------------------------------</a><a name="SETindex_">-</a> + + SETindex_( set, elem ) + return index of elem in set + + notes: + for use with FOREACH iteration + WARN64 -- Maximum set size is 2G + + example: + i= SETindex_(ridges, ridge) +*/ +#define SETindex_(set, elem) ((int)((void **)elem##p - (void **)&(set)->e[1].p)) + +/*-<a href="qh-set_r.htm#TOC" + >---------------------------------------</a><a name="SETref_">-</a> + + SETref_( elem ) + l.h.s. for modifying the current element in a FOREACH iteration + + example: + SETref_(ridge)= anotherridge; +*/ +#define SETref_(elem) (elem##p[-1]) + +/*-<a href="qh-set_r.htm#TOC" + >---------------------------------------</a><a name="SETelem_">-</a> + + SETelem_(set, n) + return the n'th element of set + + notes: + assumes that n is valid [0..size] and that set is defined + use SETelemt_() for type cast +*/ +#define SETelem_(set, n) ((set)->e[n].p) + +/*-<a href="qh-set_r.htm#TOC" + >---------------------------------------</a><a name="SETelemt_">-</a> + + SETelemt_(set, n, type) + return the n'th element of set as a type + + notes: + assumes that n is valid [0..size] and that set is defined +*/ +#define SETelemt_(set, n, type) ((type *)((set)->e[n].p)) + +/*-<a href="qh-set_r.htm#TOC" + >---------------------------------------</a><a name="SETelemaddr_">-</a> + + SETelemaddr_(set, n, type) + return address of the n'th element of a set + + notes: + assumes that n is valid [0..size] and set is defined +*/ +#define SETelemaddr_(set, n, type) ((type **)(&((set)->e[n].p))) + +/*-<a href="qh-set_r.htm#TOC" + >---------------------------------------</a><a name="SETfirst_">-</a> + + SETfirst_(set) + return first element of set + +*/ +#define SETfirst_(set) ((set)->e[0].p) + +/*-<a href="qh-set_r.htm#TOC" + >---------------------------------------</a><a name="SETfirstt_">-</a> + + SETfirstt_(set, type) + return first element of set as a type + +*/ +#define SETfirstt_(set, type) ((type *)((set)->e[0].p)) + +/*-<a href="qh-set_r.htm#TOC" + >---------------------------------------</a><a name="SETsecond_">-</a> + + SETsecond_(set) + return second element of set + +*/ +#define SETsecond_(set) ((set)->e[1].p) + +/*-<a href="qh-set_r.htm#TOC" + >---------------------------------------</a><a name="SETsecondt_">-</a> + + SETsecondt_(set, type) + return second element of set as a type +*/ +#define SETsecondt_(set, type) ((type *)((set)->e[1].p)) + +/*-<a href="qh-set_r.htm#TOC" + >---------------------------------------</a><a name="SETaddr_">-</a> + + SETaddr_(set, type) + return address of set's elements +*/ +#define SETaddr_(set,type) ((type **)(&((set)->e[0].p))) + +/*-<a href="qh-set_r.htm#TOC" + >---------------------------------------</a><a name="SETreturnsize_">-</a> + + SETreturnsize_(set, size) + return size of a set + + notes: + set must be defined + use qh_setsize(qhT *qh, set) unless speed is critical +*/ +#define SETreturnsize_(set, size) (((size)= ((set)->e[(set)->maxsize].i))?(--(size)):((size)= (set)->maxsize)) + +/*-<a href="qh-set_r.htm#TOC" + >---------------------------------------</a><a name="SETempty_">-</a> + + SETempty_(set) + return true(1) if set is empty (i.e., FOREACHsetelement_ is empty) + + notes: + set may be NULL + qh_setsize may be non-zero if first element is NULL +*/ +#define SETempty_(set) (!set || (SETfirst_(set) ? 0 : 1)) + +/*-<a href="qh-set_r.htm#TOC" + >-------------------------------<a name="SETsizeaddr_">-</a> + + SETsizeaddr_(set) + return pointer to 'actual size+1' of set (set CANNOT be NULL!!) + Its type is setelemT* for strict aliasing + All SETelemaddr_ must be cast to setelemT + + + notes: + *SETsizeaddr==NULL or e[*SETsizeaddr-1].p==NULL +*/ +#define SETsizeaddr_(set) (&((set)->e[(set)->maxsize])) + +/*-<a href="qh-set_r.htm#TOC" + >---------------------------------------</a><a name="SETtruncate_">-</a> + + SETtruncate_(set, size) + truncate set to size + + see: + qh_settruncate() + +*/ +#define SETtruncate_(set, size) {set->e[set->maxsize].i= size+1; /* maybe overwritten */ \ + set->e[size].p= NULL;} + +/*======= prototypes in alphabetical order ============*/ + +#ifdef __cplusplus +extern "C" { +#endif + +void qh_setaddsorted(qhT *qh, setT **setp, void *elem); +void qh_setaddnth(qhT *qh, setT **setp, int nth, void *newelem); +void qh_setappend(qhT *qh, setT **setp, void *elem); +void qh_setappend_set(qhT *qh, setT **setp, setT *setA); +void qh_setappend2ndlast(qhT *qh, setT **setp, void *elem); +void qh_setcheck(qhT *qh, setT *set, const char *tname, unsigned int id); +void qh_setcompact(qhT *qh, setT *set); +setT *qh_setcopy(qhT *qh, setT *set, int extra); +void *qh_setdel(setT *set, void *elem); +void *qh_setdellast(setT *set); +void *qh_setdelnth(qhT *qh, setT *set, int nth); +void *qh_setdelnthsorted(qhT *qh, setT *set, int nth); +void *qh_setdelsorted(setT *set, void *newelem); +setT *qh_setduplicate(qhT *qh, setT *set, int elemsize); +void **qh_setendpointer(setT *set); +int qh_setequal(setT *setA, setT *setB); +int qh_setequal_except(setT *setA, void *skipelemA, setT *setB, void *skipelemB); +int qh_setequal_skip(setT *setA, int skipA, setT *setB, int skipB); +void qh_setfree(qhT *qh, setT **set); +void qh_setfree2(qhT *qh, setT **setp, int elemsize); +void qh_setfreelong(qhT *qh, setT **set); +int qh_setin(setT *set, void *setelem); +int qh_setindex(setT *set, void *setelem); +void qh_setlarger(qhT *qh, setT **setp); +int qh_setlarger_quick(qhT *qh, int setsize, int *newsize); +void *qh_setlast(setT *set); +setT *qh_setnew(qhT *qh, int size); +setT *qh_setnew_delnthsorted(qhT *qh, setT *set, int size, int nth, int prepend); +void qh_setprint(qhT *qh, FILE *fp, const char* string, setT *set); +void qh_setreplace(qhT *qh, setT *set, void *oldelem, void *newelem); +int qh_setsize(qhT *qh, setT *set); +setT *qh_settemp(qhT *qh, int setsize); +void qh_settempfree(qhT *qh, setT **set); +void qh_settempfree_all(qhT *qh); +setT *qh_settemppop(qhT *qh); +void qh_settemppush(qhT *qh, setT *set); +void qh_settruncate(qhT *qh, setT *set, int size); +int qh_setunique(qhT *qh, setT **set, void *elem); +void qh_setzero(qhT *qh, setT *set, int idx, int size); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* qhDEFset */ diff --git a/contrib/libs/qhull/libqhull_r/random_r.c b/contrib/libs/qhull/libqhull_r/random_r.c new file mode 100644 index 0000000000..7eecd30e09 --- /dev/null +++ b/contrib/libs/qhull/libqhull_r/random_r.c @@ -0,0 +1,249 @@ +/*<html><pre> -<a href="index_r.htm#TOC" + >-------------------------------</a><a name="TOP">-</a> + + random_r.c and utilities + Park & Miller's minimimal standard random number generator + argc/argv conversion + + Used by rbox. Do not use 'qh' +*/ + +#include "libqhull_r.h" +#include "random_r.h" + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +#ifdef _MSC_VER /* Microsoft Visual C++ -- warning level 4 */ +#pragma warning( disable : 4706) /* assignment within conditional function */ +#pragma warning( disable : 4996) /* function was declared deprecated(strcpy, localtime, etc.) */ +#endif + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="argv_to_command">-</a> + + qh_argv_to_command( argc, argv, command, max_size ) + + build command from argc/argv + max_size is at least + + returns: + a space-delimited string of options (just as typed) + returns false if max_size is too short + + notes: + silently removes + makes option string easy to input and output + matches qh_argv_to_command_size + argc may be 0 +*/ +int qh_argv_to_command(int argc, char *argv[], char* command, int max_size) { + int i, remaining; + char *s; + *command= '\0'; /* max_size > 0 */ + + if (argc) { + if ((s= strrchr( argv[0], '\\')) /* get filename w/o .exe extension */ + || (s= strrchr( argv[0], '/'))) + s++; + else + s= argv[0]; + if ((int)strlen(s) < max_size) /* WARN64 */ + strcpy(command, s); + else + goto error_argv; + if ((s= strstr(command, ".EXE")) + || (s= strstr(command, ".exe"))) + *s= '\0'; + } + for (i=1; i < argc; i++) { + s= argv[i]; + remaining= max_size - (int)strlen(command) - (int)strlen(s) - 2; /* WARN64 */ + if (!*s || strchr(s, ' ')) { + char *t= command + strlen(command); + remaining -= 2; + if (remaining < 0) { + goto error_argv; + } + *t++= ' '; + *t++= '"'; + while (*s) { + if (*s == '"') { + if (--remaining < 0) + goto error_argv; + *t++= '\\'; + } + *t++= *s++; + } + *t++= '"'; + *t= '\0'; + }else if (remaining < 0) { + goto error_argv; + }else { + strcat(command, " "); + strcat(command, s); + } + } + return 1; + +error_argv: + return 0; +} /* argv_to_command */ + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="argv_to_command_size">-</a> + + qh_argv_to_command_size( argc, argv ) + + return size to allocate for qh_argv_to_command() + + notes: + only called from rbox with qh_errexit not enabled + caller should report error if returned size is less than 1 + argc may be 0 + actual size is usually shorter +*/ +int qh_argv_to_command_size(int argc, char *argv[]) { + int count= 1; /* null-terminator if argc==0 */ + int i; + char *s; + + for (i=0; i<argc; i++){ + count += (int)strlen(argv[i]) + 1; /* WARN64 */ + if (i>0 && strchr(argv[i], ' ')) { + count += 2; /* quote delimiters */ + for (s=argv[i]; *s; s++) { + if (*s == '"') { + count++; + } + } + } + } + return count; +} /* argv_to_command_size */ + +/*-<a href="qh-geom_r.htm#TOC" + >-------------------------------</a><a name="rand">-</a> + + qh_rand() + qh_srand(qh, seed ) + generate pseudo-random number between 1 and 2^31 -2 + + notes: + For qhull and rbox, called from qh_RANDOMint(),etc. [user_r.h] + + From Park & Miller's minimal standard random number generator + Communications of the ACM, 31:1192-1201, 1988. + Does not use 0 or 2^31 -1 + this is silently enforced by qh_srand() + Can make 'Rn' much faster by moving qh_rand to qh_distplane +*/ + +/* Global variables and constants */ + +#define qh_rand_a 16807 +#define qh_rand_m 2147483647 +#define qh_rand_q 127773 /* m div a */ +#define qh_rand_r 2836 /* m mod a */ + +int qh_rand(qhT *qh) { + int lo, hi, test; + int seed= qh->last_random; + + hi= seed / qh_rand_q; /* seed div q */ + lo= seed % qh_rand_q; /* seed mod q */ + test= qh_rand_a * lo - qh_rand_r * hi; + if (test > 0) + seed= test; + else + seed= test + qh_rand_m; + qh->last_random= seed; + /* seed= seed < qh_RANDOMmax/2 ? 0 : qh_RANDOMmax; for testing */ + /* seed= qh_RANDOMmax; for testing */ + return seed; +} /* rand */ + +void qh_srand(qhT *qh, int seed) { + if (seed < 1) + qh->last_random= 1; + else if (seed >= qh_rand_m) + qh->last_random= qh_rand_m - 1; + else + qh->last_random= seed; +} /* qh_srand */ + +/*-<a href="qh-geom_r.htm#TOC" +>-------------------------------</a><a name="randomfactor">-</a> + +qh_randomfactor(qh, scale, offset ) + return a random factor r * scale + offset + +notes: + qh.RANDOMa/b are defined in global_r.c + qh_RANDOMint requires 'qh' +*/ +realT qh_randomfactor(qhT *qh, realT scale, realT offset) { + realT randr; + + randr= qh_RANDOMint; + return randr * scale + offset; +} /* randomfactor */ + +/*-<a href="qh-geom_r.htm#TOC" +>-------------------------------</a><a name="randommatrix">-</a> + + qh_randommatrix(qh, buffer, dim, rows ) + generate a random dim X dim matrix in range [-1,1] + assumes buffer is [dim+1, dim] + + returns: + sets buffer to random numbers + sets rows to rows of buffer + sets row[dim] as scratch row + + notes: + qh_RANDOMint requires 'qh' +*/ +void qh_randommatrix(qhT *qh, realT *buffer, int dim, realT **rows) { + int i, k; + realT **rowi, *coord, realr; + + coord= buffer; + rowi= rows; + for (i=0; i < dim; i++) { + *(rowi++)= coord; + for (k=0; k < dim; k++) { + realr= qh_RANDOMint; + *(coord++)= 2.0 * realr/(qh_RANDOMmax+1) - 1.0; + } + } + *rowi= coord; +} /* randommatrix */ + +/*-<a href="qh-globa_r.htm#TOC" + >-------------------------------</a><a name="strtol">-</a> + + qh_strtol( s, endp) qh_strtod( s, endp) + internal versions of strtol() and strtod() + does not skip trailing spaces + notes: + some implementations of strtol()/strtod() skip trailing spaces +*/ +double qh_strtod(const char *s, char **endp) { + double result; + + result= strtod(s, endp); + if (s < (*endp) && (*endp)[-1] == ' ') + (*endp)--; + return result; +} /* strtod */ + +int qh_strtol(const char *s, char **endp) { + int result; + + result= (int) strtol(s, endp, 10); /* WARN64 */ + if (s< (*endp) && (*endp)[-1] == ' ') + (*endp)--; + return result; +} /* strtol */ diff --git a/contrib/libs/qhull/libqhull_r/random_r.h b/contrib/libs/qhull/libqhull_r/random_r.h new file mode 100644 index 0000000000..a17549d3b9 --- /dev/null +++ b/contrib/libs/qhull/libqhull_r/random_r.h @@ -0,0 +1,41 @@ +/*<html><pre> -<a href="qh-geom_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + random_r.h + header file for random and utility routines + + see qh-geom_r.htm and random_r.c + + Copyright (c) 1993-2020 The Geometry Center. + $Id: //main/2019/qhull/src/libqhull_r/random_r.h#3 $$Change: 2953 $ + $DateTime: 2020/05/21 22:05:32 $$Author: bbarber $ +*/ + +#ifndef qhDEFrandom +#define qhDEFrandom 1 + +#include "libqhull_r.h" + +/*============= prototypes in alphabetical order ======= */ + +#ifdef __cplusplus +extern "C" { +#endif + +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(qhT *qh); +void qh_srand(qhT *qh, int seed); +realT qh_randomfactor(qhT *qh, realT scale, realT offset); +void qh_randommatrix(qhT *qh, realT *buffer, int dim, realT **row); +int qh_strtol(const char *s, char **endp); +double qh_strtod(const char *s, char **endp); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* qhDEFrandom */ + + + diff --git a/contrib/libs/qhull/libqhull_r/rboxlib_r.c b/contrib/libs/qhull/libqhull_r/rboxlib_r.c new file mode 100644 index 0000000000..66ceda5d64 --- /dev/null +++ b/contrib/libs/qhull/libqhull_r/rboxlib_r.c @@ -0,0 +1,854 @@ +/*<html><pre> -<a href="index_r.htm#TOC" + >-------------------------------</a><a name="TOP">-</a> + + rboxlib_r.c + Generate input points + + notes: + For documentation, see prompt[] of rbox_r.c + 50 points generated for 'rbox D4' + + WARNING: + incorrect range if qh_RANDOMmax is defined wrong (user_r.h) +*/ + +#include "libqhull_r.h" /* First for user_r.h */ +#include "random_r.h" + +#include <ctype.h> +#include <limits.h> +#include <math.h> +#include <setjmp.h> +#include <string.h> +#include <time.h> +#include <stdio.h> +#include <stdlib.h> + +#ifdef _MSC_VER /* Microsoft Visual C++ */ +#pragma warning( disable : 4706) /* assignment within conditional expression. */ +#pragma warning( disable : 4996) /* this function (strncat,sprintf,strcpy) or variable may be unsafe. */ +#endif + +#define MAXdim 200 +#define PI 3.1415926535897932384 + +/* ------------------------------ prototypes ----------------*/ +int qh_roundi(qhT *qh, double a); +void qh_out1(qhT *qh, double a); +void qh_out2n(qhT *qh, double a, double b); +void qh_out3n(qhT *qh, double a, double b, double c); +void qh_outcoord(qhT *qh, int iscdd, double *coord, int dim); +void qh_outcoincident(qhT *qh, int coincidentpoints, double radius, int iscdd, double *coord, int dim); +void qh_rboxpoints2(qhT *qh, char* rbox_command, double **simplex); + +void qh_fprintf_rbox(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... ); +void qh_free(void *mem); +void *qh_malloc(size_t size); +int qh_rand(qhT *qh); +void qh_srand(qhT *qh, int seed); + +/*-<a href="qh-qhull_r.htm#TOC" + >-------------------------------</a><a name="rboxpoints">-</a> + + qh_rboxpoints(qh, rbox_command ) + Generate points to qh.fout according to rbox options + Report errors on qh.ferr + + returns: + 0 (qh_ERRnone) on success + 1 (qh_ERRinput) on input error + 4 (qh_ERRmem) on memory error + 5 (qh_ERRqhull) on internal error + + notes: + To avoid using stdio, redefine qh_malloc, qh_free, and qh_fprintf_rbox (user_r.c) + Split out qh_rboxpoints2() to avoid -Wclobbered + + design: + Straight line code (consider defining a struct and functions): + + Parse arguments into variables + Determine the number of points + Generate the points +*/ +int qh_rboxpoints(qhT *qh, char* rbox_command) { + int exitcode; + double *simplex; + + simplex= NULL; + exitcode= setjmp(qh->rbox_errexit); + if (exitcode) { + /* same code for error exit and normal return. qh.NOerrexit is set */ + if (simplex) + qh_free(simplex); + return exitcode; + } + qh_rboxpoints2(qh, rbox_command, &simplex); + /* same code for error exit and normal return */ + if (simplex) + qh_free(simplex); + return qh_ERRnone; +} /* rboxpoints */ + +void qh_rboxpoints2(qhT *qh, char* rbox_command, double **simplex) { + int i,j,k; + int gendim; + int coincidentcount=0, coincidenttotal=0, coincidentpoints=0; + int cubesize, diamondsize, seed=0, count, apex; + int dim=3, numpoints=0, totpoints, addpoints=0; + int issphere=0, isaxis=0, iscdd=0, islens=0, isregular=0, iswidth=0, addcube=0; + int isgap=0, isspiral=0, NOcommand=0, adddiamond=0; + int israndom=0, istime=0; + int isbox=0, issimplex=0, issimplex2=0, ismesh=0; + double width=0.0, gap=0.0, radius=0.0, coincidentradius=0.0; + double coord[MAXdim], offset, meshm=3.0, meshn=4.0, meshr=5.0; + double *coordp, *simplexp; + int nthroot, mult[MAXdim]; + double norm, factor, randr, rangap, tempr, lensangle=0, lensbase=1; + double anglediff, angle, x, y, cube=0.0, diamond=0.0; + double box= qh_DEFAULTbox; /* scale all numbers before output */ + double randmax= qh_RANDOMmax; + char command[250], seedbuf[50]; + char *s=command, *t, *first_point=NULL; + time_t timedata; + + *command= '\0'; + strncat(command, rbox_command, sizeof(command)-sizeof(seedbuf)-strlen(command)-1); + + while (*s && !isspace(*s)) /* skip program name */ + s++; + while (*s) { + while (*s && isspace(*s)) + s++; + if (*s == '-') + s++; + if (!*s) + break; + if (isdigit(*s)) { + numpoints= qh_strtol(s, &s); + continue; + } + /* ============= read flags =============== */ + switch (*s++) { + case 'c': + addcube= 1; + t= s; + while (isspace(*t)) + t++; + if (*t == 'G') + cube= qh_strtod(++t, &s); + break; + case 'd': + adddiamond= 1; + t= s; + while (isspace(*t)) + t++; + if (*t == 'G') + diamond= qh_strtod(++t, &s); + break; + case 'h': + iscdd= 1; + break; + case 'l': + isspiral= 1; + break; + case 'n': + NOcommand= 1; + break; + case 'r': + isregular= 1; + break; + case 's': + issphere= 1; + break; + case 't': + istime= 1; + if (isdigit(*s)) { + seed= qh_strtol(s, &s); + israndom= 0; + }else + israndom= 1; + break; + case 'x': + issimplex= 1; + break; + case 'y': + issimplex2= 1; + break; + case 'z': + qh->rbox_isinteger= 1; + break; + case 'B': + box= qh_strtod(s, &s); + isbox= 1; + break; + case 'C': + if (*s) + coincidentpoints= qh_strtol(s, &s); + if (*s == ',') { + ++s; + coincidentradius= qh_strtod(s, &s); + } + if (*s == ',') { + ++s; + coincidenttotal= qh_strtol(s, &s); + } + if (*s && !isspace(*s)) { + qh_fprintf_rbox(qh, qh->ferr, 7080, "rbox error: arguments for 'Cn,r,m' are not 'int', 'float', and 'int'. Remaining string is '%s'\n", s); + qh_errexit_rbox(qh, qh_ERRinput); + } + if (coincidentpoints==0){ + qh_fprintf_rbox(qh, qh->ferr, 6268, "rbox error: missing arguments for 'Cn,r,m' where n is the number of coincident points, r is the radius (default 0.0), and m is the number of points\n"); + qh_errexit_rbox(qh, qh_ERRinput); + } + if (coincidentpoints<0 || coincidenttotal<0 || coincidentradius<0.0){ + qh_fprintf_rbox(qh, qh->ferr, 6269, "rbox error: negative arguments for 'Cn,m,r' where n (%d) is the number of coincident points, m (%d) is the number of points, and r (%.2g) is the radius (default 0.0)\n", coincidentpoints, coincidenttotal, coincidentradius); + qh_errexit_rbox(qh, qh_ERRinput); + } + break; + case 'D': + dim= qh_strtol(s, &s); + if (dim < 1 + || dim > MAXdim) { + qh_fprintf_rbox(qh, qh->ferr, 6189, "rbox error: dimension, D%d, out of bounds (>=%d or <=0)\n", dim, MAXdim); + qh_errexit_rbox(qh, qh_ERRinput); + } + break; + case 'G': + if (isdigit(*s)) + gap= qh_strtod(s, &s); + else + gap= 0.5; + isgap= 1; + break; + case 'L': + if (isdigit(*s)) + radius= qh_strtod(s, &s); + else + radius= 10; + islens= 1; + break; + case 'M': + ismesh= 1; + if (*s) + meshn= qh_strtod(s, &s); + if (*s == ',') { + ++s; + meshm= qh_strtod(s, &s); + }else + meshm= 0.0; + if (*s == ',') { + ++s; + meshr= qh_strtod(s, &s); + }else + meshr= sqrt(meshn*meshn + meshm*meshm); + if (*s && !isspace(*s)) { + qh_fprintf_rbox(qh, qh->ferr, 7069, "rbox warning: assuming 'M3,4,5' since mesh args are not integers or reals\n"); + meshn= 3.0, meshm=4.0, meshr=5.0; + } + break; + case 'O': + qh->rbox_out_offset= qh_strtod(s, &s); + break; + case 'P': + if (!first_point) + first_point= s - 1; + addpoints++; + while (*s && !isspace(*s)) /* read points later */ + s++; + break; + case 'W': + width= qh_strtod(s, &s); + iswidth= 1; + break; + case 'Z': + if (isdigit(*s)) + radius= qh_strtod(s, &s); + else + radius= 1.0; + isaxis= 1; + break; + default: + qh_fprintf_rbox(qh, qh->ferr, 6352, "rbox error: unknown flag at '%s'.\nExecute 'rbox' without arguments for documentation.\n", s - 1); + qh_errexit_rbox(qh, qh_ERRinput); + } + if (*s && !isspace(*s)) { + qh_fprintf_rbox(qh, qh->ferr, 6353, "rbox error: missing space between flags at %s.\n", s); + qh_errexit_rbox(qh, qh_ERRinput); + } + } + + /* ============= defaults, constants, and sizes =============== */ + if (qh->rbox_isinteger && !isbox) + box= qh_DEFAULTzbox; + if (addcube) { + tempr= floor(ldexp(1.0,dim)+0.5); + cubesize= (int)tempr; + if (cube == 0.0) + cube= box; + }else + cubesize= 0; + if (adddiamond) { + diamondsize= 2*dim; + if (diamond == 0.0) + diamond= box; + }else + diamondsize= 0; + if (islens) { + if (isaxis) { + qh_fprintf_rbox(qh, qh->ferr, 6190, "rbox error: can not combine 'Ln' with 'Zn'\n"); + qh_errexit_rbox(qh, qh_ERRinput); + } + if (radius <= 1.0) { + qh_fprintf_rbox(qh, qh->ferr, 6191, "rbox error: lens radius %.2g should be greater than 1.0\n", + radius); + qh_errexit_rbox(qh, qh_ERRinput); + } + lensangle= asin(1.0/radius); + lensbase= radius * cos(lensangle); + } + + if (!numpoints) { + if (issimplex2) + ; /* ok */ + else if (isregular + issimplex + islens + issphere + isaxis + isspiral + iswidth + ismesh) { + qh_fprintf_rbox(qh, qh->ferr, 6192, "rbox error: missing count\n"); + qh_errexit_rbox(qh, qh_ERRinput); + }else if (adddiamond + addcube + addpoints) + ; /* ok */ + else { + numpoints= 50; /* ./rbox D4 is the test case */ + issphere= 1; + } + } + if ((issimplex + islens + isspiral + ismesh > 1) + || (issimplex + issphere + isspiral + ismesh > 1)) { + qh_fprintf_rbox(qh, qh->ferr, 6193, "rbox error: can only specify one of 'l', 's', 'x', 'Ln', or 'Mn,m,r' ('Ln s' is ok).\n"); + qh_errexit_rbox(qh, qh_ERRinput); + } + if (coincidentpoints>0 && (numpoints == 0 || coincidenttotal > numpoints)) { + qh_fprintf_rbox(qh, qh->ferr, 6270, "rbox error: 'Cn,r,m' requested n coincident points for each of m points. Either there is no points or m (%d) is greater than the number of points (%d).\n", coincidenttotal, numpoints); + qh_errexit_rbox(qh, qh_ERRinput); + } + if (coincidentpoints > 0 && isregular) { + qh_fprintf_rbox(qh, qh->ferr, 6423, "rbox error: 'Cn,r,m' is not implemented for regular points ('r')\n"); + qh_errexit_rbox(qh, qh_ERRinput); + } + + if (coincidenttotal == 0) + coincidenttotal= numpoints; + + /* ============= print header with total points =============== */ + if (issimplex || ismesh) + totpoints= numpoints; + else if (issimplex2) + totpoints= numpoints+dim+1; + else if (isregular) { + totpoints= numpoints; + if (dim == 2) { + if (islens) + totpoints += numpoints - 2; + }else if (dim == 3) { + if (islens) + totpoints += 2 * numpoints; + else if (isgap) + totpoints += 1 + numpoints; + else + totpoints += 2; + } + }else + totpoints= numpoints + isaxis; + totpoints += cubesize + diamondsize + addpoints; + totpoints += coincidentpoints*coincidenttotal; + + /* ============= seed randoms =============== */ + if (istime == 0) { + for (s=command; *s; s++) { + if (issimplex2 && *s == 'y') /* make 'y' same seed as 'x' */ + i= 'x'; + else + i= *s; + seed= 11*seed + i; + } + }else if (israndom) { + seed= (int)time(&timedata); + sprintf(seedbuf, " t%d", seed); /* appends an extra t, not worth removing */ + strncat(command, seedbuf, sizeof(command) - strlen(command) - 1); + t= strstr(command, " t "); + if (t) + strcpy(t+1, t+3); /* remove " t " */ + } /* else, seed explicitly set to n */ + qh_RANDOMseed_(qh, seed); + + /* ============= print header =============== */ + + if (iscdd) + qh_fprintf_rbox(qh, qh->fout, 9391, "%s\nbegin\n %d %d %s\n", + NOcommand ? "" : command, + totpoints, dim+1, + qh->rbox_isinteger ? "integer" : "real"); + else if (NOcommand) + qh_fprintf_rbox(qh, qh->fout, 9392, "%d\n%d\n", dim, totpoints); + else + /* qh_fprintf_rbox special cases 9393 to append 'command' to the RboxPoints.comment() */ + qh_fprintf_rbox(qh, qh->fout, 9393, "%d %s\n%d\n", dim, command, totpoints); + + /* ============= explicit points =============== */ + if ((s= first_point)) { + while (s && *s) { /* 'P' */ + count= 0; + if (iscdd) + qh_out1(qh, 1.0); + while (*++s) { + qh_out1(qh, qh_strtod(s, &s)); + count++; + if (isspace(*s) || !*s) + break; + if (*s != ',') { + qh_fprintf_rbox(qh, qh->ferr, 6194, "rbox error: missing comma after coordinate in %s\n\n", s); + qh_errexit_rbox(qh, qh_ERRinput); + } + } + if (count < dim) { + for (k=dim-count; k--; ) + qh_out1(qh, 0.0); + }else if (count > dim) { + qh_fprintf_rbox(qh, qh->ferr, 6195, "rbox error: %d coordinates instead of %d coordinates in %s\n\n", + count, dim, s); + qh_errexit_rbox(qh, qh_ERRinput); + } + qh_fprintf_rbox(qh, qh->fout, 9394, "\n"); + while ((s= strchr(s, 'P'))) { + if (isspace(s[-1])) + break; + } + } + } + + /* ============= simplex distribution =============== */ + if (issimplex+issimplex2) { + if (!(*simplex= (double *)qh_malloc( (size_t)(dim * (dim+1)) * sizeof(double)))) { + qh_fprintf_rbox(qh, qh->ferr, 6196, "rbox error: insufficient memory for simplex\n"); + qh_errexit_rbox(qh, qh_ERRmem); /* qh_ERRmem */ + } + simplexp= *simplex; + if (isregular) { + for (i=0; i<dim; i++) { + for (k=0; k<dim; k++) + *(simplexp++)= i==k ? 1.0 : 0.0; + } + for (k=0; k<dim; k++) + *(simplexp++)= -1.0; + }else { + for (i=0; i<dim+1; i++) { + for (k=0; k<dim; k++) { + randr= qh_RANDOMint; + *(simplexp++)= 2.0 * randr/randmax - 1.0; + } + } + } + if (issimplex2) { + simplexp= *simplex; + for (i=0; i<dim+1; i++) { + if (iscdd) + qh_out1(qh, 1.0); + for (k=0; k<dim; k++) + qh_out1(qh, *(simplexp++) * box); + qh_fprintf_rbox(qh, qh->fout, 9395, "\n"); + } + } + for (j=0; j<numpoints; j++) { + if (iswidth) + apex= qh_RANDOMint % (dim+1); + else + apex= -1; + for (k=0; k<dim; k++) + coord[k]= 0.0; + norm= 0.0; + for (i=0; i<dim+1; i++) { + randr= qh_RANDOMint; + factor= randr/randmax; + if (i == apex) + factor *= width; + norm += factor; + for (k=0; k<dim; k++) { + simplexp= *simplex + i*dim + k; + coord[k] += factor * (*simplexp); + } + } + for (k=0; k<dim; k++) + coord[k] *= box/norm; + qh_outcoord(qh, iscdd, coord, dim); + if(coincidentcount++ < coincidenttotal) + qh_outcoincident(qh, coincidentpoints, coincidentradius, iscdd, coord, dim); + } + isregular= 0; /* continue with isbox */ + numpoints= 0; + } + + /* ============= mesh distribution =============== */ + if (ismesh) { + nthroot= (int)(pow((double)numpoints, 1.0/dim) + 0.99999); + for (k=dim; k--; ) + mult[k]= 0; + for (i=0; i < numpoints; i++) { + coordp= coord; + for (k=0; k < dim; k++) { + if (k == 0) + *(coordp++)= mult[0] * meshn + mult[1] * (-meshm); + else if (k == 1) + *(coordp++)= mult[0] * meshm + mult[1] * meshn; + else + *(coordp++)= mult[k] * meshr; + } + qh_outcoord(qh, iscdd, coord, dim); + if(coincidentcount++ < coincidenttotal) + qh_outcoincident(qh, coincidentpoints, coincidentradius, iscdd, coord, dim); + for (k=0; k < dim; k++) { + if (++mult[k] < nthroot) + break; + mult[k]= 0; + } + } + } + /* ============= regular points for 's' =============== */ + else if (isregular && !islens) { + if (dim != 2 && dim != 3) { + qh_fprintf_rbox(qh, qh->ferr, 6197, "rbox error: regular points can be used only in 2-d and 3-d\n\n"); + qh_errexit_rbox(qh, qh_ERRinput); + } + if (!isaxis || radius == 0.0) { + isaxis= 1; + radius= 1.0; + } + if (dim == 3) { + if (iscdd) + qh_out1(qh, 1.0); + qh_out3n(qh, 0.0, 0.0, -box); + if (!isgap) { + if (iscdd) + qh_out1(qh, 1.0); + qh_out3n(qh, 0.0, 0.0, box); + } + } + angle= 0.0; + anglediff= 2.0 * PI/numpoints; + for (i=0; i < numpoints; i++) { + angle += anglediff; + x= radius * cos(angle); + y= radius * sin(angle); + if (dim == 2) { + if (iscdd) + qh_out1(qh, 1.0); + qh_out2n(qh, x*box, y*box); + }else { + norm= sqrt(1.0 + x*x + y*y); + if (iscdd) + qh_out1(qh, 1.0); + qh_out3n(qh, box*x/norm, box*y/norm, box/norm); + if (isgap) { + x *= 1-gap; + y *= 1-gap; + norm= sqrt(1.0 + x*x + y*y); + if (iscdd) + qh_out1(qh, 1.0); + qh_out3n(qh, box*x/norm, box*y/norm, box/norm); + } + } + } + } + /* ============= regular points for 'r Ln D2' =============== */ + else if (isregular && islens && dim == 2) { + double cos_0; + + angle= lensangle; + anglediff= 2 * lensangle/(numpoints - 1); + cos_0= cos(lensangle); + for (i=0; i < numpoints; i++, angle -= anglediff) { + x= radius * sin(angle); + y= radius * (cos(angle) - cos_0); + if (iscdd) + qh_out1(qh, 1.0); + qh_out2n(qh, x*box, y*box); + if (i != 0 && i != numpoints - 1) { + if (iscdd) + qh_out1(qh, 1.0); + qh_out2n(qh, x*box, -y*box); + } + } + } + /* ============= regular points for 'r Ln D3' =============== */ + else if (isregular && islens && dim != 2) { + if (dim != 3) { + qh_fprintf_rbox(qh, qh->ferr, 6198, "rbox error: regular points can be used only in 2-d and 3-d\n\n"); + qh_errexit_rbox(qh, qh_ERRinput); + } + angle= 0.0; + anglediff= 2* PI/numpoints; + if (!isgap) { + isgap= 1; + gap= 0.5; + } + offset= sqrt(radius * radius - (1-gap)*(1-gap)) - lensbase; + for (i=0; i < numpoints; i++, angle += anglediff) { + x= cos(angle); + y= sin(angle); + if (iscdd) + qh_out1(qh, 1.0); + qh_out3n(qh, box*x, box*y, 0.0); + x *= 1-gap; + y *= 1-gap; + if (iscdd) + qh_out1(qh, 1.0); + qh_out3n(qh, box*x, box*y, box * offset); + if (iscdd) + qh_out1(qh, 1.0); + qh_out3n(qh, box*x, box*y, -box * offset); + } + } + /* ============= apex of 'Zn' distribution + gendim =============== */ + else { + if (isaxis) { + gendim= dim-1; + if (iscdd) + qh_out1(qh, 1.0); + for (j=0; j < gendim; j++) + qh_out1(qh, 0.0); + qh_out1(qh, -box); + qh_fprintf_rbox(qh, qh->fout, 9398, "\n"); + }else if (islens) + gendim= dim-1; + else + gendim= dim; + /* ============= generate random point in unit cube =============== */ + for (i=0; i < numpoints; i++) { + norm= 0.0; + for (j=0; j < gendim; j++) { + randr= qh_RANDOMint; + coord[j]= 2.0 * randr/randmax - 1.0; + norm += coord[j] * coord[j]; + } + norm= sqrt(norm); + /* ============= dim-1 point of 'Zn' distribution ========== */ + if (isaxis) { + if (!isgap) { + isgap= 1; + gap= 1.0; + } + randr= qh_RANDOMint; + rangap= 1.0 - gap * randr/randmax; + factor= radius * rangap / norm; + for (j=0; j<gendim; j++) + coord[j]= factor * coord[j]; + /* ============= dim-1 point of 'Ln s' distribution =========== */ + }else if (islens && issphere) { + if (!isgap) { + isgap= 1; + gap= 1.0; + } + randr= qh_RANDOMint; + rangap= 1.0 - gap * randr/randmax; + factor= rangap / norm; + for (j=0; j<gendim; j++) + coord[j]= factor * coord[j]; + /* ============= dim-1 point of 'Ln' distribution ========== */ + }else if (islens && !issphere) { + if (!isgap) { + isgap= 1; + gap= 1.0; + } + j= qh_RANDOMint % gendim; + if (coord[j] < 0) + coord[j]= -1.0 - coord[j] * gap; + else + coord[j]= 1.0 - coord[j] * gap; + /* ============= point of 'l' distribution =============== */ + }else if (isspiral) { + if (dim != 3) { + qh_fprintf_rbox(qh, qh->ferr, 6199, "rbox error: spiral distribution is available only in 3d\n\n"); + qh_errexit_rbox(qh, qh_ERRinput); + } + coord[0]= cos(2*PI*i/(numpoints - 1)); + coord[1]= sin(2*PI*i/(numpoints - 1)); + coord[2]= 2.0*(double)i/(double)(numpoints - 1) - 1.0; + /* ============= point of 's' distribution =============== */ + }else if (issphere) { + factor= 1.0/norm; + if (iswidth) { + randr= qh_RANDOMint; + factor *= 1.0 - width * randr/randmax; + } + for (j=0; j<dim; j++) + coord[j]= factor * coord[j]; + } + /* ============= project 'Zn s' point in to sphere =============== */ + if (isaxis && issphere) { + coord[dim-1]= 1.0; + norm= 1.0; + for (j=0; j<gendim; j++) + norm += coord[j] * coord[j]; + norm= sqrt(norm); + for (j=0; j<dim; j++) + coord[j]= coord[j] / norm; + if (iswidth) { + randr= qh_RANDOMint; + coord[dim-1] *= 1 - width * randr/randmax; + } + /* ============= project 'Zn' point onto cube =============== */ + }else if (isaxis && !issphere) { /* not very interesting */ + randr= qh_RANDOMint; + coord[dim-1]= 2.0 * randr/randmax - 1.0; + /* ============= project 'Ln' point out to sphere =============== */ + }else if (islens) { + coord[dim-1]= lensbase; + for (j=0, norm= 0; j<dim; j++) + norm += coord[j] * coord[j]; + norm= sqrt(norm); + for (j=0; j<dim; j++) + coord[j]= coord[j] * radius/ norm; + coord[dim-1] -= lensbase; + if (iswidth) { + randr= qh_RANDOMint; + coord[dim-1] *= 1 - width * randr/randmax; + } + if (qh_RANDOMint > randmax/2) + coord[dim-1]= -coord[dim-1]; + /* ============= project 'Wn' point toward boundary =============== */ + }else if (iswidth && !issphere) { + j= qh_RANDOMint % gendim; + if (coord[j] < 0) + coord[j]= -1.0 - coord[j] * width; + else + coord[j]= 1.0 - coord[j] * width; + } + /* ============= scale point to box =============== */ + for (k=0; k<dim; k++) + coord[k]= coord[k] * box; + + /* ============= write output =============== */ + qh_outcoord(qh, iscdd, coord, dim); + if(coincidentcount++ < coincidenttotal) + qh_outcoincident(qh, coincidentpoints, coincidentradius, iscdd, coord, dim); + } + } + + /* ============= write cube vertices =============== */ + if (addcube) { + for (j=0; j<cubesize; j++) { + if (iscdd) + qh_out1(qh, 1.0); + for (k=dim-1; k>=0; k--) { + if (j & ( 1 << k)) + qh_out1(qh, cube); + else + qh_out1(qh, -cube); + } + qh_fprintf_rbox(qh, qh->fout, 9400, "\n"); + } + } + + /* ============= write diamond vertices =============== */ + if (adddiamond) { + for (j=0; j<diamondsize; j++) { + if (iscdd) + qh_out1(qh, 1.0); + for (k=dim-1; k>=0; k--) { + if (j/2 != k) + qh_out1(qh, 0.0); + else if (j & 0x1) + qh_out1(qh, diamond); + else + qh_out1(qh, -diamond); + } + qh_fprintf_rbox(qh, qh->fout, 9401, "\n"); + } + } + + if (iscdd) + qh_fprintf_rbox(qh, qh->fout, 9402, "end\nhull\n"); +} /* rboxpoints2 */ + +/*------------------------------------------------ +outxxx - output functions for qh_rboxpoints +*/ +int qh_roundi(qhT *qh, double a) { + if (a < 0.0) { + if (a - 0.5 < INT_MIN) { + qh_fprintf_rbox(qh, qh->ferr, 6200, "rbox input error: negative coordinate %2.2g is too large. Reduce 'Bn'\n", a); + qh_errexit_rbox(qh, qh_ERRinput); + } + return (int)(a - 0.5); + }else { + if (a + 0.5 > INT_MAX) { + qh_fprintf_rbox(qh, qh->ferr, 6201, "rbox input error: coordinate %2.2g is too large. Reduce 'Bn'\n", a); + qh_errexit_rbox(qh, qh_ERRinput); + } + return (int)(a + 0.5); + } +} /* qh_roundi */ + +void qh_out1(qhT *qh, double a) { + + if (qh->rbox_isinteger) + qh_fprintf_rbox(qh, qh->fout, 9403, "%d ", qh_roundi(qh, a+qh->rbox_out_offset)); + else + qh_fprintf_rbox(qh, qh->fout, 9404, qh_REAL_1, a+qh->rbox_out_offset); +} /* qh_out1 */ + +void qh_out2n(qhT *qh, double a, double b) { + + if (qh->rbox_isinteger) + qh_fprintf_rbox(qh, qh->fout, 9405, "%d %d\n", qh_roundi(qh, a+qh->rbox_out_offset), qh_roundi(qh, b+qh->rbox_out_offset)); + else + qh_fprintf_rbox(qh, qh->fout, 9406, qh_REAL_2n, a+qh->rbox_out_offset, b+qh->rbox_out_offset); +} /* qh_out2n */ + +void qh_out3n(qhT *qh, double a, double b, double c) { + + if (qh->rbox_isinteger) + qh_fprintf_rbox(qh, qh->fout, 9407, "%d %d %d\n", qh_roundi(qh, a+qh->rbox_out_offset), qh_roundi(qh, b+qh->rbox_out_offset), qh_roundi(qh, c+qh->rbox_out_offset)); + else + qh_fprintf_rbox(qh, qh->fout, 9408, qh_REAL_3n, a+qh->rbox_out_offset, b+qh->rbox_out_offset, c+qh->rbox_out_offset); +} /* qh_out3n */ + +void qh_outcoord(qhT *qh, int iscdd, double *coord, int dim) { + double *p= coord; + int k; + + if (iscdd) + qh_out1(qh, 1.0); + for (k=0; k < dim; k++) + qh_out1(qh, *(p++)); + qh_fprintf_rbox(qh, qh->fout, 9396, "\n"); +} /* qh_outcoord */ + +void qh_outcoincident(qhT *qh, int coincidentpoints, double radius, int iscdd, double *coord, int dim) { + double *p; + double randr, delta; + int i,k; + double randmax= qh_RANDOMmax; + + for (i=0; i<coincidentpoints; i++) { + p= coord; + if (iscdd) + qh_out1(qh, 1.0); + for (k=0; k < dim; k++) { + randr= qh_RANDOMint; + delta= 2.0 * randr/randmax - 1.0; /* -1..+1 */ + delta *= radius; + qh_out1(qh, *(p++) + delta); + } + qh_fprintf_rbox(qh, qh->fout, 9410, "\n"); + } +} /* qh_outcoincident */ + +/*------------------------------------------------ + Only called from qh_rboxpoints2 or qh_fprintf_rbox + qh_fprintf_rbox is only called from qh_rboxpoints2 + The largest exitcode is '255' for compatibility with exit() +*/ +void qh_errexit_rbox(qhT *qh, int exitcode) +{ + longjmp(qh->rbox_errexit, exitcode); +} /* qh_errexit_rbox */ + diff --git a/contrib/libs/qhull/libqhull_r/stat_r.c b/contrib/libs/qhull/libqhull_r/stat_r.c new file mode 100644 index 0000000000..5661e010e4 --- /dev/null +++ b/contrib/libs/qhull/libqhull_r/stat_r.c @@ -0,0 +1,727 @@ +/*<html><pre> -<a href="qh-stat_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + stat_r.c + contains all statistics that are collected for qhull + + see qh-stat_r.htm and stat_r.h + + Copyright (c) 1993-2020 The Geometry Center. + $Id: //main/2019/qhull/src/libqhull_r/stat_r.c#9 $$Change: 3037 $ + $DateTime: 2020/09/03 17:28:32 $$Author: bbarber $ +*/ + +#include "qhull_ra.h" + +/*========== functions in alphabetic order ================*/ + +/*-<a href="qh-stat_r.htm#TOC" + >-------------------------------</a><a name="allstatA">-</a> + + qh_allstatA() + define statistics in groups of 20 + + notes: + (otherwise, 'gcc -O2' uses too much memory) + uses qhstat.next +*/ +void qh_allstatA(qhT *qh) { + + /* 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", 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); + + qh->qhstat.precision= qh->qhstat.next; /* usually call qh_joggle_restart, printed if Q0 or QJn */ + 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, "dupridges with multiple neighbors", -1); + zzdef_(zinc, Zflipridge, "dupridges with flip facet into good neighbor", -1); + zzdef_(zinc, Zflipridge2, "dupridges with flip facet into good flip neighbor", -1); +} +void qh_allstatB(qhT *qh) { + 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, "simplicial facets that were non-simplicial", -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 cosine (angle) of facet normals for all ridges", Zangle); + zdef_(wmax, Wanglemax, " maximum cosine of facet normals (flatest) across a ridge", -1); + zdef_(wmin, Wanglemin, " minimum cosine of facet normals (sharpest) 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(qhT *qh) { + 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, " count", -1); + zdef_(zinc, Zsearchpoints, "searches of all points for initial simplex", -1); + zdef_(zinc, Zdetfacetarea, "determinants for facet area", -1); + zdef_(zinc, Znoarea, " determinants not computed because vertex too low", -1); + zdef_(zinc, Zdetsimplex, "determinants for initial hull or voronoi vertices", -1); + zdef_(zinc, Znotmax, "points ignored (!above max_outside)", -1); + zdef_(zinc, Zpinchedapex, "points ignored (pinched apex)", -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(qhT *qh) { + 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, Znewbesthorizon, " new bestfacets during qh_findbesthorizon", -1); + zdef_(zinc, Zpartangle, "angle tests for repartitioned coplanar points", -1); + zdef_(zinc, Zpartcorner, " repartitioned coplanar points above a corner facet", -1); + zdef_(zinc, Zparthidden, " repartitioned coplanar points above a hidden facet", -1); + zdef_(zinc, Zparttwisted, " repartitioned coplanar points above a twisted facet", -1); +} +void qh_allstatE(qhT *qh) { + zdef_(zinc, Zpartinside, "inside points", -1); + zdef_(zinc, Zpartnear, " near 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_(zinc, Zbestlowerall, " with rare search of all facets", -1); + zdef_(zmax, Zbestloweralln, " facets per search of all facets", -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); + zzdef_(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(qhT *qh) { + 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, Zvertextests, "distance tests for vertex 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 or vertices in getmergeset", -1); + zdef_(zinc, Zconcaveridge, "concave ridges in getmergeset", -1); + zdef_(zinc, Zconcavecoplanarridge, "concave-coplanar ridges in getmergeset", -1); + zdef_(zinc, Ztwistedridge, "twisted ridges in getmergeset", -1); +} +void qh_allstatF(qhT *qh) { + 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 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, Zmergeintocoplanar, "new facets merged into coplanar horizon", -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, Zredundant, "merges due to redundant neighbors", -1); + zdef_(zinc, Zredundantmerge, " detected by qh_test_nonsimplicial_merge instead of qh_test_redundant_neighbors", -1); + zdef_(zadd, Ztestvneighbor, "non-convex vertex neighbors", -1); +} +void qh_allstatG(qhT *qh) { + 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, Zconcavecoplanar, "merges due to concave-coplanar facets", -1); + zdef_(wadd, Wconcavecoplanartot, " average merge distance", Zconcavecoplanar); + zdef_(wmax, Wconcavecoplanarmax, " 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 dupridges", -1); + zdef_(wadd, Wduplicatetot, " average merge distance", Zduplicate); + zdef_(wmax, Wduplicatemax, " maximum merge distance", -1); + zdef_(zinc, Ztwisted, "merges due to twisted facets", -1); + zdef_(wadd, Wtwistedtot, " average merge distance", Ztwisted); + zdef_(wmax, Wtwistedmax, " maximum merge distance", -1); +} +void qh_allstatH(qhT *qh) { + zdef_(zdoc, Zdoc8, "statistics for vertex merges", -1); + zzdef_(zinc, Zpinchduplicate, "merge pinched vertices for a duplicate ridge", -1); + zzdef_(zinc, Zpinchedvertex, "merge pinched vertices for a dupridge", -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, Znewvertexridge, " found new vertex in ridge", -1); + zdef_(zinc, Zdelridge, "deleted ridges due to renamed vertices", -1); + zdef_(zinc, Zdropneighbor, "dropped neighbors due to renamed vertices", -1); + zdef_(zinc, Zdropdegen, "merge 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, Zretryadd, "retry qh_addpoint after merge pinched vertex", -1); + zdef_(zadd, Zretryaddtot, " tot. merge pinched vertex due to dupridge", -1); + zdef_(zmax, Zretryaddmax, " max. merge pinched vertex for a qh_addpoint", -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, outside and coplanar sets, and qhT",-1); + zdef_(zadd, Zmemridges, "for ridges and their vertex sets", -1); +} /* allstat */ + +void qh_allstatI(qhT *qh) { + qh->qhstat.vridges= qh->qhstat.next; /* printed in qh_produce_output2 if non-zero Zridge or Zridgemid */ + 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 */ + +/*-<a href="qh-stat_r.htm#TOC" + >-------------------------------</a><a name="allstatistics">-</a> + + qh_allstatistics() + reset printed flag for all statistics +*/ +void qh_allstatistics(qhT *qh) { + int i; + + for(i=ZEND; i--; ) + qh->qhstat.printed[i]= False; +} /* allstatistics */ + +#if qh_KEEPstatistics +/*-<a href="qh-stat_r.htm#TOC" + >-------------------------------</a><a name="collectstatistics">-</a> + + qh_collectstatistics() + collect statistics for qh.facet_list + +*/ +void qh_collectstatistics(qhT *qh) { + 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 + (int)sizeof(qhT); + 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, 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); + if (!qh_checklists(qh, qh->facet_list)) { + qh_fprintf(qh, qh->ferr, 6373, "qhull internal error: qh_checklists failed on qh_collectstatistics\n"); + if (!qh->ERREXITcalled) + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + 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(qh, facet->vertices); + sizneighbors= qh_setsize(qh, facet->neighbors); + sizridges= qh_setsize(qh, 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, (int)sizeof(facetT) + qh->normal_size + 2*(int)sizeof(setT) + + SETelemsize * (sizneighbors + sizvertices)); + if (facet->ridges) { + zadd_(Zmemridges, + (int)sizeof(setT) + SETelemsize * sizridges + sizridges * + ((int)sizeof(ridgeT) + (int)sizeof(setT) + SETelemsize * (qh->hull_dim-1))/2); + } + if (facet->outsideset) + zadd_(Zmempoints, (int)sizeof(setT) + SETelemsize * qh_setsize(qh, facet->outsideset)); + if (facet->coplanarset) + zadd_(Zmempoints, (int)sizeof(setT) + SETelemsize * qh_setsize(qh, 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(qh, 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(qh, vertex->point, facet, &dist); + wmax_(Wvertexmax, dist); + wmin_(Wvertexmin, dist); + } + } + } + FORALLvertices { + if (vertex->deleted) + continue; + zadd_(Zmemvertices, (int)sizeof(vertexT)); + if (vertex->neighbors) { + sizneighbors= qh_setsize(qh, vertex->neighbors); + zadd_(Znumvneighbors, sizneighbors); + zmax_(Zmaxvneighbors, sizneighbors); + zadd_(Zmemvertices, (int)sizeof(vertexT) + SETelemsize * sizneighbors); + } + } + qh->RANDOMdist= qh->old_randomdist; +} /* collectstatistics */ +#endif /* qh_KEEPstatistics */ + +/*-<a href="qh-stat_r.htm#TOC" + >-------------------------------</a><a name="initstatistics">-</a> + + qh_initstatistics(qh) + initialize statistics + + notes: + NOerrors -- qh_initstatistics can not use qh_errexit(), qh_fprintf, or qh.ferr + On first call, only qhmem.ferr is defined. qh_memalloc is not setup. + Also invoked by QhullQh(). +*/ +void qh_initstatistics(qhT *qh) { + int i; + realT realx; + int intx; + + qh_allstatistics(qh); + qh->qhstat.next= 0; + qh_allstatA(qh); + qh_allstatB(qh); + qh_allstatC(qh); + qh_allstatD(qh); + qh_allstatE(qh); + qh_allstatE2(qh); + qh_allstatF(qh); + qh_allstatG(qh); + qh_allstatH(qh); + qh_allstatI(qh); + if (qh->qhstat.next > (int)sizeof(qh->qhstat.id)) { + qh_fprintf_stderr(6184, "qhull internal error (qh_initstatistics): increase size of qhstat.id[]. qhstat.next %d should be <= sizeof(qh->qhstat.id) %d\n", + qh->qhstat.next, (int)sizeof(qh->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 (qh->qhstat.id[i] == qh->qhstat.id[j]) { + qh_fprintf_stderr(6185, "qhull error (qh_initstatistics): duplicated statistic %d at indices %d and %d\n", + qh->qhstat.id[i], i, j); + } + } + } +#endif + qh_exit(qh_ERRqhull); /* can not use qh_errexit() */ + } + qh->qhstat.init[zinc].i= 0; + qh->qhstat.init[zadd].i= 0; + qh->qhstat.init[zmin].i= INT_MAX; + qh->qhstat.init[zmax].i= INT_MIN; + qh->qhstat.init[wadd].r= 0; + qh->qhstat.init[wmin].r= REALmax; + qh->qhstat.init[wmax].r= -REALmax; + for(i=0; i < ZEND; i++) { + if (qh->qhstat.type[i] > ZTYPEreal) { + realx= qh->qhstat.init[(unsigned char)(qh->qhstat.type[i])].r; + qh->qhstat.stats[i].r= realx; + }else if (qh->qhstat.type[i] != zdoc) { + intx= qh->qhstat.init[(unsigned char)(qh->qhstat.type[i])].i; + qh->qhstat.stats[i].i= intx; + } + } +} /* initstatistics */ + +/*-<a href="qh-stat_r.htm#TOC" + >-------------------------------</a><a name="newstats">-</a> + + qh_newstats(qh ) + returns True if statistics for zdoc + + returns: + next zdoc +*/ +boolT qh_newstats(qhT *qh, int idx, int *nextindex) { + boolT isnew= False; + int start, i; + + if (qh->qhstat.type[qh->qhstat.id[idx]] == zdoc) + start= idx+1; + else + start= idx; + for(i= start; i < qh->qhstat.next && qh->qhstat.type[qh->qhstat.id[i]] != zdoc; i++) { + if (!qh_nostatistic(qh, qh->qhstat.id[i]) && !qh->qhstat.printed[qh->qhstat.id[i]]) + isnew= True; + } + *nextindex= i; + return isnew; +} /* newstats */ + +/*-<a href="qh-stat_r.htm#TOC" + >-------------------------------</a><a name="nostatistic">-</a> + + qh_nostatistic(qh, index ) + true if no statistic to print +*/ +boolT qh_nostatistic(qhT *qh, int i) { + + if ((qh->qhstat.type[i] > ZTYPEreal + &&qh->qhstat.stats[i].r == qh->qhstat.init[(unsigned char)(qh->qhstat.type[i])].r) + || (qh->qhstat.type[i] < ZTYPEreal + &&qh->qhstat.stats[i].i == qh->qhstat.init[(unsigned char)(qh->qhstat.type[i])].i)) + return True; + return False; +} /* nostatistic */ + +#if qh_KEEPstatistics +/*-<a href="qh-stat_r.htm#TOC" + >-------------------------------</a><a name="printallstatistics">-</a> + + qh_printallstatistics(qh, fp, string ) + print all statistics with header 'string' +*/ +void qh_printallstatistics(qhT *qh, FILE *fp, const char *string) { + + qh_allstatistics(qh); + qh_collectstatistics(qh); + qh_printstatistics(qh, fp, string); + qh_memstatistics(qh, fp); +} + + +/*-<a href="qh-stat_r.htm#TOC" + >-------------------------------</a><a name="printstatistics">-</a> + + qh_printstatistics(qh, 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(qhT *qh, FILE *fp, const char *string) { + int i, k; + realT ave; /* ignored */ + + if (qh->num_points != qh->num_vertices || zval_(Zpbalance) == 0) { + wval_(Wpbalance)= 0.0; + wval_(Wpbalance2)= 0.0; + }else + wval_(Wpbalance2)= qh_stddev(qh, zval_(Zpbalance), wval_(Wpbalance), + wval_(Wpbalance2), &ave); + if (zval_(Zprocessed) == 0) + wval_(Wnewbalance2)= 0.0; + else + wval_(Wnewbalance2)= qh_stddev(qh, zval_(Zprocessed), wval_(Wnewbalance), + wval_(Wnewbalance2), &ave); + qh_fprintf(qh, 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(qh, 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(qh, fp, 9352, "\ + %6.2g max. distance for near-inside points\n", qh->NEARinside); + if (qh->premerge_cos < REALmax/2) qh_fprintf(qh, fp, 9353, "\ + %6.2g max. cosine for pre-merge angle\n", qh->premerge_cos); + if (qh->PREmerge) qh_fprintf(qh, fp, 9354, "\ + %6.2g radius of pre-merge centrum\n", qh->premerge_centrum); + if (qh->postmerge_cos < REALmax/2) qh_fprintf(qh, fp, 9355, "\ + %6.2g max. cosine for post-merge angle\n", qh->postmerge_cos); + if (qh->POSTmerge) qh_fprintf(qh, fp, 9356, "\ + %6.2g radius of post-merge centrum\n", qh->postmerge_centrum); + qh_fprintf(qh, 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 division\n\ + zero diagonal for Gauss: ", qh->ONEmerge, REALepsilon, qh->MINdenom); + for(k=0; k < qh->hull_dim; k++) + qh_fprintf(qh, fp, 9358, "%6.2e ", qh->NEARzero[k]); + qh_fprintf(qh, fp, 9359, "\n\n"); + for(i=0 ; i < qh->qhstat.next; ) + qh_printstats(qh, fp, i, &i); +} /* printstatistics */ +#endif /* qh_KEEPstatistics */ + +/*-<a href="qh-stat_r.htm#TOC" + >-------------------------------</a><a name="printstatlevel">-</a> + + qh_printstatlevel(qh, fp, id ) + print level information for a statistic + + notes: + nop if id >= ZEND, printed, or same as initial value +*/ +void qh_printstatlevel(qhT *qh, FILE *fp, int id) { + + if (id >= ZEND || qh->qhstat.printed[id]) + return; + if (qh->qhstat.type[id] == zdoc) { + qh_fprintf(qh, fp, 9360, "%s\n", qh->qhstat.doc[id]); + return; + } + if (qh_nostatistic(qh, id) || !qh->qhstat.doc[id]) + return; + qh->qhstat.printed[id]= True; + if (qh->qhstat.count[id] != -1 + && qh->qhstat.stats[(unsigned char)(qh->qhstat.count[id])].i == 0) + qh_fprintf(qh, fp, 9361, " *0 cnt*"); + else if (qh->qhstat.type[id] >= ZTYPEreal && qh->qhstat.count[id] == -1) + qh_fprintf(qh, fp, 9362, "%7.2g", qh->qhstat.stats[id].r); + else if (qh->qhstat.type[id] >= ZTYPEreal && qh->qhstat.count[id] != -1) + qh_fprintf(qh, fp, 9363, "%7.2g", qh->qhstat.stats[id].r/ qh->qhstat.stats[(unsigned char)(qh->qhstat.count[id])].i); + else if (qh->qhstat.type[id] < ZTYPEreal && qh->qhstat.count[id] == -1) + qh_fprintf(qh, fp, 9364, "%7d", qh->qhstat.stats[id].i); + else if (qh->qhstat.type[id] < ZTYPEreal && qh->qhstat.count[id] != -1) + qh_fprintf(qh, fp, 9365, "%7.3g", (realT) qh->qhstat.stats[id].i / qh->qhstat.stats[(unsigned char)(qh->qhstat.count[id])].i); + qh_fprintf(qh, fp, 9366, " %s\n", qh->qhstat.doc[id]); +} /* printstatlevel */ + + +/*-<a href="qh-stat_r.htm#TOC" + >-------------------------------</a><a name="printstats">-</a> + + qh_printstats(qh, fp, index, nextindex ) + print statistics for a zdoc group + + returns: + next zdoc if non-null +*/ +void qh_printstats(qhT *qh, FILE *fp, int idx, int *nextindex) { + int j, nexti; + + if (qh_newstats(qh, idx, &nexti)) { + qh_fprintf(qh, fp, 9367, "\n"); + for (j=idx; j<nexti; j++) + qh_printstatlevel(qh, fp, qh->qhstat.id[j]); + } + if (nextindex) + *nextindex= nexti; +} /* printstats */ + +#if qh_KEEPstatistics + +/*-<a href="qh-stat_r.htm#TOC" + >-------------------------------</a><a name="stddev">-</a> + + qh_stddev(qh, num, tot, tot2, ave ) + compute the standard deviation and average from statistics + + tot2 is the sum of the squares + notes: + computes r.m.s.: + (x-ave)^2 + == x^2 - 2x tot/num + (tot/num)^2 + == tot2 - 2 tot tot/num + tot tot/num + == tot2 - tot ave +*/ +realT qh_stddev(qhT *qh, int num, realT tot, realT tot2, realT *ave) { + realT stddev; + + if (num <= 0) { + qh_fprintf(qh, qh->ferr, 7101, "qhull warning (qh_stddev): expecting num > 0. Got num %d, tot %4.4g, tot2 %4.4g. Returning 0.0\n", + num, tot, tot2); + return 0.0; + } + *ave= tot/num; + stddev= sqrt(fabs(tot2/num - *ave * *ave)); + return stddev; +} /* stddev */ +#else +realT qh_stddev(qhT *qh, int num, realT tot, realT tot2, realT *ave) { /* for qhull_r-exports.def */ + QHULL_UNUSED(qh) + QHULL_UNUSED(num) + QHULL_UNUSED(tot) + QHULL_UNUSED(tot2) + QHULL_UNUSED(ave) + + return 0.0; +} +#endif /* qh_KEEPstatistics */ + +#if !qh_KEEPstatistics +void qh_collectstatistics(qhT *qh) {} +void qh_printallstatistics(qhT *qh, FILE *fp, const char *string) {} +void qh_printstatistics(qhT *qh, FILE *fp, const char *string) {} +#endif + diff --git a/contrib/libs/qhull/libqhull_r/stat_r.h b/contrib/libs/qhull/libqhull_r/stat_r.h new file mode 100644 index 0000000000..41b6e5171d --- /dev/null +++ b/contrib/libs/qhull/libqhull_r/stat_r.h @@ -0,0 +1,563 @@ +/*<html><pre> -<a href="qh-stat_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + stat_r.h + contains all statistics that are collected for qhull + + see qh-stat_r.htm and stat_r.c + + Copyright (c) 1993-2020 The Geometry Center. + $Id: //main/2019/qhull/src/libqhull_r/stat_r.h#4 $$Change: 2953 $ + $DateTime: 2020/05/21 22:05:32 $$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 + +/* Depends on realT. Do not include "libqhull_r" to avoid circular dependency */ + +#ifndef DEFqhT +#define DEFqhT 1 +typedef struct qhT qhT; /* Defined by libqhull_r.h */ +#endif + +#ifndef DEFqhstatT +#define DEFqhstatT 1 +typedef struct qhstatT qhstatT; /* Defined here */ +#endif + +/*-<a href="qh-stat_r.htm#TOC" + >-------------------------------</a><a name="KEEPstatistics">-</a> + + qh_KEEPstatistics + 0 turns off statistic reporting and gathering (except zzdef/zzinc/zzadd/zzval/wwval) + + set qh_KEEPstatistics in user_r.h to 0 to turn off statistics +*/ +#ifndef qh_KEEPstatistics +#define qh_KEEPstatistics 1 +#endif + +/*-<a href="qh-stat_r.htm#TOC" + >-------------------------------</a><a name="statistics">-</a> + + Zxxx for integers, Wxxx for reals + + notes: + be sure that all statistics are defined in stat_r.c + otherwise initialization may core dump + can pick up all statistics by: + grep '[zw].*_[(][ZW]' *.c >z.x + remove trailers with query">-</a> + remove leaders with query-replace-regexp [ ^I]+ ( +*/ +#if qh_KEEPstatistics +enum qh_statistics { /* alphabetical after Z/W */ + Zacoplanar, + Wacoplanarmax, + Wacoplanartot, + Zangle, + Wangle, + Wanglemax, + Wanglemin, + Zangletests, + Wareatot, + Wareamax, + Wareamin, + Zavoidold, + Wavoidoldmax, + Wavoidoldtot, + Zback0, + Zbestcentrum, + Zbestdist, + Zbestlower, + Zbestlowerall, + Zbestloweralln, + Zbestlowerv, + Zcentrumtests, + Zcheckpart, + Zcomputefurthest, + Zconcave, + Wconcavemax, + Wconcavetot, + Zconcavecoplanar, + Wconcavecoplanarmax, + Wconcavecoplanartot, + Zconcavecoplanarridge, + Zconcaveridge, + Zconcaveridges, + Zcoplanar, + Wcoplanarmax, + Wcoplanartot, + Zcoplanarangle, + Zcoplanarcentrum, + Zcoplanarhorizon, + Zcoplanarinside, + Zcoplanarpart, + Zcoplanarridges, + Wcpu, + Zcyclefacetmax, + Zcyclefacettot, + Zcyclehorizon, + Zcyclevertex, + Zdegen, + Wdegenmax, + Wdegentot, + Zdegenvertex, + Zdelfacetdup, + Zdelridge, + Zdelvertextot, + Zdelvertexmax, + Zdetfacetarea, + 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, + Zdupsame, + Zflipped, + Wflippedmax, + Wflippedtot, + Zflippedfacets, + Zflipridge, + Zflipridge2, + 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, + Zmergeintocoplanar, + Zmergeintohorizon, + Zmergenew, + Zmergesettot, + Zmergesetmax, + Zmergesettot2, + Zmergesimplex, + Zmergevertex, + Wmindenom, + Wminvertex, + Zminnorm, + Zmultiridge, + Znearlysingular, + Zredundant, + Wnewbalance, + Wnewbalance2, + Znewbesthorizon, + Znewfacettot, + Znewfacetmax, + Znewvertex, + Wnewvertex, + Wnewvertexmax, + Znewvertexridge, + Znoarea, + Znonsimplicial, + Znowsimplicial, + Znotgood, + Znotgoodnew, + Znotmax, + Znumfacets, + Znummergemax, + Znummergetot, + Znumneighbors, + Znumridges, + Znumvertices, + Znumvisibility, + Znumvneighbors, + Zonehorizon, + Zpartangle, + Zpartcoplanar, + Zpartcorner, + Zparthidden, + Zpartinside, + Zpartition, + Zpartitionall, + Zpartnear, + Zparttwisted, + Zpbalance, + Wpbalance, + Wpbalance2, + Zpinchduplicate, + Zpinchedapex, + Zpinchedvertex, + Zpostfacets, + Zpremergetot, + Zprocessed, + Zremvertex, + Zremvertexdel, + Zredundantmerge, + Zrenameall, + Zrenamepinch, + Zrenameshare, + Zretry, + Wretrymax, + Zretryadd, + Zretryaddmax, + Zretryaddtot, + 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, + Ztwisted, + Wtwistedtot, + Wtwistedmax, + Ztwistedridge, + Zvertextests, + Wvertexmax, + Wvertexmin, + Zvertexridge, + Zvertexridgetot, + Zvertexridgemax, + Zvertices, + Zvisfacettot, + Zvisfacetmax, + Zvisit, + Zvisit2max, + Zvisvertextot, + Zvisvertexmax, + Zvvisit, + Zvvisit2max, + Zwidefacet, + Zwidevertices, + ZEND}; + +/*-<a href="qh-stat_r.htm#TOC" + >-------------------------------</a><a name="ZZstat">-</a> + + 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 qh_statistics { /* for zzdef etc. macros */ + Zback0, + Zbestdist, + Zcentrumtests, + Zcheckpart, + Zconcaveridges, + Zcoplanarhorizon, + Zcoplanarpart, + Zcoplanarridges, + Zcyclefacettot, + Zcyclehorizon, + Zdelvertextot, + Zdistcheck, + Zdistconvex, + Zdistplane, + Zdistzero, + Zdoc1, + Zdoc2, + Zdoc3, + Zdoc11, + Zflippedfacets, + Zflipridge, + Zflipridge2, + Zgauss0, + Zminnorm, + Zmultiridge, + Znearlysingular, + Wnewvertexmax, + Znumvisibility, + Zpartcoplanar, + Zpartition, + Zpartitionall, + Zpinchduplicate, + Zpinchedvertex, + Zprocessed, + Zretry, + Zridge, + Wridge, + Wridgemax, + Zridge0, + Wridge0, + Wridge0max, + Zridgemid, + Wridgemid, + Wridgemidmax, + Zridgeok, + Wridgeok, + Wridgeokmax, + Zsetplane, + Ztotcheck, + Ztotmerge, + Zvertextests, + ZEND}; +#endif + +/*-<a href="qh-stat_r.htm#TOC" + >-------------------------------</a><a name="ztype">-</a> + + 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 =============*/ + +/*-<a href="qh-stat_r.htm#TOC" + >--------------------------------</a><a name="MAYdebugx">-</a> + + MAYdebugx + define as maydebug() to be called frequently for error trapping +*/ +#define MAYdebugx + +/*-<a href="qh-stat_r.htm#TOC" + >--------------------------------</a><a name="zdef_">-</a> + + 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) qh->qhstat.id[qh->qhstat.next++]=name; \ + qh->qhstat.doc[name]= string; qh->qhstat.count[name]= cnt; qh->qhstat.type[name]= stype +#if qh_KEEPstatistics +#define zdef_(stype,name,string,cnt) qh->qhstat.id[qh->qhstat.next++]=name; \ + qh->qhstat.doc[name]= string; qh->qhstat.count[name]= cnt; qh->qhstat.type[name]= stype +#else +#define zdef_(type,name,doc,count) +#endif + +/*-<a href="qh-stat_r.htm#TOC" + >--------------------------------</a><a name="zinc_">-</a> + + zzinc_( name ), zinc_( name) + increment an integer statistic +*/ +#define zzinc_(id) {MAYdebugx; qh->qhstat.stats[id].i++;} +#if qh_KEEPstatistics +#define zinc_(id) {MAYdebugx; qh->qhstat.stats[id].i++;} +#else +#define zinc_(id) {} +#endif + +/*-<a href="qh-stat_r.htm#TOC" + >--------------------------------</a><a name="zadd_">-</a> + + zzadd_( name, value ), zadd_( name, value ), wadd_( name, value ) + add value to an integer or real statistic +*/ +#define zzadd_(id, val) {MAYdebugx; qh->qhstat.stats[id].i += (val);} +#define wwadd_(id, val) {MAYdebugx; qh->qhstat.stats[id].r += (val);} +#if qh_KEEPstatistics +#define zadd_(id, val) {MAYdebugx; qh->qhstat.stats[id].i += (val);} +#define wadd_(id, val) {MAYdebugx; qh->qhstat.stats[id].r += (val);} +#else +#define zadd_(id, val) {} +#define wadd_(id, val) {} +#endif + +/*-<a href="qh-stat_r.htm#TOC" + >--------------------------------</a><a name="zval_">-</a> + + zzval_( name ), zval_( name ), wwval_( name ) + set or return value of a statistic +*/ +#define zzval_(id) ((qh->qhstat.stats[id]).i) +#define wwval_(id) ((qh->qhstat.stats[id]).r) +#if qh_KEEPstatistics +#define zval_(id) ((qh->qhstat.stats[id]).i) +#define wval_(id) ((qh->qhstat.stats[id]).r) +#else +#define zval_(id) qh->qhstat.tempi +#define wval_(id) qh->qhstat.tempr +#endif + +/*-<a href="qh-stat_r.htm#TOC" + >--------------------------------</a><a name="zmax_">-</a> + + zmax_( id, val ), wmax_( id, value ) + maximize id with val +*/ +#define wwmax_(id, val) {MAYdebugx; maximize_(qh->qhstat.stats[id].r,(val));} +#if qh_KEEPstatistics +#define zmax_(id, val) {MAYdebugx; maximize_(qh->qhstat.stats[id].i,(val));} +#define wmax_(id, val) {MAYdebugx; maximize_(qh->qhstat.stats[id].r,(val));} +#else +#define zmax_(id, val) {} +#define wmax_(id, val) {} +#endif + +/*-<a href="qh-stat_r.htm#TOC" + >--------------------------------</a><a name="zmin_">-</a> + + zmin_( id, val ), wmin_( id, value ) + minimize id with val +*/ +#if qh_KEEPstatistics +#define zmin_(id, val) {MAYdebugx; minimize_(qh->qhstat.stats[id].i,(val));} +#define wmin_(id, val) {MAYdebugx; minimize_(qh->qhstat.stats[id].r,(val));} +#else +#define zmin_(id, val) {} +#define wmin_(id, val) {} +#endif + +/*================== stat_r.h types ==============*/ + + +/*-<a href="qh-stat_r.htm#TOC" + >--------------------------------</a><a name="intrealT">-</a> + + intrealT + union of integer and real, used for statistics +*/ +typedef union intrealT intrealT; /* union of int and realT */ +union intrealT { + int i; + realT r; +}; + +/*-<a href="qh-stat_r.htm#TOC" + >--------------------------------</a><a name="qhstat">-</a> + + qhstat + Data structure for statistics, similar to qh and qhrbox + + Allocated as part of qhT (libqhull_r.h) +*/ + +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, printed on qh_errexit and qh_produce_output2/Q0/QJn */ + int vridges; /* index for Voronoi ridges, printed on qh_produce_output2 */ + int tempi; + realT tempr; +}; + +/*========== function prototypes ===========*/ + +#ifdef __cplusplus +extern "C" { +#endif + +void qh_allstatA(qhT *qh); +void qh_allstatB(qhT *qh); +void qh_allstatC(qhT *qh); +void qh_allstatD(qhT *qh); +void qh_allstatE(qhT *qh); +void qh_allstatE2(qhT *qh); +void qh_allstatF(qhT *qh); +void qh_allstatG(qhT *qh); +void qh_allstatH(qhT *qh); +void qh_allstatI(qhT *qh); +void qh_allstatistics(qhT *qh); +void qh_collectstatistics(qhT *qh); +void qh_initstatistics(qhT *qh); +boolT qh_newstats(qhT *qh, int idx, int *nextindex); +boolT qh_nostatistic(qhT *qh, int i); +void qh_printallstatistics(qhT *qh, FILE *fp, const char *string); +void qh_printstatistics(qhT *qh, FILE *fp, const char *string); +void qh_printstatlevel(qhT *qh, FILE *fp, int id); +void qh_printstats(qhT *qh, FILE *fp, int idx, int *nextindex); +realT qh_stddev(qhT *qh, int num, realT tot, realT tot2, realT *ave); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* qhDEFstat */ diff --git a/contrib/libs/qhull/libqhull_r/user_r.c b/contrib/libs/qhull/libqhull_r/user_r.c new file mode 100644 index 0000000000..f41b4057da --- /dev/null +++ b/contrib/libs/qhull/libqhull_r/user_r.c @@ -0,0 +1,640 @@ +/*<html><pre> -<a href="qh-user_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + user_r.c + user redefinable functions + + see user2_r.c for qh_fprintf, qh_malloc, qh_free + + see README.txt see COPYING.txt for copyright information. + + see libqhull_r.h for data structures, macros, and user-callable functions. + + see user_eg_r.c, user_eg2_r.c, and unix_r.c for examples. + + see user_r.h for user-definable constants + + use qh_NOmem in mem_r.h to turn off memory management + use qh_NOmerge in user_r.h to turn off facet merging + set qh_KEEPstatistics in user_r.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 + + Qhull-template 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_r.c for an + example. We recommend keeping io.o for the extra debugging + information it supplies. +*/ + +#include "qhull_ra.h" + +#include <stdarg.h> + +/*-<a href="qh-user_r.htm#TOC" + >-------------------------------</a><a name="qhull_template">-</a> + + Qhull-template + Template for calling qhull from inside your program + + returns: + exit code(see qh_ERR... in libqhull_r.h) + all memory freed + + notes: + This can be called any number of times. +*/ +#if 0 +{ + int dim; /* dimension of points */ + int numpoints; /* number of points */ + coordT *points; /* array of coordinates for each point */ + boolT ismalloc; /* True if qhull should free points in qh_freeqhull() or reallocation */ + char flags[]= "qhull Tv"; /* option flags for qhull, see html/qh-quick.htm */ + FILE *outfile= stdout; /* output from qh_produce_output + use NULL to skip qh_produce_output */ + FILE *errfile= stderr; /* error messages from qhull code */ + int exitcode; /* 0 if no error from qhull */ + facetT *facet; /* set by FORALLfacets */ + int curlong, totlong; /* memory remaining after qh_memfreeshort */ + + qhT qh_qh; /* Qhull's data structure. First argument of most calls */ + qhT *qh= &qh_qh; /* Alternatively -- qhT *qh= (qhT *)malloc(sizeof(qhT)) */ + + QHULL_LIB_CHECK /* Check for compatible library */ + + qh_zero(qh, errfile); + + /* initialize dim, numpoints, points[], ismalloc here */ + exitcode= qh_new_qhull(qh, dim, numpoints, points, ismalloc, + flags, outfile, errfile); + if (!exitcode) { /* if no error */ + /* 'qh->facet_list' contains the convex hull */ + FORALLfacets { + /* ... your code ... */ + } + } + qh_freeqhull(qh, !qh_ALL); + qh_memfreeshort(qh, &curlong, &totlong); + if (curlong || totlong) + qh_fprintf(qh, errfile, 7079, "qhull internal warning (main): did not free %d bytes of long memory(%d pieces)\n", totlong, curlong); +} +#endif + +/*-<a href="qh-user_r.htm#TOC" + >-------------------------------</a><a name="new_qhull">-</a> + + qh_new_qhull(qh, dim, numpoints, points, ismalloc, qhull_cmd, outfile, errfile ) + Run qhull + Before first call, either call qh_zero(qh, errfile), or set qh to all zero. + + returns: + results in qh + exitcode (0 if no errors). + + notes: + do not modify points until finished with results. + The qhull data structure contains pointers into the points array. + do not call qhull functions before qh_new_qhull(). + The qhull data structure is not initialized until qh_new_qhull(). + do not call qh_init_A (global_r.c) + + Default errfile is stderr, outfile may be null + qhull_cmd must start with "qhull " + projects points to a new point array for Delaunay triangulations ('d' and 'v') + transforms points into a new point array for halfspace intersection ('H') + + see: + Qhull-template at the beginning of this file. + An example of using qh_new_qhull is user_eg_r.c +*/ +int qh_new_qhull(qhT *qh, int dim, int numpoints, coordT *points, boolT ismalloc, + char *qhull_cmd, FILE *outfile, FILE *errfile) { + return qh_new_qhull_feaspoint(qh, dim, numpoints, points, ismalloc, + qhull_cmd, outfile, errfile, NULL); +} + +int qh_new_qhull_feaspoint(qhT *qh, int dim, int numpoints, coordT *points, boolT ismalloc, + char *qhull_cmd, FILE *outfile, FILE *errfile, coordT* feaspoint) { + /* gcc may issue a "might be clobbered" warning for dim, points, and ismalloc [-Wclobbered]. + These parameters are not referenced after a longjmp() and hence not clobbered. + See http://stackoverflow.com/questions/7721854/what-sense-do-these-clobbered-variable-warnings-make */ + int exitcode, hulldim; + boolT new_ismalloc; + coordT *new_points; + + if(!errfile){ + errfile= stderr; + } + if (!qh->qhmem.ferr) { + qh_meminit(qh, errfile); + } else { + qh_memcheck(qh); + } + if (strncmp(qhull_cmd, "qhull ", (size_t)6) && strcmp(qhull_cmd, "qhull") != 0) { + qh_fprintf(qh, errfile, 6186, "qhull error (qh_new_qhull): start qhull_cmd argument with \"qhull \" or set to \"qhull\"\n"); + return qh_ERRinput; + } + qh_initqhull_start(qh, NULL, outfile, errfile); + if(numpoints==0 && points==NULL){ + trace1((qh, qh->ferr, 1047, "qh_new_qhull: initialize Qhull\n")); + return 0; + } + trace1((qh, qh->ferr, 1044, "qh_new_qhull: build new Qhull for %d %d-d points with %s\n", numpoints, dim, qhull_cmd)); + exitcode= setjmp(qh->errexit); + if (!exitcode) { + qh->NOerrexit= False; + qh_initflags(qh, qhull_cmd); + if (qh->DELAUNAY) + qh->PROJECTdelaunay= True; + if (qh->HALFspace) { + /* points is an array of halfspaces, + the last coordinate of each halfspace is its offset */ + hulldim= dim-1; + if(feaspoint) + { + if (!(qh->feasible_point= (pointT*)qh_malloc(hulldim * sizeof(coordT)))) { + qh_fprintf(qh, qh->ferr, 6079, "qhull error: insufficient memory for 'Hn,n,n'\n"); + qh_errexit(qh, qh_ERRmem, NULL, NULL); + } + coordT* coords = qh->feasible_point; + coordT* value = feaspoint; + int i; + for(i = 0; i < hulldim; ++i) + { + *(coords++) = *(value++); + } + } + else + { + qh_setfeasible(qh, hulldim); + } + new_points= qh_sethalfspace_all(qh, dim, numpoints, points, qh->feasible_point); + new_ismalloc= True; + if (ismalloc) + qh_free(points); + }else { + hulldim= dim; + new_points= points; + new_ismalloc= ismalloc; + } + qh_init_B(qh, new_points, numpoints, hulldim, new_ismalloc); + qh_qhull(qh); + qh_check_output(qh); + if (outfile) { + qh_produce_output(qh); + }else { + qh_prepare_output(qh); + } + if (qh->VERIFYoutput && !qh->FORCEoutput && !qh->STOPadd && !qh->STOPcone && !qh->STOPpoint) + qh_check_points(qh); + } + qh->NOerrexit= True; + return exitcode; +} /* new_qhull */ + +/*-<a href="qh-user_r.htm#TOC" + >-------------------------------</a><a name="errexit">-</a> + + qh_errexit(qh, exitcode, facet, ridge ) + report and exit from an error + report facet and ridge if non-NULL + reports useful information such as last point processed + set qh.FORCEoutput to print neighborhood of facet + + see: + qh_errexit2() in libqhull_r.c for printing 2 facets + + design: + check for error within error processing + compute qh.hulltime + print facet and ridge (if any) + report commandString, options, qh.furthest_id + print summary and statistics (including precision statistics) + if qh_ERRsingular + print help text for singular data set + exit program via long jump (if defined) or exit() +*/ +void qh_errexit(qhT *qh, int exitcode, facetT *facet, ridgeT *ridge) { + + qh->tracefacet= NULL; /* avoid infinite recursion through qh_fprintf */ + qh->traceridge= NULL; + qh->tracevertex= NULL; + if (qh->ERREXITcalled) { + qh_fprintf(qh, qh->ferr, 8126, "\nqhull error while handling previous error in qh_errexit. Exit program\n"); + qh_exit(qh_ERRother); + } + qh->ERREXITcalled= True; + if (!qh->QHULLfinished) + qh->hulltime= qh_CPUclock - qh->hulltime; + qh_errprint(qh, "ERRONEOUS", facet, NULL, ridge, NULL); + qh_option(qh, "_maxoutside", NULL, &qh->MAXoutside); + qh_fprintf(qh, qh->ferr, 8127, "\nWhile executing: %s | %s\n", qh->rbox_command, qh->qhull_command); + qh_fprintf(qh, qh->ferr, 8128, "Options selected for Qhull %s:\n%s\n", qh_version, qh->qhull_options); + if (qh->furthest_id >= 0) { + qh_fprintf(qh, qh->ferr, 8129, "Last point added to hull was p%d.", qh->furthest_id); + if (zzval_(Ztotmerge)) + qh_fprintf(qh, qh->ferr, 8130, " Last merge was #%d.", zzval_(Ztotmerge)); + if (qh->QHULLfinished) + qh_fprintf(qh, qh->ferr, 8131, "\nQhull has finished constructing the hull."); + else if (qh->POSTmerging) + qh_fprintf(qh, qh->ferr, 8132, "\nQhull has started post-merging."); + qh_fprintf(qh, qh->ferr, 8133, "\n"); + } + if (qh->FORCEoutput && (qh->QHULLfinished || (!facet && !ridge))) + qh_produce_output(qh); + else if (exitcode != qh_ERRinput) { + if (exitcode != qh_ERRsingular && zzval_(Zsetplane) > qh->hull_dim+1) { + qh_fprintf(qh, qh->ferr, 8134, "\nAt error exit:\n"); + qh_printsummary(qh, qh->ferr); + if (qh->PRINTstatistics) { + qh_collectstatistics(qh); + qh_allstatistics(qh); + qh_printstatistics(qh, qh->ferr, "at error exit"); + qh_memstatistics(qh, qh->ferr); + } + } + if (qh->PRINTprecision) + qh_printstats(qh, qh->ferr, qh->qhstat.precision, NULL); + } + if (!exitcode) + exitcode= qh_ERRother; + else if (exitcode == qh_ERRprec && !qh->PREmerge) + qh_printhelp_degenerate(qh, qh->ferr); + else if (exitcode == qh_ERRqhull) + qh_printhelp_internal(qh, qh->ferr); + else if (exitcode == qh_ERRsingular) + qh_printhelp_singular(qh, qh->ferr); + else if (exitcode == qh_ERRdebug) + qh_fprintf(qh, qh->ferr, 8016, "qhull exit due to qh_ERRdebug\n"); + else if (exitcode == qh_ERRtopology || exitcode == qh_ERRwide || exitcode == qh_ERRprec) { + if (qh->NOpremerge && !qh->MERGING) + qh_printhelp_degenerate(qh, qh->ferr); + else if (exitcode == qh_ERRtopology) + qh_printhelp_topology(qh, qh->ferr); + else if (exitcode == qh_ERRwide) + qh_printhelp_wide(qh, qh->ferr); + }else if (exitcode > 255) { + qh_fprintf(qh, qh->ferr, 6426, "qhull internal error (qh_errexit): exit code %d is greater than 255. Invalid argument for exit(). Replaced with 255\n", exitcode); + exitcode= 255; + } + if (qh->NOerrexit) { + qh_fprintf(qh, qh->ferr, 6187, "qhull internal error (qh_errexit): either error while reporting error QH%d, or qh.NOerrexit not cleared after setjmp(). Exit program with error status %d\n", + qh->last_errcode, exitcode); + qh_exit(exitcode); + } + qh->ERREXITcalled= False; + qh->NOerrexit= True; + qh->ALLOWrestart= False; /* longjmp will undo qh_build_withrestart */ + longjmp(qh->errexit, exitcode); +} /* errexit */ + +/*-<a href="qh-user_r.htm#TOC" + >-------------------------------</a><a name="errprint">-</a> + + qh_errprint(qh, fp, string, atfacet, otherfacet, atridge, atvertex ) + prints out the information of facets and ridges to fp + also prints neighbors and geomview output + + notes: + except for string, any parameter may be NULL +*/ +void qh_errprint(qhT *qh, const char *string, facetT *atfacet, facetT *otherfacet, ridgeT *atridge, vertexT *atvertex) { + int i; + + if (atvertex) { + qh_fprintf(qh, qh->ferr, 8138, "%s VERTEX:\n", string); + qh_printvertex(qh, qh->ferr, atvertex); + } + if (atridge) { + qh_fprintf(qh, qh->ferr, 8137, "%s RIDGE:\n", string); + qh_printridge(qh, qh->ferr, atridge); + if (!atfacet) + atfacet= atridge->top; + if (!otherfacet) + otherfacet= otherfacet_(atridge, atfacet); + if (atridge->top && atridge->top != atfacet && atridge->top != otherfacet) + qh_printfacet(qh, qh->ferr, atridge->top); + if (atridge->bottom && atridge->bottom != atfacet && atridge->bottom != otherfacet) + qh_printfacet(qh, qh->ferr, atridge->bottom); + } + if (atfacet) { + qh_fprintf(qh, qh->ferr, 8135, "%s FACET:\n", string); + qh_printfacet(qh, qh->ferr, atfacet); + } + if (otherfacet) { + qh_fprintf(qh, qh->ferr, 8136, "%s OTHER FACET:\n", string); + qh_printfacet(qh, qh->ferr, otherfacet); + } + if (qh->fout && qh->FORCEoutput && atfacet && !qh->QHULLfinished && !qh->IStracing) { + qh_fprintf(qh, qh->ferr, 8139, "ERRONEOUS and NEIGHBORING FACETS to output\n"); + for (i=0; i < qh_PRINTEND; i++) /* use fout for geomview output */ + qh_printneighborhood(qh, qh->fout, qh->PRINTout[i], atfacet, otherfacet, + !qh_ALL); + } +} /* errprint */ + + +/*-<a href="qh-user_r.htm#TOC" + >-------------------------------</a><a name="printfacetlist">-</a> + + qh_printfacetlist(qh, fp, facetlist, facets, printall ) + print all fields for a facet list and/or set of facets to fp + if !printall, + only prints good facets + + notes: + also prints all vertices +*/ +void qh_printfacetlist(qhT *qh, facetT *facetlist, setT *facets, boolT printall) { + facetT *facet, **facetp; + + if (facetlist) + qh_checklists(qh, facetlist); + qh_fprintf(qh, qh->ferr, 9424, "printfacetlist: vertices\n"); + qh_printbegin(qh, qh->ferr, qh_PRINTfacets, facetlist, facets, printall); + if (facetlist) { + qh_fprintf(qh, qh->ferr, 9413, "printfacetlist: facetlist\n"); + FORALLfacet_(facetlist) + qh_printafacet(qh, qh->ferr, qh_PRINTfacets, facet, printall); + } + if (facets) { + qh_fprintf(qh, qh->ferr, 9414, "printfacetlist: %d facets\n", qh_setsize(qh, facets)); + FOREACHfacet_(facets) + qh_printafacet(qh, qh->ferr, qh_PRINTfacets, facet, printall); + } + qh_fprintf(qh, qh->ferr, 9412, "printfacetlist: end\n"); + qh_printend(qh, qh->ferr, qh_PRINTfacets, facetlist, facets, printall); +} /* printfacetlist */ + + +/*-<a href="qh-user_r.htm#TOC" + >-------------------------------</a><a name="printhelp_degenerate">-</a> + + qh_printhelp_degenerate(qh, fp ) + prints descriptive message for precision error with qh_ERRprec + + notes: + no message if qh_QUICKhelp +*/ +void qh_printhelp_degenerate(qhT *qh, FILE *fp) { + + if (qh->MERGEexact || qh->PREmerge || qh->JOGGLEmax < REALmax/2) + qh_fprintf(qh, fp, 9368, "\n\ +A Qhull error has occurred. Qhull should have corrected the above\n\ +precision error. Please send the input and all of the output to\n\ +qhull_bug@qhull.org\n"); + else if (!qh_QUICKhelp) { + qh_fprintf(qh, fp, 9369, "\n\ +Precision problems were detected during construction of the convex hull.\n\ +This occurs because convex hull algorithms assume that calculations are\n\ +exact, but floating-point arithmetic has roundoff errors.\n\ +\n\ +To correct for precision problems, do not use 'Q0'. By default, Qhull\n\ +selects 'C-0' or 'Qx' and merges non-convex facets. With option 'QJ',\n\ +Qhull joggles the input to prevent precision problems. See \"Imprecision\n\ +in Qhull\" (qh-impre.htm).\n\ +\n\ +If you use 'Q0', the output may include\n\ +coplanar ridges, concave ridges, and flipped facets. In 4-d and higher,\n\ +Qhull may produce a ridge with four neighbors or two facets with the same \n\ +vertices. Qhull reports these events when they occur. It stops when a\n\ +concave ridge, flipped facet, or duplicate facet occurs.\n"); +#if REALfloat + qh_fprintf(qh, fp, 9370, "\ +\n\ +Qhull is currently using single precision arithmetic. The following\n\ +will probably remove the precision problems:\n\ + - recompile qhull for realT precision(#define REALfloat 0 in user_r.h).\n"); +#endif + if (qh->DELAUNAY && !qh->SCALElast && qh->MAXabs_coord > 1e4) + qh_fprintf(qh, fp, 9371, "\ +\n\ +When computing the Delaunay triangulation of coordinates > 1.0,\n\ + - use 'Qbb' to scale the last coordinate to [0,m] (max previous coordinate)\n"); + if (qh->DELAUNAY && !qh->ATinfinity) + qh_fprintf(qh, fp, 9372, "\ +When computing the Delaunay triangulation:\n\ + - use 'Qz' to add a point at-infinity. This reduces precision problems.\n"); + + qh_fprintf(qh, fp, 9373, "\ +\n\ +If you need triangular output:\n\ + - use option 'Qt' to triangulate the output\n\ + - use option 'QJ' to joggle the input points and remove precision errors\n\ + - use option 'Ft'. It triangulates non-simplicial facets with added points.\n\ +\n\ +If you must use 'Q0',\n\ +try one or more of the following options. They can not guarantee an output.\n\ + - use 'QbB' to scale the input to a cube.\n\ + - use 'Po' to produce output and prevent partitioning for flipped facets\n\ + - use 'V0' to set min. distance to visible facet as 0 instead of roundoff\n\ + - use 'En' to specify a maximum roundoff error less than %2.2g.\n\ + - options 'Qf', 'Qbb', and 'QR0' may also help\n", + qh->DISTround); + qh_fprintf(qh, fp, 9374, "\ +\n\ +To guarantee simplicial output:\n\ + - use option 'Qt' to triangulate the output\n\ + - use option 'QJ' to joggle the input points and remove precision errors\n\ + - use option 'Ft' to triangulate the output by adding points\n\ + - use exact arithmetic (see \"Imprecision in Qhull\", qh-impre.htm)\n\ +"); + } +} /* printhelp_degenerate */ + +/*-<a href="qh-user_r.htm#TOC" + >-------------------------------</a><a name="printhelp_internal">-</a> + + qh_printhelp_internal(qh, fp ) + prints descriptive message for qhull internal error with qh_ERRqhull + + notes: + no message if qh_QUICKhelp +*/ +void qh_printhelp_internal(qhT *qh, FILE *fp) { + + if (!qh_QUICKhelp) { + qh_fprintf(qh, fp, 9426, "\n\ +A Qhull internal error has occurred. Please send the input and output to\n\ +qhull_bug@qhull.org. If you can duplicate the error with logging ('T4z'), please\n\ +include the log file.\n"); + } +} /* printhelp_internal */ + +/*-<a href="qh-user_r.htm#TOC" + >-------------------------------</a><a name="printhelp_narrowhull">-</a> + + qh_printhelp_narrowhull(qh, minangle ) + Warn about a narrow hull + + notes: + Alternatively, reduce qh_WARNnarrow in user_r.h + +*/ +void qh_printhelp_narrowhull(qhT *qh, FILE *fp, realT minangle) { + + qh_fprintf(qh, fp, 7089, "qhull precision warning: The initial hull is narrow. Is the input lower\n\ +dimensional (e.g., a square in 3-d instead of a cube)? Cosine of the minimum\n\ +angle is %.16f. If so, Qhull may produce a wide facet.\n\ +Options 'Qs' (search all points), 'Qbb' (scale last coordinate), or\n\ +'QbB' (scale to unit box) may remove this warning.\n\ +See 'Limitations' in qh-impre.htm. Use 'Pp' to skip this warning.\n", + -minangle); /* convert from angle between normals to angle between facets */ +} /* printhelp_narrowhull */ + +/*-<a href="qh-user_r.htm#TOC" + >-------------------------------</a><a name="printhelp_singular">-</a> + + qh_printhelp_singular(qh, fp ) + prints descriptive message for singular input +*/ +void qh_printhelp_singular(qhT *qh, FILE *fp) { + facetT *facet; + vertexT *vertex, **vertexp; + realT min, max, *coord, dist; + int i,k; + + qh_fprintf(qh, fp, 9376, "\n\ +The input to qhull appears to be less than %d dimensional, or a\n\ +computation has overflowed.\n\n\ +Qhull could not construct a clearly convex simplex from points:\n", + qh->hull_dim); + qh_printvertexlist(qh, fp, "", qh->facet_list, NULL, qh_ALL); + if (!qh_QUICKhelp) + qh_fprintf(qh, fp, 9377, "\n\ +The center point is coplanar with a facet, or a vertex is coplanar\n\ +with a neighboring facet. The maximum round off error for\n\ +computing distances is %2.2g. The center point, facets and distances\n\ +to the center point are as follows:\n\n", qh->DISTround); + qh_printpointid(qh, fp, "center point", qh->hull_dim, qh->interior_point, qh_IDunknown); + qh_fprintf(qh, fp, 9378, "\n"); + FORALLfacets { + qh_fprintf(qh, fp, 9379, "facet"); + FOREACHvertex_(facet->vertices) + qh_fprintf(qh, fp, 9380, " p%d", qh_pointid(qh, vertex->point)); + zinc_(Zdistio); + qh_distplane(qh, qh->interior_point, facet, &dist); + qh_fprintf(qh, fp, 9381, " distance= %4.2g\n", dist); + } + if (!qh_QUICKhelp) { + if (qh->HALFspace) + qh_fprintf(qh, fp, 9382, "\n\ +These points are the dual of the given halfspaces. They indicate that\n\ +the intersection is degenerate.\n"); + qh_fprintf(qh, fp, 9383,"\n\ +These points either have a maximum or minimum x-coordinate, or\n\ +they maximize the determinant for k coordinates. Trial points\n\ +are first selected from points that maximize a coordinate.\n"); + if (qh->hull_dim >= qh_INITIALmax) + qh_fprintf(qh, fp, 9384, "\n\ +Because of the high dimension, the min x-coordinate and max-coordinate\n\ +points are used if the determinant is non-zero. Option 'Qs' will\n\ +do a better, though much slower, job. Instead of 'Qs', you can change\n\ +the points by randomly rotating the input with 'QR0'.\n"); + } + qh_fprintf(qh, fp, 9385, "\nThe min and max coordinates for each dimension are:\n"); + for (k=0; k < qh->hull_dim; k++) { + min= REALmax; + max= -REALmin; + for (i=qh->num_points, coord= qh->first_point+k; i--; coord += qh->hull_dim) { + maximize_(max, *coord); + minimize_(min, *coord); + } + qh_fprintf(qh, fp, 9386, " %d: %8.4g %8.4g difference= %4.4g\n", k, min, max, max-min); + } + if (!qh_QUICKhelp) { + qh_fprintf(qh, fp, 9387, "\n\ +If the input should be full dimensional, you have several options that\n\ +may determine an initial simplex:\n\ + - use 'QJ' to joggle the input and make it full dimensional\n\ + - use 'QbB' to scale the points to the unit cube\n\ + - use 'QR0' to randomly rotate the input for different maximum points\n\ + - use 'Qs' to search all points for the initial simplex\n\ + - use 'En' to specify a maximum roundoff error less than %2.2g.\n\ + - trace execution with 'T3' to see the determinant for each point.\n", + qh->DISTround); +#if REALfloat + qh_fprintf(qh, fp, 9388, "\ + - recompile qhull for realT precision(#define REALfloat 0 in libqhull_r.h).\n"); +#endif + qh_fprintf(qh, fp, 9389, "\n\ +If the input is lower dimensional:\n\ + - use 'QJ' to joggle the input and make it full dimensional\n\ + - use 'Qbk:0Bk:0' to delete coordinate k from the input. You should\n\ + pick the coordinate with the least range. The hull will have the\n\ + correct topology.\n\ + - determine the flat containing the points, rotate the points\n\ + into a coordinate plane, and delete the other coordinates.\n\ + - add one or more points to make the input full dimensional.\n\ +"); + } +} /* printhelp_singular */ + +/*-<a href="qh-user_r.htm#TOC" + >-------------------------------</a><a name="printhelp_topology">-</a> + + qh_printhelp_topology(qh, fp ) + prints descriptive message for qhull topology error with qh_ERRtopology + + notes: + no message if qh_QUICKhelp +*/ +void qh_printhelp_topology(qhT *qh, FILE *fp) { + + if (!qh_QUICKhelp) { + qh_fprintf(qh, fp, 9427, "\n\ +A Qhull topology error has occurred. Qhull did not recover from facet merges and vertex merges.\n\ +This usually occurs when the input is nearly degenerate and substantial merging has occurred.\n\ +See http://www.qhull.org/html/qh-impre.htm#limit\n"); + } +} /* printhelp_topology */ + +/*-<a href="qh-user_r.htm#TOC" + >-------------------------------</a><a name="printhelp_wide">-</a> + + qh_printhelp_wide(qh, fp ) + prints descriptive message for qhull wide facet with qh_ERRwide + + notes: + no message if qh_QUICKhelp +*/ +void qh_printhelp_wide(qhT *qh, FILE *fp) { + + if (!qh_QUICKhelp) { + qh_fprintf(qh, fp, 9428, "\n\ +A wide merge error has occurred. Qhull has produced a wide facet due to facet merges and vertex merges.\n\ +This usually occurs when the input is nearly degenerate and substantial merging has occurred.\n\ +See http://www.qhull.org/html/qh-impre.htm#limit\n"); + } +} /* printhelp_wide */ + +/*-<a href="qh-user_r.htm#TOC" + >-------------------------------</a><a name="user_memsizes">-</a> + + qh_user_memsizes(qh) + allocate up to 10 additional, quick allocation sizes + + notes: + increase maximum number of allocations in qh_initqhull_mem() +*/ +void qh_user_memsizes(qhT *qh) { + + QHULL_UNUSED(qh) + /* qh_memsize(qh, size); */ +} /* user_memsizes */ + + diff --git a/contrib/libs/qhull/libqhull_r/user_r.h b/contrib/libs/qhull/libqhull_r/user_r.h new file mode 100644 index 0000000000..8c100fac09 --- /dev/null +++ b/contrib/libs/qhull/libqhull_r/user_r.h @@ -0,0 +1,1060 @@ +/*<html><pre> -<a href="qh-user_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + user_r.h + user redefinable constants + + for each source file, user_r.h is included first + + see qh-user_r.htm. see COPYING for copyright information. + + See user_r.c for sample code. + + before reading any code, review libqhull_r.h for data structure definitions + +Sections: + ============= qhull library constants ====================== + ============= data types and configuration macros ========== + ============= performance related constants ================ + ============= memory constants ============================= + ============= joggle constants ============================= + ============= conditional compilation ====================== + ============= merge constants ============================== + ============= Microsoft DevStudio ========================== + +Code flags -- + NOerrors -- the code does not call qh_errexit() + WARN64 -- the code may be incompatible with 64-bit pointers + +*/ + +#include <float.h> +#include <limits.h> +#include <time.h> + +#ifndef qhDEFuser +#define qhDEFuser 1 + +/* Derived from Qt's corelib/global/qglobal.h */ +#if !defined(SAG_COM) && !defined(__CYGWIN__) && (defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)) +# define QHULL_OS_WIN +#elif defined(__MWERKS__) && defined(__INTEL__) /* Metrowerks discontinued before the release of Intel Macs */ +# define QHULL_OS_WIN +#endif + +/*============================================================*/ +/*============= qhull library constants ======================*/ +/*============================================================*/ + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="filenamelen">-</a> + + FILENAMElen -- max length for TI and TO filenames + +*/ + +#define qh_FILENAMElen 500 + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="msgcode">-</a> + + msgcode -- Unique message codes for qh_fprintf + + If add new messages, assign these values and increment in user.h and user_r.h + See QhullError.h for 10000 error codes. + Cannot use '0031' since it would be octal + + def counters = [31/32/33/38, 1067, 2113, 3079, 4097, 5006, + 6429, 7027/7028/7035/7068/7070/7102, 8163, 9428, 10000, 11034] + + See: qh_ERR* [libqhull_r.h] +*/ + +#define MSG_TRACE0 0 /* always include if logging ('Tn') */ +#define MSG_TRACE1 1000 +#define MSG_TRACE2 2000 +#define MSG_TRACE3 3000 +#define MSG_TRACE4 4000 +#define MSG_TRACE5 5000 +#define MSG_ERROR 6000 /* errors written to qh.ferr */ +#define MSG_WARNING 7000 +#define MSG_STDERR 8000 /* log messages Written to qh.ferr */ +#define MSG_OUTPUT 9000 +#define MSG_QHULL_ERROR 10000 /* errors thrown by QhullError.cpp (QHULLlastError is in QhullError.h) */ +#define MSG_FIX 11000 /* Document as 'QH11... FIX: ...' */ +#define MSG_MAXLEN 3000 /* qh_printhelp_degenerate() in user_r.c */ + + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="qh_OPTIONline">-</a> + + qh_OPTIONline -- max length of an option line 'FO' +*/ +#define qh_OPTIONline 80 + +/*============================================================*/ +/*============= data types and configuration macros ==========*/ +/*============================================================*/ + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="realT">-</a> + + realT + set the size of floating point numbers + + qh_REALdigits + maximimum number of significant digits + + qh_REAL_1, qh_REAL_2n, qh_REAL_3n + format strings for printf + + qh_REALmax, qh_REALmin + maximum and minimum (near zero) values + + qh_REALepsilon + machine roundoff. Maximum roundoff error for addition and multiplication. + + notes: + Select whether to store floating point numbers in single precision (float) + or double precision (double). + + Use 'float' to save about 8% in time and 25% in space. This is particularly + helpful if high-d where convex hulls are space limited. Using 'float' also + reduces the printed size of Qhull's output since numbers have 8 digits of + precision. + + Use 'double' when greater arithmetic precision is needed. This is needed + for Delaunay triangulations and Voronoi diagrams when you are not merging + facets. + + If 'double' gives insufficient precision, your data probably includes + degeneracies. If so you should use facet merging (done by default) + or exact arithmetic (see imprecision section of manual, qh-impre.htm). + You may also use option 'Po' to force output despite precision errors. + + You may use 'long double', but many format statements need to be changed + and you may need a 'long double' square root routine. S. Grundmann + (sg@eeiwzb.et.tu-dresden.de) has done this. He reports that the code runs + much slower with little gain in precision. + + WARNING: on some machines, int f(){realT a= REALmax;return (a == REALmax);} + returns False. Use (a > REALmax/2) instead of (a == REALmax). + + REALfloat = 1 all numbers are 'float' type + = 0 all numbers are 'double' type +*/ +#define REALfloat 0 + +#if (REALfloat == 1) +#define realT float +#define REALmax FLT_MAX +#define REALmin FLT_MIN +#define REALepsilon FLT_EPSILON +#define qh_REALdigits 8 /* maximum number of significant digits */ +#define qh_REAL_1 "%6.8g " +#define qh_REAL_2n "%6.8g %6.8g\n" +#define qh_REAL_3n "%6.8g %6.8g %6.8g\n" + +#elif (REALfloat == 0) +#define realT double +#define REALmax DBL_MAX +#define REALmin DBL_MIN +#define REALepsilon DBL_EPSILON +#define qh_REALdigits 16 /* maximum number of significant digits */ +#define qh_REAL_1 "%6.16g " +#define qh_REAL_2n "%6.16g %6.16g\n" +#define qh_REAL_3n "%6.16g %6.16g %6.16g\n" + +#else +#error unknown float option +#endif + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="countT">-</a> + + countT + The type for counts and identifiers (e.g., the number of points, vertex identifiers) + Currently used by C++ code-only. Decided against using it for setT because most sets are small. + + Defined as 'int' for C-code compatibility and QH11026 + + QH11026 FIX: countT may be defined as a 'unsigned int', but several code issues need to be solved first. See countT in Changes.txt +*/ + +#ifndef DEFcountT +#define DEFcountT 1 +typedef int countT; +#endif +#define COUNTmax INT_MAX + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="qh_POINTSmax">-</a> + + qh_POINTSmax + Maximum number of points for qh.num_points and point allocation in qh_readpoints +*/ +#define qh_POINTSmax (INT_MAX-16) + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="CPUclock">-</a> + + qh_CPUclock + define the clock() function for reporting the total time spent by Qhull + returns CPU ticks as a 'long int' + qh_CPUclock is only used for reporting the total time spent by Qhull + + qh_SECticks + the number of clock ticks per second + + notes: + looks for CLOCKS_PER_SEC, CLOCKS_PER_SECOND, or assumes microseconds + to define a custom clock, set qh_CLOCKtype to 0 + + if your system does not use clock() to return CPU ticks, replace + qh_CPUclock with the corresponding function. It is converted + to 'unsigned long' to prevent wrap-around during long runs. By default, + <time.h> defines clock_t as 'long' + + Set qh_CLOCKtype to + + 1 for CLOCKS_PER_SEC, CLOCKS_PER_SECOND, or microsecond + Note: may fail if more than 1 hour elapsed time + + 2 use qh_clock() with POSIX times() (see global_r.c) +*/ +#define qh_CLOCKtype 1 /* change to the desired number */ + +#if (qh_CLOCKtype == 1) + +#if defined(CLOCKS_PER_SECOND) +#define qh_CPUclock ((unsigned long)clock()) /* return CPU clock, may be converted to approximate double */ +#define qh_SECticks CLOCKS_PER_SECOND + +#elif defined(CLOCKS_PER_SEC) +#define qh_CPUclock ((unsigned long)clock()) /* return CPU clock, may be converted to approximate double */ +#define qh_SECticks CLOCKS_PER_SEC + +#elif defined(CLK_TCK) +#define qh_CPUclock ((unsigned long)clock()) /* return CPU clock, may be converted to approximate double */ +#define qh_SECticks CLK_TCK + +#else +#define qh_CPUclock ((unsigned long)clock()) /* return CPU clock, may be converted to approximate double */ +#define qh_SECticks 1E6 +#endif + +#elif (qh_CLOCKtype == 2) +#define qh_CPUclock qh_clock() /* return CPU clock, may be converted to approximate double */ +#define qh_SECticks 100 + +#else /* qh_CLOCKtype == ? */ +#error unknown clock option +#endif + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="RANDOM">-</a> + + qh_RANDOMtype, qh_RANDOMmax, qh_RANDOMseed + define random number generator + + qh_RANDOMint generates a random integer between 0 and qh_RANDOMmax. + qh_RANDOMseed sets the random number seed for qh_RANDOMint + + Set qh_RANDOMtype (default 5) to: + 1 for random() with 31 bits (UCB) + 2 for rand() with RAND_MAX or 15 bits (system 5) + 3 for rand() with 31 bits (Sun) + 4 for lrand48() with 31 bits (Solaris) + 5 for qh_rand(qh) with 31 bits (included with Qhull, requires 'qh') + + notes: + Random numbers are used by rbox to generate point sets. Random + numbers are used by Qhull to rotate the input ('QRn' option), + simulate a randomized algorithm ('Qr' option), and to simulate + roundoff errors ('Rn' option). + + Random number generators differ between systems. Most systems provide + rand() but the period varies. The period of rand() is not critical + since qhull does not normally use random numbers. + + The default generator is Park & Miller's minimal standard random + number generator [CACM 31:1195 '88]. It is included with Qhull. + + If qh_RANDOMmax is wrong, qhull will report a warning and Geomview + output will likely be invisible. +*/ +#define qh_RANDOMtype 5 /* *** change to the desired number *** */ + +#if (qh_RANDOMtype == 1) +#define qh_RANDOMmax ((realT)0x7fffffffUL) /* 31 bits, random()/MAX */ +#define qh_RANDOMint random() +#define qh_RANDOMseed_(qh, seed) srandom(seed); + +#elif (qh_RANDOMtype == 2) +#ifdef RAND_MAX +#define qh_RANDOMmax ((realT)RAND_MAX) +#else +#define qh_RANDOMmax ((realT)32767) /* 15 bits (System 5) */ +#endif +#define qh_RANDOMint rand() +#define qh_RANDOMseed_(qh, seed) srand((unsigned int)seed); + +#elif (qh_RANDOMtype == 3) +#define qh_RANDOMmax ((realT)0x7fffffffUL) /* 31 bits, Sun */ +#define qh_RANDOMint rand() +#define qh_RANDOMseed_(qh, seed) srand((unsigned int)seed); + +#elif (qh_RANDOMtype == 4) +#define qh_RANDOMmax ((realT)0x7fffffffUL) /* 31 bits, lrand38()/MAX */ +#define qh_RANDOMint lrand48() +#define qh_RANDOMseed_(qh, seed) srand48(seed); + +#elif (qh_RANDOMtype == 5) /* 'qh' is an implicit parameter */ +#define qh_RANDOMmax ((realT)2147483646UL) /* 31 bits, qh_rand/MAX */ +#define qh_RANDOMint qh_rand(qh) +#define qh_RANDOMseed_(qh, seed) qh_srand(qh, seed); +/* unlike rand(), never returns 0 */ + +#else +#error: unknown random option +#endif + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="ORIENTclock">-</a> + + qh_ORIENTclock + 0 for inward pointing normals by Geomview convention +*/ +#define qh_ORIENTclock 0 + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="RANDOMdist">-</a> + + qh_RANDOMdist + define for random perturbation of qh_distplane and qh_setfacetplane (qh.RANDOMdist, 'QRn') + + For testing qh.DISTround. Qhull should not depend on computations always producing the same roundoff error + + #define qh_RANDOMdist 1e-13 +*/ + +/*============================================================*/ +/*============= joggle constants =============================*/ +/*============================================================*/ + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="JOGGLEdefault">-</a> + + qh_JOGGLEdefault + default qh.JOGGLEmax is qh.DISTround * qh_JOGGLEdefault + + notes: + rbox s r 100 | qhull QJ1e-15 QR0 generates 90% faults at distround 7e-16 + rbox s r 100 | qhull QJ1e-14 QR0 generates 70% faults + rbox s r 100 | qhull QJ1e-13 QR0 generates 35% faults + rbox s r 100 | qhull QJ1e-12 QR0 generates 8% faults + rbox s r 100 | qhull QJ1e-11 QR0 generates 1% faults + rbox s r 100 | qhull QJ1e-10 QR0 generates 0% faults + rbox 1000 W0 | qhull QJ1e-12 QR0 generates 86% faults + rbox 1000 W0 | qhull QJ1e-11 QR0 generates 20% faults + rbox 1000 W0 | qhull QJ1e-10 QR0 generates 2% faults + the later have about 20 points per facet, each of which may interfere + + pick a value large enough to avoid retries on most inputs +*/ +#define qh_JOGGLEdefault 30000.0 + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="JOGGLEincrease">-</a> + + qh_JOGGLEincrease + factor to increase qh.JOGGLEmax on qh_JOGGLEretry or qh_JOGGLEagain +*/ +#define qh_JOGGLEincrease 10.0 + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="JOGGLEretry">-</a> + + qh_JOGGLEretry + if ZZretry = qh_JOGGLEretry, increase qh.JOGGLEmax + +notes: +try twice at the original value in case of bad luck the first time +*/ +#define qh_JOGGLEretry 2 + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="JOGGLEagain">-</a> + + qh_JOGGLEagain + every following qh_JOGGLEagain, increase qh.JOGGLEmax + + notes: + 1 is OK since it's already failed qh_JOGGLEretry times +*/ +#define qh_JOGGLEagain 1 + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="JOGGLEmaxincrease">-</a> + + qh_JOGGLEmaxincrease + maximum qh.JOGGLEmax due to qh_JOGGLEincrease + relative to qh.MAXwidth + + notes: + qh.joggleinput will retry at this value until qh_JOGGLEmaxretry +*/ +#define qh_JOGGLEmaxincrease 1e-2 + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="JOGGLEmaxretry">-</a> + + qh_JOGGLEmaxretry + stop after qh_JOGGLEmaxretry attempts +*/ +#define qh_JOGGLEmaxretry 50 + +/*============================================================*/ +/*============= performance related constants ================*/ +/*============================================================*/ + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="HASHfactor">-</a> + + qh_HASHfactor + total hash slots / used hash slots. Must be at least 1.1. + + notes: + =2 for at worst 50% occupancy for qh.hash_table and normally 25% occupancy +*/ +#define qh_HASHfactor 2 + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="VERIFYdirect">-</a> + + qh_VERIFYdirect + with 'Tv' verify all points against all facets if op count is smaller + + notes: + if greater, calls qh_check_bestdist() instead +*/ +#define qh_VERIFYdirect 1000000 + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="INITIALsearch">-</a> + + qh_INITIALsearch + if qh_INITIALmax, search points up to this dimension +*/ +#define qh_INITIALsearch 6 + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="INITIALmax">-</a> + + qh_INITIALmax + if dim >= qh_INITIALmax, use min/max coordinate points for initial simplex + + notes: + from points with non-zero determinants + use option 'Qs' to override (much slower) +*/ +#define qh_INITIALmax 8 + +/*============================================================*/ +/*============= memory constants =============================*/ +/*============================================================*/ + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="MEMalign">-</a> + + qh_MEMalign + memory alignment for qh_meminitbuffers() in global_r.c + + notes: + to avoid bus errors, memory allocation must consider alignment requirements. + malloc() automatically takes care of alignment. Since mem_r.c manages + its own memory, we need to explicitly specify alignment in + qh_meminitbuffers(). + + A safe choice is sizeof(double). sizeof(float) may be used if doubles + do not occur in data structures and pointers are the same size. Be careful + of machines (e.g., DEC Alpha) with large pointers. + + If using gcc, best alignment is [fmax_() is defined in geom_r.h] + #define qh_MEMalign fmax_(__alignof__(realT),__alignof__(void *)) +*/ +#define qh_MEMalign ((int)(fmax_(sizeof(realT), sizeof(void *)))) + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="MEMbufsize">-</a> + + qh_MEMbufsize + size of additional memory buffers + + notes: + used for qh_meminitbuffers() in global_r.c +*/ +#define qh_MEMbufsize 0x10000 /* allocate 64K memory buffers */ + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="MEMinitbuf">-</a> + + qh_MEMinitbuf + size of initial memory buffer + + notes: + use for qh_meminitbuffers() in global_r.c +*/ +#define qh_MEMinitbuf 0x20000 /* initially allocate 128K buffer */ + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="INFINITE">-</a> + + qh_INFINITE + on output, indicates Voronoi center at infinity +*/ +#define qh_INFINITE -10.101 + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="DEFAULTbox">-</a> + + qh_DEFAULTbox + default box size (Geomview expects 0.5) + + qh_DEFAULTbox + default box size for integer coorindate (rbox only) +*/ +#define qh_DEFAULTbox 0.5 +#define qh_DEFAULTzbox 1e6 + +/*============================================================*/ +/*============= conditional compilation ======================*/ +/*============================================================*/ + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="compiler">-</a> + + __cplusplus + defined by C++ compilers + + __MSC_VER + defined by Microsoft Visual C++ + + __MWERKS__ && __INTEL__ + defined by Metrowerks when compiling for Windows (not Intel-based Macintosh) + + __MWERKS__ && __POWERPC__ + defined by Metrowerks when compiling for PowerPC-based Macintosh + + __STDC__ + defined for strict ANSI C +*/ + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="COMPUTEfurthest">-</a> + + qh_COMPUTEfurthest + compute furthest distance to an outside point instead of storing it with the facet + =1 to compute furthest + + notes: + computing furthest saves memory but costs time + about 40% more distance tests for partitioning + removes facet->furthestdist +*/ +#define qh_COMPUTEfurthest 0 + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="KEEPstatistics">-</a> + + qh_KEEPstatistics + =0 removes most of statistic gathering and reporting + + notes: + if 0, code size is reduced by about 4%. +*/ +#define qh_KEEPstatistics 1 + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="MAXoutside">-</a> + + qh_MAXoutside + record outer plane for each facet + =1 to record facet->maxoutside + + notes: + this takes a realT per facet and slightly slows down qhull + it produces better outer planes for geomview output +*/ +#define qh_MAXoutside 1 + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="NOmerge">-</a> + + qh_NOmerge + disables facet merging if defined + For MSVC compiles, use qhull_r-exports-nomerge.def instead of qhull_r-exports.def + + notes: + This saves about 25% space, 30% space in combination with qh_NOtrace, + and 36% with qh_NOtrace and qh_KEEPstatistics 0 + + Unless option 'Q0' is used + qh_NOmerge sets 'QJ' to avoid precision errors + + see: + <a href="mem_r.h#NOmem">qh_NOmem</a> in mem_r.h + + see user_r.c/user_eg.c for removing io_r.o + + #define qh_NOmerge +*/ + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="NOtrace">-</a> + + qh_NOtrace + no tracing if defined + disables 'Tn', 'TMn', 'TPn' and 'TWn' + override with 'Qw' for qh_addpoint tracing and various other items + + notes: + This saves about 15% space. + Removes all traceN((...)) code and substantial sections of qh.IStracing code + + #define qh_NOtrace +*/ + +#if 0 /* sample code */ + exitcode= qh_new_qhull(qhT *qh, dim, numpoints, points, ismalloc, + flags, outfile, errfile); + qh_freeqhull(qhT *qh, !qh_ALL); /* frees long memory used by second call */ + qh_memfreeshort(qhT *qh, &curlong, &totlong); /* frees short memory and memory allocator */ +#endif + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="QUICKhelp">-</a> + + qh_QUICKhelp + =1 to use abbreviated help messages, e.g., for degenerate inputs +*/ +#define qh_QUICKhelp 0 + +/*============================================================*/ +/*============= merge constants ==============================*/ +/*============================================================*/ +/* + These constants effect facet merging. You probably will not need + to modify them. They effect the performance of facet merging. +*/ + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="BESTcentrum">-</a> + + qh_BESTcentrum + if > 2*dim+n vertices, qh_findbestneighbor() tests centrums (faster) + else, qh_findbestneighbor() tests all vertices (much better merges) + + qh_BESTcentrum2 + if qh_BESTcentrum2 * DIM3 + BESTcentrum < #vertices tests centrums +*/ +#define qh_BESTcentrum 20 +#define qh_BESTcentrum2 2 + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="BESTnonconvex">-</a> + + qh_BESTnonconvex + if > dim+n neighbors, qh_findbestneighbor() tests nonconvex ridges. + + notes: + It is needed because qh_findbestneighbor is slow for large facets +*/ +#define qh_BESTnonconvex 15 + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="COPLANARratio">-</a> + + qh_COPLANARratio + for 3-d+ merging, qh.MINvisible is n*premerge_centrum + + notes: + for non-merging, it's DISTround +*/ +#define qh_COPLANARratio 3 + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="DIMmergeVertex">-</a> + + qh_DIMmergeVertex + max dimension for vertex merging (it is not effective in high-d) +*/ +#define qh_DIMmergeVertex 6 + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="DIMreduceBuild">-</a> + + qh_DIMreduceBuild + max dimension for vertex reduction during build (slow in high-d) +*/ +#define qh_DIMreduceBuild 5 + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="DISToutside">-</a> + + qh_DISToutside + When is a point clearly outside of a facet? + Stops search in qh_findbestnew or qh_partitionall + qh_findbest uses qh.MINoutside since since it is only called if no merges. + + notes: + 'Qf' always searches for best facet + if !qh.MERGING, same as qh.MINoutside. + if qh_USEfindbestnew, increase value since neighboring facets may be ill-behaved + [Note: Zdelvertextot occurs normally with interior points] + RBOX 1000 s Z1 G1e-13 t1001188774 | QHULL Tv + When there is a sharp edge, need to move points to a + clearly good facet; otherwise may be lost in another partitioning. + if too big then O(n^2) behavior for partitioning in cone + if very small then important points not processed + Needed in qh_partitionall for + RBOX 1000 s Z1 G1e-13 t1001032651 | QHULL Tv + Needed in qh_findbestnew for many instances of + RBOX 1000 s Z1 G1e-13 t | QHULL Tv + + See: + qh_DISToutside -- when is a point clearly outside of a facet + qh_SEARCHdist -- when is facet coplanar with the best facet? + qh_USEfindbestnew -- when to use qh_findbestnew for qh_partitionpoint() +*/ +#define qh_DISToutside ((qh_USEfindbestnew ? 2 : 1) * \ + fmax_((qh->MERGING ? 2 : 1)*qh->MINoutside, qh->max_outside)) + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="MAXcheckpoint">-</a> + + qh_MAXcheckpoint + Report up to qh_MAXcheckpoint errors per facet in qh_check_point ('Tv') +*/ +#define qh_MAXcheckpoint 10 + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="MAXcoplanarcentrum">-</a> + + qh_MAXcoplanarcentrum + if pre-merging with qh.MERGEexact ('Qx') and f.nummerge > qh_MAXcoplanarcentrum + use f.maxoutside instead of qh.centrum_radius for coplanarity testing + + notes: + see qh_test_nonsimplicial_merges + with qh.MERGEexact, a coplanar ridge is ignored until post-merging + otherwise a large facet with many merges may take all the facets +*/ +#define qh_MAXcoplanarcentrum 10 + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="MAXnewcentrum">-</a> + + qh_MAXnewcentrum + if <= dim+n vertices (n approximates the number of merges), + reset the centrum in qh_updatetested() and qh_mergecycle_facets() + + notes: + needed to reduce cost and because centrums may move too much if + many vertices in high-d +*/ +#define qh_MAXnewcentrum 5 + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="MAXnewmerges">-</a> + + qh_MAXnewmerges + if >n newmerges, qh_merge_nonconvex() calls qh_reducevertices_centrums. + + notes: + It is needed because postmerge can merge many facets at once +*/ +#define qh_MAXnewmerges 2 + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="RATIOconcavehorizon">-</a> + + qh_RATIOconcavehorizon + ratio of horizon vertex distance to max_outside for concave, twisted new facets in qh_test_nonsimplicial_merge + if too small, end up with vertices far below merged facets +*/ +#define qh_RATIOconcavehorizon 20.0 + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="RATIOconvexmerge">-</a> + + qh_RATIOconvexmerge + ratio of vertex distance to qh.min_vertex for clearly convex new facets in qh_test_nonsimplicial_merge + + notes: + must be convex for MRGtwisted +*/ +#define qh_RATIOconvexmerge 10.0 + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="RATIOcoplanarapex">-</a> + + qh_RATIOcoplanarapex + ratio of best distance for coplanar apex vs. vertex merge in qh_getpinchedmerges + + notes: + A coplanar apex always works, while a vertex merge may fail +*/ +#define qh_RATIOcoplanarapex 3.0 + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="RATIOcoplanaroutside">-</a> + + qh_RATIOcoplanaroutside + qh.MAXoutside ratio to repartition a coplanar point in qh_partitioncoplanar and qh_check_maxout + + notes: + combines several tests, see qh_partitioncoplanar + +*/ +#define qh_RATIOcoplanaroutside 30.0 + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="RATIOmaxsimplex">-</a> + + qh_RATIOmaxsimplex + ratio of max determinate to estimated determinate for searching all points in qh_maxsimplex + + notes: + As each point is added to the simplex, the max determinate is should approximate the previous determinate * qh.MAXwidth + If maxdet is significantly less, the simplex may not be full-dimensional. + If so, all points are searched, stopping at 10 times qh_RATIOmaxsimplex +*/ +#define qh_RATIOmaxsimplex 1.0e-3 + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="RATIOnearinside">-</a> + + qh_RATIOnearinside + ratio of qh.NEARinside to qh.ONEmerge for retaining inside points for + qh_check_maxout(). + + notes: + This is overkill since do not know the correct value. + It effects whether 'Qc' reports all coplanar points + Not used for 'd' since non-extreme points are coplanar, nearly incident points +*/ +#define qh_RATIOnearinside 5 + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="RATIOpinchedsubridge">-</a> + + qh_RATIOpinchedsubridge + ratio to qh.ONEmerge to accept vertices in qh_findbest_pinchedvertex + skips search of neighboring vertices + facet width may increase by this ratio +*/ +#define qh_RATIOpinchedsubridge 10.0 + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="RATIOtrypinched">-</a> + + qh_RATIOtrypinched + ratio to qh.ONEmerge to try qh_getpinchedmerges in qh_buildcone_mergepinched + otherwise a duplicate ridge will increase facet width by this amount +*/ +#define qh_RATIOtrypinched 4.0 + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="RATIOtwisted">-</a> + + qh_RATIOtwisted + maximum ratio to qh.ONEmerge to merge twisted facets in qh_merge_twisted +*/ +#define qh_RATIOtwisted 20.0 + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="SEARCHdist">-</a> + + qh_SEARCHdist + When is a facet coplanar with the best facet? + qh_findbesthorizon: all coplanar facets of the best facet need to be searched. + increases minsearch if ischeckmax and more than 100 neighbors (is_5x_minsearch) + See: + qh_DISToutside -- when is a point clearly outside of a facet + qh_SEARCHdist -- when is facet coplanar with the best facet? + qh_USEfindbestnew -- when to use qh_findbestnew for qh_partitionpoint() +*/ +#define qh_SEARCHdist ((qh_USEfindbestnew ? 2 : 1) * \ + (qh->max_outside + 2 * qh->DISTround + fmax_( qh->MINvisible, qh->MAXcoplanar))); + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="USEfindbestnew">-</a> + + qh_USEfindbestnew + Always use qh_findbestnew for qh_partitionpoint, otherwise use + qh_findbestnew if merged new facet or sharpnewfacets. + + See: + qh_DISToutside -- when is a point clearly outside of a facet + qh_SEARCHdist -- when is facet coplanar with the best facet? + qh_USEfindbestnew -- when to use qh_findbestnew for qh_partitionpoint() +*/ +#define qh_USEfindbestnew (zzval_(Ztotmerge) > 50) + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="MAXnarrow">-</a> + + qh_MAXnarrow + max. cosine in initial hull that sets qh.NARROWhull + + notes: + If qh.NARROWhull, the initial partition does not make + coplanar points. If narrow, a coplanar point can be + coplanar to two facets of opposite orientations and + distant from the exact convex hull. + + Conservative estimate. Don't actually see problems until it is -1.0 +*/ +#define qh_MAXnarrow -0.99999999 + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="WARNnarrow">-</a> + + qh_WARNnarrow + max. cosine in initial hull to warn about qh.NARROWhull + + notes: + this is a conservative estimate. + Don't actually see problems until it is -1.0. See qh-impre.htm +*/ +#define qh_WARNnarrow -0.999999999999999 + +/*-<a href="qh-user_r.htm#TOC" +>--------------------------------</a><a name="WIDEcoplanar">-</a> + + qh_WIDEcoplanar + n*MAXcoplanar or n*MINvisible for a WIDEfacet + + if vertex is further than qh.WIDEfacet from the hyperplane + then its ridges are not counted in computing the area, and + the facet's centrum is frozen. + + notes: + qh.WIDEfacet= max(qh.MAXoutside,qh_WIDEcoplanar*qh.MAXcoplanar, + qh_WIDEcoplanar * qh.MINvisible); +*/ +#define qh_WIDEcoplanar 6 + +/*-<a href="qh-user_r.htm#TOC" +>--------------------------------</a><a name="WIDEduplicate">-</a> + + qh_WIDEduplicate + merge ratio for errexit from qh_forcedmerges due to duplicate ridge + Override with option Q12-allow-wide + + Notes: + Merging a duplicate ridge can lead to very wide facets. +*/ +#define qh_WIDEduplicate 100 + +/*-<a href="qh-user_r.htm#TOC" +>--------------------------------</a><a name="WIDEdupridge">-</a> + + qh_WIDEdupridge + Merge ratio for selecting a forced dupridge merge + + Notes: + Merging a dupridge can lead to very wide facets. +*/ +#define qh_WIDEdupridge 50 + +/*-<a href="qh-user_r.htm#TOC" +>--------------------------------</a><a name="WIDEmaxoutside">-</a> + + qh_WIDEmaxoutside + Precision ratio for maximum increase for qh.max_outside in qh_check_maxout + Precision errors while constructing the hull, may lead to very wide facets when checked in qh_check_maxout + Nearly incident points in 4-d and higher is the most likely culprit + Skip qh_check_maxout with 'Q5' (no-check-outer) + Do not error with option 'Q12' (allow-wide) + Do not warn with options 'Q12 Pp' +*/ +#define qh_WIDEmaxoutside 100 + +/*-<a href="qh-user_r.htm#TOC" +>--------------------------------</a><a name="WIDEmaxoutside2">-</a> + + qh_WIDEmaxoutside2 + Precision ratio for maximum qh.max_outside in qh_check_maxout + Skip qh_check_maxout with 'Q5' no-check-outer + Do not error with option 'Q12' allow-wide +*/ +#define qh_WIDEmaxoutside2 (10*qh_WIDEmaxoutside) + + +/*-<a href="qh-user_r.htm#TOC" +>--------------------------------</a><a name="WIDEpinched">-</a> + + qh_WIDEpinched + Merge ratio for distance between pinched vertices compared to current facet width for qh_getpinchedmerges and qh_next_vertexmerge + Reports warning and merges duplicate ridges instead + Enable these attempts with option Q14 merge-pinched-vertices + + notes: + Merging pinched vertices should prevent duplicate ridges (see qh_WIDEduplicate) + Merging the duplicate ridges may be better than merging the pinched vertices + Found up to 45x ratio for qh_pointdist -- for ((i=1; i<20; i++)); do rbox 175 C1,6e-13 t | qhull d T4 2>&1 | tee x.1 | grep -E 'QH|non-simplicial|Statis|pinched'; done + Actual distance to facets is a third to a tenth of the qh_pointdist (T1) +*/ +#define qh_WIDEpinched 100 + +/*-<a href="qh-user_r.htm#TOC" + >--------------------------------</a><a name="ZEROdelaunay">-</a> + + qh_ZEROdelaunay + a zero Delaunay facet occurs for input sites coplanar with their convex hull + the last normal coefficient of a zero Delaunay facet is within + qh_ZEROdelaunay * qh.ANGLEround of 0 + + notes: + qh_ZEROdelaunay does not allow for joggled input ('QJ'). + + You can avoid zero Delaunay facets by surrounding the input with a box. + + Use option 'PDk:-n' to explicitly define zero Delaunay facets + k= dimension of input sites (e.g., 3 for 3-d Delaunay triangulation) + n= the cutoff for zero Delaunay facets (e.g., 'PD3:-1e-12') +*/ +#define qh_ZEROdelaunay 2 + +/*============================================================*/ +/*============= Microsoft DevStudio ==========================*/ +/*============================================================*/ + +/* + Finding Memory Leaks Using the CRT Library + https://msdn.microsoft.com/en-us/library/x98tx3cf(v=vs.100).aspx + + Reports enabled in qh_lib_check for Debug window and stderr + + From 2005=>msvcr80d, 2010=>msvcr100d, 2012=>msvcr110d + + Watch: {,,msvcr80d.dll}_crtBreakAlloc Value from {n} in the leak report + _CrtSetBreakAlloc(689); // qh_lib_check() [global_r.c] + + Examples + http://free-cad.sourceforge.net/SrcDocu/d2/d7f/MemDebug_8cpp_source.html + https://github.com/illlust/Game/blob/master/library/MemoryLeak.cpp +*/ +#if 0 /* off (0) by default for QHULL_CRTDBG */ +#define QHULL_CRTDBG +#endif + +#if defined(_MSC_VER) && defined(_DEBUG) && defined(QHULL_CRTDBG) +#define _CRTDBG_MAP_ALLOC +#include <stdlib.h> +#include <crtdbg.h> +#endif + +#endif /* qh_DEFuser */ diff --git a/contrib/libs/qhull/libqhull_r/usermem_r.c b/contrib/libs/qhull/libqhull_r/usermem_r.c new file mode 100644 index 0000000000..185a421416 --- /dev/null +++ b/contrib/libs/qhull/libqhull_r/usermem_r.c @@ -0,0 +1,97 @@ +/*<html><pre> -<a href="qh-user_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + usermem_r.c + user redefinable functions -- 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_r.h for data structures, macros, and user-callable functions. + See user_r.c for qhull-related, redefinable functions + see user_r.h for user-definable constants + See userprintf_r.c for qh_fprintf and userprintf_rbox_r.c for qh_fprintf_rbox + + Please report any errors that you fix to qhull@qhull.org +*/ + +#include "libqhull_r.h" + +#include <stdarg.h> +#include <stdlib.h> + +/*-<a href="qh-user_r.htm#TOC" + >-------------------------------</a><a name="qh_exit">-</a> + + qh_exit( exitcode ) + exit program + the exitcode must be 255 or less. Zero indicates success. + Note: Exit status ('$?') in bash reports 256 as 0 + + notes: + qh_exit() is called when qh_errexit() and longjmp() are not available. + + This is the only use of exit() in Qhull + To replace qh_exit with 'throw', see libqhullcpp/usermem_r-cpp.cpp +*/ +void qh_exit(int exitcode) { + exit(exitcode); +} /* exit */ + +/*-<a href="qh-user_r.htm#TOC" + >-------------------------------</a><a name="qh_fprintf_stderr">-</a> + + qh_fprintf_stderr( msgcode, format, list of args ) + fprintf to stderr with msgcode (non-zero) + + notes: + qh_fprintf_stderr() is called when qh.ferr is not defined, usually due to an initialization error + if msgcode is a MSG_ERROR (6000), caller should set qh.last_errcode (like qh_fprintf) or variable 'last_errcode' + + It is typically followed by qh_errexit(). + + Redefine this function to avoid using stderr + + Use qh_fprintf [userprintf_r.c] for normal printing +*/ +void qh_fprintf_stderr(int msgcode, const char *fmt, ... ) { + va_list args; + + va_start(args, fmt); + if(msgcode) + fprintf(stderr, "QH%.4d ", msgcode); + vfprintf(stderr, fmt, args); + va_end(args); +} /* fprintf_stderr */ + +/*-<a href="qh-user_r.htm#TOC" +>-------------------------------</a><a name="qh_free">-</a> + + qh_free(qh, mem ) + free memory + + notes: + same as free() + No calls to qh_errexit() +*/ +void qh_free(void *mem) { + free(mem); +} /* free */ + +/*-<a href="qh-user_r.htm#TOC" + >-------------------------------</a><a name="qh_malloc">-</a> + + qh_malloc( mem ) + allocate memory + + notes: + same as malloc() +*/ +void *qh_malloc(size_t size) { + return malloc(size); +} /* malloc */ + + diff --git a/contrib/libs/qhull/libqhull_r/userprintf_r.c b/contrib/libs/qhull/libqhull_r/userprintf_r.c new file mode 100644 index 0000000000..6512407d92 --- /dev/null +++ b/contrib/libs/qhull/libqhull_r/userprintf_r.c @@ -0,0 +1,95 @@ +/*<html><pre> -<a href="qh-user_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + userprintf_r.c + user redefinable function -- qh_fprintf + + see README.txt see COPYING.txt for copyright information. + + If you recompile and load this file, then userprintf_r.o will not be loaded + from qhull_r.a or qhull_r.lib + + See libqhull_r.h for data structures, macros, and user-callable functions. + See user_r.c for qhull-related, redefinable functions + see user_r.h for user-definable constants + See usermem_r.c for qh_exit(), qh_free(), and qh_malloc() + see Qhull.cpp and RboxPoints.cpp for examples. + + qh_printf is a good location for debugging traps, checked on each log line + + Please report any errors that you fix to qhull@qhull.org +*/ + +#include "libqhull_r.h" +#include "poly_r.h" /* for qh.tracefacet */ + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +/*-<a href="qh-user_r.htm#TOC" + >-------------------------------</a><a name="qh_fprintf">-</a> + + qh_fprintf(qh, fp, msgcode, format, list of args ) + print arguments to *fp according to format + Use qh_fprintf_rbox() for rboxlib_r.c + + notes: + sets qh.last_errcode if msgcode is error 6000..6999 + same as fprintf() + fgets() is not trapped like fprintf() + exit qh_fprintf via qh_errexit() + may be called for errors in qh_initstatistics and qh_meminit +*/ + +void qh_fprintf(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... ) { + va_list args; + facetT *neighbor, **neighborp; + + if (!fp) { + if(!qh){ + qh_fprintf_stderr(6241, "qhull internal error (userprintf_r.c): fp and qh not defined for qh_fprintf '%s'\n", fmt); + qh->last_errcode= 6241; + qh_exit(qh_ERRqhull); /* can not use qh_errexit() */ + } + /* could use qh->qhmem.ferr, but probably better to be cautious */ + qh_fprintf_stderr(6028, "qhull internal error (userprintf_r.c): fp is 0. Wrong qh_fprintf was called.\n"); + qh->last_errcode= 6028; + qh_errexit(qh, qh_ERRqhull, NULL, NULL); + } + if ((qh && qh->ANNOTATEoutput) || msgcode < MSG_TRACE4) { + fprintf(fp, "[QH%.4d]", msgcode); + }else if (msgcode >= MSG_ERROR && msgcode < MSG_STDERR ) { + fprintf(fp, "QH%.4d ", msgcode); + } + va_start(args, fmt); + vfprintf(fp, fmt, args); + va_end(args); + + if (qh) { + if (msgcode >= MSG_ERROR && msgcode < MSG_WARNING) + qh->last_errcode= msgcode; + /* Place debugging traps here. Use with trace option 'Tn' + Set qh.tracefacet_id, qh.traceridge_id, and/or qh.tracevertex_id in global_r.c + */ + if (False) { /* in production skip test for debugging traps */ + if (qh->tracefacet && qh->tracefacet->tested) { + if (qh_setsize(qh, qh->tracefacet->neighbors) < qh->hull_dim) + qh_errexit(qh, qh_ERRdebug, qh->tracefacet, qh->traceridge); + FOREACHneighbor_(qh->tracefacet) { + if (neighbor != qh_DUPLICATEridge && neighbor != qh_MERGEridge && neighbor->visible) + qh_errexit2(qh, qh_ERRdebug, qh->tracefacet, neighbor); + } + } + if (qh->traceridge && qh->traceridge->top->id == 234342223) { + qh_errexit(qh, qh_ERRdebug, qh->tracefacet, qh->traceridge); + } + if (qh->tracevertex && qh_setsize(qh, qh->tracevertex->neighbors)>3434334) { + qh_errexit(qh, qh_ERRdebug, qh->tracefacet, qh->traceridge); + } + } + if (qh->FLUSHprint) + fflush(fp); + } +} /* qh_fprintf */ + diff --git a/contrib/libs/qhull/libqhull_r/userprintf_rbox_r.c b/contrib/libs/qhull/libqhull_r/userprintf_rbox_r.c new file mode 100644 index 0000000000..64c16cb57b --- /dev/null +++ b/contrib/libs/qhull/libqhull_r/userprintf_rbox_r.c @@ -0,0 +1,53 @@ +/*<html><pre> -<a href="qh-user_r.htm" + >-------------------------------</a><a name="TOP">-</a> + + userprintf_rbox_r.c + user redefinable function -- qh_fprintf_rbox + + see README.txt see COPYING.txt for copyright information. + + If you recompile and load this file, then userprintf_rbox_r.o will not be loaded + from qhull.a or qhull.lib + + See libqhull_r.h for data structures, macros, and user-callable functions. + See user_r.c for qhull-related, redefinable functions + see user_r.h for user-definable constants + See usermem_r.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_r.h" + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +/*-<a href="qh-user_r.htm#TOC" + >-------------------------------</a><a name="qh_fprintf_rbox">-</a> + + qh_fprintf_rbox(qh, fp, msgcode, format, list of args ) + print arguments to *fp according to format + Use qh_fprintf_rbox() for rboxlib_r.c + + notes: + same as fprintf() + fgets() is not trapped like fprintf() + exit qh_fprintf_rbox via qh_errexit_rbox() +*/ + +void qh_fprintf_rbox(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... ) { + va_list args; + + if (!fp) { + qh_fprintf_stderr(6231, "qhull internal error (userprintf_rbox_r.c): fp is 0. Wrong qh_fprintf_rbox called.\n"); + qh_errexit_rbox(qh, qh_ERRqhull); + } + if (msgcode >= MSG_ERROR && msgcode < MSG_STDERR) + fprintf(fp, "QH%.4d ", msgcode); + va_start(args, fmt); + vfprintf(fp, fmt, args); + va_end(args); +} /* qh_fprintf_rbox */ + |