diff options
Diffstat (limited to 'src/qhull/src/libqhull')
45 files changed, 32284 insertions, 0 deletions
diff --git a/src/qhull/src/libqhull/DEPRECATED.txt b/src/qhull/src/libqhull/DEPRECATED.txt new file mode 100644 index 000000000..617275c14 --- /dev/null +++ b/src/qhull/src/libqhull/DEPRECATED.txt @@ -0,0 +1,29 @@ + +qhull/src/libqhull + + This directory contains the non-reentrant version of qhull, libqhull. + + New code should use the reentrant version of qhull (libqhull_r). + It allows multiple instances of qhull to run at the same time. On + modern architectures, it is nearly as fast as libqhull. + + Qhull programs may be built with either library. Each program has a + reentrant version (e.g., qconvex_r.c) and a non-reentrant + version (qconvex.c). The programs, rbox, qconvex, qdelaunay, qhalf, + and qvoronoi, are built with libqhull. The qhull program is built + with libqhull_r. + + Qhull's C++ interface requires libqhull_r. If you previously used the + C++ interface, you will need to update your code. See Changes.txt for + suggestions. + + The C code in libqhull looks unusual because of the 'qh' macro. The 'qh' + macro controls access to Qhull's global data structure, qhT. If + 'qh_QHpointer' is defined, 'qh' is 'qh_qh->' and 'qh_qh' is defined as + 'qhT *qh_qh', otherwise 'qh' is 'qh_qh.' and 'qh_qh' is defined as + 'qhT qh_qh'. + + libqhull will be supported indefinitely. The qh_QHpointer variation + of libqhull will be not be retested each release. It is replaced by + libqhull_r. + diff --git a/src/qhull/src/libqhull/Makefile b/src/qhull/src/libqhull/Makefile new file mode 100644 index 000000000..bcad65777 --- /dev/null +++ b/src/qhull/src/libqhull/Makefile @@ -0,0 +1,240 @@ +# Simple gcc Makefile for non-reentrant qhull and rbox (default gcc/g++) +# +# make help +# See README.txt and ../../Makefile +# +# Variables +# BINDIR directory where to copy executables +# DESTDIR destination directory for 'make install' +# DOCDIR directory where to copy html documentation +# INCDIR directory where to copy headers +# LIBDIR directory where to copy libraries +# MANDIR directory where to copy manual pages +# PRINTMAN command for printing manual pages +# PRINTC command for printing C files +# CC ANSI C or C++ compiler +# CC_OPTS1 options used to compile .c files +# CC_OPTS2 options used to link .o files +# CC_OPTS3 options to build shared libraries +# +# LIBQHULL_OBJS .o files for linking +# LIBQHULL_HDRS .h files for printing +# CFILES .c files for printing +# DOCFILES documentation files +# FILES miscellaneous files for printing +# TFILES .txt versions of html files +# FILES all other files +# LIBQHULL_OBJS specifies the object files of libqhullstatic_r.a +# +# Results +# rbox Generates points sets for qhull, qconvex, etc. +# qhull Computes convex hulls and related structures +# qconvex, qdelaunay, qhalf, qvoronoi +# Specializations of qhull for each geometric structure +# libqhullstatic_r.a Static library for non-reentrant qhull +# testqset_r Standalone test of non-reentrant qset_r.c with mem_r.c +# user_eg An example of using qhull (non-reentrant) +# user_eg2 An example of using qhull (non-reentrant) +# +# Make targets +# make Build results using gcc or another compiler +# make clean Remove object files +# make cleanall Remove generated files +# make doc Print documentation +# make help +# make install Copy qhull, rbox, qhull.1, rbox.1 to BINDIR, MANDIR +# make new Rebuild qhull and rbox from source +# make printall Print all files +# make qtest Quick test of qset, rbox, and qhull +# make test Quck test of qhull, qconvex, etc. +# +# Do not replace tabs with spaces. Needed for build rules +# Unix line endings (\n) +# $Id: //main/2015/qhull/src/libqhull/Makefile#8 $ + +DESTDIR = /usr/local +BINDIR = $(DESTDIR)/bin +INCDIR = $(DESTDIR)/include +LIBDIR = $(DESTDIR)/lib +DOCDIR = $(DESTDIR)/share/doc/qhull +MANDIR = $(DESTDIR)/share/man/man1 + +# if you do not have enscript, try a2ps or just use lpr. The files are text. +PRINTMAN = enscript -2rl +PRINTC = enscript -2r +# PRINTMAN = lpr +# PRINTC = lpr + +#for Gnu's gcc compiler, -O3 for optimization, -g for debugging, -pg for profiling +# -fpic needed for gcc x86_64-linux-gnu. Not needed for mingw +CC = gcc +CC_OPTS1 = -O3 -ansi -I../../src -fpic $(CC_WARNINGS) + +# for Sun's cc compiler, -fast or O2 for optimization, -g for debugging, -Xc for ANSI +#CC = cc +#CC_OPTS1 = -Xc -v -fast -I../../src + +# for Silicon Graphics cc compiler, -O2 for optimization, -g for debugging +#CC = cc +#CC_OPTS1 = -ansi -O2 -I../../src + +# for Next cc compiler with fat executable +#CC = cc +#CC_OPTS1 = -ansi -O2 -I../../src -arch m68k -arch i386 -arch hppa + +# For loader, ld, +CC_OPTS2 = $(CC_OPTS1) + +# Default targets for make + +all: qhull_links qhull_all qtest + +help: + head -n 50 Makefile + +clean: + rm -f *.o + # Delete linked files from other directories [qhull_links] + rm -f qconvex.c unix.c qdelaun.c qhalf.c qvoronoi.c rbox.c + rm -f user_eg.c user_eg2.c testqset.c + +cleanall: clean + rm -f qconvex qdelaunay qhalf qvoronoi qhull *.exe + rm -f core user_eg user_eg2 testqset libqhullstatic.a + +doc: + $(PRINTMAN) $(TXTFILES) $(DOCFILES) + +install: + mkdir -p $(BINDIR) + mkdir -p $(DOCDIR) + mkdir -p $(INCDIR)/libqhull + mkdir -p $(MANDIR) + cp -p qconvex qdelaunay qhalf qhull qvoronoi rbox $(BINDIR) + cp -p libqhullstatic.a $(LIBDIR) + cp -p ../../html/qhull.man $(MANDIR)/qhull.1 + cp -p ../../html/rbox.man $(MANDIR)/rbox.1 + cp -p ../../html/* $(DOCDIR) + cp *.h $(INCDIR)/libqhull + +new: cleanall all + +printall: doc printh printc printf + +printh: + $(PRINTC) $(LIBQHULL_HDRS) + +printc: + $(PRINTC) $(CFILES) + +# LIBQHULL_OBJS_1 ordered by frequency of execution with small files at end. Better locality. +# Same definitions as ../../Makefile + +LIBQHULLS_OBJS_1= global.o stat.o geom2.o poly2.o merge.o \ + libqhull.o geom.o poly.o qset.o mem.o random.o + +LIBQHULLS_OBJS_2= $(LIBQHULLS_OBJS_1) usermem.o userprintf.o io.o user.o + +LIBQHULLS_OBJS= $(LIBQHULLS_OBJS_2) rboxlib.o userprintf_rbox.o + +LIBQHULL_HDRS= user.h libqhull.h qhull_a.h geom.h \ + io.h mem.h merge.h poly.h random.h \ + qset.h stat.h + +# CFILES ordered alphabetically after libqhull.c +CFILES= ../qhull/unix.c libqhull.c geom.c geom2.c global.c io.c \ + mem.c merge.c poly.c poly2.c random.c rboxlib.c \ + qset.c stat.c user.c usermem.c userprintf.c \ + ../qconvex/qconvex.c ../qdelaunay/qdelaun.c ../qhalf/qhalf.c ../qvoronoi/qvoronoi.c + +TXTFILES= ../../Announce.txt ../../REGISTER.txt ../../COPYING.txt ../../README.txt ../Changes.txt +DOCFILES= ../../html/rbox.txt ../../html/qhull.txt + +.c.o: + $(CC) -c $(CC_OPTS1) -o $@ $< + +# Work around problems with ../ in Red Hat Linux +qhull_links: + # On MINSYS, 'ln -s' may create a copy instead of a symbolic link + [ -f qconvex.c ] || ln -s ../qconvex/qconvex.c + [ -f qdelaun.c ] || ln -s ../qdelaunay/qdelaun.c + [ -f qhalf.c ] || ln -s ../qhalf/qhalf.c + [ -f qvoronoi.c ] || ln -s ../qvoronoi/qvoronoi.c + [ -f rbox.c ] || ln -s ../rbox/rbox.c + [ -f testqset.c ] || ln -s ../testqset/testqset.c + [ -f unix.c ] || ln -s ../qhull/unix.c + [ -f user_eg.c ] || ln -s ../user_eg/user_eg.c + [ -f user_eg2.c ] || ln -s ../user_eg2/user_eg2.c + +# compile qhull without using bin/libqhullstatic.a +qhull_all: qconvex.o qdelaun.o qhalf.o qvoronoi.o unix.o user_eg.o user_eg2.o rbox.o testqset.o $(LIBQHULLS_OBJS) + $(CC) -o qconvex $(CC_OPTS2) -lm $(LIBQHULLS_OBJS_2) qconvex.o + $(CC) -o qdelaunay $(CC_OPTS2) -lm $(LIBQHULLS_OBJS_2) qdelaun.o + $(CC) -o qhalf $(CC_OPTS2) -lm $(LIBQHULLS_OBJS_2) qhalf.o + $(CC) -o qvoronoi $(CC_OPTS2) -lm $(LIBQHULLS_OBJS_2) qvoronoi.o + $(CC) -o qhull $(CC_OPTS2) -lm $(LIBQHULLS_OBJS_2) unix.o + $(CC) -o rbox $(CC_OPTS2) -lm $(LIBQHULLS_OBJS) rbox.o + $(CC) -o user_eg $(CC_OPTS2) -lm $(LIBQHULLS_OBJS_2) user_eg.o + $(CC) -o user_eg2 $(CC_OPTS2) -lm $(LIBQHULLS_OBJS_1) user_eg2.o usermem.o userprintf.o io.o + $(CC) -o testqset $(CC_OPTS2) -lm mem.o qset.o usermem.o testqset.o + -ar -rs libqhullstatic.a $(LIBQHULLS_OBJS) + #libqhullstatic.a is not needed for qhull + #If 'ar -rs' fails try using 'ar -s' with 'ranlib' + #ranlib libqhullstatic.a + +qtest: + @echo ============================================ + @echo == make qtest ============================== + @echo ============================================ + @echo -n "== " + @date + @echo + @echo Testing qset.c and mem.c with testqset + ./testqset 10000 + @echo Run the qhull smoketest + ./rbox D4 | ./qhull + @echo ============================================ + @echo == To smoketest qhull programs + @echo '== make test' + @echo ============================================ + @echo + @echo ============================================ + @echo == For all make targets + @echo '== make help' + @echo ============================================ + @echo + +test: qtest + @echo ============================== + @echo ========= qconvex ============ + @echo ============================== + -./rbox 10 | ./qconvex Tv + @echo + @echo ============================== + @echo ========= qdelaunay ========== + @echo ============================== + -./rbox 10 | ./qdelaunay Tv + @echo + @echo ============================== + @echo ========= qhalf ============== + @echo ============================== + -./rbox 10 | ./qconvex FQ FV n Tv | ./qhalf Tv + @echo + @echo ============================== + @echo ========= qvoronoi =========== + @echo ============================== + -./rbox 10 | ./qvoronoi Tv + @echo + @echo ============================== + @echo ========= user_eg ============ + @echo == w/o shared library ======== + @echo ============================== + -./user_eg + @echo + @echo ============================== + @echo ========= user_eg2 =========== + @echo ============================== + -./user_eg2 + @echo + +# end of Makefile diff --git a/src/qhull/src/libqhull/Mborland b/src/qhull/src/libqhull/Mborland new file mode 100644 index 000000000..46d88dbd4 --- /dev/null +++ b/src/qhull/src/libqhull/Mborland @@ -0,0 +1,206 @@ +######################################################################### +# Borland C++ 4.02 for Win32 and DOS Power Pack # +# Makefile for qhull and rbox # +# # +# make -fMborland all to produce qconvex, qhull, and rbox # +# make -fMborland user_eg to produce user_eg # +# make -fMborland user_eg2 to produce user_eg2 # +# make -fMborland new to rebuild qhull and rbox from source # +# make -fMborland clean to remove object files # +# make -fMborland cleanall to remove all generated files # +# make -fMborland test to test rbox and qhull # +# # +# Author: D. Zwick of Germany, C.B. Barber # +######################################################################### + +CC = bcc32 # 32 bit compiler for DOS + # bcc32i - Intel's compiler +LINKER = $(CC) # bcc calls tlink32 with needed options +CFLAGS = -w- -A -O2 + # -w- no warnings, bcc doesn't handle assigns in conditions + # -A Ansi standard + # -X no auto-dependency outputs + # -v debugging, use CCOPTS for both + # -O2 optimization +!if $d(_DPMI) +LFLAGS = -WX -w- # -WX loads DPMI library +!else +LFLAGS = -lap -lx -lc + # -lap 32-bit console application + # -lx no map file + # -lc case is significant +!endif + +EXERB = rbox +EXEQH = qhull +EXEQC = qconvex +EXEQD = qdelaunay +EXEQV = qvoronoi +EXEQF = qhalf +EXEEG = user_eg +EXEEG2 = user_eg2 + +TMPFILE = BCC32tmp.cfg + +OBJS1 = global.obj stat.obj geom2.obj poly2.obj merge.obj +OBJS2 = libqhull.obj geom.obj poly.obj qset.obj mem.obj +OBJS3 = random.obj usermem.obj userprintf.obj io.obj user.obj +OBJS4 = rboxlib.obj random.obj usermem.obj userprintf_rbox.obj + +HFILES1 = libqhull.h stat.h qhull_a.h user.h + + +# General rules + +.c.obj: + $(CC) -c $(CFLAGS) $< + +# Default + +all: $(EXERB) $(EXEQH) $(EXEQC) $(EXEQD) $(EXEQV) $(EXEQF) test + +help: + @echo USAGE: + @echo "make all to produce qhull, rbox, qconvex, qdelaun, qvoronoi, qhalf" + @echo "make user_eg to produce user_eg" + @echo "make user_eg2 to produce user_eg2" + @echo "make new to rebuild qhull and rbox from source" + @echo "make clean to remove object files" + @echo "make cleanall to remove all generated file" + @echo "make test to test rbox and qhull" + @echo OPTIONS (default is 32-bit console app): + @echo "-D_DPMI for C++ 4.01 and DOS Power Pack" + +# Executables + +$(EXEQH): ..\..\bin\$(EXEQH).exe + @echo Made ..\..\bin\$(EXEQH).exe + +unix.obj: ..\qhull\unix.c +..\..\bin\$(EXEQH).exe: unix.obj $(OBJS1) $(OBJS2) $(OBJS3) + @echo unix.obj > $(TMPFILE) + @echo $(OBJS1) >> $(TMPFILE) + @echo $(OBJS2) >> $(TMPFILE) + @echo $(OBJS3) >> $(TMPFILE) + $(LINKER) -e$@ $(CFLAGS) $(LFLAGS) @$(TMPFILE) + +$(EXEQC): ..\..\bin\$(EXEQC).exe + @echo Made ..\..\bin\$(EXEQC).exe + +qconvex.obj: ..\qconvex\qconvex.c +..\..\bin\$(EXEQC).exe: qconvex.obj $(OBJS1) $(OBJS2) $(OBJS3) + @echo qconvex.obj > $(TMPFILE) + @echo $(OBJS1) >> $(TMPFILE) + @echo $(OBJS2) >> $(TMPFILE) + @echo $(OBJS3) >> $(TMPFILE) + $(LINKER) -e$@ $(CFLAGS) $(LFLAGS) @$(TMPFILE) + +$(EXEQD): ..\..\bin\$(EXEQD).exe + @echo Made ..\..\bin\$(EXEQD).exe + +qdelaun.obj: ..\qdelaunay\qdelaun.c +..\..\bin\$(EXEQD).exe: qdelaun.obj $(OBJS1) $(OBJS2) $(OBJS3) + @echo qdelaun.obj > $(TMPFILE) + @echo $(OBJS1) >> $(TMPFILE) + @echo $(OBJS2) >> $(TMPFILE) + @echo $(OBJS3) >> $(TMPFILE) + $(LINKER) -e$@ $(CFLAGS) $(LFLAGS) @$(TMPFILE) + +$(EXEQV): ..\..\bin\$(EXEQV).exe + @echo Made ..\..\bin\$(EXEQV).exe + +qvoronoi.obj: ..\qvoronoi\qvoronoi.c +..\..\bin\$(EXEQV).exe: qvoronoi.obj $(OBJS1) $(OBJS2) $(OBJS3) + @echo qvoronoi.obj > $(TMPFILE) + @echo $(OBJS1) >> $(TMPFILE) + @echo $(OBJS2) >> $(TMPFILE) + @echo $(OBJS3) >> $(TMPFILE) + $(LINKER) -e$@ $(CFLAGS) $(LFLAGS) @$(TMPFILE) + +$(EXEQF): ..\..\bin\$(EXEQF).exe + @echo Made ..\..\bin\$(EXEQF).exe + +qhalf.obj: ..\qhalf\qhalf.c +..\..\bin\$(EXEQF).exe: qhalf.obj $(OBJS1) $(OBJS2) $(OBJS3) + @echo qhalf.obj > $(TMPFILE) + @echo $(OBJS1) >> $(TMPFILE) + @echo $(OBJS2) >> $(TMPFILE) + @echo $(OBJS3) >> $(TMPFILE) + $(LINKER) -e$@ $(CFLAGS) $(LFLAGS) @$(TMPFILE) + +$(EXEEG): ..\..\bin\$(EXEEG).exe + @echo Made ..\..\bin\$(EXEEG).exe + +user_eg.obj: ..\user_eg\user_eg.c +..\..\bin\$(EXEEG).exe: user_eg.obj $(OBJS1) $(OBJS2) $(OBJS3) + @echo user_eg.obj > $(TMPFILE) + @echo $(OBJS1) >> $(TMPFILE) + @echo $(OBJS2) >> $(TMPFILE) + @echo $(OBJS3) >> $(TMPFILE) + $(LINKER) -e$@ $(CFLAGS) $(LFLAGS) @$(TMPFILE) + +$(EXEEG2): ..\..\bin\$(EXEEG2).exe + @echo Made ..\..\bin\$(EXEEG2).exe + +user_eg2.obj: ..\user_eg2\user_eg2.c +..\..\bin\$(EXEEG2).exe: user_eg2.obj $(OBJS1) $(OBJS2) $(OBJS3) + @echo user_eg2.obj > $(TMPFILE) + @echo $(OBJS1) >> $(TMPFILE) + @echo $(OBJS2) >> $(TMPFILE) + @echo $(OBJS3) >> $(TMPFILE) + $(LINKER) -e$@ $(CFLAGS) $(LFLAGS) @$(TMPFILE) + +$(EXERB): ..\..\bin\$(EXERB).exe + @echo Made ..\..\bin\$(EXERB).exe + +rbox.obj: ..\rbox\rbox.c +..\..\bin\$(EXERB).exe: rbox.obj $(OBJS4) + @echo rbox.obj > $(TMPFILE) + @echo $(OBJS4) >> $(TMPFILE) + $(LINKER) -e$@ $(CFLAGS) $(LFLAGS) @$(TMPFILE) + +# Test rbox and qhull + +test: + @..\..\bin\rbox D4 > test.x + @..\..\bin\qhull <test.x + @del test.x + +# Clean up + +clean: + @del *.obj + @del $(TMPFILE) + +cleanall: clean + @del ..\..\bin\$(EXERB).exe + @del ..\..\bin\$(EXEQC).exe + @del ..\..\bin\$(EXEQD).exe + @del ..\..\bin\$(EXEQF).exe + @del ..\..\bin\$(EXEQH).exe + @del ..\..\bin\$(EXEQV).exe + @del ..\..\bin\$(EXEEG).exe + @del ..\..\bin\$(EXEEG2).exe + @del ..\q_test.x + @del ..\q_test.log.1 + +# Clean up and rebuild all + +new: cleanall all + +# Header file dependencies + +libqhull.obj stat.obj user.obj global.obj usermem.obj userprintf.obj: $(HFILES1) +random.obj: libqhull.h random.h +geom.obj geom2.obj: $(HFILES1) geom.h +poly.obj poly2.obj: $(HFILES1) poly.h +io.obj: $(HFILES1) io.h +merge.obj: $(HFILES1) merge.h +mem.obj: mem.h +qset.obj: qset.h mem.h +unix.obj: libqhull.h user.h +qconvex.obj: libqhull.h user.h +qdelaun.obj: libqhull.h user.h +qhalf.obj: libqhull.h user.h +qvoronoi.obj: libqhull.h user.h +rbox.obj: user.h diff --git a/src/qhull/src/libqhull/geom.c b/src/qhull/src/libqhull/geom.c new file mode 100644 index 000000000..71444f05a --- /dev/null +++ b/src/qhull/src/libqhull/geom.c @@ -0,0 +1,1234 @@ +/*<html><pre> -<a href="qh-geom.htm" + >-------------------------------</a><a name="TOP">-</a> + + geom.c + geometric routines of qhull + + see qh-geom.htm and geom.h + + Copyright (c) 1993-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull/geom.c#2 $$Change: 1995 $ + $DateTime: 2015/10/13 21:59:42 $$Author: bbarber $ + + infrequent code goes into geom2.c +*/ + +#include "qhull_a.h" + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="distplane">-</a> + + qh_distplane( point, facet, dist ) + return distance from point to facet + + returns: + dist + if qh.RANDOMdist, joggles result + + notes: + dist > 0 if point is above facet (i.e., outside) + does not error (for qh_sortfacets, qh_outerinner) + + see: + qh_distnorm in geom2.c + qh_distplane [geom.c], QhullFacet::distance, and QhullHyperplane::distance are copies +*/ +void qh_distplane(pointT *point, facetT *facet, realT *dist) { + coordT *normal= facet->normal, *coordp, randr; + int k; + + switch (qh hull_dim){ + case 2: + *dist= facet->offset + point[0] * normal[0] + point[1] * normal[1]; + break; + case 3: + *dist= facet->offset + point[0] * normal[0] + point[1] * normal[1] + point[2] * normal[2]; + break; + case 4: + *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]; + break; + case 5: + *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]; + break; + case 6: + *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]+point[5]*normal[5]; + break; + case 7: + *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]+point[5]*normal[5]+point[6]*normal[6]; + break; + case 8: + *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]+point[5]*normal[5]+point[6]*normal[6]+point[7]*normal[7]; + break; + default: + *dist= facet->offset; + coordp= point; + for (k=qh hull_dim; k--; ) + *dist += *coordp++ * *normal++; + break; + } + zinc_(Zdistplane); + if (!qh RANDOMdist && qh IStracing < 4) + return; + if (qh RANDOMdist) { + randr= qh_RANDOMint; + *dist += (2.0 * randr / qh_RANDOMmax - 1.0) * + qh RANDOMfactor * qh MAXabs_coord; + } + if (qh IStracing >= 4) { + qh_fprintf(qh ferr, 8001, "qh_distplane: "); + qh_fprintf(qh ferr, 8002, qh_REAL_1, *dist); + qh_fprintf(qh ferr, 8003, "from p%d to f%d\n", qh_pointid(point), facet->id); + } + return; +} /* distplane */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="findbest">-</a> + + qh_findbest( point, startfacet, bestoutside, qh_ISnewfacets, qh_NOupper, dist, isoutside, numpart ) + find facet that is furthest below a point + for upperDelaunay facets + returns facet only if !qh_NOupper and clearly above + + input: + starts search at 'startfacet' (can not be flipped) + if !bestoutside(qh_ALL), stops at qh.MINoutside + + returns: + best facet (reports error if NULL) + early out if isoutside defined and bestdist > qh.MINoutside + dist is distance to facet + isoutside is true if point is outside of facet + numpart counts the number of distance tests + + see also: + qh_findbestnew() + + notes: + If merging (testhorizon), searches horizon facets of coplanar best facets because + after qh_distplane, this and qh_partitionpoint are the most expensive in 3-d + avoid calls to distplane, function calls, and real number operations. + caller traces result + Optimized for outside points. Tried recording a search set for qh_findhorizon. + Made code more complicated. + + when called by qh_partitionvisible(): + indicated by qh_ISnewfacets + qh.newfacet_list is list of simplicial, new facets + qh_findbestnew set if qh_sharpnewfacets returns True (to use qh_findbestnew) + qh.bestfacet_notsharp set if qh_sharpnewfacets returns False + + when called by qh_findfacet(), qh_partitionpoint(), qh_partitioncoplanar(), + qh_check_bestdist(), qh_addpoint() + indicated by !qh_ISnewfacets + returns best facet in neighborhood of given facet + this is best facet overall if dist > - qh.MAXcoplanar + or hull has at least a "spherical" curvature + + design: + initialize and test for early exit + repeat while there are better facets + for each neighbor of facet + exit if outside facet found + test for better facet + if point is inside and partitioning + test for new facets with a "sharp" intersection + if so, future calls go to qh_findbestnew() + test horizon facets +*/ +facetT *qh_findbest(pointT *point, facetT *startfacet, + boolT bestoutside, boolT isnewfacets, boolT noupper, + realT *dist, boolT *isoutside, int *numpart) { + realT bestdist= -REALmax/2 /* avoid underflow */; + facetT *facet, *neighbor, **neighborp; + facetT *bestfacet= NULL, *lastfacet= NULL; + int oldtrace= qh IStracing; + unsigned int visitid= ++qh visit_id; + int numpartnew=0; + boolT testhorizon = True; /* needed if precise, e.g., rbox c D6 | qhull Q0 Tv */ + + zinc_(Zfindbest); + if (qh IStracing >= 3 || (qh TRACElevel && qh TRACEpoint >= 0 && qh TRACEpoint == qh_pointid(point))) { + if (qh TRACElevel > qh IStracing) + qh IStracing= qh TRACElevel; + qh_fprintf(qh ferr, 8004, "qh_findbest: point p%d starting at f%d isnewfacets? %d, unless %d exit if > %2.2g\n", + qh_pointid(point), startfacet->id, isnewfacets, bestoutside, qh MINoutside); + qh_fprintf(qh ferr, 8005, " testhorizon? %d noupper? %d", testhorizon, noupper); + qh_fprintf(qh ferr, 8006, " Last point added was p%d.", qh furthest_id); + qh_fprintf(qh ferr, 8007, " Last merge was #%d. max_outside %2.2g\n", zzval_(Ztotmerge), qh max_outside); + } + if (isoutside) + *isoutside= True; + if (!startfacet->flipped) { /* test startfacet */ + *numpart= 1; + qh_distplane(point, startfacet, dist); /* this code is duplicated below */ + if (!bestoutside && *dist >= qh MINoutside + && (!startfacet->upperdelaunay || !noupper)) { + bestfacet= startfacet; + goto LABELreturn_best; + } + bestdist= *dist; + if (!startfacet->upperdelaunay) { + bestfacet= startfacet; + } + }else + *numpart= 0; + startfacet->visitid= visitid; + facet= startfacet; + while (facet) { + trace4((qh ferr, 4001, "qh_findbest: neighbors of f%d, bestdist %2.2g f%d\n", + facet->id, bestdist, getid_(bestfacet))); + lastfacet= facet; + FOREACHneighbor_(facet) { + if (!neighbor->newfacet && isnewfacets) + continue; + if (neighbor->visitid == visitid) + continue; + neighbor->visitid= visitid; + if (!neighbor->flipped) { /* code duplicated above */ + (*numpart)++; + qh_distplane(point, neighbor, dist); + if (*dist > bestdist) { + if (!bestoutside && *dist >= qh MINoutside + && (!neighbor->upperdelaunay || !noupper)) { + bestfacet= neighbor; + goto LABELreturn_best; + } + if (!neighbor->upperdelaunay) { + bestfacet= neighbor; + bestdist= *dist; + break; /* switch to neighbor */ + }else if (!bestfacet) { + bestdist= *dist; + break; /* switch to neighbor */ + } + } /* end of *dist>bestdist */ + } /* end of !flipped */ + } /* end of FOREACHneighbor */ + facet= neighbor; /* non-NULL only if *dist>bestdist */ + } /* end of while facet (directed search) */ + if (isnewfacets) { + if (!bestfacet) { + bestdist= -REALmax/2; + bestfacet= qh_findbestnew(point, startfacet->next, &bestdist, bestoutside, isoutside, &numpartnew); + testhorizon= False; /* qh_findbestnew calls qh_findbesthorizon */ + }else if (!qh findbest_notsharp && bestdist < - qh DISTround) { + if (qh_sharpnewfacets()) { + /* seldom used, qh_findbestnew will retest all facets */ + zinc_(Zfindnewsharp); + bestfacet= qh_findbestnew(point, bestfacet, &bestdist, bestoutside, isoutside, &numpartnew); + testhorizon= False; /* qh_findbestnew calls qh_findbesthorizon */ + qh findbestnew= True; + }else + qh findbest_notsharp= True; + } + } + if (!bestfacet) + bestfacet= qh_findbestlower(lastfacet, point, &bestdist, numpart); + if (testhorizon) + bestfacet= qh_findbesthorizon(!qh_IScheckmax, point, bestfacet, noupper, &bestdist, &numpartnew); + *dist= bestdist; + if (isoutside && bestdist < qh MINoutside) + *isoutside= False; +LABELreturn_best: + zadd_(Zfindbesttot, *numpart); + zmax_(Zfindbestmax, *numpart); + (*numpart) += numpartnew; + qh IStracing= oldtrace; + return bestfacet; +} /* findbest */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="findbesthorizon">-</a> + + qh_findbesthorizon( qh_IScheckmax, point, startfacet, qh_NOupper, &bestdist, &numpart ) + search coplanar and better horizon facets from startfacet/bestdist + ischeckmax turns off statistics and minsearch update + all arguments must be initialized + returns(ischeckmax): + best facet + returns(!ischeckmax): + best facet that is not upperdelaunay + allows upperdelaunay that is clearly outside + returns: + bestdist is distance to bestfacet + numpart -- updates number of distance tests + + notes: + no early out -- use qh_findbest() or qh_findbestnew() + Searches coplanar or better horizon facets + + when called by qh_check_maxout() (qh_IScheckmax) + startfacet must be closest to the point + Otherwise, if point is beyond and below startfacet, startfacet may be a local minimum + even though other facets are below the point. + updates facet->maxoutside for good, visited facets + may return NULL + + searchdist is qh.max_outside + 2 * DISTround + + max( MINvisible('Vn'), MAXcoplanar('Un')); + This setting is a guess. It must be at least max_outside + 2*DISTround + because a facet may have a geometric neighbor across a vertex + + design: + for each horizon facet of coplanar best facets + continue if clearly inside + unless upperdelaunay or clearly outside + update best facet +*/ +facetT *qh_findbesthorizon(boolT ischeckmax, pointT* point, facetT *startfacet, boolT noupper, realT *bestdist, int *numpart) { + facetT *bestfacet= startfacet; + realT dist; + facetT *neighbor, **neighborp, *facet; + facetT *nextfacet= NULL; /* optimize last facet of coplanarfacetset */ + int numpartinit= *numpart, coplanarfacetset_size; + unsigned int visitid= ++qh visit_id; + boolT newbest= False; /* for tracing */ + realT minsearch, searchdist; /* skip facets that are too far from point */ + + if (!ischeckmax) { + zinc_(Zfindhorizon); + }else { +#if qh_MAXoutside + if ((!qh ONLYgood || startfacet->good) && *bestdist > startfacet->maxoutside) + startfacet->maxoutside= *bestdist; +#endif + } + searchdist= qh_SEARCHdist; /* multiple of qh.max_outside and precision constants */ + minsearch= *bestdist - searchdist; + if (ischeckmax) { + /* Always check coplanar facets. Needed for RBOX 1000 s Z1 G1e-13 t996564279 | QHULL Tv */ + minimize_(minsearch, -searchdist); + } + coplanarfacetset_size= 0; + facet= startfacet; + while (True) { + trace4((qh ferr, 4002, "qh_findbesthorizon: neighbors of f%d bestdist %2.2g f%d ischeckmax? %d noupper? %d minsearch %2.2g searchdist %2.2g\n", + facet->id, *bestdist, getid_(bestfacet), ischeckmax, noupper, + minsearch, searchdist)); + FOREACHneighbor_(facet) { + if (neighbor->visitid == visitid) + continue; + neighbor->visitid= visitid; + if (!neighbor->flipped) { + qh_distplane(point, neighbor, &dist); + (*numpart)++; + if (dist > *bestdist) { + if (!neighbor->upperdelaunay || ischeckmax || (!noupper && dist >= qh MINoutside)) { + bestfacet= neighbor; + *bestdist= dist; + newbest= True; + if (!ischeckmax) { + minsearch= dist - searchdist; + if (dist > *bestdist + searchdist) { + zinc_(Zfindjump); /* everything in qh.coplanarfacetset at least searchdist below */ + coplanarfacetset_size= 0; + } + } + } + }else if (dist < minsearch) + continue; /* if ischeckmax, dist can't be positive */ +#if qh_MAXoutside + if (ischeckmax && dist > neighbor->maxoutside) + neighbor->maxoutside= dist; +#endif + } /* end of !flipped */ + if (nextfacet) { + if (!coplanarfacetset_size++) { + SETfirst_(qh coplanarfacetset)= nextfacet; + SETtruncate_(qh coplanarfacetset, 1); + }else + qh_setappend(&qh coplanarfacetset, nextfacet); /* Was needed for RBOX 1000 s W1e-13 P0 t996547055 | QHULL d Qbb Qc Tv + and RBOX 1000 s Z1 G1e-13 t996564279 | qhull Tv */ + } + nextfacet= neighbor; + } /* end of EACHneighbor */ + facet= nextfacet; + if (facet) + nextfacet= NULL; + else if (!coplanarfacetset_size) + break; + else if (!--coplanarfacetset_size) { + facet= SETfirstt_(qh coplanarfacetset, facetT); + SETtruncate_(qh coplanarfacetset, 0); + }else + facet= (facetT*)qh_setdellast(qh coplanarfacetset); + } /* while True, for each facet in qh.coplanarfacetset */ + if (!ischeckmax) { + zadd_(Zfindhorizontot, *numpart - numpartinit); + zmax_(Zfindhorizonmax, *numpart - numpartinit); + if (newbest) + zinc_(Zparthorizon); + } + trace4((qh ferr, 4003, "qh_findbesthorizon: newbest? %d bestfacet f%d bestdist %2.2g\n", newbest, getid_(bestfacet), *bestdist)); + return bestfacet; +} /* findbesthorizon */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="findbestnew">-</a> + + qh_findbestnew( point, startfacet, dist, isoutside, numpart ) + find best newfacet for point + searches all of qh.newfacet_list starting at startfacet + searches horizon facets of coplanar best newfacets + searches all facets if startfacet == qh.facet_list + returns: + best new or horizon facet that is not upperdelaunay + early out if isoutside and not 'Qf' + dist is distance to facet + isoutside is true if point is outside of facet + numpart is number of distance tests + + notes: + Always used for merged new facets (see qh_USEfindbestnew) + Avoids upperdelaunay facet unless (isoutside and outside) + + Uses qh.visit_id, qh.coplanarfacetset. + If share visit_id with qh_findbest, coplanarfacetset is incorrect. + + If merging (testhorizon), searches horizon facets of coplanar best facets because + a point maybe coplanar to the bestfacet, below its horizon facet, + and above a horizon facet of a coplanar newfacet. For example, + rbox 1000 s Z1 G1e-13 | qhull + rbox 1000 s W1e-13 P0 t992110337 | QHULL d Qbb Qc + + qh_findbestnew() used if + qh_sharpnewfacets -- newfacets contains a sharp angle + if many merges, qh_premerge found a merge, or 'Qf' (qh.findbestnew) + + see also: + qh_partitionall() and qh_findbest() + + design: + for each new facet starting from startfacet + test distance from point to facet + return facet if clearly outside + unless upperdelaunay and a lowerdelaunay exists + update best facet + test horizon facets +*/ +facetT *qh_findbestnew(pointT *point, facetT *startfacet, + realT *dist, boolT bestoutside, boolT *isoutside, int *numpart) { + realT bestdist= -REALmax/2; + facetT *bestfacet= NULL, *facet; + int oldtrace= qh IStracing, i; + unsigned int visitid= ++qh visit_id; + realT distoutside= 0.0; + boolT isdistoutside; /* True if distoutside is defined */ + boolT testhorizon = True; /* needed if precise, e.g., rbox c D6 | qhull Q0 Tv */ + + if (!startfacet) { + if (qh MERGING) + qh_fprintf(qh ferr, 6001, "qhull precision error (qh_findbestnew): merging has formed and deleted a cone of new facets. Can not continue.\n"); + else + qh_fprintf(qh ferr, 6002, "qhull internal error (qh_findbestnew): no new facets for point p%d\n", + qh furthest_id); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + zinc_(Zfindnew); + if (qh BESToutside || bestoutside) + isdistoutside= False; + else { + isdistoutside= True; + distoutside= qh_DISToutside; /* multiple of qh.MINoutside & qh.max_outside, see user.h */ + } + if (isoutside) + *isoutside= True; + *numpart= 0; + if (qh IStracing >= 3 || (qh TRACElevel && qh TRACEpoint >= 0 && qh TRACEpoint == qh_pointid(point))) { + if (qh TRACElevel > qh IStracing) + qh IStracing= qh TRACElevel; + qh_fprintf(qh ferr, 8008, "qh_findbestnew: point p%d facet f%d. Stop? %d if dist > %2.2g\n", + qh_pointid(point), startfacet->id, isdistoutside, distoutside); + qh_fprintf(qh ferr, 8009, " Last point added p%d visitid %d.", qh furthest_id, visitid); + qh_fprintf(qh ferr, 8010, " Last merge was #%d.\n", zzval_(Ztotmerge)); + } + /* visit all new facets starting with startfacet, maybe qh facet_list */ + for (i=0, facet=startfacet; i < 2; i++, facet= qh newfacet_list) { + FORALLfacet_(facet) { + if (facet == startfacet && i) + break; + facet->visitid= visitid; + if (!facet->flipped) { + qh_distplane(point, facet, dist); + (*numpart)++; + if (*dist > bestdist) { + if (!facet->upperdelaunay || *dist >= qh MINoutside) { + bestfacet= facet; + if (isdistoutside && *dist >= distoutside) + goto LABELreturn_bestnew; + bestdist= *dist; + } + } + } /* end of !flipped */ + } /* FORALLfacet from startfacet or qh newfacet_list */ + } + if (testhorizon || !bestfacet) /* testhorizon is always True. Keep the same code as qh_findbest */ + bestfacet= qh_findbesthorizon(!qh_IScheckmax, point, bestfacet ? bestfacet : startfacet, + !qh_NOupper, &bestdist, numpart); + *dist= bestdist; + if (isoutside && *dist < qh MINoutside) + *isoutside= False; +LABELreturn_bestnew: + zadd_(Zfindnewtot, *numpart); + zmax_(Zfindnewmax, *numpart); + trace4((qh ferr, 4004, "qh_findbestnew: bestfacet f%d bestdist %2.2g\n", getid_(bestfacet), *dist)); + qh IStracing= oldtrace; + return bestfacet; +} /* findbestnew */ + +/* ============ hyperplane functions -- keep code together [?] ============ */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="backnormal">-</a> + + qh_backnormal( rows, numrow, numcol, sign, normal, nearzero ) + given an upper-triangular rows array and a sign, + solve for normal equation x using back substitution over rows U + + returns: + normal= x + + if will not be able to divzero() when normalized(qh.MINdenom_2 and qh.MINdenom_1_2), + if fails on last row + this means that the hyperplane intersects [0,..,1] + sets last coordinate of normal to sign + otherwise + sets tail of normal to [...,sign,0,...], i.e., solves for b= [0...0] + sets nearzero + + notes: + assumes numrow == numcol-1 + + see Golub & van Loan, 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(realT **rows, int numrow, int numcol, boolT sign, + coordT *normal, boolT *nearzero) { + int i, j; + coordT *normalp, *normal_tail, *ai, *ak; + realT diagonal; + boolT waszero; + int zerocol= -1; + + normalp= normal + numcol - 1; + *normalp--= (sign ? -1.0 : 1.0); + for (i=numrow; i--; ) { + *normalp= 0.0; + ai= rows[i] + i + 1; + ak= normalp+1; + for (j=i+1; j < numcol; j++) + *normalp -= *ai++ * *ak++; + diagonal= (rows[i])[i]; + if (fabs_(diagonal) > qh MINdenom_2) + *(normalp--) /= diagonal; + else { + waszero= False; + *normalp= qh_divzero(*normalp, diagonal, qh MINdenom_1_2, &waszero); + if (waszero) { + zerocol= i; + *(normalp--)= (sign ? -1.0 : 1.0); + for (normal_tail= normalp+2; normal_tail < normal + numcol; normal_tail++) + *normal_tail= 0.0; + }else + normalp--; + } + } + if (zerocol != -1) { + zzinc_(Zback0); + *nearzero= True; + trace4((qh ferr, 4005, "qh_backnormal: zero diagonal at column %d.\n", i)); + qh_precision("zero diagonal on back substitution"); + } +} /* backnormal */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="gausselim">-</a> + + qh_gausselim( rows, numrow, numcol, sign ) + Gaussian elimination with partial pivoting + + returns: + rows is upper triangular (includes row exchanges) + flips sign for each row exchange + sets nearzero if pivot[k] < qh.NEARzero[k], else clears it + + notes: + if nearzero, the determinant's sign may be incorrect. + assumes numrow <= numcol + + design: + for each row + determine pivot and exchange rows if necessary + test for near zero + perform gaussian elimination step +*/ +void qh_gausselim(realT **rows, int numrow, int numcol, boolT *sign, boolT *nearzero) { + realT *ai, *ak, *rowp, *pivotrow; + realT n, pivot, pivot_abs= 0.0, temp; + int i, j, k, pivoti, flip=0; + + *nearzero= False; + for (k=0; k < numrow; k++) { + pivot_abs= fabs_((rows[k])[k]); + pivoti= k; + for (i=k+1; i < numrow; i++) { + if ((temp= fabs_((rows[i])[k])) > pivot_abs) { + pivot_abs= temp; + pivoti= i; + } + } + if (pivoti != k) { + rowp= rows[pivoti]; + rows[pivoti]= rows[k]; + rows[k]= rowp; + *sign ^= 1; + flip ^= 1; + } + if (pivot_abs <= qh NEARzero[k]) { + *nearzero= True; + if (pivot_abs == 0.0) { /* remainder of column == 0 */ + if (qh IStracing >= 4) { + qh_fprintf(qh ferr, 8011, "qh_gausselim: 0 pivot at column %d. (%2.2g < %2.2g)\n", k, pivot_abs, qh DISTround); + qh_printmatrix(qh ferr, "Matrix:", rows, numrow, numcol); + } + zzinc_(Zgauss0); + qh_precision("zero pivot for Gaussian elimination"); + goto LABELnextcol; + } + } + pivotrow= rows[k] + k; + pivot= *pivotrow++; /* signed value of pivot, and remainder of row */ + for (i=k+1; i < numrow; i++) { + ai= rows[i] + k; + ak= pivotrow; + n= (*ai++)/pivot; /* divzero() not needed since |pivot| >= |*ai| */ + for (j= numcol - (k+1); j--; ) + *ai++ -= n * *ak++; + } + LABELnextcol: + ; + } + wmin_(Wmindenom, pivot_abs); /* last pivot element */ + if (qh IStracing >= 5) + qh_printmatrix(qh ferr, "qh_gausselem: result", rows, numrow, numcol); +} /* gausselim */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="getangle">-</a> + + qh_getangle( vect1, vect2 ) + returns the dot product of two vectors + if qh.RANDOMdist, joggles result + + notes: + the angle may be > 1.0 or < -1.0 because of roundoff errors + +*/ +realT qh_getangle(pointT *vect1, pointT *vect2) { + realT angle= 0, randr; + int k; + + for (k=qh hull_dim; k--; ) + angle += *vect1++ * *vect2++; + if (qh RANDOMdist) { + randr= qh_RANDOMint; + angle += (2.0 * randr / qh_RANDOMmax - 1.0) * + qh RANDOMfactor; + } + trace4((qh ferr, 4006, "qh_getangle: %2.2g\n", angle)); + return(angle); +} /* getangle */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="getcenter">-</a> + + qh_getcenter( vertices ) + returns arithmetic center of a set of vertices as a new point + + notes: + allocates point array for center +*/ +pointT *qh_getcenter(setT *vertices) { + int k; + pointT *center, *coord; + vertexT *vertex, **vertexp; + int count= qh_setsize(vertices); + + if (count < 2) { + qh_fprintf(qh ferr, 6003, "qhull internal error (qh_getcenter): not defined for %d points\n", count); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + center= (pointT *)qh_memalloc(qh normal_size); + for (k=0; k < qh hull_dim; k++) { + coord= center+k; + *coord= 0.0; + FOREACHvertex_(vertices) + *coord += vertex->point[k]; + *coord /= count; /* count>=2 by QH6003 */ + } + return(center); +} /* getcenter */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="getcentrum">-</a> + + qh_getcentrum( facet ) + returns the centrum for a facet as a new point + + notes: + allocates the centrum +*/ +pointT *qh_getcentrum(facetT *facet) { + realT dist; + pointT *centrum, *point; + + point= qh_getcenter(facet->vertices); + zzinc_(Zcentrumtests); + qh_distplane(point, facet, &dist); + centrum= qh_projectpoint(point, facet, dist); + qh_memfree(point, qh normal_size); + trace4((qh ferr, 4007, "qh_getcentrum: for f%d, %d vertices dist= %2.2g\n", + facet->id, qh_setsize(facet->vertices), dist)); + return centrum; +} /* getcentrum */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="getdistance">-</a> + + qh_getdistance( facet, neighbor, mindist, maxdist ) + returns the maxdist and mindist distance of any vertex from neighbor + + returns: + the max absolute value + + design: + for each vertex of facet that is not in neighbor + test the distance from vertex to neighbor +*/ +realT qh_getdistance(facetT *facet, facetT *neighbor, realT *mindist, realT *maxdist) { + vertexT *vertex, **vertexp; + realT dist, maxd, mind; + + FOREACHvertex_(facet->vertices) + vertex->seen= False; + FOREACHvertex_(neighbor->vertices) + vertex->seen= True; + mind= 0.0; + maxd= 0.0; + FOREACHvertex_(facet->vertices) { + if (!vertex->seen) { + zzinc_(Zbestdist); + qh_distplane(vertex->point, neighbor, &dist); + if (dist < mind) + mind= dist; + else if (dist > maxd) + maxd= dist; + } + } + *mindist= mind; + *maxdist= maxd; + mind= -mind; + if (maxd > mind) + return maxd; + else + return mind; +} /* getdistance */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="normalize">-</a> + + qh_normalize( normal, dim, toporient ) + normalize a vector and report if too small + does not use min norm + + see: + qh_normalize2 +*/ +void qh_normalize(coordT *normal, int dim, boolT toporient) { + qh_normalize2( normal, dim, toporient, NULL, NULL); +} /* normalize */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="normalize2">-</a> + + qh_normalize2( normal, dim, toporient, minnorm, ismin ) + normalize a vector and report if too small + qh.MINdenom/MINdenom1 are the upper limits for divide overflow + + returns: + normalized vector + flips sign if !toporient + if minnorm non-NULL, + sets ismin if normal < minnorm + + notes: + if zero norm + sets all elements to sqrt(1.0/dim) + if divide by zero (divzero()) + sets largest element to +/-1 + bumps Znearlysingular + + design: + computes norm + test for minnorm + if not near zero + normalizes normal + else if zero norm + sets normal to standard value + else + uses qh_divzero to normalize + if nearzero + sets norm to direction of maximum value +*/ +void qh_normalize2(coordT *normal, int dim, boolT toporient, + realT *minnorm, boolT *ismin) { + int k; + realT *colp, *maxp, norm= 0, temp, *norm1, *norm2, *norm3; + boolT zerodiv; + + norm1= normal+1; + norm2= normal+2; + norm3= normal+3; + if (dim == 2) + norm= sqrt((*normal)*(*normal) + (*norm1)*(*norm1)); + else if (dim == 3) + norm= sqrt((*normal)*(*normal) + (*norm1)*(*norm1) + (*norm2)*(*norm2)); + else if (dim == 4) { + norm= sqrt((*normal)*(*normal) + (*norm1)*(*norm1) + (*norm2)*(*norm2) + + (*norm3)*(*norm3)); + }else if (dim > 4) { + norm= (*normal)*(*normal) + (*norm1)*(*norm1) + (*norm2)*(*norm2) + + (*norm3)*(*norm3); + for (k=dim-4, colp=normal+4; k--; colp++) + norm += (*colp) * (*colp); + norm= sqrt(norm); + } + if (minnorm) { + if (norm < *minnorm) + *ismin= True; + else + *ismin= False; + } + wmin_(Wmindenom, norm); + if (norm > qh MINdenom) { + if (!toporient) + norm= -norm; + *normal /= norm; + *norm1 /= norm; + if (dim == 2) + ; /* all done */ + else if (dim == 3) + *norm2 /= norm; + else if (dim == 4) { + *norm2 /= norm; + *norm3 /= norm; + }else if (dim >4) { + *norm2 /= norm; + *norm3 /= norm; + for (k=dim-4, colp=normal+4; k--; ) + *colp++ /= norm; + } + }else if (norm == 0.0) { + temp= sqrt(1.0/dim); + for (k=dim, colp=normal; k--; ) + *colp++ = temp; + }else { + if (!toporient) + norm= -norm; + for (k=dim, colp=normal; k--; colp++) { /* k used below */ + temp= qh_divzero(*colp, norm, qh MINdenom_1, &zerodiv); + if (!zerodiv) + *colp= temp; + else { + maxp= qh_maxabsval(normal, dim); + temp= ((*maxp * norm >= 0.0) ? 1.0 : -1.0); + for (k=dim, colp=normal; k--; colp++) + *colp= 0.0; + *maxp= temp; + zzinc_(Znearlysingular); + trace0((qh ferr, 1, "qh_normalize: norm=%2.2g too small during p%d\n", + norm, qh furthest_id)); + return; + } + } + } +} /* normalize */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="projectpoint">-</a> + + qh_projectpoint( point, facet, dist ) + project point onto a facet by dist + + returns: + returns a new point + + notes: + if dist= distplane(point,facet) + this projects point to hyperplane + assumes qh_memfree_() is valid for normal_size +*/ +pointT *qh_projectpoint(pointT *point, facetT *facet, realT dist) { + pointT *newpoint, *np, *normal; + int normsize= qh normal_size; + int k; + void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */ + + qh_memalloc_(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.htm#TOC" + >-------------------------------</a><a name="setfacetplane">-</a> + + qh_setfacetplane( facet ) + sets the hyperplane for a facet + if qh.RANDOMdist, joggles hyperplane + + notes: + uses global buffers qh.gm_matrix and qh.gm_row + overwrites facet->normal if already defined + updates Wnewvertex if PRINTstatistics + sets facet->upperdelaunay if upper envelope of Delaunay triangulation + + design: + copy vertex coordinates to qh.gm_matrix/gm_row + compute determinate + if nearzero + recompute determinate with gaussian elimination + if nearzero + force outside orientation by testing interior point +*/ +void qh_setfacetplane(facetT *facet) { + pointT *point; + vertexT *vertex, **vertexp; + int normsize= qh normal_size; + int k,i, oldtrace= 0; + realT dist; + void **freelistp; /* used 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_(normsize, freelistp, facet->normal, coordT); + if (facet == qh tracefacet) { + oldtrace= qh IStracing; + qh IStracing= 5; + qh_fprintf(qh ferr, 8012, "qh_setfacetplane: facet f%d created.\n", facet->id); + qh_fprintf(qh ferr, 8013, " Last point added to hull was p%d.", qh furthest_id); + if (zzval_(Ztotmerge)) + qh_fprintf(qh ferr, 8014, " Last merge was #%d.", zzval_(Ztotmerge)); + qh_fprintf(qh ferr, 8015, "\n\nCurrent summary is:\n"); + qh_printsummary(qh ferr); + } + if (qh hull_dim <= 4) { + i= 0; + if (qh RANDOMdist) { + gmcoord= qh gm_matrix; + FOREACHvertex_(facet->vertices) { + qh gm_row[i++]= gmcoord; + coord= vertex->point; + for (k=qh hull_dim; k--; ) + *(gmcoord++)= *coord++ * qh_randomfactor(qh RANDOMa, qh RANDOMb); + } + }else { + FOREACHvertex_(facet->vertices) + qh gm_row[i++]= vertex->point; + } + qh_sethyperplane_det(qh hull_dim, qh gm_row, point0, facet->toporient, + facet->normal, &facet->offset, &nearzero); + } + if (qh hull_dim > 4 || nearzero) { + i= 0; + gmcoord= qh gm_matrix; + FOREACHvertex_(facet->vertices) { + if (vertex->point != point0) { + qh gm_row[i++]= gmcoord; + coord= vertex->point; + point= point0; + for (k=qh hull_dim; k--; ) + *(gmcoord++)= *coord++ - *point++; + } + } + qh gm_row[i]= gmcoord; /* for areasimplex */ + if (qh RANDOMdist) { + gmcoord= qh gm_matrix; + for (i=qh hull_dim-1; i--; ) { + for (k=qh hull_dim; k--; ) + *(gmcoord++) *= qh_randomfactor(qh RANDOMa, qh RANDOMb); + } + } + qh_sethyperplane_gauss(qh hull_dim, qh gm_row, point0, facet->toporient, + facet->normal, &facet->offset, &nearzero); + if (nearzero) { + if (qh_orientoutside(facet)) { + trace0((qh ferr, 2, "qh_setfacetplane: flipped orientation after testing interior_point during p%d\n", qh furthest_id)); + /* this is part of using Gaussian Elimination. For example in 5-d + 1 1 1 1 0 + 1 1 1 1 1 + 0 0 0 1 0 + 0 1 0 0 0 + 1 0 0 0 0 + norm= 0.38 0.38 -0.76 0.38 0 + has a determinate of 1, but g.e. after subtracting pt. 0 has + 0's in the diagonal, even with full pivoting. It does work + if you subtract pt. 4 instead. */ + } + } + } + facet->upperdelaunay= False; + if (qh DELAUNAY) { + if (qh UPPERdelaunay) { /* matches qh_triangulate_facet and qh.lower_threshold in qh_initbuild */ + if (facet->normal[qh hull_dim -1] >= qh ANGLEround * qh_ZEROdelaunay) + facet->upperdelaunay= True; + }else { + if (facet->normal[qh hull_dim -1] > -qh ANGLEround * qh_ZEROdelaunay) + facet->upperdelaunay= True; + } + } + if (qh PRINTstatistics || qh IStracing || qh TRACElevel || qh JOGGLEmax < REALmax) { + qh old_randomdist= qh RANDOMdist; + qh RANDOMdist= False; + FOREACHvertex_(facet->vertices) { + if (vertex->point != point0) { + boolT istrace= False; + zinc_(Zdiststat); + qh_distplane(vertex->point, facet, &dist); + dist= fabs_(dist); + zinc_(Znewvertex); + wadd_(Wnewvertex, dist); + if (dist > wwval_(Wnewvertexmax)) { + wwval_(Wnewvertexmax)= dist; + if (dist > qh max_outside) { + qh max_outside= dist; /* used by qh_maxouter() */ + if (dist > qh TRACEdist) + istrace= True; + } + }else if (-dist > qh TRACEdist) + istrace= True; + if (istrace) { + qh_fprintf(qh ferr, 8016, "qh_setfacetplane: ====== vertex p%d(v%d) increases max_outside to %2.2g for new facet f%d last p%d\n", + qh_pointid(vertex->point), vertex->id, dist, facet->id, qh furthest_id); + qh_errprint("DISTANT", facet, NULL, NULL, NULL); + } + } + } + qh RANDOMdist= qh old_randomdist; + } + if (qh IStracing >= 3) { + qh_fprintf(qh ferr, 8017, "qh_setfacetplane: f%d offset %2.2g normal: ", + facet->id, facet->offset); + for (k=0; k < qh hull_dim; k++) + qh_fprintf(qh ferr, 8018, "%2.2g ", facet->normal[k]); + qh_fprintf(qh ferr, 8019, "\n"); + } + if (facet == qh tracefacet) + qh IStracing= oldtrace; +} /* setfacetplane */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="sethyperplane_det">-</a> + + qh_sethyperplane_det( dim, rows, point0, toporient, normal, offset, nearzero ) + given dim X dim array indexed by rows[], one row per point, + toporient(flips all signs), + and point0 (any row) + set normalized hyperplane equation from oriented simplex + + returns: + normal (normalized) + offset (places point0 on the hyperplane) + sets nearzero if hyperplane not through points + + notes: + only defined for dim == 2..4 + rows[] is not modified + solves det(P-V_0, V_n-V_0, ..., V_1-V_0)=0, i.e. every point is on hyperplane + see Bower & Woodworth, A programmer's geometry, Butterworths 1983. + + derivation of 3-d minnorm + Goal: all vertices V_i within qh.one_merge of hyperplane + Plan: exactly translate the facet so that V_0 is the origin + exactly rotate the facet so that V_1 is on the x-axis and y_2=0. + exactly rotate the effective perturbation to only effect n_0 + this introduces a factor of sqrt(3) + n_0 = ((y_2-y_0)*(z_1-z_0) - (z_2-z_0)*(y_1-y_0)) / norm + Let M_d be the max coordinate difference + Let M_a be the greater of M_d and the max abs. coordinate + Let u be machine roundoff and distround be max error for distance computation + The max error for n_0 is sqrt(3) u M_a M_d / norm. n_1 is approx. 1 and n_2 is approx. 0 + The max error for distance of V_1 is sqrt(3) u M_a M_d M_d / norm. Offset=0 at origin + Then minnorm = 1.8 u M_a M_d M_d / qh.ONEmerge + Note that qh.one_merge is approx. 45.5 u M_a and norm is usually about M_d M_d + + derivation of 4-d minnorm + same as above except rotate the facet so that V_1 on x-axis and w_2, y_3, w_3=0 + [if two vertices fixed on x-axis, can rotate the other two in yzw.] + n_0 = det3_(...) = y_2 det2_(z_1, w_1, z_3, w_3) = - y_2 w_1 z_3 + [all other terms contain at least two factors nearly zero.] + The max error for n_0 is sqrt(4) u M_a M_d M_d / norm + Then minnorm = 2 u M_a M_d M_d M_d / qh.ONEmerge + Note that qh.one_merge is approx. 82 u M_a and norm is usually about M_d M_d M_d +*/ +void qh_sethyperplane_det(int dim, coordT **rows, coordT *point0, + boolT toporient, coordT *normal, realT *offset, boolT *nearzero) { + realT maxround, dist; + int i; + pointT *point; + + + if (dim == 2) { + normal[0]= dY(1,0); + normal[1]= dX(0,1); + qh_normalize2(normal, dim, toporient, NULL, NULL); + *offset= -(point0[0]*normal[0]+point0[1]*normal[1]); + *nearzero= False; /* since nearzero norm => incident points */ + }else if (dim == 3) { + normal[0]= det2_(dY(2,0), dZ(2,0), + dY(1,0), dZ(1,0)); + normal[1]= det2_(dX(1,0), dZ(1,0), + dX(2,0), dZ(2,0)); + normal[2]= det2_(dX(2,0), dY(2,0), + dX(1,0), dY(1,0)); + qh_normalize2(normal, dim, toporient, NULL, NULL); + *offset= -(point0[0]*normal[0] + point0[1]*normal[1] + + point0[2]*normal[2]); + maxround= qh DISTround; + for (i=dim; i--; ) { + point= rows[i]; + if (point != point0) { + dist= *offset + (point[0]*normal[0] + point[1]*normal[1] + + point[2]*normal[2]); + if (dist > maxround || dist < -maxround) { + *nearzero= True; + break; + } + } + } + }else if (dim == 4) { + normal[0]= - det3_(dY(2,0), dZ(2,0), dW(2,0), + dY(1,0), dZ(1,0), dW(1,0), + dY(3,0), dZ(3,0), dW(3,0)); + normal[1]= det3_(dX(2,0), dZ(2,0), dW(2,0), + dX(1,0), dZ(1,0), dW(1,0), + dX(3,0), dZ(3,0), dW(3,0)); + normal[2]= - det3_(dX(2,0), dY(2,0), dW(2,0), + dX(1,0), dY(1,0), dW(1,0), + dX(3,0), dY(3,0), dW(3,0)); + normal[3]= det3_(dX(2,0), dY(2,0), dZ(2,0), + dX(1,0), dY(1,0), dZ(1,0), + dX(3,0), dY(3,0), dZ(3,0)); + qh_normalize2(normal, dim, toporient, NULL, NULL); + *offset= -(point0[0]*normal[0] + point0[1]*normal[1] + + point0[2]*normal[2] + point0[3]*normal[3]); + maxround= qh DISTround; + for (i=dim; i--; ) { + point= rows[i]; + if (point != point0) { + dist= *offset + (point[0]*normal[0] + point[1]*normal[1] + + point[2]*normal[2] + point[3]*normal[3]); + if (dist > maxround || dist < -maxround) { + *nearzero= True; + break; + } + } + } + } + if (*nearzero) { + zzinc_(Zminnorm); + trace0((qh ferr, 3, "qh_sethyperplane_det: degenerate norm during p%d.\n", qh furthest_id)); + zzinc_(Znearlysingular); + } +} /* sethyperplane_det */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="sethyperplane_gauss">-</a> + + qh_sethyperplane_gauss( dim, rows, point0, toporient, normal, offset, nearzero ) + given(dim-1) X dim array of rows[i]= V_{i+1} - V_0 (point0) + set normalized hyperplane equation from oriented simplex + + returns: + normal (normalized) + offset (places point0 on the hyperplane) + + notes: + if nearzero + orientation may be incorrect because of incorrect sign flips in gausselim + solves [V_n-V_0,...,V_1-V_0, 0 .. 0 1] * N == [0 .. 0 1] + or [V_n-V_0,...,V_1-V_0, 0 .. 0 1] * N == [0] + i.e., N is normal to the hyperplane, and the unnormalized + distance to [0 .. 1] is either 1 or 0 + + design: + perform gaussian elimination + flip sign for negative values + perform back substitution + normalize result + compute offset +*/ +void qh_sethyperplane_gauss(int dim, coordT **rows, pointT *point0, + boolT toporient, coordT *normal, coordT *offset, boolT *nearzero) { + coordT *pointcoord, *normalcoef; + int k; + boolT sign= toporient, nearzero2= False; + + qh_gausselim(rows, dim-1, dim, &sign, nearzero); + for (k=dim-1; k--; ) { + if ((rows[k])[k] < 0) + sign ^= 1; + } + if (*nearzero) { + zzinc_(Znearlysingular); + trace0((qh ferr, 4, "qh_sethyperplane_gauss: nearly singular or axis parallel hyperplane during p%d.\n", qh furthest_id)); + qh_backnormal(rows, dim-1, dim, sign, normal, &nearzero2); + }else { + qh_backnormal(rows, dim-1, dim, sign, normal, &nearzero2); + if (nearzero2) { + zzinc_(Znearlysingular); + trace0((qh ferr, 5, "qh_sethyperplane_gauss: singular or axis parallel hyperplane at normalization during p%d.\n", qh furthest_id)); + } + } + if (nearzero2) + *nearzero= True; + qh_normalize2(normal, dim, True, NULL, NULL); + pointcoord= point0; + normalcoef= normal; + *offset= -(*pointcoord++ * *normalcoef++); + for (k=dim-1; k--; ) + *offset -= *pointcoord++ * *normalcoef++; +} /* sethyperplane_gauss */ + + + diff --git a/src/qhull/src/libqhull/geom.h b/src/qhull/src/libqhull/geom.h new file mode 100644 index 000000000..16ef48d2d --- /dev/null +++ b/src/qhull/src/libqhull/geom.h @@ -0,0 +1,176 @@ +/*<html><pre> -<a href="qh-geom.htm" + >-------------------------------</a><a name="TOP">-</a> + + geom.h + header file for geometric routines + + see qh-geom.htm and geom.c + + Copyright (c) 1993-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull/geom.h#1 $$Change: 1981 $ + $DateTime: 2015/09/28 20:26:32 $$Author: bbarber $ +*/ + +#ifndef qhDEFgeom +#define qhDEFgeom 1 + +#include "libqhull.h" + +/* ============ -macros- ======================== */ + +/*-<a href="qh-geom.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.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.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.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.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.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.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.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 ======= */ + +void qh_backnormal(realT **rows, int numrow, int numcol, boolT sign, coordT *normal, boolT *nearzero); +void qh_distplane(pointT *point, facetT *facet, realT *dist); +facetT *qh_findbest(pointT *point, facetT *startfacet, + boolT bestoutside, boolT isnewfacets, boolT noupper, + realT *dist, boolT *isoutside, int *numpart); +facetT *qh_findbesthorizon(boolT ischeckmax, pointT *point, + facetT *startfacet, boolT noupper, realT *bestdist, int *numpart); +facetT *qh_findbestnew(pointT *point, facetT *startfacet, realT *dist, + boolT bestoutside, boolT *isoutside, int *numpart); +void qh_gausselim(realT **rows, int numrow, int numcol, boolT *sign, boolT *nearzero); +realT qh_getangle(pointT *vect1, pointT *vect2); +pointT *qh_getcenter(setT *vertices); +pointT *qh_getcentrum(facetT *facet); +realT qh_getdistance(facetT *facet, facetT *neighbor, realT *mindist, realT *maxdist); +void qh_normalize(coordT *normal, int dim, boolT toporient); +void qh_normalize2(coordT *normal, int dim, boolT toporient, + realT *minnorm, boolT *ismin); +pointT *qh_projectpoint(pointT *point, facetT *facet, realT dist); + +void qh_setfacetplane(facetT *newfacets); +void qh_sethyperplane_det(int dim, coordT **rows, coordT *point0, + boolT toporient, coordT *normal, realT *offset, boolT *nearzero); +void qh_sethyperplane_gauss(int dim, coordT **rows, pointT *point0, + boolT toporient, coordT *normal, coordT *offset, boolT *nearzero); +boolT qh_sharpnewfacets(void); + +/*========= infrequently used code in geom2.c =============*/ + +coordT *qh_copypoints(coordT *points, int numpoints, int dimension); +void qh_crossproduct(int dim, realT vecA[3], realT vecB[3], realT vecC[3]); +realT qh_determinant(realT **rows, int dim, boolT *nearzero); +realT qh_detjoggle(pointT *points, int numpoints, int dimension); +void qh_detroundoff(void); +realT qh_detsimplex(pointT *apex, setT *points, int dim, boolT *nearzero); +realT qh_distnorm(int dim, pointT *point, pointT *normal, realT *offsetp); +realT qh_distround(int dimension, realT maxabs, realT maxsumabs); +realT qh_divzero(realT numer, realT denom, realT mindenom1, boolT *zerodiv); +realT qh_facetarea(facetT *facet); +realT qh_facetarea_simplex(int dim, coordT *apex, setT *vertices, + vertexT *notvertex, boolT toporient, coordT *normal, realT *offset); +pointT *qh_facetcenter(setT *vertices); +facetT *qh_findgooddist(pointT *point, facetT *facetA, realT *distp, facetT **facetlist); +void qh_getarea(facetT *facetlist); +boolT qh_gram_schmidt(int dim, realT **rows); +boolT qh_inthresholds(coordT *normal, realT *angle); +void qh_joggleinput(void); +realT *qh_maxabsval(realT *normal, int dim); +setT *qh_maxmin(pointT *points, int numpoints, int dimension); +realT qh_maxouter(void); +void qh_maxsimplex(int dim, setT *maxpoints, pointT *points, int numpoints, setT **simplex); +realT qh_minabsval(realT *normal, int dim); +int qh_mindiff(realT *vecA, realT *vecB, int dim); +boolT qh_orientoutside(facetT *facet); +void qh_outerinner(facetT *facet, realT *outerplane, realT *innerplane); +coordT qh_pointdist(pointT *point1, pointT *point2, int dim); +void qh_printmatrix(FILE *fp, const char *string, realT **rows, int numrow, int numcol); +void qh_printpoints(FILE *fp, const char *string, setT *points); +void qh_projectinput(void); +void qh_projectpoints(signed char *project, int n, realT *points, + int numpoints, int dim, realT *newpoints, int newdim); +void qh_rotateinput(realT **rows); +void qh_rotatepoints(realT *points, int numpoints, int dim, realT **rows); +void qh_scaleinput(void); +void qh_scalelast(coordT *points, int numpoints, int dim, coordT low, + coordT high, coordT newhigh); +void qh_scalepoints(pointT *points, int numpoints, int dim, + realT *newlows, realT *newhighs); +boolT qh_sethalfspace(int dim, coordT *coords, coordT **nextp, + coordT *normal, coordT *offset, coordT *feasible); +coordT *qh_sethalfspace_all(int dim, int count, coordT *halfspaces, pointT *feasible); +pointT *qh_voronoi_center(int dim, setT *points); + +#endif /* qhDEFgeom */ + + + diff --git a/src/qhull/src/libqhull/geom2.c b/src/qhull/src/libqhull/geom2.c new file mode 100644 index 000000000..82ec4936e --- /dev/null +++ b/src/qhull/src/libqhull/geom2.c @@ -0,0 +1,2094 @@ +/*<html><pre> -<a href="qh-geom.htm" + >-------------------------------</a><a name="TOP">-</a> + + + geom2.c + infrequently used geometric routines of qhull + + see qh-geom.htm and geom.h + + Copyright (c) 1993-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull/geom2.c#6 $$Change: 2065 $ + $DateTime: 2016/01/18 13:51:04 $$Author: bbarber $ + + frequently used code goes into geom.c +*/ + +#include "qhull_a.h" + +/*================== functions in alphabetic order ============*/ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="copypoints">-</a> + + qh_copypoints( points, numpoints, dimension) + return qh_malloc'd copy of points + notes: + qh_free the returned points to avoid a memory leak +*/ +coordT *qh_copypoints(coordT *points, int numpoints, int dimension) { + int size; + coordT *newpoints; + + size= numpoints * dimension * (int)sizeof(coordT); + if (!(newpoints=(coordT*)qh_malloc((size_t)size))) { + qh_fprintf(qh ferr, 6004, "qhull error: insufficient memory to copy %d points\n", + numpoints); + qh_errexit(qh_ERRmem, NULL, NULL); + } + memcpy((char *)newpoints, (char *)points, (size_t)size); /* newpoints!=0 by QH6004 */ + return newpoints; +} /* copypoints */ + +/*-<a href="qh-geom.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.htm#TOC" + >-------------------------------</a><a name="determinant">-</a> + + qh_determinant( rows, dim, nearzero ) + compute signed determinant of a square matrix + uses qh.NEARzero to test for degenerate matrices + + returns: + determinant + overwrites rows and the matrix + if dim == 2 or 3 + nearzero iff determinant < qh NEARzero[dim-1] + (!quite correct, not critical) + if dim >= 4 + nearzero iff diagonal[k] < qh NEARzero[k] +*/ +realT qh_determinant(realT **rows, int dim, boolT *nearzero) { + realT det=0; + int i; + boolT sign= False; + + *nearzero= False; + if (dim < 2) { + qh_fprintf(qh ferr, 6005, "qhull internal error (qh_determinate): only implemented for dimension >= 2\n"); + qh_errexit(qh_ERRqhull, NULL, NULL); + }else if (dim == 2) { + det= det2_(rows[0][0], rows[0][1], + rows[1][0], rows[1][1]); + if (fabs_(det) < 10*qh NEARzero[1]) /* not really correct, what should this be? */ + *nearzero= True; + }else if (dim == 3) { + det= det3_(rows[0][0], rows[0][1], rows[0][2], + rows[1][0], rows[1][1], rows[1][2], + rows[2][0], rows[2][1], rows[2][2]); + if (fabs_(det) < 10*qh NEARzero[2]) /* 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(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.htm#TOC" + >-------------------------------</a><a name="detjoggle">-</a> + + qh_detjoggle( points, numpoints, dimension ) + determine default max joggle for point array + as qh_distround * qh_JOGGLEdefault + + returns: + initial value for JOGGLEmax from points and REALepsilon + + notes: + computes DISTround since qh_maxmin not called yet + if qh SCALElast, last dimension will be scaled later to MAXwidth + + loop duplicated from qh_maxmin +*/ +realT qh_detjoggle(pointT *points, int numpoints, int dimension) { + realT abscoord, distround, joggle, maxcoord, mincoord; + pointT *point, *pointtemp; + realT maxabs= -REALmax; + realT sumabs= 0; + realT maxwidth= 0; + int k; + + for (k=0; k < dimension; k++) { + if (qh SCALElast && k == dimension-1) + abscoord= maxwidth; + else if (qh DELAUNAY && k == dimension-1) /* will qh_setdelaunay() */ + abscoord= 2 * maxabs * maxabs; /* may be low by qh hull_dim/2 */ + else { + maxcoord= -REALmax; + mincoord= REALmax; + FORALLpoint_(points, numpoints) { + maximize_(maxcoord, point[k]); + minimize_(mincoord, point[k]); + } + maximize_(maxwidth, maxcoord-mincoord); + abscoord= fmax_(maxcoord, -mincoord); + } + sumabs += abscoord; + maximize_(maxabs, abscoord); + } /* for k */ + distround= qh_distround(qh hull_dim, maxabs, sumabs); + joggle= distround * qh_JOGGLEdefault; + maximize_(joggle, REALepsilon * qh_JOGGLEdefault); + trace2((qh ferr, 2001, "qh_detjoggle: joggle=%2.2g maxwidth=%2.2g\n", joggle, maxwidth)); + return joggle; +} /* detjoggle */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="detroundoff">-</a> + + qh_detroundoff() + determine maximum roundoff errors from + REALepsilon, REALmax, REALmin, qh.hull_dim, qh.MAXabs_coord, + qh.MAXsumcoord, qh.MAXwidth, qh.MINdenom_1 + + accounts for qh.SETroundoff, qh.RANDOMdist, qh MERGEexact + qh.premerge_cos, qh.postmerge_cos, qh.premerge_centrum, + qh.postmerge_centrum, qh.MINoutside, + qh_RATIOnearinside, qh_COPLANARratio, qh_WIDEcoplanar + + returns: + sets qh.DISTround, etc. (see below) + appends precision constants to qh.qhull_options + + see: + qh_maxmin() for qh.NEARzero + + design: + determine qh.DISTround for distance computations + determine minimum denominators for qh_divzero + determine qh.ANGLEround for angle computations + adjust qh.premerge_cos,... for roundoff error + determine qh.ONEmerge for maximum error due to a single merge + determine qh.NEARinside, qh.MAXcoplanar, qh.MINvisible, + qh.MINoutside, qh.WIDEfacet + initialize qh.max_vertex and qh.minvertex +*/ +void qh_detroundoff(void) { + + qh_option("_max-width", NULL, &qh MAXwidth); + if (!qh SETroundoff) { + qh DISTround= qh_distround(qh hull_dim, qh MAXabs_coord, qh MAXsumcoord); + if (qh RANDOMdist) + qh DISTround += qh RANDOMfactor * qh MAXabs_coord; + qh_option("Error-roundoff", NULL, &qh DISTround); + } + qh MINdenom= qh MINdenom_1 * qh MAXabs_coord; + qh MINdenom_1_2= sqrt(qh MINdenom_1 * qh hull_dim) ; /* if will be normalized */ + qh MINdenom_2= qh MINdenom_1_2 * qh MAXabs_coord; + /* for inner product */ + qh ANGLEround= 1.01 * qh hull_dim * REALepsilon; + if (qh RANDOMdist) + qh ANGLEround += qh RANDOMfactor; + if (qh premerge_cos < REALmax/2) { + qh premerge_cos -= qh ANGLEround; + if (qh RANDOMdist) + qh_option("Angle-premerge-with-random", NULL, &qh premerge_cos); + } + if (qh postmerge_cos < REALmax/2) { + qh postmerge_cos -= qh ANGLEround; + if (qh RANDOMdist) + qh_option("Angle-postmerge-with-random", NULL, &qh postmerge_cos); + } + qh premerge_centrum += 2 * qh DISTround; /*2 for centrum and distplane()*/ + qh postmerge_centrum += 2 * qh DISTround; + if (qh RANDOMdist && (qh MERGEexact || qh PREmerge)) + qh_option("Centrum-premerge-with-random", NULL, &qh premerge_centrum); + if (qh RANDOMdist && qh POSTmerge) + qh_option("Centrum-postmerge-with-random", NULL, &qh postmerge_centrum); + { /* compute ONEmerge, max vertex offset for merging simplicial facets */ + realT maxangle= 1.0, maxrho; + + minimize_(maxangle, qh premerge_cos); + minimize_(maxangle, qh postmerge_cos); + /* max diameter * sin theta + DISTround for vertex to its hyperplane */ + qh ONEmerge= sqrt((realT)qh hull_dim) * qh MAXwidth * + sqrt(1.0 - maxangle * maxangle) + qh DISTround; + maxrho= qh hull_dim * qh premerge_centrum + qh DISTround; + maximize_(qh ONEmerge, maxrho); + maxrho= qh hull_dim * qh postmerge_centrum + qh DISTround; + maximize_(qh ONEmerge, maxrho); + if (qh MERGING) + qh_option("_one-merge", NULL, &qh ONEmerge); + } + qh NEARinside= qh ONEmerge * qh_RATIOnearinside; /* only used if qh KEEPnearinside */ + if (qh JOGGLEmax < REALmax/2 && (qh KEEPcoplanar || qh KEEPinside)) { + realT maxdist; /* adjust qh.NEARinside for joggle */ + qh KEEPnearinside= True; + maxdist= sqrt((realT)qh hull_dim) * qh JOGGLEmax + qh DISTround; + maxdist= 2*maxdist; /* vertex and coplanar point can joggle in opposite directions */ + maximize_(qh NEARinside, maxdist); /* must agree with qh_nearcoplanar() */ + } + if (qh KEEPnearinside) + qh_option("_near-inside", NULL, &qh NEARinside); + if (qh JOGGLEmax < qh DISTround) { + qh_fprintf(qh ferr, 6006, "qhull error: the joggle for 'QJn', %.2g, is below roundoff for distance computations, %.2g\n", + qh JOGGLEmax, qh DISTround); + qh_errexit(qh_ERRinput, NULL, NULL); + } + if (qh MINvisible > REALmax/2) { + if (!qh MERGING) + qh MINvisible= qh DISTround; + else if (qh hull_dim <= 3) + qh MINvisible= qh premerge_centrum; + else + qh MINvisible= qh_COPLANARratio * qh premerge_centrum; + if (qh APPROXhull && qh MINvisible > qh MINoutside) + qh MINvisible= qh MINoutside; + qh_option("Visible-distance", NULL, &qh MINvisible); + } + if (qh MAXcoplanar > REALmax/2) { + qh MAXcoplanar= qh MINvisible; + qh_option("U-coplanar-distance", NULL, &qh MAXcoplanar); + } + if (!qh APPROXhull) { /* user may specify qh MINoutside */ + qh MINoutside= 2 * qh MINvisible; + if (qh premerge_cos < REALmax/2) + maximize_(qh MINoutside, (1- qh premerge_cos) * qh MAXabs_coord); + qh_option("Width-outside", NULL, &qh MINoutside); + } + qh WIDEfacet= qh MINoutside; + maximize_(qh WIDEfacet, qh_WIDEcoplanar * qh MAXcoplanar); + maximize_(qh WIDEfacet, qh_WIDEcoplanar * qh MINvisible); + qh_option("_wide-facet", NULL, &qh WIDEfacet); + if (qh MINvisible > qh MINoutside + 3 * REALepsilon + && !qh BESToutside && !qh FORCEoutput) + qh_fprintf(qh ferr, 7001, "qhull input warning: minimum visibility V%.2g is greater than \nminimum outside W%.2g. Flipped facets are likely.\n", + qh MINvisible, qh MINoutside); + qh max_vertex= qh DISTround; + qh min_vertex= -qh DISTround; + /* numeric constants reported in printsummary */ +} /* detroundoff */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="detsimplex">-</a> + + qh_detsimplex( apex, points, dim, nearzero ) + compute determinant of a simplex with point apex and base points + + returns: + signed determinant and nearzero from qh_determinant + + notes: + uses qh.gm_matrix/qh.gm_row (assumes they're big enough) + + design: + construct qm_matrix by subtracting apex from points + compute determinate +*/ +realT qh_detsimplex(pointT *apex, setT *points, int dim, boolT *nearzero) { + pointT *coorda, *coordp, *gmcoord, *point, **pointp; + coordT **rows; + int k, i=0; + realT det; + + zinc_(Zdetsimplex); + gmcoord= qh gm_matrix; + rows= qh gm_row; + FOREACHpoint_(points) { + if (i == dim) + break; + rows[i++]= gmcoord; + coordp= point; + coorda= apex; + for (k=dim; k--; ) + *(gmcoord++)= *coordp++ - *coorda++; + } + if (i < dim) { + qh_fprintf(qh ferr, 6007, "qhull internal error (qh_detsimplex): #points %d < dimension %d\n", + i, dim); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + det= qh_determinant(rows, dim, nearzero); + trace2((qh ferr, 2002, "qh_detsimplex: det=%2.2g for point p%d, dim %d, nearzero? %d\n", + det, qh_pointid(apex), dim, *nearzero)); + return det; +} /* detsimplex */ + +/*-<a href="qh-geom.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.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.htm#TOC" + >-------------------------------</a><a name="distround">-</a> + + qh_distround(dimension, maxabs, maxsumabs ) + compute maximum round-off error for a distance computation + to a normalized hyperplane + maxabs is the maximum absolute value of a coordinate + maxsumabs is the maximum possible sum of absolute coordinate values + + returns: + max dist round for REALepsilon + + notes: + calculate roundoff error according to 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(int dimension, realT maxabs, realT maxsumabs) { + realT maxdistsum, maxround; + + maxdistsum= sqrt((realT)dimension) * maxabs; + minimize_( maxdistsum, maxsumabs); + maxround= REALepsilon * (dimension * maxdistsum * 1.01 + maxabs); + /* adds maxabs for offset */ + trace4((qh ferr, 4008, "qh_distround: %2.2g maxabs %2.2g maxsumabs %2.2g maxdistsum %2.2g\n", + maxround, maxabs, maxsumabs, maxdistsum)); + return maxround; +} /* distround */ + +/*-<a href="qh-geom.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.htm#TOC" + >-------------------------------</a><a name="facetarea">-</a> + + qh_facetarea( facet ) + return area for a facet + + notes: + if non-simplicial, + uses centrum to triangulate facet and sums the projected areas. + if (qh DELAUNAY), + computes projected area instead for last coordinate + assumes facet->normal exists + projecting tricoplanar facets to the hyperplane does not appear to make a difference + + design: + if simplicial + compute area + else + for each ridge + compute area from centrum to ridge + negate area if upper Delaunay facet +*/ +realT qh_facetarea(facetT *facet) { + vertexT *apex; + pointT *centrum; + realT area= 0.0; + ridgeT *ridge, **ridgep; + + if (facet->simplicial) { + apex= SETfirstt_(facet->vertices, vertexT); + area= qh_facetarea_simplex(qh hull_dim, apex->point, facet->vertices, + apex, facet->toporient, facet->normal, &facet->offset); + }else { + if (qh CENTERtype == qh_AScentrum) + centrum= facet->center; + else + centrum= qh_getcentrum(facet); + FOREACHridge_(facet->ridges) + area += qh_facetarea_simplex(qh hull_dim, centrum, ridge->vertices, + NULL, (boolT)(ridge->top == facet), facet->normal, &facet->offset); + if (qh CENTERtype != qh_AScentrum) + qh_memfree(centrum, qh normal_size); + } + if (facet->upperdelaunay && qh DELAUNAY) + area= -area; /* the normal should be [0,...,1] */ + trace4((qh ferr, 4009, "qh_facetarea: f%d area %2.2g\n", facet->id, area)); + return area; +} /* facetarea */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="facetarea_simplex">-</a> + + qh_facetarea_simplex( dim, apex, vertices, notvertex, toporient, normal, offset ) + return area for a simplex defined by + an apex, a base of vertices, an orientation, and a unit normal + if simplicial or tricoplanar facet, + notvertex is defined and it is skipped in vertices + + returns: + computes area of simplex projected to plane [normal,offset] + returns 0 if vertex too far below plane (qh WIDEfacet) + vertex can't be apex of tricoplanar facet + + notes: + if (qh DELAUNAY), + computes projected area instead for last coordinate + uses qh gm_matrix/gm_row and qh hull_dim + helper function for qh_facetarea + + design: + if Notvertex + translate simplex to apex + else + project simplex to normal/offset + translate simplex to apex + if Delaunay + set last row/column to 0 with -1 on diagonal + else + set last row to Normal + compute determinate + scale and flip sign for area +*/ +realT qh_facetarea_simplex(int dim, coordT *apex, setT *vertices, + vertexT *notvertex, boolT toporient, coordT *normal, realT *offset) { + pointT *coorda, *coordp, *gmcoord; + coordT **rows, *normalp; + int k, i=0; + realT area, dist; + vertexT *vertex, **vertexp; + boolT nearzero; + + gmcoord= qh gm_matrix; + rows= qh gm_row; + FOREACHvertex_(vertices) { + if (vertex == notvertex) + continue; + rows[i++]= gmcoord; + coorda= apex; + coordp= vertex->point; + normalp= normal; + if (notvertex) { + for (k=dim; k--; ) + *(gmcoord++)= *coordp++ - *coorda++; + }else { + dist= *offset; + for (k=dim; k--; ) + dist += *coordp++ * *normalp++; + if (dist < -qh WIDEfacet) { + zinc_(Znoarea); + return 0.0; + } + coordp= vertex->point; + normalp= normal; + for (k=dim; k--; ) + *(gmcoord++)= (*coordp++ - dist * *normalp++) - *coorda++; + } + } + if (i != dim-1) { + qh_fprintf(qh ferr, 6008, "qhull internal error (qh_facetarea_simplex): #points %d != dim %d -1\n", + i, dim); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + rows[i]= gmcoord; + if (qh DELAUNAY) { + for (i=0; i < dim-1; i++) + rows[i][dim-1]= 0.0; + for (k=dim; k--; ) + *(gmcoord++)= 0.0; + rows[dim-1][dim-1]= -1.0; + }else { + normalp= normal; + for (k=dim; k--; ) + *(gmcoord++)= *normalp++; + } + zinc_(Zdetsimplex); + area= qh_determinant(rows, dim, &nearzero); + if (toporient) + area= -area; + area *= qh AREAfactor; + trace4((qh ferr, 4010, "qh_facetarea_simplex: area=%2.2g for point p%d, toporient %d, nearzero? %d\n", + area, qh_pointid(apex), toporient, nearzero)); + return area; +} /* facetarea_simplex */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="facetcenter">-</a> + + qh_facetcenter( vertices ) + return Voronoi center (Voronoi vertex) for a facet's vertices + + returns: + return temporary point equal to the center + + see: + qh_voronoi_center() +*/ +pointT *qh_facetcenter(setT *vertices) { + setT *points= qh_settemp(qh_setsize(vertices)); + vertexT *vertex, **vertexp; + pointT *center; + + FOREACHvertex_(vertices) + qh_setappend(&points, vertex->point); + center= qh_voronoi_center(qh hull_dim-1, points); + qh_settempfree(&points); + return center; +} /* facetcenter */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="findgooddist">-</a> + + qh_findgooddist( point, facetA, dist, facetlist ) + find best good facet visible for point from facetA + assumes facetA is visible from point + + returns: + best facet, i.e., good facet that is furthest from point + distance to best facet + NULL if none + + moves good, visible facets (and some other visible facets) + to end of qh facet_list + + notes: + uses qh visit_id + + design: + initialize bestfacet if facetA is good + move facetA to end of facetlist + for each facet on facetlist + for each unvisited neighbor of facet + move visible neighbors to end of facetlist + update best good neighbor + if no good neighbors, update best facet +*/ +facetT *qh_findgooddist(pointT *point, facetT *facetA, realT *distp, + facetT **facetlist) { + realT bestdist= -REALmax, dist; + facetT *neighbor, **neighborp, *bestfacet=NULL, *facet; + boolT goodseen= False; + + if (facetA->good) { + zzinc_(Zcheckpart); /* calls from check_bestdist occur after print stats */ + qh_distplane(point, facetA, &bestdist); + bestfacet= facetA; + goodseen= True; + } + qh_removefacet(facetA); + qh_appendfacet(facetA); + *facetlist= facetA; + facetA->visitid= ++qh visit_id; + FORALLfacet_(*facetlist) { + FOREACHneighbor_(facet) { + if (neighbor->visitid == qh visit_id) + continue; + neighbor->visitid= qh visit_id; + if (goodseen && !neighbor->good) + continue; + zzinc_(Zcheckpart); + qh_distplane(point, neighbor, &dist); + if (dist > 0) { + qh_removefacet(neighbor); + qh_appendfacet(neighbor); + if (neighbor->good) { + goodseen= True; + if (dist > bestdist) { + bestdist= dist; + bestfacet= neighbor; + } + } + } + } + } + if (bestfacet) { + *distp= bestdist; + trace2((qh ferr, 2003, "qh_findgooddist: p%d is %2.2g above good facet f%d\n", + qh_pointid(point), bestdist, bestfacet->id)); + return bestfacet; + } + trace4((qh ferr, 4011, "qh_findgooddist: no good facet for p%d above f%d\n", + qh_pointid(point), facetA->id)); + return NULL; +} /* findgooddist */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="getarea">-</a> + + qh_getarea( facetlist ) + set area of all facets in facetlist + collect statistics + nop if hasAreaVolume + + returns: + sets qh totarea/totvol to total area and volume of convex hull + for Delaunay triangulation, computes projected area of the lower or upper hull + ignores upper hull if qh ATinfinity + + notes: + could compute outer volume by expanding facet area by rays from interior + the following attempt at perpendicular projection underestimated badly: + qh.totoutvol += (-dist + facet->maxoutside + qh DISTround) + * area/ qh hull_dim; + design: + for each facet on facetlist + compute facet->area + update qh.totarea and qh.totvol +*/ +void qh_getarea(facetT *facetlist) { + realT area; + realT dist; + facetT *facet; + + if (qh hasAreaVolume) + return; + if (qh REPORTfreq) + qh_fprintf(qh ferr, 8020, "computing area of each facet and volume of the convex hull\n"); + else + trace1((qh ferr, 1001, "qh_getarea: computing volume and area for each facet\n")); + qh totarea= qh totvol= 0.0; + FORALLfacet_(facetlist) { + if (!facet->normal) + continue; + if (facet->upperdelaunay && qh ATinfinity) + continue; + if (!facet->isarea) { + facet->f.area= qh_facetarea(facet); + facet->isarea= True; + } + area= facet->f.area; + if (qh DELAUNAY) { + if (facet->upperdelaunay == qh UPPERdelaunay) + qh totarea += area; + }else { + qh totarea += area; + qh_distplane(qh interior_point, facet, &dist); + qh totvol += -dist * area/ qh hull_dim; + } + if (qh PRINTstatistics) { + wadd_(Wareatot, area); + wmax_(Wareamax, area); + wmin_(Wareamin, area); + } + } + qh hasAreaVolume= True; +} /* getarea */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="gram_schmidt">-</a> + + qh_gram_schmidt( dim, row ) + implements Gram-Schmidt orthogonalization by rows + + returns: + false if zero norm + overwrites rows[dim][dim] + + notes: + see Golub & van Loan, 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(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.htm#TOC" + >-------------------------------</a><a name="inthresholds">-</a> + + qh_inthresholds( normal, angle ) + return True if normal within qh.lower_/upper_threshold + + returns: + estimate of angle by summing of threshold diffs + angle may be NULL + smaller "angle" is better + + notes: + invalid if qh.SPLITthresholds + + see: + qh.lower_threshold in qh_initbuild() + qh_initthresholds() + + design: + for each dimension + test threshold +*/ +boolT qh_inthresholds(coordT *normal, realT *angle) { + boolT within= True; + int k; + realT threshold; + + if (angle) + *angle= 0.0; + for (k=0; k < qh hull_dim; k++) { + threshold= qh lower_threshold[k]; + if (threshold > -REALmax/2) { + if (normal[k] < threshold) + within= False; + if (angle) { + threshold -= normal[k]; + *angle += fabs_(threshold); + } + } + if (qh upper_threshold[k] < REALmax/2) { + threshold= qh upper_threshold[k]; + if (normal[k] > threshold) + within= False; + if (angle) { + threshold -= normal[k]; + *angle += fabs_(threshold); + } + } + } + return within; +} /* inthresholds */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="joggleinput">-</a> + + qh_joggleinput() + randomly joggle input to Qhull by qh.JOGGLEmax + initial input is qh.first_point/qh.num_points of qh.hull_dim + repeated calls use qh.input_points/qh.num_points + + returns: + joggles points at qh.first_point/qh.num_points + copies data to qh.input_points/qh.input_malloc if first time + determines qh.JOGGLEmax if it was zero + if qh.DELAUNAY + computes the Delaunay projection of the joggled points + + notes: + if qh.DELAUNAY, unnecessarily joggles the last coordinate + the initial 'QJn' may be set larger than qh_JOGGLEmaxincrease + + design: + if qh.DELAUNAY + set qh.SCALElast for reduced precision errors + if first call + initialize qh.input_points to the original input points + if qh.JOGGLEmax == 0 + determine default qh.JOGGLEmax + else + increase qh.JOGGLEmax according to qh.build_cnt + joggle the input by adding a random number in [-qh.JOGGLEmax,qh.JOGGLEmax] + if qh.DELAUNAY + sets the Delaunay projection +*/ +void qh_joggleinput(void) { + int i, seed, size; + coordT *coordp, *inputp; + realT randr, randa, randb; + + if (!qh input_points) { /* first call */ + qh input_points= qh first_point; + qh input_malloc= qh POINTSmalloc; + size= qh num_points * qh hull_dim * sizeof(coordT); + if (!(qh first_point=(coordT*)qh_malloc((size_t)size))) { + qh_fprintf(qh ferr, 6009, "qhull error: insufficient memory to joggle %d points\n", + qh num_points); + qh_errexit(qh_ERRmem, NULL, NULL); + } + qh POINTSmalloc= True; + if (qh JOGGLEmax == 0.0) { + qh JOGGLEmax= qh_detjoggle(qh input_points, qh num_points, qh hull_dim); + qh_option("QJoggle", NULL, &qh JOGGLEmax); + } + }else { /* repeated call */ + if (!qh RERUN && qh build_cnt > qh_JOGGLEretry) { + if (((qh build_cnt-qh_JOGGLEretry-1) % qh_JOGGLEagain) == 0) { + realT maxjoggle= qh MAXwidth * qh_JOGGLEmaxincrease; + if (qh JOGGLEmax < maxjoggle) { + qh JOGGLEmax *= qh_JOGGLEincrease; + minimize_(qh JOGGLEmax, maxjoggle); + } + } + } + qh_option("QJoggle", NULL, &qh JOGGLEmax); + } + if (qh build_cnt > 1 && qh JOGGLEmax > fmax_(qh MAXwidth/4, 0.1)) { + qh_fprintf(qh ferr, 6010, "qhull error: the current joggle for 'QJn', %.2g, is too large for the width\nof the input. If possible, recompile Qhull with higher-precision reals.\n", + qh JOGGLEmax); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + /* for some reason, using qh ROTATErandom and qh_RANDOMseed does not repeat the run. Use 'TRn' instead */ + seed= qh_RANDOMint; + qh_option("_joggle-seed", &seed, NULL); + trace0((qh ferr, 6, "qh_joggleinput: joggle input by %2.2g with seed %d\n", + qh JOGGLEmax, seed)); + inputp= qh input_points; + coordp= qh first_point; + randa= 2.0 * qh JOGGLEmax/qh_RANDOMmax; + randb= -qh JOGGLEmax; + size= qh num_points * qh hull_dim; + for (i=size; i--; ) { + randr= qh_RANDOMint; + *(coordp++)= *(inputp++) + (randr * randa + randb); + } + if (qh DELAUNAY) { + qh last_low= qh last_high= qh last_newhigh= REALmax; + qh_setdelaunay(qh hull_dim, qh num_points, qh first_point); + } +} /* joggleinput */ + +/*-<a href="qh-geom.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.htm#TOC" + >-------------------------------</a><a name="maxmin">-</a> + + qh_maxmin( points, numpoints, dimension ) + return max/min points for each dimension + determine max and min coordinates + + returns: + returns a temporary set of max and min points + may include duplicate points. Does not include qh.GOODpoint + sets qh.NEARzero, qh.MAXabs_coord, qh.MAXsumcoord, qh.MAXwidth + qh.MAXlastcoord, qh.MINlastcoord + initializes qh.max_outside, qh.min_vertex, qh.WAScoplanar, qh.ZEROall_ok + + notes: + loop duplicated in qh_detjoggle() + + design: + initialize global precision variables + checks definition of REAL... + for each dimension + for each point + collect maximum and minimum point + collect maximum of maximums and minimum of minimums + determine qh.NEARzero for Gaussian Elimination +*/ +setT *qh_maxmin(pointT *points, int numpoints, int dimension) { + int k; + realT maxcoord, temp; + pointT *minimum, *maximum, *point, *pointtemp; + setT *set; + + qh max_outside= 0.0; + qh MAXabs_coord= 0.0; + qh MAXwidth= -REALmax; + qh MAXsumcoord= 0.0; + qh min_vertex= 0.0; + qh WAScoplanar= False; + if (qh ZEROcentrum) + qh ZEROall_ok= True; + if (REALmin < REALepsilon && REALmin < REALmax && REALmin > -REALmax + && REALmax > 0.0 && -REALmax < 0.0) + ; /* all ok */ + else { + qh_fprintf(qh ferr, 6011, "qhull error: floating point constants in user.h are wrong\n\ +REALepsilon %g REALmin %g REALmax %g -REALmax %g\n", + REALepsilon, REALmin, REALmax, -REALmax); + qh_errexit(qh_ERRinput, NULL, NULL); + } + set= qh_settemp(2*dimension); + for (k=0; k < dimension; k++) { + if (points == qh GOODpointp) + minimum= maximum= points + dimension; + else + minimum= maximum= points; + FORALLpoint_(points, numpoints) { + if (point == qh GOODpointp) + continue; + if (maximum[k] < point[k]) + maximum= point; + else if (minimum[k] > point[k]) + minimum= point; + } + if (k == dimension-1) { + qh MINlastcoord= minimum[k]; + qh MAXlastcoord= maximum[k]; + } + if (qh SCALElast && k == dimension-1) + maxcoord= qh MAXwidth; + else { + maxcoord= fmax_(maximum[k], -minimum[k]); + if (qh GOODpointp) { + temp= fmax_(qh GOODpointp[k], -qh GOODpointp[k]); + maximize_(maxcoord, temp); + } + temp= maximum[k] - minimum[k]; + maximize_(qh MAXwidth, temp); + } + maximize_(qh MAXabs_coord, maxcoord); + qh MAXsumcoord += maxcoord; + qh_setappend(&set, maximum); + qh_setappend(&set, minimum); + /* calculation of qh NEARzero is based on 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; + } + if (qh IStracing >=1) + qh_printpoints(qh ferr, "qh_maxmin: found the max and min points(by dim):", set); + return(set); +} /* maxmin */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="maxouter">-</a> + + qh_maxouter() + return maximum distance from facet to outer plane + normally this is qh.max_outside+qh.DISTround + does not include qh.JOGGLEmax + + see: + qh_outerinner() + + notes: + need to add another qh.DISTround if testing actual point with computation + + for joggle: + qh_setfacetplane() updated qh.max_outer for Wnewvertexmax (max distance to vertex) + need to use Wnewvertexmax since could have a coplanar point for a high + facet that is replaced by a low facet + need to add qh.JOGGLEmax if testing input points +*/ +realT qh_maxouter(void) { + realT dist; + + dist= fmax_(qh max_outside, qh DISTround); + dist += qh DISTround; + trace4((qh ferr, 4012, "qh_maxouter: max distance from facet to outer plane is %2.2g max_outside is %2.2g\n", dist, qh max_outside)); + return dist; +} /* maxouter */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="maxsimplex">-</a> + + qh_maxsimplex( dim, maxpoints, points, numpoints, simplex ) + determines maximum simplex for a set of points + starts from points already in simplex + skips qh.GOODpointp (assumes that it isn't in maxpoints) + + returns: + simplex with dim+1 points + + notes: + assumes at least pointsneeded points in points + maximizes determinate for x,y,z,w, etc. + uses maxpoints as long as determinate is clearly non-zero + + design: + initialize simplex with at least two points + (find points with max or min x coordinate) + for each remaining dimension + add point that maximizes the determinate + (use points from maxpoints first) +*/ +void qh_maxsimplex(int dim, setT *maxpoints, pointT *points, int numpoints, setT **simplex) { + pointT *point, **pointp, *pointtemp, *maxpoint, *minx=NULL, *maxx=NULL; + boolT nearzero, maxnearzero= False; + int k, sizinit; + realT maxdet= -REALmax, det, mincoord= REALmax, maxcoord= -REALmax; + + sizinit= qh_setsize(*simplex); + if (sizinit < 2) { + if (qh_setsize(maxpoints) >= 2) { + FOREACHpoint_(maxpoints) { + if (maxcoord < point[0]) { + maxcoord= point[0]; + maxx= point; + } + if (mincoord > point[0]) { + mincoord= point[0]; + minx= point; + } + } + }else { + FORALLpoint_(points, numpoints) { + if (point == qh GOODpointp) + continue; + if (maxcoord < point[0]) { + maxcoord= point[0]; + maxx= point; + } + if (mincoord > point[0]) { + mincoord= point[0]; + minx= point; + } + } + } + qh_setunique(simplex, minx); + if (qh_setsize(*simplex) < 2) + qh_setunique(simplex, maxx); + sizinit= qh_setsize(*simplex); + if (sizinit < 2) { + qh_precision("input has same x coordinate"); + if (zzval_(Zsetplane) > qh hull_dim+1) { + qh_fprintf(qh ferr, 6012, "qhull precision error (qh_maxsimplex for voronoi_center):\n%d points with the same x coordinate.\n", + qh_setsize(maxpoints)+numpoints); + qh_errexit(qh_ERRprec, NULL, NULL); + }else { + qh_fprintf(qh ferr, 6013, "qhull input error: input is less than %d-dimensional since it has the same x coordinate\n", qh hull_dim); + qh_errexit(qh_ERRinput, NULL, NULL); + } + } + } + for (k=sizinit; k < dim+1; k++) { + maxpoint= NULL; + maxdet= -REALmax; + FOREACHpoint_(maxpoints) { + if (!qh_setin(*simplex, point)) { + det= qh_detsimplex(point, *simplex, k, &nearzero); + if ((det= fabs_(det)) > maxdet) { + maxdet= det; + maxpoint= point; + maxnearzero= nearzero; + } + } + } + if (!maxpoint || maxnearzero) { + zinc_(Zsearchpoints); + if (!maxpoint) { + trace0((qh ferr, 7, "qh_maxsimplex: searching all points for %d-th initial vertex.\n", k+1)); + }else { + trace0((qh ferr, 8, "qh_maxsimplex: searching all points for %d-th initial vertex, better than p%d det %2.2g\n", + k+1, qh_pointid(maxpoint), maxdet)); + } + FORALLpoint_(points, numpoints) { + if (point == qh GOODpointp) + continue; + if (!qh_setin(*simplex, point)) { + det= qh_detsimplex(point, *simplex, k, &nearzero); + if ((det= fabs_(det)) > maxdet) { + maxdet= det; + maxpoint= point; + maxnearzero= nearzero; + } + } + } + } /* !maxpoint */ + if (!maxpoint) { + qh_fprintf(qh ferr, 6014, "qhull internal error (qh_maxsimplex): not enough points available\n"); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + qh_setappend(simplex, maxpoint); + trace1((qh ferr, 1002, "qh_maxsimplex: selected point p%d for %d`th initial vertex, det=%2.2g\n", + qh_pointid(maxpoint), k+1, maxdet)); + } /* k */ +} /* maxsimplex */ + +/*-<a href="qh-geom.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.htm#TOC" + >-------------------------------</a><a name="mindiff">-</a> + + qh_mindif( vecA, vecB, dim ) + return index of min abs. difference of two vectors +*/ +int qh_mindiff(realT *vecA, realT *vecB, int dim) { + realT mindiff= REALmax, diff; + realT *vecAp= vecA, *vecBp= vecB; + int k, mink= 0; + + for (k=0; k < dim; k++) { + diff= *vecAp++ - *vecBp++; + diff= fabs_(diff); + if (diff < mindiff) { + mindiff= diff; + mink= k; + } + } + return mink; +} /* mindiff */ + + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="orientoutside">-</a> + + qh_orientoutside( facet ) + make facet outside oriented via qh.interior_point + + returns: + True if facet reversed orientation. +*/ +boolT qh_orientoutside(facetT *facet) { + int k; + realT dist; + + qh_distplane(qh interior_point, facet, &dist); + if (dist > 0) { + for (k=qh hull_dim; k--; ) + facet->normal[k]= -facet->normal[k]; + facet->offset= -facet->offset; + return True; + } + return False; +} /* orientoutside */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="outerinner">-</a> + + qh_outerinner( facet, outerplane, innerplane ) + if facet and qh.maxoutdone (i.e., qh_check_maxout) + returns outer and inner plane for facet + else + returns maximum outer and inner plane + accounts for qh.JOGGLEmax + + see: + qh_maxouter(), qh_check_bestdist(), qh_check_points() + + notes: + outerplaner or innerplane may be NULL + facet is const + Does not error (QhullFacet) + + includes qh.DISTround for actual points + adds another qh.DISTround if testing with floating point arithmetic +*/ +void qh_outerinner(facetT *facet, realT *outerplane, realT *innerplane) { + realT dist, mindist; + vertexT *vertex, **vertexp; + + if (outerplane) { + if (!qh_MAXoutside || !facet || !qh maxoutdone) { + *outerplane= qh_maxouter(); /* includes qh.DISTround */ + }else { /* qh_MAXoutside ... */ +#if qh_MAXoutside + *outerplane= facet->maxoutside + qh DISTround; +#endif + + } + if (qh JOGGLEmax < REALmax/2) + *outerplane += qh JOGGLEmax * sqrt((realT)qh hull_dim); + } + if (innerplane) { + if (facet) { + mindist= REALmax; + FOREACHvertex_(facet->vertices) { + zinc_(Zdistio); + qh_distplane(vertex->point, facet, &dist); + minimize_(mindist, dist); + } + *innerplane= mindist - qh DISTround; + }else + *innerplane= qh min_vertex - qh DISTround; + if (qh JOGGLEmax < REALmax/2) + *innerplane -= qh JOGGLEmax * sqrt((realT)qh hull_dim); + } +} /* outerinner */ + +/*-<a href="qh-geom.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.htm#TOC" + >-------------------------------</a><a name="printmatrix">-</a> + + qh_printmatrix( fp, string, rows, numrow, numcol ) + print matrix to fp given by row vectors + print string as header + + notes: + print a vector by qh_printmatrix(fp, "", &vect, 1, len) +*/ +void qh_printmatrix(FILE *fp, const char *string, realT **rows, int numrow, int numcol) { + realT *rowp; + realT r; /*bug fix*/ + int i,k; + + qh_fprintf(fp, 9001, "%s\n", string); + for (i=0; i < numrow; i++) { + rowp= rows[i]; + for (k=0; k < numcol; k++) { + r= *rowp++; + qh_fprintf(fp, 9002, "%6.3g ", r); + } + qh_fprintf(fp, 9003, "\n"); + } +} /* printmatrix */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="printpoints">-</a> + + qh_printpoints( fp, string, points ) + print pointids to fp for a set of points + if string, prints string and 'p' point ids +*/ +void qh_printpoints(FILE *fp, const char *string, setT *points) { + pointT *point, **pointp; + + if (string) { + qh_fprintf(fp, 9004, "%s", string); + FOREACHpoint_(points) + qh_fprintf(fp, 9005, " p%d", qh_pointid(point)); + qh_fprintf(fp, 9006, "\n"); + }else { + FOREACHpoint_(points) + qh_fprintf(fp, 9007, " %d", qh_pointid(point)); + qh_fprintf(fp, 9008, "\n"); + } +} /* printpoints */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="projectinput">-</a> + + qh_projectinput() + project input points using qh.lower_bound/upper_bound and qh DELAUNAY + if qh.lower_bound[k]=qh.upper_bound[k]= 0, + removes dimension k + if halfspace intersection + removes dimension k from qh.feasible_point + input points in qh first_point, num_points, input_dim + + returns: + new point array in qh first_point of qh hull_dim coordinates + sets qh POINTSmalloc + if qh DELAUNAY + projects points to paraboloid + lowbound/highbound is also projected + if qh ATinfinity + adds point "at-infinity" + if qh POINTSmalloc + frees old point array + + notes: + checks that qh.hull_dim agrees with qh.input_dim, PROJECTinput, and DELAUNAY + + + design: + sets project[k] to -1 (delete), 0 (keep), 1 (add for Delaunay) + determines newdim and newnum for qh hull_dim and qh num_points + projects points to newpoints + projects qh.lower_bound to itself + projects qh.upper_bound to itself + if qh DELAUNAY + if qh ATINFINITY + projects points to paraboloid + computes "infinity" point as vertex average and 10% above all points + else + uses qh_setdelaunay to project points to paraboloid +*/ +void qh_projectinput(void) { + int k,i; + int newdim= qh input_dim, newnum= qh num_points; + signed char *project; + int projectsize= (qh input_dim+1)*sizeof(*project); + pointT *newpoints, *coord, *infinity; + realT paraboloid, maxboloid= 0; + + project= (signed char*)qh_memalloc(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 && qh upper_bound[k] == 0) { + project[k]= -1; + newdim--; + } + } + if (qh DELAUNAY) { + project[k]= 1; + newdim++; + if (qh ATinfinity) + newnum++; + } + if (newdim != qh hull_dim) { + qh_memfree(project, projectsize); + qh_fprintf(qh ferr, 6015, "qhull internal error (qh_projectinput): dimension after projection %d != hull_dim %d\n", newdim, qh hull_dim); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + if (!(newpoints= qh temp_malloc= (coordT*)qh_malloc(newnum*newdim*sizeof(coordT)))){ + qh_memfree(project, projectsize); + qh_fprintf(qh ferr, 6016, "qhull error: insufficient memory to project %d points\n", + qh num_points); + qh_errexit(qh_ERRmem, NULL, NULL); + } + /* qh_projectpoints throws error if mismatched dimensions */ + qh_projectpoints(project, qh input_dim+1, qh first_point, + qh num_points, qh input_dim, newpoints, newdim); + trace1((qh ferr, 1003, "qh_projectinput: updating lower and upper_bound\n")); + qh_projectpoints(project, qh input_dim+1, qh lower_bound, + 1, qh input_dim+1, qh lower_bound, newdim+1); + qh_projectpoints(project, qh input_dim+1, qh upper_bound, + 1, qh input_dim+1, qh upper_bound, newdim+1); + if (qh HALFspace) { + if (!qh feasible_point) { + qh_memfree(project, projectsize); + qh_fprintf(qh ferr, 6017, "qhull internal error (qh_projectinput): HALFspace defined without qh.feasible_point\n"); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + qh_projectpoints(project, qh input_dim, qh feasible_point, + 1, qh input_dim, qh feasible_point, newdim); + } + qh_memfree(project, 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 ferr, 9, "qh_projectinput: projected points to paraboloid for Delaunay\n")); + }else if (qh DELAUNAY) /* !qh ATinfinity */ + qh_setdelaunay( qh hull_dim, qh num_points, qh first_point); +} /* projectinput */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="projectpoints">-</a> + + qh_projectpoints( project, n, points, numpoints, dim, newpoints, newdim ) + project points/numpoints/dim to newpoints/newdim + if project[k] == -1 + delete dimension k + if project[k] == 1 + add dimension k by duplicating previous column + n is size of project + + notes: + newpoints may be points if only adding dimension at end + + design: + check that 'project' and 'newdim' agree + for each dimension + if project == -1 + skip dimension + else + determine start of column in newpoints + determine start of column in points + if project == +1, duplicate previous column + copy dimension (column) from points to newpoints +*/ +void qh_projectpoints(signed char *project, int n, realT *points, + int numpoints, int dim, realT *newpoints, int newdim) { + int testdim= dim, oldk=0, newk=0, i,j=0,k; + realT *newp, *oldp; + + for (k=0; k < n; k++) + testdim += project[k]; + if (testdim != newdim) { + qh_fprintf(qh ferr, 6018, "qhull internal error (qh_projectpoints): newdim %d should be %d after projection\n", + newdim, testdim); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + for (j=0; j<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 ferr, 1004, "qh_projectpoints: projected %d points from dim %d to dim %d\n", + numpoints, dim, newdim)); +} /* projectpoints */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="rotateinput">-</a> + + qh_rotateinput( 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(realT **rows) { + + if (!qh POINTSmalloc) { + qh first_point= qh_copypoints(qh first_point, qh num_points, qh hull_dim); + qh POINTSmalloc= True; + } + qh_rotatepoints(qh first_point, qh num_points, qh hull_dim, rows); +} /* rotateinput */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="rotatepoints">-</a> + + qh_rotatepoints( 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(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 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.htm#TOC" + >-------------------------------</a><a name="scaleinput">-</a> + + qh_scaleinput() + 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(void) { + + if (!qh POINTSmalloc) { + qh first_point= qh_copypoints(qh first_point, qh num_points, qh hull_dim); + qh POINTSmalloc= True; + } + qh_scalepoints(qh first_point, qh num_points, qh hull_dim, + qh lower_bound, qh upper_bound); +} /* scaleinput */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="scalelast">-</a> + + qh_scalelast( points, numpoints, dim, low, high, newhigh ) + scale last coordinate to [0,m] for Delaunay triangulations + input points given by points, numpoints, dim + + returns: + changes scale of last coordinate from [low, high] to [0, newhigh] + overwrites last coordinate of each point + saves low/high/newhigh in qh.last_low, etc. for qh_setdelaunay() + + notes: + when called by qh_setdelaunay, low/high may not match actual data + + design: + compute scale and shift factors + apply to last coordinate of each point +*/ +void qh_scalelast(coordT *points, int numpoints, int dim, coordT low, + coordT high, coordT newhigh) { + realT scale, shift; + coordT *coord; + int i; + boolT nearzero= False; + + trace4((qh ferr, 4013, "qh_scalelast: scale last coordinate from [%2.2g, %2.2g] to [0,%2.2g]\n", + low, high, newhigh)); + qh last_low= low; + qh last_high= high; + qh last_newhigh= newhigh; + scale= qh_divzero(newhigh, high - low, + qh MINdenom_1, &nearzero); + if (nearzero) { + if (qh DELAUNAY) + qh_fprintf(qh ferr, 6019, "qhull input error: can not scale last coordinate. Input is cocircular\n or cospherical. Use option 'Qz' to add a point at infinity.\n"); + else + qh_fprintf(qh ferr, 6020, "qhull input error: can not scale last coordinate. New bounds [0, %2.2g] are too wide for\nexisting bounds [%2.2g, %2.2g] (width %2.2g)\n", + newhigh, low, high, high-low); + qh_errexit(qh_ERRinput, NULL, NULL); + } + shift= - low * newhigh / (high-low); + coord= points + dim - 1; + for (i=numpoints; i--; coord += dim) + *coord= *coord * scale + shift; +} /* scalelast */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="scalepoints">-</a> + + qh_scalepoints( 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(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 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_ERRinput, NULL, NULL); + } + scale= qh_divzero(newhigh - newlow, high - low, + qh MINdenom_1, &nearzero); + if (nearzero) { + qh_fprintf(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_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 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.htm#TOC" + >-------------------------------</a><a name="setdelaunay">-</a> + + qh_setdelaunay( 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 hull_dim, 1, point) + + Do not use options 'Qbk', 'QBk', or 'QbB' since they scale + the coordinates after the original projection. + +*/ +void qh_setdelaunay(int dim, int count, pointT *points) { + int i, k; + coordT *coordp, coord; + realT paraboloid; + + trace0((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(points, count, dim, qh last_low, qh last_high, qh last_newhigh); +} /* setdelaunay */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="sethalfspace">-</a> + + qh_sethalfspace( 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(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; + if (qh IStracing >= 4) { + qh_fprintf(qh ferr, 8021, "qh_sethalfspace: halfspace at offset %6.2g to point: ", *offset); + for (k=dim, coordp=coords; k--; ) { + r= *coordp++; + qh_fprintf(qh ferr, 8022, " %6.2g", r); + } + qh_fprintf(qh ferr, 8023, "\n"); + } + return True; +LABELerroroutside: + feasiblep= feasible; + normp= normal; + qh_fprintf(qh ferr, 6023, "qhull input error: feasible point is not clearly inside halfspace\nfeasible point: "); + for (k=dim; k--; ) + qh_fprintf(qh ferr, 8024, qh_REAL_1, r=*(feasiblep++)); + qh_fprintf(qh ferr, 8025, "\n halfspace: "); + for (k=dim; k--; ) + qh_fprintf(qh ferr, 8026, qh_REAL_1, r=*(normp++)); + qh_fprintf(qh ferr, 8027, "\n at offset: "); + qh_fprintf(qh ferr, 8028, qh_REAL_1, *offset); + qh_fprintf(qh ferr, 8029, " and distance: "); + qh_fprintf(qh ferr, 8030, qh_REAL_1, dist); + qh_fprintf(qh ferr, 8031, "\n"); + return False; +} /* sethalfspace */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="sethalfspace_all">-</a> + + qh_sethalfspace_all( 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(int dim, int count, coordT *halfspaces, pointT *feasible) { + int i, newdim; + pointT *newpoints; + coordT *coordp, *normalp, *offsetp; + + trace0((qh ferr, 12, "qh_sethalfspace_all: compute dual for halfspace intersection\n")); + newdim= dim - 1; + if (!(newpoints=(coordT*)qh_malloc(count*newdim*sizeof(coordT)))){ + qh_fprintf(qh ferr, 6024, "qhull error: insufficient memory to compute dual of %d halfspaces\n", + count); + qh_errexit(qh_ERRmem, NULL, NULL); + } + coordp= newpoints; + normalp= halfspaces; + for (i=0; i < count; i++) { + offsetp= normalp + newdim; + if (!qh_sethalfspace(newdim, coordp, &coordp, normalp, offsetp, feasible)) { + qh_free(newpoints); /* feasible is not inside halfspace as reported by qh_sethalfspace */ + qh_fprintf(qh ferr, 8032, "The halfspace was at index %d\n", i); + qh_errexit(qh_ERRinput, NULL, NULL); + } + normalp= offsetp + 1; + } + return newpoints; +} /* sethalfspace_all */ + + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="sharpnewfacets">-</a> + + qh_sharpnewfacets() + + 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(void) { + facetT *facet; + boolT issharp = False; + int *quadrant, k; + + quadrant= (int*)qh_memalloc(qh hull_dim * 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( quadrant, qh hull_dim * sizeof(int)); + trace3((qh ferr, 3001, "qh_sharpnewfacets: %d\n", issharp)); + return issharp; +} /* sharpnewfacets */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="voronoi_center">-</a> + + qh_voronoi_center( 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(int dim, setT *points) { + pointT *point, **pointp, *point0; + pointT *center= (pointT*)qh_memalloc(qh center_size); + setT *simplex; + int i, j, k, size= qh_setsize(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(center, qh center_size); + qh_fprintf(qh ferr, 6025, "qhull internal error (qh_voronoi_center):\n need at least %d points to construct a Voronoi center\n", + dim+1); + qh_errexit(qh_ERRqhull, NULL, NULL); + simplex= points; /* never executed -- avoids warning */ + }else { + simplex= qh_settemp(dim+1); + qh_maxsimplex(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 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 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 gm_row, dim, &nearzero)*factor + point0[i]; + } +#ifndef qh_NOtrace + if (qh IStracing >= 3) { + qh_fprintf(qh ferr, 8033, "qh_voronoi_center: det %2.2g factor %2.2g ", det, factor); + qh_printmatrix(qh ferr, "center:", ¢er, 1, dim); + if (qh IStracing >= 5) { + qh_printpoints(qh ferr, "points", simplex); + FOREACHpoint_(simplex) + qh_fprintf(qh ferr, 8034, "p%d dist %.2g, ", qh_pointid(point), + qh_pointdist(point, center, dim)); + qh_fprintf(qh ferr, 8035, "\n"); + } + } +#endif + } + if (simplex != points) + qh_settempfree(&simplex); + return center; +} /* voronoi_center */ + diff --git a/src/qhull/src/libqhull/global.c b/src/qhull/src/libqhull/global.c new file mode 100644 index 000000000..0328fea7b --- /dev/null +++ b/src/qhull/src/libqhull/global.c @@ -0,0 +1,2217 @@ + +/*<html><pre> -<a href="qh-globa.htm" + >-------------------------------</a><a name="TOP">-</a> + + global.c + initializes all the globals of the qhull application + + see README + + see libqhull.h for qh.globals and function prototypes + + see qhull_a.h for internal functions + + Copyright (c) 1993-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull/global.c#17 $$Change: 2066 $ + $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $ + */ + +#include "qhull_a.h" + +/*========= qh definition -- globals defined in libqhull.h =======================*/ + +#if qh_QHpointer +qhT *qh_qh= NULL; /* pointer to all global variables */ +#else +qhT qh_qh; /* all global variables. + Add "= {0}" if this causes a compiler error. + Also qh_qhstat in stat.c and qhmem in mem.c. */ +#endif + +/*-<a href ="qh-globa.htm#TOC" + >--------------------------------</a><a name="qh_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 @version is the same as qh_version2 + change year: Copying.txt + check download size + recompile user_eg.c, rbox.c, libqhull.c, qconvex.c, qdelaun.c qvoronoi.c, qhalf.c, testqset.c +*/ + +const char qh_version[]= "2015.2 2016/01/18"; +const char qh_version2[]= "qhull 7.2.0 (2015.2 2016/01/18)"; + +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="appendprint">-</a> + + qh_appendprint( printFormat ) + append printFormat to qh.PRINTout unless already defined +*/ +void qh_appendprint(qh_PRINT format) { + int i; + + for (i=0; i < qh_PRINTEND; i++) { + if (qh PRINTout[i] == format && format != qh_PRINTqhull) + break; + if (!qh PRINTout[i]) { + qh PRINTout[i]= format; + break; + } + } +} /* appendprint */ + +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="checkflags">-</a> + + qh_checkflags( 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(char *command, char *hiddenflags) { + char *s= command, *t, *chkerr; /* qh_skipfilename is non-const */ + char key, opt, prevopt; + char chkkey[]= " "; + char chkopt[]= " "; + char chkopt2[]= " "; + boolT waserr= False; + + if (*hiddenflags != ' ' || hiddenflags[strlen(hiddenflags)-1] != ' ') { + qh_fprintf(qh ferr, 6026, "qhull error (qh_checkflags): hiddenflags must start and end with a space: \"%s\"", hiddenflags); + qh_errexit(qh_ERRinput, NULL, NULL); + } + if (strpbrk(hiddenflags, ",\n\r\t")) { + qh_fprintf(qh ferr, 6027, "qhull error (qh_checkflags): hiddenflags contains commas, newlines, or tabs: \"%s\"", hiddenflags); + qh_errexit(qh_ERRinput, NULL, NULL); + } + while (*s && !isspace(*s)) /* skip program name */ + s++; + while (*s) { + while (*s && isspace(*s)) + s++; + if (*s == '-') + s++; + if (!*s) + break; + key = *s++; + chkerr = NULL; + if (key == 'T' && (*s == 'I' || *s == 'O')) { /* TI or TO 'file name' */ + s= qh_skipfilename(++s); + continue; + } + chkkey[1]= key; + if (strstr(hiddenflags, chkkey)) { + chkerr= chkkey; + }else if (isupper(key)) { + opt= ' '; + prevopt= ' '; + chkopt[1]= key; + chkopt2[1]= key; + while (!chkerr && *s && !isspace(*s)) { + opt= *s++; + if (isalpha(opt)) { + chkopt[2]= opt; + if (strstr(hiddenflags, chkopt)) + chkerr= chkopt; + if (prevopt != ' ') { + chkopt2[2]= prevopt; + chkopt2[3]= opt; + if (strstr(hiddenflags, chkopt2)) + chkerr= chkopt2; + } + }else if (key == 'Q' && isdigit(opt) && prevopt != 'b' + && (prevopt == ' ' || islower(prevopt))) { + chkopt[2]= opt; + if (strstr(hiddenflags, chkopt)) + chkerr= chkopt; + }else { + qh_strtod(s-1, &t); + if (s < t) + s= t; + } + prevopt= opt; + } + } + if (chkerr) { + *chkerr= '\''; + chkerr[strlen(chkerr)-1]= '\''; + qh_fprintf(qh ferr, 6029, "qhull error: option %s is not used with this program.\n It may be used with qhull.\n", chkerr); + waserr= True; + } + } + if (waserr) + qh_errexit(qh_ERRinput, NULL, NULL); +} /* checkflags */ + +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="qh_clear_outputflags">-</a> + + qh_clear_outputflags() + Clear output flags for QhullPoints +*/ +void qh_clear_outputflags(void) { + int i,k; + + qh ANNOTATEoutput= False; + qh DOintersections= False; + qh DROPdim= -1; + qh FORCEoutput= False; + qh GETarea= False; + qh GOODpoint= 0; + qh GOODpointp= NULL; + qh GOODthreshold= False; + qh GOODvertex= 0; + qh GOODvertexp= NULL; + qh IStracing= 0; + qh KEEParea= False; + qh KEEPmerge= False; + qh KEEPminArea= REALmax; + qh PRINTcentrums= False; + qh PRINTcoplanar= False; + qh PRINTdots= False; + qh PRINTgood= False; + qh PRINTinner= False; + qh PRINTneighbors= False; + qh PRINTnoplanes= False; + qh PRINToptions1st= False; + qh PRINTouter= False; + qh PRINTprecision= True; + qh PRINTridges= False; + qh PRINTspheres= False; + qh PRINTstatistics= False; + qh PRINTsummary= False; + qh PRINTtransparent= False; + qh SPLITthresholds= False; + qh TRACElevel= 0; + qh TRInormals= False; + qh USEstdout= False; + qh VERIFYoutput= False; + for (k=qh input_dim+1; k--; ) { /* duplicated in qh_initqhull_buffers and qh_clear_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.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(void) { + +#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 ferr, 6030, "qhull internal error (qh_clock): sysconf() failed. Use qh_CLOCKtype 1 in user.h\n"); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + } + if (times(&time) == -1) { + qh_fprintf(qh ferr, 6031, "qhull internal error (qh_clock): times() failed. Use qh_CLOCKtype 1 in user.h\n"); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + ratio= qh_SECticks / (double)clktck; + ticks= time.tms_utime * ratio; + return ticks; +#else + qh_fprintf(qh ferr, 6032, "qhull internal error (qh_clock): use qh_CLOCKtype 2 in user.h\n"); + qh_errexit(qh_ERRqhull, NULL, NULL); /* never returns */ + return 0; +#endif +} /* clock */ + +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="freebuffers">-</a> + + qh_freebuffers() + free up global memory buffers + + notes: + must match qh_initbuffers() +*/ +void qh_freebuffers(void) { + + trace5((qh ferr, 5001, "qh_freebuffers: freeing up global memory buffers\n")); + /* allocated by qh_initqhull_buffers */ + qh_memfree(qh NEARzero, qh hull_dim * sizeof(realT)); + qh_memfree(qh lower_threshold, (qh input_dim+1) * sizeof(realT)); + qh_memfree(qh upper_threshold, (qh input_dim+1) * sizeof(realT)); + qh_memfree(qh lower_bound, (qh input_dim+1) * sizeof(realT)); + qh_memfree(qh upper_bound, (qh input_dim+1) * sizeof(realT)); + qh_memfree(qh gm_matrix, (qh hull_dim+1) * qh hull_dim * sizeof(coordT)); + qh_memfree(qh gm_row, (qh hull_dim+1) * sizeof(coordT *)); + qh NEARzero= qh lower_threshold= qh upper_threshold= NULL; + qh lower_bound= qh upper_bound= NULL; + qh gm_matrix= NULL; + qh gm_row= NULL; + qh_setfree(&qh other_points); + qh_setfree(&qh del_vertices); + qh_setfree(&qh coplanarfacetset); + if (qh line) /* allocated by qh_readinput, freed if no error */ + qh_free(qh line); + if (qh half_space) + qh_free(qh half_space); + if (qh temp_malloc) + qh_free(qh temp_malloc); + if (qh feasible_point) /* allocated by qh_readfeasible */ + qh_free(qh feasible_point); + if (qh feasible_string) /* allocated by qh_initflags */ + qh_free(qh feasible_string); + qh line= qh feasible_string= NULL; + qh half_space= qh feasible_point= qh temp_malloc= NULL; + /* usually allocated by qh_readinput */ + if (qh first_point && qh POINTSmalloc) { + qh_free(qh first_point); + qh first_point= NULL; + } + if (qh input_points && qh input_malloc) { /* set by qh_joggleinput */ + qh_free(qh input_points); + qh input_points= NULL; + } + trace5((qh ferr, 5002, "qh_freebuffers: finished\n")); +} /* freebuffers */ + + +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="freebuild">-</a> + + qh_freebuild( allmem ) + free global memory used by qh_initbuild and qh_buildhull + if !allmem, + does not free short memory (e.g., facetT, freed by qh_memfreeshort) + + design: + free centrums + free each vertex + mark unattached ridges + for each facet + free ridges + free outside set, coplanar set, neighbor set, ridge set, vertex set + free facet + free hash table + free interior point + free merge set + free temporary sets +*/ +void qh_freebuild(boolT allmem) { + facetT *facet; + vertexT *vertex; + ridgeT *ridge, **ridgep; + mergeT *merge, **mergep; + + trace1((qh ferr, 1005, "qh_freebuild: free memory from qh_inithull and qh_buildhull\n")); + if (qh del_vertices) + qh_settruncate(qh del_vertices, 0); + if (allmem) { + while ((vertex= qh vertex_list)) { + if (vertex->next) + qh_delvertex(vertex); + else { + qh_memfree(vertex, (int)sizeof(vertexT)); + qh newvertex_list= qh vertex_list= NULL; + } + } + }else if (qh VERTEXneighbors) { + FORALLvertices + qh_setfreelong(&(vertex->neighbors)); + } + qh VERTEXneighbors= False; + qh GOODclosest= NULL; + if (allmem) { + FORALLfacets { + FOREACHridge_(facet->ridges) + ridge->seen= False; + } + FORALLfacets { + if (facet->visible) { + FOREACHridge_(facet->ridges) { + if (!otherfacet_(ridge, facet)->visible) + ridge->seen= True; /* an unattached ridge */ + } + } + } + while ((facet= qh facet_list)) { + FOREACHridge_(facet->ridges) { + if (ridge->seen) { + qh_setfree(&(ridge->vertices)); + qh_memfree(ridge, (int)sizeof(ridgeT)); + }else + ridge->seen= True; + } + qh_setfree(&(facet->outsideset)); + qh_setfree(&(facet->coplanarset)); + qh_setfree(&(facet->neighbors)); + qh_setfree(&(facet->ridges)); + qh_setfree(&(facet->vertices)); + if (facet->next) + qh_delfacet(facet); + else { + qh_memfree(facet, (int)sizeof(facetT)); + qh visible_list= qh newfacet_list= qh facet_list= NULL; + } + } + }else { + FORALLfacets { + qh_setfreelong(&(facet->outsideset)); + qh_setfreelong(&(facet->coplanarset)); + if (!facet->simplicial) { + qh_setfreelong(&(facet->neighbors)); + qh_setfreelong(&(facet->ridges)); + qh_setfreelong(&(facet->vertices)); + } + } + } + qh_setfree(&(qh hash_table)); + qh_memfree(qh interior_point, qh normal_size); + qh interior_point= NULL; + FOREACHmerge_(qh facet_mergeset) /* usually empty */ + qh_memfree(merge, (int)sizeof(mergeT)); + qh facet_mergeset= NULL; /* temp set */ + qh degen_mergeset= NULL; /* temp set */ + qh_settempfree_all(); +} /* freebuild */ + +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="freeqhull">-</a> + + qh_freeqhull( allmem ) + see qh_freeqhull2 + if qh_QHpointer, frees qh_qh +*/ +void qh_freeqhull(boolT allmem) { + qh_freeqhull2(allmem); +#if qh_QHpointer + qh_free(qh_qh); + qh_qh= NULL; +#endif +} + +/*-<a href="qh-globa.htm#TOC" +>-------------------------------</a><a name="freeqhull2">-</a> + +qh_freeqhull2( 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() + +design: + free global and temporary memory from qh_initbuild and qh_buildhull + free buffers + free statistics +*/ +void qh_freeqhull2(boolT allmem) { + + qh NOerrexit= True; /* no more setjmp since called at exit and ~QhullQh */ + trace1((qh ferr, 1006, "qh_freeqhull: free global memory\n")); + qh_freebuild(allmem); + qh_freebuffers(); + qh_freestatistics(); +#if qh_QHpointer + memset((char *)qh_qh, 0, sizeof(qhT)); + /* qh_qh freed by caller, qh_freeqhull() */ +#else + memset((char *)&qh_qh, 0, sizeof(qhT)); +#endif + qh NOerrexit= True; +} /* freeqhull2 */ + +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="init_A">-</a> + + qh_init_A( infile, outfile, errfile, argc, argv ) + initialize memory and stdio files + convert input options to option string (qh.qhull_command) + + notes: + infile may be NULL if qh_readpoints() is not called + + errfile should always be defined. It is used for reporting + errors. outfile is used for output and format options. + + argc/argv may be 0/NULL + + called before error handling initialized + qh_errexit() may not be used +*/ +void qh_init_A(FILE *infile, FILE *outfile, FILE *errfile, int argc, char *argv[]) { + qh_meminit(errfile); + qh_initqhull_start(infile, outfile, errfile); + qh_init_qhull_command(argc, argv); +} /* init_A */ + +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="init_B">-</a> + + qh_init_B( points, numpoints, dim, ismalloc ) + initialize globals for points array + + points has numpoints dim-dimensional points + points[0] is the first coordinate of the first point + points[1] is the second coordinate of the first point + points[dim] is the first coordinate of the second point + + ismalloc=True + Qhull will call qh_free(points) on exit or input transformation + ismalloc=False + Qhull will allocate a new point array if needed for input transformation + + qh.qhull_command + is the option string. + It is defined by qh_init_B(), qh_qhull_command(), or qh_initflags + + returns: + if qh.PROJECTinput or (qh.DELAUNAY and qh.PROJECTdelaunay) + projects the input to a new point array + + if qh.DELAUNAY, + qh.hull_dim is increased by one + if qh.ATinfinity, + qh_projectinput adds point-at-infinity for Delaunay tri. + + if qh.SCALEinput + changes the upper and lower bounds of the input, see qh_scaleinput() + + if qh.ROTATEinput + rotates the input by a random rotation, see qh_rotateinput() + if qh.DELAUNAY + rotates about the last coordinate + + notes: + called after points are defined + qh_errexit() may be used +*/ +void qh_init_B(coordT *points, int numpoints, int dim, boolT ismalloc) { + qh_initqhull_globals(points, numpoints, dim, ismalloc); + if (qhmem.LASTsize == 0) + qh_initqhull_mem(); + /* mem.c and qset.c are initialized */ + qh_initqhull_buffers(); + qh_initthresholds(qh qhull_command); + if (qh PROJECTinput || (qh DELAUNAY && qh PROJECTdelaunay)) + qh_projectinput(); + if (qh SCALEinput) + qh_scaleinput(); + if (qh ROTATErandom >= 0) { + qh_randommatrix(qh gm_matrix, qh hull_dim, qh gm_row); + if (qh DELAUNAY) { + int k, lastk= qh hull_dim-1; + for (k=0; k < lastk; k++) { + qh gm_row[k][lastk]= 0.0; + qh gm_row[lastk][k]= 0.0; + } + qh gm_row[lastk][lastk]= 1.0; + } + qh_gram_schmidt(qh hull_dim, qh gm_row); + qh_rotateinput(qh gm_row); + } +} /* init_B */ + +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="init_qhull_command">-</a> + + qh_init_qhull_command( 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(int argc, char *argv[]) { + + if (!qh_argv_to_command(argc, argv, qh qhull_command, (int)sizeof(qh qhull_command))){ + /* Assumes qh.ferr is defined. */ + qh_fprintf(qh ferr, 6033, "qhull input error: more than %d characters in command line\n", + (int)sizeof(qh qhull_command)); + qh_exit(qh_ERRinput); /* error reported, can not use qh_errexit */ + } +} /* init_qhull_command */ + +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="initflags">-</a> + + qh_initflags( 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 d") + use qh_strtol/strtod since strtol/strtod may or may not skip trailing spaces + + see: + qh_initthresholds() continues processing of 'Pdn' and 'PDn' + 'prompt' in unix.c for documentation + + design: + for each space-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(char *command) { + int k, i, lastproject; + char *s= command, *t, *prev_s, *start, key; + boolT isgeom= False, wasproject; + realT r; + + if(qh NOerrexit){/* without this comment, segfault in gcc 4.4.0 mingw32 */ + qh_fprintf(qh ferr, 6245, "qhull initflags error: qh.NOerrexit was not cleared before calling qh_initflags(). It should be cleared after setjmp(). Exit qhull."); + qh_exit(6245); + } + if (command <= &qh qhull_command[0] || command > &qh qhull_command[0] + sizeof(qh qhull_command)) { + if (command != &qh qhull_command[0]) { + *qh qhull_command= '\0'; + strncat(qh qhull_command, command, sizeof(qh qhull_command)-strlen(qh qhull_command)-1); + } + while (*s && !isspace(*s)) /* skip program name */ + s++; + } + while (*s) { + while (*s && isspace(*s)) + s++; + if (*s == '-') + s++; + if (!*s) + break; + prev_s= s; + switch (*s++) { + case 'd': + qh_option("delaunay", NULL, NULL); + qh DELAUNAY= True; + break; + case 'f': + qh_option("facets", NULL, NULL); + qh_appendprint(qh_PRINTfacets); + break; + case 'i': + qh_option("incidence", NULL, NULL); + qh_appendprint(qh_PRINTincidences); + break; + case 'm': + qh_option("mathematica", NULL, NULL); + qh_appendprint(qh_PRINTmathematica); + break; + case 'n': + qh_option("normals", NULL, NULL); + qh_appendprint(qh_PRINTnormals); + break; + case 'o': + qh_option("offFile", NULL, NULL); + qh_appendprint(qh_PRINToff); + break; + case 'p': + qh_option("points", NULL, NULL); + qh_appendprint(qh_PRINTpoints); + break; + case 's': + qh_option("summary", NULL, NULL); + qh PRINTsummary= True; + break; + case 'v': + qh_option("voronoi", NULL, NULL); + qh VORONOI= True; + qh DELAUNAY= True; + break; + case 'A': + if (!isdigit(*s) && *s != '.' && *s != '-') + qh_fprintf(qh ferr, 7002, "qhull warning: no maximum cosine angle given for option 'An'. Ignored.\n"); + else { + if (*s == '-') { + qh premerge_cos= -qh_strtod(s, &s); + qh_option("Angle-premerge-", NULL, &qh premerge_cos); + qh PREmerge= True; + }else { + qh postmerge_cos= qh_strtod(s, &s); + qh_option("Angle-postmerge", NULL, &qh postmerge_cos); + qh POSTmerge= True; + } + qh MERGING= True; + } + break; + case 'C': + if (!isdigit(*s) && *s != '.' && *s != '-') + qh_fprintf(qh ferr, 7003, "qhull warning: no centrum radius given for option 'Cn'. Ignored.\n"); + else { + if (*s == '-') { + qh premerge_centrum= -qh_strtod(s, &s); + qh_option("Centrum-premerge-", NULL, &qh premerge_centrum); + qh PREmerge= True; + }else { + qh postmerge_centrum= qh_strtod(s, &s); + qh_option("Centrum-postmerge", NULL, &qh postmerge_centrum); + qh POSTmerge= True; + } + qh MERGING= True; + } + break; + case 'E': + if (*s == '-') + qh_fprintf(qh ferr, 7004, "qhull warning: negative maximum roundoff given for option 'An'. Ignored.\n"); + else if (!isdigit(*s)) + qh_fprintf(qh ferr, 7005, "qhull warning: no maximum roundoff given for option 'En'. Ignored.\n"); + else { + qh DISTround= qh_strtod(s, &s); + qh_option("Distance-roundoff", NULL, &qh DISTround); + qh SETroundoff= True; + } + break; + case 'H': + start= s; + qh HALFspace= True; + qh_strtod(s, &t); + while (t > s) { + if (*t && !isspace(*t)) { + if (*t == ',') + t++; + else + qh_fprintf(qh ferr, 7006, "qhull warning: origin for Halfspace intersection should be 'Hn,n,n,...'\n"); + } + s= t; + qh_strtod(s, &t); + } + if (start < t) { + if (!(qh feasible_string= (char*)calloc((size_t)(t-start+1), (size_t)1))) { + qh_fprintf(qh ferr, 6034, "qhull error: insufficient memory for 'Hn,n,n'\n"); + qh_errexit(qh_ERRmem, NULL, NULL); + } + strncpy(qh feasible_string, start, (size_t)(t-start)); + qh_option("Halfspace-about", NULL, NULL); + qh_option(qh feasible_string, NULL, NULL); + }else + qh_option("Halfspace", NULL, NULL); + break; + case 'R': + if (!isdigit(*s)) + qh_fprintf(qh ferr, 7007, "qhull warning: missing random perturbation for option 'Rn'. Ignored\n"); + else { + qh RANDOMfactor= qh_strtod(s, &s); + qh_option("Random_perturb", NULL, &qh RANDOMfactor); + qh RANDOMdist= True; + } + break; + case 'V': + if (!isdigit(*s) && *s != '-') + qh_fprintf(qh ferr, 7008, "qhull warning: missing visible distance for option 'Vn'. Ignored\n"); + else { + qh MINvisible= qh_strtod(s, &s); + qh_option("Visible", NULL, &qh MINvisible); + } + break; + case 'U': + if (!isdigit(*s) && *s != '-') + qh_fprintf(qh ferr, 7009, "qhull warning: missing coplanar distance for option 'Un'. Ignored\n"); + else { + qh MAXcoplanar= qh_strtod(s, &s); + qh_option("U-coplanar", NULL, &qh MAXcoplanar); + } + break; + case 'W': + if (*s == '-') + qh_fprintf(qh ferr, 7010, "qhull warning: negative outside width for option 'Wn'. Ignored.\n"); + else if (!isdigit(*s)) + qh_fprintf(qh ferr, 7011, "qhull warning: missing outside width for option 'Wn'. Ignored\n"); + else { + qh MINoutside= qh_strtod(s, &s); + qh_option("W-outside", NULL, &qh MINoutside); + qh APPROXhull= True; + } + break; + /************ sub menus ***************/ + case 'F': + while (*s && !isspace(*s)) { + switch (*s++) { + case 'a': + qh_option("Farea", NULL, NULL); + qh_appendprint(qh_PRINTarea); + qh GETarea= True; + break; + case 'A': + qh_option("FArea-total", NULL, NULL); + qh GETarea= True; + break; + case 'c': + qh_option("Fcoplanars", NULL, NULL); + qh_appendprint(qh_PRINTcoplanars); + break; + case 'C': + qh_option("FCentrums", NULL, NULL); + qh_appendprint(qh_PRINTcentrums); + break; + case 'd': + qh_option("Fd-cdd-in", NULL, NULL); + qh CDDinput= True; + break; + case 'D': + qh_option("FD-cdd-out", NULL, NULL); + qh CDDoutput= True; + break; + case 'F': + qh_option("FFacets-xridge", NULL, NULL); + qh_appendprint(qh_PRINTfacets_xridge); + break; + case 'i': + qh_option("Finner", NULL, NULL); + qh_appendprint(qh_PRINTinner); + break; + case 'I': + qh_option("FIDs", NULL, NULL); + qh_appendprint(qh_PRINTids); + break; + case 'm': + qh_option("Fmerges", NULL, NULL); + qh_appendprint(qh_PRINTmerges); + break; + case 'M': + qh_option("FMaple", NULL, NULL); + qh_appendprint(qh_PRINTmaple); + break; + case 'n': + qh_option("Fneighbors", NULL, NULL); + qh_appendprint(qh_PRINTneighbors); + break; + case 'N': + qh_option("FNeighbors-vertex", NULL, NULL); + qh_appendprint(qh_PRINTvneighbors); + break; + case 'o': + qh_option("Fouter", NULL, NULL); + qh_appendprint(qh_PRINTouter); + break; + case 'O': + if (qh PRINToptions1st) { + qh_option("FOptions", NULL, NULL); + qh_appendprint(qh_PRINToptions); + }else + qh PRINToptions1st= True; + break; + case 'p': + qh_option("Fpoint-intersect", NULL, NULL); + qh_appendprint(qh_PRINTpointintersect); + break; + case 'P': + qh_option("FPoint-nearest", NULL, NULL); + qh_appendprint(qh_PRINTpointnearest); + break; + case 'Q': + qh_option("FQhull", NULL, NULL); + qh_appendprint(qh_PRINTqhull); + break; + case 's': + qh_option("Fsummary", NULL, NULL); + qh_appendprint(qh_PRINTsummary); + break; + case 'S': + qh_option("FSize", NULL, NULL); + qh_appendprint(qh_PRINTsize); + qh GETarea= True; + break; + case 't': + qh_option("Ftriangles", NULL, NULL); + qh_appendprint(qh_PRINTtriangles); + break; + case 'v': + /* option set in qh_initqhull_globals */ + qh_appendprint(qh_PRINTvertices); + break; + case 'V': + qh_option("FVertex-average", NULL, NULL); + qh_appendprint(qh_PRINTaverage); + break; + case 'x': + qh_option("Fxtremes", NULL, NULL); + qh_appendprint(qh_PRINTextremes); + break; + default: + s--; + qh_fprintf(qh ferr, 7012, "qhull warning: unknown 'F' output option %c, rest ignored\n", (int)s[0]); + while (*++s && !isspace(*s)); + break; + } + } + break; + case 'G': + isgeom= True; + qh_appendprint(qh_PRINTgeom); + while (*s && !isspace(*s)) { + switch (*s++) { + case 'a': + qh_option("Gall-points", NULL, NULL); + qh PRINTdots= True; + break; + case 'c': + qh_option("Gcentrums", NULL, NULL); + qh PRINTcentrums= True; + break; + case 'h': + qh_option("Gintersections", NULL, NULL); + qh DOintersections= True; + break; + case 'i': + qh_option("Ginner", NULL, NULL); + qh PRINTinner= True; + break; + case 'n': + qh_option("Gno-planes", NULL, NULL); + qh PRINTnoplanes= True; + break; + case 'o': + qh_option("Gouter", NULL, NULL); + qh PRINTouter= True; + break; + case 'p': + qh_option("Gpoints", NULL, NULL); + qh PRINTcoplanar= True; + break; + case 'r': + qh_option("Gridges", NULL, NULL); + qh PRINTridges= True; + break; + case 't': + qh_option("Gtransparent", NULL, NULL); + qh PRINTtransparent= True; + break; + case 'v': + qh_option("Gvertices", NULL, NULL); + qh PRINTspheres= True; + break; + case 'D': + if (!isdigit(*s)) + qh_fprintf(qh ferr, 6035, "qhull input error: missing dimension for option 'GDn'\n"); + else { + if (qh DROPdim >= 0) + qh_fprintf(qh ferr, 7013, "qhull warning: can only drop one dimension. Previous 'GD%d' ignored\n", + qh DROPdim); + qh DROPdim= qh_strtol(s, &s); + qh_option("GDrop-dim", &qh DROPdim, NULL); + } + break; + default: + s--; + qh_fprintf(qh ferr, 7014, "qhull warning: unknown 'G' print option %c, rest ignored\n", (int)s[0]); + while (*++s && !isspace(*s)); + break; + } + } + break; + case 'P': + while (*s && !isspace(*s)) { + switch (*s++) { + case 'd': case 'D': /* see qh_initthresholds() */ + key= s[-1]; + i= qh_strtol(s, &s); + r= 0; + if (*s == ':') { + s++; + r= qh_strtod(s, &s); + } + if (key == 'd') + qh_option("Pdrop-facets-dim-less", &i, &r); + else + qh_option("PDrop-facets-dim-more", &i, &r); + break; + case 'g': + qh_option("Pgood-facets", NULL, NULL); + qh PRINTgood= True; + break; + case 'G': + qh_option("PGood-facet-neighbors", NULL, NULL); + qh PRINTneighbors= True; + break; + case 'o': + qh_option("Poutput-forced", NULL, NULL); + qh FORCEoutput= True; + break; + case 'p': + qh_option("Pprecision-ignore", NULL, NULL); + qh PRINTprecision= False; + break; + case 'A': + if (!isdigit(*s)) + qh_fprintf(qh ferr, 6036, "qhull input error: missing facet count for keep area option 'PAn'\n"); + else { + qh KEEParea= qh_strtol(s, &s); + qh_option("PArea-keep", &qh KEEParea, NULL); + qh GETarea= True; + } + break; + case 'F': + if (!isdigit(*s)) + qh_fprintf(qh ferr, 6037, "qhull input error: missing facet area for option 'PFn'\n"); + else { + qh KEEPminArea= qh_strtod(s, &s); + qh_option("PFacet-area-keep", NULL, &qh KEEPminArea); + qh GETarea= True; + } + break; + case 'M': + if (!isdigit(*s)) + qh_fprintf(qh ferr, 6038, "qhull input error: missing merge count for option 'PMn'\n"); + else { + qh KEEPmerge= qh_strtol(s, &s); + qh_option("PMerge-keep", &qh KEEPmerge, NULL); + } + break; + default: + s--; + qh_fprintf(qh ferr, 7015, "qhull warning: unknown 'P' print option %c, rest ignored\n", (int)s[0]); + while (*++s && !isspace(*s)); + break; + } + } + break; + case 'Q': + lastproject= -1; + while (*s && !isspace(*s)) { + switch (*s++) { + case 'b': case 'B': /* handled by qh_initthresholds */ + key= s[-1]; + if (key == 'b' && *s == 'B') { + s++; + r= qh_DEFAULTbox; + qh SCALEinput= True; + qh_option("QbBound-unit-box", NULL, &r); + break; + } + if (key == 'b' && *s == 'b') { + s++; + qh SCALElast= True; + qh_option("Qbbound-last", NULL, NULL); + break; + } + k= qh_strtol(s, &s); + r= 0.0; + wasproject= False; + if (*s == ':') { + s++; + if ((r= qh_strtod(s, &s)) == 0.0) { + t= s; /* need true dimension for memory allocation */ + while (*t && !isspace(*t)) { + if (toupper(*t++) == 'B' + && k == qh_strtol(t, &t) + && *t++ == ':' + && qh_strtod(t, &t) == 0.0) { + qh PROJECTinput++; + trace2((qh ferr, 2004, "qh_initflags: project dimension %d\n", k)); + qh_option("Qb-project-dim", &k, NULL); + wasproject= True; + lastproject= k; + break; + } + } + } + } + if (!wasproject) { + if (lastproject == k && r == 0.0) + lastproject= -1; /* doesn't catch all possible sequences */ + else if (key == 'b') { + qh SCALEinput= True; + if (r == 0.0) + r= -qh_DEFAULTbox; + qh_option("Qbound-dim-low", &k, &r); + }else { + qh SCALEinput= True; + if (r == 0.0) + r= qh_DEFAULTbox; + qh_option("QBound-dim-high", &k, &r); + } + } + break; + case 'c': + qh_option("Qcoplanar-keep", NULL, NULL); + qh KEEPcoplanar= True; + break; + case 'f': + qh_option("Qfurthest-outside", NULL, NULL); + qh BESToutside= True; + break; + case 'g': + qh_option("Qgood-facets-only", NULL, NULL); + qh ONLYgood= True; + break; + case 'i': + qh_option("Qinterior-keep", NULL, NULL); + qh KEEPinside= True; + break; + case 'm': + qh_option("Qmax-outside-only", NULL, NULL); + qh ONLYmax= True; + break; + case 'r': + qh_option("Qrandom-outside", NULL, NULL); + qh RANDOMoutside= True; + break; + case 's': + qh_option("Qsearch-initial-simplex", NULL, NULL); + qh ALLpoints= True; + break; + case 't': + qh_option("Qtriangulate", NULL, NULL); + qh TRIangulate= True; + break; + case 'T': + qh_option("QTestPoints", NULL, NULL); + if (!isdigit(*s)) + qh_fprintf(qh ferr, 6039, "qhull input error: missing number of test points for option 'QTn'\n"); + else { + qh TESTpoints= qh_strtol(s, &s); + qh_option("QTestPoints", &qh TESTpoints, NULL); + } + break; + case 'u': + qh_option("QupperDelaunay", NULL, NULL); + qh UPPERdelaunay= True; + break; + case 'v': + qh_option("Qvertex-neighbors-convex", NULL, NULL); + qh TESTvneighbors= True; + break; + case 'x': + qh_option("Qxact-merge", NULL, NULL); + qh MERGEexact= True; + break; + case 'z': + qh_option("Qz-infinity-point", NULL, NULL); + qh ATinfinity= True; + break; + case '0': + qh_option("Q0-no-premerge", NULL, NULL); + qh NOpremerge= True; + break; + case '1': + if (!isdigit(*s)) { + qh_option("Q1-no-angle-sort", NULL, NULL); + qh ANGLEmerge= False; + break; + } + switch (*s++) { + case '0': + qh_option("Q10-no-narrow", NULL, NULL); + qh NOnarrow= True; + break; + case '1': + qh_option("Q11-trinormals Qtriangulate", NULL, NULL); + qh TRInormals= True; + qh TRIangulate= True; + break; + case '2': + qh_option("Q12-no-wide-dup", NULL, NULL); + qh NOwide= True; + break; + default: + s--; + qh_fprintf(qh ferr, 7016, "qhull warning: unknown 'Q' qhull option 1%c, rest ignored\n", (int)s[0]); + while (*++s && !isspace(*s)); + break; + } + break; + case '2': + qh_option("Q2-no-merge-independent", NULL, NULL); + qh MERGEindependent= False; + goto LABELcheckdigit; + break; /* no warnings */ + case '3': + qh_option("Q3-no-merge-vertices", NULL, NULL); + qh MERGEvertices= False; + LABELcheckdigit: + if (isdigit(*s)) + qh_fprintf(qh ferr, 7017, "qhull warning: can not follow '1', '2', or '3' with a digit. '%c' skipped.\n", + *s++); + break; + case '4': + qh_option("Q4-avoid-old-into-new", NULL, NULL); + qh AVOIDold= True; + break; + case '5': + qh_option("Q5-no-check-outer", NULL, NULL); + qh SKIPcheckmax= True; + break; + case '6': + qh_option("Q6-no-concave-merge", NULL, NULL); + qh SKIPconvex= True; + break; + case '7': + qh_option("Q7-no-breadth-first", NULL, NULL); + qh VIRTUALmemory= True; + break; + case '8': + qh_option("Q8-no-near-inside", NULL, NULL); + qh NOnearinside= True; + break; + case '9': + qh_option("Q9-pick-furthest", NULL, NULL); + qh PICKfurthest= True; + break; + case 'G': + i= qh_strtol(s, &t); + if (qh GOODpoint) + qh_fprintf(qh ferr, 7018, "qhull warning: good point already defined for option 'QGn'. Ignored\n"); + else if (s == t) + qh_fprintf(qh ferr, 7019, "qhull warning: missing good point id for option 'QGn'. Ignored\n"); + else if (i < 0 || *s == '-') { + qh GOODpoint= i-1; + qh_option("QGood-if-dont-see-point", &i, NULL); + }else { + qh GOODpoint= i+1; + qh_option("QGood-if-see-point", &i, NULL); + } + s= t; + break; + case 'J': + if (!isdigit(*s) && *s != '-') + qh JOGGLEmax= 0.0; + else { + qh JOGGLEmax= (realT) qh_strtod(s, &s); + qh_option("QJoggle", NULL, &qh JOGGLEmax); + } + break; + case 'R': + if (!isdigit(*s) && *s != '-') + qh_fprintf(qh ferr, 7020, "qhull warning: missing random seed for option 'QRn'. Ignored\n"); + else { + qh ROTATErandom= i= qh_strtol(s, &s); + if (i > 0) + qh_option("QRotate-id", &i, NULL ); + else if (i < -1) + qh_option("QRandom-seed", &i, NULL ); + } + break; + case 'V': + i= qh_strtol(s, &t); + if (qh GOODvertex) + qh_fprintf(qh ferr, 7021, "qhull warning: good vertex already defined for option 'QVn'. Ignored\n"); + else if (s == t) + qh_fprintf(qh ferr, 7022, "qhull warning: no good point id given for option 'QVn'. Ignored\n"); + else if (i < 0) { + qh GOODvertex= i - 1; + qh_option("QV-good-facets-not-point", &i, NULL); + }else { + qh_option("QV-good-facets-point", &i, NULL); + qh GOODvertex= i + 1; + } + s= t; + break; + default: + s--; + qh_fprintf(qh ferr, 7023, "qhull warning: unknown 'Q' qhull option %c, rest ignored\n", (int)s[0]); + while (*++s && !isspace(*s)); + break; + } + } + break; + case 'T': + while (*s && !isspace(*s)) { + if (isdigit(*s) || *s == '-') + qh IStracing= qh_strtol(s, &s); + else switch (*s++) { + case 'a': + qh_option("Tannotate-output", NULL, NULL); + qh ANNOTATEoutput= True; + break; + case 'c': + qh_option("Tcheck-frequently", NULL, NULL); + qh CHECKfrequently= True; + break; + case 's': + qh_option("Tstatistics", NULL, NULL); + qh PRINTstatistics= True; + break; + case 'v': + qh_option("Tverify", NULL, NULL); + qh VERIFYoutput= True; + break; + case 'z': + if (qh ferr == qh_FILEstderr) { + /* The C++ interface captures the output in qh_fprint_qhull() */ + qh_option("Tz-stdout", NULL, NULL); + qh USEstdout= True; + }else if (!qh fout) + qh_fprintf(qh ferr, 7024, "qhull warning: output file undefined(stdout). Option 'Tz' ignored.\n"); + else { + qh_option("Tz-stdout", NULL, NULL); + qh USEstdout= True; + qh ferr= qh fout; + qhmem.ferr= qh fout; + } + break; + case 'C': + if (!isdigit(*s)) + qh_fprintf(qh ferr, 7025, "qhull warning: missing point id for cone for trace option 'TCn'. Ignored\n"); + else { + i= qh_strtol(s, &s); + qh_option("TCone-stop", &i, NULL); + qh STOPcone= i + 1; + } + break; + case 'F': + if (!isdigit(*s)) + qh_fprintf(qh ferr, 7026, "qhull warning: missing frequency count for trace option 'TFn'. Ignored\n"); + else { + qh REPORTfreq= qh_strtol(s, &s); + qh_option("TFacet-log", &qh REPORTfreq, NULL); + qh REPORTfreq2= qh REPORTfreq/2; /* for tracemerging() */ + } + break; + case 'I': + if (!isspace(*s)) + qh_fprintf(qh ferr, 7027, "qhull warning: missing space between 'TI' and filename, %s\n", s); + while (isspace(*s)) + s++; + t= qh_skipfilename(s); + { + char filename[qh_FILENAMElen]; + + qh_copyfilename(filename, (int)sizeof(filename), s, (int)(t-s)); /* WARN64 */ + s= t; + if (!freopen(filename, "r", stdin)) { + qh_fprintf(qh ferr, 6041, "qhull error: could not open file \"%s\".", filename); + qh_errexit(qh_ERRinput, NULL, NULL); + }else { + qh_option("TInput-file", NULL, NULL); + qh_option(filename, NULL, NULL); + } + } + break; + case 'O': + if (!isspace(*s)) + qh_fprintf(qh ferr, 7028, "qhull warning: missing space between 'TO' and filename, %s\n", s); + while (isspace(*s)) + s++; + t= qh_skipfilename(s); + { + char filename[qh_FILENAMElen]; + + qh_copyfilename(filename, (int)sizeof(filename), s, (int)(t-s)); /* WARN64 */ + s= t; + if (!qh fout) { + qh_fprintf(qh ferr, 6266, "qhull input warning: qh.fout was not set by caller. Cannot use option 'TO' to redirect output. Ignoring option 'TO'\n"); + }else if (!freopen(filename, "w", qh fout)) { + qh_fprintf(qh ferr, 6044, "qhull error: could not open file \"%s\".", filename); + qh_errexit(qh_ERRinput, NULL, NULL); + }else { + qh_option("TOutput-file", NULL, NULL); + qh_option(filename, NULL, NULL); + } + } + break; + case 'P': + if (!isdigit(*s)) + qh_fprintf(qh ferr, 7029, "qhull warning: missing point id for trace option 'TPn'. Ignored\n"); + else { + qh TRACEpoint= qh_strtol(s, &s); + qh_option("Trace-point", &qh TRACEpoint, NULL); + } + break; + case 'M': + if (!isdigit(*s)) + qh_fprintf(qh ferr, 7030, "qhull warning: missing merge id for trace option 'TMn'. Ignored\n"); + else { + qh TRACEmerge= qh_strtol(s, &s); + qh_option("Trace-merge", &qh TRACEmerge, NULL); + } + break; + case 'R': + if (!isdigit(*s)) + qh_fprintf(qh ferr, 7031, "qhull warning: missing rerun count for trace option 'TRn'. Ignored\n"); + else { + qh RERUN= qh_strtol(s, &s); + qh_option("TRerun", &qh RERUN, NULL); + } + break; + case 'V': + i= qh_strtol(s, &t); + if (s == t) + qh_fprintf(qh ferr, 7032, "qhull warning: missing furthest point id for trace option 'TVn'. Ignored\n"); + else if (i < 0) { + qh STOPpoint= i - 1; + qh_option("TV-stop-before-point", &i, NULL); + }else { + qh STOPpoint= i + 1; + qh_option("TV-stop-after-point", &i, NULL); + } + s= t; + break; + case 'W': + if (!isdigit(*s)) + qh_fprintf(qh ferr, 7033, "qhull warning: missing max width for trace option 'TWn'. Ignored\n"); + else { + qh TRACEdist= (realT) qh_strtod(s, &s); + qh_option("TWide-trace", NULL, &qh TRACEdist); + } + break; + default: + s--; + qh_fprintf(qh ferr, 7034, "qhull warning: unknown 'T' trace option %c, rest ignored\n", (int)s[0]); + while (*++s && !isspace(*s)); + break; + } + } + break; + default: + qh_fprintf(qh ferr, 7035, "qhull warning: unknown flag %c(%x)\n", (int)s[-1], + (int)s[-1]); + break; + } + if (s-1 == prev_s && *s && !isspace(*s)) { + qh_fprintf(qh ferr, 7036, "qhull warning: missing space after flag %c(%x); reserved for menu. Skipped.\n", + (int)*prev_s, (int)*prev_s); + while (*s && !isspace(*s)) + s++; + } + } + if (qh STOPcone && qh JOGGLEmax < REALmax/2) + qh_fprintf(qh ferr, 7078, "qhull warning: 'TCn' (stopCone) ignored when used with 'QJn' (joggle)\n"); + if (isgeom && !qh FORCEoutput && qh PRINTout[1]) + qh_fprintf(qh ferr, 7037, "qhull warning: additional output formats are not compatible with Geomview\n"); + /* set derived values in qh_initqhull_globals */ +} /* initflags */ + + +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="initqhull_buffers">-</a> + + qh_initqhull_buffers() + initialize global memory buffers + + notes: + must match qh_freebuffers() +*/ +void qh_initqhull_buffers(void) { + int k; + + qh TEMPsize= (qhmem.LASTsize - sizeof(setT))/SETelemsize; + if (qh TEMPsize <= 0 || qh TEMPsize > qhmem.LASTsize) + qh TEMPsize= 8; /* e.g., if qh_NOmem */ + qh other_points= qh_setnew(qh TEMPsize); + qh del_vertices= qh_setnew(qh TEMPsize); + qh coplanarfacetset= qh_setnew(qh TEMPsize); + qh NEARzero= (realT *)qh_memalloc(qh hull_dim * sizeof(realT)); + qh lower_threshold= (realT *)qh_memalloc((qh input_dim+1) * sizeof(realT)); + qh upper_threshold= (realT *)qh_memalloc((qh input_dim+1) * sizeof(realT)); + qh lower_bound= (realT *)qh_memalloc((qh input_dim+1) * sizeof(realT)); + qh upper_bound= (realT *)qh_memalloc((qh input_dim+1) * sizeof(realT)); + for (k=qh input_dim+1; k--; ) { /* duplicated in qh_initqhull_buffers and qh_clear_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 hull_dim+1) * qh hull_dim * sizeof(coordT)); + qh gm_row= (coordT **)qh_memalloc((qh hull_dim+1) * sizeof(coordT *)); +} /* initqhull_buffers */ + +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="initqhull_globals">-</a> + + qh_initqhull_globals( points, numpoints, dim, ismalloc ) + initialize globals + if ismalloc + points were malloc'd and qhull should free at end + + returns: + sets qh.first_point, num_points, input_dim, hull_dim and others + seeds random number generator (seed=1 if tracing) + modifies qh.hull_dim if ((qh.DELAUNAY and qh.PROJECTdelaunay) or qh.PROJECTinput) + adjust user flags as needed + also checks DIM3 dependencies and constants + + notes: + do not use qh_point() since an input transformation may move them elsewhere + + see: + qh_initqhull_start() sets default values for non-zero globals + + design: + initialize points array from input arguments + test for qh.ZEROcentrum + (i.e., use opposite vertex instead of cetrum for convexity testing) + initialize qh.CENTERtype, qh.normal_size, + qh.center_size, qh.TRACEpoint/level, + initialize and test random numbers + qh_initqhull_outputflags() -- adjust and test output flags +*/ +void qh_initqhull_globals(coordT *points, int numpoints, int dim, boolT ismalloc) { + int seed, pointsneeded, extra= 0, i, randi, k; + realT randr; + realT factorial; + + time_t timedata; + + trace0((qh ferr, 13, "qh_initqhull_globals: for %s | %s\n", qh rbox_command, + qh qhull_command)); + qh POINTSmalloc= ismalloc; + qh first_point= points; + qh num_points= numpoints; + qh hull_dim= qh input_dim= dim; + if (!qh NOpremerge && !qh MERGEexact && !qh PREmerge && qh JOGGLEmax > REALmax/2) { + qh MERGING= True; + if (qh hull_dim <= 4) { + qh PREmerge= True; + qh_option("_pre-merge", NULL, NULL); + }else { + qh MERGEexact= True; + qh_option("Qxact_merge", NULL, NULL); + } + }else if (qh MERGEexact) + qh MERGING= True; + if (!qh NOpremerge && qh JOGGLEmax > REALmax/2) { +#ifdef qh_NOmerge + qh JOGGLEmax= 0.0; +#endif + } + if (qh TRIangulate && qh JOGGLEmax < REALmax/2 && qh PRINTprecision) + qh_fprintf(qh ferr, 7038, "qhull warning: joggle('QJ') always produces simplicial output. Triangulated output('Qt') does nothing.\n"); + if (qh JOGGLEmax < REALmax/2 && qh DELAUNAY && !qh SCALEinput && !qh SCALElast) { + qh SCALElast= True; + qh_option("Qbbound-last-qj", NULL, NULL); + } + if (qh MERGING && !qh POSTmerge && qh premerge_cos > REALmax/2 + && qh premerge_centrum == 0) { + qh ZEROcentrum= True; + qh ZEROall_ok= True; + qh_option("_zero-centrum", NULL, NULL); + } + if (qh JOGGLEmax < REALmax/2 && REALepsilon > 2e-8 && qh PRINTprecision) + qh_fprintf(qh ferr, 7039, "qhull warning: real epsilon, %2.2g, is probably too large for joggle('QJn')\nRecompile with double precision reals(see user.h).\n", + REALepsilon); +#ifdef qh_NOmerge + if (qh MERGING) { + qh_fprintf(qh ferr, 6045, "qhull input error: merging not installed(qh_NOmerge + 'Qx', 'Cn' or 'An')\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } +#endif + if (qh DELAUNAY && qh KEEPcoplanar && !qh KEEPinside) { + qh KEEPinside= True; + qh_option("Qinterior-keep", NULL, NULL); + } + if (qh DELAUNAY && qh HALFspace) { + qh_fprintf(qh ferr, 6046, "qhull input error: can not use Delaunay('d') or Voronoi('v') with halfspace intersection('H')\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } + if (!qh DELAUNAY && (qh UPPERdelaunay || qh ATinfinity)) { + qh_fprintf(qh ferr, 6047, "qhull input error: use upper-Delaunay('Qu') or infinity-point('Qz') with Delaunay('d') or Voronoi('v')\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } + if (qh UPPERdelaunay && qh ATinfinity) { + qh_fprintf(qh ferr, 6048, "qhull input error: can not use infinity-point('Qz') with upper-Delaunay('Qu')\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } + if (qh SCALElast && !qh DELAUNAY && qh PRINTprecision) + qh_fprintf(qh ferr, 7040, "qhull input warning: option 'Qbb' (scale-last-coordinate) is normally used with 'd' or 'v'\n"); + qh DOcheckmax= (!qh SKIPcheckmax && qh MERGING ); + qh KEEPnearinside= (qh DOcheckmax && !(qh KEEPinside && qh KEEPcoplanar) + && !qh NOnearinside); + if (qh MERGING) + qh CENTERtype= qh_AScentrum; + else if (qh VORONOI) + qh CENTERtype= qh_ASvoronoi; + if (qh TESTvneighbors && !qh MERGING) { + qh_fprintf(qh ferr, 6049, "qhull input error: test vertex neighbors('Qv') needs a merge option\n"); + qh_errexit(qh_ERRinput, NULL ,NULL); + } + if (qh PROJECTinput || (qh DELAUNAY && qh PROJECTdelaunay)) { + qh hull_dim -= qh PROJECTinput; + if (qh DELAUNAY) { + qh hull_dim++; + if (qh ATinfinity) + extra= 1; + } + } + if (qh hull_dim <= 1) { + qh_fprintf(qh ferr, 6050, "qhull error: dimension %d must be > 1\n", qh hull_dim); + qh_errexit(qh_ERRinput, NULL, NULL); + } + for (k=2, factorial=1.0; k < qh hull_dim; k++) + factorial *= k; + qh AREAfactor= 1.0 / factorial; + trace2((qh ferr, 2005, "qh_initqhull_globals: initialize globals. dim %d numpoints %d malloc? %d projected %d to hull_dim %d\n", + dim, numpoints, ismalloc, qh PROJECTinput, qh hull_dim)); + qh normal_size= qh hull_dim * sizeof(coordT); + qh center_size= qh normal_size - sizeof(coordT); + pointsneeded= qh hull_dim+1; + if (qh hull_dim > qh_DIMmergeVertex) { + qh MERGEvertices= False; + qh_option("Q3-no-merge-vertices-dim-high", NULL, NULL); + } + if (qh GOODpoint) + pointsneeded++; +#ifdef qh_NOtrace + if (qh IStracing) { + qh_fprintf(qh ferr, 6051, "qhull input error: tracing is not installed(qh_NOtrace in user.h)"); + qh_errexit(qh_ERRqhull, NULL, NULL); + } +#endif + if (qh RERUN > 1) { + qh TRACElastrun= qh IStracing; /* qh_build_withrestart duplicates next conditional */ + if (qh IStracing != -1) + qh IStracing= 0; + }else if (qh TRACEpoint != qh_IDunknown || qh TRACEdist < REALmax/2 || qh TRACEmerge) { + qh TRACElevel= (qh IStracing? qh IStracing : 3); + qh IStracing= 0; + } + if (qh ROTATErandom == 0 || qh ROTATErandom == -1) { + seed= (int)time(&timedata); + if (qh ROTATErandom == -1) { + seed= -seed; + qh_option("QRandom-seed", &seed, NULL ); + }else + qh_option("QRotate-random", &seed, NULL); + qh ROTATErandom= seed; + } + seed= qh ROTATErandom; + if (seed == INT_MIN) /* default value */ + seed= 1; + else if (seed < 0) + seed= -seed; + qh_RANDOMseed_(seed); + randr= 0.0; + for (i=1000; i--; ) { + randi= qh_RANDOMint; + randr += randi; + if (randi > qh_RANDOMmax) { + qh_fprintf(qh ferr, 8036, "\ +qhull configuration error (qh_RANDOMmax in user.h):\n\ + random integer %d > qh_RANDOMmax(%.8g)\n", + randi, qh_RANDOMmax); + qh_errexit(qh_ERRinput, NULL, NULL); + } + } + qh_RANDOMseed_(seed); + randr = randr/1000; + if (randr < qh_RANDOMmax * 0.1 + || randr > qh_RANDOMmax * 0.9) + qh_fprintf(qh ferr, 8037, "\ +qhull configuration warning (qh_RANDOMmax in user.h):\n\ + average of 1000 random integers (%.2g) is much different than expected (%.2g).\n\ + Is qh_RANDOMmax (%.2g) wrong?\n", + randr, qh_RANDOMmax * 0.5, qh_RANDOMmax); + qh RANDOMa= 2.0 * qh RANDOMfactor/qh_RANDOMmax; + qh RANDOMb= 1.0 - qh RANDOMfactor; + if (qh_HASHfactor < 1.1) { + qh_fprintf(qh ferr, 6052, "qhull internal error (qh_initqhull_globals): qh_HASHfactor %d must be at least 1.1. Qhull uses linear hash probing\n", + qh_HASHfactor); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + if (numpoints+extra < pointsneeded) { + qh_fprintf(qh ferr, 6214, "qhull input error: not enough points(%d) to construct initial simplex (need %d)\n", + numpoints, pointsneeded); + qh_errexit(qh_ERRinput, NULL, NULL); + } + qh_initqhull_outputflags(); +} /* initqhull_globals */ + +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="initqhull_mem">-</a> + + qh_initqhull_mem( ) + initialize mem.c for qhull + qh.hull_dim and qh.normal_size determine some of the allocation sizes + if qh.MERGING, + includes ridgeT + calls qh_user_memsizes() to add up to 10 additional sizes for quick allocation + (see numsizes below) + + returns: + mem.c already for qh_memalloc/qh_memfree (errors if called beforehand) + + notes: + qh_produceoutput() prints memsizes + +*/ +void qh_initqhull_mem(void) { + int numsizes; + int i; + + numsizes= 8+10; + qh_meminitbuffers(qh IStracing, qh_MEMalign, numsizes, + qh_MEMbufsize,qh_MEMinitbuf); + qh_memsize((int)sizeof(vertexT)); + if (qh MERGING) { + qh_memsize((int)sizeof(ridgeT)); + qh_memsize((int)sizeof(mergeT)); + } + qh_memsize((int)sizeof(facetT)); + i= sizeof(setT) + (qh hull_dim - 1) * SETelemsize; /* ridge.vertices */ + qh_memsize(i); + qh_memsize(qh normal_size); /* normal */ + i += SETelemsize; /* facet.vertices, .ridges, .neighbors */ + qh_memsize(i); + qh_user_memsizes(); + qh_memsetup(); +} /* initqhull_mem */ + +/*-<a href="qh-globa.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(void) { + boolT printgeom= False, printmath= False, printcoplanar= False; + int i; + + trace3((qh ferr, 3024, "qh_initqhull_outputflags: %s\n", qh qhull_command)); + if (!(qh PRINTgood || qh PRINTneighbors)) { + if (qh KEEParea || qh KEEPminArea < REALmax/2 || qh KEEPmerge || qh DELAUNAY + || (!qh ONLYgood && (qh GOODvertex || qh GOODpoint))) { + qh PRINTgood= True; + qh_option("Pgood", NULL, NULL); + } + } + if (qh PRINTtransparent) { + if (qh hull_dim != 4 || !qh DELAUNAY || qh VORONOI || qh DROPdim >= 0) { + qh_fprintf(qh ferr, 6215, "qhull input error: transparent Delaunay('Gt') needs 3-d Delaunay('d') w/o 'GDn'\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } + qh DROPdim = 3; + qh PRINTridges = True; + } + for (i=qh_PRINTEND; i--; ) { + if (qh PRINTout[i] == qh_PRINTgeom) + printgeom= True; + else if (qh PRINTout[i] == qh_PRINTmathematica || qh PRINTout[i] == qh_PRINTmaple) + printmath= True; + else if (qh PRINTout[i] == qh_PRINTcoplanars) + printcoplanar= True; + else if (qh PRINTout[i] == qh_PRINTpointnearest) + printcoplanar= True; + else if (qh PRINTout[i] == qh_PRINTpointintersect && !qh HALFspace) { + qh_fprintf(qh ferr, 6053, "qhull input error: option 'Fp' is only used for \nhalfspace intersection('Hn,n,n').\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + }else if (qh PRINTout[i] == qh_PRINTtriangles && (qh HALFspace || qh VORONOI)) { + qh_fprintf(qh ferr, 6054, "qhull input error: option 'Ft' is not available for Voronoi vertices or halfspace intersection\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + }else if (qh PRINTout[i] == qh_PRINTcentrums && qh VORONOI) { + qh_fprintf(qh ferr, 6055, "qhull input error: option 'FC' is not available for Voronoi vertices('v')\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + }else if (qh PRINTout[i] == qh_PRINTvertices) { + if (qh VORONOI) + qh_option("Fvoronoi", NULL, NULL); + else + qh_option("Fvertices", NULL, NULL); + } + } + if (printcoplanar && qh DELAUNAY && qh JOGGLEmax < REALmax/2) { + if (qh PRINTprecision) + qh_fprintf(qh ferr, 7041, "qhull input warning: 'QJ' (joggle) will usually prevent coincident input sites for options 'Fc' and 'FP'\n"); + } + if (printmath && (qh hull_dim > 3 || qh VORONOI)) { + qh_fprintf(qh ferr, 6056, "qhull input error: Mathematica and Maple output is only available for 2-d and 3-d convex hulls and 2-d Delaunay triangulations\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } + if (printgeom) { + if (qh hull_dim > 4) { + qh_fprintf(qh ferr, 6057, "qhull input error: Geomview output is only available for 2-d, 3-d and 4-d\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } + if (qh PRINTnoplanes && !(qh PRINTcoplanar + qh PRINTcentrums + + qh PRINTdots + qh PRINTspheres + qh DOintersections + qh PRINTridges)) { + qh_fprintf(qh ferr, 6058, "qhull input error: no output specified for Geomview\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } + if (qh VORONOI && (qh hull_dim > 3 || qh DROPdim >= 0)) { + qh_fprintf(qh ferr, 6059, "qhull input error: Geomview output for Voronoi diagrams only for 2-d\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } + /* can not warn about furthest-site Geomview output: no lower_threshold */ + if (qh hull_dim == 4 && qh DROPdim == -1 && + (qh PRINTcoplanar || qh PRINTspheres || qh PRINTcentrums)) { + qh_fprintf(qh ferr, 7042, "qhull input warning: coplanars, vertices, and centrums output not\n\ +available for 4-d output(ignored). Could use 'GDn' instead.\n"); + qh PRINTcoplanar= qh PRINTspheres= qh PRINTcentrums= False; + } + } + if (!qh KEEPcoplanar && !qh KEEPinside && !qh ONLYgood) { + if ((qh PRINTcoplanar && qh PRINTspheres) || printcoplanar) { + if (qh QHULLfinished) { + qh_fprintf(qh ferr, 7072, "qhull output warning: ignoring coplanar points, option 'Qc' was not set for the first run of qhull.\n"); + }else { + qh KEEPcoplanar = True; + qh_option("Qcoplanar", NULL, NULL); + } + } + } + qh PRINTdim= qh hull_dim; + if (qh DROPdim >=0) { /* after Geomview checks */ + if (qh DROPdim < qh hull_dim) { + qh PRINTdim--; + if (!printgeom || qh hull_dim < 3) + qh_fprintf(qh ferr, 7043, "qhull input warning: drop dimension 'GD%d' is only available for 3-d/4-d Geomview\n", qh DROPdim); + }else + qh DROPdim= -1; + }else if (qh VORONOI) { + qh DROPdim= qh hull_dim-1; + qh PRINTdim= qh hull_dim-1; + } +} /* qh_initqhull_outputflags */ + +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="initqhull_start">-</a> + + qh_initqhull_start( infile, outfile, errfile ) + allocate memory if needed and call qh_initqhull_start2() +*/ +void qh_initqhull_start(FILE *infile, FILE *outfile, FILE *errfile) { + +#if qh_QHpointer + if (qh_qh) { + qh_fprintf(errfile, 6205, "qhull error (qh_initqhull_start): qh_qh already defined. Call qh_save_qhull() first\n"); + qh_exit(qh_ERRqhull); /* no error handler */ + } + if (!(qh_qh= (qhT *)qh_malloc(sizeof(qhT)))) { + qh_fprintf(errfile, 6060, "qhull error (qh_initqhull_start): insufficient memory\n"); + qh_exit(qh_ERRmem); /* no error handler */ + } +#endif + qh_initstatistics(); + qh_initqhull_start2(infile, outfile, errfile); +} /* initqhull_start */ + +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="initqhull_start2">-</a> + + qh_initqhull_start2( infile, outfile, errfile ) + start initialization of qhull + initialize statistics, stdio, default values for global variables + assumes qh_qh is defined + notes: + report errors elsewhere, error handling and g_qhull_output [Qhull.cpp, QhullQh()] not in initialized + see: + qh_maxmin() determines the precision constants + qh_freeqhull2() +*/ +void qh_initqhull_start2(FILE *infile, FILE *outfile, FILE *errfile) { + time_t timedata; + int seed; + + qh_CPUclock; /* start the clock(for qh_clock). One-shot. */ +#if qh_QHpointer + memset((char *)qh_qh, 0, sizeof(qhT)); /* every field is 0, FALSE, NULL */ +#else + memset((char *)&qh_qh, 0, sizeof(qhT)); +#endif + qh ANGLEmerge= True; + qh DROPdim= -1; + qh ferr= errfile; + qh fin= infile; + qh fout= outfile; + qh furthest_id= qh_IDunknown; + qh JOGGLEmax= REALmax; + qh KEEPminArea = REALmax; + qh last_low= REALmax; + qh last_high= REALmax; + qh last_newhigh= REALmax; + qh max_outside= 0.0; + qh max_vertex= 0.0; + qh MAXabs_coord= 0.0; + qh MAXsumcoord= 0.0; + qh MAXwidth= -REALmax; + qh MERGEindependent= True; + qh MINdenom_1= fmax_(1.0/REALmax, REALmin); /* used by qh_scalepoints */ + qh MINoutside= 0.0; + qh MINvisible= REALmax; + qh MAXcoplanar= REALmax; + qh outside_err= REALmax; + qh premerge_centrum= 0.0; + qh premerge_cos= REALmax; + qh PRINTprecision= True; + qh PRINTradius= 0.0; + qh postmerge_cos= REALmax; + qh postmerge_centrum= 0.0; + qh ROTATErandom= INT_MIN; + qh MERGEvertices= True; + qh totarea= 0.0; + qh totvol= 0.0; + qh TRACEdist= REALmax; + qh TRACEpoint= qh_IDunknown; /* recompile or use 'TPn' */ + qh tracefacet_id= UINT_MAX; /* recompile to trace a facet */ + qh tracevertex_id= UINT_MAX; /* recompile to trace a vertex */ + seed= (int)time(&timedata); + qh_RANDOMseed_(seed); + qh run_id= qh_RANDOMint; + if(!qh run_id) + qh run_id++; /* guarantee non-zero */ + qh_option("run-id", &qh run_id, NULL); + strcat(qh qhull, "qhull"); +} /* initqhull_start2 */ + +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="initthresholds">-</a> + + qh_initthresholds( commandString ) + set thresholds for printing and scaling from commandString + + returns: + sets qh.GOODthreshold or qh.SPLITthreshold if 'Pd0D1' used + + see: + qh_initflags(), 'Qbk' 'QBk' 'Pdk' and 'PDk' + qh_inthresholds() + + design: + for each 'Pdn' or 'PDn' option + check syntax + set qh.lower_threshold or qh.upper_threshold + set qh.GOODthreshold if an unbounded threshold is used + set qh.SPLITthreshold if a bounded threshold is used +*/ +void qh_initthresholds(char *command) { + realT value; + int idx, maxdim, k; + char *s= command; /* non-const due to strtol */ + char key; + + maxdim= qh input_dim; + if (qh DELAUNAY && (qh PROJECTdelaunay || qh PROJECTinput)) + maxdim++; + while (*s) { + if (*s == '-') + s++; + if (*s == 'P') { + s++; + while (*s && !isspace(key= *s++)) { + if (key == 'd' || key == 'D') { + if (!isdigit(*s)) { + qh_fprintf(qh ferr, 7044, "qhull warning: no dimension given for Print option '%c' at: %s. Ignored\n", + key, s-1); + continue; + } + idx= qh_strtol(s, &s); + if (idx >= qh hull_dim) { + qh_fprintf(qh ferr, 7045, "qhull warning: dimension %d for Print option '%c' is >= %d. Ignored\n", + idx, key, qh hull_dim); + continue; + } + if (*s == ':') { + s++; + value= qh_strtod(s, &s); + if (fabs((double)value) > 1.0) { + qh_fprintf(qh ferr, 7046, "qhull warning: value %2.4g for Print option %c is > +1 or < -1. Ignored\n", + value, key); + continue; + } + }else + value= 0.0; + if (key == 'd') + qh lower_threshold[idx]= value; + else + qh upper_threshold[idx]= value; + } + } + }else if (*s == 'Q') { + s++; + while (*s && !isspace(key= *s++)) { + if (key == 'b' && *s == 'B') { + s++; + for (k=maxdim; k--; ) { + qh lower_bound[k]= -qh_DEFAULTbox; + qh upper_bound[k]= qh_DEFAULTbox; + } + }else if (key == 'b' && *s == 'b') + s++; + else if (key == 'b' || key == 'B') { + if (!isdigit(*s)) { + qh_fprintf(qh ferr, 7047, "qhull warning: no dimension given for Qhull option %c. Ignored\n", + key); + continue; + } + idx= qh_strtol(s, &s); + if (idx >= maxdim) { + qh_fprintf(qh ferr, 7048, "qhull warning: dimension %d for Qhull option %c is >= %d. Ignored\n", + idx, key, maxdim); + continue; + } + if (*s == ':') { + s++; + value= qh_strtod(s, &s); + }else if (key == 'b') + value= -qh_DEFAULTbox; + else + value= qh_DEFAULTbox; + if (key == 'b') + qh lower_bound[idx]= value; + else + qh upper_bound[idx]= value; + } + } + }else { + while (*s && !isspace(*s)) + s++; + } + while (isspace(*s)) + s++; + } + for (k=qh hull_dim; k--; ) { + if (qh lower_threshold[k] > -REALmax/2) { + qh GOODthreshold= True; + if (qh upper_threshold[k] < REALmax/2) { + qh SPLITthresholds= True; + qh GOODthreshold= False; + break; + } + }else if (qh upper_threshold[k] < REALmax/2) + qh GOODthreshold= True; + } +} /* initthresholds */ + +/*-<a href="qh-globa.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) { + boolT iserror= False; + +#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 */ + if (qh_QHpointer) { + qh_fprintf_stderr(6246, "qh_lib_check: Incorrect qhull library called. Caller uses a static qhT while library uses a dynamic qhT via qh_QHpointer. Both caller and library are non-reentrant.\n"); + iserror= True; + } + }else if (qhullLibraryType==QHULL_QH_POINTER) { /* 1 */ + if (!qh_QHpointer) { + qh_fprintf_stderr(6247, "qh_lib_check: Incorrect qhull library called. Caller uses a dynamic qhT via qh_QHpointer while library uses a static qhT. Both caller and library are non-reentrant.\n"); + iserror= True; + } + }else if (qhullLibraryType==QHULL_REENTRANT) { /* 2 */ + qh_fprintf_stderr(6248, "qh_lib_check: Incorrect qhull library called. Caller uses reentrant Qhull while library is non-reentrant\n"); + iserror= True; + }else{ + 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); + iserror= True; + } + if (qhTsize != sizeof(qhT)) { + qh_fprintf_stderr(6249, "qh_lib_check: Incorrect qhull library called. Size of qhT for caller is %d, but for library is %d.\n", qhTsize, sizeof(qhT)); + iserror= True; + } + if (vertexTsize != sizeof(vertexT)) { + qh_fprintf_stderr(6250, "qh_lib_check: Incorrect qhull library called. Size of vertexT for caller is %d, but for library is %d.\n", vertexTsize, sizeof(vertexT)); + iserror= True; + } + if (ridgeTsize != sizeof(ridgeT)) { + qh_fprintf_stderr(6251, "qh_lib_check: Incorrect qhull library called. Size of ridgeT for caller is %d, but for library is %d.\n", ridgeTsize, sizeof(ridgeT)); + iserror= True; + } + if (facetTsize != sizeof(facetT)) { + qh_fprintf_stderr(6252, "qh_lib_check: Incorrect qhull library called. Size of facetT for caller is %d, but for library is %d.\n", facetTsize, sizeof(facetT)); + iserror= True; + } + if (setTsize && setTsize != sizeof(setT)) { + qh_fprintf_stderr(6253, "qh_lib_check: Incorrect qhull library called. Size of setT for caller is %d, but for library is %d.\n", setTsize, sizeof(setT)); + iserror= True; + } + 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 library is %d.\n", qhmemTsize, sizeof(qhmemT)); + iserror= True; + } + if (iserror) { + if(qh_QHpointer){ + qh_fprintf_stderr(6255, "qh_lib_check: Cannot continue. Library '%s' uses a dynamic qhT via qh_QHpointer (e.g., qhull_p.so)\n", qh_version2); + }else{ + qh_fprintf_stderr(6256, "qh_lib_check: Cannot continue. Library '%s' uses a static qhT (e.g., libqhull.so)\n", qh_version2); + } + qh_exit(qh_ERRqhull); /* can not use qh_errexit() */ + } +} /* lib_check */ + +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="option">-</a> + + qh_option( option, intVal, realVal ) + add an option description to qh.qhull_options + + notes: + NOerrors -- qh_option can not call qh_errexit() [qh_initqhull_start2] + will be printed with statistics ('Ts') and errors + strlen(option) < 40 +*/ +void qh_option(const char *option, int *i, realT *r) { + char buf[200]; + int len, maxlen; + + sprintf(buf, " %s", option); + if (i) + sprintf(buf+strlen(buf), " %d", *i); + if (r) + sprintf(buf+strlen(buf), " %2.2g", *r); + len= (int)strlen(buf); /* WARN64 */ + qh qhull_optionlen += len; + maxlen= sizeof(qh qhull_options) - len -1; + maximize_(maxlen, 0); + if (qh qhull_optionlen >= qh_OPTIONline && maxlen > 0) { + qh qhull_optionlen= len; + strncat(qh qhull_options, "\n", (size_t)(maxlen--)); + } + strncat(qh qhull_options, buf, (size_t)maxlen); +} /* option */ + +#if qh_QHpointer +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="restore_qhull">-</a> + + qh_restore_qhull( oldqh ) + restores a previously saved qhull + also restores qh_qhstat and qhmem.tempstack + Sets *oldqh to NULL + notes: + errors if current qhull hasn't been saved or freed + uses qhmem for error reporting + + NOTE 1998/5/11: + Freeing memory after qh_save_qhull and qh_restore_qhull + is complicated. The procedures will be redesigned. + + see: + qh_save_qhull(), UsingLibQhull +*/ +void qh_restore_qhull(qhT **oldqh) { + + if (*oldqh && strcmp((*oldqh)->qhull, "qhull")) { + qh_fprintf(qhmem.ferr, 6061, "qhull internal error (qh_restore_qhull): %p is not a qhull data structure\n", + *oldqh); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + if (qh_qh) { + qh_fprintf(qhmem.ferr, 6062, "qhull internal error (qh_restore_qhull): did not save or free existing qhull\n"); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + if (!*oldqh || !(*oldqh)->old_qhstat) { + qh_fprintf(qhmem.ferr, 6063, "qhull internal error (qh_restore_qhull): did not previously save qhull %p\n", + *oldqh); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + qh_qh= *oldqh; + *oldqh= NULL; + qh_qhstat= qh old_qhstat; + qhmem.tempstack= qh old_tempstack; + qh old_qhstat= 0; + qh old_tempstack= 0; + trace1((qh ferr, 1007, "qh_restore_qhull: restored qhull from %p\n", *oldqh)); +} /* restore_qhull */ + +/*-<a href="qh-globa.htm#TOC" + >-------------------------------</a><a name="save_qhull">-</a> + + qh_save_qhull( ) + saves qhull for a later qh_restore_qhull + also saves qh_qhstat and qhmem.tempstack + + returns: + qh_qh=NULL + + notes: + need to initialize qhull or call qh_restore_qhull before continuing + + NOTE 1998/5/11: + Freeing memory after qh_save_qhull and qh_restore_qhull + is complicated. The procedures will be redesigned. + + see: + qh_restore_qhull() +*/ +qhT *qh_save_qhull(void) { + qhT *oldqh; + + trace1((qhmem.ferr, 1045, "qh_save_qhull: save qhull %p\n", qh_qh)); + if (!qh_qh) { + qh_fprintf(qhmem.ferr, 6064, "qhull internal error (qh_save_qhull): qhull not initialized\n"); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + qh old_qhstat= qh_qhstat; + qh_qhstat= NULL; + qh old_tempstack= qhmem.tempstack; + qhmem.tempstack= NULL; + oldqh= qh_qh; + qh_qh= NULL; + return oldqh; +} /* save_qhull */ + +#endif + diff --git a/src/qhull/src/libqhull/index.htm b/src/qhull/src/libqhull/index.htm new file mode 100644 index 000000000..62b9d9970 --- /dev/null +++ b/src/qhull/src/libqhull/index.htm @@ -0,0 +1,264 @@ +<!-- Do not edit with Front Page, it adds too many spaces --> +<html> +<head> +<meta http-equiv="Content-Type" +content="text/html; charset=iso-8859-1"> +<title>Qhull functions, macros, and data structures</title> +</head> + +<body> +<!-- Navigation links --> +<p><a name="TOP"><b>Up:</b></a> <a +href="http://www.qhull.org">Home page</a> for Qhull<br> +<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual</a>: Table of Contents <br> +<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a> +• <a href="../../html/qh-quick.htm#options">Options</a> +• <a href="../../html/qh-opto.htm#output">Output</a> +• <a href="../../html/qh-optf.htm#format">Formats</a> +• <a href="../../html/qh-optg.htm#geomview">Geomview</a> +• <a href="../../html/qh-optp.htm#print">Print</a> +• <a href="../../html/qh-optq.htm#qhull">Qhull</a> +• <a href="../../html/qh-optc.htm#prec">Precision</a> +• <a href="../../html/qh-optt.htm#trace">Trace</a> +• <a href="index.htm">Functions</a><br> +<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code</a><br> +<b>To:</b> <a href="#TOC">Qhull files</a><br> +<b>To:</b> <a href="qh-geom.htm">Geom</a> • <a href="qh-globa.htm">Global</a> +• <a href="qh-io.htm">Io</a> • <a href="qh-mem.htm">Mem</a> +• <a href="qh-merge.htm">Merge</a> • <a href="qh-poly.htm">Poly</a> +• <a href="qh-qhull.htm">Qhull</a> • <a href="qh-set.htm">Set</a> +• <a href="qh-stat.htm">Stat</a> • <a href="qh-user.htm">User</a> + +<hr> +<!-- Main text of document. --> + +<h1>Qhull functions, macros, and data structures</h1> +<blockquote> +<p>The following sections provide an overview and index to +Qhull's functions, macros, and data structures. +Each section starts with an introduction. +See also <a href=../../html/qh-code.htm#library>Calling +Qhull from C programs</a> and <a href="../../html/qh-code.htm#cpp">Calling Qhull from C++ programs</a>.</p> + +<p>Qhull uses the following conventions:</p> +<blockquote> + +<ul> +<li>in code, global variables start with "qh " +<li>in documentation, global variables start with 'qh.' +<li>constants start with an upper case word +<li>important globals include an '_' +<li>functions, macros, and constants start with "qh_"</li> +<li>data types end in "T"</li> +<li>macros with arguments end in "_"</li> +<li>iterators are macros that use local variables</li> +<li>iterators for sets start with "FOREACH"</li> +<li>iterators for lists start with "FORALL"</li> +<li>qhull options are in single quotes (e.g., 'Pdn')</li> +<li>lists are sorted alphabetically</li> +<li>preprocessor directives on left margin for older compilers</li> +</ul> +</blockquote> +<p> +When reading the code, please note that the +global data structure, 'qh', is a macro. It +either expands to "qh_qh." or to +"qh_qh->". The later is used for +applications which run concurrent calls to qh_qhull(). +<p> +When reading code with an editor, a search for +'<i>"function</i>' +will locate the header of <i>qh_function</i>. A search for '<i>* function</i>' +will locate the tail of <i>qh_function</i>. + +<p>A useful starting point is <a href="libqhull.h">libqhull.h</a>. It defines most +of Qhull data structures and top-level functions. Search for <i>'PFn'</i> to +determine the corresponding constant in Qhull. Search for <i>'Fp'</i> to +determine the corresponding <a href="libqhull.h#qh_PRINT">qh_PRINT...</a> constant. +Search <a href="io.c">io.c</a> to learn how the print function is implemented.</p> + +<p>If your web browser is configured for .c and .h files, the function, macro, and data type links +go to the corresponding source location. To configure your web browser for .c and .h files. +<ul> +<li>In the Download Preferences or Options panel, add file extensions 'c' and 'h' to mime type 'text/html'. +<li>Opera 12.10 +<ol> +<li>In Tools > Preferences > Advanced > Downloads +<li>Uncheck 'Hide file types opened with Opera' +<li>Quick find 'html' +<li>Select 'text/html' > Edit +<li>Add File extensions 'c,h,' +<li>Click 'OK' +</ol> +<li>Internet Explorer -- Mime types are not available from 'Internet Options'. Is there a registry key for these settings? +<li>Firefox -- Mime types are not available from 'Preferences'. Is there an add-on to change the file extensions for a mime type? +<li>Chrome -- Can Chrome be configured? +</ul> + +<p> +Please report documentation and link errors +to <a href="mailto:qhull-bug@qhull.org">qhull-bug@qhull.org</a>. +</blockquote> + +<p><b>Copyright © 1997-2015 C.B. Barber</b></p> + +<hr> + +<h2><a href="#TOP">»</a><a name="TOC">Qhull files</a> </h2> +<blockquote> + +<p>This sections lists the .c and .h files for Qhull. Please +refer to these files for detailed information.</p> +<blockquote> + +<dl> +<dt><a href="../../Makefile"><b>Makefile</b></a><b>, </b><a href="../../CMakeLists.txt"><b>CMakeLists.txt</b></a></dt> +<dd><tt>Makefile</tt> is preconfigured for gcc. <tt>CMakeLists.txt</tt> supports multiple +platforms with <a href=http://www.cmake.org/>CMake</a>. +Qhull includes project files for Visual Studio and Qt. +</dd> + +<dt> </dt> +<dt><a href="libqhull.h"><b>libqhull.h</b></a> </dt> +<dd>Include file for the Qhull library (<tt>libqhull.so</tt>, <tt>qhull.dll</tt>, <tt>libqhullstatic.a</tt>). +Data structures are documented under <a href="qh-poly.htm">Poly</a>. +Global variables are documented under <a href="qh-globa.htm">Global</a>. +Other data structures and variables are documented under +<a href="qh-qhull.htm#TOC">Qhull</a> or <a href="qh-geom.htm"><b>Geom</b></a><b>.</b></dd> + +<dt> </dt> +<dt><a href="qh-geom.htm"><b>Geom</b></a><b>, </b> +<a href="geom.h"><b>geom.h</b></a><b>, </b> +<a href="geom.c"><b>geom.c</b></a><b>, </b> +<a href="geom2.c"><b>geom2.c</b></a><b>, </b> +<a href="random.c"><b>random.c</b></a><b>, </b> +<a href="random.h"><b>random.h</b></a></dt> +<dd>Geometric routines. These routines implement mathematical +functions such as Gaussian elimination and geometric +routines needed for Qhull. Frequently used routines are +in <tt>geom.c</tt> while infrequent ones are in <tt>geom2.c</tt>. +</dd> + +<dt> </dt> +<dt><a href="qh-globa.htm"><b>Global</b></a><b>, </b> +<a href="global.c"><b>global.c</b></a><b>, </b> +<a href="libqhull.h"><b>libqhull.h</b></a> </dt> +<dd>Global routines. Qhull uses a global data structure, <tt>qh</tt>, +to store globally defined constants, lists, sets, and +variables. +<tt>global.c</tt> initializes and frees these +structures. </dd> + +<dt> </dt> +<dt><a href="qh-io.htm"><b>Io</b></a><b>, </b><a href="io.h"><b>io.h</b></a><b>, +</b><a href="io.c"><b>io.c</b></a> </dt> +<dd>Input and output routines. Qhull provides a wide range of +input and output options.</dd> + +<dt> </dt> +<dt><a href="qh-mem.htm"><b>Mem</b></a><b>, </b> +<a href="mem.h"><b>mem.h</b></a><b>, </b> +<a href="mem.c"><b>mem.c</b></a> </dt> +<dd>Memory routines. Qhull provides memory allocation and +deallocation. It uses quick-fit allocation.</dd> + +<dt> </dt> +<dt><a href="qh-merge.htm"><b>Merge</b></a><b>, </b> +<a href="merge.h"><b>merge.h</b></a><b>, </b> +<a href="merge.c"><b>merge.c</b></a> </dt> +<dd>Merge routines. Qhull handles precision problems by +merged facets or joggled input. These routines merge simplicial facets, +merge non-simplicial facets, merge cycles of facets, and +rename redundant vertices.</dd> + +<dt> </dt> +<dt><a href="qh-poly.htm"><b>Poly</b></a><b>, </b> +<a href="poly.h"><b>poly.h</b></a><b>, </b> +<a href="poly.c"><b>poly.c</b></a><b>, </b> +<a href="poly2.c"><b>poly2.c</b></a><b>, </b> +<a href="libqhull.h"><b>libqhull.h</b></a> </dt> +<dd>Polyhedral routines. Qhull produces a polyhedron as a +list of facets with vertices, neighbors, ridges, and +geometric information. <tt>libqhull.h</tt> defines the main +data structures. Frequently used routines are in <tt>poly.c</tt> +while infrequent ones are in <tt>poly2.c</tt>.</dd> + +<dt> </dt> +<dt><a href="qh-qhull.htm#TOC"><b>Qhull</b></a><b>, </b> +<a href="libqhull.c"><b>libqhull.c</b></a><b>, </b> +<a href="libqhull.h"><b>libqhull.h</b></a><b>, </b> +<a href="qhull_a.h"><b>qhull_a.h</b></a><b>, </b> +<a href="../qhullo/unix.c"><b>unix.c</b></a> <b>, </b> +<a href="../qconvex/qconvex.c"><b>qconvex.c</b></a> <b>, </b> +<a href="../qdelaunay/qdelaun.c"><b>qdelaun.c</b></a> <b>, </b> +<a href="../qhalf/qhalf.c"><b>qhalf.c</b></a> <b>, </b> +<a href="../qvoronoi/qvoronoi.c"><b>qvoronoi.c</b></a> </dt> +<dd>Top-level routines. The Quickhull algorithm is +implemented by <tt>libqhull.c</tt>. <tt>qhull_a.h</tt> +includes all header files. </dd> + +<dt> </dt> +<dt><a href="qh-set.htm"><b>Set</b></a><b>, </b> +<a href="qset.h"><b>qset.h</b></a><b>, </b> +<a href="qset.c"><b>qset.c</b></a> </dt> +<dd>Set routines. Qhull implements its data structures as +sets. A set is an array of pointers that is expanded as +needed. This is a separate package that may be used in +other applications. </dd> + +<dt> </dt> +<dt><a href="qh-stat.htm"><b>Stat</b></a><b>, </b> +<a href="stat.h"><b>stat.h</b></a><b>, </b> +<a href="stat.c"><b>stat.c</b></a> </dt> +<dd>Statistical routines. Qhull maintains statistics about +its implementation. </dd> + +<dt> </dt> +<dt><a href="qh-user.htm"><b>User</b></a><b>, </b> +<a href="user.h"><b>user.h</b></a><b>, </b> +<a href="user.c"><b>user.c</b></a><b>, </b> +<a href="../user_eg/user_eg.c"><b>user_eg.c</b></a><b>, </b> +<a href="../user_eg2/user_eg2.c"><b>user_eg2.c</b></a><b>, </b> +</dt> +<dd>User-defined routines. Qhull allows the user to configure +the code with defined constants and specialized routines. +</dd> +</dl> +</blockquote> + +</blockquote> +<p><!-- Navigation links --> </p> +<hr> +<p><b>Up:</b> +<a href="http://www.qhull.org">Home page for +Qhull</a> <br> +<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual: Table of Contents</a> <br> +<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a> +• <a href="../../html/qh-quick.htm#options">Options</a> +• <a href="../../html/qh-opto.htm#output">Output</a> +• <a href="../../html/qh-optf.htm#format">Formats</a> +• <a href="../../html/qh-optg.htm#geomview">Geomview</a> +• <a href="../../html/qh-optp.htm#print">Print</a> +• <a href="../../html/qh-optq.htm#qhull">Qhull</a> +• <a href="../../html/qh-optc.htm#prec">Precision</a> +• <a href="../../html/qh-optt.htm#trace">Trace</a> +• <a href="index.htm">Functions</a><br> +<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a> <br> +<b>To:</b> <a href="#TOC">Qhull files</a><br> +<b>To:</b> <a href="qh-geom.htm">Geom</a> • +<a href="qh-globa.htm">Global</a> • <a href="qh-io.htm">Io</a> +• <a href="qh-mem.htm">Mem</a> • <a href="qh-merge.htm">Merge</a> +• <a href="qh-poly.htm">Poly</a> • <a href="qh-qhull.htm#TOC">Qhull</a> +• <a href="qh-set.htm">Set</a> • <a href="qh-stat.htm">Stat</a> +• <a href="qh-user.htm">User</a><br> + +<p><!-- GC common information --> </p> +<hr> +<p><a href="http://www.geom.uiuc.edu/"><img +src="../../html/qh--geom.gif" align="middle" width="40" height="40"></a><i>The +Geometry Center Home Page </i></p> +<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a> +</a><br> +Created: May 2, 1997 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p> +</body> +</html> diff --git a/src/qhull/src/libqhull/io.c b/src/qhull/src/libqhull/io.c new file mode 100644 index 000000000..401987ec0 --- /dev/null +++ b/src/qhull/src/libqhull/io.c @@ -0,0 +1,4062 @@ +/*<html><pre> -<a href="qh-io.htm" + >-------------------------------</a><a name="TOP">-</a> + + io.c + Input/Output routines of qhull application + + see qh-io.htm and io.h + + see user.c for qh_errprint and qh_printfacetlist + + unix.c calls qh_readpoints and qh_produce_output + + unix.c and user.c are the only callers of io.c functions + This allows the user to avoid loading io.o from qhull.a + + Copyright (c) 1993-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull/io.c#5 $$Change: 2064 $ + $DateTime: 2016/01/18 12:36:08 $$Author: bbarber $ +*/ + +#include "qhull_a.h" + +/*========= -functions in alphabetical order after qh_produce_output() =====*/ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="produce_output">-</a> + + qh_produce_output() + qh_produce_output2() + prints out the result of qhull in desired format + qh_produce_output2() does not call qh_prepare_output() + if qh.GETarea + computes and prints area and volume + qh.PRINTout[] is an array of output formats + + notes: + prints output in qh.PRINTout order +*/ +void qh_produce_output(void) { + int tempsize= qh_setsize(qhmem.tempstack); + + qh_prepare_output(); + qh_produce_output2(); + if (qh_setsize(qhmem.tempstack) != tempsize) { + qh_fprintf(qh ferr, 6206, "qhull internal error (qh_produce_output): temporary sets not empty(%d)\n", + qh_setsize(qhmem.tempstack)); + qh_errexit(qh_ERRqhull, NULL, NULL); + } +} /* produce_output */ + + +void qh_produce_output2(void) { + int i, tempsize= qh_setsize(qhmem.tempstack), d_1; + + if (qh PRINTsummary) + qh_printsummary(qh ferr); + else if (qh PRINTout[0] == qh_PRINTnone) + qh_printsummary(qh fout); + for (i=0; i < qh_PRINTEND; i++) + qh_printfacets(qh fout, qh PRINTout[i], qh facet_list, NULL, !qh_ALL); + qh_allstatistics(); + if (qh PRINTprecision && !qh MERGING && (qh JOGGLEmax > REALmax/2 || qh RERUN)) + qh_printstats(qh ferr, qhstat precision, NULL); + if (qh VERIFYoutput && (zzval_(Zridge) > 0 || zzval_(Zridgemid) > 0)) + qh_printstats(qh ferr, qhstat vridges, NULL); + if (qh PRINTstatistics) { + qh_printstatistics(qh ferr, ""); + qh_memstatistics(qh ferr); + d_1= sizeof(setT) + (qh hull_dim - 1) * SETelemsize; + qh_fprintf(qh ferr, 8040, "\ + size in bytes: merge %d ridge %d vertex %d facet %d\n\ + normal %d ridge vertices %d facet vertices or neighbors %d\n", + (int)sizeof(mergeT), (int)sizeof(ridgeT), + (int)sizeof(vertexT), (int)sizeof(facetT), + qh normal_size, d_1, d_1 + SETelemsize); + } + if (qh_setsize(qhmem.tempstack) != tempsize) { + qh_fprintf(qh ferr, 6065, "qhull internal error (qh_produce_output2): temporary sets not empty(%d)\n", + qh_setsize(qhmem.tempstack)); + qh_errexit(qh_ERRqhull, NULL, NULL); + } +} /* produce_output2 */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="dfacet">-</a> + + qh_dfacet( id ) + print facet by id, for debugging + +*/ +void qh_dfacet(unsigned id) { + facetT *facet; + + FORALLfacets { + if (facet->id == id) { + qh_printfacet(qh fout, facet); + break; + } + } +} /* dfacet */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="dvertex">-</a> + + qh_dvertex( id ) + print vertex by id, for debugging +*/ +void qh_dvertex(unsigned id) { + vertexT *vertex; + + FORALLvertices { + if (vertex->id == id) { + qh_printvertex(qh fout, vertex); + break; + } + } +} /* dvertex */ + + +/*-<a href="qh-io.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.htm#TOC" + >-------------------------------</a><a name="compare_facetmerge">-</a> + + qh_compare_facetmerge( p1, p2 ) + used by qsort() to order facets by number of merges +*/ +int qh_compare_facetmerge(const void *p1, const void *p2) { + const facetT *a= *((facetT *const*)p1), *b= *((facetT *const*)p2); + + return(a->nummerge - b->nummerge); +} /* compare_facetvisit */ + +/*-<a href="qh-io.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= a->visitid)) + i= 0 - a->id; /* do not convert to int, sign distinguishes id from visitid */ + if (!(j= b->visitid)) + j= 0 - b->id; + return(i - j); +} /* compare_facetvisit */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="compare_vertexpoint">-</a> + + qh_compare_vertexpoint( p1, p2 ) + used by qsort() to order vertices by point id + + Not used. Not available in libqhull_r.h since qh_pointid depends on qh +*/ +int qh_compare_vertexpoint(const void *p1, const void *p2) { + const vertexT *a= *((vertexT *const*)p1), *b= *((vertexT *const*)p2); + + return((qh_pointid(a->point) > qh_pointid(b->point)?1:-1)); +} /* compare_vertexpoint */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="copyfilename">-</a> + + qh_copyfilename( dest, size, source, length ) + copy filename identified by qh_skipfilename() + + notes: + see qh_skipfilename() for syntax +*/ +void qh_copyfilename(char *filename, int size, const char* source, int length) { + char c= *source; + + if (length > size + 1) { + qh_fprintf(qh ferr, 6040, "qhull error: filename is more than %d characters, %s\n", size-1, source); + qh_errexit(qh_ERRinput, NULL, NULL); + } + strncpy(filename, source, length); + filename[length]= '\0'; + if (c == '\'' || c == '"') { + char *s= filename + 1; + char *t= filename; + while (*s) { + if (*s == c) { + if (s[-1] == '\\') + t[-1]= c; + }else + *t++= *s; + s++; + } + *t= '\0'; + } +} /* copyfilename */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="countfacets">-</a> + + qh_countfacets( facetlist, facets, printall, + numfacets, numsimplicial, totneighbors, numridges, numcoplanar, numtricoplanars ) + count good facets for printing and set visitid + if allfacets, ignores qh_skipfacet() + + notes: + qh_printsummary and qh_countfacets must match counts + + returns: + numfacets, numsimplicial, total neighbors, numridges, coplanars + each facet with ->visitid indicating 1-relative position + ->visitid==0 indicates not good + + notes + numfacets >= numsimplicial + if qh.NEWfacets, + does not count visible facets (matches qh_printafacet) + + design: + for all facets on facetlist and in facets set + unless facet is skipped or visible (i.e., will be deleted) + mark facet->visitid + update counts +*/ +void qh_countfacets(facetT *facetlist, setT *facets, boolT printall, + int *numfacetsp, int *numsimplicialp, int *totneighborsp, int *numridgesp, int *numcoplanarsp, int *numtricoplanarsp) { + facetT *facet, **facetp; + int numfacets= 0, numsimplicial= 0, numridges= 0, totneighbors= 0, numcoplanars= 0, numtricoplanars= 0; + + FORALLfacet_(facetlist) { + if ((facet->visible && qh NEWfacets) + || (!printall && qh_skipfacet(facet))) + facet->visitid= 0; + else { + facet->visitid= ++numfacets; + totneighbors += qh_setsize(facet->neighbors); + if (facet->simplicial) { + numsimplicial++; + if (facet->keepcentrum && facet->tricoplanar) + numtricoplanars++; + }else + numridges += qh_setsize(facet->ridges); + if (facet->coplanarset) + numcoplanars += qh_setsize(facet->coplanarset); + } + } + + FOREACHfacet_(facets) { + if ((facet->visible && qh NEWfacets) + || (!printall && qh_skipfacet(facet))) + facet->visitid= 0; + else { + facet->visitid= ++numfacets; + totneighbors += qh_setsize(facet->neighbors); + if (facet->simplicial){ + numsimplicial++; + if (facet->keepcentrum && facet->tricoplanar) + numtricoplanars++; + }else + numridges += qh_setsize(facet->ridges); + if (facet->coplanarset) + numcoplanars += qh_setsize(facet->coplanarset); + } + } + qh visit_id += numfacets+1; + *numfacetsp= numfacets; + *numsimplicialp= numsimplicial; + *totneighborsp= totneighbors; + *numridgesp= numridges; + *numcoplanarsp= numcoplanars; + *numtricoplanarsp= numtricoplanars; +} /* countfacets */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="detvnorm">-</a> + + qh_detvnorm( vertex, vertexA, centers, offset ) + compute separating plane of the Voronoi diagram for a pair of input sites + centers= set of facets (i.e., Voronoi vertices) + facet->visitid= 0 iff vertex-at-infinity (i.e., unbounded) + + assumes: + qh_ASvoronoi and qh_vertexneighbors() already set + + returns: + norm + a pointer into qh.gm_matrix to qh.hull_dim-1 reals + copy the data before reusing qh.gm_matrix + offset + if 'QVn' + sign adjusted so that qh.GOODvertexp is inside + else + sign adjusted so that vertex is inside + + qh.gm_matrix= simplex of points from centers relative to first center + + notes: + in io.c so that code for 'v Tv' can be removed by removing io.c + returns pointer into qh.gm_matrix to avoid tracking of temporary memory + + design: + determine midpoint of input sites + build points as the set of Voronoi vertices + select a simplex from points (if necessary) + include midpoint if the Voronoi region is unbounded + relocate the first vertex of the simplex to the origin + compute the normalized hyperplane through the simplex + orient the hyperplane toward 'QVn' or 'vertex' + if 'Tv' or 'Ts' + if bounded + test that hyperplane is the perpendicular bisector of the input sites + test that Voronoi vertices not in the simplex are still on the hyperplane + free up temporary memory +*/ +pointT *qh_detvnorm(vertexT *vertex, vertexT *vertexA, setT *centers, realT *offsetp) { + facetT *facet, **facetp; + int i, k, pointid, pointidA, point_i, point_n; + setT *simplex= NULL; + pointT *point, **pointp, *point0, *midpoint, *normal, *inpoint; + coordT *coord, *gmcoord, *normalp; + setT *points= qh_settemp(qh TEMPsize); + boolT nearzero= False; + boolT unbounded= False; + int numcenters= 0; + int dim= qh hull_dim - 1; + realT dist, offset, angle, zero= 0.0; + + midpoint= qh gm_matrix + qh hull_dim * qh hull_dim; /* last row */ + for (k=0; k < dim; k++) + midpoint[k]= (vertex->point[k] + vertexA->point[k])/2; + FOREACHfacet_(centers) { + numcenters++; + if (!facet->visitid) + unbounded= True; + else { + if (!facet->center) + facet->center= qh_facetcenter(facet->vertices); + qh_setappend(&points, facet->center); + } + } + if (numcenters > dim) { + simplex= qh_settemp(qh TEMPsize); + qh_setappend(&simplex, vertex->point); + if (unbounded) + qh_setappend(&simplex, midpoint); + qh_maxsimplex(dim, points, NULL, 0, &simplex); + qh_setdelnth(simplex, 0); + }else if (numcenters == dim) { + if (unbounded) + qh_setappend(&points, midpoint); + simplex= points; + }else { + qh_fprintf(qh ferr, 6216, "qhull internal error (qh_detvnorm): too few points(%d) to compute separating plane\n", numcenters); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + i= 0; + gmcoord= qh gm_matrix; + point0= SETfirstt_(simplex, pointT); + FOREACHpoint_(simplex) { + if (qh IStracing >= 4) + qh_printmatrix(qh ferr, "qh_detvnorm: Voronoi vertex or midpoint", + &point, 1, dim); + if (point != point0) { + qh gm_row[i++]= gmcoord; + coord= point0; + for (k=dim; k--; ) + *(gmcoord++)= *point++ - *coord++; + } + } + qh gm_row[i]= gmcoord; /* does not overlap midpoint, may be used later for qh_areasimplex */ + normal= gmcoord; + qh_sethyperplane_gauss(dim, qh gm_row, point0, True, + normal, &offset, &nearzero); + if (qh GOODvertexp == vertexA->point) + inpoint= vertexA->point; + else + inpoint= vertex->point; + zinc_(Zdistio); + dist= qh_distnorm(dim, inpoint, normal, &offset); + if (dist > 0) { + offset= -offset; + normalp= normal; + for (k=dim; k--; ) { + *normalp= -(*normalp); + normalp++; + } + } + if (qh VERIFYoutput || qh PRINTstatistics) { + pointid= qh_pointid(vertex->point); + pointidA= qh_pointid(vertexA->point); + if (!unbounded) { + zinc_(Zdiststat); + dist= qh_distnorm(dim, midpoint, normal, &offset); + if (dist < 0) + dist= -dist; + zzinc_(Zridgemid); + wwmax_(Wridgemidmax, dist); + wwadd_(Wridgemid, dist); + trace4((qh ferr, 4014, "qh_detvnorm: points %d %d midpoint dist %2.2g\n", + pointid, pointidA, dist)); + for (k=0; k < dim; k++) + midpoint[k]= vertexA->point[k] - vertex->point[k]; /* overwrites midpoint! */ + qh_normalize(midpoint, dim, False); + angle= qh_distnorm(dim, midpoint, normal, &zero); /* qh_detangle uses dim+1 */ + if (angle < 0.0) + angle= angle + 1.0; + else + angle= angle - 1.0; + if (angle < 0.0) + angle -= angle; + trace4((qh ferr, 4015, "qh_detvnorm: points %d %d angle %2.2g nearzero %d\n", + pointid, pointidA, angle, nearzero)); + if (nearzero) { + zzinc_(Zridge0); + wwmax_(Wridge0max, angle); + wwadd_(Wridge0, angle); + }else { + zzinc_(Zridgeok) + wwmax_(Wridgeokmax, angle); + wwadd_(Wridgeok, angle); + } + } + if (simplex != points) { + FOREACHpoint_i_(points) { + if (!qh_setin(simplex, point)) { + facet= SETelemt_(centers, point_i, facetT); + zinc_(Zdiststat); + dist= qh_distnorm(dim, point, normal, &offset); + if (dist < 0) + dist= -dist; + zzinc_(Zridge); + wwmax_(Wridgemax, dist); + wwadd_(Wridge, dist); + trace4((qh ferr, 4016, "qh_detvnorm: points %d %d Voronoi vertex %d dist %2.2g\n", + pointid, pointidA, facet->visitid, dist)); + } + } + } + } + *offsetp= offset; + if (simplex != points) + qh_settempfree(&simplex); + qh_settempfree(&points); + return normal; +} /* detvnorm */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="detvridge">-</a> + + qh_detvridge( vertexA ) + determine Voronoi ridge from 'seen' neighbors of vertexA + include one vertex-at-infinite if an !neighbor->visitid + + returns: + temporary set of centers (facets, i.e., Voronoi vertices) + sorted by center id +*/ +setT *qh_detvridge(vertexT *vertex) { + setT *centers= qh_settemp(qh TEMPsize); + setT *tricenters= qh_settemp(qh TEMPsize); + facetT *neighbor, **neighborp; + boolT firstinf= True; + + FOREACHneighbor_(vertex) { + if (neighbor->seen) { + if (neighbor->visitid) { + if (!neighbor->tricoplanar || qh_setunique(&tricenters, neighbor->center)) + qh_setappend(¢ers, neighbor); + }else if (firstinf) { + firstinf= False; + qh_setappend(¢ers, neighbor); + } + } + } + qsort(SETaddr_(centers, facetT), (size_t)qh_setsize(centers), + sizeof(facetT *), qh_compare_facetvisit); + qh_settempfree(&tricenters); + return centers; +} /* detvridge */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="detvridge3">-</a> + + qh_detvridge3( atvertex, vertex ) + determine 3-d Voronoi ridge from 'seen' neighbors of atvertex and vertex + include one vertex-at-infinite for !neighbor->visitid + assumes all facet->seen2= True + + returns: + temporary set of centers (facets, i.e., Voronoi vertices) + listed in adjacency order (!oriented) + all facet->seen2= True + + design: + mark all neighbors of atvertex + for each adjacent neighbor of both atvertex and vertex + if neighbor selected + add neighbor to set of Voronoi vertices +*/ +setT *qh_detvridge3(vertexT *atvertex, vertexT *vertex) { + setT *centers= qh_settemp(qh TEMPsize); + setT *tricenters= qh_settemp(qh TEMPsize); + facetT *neighbor, **neighborp, *facet= NULL; + boolT firstinf= True; + + FOREACHneighbor_(atvertex) + neighbor->seen2= False; + FOREACHneighbor_(vertex) { + if (!neighbor->seen2) { + facet= neighbor; + break; + } + } + while (facet) { + facet->seen2= True; + if (neighbor->seen) { + if (facet->visitid) { + if (!facet->tricoplanar || qh_setunique(&tricenters, facet->center)) + qh_setappend(¢ers, facet); + }else if (firstinf) { + firstinf= False; + qh_setappend(¢ers, facet); + } + } + FOREACHneighbor_(facet) { + if (!neighbor->seen2) { + if (qh_setin(vertex->neighbors, neighbor)) + break; + else + neighbor->seen2= True; + } + } + facet= neighbor; + } + if (qh CHECKfrequently) { + FOREACHneighbor_(vertex) { + if (!neighbor->seen2) { + qh_fprintf(qh ferr, 6217, "qhull internal error (qh_detvridge3): neighbors of vertex p%d are not connected at facet %d\n", + qh_pointid(vertex->point), neighbor->id); + qh_errexit(qh_ERRqhull, neighbor, NULL); + } + } + } + FOREACHneighbor_(atvertex) + neighbor->seen2= True; + qh_settempfree(&tricenters); + return centers; +} /* detvridge3 */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="eachvoronoi">-</a> + + qh_eachvoronoi( fp, printvridge, vertex, visitall, innerouter, inorder ) + if visitall, + visit all Voronoi ridges for vertex (i.e., an input site) + else + visit all unvisited Voronoi ridges for vertex + all vertex->seen= False if unvisited + assumes + all facet->seen= False + all facet->seen2= True (for qh_detvridge3) + all facet->visitid == 0 if vertex_at_infinity + == index of Voronoi vertex + >= qh.num_facets if ignored + innerouter: + qh_RIDGEall-- both inner (bounded) and outer(unbounded) ridges + qh_RIDGEinner- only inner + qh_RIDGEouter- only outer + + if inorder + orders vertices for 3-d Voronoi diagrams + + returns: + number of visited ridges (does not include previously visited ridges) + + if printvridge, + calls printvridge( fp, vertex, vertexA, centers) + fp== any pointer (assumes FILE*) + vertex,vertexA= pair of input sites that define a Voronoi ridge + centers= set of facets (i.e., Voronoi vertices) + ->visitid == index or 0 if vertex_at_infinity + ordered for 3-d Voronoi diagram + notes: + uses qh.vertex_visit + + see: + qh_eachvoronoi_all() + + design: + mark selected neighbors of atvertex + for each selected neighbor (either Voronoi vertex or vertex-at-infinity) + for each unvisited vertex + if atvertex and vertex share more than d-1 neighbors + bump totalcount + if printvridge defined + build the set of shared neighbors (i.e., Voronoi vertices) + call printvridge +*/ +int qh_eachvoronoi(FILE *fp, printvridgeT printvridge, vertexT *atvertex, boolT visitall, qh_RIDGE innerouter, boolT inorder) { + boolT unbounded; + int count; + facetT *neighbor, **neighborp, *neighborA, **neighborAp; + setT *centers; + setT *tricenters= qh_settemp(qh TEMPsize); + + vertexT *vertex, **vertexp; + boolT firstinf; + unsigned int numfacets= (unsigned int)qh num_facets; + int totridges= 0; + + qh vertex_visit++; + atvertex->seen= True; + if (visitall) { + FORALLvertices + vertex->seen= False; + } + FOREACHneighbor_(atvertex) { + if (neighbor->visitid < numfacets) + neighbor->seen= True; + } + FOREACHneighbor_(atvertex) { + if (neighbor->seen) { + FOREACHvertex_(neighbor->vertices) { + if (vertex->visitid != qh vertex_visit && !vertex->seen) { + vertex->visitid= qh vertex_visit; + count= 0; + firstinf= True; + qh_settruncate(tricenters, 0); + FOREACHneighborA_(vertex) { + if (neighborA->seen) { + if (neighborA->visitid) { + if (!neighborA->tricoplanar || qh_setunique(&tricenters, neighborA->center)) + count++; + }else if (firstinf) { + count++; + firstinf= False; + } + } + } + if (count >= qh hull_dim - 1) { /* e.g., 3 for 3-d Voronoi */ + if (firstinf) { + if (innerouter == qh_RIDGEouter) + continue; + unbounded= False; + }else { + if (innerouter == qh_RIDGEinner) + continue; + unbounded= True; + } + totridges++; + trace4((qh ferr, 4017, "qh_eachvoronoi: Voronoi ridge of %d vertices between sites %d and %d\n", + count, qh_pointid(atvertex->point), qh_pointid(vertex->point))); + if (printvridge && fp) { + if (inorder && qh hull_dim == 3+1) /* 3-d Voronoi diagram */ + centers= qh_detvridge3(atvertex, vertex); + else + centers= qh_detvridge(vertex); + (*printvridge)(fp, atvertex, vertex, centers, unbounded); + qh_settempfree(¢ers); + } + } + } + } + } + } + FOREACHneighbor_(atvertex) + neighbor->seen= False; + qh_settempfree(&tricenters); + return totridges; +} /* eachvoronoi */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="eachvoronoi_all">-</a> + + qh_eachvoronoi_all( fp, printvridge, isUpper, innerouter, inorder ) + visit all Voronoi ridges + + innerouter: + see qh_eachvoronoi() + + if inorder + orders vertices for 3-d Voronoi diagrams + + returns + total number of ridges + + if isUpper == facet->upperdelaunay (i.e., a Vornoi vertex) + facet->visitid= Voronoi vertex index(same as 'o' format) + else + facet->visitid= 0 + + if printvridge, + calls printvridge( fp, vertex, vertexA, centers) + [see qh_eachvoronoi] + + notes: + Not used for qhull.exe + same effect as qh_printvdiagram but ridges not sorted by point id +*/ +int qh_eachvoronoi_all(FILE *fp, printvridgeT printvridge, boolT isUpper, qh_RIDGE innerouter, boolT inorder) { + facetT *facet; + vertexT *vertex; + int numcenters= 1; /* vertex 0 is vertex-at-infinity */ + int totridges= 0; + + qh_clearcenters(qh_ASvoronoi); + qh_vertexneighbors(); + maximize_(qh visit_id, (unsigned) qh num_facets); + FORALLfacets { + facet->visitid= 0; + facet->seen= False; + facet->seen2= True; + } + FORALLfacets { + if (facet->upperdelaunay == isUpper) + facet->visitid= numcenters++; + } + FORALLvertices + vertex->seen= False; + FORALLvertices { + if (qh GOODvertex > 0 && qh_pointid(vertex->point)+1 != qh GOODvertex) + continue; + totridges += qh_eachvoronoi(fp, printvridge, vertex, + !qh_ALL, innerouter, inorder); + } + return totridges; +} /* eachvoronoi_all */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="facet2point">-</a> + + qh_facet2point( facet, point0, point1, mindist ) + return two projected temporary vertices for a 2-d facet + may be non-simplicial + + returns: + point0 and point1 oriented and projected to the facet + returns mindist (maximum distance below plane) +*/ +void qh_facet2point(facetT *facet, pointT **point0, pointT **point1, realT *mindist) { + vertexT *vertex0, *vertex1; + realT dist; + + if (facet->toporient ^ qh_ORIENTclock) { + vertex0= SETfirstt_(facet->vertices, vertexT); + vertex1= SETsecondt_(facet->vertices, vertexT); + }else { + vertex1= SETfirstt_(facet->vertices, vertexT); + vertex0= SETsecondt_(facet->vertices, vertexT); + } + zadd_(Zdistio, 2); + qh_distplane(vertex0->point, facet, &dist); + *mindist= dist; + *point0= qh_projectpoint(vertex0->point, facet, dist); + qh_distplane(vertex1->point, facet, &dist); + minimize_(*mindist, dist); + *point1= qh_projectpoint(vertex1->point, facet, dist); +} /* facet2point */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="facetvertices">-</a> + + qh_facetvertices( facetlist, facets, allfacets ) + returns temporary set of vertices in a set and/or list of facets + if allfacets, ignores qh_skipfacet() + + returns: + vertices with qh.vertex_visit + + notes: + optimized for allfacets of facet_list + + design: + if allfacets of facet_list + create vertex set from vertex_list + else + for each selected facet in facets or facetlist + append unvisited vertices to vertex set +*/ +setT *qh_facetvertices(facetT *facetlist, setT *facets, boolT allfacets) { + setT *vertices; + facetT *facet, **facetp; + vertexT *vertex, **vertexp; + + qh vertex_visit++; + if (facetlist == qh facet_list && allfacets && !facets) { + vertices= qh_settemp(qh num_vertices); + FORALLvertices { + vertex->visitid= qh vertex_visit; + qh_setappend(&vertices, vertex); + } + }else { + vertices= qh_settemp(qh TEMPsize); + FORALLfacet_(facetlist) { + if (!allfacets && qh_skipfacet(facet)) + continue; + FOREACHvertex_(facet->vertices) { + if (vertex->visitid != qh vertex_visit) { + vertex->visitid= qh vertex_visit; + qh_setappend(&vertices, vertex); + } + } + } + } + FOREACHfacet_(facets) { + if (!allfacets && qh_skipfacet(facet)) + continue; + FOREACHvertex_(facet->vertices) { + if (vertex->visitid != qh vertex_visit) { + vertex->visitid= qh vertex_visit; + qh_setappend(&vertices, vertex); + } + } + } + return vertices; +} /* facetvertices */ + +/*-<a href="qh-geom.htm#TOC" + >-------------------------------</a><a name="geomplanes">-</a> + + qh_geomplanes( facet, outerplane, innerplane ) + return outer and inner planes for Geomview + qh.PRINTradius is size of vertices and points (includes qh.JOGGLEmax) + + notes: + assume precise calculations in io.c with roundoff covered by qh_GEOMepsilon +*/ +void qh_geomplanes(facetT *facet, realT *outerplane, realT *innerplane) { + realT radius; + + if (qh MERGING || qh JOGGLEmax < REALmax/2) { + qh_outerinner(facet, outerplane, innerplane); + radius= qh PRINTradius; + if (qh JOGGLEmax < REALmax/2) + radius -= qh JOGGLEmax * sqrt((realT)qh hull_dim); /* already accounted for in qh_outerinner() */ + *outerplane += radius; + *innerplane -= radius; + if (qh PRINTcoplanar || qh PRINTspheres) { + *outerplane += qh MAXabs_coord * qh_GEOMepsilon; + *innerplane -= qh MAXabs_coord * qh_GEOMepsilon; + } + }else + *innerplane= *outerplane= 0; +} /* geomplanes */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="markkeep">-</a> + + qh_markkeep( facetlist ) + mark good facets that meet qh.KEEParea, qh.KEEPmerge, and qh.KEEPminArea + ignores visible facets (!part of convex hull) + + returns: + may clear facet->good + recomputes qh.num_good + + design: + get set of good facets + if qh.KEEParea + sort facets by area + clear facet->good for all but n largest facets + if qh.KEEPmerge + sort facets by merge count + clear facet->good for all but n most merged facets + if qh.KEEPminarea + clear facet->good if area too small + update qh.num_good +*/ +void qh_markkeep(facetT *facetlist) { + facetT *facet, **facetp; + setT *facets= qh_settemp(qh num_facets); + int size, count; + + trace2((qh ferr, 2006, "qh_markkeep: only keep %d largest and/or %d most merged facets and/or min area %.2g\n", + qh KEEParea, qh KEEPmerge, qh KEEPminArea)); + FORALLfacet_(facetlist) { + if (!facet->visible && facet->good) + qh_setappend(&facets, facet); + } + size= qh_setsize(facets); + if (qh KEEParea) { + qsort(SETaddr_(facets, facetT), (size_t)size, + sizeof(facetT *), qh_compare_facetarea); + if ((count= size - qh KEEParea) > 0) { + FOREACHfacet_(facets) { + facet->good= False; + if (--count == 0) + break; + } + } + } + if (qh KEEPmerge) { + qsort(SETaddr_(facets, facetT), (size_t)size, + sizeof(facetT *), qh_compare_facetmerge); + if ((count= size - qh KEEPmerge) > 0) { + FOREACHfacet_(facets) { + facet->good= False; + if (--count == 0) + break; + } + } + } + if (qh KEEPminArea < REALmax/2) { + FOREACHfacet_(facets) { + if (!facet->isarea || facet->f.area < qh KEEPminArea) + facet->good= False; + } + } + qh_settempfree(&facets); + count= 0; + FORALLfacet_(facetlist) { + if (facet->good) + count++; + } + qh num_good= count; +} /* markkeep */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="markvoronoi">-</a> + + qh_markvoronoi( facetlist, facets, printall, isLower, numcenters ) + mark voronoi vertices for printing by site pairs + + returns: + temporary set of vertices indexed by pointid + isLower set if printing lower hull (i.e., at least one facet is lower hull) + numcenters= total number of Voronoi vertices + bumps qh.printoutnum for vertex-at-infinity + clears all facet->seen and sets facet->seen2 + + if selected + facet->visitid= Voronoi vertex id + else if upper hull (or 'Qu' and lower hull) + facet->visitid= 0 + else + facet->visitid >= qh num_facets + + notes: + ignores qh.ATinfinity, if defined +*/ +setT *qh_markvoronoi(facetT *facetlist, setT *facets, boolT printall, boolT *isLowerp, int *numcentersp) { + int numcenters=0; + facetT *facet, **facetp; + setT *vertices; + boolT isLower= False; + + qh printoutnum++; + qh_clearcenters(qh_ASvoronoi); /* in case, qh_printvdiagram2 called by user */ + qh_vertexneighbors(); + vertices= qh_pointvertex(); + if (qh ATinfinity) + SETelem_(vertices, qh num_points-1)= NULL; + qh visit_id++; + maximize_(qh visit_id, (unsigned) qh num_facets); + FORALLfacet_(facetlist) { + if (printall || !qh_skipfacet(facet)) { + if (!facet->upperdelaunay) { + isLower= True; + break; + } + } + } + FOREACHfacet_(facets) { + if (printall || !qh_skipfacet(facet)) { + if (!facet->upperdelaunay) { + isLower= True; + break; + } + } + } + FORALLfacets { + if (facet->normal && (facet->upperdelaunay == isLower)) + facet->visitid= 0; /* facetlist or facets may overwrite */ + else + facet->visitid= qh visit_id; + facet->seen= False; + facet->seen2= True; + } + numcenters++; /* qh_INFINITE */ + FORALLfacet_(facetlist) { + if (printall || !qh_skipfacet(facet)) + facet->visitid= numcenters++; + } + FOREACHfacet_(facets) { + if (printall || !qh_skipfacet(facet)) + facet->visitid= numcenters++; + } + *isLowerp= isLower; + *numcentersp= numcenters; + trace2((qh ferr, 2007, "qh_markvoronoi: isLower %d numcenters %d\n", isLower, numcenters)); + return vertices; +} /* markvoronoi */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="order_vertexneighbors">-</a> + + qh_order_vertexneighbors( vertex ) + order facet neighbors of a 2-d or 3-d vertex by adjacency + + notes: + does not orient the neighbors + + design: + initialize a new neighbor set with the first facet in vertex->neighbors + while vertex->neighbors non-empty + select next neighbor in the previous facet's neighbor set + set vertex->neighbors to the new neighbor set +*/ +void qh_order_vertexneighbors(vertexT *vertex) { + setT *newset; + facetT *facet, *neighbor, **neighborp; + + trace4((qh ferr, 4018, "qh_order_vertexneighbors: order neighbors of v%d for 3-d\n", vertex->id)); + newset= qh_settemp(qh_setsize(vertex->neighbors)); + facet= (facetT*)qh_setdellast(vertex->neighbors); + qh_setappend(&newset, facet); + while (qh_setsize(vertex->neighbors)) { + FOREACHneighbor_(vertex) { + if (qh_setin(facet->neighbors, neighbor)) { + qh_setdel(vertex->neighbors, neighbor); + qh_setappend(&newset, neighbor); + facet= neighbor; + break; + } + } + if (!neighbor) { + qh_fprintf(qh ferr, 6066, "qhull internal error (qh_order_vertexneighbors): no neighbor of v%d for f%d\n", + vertex->id, facet->id); + qh_errexit(qh_ERRqhull, facet, NULL); + } + } + qh_setfree(&vertex->neighbors); + qh_settemppop(); + vertex->neighbors= newset; +} /* order_vertexneighbors */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="prepare_output">-</a> + + qh_prepare_output( ) + prepare for qh_produce_output2() according to + qh.KEEPminArea, KEEParea, KEEPmerge, GOODvertex, GOODthreshold, GOODpoint, ONLYgood, SPLITthresholds + does not reset facet->good + + notes + except for PRINTstatistics, no-op if previously called with same options +*/ +void qh_prepare_output(void) { + if (qh VORONOI) { + qh_clearcenters(qh_ASvoronoi); /* must be before qh_triangulate */ + qh_vertexneighbors(); + } + if (qh TRIangulate && !qh hasTriangulation) { + qh_triangulate(); + if (qh VERIFYoutput && !qh CHECKfrequently) + qh_checkpolygon(qh facet_list); + } + qh_findgood_all(qh facet_list); + if (qh GETarea) + qh_getarea(qh facet_list); + if (qh KEEParea || qh KEEPmerge || qh KEEPminArea < REALmax/2) + qh_markkeep(qh facet_list); + if (qh PRINTstatistics) + qh_collectstatistics(); +} + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printafacet">-</a> + + qh_printafacet( fp, format, facet, printall ) + print facet to fp in given output format (see qh.PRINTout) + + returns: + nop if !printall and qh_skipfacet() + nop if visible facet and NEWfacets and format != PRINTfacets + must match qh_countfacets + + notes + preserves qh.visit_id + facet->normal may be null if PREmerge/MERGEexact and STOPcone before merge + + see + qh_printbegin() and qh_printend() + + design: + test for printing facet + call appropriate routine for format + or output results directly +*/ +void qh_printafacet(FILE *fp, qh_PRINT format, facetT *facet, boolT printall) { + realT color[4], offset, dist, outerplane, innerplane; + boolT zerodiv; + coordT *point, *normp, *coordp, **pointp, *feasiblep; + int k; + vertexT *vertex, **vertexp; + facetT *neighbor, **neighborp; + + if (!printall && qh_skipfacet(facet)) + return; + if (facet->visible && qh NEWfacets && format != qh_PRINTfacets) + return; + qh printoutnum++; + switch (format) { + case qh_PRINTarea: + if (facet->isarea) { + qh_fprintf(fp, 9009, qh_REAL_1, facet->f.area); + qh_fprintf(fp, 9010, "\n"); + }else + qh_fprintf(fp, 9011, "0\n"); + break; + case qh_PRINTcoplanars: + qh_fprintf(fp, 9012, "%d", qh_setsize(facet->coplanarset)); + FOREACHpoint_(facet->coplanarset) + qh_fprintf(fp, 9013, " %d", qh_pointid(point)); + qh_fprintf(fp, 9014, "\n"); + break; + case qh_PRINTcentrums: + qh_printcenter(fp, format, NULL, facet); + break; + case qh_PRINTfacets: + qh_printfacet(fp, facet); + break; + case qh_PRINTfacets_xridge: + qh_printfacetheader(fp, facet); + break; + case qh_PRINTgeom: /* either 2 , 3, or 4-d by qh_printbegin */ + if (!facet->normal) + break; + for (k=qh hull_dim; k--; ) { + color[k]= (facet->normal[k]+1.0)/2.0; + maximize_(color[k], -1.0); + minimize_(color[k], +1.0); + } + qh_projectdim3(color, color); + if (qh PRINTdim != qh hull_dim) + qh_normalize2(color, 3, True, NULL, NULL); + if (qh hull_dim <= 2) + qh_printfacet2geom(fp, facet, color); + else if (qh hull_dim == 3) { + if (facet->simplicial) + qh_printfacet3geom_simplicial(fp, facet, color); + else + qh_printfacet3geom_nonsimplicial(fp, facet, color); + }else { + if (facet->simplicial) + qh_printfacet4geom_simplicial(fp, facet, color); + else + qh_printfacet4geom_nonsimplicial(fp, facet, color); + } + break; + case qh_PRINTids: + qh_fprintf(fp, 9015, "%d\n", facet->id); + break; + case qh_PRINTincidences: + case qh_PRINToff: + case qh_PRINTtriangles: + if (qh hull_dim == 3 && format != qh_PRINTtriangles) + qh_printfacet3vertex(fp, facet, format); + else if (facet->simplicial || qh hull_dim == 2 || format == qh_PRINToff) + qh_printfacetNvertex_simplicial(fp, facet, format); + else + qh_printfacetNvertex_nonsimplicial(fp, facet, qh printoutvar++, format); + break; + case qh_PRINTinner: + qh_outerinner(facet, NULL, &innerplane); + offset= facet->offset - innerplane; + goto LABELprintnorm; + break; /* prevent warning */ + case qh_PRINTmerges: + qh_fprintf(fp, 9016, "%d\n", facet->nummerge); + break; + case qh_PRINTnormals: + offset= facet->offset; + goto LABELprintnorm; + break; /* prevent warning */ + case qh_PRINTouter: + qh_outerinner(facet, &outerplane, NULL); + offset= facet->offset - outerplane; + LABELprintnorm: + if (!facet->normal) { + qh_fprintf(fp, 9017, "no normal for facet f%d\n", facet->id); + break; + } + if (qh CDDoutput) { + qh_fprintf(fp, 9018, qh_REAL_1, -offset); + for (k=0; k < qh hull_dim; k++) + qh_fprintf(fp, 9019, qh_REAL_1, -facet->normal[k]); + }else { + for (k=0; k < qh hull_dim; k++) + qh_fprintf(fp, 9020, qh_REAL_1, facet->normal[k]); + qh_fprintf(fp, 9021, qh_REAL_1, offset); + } + qh_fprintf(fp, 9022, "\n"); + break; + case qh_PRINTmathematica: /* either 2 or 3-d by qh_printbegin */ + case qh_PRINTmaple: + if (qh hull_dim == 2) + qh_printfacet2math(fp, facet, format, qh printoutvar++); + else + qh_printfacet3math(fp, facet, format, qh printoutvar++); + break; + case qh_PRINTneighbors: + qh_fprintf(fp, 9023, "%d", qh_setsize(facet->neighbors)); + FOREACHneighbor_(facet) + qh_fprintf(fp, 9024, " %d", + neighbor->visitid ? neighbor->visitid - 1: 0 - neighbor->id); + qh_fprintf(fp, 9025, "\n"); + break; + case qh_PRINTpointintersect: + if (!qh feasible_point) { + qh_fprintf(qh ferr, 6067, "qhull input error (qh_printafacet): option 'Fp' needs qh feasible_point\n"); + qh_errexit( qh_ERRinput, NULL, NULL); + } + if (facet->offset > 0) + goto LABELprintinfinite; + point= coordp= (coordT*)qh_memalloc(qh normal_size); + normp= facet->normal; + feasiblep= qh feasible_point; + if (facet->offset < -qh MINdenom) { + for (k=qh hull_dim; k--; ) + *(coordp++)= (*(normp++) / - facet->offset) + *(feasiblep++); + }else { + for (k=qh hull_dim; k--; ) { + *(coordp++)= qh_divzero(*(normp++), facet->offset, qh MINdenom_1, + &zerodiv) + *(feasiblep++); + if (zerodiv) { + qh_memfree(point, qh normal_size); + goto LABELprintinfinite; + } + } + } + qh_printpoint(fp, NULL, point); + qh_memfree(point, qh normal_size); + break; + LABELprintinfinite: + for (k=qh hull_dim; k--; ) + qh_fprintf(fp, 9026, qh_REAL_1, qh_INFINITE); + qh_fprintf(fp, 9027, "\n"); + break; + case qh_PRINTpointnearest: + FOREACHpoint_(facet->coplanarset) { + int id, id2; + vertex= qh_nearvertex(facet, point, &dist); + id= qh_pointid(vertex->point); + id2= qh_pointid(point); + qh_fprintf(fp, 9028, "%d %d %d " qh_REAL_1 "\n", id, id2, facet->id, dist); + } + break; + case qh_PRINTpoints: /* VORONOI only by qh_printbegin */ + if (qh CDDoutput) + qh_fprintf(fp, 9029, "1 "); + qh_printcenter(fp, format, NULL, facet); + break; + case qh_PRINTvertices: + qh_fprintf(fp, 9030, "%d", qh_setsize(facet->vertices)); + FOREACHvertex_(facet->vertices) + qh_fprintf(fp, 9031, " %d", qh_pointid(vertex->point)); + qh_fprintf(fp, 9032, "\n"); + break; + default: + break; + } +} /* printafacet */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printbegin">-</a> + + qh_printbegin( ) + prints header for all output formats + + returns: + checks for valid format + + notes: + uses qh.visit_id for 3/4off + changes qh.interior_point if printing centrums + qh_countfacets clears facet->visitid for non-good facets + + see + qh_printend() and qh_printafacet() + + design: + count facets and related statistics + print header for format +*/ +void qh_printbegin(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) { + int numfacets, numsimplicial, numridges, totneighbors, numcoplanars, numtricoplanars; + int i, num; + facetT *facet, **facetp; + vertexT *vertex, **vertexp; + setT *vertices; + pointT *point, **pointp, *pointtemp; + + qh printoutnum= 0; + qh_countfacets(facetlist, facets, printall, &numfacets, &numsimplicial, + &totneighbors, &numridges, &numcoplanars, &numtricoplanars); + switch (format) { + case qh_PRINTnone: + break; + case qh_PRINTarea: + qh_fprintf(fp, 9033, "%d\n", numfacets); + break; + case qh_PRINTcoplanars: + qh_fprintf(fp, 9034, "%d\n", numfacets); + break; + case qh_PRINTcentrums: + if (qh CENTERtype == qh_ASnone) + qh_clearcenters(qh_AScentrum); + qh_fprintf(fp, 9035, "%d\n%d\n", qh hull_dim, numfacets); + break; + case qh_PRINTfacets: + case qh_PRINTfacets_xridge: + if (facetlist) + qh_printvertexlist(fp, "Vertices and facets:\n", facetlist, facets, printall); + break; + case qh_PRINTgeom: + if (qh hull_dim > 4) /* qh_initqhull_globals also checks */ + goto LABELnoformat; + if (qh VORONOI && qh hull_dim > 3) /* PRINTdim == DROPdim == hull_dim-1 */ + goto LABELnoformat; + if (qh hull_dim == 2 && (qh PRINTridges || qh DOintersections)) + qh_fprintf(qh ferr, 7049, "qhull warning: output for ridges and intersections not implemented in 2-d\n"); + if (qh hull_dim == 4 && (qh PRINTinner || qh PRINTouter || + (qh PRINTdim == 4 && qh PRINTcentrums))) + qh_fprintf(qh ferr, 7050, "qhull warning: output for outer/inner planes and centrums not implemented in 4-d\n"); + if (qh PRINTdim == 4 && (qh PRINTspheres)) + qh_fprintf(qh ferr, 7051, "qhull warning: output for vertices not implemented in 4-d\n"); + if (qh PRINTdim == 4 && qh DOintersections && qh PRINTnoplanes) + qh_fprintf(qh ferr, 7052, "qhull warning: 'Gnh' generates no output in 4-d\n"); + if (qh PRINTdim == 2) { + qh_fprintf(fp, 9036, "{appearance {linewidth 3} LIST # %s | %s\n", + qh rbox_command, qh qhull_command); + }else if (qh PRINTdim == 3) { + qh_fprintf(fp, 9037, "{appearance {+edge -evert linewidth 2} LIST # %s | %s\n", + qh rbox_command, qh qhull_command); + }else if (qh PRINTdim == 4) { + qh visit_id++; + num= 0; + FORALLfacet_(facetlist) /* get number of ridges to be printed */ + qh_printend4geom(NULL, facet, &num, printall); + FOREACHfacet_(facets) + qh_printend4geom(NULL, facet, &num, printall); + qh ridgeoutnum= num; + qh printoutvar= 0; /* counts number of ridges in output */ + qh_fprintf(fp, 9038, "LIST # %s | %s\n", qh rbox_command, qh qhull_command); + } + + if (qh PRINTdots) { + qh printoutnum++; + num= qh num_points + qh_setsize(qh other_points); + if (qh DELAUNAY && qh ATinfinity) + num--; + if (qh PRINTdim == 4) + qh_fprintf(fp, 9039, "4VECT %d %d 1\n", num, num); + else + qh_fprintf(fp, 9040, "VECT %d %d 1\n", num, num); + + for (i=num; i--; ) { + if (i % 20 == 0) + qh_fprintf(fp, 9041, "\n"); + qh_fprintf(fp, 9042, "1 "); + } + qh_fprintf(fp, 9043, "# 1 point per line\n1 "); + for (i=num-1; i--; ) { /* num at least 3 for D2 */ + if (i % 20 == 0) + qh_fprintf(fp, 9044, "\n"); + qh_fprintf(fp, 9045, "0 "); + } + qh_fprintf(fp, 9046, "# 1 color for all\n"); + FORALLpoints { + if (!qh DELAUNAY || !qh ATinfinity || qh_pointid(point) != qh num_points-1) { + if (qh PRINTdim == 4) + qh_printpoint(fp, NULL, point); + else + qh_printpoint3(fp, point); + } + } + FOREACHpoint_(qh other_points) { + if (qh PRINTdim == 4) + qh_printpoint(fp, NULL, point); + else + qh_printpoint3(fp, point); + } + qh_fprintf(fp, 9047, "0 1 1 1 # color of points\n"); + } + + if (qh PRINTdim == 4 && !qh PRINTnoplanes) + /* 4dview loads up multiple 4OFF objects slowly */ + qh_fprintf(fp, 9048, "4OFF %d %d 1\n", 3*qh ridgeoutnum, qh ridgeoutnum); + qh PRINTcradius= 2 * qh DISTround; /* include test DISTround */ + if (qh PREmerge) { + maximize_(qh PRINTcradius, qh premerge_centrum + qh DISTround); + }else if (qh POSTmerge) + maximize_(qh PRINTcradius, qh postmerge_centrum + qh DISTround); + qh PRINTradius= qh PRINTcradius; + if (qh PRINTspheres + qh PRINTcoplanar) + maximize_(qh PRINTradius, qh MAXabs_coord * qh_MINradius); + if (qh premerge_cos < REALmax/2) { + maximize_(qh PRINTradius, (1- qh premerge_cos) * qh MAXabs_coord); + }else if (!qh PREmerge && qh POSTmerge && qh postmerge_cos < REALmax/2) { + maximize_(qh PRINTradius, (1- qh postmerge_cos) * qh MAXabs_coord); + } + maximize_(qh PRINTradius, qh MINvisible); + if (qh JOGGLEmax < REALmax/2) + qh PRINTradius += qh JOGGLEmax * sqrt((realT)qh hull_dim); + if (qh PRINTdim != 4 && + (qh PRINTcoplanar || qh PRINTspheres || qh PRINTcentrums)) { + vertices= qh_facetvertices(facetlist, facets, printall); + if (qh PRINTspheres && qh PRINTdim <= 3) + qh_printspheres(fp, vertices, qh PRINTradius); + if (qh PRINTcoplanar || qh PRINTcentrums) { + qh firstcentrum= True; + if (qh PRINTcoplanar&& !qh PRINTspheres) { + FOREACHvertex_(vertices) + qh_printpointvect2(fp, vertex->point, NULL, qh interior_point, qh PRINTradius); + } + FORALLfacet_(facetlist) { + if (!printall && qh_skipfacet(facet)) + continue; + if (!facet->normal) + continue; + if (qh PRINTcentrums && qh PRINTdim <= 3) + qh_printcentrum(fp, facet, qh PRINTcradius); + if (!qh PRINTcoplanar) + continue; + FOREACHpoint_(facet->coplanarset) + qh_printpointvect2(fp, point, facet->normal, NULL, qh PRINTradius); + FOREACHpoint_(facet->outsideset) + qh_printpointvect2(fp, point, facet->normal, NULL, qh PRINTradius); + } + FOREACHfacet_(facets) { + if (!printall && qh_skipfacet(facet)) + continue; + if (!facet->normal) + continue; + if (qh PRINTcentrums && qh PRINTdim <= 3) + qh_printcentrum(fp, facet, qh PRINTcradius); + if (!qh PRINTcoplanar) + continue; + FOREACHpoint_(facet->coplanarset) + qh_printpointvect2(fp, point, facet->normal, NULL, qh PRINTradius); + FOREACHpoint_(facet->outsideset) + qh_printpointvect2(fp, point, facet->normal, NULL, qh PRINTradius); + } + } + qh_settempfree(&vertices); + } + qh visit_id++; /* for printing hyperplane intersections */ + break; + case qh_PRINTids: + qh_fprintf(fp, 9049, "%d\n", numfacets); + break; + case qh_PRINTincidences: + if (qh VORONOI && qh PRINTprecision) + qh_fprintf(qh ferr, 7053, "qhull warning: writing Delaunay. Use 'p' or 'o' for Voronoi centers\n"); + qh printoutvar= qh vertex_id; /* centrum id for non-simplicial facets */ + if (qh hull_dim <= 3) + qh_fprintf(fp, 9050, "%d\n", numfacets); + else + qh_fprintf(fp, 9051, "%d\n", numsimplicial+numridges); + break; + case qh_PRINTinner: + case qh_PRINTnormals: + case qh_PRINTouter: + if (qh CDDoutput) + qh_fprintf(fp, 9052, "%s | %s\nbegin\n %d %d real\n", qh rbox_command, + qh qhull_command, numfacets, qh hull_dim+1); + else + qh_fprintf(fp, 9053, "%d\n%d\n", qh hull_dim+1, numfacets); + break; + case qh_PRINTmathematica: + case qh_PRINTmaple: + if (qh hull_dim > 3) /* qh_initbuffers also checks */ + goto LABELnoformat; + if (qh VORONOI) + qh_fprintf(qh ferr, 7054, "qhull warning: output is the Delaunay triangulation\n"); + if (format == qh_PRINTmaple) { + if (qh hull_dim == 2) + qh_fprintf(fp, 9054, "PLOT(CURVES(\n"); + else + qh_fprintf(fp, 9055, "PLOT3D(POLYGONS(\n"); + }else + qh_fprintf(fp, 9056, "{\n"); + qh printoutvar= 0; /* counts number of facets for notfirst */ + break; + case qh_PRINTmerges: + qh_fprintf(fp, 9057, "%d\n", numfacets); + break; + case qh_PRINTpointintersect: + qh_fprintf(fp, 9058, "%d\n%d\n", qh hull_dim, numfacets); + break; + case qh_PRINTneighbors: + qh_fprintf(fp, 9059, "%d\n", numfacets); + break; + case qh_PRINToff: + case qh_PRINTtriangles: + if (qh VORONOI) + goto LABELnoformat; + num = qh hull_dim; + if (format == qh_PRINToff || qh hull_dim == 2) + qh_fprintf(fp, 9060, "%d\n%d %d %d\n", num, + qh num_points+qh_setsize(qh other_points), numfacets, totneighbors/2); + else { /* qh_PRINTtriangles */ + qh printoutvar= qh num_points+qh_setsize(qh other_points); /* first centrum */ + if (qh DELAUNAY) + num--; /* drop last dimension */ + qh_fprintf(fp, 9061, "%d\n%d %d %d\n", num, qh printoutvar + + numfacets - numsimplicial, numsimplicial + numridges, totneighbors/2); + } + FORALLpoints + qh_printpointid(qh fout, NULL, num, point, qh_IDunknown); + FOREACHpoint_(qh other_points) + qh_printpointid(qh fout, NULL, num, point, qh_IDunknown); + if (format == qh_PRINTtriangles && qh hull_dim > 2) { + FORALLfacets { + if (!facet->simplicial && facet->visitid) + qh_printcenter(qh fout, format, NULL, facet); + } + } + break; + case qh_PRINTpointnearest: + qh_fprintf(fp, 9062, "%d\n", numcoplanars); + break; + case qh_PRINTpoints: + if (!qh VORONOI) + goto LABELnoformat; + if (qh CDDoutput) + qh_fprintf(fp, 9063, "%s | %s\nbegin\n%d %d real\n", qh rbox_command, + qh qhull_command, numfacets, qh hull_dim); + else + qh_fprintf(fp, 9064, "%d\n%d\n", qh hull_dim-1, numfacets); + break; + case qh_PRINTvertices: + qh_fprintf(fp, 9065, "%d\n", numfacets); + break; + case qh_PRINTsummary: + default: + LABELnoformat: + qh_fprintf(qh ferr, 6068, "qhull internal error (qh_printbegin): can not use this format for dimension %d\n", + qh hull_dim); + qh_errexit(qh_ERRqhull, NULL, NULL); + } +} /* printbegin */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printcenter">-</a> + + qh_printcenter( fp, string, facet ) + print facet->center as centrum or Voronoi center + string may be NULL. Don't include '%' codes. + nop if qh CENTERtype neither CENTERvoronoi nor CENTERcentrum + if upper envelope of Delaunay triangulation and point at-infinity + prints qh_INFINITE instead; + + notes: + defines facet->center if needed + if format=PRINTgeom, adds a 0 if would otherwise be 2-d + Same as QhullFacet::printCenter +*/ +void qh_printcenter(FILE *fp, qh_PRINT format, const char *string, facetT *facet) { + int k, num; + + if (qh CENTERtype != qh_ASvoronoi && qh CENTERtype != qh_AScentrum) + return; + if (string) + qh_fprintf(fp, 9066, string); + if (qh CENTERtype == qh_ASvoronoi) { + num= qh hull_dim-1; + if (!facet->normal || !facet->upperdelaunay || !qh ATinfinity) { + if (!facet->center) + facet->center= qh_facetcenter(facet->vertices); + for (k=0; k < num; k++) + qh_fprintf(fp, 9067, qh_REAL_1, facet->center[k]); + }else { + for (k=0; k < num; k++) + qh_fprintf(fp, 9068, qh_REAL_1, qh_INFINITE); + } + }else /* qh.CENTERtype == qh_AScentrum */ { + num= qh hull_dim; + if (format == qh_PRINTtriangles && qh DELAUNAY) + num--; + if (!facet->center) + facet->center= qh_getcentrum(facet); + for (k=0; k < num; k++) + qh_fprintf(fp, 9069, qh_REAL_1, facet->center[k]); + } + if (format == qh_PRINTgeom && num == 2) + qh_fprintf(fp, 9070, " 0\n"); + else + qh_fprintf(fp, 9071, "\n"); +} /* printcenter */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printcentrum">-</a> + + qh_printcentrum( fp, facet, radius ) + print centrum for a facet in OOGL format + radius defines size of centrum + 2-d or 3-d only + + returns: + defines facet->center if needed +*/ +void qh_printcentrum(FILE *fp, facetT *facet, realT radius) { + pointT *centrum, *projpt; + boolT tempcentrum= False; + realT xaxis[4], yaxis[4], normal[4], dist; + realT green[3]={0, 1, 0}; + vertexT *apex; + int k; + + if (qh CENTERtype == qh_AScentrum) { + if (!facet->center) + facet->center= qh_getcentrum(facet); + centrum= facet->center; + }else { + centrum= qh_getcentrum(facet); + tempcentrum= True; + } + qh_fprintf(fp, 9072, "{appearance {-normal -edge normscale 0} "); + if (qh firstcentrum) { + qh firstcentrum= False; + qh_fprintf(fp, 9073, "{INST geom { define centrum CQUAD # f%d\n\ +-0.3 -0.3 0.0001 0 0 1 1\n\ + 0.3 -0.3 0.0001 0 0 1 1\n\ + 0.3 0.3 0.0001 0 0 1 1\n\ +-0.3 0.3 0.0001 0 0 1 1 } transform { \n", facet->id); + }else + qh_fprintf(fp, 9074, "{INST geom { : centrum } transform { # f%d\n", facet->id); + apex= SETfirstt_(facet->vertices, vertexT); + qh_distplane(apex->point, facet, &dist); + projpt= qh_projectpoint(apex->point, facet, dist); + for (k=qh hull_dim; k--; ) { + xaxis[k]= projpt[k] - centrum[k]; + normal[k]= facet->normal[k]; + } + if (qh hull_dim == 2) { + xaxis[2]= 0; + normal[2]= 0; + }else if (qh hull_dim == 4) { + qh_projectdim3(xaxis, xaxis); + qh_projectdim3(normal, normal); + qh_normalize2(normal, qh PRINTdim, True, NULL, NULL); + } + qh_crossproduct(3, xaxis, normal, yaxis); + qh_fprintf(fp, 9075, "%8.4g %8.4g %8.4g 0\n", xaxis[0], xaxis[1], xaxis[2]); + qh_fprintf(fp, 9076, "%8.4g %8.4g %8.4g 0\n", yaxis[0], yaxis[1], yaxis[2]); + qh_fprintf(fp, 9077, "%8.4g %8.4g %8.4g 0\n", normal[0], normal[1], normal[2]); + qh_printpoint3(fp, centrum); + qh_fprintf(fp, 9078, "1 }}}\n"); + qh_memfree(projpt, qh normal_size); + qh_printpointvect(fp, centrum, facet->normal, NULL, radius, green); + if (tempcentrum) + qh_memfree(centrum, qh normal_size); +} /* printcentrum */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printend">-</a> + + qh_printend( fp, format ) + prints trailer for all output formats + + see: + qh_printbegin() and qh_printafacet() + +*/ +void qh_printend(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) { + int num; + facetT *facet, **facetp; + + if (!qh printoutnum) + qh_fprintf(qh ferr, 7055, "qhull warning: no facets printed\n"); + switch (format) { + case qh_PRINTgeom: + if (qh hull_dim == 4 && qh DROPdim < 0 && !qh PRINTnoplanes) { + qh visit_id++; + num= 0; + FORALLfacet_(facetlist) + qh_printend4geom(fp, facet,&num, printall); + FOREACHfacet_(facets) + qh_printend4geom(fp, facet, &num, printall); + if (num != qh ridgeoutnum || qh printoutvar != qh ridgeoutnum) { + qh_fprintf(qh ferr, 6069, "qhull internal error (qh_printend): number of ridges %d != number printed %d and at end %d\n", qh ridgeoutnum, qh printoutvar, num); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + }else + qh_fprintf(fp, 9079, "}\n"); + break; + case qh_PRINTinner: + case qh_PRINTnormals: + case qh_PRINTouter: + if (qh CDDoutput) + qh_fprintf(fp, 9080, "end\n"); + break; + case qh_PRINTmaple: + qh_fprintf(fp, 9081, "));\n"); + break; + case qh_PRINTmathematica: + qh_fprintf(fp, 9082, "}\n"); + break; + case qh_PRINTpoints: + if (qh CDDoutput) + qh_fprintf(fp, 9083, "end\n"); + break; + default: + break; + } +} /* printend */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printend4geom">-</a> + + qh_printend4geom( fp, facet, numridges, printall ) + helper function for qh_printbegin/printend + + returns: + number of printed ridges + + notes: + just counts printed ridges if fp=NULL + uses facet->visitid + must agree with qh_printfacet4geom... + + design: + computes color for facet from its normal + prints each ridge of facet +*/ +void qh_printend4geom(FILE *fp, facetT *facet, int *nump, boolT printall) { + realT color[3]; + int i, num= *nump; + facetT *neighbor, **neighborp; + ridgeT *ridge, **ridgep; + + if (!printall && qh_skipfacet(facet)) + return; + if (qh PRINTnoplanes || (facet->visible && qh NEWfacets)) + return; + if (!facet->normal) + return; + if (fp) { + for (i=0; i < 3; i++) { + color[i]= (facet->normal[i]+1.0)/2.0; + maximize_(color[i], -1.0); + minimize_(color[i], +1.0); + } + } + facet->visitid= qh visit_id; + if (facet->simplicial) { + FOREACHneighbor_(facet) { + if (neighbor->visitid != qh visit_id) { + if (fp) + qh_fprintf(fp, 9084, "3 %d %d %d %8.4g %8.4g %8.4g 1 # f%d f%d\n", + 3*num, 3*num+1, 3*num+2, color[0], color[1], color[2], + facet->id, neighbor->id); + num++; + } + } + }else { + FOREACHridge_(facet->ridges) { + neighbor= otherfacet_(ridge, facet); + if (neighbor->visitid != qh visit_id) { + if (fp) + qh_fprintf(fp, 9085, "3 %d %d %d %8.4g %8.4g %8.4g 1 #r%d f%d f%d\n", + 3*num, 3*num+1, 3*num+2, color[0], color[1], color[2], + ridge->id, facet->id, neighbor->id); + num++; + } + } + } + *nump= num; +} /* printend4geom */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printextremes">-</a> + + qh_printextremes( fp, facetlist, facets, printall ) + print extreme points for convex hulls or halfspace intersections + + notes: + #points, followed by ids, one per line + + sorted by id + same order as qh_printpoints_out if no coplanar/interior points +*/ +void qh_printextremes(FILE *fp, facetT *facetlist, setT *facets, boolT printall) { + setT *vertices, *points; + pointT *point; + vertexT *vertex, **vertexp; + int id; + int numpoints=0, point_i, point_n; + int allpoints= qh num_points + qh_setsize(qh other_points); + + points= qh_settemp(allpoints); + qh_setzero(points, 0, allpoints); + vertices= qh_facetvertices(facetlist, facets, printall); + FOREACHvertex_(vertices) { + id= qh_pointid(vertex->point); + if (id >= 0) { + SETelem_(points, id)= vertex->point; + numpoints++; + } + } + qh_settempfree(&vertices); + qh_fprintf(fp, 9086, "%d\n", numpoints); + FOREACHpoint_i_(points) { + if (point) + qh_fprintf(fp, 9087, "%d\n", point_i); + } + qh_settempfree(&points); +} /* printextremes */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printextremes_2d">-</a> + + qh_printextremes_2d( fp, facetlist, facets, printall ) + prints point ids for facets in qh_ORIENTclock order + + notes: + #points, followed by ids, one per line + if facetlist/facets are disjoint than the output includes skips + errors if facets form a loop + does not print coplanar points +*/ +void qh_printextremes_2d(FILE *fp, facetT *facetlist, setT *facets, boolT printall) { + int numfacets, numridges, totneighbors, numcoplanars, numsimplicial, numtricoplanars; + setT *vertices; + facetT *facet, *startfacet, *nextfacet; + vertexT *vertexA, *vertexB; + + qh_countfacets(facetlist, facets, printall, &numfacets, &numsimplicial, + &totneighbors, &numridges, &numcoplanars, &numtricoplanars); /* marks qh visit_id */ + vertices= qh_facetvertices(facetlist, facets, printall); + qh_fprintf(fp, 9088, "%d\n", qh_setsize(vertices)); + qh_settempfree(&vertices); + if (!numfacets) + return; + facet= startfacet= facetlist ? facetlist : SETfirstt_(facets, facetT); + qh vertex_visit++; + qh visit_id++; + do { + if (facet->toporient ^ qh_ORIENTclock) { + vertexA= SETfirstt_(facet->vertices, vertexT); + vertexB= SETsecondt_(facet->vertices, vertexT); + nextfacet= SETfirstt_(facet->neighbors, facetT); + }else { + vertexA= SETsecondt_(facet->vertices, vertexT); + vertexB= SETfirstt_(facet->vertices, vertexT); + nextfacet= SETsecondt_(facet->neighbors, facetT); + } + if (facet->visitid == qh visit_id) { + qh_fprintf(qh ferr, 6218, "Qhull internal error (qh_printextremes_2d): loop in facet list. facet %d nextfacet %d\n", + facet->id, nextfacet->id); + qh_errexit2(qh_ERRqhull, facet, nextfacet); + } + if (facet->visitid) { + if (vertexA->visitid != qh vertex_visit) { + vertexA->visitid= qh vertex_visit; + qh_fprintf(fp, 9089, "%d\n", qh_pointid(vertexA->point)); + } + if (vertexB->visitid != qh vertex_visit) { + vertexB->visitid= qh vertex_visit; + qh_fprintf(fp, 9090, "%d\n", qh_pointid(vertexB->point)); + } + } + facet->visitid= qh visit_id; + facet= nextfacet; + }while (facet && facet != startfacet); +} /* printextremes_2d */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printextremes_d">-</a> + + qh_printextremes_d( fp, facetlist, facets, printall ) + print extreme points of input sites for Delaunay triangulations + + notes: + #points, followed by ids, one per line + + unordered +*/ +void qh_printextremes_d(FILE *fp, facetT *facetlist, setT *facets, boolT printall) { + setT *vertices; + vertexT *vertex, **vertexp; + boolT upperseen, lowerseen; + facetT *neighbor, **neighborp; + int numpoints=0; + + vertices= qh_facetvertices(facetlist, facets, printall); + qh_vertexneighbors(); + FOREACHvertex_(vertices) { + upperseen= lowerseen= False; + FOREACHneighbor_(vertex) { + if (neighbor->upperdelaunay) + upperseen= True; + else + lowerseen= True; + } + if (upperseen && lowerseen) { + vertex->seen= True; + numpoints++; + }else + vertex->seen= False; + } + qh_fprintf(fp, 9091, "%d\n", numpoints); + FOREACHvertex_(vertices) { + if (vertex->seen) + qh_fprintf(fp, 9092, "%d\n", qh_pointid(vertex->point)); + } + qh_settempfree(&vertices); +} /* printextremes_d */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printfacet">-</a> + + qh_printfacet( fp, facet ) + prints all fields of a facet to fp + + notes: + ridges printed in neighbor order +*/ +void qh_printfacet(FILE *fp, facetT *facet) { + + qh_printfacetheader(fp, facet); + if (facet->ridges) + qh_printfacetridges(fp, facet); +} /* printfacet */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printfacet2geom">-</a> + + qh_printfacet2geom( fp, facet, color ) + print facet as part of a 2-d VECT for Geomview + + notes: + assume precise calculations in io.c with roundoff covered by qh_GEOMepsilon + mindist is calculated within io.c. maxoutside is calculated elsewhere + so a DISTround error may have occurred. +*/ +void qh_printfacet2geom(FILE *fp, facetT *facet, realT color[3]) { + pointT *point0, *point1; + realT mindist, innerplane, outerplane; + int k; + + qh_facet2point(facet, &point0, &point1, &mindist); + qh_geomplanes(facet, &outerplane, &innerplane); + if (qh PRINTouter || (!qh PRINTnoplanes && !qh PRINTinner)) + qh_printfacet2geom_points(fp, point0, point1, facet, outerplane, color); + if (qh PRINTinner || (!qh PRINTnoplanes && !qh PRINTouter && + outerplane - innerplane > 2 * qh MAXabs_coord * qh_GEOMepsilon)) { + for (k=3; k--; ) + color[k]= 1.0 - color[k]; + qh_printfacet2geom_points(fp, point0, point1, facet, innerplane, color); + } + qh_memfree(point1, qh normal_size); + qh_memfree(point0, qh normal_size); +} /* printfacet2geom */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printfacet2geom_points">-</a> + + qh_printfacet2geom_points( fp, point1, point2, facet, offset, color ) + prints a 2-d facet as a VECT with 2 points at some offset. + The points are on the facet's plane. +*/ +void qh_printfacet2geom_points(FILE *fp, pointT *point1, pointT *point2, + facetT *facet, realT offset, realT color[3]) { + pointT *p1= point1, *p2= point2; + + qh_fprintf(fp, 9093, "VECT 1 2 1 2 1 # f%d\n", facet->id); + if (offset != 0.0) { + p1= qh_projectpoint(p1, facet, -offset); + p2= qh_projectpoint(p2, facet, -offset); + } + qh_fprintf(fp, 9094, "%8.4g %8.4g %8.4g\n%8.4g %8.4g %8.4g\n", + p1[0], p1[1], 0.0, p2[0], p2[1], 0.0); + if (offset != 0.0) { + qh_memfree(p1, qh normal_size); + qh_memfree(p2, qh normal_size); + } + qh_fprintf(fp, 9095, "%8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]); +} /* printfacet2geom_points */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printfacet2math">-</a> + + qh_printfacet2math( fp, facet, format, notfirst ) + print 2-d Maple or Mathematica output for a facet + may be non-simplicial + + notes: + use %16.8f since Mathematica 2.2 does not handle exponential format + see qh_printfacet3math +*/ +void qh_printfacet2math(FILE *fp, facetT *facet, qh_PRINT format, int notfirst) { + pointT *point0, *point1; + realT mindist; + const char *pointfmt; + + qh_facet2point(facet, &point0, &point1, &mindist); + if (notfirst) + qh_fprintf(fp, 9096, ","); + if (format == qh_PRINTmaple) + pointfmt= "[[%16.8f, %16.8f], [%16.8f, %16.8f]]\n"; + else + pointfmt= "Line[{{%16.8f, %16.8f}, {%16.8f, %16.8f}}]\n"; + qh_fprintf(fp, 9097, pointfmt, point0[0], point0[1], point1[0], point1[1]); + qh_memfree(point1, qh normal_size); + qh_memfree(point0, qh normal_size); +} /* printfacet2math */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printfacet3geom_nonsimplicial">-</a> + + qh_printfacet3geom_nonsimplicial( fp, facet, color ) + print Geomview OFF for a 3-d nonsimplicial facet. + if DOintersections, prints ridges to unvisited neighbors(qh visit_id) + + notes + uses facet->visitid for intersections and ridges +*/ +void qh_printfacet3geom_nonsimplicial(FILE *fp, facetT *facet, realT color[3]) { + ridgeT *ridge, **ridgep; + setT *projectedpoints, *vertices; + vertexT *vertex, **vertexp, *vertexA, *vertexB; + pointT *projpt, *point, **pointp; + facetT *neighbor; + realT dist, outerplane, innerplane; + int cntvertices, k; + realT black[3]={0, 0, 0}, green[3]={0, 1, 0}; + + qh_geomplanes(facet, &outerplane, &innerplane); + vertices= qh_facet3vertex(facet); /* oriented */ + cntvertices= qh_setsize(vertices); + projectedpoints= qh_settemp(cntvertices); + FOREACHvertex_(vertices) { + zinc_(Zdistio); + qh_distplane(vertex->point, facet, &dist); + projpt= qh_projectpoint(vertex->point, facet, dist); + qh_setappend(&projectedpoints, projpt); + } + if (qh PRINTouter || (!qh PRINTnoplanes && !qh PRINTinner)) + qh_printfacet3geom_points(fp, projectedpoints, facet, outerplane, color); + if (qh PRINTinner || (!qh PRINTnoplanes && !qh PRINTouter && + outerplane - innerplane > 2 * qh MAXabs_coord * qh_GEOMepsilon)) { + for (k=3; k--; ) + color[k]= 1.0 - color[k]; + qh_printfacet3geom_points(fp, projectedpoints, facet, innerplane, color); + } + FOREACHpoint_(projectedpoints) + qh_memfree(point, qh normal_size); + qh_settempfree(&projectedpoints); + qh_settempfree(&vertices); + if ((qh DOintersections || qh PRINTridges) + && (!facet->visible || !qh NEWfacets)) { + facet->visitid= qh visit_id; + FOREACHridge_(facet->ridges) { + neighbor= otherfacet_(ridge, facet); + if (neighbor->visitid != qh visit_id) { + if (qh DOintersections) + qh_printhyperplaneintersection(fp, facet, neighbor, ridge->vertices, black); + if (qh PRINTridges) { + vertexA= SETfirstt_(ridge->vertices, vertexT); + vertexB= SETsecondt_(ridge->vertices, vertexT); + qh_printline3geom(fp, vertexA->point, vertexB->point, green); + } + } + } + } +} /* printfacet3geom_nonsimplicial */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printfacet3geom_points">-</a> + + qh_printfacet3geom_points( fp, points, facet, offset ) + prints a 3-d facet as OFF Geomview object. + offset is relative to the facet's hyperplane + Facet is determined as a list of points +*/ +void qh_printfacet3geom_points(FILE *fp, setT *points, facetT *facet, realT offset, realT color[3]) { + int k, n= qh_setsize(points), i; + pointT *point, **pointp; + setT *printpoints; + + qh_fprintf(fp, 9098, "{ OFF %d 1 1 # f%d\n", n, facet->id); + if (offset != 0.0) { + printpoints= qh_settemp(n); + FOREACHpoint_(points) + qh_setappend(&printpoints, qh_projectpoint(point, facet, -offset)); + }else + printpoints= points; + FOREACHpoint_(printpoints) { + for (k=0; k < qh hull_dim; k++) { + if (k == qh DROPdim) + qh_fprintf(fp, 9099, "0 "); + else + qh_fprintf(fp, 9100, "%8.4g ", point[k]); + } + if (printpoints != points) + qh_memfree(point, qh normal_size); + qh_fprintf(fp, 9101, "\n"); + } + if (printpoints != points) + qh_settempfree(&printpoints); + qh_fprintf(fp, 9102, "%d ", n); + for (i=0; i < n; i++) + qh_fprintf(fp, 9103, "%d ", i); + qh_fprintf(fp, 9104, "%8.4g %8.4g %8.4g 1.0 }\n", color[0], color[1], color[2]); +} /* printfacet3geom_points */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printfacet3geom_simplicial">-</a> + + qh_printfacet3geom_simplicial( ) + print Geomview OFF for a 3-d simplicial facet. + + notes: + may flip color + uses facet->visitid for intersections and ridges + + assume precise calculations in io.c with roundoff covered by qh_GEOMepsilon + innerplane may be off by qh DISTround. Maxoutside is calculated elsewhere + so a DISTround error may have occurred. +*/ +void qh_printfacet3geom_simplicial(FILE *fp, facetT *facet, realT color[3]) { + setT *points, *vertices; + vertexT *vertex, **vertexp, *vertexA, *vertexB; + facetT *neighbor, **neighborp; + realT outerplane, innerplane; + realT black[3]={0, 0, 0}, green[3]={0, 1, 0}; + int k; + + qh_geomplanes(facet, &outerplane, &innerplane); + vertices= qh_facet3vertex(facet); + points= qh_settemp(qh TEMPsize); + FOREACHvertex_(vertices) + qh_setappend(&points, vertex->point); + if (qh PRINTouter || (!qh PRINTnoplanes && !qh PRINTinner)) + qh_printfacet3geom_points(fp, points, facet, outerplane, color); + if (qh PRINTinner || (!qh PRINTnoplanes && !qh PRINTouter && + outerplane - innerplane > 2 * qh MAXabs_coord * qh_GEOMepsilon)) { + for (k=3; k--; ) + color[k]= 1.0 - color[k]; + qh_printfacet3geom_points(fp, points, facet, innerplane, color); + } + qh_settempfree(&points); + qh_settempfree(&vertices); + if ((qh DOintersections || qh PRINTridges) + && (!facet->visible || !qh NEWfacets)) { + facet->visitid= qh visit_id; + FOREACHneighbor_(facet) { + if (neighbor->visitid != qh visit_id) { + vertices= qh_setnew_delnthsorted(facet->vertices, qh hull_dim, + SETindex_(facet->neighbors, neighbor), 0); + if (qh DOintersections) + qh_printhyperplaneintersection(fp, facet, neighbor, vertices, black); + if (qh PRINTridges) { + vertexA= SETfirstt_(vertices, vertexT); + vertexB= SETsecondt_(vertices, vertexT); + qh_printline3geom(fp, vertexA->point, vertexB->point, green); + } + qh_setfree(&vertices); + } + } + } +} /* printfacet3geom_simplicial */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printfacet3math">-</a> + + qh_printfacet3math( fp, facet, notfirst ) + print 3-d Maple or Mathematica output for a facet + + notes: + may be non-simplicial + use %16.8f since Mathematica 2.2 does not handle exponential format + see qh_printfacet2math +*/ +void qh_printfacet3math(FILE *fp, facetT *facet, qh_PRINT format, int notfirst) { + vertexT *vertex, **vertexp; + setT *points, *vertices; + pointT *point, **pointp; + boolT firstpoint= True; + realT dist; + const char *pointfmt, *endfmt; + + if (notfirst) + qh_fprintf(fp, 9105, ",\n"); + vertices= qh_facet3vertex(facet); + points= qh_settemp(qh_setsize(vertices)); + FOREACHvertex_(vertices) { + zinc_(Zdistio); + qh_distplane(vertex->point, facet, &dist); + point= qh_projectpoint(vertex->point, facet, dist); + qh_setappend(&points, point); + } + if (format == qh_PRINTmaple) { + qh_fprintf(fp, 9106, "["); + pointfmt= "[%16.8f, %16.8f, %16.8f]"; + endfmt= "]"; + }else { + qh_fprintf(fp, 9107, "Polygon[{"); + pointfmt= "{%16.8f, %16.8f, %16.8f}"; + endfmt= "}]"; + } + FOREACHpoint_(points) { + if (firstpoint) + firstpoint= False; + else + qh_fprintf(fp, 9108, ",\n"); + qh_fprintf(fp, 9109, pointfmt, point[0], point[1], point[2]); + } + FOREACHpoint_(points) + qh_memfree(point, qh normal_size); + qh_settempfree(&points); + qh_settempfree(&vertices); + qh_fprintf(fp, 9110, "%s", endfmt); +} /* printfacet3math */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printfacet3vertex">-</a> + + qh_printfacet3vertex( fp, facet, format ) + print vertices in a 3-d facet as point ids + + notes: + prints number of vertices first if format == qh_PRINToff + the facet may be non-simplicial +*/ +void qh_printfacet3vertex(FILE *fp, facetT *facet, qh_PRINT format) { + vertexT *vertex, **vertexp; + setT *vertices; + + vertices= qh_facet3vertex(facet); + if (format == qh_PRINToff) + qh_fprintf(fp, 9111, "%d ", qh_setsize(vertices)); + FOREACHvertex_(vertices) + qh_fprintf(fp, 9112, "%d ", qh_pointid(vertex->point)); + qh_fprintf(fp, 9113, "\n"); + qh_settempfree(&vertices); +} /* printfacet3vertex */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printfacet4geom_nonsimplicial">-</a> + + qh_printfacet4geom_nonsimplicial( ) + print Geomview 4OFF file for a 4d nonsimplicial facet + prints all ridges to unvisited neighbors (qh.visit_id) + if qh.DROPdim + prints in OFF format + + notes: + must agree with printend4geom() +*/ +void qh_printfacet4geom_nonsimplicial(FILE *fp, facetT *facet, realT color[3]) { + facetT *neighbor; + ridgeT *ridge, **ridgep; + vertexT *vertex, **vertexp; + pointT *point; + int k; + realT dist; + + facet->visitid= qh visit_id; + if (qh PRINTnoplanes || (facet->visible && qh NEWfacets)) + return; + FOREACHridge_(facet->ridges) { + neighbor= otherfacet_(ridge, facet); + if (neighbor->visitid == qh visit_id) + continue; + if (qh PRINTtransparent && !neighbor->good) + continue; + if (qh DOintersections) + qh_printhyperplaneintersection(fp, facet, neighbor, ridge->vertices, color); + else { + if (qh DROPdim >= 0) + qh_fprintf(fp, 9114, "OFF 3 1 1 # f%d\n", facet->id); + else { + qh printoutvar++; + qh_fprintf(fp, 9115, "# r%d between f%d f%d\n", ridge->id, facet->id, neighbor->id); + } + FOREACHvertex_(ridge->vertices) { + zinc_(Zdistio); + qh_distplane(vertex->point,facet, &dist); + point=qh_projectpoint(vertex->point,facet, dist); + for (k=0; k < qh hull_dim; k++) { + if (k != qh DROPdim) + qh_fprintf(fp, 9116, "%8.4g ", point[k]); + } + qh_fprintf(fp, 9117, "\n"); + qh_memfree(point, qh normal_size); + } + if (qh DROPdim >= 0) + qh_fprintf(fp, 9118, "3 0 1 2 %8.4g %8.4g %8.4g\n", color[0], color[1], color[2]); + } + } +} /* printfacet4geom_nonsimplicial */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printfacet4geom_simplicial">-</a> + + qh_printfacet4geom_simplicial( fp, facet, color ) + print Geomview 4OFF file for a 4d simplicial facet + prints triangles for unvisited neighbors (qh.visit_id) + + notes: + must agree with printend4geom() +*/ +void qh_printfacet4geom_simplicial(FILE *fp, facetT *facet, realT color[3]) { + setT *vertices; + facetT *neighbor, **neighborp; + vertexT *vertex, **vertexp; + int k; + + facet->visitid= qh visit_id; + if (qh PRINTnoplanes || (facet->visible && qh NEWfacets)) + return; + FOREACHneighbor_(facet) { + if (neighbor->visitid == qh visit_id) + continue; + if (qh PRINTtransparent && !neighbor->good) + continue; + vertices= qh_setnew_delnthsorted(facet->vertices, qh hull_dim, + SETindex_(facet->neighbors, neighbor), 0); + if (qh DOintersections) + qh_printhyperplaneintersection(fp, facet, neighbor, vertices, color); + else { + if (qh DROPdim >= 0) + qh_fprintf(fp, 9119, "OFF 3 1 1 # ridge between f%d f%d\n", + facet->id, neighbor->id); + else { + qh printoutvar++; + qh_fprintf(fp, 9120, "# ridge between f%d f%d\n", facet->id, neighbor->id); + } + FOREACHvertex_(vertices) { + for (k=0; k < qh hull_dim; k++) { + if (k != qh DROPdim) + qh_fprintf(fp, 9121, "%8.4g ", vertex->point[k]); + } + qh_fprintf(fp, 9122, "\n"); + } + if (qh DROPdim >= 0) + qh_fprintf(fp, 9123, "3 0 1 2 %8.4g %8.4g %8.4g\n", color[0], color[1], color[2]); + } + qh_setfree(&vertices); + } +} /* printfacet4geom_simplicial */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printfacetNvertex_nonsimplicial">-</a> + + qh_printfacetNvertex_nonsimplicial( fp, facet, id, format ) + print vertices for an N-d non-simplicial facet + triangulates each ridge to the id +*/ +void qh_printfacetNvertex_nonsimplicial(FILE *fp, facetT *facet, int id, qh_PRINT format) { + vertexT *vertex, **vertexp; + ridgeT *ridge, **ridgep; + + if (facet->visible && qh NEWfacets) + return; + FOREACHridge_(facet->ridges) { + if (format == qh_PRINTtriangles) + qh_fprintf(fp, 9124, "%d ", qh hull_dim); + qh_fprintf(fp, 9125, "%d ", id); + if ((ridge->top == facet) ^ qh_ORIENTclock) { + FOREACHvertex_(ridge->vertices) + qh_fprintf(fp, 9126, "%d ", qh_pointid(vertex->point)); + }else { + FOREACHvertexreverse12_(ridge->vertices) + qh_fprintf(fp, 9127, "%d ", qh_pointid(vertex->point)); + } + qh_fprintf(fp, 9128, "\n"); + } +} /* printfacetNvertex_nonsimplicial */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printfacetNvertex_simplicial">-</a> + + qh_printfacetNvertex_simplicial( fp, facet, format ) + print vertices for an N-d simplicial facet + prints vertices for non-simplicial facets + 2-d facets (orientation preserved by qh_mergefacet2d) + PRINToff ('o') for 4-d and higher +*/ +void qh_printfacetNvertex_simplicial(FILE *fp, facetT *facet, qh_PRINT format) { + vertexT *vertex, **vertexp; + + if (format == qh_PRINToff || format == qh_PRINTtriangles) + qh_fprintf(fp, 9129, "%d ", qh_setsize(facet->vertices)); + if ((facet->toporient ^ qh_ORIENTclock) + || (qh hull_dim > 2 && !facet->simplicial)) { + FOREACHvertex_(facet->vertices) + qh_fprintf(fp, 9130, "%d ", qh_pointid(vertex->point)); + }else { + FOREACHvertexreverse12_(facet->vertices) + qh_fprintf(fp, 9131, "%d ", qh_pointid(vertex->point)); + } + qh_fprintf(fp, 9132, "\n"); +} /* printfacetNvertex_simplicial */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printfacetheader">-</a> + + qh_printfacetheader( fp, facet ) + prints header fields of a facet to fp + + notes: + for 'f' output and debugging + Same as QhullFacet::printHeader() +*/ +void qh_printfacetheader(FILE *fp, facetT *facet) { + pointT *point, **pointp, *furthest; + facetT *neighbor, **neighborp; + realT dist; + + if (facet == qh_MERGEridge) { + qh_fprintf(fp, 9133, " MERGEridge\n"); + return; + }else if (facet == qh_DUPLICATEridge) { + qh_fprintf(fp, 9134, " DUPLICATEridge\n"); + return; + }else if (!facet) { + qh_fprintf(fp, 9135, " NULLfacet\n"); + return; + } + qh old_randomdist= qh RANDOMdist; + qh RANDOMdist= False; + qh_fprintf(fp, 9136, "- f%d\n", facet->id); + qh_fprintf(fp, 9137, " - flags:"); + if (facet->toporient) + qh_fprintf(fp, 9138, " top"); + else + qh_fprintf(fp, 9139, " bottom"); + if (facet->simplicial) + qh_fprintf(fp, 9140, " simplicial"); + if (facet->tricoplanar) + qh_fprintf(fp, 9141, " tricoplanar"); + if (facet->upperdelaunay) + qh_fprintf(fp, 9142, " upperDelaunay"); + if (facet->visible) + qh_fprintf(fp, 9143, " visible"); + if (facet->newfacet) + qh_fprintf(fp, 9144, " new"); + if (facet->tested) + qh_fprintf(fp, 9145, " tested"); + if (!facet->good) + qh_fprintf(fp, 9146, " notG"); + if (facet->seen) + qh_fprintf(fp, 9147, " seen"); + if (facet->coplanar) + qh_fprintf(fp, 9148, " coplanar"); + if (facet->mergehorizon) + qh_fprintf(fp, 9149, " mergehorizon"); + if (facet->keepcentrum) + qh_fprintf(fp, 9150, " keepcentrum"); + if (facet->dupridge) + qh_fprintf(fp, 9151, " dupridge"); + if (facet->mergeridge && !facet->mergeridge2) + qh_fprintf(fp, 9152, " mergeridge1"); + if (facet->mergeridge2) + qh_fprintf(fp, 9153, " mergeridge2"); + if (facet->newmerge) + qh_fprintf(fp, 9154, " newmerge"); + if (facet->flipped) + qh_fprintf(fp, 9155, " flipped"); + if (facet->notfurthest) + qh_fprintf(fp, 9156, " notfurthest"); + if (facet->degenerate) + qh_fprintf(fp, 9157, " degenerate"); + if (facet->redundant) + qh_fprintf(fp, 9158, " redundant"); + qh_fprintf(fp, 9159, "\n"); + if (facet->isarea) + qh_fprintf(fp, 9160, " - area: %2.2g\n", facet->f.area); + else if (qh NEWfacets && facet->visible && facet->f.replace) + qh_fprintf(fp, 9161, " - replacement: f%d\n", facet->f.replace->id); + else if (facet->newfacet) { + if (facet->f.samecycle && facet->f.samecycle != facet) + qh_fprintf(fp, 9162, " - shares same visible/horizon as f%d\n", facet->f.samecycle->id); + }else if (facet->tricoplanar /* !isarea */) { + if (facet->f.triowner) + qh_fprintf(fp, 9163, " - owner of normal & centrum is facet f%d\n", facet->f.triowner->id); + }else if (facet->f.newcycle) + qh_fprintf(fp, 9164, " - was horizon to f%d\n", facet->f.newcycle->id); + if (facet->nummerge) + qh_fprintf(fp, 9165, " - merges: %d\n", facet->nummerge); + qh_printpointid(fp, " - normal: ", qh hull_dim, facet->normal, qh_IDunknown); + qh_fprintf(fp, 9166, " - offset: %10.7g\n", facet->offset); + if (qh CENTERtype == qh_ASvoronoi || facet->center) + qh_printcenter(fp, qh_PRINTfacets, " - center: ", facet); +#if qh_MAXoutside + if (facet->maxoutside > qh DISTround) + qh_fprintf(fp, 9167, " - maxoutside: %10.7g\n", facet->maxoutside); +#endif + if (!SETempty_(facet->outsideset)) { + furthest= (pointT*)qh_setlast(facet->outsideset); + if (qh_setsize(facet->outsideset) < 6) { + qh_fprintf(fp, 9168, " - outside set(furthest p%d):\n", qh_pointid(furthest)); + FOREACHpoint_(facet->outsideset) + qh_printpoint(fp, " ", point); + }else if (qh_setsize(facet->outsideset) < 21) { + qh_printpoints(fp, " - outside set:", facet->outsideset); + }else { + qh_fprintf(fp, 9169, " - outside set: %d points.", qh_setsize(facet->outsideset)); + qh_printpoint(fp, " Furthest", furthest); + } +#if !qh_COMPUTEfurthest + qh_fprintf(fp, 9170, " - furthest distance= %2.2g\n", facet->furthestdist); +#endif + } + if (!SETempty_(facet->coplanarset)) { + furthest= (pointT*)qh_setlast(facet->coplanarset); + if (qh_setsize(facet->coplanarset) < 6) { + qh_fprintf(fp, 9171, " - coplanar set(furthest p%d):\n", qh_pointid(furthest)); + FOREACHpoint_(facet->coplanarset) + qh_printpoint(fp, " ", point); + }else if (qh_setsize(facet->coplanarset) < 21) { + qh_printpoints(fp, " - coplanar set:", facet->coplanarset); + }else { + qh_fprintf(fp, 9172, " - coplanar set: %d points.", qh_setsize(facet->coplanarset)); + qh_printpoint(fp, " Furthest", furthest); + } + zinc_(Zdistio); + qh_distplane(furthest, facet, &dist); + qh_fprintf(fp, 9173, " furthest distance= %2.2g\n", dist); + } + qh_printvertices(fp, " - vertices:", facet->vertices); + qh_fprintf(fp, 9174, " - neighboring facets:"); + FOREACHneighbor_(facet) { + if (neighbor == qh_MERGEridge) + qh_fprintf(fp, 9175, " MERGE"); + else if (neighbor == qh_DUPLICATEridge) + qh_fprintf(fp, 9176, " DUP"); + else + qh_fprintf(fp, 9177, " f%d", neighbor->id); + } + qh_fprintf(fp, 9178, "\n"); + qh RANDOMdist= qh old_randomdist; +} /* printfacetheader */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printfacetridges">-</a> + + qh_printfacetridges( fp, facet ) + prints ridges of a facet to fp + + notes: + ridges printed in neighbor order + assumes the ridges exist + for 'f' output + same as QhullFacet::printRidges +*/ +void qh_printfacetridges(FILE *fp, facetT *facet) { + facetT *neighbor, **neighborp; + ridgeT *ridge, **ridgep; + int numridges= 0; + + + if (facet->visible && qh NEWfacets) { + qh_fprintf(fp, 9179, " - ridges(ids may be garbage):"); + FOREACHridge_(facet->ridges) + qh_fprintf(fp, 9180, " r%d", ridge->id); + qh_fprintf(fp, 9181, "\n"); + }else { + qh_fprintf(fp, 9182, " - ridges:\n"); + FOREACHridge_(facet->ridges) + ridge->seen= False; + if (qh hull_dim == 3) { + ridge= SETfirstt_(facet->ridges, ridgeT); + while (ridge && !ridge->seen) { + ridge->seen= True; + qh_printridge(fp, ridge); + numridges++; + ridge= qh_nextridge3d(ridge, facet, NULL); + } + }else { + FOREACHneighbor_(facet) { + FOREACHridge_(facet->ridges) { + if (otherfacet_(ridge,facet) == neighbor) { + ridge->seen= True; + qh_printridge(fp, ridge); + numridges++; + } + } + } + } + if (numridges != qh_setsize(facet->ridges)) { + qh_fprintf(fp, 9183, " - all ridges:"); + FOREACHridge_(facet->ridges) + qh_fprintf(fp, 9184, " r%d", ridge->id); + qh_fprintf(fp, 9185, "\n"); + } + FOREACHridge_(facet->ridges) { + if (!ridge->seen) + qh_printridge(fp, ridge); + } + } +} /* printfacetridges */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printfacets">-</a> + + qh_printfacets( fp, format, facetlist, facets, printall ) + prints facetlist and/or facet set in output format + + notes: + also used for specialized formats ('FO' and summary) + turns off 'Rn' option since want actual numbers +*/ +void qh_printfacets(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) { + int numfacets, numsimplicial, numridges, totneighbors, numcoplanars, numtricoplanars; + facetT *facet, **facetp; + setT *vertices; + coordT *center; + realT outerplane, innerplane; + + qh old_randomdist= qh RANDOMdist; + qh RANDOMdist= False; + if (qh CDDoutput && (format == qh_PRINTcentrums || format == qh_PRINTpointintersect || format == qh_PRINToff)) + qh_fprintf(qh ferr, 7056, "qhull warning: CDD format is not available for centrums, halfspace\nintersections, and OFF file format.\n"); + if (format == qh_PRINTnone) + ; /* print nothing */ + else if (format == qh_PRINTaverage) { + vertices= qh_facetvertices(facetlist, facets, printall); + center= qh_getcenter(vertices); + qh_fprintf(fp, 9186, "%d 1\n", qh hull_dim); + qh_printpointid(fp, NULL, qh hull_dim, center, qh_IDunknown); + qh_memfree(center, qh normal_size); + qh_settempfree(&vertices); + }else if (format == qh_PRINTextremes) { + if (qh DELAUNAY) + qh_printextremes_d(fp, facetlist, facets, printall); + else if (qh hull_dim == 2) + qh_printextremes_2d(fp, facetlist, facets, printall); + else + qh_printextremes(fp, facetlist, facets, printall); + }else if (format == qh_PRINToptions) + qh_fprintf(fp, 9187, "Options selected for Qhull %s:\n%s\n", qh_version, qh qhull_options); + else if (format == qh_PRINTpoints && !qh VORONOI) + qh_printpoints_out(fp, facetlist, facets, printall); + else if (format == qh_PRINTqhull) + qh_fprintf(fp, 9188, "%s | %s\n", qh rbox_command, qh qhull_command); + else if (format == qh_PRINTsize) { + qh_fprintf(fp, 9189, "0\n2 "); + qh_fprintf(fp, 9190, qh_REAL_1, qh totarea); + qh_fprintf(fp, 9191, qh_REAL_1, qh totvol); + qh_fprintf(fp, 9192, "\n"); + }else if (format == qh_PRINTsummary) { + qh_countfacets(facetlist, facets, printall, &numfacets, &numsimplicial, + &totneighbors, &numridges, &numcoplanars, &numtricoplanars); + vertices= qh_facetvertices(facetlist, facets, printall); + qh_fprintf(fp, 9193, "10 %d %d %d %d %d %d %d %d %d %d\n2 ", qh hull_dim, + qh num_points + qh_setsize(qh other_points), + qh num_vertices, qh num_facets - qh num_visible, + qh_setsize(vertices), numfacets, numcoplanars, + numfacets - numsimplicial, zzval_(Zdelvertextot), + numtricoplanars); + qh_settempfree(&vertices); + qh_outerinner(NULL, &outerplane, &innerplane); + qh_fprintf(fp, 9194, qh_REAL_2n, outerplane, innerplane); + }else if (format == qh_PRINTvneighbors) + qh_printvneighbors(fp, facetlist, facets, printall); + else if (qh VORONOI && format == qh_PRINToff) + qh_printvoronoi(fp, format, facetlist, facets, printall); + else if (qh VORONOI && format == qh_PRINTgeom) { + qh_printbegin(fp, format, facetlist, facets, printall); + qh_printvoronoi(fp, format, facetlist, facets, printall); + qh_printend(fp, format, facetlist, facets, printall); + }else if (qh VORONOI + && (format == qh_PRINTvertices || format == qh_PRINTinner || format == qh_PRINTouter)) + qh_printvdiagram(fp, format, facetlist, facets, printall); + else { + qh_printbegin(fp, format, facetlist, facets, printall); + FORALLfacet_(facetlist) + qh_printafacet(fp, format, facet, printall); + FOREACHfacet_(facets) + qh_printafacet(fp, format, facet, printall); + qh_printend(fp, format, facetlist, facets, printall); + } + qh RANDOMdist= qh old_randomdist; +} /* printfacets */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printhyperplaneintersection">-</a> + + qh_printhyperplaneintersection( fp, facet1, facet2, vertices, color ) + print Geomview OFF or 4OFF for the intersection of two hyperplanes in 3-d or 4-d +*/ +void qh_printhyperplaneintersection(FILE *fp, facetT *facet1, facetT *facet2, + setT *vertices, realT color[3]) { + realT costheta, denominator, dist1, dist2, s, t, mindenom, p[4]; + vertexT *vertex, **vertexp; + int i, k; + boolT nearzero1, nearzero2; + + costheta= qh_getangle(facet1->normal, facet2->normal); + denominator= 1 - costheta * costheta; + i= qh_setsize(vertices); + if (qh hull_dim == 3) + qh_fprintf(fp, 9195, "VECT 1 %d 1 %d 1 ", i, i); + else if (qh hull_dim == 4 && qh DROPdim >= 0) + qh_fprintf(fp, 9196, "OFF 3 1 1 "); + else + qh printoutvar++; + qh_fprintf(fp, 9197, "# intersect f%d f%d\n", facet1->id, facet2->id); + mindenom= 1 / (10.0 * qh MAXabs_coord); + FOREACHvertex_(vertices) { + zadd_(Zdistio, 2); + qh_distplane(vertex->point, facet1, &dist1); + qh_distplane(vertex->point, facet2, &dist2); + s= qh_divzero(-dist1 + costheta * dist2, denominator,mindenom,&nearzero1); + t= qh_divzero(-dist2 + costheta * dist1, denominator,mindenom,&nearzero2); + if (nearzero1 || nearzero2) + s= t= 0.0; + for (k=qh hull_dim; k--; ) + p[k]= vertex->point[k] + facet1->normal[k] * s + facet2->normal[k] * t; + if (qh PRINTdim <= 3) { + qh_projectdim3(p, p); + qh_fprintf(fp, 9198, "%8.4g %8.4g %8.4g # ", p[0], p[1], p[2]); + }else + qh_fprintf(fp, 9199, "%8.4g %8.4g %8.4g %8.4g # ", p[0], p[1], p[2], p[3]); + if (nearzero1+nearzero2) + qh_fprintf(fp, 9200, "p%d(coplanar facets)\n", qh_pointid(vertex->point)); + else + qh_fprintf(fp, 9201, "projected p%d\n", qh_pointid(vertex->point)); + } + if (qh hull_dim == 3) + qh_fprintf(fp, 9202, "%8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]); + else if (qh hull_dim == 4 && qh DROPdim >= 0) + qh_fprintf(fp, 9203, "3 0 1 2 %8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]); +} /* printhyperplaneintersection */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printline3geom">-</a> + + qh_printline3geom( fp, pointA, pointB, color ) + prints a line as a VECT + prints 0's for qh.DROPdim + + notes: + if pointA == pointB, + it's a 1 point VECT +*/ +void qh_printline3geom(FILE *fp, pointT *pointA, pointT *pointB, realT color[3]) { + int k; + realT pA[4], pB[4]; + + qh_projectdim3(pointA, pA); + qh_projectdim3(pointB, pB); + if ((fabs(pA[0] - pB[0]) > 1e-3) || + (fabs(pA[1] - pB[1]) > 1e-3) || + (fabs(pA[2] - pB[2]) > 1e-3)) { + qh_fprintf(fp, 9204, "VECT 1 2 1 2 1\n"); + for (k=0; k < 3; k++) + qh_fprintf(fp, 9205, "%8.4g ", pB[k]); + qh_fprintf(fp, 9206, " # p%d\n", qh_pointid(pointB)); + }else + qh_fprintf(fp, 9207, "VECT 1 1 1 1 1\n"); + for (k=0; k < 3; k++) + qh_fprintf(fp, 9208, "%8.4g ", pA[k]); + qh_fprintf(fp, 9209, " # p%d\n", qh_pointid(pointA)); + qh_fprintf(fp, 9210, "%8.4g %8.4g %8.4g 1\n", color[0], color[1], color[2]); +} + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printneighborhood">-</a> + + qh_printneighborhood( fp, format, facetA, facetB, printall ) + print neighborhood of one or two facets + + notes: + calls qh_findgood_all() + bumps qh.visit_id +*/ +void qh_printneighborhood(FILE *fp, qh_PRINT format, facetT *facetA, facetT *facetB, boolT printall) { + facetT *neighbor, **neighborp, *facet; + setT *facets; + + if (format == qh_PRINTnone) + return; + qh_findgood_all(qh facet_list); + if (facetA == facetB) + facetB= NULL; + facets= qh_settemp(2*(qh_setsize(facetA->neighbors)+1)); + qh visit_id++; + for (facet= facetA; facet; facet= ((facet == facetA) ? facetB : NULL)) { + if (facet->visitid != qh visit_id) { + facet->visitid= qh visit_id; + qh_setappend(&facets, facet); + } + FOREACHneighbor_(facet) { + if (neighbor->visitid == qh visit_id) + continue; + neighbor->visitid= qh visit_id; + if (printall || !qh_skipfacet(neighbor)) + qh_setappend(&facets, neighbor); + } + } + qh_printfacets(fp, format, NULL, facets, printall); + qh_settempfree(&facets); +} /* printneighborhood */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printpoint">-</a> + + qh_printpoint( fp, string, point ) + qh_printpointid( fp, string, dim, point, id ) + prints the coordinates of a point + + returns: + if string is defined + prints 'string p%d'. Skips p%d if id=qh_IDunknown(-1) or qh_IDnone(-3) + + notes: + nop if point is NULL + Same as QhullPoint's printPoint +*/ +void qh_printpoint(FILE *fp, const char *string, pointT *point) { + int id= qh_pointid( point); + + qh_printpointid( fp, string, qh hull_dim, point, id); +} /* printpoint */ + +void qh_printpointid(FILE *fp, const char *string, int dim, pointT *point, int id) { + int k; + realT r; /*bug fix*/ + + if (!point) + return; + if (string) { + qh_fprintf(fp, 9211, "%s", string); + if (id != qh_IDunknown && id != qh_IDnone) + qh_fprintf(fp, 9212, " p%d: ", id); + } + for (k=dim; k--; ) { + r= *point++; + if (string) + qh_fprintf(fp, 9213, " %8.4g", r); + else + qh_fprintf(fp, 9214, qh_REAL_1, r); + } + qh_fprintf(fp, 9215, "\n"); +} /* printpointid */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printpoint3">-</a> + + qh_printpoint3( fp, point ) + prints 2-d, 3-d, or 4-d point as Geomview 3-d coordinates +*/ +void qh_printpoint3(FILE *fp, pointT *point) { + int k; + realT p[4]; + + qh_projectdim3(point, p); + for (k=0; k < 3; k++) + qh_fprintf(fp, 9216, "%8.4g ", p[k]); + qh_fprintf(fp, 9217, " # p%d\n", qh_pointid(point)); +} /* printpoint3 */ + +/*---------------------------------------- +-printpoints- print pointids for a set of points starting at index + see geom.c +*/ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printpoints_out">-</a> + + qh_printpoints_out( fp, facetlist, facets, printall ) + prints vertices, coplanar/inside points, for facets by their point coordinates + allows qh.CDDoutput + + notes: + same format as qhull input + if no coplanar/interior points, + same order as qh_printextremes +*/ +void qh_printpoints_out(FILE *fp, facetT *facetlist, setT *facets, boolT printall) { + int allpoints= qh num_points + qh_setsize(qh other_points); + int numpoints=0, point_i, point_n; + setT *vertices, *points; + facetT *facet, **facetp; + pointT *point, **pointp; + vertexT *vertex, **vertexp; + int id; + + points= qh_settemp(allpoints); + qh_setzero(points, 0, allpoints); + vertices= qh_facetvertices(facetlist, facets, printall); + FOREACHvertex_(vertices) { + id= qh_pointid(vertex->point); + if (id >= 0) + SETelem_(points, id)= vertex->point; + } + if (qh KEEPinside || qh KEEPcoplanar || qh KEEPnearinside) { + FORALLfacet_(facetlist) { + if (!printall && qh_skipfacet(facet)) + continue; + FOREACHpoint_(facet->coplanarset) { + id= qh_pointid(point); + if (id >= 0) + SETelem_(points, id)= point; + } + } + FOREACHfacet_(facets) { + if (!printall && qh_skipfacet(facet)) + continue; + FOREACHpoint_(facet->coplanarset) { + id= qh_pointid(point); + if (id >= 0) + SETelem_(points, id)= point; + } + } + } + qh_settempfree(&vertices); + FOREACHpoint_i_(points) { + if (point) + numpoints++; + } + if (qh CDDoutput) + qh_fprintf(fp, 9218, "%s | %s\nbegin\n%d %d real\n", qh rbox_command, + qh qhull_command, numpoints, qh hull_dim + 1); + else + qh_fprintf(fp, 9219, "%d\n%d\n", qh hull_dim, numpoints); + FOREACHpoint_i_(points) { + if (point) { + if (qh CDDoutput) + qh_fprintf(fp, 9220, "1 "); + qh_printpoint(fp, NULL, point); + } + } + if (qh CDDoutput) + qh_fprintf(fp, 9221, "end\n"); + qh_settempfree(&points); +} /* printpoints_out */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printpointvect">-</a> + + qh_printpointvect( fp, point, normal, center, radius, color ) + prints a 2-d, 3-d, or 4-d point as 3-d VECT's relative to normal or to center point +*/ +void qh_printpointvect(FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius, realT color[3]) { + realT diff[4], pointA[4]; + int k; + + for (k=qh hull_dim; k--; ) { + if (center) + diff[k]= point[k]-center[k]; + else if (normal) + diff[k]= normal[k]; + else + diff[k]= 0; + } + if (center) + qh_normalize2(diff, qh hull_dim, True, NULL, NULL); + for (k=qh hull_dim; k--; ) + pointA[k]= point[k]+diff[k] * radius; + qh_printline3geom(fp, point, pointA, color); +} /* printpointvect */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printpointvect2">-</a> + + qh_printpointvect2( fp, point, normal, center, radius ) + prints a 2-d, 3-d, or 4-d point as 2 3-d VECT's for an imprecise point +*/ +void qh_printpointvect2(FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius) { + realT red[3]={1, 0, 0}, yellow[3]={1, 1, 0}; + + qh_printpointvect(fp, point, normal, center, radius, red); + qh_printpointvect(fp, point, normal, center, -radius, yellow); +} /* printpointvect2 */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printridge">-</a> + + qh_printridge( fp, ridge ) + prints the information in a ridge + + notes: + for qh_printfacetridges() + same as operator<< [QhullRidge.cpp] +*/ +void qh_printridge(FILE *fp, ridgeT *ridge) { + + qh_fprintf(fp, 9222, " - r%d", ridge->id); + if (ridge->tested) + qh_fprintf(fp, 9223, " tested"); + if (ridge->nonconvex) + qh_fprintf(fp, 9224, " nonconvex"); + qh_fprintf(fp, 9225, "\n"); + qh_printvertices(fp, " vertices:", ridge->vertices); + if (ridge->top && ridge->bottom) + qh_fprintf(fp, 9226, " between f%d and f%d\n", + ridge->top->id, ridge->bottom->id); +} /* printridge */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printspheres">-</a> + + qh_printspheres( fp, vertices, radius ) + prints 3-d vertices as OFF spheres + + notes: + inflated octahedron from Stuart Levy earth/mksphere2 +*/ +void qh_printspheres(FILE *fp, setT *vertices, realT radius) { + vertexT *vertex, **vertexp; + + qh printoutnum++; + qh_fprintf(fp, 9227, "{appearance {-edge -normal normscale 0} {\n\ +INST geom {define vsphere OFF\n\ +18 32 48\n\ +\n\ +0 0 1\n\ +1 0 0\n\ +0 1 0\n\ +-1 0 0\n\ +0 -1 0\n\ +0 0 -1\n\ +0.707107 0 0.707107\n\ +0 -0.707107 0.707107\n\ +0.707107 -0.707107 0\n\ +-0.707107 0 0.707107\n\ +-0.707107 -0.707107 0\n\ +0 0.707107 0.707107\n\ +-0.707107 0.707107 0\n\ +0.707107 0.707107 0\n\ +0.707107 0 -0.707107\n\ +0 0.707107 -0.707107\n\ +-0.707107 0 -0.707107\n\ +0 -0.707107 -0.707107\n\ +\n\ +3 0 6 11\n\ +3 0 7 6 \n\ +3 0 9 7 \n\ +3 0 11 9\n\ +3 1 6 8 \n\ +3 1 8 14\n\ +3 1 13 6\n\ +3 1 14 13\n\ +3 2 11 13\n\ +3 2 12 11\n\ +3 2 13 15\n\ +3 2 15 12\n\ +3 3 9 12\n\ +3 3 10 9\n\ +3 3 12 16\n\ +3 3 16 10\n\ +3 4 7 10\n\ +3 4 8 7\n\ +3 4 10 17\n\ +3 4 17 8\n\ +3 5 14 17\n\ +3 5 15 14\n\ +3 5 16 15\n\ +3 5 17 16\n\ +3 6 13 11\n\ +3 7 8 6\n\ +3 9 10 7\n\ +3 11 12 9\n\ +3 14 8 17\n\ +3 15 13 14\n\ +3 16 12 15\n\ +3 17 10 16\n} transforms { TLIST\n"); + FOREACHvertex_(vertices) { + qh_fprintf(fp, 9228, "%8.4g 0 0 0 # v%d\n 0 %8.4g 0 0\n0 0 %8.4g 0\n", + radius, vertex->id, radius, radius); + qh_printpoint3(fp, vertex->point); + qh_fprintf(fp, 9229, "1\n"); + } + qh_fprintf(fp, 9230, "}}}\n"); +} /* printspheres */ + + +/*---------------------------------------------- +-printsummary- + see libqhull.c +*/ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printvdiagram">-</a> + + qh_printvdiagram( fp, format, facetlist, facets, printall ) + print voronoi diagram + # of pairs of input sites + #indices site1 site2 vertex1 ... + + sites indexed by input point id + point 0 is the first input point + vertices indexed by 'o' and 'p' order + vertex 0 is the 'vertex-at-infinity' + vertex 1 is the first Voronoi vertex + + see: + qh_printvoronoi() + qh_eachvoronoi_all() + + notes: + if all facets are upperdelaunay, + prints upper hull (furthest-site Voronoi diagram) +*/ +void qh_printvdiagram(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) { + setT *vertices; + int totcount, numcenters; + boolT isLower; + qh_RIDGE innerouter= qh_RIDGEall; + printvridgeT printvridge= NULL; + + if (format == qh_PRINTvertices) { + innerouter= qh_RIDGEall; + printvridge= qh_printvridge; + }else if (format == qh_PRINTinner) { + innerouter= qh_RIDGEinner; + printvridge= qh_printvnorm; + }else if (format == qh_PRINTouter) { + innerouter= qh_RIDGEouter; + printvridge= qh_printvnorm; + }else { + qh_fprintf(qh ferr, 6219, "Qhull internal error (qh_printvdiagram): unknown print format %d.\n", format); + qh_errexit(qh_ERRinput, NULL, NULL); + } + vertices= qh_markvoronoi(facetlist, facets, printall, &isLower, &numcenters); + totcount= qh_printvdiagram2(NULL, NULL, vertices, innerouter, False); + qh_fprintf(fp, 9231, "%d\n", totcount); + totcount= qh_printvdiagram2(fp, printvridge, vertices, innerouter, True /* inorder*/); + qh_settempfree(&vertices); +#if 0 /* for testing qh_eachvoronoi_all */ + qh_fprintf(fp, 9232, "\n"); + totcount= qh_eachvoronoi_all(fp, printvridge, qh UPPERdelaunay, innerouter, True /* inorder*/); + qh_fprintf(fp, 9233, "%d\n", totcount); +#endif +} /* printvdiagram */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printvdiagram2">-</a> + + qh_printvdiagram2( fp, printvridge, vertices, innerouter, inorder ) + visit all pairs of input sites (vertices) for selected Voronoi vertices + vertices may include NULLs + + innerouter: + qh_RIDGEall print inner ridges(bounded) and outer ridges(unbounded) + qh_RIDGEinner print only inner ridges + qh_RIDGEouter print only outer ridges + + inorder: + print 3-d Voronoi vertices in order + + assumes: + qh_markvoronoi marked facet->visitid for Voronoi vertices + all facet->seen= False + all facet->seen2= True + + returns: + total number of Voronoi ridges + if printvridge, + calls printvridge( fp, vertex, vertexA, centers) for each ridge + [see qh_eachvoronoi()] + + see: + qh_eachvoronoi_all() +*/ +int qh_printvdiagram2(FILE *fp, printvridgeT printvridge, setT *vertices, qh_RIDGE innerouter, boolT inorder) { + int totcount= 0; + int vertex_i, vertex_n; + vertexT *vertex; + + FORALLvertices + vertex->seen= False; + FOREACHvertex_i_(vertices) { + if (vertex) { + if (qh GOODvertex > 0 && qh_pointid(vertex->point)+1 != qh GOODvertex) + continue; + totcount += qh_eachvoronoi(fp, printvridge, vertex, !qh_ALL, innerouter, inorder); + } + } + return totcount; +} /* printvdiagram2 */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printvertex">-</a> + + qh_printvertex( fp, vertex ) + prints the information in a vertex + Duplicated as operator<< [QhullVertex.cpp] +*/ +void qh_printvertex(FILE *fp, vertexT *vertex) { + pointT *point; + int k, count= 0; + facetT *neighbor, **neighborp; + realT r; /*bug fix*/ + + if (!vertex) { + qh_fprintf(fp, 9234, " NULLvertex\n"); + return; + } + qh_fprintf(fp, 9235, "- p%d(v%d):", qh_pointid(vertex->point), vertex->id); + point= vertex->point; + if (point) { + for (k=qh hull_dim; k--; ) { + r= *point++; + qh_fprintf(fp, 9236, " %5.2g", r); + } + } + if (vertex->deleted) + qh_fprintf(fp, 9237, " deleted"); + if (vertex->delridge) + qh_fprintf(fp, 9238, " ridgedeleted"); + qh_fprintf(fp, 9239, "\n"); + if (vertex->neighbors) { + qh_fprintf(fp, 9240, " neighbors:"); + FOREACHneighbor_(vertex) { + if (++count % 100 == 0) + qh_fprintf(fp, 9241, "\n "); + qh_fprintf(fp, 9242, " f%d", neighbor->id); + } + qh_fprintf(fp, 9243, "\n"); + } +} /* printvertex */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printvertexlist">-</a> + + qh_printvertexlist( fp, string, facetlist, facets, printall ) + prints vertices used by a facetlist or facet set + tests qh_skipfacet() if !printall +*/ +void qh_printvertexlist(FILE *fp, const char* string, facetT *facetlist, + setT *facets, boolT printall) { + vertexT *vertex, **vertexp; + setT *vertices; + + vertices= qh_facetvertices(facetlist, facets, printall); + qh_fprintf(fp, 9244, "%s", string); + FOREACHvertex_(vertices) + qh_printvertex(fp, vertex); + qh_settempfree(&vertices); +} /* printvertexlist */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printvertices">-</a> + + qh_printvertices( fp, string, vertices ) + prints vertices in a set + duplicated as printVertexSet [QhullVertex.cpp] +*/ +void qh_printvertices(FILE *fp, const char* string, setT *vertices) { + vertexT *vertex, **vertexp; + + qh_fprintf(fp, 9245, "%s", string); + FOREACHvertex_(vertices) + qh_fprintf(fp, 9246, " p%d(v%d)", qh_pointid(vertex->point), vertex->id); + qh_fprintf(fp, 9247, "\n"); +} /* printvertices */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printvneighbors">-</a> + + qh_printvneighbors( fp, facetlist, facets, printall ) + print vertex neighbors of vertices in facetlist and facets ('FN') + + notes: + qh_countfacets clears facet->visitid for non-printed facets + + design: + collect facet count and related statistics + if necessary, build neighbor sets for each vertex + collect vertices in facetlist and facets + build a point array for point->vertex and point->coplanar facet + for each point + list vertex neighbors or coplanar facet +*/ +void qh_printvneighbors(FILE *fp, facetT* facetlist, setT *facets, boolT printall) { + int numfacets, numsimplicial, numridges, totneighbors, numneighbors, numcoplanars, numtricoplanars; + setT *vertices, *vertex_points, *coplanar_points; + int numpoints= qh num_points + qh_setsize(qh other_points); + vertexT *vertex, **vertexp; + int vertex_i, vertex_n; + facetT *facet, **facetp, *neighbor, **neighborp; + pointT *point, **pointp; + + qh_countfacets(facetlist, facets, printall, &numfacets, &numsimplicial, + &totneighbors, &numridges, &numcoplanars, &numtricoplanars); /* sets facet->visitid */ + qh_fprintf(fp, 9248, "%d\n", numpoints); + qh_vertexneighbors(); + vertices= qh_facetvertices(facetlist, facets, printall); + vertex_points= qh_settemp(numpoints); + coplanar_points= qh_settemp(numpoints); + qh_setzero(vertex_points, 0, numpoints); + qh_setzero(coplanar_points, 0, numpoints); + FOREACHvertex_(vertices) + qh_point_add(vertex_points, vertex->point, vertex); + FORALLfacet_(facetlist) { + FOREACHpoint_(facet->coplanarset) + qh_point_add(coplanar_points, point, facet); + } + FOREACHfacet_(facets) { + FOREACHpoint_(facet->coplanarset) + qh_point_add(coplanar_points, point, facet); + } + FOREACHvertex_i_(vertex_points) { + if (vertex) { + numneighbors= qh_setsize(vertex->neighbors); + qh_fprintf(fp, 9249, "%d", numneighbors); + if (qh hull_dim == 3) + qh_order_vertexneighbors(vertex); + else if (qh hull_dim >= 4) + qsort(SETaddr_(vertex->neighbors, facetT), (size_t)numneighbors, + sizeof(facetT *), qh_compare_facetvisit); + FOREACHneighbor_(vertex) + qh_fprintf(fp, 9250, " %d", + neighbor->visitid ? neighbor->visitid - 1 : 0 - neighbor->id); + qh_fprintf(fp, 9251, "\n"); + }else if ((facet= SETelemt_(coplanar_points, vertex_i, facetT))) + qh_fprintf(fp, 9252, "1 %d\n", + facet->visitid ? facet->visitid - 1 : 0 - facet->id); + else + qh_fprintf(fp, 9253, "0\n"); + } + qh_settempfree(&coplanar_points); + qh_settempfree(&vertex_points); + qh_settempfree(&vertices); +} /* printvneighbors */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printvoronoi">-</a> + + qh_printvoronoi( fp, format, facetlist, facets, printall ) + print voronoi diagram in 'o' or 'G' format + for 'o' format + prints voronoi centers for each facet and for infinity + for each vertex, lists ids of printed facets or infinity + assumes facetlist and facets are disjoint + for 'G' format + prints an OFF object + adds a 0 coordinate to center + prints infinity but does not list in vertices + + see: + qh_printvdiagram() + + notes: + if 'o', + prints a line for each point except "at-infinity" + if all facets are upperdelaunay, + reverses lower and upper hull +*/ +void qh_printvoronoi(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) { + int k, numcenters, numvertices= 0, numneighbors, numinf, vid=1, vertex_i, vertex_n; + facetT *facet, **facetp, *neighbor, **neighborp; + setT *vertices; + vertexT *vertex; + boolT isLower; + unsigned int numfacets= (unsigned int) qh num_facets; + + vertices= qh_markvoronoi(facetlist, facets, printall, &isLower, &numcenters); + FOREACHvertex_i_(vertices) { + if (vertex) { + numvertices++; + numneighbors = numinf = 0; + FOREACHneighbor_(vertex) { + if (neighbor->visitid == 0) + numinf= 1; + else if (neighbor->visitid < numfacets) + numneighbors++; + } + if (numinf && !numneighbors) { + SETelem_(vertices, vertex_i)= NULL; + numvertices--; + } + } + } + if (format == qh_PRINTgeom) + qh_fprintf(fp, 9254, "{appearance {+edge -face} OFF %d %d 1 # Voronoi centers and cells\n", + numcenters, numvertices); + else + qh_fprintf(fp, 9255, "%d\n%d %d 1\n", qh hull_dim-1, numcenters, qh_setsize(vertices)); + if (format == qh_PRINTgeom) { + for (k=qh hull_dim-1; k--; ) + qh_fprintf(fp, 9256, qh_REAL_1, 0.0); + qh_fprintf(fp, 9257, " 0 # infinity not used\n"); + }else { + for (k=qh hull_dim-1; k--; ) + qh_fprintf(fp, 9258, qh_REAL_1, qh_INFINITE); + qh_fprintf(fp, 9259, "\n"); + } + FORALLfacet_(facetlist) { + if (facet->visitid && facet->visitid < numfacets) { + if (format == qh_PRINTgeom) + qh_fprintf(fp, 9260, "# %d f%d\n", vid++, facet->id); + qh_printcenter(fp, format, NULL, facet); + } + } + FOREACHfacet_(facets) { + if (facet->visitid && facet->visitid < numfacets) { + if (format == qh_PRINTgeom) + qh_fprintf(fp, 9261, "# %d f%d\n", vid++, facet->id); + qh_printcenter(fp, format, NULL, facet); + } + } + FOREACHvertex_i_(vertices) { + numneighbors= 0; + numinf=0; + if (vertex) { + if (qh hull_dim == 3) + qh_order_vertexneighbors(vertex); + else if (qh hull_dim >= 4) + qsort(SETaddr_(vertex->neighbors, facetT), + (size_t)qh_setsize(vertex->neighbors), + sizeof(facetT *), qh_compare_facetvisit); + FOREACHneighbor_(vertex) { + if (neighbor->visitid == 0) + numinf= 1; + else if (neighbor->visitid < numfacets) + numneighbors++; + } + } + if (format == qh_PRINTgeom) { + if (vertex) { + qh_fprintf(fp, 9262, "%d", numneighbors); + FOREACHneighbor_(vertex) { + if (neighbor->visitid && neighbor->visitid < numfacets) + qh_fprintf(fp, 9263, " %d", neighbor->visitid); + } + qh_fprintf(fp, 9264, " # p%d(v%d)\n", vertex_i, vertex->id); + }else + qh_fprintf(fp, 9265, " # p%d is coplanar or isolated\n", vertex_i); + }else { + if (numinf) + numneighbors++; + qh_fprintf(fp, 9266, "%d", numneighbors); + if (vertex) { + FOREACHneighbor_(vertex) { + if (neighbor->visitid == 0) { + if (numinf) { + numinf= 0; + qh_fprintf(fp, 9267, " %d", neighbor->visitid); + } + }else if (neighbor->visitid < numfacets) + qh_fprintf(fp, 9268, " %d", neighbor->visitid); + } + } + qh_fprintf(fp, 9269, "\n"); + } + } + if (format == qh_PRINTgeom) + qh_fprintf(fp, 9270, "}\n"); + qh_settempfree(&vertices); +} /* printvoronoi */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printvnorm">-</a> + + qh_printvnorm( fp, vertex, vertexA, centers, unbounded ) + print one separating plane of the Voronoi diagram for a pair of input sites + unbounded==True if centers includes vertex-at-infinity + + assumes: + qh_ASvoronoi and qh_vertexneighbors() already set + + note: + parameter unbounded is UNUSED by this callback + + see: + qh_printvdiagram() + qh_eachvoronoi() +*/ +void qh_printvnorm(FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded) { + pointT *normal; + realT offset; + int k; + QHULL_UNUSED(unbounded); + + normal= qh_detvnorm(vertex, vertexA, centers, &offset); + qh_fprintf(fp, 9271, "%d %d %d ", + 2+qh hull_dim, qh_pointid(vertex->point), qh_pointid(vertexA->point)); + for (k=0; k< qh hull_dim-1; k++) + qh_fprintf(fp, 9272, qh_REAL_1, normal[k]); + qh_fprintf(fp, 9273, qh_REAL_1, offset); + qh_fprintf(fp, 9274, "\n"); +} /* printvnorm */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printvridge">-</a> + + qh_printvridge( fp, vertex, vertexA, centers, unbounded ) + print one ridge of the Voronoi diagram for a pair of input sites + unbounded==True if centers includes vertex-at-infinity + + see: + qh_printvdiagram() + + notes: + the user may use a different function + parameter unbounded is UNUSED +*/ +void qh_printvridge(FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded) { + facetT *facet, **facetp; + QHULL_UNUSED(unbounded); + + qh_fprintf(fp, 9275, "%d %d %d", qh_setsize(centers)+2, + qh_pointid(vertex->point), qh_pointid(vertexA->point)); + FOREACHfacet_(centers) + qh_fprintf(fp, 9276, " %d", facet->visitid); + qh_fprintf(fp, 9277, "\n"); +} /* printvridge */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="projectdim3">-</a> + + qh_projectdim3( source, destination ) + project 2-d 3-d or 4-d point to a 3-d point + uses qh.DROPdim and qh.hull_dim + source and destination may be the same + + notes: + allocate 4 elements to destination just in case +*/ +void qh_projectdim3(pointT *source, pointT *destination) { + int i,k; + + for (k=0, i=0; k < qh hull_dim; k++) { + if (qh hull_dim == 4) { + if (k != qh DROPdim) + destination[i++]= source[k]; + }else if (k == qh DROPdim) + destination[i++]= 0; + else + destination[i++]= source[k]; + } + while (i < 3) + destination[i++]= 0.0; +} /* projectdim3 */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="readfeasible">-</a> + + qh_readfeasible( 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(int dim, const char *curline) { + boolT isfirst= True; + int linecount= 0, tokcount= 0; + const char *s; + char *t, firstline[qh_MAXfirst+1]; + coordT *coords, value; + + if (!qh HALFspace) { + qh_fprintf(qh ferr, 6070, "qhull input error: feasible point(dim 1 coords) is only valid for halfspace intersection\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } + if (qh feasible_string) + qh_fprintf(qh ferr, 7057, "qhull input warning: feasible point(dim 1 coords) overrides 'Hn,n,n' feasible point for halfspace intersection\n"); + if (!(qh feasible_point= (coordT*)qh_malloc(dim* sizeof(coordT)))) { + qh_fprintf(qh ferr, 6071, "qhull error: insufficient memory for feasible point\n"); + qh_errexit(qh_ERRmem, NULL, NULL); + } + coords= qh feasible_point; + while ((s= (isfirst ? curline : fgets(firstline, qh_MAXfirst, qh fin)))) { + if (isfirst) + isfirst= False; + else + linecount++; + while (*s) { + while (isspace(*s)) + s++; + value= qh_strtod(s, &t); + if (s == t) + break; + s= t; + *(coords++)= value; + if (++tokcount == dim) { + while (isspace(*s)) + s++; + qh_strtod(s, &t); + if (s != t) { + qh_fprintf(qh ferr, 6072, "qhull input error: coordinates for feasible point do not finish out the line: %s\n", + s); + qh_errexit(qh_ERRinput, NULL, NULL); + } + return linecount; + } + } + } + qh_fprintf(qh ferr, 6073, "qhull input error: only %d coordinates. Could not read %d-d feasible point.\n", + tokcount, dim); + qh_errexit(qh_ERRinput, NULL, NULL); + return 0; +} /* readfeasible */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="readpoints">-</a> + + qh_readpoints( numpoints, dimension, ismalloc ) + read points from qh.fin into qh.first_point, qh.num_points + qh.fin is lines of coordinates, one per vertex, first line number of points + if 'rbox D4', + gives message + if qh.ATinfinity, + adds point-at-infinity for Delaunay triangulations + + returns: + number of points, array of point coordinates, dimension, ismalloc True + if qh.DELAUNAY & !qh.PROJECTinput, projects points to paraboloid + and clears qh.PROJECTdelaunay + if qh.HALFspace, reads optional feasible point, reads halfspaces, + converts to dual. + + for feasible point in "cdd format" in 3-d: + 3 1 + coordinates + comments + begin + n 4 real/integer + ... + end + + notes: + dimension will change in qh_initqhull_globals if qh.PROJECTinput + uses malloc() since qh_mem not initialized + FIXUP QH11012: qh_readpoints needs rewriting, too long +*/ +coordT *qh_readpoints(int *numpoints, int *dimension, boolT *ismalloc) { + coordT *points, *coords, *infinity= NULL; + realT paraboloid, maxboloid= -REALmax, value; + realT *coordp= NULL, *offsetp= NULL, *normalp= NULL; + char *s= 0, *t, firstline[qh_MAXfirst+1]; + int diminput=0, numinput=0, dimfeasible= 0, newnum, k, tempi; + int firsttext=0, firstshort=0, firstlong=0, firstpoint=0; + int tokcount= 0, linecount=0, maxcount, coordcount=0; + boolT islong, isfirst= True, wasbegin= False; + boolT isdelaunay= qh DELAUNAY && !qh PROJECTinput; + + if (qh CDDinput) { + while ((s= fgets(firstline, qh_MAXfirst, qh fin))) { + linecount++; + if (qh HALFspace && linecount == 1 && isdigit(*s)) { + dimfeasible= qh_strtol(s, &s); + while (isspace(*s)) + s++; + if (qh_strtol(s, &s) == 1) + linecount += qh_readfeasible(dimfeasible, s); + else + dimfeasible= 0; + }else if (!memcmp(firstline, "begin", (size_t)5) || !memcmp(firstline, "BEGIN", (size_t)5)) + break; + else if (!*qh rbox_command) + strncat(qh rbox_command, s, sizeof(qh rbox_command)-1); + } + if (!s) { + qh_fprintf(qh ferr, 6074, "qhull input error: missing \"begin\" for cdd-formated input\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } + } + while (!numinput && (s= fgets(firstline, qh_MAXfirst, qh fin))) { + linecount++; + if (!memcmp(s, "begin", (size_t)5) || !memcmp(s, "BEGIN", (size_t)5)) + wasbegin= True; + while (*s) { + while (isspace(*s)) + s++; + if (!*s) + break; + if (!isdigit(*s)) { + if (!*qh rbox_command) { + strncat(qh rbox_command, s, sizeof(qh rbox_command)-1); + firsttext= linecount; + } + break; + } + if (!diminput) + diminput= qh_strtol(s, &s); + else { + numinput= qh_strtol(s, &s); + if (numinput == 1 && diminput >= 2 && qh HALFspace && !qh CDDinput) { + linecount += qh_readfeasible(diminput, s); /* checks if ok */ + dimfeasible= diminput; + diminput= numinput= 0; + }else + break; + } + } + } + if (!s) { + qh_fprintf(qh ferr, 6075, "qhull input error: short input file. Did not find dimension and number of points\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } + if (diminput > numinput) { + tempi= diminput; /* exchange dim and n, e.g., for cdd input format */ + diminput= numinput; + numinput= tempi; + } + if (diminput < 2) { + qh_fprintf(qh ferr, 6220,"qhull input error: dimension %d(first number) should be at least 2\n", + diminput); + qh_errexit(qh_ERRinput, NULL, NULL); + } + if (isdelaunay) { + qh PROJECTdelaunay= False; + if (qh CDDinput) + *dimension= diminput; + else + *dimension= diminput+1; + *numpoints= numinput; + if (qh ATinfinity) + (*numpoints)++; + }else if (qh HALFspace) { + *dimension= diminput - 1; + *numpoints= numinput; + if (diminput < 3) { + qh_fprintf(qh ferr, 6221,"qhull input error: dimension %d(first number, includes offset) should be at least 3 for halfspaces\n", + diminput); + qh_errexit(qh_ERRinput, NULL, NULL); + } + if (dimfeasible) { + if (dimfeasible != *dimension) { + qh_fprintf(qh ferr, 6222,"qhull input error: dimension %d of feasible point is not one less than dimension %d for halfspaces\n", + dimfeasible, diminput); + qh_errexit(qh_ERRinput, NULL, NULL); + } + }else + qh_setfeasible(*dimension); + }else { + if (qh CDDinput) + *dimension= diminput-1; + else + *dimension= diminput; + *numpoints= numinput; + } + qh normal_size= *dimension * sizeof(coordT); /* for tracing with qh_printpoint */ + if (qh HALFspace) { + qh half_space= coordp= (coordT*)qh_malloc(qh normal_size + sizeof(coordT)); + if (qh CDDinput) { + offsetp= qh half_space; + normalp= offsetp + 1; + }else { + normalp= qh half_space; + offsetp= normalp + *dimension; + } + } + qh maxline= diminput * (qh_REALdigits + 5); + maximize_(qh maxline, 500); + qh line= (char*)qh_malloc((qh maxline+1) * sizeof(char)); + *ismalloc= True; /* use malloc since memory not setup */ + coords= points= qh temp_malloc= /* numinput and diminput >=2 by QH6220 */ + (coordT*)qh_malloc((*numpoints)*(*dimension)*sizeof(coordT)); + if (!coords || !qh line || (qh HALFspace && !qh half_space)) { + qh_fprintf(qh ferr, 6076, "qhull error: insufficient memory to read %d points\n", + numinput); + qh_errexit(qh_ERRmem, NULL, NULL); + } + if (isdelaunay && qh ATinfinity) { + infinity= points + numinput * (*dimension); + for (k= (*dimension) - 1; k--; ) + infinity[k]= 0.0; + } + maxcount= numinput * diminput; + paraboloid= 0.0; + while ((s= (isfirst ? s : fgets(qh line, qh maxline, qh fin)))) { + if (!isfirst) { + linecount++; + if (*s == 'e' || *s == 'E') { + if (!memcmp(s, "end", (size_t)3) || !memcmp(s, "END", (size_t)3)) { + if (qh CDDinput ) + break; + else if (wasbegin) + qh_fprintf(qh ferr, 7058, "qhull input warning: the input appears to be in cdd format. If so, use 'Fd'\n"); + } + } + } + islong= False; + while (*s) { + while (isspace(*s)) + s++; + value= qh_strtod(s, &t); + if (s == t) { + if (!*qh rbox_command) + strncat(qh rbox_command, s, sizeof(qh rbox_command)-1); + if (*s && !firsttext) + firsttext= linecount; + if (!islong && !firstshort && coordcount) + firstshort= linecount; + break; + } + if (!firstpoint) + firstpoint= linecount; + s= t; + if (++tokcount > maxcount) + continue; + if (qh HALFspace) { + if (qh CDDinput) + *(coordp++)= -value; /* both coefficients and offset */ + else + *(coordp++)= value; + }else { + *(coords++)= value; + if (qh CDDinput && !coordcount) { + if (value != 1.0) { + qh_fprintf(qh ferr, 6077, "qhull input error: for cdd format, point at line %d does not start with '1'\n", + linecount); + qh_errexit(qh_ERRinput, NULL, NULL); + } + coords--; + }else if (isdelaunay) { + paraboloid += value * value; + if (qh ATinfinity) { + if (qh CDDinput) + infinity[coordcount-1] += value; + else + infinity[coordcount] += value; + } + } + } + if (++coordcount == diminput) { + coordcount= 0; + if (isdelaunay) { + *(coords++)= paraboloid; + maximize_(maxboloid, paraboloid); + paraboloid= 0.0; + }else if (qh HALFspace) { + if (!qh_sethalfspace(*dimension, coords, &coords, normalp, offsetp, qh feasible_point)) { + qh_fprintf(qh ferr, 8048, "The halfspace was on line %d\n", linecount); + if (wasbegin) + qh_fprintf(qh ferr, 8049, "The input appears to be in cdd format. If so, you should use option 'Fd'\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } + coordp= qh half_space; + } + while (isspace(*s)) + s++; + if (*s) { + islong= True; + if (!firstlong) + firstlong= linecount; + } + } + } + if (!islong && !firstshort && coordcount) + firstshort= linecount; + if (!isfirst && s - qh line >= qh maxline) { + qh_fprintf(qh ferr, 6078, "qhull input error: line %d contained more than %d characters\n", + linecount, (int) (s - qh line)); /* WARN64 */ + qh_errexit(qh_ERRinput, NULL, NULL); + } + isfirst= False; + } + if (tokcount != maxcount) { + newnum= fmin_(numinput, tokcount/diminput); + qh_fprintf(qh ferr, 7073,"\ +qhull warning: instead of %d %d-dimensional points, input contains\n\ +%d points and %d extra coordinates. Line %d is the first\npoint", + numinput, diminput, tokcount/diminput, tokcount % diminput, firstpoint); + if (firsttext) + qh_fprintf(qh ferr, 8051, ", line %d is the first comment", firsttext); + if (firstshort) + qh_fprintf(qh ferr, 8052, ", line %d is the first short\nline", firstshort); + if (firstlong) + qh_fprintf(qh ferr, 8053, ", line %d is the first long line", firstlong); + qh_fprintf(qh ferr, 8054, ". Continue with %d points.\n", newnum); + numinput= newnum; + if (isdelaunay && qh ATinfinity) { + for (k= tokcount % diminput; k--; ) + infinity[k] -= *(--coords); + *numpoints= newnum+1; + }else { + coords -= tokcount % diminput; + *numpoints= newnum; + } + } + if (isdelaunay && qh ATinfinity) { + for (k= (*dimension) -1; k--; ) + infinity[k] /= numinput; + if (coords == infinity) + coords += (*dimension) -1; + else { + for (k=0; k < (*dimension) -1; k++) + *(coords++)= infinity[k]; + } + *(coords++)= maxboloid * 1.1; + } + if (qh rbox_command[0]) { + qh rbox_command[strlen(qh rbox_command)-1]= '\0'; + if (!strcmp(qh rbox_command, "./rbox D4")) + qh_fprintf(qh ferr, 8055, "\n\ +This is the qhull test case. If any errors or core dumps occur,\n\ +recompile qhull with 'make new'. If errors still occur, there is\n\ +an incompatibility. You should try a different compiler. You can also\n\ +change the choices in user.h. If you discover the source of the problem,\n\ +please send mail to qhull_bug@qhull.org.\n\ +\n\ +Type 'qhull' for a short list of options.\n"); + } + qh_free(qh line); + qh line= NULL; + if (qh half_space) { + qh_free(qh half_space); + qh half_space= NULL; + } + qh temp_malloc= NULL; + trace1((qh ferr, 1008,"qh_readpoints: read in %d %d-dimensional points\n", + numinput, diminput)); + return(points); +} /* readpoints */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="setfeasible">-</a> + + qh_setfeasible( 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(int dim) { + int tokcount= 0; + char *s; + coordT *coords, value; + + if (!(s= qh feasible_string)) { + qh_fprintf(qh ferr, 6223, "\ +qhull input error: halfspace intersection needs a feasible point.\n\ +Either prepend the input with 1 point or use 'Hn,n,n'. See manual.\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } + if (!(qh feasible_point= (pointT*)qh_malloc(dim * sizeof(coordT)))) { + qh_fprintf(qh ferr, 6079, "qhull error: insufficient memory for 'Hn,n,n'\n"); + qh_errexit(qh_ERRmem, NULL, NULL); + } + coords= qh feasible_point; + while (*s) { + value= qh_strtod(s, &s); + if (++tokcount > dim) { + qh_fprintf(qh ferr, 7059, "qhull input warning: more coordinates for 'H%s' than dimension %d\n", + qh feasible_string, dim); + break; + } + *(coords++)= value; + if (*s) + s++; + } + while (++tokcount <= dim) + *(coords++)= 0.0; +} /* setfeasible */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="skipfacet">-</a> + + qh_skipfacet( facet ) + returns 'True' if this facet is not to be printed + + notes: + based on the user provided slice thresholds and 'good' specifications +*/ +boolT qh_skipfacet(facetT *facet) { + facetT *neighbor, **neighborp; + + if (qh PRINTneighbors) { + if (facet->good) + return !qh PRINTgood; + FOREACHneighbor_(facet) { + if (neighbor->good) + return False; + } + return True; + }else if (qh PRINTgood) + return !facet->good; + else if (!facet->normal) + return True; + return(!qh_inthresholds(facet->normal, NULL)); +} /* skipfacet */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="skipfilename">-</a> + + qh_skipfilename( string ) + returns pointer to character after filename + + notes: + skips leading spaces + ends with spacing or eol + if starts with ' or " ends with the same, skipping \' or \" + For qhull, qh_argv_to_command() only uses double quotes +*/ +char *qh_skipfilename(char *filename) { + char *s= filename; /* non-const due to return */ + char c; + + while (*s && isspace(*s)) + s++; + c= *s++; + if (c == '\0') { + qh_fprintf(qh ferr, 6204, "qhull input error: filename expected, none found.\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } + if (c == '\'' || c == '"') { + while (*s !=c || s[-1] == '\\') { + if (!*s) { + qh_fprintf(qh ferr, 6203, "qhull input error: missing quote after filename -- %s\n", filename); + qh_errexit(qh_ERRinput, NULL, NULL); + } + s++; + } + s++; + } + else while (*s && !isspace(*s)) + s++; + return s; +} /* skipfilename */ + diff --git a/src/qhull/src/libqhull/io.h b/src/qhull/src/libqhull/io.h new file mode 100644 index 000000000..eca0369d3 --- /dev/null +++ b/src/qhull/src/libqhull/io.h @@ -0,0 +1,159 @@ +/*<html><pre> -<a href="qh-io.htm" + >-------------------------------</a><a name="TOP">-</a> + + io.h + declarations of Input/Output functions + + see README, libqhull.h and io.c + + Copyright (c) 1993-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull/io.h#1 $$Change: 1981 $ + $DateTime: 2015/09/28 20:26:32 $$Author: bbarber $ +*/ + +#ifndef qhDEFio +#define qhDEFio 1 + +#include "libqhull.h" + +/*============ constants and flags ==================*/ + +/*-<a href="qh-io.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.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.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.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.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.htm#TOC" + >--------------------------------</a><a name="printvridgeT">-</a> + + printvridgeT + prints results of qh_printvdiagram + + see: + <a href="io.c#printvridge">qh_printvridge</a> for an example +*/ +typedef void (*printvridgeT)(FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded); + +/*============== -prototypes in alphabetical order =========*/ + +void qh_dfacet(unsigned id); +void qh_dvertex(unsigned id); +int qh_compare_facetarea(const void *p1, const void *p2); +int qh_compare_facetmerge(const void *p1, const void *p2); +int qh_compare_facetvisit(const void *p1, const void *p2); +int qh_compare_vertexpoint(const void *p1, const void *p2); /* not used, not in libqhull_r.h */ +void qh_copyfilename(char *filename, int size, const char* source, int length); +void qh_countfacets(facetT *facetlist, setT *facets, boolT printall, + int *numfacetsp, int *numsimplicialp, int *totneighborsp, + int *numridgesp, int *numcoplanarsp, int *numnumtricoplanarsp); +pointT *qh_detvnorm(vertexT *vertex, vertexT *vertexA, setT *centers, realT *offsetp); +setT *qh_detvridge(vertexT *vertex); +setT *qh_detvridge3(vertexT *atvertex, vertexT *vertex); +int qh_eachvoronoi(FILE *fp, printvridgeT printvridge, vertexT *atvertex, boolT visitall, qh_RIDGE innerouter, boolT inorder); +int qh_eachvoronoi_all(FILE *fp, printvridgeT printvridge, boolT isUpper, qh_RIDGE innerouter, boolT inorder); +void qh_facet2point(facetT *facet, pointT **point0, pointT **point1, realT *mindist); +setT *qh_facetvertices(facetT *facetlist, setT *facets, boolT allfacets); +void qh_geomplanes(facetT *facet, realT *outerplane, realT *innerplane); +void qh_markkeep(facetT *facetlist); +setT *qh_markvoronoi(facetT *facetlist, setT *facets, boolT printall, boolT *isLowerp, int *numcentersp); +void qh_order_vertexneighbors(vertexT *vertex); +void qh_prepare_output(void); +void qh_printafacet(FILE *fp, qh_PRINT format, facetT *facet, boolT printall); +void qh_printbegin(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall); +void qh_printcenter(FILE *fp, qh_PRINT format, const char *string, facetT *facet); +void qh_printcentrum(FILE *fp, facetT *facet, realT radius); +void qh_printend(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall); +void qh_printend4geom(FILE *fp, facetT *facet, int *num, boolT printall); +void qh_printextremes(FILE *fp, facetT *facetlist, setT *facets, boolT printall); +void qh_printextremes_2d(FILE *fp, facetT *facetlist, setT *facets, boolT printall); +void qh_printextremes_d(FILE *fp, facetT *facetlist, setT *facets, boolT printall); +void qh_printfacet(FILE *fp, facetT *facet); +void qh_printfacet2math(FILE *fp, facetT *facet, qh_PRINT format, int notfirst); +void qh_printfacet2geom(FILE *fp, facetT *facet, realT color[3]); +void qh_printfacet2geom_points(FILE *fp, pointT *point1, pointT *point2, + facetT *facet, realT offset, realT color[3]); +void qh_printfacet3math(FILE *fp, facetT *facet, qh_PRINT format, int notfirst); +void qh_printfacet3geom_nonsimplicial(FILE *fp, facetT *facet, realT color[3]); +void qh_printfacet3geom_points(FILE *fp, setT *points, facetT *facet, realT offset, realT color[3]); +void qh_printfacet3geom_simplicial(FILE *fp, facetT *facet, realT color[3]); +void qh_printfacet3vertex(FILE *fp, facetT *facet, qh_PRINT format); +void qh_printfacet4geom_nonsimplicial(FILE *fp, facetT *facet, realT color[3]); +void qh_printfacet4geom_simplicial(FILE *fp, facetT *facet, realT color[3]); +void qh_printfacetNvertex_nonsimplicial(FILE *fp, facetT *facet, int id, qh_PRINT format); +void qh_printfacetNvertex_simplicial(FILE *fp, facetT *facet, qh_PRINT format); +void qh_printfacetheader(FILE *fp, facetT *facet); +void qh_printfacetridges(FILE *fp, facetT *facet); +void qh_printfacets(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall); +void qh_printhyperplaneintersection(FILE *fp, facetT *facet1, facetT *facet2, + setT *vertices, realT color[3]); +void qh_printneighborhood(FILE *fp, qh_PRINT format, facetT *facetA, facetT *facetB, boolT printall); +void qh_printline3geom(FILE *fp, pointT *pointA, pointT *pointB, realT color[3]); +void qh_printpoint(FILE *fp, const char *string, pointT *point); +void qh_printpointid(FILE *fp, const char *string, int dim, pointT *point, int id); +void qh_printpoint3(FILE *fp, pointT *point); +void qh_printpoints_out(FILE *fp, facetT *facetlist, setT *facets, boolT printall); +void qh_printpointvect(FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius, realT color[3]); +void qh_printpointvect2(FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius); +void qh_printridge(FILE *fp, ridgeT *ridge); +void qh_printspheres(FILE *fp, setT *vertices, realT radius); +void qh_printvdiagram(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall); +int qh_printvdiagram2(FILE *fp, printvridgeT printvridge, setT *vertices, qh_RIDGE innerouter, boolT inorder); +void qh_printvertex(FILE *fp, vertexT *vertex); +void qh_printvertexlist(FILE *fp, const char* string, facetT *facetlist, + setT *facets, boolT printall); +void qh_printvertices(FILE *fp, const char* string, setT *vertices); +void qh_printvneighbors(FILE *fp, facetT* facetlist, setT *facets, boolT printall); +void qh_printvoronoi(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall); +void qh_printvnorm(FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded); +void qh_printvridge(FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded); +void qh_produce_output(void); +void qh_produce_output2(void); +void qh_projectdim3(pointT *source, pointT *destination); +int qh_readfeasible(int dim, const char *curline); +coordT *qh_readpoints(int *numpoints, int *dimension, boolT *ismalloc); +void qh_setfeasible(int dim); +boolT qh_skipfacet(facetT *facet); +char *qh_skipfilename(char *filename); + +#endif /* qhDEFio */ diff --git a/src/qhull/src/libqhull/libqhull.c b/src/qhull/src/libqhull/libqhull.c new file mode 100644 index 000000000..7696a8a9f --- /dev/null +++ b/src/qhull/src/libqhull/libqhull.c @@ -0,0 +1,1403 @@ +/*<html><pre> -<a href="qh-qhull.htm" + >-------------------------------</a><a name="TOP">-</a> + + libqhull.c + Quickhull algorithm for convex hulls + + qhull() and top-level routines + + see qh-qhull.htm, libqhull.h, unix.c + + see qhull_a.h for internal functions + + Copyright (c) 1993-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull/libqhull.c#3 $$Change: 2047 $ + $DateTime: 2016/01/04 22:03:18 $$Author: bbarber $ +*/ + +#include "qhull_a.h" + +/*============= functions in alphabetic order after qhull() =======*/ + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="qhull">-</a> + + qh_qhull() + compute DIM3 convex hull of qh.num_points starting at qh.first_point + qh contains all global options and variables + + returns: + returns polyhedron + qh.facet_list, qh.num_facets, qh.vertex_list, qh.num_vertices, + + returns global variables + qh.hulltime, qh.max_outside, qh.interior_point, qh.max_vertex, qh.min_vertex + + returns precision constants + qh.ANGLEround, centrum_radius, cos_max, DISTround, MAXabs_coord, ONEmerge + + notes: + unless needed for output + qh.max_vertex and qh.min_vertex are max/min due to merges + + see: + to add individual points to either qh.num_points + use qh_addpoint() + + if qh.GETarea + qh_produceoutput() returns qh.totarea and qh.totvol via qh_getarea() + + design: + record starting time + initialize hull and partition points + build convex hull + unless early termination + update facet->maxoutside for vertices, coplanar, and near-inside points + error if temporary sets exist + record end time +*/ + +void qh_qhull(void) { + int numoutside; + + qh hulltime= qh_CPUclock; + if (qh RERUN || qh JOGGLEmax < REALmax/2) + qh_build_withrestart(); + else { + qh_initbuild(); + qh_buildhull(); + } + if (!qh STOPpoint && !qh STOPcone) { + if (qh ZEROall_ok && !qh TESTvneighbors && qh MERGEexact) + qh_checkzero( qh_ALL); + if (qh ZEROall_ok && !qh TESTvneighbors && !qh WAScoplanar) { + trace2((qh ferr, 2055, "qh_qhull: all facets are clearly convex and no coplanar points. Post-merging and check of maxout not needed.\n")); + qh DOcheckmax= False; + }else { + if (qh MERGEexact || (qh hull_dim > qh_DIMreduceBuild && qh PREmerge)) + qh_postmerge("First post-merge", qh premerge_centrum, qh premerge_cos, + (qh POSTmerge ? False : qh TESTvneighbors)); + else if (!qh POSTmerge && qh TESTvneighbors) + qh_postmerge("For testing vertex neighbors", qh premerge_centrum, + qh premerge_cos, True); + if (qh POSTmerge) + qh_postmerge("For post-merging", qh postmerge_centrum, + qh postmerge_cos, qh TESTvneighbors); + if (qh visible_list == qh facet_list) { /* i.e., merging done */ + qh findbestnew= True; + qh_partitionvisible(/*qh.visible_list*/ !qh_ALL, &numoutside); + qh findbestnew= False; + qh_deletevisible(/*qh.visible_list*/); + qh_resetlists(False, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */); + } + } + if (qh DOcheckmax){ + if (qh REPORTfreq) { + qh_buildtracing(NULL, NULL); + qh_fprintf(qh ferr, 8115, "\nTesting all coplanar points.\n"); + } + qh_check_maxout(); + } + if (qh KEEPnearinside && !qh maxoutdone) + qh_nearcoplanar(); + } + if (qh_setsize(qhmem.tempstack) != 0) { + qh_fprintf(qh ferr, 6164, "qhull internal error (qh_qhull): temporary sets not empty(%d)\n", + qh_setsize(qhmem.tempstack)); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + qh hulltime= qh_CPUclock - qh hulltime; + qh QHULLfinished= True; + trace1((qh ferr, 1036, "Qhull: algorithm completed\n")); +} /* qhull */ + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="addpoint">-</a> + + qh_addpoint( furthest, facet, checkdist ) + add point (usually furthest point) above facet to hull + if checkdist, + check that point is above facet. + if point is not outside of the hull, uses qh_partitioncoplanar() + assumes that facet is defined by qh_findbestfacet() + else if facet specified, + assumes that point is above facet (major damage if below) + for Delaunay triangulations, + Use qh_setdelaunay() to lift point to paraboloid and scale by 'Qbb' if needed + Do not use options 'Qbk', 'QBk', or 'QbB' since they scale the coordinates. + + returns: + returns False if user requested an early termination + qh.visible_list, newfacet_list, delvertex_list, NEWfacets may be defined + updates qh.facet_list, qh.num_facets, qh.vertex_list, qh.num_vertices + clear qh.maxoutdone (will need to call qh_check_maxout() for facet->maxoutside) + if unknown point, adds a pointer to qh.other_points + do not deallocate the point's coordinates + + notes: + assumes point is near its best facet and not at a local minimum of a lens + distributions. Use qh_findbestfacet to avoid this case. + uses qh.visible_list, qh.newfacet_list, qh.delvertex_list, qh.NEWfacets + + see also: + qh_triangulate() -- triangulate non-simplicial facets + + design: + add point to other_points if needed + if checkdist + if point not above facet + partition coplanar point + exit + exit if pre STOPpoint requested + find horizon and visible facets for point + make new facets for point to horizon + make hyperplanes for point + compute balance statistics + match neighboring new facets + update vertex neighbors and delete interior vertices + exit if STOPcone requested + merge non-convex new facets + if merge found, many merges, or 'Qf' + use qh_findbestnew() instead of qh_findbest() + partition outside points from visible facets + delete visible facets + check polyhedron if requested + exit if post STOPpoint requested + reset working lists of facets and vertices +*/ +boolT qh_addpoint(pointT *furthest, facetT *facet, boolT checkdist) { + int goodvisible, goodhorizon; + vertexT *vertex; + facetT *newfacet; + realT dist, newbalance, pbalance; + boolT isoutside= False; + int numpart, numpoints, numnew, firstnew; + + qh maxoutdone= False; + if (qh_pointid(furthest) == qh_IDunknown) + qh_setappend(&qh other_points, furthest); + if (!facet) { + qh_fprintf(qh ferr, 6213, "qhull internal error (qh_addpoint): NULL facet. Need to call qh_findbestfacet first\n"); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + if (checkdist) { + facet= qh_findbest(furthest, facet, !qh_ALL, !qh_ISnewfacets, !qh_NOupper, + &dist, &isoutside, &numpart); + zzadd_(Zpartition, numpart); + if (!isoutside) { + zinc_(Znotmax); /* last point of outsideset is no longer furthest. */ + facet->notfurthest= True; + qh_partitioncoplanar(furthest, facet, &dist); + return True; + } + } + qh_buildtracing(furthest, facet); + if (qh STOPpoint < 0 && qh furthest_id == -qh STOPpoint-1) { + facet->notfurthest= True; + return False; + } + qh_findhorizon(furthest, facet, &goodvisible, &goodhorizon); + if (qh ONLYgood && !(goodvisible+goodhorizon) && !qh GOODclosest) { + zinc_(Znotgood); + facet->notfurthest= True; + /* last point of outsideset is no longer furthest. This is ok + since all points of the outside are likely to be bad */ + qh_resetlists(False, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */); + return True; + } + zzinc_(Zprocessed); + firstnew= qh facet_id; + vertex= qh_makenewfacets(furthest /*visible_list, attaches if !ONLYgood */); + qh_makenewplanes(/* newfacet_list */); + numnew= qh facet_id - firstnew; + newbalance= numnew - (realT) (qh num_facets-qh num_visible) + * qh hull_dim/qh num_vertices; + wadd_(Wnewbalance, newbalance); + wadd_(Wnewbalance2, newbalance * newbalance); + if (qh ONLYgood + && !qh_findgood(qh newfacet_list, goodhorizon) && !qh GOODclosest) { + FORALLnew_facets + qh_delfacet(newfacet); + qh_delvertex(vertex); + qh_resetlists(True, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */); + zinc_(Znotgoodnew); + facet->notfurthest= True; + return True; + } + if (qh ONLYgood) + qh_attachnewfacets(/*visible_list*/); + qh_matchnewfacets(); + qh_updatevertices(); + if (qh STOPcone && qh furthest_id == qh STOPcone-1) { + facet->notfurthest= True; + return False; /* visible_list etc. still defined */ + } + qh findbestnew= False; + if (qh PREmerge || qh MERGEexact) { + qh_premerge(vertex, qh premerge_centrum, qh premerge_cos); + if (qh_USEfindbestnew) + qh findbestnew= True; + else { + FORALLnew_facets { + if (!newfacet->simplicial) { + qh findbestnew= True; /* use qh_findbestnew instead of qh_findbest*/ + break; + } + } + } + }else if (qh BESToutside) + qh findbestnew= True; + qh_partitionvisible(/*qh.visible_list*/ !qh_ALL, &numpoints); + qh findbestnew= False; + qh findbest_notsharp= False; + zinc_(Zpbalance); + pbalance= numpoints - (realT) qh hull_dim /* assumes all points extreme */ + * (qh num_points - qh num_vertices)/qh num_vertices; + wadd_(Wpbalance, pbalance); + wadd_(Wpbalance2, pbalance * pbalance); + qh_deletevisible(/*qh.visible_list*/); + zmax_(Zmaxvertex, qh num_vertices); + qh NEWfacets= False; + if (qh IStracing >= 4) { + if (qh num_facets < 2000) + qh_printlists(); + qh_printfacetlist(qh newfacet_list, NULL, True); + qh_checkpolygon(qh facet_list); + }else if (qh CHECKfrequently) { + if (qh num_facets < 50) + qh_checkpolygon(qh facet_list); + else + qh_checkpolygon(qh newfacet_list); + } + if (qh STOPpoint > 0 && qh furthest_id == qh STOPpoint-1) + return False; + qh_resetlists(True, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */); + /* qh_triangulate(); to test qh.TRInormals */ + trace2((qh ferr, 2056, "qh_addpoint: added p%d new facets %d new balance %2.2g point balance %2.2g\n", + qh_pointid(furthest), numnew, newbalance, pbalance)); + return True; +} /* addpoint */ + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="build_withrestart">-</a> + + qh_build_withrestart() + 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(void) { + int restart; + + qh ALLOWrestart= True; + while (True) { + restart= setjmp(qh restartexit); /* simple statement for CRAY J916 */ + if (restart) { /* only from qh_precision() */ + zzinc_(Zretry); + wmax_(Wretrymax, qh JOGGLEmax); + /* QH7078 warns about using 'TCn' with 'QJn' */ + qh STOPcone= qh_IDunknown; /* if break from joggle, prevents normal output */ + } + if (!qh RERUN && qh JOGGLEmax < REALmax/2) { + if (qh build_cnt > qh_JOGGLEmaxretry) { + qh_fprintf(qh ferr, 6229, "qhull precision error: %d attempts to construct a convex hull\n\ + with joggled input. Increase joggle above 'QJ%2.2g'\n\ + or modify qh_JOGGLE... parameters in user.h\n", + qh build_cnt, qh JOGGLEmax); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + if (qh build_cnt && !restart) + break; + }else if (qh build_cnt && qh build_cnt >= qh RERUN) + break; + qh STOPcone= 0; + qh_freebuild(True); /* first call is a nop */ + qh build_cnt++; + if (!qh qhull_optionsiz) + qh qhull_optionsiz= (int)strlen(qh qhull_options); /* WARN64 */ + else { + qh qhull_options [qh qhull_optionsiz]= '\0'; + qh qhull_optionlen= qh_OPTIONline; /* starts a new line */ + } + qh_option("_run", &qh build_cnt, NULL); + if (qh build_cnt == qh RERUN) { + qh IStracing= qh TRACElastrun; /* duplicated from qh_initqhull_globals */ + if (qh TRACEpoint != qh_IDunknown || qh TRACEdist < REALmax/2 || qh TRACEmerge) { + qh TRACElevel= (qh IStracing? qh IStracing : 3); + qh IStracing= 0; + } + qhmem.IStracing= qh IStracing; + } + if (qh JOGGLEmax < REALmax/2) + qh_joggleinput(); + qh_initbuild(); + qh_buildhull(); + if (qh JOGGLEmax < REALmax/2 && !qh MERGING) + qh_checkconvex(qh facet_list, qh_ALGORITHMfault); + } + qh ALLOWrestart= False; +} /* qh_build_withrestart */ + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="buildhull">-</a> + + qh_buildhull() + construct a convex hull by adding outside points one at a time + + returns: + + notes: + may be called multiple times + checks facet and vertex lists for incorrect flags + to recover from STOPcone, call qh_deletevisible and qh_resetlists + + design: + check visible facet and newfacet flags + check newlist vertex flags and qh.STOPcone/STOPpoint + for each facet with a furthest outside point + add point to facet + exit if qh.STOPcone or qh.STOPpoint requested + if qh.NARROWhull for initial simplex + partition remaining outside points to coplanar sets +*/ +void qh_buildhull(void) { + facetT *facet; + pointT *furthest; + vertexT *vertex; + int id; + + trace1((qh ferr, 1037, "qh_buildhull: start build hull\n")); + FORALLfacets { + if (facet->visible || facet->newfacet) { + qh_fprintf(qh ferr, 6165, "qhull internal error (qh_buildhull): visible or new facet f%d in facet list\n", + facet->id); + qh_errexit(qh_ERRqhull, facet, NULL); + } + } + FORALLvertices { + if (vertex->newlist) { + qh_fprintf(qh ferr, 6166, "qhull internal error (qh_buildhull): new vertex f%d in vertex list\n", + vertex->id); + qh_errprint("ERRONEOUS", NULL, NULL, NULL, vertex); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + id= qh_pointid(vertex->point); + if ((qh STOPpoint>0 && id == qh STOPpoint-1) || + (qh STOPpoint<0 && id == -qh STOPpoint-1) || + (qh STOPcone>0 && id == qh STOPcone-1)) { + trace1((qh ferr, 1038,"qh_buildhull: stop point or cone P%d in initial hull\n", id)); + return; + } + } + qh facet_next= qh facet_list; /* advance facet when processed */ + while ((furthest= qh_nextfurthest(&facet))) { + qh num_outside--; /* if ONLYmax, furthest may not be outside */ + if (!qh_addpoint(furthest, facet, qh ONLYmax)) + break; + } + if (qh NARROWhull) /* move points from outsideset to coplanarset */ + qh_outcoplanar( /* facet_list */ ); + if (qh num_outside && !furthest) { + qh_fprintf(qh ferr, 6167, "qhull internal error (qh_buildhull): %d outside points were never processed.\n", qh num_outside); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + trace1((qh ferr, 1039, "qh_buildhull: completed the hull construction\n")); +} /* buildhull */ + + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="buildtracing">-</a> + + qh_buildtracing( furthest, facet ) + trace an iteration of qh_buildhull() for furthest point and facet + if !furthest, prints progress message + + returns: + tracks progress with qh.lastreport + updates qh.furthest_id (-3 if furthest is NULL) + also resets visit_id, vertext_visit on wrap around + + see: + qh_tracemerging() + + design: + if !furthest + print progress message + exit + if 'TFn' iteration + print progress message + else if tracing + trace furthest point and facet + reset qh.visit_id and qh.vertex_visit if overflow may occur + set qh.furthest_id for tracing +*/ +void qh_buildtracing(pointT *furthest, facetT *facet) { + realT dist= 0; + float cpu; + int total, furthestid; + time_t timedata; + struct tm *tp; + vertexT *vertex; + + qh old_randomdist= qh RANDOMdist; + qh RANDOMdist= False; + if (!furthest) { + time(&timedata); + tp= localtime(&timedata); + cpu= (float)qh_CPUclock - (float)qh hulltime; + cpu /= (float)qh_SECticks; + total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot); + qh_fprintf(qh ferr, 8118, "\n\ +At %02d:%02d:%02d & %2.5g CPU secs, qhull has created %d facets and merged %d.\n\ + The current hull contains %d facets and %d vertices. Last point was p%d\n", + tp->tm_hour, tp->tm_min, tp->tm_sec, cpu, qh facet_id -1, + total, qh num_facets, qh num_vertices, qh furthest_id); + return; + } + furthestid= qh_pointid(furthest); + if (qh TRACEpoint == furthestid) { + qh IStracing= qh TRACElevel; + qhmem.IStracing= qh TRACElevel; + }else if (qh TRACEpoint != qh_IDunknown && qh TRACEdist < REALmax/2) { + qh IStracing= 0; + qhmem.IStracing= 0; + } + if (qh REPORTfreq && (qh facet_id-1 > qh lastreport+qh REPORTfreq)) { + qh lastreport= qh facet_id-1; + time(&timedata); + tp= localtime(&timedata); + cpu= (float)qh_CPUclock - (float)qh hulltime; + cpu /= (float)qh_SECticks; + total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot); + zinc_(Zdistio); + qh_distplane(furthest, facet, &dist); + qh_fprintf(qh ferr, 8119, "\n\ +At %02d:%02d:%02d & %2.5g CPU secs, qhull has created %d facets and merged %d.\n\ + The current hull contains %d facets and %d vertices. There are %d\n\ + outside points. Next is point p%d(v%d), %2.2g above f%d.\n", + tp->tm_hour, tp->tm_min, tp->tm_sec, cpu, qh facet_id -1, + total, qh num_facets, qh num_vertices, qh num_outside+1, + furthestid, qh vertex_id, dist, getid_(facet)); + }else if (qh IStracing >=1) { + cpu= (float)qh_CPUclock - (float)qh hulltime; + cpu /= (float)qh_SECticks; + qh_distplane(furthest, facet, &dist); + qh_fprintf(qh ferr, 8120, "qh_addpoint: add p%d(v%d) to hull of %d facets(%2.2g above f%d) and %d outside at %4.4g CPU secs. Previous was p%d.\n", + furthestid, qh vertex_id, qh num_facets, dist, + getid_(facet), qh num_outside+1, cpu, qh furthest_id); + } + zmax_(Zvisit2max, (int)qh visit_id/2); + if (qh visit_id > (unsigned) INT_MAX) { /* 31 bits */ + zinc_(Zvisit); + qh visit_id= 0; + FORALLfacets + facet->visitid= 0; + } + zmax_(Zvvisit2max, (int)qh vertex_visit/2); + if (qh vertex_visit > (unsigned) INT_MAX) { /* 31 bits */ + zinc_(Zvvisit); + qh vertex_visit= 0; + FORALLvertices + vertex->visitid= 0; + } + qh furthest_id= furthestid; + qh RANDOMdist= qh old_randomdist; +} /* buildtracing */ + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="errexit2">-</a> + + qh_errexit2( exitcode, facet, otherfacet ) + return exitcode to system after an error + report two facets + + returns: + assumes exitcode non-zero + + see: + normally use qh_errexit() in user.c(reports a facet and a ridge) +*/ +void qh_errexit2(int exitcode, facetT *facet, facetT *otherfacet) { + + qh_errprint("ERRONEOUS", facet, otherfacet, NULL, NULL); + qh_errexit(exitcode, NULL, NULL); +} /* errexit2 */ + + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="findhorizon">-</a> + + qh_findhorizon( point, facet, goodvisible, goodhorizon ) + given a visible facet, find the point's horizon and visible facets + for all facets, !facet-visible + + returns: + returns qh.visible_list/num_visible with all visible facets + marks visible facets with ->visible + updates count of good visible and good horizon facets + updates qh.max_outside, qh.max_vertex, facet->maxoutside + + see: + similar to qh_delpoint() + + design: + move facet to qh.visible_list at end of qh.facet_list + for all visible facets + for each unvisited neighbor of a visible facet + compute distance of point to neighbor + if point above neighbor + move neighbor to end of qh.visible_list + else if point is coplanar with neighbor + update qh.max_outside, qh.max_vertex, neighbor->maxoutside + mark neighbor coplanar (will create a samecycle later) + update horizon statistics +*/ +void qh_findhorizon(pointT *point, facetT *facet, int *goodvisible, int *goodhorizon) { + facetT *neighbor, **neighborp, *visible; + int numhorizon= 0, coplanar= 0; + realT dist; + + trace1((qh ferr, 1040,"qh_findhorizon: find horizon for point p%d facet f%d\n",qh_pointid(point),facet->id)); + *goodvisible= *goodhorizon= 0; + zinc_(Ztotvisible); + qh_removefacet(facet); /* visible_list at end of qh facet_list */ + qh_appendfacet(facet); + qh num_visible= 1; + if (facet->good) + (*goodvisible)++; + qh visible_list= facet; + facet->visible= True; + facet->f.replace= NULL; + if (qh IStracing >=4) + qh_errprint("visible", facet, NULL, NULL, NULL); + qh visit_id++; + FORALLvisible_facets { + if (visible->tricoplanar && !qh TRInormals) { + qh_fprintf(qh ferr, 6230, "Qhull internal error (qh_findhorizon): does not work for tricoplanar facets. Use option 'Q11'\n"); + qh_errexit(qh_ERRqhull, visible, NULL); + } + visible->visitid= qh visit_id; + FOREACHneighbor_(visible) { + if (neighbor->visitid == qh visit_id) + continue; + neighbor->visitid= qh visit_id; + zzinc_(Znumvisibility); + qh_distplane(point, neighbor, &dist); + if (dist > qh MINvisible) { + zinc_(Ztotvisible); + qh_removefacet(neighbor); /* append to end of qh visible_list */ + qh_appendfacet(neighbor); + neighbor->visible= True; + neighbor->f.replace= NULL; + qh num_visible++; + if (neighbor->good) + (*goodvisible)++; + if (qh IStracing >=4) + qh_errprint("visible", neighbor, NULL, NULL, NULL); + }else { + if (dist > - qh MAXcoplanar) { + neighbor->coplanar= True; + zzinc_(Zcoplanarhorizon); + qh_precision("coplanar horizon"); + coplanar++; + if (qh MERGING) { + if (dist > 0) { + maximize_(qh max_outside, dist); + maximize_(qh max_vertex, dist); +#if qh_MAXoutside + maximize_(neighbor->maxoutside, dist); +#endif + }else + minimize_(qh min_vertex, dist); /* due to merge later */ + } + trace2((qh ferr, 2057, "qh_findhorizon: point p%d is coplanar to horizon f%d, dist=%2.7g < qh MINvisible(%2.7g)\n", + qh_pointid(point), neighbor->id, dist, qh MINvisible)); + }else + neighbor->coplanar= False; + zinc_(Ztothorizon); + numhorizon++; + if (neighbor->good) + (*goodhorizon)++; + if (qh IStracing >=4) + qh_errprint("horizon", neighbor, NULL, NULL, NULL); + } + } + } + if (!numhorizon) { + qh_precision("empty horizon"); + qh_fprintf(qh ferr, 6168, "qhull precision error (qh_findhorizon): empty horizon\n\ +QhullPoint p%d was above all facets.\n", qh_pointid(point)); + qh_printfacetlist(qh facet_list, NULL, True); + qh_errexit(qh_ERRprec, NULL, NULL); + } + trace1((qh ferr, 1041, "qh_findhorizon: %d horizon facets(good %d), %d visible(good %d), %d coplanar\n", + numhorizon, *goodhorizon, qh num_visible, *goodvisible, coplanar)); + if (qh IStracing >= 4 && qh num_facets < 50) + qh_printlists(); +} /* findhorizon */ + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="nextfurthest">-</a> + + qh_nextfurthest( visible ) + returns next furthest point and visible facet for qh_addpoint() + starts search at qh.facet_next + + returns: + removes furthest point from outside set + NULL if none available + advances qh.facet_next over facets with empty outside sets + + design: + for each facet from qh.facet_next + if empty outside set + advance qh.facet_next + else if qh.NARROWhull + determine furthest outside point + if furthest point is not outside + advance qh.facet_next(point will be coplanar) + remove furthest point from outside set +*/ +pointT *qh_nextfurthest(facetT **visible) { + facetT *facet; + int size, idx; + realT randr, dist; + pointT *furthest; + + while ((facet= qh facet_next) != qh facet_tail) { + if (!facet->outsideset) { + qh facet_next= facet->next; + continue; + } + SETreturnsize_(facet->outsideset, size); + if (!size) { + qh_setfree(&facet->outsideset); + qh facet_next= facet->next; + continue; + } + if (qh NARROWhull) { + if (facet->notfurthest) + qh_furthestout(facet); + furthest= (pointT*)qh_setlast(facet->outsideset); +#if qh_COMPUTEfurthest + qh_distplane(furthest, facet, &dist); + zinc_(Zcomputefurthest); +#else + dist= facet->furthestdist; +#endif + if (dist < qh MINoutside) { /* remainder of outside set is coplanar for qh_outcoplanar */ + qh facet_next= facet->next; + continue; + } + } + if (!qh RANDOMoutside && !qh VIRTUALmemory) { + if (qh PICKfurthest) { + qh_furthestnext(/* qh.facet_list */); + facet= qh facet_next; + } + *visible= facet; + return((pointT*)qh_setdellast(facet->outsideset)); + } + if (qh RANDOMoutside) { + int outcoplanar = 0; + if (qh NARROWhull) { + FORALLfacets { + if (facet == qh facet_next) + break; + if (facet->outsideset) + outcoplanar += qh_setsize( facet->outsideset); + } + } + randr= qh_RANDOMint; + randr= randr/(qh_RANDOMmax+1); + idx= (int)floor((qh num_outside - outcoplanar) * randr); + FORALLfacet_(qh facet_next) { + if (facet->outsideset) { + SETreturnsize_(facet->outsideset, size); + if (!size) + qh_setfree(&facet->outsideset); + else if (size > idx) { + *visible= facet; + return((pointT*)qh_setdelnth(facet->outsideset, idx)); + }else + idx -= size; + } + } + qh_fprintf(qh ferr, 6169, "qhull internal error (qh_nextfurthest): num_outside %d is too low\nby at least %d, or a random real %g >= 1.0\n", + qh num_outside, idx+1, randr); + qh_errexit(qh_ERRqhull, NULL, NULL); + }else { /* VIRTUALmemory */ + facet= qh facet_tail->previous; + if (!(furthest= (pointT*)qh_setdellast(facet->outsideset))) { + if (facet->outsideset) + qh_setfree(&facet->outsideset); + qh_removefacet(facet); + qh_prependfacet(facet, &qh facet_list); + continue; + } + *visible= facet; + return furthest; + } + } + return NULL; +} /* nextfurthest */ + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="partitionall">-</a> + + qh_partitionall( vertices, points, numpoints ) + partitions all points in points/numpoints to the outsidesets of facets + vertices= vertices in qh.facet_list(!partitioned) + + returns: + builds facet->outsideset + does not partition qh.GOODpoint + if qh.ONLYgood && !qh.MERGING, + does not partition qh.GOODvertex + + notes: + faster if qh.facet_list sorted by anticipated size of outside set + + design: + initialize pointset with all points + remove vertices from pointset + remove qh.GOODpointp from pointset (unless it's qh.STOPcone or qh.STOPpoint) + for all facets + for all remaining points in pointset + compute distance from point to facet + if point is outside facet + remove point from pointset (by not reappending) + update bestpoint + append point or old bestpoint to facet's outside set + append bestpoint to facet's outside set (furthest) + for all points remaining in pointset + partition point into facets' outside sets and coplanar sets +*/ +void qh_partitionall(setT *vertices, pointT *points, int numpoints){ + setT *pointset; + vertexT *vertex, **vertexp; + pointT *point, **pointp, *bestpoint; + int size, point_i, point_n, point_end, remaining, i, id; + facetT *facet; + realT bestdist= -REALmax, dist, distoutside; + + trace1((qh ferr, 1042, "qh_partitionall: partition all points into outside sets\n")); + pointset= qh_settemp(numpoints); + qh num_outside= 0; + pointp= SETaddr_(pointset, pointT); + for (i=numpoints, point= points; i--; point += qh hull_dim) + *(pointp++)= point; + qh_settruncate(pointset, numpoints); + FOREACHvertex_(vertices) { + if ((id= qh_pointid(vertex->point)) >= 0) + SETelem_(pointset, id)= NULL; + } + id= qh_pointid(qh GOODpointp); + if (id >=0 && qh STOPcone-1 != id && -qh STOPpoint-1 != id) + SETelem_(pointset, id)= NULL; + if (qh GOODvertexp && qh ONLYgood && !qh MERGING) { /* matches qhull()*/ + if ((id= qh_pointid(qh GOODvertexp)) >= 0) + SETelem_(pointset, id)= NULL; + } + if (!qh BESToutside) { /* matches conditional for qh_partitionpoint below */ + distoutside= qh_DISToutside; /* multiple of qh.MINoutside & qh.max_outside, see user.h */ + zval_(Ztotpartition)= qh num_points - qh hull_dim - 1; /*misses GOOD... */ + remaining= qh num_facets; + point_end= numpoints; + FORALLfacets { + size= point_end/(remaining--) + 100; + facet->outsideset= qh_setnew(size); + bestpoint= NULL; + point_end= 0; + FOREACHpoint_i_(pointset) { + if (point) { + zzinc_(Zpartitionall); + qh_distplane(point, facet, &dist); + if (dist < distoutside) + SETelem_(pointset, point_end++)= point; + else { + qh num_outside++; + if (!bestpoint) { + bestpoint= point; + bestdist= dist; + }else if (dist > bestdist) { + qh_setappend(&facet->outsideset, bestpoint); + bestpoint= point; + bestdist= dist; + }else + qh_setappend(&facet->outsideset, point); + } + } + } + if (bestpoint) { + qh_setappend(&facet->outsideset, bestpoint); +#if !qh_COMPUTEfurthest + facet->furthestdist= bestdist; +#endif + }else + qh_setfree(&facet->outsideset); + qh_settruncate(pointset, point_end); + } + } + /* if !qh BESToutside, pointset contains points not assigned to outsideset */ + if (qh BESToutside || qh MERGING || qh KEEPcoplanar || qh KEEPinside) { + qh findbestnew= True; + FOREACHpoint_i_(pointset) { + if (point) + qh_partitionpoint(point, qh facet_list); + } + qh findbestnew= False; + } + zzadd_(Zpartitionall, zzval_(Zpartition)); + zzval_(Zpartition)= 0; + qh_settempfree(&pointset); + if (qh IStracing >= 4) + qh_printfacetlist(qh facet_list, NULL, True); +} /* partitionall */ + + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="partitioncoplanar">-</a> + + qh_partitioncoplanar( point, facet, dist ) + partition coplanar point to a facet + dist is distance from point to facet + if dist NULL, + searches for bestfacet and does nothing if inside + if qh.findbestnew set, + searches new facets instead of using qh_findbest() + + returns: + qh.max_ouside updated + if qh.KEEPcoplanar or qh.KEEPinside + point assigned to best coplanarset + + notes: + facet->maxoutside is updated at end by qh_check_maxout + + design: + if dist undefined + find best facet for point + if point sufficiently below facet (depends on qh.NEARinside and qh.KEEPinside) + exit + if keeping coplanar/nearinside/inside points + if point is above furthest coplanar point + append point to coplanar set (it is the new furthest) + update qh.max_outside + else + append point one before end of coplanar set + else if point is clearly outside of qh.max_outside and bestfacet->coplanarset + and bestfacet is more than perpendicular to facet + repartition the point using qh_findbest() -- it may be put on an outsideset + else + update qh.max_outside +*/ +void qh_partitioncoplanar(pointT *point, facetT *facet, realT *dist) { + facetT *bestfacet; + pointT *oldfurthest; + realT bestdist, dist2= 0, angle; + int numpart= 0, oldfindbest; + boolT isoutside; + + qh WAScoplanar= True; + if (!dist) { + if (qh findbestnew) + bestfacet= qh_findbestnew(point, facet, &bestdist, qh_ALL, &isoutside, &numpart); + else + bestfacet= qh_findbest(point, facet, qh_ALL, !qh_ISnewfacets, qh DELAUNAY, + &bestdist, &isoutside, &numpart); + zinc_(Ztotpartcoplanar); + zzadd_(Zpartcoplanar, numpart); + if (!qh DELAUNAY && !qh KEEPinside) { /* for 'd', bestdist skips upperDelaunay facets */ + if (qh KEEPnearinside) { + if (bestdist < -qh NEARinside) { + zinc_(Zcoplanarinside); + trace4((qh ferr, 4062, "qh_partitioncoplanar: point p%d is more than near-inside facet f%d dist %2.2g findbestnew %d\n", + qh_pointid(point), bestfacet->id, bestdist, qh findbestnew)); + return; + } + }else if (bestdist < -qh MAXcoplanar) { + trace4((qh ferr, 4063, "qh_partitioncoplanar: point p%d is inside facet f%d dist %2.2g findbestnew %d\n", + qh_pointid(point), bestfacet->id, bestdist, qh findbestnew)); + zinc_(Zcoplanarinside); + return; + } + } + }else { + bestfacet= facet; + bestdist= *dist; + } + if (bestdist > qh max_outside) { + if (!dist && facet != bestfacet) { + zinc_(Zpartangle); + angle= qh_getangle(facet->normal, bestfacet->normal); + if (angle < 0) { + /* typically due to deleted vertex and coplanar facets, e.g., + RBOX 1000 s Z1 G1e-13 t1001185205 | QHULL Tv */ + zinc_(Zpartflip); + trace2((qh ferr, 2058, "qh_partitioncoplanar: repartition point p%d from f%d. It is above flipped facet f%d dist %2.2g\n", + qh_pointid(point), facet->id, bestfacet->id, bestdist)); + oldfindbest= qh findbestnew; + qh findbestnew= False; + qh_partitionpoint(point, bestfacet); + qh findbestnew= oldfindbest; + return; + } + } + qh max_outside= bestdist; + if (bestdist > qh TRACEdist) { + qh_fprintf(qh ferr, 8122, "qh_partitioncoplanar: ====== p%d from f%d increases max_outside to %2.2g of f%d last p%d\n", + qh_pointid(point), facet->id, bestdist, bestfacet->id, qh furthest_id); + qh_errprint("DISTANT", facet, bestfacet, NULL, NULL); + } + } + if (qh KEEPcoplanar + qh KEEPinside + qh KEEPnearinside) { + oldfurthest= (pointT*)qh_setlast(bestfacet->coplanarset); + if (oldfurthest) { + zinc_(Zcomputefurthest); + qh_distplane(oldfurthest, bestfacet, &dist2); + } + if (!oldfurthest || dist2 < bestdist) + qh_setappend(&bestfacet->coplanarset, point); + else + qh_setappend2ndlast(&bestfacet->coplanarset, point); + } + trace4((qh ferr, 4064, "qh_partitioncoplanar: point p%d is coplanar with facet f%d(or inside) dist %2.2g\n", + qh_pointid(point), bestfacet->id, bestdist)); +} /* partitioncoplanar */ + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="partitionpoint">-</a> + + qh_partitionpoint( point, facet ) + assigns point to an outside set, coplanar set, or inside set (i.e., dropt) + if qh.findbestnew + uses qh_findbestnew() to search all new facets + else + uses qh_findbest() + + notes: + after qh_distplane(), this and qh_findbest() are most expensive in 3-d + + design: + find best facet for point + (either exhaustive search of new facets or directed search from facet) + if qh.NARROWhull + retain coplanar and nearinside points as outside points + if point is outside bestfacet + if point above furthest point for bestfacet + append point to outside set (it becomes the new furthest) + if outside set was empty + move bestfacet to end of qh.facet_list (i.e., after qh.facet_next) + update bestfacet->furthestdist + else + append point one before end of outside set + else if point is coplanar to bestfacet + if keeping coplanar points or need to update qh.max_outside + partition coplanar point into bestfacet + else if near-inside point + partition as coplanar point into bestfacet + else is an inside point + if keeping inside points + partition as coplanar point into bestfacet +*/ +void qh_partitionpoint(pointT *point, facetT *facet) { + realT bestdist; + boolT isoutside; + facetT *bestfacet; + int numpart; +#if qh_COMPUTEfurthest + realT dist; +#endif + + if (qh findbestnew) + bestfacet= qh_findbestnew(point, facet, &bestdist, qh BESToutside, &isoutside, &numpart); + else + bestfacet= qh_findbest(point, facet, qh BESToutside, qh_ISnewfacets, !qh_NOupper, + &bestdist, &isoutside, &numpart); + zinc_(Ztotpartition); + zzadd_(Zpartition, numpart); + if (qh NARROWhull) { + if (qh DELAUNAY && !isoutside && bestdist >= -qh MAXcoplanar) + qh_precision("nearly incident point(narrow hull)"); + if (qh KEEPnearinside) { + if (bestdist >= -qh NEARinside) + isoutside= True; + }else if (bestdist >= -qh MAXcoplanar) + isoutside= True; + } + + if (isoutside) { + if (!bestfacet->outsideset + || !qh_setlast(bestfacet->outsideset)) { + qh_setappend(&(bestfacet->outsideset), point); + if (!bestfacet->newfacet) { + qh_removefacet(bestfacet); /* make sure it's after qh facet_next */ + qh_appendfacet(bestfacet); + } +#if !qh_COMPUTEfurthest + bestfacet->furthestdist= bestdist; +#endif + }else { +#if qh_COMPUTEfurthest + zinc_(Zcomputefurthest); + qh_distplane(oldfurthest, bestfacet, &dist); + if (dist < bestdist) + qh_setappend(&(bestfacet->outsideset), point); + else + qh_setappend2ndlast(&(bestfacet->outsideset), point); +#else + if (bestfacet->furthestdist < bestdist) { + qh_setappend(&(bestfacet->outsideset), point); + bestfacet->furthestdist= bestdist; + }else + qh_setappend2ndlast(&(bestfacet->outsideset), point); +#endif + } + qh num_outside++; + trace4((qh ferr, 4065, "qh_partitionpoint: point p%d is outside facet f%d new? %d (or narrowhull)\n", + qh_pointid(point), bestfacet->id, bestfacet->newfacet)); + }else if (qh DELAUNAY || bestdist >= -qh MAXcoplanar) { /* for 'd', bestdist skips upperDelaunay facets */ + zzinc_(Zcoplanarpart); + if (qh DELAUNAY) + qh_precision("nearly incident point"); + if ((qh KEEPcoplanar + qh KEEPnearinside) || bestdist > qh max_outside) + qh_partitioncoplanar(point, bestfacet, &bestdist); + else { + trace4((qh ferr, 4066, "qh_partitionpoint: point p%d is coplanar to facet f%d (dropped)\n", + qh_pointid(point), bestfacet->id)); + } + }else if (qh KEEPnearinside && bestdist > -qh NEARinside) { + zinc_(Zpartnear); + qh_partitioncoplanar(point, bestfacet, &bestdist); + }else { + zinc_(Zpartinside); + trace4((qh ferr, 4067, "qh_partitionpoint: point p%d is inside all facets, closest to f%d dist %2.2g\n", + qh_pointid(point), bestfacet->id, bestdist)); + if (qh KEEPinside) + qh_partitioncoplanar(point, bestfacet, &bestdist); + } +} /* partitionpoint */ + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="partitionvisible">-</a> + + qh_partitionvisible( allpoints, numoutside ) + partitions points in visible facets to qh.newfacet_list + qh.visible_list= visible facets + for visible facets + 1st neighbor (if any) points to a horizon facet or a new facet + if allpoints(!used), + repartitions coplanar points + + returns: + updates outside sets and coplanar sets of qh.newfacet_list + updates qh.num_outside (count of outside points) + + notes: + qh.findbest_notsharp should be clear (extra work if set) + + design: + for all visible facets with outside set or coplanar set + select a newfacet for visible facet + if outside set + partition outside set into new facets + if coplanar set and keeping coplanar/near-inside/inside points + if allpoints + partition coplanar set into new facets, may be assigned outside + else + partition coplanar set into coplanar sets of new facets + for each deleted vertex + if allpoints + partition vertex into new facets, may be assigned outside + else + partition vertex into coplanar sets of new facets +*/ +void qh_partitionvisible(/*qh.visible_list*/ boolT allpoints, int *numoutside) { + facetT *visible, *newfacet; + pointT *point, **pointp; + int coplanar=0, size; + unsigned count; + vertexT *vertex, **vertexp; + + if (qh ONLYmax) + maximize_(qh MINoutside, qh max_vertex); + *numoutside= 0; + FORALLvisible_facets { + if (!visible->outsideset && !visible->coplanarset) + continue; + newfacet= visible->f.replace; + count= 0; + while (newfacet && newfacet->visible) { + newfacet= newfacet->f.replace; + if (count++ > qh facet_id) + qh_infiniteloop(visible); + } + if (!newfacet) + newfacet= qh newfacet_list; + if (newfacet == qh facet_tail) { + qh_fprintf(qh ferr, 6170, "qhull precision error (qh_partitionvisible): all new facets deleted as\n degenerate facets. Can not continue.\n"); + qh_errexit(qh_ERRprec, NULL, NULL); + } + if (visible->outsideset) { + size= qh_setsize(visible->outsideset); + *numoutside += size; + qh num_outside -= size; + FOREACHpoint_(visible->outsideset) + qh_partitionpoint(point, newfacet); + } + if (visible->coplanarset && (qh KEEPcoplanar + qh KEEPinside + qh KEEPnearinside)) { + size= qh_setsize(visible->coplanarset); + coplanar += size; + FOREACHpoint_(visible->coplanarset) { + if (allpoints) /* not used */ + qh_partitionpoint(point, newfacet); + else + qh_partitioncoplanar(point, newfacet, NULL); + } + } + } + FOREACHvertex_(qh del_vertices) { + if (vertex->point) { + if (allpoints) /* not used */ + qh_partitionpoint(vertex->point, qh newfacet_list); + else + qh_partitioncoplanar(vertex->point, qh newfacet_list, NULL); + } + } + trace1((qh ferr, 1043,"qh_partitionvisible: partitioned %d points from outsidesets and %d points from coplanarsets\n", *numoutside, coplanar)); +} /* partitionvisible */ + + + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="precision">-</a> + + qh_precision( reason ) + restart on precision errors if not merging and if 'QJn' +*/ +void qh_precision(const char *reason) { + + if (qh ALLOWrestart && !qh PREmerge && !qh MERGEexact) { + if (qh JOGGLEmax < REALmax/2) { + trace0((qh ferr, 26, "qh_precision: qhull restart because of %s\n", reason)); + /* May be called repeatedly if qh->ALLOWrestart */ + longjmp(qh restartexit, qh_ERRprec); + } + } +} /* qh_precision */ + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="printsummary">-</a> + + qh_printsummary( fp ) + prints summary to fp + + notes: + not in io.c so that user_eg.c can prevent io.c from loading + qh_printsummary and qh_countfacets must match counts + + design: + determine number of points, vertices, and coplanar points + print summary +*/ +void qh_printsummary(FILE *fp) { + realT ratio, outerplane, innerplane; + float cpu; + int size, id, nummerged, numvertices, numcoplanars= 0, nonsimplicial=0; + int goodused; + facetT *facet; + const char *s; + int numdel= zzval_(Zdelvertextot); + int numtricoplanars= 0; + + size= qh num_points + qh_setsize(qh other_points); + numvertices= qh num_vertices - qh_setsize(qh del_vertices); + id= qh_pointid(qh GOODpointp); + FORALLfacets { + if (facet->coplanarset) + numcoplanars += qh_setsize( facet->coplanarset); + if (facet->good) { + if (facet->simplicial) { + if (facet->keepcentrum && facet->tricoplanar) + numtricoplanars++; + }else if (qh_setsize(facet->vertices) != qh hull_dim) + nonsimplicial++; + } + } + if (id >=0 && qh STOPcone-1 != id && -qh STOPpoint-1 != id) + size--; + if (qh STOPcone || qh STOPpoint) + qh_fprintf(fp, 9288, "\nAt a premature exit due to 'TVn', 'TCn', 'TRn', or precision error with 'QJn'."); + if (qh UPPERdelaunay) + goodused= qh GOODvertex + qh GOODpoint + qh SPLITthresholds; + else if (qh DELAUNAY) + goodused= qh GOODvertex + qh GOODpoint + qh GOODthreshold; + else + goodused= qh num_good; + nummerged= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot); + if (qh VORONOI) { + if (qh UPPERdelaunay) + qh_fprintf(fp, 9289, "\n\ +Furthest-site Voronoi vertices by the convex hull of %d points in %d-d:\n\n", size, qh hull_dim); + else + qh_fprintf(fp, 9290, "\n\ +Voronoi diagram by the convex hull of %d points in %d-d:\n\n", size, qh hull_dim); + qh_fprintf(fp, 9291, " Number of Voronoi regions%s: %d\n", + qh ATinfinity ? " and at-infinity" : "", numvertices); + if (numdel) + qh_fprintf(fp, 9292, " Total number of deleted points due to merging: %d\n", numdel); + if (numcoplanars - numdel > 0) + qh_fprintf(fp, 9293, " Number of nearly incident points: %d\n", numcoplanars - numdel); + else if (size - numvertices - numdel > 0) + qh_fprintf(fp, 9294, " Total number of nearly incident points: %d\n", size - numvertices - numdel); + qh_fprintf(fp, 9295, " Number of%s Voronoi vertices: %d\n", + goodused ? " 'good'" : "", qh num_good); + if (nonsimplicial) + qh_fprintf(fp, 9296, " Number of%s non-simplicial Voronoi vertices: %d\n", + goodused ? " 'good'" : "", nonsimplicial); + }else if (qh DELAUNAY) { + if (qh UPPERdelaunay) + qh_fprintf(fp, 9297, "\n\ +Furthest-site Delaunay triangulation by the convex hull of %d points in %d-d:\n\n", size, qh hull_dim); + else + qh_fprintf(fp, 9298, "\n\ +Delaunay triangulation by the convex hull of %d points in %d-d:\n\n", size, qh hull_dim); + qh_fprintf(fp, 9299, " Number of input sites%s: %d\n", + qh ATinfinity ? " and at-infinity" : "", numvertices); + if (numdel) + qh_fprintf(fp, 9300, " Total number of deleted points due to merging: %d\n", numdel); + if (numcoplanars - numdel > 0) + qh_fprintf(fp, 9301, " Number of nearly incident points: %d\n", numcoplanars - numdel); + else if (size - numvertices - numdel > 0) + qh_fprintf(fp, 9302, " Total number of nearly incident points: %d\n", size - numvertices - numdel); + qh_fprintf(fp, 9303, " Number of%s Delaunay regions: %d\n", + goodused ? " 'good'" : "", qh num_good); + if (nonsimplicial) + qh_fprintf(fp, 9304, " Number of%s non-simplicial Delaunay regions: %d\n", + goodused ? " 'good'" : "", nonsimplicial); + }else if (qh HALFspace) { + qh_fprintf(fp, 9305, "\n\ +Halfspace intersection by the convex hull of %d points in %d-d:\n\n", size, qh hull_dim); + qh_fprintf(fp, 9306, " Number of halfspaces: %d\n", size); + qh_fprintf(fp, 9307, " Number of non-redundant halfspaces: %d\n", numvertices); + if (numcoplanars) { + if (qh KEEPinside && qh KEEPcoplanar) + s= "similar and redundant"; + else if (qh KEEPinside) + s= "redundant"; + else + s= "similar"; + qh_fprintf(fp, 9308, " Number of %s halfspaces: %d\n", s, numcoplanars); + } + qh_fprintf(fp, 9309, " Number of intersection points: %d\n", qh num_facets - qh num_visible); + if (goodused) + qh_fprintf(fp, 9310, " Number of 'good' intersection points: %d\n", qh num_good); + if (nonsimplicial) + qh_fprintf(fp, 9311, " Number of%s non-simplicial intersection points: %d\n", + goodused ? " 'good'" : "", nonsimplicial); + }else { + qh_fprintf(fp, 9312, "\n\ +Convex hull of %d points in %d-d:\n\n", size, qh hull_dim); + qh_fprintf(fp, 9313, " Number of vertices: %d\n", numvertices); + if (numcoplanars) { + if (qh KEEPinside && qh KEEPcoplanar) + s= "coplanar and interior"; + else if (qh KEEPinside) + s= "interior"; + else + s= "coplanar"; + qh_fprintf(fp, 9314, " Number of %s points: %d\n", s, numcoplanars); + } + qh_fprintf(fp, 9315, " Number of facets: %d\n", qh num_facets - qh num_visible); + if (goodused) + qh_fprintf(fp, 9316, " Number of 'good' facets: %d\n", qh num_good); + if (nonsimplicial) + qh_fprintf(fp, 9317, " Number of%s non-simplicial facets: %d\n", + goodused ? " 'good'" : "", nonsimplicial); + } + if (numtricoplanars) + qh_fprintf(fp, 9318, " Number of triangulated facets: %d\n", numtricoplanars); + qh_fprintf(fp, 9319, "\nStatistics for: %s | %s", + qh rbox_command, qh qhull_command); + if (qh ROTATErandom != INT_MIN) + qh_fprintf(fp, 9320, " QR%d\n\n", qh ROTATErandom); + else + qh_fprintf(fp, 9321, "\n\n"); + qh_fprintf(fp, 9322, " Number of points processed: %d\n", zzval_(Zprocessed)); + qh_fprintf(fp, 9323, " Number of hyperplanes created: %d\n", zzval_(Zsetplane)); + if (qh DELAUNAY) + qh_fprintf(fp, 9324, " Number of facets in hull: %d\n", qh num_facets - qh num_visible); + qh_fprintf(fp, 9325, " Number of distance tests for qhull: %d\n", zzval_(Zpartition)+ + zzval_(Zpartitionall)+zzval_(Znumvisibility)+zzval_(Zpartcoplanar)); +#if 0 /* NOTE: must print before printstatistics() */ + {realT stddev, ave; + qh_fprintf(fp, 9326, " average new facet balance: %2.2g\n", + wval_(Wnewbalance)/zval_(Zprocessed)); + stddev= qh_stddev(zval_(Zprocessed), wval_(Wnewbalance), + wval_(Wnewbalance2), &ave); + qh_fprintf(fp, 9327, " new facet standard deviation: %2.2g\n", stddev); + qh_fprintf(fp, 9328, " average partition balance: %2.2g\n", + wval_(Wpbalance)/zval_(Zpbalance)); + stddev= qh_stddev(zval_(Zpbalance), wval_(Wpbalance), + wval_(Wpbalance2), &ave); + qh_fprintf(fp, 9329, " partition standard deviation: %2.2g\n", stddev); + } +#endif + if (nummerged) { + qh_fprintf(fp, 9330," Number of distance tests for merging: %d\n",zzval_(Zbestdist)+ + zzval_(Zcentrumtests)+zzval_(Zdistconvex)+zzval_(Zdistcheck)+ + zzval_(Zdistzero)); + qh_fprintf(fp, 9331," Number of distance tests for checking: %d\n",zzval_(Zcheckpart)); + qh_fprintf(fp, 9332," Number of merged facets: %d\n", nummerged); + } + if (!qh RANDOMoutside && qh QHULLfinished) { + cpu= (float)qh hulltime; + cpu /= (float)qh_SECticks; + wval_(Wcpu)= cpu; + qh_fprintf(fp, 9333, " CPU seconds to compute hull (after input): %2.4g\n", cpu); + } + if (qh RERUN) { + if (!qh PREmerge && !qh MERGEexact) + qh_fprintf(fp, 9334, " Percentage of runs with precision errors: %4.1f\n", + zzval_(Zretry)*100.0/qh build_cnt); /* careful of order */ + }else if (qh JOGGLEmax < REALmax/2) { + if (zzval_(Zretry)) + qh_fprintf(fp, 9335, " After %d retries, input joggled by: %2.2g\n", + zzval_(Zretry), qh JOGGLEmax); + else + qh_fprintf(fp, 9336, " Input joggled by: %2.2g\n", qh JOGGLEmax); + } + if (qh totarea != 0.0) + qh_fprintf(fp, 9337, " %s facet area: %2.8g\n", + zzval_(Ztotmerge) ? "Approximate" : "Total", qh totarea); + if (qh totvol != 0.0) + qh_fprintf(fp, 9338, " %s volume: %2.8g\n", + zzval_(Ztotmerge) ? "Approximate" : "Total", qh totvol); + if (qh MERGING) { + qh_outerinner(NULL, &outerplane, &innerplane); + if (outerplane > 2 * qh DISTround) { + qh_fprintf(fp, 9339, " Maximum distance of %spoint above facet: %2.2g", + (qh QHULLfinished ? "" : "merged "), outerplane); + ratio= outerplane/(qh ONEmerge + qh DISTround); + /* don't report ratio if MINoutside is large */ + if (ratio > 0.05 && 2* qh ONEmerge > qh MINoutside && qh JOGGLEmax > REALmax/2) + qh_fprintf(fp, 9340, " (%.1fx)\n", ratio); + else + qh_fprintf(fp, 9341, "\n"); + } + if (innerplane < -2 * qh DISTround) { + qh_fprintf(fp, 9342, " Maximum distance of %svertex below facet: %2.2g", + (qh QHULLfinished ? "" : "merged "), innerplane); + ratio= -innerplane/(qh ONEmerge+qh DISTround); + if (ratio > 0.05 && qh JOGGLEmax > REALmax/2) + qh_fprintf(fp, 9343, " (%.1fx)\n", ratio); + else + qh_fprintf(fp, 9344, "\n"); + } + } + qh_fprintf(fp, 9345, "\n"); +} /* printsummary */ + + diff --git a/src/qhull/src/libqhull/libqhull.h b/src/qhull/src/libqhull/libqhull.h new file mode 100644 index 000000000..677085808 --- /dev/null +++ b/src/qhull/src/libqhull/libqhull.h @@ -0,0 +1,1140 @@ +/*<html><pre> -<a href="qh-qhull.htm" + >-------------------------------</a><a name="TOP">-</a> + + libqhull.h + user-level header file for using qhull.a library + + see qh-qhull.htm, qhull_a.h + + Copyright (c) 1993-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull/libqhull.h#7 $$Change: 2066 $ + $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $ + + NOTE: access to qh_qh is via the 'qh' macro. This allows + qh_qh to be either a pointer or a structure. An example + of using qh is "qh.DROPdim" which accesses the DROPdim + field of qh_qh. Similarly, access to qh_qhstat is via + the 'qhstat' macro. + + includes function prototypes for libqhull.c, geom.c, global.c, io.c, user.c + + use mem.h for mem.c + use qset.h for qset.c + + see unix.c for an example of using libqhull.h + + recompile qhull if you change this file +*/ + +#ifndef qhDEFlibqhull +#define qhDEFlibqhull 1 + +/*=========================== -included files ==============*/ + +/* user_r.h first for QHULL_CRTDBG */ +#include "user.h" /* user definable constants (e.g., qh_QHpointer) */ + +#include "mem.h" /* Needed qhT in libqhull_r.h. Here for compatibility */ +#include "qset.h" /* Needed for QHULL_LIB_CHECK */ +/* include stat_r.h after defining boolT. Needed for qhT in libqhull_r.h. Here for compatibility and statT */ + +#include <setjmp.h> +#include <float.h> +#include <time.h> +#include <stdio.h> + +#if __MWERKS__ && __POWERPC__ +#include <SIOUX.h> +#include <Files.h> +#include <Desk.h> +#endif + +#ifndef __STDC__ +#ifndef __cplusplus +#if !_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.h +#endif +#endif +#endif + +/*============ constants and basic types ====================*/ + +extern const char qh_version[]; /* defined in global.c */ +extern const char qh_version2[]; /* defined in global.c */ + +/*-<a href="qh-geom.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.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.htm#TOC" + >--------------------------------</a><a name="flagT">-</a> + + flagT + Boolean flag as a bit +*/ +#define flagT unsigned int + +/*-<a href="qh-qhull.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.h" /* after define of boolT */ + +/*-<a href="qh-qhull.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.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.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.htm#TOC" + >--------------------------------</a><a name="qh_ERR">-</a> + + qh_ERR + Qhull exit codes, for indicating errors + See: MSG_ERROR and MSG_WARNING [user.h] +*/ +#define qh_ERRnone 0 /* no error occurred during qhull */ +#define qh_ERRinput 1 /* input inconsistency */ +#define qh_ERRsingular 2 /* singular input data */ +#define qh_ERRprec 3 /* precision error */ +#define qh_ERRmem 4 /* insufficient memory, matches mem.h */ +#define qh_ERRqhull 5 /* internal error detected, matches mem.h */ + +/*-<a href="qh-qhull.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 + DEFsetT is likewise defined in + mem.h and qset.h +*/ + +typedef struct vertexT vertexT; +typedef struct ridgeT ridgeT; +typedef struct facetT facetT; +#ifndef DEFsetT +#define DEFsetT 1 +typedef struct setT setT; /* defined in qset.h */ +#endif + +/*-<a href="qh-poly.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 + 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 + 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 +*/ +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 mergefacet + Actual outer plane is +DISTround and + computed outer plane is +2*DISTround */ +#endif + coordT offset; /* exact offset of hyperplane from origin */ + coordT *normal; /* normal of hyperplane, hull_dim coefficients */ + /* if tricoplanar, shared with a neighbor */ + union { /* in order of testing */ + realT area; /* area of facet, only in io.c if ->isarea */ + facetT *replace; /* replacement facet if ->visible and NEWfacets + is NULL only if qh_mergedegen_redundant or interior */ + 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 */ + facetT *next; /* next facet in the facet_list */ + setT *vertices; /* vertices for this facet, inverse sorted by ID + if simplicial, 1st vertex was apex/furthest */ + setT *ridges; /* explicit ridges for nonsimplicial facets. + for simplicial facets, neighbors define the ridges */ + setT *neighbors; /* neighbors of the facet. If simplicial, the kth + neighbor is opposite the kth vertex, and the first + neighbor is the horizon facet for the first vertex*/ + setT *outsideset; /* set of points outside this facet + if non-empty, last point is furthest + if NARROWhull, includes coplanars 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 visitid; /* visit_id, for visiting all neighbors, + all uses are independent */ + unsigned id; /* unique identifier from qh.facet_id */ + unsigned nummerge:9; /* number of merges */ +#define qh_MAXnummerge 511 /* 2^9-1, 32 flags total, see "flags:" in io.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 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 duplicate ridge in facet */ + flagT mergeridge:1; /* True if facet or neighbor contains a qh_MERGEridge + ->normal defined (also defined for mergeridge2) */ + flagT mergeridge2:1; /* True if neighbor contains a qh_MERGEridge (mark_dupridges */ + flagT coplanar:1; /* True if horizon facet is coplanar at last use */ + flagT mergehorizon:1; /* True if will merge into horizon (->coplanar) */ + 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 */ + 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) */ +}; + + +/*-<a href="qh-poly.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 +*/ +struct ridgeT { + setT *vertices; /* vertices belonging to this ridge, inverse sorted by ID + NULL if a degen ridge (matchsame) */ + facetT *top; /* top facet this ridge is part of */ + facetT *bottom; /* bottom facet this ridge is part of */ + unsigned id; /* unique identifier. Same size as vertex_id and ridge_id */ + flagT seen:1; /* used to perform operations only once */ + flagT tested:1; /* True when ridge is tested for convexity */ + flagT nonconvex:1; /* True if getmergeset detected a non-convex neighbor + only one ridge between neighbors may have nonconvex */ +}; + +/*-<a href="qh-poly.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 +*/ +struct vertexT { + vertexT *next; /* next vertex in vertex_list */ + vertexT *previous; /* previous vertex in vertex_list */ + pointT *point; /* hull_dim coordinates (coordT) */ + setT *neighbors; /* neighboring facets of vertex, qh_vertexneighbors() + inits in io.c or after first merge */ + unsigned id; /* unique identifier. Same size as qh.vertex_id and qh.ridge_id */ + unsigned 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 delridge:1; /* vertex was part of a deleted ridge */ + flagT deleted:1; /* true if vertex on qh.del_vertices */ + flagT newlist:1; /* true if vertex on qh.newvertex_list */ +}; + +/*======= -global variables -qh ============================*/ + +/*-<a href="qh-globa.htm#TOC" + >--------------------------------</a><a name="qh">-</a> + + qh + all global variables for qhull are in qh, qhmem, and qhstat + + notes: + qhmem is defined in mem.h, qhstat is defined in stat.h, qhrbox is defined in rboxpoints.h + Access to qh_qh is via the "qh" macro. See qh_QHpointer in user.h + + All global variables for qhull are in qh, qhmem, and qhstat + qh must be unique for each instance of qhull + qhstat may be shared between qhull instances. + qhmem may be shared across multiple instances of Qhull. + Rbox uses global variables rbox_inuse and rbox, but does not persist data across calls. + + Qhull is not multi-threaded. Global state could be stored in thread-local storage. + + QHULL_LIB_CHECK checks that a program and the corresponding + qhull library were built with the same type of header files. +*/ + +typedef struct qhT qhT; + +#define QHULL_NON_REENTRANT 0 +#define QHULL_QH_POINTER 1 +#define QHULL_REENTRANT 2 + +#if qh_QHpointer_dllimport +#define qh qh_qh-> +__declspec(dllimport) extern qhT *qh_qh; /* allocated in global.c */ +#define QHULL_LIB_TYPE QHULL_QH_POINTER + +#elif qh_QHpointer +#define qh qh_qh-> +extern qhT *qh_qh; /* allocated in global.c */ +#define QHULL_LIB_TYPE QHULL_QH_POINTER + +#elif qh_dllimport +#define qh qh_qh. +__declspec(dllimport) extern qhT qh_qh; /* allocated in global.c */ +#define QHULL_LIB_TYPE QHULL_NON_REENTRANT + +#else +#define qh qh_qh. +extern qhT qh_qh; +#define QHULL_LIB_TYPE QHULL_NON_REENTRANT +#endif + +#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.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.htm#options defines the flags. +*/ + boolT ALLpoints; /* true 'Qs' if search all points for initial simplex */ + boolT ANGLEmerge; /* true 'Qa' if sort potential merges by angle */ + boolT APPROXhull; /* true 'Wn' if MINoutside set */ + realT MINoutside; /* 'Wn' min. distance for an outside point */ + 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 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' if computing DELAUNAY triangulation */ + boolT DOintersections; /* true 'Gh' if print hyperplane intersections */ + int DROPdim; /* drops dim 'GDn' for 4-d -> 3-d output */ + boolT FORCEoutput; /* true 'Po' if forcing output despite degeneracies */ + int GOODpoint; /* 1+n for 'QGn', good facet if visible/not(-) from point n*/ + pointT *GOODpointp; /* the actual point */ + boolT GOODthreshold; /* true if qh.lower_threshold/upper_threshold defined + false if qh.SPLITthreshold */ + int GOODvertex; /* 1+n, good facet if vertex for point n */ + pointT *GOODvertexp; /* the actual point */ + boolT HALFspace; /* true 'Hn,n,n' if halfspace intersection */ + boolT ISqhullQh; /* Set by Qhull.cpp on initialization */ + int IStracing; /* 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*/ + boolT MERGEexact; /* true 'Qx' if exact merges (coplanar, degen, dupridge, flipped) */ + boolT MERGEindependent; /* true 'Q2' if merging independent sets */ + 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 MERGEvertices; /* true 'Q3' if merging redundant vertices */ + 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 */ + boolT NOpremerge; /* true 'Q0' if no defaults for C-0 or Qx */ + boolT NOwide; /* true 'Q12' if no error on wide merge due to duplicate ridge */ + 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 */ + 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 QUICKhelp; /* true if quick help message for degen input */ + boolT RANDOMdist; /* true if randomly change distplane and setfacetplane */ + realT RANDOMfactor; /* maximum random perturbation */ + realT RANDOMa; /* qh_randomfactor is randr * RANDOMa + RANDOMb */ + realT RANDOMb; + boolT RANDOMoutside; /* true if select a random outside point */ + int REPORTfreq; /* 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' seed, 0 time, >= rotate input */ + boolT SCALEinput; /* true 'Qbk' if scaling input */ + boolT SCALElast; /* true 'Qbb' if scale last coord to max prev coord */ + boolT SETroundoff; /* true 'E' if qh.DISTround is predefined */ + boolT SKIPcheckmax; /* true 'Q5' if skip qh_check_maxout */ + boolT SKIPconvex; /* true 'Q6' if skip convexity testing during pre-merge */ + boolT SPLITthresholds; /* true if upper_/lower_threshold defines a region + used only for printing (!for qh.ONLYgood) */ + 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 */ + 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 */ + + /*--------input constants ---------*/ + realT AREAfactor; /* 1/(hull_dim-1)! for converting det's to area */ + boolT DOcheckmax; /* true if calling qh_check_maxout (qh_initqhull_globals) */ + 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.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'. sets ZEROall_ok */ + realT *upper_threshold; /* don't print if facet->normal[k]>=upper_threshold[k] + must set either GOODthreshold or SPLITthreshold + if Delaunay, default is 0.0 for upper envelope */ + 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.htm#TOC" + >--------------------------------</a><a name="qh-prec">-</a> + + qh precision constants + precision constants for Qhull + + notes: + qh_detroundoff() 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 (roundoff added) */ + realT cos_max; /* max cosine for convexity (roundoff added) */ + realT DISTround; /* max round off error for distances, 'E' overrides qh_distround() */ + realT MAXabs_coord; /* max absolute coordinate */ + realT MAXlastcoord; /* max last coordinate for qh_scalelast */ + 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 */ + boolT NARROWhull; /* set in qh_initialhull if angle < qh_MAXnarrow */ + 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 */ + +/*-<a href="qh-globa.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.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) */ + 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 */ + facetT *visible_list; /* list of visible facets preceding newfacet_list, + facet->visible set */ + int num_visible; /* current number of visible facets */ + unsigned tracefacet_id; /* set at init, then can print whenever */ + facetT *tracefacet; /* set in newfacet/mergefacet, undone in delfacet*/ + unsigned tracevertex_id; /* set at buildtracing, can print whenever */ + vertexT *tracevertex; /* set in newvertex, undone in delvertex*/ + vertexT *vertex_list; /* list of all vertices, to vertex_tail */ + vertexT *vertex_tail; /* end of vertex_list (dummy vertex) */ + vertexT *newvertex_list; /* list of vertices in newfacet_list, to vertex_tail + all vertices have 'newlist' 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 findgood_all) */ + unsigned facet_id; /* ID of next, new facet from newfacet() */ + unsigned ridge_id; /* ID of next, new ridge from newridge() */ + unsigned vertex_id; /* ID of next, new vertex from newvertex() */ + +/*-<a href="qh-globa.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 to avoid wrap-around errors */ + boolT ALLOWrestart; /* true if qh_precision 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 */ + facetT *GOODclosest; /* closest facet to GOODthreshold in qh_findgood */ + boolT hasAreaVolume; /* true if totarea, totvol was defined by qh_getarea */ + boolT hasTriangulation; /* true if triangulation created by qh_triangulate */ + realT JOGGLEmax; /* set 'QJn' if randomly joggle input */ + 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 makecone/attachnewfacets to deletevisible */ + 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 */ + 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 */ + 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 ZEROall_ok; /* True if qh_checkzero always succeeds */ + boolT WAScoplanar; /* True if qh_partitioncoplanar (qh_check_maxout) */ + +/*-<a href="qh-globa.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 *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. Have deleted set for checkfacet */ + +/*-<a href="qh-globa.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.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.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 */ + 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; + unsigned lastreport; /* for qh_buildtracing */ + int mergereport; /* for qh_tracemerging */ + qhstatT *old_qhstat; /* for saving qh_qhstat in save_qhull() and UsingLibQhull. Free with qh_free() */ + setT *old_tempstack; /* for saving qhmem.tempstack in save_qhull */ + int ridgeoutnum; /* number of ridges for 4OFF output (qh_printbegin,etc) */ +}; + +/*=========== -macros- =========================*/ + +/*-<a href="qh-poly.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.htm#TOC" + >--------------------------------</a><a name="getid_">-</a> + + getid_(p) + return int ID for facet, ridge, or vertex + return qh_IDunknown(-1) if NULL +*/ +#define getid_(p) ((p) ? (int)((p)->id) : qh_IDunknown) + +/*============== FORALL macros ===================*/ + +/*-<a href="qh-poly.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 + + see: + FORALLfacet_( facetlist ) +*/ +#define FORALLfacets for (facet=qh facet_list;facet && facet->next;facet=facet->next) + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="FORALLpoints">-</a> + + FORALLpoints { ... } + assign 'point' to each point in qh.first_point, qh.num_points + + declare: + coordT *point, *pointtemp; +*/ +#define FORALLpoints FORALLpoint_(qh first_point, qh num_points) + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="FORALLpoint_">-</a> + + FORALLpoint_( points, num) { ... } + assign 'point' to each point in points array of num points + + declare: + coordT *point, *pointtemp; +*/ +#define FORALLpoint_(points, num) for (point= (points), \ + pointtemp= (points)+qh hull_dim*(num); point < pointtemp; point += qh hull_dim) + +/*-<a href="qh-poly.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 with a sentinel +*/ +#define FORALLvertices for (vertex=qh vertex_list;vertex && vertex->next;vertex= vertex->next) + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="FOREACHfacet_">-</a> + + FOREACHfacet_( facets ) { ... } + assign 'facet' to each facet in facets + + declare: + facetT *facet, **facetp; + + see: + <a href="qset.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHfacet_(facets) FOREACHsetelement_(facetT, facets, facet) + +/*-<a href="qh-poly.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; + + see: + <a href="qset.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHneighbor_(facet) FOREACHsetelement_(facetT, facet->neighbors, neighbor) + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="FOREACHpoint_">-</a> + + FOREACHpoint_( points ) { ... } + assign 'point' to each point in points set + + declare: + pointT *point, **pointp; + + see: + <a href="qset.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHpoint_(points) FOREACHsetelement_(pointT, points, point) + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="FOREACHridge_">-</a> + + FOREACHridge_( ridges ) { ... } + assign 'ridge' to each ridge in ridges set + + declare: + ridgeT *ridge, **ridgep; + + see: + <a href="qset.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHridge_(ridges) FOREACHsetelement_(ridgeT, ridges, ridge) + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="FOREACHvertex_">-</a> + + FOREACHvertex_( vertices ) { ... } + assign 'vertex' to each vertex in vertices set + + declare: + vertexT *vertex, **vertexp; + + see: + <a href="qset.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHvertex_(vertices) FOREACHsetelement_(vertexT, vertices,vertex) + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="FOREACHfacet_i_">-</a> + + FOREACHfacet_i_( facets ) { ... } + assign 'facet' and 'facet_i' for each facet in facets set + + declare: + facetT *facet; + int facet_n, facet_i; + + see: + <a href="qset.h#FOREACHsetelement_i_">FOREACHsetelement_i_</a> +*/ +#define FOREACHfacet_i_(facets) FOREACHsetelement_i_(facetT, facets, facet) + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="FOREACHneighbor_i_">-</a> + + FOREACHneighbor_i_( facet ) { ... } + assign 'neighbor' and 'neighbor_i' for each neighbor in facet->neighbors + + FOREACHneighbor_i_( vertex ) { ... } + assign 'neighbor' and 'neighbor_i' for each neighbor in vertex->neighbors + + declare: + facetT *neighbor; + int neighbor_n, neighbor_i; + + see: + <a href="qset.h#FOREACHsetelement_i_">FOREACHsetelement_i_</a> +*/ +#define FOREACHneighbor_i_(facet) FOREACHsetelement_i_(facetT, facet->neighbors, neighbor) + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="FOREACHpoint_i_">-</a> + + FOREACHpoint_i_( points ) { ... } + assign 'point' and 'point_i' for each point in points set + + declare: + pointT *point; + int point_n, point_i; + + see: + <a href="qset.h#FOREACHsetelement_i_">FOREACHsetelement_i_</a> +*/ +#define FOREACHpoint_i_(points) FOREACHsetelement_i_(pointT, points, point) + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="FOREACHridge_i_">-</a> + + FOREACHridge_i_( ridges ) { ... } + assign 'ridge' and 'ridge_i' for each ridge in ridges set + + declare: + ridgeT *ridge; + int ridge_n, ridge_i; + + see: + <a href="qset.h#FOREACHsetelement_i_">FOREACHsetelement_i_</a> +*/ +#define FOREACHridge_i_(ridges) FOREACHsetelement_i_(ridgeT, ridges, ridge) + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="FOREACHvertex_i_">-</a> + + FOREACHvertex_i_( vertices ) { ... } + assign 'vertex' and 'vertex_i' for each vertex in vertices set + + declare: + vertexT *vertex; + int vertex_n, vertex_i; + + see: + <a href="qset.h#FOREACHsetelement_i_">FOREACHsetelement_i_</a> +*/ +#define FOREACHvertex_i_(vertices) FOREACHsetelement_i_(vertexT, vertices,vertex) + +/********* -libqhull.c prototypes (duplicated from qhull_a.h) **********************/ + +void qh_qhull(void); +boolT qh_addpoint(pointT *furthest, facetT *facet, boolT checkdist); +void qh_printsummary(FILE *fp); + +/********* -user.c prototypes (alphabetical) **********************/ + +void qh_errexit(int exitcode, facetT *facet, ridgeT *ridge); +void qh_errprint(const char* string, facetT *atfacet, facetT *otherfacet, ridgeT *atridge, vertexT *atvertex); +int qh_new_qhull(int dim, int numpoints, coordT *points, boolT ismalloc, + char *qhull_cmd, FILE *outfile, FILE *errfile); +void qh_printfacetlist(facetT *facetlist, setT *facets, boolT printall); +void qh_printhelp_degenerate(FILE *fp); +void qh_printhelp_narrowhull(FILE *fp, realT minangle); +void qh_printhelp_singular(FILE *fp); +void qh_user_memsizes(void); + +/********* -usermem.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.c and userprintf_rbox.c prototypes **********************/ +void qh_fprintf(FILE *fp, int msgcode, const char *fmt, ... ); +void qh_fprintf_rbox(FILE *fp, int msgcode, const char *fmt, ... ); + +/***** -geom.c/geom2.c/random.c prototypes (duplicated from geom.h, random.h) ****************/ + +facetT *qh_findbest(pointT *point, facetT *startfacet, + boolT bestoutside, boolT newfacets, boolT noupper, + realT *dist, boolT *isoutside, int *numpart); +facetT *qh_findbestnew(pointT *point, facetT *startfacet, + realT *dist, boolT bestoutside, boolT *isoutside, int *numpart); +boolT qh_gram_schmidt(int dim, realT **rows); +void qh_outerinner(facetT *facet, realT *outerplane, realT *innerplane); +void qh_printsummary(FILE *fp); +void qh_projectinput(void); +void qh_randommatrix(realT *buffer, int dim, realT **row); +void qh_rotateinput(realT **rows); +void qh_scaleinput(void); +void qh_setdelaunay(int dim, int count, pointT *points); +coordT *qh_sethalfspace_all(int dim, int count, coordT *halfspaces, pointT *feasible); + +/***** -global.c prototypes (alphabetical) ***********************/ + +unsigned long qh_clock(void); +void qh_checkflags(char *command, char *hiddenflags); +void qh_clear_outputflags(void); +void qh_freebuffers(void); +void qh_freeqhull(boolT allmem); +void qh_freeqhull2(boolT allmem); +void qh_init_A(FILE *infile, FILE *outfile, FILE *errfile, int argc, char *argv[]); +void qh_init_B(coordT *points, int numpoints, int dim, boolT ismalloc); +void qh_init_qhull_command(int argc, char *argv[]); +void qh_initbuffers(coordT *points, int numpoints, int dim, boolT ismalloc); +void qh_initflags(char *command); +void qh_initqhull_buffers(void); +void qh_initqhull_globals(coordT *points, int numpoints, int dim, boolT ismalloc); +void qh_initqhull_mem(void); +void qh_initqhull_outputflags(void); +void qh_initqhull_start(FILE *infile, FILE *outfile, FILE *errfile); +void qh_initqhull_start2(FILE *infile, FILE *outfile, FILE *errfile); +void qh_initthresholds(char *command); +void qh_lib_check(int qhullLibraryType, int qhTsize, int vertexTsize, int ridgeTsize, int facetTsize, int setTsize, int qhmemTsize); +void qh_option(const char *option, int *i, realT *r); +#if qh_QHpointer +void qh_restore_qhull(qhT **oldqh); +qhT *qh_save_qhull(void); +#endif + +/***** -io.c prototypes (duplicated from io.h) ***********************/ + +void qh_dfacet(unsigned id); +void qh_dvertex(unsigned id); +void qh_printneighborhood(FILE *fp, qh_PRINT format, facetT *facetA, facetT *facetB, boolT printall); +void qh_produce_output(void); +coordT *qh_readpoints(int *numpoints, int *dimension, boolT *ismalloc); + + +/********* -mem.c prototypes (duplicated from mem.h) **********************/ + +void qh_meminit(FILE *ferr); +void qh_memfreeshort(int *curlong, int *totlong); + +/********* -poly.c/poly2.c prototypes (duplicated from poly.h) **********************/ + +void qh_check_output(void); +void qh_check_points(void); +setT *qh_facetvertices(facetT *facetlist, setT *facets, boolT allfacets); +facetT *qh_findbestfacet(pointT *point, boolT bestoutside, + realT *bestdist, boolT *isoutside); +vertexT *qh_nearvertex(facetT *facet, pointT *point, realT *bestdistp); +pointT *qh_point(int id); +setT *qh_pointfacet(void /*qh.facet_list*/); +int qh_pointid(pointT *point); +setT *qh_pointvertex(void /*qh.facet_list*/); +void qh_setvoronoi_all(void); +void qh_triangulate(void /*qh.facet_list*/); + +/********* -rboxlib.c prototypes **********************/ +int qh_rboxpoints(FILE* fout, FILE* ferr, char* rbox_command); +void qh_errexit_rbox(int exitcode); + +/********* -stat.c prototypes (duplicated from stat.h) **********************/ + +void qh_collectstatistics(void); +void qh_printallstatistics(FILE *fp, const char *string); + +#endif /* qhDEFlibqhull */ diff --git a/src/qhull/src/libqhull/libqhull.pro b/src/qhull/src/libqhull/libqhull.pro new file mode 100644 index 000000000..18005da59 --- /dev/null +++ b/src/qhull/src/libqhull/libqhull.pro @@ -0,0 +1,67 @@ +# ------------------------------------------------- +# libqhull.pro -- Qt project for Qhull shared library +# ------------------------------------------------- + +include(../qhull-warn.pri) + +DESTDIR = ../../lib +DLLDESTDIR = ../../bin +TEMPLATE = lib +CONFIG += shared warn_on +CONFIG -= qt + +build_pass:CONFIG(debug, debug|release):{ + TARGET = qhull_d + OBJECTS_DIR = Debug +}else:build_pass:CONFIG(release, debug|release):{ + TARGET = qhull + OBJECTS_DIR = Release +} +win32-msvc* : QMAKE_LFLAGS += /INCREMENTAL:NO + +win32-msvc* : DEF_FILE += ../../src/libqhull/qhull-exports.def + +# Order object files by frequency of execution. Small files at end. + +# libqhull/libqhull.pro and ../qhull-libqhull-src.pri have the same SOURCES and HEADERS +SOURCES += ../libqhull/global.c +SOURCES += ../libqhull/stat.c +SOURCES += ../libqhull/geom2.c +SOURCES += ../libqhull/poly2.c +SOURCES += ../libqhull/merge.c +SOURCES += ../libqhull/libqhull.c +SOURCES += ../libqhull/geom.c +SOURCES += ../libqhull/poly.c +SOURCES += ../libqhull/qset.c +SOURCES += ../libqhull/mem.c +SOURCES += ../libqhull/random.c +SOURCES += ../libqhull/usermem.c +SOURCES += ../libqhull/userprintf.c +SOURCES += ../libqhull/io.c +SOURCES += ../libqhull/user.c +SOURCES += ../libqhull/rboxlib.c +SOURCES += ../libqhull/userprintf_rbox.c + +HEADERS += ../libqhull/geom.h +HEADERS += ../libqhull/io.h +HEADERS += ../libqhull/libqhull.h +HEADERS += ../libqhull/mem.h +HEADERS += ../libqhull/merge.h +HEADERS += ../libqhull/poly.h +HEADERS += ../libqhull/random.h +HEADERS += ../libqhull/qhull_a.h +HEADERS += ../libqhull/qset.h +HEADERS += ../libqhull/stat.h +HEADERS += ../libqhull/user.h + +OTHER_FILES += Mborland +OTHER_FILES += qh-geom.htm +OTHER_FILES += qh-globa.htm +OTHER_FILES += qh-io.htm +OTHER_FILES += qh-mem.htm +OTHER_FILES += qh-merge.htm +OTHER_FILES += qh-poly.htm +OTHER_FILES += qh-qhull.htm +OTHER_FILES += qh-set.htm +OTHER_FILES += qh-stat.htm +OTHER_FILES += qh-user.htm diff --git a/src/qhull/src/libqhull/mem.c b/src/qhull/src/libqhull/mem.c new file mode 100644 index 000000000..db72bb4e1 --- /dev/null +++ b/src/qhull/src/libqhull/mem.c @@ -0,0 +1,576 @@ +/*<html><pre> -<a href="qh-mem.htm" + >-------------------------------</a><a name="TOP">-</a> + + mem.c + memory management routines for qhull + + This is a standalone program. + + To initialize memory: + + qh_meminit(stderr); + qh_meminitbuffers(qh IStracing, qh_MEMalign, 7, qh_MEMbufsize,qh_MEMinitbuf); + qh_memsize((int)sizeof(facetT)); + qh_memsize((int)sizeof(facetT)); + ... + qh_memsetup(); + + To free up all memory buffers: + qh_memfreeshort(&curlong, &totlong); + + if qh_NOmem, + malloc/free is used instead of mem.c + + notes: + uses Quickfit algorithm (freelists for commonly allocated sizes) + assumes small sizes for freelists (it discards the tail of memory buffers) + + see: + qh-mem.htm and mem.h + global.c (qh_initbuffers) for an example of using mem.c + + Copyright (c) 1993-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull/mem.c#7 $$Change: 2065 $ + $DateTime: 2016/01/18 13:51:04 $$Author: bbarber $ +*/ + +#include "user.h" /* for QHULL_CRTDBG */ +#include "mem.h" +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +#ifndef qhDEFlibqhull +typedef struct ridgeT ridgeT; +typedef struct facetT facetT; +#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 +void qh_errexit(int exitcode, facetT *, ridgeT *); +void qh_exit(int exitcode); +void qh_fprintf(FILE *fp, int msgcode, const char *fmt, ... ); +void qh_fprintf_stderr(int msgcode, const char *fmt, ... ); +void qh_free(void *mem); +void *qh_malloc(size_t size); +#endif + +/*============ -global data structure ============== + see mem.h for definition +*/ + +qhmemT qhmem= {0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0}; /* remove "= {0}" if this causes a compiler error */ + +#ifndef qh_NOmem + +/*============= internal functions ==============*/ + +static int qh_intcompare(const void *i, const void *j); + +/*========== functions in alphabetical order ======== */ + +/*-<a href="qh-mem.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.htm#TOC" + >--------------------------------</a><a name="memalloc">-</a> + + qh_memalloc( 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 < qhmem.LASTsize + if qhmem.freelists[size] non-empty + return first object on freelist + else + round up request to size of qhmem.freelists[size] + allocate new allocation buffer if necessary + allocate object from allocation buffer + else + allocate object with qh_malloc() in user.c +*/ +void *qh_memalloc(int insize) { + void **freelistp, *newbuffer; + int idx, size, n; + int outsize, bufsize; + void *object; + + if (insize<0) { + qh_fprintf(qhmem.ferr, 6235, "qhull error (qh_memalloc): negative request size (%d). Did int overflow due to high-D?\n", insize); /* WARN64 */ + qh_errexit(qhmem_ERRmem, NULL, NULL); + } + if (insize>=0 && insize <= qhmem.LASTsize) { + idx= qhmem.indextable[insize]; + outsize= qhmem.sizetable[idx]; + qhmem.totshort += outsize; + freelistp= qhmem.freelists+idx; + if ((object= *freelistp)) { + qhmem.cntquick++; + qhmem.totfree -= outsize; + *freelistp= *((void **)*freelistp); /* replace freelist with next object */ +#ifdef qh_TRACEshort + n= qhmem.cntshort+qhmem.cntquick+qhmem.freeshort; + if (qhmem.IStracing >= 5) + qh_fprintf(qhmem.ferr, 8141, "qh_mem %p n %8d alloc quick: %d bytes (tot %d cnt %d)\n", object, n, outsize, qhmem.totshort, qhmem.cntshort+qhmem.cntquick-qhmem.freeshort); +#endif + return(object); + }else { + qhmem.cntshort++; + if (outsize > qhmem.freesize) { + qhmem.totdropped += qhmem.freesize; + if (!qhmem.curbuffer) + bufsize= qhmem.BUFinit; + else + bufsize= qhmem.BUFsize; + if (!(newbuffer= qh_malloc((size_t)bufsize))) { + qh_fprintf(qhmem.ferr, 6080, "qhull error (qh_memalloc): insufficient memory to allocate short memory buffer (%d bytes)\n", bufsize); + qh_errexit(qhmem_ERRmem, NULL, NULL); + } + *((void **)newbuffer)= qhmem.curbuffer; /* prepend newbuffer to curbuffer + list. newbuffer!=0 by QH6080 */ + qhmem.curbuffer= newbuffer; + size= (sizeof(void **) + qhmem.ALIGNmask) & ~qhmem.ALIGNmask; + qhmem.freemem= (void *)((char *)newbuffer+size); + qhmem.freesize= bufsize - size; + qhmem.totbuffer += bufsize - size; /* easier to check */ + /* Periodically test totbuffer. It matches at beginning and exit of every call */ + n = qhmem.totshort + qhmem.totfree + qhmem.totdropped + qhmem.freesize - outsize; + if (qhmem.totbuffer != n) { + qh_fprintf(qhmem.ferr, 6212, "qh_memalloc internal error: short totbuffer %d != totshort+totfree... %d\n", qhmem.totbuffer, n); + qh_errexit(qhmem_ERRmem, NULL, NULL); + } + } + object= qhmem.freemem; + qhmem.freemem= (void *)((char *)qhmem.freemem + outsize); + qhmem.freesize -= outsize; + qhmem.totunused += outsize - insize; +#ifdef qh_TRACEshort + n= qhmem.cntshort+qhmem.cntquick+qhmem.freeshort; + if (qhmem.IStracing >= 5) + qh_fprintf(qhmem.ferr, 8140, "qh_mem %p n %8d alloc short: %d bytes (tot %d cnt %d)\n", object, n, outsize, qhmem.totshort, qhmem.cntshort+qhmem.cntquick-qhmem.freeshort); +#endif + return object; + } + }else { /* long allocation */ + if (!qhmem.indextable) { + qh_fprintf(qhmem.ferr, 6081, "qhull internal error (qh_memalloc): qhmem has not been initialized.\n"); + qh_errexit(qhmem_ERRqhull, NULL, NULL); + } + outsize= insize; + qhmem.cntlong++; + qhmem.totlong += outsize; + if (qhmem.maxlong < qhmem.totlong) + qhmem.maxlong= qhmem.totlong; + if (!(object= qh_malloc((size_t)outsize))) { + qh_fprintf(qhmem.ferr, 6082, "qhull error (qh_memalloc): insufficient memory to allocate %d bytes\n", outsize); + qh_errexit(qhmem_ERRmem, NULL, NULL); + } + if (qhmem.IStracing >= 5) + qh_fprintf(qhmem.ferr, 8057, "qh_mem %p n %8d alloc long: %d bytes (tot %d cnt %d)\n", object, qhmem.cntlong+qhmem.freelong, outsize, qhmem.totlong, qhmem.cntlong-qhmem.freelong); + } + return(object); +} /* memalloc */ + + +/*-<a href="qh-mem.htm#TOC" + >--------------------------------</a><a name="memcheck">-</a> + + qh_memcheck( ) +*/ +void qh_memcheck(void) { + int i, count, totfree= 0; + void *object; + + if (qhmem.ferr == 0 || qhmem.IStracing < 0 || qhmem.IStracing > 10 || (((qhmem.ALIGNmask+1) & qhmem.ALIGNmask) != 0)) { + qh_fprintf_stderr(6244, "qh_memcheck error: either qhmem is overwritten or qhmem is not initialized. Call qh_meminit() or qh_new_qhull() before calling qh_mem routines. ferr 0x%x IsTracing %d ALIGNmask 0x%x", qhmem.ferr, qhmem.IStracing, qhmem.ALIGNmask); + qh_exit(qhmem_ERRqhull); /* can not use qh_errexit() */ + } + if (qhmem.IStracing != 0) + qh_fprintf(qhmem.ferr, 8143, "qh_memcheck: check size of freelists on qhmem\nqh_memcheck: A segmentation fault indicates an overwrite of qhmem\n"); + for (i=0; i < qhmem.TABLEsize; i++) { + count=0; + for (object= qhmem.freelists[i]; object; object= *((void **)object)) + count++; + totfree += qhmem.sizetable[i] * count; + } + if (totfree != qhmem.totfree) { + qh_fprintf(qhmem.ferr, 6211, "Qhull internal error (qh_memcheck): totfree %d not equal to freelist total %d\n", qhmem.totfree, totfree); + qh_errexit(qhmem_ERRqhull, NULL, NULL); + } + if (qhmem.IStracing != 0) + qh_fprintf(qhmem.ferr, 8144, "qh_memcheck: total size of freelists totfree is the same as qhmem.totfree\n", totfree); +} /* memcheck */ + +/*-<a href="qh-mem.htm#TOC" + >--------------------------------</a><a name="memfree">-</a> + + qh_memfree( 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 <= qhmem.LASTsize + append object to corresponding freelist + else + call qh_free(object) +*/ +void qh_memfree(void *object, int insize) { + void **freelistp; + int idx, outsize; + + if (!object) + return; + if (insize <= qhmem.LASTsize) { + qhmem.freeshort++; + idx= qhmem.indextable[insize]; + outsize= qhmem.sizetable[idx]; + qhmem.totfree += outsize; + qhmem.totshort -= outsize; + freelistp= qhmem.freelists + idx; + *((void **)object)= *freelistp; + *freelistp= object; +#ifdef qh_TRACEshort + idx= qhmem.cntshort+qhmem.cntquick+qhmem.freeshort; + if (qhmem.IStracing >= 5) + qh_fprintf(qhmem.ferr, 8142, "qh_mem %p n %8d free short: %d bytes (tot %d cnt %d)\n", object, idx, outsize, qhmem.totshort, qhmem.cntshort+qhmem.cntquick-qhmem.freeshort); +#endif + }else { + qhmem.freelong++; + qhmem.totlong -= insize; + if (qhmem.IStracing >= 5) + qh_fprintf(qhmem.ferr, 8058, "qh_mem %p n %8d free long: %d bytes (tot %d cnt %d)\n", object, qhmem.cntlong+qhmem.freelong, insize, qhmem.totlong, qhmem.cntlong-qhmem.freelong); + qh_free(object); + } +} /* memfree */ + + +/*-<a href="qh-mem.htm#TOC" + >-------------------------------</a><a name="memfreeshort">-</a> + + qh_memfreeshort( curlong, totlong ) + frees up all short and qhmem memory allocations + + returns: + number and size of current long allocations + + see: + qh_freeqhull(allMem) + qh_memtotal(curlong, totlong, curshort, totshort, maxlong, totbuffer); +*/ +void qh_memfreeshort(int *curlong, int *totlong) { + void *buffer, *nextbuffer; + FILE *ferr; + + *curlong= qhmem.cntlong - qhmem.freelong; + *totlong= qhmem.totlong; + for (buffer= qhmem.curbuffer; buffer; buffer= nextbuffer) { + nextbuffer= *((void **) buffer); + qh_free(buffer); + } + qhmem.curbuffer= NULL; + if (qhmem.LASTsize) { + qh_free(qhmem.indextable); + qh_free(qhmem.freelists); + qh_free(qhmem.sizetable); + } + ferr= qhmem.ferr; + memset((char *)&qhmem, 0, sizeof(qhmem)); /* every field is 0, FALSE, NULL */ + qhmem.ferr= ferr; +} /* memfreeshort */ + + +/*-<a href="qh-mem.htm#TOC" + >--------------------------------</a><a name="meminit">-</a> + + qh_meminit( ferr ) + initialize qhmem and test sizeof( void*) + Does not throw errors. qh_exit on failure +*/ +void qh_meminit(FILE *ferr) { + + memset((char *)&qhmem, 0, sizeof(qhmem)); /* every field is 0, FALSE, NULL */ + if (ferr) + qhmem.ferr= ferr; + else + qhmem.ferr= stderr; + if (sizeof(void*) < sizeof(int)) { + qh_fprintf(qhmem.ferr, 6083, "qhull internal error (qh_meminit): sizeof(void*) %d < sizeof(int) %d. qset.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(qhmem.ferr, 6084, "qhull internal error (qh_meminit): sizeof(void*) %d > sizeof(ptr_intT) %d. Change ptr_intT in mem.h to 'long long'\n", (int)sizeof(void*), (int)sizeof(ptr_intT)); + qh_exit(qhmem_ERRqhull); /* can not use qh_errexit() */ + } + qh_memcheck(); +} /* meminit */ + +/*-<a href="qh-mem.htm#TOC" + >-------------------------------</a><a name="meminitbuffers">-</a> + + qh_meminitbuffers( 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(int tracelevel, int alignment, int numsizes, int bufsize, int bufinit) { + + qhmem.IStracing= tracelevel; + qhmem.NUMsizes= numsizes; + qhmem.BUFsize= bufsize; + qhmem.BUFinit= bufinit; + qhmem.ALIGNmask= alignment-1; + if (qhmem.ALIGNmask & ~qhmem.ALIGNmask) { + qh_fprintf(qhmem.ferr, 6085, "qhull internal error (qh_meminit): memory alignment %d is not a power of 2\n", alignment); + qh_errexit(qhmem_ERRqhull, NULL, NULL); + } + qhmem.sizetable= (int *) calloc((size_t)numsizes, sizeof(int)); + qhmem.freelists= (void **) calloc((size_t)numsizes, sizeof(void *)); + if (!qhmem.sizetable || !qhmem.freelists) { + qh_fprintf(qhmem.ferr, 6086, "qhull error (qh_meminit): insufficient memory\n"); + qh_errexit(qhmem_ERRmem, NULL, NULL); + } + if (qhmem.IStracing >= 1) + qh_fprintf(qhmem.ferr, 8059, "qh_meminitbuffers: memory initialized with alignment %d\n", alignment); +} /* meminitbuffers */ + +/*-<a href="qh-mem.htm#TOC" + >-------------------------------</a><a name="memsetup">-</a> + + qh_memsetup() + set up memory after running memsize() +*/ +void qh_memsetup(void) { + int k,i; + + qsort(qhmem.sizetable, (size_t)qhmem.TABLEsize, sizeof(int), qh_intcompare); + qhmem.LASTsize= qhmem.sizetable[qhmem.TABLEsize-1]; + if (qhmem.LASTsize >= qhmem.BUFsize || qhmem.LASTsize >= qhmem.BUFinit) { + qh_fprintf(qhmem.ferr, 6087, "qhull error (qh_memsetup): largest mem size %d is >= buffer size %d or initial buffer size %d\n", + qhmem.LASTsize, qhmem.BUFsize, qhmem.BUFinit); + qh_errexit(qhmem_ERRmem, NULL, NULL); + } + if (!(qhmem.indextable= (int *)qh_malloc((qhmem.LASTsize+1) * sizeof(int)))) { + qh_fprintf(qhmem.ferr, 6088, "qhull error (qh_memsetup): insufficient memory\n"); + qh_errexit(qhmem_ERRmem, NULL, NULL); + } + for (k=qhmem.LASTsize+1; k--; ) + qhmem.indextable[k]= k; + i= 0; + for (k=0; k <= qhmem.LASTsize; k++) { + if (qhmem.indextable[k] <= qhmem.sizetable[i]) + qhmem.indextable[k]= i; + else + qhmem.indextable[k]= ++i; + } +} /* memsetup */ + +/*-<a href="qh-mem.htm#TOC" + >-------------------------------</a><a name="memsize">-</a> + + qh_memsize( size ) + define a free list for this size +*/ +void qh_memsize(int size) { + int k; + + if (qhmem.LASTsize) { + qh_fprintf(qhmem.ferr, 6089, "qhull error (qh_memsize): called after qhmem_setup\n"); + qh_errexit(qhmem_ERRqhull, NULL, NULL); + } + size= (size + qhmem.ALIGNmask) & ~qhmem.ALIGNmask; + for (k=qhmem.TABLEsize; k--; ) { + if (qhmem.sizetable[k] == size) + return; + } + if (qhmem.TABLEsize < qhmem.NUMsizes) + qhmem.sizetable[qhmem.TABLEsize++]= size; + else + qh_fprintf(qhmem.ferr, 7060, "qhull warning (memsize): free list table has room for only %d sizes\n", qhmem.NUMsizes); +} /* memsize */ + + +/*-<a href="qh-mem.htm#TOC" + >-------------------------------</a><a name="memstatistics">-</a> + + qh_memstatistics( fp ) + print out memory statistics + + Verifies that qhmem.totfree == sum of freelists +*/ +void qh_memstatistics(FILE *fp) { + int i; + int count; + void *object; + + qh_memcheck(); + qh_fprintf(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", + qhmem.cntquick, qhmem.cntshort, qhmem.cntlong, + qhmem.freeshort, qhmem.freelong, + qhmem.totshort, qhmem.totfree, + qhmem.totdropped + qhmem.freesize, qhmem.totunused, + qhmem.maxlong, qhmem.totlong, qhmem.cntlong - qhmem.freelong, + qhmem.totbuffer, qhmem.BUFsize, qhmem.BUFinit); + if (qhmem.cntlarger) { + qh_fprintf(fp, 9279, "%7d calls to qh_setlarger\n%7.2g average copy size\n", + qhmem.cntlarger, ((float)qhmem.totlarger)/(float)qhmem.cntlarger); + qh_fprintf(fp, 9280, " freelists(bytes->count):"); + } + for (i=0; i < qhmem.TABLEsize; i++) { + count=0; + for (object= qhmem.freelists[i]; object; object= *((void **)object)) + count++; + qh_fprintf(fp, 9281, " %d->%d", qhmem.sizetable[i], count); + } + qh_fprintf(fp, 9282, "\n\n"); +} /* memstatistics */ + + +/*-<a href="qh-mem.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(int insize) { + void *object; + + if (!(object= qh_malloc((size_t)insize))) { + qh_fprintf(qhmem.ferr, 6090, "qhull error (qh_memalloc): insufficient memory\n"); + qh_errexit(qhmem_ERRmem, NULL, NULL); + } + qhmem.cntlong++; + qhmem.totlong += insize; + if (qhmem.maxlong < qhmem.totlong) + qhmem.maxlong= qhmem.totlong; + if (qhmem.IStracing >= 5) + qh_fprintf(qhmem.ferr, 8060, "qh_mem %p n %8d alloc long: %d bytes (tot %d cnt %d)\n", object, qhmem.cntlong+qhmem.freelong, insize, qhmem.totlong, qhmem.cntlong-qhmem.freelong); + return object; +} + +void qh_memfree(void *object, int insize) { + + if (!object) + return; + qh_free(object); + qhmem.freelong++; + qhmem.totlong -= insize; + if (qhmem.IStracing >= 5) + qh_fprintf(qhmem.ferr, 8061, "qh_mem %p n %8d free long: %d bytes (tot %d cnt %d)\n", object, qhmem.cntlong+qhmem.freelong, insize, qhmem.totlong, qhmem.cntlong-qhmem.freelong); +} + +void qh_memfreeshort(int *curlong, int *totlong) { + *totlong= qhmem.totlong; + *curlong= qhmem.cntlong - qhmem.freelong; + memset((char *)&qhmem, 0, sizeof(qhmem)); /* every field is 0, FALSE, NULL */ +} + +void qh_meminit(FILE *ferr) { + + memset((char *)&qhmem, 0, sizeof(qhmem)); /* every field is 0, FALSE, NULL */ + if (ferr) + qhmem.ferr= ferr; + else + qhmem.ferr= stderr; + if (sizeof(void*) < sizeof(int)) { + qh_fprintf(qhmem.ferr, 6091, "qhull internal error (qh_meminit): sizeof(void*) %d < sizeof(int) %d. qset.c will not work\n", (int)sizeof(void*), (int)sizeof(int)); + qh_errexit(qhmem_ERRqhull, NULL, NULL); + } +} + +void qh_meminitbuffers(int tracelevel, int alignment, int numsizes, int bufsize, int bufinit) { + + qhmem.IStracing= tracelevel; +} + +void qh_memsetup(void) { + +} + +void qh_memsize(int size) { + +} + +void qh_memstatistics(FILE *fp) { + + qh_fprintf(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", + qhmem.cntlong, + qhmem.freelong, + qhmem.maxlong, qhmem.totlong, qhmem.cntlong - qhmem.freelong); +} + +#endif /* qh_NOmem */ + +/*-<a href="qh-mem.htm#TOC" +>-------------------------------</a><a name="memtotlong">-</a> + + qh_memtotal( 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 (UsingLibQhull.cpp) +*/ +void qh_memtotal(int *totlong, int *curlong, int *totshort, int *curshort, int *maxlong, int *totbuffer) { + *totlong= qhmem.totlong; + *curlong= qhmem.cntlong - qhmem.freelong; + *totshort= qhmem.totshort; + *curshort= qhmem.cntshort + qhmem.cntquick - qhmem.freeshort; + *maxlong= qhmem.maxlong; + *totbuffer= qhmem.totbuffer; +} /* memtotlong */ + diff --git a/src/qhull/src/libqhull/mem.h b/src/qhull/src/libqhull/mem.h new file mode 100644 index 000000000..453f319df --- /dev/null +++ b/src/qhull/src/libqhull/mem.h @@ -0,0 +1,222 @@ +/*<html><pre> -<a href="qh-mem.htm" + >-------------------------------</a><a name="TOP">-</a> + + mem.h + prototypes for memory management functions + + see qh-mem.htm, mem.c and qset.h + + for error handling, writes message and calls + qh_errexit(qhmem_ERRmem, NULL, NULL) if insufficient memory + and + qh_errexit(qhmem_ERRqhull, NULL, NULL) otherwise + + Copyright (c) 1993-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull/mem.h#2 $$Change: 2062 $ + $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $ +*/ + +#ifndef qhDEFmem +#define qhDEFmem 1 + +#include <stdio.h> + +/*-<a href="qh-mem.htm#TOC" + >-------------------------------</a><a name="NOmem">-</a> + + qh_NOmem + turn off quick-fit memory allocation + + notes: + mem.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.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.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.h#MEMalign">qh_MEMalign</a> in user.h for qhull's alignment +*/ + +#define qhmem_ERRmem 4 /* matches qh_ERRmem in libqhull.h */ +#define qhmem_ERRqhull 5 /* matches qh_ERRqhull in libqhull.h */ + +/*-<a href="qh-mem.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.c) + + notes: + WARN64 -- these notes indicate 64-bit issues + On 64-bit machines, a pointer may be larger than an 'int'. + qh_meminit()/mem.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 (_MSC_VER) && defined(_WIN64) +typedef long long ptr_intT; +#else +typedef long ptr_intT; +#endif + +/*-<a href="qh-mem.htm#TOC" + >--------------------------------</a><a name="qhmemT">-</a> + + qhmemT + global memory structure for mem.c + + notes: + users should ignore qhmem except for writing extensions + qhmem is allocated in mem.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 qhmem. +*/ +typedef struct qhmemT qhmemT; +extern qhmemT qhmem; + +#ifndef DEFsetT +#define DEFsetT 1 +typedef struct setT setT; /* defined in qset.h */ +#endif + +/* Update qhmem in mem.c if add or remove fields */ +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.htm#TOC" + >--------------------------------</a><a name="memalloc_">-</a> + + qh_memalloc_(insize, freelistp, object, type) + returns object of size bytes + assumes size<=qhmem.LASTsize and void **freelistp is a temp +*/ + +#if defined qh_NOmem +#define qh_memalloc_(insize, freelistp, object, type) {\ + object= (type*)qh_memalloc(insize); } +#elif defined qh_TRACEshort +#define qh_memalloc_(insize, freelistp, object, type) {\ + freelistp= NULL; /* Avoid warnings */ \ + object= (type*)qh_memalloc(insize); } +#else /* !qh_NOmem */ + +#define qh_memalloc_(insize, freelistp, object, type) {\ + freelistp= qhmem.freelists + qhmem.indextable[insize];\ + if ((object= (type*)*freelistp)) {\ + qhmem.totshort += qhmem.sizetable[qhmem.indextable[insize]]; \ + qhmem.totfree -= qhmem.sizetable[qhmem.indextable[insize]]; \ + qhmem.cntquick++; \ + *freelistp= *((void **)*freelistp);\ + }else object= (type*)qh_memalloc(insize);} +#endif + +/*-<a href="qh-mem.htm#TOC" + >--------------------------------</a><a name="memfree_">-</a> + + qh_memfree_(object, insize, freelistp) + free up an object + + notes: + object may be NULL + assumes size<=qhmem.LASTsize and void **freelistp is a temp +*/ +#if defined qh_NOmem +#define qh_memfree_(object, insize, freelistp) {\ + qh_memfree(object, insize); } +#elif defined qh_TRACEshort +#define qh_memfree_(object, insize, freelistp) {\ + freelistp= NULL; /* Avoid warnings */ \ + qh_memfree(object, insize); } +#else /* !qh_NOmem */ + +#define qh_memfree_(object, insize, freelistp) {\ + if (object) { \ + qhmem.freeshort++;\ + freelistp= qhmem.freelists + qhmem.indextable[insize];\ + qhmem.totshort -= qhmem.sizetable[qhmem.indextable[insize]]; \ + qhmem.totfree += qhmem.sizetable[qhmem.indextable[insize]]; \ + *((void **)object)= *freelistp;\ + *freelistp= object;}} +#endif + +/*=============== prototypes in alphabetical order ============*/ + +void *qh_memalloc(int insize); +void qh_memcheck(void); +void qh_memfree(void *object, int insize); +void qh_memfreeshort(int *curlong, int *totlong); +void qh_meminit(FILE *ferr); +void qh_meminitbuffers(int tracelevel, int alignment, int numsizes, + int bufsize, int bufinit); +void qh_memsetup(void); +void qh_memsize(int size); +void qh_memstatistics(FILE *fp); +void qh_memtotal(int *totlong, int *curlong, int *totshort, int *curshort, int *maxlong, int *totbuffer); + +#endif /* qhDEFmem */ diff --git a/src/qhull/src/libqhull/merge.c b/src/qhull/src/libqhull/merge.c new file mode 100644 index 000000000..22104dc03 --- /dev/null +++ b/src/qhull/src/libqhull/merge.c @@ -0,0 +1,3628 @@ +/*<html><pre> -<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="TOP">-</a> + + merge.c + merges non-convex facets + + see qh-merge.htm and merge.h + + other modules call qh_premerge() and qh_postmerge() + + the user may call qh_postmerge() to perform additional merges. + + To remove deleted facets and vertices (qhull() in libqhull.c): + qh_partitionvisible(!qh_ALL, &numoutside); // visible_list, newfacet_list + qh_deletevisible(); // qh.visible_list + qh_resetlists(False, qh_RESETvisible); // qh.visible_list newvertex_list newfacet_list + + assumes qh.CENTERtype= centrum + + merges occur in qh_mergefacet and in qh_mergecycle + vertex->neighbors not set until the first merge occurs + + Copyright (c) 1993-2015 C.B. Barber. + $Id: //main/2015/qhull/src/libqhull/merge.c#4 $$Change: 2064 $ + $DateTime: 2016/01/18 12:36:08 $$Author: bbarber $ +*/ + +#include "qhull_a.h" + +#ifndef qh_NOmerge + +/*===== functions(alphabetical after premerge and postmerge) ======*/ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="premerge">-</a> + + qh_premerge( apex, maxcentrum ) + pre-merge nonconvex facets in qh.newfacet_list for apex + maxcentrum defines coplanar and concave (qh_test_appendmerge) + + returns: + deleted facets added to qh.visible_list with facet->visible set + + notes: + uses globals, qh.MERGEexact, qh.PREmerge + + design: + mark duplicate ridges in qh.newfacet_list + merge facet cycles in qh.newfacet_list + merge duplicate ridges and concave facets in qh.newfacet_list + check merged facet cycles for degenerate and redundant facets + merge degenerate and redundant facets + collect coplanar and concave facets + merge concave, coplanar, degenerate, and redundant facets +*/ +void qh_premerge(vertexT *apex, realT maxcentrum, realT maxangle) { + boolT othermerge= False; + facetT *newfacet; + + if (qh ZEROcentrum && qh_checkzero(!qh_ALL)) + return; + trace2((qh ferr, 2008, "qh_premerge: premerge centrum %2.2g angle %2.2g for apex v%d facetlist f%d\n", + maxcentrum, maxangle, apex->id, getid_(qh newfacet_list))); + if (qh IStracing >= 4 && qh num_facets < 50) + qh_printlists(); + qh centrum_radius= maxcentrum; + qh cos_max= maxangle; + qh degen_mergeset= qh_settemp(qh TEMPsize); + qh facet_mergeset= qh_settemp(qh TEMPsize); + if (qh hull_dim >=3) { + qh_mark_dupridges(qh newfacet_list); /* facet_mergeset */ + qh_mergecycle_all(qh newfacet_list, &othermerge); + qh_forcedmerges(&othermerge /* qh.facet_mergeset */); + FORALLnew_facets { /* test samecycle merges */ + if (!newfacet->simplicial && !newfacet->mergeridge) + qh_degen_redundant_neighbors(newfacet, NULL); + } + if (qh_merge_degenredundant()) + othermerge= True; + }else /* qh.hull_dim == 2 */ + qh_mergecycle_all(qh newfacet_list, &othermerge); + qh_flippedmerges(qh newfacet_list, &othermerge); + if (!qh MERGEexact || zzval_(Ztotmerge)) { + zinc_(Zpremergetot); + qh POSTmerging= False; + qh_getmergeset_initial(qh newfacet_list); + qh_all_merges(othermerge, False); + } + qh_settempfree(&qh facet_mergeset); + qh_settempfree(&qh degen_mergeset); +} /* premerge */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="postmerge">-</a> + + qh_postmerge( reason, maxcentrum, maxangle, vneighbors ) + post-merge nonconvex facets as defined by maxcentrum and maxangle + 'reason' is for reporting progress + if vneighbors, + calls qh_test_vneighbors at end of qh_all_merge + if firstmerge, + calls qh_reducevertices before qh_getmergeset + + returns: + if first call (qh.visible_list != qh.facet_list), + builds qh.facet_newlist, qh.newvertex_list + deleted facets added to qh.visible_list with facet->visible + qh.visible_list == qh.facet_list + + notes: + + + design: + if first call + set qh.visible_list and qh.newfacet_list to qh.facet_list + add all facets to qh.newfacet_list + mark non-simplicial facets, facet->newmerge + set qh.newvertext_list to qh.vertex_list + add all vertices to qh.newvertex_list + if a pre-merge occured + set vertex->delridge {will retest the ridge} + if qh.MERGEexact + call qh_reducevertices() + if no pre-merging + merge flipped facets + determine non-convex facets + merge all non-convex facets +*/ +void qh_postmerge(const char *reason, realT maxcentrum, realT maxangle, + boolT vneighbors) { + facetT *newfacet; + boolT othermerges= False; + vertexT *vertex; + + if (qh REPORTfreq || qh IStracing) { + qh_buildtracing(NULL, NULL); + qh_printsummary(qh ferr); + if (qh PRINTstatistics) + qh_printallstatistics(qh ferr, "reason"); + qh_fprintf(qh ferr, 8062, "\n%s with 'C%.2g' and 'A%.2g'\n", + reason, maxcentrum, maxangle); + } + trace2((qh ferr, 2009, "qh_postmerge: postmerge. test vneighbors? %d\n", + vneighbors)); + qh centrum_radius= maxcentrum; + qh cos_max= maxangle; + qh POSTmerging= True; + qh degen_mergeset= qh_settemp(qh TEMPsize); + qh facet_mergeset= qh_settemp(qh TEMPsize); + if (qh visible_list != qh facet_list) { /* first call */ + qh NEWfacets= True; + qh visible_list= qh newfacet_list= qh facet_list; + FORALLnew_facets { + newfacet->newfacet= True; + if (!newfacet->simplicial) + newfacet->newmerge= True; + zinc_(Zpostfacets); + } + qh newvertex_list= qh vertex_list; + FORALLvertices + vertex->newlist= True; + if (qh VERTEXneighbors) { /* a merge has occurred */ + FORALLvertices + vertex->delridge= True; /* test for redundant, needed? */ + if (qh MERGEexact) { + if (qh hull_dim <= qh_DIMreduceBuild) + qh_reducevertices(); /* was skipped during pre-merging */ + } + } + if (!qh PREmerge && !qh MERGEexact) + qh_flippedmerges(qh newfacet_list, &othermerges); + } + qh_getmergeset_initial(qh newfacet_list); + qh_all_merges(False, vneighbors); + qh_settempfree(&qh facet_mergeset); + qh_settempfree(&qh degen_mergeset); +} /* post_merge */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="all_merges">-</a> + + qh_all_merges( othermerge, vneighbors ) + merge all non-convex facets + + set othermerge if already merged facets (for qh_reducevertices) + if vneighbors + tests vertex neighbors for convexity at end + qh.facet_mergeset lists the non-convex ridges in qh_newfacet_list + qh.degen_mergeset is defined + if qh.MERGEexact && !qh.POSTmerging, + does not merge coplanar facets + + returns: + deleted facets added to qh.visible_list with facet->visible + deleted vertices added qh.delvertex_list with vertex->delvertex + + notes: + unless !qh.MERGEindependent, + merges facets in independent sets + uses qh.newfacet_list as argument since merges call qh_removefacet() + + design: + while merges occur + for each merge in qh.facet_mergeset + unless one of the facets was already merged in this pass + merge the facets + test merged facets for additional merges + add merges to qh.facet_mergeset + if vertices record neighboring facets + rename redundant vertices + update qh.facet_mergeset + if vneighbors ?? + tests vertex neighbors for convexity at end +*/ +void qh_all_merges(boolT othermerge, boolT vneighbors) { + facetT *facet1, *facet2; + mergeT *merge; + boolT wasmerge= True, isreduce; + void **freelistp; /* used if !qh_NOmem by qh_memfree_() */ + vertexT *vertex; + mergeType mergetype; + int numcoplanar=0, numconcave=0, numdegenredun= 0, numnewmerges= 0; + + trace2((qh ferr, 2010, "qh_all_merges: starting to merge facets beginning from f%d\n", + getid_(qh newfacet_list))); + while (True) { + wasmerge= False; + while (qh_setsize(qh facet_mergeset)) { + while ((merge= (mergeT*)qh_setdellast(qh facet_mergeset))) { + facet1= merge->facet1; + facet2= merge->facet2; + mergetype= merge->type; + qh_memfree_(merge, (int)sizeof(mergeT), freelistp); + if (facet1->visible || facet2->visible) /*deleted facet*/ + continue; + if ((facet1->newfacet && !facet1->tested) + || (facet2->newfacet && !facet2->tested)) { + if (qh MERGEindependent && mergetype <= MRGanglecoplanar) + continue; /* perform independent sets of merges */ + } + qh_merge_nonconvex(facet1, facet2, mergetype); + numdegenredun += qh_merge_degenredundant(); + numnewmerges++; + wasmerge= True; + if (mergetype == MRGconcave) + numconcave++; + else /* MRGcoplanar or MRGanglecoplanar */ + numcoplanar++; + } /* while setdellast */ + if (qh POSTmerging && qh hull_dim <= qh_DIMreduceBuild + && numnewmerges > qh_MAXnewmerges) { + numnewmerges= 0; + qh_reducevertices(); /* otherwise large post merges too slow */ + } + qh_getmergeset(qh newfacet_list); /* facet_mergeset */ + } /* while mergeset */ + if (qh VERTEXneighbors) { + isreduce= False; + if (qh hull_dim >=4 && qh POSTmerging) { + FORALLvertices + vertex->delridge= True; + isreduce= True; + } + if ((wasmerge || othermerge) && (!qh MERGEexact || qh POSTmerging) + && qh hull_dim <= qh_DIMreduceBuild) { + othermerge= False; + isreduce= True; + } + if (isreduce) { + if (qh_reducevertices()) { + qh_getmergeset(qh newfacet_list); /* facet_mergeset */ + continue; + } + } + } + if (vneighbors && qh_test_vneighbors(/* qh.newfacet_list */)) + continue; + break; + } /* while (True) */ + if (qh CHECKfrequently && !qh MERGEexact) { + qh old_randomdist= qh RANDOMdist; + qh RANDOMdist= False; + qh_checkconvex(qh newfacet_list, qh_ALGORITHMfault); + /* qh_checkconnect(); [this is slow and it changes the facet order] */ + qh RANDOMdist= qh old_randomdist; + } + trace1((qh ferr, 1009, "qh_all_merges: merged %d coplanar facets %d concave facets and %d degen or redundant facets.\n", + numcoplanar, numconcave, numdegenredun)); + if (qh IStracing >= 4 && qh num_facets < 50) + qh_printlists(); +} /* all_merges */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="appendmergeset">-</a> + + qh_appendmergeset( facet, neighbor, mergetype, angle ) + appends an entry to qh.facet_mergeset or qh.degen_mergeset + + angle ignored if NULL or !qh.ANGLEmerge + + returns: + merge appended to facet_mergeset or degen_mergeset + sets ->degenerate or ->redundant if degen_mergeset + + see: + qh_test_appendmerge() + + design: + allocate merge entry + if regular merge + append to qh.facet_mergeset + else if degenerate merge and qh.facet_mergeset is all degenerate + append to qh.degen_mergeset + else if degenerate merge + prepend to qh.degen_mergeset + else if redundant merge + append to qh.degen_mergeset +*/ +void qh_appendmergeset(facetT *facet, facetT *neighbor, mergeType mergetype, realT *angle) { + mergeT *merge, *lastmerge; + void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */ + + if (facet->redundant) + return; + if (facet->degenerate && mergetype == MRGdegen) + return; + qh_memalloc_((int)sizeof(mergeT), freelistp, merge, mergeT); + merge->facet1= facet; + merge->facet2= neighbor; + merge->type= mergetype; + if (angle && qh ANGLEmerge) + merge->angle= *angle; + if (mergetype < MRGdegen) + qh_setappend(&(qh facet_mergeset), merge); + else if (mergetype == MRGdegen) { + facet->degenerate= True; + if (!(lastmerge= (mergeT*)qh_setlast(qh degen_mergeset)) + || lastmerge->type == MRGdegen) + qh_setappend(&(qh degen_mergeset), merge); + else + qh_setaddnth(&(qh degen_mergeset), 0, merge); + }else if (mergetype == MRGredundant) { + facet->redundant= True; + qh_setappend(&(qh degen_mergeset), merge); + }else /* mergetype == MRGmirror */ { + if (facet->redundant || neighbor->redundant) { + qh_fprintf(qh ferr, 6092, "qhull error (qh_appendmergeset): facet f%d or f%d is already a mirrored facet\n", + facet->id, neighbor->id); + qh_errexit2(qh_ERRqhull, facet, neighbor); + } + if (!qh_setequal(facet->vertices, neighbor->vertices)) { + qh_fprintf(qh ferr, 6093, "qhull error (qh_appendmergeset): mirrored facets f%d and f%d do not have the same vertices\n", + facet->id, neighbor->id); + qh_errexit2(qh_ERRqhull, facet, neighbor); + } + facet->redundant= True; + neighbor->redundant= True; + qh_setappend(&(qh degen_mergeset), merge); + } +} /* appendmergeset */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="basevertices">-</a> + + qh_basevertices( samecycle ) + return temporary set of base vertices for samecycle + samecycle is first facet in the cycle + assumes apex is SETfirst_( samecycle->vertices ) + + returns: + vertices(settemp) + all ->seen are cleared + + notes: + uses qh_vertex_visit; + + design: + for each facet in samecycle + for each unseen vertex in facet->vertices + append to result +*/ +setT *qh_basevertices(facetT *samecycle) { + facetT *same; + vertexT *apex, *vertex, **vertexp; + setT *vertices= qh_settemp(qh TEMPsize); + + apex= SETfirstt_(samecycle->vertices, vertexT); + apex->visitid= ++qh vertex_visit; + FORALLsame_cycle_(samecycle) { + if (same->mergeridge) + continue; + FOREACHvertex_(same->vertices) { + if (vertex->visitid != qh vertex_visit) { + qh_setappend(&vertices, vertex); + vertex->visitid= qh vertex_visit; + vertex->seen= False; + } + } + } + trace4((qh ferr, 4019, "qh_basevertices: found %d vertices\n", + qh_setsize(vertices))); + return vertices; +} /* basevertices */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="checkconnect">-</a> + + qh_checkconnect() + check that new facets are connected + new facets are on qh.newfacet_list + + notes: + this is slow and it changes the order of the facets + uses qh.visit_id + + design: + move first new facet to end of qh.facet_list + for all newly appended facets + append unvisited neighbors to end of qh.facet_list + for all new facets + report error if unvisited +*/ +void qh_checkconnect(void /* qh.newfacet_list */) { + facetT *facet, *newfacet, *errfacet= NULL, *neighbor, **neighborp; + + facet= qh newfacet_list; + qh_removefacet(facet); + qh_appendfacet(facet); + facet->visitid= ++qh visit_id; + FORALLfacet_(facet) { + FOREACHneighbor_(facet) { + if (neighbor->visitid != qh visit_id) { + qh_removefacet(neighbor); + qh_appendfacet(neighbor); + neighbor->visitid= qh visit_id; + } + } + } + FORALLnew_facets { + if (newfacet->visitid == qh visit_id) + break; + qh_fprintf(qh ferr, 6094, "qhull error: f%d is not attached to the new facets\n", + newfacet->id); + errfacet= newfacet; + } + if (errfacet) + qh_errexit(qh_ERRqhull, errfacet, NULL); +} /* checkconnect */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="checkzero">-</a> + + qh_checkzero( testall ) + check that facets are clearly convex for qh.DISTround with qh.MERGEexact + + if testall, + test all facets for qh.MERGEexact post-merging + else + test qh.newfacet_list + + if qh.MERGEexact, + allows coplanar ridges + skips convexity test while qh.ZEROall_ok + + returns: + True if all facets !flipped, !dupridge, normal + if all horizon facets are simplicial + if all vertices are clearly below neighbor + if all opposite vertices of horizon are below + clears qh.ZEROall_ok if any problems or coplanar facets + + notes: + uses qh.vertex_visit + horizon facets may define multiple new facets + + design: + for all facets in qh.newfacet_list or qh.facet_list + check for flagged faults (flipped, etc.) + for all facets in qh.newfacet_list or qh.facet_list + for each neighbor of facet + skip horizon facets for qh.newfacet_list + test the opposite vertex + if qh.newfacet_list + test the other vertices in the facet's horizon facet +*/ +boolT qh_checkzero(boolT testall) { + facetT *facet, *neighbor, **neighborp; + facetT *horizon, *facetlist; + int neighbor_i; + vertexT *vertex, **vertexp; + realT dist; + + if (testall) + facetlist= qh facet_list; + else { + facetlist= qh newfacet_list; + FORALLfacet_(facetlist) { + horizon= SETfirstt_(facet->neighbors, facetT); + if (!horizon->simplicial) + goto LABELproblem; + if (facet->flipped || facet->dupridge || !facet->normal) + goto LABELproblem; + } + if (qh MERGEexact && qh ZEROall_ok) { + trace2((qh ferr, 2011, "qh_checkzero: skip convexity check until first pre-merge\n")); + return True; + } + } + FORALLfacet_(facetlist) { + qh vertex_visit++; + neighbor_i= 0; + horizon= NULL; + FOREACHneighbor_(facet) { + if (!neighbor_i && !testall) { + horizon= neighbor; + neighbor_i++; + continue; /* horizon facet tested in qh_findhorizon */ + } + vertex= SETelemt_(facet->vertices, neighbor_i++, vertexT); + vertex->visitid= qh vertex_visit; + zzinc_(Zdistzero); + qh_distplane(vertex->point, neighbor, &dist); + if (dist >= -qh DISTround) { + qh ZEROall_ok= False; + if (!qh MERGEexact || testall || dist > qh DISTround) + goto LABELnonconvex; + } + } + if (!testall && horizon) { + FOREACHvertex_(horizon->vertices) { + if (vertex->visitid != qh vertex_visit) { + zzinc_(Zdistzero); + qh_distplane(vertex->point, facet, &dist); + if (dist >= -qh DISTround) { + qh ZEROall_ok= False; + if (!qh MERGEexact || dist > qh DISTround) + goto LABELnonconvex; + } + break; + } + } + } + } + trace2((qh ferr, 2012, "qh_checkzero: testall %d, facets are %s\n", testall, + (qh MERGEexact && !testall) ? + "not concave, flipped, or duplicate ridged" : "clearly convex")); + return True; + + LABELproblem: + qh ZEROall_ok= False; + trace2((qh ferr, 2013, "qh_checkzero: facet f%d needs pre-merging\n", + facet->id)); + return False; + + LABELnonconvex: + trace2((qh ferr, 2014, "qh_checkzero: facet f%d and f%d are not clearly convex. v%d dist %.2g\n", + facet->id, neighbor->id, vertex->id, dist)); + return False; +} /* checkzero */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="compareangle">-</a> + + qh_compareangle( angle1, angle2 ) + used by qsort() to order merges by angle +*/ +int qh_compareangle(const void *p1, const void *p2) { + const mergeT *a= *((mergeT *const*)p1), *b= *((mergeT *const*)p2); + + return((a->angle > b->angle) ? 1 : -1); +} /* compareangle */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="comparemerge">-</a> + + qh_comparemerge( merge1, merge2 ) + used by qsort() to order merges +*/ +int qh_comparemerge(const void *p1, const void *p2) { + const mergeT *a= *((mergeT *const*)p1), *b= *((mergeT *const*)p2); + + return(a->type - b->type); +} /* comparemerge */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="comparevisit">-</a> + + qh_comparevisit( vertex1, vertex2 ) + used by qsort() to order vertices by their visitid +*/ +int qh_comparevisit(const void *p1, const void *p2) { + const vertexT *a= *((vertexT *const*)p1), *b= *((vertexT *const*)p2); + + return(a->visitid - b->visitid); +} /* comparevisit */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="copynonconvex">-</a> + + qh_copynonconvex( atridge ) + set non-convex flag on other ridges (if any) between same neighbors + + notes: + may be faster if use smaller ridge set + + design: + for each ridge of atridge's top facet + if ridge shares the same neighbor + set nonconvex flag +*/ +void qh_copynonconvex(ridgeT *atridge) { + facetT *facet, *otherfacet; + ridgeT *ridge, **ridgep; + + facet= atridge->top; + otherfacet= atridge->bottom; + FOREACHridge_(facet->ridges) { + if (otherfacet == otherfacet_(ridge, facet) && ridge != atridge) { + ridge->nonconvex= True; + trace4((qh ferr, 4020, "qh_copynonconvex: moved nonconvex flag from r%d to r%d\n", + atridge->id, ridge->id)); + break; + } + } +} /* copynonconvex */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="degen_redundant_facet">-</a> + + qh_degen_redundant_facet( facet ) + check facet for degen. or redundancy + + notes: + bumps vertex_visit + called if a facet was redundant but no longer is (qh_merge_degenredundant) + qh_appendmergeset() only appends first reference to facet (i.e., redundant) + + see: + qh_degen_redundant_neighbors() + + design: + test for redundant neighbor + test for degenerate facet +*/ +void qh_degen_redundant_facet(facetT *facet) { + vertexT *vertex, **vertexp; + facetT *neighbor, **neighborp; + + trace4((qh ferr, 4021, "qh_degen_redundant_facet: test facet f%d for degen/redundant\n", + facet->id)); + FOREACHneighbor_(facet) { + qh vertex_visit++; + FOREACHvertex_(neighbor->vertices) + vertex->visitid= qh vertex_visit; + FOREACHvertex_(facet->vertices) { + if (vertex->visitid != qh vertex_visit) + break; + } + if (!vertex) { + qh_appendmergeset(facet, neighbor, MRGredundant, NULL); + trace2((qh ferr, 2015, "qh_degen_redundant_facet: f%d is contained in f%d. merge\n", facet->id, neighbor->id)); + return; + } + } + if (qh_setsize(facet->neighbors) < qh hull_dim) { + qh_appendmergeset(facet, facet, MRGdegen, NULL); + trace2((qh ferr, 2016, "qh_degen_redundant_neighbors: f%d is degenerate.\n", facet->id)); + } +} /* degen_redundant_facet */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="degen_redundant_neighbors">-</a> + + qh_degen_redundant_neighbors( facet, delfacet, ) + append degenerate and redundant neighbors to facet_mergeset + if delfacet, + only checks neighbors of both delfacet and facet + also checks current facet for degeneracy + + notes: + bumps vertex_visit + called for each qh_mergefacet() and qh_mergecycle() + merge and statistics occur in merge_nonconvex + qh_appendmergeset() only appends first reference to facet (i.e., redundant) + it appends redundant facets after degenerate ones + + a degenerate facet has fewer than hull_dim neighbors + a redundant facet's vertices is a subset of its neighbor's vertices + tests for redundant merges first (appendmergeset is nop for others) + in a merge, only needs to test neighbors of merged facet + + see: + qh_merge_degenredundant() and qh_degen_redundant_facet() + + design: + test for degenerate facet + test for redundant neighbor + test for degenerate neighbor +*/ +void qh_degen_redundant_neighbors(facetT *facet, facetT *delfacet) { + vertexT *vertex, **vertexp; + facetT *neighbor, **neighborp; + int size; + + trace4((qh ferr, 4022, "qh_degen_redundant_neighbors: test neighbors of f%d with delfacet f%d\n", + facet->id, getid_(delfacet))); + if ((size= qh_setsize(facet->neighbors)) < qh hull_dim) { + qh_appendmergeset(facet, facet, MRGdegen, NULL); + trace2((qh ferr, 2017, "qh_degen_redundant_neighbors: f%d is degenerate with %d neighbors.\n", facet->id, size)); + } + if (!delfacet) + delfacet= facet; + qh vertex_visit++; + FOREACHvertex_(facet->vertices) + vertex->visitid= qh vertex_visit; + FOREACHneighbor_(delfacet) { + /* uses early out instead of checking vertex count */ + if (neighbor == facet) + continue; + FOREACHvertex_(neighbor->vertices) { + if (vertex->visitid != qh vertex_visit) + break; + } + if (!vertex) { + qh_appendmergeset(neighbor, facet, MRGredundant, NULL); + trace2((qh ferr, 2018, "qh_degen_redundant_neighbors: f%d is contained in f%d. merge\n", neighbor->id, facet->id)); + } + } + FOREACHneighbor_(delfacet) { /* redundant merges occur first */ + if (neighbor == facet) + continue; + if ((size= qh_setsize(neighbor->neighbors)) < qh hull_dim) { + qh_appendmergeset(neighbor, neighbor, MRGdegen, NULL); + trace2((qh ferr, 2019, "qh_degen_redundant_neighbors: f%d is degenerate with %d neighbors. Neighbor of f%d.\n", neighbor->id, size, facet->id)); + } + } +} /* degen_redundant_neighbors */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="find_newvertex">-</a> + + qh_find_newvertex( oldvertex, vertices, ridges ) + locate new vertex for renaming old vertex + vertices is a set of possible new vertices + vertices sorted by number of deleted ridges + + returns: + newvertex or NULL + each ridge includes both vertex and oldvertex + vertices sorted by number of deleted ridges + + notes: + modifies vertex->visitid + new vertex is in one of the ridges + renaming will not cause a duplicate ridge + renaming will minimize the number of deleted ridges + newvertex may not be adjacent in the dual (though unlikely) + + design: + for each vertex in vertices + set vertex->visitid to number of references in ridges + remove unvisited vertices + set qh.vertex_visit above all possible values + sort vertices by number of references in ridges + add each ridge to qh.hash_table + for each vertex in vertices + look for a vertex that would not cause a duplicate ridge after a rename +*/ +vertexT *qh_find_newvertex(vertexT *oldvertex, setT *vertices, setT *ridges) { + vertexT *vertex, **vertexp; + setT *newridges; + ridgeT *ridge, **ridgep; + int size, hashsize; + int hash; + +#ifndef qh_NOtrace + if (qh IStracing >= 4) { + qh_fprintf(qh ferr, 8063, "qh_find_newvertex: find new vertex for v%d from ", + oldvertex->id); + FOREACHvertex_(vertices) + qh_fprintf(qh ferr, 8064, "v%d ", vertex->id); + FOREACHridge_(ridges) + qh_fprintf(qh ferr, 8065, "r%d ", ridge->id); + qh_fprintf(qh ferr, 8066, "\n"); + } +#endif + FOREACHvertex_(vertices) + vertex->visitid= 0; + FOREACHridge_(ridges) { + FOREACHvertex_(ridge->vertices) + vertex->visitid++; + } + FOREACHvertex_(vertices) { + if (!vertex->visitid) { + qh_setdelnth(vertices, SETindex_(vertices,vertex)); + vertexp--; /* repeat since deleted this vertex */ + } + } + qh vertex_visit += (unsigned int)qh_setsize(ridges); + if (!qh_setsize(vertices)) { + trace4((qh ferr, 4023, "qh_find_newvertex: vertices not in ridges for v%d\n", + oldvertex->id)); + return NULL; + } + qsort(SETaddr_(vertices, vertexT), (size_t)qh_setsize(vertices), + sizeof(vertexT *), qh_comparevisit); + /* can now use qh vertex_visit */ + if (qh PRINTstatistics) { + size= qh_setsize(vertices); + zinc_(Zintersect); + zadd_(Zintersecttot, size); + zmax_(Zintersectmax, size); + } + hashsize= qh_newhashtable(qh_setsize(ridges)); + FOREACHridge_(ridges) + qh_hashridge(qh hash_table, hashsize, ridge, oldvertex); + FOREACHvertex_(vertices) { + newridges= qh_vertexridges(vertex); + FOREACHridge_(newridges) { + if (qh_hashridge_find(qh hash_table, hashsize, ridge, vertex, oldvertex, &hash)) { + zinc_(Zdupridge); + break; + } + } + qh_settempfree(&newridges); + if (!ridge) + break; /* found a rename */ + } + if (vertex) { + /* counted in qh_renamevertex */ + trace2((qh ferr, 2020, "qh_find_newvertex: found v%d for old v%d from %d vertices and %d ridges.\n", + vertex->id, oldvertex->id, qh_setsize(vertices), qh_setsize(ridges))); + }else { + zinc_(Zfindfail); + trace0((qh ferr, 14, "qh_find_newvertex: no vertex for renaming v%d(all duplicated ridges) during p%d\n", + oldvertex->id, qh furthest_id)); + } + qh_setfree(&qh hash_table); + return vertex; +} /* find_newvertex */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="findbest_test">-</a> + + qh_findbest_test( testcentrum, facet, neighbor, bestfacet, dist, mindist, maxdist ) + test neighbor of facet for qh_findbestneighbor() + if testcentrum, + tests centrum (assumes it is defined) + else + tests vertices + + returns: + if a better facet (i.e., vertices/centrum of facet closer to neighbor) + updates bestfacet, dist, mindist, and maxdist +*/ +void qh_findbest_test(boolT testcentrum, facetT *facet, facetT *neighbor, + facetT **bestfacet, realT *distp, realT *mindistp, realT *maxdistp) { + realT dist, mindist, maxdist; + + if (testcentrum) { + zzinc_(Zbestdist); + qh_distplane(facet->center, neighbor, &dist); + dist *= qh hull_dim; /* estimate furthest vertex */ + if (dist < 0) { + maxdist= 0; + mindist= dist; + dist= -dist; + }else { + mindist= 0; + maxdist= dist; + } + }else + dist= qh_getdistance(facet, neighbor, &mindist, &maxdist); + if (dist < *distp) { + *bestfacet= neighbor; + *mindistp= mindist; + *maxdistp= maxdist; + *distp= dist; + } +} /* findbest_test */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="findbestneighbor">-</a> + + qh_findbestneighbor( facet, dist, mindist, maxdist ) + finds best neighbor (least dist) of a facet for merging + + returns: + returns min and max distances and their max absolute value + + notes: + 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(facetT *facet, realT *distp, realT *mindistp, realT *maxdistp) { + facetT *neighbor, **neighborp, *bestfacet= NULL; + ridgeT *ridge, **ridgep; + boolT nonconvex= True, testcentrum= False; + int size= qh_setsize(facet->vertices); + + if(qh CENTERtype==qh_ASvoronoi){ + qh_fprintf(qh ferr, 6272, "qhull error: cannot call qh_findbestneighor for f%d while qh.CENTERtype is qh_ASvoronoi\n", facet->id); + qh_errexit(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(facet); + } + if (size > qh hull_dim + qh_BESTnonconvex) { + FOREACHridge_(facet->ridges) { + if (ridge->nonconvex) { + neighbor= otherfacet_(ridge, facet); + qh_findbest_test(testcentrum, facet, neighbor, + &bestfacet, distp, mindistp, maxdistp); + } + } + } + if (!bestfacet) { + nonconvex= False; + FOREACHneighbor_(facet) + qh_findbest_test(testcentrum, facet, neighbor, + &bestfacet, distp, mindistp, maxdistp); + } + if (!bestfacet) { + qh_fprintf(qh ferr, 6095, "qhull internal error (qh_findbestneighbor): no neighbors for f%d\n", facet->id); + + qh_errexit(qh_ERRqhull, facet, NULL); + } + if (testcentrum) + qh_getdistance(facet, bestfacet, mindistp, maxdistp); + trace3((qh ferr, 3002, "qh_findbestneighbor: f%d is best neighbor for f%d testcentrum? %d nonconvex? %d dist %2.2g min %2.2g max %2.2g\n", + bestfacet->id, facet->id, testcentrum, nonconvex, *distp, *mindistp, *maxdistp)); + return(bestfacet); +} /* findbestneighbor */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="flippedmerges">-</a> + + qh_flippedmerges( facetlist, wasmerge ) + merge flipped facets into best neighbor + assumes qh.facet_mergeset at top of temporary stack + + returns: + no flipped facets on facetlist + sets wasmerge if merge occurred + degen/redundant merges passed through + + notes: + othermerges not needed since qh.facet_mergeset is empty before & after + keep it in case of change + + design: + append flipped facets to qh.facetmergeset + for each flipped merge + find best neighbor + merge facet into neighbor + merge degenerate and redundant facets + remove flipped merges from qh.facet_mergeset +*/ +void qh_flippedmerges(facetT *facetlist, boolT *wasmerge) { + facetT *facet, *neighbor, *facet1; + realT dist, mindist, maxdist; + mergeT *merge, **mergep; + setT *othermerges; + int nummerge=0; + + trace4((qh ferr, 4024, "qh_flippedmerges: begin\n")); + FORALLfacet_(facetlist) { + if (facet->flipped && !facet->visible) + qh_appendmergeset(facet, facet, MRGflip, NULL); + } + othermerges= qh_settemppop(); /* was facet_mergeset */ + qh facet_mergeset= qh_settemp(qh TEMPsize); + qh_settemppush(othermerges); + FOREACHmerge_(othermerges) { + facet1= merge->facet1; + if (merge->type != MRGflip || facet1->visible) + continue; + if (qh TRACEmerge-1 == zzval_(Ztotmerge)) + qhmem.IStracing= qh IStracing= qh TRACElevel; + neighbor= qh_findbestneighbor(facet1, &dist, &mindist, &maxdist); + trace0((qh ferr, 15, "qh_flippedmerges: merge flipped f%d into f%d dist %2.2g during p%d\n", + facet1->id, neighbor->id, dist, qh furthest_id)); + qh_mergefacet(facet1, neighbor, &mindist, &maxdist, !qh_MERGEapex); + nummerge++; + if (qh PRINTstatistics) { + zinc_(Zflipped); + wadd_(Wflippedtot, dist); + wmax_(Wflippedmax, dist); + } + qh_merge_degenredundant(); + } + FOREACHmerge_(othermerges) { + if (merge->facet1->visible || merge->facet2->visible) + qh_memfree(merge, (int)sizeof(mergeT)); + else + qh_setappend(&qh facet_mergeset, merge); + } + qh_settempfree(&othermerges); + if (nummerge) + *wasmerge= True; + trace1((qh ferr, 1010, "qh_flippedmerges: merged %d flipped facets into a good neighbor\n", nummerge)); +} /* flippedmerges */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="forcedmerges">-</a> + + qh_forcedmerges( wasmerge ) + merge duplicated ridges + + returns: + removes all duplicate ridges on facet_mergeset + wasmerge set if merge + qh.facet_mergeset may include non-forced merges(none for now) + qh.degen_mergeset includes degen/redun merges + + notes: + duplicate ridges occur when the horizon is pinched, + i.e. a subridge occurs in more than two horizon ridges. + could rename vertices that pinch the horizon + assumes qh_merge_degenredundant() has not be called + othermerges isn't needed since facet_mergeset is empty afterwards + keep it in case of change + + design: + for each duplicate ridge + find current facets by chasing f.replace links + check for wide merge due to duplicate ridge + determine best direction for facet + merge one facet into the other + remove duplicate ridges from qh.facet_mergeset +*/ +void qh_forcedmerges(boolT *wasmerge) { + facetT *facet1, *facet2; + mergeT *merge, **mergep; + realT dist1, dist2, mindist1, mindist2, maxdist1, maxdist2; + setT *othermerges; + int nummerge=0, numflip=0; + + if (qh TRACEmerge-1 == zzval_(Ztotmerge)) + qhmem.IStracing= qh IStracing= qh TRACElevel; + trace4((qh ferr, 4025, "qh_forcedmerges: begin\n")); + othermerges= qh_settemppop(); /* was facet_mergeset */ + qh facet_mergeset= qh_settemp(qh TEMPsize); + qh_settemppush(othermerges); + FOREACHmerge_(othermerges) { + if (merge->type != MRGridge) + continue; + if (qh TRACEmerge-1 == zzval_(Ztotmerge)) + qhmem.IStracing= qh IStracing= qh TRACElevel; + facet1= merge->facet1; + facet2= merge->facet2; + while (facet1->visible) /* must exist, no qh_merge_degenredunant */ + facet1= facet1->f.replace; /* previously merged facet */ + while (facet2->visible) + facet2= facet2->f.replace; /* previously merged facet */ + if (facet1 == facet2) + continue; + if (!qh_setin(facet2->neighbors, facet1)) { + qh_fprintf(qh ferr, 6096, "qhull internal error (qh_forcedmerges): f%d and f%d had a duplicate ridge but as f%d and f%d they are no longer neighbors\n", + merge->facet1->id, merge->facet2->id, facet1->id, facet2->id); + qh_errexit2(qh_ERRqhull, facet1, facet2); + } + dist1= qh_getdistance(facet1, facet2, &mindist1, &maxdist1); + dist2= qh_getdistance(facet2, facet1, &mindist2, &maxdist2); + qh_check_dupridge(facet1, dist1, facet2, dist2); + if (dist1 < dist2) + qh_mergefacet(facet1, facet2, &mindist1, &maxdist1, !qh_MERGEapex); + else { + qh_mergefacet(facet2, facet1, &mindist2, &maxdist2, !qh_MERGEapex); + dist1= dist2; + facet1= facet2; + } + if (facet1->flipped) { + zinc_(Zmergeflipdup); + numflip++; + }else + nummerge++; + if (qh PRINTstatistics) { + zinc_(Zduplicate); + wadd_(Wduplicatetot, dist1); + wmax_(Wduplicatemax, dist1); + } + } + FOREACHmerge_(othermerges) { + if (merge->type == MRGridge) + qh_memfree(merge, (int)sizeof(mergeT)); + else + qh_setappend(&qh facet_mergeset, merge); + } + qh_settempfree(&othermerges); + if (nummerge) + *wasmerge= True; + trace1((qh ferr, 1011, "qh_forcedmerges: merged %d facets and %d flipped facets across duplicated ridges\n", + nummerge, numflip)); +} /* forcedmerges */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="getmergeset">-</a> + + qh_getmergeset( facetlist ) + determines nonconvex facets on facetlist + tests !tested ridges and nonconvex ridges of !tested facets + + returns: + returns sorted qh.facet_mergeset of facet-neighbor pairs to be merged + all ridges tested + + notes: + assumes no nonconvex ridges with both facets tested + uses facet->tested/ridge->tested to prevent duplicate tests + can not limit tests to modified ridges since the centrum changed + uses qh.visit_id + + see: + qh_getmergeset_initial() + + design: + for each facet on facetlist + for each ridge of facet + if untested ridge + test ridge for convexity + if non-convex + append ridge to qh.facet_mergeset + sort qh.facet_mergeset by angle +*/ +void qh_getmergeset(facetT *facetlist) { + facetT *facet, *neighbor, **neighborp; + ridgeT *ridge, **ridgep; + int nummerges; + + nummerges= qh_setsize(qh facet_mergeset); + trace4((qh ferr, 4026, "qh_getmergeset: started.\n")); + qh visit_id++; + FORALLfacet_(facetlist) { + if (facet->tested) + continue; + facet->visitid= qh visit_id; + facet->tested= True; /* must be non-simplicial due to merge */ + FOREACHneighbor_(facet) + neighbor->seen= False; + FOREACHridge_(facet->ridges) { + if (ridge->tested && !ridge->nonconvex) + continue; + /* if tested & nonconvex, need to append merge */ + neighbor= otherfacet_(ridge, facet); + if (neighbor->seen) { + ridge->tested= True; + ridge->nonconvex= False; + }else if (neighbor->visitid != qh visit_id) { + ridge->tested= True; + ridge->nonconvex= False; + neighbor->seen= True; /* only one ridge is marked nonconvex */ + if (qh_test_appendmerge(facet, neighbor)) + ridge->nonconvex= True; + } + } + } + nummerges= qh_setsize(qh facet_mergeset); + if (qh ANGLEmerge) + qsort(SETaddr_(qh facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_compareangle); + else + qsort(SETaddr_(qh facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_comparemerge); + if (qh POSTmerging) { + zadd_(Zmergesettot2, nummerges); + }else { + zadd_(Zmergesettot, nummerges); + zmax_(Zmergesetmax, nummerges); + } + trace2((qh ferr, 2021, "qh_getmergeset: %d merges found\n", nummerges)); +} /* getmergeset */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="getmergeset_initial">-</a> + + qh_getmergeset_initial( facetlist ) + determine initial qh.facet_mergeset for facets + tests all facet/neighbor pairs on facetlist + + returns: + sorted qh.facet_mergeset with nonconvex ridges + sets facet->tested, ridge->tested, and ridge->nonconvex + + notes: + uses visit_id, assumes ridge->nonconvex is False + + see: + qh_getmergeset() + + design: + for each facet on facetlist + for each untested neighbor of facet + test facet and neighbor for convexity + if non-convex + append merge to qh.facet_mergeset + mark one of the ridges as nonconvex + sort qh.facet_mergeset by angle +*/ +void qh_getmergeset_initial(facetT *facetlist) { + facetT *facet, *neighbor, **neighborp; + ridgeT *ridge, **ridgep; + int nummerges; + + qh visit_id++; + FORALLfacet_(facetlist) { + facet->visitid= qh visit_id; + facet->tested= True; + FOREACHneighbor_(facet) { + if (neighbor->visitid != qh visit_id) { + if (qh_test_appendmerge(facet, neighbor)) { + FOREACHridge_(neighbor->ridges) { + if (facet == otherfacet_(ridge, neighbor)) { + ridge->nonconvex= True; + break; /* only one ridge is marked nonconvex */ + } + } + } + } + } + FOREACHridge_(facet->ridges) + ridge->tested= True; + } + nummerges= qh_setsize(qh facet_mergeset); + if (qh ANGLEmerge) + qsort(SETaddr_(qh facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_compareangle); + else + qsort(SETaddr_(qh facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_comparemerge); + if (qh POSTmerging) { + zadd_(Zmergeinittot2, nummerges); + }else { + zadd_(Zmergeinittot, nummerges); + zmax_(Zmergeinitmax, nummerges); + } + trace2((qh ferr, 2022, "qh_getmergeset_initial: %d merges found\n", nummerges)); +} /* getmergeset_initial */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="hashridge">-</a> + + qh_hashridge( hashtable, hashsize, ridge, oldvertex ) + add ridge to hashtable without oldvertex + + notes: + assumes hashtable is large enough + + design: + determine hash value for ridge without oldvertex + find next empty slot for ridge +*/ +void qh_hashridge(setT *hashtable, int hashsize, ridgeT *ridge, vertexT *oldvertex) { + int hash; + ridgeT *ridgeA; + + hash= qh_gethash(hashsize, ridge->vertices, qh hull_dim-1, 0, oldvertex); + while (True) { + if (!(ridgeA= SETelemt_(hashtable, hash, ridgeT))) { + SETelem_(hashtable, hash)= ridge; + break; + }else if (ridgeA == ridge) + break; + if (++hash == hashsize) + hash= 0; + } +} /* hashridge */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="hashridge_find">-</a> + + qh_hashridge_find( hashtable, hashsize, ridge, vertex, oldvertex, hashslot ) + returns matching ridge without oldvertex in hashtable + for ridge without vertex + if oldvertex is NULL + matches with any one skip + + returns: + matching ridge or NULL + if no match, + if ridge already in table + hashslot= -1 + else + hashslot= next NULL index + + notes: + assumes hashtable is large enough + can't match ridge to itself + + design: + get hash value for ridge without vertex + for each hashslot + return match if ridge matches ridgeA without oldvertex +*/ +ridgeT *qh_hashridge_find(setT *hashtable, int hashsize, ridgeT *ridge, + vertexT *vertex, vertexT *oldvertex, int *hashslot) { + int hash; + ridgeT *ridgeA; + + *hashslot= 0; + zinc_(Zhashridge); + hash= qh_gethash(hashsize, ridge->vertices, qh hull_dim-1, 0, vertex); + while ((ridgeA= SETelemt_(hashtable, hash, ridgeT))) { + if (ridgeA == ridge) + *hashslot= -1; + else { + zinc_(Zhashridgetest); + if (qh_setequal_except(ridge->vertices, vertex, ridgeA->vertices, oldvertex)) + return ridgeA; + } + if (++hash == hashsize) + hash= 0; + } + if (!*hashslot) + *hashslot= hash; + return NULL; +} /* hashridge_find */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="makeridges">-</a> + + qh_makeridges( facet ) + creates explicit ridges between simplicial facets + + returns: + facet with ridges and without qh_MERGEridge + ->simplicial is False + + notes: + allows qh_MERGEridge flag + uses existing ridges + duplicate neighbors ok if ridges already exist (qh_mergecycle_ridges) + + see: + qh_mergecycle_ridges() + + design: + look for qh_MERGEridge neighbors + mark neighbors that already have ridges + for each unprocessed neighbor of facet + create a ridge for neighbor and facet + if any qh_MERGEridge neighbors + delete qh_MERGEridge flags (already handled by qh_mark_dupridges) +*/ +void qh_makeridges(facetT *facet) { + facetT *neighbor, **neighborp; + ridgeT *ridge, **ridgep; + int neighbor_i, neighbor_n; + boolT toporient, mergeridge= False; + + if (!facet->simplicial) + return; + trace4((qh ferr, 4027, "qh_makeridges: make ridges for f%d\n", facet->id)); + facet->simplicial= False; + FOREACHneighbor_(facet) { + if (neighbor == qh_MERGEridge) + mergeridge= True; + else + neighbor->seen= False; + } + FOREACHridge_(facet->ridges) + otherfacet_(ridge, facet)->seen= True; + FOREACHneighbor_i_(facet) { + if (neighbor == qh_MERGEridge) + continue; /* fixed by qh_mark_dupridges */ + else if (!neighbor->seen) { /* no current ridges */ + ridge= qh_newridge(); + ridge->vertices= qh_setnew_delnthsorted(facet->vertices, qh hull_dim, + neighbor_i, 0); + toporient= facet->toporient ^ (neighbor_i & 0x1); + if (toporient) { + ridge->top= facet; + ridge->bottom= neighbor; + }else { + ridge->top= neighbor; + ridge->bottom= facet; + } +#if 0 /* this also works */ + flip= (facet->toporient ^ neighbor->toporient)^(skip1 & 0x1) ^ (skip2 & 0x1); + if (facet->toporient ^ (skip1 & 0x1) ^ flip) { + ridge->top= neighbor; + ridge->bottom= facet; + }else { + ridge->top= facet; + ridge->bottom= neighbor; + } +#endif + qh_setappend(&(facet->ridges), ridge); + qh_setappend(&(neighbor->ridges), ridge); + } + } + if (mergeridge) { + while (qh_setdel(facet->neighbors, qh_MERGEridge)) + ; /* delete each one */ + } +} /* makeridges */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="mark_dupridges">-</a> + + qh_mark_dupridges( facetlist ) + add duplicated ridges to qh.facet_mergeset + facet->dupridge is true + + returns: + duplicate ridges on qh.facet_mergeset + ->mergeridge/->mergeridge2 set + duplicate ridges marked by qh_MERGEridge and both sides facet->dupridge + no MERGEridges in neighbor sets + + notes: + duplicate ridges occur when the horizon is pinched, + i.e. a subridge occurs in more than two horizon ridges. + could rename vertices that pinch the horizon (thus removing subridge) + uses qh.visit_id + + design: + for all facets on facetlist + if facet contains a duplicate ridge + for each neighbor of facet + if neighbor marked qh_MERGEridge (one side of the merge) + set facet->mergeridge + else + if neighbor contains a duplicate ridge + and the back link is qh_MERGEridge + append duplicate ridge to qh.facet_mergeset + for each duplicate ridge + make ridge sets in preparation for merging + remove qh_MERGEridge from neighbor set + for each duplicate ridge + restore the missing neighbor from the neighbor set that was qh_MERGEridge + add the missing ridge for this neighbor +*/ +void qh_mark_dupridges(facetT *facetlist) { + facetT *facet, *neighbor, **neighborp; + int nummerge=0; + mergeT *merge, **mergep; + + + trace4((qh ferr, 4028, "qh_mark_dupridges: identify duplicate ridges\n")); + FORALLfacet_(facetlist) { + if (facet->dupridge) { + FOREACHneighbor_(facet) { + if (neighbor == qh_MERGEridge) { + facet->mergeridge= True; + continue; + } + if (neighbor->dupridge + && !qh_setin(neighbor->neighbors, facet)) { /* qh_MERGEridge */ + qh_appendmergeset(facet, neighbor, MRGridge, NULL); + facet->mergeridge2= True; + facet->mergeridge= True; + nummerge++; + } + } + } + } + if (!nummerge) + return; + FORALLfacet_(facetlist) { /* gets rid of qh_MERGEridge */ + if (facet->mergeridge && !facet->mergeridge2) + qh_makeridges(facet); + } + FOREACHmerge_(qh facet_mergeset) { /* restore the missing neighbors */ + if (merge->type == MRGridge) { + qh_setappend(&merge->facet2->neighbors, merge->facet1); + qh_makeridges(merge->facet1); /* and the missing ridges */ + } + } + trace1((qh ferr, 1012, "qh_mark_dupridges: found %d duplicated ridges\n", + nummerge)); +} /* mark_dupridges */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="maydropneighbor">-</a> + + qh_maydropneighbor( facet ) + drop neighbor relationship if no ridge between facet and neighbor + + returns: + neighbor sets updated + appends degenerate facets to qh.facet_mergeset + + notes: + won't cause redundant facets since vertex inclusion is the same + may drop vertex and neighbor if no ridge + uses qh.visit_id + + design: + visit all neighbors with ridges + for each unvisited neighbor of facet + delete neighbor and facet from the neighbor sets + if neighbor becomes degenerate + append neighbor to qh.degen_mergeset + if facet is degenerate + append facet to qh.degen_mergeset +*/ +void qh_maydropneighbor(facetT *facet) { + ridgeT *ridge, **ridgep; + realT angledegen= qh_ANGLEdegen; + facetT *neighbor, **neighborp; + + qh visit_id++; + trace4((qh ferr, 4029, "qh_maydropneighbor: test f%d for no ridges to a neighbor\n", + facet->id)); + FOREACHridge_(facet->ridges) { + ridge->top->visitid= qh visit_id; + ridge->bottom->visitid= qh visit_id; + } + FOREACHneighbor_(facet) { + if (neighbor->visitid != qh visit_id) { + trace0((qh ferr, 17, "qh_maydropneighbor: facets f%d and f%d are no longer neighbors during p%d\n", + facet->id, neighbor->id, qh furthest_id)); + zinc_(Zdropneighbor); + qh_setdel(facet->neighbors, neighbor); + neighborp--; /* repeat, deleted a neighbor */ + qh_setdel(neighbor->neighbors, facet); + if (qh_setsize(neighbor->neighbors) < qh hull_dim) { + zinc_(Zdropdegen); + qh_appendmergeset(neighbor, neighbor, MRGdegen, &angledegen); + trace2((qh ferr, 2023, "qh_maydropneighbors: f%d is degenerate.\n", neighbor->id)); + } + } + } + if (qh_setsize(facet->neighbors) < qh hull_dim) { + zinc_(Zdropdegen); + qh_appendmergeset(facet, facet, MRGdegen, &angledegen); + trace2((qh ferr, 2024, "qh_maydropneighbors: f%d is degenerate.\n", facet->id)); + } +} /* maydropneighbor */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="merge_degenredundant">-</a> + + qh_merge_degenredundant() + merge all degenerate and redundant facets + qh.degen_mergeset contains merges from qh_degen_redundant_neighbors() + + returns: + number of merges performed + resets facet->degenerate/redundant + if deleted (visible) facet has no neighbors + sets ->f.replace to NULL + + notes: + redundant merges happen before degenerate ones + merging and renaming vertices can result in degen/redundant facets + + design: + for each merge on qh.degen_mergeset + if redundant merge + if non-redundant facet merged into redundant facet + recheck facet for redundancy + else + merge redundant facet into other facet +*/ +int qh_merge_degenredundant(void) { + int size; + mergeT *merge; + facetT *bestneighbor, *facet1, *facet2; + realT dist, mindist, maxdist; + vertexT *vertex, **vertexp; + int nummerges= 0; + mergeType mergetype; + + while ((merge= (mergeT*)qh_setdellast(qh degen_mergeset))) { + facet1= merge->facet1; + facet2= merge->facet2; + mergetype= merge->type; + qh_memfree(merge, (int)sizeof(mergeT)); + if (facet1->visible) + continue; + facet1->degenerate= False; + facet1->redundant= False; + if (qh TRACEmerge-1 == zzval_(Ztotmerge)) + qhmem.IStracing= qh IStracing= qh TRACElevel; + if (mergetype == MRGredundant) { + zinc_(Zneighbor); + while (facet2->visible) { + if (!facet2->f.replace) { + qh_fprintf(qh ferr, 6097, "qhull internal error (qh_merge_degenredunant): f%d redundant but f%d has no replacement\n", + facet1->id, facet2->id); + qh_errexit2(qh_ERRqhull, facet1, facet2); + } + facet2= facet2->f.replace; + } + if (facet1 == facet2) { + qh_degen_redundant_facet(facet1); /* in case of others */ + continue; + } + trace2((qh ferr, 2025, "qh_merge_degenredundant: facet f%d is contained in f%d, will merge\n", + facet1->id, facet2->id)); + qh_mergefacet(facet1, facet2, NULL, NULL, !qh_MERGEapex); + /* merge distance is already accounted for */ + nummerges++; + }else { /* mergetype == MRGdegen, other merges may have fixed */ + if (!(size= qh_setsize(facet1->neighbors))) { + zinc_(Zdelfacetdup); + trace2((qh ferr, 2026, "qh_merge_degenredundant: facet f%d has no neighbors. Deleted\n", facet1->id)); + qh_willdelete(facet1, NULL); + FOREACHvertex_(facet1->vertices) { + qh_setdel(vertex->neighbors, facet1); + if (!SETfirst_(vertex->neighbors)) { + zinc_(Zdegenvertex); + trace2((qh ferr, 2027, "qh_merge_degenredundant: deleted v%d because f%d has no neighbors\n", + vertex->id, facet1->id)); + vertex->deleted= True; + qh_setappend(&qh del_vertices, vertex); + } + } + nummerges++; + }else if (size < qh hull_dim) { + bestneighbor= qh_findbestneighbor(facet1, &dist, &mindist, &maxdist); + trace2((qh ferr, 2028, "qh_merge_degenredundant: facet f%d has %d neighbors, merge into f%d dist %2.2g\n", + facet1->id, size, bestneighbor->id, dist)); + qh_mergefacet(facet1, bestneighbor, &mindist, &maxdist, !qh_MERGEapex); + nummerges++; + if (qh PRINTstatistics) { + zinc_(Zdegen); + wadd_(Wdegentot, dist); + wmax_(Wdegenmax, dist); + } + } /* else, another merge fixed the degeneracy and redundancy tested */ + } + } + return nummerges; +} /* merge_degenredundant */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="merge_nonconvex">-</a> + + qh_merge_nonconvex( facet1, facet2, mergetype ) + remove non-convex ridge between facet1 into facet2 + mergetype gives why the facet's are non-convex + + returns: + merges one of the facets into the best neighbor + + design: + if one of the facets is a new facet + prefer merging new facet into old facet + find best neighbors for both facets + merge the nearest facet into its best neighbor + update the statistics +*/ +void qh_merge_nonconvex(facetT *facet1, facetT *facet2, mergeType mergetype) { + facetT *bestfacet, *bestneighbor, *neighbor; + realT dist, dist2, mindist, mindist2, maxdist, maxdist2; + + if (qh TRACEmerge-1 == zzval_(Ztotmerge)) + qhmem.IStracing= qh IStracing= qh TRACElevel; + trace3((qh ferr, 3003, "qh_merge_nonconvex: merge #%d for f%d and f%d type %d\n", + zzval_(Ztotmerge) + 1, facet1->id, facet2->id, mergetype)); + /* concave or coplanar */ + if (!facet1->newfacet) { + bestfacet= facet2; /* avoid merging old facet if new is ok */ + facet2= facet1; + facet1= bestfacet; + }else + bestfacet= facet1; + bestneighbor= qh_findbestneighbor(bestfacet, &dist, &mindist, &maxdist); + neighbor= qh_findbestneighbor(facet2, &dist2, &mindist2, &maxdist2); + if (dist < dist2) { + qh_mergefacet(bestfacet, bestneighbor, &mindist, &maxdist, !qh_MERGEapex); + }else if (qh AVOIDold && !facet2->newfacet + && ((mindist >= -qh MAXcoplanar && maxdist <= qh max_outside) + || dist * 1.5 < dist2)) { + zinc_(Zavoidold); + wadd_(Wavoidoldtot, dist); + wmax_(Wavoidoldmax, dist); + trace2((qh ferr, 2029, "qh_merge_nonconvex: avoid merging old facet f%d dist %2.2g. Use f%d dist %2.2g instead\n", + facet2->id, dist2, facet1->id, dist2)); + qh_mergefacet(bestfacet, bestneighbor, &mindist, &maxdist, !qh_MERGEapex); + }else { + qh_mergefacet(facet2, neighbor, &mindist2, &maxdist2, !qh_MERGEapex); + dist= dist2; + } + if (qh PRINTstatistics) { + if (mergetype == MRGanglecoplanar) { + zinc_(Zacoplanar); + wadd_(Wacoplanartot, dist); + wmax_(Wacoplanarmax, dist); + }else if (mergetype == MRGconcave) { + zinc_(Zconcave); + wadd_(Wconcavetot, dist); + wmax_(Wconcavemax, dist); + }else { /* MRGcoplanar */ + zinc_(Zcoplanar); + wadd_(Wcoplanartot, dist); + wmax_(Wcoplanarmax, dist); + } + } +} /* merge_nonconvex */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="mergecycle">-</a> + + qh_mergecycle( samecycle, newfacet ) + merge a cycle of facets starting at samecycle into a newfacet + newfacet is a horizon facet with ->normal + samecycle facets are simplicial from an apex + + returns: + initializes vertex neighbors on first merge + samecycle deleted (placed on qh.visible_list) + newfacet at end of qh.facet_list + deleted vertices on qh.del_vertices + + see: + qh_mergefacet() + called by qh_mergecycle_all() for multiple, same cycle facets + + design: + make vertex neighbors if necessary + make ridges for newfacet + merge neighbor sets of samecycle into newfacet + merge ridges of samecycle into newfacet + merge vertex neighbors of samecycle into newfacet + make apex of samecycle the apex of newfacet + if newfacet wasn't a new facet + add its vertices to qh.newvertex_list + delete samecycle facets a make newfacet a newfacet +*/ +void qh_mergecycle(facetT *samecycle, facetT *newfacet) { + int traceonce= False, tracerestore= 0; + vertexT *apex; +#ifndef qh_NOtrace + facetT *same; +#endif + + if (newfacet->tricoplanar) { + if (!qh TRInormals) { + qh_fprintf(qh ferr, 6224, "Qhull internal error (qh_mergecycle): does not work for tricoplanar facets. Use option 'Q11'\n"); + qh_errexit(qh_ERRqhull, newfacet, NULL); + } + newfacet->tricoplanar= False; + newfacet->keepcentrum= False; + } + if (!qh VERTEXneighbors) + qh_vertexneighbors(); + zzinc_(Ztotmerge); + if (qh REPORTfreq2 && qh POSTmerging) { + if (zzval_(Ztotmerge) > qh mergereport + qh REPORTfreq2) + qh_tracemerging(); + } +#ifndef qh_NOtrace + if (qh TRACEmerge == zzval_(Ztotmerge)) + qhmem.IStracing= qh IStracing= qh TRACElevel; + trace2((qh ferr, 2030, "qh_mergecycle: merge #%d for facets from cycle f%d into coplanar horizon f%d\n", + zzval_(Ztotmerge), samecycle->id, newfacet->id)); + if (newfacet == qh tracefacet) { + tracerestore= qh IStracing; + qh IStracing= 4; + qh_fprintf(qh ferr, 8068, "qh_mergecycle: ========= trace merge %d of samecycle %d into trace f%d, furthest is p%d\n", + zzval_(Ztotmerge), samecycle->id, newfacet->id, qh furthest_id); + traceonce= True; + } + if (qh IStracing >=4) { + qh_fprintf(qh ferr, 8069, " same cycle:"); + FORALLsame_cycle_(samecycle) + qh_fprintf(qh ferr, 8070, " f%d", same->id); + qh_fprintf(qh ferr, 8071, "\n"); + } + if (qh IStracing >=4) + qh_errprint("MERGING CYCLE", samecycle, newfacet, NULL, NULL); +#endif /* !qh_NOtrace */ + apex= SETfirstt_(samecycle->vertices, vertexT); + qh_makeridges(newfacet); + qh_mergecycle_neighbors(samecycle, newfacet); + qh_mergecycle_ridges(samecycle, newfacet); + qh_mergecycle_vneighbors(samecycle, newfacet); + if (SETfirstt_(newfacet->vertices, vertexT) != apex) + qh_setaddnth(&newfacet->vertices, 0, apex); /* apex has last id */ + if (!newfacet->newfacet) + qh_newvertices(newfacet->vertices); + qh_mergecycle_facets(samecycle, newfacet); + qh_tracemerge(samecycle, newfacet); + /* check for degen_redundant_neighbors after qh_forcedmerges() */ + if (traceonce) { + qh_fprintf(qh ferr, 8072, "qh_mergecycle: end of trace facet\n"); + qh IStracing= tracerestore; + } +} /* mergecycle */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="mergecycle_all">-</a> + + qh_mergecycle_all( facetlist, wasmerge ) + merge all samecycles of coplanar facets into horizon + don't merge facets with ->mergeridge (these already have ->normal) + all facets are simplicial from apex + all facet->cycledone == False + + returns: + all newfacets merged into coplanar horizon facets + deleted vertices on qh.del_vertices + sets wasmerge if any merge + + see: + calls qh_mergecycle for multiple, same cycle facets + + design: + for each facet on facetlist + skip facets with duplicate ridges and normals + check that facet is in a samecycle (->mergehorizon) + if facet only member of samecycle + sets vertex->delridge for all vertices except apex + merge facet into horizon + else + mark all facets in samecycle + remove facets with duplicate ridges from samecycle + merge samecycle into horizon (deletes facets from facetlist) +*/ +void qh_mergecycle_all(facetT *facetlist, boolT *wasmerge) { + facetT *facet, *same, *prev, *horizon; + facetT *samecycle= NULL, *nextfacet, *nextsame; + vertexT *apex, *vertex, **vertexp; + int cycles=0, total=0, facets, nummerge; + + trace2((qh ferr, 2031, "qh_mergecycle_all: begin\n")); + for (facet= facetlist; facet && (nextfacet= facet->next); facet= nextfacet) { + if (facet->normal) + continue; + if (!facet->mergehorizon) { + qh_fprintf(qh ferr, 6225, "Qhull internal error (qh_mergecycle_all): f%d without normal\n", facet->id); + qh_errexit(qh_ERRqhull, facet, NULL); + } + horizon= SETfirstt_(facet->neighbors, facetT); + if (facet->f.samecycle == facet) { + zinc_(Zonehorizon); + /* merge distance done in qh_findhorizon */ + apex= SETfirstt_(facet->vertices, vertexT); + FOREACHvertex_(facet->vertices) { + if (vertex != apex) + vertex->delridge= True; + } + horizon->f.newcycle= NULL; + qh_mergefacet(facet, horizon, NULL, NULL, qh_MERGEapex); + }else { + samecycle= facet; + facets= 0; + prev= facet; + for (same= facet->f.samecycle; same; /* FORALLsame_cycle_(facet) */ + same= (same == facet ? NULL :nextsame)) { /* ends at facet */ + nextsame= same->f.samecycle; + if (same->cycledone || same->visible) + qh_infiniteloop(same); + same->cycledone= True; + if (same->normal) { + prev->f.samecycle= same->f.samecycle; /* unlink ->mergeridge */ + same->f.samecycle= NULL; + }else { + prev= same; + facets++; + } + } + while (nextfacet && nextfacet->cycledone) /* will delete samecycle */ + nextfacet= nextfacet->next; + horizon->f.newcycle= NULL; + qh_mergecycle(samecycle, horizon); + nummerge= horizon->nummerge + facets; + if (nummerge > qh_MAXnummerge) + horizon->nummerge= qh_MAXnummerge; + else + horizon->nummerge= (short unsigned int)nummerge; + zzinc_(Zcyclehorizon); + total += facets; + zzadd_(Zcyclefacettot, facets); + zmax_(Zcyclefacetmax, facets); + } + cycles++; + } + if (cycles) + *wasmerge= True; + trace1((qh ferr, 1013, "qh_mergecycle_all: merged %d same cycles or facets into coplanar horizons\n", cycles)); +} /* mergecycle_all */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="mergecycle_facets">-</a> + + qh_mergecycle_facets( samecycle, newfacet ) + finish merge of samecycle into newfacet + + returns: + samecycle prepended to visible_list for later deletion and partitioning + each facet->f.replace == newfacet + + newfacet moved to end of qh.facet_list + makes newfacet a newfacet (get's facet1->id if it was old) + sets newfacet->newmerge + clears newfacet->center (unless merging into a large facet) + clears newfacet->tested and ridge->tested for facet1 + + adds neighboring facets to facet_mergeset if redundant or degenerate + + design: + make newfacet a new facet and set its flags + move samecycle facets to qh.visible_list for later deletion + unless newfacet is large + remove its centrum +*/ +void qh_mergecycle_facets(facetT *samecycle, facetT *newfacet) { + facetT *same, *next; + + trace4((qh ferr, 4030, "qh_mergecycle_facets: make newfacet new and samecycle deleted\n")); + qh_removefacet(newfacet); /* append as a newfacet to end of qh facet_list */ + qh_appendfacet(newfacet); + newfacet->newfacet= True; + newfacet->simplicial= False; + newfacet->newmerge= True; + + for (same= samecycle->f.samecycle; same; same= (same == samecycle ? NULL : next)) { + next= same->f.samecycle; /* reused by willdelete */ + qh_willdelete(same, newfacet); + } + if (newfacet->center + && qh_setsize(newfacet->vertices) <= qh hull_dim + qh_MAXnewcentrum) { + qh_memfree(newfacet->center, qh normal_size); + newfacet->center= NULL; + } + trace3((qh ferr, 3004, "qh_mergecycle_facets: merged facets from cycle f%d into f%d\n", + samecycle->id, newfacet->id)); +} /* mergecycle_facets */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="mergecycle_neighbors">-</a> + + qh_mergecycle_neighbors( samecycle, newfacet ) + add neighbors for samecycle facets to newfacet + + returns: + newfacet with updated neighbors and vice-versa + newfacet has ridges + all neighbors of newfacet marked with qh.visit_id + samecycle facets marked with qh.visit_id-1 + ridges updated for simplicial neighbors of samecycle with a ridge + + notes: + assumes newfacet not in samecycle + usually, samecycle facets are new, simplicial facets without internal ridges + not so if horizon facet is coplanar to two different samecycles + + see: + qh_mergeneighbors() + + design: + check samecycle + delete neighbors from newfacet that are also in samecycle + for each neighbor of a facet in samecycle + if neighbor is simplicial + if first visit + move the neighbor relation to newfacet + update facet links for its ridges + else + make ridges for neighbor + remove samecycle reference + else + update neighbor sets +*/ +void qh_mergecycle_neighbors(facetT *samecycle, facetT *newfacet) { + facetT *same, *neighbor, **neighborp; + int delneighbors= 0, newneighbors= 0; + unsigned int samevisitid; + ridgeT *ridge, **ridgep; + + samevisitid= ++qh visit_id; + FORALLsame_cycle_(samecycle) { + if (same->visitid == samevisitid || same->visible) + qh_infiniteloop(samecycle); + same->visitid= samevisitid; + } + newfacet->visitid= ++qh visit_id; + trace4((qh ferr, 4031, "qh_mergecycle_neighbors: delete shared neighbors from newfacet\n")); + FOREACHneighbor_(newfacet) { + if (neighbor->visitid == samevisitid) { + SETref_(neighbor)= NULL; /* samecycle neighbors deleted */ + delneighbors++; + }else + neighbor->visitid= qh visit_id; + } + qh_setcompact(newfacet->neighbors); + + trace4((qh ferr, 4032, "qh_mergecycle_neighbors: update neighbors\n")); + FORALLsame_cycle_(samecycle) { + FOREACHneighbor_(same) { + if (neighbor->visitid == samevisitid) + continue; + if (neighbor->simplicial) { + if (neighbor->visitid != qh visit_id) { + qh_setappend(&newfacet->neighbors, neighbor); + qh_setreplace(neighbor->neighbors, same, newfacet); + newneighbors++; + neighbor->visitid= qh visit_id; + FOREACHridge_(neighbor->ridges) { /* update ridge in case of qh_makeridges */ + if (ridge->top == same) { + ridge->top= newfacet; + break; + }else if (ridge->bottom == same) { + ridge->bottom= newfacet; + break; + } + } + }else { + qh_makeridges(neighbor); + qh_setdel(neighbor->neighbors, same); + /* same can't be horizon facet for neighbor */ + } + }else { /* non-simplicial neighbor */ + qh_setdel(neighbor->neighbors, same); + if (neighbor->visitid != qh visit_id) { + qh_setappend(&neighbor->neighbors, newfacet); + qh_setappend(&newfacet->neighbors, neighbor); + neighbor->visitid= qh visit_id; + newneighbors++; + } + } + } + } + trace2((qh ferr, 2032, "qh_mergecycle_neighbors: deleted %d neighbors and added %d\n", + delneighbors, newneighbors)); +} /* mergecycle_neighbors */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="mergecycle_ridges">-</a> + + qh_mergecycle_ridges( samecycle, newfacet ) + add ridges/neighbors for facets in samecycle to newfacet + all new/old neighbors of newfacet marked with qh.visit_id + facets in samecycle marked with qh.visit_id-1 + newfacet marked with qh.visit_id + + returns: + newfacet has merged ridges + + notes: + ridge already updated for simplicial neighbors of samecycle with a ridge + + see: + qh_mergeridges() + qh_makeridges() + + design: + remove ridges between newfacet and samecycle + for each facet in samecycle + for each ridge in facet + update facet pointers in ridge + skip ridges processed in qh_mergecycle_neighors + free ridges between newfacet and samecycle + free ridges between facets of samecycle (on 2nd visit) + append remaining ridges to newfacet + if simpilicial facet + for each neighbor of facet + if simplicial facet + and not samecycle facet or newfacet + make ridge between neighbor and newfacet +*/ +void qh_mergecycle_ridges(facetT *samecycle, facetT *newfacet) { + facetT *same, *neighbor= NULL; + int numold=0, numnew=0; + int neighbor_i, neighbor_n; + unsigned int samevisitid; + ridgeT *ridge, **ridgep; + boolT toporient; + void **freelistp; /* used if !qh_NOmem by qh_memfree_() */ + + trace4((qh ferr, 4033, "qh_mergecycle_ridges: delete shared ridges from newfacet\n")); + samevisitid= qh visit_id -1; + FOREACHridge_(newfacet->ridges) { + neighbor= otherfacet_(ridge, newfacet); + if (neighbor->visitid == samevisitid) + SETref_(ridge)= NULL; /* ridge free'd below */ + } + qh_setcompact(newfacet->ridges); + + trace4((qh ferr, 4034, "qh_mergecycle_ridges: add ridges to newfacet\n")); + FORALLsame_cycle_(samecycle) { + FOREACHridge_(same->ridges) { + if (ridge->top == same) { + ridge->top= newfacet; + neighbor= ridge->bottom; + }else if (ridge->bottom == same) { + ridge->bottom= newfacet; + neighbor= ridge->top; + }else if (ridge->top == newfacet || ridge->bottom == newfacet) { + qh_setappend(&newfacet->ridges, ridge); + numold++; /* already set by qh_mergecycle_neighbors */ + continue; + }else { + qh_fprintf(qh ferr, 6098, "qhull internal error (qh_mergecycle_ridges): bad ridge r%d\n", ridge->id); + qh_errexit(qh_ERRqhull, NULL, ridge); + } + if (neighbor == newfacet) { + qh_setfree(&(ridge->vertices)); + qh_memfree_(ridge, (int)sizeof(ridgeT), freelistp); + numold++; + }else if (neighbor->visitid == samevisitid) { + qh_setdel(neighbor->ridges, ridge); + qh_setfree(&(ridge->vertices)); + qh_memfree_(ridge, (int)sizeof(ridgeT), freelistp); + numold++; + }else { + qh_setappend(&newfacet->ridges, ridge); + numold++; + } + } + if (same->ridges) + qh_settruncate(same->ridges, 0); + if (!same->simplicial) + continue; + FOREACHneighbor_i_(same) { /* note: !newfact->simplicial */ + if (neighbor->visitid != samevisitid && neighbor->simplicial) { + ridge= qh_newridge(); + ridge->vertices= qh_setnew_delnthsorted(same->vertices, qh hull_dim, + neighbor_i, 0); + toporient= same->toporient ^ (neighbor_i & 0x1); + if (toporient) { + ridge->top= newfacet; + ridge->bottom= neighbor; + }else { + ridge->top= neighbor; + ridge->bottom= newfacet; + } + qh_setappend(&(newfacet->ridges), ridge); + qh_setappend(&(neighbor->ridges), ridge); + numnew++; + } + } + } + + trace2((qh ferr, 2033, "qh_mergecycle_ridges: found %d old ridges and %d new ones\n", + numold, numnew)); +} /* mergecycle_ridges */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="mergecycle_vneighbors">-</a> + + qh_mergecycle_vneighbors( samecycle, newfacet ) + create vertex neighbors for newfacet from vertices of facets in samecycle + samecycle marked with visitid == qh.visit_id - 1 + + returns: + newfacet vertices with updated neighbors + marks newfacet with qh.visit_id-1 + deletes vertices that are merged away + sets delridge on all vertices (faster here than in mergecycle_ridges) + + see: + qh_mergevertex_neighbors() + + design: + for each vertex of samecycle facet + set vertex->delridge + delete samecycle facets from vertex neighbors + append newfacet to vertex neighbors + if vertex only in newfacet + delete it from newfacet + add it to qh.del_vertices for later deletion +*/ +void qh_mergecycle_vneighbors(facetT *samecycle, facetT *newfacet) { + facetT *neighbor, **neighborp; + unsigned int mergeid; + vertexT *vertex, **vertexp, *apex; + setT *vertices; + + trace4((qh ferr, 4035, "qh_mergecycle_vneighbors: update vertex neighbors for newfacet\n")); + mergeid= qh visit_id - 1; + newfacet->visitid= mergeid; + vertices= qh_basevertices(samecycle); /* temp */ + apex= SETfirstt_(samecycle->vertices, vertexT); + qh_setappend(&vertices, apex); + FOREACHvertex_(vertices) { + vertex->delridge= True; + FOREACHneighbor_(vertex) { + if (neighbor->visitid == mergeid) + SETref_(neighbor)= NULL; + } + qh_setcompact(vertex->neighbors); + qh_setappend(&vertex->neighbors, newfacet); + if (!SETsecond_(vertex->neighbors)) { + zinc_(Zcyclevertex); + trace2((qh ferr, 2034, "qh_mergecycle_vneighbors: deleted v%d when merging cycle f%d into f%d\n", + vertex->id, samecycle->id, newfacet->id)); + qh_setdelsorted(newfacet->vertices, vertex); + vertex->deleted= True; + qh_setappend(&qh del_vertices, vertex); + } + } + qh_settempfree(&vertices); + trace3((qh ferr, 3005, "qh_mergecycle_vneighbors: merged vertices from cycle f%d into f%d\n", + samecycle->id, newfacet->id)); +} /* mergecycle_vneighbors */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="mergefacet">-</a> + + qh_mergefacet( facet1, facet2, mindist, maxdist, mergeapex ) + merges facet1 into facet2 + mergeapex==qh_MERGEapex if merging new facet into coplanar horizon + + returns: + qh.max_outside and qh.min_vertex updated + initializes vertex neighbors on first merge + + returns: + facet2 contains facet1's vertices, neighbors, and ridges + facet2 moved to end of qh.facet_list + makes facet2 a newfacet + sets facet2->newmerge set + clears facet2->center (unless merging into a large facet) + clears facet2->tested and ridge->tested for facet1 + + facet1 prepended to visible_list for later deletion and partitioning + facet1->f.replace == facet2 + + adds neighboring facets to facet_mergeset if redundant or degenerate + + notes: + mindist/maxdist may be NULL (only if both NULL) + traces merge if fmax_(maxdist,-mindist) > TRACEdist + + see: + qh_mergecycle() + + design: + trace merge and check for degenerate simplex + make ridges for both facets + update qh.max_outside, qh.max_vertex, qh.min_vertex + update facet2->maxoutside and keepcentrum + update facet2->nummerge + update tested flags for facet2 + if facet1 is simplicial + merge facet1 into facet2 + else + merge facet1's neighbors into facet2 + merge facet1's ridges into facet2 + merge facet1's vertices into facet2 + merge facet1's vertex neighbors into facet2 + add facet2's vertices to qh.new_vertexlist + unless qh_MERGEapex + test facet2 for degenerate or redundant neighbors + move facet1 to qh.visible_list for later deletion + move facet2 to end of qh.newfacet_list +*/ +void qh_mergefacet(facetT *facet1, facetT *facet2, realT *mindist, realT *maxdist, boolT mergeapex) { + boolT traceonce= False; + vertexT *vertex, **vertexp; + int tracerestore=0, nummerge; + + if (facet1->tricoplanar || facet2->tricoplanar) { + if (!qh TRInormals) { + qh_fprintf(qh ferr, 6226, "Qhull internal error (qh_mergefacet): does not work for tricoplanar facets. Use option 'Q11'\n"); + qh_errexit2(qh_ERRqhull, facet1, facet2); + } + if (facet2->tricoplanar) { + facet2->tricoplanar= False; + facet2->keepcentrum= False; + } + } + zzinc_(Ztotmerge); + if (qh REPORTfreq2 && qh POSTmerging) { + if (zzval_(Ztotmerge) > qh mergereport + qh REPORTfreq2) + qh_tracemerging(); + } +#ifndef qh_NOtrace + if (qh build_cnt >= qh RERUN) { + if (mindist && (-*mindist > qh TRACEdist || *maxdist > qh TRACEdist)) { + tracerestore= 0; + qh IStracing= qh TRACElevel; + traceonce= True; + qh_fprintf(qh ferr, 8075, "qh_mergefacet: ========= trace wide merge #%d(%2.2g) for f%d into f%d, last point was p%d\n", zzval_(Ztotmerge), + fmax_(-*mindist, *maxdist), facet1->id, facet2->id, qh furthest_id); + }else if (facet1 == qh tracefacet || facet2 == qh tracefacet) { + tracerestore= qh IStracing; + qh IStracing= 4; + traceonce= True; + qh_fprintf(qh ferr, 8076, "qh_mergefacet: ========= trace merge #%d involving f%d, furthest is p%d\n", + zzval_(Ztotmerge), qh tracefacet_id, qh furthest_id); + } + } + if (qh IStracing >= 2) { + realT mergemin= -2; + realT mergemax= -2; + + if (mindist) { + mergemin= *mindist; + mergemax= *maxdist; + } + qh_fprintf(qh ferr, 8077, "qh_mergefacet: #%d merge f%d into f%d, mindist= %2.2g, maxdist= %2.2g\n", + zzval_(Ztotmerge), facet1->id, facet2->id, mergemin, mergemax); + } +#endif /* !qh_NOtrace */ + if (facet1 == facet2 || facet1->visible || facet2->visible) { + qh_fprintf(qh ferr, 6099, "qhull internal error (qh_mergefacet): either f%d and f%d are the same or one is a visible facet\n", + facet1->id, facet2->id); + qh_errexit2(qh_ERRqhull, facet1, facet2); + } + if (qh num_facets - qh num_visible <= qh hull_dim + 1) { + qh_fprintf(qh ferr, 6227, "\n\ +qhull precision error: Only %d facets remain. Can not merge another\n\ +pair. The input is too degenerate or the convexity constraints are\n\ +too strong.\n", qh hull_dim+1); + if (qh hull_dim >= 5 && !qh MERGEexact) + qh_fprintf(qh ferr, 8079, "Option 'Qx' may avoid this problem.\n"); + qh_errexit(qh_ERRprec, NULL, NULL); + } + if (!qh VERTEXneighbors) + qh_vertexneighbors(); + qh_makeridges(facet1); + qh_makeridges(facet2); + if (qh IStracing >=4) + qh_errprint("MERGING", facet1, facet2, NULL, NULL); + if (mindist) { + maximize_(qh max_outside, *maxdist); + maximize_(qh max_vertex, *maxdist); +#if qh_MAXoutside + maximize_(facet2->maxoutside, *maxdist); +#endif + minimize_(qh min_vertex, *mindist); + if (!facet2->keepcentrum + && (*maxdist > qh WIDEfacet || *mindist < -qh WIDEfacet)) { + facet2->keepcentrum= True; + zinc_(Zwidefacet); + } + } + nummerge= facet1->nummerge + facet2->nummerge + 1; + if (nummerge >= qh_MAXnummerge) + facet2->nummerge= qh_MAXnummerge; + else + facet2->nummerge= (short unsigned int)nummerge; + facet2->newmerge= True; + facet2->dupridge= False; + qh_updatetested(facet1, facet2); + if (qh hull_dim > 2 && qh_setsize(facet1->vertices) == qh hull_dim) + qh_mergesimplex(facet1, facet2, mergeapex); + else { + qh vertex_visit++; + FOREACHvertex_(facet2->vertices) + vertex->visitid= qh vertex_visit; + if (qh hull_dim == 2) + qh_mergefacet2d(facet1, facet2); + else { + qh_mergeneighbors(facet1, facet2); + qh_mergevertices(facet1->vertices, &facet2->vertices); + } + qh_mergeridges(facet1, facet2); + qh_mergevertex_neighbors(facet1, facet2); + if (!facet2->newfacet) + qh_newvertices(facet2->vertices); + } + if (!mergeapex) + qh_degen_redundant_neighbors(facet2, facet1); + if (facet2->coplanar || !facet2->newfacet) { + zinc_(Zmergeintohorizon); + }else if (!facet1->newfacet && facet2->newfacet) { + zinc_(Zmergehorizon); + }else { + zinc_(Zmergenew); + } + qh_willdelete(facet1, facet2); + qh_removefacet(facet2); /* append as a newfacet to end of qh facet_list */ + qh_appendfacet(facet2); + facet2->newfacet= True; + facet2->tested= False; + qh_tracemerge(facet1, facet2); + if (traceonce) { + qh_fprintf(qh ferr, 8080, "qh_mergefacet: end of wide tracing\n"); + qh IStracing= tracerestore; + } +} /* mergefacet */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="mergefacet2d">-</a> + + qh_mergefacet2d( facet1, facet2 ) + in 2d, merges neighbors and vertices of facet1 into facet2 + + returns: + build ridges for neighbors if necessary + facet2 looks like a simplicial facet except for centrum, ridges + neighbors are opposite the corresponding vertex + maintains orientation of facet2 + + notes: + qh_mergefacet() retains non-simplicial structures + they are not needed in 2d, but later routines may use them + preserves qh.vertex_visit for qh_mergevertex_neighbors() + + design: + get vertices and neighbors + determine new vertices and neighbors + set new vertices and neighbors and adjust orientation + make ridges for new neighbor if needed +*/ +void qh_mergefacet2d(facetT *facet1, facetT *facet2) { + vertexT *vertex1A, *vertex1B, *vertex2A, *vertex2B, *vertexA, *vertexB; + facetT *neighbor1A, *neighbor1B, *neighbor2A, *neighbor2B, *neighborA, *neighborB; + + vertex1A= SETfirstt_(facet1->vertices, vertexT); + vertex1B= SETsecondt_(facet1->vertices, vertexT); + vertex2A= SETfirstt_(facet2->vertices, vertexT); + vertex2B= SETsecondt_(facet2->vertices, vertexT); + neighbor1A= SETfirstt_(facet1->neighbors, facetT); + neighbor1B= SETsecondt_(facet1->neighbors, facetT); + neighbor2A= SETfirstt_(facet2->neighbors, facetT); + neighbor2B= SETsecondt_(facet2->neighbors, facetT); + if (vertex1A == vertex2A) { + vertexA= vertex1B; + vertexB= vertex2B; + neighborA= neighbor2A; + neighborB= neighbor1A; + }else if (vertex1A == vertex2B) { + vertexA= vertex1B; + vertexB= vertex2A; + neighborA= neighbor2B; + neighborB= neighbor1A; + }else if (vertex1B == vertex2A) { + vertexA= vertex1A; + vertexB= vertex2B; + neighborA= neighbor2A; + neighborB= neighbor1B; + }else { /* 1B == 2B */ + vertexA= vertex1A; + vertexB= vertex2A; + neighborA= neighbor2B; + neighborB= neighbor1B; + } + /* vertexB always from facet2, neighborB always from facet1 */ + if (vertexA->id > vertexB->id) { + SETfirst_(facet2->vertices)= vertexA; + SETsecond_(facet2->vertices)= vertexB; + if (vertexB == vertex2A) + facet2->toporient= !facet2->toporient; + SETfirst_(facet2->neighbors)= neighborA; + SETsecond_(facet2->neighbors)= neighborB; + }else { + SETfirst_(facet2->vertices)= vertexB; + SETsecond_(facet2->vertices)= vertexA; + if (vertexB == vertex2B) + facet2->toporient= !facet2->toporient; + SETfirst_(facet2->neighbors)= neighborB; + SETsecond_(facet2->neighbors)= neighborA; + } + qh_makeridges(neighborB); + qh_setreplace(neighborB->neighbors, facet1, facet2); + trace4((qh ferr, 4036, "qh_mergefacet2d: merged v%d and neighbor f%d of f%d into f%d\n", + vertexA->id, neighborB->id, facet1->id, facet2->id)); +} /* mergefacet2d */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="mergeneighbors">-</a> + + qh_mergeneighbors( facet1, facet2 ) + merges the neighbors of facet1 into facet2 + + see: + qh_mergecycle_neighbors() + + design: + for each neighbor of facet1 + if neighbor is also a neighbor of facet2 + if neighbor is simpilicial + make ridges for later deletion as a degenerate facet + update its neighbor set + else + move the neighbor relation to facet2 + remove the neighbor relation for facet1 and facet2 +*/ +void qh_mergeneighbors(facetT *facet1, facetT *facet2) { + facetT *neighbor, **neighborp; + + trace4((qh ferr, 4037, "qh_mergeneighbors: merge neighbors of f%d and f%d\n", + facet1->id, facet2->id)); + qh visit_id++; + FOREACHneighbor_(facet2) { + neighbor->visitid= qh visit_id; + } + FOREACHneighbor_(facet1) { + if (neighbor->visitid == qh visit_id) { + if (neighbor->simplicial) /* is degen, needs ridges */ + qh_makeridges(neighbor); + if (SETfirstt_(neighbor->neighbors, facetT) != facet1) /*keep newfacet->horizon*/ + qh_setdel(neighbor->neighbors, facet1); + else { + qh_setdel(neighbor->neighbors, facet2); + qh_setreplace(neighbor->neighbors, facet1, facet2); + } + }else if (neighbor != facet2) { + qh_setappend(&(facet2->neighbors), neighbor); + qh_setreplace(neighbor->neighbors, facet1, facet2); + } + } + qh_setdel(facet1->neighbors, facet2); /* here for makeridges */ + qh_setdel(facet2->neighbors, facet1); +} /* mergeneighbors */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="mergeridges">-</a> + + qh_mergeridges( facet1, facet2 ) + merges the ridge set of facet1 into facet2 + + returns: + may delete all ridges for a vertex + sets vertex->delridge on deleted ridges + + see: + qh_mergecycle_ridges() + + design: + delete ridges between facet1 and facet2 + mark (delridge) vertices on these ridges for later testing + for each remaining ridge + rename facet1 to facet2 +*/ +void qh_mergeridges(facetT *facet1, facetT *facet2) { + ridgeT *ridge, **ridgep; + vertexT *vertex, **vertexp; + + trace4((qh ferr, 4038, "qh_mergeridges: merge ridges of f%d and f%d\n", + facet1->id, facet2->id)); + FOREACHridge_(facet2->ridges) { + if ((ridge->top == facet1) || (ridge->bottom == facet1)) { + FOREACHvertex_(ridge->vertices) + vertex->delridge= True; + qh_delridge(ridge); /* expensive in high-d, could rebuild */ + ridgep--; /*repeat*/ + } + } + FOREACHridge_(facet1->ridges) { + if (ridge->top == facet1) + ridge->top= facet2; + else + ridge->bottom= facet2; + qh_setappend(&(facet2->ridges), ridge); + } +} /* mergeridges */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="mergesimplex">-</a> + + qh_mergesimplex( facet1, facet2, mergeapex ) + merge simplicial facet1 into facet2 + mergeapex==qh_MERGEapex if merging samecycle into horizon facet + vertex id is latest (most recently created) + facet1 may be contained in facet2 + ridges exist for both facets + + returns: + facet2 with updated vertices, ridges, neighbors + updated neighbors for facet1's vertices + facet1 not deleted + sets vertex->delridge on deleted ridges + + notes: + special case code since this is the most common merge + called from qh_mergefacet() + + design: + if qh_MERGEapex + add vertices of facet2 to qh.new_vertexlist if necessary + add apex to facet2 + else + for each ridge between facet1 and facet2 + set vertex->delridge + determine the apex for facet1 (i.e., vertex to be merged) + unless apex already in facet2 + insert apex into vertices for facet2 + add vertices of facet2 to qh.new_vertexlist if necessary + add apex to qh.new_vertexlist if necessary + for each vertex of facet1 + if apex + rename facet1 to facet2 in its vertex neighbors + else + delete facet1 from vertex neighors + if only in facet2 + add vertex to qh.del_vertices for later deletion + for each ridge of facet1 + delete ridges between facet1 and facet2 + append other ridges to facet2 after renaming facet to facet2 +*/ +void qh_mergesimplex(facetT *facet1, facetT *facet2, boolT mergeapex) { + vertexT *vertex, **vertexp, *apex; + ridgeT *ridge, **ridgep; + boolT issubset= False; + int vertex_i= -1, vertex_n; + facetT *neighbor, **neighborp, *otherfacet; + + if (mergeapex) { + if (!facet2->newfacet) + qh_newvertices(facet2->vertices); /* apex is new */ + apex= SETfirstt_(facet1->vertices, vertexT); + if (SETfirstt_(facet2->vertices, vertexT) != apex) + qh_setaddnth(&facet2->vertices, 0, apex); /* apex has last id */ + else + issubset= True; + }else { + zinc_(Zmergesimplex); + FOREACHvertex_(facet1->vertices) + vertex->seen= False; + FOREACHridge_(facet1->ridges) { + if (otherfacet_(ridge, facet1) == facet2) { + FOREACHvertex_(ridge->vertices) { + vertex->seen= True; + vertex->delridge= True; + } + break; + } + } + FOREACHvertex_(facet1->vertices) { + if (!vertex->seen) + break; /* must occur */ + } + apex= vertex; + trace4((qh ferr, 4039, "qh_mergesimplex: merge apex v%d of f%d into facet f%d\n", + apex->id, facet1->id, facet2->id)); + FOREACHvertex_i_(facet2->vertices) { + if (vertex->id < apex->id) { + break; + }else if (vertex->id == apex->id) { + issubset= True; + break; + } + } + if (!issubset) + qh_setaddnth(&facet2->vertices, vertex_i, apex); + if (!facet2->newfacet) + qh_newvertices(facet2->vertices); + else if (!apex->newlist) { + qh_removevertex(apex); + qh_appendvertex(apex); + } + } + trace4((qh ferr, 4040, "qh_mergesimplex: update vertex neighbors of f%d\n", + facet1->id)); + FOREACHvertex_(facet1->vertices) { + if (vertex == apex && !issubset) + qh_setreplace(vertex->neighbors, facet1, facet2); + else { + qh_setdel(vertex->neighbors, facet1); + if (!SETsecond_(vertex->neighbors)) + qh_mergevertex_del(vertex, facet1, facet2); + } + } + trace4((qh ferr, 4041, "qh_mergesimplex: merge ridges and neighbors of f%d into f%d\n", + facet1->id, facet2->id)); + qh visit_id++; + FOREACHneighbor_(facet2) + neighbor->visitid= qh visit_id; + FOREACHridge_(facet1->ridges) { + otherfacet= otherfacet_(ridge, facet1); + if (otherfacet == facet2) { + qh_setdel(facet2->ridges, ridge); + qh_setfree(&(ridge->vertices)); + qh_memfree(ridge, (int)sizeof(ridgeT)); + qh_setdel(facet2->neighbors, facet1); + }else { + qh_setappend(&facet2->ridges, ridge); + if (otherfacet->visitid != qh visit_id) { + qh_setappend(&facet2->neighbors, otherfacet); + qh_setreplace(otherfacet->neighbors, facet1, facet2); + otherfacet->visitid= qh visit_id; + }else { + if (otherfacet->simplicial) /* is degen, needs ridges */ + qh_makeridges(otherfacet); + if (SETfirstt_(otherfacet->neighbors, facetT) != facet1) + qh_setdel(otherfacet->neighbors, facet1); + else { /*keep newfacet->neighbors->horizon*/ + qh_setdel(otherfacet->neighbors, facet2); + qh_setreplace(otherfacet->neighbors, facet1, facet2); + } + } + if (ridge->top == facet1) /* wait until after qh_makeridges */ + ridge->top= facet2; + else + ridge->bottom= facet2; + } + } + SETfirst_(facet1->ridges)= NULL; /* it will be deleted */ + trace3((qh ferr, 3006, "qh_mergesimplex: merged simplex f%d apex v%d into facet f%d\n", + facet1->id, getid_(apex), facet2->id)); +} /* mergesimplex */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="mergevertex_del">-</a> + + qh_mergevertex_del( vertex, facet1, facet2 ) + delete a vertex because of merging facet1 into facet2 + + returns: + deletes vertex from facet2 + adds vertex to qh.del_vertices for later deletion +*/ +void qh_mergevertex_del(vertexT *vertex, facetT *facet1, facetT *facet2) { + + zinc_(Zmergevertex); + trace2((qh ferr, 2035, "qh_mergevertex_del: deleted v%d when merging f%d into f%d\n", + vertex->id, facet1->id, facet2->id)); + qh_setdelsorted(facet2->vertices, vertex); + vertex->deleted= True; + qh_setappend(&qh del_vertices, vertex); +} /* mergevertex_del */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="mergevertex_neighbors">-</a> + + qh_mergevertex_neighbors( facet1, facet2 ) + merge the vertex neighbors of facet1 to facet2 + + returns: + if vertex is current qh.vertex_visit + deletes facet1 from vertex->neighbors + else + renames facet1 to facet2 in vertex->neighbors + deletes vertices if only one neighbor + + notes: + assumes vertex neighbor sets are good +*/ +void qh_mergevertex_neighbors(facetT *facet1, facetT *facet2) { + vertexT *vertex, **vertexp; + + trace4((qh ferr, 4042, "qh_mergevertex_neighbors: merge vertex neighbors of f%d and f%d\n", + facet1->id, facet2->id)); + if (qh tracevertex) { + qh_fprintf(qh ferr, 8081, "qh_mergevertex_neighbors: of f%d and f%d at furthest p%d f0= %p\n", + facet1->id, facet2->id, qh furthest_id, qh tracevertex->neighbors->e[0].p); + qh_errprint("TRACE", NULL, NULL, NULL, qh tracevertex); + } + FOREACHvertex_(facet1->vertices) { + if (vertex->visitid != qh vertex_visit) + qh_setreplace(vertex->neighbors, facet1, facet2); + else { + qh_setdel(vertex->neighbors, facet1); + if (!SETsecond_(vertex->neighbors)) + qh_mergevertex_del(vertex, facet1, facet2); + } + } + if (qh tracevertex) + qh_errprint("TRACE", NULL, NULL, NULL, qh tracevertex); +} /* mergevertex_neighbors */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="mergevertices">-</a> + + qh_mergevertices( vertices1, vertices2 ) + merges the vertex set of facet1 into facet2 + + returns: + replaces vertices2 with merged set + preserves vertex_visit for qh_mergevertex_neighbors + updates qh.newvertex_list + + design: + create a merged set of both vertices (in inverse id order) +*/ +void qh_mergevertices(setT *vertices1, setT **vertices2) { + int newsize= qh_setsize(vertices1)+qh_setsize(*vertices2) - qh hull_dim + 1; + setT *mergedvertices; + vertexT *vertex, **vertexp, **vertex2= SETaddr_(*vertices2, vertexT); + + mergedvertices= qh_settemp(newsize); + FOREACHvertex_(vertices1) { + if (!*vertex2 || vertex->id > (*vertex2)->id) + qh_setappend(&mergedvertices, vertex); + else { + while (*vertex2 && (*vertex2)->id > vertex->id) + qh_setappend(&mergedvertices, *vertex2++); + if (!*vertex2 || (*vertex2)->id < vertex->id) + qh_setappend(&mergedvertices, vertex); + else + qh_setappend(&mergedvertices, *vertex2++); + } + } + while (*vertex2) + qh_setappend(&mergedvertices, *vertex2++); + if (newsize < qh_setsize(mergedvertices)) { + qh_fprintf(qh ferr, 6100, "qhull internal error (qh_mergevertices): facets did not share a ridge\n"); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + qh_setfree(vertices2); + *vertices2= mergedvertices; + qh_settemppop(); +} /* mergevertices */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="neighbor_intersections">-</a> + + qh_neighbor_intersections( vertex ) + return intersection of all vertices in vertex->neighbors except for vertex + + returns: + returns temporary set of vertices + does not include vertex + NULL if a neighbor is simplicial + NULL if empty set + + notes: + used for renaming vertices + + design: + initialize the intersection set with vertices of the first two neighbors + delete vertex from the intersection + for each remaining neighbor + intersect its vertex set with the intersection set + return NULL if empty + return the intersection set +*/ +setT *qh_neighbor_intersections(vertexT *vertex) { + facetT *neighbor, **neighborp, *neighborA, *neighborB; + setT *intersect; + int neighbor_i, neighbor_n; + + FOREACHneighbor_(vertex) { + if (neighbor->simplicial) + return NULL; + } + neighborA= SETfirstt_(vertex->neighbors, facetT); + neighborB= SETsecondt_(vertex->neighbors, facetT); + zinc_(Zintersectnum); + if (!neighborA) + return NULL; + if (!neighborB) + intersect= qh_setcopy(neighborA->vertices, 0); + else + intersect= qh_vertexintersect_new(neighborA->vertices, neighborB->vertices); + qh_settemppush(intersect); + qh_setdelsorted(intersect, vertex); + FOREACHneighbor_i_(vertex) { + if (neighbor_i >= 2) { + zinc_(Zintersectnum); + qh_vertexintersect(&intersect, neighbor->vertices); + if (!SETfirst_(intersect)) { + zinc_(Zintersectfail); + qh_settempfree(&intersect); + return NULL; + } + } + } + trace3((qh ferr, 3007, "qh_neighbor_intersections: %d vertices in neighbor intersection of v%d\n", + qh_setsize(intersect), vertex->id)); + return intersect; +} /* neighbor_intersections */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="newvertices">-</a> + + qh_newvertices( vertices ) + add vertices to end of qh.vertex_list (marks as new vertices) + + returns: + vertices on qh.newvertex_list + vertex->newlist set +*/ +void qh_newvertices(setT *vertices) { + vertexT *vertex, **vertexp; + + FOREACHvertex_(vertices) { + if (!vertex->newlist) { + qh_removevertex(vertex); + qh_appendvertex(vertex); + } + } +} /* newvertices */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="reducevertices">-</a> + + qh_reducevertices() + reduce extra vertices, shared vertices, and redundant vertices + facet->newmerge is set if merged since last call + if !qh.MERGEvertices, only removes extra vertices + + returns: + True if also merged degen_redundant facets + vertices are renamed if possible + clears facet->newmerge and vertex->delridge + + notes: + ignored if 2-d + + design: + merge any degenerate or redundant facets + for each newly merged facet + remove extra vertices + if qh.MERGEvertices + for each newly merged facet + for each vertex + if vertex was on a deleted ridge + rename vertex if it is shared + remove delridge flag from new vertices +*/ +boolT qh_reducevertices(void) { + int numshare=0, numrename= 0; + boolT degenredun= False; + facetT *newfacet; + vertexT *vertex, **vertexp; + + if (qh hull_dim == 2) + return False; + if (qh_merge_degenredundant()) + degenredun= True; + LABELrestart: + FORALLnew_facets { + if (newfacet->newmerge) { + if (!qh MERGEvertices) + newfacet->newmerge= False; + qh_remove_extravertices(newfacet); + } + } + if (!qh MERGEvertices) + return False; + FORALLnew_facets { + if (newfacet->newmerge) { + newfacet->newmerge= False; + FOREACHvertex_(newfacet->vertices) { + if (vertex->delridge) { + if (qh_rename_sharedvertex(vertex, newfacet)) { + numshare++; + vertexp--; /* repeat since deleted vertex */ + } + } + } + } + } + FORALLvertex_(qh newvertex_list) { + if (vertex->delridge && !vertex->deleted) { + vertex->delridge= False; + if (qh hull_dim >= 4 && qh_redundant_vertex(vertex)) { + numrename++; + if (qh_merge_degenredundant()) { + degenredun= True; + goto LABELrestart; + } + } + } + } + trace1((qh ferr, 1014, "qh_reducevertices: renamed %d shared vertices and %d redundant vertices. Degen? %d\n", + numshare, numrename, degenredun)); + return degenredun; +} /* reducevertices */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="redundant_vertex">-</a> + + qh_redundant_vertex( vertex ) + detect and rename a redundant vertex + vertices have full vertex->neighbors + + returns: + returns true if find a redundant vertex + deletes vertex(vertex->deleted) + + notes: + only needed if vertex->delridge and hull_dim >= 4 + may add degenerate facets to qh.facet_mergeset + doesn't change vertex->neighbors or create redundant facets + + design: + intersect vertices of all facet neighbors of vertex + determine ridges for these vertices + if find a new vertex for vertex amoung these ridges and vertices + rename vertex to the new vertex +*/ +vertexT *qh_redundant_vertex(vertexT *vertex) { + vertexT *newvertex= NULL; + setT *vertices, *ridges; + + trace3((qh ferr, 3008, "qh_redundant_vertex: check if v%d can be renamed\n", vertex->id)); + if ((vertices= qh_neighbor_intersections(vertex))) { + ridges= qh_vertexridges(vertex); + if ((newvertex= qh_find_newvertex(vertex, vertices, ridges))) + qh_renamevertex(vertex, newvertex, ridges, NULL, NULL); + qh_settempfree(&ridges); + qh_settempfree(&vertices); + } + return newvertex; +} /* redundant_vertex */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="remove_extravertices">-</a> + + qh_remove_extravertices( facet ) + remove extra vertices from non-simplicial facets + + returns: + returns True if it finds them + + design: + for each vertex in facet + if vertex not in a ridge (i.e., no longer used) + delete vertex from facet + delete facet from vertice's neighbors + unless vertex in another facet + add vertex to qh.del_vertices for later deletion +*/ +boolT qh_remove_extravertices(facetT *facet) { + ridgeT *ridge, **ridgep; + vertexT *vertex, **vertexp; + boolT foundrem= False; + + trace4((qh ferr, 4043, "qh_remove_extravertices: test f%d for extra vertices\n", + facet->id)); + FOREACHvertex_(facet->vertices) + vertex->seen= False; + FOREACHridge_(facet->ridges) { + FOREACHvertex_(ridge->vertices) + vertex->seen= True; + } + FOREACHvertex_(facet->vertices) { + if (!vertex->seen) { + foundrem= True; + zinc_(Zremvertex); + qh_setdelsorted(facet->vertices, vertex); + qh_setdel(vertex->neighbors, facet); + if (!qh_setsize(vertex->neighbors)) { + vertex->deleted= True; + qh_setappend(&qh del_vertices, vertex); + zinc_(Zremvertexdel); + trace2((qh ferr, 2036, "qh_remove_extravertices: v%d deleted because it's lost all ridges\n", vertex->id)); + }else + trace3((qh ferr, 3009, "qh_remove_extravertices: v%d removed from f%d because it's lost all ridges\n", vertex->id, facet->id)); + vertexp--; /*repeat*/ + } + } + return foundrem; +} /* remove_extravertices */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="rename_sharedvertex">-</a> + + qh_rename_sharedvertex( vertex, facet ) + detect and rename if shared vertex in facet + vertices have full ->neighbors + + returns: + newvertex or NULL + the vertex may still exist in other facets (i.e., a neighbor was pinched) + does not change facet->neighbors + updates vertex->neighbors + + notes: + a shared vertex for a facet is only in ridges to one neighbor + this may undo a pinched facet + + it does not catch pinches involving multiple facets. These appear + to be difficult to detect, since an exhaustive search is too expensive. + + design: + if vertex only has two neighbors + determine the ridges that contain the vertex + determine the vertices shared by both neighbors + if can find a new vertex in this set + rename the vertex to the new vertex +*/ +vertexT *qh_rename_sharedvertex(vertexT *vertex, facetT *facet) { + facetT *neighbor, **neighborp, *neighborA= NULL; + setT *vertices, *ridges; + vertexT *newvertex; + + if (qh_setsize(vertex->neighbors) == 2) { + neighborA= SETfirstt_(vertex->neighbors, facetT); + if (neighborA == facet) + neighborA= SETsecondt_(vertex->neighbors, facetT); + }else if (qh hull_dim == 3) + return NULL; + else { + qh visit_id++; + FOREACHneighbor_(facet) + neighbor->visitid= qh visit_id; + FOREACHneighbor_(vertex) { + if (neighbor->visitid == qh visit_id) { + if (neighborA) + return NULL; + neighborA= neighbor; + } + } + if (!neighborA) { + qh_fprintf(qh ferr, 6101, "qhull internal error (qh_rename_sharedvertex): v%d's neighbors not in f%d\n", + vertex->id, facet->id); + qh_errprint("ERRONEOUS", facet, NULL, NULL, vertex); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + } + /* the vertex is shared by facet and neighborA */ + ridges= qh_settemp(qh TEMPsize); + neighborA->visitid= ++qh visit_id; + qh_vertexridges_facet(vertex, facet, &ridges); + trace2((qh ferr, 2037, "qh_rename_sharedvertex: p%d(v%d) is shared by f%d(%d ridges) and f%d\n", + qh_pointid(vertex->point), vertex->id, facet->id, qh_setsize(ridges), neighborA->id)); + zinc_(Zintersectnum); + vertices= qh_vertexintersect_new(facet->vertices, neighborA->vertices); + qh_setdel(vertices, vertex); + qh_settemppush(vertices); + if ((newvertex= qh_find_newvertex(vertex, vertices, ridges))) + qh_renamevertex(vertex, newvertex, ridges, facet, neighborA); + qh_settempfree(&vertices); + qh_settempfree(&ridges); + return newvertex; +} /* rename_sharedvertex */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="renameridgevertex">-</a> + + qh_renameridgevertex( ridge, oldvertex, newvertex ) + renames oldvertex as newvertex in ridge + + returns: + + design: + delete oldvertex from ridge + if newvertex already in ridge + copy ridge->noconvex to another ridge if possible + delete the ridge + else + insert newvertex into the ridge + adjust the ridge's orientation +*/ +void qh_renameridgevertex(ridgeT *ridge, vertexT *oldvertex, vertexT *newvertex) { + int nth= 0, oldnth; + facetT *temp; + vertexT *vertex, **vertexp; + + oldnth= qh_setindex(ridge->vertices, oldvertex); + qh_setdelnthsorted(ridge->vertices, oldnth); + FOREACHvertex_(ridge->vertices) { + if (vertex == newvertex) { + zinc_(Zdelridge); + if (ridge->nonconvex) /* only one ridge has nonconvex set */ + qh_copynonconvex(ridge); + trace2((qh ferr, 2038, "qh_renameridgevertex: ridge r%d deleted. It contained both v%d and v%d\n", + ridge->id, oldvertex->id, newvertex->id)); + qh_delridge(ridge); + return; + } + if (vertex->id < newvertex->id) + break; + nth++; + } + qh_setaddnth(&ridge->vertices, nth, newvertex); + if (abs(oldnth - nth)%2) { + trace3((qh ferr, 3010, "qh_renameridgevertex: swapped the top and bottom of ridge r%d\n", + ridge->id)); + temp= ridge->top; + ridge->top= ridge->bottom; + ridge->bottom= temp; + } +} /* renameridgevertex */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="renamevertex">-</a> + + qh_renamevertex( oldvertex, newvertex, ridges, oldfacet, neighborA ) + renames oldvertex as newvertex in ridges + gives oldfacet/neighborA if oldvertex is shared between two facets + + returns: + oldvertex may still exist afterwards + + + notes: + can not change neighbors of newvertex (since it's a subset) + + design: + for each ridge in ridges + rename oldvertex to newvertex and delete degenerate ridges + if oldfacet not defined + for each neighbor of oldvertex + delete oldvertex from neighbor's vertices + remove extra vertices from neighbor + add oldvertex to qh.del_vertices + else if oldvertex only between oldfacet and neighborA + delete oldvertex from oldfacet and neighborA + add oldvertex to qh.del_vertices + else oldvertex is in oldfacet and neighborA and other facets (i.e., pinched) + delete oldvertex from oldfacet + delete oldfacet from oldvertice's neighbors + remove extra vertices (e.g., oldvertex) from neighborA +*/ +void qh_renamevertex(vertexT *oldvertex, vertexT *newvertex, setT *ridges, facetT *oldfacet, facetT *neighborA) { + facetT *neighbor, **neighborp; + ridgeT *ridge, **ridgep; + boolT istrace= False; + + if (qh IStracing >= 2 || oldvertex->id == qh tracevertex_id || + newvertex->id == qh tracevertex_id) + istrace= True; + FOREACHridge_(ridges) + qh_renameridgevertex(ridge, oldvertex, newvertex); + if (!oldfacet) { + zinc_(Zrenameall); + if (istrace) + qh_fprintf(qh ferr, 8082, "qh_renamevertex: renamed v%d to v%d in several facets\n", + oldvertex->id, newvertex->id); + FOREACHneighbor_(oldvertex) { + qh_maydropneighbor(neighbor); + qh_setdelsorted(neighbor->vertices, oldvertex); + if (qh_remove_extravertices(neighbor)) + neighborp--; /* neighbor may be deleted */ + } + if (!oldvertex->deleted) { + oldvertex->deleted= True; + qh_setappend(&qh del_vertices, oldvertex); + } + }else if (qh_setsize(oldvertex->neighbors) == 2) { + zinc_(Zrenameshare); + if (istrace) + qh_fprintf(qh ferr, 8083, "qh_renamevertex: renamed v%d to v%d in oldfacet f%d\n", + oldvertex->id, newvertex->id, oldfacet->id); + FOREACHneighbor_(oldvertex) + qh_setdelsorted(neighbor->vertices, oldvertex); + oldvertex->deleted= True; + qh_setappend(&qh del_vertices, oldvertex); + }else { + zinc_(Zrenamepinch); + if (istrace || qh IStracing) + qh_fprintf(qh ferr, 8084, "qh_renamevertex: renamed pinched v%d to v%d between f%d and f%d\n", + oldvertex->id, newvertex->id, oldfacet->id, neighborA->id); + qh_setdelsorted(oldfacet->vertices, oldvertex); + qh_setdel(oldvertex->neighbors, oldfacet); + qh_remove_extravertices(neighborA); + } +} /* renamevertex */ + + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="test_appendmerge">-</a> + + qh_test_appendmerge( facet, neighbor ) + tests facet/neighbor for convexity + appends to mergeset if non-convex + if pre-merging, + nop if qh.SKIPconvex, or qh.MERGEexact and coplanar + + returns: + true if appends facet/neighbor to mergeset + sets facet->center as needed + does not change facet->seen + + design: + if qh.cos_max is defined + if the angle between facet normals is too shallow + append an angle-coplanar merge to qh.mergeset + return True + make facet's centrum if needed + if facet's centrum is above the neighbor + set isconcave + else + if facet's centrum is not below the neighbor + set iscoplanar + make neighbor's centrum if needed + if neighbor's centrum is above the facet + set isconcave + else if neighbor's centrum is not below the facet + set iscoplanar + if isconcave or iscoplanar + get angle if needed + append concave or coplanar merge to qh.mergeset +*/ +boolT qh_test_appendmerge(facetT *facet, facetT *neighbor) { + realT dist, dist2= -REALmax, angle= -REALmax; + boolT isconcave= False, iscoplanar= False, okangle= False; + + if (qh SKIPconvex && !qh POSTmerging) + return False; + if ((!qh MERGEexact || qh POSTmerging) && qh cos_max < REALmax/2) { + angle= qh_getangle(facet->normal, neighbor->normal); + zinc_(Zangletests); + if (angle > qh cos_max) { + zinc_(Zcoplanarangle); + qh_appendmergeset(facet, neighbor, MRGanglecoplanar, &angle); + trace2((qh ferr, 2039, "qh_test_appendmerge: coplanar angle %4.4g between f%d and f%d\n", + angle, facet->id, neighbor->id)); + return True; + }else + okangle= True; + } + if (!facet->center) + facet->center= qh_getcentrum(facet); + zzinc_(Zcentrumtests); + qh_distplane(facet->center, neighbor, &dist); + if (dist > qh centrum_radius) + isconcave= True; + else { + if (dist > -qh centrum_radius) + iscoplanar= True; + if (!neighbor->center) + neighbor->center= qh_getcentrum(neighbor); + zzinc_(Zcentrumtests); + qh_distplane(neighbor->center, facet, &dist2); + if (dist2 > qh centrum_radius) + isconcave= True; + else if (!iscoplanar && dist2 > -qh centrum_radius) + iscoplanar= True; + } + if (!isconcave && (!iscoplanar || (qh MERGEexact && !qh POSTmerging))) + return False; + if (!okangle && qh ANGLEmerge) { + angle= qh_getangle(facet->normal, neighbor->normal); + zinc_(Zangletests); + } + if (isconcave) { + zinc_(Zconcaveridge); + if (qh ANGLEmerge) + angle += qh_ANGLEconcave + 0.5; + qh_appendmergeset(facet, neighbor, MRGconcave, &angle); + trace0((qh ferr, 18, "qh_test_appendmerge: concave f%d to f%d dist %4.4g and reverse dist %4.4g angle %4.4g during p%d\n", + facet->id, neighbor->id, dist, dist2, angle, qh furthest_id)); + }else /* iscoplanar */ { + zinc_(Zcoplanarcentrum); + qh_appendmergeset(facet, neighbor, MRGcoplanar, &angle); + trace2((qh ferr, 2040, "qh_test_appendmerge: coplanar f%d to f%d dist %4.4g, reverse dist %4.4g angle %4.4g\n", + facet->id, neighbor->id, dist, dist2, angle)); + } + return True; +} /* test_appendmerge */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="test_vneighbors">-</a> + + qh_test_vneighbors() + test vertex neighbors for convexity + tests all facets on qh.newfacet_list + + returns: + true if non-convex vneighbors appended to qh.facet_mergeset + initializes vertex neighbors if needed + + notes: + assumes all facet neighbors have been tested + this can be expensive + this does not guarantee that a centrum is below all facets + but it is unlikely + uses qh.visit_id + + design: + build vertex neighbors if necessary + for all new facets + for all vertices + for each unvisited facet neighbor of the vertex + test new facet and neighbor for convexity +*/ +boolT qh_test_vneighbors(void /* qh.newfacet_list */) { + facetT *newfacet, *neighbor, **neighborp; + vertexT *vertex, **vertexp; + int nummerges= 0; + + trace1((qh ferr, 1015, "qh_test_vneighbors: testing vertex neighbors for convexity\n")); + if (!qh VERTEXneighbors) + qh_vertexneighbors(); + FORALLnew_facets + newfacet->seen= False; + FORALLnew_facets { + newfacet->seen= True; + newfacet->visitid= qh visit_id++; + FOREACHneighbor_(newfacet) + newfacet->visitid= qh visit_id; + FOREACHvertex_(newfacet->vertices) { + FOREACHneighbor_(vertex) { + if (neighbor->seen || neighbor->visitid == qh visit_id) + continue; + if (qh_test_appendmerge(newfacet, neighbor)) + nummerges++; + } + } + } + zadd_(Ztestvneighbor, nummerges); + trace1((qh ferr, 1016, "qh_test_vneighbors: found %d non-convex, vertex neighbors\n", + nummerges)); + return (nummerges > 0); +} /* test_vneighbors */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="tracemerge">-</a> + + qh_tracemerge( facet1, facet2 ) + print trace message after merge +*/ +void qh_tracemerge(facetT *facet1, facetT *facet2) { + boolT waserror= False; + +#ifndef qh_NOtrace + if (qh IStracing >= 4) + qh_errprint("MERGED", facet2, NULL, NULL, NULL); + if (facet2 == qh tracefacet || (qh tracevertex && qh tracevertex->newlist)) { + qh_fprintf(qh ferr, 8085, "qh_tracemerge: trace facet and vertex after merge of f%d and f%d, furthest p%d\n", facet1->id, facet2->id, qh furthest_id); + if (facet2 != qh tracefacet) + qh_errprint("TRACE", qh tracefacet, + (qh tracevertex && qh tracevertex->neighbors) ? + SETfirstt_(qh tracevertex->neighbors, facetT) : NULL, + NULL, qh tracevertex); + } + if (qh tracevertex) { + if (qh tracevertex->deleted) + qh_fprintf(qh ferr, 8086, "qh_tracemerge: trace vertex deleted at furthest p%d\n", + qh furthest_id); + else + qh_checkvertex(qh tracevertex); + } + if (qh tracefacet) { + qh_checkfacet(qh tracefacet, True, &waserror); + if (waserror) + qh_errexit(qh_ERRqhull, qh tracefacet, NULL); + } +#endif /* !qh_NOtrace */ + if (qh CHECKfrequently || qh IStracing >= 4) { /* can't check polygon here */ + qh_checkfacet(facet2, True, &waserror); + if (waserror) + qh_errexit(qh_ERRqhull, NULL, NULL); + } +} /* tracemerge */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="tracemerging">-</a> + + qh_tracemerging() + print trace message during POSTmerging + + returns: + updates qh.mergereport + + notes: + called from qh_mergecycle() and qh_mergefacet() + + see: + qh_buildtracing() +*/ +void qh_tracemerging(void) { + realT cpu; + int total; + time_t timedata; + struct tm *tp; + + qh mergereport= zzval_(Ztotmerge); + time(&timedata); + tp= localtime(&timedata); + cpu= qh_CPUclock; + cpu /= qh_SECticks; + total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot); + qh_fprintf(qh ferr, 8087, "\n\ +At %d:%d:%d & %2.5g CPU secs, qhull has merged %d facets. The hull\n\ + contains %d facets and %d vertices.\n", + tp->tm_hour, tp->tm_min, tp->tm_sec, cpu, + total, qh num_facets - qh num_visible, + qh num_vertices-qh_setsize(qh del_vertices)); +} /* tracemerging */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="updatetested">-</a> + + qh_updatetested( facet1, facet2 ) + clear facet2->tested and facet1->ridge->tested for merge + + returns: + deletes facet2->center unless it's already large + if so, clears facet2->ridge->tested + + design: + clear facet2->tested + clear ridge->tested for facet1's ridges + if facet2 has a centrum + if facet2 is large + set facet2->keepcentrum + else if facet2 has 3 vertices due to many merges, or not large and post merging + clear facet2->keepcentrum + unless facet2->keepcentrum + clear facet2->center to recompute centrum later + clear ridge->tested for facet2's ridges +*/ +void qh_updatetested(facetT *facet1, facetT *facet2) { + ridgeT *ridge, **ridgep; + int size; + + facet2->tested= False; + FOREACHridge_(facet1->ridges) + ridge->tested= False; + if (!facet2->center) + return; + size= qh_setsize(facet2->vertices); + if (!facet2->keepcentrum) { + if (size > qh hull_dim + qh_MAXnewcentrum) { + facet2->keepcentrum= True; + zinc_(Zwidevertices); + } + }else if (size <= qh hull_dim + qh_MAXnewcentrum) { + /* center and keepcentrum was set */ + if (size == qh hull_dim || qh POSTmerging) + facet2->keepcentrum= False; /* if many merges need to recompute centrum */ + } + if (!facet2->keepcentrum) { + qh_memfree(facet2->center, qh normal_size); + facet2->center= NULL; + FOREACHridge_(facet2->ridges) + ridge->tested= False; + } +} /* updatetested */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="vertexridges">-</a> + + qh_vertexridges( vertex ) + return temporary set of ridges adjacent to a vertex + vertex->neighbors defined + + ntoes: + uses qh.visit_id + does not include implicit ridges for simplicial facets + + design: + for each neighbor of vertex + add ridges that include the vertex to ridges +*/ +setT *qh_vertexridges(vertexT *vertex) { + facetT *neighbor, **neighborp; + setT *ridges= qh_settemp(qh TEMPsize); + int size; + + qh visit_id++; + FOREACHneighbor_(vertex) + neighbor->visitid= qh visit_id; + FOREACHneighbor_(vertex) { + if (*neighborp) /* no new ridges in last neighbor */ + qh_vertexridges_facet(vertex, neighbor, &ridges); + } + if (qh PRINTstatistics || qh IStracing) { + size= qh_setsize(ridges); + zinc_(Zvertexridge); + zadd_(Zvertexridgetot, size); + zmax_(Zvertexridgemax, size); + trace3((qh ferr, 3011, "qh_vertexridges: found %d ridges for v%d\n", + size, vertex->id)); + } + return ridges; +} /* vertexridges */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="vertexridges_facet">-</a> + + qh_vertexridges_facet( vertex, facet, ridges ) + add adjacent ridges for vertex in facet + neighbor->visitid==qh.visit_id if it hasn't been visited + + returns: + ridges updated + sets facet->visitid to qh.visit_id-1 + + design: + for each ridge of facet + if ridge of visited neighbor (i.e., unprocessed) + if vertex in ridge + append ridge to vertex + mark facet processed +*/ +void qh_vertexridges_facet(vertexT *vertex, facetT *facet, setT **ridges) { + ridgeT *ridge, **ridgep; + facetT *neighbor; + + FOREACHridge_(facet->ridges) { + neighbor= otherfacet_(ridge, facet); + if (neighbor->visitid == qh visit_id + && qh_setin(ridge->vertices, vertex)) + qh_setappend(ridges, ridge); + } + facet->visitid= qh visit_id-1; +} /* vertexridges_facet */ + +/*-<a href="qh-merge.htm#TOC" + >-------------------------------</a><a name="willdelete">-</a> + + qh_willdelete( facet, replace ) + moves facet to visible list + sets facet->f.replace to replace (may be NULL) + + returns: + bumps qh.num_visible +*/ +void qh_willdelete(facetT *facet, facetT *replace) { + + qh_removefacet(facet); + qh_prependfacet(facet, &qh visible_list); + qh num_visible++; + facet->visible= True; + facet->f.replace= replace; +} /* willdelete */ + +#else /* qh_NOmerge */ +void qh_premerge(vertexT *apex, realT maxcentrum, realT maxangle) { +} +void qh_postmerge(const char *reason, realT maxcentrum, realT maxangle, + boolT vneighbors) { +} +boolT qh_checkzero(boolT testall) { + } +#endif /* qh_NOmerge */ + diff --git a/src/qhull/src/libqhull/merge.h b/src/qhull/src/libqhull/merge.h new file mode 100644 index 000000000..7f5ec3fb6 --- /dev/null +++ b/src/qhull/src/libqhull/merge.h @@ -0,0 +1,178 @@ +/*<html><pre> -<a href="qh-merge.htm" + >-------------------------------</a><a name="TOP">-</a> + + merge.h + header file for merge.c + + see qh-merge.htm and merge.c + + Copyright (c) 1993-2015 C.B. Barber. + $Id: //main/2015/qhull/src/libqhull/merge.h#1 $$Change: 1981 $ + $DateTime: 2015/09/28 20:26:32 $$Author: bbarber $ +*/ + +#ifndef qhDEFmerge +#define qhDEFmerge 1 + +#include "libqhull.h" + + +/*============ -constants- ==============*/ + +/*-<a href="qh-merge.htm#TOC" + >--------------------------------</a><a name="qh_ANGLEredundant">-</a> + + qh_ANGLEredundant + indicates redundant merge in mergeT->angle +*/ +#define qh_ANGLEredundant 6.0 + +/*-<a href="qh-merge.htm#TOC" + >--------------------------------</a><a name="qh_ANGLEdegen">-</a> + + qh_ANGLEdegen + indicates degenerate facet in mergeT->angle +*/ +#define qh_ANGLEdegen 5.0 + +/*-<a href="qh-merge.htm#TOC" + >--------------------------------</a><a name="qh_ANGLEconcave">-</a> + + qh_ANGLEconcave + offset to indicate concave facets in mergeT->angle + + notes: + concave facets are assigned the range of [2,4] in mergeT->angle + roundoff error may make the angle less than 2 +*/ +#define qh_ANGLEconcave 1.5 + +/*-<a href="qh-merge.htm#TOC" + >--------------------------------</a><a name="MRG">-</a> + + MRG... (mergeType) + indicates the type of a merge (mergeT->type) +*/ +typedef enum { /* in sort order for facet_mergeset */ + MRGnone= 0, + MRGcoplanar, /* centrum coplanar */ + MRGanglecoplanar, /* angle coplanar */ + /* could detect half concave ridges */ + MRGconcave, /* concave ridge */ + MRGflip, /* flipped facet. facet1 == facet2 */ + MRGridge, /* duplicate ridge (qh_MERGEridge) */ + /* degen and redundant go onto degen_mergeset */ + MRGdegen, /* degenerate facet (!enough neighbors) facet1 == facet2 */ + MRGredundant, /* redundant facet (vertex subset) */ + /* merge_degenredundant assumes degen < redundant */ + MRGmirror, /* mirror facet from qh_triangulate */ + ENDmrg +} mergeType; + +/*-<a href="qh-merge.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.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; /* angle between normals of facet1 and facet2 */ + facetT *facet1; /* will merge facet1 into facet2 */ + facetT *facet2; + mergeType type; +}; + + +/*=========== -macros- =========================*/ + +/*-<a href="qh-merge.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 since qh.facet_mergeset may change + see <a href="qset.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHmerge_( merges ) FOREACHsetelement_(mergeT, merges, merge) + +/*============ prototypes in alphabetical order after pre/postmerge =======*/ + +void qh_premerge(vertexT *apex, realT maxcentrum, realT maxangle); +void qh_postmerge(const char *reason, realT maxcentrum, realT maxangle, + boolT vneighbors); +void qh_all_merges(boolT othermerge, boolT vneighbors); +void qh_appendmergeset(facetT *facet, facetT *neighbor, mergeType mergetype, realT *angle); +setT *qh_basevertices( facetT *samecycle); +void qh_checkconnect(void /* qh.new_facets */); +boolT qh_checkzero(boolT testall); +int qh_compareangle(const void *p1, const void *p2); +int qh_comparemerge(const void *p1, const void *p2); +int qh_comparevisit(const void *p1, const void *p2); +void qh_copynonconvex(ridgeT *atridge); +void qh_degen_redundant_facet(facetT *facet); +void qh_degen_redundant_neighbors(facetT *facet, facetT *delfacet); +vertexT *qh_find_newvertex(vertexT *oldvertex, setT *vertices, setT *ridges); +void qh_findbest_test(boolT testcentrum, facetT *facet, facetT *neighbor, + facetT **bestfacet, realT *distp, realT *mindistp, realT *maxdistp); +facetT *qh_findbestneighbor(facetT *facet, realT *distp, realT *mindistp, realT *maxdistp); +void qh_flippedmerges(facetT *facetlist, boolT *wasmerge); +void qh_forcedmerges( boolT *wasmerge); +void qh_getmergeset(facetT *facetlist); +void qh_getmergeset_initial(facetT *facetlist); +void qh_hashridge(setT *hashtable, int hashsize, ridgeT *ridge, vertexT *oldvertex); +ridgeT *qh_hashridge_find(setT *hashtable, int hashsize, ridgeT *ridge, + vertexT *vertex, vertexT *oldvertex, int *hashslot); +void qh_makeridges(facetT *facet); +void qh_mark_dupridges(facetT *facetlist); +void qh_maydropneighbor(facetT *facet); +int qh_merge_degenredundant(void); +void qh_merge_nonconvex( facetT *facet1, facetT *facet2, mergeType mergetype); +void qh_mergecycle(facetT *samecycle, facetT *newfacet); +void qh_mergecycle_all(facetT *facetlist, boolT *wasmerge); +void qh_mergecycle_facets( facetT *samecycle, facetT *newfacet); +void qh_mergecycle_neighbors(facetT *samecycle, facetT *newfacet); +void qh_mergecycle_ridges(facetT *samecycle, facetT *newfacet); +void qh_mergecycle_vneighbors( facetT *samecycle, facetT *newfacet); +void qh_mergefacet(facetT *facet1, facetT *facet2, realT *mindist, realT *maxdist, boolT mergeapex); +void qh_mergefacet2d(facetT *facet1, facetT *facet2); +void qh_mergeneighbors(facetT *facet1, facetT *facet2); +void qh_mergeridges(facetT *facet1, facetT *facet2); +void qh_mergesimplex(facetT *facet1, facetT *facet2, boolT mergeapex); +void qh_mergevertex_del(vertexT *vertex, facetT *facet1, facetT *facet2); +void qh_mergevertex_neighbors(facetT *facet1, facetT *facet2); +void qh_mergevertices(setT *vertices1, setT **vertices); +setT *qh_neighbor_intersections(vertexT *vertex); +void qh_newvertices(setT *vertices); +boolT qh_reducevertices(void); +vertexT *qh_redundant_vertex(vertexT *vertex); +boolT qh_remove_extravertices(facetT *facet); +vertexT *qh_rename_sharedvertex(vertexT *vertex, facetT *facet); +void qh_renameridgevertex(ridgeT *ridge, vertexT *oldvertex, vertexT *newvertex); +void qh_renamevertex(vertexT *oldvertex, vertexT *newvertex, setT *ridges, + facetT *oldfacet, facetT *neighborA); +boolT qh_test_appendmerge(facetT *facet, facetT *neighbor); +boolT qh_test_vneighbors(void /* qh.newfacet_list */); +void qh_tracemerge(facetT *facet1, facetT *facet2); +void qh_tracemerging(void); +void qh_updatetested( facetT *facet1, facetT *facet2); +setT *qh_vertexridges(vertexT *vertex); +void qh_vertexridges_facet(vertexT *vertex, facetT *facet, setT **ridges); +void qh_willdelete(facetT *facet, facetT *replace); + +#endif /* qhDEFmerge */ diff --git a/src/qhull/src/libqhull/poly.c b/src/qhull/src/libqhull/poly.c new file mode 100644 index 000000000..b8db6a9ef --- /dev/null +++ b/src/qhull/src/libqhull/poly.c @@ -0,0 +1,1205 @@ +/*<html><pre> -<a href="qh-poly.htm" + >-------------------------------</a><a name="TOP">-</a> + + poly.c + implements polygons and simplices + + see qh-poly.htm, poly.h and libqhull.h + + infrequent code is in poly2.c + (all but top 50 and their callers 12/3/95) + + Copyright (c) 1993-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull/poly.c#3 $$Change: 2064 $ + $DateTime: 2016/01/18 12:36:08 $$Author: bbarber $ +*/ + +#include "qhull_a.h" + +/*======== functions in alphabetical order ==========*/ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="appendfacet">-</a> + + qh_appendfacet( facet ) + appends facet to end of qh.facet_list, + + returns: + updates qh.newfacet_list, facet_next, facet_list + increments qh.numfacets + + notes: + assumes qh.facet_list/facet_tail is defined (createsimplex) + + see: + qh_removefacet() + +*/ +void qh_appendfacet(facetT *facet) { + facetT *tail= qh facet_tail; + + if (tail == qh newfacet_list) + qh newfacet_list= facet; + if (tail == qh facet_next) + qh facet_next= facet; + facet->previous= tail->previous; + facet->next= tail; + if (tail->previous) + tail->previous->next= facet; + else + qh facet_list= facet; + tail->previous= facet; + qh num_facets++; + trace4((qh ferr, 4044, "qh_appendfacet: append f%d to facet_list\n", facet->id)); +} /* appendfacet */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="appendvertex">-</a> + + qh_appendvertex( vertex ) + appends vertex to end of qh.vertex_list, + + returns: + sets vertex->newlist + updates qh.vertex_list, newvertex_list + increments qh.num_vertices + + notes: + assumes qh.vertex_list/vertex_tail is defined (createsimplex) + +*/ +void qh_appendvertex(vertexT *vertex) { + vertexT *tail= qh vertex_tail; + + if (tail == qh newvertex_list) + qh newvertex_list= vertex; + vertex->newlist= True; + vertex->previous= tail->previous; + vertex->next= tail; + if (tail->previous) + tail->previous->next= vertex; + else + qh vertex_list= vertex; + tail->previous= vertex; + qh num_vertices++; + trace4((qh ferr, 4045, "qh_appendvertex: append v%d to vertex_list\n", vertex->id)); +} /* appendvertex */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="attachnewfacets">-</a> + + qh_attachnewfacets( ) + attach horizon facets to new facets in qh.newfacet_list + newfacets have neighbor and ridge links to horizon but not vice versa + only needed for qh.ONLYgood + + returns: + set qh.NEWfacets + horizon facets linked to new facets + ridges changed from visible facets to new facets + simplicial ridges deleted + qh.visible_list, no ridges valid + facet->f.replace is a newfacet (if any) + + design: + delete interior ridges and neighbor sets by + for each visible, non-simplicial facet + for each ridge + if last visit or if neighbor is simplicial + if horizon neighbor + delete ridge for horizon's ridge set + delete ridge + erase neighbor set + attach horizon facets and new facets by + for all new facets + if corresponding horizon facet is simplicial + locate corresponding visible facet {may be more than one} + link visible facet to new facet + replace visible facet with new facet in horizon + else it's non-simplicial + for all visible neighbors of the horizon facet + link visible neighbor to new facet + delete visible neighbor from horizon facet + append new facet to horizon's neighbors + the first ridge of the new facet is the horizon ridge + link the new facet into the horizon ridge +*/ +void qh_attachnewfacets(void /* qh.visible_list, newfacet_list */) { + facetT *newfacet= NULL, *neighbor, **neighborp, *horizon, *visible; + ridgeT *ridge, **ridgep; + + qh NEWfacets= True; + trace3((qh ferr, 3012, "qh_attachnewfacets: delete interior ridges\n")); + qh visit_id++; + FORALLvisible_facets { + visible->visitid= qh visit_id; + if (visible->ridges) { + FOREACHridge_(visible->ridges) { + neighbor= otherfacet_(ridge, visible); + if (neighbor->visitid == qh visit_id + || (!neighbor->visible && neighbor->simplicial)) { + if (!neighbor->visible) /* delete ridge for simplicial horizon */ + qh_setdel(neighbor->ridges, ridge); + qh_setfree(&(ridge->vertices)); /* delete on 2nd visit */ + qh_memfree(ridge, (int)sizeof(ridgeT)); + } + } + SETfirst_(visible->ridges)= NULL; + } + SETfirst_(visible->neighbors)= NULL; + } + trace1((qh ferr, 1017, "qh_attachnewfacets: attach horizon facets to new facets\n")); + FORALLnew_facets { + horizon= SETfirstt_(newfacet->neighbors, facetT); + if (horizon->simplicial) { + visible= NULL; + FOREACHneighbor_(horizon) { /* may have more than one horizon ridge */ + if (neighbor->visible) { + if (visible) { + if (qh_setequal_skip(newfacet->vertices, 0, horizon->vertices, + SETindex_(horizon->neighbors, neighbor))) { + visible= neighbor; + break; + } + }else + visible= neighbor; + } + } + if (visible) { + visible->f.replace= newfacet; + qh_setreplace(horizon->neighbors, visible, newfacet); + }else { + qh_fprintf(qh ferr, 6102, "qhull internal error (qh_attachnewfacets): couldn't find visible facet for horizon f%d of newfacet f%d\n", + horizon->id, newfacet->id); + qh_errexit2(qh_ERRqhull, horizon, newfacet); + } + }else { /* non-simplicial, with a ridge for newfacet */ + FOREACHneighbor_(horizon) { /* may hold for many new facets */ + if (neighbor->visible) { + neighbor->f.replace= newfacet; + qh_setdelnth(horizon->neighbors, + SETindex_(horizon->neighbors, neighbor)); + neighborp--; /* repeat */ + } + } + qh_setappend(&horizon->neighbors, newfacet); + ridge= SETfirstt_(newfacet->ridges, ridgeT); + if (ridge->top == horizon) + ridge->bottom= newfacet; + else + ridge->top= newfacet; + } + } /* newfacets */ + if (qh PRINTstatistics) { + FORALLvisible_facets { + if (!visible->f.replace) + zinc_(Zinsidevisible); + } + } +} /* attachnewfacets */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="checkflipped">-</a> + + qh_checkflipped( facet, dist, allerror ) + checks facet orientation to interior point + + if allerror set, + tests against qh.DISTround + else + tests against 0 since tested against DISTround before + + returns: + False if it flipped orientation (sets facet->flipped) + distance if non-NULL +*/ +boolT qh_checkflipped(facetT *facet, realT *distp, boolT allerror) { + realT dist; + + if (facet->flipped && !distp) + return False; + zzinc_(Zdistcheck); + qh_distplane(qh interior_point, facet, &dist); + if (distp) + *distp= dist; + if ((allerror && dist > -qh DISTround)|| (!allerror && dist >= 0.0)) { + facet->flipped= True; + zzinc_(Zflippedfacets); + trace0((qh ferr, 19, "qh_checkflipped: facet f%d is flipped, distance= %6.12g during p%d\n", + facet->id, dist, qh furthest_id)); + qh_precision("flipped facet"); + return False; + } + return True; +} /* checkflipped */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="delfacet">-</a> + + qh_delfacet( facet ) + removes facet from facet_list and frees up its memory + + notes: + assumes vertices and ridges already freed +*/ +void qh_delfacet(facetT *facet) { + void **freelistp; /* used if !qh_NOmem by qh_memfree_() */ + + trace4((qh ferr, 4046, "qh_delfacet: delete f%d\n", facet->id)); + if (facet == qh tracefacet) + qh tracefacet= NULL; + if (facet == qh GOODclosest) + qh GOODclosest= NULL; + qh_removefacet(facet); + if (!facet->tricoplanar || facet->keepcentrum) { + qh_memfree_(facet->normal, qh normal_size, freelistp); + if (qh CENTERtype == qh_ASvoronoi) { /* braces for macro calls */ + qh_memfree_(facet->center, qh center_size, freelistp); + }else /* AScentrum */ { + qh_memfree_(facet->center, qh normal_size, freelistp); + } + } + qh_setfree(&(facet->neighbors)); + if (facet->ridges) + qh_setfree(&(facet->ridges)); + qh_setfree(&(facet->vertices)); + if (facet->outsideset) + qh_setfree(&(facet->outsideset)); + if (facet->coplanarset) + qh_setfree(&(facet->coplanarset)); + qh_memfree_(facet, (int)sizeof(facetT), freelistp); +} /* delfacet */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="deletevisible">-</a> + + qh_deletevisible() + delete visible facets and vertices + + returns: + deletes each facet and removes from facetlist + at exit, qh.visible_list empty (== qh.newfacet_list) + + notes: + ridges already deleted + horizon facets do not reference facets on qh.visible_list + new facets in qh.newfacet_list + uses qh.visit_id; +*/ +void qh_deletevisible(void /*qh.visible_list*/) { + facetT *visible, *nextfacet; + vertexT *vertex, **vertexp; + int numvisible= 0, numdel= qh_setsize(qh del_vertices); + + trace1((qh ferr, 1018, "qh_deletevisible: delete %d visible facets and %d vertices\n", + qh num_visible, numdel)); + for (visible= qh visible_list; visible && visible->visible; + visible= nextfacet) { /* deleting current */ + nextfacet= visible->next; + numvisible++; + qh_delfacet(visible); + } + if (numvisible != qh num_visible) { + qh_fprintf(qh ferr, 6103, "qhull internal error (qh_deletevisible): qh num_visible %d is not number of visible facets %d\n", + qh num_visible, numvisible); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + qh num_visible= 0; + zadd_(Zvisfacettot, numvisible); + zmax_(Zvisfacetmax, numvisible); + zzadd_(Zdelvertextot, numdel); + zmax_(Zdelvertexmax, numdel); + FOREACHvertex_(qh del_vertices) + qh_delvertex(vertex); + qh_settruncate(qh del_vertices, 0); +} /* deletevisible */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="facetintersect">-</a> + + qh_facetintersect( facetA, facetB, skipa, skipB, prepend ) + return vertices for intersection of two simplicial facets + may include 1 prepended entry (if more, need to settemppush) + + returns: + returns set of qh.hull_dim-1 + prepend vertices + returns skipped index for each test and checks for exactly one + + notes: + does not need settemp since set in quick memory + + see also: + qh_vertexintersect and qh_vertexintersect_new + use qh_setnew_delnthsorted to get nth ridge (no skip information) + + design: + locate skipped vertex by scanning facet A's neighbors + locate skipped vertex by scanning facet B's neighbors + intersect the vertex sets +*/ +setT *qh_facetintersect(facetT *facetA, facetT *facetB, + int *skipA,int *skipB, int prepend) { + setT *intersect; + int dim= qh hull_dim, i, j; + facetT **neighborsA, **neighborsB; + + neighborsA= SETaddr_(facetA->neighbors, facetT); + neighborsB= SETaddr_(facetB->neighbors, facetT); + i= j= 0; + if (facetB == *neighborsA++) + *skipA= 0; + else if (facetB == *neighborsA++) + *skipA= 1; + else if (facetB == *neighborsA++) + *skipA= 2; + else { + for (i=3; i < dim; i++) { + if (facetB == *neighborsA++) { + *skipA= i; + break; + } + } + } + if (facetA == *neighborsB++) + *skipB= 0; + else if (facetA == *neighborsB++) + *skipB= 1; + else if (facetA == *neighborsB++) + *skipB= 2; + else { + for (j=3; j < dim; j++) { + if (facetA == *neighborsB++) { + *skipB= j; + break; + } + } + } + if (i >= dim || j >= dim) { + qh_fprintf(qh ferr, 6104, "qhull internal error (qh_facetintersect): f%d or f%d not in others neighbors\n", + facetA->id, facetB->id); + qh_errexit2(qh_ERRqhull, facetA, facetB); + } + intersect= qh_setnew_delnthsorted(facetA->vertices, qh hull_dim, *skipA, prepend); + trace4((qh ferr, 4047, "qh_facetintersect: f%d skip %d matches f%d skip %d\n", + facetA->id, *skipA, facetB->id, *skipB)); + return(intersect); +} /* facetintersect */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="gethash">-</a> + + qh_gethash( hashsize, set, size, firstindex, skipelem ) + return hashvalue for a set with firstindex and skipelem + + notes: + returned hash is in [0,hashsize) + assumes at least firstindex+1 elements + assumes skipelem is NULL, in set, or part of hash + + hashes memory addresses which may change over different runs of the same data + using sum for hash does badly in high d +*/ +int qh_gethash(int hashsize, setT *set, int size, int firstindex, void *skipelem) { + void **elemp= SETelemaddr_(set, firstindex, void); + ptr_intT hash = 0, elem; + unsigned result; + int i; +#ifdef _MSC_VER /* Microsoft Visual C++ -- warn about 64-bit issues */ +#pragma warning( push) /* WARN64 -- ptr_intT holds a 64-bit pointer */ +#pragma warning( disable : 4311) /* 'type cast': pointer truncation from 'void*' to 'ptr_intT' */ +#endif + + switch (size-firstindex) { + case 1: + hash= (ptr_intT)(*elemp) - (ptr_intT) skipelem; + break; + case 2: + hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] - (ptr_intT) skipelem; + break; + case 3: + hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] + (ptr_intT)elemp[2] + - (ptr_intT) skipelem; + break; + case 4: + hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] + (ptr_intT)elemp[2] + + (ptr_intT)elemp[3] - (ptr_intT) skipelem; + break; + case 5: + hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] + (ptr_intT)elemp[2] + + (ptr_intT)elemp[3] + (ptr_intT)elemp[4] - (ptr_intT) skipelem; + break; + case 6: + hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] + (ptr_intT)elemp[2] + + (ptr_intT)elemp[3] + (ptr_intT)elemp[4]+ (ptr_intT)elemp[5] + - (ptr_intT) skipelem; + break; + default: + hash= 0; + i= 3; + do { /* this is about 10% in 10-d */ + if ((elem= (ptr_intT)*elemp++) != (ptr_intT)skipelem) { + hash ^= (elem << i) + (elem >> (32-i)); + i += 3; + if (i >= 32) + i -= 32; + } + }while (*elemp); + break; + } + if (hashsize<0) { + qh_fprintf(qh ferr, 6202, "qhull internal error: negative hashsize %d passed to qh_gethash [poly.c]\n", hashsize); + qh_errexit2(qh_ERRqhull, NULL, NULL); + } + result= (unsigned)hash; + result %= (unsigned)hashsize; + /* result= 0; for debugging */ + return result; +#ifdef _MSC_VER +#pragma warning( pop) +#endif +} /* gethash */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="makenewfacet">-</a> + + qh_makenewfacet( vertices, toporient, horizon ) + creates a toporient? facet from vertices + + returns: + returns newfacet + adds newfacet to qh.facet_list + newfacet->vertices= vertices + if horizon + newfacet->neighbor= horizon, but not vice versa + newvertex_list updated with vertices +*/ +facetT *qh_makenewfacet(setT *vertices, boolT toporient,facetT *horizon) { + facetT *newfacet; + vertexT *vertex, **vertexp; + + FOREACHvertex_(vertices) { + if (!vertex->newlist) { + qh_removevertex(vertex); + qh_appendvertex(vertex); + } + } + newfacet= qh_newfacet(); + newfacet->vertices= vertices; + newfacet->toporient= (unsigned char)toporient; + if (horizon) + qh_setappend(&(newfacet->neighbors), horizon); + qh_appendfacet(newfacet); + return(newfacet); +} /* makenewfacet */ + + +/*-<a href="qh-poly.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(void /* newfacet_list */) { + facetT *newfacet; + + FORALLnew_facets { + if (!newfacet->mergehorizon) + qh_setfacetplane(newfacet); + } + if (qh JOGGLEmax < REALmax/2) + minimize_(qh min_vertex, -wwval_(Wnewvertexmax)); +} /* makenewplanes */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="makenew_nonsimplicial">-</a> + + qh_makenew_nonsimplicial( visible, apex, numnew ) + make new facets for ridges of a visible facet + + returns: + first newfacet, bumps numnew as needed + attaches new facets if !qh.ONLYgood + marks ridge neighbors for simplicial visible + if (qh.ONLYgood) + ridges on newfacet, horizon, and visible + else + ridge and neighbors between newfacet and horizon + visible facet's ridges are deleted + + notes: + qh.visit_id if visible has already been processed + sets neighbor->seen for building f.samecycle + assumes all 'seen' flags initially false + + design: + for each ridge of visible facet + get neighbor of visible facet + if neighbor was already processed + delete the ridge (will delete all visible facets later) + if neighbor is a horizon facet + create a new facet + if neighbor coplanar + adds newfacet to f.samecycle for later merging + else + updates neighbor's neighbor set + (checks for non-simplicial facet with multiple ridges to visible facet) + updates neighbor's ridge set + (checks for simplicial neighbor to non-simplicial visible facet) + (deletes ridge if neighbor is simplicial) + +*/ +#ifndef qh_NOmerge +facetT *qh_makenew_nonsimplicial(facetT *visible, vertexT *apex, int *numnew) { + void **freelistp; /* used if !qh_NOmem by qh_memfree_() */ + ridgeT *ridge, **ridgep; + facetT *neighbor, *newfacet= NULL, *samecycle; + setT *vertices; + boolT toporient; + int ridgeid; + + FOREACHridge_(visible->ridges) { + ridgeid= ridge->id; + neighbor= otherfacet_(ridge, visible); + if (neighbor->visible) { + if (!qh ONLYgood) { + if (neighbor->visitid == qh visit_id) { + qh_setfree(&(ridge->vertices)); /* delete on 2nd visit */ + qh_memfree_(ridge, (int)sizeof(ridgeT), freelistp); + } + } + }else { /* neighbor is an horizon facet */ + toporient= (ridge->top == visible); + vertices= qh_setnew(qh hull_dim); /* makes sure this is quick */ + qh_setappend(&vertices, apex); + qh_setappend_set(&vertices, ridge->vertices); + newfacet= qh_makenewfacet(vertices, toporient, neighbor); + (*numnew)++; + if (neighbor->coplanar) { + newfacet->mergehorizon= True; + if (!neighbor->seen) { + newfacet->f.samecycle= newfacet; + neighbor->f.newcycle= newfacet; + }else { + samecycle= neighbor->f.newcycle; + newfacet->f.samecycle= samecycle->f.samecycle; + samecycle->f.samecycle= newfacet; + } + } + if (qh ONLYgood) { + if (!neighbor->simplicial) + qh_setappend(&(newfacet->ridges), ridge); + }else { /* qh_attachnewfacets */ + if (neighbor->seen) { + if (neighbor->simplicial) { + qh_fprintf(qh ferr, 6105, "qhull internal error (qh_makenew_nonsimplicial): simplicial f%d sharing two ridges with f%d\n", + neighbor->id, visible->id); + qh_errexit2(qh_ERRqhull, neighbor, visible); + } + qh_setappend(&(neighbor->neighbors), newfacet); + }else + qh_setreplace(neighbor->neighbors, visible, newfacet); + if (neighbor->simplicial) { + qh_setdel(neighbor->ridges, ridge); + qh_setfree(&(ridge->vertices)); + qh_memfree(ridge, (int)sizeof(ridgeT)); + }else { + qh_setappend(&(newfacet->ridges), ridge); + if (toporient) + ridge->top= newfacet; + else + ridge->bottom= newfacet; + } + trace4((qh ferr, 4048, "qh_makenew_nonsimplicial: created facet f%d from v%d and r%d of horizon f%d\n", + newfacet->id, apex->id, ridgeid, neighbor->id)); + } + } + neighbor->seen= True; + } /* for each ridge */ + if (!qh ONLYgood) + SETfirst_(visible->ridges)= NULL; + return newfacet; +} /* makenew_nonsimplicial */ +#else /* qh_NOmerge */ +facetT *qh_makenew_nonsimplicial(facetT *visible, vertexT *apex, int *numnew) { + return NULL; +} +#endif /* qh_NOmerge */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="makenew_simplicial">-</a> + + qh_makenew_simplicial( visible, apex, numnew ) + make new facets for simplicial visible facet and apex + + returns: + attaches new facets if (!qh.ONLYgood) + neighbors between newfacet and horizon + + notes: + nop if neighbor->seen or neighbor->visible(see qh_makenew_nonsimplicial) + + design: + locate neighboring horizon facet for visible facet + determine vertices and orientation + create new facet + if coplanar, + add new facet to f.samecycle + update horizon facet's neighbor list +*/ +facetT *qh_makenew_simplicial(facetT *visible, vertexT *apex, int *numnew) { + facetT *neighbor, **neighborp, *newfacet= NULL; + setT *vertices; + boolT flip, toporient; + int horizonskip= 0, visibleskip= 0; + + FOREACHneighbor_(visible) { + if (!neighbor->seen && !neighbor->visible) { + vertices= qh_facetintersect(neighbor,visible, &horizonskip, &visibleskip, 1); + SETfirst_(vertices)= apex; + flip= ((horizonskip & 0x1) ^ (visibleskip & 0x1)); + if (neighbor->toporient) + toporient= horizonskip & 0x1; + else + toporient= (horizonskip & 0x1) ^ 0x1; + newfacet= qh_makenewfacet(vertices, toporient, neighbor); + (*numnew)++; + if (neighbor->coplanar && (qh PREmerge || qh MERGEexact)) { +#ifndef qh_NOmerge + newfacet->f.samecycle= newfacet; + newfacet->mergehorizon= True; +#endif + } + if (!qh ONLYgood) + SETelem_(neighbor->neighbors, horizonskip)= newfacet; + trace4((qh ferr, 4049, "qh_makenew_simplicial: create facet f%d top %d from v%d and horizon f%d skip %d top %d and visible f%d skip %d, flip? %d\n", + newfacet->id, toporient, apex->id, neighbor->id, horizonskip, + neighbor->toporient, visible->id, visibleskip, flip)); + } + } + return newfacet; +} /* makenew_simplicial */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="matchneighbor">-</a> + + qh_matchneighbor( newfacet, newskip, hashsize, hashcount ) + either match subridge of newfacet with neighbor or add to hash_table + + returns: + duplicate ridges are unmatched and marked by qh_DUPLICATEridge + + notes: + ridge is newfacet->vertices w/o newskip vertex + do not allocate memory (need to free hash_table cleanly) + uses linear hash chains + + see also: + qh_matchduplicates + + design: + for each possible matching facet in qh.hash_table + if vertices match + set ismatch, if facets have opposite orientation + if ismatch and matching facet doesn't have a match + match the facets by updating their neighbor sets + else + indicate a duplicate ridge + set facet hyperplane for later testing + add facet to hashtable + unless the other facet was already a duplicate ridge + mark both facets with a duplicate ridge + add other facet (if defined) to hash table +*/ +void qh_matchneighbor(facetT *newfacet, int newskip, int hashsize, int *hashcount) { + boolT newfound= False; /* True, if new facet is already in hash chain */ + boolT same, ismatch; + int hash, scan; + facetT *facet, *matchfacet; + int skip, matchskip; + + hash= qh_gethash(hashsize, newfacet->vertices, qh hull_dim, 1, + SETelem_(newfacet->vertices, newskip)); + trace4((qh ferr, 4050, "qh_matchneighbor: newfacet f%d skip %d hash %d hashcount %d\n", + newfacet->id, newskip, hash, *hashcount)); + zinc_(Zhashlookup); + for (scan= hash; (facet= SETelemt_(qh hash_table, scan, facetT)); + scan= (++scan >= hashsize ? 0 : scan)) { + if (facet == newfacet) { + newfound= True; + continue; + } + zinc_(Zhashtests); + if (qh_matchvertices(1, newfacet->vertices, newskip, facet->vertices, &skip, &same)) { + if (SETelem_(newfacet->vertices, newskip) == + SETelem_(facet->vertices, skip)) { + qh_precision("two facets with the same vertices"); + qh_fprintf(qh ferr, 6106, "qhull precision error: Vertex sets are the same for f%d and f%d. Can not force output.\n", + facet->id, newfacet->id); + qh_errexit2(qh_ERRprec, facet, newfacet); + } + ismatch= (same == (boolT)((newfacet->toporient ^ facet->toporient))); + matchfacet= SETelemt_(facet->neighbors, skip, facetT); + if (ismatch && !matchfacet) { + SETelem_(facet->neighbors, skip)= newfacet; + SETelem_(newfacet->neighbors, newskip)= facet; + (*hashcount)--; + trace4((qh ferr, 4051, "qh_matchneighbor: f%d skip %d matched with new f%d skip %d\n", + facet->id, skip, newfacet->id, newskip)); + return; + } + if (!qh PREmerge && !qh MERGEexact) { + qh_precision("a ridge with more than two neighbors"); + qh_fprintf(qh ferr, 6107, "qhull precision error: facets f%d, f%d and f%d meet at a ridge with more than 2 neighbors. Can not continue.\n", + facet->id, newfacet->id, getid_(matchfacet)); + qh_errexit2(qh_ERRprec, facet, newfacet); + } + SETelem_(newfacet->neighbors, newskip)= qh_DUPLICATEridge; + newfacet->dupridge= True; + if (!newfacet->normal) + qh_setfacetplane(newfacet); + qh_addhash(newfacet, qh hash_table, hashsize, hash); + (*hashcount)++; + if (!facet->normal) + qh_setfacetplane(facet); + if (matchfacet != qh_DUPLICATEridge) { + SETelem_(facet->neighbors, skip)= qh_DUPLICATEridge; + facet->dupridge= True; + if (!facet->normal) + qh_setfacetplane(facet); + if (matchfacet) { + matchskip= qh_setindex(matchfacet->neighbors, facet); + if (matchskip<0) { + qh_fprintf(qh ferr, 6260, "qhull internal 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_ERRqhull, matchfacet, facet); + } + SETelem_(matchfacet->neighbors, matchskip)= qh_DUPLICATEridge; /* matchskip>=0 by QH6260 */ + matchfacet->dupridge= True; + if (!matchfacet->normal) + qh_setfacetplane(matchfacet); + qh_addhash(matchfacet, qh hash_table, hashsize, hash); + *hashcount += 2; + } + } + trace4((qh ferr, 4052, "qh_matchneighbor: new f%d skip %d duplicates ridge for f%d skip %d matching f%d ismatch %d at hash %d\n", + newfacet->id, newskip, facet->id, skip, + (matchfacet == qh_DUPLICATEridge ? -2 : getid_(matchfacet)), + ismatch, hash)); + return; /* end of duplicate ridge */ + } + } + if (!newfound) + SETelem_(qh hash_table, scan)= newfacet; /* same as qh_addhash */ + (*hashcount)++; + trace4((qh ferr, 4053, "qh_matchneighbor: no match for f%d skip %d at hash %d\n", + newfacet->id, newskip, hash)); +} /* matchneighbor */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="matchnewfacets">-</a> + + qh_matchnewfacets() + match newfacets in qh.newfacet_list to their newfacet neighbors + + returns: + qh.newfacet_list with full neighbor sets + get vertices with nth neighbor by deleting nth vertex + if qh.PREmerge/MERGEexact or qh.FORCEoutput + sets facet->flippped if flipped normal (also prevents point partitioning) + if duplicate ridges and qh.PREmerge/MERGEexact + sets facet->dupridge + missing neighbor links identifies extra ridges to be merging (qh_MERGEridge) + + notes: + newfacets already have neighbor[0] (horizon facet) + assumes qh.hash_table is NULL + vertex->neighbors has not been updated yet + do not allocate memory after qh.hash_table (need to free it cleanly) + + design: + delete neighbor sets for all new facets + initialize a hash table + for all new facets + match facet with neighbors + if unmatched facets (due to duplicate ridges) + for each new facet with a duplicate ridge + match it with a facet + check for flipped facets +*/ +void qh_matchnewfacets(void /* qh.newfacet_list */) { + int numnew=0, hashcount=0, newskip; + facetT *newfacet, *neighbor; + int dim= qh hull_dim, hashsize, neighbor_i, neighbor_n; + setT *neighbors; +#ifndef qh_NOtrace + int facet_i, facet_n, numfree= 0; + facetT *facet; +#endif + + trace1((qh ferr, 1019, "qh_matchnewfacets: match neighbors for new facets.\n")); + FORALLnew_facets { + numnew++; + { /* inline qh_setzero(newfacet->neighbors, 1, qh hull_dim); */ + neighbors= newfacet->neighbors; + neighbors->e[neighbors->maxsize].i= dim+1; /*may be overwritten*/ + memset((char *)SETelemaddr_(neighbors, 1, void), 0, dim * SETelemsize); + } + } + + qh_newhashtable(numnew*(qh hull_dim-1)); /* twice what is normally needed, + but every ridge could be DUPLICATEridge */ + hashsize= qh_setsize(qh hash_table); + FORALLnew_facets { + for (newskip=1; newskip<qh hull_dim; newskip++) /* furthest/horizon already matched */ + /* hashsize>0 because hull_dim>1 and numnew>0 */ + qh_matchneighbor(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 ferr, 8088, "qh_matchnewfacets: after adding facet %d, hashcount %d != count %d\n", + newfacet->id, hashcount, count); + qh_errexit(qh_ERRqhull, newfacet, NULL); + } + } +#endif /* end of trap code */ + } + if (hashcount) { + FORALLnew_facets { + if (newfacet->dupridge) { + FOREACHneighbor_i_(newfacet) { + if (neighbor == qh_DUPLICATEridge) { + qh_matchduplicates(newfacet, neighbor_i, hashsize, &hashcount); + /* this may report MERGEfacet */ + } + } + } + } + } + if (hashcount) { + qh_fprintf(qh ferr, 6108, "qhull internal error (qh_matchnewfacets): %d neighbors did not match up\n", + hashcount); + qh_printhashtable(qh ferr); + qh_errexit(qh_ERRqhull, NULL, NULL); + } +#ifndef qh_NOtrace + if (qh IStracing >= 2) { + FOREACHfacet_i_(qh hash_table) { + if (!facet) + numfree++; + } + qh_fprintf(qh ferr, 8089, "qh_matchnewfacets: %d new facets, %d unused hash entries . hashsize %d\n", + numnew, numfree, qh_setsize(qh hash_table)); + } +#endif /* !qh_NOtrace */ + qh_setfree(&qh hash_table); + if (qh PREmerge || qh MERGEexact) { + if (qh IStracing >= 4) + qh_printfacetlist(qh newfacet_list, NULL, qh_ALL); + FORALLnew_facets { + if (newfacet->normal) + qh_checkflipped(newfacet, NULL, qh_ALL); + } + }else if (qh FORCEoutput) + qh_checkflipped_all(qh newfacet_list); /* prints warnings for flipped */ +} /* matchnewfacets */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="matchvertices">-</a> + + qh_matchvertices( 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 each set + sets same iff vertices have the same orientation + + notes: + 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(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 */ + *same= !((skipA & 0x1) ^ (*skipB & 0x1)); /* result is 0 or 1 */ + trace4((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.htm#TOC" + >-------------------------------</a><a name="newfacet">-</a> + + qh_newfacet() + return a new facet + + returns: + all fields initialized or cleared (NULL) + preallocates neighbors set +*/ +facetT *qh_newfacet(void) { + facetT *facet; + void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */ + + qh_memalloc_((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 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; +#endif + facet->simplicial= True; + facet->good= True; + facet->newfacet= True; + trace4((qh ferr, 4055, "qh_newfacet: created facet f%d\n", facet->id)); + return(facet); +} /* newfacet */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="newridge">-</a> + + qh_newridge() + return a new ridge +*/ +ridgeT *qh_newridge(void) { + ridgeT *ridge; + void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */ + + qh_memalloc_((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 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 ferr, 4056, "qh_newridge: created ridge r%d\n", ridge->id)); + return(ridge); +} /* newridge */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="pointid">-</a> + + qh_pointid( 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(pointT *point) { + ptr_intT offset, id; + + if (!point) + 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.htm#TOC" + >-------------------------------</a><a name="removefacet">-</a> + + qh_removefacet( 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(facetT *facet) { + facetT *next= facet->next, *previous= facet->previous; + + 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 ferr, 4057, "qh_removefacet: remove f%d from facet_list\n", facet->id)); +} /* removefacet */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="removevertex">-</a> + + qh_removevertex( vertex ) + unlinks vertex from qh.vertex_list, + + returns: + updates qh.vertex_list .newvertex_list + decrements qh.num_vertices +*/ +void qh_removevertex(vertexT *vertex) { + vertexT *next= vertex->next, *previous= vertex->previous; + + 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= vertex->next; + qh vertex_list->previous= NULL; + } + qh num_vertices--; + trace4((qh ferr, 4058, "qh_removevertex: remove v%d from vertex_list\n", vertex->id)); +} /* removevertex */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="updatevertices">-</a> + + qh_updatevertices() + update vertex neighbors and delete interior vertices + + returns: + if qh.VERTEXneighbors, updates neighbors for each vertex + 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 + + design: + if qh.VERTEXneighbors + deletes references to visible facets from vertex neighbors + appends new facets to the neighbor list for each vertex + checks all vertices of visible facets + removes visible facets from neighbor lists + marks unused vertices for deletion +*/ +void qh_updatevertices(void /*qh.newvertex_list, newfacet_list, visible_list*/) { + facetT *newfacet= NULL, *neighbor, **neighborp, *visible; + vertexT *vertex, **vertexp; + + trace3((qh ferr, 3013, "qh_updatevertices: delete interior vertices and update vertex->neighbors\n")); + if (qh VERTEXneighbors) { + FORALLvertex_(qh newvertex_list) { + FOREACHneighbor_(vertex) { + if (neighbor->visible) + SETref_(neighbor)= NULL; + } + qh_setcompact(vertex->neighbors); + } + FORALLnew_facets { + FOREACHvertex_(newfacet->vertices) + qh_setappend(&vertex->neighbors, newfacet); + } + FORALLvisible_facets { + FOREACHvertex_(visible->vertices) { + if (!vertex->newlist && !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 del_vertices, vertex); + trace2((qh ferr, 2041, "qh_updatevertices: delete vertex p%d(v%d) in f%d\n", + qh_pointid(vertex->point), vertex->id, visible->id)); + } + } + } + } + }else { /* !VERTEXneighbors */ + FORALLvisible_facets { + FOREACHvertex_(visible->vertices) { + if (!vertex->newlist && !vertex->deleted) { + vertex->deleted= True; + qh_setappend(&qh del_vertices, vertex); + trace2((qh ferr, 2042, "qh_updatevertices: delete vertex p%d(v%d) in f%d\n", + qh_pointid(vertex->point), vertex->id, visible->id)); + } + } + } + } +} /* updatevertices */ + + + diff --git a/src/qhull/src/libqhull/poly.h b/src/qhull/src/libqhull/poly.h new file mode 100644 index 000000000..af8b42077 --- /dev/null +++ b/src/qhull/src/libqhull/poly.h @@ -0,0 +1,296 @@ +/*<html><pre> -<a href="qh-poly.htm" + >-------------------------------</a><a name="TOP">-</a> + + poly.h + header file for poly.c and poly2.c + + see qh-poly.htm, libqhull.h and poly.c + + Copyright (c) 1993-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull/poly.h#3 $$Change: 2047 $ + $DateTime: 2016/01/04 22:03:18 $$Author: bbarber $ +*/ + +#ifndef qhDEFpoly +#define qhDEFpoly 1 + +#include "libqhull.h" + +/*=============== constants ========================== */ + +/*-<a href="qh-geom.htm#TOC" + >--------------------------------</a><a name="ALGORITHMfault">-</a> + + ALGORITHMfault + use as argument to checkconvex() to report errors during buildhull +*/ +#define qh_ALGORITHMfault 0 + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="DATAfault">-</a> + + DATAfault + use as argument to checkconvex() to report errors during initialhull +*/ +#define qh_DATAfault 1 + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="DUPLICATEridge">-</a> + + DUPLICATEridge + special value for facet->neighbor to indicate a duplicate ridge + + notes: + set by matchneighbor, used by matchmatch and mark_dupridge +*/ +#define qh_DUPLICATEridge (facetT *)1L + +/*-<a href="qh-poly.htm#TOC" + >--------------------------------</a><a name="MERGEridge">-</a> + + MERGEridge flag in facet + special value for facet->neighbor to indicate a merged ridge + + notes: + set by matchneighbor, used by matchmatch and mark_dupridge +*/ +#define qh_MERGEridge (facetT *)2L + + +/*============ -structures- ====================*/ + +/*=========== -macros- =========================*/ + +/*-<a href="qh-poly.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.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.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.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.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.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.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.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHneighborA_(facet) FOREACHsetelement_(facetT, facet->neighbors, neighborA) + +/*-<a href="qh-poly.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.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHvisible_(facets) FOREACHsetelement_(facetT, facets, visible) + +/*-<a href="qh-poly.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.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHnewfacet_(facets) FOREACHsetelement_(facetT, facets, newfacet) + +/*-<a href="qh-poly.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.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHvertexA_(vertices) FOREACHsetelement_(vertexT, vertices, vertexA) + +/*-<a href="qh-poly.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.h#FOREACHsetelement_">FOREACHsetelement_</a> +*/ +#define FOREACHvertexreverse12_(vertices) FOREACHsetelementreverse12_(vertexT, vertices, vertex) + + +/*=============== prototypes poly.c in alphabetical order ================*/ + +void qh_appendfacet(facetT *facet); +void qh_appendvertex(vertexT *vertex); +void qh_attachnewfacets(void /* qh.visible_list, qh.newfacet_list */); +boolT qh_checkflipped(facetT *facet, realT *dist, boolT allerror); +void qh_delfacet(facetT *facet); +void qh_deletevisible(void /*qh.visible_list, qh.horizon_list*/); +setT *qh_facetintersect(facetT *facetA, facetT *facetB, int *skipAp,int *skipBp, int extra); +int qh_gethash(int hashsize, setT *set, int size, int firstindex, void *skipelem); +facetT *qh_makenewfacet(setT *vertices, boolT toporient, facetT *facet); +void qh_makenewplanes(void /* newfacet_list */); +facetT *qh_makenew_nonsimplicial(facetT *visible, vertexT *apex, int *numnew); +facetT *qh_makenew_simplicial(facetT *visible, vertexT *apex, int *numnew); +void qh_matchneighbor(facetT *newfacet, int newskip, int hashsize, + int *hashcount); +void qh_matchnewfacets(void); +boolT qh_matchvertices(int firstindex, setT *verticesA, int skipA, + setT *verticesB, int *skipB, boolT *same); +facetT *qh_newfacet(void); +ridgeT *qh_newridge(void); +int qh_pointid(pointT *point); +void qh_removefacet(facetT *facet); +void qh_removevertex(vertexT *vertex); +void qh_updatevertices(void); + + +/*========== -prototypes poly2.c in alphabetical order ===========*/ + +void qh_addhash(void* newelem, setT *hashtable, int hashsize, int hash); +void qh_check_bestdist(void); +void qh_check_dupridge(facetT *facet1, realT dist1, facetT *facet2, realT dist2); +void qh_check_maxout(void); +void qh_check_output(void); +void qh_check_point(pointT *point, facetT *facet, realT *maxoutside, realT *maxdist, facetT **errfacet1, facetT **errfacet2); +void qh_check_points(void); +void qh_checkconvex(facetT *facetlist, int fault); +void qh_checkfacet(facetT *facet, boolT newmerge, boolT *waserrorp); +void qh_checkflipped_all(facetT *facetlist); +void qh_checkpolygon(facetT *facetlist); +void qh_checkvertex(vertexT *vertex); +void qh_clearcenters(qh_CENTER type); +void qh_createsimplex(setT *vertices); +void qh_delridge(ridgeT *ridge); +void qh_delvertex(vertexT *vertex); +setT *qh_facet3vertex(facetT *facet); +facetT *qh_findbestfacet(pointT *point, boolT bestoutside, + realT *bestdist, boolT *isoutside); +facetT *qh_findbestlower(facetT *upperfacet, pointT *point, realT *bestdistp, int *numpart); +facetT *qh_findfacet_all(pointT *point, realT *bestdist, boolT *isoutside, + int *numpart); +int qh_findgood(facetT *facetlist, int goodhorizon); +void qh_findgood_all(facetT *facetlist); +void qh_furthestnext(void /* qh.facet_list */); +void qh_furthestout(facetT *facet); +void qh_infiniteloop(facetT *facet); +void qh_initbuild(void); +void qh_initialhull(setT *vertices); +setT *qh_initialvertices(int dim, setT *maxpoints, pointT *points, int numpoints); +vertexT *qh_isvertex(pointT *point, setT *vertices); +vertexT *qh_makenewfacets(pointT *point /*horizon_list, visible_list*/); +void qh_matchduplicates(facetT *atfacet, int atskip, int hashsize, int *hashcount); +void qh_nearcoplanar(void /* qh.facet_list */); +vertexT *qh_nearvertex(facetT *facet, pointT *point, realT *bestdistp); +int qh_newhashtable(int newsize); +vertexT *qh_newvertex(pointT *point); +ridgeT *qh_nextridge3d(ridgeT *atridge, facetT *facet, vertexT **vertexp); +void qh_outcoplanar(void /* facet_list */); +pointT *qh_point(int id); +void qh_point_add(setT *set, pointT *point, void *elem); +setT *qh_pointfacet(void /*qh.facet_list*/); +setT *qh_pointvertex(void /*qh.facet_list*/); +void qh_prependfacet(facetT *facet, facetT **facetlist); +void qh_printhashtable(FILE *fp); +void qh_printlists(void); +void qh_resetlists(boolT stats, boolT resetVisible /*qh.newvertex_list qh.newfacet_list qh.visible_list*/); +void qh_setvoronoi_all(void); +void qh_triangulate(void /*qh.facet_list*/); +void qh_triangulate_facet(facetT *facetA, vertexT **first_vertex); +void qh_triangulate_link(facetT *oldfacetA, facetT *facetA, facetT *oldfacetB, facetT *facetB); +void qh_triangulate_mirror(facetT *facetA, facetT *facetB); +void qh_triangulate_null(facetT *facetA); +void qh_vertexintersect(setT **vertexsetA,setT *vertexsetB); +setT *qh_vertexintersect_new(setT *vertexsetA,setT *vertexsetB); +void qh_vertexneighbors(void /*qh.facet_list*/); +boolT qh_vertexsubset(setT *vertexsetA, setT *vertexsetB); + + +#endif /* qhDEFpoly */ diff --git a/src/qhull/src/libqhull/poly2.c b/src/qhull/src/libqhull/poly2.c new file mode 100644 index 000000000..de3e6ad0b --- /dev/null +++ b/src/qhull/src/libqhull/poly2.c @@ -0,0 +1,3222 @@ +/*<html><pre> -<a href="qh-poly.htm" + >-------------------------------</a><a name="TOP">-</a> + + poly2.c + implements polygons and simplices + + see qh-poly.htm, poly.h and libqhull.h + + frequently used code is in poly.c + + Copyright (c) 1993-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull/poly2.c#11 $$Change: 2069 $ + $DateTime: 2016/01/18 22:05:03 $$Author: bbarber $ +*/ + +#include "qhull_a.h" + +/*======== functions in alphabetical order ==========*/ + +/*-<a href="qh-poly.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.htm#TOC" + >-------------------------------</a><a name="check_bestdist">-</a> + + qh_check_bestdist() + check that all points are within max_outside of the nearest facet + if qh.ONLYgood, + ignores !good facets + + see: + qh_check_maxout(), qh_outerinner() + + notes: + only called from qh_check_points() + seldom used since qh.MERGING is almost always set + if notverified>0 at end of routine + some points were well inside the hull. If the hull contains + a lens-shaped component, these points were not verified. Use + options 'Qi Tv' to verify all points. (Exhaustive check also verifies) + + design: + determine facet for each point (if any) + for each point + start with the assigned facet or with the first facet + find the best facet for the point and check all coplanar facets + error if point is outside of facet +*/ +void qh_check_bestdist(void) { + boolT waserror= False, unassigned; + facetT *facet, *bestfacet, *errfacet1= NULL, *errfacet2= NULL; + facetT *facetlist; + realT dist, maxoutside, maxdist= -REALmax; + pointT *point; + int numpart= 0, facet_i, facet_n, notgood= 0, notverified= 0; + setT *facets; + + trace1((qh ferr, 1020, "qh_check_bestdist: check points below nearest facet. Facet_list f%d\n", + qh facet_list->id)); + maxoutside= qh_maxouter(); + maxoutside += qh DISTround; + /* one more qh.DISTround for check computation */ + trace1((qh ferr, 1021, "qh_check_bestdist: check that all points are within %2.2g of best facet\n", maxoutside)); + facets= qh_pointfacet(/*qh.facet_list*/); + if (!qh_QUICKhelp && qh PRINTprecision) + qh_fprintf(qh ferr, 8091, "\n\ +qhull output completed. Verifying that %d points are\n\ +below %2.2g of the nearest %sfacet.\n", + qh_setsize(facets), maxoutside, (qh ONLYgood ? "good " : "")); + FOREACHfacet_i_(facets) { /* for each point with facet assignment */ + if (facet) + unassigned= False; + else { + unassigned= True; + facet= qh facet_list; + } + point= qh_point(facet_i); + if (point == qh GOODpointp) + continue; + qh_distplane(point, facet, &dist); + numpart++; + bestfacet= qh_findbesthorizon(!qh_IScheckmax, point, facet, qh_NOupper, &dist, &numpart); + /* occurs after statistics reported */ + maximize_(maxdist, dist); + if (dist > maxoutside) { + if (qh ONLYgood && !bestfacet->good + && !((bestfacet= qh_findgooddist(point, bestfacet, &dist, &facetlist)) + && dist > maxoutside)) + notgood++; + else { + waserror= True; + qh_fprintf(qh ferr, 6109, "qhull precision error: point p%d is outside facet f%d, distance= %6.8g maxoutside= %6.8g\n", + facet_i, bestfacet->id, dist, maxoutside); + if (errfacet1 != bestfacet) { + errfacet2= errfacet1; + errfacet1= bestfacet; + } + } + }else if (unassigned && dist < -qh MAXcoplanar) + notverified++; + } + qh_settempfree(&facets); + if (notverified && !qh DELAUNAY && !qh_QUICKhelp && qh PRINTprecision) + qh_fprintf(qh ferr, 8092, "\n%d points were well inside the hull. If the hull contains\n\ +a lens-shaped component, these points were not verified. Use\n\ +options 'Qci Tv' to verify all points.\n", notverified); + if (maxdist > qh outside_err) { + qh_fprintf(qh ferr, 6110, "qhull precision error (qh_check_bestdist): a coplanar point is %6.2g from convex hull. The maximum value(qh.outside_err) is %6.2g\n", + maxdist, qh outside_err); + qh_errexit2(qh_ERRprec, errfacet1, errfacet2); + }else if (waserror && qh outside_err > REALmax/2) + qh_errexit2(qh_ERRprec, errfacet1, errfacet2); + /* else if waserror, the error was logged to qh.ferr but does not effect the output */ + trace0((qh ferr, 20, "qh_check_bestdist: max distance outside %2.2g\n", maxdist)); +} /* check_bestdist */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="check_dupridge">-</a> + + qh_check_dupridge(facet1, dist1, facet2, dist2) + Check duplicate ridge 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 duplicate ridge with the minimum distance between vertices + Throws error if the merge will increase the maximum facet width by qh_WIDEduplicate (100x) + + called from: + qh_forcedmerges() +*/ +#ifndef qh_NOmerge +void qh_check_dupridge(facetT *facet1, realT dist1, facetT *facet2, realT dist2) { + vertexT *vertex, **vertexp, *vertexA, **vertexAp; + realT dist, innerplane, mergedist, outerplane, prevdist, ratio; + realT minvertex= REALmax; + + mergedist= fmin_(dist1, dist2); + qh_outerinner(NULL, &outerplane, &innerplane); /* ratio from qh_printsummary */ + prevdist= fmax_(outerplane, innerplane); + maximize_(prevdist, qh ONEmerge + qh DISTround); + maximize_(prevdist, qh MINoutside + qh DISTround); + ratio= mergedist/prevdist; + FOREACHvertex_(facet1->vertices) { /* The duplicate ridge 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); + } + } + } + trace0((qh ferr, 16, "qh_check_dupridge: duplicate ridge between f%d and f%d due to nearly-coincident vertices (%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 ferr, 6271, "qhull precision error (qh_check_dupridge): wide merge (%.0f times wider) due to duplicate ridge with nearly coincident points (%2.2g) between f%d and f%d, merge dist %2.2g, while processing p%d\n- Ignore error with option 'Q12'\n- To be fixed in a later version of Qhull\n", + ratio, minvertex, facet1->id, facet2->id, mergedist, qh furthest_id); + if (qh DELAUNAY) + qh_fprintf(qh ferr, 8145, "- A bounding box for the input sites may alleviate this error.\n"); + if(minvertex > qh_WIDEduplicate*prevdist) + qh_fprintf(qh ferr, 8146, "- Vertex distance %2.2g is greater than %d times maximum distance %2.2g\n Please report to bradb@shore.net with steps to reproduce and all output\n", + minvertex, qh_WIDEduplicate, prevdist); + if (!qh NOwide) + qh_errexit2(qh_ERRqhull, facet1, facet2); + } +} /* check_dupridge */ +#endif + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="check_maxout">-</a> + + qh_check_maxout() + updates qh.max_outside by checking all points against bestfacet + if qh.ONLYgood, ignores !good facets + + returns: + updates facet->maxoutside via qh_findbesthorizon() + sets qh.maxoutdone + if printing qh.min_vertex (qh_outerinner), + it is updated to the current vertices + removes inside/coplanar points from coplanarset as needed + + notes: + defines coplanar as min_vertex instead of MAXcoplanar + may not need to check near-inside points because of qh.MAXcoplanar + and qh.KEEPnearinside (before it was -DISTround) + + see also: + qh_check_bestdist() + + design: + if qh.min_vertex is needed + for all neighbors of all vertices + test distance from vertex to neighbor + determine facet for each point (if any) + for each point with an assigned facet + find the best facet for the point and check all coplanar facets + (updates outer planes) + remove near-inside points from coplanar sets +*/ +#ifndef qh_NOmerge +void qh_check_maxout(void) { + facetT *facet, *bestfacet, *neighbor, **neighborp, *facetlist; + realT dist, maxoutside, minvertex, old_maxoutside; + pointT *point; + int numpart= 0, facet_i, facet_n, notgood= 0; + setT *facets, *vertices; + vertexT *vertex; + + trace1((qh ferr, 1022, "qh_check_maxout: check and update maxoutside for each facet.\n")); + maxoutside= minvertex= 0; + if (qh VERTEXneighbors + && (qh PRINTsummary || qh KEEPinside || qh KEEPcoplanar + || qh TRACElevel || qh PRINTstatistics + || qh PRINTout[0] == qh_PRINTsummary || qh PRINTout[0] == qh_PRINTnone)) { + trace1((qh ferr, 1023, "qh_check_maxout: determine actual maxoutside and minvertex\n")); + vertices= qh_pointvertex(/*qh.facet_list*/); + FORALLvertices { + FOREACHneighbor_(vertex) { + zinc_(Zdistvertex); /* distance also computed by main loop below */ + qh_distplane(vertex->point, neighbor, &dist); + minimize_(minvertex, dist); + if (-dist > qh TRACEdist || dist > qh TRACEdist + || neighbor == qh tracefacet || vertex == qh tracevertex) + qh_fprintf(qh ferr, 8093, "qh_check_maxout: p%d(v%d) is %.2g from f%d\n", + qh_pointid(vertex->point), vertex->id, dist, neighbor->id); + } + } + if (qh MERGING) { + wmin_(Wminvertex, qh min_vertex); + } + qh min_vertex= minvertex; + qh_settempfree(&vertices); + } + facets= qh_pointfacet(/*qh.facet_list*/); + do { + old_maxoutside= fmax_(qh max_outside, maxoutside); + FOREACHfacet_i_(facets) { /* for each point with facet assignment */ + if (facet) { + point= qh_point(facet_i); + if (point == qh GOODpointp) + continue; + zzinc_(Ztotcheck); + qh_distplane(point, facet, &dist); + numpart++; + bestfacet= qh_findbesthorizon(qh_IScheckmax, point, facet, !qh_NOupper, &dist, &numpart); + if (bestfacet && dist > maxoutside) { + if (qh ONLYgood && !bestfacet->good + && !((bestfacet= qh_findgooddist(point, bestfacet, &dist, &facetlist)) + && dist > maxoutside)) + notgood++; + else + maxoutside= dist; + } + if (dist > qh TRACEdist || (bestfacet && bestfacet == qh tracefacet)) + qh_fprintf(qh ferr, 8094, "qh_check_maxout: p%d is %.2g above f%d\n", + qh_pointid(point), dist, (bestfacet ? bestfacet->id : UINT_MAX)); + } + } + }while + (maxoutside > 2*old_maxoutside); + /* if qh.maxoutside increases substantially, qh_SEARCHdist is not valid + e.g., RBOX 5000 s Z1 G1e-13 t1001200614 | qhull */ + zzadd_(Zcheckpart, numpart); + qh_settempfree(&facets); + wval_(Wmaxout)= maxoutside - qh max_outside; + wmax_(Wmaxoutside, qh max_outside); + qh max_outside= maxoutside; + qh_nearcoplanar(/*qh.facet_list*/); + qh maxoutdone= True; + trace1((qh ferr, 1024, "qh_check_maxout: maxoutside %2.2g, min_vertex %2.2g, outside of not good %d\n", + maxoutside, qh min_vertex, notgood)); +} /* check_maxout */ +#else /* qh_NOmerge */ +void qh_check_maxout(void) { +} +#endif + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="check_output">-</a> + + qh_check_output() + performs the checks at the end of qhull algorithm + Maybe called after voronoi output. Will recompute otherwise centrums are Voronoi centers instead +*/ +void qh_check_output(void) { + int i; + + if (qh STOPcone) + return; + if (qh VERIFYoutput | qh IStracing | qh CHECKfrequently) { + qh_checkpolygon(qh facet_list); + qh_checkflipped_all(qh facet_list); + qh_checkconvex(qh facet_list, qh_ALGORITHMfault); + }else if (!qh MERGING && qh_newstats(qhstat precision, &i)) { + qh_checkflipped_all(qh facet_list); + qh_checkconvex(qh facet_list, qh_ALGORITHMfault); + } +} /* check_output */ + + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="check_point">-</a> + + qh_check_point( point, facet, maxoutside, maxdist, errfacet1, errfacet2 ) + check that point is less than maxoutside from facet +*/ +void qh_check_point(pointT *point, facetT *facet, realT *maxoutside, realT *maxdist, facetT **errfacet1, facetT **errfacet2) { + realT dist; + + /* occurs after statistics reported */ + qh_distplane(point, facet, &dist); + if (dist > *maxoutside) { + if (*errfacet1 != facet) { + *errfacet2= *errfacet1; + *errfacet1= facet; + } + qh_fprintf(qh ferr, 6111, "qhull precision error: point p%d is outside facet f%d, distance= %6.8g maxoutside= %6.8g\n", + qh_pointid(point), facet->id, dist, *maxoutside); + } + maximize_(*maxdist, dist); +} /* qh_check_point */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="check_points">-</a> + + qh_check_points() + checks that all points are inside all facets + + notes: + if many points and qh_check_maxout not called (i.e., !qh.MERGING), + calls qh_findbesthorizon (seldom done). + ignores flipped facets + maxoutside includes 2 qh.DISTrounds + one qh.DISTround for the computed distances in qh_check_points + qh_printafacet and qh_printsummary needs only one qh.DISTround + the computation for qh.VERIFYdirect does not account for qh.other_points + + design: + if many points + use qh_check_bestdist() + else + for all facets + for all points + check that point is inside facet +*/ +void qh_check_points(void) { + facetT *facet, *errfacet1= NULL, *errfacet2= NULL; + realT total, maxoutside, maxdist= -REALmax; + pointT *point, **pointp, *pointtemp; + boolT testouter; + + maxoutside= qh_maxouter(); + maxoutside += qh DISTround; + /* one more qh.DISTround for check computation */ + trace1((qh ferr, 1025, "qh_check_points: check all points below %2.2g of all facet planes\n", + maxoutside)); + if (qh num_good) /* miss counts other_points and !good facets */ + total= (float)qh num_good * (float)qh num_points; + else + total= (float)qh num_facets * (float)qh num_points; + if (total >= qh_VERIFYdirect && !qh maxoutdone) { + if (!qh_QUICKhelp && qh SKIPcheckmax && qh MERGING) + qh_fprintf(qh ferr, 7075, "qhull input warning: merging without checking outer planes('Q5' or 'Po').\n\ +Verify may report that a point is outside of a facet.\n"); + qh_check_bestdist(); + }else { + if (qh_MAXoutside && qh maxoutdone) + testouter= True; + else + testouter= False; + if (!qh_QUICKhelp) { + if (qh MERGEexact) + qh_fprintf(qh ferr, 7076, "qhull input warning: exact merge ('Qx'). Verify may report that a point\n\ +is outside of a facet. See qh-optq.htm#Qx\n"); + else if (qh SKIPcheckmax || qh NOnearinside) + qh_fprintf(qh ferr, 7077, "qhull input warning: no outer plane check ('Q5') or no processing of\n\ +near-inside points ('Q8'). Verify may report that a point is outside\n\ +of a facet.\n"); + } + if (qh PRINTprecision) { + if (testouter) + qh_fprintf(qh ferr, 8098, "\n\ +Output completed. Verifying that all points are below outer planes of\n\ +all %sfacets. Will make %2.0f distance computations.\n", + (qh ONLYgood ? "good " : ""), total); + else + qh_fprintf(qh ferr, 8099, "\n\ +Output completed. Verifying that all points are below %2.2g of\n\ +all %sfacets. Will make %2.0f distance computations.\n", + maxoutside, (qh ONLYgood ? "good " : ""), total); + } + FORALLfacets { + if (!facet->good && qh ONLYgood) + continue; + if (facet->flipped) + continue; + if (!facet->normal) { + qh_fprintf(qh ferr, 7061, "qhull warning (qh_check_points): missing normal for facet f%d\n", facet->id); + continue; + } + if (testouter) { +#if qh_MAXoutside + maxoutside= facet->maxoutside + 2* qh DISTround; + /* one DISTround to actual point and another to computed point */ +#endif + } + FORALLpoints { + if (point != qh GOODpointp) + qh_check_point(point, facet, &maxoutside, &maxdist, &errfacet1, &errfacet2); + } + FOREACHpoint_(qh other_points) { + if (point != qh GOODpointp) + qh_check_point(point, facet, &maxoutside, &maxdist, &errfacet1, &errfacet2); + } + } + if (maxdist > qh outside_err) { + qh_fprintf(qh ferr, 6112, "qhull precision error (qh_check_points): a coplanar point is %6.2g from convex hull. The maximum value(qh.outside_err) is %6.2g\n", + maxdist, qh outside_err ); + qh_errexit2( qh_ERRprec, errfacet1, errfacet2 ); + }else if (errfacet1 && qh outside_err > REALmax/2) + qh_errexit2( qh_ERRprec, errfacet1, errfacet2 ); + /* else if errfacet1, the error was logged to qh.ferr but does not effect the output */ + trace0((qh ferr, 21, "qh_check_points: max distance outside %2.2g\n", maxdist)); + } +} /* check_points */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="checkconvex">-</a> + + qh_checkconvex( facetlist, fault ) + check that each ridge in facetlist is convex + fault = qh_DATAfault if reporting errors + = qh_ALGORITHMfault otherwise + + returns: + counts Zconcaveridges and Zcoplanarridges + errors if concaveridge or if merging an coplanar ridge + + note: + if not merging, + tests vertices for neighboring simplicial facets + else if ZEROcentrum, + tests vertices for neighboring simplicial facets + else + tests centrums of neighboring facets + + design: + for all facets + report flipped facets + if ZEROcentrum and simplicial neighbors + test vertices for neighboring simplicial facets + else + test centrum against all neighbors +*/ +void qh_checkconvex(facetT *facetlist, int fault) { + facetT *facet, *neighbor, **neighborp, *errfacet1=NULL, *errfacet2=NULL; + vertexT *vertex; + realT dist; + pointT *centrum; + boolT waserror= False, centrum_warning= False, tempcentrum= False, allsimplicial; + int neighbor_i; + + trace1((qh ferr, 1026, "qh_checkconvex: check all ridges are convex\n")); + if (!qh RERUN) { + zzval_(Zconcaveridges)= 0; + zzval_(Zcoplanarridges)= 0; + } + FORALLfacet_(facetlist) { + if (facet->flipped) { + qh_precision("flipped facet"); + qh_fprintf(qh ferr, 6113, "qhull precision error: f%d is flipped(interior point is outside)\n", + facet->id); + errfacet1= facet; + waserror= True; + continue; + } + if (qh MERGING && (!qh ZEROcentrum || !facet->simplicial || facet->tricoplanar)) + allsimplicial= False; + else { + allsimplicial= True; + neighbor_i= 0; + FOREACHneighbor_(facet) { + vertex= SETelemt_(facet->vertices, neighbor_i++, vertexT); + if (!neighbor->simplicial || neighbor->tricoplanar) { + allsimplicial= False; + continue; + } + qh_distplane(vertex->point, neighbor, &dist); + if (dist > -qh DISTround) { + if (fault == qh_DATAfault) { + qh_precision("coplanar or concave ridge"); + qh_fprintf(qh ferr, 6114, "qhull precision error: initial simplex is not convex. Distance=%.2g\n", dist); + qh_errexit(qh_ERRsingular, NULL, NULL); + } + if (dist > qh DISTround) { + zzinc_(Zconcaveridges); + qh_precision("concave ridge"); + qh_fprintf(qh ferr, 6115, "qhull precision error: f%d is concave to f%d, since p%d(v%d) is %6.4g above\n", + facet->id, neighbor->id, qh_pointid(vertex->point), vertex->id, dist); + errfacet1= facet; + errfacet2= neighbor; + waserror= True; + }else if (qh ZEROcentrum) { + if (dist > 0) { /* qh_checkzero checks that dist < - qh DISTround */ + zzinc_(Zcoplanarridges); + qh_precision("coplanar ridge"); + qh_fprintf(qh ferr, 6116, "qhull precision error: f%d is clearly not convex to f%d, since p%d(v%d) is %6.4g above\n", + facet->id, neighbor->id, qh_pointid(vertex->point), vertex->id, dist); + errfacet1= facet; + errfacet2= neighbor; + waserror= True; + } + }else { + zzinc_(Zcoplanarridges); + qh_precision("coplanar ridge"); + trace0((qh ferr, 22, "qhull precision error: f%d may be coplanar to f%d, since p%d(v%d) is within %6.4g during p%d\n", + facet->id, neighbor->id, qh_pointid(vertex->point), vertex->id, dist, qh furthest_id)); + } + } + } + } + if (!allsimplicial) { + if (qh CENTERtype == qh_AScentrum) { + if (!facet->center) + facet->center= qh_getcentrum(facet); + centrum= facet->center; + }else { + if (!centrum_warning && (!facet->simplicial || facet->tricoplanar)) { + centrum_warning= True; + qh_fprintf(qh ferr, 7062, "qhull warning: recomputing centrums for convexity test. This may lead to false, precision errors.\n"); + } + centrum= qh_getcentrum(facet); + tempcentrum= True; + } + FOREACHneighbor_(facet) { + if (qh ZEROcentrum && facet->simplicial && neighbor->simplicial) + continue; + if (facet->tricoplanar || neighbor->tricoplanar) + continue; + zzinc_(Zdistconvex); + qh_distplane(centrum, neighbor, &dist); + if (dist > qh DISTround) { + zzinc_(Zconcaveridges); + qh_precision("concave ridge"); + qh_fprintf(qh ferr, 6117, "qhull precision error: f%d is concave to f%d. Centrum of f%d is %6.4g above f%d\n", + facet->id, neighbor->id, facet->id, dist, neighbor->id); + errfacet1= facet; + errfacet2= neighbor; + waserror= True; + }else if (dist >= 0.0) { /* if arithmetic always rounds the same, + can test against centrum radius instead */ + zzinc_(Zcoplanarridges); + qh_precision("coplanar ridge"); + qh_fprintf(qh ferr, 6118, "qhull precision error: f%d is coplanar or concave to f%d. Centrum of f%d is %6.4g above f%d\n", + facet->id, neighbor->id, facet->id, dist, neighbor->id); + errfacet1= facet; + errfacet2= neighbor; + waserror= True; + } + } + if (tempcentrum) + qh_memfree(centrum, qh normal_size); + } + } + if (waserror && !qh FORCEoutput) + qh_errexit2(qh_ERRprec, errfacet1, errfacet2); +} /* checkconvex */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="checkfacet">-</a> + + qh_checkfacet( facet, newmerge, waserror ) + checks for consistency errors in facet + newmerge set if from merge.c + + returns: + sets waserror if any error occurs + + checks: + vertex ids are inverse sorted + unless newmerge, at least hull_dim neighbors and vertices (exactly if simplicial) + if non-simplicial, at least as many ridges as neighbors + neighbors are not duplicated + ridges are not duplicated + in 3-d, ridges=verticies + (qh.hull_dim-1) ridge vertices + neighbors are reciprocated + ridge neighbors are facet neighbors and a ridge for every neighbor + simplicial neighbors match facetintersect + vertex intersection matches vertices of common ridges + vertex neighbors and facet vertices agree + all ridges have distinct vertex sets + + notes: + uses neighbor->seen + + design: + check sets + check vertices + check sizes of neighbors and vertices + check for qh_MERGEridge and qh_DUPLICATEridge flags + check neighbor set + check ridge set + check ridges, neighbors, and vertices +*/ +void qh_checkfacet(facetT *facet, boolT newmerge, boolT *waserrorp) { + facetT *neighbor, **neighborp, *errother=NULL; + ridgeT *ridge, **ridgep, *errridge= NULL, *ridge2; + vertexT *vertex, **vertexp; + unsigned previousid= INT_MAX; + int numneighbors, numvertices, numridges=0, numRvertices=0; + boolT waserror= False; + int skipA, skipB, ridge_i, ridge_n, i; + setT *intersection; + + if (facet->visible) { + qh_fprintf(qh ferr, 6119, "qhull internal error (qh_checkfacet): facet f%d is on the visible_list\n", + facet->id); + qh_errexit(qh_ERRqhull, facet, NULL); + } + if (!facet->normal) { + qh_fprintf(qh ferr, 6120, "qhull internal error (qh_checkfacet): facet f%d does not have a normal\n", + facet->id); + waserror= True; + } + qh_setcheck(facet->vertices, "vertices for f", facet->id); + qh_setcheck(facet->ridges, "ridges for f", facet->id); + qh_setcheck(facet->outsideset, "outsideset for f", facet->id); + qh_setcheck(facet->coplanarset, "coplanarset for f", facet->id); + qh_setcheck(facet->neighbors, "neighbors for f", facet->id); + FOREACHvertex_(facet->vertices) { + if (vertex->deleted) { + qh_fprintf(qh ferr, 6121, "qhull internal error (qh_checkfacet): deleted vertex v%d in f%d\n", vertex->id, facet->id); + qh_errprint("ERRONEOUS", NULL, NULL, NULL, vertex); + waserror= True; + } + if (vertex->id >= previousid) { + qh_fprintf(qh ferr, 6122, "qhull internal error (qh_checkfacet): vertices of f%d are not in descending id order at v%d\n", facet->id, vertex->id); + waserror= True; + break; + } + previousid= vertex->id; + } + numneighbors= qh_setsize(facet->neighbors); + numvertices= qh_setsize(facet->vertices); + numridges= qh_setsize(facet->ridges); + if (facet->simplicial) { + if (numvertices+numneighbors != 2*qh hull_dim + && !facet->degenerate && !facet->redundant) { + qh_fprintf(qh ferr, 6123, "qhull internal error (qh_checkfacet): for simplicial facet f%d, #vertices %d + #neighbors %d != 2*qh hull_dim\n", + facet->id, numvertices, numneighbors); + qh_setprint(qh ferr, "", facet->neighbors); + waserror= True; + } + }else { /* non-simplicial */ + if (!newmerge + &&(numvertices < qh hull_dim || numneighbors < qh hull_dim) + && !facet->degenerate && !facet->redundant) { + qh_fprintf(qh ferr, 6124, "qhull internal error (qh_checkfacet): for facet f%d, #vertices %d or #neighbors %d < qh hull_dim\n", + facet->id, numvertices, numneighbors); + waserror= True; + } + /* in 3-d, can get a vertex twice in an edge list, e.g., RBOX 1000 s W1e-13 t995849315 D2 | QHULL d Tc Tv TP624 TW1e-13 T4 */ + if (numridges < numneighbors + ||(qh hull_dim == 3 && numvertices > numridges && !qh NEWfacets) + ||(qh hull_dim == 2 && numridges + numvertices + numneighbors != 6)) { + if (!facet->degenerate && !facet->redundant) { + qh_fprintf(qh ferr, 6125, "qhull internal error (qh_checkfacet): for facet f%d, #ridges %d < #neighbors %d or(3-d) > #vertices %d or(2-d) not all 2\n", + facet->id, numridges, numneighbors, numvertices); + waserror= True; + } + } + } + FOREACHneighbor_(facet) { + if (neighbor == qh_MERGEridge || neighbor == qh_DUPLICATEridge) { + qh_fprintf(qh ferr, 6126, "qhull internal error (qh_checkfacet): facet f%d still has a MERGE or DUP neighbor\n", facet->id); + qh_errexit(qh_ERRqhull, facet, NULL); + } + neighbor->seen= True; + } + FOREACHneighbor_(facet) { + if (!qh_setin(neighbor->neighbors, facet)) { + qh_fprintf(qh ferr, 6127, "qhull internal error (qh_checkfacet): facet f%d has neighbor f%d, but f%d does not have neighbor f%d\n", + facet->id, neighbor->id, neighbor->id, facet->id); + errother= neighbor; + waserror= True; + } + if (!neighbor->seen) { + qh_fprintf(qh ferr, 6128, "qhull internal error (qh_checkfacet): facet f%d has a duplicate neighbor f%d\n", + facet->id, neighbor->id); + errother= neighbor; + waserror= True; + } + neighbor->seen= False; + } + FOREACHridge_(facet->ridges) { + qh_setcheck(ridge->vertices, "vertices for r", ridge->id); + ridge->seen= False; + } + FOREACHridge_(facet->ridges) { + if (ridge->seen) { + qh_fprintf(qh ferr, 6129, "qhull internal error (qh_checkfacet): facet f%d has a duplicate ridge r%d\n", + facet->id, ridge->id); + errridge= ridge; + waserror= True; + } + ridge->seen= True; + numRvertices= qh_setsize(ridge->vertices); + if (numRvertices != qh hull_dim - 1) { + qh_fprintf(qh ferr, 6130, "qhull internal error (qh_checkfacet): ridge between f%d and f%d has %d vertices\n", + ridge->top->id, ridge->bottom->id, numRvertices); + errridge= ridge; + waserror= True; + } + neighbor= otherfacet_(ridge, facet); + neighbor->seen= True; + if (!qh_setin(facet->neighbors, neighbor)) { + qh_fprintf(qh ferr, 6131, "qhull internal error (qh_checkfacet): for facet f%d, neighbor f%d of ridge r%d not in facet\n", + facet->id, neighbor->id, ridge->id); + errridge= ridge; + waserror= True; + } + } + if (!facet->simplicial) { + FOREACHneighbor_(facet) { + if (!neighbor->seen) { + qh_fprintf(qh ferr, 6132, "qhull internal error (qh_checkfacet): facet f%d does not have a ridge for neighbor f%d\n", + facet->id, neighbor->id); + errother= neighbor; + waserror= True; + } + intersection= qh_vertexintersect_new(facet->vertices, neighbor->vertices); + qh_settemppush(intersection); + FOREACHvertex_(facet->vertices) { + vertex->seen= False; + vertex->seen2= False; + } + FOREACHvertex_(intersection) + vertex->seen= True; + FOREACHridge_(facet->ridges) { + if (neighbor != otherfacet_(ridge, facet)) + continue; + FOREACHvertex_(ridge->vertices) { + if (!vertex->seen) { + qh_fprintf(qh ferr, 6133, "qhull internal error (qh_checkfacet): vertex v%d in r%d not in f%d intersect f%d\n", + vertex->id, ridge->id, facet->id, neighbor->id); + qh_errexit(qh_ERRqhull, facet, ridge); + } + vertex->seen2= True; + } + } + if (!newmerge) { + FOREACHvertex_(intersection) { + if (!vertex->seen2) { + if (qh IStracing >=3 || !qh MERGING) { + qh_fprintf(qh ferr, 6134, "qhull precision error (qh_checkfacet): vertex v%d in f%d intersect f%d but\n\ + not in a ridge. This is ok under merging. Last point was p%d\n", + vertex->id, facet->id, neighbor->id, qh furthest_id); + if (!qh FORCEoutput && !qh MERGING) { + qh_errprint("ERRONEOUS", facet, neighbor, NULL, vertex); + if (!qh MERGING) + qh_errexit(qh_ERRqhull, NULL, NULL); + } + } + } + } + } + qh_settempfree(&intersection); + } + }else { /* simplicial */ + FOREACHneighbor_(facet) { + if (neighbor->simplicial) { + skipA= SETindex_(facet->neighbors, neighbor); + skipB= qh_setindex(neighbor->neighbors, facet); + if (skipA<0 || skipB<0 || !qh_setequal_skip(facet->vertices, skipA, neighbor->vertices, skipB)) { + qh_fprintf(qh ferr, 6135, "qhull internal error (qh_checkfacet): facet f%d skip %d and neighbor f%d skip %d do not match \n", + facet->id, skipA, neighbor->id, skipB); + errother= neighbor; + waserror= True; + } + } + } + } + if (qh hull_dim < 5 && (qh IStracing > 2 || qh CHECKfrequently)) { + FOREACHridge_i_(facet->ridges) { /* expensive */ + for (i=ridge_i+1; i < ridge_n; i++) { + ridge2= SETelemt_(facet->ridges, i, ridgeT); + if (qh_setequal(ridge->vertices, ridge2->vertices)) { + qh_fprintf(qh ferr, 6227, "Qhull internal error (qh_checkfacet): ridges r%d and r%d have the same vertices\n", + ridge->id, ridge2->id); + errridge= ridge; + waserror= True; + } + } + } + } + if (waserror) { + qh_errprint("ERRONEOUS", facet, errother, errridge, NULL); + *waserrorp= True; + } +} /* checkfacet */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="checkflipped_all">-</a> + + qh_checkflipped_all( facetlist ) + checks orientation of facets in list against interior point +*/ +void qh_checkflipped_all(facetT *facetlist) { + facetT *facet; + boolT waserror= False; + realT dist; + + if (facetlist == qh facet_list) + zzval_(Zflippedfacets)= 0; + FORALLfacet_(facetlist) { + if (facet->normal && !qh_checkflipped(facet, &dist, !qh_ALL)) { + qh_fprintf(qh ferr, 6136, "qhull precision error: facet f%d is flipped, distance= %6.12g\n", + facet->id, dist); + if (!qh FORCEoutput) { + qh_errprint("ERRONEOUS", facet, NULL, NULL, NULL); + waserror= True; + } + } + } + if (waserror) { + qh_fprintf(qh ferr, 8101, "\n\ +A flipped facet occurs when its distance to the interior point is\n\ +greater than %2.2g, the maximum roundoff error.\n", -qh DISTround); + qh_errexit(qh_ERRprec, NULL, NULL); + } +} /* checkflipped_all */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="checkpolygon">-</a> + + qh_checkpolygon( facetlist ) + checks the correctness of the structure + + notes: + call with either qh.facet_list or qh.newfacet_list + checks num_facets and num_vertices if qh.facet_list + + design: + for each facet + checks facet and outside set + initializes vertexlist + for each facet + checks vertex set + if checking all facets(qh.facetlist) + check facet count + if qh.VERTEXneighbors + check vertex neighbors and count + check vertex count +*/ +void qh_checkpolygon(facetT *facetlist) { + facetT *facet; + vertexT *vertex, **vertexp, *vertexlist; + int numfacets= 0, numvertices= 0, numridges= 0; + int totvneighbors= 0, totvertices= 0; + boolT waserror= False, nextseen= False, visibleseen= False; + + trace1((qh ferr, 1027, "qh_checkpolygon: check all facets from f%d\n", facetlist->id)); + if (facetlist != qh facet_list || qh ONLYgood) + nextseen= True; + FORALLfacet_(facetlist) { + if (facet == qh visible_list) + visibleseen= True; + if (!facet->visible) { + if (!nextseen) { + if (facet == qh facet_next) + nextseen= True; + else if (qh_setsize(facet->outsideset)) { + if (!qh NARROWhull +#if !qh_COMPUTEfurthest + || facet->furthestdist >= qh MINoutside +#endif + ) { + qh_fprintf(qh ferr, 6137, "qhull internal error (qh_checkpolygon): f%d has outside points before qh facet_next\n", + facet->id); + qh_errexit(qh_ERRqhull, facet, NULL); + } + } + } + numfacets++; + qh_checkfacet(facet, False, &waserror); + } + } + if (qh visible_list && !visibleseen && facetlist == qh facet_list) { + qh_fprintf(qh ferr, 6138, "qhull internal error (qh_checkpolygon): visible list f%d no longer on facet list\n", qh visible_list->id); + qh_printlists(); + qh_errexit(qh_ERRqhull, qh visible_list, NULL); + } + if (facetlist == qh facet_list) + vertexlist= qh vertex_list; + else if (facetlist == qh newfacet_list) + vertexlist= qh newvertex_list; + else + vertexlist= NULL; + FORALLvertex_(vertexlist) { + vertex->seen= False; + vertex->visitid= 0; + } + FORALLfacet_(facetlist) { + if (facet->visible) + continue; + if (facet->simplicial) + numridges += qh hull_dim; + else + numridges += qh_setsize(facet->ridges); + FOREACHvertex_(facet->vertices) { + vertex->visitid++; + if (!vertex->seen) { + vertex->seen= True; + numvertices++; + if (qh_pointid(vertex->point) == qh_IDunknown) { + qh_fprintf(qh ferr, 6139, "qhull internal error (qh_checkpolygon): unknown point %p for vertex v%d first_point %p\n", + vertex->point, vertex->id, qh first_point); + waserror= True; + } + } + } + } + qh vertex_visit += (unsigned int)numfacets; + if (facetlist == qh facet_list) { + if (numfacets != qh num_facets - qh num_visible) { + qh_fprintf(qh ferr, 6140, "qhull internal error (qh_checkpolygon): actual number of facets is %d, cumulative facet count is %d - %d visible facets\n", + numfacets, qh num_facets, qh num_visible); + waserror= True; + } + qh vertex_visit++; + if (qh VERTEXneighbors) { + FORALLvertices { + qh_setcheck(vertex->neighbors, "neighbors for v", vertex->id); + if (vertex->deleted) + continue; + totvneighbors += qh_setsize(vertex->neighbors); + } + FORALLfacet_(facetlist) + totvertices += qh_setsize(facet->vertices); + if (totvneighbors != totvertices) { + qh_fprintf(qh ferr, 6141, "qhull internal error (qh_checkpolygon): vertex neighbors inconsistent. Totvneighbors %d, totvertices %d\n", + totvneighbors, totvertices); + waserror= True; + } + } + if (numvertices != qh num_vertices - qh_setsize(qh del_vertices)) { + qh_fprintf(qh ferr, 6142, "qhull internal error (qh_checkpolygon): actual number of vertices is %d, cumulative vertex count is %d\n", + numvertices, qh num_vertices - qh_setsize(qh del_vertices)); + waserror= True; + } + if (qh hull_dim == 2 && numvertices != numfacets) { + qh_fprintf(qh ferr, 6143, "qhull internal error (qh_checkpolygon): #vertices %d != #facets %d\n", + numvertices, numfacets); + waserror= True; + } + if (qh hull_dim == 3 && numvertices + numfacets - numridges/2 != 2) { + qh_fprintf(qh ferr, 7063, "qhull warning: #vertices %d + #facets %d - #edges %d != 2\n\ + A vertex appears twice in a edge list. May occur during merging.", + numvertices, numfacets, numridges/2); + /* occurs if lots of merging and a vertex ends up twice in an edge list. e.g., RBOX 1000 s W1e-13 t995849315 D2 | QHULL d Tc Tv */ + } + } + if (waserror) + qh_errexit(qh_ERRqhull, NULL, NULL); +} /* checkpolygon */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="checkvertex">-</a> + + qh_checkvertex( vertex ) + check vertex for consistency + checks vertex->neighbors + + notes: + neighbors checked efficiently in checkpolygon +*/ +void qh_checkvertex(vertexT *vertex) { + boolT waserror= False; + facetT *neighbor, **neighborp, *errfacet=NULL; + + if (qh_pointid(vertex->point) == qh_IDunknown) { + qh_fprintf(qh ferr, 6144, "qhull internal error (qh_checkvertex): unknown point id %p\n", vertex->point); + waserror= True; + } + if (vertex->id >= qh vertex_id) { + qh_fprintf(qh ferr, 6145, "qhull internal error (qh_checkvertex): unknown vertex id %d\n", vertex->id); + waserror= True; + } + if (!waserror && !vertex->deleted) { + if (qh_setsize(vertex->neighbors)) { + FOREACHneighbor_(vertex) { + if (!qh_setin(neighbor->vertices, vertex)) { + qh_fprintf(qh ferr, 6146, "qhull internal error (qh_checkvertex): neighbor f%d does not contain v%d\n", neighbor->id, vertex->id); + errfacet= neighbor; + waserror= True; + } + } + } + } + if (waserror) { + qh_errprint("ERRONEOUS", NULL, NULL, NULL, vertex); + qh_errexit(qh_ERRqhull, errfacet, NULL); + } +} /* checkvertex */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="clearcenters">-</a> + + qh_clearcenters( type ) + clear old data from facet->center + + notes: + sets new centertype + nop if CENTERtype is the same +*/ +void qh_clearcenters(qh_CENTER type) { + facetT *facet; + + if (qh CENTERtype != type) { + FORALLfacets { + if (facet->tricoplanar && !facet->keepcentrum) + facet->center= NULL; /* center is owned by the ->keepcentrum facet */ + else if (qh CENTERtype == qh_ASvoronoi){ + if (facet->center) { + qh_memfree(facet->center, qh center_size); + facet->center= NULL; + } + }else /* qh.CENTERtype == qh_AScentrum */ { + if (facet->center) { + qh_memfree(facet->center, qh normal_size); + facet->center= NULL; + } + } + } + qh CENTERtype= type; + } + trace2((qh ferr, 2043, "qh_clearcenters: switched to center type %d\n", type)); +} /* clearcenters */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="createsimplex">-</a> + + qh_createsimplex( vertices ) + creates a simplex from a set of vertices + + returns: + initializes qh.facet_list to the simplex + initializes qh.newfacet_list, .facet_tail + initializes qh.vertex_list, .newvertex_list, .vertex_tail + + design: + initializes lists + for each vertex + create a new facet + for each new facet + create its neighbor set +*/ +void qh_createsimplex(setT *vertices) { + facetT *facet= NULL, *newfacet; + boolT toporient= True; + int vertex_i, vertex_n, nth; + setT *newfacets= qh_settemp(qh hull_dim+1); + vertexT *vertex; + + qh facet_list= qh newfacet_list= qh facet_tail= qh_newfacet(); + qh num_facets= qh num_vertices= qh num_visible= 0; + qh vertex_list= qh newvertex_list= qh vertex_tail= qh_newvertex(NULL); + FOREACHvertex_i_(vertices) { + newfacet= qh_newfacet(); + newfacet->vertices= qh_setnew_delnthsorted(vertices, vertex_n, + vertex_i, 0); + newfacet->toporient= (unsigned char)toporient; + qh_appendfacet(newfacet); + newfacet->newfacet= True; + qh_appendvertex(vertex); + qh_setappend(&newfacets, newfacet); + toporient ^= True; + } + FORALLnew_facets { + nth= 0; + FORALLfacet_(qh newfacet_list) { + if (facet != newfacet) + SETelem_(newfacet->neighbors, nth++)= facet; + } + qh_settruncate(newfacet->neighbors, qh hull_dim); + } + qh_settempfree(&newfacets); + trace1((qh ferr, 1028, "qh_createsimplex: created simplex\n")); +} /* createsimplex */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="delridge">-</a> + + qh_delridge( ridge ) + deletes ridge from data structures it belongs to + frees up its memory + + notes: + in merge.c, caller sets vertex->delridge for each vertex + ridges also freed in qh_freeqhull +*/ +void qh_delridge(ridgeT *ridge) { + void **freelistp; /* used if !qh_NOmem by qh_memfree_() */ + + qh_setdel(ridge->top->ridges, ridge); + qh_setdel(ridge->bottom->ridges, ridge); + qh_setfree(&(ridge->vertices)); + qh_memfree_(ridge, (int)sizeof(ridgeT), freelistp); +} /* delridge */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="delvertex">-</a> + + qh_delvertex( vertex ) + deletes a vertex and frees its memory + + notes: + assumes vertex->adjacencies have been updated if needed + unlinks from vertex_list +*/ +void qh_delvertex(vertexT *vertex) { + + if (vertex == qh tracevertex) + qh tracevertex= NULL; + qh_removevertex(vertex); + qh_setfree(&vertex->neighbors); + qh_memfree(vertex, (int)sizeof(vertexT)); +} /* delvertex */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="facet3vertex">-</a> + + qh_facet3vertex( ) + return temporary set of 3-d vertices in qh_ORIENTclock order + + design: + if simplicial facet + build set from facet->vertices with facet->toporient + else + for each ridge in order + build set from ridge's vertices +*/ +setT *qh_facet3vertex(facetT *facet) { + ridgeT *ridge, *firstridge; + vertexT *vertex; + int cntvertices, cntprojected=0; + setT *vertices; + + cntvertices= qh_setsize(facet->vertices); + vertices= qh_settemp(cntvertices); + if (facet->simplicial) { + if (cntvertices != 3) { + qh_fprintf(qh ferr, 6147, "qhull internal error (qh_facet3vertex): only %d vertices for simplicial facet f%d\n", + cntvertices, facet->id); + qh_errexit(qh_ERRqhull, facet, NULL); + } + qh_setappend(&vertices, SETfirst_(facet->vertices)); + if (facet->toporient ^ qh_ORIENTclock) + qh_setappend(&vertices, SETsecond_(facet->vertices)); + else + qh_setaddnth(&vertices, 0, SETsecond_(facet->vertices)); + qh_setappend(&vertices, SETelem_(facet->vertices, 2)); + }else { + ridge= firstridge= SETfirstt_(facet->ridges, ridgeT); /* no infinite */ + while ((ridge= qh_nextridge3d(ridge, facet, &vertex))) { + qh_setappend(&vertices, vertex); + if (++cntprojected > cntvertices || ridge == firstridge) + break; + } + if (!ridge || cntprojected != cntvertices) { + qh_fprintf(qh ferr, 6148, "qhull internal error (qh_facet3vertex): ridges for facet %d don't match up. got at least %d\n", + facet->id, cntprojected); + qh_errexit(qh_ERRqhull, facet, ridge); + } + } + return vertices; +} /* facet3vertex */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="findbestfacet">-</a> + + qh_findbestfacet( point, bestoutside, bestdist, isoutside ) + find facet that is furthest below a point + + for Delaunay triangulations, + Use qh_setdelaunay() to lift point to paraboloid and scale by 'Qbb' if needed + Do not use options 'Qbk', 'QBk', or 'QbB' since they scale the coordinates. + + returns: + if bestoutside is set (e.g., qh_ALL) + returns best facet that is not upperdelaunay + if Delaunay and inside, point is outside circumsphere of bestfacet + else + returns first facet below point + if point is inside, returns nearest, !upperdelaunay facet + distance to facet + isoutside set if outside of facet + + notes: + For tricoplanar facets, this finds one of the tricoplanar facets closest + to the point. For Delaunay triangulations, the point may be inside a + different tricoplanar facet. See <a href="../html/qh-code.htm#findfacet">locate a facet with qh_findbestfacet()</a> + + 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.c#findbest">qh_findbest</a> +*/ +facetT *qh_findbestfacet(pointT *point, boolT bestoutside, + realT *bestdist, boolT *isoutside) { + facetT *bestfacet= NULL; + int numpart, totpart= 0; + + bestfacet= qh_findbest(point, qh facet_list, + bestoutside, !qh_ISnewfacets, bestoutside /* qh_NOupper */, + bestdist, isoutside, &totpart); + if (*bestdist < -qh DISTround) { + bestfacet= qh_findfacet_all(point, bestdist, isoutside, &numpart); + totpart += numpart; + if ((isoutside && *isoutside && bestoutside) + || (isoutside && !*isoutside && bestfacet->upperdelaunay)) { + bestfacet= qh_findbest(point, bestfacet, + bestoutside, False, bestoutside, + bestdist, isoutside, &totpart); + totpart += numpart; + } + } + trace3((qh ferr, 3014, "qh_findbestfacet: f%d dist %2.2g isoutside %d totpart %d\n", + bestfacet->id, *bestdist, (isoutside ? *isoutside : UINT_MAX), totpart)); + return bestfacet; +} /* findbestfacet */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="findbestlower">-</a> + + qh_findbestlower( facet, point, bestdist, numpart ) + returns best non-upper, non-flipped neighbor of facet for point + if needed, searches vertex neighbors + + returns: + returns bestdist and updates numpart + + notes: + if Delaunay and inside, point is outside of circumsphere of bestfacet + called by qh_findbest() for points above an upperdelaunay facet + +*/ +facetT *qh_findbestlower(facetT *upperfacet, pointT *point, realT *bestdistp, int *numpart) { + facetT *neighbor, **neighborp, *bestfacet= NULL; + realT bestdist= -REALmax/2 /* avoid underflow */; + realT dist; + vertexT *vertex; + boolT isoutside= False; /* not used */ + + zinc_(Zbestlower); + FOREACHneighbor_(upperfacet) { + if (neighbor->upperdelaunay || neighbor->flipped) + continue; + (*numpart)++; + qh_distplane(point, neighbor, &dist); + if (dist > bestdist) { + bestfacet= neighbor; + bestdist= dist; + } + } + if (!bestfacet) { + zinc_(Zbestlowerv); + /* rarely called, numpart does not count nearvertex computations */ + vertex= qh_nearvertex(upperfacet, point, &dist); + qh_vertexneighbors(); + FOREACHneighbor_(vertex) { + if (neighbor->upperdelaunay || neighbor->flipped) + continue; + (*numpart)++; + qh_distplane(point, neighbor, &dist); + if (dist > bestdist) { + bestfacet= neighbor; + bestdist= dist; + } + } + } + if (!bestfacet) { + zinc_(Zbestlowerall); /* invoked once per point in outsideset */ + zmax_(Zbestloweralln, qh num_facets); + /* [dec'15] Previously reported as QH6228 */ + trace3((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(point, &bestdist, &isoutside, numpart); + } + *bestdistp= bestdist; + trace3((qh ferr, 3015, "qh_findbestlower: f%d dist %2.2g for f%d p%d\n", + bestfacet->id, bestdist, upperfacet->id, qh_pointid(point))); + return bestfacet; +} /* findbestlower */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="findfacet_all">-</a> + + qh_findfacet_all( point, bestdist, isoutside, numpart ) + exhaustive search for facet below a point + + for Delaunay triangulations, + Use qh_setdelaunay() to lift point to paraboloid and scale by 'Qbb' if needed + Do not use options 'Qbk', 'QBk', or 'QbB' since they scale the coordinates. + + returns: + returns first facet below point + if point is inside, + returns nearest facet + distance to facet + isoutside if point is outside of the hull + number of distance tests + + notes: + primarily for library users, rarely used by Qhull +*/ +facetT *qh_findfacet_all(pointT *point, realT *bestdist, boolT *isoutside, + int *numpart) { + facetT *bestfacet= NULL, *facet; + realT dist; + int totpart= 0; + + *bestdist= -REALmax; + *isoutside= False; + FORALLfacets { + if (facet->flipped || !facet->normal) + continue; + totpart++; + qh_distplane(point, facet, &dist); + if (dist > *bestdist) { + *bestdist= dist; + bestfacet= facet; + if (dist > qh MINoutside) { + *isoutside= True; + break; + } + } + } + *numpart= totpart; + trace3((qh ferr, 3016, "qh_findfacet_all: f%d dist %2.2g isoutside %d totpart %d\n", + getid_(bestfacet), *bestdist, *isoutside, totpart)); + return bestfacet; +} /* findfacet_all */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="findgood">-</a> + + qh_findgood( facetlist, goodhorizon ) + identify good facets for qh.PRINTgood + if qh.GOODvertex>0 + facet includes point as vertex + if !match, returns goodhorizon + inactive if qh.MERGING + if qh.GOODpoint + facet is visible or coplanar (>0) or not visible (<0) + if qh.GOODthreshold + facet->normal matches threshold + if !goodhorizon and !match, + selects facet with closest angle + sets GOODclosest + + returns: + number of new, good facets found + determines facet->good + may update qh.GOODclosest + + notes: + qh_findgood_all further reduces the good region + + design: + count good facets + mark good facets for qh.GOODpoint + mark good facets for qh.GOODthreshold + if necessary + update qh.GOODclosest +*/ +int qh_findgood(facetT *facetlist, int goodhorizon) { + facetT *facet, *bestfacet= NULL; + realT angle, bestangle= REALmax, dist; + int numgood=0; + + FORALLfacet_(facetlist) { + if (facet->good) + numgood++; + } + if (qh GOODvertex>0 && !qh MERGING) { + FORALLfacet_(facetlist) { + if (!qh_isvertex(qh GOODvertexp, facet->vertices)) { + facet->good= False; + numgood--; + } + } + } + if (qh GOODpoint && numgood) { + FORALLfacet_(facetlist) { + if (facet->good && facet->normal) { + zinc_(Zdistgood); + qh_distplane(qh GOODpointp, facet, &dist); + if ((qh GOODpoint > 0) ^ (dist > 0.0)) { + facet->good= False; + numgood--; + } + } + } + } + if (qh GOODthreshold && (numgood || goodhorizon || qh GOODclosest)) { + FORALLfacet_(facetlist) { + if (facet->good && facet->normal) { + if (!qh_inthresholds(facet->normal, &angle)) { + facet->good= False; + numgood--; + if (angle < bestangle) { + bestangle= angle; + bestfacet= facet; + } + } + } + } + if (!numgood && (!goodhorizon || qh GOODclosest)) { + if (qh GOODclosest) { + if (qh GOODclosest->visible) + qh GOODclosest= NULL; + else { + qh_inthresholds(qh GOODclosest->normal, &angle); + if (angle < bestangle) + bestfacet= qh GOODclosest; + } + } + if (bestfacet && bestfacet != qh GOODclosest) { + if (qh GOODclosest) + qh GOODclosest->good= False; + qh GOODclosest= bestfacet; + bestfacet->good= True; + numgood++; + trace2((qh ferr, 2044, "qh_findgood: f%d is closest(%2.2g) to thresholds\n", + bestfacet->id, bestangle)); + return numgood; + } + }else if (qh GOODclosest) { /* numgood > 0 */ + qh GOODclosest->good= False; + qh GOODclosest= NULL; + } + } + zadd_(Zgoodfacet, numgood); + trace2((qh ferr, 2045, "qh_findgood: found %d good facets with %d good horizon\n", + numgood, goodhorizon)); + if (!numgood && qh GOODvertex>0 && !qh MERGING) + return goodhorizon; + return numgood; +} /* findgood */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="findgood_all">-</a> + + qh_findgood_all( facetlist ) + apply other constraints for good facets (used by qh.PRINTgood) + if qh.GOODvertex + facet includes (>0) or doesn't include (<0) point as vertex + if last good facet and ONLYgood, prints warning and continues + if qh.SPLITthresholds + facet->normal matches threshold, or if none, the closest one + calls qh_findgood + nop if good not used + + returns: + clears facet->good if not good + sets qh.num_good + + notes: + this is like qh_findgood but more restrictive + + design: + uses qh_findgood to mark good facets + marks facets for qh.GOODvertex + marks facets for qh.SPLITthreholds +*/ +void qh_findgood_all(facetT *facetlist) { + facetT *facet, *bestfacet=NULL; + realT angle, bestangle= REALmax; + int numgood=0, startgood; + + if (!qh GOODvertex && !qh GOODthreshold && !qh GOODpoint + && !qh SPLITthresholds) + return; + if (!qh ONLYgood) + qh_findgood(qh facet_list, 0); + FORALLfacet_(facetlist) { + if (facet->good) + numgood++; + } + if (qh GOODvertex <0 || (qh GOODvertex > 0 && qh MERGING)) { + FORALLfacet_(facetlist) { + if (facet->good && ((qh GOODvertex > 0) ^ !!qh_isvertex(qh GOODvertexp, facet->vertices))) { + if (!--numgood) { + if (qh ONLYgood) { + qh_fprintf(qh ferr, 7064, "qhull warning: good vertex p%d does not match last good facet f%d. Ignored.\n", + qh_pointid(qh GOODvertexp), facet->id); + return; + }else if (qh GOODvertex > 0) + qh_fprintf(qh ferr, 7065, "qhull warning: point p%d is not a vertex('QV%d').\n", + qh GOODvertex-1, qh GOODvertex-1); + else + qh_fprintf(qh ferr, 7066, "qhull warning: point p%d is a vertex for every facet('QV-%d').\n", + -qh GOODvertex - 1, -qh GOODvertex - 1); + } + facet->good= False; + } + } + } + startgood= numgood; + if (qh SPLITthresholds) { + FORALLfacet_(facetlist) { + if (facet->good) { + if (!qh_inthresholds(facet->normal, &angle)) { + facet->good= False; + numgood--; + if (angle < bestangle) { + bestangle= angle; + bestfacet= facet; + } + } + } + } + if (!numgood && bestfacet) { + bestfacet->good= True; + numgood++; + trace0((qh ferr, 23, "qh_findgood_all: f%d is closest(%2.2g) to thresholds\n", + bestfacet->id, bestangle)); + return; + } + } + qh num_good= numgood; + trace0((qh ferr, 24, "qh_findgood_all: %d good facets remain out of %d facets\n", + numgood, startgood)); +} /* findgood_all */ + +/*-<a href="qh-poly.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(void /* qh.facet_list */) { + facetT *facet, *bestfacet= NULL; + realT dist, bestdist= -REALmax; + + FORALLfacets { + if (facet->outsideset) { +#if qh_COMPUTEfurthest + pointT *furthest; + furthest= (pointT*)qh_setlast(facet->outsideset); + zinc_(Zcomputefurthest); + qh_distplane(furthest, facet, &dist); +#else + dist= facet->furthestdist; +#endif + if (dist > bestdist) { + bestfacet= facet; + bestdist= dist; + } + } + } + if (bestfacet) { + qh_removefacet(bestfacet); + qh_prependfacet(bestfacet, &qh facet_next); + trace1((qh ferr, 1029, "qh_furthestnext: made f%d next facet(dist %.2g)\n", + bestfacet->id, bestdist)); + } +} /* furthestnext */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="furthestout">-</a> + + qh_furthestout( facet ) + make furthest outside point the last point of outsideset + + returns: + updates facet->outsideset + clears facet->notfurthest + sets facet->furthestdist + + design: + determine best point of outsideset + make it the last point of outsideset +*/ +void qh_furthestout(facetT *facet) { + pointT *point, **pointp, *bestpoint= NULL; + realT dist, bestdist= -REALmax; + + FOREACHpoint_(facet->outsideset) { + qh_distplane(point, facet, &dist); + zinc_(Zcomputefurthest); + if (dist > bestdist) { + bestpoint= point; + bestdist= dist; + } + } + if (bestpoint) { + qh_setdel(facet->outsideset, point); + qh_setappend(&facet->outsideset, point); +#if !qh_COMPUTEfurthest + facet->furthestdist= bestdist; +#endif + } + facet->notfurthest= False; + trace3((qh ferr, 3017, "qh_furthestout: p%d is furthest outside point of f%d\n", + qh_pointid(point), facet->id)); +} /* furthestout */ + + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="infiniteloop">-</a> + + qh_infiniteloop( facet ) + report infinite loop error due to facet +*/ +void qh_infiniteloop(facetT *facet) { + + qh_fprintf(qh ferr, 6149, "qhull internal error (qh_infiniteloop): potential infinite loop detected\n"); + qh_errexit(qh_ERRqhull, facet, NULL); +} /* qh_infiniteloop */ + +/*-<a href="qh-poly.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') + build initial simplex + partition input points into facets of initial simplex + set up lists + if qh.ONLYgood + check consistency + add qh.GOODvertex if defined +*/ +void qh_initbuild( void) { + setT *maxpoints, *vertices; + facetT *facet; + int i, numpart; + realT dist; + boolT isoutside; + + qh furthest_id= qh_IDunknown; + qh lastreport= 0; + qh facet_id= qh vertex_id= qh ridge_id= 0; + qh visit_id= qh vertex_visit= 0; + qh maxoutdone= False; + + if (qh GOODpoint > 0) + qh GOODpointp= qh_point(qh GOODpoint-1); + else if (qh GOODpoint < 0) + qh GOODpointp= qh_point(-qh GOODpoint-1); + if (qh GOODvertex > 0) + qh GOODvertexp= qh_point(qh GOODvertex-1); + else if (qh GOODvertex < 0) + qh GOODvertexp= qh_point(-qh GOODvertex-1); + if ((qh GOODpoint + && (qh GOODpointp < qh first_point /* also catches !GOODpointp */ + || qh GOODpointp > qh_point(qh num_points-1))) + || (qh GOODvertex + && (qh GOODvertexp < qh first_point /* also catches !GOODvertexp */ + || qh GOODvertexp > qh_point(qh num_points-1)))) { + qh_fprintf(qh ferr, 6150, "qhull input error: either QGn or QVn point is > p%d\n", + qh num_points-1); + qh_errexit(qh_ERRinput, NULL, NULL); + } + maxpoints= qh_maxmin(qh first_point, qh num_points, qh hull_dim); + if (qh SCALElast) + qh_scalelast(qh first_point, qh num_points, qh hull_dim, + qh MINlastcoord, qh MAXlastcoord, qh MAXwidth); + qh_detroundoff(); + if (qh DELAUNAY && qh upper_threshold[qh hull_dim-1] > REALmax/2 + && qh lower_threshold[qh hull_dim-1] < -REALmax/2) { + for (i=qh_PRINTEND; i--; ) { + if (qh PRINTout[i] == qh_PRINTgeom && qh DROPdim < 0 + && !qh GOODthreshold && !qh SPLITthresholds) + break; /* in this case, don't set upper_threshold */ + } + if (i < 0) { + if (qh UPPERdelaunay) { /* matches qh.upperdelaunay in qh_setfacetplane */ + qh lower_threshold[qh hull_dim-1]= qh ANGLEround * qh_ZEROdelaunay; + qh GOODthreshold= True; + }else { + qh upper_threshold[qh hull_dim-1]= -qh ANGLEround * qh_ZEROdelaunay; + if (!qh GOODthreshold) + qh SPLITthresholds= True; /* build upper-convex hull even if Qg */ + /* qh_initqhull_globals errors if Qg without Pdk/etc. */ + } + } + } + vertices= qh_initialvertices(qh hull_dim, maxpoints, qh first_point, qh num_points); + qh_initialhull(vertices); /* initial qh facet_list */ + qh_partitionall(vertices, qh first_point, qh num_points); + if (qh PRINToptions1st || qh TRACElevel || qh IStracing) { + if (qh TRACElevel || qh IStracing) + qh_fprintf(qh ferr, 8103, "\nTrace level %d for %s | %s\n", + qh IStracing ? qh IStracing : qh TRACElevel, qh rbox_command, qh qhull_command); + qh_fprintf(qh ferr, 8104, "Options selected for Qhull %s:\n%s\n", qh_version, qh qhull_options); + } + qh_resetlists(False, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */); + qh facet_next= qh facet_list; + qh_furthestnext(/* qh.facet_list */); + if (qh PREmerge) { + qh cos_max= qh premerge_cos; + qh centrum_radius= qh premerge_centrum; + } + if (qh ONLYgood) { + if (qh GOODvertex > 0 && qh MERGING) { + qh_fprintf(qh ferr, 6151, "qhull input error: 'Qg QVn' (only good vertex) does not work with merging.\nUse 'QJ' to joggle the input or 'Q0' to turn off merging.\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } + if (!(qh GOODthreshold || qh GOODpoint + || (!qh MERGEexact && !qh PREmerge && qh GOODvertexp))) { + qh_fprintf(qh ferr, 6152, "qhull input error: 'Qg' (ONLYgood) needs a good threshold('Pd0D0'), a\n\ +good point(QGn or QG-n), or a good vertex with 'QJ' or 'Q0' (QVn).\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } + if (qh GOODvertex > 0 && !qh MERGING /* matches qh_partitionall */ + && !qh_isvertex(qh GOODvertexp, vertices)) { + facet= qh_findbestnew(qh GOODvertexp, qh facet_list, + &dist, !qh_ALL, &isoutside, &numpart); + zadd_(Zdistgood, numpart); + if (!isoutside) { + qh_fprintf(qh ferr, 6153, "qhull input error: point for QV%d is inside initial simplex. It can not be made a vertex.\n", + qh_pointid(qh GOODvertexp)); + qh_errexit(qh_ERRinput, NULL, NULL); + } + if (!qh_addpoint(qh GOODvertexp, facet, False)) { + qh_settempfree(&vertices); + qh_settempfree(&maxpoints); + return; + } + } + qh_findgood(qh facet_list, 0); + } + qh_settempfree(&vertices); + qh_settempfree(&maxpoints); + trace1((qh ferr, 1030, "qh_initbuild: initial hull created and points partitioned\n")); +} /* initbuild */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="initialhull">-</a> + + qh_initialhull( vertices ) + constructs the initial hull as a DIM3 simplex of vertices + + design: + creates a simplex (initializes lists) + determines orientation of simplex + sets hyperplanes for facets + doubles checks orientation (in case of axis-parallel facets with Gaussian elimination) + checks for flipped facets and qh.NARROWhull + checks the result +*/ +void qh_initialhull(setT *vertices) { + facetT *facet, *firstfacet, *neighbor, **neighborp; + realT dist, angle, minangle= REALmax; +#ifndef qh_NOtrace + int k; +#endif + + qh_createsimplex(vertices); /* qh.facet_list */ + qh_resetlists(False, qh_RESETvisible); + qh facet_next= qh facet_list; /* advance facet when processed */ + qh interior_point= qh_getcenter(vertices); + firstfacet= qh facet_list; + qh_setfacetplane(firstfacet); + zinc_(Znumvisibility); /* needs to be in printsummary */ + qh_distplane(qh interior_point, firstfacet, &dist); + if (dist > 0) { + FORALLfacets + facet->toporient ^= (unsigned char)True; + } + FORALLfacets + qh_setfacetplane(facet); + FORALLfacets { + if (!qh_checkflipped(facet, NULL, qh_ALL)) {/* due to axis-parallel facet */ + trace1((qh ferr, 1031, "qh_initialhull: initial orientation incorrect. Correct all facets\n")); + facet->flipped= False; + FORALLfacets { + facet->toporient ^= (unsigned char)True; + qh_orientoutside(facet); + } + break; + } + } + FORALLfacets { + if (!qh_checkflipped(facet, NULL, !qh_ALL)) { /* can happen with 'R0.1' */ + if (qh DELAUNAY && ! qh ATinfinity) { + if (qh UPPERdelaunay) + qh_fprintf(qh ferr, 6240, "Qhull precision error: Initial simplex is cocircular or cospherical. Option 'Qs' searches all points. Can not compute the upper Delaunay triangulation or upper Voronoi diagram of cocircular/cospherical points.\n"); + else + qh_fprintf(qh ferr, 6239, "Qhull precision error: Initial simplex is cocircular or cospherical. Use option 'Qz' for the Delaunay triangulation or Voronoi diagram of cocircular/cospherical points. Option 'Qz' adds a point \"at infinity\". Use option 'Qs' to search all points for the initial simplex.\n"); + qh_errexit(qh_ERRinput, NULL, NULL); + } + qh_precision("initial simplex is flat"); + qh_fprintf(qh ferr, 6154, "Qhull precision error: Initial simplex is flat (facet %d is coplanar with the interior point)\n", + facet->id); + qh_errexit(qh_ERRsingular, NULL, NULL); /* calls qh_printhelp_singular */ + } + FOREACHneighbor_(facet) { + angle= qh_getangle(facet->normal, neighbor->normal); + minimize_( minangle, angle); + } + } + if (minangle < qh_MAXnarrow && !qh NOnarrow) { + realT diff= 1.0 + minangle; + + qh NARROWhull= True; + qh_option("_narrow-hull", NULL, &diff); + if (minangle < qh_WARNnarrow && !qh RERUN && qh PRINTprecision) + qh_printhelp_narrowhull(qh ferr, minangle); + } + zzval_(Zprocessed)= qh hull_dim+1; + qh_checkpolygon(qh facet_list); + qh_checkconvex(qh facet_list, qh_DATAfault); +#ifndef qh_NOtrace + if (qh IStracing >= 1) { + qh_fprintf(qh ferr, 8105, "qh_initialhull: simplex constructed, interior point:"); + for (k=0; k < qh hull_dim; k++) + qh_fprintf(qh ferr, 8106, " %6.4g", qh interior_point[k]); + qh_fprintf(qh ferr, 8107, "\n"); + } +#endif +} /* initialhull */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="initialvertices">-</a> + + qh_initialvertices( dim, maxpoints, points, numpoints ) + determines a non-singular set of initial vertices + maxpoints may include duplicate points + + returns: + temporary set of dim+1 vertices in descending order by vertex id + if qh.RANDOMoutside && !qh.ALLpoints + picks random points + if dim >= qh_INITIALmax, + uses min/max x and max points with non-zero determinants + + notes: + unless qh.ALLpoints, + uses maxpoints as long as determinate is non-zero +*/ +setT *qh_initialvertices(int dim, setT *maxpoints, pointT *points, int numpoints) { + pointT *point, **pointp; + setT *vertices, *simplex, *tested; + realT randr; + int idx, point_i, point_n, k; + boolT nearzero= False; + + vertices= qh_settemp(dim + 1); + simplex= qh_settemp(dim+1); + if (qh ALLpoints) + qh_maxsimplex(dim, NULL, points, numpoints, &simplex); + else if (qh RANDOMoutside) { + while (qh_setsize(simplex) != dim+1) { + randr= qh_RANDOMint; + randr= randr/(qh_RANDOMmax+1); + idx= (int)floor(qh num_points * randr); + while (qh_setin(simplex, qh_point(idx))) { + idx++; /* in case qh_RANDOMint always returns the same value */ + idx= idx < qh num_points ? idx : 0; + } + qh_setappend(&simplex, qh_point(idx)); + } + }else if (qh hull_dim >= qh_INITIALmax) { + tested= qh_settemp(dim+1); + qh_setappend(&simplex, SETfirst_(maxpoints)); /* max and min X coord */ + qh_setappend(&simplex, SETsecond_(maxpoints)); + qh_maxsimplex(fmin_(qh_INITIALsearch, dim), maxpoints, points, numpoints, &simplex); + k= qh_setsize(simplex); + FOREACHpoint_i_(maxpoints) { + if (point_i & 0x1) { /* first pick up max. coord. points */ + if (!qh_setin(simplex, point) && !qh_setin(tested, point)){ + qh_detsimplex(point, simplex, k, &nearzero); + if (nearzero) + qh_setappend(&tested, point); + else { + qh_setappend(&simplex, point); + if (++k == dim) /* use search for last point */ + break; + } + } + } + } + while (k != dim && (point= (pointT*)qh_setdellast(maxpoints))) { + if (!qh_setin(simplex, point) && !qh_setin(tested, point)){ + qh_detsimplex(point, simplex, k, &nearzero); + if (nearzero) + qh_setappend(&tested, point); + else { + qh_setappend(&simplex, point); + k++; + } + } + } + idx= 0; + while (k != dim && (point= qh_point(idx++))) { + if (!qh_setin(simplex, point) && !qh_setin(tested, point)){ + qh_detsimplex(point, simplex, k, &nearzero); + if (!nearzero){ + qh_setappend(&simplex, point); + k++; + } + } + } + qh_settempfree(&tested); + qh_maxsimplex(dim, maxpoints, points, numpoints, &simplex); + }else + qh_maxsimplex(dim, maxpoints, points, numpoints, &simplex); + FOREACHpoint_(simplex) + qh_setaddnth(&vertices, 0, qh_newvertex(point)); /* descending order */ + qh_settempfree(&simplex); + return vertices; +} /* initialvertices */ + + +/*-<a href="qh-poly.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.htm#TOC" + >-------------------------------</a><a name="makenewfacets">-</a> + + qh_makenewfacets( point ) + make new facets from point and qh.visible_list + + returns: + qh.newfacet_list= list of new facets with hyperplanes and ->newfacet + qh.newvertex_list= list of vertices in new facets with ->newlist set + + if (qh.ONLYgood) + newfacets reference horizon facets, but not vice versa + ridges reference non-simplicial horizon ridges, but not vice versa + does not change existing facets + else + sets qh.NEWfacets + new facets attached to horizon facets and ridges + for visible facets, + visible->r.replace is corresponding new facet + + see also: + qh_makenewplanes() -- make hyperplanes for facets + qh_attachnewfacets() -- attachnewfacets if not done here(qh ONLYgood) + qh_matchnewfacets() -- match up neighbors + qh_updatevertices() -- update vertex neighbors and delvertices + qh_deletevisible() -- delete visible facets + qh_checkpolygon() --check the result + qh_triangulate() -- triangulate a non-simplicial facet + + design: + for each visible facet + make new facets to its horizon facets + update its f.replace + clear its neighbor set +*/ +vertexT *qh_makenewfacets(pointT *point /*visible_list*/) { + facetT *visible, *newfacet= NULL, *newfacet2= NULL, *neighbor, **neighborp; + vertexT *apex; + int numnew=0; + + qh newfacet_list= qh facet_tail; + qh newvertex_list= qh vertex_tail; + apex= qh_newvertex(point); + qh_appendvertex(apex); + qh visit_id++; + if (!qh ONLYgood) + qh NEWfacets= True; + FORALLvisible_facets { + FOREACHneighbor_(visible) + neighbor->seen= False; + if (visible->ridges) { + visible->visitid= qh visit_id; + newfacet2= qh_makenew_nonsimplicial(visible, apex, &numnew); + } + if (visible->simplicial) + newfacet= qh_makenew_simplicial(visible, apex, &numnew); + if (!qh ONLYgood) { + if (newfacet2) /* newfacet is null if all ridges defined */ + newfacet= newfacet2; + if (newfacet) + visible->f.replace= newfacet; + else + zinc_(Zinsidevisible); + SETfirst_(visible->neighbors)= NULL; + } + } + trace1((qh ferr, 1032, "qh_makenewfacets: created %d new facets from point p%d to horizon\n", + numnew, qh_pointid(point))); + if (qh IStracing >= 4) + qh_printfacetlist(qh newfacet_list, NULL, qh_ALL); + return apex; +} /* makenewfacets */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="matchduplicates">-</a> + + qh_matchduplicates( atfacet, atskip, hashsize, hashcount ) + match duplicate ridges in qh.hash_table for atfacet/atskip + duplicates marked with ->dupridge and qh_DUPLICATEridge + + returns: + picks match with worst merge (min distance apart) + updates hashcount + + see also: + qh_matchneighbor + + notes: + + design: + compute hash value for atfacet and atskip + repeat twice -- once to make best matches, once to match the rest + for each possible facet in qh.hash_table + if it is a matching facet and pass 2 + make match + unless tricoplanar, mark match for merging (qh_MERGEridge) + [e.g., tricoplanar RBOX s 1000 t993602376 | QHULL C-1e-3 d Qbb FA Qt] + if it is a matching facet and pass 1 + test if this is a better match + if pass 1, + make best match (it will not be merged) +*/ +#ifndef qh_NOmerge +void qh_matchduplicates(facetT *atfacet, int atskip, int hashsize, int *hashcount) { + boolT same, ismatch; + int hash, scan; + facetT *facet, *newfacet, *maxmatch= NULL, *maxmatch2= NULL, *nextfacet; + int skip, newskip, nextskip= 0, maxskip= 0, maxskip2= 0, makematch; + realT maxdist= -REALmax, mindist, dist2, low, high; + + hash= qh_gethash(hashsize, atfacet->vertices, qh hull_dim, 1, + SETelem_(atfacet->vertices, atskip)); + trace2((qh ferr, 2046, "qh_matchduplicates: find duplicate matches for f%d skip %d hash %d hashcount %d\n", + atfacet->id, atskip, hash, *hashcount)); + for (makematch= 0; makematch < 2; makematch++) { + qh visit_id++; + for (newfacet= atfacet, newskip= atskip; newfacet; newfacet= nextfacet, newskip= nextskip) { + zinc_(Zhashlookup); + nextfacet= NULL; + newfacet->visitid= qh visit_id; + for (scan= hash; (facet= SETelemt_(qh hash_table, scan, facetT)); + scan= (++scan >= hashsize ? 0 : scan)) { + if (!facet->dupridge || facet->visitid == qh visit_id) + continue; + zinc_(Zhashtests); + if (qh_matchvertices(1, newfacet->vertices, newskip, facet->vertices, &skip, &same)) { + ismatch= (same == (boolT)(newfacet->toporient ^ facet->toporient)); + if (SETelemt_(facet->neighbors, skip, facetT) != qh_DUPLICATEridge) { + if (!makematch) { + qh_fprintf(qh ferr, 6155, "qhull internal error (qh_matchduplicates): missing dupridge at f%d skip %d for new f%d skip %d hash %d\n", + facet->id, skip, newfacet->id, newskip, hash); + qh_errexit2(qh_ERRqhull, facet, newfacet); + } + }else if (ismatch && makematch) { + if (SETelemt_(newfacet->neighbors, newskip, facetT) == qh_DUPLICATEridge) { + SETelem_(facet->neighbors, skip)= newfacet; + if (newfacet->tricoplanar) + SETelem_(newfacet->neighbors, newskip)= facet; + else + SETelem_(newfacet->neighbors, newskip)= qh_MERGEridge; + *hashcount -= 2; /* removed two unmatched facets */ + trace4((qh ferr, 4059, "qh_matchduplicates: duplicate f%d skip %d matched with new f%d skip %d merge\n", + facet->id, skip, newfacet->id, newskip)); + } + }else if (ismatch) { + mindist= qh_getdistance(facet, newfacet, &low, &high); + dist2= qh_getdistance(newfacet, facet, &low, &high); + minimize_(mindist, dist2); + if (mindist > maxdist) { + maxdist= mindist; + maxmatch= facet; + maxskip= skip; + maxmatch2= newfacet; + maxskip2= newskip; + } + trace3((qh ferr, 3018, "qh_matchduplicates: duplicate f%d skip %d new f%d skip %d at dist %2.2g, max is now f%d f%d\n", + facet->id, skip, newfacet->id, newskip, mindist, + maxmatch->id, maxmatch2->id)); + }else { /* !ismatch */ + nextfacet= facet; + nextskip= skip; + } + } + if (makematch && !facet + && SETelemt_(facet->neighbors, skip, facetT) == qh_DUPLICATEridge) { + qh_fprintf(qh ferr, 6156, "qhull internal error (qh_matchduplicates): no MERGEridge match for duplicate f%d skip %d at hash %d\n", + newfacet->id, newskip, hash); + qh_errexit(qh_ERRqhull, newfacet, NULL); + } + } + } /* end of for each new facet at hash */ + if (!makematch) { + if (!maxmatch) { + qh_fprintf(qh ferr, 6157, "qhull internal error (qh_matchduplicates): no maximum match at duplicate f%d skip %d at hash %d\n", + atfacet->id, atskip, hash); + qh_errexit(qh_ERRqhull, atfacet, NULL); + } + SETelem_(maxmatch->neighbors, maxskip)= maxmatch2; /* maxmatch!=0 by QH6157 */ + SETelem_(maxmatch2->neighbors, maxskip2)= maxmatch; + *hashcount -= 2; /* removed two unmatched facets */ + zzinc_(Zmultiridge); + trace0((qh ferr, 25, "qh_matchduplicates: duplicate f%d skip %d matched with new f%d skip %d keep\n", + maxmatch->id, maxskip, maxmatch2->id, maxskip2)); + qh_precision("ridge with multiple neighbors"); + if (qh IStracing >= 4) + qh_errprint("DUPLICATED/MATCH", maxmatch, maxmatch2, NULL, NULL); + } + } +} /* matchduplicates */ + +/*-<a href="qh-poly.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(void /* qh.facet_list */) { + facetT *facet; + pointT *point, **pointp; + int numpart; + realT dist, innerplane; + + if (!qh KEEPcoplanar && !qh KEEPinside) { + FORALLfacets { + if (facet->coplanarset) + qh_setfree( &facet->coplanarset); + } + }else if (!qh KEEPcoplanar || !qh KEEPinside) { + qh_outerinner(NULL, NULL, &innerplane); + if (qh JOGGLEmax < REALmax/2) + innerplane -= qh JOGGLEmax * sqrt((realT)qh hull_dim); + numpart= 0; + FORALLfacets { + if (facet->coplanarset) { + FOREACHpoint_(facet->coplanarset) { + numpart++; + qh_distplane(point, facet, &dist); + if (dist < innerplane) { + if (!qh KEEPinside) + SETref_(point)= NULL; + }else if (!qh KEEPcoplanar) + SETref_(point)= NULL; + } + qh_setcompact(facet->coplanarset); + } + } + zzadd_(Zcheckpart, numpart); + } +} /* nearcoplanar */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="nearvertex">-</a> + + qh_nearvertex( facet, point, bestdist ) + return nearest vertex in facet to point + + returns: + vertex and its distance + + notes: + if qh.DELAUNAY + distance is measured in the input set + searches neighboring tricoplanar facets (requires vertexneighbors) + Slow implementation. Recomputes vertex set for each point. + The vertex set could be stored in the qh.keepcentrum facet. +*/ +vertexT *qh_nearvertex(facetT *facet, pointT *point, realT *bestdistp) { + realT bestdist= REALmax, dist; + vertexT *bestvertex= NULL, *vertex, **vertexp, *apex; + coordT *center; + facetT *neighbor, **neighborp; + setT *vertices; + int dim= qh hull_dim; + + if (qh DELAUNAY) + dim--; + if (facet->tricoplanar) { + if (!qh VERTEXneighbors || !facet->center) { + qh_fprintf(qh ferr, 6158, "qhull internal error (qh_nearvertex): qh.VERTEXneighbors and facet->center required for tricoplanar facets\n"); + qh_errexit(qh_ERRqhull, facet, NULL); + } + vertices= qh_settemp(qh TEMPsize); + apex= SETfirstt_(facet->vertices, vertexT); + center= facet->center; + FOREACHneighbor_(apex) { + if (neighbor->center == center) { + FOREACHvertex_(neighbor->vertices) + qh_setappend(&vertices, vertex); + } + } + }else + vertices= facet->vertices; + FOREACHvertex_(vertices) { + dist= qh_pointdist(vertex->point, point, -dim); + if (dist < bestdist) { + bestdist= dist; + bestvertex= vertex; + } + } + if (facet->tricoplanar) + qh_settempfree(&vertices); + *bestdistp= sqrt(bestdist); + if (!bestvertex) { + qh_fprintf(qh ferr, 6261, "qhull internal error (qh_nearvertex): did not find bestvertex for f%d p%d\n", facet->id, qh_pointid(point)); + qh_errexit(qh_ERRqhull, facet, NULL); + } + trace3((qh ferr, 3019, "qh_nearvertex: v%d dist %2.2g for f%d p%d\n", + bestvertex->id, *bestdistp, facet->id, qh_pointid(point))); /* bestvertex!=0 by QH2161 */ + return bestvertex; +} /* nearvertex */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="newhashtable">-</a> + + qh_newhashtable( newsize ) + returns size of qh.hash_table of at least newsize slots + + notes: + assumes qh.hash_table is NULL + qh_HASHfactor determines the number of extra slots + size is not divisible by 2, 3, or 5 +*/ +int qh_newhashtable(int newsize) { + int size; + + size= ((newsize+1)*qh_HASHfactor) | 0x1; /* odd number */ + while (True) { + if (newsize<0 || size<0) { + qh_fprintf(qhmem.ferr, 6236, "qhull error (qh_newhashtable): negative request (%d) or size (%d). Did int overflow due to high-D?\n", newsize, size); /* WARN64 */ + qh_errexit(qhmem_ERRmem, NULL, NULL); + } + if ((size%3) && (size%5)) + break; + size += 2; + /* loop terminates because there is an infinite number of primes */ + } + qh hash_table= qh_setnew(size); + qh_setzero(qh hash_table, 0, size); + return size; +} /* newhashtable */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="newvertex">-</a> + + qh_newvertex( point ) + returns a new vertex for point +*/ +vertexT *qh_newvertex(pointT *point) { + vertexT *vertex; + + zinc_(Ztotvertices); + vertex= (vertexT *)qh_memalloc((int)sizeof(vertexT)); + memset((char *) vertex, (size_t)0, sizeof(vertexT)); + if (qh vertex_id == UINT_MAX) { + qh_memfree(vertex, (int)sizeof(vertexT)); + qh_fprintf(qh ferr, 6159, "qhull error: more than 2^32 vertices. vertexT.id field overflows. Vertices would not be sorted correctly.\n"); + qh_errexit(qh_ERRqhull, NULL, NULL); + } + if (qh vertex_id == qh tracevertex_id) + qh tracevertex= vertex; + vertex->id= qh vertex_id++; + vertex->point= point; + trace4((qh ferr, 4060, "qh_newvertex: vertex p%d(v%d) created\n", qh_pointid(vertex->point), + vertex->id)); + return(vertex); +} /* newvertex */ + +/*-<a href="qh-poly.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 qh_qh. + + notes: + in qh_ORIENTclock order + this is a O(n^2) implementation to trace all ridges + be sure to stop on any 2nd visit + same as QhullRidge::nextRidge3d + does not use qh_qh or qh_errexit [QhullFacet.cpp] + + design: + for each ridge + exit if it is the ridge after atridge +*/ +ridgeT *qh_nextridge3d(ridgeT *atridge, facetT *facet, vertexT **vertexp) { + vertexT *atvertex, *vertex, *othervertex; + ridgeT *ridge, **ridgep; + + if ((atridge->top == facet) ^ qh_ORIENTclock) + atvertex= SETsecondt_(atridge->vertices, vertexT); + else + atvertex= SETfirstt_(atridge->vertices, vertexT); + FOREACHridge_(facet->ridges) { + if (ridge == atridge) + continue; + if ((ridge->top == facet) ^ qh_ORIENTclock) { + othervertex= SETsecondt_(ridge->vertices, vertexT); + vertex= SETfirstt_(ridge->vertices, vertexT); + }else { + vertex= SETsecondt_(ridge->vertices, vertexT); + othervertex= SETfirstt_(ridge->vertices, vertexT); + } + if (vertex == atvertex) { + if (vertexp) + *vertexp= othervertex; + return ridge; + } + } + return NULL; +} /* nextridge3d */ +#else /* qh_NOmerge */ +void qh_matchduplicates(facetT *atfacet, int atskip, int hashsize, int *hashcount) { +} +ridgeT *qh_nextridge3d(ridgeT *atridge, facetT *facet, vertexT **vertexp) { + + return NULL; +} +#endif /* qh_NOmerge */ + +/*-<a href="qh-poly.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(void /* facet_list */) { + pointT *point, **pointp; + facetT *facet; + realT dist; + + trace1((qh ferr, 1033, "qh_outcoplanar: move outsideset to coplanarset for qh NARROWhull\n")); + FORALLfacets { + FOREACHpoint_(facet->outsideset) { + qh num_outside--; + if (qh KEEPcoplanar || qh KEEPnearinside) { + qh_distplane(point, facet, &dist); + zinc_(Zpartition); + qh_partitioncoplanar(point, facet, &dist); + } + } + qh_setfree(&facet->outsideset); + } +} /* outcoplanar */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="point">-</a> + + qh_point( id ) + return point for a point id, or NULL if unknown + + alternative code: + return((pointT *)((unsigned long)qh.first_point + + (unsigned long)((id)*qh.normal_size))); +*/ +pointT *qh_point(int id) { + + if (id < 0) + return NULL; + if (id < qh num_points) + return qh first_point + id * qh hull_dim; + id -= qh num_points; + if (id < qh_setsize(qh other_points)) + return SETelemt_(qh other_points, id, pointT); + return NULL; +} /* point */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="point_add">-</a> + + qh_point_add( set, point, elem ) + stores elem at set[point.id] + + returns: + access function for qh_pointfacet and qh_pointvertex + + notes: + checks point.id +*/ +void qh_point_add(setT *set, pointT *point, void *elem) { + int id, size; + + SETreturnsize_(set, size); + if ((id= qh_pointid(point)) < 0) + qh_fprintf(qh ferr, 7067, "qhull internal warning (point_add): unknown point %p id %d\n", + point, id); + else if (id >= size) { + qh_fprintf(qh ferr, 6160, "qhull internal errror(point_add): point p%d is out of bounds(%d)\n", + id, size); + qh_errexit(qh_ERRqhull, NULL, NULL); + }else + SETelem_(set, id)= elem; +} /* point_add */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="pointfacet">-</a> + + qh_pointfacet() + return temporary set of facet for each point + the set is indexed by point id + + notes: + vertices assigned to one of the facets + coplanarset assigned to the facet + outside set assigned to the facet + NULL if no facet for point (inside) + includes qh.GOODpointp + + access: + FOREACHfacet_i_(facets) { ... } + SETelem_(facets, i) + + design: + for each facet + add each vertex + add each coplanar point + add each outside point +*/ +setT *qh_pointfacet(void /*qh.facet_list*/) { + int numpoints= qh num_points + qh_setsize(qh other_points); + setT *facets; + facetT *facet; + vertexT *vertex, **vertexp; + pointT *point, **pointp; + + facets= qh_settemp(numpoints); + qh_setzero(facets, 0, numpoints); + qh vertex_visit++; + FORALLfacets { + FOREACHvertex_(facet->vertices) { + if (vertex->visitid != qh vertex_visit) { + vertex->visitid= qh vertex_visit; + qh_point_add(facets, vertex->point, facet); + } + } + FOREACHpoint_(facet->coplanarset) + qh_point_add(facets, point, facet); + FOREACHpoint_(facet->outsideset) + qh_point_add(facets, point, facet); + } + return facets; +} /* pointfacet */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="pointvertex">-</a> + + qh_pointvertex( ) + return temporary set of vertices indexed by point id + entry is NULL if no vertex for a point + this will include qh.GOODpointp + + access: + FOREACHvertex_i_(vertices) { ... } + SETelem_(vertices, i) +*/ +setT *qh_pointvertex(void /*qh.facet_list*/) { + int numpoints= qh num_points + qh_setsize(qh other_points); + setT *vertices; + vertexT *vertex; + + vertices= qh_settemp(numpoints); + qh_setzero(vertices, 0, numpoints); + FORALLvertices + qh_point_add(vertices, vertex->point, vertex); + return vertices; +} /* pointvertex */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="prependfacet">-</a> + + qh_prependfacet( facet, facetlist ) + prepend facet to the start of a facetlist + + returns: + increments qh.numfacets + updates facetlist, qh.facet_list, facet_next + + notes: + be careful of prepending since it can lose a pointer. + e.g., can lose _next by deleting and then prepending before _next +*/ +void qh_prependfacet(facetT *facet, facetT **facetlist) { + facetT *prevfacet, *list; + + + trace4((qh ferr, 4061, "qh_prependfacet: prepend f%d before f%d\n", + facet->id, getid_(*facetlist))); + if (!*facetlist) + (*facetlist)= qh facet_tail; + list= *facetlist; + prevfacet= list->previous; + facet->previous= prevfacet; + if (prevfacet) + prevfacet->next= facet; + list->previous= facet; + facet->next= *facetlist; + if (qh facet_list == list) /* this may change *facetlist */ + qh facet_list= facet; + if (qh facet_next == list) + qh facet_next= facet; + *facetlist= facet; + qh num_facets++; +} /* prependfacet */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="printhashtable">-</a> + + qh_printhashtable( fp ) + print hash table to fp + + notes: + not in I/O to avoid bringing io.c in + + design: + for each hash entry + if defined + if unmatched or will merge (NULL, qh_MERGEridge, qh_DUPLICATEridge) + print entry and neighbors +*/ +void qh_printhashtable(FILE *fp) { + facetT *facet, *neighbor; + int id, facet_i, facet_n, neighbor_i= 0, neighbor_n= 0; + vertexT *vertex, **vertexp; + + FOREACHfacet_i_(qh hash_table) { + if (facet) { + FOREACHneighbor_i_(facet) { + if (!neighbor || neighbor == qh_MERGEridge || neighbor == qh_DUPLICATEridge) + break; + } + if (neighbor_i == neighbor_n) + continue; + qh_fprintf(fp, 9283, "hash %d f%d ", facet_i, facet->id); + FOREACHvertex_(facet->vertices) + qh_fprintf(fp, 9284, "v%d ", vertex->id); + qh_fprintf(fp, 9285, "\n neighbors:"); + FOREACHneighbor_i_(facet) { + if (neighbor == qh_MERGEridge) + id= -3; + else if (neighbor == qh_DUPLICATEridge) + id= -2; + else + id= getid_(neighbor); + qh_fprintf(fp, 9286, " %d", id); + } + qh_fprintf(fp, 9287, "\n"); + } + } +} /* printhashtable */ + + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="printlists">-</a> + + qh_printlists( fp ) + print out facet and vertex list for debugging (without 'f/v' tags) +*/ +void qh_printlists(void) { + facetT *facet; + vertexT *vertex; + int count= 0; + + qh_fprintf(qh ferr, 8108, "qh_printlists: facets:"); + FORALLfacets { + if (++count % 100 == 0) + qh_fprintf(qh ferr, 8109, "\n "); + qh_fprintf(qh ferr, 8110, " %d", facet->id); + } + qh_fprintf(qh ferr, 8111, "\n new facets %d visible facets %d next facet for qh_addpoint %d\n vertices(new %d):", + getid_(qh newfacet_list), getid_(qh visible_list), getid_(qh facet_next), + getid_(qh newvertex_list)); + count = 0; + FORALLvertices { + if (++count % 100 == 0) + qh_fprintf(qh ferr, 8112, "\n "); + qh_fprintf(qh ferr, 8113, " %d", vertex->id); + } + qh_fprintf(qh ferr, 8114, "\n"); +} /* printlists */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="resetlists">-</a> + + qh_resetlists( stats, qh_RESETvisible ) + reset newvertex_list, newfacet_list, visible_list + if stats, + maintains statistics + + returns: + visible_list is empty if qh_deletevisible was called +*/ +void qh_resetlists(boolT stats, boolT resetVisible /*qh.newvertex_list newfacet_list visible_list*/) { + vertexT *vertex; + facetT *newfacet, *visible; + int totnew=0, totver=0; + + if (stats) { + FORALLvertex_(qh newvertex_list) + totver++; + FORALLnew_facets + totnew++; + zadd_(Zvisvertextot, totver); + zmax_(Zvisvertexmax, totver); + zadd_(Znewfacettot, totnew); + zmax_(Znewfacetmax, totnew); + } + FORALLvertex_(qh newvertex_list) + vertex->newlist= False; + qh newvertex_list= NULL; + FORALLnew_facets + newfacet->newfacet= False; + qh newfacet_list= NULL; + if (resetVisible) { + FORALLvisible_facets { + visible->f.replace= NULL; + visible->visible= False; + } + qh num_visible= 0; + } + qh visible_list= NULL; /* may still have visible facets via qh_triangulate */ + qh NEWfacets= False; +} /* resetlists */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="setvoronoi_all">-</a> + + qh_setvoronoi_all() + compute Voronoi centers for all facets + includes upperDelaunay facets if qh.UPPERdelaunay ('Qu') + + returns: + facet->center is the Voronoi center + + notes: + this is unused/untested code + please email bradb@shore.net if this works ok for you + + use: + FORALLvertices {...} to locate the vertex for a point. + FOREACHneighbor_(vertex) {...} to visit the Voronoi centers for a Voronoi cell. +*/ +void qh_setvoronoi_all(void) { + facetT *facet; + + qh_clearcenters(qh_ASvoronoi); + qh_vertexneighbors(); + + FORALLfacets { + if (!facet->normal || !facet->upperdelaunay || qh UPPERdelaunay) { + if (!facet->center) + facet->center= qh_facetcenter(facet->vertices); + } + } +} /* setvoronoi_all */ + +#ifndef qh_NOmerge + +/*-<a href="qh-poly.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. + + notes: + call after qh_check_output since may switch to Voronoi centers + Output may overwrite ->f.triowner with ->f.area +*/ +void qh_triangulate(void /*qh.facet_list*/) { + facetT *facet, *nextfacet, *owner; + int onlygood= qh ONLYgood; + facetT *neighbor, *visible= NULL, *facet1, *facet2, *new_facet_list= NULL; + facetT *orig_neighbor= NULL, *otherfacet; + vertexT *new_vertex_list= NULL; + mergeT *merge; + mergeType mergetype; + int neighbor_i, neighbor_n; + + if (qh hasTriangulation) + return; + trace1((qh ferr, 1034, "qh_triangulate: triangulate non-simplicial facets\n")); + if (qh hull_dim == 2) + return; + if (qh VORONOI) { /* otherwise lose Voronoi centers [could rebuild vertex set from tricoplanar] */ + qh_clearcenters(qh_ASvoronoi); + qh_vertexneighbors(); + } + qh ONLYgood= False; /* for makenew_nonsimplicial */ + qh visit_id++; + qh NEWfacets= True; + qh degen_mergeset= qh_settemp(qh TEMPsize); + qh newvertex_list= qh vertex_tail; + for (facet= qh facet_list; facet && facet->next; facet= nextfacet) { /* non-simplicial facets moved to end */ + nextfacet= facet->next; + if (facet->visible || facet->simplicial) + continue; + /* triangulate all non-simplicial facets, otherwise merging does not work, e.g., RBOX c P-0.1 P+0.1 P+0.1 D3 | QHULL d Qt Tv */ + if (!new_facet_list) + new_facet_list= facet; /* will be moved to end */ + qh_triangulate_facet(facet, &new_vertex_list); + } + trace2((qh ferr, 2047, "qh_triangulate: delete null facets from f%d -- apex same as second vertex\n", getid_(new_facet_list))); + for (facet= new_facet_list; facet && facet->next; facet= nextfacet) { /* null facets moved to end */ + nextfacet= facet->next; + if (facet->visible) + continue; + if (facet->ridges) { + if (qh_setsize(facet->ridges) > 0) { + qh_fprintf(qh ferr, 6161, "qhull error (qh_triangulate): ridges still defined for f%d\n", facet->id); + qh_errexit(qh_ERRqhull, facet, NULL); + } + qh_setfree(&facet->ridges); + } + if (SETfirst_(facet->vertices) == SETsecond_(facet->vertices)) { + zinc_(Ztrinull); + qh_triangulate_null(facet); + } + } + trace2((qh ferr, 2048, "qh_triangulate: delete %d or more mirror facets -- same vertices and neighbors\n", qh_setsize(qh degen_mergeset))); + qh visible_list= qh facet_tail; + while ((merge= (mergeT*)qh_setdellast(qh degen_mergeset))) { + facet1= merge->facet1; + facet2= merge->facet2; + mergetype= merge->type; + qh_memfree(merge, (int)sizeof(mergeT)); + if (mergetype == MRGmirror) { + zinc_(Ztrimirror); + qh_triangulate_mirror(facet1, facet2); + } + } + qh_settempfree(&qh degen_mergeset); + trace2((qh ferr, 2049, "qh_triangulate: update neighbor lists for vertices from v%d\n", getid_(new_vertex_list))); + qh newvertex_list= new_vertex_list; /* all vertices of new facets */ + qh visible_list= NULL; + qh_updatevertices(/*qh.newvertex_list, empty newfacet_list and visible_list*/); + qh_resetlists(False, !qh_RESETvisible /*qh.newvertex_list, empty newfacet_list and visible_list*/); + + trace2((qh ferr, 2050, "qh_triangulate: identify degenerate tricoplanar facets from f%d\n", getid_(new_facet_list))); + trace2((qh ferr, 2051, "qh_triangulate: and replace facet->f.triowner with tricoplanar facets that own center, normal, etc.\n")); + FORALLfacet_(new_facet_list) { + if (facet->tricoplanar && !facet->visible) { + FOREACHneighbor_i_(facet) { + if (neighbor_i == 0) { /* first iteration */ + if (neighbor->tricoplanar) + orig_neighbor= neighbor->f.triowner; + else + orig_neighbor= neighbor; + }else { + if (neighbor->tricoplanar) + otherfacet= neighbor->f.triowner; + else + otherfacet= neighbor; + if (orig_neighbor == otherfacet) { + zinc_(Ztridegen); + facet->degenerate= True; + break; + } + } + } + } + } + + trace2((qh ferr, 2052, "qh_triangulate: delete visible facets -- non-simplicial, null, and mirrored facets\n")); + owner= NULL; + visible= NULL; + for (facet= new_facet_list; facet && facet->next; facet= nextfacet) { /* may delete facet */ + nextfacet= facet->next; + if (facet->visible) { + if (facet->tricoplanar) { /* a null or mirrored facet */ + qh_delfacet(facet); + qh num_visible--; + }else { /* a non-simplicial facet followed by its tricoplanars */ + if (visible && !owner) { + /* RBOX 200 s D5 t1001471447 | QHULL Qt C-0.01 Qx Qc Tv Qt -- f4483 had 6 vertices/neighbors and 8 ridges */ + trace2((qh ferr, 2053, "qh_triangulate: all tricoplanar facets degenerate for non-simplicial facet f%d\n", + visible->id)); + qh_delfacet(visible); + qh num_visible--; + } + visible= facet; + owner= NULL; + } + }else if (facet->tricoplanar) { + if (facet->f.triowner != visible || visible==NULL) { + qh_fprintf(qh ferr, 6162, "qhull error (qh_triangulate): tricoplanar facet f%d not owned by its visible, non-simplicial facet f%d\n", facet->id, getid_(visible)); + qh_errexit2(qh_ERRqhull, facet, visible); + } + if (owner) + facet->f.triowner= owner; + else if (!facet->degenerate) { + owner= facet; + nextfacet= visible->next; /* rescan tricoplanar facets with owner, 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(visible); + qh num_visible--; + } + } + } + if (visible && !owner) { + trace2((qh ferr, 2054, "qh_triangulate: all tricoplanar facets degenerate for last non-simplicial facet f%d\n", + visible->id)); + qh_delfacet(visible); + qh num_visible--; + } + qh NEWfacets= False; + qh ONLYgood= onlygood; /* restore value */ + if (qh CHECKfrequently) + qh_checkpolygon(qh facet_list); + qh hasTriangulation= True; +} /* triangulate */ + + +/*-<a href="qh-poly.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(facetT *facetA, vertexT **first_vertex) { + facetT *newfacet; + facetT *neighbor, **neighborp; + vertexT *apex; + int numnew=0; + + trace3((qh ferr, 3020, "qh_triangulate_facet: triangulate facet f%d\n", facetA->id)); + + if (qh IStracing >= 4) + qh_printfacet(qh ferr, facetA); + FOREACHneighbor_(facetA) { + neighbor->seen= False; + neighbor->coplanar= False; + } + if (qh CENTERtype == qh_ASvoronoi && !facetA->center /* matches upperdelaunay in qh_setfacetplane() */ + && fabs_(facetA->normal[qh hull_dim -1]) >= qh ANGLEround * qh_ZEROdelaunay) { + facetA->center= qh_facetcenter(facetA->vertices); + } + qh_willdelete(facetA, NULL); + qh newfacet_list= qh facet_tail; + facetA->visitid= qh visit_id; + apex= SETfirstt_(facetA->vertices, vertexT); + qh_makenew_nonsimplicial(facetA, apex, &numnew); + SETfirst_(facetA->neighbors)= NULL; + FORALLnew_facets { + newfacet->tricoplanar= True; + newfacet->f.trivisible= facetA; + newfacet->degenerate= False; + newfacet->upperdelaunay= facetA->upperdelaunay; + newfacet->good= facetA->good; + if (qh TRInormals) { /* 'Q11' triangulate duplicates ->normal and ->center */ + newfacet->keepcentrum= True; + if(facetA->normal){ + newfacet->normal= qh_memalloc(qh normal_size); + memcpy((char *)newfacet->normal, facetA->normal, qh normal_size); + } + if (qh CENTERtype == qh_AScentrum) + newfacet->center= qh_getcentrum(newfacet); + else if (qh CENTERtype == qh_ASvoronoi && facetA->center){ + newfacet->center= qh_memalloc(qh center_size); + memcpy((char *)newfacet->center, facetA->center, 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.newfacet_list*/); + zinc_(Ztricoplanar); + zadd_(Ztricoplanartot, numnew); + zmax_(Ztricoplanarmax, numnew); + qh visible_list= NULL; + if (!(*first_vertex)) + (*first_vertex)= qh newvertex_list; + qh newvertex_list= NULL; + qh_updatevertices(/*qh.newfacet_list, qh.empty visible_list and qh.newvertex_list*/); + qh_resetlists(False, !qh_RESETvisible /*qh.newfacet_list, qh.empty visible_list and qh.newvertex_list*/); +} /* triangulate_facet */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="triangulate_link">-</a> + + qh_triangulate_link(oldfacetA, facetA, oldfacetB, facetB) + relink facetA to facetB via oldfacets + returns: + adds mirror facets to qh degen_mergeset (4-d and up only) + design: + if they are already neighbors, the opposing neighbors become MRGmirror facets +*/ +void qh_triangulate_link(facetT *oldfacetA, facetT *facetA, facetT *oldfacetB, facetT *facetB) { + int errmirror= False; + + trace3((qh ferr, 3021, "qh_triangulate_link: relink old facets f%d and f%d between neighbors f%d and f%d\n", + oldfacetA->id, oldfacetB->id, facetA->id, facetB->id)); + if (qh_setin(facetA->neighbors, facetB)) { + if (!qh_setin(facetB->neighbors, facetA)) + errmirror= True; + else + qh_appendmergeset(facetA, facetB, MRGmirror, NULL); + }else if (qh_setin(facetB->neighbors, facetA)) + errmirror= True; + if (errmirror) { + qh_fprintf(qh ferr, 6163, "qhull error (qh_triangulate_link): mirror facets f%d and f%d do not match for old facets f%d and f%d\n", + facetA->id, facetB->id, oldfacetA->id, oldfacetB->id); + qh_errexit2(qh_ERRqhull, facetA, facetB); + } + qh_setreplace(facetB->neighbors, oldfacetB, facetA); + qh_setreplace(facetA->neighbors, oldfacetA, facetB); +} /* triangulate_link */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="triangulate_mirror">-</a> + + qh_triangulate_mirror(facetA, facetB) + delete mirrored facets from qh_triangulate_null() and qh_triangulate_mirror + a mirrored facet shares the same vertices of a logical ridge + design: + since a null facet duplicates the first two vertices, the opposing neighbors absorb the null facet + if they are already neighbors, the opposing neighbors become MRGmirror facets +*/ +void qh_triangulate_mirror(facetT *facetA, facetT *facetB) { + facetT *neighbor, *neighborB; + int neighbor_i, neighbor_n; + + trace3((qh ferr, 3022, "qh_triangulate_mirror: delete mirrored facets f%d and f%d\n", + facetA->id, facetB->id)); + FOREACHneighbor_i_(facetA) { + neighborB= SETelemt_(facetB->neighbors, neighbor_i, facetT); + if (neighbor == neighborB) + continue; /* occurs twice */ + qh_triangulate_link(facetA, neighbor, facetB, neighborB); + } + qh_willdelete(facetA, NULL); + qh_willdelete(facetB, NULL); +} /* triangulate_mirror */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="triangulate_null">-</a> + + qh_triangulate_null(facetA) + remove null facetA from qh_triangulate_facet() + a null facet has vertex #1 (apex) == vertex #2 + returns: + adds facetA to ->visible for deletion after qh_updatevertices + qh degen_mergeset contains mirror facets (4-d and up only) + design: + since a null facet duplicates the first two vertices, the opposing neighbors absorb the null facet + if they are already neighbors, the opposing neighbors become MRGmirror facets +*/ +void qh_triangulate_null(facetT *facetA) { + facetT *neighbor, *otherfacet; + + trace3((qh ferr, 3023, "qh_triangulate_null: delete null facet f%d\n", facetA->id)); + neighbor= SETfirstt_(facetA->neighbors, facetT); + otherfacet= SETsecondt_(facetA->neighbors, facetT); + qh_triangulate_link(facetA, neighbor, facetA, otherfacet); + qh_willdelete(facetA, NULL); +} /* triangulate_null */ + +#else /* qh_NOmerge */ +void qh_triangulate(void) { +} +#endif /* qh_NOmerge */ + + /*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="vertexintersect">-</a> + + qh_vertexintersect( vertexsetA, vertexsetB ) + intersects two vertex sets (inverse id ordered) + vertexsetA is a temporary set at the top of qhmem.tempstack + + returns: + replaces vertexsetA with the intersection + + notes: + could overwrite vertexsetA if currently too slow +*/ +void qh_vertexintersect(setT **vertexsetA,setT *vertexsetB) { + setT *intersection; + + intersection= qh_vertexintersect_new(*vertexsetA, vertexsetB); + qh_settempfree(vertexsetA); + *vertexsetA= intersection; + qh_settemppush(intersection); +} /* vertexintersect */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="vertexintersect_new">-</a> + + qh_vertexintersect_new( ) + intersects two vertex sets (inverse id ordered) + + returns: + a new set +*/ +setT *qh_vertexintersect_new(setT *vertexsetA,setT *vertexsetB) { + setT *intersection= qh_setnew(qh hull_dim - 1); + vertexT **vertexA= SETaddr_(vertexsetA, vertexT); + vertexT **vertexB= SETaddr_(vertexsetB, vertexT); + + while (*vertexA && *vertexB) { + if (*vertexA == *vertexB) { + qh_setappend(&intersection, *vertexA); + vertexA++; vertexB++; + }else { + if ((*vertexA)->id > (*vertexB)->id) + vertexA++; + else + vertexB++; + } + } + return intersection; +} /* vertexintersect_new */ + +/*-<a href="qh-poly.htm#TOC" + >-------------------------------</a><a name="vertexneighbors">-</a> + + qh_vertexneighbors() + for each vertex in qh.facet_list, + determine its neighboring facets + + returns: + sets qh.VERTEXneighbors + nop if qh.VERTEXneighbors already set + qh_addpoint() will maintain them + + notes: + assumes all vertex->neighbors are NULL + + design: + for each facet + for each vertex + append facet to vertex->neighbors +*/ +void qh_vertexneighbors(void /*qh.facet_list*/) { + facetT *facet; + vertexT *vertex, **vertexp; + + if (qh VERTEXneighbors) + return; + trace1((qh ferr, 1035, "qh_vertexneighbors: 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 hull_dim); + } + qh_setappend(&vertex->neighbors, facet); + } + } + qh VERTEXneighbors= True; +} /* vertexneighbors */ + +/*-<a href="qh-poly.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/src/qhull/src/libqhull/qh-geom.htm b/src/qhull/src/libqhull/qh-geom.htm new file mode 100644 index 000000000..6dc7465eb --- /dev/null +++ b/src/qhull/src/libqhull/qh-geom.htm @@ -0,0 +1,295 @@ +<!-- Do not edit with Front Page, it adds too many spaces --> +<html> +<head> +<meta http-equiv="Content-Type" +content="text/html; charset=iso-8859-1"> +<title>geom.c, geom2.c -- geometric and floating point routines</title> +</head> + +<body> +<!-- Navigation links --> +<p><a name="TOP"><b>Up:</b></a> <a +href="http://www.qhull.org">Home page</a> for Qhull<br> +<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual</a>: Table of Contents <br> +<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a> +• <a href="../../html/qh-quick.htm#options">Options</a> +• <a href="../../html/qh-opto.htm#output">Output</a> +• <a href="../../html/qh-optf.htm#format">Formats</a> +• <a href="../../html/qh-optg.htm#geomview">Geomview</a> +• <a href="../../html/qh-optp.htm#print">Print</a> +• <a href="../../html/qh-optq.htm#qhull">Qhull</a> +• <a href="../../html/qh-optc.htm#prec">Precision</a> +• <a href="../../html/qh-optt.htm#trace">Trace</a> +• <a href="index.htm">Functions</a><br> +<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a><br> +<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br> +<b>To:</b> <a href="qh-geom.htm#TOC">Geom</a> • <a href="qh-globa.htm">Global</a> +• <a href="qh-io.htm">Io</a> • <a href="qh-mem.htm">Mem</a> +• <a href="qh-merge.htm">Merge</a> • <a href="qh-poly.htm">Poly</a> +• <a href="qh-qhull.htm">Qhull</a> • <a href="qh-set.htm">Set</a> +• <a href="qh-stat.htm">Stat</a> • <a href="qh-user.htm">User</a> +</p> + +<hr> +<!-- Main text of document. --> + +<h2>geom.c, geom2.c, random.c -- geometric and floating point routines</h2> +<blockquote> +<p>Geometrically, a vertex is a point with <em>d</em> coordinates +and a facet is a halfspace. A <em>halfspace</em> is defined by an +oriented hyperplane through the facet's vertices. A <em>hyperplane</em> +is defined by <em>d</em> normalized coefficients and an offset. A +point is <em>above</em> a facet if its distance to the facet is +positive.</p> + +<p>Qhull uses floating point coordinates for input points, +vertices, halfspace equations, centrums, and an interior point.</p> + +<p>Qhull may be configured for single precision or double +precision floating point arithmetic (see <a href="user.h#realT">realT</a> +). </p> + +<p>Each floating point operation may incur round-off error (see +<a href="qh-merge.htm#TOC">Merge</a>). The maximum error for distance +computations is determined at initialization. The roundoff error +in halfspace computation is accounted for by computing the +distance from vertices to the halfspace. </p> +</blockquote> +<p><b>Copyright © 1995-2015 C.B. Barber</b></p> +<hr> +<p><a href="#TOP">»</a> <b>Geom</b> +<a name="TOC">•</a> <a href="qh-globa.htm#TOC">Global</a> • +<a href="qh-io.htm#TOC">Io</a> • <a href="qh-mem.htm#TOC">Mem</a> • +<a href="qh-merge.htm#TOC">Merge</a> • <a href="qh-poly.htm#TOC">Poly</a> • +<a href="qh-qhull.htm#TOC">Qhull</a> • <a href="qh-set.htm#TOC">Set</a> • +<a href="qh-stat.htm#TOC">Stat</a> • <a href="qh-user.htm#TOC">User</a> </p> + +<h3>Index to <a href="geom.c">geom.c</a>, +<a href="geom2.c">geom2.c</a>, <a href="geom.h">geom.h</a>, +<a href="random.c">random.c</a>, <a href="random.h">random.h</a> +</h3> + +<ul> +<li><a href="#gtype">geometric data types and constants</a> </li> +<li><a href="#gmacro">mathematical macros</a> +</li> +<li><a href="#gmath">mathematical functions</a> </li> +<li><a href="#gcomp">computational geometry functions</a> </li> +<li><a href="#gpoint">point array functions</a> </li> +<li><a href="#gfacet">geometric facet functions</a> </li> +<li><a href="#ground">geometric roundoff functions</a></li> +</ul> + +<h3><a href="qh-geom.htm#TOC">»</a><a name="gtype">geometric data types +and constants</a></h3> + +<ul> +<li><a href="libqhull.h#coordT">coordT</a> coordinates and +coefficients are stored as realT</li> +<li><a href="libqhull.h#pointT">pointT</a> a point is an array +of <tt>DIM3</tt> coordinates </li> +</ul> + +<h3><a href="qh-geom.htm#TOC">»</a><a name="gmacro">mathematical macros</a></h3> + +<ul> +<li><a href="geom.h#fabs_">fabs_</a> returns the absolute +value of a </li> +<li><a href="geom.h#fmax_">fmax_</a> returns the maximum +value of a and b </li> +<li><a href="geom.h#fmin_">fmin_</a> returns the minimum +value of a and b </li> +<li><a href="geom.h#maximize_">maximize_</a> maximize a value +</li> +<li><a href="geom.h#minimize_">minimize_</a> minimize a value +</li> +<li><a href="geom.h#det2_">det2_</a> compute a 2-d +determinate </li> +<li><a href="geom.h#det3_">det3_</a> compute a 3-d +determinate </li> +<li><a href="geom.h#dX">dX, dY, dZ</a> compute the difference +between two coordinates </li> +</ul> + +<h3><a href="qh-geom.htm#TOC">»</a><a name="gmath">mathematical functions</a></h3> + +<ul> +<li><a href="geom.c#backnormal">qh_backnormal</a> solve for +normal using back substitution </li> +<li><a href="geom2.c#crossproduct">qh_crossproduct</a> +compute the cross product of two 3-d vectors </li> +<li><a href="geom2.c#determinant">qh_determinant</a> compute +the determinant of a square matrix </li> +<li><a href="geom.c#gausselim">qh_gausselim</a> Gaussian +elimination with partial pivoting </li> +<li><a href="geom2.c#gram_schmidt">qh_gram_schmidt</a> +implements Gram-Schmidt orthogonalization by rows </li> +<li><a href="geom2.c#maxabsval">qh_maxabsval</a> return max +absolute value of a vector </li> +<li><a href="geom2.c#minabsval">qh_minabsval</a> return min +absolute value of a dim vector </li> +<li><a href="geom2.c#mindiff">qh_mindiff</a> return index of +min absolute difference of two vectors </li> +<li><a href="geom.c#normalize">qh_normalize</a> normalize a +vector </li> +<li><a href="geom.c#normalize2">qh_normalize2</a> normalize a +vector and report if too small </li> +<li><a href="geom2.c#printmatrix">qh_printmatrix</a> print +matrix given by row vectors </li> +<li><a href="random.c#rand">qh_rand/srand</a> generate random +numbers </li> +<li><a href="random.c#randomfactor">qh_randomfactor</a> return +a random factor near 1.0 </li> +<li><a href="random.c#randommatrix">qh_randommatrix</a> +generate a random dimXdim matrix in range (-1,1) </li> +</ul> + +<h3><a href="qh-geom.htm#TOC">»</a><a name="gcomp">computational geometry functions</a></h3> + +<ul> +<li><a href="geom2.c#detsimplex">qh_detsimplex</a> compute +determinate of a simplex of points </li> +<li><a href="io.c#detvnorm">qh_detvnorm</a> determine normal for Voronoi ridge </li> +<li><a href="geom2.c#distnorm">qh_distnorm</a> compute +distance from point to hyperplane as defined by normal and offset</li> +<li><a href="geom2.c#facetarea_simplex">qh_facetarea_simplex</a> +return area of a simplex</li> +<li><a href="geom.c#getangle">qh_getangle</a> return cosine +of angle (i.e., dot product) </li> +<li><a href="geom.c#getcenter">qh_getcenter</a> return +arithmetic center for a set of vertices </li> +<li><a href="geom2.c#pointdist">qh_pointdist</a> return +distance between two points </li> +<li><a href="geom2.c#rotatepoints">qh_rotatepoints</a> rotate +numpoints points by a row matrix </li> +<li><a href="geom2.c#sethalfspace">qh_sethalfspace</a> set +coords to dual of halfspace relative to an interior point </li> +<li><a href="geom.c#sethyperplane_det">qh_sethyperplane_det</a> +return hyperplane for oriented simplex using determinates +</li> +<li><a href="geom.c#sethyperplane_gauss">qh_sethyperplane_gauss</a> +return hyperplane for oriented simplex using Gaussian +elimination </li> +<li><a href="geom2.c#voronoi_center">qh_voronoi_center</a> +return Voronoi center for a set of points </li> +</ul> + +<h3><a href="qh-geom.htm#TOC">»</a><a name="gpoint">point array functions</a></h3> +<ul> +<li><a href="geom2.c#copypoints">qh_copypoints</a> return +malloc'd copy of points</li> +<li><a href="geom2.c#joggleinput">qh_joggleinput</a> joggle +input points by qh.JOGGLEmax </li> +<li><a href="geom2.c#maxmin">qh_maxmin</a> return max/min +points for each dimension</li> +<li><a href="geom2.c#maxsimplex">qh_maxsimplex</a> determines +maximum simplex for a set of points </li> +<li><a href="geom2.c#printpoints">qh_printpoints</a> print ids for a +set of points </li> +<li><a href="geom2.c#projectinput">qh_projectinput</a> project +input using qh DELAUNAY and qh low_bound/high_bound </li> +<li><a href="geom2.c#projectpoints">qh_projectpoints</a> +project points along one or more dimensions </li> +<li><a href="geom2.c#rotateinput">qh_rotateinput</a> rotate +input points using row matrix </li> +<li><a href="geom2.c#scaleinput">qh_scaleinput</a> scale +input points using qh low_bound/high_bound </li> +<li><a href="geom2.c#scalelast">qh_scalelast</a> scale last +coordinate to [0,m] for Delaunay triangulations </li> +<li><a href="geom2.c#scalepoints">qh_scalepoints</a> scale +points to new lowbound and highbound </li> +<li><a href="geom2.c#setdelaunay">qh_setdelaunay</a> project +points to paraboloid for Delaunay triangulation </li> +<li><a href="geom2.c#sethalfspace_all">qh_sethalfspace_all</a> +generate dual for halfspace intersection with interior +point </li> +</ul> + +<h3><a href="qh-geom.htm#TOC">»</a><a name="gfacet">geometric facet functions</a></h3> +<ul> +<li><a href="geom.c#distplane">qh_distplane</a> return +distance from point to facet </li> +<li><a href="geom2.c#facetarea">qh_facetarea</a> return area +of a facet </li> +<li><a href="geom2.c#facetcenter">qh_facetcenter</a> return +Voronoi center for a facet's vertices </li> +<li><a href="geom.c#findbest">qh_findbest</a> find visible +facet or best facet for a point </li> +<li><a href="geom.c#findbesthorizon">qh_findbesthorizon</a> +update best new facet with horizon facets</li> +<li><a href="geom.c#findbestnew">qh_findbestnew</a> find best +new facet for point </li> +<li><a href="geom2.c#getarea">qh_getarea</a> get area of all +facets in facetlist, collect statistics </li> +<li><a href="geom.c#getcentrum">qh_getcentrum</a> return +centrum for a facet </li> +<li><a href="geom.c#getdistance">qh_getdistance</a> returns +the max and min distance of a facet's vertices to a +neighboring facet</li> +<li><a href="geom2.c#findgooddist">qh_findgooddist</a> find +best good facet visible for point from facet </li> +<li><a href="geom2.c#inthresholds">qh_inthresholds</a> return +True if facet normal within 'Pdn' and 'PDn'</li> +<li><a href="geom2.c#orientoutside">qh_orientoutside</a> +orient facet so that <tt>qh.interior_point</tt> is inside</li> +<li><a href="geom.c#projectpoint">qh_projectpoint</a> project +point onto a facet </li> +<li><a href="geom.c#setfacetplane">qh_setfacetplane</a> sets +the hyperplane for a facet </li> +<li><a href="geom2.c#sharpnewfacets">qh_sharpnewfacets</a> true +if new facets contains a sharp corner</li> +</ul> + +<h3><a href="qh-geom.htm#TOC">»</a><a name="ground">geometric roundoff functions</a></h3> +<ul> +<li><a href="geom2.c#detjoggle">qh_detjoggle</a> determine +default joggle for points and distance roundoff error</li> +<li><a href="geom2.c#detroundoff">qh_detroundoff</a> +determine maximum roundoff error and other precision constants</li> +<li><a href="geom2.c#distround">qh_distround</a> compute +maximum roundoff error due to a distance computation to a +normalized hyperplane</li> +<li><a href="geom2.c#divzero">qh_divzero</a> divide by a +number that is nearly zero </li> +<li><a href="geom2.c#maxouter">qh_maxouter</a> return maximum outer +plane</li> +<li><a href="geom2.c#outerinner">qh_outerinner</a> return actual +outer and inner planes +</ul> + +<p><!-- Navigation links --> </p> +<hr> +<p><b>Up:</b> +<a href="http://www.qhull.org">Home page for +Qhull</a> <br> +<b>Up:</b> <a href="index.htm#TOC">Qhull manual: Table of Contents</a> <br> +<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a> +• <a href="../../html/qh-quick.htm#options">Options</a> +• <a href="../../html/qh-opto.htm#output">Output</a> +• <a href="../../html/qh-optf.htm#format">Formats</a> +• <a href="../../html/qh-optg.htm#geomview">Geomview</a> +• <a href="../../html/qh-optp.htm#print">Print</a> +• <a href="../../html/qh-optq.htm#qhull">Qhull</a> +• <a href="../../html/qh-optc.htm#prec">Precision</a> +• <a href="../../html/qh-optt.htm#trace">Trace</a> +• <a href="index.htm">Functions</a><br> +<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a> <br> +<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br> +<b>To:</b> <a href="qh-geom.htm">Geom</a> • +<a href="qh-globa.htm">Global</a> • <a href="qh-io.htm">Io</a> +• <a href="qh-mem.htm">Mem</a> • <a href="qh-merge.htm">Merge</a> +• <a href="qh-poly.htm">Poly</a> • <a href="qh-qhull.htm#TOC">Qhull</a> +• <a href="qh-set.htm">Set</a> • <a href="qh-stat.htm">Stat</a> +• <a href="qh-user.htm">User</a><br> + + +<p><!-- GC common information --> </p> +<hr> +<p><a href="http://www.geom.uiuc.edu/"><img +src="../../html/qh--geom.gif" align="middle" width="40" height="40"></a><i>The +Geometry Center Home Page </i></p> +<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a> +</a><br> +Created: May 2, 1997 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p> +</body> +</html> diff --git a/src/qhull/src/libqhull/qh-globa.htm b/src/qhull/src/libqhull/qh-globa.htm new file mode 100644 index 000000000..c87508b66 --- /dev/null +++ b/src/qhull/src/libqhull/qh-globa.htm @@ -0,0 +1,165 @@ +<!-- Do not edit with Front Page, it adds too many spaces --> +<html> +<head> +<meta http-equiv="Content-Type" +content="text/html; charset=iso-8859-1"> +<title>global.c -- global variables and their functions</title> +</head> + +<body> +<!-- Navigation links --> +<p><a name="TOP"><b>Up:</b></a> <a +href="http://www.qhull.org">Home page</a> for Qhull<br> +<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual</a>: Table of Contents <br> +<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a> +• <a href="../../html/qh-quick.htm#options">Options</a> +• <a href="../../html/qh-opto.htm#output">Output</a> +• <a href="../../html/qh-optf.htm#format">Formats</a> +• <a href="../../html/qh-optg.htm#geomview">Geomview</a> +• <a href="../../html/qh-optp.htm#print">Print</a> +• <a href="../../html/qh-optq.htm#qhull">Qhull</a> +• <a href="../../html/qh-optc.htm#prec">Precision</a> +• <a href="../../html/qh-optt.htm#trace">Trace</a> +• <a href="index.htm">Functions</a><br> +<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a><br> +<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br> +<b>To:</b> <a href="qh-geom.htm">Geom</a> • <a href="qh-globa.htm#TOC">Global</a> +• <a href="qh-io.htm">Io</a> • <a href="qh-mem.htm">Mem</a> +• <a href="qh-merge.htm">Merge</a> • <a href="qh-poly.htm">Poly</a> +• <a href="qh-qhull.htm">Qhull</a> • <a href="qh-set.htm">Set</a> +• <a href="qh-stat.htm">Stat</a> • <a href="qh-user.htm">User</a> +</p> + +<hr> +<!-- Main text of document. --> + +<h2>global.c -- global variables and their functions</h2> +<blockquote> +<p>Qhull uses a global data structure, <tt>qh</tt>, to store +globally defined constants, lists, sets, and variables. This +allows multiple instances of Qhull to execute at the same time. +The structure may be statically allocated or +dynamically allocated with malloc(). See +<a href="user.h#QHpointer">QHpointer</a>. +</p> +</blockquote> +<p><b>Copyright © 1995-2015 C.B. Barber</b></p> +<hr> +<p><a href="#TOP">»</a> <a href="qh-geom.htm#TOC">Geom</a> +<a name="TOC">•</a> <b>Global</b> • +<a href="qh-io.htm#TOC">Io</a> • <a href="qh-mem.htm#TOC">Mem</a> • +<a href="qh-merge.htm#TOC">Merge</a> • <a href="qh-poly.htm#TOC">Poly</a> • +<a href="qh-qhull.htm#TOC">Qhull</a> • <a href="qh-set.htm#TOC">Set</a> • +<a href="qh-stat.htm#TOC">Stat</a> • <a href="qh-user.htm#TOC">User</a> </p> + +<h3>Index to <a href="global.c">global.c</a> and +<a href="libqhull.h">libqhull.h</a></h3> + +<ul> +<li><a href="#ovar">Qhull's global variables</a> </li> +<li><a href="#ofunc">Global variable and initialization +routines</a> </li> +</ul> + +<h3><a href="qh-globa.htm#TOC">»</a><a name="ovar">Qhull's global +variables</a></h3> + +<ul> +<li><a href=global.c#qh_version>qh_version</a> version string +<li><a href="libqhull.h#qh">qh</a> all global variables for +qhull are in <tt>qh,qhmem</tt>, and <tt>qhstat</tt></li> +<li><a href="libqhull_r.h#qh">QHULL_LIB_CHECK</a> Check for compatible library</li> +<li><a href="libqhull.h#qh-const">qh constants</a> configuration +flags and constants for Qhull </li> +<li><a href="libqhull.h#qh-prec">qh precision constants</a> +precision constants for Qhull </li> +<li><a href="libqhull.h#qh-codetern">qh internal constants</a> +internal constants for Qhull </li> +<li><a href="libqhull.h#qh-lists">qh facet and vertex lists</a> +lists of facets and vertices </li> +<li><a href="libqhull.h#qh-var">qh global variables</a> minimum +and maximum distances, next visit ids, several flags, and +other global variables. </li> +<li><a href="libqhull.h#qh-set">qh global sets</a> global sets +for merging, hashing, input, etc. </li> +<li><a href="libqhull.h#qh-buf">qh global buffers</a> buffers +for matrix operations and input </li> +<li><a href="libqhull.h#qh-static">qh static variables</a> +static variables for individual functions </li> +</ul> + +<h3><a href="qh-globa.htm#TOC">»</a><a name="ofunc">Global variable and +initialization routines</a></h3> + +<ul> +<li><a href="global.c#appendprint">qh_appendprint</a> append +output format to <tt>qh.PRINTout</tt> </li> +<li><a href="global.c#freebuffers">qh_freebuffers</a> free +global memory buffers </li> +<li><a href="global.c#freeqhull">qh_freeqhull</a> free memory +used by qhull </li> +<li><a href="global.c#init_A">qh_init_A</a> called before +error handling initialized </li> +<li><a href="global.c#init_B">qh_init_B</a> called after +points are defined </li> +<li><a href="global.c#init_qhull_command">qh_init_qhull_command</a> +build <tt>qh.qhull_command</tt> from <tt>argc/argv</tt></li> +<li><a href="global.c#initflags">qh_initflags</a> set flags +and constants from command line </li> +<li><a href="global.c#initqhull_buffers">qh_initqhull_buffers</a> +initialize global memory buffers </li> +<li><a href="global.c#initqhull_globals">qh_initqhull_globals</a> +initialize global variables </li> +<li><a href="global.c#initqhull_mem">qh_initqhull_mem</a> +initialize Qhull memory management </li> +<li><a href="global.c#initqhull_start">qh_initqhull_start</a> +allocate qh_qh and call qh_initqhull_start2() +<li><a href="global.c#initqhull_start2">qh_initqhull_start2</a> +initialize default values at Qhull startup </li> +<li><a href="global.c#initthresholds">qh_initthresholds</a> +initialize 'Pdn' and 'PDn' thresholds </li> +<li><a href="global.c#lib_check">qh_lib_check</a> check for compatible Qhull library. Invoked by QHULL_LIB_CHECK at start of each program.</li> +<li><a href="global.c#option">qh_option</a> append option +description to <tt>qh.global_options</tt> </li> +<li><a href="global.c#restore_qhull">qh_restore_qhull</a> +restores a previously saved qhull </li> +<li><a href="global.c#save_qhull">qh_save_qhull</a> saves +qhull for a later qh_restore_qhull() </li> +<li><a href="global.c#strtol">qh_strtol</a> duplicates +strtod() and strtol() </li> +</ul> + +<p><!-- Navigation links --> </p> +<hr> +<p><b>Up:</b> +<a href="http://www.qhull.org">Home page for +Qhull</a> <br> +<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual: Table of Contents</a> <br> +<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a> +• <a href="../../html/qh-quick.htm#options">Options</a> +• <a href="../../html/qh-opto.htm#output">Output</a> +• <a href="../../html/qh-optf.htm#format">Formats</a> +• <a href="../../html/qh-optg.htm#geomview">Geomview</a> +• <a href="../../html/qh-optp.htm#print">Print</a> +• <a href="../../html/qh-optq.htm#qhull">Qhull</a> +• <a href="../../html/qh-optc.htm#prec">Precision</a> +• <a href="../../html/qh-optt.htm#trace">Trace</a> +• <a href="index.htm">Functions</a><br> +<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a> <br> +<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br> +<b>To:</b> <a href="qh-geom.htm">Geom</a> • +<a href="qh-globa.htm">Global</a> • <a href="qh-io.htm">Io</a> +• <a href="qh-mem.htm">Mem</a> • <a href="qh-merge.htm">Merge</a> +• <a href="qh-poly.htm">Poly</a> • <a href="qh-qhull.htm#TOC">Qhull</a> +• <a href="qh-set.htm">Set</a> • <a href="qh-stat.htm">Stat</a> +• <a href="qh-user.htm">User</a><br> +<p><!-- GC common information --> </p> +<hr> +<p><a href="http://www.geom.uiuc.edu/"><img +src="../../html/qh--geom.gif" align="middle" width="40" height="40"></a><i>The +Geometry Center Home Page </i></p> +<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a> +</a><br> +Created: May 2, 1997 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p> +</body> +</html> diff --git a/src/qhull/src/libqhull/qh-io.htm b/src/qhull/src/libqhull/qh-io.htm new file mode 100644 index 000000000..5cb591d87 --- /dev/null +++ b/src/qhull/src/libqhull/qh-io.htm @@ -0,0 +1,305 @@ +<!-- Do not edit with Front Page, it adds too many spaces --> +<html> +<head> +<meta http-equiv="Content-Type" +content="text/html; charset=iso-8859-1"> +<title>io.c -- input and output operations</title> +</head> + +<body> +<!-- Navigation links --> +<p><a name="TOP"><b>Up:</b></a> <a +href="http://www.qhull.org">Home page</a> for Qhull<br> +<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual</a>: Table of Contents <br> +<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a> +• <a href="../../html/qh-quick.htm#options">Options</a> +• <a href="../../html/qh-opto.htm#output">Output</a> +• <a href="../../html/qh-optf.htm#format">Formats</a> +• <a href="../../html/qh-optg.htm#geomview">Geomview</a> +• <a href="../../html/qh-optp.htm#print">Print</a> +• <a href="../../html/qh-optq.htm#qhull">Qhull</a> +• <a href="../../html/qh-optc.htm#prec">Precision</a> +• <a href="../../html/qh-optt.htm#trace">Trace</a> +• <a href="index.htm">Functions</a><br> +<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a><br> +<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br> +<b>To:</b> <a href="qh-geom.htm">Geom</a> • <a href="qh-globa.htm">Global</a> +• <a href="qh-io.htm#TOC">Io</a> • <a href="qh-mem.htm">Mem</a> +• <a href="qh-merge.htm">Merge</a> • <a href="qh-poly.htm">Poly</a> +• <a href="qh-qhull.htm">Qhull</a> • <a href="qh-set.htm">Set</a> +• <a href="qh-stat.htm">Stat</a> • <a href="qh-user.htm">User</a> +</p> +<hr> + +<h2>io.c -- input and output operations</h2> +<blockquote> + +<p>Qhull provides a wide range of input +and output options. To organize the code, most output formats use +the same driver: </p> + +<pre> + qh_printbegin( fp, format, facetlist, facets, printall ); + + FORALLfacet_( facetlist ) + qh_printafacet( fp, format, facet, printall ); + + FOREACHfacet_( facets ) + qh_printafacet( fp, format, facet, printall ); + + qh_printend( fp, format ); +</pre> + +<p>Note the 'printall' flag. It selects whether or not +qh_skipfacet() is tested. </p> + +</blockquote> +<p><b>Copyright © 1995-2015 C.B. Barber</b></p> +<hr> +<p><a href="#TOP">»</a> <a href="qh-geom.htm#TOC">Geom</a> <a name="TOC">•</a> +<a href="qh-globa.htm#TOC">Global</a> • <b>Io</b> • +<a href="qh-mem.htm#TOC">Mem</a> • <a href="qh-merge.htm#TOC">Merge</a> • +<a href="qh-poly.htm#TOC">Poly</a> • <a href="qh-qhull.htm#TOC">Qhull</a> • +<a href="qh-set.htm#TOC">Set</a> • <a href="qh-stat.htm#TOC">Stat</a> • +<a href="qh-user.htm#TOC">User</a> </p> + +<h3>Index to <a href="io.c">io.c</a> and <a href="io.h">io.h</a></h3> + +<ul> +<li><a href="#iconst">io.h constants and types</a> </li> +<li><a href="#ilevel">User level functions</a> </li> +<li><a href="#iprint">Print functions for all output formats</a></li> +<li><a href="#itext">Text output functions</a> </li> +<li><a href="#iutil">Text utility functions</a></li> +<li><a href="#igeom">Geomview output functions</a> </li> +<li><a href="#iview">Geomview utility functions</a></li> +</ul> + +<h3><a href="qh-io.htm#TOC">»</a><a name="iconst">io.h constants and types</a></h3> + +<ul> +<li><a href="io.h#qh_MAXfirst">qh_MAXfirst</a> maximum length +of first two lines of stdin </li> +<li><a href="io.h#qh_WHITESPACE">qh_WHITESPACE</a> possible +values of white space </li> +<li><a href="io.h#printvridgeT">printvridgeT</a> function to +print results of qh_printvdiagram or qh_eachvoronoi</li> +</ul> + +<h3><a href="qh-io.htm#TOC">»</a><a name="ilevel">User level functions</a></h3> + +<ul> +<li><a href="io.c#copyfilename">qh_copyfilename</a> +copy filename identified by qh_skipfilename +<li><a href="io.c#eachvoronoi_all">qh_eachvoronoi_all</a> +visit each Voronoi ridge of the Voronoi diagram +<li><a href="io.c#prepare_output">qh_prepare_output</a> +prepare Qhull for output (called by qh_produce_output()) +<li><a href="io.c#printhelp_degenerate">qh_printhelp_degenerate</a> +prints descriptive message for precision error </li> +<li><a href="io.c#printhelp_singular">qh_printhelp_singular</a> +print help message for singular data </li> +<li><a href="libqhull.c#printsummary">qh_printsummary</a> print +summary ('s')</li> +<li><a href="io.c#produce_output">qh_produce_output</a> +prints out the result of qhull()</li> +<li><a href="io.c#produce_output">qh_produce_output2</a> +prints out the result of qhull() without calling qh_prepare_output()</li> +<li><a href="io.c#readfeasible">qh_readfeasible</a> read +interior point from remainder and qh fin ('H')</li> +<li><a href="io.c#readpoints">qh_readpoints</a> read input +points </li> +<li><a href="io.c#setfeasible">qh_setfeasible</a> set +interior point from qh feasible_string ('Hn,n,n')</li> +<li><a href="io.c#skipfilename">qh_skipfilename</a> +skip filename in string +</ul> + +<h3><a href="qh-io.htm#TOC">»</a><a name="iprint">Print functions for all +output formats</a></h3> + +<ul> +<li><a href="io.c#countfacets">qh_countfacets</a> count good +facets for printing and set visitid </li> +<li><a href="io.c#markkeep">qh_markkeep</a> mark good facets +that meet qh.KEEParea ('PAn'), qh.KEEPmerge ('PMn'), and qh.KEEPminArea ('PFn')</li> +<li><a href="io.c#order_vertexneighbors">qh_order_vertexneighbors</a> +order neighbors for a 3-d vertex by adjacency ('i', 'o')</li> +<li><a href="io.c#printafacet">qh_printafacet</a> print facet +in an output format </li> +<li><a href="io.c#printbegin">qh_printbegin</a> print header +for an output format </li> +<li><a href="io.c#printend">qh_printend</a> print trailer for +an output format </li> +<li><a href="user.c#printfacetlist">qh_printfacetlist</a> +print facets in a facetlist</li> +<li><a href="io.c#printfacets">qh_printfacets</a> print +facetlist and/or facet set in an output format </li> +<li><a href="io.c#printneighborhood">qh_printneighborhood</a> +print neighborhood of one or two facets ('Po')</li> +<li><a href="io.c#produce_output">qh_produce_output</a> +print the results of qh_qhull() </li> +<li><a href="io.c#skipfacet">qh_skipfacet</a> True if not +printing this facet ('Pdk:n', 'QVn', 'QGn')</li> +<li><a href="io.c#facetvertices">qh_facetvertices</a> return +vertices in a set of facets ('p')</li> +</ul> + +<h3><a href="qh-io.htm#TOC">»</a><a name="itext">Text output functions</a></h3> +<ul> +<li><a href="io.c#eachvoronoi">qh_eachvoronoi</a> +print or visit each Voronoi ridge for an input site of the Voronoi diagram +<li><a href="io.c#printextremes">qh_printextremes</a> print +extreme points by point ID (vertices of convex hull) ('Fx')</li> +<li><a href="io.c#printextremes_2d">qh_printextremes_2d</a> print +2-d extreme points by point ID ('Fx')</li> +<li><a href="io.c#printextremes_d">qh_printextremes_d</a> print +extreme points of input sites for Delaunay triangulations ('Fx')</li> +<li><a href="io.c#printfacet">qh_printfacet</a> print all +fields of a facet ('f')</li> +<li><a href="io.c#printfacet2math">qh_printfacet2math</a> print +2-d Maple or Mathematica output for a facet ('FM' or 'm')</li> +<li><a href="io.c#printfacet3math">qh_printfacet3math</a> +print 3-d Maple or Mathematica facet ('FM' or 'm')</li> +<li><a href="io.c#printfacet3vertex">qh_printfacet3vertex</a> +print vertices for a 3-d facet ('i', 'o')</li> +<li><a href="io.c#printfacetheader">qh_printfacetheader</a> +prints header fields of a facet ('f')</li> +<li><a href="io.c#printfacetNvertex_nonsimplicial">qh_printfacetNvertex_nonsimplicial</a> +print vertices for an N-d non-simplicial facet ('i', 'Ft')</li> +<li><a href="io.c#printfacetNvertex_simplicial">qh_printfacetNvertex_simplicial</a> +print vertices for an N-d simplicial facet ('i', 'o', 'Ft')</li> +<li><a href="io.c#printfacetridges">qh_printfacetridges</a> +prints ridges of a facet ('f')</li> +<li><a href="io.c#printpoints_out">qh_printpoints_out</a> prints +vertices for facets by their point coordinates ('p')</li> +<li><a href="io.c#printridge">qh_printridge</a> print all +fields for a ridge ('f')</li> +<li><a href="io.c#printvdiagram">qh_printvdiagram</a> print +voronoi diagram as Voronoi vertices for each input pair</li> +<li><a href="io.c#printvertex">qh_printvertex</a> print all +fields for a vertex ('f')</li> +<li><a href="io.c#printvertexlist">qh_printvertexlist</a> +print vertices used by a list or set of facets ('f')</li> +<li><a href="io.c#printvertices">qh_printvertices</a> print a +set of vertices ('f')</li> +<li><a href="io.c#printvneighbors">qh_printvneighbors</a> +print vertex neighbors of vertices ('FN')</li> +<li><a href="io.c#printvoronoi">qh_printvoronoi</a> print +voronoi diagram in 'o' or 'G' format</li> +</ul> + +<h3><a href="qh-io.htm#TOC">»</a><a name="iutil">Text utility functions</a></h3> +<ul> +<li><a href="io.c#dfacet">dfacet</a> print facet by ID </li> +<li><a href="io.c#dvertex">dvertex</a> print vertex by ID </li> +<li><a href="io.c#compare_facetarea">qh_compare_facetarea</a> +used by qsort() to order facets by area </li> +<li><a href="io.c#compare_facetmerge">qh_compare_facetmerge</a> +used by qsort() to order facets by number of merges </li> +<li><a href="io.c#compare_facetvisit">qh_compare_facetvisit</a> +used by qsort() to order facets by visit ID or ID </li> +<li><a href="io.c#compare_vertexpoint">qh_compare_vertexpoint</a> +used by qsort() to order vertices by point ID </li> +<li><a href="io.c#detvnorm">qh_detvnorm</a> determine normal for Voronoi ridge </li> +<li><a href="io.c#detvridge">qh_detvridge</a> determine Voronoi +ridge for an input site +<li><a href="io.c#detvridge3">qh_detvridge3</a> determine 3-d Voronoi +ridge for an input site +<li><a href="io.c#facet2point">qh_facet2point</a> return two +projected temporary vertices for a 2-d facet ('m', 'G')</li> +<li><a href="io.c#markvoronoi">qh_markvoronoi</a> mark Voronoi +vertices for printing +<li><a href="io.c#printcenter">qh_printcenter</a> print +facet->center as centrum or Voronoi center ('Ft', 'v p', 'FC', 'f') </li> +<li><a href="io.c#printpoint">qh_printpoint</a>, qh_printpointid, print +coordinates of a point ('p', 'o', 'Fp', 'G', 'f')</li> +<li><a href="io.c#printpoint3">qh_printpoint3</a> prints 2-d, +3-d, or 4-d point as 3-d coordinates ('G')</li> +<li><a href="io.c#printvdiagram2">qh_printvdiagram2</a> print +voronoi diagram for each ridge of each vertex from qh_markvoronoi</li> +<li><a href="io.c#printvnorm">qh_printvnorm</a> print +separating plane of the Voronoi diagram for a pair of input sites</li> +<li><a href="io.c#printvridge">qh_printvridge</a> print +ridge of the Voronoi diagram for a pair of input sites</li> +<li><a href="io.c#projectdim3">qh_projectdim3</a> project 2-d +3-d or 4-d point to a 3-d point ('G')</li> +</ul> + +<h3><a href="qh-io.htm#TOC">»</a><a name="igeom">Geomview output functions</a></h3> +<ul> +<li><a href="io.c#printfacet2geom">qh_printfacet2geom</a> +print facet as a 2-d VECT object </li> +<li><a href="io.c#printfacet2geom_points">qh_printfacet2geom_points</a> +print points as a 2-d VECT object with offset </li> +<li><a href="io.c#printfacet3geom_nonsimplicial">qh_printfacet3geom_nonsimplicial</a> +print Geomview OFF for a 3-d nonsimplicial facet. </li> +<li><a href="io.c#printfacet3geom_points">qh_printfacet3geom_points</a> +prints a 3-d facet as OFF Geomview object. </li> +<li><a href="io.c#printfacet3geom_simplicial">qh_printfacet3geom_simplicial</a> +print Geomview OFF for a 3-d simplicial facet. </li> +<li><a href="io.c#printfacet4geom_nonsimplicial">qh_printfacet4geom_nonsimplicial</a> +print Geomview 4OFF file for a 4d nonsimplicial facet </li> +<li><a href="io.c#printfacet4geom_simplicial">qh_printfacet4geom_simplicial</a> +print Geomview 4OFF file for a 4d simplicial facet </li> +<li><a href="io.c#printhyperplaneintersection">qh_printhyperplaneintersection</a> +print hyperplane intersection as OFF or 4OFF </li> +<li><a href="io.c#printvoronoi">qh_printvoronoi</a> print +voronoi diagram in 'o' or 'G' format</li> +</ul> +<h3><a href="qh-io.htm#TOC">»</a><a name="iview">Geomview utility functions</a></h3> +<ul> +<li><a href="io.c#geomplanes">qh_geomplanes</a> + return outer and inner planes for Geomview</li> +<li><a href="io.c#printcentrum">qh_printcentrum</a> print +centrum for a facet in OOGL format </li> +<li><a href="io.c#printend4geom">qh_printend4geom</a> helper +function for qh_printbegin/printend </li> +<li><a href="io.c#printhyperplaneintersection">qh_printhyperplaneintersection</a> +print Geomview OFF or 4OFF for the intersection of two +hyperplanes in 3-d or 4-d </li> +<li><a href="io.c#printline3geom">qh_printline3geom</a> prints a +line as a VECT </li> +<li><a href="io.c#printpointvect">qh_printpointvect</a> +prints a 2-d or 3-d point as 3-d VECT's </li> +<li><a href="io.c#printpointvect2">qh_printpointvect2</a> +prints a 2-d or 3-d point as 2 3-d VECT's </li> +<li><a href="io.c#printspheres">qh_printspheres</a> prints 3-d +vertices as OFF spheres </li> +</ul> +<p> +<p><!-- Navigation links --> </p> +<hr> +<p><b>Up:</b> +<a href="http://www.qhull.org">Home page for +Qhull</a> <br> +<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual: Table of Contents</a> <br> +<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a> +• <a href="../../html/qh-quick.htm#options">Options</a> +• <a href="../../html/qh-opto.htm#output">Output</a> +• <a href="../../html/qh-optf.htm#format">Formats</a> +• <a href="../../html/qh-optg.htm#geomview">Geomview</a> +• <a href="../../html/qh-optp.htm#print">Print</a> +• <a href="../../html/qh-optq.htm#qhull">Qhull</a> +• <a href="../../html/qh-optc.htm#prec">Precision</a> +• <a href="../../html/qh-optt.htm#trace">Trace</a> +• <a href="index.htm">Functions</a><br> +<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a> <br> +<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br> +<b>To:</b> <a href="qh-geom.htm">Geom</a> • +<a href="qh-globa.htm">Global</a> • <a href="qh-io.htm">Io</a> +• <a href="qh-mem.htm">Mem</a> • <a href="qh-merge.htm">Merge</a> +• <a href="qh-poly.htm">Poly</a> • <a href="qh-qhull.htm#TOC">Qhull</a> +• <a href="qh-set.htm">Set</a> • <a href="qh-stat.htm">Stat</a> +• <a href="qh-user.htm">User</a><br> +</p> +<p><!-- GC common information --> </p> +<hr> +<p><a href="http://www.geom.uiuc.edu/"><img +src="../../html/qh--geom.gif" align="middle" width="40" height="40"></a><i>The +Geometry Center Home Page </i></p> +<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a> +</a><br> +Created: May 2, 1997 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p> +</body> +</html> diff --git a/src/qhull/src/libqhull/qh-mem.htm b/src/qhull/src/libqhull/qh-mem.htm new file mode 100644 index 000000000..b993b2229 --- /dev/null +++ b/src/qhull/src/libqhull/qh-mem.htm @@ -0,0 +1,145 @@ +<!-- Do not edit with Front Page, it adds too many spaces --> +<html> +<head> +<meta http-equiv="Content-Type" +content="text/html; charset=iso-8859-1"> +<title>mem.c -- memory operations</title> +</head> + +<body> +<!-- Navigation links --> +<p><a name="TOP"><b>Up:</b></a> <a +href="http://www.qhull.org">Home page</a> for Qhull<br> +<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual</a>: Table of Contents <br> +<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a> +• <a href="../../html/qh-quick.htm#options">Options</a> +• <a href="../../html/qh-opto.htm#output">Output</a> +• <a href="../../html/qh-optf.htm#format">Formats</a> +• <a href="../../html/qh-optg.htm#geomview">Geomview</a> +• <a href="../../html/qh-optp.htm#print">Print</a> +• <a href="../../html/qh-optq.htm#qhull">Qhull</a> +• <a href="../../html/qh-optc.htm#prec">Precision</a> +• <a href="../../html/qh-optt.htm#trace">Trace</a> +• <a href="index.htm">Functions</a><br> +<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a><br> +<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br> +<b>To:</b> <a href="qh-geom.htm">Geom</a> • <a href="qh-globa.htm">Global</a> +• <a href="qh-io.htm">Io</a> • <a href="qh-mem.htm#TOC">Mem</a> +• <a href="qh-merge.htm">Merge</a> • <a href="qh-poly.htm">Poly</a> +• <a href="qh-qhull.htm">Qhull</a> • <a href="qh-set.htm">Set</a> +• <a href="qh-stat.htm">Stat</a> • <a href="qh-user.htm">User</a> +</p> +<hr> + +<h2>mem.c -- memory operations</h2> +<blockquote> +<p>Qhull uses quick-fit memory allocation. It maintains a +set of free lists for a variety of small allocations. A +small request returns a block from the best fitting free +list. If the free list is empty, Qhull allocates a block +from a reserved buffer. </p> +<p>Use 'T5' to trace memory allocations.</p> + +</blockquote> +<p><b>Copyright © 1995-2015 C.B. Barber</b></p> +<hr> +<p><a href="#TOP">»</a> <a href="qh-geom.htm#TOC">Geom</a> +<a name="TOC">•</a> <a href="qh-globa.htm#TOC">Global</a> • +<a href="qh-io.htm#TOC">Io</a> • <b>Mem</b> +• <a href="qh-merge.htm#TOC">Merge</a> • <a href="qh-poly.htm#TOC">Poly</a> +• <a href="qh-qhull.htm#TOC">Qhull</a> • <a href="qh-set.htm#TOC">Set</a> +• <a href="qh-stat.htm#TOC">Stat</a> • <a href="qh-user.htm#TOC">User</a> +</p> +<h3>Index to <a href="mem.c">mem.c</a> and +<a href="mem.h">mem.h</a></h3> +<ul> +<li><a href="#etype">mem.h data types</a> </li> +<li><a href="#emacro">mem.h macros</a> </li> +<li><a href="#efunc">User level functions</a> </li> +</ul> +<h3><a href="qh-mem.htm#TOC">»</a><a name="etype">mem.h data types and constants</a></h3> +<ul> +<li><a href="mem.h#ptr_intT">ptr_intT</a> for casting +a void* to an integer-type </li> +<li><a href="mem.h#qhmemT">qhmemT</a> global memory +structure for mem.c </li> +<li><a href="mem.h#NOmem">qh_NOmem</a> disable memory allocation</li> +</ul> +<h3><a href="qh-mem.htm#TOC">»</a><a name="emacro">mem.h macros</a></h3> +<ul> +<li><a href="mem.h#memalloc_">qh_memalloc_</a> +allocate memory</li> +<li><a href="mem.h#memfree_">qh_memfree_</a> free +memory</li> +</ul> +<h3><a href="qh-mem.htm#TOC">»</a><a name="efunc">User level +functions</a></h3> +<ul> +<li><a href="mem.c#memalloc">qh_memalloc</a> allocate +memory </li> +<li><a href="mem.c#memcheck">qh_memcheck</a> +quick check of memory for internal consistency</li> +<li><a href="mem.c#memfree">qh_memfree</a> free +memory </li> +<li><a href="mem.c#meminit">qh_meminit</a> initialize +memory </li> +<li><a href="mem.c#memstatistics">qh_memstatistics</a> +print memory statistics </li> +<li><a href="mem.c#meminit">qh_memtotlong</a> return total, allocated long memory</li> +<li><a href="mem.c#NOmem">qh_NOmem</a> allocation routines with malloc() and free() +</ul> + +<h3><a href="qh-mem.htm#TOC">»</a><a name="m">Initialization and +termination functions</a></h3> +<ul> +<li><a href="mem.c#intcompare">qh_intcompare</a> used by +qsort and bsearch to compare two integers </li> +<li><a href="mem.c#memfreeshort">qh_memfreeshort</a> +frees up all short and qhmem memory allocations </li> +<li><a href="mem.c#meminit">qh_meminit</a> initialize +memory </li> +<li><a href="mem.c#meminitbuffers">qh_meminitbuffers</a> +initialize qhmem </li> +<li><a href="mem.c#memsetup">qh_memsetup</a> set up +memory after running memsize() </li> +<li><a href="mem.c#memsize">qh_memsize</a> define a free +list for this size </li> +<li><a href="mem.c#memstatistics">qh_memstatistics</a> +print out memory statistics </li> +</ul> + +<p><!-- Navigation links --> </p> +<hr> +<p><b>Up:</b> +<a href="http://www.qhull.org">Home page for +Qhull</a> <br> +<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual: Table of Contents</a> <br> +<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a> +• <a href="../../html/qh-quick.htm#options">Options</a> +• <a href="../../html/qh-opto.htm#output">Output</a> +• <a href="../../html/qh-optf.htm#format">Formats</a> +• <a href="../../html/qh-optg.htm#geomview">Geomview</a> +• <a href="../../html/qh-optp.htm#print">Print</a> +• <a href="../../html/qh-optq.htm#qhull">Qhull</a> +• <a href="../../html/qh-optc.htm#prec">Precision</a> +• <a href="../../html/qh-optt.htm#trace">Trace</a> +• <a href="index.htm">Functions</a><br> +<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a> <br> +<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br> +<b>To:</b> <a href="qh-geom.htm">Geom</a> • +<a href="qh-globa.htm">Global</a> • <a href="qh-io.htm">Io</a> +• <a href="qh-mem.htm">Mem</a> • <a href="qh-merge.htm">Merge</a> +• <a href="qh-poly.htm">Poly</a> • <a href="qh-qhull.htm#TOC">Qhull</a> +• <a href="qh-set.htm">Set</a> • <a href="qh-stat.htm">Stat</a> +• <a href="qh-user.htm">User</a><br> +</p> +<p><!-- GC common information --> </p> +<hr> +<p><a href="http://www.geom.uiuc.edu/"><img +src="../../html/qh--geom.gif" align="middle" width="40" height="40"></a><i>The +Geometry Center Home Page </i></p> +<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a> +</a><br> +Created: May 2, 1997 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p> +</body> +</html> diff --git a/src/qhull/src/libqhull/qh-merge.htm b/src/qhull/src/libqhull/qh-merge.htm new file mode 100644 index 000000000..54b97c88e --- /dev/null +++ b/src/qhull/src/libqhull/qh-merge.htm @@ -0,0 +1,366 @@ +<!-- Do not edit with Front Page, it adds too many spaces --> +<html> +<head> +<meta http-equiv="Content-Type" +content="text/html; charset=iso-8859-1"> +<title>merge.c -- facet merge operations</title> +</head> + +<body> +<!-- Navigation links --> +<p><a name="TOP"><b>Up:</b></a> <a +href="http://www.qhull.org">Home page</a> for Qhull<br> +<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual</a>: Table of Contents <br> +<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a> +• <a href="../../html/qh-quick.htm#options">Options</a> +• <a href="../../html/qh-opto.htm#output">Output</a> +• <a href="../../html/qh-optf.htm#format">Formats</a> +• <a href="../../html/qh-optg.htm#geomview">Geomview</a> +• <a href="../../html/qh-optp.htm#print">Print</a> +• <a href="../../html/qh-optq.htm#qhull">Qhull</a> +• <a href="../../html/qh-optc.htm#prec">Precision</a> +• <a href="../../html/qh-optt.htm#trace">Trace</a> +• <a href="index.htm">Functions</a><br> +<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a><br> +<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br> +<b>To:</b> <a href="qh-geom.htm">Geom</a> • <a href="qh-globa.htm">Global</a> +• <a href="qh-io.htm">Io</a> • <a href="qh-mem.htm">Mem</a> +• <a href="qh-merge.htm#TOC">Merge</a> • <a href="qh-poly.htm">Poly</a> +• <a href="qh-qhull.htm">Qhull</a> • <a href="qh-set.htm">Set</a> +• <a href="qh-stat.htm">Stat</a> • <a href="qh-user.htm">User</a> +</p> +<hr> + +<h2>merge.c -- facet merge operations</h2> +<blockquote> +<p>Qhull handles precision problems by merged facets or joggled input. +Except for redundant vertices, it corrects a problem by +merging two facets. When done, all facets are clearly +convex. See <a href="../../html/qh-impre.htm">Imprecision in Qhull</a> +for further information. </p> +<p>Users may joggle the input ('<a href="../../html/qh-optq.htm#QJn">QJn</a>') +instead of merging facets. </p> +<p>Qhull detects and corrects the following problems: </p> +<ul> +<li><b>More than two facets meeting at a ridge. </b>When +Qhull creates facets, it creates an even number +of facets for each ridge. A convex hull always +has two facets for each ridge. More than two +facets may be created if non-adjacent facets +share a vertex. This is called a <em>duplicate +ridge</em>. In 2-d, a duplicate ridge would +create a loop of facets. </li> +</ul> +<ul> +<li><b>A facet contained in another facet. </b>Facet +merging may leave all vertices of one facet as a +subset of the vertices of another facet. This is +called a <em>redundant facet</em>. </li> +</ul> +<ul> +<li><b>A facet with fewer than three neighbors. </b>Facet +merging may leave a facet with one or two +neighbors. This is called a <em>degenerate facet</em>. +</li> +</ul> +<ul> +<li><b>A facet with flipped orientation. </b>A +facet's hyperplane may define a halfspace that +does not include the interior point.This is +called a <em>flipped facet</em>. </li> +</ul> +<ul> +<li><strong>A coplanar horizon facet.</strong> A +newly processed point may be coplanar with an +horizon facet. Qhull creates a new facet without +a hyperplane. It links new facets for the same +horizon facet together. This is called a <em>samecycle</em>. +The new facet or samecycle is merged into the +horizon facet. </li> +</ul> +<ul> +<li><b>Concave facets. </b>A facet's centrum may be +above a neighboring facet. If so, the facets meet +at a concave angle. </li> +</ul> +<ul> +<li><b>Coplanar facets. </b>A facet's centrum may be +coplanar with a neighboring facet (i.e., it is +neither clearly below nor clearly above the +facet's hyperplane). Qhull removes coplanar +facets in independent sets sorted by angle.</li> +</ul> +<ul> +<li><b>Redundant vertex. </b>A vertex may have fewer +than three neighboring facets. If so, it is +redundant and may be renamed to an adjacent +vertex without changing the topological +structure.This is called a <em>redundant vertex</em>. +</li> +</ul> +</blockquote> +<p><b>Copyright © 1995-2015 C.B. Barber</b></p> +<hr> +<p><a href="#TOP">»</a> <a href="qh-geom.htm#TOC">Geom</a> +<a name="TOC">•</a> <a href="qh-globa.htm#TOC">Global</a> +• <a href="qh-io.htm#TOC">Io</a> • <a href="qh-mem.htm#TOC">Mem</a> +• <b>Merge</b> • <a href="qh-poly.htm#TOC">Poly</a> +• <a href="qh-qhull.htm#TOC">Qhull</a> • <a href="qh-set.htm#TOC">Set</a> +• <a href="qh-stat.htm#TOC">Stat</a> • <a href="qh-user.htm#TOC">User</a> +</p> +<h3>Index to <a href="merge.c">merge.c</a> and +<a href="merge.h">merge.h</a></h3> +<ul> +<li><a href="#mtype">merge.h data types, macros, and +global sets</a> </li> +<li><a href="#mconst">merge.h constants</a> </li> +</ul> +<ul> +<li><a href="#mtop">top-level merge functions</a> </li> +<li><a href="#mset">functions for identifying merges</a></li> +<li><a href="#mbest">functions for determining the +best merge</a> </li> +<li><a href="#mmerge">functions for merging facets</a> +</li> +<li><a href="#mcycle">functions for merging a cycle +of facets</a> </li> +<li><a href="#mrename">functions for renaming a +vertex</a> </li> +<li><a href="#mvertex">functions for identifying +vertices for renaming</a> </li> +<li><a href="#mcheck">functions for check and trace</a> </li> +</ul> +<h3><a href="qh-merge.htm#TOC">»</a><a name="mtype">merge.h data +types, macros, and global sets</a></h3> +<ul> +<li><a href="merge.h#mergeT">mergeT</a> structure to +identify a merge of two facets</li> +<li><a href="merge.h#FOREACHmerge_">FOREACHmerge_</a> +assign 'merge' to each merge in merges </li> +<li><a href="libqhull.h#qh-set">qh global sets</a> +qh.facet_mergeset contains non-convex merges +while qh.degen_mergeset contains degenerate and +redundant facets</li> +</ul> +<h3><a href="qh-merge.htm#TOC">»</a><a name="mconst">merge.h +constants</a></h3> +<ul> +<li><a href="libqhull.h#qh-prec">qh precision constants</a> +precision constants for Qhull </li> +<li><a href="merge.h#MRG">MRG...</a> indicates the +type of a merge (mergeT->type)</li> +<li><a href="merge.h#qh_ANGLEredundant">qh_ANGLEredundant</a> +indicates redundant merge in mergeT->angle </li> +<li><a href="merge.h#qh_ANGLEdegen">qh_ANGLEdegen</a> +indicates degenerate facet in mergeT->angle </li> +<li><a href="merge.h#qh_ANGLEconcave">qh_ANGLEconcave</a> +offset to indicate concave facets in +mergeT->angle </li> +<li><a href="merge.h#qh_MERGEapex">qh_MERGEapex</a> +flag for qh_mergefacet() to indicate an apex +merge </li> +</ul> +<h3><a href="qh-merge.htm#TOC">»</a><a name="mtop">top-level merge +functions</a></h3> +<ul> +<li><a href="merge.c#all_merges">qh_all_merges</a> +merge all non-convex facets </li> +<li><a href="merge.c#checkzero">qh_checkzero</a> +check that facets are clearly convex </li> +<li><a href="merge.c#flippedmerges">qh_flippedmerges</a> +merge flipped facets into best neighbor </li> +<li><a href="merge.c#forcedmerges">qh_forcedmerges</a> +merge all duplicate ridges </li> +<li><a href="merge.c#merge_degenredundant">qh_merge_degenredundant</a> +merge degenerate and redundant facets </li> +<li><a href="merge.c#merge_nonconvex">qh_merge_nonconvex</a> +merge a non-convex ridge </li> +<li><a href="merge.c#premerge">qh_premerge</a> +pre-merge non-convex facets </li> +<li><a href="merge.c#postmerge">qh_postmerge</a> +post-merge nonconvex facets as defined by +maxcentrum/maxangle </li> +</ul> + +<h3><a href="qh-merge.htm#TOC">»</a><a name="mset">functions for +identifying merges</a></h3> +<ul> +<li><a href="merge.c#appendmergeset">qh_appendmergeset</a> +appends an entry to qh.facet_mergeset</li> +<li><a href="merge.c#compareangle">qh_compareangle</a> +used by qsort() to order merges </li> +<li><a href="merge.c#comparemerge">qh_comparemerge</a> +used by qsort() to order merges </li> +<li><a href="merge.c#degen_redundant_facet">qh_degen_redundant_facet</a> +check for a degenerate and redundant facet</li> +<li><a href="merge.c#degen_redundant_neighbors">qh_degen_redundant_neighbors</a> +append degenerate and redundant neighbors to +qh.degen_mergeset </li> +<li><a href="merge.c#getmergeset_initial">qh_getmergeset_initial</a> +build initial qh.facet_mergeset </li> +<li><a href="merge.c#getmergeset">qh_getmergeset</a> +update qh.facet_mergeset </li> +<li><a href="merge.c#mark_dupridges">qh_mark_dupridges</a> +add duplicated ridges to qh.facet_mergeset</li> +<li><a href="merge.c#maydropneighbor">qh_maydropneighbor</a> +drop neighbor relationship if no ridge between +facet and neighbor </li> +<li><a href="merge.c#test_appendmerge">qh_test_appendmerge</a> +test a pair of facets for convexity and append to +qh.facet_mergeset if non-convex </li> +<li><a href="merge.c#test_vneighbors">qh_test_vneighbors</a> +test vertex neighbors for convexity </li> +</ul> + +<h3><a href="qh-merge.htm#TOC">»</a><a name="mbest">functions for +determining the best merge</a></h3> +<ul> +<li><a href="merge.c#findbest_test">qh_findbest_test</a> +test neighbor for best merge </li> +<li><a href="merge.c#findbestneighbor">qh_findbestneighbor</a> +finds best neighbor of a facet for merging (i.e., +closest hyperplane) </li> +</ul> + +<h3><a href="qh-merge.htm#TOC">»</a><a name="mmerge">functions for +merging facets</a></h3> +<ul> +<li><a href="merge.c#copynonconvex">qh_copynonconvex</a> +copy non-convex flag to another ridge for the +same neighbor </li> +<li><a href="merge.c#makeridges">qh_makeridges</a> +creates explicit ridges between simplicial facets +</li> +<li><a href="merge.c#mergefacet">qh_mergefacet</a> +merges one facet into another facet</li> +<li><a href="merge.c#mergeneighbors">qh_mergeneighbors</a> +merges the neighbors of two facets </li> +<li><a href="merge.c#mergeridges">qh_mergeridges</a> +merges the ridge sets of two facets </li> +<li><a href="merge.c#mergesimplex">qh_mergesimplex</a> +merge a simplicial facet into another simplicial +facet </li> +<li><a href="merge.c#mergevertex_del">qh_mergevertex_del</a> +delete a vertex due to merging one facet into +another facet </li> +<li><a href="merge.c#mergevertex_neighbors">qh_mergevertex_neighbors</a> +merge the vertex neighbors of two facets </li> +<li><a href="merge.c#mergevertices">qh_mergevertices</a> +merge the vertex sets of two facets </li> +<li><a href="merge.c#newvertices">qh_newvertices</a> +register all vertices as new vertices </li> +<li><a href="merge.c#updatetested">qh_updatetested</a> +clear tested flags and centrums involved in a +merge </li> +<li><a href="merge.c#willdelete">qh_willdelete</a> +moves facet to qh.visible_list; sets replacement +or NULL </li> +</ul> + +<h3><a href="qh-merge.htm#TOC">»</a><a name="mcycle">functions for +merging a cycle of facets</a></h3> +<p>If a point is coplanar with an horizon facet, the +corresponding new facets are linked together (a <em>samecycle</em>) +for merging.</p> +<ul> +<li><a href="merge.c#basevertices">qh_basevertices</a> +return temporary set of base vertices for a +samecycle </li> +<li><a href="merge.c#mergecycle">qh_mergecycle</a> +merge a samecycle into a horizon facet </li> +<li><a href="merge.c#mergecycle_all">qh_mergecycle_all</a> +merge all samecycles into horizon facets</li> +<li><a href="merge.c#mergecycle_facets">qh_mergecycle_facets</a> +finish merge of samecycle </li> +<li><a href="merge.c#mergecycle_neighbors">qh_mergecycle_neighbors</a> +merge neighbor sets for samecycle </li> +<li><a href="merge.c#mergecycle_ridges">qh_mergecycle_ridges</a> +merge ridge sets for samecycle </li> +<li><a href="merge.c#mergecycle_vneighbors">qh_mergecycle_vneighbors</a> +merge vertex neighbor sets for samecycle </li> +</ul> +<h3><a href="qh-merge.htm#TOC">»</a><a name="mrename">functions +for renaming a vertex</a></h3> +<ul> +<li><a href="merge.c#comparevisit">qh_comparevisit</a> +used by qsort() to order vertices by visitid</li> +<li><a href="merge.c#reducevertices">qh_reducevertices</a> +reduce vertex sets </li> +<li><a href="merge.c#redundant_vertex">qh_redundant_vertex</a> +returns true if detect and rename redundant +vertex </li> +<li><a href="merge.c#rename_sharedvertex">qh_rename_sharedvertex</a> +detect and rename a shared vertex </li> +<li><a href="merge.c#renameridgevertex">qh_renameridgevertex</a> +rename oldvertex to newvertex in a ridge </li> +<li><a href="merge.c#renamevertex">qh_renamevertex</a> +rename oldvertex to newvertex in ridges </li> +<li><a href="merge.c#remove_extravertices">qh_remove_extravertices</a> +remove extra vertices in non-simplicial facets </li> +</ul> + +<h3><a href="qh-merge.htm#TOC">»</a><a name="mvertex">functions +for identifying vertices for renaming</a></h3> +<ul> +<li><a href="merge.c#find_newvertex">qh_find_newvertex</a> +locate new vertex for renaming old vertex </li> +<li><a href="merge.c#hashridge">qh_hashridge</a> add +ridge to hashtable </li> +<li><a href="merge.c#hashridge_find">qh_hashridge_find</a> +returns matching ridge in hashtable</li> +<li><a href="merge.c#neighbor_intersections">qh_neighbor_intersections</a> +return intersection of vertex sets for +neighboring facets </li> +<li><a href="merge.c#vertexridges">qh_vertexridges</a> +return temporary set of ridges adjacent to a +vertex </li> +<li><a href="merge.c#vertexridges_facet">qh_vertexridges_facet</a> +add adjacent ridges for a vertex in facet </li> +</ul> + +<h3><a href="qh-merge.htm#TOC">»</a><a name="mcheck">functions for check and +trace</a></h3> +<ul> +<li><a href="merge.c#checkconnect">qh_checkconnect</a> +check that new facets are connected </li> +<li><a href="merge.c#tracemerge">qh_tracemerge</a> +print trace message after merge </li> +<li><a href="merge.c#tracemerging">qh_tracemerging</a> +print trace message during post-merging </li> +</ul> + +<p><!-- Navigation links --> </p> +<hr> +<p><b>Up:</b> +<a href="http://www.qhull.org">Home page for +Qhull</a> <br> +<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual: Table of Contents</a> <br> +<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a> +• <a href="../../html/qh-quick.htm#options">Options</a> +• <a href="../../html/qh-opto.htm#output">Output</a> +• <a href="../../html/qh-optf.htm#format">Formats</a> +• <a href="../../html/qh-optg.htm#geomview">Geomview</a> +• <a href="../../html/qh-optp.htm#print">Print</a> +• <a href="../../html/qh-optq.htm#qhull">Qhull</a> +• <a href="../../html/qh-optc.htm#prec">Precision</a> +• <a href="../../html/qh-optt.htm#trace">Trace</a> +• <a href="index.htm">Functions</a><br> +<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a> <br> +<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br> +<b>To:</b> <a href="qh-geom.htm">Geom</a> • +<a href="qh-globa.htm">Global</a> • <a href="qh-io.htm">Io</a> +• <a href="qh-mem.htm">Mem</a> • <a href="qh-merge.htm">Merge</a> +• <a href="qh-poly.htm">Poly</a> • <a href="qh-qhull.htm#TOC">Qhull</a> +• <a href="qh-set.htm">Set</a> • <a href="qh-stat.htm">Stat</a> +• <a href="qh-user.htm">User</a><br> +</p> +<p><!-- GC common information --> </p> +<hr> +<p><a href="http://www.geom.uiuc.edu/"><img +src="../../html/qh--geom.gif" align="middle" width="40" height="40"></a><i>The +Geometry Center Home Page </i></p> +<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a> +</a><br> +Created: May 2, 1997 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p> +</body> +</html> diff --git a/src/qhull/src/libqhull/qh-poly.htm b/src/qhull/src/libqhull/qh-poly.htm new file mode 100644 index 000000000..c8f6b38b0 --- /dev/null +++ b/src/qhull/src/libqhull/qh-poly.htm @@ -0,0 +1,485 @@ +<!-- Do not edit with Front Page, it adds too many spaces --> +<html> +<head> +<meta http-equiv="Content-Type" +content="text/html; charset=iso-8859-1"> +<title>poly.c, poly2.c -- polyhedron operations</title> +</head> + +<body> +<!-- Navigation links --> +<p><a name="TOP"><b>Up:</b></a> <a +href="http://www.qhull.org">Home page</a> for Qhull<br> +<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual</a>: Table of Contents <br> +<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a> +• <a href="../../html/qh-quick.htm#options">Options</a> +• <a href="../../html/qh-opto.htm#output">Output</a> +• <a href="../../html/qh-optf.htm#format">Formats</a> +• <a href="../../html/qh-optg.htm#geomview">Geomview</a> +• <a href="../../html/qh-optp.htm#print">Print</a> +• <a href="../../html/qh-optq.htm#qhull">Qhull</a> +• <a href="../../html/qh-optc.htm#prec">Precision</a> +• <a href="../../html/qh-optt.htm#trace">Trace</a> +• <a href="index.htm">Functions</a><br> +<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a><br> +<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br> +<b>To:</b> <a href="qh-geom.htm">Geom</a> • <a href="qh-globa.htm">Global</a> +• <a href="qh-io.htm">Io</a> • <a href="qh-mem.htm">Mem</a> +• <a href="qh-merge.htm">Merge</a> • <a href="qh-poly.htm#TOC">Poly</a> +• <a href="qh-qhull.htm">Qhull</a> • <a href="qh-set.htm">Set</a> +• <a href="qh-stat.htm">Stat</a> • <a href="qh-user.htm">User</a> +</p> +<hr> + +<h2>poly.c, poly2.c -- polyhedron operations</h2> +<blockquote> + +<p>Qhull uses dimension-free terminology. Qhull builds a +polyhedron in dimension <em>d. </em>A <em>polyhedron</em> is a +simplicial complex of faces with geometric information for the +top and bottom-level faces. A (<em>d-1</em>)-face is a <em>facet</em>, +a (<em>d-2</em>)-face is a <em>ridge</em>, and a <em>0</em>-face +is a <em>vertex</em>. For example in 3-d, a facet is a polygon +and a ridge is an edge. A facet is built from a ridge (the <em>base</em>) +and a vertex (the <em>apex</em>). See +<a href="../../html/index.htm#structure">Qhull's data structures</a>.</p> + +<p>Qhull's primary data structure is a polyhedron. A +polyhedron is a list of facets. Each facet has a set of +neighboring facets and a set of vertices. Each facet has a +hyperplane. For example, a tetrahedron has four facets. +If its vertices are <em>a, b, c, d</em>, and its facets +are <em>1, 2, 3, 4,</em> the tetrahedron is </p> +<blockquote> +<ul> +<li>facet 1 <ul> + <li>vertices: b c d </li> + <li>neighbors: 2 3 4 </li> +</ul> +</li> +<li>facet 2 <ul> + <li>vertices: a c d </li> + <li>neighbors: 1 3 4 </li> +</ul> +</li> +<li>facet 3 <ul> + <li>vertices: a b d </li> + <li>neighbors: 1 2 4 </li> +</ul> +</li> +<li>facet 4 <ul> + <li>vertices: a b c </li> + <li>neighbors: 1 2 3 </li> +</ul> +</li> +</ul> +</blockquote> +<p>A facet may be simplicial or non-simplicial. In 3-d, a +<i>simplicial facet</i> has three vertices and three +neighbors. A <i>nonsimplicial facet</i> has more than +three vertices and more than three neighbors. A +nonsimplicial facet has a set of ridges and a centrum. </p> +<p> +A simplicial facet has an orientation. An <i>orientation</i> +is either <i>top</i> or <i>bottom</i>. +The flag, <tt>facet->toporient,</tt> +defines the orientation of the facet's vertices. For example in 3-d, +'top' is left-handed orientation (i.e., the vertex order follows the direction +of the left-hand fingers when the thumb is pointing away from the center). +Except for axis-parallel facets in 5-d and higher, topological orientation +determines the geometric orientation of the facet's hyperplane. + +<p>A nonsimplicial facet is due to merging two or more +facets. The facet's ridge set determine a simplicial +decomposition of the facet. Each ridge is a 1-face (i.e., +it has two vertices and two neighboring facets). The +orientation of a ridge is determined by the order of the +neighboring facets. The flag, <tt>facet->toporient,</tt>is +ignored. </p> +<p>A nonsimplicial facet has a centrum for testing +convexity. A <i>centrum</i> is a point on the facet's +hyperplane that is near the center of the facet. Except +for large facets, it is the arithmetic average of the +facet's vertices. </p> +<p>A nonsimplicial facet is an approximation that is +defined by offsets from the facet's hyperplane. When +Qhull finishes, the <i>outer plane</i> is above all +points while the <i>inner plane</i> is below the facet's +vertices. This guarantees that any exact convex hull +passes between the inner and outer planes. The outer +plane is defined by <tt>facet->maxoutside</tt> while +the inner plane is computed from the facet's vertices.</p> + +<p>Qhull 3.1 includes triangulation of non-simplicial facets +('<a href="../../html/qh-optq.htm#Qt">Qt</a>'). +These facets, +called <i>tricoplanar</i>, share the same normal. centrum, and Voronoi center. +One facet (keepcentrum) owns these data structures. +While tricoplanar facets are more accurate than the simplicial facets from +joggled input, they +may have zero area or flipped orientation. + +</blockquote> +<p><b>Copyright © 1995-2015 C.B. Barber</b></p> +<hr> +<p><a href="#TOP">»</a> <a href="qh-geom.htm#TOC">Geom</a> +<a name="TOC">•</a> <a href="qh-globa.htm#TOC">Global</a> +• <a href="qh-io.htm#TOC">Io</a> • <a href="qh-mem.htm#TOC">Mem</a> +• <a href="qh-merge.htm#TOC">Merge</a> • <b>Poly</b> +• <a href="qh-qhull.htm#TOC">Qhull</a> • <a href="qh-set.htm#TOC">Set</a> +• <a href="qh-stat.htm#TOC">Stat</a> • <a href="qh-user.htm#TOC">User</a> +</p> +<h3>Index to <a href="poly.c">poly.c</a>, +<a href="poly2.c">poly2.c</a>, <a href="poly.h">poly.h</a>, +and <a href="libqhull.h">libqhull.h</a></h3> +<ul> +<li><a href="#ptype">Data types and global +lists for polyhedrons</a> </li> +<li><a href="#pconst">poly.h constants</a> </li> +<li><a href="#pgall">Global FORALL macros</a> </li> +<li><a href="#pall">FORALL macros</a> </li> +<li><a href="#peach">FOREACH macros</a> </li> +<li><a href="#pieach">Indexed FOREACH macros</a> </li> +<li><a href="#pmacro">Other macros for polyhedrons</a><p> </li> +<li><a href="#plist">Facetlist functions</a> </li> +<li><a href="#pfacet">Facet functions</a> </li> +<li><a href="#pvertex">Vertex, ridge, and point +functions</a> </li> +<li><a href="#phash">Hashtable functions</a> </li> +<li><a href="#pnew">Allocation and deallocation +functions</a> </li> +<li><a href="#pcheck">Check functions</a> </li> +</ul> +<h3><a href="qh-poly.htm#TOC">»</a><a name="ptype">Data +types and global lists for polyhedrons</a></h3> +<ul> +<li><a href="libqhull.h#facetT">facetT</a> defines a +facet </li> +<li><a href="libqhull.h#ridgeT">ridgeT</a> defines a +ridge </li> +<li><a href="libqhull.h#vertexT">vertexT</a> defines a +vertex </li> +<li><a href="libqhull.h#qh-lists">qh facet and vertex +lists</a> lists of facets and vertices </li> +<li><a href="libqhull.h#qh-set">qh global sets</a> +global sets for merging, hashing, input, etc. </li> +</ul> +<h3><a href="qh-poly.htm#TOC">»</a><a name="pconst">poly.h constants</a></h3> +<ul> +<li><a href="poly.h#ALGORITHMfault">ALGORITHMfault</a> +flag to not report errors in qh_checkconvex() </li> +<li><a href="poly.h#DATAfault">DATAfault</a> flag to +report errors in qh_checkconvex() </li> +<li><a href="poly.h#DUPLICATEridge">DUPLICATEridge</a> +special value for facet->neighbor to indicate +a duplicate ridge </li> +<li><a href="poly.h#MERGEridge">MERGEridge</a> +special value for facet->neighbor to indicate +a merged ridge </li> +</ul> +<h3><a href="qh-poly.htm#TOC">»</a><a name="pgall">Global FORALL +macros</a></h3> +<ul> +<li><a href="libqhull.h#FORALLfacets">FORALLfacets</a> +assign 'facet' to each facet in qh.facet_list </li> +<li><a href="poly.h#FORALLnew_facets">FORALLnew_facets</a> +assign 'facet' to each facet in qh.newfacet_list </li> +<li><a href="poly.h#FORALLvisible_facets">FORALLvisible_facets</a> +assign 'visible' to each visible facet in +qh.visible_list </li> +<li><a href="libqhull.h#FORALLpoints">FORALLpoints</a> +assign 'point' to each point in qh.first_point, +qh.num_points </li> +<li><a href="libqhull.h#FORALLvertices">FORALLvertices</a> +assign 'vertex' to each vertex in qh.vertex_list </li> +</ul> +<h3><a href="qh-poly.htm#TOC">»</a><a name="pall">FORALL macros</a></h3> +<ul> +<li><a href="poly.h#FORALLfacet_">FORALLfacet_</a> +assign 'facet' to each facet in facetlist </li> +<li><a href="libqhull.h#FORALLpoint_">FORALLpoint_</a> +assign 'point' to each point in points array</li> +<li><a href="poly.h#FORALLsame_">FORALLsame_</a> +assign 'same' to each facet in samecycle</li> +<li><a href="poly.h#FORALLsame_cycle_">FORALLsame_cycle_</a> +assign 'same' to each facet in samecycle</li> +<li><a href="poly.h#FORALLvertex_">FORALLvertex_</a> +assign 'vertex' to each vertex in vertexlist </li> +</ul> +<h3><a href="qh-poly.htm#TOC">»</a><a name="peach">FOREACH macros</a></h3> +<ul> +<li><a href="libqhull.h#FOREACHfacet_">FOREACHfacet_</a> +assign 'facet' to each facet in facets </li> +<li><a href="libqhull.h#FOREACHneighbor_">FOREACHneighbor_</a> +assign 'neighbor' to each facet in +facet->neighbors or vertex->neighbors</li> +<li><a href="poly.h#FOREACHnewfacet_">FOREACHnewfacet_</a> +assign 'newfacet' to each facet in facet set </li> +<li><a href="libqhull.h#FOREACHpoint_">FOREACHpoint_</a> +assign 'point' to each point in points set </li> +<li><a href="libqhull.h#FOREACHridge_">FOREACHridge_</a> +assign 'ridge' to each ridge in ridge set </li> +<li><a href="libqhull.h#FOREACHvertex_">FOREACHvertex_</a> +assign 'vertex' to each vertex in vertex set </li> +<li><a href="poly.h#FOREACHvertexA_">FOREACHvertexA_</a> +assign 'vertexA' to each vertex in vertex set</li> +<li><a href="poly.h#FOREACHvisible_">FOREACHvisible_</a> +assign 'visible' to each facet in facet set </li> +</ul> +<h3><a href="qh-poly.htm#TOC">»</a><a name="pieach">Indexed +FOREACH macros</a></h3> +<ul> +<li><a href="libqhull.h#FOREACHfacet_i_">FOREACHfacet_i_</a> +assign 'facet' and 'facet_i' to each facet in +facet set </li> +<li><a href="libqhull.h#FOREACHneighbor_i_">FOREACHneighbor_i_</a> +assign 'neighbor' and 'neighbor_i' to each facet +in facet->neighbors or vertex->neighbors</li> +<li><a href="libqhull.h#FOREACHpoint_i_">FOREACHpoint_i_</a> +assign 'point' and 'point_i' to each point in +points set </li> +<li><a href="libqhull.h#FOREACHridge_i_">FOREACHridge_i_</a> +assign 'ridge' and 'ridge_i' to each ridge in +ridges set </li> +<li><a href="libqhull.h#FOREACHvertex_i_">FOREACHvertex_i_</a> +assign 'vertex' and 'vertex_i' to each vertex in +vertices set </li> +<li><a href="poly.h#FOREACHvertexreverse12_">FOREACHvertexreverse12_</a> +assign 'vertex' to each vertex in vertex set; +reverse the order of first two vertices </li> +</ul> +<h3><a href="qh-poly.htm#TOC">»</a><a name="pmacro">Other macros for polyhedrons</a></h3> +<ul> +<li><a href="libqhull.h#getid_">getid_</a> return ID for +a facet, ridge, or vertex </li> +<li><a href="libqhull.h#otherfacet_">otherfacet_</a> +return neighboring facet for a ridge in a facet </li> +</ul> +<h3><a href="qh-poly.htm#TOC">»</a><a name="plist">Facetlist +functions</a></h3> +<ul> +<li><a href="poly.c#appendfacet">qh_appendfacet</a> +appends facet to end of qh.facet_list</li> +<li><a href="poly.c#attachnewfacets">qh_attachnewfacets</a> +attach new facets in qh.newfacet_list to the +horizon </li> +<li><a href="poly2.c#findgood">qh_findgood</a> +identify good facets for qh.PRINTgood </li> +<li><a href="poly2.c#findgood_all">qh_findgood_all</a> +identify more good facets for qh.PRINTgood </li> +<li><a href="poly2.c#furthestnext">qh_furthestnext</a> +move facet with furthest of furthest points to +facet_next </li> +<li><a href="poly2.c#initialhull">qh_initialhull</a> +construct the initial hull as a simplex of +vertices </li> +<li><a href="poly2.c#nearcoplanar">qh_nearcoplanar</a> + remove near-inside points from coplanar sets</li> +<li><a href="poly2.c#prependfacet">qh_prependfacet</a> +prepends facet to start of facetlist </li> +<li><a href="user.c#printfacetlist">qh_printfacetlist</a> +print facets in a facetlist</li> +<li><a href="poly2.c#printlists">qh_printlists</a> +print out facet list for debugging </li> +<li><a href="poly.c#removefacet">qh_removefacet</a> +unlinks facet from qh.facet_list</li> +<li><a href="poly2.c#resetlists">qh_resetlists</a> +reset qh.newvertex_list, qh.newfacet_list, and +qh.visible_list </li> +</ul> +<h3><a href="qh-poly.htm#TOC">»</a><a name="pfacet">Facet +functions</a></h3> +<ul> +<li><a href="poly2.c#createsimplex">qh_createsimplex</a> +create a simplex of facets from a set of vertices +</li> +<li><a href="poly2.c#findbestlower">qh_findbestlower</a> find best +non-upper, non-flipped facet for point at upperfacet</li> +<li><a href="poly2.c#furthestout">qh_furthestout</a> +make furthest outside point the last point of a +facet's outside set </li> +<li><a href="poly.c#makenew_nonsimplicial">qh_makenew_nonsimplicial</a> +make new facets from ridges of visible facets </li> +<li><a href="poly.c#makenew_simplicial">qh_makenew_simplicial</a> +make new facets for horizon neighbors </li> +<li><a href="poly.c#makenewfacet">qh_makenewfacet</a> +create a facet from vertices and apex </li> +<li><a href="poly2.c#makenewfacets">qh_makenewfacets</a> +make new facets from vertex, horizon facets, and +visible facets </li> +<li><a href="poly.c#makenewplanes">qh_makenewplanes</a> +make new hyperplanes for facets </li> +<li><a href="poly2.c#outcoplanar">qh_outcoplanar</a> +move points from outside set to coplanar set </li> +<li><a href="poly2.c#setvoronoi_all">qh_setvoronoi_all</a> +compute Voronoi centers for all facets </li> +<li><a href="poly2.c#triangulate">qh_triangulate</a> +triangulate non-simplicial facets</li> +<li><a href="poly2.c#triangulate_facet">qh_triangulate_facet</a> +triangulate a non-simplicial facet</li> +<li><a href="poly2.c#triangulate_link">qh_triangulate_link</a> +link facets together from qh_triangulate</li> +<li><a href="poly2.c#triangulate_mirror">qh_triangulate_mirror</a> +delete mirrored facets from qh_triangulate</li> +<li><a href="poly2.c#triangulate_null">qh_triangulate_null</a> +delete null facet from qh_triangulate</li> +</ul> +<h3><a href="qh-poly.htm#TOC">»</a><a name="pvertex">Vertex, +ridge, and point functions</a></h3> +<ul> +<li><a href="poly.c#appendvertex">qh_appendvertex</a> +append vertex to end of qh.vertex_list, </li> +<li><a href="io.c#detvridge">qh_detvridge</a> determine Voronoi +ridge for an input site +<li><a href="io.c#detvridge3">qh_detvridge3</a> determine 3-d Voronoi +ridge for an input site +<li><a href="poly2.c#facet3vertex">qh_facet3vertex</a> +return an oriented vertex set for a 3-d facet </li> +<li><a href="poly.c#facetintersect">qh_facetintersect</a> +return intersection of simplicial facets </li> +<li><a href="poly2.c#initialvertices">qh_initialvertices</a> +return non-singular set of initial vertices </li> +<li><a href="poly2.c#isvertex">qh_isvertex</a> true +if point is in a vertex set </li> +<li><a href="poly2.c#nearvertex">qh_nearvertex</a> +return nearest vertex to point </li> +<li><a href="poly2.c#nextridge3d">qh_nextridge3d</a> +iterate over each ridge and vertex for a 3-d +facet </li> +<li><a href="poly2.c#point">qh_point</a> return point +for a point ID </li> +<li><a href="poly2.c#pointfacet">qh_pointfacet</a> +return temporary set of facets indexed by point +ID </li> +<li><a href="poly.c#pointid">qh_pointid</a> return ID +for a point</li> +<li><a href="poly2.c#pointvertex">qh_pointvertex</a> +return temporary set of vertices indexed by point +ID </li> +<li><a href="poly.c#removevertex">qh_removevertex</a> +unlink vertex from qh.vertex_list, </li> +<li><a href="poly.c#updatevertices">qh_updatevertices</a> +update vertex neighbors and delete interior +vertices </li> +<li><a href="poly2.c#vertexintersect">qh_vertexintersect</a> +intersect two vertex sets </li> +<li><a href="poly2.c#vertexintersect_new">qh_vertexintersect_new</a> +return intersection of two vertex sets </li> +<li><a href="poly2.c#vertexneighbors">qh_vertexneighbors</a> +for each vertex in hull, determine facet +neighbors </li> +<li><a href="poly2.c#vertexsubset">qh_vertexsubset</a> +returns True if vertexsetA is a subset of +vertexsetB </li> +</ul> +<h3><a href="qh-poly.htm#TOC">»</a><a name="phash">Hashtable functions</a></h3> +<ul> +<li><a href="poly2.c#addhash">qh_addhash</a> add hash +element to linear hash table</li> +<li><a href="poly.c#gethash">qh_gethash</a> return +hash value for a set</li> +<li><a href="poly2.c#matchduplicates">qh_matchduplicates</a> +match duplicate ridges in hash table </li> +<li><a href="poly.c#matchneighbor">qh_matchneighbor</a> +try to match subridge of new facet with a +neighbor </li> +<li><a href="poly.c#matchnewfacets">qh_matchnewfacets</a> +match new facets with their new facet neighbors </li> +<li><a href="poly.c#matchvertices">qh_matchvertices</a> +tests whether a facet and hash entry match at a +ridge </li> +<li><a href="poly2.c#newhashtable">qh_newhashtable</a> +allocate a new qh.hash_table </li> +<li><a href="poly2.c#printhashtable">qh_printhashtable</a> +print hash table </li> +</ul> +<h3><a href="qh-poly.htm#TOC">»</a><a name="pnew">Allocation and +deallocation functions</a></h3> +<ul> +<li><a href="poly2.c#clearcenters">qh_clearcenters</a> +clear old data from facet->center </li> +<li><a href="poly.c#deletevisible">qh_deletevisible</a> +delete visible facets and vertices </li> +<li><a href="poly.c#delfacet">qh_delfacet</a> free up +the memory occupied by a facet </li> +<li><a href="poly2.c#delridge">qh_delridge</a> delete +ridge</li> +<li><a href="poly2.c#delvertex">qh_delvertex</a> +delete vertex </li> +<li><a href="poly.c#newfacet">qh_newfacet</a> create +and allocate space for a facet </li> +<li><a href="poly.c#newridge">qh_newridge</a> create +and allocate space for a ridge </li> +<li><a href="poly2.c#newvertex">qh_newvertex</a> +create and allocate space for a vertex </li> +</ul> +<h3><a href="qh-poly.htm#TOC">»</a><a name="pcheck">Check +functions</a></h3> +<ul> +<li><a href="poly2.c#check_bestdist">qh_check_bestdist</a> +check that points are not outside of facets </li> +<li><a href="poly2.c#check_dupridge">qh_check_dupridge</a> +check duplicate ridge between facet1 and facet2 for wide merge </li> +<li><a href="poly2.c#check_maxout">qh_check_maxout</a> +updates qh.max_outside and checks all points +against bestfacet </li> +<li><a href="poly2.c#check_output">qh_check_output</a> +check topological and geometric output</li> +<li><a href="poly2.c#check_point">qh_check_point</a> +check that point is not outside of facet </li> +<li><a href="poly2.c#check_points">qh_check_points</a> +check that all points are inside all facets </li> +<li><a href="poly2.c#checkconvex">qh_checkconvex</a> +check that each ridge in facetlist is convex </li> +<li><a href="poly2.c#checkfacet">qh_checkfacet</a> +check for consistency errors in facet </li> +<li><a href="poly.c#checkflipped">qh_checkflipped</a> +check facet orientation to the interior point </li> +<li><a href="poly2.c#checkflipped_all">qh_checkflipped_all</a> +check facet orientation for a facet list </li> +<li><a href="poly2.c#checkpolygon">qh_checkpolygon</a> +check topological structure </li> +<li><a href="poly2.c#checkvertex">qh_checkvertex</a> +check vertex for consistency </li> +<li><a href="poly2.c#infiniteloop">qh_infiniteloop</a> +report error for a loop of facets </li> +<li><a href="poly2.c#printlists">qh_printlists</a> +print out facet list for debugging </li> +</ul> + + +<p><!-- Navigation links --> </p> +<hr> +<p><b>Up:</b> +<a href="http://www.qhull.org">Home page for +Qhull</a> <br> +<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual: Table of Contents</a> <br> +<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a> +• <a href="../../html/qh-quick.htm#options">Options</a> +• <a href="../../html/qh-opto.htm#output">Output</a> +• <a href="../../html/qh-optf.htm#format">Formats</a> +• <a href="../../html/qh-optg.htm#geomview">Geomview</a> +• <a href="../../html/qh-optp.htm#print">Print</a> +• <a href="../../html/qh-optq.htm#qhull">Qhull</a> +• <a href="../../html/qh-optc.htm#prec">Precision</a> +• <a href="../../html/qh-optt.htm#trace">Trace</a> +• <a href="index.htm">Functions</a><br> +<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a> <br> +<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br> +<b>To:</b> <a href="qh-geom.htm">Geom</a> • +<a href="qh-globa.htm">Global</a> • <a href="qh-io.htm">Io</a> +• <a href="qh-mem.htm">Mem</a> • <a href="qh-merge.htm">Merge</a> +• <a href="qh-poly.htm">Poly</a> • <a href="qh-qhull.htm#TOC">Qhull</a> +• <a href="qh-set.htm">Set</a> • <a href="qh-stat.htm">Stat</a> +• <a href="qh-user.htm">User</a><br> +</p> +<p><!-- GC common information --> </p> +<hr> +<p><a href="http://www.geom.uiuc.edu/"><img +src="../../html/qh--geom.gif" align="middle" width="40" height="40"></a><i>The +Geometry Center Home Page </i></p> +<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a> +</a><br> +Created: May 2, 1997 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p> +</body> +</html> diff --git a/src/qhull/src/libqhull/qh-qhull.htm b/src/qhull/src/libqhull/qh-qhull.htm new file mode 100644 index 000000000..5212c6422 --- /dev/null +++ b/src/qhull/src/libqhull/qh-qhull.htm @@ -0,0 +1,279 @@ +<!-- Do not edit with Front Page, it adds too many spaces --> +<html> +<head> +<meta http-equiv="Content-Type" +content="text/html; charset=iso-8859-1"> +<title>libqhull.c -- top-level functions and basic data types</title> +</head> + +<body> +<!-- Navigation links --> +<p><a name="TOP"><b>Up:</b></a> <a +href="http://www.qhull.org">Home page</a> for Qhull<br> +<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual</a>: Table of Contents <br> +<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a> +• <a href="../../html/qh-quick.htm#options">Options</a> +• <a href="../../html/qh-opto.htm#output">Output</a> +• <a href="../../html/qh-optf.htm#format">Formats</a> +• <a href="../../html/qh-optg.htm#geomview">Geomview</a> +• <a href="../../html/qh-optp.htm#print">Print</a> +• <a href="../../html/qh-optq.htm#qhull">Qhull</a> +• <a href="../../html/qh-optc.htm#prec">Precision</a> +• <a href="../../html/qh-optt.htm#trace">Trace</a> +• <a href="index.htm">Functions</a><br> +<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a><br> +<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br> +<b>To:</b> <a href="qh-geom.htm">Geom</a> • <a href="qh-globa.htm">Global</a> +• <a href="qh-io.htm">Io</a> • <a href="qh-mem.htm">Mem</a> +• <a href="qh-merge.htm">Merge</a> • <a href="qh-poly.htm">Poly</a> +• <a href="qh-qhull.htm#TOC">Qhull</a> • <a href="qh-set.htm">Set</a> +• <a href="qh-stat.htm">Stat</a> • <a href="qh-user.htm">User</a> +</p> +<hr> + +<h2>libqhull.c -- top-level functions and basic data types</h2> +<blockquote> +<p>Qhull implements the Quickhull algorithm for computing +the convex hull. The Quickhull algorithm combines two +well-known algorithms: the 2-d quickhull algorithm and +the n-d beneath-beyond algorithm. See +<a href="../../html/index.htm#description">Description of Qhull</a>. </p> +<p>This section provides an index to the top-level +functions and base data types. The top-level header file, <tt>libqhull.h</tt>, +contains prototypes for these functions.</p> +</blockquote> +<p><b>Copyright © 1995-2015 C.B. Barber</b></p> +<hr> +<p><a href="#TOP">»</a> <a href="qh-geom.htm#TOC">Geom</a> +<a name="TOC">•</a> <a href="qh-globa.htm#TOC">Global</a> +• <a href="qh-io.htm#TOC">Io</a> • <a href="qh-mem.htm#TOC">Mem</a> +• <a href="qh-merge.htm#TOC">Merge</a> • <a href="qh-poly.htm#TOC">Poly</a> +• <b>Qhull</b> • <a href="qh-set.htm#TOC">Set</a> +• <a href="qh-stat.htm#TOC">Stat</a> • <a href="qh-user.htm#TOC">User</a> +</p> +<h3>Index to <a href="libqhull.c">libqhull.c</a>, +<a href="libqhull.h">libqhull.h</a>, and +<a href="../qhull/unix.c">unix.c</a></h3> +<ul> +<li><a href="#qtype">libqhull.h and unix.c data types and +constants</a> </li> +<li><a href="#qmacro">libqhull.h other macros</a> </li> +<li><a href="#qfunc">Quickhull routines in call order</a> </li> +<li><a href="#qinit">Top-level routines for initializing and terminating Qhull</a></li> +<li><a href="#qin">Top-level routines for reading and modifying the input</a></li> +<li><a href="#qcall">Top-level routines for calling Qhull</a></li> +<li><a href="#qout">Top-level routines for returning results</a></li> +<li><a href="#qtest">Top-level routines for testing and debugging</a></li> +</ul> + +<h3><a href="qh-qhull.htm#TOC">»</a><a name="qtype">libqhull.h and unix.c +data types and constants</a></h3> +<ul> +<li><a href="libqhull.h#flagT">flagT</a> Boolean flag as +a bit </li> +<li><a href="libqhull.h#boolT">boolT</a> boolean value, +either True or False </li> +<li><a href="libqhull.h#CENTERtype">CENTERtype</a> to +distinguish facet->center </li> +<li><a href="libqhull.h#qh_PRINT">qh_PRINT</a> output +formats for printing (qh.PRINTout) </li> +<li><a href="libqhull.h#qh_ALL">qh_ALL</a> argument flag +for selecting everything </li> +<li><a href="libqhull.h#qh_ERR">qh_ERR</a> Qhull exit +codes for indicating errors </li> +<li><a href="libqhull.h#qh_FILEstderr">qh_FILEstderr</a> Fake stderr +to distinguish error output from normal output [C++ only]</li> +<li><a href="../qhull/unix.c#prompt">qh_prompt</a> version and long prompt for Qhull</li> +<li><a href="../qhull/unix.c#prompt2">qh_prompt2</a> synopsis for Qhull</li> +<li><a href="../qhull/unix.c#prompt3">qh_prompt3</a> concise prompt for Qhull</li> +<li><a href="global.c#qh_version">qh_version</a> version stamp</li> +</ul> + +<h3><a href="qh-qhull.htm#TOC">»</a><a name="qmacro">libqhull.h other +macros</a></h3> +<ul> +<li><a href="qhull_a.h#traceN">traceN</a> print trace +message if <em>qh.IStracing >= N</em>. </li> +<li><a href="qhull_a.h#QHULL_UNUSED">QHULL_UNUSED</a> declare an + unused variable to avoid warnings. </li> +</ul> + +<h3><a href="qh-qhull.htm#TOC">»</a><a name="qfunc">Quickhull +routines in call order</a></h3> +<ul> +<li><a href="../qhull/unix.c#main">main</a> processes the +command line, calls qhull() to do the work, and +exits </li> +<li><a href="libqhull.c#qhull">qh_qhull</a> construct +the convex hull of a set of points </li> +<li><a href="libqhull.c#build_withrestart">qh_build_withrestart</a> +allow restarts while calling qh_buildhull</li> +<li><a href="poly2.c#initbuild">qh_initbuild</a> +initialize hull and outside sets with point array</li> +<li><a href="libqhull.c#partitionall">qh_partitionall</a> +partition all points into outside sets </li> +<li><a href="libqhull.c#buildhull">qh_buildhull</a> +construct a convex hull by adding points one at a +time </li> +<li><a href="libqhull.c#nextfurthest">qh_nextfurthest</a> +return next furthest point for processing </li> +<li><a href="libqhull.c#buildtracing">qh_buildtracing</a> +trace an iteration of buildhull </li> +<li><a href="libqhull.c#addpoint">qh_addpoint</a> add a +point to the convex hull </li> +<li><a href="libqhull.c#findhorizon">qh_findhorizon</a> +find the horizon and visible facets for a point </li> +<li><a href="libqhull.c#partitionvisible">qh_partitionvisible</a> +partition points from facets in qh.visible_list +to facets in qh.newfacet_list </li> +<li><a href="libqhull.c#partitionpoint">qh_partitionpoint</a> +partition a point as inside, coplanar with, or +outside a facet </li> +<li><a href="libqhull.c#partitioncoplanar">qh_partitioncoplanar</a> +partition coplanar point into a facet </li> +<li><a href="libqhull.c#precision">qh_precision</a> restart on precision errors if not merging and if 'QJn'</li> +</ul> + +<h3><a href="qh-qhull.htm#TOC">»</a><a name="qinit">Top-level routines for initializing and terminating Qhull (in other modules)</a></h3> +<ul> +<li><a href="global.c#freebuild">qh_freebuild</a> +free memory used by qh_initbuild and qh_buildhull +</li> +<li><a href="global.c#checkflags">qh_checkflags</a> +check flags for multiple frontends to qhull +<li><a href="global.c#freeqhull">qh_freeqhull</a> +free memory used by qhull </li> +<li><a href="global.c#init_A">qh_init_A</a> called +before error handling initialized </li> +<li><a href="global.c#init_B">qh_init_B</a> called +after points are defined </li> +<li><a href="global.c#initflags">qh_initflags</a> set +flags and constants from command line </li> +<li><a href="rboxlib.c#rboxpoints">qh_rboxpoints</a> +generate points for qhull </li> +<li><a href="global.c#restore_qhull">qh_restore_qhull</a> +restores a saved qhull </li> +<li><a href="global.c#save_qhull">qh_save_qhull</a> +saves qhull for later restoring </li> +<li><a href="user.c#user_memsizes">qh_user_memsizes</a> +define additional quick allocation sizes +</li> +</ul> + +<h3><a href="qh-qhull.htm#TOC">»</a><a name="qin">Top-level routines for reading and modifying the input (in other modules)</a></h3> +<ul> +<li><a href="geom2.c#gram_schmidt">qh_gram_schmidt</a> +implements Gram-Schmidt orthogonalization by rows </li> +<li><a href="geom2.c#projectinput">qh_projectinput</a> +project input along one or more dimensions + +Delaunay projection </li> +<li><a href="geom2.c#randommatrix">qh_randommatrix</a> +generate a random dimXdim matrix in range (-1,1) </li> +<li><a href="io.c#readpoints">qh_readpoints</a> read +points from input </li> +<li><a href="geom2.c#rotateinput">qh_rotateinput</a> rotate +input points using row matrix </li> +<li><a href="geom2.c#scaleinput">qh_scaleinput</a> scale +input points using qh low_bound/high_bound </li> +<li><a href="geom2.c#setdelaunay">qh_setdelaunay</a> project +points to paraboloid for Delaunay triangulation </li> +<li><a href="geom2.c#sethalfspace_all">qh_sethalfspace_all</a> +generate dual for halfspace intersection with interior +point </li> +</ul> + +<h3><a href="qh-qhull.htm#TOC">»</a><a name="qcall">Top-level routines for calling Qhull (in other modules)</a></h3> +<ul> +<li><a href="libqhull.c#addpoint">qh_addpoint</a> add +point to convex hull </li> +<li><a href="poly2.c#findbestfacet">qh_findbestfacet</a> +find facet that is furthest below a point </li> +<li><a href="poly2.c#findfacet_all">qh_findfacet_all</a> +exhaustive search for facet below a point </li> +<li><a href="libqhull.c#qhull">qh_qhull</a> construct +the convex hull of a set of points </li> +</ul> + +<h3><a href="qh-qhull.htm#TOC">»</a><a name="qout">Top-level routines for returning results (in other modules)</a></h3> +<ul> +<li><a href="stat.c#collectstatistics">qh_collectstatistics</a> +collect statistics for qh.facet_list </li> +<li><a href="poly2.c#nearvertex">qh_nearvertex</a> +return nearest vertex to point </li> +<li><a href="poly2.c#point">qh_point</a> return point +for a point ID </li> +<li><a href="poly2.c#pointfacet">qh_pointfacet</a> +return temporary set of facets indexed by point +ID </li> +<li><a href="poly.c#pointid">qh_pointid</a> return ID +for a point</li> +<li><a href="poly2.c#pointvertex">qh_pointvertex</a> +return vertices (if any) for all points</li> +<li><a href="stat.c#printallstatistics">qh_printallstatistics</a> +print all statistics </li> +<li><a href="io.c#printneighborhood">qh_printneighborhood</a> +print neighborhood of one or two facets </li> +<li><a href="libqhull.c#printsummary">qh_printsummary</a> +print summary </li> +<li><a href="io.c#produce_output">qh_produce_output</a> +print the results of qh_qhull() </li> +<li><a href="poly2.c#setvoronoi_all">qh_setvoronoi_all</a> +compute Voronoi centers for all facets </li> +</ul> + +<h3><a href="qh-qhull.htm#TOC">»</a><a name="qtest">Top-level routines for testing and debugging (in other modules)</a></h3> +<ul> +<li><a href="io.c#dfacet">dfacet</a> print facet by +ID from debugger </li> +<li><a href="io.c#dvertex">dvertex</a> print vertex +by ID from debugger </li> +<li><a href="poly2.c#check_output">qh_check_output</a> +check output </li> +<li><a href="poly2.c#check_points">qh_check_points</a> +verify that all points are inside the convex hull +</li> +<li><a href="user.c#errexit">qh_errexit</a> report +error with a facet and a ridge</li> +<li><a href="libqhull.c#errexit2">qh_errexit2</a> report +error with two facets </li> +<li><a href="user.c#errprint">qh_errprint</a> print +erroneous facets, ridge, and vertex </li> +<li><a href="user.c#printfacetlist">qh_printfacetlist</a> +print all fields for a list of facets </li> +</ul> + +<p><!-- Navigation links --> </p> +<hr> +<p><b>Up:</b> +<a href="http://www.qhull.org">Home page for +Qhull</a> <br> +<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual: Table of Contents</a> <br> +<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a> +• <a href="../../html/qh-quick.htm#options">Options</a> +• <a href="../../html/qh-opto.htm#output">Output</a> +• <a href="../../html/qh-optf.htm#format">Formats</a> +• <a href="../../html/qh-optg.htm#geomview">Geomview</a> +• <a href="../../html/qh-optp.htm#print">Print</a> +• <a href="../../html/qh-optq.htm#qhull">Qhull</a> +• <a href="../../html/qh-optc.htm#prec">Precision</a> +• <a href="../../html/qh-optt.htm#trace">Trace</a> +• <a href="index.htm">Functions</a><br> +<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a> <br> +<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br> +<b>To:</b> <a href="qh-geom.htm">Geom</a> • +<a href="qh-globa.htm">Global</a> • <a href="qh-io.htm">Io</a> +• <a href="qh-mem.htm">Mem</a> • <a href="qh-merge.htm">Merge</a> +• <a href="qh-poly.htm">Poly</a> • <a href="qh-qhull.htm#TOC">Qhull</a> +• <a href="qh-set.htm">Set</a> • <a href="qh-stat.htm">Stat</a> +• <a href="qh-user.htm">User</a><br> +</p> +<p><!-- GC common information --> </p> +<hr> +<p><a href="http://www.geom.uiuc.edu/"><img +src="../../html/qh--geom.gif" align="middle" width="40" height="40"></a><i>The +Geometry Center Home Page </i></p> +<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a> +</a><br> +Created: May 2, 1997 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p> +</body> +</html> diff --git a/src/qhull/src/libqhull/qh-set.htm b/src/qhull/src/libqhull/qh-set.htm new file mode 100644 index 000000000..06e71bbc9 --- /dev/null +++ b/src/qhull/src/libqhull/qh-set.htm @@ -0,0 +1,308 @@ +<!-- Do not edit with Front Page, it adds too many spaces --> +<html> +<head> +<meta http-equiv="Content-Type" +content="text/html; charset=iso-8859-1"> +<title>qset.c -- set data type and operations</title> +</head> + +<body> +<!-- Navigation links --> +<p><a name="TOP"><b>Up:</b></a> <a +href="http://www.qhull.org">Home page</a> for Qhull<br> +<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual</a>: Table of Contents <br> +<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a> +• <a href="../../html/qh-quick.htm#options">Options</a> +• <a href="../../html/qh-opto.htm#output">Output</a> +• <a href="../../html/qh-optf.htm#format">Formats</a> +• <a href="../../html/qh-optg.htm#geomview">Geomview</a> +• <a href="../../html/qh-optp.htm#print">Print</a> +• <a href="../../html/qh-optq.htm#qhull">Qhull</a> +• <a href="../../html/qh-optc.htm#prec">Precision</a> +• <a href="../../html/qh-optt.htm#trace">Trace</a> +• <a href="index.htm">Functions</a><br> +<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a><br> +<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br> +<b>To:</b> <a href="qh-geom.htm">Geom</a> • <a href="qh-globa.htm">Global</a> +• <a href="qh-io.htm">Io</a> • <a href="qh-mem.htm">Mem</a> +• <a href="qh-merge.htm">Merge</a> • <a href="qh-poly.htm">Poly</a> +• <a href="qh-qhull.htm">Qhull</a> • <a href="qh-set.htm#TOC">Set</a> +• <a href="qh-stat.htm">Stat</a> • <a href="qh-user.htm">User</a> +</p> +<hr> + +<h2>qset.c -- set data type and operations</h2> +<blockquote> +<p>Qhull's data structures are constructed from sets. The +functions and macros in qset.c construct, iterate, and +modify these sets. They are the most frequently called +functions in Qhull. For this reason, efficiency is the +primary concern. </p> +<p>In Qhull, a <i>set</i> is represented by an unordered +array of pointers with a maximum size and a NULL +terminator (<a href="qset.h#setT">setT</a>). +Most sets correspond to mathematical sets +(i.e., the pointers are unique). Some sets are sorted to +enforce uniqueness. Some sets are ordered. For example, +the order of vertices in a ridge determine the ridge's +orientation. If you reverse the order of adjacent +vertices, the orientation reverses. Some sets are not +mathematical sets. They may be indexed as an array and +they may include NULL pointers. </p> +<p>The most common operation on a set is to iterate its +members. This is done with a 'FOREACH...' macro. Each set +has a custom macro. For example, 'FOREACHvertex_' +iterates over a set of vertices. Each vertex is assigned +to the variable 'vertex' from the pointer 'vertexp'. </p> +<p>Most sets are constructed by appending elements to the +set. The last element of a set is either NULL or the +index of the terminating NULL for a partially full set. +If a set is full, appending an element copies the set to +a larger array. </p> + +</blockquote> +<p><b>Copyright © 1995-2015 C.B. Barber</b></p> +<hr> +<p><a href="#TOP">»</a> <a href="qh-geom.htm#TOC">Geom</a> +<a name="TOC">•</a> <a href="qh-globa.htm#TOC">Global</a> • +<a href="qh-io.htm#TOC">Io</a> • <a href="qh-mem.htm#TOC">Mem</a> • +<a href="qh-merge.htm#TOC">Merge</a> • <a href="qh-poly.htm#TOC">Poly</a> +• <a href="qh-qhull.htm#TOC">Qhull</a> • <b>Set</b> +• <a href="qh-stat.htm#TOC">Stat</a> • <a href="qh-user.htm#TOC">User</a> +</p> +<h3>Index to <a href="qset.c">qset.c</a> and +<a href="qset.h">qset.h</a></h3> +<ul> +<li><a href="#stype">Data types and constants</a> </li> +<li><a href="#seach">FOREACH macros</a> </li> +<li><a href="#saccess">access and size macros</a> </li> +<li><a href="#sint">internal macros</a> </li> +<li><a href="#saddr">address macros</a><p> </li> + +<li><a href="#snew">Allocation and deallocation functions</a> </li> +<li><a href="#spred">Access and predicate functions</a> +</li> +<li><a href="#sadd">Add functions</a> </li> +<li><a href="#scheck">Check and print functions</a></li> +<li><a href="#scopy">Copy, compact, and zero functions</a></li> +<li><a href="#sdel">Delete functions</a> </li> +<li><a href="#stemp">Temporary set functions</a> </li> +</ul> +<h3><a href="qh-set.htm#TOC">»</a><a name="stype">Data types and +constants</a></h3> +<ul> +<li><a href="qset.h#SETelemsize">SETelemsize</a> size +of a set element in bytes </li> +<li><a href="qset.h#setT">setT</a> a set with a +maximum size and a current size</li> +<li><a href="libqhull.h#qh-set">qh global sets</a> +global sets for temporary sets, etc. </li> +</ul> +<h3><a href="qh-set.htm#TOC">»</a><a name="seach">FOREACH macros</a></h3> +<ul> +<li><a href="qset.h#FOREACHelem_">FOREACHelem_</a> +assign 'elem' to each element in a set </li> +<li><a href="qset.h#FOREACHset_">FOREACHset_</a> +assign 'set' to each set in a set of sets </li> +<li><a href="qset.h#FOREACHsetelement_">FOREACHsetelement_</a> +define a FOREACH iterator </li> +<li><a href="qset.h#FOREACHsetelement_i_">FOREACHsetelement_i_</a> +define an indexed FOREACH iterator </li> +<li><a href="qset.h#FOREACHsetelementreverse_">FOREACHsetelementreverse_</a> +define a reversed FOREACH iterator </li> +<li><a href="qset.h#FOREACHsetelementreverse12_">FOREACHsetelementreverse12_</a> +define a FOREACH iterator with e[1] and e[0] +reversed </li> +</ul> +<h3><a href="qh-set.htm#TOC">»</a><a name="saccess">Access and +size macros</a></h3> +<ul> +<li><a href="qset.h#SETelem_">SETelem_</a> return the +n'th element of set </li> +<li><a href="qset.h#SETelemt_">SETelemt_</a> return +the n'th element of set as a type</li> +<li><a href="qset.h#SETempty_">SETempty_</a> return +true (1) if set is empty </li> +<li><a href="qset.h#SETfirst_">SETfirst_</a> return +first element of set </li> +<li><a href="qset.h#SETfirstt_">SETfirstt_</a> return +first element of set as a type</li> +<li><a href="qset.h#SETindex_">SETindex_</a> return +index of elem in set </li> +<li><a href="qset.h#SETreturnsize_">SETreturnsize_</a> +return size of a set (normally use <a href="qset.c#setsize">qh_setsize</a>) </li> +<li><a href="qset.h#SETsecond_">SETsecond_</a> return +second element of set </li> +<li><a href="qset.h#SETsecondt_">SETsecondt_</a> +return second element of set as a type</li> +<li><a href="qset.h#SETtruncate_">SETtruncate_</a> +truncate set to size, i.e., qh_settruncate()</li> +</ul> +<h3><a href="qh-set.htm#TOC">»</a><a name="sint">Internal macros</a></h3> +<ul> +<li><a href="qset.c#SETsizeaddr_">SETsizeaddr_</a> +return pointer to end element of a set (indicates +current size) </li> +</ul> + +<h3><a href="qh-set.htm#TOC">»</a><a name="saddr">address macros</a></h3> +<ul> +<li><a href="qset.h#SETaddr_">SETaddr_</a> return +address of a set's elements </li> +<li><a href="qset.h#SETelemaddr_">SETelemaddr_</a> +return address of the n'th element of a set </li> +<li><a href="qset.h#SETref_">SETref_</a> l.h.s. for +modifying the current element in a FOREACH +iteration </li> +</ul> + +<h3><a href="qh-set.htm#TOC">»</a><a name="snew">Allocation and +deallocation functions</a></h3> +<ul> +<li><a href="qset.c#setfree">qh_setfree</a> free the +space occupied by a set </li> +<li><a href="qset.c#setfree2">qh_setfree2</a> free a +set and its elements </li> +<li><a href="qset.c#setfreelong">qh_setfreelong</a> +free a set only if it is in long memory </li> +<li><a href="qset.c#setnew">qh_setnew</a> create a new +set </li> +</ul> + +<h3><a href="qh-set.htm#TOC">»</a><a name="spred">Access and +predicate functions </a></h3> +<ul> +<li><a href="qset.c#setendpointer">qh_setendpointer</a> return +pointer to NULL terminator of a set</li> +<li><a href="qset.c#setequal">qh_setequal</a> return 1 +if two sorted sets are equal </li> +<li><a href="qset.c#setequal_except">qh_setequal_except</a> +return 1 if two sorted sets are equal except for +an element </li> +<li><a href="qset.c#setequal_skip">qh_setequal_skip</a> +return 1 if two sorted sets are equal except for +a pair of skipped elements </li> +<li><a href="qset.c#setequal_skip">qh_setequal_skip</a> +return 1 if two sorted sets are equal except for +a pair of skipped elements </li> +<li><a href="qset.c#setin">qh_setin</a> return 1 if an +element is in a set </li> +<li><a href="qset.c#setindex">qh_setindex</a> return +the index of an element in a set </li> +<li><a href="qset.c#setlast">qh_setlast</a> return +last element of a set</li> +<li><a href="qset.c#setsize">qh_setsize</a> returns +the size of a set </li> +</ul> + +<h3><a href="qh-set.htm#TOC">»</a><a name="sadd">Add functions</a></h3> +<ul> +<li><a href="qset.c#setaddnth">qh_setaddnth</a> add a +element as n'th element of sorted or unsorted set +</li> +<li><a href="qset.c#setaddsorted">qh_setaddsorted</a> +add an element to a sorted set </li> +<li><a href="qset.c#setappend">qh_setappend</a> append +an element to a set </li> +<li><a href="qset.c#setappend_set">qh_setappend_set</a> +append a set of elements to a set </li> +<li><a href="qset.c#setappend2ndlast">qh_setappend2ndlast</a> +add an element as the next to the last element in +a set </li> +<li><a href="qset.c#setlarger">qh_setlarger</a> return +a larger set with the same elements</li> +<li><a href="qset.c#setreplace">qh_setreplace</a> +replace one element with another in a set</li> +<li><a href="qset.c#setunique">qh_setunique</a> add an +element if it is not already in a set </li> +</ul> + +<h3><a href="qh-set.htm#TOC">»</a><a name="scheck">Check and print functions</a></h3> +<ul> +<li><a href="qset.c#setcheck">qh_setcheck</a> check a +set for validity </li> +<li><a href="qset.c#setprint">qh_setprint</a> print a +set's elements to fp </li> +</ul> + +<h3><a href="qh-set.htm#TOC">»</a><a name="scopy">Copy, compact, and zero functions</a></h3> +<ul> +<li><a href="qset.c#setcompact">qh_setcompact</a> +compact NULLs from an unsorted set </li> +<li><a href="qset.c#setcopy">qh_setcopy</a> make a +copy of a sorted or unsorted set </li> +<li><a href="qset.c#setduplicate">qh_setduplicate</a> +duplicate a set and its elements </li> +<li><a href="qset.c#settruncate">qh_settruncate</a> +truncate a set to size elements </li> +<li><a href="qset.c#setzero">qh_setzero</a> zero the +remainder of a set </li> +</ul> + +<h3><a href="qh-set.htm#TOC">»</a><a name="sdel">Delete functions</a></h3> +<ul> +<li><a href="qset.c#setdel">qh_setdel</a> delete an +element from an unsorted set. </li> +<li><a href="qset.c#setdellast">qh_setdellast</a> +delete and return last element from a set</li> +<li><a href="qset.c#setdelnth">qh_setdelnth</a> delete +and return nth element from an unsorted set </li> +<li><a href="qset.c#setdelnthsorted">qh_setdelnthsorted</a> +delete and return nth element from a sorted set </li> +<li><a href="qset.c#setdelsorted">qh_setdelsorted</a> +delete an element from a sorted set </li> +<li><a href="qset.c#setnew_delnthsorted">qh_setnew_delnthsorted</a> +create a sorted set not containing the nth +element </li> +</ul> + +<h3><a href="qh-set.htm#TOC">»</a><a name="stemp">Temporary set functions</a></h3> +<ul> +<li><a href="qset.c#settemp">qh_settemp</a> return a +temporary set and append it qhmem.tempstack</li> +<li><a href="qset.c#settempfree">qh_settempfree</a> +free and pop a set from qhmem.tempstack</li> +<li><a href="qset.c#settempfree_all">qh_settempfree_all</a> +free all sets in qhmem.tempstack </li> +<li><a href="qset.c#settemppop">qh_settemppop</a> pop +a set from qhmem.tempstack (makes it permanent) </li> +<li><a href="qset.c#settemppush">qh_settemppush</a> +push a set unto qhmem.tempstack (makes it +temporary) </li> +</ul> + +<p><!-- Navigation links --> </p> +<hr> +<p><b>Up:</b> +<a href="http://www.qhull.org">Home page for +Qhull</a> <br> +<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual: Table of Contents</a> <br> +<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a> +• <a href="../../html/qh-quick.htm#options">Options</a> +• <a href="../../html/qh-opto.htm#output">Output</a> +• <a href="../../html/qh-optf.htm#format">Formats</a> +• <a href="../../html/qh-optg.htm#geomview">Geomview</a> +• <a href="../../html/qh-optp.htm#print">Print</a> +• <a href="../../html/qh-optq.htm#qhull">Qhull</a> +• <a href="../../html/qh-optc.htm#prec">Precision</a> +• <a href="../../html/qh-optt.htm#trace">Trace</a> +• <a href="index.htm">Functions</a><br> +<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a> <br> +<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br> +<b>To:</b> <a href="qh-geom.htm">Geom</a> • +<a href="qh-globa.htm">Global</a> • <a href="qh-io.htm">Io</a> +• <a href="qh-mem.htm">Mem</a> • <a href="qh-merge.htm">Merge</a> +• <a href="qh-poly.htm">Poly</a> • <a href="qh-qhull.htm#TOC">Qhull</a> +• <a href="qh-set.htm">Set</a> • <a href="qh-stat.htm">Stat</a> +• <a href="qh-user.htm">User</a><br> +</p> +<p><!-- GC common information --> </p> +<hr> +<p><a href="http://www.geom.uiuc.edu/"><img +src="../../html/qh--geom.gif" align="middle" width="40" height="40"></a><i>The +Geometry Center Home Page </i></p> +<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a> +</a><br> +Created: May 2, 1997 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p> +</body> +</html> diff --git a/src/qhull/src/libqhull/qh-stat.htm b/src/qhull/src/libqhull/qh-stat.htm new file mode 100644 index 000000000..b96854031 --- /dev/null +++ b/src/qhull/src/libqhull/qh-stat.htm @@ -0,0 +1,163 @@ +<!-- Do not edit with Front Page, it adds too many spaces --> +<html> +<head> +<meta http-equiv="Content-Type" +content="text/html; charset=iso-8859-1"> +<title>stat.c -- statistical operations</title> +</head> + +<body> +<!-- Navigation links --> +<p><a name="TOP"><b>Up:</b></a> <a +href="http://www.qhull.org">Home page</a> for Qhull<br> +<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual</a>: Table of Contents <br> +<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a> +• <a href="../../html/qh-quick.htm#options">Options</a> +• <a href="../../html/qh-opto.htm#output">Output</a> +• <a href="../../html/qh-optf.htm#format">Formats</a> +• <a href="../../html/qh-optg.htm#geomview">Geomview</a> +• <a href="../../html/qh-optp.htm#print">Print</a> +• <a href="../../html/qh-optq.htm#qhull">Qhull</a> +• <a href="../../html/qh-optc.htm#prec">Precision</a> +• <a href="../../html/qh-optt.htm#trace">Trace</a> +• <a href="index.htm">Functions</a><br> +<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a><br> +<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br> +<b>To:</b> <a href="qh-geom.htm">Geom</a> • <a href="qh-globa.htm">Global</a> +• <a href="qh-io.htm">Io</a> • <a href="qh-mem.htm">Mem</a> +• <a href="qh-merge.htm">Merge</a> • <a href="qh-poly.htm">Poly</a> +• <a href="qh-qhull.htm">Qhull</a> • <a href="qh-set.htm">Set</a> +• <a href="qh-stat.htm#TOC">Stat</a> • <a href="qh-user.htm">User</a> +</p> +<hr> + +<h2>stat.c -- statistical operations</h2> +<blockquote> +<p>Qhull records many statistics. These functions and +macros make it inexpensive to add a statistic. +<p>As with Qhull's global variables, the statistics data structure is +accessed by a macro, 'qhstat'. If qh_QHpointer is defined, the macro +is 'qh_qhstat->', otherwise the macro is 'qh_qhstat.'. +Statistics +may be turned off in user.h. If so, all but the 'zz' +statistics are ignored.</p> +</blockquote> +<p><b>Copyright © 1995-2015 C.B. Barber</b></p> +<hr> +<p><a href="#TOP">»</a> <a href="qh-geom.htm#TOC">Geom</a> +<a name="TOC">•</a> <a href="qh-globa.htm#TOC">Global</a> +• <a href="qh-io.htm#TOC">Io</a> • <a href="qh-mem.htm#TOC">Mem</a> +• <a href="qh-merge.htm#TOC">Merge</a> • <a href="qh-poly.htm#TOC">Poly</a> +• <a href="qh-qhull.htm#TOC">Qhull</a> • <a href="qh-set.htm#TOC">Set</a> +• <b>Stat</b> • <a href="qh-user.htm#TOC">User</a> +</p> +<h3>Index to <a href="stat.c">stat.c</a> and +<a href="stat.h">stat.h</a></h3> +<ul> +<li><a href="#ttype">stat.h types</a> </li> +<li><a href="#tconst">stat.h constants</a> </li> +<li><a href="#tmacro">stat.h macros</a> </li> +<li><a href="#tfunc">stat.c functions</a> </li> +</ul> + +<h3><a href="qh-stat.htm#TOC">»</a><a name="ttype">stat.h types</a></h3> +<ul> +<li><a href="stat.h#intrealT">intrealT</a> union of +integer and real</li> +<li><a href="stat.h#qhstat">qhstat</a> global data +structure for statistics </li> +</ul> +<h3><a href="qh-stat.htm#TOC">»</a><a name="tconst">stat.h +constants</a></h3> +<ul> +<li><a href="stat.h#KEEPstatistics">qh_KEEPstatistics</a> 0 turns off most statistics</li> +<li><a href="stat.h#statistics">Z..., W...</a> integer (Z) and real (W) statistics +</li> +<li><a href="stat.h#ZZstat">ZZstat</a> Z.../W... statistics that +remain defined if qh_KEEPstatistics=0 +</li> +<li><a href="stat.h#ztype">ztype</a> zdoc, zinc, etc. +for definining statistics </li> +</ul> +<h3><a href="qh-stat.htm#TOC">»</a><a name="tmacro">stat.h macros</a></h3> +<ul> +<li><a href="stat.h#MAYdebugx">MAYdebugx</a> called +frequently for error trapping </li> +<li><a href="stat.h#zadd_">zadd_/wadd_</a> add value +to an integer or real statistic </li> +<li><a href="stat.h#zdef_">zdef_</a> define a +statistic </li> +<li><a href="stat.h#zinc_">zinc_</a> increment an +integer statistic </li> +<li><a href="stat.h#zmax_">zmax_/wmax_</a> update +integer or real maximum statistic </li> +<li><a href="stat.h#zmin_">zmin_/wmin_</a> update +integer or real minimum statistic </li> +<li><a href="stat.h#zval_">zval_/wval_</a> set or +return value of a statistic </li> +</ul> + +<h3><a href="qh-stat.htm#TOC">»</a><a name="tfunc">stat.c +functions</a></h3> +<ul> +<li><a href="stat.c#allstatA">qh_allstatA</a> define +statistics in groups of 20 </li> +<li><a href="stat.c#allstatistics">qh_allstatistics</a> +reset printed flag for all statistics </li> +<li><a href="stat.c#collectstatistics">qh_collectstatistics</a> +collect statistics for qh.facet_list </li> +<li><a href="stat.c#freestatistics">qh_freestatistics</a> +free memory used for statistics </li> +<li><a href="stat.c#initstatistics">qh_initstatistics</a> +allocate and initialize statistics </li> +<li><a href="stat.c#newstats">qh_newstats</a> returns +True if statistics for zdoc </li> +<li><a href="stat.c#nostatistic">qh_nostatistic</a> +true if no statistic to print </li> +<li><a href="stat.c#printallstatistics">qh_printallstatistics</a> +print all statistics </li> +<li><a href="stat.c#printstatistics">qh_printstatistics</a> +print statistics to a file </li> +<li><a href="stat.c#printstatlevel">qh_printstatlevel</a> +print level information for a statistic </li> +<li><a href="stat.c#printstats">qh_printstats</a> +print statistics for a zdoc group </li> +<li><a href="stat.c#stddev">qh_stddev</a> compute the +standard deviation and average from statistics </li> +</ul> + +<p><!-- Navigation links --> </p> +<hr> +<p><b>Up:</b> +<a href="http://www.qhull.org">Home page for +Qhull</a> <br> +<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual: Table of Contents</a> <br> +<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a> +• <a href="../../html/qh-quick.htm#options">Options</a> +• <a href="../../html/qh-opto.htm#output">Output</a> +• <a href="../../html/qh-optf.htm#format">Formats</a> +• <a href="../../html/qh-optg.htm#geomview">Geomview</a> +• <a href="../../html/qh-optp.htm#print">Print</a> +• <a href="../../html/qh-optq.htm#qhull">Qhull</a> +• <a href="../../html/qh-optc.htm#prec">Precision</a> +• <a href="../../html/qh-optt.htm#trace">Trace</a> +• <a href="index.htm">Functions</a><br> +<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a> <br> +<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br> +<b>To:</b> <a href="qh-geom.htm">Geom</a> • +<a href="qh-globa.htm">Global</a> • <a href="qh-io.htm">Io</a> +• <a href="qh-mem.htm">Mem</a> • <a href="qh-merge.htm">Merge</a> +• <a href="qh-poly.htm">Poly</a> • <a href="qh-qhull.htm#TOC">Qhull</a> +• <a href="qh-set.htm">Set</a> • <a href="qh-stat.htm">Stat</a> +• <a href="qh-user.htm">User</a><br> +</p> +<p><!-- GC common information --> </p> +<hr> +<p><a href="http://www.geom.uiuc.edu/"><img +src="../../html/qh--geom.gif" align="middle" width="40" height="40"></a><i>The +Geometry Center Home Page </i></p> +<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a> +</a><br> +Created: May 2, 1997 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p> +</body> +</html> diff --git a/src/qhull/src/libqhull/qh-user.htm b/src/qhull/src/libqhull/qh-user.htm new file mode 100644 index 000000000..6682f4b2f --- /dev/null +++ b/src/qhull/src/libqhull/qh-user.htm @@ -0,0 +1,271 @@ +<!-- Do not edit with Front Page, it adds too many spaces --> +<html> +<head> +<meta http-equiv="Content-Type" +content="text/html; charset=iso-8859-1"> +<title>user.c -- user-definable operations</title> +</head> + +<body> +<!-- Navigation links --> +<p><a name="TOP"><b>Up:</b></a> <a +href="http://www.qhull.org">Home page</a> for Qhull<br> +<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual</a>: Table of Contents <br> +<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a> +• <a href="../../html/qh-quick.htm#options">Options</a> +• <a href="../../html/qh-opto.htm#output">Output</a> +• <a href="../../html/qh-optf.htm#format">Formats</a> +• <a href="../../html/qh-optg.htm#geomview">Geomview</a> +• <a href="../../html/qh-optp.htm#print">Print</a> +• <a href="../../html/qh-optq.htm#qhull">Qhull</a> +• <a href="../../html/qh-optc.htm#prec">Precision</a> +• <a href="../../html/qh-optt.htm#trace">Trace</a> +• <a href="index.htm">Functions</a><br> +<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a><br> +<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br> +<b>To:</b> <a href="qh-geom.htm">Geom</a> • <a href="qh-globa.htm">Global</a> +• <a href="qh-io.htm">Io</a> • <a href="qh-mem.htm">Mem</a> +• <a href="qh-merge.htm">Merge</a> • <a href="qh-poly.htm">Poly</a> +• <a href="qh-qhull.htm">Qhull</a> • <a href="qh-set.htm">Set</a> +• <a href="qh-stat.htm">Stat</a> • <a href="qh-user.htm#TOC">User</a> +</p> +<hr> +<h2>user.c -- user-definable operations</h2> +<blockquote> +<p>This section contains functions and constants that the +user may want to change. </p> + +</blockquote> +<p><b>Copyright © 1995-2015 C.B. Barber</b></p> +<hr> +<p><a href="#TOP">»</a> <a href="qh-geom.htm#TOC">Geom</a> +<a name="TOC">•</a> <a href="qh-globa.htm#TOC">Global</a> +• <a href="qh-io.htm#TOC">Io</a> • <a href="qh-mem.htm#TOC">Mem</a> +• <a href="qh-merge.htm#TOC">Merge</a> • <a href="qh-poly.htm#TOC">Poly</a> +• <a href="qh-qhull.htm#TOC">Qhull</a> • <a href="qh-set.htm#TOC">Set</a> +• <a href="qh-stat.htm#TOC">Stat</a> • <b>User</b> +</p> +<h3>Index to <a href="user.c">user.c</a>, <a href="usermem.c">usermem.c</a>, <a href="userprintf.c">userprintf.c</a>, <a href="userprintf_rbox.c">userprintf_rbox.c</a> and +<a href="user.h">user.h</a></h3> +<ul> +<li><a href="#qulllib">qhull library constants</a></li> +<li><a href="#utype">user.h data types and +configuration macros</a> </li> +<li><a href="#ujoggle">joggle constants</a></li> +<li><a href="#uperform">performance related constants</a></li> +<li><a href="#umemory">memory constants</a></li> +<li><a href="#ucond">conditional compilation</a></li> +<li><a href="#umerge">merge constants</a> </li> +<li><a href="#ufunc">user.c functions</a> </li> +<li><a href="#u2func">usermem.c functions</a> </li> +<li><a href="#u3func">userprintf.c functions</a> </li> +</ul> + +<h3><a href="qh-user.htm#TOC">»</a><a name="qulllib">Qhull library constants</a></h3> +<ul> +<li><a href="user.h#filenamelen">FILENAMElen</a> -- max length of TI or TO filename </li> +<li><a href="user.h#msgcode">msgcode</a> -- unique message codes for qh_fprintf </li> +<li><a href="user.h#qh_OPTIONline">qh_OPTIONline</a> -- max length of option line ('FO')</li> +</ul> + + +<h3><a href="qh-user.htm#TOC">»</a><a name="utype">user.h data +types and configuration macros</a></h3> +<ul> +<li><a href="user.h#realT">realT, qh_REAL...</a> size +of floating point numbers </li> +<li><a href="user.h#countT">countT, COUNTmax</a> size +of counts and identifiers, typically 'int' or 'long long' </li> +<li><a href="user.h#CPUclock">qh_CPUclock</a> clock() +function for reporting the total time spent by +Qhull </li> +<li><a href="user.h#RANDOM">qh_RANDOM...</a> random +number generator </li> +</ul> + +<h3><a href="qh-user.htm#TOC">»</a><a name="udef">definition constants</a></h3> +<ul> +<li><a href="user.h#DEFAULTbox">qh_DEFAULTbox</a> +define default box size for rbox, 'Qbb', and 'QbB' (Geomview expects 0.5) </li> +<li><a href="user.h#INFINITE">qh_INFINITE</a> on +output, indicates Voronoi center at infinity </li> +<li><a href="user.h#ORIENTclock">qh_ORIENTclock</a> +define convention for orienting facets</li> +<li><a href="user.h#ZEROdelaunay">qh_ZEROdelaunay</a> +define facets that are ignored in Delaunay triangulations</li> +</ul> + +<h3><a href="qh-user.htm#TOC">»</a><a name="ujoggle">joggle constants</a></h3> +<ul> +<li><a href="user.h#JOGGLEagain">qh_JOGGLEagain</a> +how often to retry before using qh_JOGGLEmaxincrease +again </li> +<li><a href="user.h#JOGGLEdefault">qh_JOGGLEdefault</a> +default value for qh.JOGGLEmax for 'QP' </li> +<li><a href="user.h#JOGGLEincrease">qh_JOGGLEincrease</a> +factor to increase qh.JOGGLEmax on retrys for +'QPn' </li> +<li><a href="user.h#JOGGLEmaxincrease">qh_JOGGLEmaxincrease</a> max +for increasing qh.JOGGLEmax relative to +qh.MAXwidth </li> +<li><a href="user.h#JOGGLEretry">qh_JOGGLEmaxretry</a> +report error if this many retries </li> +<li><a href="user.h#JOGGLEretry">qh_JOGGLEretry</a> +how often to retry before using qh_JOGGLEmax </li> +</ul> + +<h3><a href="qh-user.htm#TOC">»</a><a name="uperform">performance +related constants</a></h3> +<ul> +<li><a href="user.h#HASHfactor">qh_HASHfactor</a> +total/used hash slots </li> +<li><a href="user.h#INITIALmax">qh_INITIALmax</a> if +dim >= qh_INITIALmax, use min/max coordinate +points for initial simplex </li> +<li><a href="user.h#INITIALsearch">qh_INITIALsearch</a> +if qh.INITIALmax, search points up to this +dimension </li> +<li><a href="user.h#NOtrace">qh_NOtrace</a> disallow +tracing </li> +<li><a href="user.h#VERIFYdirect">qh_VERIFYdirect</a> +'Tv' verifies all <em>points X facets</em> if op +count is smaller </li> +</ul> + +<h3><a href="qh-user.htm#TOC">»</a><a name="umemory">memory constants</a></h3> +<ul> +<li><a href="user.h#MEMalign">qh_MEMalign</a> memory +alignment for qh_meminitbuffers() in global.c </li> +<li><a href="user.h#MEMbufsize">qh_MEMbufsize</a> +size of additional memory buffers </li> +<li><a href="user.h#MEMinitbuf">qh_MEMinitbuf</a> +size of initial memory buffer </li> +</ul> + +<h3><a href="qh-user.htm#TOC">»</a><a name="ucond">conditional compilation</a></h3> +<ul> +<li><a href="user.h#compiler">compiler</a> defined symbols, +e.g., _STDC_ and _cplusplus + +<li><a href="user.h#COMPUTEfurthest">qh_COMPUTEfurthest</a> + compute furthest distance to an outside point instead of storing it with the facet +<li><a href="user.h#KEEPstatistics">qh_KEEPstatistics</a> + enable statistic gathering and reporting with option 'Ts' +<li><a href="user.h#MAXoutside">qh_MAXoutside</a> +record outer plane for each facet +<li><a href="user.h#NOmerge">qh_NOmerge</a> +disable facet merging +<li><a href="user.h#NOtrace">qh_NOtrace</a> +disable tracing with option 'T4' +<li><a href="user.h#QHpointer">qh_QHpointer</a> +access global data with pointer or static structure +<li><a href="user.h#QUICKhelp">qh_QUICKhelp</a> +use abbreviated help messages, e.g., for degenerate inputs +</ul> + +<h3><a href="qh-user.htm#TOC">»</a><a name="umerge">merge +constants</a></h3> +<ul> +<li><a href="user.h#BESTcentrum">qh_BESTcentrum</a> +when does qh_findbestneighbor() test centrums? </li> +<li><a href="user.h#BESTnonconvex">qh_BESTnonconvex</a> +when does qh_findbestneighbor() test nonconvex +ridges only? </li> +<li><a href="user.h#COPLANARratio">qh_COPLANARratio</a> +what is qh.MINvisible? </li> +<li><a href="user.h#DIMreduceBuild">qh_DIMreduceBuild</a> +max dimension for vertex reduction </li> +<li><a href="user.h#DIMmergeVertex">qh_DIMmergeVertex</a> +max dimension for vertex merging </li> +<li><a href="user.h#DISToutside">qh_DISToutside</a> +when is a point clearly outside of a facet for qh_findbestnew and qh_partitionall</li> +<li><a href="user.h#MAXnarrow">qh_MAXnarrow</a> max. +cosine for qh.NARROWhull </li> +<li><a href="user.h#MAXnewcentrum">qh_MAXnewcentrum</a> +when does qh_reducevertices_centrum() reset the +centrum? </li> +<li><a href="user.h#MAXnewmerges">qh_MAXnewmerges</a> +when does qh_merge_nonconvex() call +qh_reducevertices_centrums? </li> +<li><a href="user.h#RATIOnearinside">qh_RATIOnearinside</a> +ratio for retaining inside points for +qh_check_maxout() </li> +<li><a href="user.h#SEARCHdist">qh_SEARCHdist</a> +when is facet coplanar with the best facet for qh_findbesthorizon</li> +<li><a href="user.h#USEfindbestnew">qh_USEfindbestnew</a> +when to use qh_findbestnew for qh_partitionpoint()</li> +<li><a href="user.h#WARNnarrow">qh_WARNnarrow</a> +max. cosine to warn about qh.NARROWhull </li> +<li><a href="user.h#WIDEcoplanar">qh_WIDEcoplanar</a> +what is a wide facet? </li> +<li><a href="user.h#WIDEduplicate">qh_WIDEduplicate</a> +what is a wide ratio on merging duplicate ridges? </li> +</ul> + +<h3><a href="qh-user.htm#TOC">»</a><a name="ufunc">user.c +functions</a></h3> +<ul> +<li><a href="user.c#qhull_template">Qhull template</a> for calling qh_new_qhull from your program</li> +<li><a href="user.c#errexit">qh_errexit</a> report +error and exit qhull()</li> +<li><a href="user.c#errprint">qh_errprint</a> print +information about facets and ridges </li> +<li><a href="user.c#new_qhull">qh_new_qhull</a> call qhull on an array +of points</li> +<li><a href="user.c#printfacetlist">qh_printfacetlist</a> +print all fields of all facets </li> +</ul> + +<h3><a href="qh-user.htm#TOC">»</a><a name="u2func">usermem.c +functions</a></h3> +<ul> +<li><a href="usermem.c#qh_exit">qh_exit</a> exit program, same as exit(). May be redefined as throw "QH10003.." by libqhullcpp/usermem_r-cpp.cpp</li> +<li><a href="usermem.c#qh_fprintf_stderr">qh_fprintf_stderr</a> print to stderr when qh.ferr is not defined.</li> +<li><a href="usermem.c#qh_free">qh_free</a> free memory, same as free().</li> +<li><a href="usermem.c#qh_malloc">qh_malloc</a> allocate memory, same as malloc()</li> +</ul> + +<h3><a href="qh-user.htm#TOC">»</a><a name="u3func">userprintf.c + and userprintf_rbox,c functions</a></h3> +<ul> +<li><a href="userprintf.c#qh_fprintf">qh_fprintf</a> print +information from Qhull, sames as fprintf(). </li> +<li><a href="userprintf_rbox.c#qh_fprintf_rbox">qh_fprintf_rbox</a> print +information from Rbox, sames as fprintf(). </li> +</ul> + +<p><!-- Navigation links --> </p> +<hr> +<p><b>Up:</b> +<a href="http://www.qhull.org">Home page for +Qhull</a> <br> +<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual: Table of Contents</a> <br> +<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a> +• <a href="../../html/qh-quick.htm#options">Options</a> +• <a href="../../html/qh-opto.htm#output">Output</a> +• <a href="../../html/qh-optf.htm#format">Formats</a> +• <a href="../../html/qh-optg.htm#geomview">Geomview</a> +• <a href="../../html/qh-optp.htm#print">Print</a> +• <a href="../../html/qh-optq.htm#qhull">Qhull</a> +• <a href="../../html/qh-optc.htm#prec">Precision</a> +• <a href="../../html/qh-optt.htm#trace">Trace</a> +• <a href="index.htm">Functions</a><br> +<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a> <br> +<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br> +<b>To:</b> <a href="qh-geom.htm">Geom</a> • +<a href="qh-globa.htm">Global</a> • <a href="qh-io.htm">Io</a> +• <a href="qh-mem.htm">Mem</a> • <a href="qh-merge.htm">Merge</a> +• <a href="qh-poly.htm">Poly</a> • <a href="qh-qhull.htm#TOC">Qhull</a> +• <a href="qh-set.htm">Set</a> • <a href="qh-stat.htm">Stat</a> +• <a href="qh-user.htm">User</a><br> +</p> +<p><!-- GC common information --> </p> +<hr> +<p><a href="http://www.geom.uiuc.edu/"><img +src="../../html/qh--geom.gif" align="middle" width="40" height="40"></a><i>The +Geometry Center Home Page </i></p> +<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a> +</a><br> +Created: May 2, 1997 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p> +</body> +</html> diff --git a/src/qhull/src/libqhull/qhull-exports.def b/src/qhull/src/libqhull/qhull-exports.def new file mode 100644 index 000000000..11a42b57e --- /dev/null +++ b/src/qhull/src/libqhull/qhull-exports.def @@ -0,0 +1,417 @@ +; qhull-exports.def -- msvc module-definition file +; +; Generated from depends.exe by cut-and-paste of exported symbols by mingw gcc +; [mar'11] 399 symbols +; Annotate as DATA qh_last_random qh_qh qh_qhstat qhmem rbox rbox_inuse +; Annotate as __declspec for outside access in win32 -- qh_qh qh_qhstat +; Same as ../libqhullp/qhull_p-exports.def without qh_save_qhull and qh_restore_qhull +; +; $Id: //main/2015/qhull/src/libqhull/qhull-exports.def#3 $$Change: 2047 $ +; $DateTime: 2016/01/04 22:03:18 $$Author: bbarber $ +; +; Define qhull_VERSION in CMakeLists.txt, Makefile, qhull-exports.def, qhull_p-exports.def, qhull_r-exports.def, and qhull-warn.pri +VERSION 7.0 +EXPORTS +qh_addhash +qh_addpoint +qh_all_merges +qh_allstatA +qh_allstatB +qh_allstatC +qh_allstatD +qh_allstatE +qh_allstatE2 +qh_allstatF +qh_allstatG +qh_allstatH +qh_allstatI +qh_allstatistics +qh_appendfacet +qh_appendmergeset +qh_appendprint +qh_appendvertex +qh_argv_to_command +qh_argv_to_command_size +qh_attachnewfacets +qh_backnormal +qh_basevertices +qh_build_withrestart +qh_buildhull +qh_buildtracing +qh_check_bestdist +qh_check_dupridge +qh_check_maxout +qh_check_output +qh_check_point +qh_check_points +qh_checkconnect +qh_checkconvex +qh_checkfacet +qh_checkflags +qh_checkflipped +qh_checkflipped_all +qh_checkpolygon +qh_checkvertex +qh_checkzero +qh_clear_outputflags +qh_clearcenters +qh_clock +qh_collectstatistics +qh_compare_facetarea +qh_compare_facetmerge +qh_compare_facetvisit +qh_compare_vertexpoint +qh_compareangle +qh_comparemerge +qh_comparevisit +qh_copyfilename +qh_copynonconvex +qh_copypoints +qh_countfacets +qh_createsimplex +qh_crossproduct +qh_degen_redundant_facet +qh_degen_redundant_neighbors +qh_deletevisible +qh_delfacet +qh_delridge +qh_delvertex +qh_determinant +qh_detjoggle +qh_detroundoff +qh_detsimplex +qh_detvnorm +qh_detvridge +qh_detvridge3 +qh_dfacet +qh_distnorm +qh_distplane +qh_distround +qh_divzero +qh_dvertex +qh_eachvoronoi +qh_eachvoronoi_all +qh_errexit +qh_errexit2 +qh_errexit_rbox +qh_errprint +qh_exit +qh_facet2point +qh_facet3vertex +qh_facetarea +qh_facetarea_simplex +qh_facetcenter +qh_facetintersect +qh_facetvertices +qh_find_newvertex +qh_findbest +qh_findbest_test +qh_findbestfacet +qh_findbesthorizon +qh_findbestlower +qh_findbestneighbor +qh_findbestnew +qh_findfacet_all +qh_findgood +qh_findgood_all +qh_findgooddist +qh_findhorizon +qh_flippedmerges +qh_forcedmerges +qh_fprintf +qh_fprintf_rbox +qh_fprintf_stderr +qh_free +qh_freebuffers +qh_freebuild +qh_freeqhull +qh_freeqhull2 +qh_freestatistics +qh_furthestnext +qh_furthestout +qh_gausselim +qh_geomplanes +qh_getangle +qh_getarea +qh_getcenter +qh_getcentrum +qh_getdistance +qh_gethash +qh_getmergeset +qh_getmergeset_initial +qh_gram_schmidt +qh_hashridge +qh_hashridge_find +qh_infiniteloop +qh_init_A +qh_init_B +qh_init_qhull_command +qh_initbuild +qh_initflags +qh_initialhull +qh_initialvertices +qh_initqhull_buffers +qh_initqhull_globals +qh_initqhull_mem +qh_initqhull_outputflags +qh_initqhull_start +qh_initqhull_start2 +qh_initstatistics +qh_initthresholds +qh_inthresholds +qh_isvertex +qh_joggleinput +; Mark as DATA, otherwise links a separate qh_last_random. No __declspec. +qh_last_random DATA +qh_lib_check +qh_makenew_nonsimplicial +qh_makenew_simplicial +qh_makenewfacet +qh_makenewfacets +qh_makenewplanes +qh_makeridges +qh_malloc +qh_mark_dupridges +qh_markkeep +qh_markvoronoi +qh_matchduplicates +qh_matchneighbor +qh_matchnewfacets +qh_matchvertices +qh_maxabsval +qh_maxmin +qh_maxouter +qh_maxsimplex +qh_maydropneighbor +qh_memalloc +qh_memfree +qh_memfreeshort +qh_meminit +qh_meminitbuffers +qh_memsetup +qh_memsize +qh_memstatistics +qh_memtotal +qh_merge_degenredundant +qh_merge_nonconvex +qh_mergecycle +qh_mergecycle_all +qh_mergecycle_facets +qh_mergecycle_neighbors +qh_mergecycle_ridges +qh_mergecycle_vneighbors +qh_mergefacet +qh_mergefacet2d +qh_mergeneighbors +qh_mergeridges +qh_mergesimplex +qh_mergevertex_del +qh_mergevertex_neighbors +qh_mergevertices +qh_minabsval +qh_mindiff +qh_nearcoplanar +qh_nearvertex +qh_neighbor_intersections +qh_new_qhull +qh_newfacet +qh_newhashtable +qh_newridge +qh_newstats +qh_newvertex +qh_newvertices +qh_nextfurthest +qh_nextridge3d +qh_normalize +qh_normalize2 +qh_nostatistic +qh_option +qh_order_vertexneighbors +qh_orientoutside +qh_out1 +qh_out2n +qh_out3n +qh_outcoplanar +qh_outerinner +qh_partitionall +qh_partitioncoplanar +qh_partitionpoint +qh_partitionvisible +qh_point +qh_point_add +qh_pointdist +qh_pointfacet +qh_pointid +qh_pointvertex +qh_postmerge +qh_precision +qh_premerge +qh_prepare_output +qh_prependfacet +qh_printafacet +qh_printallstatistics +qh_printbegin +qh_printcenter +qh_printcentrum +qh_printend +qh_printend4geom +qh_printextremes +qh_printextremes_2d +qh_printextremes_d +qh_printfacet +qh_printfacet2geom +qh_printfacet2geom_points +qh_printfacet2math +qh_printfacet3geom_nonsimplicial +qh_printfacet3geom_points +qh_printfacet3geom_simplicial +qh_printfacet3math +qh_printfacet3vertex +qh_printfacet4geom_nonsimplicial +qh_printfacet4geom_simplicial +qh_printfacetNvertex_nonsimplicial +qh_printfacetNvertex_simplicial +qh_printfacetheader +qh_printfacetlist +qh_printfacetridges +qh_printfacets +qh_printhashtable +qh_printhelp_degenerate +qh_printhelp_narrowhull +qh_printhelp_singular +qh_printhyperplaneintersection +qh_printline3geom +qh_printlists +qh_printmatrix +qh_printneighborhood +qh_printpoint +qh_printpoint3 +qh_printpointid +qh_printpoints +qh_printpoints_out +qh_printpointvect +qh_printpointvect2 +qh_printridge +qh_printspheres +qh_printstatistics +qh_printstatlevel +qh_printstats +qh_printsummary +qh_printvdiagram +qh_printvdiagram2 +qh_printvertex +qh_printvertexlist +qh_printvertices +qh_printvneighbors +qh_printvnorm +qh_printvoronoi +qh_printvridge +qh_produce_output +qh_produce_output2 +qh_projectdim3 +qh_projectinput +qh_projectpoint +qh_projectpoints +; Mark as DATA, otherwise links a separate qh_qh. qh_qh and qh_qhstat requires __declspec +qh_qh DATA +qh_qhstat DATA +qh_qhull +qh_rand +qh_randomfactor +qh_randommatrix +qh_rboxpoints +qh_readfeasible +qh_readpoints +qh_reducevertices +qh_redundant_vertex +qh_remove_extravertices +qh_removefacet +qh_removevertex +qh_rename_sharedvertex +qh_renameridgevertex +qh_renamevertex +qh_resetlists +qh_rotateinput +qh_rotatepoints +qh_roundi +qh_scaleinput +qh_scalelast +qh_scalepoints +qh_setaddnth +qh_setaddsorted +qh_setappend +qh_setappend2ndlast +qh_setappend_set +qh_setcheck +qh_setcompact +qh_setcopy +qh_setdel +qh_setdelaunay +qh_setdellast +qh_setdelnth +qh_setdelnthsorted +qh_setdelsorted +qh_setduplicate +qh_setequal +qh_setequal_except +qh_setequal_skip +qh_setfacetplane +qh_setfeasible +qh_setfree +qh_setfree2 +qh_setfreelong +qh_sethalfspace +qh_sethalfspace_all +qh_sethyperplane_det +qh_sethyperplane_gauss +qh_setin +qh_setindex +qh_setlarger +qh_setlast +qh_setnew +qh_setnew_delnthsorted +qh_setprint +qh_setreplace +qh_setsize +qh_settemp +qh_settempfree +qh_settempfree_all +qh_settemppop +qh_settemppush +qh_settruncate +qh_setunique +qh_setvoronoi_all +qh_setzero +qh_sharpnewfacets +qh_skipfacet +qh_skipfilename +qh_srand +qh_stddev +qh_strtod +qh_strtol +qh_test_appendmerge +qh_test_vneighbors +qh_tracemerge +qh_tracemerging +qh_triangulate +qh_triangulate_facet +qh_triangulate_link +qh_triangulate_mirror +qh_triangulate_null +qh_updatetested +qh_updatevertices +qh_user_memsizes +qh_version +qh_version2 +qh_vertexintersect +qh_vertexintersect_new +qh_vertexneighbors +qh_vertexridges +qh_vertexridges_facet +qh_vertexsubset +qh_voronoi_center +qh_willdelete +; Mark as DATA, otherwise links a separate qhmem. No __declspec +qhmem DATA +rbox DATA +rbox_inuse DATA diff --git a/src/qhull/src/libqhull/qhull_a.h b/src/qhull/src/libqhull/qhull_a.h new file mode 100644 index 000000000..729b72327 --- /dev/null +++ b/src/qhull/src/libqhull/qhull_a.h @@ -0,0 +1,150 @@ +/*<html><pre> -<a href="qh-qhull.htm" + >-------------------------------</a><a name="TOP">-</a> + + qhull_a.h + all header files for compiling qhull with non-reentrant code + included before C++ headers for user_r.h:QHULL_CRTDBG + + see qh-qhull.htm + + see libqhull.h for user-level definitions + + see user.h for user-definable constants + + defines internal functions for libqhull.c global.c + + Copyright (c) 1993-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull/qhull_a.h#4 $$Change: 2064 $ + $DateTime: 2016/01/18 12:36:08 $$Author: bbarber $ + + Notes: grep for ((" and (" to catch fprintf("lkasdjf"); + full parens around (x?y:z) + use '#include "libqhull/qhull_a.h"' to avoid name clashes +*/ + +#ifndef qhDEFqhulla +#define qhDEFqhulla 1 + +#include "libqhull.h" /* Includes user_r.h and data types */ + +#include "stat.h" +#include "random.h" +#include "mem.h" +#include "qset.h" +#include "geom.h" +#include "merge.h" +#include "poly.h" +#include "io.h" + +#include <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.c + if string.h does not define memcpy() +#include <memory.h> +*/ + +#if qh_CLOCKtype == 2 /* defined in user.h from libqhull.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.htm#TOC" + >--------------------------------</a><a name="traceN">-</a> + + traceN((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.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 + +/***** -libqhull.c prototypes (alphabetical after qhull) ********************/ + +void qh_qhull(void); +boolT qh_addpoint(pointT *furthest, facetT *facet, boolT checkdist); +void qh_buildhull(void); +void qh_buildtracing(pointT *furthest, facetT *facet); +void qh_build_withrestart(void); +void qh_errexit2(int exitcode, facetT *facet, facetT *otherfacet); +void qh_findhorizon(pointT *point, facetT *facet, int *goodvisible,int *goodhorizon); +pointT *qh_nextfurthest(facetT **visible); +void qh_partitionall(setT *vertices, pointT *points,int npoints); +void qh_partitioncoplanar(pointT *point, facetT *facet, realT *dist); +void qh_partitionpoint(pointT *point, facetT *facet); +void qh_partitionvisible(boolT allpoints, int *numpoints); +void qh_precision(const char *reason); +void qh_printsummary(FILE *fp); + +/***** -global.c internal prototypes (alphabetical) ***********************/ + +void qh_appendprint(qh_PRINT format); +void qh_freebuild(boolT allmem); +void qh_freebuffers(void); +void qh_initbuffers(coordT *points, int numpoints, int dim, boolT ismalloc); + +/***** -stat.c internal prototypes (alphabetical) ***********************/ + +void qh_allstatA(void); +void qh_allstatB(void); +void qh_allstatC(void); +void qh_allstatD(void); +void qh_allstatE(void); +void qh_allstatE2(void); +void qh_allstatF(void); +void qh_allstatG(void); +void qh_allstatH(void); +void qh_freebuffers(void); +void qh_initbuffers(coordT *points, int numpoints, int dim, boolT ismalloc); + +#endif /* qhDEFqhulla */ diff --git a/src/qhull/src/libqhull/qhull_p-exports.def b/src/qhull/src/libqhull/qhull_p-exports.def new file mode 100644 index 000000000..cadf8a4fa --- /dev/null +++ b/src/qhull/src/libqhull/qhull_p-exports.def @@ -0,0 +1,418 @@ +; qhull_p-exports.def -- msvc module-definition file +; +; Generated from depends.exe by cut-and-paste of exported symbols by mingw gcc +; [mar'11] 399 symbols [jan'15] added 3 symbols +; Annotate as DATA qh_last_random qh_qh qh_qhstat qhmem rbox rbox_inuse +; Annotate as __declspec for outside access in win32 -- qh_qh qh_qhstat +; +; $Id: //main/2011/qhull/src/libqhull/qhull-exports.def#2 $$Change: 1368 $ +; $DateTime: 2011/04/16 08:12:32 $$Author: bbarber $ +; +; Define qhull_VERSION in CMakeLists.txt, Makefile, qhull-exports.def, qhull_p-exports.def, qhull_r-exports.def, and qhull-warn.pri +VERSION 7.0 +EXPORTS +qh_addhash +qh_addpoint +qh_all_merges +qh_allstatA +qh_allstatB +qh_allstatC +qh_allstatD +qh_allstatE +qh_allstatE2 +qh_allstatF +qh_allstatG +qh_allstatH +qh_allstatI +qh_allstatistics +qh_appendfacet +qh_appendmergeset +qh_appendprint +qh_appendvertex +qh_argv_to_command +qh_argv_to_command_size +qh_attachnewfacets +qh_backnormal +qh_basevertices +qh_build_withrestart +qh_buildhull +qh_buildtracing +qh_check_bestdist +qh_check_dupridge +qh_check_maxout +qh_check_output +qh_check_point +qh_check_points +qh_checkconnect +qh_checkconvex +qh_checkfacet +qh_checkflags +qh_checkflipped +qh_checkflipped_all +qh_checkpolygon +qh_checkvertex +qh_checkzero +qh_clear_outputflags +qh_clearcenters +qh_clock +qh_collectstatistics +qh_compare_facetarea +qh_compare_facetmerge +qh_compare_facetvisit +qh_compare_vertexpoint +qh_compareangle +qh_comparemerge +qh_comparevisit +qh_copyfilename +qh_copynonconvex +qh_copypoints +qh_countfacets +qh_createsimplex +qh_crossproduct +qh_degen_redundant_facet +qh_degen_redundant_neighbors +qh_deletevisible +qh_delfacet +qh_delridge +qh_delvertex +qh_determinant +qh_detjoggle +qh_detroundoff +qh_detsimplex +qh_detvnorm +qh_detvridge +qh_detvridge3 +qh_dfacet +qh_distnorm +qh_distplane +qh_distround +qh_divzero +qh_dvertex +qh_eachvoronoi +qh_eachvoronoi_all +qh_errexit +qh_errexit2 +qh_errexit_rbox +qh_errprint +qh_exit +qh_facet2point +qh_facet3vertex +qh_facetarea +qh_facetarea_simplex +qh_facetcenter +qh_facetintersect +qh_facetvertices +qh_find_newvertex +qh_findbest +qh_findbest_test +qh_findbestfacet +qh_findbesthorizon +qh_findbestlower +qh_findbestneighbor +qh_findbestnew +qh_findfacet_all +qh_findgood +qh_findgood_all +qh_findgooddist +qh_findhorizon +qh_flippedmerges +qh_forcedmerges +qh_fprintf +qh_fprintf_rbox +qh_fprintf_stderr +qh_free +qh_freebuffers +qh_freebuild +qh_freeqhull +qh_freeqhull2 +qh_freestatistics +qh_furthestnext +qh_furthestout +qh_gausselim +qh_geomplanes +qh_getangle +qh_getarea +qh_getcenter +qh_getcentrum +qh_getdistance +qh_gethash +qh_getmergeset +qh_getmergeset_initial +qh_gram_schmidt +qh_hashridge +qh_hashridge_find +qh_infiniteloop +qh_init_A +qh_init_B +qh_init_qhull_command +qh_initbuild +qh_initflags +qh_initialhull +qh_initialvertices +qh_initqhull_buffers +qh_initqhull_globals +qh_initqhull_mem +qh_initqhull_outputflags +qh_initqhull_start +qh_initqhull_start2 +qh_initstatistics +qh_initthresholds +qh_inthresholds +qh_isvertex +qh_joggleinput +; Mark as DATA, otherwise links a separate qh_last_random. No __declspec. +qh_last_random DATA +qh_lib_check +qh_makenew_nonsimplicial +qh_makenew_simplicial +qh_makenewfacet +qh_makenewfacets +qh_makenewplanes +qh_makeridges +qh_malloc +qh_mark_dupridges +qh_markkeep +qh_markvoronoi +qh_matchduplicates +qh_matchneighbor +qh_matchnewfacets +qh_matchvertices +qh_maxabsval +qh_maxmin +qh_maxouter +qh_maxsimplex +qh_maydropneighbor +qh_memalloc +qh_memfree +qh_memfreeshort +qh_meminit +qh_meminitbuffers +qh_memsetup +qh_memsize +qh_memstatistics +qh_memtotal +qh_merge_degenredundant +qh_merge_nonconvex +qh_mergecycle +qh_mergecycle_all +qh_mergecycle_facets +qh_mergecycle_neighbors +qh_mergecycle_ridges +qh_mergecycle_vneighbors +qh_mergefacet +qh_mergefacet2d +qh_mergeneighbors +qh_mergeridges +qh_mergesimplex +qh_mergevertex_del +qh_mergevertex_neighbors +qh_mergevertices +qh_minabsval +qh_mindiff +qh_nearcoplanar +qh_nearvertex +qh_neighbor_intersections +qh_new_qhull +qh_newfacet +qh_newhashtable +qh_newridge +qh_newstats +qh_newvertex +qh_newvertices +qh_nextfurthest +qh_nextridge3d +qh_normalize +qh_normalize2 +qh_nostatistic +qh_option +qh_order_vertexneighbors +qh_orientoutside +qh_out1 +qh_out2n +qh_out3n +qh_outcoplanar +qh_outerinner +qh_partitionall +qh_partitioncoplanar +qh_partitionpoint +qh_partitionvisible +qh_point +qh_point_add +qh_pointdist +qh_pointfacet +qh_pointid +qh_pointvertex +qh_postmerge +qh_precision +qh_premerge +qh_prepare_output +qh_prependfacet +qh_printafacet +qh_printallstatistics +qh_printbegin +qh_printcenter +qh_printcentrum +qh_printend +qh_printend4geom +qh_printextremes +qh_printextremes_2d +qh_printextremes_d +qh_printfacet +qh_printfacet2geom +qh_printfacet2geom_points +qh_printfacet2math +qh_printfacet3geom_nonsimplicial +qh_printfacet3geom_points +qh_printfacet3geom_simplicial +qh_printfacet3math +qh_printfacet3vertex +qh_printfacet4geom_nonsimplicial +qh_printfacet4geom_simplicial +qh_printfacetNvertex_nonsimplicial +qh_printfacetNvertex_simplicial +qh_printfacetheader +qh_printfacetlist +qh_printfacetridges +qh_printfacets +qh_printhashtable +qh_printhelp_degenerate +qh_printhelp_narrowhull +qh_printhelp_singular +qh_printhyperplaneintersection +qh_printline3geom +qh_printlists +qh_printmatrix +qh_printneighborhood +qh_printpoint +qh_printpoint3 +qh_printpointid +qh_printpoints +qh_printpoints_out +qh_printpointvect +qh_printpointvect2 +qh_printridge +qh_printspheres +qh_printstatistics +qh_printstatlevel +qh_printstats +qh_printsummary +qh_printvdiagram +qh_printvdiagram2 +qh_printvertex +qh_printvertexlist +qh_printvertices +qh_printvneighbors +qh_printvnorm +qh_printvoronoi +qh_printvridge +qh_produce_output +qh_produce_output2 +qh_projectdim3 +qh_projectinput +qh_projectpoint +qh_projectpoints +; Mark as DATA, otherwise links a separate qh_qh. qh_qh and qh_qhstat requires __declspec +qh_qh DATA +qh_qhstat DATA +qh_qhull +qh_rand +qh_randomfactor +qh_randommatrix +qh_rboxpoints +qh_readfeasible +qh_readpoints +qh_reducevertices +qh_redundant_vertex +qh_remove_extravertices +qh_removefacet +qh_removevertex +qh_rename_sharedvertex +qh_renameridgevertex +qh_renamevertex +qh_resetlists +qh_restore_qhull +qh_rotateinput +qh_rotatepoints +qh_roundi +qh_save_qhull +qh_scaleinput +qh_scalelast +qh_scalepoints +qh_setaddnth +qh_setaddsorted +qh_setappend +qh_setappend2ndlast +qh_setappend_set +qh_setcheck +qh_setcompact +qh_setcopy +qh_setdel +qh_setdelaunay +qh_setdellast +qh_setdelnth +qh_setdelnthsorted +qh_setdelsorted +qh_setduplicate +qh_setequal +qh_setequal_except +qh_setequal_skip +qh_setfacetplane +qh_setfeasible +qh_setfree +qh_setfree2 +qh_setfreelong +qh_sethalfspace +qh_sethalfspace_all +qh_sethyperplane_det +qh_sethyperplane_gauss +qh_setin +qh_setindex +qh_setlarger +qh_setlast +qh_setnew +qh_setnew_delnthsorted +qh_setprint +qh_setreplace +qh_setsize +qh_settemp +qh_settempfree +qh_settempfree_all +qh_settemppop +qh_settemppush +qh_settruncate +qh_setunique +qh_setvoronoi_all +qh_setzero +qh_sharpnewfacets +qh_skipfacet +qh_skipfilename +qh_srand +qh_stddev +qh_strtod +qh_strtol +qh_test_appendmerge +qh_test_vneighbors +qh_tracemerge +qh_tracemerging +qh_triangulate +qh_triangulate_facet +qh_triangulate_link +qh_triangulate_mirror +qh_triangulate_null +qh_updatetested +qh_updatevertices +qh_user_memsizes +qh_version +qh_version2 +qh_vertexintersect +qh_vertexintersect_new +qh_vertexneighbors +qh_vertexridges +qh_vertexridges_facet +qh_vertexsubset +qh_voronoi_center +qh_willdelete +; Mark as DATA, otherwise links a separate qhmem. No __declspec +qhmem DATA +rbox DATA +rbox_inuse DATA diff --git a/src/qhull/src/libqhull/qset.c b/src/qhull/src/libqhull/qset.c new file mode 100644 index 000000000..a969252a7 --- /dev/null +++ b/src/qhull/src/libqhull/qset.c @@ -0,0 +1,1340 @@ +/*<html><pre> -<a href="qh-set.htm" + >-------------------------------</a><a name="TOP">-</a> + + qset.c + implements set manipulations needed for quickhull + + see qh-set.htm and qset.h + + Be careful of strict aliasing (two pointers of different types + that reference the same location). The last slot of a set is + either the actual size of the set plus 1, or the NULL terminator + of the set (i.e., setelemT). + + Copyright (c) 1993-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull/qset.c#3 $$Change: 2062 $ + $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $ +*/ + +#include "user.h" /* for QHULL_CRTDBG */ +#include "qset.h" +#include "mem.h" +#include <stdio.h> +#include <string.h> +/*** uncomment here and qhull_a.h + if string.h does not define memcpy() +#include <memory.h> +*/ + +#ifndef qhDEFlibqhull +typedef struct ridgeT ridgeT; +typedef struct facetT facetT; +void qh_errexit(int exitcode, facetT *, ridgeT *); +void qh_fprintf(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.htm#TOC" + >--------------------------------<a name="setaddnth">-</a> + + qh_setaddnth( 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(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(setp); + sizep= SETsizeaddr_(*setp); + } + oldsize= sizep->i - 1; + if (nth < 0 || nth > oldsize) { + qh_fprintf(qhmem.ferr, 6171, "qhull internal error (qh_setaddnth): nth %d is out-of-bounds for set:\n", nth); + qh_setprint(qhmem.ferr, "", *setp); + qh_errexit(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.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(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(setp, newindex, newelem); +} /* setaddsorted */ + + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setappend">-</a> + + qh_setappend( 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(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(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.htm#TOC" + >-------------------------------<a name="setappend_set">-</a> + + qh_setappend_set( 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(setT **setp, setT *setA) { + int sizeA, size; + setT *oldset; + setelemT *sizep; + + if (!setA) + return; + SETreturnsize_(setA, sizeA); + if (!*setp) + *setp= qh_setnew(sizeA); + sizep= SETsizeaddr_(*setp); + if (!(size= sizep->i)) + size= (*setp)->maxsize; + else + size--; + if (size + sizeA > (*setp)->maxsize) { + oldset= *setp; + *setp= qh_setcopy(oldset, sizeA); + qh_setfree(&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.htm#TOC" + >-------------------------------<a name="setappend2ndlast">-</a> + + qh_setappend2ndlast( 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(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(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.htm#TOC" + >-------------------------------<a name="setcheck">-</a> + + qh_setcheck( 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(setT *set, const char *tname, unsigned id) { + int maxsize, size; + int waserr= 0; + + if (!set) + return; + SETreturnsize_(set, size); + maxsize= set->maxsize; + if (size > maxsize || !maxsize) { + qh_fprintf(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(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(qhmem.ferr, "ERRONEOUS", set); + qh_errexit(qhmem_ERRqhull, NULL, NULL); + } +} /* setcheck */ + + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setcompact">-</a> + + qh_setcompact( 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(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(set, (int)(destp-firstp)); /* WARN64 */ +} /* setcompact */ + + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setcopy">-</a> + + qh_setcopy( 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(setT *set, int extra) { + setT *newset; + int size; + + if (extra < 0) + extra= 0; + SETreturnsize_(set, size); + newset= qh_setnew(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.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.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.htm#TOC" + >-------------------------------<a name="setdelnth">-</a> + + qh_setdelnth( 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(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(qhmem.ferr, 6174, "qhull internal error (qh_setdelnth): nth %d is out-of-bounds for set:\n", nth); + qh_setprint(qhmem.ferr, "", set); + qh_errexit(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.htm#TOC" + >-------------------------------<a name="setdelnthsorted">-</a> + + qh_setdelnthsorted( 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(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(qhmem.ferr, 6175, "qhull internal error (qh_setdelnthsorted): nth %d is out-of-bounds for set:\n", nth); + qh_setprint(qhmem.ferr, "", set); + qh_errexit(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.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.htm#TOC" + >-------------------------------<a name="setduplicate">-</a> + + qh_setduplicate( 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(setT *set, int elemsize) { + void *elem, **elemp, *newElem; + setT *newSet; + int size; + + if (!(size= qh_setsize(set))) + return NULL; + newSet= qh_setnew(size); + FOREACHelem_(set) { + newElem= qh_memalloc(elemsize); + memcpy(newElem, elem, (size_t)elemsize); + qh_setappend(&newSet, newElem); + } + return newSet; +} /* setduplicate */ + + +/*-<a href="qh-set.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.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, sizeA*SETelemsize)) + return 1; + return 0; +} /* setequal */ + + +/*-<a href="qh-set.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.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.htm#TOC" + >-------------------------------<a name="setfree">-</a> + + qh_setfree( 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(setT **setp) { + int size; + void **freelistp; /* used if !qh_NOmem by qh_memfree_() */ + + if (*setp) { + size= sizeof(setT) + ((*setp)->maxsize)*SETelemsize; + if (size <= qhmem.LASTsize) { + qh_memfree_(*setp, size, freelistp); + }else + qh_memfree(*setp, size); + *setp= NULL; + } +} /* setfree */ + + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setfree2">-</a> + + qh_setfree2( 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(setT **setp, int elemsize) { + void *elem, **elemp; + + FOREACHelem_(*setp) + qh_memfree(elem, elemsize); + qh_setfree(setp); +} /* setfree2 */ + + + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setfreelong">-</a> + + qh_setfreelong( 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(setT **setp) { + int size; + + if (*setp) { + size= sizeof(setT) + ((*setp)->maxsize)*SETelemsize; + if (size > qhmem.LASTsize) { + qh_memfree(*setp, size); + *setp= NULL; + } + } +} /* setfreelong */ + + +/*-<a href="qh-set.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.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.htm#TOC" + >-------------------------------<a name="setlarger">-</a> + + qh_setlarger( oldsetp ) + returns a larger set that contains all elements of *oldsetp + + notes: + the set is at least twice as large + if temp set, updates 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(setT **oldsetp) { + int size= 1; + setT *newset, *set, **setp, *oldset; + setelemT *sizep; + setelemT *newp, *oldp; + + if (*oldsetp) { + oldset= *oldsetp; + SETreturnsize_(oldset, size); + qhmem.cntlarger++; + qhmem.totlarger += size+1; + newset= qh_setnew(2 * size); + oldp= (setelemT *)SETaddr_(oldset, void); + newp= (setelemT *)SETaddr_(newset, void); + memcpy((char *)newp, (char *)oldp, (size_t)(size+1) * SETelemsize); + sizep= SETsizeaddr_(newset); + sizep->i= size+1; + FOREACHset_((setT *)qhmem.tempstack) { + if (set == oldset) + *(setp-1)= newset; + } + qh_setfree(oldsetp); + }else + newset= qh_setnew(3); + *oldsetp= newset; +} /* setlarger */ + + +/*-<a href="qh-set.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.htm#TOC" + >-------------------------------<a name="setnew">-</a> + + qh_setnew( 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(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= sizeof(setT) + setsize * SETelemsize; + if (size>0 && size <= qhmem.LASTsize) { + qh_memalloc_(size, freelistp, set, setT); +#ifndef qh_NOmem + sizereceived= qhmem.sizetable[ qhmem.indextable[size]]; + if (sizereceived > size) + setsize += (sizereceived - size)/SETelemsize; +#endif + }else + set= (setT*)qh_memalloc(size); + set->maxsize= setsize; + set->e[setsize].i= 1; + set->e[0].p= NULL; + return(set); +} /* setnew */ + + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setnew_delnthsorted">-</a> + + qh_setnew_delnthsorted( 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(setT *set, int size, int nth, int prepend) { + setT *newset; + void **oldp, **newp; + int tailsize= size - nth -1, newsize; + + if (tailsize < 0) { + qh_fprintf(qhmem.ferr, 6176, "qhull internal error (qh_setnew_delnthsorted): nth %d is out-of-bounds for set:\n", nth); + qh_setprint(qhmem.ferr, "", set); + qh_errexit(qhmem_ERRqhull, NULL, NULL); + } + newsize= size-1 + prepend; + newset= qh_setnew(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.htm#TOC" + >-------------------------------<a name="setprint">-</a> + + qh_setprint( fp, string, set ) + print set elements to fp with identifying string + + notes: + never errors +*/ +void qh_setprint(FILE *fp, const char* string, setT *set) { + int size, k; + + if (!set) + qh_fprintf(fp, 9346, "%s set is null\n", string); + else { + SETreturnsize_(set, size); + qh_fprintf(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(fp, 9348, " %p", set->e[k].p); + qh_fprintf(fp, 9349, "\n"); + } +} /* setprint */ + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setreplace">-</a> + + qh_setreplace( 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(setT *set, void *oldelem, void *newelem) { + void **elemp; + + elemp= SETaddr_(set, void); + while (*elemp != oldelem && *elemp) + elemp++; + if (*elemp) + *elemp= newelem; + else { + qh_fprintf(qhmem.ferr, 6177, "qhull internal error (qh_setreplace): elem %p not found in set\n", + oldelem); + qh_setprint(qhmem.ferr, "", set); + qh_errexit(qhmem_ERRqhull, NULL, NULL); + } +} /* setreplace */ + + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setsize">-</a> + + qh_setsize( 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.c] and QhullSetBase::count + + design: + determine actual size of set from maxsize +*/ +int qh_setsize(setT *set) { + int size; + setelemT *sizep; + + if (!set) + return(0); + sizep= SETsizeaddr_(set); + if ((size= sizep->i)) { + size--; + if (size > set->maxsize) { + qh_fprintf(qhmem.ferr, 6178, "qhull internal error (qh_setsize): current set size %d is greater than maximum size %d\n", + size, set->maxsize); + qh_setprint(qhmem.ferr, "set: ", set); + qh_errexit(qhmem_ERRqhull, NULL, NULL); + } + }else + size= set->maxsize; + return size; +} /* setsize */ + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="settemp">-</a> + + qh_settemp( setsize ) + return a stacked, temporary set of upto setsize elements + + notes: + use settempfree or settempfree_all to release from qhmem.tempstack + see also qh_setnew + + design: + allocate set + append to qhmem.tempstack + +*/ +setT *qh_settemp(int setsize) { + setT *newset; + + newset= qh_setnew(setsize); + qh_setappend(&qhmem.tempstack, newset); + if (qhmem.IStracing >= 5) + qh_fprintf(qhmem.ferr, 8123, "qh_settemp: temp set %p of %d elements, depth %d\n", + newset, newset->maxsize, qh_setsize(qhmem.tempstack)); + return newset; +} /* settemp */ + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="settempfree">-</a> + + qh_settempfree( set ) + free temporary set at top of 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 qhmem.tempstack + free it +*/ +void qh_settempfree(setT **set) { + setT *stackedset; + + if (!*set) + return; + stackedset= qh_settemppop(); + if (stackedset != *set) { + qh_settemppush(stackedset); + qh_fprintf(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(*set), qh_setsize(qhmem.tempstack)+1, + stackedset, qh_setsize(stackedset)); + qh_errexit(qhmem_ERRqhull, NULL, NULL); + } + qh_setfree(set); +} /* settempfree */ + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="settempfree_all">-</a> + + qh_settempfree_all( ) + free all temporary sets in qhmem.tempstack + + design: + for each set in tempstack + free set + free qhmem.tempstack +*/ +void qh_settempfree_all(void) { + setT *set, **setp; + + FOREACHset_(qhmem.tempstack) + qh_setfree(&set); + qh_setfree(&qhmem.tempstack); +} /* settempfree_all */ + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="settemppop">-</a> + + qh_settemppop( ) + pop and return temporary set from qhmem.tempstack + + notes: + the returned set is permanent + + design: + pop and check top of qhmem.tempstack +*/ +setT *qh_settemppop(void) { + setT *stackedset; + + stackedset= (setT*)qh_setdellast(qhmem.tempstack); + if (!stackedset) { + qh_fprintf(qhmem.ferr, 6180, "qhull internal error (qh_settemppop): pop from empty temporary stack\n"); + qh_errexit(qhmem_ERRqhull, NULL, NULL); + } + if (qhmem.IStracing >= 5) + qh_fprintf(qhmem.ferr, 8124, "qh_settemppop: depth %d temp set %p of %d elements\n", + qh_setsize(qhmem.tempstack)+1, stackedset, qh_setsize(stackedset)); + return stackedset; +} /* settemppop */ + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="settemppush">-</a> + + qh_settemppush( set ) + push temporary set unto qhmem.tempstack (makes it temporary) + + notes: + duplicates settemp() for tracing + + design: + append set to tempstack +*/ +void qh_settemppush(setT *set) { + if (!set) { + qh_fprintf(qhmem.ferr, 6267, "qhull error (qh_settemppush): can not push a NULL temp\n"); + qh_errexit(qhmem_ERRqhull, NULL, NULL); + } + qh_setappend(&qhmem.tempstack, set); + if (qhmem.IStracing >= 5) + qh_fprintf(qhmem.ferr, 8125, "qh_settemppush: depth %d temp set %p of %d elements\n", + qh_setsize(qhmem.tempstack), set, qh_setsize(set)); +} /* settemppush */ + + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="settruncate">-</a> + + qh_settruncate( 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(setT *set, int size) { + + if (size < 0 || size > set->maxsize) { + qh_fprintf(qhmem.ferr, 6181, "qhull internal error (qh_settruncate): size %d out of bounds for set:\n", size); + qh_setprint(qhmem.ferr, "", set); + qh_errexit(qhmem_ERRqhull, NULL, NULL); + } + set->e[set->maxsize].i= size+1; /* maybe overwritten */ + set->e[size].p= NULL; +} /* settruncate */ + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setunique">-</a> + + qh_setunique( 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(setT **set, void *elem) { + + if (!qh_setin(*set, elem)) { + qh_setappend(set, elem); + return 1; + } + return 0; +} /* setunique */ + +/*-<a href="qh-set.htm#TOC" + >-------------------------------<a name="setzero">-</a> + + qh_setzero( 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(setT *set, int idx, int size) { + int count; + + if (idx < 0 || idx >= size || size > set->maxsize) { + qh_fprintf(qhmem.ferr, 6182, "qhull internal error (qh_setzero): index %d or size %d out of bounds for set:\n", idx, size); + qh_setprint(qhmem.ferr, "", set); + qh_errexit(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/src/qhull/src/libqhull/qset.h b/src/qhull/src/libqhull/qset.h new file mode 100644 index 000000000..7e4e7d14f --- /dev/null +++ b/src/qhull/src/libqhull/qset.h @@ -0,0 +1,490 @@ +/*<html><pre> -<a href="qh-set.htm" + >-------------------------------</a><a name="TOP">-</a> + + qset.h + header file for qset.c that implements set + + see qh-set.htm and qset.c + + only uses mem.c, malloc/free + + for error handling, writes message and calls + qh_errexit(qhmem_ERRqhull, NULL, NULL); + + set operations satisfy the following properties: + - sets have a max size, the actual size (if different) is stored at the end + - every set is NULL terminated + - sets may be sorted or unsorted, the caller must distinguish this + + Copyright (c) 1993-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull/qset.h#2 $$Change: 2062 $ + $DateTime: 2016/01/17 13:13:18 $$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 + +/* [jan'15] Decided not to use countT. Most sets are small. The code uses signed tests */ + +/*-<a href="qh-set.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.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.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.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 + + 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.htm#TOC" + >----------------------------------------</a><a name="FOREACHsetelement_i_">-</a> + + FOREACHsetelement_i_(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_( facets ) FOREACHsetelement_i_( 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_(type, set, variable) \ + if (((variable= NULL), set)) for (\ + variable##_i= 0, variable= (type *)((set)->e[0].p), \ + variable##_n= qh_setsize(set);\ + variable##_i < variable##_n;\ + variable= (type *)((set)->e[++variable##_i].p) ) + +/*-<a href="qh-set.htm#TOC" + >--------------------------------------</a><a name="FOREACHsetelementreverse_">-</a> + + FOREACHsetelementreverse_(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_(type, set, variable) \ + if (((variable= NULL), set)) for (\ + variable##temp= qh_setsize(set)-1, variable= qh_setlast(set);\ + variable; variable= \ + ((--variable##temp >= 0) ? SETelemt_(set, variable##temp, type) : NULL)) + +/*-<a href="qh-set.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.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: + WARNING: needs braces if nested inside another FOREACH +*/ +#define FOREACHelem_(set) FOREACHsetelement_(void, set, elem) + +/*-<a href="qh-set.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.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.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.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.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.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.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.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.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.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.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.htm#TOC" + >---------------------------------------</a><a name="SETreturnsize_">-</a> + + SETreturnsize_(set, size) + return size of a set + + notes: + set must be defined + use qh_setsize(set) unless speed is critical +*/ +#define SETreturnsize_(set, size) (((size)= ((set)->e[(set)->maxsize].i))?(--(size)):((size)= (set)->maxsize)) + +/*-<a href="qh-set.htm#TOC" + >---------------------------------------</a><a name="SETempty_">-</a> + + SETempty_(set) + return true(1) if set is empty + + notes: + set may be NULL +*/ +#define SETempty_(set) (!set || (SETfirst_(set) ? 0 : 1)) + +/*-<a href="qh-set.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.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 ============*/ + +void qh_setaddsorted(setT **setp, void *elem); +void qh_setaddnth(setT **setp, int nth, void *newelem); +void qh_setappend(setT **setp, void *elem); +void qh_setappend_set(setT **setp, setT *setA); +void qh_setappend2ndlast(setT **setp, void *elem); +void qh_setcheck(setT *set, const char *tname, unsigned id); +void qh_setcompact(setT *set); +setT *qh_setcopy(setT *set, int extra); +void *qh_setdel(setT *set, void *elem); +void *qh_setdellast(setT *set); +void *qh_setdelnth(setT *set, int nth); +void *qh_setdelnthsorted(setT *set, int nth); +void *qh_setdelsorted(setT *set, void *newelem); +setT *qh_setduplicate( 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(setT **set); +void qh_setfree2( setT **setp, int elemsize); +void qh_setfreelong(setT **set); +int qh_setin(setT *set, void *setelem); +int qh_setindex(setT *set, void *setelem); +void qh_setlarger(setT **setp); +void *qh_setlast(setT *set); +setT *qh_setnew(int size); +setT *qh_setnew_delnthsorted(setT *set, int size, int nth, int prepend); +void qh_setprint(FILE *fp, const char* string, setT *set); +void qh_setreplace(setT *set, void *oldelem, void *newelem); +int qh_setsize(setT *set); +setT *qh_settemp(int setsize); +void qh_settempfree(setT **set); +void qh_settempfree_all(void); +setT *qh_settemppop(void); +void qh_settemppush(setT *set); +void qh_settruncate(setT *set, int size); +int qh_setunique(setT **set, void *elem); +void qh_setzero(setT *set, int idx, int size); + + +#endif /* qhDEFset */ diff --git a/src/qhull/src/libqhull/random.c b/src/qhull/src/libqhull/random.c new file mode 100644 index 000000000..176d697ae --- /dev/null +++ b/src/qhull/src/libqhull/random.c @@ -0,0 +1,245 @@ +/*<html><pre> -<a href="index.htm#TOC" + >-------------------------------</a><a name="TOP">-</a> + + random.c and utilities + Park & Miller's minimimal standard random number generator + argc/argv conversion + + Used by rbox. Do not use 'qh' +*/ + +#include "libqhull.h" +#include "random.h" + +#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.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.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: + argc may be 0 + actual size is usually shorter +*/ +int qh_argv_to_command_size(int argc, char *argv[]) { + unsigned 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.htm#TOC" + >-------------------------------</a><a name="rand">-</a> + + qh_rand() + qh_srand( seed ) + generate pseudo-random number between 1 and 2^31 -2 + + notes: + For qhull and rbox, called from qh_RANDOMint(),etc. [user.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 */ + +int qh_last_random= 1; /* define as global variable instead of using qh */ + +#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( void) { + 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( 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.htm#TOC" +>-------------------------------</a><a name="randomfactor">-</a> + +qh_randomfactor( scale, offset ) + return a random factor r * scale + offset + +notes: + qh.RANDOMa/b are defined in global.c +*/ +realT qh_randomfactor(realT scale, realT offset) { + realT randr; + + randr= qh_RANDOMint; + return randr * scale + offset; +} /* randomfactor */ + +/*-<a href="qh-geom.htm#TOC" +>-------------------------------</a><a name="randommatrix">-</a> + +qh_randommatrix( 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 +*/ +void qh_randommatrix(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.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/src/qhull/src/libqhull/random.h b/src/qhull/src/libqhull/random.h new file mode 100644 index 000000000..0c6896b76 --- /dev/null +++ b/src/qhull/src/libqhull/random.h @@ -0,0 +1,34 @@ +/*<html><pre> -<a href="qh-geom.htm" + >-------------------------------</a><a name="TOP">-</a> + + random.h + header file for random and utility routines + + see qh-geom.htm and random.c + + Copyright (c) 1993-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull/random.h#2 $$Change: 2026 $ + $DateTime: 2015/11/07 22:44:39 $$Author: bbarber $ +*/ + +#ifndef qhDEFrandom +#define qhDEFrandom 1 + +#include "libqhull.h" + +/*============= prototypes in alphabetical order ======= */ + + +int qh_argv_to_command(int argc, char *argv[], char* command, int max_size); +int qh_argv_to_command_size(int argc, char *argv[]); +int qh_rand( void); +void qh_srand( int seed); +realT qh_randomfactor(realT scale, realT offset); +void qh_randommatrix(realT *buffer, int dim, realT **row); +int qh_strtol(const char *s, char **endp); +double qh_strtod(const char *s, char **endp); + +#endif /* qhDEFrandom */ + + + diff --git a/src/qhull/src/libqhull/rboxlib.c b/src/qhull/src/libqhull/rboxlib.c new file mode 100644 index 000000000..f945133fa --- /dev/null +++ b/src/qhull/src/libqhull/rboxlib.c @@ -0,0 +1,870 @@ +/*<html><pre> -<a href="index.htm#TOC" + >-------------------------------</a><a name="TOP">-</a> + + rboxlib.c + Generate input points + + notes: + For documentation, see prompt[] of rbox.c + 50 points generated for 'rbox D4' + + WARNING: + incorrect range if qh_RANDOMmax is defined wrong (user.h) +*/ + +#include "libqhull.h" /* First for user.h */ +#include "random.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( double a); +void qh_out1( double a); +void qh_out2n( double a, double b); +void qh_out3n( double a, double b, double c); +void qh_outcoord(int iscdd, double *coord, int dim); +void qh_outcoincident(int coincidentpoints, double radius, int iscdd, double *coord, int dim); + +void qh_fprintf_rbox(FILE *fp, int msgcode, const char *fmt, ... ); +void qh_free(void *mem); +void *qh_malloc(size_t size); +int qh_rand( void); +void qh_srand( int seed); + + +/* ------------------------------ globals -------------------*/ + +/* No state is carried between rbox requests */ +typedef struct rboxT rboxT; +struct rboxT { + FILE *fout; + FILE *ferr; + int isinteger; + double out_offset; + jmp_buf errexit; /* exit label for rboxpoints, defined by setjmp(), called by qh_errexit_rbox() */ + char jmpXtra[40]; /* extra bytes in case jmp_buf is defined wrong by compiler */ +}; + + +int rbox_inuse= 0; +rboxT rbox; + +/*-<a href="qh-qhull.htm#TOC" + >-------------------------------</a><a name="rboxpoints">-</a> + + qh_rboxpoints( fout, ferr, rbox_command ) + Generate points to fout according to rbox options + Report errors on 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.c) + + 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(FILE* fout, FILE* ferr, char* rbox_command) { + 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, *simplex= NULL, *simplexp; + int nthroot, mult[MAXdim]; + double norm, factor, randr, rangap, 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[200], seedbuf[200]; + char *s= command, *t, *first_point= NULL; + time_t timedata; + int exitcode; + + if (rbox_inuse) { + qh_fprintf_rbox(rbox.ferr, 6188, "rbox error: rbox in use by another process. Please lock calls to rbox.\n"); + return qh_ERRqhull; + } + rbox_inuse= True; + rbox.ferr= ferr; + rbox.fout= fout; + + exitcode= setjmp(rbox.errexit); + if (exitcode) { + /* same code for error exit and normal return. qh.NOerrexit is set */ + if (simplex) + qh_free(simplex); + rbox_inuse= False; + return exitcode; + } + + *command= '\0'; + strncat(command, rbox_command, sizeof(command)-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': + 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(rbox.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_ERRinput); + } + if (coincidentpoints==0){ + qh_fprintf_rbox(rbox.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_ERRinput); + } + if (coincidentpoints<0 || coincidenttotal<0 || coincidentradius<0.0){ + qh_fprintf_rbox(rbox.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_ERRinput); + } + break; + case 'D': + dim= qh_strtol(s, &s); + if (dim < 1 + || dim > MAXdim) { + qh_fprintf_rbox(rbox.ferr, 6189, "rbox error: dimension, D%d, out of bounds (>=%d or <=0)", dim, MAXdim); + qh_errexit_rbox(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(rbox.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': + 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(rbox.ferr, 7070, "rbox error: unknown flag at %s.\nExecute 'rbox' without arguments for documentation.\n", s); + qh_errexit_rbox(qh_ERRinput); + } + if (*s && !isspace(*s)) { + qh_fprintf_rbox(rbox.ferr, 7071, "rbox error: missing space between flags at %s.\n", s); + qh_errexit_rbox(qh_ERRinput); + } + } + + /* ============= defaults, constants, and sizes =============== */ + if (rbox.isinteger && !isbox) + box= qh_DEFAULTzbox; + if (addcube) { + cubesize= (int)floor(ldexp(1.0,dim)+0.5); + 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(rbox.ferr, 6190, "rbox error: can not combine 'Ln' with 'Zn'\n"); + qh_errexit_rbox(qh_ERRinput); + } + if (radius <= 1.0) { + qh_fprintf_rbox(rbox.ferr, 6191, "rbox error: lens radius %.2g should be greater than 1.0\n", + radius); + qh_errexit_rbox(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(rbox.ferr, 6192, "rbox error: missing count\n"); + qh_errexit_rbox(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(rbox.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_ERRinput); + } + if (coincidentpoints>0 && (numpoints == 0 || coincidenttotal > numpoints)) { + qh_fprintf_rbox(rbox.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_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_(seed); + + /* ============= print header =============== */ + + if (iscdd) + qh_fprintf_rbox(rbox.fout, 9391, "%s\nbegin\n %d %d %s\n", + NOcommand ? "" : command, + totpoints, dim+1, + rbox.isinteger ? "integer" : "real"); + else if (NOcommand) + qh_fprintf_rbox(rbox.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(rbox.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( 1.0); + while (*++s) { + qh_out1( qh_strtod(s, &s)); + count++; + if (isspace(*s) || !*s) + break; + if (*s != ',') { + qh_fprintf_rbox(rbox.ferr, 6194, "rbox error: missing comma after coordinate in %s\n\n", s); + qh_errexit_rbox(qh_ERRinput); + } + } + if (count < dim) { + for (k=dim-count; k--; ) + qh_out1( 0.0); + }else if (count > dim) { + qh_fprintf_rbox(rbox.ferr, 6195, "rbox error: %d coordinates instead of %d coordinates in %s\n\n", + count, dim, s); + qh_errexit_rbox(qh_ERRinput); + } + qh_fprintf_rbox(rbox.fout, 9394, "\n"); + while ((s= strchr(s, 'P'))) { + if (isspace(s[-1])) + break; + } + } + } + + /* ============= simplex distribution =============== */ + if (issimplex+issimplex2) { + if (!(simplex= (double*)qh_malloc( dim * (dim+1) * sizeof(double)))) { + qh_fprintf_rbox(rbox.ferr, 6196, "rbox error: insufficient memory for simplex\n"); + qh_errexit_rbox(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( 1.0); + for (k=0; k<dim; k++) + qh_out1( *(simplexp++) * box); + qh_fprintf_rbox(rbox.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(iscdd, coord, dim); + if(coincidentcount++ < coincidenttotal) + qh_outcoincident(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(iscdd, coord, dim); + if(coincidentcount++ < coincidenttotal) + qh_outcoincident(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_free(simplex); + qh_fprintf_rbox(rbox.ferr, 6197, "rbox error: regular points can be used only in 2-d and 3-d\n\n"); + qh_errexit_rbox(qh_ERRinput); + } + if (!isaxis || radius == 0.0) { + isaxis= 1; + radius= 1.0; + } + if (dim == 3) { + if (iscdd) + qh_out1( 1.0); + qh_out3n( 0.0, 0.0, -box); + if (!isgap) { + if (iscdd) + qh_out1( 1.0); + qh_out3n( 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( 1.0); + qh_out2n( x*box, y*box); + }else { + norm= sqrt(1.0 + x*x + y*y); + if (iscdd) + qh_out1( 1.0); + qh_out3n( 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( 1.0); + qh_out3n( 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( 1.0); + qh_out2n( x*box, y*box); + if (i != 0 && i != numpoints - 1) { + if (iscdd) + qh_out1( 1.0); + qh_out2n( x*box, -y*box); + } + } + } + /* ============= regular points for 'r Ln D3' =============== */ + else if (isregular && islens && dim != 2) { + if (dim != 3) { + qh_free(simplex); + qh_fprintf_rbox(rbox.ferr, 6198, "rbox error: regular points can be used only in 2-d and 3-d\n\n"); + qh_errexit_rbox(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( 1.0); + qh_out3n( box*x, box*y, 0.0); + x *= 1-gap; + y *= 1-gap; + if (iscdd) + qh_out1( 1.0); + qh_out3n( box*x, box*y, box * offset); + if (iscdd) + qh_out1( 1.0); + qh_out3n( box*x, box*y, -box * offset); + } + } + /* ============= apex of 'Zn' distribution + gendim =============== */ + else { + if (isaxis) { + gendim= dim-1; + if (iscdd) + qh_out1( 1.0); + for (j=0; j < gendim; j++) + qh_out1( 0.0); + qh_out1( -box); + qh_fprintf_rbox(rbox.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_free(simplex); + qh_fprintf_rbox(rbox.ferr, 6199, "rbox error: spiral distribution is available only in 3d\n\n"); + qh_errexit_rbox(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(iscdd, coord, dim); + if(coincidentcount++ < coincidenttotal) + qh_outcoincident(coincidentpoints, coincidentradius, iscdd, coord, dim); + } + } + + /* ============= write cube vertices =============== */ + if (addcube) { + for (j=0; j<cubesize; j++) { + if (iscdd) + qh_out1( 1.0); + for (k=dim-1; k>=0; k--) { + if (j & ( 1 << k)) + qh_out1( cube); + else + qh_out1( -cube); + } + qh_fprintf_rbox(rbox.fout, 9400, "\n"); + } + } + + /* ============= write diamond vertices =============== */ + if (adddiamond) { + for (j=0; j<diamondsize; j++) { + if (iscdd) + qh_out1( 1.0); + for (k=dim-1; k>=0; k--) { + if (j/2 != k) + qh_out1( 0.0); + else if (j & 0x1) + qh_out1( diamond); + else + qh_out1( -diamond); + } + qh_fprintf_rbox(rbox.fout, 9401, "\n"); + } + } + + if (iscdd) + qh_fprintf_rbox(rbox.fout, 9402, "end\nhull\n"); + + /* same code for error exit and normal return */ + qh_free(simplex); + rbox_inuse= False; + return qh_ERRnone; +} /* rboxpoints */ + +/*------------------------------------------------ +outxxx - output functions for qh_rboxpoints +*/ +int qh_roundi( double a) { + if (a < 0.0) { + if (a - 0.5 < INT_MIN) { + qh_fprintf_rbox(rbox.ferr, 6200, "rbox input error: negative coordinate %2.2g is too large. Reduce 'Bn'\n", a); + qh_errexit_rbox(qh_ERRinput); + } + return (int)(a - 0.5); + }else { + if (a + 0.5 > INT_MAX) { + qh_fprintf_rbox(rbox.ferr, 6201, "rbox input error: coordinate %2.2g is too large. Reduce 'Bn'\n", a); + qh_errexit_rbox(qh_ERRinput); + } + return (int)(a + 0.5); + } +} /* qh_roundi */ + +void qh_out1(double a) { + + if (rbox.isinteger) + qh_fprintf_rbox(rbox.fout, 9403, "%d ", qh_roundi( a+rbox.out_offset)); + else + qh_fprintf_rbox(rbox.fout, 9404, qh_REAL_1, a+rbox.out_offset); +} /* qh_out1 */ + +void qh_out2n( double a, double b) { + + if (rbox.isinteger) + qh_fprintf_rbox(rbox.fout, 9405, "%d %d\n", qh_roundi(a+rbox.out_offset), qh_roundi(b+rbox.out_offset)); + else + qh_fprintf_rbox(rbox.fout, 9406, qh_REAL_2n, a+rbox.out_offset, b+rbox.out_offset); +} /* qh_out2n */ + +void qh_out3n( double a, double b, double c) { + + if (rbox.isinteger) + qh_fprintf_rbox(rbox.fout, 9407, "%d %d %d\n", qh_roundi(a+rbox.out_offset), qh_roundi(b+rbox.out_offset), qh_roundi(c+rbox.out_offset)); + else + qh_fprintf_rbox(rbox.fout, 9408, qh_REAL_3n, a+rbox.out_offset, b+rbox.out_offset, c+rbox.out_offset); +} /* qh_out3n */ + +void qh_outcoord(int iscdd, double *coord, int dim) { + double *p= coord; + int k; + + if (iscdd) + qh_out1( 1.0); + for (k=0; k < dim; k++) + qh_out1(*(p++)); + qh_fprintf_rbox(rbox.fout, 9396, "\n"); +} /* qh_outcoord */ + +void qh_outcoincident(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( 1.0); + for (k=0; k < dim; k++) { + randr= qh_RANDOMint; + delta= 2.0 * randr/randmax - 1.0; /* -1..+1 */ + delta *= radius; + qh_out1(*(p++) + delta); + } + qh_fprintf_rbox(rbox.fout, 9410, "\n"); + } +} /* qh_outcoincident */ + +/*------------------------------------------------ + Only called from qh_rboxpoints or qh_fprintf_rbox + qh_fprintf_rbox is only called from qh_rboxpoints +*/ +void qh_errexit_rbox(int exitcode) +{ + longjmp(rbox.errexit, exitcode); +} /* qh_errexit_rbox */ + diff --git a/src/qhull/src/libqhull/stat.c b/src/qhull/src/libqhull/stat.c new file mode 100644 index 000000000..70196bbb0 --- /dev/null +++ b/src/qhull/src/libqhull/stat.c @@ -0,0 +1,717 @@ +/*<html><pre> -<a href="qh-stat.htm" + >-------------------------------</a><a name="TOP">-</a> + + stat.c + contains all statistics that are collected for qhull + + see qh-stat.htm and stat.h + + Copyright (c) 1993-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull/stat.c#5 $$Change: 2062 $ + $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $ +*/ + +#include "qhull_a.h" + +/*============ global data structure ==========*/ + +#if qh_QHpointer +qhstatT *qh_qhstat=NULL; /* global data structure */ +#else +qhstatT qh_qhstat; /* add "={0}" if this causes a compiler error */ +#endif + +/*========== functions in alphabetic order ================*/ + +/*-<a href="qh-stat.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(void) { + + /* zdef_(type,name,doc,average) */ + zzdef_(zdoc, Zdoc2, "precision statistics", -1); + zdef_(zinc, Znewvertex, NULL, -1); + zdef_(wadd, Wnewvertex, "ave. distance of a new vertex to a facet(!0s)", Znewvertex); + zzdef_(wmax, Wnewvertexmax, "max. distance of a new vertex to a facet", -1); + zdef_(wmax, Wvertexmax, "max. distance of an output vertex to a facet", -1); + zdef_(wmin, Wvertexmin, "min. distance of an output vertex to a facet", -1); + zdef_(wmin, Wmindenom, "min. denominator in hyperplane computation", -1); + + qhstat precision= qhstat next; /* call qh_precision for each of these */ + zzdef_(zdoc, Zdoc3, "precision problems (corrected unless 'Q0' or an error)", -1); + zzdef_(zinc, Zcoplanarridges, "coplanar half ridges in output", -1); + zzdef_(zinc, Zconcaveridges, "concave half ridges in output", -1); + zzdef_(zinc, Zflippedfacets, "flipped facets", -1); + zzdef_(zinc, Zcoplanarhorizon, "coplanar horizon facets for new vertices", -1); + zzdef_(zinc, Zcoplanarpart, "coplanar points during partitioning", -1); + zzdef_(zinc, Zminnorm, "degenerate hyperplanes recomputed with gaussian elimination", -1); + zzdef_(zinc, Znearlysingular, "nearly singular or axis-parallel hyperplanes", -1); + zzdef_(zinc, Zback0, "zero divisors during back substitute", -1); + zzdef_(zinc, Zgauss0, "zero divisors during gaussian elimination", -1); + zzdef_(zinc, Zmultiridge, "ridges with multiple neighbors", -1); +} +void qh_allstatB(void) { + zzdef_(zdoc, Zdoc1, "summary information", -1); + zdef_(zinc, Zvertices, "number of vertices in output", -1); + zdef_(zinc, Znumfacets, "number of facets in output", -1); + zdef_(zinc, Znonsimplicial, "number of non-simplicial facets in output", -1); + zdef_(zinc, Znowsimplicial, "number of simplicial facets that were merged", -1); + zdef_(zinc, Znumridges, "number of ridges in output", -1); + zdef_(zadd, Znumridges, "average number of ridges per facet", Znumfacets); + zdef_(zmax, Zmaxridges, "maximum number of ridges", -1); + zdef_(zadd, Znumneighbors, "average number of neighbors per facet", Znumfacets); + zdef_(zmax, Zmaxneighbors, "maximum number of neighbors", -1); + zdef_(zadd, Znumvertices, "average number of vertices per facet", Znumfacets); + zdef_(zmax, Zmaxvertices, "maximum number of vertices", -1); + zdef_(zadd, Znumvneighbors, "average number of neighbors per vertex", Zvertices); + zdef_(zmax, Zmaxvneighbors, "maximum number of neighbors", -1); + zdef_(wadd, Wcpu, "cpu seconds for qhull after input", -1); + zdef_(zinc, Ztotvertices, "vertices created altogether", -1); + zzdef_(zinc, Zsetplane, "facets created altogether", -1); + zdef_(zinc, Ztotridges, "ridges created altogether", -1); + zdef_(zinc, Zpostfacets, "facets before post merge", -1); + zdef_(zadd, Znummergetot, "average merges per facet(at most 511)", Znumfacets); + zdef_(zmax, Znummergemax, " maximum merges for a facet(at most 511)", -1); + zdef_(zinc, Zangle, NULL, -1); + zdef_(wadd, Wangle, "average angle(cosine) of facet normals for all ridges", Zangle); + zdef_(wmax, Wanglemax, " maximum angle(cosine) of facet normals across a ridge", -1); + zdef_(wmin, Wanglemin, " minimum angle(cosine) of facet normals across a ridge", -1); + zdef_(wadd, Wareatot, "total area of facets", -1); + zdef_(wmax, Wareamax, " maximum facet area", -1); + zdef_(wmin, Wareamin, " minimum facet area", -1); +} +void qh_allstatC(void) { + zdef_(zdoc, Zdoc9, "build hull statistics", -1); + zzdef_(zinc, Zprocessed, "points processed", -1); + zzdef_(zinc, Zretry, "retries due to precision problems", -1); + zdef_(wmax, Wretrymax, " max. random joggle", -1); + zdef_(zmax, Zmaxvertex, "max. vertices at any one time", -1); + zdef_(zinc, Ztotvisible, "ave. visible facets per iteration", Zprocessed); + zdef_(zinc, Zinsidevisible, " ave. visible facets without an horizon neighbor", Zprocessed); + zdef_(zadd, Zvisfacettot, " ave. facets deleted per iteration", Zprocessed); + zdef_(zmax, Zvisfacetmax, " maximum", -1); + zdef_(zadd, Zvisvertextot, "ave. visible vertices per iteration", Zprocessed); + zdef_(zmax, Zvisvertexmax, " maximum", -1); + zdef_(zinc, Ztothorizon, "ave. horizon facets per iteration", Zprocessed); + zdef_(zadd, Znewfacettot, "ave. new or merged facets per iteration", Zprocessed); + zdef_(zmax, Znewfacetmax, " maximum(includes initial simplex)", -1); + zdef_(wadd, Wnewbalance, "average new facet balance", Zprocessed); + zdef_(wadd, Wnewbalance2, " standard deviation", -1); + zdef_(wadd, Wpbalance, "average partition balance", Zpbalance); + zdef_(wadd, Wpbalance2, " standard deviation", -1); + zdef_(zinc, Zpbalance, " number of trials", -1); + zdef_(zinc, Zsearchpoints, "searches of all points for initial simplex", -1); + zdef_(zinc, Zdetsimplex, "determinants computed(area & initial hull)", -1); + zdef_(zinc, Znoarea, "determinants not computed because vertex too low", -1); + zdef_(zinc, Znotmax, "points ignored(!above max_outside)", -1); + zdef_(zinc, Znotgood, "points ignored(!above a good facet)", -1); + zdef_(zinc, Znotgoodnew, "points ignored(didn't create a good new facet)", -1); + zdef_(zinc, Zgoodfacet, "good facets found", -1); + zzdef_(zinc, Znumvisibility, "distance tests for facet visibility", -1); + zdef_(zinc, Zdistvertex, "distance tests to report minimum vertex", -1); + zzdef_(zinc, Ztotcheck, "points checked for facets' outer planes", -1); + zzdef_(zinc, Zcheckpart, " ave. distance tests per check", Ztotcheck); +} +void qh_allstatD(void) { + zdef_(zinc, Zvisit, "resets of visit_id", -1); + zdef_(zinc, Zvvisit, " resets of vertex_visit", -1); + zdef_(zmax, Zvisit2max, " max visit_id/2", -1); + zdef_(zmax, Zvvisit2max, " max vertex_visit/2", -1); + + zdef_(zdoc, Zdoc4, "partitioning statistics(see previous for outer planes)", -1); + zzdef_(zadd, Zdelvertextot, "total vertices deleted", -1); + zdef_(zmax, Zdelvertexmax, " maximum vertices deleted per iteration", -1); + zdef_(zinc, Zfindbest, "calls to findbest", -1); + zdef_(zadd, Zfindbesttot, " ave. facets tested", Zfindbest); + zdef_(zmax, Zfindbestmax, " max. facets tested", -1); + zdef_(zadd, Zfindcoplanar, " ave. coplanar search", Zfindbest); + zdef_(zinc, Zfindnew, "calls to findbestnew", -1); + zdef_(zadd, Zfindnewtot, " ave. facets tested", Zfindnew); + zdef_(zmax, Zfindnewmax, " max. facets tested", -1); + zdef_(zinc, Zfindnewjump, " ave. clearly better", Zfindnew); + zdef_(zinc, Zfindnewsharp, " calls due to qh_sharpnewfacets", -1); + zdef_(zinc, Zfindhorizon, "calls to findhorizon", -1); + zdef_(zadd, Zfindhorizontot, " ave. facets tested", Zfindhorizon); + zdef_(zmax, Zfindhorizonmax, " max. facets tested", -1); + zdef_(zinc, Zfindjump, " ave. clearly better", Zfindhorizon); + zdef_(zinc, Zparthorizon, " horizon facets better than bestfacet", -1); + zdef_(zinc, Zpartangle, "angle tests for repartitioned coplanar points", -1); + zdef_(zinc, Zpartflip, " repartitioned coplanar points for flipped orientation", -1); +} +void qh_allstatE(void) { + zdef_(zinc, Zpartinside, "inside points", -1); + zdef_(zinc, Zpartnear, " inside points kept with a facet", -1); + zdef_(zinc, Zcoplanarinside, " inside points that were coplanar with a facet", -1); + zdef_(zinc, Zbestlower, "calls to findbestlower", -1); + zdef_(zinc, Zbestlowerv, " with search of vertex neighbors", -1); + zdef_(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); + zdef_(zinc, Zdistplane, "total number of distance tests", -1); + zdef_(zinc, Ztotpartcoplanar, "partitions of coplanar points or deleted vertices", -1); + zzdef_(zinc, Zpartcoplanar, " distance tests for these partitions", -1); + zdef_(zinc, Zcomputefurthest, "distance tests for computing furthest", -1); +} +void qh_allstatE2(void) { + zdef_(zdoc, Zdoc5, "statistics for matching ridges", -1); + zdef_(zinc, Zhashlookup, "total lookups for matching ridges of new facets", -1); + zdef_(zinc, Zhashtests, "average number of tests to match a ridge", Zhashlookup); + zdef_(zinc, Zhashridge, "total lookups of subridges(duplicates and boundary)", -1); + zdef_(zinc, Zhashridgetest, "average number of tests per subridge", Zhashridge); + zdef_(zinc, Zdupsame, "duplicated ridges in same merge cycle", -1); + zdef_(zinc, Zdupflip, "duplicated ridges with flipped facets", -1); + + zdef_(zdoc, Zdoc6, "statistics for determining merges", -1); + zdef_(zinc, Zangletests, "angles computed for ridge convexity", -1); + zdef_(zinc, Zbestcentrum, "best merges used centrum instead of vertices",-1); + zzdef_(zinc, Zbestdist, "distance tests for best merge", -1); + zzdef_(zinc, Zcentrumtests, "distance tests for centrum convexity", -1); + zzdef_(zinc, Zdistzero, "distance tests for checking simplicial convexity", -1); + zdef_(zinc, Zcoplanarangle, "coplanar angles in getmergeset", -1); + zdef_(zinc, Zcoplanarcentrum, "coplanar centrums in getmergeset", -1); + zdef_(zinc, Zconcaveridge, "concave ridges in getmergeset", -1); +} +void qh_allstatF(void) { + zdef_(zdoc, Zdoc7, "statistics for merging", -1); + zdef_(zinc, Zpremergetot, "merge iterations", -1); + zdef_(zadd, Zmergeinittot, "ave. initial non-convex ridges per iteration", Zpremergetot); + zdef_(zadd, Zmergeinitmax, " maximum", -1); + zdef_(zadd, Zmergesettot, " ave. additional non-convex ridges per iteration", Zpremergetot); + zdef_(zadd, Zmergesetmax, " maximum additional in one pass", -1); + zdef_(zadd, Zmergeinittot2, "initial non-convex ridges for post merging", -1); + zdef_(zadd, Zmergesettot2, " additional non-convex ridges", -1); + zdef_(wmax, Wmaxoutside, "max distance of vertex or coplanar point above facet(w/roundoff)", -1); + zdef_(wmin, Wminvertex, "max distance of merged vertex below facet(or roundoff)", -1); + zdef_(zinc, Zwidefacet, "centrums frozen due to a wide merge", -1); + zdef_(zinc, Zwidevertices, "centrums frozen due to extra vertices", -1); + zzdef_(zinc, Ztotmerge, "total number of facets or cycles of facets merged", -1); + zdef_(zinc, Zmergesimplex, "merged a simplex", -1); + zdef_(zinc, Zonehorizon, "simplices merged into coplanar horizon", -1); + zzdef_(zinc, Zcyclehorizon, "cycles of facets merged into coplanar horizon", -1); + zzdef_(zadd, Zcyclefacettot, " ave. facets per cycle", Zcyclehorizon); + zdef_(zmax, Zcyclefacetmax, " max. facets", -1); + zdef_(zinc, Zmergeintohorizon, "new facets merged into horizon", -1); + zdef_(zinc, Zmergenew, "new facets merged", -1); + zdef_(zinc, Zmergehorizon, "horizon facets merged into new facets", -1); + zdef_(zinc, Zmergevertex, "vertices deleted by merging", -1); + zdef_(zinc, Zcyclevertex, "vertices deleted by merging into coplanar horizon", -1); + zdef_(zinc, Zdegenvertex, "vertices deleted by degenerate facet", -1); + zdef_(zinc, Zmergeflipdup, "merges due to flipped facets in duplicated ridge", -1); + zdef_(zinc, Zneighbor, "merges due to redundant neighbors", -1); + zdef_(zadd, Ztestvneighbor, "non-convex vertex neighbors", -1); +} +void qh_allstatG(void) { + zdef_(zinc, Zacoplanar, "merges due to angle coplanar facets", -1); + zdef_(wadd, Wacoplanartot, " average merge distance", Zacoplanar); + zdef_(wmax, Wacoplanarmax, " maximum merge distance", -1); + zdef_(zinc, Zcoplanar, "merges due to coplanar facets", -1); + zdef_(wadd, Wcoplanartot, " average merge distance", Zcoplanar); + zdef_(wmax, Wcoplanarmax, " maximum merge distance", -1); + zdef_(zinc, Zconcave, "merges due to concave facets", -1); + zdef_(wadd, Wconcavetot, " average merge distance", Zconcave); + zdef_(wmax, Wconcavemax, " maximum merge distance", -1); + zdef_(zinc, Zavoidold, "coplanar/concave merges due to avoiding old merge", -1); + zdef_(wadd, Wavoidoldtot, " average merge distance", Zavoidold); + zdef_(wmax, Wavoidoldmax, " maximum merge distance", -1); + zdef_(zinc, Zdegen, "merges due to degenerate facets", -1); + zdef_(wadd, Wdegentot, " average merge distance", Zdegen); + zdef_(wmax, Wdegenmax, " maximum merge distance", -1); + zdef_(zinc, Zflipped, "merges due to removing flipped facets", -1); + zdef_(wadd, Wflippedtot, " average merge distance", Zflipped); + zdef_(wmax, Wflippedmax, " maximum merge distance", -1); + zdef_(zinc, Zduplicate, "merges due to duplicated ridges", -1); + zdef_(wadd, Wduplicatetot, " average merge distance", Zduplicate); + zdef_(wmax, Wduplicatemax, " maximum merge distance", -1); +} +void qh_allstatH(void) { + zdef_(zdoc, Zdoc8, "renamed vertex statistics", -1); + zdef_(zinc, Zrenameshare, "renamed vertices shared by two facets", -1); + zdef_(zinc, Zrenamepinch, "renamed vertices in a pinched facet", -1); + zdef_(zinc, Zrenameall, "renamed vertices shared by multiple facets", -1); + zdef_(zinc, Zfindfail, "rename failures due to duplicated ridges", -1); + zdef_(zinc, Zdupridge, " duplicate ridges detected", -1); + zdef_(zinc, Zdelridge, "deleted ridges due to renamed vertices", -1); + zdef_(zinc, Zdropneighbor, "dropped neighbors due to renamed vertices", -1); + zdef_(zinc, Zdropdegen, "degenerate facets due to dropped neighbors", -1); + zdef_(zinc, Zdelfacetdup, " facets deleted because of no neighbors", -1); + zdef_(zinc, Zremvertex, "vertices removed from facets due to no ridges", -1); + zdef_(zinc, Zremvertexdel, " deleted", -1); + zdef_(zinc, Zintersectnum, "vertex intersections for locating redundant vertices", -1); + zdef_(zinc, Zintersectfail, "intersections failed to find a redundant vertex", -1); + zdef_(zinc, Zintersect, "intersections found redundant vertices", -1); + zdef_(zadd, Zintersecttot, " ave. number found per vertex", Zintersect); + zdef_(zmax, Zintersectmax, " max. found for a vertex", -1); + zdef_(zinc, Zvertexridge, NULL, -1); + zdef_(zadd, Zvertexridgetot, " ave. number of ridges per tested vertex", Zvertexridge); + zdef_(zmax, Zvertexridgemax, " max. number of ridges per tested vertex", -1); + + zdef_(zdoc, Zdoc10, "memory usage statistics(in bytes)", -1); + zdef_(zadd, Zmemfacets, "for facets and their normals, neighbor and vertex sets", -1); + zdef_(zadd, Zmemvertices, "for vertices and their neighbor sets", -1); + zdef_(zadd, Zmempoints, "for input points and outside and coplanar sets",-1); + zdef_(zadd, Zmemridges, "for ridges and their vertex sets", -1); +} /* allstat */ + +void qh_allstatI(void) { + qhstat vridges= qhstat next; + zzdef_(zdoc, Zdoc11, "Voronoi ridge statistics", -1); + zzdef_(zinc, Zridge, "non-simplicial Voronoi vertices for all ridges", -1); + zzdef_(wadd, Wridge, " ave. distance to ridge", Zridge); + zzdef_(wmax, Wridgemax, " max. distance to ridge", -1); + zzdef_(zinc, Zridgemid, "bounded ridges", -1); + zzdef_(wadd, Wridgemid, " ave. distance of midpoint to ridge", Zridgemid); + zzdef_(wmax, Wridgemidmax, " max. distance of midpoint to ridge", -1); + zzdef_(zinc, Zridgeok, "bounded ridges with ok normal", -1); + zzdef_(wadd, Wridgeok, " ave. angle to ridge", Zridgeok); + zzdef_(wmax, Wridgeokmax, " max. angle to ridge", -1); + zzdef_(zinc, Zridge0, "bounded ridges with near-zero normal", -1); + zzdef_(wadd, Wridge0, " ave. angle to ridge", Zridge0); + zzdef_(wmax, Wridge0max, " max. angle to ridge", -1); + + zdef_(zdoc, Zdoc12, "Triangulation statistics(Qt)", -1); + zdef_(zinc, Ztricoplanar, "non-simplicial facets triangulated", -1); + zdef_(zadd, Ztricoplanartot, " ave. new facets created(may be deleted)", Ztricoplanar); + zdef_(zmax, Ztricoplanarmax, " max. new facets created", -1); + zdef_(zinc, Ztrinull, "null new facets deleted(duplicated vertex)", -1); + zdef_(zinc, Ztrimirror, "mirrored pairs of new facets deleted(same vertices)", -1); + zdef_(zinc, Ztridegen, "degenerate new facets in output(same ridge)", -1); +} /* allstat */ + +/*-<a href="qh-stat.htm#TOC" + >-------------------------------</a><a name="allstatistics">-</a> + + qh_allstatistics() + reset printed flag for all statistics +*/ +void qh_allstatistics(void) { + int i; + + for(i=ZEND; i--; ) + qhstat printed[i]= False; +} /* allstatistics */ + +#if qh_KEEPstatistics +/*-<a href="qh-stat.htm#TOC" + >-------------------------------</a><a name="collectstatistics">-</a> + + qh_collectstatistics() + collect statistics for qh.facet_list + +*/ +void qh_collectstatistics(void) { + facetT *facet, *neighbor, **neighborp; + vertexT *vertex, **vertexp; + realT dotproduct, dist; + int sizneighbors, sizridges, sizvertices, i; + + qh old_randomdist= qh RANDOMdist; + qh RANDOMdist= False; + zval_(Zmempoints)= qh num_points * qh normal_size + + sizeof(qhT) + sizeof(qhstatT); + zval_(Zmemfacets)= 0; + zval_(Zmemridges)= 0; + zval_(Zmemvertices)= 0; + zval_(Zangle)= 0; + wval_(Wangle)= 0.0; + zval_(Znumridges)= 0; + zval_(Znumfacets)= 0; + zval_(Znumneighbors)= 0; + zval_(Znumvertices)= 0; + zval_(Znumvneighbors)= 0; + zval_(Znummergetot)= 0; + zval_(Znummergemax)= 0; + zval_(Zvertices)= qh num_vertices - qh_setsize(qh del_vertices); + if (qh MERGING || qh APPROXhull || qh JOGGLEmax < REALmax/2) + wmax_(Wmaxoutside, qh max_outside); + if (qh MERGING) + wmin_(Wminvertex, qh min_vertex); + FORALLfacets + facet->seen= False; + if (qh DELAUNAY) { + FORALLfacets { + if (facet->upperdelaunay != qh UPPERdelaunay) + facet->seen= True; /* remove from angle statistics */ + } + } + FORALLfacets { + if (facet->visible && qh NEWfacets) + continue; + sizvertices= qh_setsize(facet->vertices); + sizneighbors= qh_setsize(facet->neighbors); + sizridges= qh_setsize(facet->ridges); + zinc_(Znumfacets); + zadd_(Znumvertices, sizvertices); + zmax_(Zmaxvertices, sizvertices); + zadd_(Znumneighbors, sizneighbors); + zmax_(Zmaxneighbors, sizneighbors); + zadd_(Znummergetot, facet->nummerge); + i= facet->nummerge; /* avoid warnings */ + zmax_(Znummergemax, i); + if (!facet->simplicial) { + if (sizvertices == qh hull_dim) { + zinc_(Znowsimplicial); + }else { + zinc_(Znonsimplicial); + } + } + if (sizridges) { + zadd_(Znumridges, sizridges); + zmax_(Zmaxridges, sizridges); + } + zadd_(Zmemfacets, sizeof(facetT) + qh normal_size + 2*sizeof(setT) + + SETelemsize * (sizneighbors + sizvertices)); + if (facet->ridges) { + zadd_(Zmemridges, + sizeof(setT) + SETelemsize * sizridges + sizridges * + (sizeof(ridgeT) + sizeof(setT) + SETelemsize * (qh hull_dim-1))/2); + } + if (facet->outsideset) + zadd_(Zmempoints, sizeof(setT) + SETelemsize * qh_setsize(facet->outsideset)); + if (facet->coplanarset) + zadd_(Zmempoints, sizeof(setT) + SETelemsize * qh_setsize(facet->coplanarset)); + if (facet->seen) /* Delaunay upper envelope */ + continue; + facet->seen= True; + FOREACHneighbor_(facet) { + if (neighbor == qh_DUPLICATEridge || neighbor == qh_MERGEridge + || neighbor->seen || !facet->normal || !neighbor->normal) + continue; + dotproduct= qh_getangle(facet->normal, neighbor->normal); + zinc_(Zangle); + wadd_(Wangle, dotproduct); + wmax_(Wanglemax, dotproduct) + wmin_(Wanglemin, dotproduct) + } + if (facet->normal) { + FOREACHvertex_(facet->vertices) { + zinc_(Zdiststat); + qh_distplane(vertex->point, facet, &dist); + wmax_(Wvertexmax, dist); + wmin_(Wvertexmin, dist); + } + } + } + FORALLvertices { + if (vertex->deleted) + continue; + zadd_(Zmemvertices, sizeof(vertexT)); + if (vertex->neighbors) { + sizneighbors= qh_setsize(vertex->neighbors); + zadd_(Znumvneighbors, sizneighbors); + zmax_(Zmaxvneighbors, sizneighbors); + zadd_(Zmemvertices, sizeof(vertexT) + SETelemsize * sizneighbors); + } + } + qh RANDOMdist= qh old_randomdist; +} /* collectstatistics */ +#endif /* qh_KEEPstatistics */ + +/*-<a href="qh-stat.htm#TOC" + >-------------------------------</a><a name="freestatistics">-</a> + + qh_freestatistics( ) + free memory used for statistics +*/ +void qh_freestatistics(void) { + +#if qh_QHpointer + qh_free(qh_qhstat); + qh_qhstat= NULL; +#endif +} /* freestatistics */ + +/*-<a href="qh-stat.htm#TOC" + >-------------------------------</a><a name="initstatistics">-</a> + + qh_initstatistics( ) + allocate and initialize statistics + + notes: + uses qh_malloc() instead of qh_memalloc() since mem.c not set up yet + NOerrors -- qh_initstatistics can not use qh_errexit(), 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(void) { + int i; + realT realx; + int intx; + +#if qh_QHpointer + if(qh_qhstat){ /* qh_initstatistics may be called from Qhull::resetStatistics() */ + qh_free(qh_qhstat); + qh_qhstat= 0; + } + if (!(qh_qhstat= (qhstatT *)qh_malloc(sizeof(qhstatT)))) { + qh_fprintf_stderr(6183, "qhull error (qh_initstatistics): insufficient memory\n"); + qh_exit(qh_ERRmem); /* can not use qh_errexit() */ + } +#endif + + qhstat next= 0; + qh_allstatA(); + qh_allstatB(); + qh_allstatC(); + qh_allstatD(); + qh_allstatE(); + qh_allstatE2(); + qh_allstatF(); + qh_allstatG(); + qh_allstatH(); + qh_allstatI(); + if (qhstat next > (int)sizeof(qhstat id)) { + qh_fprintf(qhmem.ferr, 6184, "qhull error (qh_initstatistics): increase size of qhstat.id[].\n\ + qhstat.next %d should be <= sizeof(qhstat id) %d\n", qhstat next, (int)sizeof(qhstat id)); +#if 0 /* for locating error, Znumridges should be duplicated */ + for(i=0; i < ZEND; i++) { + int j; + for(j=i+1; j < ZEND; j++) { + if (qhstat id[i] == qhstat id[j]) { + qh_fprintf(qhmem.ferr, 6185, "qhull error (qh_initstatistics): duplicated statistic %d at indices %d and %d\n", + qhstat id[i], i, j); + } + } + } +#endif + qh_exit(qh_ERRqhull); /* can not use qh_errexit() */ + } + qhstat init[zinc].i= 0; + qhstat init[zadd].i= 0; + qhstat init[zmin].i= INT_MAX; + qhstat init[zmax].i= INT_MIN; + qhstat init[wadd].r= 0; + qhstat init[wmin].r= REALmax; + qhstat init[wmax].r= -REALmax; + for(i=0; i < ZEND; i++) { + if (qhstat type[i] > ZTYPEreal) { + realx= qhstat init[(unsigned char)(qhstat type[i])].r; + qhstat stats[i].r= realx; + }else if (qhstat type[i] != zdoc) { + intx= qhstat init[(unsigned char)(qhstat type[i])].i; + qhstat stats[i].i= intx; + } + } +} /* initstatistics */ + +/*-<a href="qh-stat.htm#TOC" + >-------------------------------</a><a name="newstats">-</a> + + qh_newstats( ) + returns True if statistics for zdoc + + returns: + next zdoc +*/ +boolT qh_newstats(int idx, int *nextindex) { + boolT isnew= False; + int start, i; + + if (qhstat type[qhstat id[idx]] == zdoc) + start= idx+1; + else + start= idx; + for(i= start; i < qhstat next && qhstat type[qhstat id[i]] != zdoc; i++) { + if (!qh_nostatistic(qhstat id[i]) && !qhstat printed[qhstat id[i]]) + isnew= True; + } + *nextindex= i; + return isnew; +} /* newstats */ + +/*-<a href="qh-stat.htm#TOC" + >-------------------------------</a><a name="nostatistic">-</a> + + qh_nostatistic( index ) + true if no statistic to print +*/ +boolT qh_nostatistic(int i) { + + if ((qhstat type[i] > ZTYPEreal + &&qhstat stats[i].r == qhstat init[(unsigned char)(qhstat type[i])].r) + || (qhstat type[i] < ZTYPEreal + &&qhstat stats[i].i == qhstat init[(unsigned char)(qhstat type[i])].i)) + return True; + return False; +} /* nostatistic */ + +#if qh_KEEPstatistics +/*-<a href="qh-stat.htm#TOC" + >-------------------------------</a><a name="printallstatistics">-</a> + + qh_printallstatistics( fp, string ) + print all statistics with header 'string' +*/ +void qh_printallstatistics(FILE *fp, const char *string) { + + qh_allstatistics(); + qh_collectstatistics(); + qh_printstatistics(fp, string); + qh_memstatistics(fp); +} + + +/*-<a href="qh-stat.htm#TOC" + >-------------------------------</a><a name="printstatistics">-</a> + + qh_printstatistics( fp, string ) + print statistics to a file with header 'string' + skips statistics with qhstat.printed[] (reset with qh_allstatistics) + + see: + qh_printallstatistics() +*/ +void qh_printstatistics(FILE *fp, const char *string) { + int i, k; + realT ave; + + if (qh num_points != qh num_vertices) { + wval_(Wpbalance)= 0; + wval_(Wpbalance2)= 0; + }else + wval_(Wpbalance2)= qh_stddev(zval_(Zpbalance), wval_(Wpbalance), + wval_(Wpbalance2), &ave); + wval_(Wnewbalance2)= qh_stddev(zval_(Zprocessed), wval_(Wnewbalance), + wval_(Wnewbalance2), &ave); + qh_fprintf(fp, 9350, "\n\ +%s\n\ + qhull invoked by: %s | %s\n%s with options:\n%s\n", string, qh rbox_command, + qh qhull_command, qh_version, qh qhull_options); + qh_fprintf(fp, 9351, "\nprecision constants:\n\ + %6.2g max. abs. coordinate in the (transformed) input('Qbd:n')\n\ + %6.2g max. roundoff error for distance computation('En')\n\ + %6.2g max. roundoff error for angle computations\n\ + %6.2g min. distance for outside points ('Wn')\n\ + %6.2g min. distance for visible facets ('Vn')\n\ + %6.2g max. distance for coplanar facets ('Un')\n\ + %6.2g max. facet width for recomputing centrum and area\n\ +", + qh MAXabs_coord, qh DISTround, qh ANGLEround, qh MINoutside, + qh MINvisible, qh MAXcoplanar, qh WIDEfacet); + if (qh KEEPnearinside) + qh_fprintf(fp, 9352, "\ + %6.2g max. distance for near-inside points\n", qh NEARinside); + if (qh premerge_cos < REALmax/2) qh_fprintf(fp, 9353, "\ + %6.2g max. cosine for pre-merge angle\n", qh premerge_cos); + if (qh PREmerge) qh_fprintf(fp, 9354, "\ + %6.2g radius of pre-merge centrum\n", qh premerge_centrum); + if (qh postmerge_cos < REALmax/2) qh_fprintf(fp, 9355, "\ + %6.2g max. cosine for post-merge angle\n", qh postmerge_cos); + if (qh POSTmerge) qh_fprintf(fp, 9356, "\ + %6.2g radius of post-merge centrum\n", qh postmerge_centrum); + qh_fprintf(fp, 9357, "\ + %6.2g max. distance for merging two simplicial facets\n\ + %6.2g max. roundoff error for arithmetic operations\n\ + %6.2g min. denominator for divisions\n\ + zero diagonal for Gauss: ", qh ONEmerge, REALepsilon, qh MINdenom); + for(k=0; k < qh hull_dim; k++) + qh_fprintf(fp, 9358, "%6.2e ", qh NEARzero[k]); + qh_fprintf(fp, 9359, "\n\n"); + for(i=0 ; i < qhstat next; ) + qh_printstats(fp, i, &i); +} /* printstatistics */ +#endif /* qh_KEEPstatistics */ + +/*-<a href="qh-stat.htm#TOC" + >-------------------------------</a><a name="printstatlevel">-</a> + + qh_printstatlevel( fp, id ) + print level information for a statistic + + notes: + nop if id >= ZEND, printed, or same as initial value +*/ +void qh_printstatlevel(FILE *fp, int id) { +#define NULLfield " " + + if (id >= ZEND || qhstat printed[id]) + return; + if (qhstat type[id] == zdoc) { + qh_fprintf(fp, 9360, "%s\n", qhstat doc[id]); + return; + } + if (qh_nostatistic(id) || !qhstat doc[id]) + return; + qhstat printed[id]= True; + if (qhstat count[id] != -1 + && qhstat stats[(unsigned char)(qhstat count[id])].i == 0) + qh_fprintf(fp, 9361, " *0 cnt*"); + else if (qhstat type[id] >= ZTYPEreal && qhstat count[id] == -1) + qh_fprintf(fp, 9362, "%7.2g", qhstat stats[id].r); + else if (qhstat type[id] >= ZTYPEreal && qhstat count[id] != -1) + qh_fprintf(fp, 9363, "%7.2g", qhstat stats[id].r/ qhstat stats[(unsigned char)(qhstat count[id])].i); + else if (qhstat type[id] < ZTYPEreal && qhstat count[id] == -1) + qh_fprintf(fp, 9364, "%7d", qhstat stats[id].i); + else if (qhstat type[id] < ZTYPEreal && qhstat count[id] != -1) + qh_fprintf(fp, 9365, "%7.3g", (realT) qhstat stats[id].i / qhstat stats[(unsigned char)(qhstat count[id])].i); + qh_fprintf(fp, 9366, " %s\n", qhstat doc[id]); +} /* printstatlevel */ + + +/*-<a href="qh-stat.htm#TOC" + >-------------------------------</a><a name="printstats">-</a> + + qh_printstats( fp, index, nextindex ) + print statistics for a zdoc group + + returns: + next zdoc if non-null +*/ +void qh_printstats(FILE *fp, int idx, int *nextindex) { + int j, nexti; + + if (qh_newstats(idx, &nexti)) { + qh_fprintf(fp, 9367, "\n"); + for (j=idx; j<nexti; j++) + qh_printstatlevel(fp, qhstat id[j]); + } + if (nextindex) + *nextindex= nexti; +} /* printstats */ + +#if qh_KEEPstatistics + +/*-<a href="qh-stat.htm#TOC" + >-------------------------------</a><a name="stddev">-</a> + + qh_stddev( 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(int num, realT tot, realT tot2, realT *ave) { + realT stddev; + + *ave= tot/num; + stddev= sqrt(tot2/num - *ave * *ave); + return stddev; +} /* stddev */ + +#endif /* qh_KEEPstatistics */ + +#if !qh_KEEPstatistics +void qh_collectstatistics(void) {} +void qh_printallstatistics(FILE *fp, char *string) {}; +void qh_printstatistics(FILE *fp, char *string) {} +#endif + diff --git a/src/qhull/src/libqhull/stat.h b/src/qhull/src/libqhull/stat.h new file mode 100644 index 000000000..d86fc0a87 --- /dev/null +++ b/src/qhull/src/libqhull/stat.h @@ -0,0 +1,543 @@ +/*<html><pre> -<a href="qh-stat.htm" + >-------------------------------</a><a name="TOP">-</a> + + stat.h + contains all statistics that are collected for qhull + + see qh-stat.htm and stat.c + + Copyright (c) 1993-2015 The Geometry Center. + $Id: //main/2015/qhull/src/libqhull/stat.h#4 $$Change: 2062 $ + $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $ + + recompile qhull if you change this file + + Integer statistics are Z* while real statistics are W*. + + define maydebugx to call a routine at every statistic event + +*/ + +#ifndef qhDEFstat +#define qhDEFstat 1 + +#include "libqhull.h" + +/*-<a href="qh-stat.htm#TOC" + >-------------------------------</a><a name="KEEPstatistics">-</a> + + qh_KEEPstatistics + 0 turns off statistic gathering (except zzdef/zzinc/zzadd/zzval/wwval) +*/ +#ifndef qh_KEEPstatistics +#define qh_KEEPstatistics 1 +#endif + +/*-<a href="qh-stat.htm#TOC" + >-------------------------------</a><a name="statistics">-</a> + + Zxxx for integers, Wxxx for reals + + notes: + be sure that all statistics are defined in stat.c + otherwise initialization may core dump + can pick up all statistics by: + grep '[zw].*_[(][ZW]' *.c >z.x + remove trailers with query">-</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, + Zconcaveridges, + Zconcaveridge, + Zcoplanar, + Wcoplanarmax, + Wcoplanartot, + Zcoplanarangle, + Zcoplanarcentrum, + Zcoplanarhorizon, + Zcoplanarinside, + Zcoplanarpart, + Zcoplanarridges, + Wcpu, + Zcyclefacetmax, + Zcyclefacettot, + Zcyclehorizon, + Zcyclevertex, + Zdegen, + Wdegenmax, + Wdegentot, + Zdegenvertex, + Zdelfacetdup, + Zdelridge, + Zdelvertextot, + Zdelvertexmax, + Zdetsimplex, + Zdistcheck, + Zdistconvex, + Zdistgood, + Zdistio, + Zdistplane, + Zdiststat, + Zdistvertex, + Zdistzero, + Zdoc1, + Zdoc2, + Zdoc3, + Zdoc4, + Zdoc5, + Zdoc6, + Zdoc7, + Zdoc8, + Zdoc9, + Zdoc10, + Zdoc11, + Zdoc12, + Zdropdegen, + Zdropneighbor, + Zdupflip, + Zduplicate, + Wduplicatemax, + Wduplicatetot, + Zdupridge, + Zdupsame, + Zflipped, + Wflippedmax, + Wflippedtot, + Zflippedfacets, + Zfindbest, + Zfindbestmax, + Zfindbesttot, + Zfindcoplanar, + Zfindfail, + Zfindhorizon, + Zfindhorizonmax, + Zfindhorizontot, + Zfindjump, + Zfindnew, + Zfindnewmax, + Zfindnewtot, + Zfindnewjump, + Zfindnewsharp, + Zgauss0, + Zgoodfacet, + Zhashlookup, + Zhashridge, + Zhashridgetest, + Zhashtests, + Zinsidevisible, + Zintersect, + Zintersectfail, + Zintersectmax, + Zintersectnum, + Zintersecttot, + Zmaxneighbors, + Wmaxout, + Wmaxoutside, + Zmaxridges, + Zmaxvertex, + Zmaxvertices, + Zmaxvneighbors, + Zmemfacets, + Zmempoints, + Zmemridges, + Zmemvertices, + Zmergeflipdup, + Zmergehorizon, + Zmergeinittot, + Zmergeinitmax, + Zmergeinittot2, + Zmergeintohorizon, + Zmergenew, + Zmergesettot, + Zmergesetmax, + Zmergesettot2, + Zmergesimplex, + Zmergevertex, + Wmindenom, + Wminvertex, + Zminnorm, + Zmultiridge, + Znearlysingular, + Zneighbor, + Wnewbalance, + Wnewbalance2, + Znewfacettot, + Znewfacetmax, + Znewvertex, + Wnewvertex, + Wnewvertexmax, + Znoarea, + Znonsimplicial, + Znowsimplicial, + Znotgood, + Znotgoodnew, + Znotmax, + Znumfacets, + Znummergemax, + Znummergetot, + Znumneighbors, + Znumridges, + Znumvertices, + Znumvisibility, + Znumvneighbors, + Zonehorizon, + Zpartangle, + Zpartcoplanar, + Zpartflip, + Zparthorizon, + Zpartinside, + Zpartition, + Zpartitionall, + Zpartnear, + Zpbalance, + Wpbalance, + Wpbalance2, + Zpostfacets, + Zpremergetot, + Zprocessed, + Zremvertex, + Zremvertexdel, + Zrenameall, + Zrenamepinch, + Zrenameshare, + Zretry, + Wretrymax, + Zridge, + Wridge, + Wridgemax, + Zridge0, + Wridge0, + Wridge0max, + Zridgemid, + Wridgemid, + Wridgemidmax, + Zridgeok, + Wridgeok, + Wridgeokmax, + Zsearchpoints, + Zsetplane, + Ztestvneighbor, + Ztotcheck, + Ztothorizon, + Ztotmerge, + Ztotpartcoplanar, + Ztotpartition, + Ztotridges, + Ztotvertices, + Ztotvisible, + Ztricoplanar, + Ztricoplanarmax, + Ztricoplanartot, + Ztridegen, + Ztrimirror, + Ztrinull, + Wvertexmax, + Wvertexmin, + Zvertexridge, + Zvertexridgetot, + Zvertexridgemax, + Zvertices, + Zvisfacettot, + Zvisfacetmax, + Zvisit, + Zvisit2max, + Zvisvertextot, + Zvisvertexmax, + Zvvisit, + Zvvisit2max, + Zwidefacet, + Zwidevertices, + ZEND}; + +/*-<a href="qh-stat.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, + Zdistzero, + Zdoc1, + Zdoc2, + Zdoc3, + Zdoc11, + Zflippedfacets, + Zgauss0, + Zminnorm, + Zmultiridge, + Znearlysingular, + Wnewvertexmax, + Znumvisibility, + Zpartcoplanar, + Zpartition, + Zpartitionall, + Zprocessed, + Zretry, + Zridge, + Wridge, + Wridgemax, + Zridge0, + Wridge0, + Wridge0max, + Zridgemid, + Wridgemid, + Wridgemidmax, + Zridgeok, + Wridgeok, + Wridgeokmax, + Zsetplane, + Ztotcheck, + Ztotmerge, + ZEND}; +#endif + +/*-<a href="qh-stat.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.htm#TOC" + >--------------------------------</a><a name="MAYdebugx">-</a> + + MAYdebugx + define as maydebug() to be called frequently for error trapping +*/ +#define MAYdebugx + +/*-<a href="qh-stat.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) qhstat id[qhstat next++]=name; \ + qhstat doc[name]= string; qhstat count[name]= cnt; qhstat type[name]= stype +#if qh_KEEPstatistics +#define zdef_(stype,name,string,cnt) qhstat id[qhstat next++]=name; \ + qhstat doc[name]= string; qhstat count[name]= cnt; qhstat type[name]= stype +#else +#define zdef_(type,name,doc,count) +#endif + +/*-<a href="qh-stat.htm#TOC" + >--------------------------------</a><a name="zinc_">-</a> + + zzinc_( name ), zinc_( name) + increment an integer statistic +*/ +#define zzinc_(id) {MAYdebugx; qhstat stats[id].i++;} +#if qh_KEEPstatistics +#define zinc_(id) {MAYdebugx; qhstat stats[id].i++;} +#else +#define zinc_(id) {} +#endif + +/*-<a href="qh-stat.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; qhstat stats[id].i += (val);} +#define wwadd_(id, val) {MAYdebugx; qhstat stats[id].r += (val);} +#if qh_KEEPstatistics +#define zadd_(id, val) {MAYdebugx; qhstat stats[id].i += (val);} +#define wadd_(id, val) {MAYdebugx; qhstat stats[id].r += (val);} +#else +#define zadd_(id, val) {} +#define wadd_(id, val) {} +#endif + +/*-<a href="qh-stat.htm#TOC" + >--------------------------------</a><a name="zval_">-</a> + + zzval_( name ), zval_( name ), wwval_( name ) + set or return value of a statistic +*/ +#define zzval_(id) ((qhstat stats[id]).i) +#define wwval_(id) ((qhstat stats[id]).r) +#if qh_KEEPstatistics +#define zval_(id) ((qhstat stats[id]).i) +#define wval_(id) ((qhstat stats[id]).r) +#else +#define zval_(id) qhstat tempi +#define wval_(id) qhstat tempr +#endif + +/*-<a href="qh-stat.htm#TOC" + >--------------------------------</a><a name="zmax_">-</a> + + zmax_( id, val ), wmax_( id, value ) + maximize id with val +*/ +#define wwmax_(id, val) {MAYdebugx; maximize_(qhstat stats[id].r,(val));} +#if qh_KEEPstatistics +#define zmax_(id, val) {MAYdebugx; maximize_(qhstat stats[id].i,(val));} +#define wmax_(id, val) {MAYdebugx; maximize_(qhstat stats[id].r,(val));} +#else +#define zmax_(id, val) {} +#define wmax_(id, val) {} +#endif + +/*-<a href="qh-stat.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_(qhstat stats[id].i,(val));} +#define wmin_(id, val) {MAYdebugx; minimize_(qhstat stats[id].r,(val));} +#else +#define zmin_(id, val) {} +#define wmin_(id, val) {} +#endif + +/*================== stat.h types ==============*/ + + +/*-<a href="qh-stat.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.htm#TOC" + >--------------------------------</a><a name="qhstat">-</a> + + qhstat + global data structure for statistics, similar to qh and qhrbox + + notes: + access to qh_qhstat is via the "qhstat" macro. There are two choices + qh_QHpointer = 1 access globals via a pointer + enables qh_saveqhull() and qh_restoreqhull() + = 0 qh_qhstat is a static data structure + only one instance of qhull() can be active at a time + default value + qh_QHpointer is defined in libqhull.h + qh_QHpointer_dllimport and qh_dllimport define qh_qh as __declspec(dllimport) [libqhull.h] + + allocated in stat.c using qh_malloc() +*/ +#ifndef DEFqhstatT +#define DEFqhstatT 1 +typedef struct qhstatT qhstatT; +#endif + +#if qh_QHpointer_dllimport +#define qhstat qh_qhstat-> +__declspec(dllimport) extern qhstatT *qh_qhstat; +#elif qh_QHpointer +#define qhstat qh_qhstat-> +extern qhstatT *qh_qhstat; +#elif qh_dllimport +#define qhstat qh_qhstat. +__declspec(dllimport) extern qhstatT qh_qhstat; +#else +#define qhstat qh_qhstat. +extern qhstatT qh_qhstat; +#endif +struct qhstatT { + intrealT stats[ZEND]; /* integer and real statistics */ + unsigned char id[ZEND+10]; /* id's in print order */ + const char *doc[ZEND]; /* array of documentation strings */ + short int count[ZEND]; /* -1 if none, else index of count to use */ + char type[ZEND]; /* type, see ztypes above */ + char printed[ZEND]; /* true, if statistic has been printed */ + intrealT init[ZTYPEend]; /* initial values by types, set initstatistics */ + + int next; /* next index for zdef_ */ + int precision; /* index for precision problems */ + int vridges; /* index for Voronoi ridges */ + int tempi; + realT tempr; +}; + +/*========== function prototypes ===========*/ + +void qh_allstatA(void); +void qh_allstatB(void); +void qh_allstatC(void); +void qh_allstatD(void); +void qh_allstatE(void); +void qh_allstatE2(void); +void qh_allstatF(void); +void qh_allstatG(void); +void qh_allstatH(void); +void qh_allstatI(void); +void qh_allstatistics(void); +void qh_collectstatistics(void); +void qh_freestatistics(void); +void qh_initstatistics(void); +boolT qh_newstats(int idx, int *nextindex); +boolT qh_nostatistic(int i); +void qh_printallstatistics(FILE *fp, const char *string); +void qh_printstatistics(FILE *fp, const char *string); +void qh_printstatlevel(FILE *fp, int id); +void qh_printstats(FILE *fp, int idx, int *nextindex); +realT qh_stddev(int num, realT tot, realT tot2, realT *ave); + +#endif /* qhDEFstat */ diff --git a/src/qhull/src/libqhull/user.c b/src/qhull/src/libqhull/user.c new file mode 100644 index 000000000..d4726eaa3 --- /dev/null +++ b/src/qhull/src/libqhull/user.c @@ -0,0 +1,538 @@ +/*<html><pre> -<a href="qh-user.htm" + >-------------------------------</a><a name="TOP">-</a> + + user.c + user redefinable functions + + see user2.c for qh_fprintf, qh_malloc, qh_free + + see README.txt see COPYING.txt for copyright information. + + see libqhull.h for data structures, macros, and user-callable functions. + + see user_eg.c, user_eg2.c, and unix.c for examples. + + see user.h for user-definable constants + + use qh_NOmem in mem.h to turn off memory management + use qh_NOmerge in user.h to turn off facet merging + set qh_KEEPstatistics in user.h to 0 to turn off statistics + + This is unsupported software. You're welcome to make changes, + but you're on your own if something goes wrong. Use 'Tc' to + check frequently. Usually qhull will report an error if + a data structure becomes inconsistent. If so, it also reports + the last point added to the hull, e.g., 102. You can then trace + the execution of qhull with "T4P102". + + Please report any errors that you fix to qhull@qhull.org + + 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.c for an + example. We recommend keeping io.o for the extra debugging + information it supplies. +*/ + +#include "qhull_a.h" + +#include <stdarg.h> + +/*-<a href="qh-user.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.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 qh_opt.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 */ + + QHULL_LIB_CHECK /* Check for compatible library */ + +#if qh_QHpointer /* see user.h */ + if (qh_qh){ /* should be NULL */ + qh_printf_stderr(6238, "Qhull link error. The global variable qh_qh was not initialized\n\ + to NULL by global.c. Please compile this program with -Dqh_QHpointer_dllimport\n\ + as well as -Dqh_QHpointer, or use libqhullstatic, or use a different tool chain.\n\n"); + exit(1); + } +#endif + + /* initialize dim, numpoints, points[], ismalloc here */ + exitcode= qh_new_qhull(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_ALL); + qh_memfreeshort(&curlong, &totlong); + if (curlong || totlong) + qh_fprintf(errfile, 7068, "qhull internal warning (main): did not free %d bytes of long memory(%d pieces)\n", totlong, curlong); +} +#endif + +/*-<a href="qh-user.htm#TOC" + >-------------------------------</a><a name="new_qhull">-</a> + + qh_new_qhull( dim, numpoints, points, ismalloc, qhull_cmd, outfile, errfile ) + build new qhull data structure and return exitcode (0 if no errors) + if numpoints=0 and points=NULL, initializes qhull + + 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(). + + 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') + + + To allow multiple, concurrent calls to qhull() + - set qh_QHpointer in user.h + - use qh_save_qhull and qh_restore_qhull to swap the global data structure between calls. + - use qh_freeqhull(qh_ALL) to free intermediate convex hulls + + see: + Qhull-template at the beginning of this file. + An example of using qh_new_qhull is user_eg.c +*/ +int qh_new_qhull(int dim, int numpoints, coordT *points, boolT ismalloc, + char *qhull_cmd, FILE *outfile, FILE *errfile) { + /* 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; + static boolT firstcall = True; + coordT *new_points; + if(!errfile){ + errfile= stderr; + } + if (firstcall) { + qh_meminit(errfile); + firstcall= False; + } else { + qh_memcheck(); + } + if (strncmp(qhull_cmd, "qhull ", (size_t)6)) { + qh_fprintf(errfile, 6186, "qhull error (qh_new_qhull): start qhull_cmd argument with \"qhull \"\n"); + return qh_ERRinput; + } + qh_initqhull_start(NULL, outfile, errfile); + if(numpoints==0 && points==NULL){ + trace1((qh ferr, 1047, "qh_new_qhull: initialize Qhull\n")); + return 0; + } + trace1((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(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; + qh_setfeasible(hulldim); + new_points= qh_sethalfspace_all(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(new_points, numpoints, hulldim, new_ismalloc); + qh_qhull(); + qh_check_output(); + if (outfile) { + qh_produce_output(); + }else { + qh_prepare_output(); + } + if (qh VERIFYoutput && !qh STOPpoint && !qh STOPcone) + qh_check_points(); + } + qh NOerrexit = True; + return exitcode; +} /* new_qhull */ + +/*-<a href="qh-user.htm#TOC" + >-------------------------------</a><a name="errexit">-</a> + + qh_errexit( 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.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(int exitcode, facetT *facet, ridgeT *ridge) { + + if (qh ERREXITcalled) { + qh_fprintf(qh ferr, 8126, "\nqhull error while processing previous error. Exit program\n"); + qh_exit(qh_ERRqhull); + } + qh ERREXITcalled= True; + if (!qh QHULLfinished) + qh hulltime= qh_CPUclock - qh hulltime; + qh_errprint("ERRONEOUS", facet, NULL, ridge, NULL); + qh_fprintf(qh ferr, 8127, "\nWhile executing: %s | %s\n", qh rbox_command, qh qhull_command); + qh_fprintf(qh ferr, 8128, "Options selected for Qhull %s:\n%s\n", qh_version, qh qhull_options); + if (qh furthest_id >= 0) { + qh_fprintf(qh ferr, 8129, "Last point added to hull was p%d.", qh furthest_id); + if (zzval_(Ztotmerge)) + qh_fprintf(qh ferr, 8130, " Last merge was #%d.", zzval_(Ztotmerge)); + if (qh QHULLfinished) + qh_fprintf(qh ferr, 8131, "\nQhull has finished constructing the hull."); + else if (qh POSTmerging) + qh_fprintf(qh ferr, 8132, "\nQhull has started post-merging."); + qh_fprintf(qh ferr, 8133, "\n"); + } + if (qh FORCEoutput && (qh QHULLfinished || (!facet && !ridge))) + qh_produce_output(); + else if (exitcode != qh_ERRinput) { + if (exitcode != qh_ERRsingular && zzval_(Zsetplane) > qh hull_dim+1) { + qh_fprintf(qh ferr, 8134, "\nAt error exit:\n"); + qh_printsummary(qh ferr); + if (qh PRINTstatistics) { + qh_collectstatistics(); + qh_printstatistics(qh ferr, "at error exit"); + qh_memstatistics(qh ferr); + } + } + if (qh PRINTprecision) + qh_printstats(qh ferr, qhstat precision, NULL); + } + if (!exitcode) + exitcode= qh_ERRqhull; + else if (exitcode == qh_ERRsingular) + qh_printhelp_singular(qh ferr); + else if (exitcode == qh_ERRprec && !qh PREmerge) + qh_printhelp_degenerate(qh ferr); + if (qh NOerrexit) { + qh_fprintf(qh ferr, 6187, "qhull error while ending program, or qh->NOerrexit not cleared after setjmp(). Exit program with error.\n"); + qh_exit(qh_ERRqhull); + } + qh ERREXITcalled= False; + qh NOerrexit= True; + qh ALLOWrestart= False; /* longjmp will undo qh_build_withrestart */ + longjmp(qh errexit, exitcode); +} /* errexit */ + + +/*-<a href="qh-user.htm#TOC" + >-------------------------------</a><a name="errprint">-</a> + + qh_errprint( 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(const char *string, facetT *atfacet, facetT *otherfacet, ridgeT *atridge, vertexT *atvertex) { + int i; + + if (atfacet) { + qh_fprintf(qh ferr, 8135, "%s FACET:\n", string); + qh_printfacet(qh ferr, atfacet); + } + if (otherfacet) { + qh_fprintf(qh ferr, 8136, "%s OTHER FACET:\n", string); + qh_printfacet(qh ferr, otherfacet); + } + if (atridge) { + qh_fprintf(qh ferr, 8137, "%s RIDGE:\n", string); + qh_printridge(qh ferr, atridge); + if (atridge->top && atridge->top != atfacet && atridge->top != otherfacet) + qh_printfacet(qh ferr, atridge->top); + if (atridge->bottom + && atridge->bottom != atfacet && atridge->bottom != otherfacet) + qh_printfacet(qh ferr, atridge->bottom); + if (!atfacet) + atfacet= atridge->top; + if (!otherfacet) + otherfacet= otherfacet_(atridge, atfacet); + } + if (atvertex) { + qh_fprintf(qh ferr, 8138, "%s VERTEX:\n", string); + qh_printvertex(qh ferr, atvertex); + } + if (qh fout && qh FORCEoutput && atfacet && !qh QHULLfinished && !qh IStracing) { + qh_fprintf(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 fout, qh PRINTout[i], atfacet, otherfacet, + !qh_ALL); + } +} /* errprint */ + + +/*-<a href="qh-user.htm#TOC" + >-------------------------------</a><a name="printfacetlist">-</a> + + qh_printfacetlist( 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(facetT *facetlist, setT *facets, boolT printall) { + facetT *facet, **facetp; + + qh_printbegin(qh ferr, qh_PRINTfacets, facetlist, facets, printall); + FORALLfacet_(facetlist) + qh_printafacet(qh ferr, qh_PRINTfacets, facet, printall); + FOREACHfacet_(facets) + qh_printafacet(qh ferr, qh_PRINTfacets, facet, printall); + qh_printend(qh ferr, qh_PRINTfacets, facetlist, facets, printall); +} /* printfacetlist */ + + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printhelp_degenerate">-</a> + + qh_printhelp_degenerate( fp ) + prints descriptive message for precision error + + notes: + no message if qh_QUICKhelp +*/ +void qh_printhelp_degenerate(FILE *fp) { + + if (qh MERGEexact || qh PREmerge || qh JOGGLEmax < REALmax/2) + qh_fprintf(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(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(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.h).\n"); +#endif + if (qh DELAUNAY && !qh SCALElast && qh MAXabs_coord > 1e4) + qh_fprintf(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(fp, 9372, "\ +When computing the Delaunay triangulation:\n\ + - use 'Qz' to add a point at-infinity. This reduces precision problems.\n"); + + qh_fprintf(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(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-globa.htm#TOC" + >-------------------------------</a><a name="printhelp_narrowhull">-</a> + + qh_printhelp_narrowhull( minangle ) + Warn about a narrow hull + + notes: + Alternatively, reduce qh_WARNnarrow in user.h + +*/ +void qh_printhelp_narrowhull(FILE *fp, realT minangle) { + + qh_fprintf(fp, 9375, "qhull precision warning: \n\ +The initial hull is narrow (cosine of min. angle is %.16f).\n\ +Is the input lower dimensional (e.g., on a plane in 3-d)? Qhull may\n\ +produce a wide facet. Options 'QbB' (scale to unit box) or 'Qbb' (scale\n\ +last coordinate) may remove this warning. Use 'Pp' to skip this warning.\n\ +See 'Limitations' in qh-impre.htm.\n", + -minangle); /* convert from angle between normals to angle between facets */ +} /* printhelp_narrowhull */ + +/*-<a href="qh-io.htm#TOC" + >-------------------------------</a><a name="printhelp_singular">-</a> + + qh_printhelp_singular( fp ) + prints descriptive message for singular input +*/ +void qh_printhelp_singular(FILE *fp) { + facetT *facet; + vertexT *vertex, **vertexp; + realT min, max, *coord, dist; + int i,k; + + qh_fprintf(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(fp, "", qh facet_list, NULL, qh_ALL); + if (!qh_QUICKhelp) + qh_fprintf(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(fp, "center point", qh hull_dim, qh interior_point, qh_IDunknown); + qh_fprintf(fp, 9378, "\n"); + FORALLfacets { + qh_fprintf(fp, 9379, "facet"); + FOREACHvertex_(facet->vertices) + qh_fprintf(fp, 9380, " p%d", qh_pointid(vertex->point)); + zinc_(Zdistio); + qh_distplane(qh interior_point, facet, &dist); + qh_fprintf(fp, 9381, " distance= %4.2g\n", dist); + } + if (!qh_QUICKhelp) { + if (qh HALFspace) + qh_fprintf(fp, 9382, "\n\ +These points are the dual of the given halfspaces. They indicate that\n\ +the intersection is degenerate.\n"); + qh_fprintf(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(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(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(fp, 9386, " %d: %8.4g %8.4g difference= %4.4g\n", k, min, max, max-min); + } + if (!qh_QUICKhelp) { + qh_fprintf(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(fp, 9388, "\ + - recompile qhull for realT precision(#define REALfloat 0 in libqhull.h).\n"); +#endif + qh_fprintf(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-globa.htm#TOC" + >-------------------------------</a><a name="user_memsizes">-</a> + + qh_user_memsizes() + allocate up to 10 additional, quick allocation sizes + + notes: + increase maximum number of allocations in qh_initqhull_mem() +*/ +void qh_user_memsizes(void) { + + /* qh_memsize(size); */ +} /* user_memsizes */ + + diff --git a/src/qhull/src/libqhull/user.h b/src/qhull/src/libqhull/user.h new file mode 100644 index 000000000..523aa7b4e --- /dev/null +++ b/src/qhull/src/libqhull/user.h @@ -0,0 +1,909 @@ +/*<html><pre> -<a href="qh-user.htm" + >-------------------------------</a><a name="TOP">-</a> + + user.h + user redefinable constants + + for each source file, user.h is included first + see qh-user.htm. see COPYING for copyright information. + + See user.c for sample code. + + before reading any code, review libqhull.h for data structure definitions and + the "qh" macro. + +Sections: + ============= qhull library constants ====================== + ============= data types and configuration macros ========== + ============= performance related constants ================ + ============= memory constants ============================= + ============= joggle constants ============================= + ============= conditional compilation ====================== + ============= -merge constants- ============================ + +Code flags -- + NOerrors -- the code does not call qh_errexit() + WARN64 -- the code may be incompatible with 64-bit pointers + +*/ + +#include <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.htm#TOC" + >--------------------------------</a><a name="filenamelen">-</a> + + FILENAMElen -- max length for TI and TO filenames + +*/ + +#define qh_FILENAMElen 500 + +/*-<a href="qh-user.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 errors. + + def counters = [27, 1048, 2059, 3026, 4068, 5003, + 6273, 7081, 8147, 9411, 10000, 11029] + + See: qh_ERR* [libqhull.h] +*/ + +#define MSG_TRACE0 0 +#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_FIXUP 11000 /* FIXUP QH11... */ +#define MSG_MAXLEN 3000 /* qh_printhelp_degenerate() in user.c */ + + +/*-<a href="qh-user.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.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.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.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 */ +#define qh_SECticks CLOCKS_PER_SECOND + +#elif defined(CLOCKS_PER_SEC) +#define qh_CPUclock ((unsigned long)clock()) /* return CPU clock */ +#define qh_SECticks CLOCKS_PER_SEC + +#elif defined(CLK_TCK) +#define qh_CPUclock ((unsigned long)clock()) /* return CPU clock */ +#define qh_SECticks CLK_TCK + +#else +#define qh_CPUclock ((unsigned long)clock()) /* return CPU clock */ +#define qh_SECticks 1E6 +#endif + +#elif (qh_CLOCKtype == 2) +#define qh_CPUclock qh_clock() /* return CPU clock */ +#define qh_SECticks 100 + +#else /* qh_CLOCKtype == ? */ +#error unknown clock option +#endif + +/*-<a href="qh-user.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() with 31 bits (included with Qhull) + + 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_(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_(seed) srand((unsigned)seed); + +#elif (qh_RANDOMtype == 3) +#define qh_RANDOMmax ((realT)0x7fffffffUL) /* 31 bits, Sun */ +#define qh_RANDOMint rand() +#define qh_RANDOMseed_(seed) srand((unsigned)seed); + +#elif (qh_RANDOMtype == 4) +#define qh_RANDOMmax ((realT)0x7fffffffUL) /* 31 bits, lrand38()/MAX */ +#define qh_RANDOMint lrand48() +#define qh_RANDOMseed_(seed) srand48(seed); + +#elif (qh_RANDOMtype == 5) +#define qh_RANDOMmax ((realT)2147483646UL) /* 31 bits, qh_rand/MAX */ +#define qh_RANDOMint qh_rand() +#define qh_RANDOMseed_(seed) qh_srand(seed); +/* unlike rand(), never returns 0 */ + +#else +#error: unknown random option +#endif + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="ORIENTclock">-</a> + + qh_ORIENTclock + 0 for inward pointing normals by Geomview convention +*/ +#define qh_ORIENTclock 0 + + +/*============================================================*/ +/*============= joggle constants =============================*/ +/*============================================================*/ + +/*-<a href="qh-user.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.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.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.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.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.htm#TOC" +>--------------------------------</a><a name="JOGGLEmaxretry">-</a> + +qh_JOGGLEmaxretry +stop after qh_JOGGLEmaxretry attempts +*/ +#define qh_JOGGLEmaxretry 100 + +/*============================================================*/ +/*============= performance related constants ================*/ +/*============================================================*/ + +/*-<a href="qh-user.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.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.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.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.htm#TOC" + >--------------------------------</a><a name="MEMalign">-</a> + + qh_MEMalign + memory alignment for qh_meminitbuffers() in global.c + + notes: + to avoid bus errors, memory allocation must consider alignment requirements. + malloc() automatically takes care of alignment. Since mem.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.htm#TOC" + >--------------------------------</a><a name="MEMbufsize">-</a> + + qh_MEMbufsize + size of additional memory buffers + + notes: + used for qh_meminitbuffers() in global.c +*/ +#define qh_MEMbufsize 0x10000 /* allocate 64K memory buffers */ + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="MEMinitbuf">-</a> + + qh_MEMinitbuf + size of initial memory buffer + + notes: + use for qh_meminitbuffers() in global.c +*/ +#define qh_MEMinitbuf 0x20000 /* initially allocate 128K buffer */ + +/*-<a href="qh-user.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.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.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.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.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.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.htm#TOC" + >--------------------------------</a><a name="NOmerge">-</a> + + qh_NOmerge + disables facet merging if defined + + notes: + This saves about 10% space. + + Unless 'Q0' + qh_NOmerge sets 'QJ' to avoid precision errors + + #define qh_NOmerge + + see: + <a href="mem.h#NOmem">qh_NOmem</a> in mem.c + + see user.c/user_eg.c for removing io.o +*/ + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="NOtrace">-</a> + + qh_NOtrace + no tracing if defined + + notes: + This saves about 5% space. + + #define qh_NOtrace +*/ + +/*-<a href="qh-user.htm#TOC" + >--------------------------------</a><a name="QHpointer">-</a> + + qh_QHpointer + access global data with pointer or static structure + + qh_QHpointer = 1 access globals via a pointer to allocated memory + enables qh_saveqhull() and qh_restoreqhull() + [2010, gcc] costs about 4% in time and 4% in space + [2003, msvc] costs about 8% in time and 2% in space + + = 0 qh_qh and qh_qhstat are static data structures + only one instance of qhull() can be active at a time + default value + + qh_QHpointer_dllimport and qh_dllimport define qh_qh as __declspec(dllimport) [libqhull.h] + It is required for msvc-2005. It is not needed for gcc. + + notes: + [jan'16] qh_QHpointer is deprecated for Qhull. Use libqhull_r instead. + all global variables for qhull are in qh, qhmem, and qhstat + qh is defined in libqhull.h + qhmem is defined in mem.h + qhstat is defined in stat.h + +*/ +#ifdef qh_QHpointer +#if qh_dllimport +#error QH6207 Qhull error: Use qh_QHpointer_dllimport instead of qh_dllimport with qh_QHpointer +#endif +#else +#define qh_QHpointer 0 +#if qh_QHpointer_dllimport +#error QH6234 Qhull error: Use qh_dllimport instead of qh_QHpointer_dllimport when qh_QHpointer is not defined +#endif +#endif +#if 0 /* sample code */ + qhT *oldqhA, *oldqhB; + + exitcode= qh_new_qhull(dim, numpoints, points, ismalloc, + flags, outfile, errfile); + /* use results from first call to qh_new_qhull */ + oldqhA= qh_save_qhull(); + exitcode= qh_new_qhull(dimB, numpointsB, pointsB, ismalloc, + flags, outfile, errfile); + /* use results from second call to qh_new_qhull */ + oldqhB= qh_save_qhull(); + qh_restore_qhull(&oldqhA); + /* use results from first call to qh_new_qhull */ + qh_freeqhull(qh_ALL); /* frees all memory used by first call */ + qh_restore_qhull(&oldqhB); + /* use results from second call to qh_new_qhull */ + qh_freeqhull(!qh_ALL); /* frees long memory used by second call */ + qh_memfreeshort(&curlong, &totlong); /* frees short memory and memory allocator */ +#endif + +/*-<a href="qh-user.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.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.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.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.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.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.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.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.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.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 +*/ +#define qh_RATIOnearinside 5 + +/*-<a href="qh-user.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. + + 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.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.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.htm#TOC" + >--------------------------------</a><a name="WIDEduplicate">-</a> + + qh_WIDEduplicate + Merge ratio for errexit from qh_forcedmerges due to duplicate ridge + Override with option Q12 no-wide-duplicate + + Notes: + Merging a duplicate ridge can lead to very wide facets. + A future release of qhull will avoid duplicate ridges by removing duplicate sub-ridges from the horizon +*/ +#define qh_WIDEduplicate 100 + +/*-<a href="qh-user.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.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.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/src/qhull/src/libqhull/usermem.c b/src/qhull/src/libqhull/usermem.c new file mode 100644 index 000000000..0e99e8f66 --- /dev/null +++ b/src/qhull/src/libqhull/usermem.c @@ -0,0 +1,94 @@ +/*<html><pre> -<a href="qh-user.htm" + >-------------------------------</a><a name="TOP">-</a> + + usermem.c + qh_exit(), qh_free(), and qh_malloc() + + See README.txt. + + If you redefine one of these functions you must redefine all of them. + If you recompile and load this file, then usermem.o will not be loaded + from qhull.a or qhull.lib + + See libqhull.h for data structures, macros, and user-callable functions. + See user.c for qhull-related, redefinable functions + see user.h for user-definable constants + See userprintf.c for qh_fprintf and userprintf_rbox.c for qh_fprintf_rbox + + Please report any errors that you fix to qhull@qhull.org +*/ + +#include "libqhull.h" + +#include <stdarg.h> +#include <stdlib.h> + +/*-<a href="qh-user.htm#TOC" + >-------------------------------</a><a name="qh_exit">-</a> + + qh_exit( exitcode ) + exit program + + 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.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 + + It is typically followed by qh_errexit(). + + Redefine this function to avoid using stderr + + Use qh_fprintf [userprintf.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.htm#TOC" +>-------------------------------</a><a name="qh_free">-</a> + + qh_free( mem ) + free memory + + notes: + same as free() + No calls to qh_errexit() +*/ +void qh_free(void *mem) { + free(mem); +} /* free */ + +/*-<a href="qh-user.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/src/qhull/src/libqhull/userprintf.c b/src/qhull/src/libqhull/userprintf.c new file mode 100644 index 000000000..190d7cd79 --- /dev/null +++ b/src/qhull/src/libqhull/userprintf.c @@ -0,0 +1,66 @@ +/*<html><pre> -<a href="qh-user.htm" + >-------------------------------</a><a name="TOP">-</a> + + userprintf.c + qh_fprintf() + + see README.txt see COPYING.txt for copyright information. + + If you recompile and load this file, then userprintf.o will not be loaded + from qhull.a or qhull.lib + + See libqhull.h for data structures, macros, and user-callable functions. + See user.c for qhull-related, redefinable functions + see user.h for user-definable constants + See usermem.c for qh_exit(), qh_free(), and qh_malloc() + see Qhull.cpp and RboxPoints.cpp for examples. + + Please report any errors that you fix to qhull@qhull.org +*/ + +#include "libqhull.h" +#include "mem.h" + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +/*-<a href="qh-user.htm#TOC" + >-------------------------------</a><a name="qh_fprintf">-</a> + + qh_fprintf(fp, msgcode, format, list of args ) + print arguments to *fp according to format + Use qh_fprintf_rbox() for rboxlib.c + + notes: + 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(FILE *fp, int msgcode, const char *fmt, ... ) { + va_list args; + + if (!fp) { + /* could use qhmem.ferr, but probably better to be cautious */ + qh_fprintf_stderr(6232, "Qhull internal error (userprintf.c): fp is 0. Wrong qh_fprintf called.\n"); + qh_errexit(6232, NULL, NULL); + } + va_start(args, fmt); +#if qh_QHpointer + if (qh_qh && qh ANNOTATEoutput) { +#else + if (qh ANNOTATEoutput) { +#endif + fprintf(fp, "[QH%.4d]", msgcode); + }else if (msgcode >= MSG_ERROR && msgcode < MSG_STDERR ) { + fprintf(fp, "QH%.4d ", msgcode); + } + vfprintf(fp, fmt, args); + va_end(args); + + /* Place debugging traps here. Use with option 'Tn' */ + +} /* qh_fprintf */ + diff --git a/src/qhull/src/libqhull/userprintf_rbox.c b/src/qhull/src/libqhull/userprintf_rbox.c new file mode 100644 index 000000000..8edd2001a --- /dev/null +++ b/src/qhull/src/libqhull/userprintf_rbox.c @@ -0,0 +1,53 @@ +/*<html><pre> -<a href="qh-user.htm" + >-------------------------------</a><a name="TOP">-</a> + + userprintf_rbox.c + qh_fprintf_rbox() + + see README.txt see COPYING.txt for copyright information. + + If you recompile and load this file, then userprintf_rbox.o will not be loaded + from qhull.a or qhull.lib + + See libqhull.h for data structures, macros, and user-callable functions. + See user.c for qhull-related, redefinable functions + see user.h for user-definable constants + See usermem.c for qh_exit(), qh_free(), and qh_malloc() + see Qhull.cpp and RboxPoints.cpp for examples. + + Please report any errors that you fix to qhull@qhull.org +*/ + +#include "libqhull.h" + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +/*-<a href="qh-user.htm#TOC" + >-------------------------------</a><a name="qh_fprintf_rbox">-</a> + + qh_fprintf_rbox(fp, msgcode, format, list of args ) + print arguments to *fp according to format + Use qh_fprintf_rbox() for rboxlib.c + + notes: + same as fprintf() + fgets() is not trapped like fprintf() + exit qh_fprintf_rbox via qh_errexit_rbox() +*/ + +void qh_fprintf_rbox(FILE *fp, int msgcode, const char *fmt, ... ) { + va_list args; + + if (!fp) { + qh_fprintf_stderr(6231, "Qhull internal error (userprintf_rbox.c): fp is 0. Wrong qh_fprintf_rbox called.\n"); + qh_errexit_rbox(6231); + } + 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 */ + |