geometry/0000755000176200001440000000000014367311205012107 5ustar liggesusersgeometry/NAMESPACE0000644000176200001440000000213114366503453013332 0ustar liggesusers# Generated by roxygen2: do not edit by hand S3method(plot,convhulln) S3method(plot,delaunayn) S3method(plot,intersectn) S3method(to.mesh3d,convhulln) export("entry.value<-") export(Unique) export(bary2cart) export(cart2bary) export(cart2pol) export(cart2sph) export(convhulln) export(delaunayn) export(distmesh2d) export(distmeshnd) export(dot) export(entry.value) export(extprod3d) export(feasible.point) export(halfspacen) export(inhulln) export(intersectn) export(matmax) export(matmin) export(matorder) export(matsort) export(mesh.dcircle) export(mesh.diff) export(mesh.drectangle) export(mesh.dsphere) export(mesh.hunif) export(mesh.intersect) export(mesh.union) export(pol2cart) export(polyarea) export(rbox) export(sph2cart) export(surf.tri) export(tetramesh) export(to.mesh3d) export(trimesh) export(tsearch) export(tsearchn) importFrom(Rcpp,sourceCpp) importFrom(graphics,box) importFrom(graphics,plot) importFrom(graphics,plot.new) importFrom(graphics,plot.window) importFrom(graphics,segments) importFrom(magic,ashift) importFrom(magic,shift) importFrom(utils,packageDescription) useDynLib(geometry) geometry/demo/0000755000176200001440000000000013430240556013033 5ustar liggesusersgeometry/demo/intersectn.R0000644000176200001440000000135313430240556015336 0ustar liggesuserslibrary(geometry) ## 2D example ps1 <- rbind(c(0, sqrt(3)), c(3/2, -sqrt(3)/2), c(-3/2, -sqrt(3)/2)) ps2 <- ps1 ps2[,2] <- -ps2[,2] is <- intersectn(ps1, ps2) plot(is, asp=1) ## 3D example ps1a <- rbox(2, C=0.5) dt1a <- delaunayn(ps1a) ps1b <- rbox(2, C=0.5) + 2 dt1b <- delaunayn(ps1b) ps1 <- rbind(ps1a, ps1b) dt1 <- rbind(dt1a, dt1b + nrow(ps1a)) tetramesh(dt1, ps1, alpha=0.5, col="yellow") ps2 <- rbox(2, C=0.5) + 0.5 dt2 <- delaunayn(ps2) tetramesh(dt2, ps2, alpha=0.5, col="red", clear=FALSE) vol <- 0 for (i in 1:nrow(dt1)) { for (j in 1:nrow(dt2)) { is <- intersectn(ps1[dt1[i,],], ps2[dt2[j,],]) vol <- vol + is$ch$vol } } message(paste("Volume of overlap should be 0.125. It is:", vol)) geometry/demo/00Index0000644000176200001440000000007713300625475014174 0ustar liggesusersintersectn Demonstration of intersection of 2D convex hulls geometry/man/0000755000176200001440000000000014366321774012675 5ustar liggesusersgeometry/man/sph2cart.Rd0000644000176200001440000000166513433536400014706 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/sph2cart.R \name{sph2cart} \alias{sph2cart} \title{Transform spherical coordinates to Cartesian coordinates} \usage{ sph2cart(theta, phi = NULL, r = NULL) } \arguments{ \item{theta}{describes the angle relative to the positive x-axis.} \item{phi}{is the angle relative to the xy-plane.} \item{r}{is the distance to the origin \code{(0, 0, 0)}. If only a single return argument is requested then return a matrix \code{C} where each row represents one Cartesian coordinate (\code{x}, \code{y}, \code{z}).} } \description{ The inputs \code{theta}, \code{phi}, and \code{r} must be the same shape, or scalar. If called with a single matrix argument then each row of \code{S} represents the spherical coordinate (\code{theta}, \code{phi}, \code{r}). } \seealso{ \code{\link{cart2sph}}, \code{\link{pol2cart}}, \code{\link{cart2pol}} } \author{ Kai Habel David Sterratt } geometry/man/matmax.Rd0000644000176200001440000000217713162524123014444 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/matmax.R \name{matmax} \alias{matmax} \alias{matmin} \alias{matsort} \alias{matorder} \title{Row-wise matrix functions} \usage{ matmax(...) } \arguments{ \item{\dots}{A numeric matrix or a set of numeric vectors (that are column-wise bind together into a matrix with \code{cbind}).} } \value{ \code{matmin} and \code{matmax} return a vector of length \code{nrow(cbind(...))}. \code{matsort} returns a matrix of dimension \code{dim(cbind(...))} with in each row of \code{cbind(...)} sorted. \code{matsort(x)} is a lot faster than, e.g., \code{t(apply(x,1,sort))}, if \code{x} is tall (i.e., \code{nrow(x)}>>\code{ncol(x)} and \code{ncol(x)}<30. If \code{ncol(x)}>30 then \code{matsort} simply calls `\code{t(apply(x,1,sort))}'. \code{matorder} returns a permutation which rearranges its first argument into ascending order, breaking ties by further arguments. } \description{ Compute maximum or minimum of each row, or sort each row of a matrix, or a set of (equal length) vectors. } \examples{ example(Unique) } \author{ Raoul Grasman } \keyword{arith} \keyword{array} geometry/man/entry.value.Rd0000644000176200001440000000220013100737575015425 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/entry.value.R \name{entry.value} \alias{entry.value} \alias{entry.value<-} \title{Retrieve or set a list of array element values} \usage{ entry.value(a, idx) } \arguments{ \item{a}{An array.} \item{idx}{Numerical matrix with the same number of columns as the number of dimensions of \code{a}. Each row indices a cell in \code{a} of which the value is to be retrieved or set.} \item{value}{An array of length \code{nrow(idx)}.} } \value{ \code{entry.value(a,idx)} returns a vector of values at the indicated cells. \code{entry.value(a,idx) <- val} changes the indicated cells of \code{a} to \code{val}. } \description{ \code{entry.value} retrieves or sets the values in an array \code{a} at the positions indicated by the rows of a matrix \code{idx}. } \examples{ a = array(1:(4^4),c(4,4,4,4)) entry.value(a,cbind(1:4,1:4,1:4,1:4)) entry.value(a,cbind(1:4,1:4,1:4,1:4)) <- 0 entry.value(a, as.matrix(expand.grid(1:4,1:4,1:4,1:4))) # same as `c(a[1:4,1:4,1:4,1:4])' which is same as `c(a)' } \author{ Raoul Grasman } \keyword{arith} \keyword{array} \keyword{math} geometry/man/mesh.diff.Rd0000644000176200001440000000264313162524123015016 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/mesh.diff.R \name{mesh.diff} \alias{mesh.diff} \alias{mesh.union} \alias{mesh.intersect} \title{Difference, union and intersection operation on two regions} \usage{ mesh.diff(p, regionA, regionB, ...) } \arguments{ \item{p}{A matrix with 2 columns (3 in \code{mesh.dsphere}), each row representing a point in the plane.} \item{regionA}{vectorized function describing region A in the union / intersection / difference} \item{regionB}{vectorized function describing region B in the union / intersection / difference} \item{...}{additional arguments passed to \code{regionA} and \code{regionB}} } \value{ A vector of length \code{nrow(p)} containing the signed distances to the boundary of the region. } \description{ Compute the signed distances from points \code{p} to a region defined by the difference, union or intersection of regions specified by the functions \code{regionA} and \code{regionB}. \code{regionA} and \code{regionB} must accept a matrix \code{p} with 2 columns as their first argument, and must return a vector of length \code{nrow(p)} containing the signed distances of the supplied points in \code{p} to their respective regions. } \seealso{ \code{\link{distmesh2d}}, \code{\link{mesh.dcircle}}, \code{\link{mesh.drectangle}} \code{\link{mesh.dsphere}} } \author{ Raoul Grasman; translated from original Matlab sources of Per-Olof Persson. } geometry/man/bary2cart.Rd0000644000176200001440000000215013433536400015037 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tsearch.R \name{bary2cart} \alias{bary2cart} \title{Conversion of Barycentric to Cartesian coordinates} \usage{ bary2cart(X, Beta) } \arguments{ \item{X}{Reference simplex in \eqn{N} dimensions represented by a \eqn{N+1}-by-\eqn{N} matrix} \item{Beta}{\eqn{M} points in barycentric coordinates with respect to the simplex \code{X} represented by a \eqn{M}-by-\eqn{N+1} matrix} } \value{ \eqn{M}-by-\eqn{N} matrix in which each row is the Cartesian coordinates of corresponding row of \code{Beta} } \description{ Given the barycentric coordinates of one or more points with respect to a simplex, compute the Cartesian coordinates of these points. } \examples{ ## Define simplex in 2D (i.e. a triangle) X <- rbind(c(0, 0), c(0, 1), c(1, 0)) ## Cartesian cooridinates of points beta <- rbind(c(0, 0.5, 0.5), c(0.1, 0.8, 0.1)) ## Plot triangle and points trimesh(rbind(1:3), X) text(X[,1], X[,2], 1:3) # Label vertices P <- bary2cart(X, beta) points(P) } \seealso{ \code{\link{cart2bary}} } \author{ David Sterratt } geometry/man/mesh.dcircle.Rd0000644000176200001440000000213613100737575015521 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/mesh.dcircle.R \name{mesh.dcircle} \alias{mesh.dcircle} \title{Circle distance function} \usage{ mesh.dcircle(p, radius = 1, ...) } \arguments{ \item{p}{A matrix with 2 columns (3 in \code{mesh.dsphere}), each row representing a point in the plane.} \item{radius}{radius of circle} \item{...}{additional arguments (not used)} } \value{ A vector of length \code{nrow(p)} containing the signed distances to the circle } \description{ Signed distance from points \code{p} to boundary of circle to allow easy definition of regions in \code{\link{distmesh2d}}. } \examples{ example(distmesh2d) } \references{ \url{http://persson.berkeley.edu/distmesh/} \cite{P.-O. Persson, G. Strang, A Simple Mesh Generator in MATLAB. SIAM Review, Volume 46 (2), pp. 329-345, June 2004} } \seealso{ \code{\link{distmesh2d}}, \code{\link{mesh.drectangle}}, \code{\link{mesh.diff}}, \code{\link{mesh.intersect}}, \code{\link{mesh.union}} } \author{ Raoul Grasman; translated from original Matlab sources of Per-Olof Persson. } \keyword{arith} \keyword{math} geometry/man/mesh.drectangle.Rd0000644000176200001440000000236313100737575016226 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/mesh.drectangle.R \name{mesh.drectangle} \alias{mesh.drectangle} \title{Rectangle distance function} \usage{ mesh.drectangle(p, x1 = -1/2, y1 = -1/2, x2 = 1/2, y2 = 1/2, ...) } \arguments{ \item{p}{A matrix with 2 columns, each row representing a point in the plane.} \item{x1}{lower left corner of rectangle} \item{y1}{lower left corner of rectangle} \item{x2}{upper right corner of rectangle} \item{y2}{upper right corner of rectangle} \item{\dots}{additional arguments (not used)} } \value{ a vector of length \code{nrow(p)} containing the signed distances } \description{ Signed distance from points \code{p} to boundary of rectangle to allow easy definition of regions in \code{\link{distmesh2d}}. } \examples{ example(distmesh2d) } \references{ \url{http://persson.berkeley.edu/distmesh/} \cite{P.-O. Persson, G. Strang, A Simple Mesh Generator in MATLAB. SIAM Review, Volume 46 (2), pp. 329-345, June 2004} } \seealso{ \code{\link{distmesh2d}}, \code{\link{mesh.drectangle}}, \code{\link{mesh.diff}}, \code{\link{mesh.intersect}}, \code{\link{mesh.union}} } \author{ Raoul Grasman; translated from original Matlab sources of Per-Olof Persson. } \keyword{arith} \keyword{math} geometry/man/to.mesh3d.Rd0000644000176200001440000000112313431000557014746 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/convhulln.R \name{to.mesh3d} \alias{to.mesh3d} \title{Convert convhulln object to RGL mesh} \usage{ to.mesh3d(x, ...) } \arguments{ \item{x}{\code{\link{convhulln}} object} \item{...}{Arguments to \code{\link[rgl]{qmesh3d}} or \code{\link[rgl]{tmesh3d}}} } \value{ \code{\link[rgl]{mesh3d}} object, which can be displayed in RGL with \code{\link[rgl]{dot3d}}, \code{\link[rgl]{wire3d}} or \code{\link[rgl]{shade3d}} } \description{ Convert convhulln object to RGL mesh } \seealso{ \code{\link[rgl]{as.mesh3d}} } geometry/man/cart2sph.Rd0000644000176200001440000000156713717744722014724 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/cart2sph.R \name{cart2sph} \alias{cart2sph} \title{Transform Cartesian to spherical coordinates} \usage{ cart2sph(x, y = NULL, z = NULL) } \arguments{ \item{x}{x-coordinates or matrix with three columns} \item{y}{y-coordinates (optional, if \code{x}) is a matrix} \item{z}{z-coordinates (optional, if \code{x}) is a matrix} } \value{ Matrix with columns: \item{\code{theta}}{the angle relative to the positive x-axis} \item{\code{phi}}{the angle relative to the xy-plane} \item{\code{r}}{the distance to the origin \code{(0, 0, 0)}} } \description{ If called with a single matrix argument then each row of \code{c} represents the Cartesian coordinate (\code{x}, \code{y}, \code{z}). } \seealso{ \code{\link{sph2cart}}, \code{\link{cart2pol}}, \code{\link{pol2cart}} } \author{ Kai Habel David Sterratt } geometry/man/cart2bary.Rd0000644000176200001440000000431614227220463015046 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tsearch.R \name{cart2bary} \alias{cart2bary} \title{Conversion of Cartesian to Barycentric coordinates.} \usage{ cart2bary(X, P) } \arguments{ \item{X}{Reference simplex in \eqn{N} dimensions represented by a \eqn{N+1}-by-\eqn{N} matrix} \item{P}{\eqn{M}-by-\eqn{N} matrix in which each row is the Cartesian coordinates of a point.} } \value{ \eqn{M}-by-\eqn{N+1} matrix in which each row is the barycentric coordinates of corresponding row of \code{P}. If the simplex is degenerate a warning is issued and the function returns \code{NULL}. } \description{ Given the Cartesian coordinates of one or more points, compute the barycentric coordinates of these points with respect to a simplex. } \details{ Given a reference simplex in \eqn{N} dimensions represented by a \eqn{N+1}-by-\eqn{N} matrix an arbitrary point \eqn{P} in Cartesian coordinates, represented by a 1-by-\eqn{N} row vector, can be written as \deqn{P = \beta X} where \eqn{\beta} is an \eqn{N+1} vector of the barycentric coordinates. A criterion on \eqn{\beta} is that \deqn{\sum_i\beta_i = 1} Now partition the simplex into its first \eqn{N} rows \eqn{X_N} and its \eqn{N+1}th row \eqn{X_{N+1}}. Partition the barycentric coordinates into the first \eqn{N} columns \eqn{\beta_N} and the \eqn{N+1}th column \eqn{\beta_{N+1}}. This allows us to write \deqn{P_{N+1} - X_{N+1} = \beta_N X_N + \beta_{N+1} X_{N+1} - X_{N+1}} which can be written \deqn{P_{N+1} - X_{N+1} = \beta_N(X_N - 1_N X_{N+1})} where \eqn{1_N} is an \eqn{N}-by-1 matrix of ones. We can then solve for \eqn{\beta_N}: \deqn{\beta_N = (P_{N+1} - X_{N+1})(X_N - 1_N X_{N+1})^{-1}} and compute \deqn{\beta_{N+1} = 1 - \sum_{i=1}^N\beta_i} This can be generalised for multiple values of \eqn{P}, one per row. } \note{ Based on the Octave function by David Bateman. } \examples{ ## Define simplex in 2D (i.e. a triangle) X <- rbind(c(0, 0), c(0, 1), c(1, 0)) ## Cartesian coordinates of points P <- rbind(c(0.5, 0.5), c(0.1, 0.8)) ## Plot triangle and points trimesh(rbind(1:3), X) text(X[,1], X[,2], 1:3) # Label vertices points(P) cart2bary(X, P) } \seealso{ \code{\link{bary2cart}} } \author{ David Sterratt } geometry/man/distmeshnd.Rd0000644000176200001440000001033014366274320015315 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/distmeshnd.R \name{distmeshnd} \alias{distmeshnd} \title{A simple mesh generator for non-convex regions in n-D space} \usage{ distmeshnd( fdist, fh, h, box, pfix = array(dim = c(0, ncol(box))), ..., ptol = 0.001, ttol = 0.1, deltat = 0.1, geps = 0.1 * h, deps = sqrt(.Machine$double.eps) * h ) } \arguments{ \item{fdist}{Vectorized signed distance function, for example \code{\link{mesh.dsphere}}, accepting an \code{m}-by-\code{n} matrix, where \code{m} is arbitrary, as the first argument.} \item{fh}{Vectorized function, for example \code{\link{mesh.hunif}}, that returns desired edge length as a function of position. Accepts an \code{m}-by-\code{n} matrix, where \code{n} is arbitrary, as its first argument.} \item{h}{Initial distance between mesh nodes.} \item{box}{\code{2}-by-\code{n} matrix that specifies the bounding box. (See \link{distmesh2d} for an example.)} \item{pfix}{\code{nfix}-by-2 matrix with fixed node positions.} \item{\dots}{parameters that are passed to \code{fdist} and \code{fh}} \item{ptol}{Algorithm stops when all node movements are smaller than \code{dptol}} \item{ttol}{Controls how far the points can move (relatively) before a retriangulation with \code{\link{delaunayn}}.} \item{deltat}{Size of the time step in Euler's method.} \item{geps}{Tolerance in the geometry evaluations.} \item{deps}{Stepsize \eqn{\Delta x} in numerical derivative computation for distance function.} } \value{ \code{m}-by-\code{n} matrix with node positions. } \description{ An unstructured simplex requires a choice of mesh points (vertex nodes) and a triangulation. This is a simple and short algorithm that improves the quality of a mesh by relocating the mesh points according to a relaxation scheme of forces in a truss structure. The topology of the truss is reset using Delaunay triangulation. A (sufficiently smooth) user supplied signed distance function (\code{fd}) indicates if a given node is inside or outside the region. Points outside the region are projected back to the boundary. } \details{ This is an implementation of original Matlab software of Per-Olof Persson. Excerpt (modified) from the reference below: \sQuote{The algorithm is based on a mechanical analogy between a triangular mesh and a n-D truss structure. In the physical model, the edges of the Delaunay triangles of a set of points correspond to bars of a truss. Each bar has a force-displacement relationship \eqn{f(\ell, \ell_{0})}{F(L,L0)} depending on its current length \eqn{\ell}{L} and its unextended length \eqn{\ell_{0}}{L0}.} \sQuote{External forces on the structure come at the boundaries, on which external forces have normal orientations. These external forces are just large enough to prevent nodes from moving outside the boundary. The position of the nodes are the unknowns, and are found by solving for a static force equilibrium. The hope is that (when \code{fh = function(p) return(rep(1,nrow(p)))}), the lengths of all the bars at equilibrium will be nearly equal, giving a well-shaped triangular mesh.} See the references below for all details. Also, see the comments in the source file of \code{distmesh2d}. } \section{Wishlist }{ \itemize{ \item Implement in C/Fortran \item Translate other functions of the Matlab package } } \examples{ \dontrun{ # examples distmeshnd require(rgl) fd = function(p, ...) sqrt((p^2)\%*\%c(1,1,1)) - 1 # also predefined as `mesh.dsphere' fh = function(p,...) rep(1,nrow(p)) # also predefined as `mesh.hunif' bbox = matrix(c(-1,1),2,3) p = distmeshnd(fd,fh,0.2,bbox, maxiter=100) # this may take a while: # press Esc to get result of current iteration } } \references{ \url{http://persson.berkeley.edu/distmesh/} \cite{P.-O. Persson, G. Strang, A Simple Mesh Generator in MATLAB. SIAM Review, Volume 46 (2), pp. 329-345, June 2004} } \seealso{ \code{\link{distmesh2d}}, \code{\link[interp]{tri.mesh}}, \code{\link{delaunayn}}, \code{\link{mesh.dsphere}}, \code{\link{mesh.hunif}},\cr \code{\link{mesh.diff}}, \code{\link{mesh.union}}, \code{\link{mesh.intersect}} } \author{ Raoul Grasman; translated from original Matlab sources of Per-Olof Persson. } \keyword{dplot} \keyword{graphs} \keyword{math} \keyword{optimize} geometry/man/cart2pol.Rd0000644000176200001440000000163113433536400014677 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/cart2pol.R \name{cart2pol} \alias{cart2pol} \title{Transform Cartesian coordinates to polar or cylindrical coordinates.} \usage{ cart2pol(x, y = NULL, z = NULL) } \arguments{ \item{x}{x-coordinates or matrix with three columns} \item{y}{y-coordinates (optional, if \code{x}) is a matrix} \item{z}{z-coordinates (optional, if \code{x}) is a matrix} } \value{ A matrix \code{P} where each row represents one polar/(cylindrical) coordinate (\code{theta}, \code{r}, (, \code{z})). } \description{ The inputs \code{x}, \code{y} (, and \code{z}) must be the same shape, or scalar. If called with a single matrix argument then each row of \code{C} represents the Cartesian coordinate (\code{x}, \code{y} (, \code{z})). } \seealso{ \code{\link{pol2cart}}, \code{\link{cart2sph}}, \code{\link{sph2cart}} } \author{ Kai Habel David Sterratt } geometry/man/tsearchn.Rd0000644000176200001440000000361213433536400014761 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tsearch.R \name{tsearchn} \alias{tsearchn} \title{Search for the enclosing Delaunay convex hull} \usage{ tsearchn(x, t, xi, ...) } \arguments{ \item{x}{An \eqn{N}-column matrix, in which each row represents a point in \eqn{N}-dimensional space.} \item{t}{A matrix with \eqn{N+1} columns. A row of \code{t} contains indices into \code{x} of the vertices of an \eqn{N}-dimensional simplex. \code{t} is usually the output of delaunayn.} \item{xi}{An \eqn{M}-by-\eqn{N} matrix. The rows of \code{xi} represent \eqn{M} points in \eqn{N}-dimensional space whose positions in the mesh are being sought.} \item{...}{Additional arguments} } \value{ A list containing: \describe{ \item{\code{idx}}{An \eqn{M}-long vector containing the indices of the row of \code{t} in which each point in \code{xi} is found.} \item{\code{p}}{An \eqn{M}-by-\eqn{N+1} matrix containing the barycentric coordinates with respect to the enclosing simplex of each point in \code{xi}.}} } \description{ For \code{t = delaunayn(x)}, where \code{x} is a set of points in \eqn{N} dimensions, \code{tsearchn(x, t, xi)} finds the index in \code{t} containing the points \code{xi}. For points outside the convex hull, \code{idx} is \code{NA}. \code{tsearchn} also returns the barycentric coordinates \code{p} of the enclosing triangles. } \details{ If \code{x} is \code{NA} and the \code{t} is a \code{delaunayn} object produced by \code{\link{delaunayn}} with the \code{full} option, then use the Qhull library to perform the search. Please note that this is experimental in geometry version 0.4.0 and is only partly tested for 3D hulls, and does not yet work for hulls of 4 dimensions and above. } \note{ Based on the Octave function Copyright (C) 2007-2012 David Bateman. } \seealso{ \code{\link{tsearch}}, \code{\link{delaunayn}} } \author{ David Sterratt } geometry/man/surf.tri.Rd0000644000176200001440000000461614366321774014747 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/surf.tri.R \name{surf.tri} \alias{surf.tri} \title{Find surface triangles from tetrahedral mesh} \usage{ surf.tri(p, t) } \arguments{ \item{p}{An \code{n}-by-\code{3} matrix. The rows of \code{p} represent \code{n} points in \code{dim}-dimensional space.} \item{t}{Matrix with 4 columns, interpreted as output of \code{\link{delaunayn}}.} } \value{ An \code{m}-by-\code{3} index matrix of which each row defines a triangle. The indices refer to the rows in \code{p}. } \description{ Find surface triangles from tetrahedral mesh typically obtained with \code{\link{delaunayn}}. } \details{ \code{surf.tri} and \code{\link{convhulln}} serve a similar purpose in 3D, but \code{surf.tri} also works for non-convex meshes obtained e.g. with \code{\link{distmeshnd}}. It also does not produce currently unavoidable diagnostic output on the console as \code{convhulln} does at the Rterm console--i.e., \code{surf.tri} is silent. } \note{ \code{surf.tri} was based on Matlab code for mesh of Per-Olof Persson (\url{http://persson.berkeley.edu/distmesh/}). } \examples{ \dontrun{ # more extensive example of surf.tri # url's of publically available data: data1.url = "http://neuroimage.usc.edu/USCPhantom/mesh_data.bin" data2.url = "http://neuroimage.usc.edu/USCPhantom/CT_PCS_trans.bin" meshdata = R.matlab::readMat(url(data1.url)) elec = R.matlab::readMat(url(data2.url))$eeg.ct2pcs/1000 brain = meshdata$mesh.brain[,c(1,3,2)] scalp = meshdata$mesh.scalp[,c(1,3,2)] skull = meshdata$mesh.skull[,c(1,3,2)] tbr = t(surf.tri(brain, delaunayn(brain))) tsk = t(surf.tri(skull, delaunayn(skull))) tsc = t(surf.tri(scalp, delaunayn(scalp))) rgl::triangles3d(brain[tbr,1], brain[tbr,2], brain[tbr,3],col="gray") rgl::triangles3d(skull[tsk,1], skull[tsk,2], skull[tsk,3],col="white", alpha=0.3) rgl::triangles3d(scalp[tsc,1], scalp[tsc,2], scalp[tsc,3],col="#a53900", alpha=0.6) rgl::view3d(-40,30,.4,zoom=.03) lx = c(-.025,.025); ly = -c(.02,.02); rgl::spheres3d(elec[,1],elec[,3],elec[,2],radius=.0025,col='gray') rgl::spheres3d( lx, ly,.11,radius=.015,col="white") rgl::spheres3d( lx, ly,.116,radius=.015*.7,col="brown") rgl::spheres3d( lx, ly,.124,radius=.015*.25,col="black") } } \seealso{ \code{\link[interp]{tri.mesh}}, \code{\link{convhulln}}, \code{\link{surf.tri}}, \code{\link{distmesh2d}} } \author{ Raoul Grasman } \keyword{dplot} \keyword{math} \keyword{optimize} geometry/man/tetramesh.Rd0000644000176200001440000000267014366321774015165 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tetramesh.R \name{tetramesh} \alias{tetramesh} \title{Render tetrahedron mesh (3D)} \usage{ tetramesh(T, X, col = grDevices::heat.colors(nrow(T)), clear = TRUE, ...) } \arguments{ \item{T}{T is a \code{m}-by-3 matrix in trimesh and \code{m}-by-4 in tetramesh. A row of \code{T} contains indices into \code{X} of the vertices of a triangle/tetrahedron. \code{T} is usually the output of delaunayn.} \item{X}{X is an n-by-2/n-by-3 matrix. The rows of X represent \code{n} points in 2D/3D space.} \item{col}{The tetrahedron colour. See rgl documentation for details.} \item{clear}{Should the current rendering device be cleared?} \item{\dots}{Parameters to the rendering device. See the \link[rgl]{rgl} package.} } \description{ \code{tetramesh(T, X, col)} uses the \link[rgl]{rgl} package to display the tetrahedrons defined in the m-by-4 matrix T as mesh. Each row of \code{T} specifies a tetrahedron by giving the 4 indices of its points in \code{X}. } \examples{ \dontrun{ # example delaunayn d = c(-1,1) pc = as.matrix(rbind(expand.grid(d,d,d),0)) tc = delaunayn(pc) # example tetramesh clr = rep(1,3) \%o\% (1:nrow(tc)+1) rgl::view3d(60,fov=20) rgl::light3d(270,60) tetramesh(tc,pc,alpha=0.7,col=clr) } } \seealso{ \code{\link{trimesh}}, \code{\link[rgl]{rgl}}, \code{\link{delaunayn}}, \code{\link{convhulln}}, \code{\link{surf.tri}} } \author{ Raoul Grasman } \keyword{hplot} geometry/man/inhulln.Rd0000644000176200001440000000255314112721316014623 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/inhulln.R \name{inhulln} \alias{inhulln} \title{Test if points lie in convex hull} \usage{ inhulln(ch, p) } \arguments{ \item{ch}{Convex hull produced using \code{\link{convhulln}}} \item{p}{An \eqn{M}-by-\eqn{N} matrix of points to test. The rows of \code{p} represent \eqn{M} points in \eqn{N}-dimensional space.} } \value{ A boolean vector with \eqn{M} elements } \description{ Tests if a set of points lies within a convex hull, returning a boolean vector in which each element is \code{TRUE} if the corresponding point lies within the hull and \code{FALSE} if it lies outwith the hull or on one of its facets. } \note{ \code{inhulln} was introduced in geometry 0.4.0, and is still under development. It is worth checking results for unexpected behaviour. } \examples{ p <- cbind(c(-1, -1, 1), c(-1, 1, -1)) ch <- convhulln(p) ## First point should be in the hull; last two outside inhulln(ch, rbind(c(-0.5, -0.5), c( 1 , 1), c(10 , 0))) ## Test hypercube p <- rbox(D=4, B=1) ch <- convhulln(p) tp <- cbind(seq(-1.9, 1.9, by=0.2), 0, 0, 0) pin <- inhulln(ch, tp) ## Points on x-axis should be in box only betw,een -1 and 1 pin == (tp[,1] < 1 & tp[,1] > -1) } \seealso{ \code{\link{convhulln}}, \code{point.in.polygon} in \pkg{sp} } \author{ David Sterratt } geometry/man/mesh.hunif.Rd0000644000176200001440000000136013100737575015223 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/mesh.hunif.R \name{mesh.hunif} \alias{mesh.hunif} \title{Uniform desired edge length} \usage{ mesh.hunif(p, ...) } \arguments{ \item{p}{A \code{n}-by-\code{m} matrix, each row representing a point in an \code{m}-dimensional space.} \item{...}{additional arguments (not used)} } \value{ Vector of ones of length \code{n}. } \description{ Uniform desired edge length function of position to allow easy definition of regions when passed as the \code{fh} argument of \code{\link{distmesh2d}} or \code{\link{distmeshnd}}. } \seealso{ \code{\link{distmesh2d}} and \code{\link{distmeshnd}}. } \author{ Raoul Grasman; translated from original Matlab sources of Per-Olof Persson. } geometry/man/feasible.point.Rd0000644000176200001440000000124713431000557016053 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/intersectn.R \name{feasible.point} \alias{feasible.point} \title{Find point in intersection of convex hulls} \usage{ feasible.point(ch1, ch2, tol = 0) } \arguments{ \item{ch1}{First convex hull with normals} \item{ch2}{Second convex hull with normals} \item{tol}{The point must be at least this far within the facets of both convex hulls} } \description{ Find point that lies somewhere in interesction of two convex hulls. If such a point does not exist, return \code{NA}. The feasible point is found using a linear program similar to the one suggested at \url{../doc/qhull/html/qhalf.html#notes} } geometry/man/tsearch.Rd0000644000176200001440000000350113433536400014600 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tsearch.R \name{tsearch} \alias{tsearch} \title{Search for the enclosing Delaunay convex hull} \usage{ tsearch(x, y, t, xi, yi, bary = FALSE, method = "quadtree") } \arguments{ \item{x}{X-coordinates of triangulation points} \item{y}{Y-coordinates of triangulation points} \item{t}{Triangulation, e.g. produced by \code{t <- delaunayn(cbind(x, y))}} \item{xi}{X-coordinates of points to test} \item{yi}{Y-coordinates of points to test} \item{bary}{If \code{TRUE} return barycentric coordinates as well as index of triangle.} \item{method}{One of \code{"quadtree"} or \code{"orig"}. The Quadtree algorithm is much faster and new from version 0.4.0. The \code{orig} option uses the tsearch algorithm adapted from Octave code. Its use is deprecated and it may be removed from a future version of the package.} } \value{ If \code{bary} is \code{FALSE}, the index in \code{t} containing the points \code{(xi, yi)}. For points outside the convex hull the index is \code{NA}. If \code{bary} is \code{TRUE}, a list containing: \describe{ \item{list("idx")}{the index in \code{t} containing the points \code{(xi, yi)}} \item{list("p")}{a 3-column matrix containing the barycentric coordinates with respect to the enclosing triangle of each point \code{(xi, yi)}.} } } \description{ For \code{t <- delaunay(cbind(x, y))}, where \code{(x, y)} is a 2D set of points, \code{tsearch(x, y, t, xi, yi)} finds the index in \code{t} containing the points \code{(xi, yi)}. For points outside the convex hull the index is \code{NA}. } \note{ The original Octave function is Copyright (C) 2007-2012 David Bateman } \seealso{ \code{\link{tsearchn}}, \code{\link{delaunayn}} } \author{ Jean-Romain Roussel (Quadtree algorithm), David Sterratt (Octave-based implementation) } geometry/man/Unique.Rd0000644000176200001440000000213513100737575014426 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/Unique.R \name{Unique} \alias{Unique} \title{Extract Unique Rows} \usage{ Unique(X, rows.are.sets = FALSE) } \arguments{ \item{X}{Numerical matrix.} \item{rows.are.sets}{If \sQuote{\code{TRUE}}, rows are treated as sets - i.e., to define uniqueness, the order of the rows does not matter.} } \value{ Matrix of the same number of columns as \code{x}, with the unique rows in \code{x} sorted according to the columns of \code{x}. If \code{rows.are.sets = TRUE} the rows are also sorted. } \description{ \sQuote{Unique} returns a vector, data frame or array like 'x' but with duplicate elements removed. } \note{ \sQuote{\code{Unique}} is (under circumstances) much quicker than the more generic base function \sQuote{\code{unique}}. } \examples{ # `Unique' is faster than `unique' x = matrix(sample(1:(4*8),4*8),ncol=4) y = x[sample(1:nrow(x),3000,TRUE), ] gc(); system.time(unique(y)) gc(); system.time(Unique(y)) # z = Unique(y) x[matorder(x),] z[matorder(z),] } \author{ Raoul Grasman } \keyword{arith} \keyword{array} \keyword{math} geometry/man/trimesh.Rd0000644000176200001440000000234013100737575014631 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/trimesh.R \name{trimesh} \alias{trimesh} \title{Display triangles mesh (2D)} \usage{ trimesh(T, p, p2, add = FALSE, axis = FALSE, boxed = FALSE, ...) } \arguments{ \item{T}{T is a \code{m}-by-3 matrix. A row of \code{T} contains indices into \code{X} of the vertices of a triangle. \code{T} is usually the output of \code{\link{delaunayn}}.} \item{p}{A vector or a matrix.} \item{p2}{if \code{p} is not a matrix \code{p} and \code{p2} are bind to a matrix with \code{cbind}.} \item{add}{Add to existing plot in current active device?} \item{axis}{Draw axes?} \item{boxed}{Plot box?} \item{\dots}{Parameters to the rendering device. See the \link[rgl]{rgl} package.} } \description{ \code{trimesh(T, p)} displays the triangles defined in the m-by-3 matrix \code{T} and points \code{p} as a mesh. Each row of \code{T} specifies a triangle by giving the 3 indices of its points in \code{X}. } \examples{ #example trimesh p = cbind(x=rnorm(30), y=rnorm(30)) tt = delaunayn(p) trimesh(tt,p) } \seealso{ \code{\link{tetramesh}}, \code{\link[rgl]{rgl}}, \code{\link{delaunayn}}, \code{\link{convhulln}}, \code{\link{surf.tri}} } \author{ Raoul Grasman } \keyword{hplot} geometry/man/intersectn.Rd0000644000176200001440000000503114112717220015321 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/intersectn.R \name{intersectn} \alias{intersectn} \title{Compute convex hull of intersection of two sets of points} \usage{ intersectn( ps1, ps2, tol = 0, return.chs = TRUE, options = "Tv", fp = NULL, autoscale = FALSE ) } \arguments{ \item{ps1}{First set of points} \item{ps2}{Second set of points} \item{tol}{Tolerance used to determine if a feasible point lies within the convex hulls of both points and to round off the points generated by the halfspace intersection, which sometimes produces points very close together.} \item{return.chs}{If \code{TRUE} (default) return the convex hulls of the first and second sets of points, as well as the convex hull of the intersection.} \item{options}{Options passed to \code{\link{halfspacen}}. By default this is \code{Tv}.} \item{fp}{Coordinates of feasible point, i.e. a point known to lie in the hulls of \code{ps1} and \code{ps2}. The feasible point is required for \code{\link{halfspacen}} to find the intersection. \code{intersectn} tries to find the feasible point automatically using the linear program in \code{\link{feasible.point}}, but currently the linear program fails on some examples where there is an obvious solution. This option overrides the automatic search for a feasible point} \item{autoscale}{\emph{Experimental in v0.4.2} Automatically scale the points to lie in a sensible numeric range. May help to correct some numerical issues.} } \value{ List containing named elements: \code{ch1}, the convex hull of the first set of points, with volumes, areas and normals (see \code{\link{convhulln}}; \code{ch2}, the convex hull of the first set of points, with volumes, areas and normals; \code{ps}, the intersection points of convex hulls \code{ch1} and \code{ch2}; and \code{ch}, the convex hull of the intersection points, with volumes, areas and normals. } \description{ Compute convex hull of intersection of two sets of points } \note{ \code{intersectn} was introduced in geometry 0.4.0, and is still under development. It is worth checking results for unexpected behaviour. } \examples{ # Two overlapping boxes ps1 <- rbox(0, C=0.5) ps2 <- rbox(0, C=0.5) + 0.5 out <- intersectn(ps1, ps2) message("Volume of 1st convex hull: ", out$ch1$vol) message("Volume of 2nd convex hull: ", out$ch2$vol) message("Volume of intersection convex hull: ", out$ch$vol) } \seealso{ \code{\link{convhulln}}, \code{\link{halfspacen}}, \code{\link{inhulln}}, \code{\link{feasible.point}} } \author{ David Sterratt } geometry/man/distmesh2d.Rd0000644000176200001440000001142414366274320015226 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/distmesh2d.R \name{distmesh2d} \alias{distmesh2d} \title{A simple mesh generator for non-convex regions} \usage{ distmesh2d( fd, fh, h0, bbox, p = NULL, pfix = array(0, dim = c(0, 2)), ..., dptol = 0.001, ttol = 0.1, Fscale = 1.2, deltat = 0.2, geps = 0.001 * h0, deps = sqrt(.Machine$double.eps) * h0, maxiter = 1000, plot = TRUE ) } \arguments{ \item{fd}{Vectorized signed distance function, for example \code{\link{mesh.dcircle}} or \code{\link{mesh.diff}}, accepting an \code{n}-by-\code{2} matrix, where \code{n} is arbitrary, as the first argument.} \item{fh}{Vectorized function, for example \code{\link{mesh.hunif}}, that returns desired edge length as a function of position. Accepts an \code{n}-by-\code{2} matrix, where \code{n} is arbitrary, as its first argument.} \item{h0}{Initial distance between mesh nodes. (Ignored of \code{p} is supplied)} \item{bbox}{Bounding box \code{cbind(c(xmin,xmax), c(ymin,ymax))}} \item{p}{An \code{n}-by-\code{2} matrix. The rows of \code{p} represent locations of starting mesh nodes.} \item{pfix}{\code{nfix}-by-2 matrix with fixed node positions.} \item{\dots}{parameters to be passed to \code{fd} and/or \code{fh}} \item{dptol}{Algorithm stops when all node movements are smaller than \code{dptol}} \item{ttol}{Controls how far the points can move (relatively) before a retriangulation with \code{\link{delaunayn}}.} \item{Fscale}{\dQuote{Internal pressure} in the edges.} \item{deltat}{Size of the time step in Euler's method.} \item{geps}{Tolerance in the geometry evaluations.} \item{deps}{Stepsize \eqn{\Delta x} in numerical derivative computation for distance function.} \item{maxiter}{Maximum iterations.} \item{plot}{logical. If \code{TRUE} (default), the mesh is plotted as it is generated.} } \value{ \code{n}-by-\code{2} matrix with node positions. } \description{ An unstructured simplex requires a choice of mesh points (vertex nodes) and a triangulation. This is a simple and short algorithm that improves the quality of a mesh by relocating the mesh points according to a relaxation scheme of forces in a truss structure. The topology of the truss is reset using Delaunay triangulation. A (sufficiently smooth) user supplied signed distance function (\code{fd}) indicates if a given node is inside or outside the region. Points outside the region are projected back to the boundary. } \details{ This is an implementation of original Matlab software of Per-Olof Persson. Excerpt (modified) from the reference below: \sQuote{The algorithm is based on a mechanical analogy between a triangular mesh and a 2D truss structure. In the physical model, the edges of the Delaunay triangles of a set of points correspond to bars of a truss. Each bar has a force-displacement relationship \eqn{f(\ell, \ell_{0})}{F(L,L0)} depending on its current length \eqn{\ell}{L} and its unextended length \eqn{\ell_{0}}{L0}.} \sQuote{External forces on the structure come at the boundaries, on which external forces have normal orientations. These external forces are just large enough to prevent nodes from moving outside the boundary. The position of the nodes are the unknowns, and are found by solving for a static force equilibrium. The hope is that (when \code{fh = function(p) return(rep(1,nrow(p)))}), the lengths of all the bars at equilibrium will be nearly equal, giving a well-shaped triangular mesh.} See the references below for all details. Also, see the comments in the source file. } \section{Wishlist }{ \itemize{ \item Implement in C/Fortran \item Implement an \code{n}D version as provided in the Matlab package \item Translate other functions of the Matlab package } } \examples{ # examples distmesh2d fd <- function(p, ...) sqrt((p^2)\%*\%c(1,1)) - 1 # also predefined as `mesh.dcircle' fh <- function(p,...) rep(1,nrow(p)) bbox <- matrix(c(-1,1,-1,1),2,2) p <- distmesh2d(fd,fh,0.2,bbox, maxiter=100) # this may take a while: # press Esc to get result of current iteration # example with non-convex region fd <- function(p, ...) mesh.diff(p , mesh.drectangle, mesh.dcircle, radius=.3) # fd defines difference of square and circle p <- distmesh2d(fd,fh,0.05,bbox,radius=0.3,maxiter=4) p <- distmesh2d(fd,fh,0.05,bbox,radius=0.3, maxiter=10) # continue on previous mesh } \references{ \url{http://persson.berkeley.edu/distmesh/} \cite{P.-O. Persson, G. Strang, A Simple Mesh Generator in MATLAB. SIAM Review, Volume 46 (2), pp. 329-345, June 2004} } \seealso{ \code{\link[interp]{tri.mesh}}, \code{\link{delaunayn}}, \code{\link{mesh.dcircle}}, \code{\link{mesh.drectangle}}, \code{\link{mesh.diff}}, \code{\link{mesh.union}}, \code{\link{mesh.intersect}} } \author{ Raoul Grasman } \keyword{dplot} \keyword{graphs} \keyword{math} \keyword{optimize} geometry/man/dot.Rd0000644000176200001440000000122613100737575013746 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/dotprod.R \name{dot} \alias{dot} \title{Compute the dot product of two vectors} \usage{ dot(x, y, d = NULL) } \arguments{ \item{x}{Matrix of vectors} \item{y}{Matrix of vectors} \item{d}{Dimension along which to calculate the dot product} } \value{ Vector with length of \code{d}th dimension } \description{ If \code{x} and \code{y} are matrices, calculate the dot-product along the first non-singleton dimension. If the optional argument \code{d} is given, calculate the dot-product along this dimension. } \author{ David Sterratt } \keyword{arith} \keyword{array} \keyword{math} geometry/man/pol2cart.Rd0000644000176200001440000000161713433536400014703 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/pol2cart.R \name{pol2cart} \alias{pol2cart} \title{Transform polar or cylindrical coordinates to Cartesian coordinates.} \usage{ pol2cart(theta, r = NULL, z = NULL) } \arguments{ \item{theta}{describes the angle relative to the positive x-axis.} \item{r}{is the distance to the z-axis (0, 0, z).} \item{z}{(optional) is the z-coordinate} } \value{ a matrix \code{C} where each row represents one Cartesian coordinate (\code{x}, \code{y} (, \code{z})). } \description{ The inputs \code{theta}, \code{r}, (and \code{z}) must be the same shape, or scalar. If called with a single matrix argument then each row of \code{P} represents the polar/(cylindrical) coordinate (\code{theta}, \code{r} (, \code{z})). } \seealso{ \code{\link{cart2pol}}, \code{\link{sph2cart}}, \code{\link{cart2sph}} } \author{ Kai Habel David Sterratt } geometry/man/convhulln.Rd0000644000176200001440000001037114366321774015176 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/convhulln.R \name{convhulln} \alias{convhulln} \title{Compute smallest convex hull that encloses a set of points} \usage{ convhulln( p, options = "Tv", output.options = NULL, return.non.triangulated.facets = FALSE ) } \arguments{ \item{p}{An \eqn{M}-by-\eqn{N} matrix. The rows of \code{p} represent \eqn{M} points in \eqn{N}-dimensional space.} \item{options}{String containing extra options for the underlying Qhull command; see details below and Qhull documentation at \url{../doc/qhull/html/qconvex.html#synopsis}.} \item{output.options}{String containing Qhull options to generate extra output. Currently \code{n} (normals) and \code{FA} (generalised areas and volumes) are supported; see \sQuote{Value} for details. If \code{output.options} is \code{TRUE}, select all supported options.} \item{return.non.triangulated.facets}{logical defining whether the output facets should be triangulated; \code{FALSE} by default.} } \value{ By default (\code{return.non.triangulated.facets} is \code{FALSE}), return an \eqn{M}-by-\eqn{N} matrix in which each row contains the indices of the points in \code{p} forming an \eqn{N-1}-dimensional facet. e.g In 3 dimensions, there are 3 indices in each row describing the vertices of 2-dimensional triangles. If \code{return.non.triangulated.facets} is \code{TRUE} then the number of columns equals the maximum number of vertices in a facet, and each row defines a polygon corresponding to a facet of the convex hull with its vertices followed by \code{NA}s until the end of the row. If the \code{output.options} or \code{options} argument contains \code{FA} or \code{n}, return a list with class \code{convhulln} comprising the named elements: \describe{ \item{\code{p}}{The points passed to \code{convnhulln}} \item{\code{hull}}{The convex hull, represented as a matrix indexing \code{p}, as described above} \item{\code{area}}{If \code{FA} is specified, the generalised area of the hull. This is the surface area of a 3D hull or the length of the perimeter of a 2D hull. See \url{../doc/qhull/html/qh-optf.html#FA}.} \item{\code{vol}}{If \code{FA} is specified, the generalised volume of the hull. This is volume of a 3D hull or the area of a 2D hull. See \url{../doc/qhull/html/qh-optf.html#FA}. } \item{\code{normals}}{If \code{n} is specified, this is a matrix hyperplane normals with offsets. See \url{../doc/qhull/html/qh-opto.html#n}.} } } \description{ Returns information about the smallest convex complex of a set of input points in \eqn{N}-dimensional space (the convex hull of the points). By default, indices to points forming the facets of the hull are returned; optionally normals to the facets and the generalised surface area and volume can be returned. This function interfaces the \href{http://www.qhull.org}{Qhull} library. } \note{ This function was originally a port of the \href{https://octave.org/}{Octave} convhulln function written by Kai Habel. See further notes in \code{\link{delaunayn}}. } \examples{ ## Points in a sphere ps <- matrix(rnorm(3000), ncol=3) ps <- sqrt(3)*ps/drop(sqrt((ps^2) \%*\% rep(1, 3))) ts.surf <- t(convhulln(ps)) # see the qhull documentations for the options \dontrun{ rgl::triangles3d(ps[ts.surf,1],ps[ts.surf,2],ps[ts.surf,3],col="blue",alpha=.2) for(i in 1:(8*360)) rgl::view3d(i/8) } ## Square pq <- rbox(0, C=0.5, D=2) # Return indices only convhulln(pq) # Return convhulln object with normals, generalised area and volume ch <- convhulln(pq, output.options=TRUE) plot(ch) ## Cube pc <- rbox(0, C=0.5, D=3) # Return indices of triangles on surface convhulln(pc) # Return indices of squares on surface convhulln(pc, return.non.triangulated.facets=TRUE) } \references{ \cite{Barber, C.B., Dobkin, D.P., and Huhdanpaa, H.T., \dQuote{The Quickhull algorithm for convex hulls,} \emph{ACM Trans. on Mathematical Software,} Dec 1996.} \url{http://www.qhull.org} } \seealso{ \code{\link{intersectn}}, \code{\link{delaunayn}}, \code{\link{surf.tri}}, \code{\link[interp]{convex.hull}} } \author{ Raoul Grasman, Robert B. Gramacy, Pavlo Mozharovskyi and David Sterratt \email{david.c.sterratt@ed.ac.uk} } \keyword{dplot} \keyword{graphs} \keyword{math} geometry/man/polyarea.Rd0000644000176200001440000000215713162524123014767 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/polyarea.R \name{polyarea} \alias{polyarea} \title{Determines area of a polygon by triangle method.} \usage{ polyarea(x, y, d = 1) } \arguments{ \item{x}{X coordinates of vertices.} \item{y}{Y coordinates of vertices.} \item{d}{Dimension of array to work along.} } \value{ Area(s) of polygon(s). } \description{ Determines area of a polygon by triangle method. The variables \code{x} and \code{y} define the vertex pairs, and must therefore have the same shape. They can be either vectors or arrays. If they are arrays then the columns of \code{x} and \code{y} are treated separately and an area returned for each. } \details{ If the optional \code{dim} argument is given, then \code{polyarea} works along this dimension of the arrays \code{x} and \code{y}. } \examples{ x <- c(1, 1, 3, 3, 1) y <- c(1, 3, 3, 1, 1) polyarea(x, y) polyarea(cbind(x, x), cbind(y, y)) ## c(4, 4) polyarea(cbind(x, x), cbind(y, y), 1) ## c(4, 4) polyarea(rbind(x, x), rbind(y, y), 2) ## c(4, 4) } \author{ David Sterratt based on the octave sources by David M. Doolin } geometry/man/extprod3d.Rd0000644000176200001440000000220513433536400015063 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/extprod3d.R \name{extprod3d} \alias{extprod3d} \title{Compute external- or `cross'- product of 3D vectors.} \usage{ extprod3d(x, y, drop = TRUE) } \arguments{ \item{x}{\code{n}-by-3 matrix. Each row is one \bold{x}-vector} \item{y}{\code{n}-by-3 matrix. Each row is one \bold{y}-vector} \item{drop}{logical. If \code{TRUE} and if the inputs are one row matrices or vectors, then delete the dimensions of the array returned.} } \value{ If \code{n} is greater than 1 or \code{drop} is \code{FALSE}, \code{n}-by-3 matrix; if \code{n} is 1 and \code{drop} is \code{TRUE}, a vector of length 3. } \description{ Computes the external product \deqn{ }{ (x2 * y3 - x3 * y2, x3 * y1 - x1 * y3, x1 * y2 - x2 * y1) }\deqn{ \left(x_2 y_3 - x_3 y_2,\; x_3 y_1 - x_1 y_3,\; x_1 y_2 - x_2 y_1 \right) }{ (x2 * y3 - x3 * y2, x3 * y1 - x1 * y3, x1 * y2 - x2 * y1) }\deqn{ }{ (x2 * y3 - x3 * y2, x3 * y1 - x1 * y3, x1 * y2 - x2 * y1) } of the 3D vectors in \bold{x} and \bold{y}. } \seealso{ \code{\link[base]{drop}} } \author{ Raoul Grasman } \keyword{arith} \keyword{array} \keyword{math} geometry/man/halfspacen.Rd0000644000176200001440000000310113450441023015242 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/halfspacen.R \name{halfspacen} \alias{halfspacen} \title{Compute halfspace intersection about a point} \usage{ halfspacen(p, fp, options = "Tv") } \arguments{ \item{p}{An \eqn{M}-by-\eqn{N+1} matrix. Each row of \code{p} represents a halfspace by a \eqn{N}-dimensional normal to a hyperplane and the offset of the hyperplane.} \item{fp}{A \dQuote{feasible} point that is within the space contained within all the halfspaces.} \item{options}{String containing extra options, separated by spaces, for the underlying Qhull command; see Qhull documentation at \url{../doc/qhull/html/qhalf.html}.} } \value{ A \eqn{N}-column matrix containing the intersection points of the hyperplanes \url{../doc/qhull/html/qhalf.html}. } \description{ Compute halfspace intersection about a point } \note{ \code{halfspacen} was introduced in geometry 0.4.0, and is still under development. It is worth checking results for unexpected behaviour. } \examples{ p <- rbox(0, C=0.5) # Generate points on a unit cube centered around the origin ch <- convhulln(p, "n") # Generate convex hull, including normals to facets, with "n" option # Intersections of half planes # These points should be the same as the orginal points pn <- halfspacen(ch$normals, c(0, 0, 0)) } \references{ \cite{Barber, C.B., Dobkin, D.P., and Huhdanpaa, H.T., \dQuote{The Quickhull algorithm for convex hulls,} \emph{ACM Trans. on Mathematical Software,} Dec 1996.} \url{http://www.qhull.org} } \seealso{ \code{\link{convhulln}} } \author{ David Sterratt } geometry/man/mesh.dsphere.Rd0000644000176200001440000000175613100737575015555 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/mesh.dsphere.R \name{mesh.dsphere} \alias{mesh.dsphere} \title{Sphere distance function} \usage{ mesh.dsphere(p, radius = 1, ...) } \arguments{ \item{p}{A matrix with 2 columns (3 in \code{mesh.dsphere}), each row representing a point in the plane.} \item{radius}{radius of sphere} \item{...}{additional arguments (not used)} } \value{ A vector of length \code{nrow(p)} containing the signed distances to the sphere } \description{ Signed distance from points \code{p} to boundary of sphere to allow easy definition of regions in \code{\link{distmeshnd}}. } \examples{ example(distmeshnd) } \references{ \url{http://persson.berkeley.edu/distmesh/} \cite{P.-O. Persson, G. Strang, A Simple Mesh Generator in MATLAB. SIAM Review, Volume 46 (2), pp. 329-345, June 2004} } \seealso{ \code{\link{distmeshnd}} } \author{ Raoul Grasman; translated from original Matlab sources of Per-Olof Persson. } \keyword{arith} \keyword{math} geometry/man/delaunayn.Rd0000644000176200001440000001152114366511022015127 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/delaunayn.R \name{delaunayn} \alias{delaunayn} \title{Delaunay triangulation in N dimensions} \usage{ delaunayn(p, options = NULL, output.options = NULL, full = FALSE) } \arguments{ \item{p}{An \eqn{M}-by-\eqn{N} matrix whose rows represent \eqn{M} points in \eqn{N}-dimensional space.} \item{options}{String containing extra control options for the underlying Qhull command; see the Qhull documentation (\url{../doc/qhull/html/qdelaun.html}) for the available options. The \code{Qbb} option is always passed to Qhull. The remaining default options are \code{Qcc Qc Qt Qz} for \eqn{N<4} and \code{Qcc Qc Qt Qx} for \eqn{N>=4}. If neither of the \code{QJ} or \code{Qt} options are supplied, the \code{Qt} option is passed to Qhull. The \code{Qt} option ensures all Delaunay regions are simplical (e.g., triangles in 2D). See \url{../doc/qhull/html/qdelaun.html} for more details. Contrary to the Qhull documentation, no degenerate (zero area) regions are returned with the \code{Qt} option since the R function removes them from the triangulation. \emph{If \code{options} is specified, the default options are overridden.} It is recommended to use \code{output.options} for options controlling the outputs.} \item{output.options}{String containing Qhull options to control output. Currently \code{Fn} (neighbours) and \code{Fa} (areas) are supported. Causes an object of return value for details. If \code{output.options} is \code{TRUE}, select all supported options.} \item{full}{Deprecated and will be removed in a future release. Adds options \code{Fa} and \code{Fn}.} } \value{ If \code{output.options} is \code{NULL} (the default), return the Delaunay triangulation as a matrix with \eqn{M} rows and \eqn{N+1} columns in which each row contains a set of indices to the input points \code{p}. Thus each row describes a simplex of dimension \eqn{N}, e.g. a triangle in 2D or a tetrahedron in 3D. If the \code{output.options} argument is \code{TRUE} or is a string containing \code{Fn} or \code{Fa}, return a list with class \code{delaunayn} comprising the named elements: \describe{ \item{\code{tri}}{The Delaunay triangulation described above} \item{\code{areas}}{If \code{TRUE} or if \code{Fa} is specified, an \eqn{M}-dimensional vector containing the generalised area of each simplex (e.g. in 2D the areas of triangles; in 3D the volumes of tetrahedra). See \url{../doc/qhull/html/qh-optf.html#Fa}.} \item{\code{neighbours}}{If \code{TRUE} or if \code{Fn} is specified, a list of neighbours of each simplex. Note that a negative number corresponds to "facet" (="edge" in 2D or "face" in 3D) that has no neighbour, as will be the case for some simplices on the boundary of the triangulation. See \url{../doc/qhull/html/qh-optf.html#Fn}} } } \description{ The Delaunay triangulation is a tessellation of the convex hull of the points such that no \eqn{N}-sphere defined by the \eqn{N}- triangles contains any other points from the set. } \note{ This function interfaces the Qhull library and is a port from Octave (\url{https://octave.org/}) to R. Qhull computes convex hulls, Delaunay triangulations, halfspace intersections about a point, Voronoi diagrams, furthest-site Delaunay triangulations, and furthest-site Voronoi diagrams. It runs in 2D, 3D, 4D, and higher dimensions. It implements the Quickhull algorithm for computing the convex hull. Qhull handles round-off errors from floating point arithmetic. It computes volumes, surface areas, and approximations to the convex hull. See the Qhull documentation included in this distribution (the doc directory \url{../doc/qhull/index.html}). Qhull does not support constrained Delaunay triangulations, triangulation of non-convex surfaces, mesh generation of non-convex objects, or medium-sized inputs in 9D and higher. A rudimentary algorithm for mesh generation in non-convex regions using Delaunay triangulation is implemented in \link{distmesh2d} (currently only 2D). } \examples{ # example delaunayn d <- c(-1,1) pc <- as.matrix(rbind(expand.grid(d,d,d),0)) tc <- delaunayn(pc) # example tetramesh \dontrun{ rgl::view3d(60) rgl::light3d(120,60) tetramesh(tc,pc, alpha=0.9) } tc1 <- delaunayn(pc, output.options="Fa") ## sum of generalised areas is total volume of cube sum(tc1$areas) } \references{ \cite{Barber, C.B., Dobkin, D.P., and Huhdanpaa, H.T., \dQuote{The Quickhull algorithm for convex hulls,} \emph{ACM Trans. on Mathematical Software,} Dec 1996.} \url{http://www.qhull.org} } \seealso{ \code{\link[interp]{tri.mesh}}, \code{\link{convhulln}}, \code{\link{surf.tri}}, \code{\link{distmesh2d}} } \author{ Raoul Grasman and Robert B. Gramacy; based on the corresponding Octave sources of Kai Habel. } \keyword{dplot} \keyword{graphs} \keyword{math} geometry/man/rbox.Rd0000644000176200001440000000112513161753572014132 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/rbox.R \name{rbox} \alias{rbox} \title{Generate various point distributions} \usage{ rbox(n = 3000, D = 3, B = 0.5, C = NA) } \arguments{ \item{n}{number of random points in hypercube} \item{D}{number of dimensions of hypercube} \item{B}{bounding box coordinate - faces will be \code{-B} and \code{B} from origin} \item{C}{add a unit hypercube to the output - faces will be \code{-C} and \code{C} from origin} } \value{ Matrix of points } \description{ Default is corners of a hypercube. } \author{ David Sterratt } geometry/DESCRIPTION0000644000176200001440000000401114367311205013611 0ustar liggesusersPackage: geometry License: GPL (>= 3) Title: Mesh Generation and Surface Tessellation Authors@R: c( person("Jean-Romain", "Roussel" , role=c("cph", "ctb"), comment = "wrote tsearch function with QuadTrees"), person("C. B.", "Barber" , role="cph"), person("Kai", "Habel", role=c("cph","aut")), person("Raoul", "Grasman", role=c("cph","aut")), person("Robert B.", "Gramacy", role=c("cph","aut")), person("Pavlo", "Mozharovskyi", role=c("cph","aut")), person("David C.", "Sterratt", role=c("cph","aut","cre"), email="david.c.sterratt@ed.ac.uk", comment=c(ORCID="0000-0001-9092-9099"))) Description: Makes the 'Qhull' library available in R, in a similar manner as in Octave and MATLAB. Qhull computes convex hulls, Delaunay triangulations, halfspace intersections about a point, Voronoi diagrams, furthest-site Delaunay triangulations, and furthest-site Voronoi diagrams. It runs in 2D, 3D, 4D, and higher dimensions. It implements the Quickhull algorithm for computing the convex hull. Qhull does not support constrained Delaunay triangulations, or mesh generation of non-convex objects, but the package does include some R functions that allow for this. Version: 0.4.7 URL: https://davidcsterratt.github.io/geometry/ Date: 2023-02-03 BugReports: https://github.com/davidcsterratt/geometry/issues Depends: R (>= 3.0.0) Imports: magic, Rcpp, lpSolve, linprog Suggests: spelling, testthat, rgl, R.matlab, interp LinkingTo: Rcpp, RcppProgress Encoding: UTF-8 Language: en-GB RoxygenNote: 7.2.3 NeedsCompilation: yes Packaged: 2023-02-03 22:24:09 UTC; dcs Author: Jean-Romain Roussel [cph, ctb] (wrote tsearch function with QuadTrees), C. B. Barber [cph], Kai Habel [cph, aut], Raoul Grasman [cph, aut], Robert B. Gramacy [cph, aut], Pavlo Mozharovskyi [cph, aut], David C. Sterratt [cph, aut, cre] () Maintainer: David C. Sterratt Repository: CRAN Date/Publication: 2023-02-03 23:02:29 UTC geometry/build/0000755000176200001440000000000014367304611013211 5ustar liggesusersgeometry/build/vignette.rds0000644000176200001440000000030714367304611015550 0ustar liggesusersb```b`a@, $؀XX84OaFiNnj^P^9_ HN!"1 '΂449.@˅2@„5/1hvԂԼ?iN,/AQU▙ 7$apq2݀a>9`~xMI,F(WJbI^ZP?Ggeometry/tests/0000755000176200001440000000000013432270466013256 5ustar liggesusersgeometry/tests/spelling.R0000644000176200001440000000024113432270466015213 0ustar liggesusersif(requireNamespace('spelling', quietly = TRUE)) spelling::spell_check_test(vignettes = TRUE, error = FALSE, skip_on_cran = TRUE) geometry/tests/testthat/0000755000176200001440000000000014367311205015111 5ustar liggesusersgeometry/tests/testthat/test-tsearch.R0000644000176200001440000001222513717744212017652 0ustar liggesuserscontext("tsearch") test_that("tsearch gives the expected output", { x <- c(-1, -1, 1) y <- c(-1, 1, -1) p <- cbind(x, y) tri <- matrix(c(1, 2, 3), 1, 3) ## Should be in triangle #1 ts <- tsearch(x, y, tri, -1, -1) expect_equal(ts, 1) ## Should be in triangle #1 ts <- tsearch(x, y, tri, 1, -1) expect_equal(ts, 1) ## Should be in triangle #1 ts <- tsearch(x, y, tri, -1, 1) expect_equal(ts, 1) ## Centroid ts <- tsearch(x, y, tri, -1/3, -1/3) expect_equal(ts, 1) ## Should be outside triangle #1, so should return NA ts <- tsearch(x, y, tri, 1, 1) expect_true(is.na(ts)) }) test_that("tsearch can deal with faulty input", { x <- c(-1, -1, 1) y <- c(-1, 1, -1) p <- cbind(x, y) tri <- matrix(c(1, 2, 3), 1, 3) ## NULLs and NAs ## expect_error(tsearch(x, y, tri, NA, NA)) expect_error(tsearch(x, y, NA, -1, 1)) expect_error(tsearch(NA, NA, tri, -1, 1)) expect_error(tsearch(x, y, tri, NULL, NULL)) expect_error(tsearch(x, y, NULL, -1, 1)) expect_error(tsearch(NULL, NULL, tri, -1, 1)) ## Wrong number of columns expect_error(tsearch(p, 0, tri, -1, 1)) ## Non-integer triangulation expect_error(tsearch(x, y, matrix(runif(15), 5, 3), -1, 1), regexp="does not have integer elements") ## Wrong number of columns in triangulation expect_error(tsearch(x, y, matrix(1:4, 4, 2), -1, 1)) ## Mismatch in x and y lengths expect_error(tsearch(x, y[-1], tri, -1, 1)) ## Mismatch in xi and yi lengths expect_error(tsearch(x, y, tri, c(-1, 1), 1)) ## A subtle one! This gives numeric(0) as the final arguments and ## should give idx with no elements and a 0x3 matrix for p ps <- matrix(0, 0, 2) expect_equal(tsearch(x, y, tri, ps[,1], ps[,2], bary=TRUE), list(idx=integer(0), p=matrix(0, 0, 3))) }) ## See ## http://totologic.blogspot.co.uk/2014/01/accurate-point-in-triangle-test.html ## for inspiration for the test below test_that("tsearch gives the expected output when computer precision problem arise", { # ==== Hand made test ==== x1 <- 1/10 y1 <- 1/9 x2 <- 100/8 y2 <- 100/3 P <- rbind(c(x1, y1), c(x2, y2), c(100/4, 100/9), c(-100/8, 100/6)) # And a single point p(x, y) lying exactly on the segment [p1, p2] : xi <- x1 + (3/7)*(x2 - x1) yi <- y1 + (3/7)*(y2 - y1) # Should always give triangle 2 since this is the lastest tested tri1 <- rbind(1:3, c(1, 2, 4)) ts <- tsearch(P[,1], P[,2], tri1, xi, yi) expect_equal(ts, 2) tri2 <- rbind(c(1, 2, 4), 1:3) ts <- tsearch(P[,1], P[,2], tri2, xi, yi) expect_equal(ts, 2) # The same but with only one triangle P <- rbind(c(x1, y1), c(x2, y2), c(100/4, 100/9)) tri <- matrix(c(1, 2, 3), 1, 3) ts <- tsearch(P[,1], P[,2], tri, xi, yi) expect_equal(ts, 1) tri <- matrix(c(3, 2, 1), 1, 3) ts <- tsearch(P[,1], P[,2], tri, xi, yi) expect_equal(ts, 1) # The same but with the other triangle P <- rbind(c(x2, y2), c(100/4, 100/9), c(-100/8, 100/6)) tri <- matrix(c(1, 2, 3), 1, 3) ts <- tsearch(P[,1], P[,2], tri, xi, yi) expect_equal(ts, 1) tri <- matrix(c(3, 2, 1), 1, 3) ts <- tsearch(P[,1], P[,2], tri, xi, yi) expect_equal(ts, 1) # Another test x <- c(6.89, 7.15, 7.03) y <- c(7.76, 7.75, 8.35) tri <- matrix(c(1, 2, 3), 1, 3) ts <- tsearch(x, y, tri, 7.125, 7.875) expect_equal(ts, 1) # ==== Test known to bug in former code ==== x <- c(278287.03, 278286.89, 278287.15, 278287.3) y <- c(602248.35, 602247.76, 602247.75, 602248.35) xi = 278287.125 yi = 602247.875 # Should always give triangle 2 but here it does not work tri = rbind(c(3,1,4), c(3,1,2)) ts <- tsearch(x, y, tri, xi, yi) expect_equal(ts, 2) tri = rbind(c(1,2,3), c(1,3,4)) ts <- tsearch(x, y, tri, xi, yi) expect_equal(ts, 1) # This is because the buffer epsilon is 1.0e-12. x <- c(278287.03, 278287.15, 278287.3) y <- c(602248.35, 602247.75, 602248.35) tri <- matrix(c(1, 2, 3), 1, 3) ts <- tsearch(x, y, tri, xi, yi) expect_true(is.na(ts)) #expect_equal(ts, 1) #With epsilon = 1.0e-10 it works. }) test_that("no regression on Issue #39", { ## See https://github.com/davidcsterratt/geometry/issues/39 ## vertices P <- rbind( c(373.8112, 4673.726), #31 c(222.9705, 4280.085), #32 c(291.0508, 4476.996), #42 c(553.4783, 4523.605), #43 c(222.6388, 4023.920), #44 c(445.2401, 4370.940), #48 c(81.54986, 4125.393)) #61 ## I found this error on my system with Ubuntu 16.04, R 3.4.1 and ## `geometry_0.4.2`. `geometry_0.4.0` gave the same error). The error ## persists on Windows 10 with R 3.6.1. ## triangulation T <- rbind( c(3, 2, 6), c(5, 2, 7), c(4, 1, 3)) ## data data <- rbind( c(221.6, 4171.8), c(250.4, 4311.8), c(496.6, 4516.2), c(254.0, 4294.8), c(199.4, 4072.6)) ## With bug returns NA for datapoints 3 and 5 expect_equal(tsearch(P[,1], P[,2],T, data[,1], data[,2]), c(2, 1, 3, 1, 2)) data2 <- rbind( c(221.6, 4171.8), c(250.4, 4311.8), c(496.6, 4516.2), c(254.0, 4294.8), c(199.4, 4072.0)) #note that only the Y coordinate of datapoint 5 was changed expect_equal(tsearch(P[,1], P[,2],T, data2[,1], data2[,2]), c(2, 1, 3, 1, 2)) }) geometry/tests/testthat/test-cart2sph.R0000644000176200001440000000377113432317404017746 0ustar liggesuserscontext("cart2sph") test_that("cart2sph works correctly", { x <- c(0, 1, 2) y <- c(0, 1, 2) z <- c(0, 1, 2) Ps <- cart2sph(x, y, z) expect_equal(Ps[,"theta"], c(0, pi/4, pi/4)) expect_equal(Ps[,"phi"], c(0, 1, 1)*atan(sqrt(0.5))) expect_equal(Ps[,"r"], c(0, 1, 2)*sqrt(3)) x <- 0 y <- c(0, 1, 2) z <- c(0, 1, 2) Ps <- cart2sph(x, y, z) expect_equal(Ps[,"theta"], c(0, 1, 1)*pi/2) expect_equal(Ps[,"phi"], c(0, 1, 1)*pi/4) expect_equal(Ps[,"r"], c(0, 1, 2)*sqrt(2)) x <- c(0, 1, 2) y <- 0 z <- c(0, 1, 2) Ps <- cart2sph(x, y, z) expect_equal(Ps[,"theta"], c(0, 0, 0)) expect_equal(Ps[,"phi"], c(0, 1, 1)*pi/4) expect_equal(Ps[,"r"], c(0, 1, 2)*sqrt(2)) x <- c(0, 1, 2) y <- c(0, 1, 2) z <- 0 Ps <- cart2sph(x, y, z) expect_equal(Ps[,"theta"], c(0, 1, 1)*pi/4) expect_equal(Ps[,"phi"], c(0, 0, 0)) expect_equal(Ps[,"r"], c(0, 1, 2)*sqrt(2)) C <- rbind(c(0, 0, 0), c(1, 0, 1), c(2, 0, 2)) S <- rbind(c(theta=0, phi=0, r=0), c(0, pi/4, sqrt(2)), c(0, pi/4, 2*sqrt(2))) expect_equal(cart2sph(C), S) }) test_that("cart2sph error validation works correctly", { expect_error(cart2sph()) expect_error(cart2sph(1,2)) expect_error(cart2sph(1,2,3,4)) expect_error(cart2sph(list(1, 2, 3)), regexp="input must be matrix with 3 columns") expect_error(cart2sph(array(1, c(3,3,2))), regexp="matrix input must have 3 columns") expect_error(cart2sph(cbind(1,2,3,4)), regexp=c("matrix input must have 3 columns")) expect_error(cart2sph(list(1,2,3), c(1,2,3), c(1,2,3)), regexp="numeric arrays of the same size") expect_error(cart2sph(c(1,2,3), list(1,2,3), c(1,2,3), regexp="numeric arrays of the same size")) expect_error(cart2sph(c(1,2,3), c(1,2,3), list(1,2,3)), regexp="numeric arrays of the same size") expect_error(cart2sph(array(1, c(3, 3, 3)), 1, array(1, c(3,3,2))), regexp="matrices of the same size") expect_error(cart2sph(array(1, c(3, 3, 3)), array(1, c(3,3,2)), 1), regexp="matrices of the same size") }) geometry/tests/testthat/test-delaunayn.R0000644000176200001440000000760713717744212020211 0ustar liggesuserscontext("delaunayn") test_that("delaunayn produces the correct output", { ## Create points that, when passed to Qhull with the Qt option, ## would give degenerate simplices - thanks to Bill Denney for ## example ps <- as.matrix(rbind(data.frame(a=0, b=0, d=0), merge(merge(data.frame(a=c(-1, 1)), data.frame(b=c(-1, 1))), data.frame(d=c(-1, 1))))) ts <- delaunayn(ps) expect_type(ts, "integer") expect_identical(dim(ts), c(12L, 4L)) ## With output.options=TRUE, there should be a trinagulation, areas and ## neighbours and the sum of the ares should be 8 ts.full <- delaunayn(ps, output.options=TRUE) expect_equal(ts, ts.full$tri, check.attributes=FALSE) expect_equal(length(ts.full$areas), nrow(ts.full$tri)) expect_equal(length(ts.full$neighbours), nrow(ts.full$tri)) expect_equal(sum(ts.full$area), 8) ## With full output, there should be a trinagulation, areas and ## neighbours and the sum of the ares should be 8 ## full will be deprecated in a future version ts.full <- delaunayn(ps, full=TRUE) expect_equal(ts, ts.full$tri, check.attributes=FALSE) expect_equal(length(ts.full$areas), nrow(ts.full$tri)) expect_equal(length(ts.full$neighbours), nrow(ts.full$tri)) expect_equal(sum(ts.full$area), 8) ## tsearchn shouldn't return a "degnerate simplex" error. expect_silent(tsearchn(ps, ts, cbind(1, 2, 4))) ## If the input matrix contains NAs, delaunayn should return an error ps <- rbind(ps, NA) expect_error(delaunayn(ps)) }) test_that("In the case of just one triangle, delaunayn returns a matrix", { pc <- rbind(c(0, 0), c(0, 1), c(1, 0)) pct <- delaunayn(pc) expect_type(pct, "integer") expect_identical(dim(pct), c(1L, 3L)) ## With no options it should also produce a triangulation. This ## mirrors the behaviour of octave and matlab pct <- delaunayn(pc, "") expect_type(pct, "integer") expect_identical(dim(pct), c(1L, 3L)) pct.full <- delaunayn(pc, output.options=TRUE) expect_equal(pct.full$areas, 0.5) }) test_that("In the case of a degenerate triangle, delaunayn returns a matrix with zero rows", { pc <- rbind(c(0, 0), c(0, 1), c(0, 2)) pct <- delaunayn(pc) expect_type(pct, "integer") expect_identical(dim(pct), c(0L, 3L)) pct.full <- delaunayn(pc, output.options=TRUE) expect_equal(length(pct.full$areas), 0) expect_equal(length(pct.full$neighbours), 0) }) test_that("In the case of just one tetrahaedron, delaunayn returns a matrix", { pc <- rbind(c(0, 0, 0), c(0, 1, 0), c(1, 0, 0), c(0, 0, 1)) pct <- delaunayn(pc) expect_type(pct, "integer") expect_identical(dim(pct), c(1L, 4L)) pct.full <- delaunayn(pc, output.options=TRUE) expect_equal(pct.full$areas, 1/6) }) test_that("Output to file works", { ps <- matrix(rnorm(3000), ncol=3) ps <- sqrt(3)*ps/drop(sqrt((ps^2) %*% rep(1, 3))) fname <- path.expand(file.path(tempdir(), "test1.txt")) pst <- delaunayn(ps, paste0("QJ TO '", fname, "'")) expect_true(file.exists(fname)) }) test_that("The QJ option can give degenerate simplices", { ## Create degenerate simplex - thanks to Bill Denney for example ps <- as.matrix(rbind(data.frame(a=0, b=0, d=0), merge(merge(data.frame(a=c(-1, 1)), data.frame(b=c(-1, 1))), data.frame(d=c(-1, 1))))) ## The QJ option leads to on simplex being very small ts <- delaunayn(ps, "QJ") expect_warning(tsearchn(ps, ts, cbind(1, 2, 4))) }) test_that("A square is triangulated", { ## This doesn't work if the Qz option isn't supplied square <- rbind(c(0, 0), c(0, 1), c(1, 0), c(1, 1)) expect_equal(delaunayn(square), rbind(c(4, 2, 1), c(4, 3, 1)), check.attributes=FALSE) expect_error(delaunayn(square, "", "QH6239 Qhull precision error: Initial simplex is cocircular or cospherical")) }) geometry/tests/testthat/test-pol2cart.R0000644000176200001440000000614513432317404017744 0ustar liggesuserscontext("pol2cart") test_that("pol2cart works correctly", { t <- c(0, 0.5, 1)*pi r <- 1 C <- pol2cart(t, r) expect_equal(C[,"x"], c(1, 0, -1)) expect_equal(C[,"y"], c(0, 1, 0)) t <- c(0, 1, 1)*pi/4 r <- sqrt(2)*c(0, 1, 2) C <- pol2cart(t, r) expect_equal(C[,"x"], c(0, 1, 2)) expect_equal(C[,"y"], c(0, 1, 2)) t <- c(0, 1, 1)*pi/4 r <- sqrt(2)*c(0, 1, 2) z <- c(0, 1, 2) C <- pol2cart(t, r, z) expect_equal(C[,"x"], c(0, 1, 2)) expect_equal(C[,"y"], c(0, 1, 2)) expect_equal(C[,"z"], z) t <- 0 r <- c(0, 1, 2) z <- c(0, 1, 2) C <- pol2cart (t, r, z) expect_equal (C[,"x"], c(0, 1, 2)) expect_equal (C[,"y"], c(0, 0, 0)) expect_equal (C[,"z"], z) t <- c(1, 1, 1)*pi/4 r <- 1 z <- c(0, 1, 2) C <- pol2cart (t, r, z) expect_equal(C[,"x"], c(1, 1, 1)/sqrt(2)) expect_equal(C[,"y"], c(1, 1, 1)/sqrt(2)) expect_equal(C[,"z"], z) t <- 0 r <- c(1, 2, 3) z <- 1 C <- pol2cart (t, r, z) expect_equal(C[,"x"], c(1, 2, 3)) expect_equal(C[,"y"], c(0, 0, 0)/sqrt (2)) expect_equal(C[,"z"], c(1, 1, 1)) P <- rbind(c(theta=0, r=0), c(pi/4, sqrt(2)), c(pi/4, 2*sqrt(2))) C <- rbind(c(x=0, y=0), c(1, 1), c(2, 2)) expect_equal(pol2cart(P), C) ## %!test ## %! P <- c(0, 0, 0 pi/4, sqrt(2), 1 pi/4, 2*sqrt(2), 2) ## %! C <- c(0, 0, 0 1, 1, 1 2, 2, 2) ## %! expect_equal (pol2cart (P), C, sqrt (eps)) ## %!test ## %! r <- ones (1, 1, 1, 2) ## %! r(1, 1, 1, 2) <- 2 ## %! t <- pi/2 * r ## %! c(x, y) <- pol2cart (t, r) ## %! X <- zeros (1, 1, 1, 2) ## %! X(1, 1, 1, 2) <- -2 ## %! Y <- zeros (1, 1, 1, 2) ## %! Y(1, 1, 1, 1) <- 1 ## %! expect_equal (C[,"x"], X, 2*eps) ## %! expect_equal (C[,"y"], Y, 2*eps) ## %!test ## %! c(t, r, Z) <- meshgrid (c(0, pi/2), c(1, 2), c(0, 1)) ## %! c(x, y, z) <- pol2cart (t, r, Z) ## %! X <- zeros(2, 2, 2) ## %! X(:, 1, 1) <- c(1 2) ## %! X(:, 1, 2) <- c(1 2) ## %! Y <- zeros(2, 2, 2) ## %! Y(:, 2, 1) <- c(1 2) ## %! Y(:, 2, 2) <- c(1 2) ## %! expect_equal (C[,"x"], X, eps) ## %! expect_equal (C[,"y"], Y, eps) ## %! expect_equal (z, Z) ## Test input validation expect_error(pol2cart()) expect_error(pol2cart(1,2,3,4)) expect_error(pol2cart(list(1,2,3)), regexp="input must be matrix with 2 or 3 columns") ## %expect_error pol2cart (ones (3,3,2)) ## %expect_error pol2cart (c(1)) ## %expect_error pol2cart (c(1,2,3,4)) ## %expect_error pol2cart ({1,2,3}, c(1,2,3)) ## %expect_error pol2cart (c(1,2,3), {1,2,3}) ## %expect_error pol2cart (ones (3,3,3), ones (3,2,3)) ## %expect_error pol2cart ({1,2,3}, c(1,2,3), c(1,2,3)) ## %expect_error pol2cart (c(1,2,3), {1,2,3}, c(1,2,3)) ## %expect_error pol2cart (c(1,2,3), c(1,2,3), {1,2,3}) ## %expect_error pol2cart (ones (3,3,3), 1, ones (3,2,3)) ## %expect_error pol2cart (ones (3,3,3), ones (3,2,3), 1) }) geometry/tests/testthat/test-tsearch-tsearchn-comparison.R0000644000176200001440000000073513717744212023632 0ustar liggesuserscontext("Comparison of tsearch and tsearchn") test_that("tsearch and tsearchn give the same results", { set.seed(1) X <- runif(50) Y <- runif(50) T <- delaunayn(cbind(X, Y)) XI <- runif(1000) YI <- runif(1000) out <- tsearch(X, Y, T, XI, YI) outn <- tsearchn(cbind(X, Y), T, cbind(XI, YI), fast=FALSE) expect_equal(na.omit(out), na.omit(outn$idx)) out <- tsearch(X, Y, T, XI, YI, TRUE) expect_equal(na.omit(outn$p), na.omit(out$p), tolerance=1e-12) }) geometry/tests/testthat/test-parallel.R0000644000176200001440000000125613717744212020017 0ustar liggesuserscontext("Interaction with parallel package") library(parallel) test_that("delaunayn can be called with mc.apply", { ## mc.cores must be 1 on Windows. Otherwise use only 2 cores to comply ## with CRAN guidelines. mc.cores <- ifelse(Sys.info()[1] == "Windows", 1, 2) ## Set seed for replicability set.seed(1) ## Create points and try standard Delaunay Triangulation N <- 100000 P <- matrix(runif(2*N), N, 2) T <- delaunayn(P) expect_identical(nrow(T), 199966L) ## Now try out the parallel version. Ts <- mclapply(list(P, P, P, P), delaunayn, mc.cores=mc.cores) expect_length(Ts, 4) expect_identical(nrow(Ts[[1]]), 199966L) expect_identical(Ts[[1]], T) }) geometry/tests/testthat/test-polyarea.R0000644000176200001440000000050513717744212020033 0ustar liggesuserscontext("polyarea") test_that("ployarea computes the area of two identical squares", { x <- c(1, 1, 3, 3, 1) y <- c(1, 3, 3, 1, 1) expect_equal(polyarea(cbind(x, x), cbind(y, y)), c(4, 4)) expect_equal(polyarea(cbind(x, x), cbind(y, y), 1), c(4, 4)) expect_equal(polyarea(rbind(x, x), rbind(y, y), 2), c(4, 4)) }) geometry/tests/testthat/test-distmesh2d.R0000644000176200001440000000122313432317404020254 0ustar liggesuserscontext("distmesh2d") test_that("distmesh2d can create a mesh on an ellipse", { bbox <- 2*matrix(c(-1,1,-1/2,1/2),2,2) ## Ellipse fd1 <- function(p,ra2=1/1.,rb2=1/2,xc2=0,yc2=0, ...){ if (!is.matrix(p)) p <- t(as.matrix(p)) return(sqrt(((p[,1]-xc2)/ra2)^2+((p[,2]-yc2)/rb2)^2)-1) } ## Solve using distmesh2d() fh <- function(p,...) rep(1,nrow(p)) ## This is the original line, which throws a warning. New one does ## too, but runs faster p <- ## distmesh2d(fd=fd1,fh=fh,p=NULL,h0=0.05,bbox=bbox,maxiter=1000, ## plot=FALSE) expect_warning(p <- distmesh2d(fd=fd1,fh=fh,p=NULL,h0=0.05,bbox=bbox,maxiter=10, plot=FALSE)) }) geometry/tests/testthat/test-intersectn.R0000644000176200001440000002131113462263674020400 0ustar liggesuserstest_that("intersectn can run on overlapping triangles", { ## Make star of David from isosceles triangles of length 3 ps1 <- rbind(c(0, sqrt(3)), c(3/2, -sqrt(3)/2), c(-3/2, -sqrt(3)/2)) ps2 <- ps1 ps2[,2] <- -ps2[,2] expect_equal(feasible.point(convhulln(ps1, output.options=TRUE), convhulln(ps2, output.options=TRUE)), c(0, 0)) is <- intersectn(ps1, ps2) isa <- intersectn(ps1, ps2, autoscale=TRUE) ## Intersecting area is same as 6 isosceles triangles of length 1, which have ## area sqrt(3)/4 ## expect_equal(is$ch$vol, sqrt(3)/4*6) expect_equal(isa$ch$vol, sqrt(3)/4*6) ## Another overlapping example ps2 <- ps1 ps2[,2] <- ps2[,2]+2 is <- intersectn(ps1, ps2) ## Now make one element of feasible point negative ps3 <- ps1 ps4 <- ps1 ps4[,2] <- -ps4[,2] ps3[,2] <- ps3[,2] - 10 ps4[,2] <- ps4[,2] - 10 expect_equal(feasible.point(convhulln(ps3, output.options=TRUE), convhulln(ps4, output.options=TRUE)), c(0, -10)) expect_equal(intersectn(ps3, ps4)$ch$vol, sqrt(3)/4*6) }) test_that("intersectn gives zero volume on non-overlapping triangles", { ps1 <- rbind(c(0, sqrt(3)), c(3/2, -sqrt(3)/2), c(-3/2, -sqrt(3)/2)) ps2 <- ps1 ps2[,2] <- ps2[,2] + 3 expect_equal(feasible.point(convhulln(ps1, "n"), convhulln(ps2, "n")), NA) is <- intersectn(ps1, ps2) expect_equal(is$ch$vol, 0) }) test_that("intersectn gives zero volume on non-overlapping triangles", { ps1 <- rbind(c(0, sqrt(3)), c(3/2, -sqrt(3)/2), c(-3/2, -sqrt(3)/2)) ps2 <- ps1 ps2[,2] <- ps2[,2] + 3 expect_equal(feasible.point(convhulln(ps1, "n"), convhulln(ps2, "n")), NA) is <- intersectn(ps1, ps2) expect_equal(is$ch$vol, 0) }) test_that("feasible.point works on a 3D example", { ## These tetrahedra do not overlap ps1 <- rbind(c( 0.5000000, -0.5000000, 0.5000000), c(-0.1018942, 0.1848312, -0.1260239), c( 0.5000000, -0.5000000, -0.5000000), c(-0.5000000, -0.5000000, -0.5000000)) ps2 <- rbind(c( 0.7581575, 0.6352585, 0.32876), c( 1.0000000, 0.0000000, 1.00000), c( 0.0000000, 0.0000000, 1.00000), c( 1.0000000, 0.0000000, 0.00000)) expect_equal(feasible.point(convhulln(ps1, "n"), convhulln(ps2, "n")), NA) }) test_that("intersectn can run on overlapping tetrahedra", { ## Make star of David from isocelese triangles of length 3 ps1 <- rbind(c(0, sqrt(3), 0), c(3/2, -sqrt(3)/2, 0), c(-3/2, -sqrt(3)/2, 0), c(0, 0, 3*sqrt(2/3))) ch1 <- convhulln(ps1, "FA") expect_equal(ch1$vol, sqrt(2)/12*27) ps2 <- ps1 ## By shifting tetrahedron up by half of its height, we should make ## something with 1/8 of the volume ps2[,3] <- ps2[,3] + 3/2*sqrt(2/3) is <- intersectn(ps1, ps2) expect_equal(is$ch$vol, sqrt(2)/12*27/8) }) test_that("intersectn can run on tetrahedra with a common point", { ps1 <- rbind(c(-0.4015654, -0.1084358, -0.3727391), c( 0.2384763, 0.3896078, -0.4447473), c( 0.5000000, -0.5000000, -0.5000000), c(-0.5000000, -0.5000000, -0.5000000)) ps2 <- rbind(c(-0.1392469, 0.03303547, -0.2436112), c( 0.3434195, -0.20338201, -0.4638141), c(-0.5000000, 0.50000000, -0.5000000), c(-0.5000000, -0.50000000, -0.5000000)) is <- intersectn(ps1, ps2) }) test_that("intersectn can compute the volume of overlapping delaunay triangulations of boxes", { ## Volume of overlap should be 1/8 ps1 <- rbox(2, B=0.5, C=0.5) ps2 <- rbox(2, B=0.5, C=0.5) + 0.5 dt1 <- delaunayn(ps1) dt2 <- delaunayn(ps2) vol <- 0 for (i in 1:nrow(dt1)) { for (j in 1:nrow(dt2)) { is <- intersectn(ps1[dt1[i,],], ps2[dt2[j,],]) vol <- vol + is$ch$vol } } expect_equal(vol, 0.125, tol=0.0001) }) test_that("intersectn can deal with some input that caused errors before fixing Issue #34", { ## Issue 34: https://github.com/davidcsterratt/geometry/issues/34 ps1 <- rbind( c(500.9656357388012111187, 843268.9656357388012111, 5.5), c(658.9656357388012111187, 843109.9656357388012111, 10.0), c(576.9656357388012111187, 843174.9656357388012111, 2.0), c(795.9656357388012111187, 843235.9656357388012111, 20.0)) ps2 <- rbind( c(707.9656400000000076034, 843153.9656399999512359, 12.000000000000000000000), c(645.6795799999999871943, 843166.4228499999735504, 10.200630000000000308091), c(631.6632399999999734064, 843182.9680800000205636, 8.772800000000000153477), c(707.9656400000000076034, 843153.9656399999512359, 12.000000000000000000000), c(608.9447900000000117871, 843172.7368899999419227, 7.772330000000000183036), c(607.9656400000000076034, 843173.9656399999512359, 7.669999999999999928946)) ## Before Issue #34 was fixed this threw an error: ## Received error code 2 from qhull. Qhull error: ## qhull precision warning: ## The initial hull is narrow (cosine of min. angle is 1.0000000000000002). ## expect_error(intersectn(ps1, ps2, tol=1E-4, return.chs=FALSE, options="Tv"), ".*The initial hull is narrow.*") ## This threw an error in Rev aab45b7311b6 out <- intersectn(ps1, ps2, tol=1E-4, return.chs=FALSE) }) test_that("intersectn works on rotated boxes", { rot <- function(theta) {return(rbind(c(cos(theta), sin(theta)), c(-sin(theta), cos(theta))))} ## Area of octogan created by two squares at 45 deg to each other sq <- rbox(C=1, D=2, n=0) expect_equal(intersectn(sq%*%rot(pi/4), sq)$ch$vol, 8*(sqrt(2) - 1)) rot4 <- function(theta) {return(rbind(c(cos(theta), sin(theta), 0, 0), c(-sin(theta), cos(theta), 0, 0), c(0, 0, 1, 0), c(0, 0, 0 ,1)))} ## Area of hyperoctoid created by two hypercubes at 45 deg to each other hc <- rbox(C=1, D=4, n=0) expect_equal(intersectn(hc%*%rot4(pi/4), hc)$ch$vol, 4*8*(sqrt(2) - 1)) }) test_that("intersectn works in 4D", { load(file.path(system.file(package="geometry"), "extdata", "intersectn4D.RData")) chi <- convhulln(seti, output.options=TRUE) chj <- convhulln(setj, output.options=TRUE) chij <- intersectn(seti, setj) chji <- intersectn(setj, seti) expect_equal(chij$ch$vol, chji$ch$vol) expect_true(chi$vol >= chij$ch$vol) expect_equal(chj$vol, chij$ch$vol) }) test_that("no regression on issue 35", { ## This gave an error in version 0.4.1 ## See https://github.com/davidcsterratt/geometry/issues/35 load(file.path(system.file(package="geometry"), "extdata", "issue35-intersectn.RData")) ch <- intersectn(seti, setj) expect_true(ch$ch$vol > 0) cha <- intersectn(seti, setj, autoscale=TRUE) expect_true(cha$ch$vol > 0) expect_equal(ch$ch$vol, cha$ch$vol) }) test_that("no regression on issue 35", { ## This is an example that requires various combinations of flags to ## be provided to lpSolve::lp ## ## Also testing a scaled version, which was easier to fixed with the ## set of flags used originally. ## https://github.com/davidcsterratt/geometry/issues/35 load(file.path(system.file(package="geometry"), "extdata", "error_15_620.RData")) ch <- intersectn(p1, p1) expect_true(ch$ch$vol > 0) cha <- intersectn(p1, p1, autoscale=TRUE) expect_true(cha$ch$vol > 0) expect_equal(ch$ch$vol, cha$ch$vol) zfac <- 10 p1[,3] <- p1[,3]*zfac p2[,3] <- p2[,3]*zfac ch <- intersectn(p1, p1) expect_true(ch$ch$vol > 0) cha <- intersectn(p1, p1, autoscale=TRUE) expect_true(cha$ch$vol > 0) expect_equal(ch$ch$vol, cha$ch$vol) }) test_that("intersectn doesn't crash on some input", { ## This is an example causes a crash if flag SCALE_GEOMETRIC (4) is ## given to lpSolve::lp in feasible.point() load(file.path(system.file(package="geometry"), "extdata", "overlap260-5034.RData")) ch <- intersectn(p1, p2) cha <- intersectn(p1, p2, autoscale=TRUE) expect_equal(ch$ch$vol, cha$ch$vol) }) test_that("intersectn doesn't crash on input that causes a crash with scale=7 on some processors", { ## This is an example causes a crash on some processors if flag SCALE_CURTISREID (7) is ## given to lpSolve::lp in feasible.point() load(file.path(system.file(package="geometry"), "extdata", "save-overlap32-176.RData")) intersectn(p1, p2, tol=1E-3) load(file.path(system.file(package="geometry"), "extdata", "save-overlap68-557.RData")) intersectn(p1, p2, tol=1E-3) }) test_that("intersectn doesn't crash on input that causes a crash with EQUILIBRIATE=1 on some processors", { ## This is an example causes a crash on some processors if flag ## EQUILIBRIATE is given to lpSolve::lp in feasible.point() load(file.path(system.file(package="geometry"), "extdata", "save-overlap149-9428.RData")) intersectn(p1, p2, tol=1E-3) }) geometry/tests/testthat/test-tsearchn.R0000644000176200001440000001002713525562746020035 0ustar liggesuserscontext("tsearchn") test_that("tsearchn gives the expected output", { ## Simple example x <- c(-1, -1, 1) y <- c(-1, 1, -1) p <- cbind(x, y) tri <- matrix(c(1, 2, 3), 1, 3) ## Should be in triangle #1 ts <- tsearchn(p, tri, cbind(-1, -1),fast=FALSE) expect_equal(ts$idx, 1) expect_equal(ts$p, cbind(1, 0, 0)) ## Should be in triangle #1 ts <- tsearchn(p, tri, cbind(1, -1), fast=FALSE) expect_equal(ts$idx, 1) expect_equal(ts$p, cbind(0, 0, 1)) ## Should be in triangle #1 ts <- tsearchn(p, tri, cbind(-1, 1), fast=FALSE) expect_equal(ts$idx, 1) expect_equal(ts$p, cbind(0, 1, 0)) ## Centroid ts <- tsearchn(p, tri, cbind(-1/3, -1/3), fast=FALSE) expect_equal(ts$idx, 1) expect_equal(ts$p, cbind(1/3, 1/3, 1/3)) ## Should be outside triangle #1, so should return NA ts <- tsearchn(p, tri, cbind(1, 1), fast=FALSE) expect_true(is.na(ts$idx)) expect_true(all(is.na(ts$p))) ## Create a mesh with a zero-area element (degenerate simplex) p <- cbind(c(-1, -1, 0, 1, 2), c(-1, 1, 0, 0, 0)) tri <- rbind(c(1, 2, 3), c(3, 4, 5)) ## Look for one point in one of the simplices and a point outwith the ## simplices. This forces tsearchn to look in all simplices. It ## shouldn't fail on the degenerate simplex. expect_warning(ts <- tsearchn(p, tri, rbind(c(-0.5, 0), c(3, 1)), fast=FALSE)) expect_equal(ts$idx, c(1, NA)) ts <- tsearchn(p, tri, rbind(c(-0.5, 0), c(3, 1)), fast=TRUE) expect_equal(ts$idx, c(1, NA)) }) context("tsearchn_delaunayn") test_that("tsearchn gives the expected output", { ## Erroneous input is caught safely. Force ## tsearchn_delaunayn to be called tfake <- matrix(1:3, 1, 3) class(tfake) <- "delaunayn" expect_error(suppressWarnings(tsearchn(NA, tfake, matrix(1:2, 1, 2))), "Delaunay triangulation has no delaunayn attribute") x <- cbind(c(-1, -1, 1), c(-1, 1, -1)) dt <- delaunayn(x, output.options=TRUE) ## Should be in triangle #1 xi <- cbind(-1, 1) expect_warning(ts <- tsearchn(NA, dt, xi)) expect_equal(ts$idx, 1) expect_equal(bary2cart(x[dt$tri[ts$idx,],], ts$p), xi) ## Centroid xi <- cbind(-1/3, -1/3) expect_warning(ts <- tsearchn(NA, dt, xi)) expect_equal(ts$idx, 1) expect_equal(ts$p, cbind(1/3, 1/3, 1/3)) ## Should be outside triangle #1, so should return NA xi <- cbind(1, 1) expect_warning(ts <- tsearchn(NA, dt, xi)) expect_true(is.na(ts$idx)) expect_true(all(is.na(ts$p))) ## Check mutliple points work xi <- rbind(c(-1, 1), c(-1/3, -1/3)) expect_warning(ts <- tsearchn(NA, dt, xi)) expect_equal(ts$idx, c(1, 1)) expect_equal(do.call(rbind, lapply(1:2, function(i) { bary2cart(x[dt$tri[ts$idx[i],],], ts$p[i,]) })), xi) ## Test against original version p <- cbind(c(0, 0, 1, 1, 0.5), c(0, 1, 1, 0, 0.5)) dt <- delaunayn(p, "FA") ## Interesting error, as default options are 'nixed dt <- delaunayn(p, output.options=TRUE) xi <- c(0.1, 0.5, 0.9, 0.5) yi <- c(0.5, 0.9, 0.5, 0.1) expect_warning(ts <- tsearchn(NA, dt, cbind(xi, yi))) expect_equal(ts$idx, tsearch(p[,1], p[,2], dt$tri, xi, yi, method="orig")) ## 3D test x <- rbox(D=3, B=1) dt <- delaunayn(x, output.options=TRUE) xi <- rbind(c(0.5, 0.5, 0.5), c(-0.5, -0.5, -0.5), c(0.9, 0, 0)) expect_warning(ts <- tsearchn(NA, dt, xi)) expect_equal(do.call(rbind, lapply(1:3, function(i) { bary2cart(x[dt$tri[ts$idx[i],],], ts$p[i,]) })), xi) ## 4D test ## ## This does not work yet. The "best" facet is not always the correct facet. ## x <- rbox(D=4, B=1) ## dt <- delaunayn(x, output.options=TRUE) ## xi <- rbind(c(0.5, 0.5, 0.5, 0.5), ## c(-0.49, -0.49, -0.49, -0.49), ## c(0.9, 0, 0, 0)) ## ts <- tsearchn(dt, NA, xi) ## expect_equal(do.call(rbind, lapply(1:3, function(i) { ## bary2cart(x[dt$tri[ts$idx[i],],], ts$p[i,]) ## })), xi) ## We don't need to test when creating a mesh with a zero-area ## element (degenerate simplex), as these shouldn't be produced by ## qhull. }) geometry/tests/testthat/test-cart2pol.R0000644000176200001440000000574113432317404017745 0ustar liggesuserscontext("cart2pol") test_that("cart2pol works correctly", { x <- c(0, 1, 2) y <- 0 P <- cart2pol (x, y) expect_equal (P[,"theta"], c(0, 0, 0)) expect_equal (P[,"r"], x) x <- c(0, 1, 2) y <- c(0, 1, 2) P <- cart2pol(x, y) expect_equal (P[,"theta"], c(0, pi/4, pi/4)) expect_equal (P[,"r"], sqrt (2)*c(0, 1, 2)) x <- c(0, 1, 2) y <- c(0, 1, 2) z <- c(0, 1, 2) P <- cart2pol (x, y, z) expect_equal (P[,"theta"], c(0, pi/4, pi/4)) expect_equal (P[,"r"], sqrt (2)*c(0, 1, 2)) expect_equal (P[,"z"], z) x <- c(0, 1, 2) y <- 0 z <- 0 P <- cart2pol (x, y, z) expect_equal (P[,"theta"], c(0, 0, 0)) expect_equal (P[,"r"], x) expect_equal (P[,"z"], c(0, 0, 0)) x <- 0 y <- c(0, 1, 2) z <- 0 P <- cart2pol (x, y, z) expect_equal (P[,"theta"], c(0, 1, 1)*pi/2) expect_equal (P[,"r"], y) expect_equal (P[,"z"], c(0, 0, 0)) x <- 0 y <- 0 z <- c(0, 1, 2) P <- cart2pol (x, y, z) expect_equal (P[,"theta"], c(0, 0, 0)) expect_equal (P[,"r"], c(0, 0, 0)) expect_equal (P[,"z"], z) C <- rbind(c(x=0, y=0), c(1, 1), c( 2, 2)) P <- rbind(c(theta=0, r=0), c(pi/4, sqrt(2)), c(pi/4, 2*sqrt(2))) expect_equal(cart2pol(C), P) ## %!test ## %! C <- c(0, 0, 0 1, 1, 1 2, 2, 2) ## %! P <- c(0, 0, 0 pi/4, sqrt(2), 1 pi/4, 2*sqrt(2), 2) ## %! expect_equal (cart2pol (C), P) ## %!test ## %! x <- zeros (1, 1, 1, 2) ## %! x(1, 1, 1, 2) <- sqrt (2) ## %! y <- x ## %! c(P[,"theta"], r) <- cart2pol (x, y) ## %! T <- zeros (1, 1, 1, 2) ## %! T(1, 1, 1, 2) <- pi/4 ## %! R <- zeros (1, 1, 1, 2) ## %! R(1, 1, 1, 2) <- 2 ## %! expect_equal (P[,"theta"], T) ## %! expect_equal (P[,"r"], R) ## %!test ## %! c(x, y, Z) <- meshgrid (c(0, 1), c(0, 1), c(0, 1)) ## %! c(t, r, z) <- cart2pol (x, y, Z) ## %! T(:, :, 1) <- c(0, 0 pi/2, pi/4) ## %! T(:, :, 2) <- T(:, :, 1) ## %! R <- sqrt (x.^2 + y.^2) ## %! expect_equal (t, T) ## %! expect_equal (P[,"r"], R) ## %! expect_equal (z, Z) ## Test input validation expect_error(cart2pol()) expect_error(cart2pol(1,2,3,4)) expect_error(cart2pol(list(1,2,3)), regexp="input must be matrix with 2 or 3 columns") ## expect_error cart2pol (ones (3,3,2)) ## expect_error cart2pol (c(1)) ## expect_error cart2pol (c(1,2,3,4)) ## expect_error cart2pol ({1,2,3}, c(1,2,3)) ## expect_error cart2pol (c(1,2,3), {1,2,3}) ## expect_error cart2pol (ones (3,3,3), ones (3,2,3)) ## expect_error cart2pol ({1,2,3}, c(1,2,3), c(1,2,3)) ## expect_error cart2pol (c(1,2,3), {1,2,3}, c(1,2,3)) ## expect_error cart2pol (c(1,2,3), c(1,2,3), {1,2,3}) ## expect_error cart2pol (ones (3,3,3), 1, ones (3,2,3)) ## expect_error cart2pol (ones (3,3,3), ones (3,2,3), 1) }) geometry/tests/testthat/test-inhulln.R0000644000176200001440000000226013717744212017670 0ustar liggesuserscontext("inhulln") test_that("inhulln gives the expected output", { ## Basic test x <- c(-1, -1, 1) y <- c(-1, 1, -1) p <- cbind(x, y) ch <- convhulln(p) ## Should be in hull pin <- inhulln(ch, cbind(-0.5, -0.5)) expect_true(pin) ## Should be outside hull pout <- inhulln(ch, cbind(1, 1)) expect_false(pout) ## Erroneous input is caught safely expect_error(inhulln(1, 2), "Convex hull has no convhulln attribute") expect_error(inhulln(ch, rbind(1, 1)), "Number of columns in test points p (1) not equal to dimension of hull (2).", fixed=TRUE) expect_error(inhulln(ch, cbind(1, 1, 1)), "Number of columns in test points p (3) not equal to dimension of hull (2).", fixed=TRUE) ## Test cube p <- rbox(n=0, D=3, C=1) ch <- convhulln(p) tp <- cbind(seq(-1.9, 1.9, by=0.2), 0, 0) pin <- inhulln(ch, tp) ## Points on x-axis should be in box only between -1 and 1 expect_equal(pin, tp[,1] < 1 & tp[,1] > -1) ## Test hypercube p <- rbox(n=0, D=4, C=1) ch <- convhulln(p) tp <- cbind(seq(-1.9, 1.9, by=0.2), 0, 0, 0) pin <- inhulln(ch, tp) ## Points on x-axis should be in box only between -1 and 1 expect_equal(pin, tp[,1] < 1 & tp[,1] > -1) }) geometry/tests/testthat/test-convhulln.R0000644000176200001440000001073213717744212020232 0ustar liggesuserscontext("convhulln") test_that("convhulln works on a cube", { ## Cube with unit length edges, centred on the origin ps <- rbox(0, C=0.5) ts <- convhulln(ps) ## Expect 12 facets, since faceted output is produced by default expect_equal(nrow(ts), 12) ## When "FA" is specified area and volume should be returned ts <- convhulln(ps, "FA") expect_equal(length(ts), 4) expect_equal(ts$area, 6) expect_equal(ts$vol, 1) ## When "n" is specified normals should be returned ts <- convhulln(ps, "n") expect_equal(length(ts), 3) ## There are 12 normals, one for each facet. There are 6 *unique* ## normals, since for each face of the cube there are two triangular ## facets with the same normal expect_equal(ts$normals, rbind(c( 0, 0, -1, -0.5), c( 0, 0, -1, -0.5), c( 0, -1, 0, -0.5), c( 0, -1, 0, -0.5), c( 1, 0, 0, -0.5), c( 1, 0, 0, -0.5), c( -1, 0, 0, -0.5), c( -1, 0, 0, -0.5), c( 0, 1, 0, -0.5), c( 0, 1, 0, -0.5), c( 0, 0, 1, -0.5), c( 0, 0, 1, -0.5))) }) test_that("convhulln works on a cube with output.options", { ## Cube with unit length edges, centred on the origin ps <- rbox(0, C=0.5) ts <- convhulln(ps) ## Expect 12 facets, since faceted output is produced by default expect_equal(nrow(ts), 12) ## When "FA" is specified area and volume should be returned ts <- convhulln(ps, output.options="FA") expect_equal(length(ts), 4) expect_equal(ts$area, 6) expect_equal(ts$vol, 1) ## When "n" is specified normals should be returned ts <- convhulln(ps, output.options="n") expect_equal(length(ts), 3) ## There are 12 normals, one for each facet. There are 6 *unique* ## normals, since for each face of the cube there are two triangular ## facets with the same normal expect_equal(ts$normals, rbind(c( 0, 0, -1, -0.5), c( 0, 0, -1, -0.5), c( 0, -1, 0, -0.5), c( 0, -1, 0, -0.5), c( 1, 0, 0, -0.5), c( 1, 0, 0, -0.5), c( -1, 0, 0, -0.5), c( -1, 0, 0, -0.5), c( 0, 1, 0, -0.5), c( 0, 1, 0, -0.5), c( 0, 0, 1, -0.5), c( 0, 0, 1, -0.5))) ts <- convhulln(ps, output.options=TRUE) expect_equal(length(ts), 5) }) test_that("convhulln can run on an example with 3000 points", { set.seed(1) ps <- matrix(rnorm(3000), ncol=3) ps <- sqrt(3)*ps/drop(sqrt((ps^2) %*% rep(1,3))) ts <- convhulln(ps) expect_identical(nrow(ts), 1996L) ts.full <- convhulln(ps, "FA") expect_equal(ts.full$area, 37.47065, tolerance=0.001) expect_equal(ts.full$vol, 21.50165, tolerance=0.001) }) test_that("convhulln throws an error with duplicated points", { load(file.path(system.file(package="geometry"), "extdata", "ordination.Rdata")) expect_error(out <- convhulln(ordination), "QH6114 qhull precision error: initial simplex is not convex") }) test_that("If the input matrix contains NAs, convhulln should return an error", { ps <- matrix(rnorm(999), ncol=3) ps <- sqrt(3)*ps/drop(sqrt((ps^2) %*% rep(1,3))) ps <- rbind(ps, NA) expect_error(convhulln(ps)) }) test_that("If there are not enough points to construct a simplex, an error is thrown", { expect_error(convhulln(diag(4))) }) test_that("Output to file works", { ## To prevent regression in package betapart fname <- path.expand(file.path(tempdir(), "vert.txt")) unlink(fname) tr <- rbind(c(3,1),c(2,1),c(4,3),c(4,2)) convhulln(tr, paste0("Fx TO '", fname, "'")) expect_true(file.exists(fname)) vert <- scan(fname, quiet=TRUE) expect_equal(vert, c(4, 2, 1, 0, 3)) }) test_that("Output of non-triangulated facets works", { X1 <- matrix(c( 1, 1, 1, 1, 1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 3, 0, 0), ncol=3, byrow = TRUE) ts1 <- convhulln(X1, return.non.triangulated.facets = TRUE) tbl1 <- table(rowSums(!is.na(ts1))) expect_equal(names(tbl1), c("3", "4")) expect_equal(as.numeric(tbl1), c(4, 5)) }) geometry/tests/testthat/test-extprod3d.R0000644000176200001440000000143113432317404020123 0ustar liggesusers## Based on Octave tests for cross.m ## http://hg.savannah.gnu.org/hgweb/octave/file/c2ef0eddf6bc/scripts/linear-algebra/cross.m context("extprod3d") test_that("extprod3d gives the expected output", { x <- c(1, 0, 0) y <- c(0, 1, 0) r <- c(0, 0, 1) expect_equal(extprod3d(x, y), r) expect_equal(extprod3d(x, y, drop=FALSE), t(r)) x <- c(1, 2, 3) y <- c(4, 5, 6) r <- c((2*6-3*5), (3*4-1*6), (1*5-2*4)) expect_equal(extprod3d(x, y), r) x <- rbind(c(1, 0, 0), c(0, 1, 0), c(0, 0, 1)) y <- rbind(c(0, 1, 0), c(0, 0, 1), c(1, 0, 0)) r <- rbind(c(0, 0, 1), c(1, 0, 0), c(0, 1, 0)) expect_equal(extprod3d(x, y), r) ##error extprod3d (0,0) ##error extprod3d () }) geometry/tests/testthat/test-sph2cart.R0000644000176200001440000000447313432317404017746 0ustar liggesuserscontext("sph2cart") test_that("sph2cart works correctly", { t <- c(0, 0, 0) p <- c(0, 0, 0) r <- c(0, 1, 2) C <- sph2cart (t, p, r) expect_equal(C[,"x"], r) expect_equal(C[,"y"], c(0, 0, 0)) expect_equal(C[,"z"], c(0, 0, 0)) t <- 0 p <- c(0, 0, 0) r <- c(0, 1, 2) C <- sph2cart(t, p, r) expect_equal(C[,"x"], r) expect_equal(C[,"y"], c(0, 0, 0)) expect_equal(C[,"z"], c(0, 0, 0)) t <- c(0, 0, 0) p <- 0 r <- c(0, 1, 2) C <- sph2cart (t, p, r) expect_equal(C[,"x"], r) expect_equal(C[,"y"], c(0, 0, 0)) expect_equal(C[,"z"], c(0, 0, 0)) t <- c(0, 0.5, 1)*pi p <- c(0, 0, 0) r <- 1 C <- sph2cart(t, p, r) expect_equal(C[,"x"], c(1, 0, -1)) expect_equal(C[,"y"], c(0, 1, 0)) expect_equal(C[,"z"], c(0, 0, 0)) C <- sph2cart(c(0, 0, 0), 0, 1) expect_equal(C[,"x"], c(1, 1, 1)) expect_equal(C[,"y"], c(0, 0, 0)) expect_equal(C[,"z"], c(0, 0, 0)) S <- rbind(c(0, 0, 1), c(0.5*pi, 0, 1), c(pi, 0, 1)) C <- rbind(c(x=1, y=0, z=0), c(0, 1, 0), c(-1, 0, 0)) expect_equal(sph2cart(S), C) }) # FIXME: to implement #! c(t, p, r) <- meshgrid (c(0, pi/2), c(0, pi/2), c(0, 1)) #! c(x, y, z) <- sph2cart (t, p, r) #! X <- zeros(2, 2, 2) #! X(1, 1, 2) <- 1 #! Y <- zeros(2, 2, 2) #! Y(1, 2, 2) <- 1 #! Z <- zeros(2, 2, 2) #! Z(2, :, 2) <- c(1 1) #! expect_equal(x, X, eps) #! expect_equal(y, Y, eps) #! expect_equal(z, Z) test_that("sph2cart error validation works correctly", { expect_error(sph2cart()) expect_error(sph2cart(1,2)) expect_error(sph2cart(1,2,3,4)) expect_error(sph2cart(list(1, 2, 3)), regexp="input must be matrix with 3 columns") expect_error(sph2cart(array(1, c(3,3,2))), regexp="matrix input must have 3 columns") expect_error(sph2cart(cbind(1,2,3,4)), regexp=c("matrix input must have 3 columns")) expect_error(sph2cart(list(1,2,3), c(1,2,3), c(1,2,3)), regexp="numeric arrays of the same size") expect_error(sph2cart(c(1,2,3), list(1,2,3), c(1,2,3), regexp="numeric arrays of the same size")) expect_error(sph2cart(c(1,2,3), c(1,2,3), list(1,2,3)), regexp="numeric arrays of the same size") expect_error(sph2cart(array(1, c(3, 3, 3)), 1, array(1, c(3,3,2))), regexp="matrices of the same size") expect_error(sph2cart(array(1, c(3, 3, 3)), array(1, c(3,3,2)), 1), regexp="matrices of the same size") }) geometry/tests/testthat/test-halfspacen.R0000644000176200001440000000534013432317404020316 0ustar liggesuserscontext("halfspacen") test_that("halfspacen works on a cube", { ## Cube with unit length edges, centred on the origin ps <- rbox(0, C=0.5) ## Convex hull. When "n" is specified normals should be returned ch <- convhulln(ps, "n") ## Intersections of half planes ## These points should be the same as the orginal points pn <- halfspacen(ch$normals, c(0, 0, 0)) ## Convex hull of these points should have same characteristics as original cube ts <- convhulln(pn, "FA") expect_equal(ts$area, 6) expect_equal(ts$vol, 1) ## If the feasible point is outwith the normlas to the cube, an ## error should be thrown expect_error(halfspacen(ch$normals, c(1, 1, 1))) }) test_that("halfspacen works on a cube with non triangulated facets", { ## Cube with unit length edges, centred on the origin ps <- rbox(0, C=0.5) ## Convex hull. When "n" is specified normals should be returned ch <- convhulln(ps, "n", return.non.triangulated.facets=TRUE) ## Intersections of half planes ## These points should be the same as the orginal points pn <- halfspacen(ch$normals, c(0, 0, 0)) ## Convex hull of these points should have same characteristics as original cube ts <- convhulln(pn, "FA") expect_equal(ts$area, 6) expect_equal(ts$vol, 1) ## If the feasible point is outwith the normlas to the cube, an ## error should be thrown expect_error(halfspacen(ch$normals, c(1, 1, 1))) }) test_that("halfspacen can compute volume of intersection of halfspaces", { ## Cube with unit length edges, centred on the origin ps1 <- rbox(0, C=0.5) ## Cube with unit length edges, centred on the (0.5, 0.5, 0.5) ps2 <- rbox(0, C=0.5) + 0.5 ## Convex hulls with normals ch1 <- convhulln(ps1, "n", return.non.triangulated.facets=TRUE) ch2 <- convhulln(ps2, "n", return.non.triangulated.facets=TRUE) ## Intersection of merged halfspaces pn <- halfspacen(rbind(ch1$normals, ch2$normals), c(0.25, 0.25, 0.25)) ## Convex hull of these points should be cube with vertices at ## intersection of cubes, i.e. a cube of length 0.5 ts <- convhulln(pn, "FA") expect_equal(ts$area, 6*0.5^2) expect_equal(ts$vol, 1*0.5^3) }) test_that("halfspacen can do the round trip on an example with 3000 points", { set.seed(1) ps <- matrix(rnorm(3000), ncol=3) ps <- sqrt(3)*ps/drop(sqrt((ps^2) %*% rep(1,3))) ch <- convhulln(ps, "n FA") pn <- halfspacen(ch$normals, c(0, 0, 0)) chn <- convhulln(pn, "n FA") expect_equal(ch$area, chn$area) expect_equal(ch$vol, chn$vol) }) test_that("halfspacen throws an error when the feasible point is not clearly inside the halfspace", { load(file.path(system.file(package="geometry"), "extdata", "halfspacen.RData")) expect_error(halfspacen(normals, fp), "QH6023 qhull input error") }) geometry/tests/testthat.R0000644000176200001440000000005213432270466015236 0ustar liggesuserslibrary(testthat) test_check("geometry") geometry/src/0000755000176200001440000000000014367304611012701 5ustar liggesusersgeometry/src/Rtsearch.cpp0000644000176200001440000000776213527030173015170 0ustar liggesusers/* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ // Originally written for package lidR by Jean-Romain Roussel // Author: Jean-Romain Roussel // 3 may 2017: copy from package lidR to package geometry by Jean-Romain Roussel to replace former code of tsearch // 4 may 2017: Add barycentric coordinates support to reproduce original tsearch function // 23 sept 2017: fix bug of computeur precision by Jean-Romain Roussel // [[Rcpp::depends(RcppProgress)]] #include #include #include "QuadTree.h" using namespace Rcpp; static inline double max (double a, double b, double c) { if (a < b) return (b < c ? c : b); else return (a < c ? c : a); } static inline double min (double a, double b, double c) { if (a > b) return (b > c ? c : b); else return (a > c ? c : a); } bool PointInTriangle(Point p0, Point p1, Point p2, Point p, Point* bary, double eps) { double det = ((p1.y - p2.y)*(p0.x - p2.x) + (p2.x - p1.x)*(p0.y - p2.y)); double a = ((p1.y - p2.y)*(p.x - p2.x) + (p2.x - p1.x)*(p.y - p2.y)) / det; double b = ((p2.y - p0.y)*(p.x - p2.x) + (p0.x - p2.x)*(p.y - p2.y)) / det; double c = 1 - a - b; bary->x = c; bary->y = b; return -eps <= a && a <= 1+eps && -eps <= b && b <= 1+eps && -eps <= c && c <= 1+eps; } //' @importFrom Rcpp sourceCpp // [[Rcpp::export]] SEXP C_tsearch(NumericVector x, NumericVector y, IntegerMatrix elem, NumericVector xi, NumericVector yi, bool bary = false, double eps = 1.0e-12) { QuadTree *tree = QuadTree::create(as< std::vector >(xi),as< std::vector >(yi), eps); int nelem = elem.nrow(); int np = xi.size(); // set false -> true if you want to display a progressbar Progress p(nelem, false); IntegerVector indexes(np); std::fill(indexes.begin(), indexes.end(), NA_INTEGER); NumericMatrix barycentric; if(bary) { barycentric = NumericMatrix(np, 3); std::fill(barycentric.begin(), barycentric.end(), NA_REAL); } // Loop over each triangle for (int k = 0; k < nelem; k++) { if (Progress::check_abort() ) { delete tree; return indexes; } else p.update(k); // Retrieve triangle A B C coordinates int iA = elem(k, 0) - 1; int iB = elem(k, 1) - 1; int iC = elem(k, 2) - 1; Point A(x(iA), y(iA)); Point B(x(iB), y(iB)); Point C(x(iC), y(iC)); // Boundingbox of A B C double rminx = min(A.x, B.x, C.x); double rmaxx = max(A.x, B.x, C.x); double rminy = min(A.y, B.y, C.y); double rmaxy = max(A.y, B.y, C.y); double xcenter = (rminx + rmaxx)/2; double ycenter = (rminy + rmaxy)/2; double half_width = (rmaxx - rminx)/2; double half_height = (rmaxy - rminy)/2; // QuadTree search of points in enclosing boundingbox std::vector points; tree->rect_lookup(xcenter, ycenter, half_width + eps, half_height + eps, points); // Compute if the points are in A B C for (unsigned int i = 0 ; i < points.size() ; i++) { Point pbary; if (PointInTriangle(A, B, C, *points[i], &pbary, eps)) { int id = points[i]->id; indexes(id) = k + 1; if(bary) { barycentric(id, 0) = 1 - pbary.x - pbary.y; barycentric(id, 1) = pbary.y; barycentric(id, 2) = pbary.x; } } } } delete tree; if (bary) { return (List::create(indexes, barycentric)); } else return (indexes); } geometry/src/random_r.c0000644000176200001440000001451413432323610014643 0ustar liggesusers/*
  ---------------------------------

   random_r.c and utilities
     Park & Miller's minimimal standard random number generator
     argc/argv conversion

     Used by rbox.  Do not use 'qh' 
*/

#include "libqhull_r.h"
#include "random_r.h"

#include 
#include 
#include 

#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

/*---------------------------------

 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 */

/*---------------------------------

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; i0 && strchr(argv[i], ' ')) {
        count += 2;  /* quote delimiters */
        for (s=argv[i]; *s; s++) {
          if (*s == '"') {
            count++;
          }
        }
      }
    }
    return count;
} /* argv_to_command_size */

/*---------------------------------

  qh_rand()
  qh_srand(qh, seed )
    generate pseudo-random number between 1 and 2^31 -2

  notes:
    For qhull and rbox, called from qh_RANDOMint(),etc. [user.h]

    From Park & Miller's minimal standard random number generator
      Communications of the ACM, 31:1192-1201, 1988.
    Does not use 0 or 2^31 -1
      this is silently enforced by qh_srand()
    Can make 'Rn' much faster by moving qh_rand to qh_distplane
*/

/* Global variables and constants */

#define qh_rand_a 16807
#define qh_rand_m 2147483647
#define qh_rand_q 127773  /* m div a */
#define qh_rand_r 2836    /* m mod a */

int qh_rand(qhT *qh) {
    int lo, hi, test;
    int seed = qh->last_random;

    hi = seed / qh_rand_q;  /* seed div q */
    lo = seed % qh_rand_q;  /* seed mod q */
    test = qh_rand_a * lo - qh_rand_r * hi;
    if (test > 0)
        seed= test;
    else
        seed= test + qh_rand_m;
    qh->last_random= seed;
    /* seed = seed < qh_RANDOMmax/2 ? 0 : qh_RANDOMmax;  for testing */
    /* seed = qh_RANDOMmax;  for testing */
    return seed;
} /* rand */

void qh_srand(qhT *qh, int seed) {
    if (seed < 1)
        qh->last_random= 1;
    else if (seed >= qh_rand_m)
        qh->last_random= qh_rand_m - 1;
    else
        qh->last_random= seed;
} /* qh_srand */

/*---------------------------------

qh_randomfactor(qh, scale, offset )
  return a random factor r * scale + offset

notes:
  qh.RANDOMa/b are defined in global_r.c
  qh_RANDOMint requires 'qh'
*/
realT qh_randomfactor(qhT *qh, realT scale, realT offset) {
    realT randr;

    randr= qh_RANDOMint;
    return randr * scale + offset;
} /* randomfactor */

/*---------------------------------

qh_randommatrix(qh, buffer, dim, rows )
  generate a random dim X dim matrix in range [-1,1]
  assumes buffer is [dim+1, dim]

  returns:
    sets buffer to random numbers
    sets rows to rows of buffer
    sets row[dim] as scratch row

  notes:
    qh_RANDOMint requires 'qh'
*/
void qh_randommatrix(qhT *qh, realT *buffer, int dim, realT **rows) {
    int i, k;
    realT **rowi, *coord, realr;

    coord= buffer;
    rowi= rows;
    for (i=0; i < dim; i++) {
        *(rowi++)= coord;
        for (k=0; k < dim; k++) {
            realr= qh_RANDOMint;
            *(coord++)= 2.0 * realr/(qh_RANDOMmax+1) - 1.0;
        }
    }
    *rowi= coord;
} /* randommatrix */

/*---------------------------------

  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 */
geometry/src/libqhull_r.h0000644000176200001440000014713413432323614015215 0ustar  liggesusers/*
  ---------------------------------

   libqhull_r.h
   user-level header file for using qhull.a library

   see qh-qhull_r.htm, qhull_ra.h

   Copyright (c) 1993-2015 The Geometry Center.
   $Id: //main/2015/qhull/src/libqhull_r/libqhull_r.h#8 $$Change: 2079 $
   $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $

   includes function prototypes for libqhull_r.c, geom_r.c, global_r.c, io_r.c, user.c

   use mem_r.h for mem_r.c
   use qset_r.h for qset_r.c

   see unix_r.c for an example of using libqhull_r.h

   recompile qhull if you change this file
*/

#ifndef qhDEFlibqhull
#define qhDEFlibqhull 1

/*=========================== -included files ==============*/

/* user_r.h first for QHULL_CRTDBG */
#include "user_r.h"      /* user definable constants (e.g., realT). */

#include "mem_r.h"   /* Needed for qhT in libqhull_r.h */
#include "qset_r.h"   /* Needed for QHULL_LIB_CHECK */
/* include stat_r.h after defining boolT.  statT needed for qhT in libqhull_r.h */

#include 
#include 
#include 
#include 

#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_r.h
#endif
#endif
#endif

/*============ constants and basic types ====================*/

extern const char qh_version[]; /* defined in global_r.c */
extern const char qh_version2[]; /* defined in global_r.c */

/*----------------------------------

  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

/*----------------------------------

  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;

/*----------------------------------

  flagT
    Boolean flag as a bit
*/
#define flagT unsigned int

/*----------------------------------

  boolT
    boolean value, either True or False

  notes:
    needed for portability
    Use qh_False/qh_True as synonyms
*/
#define boolT unsigned int
#ifdef False
#undef False
#endif
#ifdef True
#undef True
#endif
#define False 0
#define True 1
#define qh_False 0
#define qh_True 1

#include "stat_r.h"  /* needs boolT */

/*----------------------------------

  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;

/*----------------------------------

  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;

/*----------------------------------

  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 */

/*----------------------------------

  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_r.h */
#define qh_ERRqhull 5    /* internal error detected, matches mem_r.h */

/*----------------------------------

qh_FILEstderr
Fake stderr to distinguish error output from normal output
For C++ interface.  Must redefine qh_fprintf_qhull
*/
#define qh_FILEstderr ((FILE*)1)

/* ============ -structures- ====================
   each of the following structures is defined by a typedef
   all realT and coordT fields occur at the beginning of a structure
        (otherwise space may be wasted due to alignment)
   define all flags together and pack into 32-bit number

   DEFqhT and DEFsetT are likewise defined in
   mem_r.h, qset_r.h, and stat_r.h.

*/

typedef struct vertexT vertexT;
typedef struct ridgeT ridgeT;
typedef struct facetT facetT;

#ifndef DEFqhT
#define DEFqhT 1
typedef struct qhT qhT;          /* defined below */
#endif

#ifndef DEFsetT
#define DEFsetT 1
typedef struct setT setT;          /* defined in qset_r.h */
#endif

/*----------------------------------

  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 or Voronoi center for output
    f.simplicial        True if facet is simplicial
    f.flipped           True if facet does not include qh.interior_point

  for constructing hull:
    f.visible           True if facet on list of visible facets (will be deleted)
    f.newfacet          True if facet on list of newly created facets
    f.coplanarset       set of points coplanar with this facet
                        (includes near-inside points for later testing)
    f.outsideset        set of points outside of this facet
    f.furthestdist      distance to furthest point of outside set
    f.visitid           marks visited facets during a loop
    f.replace           replacement facet for to-be-deleted, visible facets
    f.samecycle,newcycle cycle of facets for merging into horizon facet

  see below for other flags and fields
*/
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_r.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_r.c */
  flagT    tricoplanar:1; /* True if TRIangulate and simplicial and coplanar with a neighbor */
                          /*   all tricoplanars share the same apex */
                          /*   all tricoplanars share the same ->center, ->normal, ->offset, ->maxoutside */
                          /*     ->keepcentrum is true for the owner.  It has the ->coplanareset */
                          /*   if ->degenerate, does not span facet (one logical ridge) */
                          /*   during qh_triangulate, f.trivisible points to original facet */
  flagT    newfacet:1;  /* True if facet on qh.newfacet_list (new 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 (qhT *qh, 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) */
};


/*----------------------------------

  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 */
};

/*----------------------------------

  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_r.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 ============================*/

/*----------------------------------

  qhT
   All global variables for qhull are in qhT.  It includes qhmemT, qhstatT, and rbox globals

   This version of Qhull is reentrant, but it is not thread-safe.

   Do not run separate threads on the same instance of qhT.

   QHULL_LIB_CHECK checks that a program and the corresponding
   qhull library were built with the same type of header files.
*/

#define QHULL_NON_REENTRANT 0
#define QHULL_QH_POINTER 1
#define QHULL_REENTRANT 2

#define QHULL_LIB_TYPE QHULL_REENTRANT

#define QHULL_LIB_CHECK qh_lib_check(QHULL_LIB_TYPE, sizeof(qhT), sizeof(vertexT), sizeof(ridgeT), sizeof(facetT), sizeof(setT), sizeof(qhmemT));
#define QHULL_LIB_CHECK_RBOX qh_lib_check(QHULL_LIB_TYPE, sizeof(qhT), sizeof(vertexT), sizeof(ridgeT), sizeof(facetT), 0, 0);

struct qhT {

/*----------------------------------

  qh constants
    configuration flags and constants for Qhull

  notes:
    The user configures Qhull by defining flags.  They are
    copied into qh by qh_setflags().  qh-quick_r.htm#options defines the flags.
*/
  boolT ALLpoints;        /* true 'Qs' if search all points for initial simplex */
  boolT 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 (qhT *qh, 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_r.c */
  boolT KEEPnearinside;   /* true if near-inside points in coplanarset */
  int   hull_dim;         /* dimension of hull, set by initbuffers */
  int   input_dim;        /* dimension of input, set by initbuffers */
  int   num_points;       /* number of input points */
  pointT *first_point;    /* array of input points, see POINTSmalloc */
  boolT POINTSmalloc;     /*   true if qh.first_point/num_points allocated */
  pointT *input_points;   /* copy of original qh.first_point for input points for qh_joggleinput */
  boolT input_malloc;     /* true if qh.input_points malloc'd */
  char  qhull_command[256];/* command line that invoked this program */
  int   qhull_commandsiz2; /*    size of qhull_command at qh_clear_outputflags */
  char  rbox_command[256]; /* command line that produced the input points */
  char  qhull_options[512];/* descriptive list of options */
  int   qhull_optionlen;  /*    length of last line */
  int   qhull_optionsiz;  /*    size of qhull_options at qh_build_withrestart */
  int   qhull_optionsiz2; /*    size of qhull_options at qh_clear_outputflags */
  int   run_id;           /* non-zero, random identifier for this instance of qhull */
  boolT VERTEXneighbors;  /* true if maintaining vertex neighbors */
  boolT ZEROcentrum;      /* true if 'C-0' or 'C-0 Qx'.  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 */

/*----------------------------------

  qh precision constants
    precision constants for Qhull

  notes:
    qh_detroundoff(qh) 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 */

/*----------------------------------

  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_start */
  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) */

/*----------------------------------

  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() */

/*----------------------------------

  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 (qhT *qh, qh_check_maxout) */

/*----------------------------------

  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 */

/*----------------------------------

  qh global buffers
    defines buffers for maxtrix operations, input, and error messages
*/
  coordT *gm_matrix;      /* (dim+1)Xdim matrix for geom_r.c */
  coordT **gm_row;        /* array of gm_matrix rows */
  char* line;             /* malloc'd input line of maxline+1 chars */
  int maxline;
  coordT *half_space;     /* malloc'd input array for halfspace (qh.normal_size+coordT) */
  coordT *temp_malloc;    /* malloc'd input array for points */

/*----------------------------------

  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 (qhT *qh, 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 */
  setT *old_tempstack;    /* for saving qh->qhmem.tempstack in save_qhull */
  int   ridgeoutnum;      /* number of ridges for 4OFF output (qh_printbegin,etc) */

/*----------------------------------

  qh memory management, rbox globals, and statistics

  Replaces global data structures defined for libqhull
*/
  int     last_random;    /* Last random number from qh_rand (random_r.c) */
  jmp_buf rbox_errexit;   /* errexit from rboxlib_r.c, defined by qh_rboxpoints() only */
  char    jmpXtra3[40];   /* extra bytes in case jmp_buf is defined wrong by compiler */
  int     rbox_isinteger;
  double  rbox_out_offset;
  void *  cpp_object;     /* C++ pointer.  Currently used by RboxPoints.qh_fprintf_rbox */

  /* Last, otherwise zero'd by qh_initqhull_start2 (global_r.c */
  qhmemT  qhmem;          /* Qhull managed memory (mem_r.h) */
  /* After qhmem because its size depends on the number of statistics */
  qhstatT qhstat;         /* Qhull statistics (stat_r.h) */
};

/*=========== -macros- =========================*/

/*----------------------------------

  otherfacet_(ridge, facet)
    return neighboring facet for a ridge in facet
*/
#define otherfacet_(ridge, facet) \
                        (((ridge)->top == (facet)) ? (ridge)->bottom : (ridge)->top)

/*----------------------------------

  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 ===================*/

/*----------------------------------

  FORALLfacets { ... }
    assign 'facet' to each facet in qh.facet_list

  notes:
    uses 'facetT *facet;'
    assumes last facet is a sentinel
    assumes qh defined

  see:
    FORALLfacet_( facetlist )
*/
#define FORALLfacets for (facet=qh->facet_list;facet && facet->next;facet=facet->next)

/*----------------------------------

  FORALLpoints { ... }
    assign 'point' to each point in qh.first_point, qh.num_points

  notes:
    assumes qh defined

  declare:
    coordT *point, *pointtemp;
*/
#define FORALLpoints FORALLpoint_(qh, qh->first_point, qh->num_points)

/*----------------------------------

  FORALLpoint_( qh, points, num) { ... }
    assign 'point' to each point in points array of num points

  declare:
    coordT *point, *pointtemp;
*/
#define FORALLpoint_(qh, points, num) for (point= (points), \
      pointtemp= (points)+qh->hull_dim*(num); point < pointtemp; point += qh->hull_dim)

/*----------------------------------

  FORALLvertices { ... }
    assign 'vertex' to each vertex in qh.vertex_list

  declare:
    vertexT *vertex;

  notes:
    assumes qh.vertex_list terminated with a sentinel
    assumes qh defined
*/
#define FORALLvertices for (vertex=qh->vertex_list;vertex && vertex->next;vertex= vertex->next)

/*----------------------------------

  FOREACHfacet_( facets ) { ... }
    assign 'facet' to each facet in facets

  declare:
    facetT *facet, **facetp;

  see:
    FOREACHsetelement_
*/
#define FOREACHfacet_(facets)    FOREACHsetelement_(facetT, facets, facet)

/*----------------------------------

  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:
    FOREACHsetelement_
*/
#define FOREACHneighbor_(facet)  FOREACHsetelement_(facetT, facet->neighbors, neighbor)

/*----------------------------------

  FOREACHpoint_( points ) { ... }
    assign 'point' to each point in points set

  declare:
    pointT *point, **pointp;

  see:
    FOREACHsetelement_
*/
#define FOREACHpoint_(points)    FOREACHsetelement_(pointT, points, point)

/*----------------------------------

  FOREACHridge_( ridges ) { ... }
    assign 'ridge' to each ridge in ridges set

  declare:
    ridgeT *ridge, **ridgep;

  see:
    FOREACHsetelement_
*/
#define FOREACHridge_(ridges)    FOREACHsetelement_(ridgeT, ridges, ridge)

/*----------------------------------

  FOREACHvertex_( vertices ) { ... }
    assign 'vertex' to each vertex in vertices set

  declare:
    vertexT *vertex, **vertexp;

  see:
    FOREACHsetelement_
*/
#define FOREACHvertex_(vertices) FOREACHsetelement_(vertexT, vertices,vertex)

/*----------------------------------

  FOREACHfacet_i_( qh, facets ) { ... }
    assign 'facet' and 'facet_i' for each facet in facets set

  declare:
    facetT *facet;
    int     facet_n, facet_i;

  see:
    FOREACHsetelement_i_
*/
#define FOREACHfacet_i_(qh, facets)    FOREACHsetelement_i_(qh, facetT, facets, facet)

/*----------------------------------

  FOREACHneighbor_i_( qh, facet ) { ... }
    assign 'neighbor' and 'neighbor_i' for each neighbor in facet->neighbors

  FOREACHneighbor_i_( qh, vertex ) { ... }
    assign 'neighbor' and 'neighbor_i' for each neighbor in vertex->neighbors

  declare:
    facetT *neighbor;
    int     neighbor_n, neighbor_i;

  see:
    FOREACHsetelement_i_
*/
#define FOREACHneighbor_i_(qh, facet)  FOREACHsetelement_i_(qh, facetT, facet->neighbors, neighbor)

/*----------------------------------

  FOREACHpoint_i_( qh, points ) { ... }
    assign 'point' and 'point_i' for each point in points set

  declare:
    pointT *point;
    int     point_n, point_i;

  see:
    FOREACHsetelement_i_
*/
#define FOREACHpoint_i_(qh, points)    FOREACHsetelement_i_(qh, pointT, points, point)

/*----------------------------------

  FOREACHridge_i_( qh, ridges ) { ... }
    assign 'ridge' and 'ridge_i' for each ridge in ridges set

  declare:
    ridgeT *ridge;
    int     ridge_n, ridge_i;

  see:
    FOREACHsetelement_i_
*/
#define FOREACHridge_i_(qh, ridges)    FOREACHsetelement_i_(qh, ridgeT, ridges, ridge)

/*----------------------------------

  FOREACHvertex_i_( qh, vertices ) { ... }
    assign 'vertex' and 'vertex_i' for each vertex in vertices set

  declare:
    vertexT *vertex;
    int     vertex_n, vertex_i;

  see:
    FOREACHsetelement_i_
*/
#define FOREACHvertex_i_(qh, vertices) FOREACHsetelement_i_(qh, vertexT, vertices,vertex)

#ifdef __cplusplus
extern "C" {
#endif

/********* -libqhull_r.c prototypes (duplicated from qhull_ra.h) **********************/

void    qh_qhull(qhT *qh);
boolT   qh_addpoint(qhT *qh, pointT *furthest, facetT *facet, boolT checkdist);
void    qh_printsummary(qhT *qh, FILE *fp);

/********* -user.c prototypes (alphabetical) **********************/

void    qh_errexit(qhT *qh, int exitcode, facetT *facet, ridgeT *ridge);
void    qh_errprint(qhT *qh, const char* string, facetT *atfacet, facetT *otherfacet, ridgeT *atridge, vertexT *atvertex);
int     qh_new_qhull(qhT *qh, int dim, int numpoints, coordT *points, boolT ismalloc,
                char *qhull_cmd, FILE *outfile, FILE *errfile);
void    qh_printfacetlist(qhT *qh, facetT *facetlist, setT *facets, boolT printall);
void    qh_printhelp_degenerate(qhT *qh, FILE *fp);
void    qh_printhelp_narrowhull(qhT *qh, FILE *fp, realT minangle);
void    qh_printhelp_singular(qhT *qh, FILE *fp);
void    qh_user_memsizes(qhT *qh);

/********* -usermem_r.c prototypes (alphabetical) **********************/
void    qh_exit(int exitcode);
void    qh_fprintf_stderr(int msgcode, const char *fmt, ... );
void    qh_free(void *mem);
void   *qh_malloc(size_t size);

/********* -userprintf_r.c and userprintf_rbox_r.c prototypes **********************/
void    qh_fprintf(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... );
void    qh_fprintf_rbox(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... );

/***** -geom_r.c/geom2_r.c/random_r.c prototypes (duplicated from geom_r.h, random_r.h) ****************/

facetT *qh_findbest(qhT *qh, pointT *point, facetT *startfacet,
                     boolT bestoutside, boolT newfacets, boolT noupper,
                     realT *dist, boolT *isoutside, int *numpart);
facetT *qh_findbestnew(qhT *qh, pointT *point, facetT *startfacet,
                     realT *dist, boolT bestoutside, boolT *isoutside, int *numpart);
boolT   qh_gram_schmidt(qhT *qh, int dim, realT **rows);
void    qh_outerinner(qhT *qh, facetT *facet, realT *outerplane, realT *innerplane);
void    qh_printsummary(qhT *qh, FILE *fp);
void    qh_projectinput(qhT *qh);
void    qh_randommatrix(qhT *qh, realT *buffer, int dim, realT **row);
void    qh_rotateinput(qhT *qh, realT **rows);
void    qh_scaleinput(qhT *qh);
void    qh_setdelaunay(qhT *qh, int dim, int count, pointT *points);
coordT  *qh_sethalfspace_all(qhT *qh, int dim, int count, coordT *halfspaces, pointT *feasible);

/***** -global_r.c prototypes (alphabetical) ***********************/

unsigned long qh_clock(qhT *qh);
void    qh_checkflags(qhT *qh, char *command, char *hiddenflags);
void    qh_clear_outputflags(qhT *qh);
void    qh_freebuffers(qhT *qh);
void    qh_freeqhull(qhT *qh, boolT allmem);
void    qh_init_A(qhT *qh, FILE *infile, FILE *outfile, FILE *errfile, int argc, char *argv[]);
void    qh_init_B(qhT *qh, coordT *points, int numpoints, int dim, boolT ismalloc);
void    qh_init_qhull_command(qhT *qh, int argc, char *argv[]);
void    qh_initbuffers(qhT *qh, coordT *points, int numpoints, int dim, boolT ismalloc);
void    qh_initflags(qhT *qh, char *command);
void    qh_initqhull_buffers(qhT *qh);
void    qh_initqhull_globals(qhT *qh, coordT *points, int numpoints, int dim, boolT ismalloc);
void    qh_initqhull_mem(qhT *qh);
void    qh_initqhull_outputflags(qhT *qh);
void    qh_initqhull_start(qhT *qh, FILE *infile, FILE *outfile, FILE *errfile);
void    qh_initqhull_start2(qhT *qh, FILE *infile, FILE *outfile, FILE *errfile);
void    qh_initthresholds(qhT *qh, char *command);
void    qh_lib_check(int qhullLibraryType, int qhTsize, int vertexTsize, int ridgeTsize, int facetTsize, int setTsize, int qhmemTsize);
void    qh_option(qhT *qh, const char *option, int *i, realT *r);
void    qh_zero(qhT *qh, FILE *errfile);

/***** -io_r.c prototypes (duplicated from io_r.h) ***********************/

void    qh_dfacet(qhT *qh, unsigned id);
void    qh_dvertex(qhT *qh, unsigned id);
void    qh_printneighborhood(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetA, facetT *facetB, boolT printall);
void    qh_produce_output(qhT *qh);
coordT *qh_readpoints(qhT *qh, int *numpoints, int *dimension, boolT *ismalloc);


/********* -mem_r.c prototypes (duplicated from mem_r.h) **********************/

void qh_meminit(qhT *qh, FILE *ferr);
void qh_memfreeshort(qhT *qh, int *curlong, int *totlong);

/********* -poly_r.c/poly2_r.c prototypes (duplicated from poly_r.h) **********************/

void    qh_check_output(qhT *qh);
void    qh_check_points(qhT *qh);
setT   *qh_facetvertices(qhT *qh, facetT *facetlist, setT *facets, boolT allfacets);
facetT *qh_findbestfacet(qhT *qh, pointT *point, boolT bestoutside,
           realT *bestdist, boolT *isoutside);
vertexT *qh_nearvertex(qhT *qh, facetT *facet, pointT *point, realT *bestdistp);
pointT *qh_point(qhT *qh, int id);
setT   *qh_pointfacet(qhT *qh /*qh.facet_list*/);
int     qh_pointid(qhT *qh, pointT *point);
setT   *qh_pointvertex(qhT *qh /*qh.facet_list*/);
void    qh_setvoronoi_all(qhT *qh);
void    qh_triangulate(qhT *qh /*qh.facet_list*/);

/********* -rboxpoints_r.c prototypes **********************/
int     qh_rboxpoints(qhT *qh, char* rbox_command);
void    qh_errexit_rbox(qhT *qh, int exitcode);

/********* -stat_r.c prototypes (duplicated from stat_r.h) **********************/

void    qh_collectstatistics(qhT *qh);
void    qh_printallstatistics(qhT *qh, FILE *fp, const char *string);

#ifdef __cplusplus
} /* extern "C" */
#endif

#endif /* qhDEFlibqhull */
geometry/src/global_r.c0000644000176200001440000022154014366313003014624 0ustar  liggesusers
/*
  ---------------------------------

   global_r.c
   initializes all the globals of the qhull application

   see README

   see libqhull_r.h for qh.globals and function prototypes

   see qhull_ra.h for internal functions

   Copyright (c) 1993-2015 The Geometry Center.
   $Id: //main/2015/qhull/src/libqhull_r/global_r.c#16 $$Change: 2066 $
   $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
 */

#include "qhull_ra.h"

/*========= qh->definition -- globals defined in libqhull_r.h =======================*/

/*----------------------------------

  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_r.c, rbox_r.c, libqhull_r.c, qconvex_r.c, qdelaun_r.c qvoronoi_r.c, qhalf_r.c, testqset_r.c
*/

const char qh_version[]= "2015.2.r 2016/01/18";
const char qh_version2[]= "qhull_r 7.2.0 (2015.2.r 2016/01/18)";

/*---------------------------------

  qh_appendprint(qh, printFormat )
    append printFormat to qh.PRINTout unless already defined
*/
void qh_appendprint(qhT *qh, qh_PRINT format) {
  int i;

  for (i=0; i < qh_PRINTEND; i++) {
    if (qh->PRINTout[i] == format && format != qh_PRINTqhull)
      break;
    if (!qh->PRINTout[i]) {
      qh->PRINTout[i]= format;
      break;
    }
  }
} /* appendprint */

/*---------------------------------

  qh_checkflags(qh, commandStr, hiddenFlags )
    errors if commandStr contains hiddenFlags
    hiddenFlags starts and ends with a space and is space delimited (checked)

  notes:
    ignores first word (e.g., "qconvex i")
    use qh_strtol/strtod since strtol/strtod may or may not skip trailing spaces

  see:
    qh_initflags() initializes Qhull according to commandStr
*/
void qh_checkflags(qhT *qh, char *command, char *hiddenflags) {
  char *s= command, *t, *chkerr; /* qh_skipfilename is non-const */
  char key, opt, prevopt;
  char chkkey[]= "   ";
  char chkopt[]=  "    ";
  char chkopt2[]= "     ";
  boolT waserr= False;

  if (*hiddenflags != ' ' || hiddenflags[strlen(hiddenflags)-1] != ' ') {
    qh_fprintf(qh, qh->ferr, 6026, "qhull error (qh_checkflags): hiddenflags must start and end with a space: \"%s\"", hiddenflags);
    qh_errexit(qh, qh_ERRinput, NULL, NULL);
  }
  if (strpbrk(hiddenflags, ",\n\r\t")) {
    qh_fprintf(qh, qh->ferr, 6027, "qhull error (qh_checkflags): hiddenflags contains commas, newlines, or tabs: \"%s\"", hiddenflags);
    qh_errexit(qh, 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(qh, ++s);
      continue;
    }
    chkkey[1]= key;
    if (strstr(hiddenflags, chkkey)) {
      chkerr= chkkey;
    }else if (isupper(key)) {
      opt= ' ';
      prevopt= ' ';
      chkopt[1]= key;
      chkopt2[1]= key;
      while (!chkerr && *s && !isspace(*s)) {
        opt= *s++;
        if (isalpha(opt)) {
          chkopt[2]= opt;
          if (strstr(hiddenflags, chkopt))
            chkerr= chkopt;
          if (prevopt != ' ') {
            chkopt2[2]= prevopt;
            chkopt2[3]= opt;
            if (strstr(hiddenflags, chkopt2))
              chkerr= chkopt2;
          }
        }else if (key == 'Q' && isdigit(opt) && prevopt != 'b'
              && (prevopt == ' ' || islower(prevopt))) {
            chkopt[2]= opt;
            if (strstr(hiddenflags, chkopt))
              chkerr= chkopt;
        }else {
          qh_strtod(s-1, &t);
          if (s < t)
            s= t;
        }
        prevopt= opt;
      }
    }
    if (chkerr) {
      *chkerr= '\'';
      chkerr[strlen(chkerr)-1]=  '\'';
      qh_fprintf(qh, qh->ferr, 6029, "qhull error: option %s is not used with this program.\n             It may be used with qhull.\n", chkerr);
      waserr= True;
    }
  }
  if (waserr)
    qh_errexit(qh, qh_ERRinput, NULL, NULL);
} /* checkflags */

/*---------------------------------

  qh_clear_outputflags(qh)
    Clear output flags for QhullPoints
*/
void qh_clear_outputflags(qhT *qh) {
  int i,k;

  qh->ANNOTATEoutput= False;
  qh->DOintersections= False;
  qh->DROPdim= -1;
  qh->FORCEoutput= False;
  qh->GETarea= False;
  qh->GOODpoint= 0;
  qh->GOODpointp= NULL;
  qh->GOODthreshold= False;
  qh->GOODvertex= 0;
  qh->GOODvertexp= NULL;
  qh->IStracing= 0;
  qh->KEEParea= False;
  qh->KEEPmerge= False;
  qh->KEEPminArea= REALmax;
  qh->PRINTcentrums= False;
  qh->PRINTcoplanar= False;
  qh->PRINTdots= False;
  qh->PRINTgood= False;
  qh->PRINTinner= False;
  qh->PRINTneighbors= False;
  qh->PRINTnoplanes= False;
  qh->PRINToptions1st= False;
  qh->PRINTouter= False;
  qh->PRINTprecision= True;
  qh->PRINTridges= False;
  qh->PRINTspheres= False;
  qh->PRINTstatistics= False;
  qh->PRINTsummary= False;
  qh->PRINTtransparent= False;
  qh->SPLITthresholds= False;
  qh->TRACElevel= 0;
  qh->TRInormals= False;
  qh->USEstdout= False;
  qh->VERIFYoutput= False;
  for (k=qh->input_dim+1; k--; ) {  /* duplicated in qh_initqhull_buffers and qh_clear_outputflags */
    qh->lower_threshold[k]= -REALmax;
    qh->upper_threshold[k]= REALmax;
    qh->lower_bound[k]= -REALmax;
    qh->upper_bound[k]= REALmax;
  }

  for (i=0; i < qh_PRINTEND; i++) {
    qh->PRINTout[i]= qh_PRINTnone;
  }

  if (!qh->qhull_commandsiz2)
      qh->qhull_commandsiz2= (int)strlen(qh->qhull_command); /* WARN64 */
  else {
      qh->qhull_command[qh->qhull_commandsiz2]= '\0';
  }
  if (!qh->qhull_optionsiz2)
    qh->qhull_optionsiz2= (int)strlen(qh->qhull_options);  /* WARN64 */
  else {
    qh->qhull_options[qh->qhull_optionsiz2]= '\0';
    qh->qhull_optionlen= qh_OPTIONline;  /* start a new line */
  }
} /* clear_outputflags */

/*---------------------------------

  qh_clock()
    return user CPU time in 100ths (qh_SECtick)
    only defined for qh_CLOCKtype == 2

  notes:
    use first value to determine time 0
    from Stevens '92 8.15
*/
unsigned long qh_clock(qhT *qh) {

#if (qh_CLOCKtype == 2)
  struct tms time;
  static long clktck;  /* initialized first call and never updated */
  double ratio, cpu;
  unsigned long ticks;

  if (!clktck) {
    if ((clktck= sysconf(_SC_CLK_TCK)) < 0) {
      qh_fprintf(qh, qh->ferr, 6030, "qhull internal error (qh_clock): sysconf() failed.  Use qh_CLOCKtype 1 in user.h\n");
      qh_errexit(qh, qh_ERRqhull, NULL, NULL);
    }
  }
  if (times(&time) == -1) {
    qh_fprintf(qh, qh->ferr, 6031, "qhull internal error (qh_clock): times() failed.  Use qh_CLOCKtype 1 in user.h\n");
    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
  }
  ratio= qh_SECticks / (double)clktck;
  ticks= time.tms_utime * ratio;
  return ticks;
#else
  qh_fprintf(qh, qh->ferr, 6032, "qhull internal error (qh_clock): use qh_CLOCKtype 2 in user.h\n");
  qh_errexit(qh, qh_ERRqhull, NULL, NULL); /* never returns */
  return 0;
#endif
} /* clock */

/*---------------------------------

  qh_freebuffers()
    free up global memory buffers

  notes:
    must match qh_initbuffers()
*/
void qh_freebuffers(qhT *qh) {

  trace5((qh, qh->ferr, 5001, "qh_freebuffers: freeing up global memory buffers\n"));
  /* allocated by qh_initqhull_buffers */
  qh_memfree(qh, qh->NEARzero, qh->hull_dim * sizeof(realT));
  qh_memfree(qh, qh->lower_threshold, (qh->input_dim+1) * sizeof(realT));
  qh_memfree(qh, qh->upper_threshold, (qh->input_dim+1) * sizeof(realT));
  qh_memfree(qh, qh->lower_bound, (qh->input_dim+1) * sizeof(realT));
  qh_memfree(qh, qh->upper_bound, (qh->input_dim+1) * sizeof(realT));
  qh_memfree(qh, qh->gm_matrix, (qh->hull_dim+1) * qh->hull_dim * sizeof(coordT));
  qh_memfree(qh, 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, &qh->other_points);
  qh_setfree(qh, &qh->del_vertices);
  qh_setfree(qh, &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, qh->ferr, 5002, "qh_freebuffers: finished\n"));
} /* freebuffers */


/*---------------------------------

  qh_freebuild(qh, allmem )
    free global memory used by qh_initbuild and qh_buildhull
    if !allmem,
      does not free short memory (e.g., facetT, freed by qh_memfreeshort)

  design:
    free centrums
    free each vertex
    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(qhT *qh, boolT allmem) {
  facetT *facet;
  vertexT *vertex;
  ridgeT *ridge, **ridgep;
  mergeT *merge, **mergep;

  trace1((qh, qh->ferr, 1005, "qh_freebuild: free memory from qh_inithull and qh_buildhull\n"));
  if (qh->del_vertices)
    qh_settruncate(qh, qh->del_vertices, 0);
  if (allmem) {
    while ((vertex= qh->vertex_list)) {
      if (vertex->next)
        qh_delvertex(qh, vertex);
      else {
        qh_memfree(qh, vertex, (int)sizeof(vertexT));
        qh->newvertex_list= qh->vertex_list= NULL;
      }
    }
  }else if (qh->VERTEXneighbors) {
    FORALLvertices
      qh_setfreelong(qh, &(vertex->neighbors));
  }
  qh->VERTEXneighbors= False;
  qh->GOODclosest= NULL;
  if (allmem) {
    FORALLfacets {
      FOREACHridge_(facet->ridges)
        ridge->seen= False;
    }
    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(qh, &(ridge->vertices));
          qh_memfree(qh, ridge, (int)sizeof(ridgeT));
        }else
          ridge->seen= True;
      }
      qh_setfree(qh, &(facet->outsideset));
      qh_setfree(qh, &(facet->coplanarset));
      qh_setfree(qh, &(facet->neighbors));
      qh_setfree(qh, &(facet->ridges));
      qh_setfree(qh, &(facet->vertices));
      if (facet->next)
        qh_delfacet(qh, facet);
      else {
        qh_memfree(qh, facet, (int)sizeof(facetT));
        qh->visible_list= qh->newfacet_list= qh->facet_list= NULL;
      }
    }
  }else {
    FORALLfacets {
      qh_setfreelong(qh, &(facet->outsideset));
      qh_setfreelong(qh, &(facet->coplanarset));
      if (!facet->simplicial) {
        qh_setfreelong(qh, &(facet->neighbors));
        qh_setfreelong(qh, &(facet->ridges));
        qh_setfreelong(qh, &(facet->vertices));
      }
    }
  }
  qh_setfree(qh, &(qh->hash_table));
  qh_memfree(qh, qh->interior_point, qh->normal_size);
  qh->interior_point= NULL;
  FOREACHmerge_(qh->facet_mergeset)  /* usually empty */
    qh_memfree(qh, merge, (int)sizeof(mergeT));
  qh->facet_mergeset= NULL;  /* temp set */
  qh->degen_mergeset= NULL;  /* temp set */
  qh_settempfree_all(qh);
} /* freebuild */

/*---------------------------------

  qh_freeqhull(qh, allmem )

  free global memory and set qhT to 0
  if !allmem,
    does not free short memory (freed by qh_memfreeshort unless qh_NOmem)

notes:
  sets qh.NOerrexit in case caller forgets to
  Does not throw errors

see:
  see qh_initqhull_start2()
  For libqhull_r, qhstatT is part of qhT

design:
  free global and temporary memory from qh_initbuild and qh_buildhull
  free buffers
*/
void qh_freeqhull(qhT *qh, boolT allmem) {

  qh->NOerrexit= True;  /* no more setjmp since called at exit and ~QhullQh */
  trace1((qh, qh->ferr, 1006, "qh_freeqhull: free global memory\n"));
  qh_freebuild(qh, allmem);
  qh_freebuffers(qh);
  /* memset is the same in qh_freeqhull() and qh_initqhull_start2() */
  memset((char *)qh, 0, sizeof(qhT)-sizeof(qhmemT)-sizeof(qhstatT));
  qh->NOerrexit= True;
} /* freeqhull2 */

/*---------------------------------

  qh_init_A(qh, infile, outfile, errfile, argc, argv )
    initialize memory and stdio files
    convert input options to option string (qh.qhull_command)

  notes:
    infile may be NULL if qh_readpoints() is not called

    errfile should always be defined.  It is used for reporting
    errors.  outfile is used for output and format options.

    argc/argv may be 0/NULL

    called before error handling initialized
    qh_errexit() may not be used
*/
void qh_init_A(qhT *qh, FILE *infile, FILE *outfile, FILE *errfile, int argc, char *argv[]) {
  qh_meminit(qh, errfile);
  qh_initqhull_start(qh, infile, outfile, errfile);
  qh_init_qhull_command(qh, argc, argv);
} /* init_A */

/*---------------------------------

  qh_init_B(qh, points, numpoints, dim, ismalloc )
    initialize globals for points array

    points has numpoints dim-dimensional points
      points[0] is the first coordinate of the first point
      points[1] is the second coordinate of the first point
      points[dim] is the first coordinate of the second point

    ismalloc=True
      Qhull will call qh_free(points) on exit or input transformation
    ismalloc=False
      Qhull will allocate a new point array if needed for input transformation

    qh.qhull_command
      is the option string.
      It is defined by qh_init_B(), qh_qhull_command(), or qh_initflags

  returns:
    if qh.PROJECTinput or (qh.DELAUNAY and qh.PROJECTdelaunay)
      projects the input to a new point array

        if qh.DELAUNAY,
          qh.hull_dim is increased by one
        if qh.ATinfinity,
          qh_projectinput adds point-at-infinity for Delaunay tri.

    if qh.SCALEinput
      changes the upper and lower bounds of the input, see qh_scaleinput(qh)

    if qh.ROTATEinput
      rotates the input by a random rotation, see qh_rotateinput()
      if qh.DELAUNAY
        rotates about the last coordinate

  notes:
    called after points are defined
    qh_errexit() may be used
*/
void qh_init_B(qhT *qh, coordT *points, int numpoints, int dim, boolT ismalloc) {
  qh_initqhull_globals(qh, points, numpoints, dim, ismalloc);
  if (qh->qhmem.LASTsize == 0)
    qh_initqhull_mem(qh);
  /* mem_r.c and qset_r.c are initialized */
  qh_initqhull_buffers(qh);
  qh_initthresholds(qh, qh->qhull_command);
  if (qh->PROJECTinput || (qh->DELAUNAY && qh->PROJECTdelaunay))
    qh_projectinput(qh);
  if (qh->SCALEinput)
    qh_scaleinput(qh);
  if (qh->ROTATErandom >= 0) {
    qh_randommatrix(qh, qh->gm_matrix, qh->hull_dim, qh->gm_row);
    if (qh->DELAUNAY) {
      int k, lastk= qh->hull_dim-1;
      for (k=0; k < lastk; k++) {
        qh->gm_row[k][lastk]= 0.0;
        qh->gm_row[lastk][k]= 0.0;
      }
      qh->gm_row[lastk][lastk]= 1.0;
    }
    qh_gram_schmidt(qh, qh->hull_dim, qh->gm_row);
    qh_rotateinput(qh, qh->gm_row);
  }
} /* init_B */

/*---------------------------------

  qh_init_qhull_command(qh, argc, argv )
    build qh.qhull_command from argc/argv
    Calls qh_exit if qhull_command is too short

  returns:
    a space-delimited string of options (just as typed)

  notes:
    makes option string easy to input and output

    argc/argv may be 0/NULL
*/
void qh_init_qhull_command(qhT *qh, int argc, char *argv[]) {

  if (!qh_argv_to_command(argc, argv, qh->qhull_command, (int)sizeof(qh->qhull_command))){
    /* Assumes qh.ferr is defined. */
    qh_fprintf(qh, qh->ferr, 6033, "qhull input error: more than %d characters in command line.\n",
          (int)sizeof(qh->qhull_command));
    qh_exit(qh_ERRinput);  /* error reported, can not use qh_errexit */
  }
} /* init_qhull_command */

/*---------------------------------

  qh_initflags(qh, commandStr )
    set flags and initialized constants from commandStr
    calls qh_exit() if qh->NOerrexit

  returns:
    sets qh.qhull_command to command if needed

  notes:
    ignores first word (e.g., "qhull d")
    use qh_strtol/strtod since strtol/strtod may or may not skip trailing spaces

  see:
    qh_initthresholds() continues processing of 'Pdn' and 'PDn'
    'prompt' in unix_r.c for documentation

  design:
    for each space-delimited option group
      if top-level option
        check syntax
        append appropriate option to option string
        set appropriate global variable or append printFormat to print options
      else
        for each sub-option
          check syntax
          append appropriate option to option string
          set appropriate global variable or append printFormat to print options
*/
void qh_initflags(qhT *qh, char *command) {
  int k, i, lastproject;
  char *s= command, *t, *prev_s, *start, key;
  boolT isgeom= False, wasproject;
  realT r;

  if(qh->NOerrexit){
    qh_fprintf(qh, 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(qh, "delaunay", NULL, NULL);
      qh->DELAUNAY= True;
      break;
    case 'f':
      qh_option(qh, "facets", NULL, NULL);
      qh_appendprint(qh, qh_PRINTfacets);
      break;
    case 'i':
      qh_option(qh, "incidence", NULL, NULL);
      qh_appendprint(qh, qh_PRINTincidences);
      break;
    case 'm':
      qh_option(qh, "mathematica", NULL, NULL);
      qh_appendprint(qh, qh_PRINTmathematica);
      break;
    case 'n':
      qh_option(qh, "normals", NULL, NULL);
      qh_appendprint(qh, qh_PRINTnormals);
      break;
    case 'o':
      qh_option(qh, "offFile", NULL, NULL);
      qh_appendprint(qh, qh_PRINToff);
      break;
    case 'p':
      qh_option(qh, "points", NULL, NULL);
      qh_appendprint(qh, qh_PRINTpoints);
      break;
    case 's':
      qh_option(qh, "summary", NULL, NULL);
      qh->PRINTsummary= True;
      break;
    case 'v':
      qh_option(qh, "voronoi", NULL, NULL);
      qh->VORONOI= True;
      qh->DELAUNAY= True;
      break;
    case 'A':
      if (!isdigit(*s) && *s != '.' && *s != '-')
        qh_fprintf(qh, qh->ferr, 7002, "qhull warning: no maximum cosine angle given for option 'An'.  Ignored.\n");
      else {
        if (*s == '-') {
          qh->premerge_cos= -qh_strtod(s, &s);
          qh_option(qh, "Angle-premerge-", NULL, &qh->premerge_cos);
          qh->PREmerge= True;
        }else {
          qh->postmerge_cos= qh_strtod(s, &s);
          qh_option(qh, "Angle-postmerge", NULL, &qh->postmerge_cos);
          qh->POSTmerge= True;
        }
        qh->MERGING= True;
      }
      break;
    case 'C':
      if (!isdigit(*s) && *s != '.' && *s != '-')
        qh_fprintf(qh, qh->ferr, 7003, "qhull warning: no centrum radius given for option 'Cn'.  Ignored.\n");
      else {
        if (*s == '-') {
          qh->premerge_centrum= -qh_strtod(s, &s);
          qh_option(qh, "Centrum-premerge-", NULL, &qh->premerge_centrum);
          qh->PREmerge= True;
        }else {
          qh->postmerge_centrum= qh_strtod(s, &s);
          qh_option(qh, "Centrum-postmerge", NULL, &qh->postmerge_centrum);
          qh->POSTmerge= True;
        }
        qh->MERGING= True;
      }
      break;
    case 'E':
      if (*s == '-')
        qh_fprintf(qh, qh->ferr, 7004, "qhull warning: negative maximum roundoff given for option 'An'.  Ignored.\n");
      else if (!isdigit(*s))
        qh_fprintf(qh, qh->ferr, 7005, "qhull warning: no maximum roundoff given for option 'En'.  Ignored.\n");
      else {
        qh->DISTround= qh_strtod(s, &s);
        qh_option(qh, "Distance-roundoff", NULL, &qh->DISTround);
        qh->SETroundoff= True;
      }
      break;
    case 'H':
      start= s;
      qh->HALFspace= True;
      qh_strtod(s, &t);
      while (t > s)  {
        if (*t && !isspace(*t)) {
          if (*t == ',')
            t++;
          else
            qh_fprintf(qh, qh->ferr, 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, qh->ferr, 6034, "qhull error: insufficient memory for 'Hn,n,n'\n");
          qh_errexit(qh, qh_ERRmem, NULL, NULL);
        }
        strncpy(qh->feasible_string, start, (size_t)(t-start));
        qh_option(qh, "Halfspace-about", NULL, NULL);
        qh_option(qh, qh->feasible_string, NULL, NULL);
      }else
        qh_option(qh, "Halfspace", NULL, NULL);
      break;
    case 'R':
      if (!isdigit(*s))
        qh_fprintf(qh, qh->ferr, 7007, "qhull warning: missing random perturbation for option 'Rn'.  Ignored\n");
      else {
        qh->RANDOMfactor= qh_strtod(s, &s);
        qh_option(qh, "Random_perturb", NULL, &qh->RANDOMfactor);
        qh->RANDOMdist= True;
      }
      break;
    case 'V':
      if (!isdigit(*s) && *s != '-')
        qh_fprintf(qh, qh->ferr, 7008, "qhull warning: missing visible distance for option 'Vn'.  Ignored\n");
      else {
        qh->MINvisible= qh_strtod(s, &s);
        qh_option(qh, "Visible", NULL, &qh->MINvisible);
      }
      break;
    case 'U':
      if (!isdigit(*s) && *s != '-')
        qh_fprintf(qh, qh->ferr, 7009, "qhull warning: missing coplanar distance for option 'Un'.  Ignored\n");
      else {
        qh->MAXcoplanar= qh_strtod(s, &s);
        qh_option(qh, "U-coplanar", NULL, &qh->MAXcoplanar);
      }
      break;
    case 'W':
      if (*s == '-')
        qh_fprintf(qh, qh->ferr, 7010, "qhull warning: negative outside width for option 'Wn'.  Ignored.\n");
      else if (!isdigit(*s))
        qh_fprintf(qh, qh->ferr, 7011, "qhull warning: missing outside width for option 'Wn'.  Ignored\n");
      else {
        qh->MINoutside= qh_strtod(s, &s);
        qh_option(qh, "W-outside", NULL, &qh->MINoutside);
        qh->APPROXhull= True;
      }
      break;
    /************  sub menus ***************/
    case 'F':
      while (*s && !isspace(*s)) {
        switch (*s++) {
        case 'a':
          qh_option(qh, "Farea", NULL, NULL);
          qh_appendprint(qh, qh_PRINTarea);
          qh->GETarea= True;
          break;
        case 'A':
          qh_option(qh, "FArea-total", NULL, NULL);
          qh->GETarea= True;
          break;
        case 'c':
          qh_option(qh, "Fcoplanars", NULL, NULL);
          qh_appendprint(qh, qh_PRINTcoplanars);
          break;
        case 'C':
          qh_option(qh, "FCentrums", NULL, NULL);
          qh_appendprint(qh, qh_PRINTcentrums);
          break;
        case 'd':
          qh_option(qh, "Fd-cdd-in", NULL, NULL);
          qh->CDDinput= True;
          break;
        case 'D':
          qh_option(qh, "FD-cdd-out", NULL, NULL);
          qh->CDDoutput= True;
          break;
        case 'F':
          qh_option(qh, "FFacets-xridge", NULL, NULL);
          qh_appendprint(qh, qh_PRINTfacets_xridge);
          break;
        case 'i':
          qh_option(qh, "Finner", NULL, NULL);
          qh_appendprint(qh, qh_PRINTinner);
          break;
        case 'I':
          qh_option(qh, "FIDs", NULL, NULL);
          qh_appendprint(qh, qh_PRINTids);
          break;
        case 'm':
          qh_option(qh, "Fmerges", NULL, NULL);
          qh_appendprint(qh, qh_PRINTmerges);
          break;
        case 'M':
          qh_option(qh, "FMaple", NULL, NULL);
          qh_appendprint(qh, qh_PRINTmaple);
          break;
        case 'n':
          qh_option(qh, "Fneighbors", NULL, NULL);
          qh_appendprint(qh, qh_PRINTneighbors);
          break;
        case 'N':
          qh_option(qh, "FNeighbors-vertex", NULL, NULL);
          qh_appendprint(qh, qh_PRINTvneighbors);
          break;
        case 'o':
          qh_option(qh, "Fouter", NULL, NULL);
          qh_appendprint(qh, qh_PRINTouter);
          break;
        case 'O':
          if (qh->PRINToptions1st) {
            qh_option(qh, "FOptions", NULL, NULL);
            qh_appendprint(qh, qh_PRINToptions);
          }else
            qh->PRINToptions1st= True;
          break;
        case 'p':
          qh_option(qh, "Fpoint-intersect", NULL, NULL);
          qh_appendprint(qh, qh_PRINTpointintersect);
          break;
        case 'P':
          qh_option(qh, "FPoint-nearest", NULL, NULL);
          qh_appendprint(qh, qh_PRINTpointnearest);
          break;
        case 'Q':
          qh_option(qh, "FQhull", NULL, NULL);
          qh_appendprint(qh, qh_PRINTqhull);
          break;
        case 's':
          qh_option(qh, "Fsummary", NULL, NULL);
          qh_appendprint(qh, qh_PRINTsummary);
          break;
        case 'S':
          qh_option(qh, "FSize", NULL, NULL);
          qh_appendprint(qh, qh_PRINTsize);
          qh->GETarea= True;
          break;
        case 't':
          qh_option(qh, "Ftriangles", NULL, NULL);
          qh_appendprint(qh, qh_PRINTtriangles);
          break;
        case 'v':
          /* option set in qh_initqhull_globals */
          qh_appendprint(qh, qh_PRINTvertices);
          break;
        case 'V':
          qh_option(qh, "FVertex-average", NULL, NULL);
          qh_appendprint(qh, qh_PRINTaverage);
          break;
        case 'x':
          qh_option(qh, "Fxtremes", NULL, NULL);
          qh_appendprint(qh, qh_PRINTextremes);
          break;
        default:
          s--;
          qh_fprintf(qh, qh->ferr, 7012, "qhull 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, qh_PRINTgeom);
      while (*s && !isspace(*s)) {
        switch (*s++) {
        case 'a':
          qh_option(qh, "Gall-points", NULL, NULL);
          qh->PRINTdots= True;
          break;
        case 'c':
          qh_option(qh, "Gcentrums", NULL, NULL);
          qh->PRINTcentrums= True;
          break;
        case 'h':
          qh_option(qh, "Gintersections", NULL, NULL);
          qh->DOintersections= True;
          break;
        case 'i':
          qh_option(qh, "Ginner", NULL, NULL);
          qh->PRINTinner= True;
          break;
        case 'n':
          qh_option(qh, "Gno-planes", NULL, NULL);
          qh->PRINTnoplanes= True;
          break;
        case 'o':
          qh_option(qh, "Gouter", NULL, NULL);
          qh->PRINTouter= True;
          break;
        case 'p':
          qh_option(qh, "Gpoints", NULL, NULL);
          qh->PRINTcoplanar= True;
          break;
        case 'r':
          qh_option(qh, "Gridges", NULL, NULL);
          qh->PRINTridges= True;
          break;
        case 't':
          qh_option(qh, "Gtransparent", NULL, NULL);
          qh->PRINTtransparent= True;
          break;
        case 'v':
          qh_option(qh, "Gvertices", NULL, NULL);
          qh->PRINTspheres= True;
          break;
        case 'D':
          if (!isdigit(*s))
            qh_fprintf(qh, qh->ferr, 6035, "qhull input error: missing dimension for option 'GDn'\n");
          else {
            if (qh->DROPdim >= 0)
              qh_fprintf(qh, 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(qh, "GDrop-dim", &qh->DROPdim, NULL);
          }
          break;
        default:
          s--;
          qh_fprintf(qh, 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(qh, "Pdrop-facets-dim-less", &i, &r);
          else
            qh_option(qh, "PDrop-facets-dim-more", &i, &r);
          break;
        case 'g':
          qh_option(qh, "Pgood-facets", NULL, NULL);
          qh->PRINTgood= True;
          break;
        case 'G':
          qh_option(qh, "PGood-facet-neighbors", NULL, NULL);
          qh->PRINTneighbors= True;
          break;
        case 'o':
          qh_option(qh, "Poutput-forced", NULL, NULL);
          qh->FORCEoutput= True;
          break;
        case 'p':
          qh_option(qh, "Pprecision-ignore", NULL, NULL);
          qh->PRINTprecision= False;
          break;
        case 'A':
          if (!isdigit(*s))
            qh_fprintf(qh, qh->ferr, 6036, "qhull input error: missing facet count for keep area option 'PAn'\n");
          else {
            qh->KEEParea= qh_strtol(s, &s);
            qh_option(qh, "PArea-keep", &qh->KEEParea, NULL);
            qh->GETarea= True;
          }
          break;
        case 'F':
          if (!isdigit(*s))
            qh_fprintf(qh, qh->ferr, 6037, "qhull input error: missing facet area for option 'PFn'\n");
          else {
            qh->KEEPminArea= qh_strtod(s, &s);
            qh_option(qh, "PFacet-area-keep", NULL, &qh->KEEPminArea);
            qh->GETarea= True;
          }
          break;
        case 'M':
          if (!isdigit(*s))
            qh_fprintf(qh, qh->ferr, 6038, "qhull input error: missing merge count for option 'PMn'\n");
          else {
            qh->KEEPmerge= qh_strtol(s, &s);
            qh_option(qh, "PMerge-keep", &qh->KEEPmerge, NULL);
          }
          break;
        default:
          s--;
          qh_fprintf(qh, qh->ferr, 7015, "qhull 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(qh, "QbBound-unit-box", NULL, &r);
            break;
          }
          if (key == 'b' && *s == 'b') {
            s++;
            qh->SCALElast= True;
            qh_option(qh, "Qbbound-last", NULL, NULL);
            break;
          }
          k= qh_strtol(s, &s);
          r= 0.0;
          wasproject= False;
          if (*s == ':') {
            s++;
            if ((r= qh_strtod(s, &s)) == 0.0) {
              t= s;            /* need true dimension for memory allocation */
              while (*t && !isspace(*t)) {
                if (toupper(*t++) == 'B'
                 && k == qh_strtol(t, &t)
                 && *t++ == ':'
                 && qh_strtod(t, &t) == 0.0) {
                  qh->PROJECTinput++;
                  trace2((qh, qh->ferr, 2004, "qh_initflags: project dimension %d\n", k));
                  qh_option(qh, "Qb-project-dim", &k, NULL);
                  wasproject= True;
                  lastproject= k;
                  break;
                }
              }
            }
          }
          if (!wasproject) {
            if (lastproject == k && r == 0.0)
              lastproject= -1;  /* doesn't catch all possible sequences */
            else if (key == 'b') {
              qh->SCALEinput= True;
              if (r == 0.0)
                r= -qh_DEFAULTbox;
              qh_option(qh, "Qbound-dim-low", &k, &r);
            }else {
              qh->SCALEinput= True;
              if (r == 0.0)
                r= qh_DEFAULTbox;
              qh_option(qh, "QBound-dim-high", &k, &r);
            }
          }
          break;
        case 'c':
          qh_option(qh, "Qcoplanar-keep", NULL, NULL);
          qh->KEEPcoplanar= True;
          break;
        case 'f':
          qh_option(qh, "Qfurthest-outside", NULL, NULL);
          qh->BESToutside= True;
          break;
        case 'g':
          qh_option(qh, "Qgood-facets-only", NULL, NULL);
          qh->ONLYgood= True;
          break;
        case 'i':
          qh_option(qh, "Qinterior-keep", NULL, NULL);
          qh->KEEPinside= True;
          break;
        case 'm':
          qh_option(qh, "Qmax-outside-only", NULL, NULL);
          qh->ONLYmax= True;
          break;
        case 'r':
          qh_option(qh, "Qrandom-outside", NULL, NULL);
          qh->RANDOMoutside= True;
          break;
        case 's':
          qh_option(qh, "Qsearch-initial-simplex", NULL, NULL);
          qh->ALLpoints= True;
          break;
        case 't':
          qh_option(qh, "Qtriangulate", NULL, NULL);
          qh->TRIangulate= True;
          break;
        case 'T':
          qh_option(qh, "QTestPoints", NULL, NULL);
          if (!isdigit(*s))
            qh_fprintf(qh, qh->ferr, 6039, "qhull input error: missing number of test points for option 'QTn'\n");
          else {
            qh->TESTpoints= qh_strtol(s, &s);
            qh_option(qh, "QTestPoints", &qh->TESTpoints, NULL);
          }
          break;
        case 'u':
          qh_option(qh, "QupperDelaunay", NULL, NULL);
          qh->UPPERdelaunay= True;
          break;
        case 'v':
          qh_option(qh, "Qvertex-neighbors-convex", NULL, NULL);
          qh->TESTvneighbors= True;
          break;
        case 'x':
          qh_option(qh, "Qxact-merge", NULL, NULL);
          qh->MERGEexact= True;
          break;
        case 'z':
          qh_option(qh, "Qz-infinity-point", NULL, NULL);
          qh->ATinfinity= True;
          break;
        case '0':
          qh_option(qh, "Q0-no-premerge", NULL, NULL);
          qh->NOpremerge= True;
          break;
        case '1':
          if (!isdigit(*s)) {
            qh_option(qh, "Q1-no-angle-sort", NULL, NULL);
            qh->ANGLEmerge= False;
            break;
          }
          switch (*s++) {
          case '0':
            qh_option(qh, "Q10-no-narrow", NULL, NULL);
            qh->NOnarrow= True;
            break;
          case '1':
            qh_option(qh, "Q11-trinormals Qtriangulate", NULL, NULL);
            qh->TRInormals= True;
            qh->TRIangulate= True;
            break;
          case '2':
              qh_option(qh, "Q12-no-wide-dup", NULL, NULL);
              qh->NOwide= True;
            break;
          default:
            s--;
            qh_fprintf(qh, 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(qh, "Q2-no-merge-independent", NULL, NULL);
          qh->MERGEindependent= False;
          goto LABELcheckdigit;
          break; /* no warnings */
        case '3':
          qh_option(qh, "Q3-no-merge-vertices", NULL, NULL);
          qh->MERGEvertices= False;
        LABELcheckdigit:
          if (isdigit(*s))
            qh_fprintf(qh, qh->ferr, 7017, "qhull warning: can not follow '1', '2', or '3' with a digit.  '%c' skipped.\n",
                     *s++);
          break;
        case '4':
          qh_option(qh, "Q4-avoid-old-into-new", NULL, NULL);
          qh->AVOIDold= True;
          break;
        case '5':
          qh_option(qh, "Q5-no-check-outer", NULL, NULL);
          qh->SKIPcheckmax= True;
          break;
        case '6':
          qh_option(qh, "Q6-no-concave-merge", NULL, NULL);
          qh->SKIPconvex= True;
          break;
        case '7':
          qh_option(qh, "Q7-no-breadth-first", NULL, NULL);
          qh->VIRTUALmemory= True;
          break;
        case '8':
          qh_option(qh, "Q8-no-near-inside", NULL, NULL);
          qh->NOnearinside= True;
          break;
        case '9':
          qh_option(qh, "Q9-pick-furthest", NULL, NULL);
          qh->PICKfurthest= True;
          break;
        case 'G':
          i= qh_strtol(s, &t);
          if (qh->GOODpoint)
            qh_fprintf(qh, qh->ferr, 7018, "qhull warning: good point already defined for option 'QGn'.  Ignored\n");
          else if (s == t)
            qh_fprintf(qh, 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(qh, "QGood-if-dont-see-point", &i, NULL);
          }else {
            qh->GOODpoint= i+1;
            qh_option(qh, "QGood-if-see-point", &i, NULL);
          }
          s= t;
          break;
        case 'J':
          if (!isdigit(*s) && *s != '-')
            qh->JOGGLEmax= 0.0;
          else {
            qh->JOGGLEmax= (realT) qh_strtod(s, &s);
            qh_option(qh, "QJoggle", NULL, &qh->JOGGLEmax);
          }
          break;
        case 'R':
          if (!isdigit(*s) && *s != '-')
            qh_fprintf(qh, qh->ferr, 7020, "qhull warning: missing random seed for option 'QRn'.  Ignored\n");
          else {
            qh->ROTATErandom= i= qh_strtol(s, &s);
            if (i > 0)
              qh_option(qh, "QRotate-id", &i, NULL );
            else if (i < -1)
              qh_option(qh, "QRandom-seed", &i, NULL );
          }
          break;
        case 'V':
          i= qh_strtol(s, &t);
          if (qh->GOODvertex)
            qh_fprintf(qh, qh->ferr, 7021, "qhull warning: good vertex already defined for option 'QVn'.  Ignored\n");
          else if (s == t)
            qh_fprintf(qh, 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(qh, "QV-good-facets-not-point", &i, NULL);
          }else {
            qh_option(qh, "QV-good-facets-point", &i, NULL);
            qh->GOODvertex= i + 1;
          }
          s= t;
          break;
        default:
          s--;
          qh_fprintf(qh, 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(qh, "Tannotate-output", NULL, NULL);
          qh->ANNOTATEoutput= True;
          break;
        case 'c':
          qh_option(qh, "Tcheck-frequently", NULL, NULL);
          qh->CHECKfrequently= True;
          break;
        case 's':
          qh_option(qh, "Tstatistics", NULL, NULL);
          qh->PRINTstatistics= True;
          break;
        case 'v':
          qh_option(qh, "Tverify", NULL, NULL);
          qh->VERIFYoutput= True;
          break;
        case 'z':
          if (qh->ferr == qh_FILEstderr) {
            /* The C++ interface captures the output in qh_fprint_qhull() */
            qh_option(qh, "Tz-stdout", NULL, NULL);
            qh->USEstdout= True;
          }else if (!qh->fout)
            qh_fprintf(qh, qh->ferr, 7024, "qhull warning: output file undefined(stdout).  Option 'Tz' ignored.\n");
          else {
            qh_option(qh, "Tz-stdout", NULL, NULL);
            qh->USEstdout= True;
            qh->ferr= qh->fout;
            qh->qhmem.ferr= qh->fout;
          }
          break;
        case 'C':
          if (!isdigit(*s))
            qh_fprintf(qh, qh->ferr, 7025, "qhull warning: missing point id for cone for trace option 'TCn'.  Ignored\n");
          else {
            i= qh_strtol(s, &s);
            qh_option(qh, "TCone-stop", &i, NULL);
            qh->STOPcone= i + 1;
          }
          break;
        case 'F':
          if (!isdigit(*s))
            qh_fprintf(qh, qh->ferr, 7026, "qhull warning: missing frequency count for trace option 'TFn'.  Ignored\n");
          else {
            qh->REPORTfreq= qh_strtol(s, &s);
            qh_option(qh, "TFacet-log", &qh->REPORTfreq, NULL);
            qh->REPORTfreq2= qh->REPORTfreq/2;  /* for tracemerging() */
          }
          break;
        case 'I':
          if (!isspace(*s))
            qh_fprintf(qh, qh->ferr, 7027, "qhull warning: missing space between 'TI' and filename, %s\n", s);
          while (isspace(*s))
            s++;
          t= qh_skipfilename(qh, s);
          {
            char filename[qh_FILENAMElen];

            qh_copyfilename(qh, filename, (int)sizeof(filename), s, (int)(t-s));   /* WARN64 */
            s= t;
            if (!freopen(filename, "r", stdin)) {
              qh_fprintf(qh, qh->ferr, 6041, "qhull error: could not open file \"%s\".", filename);
              qh_errexit(qh, qh_ERRinput, NULL, NULL);
            }else {
              qh_option(qh, "TInput-file", NULL, NULL);
              qh_option(qh, filename, NULL, NULL);
            }
          }
          break;
        case 'O':
            if (!isspace(*s))
                qh_fprintf(qh, qh->ferr, 7028, "qhull warning: missing space between 'TO' and filename, %s\n", s);
            while (isspace(*s))
                s++;
            t= qh_skipfilename(qh, s);
            {
              char filename[qh_FILENAMElen];

              qh_copyfilename(qh, filename, (int)sizeof(filename), s, (int)(t-s));  /* WARN64 */
              s= t;
              if (!qh->fout) {
                qh_fprintf(qh, 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, qh->ferr, 6044, "qhull error: could not open file \"%s\".", filename);
                qh_errexit(qh, qh_ERRinput, NULL, NULL);
              }else {
                qh_option(qh, "TOutput-file", NULL, NULL);
              qh_option(qh, filename, NULL, NULL);
            }
          }
          break;
        case 'P':
          if (!isdigit(*s))
            qh_fprintf(qh, qh->ferr, 7029, "qhull warning: missing point id for trace option 'TPn'.  Ignored\n");
          else {
            qh->TRACEpoint= qh_strtol(s, &s);
            qh_option(qh, "Trace-point", &qh->TRACEpoint, NULL);
          }
          break;
        case 'M':
          if (!isdigit(*s))
            qh_fprintf(qh, qh->ferr, 7030, "qhull warning: missing merge id for trace option 'TMn'.  Ignored\n");
          else {
            qh->TRACEmerge= qh_strtol(s, &s);
            qh_option(qh, "Trace-merge", &qh->TRACEmerge, NULL);
          }
          break;
        case 'R':
          if (!isdigit(*s))
            qh_fprintf(qh, qh->ferr, 7031, "qhull warning: missing rerun count for trace option 'TRn'.  Ignored\n");
          else {
            qh->RERUN= qh_strtol(s, &s);
            qh_option(qh, "TRerun", &qh->RERUN, NULL);
          }
          break;
        case 'V':
          i= qh_strtol(s, &t);
          if (s == t)
            qh_fprintf(qh, qh->ferr, 7032, "qhull warning: missing furthest point id for trace option 'TVn'.  Ignored\n");
          else if (i < 0) {
            qh->STOPpoint= i - 1;
            qh_option(qh, "TV-stop-before-point", &i, NULL);
          }else {
            qh->STOPpoint= i + 1;
            qh_option(qh, "TV-stop-after-point", &i, NULL);
          }
          s= t;
          break;
        case 'W':
          if (!isdigit(*s))
            qh_fprintf(qh, qh->ferr, 7033, "qhull warning: missing max width for trace option 'TWn'.  Ignored\n");
          else {
            qh->TRACEdist= (realT) qh_strtod(s, &s);
            qh_option(qh, "TWide-trace", NULL, &qh->TRACEdist);
          }
          break;
        default:
          s--;
          qh_fprintf(qh, qh->ferr, 7034, "qhull warning: unknown 'T' trace option %c, rest ignored\n", (int)s[0]);
          while (*++s && !isspace(*s));
          break;
        }
      }
      break;
    default:
      qh_fprintf(qh, 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, 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, qh->ferr, 7078, "qhull warning: 'TCn' (stopCone) ignored when used with 'QJn' (joggle)\n");
  if (isgeom && !qh->FORCEoutput && qh->PRINTout[1])
    qh_fprintf(qh, qh->ferr, 7037, "qhull warning: additional output formats are not compatible with Geomview\n");
  /* set derived values in qh_initqhull_globals */
} /* initflags */


/*---------------------------------

  qh_initqhull_buffers(qh)
    initialize global memory buffers

  notes:
    must match qh_freebuffers()
*/
void qh_initqhull_buffers(qhT *qh) {
  int k;

  qh->TEMPsize= (qh->qhmem.LASTsize - sizeof(setT))/SETelemsize;
  if (qh->TEMPsize <= 0 || qh->TEMPsize > qh->qhmem.LASTsize)
    qh->TEMPsize= 8;  /* e.g., if qh_NOmem */
  qh->other_points= qh_setnew(qh, qh->TEMPsize);
  qh->del_vertices= qh_setnew(qh, qh->TEMPsize);
  qh->coplanarfacetset= qh_setnew(qh, qh->TEMPsize);
  qh->NEARzero= (realT *)qh_memalloc(qh, qh->hull_dim * sizeof(realT));
  qh->lower_threshold= (realT *)qh_memalloc(qh, (qh->input_dim+1) * sizeof(realT));
  qh->upper_threshold= (realT *)qh_memalloc(qh, (qh->input_dim+1) * sizeof(realT));
  qh->lower_bound= (realT *)qh_memalloc(qh, (qh->input_dim+1) * sizeof(realT));
  qh->upper_bound= (realT *)qh_memalloc(qh, (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, (qh->hull_dim+1) * qh->hull_dim * sizeof(coordT));
  qh->gm_row= (coordT **)qh_memalloc(qh, (qh->hull_dim+1) * sizeof(coordT *));
} /* initqhull_buffers */

/*---------------------------------

  qh_initqhull_globals(qh, points, numpoints, dim, ismalloc )
    initialize globals
    if ismalloc
      points were malloc'd and qhull should free at end

  returns:
    sets qh.first_point, num_points, input_dim, hull_dim and others
    seeds random number generator (seed=1 if tracing)
    modifies qh.hull_dim if ((qh.DELAUNAY and qh.PROJECTdelaunay) or qh.PROJECTinput)
    adjust user flags as needed
    also checks DIM3 dependencies and constants

  notes:
    do not use qh_point() since an input transformation may move them elsewhere

  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(qhT *qh, coordT *points, int numpoints, int dim, boolT ismalloc) {
  int seed, pointsneeded, extra= 0, i, randi, k;
  realT randr;
  realT factorial;

  time_t timedata;

  trace0((qh, qh->ferr, 13, "qh_initqhull_globals: for %s | %s\n", qh->rbox_command,
      qh->qhull_command));
  qh->POINTSmalloc= ismalloc;
  qh->first_point= points;
  qh->num_points= numpoints;
  qh->hull_dim= qh->input_dim= dim;
  if (!qh->NOpremerge && !qh->MERGEexact && !qh->PREmerge && qh->JOGGLEmax > REALmax/2) {
    qh->MERGING= True;
    if (qh->hull_dim <= 4) {
      qh->PREmerge= True;
      qh_option(qh, "_pre-merge", NULL, NULL);
    }else {
      qh->MERGEexact= True;
      qh_option(qh, "Qxact_merge", NULL, NULL);
    }
  }else if (qh->MERGEexact)
    qh->MERGING= True;
  if (!qh->NOpremerge && qh->JOGGLEmax > REALmax/2) {
#ifdef qh_NOmerge
    qh->JOGGLEmax= 0.0;
#endif
  }
  if (qh->TRIangulate && qh->JOGGLEmax < REALmax/2 && qh->PRINTprecision)
    qh_fprintf(qh, 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(qh, "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(qh, "_zero-centrum", NULL, NULL);
  }
  if (qh->JOGGLEmax < REALmax/2 && REALepsilon > 2e-8 && qh->PRINTprecision)
    qh_fprintf(qh, qh->ferr, 7039, "qhull warning: real epsilon, %2.2g, is probably too large for joggle('QJn')\nRecompile with double precision reals(see user.h).\n",
          REALepsilon);
#ifdef qh_NOmerge
  if (qh->MERGING) {
    qh_fprintf(qh, qh->ferr, 6045, "qhull input error: merging not installed(qh_NOmerge + 'Qx', 'Cn' or 'An')\n");
    qh_errexit(qh, qh_ERRinput, NULL, NULL);
  }
#endif
  if (qh->DELAUNAY && qh->KEEPcoplanar && !qh->KEEPinside) {
    qh->KEEPinside= True;
    qh_option(qh, "Qinterior-keep", NULL, NULL);
  }
  if (qh->DELAUNAY && qh->HALFspace) {
    qh_fprintf(qh, qh->ferr, 6046, "qhull input error: can not use Delaunay('d') or Voronoi('v') with halfspace intersection('H')\n");
    qh_errexit(qh, qh_ERRinput, NULL, NULL);
  }
  if (!qh->DELAUNAY && (qh->UPPERdelaunay || qh->ATinfinity)) {
    qh_fprintf(qh, qh->ferr, 6047, "qhull input error: use upper-Delaunay('Qu') or infinity-point('Qz') with Delaunay('d') or Voronoi('v')\n");
    qh_errexit(qh, qh_ERRinput, NULL, NULL);
  }
  if (qh->UPPERdelaunay && qh->ATinfinity) {
    qh_fprintf(qh, qh->ferr, 6048, "qhull input error: can not use infinity-point('Qz') with upper-Delaunay('Qu')\n");
    qh_errexit(qh, qh_ERRinput, NULL, NULL);
  }
  if (qh->SCALElast && !qh->DELAUNAY && qh->PRINTprecision)
    qh_fprintf(qh, 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, qh->ferr, 6049, "qhull input error: test vertex neighbors('Qv') needs a merge option\n");
    qh_errexit(qh, qh_ERRinput, NULL ,NULL);
  }
  if (qh->PROJECTinput || (qh->DELAUNAY && qh->PROJECTdelaunay)) {
    qh->hull_dim -= qh->PROJECTinput;
    if (qh->DELAUNAY) {
      qh->hull_dim++;
      if (qh->ATinfinity)
        extra= 1;
    }
  }
  if (qh->hull_dim <= 1) {
    qh_fprintf(qh, qh->ferr, 6050, "qhull error: dimension %d must be > 1\n", qh->hull_dim);
    qh_errexit(qh, qh_ERRinput, NULL, NULL);
  }
  for (k=2, factorial=1.0; k < qh->hull_dim; k++)
    factorial *= k;
  qh->AREAfactor= 1.0 / factorial;
  trace2((qh, qh->ferr, 2005, "qh_initqhull_globals: initialize globals.  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(qh, "Q3-no-merge-vertices-dim-high", NULL, NULL);
  }
  if (qh->GOODpoint)
    pointsneeded++;
#ifdef qh_NOtrace
  if (qh->IStracing) {
    qh_fprintf(qh, qh->ferr, 6051, "qhull input error: tracing is not installed(qh_NOtrace in user.h)");
    qh_errexit(qh, 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(qh, "QRandom-seed", &seed, NULL );
    }else
      qh_option(qh, "QRotate-random", &seed, NULL);
    qh->ROTATErandom= seed;
  }
  seed= qh->ROTATErandom;
  if (seed == INT_MIN)    /* default value */
    seed= 1;
  else if (seed < 0)
    seed= -seed;
  qh_RANDOMseed_(qh, seed);
  randr= 0.0;
  for (i=1000; i--; ) {
    randi= qh_RANDOMint;
    randr += randi;
    if (randi > qh_RANDOMmax) {
      qh_fprintf(qh, qh->ferr, 8036, "\
qhull configuration error (qh_RANDOMmax in user.h):\n\
   random integer %d > qh_RANDOMmax(qh, %.8g)\n",
               randi, qh_RANDOMmax);
      qh_errexit(qh, qh_ERRinput, NULL, NULL);
    }
  }
  qh_RANDOMseed_(qh, seed);
  randr = randr/1000;
  if (randr < qh_RANDOMmax * 0.1
  || randr > qh_RANDOMmax * 0.9)
    qh_fprintf(qh, qh->ferr, 8037, "\
qhull configuration warning (qh_RANDOMmax in user.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, qh->ferr, 6052, "qhull internal error (qh_initqhull_globals): qh_HASHfactor %d must be at least 1.1.  Qhull uses linear hash probing\n",
      qh_HASHfactor);
    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
  }
  if (numpoints+extra < pointsneeded) {
    qh_fprintf(qh, qh->ferr, 6214, "qhull input error: not enough points(%d) to construct initial simplex (need %d)\n",
            numpoints, pointsneeded);
    qh_errexit(qh, qh_ERRinput, NULL, NULL);
  }
  qh_initqhull_outputflags(qh);
} /* initqhull_globals */

/*---------------------------------

  qh_initqhull_mem(qh, )
    initialize mem_r.c for qhull
    qh.hull_dim and qh.normal_size determine some of the allocation sizes
    if qh.MERGING,
      includes ridgeT
    calls qh_user_memsizes(qh) to add up to 10 additional sizes for quick allocation
      (see numsizes below)

  returns:
    mem_r.c already for qh_memalloc/qh_memfree (errors if called beforehand)

  notes:
    qh_produceoutput() prints memsizes

*/
void qh_initqhull_mem(qhT *qh) {
  int numsizes;
  int i;

  numsizes= 8+10;
  qh_meminitbuffers(qh, qh->IStracing, qh_MEMalign, numsizes,
                     qh_MEMbufsize, qh_MEMinitbuf);
  qh_memsize(qh, (int)sizeof(vertexT));
  if (qh->MERGING) {
    qh_memsize(qh, (int)sizeof(ridgeT));
    qh_memsize(qh, (int)sizeof(mergeT));
  }
  qh_memsize(qh, (int)sizeof(facetT));
  i= sizeof(setT) + (qh->hull_dim - 1) * SETelemsize;  /* ridge.vertices */
  qh_memsize(qh, i);
  qh_memsize(qh, qh->normal_size);        /* normal */
  i += SETelemsize;                 /* facet.vertices, .ridges, .neighbors */
  qh_memsize(qh, i);
  qh_user_memsizes(qh);
  qh_memsetup(qh);
} /* initqhull_mem */

/*---------------------------------

  qh_initqhull_outputflags
    initialize flags concerned with output

  returns:
    adjust user flags as needed

  see:
    qh_clear_outputflags() resets the flags

  design:
    test for qh.PRINTgood (i.e., only print 'good' facets)
    check for conflicting print output options
*/
void qh_initqhull_outputflags(qhT *qh) {
  boolT printgeom= False, printmath= False, printcoplanar= False;
  int i;

  trace3((qh, qh->ferr, 3024, "qh_initqhull_outputflags: %s\n", qh->qhull_command));
  if (!(qh->PRINTgood || qh->PRINTneighbors)) {
    if (qh->KEEParea || qh->KEEPminArea < REALmax/2 || qh->KEEPmerge || qh->DELAUNAY
        || (!qh->ONLYgood && (qh->GOODvertex || qh->GOODpoint))) {
      qh->PRINTgood= True;
      qh_option(qh, "Pgood", NULL, NULL);
    }
  }
  if (qh->PRINTtransparent) {
    if (qh->hull_dim != 4 || !qh->DELAUNAY || qh->VORONOI || qh->DROPdim >= 0) {
      qh_fprintf(qh, qh->ferr, 6215, "qhull input error: transparent Delaunay('Gt') needs 3-d Delaunay('d') w/o 'GDn'\n");
      qh_errexit(qh, qh_ERRinput, NULL, NULL);
    }
    qh->DROPdim = 3;
    qh->PRINTridges = True;
  }
  for (i=qh_PRINTEND; i--; ) {
    if (qh->PRINTout[i] == qh_PRINTgeom)
      printgeom= True;
    else if (qh->PRINTout[i] == qh_PRINTmathematica || qh->PRINTout[i] == qh_PRINTmaple)
      printmath= True;
    else if (qh->PRINTout[i] == qh_PRINTcoplanars)
      printcoplanar= True;
    else if (qh->PRINTout[i] == qh_PRINTpointnearest)
      printcoplanar= True;
    else if (qh->PRINTout[i] == qh_PRINTpointintersect && !qh->HALFspace) {
      qh_fprintf(qh, qh->ferr, 6053, "qhull input error: option 'Fp' is only used for \nhalfspace intersection('Hn,n,n').\n");
      qh_errexit(qh, qh_ERRinput, NULL, NULL);
    }else if (qh->PRINTout[i] == qh_PRINTtriangles && (qh->HALFspace || qh->VORONOI)) {
      qh_fprintf(qh, qh->ferr, 6054, "qhull input error: option 'Ft' is not available for Voronoi vertices or halfspace intersection\n");
      qh_errexit(qh, qh_ERRinput, NULL, NULL);
    }else if (qh->PRINTout[i] == qh_PRINTcentrums && qh->VORONOI) {
      qh_fprintf(qh, qh->ferr, 6055, "qhull input error: option 'FC' is not available for Voronoi vertices('v')\n");
      qh_errexit(qh, qh_ERRinput, NULL, NULL);
    }else if (qh->PRINTout[i] == qh_PRINTvertices) {
      if (qh->VORONOI)
        qh_option(qh, "Fvoronoi", NULL, NULL);
      else
        qh_option(qh, "Fvertices", NULL, NULL);
    }
  }
  if (printcoplanar && qh->DELAUNAY && qh->JOGGLEmax < REALmax/2) {
    if (qh->PRINTprecision)
      qh_fprintf(qh, qh->ferr, 7041, "qhull 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, 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, qh_ERRinput, NULL, NULL);
  }
  if (printgeom) {
    if (qh->hull_dim > 4) {
      qh_fprintf(qh, qh->ferr, 6057, "qhull input error: Geomview output is only available for 2-d, 3-d and 4-d\n");
      qh_errexit(qh, qh_ERRinput, NULL, NULL);
    }
    if (qh->PRINTnoplanes && !(qh->PRINTcoplanar + qh->PRINTcentrums
     + qh->PRINTdots + qh->PRINTspheres + qh->DOintersections + qh->PRINTridges)) {
      qh_fprintf(qh, qh->ferr, 6058, "qhull input error: no output specified for Geomview\n");
      qh_errexit(qh, qh_ERRinput, NULL, NULL);
    }
    if (qh->VORONOI && (qh->hull_dim > 3 || qh->DROPdim >= 0)) {
      qh_fprintf(qh, qh->ferr, 6059, "qhull input error: Geomview output for Voronoi diagrams only for 2-d\n");
      qh_errexit(qh, qh_ERRinput, NULL, NULL);
    }
    /* can not warn about furthest-site Geomview output: no lower_threshold */
    if (qh->hull_dim == 4 && qh->DROPdim == -1 &&
        (qh->PRINTcoplanar || qh->PRINTspheres || qh->PRINTcentrums)) {
      qh_fprintf(qh, qh->ferr, 7042, "qhull 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, qh->ferr, 7072, "qhull output warning: ignoring coplanar points, option 'Qc' was not set for the first run of qhull.\n");
      }else {
        qh->KEEPcoplanar = True;
        qh_option(qh, "Qcoplanar", NULL, NULL);
      }
    }
  }
  qh->PRINTdim= qh->hull_dim;
  if (qh->DROPdim >=0) {    /* after Geomview checks */
    if (qh->DROPdim < qh->hull_dim) {
      qh->PRINTdim--;
      if (!printgeom || qh->hull_dim < 3)
        qh_fprintf(qh, qh->ferr, 7043, "qhull input warning: drop dimension 'GD%d' is only available for 3-d/4-d Geomview\n", qh->DROPdim);
    }else
      qh->DROPdim= -1;
  }else if (qh->VORONOI) {
    qh->DROPdim= qh->hull_dim-1;
    qh->PRINTdim= qh->hull_dim-1;
  }
} /* qh_initqhull_outputflags */

/*---------------------------------

  qh_initqhull_start(qh, infile, outfile, errfile )
    allocate memory if needed and call qh_initqhull_start2()
*/
void qh_initqhull_start(qhT *qh, FILE *infile, FILE *outfile, FILE *errfile) {

  qh_initstatistics(qh);
  qh_initqhull_start2(qh, infile, outfile, errfile);
} /* initqhull_start */

/*---------------------------------

  qh_initqhull_start2(qh, infile, outfile, errfile )
    start initialization of qhull
    initialize statistics, stdio, default values for global variables
    assumes qh is allocated
  notes:
    report errors elsewhere, error handling and g_qhull_output [Qhull.cpp, QhullQh()] not in initialized
  see:
    qh_maxmin() determines the precision constants
    qh_freeqhull()
*/
void qh_initqhull_start2(qhT *qh, FILE *infile, FILE *outfile, FILE *errfile) {
  time_t timedata;
  int seed;

  qh_CPUclock; /* start the clock(for qh_clock).  One-shot. */
  /* memset is the same in qh_freeqhull() and qh_initqhull_start2() */
  memset((char *)qh, 0, sizeof(qhT)-sizeof(qhmemT)-sizeof(qhstatT));   /* every field is 0, FALSE, NULL */
  qh->NOerrexit= True;
  qh->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->last_random= 1;
  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_(qh, seed);
  qh->run_id= qh_RANDOMint;
  if(!qh->run_id)
      qh->run_id++;  /* guarantee non-zero */
  qh_option(qh, "run-id", &qh->run_id, NULL);
  strcat(qh->qhull, "qhull");
} /* initqhull_start2 */

/*---------------------------------

  qh_initthresholds(qh, commandString )
    set thresholds for printing and scaling from commandString

  returns:
    sets qh.GOODthreshold or qh.SPLITthreshold if 'Pd0D1' used

  see:
    qh_initflags(), 'Qbk' 'QBk' 'Pdk' and 'PDk'
    qh_inthresholds()

  design:
    for each 'Pdn' or 'PDn' option
      check syntax
      set qh.lower_threshold or qh.upper_threshold
    set qh.GOODthreshold if an unbounded threshold is used
    set qh.SPLITthreshold if a bounded threshold is used
*/
void qh_initthresholds(qhT *qh, char *command) {
  realT value;
  int idx, maxdim, k;
  char *s= command; /* non-const due to strtol */
  char 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, 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, 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, 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, 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, qh->ferr, 7048, "qhull warning: dimension %d for Qhull option %c is >= %d.  Ignored\n",
                idx, key, maxdim);
            continue;
          }
          if (*s == ':') {
            s++;
            value= qh_strtod(s, &s);
          }else if (key == 'b')
            value= -qh_DEFAULTbox;
          else
            value= qh_DEFAULTbox;
          if (key == 'b')
            qh->lower_bound[idx]= value;
          else
            qh->upper_bound[idx]= value;
        }
      }
    }else {
      while (*s && !isspace(*s))
        s++;
    }
    while (isspace(*s))
      s++;
  }
  for (k=qh->hull_dim; k--; ) {
    if (qh->lower_threshold[k] > -REALmax/2) {
      qh->GOODthreshold= True;
      if (qh->upper_threshold[k] < REALmax/2) {
        qh->SPLITthresholds= True;
        qh->GOODthreshold= False;
        break;
      }
    }else if (qh->upper_threshold[k] < REALmax/2)
      qh->GOODthreshold= True;
  }
} /* initthresholds */

/*---------------------------------

  qh_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 */
        qh_fprintf_stderr(6257, "qh_lib_check: Incorrect qhull library called.  Caller uses non-reentrant Qhull with a static qhT.  Library is reentrant.\n");
        iserror= True;
    }else if (qhullLibraryType==QHULL_QH_POINTER) { /* 1 */
        qh_fprintf_stderr(6258, "qh_lib_check: Incorrect qhull library called.  Caller uses non-reentrant Qhull with a dynamic qhT via qh_QHpointer.  Library is reentrant.\n");
        iserror= True;
    }else if (qhullLibraryType!=QHULL_REENTRANT) { /* 2 */
        qh_fprintf_stderr(6262, "qh_lib_check: Expecting qhullLibraryType QHULL_NON_REENTRANT(0), QHULL_QH_POINTER(1), or QHULL_REENTRANT(2).  Got %d\n", qhullLibraryType);
        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) {
        qh_fprintf_stderr(6259, "qh_lib_check: Cannot continue.  Library '%s' is reentrant (e.g., qhull_r.so)\n", qh_version2);
        qh_exit(qh_ERRqhull);  /* can not use qh_errexit() */
    }
} /* lib_check */

/*---------------------------------

  qh_option(qh, option, intVal, realVal )
    add an option description to qh.qhull_options

  notes:
    NOerrors -- qh_option can not call qh_errexit() [qh_initqhull_start2]
    will be printed with statistics ('Ts') and errors
    strlen(option) < 40
*/
void qh_option(qhT *qh, const char *option, int *i, realT *r) {
  char buf[200+23+12];
  int len, maxlen;

  snprintf(buf, 199, "  %s", option);
  if (i)
    snprintf(buf+strlen(buf), 22, " %d", *i);
  if (r)
    snprintf(buf+strlen(buf), 11, " %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 */

/*---------------------------------

  qh_zero( qh, errfile )
    Initialize and zero Qhull's memory for qh_new_qhull()

  notes:
    Not needed in global.c because static variables are initialized to zero
*/
void qh_zero(qhT *qh, FILE *errfile) {
    memset((char *)qh, 0, sizeof(qhT));   /* every field is 0, FALSE, NULL */
    qh->NOerrexit= True;
    qh_meminit(qh, errfile);
} /* zero */

geometry/src/Rconvhulln.c0000644000176200001440000001277713532164477015224 0ustar  liggesusers/* Copyright (C) 2000 Kai Habel
** Copyright R-version (C) 2005 Raoul Grasman 
**                     (C) 2013-2015, 2017-2019 David Sterratt
**                     (C) 2018 Pavlo Mozharovskyi
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
*/

/*
29. July 2000 - Kai Habel: first release
2002-04-22 Paul Kienzle
* Use warning(...) function rather than writing to cerr

23. May 2005 - Raoul Grasman: ported to R
* Changed the interface for R

02. February 2018 - Pavlo Mozharovskyi: added non-triangulated output
*/

#include "Rgeometry.h"

SEXP C_convhulln(const SEXP p, const SEXP options, const SEXP returnNonTriangulatedFacets, const SEXP tmp_stdout, const SEXP tmp_stderr)
{
  /* Initialise return values */
  SEXP retval, area, vol, normals, retlist, retnames;
  retval = area = vol = normals = retlist = R_NilValue;

  /* Run Qhull */
  qhT *qh= (qhT*)malloc(sizeof(qhT));
  char errstr[ERRSTRSIZE];
  unsigned int dim, n;
  char cmd[50] = "qhull";
  int exitcode = qhullNewQhull(qh, p, cmd,  options, tmp_stdout, tmp_stderr, &dim, &n, errstr);

  /* Error handling */
  if (exitcode) {
    freeQhull(qh);
    error("Received error code %d from qhull. Qhull error:\n%s", exitcode, errstr);
  }
  
  /* Extract information from output */
  int i, j, *idx;
  facetT *facet;              /* set by FORALLfacets */
  vertexT *vertex, **vertexp; /* set by FORALLfacets */
  unsigned int nf = qh->num_facets;
  unsigned int nVertexMax = 0;

  /* If parameter (flag) returnNonTriangulatedFacets is set, count the
     number of columns in the output matrix of vertices as the maximal
     number of vertices in a facet, then allocate the matrix. */
  if (INTEGER(returnNonTriangulatedFacets)[0] > 0){
    i = 0;
    FORALLfacets {
      j = 0;
      FOREACHvertex_ (facet->vertices) {
        j++;
      }
      if (j > nVertexMax){
        nVertexMax = j;
      }
    }
  } else {
    /* If parameter (flag) returnNonTriangulatedFacets is not set, the
       number of columns equals dimension. */
    nVertexMax = dim;
  }
  retval = PROTECT(allocMatrix(INTSXP, nf, nVertexMax));
  idx = (int *) R_alloc(nf*nVertexMax,sizeof(int));

  if (hasPrintOption(qh, qh_PRINTnormals)) {
    normals = PROTECT(allocMatrix(REALSXP, nf, dim+1));
  } else {
    normals = PROTECT(R_NilValue);
  }

  qh_vertexneighbors(qh);

  i = 0; /* Facet counter */
  FORALLfacets {
    j = 0;
    /* qh_printfacet(stdout,facet); */
    FOREACHvertex_ (facet->vertices) {
      /* qh_printvertex(stdout,vertex); */
      if (INTEGER(returnNonTriangulatedFacets)[0] == 0 && j >= dim)
        warning("extra vertex %d of facet %d = %d",
                j++, i, 1+qh_pointid(qh, vertex->point));
      else
        idx[i + nf*j++] = 1 + qh_pointid(qh, vertex->point);
    }
    if (j < dim) warning("facet %d only has %d vertices",i,j);
    while (j < nVertexMax){
      idx[i + nf*j++] = 0; /* Fill with zeros for the moment */
    }

    /* Output normals */
    if (hasPrintOption(qh, qh_PRINTnormals)) {
      if (facet->normal) {
        for (j=0; jnormal[j];
        }
        REAL(normals)[i + nrows(normals)*dim] = facet->offset;
      } else {
        for (j=0; j<=dim; j++) {
          REAL(normals)[i + nrows(normals)*j] = 0;
        }
      }
    }
    i++; /* Increment facet counter */
  }
  j = 0;
  for(i = 0; i 0){
        INTEGER(retval)[i + nrows(retval)*j] = idx[i + nf*j];
      } else {
        INTEGER(retval)[i + nrows(retval)*j] = NA_INTEGER;
      }

  /* Return area and volume - will be there when option "FA" is provided */
  if (qh->totarea != 0.0) {
    area = PROTECT(allocVector(REALSXP, 1));
    REAL(area)[0] = qh->totarea;
  } else {
    area = PROTECT(R_NilValue);
  }
  if (qh->totvol != 0.0) {
    vol = PROTECT(allocVector(REALSXP, 1));
    REAL(vol)[0] = qh->totvol;
  } else {
    vol = PROTECT(R_NilValue);
  }

  /* Set up output structure */
  retlist =  PROTECT(allocVector(VECSXP, 4));
  retnames = PROTECT(allocVector(VECSXP, 4));
  SET_VECTOR_ELT(retlist,  0, retval);
  SET_VECTOR_ELT(retnames, 0, mkChar("hull"));
  SET_VECTOR_ELT(retlist,  1, area);
  SET_VECTOR_ELT(retnames, 1, mkChar("area"));
  SET_VECTOR_ELT(retlist,  2, vol);
  SET_VECTOR_ELT(retnames, 2, mkChar("vol"));
  SET_VECTOR_ELT(retlist,  3, normals);
  SET_VECTOR_ELT(retnames, 3, mkChar("normals"));
  setAttrib(retlist, R_NamesSymbol, retnames);

  /* Register qhullFinalizer() for garbage collection and attach a
     pointer to the hull as an attribute for future use. */
  SEXP ptr, tag;
  tag = PROTECT(allocVector(STRSXP, 1));
  SET_STRING_ELT(tag, 0, mkChar("convhulln"));
  ptr = PROTECT(R_MakeExternalPtr(qh, tag, R_NilValue));
  R_RegisterCFinalizerEx(ptr, qhullFinalizer, TRUE);
  setAttrib(retlist, tag, ptr);

  UNPROTECT(8); /* ptr, tag, retnames, retlist, normals, vol, area, retval */

  return retlist;
}
geometry/src/Rtsearchn.c0000644000176200001440000001147113525562746015014 0ustar  liggesusers/*

  Copyright (C) 2017 Andreas Stahel

  This program is free software; you can redistribute it and/or modify it
  under the terms of the GNU General Public License as published by the
  Free Software Foundation; either version 3 of the License, or (at your
  option) any later version.

  This program is distributed in the hope that it will be useful, but WITHOUT
  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  for more details.

  You should have received a copy of the GNU General Public License
  along with this program. If not, see
  .

*/

#include 
#include 
#include 
#include 
#include "Rgeometry.h"
#include "qhull_ra.h"

SEXP C_tsearchn(const SEXP dt, const SEXP p)
{
  int debug = 0;
  /* Get the qh object from the delaunayn object */
  SEXP ptr, tag;
  qhT *qh;
  tag = PROTECT(allocVector(STRSXP, 1));
  SET_STRING_ELT(tag, 0, mkChar("delaunayn"));
  ptr = PROTECT(getAttrib(dt, tag));
  if (ptr == R_NilValue) {
    error("Delaunay triangulation has no delaunayn attribute");
  }
  qh = R_ExternalPtrAddr(ptr);
  UNPROTECT(2);

  /* Check input matrix */
  if(!isMatrix(p) || !isReal(p)){
    error("Second argument should be a real matrix.");
  }
  unsigned int dim, n;
  dim = ncols(p) + 1;
  n   = nrows(p);
  if(dim <= 0 || n <= 0){
    error("Invalid input matrix.");
  }
  if (dim != qh->hull_dim)
    error("Invalid input matrix.");
  
  /* Construct map from facet id to index */ 
  facetT *facet;

  /* Count the number of facets so we know how much space to
     allocate in R */
  int nf = 0;                   /* Number of facets */
  int max_facet_id = 0;
  int exitcode = 0;
  FORALLfacets {
    if (!facet->upperdelaunay) {
      nf++;
      if (facet->id > max_facet_id)
        max_facet_id = facet->id;

      /* Double check. Non-simplicial facets will cause segfault
         below */
      if (!facet->simplicial) {
        Rprintf("Qhull returned non-simplicial facets -- try delaunayn with different options");
        exitcode = 1;
        break;
      }
    }
  }

  int *idmap = (int *) R_alloc(max_facet_id + 1, sizeof(int));
  int i = 0;
  FORALLfacets {
    if (!facet->upperdelaunay) {
      i++;
      if (debug & 1) Rprintf("Facet id %d; index %d\n;", facet->id, i);
       if (facet->id < 1 || facet->id > max_facet_id) {
         Rf_error("facet_id %d (at index %d) is not in {1,...,%d}", facet->id, i, max_facet_id);
       }
      idmap[facet->id] = i;
    }
  }
    
  /* Make space for output */
  SEXP retlist, retnames;       /* Return list and names */
  int retlen = 2;               /* Length of return list */
  SEXP idx, points;
  idx = PROTECT(allocVector(INTSXP, n));
  int *iidx = INTEGER(idx);
  points = PROTECT(allocMatrix(REALSXP, qh->num_points, dim - 1));

  int j, k;

  /* Output points */
  pointT *point;
  pointT *pointtemp;
  if (debug & 2) Rprintf("%d POINTS\n", qh->num_points);
  i = 0;
  FORALLpoints {
    for (k=0; k<(dim - 1); k++) {
      REAL(points)[i+k*qh->num_points] = point[k];
      if (debug & 2) Rprintf("%f ", point[k]);
    }
    i++;
    if (debug & 2) Rprintf("\n");
  }
  
  /* Run through the matrix using qh_findbestfacet to determine
     whether in hull or not */
  boolT isoutside;
  realT bestdist;
  vertexT *vertex, **vertexp;

  /* The name point is reserved for use with FORALLpoints */
  coordT *testpoint;
  testpoint = (coordT *) R_alloc(dim, sizeof(coordT));

  for(i=0; i < n; i++) {
    if (debug) Rprintf("\nTestpoint\n");
    for(k=0; k < (dim - 1); k++) {
      testpoint[k] = REAL(p)[i+n*k]; /* could have been pt_array = REAL(p) if p had been transposed */
      if (debug) Rprintf(" %f", testpoint[k]);
    }
    if (debug) Rprintf("\n");
    qh_setdelaunay(qh, dim, 1, testpoint);
    facet = qh_findbestfacet(qh, testpoint, qh_ALL, &bestdist, &isoutside);
    if (facet->tricoplanar) {
      exitcode = 1;
      break;
    }
    if (debug) Rprintf("Facet id %d; index %d\n", facet->id, idmap[facet->id]);
    /* Convert facet id to id of triangle */
    iidx[i] = idmap[facet->id];
    /* /\* Return vertices of triangle *\/ */
    j = 0;
    FOREACHvertex_ (facet->vertices) {
      for (j=0; jpoint[j]);
      }
      if (debug) Rprintf("\n");
    }

  }

  retlist = PROTECT(allocVector(VECSXP, retlen));
  retnames = PROTECT(allocVector(VECSXP, retlen));
  SET_VECTOR_ELT(retlist, 0, idx);
  SET_VECTOR_ELT(retnames, 0, mkChar("idx"));
  SET_VECTOR_ELT(retlist, 1, points);
  SET_VECTOR_ELT(retnames, 1, mkChar("P"));
  setAttrib(retlist, R_NamesSymbol, retnames);
  UNPROTECT(4);
  
  if (exitcode)
    error("findDelaunay: not implemented for triangulated, non-simplicial Delaunay regions (tricoplanar facet, f%d).", facet->id);
  
  return retlist;
}
geometry/src/libqhull_r.c0000644000176200001440000015045513432323610015204 0ustar  liggesusers/*
  ---------------------------------

   libqhull_r.c
   Quickhull algorithm for convex hulls

   qhull() and top-level routines

   see qh-qhull_r.htm, libqhull.h, unix_r.c

   see qhull_ra.h for internal functions

   Copyright (c) 1993-2015 The Geometry Center.
   $Id: //main/2015/qhull/src/libqhull_r/libqhull_r.c#2 $$Change: 2047 $
   $DateTime: 2016/01/04 22:03:18 $$Author: bbarber $
*/

#include "qhull_ra.h"

/*============= functions in alphabetic order after qhull() =======*/

/*---------------------------------

  qh_qhull(qh)
    compute DIM3 convex hull of qh.num_points starting at qh.first_point
    qh->contains all global options and variables

  returns:
    returns polyhedron
      qh.facet_list, qh.num_facets, qh.vertex_list, qh.num_vertices,

    returns global variables
      qh.hulltime, qh.max_outside, qh.interior_point, qh.max_vertex, qh.min_vertex

    returns precision constants
      qh.ANGLEround, centrum_radius, cos_max, DISTround, MAXabs_coord, ONEmerge

  notes:
    unless needed for output
      qh.max_vertex and qh.min_vertex are max/min due to merges

  see:
    to add individual points to either qh.num_points
      use qh_addpoint()

    if qh.GETarea
      qh_produceoutput() returns qh.totarea and qh.totvol via qh_getarea()

  design:
    record starting time
    initialize hull and partition points
    build convex hull
    unless early termination
      update facet->maxoutside for vertices, coplanar, and near-inside points
    error if temporary sets exist
    record end time
*/

void qh_qhull(qhT *qh) {
  int numoutside;

  qh->hulltime= qh_CPUclock;
  if (qh->RERUN || qh->JOGGLEmax < REALmax/2)
    qh_build_withrestart(qh);
  else {
    qh_initbuild(qh);
    qh_buildhull(qh);
  }
  if (!qh->STOPpoint && !qh->STOPcone) {
    if (qh->ZEROall_ok && !qh->TESTvneighbors && qh->MERGEexact)
      qh_checkzero(qh, qh_ALL);
    if (qh->ZEROall_ok && !qh->TESTvneighbors && !qh->WAScoplanar) {
      trace2((qh, qh->ferr, 2055, "qh_qhull: all facets are clearly convex and no coplanar points.  Post-merging and check of maxout not needed.\n"));
      qh->DOcheckmax= False;
    }else {
      if (qh->MERGEexact || (qh->hull_dim > qh_DIMreduceBuild && qh->PREmerge))
        qh_postmerge(qh, "First post-merge", qh->premerge_centrum, qh->premerge_cos,
             (qh->POSTmerge ? False : qh->TESTvneighbors));
      else if (!qh->POSTmerge && qh->TESTvneighbors)
        qh_postmerge(qh, "For testing vertex neighbors", qh->premerge_centrum,
             qh->premerge_cos, True);
      if (qh->POSTmerge)
        qh_postmerge(qh, "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 /*qh.visible_list*/, !qh_ALL, &numoutside);
        qh->findbestnew= False;
        qh_deletevisible(qh /*qh.visible_list*/);
        qh_resetlists(qh, False, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */);
      }
    }
    if (qh->DOcheckmax){
      if (qh->REPORTfreq) {
        qh_buildtracing(qh, NULL, NULL);
        qh_fprintf(qh, qh->ferr, 8115, "\nTesting all coplanar points.\n");
      }
      qh_check_maxout(qh);
    }
    if (qh->KEEPnearinside && !qh->maxoutdone)
      qh_nearcoplanar(qh);
  }
  if (qh_setsize(qh, qh->qhmem.tempstack) != 0) {
    qh_fprintf(qh, qh->ferr, 6164, "qhull internal error (qh_qhull): temporary sets not empty(%d)\n",
             qh_setsize(qh, qh->qhmem.tempstack));
    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
  }
  qh->hulltime= qh_CPUclock - qh->hulltime;
  qh->QHULLfinished= True;
  trace1((qh, qh->ferr, 1036, "Qhull: algorithm completed\n"));
} /* qhull */

/*---------------------------------

  qh_addpoint(qh, furthest, facet, checkdist )
    add point (usually furthest point) above facet to hull
    if checkdist,
      check that point is above facet.
      if point is not outside of the hull, uses qh_partitioncoplanar()
      assumes that facet is defined by qh_findbestfacet()
    else if facet specified,
      assumes that point is above facet (major damage if below)
    for Delaunay triangulations,
      Use qh_setdelaunay() to lift point to paraboloid and scale by 'Qbb' if needed
      Do not use options 'Qbk', 'QBk', or 'QbB' since they scale the coordinates.

  returns:
    returns False if user requested an early termination
     qh.visible_list, newfacet_list, delvertex_list, NEWfacets may be defined
    updates qh.facet_list, qh.num_facets, qh.vertex_list, qh.num_vertices
    clear qh.maxoutdone (will need to call qh_check_maxout() for facet->maxoutside)
    if unknown point, adds a pointer to qh.other_points
      do not deallocate the point's coordinates

  notes:
    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(qhT *qh, 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(qh, furthest) == qh_IDunknown)
    qh_setappend(qh, &qh->other_points, furthest);
  if (!facet) {
    qh_fprintf(qh, qh->ferr, 6213, "qhull internal error (qh_addpoint): NULL facet.  Need to call qh_findbestfacet first\n");
    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
  }
  if (checkdist) {
    facet= qh_findbest(qh, furthest, facet, !qh_ALL, !qh_ISnewfacets, !qh_NOupper,
                        &dist, &isoutside, &numpart);
    zzadd_(Zpartition, numpart);
    if (!isoutside) {
      zinc_(Znotmax);  /* last point of outsideset is no longer furthest. */
      facet->notfurthest= True;
      qh_partitioncoplanar(qh, furthest, facet, &dist);
      return True;
    }
  }
  qh_buildtracing(qh, furthest, facet);
  if (qh->STOPpoint < 0 && qh->furthest_id == -qh->STOPpoint-1) {
    facet->notfurthest= True;
    return False;
  }
  qh_findhorizon(qh, furthest, facet, &goodvisible, &goodhorizon);
  if (qh->ONLYgood && !(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(qh, False, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */);
    return True;
  }
  zzinc_(Zprocessed);
  firstnew= qh->facet_id;
  vertex= qh_makenewfacets(qh, furthest /*visible_list, attaches if !ONLYgood */);
  qh_makenewplanes(qh /* 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, qh->newfacet_list, goodhorizon) && !qh->GOODclosest) {
    FORALLnew_facets
      qh_delfacet(qh, newfacet);
    qh_delvertex(qh, vertex);
    qh_resetlists(qh, True, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */);
    zinc_(Znotgoodnew);
    facet->notfurthest= True;
    return True;
  }
  if (qh->ONLYgood)
    qh_attachnewfacets(qh /*visible_list*/);
  qh_matchnewfacets(qh);
  qh_updatevertices(qh);
  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(qh, 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 /*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 /*qh.visible_list*/);
  zmax_(Zmaxvertex, qh->num_vertices);
  qh->NEWfacets= False;
  if (qh->IStracing >= 4) {
    if (qh->num_facets < 2000)
      qh_printlists(qh);
    qh_printfacetlist(qh, qh->newfacet_list, NULL, True);
    qh_checkpolygon(qh, qh->facet_list);
  }else if (qh->CHECKfrequently) {
    if (qh->num_facets < 50)
      qh_checkpolygon(qh, qh->facet_list);
    else
      qh_checkpolygon(qh, qh->newfacet_list);
  }
  if (qh->STOPpoint > 0 && qh->furthest_id == qh->STOPpoint-1)
    return False;
  qh_resetlists(qh, True, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */);
  /* qh_triangulate(qh); to test qh.TRInormals */
  trace2((qh, qh->ferr, 2056, "qh_addpoint: added p%d new facets %d new balance %2.2g point balance %2.2g\n",
    qh_pointid(qh, furthest), numnew, newbalance, pbalance));
  return True;
} /* addpoint */

/*---------------------------------

  qh_build_withrestart(qh)
    allow restarts due to qh.JOGGLEmax while calling qh_buildhull()
       qh_errexit always undoes qh_build_withrestart()
    qh.FIRSTpoint/qh.NUMpoints is point array
       it may be moved by qh_joggleinput(qh)
*/
void qh_build_withrestart(qhT *qh) {
  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, 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, 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(qh, True);  /* first call is a nop */
    qh->build_cnt++;
    if (!qh->qhull_optionsiz)
      qh->qhull_optionsiz= (int)strlen(qh->qhull_options);   /* WARN64 */
    else {
      qh->qhull_options [qh->qhull_optionsiz]= '\0';
      qh->qhull_optionlen= qh_OPTIONline;  /* starts a new line */
    }
    qh_option(qh, "_run", &qh->build_cnt, NULL);
    if (qh->build_cnt == qh->RERUN) {
      qh->IStracing= qh->TRACElastrun;  /* duplicated from qh_initqhull_globals */
      if (qh->TRACEpoint != qh_IDunknown || qh->TRACEdist < REALmax/2 || qh->TRACEmerge) {
        qh->TRACElevel= (qh->IStracing? qh->IStracing : 3);
        qh->IStracing= 0;
      }
      qh->qhmem.IStracing= qh->IStracing;
    }
    if (qh->JOGGLEmax < REALmax/2)
      qh_joggleinput(qh);
    qh_initbuild(qh);
    qh_buildhull(qh);
    if (qh->JOGGLEmax < REALmax/2 && !qh->MERGING)
      qh_checkconvex(qh, qh->facet_list, qh_ALGORITHMfault);
  }
  qh->ALLOWrestart= False;
} /* qh_build_withrestart */

/*---------------------------------

  qh_buildhull(qh)
    construct a convex hull by adding outside points one at a time

  returns:

  notes:
    may be called multiple times
    checks facet and vertex lists for incorrect flags
    to recover from STOPcone, call qh_deletevisible and qh_resetlists

  design:
    check visible facet and newfacet flags
    check 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(qhT *qh) {
  facetT *facet;
  pointT *furthest;
  vertexT *vertex;
  int id;

  trace1((qh, qh->ferr, 1037, "qh_buildhull: start build hull\n"));
  FORALLfacets {
    if (facet->visible || facet->newfacet) {
      qh_fprintf(qh, qh->ferr, 6165, "qhull internal error (qh_buildhull): visible or new facet f%d in facet list\n",
                   facet->id);
      qh_errexit(qh, qh_ERRqhull, facet, NULL);
    }
  }
  FORALLvertices {
    if (vertex->newlist) {
      qh_fprintf(qh, qh->ferr, 6166, "qhull internal error (qh_buildhull): new vertex f%d in vertex list\n",
                   vertex->id);
      qh_errprint(qh, "ERRONEOUS", NULL, NULL, NULL, vertex);
      qh_errexit(qh, qh_ERRqhull, NULL, NULL);
    }
    id= qh_pointid(qh, vertex->point);
    if ((qh->STOPpoint>0 && id == qh->STOPpoint-1) ||
        (qh->STOPpoint<0 && id == -qh->STOPpoint-1) ||
        (qh->STOPcone>0 && id == qh->STOPcone-1)) {
      trace1((qh, qh->ferr, 1038,"qh_buildhull: stop point or cone P%d in initial hull\n", id));
      return;
    }
  }
  qh->facet_next= qh->facet_list;      /* advance facet when processed */
  while ((furthest= qh_nextfurthest(qh, &facet))) {
    qh->num_outside--;  /* if ONLYmax, furthest may not be outside */
    if (!qh_addpoint(qh, furthest, facet, qh->ONLYmax))
      break;
  }
  if (qh->NARROWhull) /* move points from outsideset to coplanarset */
    qh_outcoplanar(qh /* facet_list */ );
  if (qh->num_outside && !furthest) {
    qh_fprintf(qh, qh->ferr, 6167, "qhull internal error (qh_buildhull): %d outside points were never processed.\n", qh->num_outside);
    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
  }
  trace1((qh, qh->ferr, 1039, "qh_buildhull: completed the hull construction\n"));
} /* buildhull */


/*---------------------------------

  qh_buildtracing(qh, furthest, facet )
    trace an iteration of qh_buildhull() for furthest point and facet
    if !furthest, prints progress message

  returns:
    tracks progress with qh.lastreport
    updates qh.furthest_id (-3 if furthest is NULL)
    also resets visit_id, vertext_visit on wrap around

  see:
    qh_tracemerging()

  design:
    if !furthest
      print progress message
      exit
    if 'TFn' iteration
      print progress message
    else if tracing
      trace furthest point and facet
    reset qh.visit_id and qh.vertex_visit if overflow may occur
    set qh.furthest_id for tracing
*/
void qh_buildtracing(qhT *qh, pointT *furthest, facetT *facet) {
  realT dist= 0;
  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, qh->ferr, 8118, "\n\
At %02d:%02d:%02d & %2.5g CPU secs, qhull has created %d facets and merged %d.\n\
 The current hull contains %d facets and %d vertices.  Last point was p%d\n",
      tp->tm_hour, tp->tm_min, tp->tm_sec, cpu, qh->facet_id -1,
      total, qh->num_facets, qh->num_vertices, qh->furthest_id);
    return;
  }
  furthestid= qh_pointid(qh, furthest);
  if (qh->TRACEpoint == furthestid) {
    qh->IStracing= qh->TRACElevel;
    qh->qhmem.IStracing= qh->TRACElevel;
  }else if (qh->TRACEpoint != qh_IDunknown && qh->TRACEdist < REALmax/2) {
    qh->IStracing= 0;
    qh->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(qh, furthest, facet, &dist);
    qh_fprintf(qh, qh->ferr, 8119, "\n\
At %02d:%02d:%02d & %2.5g CPU secs, qhull has created %d facets and merged %d.\n\
 The current hull contains %d facets and %d vertices.  There are %d\n\
 outside points.  Next is point p%d(v%d), %2.2g above f%d.\n",
      tp->tm_hour, tp->tm_min, tp->tm_sec, cpu, qh->facet_id -1,
      total, qh->num_facets, qh->num_vertices, qh->num_outside+1,
      furthestid, qh->vertex_id, dist, getid_(facet));
  }else if (qh->IStracing >=1) {
    cpu= (float)qh_CPUclock - (float)qh->hulltime;
    cpu /= (float)qh_SECticks;
    qh_distplane(qh, furthest, facet, &dist);
    qh_fprintf(qh, 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 */

/*---------------------------------

  qh_errexit2(qh, exitcode, facet, otherfacet )
    return exitcode to system after an error
    report two facets

  returns:
    assumes exitcode non-zero

  see:
    normally use qh_errexit() in user.c(reports a facet and a ridge)
*/
void qh_errexit2(qhT *qh, int exitcode, facetT *facet, facetT *otherfacet) {

  qh_errprint(qh, "ERRONEOUS", facet, otherfacet, NULL, NULL);
  qh_errexit(qh, exitcode, NULL, NULL);
} /* errexit2 */


/*---------------------------------

  qh_findhorizon(qh, point, facet, goodvisible, goodhorizon )
    given a visible facet, find the point's horizon and visible facets
    for all facets, !facet-visible

  returns:
    returns qh.visible_list/num_visible with all visible facets
      marks visible facets with ->visible
    updates count of good visible and good horizon facets
    updates qh.max_outside, qh.max_vertex, facet->maxoutside

  see:
    similar to qh_delpoint()

  design:
    move facet to qh.visible_list at end of qh.facet_list
    for all visible facets
     for each unvisited neighbor of a visible facet
       compute distance of point to neighbor
       if point above neighbor
         move neighbor to end of qh.visible_list
       else if point is coplanar with neighbor
         update qh.max_outside, qh.max_vertex, neighbor->maxoutside
         mark neighbor coplanar (will create a samecycle later)
         update horizon statistics
*/
void qh_findhorizon(qhT *qh, pointT *point, facetT *facet, int *goodvisible, int *goodhorizon) {
  facetT *neighbor, **neighborp, *visible;
  int numhorizon= 0, coplanar= 0;
  realT dist;

  trace1((qh, qh->ferr, 1040,"qh_findhorizon: find horizon for point p%d facet f%d\n",qh_pointid(qh, point),facet->id));
  *goodvisible= *goodhorizon= 0;
  zinc_(Ztotvisible);
  qh_removefacet(qh, facet);  /* visible_list at end of qh->facet_list */
  qh_appendfacet(qh, facet);
  qh->num_visible= 1;
  if (facet->good)
    (*goodvisible)++;
  qh->visible_list= facet;
  facet->visible= True;
  facet->f.replace= NULL;
  if (qh->IStracing >=4)
    qh_errprint(qh, "visible", facet, NULL, NULL, NULL);
  qh->visit_id++;
  FORALLvisible_facets {
    if (visible->tricoplanar && !qh->TRInormals) {
      qh_fprintf(qh, qh->ferr, 6230, "Qhull internal error (qh_findhorizon): does not work for tricoplanar facets.  Use option 'Q11'\n");
      qh_errexit(qh, qh_ERRqhull, visible, NULL);
    }
    visible->visitid= qh->visit_id;
    FOREACHneighbor_(visible) {
      if (neighbor->visitid == qh->visit_id)
        continue;
      neighbor->visitid= qh->visit_id;
      zzinc_(Znumvisibility);
      qh_distplane(qh, point, neighbor, &dist);
      if (dist > qh->MINvisible) {
        zinc_(Ztotvisible);
        qh_removefacet(qh, neighbor);  /* append to end of qh->visible_list */
        qh_appendfacet(qh, neighbor);
        neighbor->visible= True;
        neighbor->f.replace= NULL;
        qh->num_visible++;
        if (neighbor->good)
          (*goodvisible)++;
        if (qh->IStracing >=4)
          qh_errprint(qh, "visible", neighbor, NULL, NULL, NULL);
      }else {
        if (dist > - qh->MAXcoplanar) {
          neighbor->coplanar= True;
          zzinc_(Zcoplanarhorizon);
          qh_precision(qh, "coplanar horizon");
          coplanar++;
          if (qh->MERGING) {
            if (dist > 0) {
              maximize_(qh->max_outside, dist);
              maximize_(qh->max_vertex, dist);
#if qh_MAXoutside
              maximize_(neighbor->maxoutside, dist);
#endif
            }else
              minimize_(qh->min_vertex, dist);  /* due to merge later */
          }
          trace2((qh, qh->ferr, 2057, "qh_findhorizon: point p%d is coplanar to horizon f%d, dist=%2.7g < qh->MINvisible(%2.7g)\n",
              qh_pointid(qh, point), neighbor->id, dist, qh->MINvisible));
        }else
          neighbor->coplanar= False;
        zinc_(Ztothorizon);
        numhorizon++;
        if (neighbor->good)
          (*goodhorizon)++;
        if (qh->IStracing >=4)
          qh_errprint(qh, "horizon", neighbor, NULL, NULL, NULL);
      }
    }
  }
  if (!numhorizon) {
    qh_precision(qh, "empty horizon");
    qh_fprintf(qh, qh->ferr, 6168, "qhull precision error (qh_findhorizon): empty horizon\n\
QhullPoint p%d was above all facets.\n", qh_pointid(qh, point));
    qh_printfacetlist(qh, qh->facet_list, NULL, True);
    qh_errexit(qh, qh_ERRprec, NULL, NULL);
  }
  trace1((qh, qh->ferr, 1041, "qh_findhorizon: %d horizon facets(good %d), %d visible(good %d), %d coplanar\n",
       numhorizon, *goodhorizon, qh->num_visible, *goodvisible, coplanar));
  if (qh->IStracing >= 4 && qh->num_facets < 50)
    qh_printlists(qh);
} /* findhorizon */

/*---------------------------------

  qh_nextfurthest(qh, visible )
    returns next furthest point and visible facet for qh_addpoint()
    starts search at qh.facet_next

  returns:
    removes furthest point from outside set
    NULL if none available
    advances qh.facet_next over facets with empty outside sets

  design:
    for each facet from qh.facet_next
      if empty outside set
        advance qh.facet_next
      else if qh.NARROWhull
        determine furthest outside point
        if furthest point is not outside
          advance qh.facet_next(point will be coplanar)
    remove furthest point from outside set
*/
pointT *qh_nextfurthest(qhT *qh, facetT **visible) {
  facetT *facet;
  int size, idx;
  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(qh, &facet->outsideset);
      qh->facet_next= facet->next;
      continue;
    }
    if (qh->NARROWhull) {
      if (facet->notfurthest)
        qh_furthestout(qh, facet);
      furthest= (pointT*)qh_setlast(facet->outsideset);
#if qh_COMPUTEfurthest
      qh_distplane(qh, furthest, facet, &dist);
      zinc_(Zcomputefurthest);
#else
      dist= facet->furthestdist;
#endif
      if (dist < qh->MINoutside) { /* remainder of outside set is coplanar for qh_outcoplanar */
        qh->facet_next= facet->next;
        continue;
      }
    }
    if (!qh->RANDOMoutside && !qh->VIRTUALmemory) {
      if (qh->PICKfurthest) {
        qh_furthestnext(qh /* qh->facet_list */);
        facet= qh->facet_next;
      }
      *visible= facet;
      return((pointT*)qh_setdellast(facet->outsideset));
    }
    if (qh->RANDOMoutside) {
      int outcoplanar = 0;
      if (qh->NARROWhull) {
        FORALLfacets {
          if (facet == qh->facet_next)
            break;
          if (facet->outsideset)
            outcoplanar += qh_setsize(qh, facet->outsideset);
        }
      }
      randr= qh_RANDOMint;
      randr= randr/(qh_RANDOMmax+1);
      idx= (int)floor((qh->num_outside - outcoplanar) * randr);
      FORALLfacet_(qh->facet_next) {
        if (facet->outsideset) {
          SETreturnsize_(facet->outsideset, size);
          if (!size)
            qh_setfree(qh, &facet->outsideset);
          else if (size > idx) {
            *visible= facet;
            return((pointT*)qh_setdelnth(qh, facet->outsideset, idx));
          }else
            idx -= size;
        }
      }
      qh_fprintf(qh, qh->ferr, 6169, "qhull internal error (qh_nextfurthest): num_outside %d is too low\nby at least %d, or a random real %g >= 1.0\n",
              qh->num_outside, idx+1, randr);
      qh_errexit(qh, qh_ERRqhull, NULL, NULL);
    }else { /* VIRTUALmemory */
      facet= qh->facet_tail->previous;
      if (!(furthest= (pointT*)qh_setdellast(facet->outsideset))) {
        if (facet->outsideset)
          qh_setfree(qh, &facet->outsideset);
        qh_removefacet(qh, facet);
        qh_prependfacet(qh, facet, &qh->facet_list);
        continue;
      }
      *visible= facet;
      return furthest;
    }
  }
  return NULL;
} /* nextfurthest */

/*---------------------------------

  qh_partitionall(qh, vertices, points, numpoints )
    partitions all points in points/numpoints to the outsidesets of facets
    vertices= vertices in qh.facet_list(!partitioned)

  returns:
    builds facet->outsideset
    does not partition qh.GOODpoint
    if qh.ONLYgood && !qh.MERGING,
      does not partition qh.GOODvertex

  notes:
    faster if qh.facet_list sorted by anticipated size of outside set

  design:
    initialize pointset with all points
    remove vertices from pointset
    remove qh.GOODpointp from pointset (unless it's qh.STOPcone or qh.STOPpoint)
    for all facets
      for all remaining points in pointset
        compute distance from point to facet
        if point is outside facet
          remove point from pointset (by not reappending)
          update bestpoint
          append point or old bestpoint to facet's outside set
      append bestpoint to facet's outside set (furthest)
    for all points remaining in pointset
      partition point into facets' outside sets and coplanar sets
*/
void qh_partitionall(qhT *qh, setT *vertices, pointT *points, int numpoints){
  setT *pointset;
  vertexT *vertex, **vertexp;
  pointT *point, **pointp, *bestpoint;
  int size, point_i, point_n, point_end, remaining, i, id;
  facetT *facet;
  realT bestdist= -REALmax, dist, distoutside;

  trace1((qh, qh->ferr, 1042, "qh_partitionall: partition all points into outside sets\n"));
  pointset= qh_settemp(qh, numpoints);
  qh->num_outside= 0;
  pointp= SETaddr_(pointset, pointT);
  for (i=numpoints, point= points; i--; point += qh->hull_dim)
    *(pointp++)= point;
  qh_settruncate(qh, pointset, numpoints);
  FOREACHvertex_(vertices) {
    if ((id= qh_pointid(qh, vertex->point)) >= 0)
      SETelem_(pointset, id)= NULL;
  }
  id= qh_pointid(qh, qh->GOODpointp);
  if (id >=0 && qh->STOPcone-1 != id && -qh->STOPpoint-1 != id)
    SETelem_(pointset, id)= NULL;
  if (qh->GOODvertexp && qh->ONLYgood && !qh->MERGING) { /* matches qhull()*/
    if ((id= qh_pointid(qh, qh->GOODvertexp)) >= 0)
      SETelem_(pointset, id)= NULL;
  }
  if (!qh->BESToutside) {  /* matches conditional for qh_partitionpoint below */
    distoutside= qh_DISToutside; /* multiple of qh.MINoutside & qh.max_outside, see user.h */
    zval_(Ztotpartition)= qh->num_points - qh->hull_dim - 1; /*misses GOOD... */
    remaining= qh->num_facets;
    point_end= numpoints;
    FORALLfacets {
      size= point_end/(remaining--) + 100;
      facet->outsideset= qh_setnew(qh, size);
      bestpoint= NULL;
      point_end= 0;
      FOREACHpoint_i_(qh, pointset) {
        if (point) {
          zzinc_(Zpartitionall);
          qh_distplane(qh, point, facet, &dist);
          if (dist < distoutside)
            SETelem_(pointset, point_end++)= point;
          else {
            qh->num_outside++;
            if (!bestpoint) {
              bestpoint= point;
              bestdist= dist;
            }else if (dist > bestdist) {
              qh_setappend(qh, &facet->outsideset, bestpoint);
              bestpoint= point;
              bestdist= dist;
            }else
              qh_setappend(qh, &facet->outsideset, point);
          }
        }
      }
      if (bestpoint) {
        qh_setappend(qh, &facet->outsideset, bestpoint);
#if !qh_COMPUTEfurthest
        facet->furthestdist= bestdist;
#endif
      }else
        qh_setfree(qh, &facet->outsideset);
      qh_settruncate(qh, pointset, point_end);
    }
  }
  /* if !qh->BESToutside, pointset contains points not assigned to outsideset */
  if (qh->BESToutside || qh->MERGING || qh->KEEPcoplanar || qh->KEEPinside) {
    qh->findbestnew= True;
    FOREACHpoint_i_(qh, pointset) {
      if (point)
        qh_partitionpoint(qh, point, qh->facet_list);
    }
    qh->findbestnew= False;
  }
  zzadd_(Zpartitionall, zzval_(Zpartition));
  zzval_(Zpartition)= 0;
  qh_settempfree(qh, &pointset);
  if (qh->IStracing >= 4)
    qh_printfacetlist(qh, qh->facet_list, NULL, True);
} /* partitionall */


/*---------------------------------

  qh_partitioncoplanar(qh, 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(qhT *qh, 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(qh, point, facet, &bestdist, qh_ALL, &isoutside, &numpart);
    else
      bestfacet= qh_findbest(qh, point, facet, qh_ALL, !qh_ISnewfacets, qh->DELAUNAY,
                          &bestdist, &isoutside, &numpart);
    zinc_(Ztotpartcoplanar);
    zzadd_(Zpartcoplanar, numpart);
    if (!qh->DELAUNAY && !qh->KEEPinside) { /*  for 'd', bestdist skips upperDelaunay facets */
      if (qh->KEEPnearinside) {
        if (bestdist < -qh->NEARinside) {
          zinc_(Zcoplanarinside);
          trace4((qh, qh->ferr, 4062, "qh_partitioncoplanar: point p%d is more than near-inside facet f%d dist %2.2g findbestnew %d\n",
                  qh_pointid(qh, point), bestfacet->id, bestdist, qh->findbestnew));
          return;
        }
      }else if (bestdist < -qh->MAXcoplanar) {
          trace4((qh, qh->ferr, 4063, "qh_partitioncoplanar: point p%d is inside facet f%d dist %2.2g findbestnew %d\n",
                  qh_pointid(qh, 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(qh, 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, 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(qh, point), facet->id, bestfacet->id, bestdist));
        oldfindbest= qh->findbestnew;
        qh->findbestnew= False;
        qh_partitionpoint(qh, point, bestfacet);
        qh->findbestnew= oldfindbest;
        return;
      }
    }
    qh->max_outside= bestdist;
    if (bestdist > qh->TRACEdist) {
      qh_fprintf(qh, 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(qh, point), facet->id, bestdist, bestfacet->id, qh->furthest_id);
      qh_errprint(qh, "DISTANT", facet, bestfacet, NULL, NULL);
    }
  }
  if (qh->KEEPcoplanar + qh->KEEPinside + qh->KEEPnearinside) {
    oldfurthest= (pointT*)qh_setlast(bestfacet->coplanarset);
    if (oldfurthest) {
      zinc_(Zcomputefurthest);
      qh_distplane(qh, oldfurthest, bestfacet, &dist2);
    }
    if (!oldfurthest || dist2 < bestdist)
      qh_setappend(qh, &bestfacet->coplanarset, point);
    else
      qh_setappend2ndlast(qh, &bestfacet->coplanarset, point);
  }
  trace4((qh, qh->ferr, 4064, "qh_partitioncoplanar: point p%d is coplanar with facet f%d(or inside) dist %2.2g\n",
          qh_pointid(qh, point), bestfacet->id, bestdist));
} /* partitioncoplanar */

/*---------------------------------

  qh_partitionpoint(qh, point, facet )
    assigns point to an outside set, coplanar set, or inside set (i.e., dropt)
    if qh.findbestnew
      uses qh_findbestnew() to search all new facets
    else
      uses qh_findbest()

  notes:
    after qh_distplane(), this and qh_findbest() are most expensive in 3-d

  design:
    find best facet for point
      (either exhaustive search of new facets or directed search from facet)
    if qh.NARROWhull
      retain coplanar and nearinside points as outside points
    if point is outside bestfacet
      if point above furthest point for bestfacet
        append point to outside set (it becomes the new furthest)
        if outside set was empty
          move bestfacet to end of qh.facet_list (i.e., after qh.facet_next)
        update bestfacet->furthestdist
      else
        append point one before end of outside set
    else if point is coplanar to bestfacet
      if keeping coplanar points or need to update qh.max_outside
        partition coplanar point into bestfacet
    else if near-inside point
      partition as coplanar point into bestfacet
    else is an inside point
      if keeping inside points
        partition as coplanar point into bestfacet
*/
void qh_partitionpoint(qhT *qh, pointT *point, facetT *facet) {
  realT bestdist;
  boolT isoutside;
  facetT *bestfacet;
  int numpart;
#if qh_COMPUTEfurthest
  realT dist;
#endif

  if (qh->findbestnew)
    bestfacet= qh_findbestnew(qh, point, facet, &bestdist, qh->BESToutside, &isoutside, &numpart);
  else
    bestfacet= qh_findbest(qh, point, facet, qh->BESToutside, qh_ISnewfacets, !qh_NOupper,
                          &bestdist, &isoutside, &numpart);
  zinc_(Ztotpartition);
  zzadd_(Zpartition, numpart);
  if (qh->NARROWhull) {
    if (qh->DELAUNAY && !isoutside && bestdist >= -qh->MAXcoplanar)
      qh_precision(qh, "nearly incident point(narrow hull)");
    if (qh->KEEPnearinside) {
      if (bestdist >= -qh->NEARinside)
        isoutside= True;
    }else if (bestdist >= -qh->MAXcoplanar)
      isoutside= True;
  }

  if (isoutside) {
    if (!bestfacet->outsideset
    || !qh_setlast(bestfacet->outsideset)) {
      qh_setappend(qh, &(bestfacet->outsideset), point);
      if (!bestfacet->newfacet) {
        qh_removefacet(qh, bestfacet);  /* make sure it's after qh->facet_next */
        qh_appendfacet(qh, bestfacet);
      }
#if !qh_COMPUTEfurthest
      bestfacet->furthestdist= bestdist;
#endif
    }else {
#if qh_COMPUTEfurthest
      zinc_(Zcomputefurthest);
      qh_distplane(qh, oldfurthest, bestfacet, &dist);
      if (dist < bestdist)
        qh_setappend(qh, &(bestfacet->outsideset), point);
      else
        qh_setappend2ndlast(qh, &(bestfacet->outsideset), point);
#else
      if (bestfacet->furthestdist < bestdist) {
        qh_setappend(qh, &(bestfacet->outsideset), point);
        bestfacet->furthestdist= bestdist;
      }else
        qh_setappend2ndlast(qh, &(bestfacet->outsideset), point);
#endif
    }
    qh->num_outside++;
    trace4((qh, qh->ferr, 4065, "qh_partitionpoint: point p%d is outside facet f%d new? %d (or narrowhull)\n",
          qh_pointid(qh, 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(qh, "nearly incident point");
    if ((qh->KEEPcoplanar + qh->KEEPnearinside) || bestdist > qh->max_outside)
      qh_partitioncoplanar(qh, point, bestfacet, &bestdist);
    else {
      trace4((qh, qh->ferr, 4066, "qh_partitionpoint: point p%d is coplanar to facet f%d (dropped)\n",
          qh_pointid(qh, point), bestfacet->id));
    }
  }else if (qh->KEEPnearinside && bestdist > -qh->NEARinside) {
    zinc_(Zpartnear);
    qh_partitioncoplanar(qh, point, bestfacet, &bestdist);
  }else {
    zinc_(Zpartinside);
    trace4((qh, qh->ferr, 4067, "qh_partitionpoint: point p%d is inside all facets, closest to f%d dist %2.2g\n",
          qh_pointid(qh, point), bestfacet->id, bestdist));
    if (qh->KEEPinside)
      qh_partitioncoplanar(qh, point, bestfacet, &bestdist);
  }
} /* partitionpoint */

/*---------------------------------

  qh_partitionvisible(qh, 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(qhT *qh /*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(qh, visible);
    }
    if (!newfacet)
      newfacet= qh->newfacet_list;
    if (newfacet == qh->facet_tail) {
      qh_fprintf(qh, qh->ferr, 6170, "qhull precision error (qh_partitionvisible): all new facets deleted as\n        degenerate facets. Can not continue.\n");
      qh_errexit(qh, qh_ERRprec, NULL, NULL);
    }
    if (visible->outsideset) {
      size= qh_setsize(qh, visible->outsideset);
      *numoutside += size;
      qh->num_outside -= size;
      FOREACHpoint_(visible->outsideset)
        qh_partitionpoint(qh, point, newfacet);
    }
    if (visible->coplanarset && (qh->KEEPcoplanar + qh->KEEPinside + qh->KEEPnearinside)) {
      size= qh_setsize(qh, visible->coplanarset);
      coplanar += size;
      FOREACHpoint_(visible->coplanarset) {
        if (allpoints) /* not used */
          qh_partitionpoint(qh, point, newfacet);
        else
          qh_partitioncoplanar(qh, point, newfacet, NULL);
      }
    }
  }
  FOREACHvertex_(qh->del_vertices) {
    if (vertex->point) {
      if (allpoints) /* not used */
        qh_partitionpoint(qh, vertex->point, qh->newfacet_list);
      else
        qh_partitioncoplanar(qh, vertex->point, qh->newfacet_list, NULL);
    }
  }
  trace1((qh, qh->ferr, 1043,"qh_partitionvisible: partitioned %d points from outsidesets and %d points from coplanarsets\n", *numoutside, coplanar));
} /* partitionvisible */



/*---------------------------------

  qh_precision(qh, reason )
    restart on precision errors if not merging and if 'QJn'
*/
void qh_precision(qhT *qh, const char *reason) {

  if (qh->ALLOWrestart && !qh->PREmerge && !qh->MERGEexact) {
    if (qh->JOGGLEmax < REALmax/2) {
      trace0((qh, 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 */

/*---------------------------------

  qh_printsummary(qh, fp )
    prints summary to fp

  notes:
    not in io_r.c so that user_eg.c can prevent io_r.c from loading
    qh_printsummary and qh_countfacets must match counts

  design:
    determine number of points, vertices, and coplanar points
    print summary
*/
void qh_printsummary(qhT *qh, 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, qh->other_points);
  numvertices= qh->num_vertices - qh_setsize(qh, qh->del_vertices);
  id= qh_pointid(qh, qh->GOODpointp);
  FORALLfacets {
    if (facet->coplanarset)
      numcoplanars += qh_setsize(qh, facet->coplanarset);
    if (facet->good) {
      if (facet->simplicial) {
        if (facet->keepcentrum && facet->tricoplanar)
          numtricoplanars++;
      }else if (qh_setsize(qh, facet->vertices) != qh->hull_dim)
        nonsimplicial++;
    }
  }
  if (id >=0 && qh->STOPcone-1 != id && -qh->STOPpoint-1 != id)
    size--;
  if (qh->STOPcone || qh->STOPpoint)
      qh_fprintf(qh, 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(qh, fp, 9289, "\n\
Furthest-site Voronoi vertices by the convex hull of %d points in %d-d:\n\n", size, qh->hull_dim);
    else
      qh_fprintf(qh, fp, 9290, "\n\
Voronoi diagram by the convex hull of %d points in %d-d:\n\n", size, qh->hull_dim);
    qh_fprintf(qh, fp, 9291, "  Number of Voronoi regions%s: %d\n",
              qh->ATinfinity ? " and at-infinity" : "", numvertices);
    if (numdel)
      qh_fprintf(qh, fp, 9292, "  Total number of deleted points due to merging: %d\n", numdel);
    if (numcoplanars - numdel > 0)
      qh_fprintf(qh, fp, 9293, "  Number of nearly incident points: %d\n", numcoplanars - numdel);
    else if (size - numvertices - numdel > 0)
      qh_fprintf(qh, fp, 9294, "  Total number of nearly incident points: %d\n", size - numvertices - numdel);
    qh_fprintf(qh, fp, 9295, "  Number of%s Voronoi vertices: %d\n",
              goodused ? " 'good'" : "", qh->num_good);
    if (nonsimplicial)
      qh_fprintf(qh, fp, 9296, "  Number of%s non-simplicial Voronoi vertices: %d\n",
              goodused ? " 'good'" : "", nonsimplicial);
  }else if (qh->DELAUNAY) {
    if (qh->UPPERdelaunay)
      qh_fprintf(qh, fp, 9297, "\n\
Furthest-site Delaunay triangulation by the convex hull of %d points in %d-d:\n\n", size, qh->hull_dim);
    else
      qh_fprintf(qh, fp, 9298, "\n\
Delaunay triangulation by the convex hull of %d points in %d-d:\n\n", size, qh->hull_dim);
    qh_fprintf(qh, fp, 9299, "  Number of input sites%s: %d\n",
              qh->ATinfinity ? " and at-infinity" : "", numvertices);
    if (numdel)
      qh_fprintf(qh, fp, 9300, "  Total number of deleted points due to merging: %d\n", numdel);
    if (numcoplanars - numdel > 0)
      qh_fprintf(qh, fp, 9301, "  Number of nearly incident points: %d\n", numcoplanars - numdel);
    else if (size - numvertices - numdel > 0)
      qh_fprintf(qh, fp, 9302, "  Total number of nearly incident points: %d\n", size - numvertices - numdel);
    qh_fprintf(qh, fp, 9303, "  Number of%s Delaunay regions: %d\n",
              goodused ? " 'good'" : "", qh->num_good);
    if (nonsimplicial)
      qh_fprintf(qh, fp, 9304, "  Number of%s non-simplicial Delaunay regions: %d\n",
              goodused ? " 'good'" : "", nonsimplicial);
  }else if (qh->HALFspace) {
    qh_fprintf(qh, fp, 9305, "\n\
Halfspace intersection by the convex hull of %d points in %d-d:\n\n", size, qh->hull_dim);
    qh_fprintf(qh, fp, 9306, "  Number of halfspaces: %d\n", size);
    qh_fprintf(qh, fp, 9307, "  Number of non-redundant halfspaces: %d\n", numvertices);
    if (numcoplanars) {
      if (qh->KEEPinside && qh->KEEPcoplanar)
        s= "similar and redundant";
      else if (qh->KEEPinside)
        s= "redundant";
      else
        s= "similar";
      qh_fprintf(qh, fp, 9308, "  Number of %s halfspaces: %d\n", s, numcoplanars);
    }
    qh_fprintf(qh, fp, 9309, "  Number of intersection points: %d\n", qh->num_facets - qh->num_visible);
    if (goodused)
      qh_fprintf(qh, fp, 9310, "  Number of 'good' intersection points: %d\n", qh->num_good);
    if (nonsimplicial)
      qh_fprintf(qh, fp, 9311, "  Number of%s non-simplicial intersection points: %d\n",
              goodused ? " 'good'" : "", nonsimplicial);
  }else {
    qh_fprintf(qh, fp, 9312, "\n\
Convex hull of %d points in %d-d:\n\n", size, qh->hull_dim);
    qh_fprintf(qh, fp, 9313, "  Number of vertices: %d\n", numvertices);
    if (numcoplanars) {
      if (qh->KEEPinside && qh->KEEPcoplanar)
        s= "coplanar and interior";
      else if (qh->KEEPinside)
        s= "interior";
      else
        s= "coplanar";
      qh_fprintf(qh, fp, 9314, "  Number of %s points: %d\n", s, numcoplanars);
    }
    qh_fprintf(qh, fp, 9315, "  Number of facets: %d\n", qh->num_facets - qh->num_visible);
    if (goodused)
      qh_fprintf(qh, fp, 9316, "  Number of 'good' facets: %d\n", qh->num_good);
    if (nonsimplicial)
      qh_fprintf(qh, fp, 9317, "  Number of%s non-simplicial facets: %d\n",
              goodused ? " 'good'" : "", nonsimplicial);
  }
  if (numtricoplanars)
      qh_fprintf(qh, fp, 9318, "  Number of triangulated facets: %d\n", numtricoplanars);
  qh_fprintf(qh, fp, 9319, "\nStatistics for: %s | %s",
                      qh->rbox_command, qh->qhull_command);
  if (qh->ROTATErandom != INT_MIN)
    qh_fprintf(qh, fp, 9320, " QR%d\n\n", qh->ROTATErandom);
  else
    qh_fprintf(qh, fp, 9321, "\n\n");
  qh_fprintf(qh, fp, 9322, "  Number of points processed: %d\n", zzval_(Zprocessed));
  qh_fprintf(qh, fp, 9323, "  Number of hyperplanes created: %d\n", zzval_(Zsetplane));
  if (qh->DELAUNAY)
    qh_fprintf(qh, fp, 9324, "  Number of facets in hull: %d\n", qh->num_facets - qh->num_visible);
  qh_fprintf(qh, fp, 9325, "  Number of distance tests for qhull: %d\n", zzval_(Zpartition)+
      zzval_(Zpartitionall)+zzval_(Znumvisibility)+zzval_(Zpartcoplanar));
#if 0  /* NOTE: must print before printstatistics() */
  {realT stddev, ave;
  qh_fprintf(qh, fp, 9326, "  average new facet balance: %2.2g\n",
          wval_(Wnewbalance)/zval_(Zprocessed));
  stddev= qh_stddev(zval_(Zprocessed), wval_(Wnewbalance),
                                 wval_(Wnewbalance2), &ave);
  qh_fprintf(qh, fp, 9327, "  new facet standard deviation: %2.2g\n", stddev);
  qh_fprintf(qh, fp, 9328, "  average partition balance: %2.2g\n",
          wval_(Wpbalance)/zval_(Zpbalance));
  stddev= qh_stddev(zval_(Zpbalance), wval_(Wpbalance),
                                 wval_(Wpbalance2), &ave);
  qh_fprintf(qh, fp, 9329, "  partition standard deviation: %2.2g\n", stddev);
  }
#endif
  if (nummerged) {
    qh_fprintf(qh, fp, 9330,"  Number of distance tests for merging: %d\n",zzval_(Zbestdist)+
          zzval_(Zcentrumtests)+zzval_(Zdistconvex)+zzval_(Zdistcheck)+
          zzval_(Zdistzero));
    qh_fprintf(qh, fp, 9331,"  Number of distance tests for checking: %d\n",zzval_(Zcheckpart));
    qh_fprintf(qh, 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(qh, fp, 9333, "  CPU seconds to compute hull (after input): %2.4g\n", cpu);
  }
  if (qh->RERUN) {
    if (!qh->PREmerge && !qh->MERGEexact)
      qh_fprintf(qh, fp, 9334, "  Percentage of runs with precision errors: %4.1f\n",
           zzval_(Zretry)*100.0/qh->build_cnt);  /* careful of order */
  }else if (qh->JOGGLEmax < REALmax/2) {
    if (zzval_(Zretry))
      qh_fprintf(qh, fp, 9335, "  After %d retries, input joggled by: %2.2g\n",
         zzval_(Zretry), qh->JOGGLEmax);
    else
      qh_fprintf(qh, fp, 9336, "  Input joggled by: %2.2g\n", qh->JOGGLEmax);
  }
  if (qh->totarea != 0.0)
    qh_fprintf(qh, fp, 9337, "  %s facet area:   %2.8g\n",
            zzval_(Ztotmerge) ? "Approximate" : "Total", qh->totarea);
  if (qh->totvol != 0.0)
    qh_fprintf(qh, fp, 9338, "  %s volume:       %2.8g\n",
            zzval_(Ztotmerge) ? "Approximate" : "Total", qh->totvol);
  if (qh->MERGING) {
    qh_outerinner(qh, NULL, &outerplane, &innerplane);
    if (outerplane > 2 * qh->DISTround) {
      qh_fprintf(qh, fp, 9339, "  Maximum distance of %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(qh, fp, 9340, " (%.1fx)\n", ratio);
      else
        qh_fprintf(qh, fp, 9341, "\n");
    }
    if (innerplane < -2 * qh->DISTround) {
      qh_fprintf(qh, fp, 9342, "  Maximum distance of %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(qh, fp, 9343, " (%.1fx)\n", ratio);
      else
        qh_fprintf(qh, fp, 9344, "\n");
    }
  }
  qh_fprintf(qh, fp, 9345, "\n");
} /* printsummary */


geometry/src/geom_r.c0000644000176200001440000012452313432323610014314 0ustar  liggesusers/*
  ---------------------------------

   geom_r.c
   geometric routines of qhull

   see qh-geom_r.htm and geom_r.h

   Copyright (c) 1993-2015 The Geometry Center.
   $Id: //main/2015/qhull/src/libqhull_r/geom_r.c#2 $$Change: 1995 $
   $DateTime: 2015/10/13 21:59:42 $$Author: bbarber $

   infrequent code goes into geom2_r.c
*/

#include "qhull_ra.h"

/*---------------------------------

  qh_distplane(qh, point, facet, dist )
    return distance from point to facet

  returns:
    dist
    if qh.RANDOMdist, joggles result

  notes:
    dist > 0 if point is above facet (i.e., outside)
    does not error (for qh_sortfacets, qh_outerinner)

  see:
    qh_distnorm in geom2_r.c
    qh_distplane [geom_r.c], QhullFacet::distance, and QhullHyperplane::distance are copies
*/
void qh_distplane(qhT *qh, pointT *point, facetT *facet, realT *dist) {
  coordT *normal= facet->normal, *coordp, randr;
  int k;

  switch (qh->hull_dim){
  case 2:
    *dist= facet->offset + point[0] * normal[0] + point[1] * normal[1];
    break;
  case 3:
    *dist= facet->offset + point[0] * normal[0] + point[1] * normal[1] + point[2] * normal[2];
    break;
  case 4:
    *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3];
    break;
  case 5:
    *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4];
    break;
  case 6:
    *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]+point[5]*normal[5];
    break;
  case 7:
    *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]+point[5]*normal[5]+point[6]*normal[6];
    break;
  case 8:
    *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]+point[5]*normal[5]+point[6]*normal[6]+point[7]*normal[7];
    break;
  default:
    *dist= facet->offset;
    coordp= point;
    for (k=qh->hull_dim; k--; )
      *dist += *coordp++ * *normal++;
    break;
  }
  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, qh->ferr, 8001, "qh_distplane: ");
    qh_fprintf(qh, qh->ferr, 8002, qh_REAL_1, *dist);
    qh_fprintf(qh, qh->ferr, 8003, "from p%d to f%d\n", qh_pointid(qh, point), facet->id);
  }
  return;
} /* distplane */


/*---------------------------------

  qh_findbest(qh, point, startfacet, bestoutside, qh_ISnewfacets, qh_NOupper, dist, isoutside, numpart )
    find facet that is furthest below a point
    for upperDelaunay facets
      returns facet only if !qh_NOupper and clearly above

  input:
    starts search at 'startfacet' (can not be flipped)
    if !bestoutside(qh_ALL), stops at qh.MINoutside

  returns:
    best facet (reports error if NULL)
    early out if isoutside defined and bestdist > qh.MINoutside
    dist is distance to facet
    isoutside is true if point is outside of facet
    numpart counts the number of distance tests

  see also:
    qh_findbestnew()

  notes:
    If merging (testhorizon), searches horizon facets of coplanar best facets because
    after qh_distplane, this and qh_partitionpoint are the most expensive in 3-d
      avoid calls to distplane, function calls, and real number operations.
    caller traces result
    Optimized for outside points.   Tried recording a search set for qh_findhorizon.
    Made code more complicated.

  when called by qh_partitionvisible():
    indicated by qh_ISnewfacets
    qh.newfacet_list is list of simplicial, new facets
    qh_findbestnew set if qh_sharpnewfacets returns True (to use qh_findbestnew)
    qh.bestfacet_notsharp set if qh_sharpnewfacets returns False

  when called by qh_findfacet(), qh_partitionpoint(), qh_partitioncoplanar(),
                 qh_check_bestdist(), qh_addpoint()
    indicated by !qh_ISnewfacets
    returns best facet in neighborhood of given facet
      this is best facet overall if dist > -   qh.MAXcoplanar
        or hull has at least a "spherical" curvature

  design:
    initialize and test for early exit
    repeat while there are better facets
      for each neighbor of facet
        exit if outside facet found
        test for better facet
    if point is inside and partitioning
      test for new facets with a "sharp" intersection
      if so, future calls go to qh_findbestnew()
    test horizon facets
*/
facetT *qh_findbest(qhT *qh, pointT *point, facetT *startfacet,
                     boolT bestoutside, boolT isnewfacets, boolT noupper,
                     realT *dist, boolT *isoutside, int *numpart) {
  realT bestdist= -REALmax/2 /* avoid underflow */;
  facetT *facet, *neighbor, **neighborp;
  facetT *bestfacet= NULL, *lastfacet= NULL;
  int oldtrace= qh->IStracing;
  unsigned int visitid= ++qh->visit_id;
  int numpartnew=0;
  boolT testhorizon = True; /* needed if precise, e.g., rbox c D6 | qhull Q0 Tv */

  zinc_(Zfindbest);
  if (qh->IStracing >= 3 || (qh->TRACElevel && qh->TRACEpoint >= 0 && qh->TRACEpoint == qh_pointid(qh, point))) {
    if (qh->TRACElevel > qh->IStracing)
      qh->IStracing= qh->TRACElevel;
    qh_fprintf(qh, qh->ferr, 8004, "qh_findbest: point p%d starting at f%d isnewfacets? %d, unless %d exit if > %2.2g\n",
             qh_pointid(qh, point), startfacet->id, isnewfacets, bestoutside, qh->MINoutside);
    qh_fprintf(qh, qh->ferr, 8005, "  testhorizon? %d noupper? %d", testhorizon, noupper);
    qh_fprintf(qh, qh->ferr, 8006, "  Last point added was p%d.", qh->furthest_id);
    qh_fprintf(qh, 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(qh, point, startfacet, dist);  /* this code is duplicated below */
    if (!bestoutside && *dist >= qh->MINoutside
    && (!startfacet->upperdelaunay || !noupper)) {
      bestfacet= startfacet;
      goto LABELreturn_best;
    }
    bestdist= *dist;
    if (!startfacet->upperdelaunay) {
      bestfacet= startfacet;
    }
  }else
    *numpart= 0;
  startfacet->visitid= visitid;
  facet= startfacet;
  while (facet) {
    trace4((qh, qh->ferr, 4001, "qh_findbest: neighbors of f%d, bestdist %2.2g f%d\n",
                facet->id, bestdist, getid_(bestfacet)));
    lastfacet= facet;
    FOREACHneighbor_(facet) {
      if (!neighbor->newfacet && isnewfacets)
        continue;
      if (neighbor->visitid == visitid)
        continue;
      neighbor->visitid= visitid;
      if (!neighbor->flipped) {  /* code duplicated above */
        (*numpart)++;
        qh_distplane(qh, point, neighbor, dist);
        if (*dist > bestdist) {
          if (!bestoutside && *dist >= qh->MINoutside
          && (!neighbor->upperdelaunay || !noupper)) {
            bestfacet= neighbor;
            goto LABELreturn_best;
          }
          if (!neighbor->upperdelaunay) {
            bestfacet= neighbor;
            bestdist= *dist;
            break; /* switch to neighbor */
          }else if (!bestfacet) {
            bestdist= *dist;
            break; /* switch to neighbor */
          }
        } /* end of *dist>bestdist */
      } /* end of !flipped */
    } /* end of FOREACHneighbor */
    facet= neighbor;  /* non-NULL only if *dist>bestdist */
  } /* end of while facet (directed search) */
  if (isnewfacets) {
    if (!bestfacet) {
      bestdist= -REALmax/2;
      bestfacet= qh_findbestnew(qh, 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(qh)) {
        /* seldom used, qh_findbestnew will retest all facets */
        zinc_(Zfindnewsharp);
        bestfacet= qh_findbestnew(qh, point, bestfacet, &bestdist, bestoutside, isoutside, &numpartnew);
        testhorizon= False; /* qh_findbestnew calls qh_findbesthorizon */
        qh->findbestnew= True;
      }else
        qh->findbest_notsharp= True;
    }
  }
  if (!bestfacet)
    bestfacet= qh_findbestlower(qh, lastfacet, point, &bestdist, numpart);
  if (testhorizon)
    bestfacet= qh_findbesthorizon(qh, !qh_IScheckmax, point, bestfacet, noupper, &bestdist, &numpartnew);
  *dist= bestdist;
  if (isoutside && bestdist < qh->MINoutside)
    *isoutside= False;
LABELreturn_best:
  zadd_(Zfindbesttot, *numpart);
  zmax_(Zfindbestmax, *numpart);
  (*numpart) += numpartnew;
  qh->IStracing= oldtrace;
  return bestfacet;
}  /* findbest */


/*---------------------------------

  qh_findbesthorizon(qh, qh_IScheckmax, point, startfacet, qh_NOupper, &bestdist, &numpart )
    search coplanar and better horizon facets from startfacet/bestdist
    ischeckmax turns off statistics and minsearch update
    all arguments must be initialized
  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(qhT *qh, boolT ischeckmax, pointT* point, facetT *startfacet, boolT noupper, realT *bestdist, int *numpart) {
  facetT *bestfacet= startfacet;
  realT dist;
  facetT *neighbor, **neighborp, *facet;
  facetT *nextfacet= NULL; /* optimize last facet of coplanarfacetset */
  int numpartinit= *numpart, coplanarfacetset_size;
  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, 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(qh, 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, &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, qh->ferr, 4003, "qh_findbesthorizon: newbest? %d bestfacet f%d bestdist %2.2g\n", newbest, getid_(bestfacet), *bestdist));
  return bestfacet;
}  /* findbesthorizon */

/*---------------------------------

  qh_findbestnew(qh, point, startfacet, dist, isoutside, numpart )
    find best newfacet for point
    searches all of qh.newfacet_list starting at startfacet
    searches horizon facets of coplanar best newfacets
    searches all facets if startfacet == qh.facet_list
  returns:
    best new or horizon facet that is not upperdelaunay
    early out if isoutside and not 'Qf'
    dist is distance to facet
    isoutside is true if point is outside of facet
    numpart is number of distance tests

  notes:
    Always used for merged new facets (see qh_USEfindbestnew)
    Avoids upperdelaunay facet unless (isoutside and outside)

    Uses qh.visit_id, qh.coplanarfacetset.
    If share visit_id with qh_findbest, coplanarfacetset is incorrect.

    If merging (testhorizon), searches horizon facets of coplanar best facets because
    a point maybe coplanar to the bestfacet, below its horizon facet,
    and above a horizon facet of a coplanar newfacet.  For example,
      rbox 1000 s Z1 G1e-13 | qhull
      rbox 1000 s W1e-13 P0 t992110337 | QHULL d Qbb Qc

    qh_findbestnew() used if
       qh_sharpnewfacets -- newfacets contains a sharp angle
       if many merges, qh_premerge found a merge, or 'Qf' (qh.findbestnew)

  see also:
    qh_partitionall() and qh_findbest()

  design:
    for each new facet starting from startfacet
      test distance from point to facet
      return facet if clearly outside
      unless upperdelaunay and a lowerdelaunay exists
         update best facet
    test horizon facets
*/
facetT *qh_findbestnew(qhT *qh, pointT *point, facetT *startfacet,
           realT *dist, boolT bestoutside, boolT *isoutside, int *numpart) {
  realT bestdist= -REALmax/2;
  facetT *bestfacet= NULL, *facet;
  int oldtrace= qh->IStracing, i;
  unsigned int visitid= ++qh->visit_id;
  realT distoutside= 0.0;
  boolT isdistoutside; /* True if distoutside is defined */
  boolT testhorizon = True; /* needed if precise, e.g., rbox c D6 | qhull Q0 Tv */

  if (!startfacet) {
    if (qh->MERGING)
      qh_fprintf(qh, 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, qh->ferr, 6002, "qhull internal error (qh_findbestnew): no new facets for point p%d\n",
              qh->furthest_id);
    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
  }
  zinc_(Zfindnew);
  if (qh->BESToutside || bestoutside)
    isdistoutside= False;
  else {
    isdistoutside= True;
    distoutside= qh_DISToutside; /* multiple of qh.MINoutside & qh.max_outside, see user.h */
  }
  if (isoutside)
    *isoutside= True;
  *numpart= 0;
  if (qh->IStracing >= 3 || (qh->TRACElevel && qh->TRACEpoint >= 0 && qh->TRACEpoint == qh_pointid(qh, point))) {
    if (qh->TRACElevel > qh->IStracing)
      qh->IStracing= qh->TRACElevel;
    qh_fprintf(qh, qh->ferr, 8008, "qh_findbestnew: point p%d facet f%d. Stop? %d if dist > %2.2g\n",
             qh_pointid(qh, point), startfacet->id, isdistoutside, distoutside);
    qh_fprintf(qh, qh->ferr, 8009, "  Last point added p%d visitid %d.",  qh->furthest_id, visitid);
    qh_fprintf(qh, 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(qh, point, facet, dist);
        (*numpart)++;
        if (*dist > bestdist) {
          if (!facet->upperdelaunay || *dist >= qh->MINoutside) {
            bestfacet= facet;
            if (isdistoutside && *dist >= distoutside)
              goto LABELreturn_bestnew;
            bestdist= *dist;
          }
        }
      } /* end of !flipped */
    } /* FORALLfacet from startfacet or qh->newfacet_list */
  }
  if (testhorizon || !bestfacet) /* testhorizon is always True.  Keep the same code as qh_findbest */
    bestfacet= qh_findbesthorizon(qh, !qh_IScheckmax, point, bestfacet ? bestfacet : startfacet,
                                        !qh_NOupper, &bestdist, numpart);
  *dist= bestdist;
  if (isoutside && *dist < qh->MINoutside)
    *isoutside= False;
LABELreturn_bestnew:
  zadd_(Zfindnewtot, *numpart);
  zmax_(Zfindnewmax, *numpart);
  trace4((qh, qh->ferr, 4004, "qh_findbestnew: bestfacet f%d bestdist %2.2g\n", getid_(bestfacet), *dist));
  qh->IStracing= oldtrace;
  return bestfacet;
}  /* findbestnew */

/* ============ hyperplane functions -- keep code together [?] ============ */

/*---------------------------------

  qh_backnormal(qh, rows, numrow, numcol, sign, normal, nearzero )
    given an upper-triangular rows array and a sign,
    solve for normal equation x using back substitution over rows U

  returns:
     normal= x

     if will not be able to divzero() when normalized(qh.MINdenom_2 and qh.MINdenom_1_2),
       if fails on last row
         this means that the hyperplane intersects [0,..,1]
         sets last coordinate of normal to sign
       otherwise
         sets tail of normal to [...,sign,0,...], i.e., solves for b= [0...0]
         sets nearzero

  notes:
     assumes numrow == numcol-1

     see Golub & van Loan, 1983, Eq. 4.4-9 for "Gaussian elimination with complete pivoting"

     solves Ux=b where Ax=b and PA=LU
     b= [0,...,0,sign or 0]  (sign is either -1 or +1)
     last row of A= [0,...,0,1]

     1) Ly=Pb == y=b since P only permutes the 0's of   b

  design:
    for each row from end
      perform back substitution
      if near zero
        use qh_divzero for division
        if zero divide and not last row
          set tail of normal to 0
*/
void qh_backnormal(qhT *qh, realT **rows, int numrow, int numcol, boolT sign,
        coordT *normal, boolT *nearzero) {
  int i, j;
  coordT *normalp, *normal_tail, *ai, *ak;
  realT diagonal;
  boolT waszero;
  int zerocol= -1;

  normalp= normal + numcol - 1;
  *normalp--= (sign ? -1.0 : 1.0);
  for (i=numrow; i--; ) {
    *normalp= 0.0;
    ai= rows[i] + i + 1;
    ak= normalp+1;
    for (j=i+1; j < numcol; j++)
      *normalp -= *ai++ * *ak++;
    diagonal= (rows[i])[i];
    if (fabs_(diagonal) > qh->MINdenom_2)
      *(normalp--) /= diagonal;
    else {
      waszero= False;
      *normalp= qh_divzero(*normalp, diagonal, qh->MINdenom_1_2, &waszero);
      if (waszero) {
        zerocol= i;
        *(normalp--)= (sign ? -1.0 : 1.0);
        for (normal_tail= normalp+2; normal_tail < normal + numcol; normal_tail++)
          *normal_tail= 0.0;
      }else
        normalp--;
    }
  }
  if (zerocol != -1) {
    zzinc_(Zback0);
    *nearzero= True;
    trace4((qh, qh->ferr, 4005, "qh_backnormal: zero diagonal at column %d.\n", i));
    qh_precision(qh, "zero diagonal on back substitution");
  }
} /* backnormal */

/*---------------------------------

  qh_gausselim(qh, rows, numrow, numcol, sign )
    Gaussian elimination with partial pivoting

  returns:
    rows is upper triangular (includes row exchanges)
    flips sign for each row exchange
    sets nearzero if pivot[k] < qh.NEARzero[k], else clears it

  notes:
    if nearzero, the determinant's sign may be incorrect.
    assumes numrow <= numcol

  design:
    for each row
      determine pivot and exchange rows if necessary
      test for near zero
      perform gaussian elimination step
*/
void qh_gausselim(qhT *qh, realT **rows, int numrow, int numcol, boolT *sign, boolT *nearzero) {
  realT *ai, *ak, *rowp, *pivotrow;
  realT n, pivot, pivot_abs= 0.0, temp;
  int i, j, k, pivoti, flip=0;

  *nearzero= False;
  for (k=0; k < numrow; k++) {
    pivot_abs= fabs_((rows[k])[k]);
    pivoti= k;
    for (i=k+1; i < numrow; i++) {
      if ((temp= fabs_((rows[i])[k])) > pivot_abs) {
        pivot_abs= temp;
        pivoti= i;
      }
    }
    if (pivoti != k) {
      rowp= rows[pivoti];
      rows[pivoti]= rows[k];
      rows[k]= rowp;
      *sign ^= 1;
      flip ^= 1;
    }
    if (pivot_abs <= qh->NEARzero[k]) {
      *nearzero= True;
      if (pivot_abs == 0.0) {   /* remainder of column == 0 */
        if (qh->IStracing >= 4) {
          qh_fprintf(qh, qh->ferr, 8011, "qh_gausselim: 0 pivot at column %d. (%2.2g < %2.2g)\n", k, pivot_abs, qh->DISTround);
          qh_printmatrix(qh, qh->ferr, "Matrix:", rows, numrow, numcol);
        }
        zzinc_(Zgauss0);
        qh_precision(qh, "zero pivot for Gaussian elimination");
        goto LABELnextcol;
      }
    }
    pivotrow= rows[k] + k;
    pivot= *pivotrow++;  /* signed value of pivot, and remainder of row */
    for (i=k+1; i < numrow; i++) {
      ai= rows[i] + k;
      ak= pivotrow;
      n= (*ai++)/pivot;   /* divzero() not needed since |pivot| >= |*ai| */
      for (j= numcol - (k+1); j--; )
        *ai++ -= n * *ak++;
    }
  LABELnextcol:
    ;
  }
  wmin_(Wmindenom, pivot_abs);  /* last pivot element */
  if (qh->IStracing >= 5)
    qh_printmatrix(qh, qh->ferr, "qh_gausselem: result", rows, numrow, numcol);
} /* gausselim */


/*---------------------------------

  qh_getangle(qh, vect1, vect2 )
    returns the dot product of two vectors
    if qh.RANDOMdist, joggles result

  notes:
    the angle may be > 1.0 or < -1.0 because of roundoff errors

*/
realT qh_getangle(qhT *qh, pointT *vect1, pointT *vect2) {
  realT angle= 0, randr;
  int k;

  for (k=qh->hull_dim; k--; )
    angle += *vect1++ * *vect2++;
  if (qh->RANDOMdist) {
    randr= qh_RANDOMint;
    angle += (2.0 * randr / qh_RANDOMmax - 1.0) *
      qh->RANDOMfactor;
  }
  trace4((qh, qh->ferr, 4006, "qh_getangle: %2.2g\n", angle));
  return(angle);
} /* getangle */


/*---------------------------------

  qh_getcenter(qh, vertices )
    returns arithmetic center of a set of vertices as a new point

  notes:
    allocates point array for center
*/
pointT *qh_getcenter(qhT *qh, setT *vertices) {
  int k;
  pointT *center, *coord;
  vertexT *vertex, **vertexp;
  int count= qh_setsize(qh, vertices);

  if (count < 2) {
    qh_fprintf(qh, qh->ferr, 6003, "qhull internal error (qh_getcenter): not defined for %d points\n", count);
    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
  }
  center= (pointT *)qh_memalloc(qh, qh->normal_size);
  for (k=0; k < qh->hull_dim; k++) {
    coord= center+k;
    *coord= 0.0;
    FOREACHvertex_(vertices)
      *coord += vertex->point[k];
    *coord /= count;  /* count>=2 by QH6003 */
  }
  return(center);
} /* getcenter */


/*---------------------------------

  qh_getcentrum(qh, facet )
    returns the centrum for a facet as a new point

  notes:
    allocates the centrum
*/
pointT *qh_getcentrum(qhT *qh, facetT *facet) {
  realT dist;
  pointT *centrum, *point;

  point= qh_getcenter(qh, facet->vertices);
  zzinc_(Zcentrumtests);
  qh_distplane(qh, point, facet, &dist);
  centrum= qh_projectpoint(qh, point, facet, dist);
  qh_memfree(qh, point, qh->normal_size);
  trace4((qh, qh->ferr, 4007, "qh_getcentrum: for f%d, %d vertices dist= %2.2g\n",
          facet->id, qh_setsize(qh, facet->vertices), dist));
  return centrum;
} /* getcentrum */


/*---------------------------------

  qh_getdistance(qh, 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(qhT *qh, 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(qh, vertex->point, neighbor, &dist);
      if (dist < mind)
        mind= dist;
      else if (dist > maxd)
        maxd= dist;
    }
  }
  *mindist= mind;
  *maxdist= maxd;
  mind= -mind;
  if (maxd > mind)
    return maxd;
  else
    return mind;
} /* getdistance */


/*---------------------------------

  qh_normalize(qh, normal, dim, toporient )
    normalize a vector and report if too small
    does not use min norm

  see:
    qh_normalize2
*/
void qh_normalize(qhT *qh, coordT *normal, int dim, boolT toporient) {
  qh_normalize2(qh, normal, dim, toporient, NULL, NULL);
} /* normalize */

/*---------------------------------

  qh_normalize2(qh, normal, dim, toporient, minnorm, ismin )
    normalize a vector and report if too small
    qh.MINdenom/MINdenom1 are the upper limits for divide overflow

  returns:
    normalized vector
    flips sign if !toporient
    if minnorm non-NULL,
      sets ismin if normal < minnorm

  notes:
    if zero norm
       sets all elements to sqrt(1.0/dim)
    if divide by zero (divzero())
       sets largest element to   +/-1
       bumps Znearlysingular

  design:
    computes norm
    test for minnorm
    if not near zero
      normalizes normal
    else if zero norm
      sets normal to standard value
    else
      uses qh_divzero to normalize
      if nearzero
        sets norm to direction of maximum value
*/
void qh_normalize2(qhT *qh, coordT *normal, int dim, boolT toporient,
            realT *minnorm, boolT *ismin) {
  int k;
  realT *colp, *maxp, norm= 0, temp, *norm1, *norm2, *norm3;
  boolT zerodiv;

  norm1= normal+1;
  norm2= normal+2;
  norm3= normal+3;
  if (dim == 2)
    norm= sqrt((*normal)*(*normal) + (*norm1)*(*norm1));
  else if (dim == 3)
    norm= sqrt((*normal)*(*normal) + (*norm1)*(*norm1) + (*norm2)*(*norm2));
  else if (dim == 4) {
    norm= sqrt((*normal)*(*normal) + (*norm1)*(*norm1) + (*norm2)*(*norm2)
               + (*norm3)*(*norm3));
  }else if (dim > 4) {
    norm= (*normal)*(*normal) + (*norm1)*(*norm1) + (*norm2)*(*norm2)
               + (*norm3)*(*norm3);
    for (k=dim-4, colp=normal+4; k--; colp++)
      norm += (*colp) * (*colp);
    norm= sqrt(norm);
  }
  if (minnorm) {
    if (norm < *minnorm)
      *ismin= True;
    else
      *ismin= False;
  }
  wmin_(Wmindenom, norm);
  if (norm > qh->MINdenom) {
    if (!toporient)
      norm= -norm;
    *normal /= norm;
    *norm1 /= norm;
    if (dim == 2)
      ; /* all done */
    else if (dim == 3)
      *norm2 /= norm;
    else if (dim == 4) {
      *norm2 /= norm;
      *norm3 /= norm;
    }else if (dim >4) {
      *norm2 /= norm;
      *norm3 /= norm;
      for (k=dim-4, colp=normal+4; k--; )
        *colp++ /= norm;
    }
  }else if (norm == 0.0) {
    temp= sqrt(1.0/dim);
    for (k=dim, colp=normal; k--; )
      *colp++ = temp;
  }else {
    if (!toporient)
      norm= -norm;
    for (k=dim, colp=normal; k--; colp++) { /* k used below */
      temp= qh_divzero(*colp, norm, qh->MINdenom_1, &zerodiv);
      if (!zerodiv)
        *colp= temp;
      else {
        maxp= qh_maxabsval(normal, dim);
        temp= ((*maxp * norm >= 0.0) ? 1.0 : -1.0);
        for (k=dim, colp=normal; k--; colp++)
          *colp= 0.0;
        *maxp= temp;
        zzinc_(Znearlysingular);
        trace0((qh, qh->ferr, 1, "qh_normalize: norm=%2.2g too small during p%d\n",
               norm, qh->furthest_id));
        return;
      }
    }
  }
} /* normalize */


/*---------------------------------

  qh_projectpoint(qh, point, facet, dist )
    project point onto a facet by dist

  returns:
    returns a new point

  notes:
    if dist= distplane(point,facet)
      this projects point to hyperplane
    assumes qh_memfree_() is valid for normal_size
*/
pointT *qh_projectpoint(qhT *qh, pointT *point, facetT *facet, realT dist) {
  pointT *newpoint, *np, *normal;
  int normsize= qh->normal_size;
  int k;
  void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */

  qh_memalloc_(qh, normsize, freelistp, newpoint, pointT);
  np= newpoint;
  normal= facet->normal;
  for (k=qh->hull_dim; k--; )
    *(np++)= *point++ - dist * *normal++;
  return(newpoint);
} /* projectpoint */


/*---------------------------------

  qh_setfacetplane(qh, facet )
    sets the hyperplane for a facet
    if qh.RANDOMdist, joggles hyperplane

  notes:
    uses global buffers qh.gm_matrix and qh.gm_row
    overwrites facet->normal if already defined
    updates Wnewvertex if PRINTstatistics
    sets facet->upperdelaunay if upper envelope of Delaunay triangulation

  design:
    copy vertex coordinates to qh.gm_matrix/gm_row
    compute determinate
    if nearzero
      recompute determinate with gaussian elimination
      if nearzero
        force outside orientation by testing interior point
*/
void qh_setfacetplane(qhT *qh, facetT *facet) {
  pointT *point;
  vertexT *vertex, **vertexp;
  int normsize= qh->normal_size;
  int k,i, oldtrace= 0;
  realT dist;
  void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */
  coordT *coord, *gmcoord;
  pointT *point0= SETfirstt_(facet->vertices, vertexT)->point;
  boolT nearzero= False;

  zzinc_(Zsetplane);
  if (!facet->normal)
    qh_memalloc_(qh, normsize, freelistp, facet->normal, coordT);
  if (facet == qh->tracefacet) {
    oldtrace= qh->IStracing;
    qh->IStracing= 5;
    qh_fprintf(qh, qh->ferr, 8012, "qh_setfacetplane: facet f%d created.\n", facet->id);
    qh_fprintf(qh, qh->ferr, 8013, "  Last point added to hull was p%d.", qh->furthest_id);
    if (zzval_(Ztotmerge))
      qh_fprintf(qh, qh->ferr, 8014, "  Last merge was #%d.", zzval_(Ztotmerge));
    qh_fprintf(qh, qh->ferr, 8015, "\n\nCurrent summary is:\n");
      qh_printsummary(qh, qh->ferr);
  }
  if (qh->hull_dim <= 4) {
    i= 0;
    if (qh->RANDOMdist) {
      gmcoord= qh->gm_matrix;
      FOREACHvertex_(facet->vertices) {
        qh->gm_row[i++]= gmcoord;
        coord= vertex->point;
        for (k=qh->hull_dim; k--; )
          *(gmcoord++)= *coord++ * qh_randomfactor(qh, qh->RANDOMa, qh->RANDOMb);
      }
    }else {
      FOREACHvertex_(facet->vertices)
       qh->gm_row[i++]= vertex->point;
    }
    qh_sethyperplane_det(qh, qh->hull_dim, qh->gm_row, point0, facet->toporient,
                facet->normal, &facet->offset, &nearzero);
  }
  if (qh->hull_dim > 4 || nearzero) {
    i= 0;
    gmcoord= qh->gm_matrix;
    FOREACHvertex_(facet->vertices) {
      if (vertex->point != point0) {
        qh->gm_row[i++]= gmcoord;
        coord= vertex->point;
        point= point0;
        for (k=qh->hull_dim; k--; )
          *(gmcoord++)= *coord++ - *point++;
      }
    }
    qh->gm_row[i]= gmcoord;  /* for areasimplex */
    if (qh->RANDOMdist) {
      gmcoord= qh->gm_matrix;
      for (i=qh->hull_dim-1; i--; ) {
        for (k=qh->hull_dim; k--; )
          *(gmcoord++) *= qh_randomfactor(qh, qh->RANDOMa, qh->RANDOMb);
      }
    }
    qh_sethyperplane_gauss(qh, qh->hull_dim, qh->gm_row, point0, facet->toporient,
                facet->normal, &facet->offset, &nearzero);
    if (nearzero) {
      if (qh_orientoutside(qh, facet)) {
        trace0((qh, qh->ferr, 2, "qh_setfacetplane: flipped orientation 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(qh, vertex->point, facet, &dist);
        dist= fabs_(dist);
        zinc_(Znewvertex);
        wadd_(Wnewvertex, dist);
        if (dist > wwval_(Wnewvertexmax)) {
          wwval_(Wnewvertexmax)= dist;
          if (dist > qh->max_outside) {
            qh->max_outside= dist;  /* used by qh_maxouter(qh) */
            if (dist > qh->TRACEdist)
              istrace= True;
          }
        }else if (-dist > qh->TRACEdist)
          istrace= True;
        if (istrace) {
          qh_fprintf(qh, qh->ferr, 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(qh, vertex->point), vertex->id, dist, facet->id, qh->furthest_id);
          qh_errprint(qh, "DISTANT", facet, NULL, NULL, NULL);
        }
      }
    }
    qh->RANDOMdist= qh->old_randomdist;
  }
  if (qh->IStracing >= 3) {
    qh_fprintf(qh, qh->ferr, 8017, "qh_setfacetplane: f%d offset %2.2g normal: ",
             facet->id, facet->offset);
    for (k=0; k < qh->hull_dim; k++)
      qh_fprintf(qh, qh->ferr, 8018, "%2.2g ", facet->normal[k]);
    qh_fprintf(qh, qh->ferr, 8019, "\n");
  }
  if (facet == qh->tracefacet)
    qh->IStracing= oldtrace;
} /* setfacetplane */


/*---------------------------------

  qh_sethyperplane_det(qh, dim, rows, point0, toporient, normal, offset, nearzero )
    given dim X dim array indexed by rows[], one row per point,
        toporient(flips all signs),
        and point0 (any row)
    set normalized hyperplane equation from oriented simplex

  returns:
    normal (normalized)
    offset (places point0 on the hyperplane)
    sets nearzero if hyperplane not through points

  notes:
    only defined for dim == 2..4
    rows[] is not modified
    solves det(P-V_0, V_n-V_0, ..., V_1-V_0)=0, i.e. every point is on hyperplane
    see Bower & Woodworth, A programmer's geometry, Butterworths 1983.

  derivation of 3-d minnorm
    Goal: all vertices V_i within qh.one_merge of hyperplane
    Plan: exactly translate the facet so that V_0 is the origin
          exactly rotate the facet so that V_1 is on the x-axis and y_2=0.
          exactly rotate the effective perturbation to only effect n_0
             this introduces a factor of sqrt(3)
    n_0 = ((y_2-y_0)*(z_1-z_0) - (z_2-z_0)*(y_1-y_0)) / norm
    Let M_d be the max coordinate difference
    Let M_a be the greater of M_d and the max abs. coordinate
    Let u be machine roundoff and distround be max error for distance computation
    The max error for n_0 is sqrt(3) u M_a M_d / norm.  n_1 is approx. 1 and n_2 is approx. 0
    The max error for distance of V_1 is sqrt(3) u M_a M_d M_d / norm.  Offset=0 at origin
    Then minnorm = 1.8 u M_a M_d M_d / qh.ONEmerge
    Note that qh.one_merge is approx. 45.5 u M_a and norm is usually about M_d M_d

  derivation of 4-d minnorm
    same as above except rotate the facet so that V_1 on x-axis and w_2, y_3, w_3=0
     [if two vertices fixed on x-axis, can rotate the other two in yzw.]
    n_0 = det3_(...) = y_2 det2_(z_1, w_1, z_3, w_3) = - y_2 w_1 z_3
     [all other terms contain at least two factors nearly zero.]
    The max error for n_0 is sqrt(4) u M_a M_d M_d / norm
    Then minnorm = 2 u M_a M_d M_d M_d / qh.ONEmerge
    Note that qh.one_merge is approx. 82 u M_a and norm is usually about M_d M_d M_d
*/
void qh_sethyperplane_det(qhT *qh, int dim, coordT **rows, coordT *point0,
          boolT toporient, coordT *normal, realT *offset, boolT *nearzero) {
  realT maxround, dist;
  int i;
  pointT *point;


  if (dim == 2) {
    normal[0]= dY(1,0);
    normal[1]= dX(0,1);
    qh_normalize2(qh, normal, dim, toporient, NULL, NULL);
    *offset= -(point0[0]*normal[0]+point0[1]*normal[1]);
    *nearzero= False;  /* since nearzero norm => incident points */
  }else if (dim == 3) {
    normal[0]= det2_(dY(2,0), dZ(2,0),
                     dY(1,0), dZ(1,0));
    normal[1]= det2_(dX(1,0), dZ(1,0),
                     dX(2,0), dZ(2,0));
    normal[2]= det2_(dX(2,0), dY(2,0),
                     dX(1,0), dY(1,0));
    qh_normalize2(qh, normal, dim, toporient, NULL, NULL);
    *offset= -(point0[0]*normal[0] + point0[1]*normal[1]
               + point0[2]*normal[2]);
    maxround= qh->DISTround;
    for (i=dim; i--; ) {
      point= rows[i];
      if (point != point0) {
        dist= *offset + (point[0]*normal[0] + point[1]*normal[1]
               + point[2]*normal[2]);
        if (dist > maxround || dist < -maxround) {
          *nearzero= True;
          break;
        }
      }
    }
  }else if (dim == 4) {
    normal[0]= - det3_(dY(2,0), dZ(2,0), dW(2,0),
                        dY(1,0), dZ(1,0), dW(1,0),
                        dY(3,0), dZ(3,0), dW(3,0));
    normal[1]=   det3_(dX(2,0), dZ(2,0), dW(2,0),
                        dX(1,0), dZ(1,0), dW(1,0),
                        dX(3,0), dZ(3,0), dW(3,0));
    normal[2]= - det3_(dX(2,0), dY(2,0), dW(2,0),
                        dX(1,0), dY(1,0), dW(1,0),
                        dX(3,0), dY(3,0), dW(3,0));
    normal[3]=   det3_(dX(2,0), dY(2,0), dZ(2,0),
                        dX(1,0), dY(1,0), dZ(1,0),
                        dX(3,0), dY(3,0), dZ(3,0));
    qh_normalize2(qh, normal, dim, toporient, NULL, NULL);
    *offset= -(point0[0]*normal[0] + point0[1]*normal[1]
               + point0[2]*normal[2] + point0[3]*normal[3]);
    maxround= qh->DISTround;
    for (i=dim; i--; ) {
      point= rows[i];
      if (point != point0) {
        dist= *offset + (point[0]*normal[0] + point[1]*normal[1]
               + point[2]*normal[2] + point[3]*normal[3]);
        if (dist > maxround || dist < -maxround) {
          *nearzero= True;
          break;
        }
      }
    }
  }
  if (*nearzero) {
    zzinc_(Zminnorm);
    trace0((qh, qh->ferr, 3, "qh_sethyperplane_det: degenerate norm during p%d.\n", qh->furthest_id));
    zzinc_(Znearlysingular);
  }
} /* sethyperplane_det */


/*---------------------------------

  qh_sethyperplane_gauss(qh, dim, rows, point0, toporient, normal, offset, nearzero )
    given(dim-1) X dim array of rows[i]= V_{i+1} - V_0 (point0)
    set normalized hyperplane equation from oriented simplex

  returns:
    normal (normalized)
    offset (places point0 on the hyperplane)

  notes:
    if nearzero
      orientation may be incorrect because of incorrect sign flips in gausselim
    solves [V_n-V_0,...,V_1-V_0, 0 .. 0 1] * N == [0 .. 0 1]
        or [V_n-V_0,...,V_1-V_0, 0 .. 0 1] * N == [0]
    i.e., N is normal to the hyperplane, and the unnormalized
        distance to [0 .. 1] is either 1 or   0

  design:
    perform gaussian elimination
    flip sign for negative values
    perform back substitution
    normalize result
    compute offset
*/
void qh_sethyperplane_gauss(qhT *qh, int dim, coordT **rows, pointT *point0,
                boolT toporient, coordT *normal, coordT *offset, boolT *nearzero) {
  coordT *pointcoord, *normalcoef;
  int k;
  boolT sign= toporient, nearzero2= False;

  qh_gausselim(qh, rows, dim-1, dim, &sign, nearzero);
  for (k=dim-1; k--; ) {
    if ((rows[k])[k] < 0)
      sign ^= 1;
  }
  if (*nearzero) {
    zzinc_(Znearlysingular);
    trace0((qh, qh->ferr, 4, "qh_sethyperplane_gauss: nearly singular or axis parallel hyperplane during p%d.\n", qh->furthest_id));
    qh_backnormal(qh, rows, dim-1, dim, sign, normal, &nearzero2);
  }else {
    qh_backnormal(qh, rows, dim-1, dim, sign, normal, &nearzero2);
    if (nearzero2) {
      zzinc_(Znearlysingular);
      trace0((qh, qh->ferr, 5, "qh_sethyperplane_gauss: singular or axis parallel hyperplane at normalization during p%d.\n", qh->furthest_id));
    }
  }
  if (nearzero2)
    *nearzero= True;
  qh_normalize2(qh, normal, dim, True, NULL, NULL);
  pointcoord= point0;
  normalcoef= normal;
  *offset= -(*pointcoord++ * *normalcoef++);
  for (k=dim-1; k--; )
    *offset -= *pointcoord++ * *normalcoef++;
} /* sethyperplane_gauss */



geometry/src/userprintf_r.c0000644000176200001440000000505613432324643015574 0ustar  liggesusers/*
  ---------------------------------

   userprintf_r.c
   qh_fprintf()

   see README.txt  see COPYING.txt for copyright information.

   If you recompile and load this file, then userprintf_r.o will not be loaded
   from qhull.a or qhull.lib

   See libqhull_r.h for data structures, macros, and user-callable functions.
   See user_r.c for qhull-related, redefinable functions
   see user_r.h for user-definable constants
   See usermem_r.c for qh_exit(), qh_free(), and qh_malloc()
   see Qhull.cpp and RboxPoints.cpp for examples.

   Please report any errors that you fix to qhull@qhull.org
*/

#include "libqhull_r.h"

#include 
#include 
#include 

/*---------------------------------

   qh_fprintf(qh, fp, msgcode, format, list of args )
     print arguments to *fp according to format
     Use qh_fprintf_rbox() for rboxlib_r.c

   notes:
     same as fprintf()
     fgets() is not trapped like fprintf()
     exit qh_fprintf via qh_errexit()
     may be called for errors in qh_initstatistics and qh_meminit
*/

void qh_fprintf(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... ) {
    va_list args;

    if (!fp) {
        if(!qh){
            qh_fprintf_stderr(6241, "userprintf_r.c: fp and qh not defined for qh_fprintf '%s'", fmt);
            qh_exit(qhmem_ERRqhull);  /* can not use qh_errexit() */
        }
        /* could use qh->qhmem.ferr, but probably better to be cautious */
        qh_fprintf_stderr(6232, "Qhull internal error (userprintf_r.c): fp is 0.  Wrong qh_fprintf called.\n");
        qh_errexit(qh, 6232, NULL, NULL);
    }
    va_start(args, fmt);
    if (qh && qh->ANNOTATEoutput) {
      /* CHANGE TO SOURCE */
      /* fprintf(fp, "[QH%.4d]", msgcode); */
      if (fp && (fp != qh_FILEstderr)) {
        fprintf(fp, "[QH%.4d]", msgcode);
      } else {
        REprintf("[QH%.4d]", msgcode);
      }
    }else if (msgcode >= MSG_ERROR && msgcode < MSG_STDERR ) {
      /* CHANGE TO SOURCE */
      /* fprintf(fp, "QH%.4d ", msgcode); */
      if (fp && (fp != qh_FILEstderr)) {
        fprintf(fp, "QH%.4d ", msgcode);
      } else {
        REprintf("QH%.4d ", msgcode);
        REvprintf(fmt, args);
      }
    }
    /* CHANGE TO SOURCE */
    /* vfprintf(fp, fmt, args); */
    if (fp && (fp != qh_FILEstderr)) {
      vfprintf(fp, fmt, args);
    }
    va_end(args);

    /* Place debugging traps here. Use with option 'Tn' */

} /* qh_fprintf */

geometry/src/Rinhulln.c0000644000176200001440000000435314227215261014642 0ustar  liggesusers/* Copyright (C) 2015, 2017, 2019 David Sterratt
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
*/

#include "Rgeometry.h"
#include "qhull_ra.h"

/*  ch is the hull object produced by convhulln()
    p are the test points */
SEXP C_inhulln(const SEXP ch, const SEXP p)
{
  /* Get the qh object from the convhulln object */
  SEXP ptr, tag;
  qhT *qh;
  PROTECT(tag = allocVector(STRSXP, 1));
  SET_STRING_ELT(tag, 0, mkChar("convhulln"));
  PROTECT(ptr = getAttrib(ch, tag));
  if (ptr == R_NilValue) {
    error("Convex hull has no convhulln attribute");
  }
  qh = R_ExternalPtrAddr(ptr);
  UNPROTECT(2);
  
  /* Initialise return value */
  SEXP inside;
  inside = R_NilValue;

  /* Check input matrix */
  if(!isMatrix(p) || !isReal(p)){
    error("Second argument should be a real matrix.");
  }
  unsigned int dim, n;
  dim = ncols(p);
  n   = nrows(p);
  if(dim <= 0 || n <= 0){
    error("Invalid input matrix.");
  }
  if(dim != qh->hull_dim){
    error("Number of columns in test points p (%d) not equal to dimension of hull (%d).", dim, qh->hull_dim);
  }

  /* Run through the matrix using qh_findbestfacet to determine
     whether in hull or not */
  PROTECT(inside = allocVector(LGLSXP, n));
  double *point;
  point = (double *) R_alloc(dim, sizeof(double));
  boolT isoutside;
  realT bestdist;
  int i, j;
  for(i=0; i < n; i++) {
    for(j=0; j < dim; j++)
      point[j] = REAL(p)[i+n*j]; /* could have been pt_array = REAL(p) if p had been transposed */
    qh_findbestfacet(qh, point, !qh_ALL, &bestdist, &isoutside);
    LOGICAL(inside)[i] = !isoutside;
  }
  UNPROTECT(1);
  
  return inside;
}
geometry/src/geometry_init.c0000644000176200001440000000230013532164477015725 0ustar  liggesusers#include 
#include 
#include  // for NULL
#include 

/* FIXME: 
   Check these declarations against the C/Fortran source code.
*/

/* .Call calls */
extern SEXP _geometry_C_tsearch(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP);
extern SEXP C_convhulln(SEXP, SEXP, SEXP, SEXP, SEXP);
extern SEXP C_delaunayn(SEXP, SEXP, SEXP, SEXP);
extern SEXP C_halfspacen(SEXP, SEXP, SEXP, SEXP);
extern SEXP C_inhulln(SEXP, SEXP);
extern SEXP C_tsearch_orig(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP);
extern SEXP C_tsearchn(SEXP, SEXP);

static const R_CallMethodDef CallEntries[] = {
    {"_geometry_C_tsearch", (DL_FUNC) &_geometry_C_tsearch, 7},
    {"C_convhulln",         (DL_FUNC) &C_convhulln,         5},
    {"C_delaunayn",         (DL_FUNC) &C_delaunayn,         4},
    {"C_halfspacen",        (DL_FUNC) &C_halfspacen,        4},
    {"C_inhulln",           (DL_FUNC) &C_inhulln,           2},
    {"C_tsearch_orig",      (DL_FUNC) &C_tsearch_orig,      6},
    {"C_tsearchn",          (DL_FUNC) &C_tsearchn,          2},
    {NULL, NULL, 0}
};

void R_init_geometry(DllInfo *dll)
{
    R_registerRoutines(dll, NULL, CallEntries, NULL, NULL);
    R_useDynamicSymbols(dll, FALSE);
}
geometry/src/qhull_ra.h0000644000176200001440000001207613432323614014663 0ustar  liggesusers/*
  ---------------------------------

   qhull_ra.h
   all header files for compiling qhull with reentrant code
   included before C++ headers for user_r.h:QHULL_CRTDBG

   see qh-qhull.htm

   see libqhull_r.h for user-level definitions

   see user_r.h for user-definable constants

   defines internal functions for libqhull_r.c global_r.c

   Copyright (c) 1993-2015 The Geometry Center.
   $Id: //main/2015/qhull/src/libqhull_r/qhull_ra.h#6 $$Change: 2079 $
   $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $

   Notes:  grep for ((" and (" to catch fprintf("lkasdjf");
           full parens around (x?y:z)
           use '#include "libqhull_r/qhull_ra.h"' to avoid name clashes
*/

#ifndef qhDEFqhulla
#define qhDEFqhulla 1

#include "libqhull_r.h"  /* Includes user_r.h and data types */

#include "stat_r.h"
#include "random_r.h"
#include "mem_r.h"
#include "qset_r.h"
#include "geom_r.h"
#include "merge_r.h"
#include "poly_r.h"
#include "io_r.h"

#include 
#include 
#include 
#include     /* some compilers will not need float.h */
#include 
#include 
#include 
#include 
#include 
/*** uncomment here and qset_r.c
     if string.h does not define memcpy()
#include 
*/

#if qh_CLOCKtype == 2  /* defined in user_r.h from libqhull_r.h */
#include 
#include 
#include 
#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- =========== */

/*----------------------------------

  traceN((qh, qh->ferr, 0Nnnn, "format\n", vars));
    calls qh_fprintf if qh.IStracing >= N

    Add debugging traps to the end of qh_fprintf

  notes:
    removing tracing reduces code size but doesn't change execution speed
*/
#ifndef qh_NOtrace
#define trace0(args) {if (qh->IStracing) qh_fprintf args;}
#define trace1(args) {if (qh->IStracing >= 1) qh_fprintf args;}
#define trace2(args) {if (qh->IStracing >= 2) qh_fprintf args;}
#define trace3(args) {if (qh->IStracing >= 3) qh_fprintf args;}
#define trace4(args) {if (qh->IStracing >= 4) qh_fprintf args;}
#define trace5(args) {if (qh->IStracing >= 5) qh_fprintf args;}
#else /* qh_NOtrace */
#define trace0(args) {}
#define trace1(args) {}
#define trace2(args) {}
#define trace3(args) {}
#define trace4(args) {}
#define trace5(args) {}
#endif /* qh_NOtrace */

/*----------------------------------

  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 
inline void qhullUnused(T &x) { (void)x; }
#  define QHULL_UNUSED(x) qhullUnused(x);
#else
#  define QHULL_UNUSED(x) (void)x;
#endif

#ifdef __cplusplus
extern "C" {
#endif

/***** -libqhull_r.c prototypes (alphabetical after qhull) ********************/

void    qh_qhull(qhT *qh);
boolT   qh_addpoint(qhT *qh, pointT *furthest, facetT *facet, boolT checkdist);
void    qh_buildhull(qhT *qh);
void    qh_buildtracing(qhT *qh, pointT *furthest, facetT *facet);
void    qh_build_withrestart(qhT *qh);
void    qh_errexit2(qhT *qh, int exitcode, facetT *facet, facetT *otherfacet);
void    qh_findhorizon(qhT *qh, pointT *point, facetT *facet, int *goodvisible,int *goodhorizon);
pointT *qh_nextfurthest(qhT *qh, facetT **visible);
void    qh_partitionall(qhT *qh, setT *vertices, pointT *points,int npoints);
void    qh_partitioncoplanar(qhT *qh, pointT *point, facetT *facet, realT *dist);
void    qh_partitionpoint(qhT *qh, pointT *point, facetT *facet);
void    qh_partitionvisible(qhT *qh, boolT allpoints, int *numpoints);
void    qh_precision(qhT *qh, const char *reason);
void    qh_printsummary(qhT *qh, FILE *fp);

/***** -global_r.c internal prototypes (alphabetical) ***********************/

void    qh_appendprint(qhT *qh, qh_PRINT format);
void    qh_freebuild(qhT *qh, boolT allmem);
void    qh_freebuffers(qhT *qh);
void    qh_initbuffers(qhT *qh, coordT *points, int numpoints, int dim, boolT ismalloc);

/***** -stat_r.c internal prototypes (alphabetical) ***********************/

void    qh_allstatA(qhT *qh);
void    qh_allstatB(qhT *qh);
void    qh_allstatC(qhT *qh);
void    qh_allstatD(qhT *qh);
void    qh_allstatE(qhT *qh);
void    qh_allstatE2(qhT *qh);
void    qh_allstatF(qhT *qh);
void    qh_allstatG(qhT *qh);
void    qh_allstatH(qhT *qh);
void    qh_freebuffers(qhT *qh);
void    qh_initbuffers(qhT *qh, coordT *points, int numpoints, int dim, boolT ismalloc);

#ifdef __cplusplus
} /* extern "C" */
#endif

#endif /* qhDEFqhulla */
geometry/src/merge_r.c0000644000176200001440000036400113432323610014461 0ustar  liggesusers/*
  ---------------------------------

   merge_r.c
   merges non-convex facets

   see qh-merge_r.htm and merge_r.h

   other modules call qh_premerge() and qh_postmerge()

   the user may call qh_postmerge() to perform additional merges.

   To remove deleted facets and vertices (qhull() in libqhull_r.c):
     qh_partitionvisible(qh, !qh_ALL, &numoutside);  // visible_list, newfacet_list
     qh_deletevisible();         // qh.visible_list
     qh_resetlists(qh, False, qh_RESETvisible);       // qh.visible_list newvertex_list newfacet_list

   assumes qh.CENTERtype= centrum

   merges occur in qh_mergefacet and in qh_mergecycle
   vertex->neighbors not set until the first merge occurs

   Copyright (c) 1993-2015 C.B. Barber.
   $Id: //main/2015/qhull/src/libqhull_r/merge_r.c#5 $$Change: 2064 $
   $DateTime: 2016/01/18 12:36:08 $$Author: bbarber $
*/

#include "qhull_ra.h"

#ifndef qh_NOmerge

/*===== functions(alphabetical after premerge and postmerge) ======*/

/*---------------------------------

  qh_premerge(qh, 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(qhT *qh, vertexT *apex, realT maxcentrum, realT maxangle) {
  boolT othermerge= False;
  facetT *newfacet;

  if (qh->ZEROcentrum && qh_checkzero(qh, !qh_ALL))
    return;
  trace2((qh, 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);
  qh->centrum_radius= maxcentrum;
  qh->cos_max= maxangle;
  qh->degen_mergeset= qh_settemp(qh, qh->TEMPsize);
  qh->facet_mergeset= qh_settemp(qh, qh->TEMPsize);
  if (qh->hull_dim >=3) {
    qh_mark_dupridges(qh, qh->newfacet_list); /* facet_mergeset */
    qh_mergecycle_all(qh, qh->newfacet_list, &othermerge);
    qh_forcedmerges(qh, &othermerge /* qh->facet_mergeset */);
    FORALLnew_facets {  /* test samecycle merges */
      if (!newfacet->simplicial && !newfacet->mergeridge)
        qh_degen_redundant_neighbors(qh, newfacet, NULL);
    }
    if (qh_merge_degenredundant(qh))
      othermerge= True;
  }else /* qh->hull_dim == 2 */
    qh_mergecycle_all(qh, qh->newfacet_list, &othermerge);
  qh_flippedmerges(qh, qh->newfacet_list, &othermerge);
  if (!qh->MERGEexact || zzval_(Ztotmerge)) {
    zinc_(Zpremergetot);
    qh->POSTmerging= False;
    qh_getmergeset_initial(qh, qh->newfacet_list);
    qh_all_merges(qh, othermerge, False);
  }
  qh_settempfree(qh, &qh->facet_mergeset);
  qh_settempfree(qh, &qh->degen_mergeset);
} /* premerge */

/*---------------------------------

  qh_postmerge(qh, 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(qhT *qh, const char *reason, realT maxcentrum, realT maxangle,
                      boolT vneighbors) {
  facetT *newfacet;
  boolT othermerges= False;
  vertexT *vertex;

  if (qh->REPORTfreq || qh->IStracing) {
    qh_buildtracing(qh, NULL, NULL);
    qh_printsummary(qh, qh->ferr);
    if (qh->PRINTstatistics)
      qh_printallstatistics(qh, qh->ferr, "reason");
    qh_fprintf(qh, qh->ferr, 8062, "\n%s with 'C%.2g' and 'A%.2g'\n",
        reason, maxcentrum, maxangle);
  }
  trace2((qh, qh->ferr, 2009, "qh_postmerge: postmerge.  test vneighbors? %d\n",
            vneighbors));
  qh->centrum_radius= maxcentrum;
  qh->cos_max= maxangle;
  qh->POSTmerging= True;
  qh->degen_mergeset= qh_settemp(qh, qh->TEMPsize);
  qh->facet_mergeset= qh_settemp(qh, 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(qh); /* was skipped during pre-merging */
      }
    }
    if (!qh->PREmerge && !qh->MERGEexact)
      qh_flippedmerges(qh, qh->newfacet_list, &othermerges);
  }
  qh_getmergeset_initial(qh, qh->newfacet_list);
  qh_all_merges(qh, False, vneighbors);
  qh_settempfree(qh, &qh->facet_mergeset);
  qh_settempfree(qh, &qh->degen_mergeset);
} /* post_merge */

/*---------------------------------

  qh_all_merges(qh, 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(qhT *qh, 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, 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, qh->facet_mergeset)) {
      while ((merge= (mergeT*)qh_setdellast(qh->facet_mergeset))) {
        facet1= merge->facet1;
        facet2= merge->facet2;
        mergetype= merge->type;
        qh_memfree_(qh, 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(qh, facet1, facet2, mergetype);
        numdegenredun += qh_merge_degenredundant(qh);
        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(qh);  /* otherwise large post merges too slow */
      }
      qh_getmergeset(qh, 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)) {
          qh_getmergeset(qh, qh->newfacet_list); /* facet_mergeset */
          continue;
        }
      }
    }
    if (vneighbors && qh_test_vneighbors(qh /* qh->newfacet_list */))
      continue;
    break;
  } /* while (True) */
  if (qh->CHECKfrequently && !qh->MERGEexact) {
    qh->old_randomdist= qh->RANDOMdist;
    qh->RANDOMdist= False;
    qh_checkconvex(qh, qh->newfacet_list, qh_ALGORITHMfault);
    /* qh_checkconnect(qh); [this is slow and it changes the facet order] */
    qh->RANDOMdist= qh->old_randomdist;
  }
  trace1((qh, qh->ferr, 1009, "qh_all_merges: merged %d coplanar facets %d concave facets and %d degen or redundant facets.\n",
    numcoplanar, numconcave, numdegenredun));
  if (qh->IStracing >= 4 && qh->num_facets < 50)
    qh_printlists(qh);
} /* all_merges */


/*---------------------------------

  qh_appendmergeset(qh, 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(qhT *qh, 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_(qh, (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, &(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, &(qh->degen_mergeset), merge);
    else
      qh_setaddnth(qh, &(qh->degen_mergeset), 0, merge);
  }else if (mergetype == MRGredundant) {
    facet->redundant= True;
    qh_setappend(qh, &(qh->degen_mergeset), merge);
  }else /* mergetype == MRGmirror */ {
    if (facet->redundant || neighbor->redundant) {
      qh_fprintf(qh, qh->ferr, 6092, "qhull error (qh_appendmergeset): facet f%d or f%d is already a mirrored facet\n",
           facet->id, neighbor->id);
      qh_errexit2(qh, qh_ERRqhull, facet, neighbor);
    }
    if (!qh_setequal(facet->vertices, neighbor->vertices)) {
      qh_fprintf(qh, qh->ferr, 6093, "qhull error (qh_appendmergeset): mirrored facets f%d and f%d do not have the same vertices\n",
           facet->id, neighbor->id);
      qh_errexit2(qh, qh_ERRqhull, facet, neighbor);
    }
    facet->redundant= True;
    neighbor->redundant= True;
    qh_setappend(qh, &(qh->degen_mergeset), merge);
  }
} /* appendmergeset */


/*---------------------------------

  qh_basevertices(qh, samecycle )
    return temporary set of base vertices for samecycle
    samecycle is first facet in the cycle
    assumes apex is SETfirst_( samecycle->vertices )

  returns:
    vertices(settemp)
    all ->seen are cleared

  notes:
    uses qh_vertex_visit;

  design:
    for each facet in samecycle
      for each unseen vertex in facet->vertices
        append to result
*/
setT *qh_basevertices(qhT *qh, facetT *samecycle) {
  facetT *same;
  vertexT *apex, *vertex, **vertexp;
  setT *vertices= qh_settemp(qh, qh->TEMPsize);

  apex= SETfirstt_(samecycle->vertices, vertexT);
  apex->visitid= ++qh->vertex_visit;
  FORALLsame_cycle_(samecycle) {
    if (same->mergeridge)
      continue;
    FOREACHvertex_(same->vertices) {
      if (vertex->visitid != qh->vertex_visit) {
        qh_setappend(qh, &vertices, vertex);
        vertex->visitid= qh->vertex_visit;
        vertex->seen= False;
      }
    }
  }
  trace4((qh, qh->ferr, 4019, "qh_basevertices: found %d vertices\n",
         qh_setsize(qh, vertices)));
  return vertices;
} /* basevertices */

/*---------------------------------

  qh_checkconnect(qh)
    check that new facets are connected
    new facets are on qh.newfacet_list

  notes:
    this is slow and it changes the order of the facets
    uses qh.visit_id

  design:
    move first new facet to end of qh.facet_list
    for all newly appended facets
      append unvisited neighbors to end of qh.facet_list
    for all new facets
      report error if unvisited
*/
void qh_checkconnect(qhT *qh /* qh->newfacet_list */) {
  facetT *facet, *newfacet, *errfacet= NULL, *neighbor, **neighborp;

  facet= qh->newfacet_list;
  qh_removefacet(qh, facet);
  qh_appendfacet(qh, facet);
  facet->visitid= ++qh->visit_id;
  FORALLfacet_(facet) {
    FOREACHneighbor_(facet) {
      if (neighbor->visitid != qh->visit_id) {
        qh_removefacet(qh, neighbor);
        qh_appendfacet(qh, neighbor);
        neighbor->visitid= qh->visit_id;
      }
    }
  }
  FORALLnew_facets {
    if (newfacet->visitid == qh->visit_id)
      break;
    qh_fprintf(qh, qh->ferr, 6094, "qhull error: f%d is not attached to the new facets\n",
         newfacet->id);
    errfacet= newfacet;
  }
  if (errfacet)
    qh_errexit(qh, qh_ERRqhull, errfacet, NULL);
} /* checkconnect */

/*---------------------------------

  qh_checkzero(qh, testall )
    check that facets are clearly convex for qh.DISTround with qh.MERGEexact

    if testall,
      test all facets for qh.MERGEexact post-merging
    else
      test qh.newfacet_list

    if qh.MERGEexact,
      allows coplanar ridges
      skips convexity test while qh.ZEROall_ok

  returns:
    True if all facets !flipped, !dupridge, normal
         if all horizon facets are simplicial
         if all vertices are clearly below neighbor
         if all opposite vertices of horizon are below
    clears qh.ZEROall_ok if any problems or coplanar facets

  notes:
    uses qh.vertex_visit
    horizon facets may define multiple new facets

  design:
    for all facets in qh.newfacet_list or qh.facet_list
      check for flagged faults (flipped, etc.)
    for all facets in qh.newfacet_list or qh.facet_list
      for each neighbor of facet
        skip horizon facets for qh.newfacet_list
        test the opposite vertex
      if qh.newfacet_list
        test the other vertices in the facet's horizon facet
*/
boolT qh_checkzero(qhT *qh, boolT testall) {
  facetT *facet, *neighbor, **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, 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(qh, 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(qh, vertex->point, facet, &dist);
          if (dist >= -qh->DISTround) {
            qh->ZEROall_ok= False;
            if (!qh->MERGEexact || dist > qh->DISTround)
              goto LABELnonconvex;
          }
          break;
        }
      }
    }
  }
  trace2((qh, 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, qh->ferr, 2013, "qh_checkzero: facet f%d needs pre-merging\n",
       facet->id));
  return False;

 LABELnonconvex:
  trace2((qh, qh->ferr, 2014, "qh_checkzero: facet f%d and f%d are not clearly convex.  v%d dist %.2g\n",
         facet->id, neighbor->id, vertex->id, dist));
  return False;
} /* checkzero */

/*---------------------------------

  qh_compareangle(angle1, angle2 )
    used by qsort() to order merges by angle
*/
int qh_compareangle(const void *p1, const void *p2) {
  const mergeT *a= *((mergeT *const*)p1), *b= *((mergeT *const*)p2);

  return((a->angle > b->angle) ? 1 : -1);
} /* compareangle */

/*---------------------------------

  qh_comparemerge(merge1, merge2 )
    used by qsort() to order merges
*/
int qh_comparemerge(const void *p1, const void *p2) {
  const mergeT *a= *((mergeT *const*)p1), *b= *((mergeT *const*)p2);

  return(a->type - b->type);
} /* comparemerge */

/*---------------------------------

  qh_comparevisit(vertex1, vertex2 )
    used by qsort() to order vertices by their visitid
*/
int qh_comparevisit(const void *p1, const void *p2) {
  const vertexT *a= *((vertexT *const*)p1), *b= *((vertexT *const*)p2);

  return(a->visitid - b->visitid);
} /* comparevisit */

/*---------------------------------

  qh_copynonconvex(qh, atridge )
    set non-convex flag on other ridges (if any) between same neighbors

  notes:
    may be faster if use smaller ridge set

  design:
    for each ridge of atridge's top facet
      if ridge shares the same neighbor
        set nonconvex flag
*/
void qh_copynonconvex(qhT *qh, ridgeT *atridge) {
  facetT *facet, *otherfacet;
  ridgeT *ridge, **ridgep;

  facet= atridge->top;
  otherfacet= atridge->bottom;
  FOREACHridge_(facet->ridges) {
    if (otherfacet == otherfacet_(ridge, facet) && ridge != atridge) {
      ridge->nonconvex= True;
      trace4((qh, qh->ferr, 4020, "qh_copynonconvex: moved nonconvex flag from r%d to r%d\n",
              atridge->id, ridge->id));
      break;
    }
  }
} /* copynonconvex */

/*---------------------------------

  qh_degen_redundant_facet(qh, 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(qhT *qh, facetT *facet) {
  vertexT *vertex, **vertexp;
  facetT *neighbor, **neighborp;

  trace4((qh, 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(qh, facet, neighbor, MRGredundant, NULL);
      trace2((qh, qh->ferr, 2015, "qh_degen_redundant_facet: f%d is contained in f%d.  merge\n", facet->id, neighbor->id));
      return;
    }
  }
  if (qh_setsize(qh, facet->neighbors) < qh->hull_dim) {
    qh_appendmergeset(qh, facet, facet, MRGdegen, NULL);
    trace2((qh, qh->ferr, 2016, "qh_degen_redundant_neighbors: f%d is degenerate.\n", facet->id));
  }
} /* degen_redundant_facet */


/*---------------------------------

  qh_degen_redundant_neighbors(qh, 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(qhT *qh, facetT *facet, facetT *delfacet) {
  vertexT *vertex, **vertexp;
  facetT *neighbor, **neighborp;
  int size;

  trace4((qh, 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(qh, facet->neighbors)) < qh->hull_dim) {
    qh_appendmergeset(qh, facet, facet, MRGdegen, NULL);
    trace2((qh, 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(qh, neighbor, facet, MRGredundant, NULL);
      trace2((qh, 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(qh, neighbor->neighbors)) < qh->hull_dim) {
      qh_appendmergeset(qh, neighbor, neighbor, MRGdegen, NULL);
      trace2((qh, qh->ferr, 2019, "qh_degen_redundant_neighbors: f%d is degenerate with %d neighbors.  Neighbor of f%d.\n", neighbor->id, size, facet->id));
    }
  }
} /* degen_redundant_neighbors */


/*---------------------------------

  qh_find_newvertex(qh, oldvertex, vertices, ridges )
    locate new vertex for renaming old vertex
    vertices is a set of possible new vertices
      vertices sorted by number of deleted ridges

  returns:
    newvertex or NULL
      each ridge includes both 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(qhT *qh, 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, qh->ferr, 8063, "qh_find_newvertex: find new vertex for v%d from ",
             oldvertex->id);
    FOREACHvertex_(vertices)
      qh_fprintf(qh, qh->ferr, 8064, "v%d ", vertex->id);
    FOREACHridge_(ridges)
      qh_fprintf(qh, qh->ferr, 8065, "r%d ", ridge->id);
    qh_fprintf(qh, qh->ferr, 8066, "\n");
  }
#endif
  FOREACHvertex_(vertices)
    vertex->visitid= 0;
  FOREACHridge_(ridges) {
    FOREACHvertex_(ridge->vertices)
      vertex->visitid++;
  }
  FOREACHvertex_(vertices) {
    if (!vertex->visitid) {
      qh_setdelnth(qh, vertices, SETindex_(vertices,vertex));
      vertexp--; /* repeat since deleted this vertex */
    }
  }
  qh->vertex_visit += (unsigned int)qh_setsize(qh, ridges);
  if (!qh_setsize(qh, vertices)) {
    trace4((qh, qh->ferr, 4023, "qh_find_newvertex: vertices not in ridges for v%d\n",
            oldvertex->id));
    return NULL;
  }
  qsort(SETaddr_(vertices, vertexT), (size_t)qh_setsize(qh, vertices),
                sizeof(vertexT *), qh_comparevisit);
  /* can now use qh->vertex_visit */
  if (qh->PRINTstatistics) {
    size= qh_setsize(qh, vertices);
    zinc_(Zintersect);
    zadd_(Zintersecttot, size);
    zmax_(Zintersectmax, size);
  }
  hashsize= qh_newhashtable(qh, qh_setsize(qh, ridges));
  FOREACHridge_(ridges)
    qh_hashridge(qh, qh->hash_table, hashsize, ridge, oldvertex);
  FOREACHvertex_(vertices) {
    newridges= qh_vertexridges(qh, vertex);
    FOREACHridge_(newridges) {
      if (qh_hashridge_find(qh, qh->hash_table, hashsize, ridge, vertex, oldvertex, &hash)) {
        zinc_(Zdupridge);
        break;
      }
    }
    qh_settempfree(qh, &newridges);
    if (!ridge)
      break;  /* found a rename */
  }
  if (vertex) {
    /* counted in qh_renamevertex */
    trace2((qh, qh->ferr, 2020, "qh_find_newvertex: found v%d for old v%d from %d vertices and %d ridges.\n",
      vertex->id, oldvertex->id, qh_setsize(qh, vertices), qh_setsize(qh, ridges)));
  }else {
    zinc_(Zfindfail);
    trace0((qh, qh->ferr, 14, "qh_find_newvertex: no vertex for renaming v%d(all duplicated ridges) during p%d\n",
      oldvertex->id, qh->furthest_id));
  }
  qh_setfree(qh, &qh->hash_table);
  return vertex;
} /* find_newvertex */

/*---------------------------------

  qh_findbest_test(qh, testcentrum, facet, neighbor, bestfacet, dist, mindist, maxdist )
    test neighbor of facet for qh_findbestneighbor()
    if testcentrum,
      tests centrum (assumes it is defined)
    else
      tests vertices

  returns:
    if a better facet (i.e., vertices/centrum of facet closer to neighbor)
      updates bestfacet, dist, mindist, and maxdist
*/
void qh_findbest_test(qhT *qh, boolT testcentrum, facetT *facet, facetT *neighbor,
      facetT **bestfacet, realT *distp, realT *mindistp, realT *maxdistp) {
  realT dist, mindist, maxdist;

  if (testcentrum) {
    zzinc_(Zbestdist);
    qh_distplane(qh, facet->center, neighbor, &dist);
    dist *= qh->hull_dim; /* estimate furthest vertex */
    if (dist < 0) {
      maxdist= 0;
      mindist= dist;
      dist= -dist;
    }else {
      mindist= 0;
      maxdist= dist;
    }
  }else
    dist= qh_getdistance(qh, facet, neighbor, &mindist, &maxdist);
  if (dist < *distp) {
    *bestfacet= neighbor;
    *mindistp= mindist;
    *maxdistp= maxdist;
    *distp= dist;
  }
} /* findbest_test */

/*---------------------------------

  qh_findbestneighbor(qh, facet, dist, mindist, maxdist )
    finds best neighbor (least dist) of a facet for merging

  returns:
    returns min and max distances and their max absolute value

  notes:
    error if qh_ASvoronoi
    avoids merging old into new
    assumes ridge->nonconvex only set on one ridge between a pair of facets
    could use an early out predicate but not worth it

  design:
    if a large facet
      will test centrum
    else
      will test vertices
    if a large facet
      test nonconvex neighbors for best merge
    else
      test all neighbors for the best merge
    if testing centrum
      get distance information
*/
facetT *qh_findbestneighbor(qhT *qh, facetT *facet, realT *distp, realT *mindistp, realT *maxdistp) {
  facetT *neighbor, **neighborp, *bestfacet= NULL;
  ridgeT *ridge, **ridgep;
  boolT nonconvex= True, testcentrum= False;
  int size= qh_setsize(qh, facet->vertices);

  if(qh->CENTERtype==qh_ASvoronoi){
    qh_fprintf(qh, qh->ferr, 6272, "qhull error: cannot call qh_findbestneighor for f%d while qh.CENTERtype is qh_ASvoronoi\n", facet->id);
    qh_errexit(qh, qh_ERRqhull, facet, NULL);
  }
  *distp= REALmax;
  if (size > qh_BESTcentrum2 * qh->hull_dim + qh_BESTcentrum) {
    testcentrum= True;
    zinc_(Zbestcentrum);
    if (!facet->center)
       facet->center= qh_getcentrum(qh, facet);
  }
  if (size > qh->hull_dim + qh_BESTnonconvex) {
    FOREACHridge_(facet->ridges) {
      if (ridge->nonconvex) {
        neighbor= otherfacet_(ridge, facet);
        qh_findbest_test(qh, testcentrum, facet, neighbor,
                          &bestfacet, distp, mindistp, maxdistp);
      }
    }
  }
  if (!bestfacet) {
    nonconvex= False;
    FOREACHneighbor_(facet)
      qh_findbest_test(qh, testcentrum, facet, neighbor,
                        &bestfacet, distp, mindistp, maxdistp);
  }
  if (!bestfacet) {
    qh_fprintf(qh, qh->ferr, 6095, "qhull internal error (qh_findbestneighbor): no neighbors for f%d\n", facet->id);
    qh_errexit(qh, qh_ERRqhull, facet, NULL);
  }
  if (testcentrum)
    qh_getdistance(qh, facet, bestfacet, mindistp, maxdistp);
  trace3((qh, qh->ferr, 3002, "qh_findbestneighbor: f%d is best neighbor for f%d testcentrum? %d nonconvex? %d dist %2.2g min %2.2g max %2.2g\n",
     bestfacet->id, facet->id, testcentrum, nonconvex, *distp, *mindistp, *maxdistp));
  return(bestfacet);
} /* findbestneighbor */


/*---------------------------------

  qh_flippedmerges(qh, facetlist, wasmerge )
    merge flipped facets into best neighbor
    assumes qh.facet_mergeset at top of temporary stack

  returns:
    no flipped facets on facetlist
    sets wasmerge if merge occurred
    degen/redundant merges passed through

  notes:
    othermerges not needed since qh.facet_mergeset is empty before & after
      keep it in case of change

  design:
    append flipped facets to qh.facetmergeset
    for each flipped merge
      find best neighbor
      merge facet into neighbor
      merge degenerate and redundant facets
    remove flipped merges from qh.facet_mergeset
*/
void qh_flippedmerges(qhT *qh, facetT *facetlist, boolT *wasmerge) {
  facetT *facet, *neighbor, *facet1;
  realT dist, mindist, maxdist;
  mergeT *merge, **mergep;
  setT *othermerges;
  int nummerge=0;

  trace4((qh, qh->ferr, 4024, "qh_flippedmerges: begin\n"));
  FORALLfacet_(facetlist) {
    if (facet->flipped && !facet->visible)
      qh_appendmergeset(qh, facet, facet, MRGflip, NULL);
  }
  othermerges= qh_settemppop(qh); /* was facet_mergeset */
  qh->facet_mergeset= qh_settemp(qh, qh->TEMPsize);
  qh_settemppush(qh, othermerges);
  FOREACHmerge_(othermerges) {
    facet1= merge->facet1;
    if (merge->type != MRGflip || facet1->visible)
      continue;
    if (qh->TRACEmerge-1 == zzval_(Ztotmerge))
      qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel;
    neighbor= qh_findbestneighbor(qh, facet1, &dist, &mindist, &maxdist);
    trace0((qh, qh->ferr, 15, "qh_flippedmerges: merge flipped f%d into f%d dist %2.2g during p%d\n",
      facet1->id, neighbor->id, dist, qh->furthest_id));
    qh_mergefacet(qh, facet1, neighbor, &mindist, &maxdist, !qh_MERGEapex);
    nummerge++;
    if (qh->PRINTstatistics) {
      zinc_(Zflipped);
      wadd_(Wflippedtot, dist);
      wmax_(Wflippedmax, dist);
    }
    qh_merge_degenredundant(qh);
  }
  FOREACHmerge_(othermerges) {
    if (merge->facet1->visible || merge->facet2->visible)
      qh_memfree(qh, merge, (int)sizeof(mergeT));
    else
      qh_setappend(qh, &qh->facet_mergeset, merge);
  }
  qh_settempfree(qh, &othermerges);
  if (nummerge)
    *wasmerge= True;
  trace1((qh, qh->ferr, 1010, "qh_flippedmerges: merged %d flipped facets into a good neighbor\n", nummerge));
} /* flippedmerges */


/*---------------------------------

  qh_forcedmerges(qh, 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(qhT *qh, 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))
    qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel;
  trace4((qh, qh->ferr, 4025, "qh_forcedmerges: begin\n"));
  othermerges= qh_settemppop(qh); /* was facet_mergeset */
  qh->facet_mergeset= qh_settemp(qh, qh->TEMPsize);
  qh_settemppush(qh, othermerges);
  FOREACHmerge_(othermerges) {
    if (merge->type != MRGridge)
        continue;
    if (qh->TRACEmerge-1 == zzval_(Ztotmerge))
        qh->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, 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, qh_ERRqhull, facet1, facet2);
    }
    dist1= qh_getdistance(qh, facet1, facet2, &mindist1, &maxdist1);
    dist2= qh_getdistance(qh, facet2, facet1, &mindist2, &maxdist2);
    qh_check_dupridge(qh, facet1, dist1, facet2, dist2);
    if (dist1 < dist2)
      qh_mergefacet(qh, facet1, facet2, &mindist1, &maxdist1, !qh_MERGEapex);
    else {
      qh_mergefacet(qh, 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(qh, merge, (int)sizeof(mergeT));
    else
      qh_setappend(qh, &qh->facet_mergeset, merge);
  }
  qh_settempfree(qh, &othermerges);
  if (nummerge)
    *wasmerge= True;
  trace1((qh, qh->ferr, 1011, "qh_forcedmerges: merged %d facets and %d flipped facets across duplicated ridges\n",
                nummerge, numflip));
} /* forcedmerges */


/*---------------------------------

  qh_getmergeset(qh, facetlist )
    determines nonconvex facets on facetlist
    tests !tested ridges and nonconvex ridges of !tested facets

  returns:
    returns sorted qh.facet_mergeset of facet-neighbor pairs to be merged
    all ridges tested

  notes:
    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(qhT *qh, facetT *facetlist) {
  facetT *facet, *neighbor, **neighborp;
  ridgeT *ridge, **ridgep;
  int nummerges;

  nummerges= qh_setsize(qh, qh->facet_mergeset);
  trace4((qh, qh->ferr, 4026, "qh_getmergeset: started.\n"));
  qh->visit_id++;
  FORALLfacet_(facetlist) {
    if (facet->tested)
      continue;
    facet->visitid= qh->visit_id;
    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(qh, facet, neighbor))
          ridge->nonconvex= True;
      }
    }
  }
  nummerges= qh_setsize(qh, 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, qh->ferr, 2021, "qh_getmergeset: %d merges found\n", nummerges));
} /* getmergeset */


/*---------------------------------

  qh_getmergeset_initial(qh, facetlist )
    determine initial qh.facet_mergeset for facets
    tests all facet/neighbor pairs on facetlist

  returns:
    sorted qh.facet_mergeset with nonconvex ridges
    sets facet->tested, ridge->tested, and ridge->nonconvex

  notes:
    uses visit_id, assumes ridge->nonconvex is False

  see:
    qh_getmergeset()

  design:
    for each facet on facetlist
      for each untested neighbor of facet
        test facet and neighbor for convexity
        if non-convex
          append merge to qh.facet_mergeset
          mark one of the ridges as nonconvex
    sort qh.facet_mergeset by angle
*/
void qh_getmergeset_initial(qhT *qh, 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(qh, 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, 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, qh->ferr, 2022, "qh_getmergeset_initial: %d merges found\n", nummerges));
} /* getmergeset_initial */


/*---------------------------------

  qh_hashridge(qh, hashtable, hashsize, ridge, oldvertex )
    add ridge to hashtable without oldvertex

  notes:
    assumes hashtable is large enough

  design:
    determine hash value for ridge without oldvertex
    find next empty slot for ridge
*/
void qh_hashridge(qhT *qh, setT *hashtable, int hashsize, ridgeT *ridge, vertexT *oldvertex) {
  int hash;
  ridgeT *ridgeA;

  hash= qh_gethash(qh, hashsize, ridge->vertices, qh->hull_dim-1, 0, oldvertex);
  while (True) {
    if (!(ridgeA= SETelemt_(hashtable, hash, ridgeT))) {
      SETelem_(hashtable, hash)= ridge;
      break;
    }else if (ridgeA == ridge)
      break;
    if (++hash == hashsize)
      hash= 0;
  }
} /* hashridge */


/*---------------------------------

  qh_hashridge_find(qh, hashtable, hashsize, ridge, vertex, oldvertex, hashslot )
    returns matching ridge without oldvertex in hashtable
      for ridge without vertex
    if oldvertex is NULL
      matches with any one skip

  returns:
    matching ridge or NULL
    if no match,
      if ridge already in   table
        hashslot= -1
      else
        hashslot= next NULL index

  notes:
    assumes hashtable is large enough
    can't match ridge to itself

  design:
    get hash value for ridge without vertex
    for each hashslot
      return match if ridge matches ridgeA without oldvertex
*/
ridgeT *qh_hashridge_find(qhT *qh, setT *hashtable, int hashsize, ridgeT *ridge,
              vertexT *vertex, vertexT *oldvertex, int *hashslot) {
  int hash;
  ridgeT *ridgeA;

  *hashslot= 0;
  zinc_(Zhashridge);
  hash= qh_gethash(qh, hashsize, ridge->vertices, qh->hull_dim-1, 0, vertex);
  while ((ridgeA= SETelemt_(hashtable, hash, ridgeT))) {
    if (ridgeA == ridge)
      *hashslot= -1;
    else {
      zinc_(Zhashridgetest);
      if (qh_setequal_except(ridge->vertices, vertex, ridgeA->vertices, oldvertex))
        return ridgeA;
    }
    if (++hash == hashsize)
      hash= 0;
  }
  if (!*hashslot)
    *hashslot= hash;
  return NULL;
} /* hashridge_find */


/*---------------------------------

  qh_makeridges(qh, 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(qhT *qh, facetT *facet) {
  facetT *neighbor, **neighborp;
  ridgeT *ridge, **ridgep;
  int neighbor_i, neighbor_n;
  boolT toporient, mergeridge= False;

  if (!facet->simplicial)
    return;
  trace4((qh, qh->ferr, 4027, "qh_makeridges: make ridges for f%d\n", facet->id));
  facet->simplicial= False;
  FOREACHneighbor_(facet) {
    if (neighbor == qh_MERGEridge)
      mergeridge= True;
    else
      neighbor->seen= False;
  }
  FOREACHridge_(facet->ridges)
    otherfacet_(ridge, facet)->seen= True;
  FOREACHneighbor_i_(qh, facet) {
    if (neighbor == qh_MERGEridge)
      continue;  /* fixed by qh_mark_dupridges */
    else if (!neighbor->seen) {  /* no current ridges */
      ridge= qh_newridge(qh);
      ridge->vertices= qh_setnew_delnthsorted(qh, facet->vertices, qh->hull_dim,
                                                          neighbor_i, 0);
      toporient= 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(qh, &(facet->ridges), ridge);
      qh_setappend(qh, &(neighbor->ridges), ridge);
    }
  }
  if (mergeridge) {
    while (qh_setdel(facet->neighbors, qh_MERGEridge))
      ; /* delete each one */
  }
} /* makeridges */


/*---------------------------------

  qh_mark_dupridges(qh, 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(qhT *qh, facetT *facetlist) {
  facetT *facet, *neighbor, **neighborp;
  int nummerge=0;
  mergeT *merge, **mergep;


  trace4((qh, 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(qh, 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(qh, facet);
  }
  FOREACHmerge_(qh->facet_mergeset) {   /* restore the missing neighbors */
    if (merge->type == MRGridge) {
      qh_setappend(qh, &merge->facet2->neighbors, merge->facet1);
      qh_makeridges(qh, merge->facet1);   /* and the missing ridges */
    }
  }
  trace1((qh, qh->ferr, 1012, "qh_mark_dupridges: found %d duplicated ridges\n",
                nummerge));
} /* mark_dupridges */

/*---------------------------------

  qh_maydropneighbor(qh, 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(qhT *qh, facetT *facet) {
  ridgeT *ridge, **ridgep;
  realT angledegen= qh_ANGLEdegen;
  facetT *neighbor, **neighborp;

  qh->visit_id++;
  trace4((qh, qh->ferr, 4029, "qh_maydropneighbor: test f%d for no ridges to a neighbor\n",
          facet->id));
  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, 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(qh, neighbor->neighbors) < qh->hull_dim) {
        zinc_(Zdropdegen);
        qh_appendmergeset(qh, neighbor, neighbor, MRGdegen, &angledegen);
        trace2((qh, qh->ferr, 2023, "qh_maydropneighbors: f%d is degenerate.\n", neighbor->id));
      }
    }
  }
  if (qh_setsize(qh, facet->neighbors) < qh->hull_dim) {
    zinc_(Zdropdegen);
    qh_appendmergeset(qh, facet, facet, MRGdegen, &angledegen);
    trace2((qh, qh->ferr, 2024, "qh_maydropneighbors: f%d is degenerate.\n", facet->id));
  }
} /* maydropneighbor */


/*---------------------------------

  qh_merge_degenredundant(qh)
    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(qhT *qh) {
  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(qh, merge, (int)sizeof(mergeT));
    if (facet1->visible)
      continue;
    facet1->degenerate= False;
    facet1->redundant= False;
    if (qh->TRACEmerge-1 == zzval_(Ztotmerge))
      qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel;
    if (mergetype == MRGredundant) {
      zinc_(Zneighbor);
      while (facet2->visible) {
        if (!facet2->f.replace) {
          qh_fprintf(qh, 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, qh_ERRqhull, facet1, facet2);
        }
        facet2= facet2->f.replace;
      }
      if (facet1 == facet2) {
        qh_degen_redundant_facet(qh, facet1); /* in case of others */
        continue;
      }
      trace2((qh, qh->ferr, 2025, "qh_merge_degenredundant: facet f%d is contained in f%d, will merge\n",
            facet1->id, facet2->id));
      qh_mergefacet(qh, 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(qh, facet1->neighbors))) {
        zinc_(Zdelfacetdup);
        trace2((qh, qh->ferr, 2026, "qh_merge_degenredundant: facet f%d has no neighbors.  Deleted\n", facet1->id));
        qh_willdelete(qh, facet1, NULL);
        FOREACHvertex_(facet1->vertices) {
          qh_setdel(vertex->neighbors, facet1);
          if (!SETfirst_(vertex->neighbors)) {
            zinc_(Zdegenvertex);
            trace2((qh, qh->ferr, 2027, "qh_merge_degenredundant: deleted v%d because f%d has no neighbors\n",
                 vertex->id, facet1->id));
            vertex->deleted= True;
            qh_setappend(qh, &qh->del_vertices, vertex);
          }
        }
        nummerges++;
      }else if (size < qh->hull_dim) {
        bestneighbor= qh_findbestneighbor(qh, facet1, &dist, &mindist, &maxdist);
        trace2((qh, qh->ferr, 2028, "qh_merge_degenredundant: facet f%d has %d neighbors, merge into f%d dist %2.2g\n",
              facet1->id, size, bestneighbor->id, dist));
        qh_mergefacet(qh, facet1, bestneighbor, &mindist, &maxdist, !qh_MERGEapex);
        nummerges++;
        if (qh->PRINTstatistics) {
          zinc_(Zdegen);
          wadd_(Wdegentot, dist);
          wmax_(Wdegenmax, dist);
        }
      } /* else, another merge fixed the degeneracy and redundancy tested */
    }
  }
  return nummerges;
} /* merge_degenredundant */

/*---------------------------------

  qh_merge_nonconvex(qh, facet1, facet2, mergetype )
    remove non-convex ridge between facet1 into facet2
    mergetype gives why the facet's are non-convex

  returns:
    merges one of the facets into the best neighbor

  design:
    if one of the facets is a new facet
      prefer merging new facet into old facet
    find best neighbors for both facets
    merge the nearest facet into its best neighbor
    update the statistics
*/
void qh_merge_nonconvex(qhT *qh, facetT *facet1, facetT *facet2, mergeType mergetype) {
  facetT *bestfacet, *bestneighbor, *neighbor;
  realT dist, dist2, mindist, mindist2, maxdist, maxdist2;

  if (qh->TRACEmerge-1 == zzval_(Ztotmerge))
    qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel;
  trace3((qh, qh->ferr, 3003, "qh_merge_nonconvex: merge #%d for f%d and f%d type %d\n",
      zzval_(Ztotmerge) + 1, facet1->id, facet2->id, mergetype));
  /* concave or coplanar */
  if (!facet1->newfacet) {
    bestfacet= facet2;   /* avoid merging old facet if new is ok */
    facet2= facet1;
    facet1= bestfacet;
  }else
    bestfacet= facet1;
  bestneighbor= qh_findbestneighbor(qh, bestfacet, &dist, &mindist, &maxdist);
  neighbor= qh_findbestneighbor(qh, facet2, &dist2, &mindist2, &maxdist2);
  if (dist < dist2) {
    qh_mergefacet(qh, 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, 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(qh, bestfacet, bestneighbor, &mindist, &maxdist, !qh_MERGEapex);
  }else {
    qh_mergefacet(qh, facet2, neighbor, &mindist2, &maxdist2, !qh_MERGEapex);
    dist= dist2;
  }
  if (qh->PRINTstatistics) {
    if (mergetype == MRGanglecoplanar) {
      zinc_(Zacoplanar);
      wadd_(Wacoplanartot, dist);
      wmax_(Wacoplanarmax, dist);
    }else if (mergetype == MRGconcave) {
      zinc_(Zconcave);
      wadd_(Wconcavetot, dist);
      wmax_(Wconcavemax, dist);
    }else { /* MRGcoplanar */
      zinc_(Zcoplanar);
      wadd_(Wcoplanartot, dist);
      wmax_(Wcoplanarmax, dist);
    }
  }
} /* merge_nonconvex */

/*---------------------------------

  qh_mergecycle(qh, samecycle, newfacet )
    merge a cycle of facets starting at samecycle into a newfacet
    newfacet is a horizon facet with ->normal
    samecycle facets are simplicial from an apex

  returns:
    initializes vertex neighbors on first merge
    samecycle deleted (placed on qh.visible_list)
    newfacet at end of qh.facet_list
    deleted vertices on qh.del_vertices

  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(qhT *qh, 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, qh->ferr, 6224, "Qhull internal error (qh_mergecycle): does not work for tricoplanar facets.  Use option 'Q11'\n");
      qh_errexit(qh, qh_ERRqhull, newfacet, NULL);
    }
    newfacet->tricoplanar= False;
    newfacet->keepcentrum= False;
  }
  if (!qh->VERTEXneighbors)
    qh_vertexneighbors(qh);
  zzinc_(Ztotmerge);
  if (qh->REPORTfreq2 && qh->POSTmerging) {
    if (zzval_(Ztotmerge) > qh->mergereport + qh->REPORTfreq2)
      qh_tracemerging(qh);
  }
#ifndef qh_NOtrace
  if (qh->TRACEmerge == zzval_(Ztotmerge))
    qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel;
  trace2((qh, qh->ferr, 2030, "qh_mergecycle: merge #%d for facets from cycle f%d into coplanar horizon f%d\n",
        zzval_(Ztotmerge), samecycle->id, newfacet->id));
  if (newfacet == qh->tracefacet) {
    tracerestore= qh->IStracing;
    qh->IStracing= 4;
    qh_fprintf(qh, qh->ferr, 8068, "qh_mergecycle: ========= trace merge %d of samecycle %d into trace f%d, furthest is p%d\n",
               zzval_(Ztotmerge), samecycle->id, newfacet->id,  qh->furthest_id);
    traceonce= True;
  }
  if (qh->IStracing >=4) {
    qh_fprintf(qh, qh->ferr, 8069, "  same cycle:");
    FORALLsame_cycle_(samecycle)
      qh_fprintf(qh, qh->ferr, 8070, " f%d", same->id);
    qh_fprintf(qh, qh->ferr, 8071, "\n");
  }
  if (qh->IStracing >=4)
    qh_errprint(qh, "MERGING CYCLE", samecycle, newfacet, NULL, NULL);
#endif /* !qh_NOtrace */
  apex= SETfirstt_(samecycle->vertices, vertexT);
  qh_makeridges(qh, newfacet);
  qh_mergecycle_neighbors(qh, samecycle, newfacet);
  qh_mergecycle_ridges(qh, samecycle, newfacet);
  qh_mergecycle_vneighbors(qh, samecycle, newfacet);
  if (SETfirstt_(newfacet->vertices, vertexT) != apex)
    qh_setaddnth(qh, &newfacet->vertices, 0, apex);  /* apex has last id */
  if (!newfacet->newfacet)
    qh_newvertices(qh, newfacet->vertices);
  qh_mergecycle_facets(qh, samecycle, newfacet);
  qh_tracemerge(qh, samecycle, newfacet);
  /* check for degen_redundant_neighbors after qh_forcedmerges() */
  if (traceonce) {
    qh_fprintf(qh, qh->ferr, 8072, "qh_mergecycle: end of trace facet\n");
    qh->IStracing= tracerestore;
  }
} /* mergecycle */

/*---------------------------------

  qh_mergecycle_all(qh, facetlist, wasmerge )
    merge all samecycles of coplanar facets into horizon
    don't merge facets with ->mergeridge (these already have ->normal)
    all facets are simplicial from apex
    all facet->cycledone == False

  returns:
    all newfacets merged into coplanar horizon facets
    deleted vertices on  qh.del_vertices
    sets wasmerge if any merge

  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(qhT *qh, 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, 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, qh->ferr, 6225, "Qhull internal error (qh_mergecycle_all): f%d without normal\n", facet->id);
      qh_errexit(qh, qh_ERRqhull, facet, NULL);
    }
    horizon= SETfirstt_(facet->neighbors, facetT);
    if (facet->f.samecycle == facet) {
      zinc_(Zonehorizon);
      /* merge distance done in qh_findhorizon */
      apex= SETfirstt_(facet->vertices, vertexT);
      FOREACHvertex_(facet->vertices) {
        if (vertex != apex)
          vertex->delridge= True;
      }
      horizon->f.newcycle= NULL;
      qh_mergefacet(qh, facet, horizon, NULL, NULL, qh_MERGEapex);
    }else {
      samecycle= facet;
      facets= 0;
      prev= facet;
      for (same= facet->f.samecycle; same;  /* FORALLsame_cycle_(facet) */
           same= (same == facet ? NULL :nextsame)) { /* ends at facet */
        nextsame= same->f.samecycle;
        if (same->cycledone || same->visible)
          qh_infiniteloop(qh, same);
        same->cycledone= True;
        if (same->normal) {
          prev->f.samecycle= same->f.samecycle; /* unlink ->mergeridge */
          same->f.samecycle= NULL;
        }else {
          prev= same;
          facets++;
        }
      }
      while (nextfacet && nextfacet->cycledone)  /* will delete samecycle */
        nextfacet= nextfacet->next;
      horizon->f.newcycle= NULL;
      qh_mergecycle(qh, samecycle, horizon);
      nummerge= horizon->nummerge + facets;
      if (nummerge > qh_MAXnummerge)
        horizon->nummerge= qh_MAXnummerge;
      else
        horizon->nummerge= (short unsigned int)nummerge;
      zzinc_(Zcyclehorizon);
      total += facets;
      zzadd_(Zcyclefacettot, facets);
      zmax_(Zcyclefacetmax, facets);
    }
    cycles++;
  }
  if (cycles)
    *wasmerge= True;
  trace1((qh, qh->ferr, 1013, "qh_mergecycle_all: merged %d same cycles or facets into coplanar horizons\n", cycles));
} /* mergecycle_all */

/*---------------------------------

  qh_mergecycle_facets(qh, samecycle, newfacet )
    finish merge of samecycle into newfacet

  returns:
    samecycle prepended to visible_list for later deletion and partitioning
      each facet->f.replace == newfacet

    newfacet moved to end of qh.facet_list
      makes newfacet a newfacet (get's facet1->id if it was old)
      sets newfacet->newmerge
      clears newfacet->center (unless merging into a large facet)
      clears newfacet->tested and ridge->tested for facet1

    adds neighboring facets to facet_mergeset if redundant or degenerate

  design:
    make newfacet a new facet and set its flags
    move samecycle facets to qh.visible_list for later deletion
    unless newfacet is large
      remove its centrum
*/
void qh_mergecycle_facets(qhT *qh, facetT *samecycle, facetT *newfacet) {
  facetT *same, *next;

  trace4((qh, qh->ferr, 4030, "qh_mergecycle_facets: make newfacet new and samecycle deleted\n"));
  qh_removefacet(qh, newfacet);  /* append as a newfacet to end of qh->facet_list */
  qh_appendfacet(qh, newfacet);
  newfacet->newfacet= True;
  newfacet->simplicial= False;
  newfacet->newmerge= True;

  for (same= samecycle->f.samecycle; same; same= (same == samecycle ?  NULL : next)) {
    next= same->f.samecycle;  /* reused by willdelete */
    qh_willdelete(qh, same, newfacet);
  }
  if (newfacet->center
      && qh_setsize(qh, newfacet->vertices) <= qh->hull_dim + qh_MAXnewcentrum) {
    qh_memfree(qh, newfacet->center, qh->normal_size);
    newfacet->center= NULL;
  }
  trace3((qh, qh->ferr, 3004, "qh_mergecycle_facets: merged facets from cycle f%d into f%d\n",
             samecycle->id, newfacet->id));
} /* mergecycle_facets */

/*---------------------------------

  qh_mergecycle_neighbors(qh, samecycle, newfacet )
    add neighbors for samecycle facets to newfacet

  returns:
    newfacet with updated neighbors and vice-versa
    newfacet has ridges
    all neighbors of newfacet marked with qh.visit_id
    samecycle facets marked with qh.visit_id-1
    ridges updated for simplicial neighbors of samecycle with a ridge

  notes:
    assumes newfacet not in samecycle
    usually, samecycle facets are new, simplicial facets without internal ridges
      not so if horizon facet is coplanar to two different samecycles

  see:
    qh_mergeneighbors()

  design:
    check samecycle
    delete neighbors from newfacet that are also in samecycle
    for each neighbor of a facet in samecycle
      if neighbor is simplicial
        if first visit
          move the neighbor relation to newfacet
          update facet links for its ridges
        else
          make ridges for neighbor
          remove samecycle reference
      else
        update neighbor sets
*/
void qh_mergecycle_neighbors(qhT *qh, facetT *samecycle, facetT *newfacet) {
  facetT *same, *neighbor, **neighborp;
  int delneighbors= 0, newneighbors= 0;
  unsigned int samevisitid;
  ridgeT *ridge, **ridgep;

  samevisitid= ++qh->visit_id;
  FORALLsame_cycle_(samecycle) {
    if (same->visitid == samevisitid || same->visible)
      qh_infiniteloop(qh, samecycle);
    same->visitid= samevisitid;
  }
  newfacet->visitid= ++qh->visit_id;
  trace4((qh, qh->ferr, 4031, "qh_mergecycle_neighbors: delete shared neighbors from newfacet\n"));
  FOREACHneighbor_(newfacet) {
    if (neighbor->visitid == samevisitid) {
      SETref_(neighbor)= NULL;  /* samecycle neighbors deleted */
      delneighbors++;
    }else
      neighbor->visitid= qh->visit_id;
  }
  qh_setcompact(qh, newfacet->neighbors);

  trace4((qh, qh->ferr, 4032, "qh_mergecycle_neighbors: update neighbors\n"));
  FORALLsame_cycle_(samecycle) {
    FOREACHneighbor_(same) {
      if (neighbor->visitid == samevisitid)
        continue;
      if (neighbor->simplicial) {
        if (neighbor->visitid != qh->visit_id) {
          qh_setappend(qh, &newfacet->neighbors, neighbor);
          qh_setreplace(qh, neighbor->neighbors, same, newfacet);
          newneighbors++;
          neighbor->visitid= qh->visit_id;
          FOREACHridge_(neighbor->ridges) { /* update ridge in case of qh_makeridges */
            if (ridge->top == same) {
              ridge->top= newfacet;
              break;
            }else if (ridge->bottom == same) {
              ridge->bottom= newfacet;
              break;
            }
          }
        }else {
          qh_makeridges(qh, neighbor);
          qh_setdel(neighbor->neighbors, same);
          /* same can't be horizon facet for neighbor */
        }
      }else { /* non-simplicial neighbor */
        qh_setdel(neighbor->neighbors, same);
        if (neighbor->visitid != qh->visit_id) {
          qh_setappend(qh, &neighbor->neighbors, newfacet);
          qh_setappend(qh, &newfacet->neighbors, neighbor);
          neighbor->visitid= qh->visit_id;
          newneighbors++;
        }
      }
    }
  }
  trace2((qh, qh->ferr, 2032, "qh_mergecycle_neighbors: deleted %d neighbors and added %d\n",
             delneighbors, newneighbors));
} /* mergecycle_neighbors */

/*---------------------------------

  qh_mergecycle_ridges(qh, samecycle, newfacet )
    add ridges/neighbors for facets in samecycle to newfacet
    all new/old neighbors of newfacet marked with qh.visit_id
    facets in samecycle marked with qh.visit_id-1
    newfacet marked with qh.visit_id

  returns:
    newfacet has merged ridges

  notes:
    ridge already updated for simplicial neighbors of samecycle with a ridge

  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(qhT *qh, facetT *samecycle, facetT *newfacet) {
  facetT *same, *neighbor= NULL;
  int numold=0, numnew=0;
  int neighbor_i, neighbor_n;
  unsigned int samevisitid;
  ridgeT *ridge, **ridgep;
  boolT toporient;
  void **freelistp; /* used if !qh_NOmem by qh_memfree_() */

  trace4((qh, qh->ferr, 4033, "qh_mergecycle_ridges: delete shared ridges from newfacet\n"));
  samevisitid= qh->visit_id -1;
  FOREACHridge_(newfacet->ridges) {
    neighbor= otherfacet_(ridge, newfacet);
    if (neighbor->visitid == samevisitid)
      SETref_(ridge)= NULL; /* ridge free'd below */
  }
  qh_setcompact(qh, newfacet->ridges);

  trace4((qh, qh->ferr, 4034, "qh_mergecycle_ridges: add ridges to newfacet\n"));
  FORALLsame_cycle_(samecycle) {
    FOREACHridge_(same->ridges) {
      if (ridge->top == same) {
        ridge->top= newfacet;
        neighbor= ridge->bottom;
      }else if (ridge->bottom == same) {
        ridge->bottom= newfacet;
        neighbor= ridge->top;
      }else if (ridge->top == newfacet || ridge->bottom == newfacet) {
        qh_setappend(qh, &newfacet->ridges, ridge);
        numold++;  /* already set by qh_mergecycle_neighbors */
        continue;
      }else {
        qh_fprintf(qh, qh->ferr, 6098, "qhull internal error (qh_mergecycle_ridges): bad ridge r%d\n", ridge->id);
        qh_errexit(qh, qh_ERRqhull, NULL, ridge);
      }
      if (neighbor == newfacet) {
        qh_setfree(qh, &(ridge->vertices));
        qh_memfree_(qh, ridge, (int)sizeof(ridgeT), freelistp);
        numold++;
      }else if (neighbor->visitid == samevisitid) {
        qh_setdel(neighbor->ridges, ridge);
        qh_setfree(qh, &(ridge->vertices));
        qh_memfree_(qh, ridge, (int)sizeof(ridgeT), freelistp);
        numold++;
      }else {
        qh_setappend(qh, &newfacet->ridges, ridge);
        numold++;
      }
    }
    if (same->ridges)
      qh_settruncate(qh, same->ridges, 0);
    if (!same->simplicial)
      continue;
    FOREACHneighbor_i_(qh, same) {       /* note: !newfact->simplicial */
      if (neighbor->visitid != samevisitid && neighbor->simplicial) {
        ridge= qh_newridge(qh);
        ridge->vertices= qh_setnew_delnthsorted(qh, same->vertices, qh->hull_dim,
                                                          neighbor_i, 0);
        toporient= same->toporient ^ (neighbor_i & 0x1);
        if (toporient) {
          ridge->top= newfacet;
          ridge->bottom= neighbor;
        }else {
          ridge->top= neighbor;
          ridge->bottom= newfacet;
        }
        qh_setappend(qh, &(newfacet->ridges), ridge);
        qh_setappend(qh, &(neighbor->ridges), ridge);
        numnew++;
      }
    }
  }

  trace2((qh, qh->ferr, 2033, "qh_mergecycle_ridges: found %d old ridges and %d new ones\n",
             numold, numnew));
} /* mergecycle_ridges */

/*---------------------------------

  qh_mergecycle_vneighbors(qh, samecycle, newfacet )
    create vertex neighbors for newfacet from vertices of facets in samecycle
    samecycle marked with visitid == qh.visit_id - 1

  returns:
    newfacet vertices with updated neighbors
    marks newfacet with qh.visit_id-1
    deletes vertices that are merged away
    sets delridge on all vertices (faster here than in mergecycle_ridges)

  see:
    qh_mergevertex_neighbors()

  design:
    for each vertex of samecycle facet
      set vertex->delridge
      delete samecycle facets from vertex neighbors
      append newfacet to vertex neighbors
      if vertex only in newfacet
        delete it from newfacet
        add it to qh.del_vertices for later deletion
*/
void qh_mergecycle_vneighbors(qhT *qh, facetT *samecycle, facetT *newfacet) {
  facetT *neighbor, **neighborp;
  unsigned int mergeid;
  vertexT *vertex, **vertexp, *apex;
  setT *vertices;

  trace4((qh, qh->ferr, 4035, "qh_mergecycle_vneighbors: update vertex neighbors for newfacet\n"));
  mergeid= qh->visit_id - 1;
  newfacet->visitid= mergeid;
  vertices= qh_basevertices(qh, samecycle); /* temp */
  apex= SETfirstt_(samecycle->vertices, vertexT);
  qh_setappend(qh, &vertices, apex);
  FOREACHvertex_(vertices) {
    vertex->delridge= True;
    FOREACHneighbor_(vertex) {
      if (neighbor->visitid == mergeid)
        SETref_(neighbor)= NULL;
    }
    qh_setcompact(qh, vertex->neighbors);
    qh_setappend(qh, &vertex->neighbors, newfacet);
    if (!SETsecond_(vertex->neighbors)) {
      zinc_(Zcyclevertex);
      trace2((qh, qh->ferr, 2034, "qh_mergecycle_vneighbors: deleted v%d when merging cycle f%d into f%d\n",
        vertex->id, samecycle->id, newfacet->id));
      qh_setdelsorted(newfacet->vertices, vertex);
      vertex->deleted= True;
      qh_setappend(qh, &qh->del_vertices, vertex);
    }
  }
  qh_settempfree(qh, &vertices);
  trace3((qh, qh->ferr, 3005, "qh_mergecycle_vneighbors: merged vertices from cycle f%d into f%d\n",
             samecycle->id, newfacet->id));
} /* mergecycle_vneighbors */

/*---------------------------------

  qh_mergefacet(qh, 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(qhT *qh, 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, qh->ferr, 6226, "Qhull internal error (qh_mergefacet): does not work for tricoplanar facets.  Use option 'Q11'\n");
      qh_errexit2(qh, qh_ERRqhull, facet1, facet2);
    }
    if (facet2->tricoplanar) {
      facet2->tricoplanar= False;
      facet2->keepcentrum= False;
    }
  }
  zzinc_(Ztotmerge);
  if (qh->REPORTfreq2 && qh->POSTmerging) {
    if (zzval_(Ztotmerge) > qh->mergereport + qh->REPORTfreq2)
      qh_tracemerging(qh);
  }
#ifndef qh_NOtrace
  if (qh->build_cnt >= qh->RERUN) {
    if (mindist && (-*mindist > qh->TRACEdist || *maxdist > qh->TRACEdist)) {
      tracerestore= 0;
      qh->IStracing= qh->TRACElevel;
      traceonce= True;
      qh_fprintf(qh, qh->ferr, 8075, "qh_mergefacet: ========= trace wide merge #%d(%2.2g) for f%d into f%d, 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, 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, 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, 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, qh_ERRqhull, facet1, facet2);
  }
  if (qh->num_facets - qh->num_visible <= qh->hull_dim + 1) {
    qh_fprintf(qh, 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, qh->ferr, 8079, "Option 'Qx' may avoid this problem.\n");
    qh_errexit(qh, qh_ERRprec, NULL, NULL);
  }
  if (!qh->VERTEXneighbors)
    qh_vertexneighbors(qh);
  qh_makeridges(qh, facet1);
  qh_makeridges(qh, facet2);
  if (qh->IStracing >=4)
    qh_errprint(qh, "MERGING", facet1, facet2, NULL, NULL);
  if (mindist) {
    maximize_(qh->max_outside, *maxdist);
    maximize_(qh->max_vertex, *maxdist);
#if qh_MAXoutside
    maximize_(facet2->maxoutside, *maxdist);
#endif
    minimize_(qh->min_vertex, *mindist);
    if (!facet2->keepcentrum
    && (*maxdist > qh->WIDEfacet || *mindist < -qh->WIDEfacet)) {
      facet2->keepcentrum= True;
      zinc_(Zwidefacet);
    }
  }
  nummerge= facet1->nummerge + facet2->nummerge + 1;
  if (nummerge >= qh_MAXnummerge)
    facet2->nummerge= qh_MAXnummerge;
  else
    facet2->nummerge= (short unsigned int)nummerge;
  facet2->newmerge= True;
  facet2->dupridge= False;
  qh_updatetested(qh, facet1, facet2);
  if (qh->hull_dim > 2 && qh_setsize(qh, facet1->vertices) == qh->hull_dim)
    qh_mergesimplex(qh, facet1, facet2, mergeapex);
  else {
    qh->vertex_visit++;
    FOREACHvertex_(facet2->vertices)
      vertex->visitid= qh->vertex_visit;
    if (qh->hull_dim == 2)
      qh_mergefacet2d(qh, facet1, facet2);
    else {
      qh_mergeneighbors(qh, facet1, facet2);
      qh_mergevertices(qh, facet1->vertices, &facet2->vertices);
    }
    qh_mergeridges(qh, facet1, facet2);
    qh_mergevertex_neighbors(qh, facet1, facet2);
    if (!facet2->newfacet)
      qh_newvertices(qh, facet2->vertices);
  }
  if (!mergeapex)
    qh_degen_redundant_neighbors(qh, facet2, facet1);
  if (facet2->coplanar || !facet2->newfacet) {
    zinc_(Zmergeintohorizon);
  }else if (!facet1->newfacet && facet2->newfacet) {
    zinc_(Zmergehorizon);
  }else {
    zinc_(Zmergenew);
  }
  qh_willdelete(qh, facet1, facet2);
  qh_removefacet(qh, facet2);  /* append as a newfacet to end of qh->facet_list */
  qh_appendfacet(qh, facet2);
  facet2->newfacet= True;
  facet2->tested= False;
  qh_tracemerge(qh, facet1, facet2);
  if (traceonce) {
    qh_fprintf(qh, qh->ferr, 8080, "qh_mergefacet: end of wide tracing\n");
    qh->IStracing= tracerestore;
  }
} /* mergefacet */


/*---------------------------------

  qh_mergefacet2d(qh, facet1, facet2 )
    in 2d, merges neighbors and vertices of facet1 into facet2

  returns:
    build ridges for neighbors if necessary
    facet2 looks like a simplicial facet except for centrum, ridges
      neighbors are opposite the corresponding vertex
      maintains orientation of facet2

  notes:
    qh_mergefacet() retains non-simplicial structures
      they are not needed in 2d, but later routines may use them
    preserves qh.vertex_visit for qh_mergevertex_neighbors()

  design:
    get vertices and neighbors
    determine new vertices and neighbors
    set new vertices and neighbors and adjust orientation
    make ridges for new neighbor if needed
*/
void qh_mergefacet2d(qhT *qh, facetT *facet1, facetT *facet2) {
  vertexT *vertex1A, *vertex1B, *vertex2A, *vertex2B, *vertexA, *vertexB;
  facetT *neighbor1A, *neighbor1B, *neighbor2A, *neighbor2B, *neighborA, *neighborB;

  vertex1A= SETfirstt_(facet1->vertices, vertexT);
  vertex1B= SETsecondt_(facet1->vertices, vertexT);
  vertex2A= SETfirstt_(facet2->vertices, vertexT);
  vertex2B= SETsecondt_(facet2->vertices, vertexT);
  neighbor1A= SETfirstt_(facet1->neighbors, facetT);
  neighbor1B= SETsecondt_(facet1->neighbors, facetT);
  neighbor2A= SETfirstt_(facet2->neighbors, facetT);
  neighbor2B= SETsecondt_(facet2->neighbors, facetT);
  if (vertex1A == vertex2A) {
    vertexA= vertex1B;
    vertexB= vertex2B;
    neighborA= neighbor2A;
    neighborB= neighbor1A;
  }else if (vertex1A == vertex2B) {
    vertexA= vertex1B;
    vertexB= vertex2A;
    neighborA= neighbor2B;
    neighborB= neighbor1A;
  }else if (vertex1B == vertex2A) {
    vertexA= vertex1A;
    vertexB= vertex2B;
    neighborA= neighbor2A;
    neighborB= neighbor1B;
  }else { /* 1B == 2B */
    vertexA= vertex1A;
    vertexB= vertex2A;
    neighborA= neighbor2B;
    neighborB= neighbor1B;
  }
  /* vertexB always from facet2, neighborB always from facet1 */
  if (vertexA->id > vertexB->id) {
    SETfirst_(facet2->vertices)= vertexA;
    SETsecond_(facet2->vertices)= vertexB;
    if (vertexB == vertex2A)
      facet2->toporient= !facet2->toporient;
    SETfirst_(facet2->neighbors)= neighborA;
    SETsecond_(facet2->neighbors)= neighborB;
  }else {
    SETfirst_(facet2->vertices)= vertexB;
    SETsecond_(facet2->vertices)= vertexA;
    if (vertexB == vertex2B)
      facet2->toporient= !facet2->toporient;
    SETfirst_(facet2->neighbors)= neighborB;
    SETsecond_(facet2->neighbors)= neighborA;
  }
  qh_makeridges(qh, neighborB);
  qh_setreplace(qh, neighborB->neighbors, facet1, facet2);
  trace4((qh, qh->ferr, 4036, "qh_mergefacet2d: merged v%d and neighbor f%d of f%d into f%d\n",
       vertexA->id, neighborB->id, facet1->id, facet2->id));
} /* mergefacet2d */


/*---------------------------------

  qh_mergeneighbors(qh, 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(qhT *qh, facetT *facet1, facetT *facet2) {
  facetT *neighbor, **neighborp;

  trace4((qh, qh->ferr, 4037, "qh_mergeneighbors: merge neighbors of f%d and f%d\n",
          facet1->id, facet2->id));
  qh->visit_id++;
  FOREACHneighbor_(facet2) {
    neighbor->visitid= qh->visit_id;
  }
  FOREACHneighbor_(facet1) {
    if (neighbor->visitid == qh->visit_id) {
      if (neighbor->simplicial)    /* is degen, needs ridges */
        qh_makeridges(qh, neighbor);
      if (SETfirstt_(neighbor->neighbors, facetT) != facet1) /*keep newfacet->horizon*/
        qh_setdel(neighbor->neighbors, facet1);
      else {
        qh_setdel(neighbor->neighbors, facet2);
        qh_setreplace(qh, neighbor->neighbors, facet1, facet2);
      }
    }else if (neighbor != facet2) {
      qh_setappend(qh, &(facet2->neighbors), neighbor);
      qh_setreplace(qh, neighbor->neighbors, facet1, facet2);
    }
  }
  qh_setdel(facet1->neighbors, facet2);  /* here for makeridges */
  qh_setdel(facet2->neighbors, facet1);
} /* mergeneighbors */


/*---------------------------------

  qh_mergeridges(qh, facet1, facet2 )
    merges the ridge set of facet1 into facet2

  returns:
    may delete all ridges for a vertex
    sets vertex->delridge on deleted ridges

  see:
    qh_mergecycle_ridges()

  design:
    delete ridges between facet1 and facet2
      mark (delridge) vertices on these ridges for later testing
    for each remaining ridge
      rename facet1 to facet2
*/
void qh_mergeridges(qhT *qh, facetT *facet1, facetT *facet2) {
  ridgeT *ridge, **ridgep;
  vertexT *vertex, **vertexp;

  trace4((qh, 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(qh, 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(qh, &(facet2->ridges), ridge);
  }
} /* mergeridges */


/*---------------------------------

  qh_mergesimplex(qh, facet1, facet2, mergeapex )
    merge simplicial facet1 into facet2
    mergeapex==qh_MERGEapex if merging samecycle into horizon facet
      vertex id is latest (most recently created)
    facet1 may be contained in facet2
    ridges exist for both facets

  returns:
    facet2 with updated vertices, ridges, neighbors
    updated neighbors for facet1's vertices
    facet1 not deleted
    sets vertex->delridge on deleted ridges

  notes:
    special case code since this is the most common merge
    called from qh_mergefacet()

  design:
    if qh_MERGEapex
      add vertices of facet2 to qh.new_vertexlist if necessary
      add apex to facet2
    else
      for each ridge between facet1 and facet2
        set vertex->delridge
      determine the apex for facet1 (i.e., vertex to be merged)
      unless apex already in facet2
        insert apex into vertices for facet2
      add vertices of facet2 to qh.new_vertexlist if necessary
      add apex to qh.new_vertexlist if necessary
      for each vertex of facet1
        if apex
          rename facet1 to facet2 in its vertex neighbors
        else
          delete facet1 from vertex 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(qhT *qh, 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(qh, facet2->vertices);  /* apex is new */
    apex= SETfirstt_(facet1->vertices, vertexT);
    if (SETfirstt_(facet2->vertices, vertexT) != apex)
      qh_setaddnth(qh, &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, 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_(qh, facet2->vertices) {
      if (vertex->id < apex->id) {
        break;
      }else if (vertex->id == apex->id) {
        issubset= True;
        break;
      }
    }
    if (!issubset)
      qh_setaddnth(qh, &facet2->vertices, vertex_i, apex);
    if (!facet2->newfacet)
      qh_newvertices(qh, facet2->vertices);
    else if (!apex->newlist) {
      qh_removevertex(qh, apex);
      qh_appendvertex(qh, apex);
    }
  }
  trace4((qh, qh->ferr, 4040, "qh_mergesimplex: update vertex neighbors of f%d\n",
          facet1->id));
  FOREACHvertex_(facet1->vertices) {
    if (vertex == apex && !issubset)
      qh_setreplace(qh, vertex->neighbors, facet1, facet2);
    else {
      qh_setdel(vertex->neighbors, facet1);
      if (!SETsecond_(vertex->neighbors))
        qh_mergevertex_del(qh, vertex, facet1, facet2);
    }
  }
  trace4((qh, qh->ferr, 4041, "qh_mergesimplex: merge ridges and neighbors of f%d into f%d\n",
          facet1->id, facet2->id));
  qh->visit_id++;
  FOREACHneighbor_(facet2)
    neighbor->visitid= qh->visit_id;
  FOREACHridge_(facet1->ridges) {
    otherfacet= otherfacet_(ridge, facet1);
    if (otherfacet == facet2) {
      qh_setdel(facet2->ridges, ridge);
      qh_setfree(qh, &(ridge->vertices));
      qh_memfree(qh, ridge, (int)sizeof(ridgeT));
      qh_setdel(facet2->neighbors, facet1);
    }else {
      qh_setappend(qh, &facet2->ridges, ridge);
      if (otherfacet->visitid != qh->visit_id) {
        qh_setappend(qh, &facet2->neighbors, otherfacet);
        qh_setreplace(qh, otherfacet->neighbors, facet1, facet2);
        otherfacet->visitid= qh->visit_id;
      }else {
        if (otherfacet->simplicial)    /* is degen, needs ridges */
          qh_makeridges(qh, otherfacet);
        if (SETfirstt_(otherfacet->neighbors, facetT) != facet1)
          qh_setdel(otherfacet->neighbors, facet1);
        else {   /*keep newfacet->neighbors->horizon*/
          qh_setdel(otherfacet->neighbors, facet2);
          qh_setreplace(qh, 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, qh->ferr, 3006, "qh_mergesimplex: merged simplex f%d apex v%d into facet f%d\n",
          facet1->id, getid_(apex), facet2->id));
} /* mergesimplex */

/*---------------------------------

  qh_mergevertex_del(qh, vertex, facet1, facet2 )
    delete a vertex because of merging facet1 into facet2

  returns:
    deletes vertex from facet2
    adds vertex to qh.del_vertices for later deletion
*/
void qh_mergevertex_del(qhT *qh, vertexT *vertex, facetT *facet1, facetT *facet2) {

  zinc_(Zmergevertex);
  trace2((qh, qh->ferr, 2035, "qh_mergevertex_del: deleted v%d when merging f%d into f%d\n",
          vertex->id, facet1->id, facet2->id));
  qh_setdelsorted(facet2->vertices, vertex);
  vertex->deleted= True;
  qh_setappend(qh, &qh->del_vertices, vertex);
} /* mergevertex_del */

/*---------------------------------

  qh_mergevertex_neighbors(qh, facet1, facet2 )
    merge the vertex neighbors of facet1 to facet2

  returns:
    if vertex is current qh.vertex_visit
      deletes facet1 from vertex->neighbors
    else
      renames facet1 to facet2 in vertex->neighbors
    deletes vertices if only one neighbor

  notes:
    assumes vertex neighbor sets are good
*/
void qh_mergevertex_neighbors(qhT *qh, facetT *facet1, facetT *facet2) {
  vertexT *vertex, **vertexp;

  trace4((qh, qh->ferr, 4042, "qh_mergevertex_neighbors: merge vertex neighbors of f%d and f%d\n",
          facet1->id, facet2->id));
  if (qh->tracevertex) {
    qh_fprintf(qh, 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(qh, "TRACE", NULL, NULL, NULL, qh->tracevertex);
  }
  FOREACHvertex_(facet1->vertices) {
    if (vertex->visitid != qh->vertex_visit)
      qh_setreplace(qh, vertex->neighbors, facet1, facet2);
    else {
      qh_setdel(vertex->neighbors, facet1);
      if (!SETsecond_(vertex->neighbors))
        qh_mergevertex_del(qh, vertex, facet1, facet2);
    }
  }
  if (qh->tracevertex)
    qh_errprint(qh, "TRACE", NULL, NULL, NULL, qh->tracevertex);
} /* mergevertex_neighbors */


/*---------------------------------

  qh_mergevertices(qh, vertices1, vertices2 )
    merges the vertex set of facet1 into facet2

  returns:
    replaces vertices2 with merged set
    preserves vertex_visit for qh_mergevertex_neighbors
    updates qh.newvertex_list

  design:
    create a merged set of both vertices (in inverse id order)
*/
void qh_mergevertices(qhT *qh, setT *vertices1, setT **vertices2) {
  int newsize= qh_setsize(qh, vertices1)+qh_setsize(qh, *vertices2) - qh->hull_dim + 1;
  setT *mergedvertices;
  vertexT *vertex, **vertexp, **vertex2= SETaddr_(*vertices2, vertexT);

  mergedvertices= qh_settemp(qh, newsize);
  FOREACHvertex_(vertices1) {
    if (!*vertex2 || vertex->id > (*vertex2)->id)
      qh_setappend(qh, &mergedvertices, vertex);
    else {
      while (*vertex2 && (*vertex2)->id > vertex->id)
        qh_setappend(qh, &mergedvertices, *vertex2++);
      if (!*vertex2 || (*vertex2)->id < vertex->id)
        qh_setappend(qh, &mergedvertices, vertex);
      else
        qh_setappend(qh, &mergedvertices, *vertex2++);
    }
  }
  while (*vertex2)
    qh_setappend(qh, &mergedvertices, *vertex2++);
  if (newsize < qh_setsize(qh, mergedvertices)) {
    qh_fprintf(qh, qh->ferr, 6100, "qhull internal error (qh_mergevertices): facets did not share a ridge\n");
    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
  }
  qh_setfree(qh, vertices2);
  *vertices2= mergedvertices;
  qh_settemppop(qh);
} /* mergevertices */


/*---------------------------------

  qh_neighbor_intersections(qh, vertex )
    return intersection of all vertices in vertex->neighbors except for vertex

  returns:
    returns temporary set of vertices
    does not include vertex
    NULL if a neighbor is simplicial
    NULL if empty set

  notes:
    used for renaming vertices

  design:
    initialize the intersection set with vertices of the first two neighbors
    delete vertex from the intersection
    for each remaining neighbor
      intersect its vertex set with the intersection set
      return NULL if empty
    return the intersection set
*/
setT *qh_neighbor_intersections(qhT *qh, vertexT *vertex) {
  facetT *neighbor, **neighborp, *neighborA, *neighborB;
  setT *intersect;
  int neighbor_i, neighbor_n;

  FOREACHneighbor_(vertex) {
    if (neighbor->simplicial)
      return NULL;
  }
  neighborA= SETfirstt_(vertex->neighbors, facetT);
  neighborB= SETsecondt_(vertex->neighbors, facetT);
  zinc_(Zintersectnum);
  if (!neighborA)
    return NULL;
  if (!neighborB)
    intersect= qh_setcopy(qh, neighborA->vertices, 0);
  else
    intersect= qh_vertexintersect_new(qh, neighborA->vertices, neighborB->vertices);
  qh_settemppush(qh, intersect);
  qh_setdelsorted(intersect, vertex);
  FOREACHneighbor_i_(qh, vertex) {
    if (neighbor_i >= 2) {
      zinc_(Zintersectnum);
      qh_vertexintersect(qh, &intersect, neighbor->vertices);
      if (!SETfirst_(intersect)) {
        zinc_(Zintersectfail);
        qh_settempfree(qh, &intersect);
        return NULL;
      }
    }
  }
  trace3((qh, qh->ferr, 3007, "qh_neighbor_intersections: %d vertices in neighbor intersection of v%d\n",
          qh_setsize(qh, intersect), vertex->id));
  return intersect;
} /* neighbor_intersections */

/*---------------------------------

  qh_newvertices(qh, 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(qhT *qh, setT *vertices) {
  vertexT *vertex, **vertexp;

  FOREACHvertex_(vertices) {
    if (!vertex->newlist) {
      qh_removevertex(qh, vertex);
      qh_appendvertex(qh, vertex);
    }
  }
} /* newvertices */

/*---------------------------------

  qh_reducevertices(qh)
    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(qhT *qh) {
  int numshare=0, numrename= 0;
  boolT degenredun= False;
  facetT *newfacet;
  vertexT *vertex, **vertexp;

  if (qh->hull_dim == 2)
    return False;
  if (qh_merge_degenredundant(qh))
    degenredun= True;
 LABELrestart:
  FORALLnew_facets {
    if (newfacet->newmerge) {
      if (!qh->MERGEvertices)
        newfacet->newmerge= False;
      qh_remove_extravertices(qh, newfacet);
    }
  }
  if (!qh->MERGEvertices)
    return False;
  FORALLnew_facets {
    if (newfacet->newmerge) {
      newfacet->newmerge= False;
      FOREACHvertex_(newfacet->vertices) {
        if (vertex->delridge) {
          if (qh_rename_sharedvertex(qh, vertex, newfacet)) {
            numshare++;
            vertexp--; /* repeat since deleted vertex */
          }
        }
      }
    }
  }
  FORALLvertex_(qh->newvertex_list) {
    if (vertex->delridge && !vertex->deleted) {
      vertex->delridge= False;
      if (qh->hull_dim >= 4 && qh_redundant_vertex(qh, vertex)) {
        numrename++;
        if (qh_merge_degenredundant(qh)) {
          degenredun= True;
          goto LABELrestart;
        }
      }
    }
  }
  trace1((qh, qh->ferr, 1014, "qh_reducevertices: renamed %d shared vertices and %d redundant vertices. Degen? %d\n",
          numshare, numrename, degenredun));
  return degenredun;
} /* reducevertices */

/*---------------------------------

  qh_redundant_vertex(qh, 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(qhT *qh, vertexT *vertex) {
  vertexT *newvertex= NULL;
  setT *vertices, *ridges;

  trace3((qh, qh->ferr, 3008, "qh_redundant_vertex: check if v%d can be renamed\n", vertex->id));
  if ((vertices= qh_neighbor_intersections(qh, vertex))) {
    ridges= qh_vertexridges(qh, vertex);
    if ((newvertex= qh_find_newvertex(qh, vertex, vertices, ridges)))
      qh_renamevertex(qh, vertex, newvertex, ridges, NULL, NULL);
    qh_settempfree(qh, &ridges);
    qh_settempfree(qh, &vertices);
  }
  return newvertex;
} /* redundant_vertex */

/*---------------------------------

  qh_remove_extravertices(qh, 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(qhT *qh, facetT *facet) {
  ridgeT *ridge, **ridgep;
  vertexT *vertex, **vertexp;
  boolT foundrem= False;

  trace4((qh, 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(qh, vertex->neighbors)) {
        vertex->deleted= True;
        qh_setappend(qh, &qh->del_vertices, vertex);
        zinc_(Zremvertexdel);
        trace2((qh, qh->ferr, 2036, "qh_remove_extravertices: v%d deleted because it's lost all ridges\n", vertex->id));
      }else
        trace3((qh, qh->ferr, 3009, "qh_remove_extravertices: v%d removed from f%d because it's lost all ridges\n", vertex->id, facet->id));
      vertexp--; /*repeat*/
    }
  }
  return foundrem;
} /* remove_extravertices */

/*---------------------------------

  qh_rename_sharedvertex(qh, vertex, facet )
    detect and rename if shared vertex in facet
    vertices have full ->neighbors

  returns:
    newvertex or NULL
    the vertex may still exist in other facets (i.e., a neighbor was pinched)
    does not change facet->neighbors
    updates vertex->neighbors

  notes:
    a shared vertex for a facet is only in ridges to one neighbor
    this may undo a pinched facet

    it does not catch pinches involving multiple facets.  These appear
      to be difficult to detect, since an exhaustive search is too expensive.

  design:
    if vertex only has two neighbors
      determine the ridges that contain the vertex
      determine the vertices shared by both neighbors
      if can find a new vertex in this set
        rename the vertex to the new vertex
*/
vertexT *qh_rename_sharedvertex(qhT *qh, vertexT *vertex, facetT *facet) {
  facetT *neighbor, **neighborp, *neighborA= NULL;
  setT *vertices, *ridges;
  vertexT *newvertex;

  if (qh_setsize(qh, vertex->neighbors) == 2) {
    neighborA= SETfirstt_(vertex->neighbors, facetT);
    if (neighborA == facet)
      neighborA= SETsecondt_(vertex->neighbors, facetT);
  }else if (qh->hull_dim == 3)
    return NULL;
  else {
    qh->visit_id++;
    FOREACHneighbor_(facet)
      neighbor->visitid= qh->visit_id;
    FOREACHneighbor_(vertex) {
      if (neighbor->visitid == qh->visit_id) {
        if (neighborA)
          return NULL;
        neighborA= neighbor;
      }
    }
    if (!neighborA) {
      qh_fprintf(qh, qh->ferr, 6101, "qhull internal error (qh_rename_sharedvertex): v%d's neighbors not in f%d\n",
        vertex->id, facet->id);
      qh_errprint(qh, "ERRONEOUS", facet, NULL, NULL, vertex);
      qh_errexit(qh, qh_ERRqhull, NULL, NULL);
    }
  }
  /* the vertex is shared by facet and neighborA */
  ridges= qh_settemp(qh, qh->TEMPsize);
  neighborA->visitid= ++qh->visit_id;
  qh_vertexridges_facet(qh, vertex, facet, &ridges);
  trace2((qh, qh->ferr, 2037, "qh_rename_sharedvertex: p%d(v%d) is shared by f%d(%d ridges) and f%d\n",
    qh_pointid(qh, vertex->point), vertex->id, facet->id, qh_setsize(qh, ridges), neighborA->id));
  zinc_(Zintersectnum);
  vertices= qh_vertexintersect_new(qh, facet->vertices, neighborA->vertices);
  qh_setdel(vertices, vertex);
  qh_settemppush(qh, vertices);
  if ((newvertex= qh_find_newvertex(qh, vertex, vertices, ridges)))
    qh_renamevertex(qh, vertex, newvertex, ridges, facet, neighborA);
  qh_settempfree(qh, &vertices);
  qh_settempfree(qh, &ridges);
  return newvertex;
} /* rename_sharedvertex */

/*---------------------------------

  qh_renameridgevertex(qh, 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(qhT *qh, ridgeT *ridge, vertexT *oldvertex, vertexT *newvertex) {
  int nth= 0, oldnth;
  facetT *temp;
  vertexT *vertex, **vertexp;

  oldnth= qh_setindex(ridge->vertices, oldvertex);
  qh_setdelnthsorted(qh, ridge->vertices, oldnth);
  FOREACHvertex_(ridge->vertices) {
    if (vertex == newvertex) {
      zinc_(Zdelridge);
      if (ridge->nonconvex) /* only one ridge has nonconvex set */
        qh_copynonconvex(qh, ridge);
      trace2((qh, qh->ferr, 2038, "qh_renameridgevertex: ridge r%d deleted.  It contained both v%d and v%d\n",
        ridge->id, oldvertex->id, newvertex->id));
      qh_delridge(qh, ridge);
      return;
    }
    if (vertex->id < newvertex->id)
      break;
    nth++;
  }
  qh_setaddnth(qh, &ridge->vertices, nth, newvertex);
  if (abs(oldnth - nth)%2) {
    trace3((qh, qh->ferr, 3010, "qh_renameridgevertex: swapped the top and bottom of ridge r%d\n",
            ridge->id));
    temp= ridge->top;
    ridge->top= ridge->bottom;
    ridge->bottom= temp;
  }
} /* renameridgevertex */


/*---------------------------------

  qh_renamevertex(qh, 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(qhT *qh, 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(qh, ridge, oldvertex, newvertex);
  if (!oldfacet) {
    zinc_(Zrenameall);
    if (istrace)
      qh_fprintf(qh, qh->ferr, 8082, "qh_renamevertex: renamed v%d to v%d in several facets\n",
               oldvertex->id, newvertex->id);
    FOREACHneighbor_(oldvertex) {
      qh_maydropneighbor(qh, neighbor);
      qh_setdelsorted(neighbor->vertices, oldvertex);
      if (qh_remove_extravertices(qh, neighbor))
        neighborp--; /* neighbor may be deleted */
    }
    if (!oldvertex->deleted) {
      oldvertex->deleted= True;
      qh_setappend(qh, &qh->del_vertices, oldvertex);
    }
  }else if (qh_setsize(qh, oldvertex->neighbors) == 2) {
    zinc_(Zrenameshare);
    if (istrace)
      qh_fprintf(qh, qh->ferr, 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, &qh->del_vertices, oldvertex);
  }else {
    zinc_(Zrenamepinch);
    if (istrace || qh->IStracing)
      qh_fprintf(qh, 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(qh, neighborA);
  }
} /* renamevertex */


/*---------------------------------

  qh_test_appendmerge(qh, 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(qhT *qh, 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(qh, facet->normal, neighbor->normal);
    zinc_(Zangletests);
    if (angle > qh->cos_max) {
      zinc_(Zcoplanarangle);
      qh_appendmergeset(qh, facet, neighbor, MRGanglecoplanar, &angle);
      trace2((qh, qh->ferr, 2039, "qh_test_appendmerge: coplanar angle %4.4g between f%d and f%d\n",
         angle, facet->id, neighbor->id));
      return True;
    }else
      okangle= True;
  }
  if (!facet->center)
    facet->center= qh_getcentrum(qh, facet);
  zzinc_(Zcentrumtests);
  qh_distplane(qh, facet->center, neighbor, &dist);
  if (dist > qh->centrum_radius)
    isconcave= True;
  else {
    if (dist > -qh->centrum_radius)
      iscoplanar= True;
    if (!neighbor->center)
      neighbor->center= qh_getcentrum(qh, neighbor);
    zzinc_(Zcentrumtests);
    qh_distplane(qh, neighbor->center, facet, &dist2);
    if (dist2 > qh->centrum_radius)
      isconcave= True;
    else if (!iscoplanar && dist2 > -qh->centrum_radius)
      iscoplanar= True;
  }
  if (!isconcave && (!iscoplanar || (qh->MERGEexact && !qh->POSTmerging)))
    return False;
  if (!okangle && qh->ANGLEmerge) {
    angle= qh_getangle(qh, facet->normal, neighbor->normal);
    zinc_(Zangletests);
  }
  if (isconcave) {
    zinc_(Zconcaveridge);
    if (qh->ANGLEmerge)
      angle += qh_ANGLEconcave + 0.5;
    qh_appendmergeset(qh, facet, neighbor, MRGconcave, &angle);
    trace0((qh, 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(qh, facet, neighbor, MRGcoplanar, &angle);
    trace2((qh, qh->ferr, 2040, "qh_test_appendmerge: coplanar f%d to f%d dist %4.4g, reverse dist %4.4g angle %4.4g\n",
              facet->id, neighbor->id, dist, dist2, angle));
  }
  return True;
} /* test_appendmerge */

/*---------------------------------

  qh_test_vneighbors(qh)
    test vertex neighbors for convexity
    tests all facets on qh.newfacet_list

  returns:
    true if non-convex vneighbors appended to qh.facet_mergeset
    initializes vertex neighbors if needed

  notes:
    assumes all facet neighbors have been tested
    this can be expensive
    this does not guarantee that a centrum is below all facets
      but it is unlikely
    uses qh.visit_id

  design:
    build vertex neighbors if necessary
    for all new facets
      for all vertices
        for each unvisited facet neighbor of the vertex
          test new facet and neighbor for convexity
*/
boolT qh_test_vneighbors(qhT *qh /* qh->newfacet_list */) {
  facetT *newfacet, *neighbor, **neighborp;
  vertexT *vertex, **vertexp;
  int nummerges= 0;

  trace1((qh, qh->ferr, 1015, "qh_test_vneighbors: testing vertex neighbors for convexity\n"));
  if (!qh->VERTEXneighbors)
    qh_vertexneighbors(qh);
  FORALLnew_facets
    newfacet->seen= False;
  FORALLnew_facets {
    newfacet->seen= True;
    newfacet->visitid= qh->visit_id++;
    FOREACHneighbor_(newfacet)
      newfacet->visitid= qh->visit_id;
    FOREACHvertex_(newfacet->vertices) {
      FOREACHneighbor_(vertex) {
        if (neighbor->seen || neighbor->visitid == qh->visit_id)
          continue;
        if (qh_test_appendmerge(qh, newfacet, neighbor))
          nummerges++;
      }
    }
  }
  zadd_(Ztestvneighbor, nummerges);
  trace1((qh, qh->ferr, 1016, "qh_test_vneighbors: found %d non-convex, vertex neighbors\n",
           nummerges));
  return (nummerges > 0);
} /* test_vneighbors */

/*---------------------------------

  qh_tracemerge(qh, facet1, facet2 )
    print trace message after merge
*/
void qh_tracemerge(qhT *qh, facetT *facet1, facetT *facet2) {
  boolT waserror= False;

#ifndef qh_NOtrace
  if (qh->IStracing >= 4)
    qh_errprint(qh, "MERGED", facet2, NULL, NULL, NULL);
  if (facet2 == qh->tracefacet || (qh->tracevertex && qh->tracevertex->newlist)) {
    qh_fprintf(qh, 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(qh, "TRACE", qh->tracefacet,
        (qh->tracevertex && qh->tracevertex->neighbors) ?
           SETfirstt_(qh->tracevertex->neighbors, facetT) : NULL,
        NULL, qh->tracevertex);
  }
  if (qh->tracevertex) {
    if (qh->tracevertex->deleted)
      qh_fprintf(qh, qh->ferr, 8086, "qh_tracemerge: trace vertex deleted at furthest p%d\n",
            qh->furthest_id);
    else
      qh_checkvertex(qh, qh->tracevertex);
  }
  if (qh->tracefacet) {
    qh_checkfacet(qh, qh->tracefacet, True, &waserror);
    if (waserror)
      qh_errexit(qh, qh_ERRqhull, qh->tracefacet, NULL);
  }
#endif /* !qh_NOtrace */
  if (qh->CHECKfrequently || qh->IStracing >= 4) { /* can't check polygon here */
    qh_checkfacet(qh, facet2, True, &waserror);
    if (waserror)
      qh_errexit(qh, qh_ERRqhull, NULL, NULL);
  }
} /* tracemerge */

/*---------------------------------

  qh_tracemerging(qh)
    print trace message during POSTmerging

  returns:
    updates qh.mergereport

  notes:
    called from qh_mergecycle() and qh_mergefacet()

  see:
    qh_buildtracing()
*/
void qh_tracemerging(qhT *qh) {
  realT cpu;
  int total;
  time_t timedata;
  struct tm *tp;

  qh->mergereport= zzval_(Ztotmerge);
  time(&timedata);
  tp= localtime(&timedata);
  cpu= qh_CPUclock;
  cpu /= qh_SECticks;
  total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot);
  qh_fprintf(qh, qh->ferr, 8087, "\n\
At %d:%d:%d & %2.5g CPU secs, qhull has merged %d facets.  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, qh->del_vertices));
} /* tracemerging */

/*---------------------------------

  qh_updatetested(qh, facet1, facet2 )
    clear facet2->tested and facet1->ridge->tested for merge

  returns:
    deletes facet2->center unless it's already large
      if so, clears facet2->ridge->tested

  design:
    clear facet2->tested
    clear ridge->tested for facet1's ridges
    if facet2 has a centrum
      if facet2 is large
        set facet2->keepcentrum
      else if facet2 has 3 vertices due to many merges, or not large and post merging
        clear facet2->keepcentrum
      unless facet2->keepcentrum
        clear facet2->center to recompute centrum later
        clear ridge->tested for facet2's ridges
*/
void qh_updatetested(qhT *qh, facetT *facet1, facetT *facet2) {
  ridgeT *ridge, **ridgep;
  int size;

  facet2->tested= False;
  FOREACHridge_(facet1->ridges)
    ridge->tested= False;
  if (!facet2->center)
    return;
  size= qh_setsize(qh, facet2->vertices);
  if (!facet2->keepcentrum) {
    if (size > qh->hull_dim + qh_MAXnewcentrum) {
      facet2->keepcentrum= True;
      zinc_(Zwidevertices);
    }
  }else if (size <= qh->hull_dim + qh_MAXnewcentrum) {
    /* center and keepcentrum was set */
    if (size == qh->hull_dim || qh->POSTmerging)
      facet2->keepcentrum= False; /* if many merges need to recompute centrum */
  }
  if (!facet2->keepcentrum) {
    qh_memfree(qh, facet2->center, qh->normal_size);
    facet2->center= NULL;
    FOREACHridge_(facet2->ridges)
      ridge->tested= False;
  }
} /* updatetested */

/*---------------------------------

  qh_vertexridges(qh, 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(qhT *qh, vertexT *vertex) {
  facetT *neighbor, **neighborp;
  setT *ridges= qh_settemp(qh, 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(qh, vertex, neighbor, &ridges);
  }
  if (qh->PRINTstatistics || qh->IStracing) {
    size= qh_setsize(qh, ridges);
    zinc_(Zvertexridge);
    zadd_(Zvertexridgetot, size);
    zmax_(Zvertexridgemax, size);
    trace3((qh, qh->ferr, 3011, "qh_vertexridges: found %d ridges for v%d\n",
             size, vertex->id));
  }
  return ridges;
} /* vertexridges */

/*---------------------------------

  qh_vertexridges_facet(qh, vertex, facet, ridges )
    add adjacent ridges for vertex in facet
    neighbor->visitid==qh.visit_id if it hasn't been visited

  returns:
    ridges updated
    sets facet->visitid to qh.visit_id-1

  design:
    for each ridge of facet
      if ridge of visited neighbor (i.e., unprocessed)
        if vertex in ridge
          append ridge to vertex
    mark facet processed
*/
void qh_vertexridges_facet(qhT *qh, 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(qh, ridges, ridge);
  }
  facet->visitid= qh->visit_id-1;
} /* vertexridges_facet */

/*---------------------------------

  qh_willdelete(qh, facet, replace )
    moves facet to visible list
    sets facet->f.replace to replace (may be NULL)

  returns:
    bumps qh.num_visible
*/
void qh_willdelete(qhT *qh, facetT *facet, facetT *replace) {

  qh_removefacet(qh, facet);
  qh_prependfacet(qh, facet, &qh->visible_list);
  qh->num_visible++;
  facet->visible= True;
  facet->f.replace= replace;
} /* willdelete */

#else /* qh_NOmerge */
void qh_premerge(qhT *qh, vertexT *apex, realT maxcentrum, realT maxangle) {
}
void qh_postmerge(qhT *qh, const char *reason, realT maxcentrum, realT maxangle,
                      boolT vneighbors) {
}
boolT qh_checkzero(qhT *qh, boolT testall) {
   }
#endif /* qh_NOmerge */

geometry/src/stat_r.c0000644000176200001440000007314213432323610014340 0ustar  liggesusers/*
  ---------------------------------

   stat_r.c
   contains all statistics that are collected for qhull

   see qh-stat_r.htm and stat_r.h

   Copyright (c) 1993-2015 The Geometry Center.
   $Id: //main/2015/qhull/src/libqhull_r/stat_r.c#5 $$Change: 2062 $
   $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
*/

#include "qhull_ra.h"

/*========== functions in alphabetic order ================*/

/*---------------------------------

  qh_allstatA()
    define statistics in groups of 20

  notes:
    (otherwise, 'gcc -O2' uses too much memory)
    uses qhstat.next
*/
void qh_allstatA(qhT *qh) {

   /* zdef_(type,name,doc,average) */
  zzdef_(zdoc, Zdoc2, "precision statistics", -1);
  zdef_(zinc, Znewvertex, NULL, -1);
  zdef_(wadd, Wnewvertex, "ave. distance of a new vertex to a facet(!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);

  qh->qhstat.precision= qh->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(qhT *qh) {
  zzdef_(zdoc, Zdoc1, "summary information", -1);
  zdef_(zinc, Zvertices, "number of vertices in output", -1);
  zdef_(zinc, Znumfacets, "number of facets in output", -1);
  zdef_(zinc, Znonsimplicial, "number of non-simplicial facets in output", -1);
  zdef_(zinc, Znowsimplicial, "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(qhT *qh) {
  zdef_(zdoc, Zdoc9, "build hull statistics", -1);
  zzdef_(zinc, Zprocessed, "points processed", -1);
  zzdef_(zinc, Zretry, "retries due to precision problems", -1);
  zdef_(wmax, Wretrymax, "  max. random joggle", -1);
  zdef_(zmax, Zmaxvertex, "max. vertices at any one time", -1);
  zdef_(zinc, Ztotvisible, "ave. visible facets per iteration", Zprocessed);
  zdef_(zinc, Zinsidevisible, "  ave. visible facets without an horizon neighbor", Zprocessed);
  zdef_(zadd, Zvisfacettot,  "  ave. facets deleted per iteration", Zprocessed);
  zdef_(zmax, Zvisfacetmax,  "    maximum", -1);
  zdef_(zadd, Zvisvertextot, "ave. visible vertices per iteration", Zprocessed);
  zdef_(zmax, Zvisvertexmax, "    maximum", -1);
  zdef_(zinc, Ztothorizon, "ave. horizon facets per iteration", Zprocessed);
  zdef_(zadd, Znewfacettot,  "ave. new or merged facets per iteration", Zprocessed);
  zdef_(zmax, Znewfacetmax,  "    maximum(includes initial simplex)", -1);
  zdef_(wadd, Wnewbalance, "average new facet balance", Zprocessed);
  zdef_(wadd, Wnewbalance2, "  standard deviation", -1);
  zdef_(wadd, Wpbalance, "average partition balance", Zpbalance);
  zdef_(wadd, Wpbalance2, "  standard deviation", -1);
  zdef_(zinc, Zpbalance, "  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(qhT *qh) {
  zdef_(zinc, Zvisit, "resets of visit_id", -1);
  zdef_(zinc, Zvvisit, "  resets of vertex_visit", -1);
  zdef_(zmax, Zvisit2max, "  max visit_id/2", -1);
  zdef_(zmax, Zvvisit2max, "  max vertex_visit/2", -1);

  zdef_(zdoc, Zdoc4, "partitioning statistics(see previous for outer planes)", -1);
  zzdef_(zadd, Zdelvertextot, "total vertices deleted", -1);
  zdef_(zmax, Zdelvertexmax, "    maximum vertices deleted per iteration", -1);
  zdef_(zinc, Zfindbest, "calls to findbest", -1);
  zdef_(zadd, Zfindbesttot, " ave. facets tested", Zfindbest);
  zdef_(zmax, Zfindbestmax, " max. facets tested", -1);
  zdef_(zadd, Zfindcoplanar, " ave. coplanar search", Zfindbest);
  zdef_(zinc, Zfindnew, "calls to findbestnew", -1);
  zdef_(zadd, Zfindnewtot, " ave. facets tested", Zfindnew);
  zdef_(zmax, Zfindnewmax, " max. facets tested", -1);
  zdef_(zinc, Zfindnewjump, " ave. clearly better", Zfindnew);
  zdef_(zinc, Zfindnewsharp, " calls due to qh_sharpnewfacets", -1);
  zdef_(zinc, Zfindhorizon, "calls to findhorizon", -1);
  zdef_(zadd, Zfindhorizontot, " ave. facets tested", Zfindhorizon);
  zdef_(zmax, Zfindhorizonmax, " max. facets tested", -1);
  zdef_(zinc, Zfindjump,       " ave. clearly better", Zfindhorizon);
  zdef_(zinc, 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(qhT *qh) {
  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(qhT *qh) {
  zdef_(zdoc, Zdoc5, "statistics for matching ridges", -1);
  zdef_(zinc, Zhashlookup, "total lookups for matching ridges of new facets", -1);
  zdef_(zinc, Zhashtests, "average number of tests to match a ridge", Zhashlookup);
  zdef_(zinc, Zhashridge, "total lookups of subridges(duplicates and boundary)", -1);
  zdef_(zinc, Zhashridgetest, "average number of tests per subridge", Zhashridge);
  zdef_(zinc, Zdupsame, "duplicated ridges in same merge cycle", -1);
  zdef_(zinc, Zdupflip, "duplicated ridges with flipped facets", -1);

  zdef_(zdoc, Zdoc6, "statistics for determining merges", -1);
  zdef_(zinc, Zangletests, "angles computed for ridge convexity", -1);
  zdef_(zinc, Zbestcentrum, "best merges used centrum instead of vertices",-1);
  zzdef_(zinc, Zbestdist, "distance tests for best merge", -1);
  zzdef_(zinc, Zcentrumtests, "distance tests for centrum convexity", -1);
  zzdef_(zinc, 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(qhT *qh) {
  zdef_(zdoc, Zdoc7, "statistics for merging", -1);
  zdef_(zinc, Zpremergetot, "merge iterations", -1);
  zdef_(zadd, Zmergeinittot, "ave. initial non-convex ridges per iteration", Zpremergetot);
  zdef_(zadd, Zmergeinitmax, "  maximum", -1);
  zdef_(zadd, Zmergesettot, "  ave. additional non-convex ridges per iteration", Zpremergetot);
  zdef_(zadd, Zmergesetmax, "  maximum additional in one pass", -1);
  zdef_(zadd, Zmergeinittot2, "initial non-convex ridges for post merging", -1);
  zdef_(zadd, Zmergesettot2, "  additional non-convex ridges", -1);
  zdef_(wmax, Wmaxoutside, "max distance of vertex or coplanar point above facet(w/roundoff)", -1);
  zdef_(wmin, Wminvertex, "max distance of 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(qhT *qh) {
  zdef_(zinc, Zacoplanar, "merges due to angle coplanar facets", -1);
  zdef_(wadd, Wacoplanartot, "  average merge distance", Zacoplanar);
  zdef_(wmax, Wacoplanarmax, "  maximum merge distance", -1);
  zdef_(zinc, Zcoplanar, "merges due to coplanar facets", -1);
  zdef_(wadd, Wcoplanartot, "  average merge distance", Zcoplanar);
  zdef_(wmax, Wcoplanarmax, "  maximum merge distance", -1);
  zdef_(zinc, Zconcave, "merges due to concave facets", -1);
  zdef_(wadd, Wconcavetot, "  average merge distance", Zconcave);
  zdef_(wmax, Wconcavemax, "  maximum merge distance", -1);
  zdef_(zinc, 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(qhT *qh) {
  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, outside and coplanar sets, and qhT",-1);
  zdef_(zadd, Zmemridges, "for ridges and their vertex sets", -1);
} /* allstat */

void qh_allstatI(qhT *qh) {
  qh->qhstat.vridges= qh->qhstat.next;
  zzdef_(zdoc, Zdoc11, "Voronoi ridge statistics", -1);
  zzdef_(zinc, Zridge, "non-simplicial Voronoi vertices for all ridges", -1);
  zzdef_(wadd, Wridge, "  ave. distance to ridge", Zridge);
  zzdef_(wmax, Wridgemax, "  max. distance to ridge", -1);
  zzdef_(zinc, Zridgemid, "bounded ridges", -1);
  zzdef_(wadd, Wridgemid, "  ave. distance of midpoint to ridge", Zridgemid);
  zzdef_(wmax, Wridgemidmax, "  max. distance of midpoint to ridge", -1);
  zzdef_(zinc, Zridgeok, "bounded ridges with ok normal", -1);
  zzdef_(wadd, Wridgeok, "  ave. angle to ridge", Zridgeok);
  zzdef_(wmax, Wridgeokmax, "  max. angle to ridge", -1);
  zzdef_(zinc, Zridge0, "bounded ridges with near-zero normal", -1);
  zzdef_(wadd, Wridge0, "  ave. angle to ridge", Zridge0);
  zzdef_(wmax, Wridge0max, "  max. angle to ridge", -1);

  zdef_(zdoc, Zdoc12, "Triangulation statistics(Qt)", -1);
  zdef_(zinc, Ztricoplanar, "non-simplicial facets triangulated", -1);
  zdef_(zadd, Ztricoplanartot, "  ave. new facets created(may be deleted)", Ztricoplanar);
  zdef_(zmax, Ztricoplanarmax, "  max. new facets created", -1);
  zdef_(zinc, Ztrinull, "null new facets deleted(duplicated vertex)", -1);
  zdef_(zinc, Ztrimirror, "mirrored pairs of new facets deleted(same vertices)", -1);
  zdef_(zinc, Ztridegen, "degenerate new facets in output(same ridge)", -1);
} /* allstat */

/*---------------------------------

  qh_allstatistics()
    reset printed flag for all statistics
*/
void qh_allstatistics(qhT *qh) {
  int i;

  for(i=ZEND; i--; )
    qh->qhstat.printed[i]= False;
} /* allstatistics */

#if qh_KEEPstatistics
/*---------------------------------

  qh_collectstatistics()
    collect statistics for qh.facet_list

*/
void qh_collectstatistics(qhT *qh) {
  facetT *facet, *neighbor, **neighborp;
  vertexT *vertex, **vertexp;
  realT dotproduct, dist;
  int sizneighbors, sizridges, sizvertices, i;

  qh->old_randomdist= qh->RANDOMdist;
  qh->RANDOMdist= False;
  zval_(Zmempoints)= qh->num_points * qh->normal_size + sizeof(qhT);
  zval_(Zmemfacets)= 0;
  zval_(Zmemridges)= 0;
  zval_(Zmemvertices)= 0;
  zval_(Zangle)= 0;
  wval_(Wangle)= 0.0;
  zval_(Znumridges)= 0;
  zval_(Znumfacets)= 0;
  zval_(Znumneighbors)= 0;
  zval_(Znumvertices)= 0;
  zval_(Znumvneighbors)= 0;
  zval_(Znummergetot)= 0;
  zval_(Znummergemax)= 0;
  zval_(Zvertices)= qh->num_vertices - qh_setsize(qh, qh->del_vertices);
  if (qh->MERGING || qh->APPROXhull || qh->JOGGLEmax < REALmax/2)
    wmax_(Wmaxoutside, qh->max_outside);
  if (qh->MERGING)
    wmin_(Wminvertex, qh->min_vertex);
  FORALLfacets
    facet->seen= False;
  if (qh->DELAUNAY) {
    FORALLfacets {
      if (facet->upperdelaunay != qh->UPPERdelaunay)
        facet->seen= True; /* remove from angle statistics */
    }
  }
  FORALLfacets {
    if (facet->visible && qh->NEWfacets)
      continue;
    sizvertices= qh_setsize(qh, facet->vertices);
    sizneighbors= qh_setsize(qh, facet->neighbors);
    sizridges= qh_setsize(qh, facet->ridges);
    zinc_(Znumfacets);
    zadd_(Znumvertices, sizvertices);
    zmax_(Zmaxvertices, sizvertices);
    zadd_(Znumneighbors, sizneighbors);
    zmax_(Zmaxneighbors, sizneighbors);
    zadd_(Znummergetot, facet->nummerge);
    i= facet->nummerge; /* avoid warnings */
    zmax_(Znummergemax, i);
    if (!facet->simplicial) {
      if (sizvertices == qh->hull_dim) {
        zinc_(Znowsimplicial);
      }else {
        zinc_(Znonsimplicial);
      }
    }
    if (sizridges) {
      zadd_(Znumridges, sizridges);
      zmax_(Zmaxridges, sizridges);
    }
    zadd_(Zmemfacets, 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(qh, facet->outsideset));
    if (facet->coplanarset)
      zadd_(Zmempoints, sizeof(setT) + SETelemsize * qh_setsize(qh, facet->coplanarset));
    if (facet->seen) /* Delaunay upper envelope */
      continue;
    facet->seen= True;
    FOREACHneighbor_(facet) {
      if (neighbor == qh_DUPLICATEridge || neighbor == qh_MERGEridge
          || neighbor->seen || !facet->normal || !neighbor->normal)
        continue;
      dotproduct= qh_getangle(qh, facet->normal, neighbor->normal);
      zinc_(Zangle);
      wadd_(Wangle, dotproduct);
      wmax_(Wanglemax, dotproduct)
      wmin_(Wanglemin, dotproduct)
    }
    if (facet->normal) {
      FOREACHvertex_(facet->vertices) {
        zinc_(Zdiststat);
        qh_distplane(qh, vertex->point, facet, &dist);
        wmax_(Wvertexmax, dist);
        wmin_(Wvertexmin, dist);
      }
    }
  }
  FORALLvertices {
    if (vertex->deleted)
      continue;
    zadd_(Zmemvertices, sizeof(vertexT));
    if (vertex->neighbors) {
      sizneighbors= qh_setsize(qh, vertex->neighbors);
      zadd_(Znumvneighbors, sizneighbors);
      zmax_(Zmaxvneighbors, sizneighbors);
      zadd_(Zmemvertices, sizeof(vertexT) + SETelemsize * sizneighbors);
    }
  }
  qh->RANDOMdist= qh->old_randomdist;
} /* collectstatistics */
#endif /* qh_KEEPstatistics */

/*---------------------------------

  qh_initstatistics(qh)
    initialize statistics

  notes:
  NOerrors -- qh_initstatistics can not use qh_errexit(), qh_fprintf, or qh.ferr
  On first call, only qhmem.ferr is defined.  qh_memalloc is not setup.
  Also invoked by QhullQh().
*/
void qh_initstatistics(qhT *qh) {
  int i;
  realT realx;
  int intx;

  qh->qhstat.next= 0;
  qh_allstatA(qh);
  qh_allstatB(qh);
  qh_allstatC(qh);
  qh_allstatD(qh);
  qh_allstatE(qh);
  qh_allstatE2(qh);
  qh_allstatF(qh);
  qh_allstatG(qh);
  qh_allstatH(qh);
  qh_allstatI(qh);
  if (qh->qhstat.next > (int)sizeof(qh->qhstat.id)) {
    qh_fprintf(qh, qh->qhmem.ferr, 6184, "qhull error (qh_initstatistics): increase size of qhstat.id[].\n\
      qhstat.next %d should be <= sizeof(qh->qhstat.id) %d\n", qh->qhstat.next, (int)sizeof(qh->qhstat.id));
#if 0 /* for locating error, Znumridges should be duplicated */
    for(i=0; i < ZEND; i++) {
      int j;
      for(j=i+1; j < ZEND; j++) {
        if (qh->qhstat.id[i] == qh->qhstat.id[j]) {
          qh_fprintf(qh, qh->qhmem.ferr, 6185, "qhull error (qh_initstatistics): duplicated statistic %d at indices %d and %d\n",
              qh->qhstat.id[i], i, j);
        }
      }
    }
#endif
    qh_exit(qh_ERRqhull);  /* can not use qh_errexit() */
  }
  qh->qhstat.init[zinc].i= 0;
  qh->qhstat.init[zadd].i= 0;
  qh->qhstat.init[zmin].i= INT_MAX;
  qh->qhstat.init[zmax].i= INT_MIN;
  qh->qhstat.init[wadd].r= 0;
  qh->qhstat.init[wmin].r= REALmax;
  qh->qhstat.init[wmax].r= -REALmax;
  for(i=0; i < ZEND; i++) {
    if (qh->qhstat.type[i] > ZTYPEreal) {
      realx= qh->qhstat.init[(unsigned char)(qh->qhstat.type[i])].r;
      qh->qhstat.stats[i].r= realx;
    }else if (qh->qhstat.type[i] != zdoc) {
      intx= qh->qhstat.init[(unsigned char)(qh->qhstat.type[i])].i;
      qh->qhstat.stats[i].i= intx;
    }
  }
} /* initstatistics */

/*---------------------------------

  qh_newstats(qh, )
    returns True if statistics for zdoc

  returns:
    next zdoc
*/
boolT qh_newstats(qhT *qh, int idx, int *nextindex) {
  boolT isnew= False;
  int start, i;

  if (qh->qhstat.type[qh->qhstat.id[idx]] == zdoc)
    start= idx+1;
  else
    start= idx;
  for(i= start; i < qh->qhstat.next && qh->qhstat.type[qh->qhstat.id[i]] != zdoc; i++) {
    if (!qh_nostatistic(qh, qh->qhstat.id[i]) && !qh->qhstat.printed[qh->qhstat.id[i]])
        isnew= True;
  }
  *nextindex= i;
  return isnew;
} /* newstats */

/*---------------------------------

  qh_nostatistic(qh, index )
    true if no statistic to print
*/
boolT qh_nostatistic(qhT *qh, int i) {

  if ((qh->qhstat.type[i] > ZTYPEreal
       &&qh->qhstat.stats[i].r == qh->qhstat.init[(unsigned char)(qh->qhstat.type[i])].r)
      || (qh->qhstat.type[i] < ZTYPEreal
          &&qh->qhstat.stats[i].i == qh->qhstat.init[(unsigned char)(qh->qhstat.type[i])].i))
    return True;
  return False;
} /* nostatistic */

#if qh_KEEPstatistics
/*---------------------------------

  qh_printallstatistics(qh, fp, string )
    print all statistics with header 'string'
*/
void qh_printallstatistics(qhT *qh, FILE *fp, const char *string) {

  qh_allstatistics(qh);
  qh_collectstatistics(qh);
  qh_printstatistics(qh, fp, string);
  qh_memstatistics(qh, fp);
}


/*---------------------------------

  qh_printstatistics(qh, fp, string )
    print statistics to a file with header 'string'
    skips statistics with qhstat.printed[] (reset with qh_allstatistics)

  see:
    qh_printallstatistics()
*/
void qh_printstatistics(qhT *qh, FILE *fp, const char *string) {
  int i, k;
  realT ave;

  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(qh, fp, 9350, "\n\
%s\n\
 qhull invoked by: %s | %s\n%s with options:\n%s\n", string, qh->rbox_command,
     qh->qhull_command, qh_version, qh->qhull_options);
  qh_fprintf(qh, fp, 9351, "\nprecision constants:\n\
 %6.2g max. abs. coordinate in the (transformed) input('Qbd:n')\n\
 %6.2g max. roundoff error for distance computation('En')\n\
 %6.2g max. roundoff error for angle computations\n\
 %6.2g min. distance for outside points ('Wn')\n\
 %6.2g min. distance for visible facets ('Vn')\n\
 %6.2g max. distance for coplanar facets ('Un')\n\
 %6.2g max. facet width for recomputing centrum and area\n\
",
  qh->MAXabs_coord, qh->DISTround, qh->ANGLEround, qh->MINoutside,
        qh->MINvisible, qh->MAXcoplanar, qh->WIDEfacet);
  if (qh->KEEPnearinside)
    qh_fprintf(qh, fp, 9352, "\
 %6.2g max. distance for near-inside points\n", qh->NEARinside);
  if (qh->premerge_cos < REALmax/2) qh_fprintf(qh, fp, 9353, "\
 %6.2g max. cosine for pre-merge angle\n", qh->premerge_cos);
  if (qh->PREmerge) qh_fprintf(qh, fp, 9354, "\
 %6.2g radius of pre-merge centrum\n", qh->premerge_centrum);
  if (qh->postmerge_cos < REALmax/2) qh_fprintf(qh, fp, 9355, "\
 %6.2g max. cosine for post-merge angle\n", qh->postmerge_cos);
  if (qh->POSTmerge) qh_fprintf(qh, fp, 9356, "\
 %6.2g radius of post-merge centrum\n", qh->postmerge_centrum);
  qh_fprintf(qh, fp, 9357, "\
 %6.2g max. distance for merging two simplicial facets\n\
 %6.2g max. roundoff error for arithmetic operations\n\
 %6.2g min. denominator for divisions\n\
  zero diagonal for Gauss: ", qh->ONEmerge, REALepsilon, qh->MINdenom);
  for(k=0; k < qh->hull_dim; k++)
    qh_fprintf(qh, fp, 9358, "%6.2e ", qh->NEARzero[k]);
  qh_fprintf(qh, fp, 9359, "\n\n");
  for(i=0 ; i < qh->qhstat.next; )
    qh_printstats(qh, fp, i, &i);
} /* printstatistics */
#endif /* qh_KEEPstatistics */

/*---------------------------------

  qh_printstatlevel(qh, fp, id )
    print level information for a statistic

  notes:
    nop if id >= ZEND, printed, or same as initial value
*/
void qh_printstatlevel(qhT *qh, FILE *fp, int id) {
#define NULLfield "       "

  if (id >= ZEND || qh->qhstat.printed[id])
    return;
  if (qh->qhstat.type[id] == zdoc) {
    qh_fprintf(qh, fp, 9360, "%s\n", qh->qhstat.doc[id]);
    return;
  }
  if (qh_nostatistic(qh, id) || !qh->qhstat.doc[id])
    return;
  qh->qhstat.printed[id]= True;
  if (qh->qhstat.count[id] != -1
      && qh->qhstat.stats[(unsigned char)(qh->qhstat.count[id])].i == 0)
    qh_fprintf(qh, fp, 9361, " *0 cnt*");
  else if (qh->qhstat.type[id] >= ZTYPEreal && qh->qhstat.count[id] == -1)
    qh_fprintf(qh, fp, 9362, "%7.2g", qh->qhstat.stats[id].r);
  else if (qh->qhstat.type[id] >= ZTYPEreal && qh->qhstat.count[id] != -1)
    qh_fprintf(qh, fp, 9363, "%7.2g", qh->qhstat.stats[id].r/ qh->qhstat.stats[(unsigned char)(qh->qhstat.count[id])].i);
  else if (qh->qhstat.type[id] < ZTYPEreal && qh->qhstat.count[id] == -1)
    qh_fprintf(qh, fp, 9364, "%7d", qh->qhstat.stats[id].i);
  else if (qh->qhstat.type[id] < ZTYPEreal && qh->qhstat.count[id] != -1)
    qh_fprintf(qh, fp, 9365, "%7.3g", (realT) qh->qhstat.stats[id].i / qh->qhstat.stats[(unsigned char)(qh->qhstat.count[id])].i);
  qh_fprintf(qh, fp, 9366, " %s\n", qh->qhstat.doc[id]);
} /* printstatlevel */


/*---------------------------------

  qh_printstats(qh, fp, index, nextindex )
    print statistics for a zdoc group

  returns:
    next zdoc if non-null
*/
void qh_printstats(qhT *qh, FILE *fp, int idx, int *nextindex) {
  int j, nexti;

  if (qh_newstats(qh, idx, &nexti)) {
    qh_fprintf(qh, fp, 9367, "\n");
    for (j=idx; jqhstat.id[j]);
  }
  if (nextindex)
    *nextindex= nexti;
} /* printstats */

#if qh_KEEPstatistics

/*---------------------------------

  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(qhT *qh) {}
void    qh_printallstatistics(qhT *qh, FILE *fp, char *string) {};
void    qh_printstatistics(qhT *qh, FILE *fp, char *string) {}
#endif

geometry/src/random_r.h0000644000176200001440000000206013432323614014645 0ustar  liggesusers/*
  ---------------------------------

  random.h
    header file for random and utility routines

   see qh-geom_r.htm and random_r.c

   Copyright (c) 1993-2015 The Geometry Center.
   $Id: //main/2015/qhull/src/libqhull_r/random_r.h#4 $$Change: 2079 $
   $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
*/

#ifndef qhDEFrandom
#define qhDEFrandom 1

#include "libqhull_r.h"

/*============= prototypes in alphabetical order ======= */

#ifdef __cplusplus
extern "C" {
#endif

int     qh_argv_to_command(int argc, char *argv[], char* command, int max_size);
int     qh_argv_to_command_size(int argc, char *argv[]);
int     qh_rand(qhT *qh);
void    qh_srand(qhT *qh, int seed);
realT   qh_randomfactor(qhT *qh, realT scale, realT offset);
void    qh_randommatrix(qhT *qh, realT *buffer, int dim, realT **row);
int     qh_strtol(const char *s, char **endp);
double  qh_strtod(const char *s, char **endp);

#ifdef __cplusplus
} /* extern "C" */
#endif

#endif /* qhDEFrandom */



geometry/src/user_r.h0000644000176200001440000007100413432323614014347 0ustar  liggesusers/*
  ---------------------------------

   user.h
   user redefinable constants

   for each source file, user_r.h is included first

   see qh-user_r.htm.  see COPYING for copyright information.

   See user_r.c for sample code.

   before reading any code, review libqhull_r.h for data structure definitions

Sections:
   ============= qhull library constants ======================
   ============= data types and configuration macros ==========
   ============= performance related constants ================
   ============= memory constants =============================
   ============= joggle constants =============================
   ============= conditional compilation ======================
   ============= -merge constants- ============================

Code flags --
  NOerrors -- the code does not call qh_errexit()
  WARN64 -- the code may be incompatible with 64-bit pointers

*/

#include 

#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 ======================*/
/*============================================================*/

/*----------------------------------

  FILENAMElen -- max length for TI and TO filenames

*/

#define qh_FILENAMElen 500

/*----------------------------------

  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_r.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 */


/*----------------------------------

  qh_OPTIONline -- max length of an option line 'FO'
*/
#define qh_OPTIONline 80

/*============================================================*/
/*============= data types and configuration macros ==========*/
/*============================================================*/

/*----------------------------------

  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

/*----------------------------------

  countT
    The type for counts and identifiers (e.g., the number of points, vertex identifiers)
    Currently used by C++ code-only.  Decided against using it for setT because most sets are small.

    Defined as 'int' for C-code compatibility and QH11026

    FIXUP QH11026 countT may be defined as a unsigned value, but several code issues need to be solved first.  See countT in Changes.txt
*/

#ifndef DEFcountT
#define DEFcountT 1
typedef int countT;
#endif
#define COUNTmax 0x7fffffff


/*----------------------------------

  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,
     defines clock_t as 'long'

   Set qh_CLOCKtype to

     1          for CLOCKS_PER_SEC, CLOCKS_PER_SECOND, or microsecond
                Note:  may fail if more than 1 hour elapsed time

     2          use qh_clock() with POSIX times() (see global_r.c)
*/
#define qh_CLOCKtype 1  /* change to the desired number */

#if (qh_CLOCKtype == 1)

#if defined(CLOCKS_PER_SECOND)
#define qh_CPUclock    ((unsigned long)clock())  /* return CPU clock */
#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

/*----------------------------------

  qh_RANDOMtype, qh_RANDOMmax, qh_RANDOMseed
    define random number generator

    qh_RANDOMint generates a random integer between 0 and qh_RANDOMmax.
    qh_RANDOMseed sets the random number seed for qh_RANDOMint

  Set qh_RANDOMtype (default 5) to:
    1       for random() with 31 bits (UCB)
    2       for rand() with RAND_MAX or 15 bits (system 5)
    3       for rand() with 31 bits (Sun)
    4       for lrand48() with 31 bits (Solaris)
    5       for qh_rand(qh) with 31 bits (included with Qhull, requires 'qh')

  notes:
    Random numbers are used by rbox to generate point sets.  Random
    numbers are used by Qhull to rotate the input ('QRn' option),
    simulate a randomized algorithm ('Qr' option), and to simulate
    roundoff errors ('Rn' option).

    Random number generators differ between systems.  Most systems provide
    rand() but the period varies.  The period of rand() is not critical
    since qhull does not normally use random numbers.

    The default generator is Park & Miller's minimal standard random
    number generator [CACM 31:1195 '88].  It is included with Qhull.

    If qh_RANDOMmax is wrong, qhull will report a warning and Geomview
    output will likely be invisible.
*/
#define qh_RANDOMtype 5   /* *** change to the desired number *** */

#if (qh_RANDOMtype == 1)
#define qh_RANDOMmax ((realT)0x7fffffffUL)  /* 31 bits, random()/MAX */
#define qh_RANDOMint random()
#define qh_RANDOMseed_(qh, seed) srandom(seed);

#elif (qh_RANDOMtype == 2)
#ifdef RAND_MAX
#define qh_RANDOMmax ((realT)RAND_MAX)
#else
#define qh_RANDOMmax ((realT)32767)   /* 15 bits (System 5) */
#endif
#define qh_RANDOMint  rand()
#define qh_RANDOMseed_(qh, seed) srand((unsigned)seed);

#elif (qh_RANDOMtype == 3)
#define qh_RANDOMmax ((realT)0x7fffffffUL)  /* 31 bits, Sun */
#define qh_RANDOMint  rand()
#define qh_RANDOMseed_(qh, seed) srand((unsigned)seed);

#elif (qh_RANDOMtype == 4)
#define qh_RANDOMmax ((realT)0x7fffffffUL)  /* 31 bits, lrand38()/MAX */
#define qh_RANDOMint lrand48()
#define qh_RANDOMseed_(qh, seed) srand48(seed);

#elif (qh_RANDOMtype == 5)  /* 'qh' is an implicit parameter */
#define qh_RANDOMmax ((realT)2147483646UL)  /* 31 bits, qh_rand/MAX */
#define qh_RANDOMint qh_rand(qh)
#define qh_RANDOMseed_(qh, seed) qh_srand(qh, seed);
/* unlike rand(), never returns 0 */

#else
#error: unknown random option
#endif

/*----------------------------------

  qh_ORIENTclock
    0 for inward pointing normals by Geomview convention
*/
#define qh_ORIENTclock 0


/*============================================================*/
/*============= joggle constants =============================*/
/*============================================================*/

/*----------------------------------

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

/*----------------------------------

qh_JOGGLEincrease
factor to increase qh.JOGGLEmax on qh_JOGGLEretry or qh_JOGGLEagain
*/
#define qh_JOGGLEincrease 10.0

/*----------------------------------

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

/*----------------------------------

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

/*----------------------------------

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

/*----------------------------------

qh_JOGGLEmaxretry
stop after qh_JOGGLEmaxretry attempts
*/
#define qh_JOGGLEmaxretry 100

/*============================================================*/
/*============= performance related constants ================*/
/*============================================================*/

/*----------------------------------

  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

/*----------------------------------

  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

/*----------------------------------

  qh_INITIALsearch
     if qh_INITIALmax, search points up to this dimension
*/
#define qh_INITIALsearch 6

/*----------------------------------

  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 =============================*/
/*============================================================*/

/*----------------------------------

  qh_MEMalign
    memory alignment for qh_meminitbuffers() in global_r.c

  notes:
    to avoid bus errors, memory allocation must consider alignment requirements.
    malloc() automatically takes care of alignment.   Since mem_r.c manages
    its own memory, we need to explicitly specify alignment in
    qh_meminitbuffers().

    A safe choice is sizeof(double).  sizeof(float) may be used if doubles
    do not occur in data structures and pointers are the same size.  Be careful
    of machines (e.g., DEC Alpha) with large pointers.

    If using gcc, best alignment is [fmax_() is defined in geom_r.h]
              #define qh_MEMalign fmax_(__alignof__(realT),__alignof__(void *))
*/
#define qh_MEMalign ((int)(fmax_(sizeof(realT), sizeof(void *))))

/*----------------------------------

  qh_MEMbufsize
    size of additional memory buffers

  notes:
    used for qh_meminitbuffers() in global_r.c
*/
#define qh_MEMbufsize 0x10000       /* allocate 64K memory buffers */

/*----------------------------------

  qh_MEMinitbuf
    size of initial memory buffer

  notes:
    use for qh_meminitbuffers() in global_r.c
*/
#define qh_MEMinitbuf 0x20000      /* initially allocate 128K buffer */

/*----------------------------------

  qh_INFINITE
    on output, indicates Voronoi center at infinity
*/
#define qh_INFINITE  -10.101

/*----------------------------------

  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 ======================*/
/*============================================================*/

/*----------------------------------

  __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
*/

/*----------------------------------

  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

/*----------------------------------

  qh_KEEPstatistics
    =0 removes most of statistic gathering and reporting

  notes:
    if 0, code size is reduced by about 4%.
*/
#define qh_KEEPstatistics 1

/*----------------------------------

  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

/*----------------------------------

  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:
    qh_NOmem in mem_r.c

    see user_r.c/user_eg.c for removing io_r.o
*/

/*----------------------------------

  qh_NOtrace
    no tracing if defined

  notes:
    This saves about 5% space.

    #define qh_NOtrace
*/

#if 0  /* sample code */
    exitcode= qh_new_qhull(qhT *qh, dim, numpoints, points, ismalloc,
                      flags, outfile, errfile);
    qh_freeqhull(qhT *qh, !qh_ALL); /* frees long memory used by second call */
    qh_memfreeshort(qhT *qh, &curlong, &totlong);  /* frees short memory and memory allocator */
#endif

/*----------------------------------

  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.
*/

/*----------------------------------

  qh_DIMmergeVertex
    max dimension for vertex merging (it is not effective in high-d)
*/
#define qh_DIMmergeVertex 6

/*----------------------------------

  qh_DIMreduceBuild
     max dimension for vertex reduction during build (slow in high-d)
*/
#define qh_DIMreduceBuild 5

/*----------------------------------

  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

/*----------------------------------

  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

/*----------------------------------

  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

/*----------------------------------

  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

/*----------------------------------

  qh_COPLANARratio
    for 3-d+ merging, qh.MINvisible is n*premerge_centrum

  notes:
    for non-merging, it's DISTround
*/
#define qh_COPLANARratio 3

/*----------------------------------

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

/*----------------------------------

  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

/*----------------------------------

  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)));

/*----------------------------------

  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)

/*----------------------------------

  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

/*----------------------------------

  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

/*----------------------------------

  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

/*----------------------------------

  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

/*----------------------------------

  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 
#include 
#endif

#endif /* qh_DEFuser */



geometry/src/qset_r.c0000644000176200001440000010360313432323610014335 0ustar  liggesusers/*
  ---------------------------------

   qset_r.c
   implements set manipulations needed for quickhull

   see qh-set_r.htm and qset_r.h

   Be careful of strict aliasing (two pointers of different types
   that reference the same location).  The last slot of a set is
   either the actual size of the set plus 1, or the NULL terminator
   of the set (i.e., setelemT).

   Copyright (c) 1993-2015 The Geometry Center.
   $Id: //main/2015/qhull/src/libqhull_r/qset_r.c#3 $$Change: 2062 $
   $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
*/

#include "libqhull_r.h" /* for qhT and QHULL_CRTDBG */
#include "qset_r.h"
#include "mem_r.h"
#include 
#include 
/*** uncomment here and qhull_ra.h
     if string.h does not define memcpy()
#include 
*/

#ifndef qhDEFlibqhull
typedef struct ridgeT ridgeT;
typedef struct facetT facetT;
void    qh_errexit(qhT *qh, int exitcode, facetT *, ridgeT *);
void    qh_fprintf(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... );
#  ifdef _MSC_VER  /* Microsoft Visual C++ -- warning level 4 */
#  pragma warning( disable : 4127)  /* conditional expression is constant */
#  pragma warning( disable : 4706)  /* assignment within conditional function */
#  endif
#endif

/*=============== internal macros ===========================*/

/*============ functions in alphabetical order ===================*/

/*----------------------------------

  qh_setaddnth(qh, setp, nth, newelem)
    adds newelem as n'th element of sorted or unsorted *setp

  notes:
    *setp and newelem must be defined
    *setp may be a temp set
    nth=0 is first element
    errors if nth is out of bounds

  design:
    expand *setp if empty or full
    move tail of *setp up one
    insert newelem
*/
void qh_setaddnth(qhT *qh, setT **setp, int nth, void *newelem) {
  int oldsize, i;
  setelemT *sizep;          /* avoid strict aliasing */
  setelemT *oldp, *newp;

  if (!*setp || (sizep= SETsizeaddr_(*setp))->i==0) {
    qh_setlarger(qh, setp);
    sizep= SETsizeaddr_(*setp);
  }
  oldsize= sizep->i - 1;
  if (nth < 0 || nth > oldsize) {
    qh_fprintf(qh, qh->qhmem.ferr, 6171, "qhull internal error (qh_setaddnth): nth %d is out-of-bounds for set:\n", nth);
    qh_setprint(qh, qh->qhmem.ferr, "", *setp);
    qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
  }
  sizep->i++;
  oldp= (setelemT *)SETelemaddr_(*setp, oldsize, void);   /* NULL */
  newp= oldp+1;
  for (i=oldsize-nth+1; i--; )  /* move at least NULL  */
    (newp--)->p= (oldp--)->p;       /* may overwrite *sizep */
  newp->p= newelem;
} /* setaddnth */


/*----------------------------------

  setaddsorted( setp, newelem )
    adds an newelem into sorted *setp

  notes:
    *setp and newelem must be defined
    *setp may be a temp set
    nop if newelem already in set

  design:
    find newelem's position in *setp
    insert newelem
*/
void qh_setaddsorted(qhT *qh, setT **setp, void *newelem) {
  int newindex=0;
  void *elem, **elemp;

  FOREACHelem_(*setp) {          /* could use binary search instead */
    if (elem < newelem)
      newindex++;
    else if (elem == newelem)
      return;
    else
      break;
  }
  qh_setaddnth(qh, setp, newindex, newelem);
} /* setaddsorted */


/*---------------------------------

  qh_setappend(qh, setp, newelem)
    append newelem to *setp

  notes:
    *setp may be a temp set
    *setp and newelem may be NULL

  design:
    expand *setp if empty or full
    append newelem to *setp

*/
void qh_setappend(qhT *qh, setT **setp, void *newelem) {
  setelemT *sizep;  /* Avoid strict aliasing.  Writing to *endp may overwrite *sizep */
  setelemT *endp;
  int count;

  if (!newelem)
    return;
  if (!*setp || (sizep= SETsizeaddr_(*setp))->i==0) {
    qh_setlarger(qh, setp);
    sizep= SETsizeaddr_(*setp);
  }
  count= (sizep->i)++ - 1;
  endp= (setelemT *)SETelemaddr_(*setp, count, void);
  (endp++)->p= newelem;
  endp->p= NULL;
} /* setappend */

/*---------------------------------

  qh_setappend_set(qh, setp, setA)
    appends setA to *setp

  notes:
    *setp can not be a temp set
    *setp and setA may be NULL

  design:
    setup for copy
    expand *setp if it is too small
    append all elements of setA to *setp
*/
void qh_setappend_set(qhT *qh, setT **setp, setT *setA) {
  int sizeA, size;
  setT *oldset;
  setelemT *sizep;

  if (!setA)
    return;
  SETreturnsize_(setA, sizeA);
  if (!*setp)
    *setp= qh_setnew(qh, sizeA);
  sizep= SETsizeaddr_(*setp);
  if (!(size= sizep->i))
    size= (*setp)->maxsize;
  else
    size--;
  if (size + sizeA > (*setp)->maxsize) {
    oldset= *setp;
    *setp= qh_setcopy(qh, oldset, sizeA);
    qh_setfree(qh, &oldset);
    sizep= SETsizeaddr_(*setp);
  }
  if (sizeA > 0) {
    sizep->i= size+sizeA+1;   /* memcpy may overwrite */
    memcpy((char *)&((*setp)->e[size].p), (char *)&(setA->e[0].p), (size_t)(sizeA+1) * SETelemsize);
  }
} /* setappend_set */


/*---------------------------------

  qh_setappend2ndlast(qh, setp, newelem )
    makes newelem the next to the last element in *setp

  notes:
    *setp must have at least one element
    newelem must be defined
    *setp may be a temp set

  design:
    expand *setp if empty or full
    move last element of *setp up one
    insert newelem
*/
void qh_setappend2ndlast(qhT *qh, setT **setp, void *newelem) {
    setelemT *sizep;  /* Avoid strict aliasing.  Writing to *endp may overwrite *sizep */
    setelemT *endp, *lastp;
    int count;

    if (!*setp || (sizep= SETsizeaddr_(*setp))->i==0) {
        qh_setlarger(qh, setp);
        sizep= SETsizeaddr_(*setp);
    }
    count= (sizep->i)++ - 1;
    endp= (setelemT *)SETelemaddr_(*setp, count, void); /* NULL */
    lastp= endp-1;
    *(endp++)= *lastp;
    endp->p= NULL;    /* may overwrite *sizep */
    lastp->p= newelem;
} /* setappend2ndlast */

/*---------------------------------

  qh_setcheck(qh, set, typename, id )
    check set for validity
    report errors with typename and id

  design:
    checks that maxsize, actual size, and NULL terminator agree
*/
void qh_setcheck(qhT *qh, setT *set, const char *tname, unsigned id) {
  int maxsize, size;
  int waserr= 0;

  if (!set)
    return;
  SETreturnsize_(set, size);
  maxsize= set->maxsize;
  if (size > maxsize || !maxsize) {
    qh_fprintf(qh, qh->qhmem.ferr, 6172, "qhull internal error (qh_setcheck): actual size %d of %s%d is greater than max size %d\n",
             size, tname, id, maxsize);
    waserr= 1;
  }else if (set->e[size].p) {
    qh_fprintf(qh, qh->qhmem.ferr, 6173, "qhull internal error (qh_setcheck): %s%d(size %d max %d) is not null terminated.\n",
             tname, id, size-1, maxsize);
    waserr= 1;
  }
  if (waserr) {
    qh_setprint(qh, qh->qhmem.ferr, "ERRONEOUS", set);
    qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
  }
} /* setcheck */


/*---------------------------------

  qh_setcompact(qh, set )
    remove internal NULLs from an unsorted set

  returns:
    updated set

  notes:
    set may be NULL
    it would be faster to swap tail of set into holes, like qh_setdel

  design:
    setup pointers into set
    skip NULLs while copying elements to start of set
    update the actual size
*/
void qh_setcompact(qhT *qh, setT *set) {
  int size;
  void **destp, **elemp, **endp, **firstp;

  if (!set)
    return;
  SETreturnsize_(set, size);
  destp= elemp= firstp= SETaddr_(set, void);
  endp= destp + size;
  while (1) {
    if (!(*destp++ = *elemp++)) {
      destp--;
      if (elemp > endp)
        break;
    }
  }
  qh_settruncate(qh, set, (int)(destp-firstp));   /* WARN64 */
} /* setcompact */


/*---------------------------------

  qh_setcopy(qh, set, extra )
    make a copy of a sorted or unsorted set with extra slots

  returns:
    new set

  design:
    create a newset with extra slots
    copy the elements to the newset

*/
setT *qh_setcopy(qhT *qh, setT *set, int extra) {
  setT *newset;
  int size;

  if (extra < 0)
    extra= 0;
  SETreturnsize_(set, size);
  newset= qh_setnew(qh, size+extra);
  SETsizeaddr_(newset)->i= size+1;    /* memcpy may overwrite */
  memcpy((char *)&(newset->e[0].p), (char *)&(set->e[0].p), (size_t)(size+1) * SETelemsize);
  return(newset);
} /* setcopy */


/*---------------------------------

  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 */


/*---------------------------------

  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 */


/*---------------------------------

  qh_setdelnth(qh, set, nth )
    deletes nth element from unsorted set
    0 is first element

  returns:
    returns the element (needs type conversion)

  notes:
    errors if nth invalid

  design:
    setup points and check nth
    delete nth element and overwrite with last element
*/
void *qh_setdelnth(qhT *qh, setT *set, int nth) {
  void *elem;
  setelemT *sizep;
  setelemT *elemp, *lastp;

  sizep= SETsizeaddr_(set);
  if ((sizep->i--)==0)         /*  if was a full set */
    sizep->i= set->maxsize;  /*     *sizep= (maxsize-1)+ 1 */
  if (nth < 0 || nth >= sizep->i) {
    qh_fprintf(qh, qh->qhmem.ferr, 6174, "qhull internal error (qh_setdelnth): nth %d is out-of-bounds for set:\n", nth);
    qh_setprint(qh, qh->qhmem.ferr, "", set);
    qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
  }
  elemp= (setelemT *)SETelemaddr_(set, nth, void); /* nth valid by QH6174 */
  lastp= (setelemT *)SETelemaddr_(set, sizep->i-1, void);
  elem= elemp->p;
  elemp->p= lastp->p;      /* may overwrite itself */
  lastp->p= NULL;
  return elem;
} /* setdelnth */

/*---------------------------------

  qh_setdelnthsorted(qh, set, nth )
    deletes nth element from sorted set

  returns:
    returns the element (use type conversion)

  notes:
    errors if nth invalid

  see also:
    setnew_delnthsorted

  design:
    setup points and check nth
    copy remaining elements down one
    update actual size
*/
void *qh_setdelnthsorted(qhT *qh, setT *set, int nth) {
  void *elem;
  setelemT *sizep;
  setelemT *newp, *oldp;

  sizep= SETsizeaddr_(set);
  if (nth < 0 || (sizep->i && nth >= sizep->i-1) || nth >= set->maxsize) {
    qh_fprintf(qh, qh->qhmem.ferr, 6175, "qhull internal error (qh_setdelnthsorted): nth %d is out-of-bounds for set:\n", nth);
    qh_setprint(qh, qh->qhmem.ferr, "", set);
    qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
  }
  newp= (setelemT *)SETelemaddr_(set, nth, void);
  elem= newp->p;
  oldp= newp+1;
  while (((newp++)->p= (oldp++)->p))
    ; /* copy remaining elements and NULL */
  if ((sizep->i--)==0)         /*  if was a full set */
    sizep->i= set->maxsize;  /*     *sizep= (max size-1)+ 1 */
  return elem;
} /* setdelnthsorted */


/*---------------------------------

  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 */


/*---------------------------------

  qh_setduplicate(qh, set, elemsize )
    duplicate a set of elemsize elements

  notes:
    use setcopy if retaining old elements

  design:
    create a new set
    for each elem of the old set
      create a newelem
      append newelem to newset
*/
setT *qh_setduplicate(qhT *qh, setT *set, int elemsize) {
  void          *elem, **elemp, *newElem;
  setT          *newSet;
  int           size;

  if (!(size= qh_setsize(qh, set)))
    return NULL;
  newSet= qh_setnew(qh, size);
  FOREACHelem_(set) {
    newElem= qh_memalloc(qh, elemsize);
    memcpy(newElem, elem, (size_t)elemsize);
    qh_setappend(qh, &newSet, newElem);
  }
  return newSet;
} /* setduplicate */


/*---------------------------------

  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 */

/*---------------------------------

  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 */


/*---------------------------------

  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 */


/*---------------------------------

  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 */


/*---------------------------------

  qh_setfree(qh, setp )
    frees the space occupied by a sorted or unsorted set

  returns:
    sets setp to NULL

  notes:
    set may be NULL

  design:
    free array
    free set
*/
void qh_setfree(qhT *qh, setT **setp) {
  int size;
  void **freelistp;  /* used if !qh_NOmem by qh_memfree_() */

  if (*setp) {
    size= sizeof(setT) + ((*setp)->maxsize)*SETelemsize;
    if (size <= qh->qhmem.LASTsize) {
      qh_memfree_(qh, *setp, size, freelistp);
    }else
      qh_memfree(qh, *setp, size);
    *setp= NULL;
  }
} /* setfree */


/*---------------------------------

  qh_setfree2(qh, setp, elemsize )
    frees the space occupied by a set and its elements

  notes:
    set may be NULL

  design:
    free each element
    free set
*/
void qh_setfree2(qhT *qh, setT **setp, int elemsize) {
  void          *elem, **elemp;

  FOREACHelem_(*setp)
    qh_memfree(qh, elem, elemsize);
  qh_setfree(qh, setp);
} /* setfree2 */



/*---------------------------------

  qh_setfreelong(qh, setp )
    frees a set only if it's in long memory

  returns:
    sets setp to NULL if it is freed

  notes:
    set may be NULL

  design:
    if set is large
      free it
*/
void qh_setfreelong(qhT *qh, setT **setp) {
  int size;

  if (*setp) {
    size= sizeof(setT) + ((*setp)->maxsize)*SETelemsize;
    if (size > qh->qhmem.LASTsize) {
      qh_memfree(qh, *setp, size);
      *setp= NULL;
    }
  }
} /* setfreelong */


/*---------------------------------

  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 */


/*---------------------------------

  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 */


/*---------------------------------

  qh_setlarger(qh, oldsetp )
    returns a larger set that contains all elements of *oldsetp

  notes:
    the set is at least twice as large
    if temp set, updates qh->qhmem.tempstack

  design:
    creates a new set
    copies the old set to the new set
    updates pointers in tempstack
    deletes the old set
*/
void qh_setlarger(qhT *qh, setT **oldsetp) {
  int size= 1;
  setT *newset, *set, **setp, *oldset;
  setelemT *sizep;
  setelemT *newp, *oldp;

  if (*oldsetp) {
    oldset= *oldsetp;
    SETreturnsize_(oldset, size);
    qh->qhmem.cntlarger++;
    qh->qhmem.totlarger += size+1;
    newset= qh_setnew(qh, 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 *)qh->qhmem.tempstack) {
      if (set == oldset)
        *(setp-1)= newset;
    }
    qh_setfree(qh, oldsetp);
  }else
    newset= qh_setnew(qh, 3);
  *oldsetp= newset;
} /* setlarger */


/*---------------------------------

  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 */


/*---------------------------------

  qh_setnew(qh, setsize )
    creates and allocates space for a set

  notes:
    setsize means the number of elements (!including the NULL terminator)
    use qh_settemp/qh_setfreetemp if set is temporary

  design:
    allocate memory for set
    roundup memory if small set
    initialize as empty set
*/
setT *qh_setnew(qhT *qh, int setsize) {
  setT *set;
  int sizereceived; /* used if !qh_NOmem */
  int size;
  void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */

  if (!setsize)
    setsize++;
  size= sizeof(setT) + setsize * SETelemsize;
  if (size>0 && size <= qh->qhmem.LASTsize) {
    qh_memalloc_(qh, size, freelistp, set, setT);
#ifndef qh_NOmem
    sizereceived= qh->qhmem.sizetable[ qh->qhmem.indextable[size]];
    if (sizereceived > size)
      setsize += (sizereceived - size)/SETelemsize;
#endif
  }else
    set= (setT*)qh_memalloc(qh, size);
  set->maxsize= setsize;
  set->e[setsize].i= 1;
  set->e[0].p= NULL;
  return(set);
} /* setnew */


/*---------------------------------

  qh_setnew_delnthsorted(qh, set, size, nth, prepend )
    creates a sorted set not containing nth element
    if prepend, the first prepend elements are undefined

  notes:
    set must be defined
    checks nth
    see also: setdelnthsorted

  design:
    create new set
    setup pointers and allocate room for prepend'ed entries
    append head of old set to new set
    append tail of old set to new set
*/
setT *qh_setnew_delnthsorted(qhT *qh, setT *set, int size, int nth, int prepend) {
  setT *newset;
  void **oldp, **newp;
  int tailsize= size - nth -1, newsize;

  if (tailsize < 0) {
    qh_fprintf(qh, qh->qhmem.ferr, 6176, "qhull internal error (qh_setnew_delnthsorted): nth %d is out-of-bounds for set:\n", nth);
    qh_setprint(qh, qh->qhmem.ferr, "", set);
    qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
  }
  newsize= size-1 + prepend;
  newset= qh_setnew(qh, newsize);
  newset->e[newset->maxsize].i= newsize+1;  /* may be overwritten */
  oldp= SETaddr_(set, void);
  newp= SETaddr_(newset, void) + prepend;
  switch (nth) {
  case 0:
    break;
  case 1:
    *(newp++)= *oldp++;
    break;
  case 2:
    *(newp++)= *oldp++;
    *(newp++)= *oldp++;
    break;
  case 3:
    *(newp++)= *oldp++;
    *(newp++)= *oldp++;
    *(newp++)= *oldp++;
    break;
  case 4:
    *(newp++)= *oldp++;
    *(newp++)= *oldp++;
    *(newp++)= *oldp++;
    *(newp++)= *oldp++;
    break;
  default:
    memcpy((char *)newp, (char *)oldp, (size_t)nth * SETelemsize);
    newp += nth;
    oldp += nth;
    break;
  }
  oldp++;
  switch (tailsize) {
  case 0:
    break;
  case 1:
    *(newp++)= *oldp++;
    break;
  case 2:
    *(newp++)= *oldp++;
    *(newp++)= *oldp++;
    break;
  case 3:
    *(newp++)= *oldp++;
    *(newp++)= *oldp++;
    *(newp++)= *oldp++;
    break;
  case 4:
    *(newp++)= *oldp++;
    *(newp++)= *oldp++;
    *(newp++)= *oldp++;
    *(newp++)= *oldp++;
    break;
  default:
    memcpy((char *)newp, (char *)oldp, (size_t)tailsize * SETelemsize);
    newp += tailsize;
  }
  *newp= NULL;
  return(newset);
} /* setnew_delnthsorted */


/*---------------------------------

  qh_setprint(qh, fp, string, set )
    print set elements to fp with identifying string

  notes:
    never errors
*/
void qh_setprint(qhT *qh, FILE *fp, const char* string, setT *set) {
  int size, k;

  if (!set)
    qh_fprintf(qh, fp, 9346, "%s set is null\n", string);
  else {
    SETreturnsize_(set, size);
    qh_fprintf(qh, fp, 9347, "%s set=%p maxsize=%d size=%d elems=",
             string, set, set->maxsize, size);
    if (size > set->maxsize)
      size= set->maxsize+1;
    for (k=0; k < size; k++)
      qh_fprintf(qh, fp, 9348, " %p", set->e[k].p);
    qh_fprintf(qh, fp, 9349, "\n");
  }
} /* setprint */

/*---------------------------------

  qh_setreplace(qh, set, oldelem, newelem )
    replaces oldelem in set with newelem

  notes:
    errors if oldelem not in the set
    newelem may be NULL, but it turns the set into an indexed set (no FOREACH)

  design:
    find oldelem
    replace with newelem
*/
void qh_setreplace(qhT *qh, setT *set, void *oldelem, void *newelem) {
  void **elemp;

  elemp= SETaddr_(set, void);
  while (*elemp != oldelem && *elemp)
    elemp++;
  if (*elemp)
    *elemp= newelem;
  else {
    qh_fprintf(qh, qh->qhmem.ferr, 6177, "qhull internal error (qh_setreplace): elem %p not found in set\n",
       oldelem);
    qh_setprint(qh, qh->qhmem.ferr, "", set);
    qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
  }
} /* setreplace */


/*---------------------------------

  qh_setsize(qh, set )
    returns the size of a set

  notes:
    errors if set's maxsize is incorrect
    same as SETreturnsize_(set)
    same code for qh_setsize [qset_r.c] and QhullSetBase::count

  design:
    determine actual size of set from maxsize
*/
int qh_setsize(qhT *qh, setT *set) {
  int size;
  setelemT *sizep;

  if (!set)
    return(0);
  sizep= SETsizeaddr_(set);
  if ((size= sizep->i)) {
    size--;
    if (size > set->maxsize) {
      qh_fprintf(qh, qh->qhmem.ferr, 6178, "qhull internal error (qh_setsize): current set size %d is greater than maximum size %d\n",
               size, set->maxsize);
      qh_setprint(qh, qh->qhmem.ferr, "set: ", set);
      qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
    }
  }else
    size= set->maxsize;
  return size;
} /* setsize */

/*---------------------------------

  qh_settemp(qh, setsize )
    return a stacked, temporary set of upto setsize elements

  notes:
    use settempfree or settempfree_all to release from qh->qhmem.tempstack
    see also qh_setnew

  design:
    allocate set
    append to qh->qhmem.tempstack

*/
setT *qh_settemp(qhT *qh, int setsize) {
  setT *newset;

  newset= qh_setnew(qh, setsize);
  qh_setappend(qh, &qh->qhmem.tempstack, newset);
  if (qh->qhmem.IStracing >= 5)
    qh_fprintf(qh, qh->qhmem.ferr, 8123, "qh_settemp: temp set %p of %d elements, depth %d\n",
       newset, newset->maxsize, qh_setsize(qh, qh->qhmem.tempstack));
  return newset;
} /* settemp */

/*---------------------------------

  qh_settempfree(qh, set )
    free temporary set at top of qh->qhmem.tempstack

  notes:
    nop if set is NULL
    errors if set not from previous   qh_settemp

  to locate errors:
    use 'T2' to find source and then find mis-matching qh_settemp

  design:
    check top of qh->qhmem.tempstack
    free it
*/
void qh_settempfree(qhT *qh, setT **set) {
  setT *stackedset;

  if (!*set)
    return;
  stackedset= qh_settemppop(qh);
  if (stackedset != *set) {
    qh_settemppush(qh, stackedset);
    qh_fprintf(qh, qh->qhmem.ferr, 6179, "qhull internal error (qh_settempfree): set %p(size %d) was not last temporary allocated(depth %d, set %p, size %d)\n",
             *set, qh_setsize(qh, *set), qh_setsize(qh, qh->qhmem.tempstack)+1,
             stackedset, qh_setsize(qh, stackedset));
    qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
  }
  qh_setfree(qh, set);
} /* settempfree */

/*---------------------------------

  qh_settempfree_all(qh)
    free all temporary sets in qh->qhmem.tempstack

  design:
    for each set in tempstack
      free set
    free qh->qhmem.tempstack
*/
void qh_settempfree_all(qhT *qh) {
  setT *set, **setp;

  FOREACHset_(qh->qhmem.tempstack)
    qh_setfree(qh, &set);
  qh_setfree(qh, &qh->qhmem.tempstack);
} /* settempfree_all */

/*---------------------------------

  qh_settemppop(qh)
    pop and return temporary set from qh->qhmem.tempstack

  notes:
    the returned set is permanent

  design:
    pop and check top of qh->qhmem.tempstack
*/
setT *qh_settemppop(qhT *qh) {
  setT *stackedset;

  stackedset= (setT*)qh_setdellast(qh->qhmem.tempstack);
  if (!stackedset) {
    qh_fprintf(qh, qh->qhmem.ferr, 6180, "qhull internal error (qh_settemppop): pop from empty temporary stack\n");
    qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
  }
  if (qh->qhmem.IStracing >= 5)
    qh_fprintf(qh, qh->qhmem.ferr, 8124, "qh_settemppop: depth %d temp set %p of %d elements\n",
       qh_setsize(qh, qh->qhmem.tempstack)+1, stackedset, qh_setsize(qh, stackedset));
  return stackedset;
} /* settemppop */

/*---------------------------------

  qh_settemppush(qh, set )
    push temporary set unto qh->qhmem.tempstack (makes it temporary)

  notes:
    duplicates settemp() for tracing

  design:
    append set to tempstack
*/
void qh_settemppush(qhT *qh, setT *set) {
  if (!set) {
    qh_fprintf(qh, qh->qhmem.ferr, 6267, "qhull error (qh_settemppush): can not push a NULL temp\n");
    qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
  }
  qh_setappend(qh, &qh->qhmem.tempstack, set);
  if (qh->qhmem.IStracing >= 5)
    qh_fprintf(qh, qh->qhmem.ferr, 8125, "qh_settemppush: depth %d temp set %p of %d elements\n",
      qh_setsize(qh, qh->qhmem.tempstack), set, qh_setsize(qh, set));
} /* settemppush */


/*---------------------------------

  qh_settruncate(qh, set, size )
    truncate set to size elements

  notes:
    set must be defined

  see:
    SETtruncate_

  design:
    check size
    update actual size of set
*/
void qh_settruncate(qhT *qh, setT *set, int size) {

  if (size < 0 || size > set->maxsize) {
    qh_fprintf(qh, qh->qhmem.ferr, 6181, "qhull internal error (qh_settruncate): size %d out of bounds for set:\n", size);
    qh_setprint(qh, qh->qhmem.ferr, "", set);
    qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
  }
  set->e[set->maxsize].i= size+1;   /* maybe overwritten */
  set->e[size].p= NULL;
} /* settruncate */

/*---------------------------------

  qh_setunique(qh, set, elem )
    add elem to unsorted set unless it is already in set

  notes:
    returns 1 if it is appended

  design:
    if elem not in set
      append elem to set
*/
int qh_setunique(qhT *qh, setT **set, void *elem) {

  if (!qh_setin(*set, elem)) {
    qh_setappend(qh, set, elem);
    return 1;
  }
  return 0;
} /* setunique */

/*---------------------------------

  qh_setzero(qh, set, index, size )
    zero elements from index on
    set actual size of set to size

  notes:
    set must be defined
    the set becomes an indexed set (can not use FOREACH...)

  see also:
    qh_settruncate

  design:
    check index and size
    update actual size
    zero elements starting at e[index]
*/
void qh_setzero(qhT *qh, setT *set, int idx, int size) {
  int count;

  if (idx < 0 || idx >= size || size > set->maxsize) {
    qh_fprintf(qh, qh->qhmem.ferr, 6182, "qhull internal error (qh_setzero): index %d or size %d out of bounds for set:\n", idx, size);
    qh_setprint(qh, qh->qhmem.ferr, "", set);
    qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
  }
  set->e[set->maxsize].i=  size+1;  /* may be overwritten */
  count= size - idx + 1;   /* +1 for NULL terminator */
  memset((char *)SETelemaddr_(set, idx, void), 0, (size_t)count * SETelemsize);
} /* setzero */


geometry/src/mem_r.c0000644000176200001440000005167413432323610014151 0ustar  liggesusers/*
  ---------------------------------

  mem_r.c
    memory management routines for qhull

  See libqhull/mem_r.c for a standalone program.

  To initialize memory:

    qh_meminit(qh, stderr);
    qh_meminitbuffers(qh, qh->IStracing, qh_MEMalign, 7, qh_MEMbufsize,qh_MEMinitbuf);
    qh_memsize(qh, (int)sizeof(facetT));
    qh_memsize(qh, (int)sizeof(facetT));
    ...
    qh_memsetup(qh);

  To free up all memory buffers:
    qh_memfreeshort(qh, &curlong, &totlong);

  if qh_NOmem,
    malloc/free is used instead of mem.c

  notes:
    uses Quickfit algorithm (freelists for commonly allocated sizes)
    assumes small sizes for freelists (it discards the tail of memory buffers)

  see:
    qh-mem_r.htm and mem_r.h
    global_r.c (qh_initbuffers) for an example of using mem_r.c

  Copyright (c) 1993-2015 The Geometry Center.
  $Id: //main/2015/qhull/src/libqhull_r/mem_r.c#5 $$Change: 2065 $
  $DateTime: 2016/01/18 13:51:04 $$Author: bbarber $
*/

#include "libqhull_r.h"  /* includes user_r.h and mem_r.h */

#include 
#include 
#include 

#ifndef qh_NOmem

/*============= internal functions ==============*/

static int qh_intcompare(const void *i, const void *j);

/*========== functions in alphabetical order ======== */

/*---------------------------------

  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 */


/*----------------------------------

  qh_memalloc( qh, insize )
    returns object of insize bytes
    qhmem is the global memory structure

  returns:
    pointer to allocated memory
    errors if insufficient memory

  notes:
    use explicit type conversion to avoid type warnings on some compilers
    actual object may be larger than insize
    use qh_memalloc_() for inline code for quick allocations
    logs allocations if 'T5'
    caller is responsible for freeing the memory.
    short memory is freed on shutdown by qh_memfreeshort unless qh_NOmem

  design:
    if size < qh->qhmem.LASTsize
      if qh->qhmem.freelists[size] non-empty
        return first object on freelist
      else
        round up request to size of qh->qhmem.freelists[size]
        allocate new allocation buffer if necessary
        allocate object from allocation buffer
    else
      allocate object with qh_malloc() in user.c
*/
void *qh_memalloc(qhT *qh, int insize) {
  void **freelistp, *newbuffer;
  int idx, size, n;
  int outsize, bufsize;
  void *object;

  if (insize<0) {
      qh_fprintf(qh, qh->qhmem.ferr, 6235, "qhull error (qh_memalloc): negative request size (%d).  Did int overflow due to high-D?\n", insize); /* WARN64 */
      qh_errexit(qh, qhmem_ERRmem, NULL, NULL);
  }
  if (insize>=0 && insize <= qh->qhmem.LASTsize) {
    idx= qh->qhmem.indextable[insize];
    outsize= qh->qhmem.sizetable[idx];
    qh->qhmem.totshort += outsize;
    freelistp= qh->qhmem.freelists+idx;
    if ((object= *freelistp)) {
      qh->qhmem.cntquick++;
      qh->qhmem.totfree -= outsize;
      *freelistp= *((void **)*freelistp);  /* replace freelist with next object */
#ifdef qh_TRACEshort
      n= qh->qhmem.cntshort+qh->qhmem.cntquick+qh->qhmem.freeshort;
      if (qh->qhmem.IStracing >= 5)
          qh_fprintf(qh, qh->qhmem.ferr, 8141, "qh_mem %p n %8d alloc quick: %d bytes (tot %d cnt %d)\n", object, n, outsize, qh->qhmem.totshort, qh->qhmem.cntshort+qh->qhmem.cntquick-qh->qhmem.freeshort);
#endif
      return(object);
    }else {
      qh->qhmem.cntshort++;
      if (outsize > qh->qhmem.freesize) {
        qh->qhmem.totdropped += qh->qhmem.freesize;
        if (!qh->qhmem.curbuffer)
          bufsize= qh->qhmem.BUFinit;
        else
          bufsize= qh->qhmem.BUFsize;
        if (!(newbuffer= qh_malloc((size_t)bufsize))) {
          qh_fprintf(qh, qh->qhmem.ferr, 6080, "qhull error (qh_memalloc): insufficient memory to allocate short memory buffer (%d bytes)\n", bufsize);
          qh_errexit(qh, qhmem_ERRmem, NULL, NULL);
        }
        *((void **)newbuffer)= qh->qhmem.curbuffer;  /* prepend newbuffer to curbuffer
                                                    list.  newbuffer!=0 by QH6080 */
        qh->qhmem.curbuffer= newbuffer;
        size= (sizeof(void **) + qh->qhmem.ALIGNmask) & ~qh->qhmem.ALIGNmask;
        qh->qhmem.freemem= (void *)((char *)newbuffer+size);
        qh->qhmem.freesize= bufsize - size;
        qh->qhmem.totbuffer += bufsize - size; /* easier to check */
        /* Periodically test totbuffer.  It matches at beginning and exit of every call */
        n = qh->qhmem.totshort + qh->qhmem.totfree + qh->qhmem.totdropped + qh->qhmem.freesize - outsize;
        if (qh->qhmem.totbuffer != n) {
            qh_fprintf(qh, qh->qhmem.ferr, 6212, "qh_memalloc internal error: short totbuffer %d != totshort+totfree... %d\n", qh->qhmem.totbuffer, n);
            qh_errexit(qh, qhmem_ERRmem, NULL, NULL);
        }
      }
      object= qh->qhmem.freemem;
      qh->qhmem.freemem= (void *)((char *)qh->qhmem.freemem + outsize);
      qh->qhmem.freesize -= outsize;
      qh->qhmem.totunused += outsize - insize;
#ifdef qh_TRACEshort
      n= qh->qhmem.cntshort+qh->qhmem.cntquick+qh->qhmem.freeshort;
      if (qh->qhmem.IStracing >= 5)
          qh_fprintf(qh, qh->qhmem.ferr, 8140, "qh_mem %p n %8d alloc short: %d bytes (tot %d cnt %d)\n", object, n, outsize, qh->qhmem.totshort, qh->qhmem.cntshort+qh->qhmem.cntquick-qh->qhmem.freeshort);
#endif
      return object;
    }
  }else {                     /* long allocation */
    if (!qh->qhmem.indextable) {
      qh_fprintf(qh, qh->qhmem.ferr, 6081, "qhull internal error (qh_memalloc): qhmem has not been initialized.\n");
      qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
    }
    outsize= insize;
    qh->qhmem.cntlong++;
    qh->qhmem.totlong += outsize;
    if (qh->qhmem.maxlong < qh->qhmem.totlong)
      qh->qhmem.maxlong= qh->qhmem.totlong;
    if (!(object= qh_malloc((size_t)outsize))) {
      qh_fprintf(qh, qh->qhmem.ferr, 6082, "qhull error (qh_memalloc): insufficient memory to allocate %d bytes\n", outsize);
      qh_errexit(qh, qhmem_ERRmem, NULL, NULL);
    }
    if (qh->qhmem.IStracing >= 5)
      qh_fprintf(qh, qh->qhmem.ferr, 8057, "qh_mem %p n %8d alloc long: %d bytes (tot %d cnt %d)\n", object, qh->qhmem.cntlong+qh->qhmem.freelong, outsize, qh->qhmem.totlong, qh->qhmem.cntlong-qh->qhmem.freelong);
  }
  return(object);
} /* memalloc */


/*----------------------------------

  qh_memcheck(qh)
*/
void qh_memcheck(qhT *qh) {
  int i, count, totfree= 0;
  void *object;

  if (!qh) {
    qh_fprintf_stderr(6243, "qh_memcheck(qh) error: qh is 0.  It does not point to a qhT");
    qh_exit(qhmem_ERRqhull);  /* can not use qh_errexit() */
  }
  if (qh->qhmem.ferr == 0 || qh->qhmem.IStracing < 0 || qh->qhmem.IStracing > 10 || (((qh->qhmem.ALIGNmask+1) & qh->qhmem.ALIGNmask) != 0)) {
    qh_fprintf_stderr(6244, "qh_memcheck error: either qh->qhmem is overwritten or qh->qhmem is not initialized.  Call qh_mem_new() or qh_new_qhull() before calling qh_mem routines.  ferr 0x%x IsTracing %d ALIGNmask 0x%x", qh->qhmem.ferr, qh->qhmem.IStracing, qh->qhmem.ALIGNmask);
    qh_exit(qhmem_ERRqhull);  /* can not use qh_errexit() */
  }
  if (qh->qhmem.IStracing != 0)
    qh_fprintf(qh, qh->qhmem.ferr, 8143, "qh_memcheck: check size of freelists on qh->qhmem\nqh_memcheck: A segmentation fault indicates an overwrite of qh->qhmem\n");
  for (i=0; i < qh->qhmem.TABLEsize; i++) {
    count=0;
    for (object= qh->qhmem.freelists[i]; object; object= *((void **)object))
      count++;
    totfree += qh->qhmem.sizetable[i] * count;
  }
  if (totfree != qh->qhmem.totfree) {
    qh_fprintf(qh, qh->qhmem.ferr, 6211, "Qhull internal error (qh_memcheck): totfree %d not equal to freelist total %d\n", qh->qhmem.totfree, totfree);
    qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
  }
  if (qh->qhmem.IStracing != 0)
    qh_fprintf(qh, qh->qhmem.ferr, 8144, "qh_memcheck: total size of freelists totfree is the same as qh->qhmem.totfree\n", totfree);
} /* memcheck */

/*----------------------------------

  qh_memfree(qh, object, insize )
    free up an object of size bytes
    size is insize from qh_memalloc

  notes:
    object may be NULL
    type checking warns if using (void **)object
    use qh_memfree_() for quick free's of small objects

  design:
    if size <= qh->qhmem.LASTsize
      append object to corresponding freelist
    else
      call qh_free(object)
*/
void qh_memfree(qhT *qh, void *object, int insize) {
  void **freelistp;
  int idx, outsize;

  if (!object)
    return;
  if (insize <= qh->qhmem.LASTsize) {
    qh->qhmem.freeshort++;
    idx= qh->qhmem.indextable[insize];
    outsize= qh->qhmem.sizetable[idx];
    qh->qhmem.totfree += outsize;
    qh->qhmem.totshort -= outsize;
    freelistp= qh->qhmem.freelists + idx;
    *((void **)object)= *freelistp;
    *freelistp= object;
#ifdef qh_TRACEshort
    idx= qh->qhmem.cntshort+qh->qhmem.cntquick+qh->qhmem.freeshort;
    if (qh->qhmem.IStracing >= 5)
        qh_fprintf(qh, qh->qhmem.ferr, 8142, "qh_mem %p n %8d free short: %d bytes (tot %d cnt %d)\n", object, idx, outsize, qh->qhmem.totshort, qh->qhmem.cntshort+qh->qhmem.cntquick-qh->qhmem.freeshort);
#endif
  }else {
    qh->qhmem.freelong++;
    qh->qhmem.totlong -= insize;
    if (qh->qhmem.IStracing >= 5)
      qh_fprintf(qh, qh->qhmem.ferr, 8058, "qh_mem %p n %8d free long: %d bytes (tot %d cnt %d)\n", object, qh->qhmem.cntlong+qh->qhmem.freelong, insize, qh->qhmem.totlong, qh->qhmem.cntlong-qh->qhmem.freelong);
    qh_free(object);
  }
} /* memfree */


/*---------------------------------

  qh_memfreeshort(qh, curlong, totlong )
    frees up all short and qhmem memory allocations

  returns:
    number and size of current long allocations
  
  notes:
    if qh_NOmem (qh_malloc() for all allocations), 
       short objects (e.g., facetT) are not recovered.
       use qh_freeqhull(qh, qh_ALL) instead.
 
  see:
    qh_freeqhull(qh, allMem)
    qh_memtotal(qh, curlong, totlong, curshort, totshort, maxlong, totbuffer);
*/
void qh_memfreeshort(qhT *qh, int *curlong, int *totlong) {
  void *buffer, *nextbuffer;
  FILE *ferr;

  *curlong= qh->qhmem.cntlong - qh->qhmem.freelong;
  *totlong= qh->qhmem.totlong;
  for (buffer= qh->qhmem.curbuffer; buffer; buffer= nextbuffer) {
    nextbuffer= *((void **) buffer);
    qh_free(buffer);
  }
  qh->qhmem.curbuffer= NULL;
  if (qh->qhmem.LASTsize) {
    qh_free(qh->qhmem.indextable);
    qh_free(qh->qhmem.freelists);
    qh_free(qh->qhmem.sizetable);
  }
  ferr= qh->qhmem.ferr;
  memset((char *)&qh->qhmem, 0, sizeof(qh->qhmem));  /* every field is 0, FALSE, NULL */
  qh->qhmem.ferr= ferr;
} /* memfreeshort */


/*----------------------------------

  qh_meminit(qh, ferr )
    initialize qhmem and test sizeof( void*)
    Does not throw errors.  qh_exit on failure
*/
void qh_meminit(qhT *qh, FILE *ferr) {

  memset((char *)&qh->qhmem, 0, sizeof(qh->qhmem));  /* every field is 0, FALSE, NULL */
  if (ferr)
      qh->qhmem.ferr= ferr;
  else
      qh->qhmem.ferr= stderr;
  if (sizeof(void*) < sizeof(int)) {
    qh_fprintf(qh, qh->qhmem.ferr, 6083, "qhull internal error (qh_meminit): sizeof(void*) %d < sizeof(int) %d.  qset.c will not work\n", (int)sizeof(void*), (int)sizeof(int));
    qh_exit(qhmem_ERRqhull);  /* can not use qh_errexit() */
  }
  if (sizeof(void*) > sizeof(ptr_intT)) {
      qh_fprintf(qh, qh->qhmem.ferr, 6084, "qhull internal error (qh_meminit): sizeof(void*) %d > sizeof(ptr_intT) %d. Change ptr_intT in mem.h to 'long long'\n", (int)sizeof(void*), (int)sizeof(ptr_intT));
      qh_exit(qhmem_ERRqhull);  /* can not use qh_errexit() */
  }
  qh_memcheck(qh);
} /* meminit */

/*---------------------------------

  qh_meminitbuffers(qh, tracelevel, alignment, numsizes, bufsize, bufinit )
    initialize qhmem
    if tracelevel >= 5, trace memory allocations
    alignment= desired address alignment for memory allocations
    numsizes= number of freelists
    bufsize=  size of additional memory buffers for short allocations
    bufinit=  size of initial memory buffer for short allocations
*/
void qh_meminitbuffers(qhT *qh, int tracelevel, int alignment, int numsizes, int bufsize, int bufinit) {

  qh->qhmem.IStracing= tracelevel;
  qh->qhmem.NUMsizes= numsizes;
  qh->qhmem.BUFsize= bufsize;
  qh->qhmem.BUFinit= bufinit;
  qh->qhmem.ALIGNmask= alignment-1;
  if (qh->qhmem.ALIGNmask & ~qh->qhmem.ALIGNmask) {
    qh_fprintf(qh, qh->qhmem.ferr, 6085, "qhull internal error (qh_meminit): memory alignment %d is not a power of 2\n", alignment);
    qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
  }
  qh->qhmem.sizetable= (int *) calloc((size_t)numsizes, sizeof(int));
  qh->qhmem.freelists= (void **) calloc((size_t)numsizes, sizeof(void *));
  if (!qh->qhmem.sizetable || !qh->qhmem.freelists) {
    qh_fprintf(qh, qh->qhmem.ferr, 6086, "qhull error (qh_meminit): insufficient memory\n");
    qh_errexit(qh, qhmem_ERRmem, NULL, NULL);
  }
  if (qh->qhmem.IStracing >= 1)
    qh_fprintf(qh, qh->qhmem.ferr, 8059, "qh_meminitbuffers: memory initialized with alignment %d\n", alignment);
} /* meminitbuffers */

/*---------------------------------

  qh_memsetup(qh)
    set up memory after running memsize()
*/
void qh_memsetup(qhT *qh) {
  int k,i;

  qsort(qh->qhmem.sizetable, (size_t)qh->qhmem.TABLEsize, sizeof(int), qh_intcompare);
  qh->qhmem.LASTsize= qh->qhmem.sizetable[qh->qhmem.TABLEsize-1];
  if(qh->qhmem.LASTsize >= qh->qhmem.BUFsize || qh->qhmem.LASTsize >= qh->qhmem.BUFinit) {
    qh_fprintf(qh, qh->qhmem.ferr, 6087, "qhull error (qh_memsetup): largest mem size %d is >= buffer size %d or initial buffer size %d\n",
            qh->qhmem.LASTsize, qh->qhmem.BUFsize, qh->qhmem.BUFinit);
    qh_errexit(qh, qhmem_ERRmem, NULL, NULL);
  }
  if (!(qh->qhmem.indextable= (int *)qh_malloc((qh->qhmem.LASTsize+1) * sizeof(int)))) {
    qh_fprintf(qh, qh->qhmem.ferr, 6088, "qhull error (qh_memsetup): insufficient memory\n");
    qh_errexit(qh, qhmem_ERRmem, NULL, NULL);
  }
  for (k=qh->qhmem.LASTsize+1; k--; )
    qh->qhmem.indextable[k]= k;
  i= 0;
  for (k=0; k <= qh->qhmem.LASTsize; k++) {
    if (qh->qhmem.indextable[k] <= qh->qhmem.sizetable[i])
      qh->qhmem.indextable[k]= i;
    else
      qh->qhmem.indextable[k]= ++i;
  }
} /* memsetup */

/*---------------------------------

  qh_memsize(qh, size )
    define a free list for this size
*/
void qh_memsize(qhT *qh, int size) {
  int k;

  if(qh->qhmem.LASTsize) {
    qh_fprintf(qh, qh->qhmem.ferr, 6089, "qhull error (qh_memsize): called after qhmem_setup\n");
    qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
  }
  size= (size + qh->qhmem.ALIGNmask) & ~qh->qhmem.ALIGNmask;
  for (k=qh->qhmem.TABLEsize; k--; ) {
    if (qh->qhmem.sizetable[k] == size)
      return;
  }
  if (qh->qhmem.TABLEsize < qh->qhmem.NUMsizes)
    qh->qhmem.sizetable[qh->qhmem.TABLEsize++]= size;
  else
    qh_fprintf(qh, qh->qhmem.ferr, 7060, "qhull warning (memsize): free list table has room for only %d sizes\n", qh->qhmem.NUMsizes);
} /* memsize */


/*---------------------------------

  qh_memstatistics(qh, fp )
    print out memory statistics

    Verifies that qh->qhmem.totfree == sum of freelists
*/
void qh_memstatistics(qhT *qh, FILE *fp) {
  int i;
  int count;
  void *object;

  qh_memcheck(qh);
  qh_fprintf(qh, fp, 9278, "\nmemory statistics:\n\
%7d quick allocations\n\
%7d short allocations\n\
%7d long allocations\n\
%7d short frees\n\
%7d long frees\n\
%7d bytes of short memory in use\n\
%7d bytes of short memory in freelists\n\
%7d bytes of dropped short memory\n\
%7d bytes of unused short memory (estimated)\n\
%7d bytes of long memory allocated (max, except for input)\n\
%7d bytes of long memory in use (in %d pieces)\n\
%7d bytes of short memory buffers (minus links)\n\
%7d bytes per short memory buffer (initially %d bytes)\n",
           qh->qhmem.cntquick, qh->qhmem.cntshort, qh->qhmem.cntlong,
           qh->qhmem.freeshort, qh->qhmem.freelong,
           qh->qhmem.totshort, qh->qhmem.totfree,
           qh->qhmem.totdropped + qh->qhmem.freesize, qh->qhmem.totunused,
           qh->qhmem.maxlong, qh->qhmem.totlong, qh->qhmem.cntlong - qh->qhmem.freelong,
           qh->qhmem.totbuffer, qh->qhmem.BUFsize, qh->qhmem.BUFinit);
  if (qh->qhmem.cntlarger) {
    qh_fprintf(qh, fp, 9279, "%7d calls to qh_setlarger\n%7.2g     average copy size\n",
           qh->qhmem.cntlarger, ((float)qh->qhmem.totlarger)/(float)qh->qhmem.cntlarger);
    qh_fprintf(qh, fp, 9280, "  freelists(bytes->count):");
  }
  for (i=0; i < qh->qhmem.TABLEsize; i++) {
    count=0;
    for (object= qh->qhmem.freelists[i]; object; object= *((void **)object))
      count++;
    qh_fprintf(qh, fp, 9281, " %d->%d", qh->qhmem.sizetable[i], count);
  }
  qh_fprintf(qh, fp, 9282, "\n\n");
} /* memstatistics */


/*---------------------------------

  qh_NOmem
    turn off quick-fit memory allocation

  notes:
    uses qh_malloc() and qh_free() instead
*/
#else /* qh_NOmem */

void *qh_memalloc(qhT *qh, int insize) {
  void *object;

  if (!(object= qh_malloc((size_t)insize))) {
    qh_fprintf(qh, qh->qhmem.ferr, 6090, "qhull error (qh_memalloc): insufficient memory\n");
    qh_errexit(qh, qhmem_ERRmem, NULL, NULL);
  }
  qh->qhmem.cntlong++;
  qh->qhmem.totlong += insize;
  if (qh->qhmem.maxlong < qh->qhmem.totlong)
      qh->qhmem.maxlong= qh->qhmem.totlong;
  if (qh->qhmem.IStracing >= 5)
    qh_fprintf(qh, qh->qhmem.ferr, 8060, "qh_mem %p n %8d alloc long: %d bytes (tot %d cnt %d)\n", object, qh->qhmem.cntlong+qh->qhmem.freelong, insize, qh->qhmem.totlong, qh->qhmem.cntlong-qh->qhmem.freelong);
  return object;
}

void qh_memfree(qhT *qh, void *object, int insize) {

  if (!object)
    return;
  qh_free(object);
  qh->qhmem.freelong++;
  qh->qhmem.totlong -= insize;
  if (qh->qhmem.IStracing >= 5)
    qh_fprintf(qh, qh->qhmem.ferr, 8061, "qh_mem %p n %8d free long: %d bytes (tot %d cnt %d)\n", object, qh->qhmem.cntlong+qh->qhmem.freelong, insize, qh->qhmem.totlong, qh->qhmem.cntlong-qh->qhmem.freelong);
}

void qh_memfreeshort(qhT *qh, int *curlong, int *totlong) {
  *totlong= qh->qhmem.totlong;
  *curlong= qh->qhmem.cntlong - qh->qhmem.freelong;
  memset((char *)&qh->qhmem, 0, sizeof(qh->qhmem));  /* every field is 0, FALSE, NULL */
}

void qh_meminit(qhT *qh, FILE *ferr) {

  memset((char *)&qh->qhmem, 0, sizeof(qh->qhmem));  /* every field is 0, FALSE, NULL */
  if (ferr)
      qh->qhmem.ferr= ferr;
  else
      qh->qhmem.ferr= stderr;
  if (sizeof(void*) < sizeof(int)) {
    qh_fprintf(qh, qh->qhmem.ferr, 6091, "qhull internal error (qh_meminit): sizeof(void*) %d < sizeof(int) %d.  qset.c will not work\n", (int)sizeof(void*), (int)sizeof(int));
    qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
  }
}

void qh_meminitbuffers(qhT *qh, int tracelevel, int alignment, int numsizes, int bufsize, int bufinit) {

  qh->qhmem.IStracing= tracelevel;
}

void qh_memsetup(qhT *qh) {

}

void qh_memsize(qhT *qh, int size) {

}

void qh_memstatistics(qhT *qh, FILE *fp) {

  qh_fprintf(qh, fp, 9409, "\nmemory statistics:\n\
%7d long allocations\n\
%7d long frees\n\
%7d bytes of long memory allocated (max, except for input)\n\
%7d bytes of long memory in use (in %d pieces)\n",
           qh->qhmem.cntlong,
           qh->qhmem.freelong,
           qh->qhmem.maxlong, qh->qhmem.totlong, qh->qhmem.cntlong - qh->qhmem.freelong);
}

#endif /* qh_NOmem */

/*---------------------------------

  qh_memtotal(qh, totlong, curlong, totshort, curshort, maxlong, totbuffer )
    Return the total, allocated long and short memory

  returns:
    Returns the total current bytes of long and short allocations
    Returns the current count of long and short allocations
    Returns the maximum long memory and total short buffer (minus one link per buffer)
    Does not error (for deprecated UsingLibQhull.cpp (libqhullpcpp))
*/
void qh_memtotal(qhT *qh, int *totlong, int *curlong, int *totshort, int *curshort, int *maxlong, int *totbuffer) {
    *totlong= qh->qhmem.totlong;
    *curlong= qh->qhmem.cntlong - qh->qhmem.freelong;
    *totshort= qh->qhmem.totshort;
    *curshort= qh->qhmem.cntshort + qh->qhmem.cntquick - qh->qhmem.freeshort;
    *maxlong= qh->qhmem.maxlong;
    *totbuffer= qh->qhmem.totbuffer;
} /* memtotlong */

geometry/src/geom2_r.c0000644000176200001440000020477513432323610014406 0ustar  liggesusers/*
  ---------------------------------


   geom2_r.c
   infrequently used geometric routines of qhull

   see qh-geom_r.htm and geom_r.h

   Copyright (c) 1993-2015 The Geometry Center.
   $Id: //main/2015/qhull/src/libqhull_r/geom2_r.c#6 $$Change: 2065 $
   $DateTime: 2016/01/18 13:51:04 $$Author: bbarber $

   frequently used code goes into geom_r.c
*/

#include "qhull_ra.h"

/*================== functions in alphabetic order ============*/

/*---------------------------------

  qh_copypoints(qh, points, numpoints, dimension)
    return qh_malloc'd copy of points
  
  notes:
    qh_free the returned points to avoid a memory leak
*/
coordT *qh_copypoints(qhT *qh, coordT *points, int numpoints, int dimension) {
  int size;
  coordT *newpoints;

  size= numpoints * dimension * (int)sizeof(coordT);
  if (!(newpoints= (coordT*)qh_malloc((size_t)size))) {
    qh_fprintf(qh, qh->ferr, 6004, "qhull error: insufficient memory to copy %d points\n",
        numpoints);
    qh_errexit(qh, qh_ERRmem, NULL, NULL);
  }
  memcpy((char *)newpoints, (char *)points, (size_t)size); /* newpoints!=0 by QH6004 */
  return newpoints;
} /* copypoints */

/*---------------------------------

  qh_crossproduct( dim, vecA, vecB, vecC )
    crossproduct of 2 dim vectors
    C= A x B

  notes:
    from Glasner, Graphics Gems I, p. 639
    only defined for dim==3
*/
void qh_crossproduct(int dim, realT vecA[3], realT vecB[3], realT vecC[3]){

  if (dim == 3) {
    vecC[0]=   det2_(vecA[1], vecA[2],
                     vecB[1], vecB[2]);
    vecC[1]= - det2_(vecA[0], vecA[2],
                     vecB[0], vecB[2]);
    vecC[2]=   det2_(vecA[0], vecA[1],
                     vecB[0], vecB[1]);
  }
} /* vcross */

/*---------------------------------

  qh_determinant(qh, rows, dim, nearzero )
    compute signed determinant of a square matrix
    uses qh.NEARzero to test for degenerate matrices

  returns:
    determinant
    overwrites rows and the matrix
    if dim == 2 or 3
      nearzero iff determinant < qh->NEARzero[dim-1]
      (!quite correct, not critical)
    if dim >= 4
      nearzero iff diagonal[k] < qh->NEARzero[k]
*/
realT qh_determinant(qhT *qh, realT **rows, int dim, boolT *nearzero) {
  realT det=0;
  int i;
  boolT sign= False;

  *nearzero= False;
  if (dim < 2) {
    qh_fprintf(qh, qh->ferr, 6005, "qhull internal error (qh_determinate): only implemented for dimension >= 2\n");
    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
  }else if (dim == 2) {
    det= det2_(rows[0][0], rows[0][1],
                 rows[1][0], rows[1][1]);
    if (fabs_(det) < 10*qh->NEARzero[1])  /* 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(qh, rows, dim, dim, &sign, nearzero);  /* if nearzero, diagonal still ok*/
    det= 1.0;
    for (i=dim; i--; )
      det *= (rows[i])[i];
    if (sign)
      det= -det;
  }
  return det;
} /* determinant */

/*---------------------------------

  qh_detjoggle(qh, points, numpoints, dimension )
    determine default max joggle for point array
      as qh_distround * qh_JOGGLEdefault

  returns:
    initial value for JOGGLEmax from points and REALepsilon

  notes:
    computes DISTround since qh_maxmin not called yet
    if qh->SCALElast, last dimension will be scaled later to MAXwidth

    loop duplicated from qh_maxmin
*/
realT qh_detjoggle(qhT *qh, pointT *points, int numpoints, int dimension) {
  realT abscoord, distround, joggle, maxcoord, mincoord;
  pointT *point, *pointtemp;
  realT maxabs= -REALmax;
  realT sumabs= 0;
  realT maxwidth= 0;
  int k;

  for (k=0; k < dimension; k++) {
    if (qh->SCALElast && k == dimension-1)
      abscoord= maxwidth;
    else if (qh->DELAUNAY && k == dimension-1) /* will qh_setdelaunay() */
      abscoord= 2 * maxabs * maxabs;  /* may be low by qh->hull_dim/2 */
    else {
      maxcoord= -REALmax;
      mincoord= REALmax;
      FORALLpoint_(qh, points, numpoints) {
        maximize_(maxcoord, point[k]);
        minimize_(mincoord, point[k]);
      }
      maximize_(maxwidth, maxcoord-mincoord);
      abscoord= fmax_(maxcoord, -mincoord);
    }
    sumabs += abscoord;
    maximize_(maxabs, abscoord);
  } /* for k */
  distround= qh_distround(qh, qh->hull_dim, maxabs, sumabs);
  joggle= distround * qh_JOGGLEdefault;
  maximize_(joggle, REALepsilon * qh_JOGGLEdefault);
  trace2((qh, qh->ferr, 2001, "qh_detjoggle: joggle=%2.2g maxwidth=%2.2g\n", joggle, maxwidth));
  return joggle;
} /* detjoggle */

/*---------------------------------

  qh_detroundoff(qh)
    determine maximum roundoff errors from
      REALepsilon, REALmax, REALmin, qh.hull_dim, qh.MAXabs_coord,
      qh.MAXsumcoord, qh.MAXwidth, qh.MINdenom_1

    accounts for qh.SETroundoff, qh.RANDOMdist, qh->MERGEexact
      qh.premerge_cos, qh.postmerge_cos, qh.premerge_centrum,
      qh.postmerge_centrum, qh.MINoutside,
      qh_RATIOnearinside, qh_COPLANARratio, qh_WIDEcoplanar

  returns:
    sets qh.DISTround, etc. (see below)
    appends precision constants to qh.qhull_options

  see:
    qh_maxmin() for qh.NEARzero

  design:
    determine qh.DISTround for distance computations
    determine minimum denominators for qh_divzero
    determine qh.ANGLEround for angle computations
    adjust qh.premerge_cos,... for roundoff error
    determine qh.ONEmerge for maximum error due to a single merge
    determine qh.NEARinside, qh.MAXcoplanar, qh.MINvisible,
      qh.MINoutside, qh.WIDEfacet
    initialize qh.max_vertex and qh.minvertex
*/
void qh_detroundoff(qhT *qh) {

  qh_option(qh, "_max-width", NULL, &qh->MAXwidth);
  if (!qh->SETroundoff) {
    qh->DISTround= qh_distround(qh, qh->hull_dim, qh->MAXabs_coord, qh->MAXsumcoord);
    if (qh->RANDOMdist)
      qh->DISTround += qh->RANDOMfactor * qh->MAXabs_coord;
    qh_option(qh, "Error-roundoff", NULL, &qh->DISTround);
  }
  qh->MINdenom= qh->MINdenom_1 * qh->MAXabs_coord;
  qh->MINdenom_1_2= sqrt(qh->MINdenom_1 * qh->hull_dim) ;  /* if will be normalized */
  qh->MINdenom_2= qh->MINdenom_1_2 * qh->MAXabs_coord;
                                              /* for inner product */
  qh->ANGLEround= 1.01 * qh->hull_dim * REALepsilon;
  if (qh->RANDOMdist)
    qh->ANGLEround += qh->RANDOMfactor;
  if (qh->premerge_cos < REALmax/2) {
    qh->premerge_cos -= qh->ANGLEround;
    if (qh->RANDOMdist)
      qh_option(qh, "Angle-premerge-with-random", NULL, &qh->premerge_cos);
  }
  if (qh->postmerge_cos < REALmax/2) {
    qh->postmerge_cos -= qh->ANGLEround;
    if (qh->RANDOMdist)
      qh_option(qh, "Angle-postmerge-with-random", NULL, &qh->postmerge_cos);
  }
  qh->premerge_centrum += 2 * qh->DISTround;    /*2 for centrum and distplane()*/
  qh->postmerge_centrum += 2 * qh->DISTround;
  if (qh->RANDOMdist && (qh->MERGEexact || qh->PREmerge))
    qh_option(qh, "Centrum-premerge-with-random", NULL, &qh->premerge_centrum);
  if (qh->RANDOMdist && qh->POSTmerge)
    qh_option(qh, "Centrum-postmerge-with-random", NULL, &qh->postmerge_centrum);
  { /* compute ONEmerge, max vertex offset for merging simplicial facets */
    realT maxangle= 1.0, maxrho;

    minimize_(maxangle, qh->premerge_cos);
    minimize_(maxangle, qh->postmerge_cos);
    /* max diameter * sin theta + DISTround for vertex to its hyperplane */
    qh->ONEmerge= sqrt((realT)qh->hull_dim) * qh->MAXwidth *
      sqrt(1.0 - maxangle * maxangle) + qh->DISTround;
    maxrho= qh->hull_dim * qh->premerge_centrum + qh->DISTround;
    maximize_(qh->ONEmerge, maxrho);
    maxrho= qh->hull_dim * qh->postmerge_centrum + qh->DISTround;
    maximize_(qh->ONEmerge, maxrho);
    if (qh->MERGING)
      qh_option(qh, "_one-merge", NULL, &qh->ONEmerge);
  }
  qh->NEARinside= qh->ONEmerge * qh_RATIOnearinside; /* only used if qh->KEEPnearinside */
  if (qh->JOGGLEmax < REALmax/2 && (qh->KEEPcoplanar || qh->KEEPinside)) {
    realT maxdist;             /* adjust qh.NEARinside for joggle */
    qh->KEEPnearinside= True;
    maxdist= sqrt((realT)qh->hull_dim) * qh->JOGGLEmax + qh->DISTround;
    maxdist= 2*maxdist;        /* vertex and coplanar point can joggle in opposite directions */
    maximize_(qh->NEARinside, maxdist);  /* must agree with qh_nearcoplanar() */
  }
  if (qh->KEEPnearinside)
    qh_option(qh, "_near-inside", NULL, &qh->NEARinside);
  if (qh->JOGGLEmax < qh->DISTround) {
    qh_fprintf(qh, qh->ferr, 6006, "qhull error: the joggle for 'QJn', %.2g, is below roundoff for distance computations, %.2g\n",
         qh->JOGGLEmax, qh->DISTround);
    qh_errexit(qh, qh_ERRinput, NULL, NULL);
  }
  if (qh->MINvisible > REALmax/2) {
    if (!qh->MERGING)
      qh->MINvisible= qh->DISTround;
    else if (qh->hull_dim <= 3)
      qh->MINvisible= qh->premerge_centrum;
    else
      qh->MINvisible= qh_COPLANARratio * qh->premerge_centrum;
    if (qh->APPROXhull && qh->MINvisible > qh->MINoutside)
      qh->MINvisible= qh->MINoutside;
    qh_option(qh, "Visible-distance", NULL, &qh->MINvisible);
  }
  if (qh->MAXcoplanar > REALmax/2) {
    qh->MAXcoplanar= qh->MINvisible;
    qh_option(qh, "U-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(qh, "Width-outside", NULL, &qh->MINoutside);
  }
  qh->WIDEfacet= qh->MINoutside;
  maximize_(qh->WIDEfacet, qh_WIDEcoplanar * qh->MAXcoplanar);
  maximize_(qh->WIDEfacet, qh_WIDEcoplanar * qh->MINvisible);
  qh_option(qh, "_wide-facet", NULL, &qh->WIDEfacet);
  if (qh->MINvisible > qh->MINoutside + 3 * REALepsilon
  && !qh->BESToutside && !qh->FORCEoutput)
    qh_fprintf(qh, qh->ferr, 7001, "qhull input warning: minimum visibility V%.2g is greater than \nminimum outside W%.2g.  Flipped facets are likely.\n",
             qh->MINvisible, qh->MINoutside);
  qh->max_vertex= qh->DISTround;
  qh->min_vertex= -qh->DISTround;
  /* numeric constants reported in printsummary */
} /* detroundoff */

/*---------------------------------

  qh_detsimplex(qh, apex, points, dim, nearzero )
    compute determinant of a simplex with point apex and base points

  returns:
     signed determinant and nearzero from qh_determinant

  notes:
     uses qh.gm_matrix/qh.gm_row (assumes they're big enough)

  design:
    construct qm_matrix by subtracting apex from points
    compute determinate
*/
realT qh_detsimplex(qhT *qh, pointT *apex, setT *points, int dim, boolT *nearzero) {
  pointT *coorda, *coordp, *gmcoord, *point, **pointp;
  coordT **rows;
  int k,  i=0;
  realT det;

  zinc_(Zdetsimplex);
  gmcoord= qh->gm_matrix;
  rows= qh->gm_row;
  FOREACHpoint_(points) {
    if (i == dim)
      break;
    rows[i++]= gmcoord;
    coordp= point;
    coorda= apex;
    for (k=dim; k--; )
      *(gmcoord++)= *coordp++ - *coorda++;
  }
  if (i < dim) {
    qh_fprintf(qh, qh->ferr, 6007, "qhull internal error (qh_detsimplex): #points %d < dimension %d\n",
               i, dim);
    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
  }
  det= qh_determinant(qh, rows, dim, nearzero);
  trace2((qh, qh->ferr, 2002, "qh_detsimplex: det=%2.2g for point p%d, dim %d, nearzero? %d\n",
          det, qh_pointid(qh, apex), dim, *nearzero));
  return det;
} /* detsimplex */

/*---------------------------------

  qh_distnorm( dim, point, normal, offset )
    return distance from point to hyperplane at normal/offset

  returns:
    dist

  notes:
    dist > 0 if point is outside of hyperplane

  see:
    qh_distplane in geom_r.c
*/
realT qh_distnorm(int dim, pointT *point, pointT *normal, realT *offsetp) {
  coordT *normalp= normal, *coordp= point;
  realT dist;
  int k;

  dist= *offsetp;
  for (k=dim; k--; )
    dist += *(coordp++) * *(normalp++);
  return dist;
} /* distnorm */

/*---------------------------------

  qh_distround(qh, dimension, maxabs, maxsumabs )
    compute maximum round-off error for a distance computation
      to a normalized hyperplane
    maxabs is the maximum absolute value of a coordinate
    maxsumabs is the maximum possible sum of absolute coordinate values

  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(qhT *qh, 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, qh->ferr, 4008, "qh_distround: %2.2g maxabs %2.2g maxsumabs %2.2g maxdistsum %2.2g\n",
                 maxround, maxabs, maxsumabs, maxdistsum));
  return maxround;
} /* distround */

/*---------------------------------

  qh_divzero( numer, denom, mindenom1, zerodiv )
    divide by a number that's nearly zero
    mindenom1= minimum denominator for dividing into 1.0

  returns:
    quotient
    sets zerodiv and returns 0.0 if it would overflow

  design:
    if numer is nearly zero and abs(numer) < abs(denom)
      return numer/denom
    else if numer is nearly zero
      return 0 and zerodiv
    else if denom/numer non-zero
      return numer/denom
    else
      return 0 and zerodiv
*/
realT qh_divzero(realT numer, realT denom, realT mindenom1, boolT *zerodiv) {
  realT temp, numerx, denomx;


  if (numer < mindenom1 && numer > -mindenom1) {
    numerx= fabs_(numer);
    denomx= fabs_(denom);
    if (numerx < denomx) {
      *zerodiv= False;
      return numer/denom;
    }else {
      *zerodiv= True;
      return 0.0;
    }
  }
  temp= denom/numer;
  if (temp > mindenom1 || temp < -mindenom1) {
    *zerodiv= False;
    return numer/denom;
  }else {
    *zerodiv= True;
    return 0.0;
  }
} /* divzero */


/*---------------------------------

  qh_facetarea(qh, facet )
    return area for a facet

  notes:
    if non-simplicial,
      uses centrum to triangulate facet and sums the projected areas.
    if (qh->DELAUNAY),
      computes projected area instead for last coordinate
    assumes facet->normal exists
    projecting tricoplanar facets to the hyperplane does not appear to make a difference

  design:
    if simplicial
      compute area
    else
      for each ridge
        compute area from centrum to ridge
    negate area if upper Delaunay facet
*/
realT qh_facetarea(qhT *qh, facetT *facet) {
  vertexT *apex;
  pointT *centrum;
  realT area= 0.0;
  ridgeT *ridge, **ridgep;

  if (facet->simplicial) {
    apex= SETfirstt_(facet->vertices, vertexT);
    area= qh_facetarea_simplex(qh, qh->hull_dim, apex->point, facet->vertices,
                    apex, facet->toporient, facet->normal, &facet->offset);
  }else {
    if (qh->CENTERtype == qh_AScentrum)
      centrum= facet->center;
    else
      centrum= qh_getcentrum(qh, facet);
    FOREACHridge_(facet->ridges)
      area += qh_facetarea_simplex(qh, qh->hull_dim, centrum, ridge->vertices,
                 NULL, (boolT)(ridge->top == facet),  facet->normal, &facet->offset);
    if (qh->CENTERtype != qh_AScentrum)
      qh_memfree(qh, centrum, qh->normal_size);
  }
  if (facet->upperdelaunay && qh->DELAUNAY)
    area= -area;  /* the normal should be [0,...,1] */
  trace4((qh, qh->ferr, 4009, "qh_facetarea: f%d area %2.2g\n", facet->id, area));
  return area;
} /* facetarea */

/*---------------------------------

  qh_facetarea_simplex(qh, dim, apex, vertices, notvertex, toporient, normal, offset )
    return area for a simplex defined by
      an apex, a base of vertices, an orientation, and a unit normal
    if simplicial or tricoplanar facet,
      notvertex is defined and it is skipped in vertices

  returns:
    computes area of simplex projected to plane [normal,offset]
    returns 0 if vertex too far below plane (qh->WIDEfacet)
      vertex can't be apex of tricoplanar facet

  notes:
    if (qh->DELAUNAY),
      computes projected area instead for last coordinate
    uses qh->gm_matrix/gm_row and qh->hull_dim
    helper function for qh_facetarea

  design:
    if Notvertex
      translate simplex to apex
    else
      project simplex to normal/offset
      translate simplex to apex
    if Delaunay
      set last row/column to 0 with -1 on diagonal
    else
      set last row to Normal
    compute determinate
    scale and flip sign for area
*/
realT qh_facetarea_simplex(qhT *qh, int dim, coordT *apex, setT *vertices,
        vertexT *notvertex,  boolT toporient, coordT *normal, realT *offset) {
  pointT *coorda, *coordp, *gmcoord;
  coordT **rows, *normalp;
  int k,  i=0;
  realT area, dist;
  vertexT *vertex, **vertexp;
  boolT nearzero;

  gmcoord= qh->gm_matrix;
  rows= qh->gm_row;
  FOREACHvertex_(vertices) {
    if (vertex == notvertex)
      continue;
    rows[i++]= gmcoord;
    coorda= apex;
    coordp= vertex->point;
    normalp= normal;
    if (notvertex) {
      for (k=dim; k--; )
        *(gmcoord++)= *coordp++ - *coorda++;
    }else {
      dist= *offset;
      for (k=dim; k--; )
        dist += *coordp++ * *normalp++;
      if (dist < -qh->WIDEfacet) {
        zinc_(Znoarea);
        return 0.0;
      }
      coordp= vertex->point;
      normalp= normal;
      for (k=dim; k--; )
        *(gmcoord++)= (*coordp++ - dist * *normalp++) - *coorda++;
    }
  }
  if (i != dim-1) {
    qh_fprintf(qh, qh->ferr, 6008, "qhull internal error (qh_facetarea_simplex): #points %d != dim %d -1\n",
               i, dim);
    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
  }
  rows[i]= gmcoord;
  if (qh->DELAUNAY) {
    for (i=0; i < dim-1; i++)
      rows[i][dim-1]= 0.0;
    for (k=dim; k--; )
      *(gmcoord++)= 0.0;
    rows[dim-1][dim-1]= -1.0;
  }else {
    normalp= normal;
    for (k=dim; k--; )
      *(gmcoord++)= *normalp++;
  }
  zinc_(Zdetsimplex);
  area= qh_determinant(qh, rows, dim, &nearzero);
  if (toporient)
    area= -area;
  area *= qh->AREAfactor;
  trace4((qh, qh->ferr, 4010, "qh_facetarea_simplex: area=%2.2g for point p%d, toporient %d, nearzero? %d\n",
          area, qh_pointid(qh, apex), toporient, nearzero));
  return area;
} /* facetarea_simplex */

/*---------------------------------

  qh_facetcenter(qh, vertices )
    return Voronoi center (Voronoi vertex) for a facet's vertices

  returns:
    return temporary point equal to the center

  see:
    qh_voronoi_center()
*/
pointT *qh_facetcenter(qhT *qh, setT *vertices) {
  setT *points= qh_settemp(qh, qh_setsize(qh, vertices));
  vertexT *vertex, **vertexp;
  pointT *center;

  FOREACHvertex_(vertices)
    qh_setappend(qh, &points, vertex->point);
  center= qh_voronoi_center(qh, qh->hull_dim-1, points);
  qh_settempfree(qh, &points);
  return center;
} /* facetcenter */

/*---------------------------------

  qh_findgooddist(qh, point, facetA, dist, facetlist )
    find best good facet visible for point from facetA
    assumes facetA is visible from point

  returns:
    best facet, i.e., good facet that is furthest from point
      distance to best facet
      NULL if none

    moves good, visible facets (and some other visible facets)
      to end of qh->facet_list

  notes:
    uses qh->visit_id

  design:
    initialize bestfacet if facetA is good
    move facetA to end of facetlist
    for each facet on facetlist
      for each unvisited neighbor of facet
        move visible neighbors to end of facetlist
        update best good neighbor
        if no good neighbors, update best facet
*/
facetT *qh_findgooddist(qhT *qh, pointT *point, facetT *facetA, realT *distp,
               facetT **facetlist) {
  realT bestdist= -REALmax, dist;
  facetT *neighbor, **neighborp, *bestfacet=NULL, *facet;
  boolT goodseen= False;

  if (facetA->good) {
    zzinc_(Zcheckpart);  /* calls from check_bestdist occur after print stats */
    qh_distplane(qh, point, facetA, &bestdist);
    bestfacet= facetA;
    goodseen= True;
  }
  qh_removefacet(qh, facetA);
  qh_appendfacet(qh, facetA);
  *facetlist= facetA;
  facetA->visitid= ++qh->visit_id;
  FORALLfacet_(*facetlist) {
    FOREACHneighbor_(facet) {
      if (neighbor->visitid == qh->visit_id)
        continue;
      neighbor->visitid= qh->visit_id;
      if (goodseen && !neighbor->good)
        continue;
      zzinc_(Zcheckpart);
      qh_distplane(qh, point, neighbor, &dist);
      if (dist > 0) {
        qh_removefacet(qh, neighbor);
        qh_appendfacet(qh, neighbor);
        if (neighbor->good) {
          goodseen= True;
          if (dist > bestdist) {
            bestdist= dist;
            bestfacet= neighbor;
          }
        }
      }
    }
  }
  if (bestfacet) {
    *distp= bestdist;
    trace2((qh, qh->ferr, 2003, "qh_findgooddist: p%d is %2.2g above good facet f%d\n",
      qh_pointid(qh, point), bestdist, bestfacet->id));
    return bestfacet;
  }
  trace4((qh, qh->ferr, 4011, "qh_findgooddist: no good facet for p%d above f%d\n",
      qh_pointid(qh, point), facetA->id));
  return NULL;
}  /* findgooddist */

/*---------------------------------

  qh_getarea(qh, facetlist )
    set area of all facets in facetlist
    collect statistics
    nop if hasAreaVolume

  returns:
    sets qh->totarea/totvol to total area and volume of convex hull
    for Delaunay triangulation, computes projected area of the lower or upper hull
      ignores upper hull if qh->ATinfinity

  notes:
    could compute outer volume by expanding facet area by rays from interior
    the following attempt at perpendicular projection underestimated badly:
      qh.totoutvol += (-dist + facet->maxoutside + qh->DISTround)
                            * area/ qh->hull_dim;
  design:
    for each facet on facetlist
      compute facet->area
      update qh.totarea and qh.totvol
*/
void qh_getarea(qhT *qh, facetT *facetlist) {
  realT area;
  realT dist;
  facetT *facet;

  if (qh->hasAreaVolume)
    return;
  if (qh->REPORTfreq)
    qh_fprintf(qh, qh->ferr, 8020, "computing area of each facet and volume of the convex hull\n");
  else
    trace1((qh, qh->ferr, 1001, "qh_getarea: computing 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(qh, facet);
      facet->isarea= True;
    }
    area= facet->f.area;
    if (qh->DELAUNAY) {
      if (facet->upperdelaunay == qh->UPPERdelaunay)
        qh->totarea += area;
    }else {
      qh->totarea += area;
      qh_distplane(qh, qh->interior_point, facet, &dist);
      qh->totvol += -dist * area/ qh->hull_dim;
    }
    if (qh->PRINTstatistics) {
      wadd_(Wareatot, area);
      wmax_(Wareamax, area);
      wmin_(Wareamin, area);
    }
  }
  qh->hasAreaVolume= True;
} /* getarea */

/*---------------------------------

  qh_gram_schmidt(qh, dim, row )
    implements Gram-Schmidt orthogonalization by rows

  returns:
    false if zero norm
    overwrites rows[dim][dim]

  notes:
    see Golub & van Loan, 1983, Algorithm 6.2-2, "Modified Gram-Schmidt"
    overflow due to small divisors not handled

  design:
    for each row
      compute norm for row
      if non-zero, normalize row
      for each remaining rowA
        compute inner product of row and rowA
        reduce rowA by row * inner product
*/
boolT qh_gram_schmidt(qhT *qh, int dim, realT **row) {
  realT *rowi, *rowj, norm;
  int i, j, k;

  for (i=0; i < dim; i++) {
    rowi= row[i];
    for (norm= 0.0, k= dim; k--; rowi++)
      norm += *rowi * *rowi;
    norm= sqrt(norm);
    wmin_(Wmindenom, norm);
    if (norm == 0.0)  /* either 0 or overflow due to sqrt */
      return False;
    for (k=dim; k--; )
      *(--rowi) /= norm;
    for (j=i+1; j < dim; j++) {
      rowj= row[j];
      for (norm= 0.0, k=dim; k--; )
        norm += *rowi++ * *rowj++;
      for (k=dim; k--; )
        *(--rowj) -= *(--rowi) * norm;
    }
  }
  return True;
} /* gram_schmidt */


/*---------------------------------

  qh_inthresholds(qh, normal, angle )
    return True if normal within qh.lower_/upper_threshold

  returns:
    estimate of angle by summing of threshold diffs
      angle may be NULL
      smaller "angle" is better

  notes:
    invalid if qh.SPLITthresholds

  see:
    qh.lower_threshold in qh_initbuild()
    qh_initthresholds()

  design:
    for each dimension
      test threshold
*/
boolT qh_inthresholds(qhT *qh, coordT *normal, realT *angle) {
  boolT within= True;
  int k;
  realT threshold;

  if (angle)
    *angle= 0.0;
  for (k=0; k < qh->hull_dim; k++) {
    threshold= qh->lower_threshold[k];
    if (threshold > -REALmax/2) {
      if (normal[k] < threshold)
        within= False;
      if (angle) {
        threshold -= normal[k];
        *angle += fabs_(threshold);
      }
    }
    if (qh->upper_threshold[k] < REALmax/2) {
      threshold= qh->upper_threshold[k];
      if (normal[k] > threshold)
        within= False;
      if (angle) {
        threshold -= normal[k];
        *angle += fabs_(threshold);
      }
    }
  }
  return within;
} /* inthresholds */


/*---------------------------------

  qh_joggleinput(qh)
    randomly joggle input to Qhull by qh.JOGGLEmax
    initial input is qh.first_point/qh.num_points of qh.hull_dim
      repeated calls use qh.input_points/qh.num_points

  returns:
    joggles points at qh.first_point/qh.num_points
    copies data to qh.input_points/qh.input_malloc if first time
    determines qh.JOGGLEmax if it was zero
    if qh.DELAUNAY
      computes the Delaunay projection of the joggled points

  notes:
    if qh.DELAUNAY, unnecessarily joggles the last coordinate
    the initial 'QJn' may be set larger than qh_JOGGLEmaxincrease

  design:
    if qh.DELAUNAY
      set qh.SCALElast for reduced precision errors
    if first call
      initialize qh.input_points to the original input points
      if qh.JOGGLEmax == 0
        determine default qh.JOGGLEmax
    else
      increase qh.JOGGLEmax according to qh.build_cnt
    joggle the input by adding a random number in [-qh.JOGGLEmax,qh.JOGGLEmax]
    if qh.DELAUNAY
      sets the Delaunay projection
*/
void qh_joggleinput(qhT *qh) {
  int i, seed, size;
  coordT *coordp, *inputp;
  realT randr, randa, randb;

  if (!qh->input_points) { /* first call */
    qh->input_points= qh->first_point;
    qh->input_malloc= qh->POINTSmalloc;
    size= qh->num_points * qh->hull_dim * sizeof(coordT);
    if (!(qh->first_point=(coordT*)qh_malloc((size_t)size))) {
      qh_fprintf(qh, qh->ferr, 6009, "qhull error: insufficient memory to joggle %d points\n",
          qh->num_points);
      qh_errexit(qh, qh_ERRmem, NULL, NULL);
    }
    qh->POINTSmalloc= True;
    if (qh->JOGGLEmax == 0.0) {
      qh->JOGGLEmax= qh_detjoggle(qh, qh->input_points, qh->num_points, qh->hull_dim);
      qh_option(qh, "QJoggle", NULL, &qh->JOGGLEmax);
    }
  }else {                 /* repeated call */
    if (!qh->RERUN && qh->build_cnt > qh_JOGGLEretry) {
      if (((qh->build_cnt-qh_JOGGLEretry-1) % qh_JOGGLEagain) == 0) {
        realT maxjoggle= qh->MAXwidth * qh_JOGGLEmaxincrease;
        if (qh->JOGGLEmax < maxjoggle) {
          qh->JOGGLEmax *= qh_JOGGLEincrease;
          minimize_(qh->JOGGLEmax, maxjoggle);
        }
      }
    }
    qh_option(qh, "QJoggle", NULL, &qh->JOGGLEmax);
  }
  if (qh->build_cnt > 1 && qh->JOGGLEmax > fmax_(qh->MAXwidth/4, 0.1)) {
      qh_fprintf(qh, qh->ferr, 6010, "qhull 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, 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(qh, "_joggle-seed", &seed, NULL);
  trace0((qh, 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, qh->hull_dim, qh->num_points, qh->first_point);
  }
} /* joggleinput */

/*---------------------------------

  qh_maxabsval( normal, dim )
    return pointer to maximum absolute value of a dim vector
    returns NULL if dim=0
*/
realT *qh_maxabsval(realT *normal, int dim) {
  realT maxval= -REALmax;
  realT *maxp= NULL, *colp, absval;
  int k;

  for (k=dim, colp= normal; k--; colp++) {
    absval= fabs_(*colp);
    if (absval > maxval) {
      maxval= absval;
      maxp= colp;
    }
  }
  return maxp;
} /* maxabsval */


/*---------------------------------

  qh_maxmin(qh, points, numpoints, dimension )
    return max/min points for each dimension
    determine max and min coordinates

  returns:
    returns a temporary set of max and min points
      may include duplicate points. Does not include qh.GOODpoint
    sets qh.NEARzero, qh.MAXabs_coord, qh.MAXsumcoord, qh.MAXwidth
         qh.MAXlastcoord, qh.MINlastcoord
    initializes qh.max_outside, qh.min_vertex, qh.WAScoplanar, qh.ZEROall_ok

  notes:
    loop duplicated in qh_detjoggle()

  design:
    initialize global precision variables
    checks definition of REAL...
    for each dimension
      for each point
        collect maximum and minimum point
      collect maximum of maximums and minimum of minimums
      determine qh.NEARzero for Gaussian Elimination
*/
setT *qh_maxmin(qhT *qh, pointT *points, int numpoints, int dimension) {
  int k;
  realT maxcoord, temp;
  pointT *minimum, *maximum, *point, *pointtemp;
  setT *set;

  qh->max_outside= 0.0;
  qh->MAXabs_coord= 0.0;
  qh->MAXwidth= -REALmax;
  qh->MAXsumcoord= 0.0;
  qh->min_vertex= 0.0;
  qh->WAScoplanar= False;
  if (qh->ZEROcentrum)
    qh->ZEROall_ok= True;
  if (REALmin < REALepsilon && REALmin < REALmax && REALmin > -REALmax
  && REALmax > 0.0 && -REALmax < 0.0)
    ; /* all ok */
  else {
    qh_fprintf(qh, qh->ferr, 6011, "qhull error: 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, qh_ERRinput, NULL, NULL);
  }
  set= qh_settemp(qh, 2*dimension);
  for (k=0; k < dimension; k++) {
    if (points == qh->GOODpointp)
      minimum= maximum= points + dimension;
    else
      minimum= maximum= points;
    FORALLpoint_(qh, points, numpoints) {
      if (point == qh->GOODpointp)
        continue;
      if (maximum[k] < point[k])
        maximum= point;
      else if (minimum[k] > point[k])
        minimum= point;
    }
    if (k == dimension-1) {
      qh->MINlastcoord= minimum[k];
      qh->MAXlastcoord= maximum[k];
    }
    if (qh->SCALElast && k == dimension-1)
      maxcoord= qh->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(qh, &set, maximum);
    qh_setappend(qh, &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, qh->ferr, "qh_maxmin: found the max and min points(by dim):", set);
  return(set);
} /* maxmin */

/*---------------------------------

  qh_maxouter(qh)
    return maximum distance from facet to outer plane
    normally this is qh.max_outside+qh.DISTround
    does not include qh.JOGGLEmax

  see:
    qh_outerinner()

  notes:
    need to add another qh.DISTround if testing actual point with computation

  for joggle:
    qh_setfacetplane() updated qh.max_outer for Wnewvertexmax (max distance to vertex)
    need to use Wnewvertexmax since could have a coplanar point for a high
      facet that is replaced by a low facet
    need to add qh.JOGGLEmax if testing input points
*/
realT qh_maxouter(qhT *qh) {
  realT dist;

  dist= fmax_(qh->max_outside, qh->DISTround);
  dist += qh->DISTround;
  trace4((qh, qh->ferr, 4012, "qh_maxouter: max distance from facet to outer plane is %2.2g max_outside is %2.2g\n", dist, qh->max_outside));
  return dist;
} /* maxouter */

/*---------------------------------

  qh_maxsimplex(qh, 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(qhT *qh, int dim, setT *maxpoints, pointT *points, int numpoints, setT **simplex) {
  pointT *point, **pointp, *pointtemp, *maxpoint, *minx=NULL, *maxx=NULL;
  boolT nearzero, maxnearzero= False;
  int k, sizinit;
  realT maxdet= -REALmax, det, mincoord= REALmax, maxcoord= -REALmax;

  sizinit= qh_setsize(qh, *simplex);
  if (sizinit < 2) {
    if (qh_setsize(qh, maxpoints) >= 2) {
      FOREACHpoint_(maxpoints) {
        if (maxcoord < point[0]) {
          maxcoord= point[0];
          maxx= point;
        }
        if (mincoord > point[0]) {
          mincoord= point[0];
          minx= point;
        }
      }
    }else {
      FORALLpoint_(qh, points, numpoints) {
        if (point == qh->GOODpointp)
          continue;
        if (maxcoord < point[0]) {
          maxcoord= point[0];
          maxx= point;
        }
        if (mincoord > point[0]) {
          mincoord= point[0];
          minx= point;
        }
      }
    }
    qh_setunique(qh, simplex, minx);
    if (qh_setsize(qh, *simplex) < 2)
      qh_setunique(qh, simplex, maxx);
    sizinit= qh_setsize(qh, *simplex);
    if (sizinit < 2) {
      qh_precision(qh, "input has same x coordinate");
      if (zzval_(Zsetplane) > qh->hull_dim+1) {
        qh_fprintf(qh, qh->ferr, 6012, "qhull precision error (qh_maxsimplex for voronoi_center):\n%d points with the same x coordinate.\n",
                 qh_setsize(qh, maxpoints)+numpoints);
        qh_errexit(qh, qh_ERRprec, NULL, NULL);
      }else {
        qh_fprintf(qh, 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, 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(qh, point, *simplex, k, &nearzero);
        if ((det= fabs_(det)) > maxdet) {
          maxdet= det;
          maxpoint= point;
          maxnearzero= nearzero;
        }
      }
    }
    if (!maxpoint || maxnearzero) {
      zinc_(Zsearchpoints);
      if (!maxpoint) {
        trace0((qh, qh->ferr, 7, "qh_maxsimplex: searching all points for %d-th initial vertex.\n", k+1));
      }else {
        trace0((qh, 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(qh, maxpoint), maxdet));
      }
      FORALLpoint_(qh, points, numpoints) {
        if (point == qh->GOODpointp)
          continue;
        if (!qh_setin(*simplex, point)) {
          det= qh_detsimplex(qh, point, *simplex, k, &nearzero);
          if ((det= fabs_(det)) > maxdet) {
            maxdet= det;
            maxpoint= point;
            maxnearzero= nearzero;
          }
        }
      }
    } /* !maxpoint */
    if (!maxpoint) {
      qh_fprintf(qh, qh->ferr, 6014, "qhull internal error (qh_maxsimplex): not enough points available\n");
      qh_errexit(qh, qh_ERRqhull, NULL, NULL);
    }
    qh_setappend(qh, simplex, maxpoint);
    trace1((qh, qh->ferr, 1002, "qh_maxsimplex: selected point p%d for %d`th initial vertex, det=%2.2g\n",
            qh_pointid(qh, maxpoint), k+1, maxdet));
  } /* k */
} /* maxsimplex */

/*---------------------------------

  qh_minabsval( normal, dim )
    return minimum absolute value of a dim vector
*/
realT qh_minabsval(realT *normal, int dim) {
  realT minval= 0;
  realT maxval= 0;
  realT *colp;
  int k;

  for (k=dim, colp=normal; k--; colp++) {
    maximize_(maxval, *colp);
    minimize_(minval, *colp);
  }
  return fmax_(maxval, -minval);
} /* minabsval */


/*---------------------------------

  qh_mindif(qh, vecA, vecB, dim )
    return index of min abs. difference of two vectors
*/
int qh_mindiff(realT *vecA, realT *vecB, int dim) {
  realT mindiff= REALmax, diff;
  realT *vecAp= vecA, *vecBp= vecB;
  int k, mink= 0;

  for (k=0; k < dim; k++) {
    diff= *vecAp++ - *vecBp++;
    diff= fabs_(diff);
    if (diff < mindiff) {
      mindiff= diff;
      mink= k;
    }
  }
  return mink;
} /* mindiff */



/*---------------------------------

  qh_orientoutside(qh, facet  )
    make facet outside oriented via qh.interior_point

  returns:
    True if facet reversed orientation.
*/
boolT qh_orientoutside(qhT *qh, facetT *facet) {
  int k;
  realT dist;

  qh_distplane(qh, qh->interior_point, facet, &dist);
  if (dist > 0) {
    for (k=qh->hull_dim; k--; )
      facet->normal[k]= -facet->normal[k];
    facet->offset= -facet->offset;
    return True;
  }
  return False;
} /* orientoutside */

/*---------------------------------

  qh_outerinner(qh, facet, outerplane, innerplane  )
    if facet and qh.maxoutdone (i.e., qh_check_maxout)
      returns outer and inner plane for facet
    else
      returns maximum outer and inner plane
    accounts for qh.JOGGLEmax

  see:
    qh_maxouter(qh), qh_check_bestdist(), qh_check_points()

  notes:
    outerplaner or innerplane may be NULL
    facet is const
    Does not error (QhullFacet)

    includes qh.DISTround for actual points
    adds another qh.DISTround if testing with floating point arithmetic
*/
void qh_outerinner(qhT *qh, facetT *facet, realT *outerplane, realT *innerplane) {
  realT dist, mindist;
  vertexT *vertex, **vertexp;

  if (outerplane) {
    if (!qh_MAXoutside || !facet || !qh->maxoutdone) {
      *outerplane= qh_maxouter(qh);       /* includes qh.DISTround */
    }else { /* qh_MAXoutside ... */
#if qh_MAXoutside
      *outerplane= facet->maxoutside + qh->DISTround;
#endif

    }
    if (qh->JOGGLEmax < REALmax/2)
      *outerplane += qh->JOGGLEmax * sqrt((realT)qh->hull_dim);
  }
  if (innerplane) {
    if (facet) {
      mindist= REALmax;
      FOREACHvertex_(facet->vertices) {
        zinc_(Zdistio);
        qh_distplane(qh, vertex->point, facet, &dist);
        minimize_(mindist, dist);
      }
      *innerplane= mindist - qh->DISTround;
    }else
      *innerplane= qh->min_vertex - qh->DISTround;
    if (qh->JOGGLEmax < REALmax/2)
      *innerplane -= qh->JOGGLEmax * sqrt((realT)qh->hull_dim);
  }
} /* outerinner */

/*---------------------------------

  qh_pointdist( point1, point2, dim )
    return distance between two points

  notes:
    returns distance squared if 'dim' is negative
*/
coordT qh_pointdist(pointT *point1, pointT *point2, int dim) {
  coordT dist, diff;
  int k;

  dist= 0.0;
  for (k= (dim > 0 ? dim : -dim); k--; ) {
    diff= *point1++ - *point2++;
    dist += diff * diff;
  }
  if (dim > 0)
    return(sqrt(dist));
  return dist;
} /* pointdist */


/*---------------------------------

  qh_printmatrix(qh, fp, string, rows, numrow, numcol )
    print matrix to fp given by row vectors
    print string as header
    qh may be NULL if fp is defined

  notes:
    print a vector by qh_printmatrix(qh, fp, "", &vect, 1, len)
*/
void qh_printmatrix(qhT *qh, FILE *fp, const char *string, realT **rows, int numrow, int numcol) {
  realT *rowp;
  realT r; /*bug fix*/
  int i,k;

  qh_fprintf(qh, fp, 9001, "%s\n", string);
  for (i=0; i < numrow; i++) {
    rowp= rows[i];
    for (k=0; k < numcol; k++) {
      r= *rowp++;
      qh_fprintf(qh, fp, 9002, "%6.3g ", r);
    }
    qh_fprintf(qh, fp, 9003, "\n");
  }
} /* printmatrix */


/*---------------------------------

  qh_printpoints(qh, fp, string, points )
    print pointids to fp for a set of points
    if string, prints string and 'p' point ids
*/
void qh_printpoints(qhT *qh, FILE *fp, const char *string, setT *points) {
  pointT *point, **pointp;

  if (string) {
    qh_fprintf(qh, fp, 9004, "%s", string);
    FOREACHpoint_(points)
      qh_fprintf(qh, fp, 9005, " p%d", qh_pointid(qh, point));
    qh_fprintf(qh, fp, 9006, "\n");
  }else {
    FOREACHpoint_(points)
      qh_fprintf(qh, fp, 9007, " %d", qh_pointid(qh, point));
    qh_fprintf(qh, fp, 9008, "\n");
  }
} /* printpoints */


/*---------------------------------

  qh_projectinput(qh)
    project input points using qh.lower_bound/upper_bound and qh->DELAUNAY
    if qh.lower_bound[k]=qh.upper_bound[k]= 0,
      removes dimension k
    if halfspace intersection
      removes dimension k from qh.feasible_point
    input points in qh->first_point, num_points, input_dim

  returns:
    new point array in qh->first_point of qh->hull_dim coordinates
    sets qh->POINTSmalloc
    if qh->DELAUNAY
      projects points to paraboloid
      lowbound/highbound is also projected
    if qh->ATinfinity
      adds point "at-infinity"
    if qh->POINTSmalloc
      frees old point array

  notes:
    checks that qh.hull_dim agrees with qh.input_dim, PROJECTinput, and DELAUNAY


  design:
    sets project[k] to -1 (delete), 0 (keep), 1 (add for Delaunay)
    determines newdim and newnum for qh->hull_dim and qh->num_points
    projects points to newpoints
    projects qh.lower_bound to itself
    projects qh.upper_bound to itself
    if qh->DELAUNAY
      if qh->ATINFINITY
        projects points to paraboloid
        computes "infinity" point as vertex average and 10% above all points
      else
        uses qh_setdelaunay to project points to paraboloid
*/
void qh_projectinput(qhT *qh) {
  int k,i;
  int newdim= qh->input_dim, newnum= qh->num_points;
  signed char *project;
  int projectsize= (qh->input_dim+1)*sizeof(*project);
  pointT *newpoints, *coord, *infinity;
  realT paraboloid, maxboloid= 0;

  project= (signed char*)qh_memalloc(qh, projectsize);
  memset((char*)project, 0, (size_t)projectsize);
  for (k=0; k < qh->input_dim; k++) {   /* skip Delaunay bound */
    if (qh->lower_bound[k] == 0 && 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(qh, project, projectsize);
    qh_fprintf(qh, qh->ferr, 6015, "qhull internal error (qh_projectinput): dimension after projection %d != hull_dim %d\n", newdim, qh->hull_dim);
    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
  }
  if (!(newpoints= qh->temp_malloc= (coordT*)qh_malloc(newnum*newdim*sizeof(coordT)))){
    qh_memfree(qh, project, projectsize);
    qh_fprintf(qh, qh->ferr, 6016, "qhull error: insufficient memory to project %d points\n",
           qh->num_points);
    qh_errexit(qh, qh_ERRmem, NULL, NULL);
  }
  /* qh_projectpoints throws error if mismatched dimensions */
  qh_projectpoints(qh, project, qh->input_dim+1, qh->first_point,
                    qh->num_points, qh->input_dim, newpoints, newdim);
  trace1((qh, qh->ferr, 1003, "qh_projectinput: updating lower and upper_bound\n"));
  qh_projectpoints(qh, project, qh->input_dim+1, qh->lower_bound,
                    1, qh->input_dim+1, qh->lower_bound, newdim+1);
  qh_projectpoints(qh, project, qh->input_dim+1, qh->upper_bound,
                    1, qh->input_dim+1, qh->upper_bound, newdim+1);
  if (qh->HALFspace) {
    if (!qh->feasible_point) {
      qh_memfree(qh, project, projectsize);
      qh_fprintf(qh, qh->ferr, 6017, "qhull internal error (qh_projectinput): HALFspace defined without qh.feasible_point\n");
      qh_errexit(qh, qh_ERRqhull, NULL, NULL);
    }
    qh_projectpoints(qh, project, qh->input_dim, qh->feasible_point,
                      1, qh->input_dim, qh->feasible_point, newdim);
  }
  qh_memfree(qh, project, projectsize);
  if (qh->POINTSmalloc)
    qh_free(qh->first_point);
  qh->first_point= newpoints;
  qh->POINTSmalloc= True;
  qh->temp_malloc= NULL;
  if (qh->DELAUNAY && qh->ATinfinity) {
    coord= qh->first_point;
    infinity= qh->first_point + qh->hull_dim * qh->num_points;
    for (k=qh->hull_dim-1; k--; )
      infinity[k]= 0.0;
    for (i=qh->num_points; i--; ) {
      paraboloid= 0.0;
      for (k=0; k < qh->hull_dim-1; k++) {
        paraboloid += *coord * *coord;
        infinity[k] += *coord;
        coord++;
      }
      *(coord++)= paraboloid;
      maximize_(maxboloid, paraboloid);
    }
    /* coord == infinity */
    for (k=qh->hull_dim-1; k--; )
      *(coord++) /= qh->num_points;
    *(coord++)= maxboloid * 1.1;
    qh->num_points++;
    trace0((qh, qh->ferr, 9, "qh_projectinput: projected points to paraboloid for Delaunay\n"));
  }else if (qh->DELAUNAY)  /* !qh->ATinfinity */
    qh_setdelaunay(qh, qh->hull_dim, qh->num_points, qh->first_point);
} /* projectinput */


/*---------------------------------

  qh_projectpoints(qh, project, n, points, numpoints, dim, newpoints, newdim )
    project points/numpoints/dim to newpoints/newdim
    if project[k] == -1
      delete dimension k
    if project[k] == 1
      add dimension k by duplicating previous column
    n is size of project

  notes:
    newpoints may be points if only adding dimension at end

  design:
    check that 'project' and 'newdim' agree
    for each dimension
      if project == -1
        skip dimension
      else
        determine start of column in newpoints
        determine start of column in points
          if project == +1, duplicate previous column
        copy dimension (column) from points to newpoints
*/
void qh_projectpoints(qhT *qh, signed char *project, int n, realT *points,
        int numpoints, int dim, realT *newpoints, int newdim) {
  int testdim= dim, oldk=0, newk=0, i,j=0,k;
  realT *newp, *oldp;

  for (k=0; k < n; k++)
    testdim += project[k];
  if (testdim != newdim) {
    qh_fprintf(qh, qh->ferr, 6018, "qhull internal error (qh_projectpoints): newdim %d should be %d after projection\n",
      newdim, testdim);
    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
  }
  for (j=0; j= dim)
          continue;
        oldp= points+oldk;
      }else
        oldp= points+oldk++;
      for (i=numpoints; i--; ) {
        *newp= *oldp;
        newp += newdim;
        oldp += dim;
      }
    }
    if (oldk >= dim)
      break;
  }
  trace1((qh, qh->ferr, 1004, "qh_projectpoints: projected %d points from dim %d to dim %d\n",
    numpoints, dim, newdim));
} /* projectpoints */


/*---------------------------------

  qh_rotateinput(qh, rows )
    rotate input using row matrix
    input points given by qh->first_point, num_points, hull_dim
    assumes rows[dim] is a scratch buffer
    if qh->POINTSmalloc, overwrites input points, else mallocs a new array

  returns:
    rotated input
    sets qh->POINTSmalloc

  design:
    see qh_rotatepoints
*/
void qh_rotateinput(qhT *qh, realT **rows) {

  if (!qh->POINTSmalloc) {
    qh->first_point= qh_copypoints(qh, qh->first_point, qh->num_points, qh->hull_dim);
    qh->POINTSmalloc= True;
  }
  qh_rotatepoints(qh, qh->first_point, qh->num_points, qh->hull_dim, rows);
}  /* rotateinput */

/*---------------------------------

  qh_rotatepoints(qh, points, numpoints, dim, row )
    rotate numpoints points by a d-dim row matrix
    assumes rows[dim] is a scratch buffer

  returns:
    rotated points in place

  design:
    for each point
      for each coordinate
        use row[dim] to compute partial inner product
      for each coordinate
        rotate by partial inner product
*/
void qh_rotatepoints(qhT *qh, realT *points, int numpoints, int dim, realT **row) {
  realT *point, *rowi, *coord= NULL, sum, *newval;
  int i,j,k;

  if (qh->IStracing >= 1)
    qh_printmatrix(qh, qh->ferr, "qh_rotatepoints: rotate points by", row, dim, dim);
  for (point= points, j= numpoints; j--; point += dim) {
    newval= row[dim];
    for (i=0; i < dim; i++) {
      rowi= row[i];
      coord= point;
      for (sum= 0.0, k= dim; k--; )
        sum += *rowi++ * *coord++;
      *(newval++)= sum;
    }
    for (k=dim; k--; )
      *(--coord)= *(--newval);
  }
} /* rotatepoints */


/*---------------------------------

  qh_scaleinput(qh)
    scale input points using qh->low_bound/high_bound
    input points given by qh->first_point, num_points, hull_dim
    if qh->POINTSmalloc, overwrites input points, else mallocs a new array

  returns:
    scales coordinates of points to low_bound[k], high_bound[k]
    sets qh->POINTSmalloc

  design:
    see qh_scalepoints
*/
void qh_scaleinput(qhT *qh) {

  if (!qh->POINTSmalloc) {
    qh->first_point= qh_copypoints(qh, qh->first_point, qh->num_points, qh->hull_dim);
    qh->POINTSmalloc= True;
  }
  qh_scalepoints(qh, qh->first_point, qh->num_points, qh->hull_dim,
       qh->lower_bound, qh->upper_bound);
}  /* scaleinput */

/*---------------------------------

  qh_scalelast(qh, 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(qhT *qh, coordT *points, int numpoints, int dim, coordT low,
                   coordT high, coordT newhigh) {
  realT scale, shift;
  coordT *coord;
  int i;
  boolT nearzero= False;

  trace4((qh, 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, 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, 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, qh_ERRinput, NULL, NULL);
  }
  shift= - low * newhigh / (high-low);
  coord= points + dim - 1;
  for (i=numpoints; i--; coord += dim)
    *coord= *coord * scale + shift;
} /* scalelast */

/*---------------------------------

  qh_scalepoints(qh, points, numpoints, dim, newlows, newhighs )
    scale points to new lowbound and highbound
    retains old bound when newlow= -REALmax or newhigh= +REALmax

  returns:
    scaled points
    overwrites old points

  design:
    for each coordinate
      compute current low and high bound
      compute scale and shift factors
      scale all points
      enforce new low and high bound for all points
*/
void qh_scalepoints(qhT *qh, pointT *points, int numpoints, int dim,
        realT *newlows, realT *newhighs) {
  int i,k;
  realT shift, scale, *coord, low, high, newlow, newhigh, mincoord, maxcoord;
  boolT nearzero= False;

  for (k=0; k < dim; k++) {
    newhigh= newhighs[k];
    newlow= newlows[k];
    if (newhigh > REALmax/2 && newlow < -REALmax/2)
      continue;
    low= REALmax;
    high= -REALmax;
    for (i=numpoints, coord=points+k; i--; coord += dim) {
      minimize_(low, *coord);
      maximize_(high, *coord);
    }
    if (newhigh > REALmax/2)
      newhigh= high;
    if (newlow < -REALmax/2)
      newlow= low;
    if (qh->DELAUNAY && k == dim-1 && newhigh < newlow) {
      qh_fprintf(qh, qh->ferr, 6021, "qhull input error: 'Qb%d' or 'QB%d' inverts paraboloid since high bound %.2g < low bound %.2g\n",
               k, k, newhigh, newlow);
      qh_errexit(qh, qh_ERRinput, NULL, NULL);
    }
    scale= qh_divzero(newhigh - newlow, high - low,
                  qh->MINdenom_1, &nearzero);
    if (nearzero) {
      qh_fprintf(qh, qh->ferr, 6022, "qhull input error: %d'th dimension's new bounds [%2.2g, %2.2g] too wide for\nexisting bounds [%2.2g, %2.2g]\n",
              k, newlow, newhigh, low, high);
      qh_errexit(qh, qh_ERRinput, NULL, NULL);
    }
    shift= (newlow * high - low * newhigh)/(high-low);
    coord= points+k;
    for (i=numpoints; i--; coord += dim)
      *coord= *coord * scale + shift;
    coord= points+k;
    if (newlow < newhigh) {
      mincoord= newlow;
      maxcoord= newhigh;
    }else {
      mincoord= newhigh;
      maxcoord= newlow;
    }
    for (i=numpoints; i--; coord += dim) {
      minimize_(*coord, maxcoord);  /* because of roundoff error */
      maximize_(*coord, mincoord);
    }
    trace0((qh, qh->ferr, 10, "qh_scalepoints: scaled %d'th coordinate [%2.2g, %2.2g] to [%.2g, %.2g] for %d points by %2.2g and shifted %2.2g\n",
      k, low, high, newlow, newhigh, numpoints, scale, shift));
  }
} /* scalepoints */


/*---------------------------------

  qh_setdelaunay(qh, dim, count, points )
    project count points to dim-d paraboloid for Delaunay triangulation

    dim is one more than the dimension of the input set
    assumes dim is at least 3 (i.e., at least a 2-d Delaunay triangulation)

    points is a dim*count realT array.  The first dim-1 coordinates
    are the coordinates of the first input point.  array[dim] is
    the first coordinate of the second input point.  array[2*dim] is
    the first coordinate of the third input point.

    if qh.last_low defined (i.e., 'Qbb' called qh_scalelast)
      calls qh_scalelast to scale the last coordinate the same as the other points

  returns:
    for each point
      sets point[dim-1] to sum of squares of coordinates
    scale points to 'Qbb' if needed

  notes:
    to project one point, use
      qh_setdelaunay(qh, qh->hull_dim, 1, point)

    Do not use options 'Qbk', 'QBk', or 'QbB' since they scale
    the coordinates after the original projection.

*/
void qh_setdelaunay(qhT *qh, int dim, int count, pointT *points) {
  int i, k;
  coordT *coordp, coord;
  realT paraboloid;

  trace0((qh, qh->ferr, 11, "qh_setdelaunay: project %d points to paraboloid for Delaunay triangulation\n", count));
  coordp= points;
  for (i=0; i < count; i++) {
    coord= *coordp++;
    paraboloid= coord*coord;
    for (k=dim-2; k--; ) {
      coord= *coordp++;
      paraboloid += coord*coord;
    }
    *coordp++ = paraboloid;
  }
  if (qh->last_low < REALmax/2)
    qh_scalelast(qh, points, count, dim, qh->last_low, qh->last_high, qh->last_newhigh);
} /* setdelaunay */


/*---------------------------------

  qh_sethalfspace(qh, dim, coords, nextp, normal, offset, feasible )
    set point to dual of halfspace relative to feasible point
    halfspace is normal coefficients and offset.

  returns:
    false and prints error if feasible point is outside of hull
    overwrites coordinates for point at dim coords
    nextp= next point (coords)
    does not call qh_errexit

  design:
    compute distance from feasible point to halfspace
    divide each normal coefficient by -dist
*/
boolT qh_sethalfspace(qhT *qh, int dim, coordT *coords, coordT **nextp,
         coordT *normal, coordT *offset, coordT *feasible) {
  coordT *normp= normal, *feasiblep= feasible, *coordp= coords;
  realT dist;
  realT r; /*bug fix*/
  int k;
  boolT zerodiv;

  dist= *offset;
  for (k=dim; k--; )
    dist += *(normp++) * *(feasiblep++);
  if (dist > 0)
    goto LABELerroroutside;
  normp= normal;
  if (dist < -qh->MINdenom) {
    for (k=dim; k--; )
      *(coordp++)= *(normp++) / -dist;
  }else {
    for (k=dim; k--; ) {
      *(coordp++)= qh_divzero(*(normp++), -dist, qh->MINdenom_1, &zerodiv);
      if (zerodiv)
        goto LABELerroroutside;
    }
  }
  *nextp= coordp;
  if (qh->IStracing >= 4) {
    qh_fprintf(qh, qh->ferr, 8021, "qh_sethalfspace: halfspace at offset %6.2g to point: ", *offset);
    for (k=dim, coordp=coords; k--; ) {
      r= *coordp++;
      qh_fprintf(qh, qh->ferr, 8022, " %6.2g", r);
    }
    qh_fprintf(qh, qh->ferr, 8023, "\n");
  }
  return True;
LABELerroroutside:
  feasiblep= feasible;
  normp= normal;
  qh_fprintf(qh, qh->ferr, 6023, "qhull input error: feasible point is not clearly inside halfspace\nfeasible point: ");
  for (k=dim; k--; )
    qh_fprintf(qh, qh->ferr, 8024, qh_REAL_1, r=*(feasiblep++));
  qh_fprintf(qh, qh->ferr, 8025, "\n     halfspace: ");
  for (k=dim; k--; )
    qh_fprintf(qh, qh->ferr, 8026, qh_REAL_1, r=*(normp++));
  qh_fprintf(qh, qh->ferr, 8027, "\n     at offset: ");
  qh_fprintf(qh, qh->ferr, 8028, qh_REAL_1, *offset);
  qh_fprintf(qh, qh->ferr, 8029, " and distance: ");
  qh_fprintf(qh, qh->ferr, 8030, qh_REAL_1, dist);
  qh_fprintf(qh, qh->ferr, 8031, "\n");
  return False;
} /* sethalfspace */

/*---------------------------------

  qh_sethalfspace_all(qh, dim, count, halfspaces, feasible )
    generate dual for halfspace intersection with feasible point
    array of count halfspaces
      each halfspace is normal coefficients followed by offset
      the origin is inside the halfspace if the offset is negative
    feasible is a point inside all halfspaces (http://www.qhull.org/html/qhalf.htm#notes)

  returns:
    malloc'd array of count X dim-1 points

  notes:
    call before qh_init_B or qh_initqhull_globals
    free memory when done
    unused/untested code: please email bradb@shore.net if this works ok for you
    if using option 'Fp', qh->feasible_point must be set (e.g., to 'feasible')
    qh->feasible_point is a malloc'd array that is freed by qh_freebuffers.

  design:
    see qh_sethalfspace
*/
coordT *qh_sethalfspace_all(qhT *qh, int dim, int count, coordT *halfspaces, pointT *feasible) {
  int i, newdim;
  pointT *newpoints;
  coordT *coordp, *normalp, *offsetp;

  trace0((qh, qh->ferr, 12, "qh_sethalfspace_all: compute dual for halfspace intersection\n"));
  newdim= dim - 1;
  if (!(newpoints=(coordT*)qh_malloc(count*newdim*sizeof(coordT)))){
    qh_fprintf(qh, qh->ferr, 6024, "qhull error: insufficient memory to compute dual of %d halfspaces\n",
          count);
    qh_errexit(qh, qh_ERRmem, NULL, NULL);
  }
  coordp= newpoints;
  normalp= halfspaces;
  for (i=0; i < count; i++) {
    offsetp= normalp + newdim;
    if (!qh_sethalfspace(qh, newdim, coordp, &coordp, normalp, offsetp, feasible)) {
      qh_free(newpoints);  /* feasible is not inside halfspace as reported by qh_sethalfspace */
      qh_fprintf(qh, qh->ferr, 8032, "The halfspace was at index %d\n", i);
      qh_errexit(qh, qh_ERRinput, NULL, NULL);
    }
    normalp= offsetp + 1;
  }
  return newpoints;
} /* sethalfspace_all */


/*---------------------------------

  qh_sharpnewfacets(qh)

  returns:
    true if could be an acute angle (facets in different quadrants)

  notes:
    for qh_findbest

  design:
    for all facets on qh.newfacet_list
      if two facets are in different quadrants
        set issharp
*/
boolT qh_sharpnewfacets(qhT *qh) {
  facetT *facet;
  boolT issharp = False;
  int *quadrant, k;

  quadrant= (int*)qh_memalloc(qh, qh->hull_dim * sizeof(int));
  FORALLfacet_(qh->newfacet_list) {
    if (facet == qh->newfacet_list) {
      for (k=qh->hull_dim; k--; )
        quadrant[ k]= (facet->normal[ k] > 0);
    }else {
      for (k=qh->hull_dim; k--; ) {
        if (quadrant[ k] != (facet->normal[ k] > 0)) {
          issharp= True;
          break;
        }
      }
    }
    if (issharp)
      break;
  }
  qh_memfree(qh, quadrant, qh->hull_dim * sizeof(int));
  trace3((qh, qh->ferr, 3001, "qh_sharpnewfacets: %d\n", issharp));
  return issharp;
} /* sharpnewfacets */

/*---------------------------------

  qh_voronoi_center(qh, dim, points )
    return Voronoi center for a set of points
    dim is the orginal dimension of the points
    gh.gm_matrix/qh.gm_row are scratch buffers

  returns:
    center as a temporary point (qh_memalloc)
    if non-simplicial,
      returns center for max simplex of points

  notes:
    only called by qh_facetcenter
    from Bowyer & Woodwark, A Programmer's Geometry, 1983, p. 65

  design:
    if non-simplicial
      determine max simplex for points
    translate point0 of simplex to origin
    compute sum of squares of diagonal
    compute determinate
    compute Voronoi center (see Bowyer & Woodwark)
*/
pointT *qh_voronoi_center(qhT *qh, int dim, setT *points) {
  pointT *point, **pointp, *point0;
  pointT *center= (pointT*)qh_memalloc(qh, qh->center_size);
  setT *simplex;
  int i, j, k, size= qh_setsize(qh, points);
  coordT *gmcoord;
  realT *diffp, sum2, *sum2row, *sum2p, det, factor;
  boolT nearzero, infinite;

  if (size == dim+1)
    simplex= points;
  else if (size < dim+1) {
    qh_memfree(qh, center, qh->center_size);
    qh_fprintf(qh, qh->ferr, 6025, "qhull internal error (qh_voronoi_center):\n  need at least %d points to construct a Voronoi center\n",
             dim+1);
    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
    simplex= points;  /* never executed -- avoids warning */
  }else {
    simplex= qh_settemp(qh, dim+1);
    qh_maxsimplex(qh, dim, points, NULL, 0, &simplex);
  }
  point0= SETfirstt_(simplex, pointT);
  gmcoord= qh->gm_matrix;
  for (k=0; k < dim; k++) {
    qh->gm_row[k]= gmcoord;
    FOREACHpoint_(simplex) {
      if (point != point0)
        *(gmcoord++)= point[k] - point0[k];
    }
  }
  sum2row= gmcoord;
  for (i=0; i < dim; i++) {
    sum2= 0.0;
    for (k=0; k < dim; k++) {
      diffp= qh->gm_row[k] + i;
      sum2 += *diffp * *diffp;
    }
    *(gmcoord++)= sum2;
  }
  det= qh_determinant(qh, qh->gm_row, dim, &nearzero);
  factor= qh_divzero(0.5, det, qh->MINdenom, &infinite);
  if (infinite) {
    for (k=dim; k--; )
      center[k]= qh_INFINITE;
    if (qh->IStracing)
      qh_printpoints(qh, qh->ferr, "qh_voronoi_center: at infinity for ", simplex);
  }else {
    for (i=0; i < dim; i++) {
      gmcoord= qh->gm_matrix;
      sum2p= sum2row;
      for (k=0; k < dim; k++) {
        qh->gm_row[k]= gmcoord;
        if (k == i) {
          for (j=dim; j--; )
            *(gmcoord++)= *sum2p++;
        }else {
          FOREACHpoint_(simplex) {
            if (point != point0)
              *(gmcoord++)= point[k] - point0[k];
          }
        }
      }
      center[i]= qh_determinant(qh, qh->gm_row, dim, &nearzero)*factor + point0[i];
    }
#ifndef qh_NOtrace
    if (qh->IStracing >= 3) {
      qh_fprintf(qh, qh->ferr, 8033, "qh_voronoi_center: det %2.2g factor %2.2g ", det, factor);
      qh_printmatrix(qh, qh->ferr, "center:", ¢er, 1, dim);
      if (qh->IStracing >= 5) {
        qh_printpoints(qh, qh->ferr, "points", simplex);
        FOREACHpoint_(simplex)
          qh_fprintf(qh, qh->ferr, 8034, "p%d dist %.2g, ", qh_pointid(qh, point),
                   qh_pointdist(point, center, dim));
        qh_fprintf(qh, qh->ferr, 8035, "\n");
      }
    }
#endif
  }
  if (simplex != points)
    qh_settempfree(qh, &simplex);
  return center;
} /* voronoi_center */

geometry/src/userprintf_rbox_r.c0000644000176200001440000000320113432323610016605 0ustar  liggesusers/*
  ---------------------------------

   userprintf_rbox_r.c
   qh_fprintf_rbox()

   see README.txt  see COPYING.txt for copyright information.

   If you recompile and load this file, then userprintf_rbox_r.o will not be loaded
   from qhull.a or qhull.lib

   See libqhull_r.h for data structures, macros, and user-callable functions.
   See user_r.c for qhull-related, redefinable functions
   see user_r.h for user-definable constants
   See usermem_r.c for qh_exit(), qh_free(), and qh_malloc()
   see Qhull.cpp and RboxPoints.cpp for examples.

   Please report any errors that you fix to qhull@qhull.org
*/

#include "libqhull_r.h"

#include 
#include 
#include 

/*---------------------------------

   qh_fprintf_rbox(qh, fp, msgcode, format, list of args )
     print arguments to *fp according to format
     Use qh_fprintf_rbox() for rboxlib_r.c

   notes:
     same as fprintf()
     fgets() is not trapped like fprintf()
     exit qh_fprintf_rbox via qh_errexit_rbox()
*/

void qh_fprintf_rbox(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... ) {
    va_list args;

    if (!fp) {
        qh_fprintf_stderr(6231, "Qhull internal error (userprintf_rbox_r.c): fp is 0.  Wrong qh_fprintf_rbox called.\n");
        qh_errexit_rbox(qh, 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 */

geometry/src/Makevars0000644000176200001440000000007413571234476014405 0ustar  liggesusersPKG_CFLAGS = -include Rgeometry.h
PKG_LDFLAGS = -fno-common
geometry/src/Rgeometry.h0000644000176200001440000000167014227215261015030 0ustar  liggesusers/* This file is included via Makevars in all C files */
#include 
#include 
#include 

/* The following fixes a problem R check has with stderr. I tried
   redefining as NULL to prevent output, but a FILE handle is needed
   by qh_new_qhull() due to a call to freopen() somewhere in the
   library. Qhull already defines a dummy stderr qh_FILEstderr in
   libqhull_r.h */
#undef stderr
#define stderr qh_FILEstderr

/* PI has been defined by the R header files, but the Qhull package
   defines it again, so undefine it here. */
#undef PI

/* Size of error string to pass back to R from QH */
#define ERRSTRSIZE 1000

#include "qhull_ra.h"

void freeQhull(qhT *qh);
void qhullFinalizer(SEXP ptr);
boolT hasPrintOption(qhT *qh, qh_PRINT format);
int qhullNewQhull(qhT *qh, const SEXP p, char* cmd, const SEXP options, const SEXP tmp_stdout, const SEXP tmp_stderr, unsigned int* pdim, unsigned int* pn, char errstr[1000]);
geometry/src/Rhalfspacen.c0000644000176200001440000000632413532164477015307 0ustar  liggesusers/* Copyright (C) 2018, 2019 David Sterratt
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
*/

#include "Rgeometry.h"
#include "qhull_ra.h"
#include               /* For unlink() */

SEXP C_halfspacen(const SEXP p, const SEXP options, const SEXP tmp_stdout, const SEXP tmp_stderr)
{
  /* Return value*/
  SEXP retval;

  /* Run Qhull */
  qhT *qh= (qhT*)malloc(sizeof(qhT));
  char errstr[ERRSTRSIZE];
  unsigned int dim, n;
  char cmd[50] = "qhull H";
  int exitcode = qhullNewQhull(qh, p, cmd,  options, tmp_stdout, tmp_stdout, &dim, &n, errstr);

  /* If error */
  if (exitcode) {
    freeQhull(qh);
    error("Received error code %d from qhull. Qhull error:\n%s", exitcode, errstr);
  }

  if (!qh->feasible_point) {
    freeQhull(qh);
    error("qhull input error (qh_printafacet): option 'Fp' needs qh->feasible_point");
  }
  
  /* Extract information from output */
  int i;
  facetT *facet;
  boolT zerodiv;
  coordT *point, *normp, *coordp, *feasiblep;
    
  /* Count facets. Perhaps a better way of doing this is: 
     int numfacets, numsimplicial, numridges, totneighbors, numcoplanars, numtricoplanars;
     int num;
     qh_countfacets(qh, NULL, facets, printall, &numfacets, &numsimplicial,
     &totneighbors, &numridges, &numcoplanars, &numtricoplanars); */
  int nf = 0;
  FORALLfacets {
    nf++;
  }

  /* Output of intersections based on case qh_PRINTpointintersect:
     qh_printafacet() in io_r.c . This corresponds to the "Fp"
     option to the qhull program */
  retval = PROTECT(allocMatrix(REALSXP, nf, dim-1));
  int k;
  i=0; /* Facet counter */
  FORALLfacets {
    point = coordp = (coordT*)qh_memalloc(qh, qh->normal_size);
    if (facet->offset > 0) {
      for (k=qh->hull_dim; k--; ) {
        point[k] = R_PosInf;
      }
    } else {
      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) {
            for (k=qh->hull_dim; k--; ) {
              point[k] = R_PosInf;
            }
          }
        }
      }
    }
    /* qh_printpoint(qh, fp, NULL, point); */
    for (k=0; khull_dim; k++) {
      REAL(retval)[i + k*nf] = point[k];
    }
    qh_memfree(qh, point, qh->normal_size);
    i++; /* Increment facet counter */
  }

  freeQhull(qh);
  UNPROTECT(1);

  return retval;
}
geometry/src/Rgeometry.c0000644000176200001440000000656214366300532015030 0ustar  liggesusers#include 
#include 
#include 
#include "qhull_ra.h"
#include               /* For unlink() */

void freeQhull(qhT *qh) {
  int curlong, totlong;
  qh_freeqhull(qh, !qh_ALL);                /* free long memory */
  qh_memfreeshort (qh, &curlong, &totlong);	/* free short memory and memory allocator */
  if (curlong || totlong) {
    warning("convhulln: did not free %d bytes of long memory (%d pieces)",
	    totlong, curlong);
  }
  qh_free(qh);
}

/* Finalizer which R will call when garbage collecting. This is
   registered at the end of convhulln() */
void qhullFinalizer(SEXP ptr)
{
  if(!R_ExternalPtrAddr(ptr)) return;
  qhT *qh;
  qh = R_ExternalPtrAddr(ptr);
  freeQhull(qh);
  R_ClearExternalPtr(ptr); /* not really needed */
}

boolT hasPrintOption(qhT *qh, qh_PRINT format) {
  for (int i=0; i < qh_PRINTEND; i++) {
    if (qh->PRINTout[i] == format) {
      return(True);
    }
  }
  return(False);
}

int qhullNewQhull(qhT *qh, const SEXP p, char* cmd, const SEXP options, const SEXP tmp_stdout, const SEXP tmp_stderr, unsigned int* pdim, unsigned int* pn, char errstr[ERRSTRSIZE]) {
  unsigned int dim, n;
  int exitcode = 1; 
  boolT ismalloc;
  char flags[250];             /* option flags for qhull, see qh_opt.htm */
  double *pt_array;
  int i, j;
  
  /* We cannot print directly to stdout in R, and the alternative of
     using R_Outputfile does not seem to work for all architectures.
     Setting outfile to NULL is not an option, as an open file handle
     is required for a call to freopen in the Qhull code when
     qh_new_qhull() is called. Therefore use the ersatz stdout,
     tmpstdout (see below). */
  /* qh_fprintf() in userprint.c has been redefined so that a NULL
     errfile results in printing via REprintf(). */
  FILE *tmpstdout = NULL;
  FILE *errfile = NULL;       

  if(!isString(options) || length(options) != 1){
    error("Second argument must be a single string.");
  }
  if(!isMatrix(p) || !isReal(p)){
    error("First argument should be a real matrix.");
  }

  /* Read options into command */
	i = LENGTH(STRING_ELT(options,0)); 
  if (i > 200) 
    error("Option string too long");
  snprintf(flags, 249, "%s %s", cmd, CHAR(STRING_ELT(options,0)));

  /* Check input matrix */
  dim = ncols(p);
  n   = nrows(p);
  if(dim <= 0 || n <= 0){
    error("Invalid input matrix.");
  }

  pt_array = (double *) R_alloc(n*dim, sizeof(double)); 
  for(i=0; i < n; i++)
    for(j=0; j < dim; j++)
      pt_array[dim*i+j] = REAL(p)[i+n*j]; /* could have been pt_array = REAL(p) if p had been transposed */

  ismalloc = False; /* True if qhull should free points in qh_freeqhull() or reallocation */

  /* Jiggery-pokery to create and destroy the ersatz stdout, and the
     call to qhull itself. */    
  const char *name, *errname;
  name = CHAR(STRING_ELT(tmp_stdout, 0));
  tmpstdout = fopen(name, "w");
  errname = CHAR(STRING_ELT(tmp_stderr, 0));
  errfile = fopen(errname, "w+");
  qh_zero(qh, errfile);
  exitcode = qh_new_qhull (qh, dim, n, pt_array, ismalloc, flags, tmpstdout, errfile);
  fclose(tmpstdout);
  unlink(name);
  rewind(errfile);
  char buf[200];
  errstr[0] = '\0';
  while(fgets(buf, sizeof(buf), errfile) != NULL &&
        (ERRSTRSIZE - strlen(errstr) - 1) > 0) {
    errstr = strncat(errstr, buf, ERRSTRSIZE - strlen(errstr) - 1);
  }
  
  fclose(errfile);
  unlink(errname);

  *pdim = dim;
  *pn = n;
  return(exitcode);
}
geometry/src/Rdelaunayn.c0000644000176200001440000001612313532164477015161 0ustar  liggesusers/* Copyright (C) 2000 Kai Habel
** Copyright R-version (C) 2005 Raoul Grasman
** Copyright           (C) 2013-2019 David Sterratt
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
*/

/*
16. July 2000 - Kai Habel: first release

25. September 2002 - Changes by Rafael Laboissiere 

 * Added Qbb option to normalize the input and avoid crashes in Octave.
 * delaunayn accepts now a second (optional) argument that must be a string
   containing extra options to the qhull command.
 * Fixed doc string.  The dimension of the result matrix is [m, dim+1], and
   not [n, dim-1].

20. May 2005 - Raoul Grasman: ported to R
 * Changed the interface for R
*/

#include "Rgeometry.h"
#include               /* For unlink() */

SEXP C_delaunayn(const SEXP p, const SEXP options, SEXP tmp_stdout, SEXP tmp_stderr)
{
  /* Initialise return values */ 

  SEXP retlist, retnames;       /* Return list and names */
	SEXP tri;                     /* The triangulation */
  SEXP neighbour, neighbours;   /* List of neighbours */
  SEXP areas;                   /* Facet areas */
	tri = neighbours = retlist = areas = R_NilValue;

  /* Run Qhull */
  
  qhT *qh= (qhT*)malloc(sizeof(qhT));
  char errstr[ERRSTRSIZE];
  unsigned int dim, n;
  char cmd[50] = "qhull d Qbb T0";
  /* Qz forces triangulation when the number of points is equal to the
     number of dimensions + 1 ; This mirrors the behaviour of octave
     and matlab */
  if (nrows(p) == ncols(p) + 1) {
    strncat(cmd, " Qz", 4);
  }
  int exitcode = qhullNewQhull(qh, p, cmd,  options, tmp_stdout, tmp_stderr, &dim, &n, errstr);

  /* Extract information from output */
  
  if (!exitcode) {                    /* 0 if no error from qhull */
    /* Triangulate non-simplicial facets - this commented out code
       does not appear to be needed, but retaining in case useful --
       David Sterratt, 2013-04-17 */
    /* qh_triangulate (); */

    facetT *facet;                  /* set by FORALLfacets */
    vertexT *vertex, **vertexp;
    facetT *neighbor, **neighborp;

    /* Count the number of facets so we know how much space to
       allocate in R */
    int nf=0;                 /* Number of facets */
    FORALLfacets {
      if (!facet->upperdelaunay) {
        /* Remove degenerate simplicies */
        if (!facet->isarea) {
          facet->f.area= qh_facetarea(qh, facet);
          facet->isarea= True;
        }
        if (facet->f.area)
          nf++;
      }
      /* Double check. Non-simplicial facets will cause segfault
         below */
      if (! facet->simplicial) {
        Rprintf("Qhull returned non-simplicial facets -- try delaunayn with different options");
        exitcode = 1;
        break;
      }
    }
      
    /* Alocate the space in R */
    PROTECT(tri = allocMatrix(INTSXP, nf, dim+1));
    if (hasPrintOption(qh, qh_PRINTneighbors)) {
      PROTECT(neighbours = allocVector(VECSXP, nf));
    } else {
      PROTECT(neighbours = R_NilValue);
    }
    if (hasPrintOption(qh, qh_PRINTarea)) {
      PROTECT(areas = allocVector(REALSXP, nf));      
    } else {
      PROTECT(areas = R_NilValue);
    }
    
    /* Iterate through facets to extract information */
    int i=0;
    FORALLfacets {
      if (!facet->upperdelaunay && facet->f.area) {
        if (i >= nf) {
          error("Trying to access non-existent facet %i", i);
        }

        /* Triangulation */
        int j=0;
        FOREACHvertex_ (facet->vertices) {
          if ((i + nf*j) >= nf*(dim+1))
            error("Trying to write to non-existent area of memory i=%i, j=%i, nf=%i, dim=%i", i, j, nf, dim);
          INTEGER(tri)[i + nf*j] = 1 + qh_pointid(qh, vertex->point);
          j++;
        }

        /* Neighbours - option Fn */
        if (hasPrintOption(qh, qh_PRINTneighbors)) {
          PROTECT(neighbour = allocVector(INTSXP, qh_setsize(qh, facet->neighbors)));
          j=0;
          FOREACHneighbor_(facet) {
            INTEGER(neighbour)[j] = neighbor->visitid ? neighbor->visitid: 0 - neighbor->id;
            j++;
          }
          SET_VECTOR_ELT(neighbours, i, neighbour);
          UNPROTECT(1);
        }

        /* Areas - option Fa */
        if (hasPrintOption(qh, qh_PRINTarea)) {
          /* Area. Code modified from qh_getarea() in libquhull/geom2.c */
          if ((facet->normal) && !(facet->upperdelaunay && qh->ATinfinity)) {
            if (!facet->isarea) {
              facet->f.area= qh_facetarea(qh, facet);
              facet->isarea= True;
            }
            REAL(areas)[i] = facet->f.area;
          }
        }

        i++;
      }
    }
  } else { /* exitcode != 1 */
    /* There has been an error; Qhull will print the error
       message */
    PROTECT(tri = allocMatrix(INTSXP, 0, dim+1));
    if (hasPrintOption(qh, qh_PRINTneighbors)) {
      PROTECT(neighbours = allocVector(VECSXP, 0));
    } else {
      PROTECT(neighbours = R_NilValue);
    }
    if (hasPrintOption(qh, qh_PRINTarea)) {
      PROTECT(areas = allocVector(REALSXP, 0));
    } else {
      PROTECT(areas = R_NilValue);
    }

    /* If the error been because the points are colinear, coplanar
       &c., then avoid mentioning an error by setting exitcode=2 .

       This is the same behaviour as octave:
    >> delaunayn([0 0; 1 1; 2 2], "")
       ans = [](0x3)

       But Matlab has different behaviour:
       delaunayn([0 0; 1 1; 2 2])
       ans =     1     2     3
    */

    if ((dim + 1) == n) {
      exitcode = 2;
    }
  }

  /* Set up output structure */
  retlist =  PROTECT(allocVector(VECSXP, 3));
  retnames = PROTECT(allocVector(VECSXP, 3));
  SET_VECTOR_ELT(retlist,  0, tri);
  SET_VECTOR_ELT(retnames, 0, mkChar("tri"));
  SET_VECTOR_ELT(retlist,  1, neighbours);
  SET_VECTOR_ELT(retnames, 1, mkChar("neighbours"));
  SET_VECTOR_ELT(retlist,  2, areas);
  SET_VECTOR_ELT(retnames, 2, mkChar("areas"));
  setAttrib(retlist, R_NamesSymbol, retnames);
  
  /* Register qhullFinalizer() for garbage collection and attach a
     pointer to the hull as an attribute for future use. */
  SEXP ptr, tag;
  PROTECT(tag = allocVector(STRSXP, 1));
  SET_STRING_ELT(tag, 0, mkChar("delaunayn"));
  PROTECT(ptr = R_MakeExternalPtr(qh, tag, R_NilValue));
  if (exitcode) {
    qhullFinalizer(ptr);
  } else {
    R_RegisterCFinalizerEx(ptr, qhullFinalizer, TRUE);
    setAttrib(retlist, tag, ptr);
  }

  UNPROTECT(7); /* ptr, tag, retnames, retlist, areas, neigbours, tri */
  
  if (exitcode & (exitcode != 2)) {
    error("Received error code %d from qhull. Qhull error:\n%s", exitcode, errstr);
  } 
  
	return retlist;
}


geometry/src/rboxlib_r.c0000644000176200001440000006203414366313424015035 0ustar  liggesusers/*
  ---------------------------------

   rboxlib_r.c
     Generate input points

   notes:
     For documentation, see prompt[] of rbox_r.c
     50 points generated for 'rbox D4'

   WARNING:
     incorrect range if qh_RANDOMmax is defined wrong (user_r.h)
*/

#include "libqhull_r.h"  /* First for user_r.h */
#include "random_r.h"

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#ifdef _MSC_VER  /* Microsoft Visual C++ */
#pragma warning( disable : 4706)  /* assignment within conditional expression. */
#pragma warning( disable : 4996)  /* this function (strncat,sprintf,strcpy) or variable may be unsafe. */
#endif

#define MAXdim 200
#define PI 3.1415926535897932384

/* ------------------------------ prototypes ----------------*/
int qh_roundi(qhT *qh, double a);
void qh_out1(qhT *qh, double a);
void qh_out2n(qhT *qh, double a, double b);
void qh_out3n(qhT *qh, double a, double b, double c);
void qh_outcoord(qhT *qh, int iscdd, double *coord, int dim);
void qh_outcoincident(qhT *qh, int coincidentpoints, double radius, int iscdd, double *coord, int dim);

void    qh_fprintf_rbox(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... );
void    qh_free(void *mem);
void   *qh_malloc(size_t size);
int     qh_rand(qhT *qh);
void    qh_srand(qhT *qh, int seed);

/*---------------------------------

  qh_rboxpoints(qh, rbox_command )
    Generate points to qh->fout according to rbox options
    Report errors on qh->ferr

  returns:
    0 (qh_ERRnone) on success
    1 (qh_ERRinput) on input error
    4 (qh_ERRmem) on memory error
    5 (qh_ERRqhull) on internal error

  notes:
    To avoid using stdio, redefine qh_malloc, qh_free, and qh_fprintf_rbox (user_r.c)

  design:
    Straight line code (consider defining a struct and functions):

    Parse arguments into variables
    Determine the number of points
    Generate the points
*/
int qh_rboxpoints(qhT *qh, char* rbox_command) {
  int 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[400], seedbuf[200];
  char *s= command, *t, *first_point= NULL;
  time_t timedata;
  int exitcode;

  exitcode= setjmp(qh->rbox_errexit);
  if (exitcode) {
    /* same code for error exit and normal return, qh->NOerrexit is set */
    if (simplex)
        qh_free(simplex);
    return exitcode;
  }

  *command= '\0';
  strncat(command, rbox_command, sizeof(command)-sizeof(seedbuf)-strlen(command)-1);

  while (*s && !isspace(*s))  /* skip program name */
    s++;
  while (*s) {
    while (*s && isspace(*s))
      s++;
    if (*s == '-')
      s++;
    if (!*s)
      break;
    if (isdigit(*s)) {
      numpoints= qh_strtol(s, &s);
      continue;
    }
    /* ============= read flags =============== */
    switch (*s++) {
    case 'c':
      addcube= 1;
      t= s;
      while (isspace(*t))
        t++;
      if (*t == 'G')
        cube= qh_strtod(++t, &s);
      break;
    case 'd':
      adddiamond= 1;
      t= s;
      while (isspace(*t))
        t++;
      if (*t == 'G')
        diamond= qh_strtod(++t, &s);
      break;
    case 'h':
      iscdd= 1;
      break;
    case 'l':
      isspiral= 1;
      break;
    case 'n':
      NOcommand= 1;
      break;
    case 'r':
      isregular= 1;
      break;
    case 's':
      issphere= 1;
      break;
    case 't':
      istime= 1;
      if (isdigit(*s)) {
        seed= qh_strtol(s, &s);
        israndom= 0;
      }else
        israndom= 1;
      break;
    case 'x':
      issimplex= 1;
      break;
    case 'y':
      issimplex2= 1;
      break;
    case 'z':
      qh->rbox_isinteger= 1;
      break;
    case 'B':
      box= qh_strtod(s, &s);
      isbox= 1;
      break;
    case 'C':
      if (*s)
        coincidentpoints=  qh_strtol(s, &s);
      if (*s == ',') {
        ++s;
        coincidentradius=  qh_strtod(s, &s);
      }
      if (*s == ',') {
        ++s;
        coincidenttotal=  qh_strtol(s, &s);
      }
      if (*s && !isspace(*s)) {
        qh_fprintf_rbox(qh, qh->ferr, 7080, "rbox error: arguments for 'Cn,r,m' are not 'int', 'float', and 'int'.  Remaining string is '%s'\n", s);
        qh_errexit_rbox(qh, qh_ERRinput);
      }
      if (coincidentpoints==0){
        qh_fprintf_rbox(qh, qh->ferr, 6268, "rbox error: missing arguments for 'Cn,r,m' where n is the number of coincident points, r is the radius (default 0.0), and m is the number of points\n");
        qh_errexit_rbox(qh, qh_ERRinput);
      }
      if (coincidentpoints<0 || coincidenttotal<0 || coincidentradius<0.0){
        qh_fprintf_rbox(qh, qh->ferr, 6269, "rbox error: negative arguments for 'Cn,m,r' where n (%d) is the number of coincident points, m (%d) is the number of points, and r (%.2g) is the radius (default 0.0)\n", coincidentpoints, coincidenttotal, coincidentradius);
        qh_errexit_rbox(qh, qh_ERRinput);
      }
      break;
    case 'D':
      dim= qh_strtol(s, &s);
      if (dim < 1
      || dim > MAXdim) {
        qh_fprintf_rbox(qh, qh->ferr, 6189, "rbox error: dimension, D%d, out of bounds (>=%d or <=0)", dim, MAXdim);
        qh_errexit_rbox(qh, qh_ERRinput);
      }
      break;
    case 'G':
      if (isdigit(*s))
        gap= qh_strtod(s, &s);
      else
        gap= 0.5;
      isgap= 1;
      break;
    case 'L':
      if (isdigit(*s))
        radius= qh_strtod(s, &s);
      else
        radius= 10;
      islens= 1;
      break;
    case 'M':
      ismesh= 1;
      if (*s)
        meshn= qh_strtod(s, &s);
      if (*s == ',') {
        ++s;
        meshm= qh_strtod(s, &s);
      }else
        meshm= 0.0;
      if (*s == ',') {
        ++s;
        meshr= qh_strtod(s, &s);
      }else
        meshr= sqrt(meshn*meshn + meshm*meshm);
      if (*s && !isspace(*s)) {
        qh_fprintf_rbox(qh, qh->ferr, 7069, "rbox warning: assuming 'M3,4,5' since mesh args are not integers or reals\n");
        meshn= 3.0, meshm=4.0, meshr=5.0;
      }
      break;
    case 'O':
      qh->rbox_out_offset= qh_strtod(s, &s);
      break;
    case 'P':
      if (!first_point)
        first_point= s-1;
      addpoints++;
      while (*s && !isspace(*s))   /* read points later */
        s++;
      break;
    case 'W':
      width= qh_strtod(s, &s);
      iswidth= 1;
      break;
    case 'Z':
      if (isdigit(*s))
        radius= qh_strtod(s, &s);
      else
        radius= 1.0;
      isaxis= 1;
      break;
    default:
      qh_fprintf_rbox(qh, qh->ferr, 7070, "rbox error: unknown flag at %s.\nExecute 'rbox' without arguments for documentation.\n", s);
      qh_errexit_rbox(qh, qh_ERRinput);
    }
    if (*s && !isspace(*s)) {
      qh_fprintf_rbox(qh, qh->ferr, 7071, "rbox error: missing space between flags at %s.\n", s);
      qh_errexit_rbox(qh, qh_ERRinput);
    }
  }

  /* ============= defaults, constants, and sizes =============== */
  if (qh->rbox_isinteger && !isbox)
    box= qh_DEFAULTzbox;
  if (addcube) {
    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(qh, qh->ferr, 6190, "rbox error: can not combine 'Ln' with 'Zn'\n");
        qh_errexit_rbox(qh, qh_ERRinput);
    }
    if (radius <= 1.0) {
        qh_fprintf_rbox(qh, qh->ferr, 6191, "rbox error: lens radius %.2g should be greater than 1.0\n",
               radius);
        qh_errexit_rbox(qh, qh_ERRinput);
    }
    lensangle= asin(1.0/radius);
    lensbase= radius * cos(lensangle);
  }

  if (!numpoints) {
    if (issimplex2)
        ; /* ok */
    else if (isregular + issimplex + islens + issphere + isaxis + isspiral + iswidth + ismesh) {
        qh_fprintf_rbox(qh, qh->ferr, 6192, "rbox error: missing count\n");
        qh_errexit_rbox(qh, qh_ERRinput);
    }else if (adddiamond + addcube + addpoints)
        ; /* ok */
    else {
        numpoints= 50;  /* ./rbox D4 is the test case */
        issphere= 1;
    }
  }
  if ((issimplex + islens + isspiral + ismesh > 1)
  || (issimplex + issphere + isspiral + ismesh > 1)) {
    qh_fprintf_rbox(qh, qh->ferr, 6193, "rbox error: can only specify one of 'l', 's', 'x', 'Ln', or 'Mn,m,r' ('Ln s' is ok).\n");
    qh_errexit_rbox(qh, qh_ERRinput);
  }
  if (coincidentpoints>0 && (numpoints == 0 || coincidenttotal > numpoints)) {
    qh_fprintf_rbox(qh, qh->ferr, 6270, "rbox error: 'Cn,r,m' requested n coincident points for each of m points.  Either there is no points or m (%d) is greater than the number of points (%d).\n", coincidenttotal, numpoints);
    qh_errexit_rbox(qh, qh_ERRinput);
  }
  if (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);
    snprintf(seedbuf, 22, " t%d", seed);  /* appends an extra t, not worth removing */
    strncat(command, seedbuf, sizeof(command)-strlen(command)-1);
    t= strstr(command, " t ");
    if (t)
      strcpy(t+1, t+3); /* remove " t " */
  } /* else, seed explicitly set to n */
  qh_RANDOMseed_(qh, seed);

  /* ============= print header =============== */

  if (iscdd)
      qh_fprintf_rbox(qh, qh->fout, 9391, "%s\nbegin\n        %d %d %s\n",
      NOcommand ? "" : command,
      totpoints, dim+1,
      qh->rbox_isinteger ? "integer" : "real");
  else if (NOcommand)
      qh_fprintf_rbox(qh, qh->fout, 9392, "%d\n%d\n", dim, totpoints);
  else
      /* qh_fprintf_rbox special cases 9393 to append 'command' to the RboxPoints.comment() */
      qh_fprintf_rbox(qh, qh->fout, 9393, "%d %s\n%d\n", dim, command, totpoints);

  /* ============= explicit points =============== */
  if ((s= first_point)) {
    while (s && *s) { /* 'P' */
      count= 0;
      if (iscdd)
        qh_out1(qh, 1.0);
      while (*++s) {
        qh_out1(qh, qh_strtod(s, &s));
        count++;
        if (isspace(*s) || !*s)
          break;
        if (*s != ',') {
          qh_fprintf_rbox(qh, qh->ferr, 6194, "rbox error: missing comma after coordinate in %s\n\n", s);
          qh_errexit_rbox(qh, qh_ERRinput);
        }
      }
      if (count < dim) {
        for (k=dim-count; k--; )
          qh_out1(qh, 0.0);
      }else if (count > dim) {
        qh_fprintf_rbox(qh, qh->ferr, 6195, "rbox error: %d coordinates instead of %d coordinates in %s\n\n",
                  count, dim, s);
        qh_errexit_rbox(qh, qh_ERRinput);
      }
      qh_fprintf_rbox(qh, qh->fout, 9394, "\n");
      while ((s= strchr(s, 'P'))) {
        if (isspace(s[-1]))
          break;
      }
    }
  }

  /* ============= simplex distribution =============== */
  if (issimplex+issimplex2) {
    if (!(simplex= (double*)qh_malloc( dim * (dim+1) * sizeof(double)))) {
      qh_fprintf_rbox(qh, qh->ferr, 6196, "rbox error: insufficient memory for simplex\n");
      qh_errexit_rbox(qh, qh_ERRmem); /* qh_ERRmem */
    }
    simplexp= simplex;
    if (isregular) {
      for (i=0; ifout, 9395, "\n");
      }
    }
    for (j=0; jferr, 6197, "rbox error: regular points can be used only in 2-d and 3-d\n\n");
      qh_errexit_rbox(qh, qh_ERRinput);
    }
    if (!isaxis || radius == 0.0) {
      isaxis= 1;
      radius= 1.0;
    }
    if (dim == 3) {
      if (iscdd)
        qh_out1(qh, 1.0);
      qh_out3n(qh, 0.0, 0.0, -box);
      if (!isgap) {
        if (iscdd)
          qh_out1(qh, 1.0);
        qh_out3n(qh, 0.0, 0.0, box);
      }
    }
    angle= 0.0;
    anglediff= 2.0 * PI/numpoints;
    for (i=0; i < numpoints; i++) {
      angle += anglediff;
      x= radius * cos(angle);
      y= radius * sin(angle);
      if (dim == 2) {
        if (iscdd)
          qh_out1(qh, 1.0);
        qh_out2n(qh, x*box, y*box);
      }else {
        norm= sqrt(1.0 + x*x + y*y);
        if (iscdd)
          qh_out1(qh, 1.0);
        qh_out3n(qh, box*x/norm, box*y/norm, box/norm);
        if (isgap) {
          x *= 1-gap;
          y *= 1-gap;
          norm= sqrt(1.0 + x*x + y*y);
          if (iscdd)
            qh_out1(qh, 1.0);
          qh_out3n(qh, box*x/norm, box*y/norm, box/norm);
        }
      }
    }
  }
  /* ============= regular points for 'r Ln D2' =============== */
  else if (isregular && islens && dim == 2) {
    double cos_0;

    angle= lensangle;
    anglediff= 2 * lensangle/(numpoints - 1);
    cos_0= cos(lensangle);
    for (i=0; i < numpoints; i++, angle -= anglediff) {
      x= radius * sin(angle);
      y= radius * (cos(angle) - cos_0);
      if (iscdd)
        qh_out1(qh, 1.0);
      qh_out2n(qh, x*box, y*box);
      if (i != 0 && i != numpoints - 1) {
        if (iscdd)
          qh_out1(qh, 1.0);
        qh_out2n(qh, x*box, -y*box);
      }
    }
  }
  /* ============= regular points for 'r Ln D3' =============== */
  else if (isregular && islens && dim != 2) {
    if (dim != 3) {
      qh_free(simplex);
      qh_fprintf_rbox(qh, qh->ferr, 6198, "rbox error: regular points can be used only in 2-d and 3-d\n\n");
      qh_errexit_rbox(qh, qh_ERRinput);
    }
    angle= 0.0;
    anglediff= 2* PI/numpoints;
    if (!isgap) {
      isgap= 1;
      gap= 0.5;
    }
    offset= sqrt(radius * radius - (1-gap)*(1-gap)) - lensbase;
    for (i=0; i < numpoints; i++, angle += anglediff) {
      x= cos(angle);
      y= sin(angle);
      if (iscdd)
        qh_out1(qh, 1.0);
      qh_out3n(qh, box*x, box*y, 0.0);
      x *= 1-gap;
      y *= 1-gap;
      if (iscdd)
        qh_out1(qh, 1.0);
      qh_out3n(qh, box*x, box*y, box * offset);
      if (iscdd)
        qh_out1(qh, 1.0);
      qh_out3n(qh, box*x, box*y, -box * offset);
    }
  }
  /* ============= apex of 'Zn' distribution + gendim =============== */
  else {
    if (isaxis) {
      gendim= dim-1;
      if (iscdd)
        qh_out1(qh, 1.0);
      for (j=0; j < gendim; j++)
        qh_out1(qh, 0.0);
      qh_out1(qh, -box);
      qh_fprintf_rbox(qh, qh->fout, 9398, "\n");
    }else if (islens)
      gendim= dim-1;
    else
      gendim= dim;
    /* ============= generate random point in unit cube =============== */
    for (i=0; i < numpoints; i++) {
      norm= 0.0;
      for (j=0; j < gendim; j++) {
        randr= qh_RANDOMint;
        coord[j]= 2.0 * randr/randmax - 1.0;
        norm += coord[j] * coord[j];
      }
      norm= sqrt(norm);
      /* ============= dim-1 point of 'Zn' distribution ========== */
      if (isaxis) {
        if (!isgap) {
          isgap= 1;
          gap= 1.0;
        }
        randr= qh_RANDOMint;
        rangap= 1.0 - gap * randr/randmax;
        factor= radius * rangap / norm;
        for (j=0; jferr, 6199, "rbox error: spiral distribution is available only in 3d\n\n");
          qh_errexit_rbox(qh, qh_ERRinput);
        }
        coord[0]= cos(2*PI*i/(numpoints - 1));
        coord[1]= sin(2*PI*i/(numpoints - 1));
        coord[2]= 2.0*(double)i/(double)(numpoints-1) - 1.0;
      /* ============= point of 's' distribution =============== */
      }else if (issphere) {
        factor= 1.0/norm;
        if (iswidth) {
          randr= qh_RANDOMint;
          factor *= 1.0 - width * randr/randmax;
        }
        for (j=0; j 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=0; k--) {
        if (j & ( 1 << k))
          qh_out1(qh, cube);
        else
          qh_out1(qh, -cube);
      }
      qh_fprintf_rbox(qh, qh->fout, 9400, "\n");
    }
  }

  /* ============= write diamond vertices =============== */
  if (adddiamond) {
    for (j=0; j=0; k--) {
        if (j/2 != k)
          qh_out1(qh, 0.0);
        else if (j & 0x1)
          qh_out1(qh, diamond);
        else
          qh_out1(qh, -diamond);
      }
      qh_fprintf_rbox(qh, qh->fout, 9401, "\n");
    }
  }

  if (iscdd)
    qh_fprintf_rbox(qh, qh->fout, 9402, "end\nhull\n");

  /* same code for error exit and normal return */
  qh_free(simplex);
  return qh_ERRnone;
} /* rboxpoints */

/*------------------------------------------------
outxxx - output functions for qh_rboxpoints
*/
int qh_roundi(qhT *qh, double a) {
  if (a < 0.0) {
    if (a - 0.5 < INT_MIN) {
      qh_fprintf_rbox(qh, qh->ferr, 6200, "rbox input error: negative coordinate %2.2g is too large.  Reduce 'Bn'\n", a);
      qh_errexit_rbox(qh, qh_ERRinput);
    }
    return (int)(a - 0.5);
  }else {
    if (a + 0.5 > INT_MAX) {
      qh_fprintf_rbox(qh, qh->ferr, 6201, "rbox input error: coordinate %2.2g is too large.  Reduce 'Bn'\n", a);
      qh_errexit_rbox(qh, qh_ERRinput);
    }
    return (int)(a + 0.5);
  }
} /* qh_roundi */

void qh_out1(qhT *qh, double a) {

  if (qh->rbox_isinteger)
    qh_fprintf_rbox(qh, qh->fout, 9403, "%d ", qh_roundi(qh, a+qh->rbox_out_offset));
  else
    qh_fprintf_rbox(qh, qh->fout, 9404, qh_REAL_1, a+qh->rbox_out_offset);
} /* qh_out1 */

void qh_out2n(qhT *qh, double a, double b) {

  if (qh->rbox_isinteger)
    qh_fprintf_rbox(qh, qh->fout, 9405, "%d %d\n", qh_roundi(qh, a+qh->rbox_out_offset), qh_roundi(qh, b+qh->rbox_out_offset));
  else
    qh_fprintf_rbox(qh, qh->fout, 9406, qh_REAL_2n, a+qh->rbox_out_offset, b+qh->rbox_out_offset);
} /* qh_out2n */

void qh_out3n(qhT *qh, double a, double b, double c) {

  if (qh->rbox_isinteger)
    qh_fprintf_rbox(qh, qh->fout, 9407, "%d %d %d\n", qh_roundi(qh, a+qh->rbox_out_offset), qh_roundi(qh, b+qh->rbox_out_offset), qh_roundi(qh, c+qh->rbox_out_offset));
  else
    qh_fprintf_rbox(qh, qh->fout, 9408, qh_REAL_3n, a+qh->rbox_out_offset, b+qh->rbox_out_offset, c+qh->rbox_out_offset);
} /* qh_out3n */

void qh_outcoord(qhT *qh, int iscdd, double *coord, int dim) {
    double *p= coord;
    int k;

    if (iscdd)
      qh_out1(qh, 1.0);
    for (k=0; k < dim; k++)
      qh_out1(qh, *(p++));
    qh_fprintf_rbox(qh, qh->fout, 9396, "\n");
} /* qh_outcoord */

void qh_outcoincident(qhT *qh, int coincidentpoints, double radius, int iscdd, double *coord, int dim) {
  double *p;
  double randr, delta;
  int i,k;
  double randmax= qh_RANDOMmax;

  for (i= 0; ifout, 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(qhT *qh, int exitcode)
{
    longjmp(qh->rbox_errexit, exitcode);
} /* qh_errexit_rbox */

geometry/src/Rtsearch_orig.c0000644000176200001440000001316214227214776015652 0ustar  liggesusers/*

  Copyright (C) 2002-2011, 2017 Andreas Stahel
  Copyright (C) 2011-2017 David Sterratt

  This program is free software; you can redistribute it and/or modify it
  under the terms of the GNU General Public License as published by the
  Free Software Foundation; either version 3 of the License, or (at your
  option) any later version.

  This program is distributed in the hope that it will be useful, but WITHOUT
  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  for more details.

  You should have received a copy of the GNU General Public License
  along with this program. If not, see
  .

*/

/* Originally written for Octave */
/* Author: Andreas Stahel  */
/* 19 August 2011: Ported to R by David Sterratt  */

#include 
#include 
#include 
#include 
#include "Rgeometry.h"
#include "qhull_ra.h"

static inline double max (double a, double b, double c)
{
  if (a < b)
    return (b < c ? c : b);
  else
    return (a < c ? c : a);
}

static inline double min (double a, double b, double c)
{
  if (a > b)
    return (b > c ? c : b);
  else
    return (a > c ? c : a);
}


#define REF(x,k,i) x[ielem[k + i*nelem] - 1]

/* for large data set the algorithm is very slow one should presort
 (how?) either the elements of the points of evaluation to cut down
 the time needed to decide which triangle contains the given point

 e.g., build up a neighbouring triangle structure and use a simplex-like
 method to traverse it
*/

SEXP C_tsearch_orig(SEXP x,  SEXP y, SEXP elem,
                    SEXP xi, SEXP yi,
                    SEXP bary) {
  int ibary = 0;
  if (isLogical(bary))
    if (*LOGICAL(bary) == TRUE)
      ibary = 1;

  /* printf("Here 1\n"); */
  double *rx = REAL(x);
  double *ry = REAL(y);
  int nelem = nrows(elem);
  int *ielem = INTEGER(elem);
  double *rxi = REAL(xi);
  double *ryi = REAL(yi);
  int np = LENGTH(xi);
  /* printf("%i points\n", np); */
  SEXP minx, maxx, miny, maxy;
  PROTECT(minx = allocVector(REALSXP, nelem));
  PROTECT(maxx = allocVector(REALSXP, nelem));
  PROTECT(miny = allocVector(REALSXP, nelem));
  PROTECT(maxy = allocVector(REALSXP, nelem));
  double *rminx = REAL(minx);
  double *rmaxx = REAL(maxx);
  double *rminy = REAL(miny);
  double *rmaxy = REAL(maxy);

  /* Find bounding boxes of each triangle */
  for (int k = 0; k < nelem; k++) {
    /* printf("X[T[%i, 1]] = %f; T[%i, 1] = %i\n", k+1, REF(rx, k, 0), k+1, ielem[k + 0*nelem]); */
    rminx[k] = min(REF(rx, k, 0), REF(rx, k, 1), REF(rx, k, 2)) - DBL_EPSILON;
    rmaxx[k] = max(REF(rx, k, 0), REF(rx, k, 1), REF(rx, k, 2)) + DBL_EPSILON;
    rminy[k] = min(REF(ry, k, 0), REF(ry, k, 1), REF(ry, k, 2)) - DBL_EPSILON;
    rmaxy[k] = max(REF(ry, k, 0), REF(ry, k, 1), REF(ry, k, 2)) + DBL_EPSILON;
    /* printf("%f %f %f %f\n", rminx[k], rmaxx[k], rminy[k], rmaxy[k]); */
  }

  /* Make space for output */
  SEXP values;
  PROTECT(values = allocVector(INTSXP, np));
  int *ivalues = INTEGER(values);
  SEXP p = NULL;
  double *rp = NULL;
  if (ibary) {
    PROTECT(p = allocMatrix(REALSXP, np, 3));
    rp = REAL(p);
    for (int k = 0; k < 3*np; k++)
      rp[k] = NA_REAL;
  }

  double x0 = 0.0, y0 = 0.0;
  double a11 = 0.0, a12 = 0.0, a21 = 0.0, a22 = 0.0, det = 0.0;

  double xt, yt;
  double dx1, dx2, c1, c2;
  int k = nelem; // k is a counter of elements
  for (int kp = 0; kp < np; kp++) {
    xt = rxi[kp];
    yt = ryi[kp];

    /* check if last triangle contains the next point */
    if (k < nelem) {
      dx1 = xt - x0;
      dx2 = yt - y0;
      c1 = ( a22 * dx1 - a21 * dx2) / det;
      c2 = (-a12 * dx1 + a11 * dx2) / det;
      if ((c1 >= -DBL_EPSILON) && (c2 >= -DBL_EPSILON) && ((c1 + c2) <= (1 + DBL_EPSILON))) {
        ivalues[kp] = k+1;
        if (ibary) {
          rp[kp] = 1 - c1 - c2;
          rp[kp+np] = c1;
          rp[kp+2*np] = c2;
        }
        continue;
      }
    }

    // it doesn't, so go through all elements
    for (k = 0; k < nelem; k++) {
      /* OCTAVE_QUIT; */
      if (xt >= rminx[k] && xt <= rmaxx[k] && yt >= rminy[k] && yt <= rmaxy[k]) {
        /* printf("Point %i (%1.3f, %1.3f) could be in triangle %i (%1.3f, %1.3f) (%1.3f, %1.3f)\n",  */
        /*        kp+1, xt, yt, k+1, rminx[k], rminy[k], rmaxx[k], rmaxy[k]); */
        // element inside the minimum rectangle: examine it closely
        x0  = REF(rx, k, 0);
        y0  = REF(ry, k, 0);
        /* printf("Triangle %i: x0=%f, y0=%f\n", k+1, x0, y0); */
        a11 = REF(rx, k, 1) - x0;
        a12 = REF(ry, k, 1) - y0;
        a21 = REF(rx, k, 2) - x0;
        a22 = REF(ry, k, 2) - y0;
        det = a11 * a22 - a21 * a12;

        // solve the system
        dx1 = xt - x0;
        dx2 = yt - y0;
        c1 = ( a22 * dx1 - a21 * dx2) / det;
        c2 = (-a12 * dx1 + a11 * dx2) / det;
        if ((c1 >= -DBL_EPSILON) && (c2 >= -DBL_EPSILON) && ((c1 + c2) <= (1 + DBL_EPSILON))) {
          /* printf("Setting point %i's triangle to %i\n", kp+1, k+1);  */
          ivalues[kp] = k+1;
          if (ibary) {
            rp[kp] = 1 - c1 - c2;
            rp[kp+np] = c1;
            rp[kp+2*np] = c2;
          }
          break;
        }
      } //endif # examine this element closely
    } //endfor # each element
    /* printf("%i\n", kp); */
    if (k == nelem) {
      ivalues[kp] = NA_INTEGER;
    }
  } //endfor # kp

  SEXP ans;
  if (ibary) {
    PROTECT(ans = allocVector(VECSXP, 2));
    SET_VECTOR_ELT(ans, 0, values);
    SET_VECTOR_ELT(ans, 1, p);
    UNPROTECT(7);
    return(ans);
  } else {
    UNPROTECT(5);
    return(values);
  }
}
geometry/src/poly_r.h0000644000176200001440000002674313432323614014366 0ustar  liggesusers/*
  ---------------------------------

   poly_r.h
   header file for poly_r.c and poly2_r.c

   see qh-poly_r.htm, libqhull_r.h and poly_r.c

   Copyright (c) 1993-2015 The Geometry Center.
   $Id: //main/2015/qhull/src/libqhull_r/poly_r.h#5 $$Change: 2079 $
   $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
*/

#ifndef qhDEFpoly
#define qhDEFpoly 1

#include "libqhull_r.h"

/*===============   constants ========================== */

/*----------------------------------

  ALGORITHMfault
    use as argument to checkconvex() to report errors during buildhull
*/
#define qh_ALGORITHMfault 0

/*----------------------------------

  DATAfault
    use as argument to checkconvex() to report errors during initialhull
*/
#define qh_DATAfault 1

/*----------------------------------

  DUPLICATEridge
    special value for facet->neighbor to indicate a duplicate ridge

  notes:
    set by matchneighbor, used by matchmatch and mark_dupridge
*/
#define qh_DUPLICATEridge (facetT *)1L

/*----------------------------------

  MERGEridge       flag in facet
    special value for facet->neighbor to indicate a merged ridge

  notes:
    set by matchneighbor, used by matchmatch and mark_dupridge
*/
#define qh_MERGEridge (facetT *)2L


/*============ -structures- ====================*/

/*=========== -macros- =========================*/

/*----------------------------------

  FORALLfacet_( facetlist ) { ... }
    assign 'facet' to each facet in facetlist

  notes:
    uses 'facetT *facet;'
    assumes last facet is a sentinel

  see:
    FORALLfacets
*/
#define FORALLfacet_( facetlist ) if (facetlist ) for ( facet=( facetlist ); facet && facet->next; facet= facet->next )

/*----------------------------------

  FORALLnew_facets { ... }
    assign 'newfacet' to each facet in qh.newfacet_list

  notes:
    uses 'facetT *newfacet;'
    at exit, newfacet==NULL
*/
#define FORALLnew_facets for ( newfacet=qh->newfacet_list;newfacet && newfacet->next;newfacet=newfacet->next )

/*----------------------------------

  FORALLvertex_( vertexlist ) { ... }
    assign 'vertex' to each vertex in vertexlist

  notes:
    uses 'vertexT *vertex;'
    at exit, vertex==NULL
*/
#define FORALLvertex_( vertexlist ) for (vertex=( vertexlist );vertex && vertex->next;vertex= vertex->next )

/*----------------------------------

  FORALLvisible_facets { ... }
    assign 'visible' to each visible facet in qh.visible_list

  notes:
    uses 'vacetT *visible;'
    at exit, visible==NULL
*/
#define FORALLvisible_facets for (visible=qh->visible_list; visible && visible->visible; visible= visible->next)

/*----------------------------------

  FORALLsame_( newfacet ) { ... }
    assign 'same' to each facet in newfacet->f.samecycle

  notes:
    uses 'facetT *same;'
    stops when it returns to newfacet
*/
#define FORALLsame_(newfacet) for (same= newfacet->f.samecycle; same != newfacet; same= same->f.samecycle)

/*----------------------------------

  FORALLsame_cycle_( newfacet ) { ... }
    assign 'same' to each facet in newfacet->f.samecycle

  notes:
    uses 'facetT *same;'
    at exit, same == NULL
*/
#define FORALLsame_cycle_(newfacet) \
     for (same= newfacet->f.samecycle; \
         same; same= (same == newfacet ?  NULL : same->f.samecycle))

/*----------------------------------

  FOREACHneighborA_( facet ) { ... }
    assign 'neighborA' to each neighbor in facet->neighbors

  FOREACHneighborA_( vertex ) { ... }
    assign 'neighborA' to each neighbor in vertex->neighbors

  declare:
    facetT *neighborA, **neighborAp;

  see:
    FOREACHsetelement_
*/
#define FOREACHneighborA_(facet)  FOREACHsetelement_(facetT, facet->neighbors, neighborA)

/*----------------------------------

  FOREACHvisible_( facets ) { ... }
    assign 'visible' to each facet in facets

  notes:
    uses 'facetT *facet, *facetp;'
    see FOREACHsetelement_
*/
#define FOREACHvisible_(facets) FOREACHsetelement_(facetT, facets, visible)

/*----------------------------------

  FOREACHnewfacet_( facets ) { ... }
    assign 'newfacet' to each facet in facets

  notes:
    uses 'facetT *newfacet, *newfacetp;'
    see FOREACHsetelement_
*/
#define FOREACHnewfacet_(facets) FOREACHsetelement_(facetT, facets, newfacet)

/*----------------------------------

  FOREACHvertexA_( vertices ) { ... }
    assign 'vertexA' to each vertex in vertices

  notes:
    uses 'vertexT *vertexA, *vertexAp;'
    see FOREACHsetelement_
*/
#define FOREACHvertexA_(vertices) FOREACHsetelement_(vertexT, vertices, vertexA)

/*----------------------------------

  FOREACHvertexreverse12_( vertices ) { ... }
    assign 'vertex' to each vertex in vertices
    reverse order of first two vertices

  notes:
    uses 'vertexT *vertex, *vertexp;'
    see FOREACHsetelement_
*/
#define FOREACHvertexreverse12_(vertices) FOREACHsetelementreverse12_(vertexT, vertices, vertex)


/*=============== prototypes poly_r.c in alphabetical order ================*/

#ifdef __cplusplus
extern "C" {
#endif

void    qh_appendfacet(qhT *qh, facetT *facet);
void    qh_appendvertex(qhT *qh, vertexT *vertex);
void    qh_attachnewfacets(qhT *qh /* qh.visible_list, qh.newfacet_list */);
boolT   qh_checkflipped(qhT *qh, facetT *facet, realT *dist, boolT allerror);
void    qh_delfacet(qhT *qh, facetT *facet);
void    qh_deletevisible(qhT *qh /* qh.visible_list, qh.horizon_list */);
setT   *qh_facetintersect(qhT *qh, facetT *facetA, facetT *facetB, int *skipAp,int *skipBp, int extra);
int     qh_gethash(qhT *qh, int hashsize, setT *set, int size, int firstindex, void *skipelem);
facetT *qh_makenewfacet(qhT *qh, setT *vertices, boolT toporient, facetT *facet);
void    qh_makenewplanes(qhT *qh /* qh.newfacet_list */);
facetT *qh_makenew_nonsimplicial(qhT *qh, facetT *visible, vertexT *apex, int *numnew);
facetT *qh_makenew_simplicial(qhT *qh, facetT *visible, vertexT *apex, int *numnew);
void    qh_matchneighbor(qhT *qh, facetT *newfacet, int newskip, int hashsize,
                          int *hashcount);
void    qh_matchnewfacets(qhT *qh);
boolT   qh_matchvertices(qhT *qh, int firstindex, setT *verticesA, int skipA,
                          setT *verticesB, int *skipB, boolT *same);
facetT *qh_newfacet(qhT *qh);
ridgeT *qh_newridge(qhT *qh);
int     qh_pointid(qhT *qh, pointT *point);
void    qh_removefacet(qhT *qh, facetT *facet);
void    qh_removevertex(qhT *qh, vertexT *vertex);
void    qh_updatevertices(qhT *qh);


/*========== -prototypes poly2_r.c in alphabetical order ===========*/

void    qh_addhash(void *newelem, setT *hashtable, int hashsize, int hash);
void    qh_check_bestdist(qhT *qh);
void    qh_check_dupridge(qhT *qh, facetT *facet1, realT dist1, facetT *facet2, realT dist2);
void    qh_check_maxout(qhT *qh);
void    qh_check_output(qhT *qh);
void    qh_check_point(qhT *qh, pointT *point, facetT *facet, realT *maxoutside, realT *maxdist, facetT **errfacet1, facetT **errfacet2);
void    qh_check_points(qhT *qh);
void    qh_checkconvex(qhT *qh, facetT *facetlist, int fault);
void    qh_checkfacet(qhT *qh, facetT *facet, boolT newmerge, boolT *waserrorp);
void    qh_checkflipped_all(qhT *qh, facetT *facetlist);
void    qh_checkpolygon(qhT *qh, facetT *facetlist);
void    qh_checkvertex(qhT *qh, vertexT *vertex);
void    qh_clearcenters(qhT *qh, qh_CENTER type);
void    qh_createsimplex(qhT *qh, setT *vertices);
void    qh_delridge(qhT *qh, ridgeT *ridge);
void    qh_delvertex(qhT *qh, vertexT *vertex);
setT   *qh_facet3vertex(qhT *qh, facetT *facet);
facetT *qh_findbestfacet(qhT *qh, pointT *point, boolT bestoutside,
           realT *bestdist, boolT *isoutside);
facetT *qh_findbestlower(qhT *qh, facetT *upperfacet, pointT *point, realT *bestdistp, int *numpart);
facetT *qh_findfacet_all(qhT *qh, pointT *point, realT *bestdist, boolT *isoutside,
                          int *numpart);
int     qh_findgood(qhT *qh, facetT *facetlist, int goodhorizon);
void    qh_findgood_all(qhT *qh, facetT *facetlist);
void    qh_furthestnext(qhT *qh /* qh.facet_list */);
void    qh_furthestout(qhT *qh, facetT *facet);
void    qh_infiniteloop(qhT *qh, facetT *facet);
void    qh_initbuild(qhT *qh);
void    qh_initialhull(qhT *qh, setT *vertices);
setT   *qh_initialvertices(qhT *qh, int dim, setT *maxpoints, pointT *points, int numpoints);
vertexT *qh_isvertex(pointT *point, setT *vertices);
vertexT *qh_makenewfacets(qhT *qh, pointT *point /*horizon_list, visible_list*/);
void    qh_matchduplicates(qhT *qh, facetT *atfacet, int atskip, int hashsize, int *hashcount);
void    qh_nearcoplanar(qhT *qh /* qh.facet_list */);
vertexT *qh_nearvertex(qhT *qh, facetT *facet, pointT *point, realT *bestdistp);
int     qh_newhashtable(qhT *qh, int newsize);
vertexT *qh_newvertex(qhT *qh, pointT *point);
ridgeT *qh_nextridge3d(ridgeT *atridge, facetT *facet, vertexT **vertexp);
void    qh_outcoplanar(qhT *qh /* qh.facet_list */);
pointT *qh_point(qhT *qh, int id);
void    qh_point_add(qhT *qh, setT *set, pointT *point, void *elem);
setT   *qh_pointfacet(qhT *qh /*qh.facet_list*/);
setT   *qh_pointvertex(qhT *qh /*qh.facet_list*/);
void    qh_prependfacet(qhT *qh, facetT *facet, facetT **facetlist);
void    qh_printhashtable(qhT *qh, FILE *fp);
void    qh_printlists(qhT *qh);
void    qh_resetlists(qhT *qh, boolT stats, boolT resetVisible /*qh.newvertex_list qh.newfacet_list qh.visible_list*/);
void    qh_setvoronoi_all(qhT *qh);
void    qh_triangulate(qhT *qh /*qh.facet_list*/);
void    qh_triangulate_facet(qhT *qh, facetT *facetA, vertexT **first_vertex);
void    qh_triangulate_link(qhT *qh, facetT *oldfacetA, facetT *facetA, facetT *oldfacetB, facetT *facetB);
void    qh_triangulate_mirror(qhT *qh, facetT *facetA, facetT *facetB);
void    qh_triangulate_null(qhT *qh, facetT *facetA);
void    qh_vertexintersect(qhT *qh, setT **vertexsetA,setT *vertexsetB);
setT   *qh_vertexintersect_new(qhT *qh, setT *vertexsetA,setT *vertexsetB);
void    qh_vertexneighbors(qhT *qh /*qh.facet_list*/);
boolT   qh_vertexsubset(setT *vertexsetA, setT *vertexsetB);

#ifdef __cplusplus
} /* extern "C" */
#endif

#endif /* qhDEFpoly */
geometry/src/poly_r.c0000644000176200001440000012063513432323610014350 0ustar  liggesusers/*
  ---------------------------------

   poly_r.c
   implements polygons and simplices

   see qh-poly_r.htm, poly_r.h and libqhull_r.h

   infrequent code is in poly2_r.c
   (all but top 50 and their callers 12/3/95)

   Copyright (c) 1993-2015 The Geometry Center.
   $Id: //main/2015/qhull/src/libqhull_r/poly_r.c#3 $$Change: 2064 $
   $DateTime: 2016/01/18 12:36:08 $$Author: bbarber $
*/

#include "qhull_ra.h"

/*======== functions in alphabetical order ==========*/

/*---------------------------------

  qh_appendfacet(qh, facet )
    appends facet to end of qh.facet_list,

  returns:
    updates qh.newfacet_list, facet_next, facet_list
    increments qh.numfacets

  notes:
    assumes qh.facet_list/facet_tail is defined (createsimplex)

  see:
    qh_removefacet()

*/
void qh_appendfacet(qhT *qh, facetT *facet) {
  facetT *tail= qh->facet_tail;

  if (tail == qh->newfacet_list)
    qh->newfacet_list= facet;
  if (tail == qh->facet_next)
    qh->facet_next= facet;
  facet->previous= tail->previous;
  facet->next= tail;
  if (tail->previous)
    tail->previous->next= facet;
  else
    qh->facet_list= facet;
  tail->previous= facet;
  qh->num_facets++;
  trace4((qh, qh->ferr, 4044, "qh_appendfacet: append f%d to facet_list\n", facet->id));
} /* appendfacet */


/*---------------------------------

  qh_appendvertex(qh, 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(qhT *qh, 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, qh->ferr, 4045, "qh_appendvertex: append v%d to vertex_list\n", vertex->id));
} /* appendvertex */


/*---------------------------------

  qh_attachnewfacets(qh, )
    attach horizon facets to new facets in qh.newfacet_list
    newfacets have neighbor and ridge links to horizon but not vice versa
    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(qhT *qh /* qh.visible_list, newfacet_list */) {
  facetT *newfacet= NULL, *neighbor, **neighborp, *horizon, *visible;
  ridgeT *ridge, **ridgep;

  qh->NEWfacets= True;
  trace3((qh, 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(qh, &(ridge->vertices)); /* delete on 2nd visit */
          qh_memfree(qh, ridge, (int)sizeof(ridgeT));
        }
      }
      SETfirst_(visible->ridges)= NULL;
    }
    SETfirst_(visible->neighbors)= NULL;
  }
  trace1((qh, qh->ferr, 1017, "qh_attachnewfacets: attach horizon facets to new facets\n"));
  FORALLnew_facets {
    horizon= SETfirstt_(newfacet->neighbors, facetT);
    if (horizon->simplicial) {
      visible= NULL;
      FOREACHneighbor_(horizon) {   /* may have more than one horizon ridge */
        if (neighbor->visible) {
          if (visible) {
            if (qh_setequal_skip(newfacet->vertices, 0, horizon->vertices,
                                  SETindex_(horizon->neighbors, neighbor))) {
              visible= neighbor;
              break;
            }
          }else
            visible= neighbor;
        }
      }
      if (visible) {
        visible->f.replace= newfacet;
        qh_setreplace(qh, horizon->neighbors, visible, newfacet);
      }else {
        qh_fprintf(qh, qh->ferr, 6102, "qhull internal error (qh_attachnewfacets): couldn't find visible facet for horizon f%d of newfacet f%d\n",
                 horizon->id, newfacet->id);
        qh_errexit2(qh, qh_ERRqhull, horizon, newfacet);
      }
    }else { /* non-simplicial, with a ridge for newfacet */
      FOREACHneighbor_(horizon) {    /* may hold for many new facets */
        if (neighbor->visible) {
          neighbor->f.replace= newfacet;
          qh_setdelnth(qh, horizon->neighbors,
                        SETindex_(horizon->neighbors, neighbor));
          neighborp--; /* repeat */
        }
      }
      qh_setappend(qh, &horizon->neighbors, newfacet);
      ridge= SETfirstt_(newfacet->ridges, ridgeT);
      if (ridge->top == horizon)
        ridge->bottom= newfacet;
      else
        ridge->top= newfacet;
      }
  } /* newfacets */
  if (qh->PRINTstatistics) {
    FORALLvisible_facets {
      if (!visible->f.replace)
        zinc_(Zinsidevisible);
    }
  }
} /* attachnewfacets */

/*---------------------------------

  qh_checkflipped(qh, 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(qhT *qh, facetT *facet, realT *distp, boolT allerror) {
  realT dist;

  if (facet->flipped && !distp)
    return False;
  zzinc_(Zdistcheck);
  qh_distplane(qh, qh->interior_point, facet, &dist);
  if (distp)
    *distp= dist;
  if ((allerror && dist > -qh->DISTround)|| (!allerror && dist >= 0.0)) {
    facet->flipped= True;
    zzinc_(Zflippedfacets);
    trace0((qh, 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(qh, "flipped facet");
    return False;
  }
  return True;
} /* checkflipped */

/*---------------------------------

  qh_delfacet(qh, facet )
    removes facet from facet_list and frees up its memory

  notes:
    assumes vertices and ridges already freed
*/
void qh_delfacet(qhT *qh, facetT *facet) {
  void **freelistp; /* used if !qh_NOmem by qh_memfree_() */

  trace4((qh, 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(qh, facet);
  if (!facet->tricoplanar || facet->keepcentrum) {
    qh_memfree_(qh, facet->normal, qh->normal_size, freelistp);
    if (qh->CENTERtype == qh_ASvoronoi) {   /* braces for macro calls */
      qh_memfree_(qh, facet->center, qh->center_size, freelistp);
    }else /* AScentrum */ {
      qh_memfree_(qh, facet->center, qh->normal_size, freelistp);
    }
  }
  qh_setfree(qh, &(facet->neighbors));
  if (facet->ridges)
    qh_setfree(qh, &(facet->ridges));
  qh_setfree(qh, &(facet->vertices));
  if (facet->outsideset)
    qh_setfree(qh, &(facet->outsideset));
  if (facet->coplanarset)
    qh_setfree(qh, &(facet->coplanarset));
  qh_memfree_(qh, facet, (int)sizeof(facetT), freelistp);
} /* delfacet */


/*---------------------------------

  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(qhT *qh /*qh.visible_list*/) {
  facetT *visible, *nextfacet;
  vertexT *vertex, **vertexp;
  int numvisible= 0, numdel= qh_setsize(qh, qh->del_vertices);

  trace1((qh, qh->ferr, 1018, "qh_deletevisible: delete %d visible facets and %d vertices\n",
         qh->num_visible, numdel));
  for (visible= qh->visible_list; visible && visible->visible;
                visible= nextfacet) { /* deleting current */
    nextfacet= visible->next;
    numvisible++;
    qh_delfacet(qh, visible);
  }
  if (numvisible != qh->num_visible) {
    qh_fprintf(qh, qh->ferr, 6103, "qhull internal error (qh_deletevisible): qh->num_visible %d is not number of visible facets %d\n",
             qh->num_visible, numvisible);
    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
  }
  qh->num_visible= 0;
  zadd_(Zvisfacettot, numvisible);
  zmax_(Zvisfacetmax, numvisible);
  zzadd_(Zdelvertextot, numdel);
  zmax_(Zdelvertexmax, numdel);
  FOREACHvertex_(qh->del_vertices)
    qh_delvertex(qh, vertex);
  qh_settruncate(qh, qh->del_vertices, 0);
} /* deletevisible */

/*---------------------------------

  qh_facetintersect(qh, facetA, facetB, skipa, skipB, prepend )
    return vertices for intersection of two simplicial facets
    may include 1 prepended entry (if more, need to settemppush)

  returns:
    returns set of qh.hull_dim-1 + prepend vertices
    returns skipped index for each test and checks for exactly one

  notes:
    does not need settemp since set in quick memory

  see also:
    qh_vertexintersect and qh_vertexintersect_new
    use qh_setnew_delnthsorted to get nth ridge (no skip information)

  design:
    locate skipped vertex by scanning facet A's neighbors
    locate skipped vertex by scanning facet B's neighbors
    intersect the vertex sets
*/
setT *qh_facetintersect(qhT *qh, facetT *facetA, facetT *facetB,
                         int *skipA,int *skipB, int prepend) {
  setT *intersect;
  int dim= qh->hull_dim, i, j;
  facetT **neighborsA, **neighborsB;

  neighborsA= SETaddr_(facetA->neighbors, facetT);
  neighborsB= SETaddr_(facetB->neighbors, facetT);
  i= j= 0;
  if (facetB == *neighborsA++)
    *skipA= 0;
  else if (facetB == *neighborsA++)
    *skipA= 1;
  else if (facetB == *neighborsA++)
    *skipA= 2;
  else {
    for (i=3; i < dim; i++) {
      if (facetB == *neighborsA++) {
        *skipA= i;
        break;
      }
    }
  }
  if (facetA == *neighborsB++)
    *skipB= 0;
  else if (facetA == *neighborsB++)
    *skipB= 1;
  else if (facetA == *neighborsB++)
    *skipB= 2;
  else {
    for (j=3; j < dim; j++) {
      if (facetA == *neighborsB++) {
        *skipB= j;
        break;
      }
    }
  }
  if (i >= dim || j >= dim) {
    qh_fprintf(qh, qh->ferr, 6104, "qhull internal error (qh_facetintersect): f%d or f%d not in others neighbors\n",
            facetA->id, facetB->id);
    qh_errexit2(qh, qh_ERRqhull, facetA, facetB);
  }
  intersect= qh_setnew_delnthsorted(qh, facetA->vertices, qh->hull_dim, *skipA, prepend);
  trace4((qh, qh->ferr, 4047, "qh_facetintersect: f%d skip %d matches f%d skip %d\n",
          facetA->id, *skipA, facetB->id, *skipB));
  return(intersect);
} /* facetintersect */

/*---------------------------------

  qh_gethash(qh, hashsize, set, size, firstindex, skipelem )
    return hashvalue for a set with firstindex and skipelem

  notes:
    returned hash is in [0,hashsize)
    assumes at least firstindex+1 elements
    assumes skipelem is NULL, in set, or part of hash

    hashes memory addresses which may change over different runs of the same data
    using sum for hash does badly in high d
*/
int qh_gethash(qhT *qh, int hashsize, setT *set, int size, int firstindex, void *skipelem) {
  void **elemp= SETelemaddr_(set, firstindex, void);
  ptr_intT hash = 0, elem;
  unsigned 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, qh->ferr, 6202, "qhull internal error: negative hashsize %d passed to qh_gethash [poly.c]\n", hashsize);
    qh_errexit2(qh, qh_ERRqhull, NULL, NULL);
  }
  result= (unsigned)hash;
  result %= (unsigned)hashsize;
  /* result= 0; for debugging */
  return result;
#ifdef _MSC_VER
#pragma warning( pop)
#endif
} /* gethash */

/*---------------------------------

  qh_makenewfacet(qh, vertices, toporient, horizon )
    creates a toporient? facet from vertices

  returns:
    returns newfacet
      adds newfacet to qh.facet_list
      newfacet->vertices= vertices
      if horizon
        newfacet->neighbor= horizon, but not vice versa
    newvertex_list updated with vertices
*/
facetT *qh_makenewfacet(qhT *qh, setT *vertices, boolT toporient,facetT *horizon) {
  facetT *newfacet;
  vertexT *vertex, **vertexp;

  FOREACHvertex_(vertices) {
    if (!vertex->newlist) {
      qh_removevertex(qh, vertex);
      qh_appendvertex(qh, vertex);
    }
  }
  newfacet= qh_newfacet(qh);
  newfacet->vertices= vertices;
  newfacet->toporient= (unsigned char)toporient;
  if (horizon)
    qh_setappend(qh, &(newfacet->neighbors), horizon);
  qh_appendfacet(qh, newfacet);
  return(newfacet);
} /* makenewfacet */


/*---------------------------------

  qh_makenewplanes()
    make new hyperplanes for facets on qh.newfacet_list

  returns:
    all facets have hyperplanes or are marked for   merging
    doesn't create hyperplane if horizon is coplanar (will merge)
    updates qh.min_vertex if qh.JOGGLEmax

  notes:
    facet->f.samecycle is defined for facet->mergehorizon facets
*/
void qh_makenewplanes(qhT *qh /* qh.newfacet_list */) {
  facetT *newfacet;

  FORALLnew_facets {
    if (!newfacet->mergehorizon)
      qh_setfacetplane(qh, newfacet);
  }
  if (qh->JOGGLEmax < REALmax/2)
    minimize_(qh->min_vertex, -wwval_(Wnewvertexmax));
} /* makenewplanes */

/*---------------------------------

  qh_makenew_nonsimplicial(qh, visible, apex, numnew )
    make new facets for ridges of a visible facet

  returns:
    first newfacet, bumps numnew as needed
    attaches new facets if !qh.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(qhT *qh, facetT *visible, vertexT *apex, int *numnew) {
  void **freelistp; /* used if !qh_NOmem by qh_memfree_() */
  ridgeT *ridge, **ridgep;
  facetT *neighbor, *newfacet= NULL, *samecycle;
  setT *vertices;
  boolT toporient;
  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(qh, &(ridge->vertices));  /* delete on 2nd visit */
          qh_memfree_(qh, ridge, (int)sizeof(ridgeT), freelistp);
        }
      }
    }else {  /* neighbor is an horizon facet */
      toporient= (ridge->top == visible);
      vertices= qh_setnew(qh, qh->hull_dim); /* makes sure this is quick */
      qh_setappend(qh, &vertices, apex);
      qh_setappend_set(qh, &vertices, ridge->vertices);
      newfacet= qh_makenewfacet(qh, vertices, toporient, neighbor);
      (*numnew)++;
      if (neighbor->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(qh, &(newfacet->ridges), ridge);
      }else {  /* qh_attachnewfacets */
        if (neighbor->seen) {
          if (neighbor->simplicial) {
            qh_fprintf(qh, qh->ferr, 6105, "qhull internal error (qh_makenew_nonsimplicial): simplicial f%d sharing two ridges with f%d\n",
                   neighbor->id, visible->id);
            qh_errexit2(qh, qh_ERRqhull, neighbor, visible);
          }
          qh_setappend(qh, &(neighbor->neighbors), newfacet);
        }else
          qh_setreplace(qh, neighbor->neighbors, visible, newfacet);
        if (neighbor->simplicial) {
          qh_setdel(neighbor->ridges, ridge);
          qh_setfree(qh, &(ridge->vertices));
          qh_memfree(qh, ridge, (int)sizeof(ridgeT));
        }else {
          qh_setappend(qh, &(newfacet->ridges), ridge);
          if (toporient)
            ridge->top= newfacet;
          else
            ridge->bottom= newfacet;
        }
      trace4((qh, qh->ferr, 4048, "qh_makenew_nonsimplicial: created facet f%d from v%d and r%d of horizon f%d\n",
            newfacet->id, apex->id, ridgeid, neighbor->id));
      }
    }
    neighbor->seen= True;
  } /* for each ridge */
  if (!qh->ONLYgood)
    SETfirst_(visible->ridges)= NULL;
  return newfacet;
} /* makenew_nonsimplicial */
#else /* qh_NOmerge */
facetT *qh_makenew_nonsimplicial(qhT *qh, facetT *visible, vertexT *apex, int *numnew) {
  return NULL;
}
#endif /* qh_NOmerge */

/*---------------------------------

  qh_makenew_simplicial(qh, 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(qhT *qh, facetT *visible, vertexT *apex, int *numnew) {
  facetT *neighbor, **neighborp, *newfacet= NULL;
  setT *vertices;
  boolT flip, toporient;
  int horizonskip= 0, visibleskip= 0;

  FOREACHneighbor_(visible) {
    if (!neighbor->seen && !neighbor->visible) {
      vertices= qh_facetintersect(qh, neighbor,visible, &horizonskip, &visibleskip, 1);
      SETfirst_(vertices)= apex;
      flip= ((horizonskip & 0x1) ^ (visibleskip & 0x1));
      if (neighbor->toporient)
        toporient= horizonskip & 0x1;
      else
        toporient= (horizonskip & 0x1) ^ 0x1;
      newfacet= qh_makenewfacet(qh, vertices, toporient, neighbor);
      (*numnew)++;
      if (neighbor->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, qh->ferr, 4049, "qh_makenew_simplicial: create facet f%d top %d from v%d and horizon f%d skip %d top %d and visible f%d skip %d, flip? %d\n",
            newfacet->id, toporient, apex->id, neighbor->id, horizonskip,
              neighbor->toporient, visible->id, visibleskip, flip));
    }
  }
  return newfacet;
} /* makenew_simplicial */

/*---------------------------------

  qh_matchneighbor(qh, 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(qhT *qh, facetT *newfacet, int newskip, int hashsize, int *hashcount) {
  boolT newfound= False;   /* True, if new facet is already in hash chain */
  boolT same, ismatch;
  int hash, scan;
  facetT *facet, *matchfacet;
  int skip, matchskip;

  hash= qh_gethash(qh, hashsize, newfacet->vertices, qh->hull_dim, 1,
                     SETelem_(newfacet->vertices, newskip));
  trace4((qh, qh->ferr, 4050, "qh_matchneighbor: newfacet f%d skip %d hash %d hashcount %d\n",
          newfacet->id, newskip, hash, *hashcount));
  zinc_(Zhashlookup);
  for (scan= hash; (facet= SETelemt_(qh->hash_table, scan, facetT));
       scan= (++scan >= hashsize ? 0 : scan)) {
    if (facet == newfacet) {
      newfound= True;
      continue;
    }
    zinc_(Zhashtests);
    if (qh_matchvertices(qh, 1, newfacet->vertices, newskip, facet->vertices, &skip, &same)) {
      if (SETelem_(newfacet->vertices, newskip) ==
          SETelem_(facet->vertices, skip)) {
        qh_precision(qh, "two facets with the same vertices");
        qh_fprintf(qh, 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, 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, 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(qh, "a ridge with more than two neighbors");
        qh_fprintf(qh, 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, qh_ERRprec, facet, newfacet);
      }
      SETelem_(newfacet->neighbors, newskip)= qh_DUPLICATEridge;
      newfacet->dupridge= True;
      if (!newfacet->normal)
        qh_setfacetplane(qh, newfacet);
      qh_addhash(newfacet, qh->hash_table, hashsize, hash);
      (*hashcount)++;
      if (!facet->normal)
        qh_setfacetplane(qh, facet);
      if (matchfacet != qh_DUPLICATEridge) {
        SETelem_(facet->neighbors, skip)= qh_DUPLICATEridge;
        facet->dupridge= True;
        if (!facet->normal)
          qh_setfacetplane(qh, facet);
        if (matchfacet) {
          matchskip= qh_setindex(matchfacet->neighbors, facet);
          if (matchskip<0) {
              qh_fprintf(qh, 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, qh_ERRqhull, matchfacet, facet);
          }
          SETelem_(matchfacet->neighbors, matchskip)= qh_DUPLICATEridge; /* matchskip>=0 by QH6260 */
          matchfacet->dupridge= True;
          if (!matchfacet->normal)
            qh_setfacetplane(qh, matchfacet);
          qh_addhash(matchfacet, qh->hash_table, hashsize, hash);
          *hashcount += 2;
        }
      }
      trace4((qh, qh->ferr, 4052, "qh_matchneighbor: new f%d skip %d duplicates ridge for f%d skip %d matching f%d ismatch %d at hash %d\n",
           newfacet->id, newskip, facet->id, skip,
           (matchfacet == qh_DUPLICATEridge ? -2 : getid_(matchfacet)),
           ismatch, hash));
      return; /* end of duplicate ridge */
    }
  }
  if (!newfound)
    SETelem_(qh->hash_table, scan)= newfacet;  /* same as qh_addhash */
  (*hashcount)++;
  trace4((qh, qh->ferr, 4053, "qh_matchneighbor: no match for f%d skip %d at hash %d\n",
           newfacet->id, newskip, hash));
} /* matchneighbor */


/*---------------------------------

  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(qhT *qh /* 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, qh->ferr, 1019, "qh_matchnewfacets: match neighbors for new facets.\n"));
  FORALLnew_facets {
    numnew++;
    {  /* inline qh_setzero(qh, newfacet->neighbors, 1, qh->hull_dim); */
      neighbors= newfacet->neighbors;
      neighbors->e[neighbors->maxsize].i= dim+1; /*may be overwritten*/
      memset((char *)SETelemaddr_(neighbors, 1, void), 0, dim * SETelemsize);
    }
  }

  qh_newhashtable(qh, numnew*(qh->hull_dim-1)); /* twice what is normally needed,
                                     but every ridge could be DUPLICATEridge */
  hashsize= qh_setsize(qh, qh->hash_table);
  FORALLnew_facets {
    for (newskip=1; newskiphull_dim; newskip++) /* furthest/horizon already matched */
      /* hashsize>0 because hull_dim>1 and numnew>0 */
      qh_matchneighbor(qh, newfacet, newskip, hashsize, &hashcount);
#if 0   /* use the following to trap hashcount errors */
    {
      int count= 0, k;
      facetT *facet, *neighbor;

      count= 0;
      FORALLfacet_(qh->newfacet_list) {  /* newfacet already in use */
        for (k=1; k < qh->hull_dim; k++) {
          neighbor= SETelemt_(facet->neighbors, k, facetT);
          if (!neighbor || neighbor == qh_DUPLICATEridge)
            count++;
        }
        if (facet == newfacet)
          break;
      }
      if (count != hashcount) {
        qh_fprintf(qh, qh->ferr, 8088, "qh_matchnewfacets: after adding facet %d, hashcount %d != count %d\n",
                 newfacet->id, hashcount, count);
        qh_errexit(qh, qh_ERRqhull, newfacet, NULL);
      }
    }
#endif  /* end of trap code */
  }
  if (hashcount) {
    FORALLnew_facets {
      if (newfacet->dupridge) {
        FOREACHneighbor_i_(qh, newfacet) {
          if (neighbor == qh_DUPLICATEridge) {
            qh_matchduplicates(qh, newfacet, neighbor_i, hashsize, &hashcount);
                    /* this may report MERGEfacet */
          }
        }
      }
    }
  }
  if (hashcount) {
    qh_fprintf(qh, qh->ferr, 6108, "qhull internal error (qh_matchnewfacets): %d neighbors did not match up\n",
        hashcount);
    qh_printhashtable(qh, qh->ferr);
    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
  }
#ifndef qh_NOtrace
  if (qh->IStracing >= 2) {
    FOREACHfacet_i_(qh, qh->hash_table) {
      if (!facet)
        numfree++;
    }
    qh_fprintf(qh, qh->ferr, 8089, "qh_matchnewfacets: %d new facets, %d unused hash entries .  hashsize %d\n",
             numnew, numfree, qh_setsize(qh, qh->hash_table));
  }
#endif /* !qh_NOtrace */
  qh_setfree(qh, &qh->hash_table);
  if (qh->PREmerge || qh->MERGEexact) {
    if (qh->IStracing >= 4)
      qh_printfacetlist(qh, qh->newfacet_list, NULL, qh_ALL);
    FORALLnew_facets {
      if (newfacet->normal)
        qh_checkflipped(qh, newfacet, NULL, qh_ALL);
    }
  }else if (qh->FORCEoutput)
    qh_checkflipped_all(qh, qh->newfacet_list);  /* prints warnings for flipped */
} /* matchnewfacets */


/*---------------------------------

  qh_matchvertices(qh, firstindex, verticesA, skipA, verticesB, skipB, same )
    tests whether vertices match with a single skip
    starts match at firstindex since all new facets have a common vertex

  returns:
    true if matched vertices
    skip index for 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(qhT *qh, int firstindex, setT *verticesA, int skipA,
       setT *verticesB, int *skipB, boolT *same) {
  vertexT **elemAp, **elemBp, **skipBp=NULL, **skipAp;

  elemAp= SETelemaddr_(verticesA, firstindex, vertexT);
  elemBp= SETelemaddr_(verticesB, firstindex, vertexT);
  skipAp= SETelemaddr_(verticesA, skipA, vertexT);
  do if (elemAp != skipAp) {
    while (*elemAp != *elemBp++) {
      if (skipBp)
        return False;
      skipBp= elemBp;  /* one extra like FOREACH */
    }
  }while (*(++elemAp));
  if (!skipBp)
    skipBp= ++elemBp;
  *skipB= SETindex_(verticesB, skipB); /* i.e., skipBp - verticesB */
  *same= !((skipA & 0x1) ^ (*skipB & 0x1)); /* result is 0 or 1 */
  trace4((qh, qh->ferr, 4054, "qh_matchvertices: matched by skip %d(v%d) and skip %d(v%d) same? %d\n",
          skipA, (*skipAp)->id, *skipB, (*(skipBp-1))->id, *same));
  return(True);
} /* matchvertices */

/*---------------------------------

  qh_newfacet(qh)
    return a new facet

  returns:
    all fields initialized or cleared   (NULL)
    preallocates neighbors set
*/
facetT *qh_newfacet(qhT *qh) {
  facetT *facet;
  void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */

  qh_memalloc_(qh, (int)sizeof(facetT), freelistp, facet, facetT);
  memset((char *)facet, (size_t)0, sizeof(facetT));
  if (qh->facet_id == qh->tracefacet_id)
    qh->tracefacet= facet;
  facet->id= qh->facet_id++;
  facet->neighbors= qh_setnew(qh, qh->hull_dim);
#if !qh_COMPUTEfurthest
  facet->furthestdist= 0.0;
#endif
#if qh_MAXoutside
  if (qh->FORCEoutput && qh->APPROXhull)
    facet->maxoutside= qh->MINoutside;
  else
    facet->maxoutside= qh->DISTround;
#endif
  facet->simplicial= True;
  facet->good= True;
  facet->newfacet= True;
  trace4((qh, qh->ferr, 4055, "qh_newfacet: created facet f%d\n", facet->id));
  return(facet);
} /* newfacet */


/*---------------------------------

  qh_newridge()
    return a new ridge
*/
ridgeT *qh_newridge(qhT *qh) {
  ridgeT *ridge;
  void **freelistp;   /* used if !qh_NOmem by qh_memalloc_() */

  qh_memalloc_(qh, (int)sizeof(ridgeT), freelistp, ridge, ridgeT);
  memset((char *)ridge, (size_t)0, sizeof(ridgeT));
  zinc_(Ztotridges);
  if (qh->ridge_id == UINT_MAX) {
    qh_fprintf(qh, qh->ferr, 7074, "\
qhull warning: more than 2^32 ridges.  Qhull results are OK.  Since the ridge ID wraps around to 0, two ridges may have the same identifier.\n");
  }
  ridge->id= qh->ridge_id++;
  trace4((qh, qh->ferr, 4056, "qh_newridge: created ridge r%d\n", ridge->id));
  return(ridge);
} /* newridge */


/*---------------------------------

  qh_pointid(qh, point )
    return id for a point,
    returns qh_IDnone(-3) if null, qh_IDinterior(-2) if interior, or qh_IDunknown(-1) if not known

  alternative code if point is in qh.first_point...
    unsigned long id;
    id= ((unsigned long)point - (unsigned long)qh.first_point)/qh.normal_size;

  notes:
    Valid points are non-negative
    WARN64 -- id truncated to 32-bits, at most 2G points
    NOerrors returned (QhullPoint::id)
    if point not in point array
      the code does a comparison of unrelated pointers.
*/
int qh_pointid(qhT *qh, pointT *point) {
  ptr_intT offset, id;

  if (!point || !qh)
    return qh_IDnone;
  else if (point == qh->interior_point)
    return qh_IDinterior;
  else if (point >= qh->first_point
  && point < qh->first_point + qh->num_points * qh->hull_dim) {
    offset= (ptr_intT)(point - qh->first_point);
    id= offset / qh->hull_dim;
  }else if ((id= qh_setindex(qh->other_points, point)) != -1)
    id += qh->num_points;
  else
    return qh_IDunknown;
  return (int)id;
} /* pointid */

/*---------------------------------

  qh_removefacet(qh, facet )
    unlinks facet from qh.facet_list,

  returns:
    updates qh.facet_list .newfacet_list .facet_next visible_list
    decrements qh.num_facets

  see:
    qh_appendfacet
*/
void qh_removefacet(qhT *qh, facetT *facet) {
  facetT *next= facet->next, *previous= facet->previous;

  if (facet == qh->newfacet_list)
    qh->newfacet_list= next;
  if (facet == qh->facet_next)
    qh->facet_next= next;
  if (facet == qh->visible_list)
    qh->visible_list= next;
  if (previous) {
    previous->next= next;
    next->previous= previous;
  }else {  /* 1st facet in qh->facet_list */
    qh->facet_list= next;
    qh->facet_list->previous= NULL;
  }
  qh->num_facets--;
  trace4((qh, qh->ferr, 4057, "qh_removefacet: remove f%d from facet_list\n", facet->id));
} /* removefacet */


/*---------------------------------

  qh_removevertex(qh, vertex )
    unlinks vertex from qh.vertex_list,

  returns:
    updates qh.vertex_list .newvertex_list
    decrements qh.num_vertices
*/
void qh_removevertex(qhT *qh, vertexT *vertex) {
  vertexT *next= vertex->next, *previous= vertex->previous;

  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, qh->ferr, 4058, "qh_removevertex: remove v%d from vertex_list\n", vertex->id));
} /* removevertex */


/*---------------------------------

  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(qhT *qh /*qh.newvertex_list, newfacet_list, visible_list*/) {
  facetT *newfacet= NULL, *neighbor, **neighborp, *visible;
  vertexT *vertex, **vertexp;

  trace3((qh, 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(qh, vertex->neighbors);
    }
    FORALLnew_facets {
      FOREACHvertex_(newfacet->vertices)
        qh_setappend(qh, &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, &qh->del_vertices, vertex);
            trace2((qh, qh->ferr, 2041, "qh_updatevertices: delete vertex p%d(v%d) in f%d\n",
                  qh_pointid(qh, vertex->point), vertex->id, visible->id));
          }
        }
      }
    }
  }else {  /* !VERTEXneighbors */
    FORALLvisible_facets {
      FOREACHvertex_(visible->vertices) {
        if (!vertex->newlist && !vertex->deleted) {
          vertex->deleted= True;
          qh_setappend(qh, &qh->del_vertices, vertex);
          trace2((qh, qh->ferr, 2042, "qh_updatevertices: delete vertex p%d(v%d) in f%d\n",
                  qh_pointid(qh, vertex->point), vertex->id, visible->id));
        }
      }
    }
  }
} /* updatevertices */



geometry/src/stat_r.h0000644000176200001440000003036513432323614014351 0ustar  liggesusers/*
  ---------------------------------

   stat_r.h
     contains all statistics that are collected for qhull

   see qh-stat_r.htm and stat_r.c

   Copyright (c) 1993-2015 The Geometry Center.
   $Id: //main/2015/qhull/src/libqhull_r/stat_r.h#5 $$Change: 2079 $
   $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $

   recompile qhull if you change this file

   Integer statistics are Z* while real statistics are W*.

   define MAYdebugx to call a routine at every statistic event

*/

#ifndef qhDEFstat
#define qhDEFstat 1

/* Depends on realT.  Do not include libqhull_r to avoid circular dependency */

#ifndef DEFqhT
#define DEFqhT 1
typedef struct qhT qhT;         /* Defined by libqhull_r.h */
#endif

#ifndef DEFqhstatT
#define DEFqhstatT 1
typedef struct qhstatT qhstatT; /* Defined here */
#endif

/*---------------------------------

  qh_KEEPstatistics
    0 turns off statistic gathering (except zzdef/zzinc/zzadd/zzval/wwval)
*/
#ifndef qh_KEEPstatistics
#define qh_KEEPstatistics 1
#endif

/*---------------------------------

  Zxxx for integers, Wxxx for reals

  notes:
    be sure that all statistics are defined in stat_r.c
      otherwise initialization may core dump
    can pick up all statistics by:
      grep '[zw].*_[(][ZW]' *.c >z.x
    remove trailers with query">-
    remove leaders with  query-replace-regexp [ ^I]+  (
*/
#if qh_KEEPstatistics
enum 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};

/*---------------------------------

  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

/*---------------------------------

  ztype
    the type of a statistic sets its initial value.

  notes:
    The type should be the same as the macro for collecting the statistic
*/
enum ztypes {zdoc,zinc,zadd,zmax,zmin,ZTYPEreal,wadd,wmax,wmin,ZTYPEend};

/*========== macros and constants =============*/

/*----------------------------------

  MAYdebugx
    define as maydebug() to be called frequently for error trapping
*/
#define MAYdebugx

/*----------------------------------

  zzdef_, zdef_( type, name, doc, -1)
    define a statistic (assumes 'qhstat.next= 0;')

  zdef_( type, name, doc, count)
    define an averaged statistic
    printed as name/count
*/
#define zzdef_(stype,name,string,cnt) qh->qhstat.id[qh->qhstat.next++]=name; \
   qh->qhstat.doc[name]= string; qh->qhstat.count[name]= cnt; qh->qhstat.type[name]= stype
#if qh_KEEPstatistics
#define zdef_(stype,name,string,cnt) qh->qhstat.id[qh->qhstat.next++]=name; \
   qh->qhstat.doc[name]= string; qh->qhstat.count[name]= cnt; qh->qhstat.type[name]= stype
#else
#define zdef_(type,name,doc,count)
#endif

/*----------------------------------

  zzinc_( name ), zinc_( name)
    increment an integer statistic
*/
#define zzinc_(id) {MAYdebugx; qh->qhstat.stats[id].i++;}
#if qh_KEEPstatistics
#define zinc_(id) {MAYdebugx; qh->qhstat.stats[id].i++;}
#else
#define zinc_(id) {}
#endif

/*----------------------------------

  zzadd_( name, value ), zadd_( name, value ), wadd_( name, value )
    add value to an integer or real statistic
*/
#define zzadd_(id, val) {MAYdebugx; qh->qhstat.stats[id].i += (val);}
#define wwadd_(id, val) {MAYdebugx; qh->qhstat.stats[id].r += (val);}
#if qh_KEEPstatistics
#define zadd_(id, val) {MAYdebugx; qh->qhstat.stats[id].i += (val);}
#define wadd_(id, val) {MAYdebugx; qh->qhstat.stats[id].r += (val);}
#else
#define zadd_(id, val) {}
#define wadd_(id, val) {}
#endif

/*----------------------------------

  zzval_( name ), zval_( name ), wwval_( name )
    set or return value of a statistic
*/
#define zzval_(id) ((qh->qhstat.stats[id]).i)
#define wwval_(id) ((qh->qhstat.stats[id]).r)
#if qh_KEEPstatistics
#define zval_(id) ((qh->qhstat.stats[id]).i)
#define wval_(id) ((qh->qhstat.stats[id]).r)
#else
#define zval_(id) qh->qhstat.tempi
#define wval_(id) qh->qhstat.tempr
#endif

/*----------------------------------

  zmax_( id, val ), wmax_( id, value )
    maximize id with val
*/
#define wwmax_(id, val) {MAYdebugx; maximize_(qh->qhstat.stats[id].r,(val));}
#if qh_KEEPstatistics
#define zmax_(id, val) {MAYdebugx; maximize_(qh->qhstat.stats[id].i,(val));}
#define wmax_(id, val) {MAYdebugx; maximize_(qh->qhstat.stats[id].r,(val));}
#else
#define zmax_(id, val) {}
#define wmax_(id, val) {}
#endif

/*----------------------------------

  zmin_( id, val ), wmin_( id, value )
    minimize id with val
*/
#if qh_KEEPstatistics
#define zmin_(id, val) {MAYdebugx; minimize_(qh->qhstat.stats[id].i,(val));}
#define wmin_(id, val) {MAYdebugx; minimize_(qh->qhstat.stats[id].r,(val));}
#else
#define zmin_(id, val) {}
#define wmin_(id, val) {}
#endif

/*================== stat_r.h types ==============*/


/*----------------------------------

  intrealT
    union of integer and real, used for statistics
*/
typedef union intrealT intrealT;    /* union of int and realT */
union intrealT {
    int i;
    realT r;
};

/*----------------------------------

  qhstat
    Data structure for statistics, similar to qh and qhrbox

    Allocated as part of qhT (libqhull_r.h)
*/

struct qhstatT {
  intrealT   stats[ZEND];     /* integer and real statistics */
  unsigned   char id[ZEND+10]; /* id's in print order */
  const char *doc[ZEND];       /* array of documentation strings */
  short int  count[ZEND];     /* -1 if none, else index of count to use */
  char       type[ZEND];      /* type, see ztypes above */
  char       printed[ZEND];   /* true, if statistic has been printed */
  intrealT   init[ZTYPEend];  /* initial values by types, set initstatistics */

  int        next;            /* next index for zdef_ */
  int        precision;       /* index for precision problems */
  int        vridges;         /* index for Voronoi ridges */
  int        tempi;
  realT      tempr;
};

/*========== function prototypes ===========*/

#ifdef __cplusplus
extern "C" {
#endif

void    qh_allstatA(qhT *qh);
void    qh_allstatB(qhT *qh);
void    qh_allstatC(qhT *qh);
void    qh_allstatD(qhT *qh);
void    qh_allstatE(qhT *qh);
void    qh_allstatE2(qhT *qh);
void    qh_allstatF(qhT *qh);
void    qh_allstatG(qhT *qh);
void    qh_allstatH(qhT *qh);
void    qh_allstatI(qhT *qh);
void    qh_allstatistics(qhT *qh);
void    qh_collectstatistics(qhT *qh);
void    qh_initstatistics(qhT *qh);
boolT   qh_newstats(qhT *qh, int idx, int *nextindex);
boolT   qh_nostatistic(qhT *qh, int i);
void    qh_printallstatistics(qhT *qh, FILE *fp, const char *string);
void    qh_printstatistics(qhT *qh, FILE *fp, const char *string);
void    qh_printstatlevel(qhT *qh, FILE *fp, int id);
void    qh_printstats(qhT *qh, FILE *fp, int idx, int *nextindex);
realT   qh_stddev(int num, realT tot, realT tot2, realT *ave);

#ifdef __cplusplus
} /* extern "C" */
#endif

#endif   /* qhDEFstat */
geometry/src/geom_r.h0000644000176200001440000001703113432323614014320 0ustar  liggesusers/*
  ---------------------------------

  geom_r.h
    header file for geometric routines

   see qh-geom_r.htm and geom_r.c

   Copyright (c) 1993-2015 The Geometry Center.
   $Id: //main/2015/qhull/src/libqhull_r/geom_r.h#3 $$Change: 2079 $
   $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
*/

#ifndef qhDEFgeom
#define qhDEFgeom 1

#include "libqhull_r.h"

/* ============ -macros- ======================== */

/*----------------------------------

  fabs_(a)
    returns the absolute value of a
*/
#define fabs_( a ) ((( a ) < 0 ) ? -( a ):( a ))

/*----------------------------------

  fmax_(a,b)
    returns the maximum value of a and b
*/
#define fmax_( a,b )  ( ( a ) < ( b ) ? ( b ) : ( a ) )

/*----------------------------------

  fmin_(a,b)
    returns the minimum value of a and b
*/
#define fmin_( a,b )  ( ( a ) > ( b ) ? ( b ) : ( a ) )

/*----------------------------------

  maximize_(maxval, val)
    set maxval to val if val is greater than maxval
*/
#define maximize_( maxval, val ) { if (( maxval ) < ( val )) ( maxval )= ( val ); }

/*----------------------------------

  minimize_(minval, val)
    set minval to val if val is less than minval
*/
#define minimize_( minval, val ) { if (( minval ) > ( val )) ( minval )= ( val ); }

/*----------------------------------

  det2_(a1, a2,
        b1, b2)

    compute a 2-d determinate
*/
#define det2_( a1,a2,b1,b2 ) (( a1 )*( b2 ) - ( a2 )*( b1 ))

/*----------------------------------

  det3_(a1, a2, a3,
       b1, b2, b3,
       c1, c2, c3)

    compute a 3-d determinate
*/
#define det3_( a1,a2,a3,b1,b2,b3,c1,c2,c3 ) ( ( a1 )*det2_( b2,b3,c2,c3 ) \
                - ( b1 )*det2_( a2,a3,c2,c3 ) + ( c1 )*det2_( a2,a3,b2,b3 ) )

/*----------------------------------

  dX( p1, p2 )
  dY( p1, p2 )
  dZ( p1, p2 )

    given two indices into rows[],

    compute the difference between X, Y, or Z coordinates
*/
#define dX( p1,p2 )  ( *( rows[p1] ) - *( rows[p2] ))
#define dY( p1,p2 )  ( *( rows[p1]+1 ) - *( rows[p2]+1 ))
#define dZ( p1,p2 )  ( *( rows[p1]+2 ) - *( rows[p2]+2 ))
#define dW( p1,p2 )  ( *( rows[p1]+3 ) - *( rows[p2]+3 ))

/*============= prototypes in alphabetical order, infrequent at end ======= */

#ifdef __cplusplus
extern "C" {
#endif

void    qh_backnormal(qhT *qh, realT **rows, int numrow, int numcol, boolT sign, coordT *normal, boolT *nearzero);
void    qh_distplane(qhT *qh, pointT *point, facetT *facet, realT *dist);
facetT *qh_findbest(qhT *qh, pointT *point, facetT *startfacet,
                     boolT bestoutside, boolT isnewfacets, boolT noupper,
                     realT *dist, boolT *isoutside, int *numpart);
facetT *qh_findbesthorizon(qhT *qh, boolT ischeckmax, pointT *point,
                     facetT *startfacet, boolT noupper, realT *bestdist, int *numpart);
facetT *qh_findbestnew(qhT *qh, pointT *point, facetT *startfacet, realT *dist,
                     boolT bestoutside, boolT *isoutside, int *numpart);
void    qh_gausselim(qhT *qh, realT **rows, int numrow, int numcol, boolT *sign, boolT *nearzero);
realT   qh_getangle(qhT *qh, pointT *vect1, pointT *vect2);
pointT *qh_getcenter(qhT *qh, setT *vertices);
pointT *qh_getcentrum(qhT *qh, facetT *facet);
realT   qh_getdistance(qhT *qh, facetT *facet, facetT *neighbor, realT *mindist, realT *maxdist);
void    qh_normalize(qhT *qh, coordT *normal, int dim, boolT toporient);
void    qh_normalize2(qhT *qh, coordT *normal, int dim, boolT toporient,
            realT *minnorm, boolT *ismin);
pointT *qh_projectpoint(qhT *qh, pointT *point, facetT *facet, realT dist);

void    qh_setfacetplane(qhT *qh, facetT *newfacets);
void    qh_sethyperplane_det(qhT *qh, int dim, coordT **rows, coordT *point0,
              boolT toporient, coordT *normal, realT *offset, boolT *nearzero);
void    qh_sethyperplane_gauss(qhT *qh, int dim, coordT **rows, pointT *point0,
             boolT toporient, coordT *normal, coordT *offset, boolT *nearzero);
boolT   qh_sharpnewfacets(qhT *qh);

/*========= infrequently used code in geom2_r.c =============*/

coordT *qh_copypoints(qhT *qh, coordT *points, int numpoints, int dimension);
void    qh_crossproduct(int dim, realT vecA[3], realT vecB[3], realT vecC[3]);
realT   qh_determinant(qhT *qh, realT **rows, int dim, boolT *nearzero);
realT   qh_detjoggle(qhT *qh, pointT *points, int numpoints, int dimension);
void    qh_detroundoff(qhT *qh);
realT   qh_detsimplex(qhT *qh, pointT *apex, setT *points, int dim, boolT *nearzero);
realT   qh_distnorm(int dim, pointT *point, pointT *normal, realT *offsetp);
realT   qh_distround(qhT *qh, int dimension, realT maxabs, realT maxsumabs);
realT   qh_divzero(realT numer, realT denom, realT mindenom1, boolT *zerodiv);
realT   qh_facetarea(qhT *qh, facetT *facet);
realT   qh_facetarea_simplex(qhT *qh, int dim, coordT *apex, setT *vertices,
          vertexT *notvertex,  boolT toporient, coordT *normal, realT *offset);
pointT *qh_facetcenter(qhT *qh, setT *vertices);
facetT *qh_findgooddist(qhT *qh, pointT *point, facetT *facetA, realT *distp, facetT **facetlist);
void    qh_getarea(qhT *qh, facetT *facetlist);
boolT   qh_gram_schmidt(qhT *qh, int dim, realT **rows);
boolT   qh_inthresholds(qhT *qh, coordT *normal, realT *angle);
void    qh_joggleinput(qhT *qh);
realT  *qh_maxabsval(realT *normal, int dim);
setT   *qh_maxmin(qhT *qh, pointT *points, int numpoints, int dimension);
realT   qh_maxouter(qhT *qh);
void    qh_maxsimplex(qhT *qh, int dim, setT *maxpoints, pointT *points, int numpoints, setT **simplex);
realT   qh_minabsval(realT *normal, int dim);
int     qh_mindiff(realT *vecA, realT *vecB, int dim);
boolT   qh_orientoutside(qhT *qh, facetT *facet);
void    qh_outerinner(qhT *qh, facetT *facet, realT *outerplane, realT *innerplane);
coordT  qh_pointdist(pointT *point1, pointT *point2, int dim);
void    qh_printmatrix(qhT *qh, FILE *fp, const char *string, realT **rows, int numrow, int numcol);
void    qh_printpoints(qhT *qh, FILE *fp, const char *string, setT *points);
void    qh_projectinput(qhT *qh);
void    qh_projectpoints(qhT *qh, signed char *project, int n, realT *points,
             int numpoints, int dim, realT *newpoints, int newdim);
void    qh_rotateinput(qhT *qh, realT **rows);
void    qh_rotatepoints(qhT *qh, realT *points, int numpoints, int dim, realT **rows);
void    qh_scaleinput(qhT *qh);
void    qh_scalelast(qhT *qh, coordT *points, int numpoints, int dim, coordT low,
                   coordT high, coordT newhigh);
void    qh_scalepoints(qhT *qh, pointT *points, int numpoints, int dim,
                realT *newlows, realT *newhighs);
boolT   qh_sethalfspace(qhT *qh, int dim, coordT *coords, coordT **nextp,
              coordT *normal, coordT *offset, coordT *feasible);
coordT *qh_sethalfspace_all(qhT *qh, int dim, int count, coordT *halfspaces, pointT *feasible);
pointT *qh_voronoi_center(qhT *qh, int dim, setT *points);

#ifdef __cplusplus
} /* extern "C"*/
#endif

#endif /* qhDEFgeom */



geometry/src/user_r.c0000644000176200001440000005002113432323610014332 0ustar  liggesusers/*
  ---------------------------------

   user.c
   user redefinable functions

   see user2_r.c for qh_fprintf, qh_malloc, qh_free

   see README.txt  see COPYING.txt for copyright information.

   see libqhull_r.h for data structures, macros, and user-callable functions.

   see user_eg.c, user_eg2.c, and unix.c for examples.

   see user.h for user-definable constants

      use qh_NOmem in mem_r.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_r.c for an
   example.  We recommend keeping io.o for the extra debugging
   information it supplies.
*/

#include "qhull_ra.h"

#include 

/*---------------------------------

  Qhull-template
    Template for calling qhull from inside your program

  returns:
    exit code(see qh_ERR... in libqhull_r.h)
    all memory freed

  notes:
    This can be called any number of times.

*/
#if 0
{
  int dim;                  /* dimension of points */
  int numpoints;            /* number of points */
  coordT *points;           /* array of coordinates for each point */
  boolT ismalloc;           /* True if qhull should free points in qh_freeqhull() or reallocation */
  char flags[]= "qhull Tv"; /* option flags for qhull, see qh_opt.htm */
  FILE *outfile= stdout;    /* output from qh_produce_output(qh)
                               use NULL to skip qh_produce_output(qh) */
  FILE *errfile= stderr;    /* error messages from qhull code */
  int exitcode;             /* 0 if no error from qhull */
  facetT *facet;            /* set by FORALLfacets */
  int curlong, totlong;     /* memory remaining after qh_memfreeshort */

  qhT qh_qh;                /* Qhull's data structure.  First argument of most calls */
  qhT *qh= &qh_qh;          /* Alternatively -- qhT *qh= (qhT*)malloc(sizeof(qhT)) */

  QHULL_LIB_CHECK /* Check for compatible library */

  qh_zero(qh, errfile);

  /* initialize dim, numpoints, points[], ismalloc here */
  exitcode= qh_new_qhull(qh, dim, numpoints, points, ismalloc,
                      flags, outfile, errfile);
  if (!exitcode) {                  /* if no error */
    /* 'qh->facet_list' contains the convex hull */
    FORALLfacets {
       /* ... your code ... */
    }
  }
  qh_freeqhull(qh, !qh_ALL);
  qh_memfreeshort(qh, &curlong, &totlong);
  if (curlong || totlong)
    qh_fprintf(qh, errfile, 7068, "qhull internal warning (main): did not free %d bytes of long memory(%d pieces)\n", totlong, curlong);
}
#endif

/*---------------------------------

  qh_new_qhull(qh, dim, numpoints, points, ismalloc, qhull_cmd, outfile, errfile )
    Run qhull and return results in qh.
    Returns exitcode (0 if no errors).
    Before first call, either call qh_zero(qh, errfile), or set qh to all zero.

  notes:
    do not modify points until finished with results.
      The qhull data structure contains pointers into the points array.
    do not call qhull functions before qh_new_qhull().
      The qhull data structure is not initialized until qh_new_qhull().
    do not call qh_init_A (global_r.c)

    Default errfile is stderr, outfile may be null
    qhull_cmd must start with "qhull "
    projects points to a new point array for Delaunay triangulations ('d' and 'v')
    transforms points into a new point array for halfspace intersection ('H')

  see:
    Qhull-template at the beginning of this file.
    An example of using qh_new_qhull is user_eg_r.c
*/
int qh_new_qhull(qhT *qh, int dim, int numpoints, coordT *points, boolT ismalloc,
                char *qhull_cmd, FILE *outfile, FILE *errfile) {
  /* gcc may issue a "might be clobbered" warning for dim, points, and ismalloc [-Wclobbered].
     These parameters are not referenced after a longjmp() and hence not clobbered.
     See http://stackoverflow.com/questions/7721854/what-sense-do-these-clobbered-variable-warnings-make */
  int exitcode, hulldim;
  boolT new_ismalloc;
  coordT *new_points;

  if(!errfile){
    errfile= stderr;
  }
  if (!qh->qhmem.ferr) {
    qh_meminit(qh, errfile);
  } else {
    qh_memcheck(qh);
  }
  if (strncmp(qhull_cmd, "qhull ", (size_t)6)) {
    qh_fprintf(qh, errfile, 6186, "qhull error (qh_new_qhull): start qhull_cmd argument with \"qhull \"\n");
    return qh_ERRinput;
  }
  qh_initqhull_start(qh, NULL, outfile, errfile);
  trace1((qh, qh->ferr, 1044, "qh_new_qhull: build new Qhull for %d %d-d points with %s\n", numpoints, dim, qhull_cmd));
  exitcode = setjmp(qh->errexit);
  if (!exitcode)
  {
    qh->NOerrexit = False;
    qh_initflags(qh, qhull_cmd);
    if (qh->DELAUNAY)
      qh->PROJECTdelaunay= True;
    if (qh->HALFspace) {
      /* points is an array of halfspaces,
         the last coordinate of each halfspace is its offset */
      hulldim= dim-1;
      qh_setfeasible(qh, hulldim);
      new_points= qh_sethalfspace_all(qh, dim, numpoints, points, qh->feasible_point);
      new_ismalloc= True;
      if (ismalloc)
        qh_free(points);
    }else {
      hulldim= dim;
      new_points= points;
      new_ismalloc= ismalloc;
    }
    qh_init_B(qh, new_points, numpoints, hulldim, new_ismalloc);
    qh_qhull(qh);
    qh_check_output(qh);
    if (outfile) {
      qh_produce_output(qh);
    }else {
      qh_prepare_output(qh);
    }
    if (qh->VERIFYoutput && !qh->STOPpoint && !qh->STOPcone)
      qh_check_points(qh);
  }
  qh->NOerrexit = True;
  return exitcode;
} /* new_qhull */

/*---------------------------------

  qh_errexit(qh, exitcode, facet, ridge )
    report and exit from an error
    report facet and ridge if non-NULL
    reports useful information such as last point processed
    set qh.FORCEoutput to print neighborhood of facet

  see:
    qh_errexit2() in libqhull_r.c for printing 2 facets

  design:
    check for error within error processing
    compute qh.hulltime
    print facet and ridge (if any)
    report commandString, options, qh.furthest_id
    print summary and statistics (including precision statistics)
    if qh_ERRsingular
      print help text for singular data set
    exit program via long jump (if defined) or exit()
*/
void qh_errexit(qhT *qh, int exitcode, facetT *facet, ridgeT *ridge) {

  if (qh->ERREXITcalled) {
    qh_fprintf(qh, 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(qh, "ERRONEOUS", facet, NULL, ridge, NULL);
  qh_fprintf(qh, qh->ferr, 8127, "\nWhile executing: %s | %s\n", qh->rbox_command, qh->qhull_command);
  qh_fprintf(qh, qh->ferr, 8128, "Options selected for Qhull %s:\n%s\n", qh_version, qh->qhull_options);
  if (qh->furthest_id >= 0) {
    qh_fprintf(qh, qh->ferr, 8129, "Last point added to hull was p%d.", qh->furthest_id);
    if (zzval_(Ztotmerge))
      qh_fprintf(qh, qh->ferr, 8130, "  Last merge was #%d.", zzval_(Ztotmerge));
    if (qh->QHULLfinished)
      qh_fprintf(qh, qh->ferr, 8131, "\nQhull has finished constructing the hull.");
    else if (qh->POSTmerging)
      qh_fprintf(qh, qh->ferr, 8132, "\nQhull has started post-merging.");
    qh_fprintf(qh, qh->ferr, 8133, "\n");
  }
  if (qh->FORCEoutput && (qh->QHULLfinished || (!facet && !ridge)))
    qh_produce_output(qh);
  else if (exitcode != qh_ERRinput) {
    if (exitcode != qh_ERRsingular && zzval_(Zsetplane) > qh->hull_dim+1) {
      qh_fprintf(qh, qh->ferr, 8134, "\nAt error exit:\n");
      qh_printsummary(qh, qh->ferr);
      if (qh->PRINTstatistics) {
        qh_collectstatistics(qh);
        qh_printstatistics(qh, qh->ferr, "at error exit");
        qh_memstatistics(qh, qh->ferr);
      }
    }
    if (qh->PRINTprecision)
      qh_printstats(qh, qh->ferr, qh->qhstat.precision, NULL);
  }
  if (!exitcode)
    exitcode= qh_ERRqhull;
  else if (exitcode == qh_ERRsingular)
    qh_printhelp_singular(qh, qh->ferr);
  else if (exitcode == qh_ERRprec && !qh->PREmerge)
    qh_printhelp_degenerate(qh, qh->ferr);
  if (qh->NOerrexit) {
    qh_fprintf(qh, 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 */


/*---------------------------------

  qh_errprint(qh, fp, string, atfacet, otherfacet, atridge, atvertex )
    prints out the information of facets and ridges to fp
    also prints neighbors and geomview output

  notes:
    except for string, any parameter may be NULL
*/
void qh_errprint(qhT *qh, const char *string, facetT *atfacet, facetT *otherfacet, ridgeT *atridge, vertexT *atvertex) {
  int i;

  if (atfacet) {
    qh_fprintf(qh, qh->ferr, 8135, "%s FACET:\n", string);
    qh_printfacet(qh, qh->ferr, atfacet);
  }
  if (otherfacet) {
    qh_fprintf(qh, qh->ferr, 8136, "%s OTHER FACET:\n", string);
    qh_printfacet(qh, qh->ferr, otherfacet);
  }
  if (atridge) {
    qh_fprintf(qh, qh->ferr, 8137, "%s RIDGE:\n", string);
    qh_printridge(qh, qh->ferr, atridge);
    if (atridge->top && atridge->top != atfacet && atridge->top != otherfacet)
      qh_printfacet(qh, qh->ferr, atridge->top);
    if (atridge->bottom
        && atridge->bottom != atfacet && atridge->bottom != otherfacet)
      qh_printfacet(qh, qh->ferr, atridge->bottom);
    if (!atfacet)
      atfacet= atridge->top;
    if (!otherfacet)
      otherfacet= otherfacet_(atridge, atfacet);
  }
  if (atvertex) {
    qh_fprintf(qh, qh->ferr, 8138, "%s VERTEX:\n", string);
    qh_printvertex(qh, qh->ferr, atvertex);
  }
  if (qh->fout && qh->FORCEoutput && atfacet && !qh->QHULLfinished && !qh->IStracing) {
    qh_fprintf(qh, qh->ferr, 8139, "ERRONEOUS and NEIGHBORING FACETS to output\n");
    for (i=0; i < qh_PRINTEND; i++)  /* use fout for geomview output */
      qh_printneighborhood(qh, qh->fout, qh->PRINTout[i], atfacet, otherfacet,
                            !qh_ALL);
  }
} /* errprint */


/*---------------------------------

  qh_printfacetlist(qh, fp, facetlist, facets, printall )
    print all fields for a facet list and/or set of facets to fp
    if !printall,
      only prints good facets

  notes:
    also prints all vertices
*/
void qh_printfacetlist(qhT *qh, facetT *facetlist, setT *facets, boolT printall) {
  facetT *facet, **facetp;

  qh_printbegin(qh, qh->ferr, qh_PRINTfacets, facetlist, facets, printall);
  FORALLfacet_(facetlist)
    qh_printafacet(qh, qh->ferr, qh_PRINTfacets, facet, printall);
  FOREACHfacet_(facets)
    qh_printafacet(qh, qh->ferr, qh_PRINTfacets, facet, printall);
  qh_printend(qh, qh->ferr, qh_PRINTfacets, facetlist, facets, printall);
} /* printfacetlist */


/*---------------------------------

  qh_printhelp_degenerate(qh, fp )
    prints descriptive message for precision error

  notes:
    no message if qh_QUICKhelp
*/
void qh_printhelp_degenerate(qhT *qh, FILE *fp) {

  if (qh->MERGEexact || qh->PREmerge || qh->JOGGLEmax < REALmax/2)
    qh_fprintf(qh, fp, 9368, "\n\
A Qhull error has occurred.  Qhull should have corrected the above\n\
precision error.  Please send the input and all of the output to\n\
qhull_bug@qhull.org\n");
  else if (!qh_QUICKhelp) {
    qh_fprintf(qh, fp, 9369, "\n\
Precision problems were detected during construction of the convex hull.\n\
This occurs because convex hull algorithms assume that calculations are\n\
exact, but floating-point arithmetic has roundoff errors.\n\
\n\
To correct for precision problems, do not use 'Q0'.  By default, Qhull\n\
selects 'C-0' or 'Qx' and merges non-convex facets.  With option 'QJ',\n\
Qhull joggles the input to prevent precision problems.  See \"Imprecision\n\
in Qhull\" (qh-impre.htm).\n\
\n\
If you use 'Q0', the output may include\n\
coplanar ridges, concave ridges, and flipped facets.  In 4-d and higher,\n\
Qhull may produce a ridge with four neighbors or two facets with the same \n\
vertices.  Qhull reports these events when they occur.  It stops when a\n\
concave ridge, flipped facet, or duplicate facet occurs.\n");
#if REALfloat
    qh_fprintf(qh, fp, 9370, "\
\n\
Qhull is currently using single precision arithmetic.  The following\n\
will probably remove the precision problems:\n\
  - recompile qhull for realT precision(#define REALfloat 0 in user.h).\n");
#endif
    if (qh->DELAUNAY && !qh->SCALElast && qh->MAXabs_coord > 1e4)
      qh_fprintf(qh, fp, 9371, "\
\n\
When computing the Delaunay triangulation of coordinates > 1.0,\n\
  - use 'Qbb' to scale the last coordinate to [0,m] (max previous coordinate)\n");
    if (qh->DELAUNAY && !qh->ATinfinity)
      qh_fprintf(qh, fp, 9372, "\
When computing the Delaunay triangulation:\n\
  - use 'Qz' to add a point at-infinity.  This reduces precision problems.\n");

    qh_fprintf(qh, fp, 9373, "\
\n\
If you need triangular output:\n\
  - use option 'Qt' to triangulate the output\n\
  - use option 'QJ' to joggle the input points and remove precision errors\n\
  - use option 'Ft'.  It triangulates non-simplicial facets with added points.\n\
\n\
If you must use 'Q0',\n\
try one or more of the following options.  They can not guarantee an output.\n\
  - use 'QbB' to scale the input to a cube.\n\
  - use 'Po' to produce output and prevent partitioning for flipped facets\n\
  - use 'V0' to set min. distance to visible facet as 0 instead of roundoff\n\
  - use 'En' to specify a maximum roundoff error less than %2.2g.\n\
  - options 'Qf', 'Qbb', and 'QR0' may also help\n",
               qh->DISTround);
    qh_fprintf(qh, fp, 9374, "\
\n\
To guarantee simplicial output:\n\
  - use option 'Qt' to triangulate the output\n\
  - use option 'QJ' to joggle the input points and remove precision errors\n\
  - use option 'Ft' to triangulate the output by adding points\n\
  - use exact arithmetic (see \"Imprecision in Qhull\", qh-impre.htm)\n\
");
  }
} /* printhelp_degenerate */


/*---------------------------------

  qh_printhelp_narrowhull(qh, minangle )
    Warn about a narrow hull

  notes:
    Alternatively, reduce qh_WARNnarrow in user.h

*/
void qh_printhelp_narrowhull(qhT *qh, FILE *fp, realT minangle) {

    qh_fprintf(qh, 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 */

/*---------------------------------

  qh_printhelp_singular(qh, fp )
    prints descriptive message for singular input
*/
void qh_printhelp_singular(qhT *qh, FILE *fp) {
  facetT *facet;
  vertexT *vertex, **vertexp;
  realT min, max, *coord, dist;
  int i,k;

  qh_fprintf(qh, fp, 9376, "\n\
The input to qhull appears to be less than %d dimensional, or a\n\
computation has overflowed.\n\n\
Qhull could not construct a clearly convex simplex from points:\n",
           qh->hull_dim);
  qh_printvertexlist(qh, fp, "", qh->facet_list, NULL, qh_ALL);
  if (!qh_QUICKhelp)
    qh_fprintf(qh, fp, 9377, "\n\
The center point is coplanar with a facet, or a vertex is coplanar\n\
with a neighboring facet.  The maximum round off error for\n\
computing distances is %2.2g.  The center point, facets and distances\n\
to the center point are as follows:\n\n", qh->DISTround);
  qh_printpointid(qh, fp, "center point", qh->hull_dim, qh->interior_point, qh_IDunknown);
  qh_fprintf(qh, fp, 9378, "\n");
  FORALLfacets {
    qh_fprintf(qh, fp, 9379, "facet");
    FOREACHvertex_(facet->vertices)
      qh_fprintf(qh, fp, 9380, " p%d", qh_pointid(qh, vertex->point));
    zinc_(Zdistio);
    qh_distplane(qh, qh->interior_point, facet, &dist);
    qh_fprintf(qh, fp, 9381, " distance= %4.2g\n", dist);
  }
  if (!qh_QUICKhelp) {
    if (qh->HALFspace)
      qh_fprintf(qh, fp, 9382, "\n\
These points are the dual of the given halfspaces.  They indicate that\n\
the intersection is degenerate.\n");
    qh_fprintf(qh, fp, 9383,"\n\
These points either have a maximum or minimum x-coordinate, or\n\
they maximize the determinant for k coordinates.  Trial points\n\
are first selected from points that maximize a coordinate.\n");
    if (qh->hull_dim >= qh_INITIALmax)
      qh_fprintf(qh, fp, 9384, "\n\
Because of the high dimension, the min x-coordinate and max-coordinate\n\
points are used if the determinant is non-zero.  Option 'Qs' will\n\
do a better, though much slower, job.  Instead of 'Qs', you can change\n\
the points by randomly rotating the input with 'QR0'.\n");
  }
  qh_fprintf(qh, fp, 9385, "\nThe min and max coordinates for each dimension are:\n");
  for (k=0; k < qh->hull_dim; k++) {
    min= REALmax;
    max= -REALmin;
    for (i=qh->num_points, coord= qh->first_point+k; i--; coord += qh->hull_dim) {
      maximize_(max, *coord);
      minimize_(min, *coord);
    }
    qh_fprintf(qh, fp, 9386, "  %d:  %8.4g  %8.4g  difference= %4.4g\n", k, min, max, max-min);
  }
  if (!qh_QUICKhelp) {
    qh_fprintf(qh, fp, 9387, "\n\
If the input should be full dimensional, you have several options that\n\
may determine an initial simplex:\n\
  - use 'QJ'  to joggle the input and make it full dimensional\n\
  - use 'QbB' to scale the points to the unit cube\n\
  - use 'QR0' to randomly rotate the input for different maximum points\n\
  - use 'Qs'  to search all points for the initial simplex\n\
  - use 'En'  to specify a maximum roundoff error less than %2.2g.\n\
  - trace execution with 'T3' to see the determinant for each point.\n",
                     qh->DISTround);
#if REALfloat
    qh_fprintf(qh, fp, 9388, "\
  - recompile qhull for realT precision(#define REALfloat 0 in libqhull_r.h).\n");
#endif
    qh_fprintf(qh, fp, 9389, "\n\
If the input is lower dimensional:\n\
  - use 'QJ' to joggle the input and make it full dimensional\n\
  - use 'Qbk:0Bk:0' to delete coordinate k from the input.  You should\n\
    pick the coordinate with the least range.  The hull will have the\n\
    correct topology.\n\
  - determine the flat containing the points, rotate the points\n\
    into a coordinate plane, and delete the other coordinates.\n\
  - add one or more points to make the input full dimensional.\n\
");
  }
} /* printhelp_singular */

/*---------------------------------

  qh_user_memsizes(qh)
    allocate up to 10 additional, quick allocation sizes

  notes:
    increase maximum number of allocations in qh_initqhull_mem()
*/
void qh_user_memsizes(qhT *qh) {

  QHULL_UNUSED(qh)
  /* qh_memsize(qh, size); */
} /* user_memsizes */


geometry/src/io_r.h0000644000176200001440000002074413432323614014005 0ustar  liggesusers/*
  ---------------------------------

   io_r.h
   declarations of Input/Output functions

   see README, libqhull_r.h and io_r.c

   Copyright (c) 1993-2015 The Geometry Center.
   $Id: //main/2015/qhull/src/libqhull_r/io_r.h#3 $$Change: 2079 $
   $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
*/

#ifndef qhDEFio
#define qhDEFio 1

#include "libqhull_r.h"

/*============ constants and flags ==================*/

/*----------------------------------

  qh_MAXfirst
    maximum length of first two lines of stdin
*/
#define qh_MAXfirst  200

/*----------------------------------

  qh_MINradius
    min radius for Gp and Gv, fraction of maxcoord
*/
#define qh_MINradius 0.02

/*----------------------------------

  qh_GEOMepsilon
    adjust outer planes for 'lines closer' and geomview roundoff.
    This prevents bleed through.
*/
#define qh_GEOMepsilon 2e-3

/*----------------------------------

  qh_WHITESPACE
    possible values of white space
*/
#define qh_WHITESPACE " \n\t\v\r\f"


/*----------------------------------

  qh_RIDGE
    to select which ridges to print in qh_eachvoronoi
*/
typedef enum
{
    qh_RIDGEall = 0, qh_RIDGEinner, qh_RIDGEouter
}
qh_RIDGE;

/*----------------------------------

  printvridgeT
    prints results of qh_printvdiagram

  see:
    qh_printvridge for an example
*/
typedef void (*printvridgeT)(qhT *qh, FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded);

/*============== -prototypes in alphabetical order =========*/

#ifdef __cplusplus
extern "C" {
#endif

void    qh_dfacet(qhT *qh, unsigned id);
void    qh_dvertex(qhT *qh, 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 useable since it depends on qh */
void    qh_copyfilename(qhT *qh, char *filename, int size, const char* source, int length);
void    qh_countfacets(qhT *qh, facetT *facetlist, setT *facets, boolT printall,
              int *numfacetsp, int *numsimplicialp, int *totneighborsp,
              int *numridgesp, int *numcoplanarsp, int *numnumtricoplanarsp);
pointT *qh_detvnorm(qhT *qh, vertexT *vertex, vertexT *vertexA, setT *centers, realT *offsetp);
setT   *qh_detvridge(qhT *qh, vertexT *vertex);
setT   *qh_detvridge3(qhT *qh, vertexT *atvertex, vertexT *vertex);
int     qh_eachvoronoi(qhT *qh, FILE *fp, printvridgeT printvridge, vertexT *atvertex, boolT visitall, qh_RIDGE innerouter, boolT inorder);
int     qh_eachvoronoi_all(qhT *qh, FILE *fp, printvridgeT printvridge, boolT isUpper, qh_RIDGE innerouter, boolT inorder);
void    qh_facet2point(qhT *qh, facetT *facet, pointT **point0, pointT **point1, realT *mindist);
setT   *qh_facetvertices(qhT *qh, facetT *facetlist, setT *facets, boolT allfacets);
void    qh_geomplanes(qhT *qh, facetT *facet, realT *outerplane, realT *innerplane);
void    qh_markkeep(qhT *qh, facetT *facetlist);
setT   *qh_markvoronoi(qhT *qh, facetT *facetlist, setT *facets, boolT printall, boolT *isLowerp, int *numcentersp);
void    qh_order_vertexneighbors(qhT *qh, vertexT *vertex);
void    qh_prepare_output(qhT *qh);
void    qh_printafacet(qhT *qh, FILE *fp, qh_PRINT format, facetT *facet, boolT printall);
void    qh_printbegin(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall);
void    qh_printcenter(qhT *qh, FILE *fp, qh_PRINT format, const char *string, facetT *facet);
void    qh_printcentrum(qhT *qh, FILE *fp, facetT *facet, realT radius);
void    qh_printend(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall);
void    qh_printend4geom(qhT *qh, FILE *fp, facetT *facet, int *num, boolT printall);
void    qh_printextremes(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall);
void    qh_printextremes_2d(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall);
void    qh_printextremes_d(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall);
void    qh_printfacet(qhT *qh, FILE *fp, facetT *facet);
void    qh_printfacet2math(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format, int notfirst);
void    qh_printfacet2geom(qhT *qh, FILE *fp, facetT *facet, realT color[3]);
void    qh_printfacet2geom_points(qhT *qh, FILE *fp, pointT *point1, pointT *point2,
                               facetT *facet, realT offset, realT color[3]);
void    qh_printfacet3math(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format, int notfirst);
void    qh_printfacet3geom_nonsimplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]);
void    qh_printfacet3geom_points(qhT *qh, FILE *fp, setT *points, facetT *facet, realT offset, realT color[3]);
void    qh_printfacet3geom_simplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]);
void    qh_printfacet3vertex(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format);
void    qh_printfacet4geom_nonsimplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]);
void    qh_printfacet4geom_simplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]);
void    qh_printfacetNvertex_nonsimplicial(qhT *qh, FILE *fp, facetT *facet, int id, qh_PRINT format);
void    qh_printfacetNvertex_simplicial(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format);
void    qh_printfacetheader(qhT *qh, FILE *fp, facetT *facet);
void    qh_printfacetridges(qhT *qh, FILE *fp, facetT *facet);
void    qh_printfacets(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall);
void    qh_printhyperplaneintersection(qhT *qh, FILE *fp, facetT *facet1, facetT *facet2,
                   setT *vertices, realT color[3]);
void    qh_printneighborhood(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetA, facetT *facetB, boolT printall);
void    qh_printline3geom(qhT *qh, FILE *fp, pointT *pointA, pointT *pointB, realT color[3]);
void    qh_printpoint(qhT *qh, FILE *fp, const char *string, pointT *point);
void    qh_printpointid(qhT *qh, FILE *fp, const char *string, int dim, pointT *point, int id);
void    qh_printpoint3(qhT *qh, FILE *fp, pointT *point);
void    qh_printpoints_out(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall);
void    qh_printpointvect(qhT *qh, FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius, realT color[3]);
void    qh_printpointvect2(qhT *qh, FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius);
void    qh_printridge(qhT *qh, FILE *fp, ridgeT *ridge);
void    qh_printspheres(qhT *qh, FILE *fp, setT *vertices, realT radius);
void    qh_printvdiagram(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall);
int     qh_printvdiagram2(qhT *qh, FILE *fp, printvridgeT printvridge, setT *vertices, qh_RIDGE innerouter, boolT inorder);
void    qh_printvertex(qhT *qh, FILE *fp, vertexT *vertex);
void    qh_printvertexlist(qhT *qh, FILE *fp, const char* string, facetT *facetlist,
                         setT *facets, boolT printall);
void    qh_printvertices(qhT *qh, FILE *fp, const char* string, setT *vertices);
void    qh_printvneighbors(qhT *qh, FILE *fp, facetT* facetlist, setT *facets, boolT printall);
void    qh_printvoronoi(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall);
void    qh_printvnorm(qhT *qh, FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded);
void    qh_printvridge(qhT *qh, FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded);
void    qh_produce_output(qhT *qh);
void    qh_produce_output2(qhT *qh);
void    qh_projectdim3(qhT *qh, pointT *source, pointT *destination);
int     qh_readfeasible(qhT *qh, int dim, const char *curline);
coordT *qh_readpoints(qhT *qh, int *numpoints, int *dimension, boolT *ismalloc);
void    qh_setfeasible(qhT *qh, int dim);
boolT   qh_skipfacet(qhT *qh, facetT *facet);
char   *qh_skipfilename(qhT *qh, char *filename);

#ifdef __cplusplus
} /* extern "C" */
#endif

#endif /* qhDEFio */
geometry/src/QuadTree.cpp0000644000176200001440000001376513527030173015127 0ustar  liggesusers/*
  This program is free software; you can redistribute it and/or modify it
  under the terms of the GNU General Public License as published by the
  Free Software Foundation; either version 3 of the License, or (at your
  option) any later version.
  This program is distributed in the hope that it will be useful, but WITHOUT
  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  for more details.
  You should have received a copy of the GNU General Public License
  along with this program. If not, see  .
*/

// Originally written for package lidR by Jean-Romain Roussel
// Author: Jean-Romain Roussel
// 3 may 2017: copy from package lidR to package geometry by Jean-Romain Roussel to operate in fast tsearch funtion

#include 
#include "QuadTree.h"
#include 
#include 

Point::Point(){}
Point::Point(const double x, const double y) : x(x), y(y), id(0) {}
Point::Point(const double x, const double y, const int id) : x(x), y(y), id(id) {}

BoundingBox::BoundingBox(){}
BoundingBox::BoundingBox(const Point center, const Point half_res) : center(center), half_res(half_res) {}

bool BoundingBox::contains(const Point& p, const double eps)
{
  if(p.x >= center.x - half_res.x - eps &&
     p.x <= center.x + half_res.x + eps &&
     p.y >= center.y - half_res.y - eps &&
     p.y <= center.y + half_res.y + eps)
    return true;
  else
    return false;
}

bool BoundingBox::intersects(const BoundingBox& b)
{

  if(center.x - half_res.x <= b.center.x + b.half_res.x &&
     center.x + half_res.x >= b.center.x - b.half_res.x &&
     center.y - half_res.y <= b.center.y + b.half_res.y &&
     center.y + half_res.y >= b.center.y - b.half_res.y)
  {
    return true;
  }
  else
    return false;
}

QuadTree::QuadTree(const BoundingBox boundary, const int parent_depth, const double eps)
{
  MAX_DEPTH = 6;
  EPSILON = eps;

  depth = parent_depth + 1;
  this->boundary = boundary;

  NE = 0;
  NW = 0;
  SE = 0;
  SW = 0;
}

QuadTree::~QuadTree()
{
  delete NE;
  delete NW;
  delete SE;
  delete SW;
}

QuadTree* QuadTree::create(const std::vector x, const std::vector y, const double eps = 1.0e-12)
{
  int n = x.size();

  double xmin = x[0];
  double ymin = y[0];
  double xmax = x[0];
  double ymax = y[0];

  for(int i = 0 ; i < n ; i++)
  {
    if(x[i] < xmin)
      xmin = x[i];
    else if(x[i] > xmax)
      xmax = x[i];

    if(y[i] < ymin)
      ymin = y[i];
    else if(y[i] > ymax)
      ymax = y[i];
  }

  double xrange = xmax - xmin;
  double yrange = ymax - ymin;
  double range = xrange > yrange ? xrange/2 : yrange/2;

  Point bcenter((xmin+xmax)/2, (ymin+ymax)/2);
  Point brange(range, range);
  BoundingBox bbox(bcenter, brange);

  QuadTree *tree = new QuadTree(bbox, 0, eps);

  for(int i = 0 ; i < n ; i++)
  {
    Point p(x[i], y[i], i);

    if (!tree->insert(p))
      error("Failed to insert point into QuadTree.\nPlease post input to tsearch  (or tsearchn at\nhttps://github.com/davidcsterratt/geometry/issues\nor email the maintainer.");
  }

  return tree;
}

bool QuadTree::insert(const Point& p)
{
  if(!boundary.contains(p, EPSILON))
    return false;

  if(depth == MAX_DEPTH)
  {
    points.push_back(p);
    return true;
  }

  if(NW == 0)
    subdivide();

  if(NW->insert(p))
    return true;
  if(NE->insert(p))
    return true;
  if(SW->insert(p))
    return true;
  if(SE->insert(p))
    return true;

  return false;
}

void QuadTree::subdivide()
{
  double half_res_half = boundary.half_res.x * 0.5;

  Point p(half_res_half, half_res_half);
  Point pNE(boundary.center.x + half_res_half, boundary.center.y + half_res_half);
  Point pNW(boundary.center.x - half_res_half, boundary.center.y + half_res_half);
  Point pSE(boundary.center.x + half_res_half, boundary.center.y - half_res_half);
  Point pSW(boundary.center.x - half_res_half, boundary.center.y - half_res_half);

  NE = new QuadTree(BoundingBox(pNE, p), depth, EPSILON);
  NW = new QuadTree(BoundingBox(pNW, p), depth, EPSILON);
  SE = new QuadTree(BoundingBox(pSE, p), depth, EPSILON);
  SW = new QuadTree(BoundingBox(pSW, p), depth, EPSILON);
}

void QuadTree::range_lookup(const BoundingBox bb, std::vector& res, const int method)
{
  if(!boundary.intersects(bb))
    return;

  if(depth == MAX_DEPTH)
  {
    switch(method)
    {
    case 1: getPointsSquare(bb, points, res);
      break;

    case 2: getPointsCircle(bb, points, res);
      break;
    }
  }

  if(NW == 0)
    return;

  NE->range_lookup(bb, res, method);
  NW->range_lookup(bb, res, method);
  SE->range_lookup(bb, res, method);
  SW->range_lookup(bb, res, method);

  return;
}

void QuadTree::rect_lookup(const double xc, const double yc, const double half_width, const double half_height, std::vector& res)
{
  range_lookup(BoundingBox(Point(xc, yc), Point(half_width, half_height)), res, 1);
  return;
}


void QuadTree::circle_lookup(const double cx, const double cy, const double range, std::vector& res)
{
  range_lookup(BoundingBox(Point(cx, cy), Point(range, range)), res, 2);
  return;
}

void QuadTree::getPointsSquare(const BoundingBox bb, std::vector& points, std::vector& res)
{
  for(std::vector::iterator it = points.begin(); it != points.end(); it++)
  {
    if(in_rect(bb, *it))
      res.push_back(&(*it));
  }
  return;
}

void QuadTree::getPointsCircle(const BoundingBox bb, std::vector& points, std::vector& res)
{
  for(std::vector::iterator it = points.begin(); it != points.end(); it++)
  {
    if(in_circle(bb.center, (*it), bb.half_res.x))
      res.push_back(&(*it));
  }
  return;
}

bool QuadTree::in_circle(const Point& p1, const Point& p2, const double r)
{
  double A = p1.x - p2.x;
  double B = p1.y - p2.y;
  double d = sqrt(A*A + B*B);

  return(d <= r);
}

bool QuadTree::in_rect(const BoundingBox& bb, const Point& p)
{
  double A = bb.center.x - p.x;
  double B = bb.center.y - p.y;
  A = A < 0 ? -A : A;
  B = B < 0 ? -B : B;

  return(A <= bb.half_res.x && B <= bb.half_res.y);
}
geometry/src/usermem_r.c0000644000176200001440000000544513432324054015046 0ustar  liggesusers/*
  ---------------------------------

   usermem_r.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_r.h for data structures, macros, and user-callable functions.
   See user_r.c for qhull-related, redefinable functions
   see user_r.h for user-definable constants
   See userprintf_r.c for qh_fprintf and userprintf_rbox_r.c for qh_fprintf_rbox

   Please report any errors that you fix to qhull@qhull.org
*/

#include "libqhull_r.h"

#include 
#include 

/*---------------------------------

  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) {
    /* CHANGE TO SOURCE: The commented line below is the original. It
     needs to be replaced to fix warnings about exit being called. --
     David Sterratt 3/4/12. */
    /* exit(exitcode); */
    error("Qhull exit, code %i", exitcode);
} /* exit */

/*---------------------------------

  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_r.c] for normal printing
*/
void qh_fprintf_stderr(int msgcode, const char *fmt, ... ) {
    va_list args;

    va_start(args, fmt);
    if(msgcode)
      /* CHANGE TO SOURCE */
      /* fprintf(stderr, "QH%.4d ", msgcode); */
      REprintf("QH%.4d", msgcode);
    /* CHANGE TO SOURCE */
    /* vfprintf(stderr, fmt, args); */
    REvprintf(fmt, args);
    va_end(args);
} /* fprintf_stderr */

/*---------------------------------

  qh_free(qhT *qh, mem )
    free memory

  notes:
    same as free()
    No calls to qh_errexit() 
*/
void qh_free(void *mem) {
    free(mem);
} /* free */

/*---------------------------------

    qh_malloc( mem )
      allocate memory

    notes:
      same as malloc()
*/
void *qh_malloc(size_t size) {
    return malloc(size);
} /* malloc */


geometry/src/io_r.c0000644000176200001440000041624313432323610013777 0ustar  liggesusers/*
  ---------------------------------

   io_r.c
   Input/Output routines of qhull application

   see qh-io_r.htm and io_r.h

   see user_r.c for qh_errprint and qh_printfacetlist

   unix_r.c calls qh_readpoints and qh_produce_output

   unix_r.c and user_r.c are the only callers of io_r.c functions
   This allows the user to avoid loading io_r.o from qhull.a

   Copyright (c) 1993-2015 The Geometry Center.
   $Id: //main/2015/qhull/src/libqhull_r/io_r.c#4 $$Change: 2064 $
   $DateTime: 2016/01/18 12:36:08 $$Author: bbarber $
*/

#include "qhull_ra.h"

/*========= -functions in alphabetical order after qh_produce_output(qh)  =====*/

/*---------------------------------

  qh_produce_output(qh)
  qh_produce_output2(qh)
    prints out the result of qhull in desired format
    qh_produce_output2(qh) does not call qh_prepare_output(qh)
    if qh.GETarea
      computes and prints area and volume
    qh.PRINTout[] is an array of output formats

  notes:
    prints output in qh.PRINTout order
*/
void qh_produce_output(qhT *qh) {
    int tempsize= qh_setsize(qh, qh->qhmem.tempstack);

    qh_prepare_output(qh);
    qh_produce_output2(qh);
    if (qh_setsize(qh, qh->qhmem.tempstack) != tempsize) {
        qh_fprintf(qh, qh->ferr, 6206, "qhull internal error (qh_produce_output): temporary sets not empty(%d)\n",
            qh_setsize(qh, qh->qhmem.tempstack));
        qh_errexit(qh, qh_ERRqhull, NULL, NULL);
    }
} /* produce_output */


void qh_produce_output2(qhT *qh) {
  int i, tempsize= qh_setsize(qh, qh->qhmem.tempstack), d_1;

  if (qh->PRINTsummary)
    qh_printsummary(qh, qh->ferr);
  else if (qh->PRINTout[0] == qh_PRINTnone)
    qh_printsummary(qh, qh->fout);
  for (i=0; i < qh_PRINTEND; i++)
    qh_printfacets(qh, qh->fout, qh->PRINTout[i], qh->facet_list, NULL, !qh_ALL);
  qh_allstatistics(qh);
  if (qh->PRINTprecision && !qh->MERGING && (qh->JOGGLEmax > REALmax/2 || qh->RERUN))
    qh_printstats(qh, qh->ferr, qh->qhstat.precision, NULL);
  if (qh->VERIFYoutput && (zzval_(Zridge) > 0 || zzval_(Zridgemid) > 0))
    qh_printstats(qh, qh->ferr, qh->qhstat.vridges, NULL);
  if (qh->PRINTstatistics) {
    qh_printstatistics(qh, qh->ferr, "");
    qh_memstatistics(qh, qh->ferr);
    d_1= sizeof(setT) + (qh->hull_dim - 1) * SETelemsize;
    qh_fprintf(qh, qh->ferr, 8040, "\
    size in bytes: merge %d ridge %d vertex %d facet %d\n\
         normal %d ridge vertices %d facet vertices or neighbors %d\n",
            (int)sizeof(mergeT), (int)sizeof(ridgeT),
            (int)sizeof(vertexT), (int)sizeof(facetT),
            qh->normal_size, d_1, d_1 + SETelemsize);
  }
  if (qh_setsize(qh, qh->qhmem.tempstack) != tempsize) {
    qh_fprintf(qh, qh->ferr, 6065, "qhull internal error (qh_produce_output2): temporary sets not empty(%d)\n",
             qh_setsize(qh, qh->qhmem.tempstack));
    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
  }
} /* produce_output2 */

/*---------------------------------

  qh_dfacet(qh, id )
    print facet by id, for debugging

*/
void qh_dfacet(qhT *qh, unsigned id) {
  facetT *facet;

  FORALLfacets {
    if (facet->id == id) {
      qh_printfacet(qh, qh->fout, facet);
      break;
    }
  }
} /* dfacet */


/*---------------------------------

  qh_dvertex(qh, id )
    print vertex by id, for debugging
*/
void qh_dvertex(qhT *qh, unsigned id) {
  vertexT *vertex;

  FORALLvertices {
    if (vertex->id == id) {
      qh_printvertex(qh, qh->fout, vertex);
      break;
    }
  }
} /* dvertex */


/*---------------------------------

  qh_compare_facetarea(p1, p2 )
    used by qsort() to order facets by area
*/
int qh_compare_facetarea(const void *p1, const void *p2) {
  const facetT *a= *((facetT *const*)p1), *b= *((facetT *const*)p2);

  if (!a->isarea)
    return -1;
  if (!b->isarea)
    return 1;
  if (a->f.area > b->f.area)
    return 1;
  else if (a->f.area == b->f.area)
    return 0;
  return -1;
} /* compare_facetarea */

/*---------------------------------

  qh_compare_facetmerge(p1, p2 )
    used by qsort() to order facets by number of merges
*/
int qh_compare_facetmerge(const void *p1, const void *p2) {
  const facetT *a= *((facetT *const*)p1), *b= *((facetT *const*)p2);

  return(a->nummerge - b->nummerge);
} /* compare_facetvisit */

/*---------------------------------

  qh_compare_facetvisit(p1, p2 )
    used by qsort() to order facets by visit id or id
*/
int qh_compare_facetvisit(const void *p1, const void *p2) {
  const facetT *a= *((facetT *const*)p1), *b= *((facetT *const*)p2);
  int i,j;

  if (!(i= a->visitid))
    i= 0 - a->id; /* do not convert to int, sign distinguishes id from visitid */
  if (!(j= b->visitid))
    j= 0 - b->id;
  return(i - j);
} /* compare_facetvisit */

/*---------------------------------

  qh_compare_vertexpoint( p1, p2 )
    used by qsort() to order vertices by point id

  Not usable in qhulllib_r 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(qh, a->point) > qh_pointid(qh, b->point)?1:-1));
}*/

/*---------------------------------

  qh_copyfilename(qh, dest, size, source, length )
    copy filename identified by qh_skipfilename()

  notes:
    see qh_skipfilename() for syntax
*/
void qh_copyfilename(qhT *qh, char *filename, int size, const char* source, int length) {
  char c= *source;

  if (length > size + 1) {
      qh_fprintf(qh, qh->ferr, 6040, "qhull error: filename is more than %d characters, %s\n",  size-1, source);
      qh_errexit(qh, qh_ERRinput, NULL, NULL);
  }
  strncpy(filename, source, length);
  filename[length]= '\0';
  if (c == '\'' || c == '"') {
    char *s= filename + 1;
    char *t= filename;
    while (*s) {
      if (*s == c) {
          if (s[-1] == '\\')
              t[-1]= c;
      }else
          *t++= *s;
      s++;
    }
    *t= '\0';
  }
} /* copyfilename */

/*---------------------------------

  qh_countfacets(qh, facetlist, facets, printall,
          numfacets, numsimplicial, totneighbors, numridges, numcoplanar, numtricoplanars  )
    count good facets for printing and set visitid
    if allfacets, ignores qh_skipfacet()

  notes:
    qh_printsummary and qh_countfacets must match counts

  returns:
    numfacets, numsimplicial, total neighbors, numridges, coplanars
    each facet with ->visitid indicating 1-relative position
      ->visitid==0 indicates not good

  notes
    numfacets >= numsimplicial
    if qh.NEWfacets,
      does not count visible facets (matches qh_printafacet)

  design:
    for all facets on facetlist and in facets set
      unless facet is skipped or visible (i.e., will be deleted)
        mark facet->visitid
        update counts
*/
void qh_countfacets(qhT *qh, facetT *facetlist, setT *facets, boolT printall,
    int *numfacetsp, int *numsimplicialp, int *totneighborsp, int *numridgesp, int *numcoplanarsp, int *numtricoplanarsp) {
  facetT *facet, **facetp;
  int numfacets= 0, numsimplicial= 0, numridges= 0, totneighbors= 0, numcoplanars= 0, numtricoplanars= 0;

  FORALLfacet_(facetlist) {
    if ((facet->visible && qh->NEWfacets)
    || (!printall && qh_skipfacet(qh, facet)))
      facet->visitid= 0;
    else {
      facet->visitid= ++numfacets;
      totneighbors += qh_setsize(qh, facet->neighbors);
      if (facet->simplicial) {
        numsimplicial++;
        if (facet->keepcentrum && facet->tricoplanar)
          numtricoplanars++;
      }else
        numridges += qh_setsize(qh, facet->ridges);
      if (facet->coplanarset)
        numcoplanars += qh_setsize(qh, facet->coplanarset);
    }
  }

  FOREACHfacet_(facets) {
    if ((facet->visible && qh->NEWfacets)
    || (!printall && qh_skipfacet(qh, facet)))
      facet->visitid= 0;
    else {
      facet->visitid= ++numfacets;
      totneighbors += qh_setsize(qh, facet->neighbors);
      if (facet->simplicial){
        numsimplicial++;
        if (facet->keepcentrum && facet->tricoplanar)
          numtricoplanars++;
      }else
        numridges += qh_setsize(qh, facet->ridges);
      if (facet->coplanarset)
        numcoplanars += qh_setsize(qh, facet->coplanarset);
    }
  }
  qh->visit_id += numfacets+1;
  *numfacetsp= numfacets;
  *numsimplicialp= numsimplicial;
  *totneighborsp= totneighbors;
  *numridgesp= numridges;
  *numcoplanarsp= numcoplanars;
  *numtricoplanarsp= numtricoplanars;
} /* countfacets */

/*---------------------------------

  qh_detvnorm(qh, vertex, vertexA, centers, offset )
    compute separating plane of the Voronoi diagram for a pair of input sites
    centers= set of facets (i.e., Voronoi vertices)
      facet->visitid= 0 iff vertex-at-infinity (i.e., unbounded)

  assumes:
    qh_ASvoronoi and qh_vertexneighbors() already set

  returns:
    norm
      a pointer into qh.gm_matrix to qh.hull_dim-1 reals
      copy the data before reusing qh.gm_matrix
    offset
      if 'QVn'
        sign adjusted so that qh.GOODvertexp is inside
      else
        sign adjusted so that vertex is inside

    qh.gm_matrix= simplex of points from centers relative to first center

  notes:
    in io_r.c so that code for 'v Tv' can be removed by removing io_r.c
    returns pointer into qh.gm_matrix to avoid tracking of temporary memory

  design:
    determine midpoint of input sites
    build points as the set of Voronoi vertices
    select a simplex from points (if necessary)
      include midpoint if the Voronoi region is unbounded
    relocate the first vertex of the simplex to the origin
    compute the normalized hyperplane through the simplex
    orient the hyperplane toward 'QVn' or 'vertex'
    if 'Tv' or 'Ts'
      if bounded
        test that hyperplane is the perpendicular bisector of the input sites
      test that Voronoi vertices not in the simplex are still on the hyperplane
    free up temporary memory
*/
pointT *qh_detvnorm(qhT *qh, vertexT *vertex, vertexT *vertexA, setT *centers, realT *offsetp) {
  facetT *facet, **facetp;
  int  i, k, pointid, pointidA, point_i, point_n;
  setT *simplex= NULL;
  pointT *point, **pointp, *point0, *midpoint, *normal, *inpoint;
  coordT *coord, *gmcoord, *normalp;
  setT *points= qh_settemp(qh, qh->TEMPsize);
  boolT nearzero= False;
  boolT unbounded= False;
  int numcenters= 0;
  int dim= qh->hull_dim - 1;
  realT dist, offset, angle, zero= 0.0;

  midpoint= qh->gm_matrix + qh->hull_dim * qh->hull_dim;  /* last row */
  for (k=0; k < dim; k++)
    midpoint[k]= (vertex->point[k] + vertexA->point[k])/2;
  FOREACHfacet_(centers) {
    numcenters++;
    if (!facet->visitid)
      unbounded= True;
    else {
      if (!facet->center)
        facet->center= qh_facetcenter(qh, facet->vertices);
      qh_setappend(qh, &points, facet->center);
    }
  }
  if (numcenters > dim) {
    simplex= qh_settemp(qh, qh->TEMPsize);
    qh_setappend(qh, &simplex, vertex->point);
    if (unbounded)
      qh_setappend(qh, &simplex, midpoint);
    qh_maxsimplex(qh, dim, points, NULL, 0, &simplex);
    qh_setdelnth(qh, simplex, 0);
  }else if (numcenters == dim) {
    if (unbounded)
      qh_setappend(qh, &points, midpoint);
    simplex= points;
  }else {
    qh_fprintf(qh, qh->ferr, 6216, "qhull internal error (qh_detvnorm): too few points(%d) to compute separating plane\n", numcenters);
    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
  }
  i= 0;
  gmcoord= qh->gm_matrix;
  point0= SETfirstt_(simplex, pointT);
  FOREACHpoint_(simplex) {
    if (qh->IStracing >= 4)
      qh_printmatrix(qh, qh->ferr, "qh_detvnorm: Voronoi vertex or midpoint",
                              &point, 1, dim);
    if (point != point0) {
      qh->gm_row[i++]= gmcoord;
      coord= point0;
      for (k=dim; k--; )
        *(gmcoord++)= *point++ - *coord++;
    }
  }
  qh->gm_row[i]= gmcoord;  /* does not overlap midpoint, may be used later for qh_areasimplex */
  normal= gmcoord;
  qh_sethyperplane_gauss(qh, dim, qh->gm_row, point0, True,
                normal, &offset, &nearzero);
  if (qh->GOODvertexp == vertexA->point)
    inpoint= vertexA->point;
  else
    inpoint= vertex->point;
  zinc_(Zdistio);
  dist= qh_distnorm(dim, inpoint, normal, &offset);
  if (dist > 0) {
    offset= -offset;
    normalp= normal;
    for (k=dim; k--; ) {
      *normalp= -(*normalp);
      normalp++;
    }
  }
  if (qh->VERIFYoutput || qh->PRINTstatistics) {
    pointid= qh_pointid(qh, vertex->point);
    pointidA= qh_pointid(qh, vertexA->point);
    if (!unbounded) {
      zinc_(Zdiststat);
      dist= qh_distnorm(dim, midpoint, normal, &offset);
      if (dist < 0)
        dist= -dist;
      zzinc_(Zridgemid);
      wwmax_(Wridgemidmax, dist);
      wwadd_(Wridgemid, dist);
      trace4((qh, qh->ferr, 4014, "qh_detvnorm: points %d %d midpoint dist %2.2g\n",
                 pointid, pointidA, dist));
      for (k=0; k < dim; k++)
        midpoint[k]= vertexA->point[k] - vertex->point[k];  /* overwrites midpoint! */
      qh_normalize(qh, midpoint, dim, False);
      angle= qh_distnorm(dim, midpoint, normal, &zero); /* qh_detangle uses dim+1 */
      if (angle < 0.0)
        angle= angle + 1.0;
      else
        angle= angle - 1.0;
      if (angle < 0.0)
        angle= -angle;
      trace4((qh, qh->ferr, 4015, "qh_detvnorm: points %d %d angle %2.2g nearzero %d\n",
                 pointid, pointidA, angle, nearzero));
      if (nearzero) {
        zzinc_(Zridge0);
        wwmax_(Wridge0max, angle);
        wwadd_(Wridge0, angle);
      }else {
        zzinc_(Zridgeok)
        wwmax_(Wridgeokmax, angle);
        wwadd_(Wridgeok, angle);
      }
    }
    if (simplex != points) {
      FOREACHpoint_i_(qh, points) {
        if (!qh_setin(simplex, point)) {
          facet= SETelemt_(centers, point_i, facetT);
          zinc_(Zdiststat);
          dist= qh_distnorm(dim, point, normal, &offset);
          if (dist < 0)
            dist= -dist;
          zzinc_(Zridge);
          wwmax_(Wridgemax, dist);
          wwadd_(Wridge, dist);
          trace4((qh, qh->ferr, 4016, "qh_detvnorm: points %d %d Voronoi vertex %d dist %2.2g\n",
                             pointid, pointidA, facet->visitid, dist));
        }
      }
    }
  }
  *offsetp= offset;
  if (simplex != points)
    qh_settempfree(qh, &simplex);
  qh_settempfree(qh, &points);
  return normal;
} /* detvnorm */

/*---------------------------------

  qh_detvridge(qh, vertexA )
    determine Voronoi ridge from 'seen' neighbors of vertexA
    include one vertex-at-infinite if an !neighbor->visitid

  returns:
    temporary set of centers (facets, i.e., Voronoi vertices)
    sorted by center id
*/
setT *qh_detvridge(qhT *qh, vertexT *vertex) {
  setT *centers= qh_settemp(qh, qh->TEMPsize);
  setT *tricenters= qh_settemp(qh, qh->TEMPsize);
  facetT *neighbor, **neighborp;
  boolT firstinf= True;

  FOREACHneighbor_(vertex) {
    if (neighbor->seen) {
      if (neighbor->visitid) {
        if (!neighbor->tricoplanar || qh_setunique(qh, &tricenters, neighbor->center))
          qh_setappend(qh, ¢ers, neighbor);
      }else if (firstinf) {
        firstinf= False;
        qh_setappend(qh, ¢ers, neighbor);
      }
    }
  }
  qsort(SETaddr_(centers, facetT), (size_t)qh_setsize(qh, centers),
             sizeof(facetT *), qh_compare_facetvisit);
  qh_settempfree(qh, &tricenters);
  return centers;
} /* detvridge */

/*---------------------------------

  qh_detvridge3(qh, atvertex, vertex )
    determine 3-d Voronoi ridge from 'seen' neighbors of atvertex and vertex
    include one vertex-at-infinite for !neighbor->visitid
    assumes all facet->seen2= True

  returns:
    temporary set of centers (facets, i.e., Voronoi vertices)
    listed in adjacency order (!oriented)
    all facet->seen2= True

  design:
    mark all neighbors of atvertex
    for each adjacent neighbor of both atvertex and vertex
      if neighbor selected
        add neighbor to set of Voronoi vertices
*/
setT *qh_detvridge3(qhT *qh, vertexT *atvertex, vertexT *vertex) {
  setT *centers= qh_settemp(qh, qh->TEMPsize);
  setT *tricenters= qh_settemp(qh, qh->TEMPsize);
  facetT *neighbor, **neighborp, *facet= NULL;
  boolT firstinf= True;

  FOREACHneighbor_(atvertex)
    neighbor->seen2= False;
  FOREACHneighbor_(vertex) {
    if (!neighbor->seen2) {
      facet= neighbor;
      break;
    }
  }
  while (facet) {
    facet->seen2= True;
    if (neighbor->seen) {
      if (facet->visitid) {
        if (!facet->tricoplanar || qh_setunique(qh, &tricenters, facet->center))
          qh_setappend(qh, ¢ers, facet);
      }else if (firstinf) {
        firstinf= False;
        qh_setappend(qh, ¢ers, facet);
      }
    }
    FOREACHneighbor_(facet) {
      if (!neighbor->seen2) {
        if (qh_setin(vertex->neighbors, neighbor))
          break;
        else
          neighbor->seen2= True;
      }
    }
    facet= neighbor;
  }
  if (qh->CHECKfrequently) {
    FOREACHneighbor_(vertex) {
      if (!neighbor->seen2) {
          qh_fprintf(qh, qh->ferr, 6217, "qhull internal error (qh_detvridge3): neighbors of vertex p%d are not connected at facet %d\n",
                 qh_pointid(qh, vertex->point), neighbor->id);
        qh_errexit(qh, qh_ERRqhull, neighbor, NULL);
      }
    }
  }
  FOREACHneighbor_(atvertex)
    neighbor->seen2= True;
  qh_settempfree(qh, &tricenters);
  return centers;
} /* detvridge3 */

/*---------------------------------

  qh_eachvoronoi(qh, fp, printvridge, vertex, visitall, innerouter, inorder )
    if visitall,
      visit all Voronoi ridges for vertex (i.e., an input site)
    else
      visit all unvisited Voronoi ridges for vertex
      all vertex->seen= False if unvisited
    assumes
      all facet->seen= False
      all facet->seen2= True (for qh_detvridge3)
      all facet->visitid == 0 if vertex_at_infinity
                         == index of Voronoi vertex
                         >= qh.num_facets if ignored
    innerouter:
      qh_RIDGEall--  both inner (bounded) and outer(unbounded) ridges
      qh_RIDGEinner- only inner
      qh_RIDGEouter- only outer

    if inorder
      orders vertices for 3-d Voronoi diagrams

  returns:
    number of visited ridges (does not include previously visited ridges)

    if printvridge,
      calls printvridge( fp, vertex, vertexA, centers)
        fp== any pointer (assumes FILE*)
        vertex,vertexA= pair of input sites that define a Voronoi ridge
        centers= set of facets (i.e., Voronoi vertices)
                 ->visitid == index or 0 if vertex_at_infinity
                 ordered for 3-d Voronoi diagram
  notes:
    uses qh.vertex_visit

  see:
    qh_eachvoronoi_all()

  design:
    mark selected neighbors of atvertex
    for each selected neighbor (either Voronoi vertex or vertex-at-infinity)
      for each unvisited vertex
        if atvertex and vertex share more than d-1 neighbors
          bump totalcount
          if printvridge defined
            build the set of shared neighbors (i.e., Voronoi vertices)
            call printvridge
*/
int qh_eachvoronoi(qhT *qh, FILE *fp, printvridgeT printvridge, vertexT *atvertex, boolT visitall, qh_RIDGE innerouter, boolT inorder) {
  boolT unbounded;
  int count;
  facetT *neighbor, **neighborp, *neighborA, **neighborAp;
  setT *centers;
  setT *tricenters= qh_settemp(qh, qh->TEMPsize);

  vertexT *vertex, **vertexp;
  boolT firstinf;
  unsigned int numfacets= (unsigned int)qh->num_facets;
  int totridges= 0;

  qh->vertex_visit++;
  atvertex->seen= True;
  if (visitall) {
    FORALLvertices
      vertex->seen= False;
  }
  FOREACHneighbor_(atvertex) {
    if (neighbor->visitid < numfacets)
      neighbor->seen= True;
  }
  FOREACHneighbor_(atvertex) {
    if (neighbor->seen) {
      FOREACHvertex_(neighbor->vertices) {
        if (vertex->visitid != qh->vertex_visit && !vertex->seen) {
          vertex->visitid= qh->vertex_visit;
          count= 0;
          firstinf= True;
          qh_settruncate(qh, tricenters, 0);
          FOREACHneighborA_(vertex) {
            if (neighborA->seen) {
              if (neighborA->visitid) {
                if (!neighborA->tricoplanar || qh_setunique(qh, &tricenters, neighborA->center))
                  count++;
              }else if (firstinf) {
                count++;
                firstinf= False;
              }
            }
          }
          if (count >= qh->hull_dim - 1) {  /* e.g., 3 for 3-d Voronoi */
            if (firstinf) {
              if (innerouter == qh_RIDGEouter)
                continue;
              unbounded= False;
            }else {
              if (innerouter == qh_RIDGEinner)
                continue;
              unbounded= True;
            }
            totridges++;
            trace4((qh, qh->ferr, 4017, "qh_eachvoronoi: Voronoi ridge of %d vertices between sites %d and %d\n",
                  count, qh_pointid(qh, atvertex->point), qh_pointid(qh, vertex->point)));
            if (printvridge && fp) {
              if (inorder && qh->hull_dim == 3+1) /* 3-d Voronoi diagram */
                centers= qh_detvridge3(qh, atvertex, vertex);
              else
                centers= qh_detvridge(qh, vertex);
              (*printvridge)(qh, fp, atvertex, vertex, centers, unbounded);
              qh_settempfree(qh, ¢ers);
            }
          }
        }
      }
    }
  }
  FOREACHneighbor_(atvertex)
    neighbor->seen= False;
  qh_settempfree(qh, &tricenters);
  return totridges;
} /* eachvoronoi */


/*---------------------------------

  qh_eachvoronoi_all(qh, fp, printvridge, isUpper, innerouter, inorder )
    visit all Voronoi ridges

    innerouter:
      see qh_eachvoronoi()

    if inorder
      orders vertices for 3-d Voronoi diagrams

  returns
    total number of ridges

    if isUpper == facet->upperdelaunay  (i.e., a Vornoi vertex)
      facet->visitid= Voronoi vertex index(same as 'o' format)
    else
      facet->visitid= 0

    if printvridge,
      calls printvridge( fp, vertex, vertexA, centers)
      [see qh_eachvoronoi]

  notes:
    Not used for qhull.exe
    same effect as qh_printvdiagram but ridges not sorted by point id
*/
int qh_eachvoronoi_all(qhT *qh, FILE *fp, printvridgeT printvridge, boolT isUpper, qh_RIDGE innerouter, boolT inorder) {
  facetT *facet;
  vertexT *vertex;
  int numcenters= 1;  /* vertex 0 is vertex-at-infinity */
  int totridges= 0;

  qh_clearcenters(qh, qh_ASvoronoi);
  qh_vertexneighbors(qh);
  maximize_(qh->visit_id, (unsigned) 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(qh, vertex->point)+1 != qh->GOODvertex)
      continue;
    totridges += qh_eachvoronoi(qh, fp, printvridge, vertex,
                   !qh_ALL, innerouter, inorder);
  }
  return totridges;
} /* eachvoronoi_all */

/*---------------------------------

  qh_facet2point(qh, facet, point0, point1, mindist )
    return two projected temporary vertices for a 2-d facet
    may be non-simplicial

  returns:
    point0 and point1 oriented and projected to the facet
    returns mindist (maximum distance below plane)
*/
void qh_facet2point(qhT *qh, facetT *facet, pointT **point0, pointT **point1, realT *mindist) {
  vertexT *vertex0, *vertex1;
  realT dist;

  if (facet->toporient ^ qh_ORIENTclock) {
    vertex0= SETfirstt_(facet->vertices, vertexT);
    vertex1= SETsecondt_(facet->vertices, vertexT);
  }else {
    vertex1= SETfirstt_(facet->vertices, vertexT);
    vertex0= SETsecondt_(facet->vertices, vertexT);
  }
  zadd_(Zdistio, 2);
  qh_distplane(qh, vertex0->point, facet, &dist);
  *mindist= dist;
  *point0= qh_projectpoint(qh, vertex0->point, facet, dist);
  qh_distplane(qh, vertex1->point, facet, &dist);
  minimize_(*mindist, dist);
  *point1= qh_projectpoint(qh, vertex1->point, facet, dist);
} /* facet2point */


/*---------------------------------

  qh_facetvertices(qh, facetlist, facets, allfacets )
    returns temporary set of vertices in a set and/or list of facets
    if allfacets, ignores qh_skipfacet()

  returns:
    vertices with qh.vertex_visit

  notes:
    optimized for allfacets of facet_list

  design:
    if allfacets of facet_list
      create vertex set from vertex_list
    else
      for each selected facet in facets or facetlist
        append unvisited vertices to vertex set
*/
setT *qh_facetvertices(qhT *qh, facetT *facetlist, setT *facets, boolT allfacets) {
  setT *vertices;
  facetT *facet, **facetp;
  vertexT *vertex, **vertexp;

  qh->vertex_visit++;
  if (facetlist == qh->facet_list && allfacets && !facets) {
    vertices= qh_settemp(qh, qh->num_vertices);
    FORALLvertices {
      vertex->visitid= qh->vertex_visit;
      qh_setappend(qh, &vertices, vertex);
    }
  }else {
    vertices= qh_settemp(qh, qh->TEMPsize);
    FORALLfacet_(facetlist) {
      if (!allfacets && qh_skipfacet(qh, facet))
        continue;
      FOREACHvertex_(facet->vertices) {
        if (vertex->visitid != qh->vertex_visit) {
          vertex->visitid= qh->vertex_visit;
          qh_setappend(qh, &vertices, vertex);
        }
      }
    }
  }
  FOREACHfacet_(facets) {
    if (!allfacets && qh_skipfacet(qh, facet))
      continue;
    FOREACHvertex_(facet->vertices) {
      if (vertex->visitid != qh->vertex_visit) {
        vertex->visitid= qh->vertex_visit;
        qh_setappend(qh, &vertices, vertex);
      }
    }
  }
  return vertices;
} /* facetvertices */

/*---------------------------------

  qh_geomplanes(qh, facet, outerplane, innerplane )
    return outer and inner planes for Geomview
    qh.PRINTradius is size of vertices and points (includes qh.JOGGLEmax)

  notes:
    assume precise calculations in io.c with roundoff covered by qh_GEOMepsilon
*/
void qh_geomplanes(qhT *qh, facetT *facet, realT *outerplane, realT *innerplane) {
  realT radius;

  if (qh->MERGING || qh->JOGGLEmax < REALmax/2) {
    qh_outerinner(qh, facet, outerplane, innerplane);
    radius= qh->PRINTradius;
    if (qh->JOGGLEmax < REALmax/2)
      radius -= qh->JOGGLEmax * sqrt((realT)qh->hull_dim);  /* already accounted for in qh_outerinner() */
    *outerplane += radius;
    *innerplane -= radius;
    if (qh->PRINTcoplanar || qh->PRINTspheres) {
      *outerplane += qh->MAXabs_coord * qh_GEOMepsilon;
      *innerplane -= qh->MAXabs_coord * qh_GEOMepsilon;
    }
  }else
    *innerplane= *outerplane= 0;
} /* geomplanes */


/*---------------------------------

  qh_markkeep(qh, 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(qhT *qh, facetT *facetlist) {
  facetT *facet, **facetp;
  setT *facets= qh_settemp(qh, qh->num_facets);
  int size, count;

  trace2((qh, qh->ferr, 2006, "qh_markkeep: only keep %d largest and/or %d most merged facets and/or min area %.2g\n",
          qh->KEEParea, qh->KEEPmerge, qh->KEEPminArea));
  FORALLfacet_(facetlist) {
    if (!facet->visible && facet->good)
      qh_setappend(qh, &facets, facet);
  }
  size= qh_setsize(qh, facets);
  if (qh->KEEParea) {
    qsort(SETaddr_(facets, facetT), (size_t)size,
             sizeof(facetT *), qh_compare_facetarea);
    if ((count= size - qh->KEEParea) > 0) {
      FOREACHfacet_(facets) {
        facet->good= False;
        if (--count == 0)
          break;
      }
    }
  }
  if (qh->KEEPmerge) {
    qsort(SETaddr_(facets, facetT), (size_t)size,
             sizeof(facetT *), qh_compare_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(qh, &facets);
  count= 0;
  FORALLfacet_(facetlist) {
    if (facet->good)
      count++;
  }
  qh->num_good= count;
} /* markkeep */


/*---------------------------------

  qh_markvoronoi(qh, facetlist, facets, printall, isLower, numcenters )
    mark voronoi vertices for printing by site pairs

  returns:
    temporary set of vertices indexed by pointid
    isLower set if printing lower hull (i.e., at least one facet is lower hull)
    numcenters= total number of Voronoi vertices
    bumps qh.printoutnum for vertex-at-infinity
    clears all facet->seen and sets facet->seen2

    if selected
      facet->visitid= Voronoi vertex id
    else if upper hull (or 'Qu' and lower hull)
      facet->visitid= 0
    else
      facet->visitid >= qh->num_facets

  notes:
    ignores qh.ATinfinity, if defined
*/
setT *qh_markvoronoi(qhT *qh, facetT *facetlist, setT *facets, boolT printall, boolT *isLowerp, int *numcentersp) {
  int numcenters=0;
  facetT *facet, **facetp;
  setT *vertices;
  boolT isLower= False;

  qh->printoutnum++;
  qh_clearcenters(qh, qh_ASvoronoi);  /* in case, qh_printvdiagram2 called by user */
  qh_vertexneighbors(qh);
  vertices= qh_pointvertex(qh);
  if (qh->ATinfinity)
    SETelem_(vertices, qh->num_points-1)= NULL;
  qh->visit_id++;
  maximize_(qh->visit_id, (unsigned) qh->num_facets);
  FORALLfacet_(facetlist) {
    if (printall || !qh_skipfacet(qh, facet)) {
      if (!facet->upperdelaunay) {
        isLower= True;
        break;
      }
    }
  }
  FOREACHfacet_(facets) {
    if (printall || !qh_skipfacet(qh, facet)) {
      if (!facet->upperdelaunay) {
        isLower= True;
        break;
      }
    }
  }
  FORALLfacets {
    if (facet->normal && (facet->upperdelaunay == isLower))
      facet->visitid= 0;  /* facetlist or facets may overwrite */
    else
      facet->visitid= qh->visit_id;
    facet->seen= False;
    facet->seen2= True;
  }
  numcenters++;  /* qh_INFINITE */
  FORALLfacet_(facetlist) {
    if (printall || !qh_skipfacet(qh, facet))
      facet->visitid= numcenters++;
  }
  FOREACHfacet_(facets) {
    if (printall || !qh_skipfacet(qh, facet))
      facet->visitid= numcenters++;
  }
  *isLowerp= isLower;
  *numcentersp= numcenters;
  trace2((qh, qh->ferr, 2007, "qh_markvoronoi: isLower %d numcenters %d\n", isLower, numcenters));
  return vertices;
} /* markvoronoi */

/*---------------------------------

  qh_order_vertexneighbors(qh, 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(qhT *qh, vertexT *vertex) {
  setT *newset;
  facetT *facet, *neighbor, **neighborp;

  trace4((qh, qh->ferr, 4018, "qh_order_vertexneighbors: order neighbors of v%d for 3-d\n", vertex->id));
  newset= qh_settemp(qh, qh_setsize(qh, vertex->neighbors));
  facet= (facetT*)qh_setdellast(vertex->neighbors);
  qh_setappend(qh, &newset, facet);
  while (qh_setsize(qh, vertex->neighbors)) {
    FOREACHneighbor_(vertex) {
      if (qh_setin(facet->neighbors, neighbor)) {
        qh_setdel(vertex->neighbors, neighbor);
        qh_setappend(qh, &newset, neighbor);
        facet= neighbor;
        break;
      }
    }
    if (!neighbor) {
      qh_fprintf(qh, qh->ferr, 6066, "qhull internal error (qh_order_vertexneighbors): no neighbor of v%d for f%d\n",
        vertex->id, facet->id);
      qh_errexit(qh, qh_ERRqhull, facet, NULL);
    }
  }
  qh_setfree(qh, &vertex->neighbors);
  qh_settemppop(qh);
  vertex->neighbors= newset;
} /* order_vertexneighbors */

/*---------------------------------

  qh_prepare_output(qh, )
    prepare for qh_produce_output2(qh) according to
      qh.KEEPminArea, KEEParea, KEEPmerge, GOODvertex, GOODthreshold, GOODpoint, ONLYgood, SPLITthresholds
    does not reset facet->good

  notes
    except for PRINTstatistics, no-op if previously called with same options
*/
void qh_prepare_output(qhT *qh) {
  if (qh->VORONOI) {
    qh_clearcenters(qh, qh_ASvoronoi);  /* must be before qh_triangulate */
    qh_vertexneighbors(qh);
  }
  if (qh->TRIangulate && !qh->hasTriangulation) {
    qh_triangulate(qh);
    if (qh->VERIFYoutput && !qh->CHECKfrequently)
      qh_checkpolygon(qh, qh->facet_list);
  }
  qh_findgood_all(qh, qh->facet_list);
  if (qh->GETarea)
    qh_getarea(qh, qh->facet_list);
  if (qh->KEEParea || qh->KEEPmerge || qh->KEEPminArea < REALmax/2)
    qh_markkeep(qh, qh->facet_list);
  if (qh->PRINTstatistics)
    qh_collectstatistics(qh);
}

/*---------------------------------

  qh_printafacet(qh, fp, format, facet, printall )
    print facet to fp in given output format (see qh.PRINTout)

  returns:
    nop if !printall and qh_skipfacet()
    nop if visible facet and NEWfacets and format != PRINTfacets
    must match qh_countfacets

  notes
    preserves qh.visit_id
    facet->normal may be null if PREmerge/MERGEexact and STOPcone before merge

  see
    qh_printbegin() and qh_printend()

  design:
    test for printing facet
    call appropriate routine for format
    or output results directly
*/
void qh_printafacet(qhT *qh, FILE *fp, qh_PRINT format, facetT *facet, boolT printall) {
  realT color[4], offset, dist, outerplane, innerplane;
  boolT zerodiv;
  coordT *point, *normp, *coordp, **pointp, *feasiblep;
  int k;
  vertexT *vertex, **vertexp;
  facetT *neighbor, **neighborp;

  if (!printall && qh_skipfacet(qh, facet))
    return;
  if (facet->visible && qh->NEWfacets && format != qh_PRINTfacets)
    return;
  qh->printoutnum++;
  switch (format) {
  case qh_PRINTarea:
    if (facet->isarea) {
      qh_fprintf(qh, fp, 9009, qh_REAL_1, facet->f.area);
      qh_fprintf(qh, fp, 9010, "\n");
    }else
      qh_fprintf(qh, fp, 9011, "0\n");
    break;
  case qh_PRINTcoplanars:
    qh_fprintf(qh, fp, 9012, "%d", qh_setsize(qh, facet->coplanarset));
    FOREACHpoint_(facet->coplanarset)
      qh_fprintf(qh, fp, 9013, " %d", qh_pointid(qh, point));
    qh_fprintf(qh, fp, 9014, "\n");
    break;
  case qh_PRINTcentrums:
    qh_printcenter(qh, fp, format, NULL, facet);
    break;
  case qh_PRINTfacets:
    qh_printfacet(qh, fp, facet);
    break;
  case qh_PRINTfacets_xridge:
    qh_printfacetheader(qh, fp, facet);
    break;
  case qh_PRINTgeom:  /* either 2 , 3, or 4-d by qh_printbegin */
    if (!facet->normal)
      break;
    for (k=qh->hull_dim; k--; ) {
      color[k]= (facet->normal[k]+1.0)/2.0;
      maximize_(color[k], -1.0);
      minimize_(color[k], +1.0);
    }
    qh_projectdim3(qh, color, color);
    if (qh->PRINTdim != qh->hull_dim)
      qh_normalize2(qh, color, 3, True, NULL, NULL);
    if (qh->hull_dim <= 2)
      qh_printfacet2geom(qh, fp, facet, color);
    else if (qh->hull_dim == 3) {
      if (facet->simplicial)
        qh_printfacet3geom_simplicial(qh, fp, facet, color);
      else
        qh_printfacet3geom_nonsimplicial(qh, fp, facet, color);
    }else {
      if (facet->simplicial)
        qh_printfacet4geom_simplicial(qh, fp, facet, color);
      else
        qh_printfacet4geom_nonsimplicial(qh, fp, facet, color);
    }
    break;
  case qh_PRINTids:
    qh_fprintf(qh, fp, 9015, "%d\n", facet->id);
    break;
  case qh_PRINTincidences:
  case qh_PRINToff:
  case qh_PRINTtriangles:
    if (qh->hull_dim == 3 && format != qh_PRINTtriangles)
      qh_printfacet3vertex(qh, fp, facet, format);
    else if (facet->simplicial || qh->hull_dim == 2 || format == qh_PRINToff)
      qh_printfacetNvertex_simplicial(qh, fp, facet, format);
    else
      qh_printfacetNvertex_nonsimplicial(qh, fp, facet, qh->printoutvar++, format);
    break;
  case qh_PRINTinner:
    qh_outerinner(qh, facet, NULL, &innerplane);
    offset= facet->offset - innerplane;
    goto LABELprintnorm;
    break; /* prevent warning */
  case qh_PRINTmerges:
    qh_fprintf(qh, fp, 9016, "%d\n", facet->nummerge);
    break;
  case qh_PRINTnormals:
    offset= facet->offset;
    goto LABELprintnorm;
    break; /* prevent warning */
  case qh_PRINTouter:
    qh_outerinner(qh, facet, &outerplane, NULL);
    offset= facet->offset - outerplane;
  LABELprintnorm:
    if (!facet->normal) {
      qh_fprintf(qh, fp, 9017, "no normal for facet f%d\n", facet->id);
      break;
    }
    if (qh->CDDoutput) {
      qh_fprintf(qh, fp, 9018, qh_REAL_1, -offset);
      for (k=0; k < qh->hull_dim; k++)
        qh_fprintf(qh, fp, 9019, qh_REAL_1, -facet->normal[k]);
    }else {
      for (k=0; k < qh->hull_dim; k++)
        qh_fprintf(qh, fp, 9020, qh_REAL_1, facet->normal[k]);
      qh_fprintf(qh, fp, 9021, qh_REAL_1, offset);
    }
    qh_fprintf(qh, fp, 9022, "\n");
    break;
  case qh_PRINTmathematica:  /* either 2 or 3-d by qh_printbegin */
  case qh_PRINTmaple:
    if (qh->hull_dim == 2)
      qh_printfacet2math(qh, fp, facet, format, qh->printoutvar++);
    else
      qh_printfacet3math(qh, fp, facet, format, qh->printoutvar++);
    break;
  case qh_PRINTneighbors:
    qh_fprintf(qh, fp, 9023, "%d", qh_setsize(qh, facet->neighbors));
    FOREACHneighbor_(facet)
      qh_fprintf(qh, fp, 9024, " %d",
               neighbor->visitid ? neighbor->visitid - 1: 0 - neighbor->id);
    qh_fprintf(qh, fp, 9025, "\n");
    break;
  case qh_PRINTpointintersect:
    if (!qh->feasible_point) {
      qh_fprintf(qh, qh->ferr, 6067, "qhull input error (qh_printafacet): option 'Fp' needs qh->feasible_point\n");
      qh_errexit(qh, qh_ERRinput, NULL, NULL);
    }
    if (facet->offset > 0)
      goto LABELprintinfinite;
    point= coordp= (coordT*)qh_memalloc(qh, qh->normal_size);
    normp= facet->normal;
    feasiblep= qh->feasible_point;
    if (facet->offset < -qh->MINdenom) {
      for (k=qh->hull_dim; k--; )
        *(coordp++)= (*(normp++) / - facet->offset) + *(feasiblep++);
    }else {
      for (k=qh->hull_dim; k--; ) {
        *(coordp++)= qh_divzero(*(normp++), facet->offset, qh->MINdenom_1,
                                 &zerodiv) + *(feasiblep++);
        if (zerodiv) {
          qh_memfree(qh, point, qh->normal_size);
          goto LABELprintinfinite;
        }
      }
    }
    qh_printpoint(qh, fp, NULL, point);
    qh_memfree(qh, point, qh->normal_size);
    break;
  LABELprintinfinite:
    for (k=qh->hull_dim; k--; )
      qh_fprintf(qh, fp, 9026, qh_REAL_1, qh_INFINITE);
    qh_fprintf(qh, fp, 9027, "\n");
    break;
  case qh_PRINTpointnearest:
    FOREACHpoint_(facet->coplanarset) {
      int id, id2;
      vertex= qh_nearvertex(qh, facet, point, &dist);
      id= qh_pointid(qh, vertex->point);
      id2= qh_pointid(qh, point);
      qh_fprintf(qh, fp, 9028, "%d %d %d " qh_REAL_1 "\n", id, id2, facet->id, dist);
    }
    break;
  case qh_PRINTpoints:  /* VORONOI only by qh_printbegin */
    if (qh->CDDoutput)
      qh_fprintf(qh, fp, 9029, "1 ");
    qh_printcenter(qh, fp, format, NULL, facet);
    break;
  case qh_PRINTvertices:
    qh_fprintf(qh, fp, 9030, "%d", qh_setsize(qh, facet->vertices));
    FOREACHvertex_(facet->vertices)
      qh_fprintf(qh, fp, 9031, " %d", qh_pointid(qh, vertex->point));
    qh_fprintf(qh, fp, 9032, "\n");
    break;
  default:
    break;
  }
} /* printafacet */

/*---------------------------------

  qh_printbegin(qh, )
    prints header for all output formats

  returns:
    checks for valid format

  notes:
    uses qh.visit_id for 3/4off
    changes qh.interior_point if printing centrums
    qh_countfacets clears facet->visitid for non-good facets

  see
    qh_printend() and qh_printafacet()

  design:
    count facets and related statistics
    print header for format
*/
void qh_printbegin(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {
  int numfacets, numsimplicial, numridges, totneighbors, numcoplanars, numtricoplanars;
  int i, num;
  facetT *facet, **facetp;
  vertexT *vertex, **vertexp;
  setT *vertices;
  pointT *point, **pointp, *pointtemp;

  qh->printoutnum= 0;
  qh_countfacets(qh, facetlist, facets, printall, &numfacets, &numsimplicial,
      &totneighbors, &numridges, &numcoplanars, &numtricoplanars);
  switch (format) {
  case qh_PRINTnone:
    break;
  case qh_PRINTarea:
    qh_fprintf(qh, fp, 9033, "%d\n", numfacets);
    break;
  case qh_PRINTcoplanars:
    qh_fprintf(qh, fp, 9034, "%d\n", numfacets);
    break;
  case qh_PRINTcentrums:
    if (qh->CENTERtype == qh_ASnone)
      qh_clearcenters(qh, qh_AScentrum);
    qh_fprintf(qh, fp, 9035, "%d\n%d\n", qh->hull_dim, numfacets);
    break;
  case qh_PRINTfacets:
  case qh_PRINTfacets_xridge:
    if (facetlist)
      qh_printvertexlist(qh, fp, "Vertices and facets:\n", facetlist, facets, printall);
    break;
  case qh_PRINTgeom:
    if (qh->hull_dim > 4)  /* qh_initqhull_globals also checks */
      goto LABELnoformat;
    if (qh->VORONOI && qh->hull_dim > 3)  /* PRINTdim == DROPdim == hull_dim-1 */
      goto LABELnoformat;
    if (qh->hull_dim == 2 && (qh->PRINTridges || qh->DOintersections))
      qh_fprintf(qh, qh->ferr, 7049, "qhull warning: output for ridges and intersections not implemented in 2-d\n");
    if (qh->hull_dim == 4 && (qh->PRINTinner || qh->PRINTouter ||
                             (qh->PRINTdim == 4 && qh->PRINTcentrums)))
      qh_fprintf(qh, qh->ferr, 7050, "qhull warning: output for outer/inner planes and centrums not implemented in 4-d\n");
    if (qh->PRINTdim == 4 && (qh->PRINTspheres))
      qh_fprintf(qh, qh->ferr, 7051, "qhull warning: output for vertices not implemented in 4-d\n");
    if (qh->PRINTdim == 4 && qh->DOintersections && qh->PRINTnoplanes)
      qh_fprintf(qh, qh->ferr, 7052, "qhull warning: 'Gnh' generates no output in 4-d\n");
    if (qh->PRINTdim == 2) {
      qh_fprintf(qh, fp, 9036, "{appearance {linewidth 3} LIST # %s | %s\n",
              qh->rbox_command, qh->qhull_command);
    }else if (qh->PRINTdim == 3) {
      qh_fprintf(qh, fp, 9037, "{appearance {+edge -evert linewidth 2} LIST # %s | %s\n",
              qh->rbox_command, qh->qhull_command);
    }else if (qh->PRINTdim == 4) {
      qh->visit_id++;
      num= 0;
      FORALLfacet_(facetlist)    /* get number of ridges to be printed */
        qh_printend4geom(qh, NULL, facet, &num, printall);
      FOREACHfacet_(facets)
        qh_printend4geom(qh, NULL, facet, &num, printall);
      qh->ridgeoutnum= num;
      qh->printoutvar= 0;  /* counts number of ridges in output */
      qh_fprintf(qh, fp, 9038, "LIST # %s | %s\n", qh->rbox_command, qh->qhull_command);
    }

    if (qh->PRINTdots) {
      qh->printoutnum++;
      num= qh->num_points + qh_setsize(qh, qh->other_points);
      if (qh->DELAUNAY && qh->ATinfinity)
        num--;
      if (qh->PRINTdim == 4)
        qh_fprintf(qh, fp, 9039, "4VECT %d %d 1\n", num, num);
      else
        qh_fprintf(qh, fp, 9040, "VECT %d %d 1\n", num, num);

      for (i=num; i--; ) {
        if (i % 20 == 0)
          qh_fprintf(qh, fp, 9041, "\n");
        qh_fprintf(qh, fp, 9042, "1 ");
      }
      qh_fprintf(qh, fp, 9043, "# 1 point per line\n1 ");
      for (i=num-1; i--; ) { /* num at least 3 for D2 */
        if (i % 20 == 0)
          qh_fprintf(qh, fp, 9044, "\n");
        qh_fprintf(qh, fp, 9045, "0 ");
      }
      qh_fprintf(qh, fp, 9046, "# 1 color for all\n");
      FORALLpoints {
        if (!qh->DELAUNAY || !qh->ATinfinity || qh_pointid(qh, point) != qh->num_points-1) {
          if (qh->PRINTdim == 4)
            qh_printpoint(qh, fp, NULL, point);
            else
              qh_printpoint3(qh, fp, point);
        }
      }
      FOREACHpoint_(qh->other_points) {
        if (qh->PRINTdim == 4)
          qh_printpoint(qh, fp, NULL, point);
        else
          qh_printpoint3(qh, fp, point);
      }
      qh_fprintf(qh, fp, 9047, "0 1 1 1  # color of points\n");
    }

    if (qh->PRINTdim == 4  && !qh->PRINTnoplanes)
      /* 4dview loads up multiple 4OFF objects slowly */
      qh_fprintf(qh, fp, 9048, "4OFF %d %d 1\n", 3*qh->ridgeoutnum, qh->ridgeoutnum);
    qh->PRINTcradius= 2 * qh->DISTround;  /* include test DISTround */
    if (qh->PREmerge) {
      maximize_(qh->PRINTcradius, qh->premerge_centrum + qh->DISTround);
    }else if (qh->POSTmerge)
      maximize_(qh->PRINTcradius, qh->postmerge_centrum + qh->DISTround);
    qh->PRINTradius= qh->PRINTcradius;
    if (qh->PRINTspheres + qh->PRINTcoplanar)
      maximize_(qh->PRINTradius, qh->MAXabs_coord * qh_MINradius);
    if (qh->premerge_cos < REALmax/2) {
      maximize_(qh->PRINTradius, (1- qh->premerge_cos) * qh->MAXabs_coord);
    }else if (!qh->PREmerge && qh->POSTmerge && qh->postmerge_cos < REALmax/2) {
      maximize_(qh->PRINTradius, (1- qh->postmerge_cos) * qh->MAXabs_coord);
    }
    maximize_(qh->PRINTradius, qh->MINvisible);
    if (qh->JOGGLEmax < REALmax/2)
      qh->PRINTradius += qh->JOGGLEmax * sqrt((realT)qh->hull_dim);
    if (qh->PRINTdim != 4 &&
        (qh->PRINTcoplanar || qh->PRINTspheres || qh->PRINTcentrums)) {
      vertices= qh_facetvertices(qh, facetlist, facets, printall);
      if (qh->PRINTspheres && qh->PRINTdim <= 3)
        qh_printspheres(qh, fp, vertices, qh->PRINTradius);
      if (qh->PRINTcoplanar || qh->PRINTcentrums) {
        qh->firstcentrum= True;
        if (qh->PRINTcoplanar&& !qh->PRINTspheres) {
          FOREACHvertex_(vertices)
            qh_printpointvect2(qh, fp, vertex->point, NULL, qh->interior_point, qh->PRINTradius);
        }
        FORALLfacet_(facetlist) {
          if (!printall && qh_skipfacet(qh, facet))
            continue;
          if (!facet->normal)
            continue;
          if (qh->PRINTcentrums && qh->PRINTdim <= 3)
            qh_printcentrum(qh, fp, facet, qh->PRINTcradius);
          if (!qh->PRINTcoplanar)
            continue;
          FOREACHpoint_(facet->coplanarset)
            qh_printpointvect2(qh, fp, point, facet->normal, NULL, qh->PRINTradius);
          FOREACHpoint_(facet->outsideset)
            qh_printpointvect2(qh, fp, point, facet->normal, NULL, qh->PRINTradius);
        }
        FOREACHfacet_(facets) {
          if (!printall && qh_skipfacet(qh, facet))
            continue;
          if (!facet->normal)
            continue;
          if (qh->PRINTcentrums && qh->PRINTdim <= 3)
            qh_printcentrum(qh, fp, facet, qh->PRINTcradius);
          if (!qh->PRINTcoplanar)
            continue;
          FOREACHpoint_(facet->coplanarset)
            qh_printpointvect2(qh, fp, point, facet->normal, NULL, qh->PRINTradius);
          FOREACHpoint_(facet->outsideset)
            qh_printpointvect2(qh, fp, point, facet->normal, NULL, qh->PRINTradius);
        }
      }
      qh_settempfree(qh, &vertices);
    }
    qh->visit_id++; /* for printing hyperplane intersections */
    break;
  case qh_PRINTids:
    qh_fprintf(qh, fp, 9049, "%d\n", numfacets);
    break;
  case qh_PRINTincidences:
    if (qh->VORONOI && qh->PRINTprecision)
      qh_fprintf(qh, qh->ferr, 7053, "qhull warning: 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(qh, fp, 9050, "%d\n", numfacets);
    else
      qh_fprintf(qh, fp, 9051, "%d\n", numsimplicial+numridges);
    break;
  case qh_PRINTinner:
  case qh_PRINTnormals:
  case qh_PRINTouter:
    if (qh->CDDoutput)
      qh_fprintf(qh, fp, 9052, "%s | %s\nbegin\n    %d %d real\n", qh->rbox_command,
            qh->qhull_command, numfacets, qh->hull_dim+1);
    else
      qh_fprintf(qh, fp, 9053, "%d\n%d\n", qh->hull_dim+1, numfacets);
    break;
  case qh_PRINTmathematica:
  case qh_PRINTmaple:
    if (qh->hull_dim > 3)  /* qh_initbuffers also checks */
      goto LABELnoformat;
    if (qh->VORONOI)
      qh_fprintf(qh, qh->ferr, 7054, "qhull warning: output is the Delaunay triangulation\n");
    if (format == qh_PRINTmaple) {
      if (qh->hull_dim == 2)
        qh_fprintf(qh, fp, 9054, "PLOT(CURVES(\n");
      else
        qh_fprintf(qh, fp, 9055, "PLOT3D(POLYGONS(\n");
    }else
      qh_fprintf(qh, fp, 9056, "{\n");
    qh->printoutvar= 0;   /* counts number of facets for notfirst */
    break;
  case qh_PRINTmerges:
    qh_fprintf(qh, fp, 9057, "%d\n", numfacets);
    break;
  case qh_PRINTpointintersect:
    qh_fprintf(qh, fp, 9058, "%d\n%d\n", qh->hull_dim, numfacets);
    break;
  case qh_PRINTneighbors:
    qh_fprintf(qh, fp, 9059, "%d\n", numfacets);
    break;
  case qh_PRINToff:
  case qh_PRINTtriangles:
    if (qh->VORONOI)
      goto LABELnoformat;
    num = qh->hull_dim;
    if (format == qh_PRINToff || qh->hull_dim == 2)
      qh_fprintf(qh, fp, 9060, "%d\n%d %d %d\n", num,
        qh->num_points+qh_setsize(qh, qh->other_points), numfacets, totneighbors/2);
    else { /* qh_PRINTtriangles */
      qh->printoutvar= qh->num_points+qh_setsize(qh, qh->other_points); /* first centrum */
      if (qh->DELAUNAY)
        num--;  /* drop last dimension */
      qh_fprintf(qh, fp, 9061, "%d\n%d %d %d\n", num, qh->printoutvar
        + numfacets - numsimplicial, numsimplicial + numridges, totneighbors/2);
    }
    FORALLpoints
      qh_printpointid(qh, qh->fout, NULL, num, point, qh_IDunknown);
    FOREACHpoint_(qh->other_points)
      qh_printpointid(qh, qh->fout, NULL, num, point, qh_IDunknown);
    if (format == qh_PRINTtriangles && qh->hull_dim > 2) {
      FORALLfacets {
        if (!facet->simplicial && facet->visitid)
          qh_printcenter(qh, qh->fout, format, NULL, facet);
      }
    }
    break;
  case qh_PRINTpointnearest:
    qh_fprintf(qh, fp, 9062, "%d\n", numcoplanars);
    break;
  case qh_PRINTpoints:
    if (!qh->VORONOI)
      goto LABELnoformat;
    if (qh->CDDoutput)
      qh_fprintf(qh, fp, 9063, "%s | %s\nbegin\n%d %d real\n", qh->rbox_command,
           qh->qhull_command, numfacets, qh->hull_dim);
    else
      qh_fprintf(qh, fp, 9064, "%d\n%d\n", qh->hull_dim-1, numfacets);
    break;
  case qh_PRINTvertices:
    qh_fprintf(qh, fp, 9065, "%d\n", numfacets);
    break;
  case qh_PRINTsummary:
  default:
  LABELnoformat:
    qh_fprintf(qh, qh->ferr, 6068, "qhull internal error (qh_printbegin): can not use this format for dimension %d\n",
         qh->hull_dim);
    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
  }
} /* printbegin */

/*---------------------------------

  qh_printcenter(qh, fp, string, facet )
    print facet->center as centrum or Voronoi center
    string may be NULL.  Don't include '%' codes.
    nop if qh->CENTERtype neither CENTERvoronoi nor CENTERcentrum
    if upper envelope of Delaunay triangulation and point at-infinity
      prints qh_INFINITE instead;

  notes:
    defines facet->center if needed
    if format=PRINTgeom, adds a 0 if would otherwise be 2-d
    Same as QhullFacet::printCenter
*/
void qh_printcenter(qhT *qh, FILE *fp, qh_PRINT format, const char *string, facetT *facet) {
  int k, num;

  if (qh->CENTERtype != qh_ASvoronoi && qh->CENTERtype != qh_AScentrum)
    return;
  if (string)
    qh_fprintf(qh, fp, 9066, string);
  if (qh->CENTERtype == qh_ASvoronoi) {
    num= qh->hull_dim-1;
    if (!facet->normal || !facet->upperdelaunay || !qh->ATinfinity) {
      if (!facet->center)
        facet->center= qh_facetcenter(qh, facet->vertices);
      for (k=0; k < num; k++)
        qh_fprintf(qh, fp, 9067, qh_REAL_1, facet->center[k]);
    }else {
      for (k=0; k < num; k++)
        qh_fprintf(qh, fp, 9068, qh_REAL_1, qh_INFINITE);
    }
  }else /* qh->CENTERtype == qh_AScentrum */ {
    num= qh->hull_dim;
    if (format == qh_PRINTtriangles && qh->DELAUNAY)
      num--;
    if (!facet->center)
      facet->center= qh_getcentrum(qh, facet);
    for (k=0; k < num; k++)
      qh_fprintf(qh, fp, 9069, qh_REAL_1, facet->center[k]);
  }
  if (format == qh_PRINTgeom && num == 2)
    qh_fprintf(qh, fp, 9070, " 0\n");
  else
    qh_fprintf(qh, fp, 9071, "\n");
} /* printcenter */

/*---------------------------------

  qh_printcentrum(qh, fp, facet, radius )
    print centrum for a facet in OOGL format
    radius defines size of centrum
    2-d or 3-d only

  returns:
    defines facet->center if needed
*/
void qh_printcentrum(qhT *qh, FILE *fp, facetT *facet, realT radius) {
  pointT *centrum, *projpt;
  boolT tempcentrum= False;
  realT xaxis[4], yaxis[4], normal[4], dist;
  realT green[3]={0, 1, 0};
  vertexT *apex;
  int k;

  if (qh->CENTERtype == qh_AScentrum) {
    if (!facet->center)
      facet->center= qh_getcentrum(qh, facet);
    centrum= facet->center;
  }else {
    centrum= qh_getcentrum(qh, facet);
    tempcentrum= True;
  }
  qh_fprintf(qh, fp, 9072, "{appearance {-normal -edge normscale 0} ");
  if (qh->firstcentrum) {
    qh->firstcentrum= False;
    qh_fprintf(qh, fp, 9073, "{INST geom { define centrum CQUAD  # f%d\n\
-0.3 -0.3 0.0001     0 0 1 1\n\
 0.3 -0.3 0.0001     0 0 1 1\n\
 0.3  0.3 0.0001     0 0 1 1\n\
-0.3  0.3 0.0001     0 0 1 1 } transform { \n", facet->id);
  }else
    qh_fprintf(qh, fp, 9074, "{INST geom { : centrum } transform { # f%d\n", facet->id);
  apex= SETfirstt_(facet->vertices, vertexT);
  qh_distplane(qh, apex->point, facet, &dist);
  projpt= qh_projectpoint(qh, apex->point, facet, dist);
  for (k=qh->hull_dim; k--; ) {
    xaxis[k]= projpt[k] - centrum[k];
    normal[k]= facet->normal[k];
  }
  if (qh->hull_dim == 2) {
    xaxis[2]= 0;
    normal[2]= 0;
  }else if (qh->hull_dim == 4) {
    qh_projectdim3(qh, xaxis, xaxis);
    qh_projectdim3(qh, normal, normal);
    qh_normalize2(qh, normal, qh->PRINTdim, True, NULL, NULL);
  }
  qh_crossproduct(3, xaxis, normal, yaxis);
  qh_fprintf(qh, fp, 9075, "%8.4g %8.4g %8.4g 0\n", xaxis[0], xaxis[1], xaxis[2]);
  qh_fprintf(qh, fp, 9076, "%8.4g %8.4g %8.4g 0\n", yaxis[0], yaxis[1], yaxis[2]);
  qh_fprintf(qh, fp, 9077, "%8.4g %8.4g %8.4g 0\n", normal[0], normal[1], normal[2]);
  qh_printpoint3(qh, fp, centrum);
  qh_fprintf(qh, fp, 9078, "1 }}}\n");
  qh_memfree(qh, projpt, qh->normal_size);
  qh_printpointvect(qh, fp, centrum, facet->normal, NULL, radius, green);
  if (tempcentrum)
    qh_memfree(qh, centrum, qh->normal_size);
} /* printcentrum */

/*---------------------------------

  qh_printend(qh, fp, format )
    prints trailer for all output formats

  see:
    qh_printbegin() and qh_printafacet()

*/
void qh_printend(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {
  int num;
  facetT *facet, **facetp;

  if (!qh->printoutnum)
    qh_fprintf(qh, qh->ferr, 7055, "qhull warning: no facets printed\n");
  switch (format) {
  case qh_PRINTgeom:
    if (qh->hull_dim == 4 && qh->DROPdim < 0  && !qh->PRINTnoplanes) {
      qh->visit_id++;
      num= 0;
      FORALLfacet_(facetlist)
        qh_printend4geom(qh, fp, facet,&num, printall);
      FOREACHfacet_(facets)
        qh_printend4geom(qh, fp, facet, &num, printall);
      if (num != qh->ridgeoutnum || qh->printoutvar != qh->ridgeoutnum) {
        qh_fprintf(qh, qh->ferr, 6069, "qhull internal error (qh_printend): number of ridges %d != number printed %d and at end %d\n", qh->ridgeoutnum, qh->printoutvar, num);
        qh_errexit(qh, qh_ERRqhull, NULL, NULL);
      }
    }else
      qh_fprintf(qh, fp, 9079, "}\n");
    break;
  case qh_PRINTinner:
  case qh_PRINTnormals:
  case qh_PRINTouter:
    if (qh->CDDoutput)
      qh_fprintf(qh, fp, 9080, "end\n");
    break;
  case qh_PRINTmaple:
    qh_fprintf(qh, fp, 9081, "));\n");
    break;
  case qh_PRINTmathematica:
    qh_fprintf(qh, fp, 9082, "}\n");
    break;
  case qh_PRINTpoints:
    if (qh->CDDoutput)
      qh_fprintf(qh, fp, 9083, "end\n");
    break;
  default:
    break;
  }
} /* printend */

/*---------------------------------

  qh_printend4geom(qh, fp, facet, numridges, printall )
    helper function for qh_printbegin/printend

  returns:
    number of printed ridges

  notes:
    just counts printed ridges if fp=NULL
    uses facet->visitid
    must agree with qh_printfacet4geom...

  design:
    computes color for facet from its normal
    prints each ridge of facet
*/
void qh_printend4geom(qhT *qh, FILE *fp, facetT *facet, int *nump, boolT printall) {
  realT color[3];
  int i, num= *nump;
  facetT *neighbor, **neighborp;
  ridgeT *ridge, **ridgep;

  if (!printall && qh_skipfacet(qh, facet))
    return;
  if (qh->PRINTnoplanes || (facet->visible && qh->NEWfacets))
    return;
  if (!facet->normal)
    return;
  if (fp) {
    for (i=0; i < 3; i++) {
      color[i]= (facet->normal[i]+1.0)/2.0;
      maximize_(color[i], -1.0);
      minimize_(color[i], +1.0);
    }
  }
  facet->visitid= qh->visit_id;
  if (facet->simplicial) {
    FOREACHneighbor_(facet) {
      if (neighbor->visitid != qh->visit_id) {
        if (fp)
          qh_fprintf(qh, fp, 9084, "3 %d %d %d %8.4g %8.4g %8.4g 1 # f%d f%d\n",
                 3*num, 3*num+1, 3*num+2, color[0], color[1], color[2],
                 facet->id, neighbor->id);
        num++;
      }
    }
  }else {
    FOREACHridge_(facet->ridges) {
      neighbor= otherfacet_(ridge, facet);
      if (neighbor->visitid != qh->visit_id) {
        if (fp)
          qh_fprintf(qh, fp, 9085, "3 %d %d %d %8.4g %8.4g %8.4g 1 #r%d f%d f%d\n",
                 3*num, 3*num+1, 3*num+2, color[0], color[1], color[2],
                 ridge->id, facet->id, neighbor->id);
        num++;
      }
    }
  }
  *nump= num;
} /* printend4geom */

/*---------------------------------

  qh_printextremes(qh, fp, facetlist, facets, printall )
    print extreme points for convex hulls or halfspace intersections

  notes:
    #points, followed by ids, one per line

    sorted by id
    same order as qh_printpoints_out if no coplanar/interior points
*/
void qh_printextremes(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall) {
  setT *vertices, *points;
  pointT *point;
  vertexT *vertex, **vertexp;
  int id;
  int numpoints=0, point_i, point_n;
  int allpoints= qh->num_points + qh_setsize(qh, qh->other_points);

  points= qh_settemp(qh, allpoints);
  qh_setzero(qh, points, 0, allpoints);
  vertices= qh_facetvertices(qh, facetlist, facets, printall);
  FOREACHvertex_(vertices) {
    id= qh_pointid(qh, vertex->point);
    if (id >= 0) {
      SETelem_(points, id)= vertex->point;
      numpoints++;
    }
  }
  qh_settempfree(qh, &vertices);
  qh_fprintf(qh, fp, 9086, "%d\n", numpoints);
  FOREACHpoint_i_(qh, points) {
    if (point)
      qh_fprintf(qh, fp, 9087, "%d\n", point_i);
  }
  qh_settempfree(qh, &points);
} /* printextremes */

/*---------------------------------

  qh_printextremes_2d(qh, fp, facetlist, facets, printall )
    prints point ids for facets in qh_ORIENTclock order

  notes:
    #points, followed by ids, one per line
    if facetlist/facets are disjoint than the output includes skips
    errors if facets form a loop
    does not print coplanar points
*/
void qh_printextremes_2d(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall) {
  int numfacets, numridges, totneighbors, numcoplanars, numsimplicial, numtricoplanars;
  setT *vertices;
  facetT *facet, *startfacet, *nextfacet;
  vertexT *vertexA, *vertexB;

  qh_countfacets(qh, facetlist, facets, printall, &numfacets, &numsimplicial,
      &totneighbors, &numridges, &numcoplanars, &numtricoplanars); /* marks qh->visit_id */
  vertices= qh_facetvertices(qh, facetlist, facets, printall);
  qh_fprintf(qh, fp, 9088, "%d\n", qh_setsize(qh, vertices));
  qh_settempfree(qh, &vertices);
  if (!numfacets)
    return;
  facet= startfacet= facetlist ? facetlist : SETfirstt_(facets, facetT);
  qh->vertex_visit++;
  qh->visit_id++;
  do {
    if (facet->toporient ^ qh_ORIENTclock) {
      vertexA= SETfirstt_(facet->vertices, vertexT);
      vertexB= SETsecondt_(facet->vertices, vertexT);
      nextfacet= SETfirstt_(facet->neighbors, facetT);
    }else {
      vertexA= SETsecondt_(facet->vertices, vertexT);
      vertexB= SETfirstt_(facet->vertices, vertexT);
      nextfacet= SETsecondt_(facet->neighbors, facetT);
    }
    if (facet->visitid == qh->visit_id) {
      qh_fprintf(qh, qh->ferr, 6218, "Qhull internal error (qh_printextremes_2d): loop in facet list.  facet %d nextfacet %d\n",
                 facet->id, nextfacet->id);
      qh_errexit2(qh, qh_ERRqhull, facet, nextfacet);
    }
    if (facet->visitid) {
      if (vertexA->visitid != qh->vertex_visit) {
        vertexA->visitid= qh->vertex_visit;
        qh_fprintf(qh, fp, 9089, "%d\n", qh_pointid(qh, vertexA->point));
      }
      if (vertexB->visitid != qh->vertex_visit) {
        vertexB->visitid= qh->vertex_visit;
        qh_fprintf(qh, fp, 9090, "%d\n", qh_pointid(qh, vertexB->point));
      }
    }
    facet->visitid= qh->visit_id;
    facet= nextfacet;
  }while (facet && facet != startfacet);
} /* printextremes_2d */

/*---------------------------------

  qh_printextremes_d(qh, fp, facetlist, facets, printall )
    print extreme points of input sites for Delaunay triangulations

  notes:
    #points, followed by ids, one per line

    unordered
*/
void qh_printextremes_d(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall) {
  setT *vertices;
  vertexT *vertex, **vertexp;
  boolT upperseen, lowerseen;
  facetT *neighbor, **neighborp;
  int numpoints=0;

  vertices= qh_facetvertices(qh, facetlist, facets, printall);
  qh_vertexneighbors(qh);
  FOREACHvertex_(vertices) {
    upperseen= lowerseen= False;
    FOREACHneighbor_(vertex) {
      if (neighbor->upperdelaunay)
        upperseen= True;
      else
        lowerseen= True;
    }
    if (upperseen && lowerseen) {
      vertex->seen= True;
      numpoints++;
    }else
      vertex->seen= False;
  }
  qh_fprintf(qh, fp, 9091, "%d\n", numpoints);
  FOREACHvertex_(vertices) {
    if (vertex->seen)
      qh_fprintf(qh, fp, 9092, "%d\n", qh_pointid(qh, vertex->point));
  }
  qh_settempfree(qh, &vertices);
} /* printextremes_d */

/*---------------------------------

  qh_printfacet(qh, fp, facet )
    prints all fields of a facet to fp

  notes:
    ridges printed in neighbor order
*/
void qh_printfacet(qhT *qh, FILE *fp, facetT *facet) {

  qh_printfacetheader(qh, fp, facet);
  if (facet->ridges)
    qh_printfacetridges(qh, fp, facet);
} /* printfacet */


/*---------------------------------

  qh_printfacet2geom(qh, fp, facet, color )
    print facet as part of a 2-d VECT for Geomview

    notes:
      assume precise calculations in io_r.c with roundoff covered by qh_GEOMepsilon
      mindist is calculated within io_r.c.  maxoutside is calculated elsewhere
      so a DISTround error may have occurred.
*/
void qh_printfacet2geom(qhT *qh, FILE *fp, facetT *facet, realT color[3]) {
  pointT *point0, *point1;
  realT mindist, innerplane, outerplane;
  int k;

  qh_facet2point(qh, facet, &point0, &point1, &mindist);
  qh_geomplanes(qh, facet, &outerplane, &innerplane);
  if (qh->PRINTouter || (!qh->PRINTnoplanes && !qh->PRINTinner))
    qh_printfacet2geom_points(qh, fp, point0, point1, facet, outerplane, color);
  if (qh->PRINTinner || (!qh->PRINTnoplanes && !qh->PRINTouter &&
                outerplane - innerplane > 2 * qh->MAXabs_coord * qh_GEOMepsilon)) {
    for (k=3; k--; )
      color[k]= 1.0 - color[k];
    qh_printfacet2geom_points(qh, fp, point0, point1, facet, innerplane, color);
  }
  qh_memfree(qh, point1, qh->normal_size);
  qh_memfree(qh, point0, qh->normal_size);
} /* printfacet2geom */

/*---------------------------------

  qh_printfacet2geom_points(qh, fp, point1, point2, facet, offset, color )
    prints a 2-d facet as a VECT with 2 points at some offset.
    The points are on the facet's plane.
*/
void qh_printfacet2geom_points(qhT *qh, FILE *fp, pointT *point1, pointT *point2,
                               facetT *facet, realT offset, realT color[3]) {
  pointT *p1= point1, *p2= point2;

  qh_fprintf(qh, fp, 9093, "VECT 1 2 1 2 1 # f%d\n", facet->id);
  if (offset != 0.0) {
    p1= qh_projectpoint(qh, p1, facet, -offset);
    p2= qh_projectpoint(qh, p2, facet, -offset);
  }
  qh_fprintf(qh, fp, 9094, "%8.4g %8.4g %8.4g\n%8.4g %8.4g %8.4g\n",
           p1[0], p1[1], 0.0, p2[0], p2[1], 0.0);
  if (offset != 0.0) {
    qh_memfree(qh, p1, qh->normal_size);
    qh_memfree(qh, p2, qh->normal_size);
  }
  qh_fprintf(qh, fp, 9095, "%8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]);
} /* printfacet2geom_points */


/*---------------------------------

  qh_printfacet2math(qh, fp, facet, format, notfirst )
    print 2-d Maple or Mathematica output for a facet
    may be non-simplicial

  notes:
    use %16.8f since Mathematica 2.2 does not handle exponential format
    see qh_printfacet3math
*/
void qh_printfacet2math(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format, int notfirst) {
  pointT *point0, *point1;
  realT mindist;
  const char *pointfmt;

  qh_facet2point(qh, facet, &point0, &point1, &mindist);
  if (notfirst)
    qh_fprintf(qh, fp, 9096, ",");
  if (format == qh_PRINTmaple)
    pointfmt= "[[%16.8f, %16.8f], [%16.8f, %16.8f]]\n";
  else
    pointfmt= "Line[{{%16.8f, %16.8f}, {%16.8f, %16.8f}}]\n";
  qh_fprintf(qh, fp, 9097, pointfmt, point0[0], point0[1], point1[0], point1[1]);
  qh_memfree(qh, point1, qh->normal_size);
  qh_memfree(qh, point0, qh->normal_size);
} /* printfacet2math */


/*---------------------------------

  qh_printfacet3geom_nonsimplicial(qh, fp, facet, color )
    print Geomview OFF for a 3-d nonsimplicial facet.
    if DOintersections, prints ridges to unvisited neighbors(qh->visit_id)

  notes
    uses facet->visitid for intersections and ridges
*/
void qh_printfacet3geom_nonsimplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]) {
  ridgeT *ridge, **ridgep;
  setT *projectedpoints, *vertices;
  vertexT *vertex, **vertexp, *vertexA, *vertexB;
  pointT *projpt, *point, **pointp;
  facetT *neighbor;
  realT dist, outerplane, innerplane;
  int cntvertices, k;
  realT black[3]={0, 0, 0}, green[3]={0, 1, 0};

  qh_geomplanes(qh, facet, &outerplane, &innerplane);
  vertices= qh_facet3vertex(qh, facet); /* oriented */
  cntvertices= qh_setsize(qh, vertices);
  projectedpoints= qh_settemp(qh, cntvertices);
  FOREACHvertex_(vertices) {
    zinc_(Zdistio);
    qh_distplane(qh, vertex->point, facet, &dist);
    projpt= qh_projectpoint(qh, vertex->point, facet, dist);
    qh_setappend(qh, &projectedpoints, projpt);
  }
  if (qh->PRINTouter || (!qh->PRINTnoplanes && !qh->PRINTinner))
    qh_printfacet3geom_points(qh, fp, projectedpoints, facet, outerplane, color);
  if (qh->PRINTinner || (!qh->PRINTnoplanes && !qh->PRINTouter &&
                outerplane - innerplane > 2 * qh->MAXabs_coord * qh_GEOMepsilon)) {
    for (k=3; k--; )
      color[k]= 1.0 - color[k];
    qh_printfacet3geom_points(qh, fp, projectedpoints, facet, innerplane, color);
  }
  FOREACHpoint_(projectedpoints)
    qh_memfree(qh, point, qh->normal_size);
  qh_settempfree(qh, &projectedpoints);
  qh_settempfree(qh, &vertices);
  if ((qh->DOintersections || qh->PRINTridges)
  && (!facet->visible || !qh->NEWfacets)) {
    facet->visitid= qh->visit_id;
    FOREACHridge_(facet->ridges) {
      neighbor= otherfacet_(ridge, facet);
      if (neighbor->visitid != qh->visit_id) {
        if (qh->DOintersections)
          qh_printhyperplaneintersection(qh, fp, facet, neighbor, ridge->vertices, black);
        if (qh->PRINTridges) {
          vertexA= SETfirstt_(ridge->vertices, vertexT);
          vertexB= SETsecondt_(ridge->vertices, vertexT);
          qh_printline3geom(qh, fp, vertexA->point, vertexB->point, green);
        }
      }
    }
  }
} /* printfacet3geom_nonsimplicial */

/*---------------------------------

  qh_printfacet3geom_points(qh, fp, points, facet, offset )
    prints a 3-d facet as OFF Geomview object.
    offset is relative to the facet's hyperplane
    Facet is determined as a list of points
*/
void qh_printfacet3geom_points(qhT *qh, FILE *fp, setT *points, facetT *facet, realT offset, realT color[3]) {
  int k, n= qh_setsize(qh, points), i;
  pointT *point, **pointp;
  setT *printpoints;

  qh_fprintf(qh, fp, 9098, "{ OFF %d 1 1 # f%d\n", n, facet->id);
  if (offset != 0.0) {
    printpoints= qh_settemp(qh, n);
    FOREACHpoint_(points)
      qh_setappend(qh, &printpoints, qh_projectpoint(qh, point, facet, -offset));
  }else
    printpoints= points;
  FOREACHpoint_(printpoints) {
    for (k=0; k < qh->hull_dim; k++) {
      if (k == qh->DROPdim)
        qh_fprintf(qh, fp, 9099, "0 ");
      else
        qh_fprintf(qh, fp, 9100, "%8.4g ", point[k]);
    }
    if (printpoints != points)
      qh_memfree(qh, point, qh->normal_size);
    qh_fprintf(qh, fp, 9101, "\n");
  }
  if (printpoints != points)
    qh_settempfree(qh, &printpoints);
  qh_fprintf(qh, fp, 9102, "%d ", n);
  for (i=0; i < n; i++)
    qh_fprintf(qh, fp, 9103, "%d ", i);
  qh_fprintf(qh, fp, 9104, "%8.4g %8.4g %8.4g 1.0 }\n", color[0], color[1], color[2]);
} /* printfacet3geom_points */


/*---------------------------------

  qh_printfacet3geom_simplicial(qh, )
    print Geomview OFF for a 3-d simplicial facet.

  notes:
    may flip color
    uses facet->visitid for intersections and ridges

    assume precise calculations in io_r.c with roundoff covered by qh_GEOMepsilon
    innerplane may be off by qh->DISTround.  Maxoutside is calculated elsewhere
    so a DISTround error may have occurred.
*/
void qh_printfacet3geom_simplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]) {
  setT *points, *vertices;
  vertexT *vertex, **vertexp, *vertexA, *vertexB;
  facetT *neighbor, **neighborp;
  realT outerplane, innerplane;
  realT black[3]={0, 0, 0}, green[3]={0, 1, 0};
  int k;

  qh_geomplanes(qh, facet, &outerplane, &innerplane);
  vertices= qh_facet3vertex(qh, facet);
  points= qh_settemp(qh, qh->TEMPsize);
  FOREACHvertex_(vertices)
    qh_setappend(qh, &points, vertex->point);
  if (qh->PRINTouter || (!qh->PRINTnoplanes && !qh->PRINTinner))
    qh_printfacet3geom_points(qh, fp, points, facet, outerplane, color);
  if (qh->PRINTinner || (!qh->PRINTnoplanes && !qh->PRINTouter &&
              outerplane - innerplane > 2 * qh->MAXabs_coord * qh_GEOMepsilon)) {
    for (k=3; k--; )
      color[k]= 1.0 - color[k];
    qh_printfacet3geom_points(qh, fp, points, facet, innerplane, color);
  }
  qh_settempfree(qh, &points);
  qh_settempfree(qh, &vertices);
  if ((qh->DOintersections || qh->PRINTridges)
  && (!facet->visible || !qh->NEWfacets)) {
    facet->visitid= qh->visit_id;
    FOREACHneighbor_(facet) {
      if (neighbor->visitid != qh->visit_id) {
        vertices= qh_setnew_delnthsorted(qh, facet->vertices, qh->hull_dim,
                          SETindex_(facet->neighbors, neighbor), 0);
        if (qh->DOintersections)
           qh_printhyperplaneintersection(qh, fp, facet, neighbor, vertices, black);
        if (qh->PRINTridges) {
          vertexA= SETfirstt_(vertices, vertexT);
          vertexB= SETsecondt_(vertices, vertexT);
          qh_printline3geom(qh, fp, vertexA->point, vertexB->point, green);
        }
        qh_setfree(qh, &vertices);
      }
    }
  }
} /* printfacet3geom_simplicial */

/*---------------------------------

  qh_printfacet3math(qh, fp, facet, notfirst )
    print 3-d Maple or Mathematica output for a facet

  notes:
    may be non-simplicial
    use %16.8f since Mathematica 2.2 does not handle exponential format
    see qh_printfacet2math
*/
void qh_printfacet3math(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format, int notfirst) {
  vertexT *vertex, **vertexp;
  setT *points, *vertices;
  pointT *point, **pointp;
  boolT firstpoint= True;
  realT dist;
  const char *pointfmt, *endfmt;

  if (notfirst)
    qh_fprintf(qh, fp, 9105, ",\n");
  vertices= qh_facet3vertex(qh, facet);
  points= qh_settemp(qh, qh_setsize(qh, vertices));
  FOREACHvertex_(vertices) {
    zinc_(Zdistio);
    qh_distplane(qh, vertex->point, facet, &dist);
    point= qh_projectpoint(qh, vertex->point, facet, dist);
    qh_setappend(qh, &points, point);
  }
  if (format == qh_PRINTmaple) {
    qh_fprintf(qh, fp, 9106, "[");
    pointfmt= "[%16.8f, %16.8f, %16.8f]";
    endfmt= "]";
  }else {
    qh_fprintf(qh, fp, 9107, "Polygon[{");
    pointfmt= "{%16.8f, %16.8f, %16.8f}";
    endfmt= "}]";
  }
  FOREACHpoint_(points) {
    if (firstpoint)
      firstpoint= False;
    else
      qh_fprintf(qh, fp, 9108, ",\n");
    qh_fprintf(qh, fp, 9109, pointfmt, point[0], point[1], point[2]);
  }
  FOREACHpoint_(points)
    qh_memfree(qh, point, qh->normal_size);
  qh_settempfree(qh, &points);
  qh_settempfree(qh, &vertices);
  qh_fprintf(qh, fp, 9110, "%s", endfmt);
} /* printfacet3math */


/*---------------------------------

  qh_printfacet3vertex(qh, fp, facet, format )
    print vertices in a 3-d facet as point ids

  notes:
    prints number of vertices first if format == qh_PRINToff
    the facet may be non-simplicial
*/
void qh_printfacet3vertex(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format) {
  vertexT *vertex, **vertexp;
  setT *vertices;

  vertices= qh_facet3vertex(qh, facet);
  if (format == qh_PRINToff)
    qh_fprintf(qh, fp, 9111, "%d ", qh_setsize(qh, vertices));
  FOREACHvertex_(vertices)
    qh_fprintf(qh, fp, 9112, "%d ", qh_pointid(qh, vertex->point));
  qh_fprintf(qh, fp, 9113, "\n");
  qh_settempfree(qh, &vertices);
} /* printfacet3vertex */


/*---------------------------------

  qh_printfacet4geom_nonsimplicial(qh, )
    print Geomview 4OFF file for a 4d nonsimplicial facet
    prints all ridges to unvisited neighbors (qh.visit_id)
    if qh.DROPdim
      prints in OFF format

  notes:
    must agree with printend4geom()
*/
void qh_printfacet4geom_nonsimplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]) {
  facetT *neighbor;
  ridgeT *ridge, **ridgep;
  vertexT *vertex, **vertexp;
  pointT *point;
  int k;
  realT dist;

  facet->visitid= qh->visit_id;
  if (qh->PRINTnoplanes || (facet->visible && qh->NEWfacets))
    return;
  FOREACHridge_(facet->ridges) {
    neighbor= otherfacet_(ridge, facet);
    if (neighbor->visitid == qh->visit_id)
      continue;
    if (qh->PRINTtransparent && !neighbor->good)
      continue;
    if (qh->DOintersections)
      qh_printhyperplaneintersection(qh, fp, facet, neighbor, ridge->vertices, color);
    else {
      if (qh->DROPdim >= 0)
        qh_fprintf(qh, fp, 9114, "OFF 3 1 1 # f%d\n", facet->id);
      else {
        qh->printoutvar++;
        qh_fprintf(qh, fp, 9115, "# r%d between f%d f%d\n", ridge->id, facet->id, neighbor->id);
      }
      FOREACHvertex_(ridge->vertices) {
        zinc_(Zdistio);
        qh_distplane(qh, vertex->point,facet, &dist);
        point=qh_projectpoint(qh, vertex->point,facet, dist);
        for (k=0; k < qh->hull_dim; k++) {
          if (k != qh->DROPdim)
            qh_fprintf(qh, fp, 9116, "%8.4g ", point[k]);
        }
        qh_fprintf(qh, fp, 9117, "\n");
        qh_memfree(qh, point, qh->normal_size);
      }
      if (qh->DROPdim >= 0)
        qh_fprintf(qh, fp, 9118, "3 0 1 2 %8.4g %8.4g %8.4g\n", color[0], color[1], color[2]);
    }
  }
} /* printfacet4geom_nonsimplicial */


/*---------------------------------

  qh_printfacet4geom_simplicial(qh, fp, facet, color )
    print Geomview 4OFF file for a 4d simplicial facet
    prints triangles for unvisited neighbors (qh.visit_id)

  notes:
    must agree with printend4geom()
*/
void qh_printfacet4geom_simplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]) {
  setT *vertices;
  facetT *neighbor, **neighborp;
  vertexT *vertex, **vertexp;
  int k;

  facet->visitid= qh->visit_id;
  if (qh->PRINTnoplanes || (facet->visible && qh->NEWfacets))
    return;
  FOREACHneighbor_(facet) {
    if (neighbor->visitid == qh->visit_id)
      continue;
    if (qh->PRINTtransparent && !neighbor->good)
      continue;
    vertices= qh_setnew_delnthsorted(qh, facet->vertices, qh->hull_dim,
                          SETindex_(facet->neighbors, neighbor), 0);
    if (qh->DOintersections)
      qh_printhyperplaneintersection(qh, fp, facet, neighbor, vertices, color);
    else {
      if (qh->DROPdim >= 0)
        qh_fprintf(qh, fp, 9119, "OFF 3 1 1 # ridge between f%d f%d\n",
                facet->id, neighbor->id);
      else {
        qh->printoutvar++;
        qh_fprintf(qh, fp, 9120, "# ridge between f%d f%d\n", facet->id, neighbor->id);
      }
      FOREACHvertex_(vertices) {
        for (k=0; k < qh->hull_dim; k++) {
          if (k != qh->DROPdim)
            qh_fprintf(qh, fp, 9121, "%8.4g ", vertex->point[k]);
        }
        qh_fprintf(qh, fp, 9122, "\n");
      }
      if (qh->DROPdim >= 0)
        qh_fprintf(qh, fp, 9123, "3 0 1 2 %8.4g %8.4g %8.4g\n", color[0], color[1], color[2]);
    }
    qh_setfree(qh, &vertices);
  }
} /* printfacet4geom_simplicial */


/*---------------------------------

  qh_printfacetNvertex_nonsimplicial(qh, fp, facet, id, format )
    print vertices for an N-d non-simplicial facet
    triangulates each ridge to the id
*/
void qh_printfacetNvertex_nonsimplicial(qhT *qh, FILE *fp, facetT *facet, int id, qh_PRINT format) {
  vertexT *vertex, **vertexp;
  ridgeT *ridge, **ridgep;

  if (facet->visible && qh->NEWfacets)
    return;
  FOREACHridge_(facet->ridges) {
    if (format == qh_PRINTtriangles)
      qh_fprintf(qh, fp, 9124, "%d ", qh->hull_dim);
    qh_fprintf(qh, fp, 9125, "%d ", id);
    if ((ridge->top == facet) ^ qh_ORIENTclock) {
      FOREACHvertex_(ridge->vertices)
        qh_fprintf(qh, fp, 9126, "%d ", qh_pointid(qh, vertex->point));
    }else {
      FOREACHvertexreverse12_(ridge->vertices)
        qh_fprintf(qh, fp, 9127, "%d ", qh_pointid(qh, vertex->point));
    }
    qh_fprintf(qh, fp, 9128, "\n");
  }
} /* printfacetNvertex_nonsimplicial */


/*---------------------------------

  qh_printfacetNvertex_simplicial(qh, fp, facet, format )
    print vertices for an N-d simplicial facet
    prints vertices for non-simplicial facets
      2-d facets (orientation preserved by qh_mergefacet2d)
      PRINToff ('o') for 4-d and higher
*/
void qh_printfacetNvertex_simplicial(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format) {
  vertexT *vertex, **vertexp;

  if (format == qh_PRINToff || format == qh_PRINTtriangles)
    qh_fprintf(qh, fp, 9129, "%d ", qh_setsize(qh, facet->vertices));
  if ((facet->toporient ^ qh_ORIENTclock)
  || (qh->hull_dim > 2 && !facet->simplicial)) {
    FOREACHvertex_(facet->vertices)
      qh_fprintf(qh, fp, 9130, "%d ", qh_pointid(qh, vertex->point));
  }else {
    FOREACHvertexreverse12_(facet->vertices)
      qh_fprintf(qh, fp, 9131, "%d ", qh_pointid(qh, vertex->point));
  }
  qh_fprintf(qh, fp, 9132, "\n");
} /* printfacetNvertex_simplicial */


/*---------------------------------

  qh_printfacetheader(qh, fp, facet )
    prints header fields of a facet to fp

  notes:
    for 'f' output and debugging
    Same as QhullFacet::printHeader()
*/
void qh_printfacetheader(qhT *qh, FILE *fp, facetT *facet) {
  pointT *point, **pointp, *furthest;
  facetT *neighbor, **neighborp;
  realT dist;

  if (facet == qh_MERGEridge) {
    qh_fprintf(qh, fp, 9133, " MERGEridge\n");
    return;
  }else if (facet == qh_DUPLICATEridge) {
    qh_fprintf(qh, fp, 9134, " DUPLICATEridge\n");
    return;
  }else if (!facet) {
    qh_fprintf(qh, fp, 9135, " NULLfacet\n");
    return;
  }
  qh->old_randomdist= qh->RANDOMdist;
  qh->RANDOMdist= False;
  qh_fprintf(qh, fp, 9136, "- f%d\n", facet->id);
  qh_fprintf(qh, fp, 9137, "    - flags:");
  if (facet->toporient)
    qh_fprintf(qh, fp, 9138, " top");
  else
    qh_fprintf(qh, fp, 9139, " bottom");
  if (facet->simplicial)
    qh_fprintf(qh, fp, 9140, " simplicial");
  if (facet->tricoplanar)
    qh_fprintf(qh, fp, 9141, " tricoplanar");
  if (facet->upperdelaunay)
    qh_fprintf(qh, fp, 9142, " upperDelaunay");
  if (facet->visible)
    qh_fprintf(qh, fp, 9143, " visible");
  if (facet->newfacet)
    qh_fprintf(qh, fp, 9144, " new");
  if (facet->tested)
    qh_fprintf(qh, fp, 9145, " tested");
  if (!facet->good)
    qh_fprintf(qh, fp, 9146, " notG");
  if (facet->seen)
    qh_fprintf(qh, fp, 9147, " seen");
  if (facet->coplanar)
    qh_fprintf(qh, fp, 9148, " coplanar");
  if (facet->mergehorizon)
    qh_fprintf(qh, fp, 9149, " mergehorizon");
  if (facet->keepcentrum)
    qh_fprintf(qh, fp, 9150, " keepcentrum");
  if (facet->dupridge)
    qh_fprintf(qh, fp, 9151, " dupridge");
  if (facet->mergeridge && !facet->mergeridge2)
    qh_fprintf(qh, fp, 9152, " mergeridge1");
  if (facet->mergeridge2)
    qh_fprintf(qh, fp, 9153, " mergeridge2");
  if (facet->newmerge)
    qh_fprintf(qh, fp, 9154, " newmerge");
  if (facet->flipped)
    qh_fprintf(qh, fp, 9155, " flipped");
  if (facet->notfurthest)
    qh_fprintf(qh, fp, 9156, " notfurthest");
  if (facet->degenerate)
    qh_fprintf(qh, fp, 9157, " degenerate");
  if (facet->redundant)
    qh_fprintf(qh, fp, 9158, " redundant");
  qh_fprintf(qh, fp, 9159, "\n");
  if (facet->isarea)
    qh_fprintf(qh, fp, 9160, "    - area: %2.2g\n", facet->f.area);
  else if (qh->NEWfacets && facet->visible && facet->f.replace)
    qh_fprintf(qh, fp, 9161, "    - replacement: f%d\n", facet->f.replace->id);
  else if (facet->newfacet) {
    if (facet->f.samecycle && facet->f.samecycle != facet)
      qh_fprintf(qh, fp, 9162, "    - shares same visible/horizon as f%d\n", facet->f.samecycle->id);
  }else if (facet->tricoplanar /* !isarea */) {
    if (facet->f.triowner)
      qh_fprintf(qh, fp, 9163, "    - owner of normal & centrum is facet f%d\n", facet->f.triowner->id);
  }else if (facet->f.newcycle)
    qh_fprintf(qh, fp, 9164, "    - was horizon to f%d\n", facet->f.newcycle->id);
  if (facet->nummerge)
    qh_fprintf(qh, fp, 9165, "    - merges: %d\n", facet->nummerge);
  qh_printpointid(qh, fp, "    - normal: ", qh->hull_dim, facet->normal, qh_IDunknown);
  qh_fprintf(qh, fp, 9166, "    - offset: %10.7g\n", facet->offset);
  if (qh->CENTERtype == qh_ASvoronoi || facet->center)
    qh_printcenter(qh, fp, qh_PRINTfacets, "    - center: ", facet);
#if qh_MAXoutside
  if (facet->maxoutside > qh->DISTround)
    qh_fprintf(qh, fp, 9167, "    - maxoutside: %10.7g\n", facet->maxoutside);
#endif
  if (!SETempty_(facet->outsideset)) {
    furthest= (pointT*)qh_setlast(facet->outsideset);
    if (qh_setsize(qh, facet->outsideset) < 6) {
      qh_fprintf(qh, fp, 9168, "    - outside set(furthest p%d):\n", qh_pointid(qh, furthest));
      FOREACHpoint_(facet->outsideset)
        qh_printpoint(qh, fp, "     ", point);
    }else if (qh_setsize(qh, facet->outsideset) < 21) {
      qh_printpoints(qh, fp, "    - outside set:", facet->outsideset);
    }else {
      qh_fprintf(qh, fp, 9169, "    - outside set:  %d points.", qh_setsize(qh, facet->outsideset));
      qh_printpoint(qh, fp, "  Furthest", furthest);
    }
#if !qh_COMPUTEfurthest
    qh_fprintf(qh, fp, 9170, "    - furthest distance= %2.2g\n", facet->furthestdist);
#endif
  }
  if (!SETempty_(facet->coplanarset)) {
    furthest= (pointT*)qh_setlast(facet->coplanarset);
    if (qh_setsize(qh, facet->coplanarset) < 6) {
      qh_fprintf(qh, fp, 9171, "    - coplanar set(furthest p%d):\n", qh_pointid(qh, furthest));
      FOREACHpoint_(facet->coplanarset)
        qh_printpoint(qh, fp, "     ", point);
    }else if (qh_setsize(qh, facet->coplanarset) < 21) {
      qh_printpoints(qh, fp, "    - coplanar set:", facet->coplanarset);
    }else {
      qh_fprintf(qh, fp, 9172, "    - coplanar set:  %d points.", qh_setsize(qh, facet->coplanarset));
      qh_printpoint(qh, fp, "  Furthest", furthest);
    }
    zinc_(Zdistio);
    qh_distplane(qh, furthest, facet, &dist);
    qh_fprintf(qh, fp, 9173, "      furthest distance= %2.2g\n", dist);
  }
  qh_printvertices(qh, fp, "    - vertices:", facet->vertices);
  qh_fprintf(qh, fp, 9174, "    - neighboring facets:");
  FOREACHneighbor_(facet) {
    if (neighbor == qh_MERGEridge)
      qh_fprintf(qh, fp, 9175, " MERGE");
    else if (neighbor == qh_DUPLICATEridge)
      qh_fprintf(qh, fp, 9176, " DUP");
    else
      qh_fprintf(qh, fp, 9177, " f%d", neighbor->id);
  }
  qh_fprintf(qh, fp, 9178, "\n");
  qh->RANDOMdist= qh->old_randomdist;
} /* printfacetheader */


/*---------------------------------

  qh_printfacetridges(qh, fp, facet )
    prints ridges of a facet to fp

  notes:
    ridges printed in neighbor order
    assumes the ridges exist
    for 'f' output
    same as QhullFacet::printRidges
*/
void qh_printfacetridges(qhT *qh, FILE *fp, facetT *facet) {
  facetT *neighbor, **neighborp;
  ridgeT *ridge, **ridgep;
  int numridges= 0;


  if (facet->visible && qh->NEWfacets) {
    qh_fprintf(qh, fp, 9179, "    - ridges(ids may be garbage):");
    FOREACHridge_(facet->ridges)
      qh_fprintf(qh, fp, 9180, " r%d", ridge->id);
    qh_fprintf(qh, fp, 9181, "\n");
  }else {
    qh_fprintf(qh, fp, 9182, "    - ridges:\n");
    FOREACHridge_(facet->ridges)
      ridge->seen= False;
    if (qh->hull_dim == 3) {
      ridge= SETfirstt_(facet->ridges, ridgeT);
      while (ridge && !ridge->seen) {
        ridge->seen= True;
        qh_printridge(qh, fp, ridge);
        numridges++;
        ridge= qh_nextridge3d(ridge, facet, NULL);
        }
    }else {
      FOREACHneighbor_(facet) {
        FOREACHridge_(facet->ridges) {
          if (otherfacet_(ridge,facet) == neighbor) {
            ridge->seen= True;
            qh_printridge(qh, fp, ridge);
            numridges++;
          }
        }
      }
    }
    if (numridges != qh_setsize(qh, facet->ridges)) {
      qh_fprintf(qh, fp, 9183, "     - all ridges:");
      FOREACHridge_(facet->ridges)
        qh_fprintf(qh, fp, 9184, " r%d", ridge->id);
      qh_fprintf(qh, fp, 9185, "\n");
    }
    FOREACHridge_(facet->ridges) {
      if (!ridge->seen)
        qh_printridge(qh, fp, ridge);
    }
  }
} /* printfacetridges */

/*---------------------------------

  qh_printfacets(qh, fp, format, facetlist, facets, printall )
    prints facetlist and/or facet set in output format

  notes:
    also used for specialized formats ('FO' and summary)
    turns off 'Rn' option since want actual numbers
*/
void qh_printfacets(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {
  int numfacets, numsimplicial, numridges, totneighbors, numcoplanars, numtricoplanars;
  facetT *facet, **facetp;
  setT *vertices;
  coordT *center;
  realT outerplane, innerplane;

  qh->old_randomdist= qh->RANDOMdist;
  qh->RANDOMdist= False;
  if (qh->CDDoutput && (format == qh_PRINTcentrums || format == qh_PRINTpointintersect || format == qh_PRINToff))
    qh_fprintf(qh, qh->ferr, 7056, "qhull warning: CDD format is not available for centrums, halfspace\nintersections, and OFF file format.\n");
  if (format == qh_PRINTnone)
    ; /* print nothing */
  else if (format == qh_PRINTaverage) {
    vertices= qh_facetvertices(qh, facetlist, facets, printall);
    center= qh_getcenter(qh, vertices);
    qh_fprintf(qh, fp, 9186, "%d 1\n", qh->hull_dim);
    qh_printpointid(qh, fp, NULL, qh->hull_dim, center, qh_IDunknown);
    qh_memfree(qh, center, qh->normal_size);
    qh_settempfree(qh, &vertices);
  }else if (format == qh_PRINTextremes) {
    if (qh->DELAUNAY)
      qh_printextremes_d(qh, fp, facetlist, facets, printall);
    else if (qh->hull_dim == 2)
      qh_printextremes_2d(qh, fp, facetlist, facets, printall);
    else
      qh_printextremes(qh, fp, facetlist, facets, printall);
  }else if (format == qh_PRINToptions)
    qh_fprintf(qh, fp, 9187, "Options selected for Qhull %s:\n%s\n", qh_version, qh->qhull_options);
  else if (format == qh_PRINTpoints && !qh->VORONOI)
    qh_printpoints_out(qh, fp, facetlist, facets, printall);
  else if (format == qh_PRINTqhull)
    qh_fprintf(qh, fp, 9188, "%s | %s\n", qh->rbox_command, qh->qhull_command);
  else if (format == qh_PRINTsize) {
    qh_fprintf(qh, fp, 9189, "0\n2 ");
    qh_fprintf(qh, fp, 9190, qh_REAL_1, qh->totarea);
    qh_fprintf(qh, fp, 9191, qh_REAL_1, qh->totvol);
    qh_fprintf(qh, fp, 9192, "\n");
  }else if (format == qh_PRINTsummary) {
    qh_countfacets(qh, facetlist, facets, printall, &numfacets, &numsimplicial,
      &totneighbors, &numridges, &numcoplanars, &numtricoplanars);
    vertices= qh_facetvertices(qh, facetlist, facets, printall);
    qh_fprintf(qh, fp, 9193, "10 %d %d %d %d %d %d %d %d %d %d\n2 ", qh->hull_dim,
                qh->num_points + qh_setsize(qh, qh->other_points),
                qh->num_vertices, qh->num_facets - qh->num_visible,
                qh_setsize(qh, vertices), numfacets, numcoplanars,
                numfacets - numsimplicial, zzval_(Zdelvertextot),
                numtricoplanars);
    qh_settempfree(qh, &vertices);
    qh_outerinner(qh, NULL, &outerplane, &innerplane);
    qh_fprintf(qh, fp, 9194, qh_REAL_2n, outerplane, innerplane);
  }else if (format == qh_PRINTvneighbors)
    qh_printvneighbors(qh, fp, facetlist, facets, printall);
  else if (qh->VORONOI && format == qh_PRINToff)
    qh_printvoronoi(qh, fp, format, facetlist, facets, printall);
  else if (qh->VORONOI && format == qh_PRINTgeom) {
    qh_printbegin(qh, fp, format, facetlist, facets, printall);
    qh_printvoronoi(qh, fp, format, facetlist, facets, printall);
    qh_printend(qh, fp, format, facetlist, facets, printall);
  }else if (qh->VORONOI
  && (format == qh_PRINTvertices || format == qh_PRINTinner || format == qh_PRINTouter))
    qh_printvdiagram(qh, fp, format, facetlist, facets, printall);
  else {
    qh_printbegin(qh, fp, format, facetlist, facets, printall);
    FORALLfacet_(facetlist)
      qh_printafacet(qh, fp, format, facet, printall);
    FOREACHfacet_(facets)
      qh_printafacet(qh, fp, format, facet, printall);
    qh_printend(qh, fp, format, facetlist, facets, printall);
  }
  qh->RANDOMdist= qh->old_randomdist;
} /* printfacets */


/*---------------------------------

  qh_printhyperplaneintersection(qh, fp, facet1, facet2, vertices, color )
    print Geomview OFF or 4OFF for the intersection of two hyperplanes in 3-d or 4-d
*/
void qh_printhyperplaneintersection(qhT *qh, FILE *fp, facetT *facet1, facetT *facet2,
                   setT *vertices, realT color[3]) {
  realT costheta, denominator, dist1, dist2, s, t, mindenom, p[4];
  vertexT *vertex, **vertexp;
  int i, k;
  boolT nearzero1, nearzero2;

  costheta= qh_getangle(qh, facet1->normal, facet2->normal);
  denominator= 1 - costheta * costheta;
  i= qh_setsize(qh, vertices);
  if (qh->hull_dim == 3)
    qh_fprintf(qh, fp, 9195, "VECT 1 %d 1 %d 1 ", i, i);
  else if (qh->hull_dim == 4 && qh->DROPdim >= 0)
    qh_fprintf(qh, fp, 9196, "OFF 3 1 1 ");
  else
    qh->printoutvar++;
  qh_fprintf(qh, fp, 9197, "# intersect f%d f%d\n", facet1->id, facet2->id);
  mindenom= 1 / (10.0 * qh->MAXabs_coord);
  FOREACHvertex_(vertices) {
    zadd_(Zdistio, 2);
    qh_distplane(qh, vertex->point, facet1, &dist1);
    qh_distplane(qh, vertex->point, facet2, &dist2);
    s= qh_divzero(-dist1 + costheta * dist2, denominator,mindenom,&nearzero1);
    t= qh_divzero(-dist2 + costheta * dist1, denominator,mindenom,&nearzero2);
    if (nearzero1 || nearzero2)
      s= t= 0.0;
    for (k=qh->hull_dim; k--; )
      p[k]= vertex->point[k] + facet1->normal[k] * s + facet2->normal[k] * t;
    if (qh->PRINTdim <= 3) {
      qh_projectdim3(qh, p, p);
      qh_fprintf(qh, fp, 9198, "%8.4g %8.4g %8.4g # ", p[0], p[1], p[2]);
    }else
      qh_fprintf(qh, fp, 9199, "%8.4g %8.4g %8.4g %8.4g # ", p[0], p[1], p[2], p[3]);
    if (nearzero1+nearzero2)
      qh_fprintf(qh, fp, 9200, "p%d(coplanar facets)\n", qh_pointid(qh, vertex->point));
    else
      qh_fprintf(qh, fp, 9201, "projected p%d\n", qh_pointid(qh, vertex->point));
  }
  if (qh->hull_dim == 3)
    qh_fprintf(qh, fp, 9202, "%8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]);
  else if (qh->hull_dim == 4 && qh->DROPdim >= 0)
    qh_fprintf(qh, fp, 9203, "3 0 1 2 %8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]);
} /* printhyperplaneintersection */

/*---------------------------------

  qh_printline3geom(qh, fp, pointA, pointB, color )
    prints a line as a VECT
    prints 0's for qh.DROPdim

  notes:
    if pointA == pointB,
      it's a 1 point VECT
*/
void qh_printline3geom(qhT *qh, FILE *fp, pointT *pointA, pointT *pointB, realT color[3]) {
  int k;
  realT pA[4], pB[4];

  qh_projectdim3(qh, pointA, pA);
  qh_projectdim3(qh, pointB, pB);
  if ((fabs(pA[0] - pB[0]) > 1e-3) ||
      (fabs(pA[1] - pB[1]) > 1e-3) ||
      (fabs(pA[2] - pB[2]) > 1e-3)) {
    qh_fprintf(qh, fp, 9204, "VECT 1 2 1 2 1\n");
    for (k=0; k < 3; k++)
       qh_fprintf(qh, fp, 9205, "%8.4g ", pB[k]);
    qh_fprintf(qh, fp, 9206, " # p%d\n", qh_pointid(qh, pointB));
  }else
    qh_fprintf(qh, fp, 9207, "VECT 1 1 1 1 1\n");
  for (k=0; k < 3; k++)
    qh_fprintf(qh, fp, 9208, "%8.4g ", pA[k]);
  qh_fprintf(qh, fp, 9209, " # p%d\n", qh_pointid(qh, pointA));
  qh_fprintf(qh, fp, 9210, "%8.4g %8.4g %8.4g 1\n", color[0], color[1], color[2]);
}

/*---------------------------------

  qh_printneighborhood(qh, fp, format, facetA, facetB, printall )
    print neighborhood of one or two facets

  notes:
    calls qh_findgood_all()
    bumps qh.visit_id
*/
void qh_printneighborhood(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetA, facetT *facetB, boolT printall) {
  facetT *neighbor, **neighborp, *facet;
  setT *facets;

  if (format == qh_PRINTnone)
    return;
  qh_findgood_all(qh, qh->facet_list);
  if (facetA == facetB)
    facetB= NULL;
  facets= qh_settemp(qh, 2*(qh_setsize(qh, facetA->neighbors)+1));
  qh->visit_id++;
  for (facet= facetA; facet; facet= ((facet == facetA) ? facetB : NULL)) {
    if (facet->visitid != qh->visit_id) {
      facet->visitid= qh->visit_id;
      qh_setappend(qh, &facets, facet);
    }
    FOREACHneighbor_(facet) {
      if (neighbor->visitid == qh->visit_id)
        continue;
      neighbor->visitid= qh->visit_id;
      if (printall || !qh_skipfacet(qh, neighbor))
        qh_setappend(qh, &facets, neighbor);
    }
  }
  qh_printfacets(qh, fp, format, NULL, facets, printall);
  qh_settempfree(qh, &facets);
} /* printneighborhood */

/*---------------------------------

  qh_printpoint(qh, fp, string, point )
  qh_printpointid(qh, fp, string, dim, point, id )
    prints the coordinates of a point

  returns:
    if string is defined
      prints 'string p%d'.  Skips p%d if id=qh_IDunknown(-1) or qh_IDnone(-3)

  notes:
    nop if point is NULL
    Same as QhullPoint's printPoint
*/
void qh_printpoint(qhT *qh, FILE *fp, const char *string, pointT *point) {
  int id= qh_pointid(qh, point);

  qh_printpointid(qh, fp, string, qh->hull_dim, point, id);
} /* printpoint */

void qh_printpointid(qhT *qh, FILE *fp, const char *string, int dim, pointT *point, int id) {
  int k;
  realT r; /*bug fix*/

  if (!point)
    return;
  if (string) {
    qh_fprintf(qh, fp, 9211, "%s", string);
    if (id != qh_IDunknown && id != qh_IDnone)
      qh_fprintf(qh, fp, 9212, " p%d: ", id);
  }
  for (k=dim; k--; ) {
    r= *point++;
    if (string)
      qh_fprintf(qh, fp, 9213, " %8.4g", r);
    else
      qh_fprintf(qh, fp, 9214, qh_REAL_1, r);
  }
  qh_fprintf(qh, fp, 9215, "\n");
} /* printpointid */

/*---------------------------------

  qh_printpoint3(qh, fp, point )
    prints 2-d, 3-d, or 4-d point as Geomview 3-d coordinates
*/
void qh_printpoint3(qhT *qh, FILE *fp, pointT *point) {
  int k;
  realT p[4];

  qh_projectdim3(qh, point, p);
  for (k=0; k < 3; k++)
    qh_fprintf(qh, fp, 9216, "%8.4g ", p[k]);
  qh_fprintf(qh, fp, 9217, " # p%d\n", qh_pointid(qh, point));
} /* printpoint3 */

/*----------------------------------------
-printpoints- print pointids for a set of points starting at index
   see geom_r.c
*/

/*---------------------------------

  qh_printpoints_out(qh, fp, facetlist, facets, printall )
    prints vertices, coplanar/inside points, for facets by their point coordinates
    allows qh.CDDoutput

  notes:
    same format as qhull input
    if no coplanar/interior points,
      same order as qh_printextremes
*/
void qh_printpoints_out(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall) {
  int allpoints= qh->num_points + qh_setsize(qh, qh->other_points);
  int numpoints=0, point_i, point_n;
  setT *vertices, *points;
  facetT *facet, **facetp;
  pointT *point, **pointp;
  vertexT *vertex, **vertexp;
  int id;

  points= qh_settemp(qh, allpoints);
  qh_setzero(qh, points, 0, allpoints);
  vertices= qh_facetvertices(qh, facetlist, facets, printall);
  FOREACHvertex_(vertices) {
    id= qh_pointid(qh, vertex->point);
    if (id >= 0)
      SETelem_(points, id)= vertex->point;
  }
  if (qh->KEEPinside || qh->KEEPcoplanar || qh->KEEPnearinside) {
    FORALLfacet_(facetlist) {
      if (!printall && qh_skipfacet(qh, facet))
        continue;
      FOREACHpoint_(facet->coplanarset) {
        id= qh_pointid(qh, point);
        if (id >= 0)
          SETelem_(points, id)= point;
      }
    }
    FOREACHfacet_(facets) {
      if (!printall && qh_skipfacet(qh, facet))
        continue;
      FOREACHpoint_(facet->coplanarset) {
        id= qh_pointid(qh, point);
        if (id >= 0)
          SETelem_(points, id)= point;
      }
    }
  }
  qh_settempfree(qh, &vertices);
  FOREACHpoint_i_(qh, points) {
    if (point)
      numpoints++;
  }
  if (qh->CDDoutput)
    qh_fprintf(qh, fp, 9218, "%s | %s\nbegin\n%d %d real\n", qh->rbox_command,
             qh->qhull_command, numpoints, qh->hull_dim + 1);
  else
    qh_fprintf(qh, fp, 9219, "%d\n%d\n", qh->hull_dim, numpoints);
  FOREACHpoint_i_(qh, points) {
    if (point) {
      if (qh->CDDoutput)
        qh_fprintf(qh, fp, 9220, "1 ");
      qh_printpoint(qh, fp, NULL, point);
    }
  }
  if (qh->CDDoutput)
    qh_fprintf(qh, fp, 9221, "end\n");
  qh_settempfree(qh, &points);
} /* printpoints_out */


/*---------------------------------

  qh_printpointvect(qh, fp, point, normal, center, radius, color )
    prints a 2-d, 3-d, or 4-d point as 3-d VECT's relative to normal or to center point
*/
void qh_printpointvect(qhT *qh, FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius, realT color[3]) {
  realT diff[4], pointA[4];
  int k;

  for (k=qh->hull_dim; k--; ) {
    if (center)
      diff[k]= point[k]-center[k];
    else if (normal)
      diff[k]= normal[k];
    else
      diff[k]= 0;
  }
  if (center)
    qh_normalize2(qh, diff, qh->hull_dim, True, NULL, NULL);
  for (k=qh->hull_dim; k--; )
    pointA[k]= point[k]+diff[k] * radius;
  qh_printline3geom(qh, fp, point, pointA, color);
} /* printpointvect */

/*---------------------------------

  qh_printpointvect2(qh, fp, point, normal, center, radius )
    prints a 2-d, 3-d, or 4-d point as 2 3-d VECT's for an imprecise point
*/
void qh_printpointvect2(qhT *qh, FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius) {
  realT red[3]={1, 0, 0}, yellow[3]={1, 1, 0};

  qh_printpointvect(qh, fp, point, normal, center, radius, red);
  qh_printpointvect(qh, fp, point, normal, center, -radius, yellow);
} /* printpointvect2 */

/*---------------------------------

  qh_printridge(qh, fp, ridge )
    prints the information in a ridge

  notes:
    for qh_printfacetridges()
    same as operator<< [QhullRidge.cpp]
*/
void qh_printridge(qhT *qh, FILE *fp, ridgeT *ridge) {

  qh_fprintf(qh, fp, 9222, "     - r%d", ridge->id);
  if (ridge->tested)
    qh_fprintf(qh, fp, 9223, " tested");
  if (ridge->nonconvex)
    qh_fprintf(qh, fp, 9224, " nonconvex");
  qh_fprintf(qh, fp, 9225, "\n");
  qh_printvertices(qh, fp, "           vertices:", ridge->vertices);
  if (ridge->top && ridge->bottom)
    qh_fprintf(qh, fp, 9226, "           between f%d and f%d\n",
            ridge->top->id, ridge->bottom->id);
} /* printridge */

/*---------------------------------

  qh_printspheres(qh, fp, vertices, radius )
    prints 3-d vertices as OFF spheres

  notes:
    inflated octahedron from Stuart Levy earth/mksphere2
*/
void qh_printspheres(qhT *qh, FILE *fp, setT *vertices, realT radius) {
  vertexT *vertex, **vertexp;

  qh->printoutnum++;
  qh_fprintf(qh, fp, 9227, "{appearance {-edge -normal normscale 0} {\n\
INST geom {define vsphere OFF\n\
18 32 48\n\
\n\
0 0 1\n\
1 0 0\n\
0 1 0\n\
-1 0 0\n\
0 -1 0\n\
0 0 -1\n\
0.707107 0 0.707107\n\
0 -0.707107 0.707107\n\
0.707107 -0.707107 0\n\
-0.707107 0 0.707107\n\
-0.707107 -0.707107 0\n\
0 0.707107 0.707107\n\
-0.707107 0.707107 0\n\
0.707107 0.707107 0\n\
0.707107 0 -0.707107\n\
0 0.707107 -0.707107\n\
-0.707107 0 -0.707107\n\
0 -0.707107 -0.707107\n\
\n\
3 0 6 11\n\
3 0 7 6 \n\
3 0 9 7 \n\
3 0 11 9\n\
3 1 6 8 \n\
3 1 8 14\n\
3 1 13 6\n\
3 1 14 13\n\
3 2 11 13\n\
3 2 12 11\n\
3 2 13 15\n\
3 2 15 12\n\
3 3 9 12\n\
3 3 10 9\n\
3 3 12 16\n\
3 3 16 10\n\
3 4 7 10\n\
3 4 8 7\n\
3 4 10 17\n\
3 4 17 8\n\
3 5 14 17\n\
3 5 15 14\n\
3 5 16 15\n\
3 5 17 16\n\
3 6 13 11\n\
3 7 8 6\n\
3 9 10 7\n\
3 11 12 9\n\
3 14 8 17\n\
3 15 13 14\n\
3 16 12 15\n\
3 17 10 16\n} transforms { TLIST\n");
  FOREACHvertex_(vertices) {
    qh_fprintf(qh, fp, 9228, "%8.4g 0 0 0 # v%d\n 0 %8.4g 0 0\n0 0 %8.4g 0\n",
      radius, vertex->id, radius, radius);
    qh_printpoint3(qh, fp, vertex->point);
    qh_fprintf(qh, fp, 9229, "1\n");
  }
  qh_fprintf(qh, fp, 9230, "}}}\n");
} /* printspheres */


/*----------------------------------------------
-printsummary-
                see libqhull_r.c
*/

/*---------------------------------

  qh_printvdiagram(qh, fp, format, facetlist, facets, printall )
    print voronoi diagram
      # of pairs of input sites
      #indices site1 site2 vertex1 ...

    sites indexed by input point id
      point 0 is the first input point
    vertices indexed by 'o' and 'p' order
      vertex 0 is the 'vertex-at-infinity'
      vertex 1 is the first Voronoi vertex

  see:
    qh_printvoronoi()
    qh_eachvoronoi_all()

  notes:
    if all facets are upperdelaunay,
      prints upper hull (furthest-site Voronoi diagram)
*/
void qh_printvdiagram(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {
  setT *vertices;
  int totcount, numcenters;
  boolT isLower;
  qh_RIDGE innerouter= qh_RIDGEall;
  printvridgeT printvridge= NULL;

  if (format == qh_PRINTvertices) {
    innerouter= qh_RIDGEall;
    printvridge= qh_printvridge;
  }else if (format == qh_PRINTinner) {
    innerouter= qh_RIDGEinner;
    printvridge= qh_printvnorm;
  }else if (format == qh_PRINTouter) {
    innerouter= qh_RIDGEouter;
    printvridge= qh_printvnorm;
  }else {
    qh_fprintf(qh, qh->ferr, 6219, "Qhull internal error (qh_printvdiagram): unknown print format %d.\n", format);
    qh_errexit(qh, qh_ERRinput, NULL, NULL);
  }
  vertices= qh_markvoronoi(qh, facetlist, facets, printall, &isLower, &numcenters);
  totcount= qh_printvdiagram2(qh, NULL, NULL, vertices, innerouter, False);
  qh_fprintf(qh, fp, 9231, "%d\n", totcount);
  totcount= qh_printvdiagram2(qh, fp, printvridge, vertices, innerouter, True /* inorder*/);
  qh_settempfree(qh, &vertices);
#if 0  /* for testing qh_eachvoronoi_all */
  qh_fprintf(qh, fp, 9232, "\n");
  totcount= qh_eachvoronoi_all(qh, fp, printvridge, qh->UPPERdelaunay, innerouter, True /* inorder*/);
  qh_fprintf(qh, fp, 9233, "%d\n", totcount);
#endif
} /* printvdiagram */

/*---------------------------------

  qh_printvdiagram2(qh, fp, printvridge, vertices, innerouter, inorder )
    visit all pairs of input sites (vertices) for selected Voronoi vertices
    vertices may include NULLs

  innerouter:
    qh_RIDGEall   print inner ridges(bounded) and outer ridges(unbounded)
    qh_RIDGEinner print only inner ridges
    qh_RIDGEouter print only outer ridges

  inorder:
    print 3-d Voronoi vertices in order

  assumes:
    qh_markvoronoi marked facet->visitid for Voronoi vertices
    all facet->seen= False
    all facet->seen2= True

  returns:
    total number of Voronoi ridges
    if printvridge,
      calls printvridge( fp, vertex, vertexA, centers) for each ridge
      [see qh_eachvoronoi()]

  see:
    qh_eachvoronoi_all()
*/
int qh_printvdiagram2(qhT *qh, FILE *fp, printvridgeT printvridge, setT *vertices, qh_RIDGE innerouter, boolT inorder) {
  int totcount= 0;
  int vertex_i, vertex_n;
  vertexT *vertex;

  FORALLvertices
    vertex->seen= False;
  FOREACHvertex_i_(qh, vertices) {
    if (vertex) {
      if (qh->GOODvertex > 0 && qh_pointid(qh, vertex->point)+1 != qh->GOODvertex)
        continue;
      totcount += qh_eachvoronoi(qh, fp, printvridge, vertex, !qh_ALL, innerouter, inorder);
    }
  }
  return totcount;
} /* printvdiagram2 */

/*---------------------------------

  qh_printvertex(qh, fp, vertex )
    prints the information in a vertex
    Duplicated as operator<< [QhullVertex.cpp]
*/
void qh_printvertex(qhT *qh, FILE *fp, vertexT *vertex) {
  pointT *point;
  int k, count= 0;
  facetT *neighbor, **neighborp;
  realT r; /*bug fix*/

  if (!vertex) {
    qh_fprintf(qh, fp, 9234, "  NULLvertex\n");
    return;
  }
  qh_fprintf(qh, fp, 9235, "- p%d(v%d):", qh_pointid(qh, vertex->point), vertex->id);
  point= vertex->point;
  if (point) {
    for (k=qh->hull_dim; k--; ) {
      r= *point++;
      qh_fprintf(qh, fp, 9236, " %5.2g", r);
    }
  }
  if (vertex->deleted)
    qh_fprintf(qh, fp, 9237, " deleted");
  if (vertex->delridge)
    qh_fprintf(qh, fp, 9238, " ridgedeleted");
  qh_fprintf(qh, fp, 9239, "\n");
  if (vertex->neighbors) {
    qh_fprintf(qh, fp, 9240, "  neighbors:");
    FOREACHneighbor_(vertex) {
      if (++count % 100 == 0)
        qh_fprintf(qh, fp, 9241, "\n     ");
      qh_fprintf(qh, fp, 9242, " f%d", neighbor->id);
    }
    qh_fprintf(qh, fp, 9243, "\n");
  }
} /* printvertex */


/*---------------------------------

  qh_printvertexlist(qh, fp, string, facetlist, facets, printall )
    prints vertices used by a facetlist or facet set
    tests qh_skipfacet() if !printall
*/
void qh_printvertexlist(qhT *qh, FILE *fp, const char* string, facetT *facetlist,
                         setT *facets, boolT printall) {
  vertexT *vertex, **vertexp;
  setT *vertices;

  vertices= qh_facetvertices(qh, facetlist, facets, printall);
  qh_fprintf(qh, fp, 9244, "%s", string);
  FOREACHvertex_(vertices)
    qh_printvertex(qh, fp, vertex);
  qh_settempfree(qh, &vertices);
} /* printvertexlist */


/*---------------------------------

  qh_printvertices(qh, fp, string, vertices )
    prints vertices in a set
    duplicated as printVertexSet [QhullVertex.cpp]
*/
void qh_printvertices(qhT *qh, FILE *fp, const char* string, setT *vertices) {
  vertexT *vertex, **vertexp;

  qh_fprintf(qh, fp, 9245, "%s", string);
  FOREACHvertex_(vertices)
    qh_fprintf(qh, fp, 9246, " p%d(v%d)", qh_pointid(qh, vertex->point), vertex->id);
  qh_fprintf(qh, fp, 9247, "\n");
} /* printvertices */

/*---------------------------------

  qh_printvneighbors(qh, fp, facetlist, facets, printall )
    print vertex neighbors of vertices in facetlist and facets ('FN')

  notes:
    qh_countfacets clears facet->visitid for non-printed facets

  design:
    collect facet count and related statistics
    if necessary, build neighbor sets for each vertex
    collect vertices in facetlist and facets
    build a point array for point->vertex and point->coplanar facet
    for each point
      list vertex neighbors or coplanar facet
*/
void qh_printvneighbors(qhT *qh, FILE *fp, facetT* facetlist, setT *facets, boolT printall) {
  int numfacets, numsimplicial, numridges, totneighbors, numneighbors, numcoplanars, numtricoplanars;
  setT *vertices, *vertex_points, *coplanar_points;
  int numpoints= qh->num_points + qh_setsize(qh, qh->other_points);
  vertexT *vertex, **vertexp;
  int vertex_i, vertex_n;
  facetT *facet, **facetp, *neighbor, **neighborp;
  pointT *point, **pointp;

  qh_countfacets(qh, facetlist, facets, printall, &numfacets, &numsimplicial,
      &totneighbors, &numridges, &numcoplanars, &numtricoplanars);  /* sets facet->visitid */
  qh_fprintf(qh, fp, 9248, "%d\n", numpoints);
  qh_vertexneighbors(qh);
  vertices= qh_facetvertices(qh, facetlist, facets, printall);
  vertex_points= qh_settemp(qh, numpoints);
  coplanar_points= qh_settemp(qh, numpoints);
  qh_setzero(qh, vertex_points, 0, numpoints);
  qh_setzero(qh, coplanar_points, 0, numpoints);
  FOREACHvertex_(vertices)
    qh_point_add(qh, vertex_points, vertex->point, vertex);
  FORALLfacet_(facetlist) {
    FOREACHpoint_(facet->coplanarset)
      qh_point_add(qh, coplanar_points, point, facet);
  }
  FOREACHfacet_(facets) {
    FOREACHpoint_(facet->coplanarset)
      qh_point_add(qh, coplanar_points, point, facet);
  }
  FOREACHvertex_i_(qh, vertex_points) {
    if (vertex) {
      numneighbors= qh_setsize(qh, vertex->neighbors);
      qh_fprintf(qh, fp, 9249, "%d", numneighbors);
      if (qh->hull_dim == 3)
        qh_order_vertexneighbors(qh, vertex);
      else if (qh->hull_dim >= 4)
        qsort(SETaddr_(vertex->neighbors, facetT), (size_t)numneighbors,
             sizeof(facetT *), qh_compare_facetvisit);
      FOREACHneighbor_(vertex)
        qh_fprintf(qh, fp, 9250, " %d",
                 neighbor->visitid ? neighbor->visitid - 1 : 0 - neighbor->id);
      qh_fprintf(qh, fp, 9251, "\n");
    }else if ((facet= SETelemt_(coplanar_points, vertex_i, facetT)))
      qh_fprintf(qh, fp, 9252, "1 %d\n",
                  facet->visitid ? facet->visitid - 1 : 0 - facet->id);
    else
      qh_fprintf(qh, fp, 9253, "0\n");
  }
  qh_settempfree(qh, &coplanar_points);
  qh_settempfree(qh, &vertex_points);
  qh_settempfree(qh, &vertices);
} /* printvneighbors */

/*---------------------------------

  qh_printvoronoi(qh, fp, format, facetlist, facets, printall )
    print voronoi diagram in 'o' or 'G' format
    for 'o' format
      prints voronoi centers for each facet and for infinity
      for each vertex, lists ids of printed facets or infinity
      assumes facetlist and facets are disjoint
    for 'G' format
      prints an OFF object
      adds a 0 coordinate to center
      prints infinity but does not list in vertices

  see:
    qh_printvdiagram()

  notes:
    if 'o',
      prints a line for each point except "at-infinity"
    if all facets are upperdelaunay,
      reverses lower and upper hull
*/
void qh_printvoronoi(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {
  int k, numcenters, numvertices= 0, numneighbors, numinf, vid=1, vertex_i, vertex_n;
  facetT *facet, **facetp, *neighbor, **neighborp;
  setT *vertices;
  vertexT *vertex;
  boolT isLower;
  unsigned int numfacets= (unsigned int) qh->num_facets;

  vertices= qh_markvoronoi(qh, facetlist, facets, printall, &isLower, &numcenters);
  FOREACHvertex_i_(qh, vertices) {
    if (vertex) {
      numvertices++;
      numneighbors = numinf = 0;
      FOREACHneighbor_(vertex) {
        if (neighbor->visitid == 0)
          numinf= 1;
        else if (neighbor->visitid < numfacets)
          numneighbors++;
      }
      if (numinf && !numneighbors) {
        SETelem_(vertices, vertex_i)= NULL;
        numvertices--;
      }
    }
  }
  if (format == qh_PRINTgeom)
    qh_fprintf(qh, fp, 9254, "{appearance {+edge -face} OFF %d %d 1 # Voronoi centers and cells\n",
                numcenters, numvertices);
  else
    qh_fprintf(qh, fp, 9255, "%d\n%d %d 1\n", qh->hull_dim-1, numcenters, qh_setsize(qh, vertices));
  if (format == qh_PRINTgeom) {
    for (k=qh->hull_dim-1; k--; )
      qh_fprintf(qh, fp, 9256, qh_REAL_1, 0.0);
    qh_fprintf(qh, fp, 9257, " 0 # infinity not used\n");
  }else {
    for (k=qh->hull_dim-1; k--; )
      qh_fprintf(qh, fp, 9258, qh_REAL_1, qh_INFINITE);
    qh_fprintf(qh, fp, 9259, "\n");
  }
  FORALLfacet_(facetlist) {
    if (facet->visitid && facet->visitid < numfacets) {
      if (format == qh_PRINTgeom)
        qh_fprintf(qh, fp, 9260, "# %d f%d\n", vid++, facet->id);
      qh_printcenter(qh, fp, format, NULL, facet);
    }
  }
  FOREACHfacet_(facets) {
    if (facet->visitid && facet->visitid < numfacets) {
      if (format == qh_PRINTgeom)
        qh_fprintf(qh, fp, 9261, "# %d f%d\n", vid++, facet->id);
      qh_printcenter(qh, fp, format, NULL, facet);
    }
  }
  FOREACHvertex_i_(qh, vertices) {
    numneighbors= 0;
    numinf=0;
    if (vertex) {
      if (qh->hull_dim == 3)
        qh_order_vertexneighbors(qh, vertex);
      else if (qh->hull_dim >= 4)
        qsort(SETaddr_(vertex->neighbors, facetT),
             (size_t)qh_setsize(qh, 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(qh, fp, 9262, "%d", numneighbors);
        FOREACHneighbor_(vertex) {
          if (neighbor->visitid && neighbor->visitid < numfacets)
            qh_fprintf(qh, fp, 9263, " %d", neighbor->visitid);
        }
        qh_fprintf(qh, fp, 9264, " # p%d(v%d)\n", vertex_i, vertex->id);
      }else
        qh_fprintf(qh, fp, 9265, " # p%d is coplanar or isolated\n", vertex_i);
    }else {
      if (numinf)
        numneighbors++;
      qh_fprintf(qh, fp, 9266, "%d", numneighbors);
      if (vertex) {
        FOREACHneighbor_(vertex) {
          if (neighbor->visitid == 0) {
            if (numinf) {
              numinf= 0;
              qh_fprintf(qh, fp, 9267, " %d", neighbor->visitid);
            }
          }else if (neighbor->visitid < numfacets)
            qh_fprintf(qh, fp, 9268, " %d", neighbor->visitid);
        }
      }
      qh_fprintf(qh, fp, 9269, "\n");
    }
  }
  if (format == qh_PRINTgeom)
    qh_fprintf(qh, fp, 9270, "}\n");
  qh_settempfree(qh, &vertices);
} /* printvoronoi */

/*---------------------------------

  qh_printvnorm(qh, fp, vertex, vertexA, centers, unbounded )
    print one separating plane of the Voronoi diagram for a pair of input sites
    unbounded==True if centers includes vertex-at-infinity

  assumes:
    qh_ASvoronoi and qh_vertexneighbors() already set

  note:
    parameter unbounded is UNUSED by this callback

  see:
    qh_printvdiagram()
    qh_eachvoronoi()
*/
void qh_printvnorm(qhT *qh, FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded) {
  pointT *normal;
  realT offset;
  int k;
  QHULL_UNUSED(unbounded);

  normal= qh_detvnorm(qh, vertex, vertexA, centers, &offset);
  qh_fprintf(qh, fp, 9271, "%d %d %d ",
      2+qh->hull_dim, qh_pointid(qh, vertex->point), qh_pointid(qh, vertexA->point));
  for (k=0; k< qh->hull_dim-1; k++)
    qh_fprintf(qh, fp, 9272, qh_REAL_1, normal[k]);
  qh_fprintf(qh, fp, 9273, qh_REAL_1, offset);
  qh_fprintf(qh, fp, 9274, "\n");
} /* printvnorm */

/*---------------------------------

  qh_printvridge(qh, fp, vertex, vertexA, centers, unbounded )
    print one ridge of the Voronoi diagram for a pair of input sites
    unbounded==True if centers includes vertex-at-infinity

  see:
    qh_printvdiagram()

  notes:
    the user may use a different function
    parameter unbounded is UNUSED
*/
void qh_printvridge(qhT *qh, FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded) {
  facetT *facet, **facetp;
  QHULL_UNUSED(unbounded);

  qh_fprintf(qh, fp, 9275, "%d %d %d", qh_setsize(qh, centers)+2,
       qh_pointid(qh, vertex->point), qh_pointid(qh, vertexA->point));
  FOREACHfacet_(centers)
    qh_fprintf(qh, fp, 9276, " %d", facet->visitid);
  qh_fprintf(qh, fp, 9277, "\n");
} /* printvridge */

/*---------------------------------

  qh_projectdim3(qh, source, destination )
    project 2-d 3-d or 4-d point to a 3-d point
    uses qh.DROPdim and qh.hull_dim
    source and destination may be the same

  notes:
    allocate 4 elements to destination just in case
*/
void qh_projectdim3(qhT *qh, pointT *source, pointT *destination) {
  int i,k;

  for (k=0, i=0; k < qh->hull_dim; k++) {
    if (qh->hull_dim == 4) {
      if (k != qh->DROPdim)
        destination[i++]= source[k];
    }else if (k == qh->DROPdim)
      destination[i++]= 0;
    else
      destination[i++]= source[k];
  }
  while (i < 3)
    destination[i++]= 0.0;
} /* projectdim3 */

/*---------------------------------

  qh_readfeasible(qh, dim, curline )
    read feasible point from current line and qh.fin

  returns:
    number of lines read from qh.fin
    sets qh.feasible_point with malloc'd coordinates

  notes:
    checks for qh.HALFspace
    assumes dim > 1

  see:
    qh_setfeasible
*/
int qh_readfeasible(qhT *qh, int dim, const char *curline) {
  boolT isfirst= True;
  int linecount= 0, tokcount= 0;
  const char *s;
  char *t, firstline[qh_MAXfirst+1];
  coordT *coords, value;

  if (!qh->HALFspace) {
    qh_fprintf(qh, qh->ferr, 6070, "qhull input error: feasible point(dim 1 coords) is only valid for halfspace intersection\n");
    qh_errexit(qh, qh_ERRinput, NULL, NULL);
  }
  if (qh->feasible_string)
    qh_fprintf(qh, qh->ferr, 7057, "qhull input warning: feasible point(dim 1 coords) overrides 'Hn,n,n' feasible point for halfspace intersection\n");
  if (!(qh->feasible_point= (coordT*)qh_malloc(dim* sizeof(coordT)))) {
    qh_fprintf(qh, qh->ferr, 6071, "qhull error: insufficient memory for feasible point\n");
    qh_errexit(qh, qh_ERRmem, NULL, NULL);
  }
  coords= qh->feasible_point;
  while ((s= (isfirst ?  curline : fgets(firstline, qh_MAXfirst, qh->fin)))) {
    if (isfirst)
      isfirst= False;
    else
      linecount++;
    while (*s) {
      while (isspace(*s))
        s++;
      value= qh_strtod(s, &t);
      if (s == t)
        break;
      s= t;
      *(coords++)= value;
      if (++tokcount == dim) {
        while (isspace(*s))
          s++;
        qh_strtod(s, &t);
        if (s != t) {
          qh_fprintf(qh, qh->ferr, 6072, "qhull input error: coordinates for feasible point do not finish out the line: %s\n",
               s);
          qh_errexit(qh, qh_ERRinput, NULL, NULL);
        }
        return linecount;
      }
    }
  }
  qh_fprintf(qh, qh->ferr, 6073, "qhull input error: only %d coordinates.  Could not read %d-d feasible point.\n",
           tokcount, dim);
  qh_errexit(qh, qh_ERRinput, NULL, NULL);
  return 0;
} /* readfeasible */

/*---------------------------------

  qh_readpoints(qh, numpoints, dimension, ismalloc )
    read points from qh.fin into qh.first_point, qh.num_points
    qh.fin is lines of coordinates, one per vertex, first line number of points
    if 'rbox D4',
      gives message
    if qh.ATinfinity,
      adds point-at-infinity for Delaunay triangulations

  returns:
    number of points, array of point coordinates, dimension, ismalloc True
    if qh.DELAUNAY & !qh.PROJECTinput, projects points to paraboloid
        and clears qh.PROJECTdelaunay
    if qh.HALFspace, reads optional feasible point, reads halfspaces,
        converts to dual.

  for feasible point in "cdd format" in 3-d:
    3 1
    coordinates
    comments
    begin
    n 4 real/integer
    ...
    end

  notes:
    dimension will change in qh_initqhull_globals if qh.PROJECTinput
    uses malloc() since qh_mem not initialized
    FIXUP QH11012: qh_readpoints needs rewriting, too long
*/
coordT *qh_readpoints(qhT *qh, int *numpoints, int *dimension, boolT *ismalloc) {
  coordT *points, *coords, *infinity= NULL;
  realT paraboloid, maxboloid= -REALmax, value;
  realT *coordp= NULL, *offsetp= NULL, *normalp= NULL;
  char *s= 0, *t, firstline[qh_MAXfirst+1];
  int diminput=0, numinput=0, dimfeasible= 0, newnum, k, tempi;
  int firsttext=0, firstshort=0, firstlong=0, firstpoint=0;
  int tokcount= 0, linecount=0, maxcount, coordcount=0;
  boolT islong, isfirst= True, wasbegin= False;
  boolT isdelaunay= qh->DELAUNAY && !qh->PROJECTinput;

  if (qh->CDDinput) {
    while ((s= fgets(firstline, qh_MAXfirst, qh->fin))) {
      linecount++;
      if (qh->HALFspace && linecount == 1 && isdigit(*s)) {
        dimfeasible= qh_strtol(s, &s);
        while (isspace(*s))
          s++;
        if (qh_strtol(s, &s) == 1)
          linecount += qh_readfeasible(qh, dimfeasible, s);
        else
          dimfeasible= 0;
      }else if (!memcmp(firstline, "begin", (size_t)5) || !memcmp(firstline, "BEGIN", (size_t)5))
        break;
      else if (!*qh->rbox_command)
        strncat(qh->rbox_command, s, sizeof(qh->rbox_command)-1);
    }
    if (!s) {
      qh_fprintf(qh, qh->ferr, 6074, "qhull input error: missing \"begin\" for cdd-formated input\n");
      qh_errexit(qh, qh_ERRinput, NULL, NULL);
    }
  }
  while (!numinput && (s= fgets(firstline, qh_MAXfirst, qh->fin))) {
    linecount++;
    if (!memcmp(s, "begin", (size_t)5) || !memcmp(s, "BEGIN", (size_t)5))
      wasbegin= True;
    while (*s) {
      while (isspace(*s))
        s++;
      if (!*s)
        break;
      if (!isdigit(*s)) {
        if (!*qh->rbox_command) {
          strncat(qh->rbox_command, s, sizeof(qh->rbox_command)-1);
          firsttext= linecount;
        }
        break;
      }
      if (!diminput)
        diminput= qh_strtol(s, &s);
      else {
        numinput= qh_strtol(s, &s);
        if (numinput == 1 && diminput >= 2 && qh->HALFspace && !qh->CDDinput) {
          linecount += qh_readfeasible(qh, diminput, s); /* checks if ok */
          dimfeasible= diminput;
          diminput= numinput= 0;
        }else
          break;
      }
    }
  }
  if (!s) {
    qh_fprintf(qh, qh->ferr, 6075, "qhull input error: short input file.  Did not find dimension and number of points\n");
    qh_errexit(qh, qh_ERRinput, NULL, NULL);
  }
  if (diminput > numinput) {
    tempi= diminput;    /* exchange dim and n, e.g., for cdd input format */
    diminput= numinput;
    numinput= tempi;
  }
  if (diminput < 2) {
    qh_fprintf(qh, qh->ferr, 6220,"qhull input error: dimension %d(first number) should be at least 2\n",
            diminput);
    qh_errexit(qh, 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, qh->ferr, 6221,"qhull input error: dimension %d(first number, includes offset) should be at least 3 for halfspaces\n",
            diminput);
      qh_errexit(qh, qh_ERRinput, NULL, NULL);
    }
    if (dimfeasible) {
      if (dimfeasible != *dimension) {
        qh_fprintf(qh, qh->ferr, 6222,"qhull input error: dimension %d of feasible point is not one less than dimension %d for halfspaces\n",
          dimfeasible, diminput);
        qh_errexit(qh, qh_ERRinput, NULL, NULL);
      }
    }else
      qh_setfeasible(qh, *dimension);
  }else {
    if (qh->CDDinput)
      *dimension= diminput-1;
    else
      *dimension= diminput;
    *numpoints= numinput;
  }
  qh->normal_size= *dimension * 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, qh->ferr, 6076, "qhull error: insufficient memory to read %d points\n",
            numinput);
    qh_errexit(qh, qh_ERRmem, NULL, NULL);
  }
  if (isdelaunay && qh->ATinfinity) {
    infinity= points + numinput * (*dimension);
    for (k= (*dimension) - 1; k--; )
      infinity[k]= 0.0;
  }
  maxcount= numinput * diminput;
  paraboloid= 0.0;
  while ((s= (isfirst ?  s : fgets(qh->line, qh->maxline, qh->fin)))) {
    if (!isfirst) {
      linecount++;
      if (*s == 'e' || *s == 'E') {
        if (!memcmp(s, "end", (size_t)3) || !memcmp(s, "END", (size_t)3)) {
          if (qh->CDDinput )
            break;
          else if (wasbegin)
            qh_fprintf(qh, qh->ferr, 7058, "qhull input warning: the input appears to be in cdd format.  If so, use 'Fd'\n");
        }
      }
    }
    islong= False;
    while (*s) {
      while (isspace(*s))
        s++;
      value= qh_strtod(s, &t);
      if (s == t) {
        if (!*qh->rbox_command)
         strncat(qh->rbox_command, s, sizeof(qh->rbox_command)-1);
        if (*s && !firsttext)
          firsttext= linecount;
        if (!islong && !firstshort && coordcount)
          firstshort= linecount;
        break;
      }
      if (!firstpoint)
        firstpoint= linecount;
      s= t;
      if (++tokcount > maxcount)
        continue;
      if (qh->HALFspace) {
        if (qh->CDDinput)
          *(coordp++)= -value; /* both coefficients and offset */
        else
          *(coordp++)= value;
      }else {
        *(coords++)= value;
        if (qh->CDDinput && !coordcount) {
          if (value != 1.0) {
            qh_fprintf(qh, qh->ferr, 6077, "qhull input error: for cdd format, point at line %d does not start with '1'\n",
                   linecount);
            qh_errexit(qh, qh_ERRinput, NULL, NULL);
          }
          coords--;
        }else if (isdelaunay) {
          paraboloid += value * value;
          if (qh->ATinfinity) {
            if (qh->CDDinput)
              infinity[coordcount-1] += value;
            else
              infinity[coordcount] += value;
          }
        }
      }
      if (++coordcount == diminput) {
        coordcount= 0;
        if (isdelaunay) {
          *(coords++)= paraboloid;
          maximize_(maxboloid, paraboloid);
          paraboloid= 0.0;
        }else if (qh->HALFspace) {
          if (!qh_sethalfspace(qh, *dimension, coords, &coords, normalp, offsetp, qh->feasible_point)) {
            qh_fprintf(qh, qh->ferr, 8048, "The halfspace was on line %d\n", linecount);
            if (wasbegin)
              qh_fprintf(qh, qh->ferr, 8049, "The input appears to be in cdd format.  If so, you should use option 'Fd'\n");
            qh_errexit(qh, qh_ERRinput, NULL, NULL);
          }
          coordp= qh->half_space;
        }
        while (isspace(*s))
          s++;
        if (*s) {
          islong= True;
          if (!firstlong)
            firstlong= linecount;
        }
      }
    }
    if (!islong && !firstshort && coordcount)
      firstshort= linecount;
    if (!isfirst && s - qh->line >= qh->maxline) {
      qh_fprintf(qh, qh->ferr, 6078, "qhull input error: line %d contained more than %d characters\n",
              linecount, (int) (s - qh->line));   /* WARN64 */
      qh_errexit(qh, qh_ERRinput, NULL, NULL);
    }
    isfirst= False;
  }
  if (tokcount != maxcount) {
    newnum= fmin_(numinput, tokcount/diminput);
    qh_fprintf(qh, 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, qh->ferr, 8051, ", line %d is the first comment", firsttext);
    if (firstshort)
      qh_fprintf(qh, qh->ferr, 8052, ", line %d is the first short\nline", firstshort);
    if (firstlong)
      qh_fprintf(qh, qh->ferr, 8053, ", line %d is the first long line", firstlong);
    qh_fprintf(qh, 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, 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, qh->ferr, 1008,"qh_readpoints: read in %d %d-dimensional points\n",
          numinput, diminput));
  return(points);
} /* readpoints */


/*---------------------------------

  qh_setfeasible(qh, dim )
    set qh.feasible_point from qh.feasible_string in "n,n,n" or "n n n" format

  notes:
    "n,n,n" already checked by qh_initflags()
    see qh_readfeasible()
    called only once from qh_new_qhull, otherwise leaks memory
*/
void qh_setfeasible(qhT *qh, int dim) {
  int tokcount= 0;
  char *s;
  coordT *coords, value;

  if (!(s= qh->feasible_string)) {
    qh_fprintf(qh, qh->ferr, 6223, "\
qhull input error: halfspace intersection needs a feasible point.\n\
Either prepend the input with 1 point or use 'Hn,n,n'.  See manual.\n");
    qh_errexit(qh, qh_ERRinput, NULL, NULL);
  }
  if (!(qh->feasible_point= (pointT*)qh_malloc(dim * sizeof(coordT)))) {
    qh_fprintf(qh, qh->ferr, 6079, "qhull error: insufficient memory for 'Hn,n,n'\n");
    qh_errexit(qh, qh_ERRmem, NULL, NULL);
  }
  coords= qh->feasible_point;
  while (*s) {
    value= qh_strtod(s, &s);
    if (++tokcount > dim) {
      qh_fprintf(qh, qh->ferr, 7059, "qhull input warning: more coordinates for 'H%s' than dimension %d\n",
          qh->feasible_string, dim);
      break;
    }
    *(coords++)= value;
    if (*s)
      s++;
  }
  while (++tokcount <= dim)
    *(coords++)= 0.0;
} /* setfeasible */

/*---------------------------------

  qh_skipfacet(qh, facet )
    returns 'True' if this facet is not to be printed

  notes:
    based on the user provided slice thresholds and 'good' specifications
*/
boolT qh_skipfacet(qhT *qh, facetT *facet) {
  facetT *neighbor, **neighborp;

  if (qh->PRINTneighbors) {
    if (facet->good)
      return !qh->PRINTgood;
    FOREACHneighbor_(facet) {
      if (neighbor->good)
        return False;
    }
    return True;
  }else if (qh->PRINTgood)
    return !facet->good;
  else if (!facet->normal)
    return True;
  return(!qh_inthresholds(qh, facet->normal, NULL));
} /* skipfacet */

/*---------------------------------

  qh_skipfilename(qh, string )
    returns pointer to character after filename

  notes:
    skips leading spaces
    ends with spacing or eol
    if starts with ' or " ends with the same, skipping \' or \"
    For qhull, qh_argv_to_command() only uses double quotes
*/
char *qh_skipfilename(qhT *qh, char *filename) {
  char *s= filename;  /* non-const due to return */
  char c;

  while (*s && isspace(*s))
    s++;
  c= *s++;
  if (c == '\0') {
    qh_fprintf(qh, qh->ferr, 6204, "qhull input error: filename expected, none found.\n");
    qh_errexit(qh, qh_ERRinput, NULL, NULL);
  }
  if (c == '\'' || c == '"') {
    while (*s !=c || s[-1] == '\\') {
      if (!*s) {
        qh_fprintf(qh, qh->ferr, 6203, "qhull input error: missing quote after filename -- %s\n", filename);
        qh_errexit(qh, qh_ERRinput, NULL, NULL);
      }
      s++;
    }
    s++;
  }
  else while (*s && !isspace(*s))
      s++;
  return s;
} /* skipfilename */

geometry/src/merge_r.h0000644000176200001440000001727513432323614014502 0ustar  liggesusers/*
  ---------------------------------

   merge_r.h
   header file for merge_r.c

   see qh-merge_r.htm and merge_r.c

   Copyright (c) 1993-2015 C.B. Barber.
   $Id: //main/2015/qhull/src/libqhull_r/merge_r.h#3 $$Change: 2079 $
   $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
*/

#ifndef qhDEFmerge
#define qhDEFmerge 1

#include "libqhull_r.h"


/*============ -constants- ==============*/

/*----------------------------------

  qh_ANGLEredundant
    indicates redundant merge in mergeT->angle
*/
#define qh_ANGLEredundant 6.0

/*----------------------------------

  qh_ANGLEdegen
    indicates degenerate facet in mergeT->angle
*/
#define qh_ANGLEdegen     5.0

/*----------------------------------

  qh_ANGLEconcave
    offset to indicate concave facets in mergeT->angle

  notes:
    concave facets are assigned the range of [2,4] in mergeT->angle
    roundoff error may make the angle less than 2
*/
#define qh_ANGLEconcave  1.5

/*----------------------------------

  MRG... (mergeType)
    indicates the type of a merge (mergeT->type)
*/
typedef enum {  /* in sort order for facet_mergeset */
  MRGnone= 0,
  MRGcoplanar,          /* centrum coplanar */
  MRGanglecoplanar,     /* angle coplanar */
                        /* could detect half concave ridges */
  MRGconcave,           /* concave ridge */
  MRGflip,              /* flipped facet. facet1 == facet2 */
  MRGridge,             /* duplicate ridge (qh_MERGEridge) */
                        /* degen and redundant go onto degen_mergeset */
  MRGdegen,             /* degenerate facet (!enough neighbors) facet1 == facet2 */
  MRGredundant,         /* redundant facet (vertex subset) */
                        /* merge_degenredundant assumes degen < redundant */
  MRGmirror,            /* mirror facet from qh_triangulate */
  ENDmrg
} mergeType;

/*----------------------------------

  qh_MERGEapex
    flag for qh_mergefacet() to indicate an apex merge
*/
#define qh_MERGEapex     True

/*============ -structures- ====================*/

/*----------------------------------

  mergeT
    structure used to merge facets
*/

typedef struct mergeT mergeT;
struct mergeT {         /* initialize in qh_appendmergeset */
  realT   angle;        /* angle between normals of facet1 and facet2 */
  facetT *facet1;       /* will merge facet1 into facet2 */
  facetT *facet2;
  mergeType type;
};


/*=========== -macros- =========================*/

/*----------------------------------

  FOREACHmerge_( merges ) {...}
    assign 'merge' to each merge in merges

  notes:
    uses 'mergeT *merge, **mergep;'
    if qh_mergefacet(),
      restart since qh.facet_mergeset may change
    see FOREACHsetelement_
*/
#define FOREACHmerge_( merges ) FOREACHsetelement_(mergeT, merges, merge)

/*============ prototypes in alphabetical order after pre/postmerge =======*/

#ifdef __cplusplus
extern "C" {
#endif

void    qh_premerge(qhT *qh, vertexT *apex, realT maxcentrum, realT maxangle);
void    qh_postmerge(qhT *qh, const char *reason, realT maxcentrum, realT maxangle,
             boolT vneighbors);
void    qh_all_merges(qhT *qh, boolT othermerge, boolT vneighbors);
void    qh_appendmergeset(qhT *qh, facetT *facet, facetT *neighbor, mergeType mergetype, realT *angle);
setT   *qh_basevertices(qhT *qh, facetT *samecycle);
void    qh_checkconnect(qhT *qh /* qh.new_facets */);
boolT   qh_checkzero(qhT *qh, 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(qhT *qh, ridgeT *atridge);
void    qh_degen_redundant_facet(qhT *qh, facetT *facet);
void    qh_degen_redundant_neighbors(qhT *qh, facetT *facet, facetT *delfacet);
vertexT *qh_find_newvertex(qhT *qh, vertexT *oldvertex, setT *vertices, setT *ridges);
void    qh_findbest_test(qhT *qh, boolT testcentrum, facetT *facet, facetT *neighbor,
           facetT **bestfacet, realT *distp, realT *mindistp, realT *maxdistp);
facetT *qh_findbestneighbor(qhT *qh, facetT *facet, realT *distp, realT *mindistp, realT *maxdistp);
void    qh_flippedmerges(qhT *qh, facetT *facetlist, boolT *wasmerge);
void    qh_forcedmerges(qhT *qh, boolT *wasmerge);
void    qh_getmergeset(qhT *qh, facetT *facetlist);
void    qh_getmergeset_initial(qhT *qh, facetT *facetlist);
void    qh_hashridge(qhT *qh, setT *hashtable, int hashsize, ridgeT *ridge, vertexT *oldvertex);
ridgeT *qh_hashridge_find(qhT *qh, setT *hashtable, int hashsize, ridgeT *ridge,
              vertexT *vertex, vertexT *oldvertex, int *hashslot);
void    qh_makeridges(qhT *qh, facetT *facet);
void    qh_mark_dupridges(qhT *qh, facetT *facetlist);
void    qh_maydropneighbor(qhT *qh, facetT *facet);
int     qh_merge_degenredundant(qhT *qh);
void    qh_merge_nonconvex(qhT *qh, facetT *facet1, facetT *facet2, mergeType mergetype);
void    qh_mergecycle(qhT *qh, facetT *samecycle, facetT *newfacet);
void    qh_mergecycle_all(qhT *qh, facetT *facetlist, boolT *wasmerge);
void    qh_mergecycle_facets(qhT *qh, facetT *samecycle, facetT *newfacet);
void    qh_mergecycle_neighbors(qhT *qh, facetT *samecycle, facetT *newfacet);
void    qh_mergecycle_ridges(qhT *qh, facetT *samecycle, facetT *newfacet);
void    qh_mergecycle_vneighbors(qhT *qh, facetT *samecycle, facetT *newfacet);
void    qh_mergefacet(qhT *qh, facetT *facet1, facetT *facet2, realT *mindist, realT *maxdist, boolT mergeapex);
void    qh_mergefacet2d(qhT *qh, facetT *facet1, facetT *facet2);
void    qh_mergeneighbors(qhT *qh, facetT *facet1, facetT *facet2);
void    qh_mergeridges(qhT *qh, facetT *facet1, facetT *facet2);
void    qh_mergesimplex(qhT *qh, facetT *facet1, facetT *facet2, boolT mergeapex);
void    qh_mergevertex_del(qhT *qh, vertexT *vertex, facetT *facet1, facetT *facet2);
void    qh_mergevertex_neighbors(qhT *qh, facetT *facet1, facetT *facet2);
void    qh_mergevertices(qhT *qh, setT *vertices1, setT **vertices);
setT   *qh_neighbor_intersections(qhT *qh, vertexT *vertex);
void    qh_newvertices(qhT *qh, setT *vertices);
boolT   qh_reducevertices(qhT *qh);
vertexT *qh_redundant_vertex(qhT *qh, vertexT *vertex);
boolT   qh_remove_extravertices(qhT *qh, facetT *facet);
vertexT *qh_rename_sharedvertex(qhT *qh, vertexT *vertex, facetT *facet);
void    qh_renameridgevertex(qhT *qh, ridgeT *ridge, vertexT *oldvertex, vertexT *newvertex);
void    qh_renamevertex(qhT *qh, vertexT *oldvertex, vertexT *newvertex, setT *ridges,
                        facetT *oldfacet, facetT *neighborA);
boolT   qh_test_appendmerge(qhT *qh, facetT *facet, facetT *neighbor);
boolT   qh_test_vneighbors(qhT *qh /* qh.newfacet_list */);
void    qh_tracemerge(qhT *qh, facetT *facet1, facetT *facet2);
void    qh_tracemerging(qhT *qh);
void    qh_updatetested(qhT *qh, facetT *facet1, facetT *facet2);
setT   *qh_vertexridges(qhT *qh, vertexT *vertex);
void    qh_vertexridges_facet(qhT *qh, vertexT *vertex, facetT *facet, setT **ridges);
void    qh_willdelete(qhT *qh, facetT *facet, facetT *replace);

#ifdef __cplusplus
} /* extern "C" */
#endif

#endif /* qhDEFmerge */
geometry/src/QuadTree.h0000644000176200001440000000423613527030173014565 0ustar  liggesusers/*
  This program is free software; you can redistribute it and/or modify it
  under the terms of the GNU General Public License as published by the
  Free Software Foundation; either version 3 of the License, or (at your
  option) any later version.
  This program is distributed in the hope that it will be useful, but WITHOUT
  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  for more details.
  You should have received a copy of the GNU General Public License
  along with this program. If not, see  .
*/

// Originally written for package lidR by Jean-Romain Roussel
// Author: Jean-Romain Roussel
// 3 may 2017: copy from package lidR to package geometry by Jean-Romain Roussel to operate in fast tsearch funtion

#ifndef QT_H
#define QT_H

#include 

struct Point
{
  double x, y;
  int id;

  Point();
  Point(const double, const double);
  Point(const double, const double, const int);
};

struct BoundingBox
{
  Point center, half_res;

  BoundingBox();
  BoundingBox(const Point,const Point);
  bool contains(const Point&, const double);
  bool intersects(const BoundingBox&);
};

class QuadTree
{
public:
  ~QuadTree();
  static QuadTree* create(const std::vector, const std::vector, const double eps);
  bool insert(const Point&);
  void rect_lookup(const double, const double, const double, const double, std::vector&);
  void circle_lookup(const double, const double, const double, std::vector&);


private:
  int MAX_DEPTH;
  double EPSILON;
  int depth;
  BoundingBox boundary;
  std::vector points;
  QuadTree* NE;
  QuadTree* NW;
  QuadTree* SE;
  QuadTree* SW;

  QuadTree(const BoundingBox, const int, const double);

  void subdivide();
  void range_lookup(const BoundingBox, std::vector&, const int);
  void getPointsSquare(const BoundingBox, std::vector&, std::vector&);
  void getPointsCircle(const BoundingBox, std::vector&, std::vector&);
  bool in_circle(const Point&, const Point&, const double);
  bool in_rect(const BoundingBox&, const Point&);
};

#endif //QT_H
geometry/src/RcppExports.cpp0000644000176200001440000000237514366313450015705 0ustar  liggesusers// Generated by using Rcpp::compileAttributes() -> do not edit by hand
// Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393

#include 

using namespace Rcpp;

#ifdef RCPP_USE_GLOBAL_ROSTREAM
Rcpp::Rostream&  Rcpp::Rcout = Rcpp::Rcpp_cout_get();
Rcpp::Rostream& Rcpp::Rcerr = Rcpp::Rcpp_cerr_get();
#endif

// C_tsearch
SEXP C_tsearch(NumericVector x, NumericVector y, IntegerMatrix elem, NumericVector xi, NumericVector yi, bool bary, double eps);
RcppExport SEXP _geometry_C_tsearch(SEXP xSEXP, SEXP ySEXP, SEXP elemSEXP, SEXP xiSEXP, SEXP yiSEXP, SEXP barySEXP, SEXP epsSEXP) {
BEGIN_RCPP
    Rcpp::RObject rcpp_result_gen;
    Rcpp::RNGScope rcpp_rngScope_gen;
    Rcpp::traits::input_parameter< NumericVector >::type x(xSEXP);
    Rcpp::traits::input_parameter< NumericVector >::type y(ySEXP);
    Rcpp::traits::input_parameter< IntegerMatrix >::type elem(elemSEXP);
    Rcpp::traits::input_parameter< NumericVector >::type xi(xiSEXP);
    Rcpp::traits::input_parameter< NumericVector >::type yi(yiSEXP);
    Rcpp::traits::input_parameter< bool >::type bary(barySEXP);
    Rcpp::traits::input_parameter< double >::type eps(epsSEXP);
    rcpp_result_gen = Rcpp::wrap(C_tsearch(x, y, elem, xi, yi, bary, eps));
    return rcpp_result_gen;
END_RCPP
}
geometry/src/poly2_r.c0000644000176200001440000034543613432323610014442 0ustar  liggesusers/*
  ---------------------------------

   poly2_r.c
   implements polygons and simplicies

   see qh-poly_r.htm, poly_r.h and libqhull_r.h

   frequently used code is in poly_r.c

   Copyright (c) 1993-2015 The Geometry Center.
   $Id: //main/2015/qhull/src/libqhull_r/poly2_r.c#10 $$Change: 2069 $
   $DateTime: 2016/01/18 22:05:03 $$Author: bbarber $
*/

#include "qhull_ra.h"

/*======== functions in alphabetical order ==========*/

/*---------------------------------

  qh_addhash( newelem, hashtable, hashsize, hash )
    add newelem to linear hash table at hash if not already there
*/
void qh_addhash(void *newelem, setT *hashtable, int hashsize, int hash) {
  int scan;
  void *elem;

  for (scan= (int)hash; (elem= SETelem_(hashtable, scan));
       scan= (++scan >= hashsize ? 0 : scan)) {
    if (elem == newelem)
      break;
  }
  /* loop terminates because qh_HASHfactor >= 1.1 by qh_initbuffers */
  if (!elem)
    SETelem_(hashtable, scan)= newelem;
} /* addhash */

/*---------------------------------

  qh_check_bestdist(qh)
    check that all points are within max_outside of the nearest facet
    if qh.ONLYgood,
      ignores !good facets

  see:
    qh_check_maxout(), qh_outerinner()

  notes:
    only called from qh_check_points()
      seldom used since qh.MERGING is almost always set
    if notverified>0 at end of routine
      some points were well inside the hull.  If the hull contains
      a lens-shaped component, these points were not verified.  Use
      options 'Qi Tv' to verify all points.  (Exhaustive check also verifies)

  design:
    determine facet for each point (if any)
    for each point
      start with the assigned facet or with the first facet
      find the best facet for the point and check all coplanar facets
      error if point is outside of facet
*/
void qh_check_bestdist(qhT *qh) {
  boolT waserror= False, unassigned;
  facetT *facet, *bestfacet, *errfacet1= NULL, *errfacet2= NULL;
  facetT *facetlist;
  realT dist, maxoutside, maxdist= -REALmax;
  pointT *point;
  int numpart= 0, facet_i, facet_n, notgood= 0, notverified= 0;
  setT *facets;

  trace1((qh, qh->ferr, 1020, "qh_check_bestdist: check points below nearest facet.  Facet_list f%d\n",
      qh->facet_list->id));
  maxoutside= qh_maxouter(qh);
  maxoutside += qh->DISTround;
  /* one more qh.DISTround for check computation */
  trace1((qh, qh->ferr, 1021, "qh_check_bestdist: check that all points are within %2.2g of best facet\n", maxoutside));
  facets= qh_pointfacet(qh /*qh.facet_list*/);
  if (!qh_QUICKhelp && qh->PRINTprecision)
    qh_fprintf(qh, qh->ferr, 8091, "\n\
qhull output completed.  Verifying that %d points are\n\
below %2.2g of the nearest %sfacet.\n",
             qh_setsize(qh, facets), maxoutside, (qh->ONLYgood ?  "good " : ""));
  FOREACHfacet_i_(qh, facets) {  /* for each point with facet assignment */
    if (facet)
      unassigned= False;
    else {
      unassigned= True;
      facet= qh->facet_list;
    }
    point= qh_point(qh, facet_i);
    if (point == qh->GOODpointp)
      continue;
    qh_distplane(qh, point, facet, &dist);
    numpart++;
    bestfacet= qh_findbesthorizon(qh, !qh_IScheckmax, point, facet, qh_NOupper, &dist, &numpart);
    /* occurs after statistics reported */
    maximize_(maxdist, dist);
    if (dist > maxoutside) {
      if (qh->ONLYgood && !bestfacet->good
          && !((bestfacet= qh_findgooddist(qh, point, bestfacet, &dist, &facetlist))
               && dist > maxoutside))
        notgood++;
      else {
        waserror= True;
        qh_fprintf(qh, qh->ferr, 6109, "qhull precision error: point p%d is outside facet f%d, distance= %6.8g maxoutside= %6.8g\n",
                facet_i, bestfacet->id, dist, maxoutside);
        if (errfacet1 != bestfacet) {
          errfacet2= errfacet1;
          errfacet1= bestfacet;
        }
      }
    }else if (unassigned && dist < -qh->MAXcoplanar)
      notverified++;
  }
  qh_settempfree(qh, &facets);
  if (notverified && !qh->DELAUNAY && !qh_QUICKhelp && qh->PRINTprecision)
    qh_fprintf(qh, qh->ferr, 8092, "\n%d points were well inside the hull.  If the hull contains\n\
a lens-shaped component, these points were not verified.  Use\n\
options 'Qci Tv' to verify all points.\n", notverified);
  if (maxdist > qh->outside_err) {
    qh_fprintf(qh, qh->ferr, 6110, "qhull precision error (qh_check_bestdist): a coplanar point is %6.2g from convex hull.  The maximum value(qh.outside_err) is %6.2g\n",
              maxdist, qh->outside_err);
    qh_errexit2(qh, qh_ERRprec, errfacet1, errfacet2);
  }else if (waserror && qh->outside_err > REALmax/2)
    qh_errexit2(qh, qh_ERRprec, errfacet1, errfacet2);
  /* else if waserror, the error was logged to qh.ferr but does not effect the output */
  trace0((qh, qh->ferr, 20, "qh_check_bestdist: max distance outside %2.2g\n", maxdist));
} /* check_bestdist */

/*---------------------------------

  qh_check_dupridge(qh, 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(qhT *qh, 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(qh, 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, 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, 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, qh->ferr, 8145, "- A bounding box for the input sites may alleviate this error.\n");
    if(minvertex > qh_WIDEduplicate*prevdist)
      qh_fprintf(qh, 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, qh_ERRqhull, facet1, facet2);
  }
} /* check_dupridge */
#endif

/*---------------------------------

  qh_check_maxout(qh)
    updates qh.max_outside by checking all points against bestfacet
    if qh.ONLYgood, ignores !good facets

  returns:
    updates facet->maxoutside via qh_findbesthorizon()
    sets qh.maxoutdone
    if printing qh.min_vertex (qh_outerinner),
      it is updated to the current vertices
    removes inside/coplanar points from coplanarset as needed

  notes:
    defines coplanar as 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(qhT *qh) {
  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, 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, qh->ferr, 1023, "qh_check_maxout: determine actual maxoutside and minvertex\n"));
    vertices= qh_pointvertex(qh /*qh.facet_list*/);
    FORALLvertices {
      FOREACHneighbor_(vertex) {
        zinc_(Zdistvertex);  /* distance also computed by main loop below */
        qh_distplane(qh, vertex->point, neighbor, &dist);
        minimize_(minvertex, dist);
        if (-dist > qh->TRACEdist || dist > qh->TRACEdist
        || neighbor == qh->tracefacet || vertex == qh->tracevertex)
          qh_fprintf(qh, qh->ferr, 8093, "qh_check_maxout: p%d(v%d) is %.2g from f%d\n",
                    qh_pointid(qh, vertex->point), vertex->id, dist, neighbor->id);
      }
    }
    if (qh->MERGING) {
      wmin_(Wminvertex, qh->min_vertex);
    }
    qh->min_vertex= minvertex;
    qh_settempfree(qh, &vertices);
  }
  facets= qh_pointfacet(qh /*qh.facet_list*/);
  do {
    old_maxoutside= fmax_(qh->max_outside, maxoutside);
    FOREACHfacet_i_(qh, facets) {     /* for each point with facet assignment */
      if (facet) {
        point= qh_point(qh, facet_i);
        if (point == qh->GOODpointp)
          continue;
        zzinc_(Ztotcheck);
        qh_distplane(qh, point, facet, &dist);
        numpart++;
        bestfacet= qh_findbesthorizon(qh, qh_IScheckmax, point, facet, !qh_NOupper, &dist, &numpart);
        if (bestfacet && dist > maxoutside) {
          if (qh->ONLYgood && !bestfacet->good
          && !((bestfacet= qh_findgooddist(qh, point, bestfacet, &dist, &facetlist))
               && dist > maxoutside))
            notgood++;
          else
            maxoutside= dist;
        }
        if (dist > qh->TRACEdist || (bestfacet && bestfacet == qh->tracefacet))
          qh_fprintf(qh, qh->ferr, 8094, "qh_check_maxout: p%d is %.2g above f%d\n",
          qh_pointid(qh, point), dist, (bestfacet ? bestfacet->id : UINT_MAX));
      }
    }
  }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(qh, &facets);
  wval_(Wmaxout)= maxoutside - qh->max_outside;
  wmax_(Wmaxoutside, qh->max_outside);
  qh->max_outside= maxoutside;
  qh_nearcoplanar(qh /*qh.facet_list*/);
  qh->maxoutdone= True;
  trace1((qh, 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(qhT *qh) {
}
#endif

/*---------------------------------

  qh_check_output(qh)
    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(qhT *qh) {
  int i;

  if (qh->STOPcone)
    return;
  if (qh->VERIFYoutput | qh->IStracing | qh->CHECKfrequently) {
    qh_checkpolygon(qh, qh->facet_list);
    qh_checkflipped_all(qh, qh->facet_list);
    qh_checkconvex(qh, qh->facet_list, qh_ALGORITHMfault);
  }else if (!qh->MERGING && qh_newstats(qh, qh->qhstat.precision, &i)) {
    qh_checkflipped_all(qh, qh->facet_list);
    qh_checkconvex(qh, qh->facet_list, qh_ALGORITHMfault);
  }
} /* check_output */



/*---------------------------------

  qh_check_point(qh, point, facet, maxoutside, maxdist, errfacet1, errfacet2 )
    check that point is less than maxoutside from facet
*/
void qh_check_point(qhT *qh, pointT *point, facetT *facet, realT *maxoutside, realT *maxdist, facetT **errfacet1, facetT **errfacet2) {
  realT dist;

  /* occurs after statistics reported */
  qh_distplane(qh, point, facet, &dist);
  if (dist > *maxoutside) {
    if (*errfacet1 != facet) {
      *errfacet2= *errfacet1;
      *errfacet1= facet;
    }
    qh_fprintf(qh, qh->ferr, 6111, "qhull precision error: point p%d is outside facet f%d, distance= %6.8g maxoutside= %6.8g\n",
              qh_pointid(qh, point), facet->id, dist, *maxoutside);
  }
  maximize_(*maxdist, dist);
} /* qh_check_point */


/*---------------------------------

  qh_check_points(qh)
    checks that all points are inside all facets

  notes:
    if many points and qh_check_maxout not called (i.e., !qh.MERGING),
       calls qh_findbesthorizon (seldom done).
    ignores flipped facets
    maxoutside includes 2 qh.DISTrounds
      one qh.DISTround for the computed distances in qh_check_points
    qh_printafacet and qh_printsummary needs only one qh.DISTround
    the computation for qh.VERIFYdirect does not account for qh.other_points

  design:
    if many points
      use qh_check_bestdist()
    else
      for all facets
        for all points
          check that point is inside facet
*/
void qh_check_points(qhT *qh) {
  facetT *facet, *errfacet1= NULL, *errfacet2= NULL;
  realT total, maxoutside, maxdist= -REALmax;
  pointT *point, **pointp, *pointtemp;
  boolT testouter;

  maxoutside= qh_maxouter(qh);
  maxoutside += qh->DISTround;
  /* one more qh.DISTround for check computation */
  trace1((qh, qh->ferr, 1025, "qh_check_points: check all points below %2.2g of all facet planes\n",
          maxoutside));
  if (qh->num_good)   /* miss counts other_points and !good facets */
     total= (float)qh->num_good * (float)qh->num_points;
  else
     total= (float)qh->num_facets * (float)qh->num_points;
  if (total >= qh_VERIFYdirect && !qh->maxoutdone) {
    if (!qh_QUICKhelp && qh->SKIPcheckmax && qh->MERGING)
      qh_fprintf(qh, qh->ferr, 7075, "qhull input warning: merging without checking outer planes('Q5' or 'Po').\n\
Verify may report that a point is outside of a facet.\n");
    qh_check_bestdist(qh);
  }else {
    if (qh_MAXoutside && qh->maxoutdone)
      testouter= True;
    else
      testouter= False;
    if (!qh_QUICKhelp) {
      if (qh->MERGEexact)
        qh_fprintf(qh, qh->ferr, 7076, "qhull input warning: exact merge ('Qx').  Verify may report that a point\n\
is outside of a facet.  See qh-optq.htm#Qx\n");
      else if (qh->SKIPcheckmax || qh->NOnearinside)
        qh_fprintf(qh, qh->ferr, 7077, "qhull input warning: no outer plane check ('Q5') or no processing of\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, qh->ferr, 8098, "\n\
Output completed.  Verifying that all points are below outer planes of\n\
all %sfacets.  Will make %2.0f distance computations.\n",
              (qh->ONLYgood ?  "good " : ""), total);
      else
        qh_fprintf(qh, qh->ferr, 8099, "\n\
Output completed.  Verifying that all points are below %2.2g of\n\
all %sfacets.  Will make %2.0f distance computations.\n",
              maxoutside, (qh->ONLYgood ?  "good " : ""), total);
    }
    FORALLfacets {
      if (!facet->good && qh->ONLYgood)
        continue;
      if (facet->flipped)
        continue;
      if (!facet->normal) {
        qh_fprintf(qh, qh->ferr, 7061, "qhull warning (qh_check_points): missing normal for facet f%d\n", facet->id);
        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(qh, point, facet, &maxoutside, &maxdist, &errfacet1, &errfacet2);
      }
      FOREACHpoint_(qh->other_points) {
        if (point != qh->GOODpointp)
          qh_check_point(qh, point, facet, &maxoutside, &maxdist, &errfacet1, &errfacet2);
      }
    }
    if (maxdist > qh->outside_err) {
      qh_fprintf(qh, qh->ferr, 6112, "qhull precision error (qh_check_points): a coplanar point is %6.2g from convex hull.  The maximum value(qh.outside_err) is %6.2g\n",
                maxdist, qh->outside_err );
      qh_errexit2(qh, qh_ERRprec, errfacet1, errfacet2 );
    }else if (errfacet1 && qh->outside_err > REALmax/2)
        qh_errexit2(qh, qh_ERRprec, errfacet1, errfacet2 );
    /* else if errfacet1, the error was logged to qh.ferr but does not effect the output */
    trace0((qh, qh->ferr, 21, "qh_check_points: max distance outside %2.2g\n", maxdist));
  }
} /* check_points */


/*---------------------------------

  qh_checkconvex(qh, 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(qhT *qh, facetT *facetlist, int fault) {
  facetT *facet, *neighbor, **neighborp, *errfacet1=NULL, *errfacet2=NULL;
  vertexT *vertex;
  realT dist;
  pointT *centrum;
  boolT waserror= False, centrum_warning= False, tempcentrum= False, allsimplicial;
  int neighbor_i;

  trace1((qh, 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(qh, "flipped facet");
      qh_fprintf(qh, qh->ferr, 6113, "qhull precision error: f%d is flipped(interior point is outside)\n",
               facet->id);
      errfacet1= facet;
      waserror= True;
      continue;
    }
    if (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(qh, vertex->point, neighbor, &dist);
        if (dist > -qh->DISTround) {
          if (fault == qh_DATAfault) {
            qh_precision(qh, "coplanar or concave ridge");
            qh_fprintf(qh, qh->ferr, 6114, "qhull precision error: initial simplex is not convex. Distance=%.2g\n", dist);
            qh_errexit(qh, qh_ERRsingular, NULL, NULL);
          }
          if (dist > qh->DISTround) {
            zzinc_(Zconcaveridges);
            qh_precision(qh, "concave ridge");
            qh_fprintf(qh, qh->ferr, 6115, "qhull precision error: f%d is concave to f%d, since p%d(v%d) is %6.4g above\n",
              facet->id, neighbor->id, qh_pointid(qh, 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(qh, "coplanar ridge");
              qh_fprintf(qh, qh->ferr, 6116, "qhull precision error: f%d is clearly not convex to f%d, since p%d(v%d) is %6.4g above\n",
                facet->id, neighbor->id, qh_pointid(qh, vertex->point), vertex->id, dist);
              errfacet1= facet;
              errfacet2= neighbor;
              waserror= True;
            }
          }else {
            zzinc_(Zcoplanarridges);
            qh_precision(qh, "coplanar ridge");
            trace0((qh, 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(qh, vertex->point), vertex->id, dist, qh->furthest_id));
          }
        }
      }
    }
    if (!allsimplicial) {
      if (qh->CENTERtype == qh_AScentrum) {
        if (!facet->center)
          facet->center= qh_getcentrum(qh, facet);
        centrum= facet->center;
      }else {
        if (!centrum_warning && (!facet->simplicial || facet->tricoplanar)) {
           centrum_warning= True;
           qh_fprintf(qh, qh->ferr, 7062, "qhull warning: recomputing centrums for convexity test.  This may lead to false, precision errors.\n");
        }
        centrum= qh_getcentrum(qh, facet);
        tempcentrum= True;
      }
      FOREACHneighbor_(facet) {
        if (qh->ZEROcentrum && facet->simplicial && neighbor->simplicial)
          continue;
        if (facet->tricoplanar || neighbor->tricoplanar)
          continue;
        zzinc_(Zdistconvex);
        qh_distplane(qh, centrum, neighbor, &dist);
        if (dist > qh->DISTround) {
          zzinc_(Zconcaveridges);
          qh_precision(qh, "concave ridge");
          qh_fprintf(qh, qh->ferr, 6117, "qhull precision error: f%d is concave to f%d.  Centrum of f%d is %6.4g above f%d\n",
            facet->id, neighbor->id, facet->id, dist, neighbor->id);
          errfacet1= facet;
          errfacet2= neighbor;
          waserror= True;
        }else if (dist >= 0.0) {   /* if arithmetic always rounds the same,
                                     can test against centrum radius instead */
          zzinc_(Zcoplanarridges);
          qh_precision(qh, "coplanar ridge");
          qh_fprintf(qh, qh->ferr, 6118, "qhull precision error: f%d is coplanar or concave to f%d.  Centrum of f%d is %6.4g above f%d\n",
            facet->id, neighbor->id, facet->id, dist, neighbor->id);
          errfacet1= facet;
          errfacet2= neighbor;
          waserror= True;
        }
      }
      if (tempcentrum)
        qh_memfree(qh, centrum, qh->normal_size);
    }
  }
  if (waserror && !qh->FORCEoutput)
    qh_errexit2(qh, qh_ERRprec, errfacet1, errfacet2);
} /* checkconvex */


/*---------------------------------

  qh_checkfacet(qh, facet, newmerge, waserror )
    checks for consistency errors in facet
    newmerge set if from merge_r.c

  returns:
    sets waserror if any error occurs

  checks:
    vertex ids are inverse sorted
    unless newmerge, at least hull_dim neighbors and vertices (exactly if simplicial)
    if non-simplicial, at least as many ridges as neighbors
    neighbors are not duplicated
    ridges are not duplicated
    in 3-d, ridges=verticies
    (qh.hull_dim-1) ridge vertices
    neighbors are reciprocated
    ridge neighbors are facet neighbors and a ridge for every neighbor
    simplicial neighbors match facetintersect
    vertex intersection matches vertices of common ridges
    vertex neighbors and facet vertices agree
    all ridges have distinct vertex sets

  notes:
    uses neighbor->seen

  design:
    check sets
    check vertices
    check sizes of neighbors and vertices
    check for qh_MERGEridge and qh_DUPLICATEridge flags
    check neighbor set
    check ridge set
    check ridges, neighbors, and vertices
*/
void qh_checkfacet(qhT *qh, facetT *facet, boolT newmerge, boolT *waserrorp) {
  facetT *neighbor, **neighborp, *errother=NULL;
  ridgeT *ridge, **ridgep, *errridge= NULL, *ridge2;
  vertexT *vertex, **vertexp;
  unsigned 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, qh->ferr, 6119, "qhull internal error (qh_checkfacet): facet f%d is on the visible_list\n",
      facet->id);
    qh_errexit(qh, qh_ERRqhull, facet, NULL);
  }
  if (!facet->normal) {
    qh_fprintf(qh, qh->ferr, 6120, "qhull internal error (qh_checkfacet): facet f%d does not have  a normal\n",
      facet->id);
    waserror= True;
  }
  qh_setcheck(qh, facet->vertices, "vertices for f", facet->id);
  qh_setcheck(qh, facet->ridges, "ridges for f", facet->id);
  qh_setcheck(qh, facet->outsideset, "outsideset for f", facet->id);
  qh_setcheck(qh, facet->coplanarset, "coplanarset for f", facet->id);
  qh_setcheck(qh, facet->neighbors, "neighbors for f", facet->id);
  FOREACHvertex_(facet->vertices) {
    if (vertex->deleted) {
      qh_fprintf(qh, qh->ferr, 6121, "qhull internal error (qh_checkfacet): deleted vertex v%d in f%d\n", vertex->id, facet->id);
      qh_errprint(qh, "ERRONEOUS", NULL, NULL, NULL, vertex);
      waserror= True;
    }
    if (vertex->id >= previousid) {
      qh_fprintf(qh, qh->ferr, 6122, "qhull internal error (qh_checkfacet): vertices of f%d are not in descending id order at v%d\n", facet->id, vertex->id);
      waserror= True;
      break;
    }
    previousid= vertex->id;
  }
  numneighbors= qh_setsize(qh, facet->neighbors);
  numvertices= qh_setsize(qh, facet->vertices);
  numridges= qh_setsize(qh, facet->ridges);
  if (facet->simplicial) {
    if (numvertices+numneighbors != 2*qh->hull_dim
    && !facet->degenerate && !facet->redundant) {
      qh_fprintf(qh, qh->ferr, 6123, "qhull internal error (qh_checkfacet): for simplicial facet f%d, #vertices %d + #neighbors %d != 2*qh->hull_dim\n",
                facet->id, numvertices, numneighbors);
      qh_setprint(qh, qh->ferr, "", facet->neighbors);
      waserror= True;
    }
  }else { /* non-simplicial */
    if (!newmerge
    &&(numvertices < qh->hull_dim || numneighbors < qh->hull_dim)
    && !facet->degenerate && !facet->redundant) {
      qh_fprintf(qh, qh->ferr, 6124, "qhull internal error (qh_checkfacet): for facet f%d, #vertices %d or #neighbors %d < qh->hull_dim\n",
         facet->id, numvertices, numneighbors);
       waserror= True;
    }
    /* in 3-d, can get a vertex twice in an edge list, e.g., RBOX 1000 s W1e-13 t995849315 D2 | QHULL d Tc Tv TP624 TW1e-13 T4 */
    if (numridges < numneighbors
    ||(qh->hull_dim == 3 && numvertices > numridges && !qh->NEWfacets)
    ||(qh->hull_dim == 2 && numridges + numvertices + numneighbors != 6)) {
      if (!facet->degenerate && !facet->redundant) {
        qh_fprintf(qh, qh->ferr, 6125, "qhull internal error (qh_checkfacet): for facet f%d, #ridges %d < #neighbors %d or(3-d) > #vertices %d or(2-d) not all 2\n",
            facet->id, numridges, numneighbors, numvertices);
        waserror= True;
      }
    }
  }
  FOREACHneighbor_(facet) {
    if (neighbor == qh_MERGEridge || neighbor == qh_DUPLICATEridge) {
      qh_fprintf(qh, qh->ferr, 6126, "qhull internal error (qh_checkfacet): facet f%d still has a MERGE or DUP neighbor\n", facet->id);
      qh_errexit(qh, qh_ERRqhull, facet, NULL);
    }
    neighbor->seen= True;
  }
  FOREACHneighbor_(facet) {
    if (!qh_setin(neighbor->neighbors, facet)) {
      qh_fprintf(qh, qh->ferr, 6127, "qhull internal error (qh_checkfacet): facet f%d has neighbor f%d, but f%d does not have neighbor f%d\n",
              facet->id, neighbor->id, neighbor->id, facet->id);
      errother= neighbor;
      waserror= True;
    }
    if (!neighbor->seen) {
      qh_fprintf(qh, qh->ferr, 6128, "qhull internal error (qh_checkfacet): facet f%d has a duplicate neighbor f%d\n",
              facet->id, neighbor->id);
      errother= neighbor;
      waserror= True;
    }
    neighbor->seen= False;
  }
  FOREACHridge_(facet->ridges) {
    qh_setcheck(qh, ridge->vertices, "vertices for r", ridge->id);
    ridge->seen= False;
  }
  FOREACHridge_(facet->ridges) {
    if (ridge->seen) {
      qh_fprintf(qh, qh->ferr, 6129, "qhull internal error (qh_checkfacet): facet f%d has a duplicate ridge r%d\n",
              facet->id, ridge->id);
      errridge= ridge;
      waserror= True;
    }
    ridge->seen= True;
    numRvertices= qh_setsize(qh, ridge->vertices);
    if (numRvertices != qh->hull_dim - 1) {
      qh_fprintf(qh, qh->ferr, 6130, "qhull internal error (qh_checkfacet): ridge between f%d and f%d has %d vertices\n",
                ridge->top->id, ridge->bottom->id, numRvertices);
      errridge= ridge;
      waserror= True;
    }
    neighbor= otherfacet_(ridge, facet);
    neighbor->seen= True;
    if (!qh_setin(facet->neighbors, neighbor)) {
      qh_fprintf(qh, qh->ferr, 6131, "qhull internal error (qh_checkfacet): for facet f%d, neighbor f%d of ridge r%d not in facet\n",
           facet->id, neighbor->id, ridge->id);
      errridge= ridge;
      waserror= True;
    }
  }
  if (!facet->simplicial) {
    FOREACHneighbor_(facet) {
      if (!neighbor->seen) {
        qh_fprintf(qh, qh->ferr, 6132, "qhull internal error (qh_checkfacet): facet f%d does not have a ridge for neighbor f%d\n",
              facet->id, neighbor->id);
        errother= neighbor;
        waserror= True;
      }
      intersection= qh_vertexintersect_new(qh, facet->vertices, neighbor->vertices);
      qh_settemppush(qh, intersection);
      FOREACHvertex_(facet->vertices) {
        vertex->seen= False;
        vertex->seen2= False;
      }
      FOREACHvertex_(intersection)
        vertex->seen= True;
      FOREACHridge_(facet->ridges) {
        if (neighbor != otherfacet_(ridge, facet))
            continue;
        FOREACHvertex_(ridge->vertices) {
          if (!vertex->seen) {
            qh_fprintf(qh, qh->ferr, 6133, "qhull internal error (qh_checkfacet): vertex v%d in r%d not in f%d intersect f%d\n",
                  vertex->id, ridge->id, facet->id, neighbor->id);
            qh_errexit(qh, qh_ERRqhull, facet, ridge);
          }
          vertex->seen2= True;
        }
      }
      if (!newmerge) {
        FOREACHvertex_(intersection) {
          if (!vertex->seen2) {
            if (qh->IStracing >=3 || !qh->MERGING) {
              qh_fprintf(qh, 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(qh, "ERRONEOUS", facet, neighbor, NULL, vertex);
                if (!qh->MERGING)
                  qh_errexit(qh, qh_ERRqhull, NULL, NULL);
              }
            }
          }
        }
      }
      qh_settempfree(qh, &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, 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_(qh, 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, 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(qh, "ERRONEOUS", facet, errother, errridge, NULL);
    *waserrorp= True;
  }
} /* checkfacet */


/*---------------------------------

  qh_checkflipped_all(qh, facetlist )
    checks orientation of facets in list against interior point
*/
void qh_checkflipped_all(qhT *qh, facetT *facetlist) {
  facetT *facet;
  boolT waserror= False;
  realT dist;

  if (facetlist == qh->facet_list)
    zzval_(Zflippedfacets)= 0;
  FORALLfacet_(facetlist) {
    if (facet->normal && !qh_checkflipped(qh, facet, &dist, !qh_ALL)) {
      qh_fprintf(qh, qh->ferr, 6136, "qhull precision error: facet f%d is flipped, distance= %6.12g\n",
              facet->id, dist);
      if (!qh->FORCEoutput) {
        qh_errprint(qh, "ERRONEOUS", facet, NULL, NULL, NULL);
        waserror= True;
      }
    }
  }
  if (waserror) {
    qh_fprintf(qh, qh->ferr, 8101, "\n\
A flipped facet occurs when its distance to the interior point is\n\
greater than %2.2g, the maximum roundoff error.\n", -qh->DISTround);
    qh_errexit(qh, qh_ERRprec, NULL, NULL);
  }
} /* checkflipped_all */

/*---------------------------------

  qh_checkpolygon(qh, 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(qhT *qh, 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, 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(qh, facet->outsideset)) {
          if (!qh->NARROWhull
#if !qh_COMPUTEfurthest
               || facet->furthestdist >= qh->MINoutside
#endif
                        ) {
            qh_fprintf(qh, qh->ferr, 6137, "qhull internal error (qh_checkpolygon): f%d has outside points before qh->facet_next\n",
                     facet->id);
            qh_errexit(qh, qh_ERRqhull, facet, NULL);
          }
        }
      }
      numfacets++;
      qh_checkfacet(qh, facet, False, &waserror);
    }
  }
  if (qh->visible_list && !visibleseen && facetlist == qh->facet_list) {
    qh_fprintf(qh, 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);
    qh_errexit(qh, 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(qh, facet->ridges);
    FOREACHvertex_(facet->vertices) {
      vertex->visitid++;
      if (!vertex->seen) {
        vertex->seen= True;
        numvertices++;
        if (qh_pointid(qh, vertex->point) == qh_IDunknown) {
          qh_fprintf(qh, qh->ferr, 6139, "qhull internal error (qh_checkpolygon): unknown point %p for vertex v%d first_point %p\n",
                   vertex->point, vertex->id, qh->first_point);
          waserror= True;
        }
      }
    }
  }
  qh->vertex_visit += (unsigned int)numfacets;
  if (facetlist == qh->facet_list) {
    if (numfacets != qh->num_facets - qh->num_visible) {
      qh_fprintf(qh, qh->ferr, 6140, "qhull internal error (qh_checkpolygon): actual number of facets is %d, cumulative facet count is %d - %d visible facets\n",
              numfacets, qh->num_facets, qh->num_visible);
      waserror= True;
    }
    qh->vertex_visit++;
    if (qh->VERTEXneighbors) {
      FORALLvertices {
        qh_setcheck(qh, vertex->neighbors, "neighbors for v", vertex->id);
        if (vertex->deleted)
          continue;
        totvneighbors += qh_setsize(qh, vertex->neighbors);
      }
      FORALLfacet_(facetlist)
        totvertices += qh_setsize(qh, facet->vertices);
      if (totvneighbors != totvertices) {
        qh_fprintf(qh, 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, qh->del_vertices)) {
      qh_fprintf(qh, qh->ferr, 6142, "qhull internal error (qh_checkpolygon): actual number of vertices is %d, cumulative vertex count is %d\n",
              numvertices, qh->num_vertices - qh_setsize(qh, qh->del_vertices));
      waserror= True;
    }
    if (qh->hull_dim == 2 && numvertices != numfacets) {
      qh_fprintf(qh, qh->ferr, 6143, "qhull internal error (qh_checkpolygon): #vertices %d != #facets %d\n",
        numvertices, numfacets);
      waserror= True;
    }
    if (qh->hull_dim == 3 && numvertices + numfacets - numridges/2 != 2) {
      qh_fprintf(qh, qh->ferr, 7063, "qhull warning: #vertices %d + #facets %d - #edges %d != 2\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, qh_ERRqhull, NULL, NULL);
} /* checkpolygon */


/*---------------------------------

  qh_checkvertex(qh, vertex )
    check vertex for consistency
    checks vertex->neighbors

  notes:
    neighbors checked efficiently in checkpolygon
*/
void qh_checkvertex(qhT *qh, vertexT *vertex) {
  boolT waserror= False;
  facetT *neighbor, **neighborp, *errfacet=NULL;

  if (qh_pointid(qh, vertex->point) == qh_IDunknown) {
    qh_fprintf(qh, qh->ferr, 6144, "qhull internal error (qh_checkvertex): unknown point id %p\n", vertex->point);
    waserror= True;
  }
  if (vertex->id >= qh->vertex_id) {
    qh_fprintf(qh, qh->ferr, 6145, "qhull internal error (qh_checkvertex): unknown vertex id %d\n", vertex->id);
    waserror= True;
  }
  if (!waserror && !vertex->deleted) {
    if (qh_setsize(qh, vertex->neighbors)) {
      FOREACHneighbor_(vertex) {
        if (!qh_setin(neighbor->vertices, vertex)) {
          qh_fprintf(qh, qh->ferr, 6146, "qhull internal error (qh_checkvertex): neighbor f%d does not contain v%d\n", neighbor->id, vertex->id);
          errfacet= neighbor;
          waserror= True;
        }
      }
    }
  }
  if (waserror) {
    qh_errprint(qh, "ERRONEOUS", NULL, NULL, NULL, vertex);
    qh_errexit(qh, qh_ERRqhull, errfacet, NULL);
  }
} /* checkvertex */

/*---------------------------------

  qh_clearcenters(qh, type )
    clear old data from facet->center

  notes:
    sets new centertype
    nop if CENTERtype is the same
*/
void qh_clearcenters(qhT *qh, qh_CENTER type) {
  facetT *facet;

  if (qh->CENTERtype != type) {
    FORALLfacets {
      if (facet->tricoplanar && !facet->keepcentrum)
          facet->center= NULL;  /* center is owned by the ->keepcentrum facet */
      else if (qh->CENTERtype == qh_ASvoronoi){
        if (facet->center) {
          qh_memfree(qh, facet->center, qh->center_size);
          facet->center= NULL;
        }
      }else /* qh->CENTERtype == qh_AScentrum */ {
        if (facet->center) {
          qh_memfree(qh, facet->center, qh->normal_size);
          facet->center= NULL;
        }
      }
    }
    qh->CENTERtype= type;
  }
  trace2((qh, qh->ferr, 2043, "qh_clearcenters: switched to center type %d\n", type));
} /* clearcenters */

/*---------------------------------

  qh_createsimplex(qh, 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(qhT *qh, setT *vertices) {
  facetT *facet= NULL, *newfacet;
  boolT toporient= True;
  int vertex_i, vertex_n, nth;
  setT *newfacets= qh_settemp(qh, qh->hull_dim+1);
  vertexT *vertex;

  qh->facet_list= qh->newfacet_list= qh->facet_tail= qh_newfacet(qh);
  qh->num_facets= qh->num_vertices= qh->num_visible= 0;
  qh->vertex_list= qh->newvertex_list= qh->vertex_tail= qh_newvertex(qh, NULL);
  FOREACHvertex_i_(qh, vertices) {
    newfacet= qh_newfacet(qh);
    newfacet->vertices= qh_setnew_delnthsorted(qh, vertices, vertex_n,
                                                vertex_i, 0);
    newfacet->toporient= (unsigned char)toporient;
    qh_appendfacet(qh, newfacet);
    newfacet->newfacet= True;
    qh_appendvertex(qh, vertex);
    qh_setappend(qh, &newfacets, newfacet);
    toporient ^= True;
  }
  FORALLnew_facets {
    nth= 0;
    FORALLfacet_(qh->newfacet_list) {
      if (facet != newfacet)
        SETelem_(newfacet->neighbors, nth++)= facet;
    }
    qh_settruncate(qh, newfacet->neighbors, qh->hull_dim);
  }
  qh_settempfree(qh, &newfacets);
  trace1((qh, qh->ferr, 1028, "qh_createsimplex: created simplex\n"));
} /* createsimplex */

/*---------------------------------

  qh_delridge(qh, ridge )
    deletes ridge from data structures it belongs to
    frees up its memory

  notes:
    in merge_r.c, caller sets vertex->delridge for each vertex
    ridges also freed in qh_freeqhull
*/
void qh_delridge(qhT *qh, 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(qh, &(ridge->vertices));
  qh_memfree_(qh, ridge, (int)sizeof(ridgeT), freelistp);
} /* delridge */


/*---------------------------------

  qh_delvertex(qh, vertex )
    deletes a vertex and frees its memory

  notes:
    assumes vertex->adjacencies have been updated if needed
    unlinks from vertex_list
*/
void qh_delvertex(qhT *qh, vertexT *vertex) {

  if (vertex == qh->tracevertex)
    qh->tracevertex= NULL;
  qh_removevertex(qh, vertex);
  qh_setfree(qh, &vertex->neighbors);
  qh_memfree(qh, vertex, (int)sizeof(vertexT));
} /* delvertex */


/*---------------------------------

  qh_facet3vertex(qh, )
    return temporary set of 3-d vertices in qh_ORIENTclock order

  design:
    if simplicial facet
      build set from facet->vertices with facet->toporient
    else
      for each ridge in order
        build set from ridge's vertices
*/
setT *qh_facet3vertex(qhT *qh, facetT *facet) {
  ridgeT *ridge, *firstridge;
  vertexT *vertex;
  int cntvertices, cntprojected=0;
  setT *vertices;

  cntvertices= qh_setsize(qh, facet->vertices);
  vertices= qh_settemp(qh, cntvertices);
  if (facet->simplicial) {
    if (cntvertices != 3) {
      qh_fprintf(qh, qh->ferr, 6147, "qhull internal error (qh_facet3vertex): only %d vertices for simplicial facet f%d\n",
                  cntvertices, facet->id);
      qh_errexit(qh, qh_ERRqhull, facet, NULL);
    }
    qh_setappend(qh, &vertices, SETfirst_(facet->vertices));
    if (facet->toporient ^ qh_ORIENTclock)
      qh_setappend(qh, &vertices, SETsecond_(facet->vertices));
    else
      qh_setaddnth(qh, &vertices, 0, SETsecond_(facet->vertices));
    qh_setappend(qh, &vertices, SETelem_(facet->vertices, 2));
  }else {
    ridge= firstridge= SETfirstt_(facet->ridges, ridgeT);   /* no infinite */
    while ((ridge= qh_nextridge3d(ridge, facet, &vertex))) {
      qh_setappend(qh, &vertices, vertex);
      if (++cntprojected > cntvertices || ridge == firstridge)
        break;
    }
    if (!ridge || cntprojected != cntvertices) {
      qh_fprintf(qh, qh->ferr, 6148, "qhull internal error (qh_facet3vertex): ridges for facet %d don't match up.  got at least %d\n",
                  facet->id, cntprojected);
      qh_errexit(qh, qh_ERRqhull, facet, ridge);
    }
  }
  return vertices;
} /* facet3vertex */

/*---------------------------------

  qh_findbestfacet(qh, point, bestoutside, bestdist, isoutside )
    find facet that is furthest below a point

    for Delaunay triangulations,
      Use qh_setdelaunay() to lift point to paraboloid and scale by 'Qbb' if needed
      Do not use options 'Qbk', 'QBk', or 'QbB' since they scale the coordinates.

  returns:
    if bestoutside is set (e.g., qh_ALL)
      returns best facet that is not upperdelaunay
      if Delaunay and inside, point is outside circumsphere of bestfacet
    else
      returns first facet below point
      if point is inside, returns nearest, !upperdelaunay facet
    distance to facet
    isoutside set if outside of facet

  notes:
    For tricoplanar facets, this finds one of the tricoplanar facets closest
    to the point.  For Delaunay triangulations, the point may be inside a
    different tricoplanar facet. See locate a facet with qh_findbestfacet()

    If inside, qh_findbestfacet performs an exhaustive search
       this may be too conservative.  Sometimes it is clearly required.

    qh_findbestfacet is not used by qhull.
    uses qh.visit_id and qh.coplanarset

  see:
    qh_findbest
*/
facetT *qh_findbestfacet(qhT *qh, pointT *point, boolT bestoutside,
           realT *bestdist, boolT *isoutside) {
  facetT *bestfacet= NULL;
  int numpart, totpart= 0;

  bestfacet= qh_findbest(qh, point, qh->facet_list,
                            bestoutside, !qh_ISnewfacets, bestoutside /* qh_NOupper */,
                            bestdist, isoutside, &totpart);
  if (*bestdist < -qh->DISTround) {
    bestfacet= qh_findfacet_all(qh, point, bestdist, isoutside, &numpart);
    totpart += numpart;
    if ((isoutside && *isoutside && bestoutside)
    || (isoutside && !*isoutside && bestfacet->upperdelaunay)) {
      bestfacet= qh_findbest(qh, point, bestfacet,
                            bestoutside, False, bestoutside,
                            bestdist, isoutside, &totpart);
      totpart += numpart;
    }
  }
  trace3((qh, qh->ferr, 3014, "qh_findbestfacet: f%d dist %2.2g isoutside %d totpart %d\n",
          bestfacet->id, *bestdist, (isoutside ? *isoutside : UINT_MAX), totpart));
  return bestfacet;
} /* findbestfacet */

/*---------------------------------

  qh_findbestlower(qh, facet, point, bestdist, numpart )
    returns best non-upper, non-flipped neighbor of facet for point
    if needed, searches vertex neighbors

  returns:
    returns bestdist and updates numpart

  notes:
    if Delaunay and inside, point is outside of circumsphere of bestfacet
    called by qh_findbest() for points above an upperdelaunay facet

*/
facetT *qh_findbestlower(qhT *qh, facetT *upperfacet, pointT *point, realT *bestdistp, int *numpart) {
  facetT *neighbor, **neighborp, *bestfacet= NULL;
  realT bestdist= -REALmax/2 /* avoid underflow */;
  realT dist;
  vertexT *vertex;
  boolT isoutside= False;  /* not used */

  zinc_(Zbestlower);
  FOREACHneighbor_(upperfacet) {
    if (neighbor->upperdelaunay || neighbor->flipped)
      continue;
    (*numpart)++;
    qh_distplane(qh, point, neighbor, &dist);
    if (dist > bestdist) {
      bestfacet= neighbor;
      bestdist= dist;
    }
  }
  if (!bestfacet) {
    zinc_(Zbestlowerv);
    /* rarely called, numpart does not count nearvertex computations */
    vertex= qh_nearvertex(qh, upperfacet, point, &dist);
    qh_vertexneighbors(qh);
    FOREACHneighbor_(vertex) {
      if (neighbor->upperdelaunay || neighbor->flipped)
        continue;
      (*numpart)++;
      qh_distplane(qh, point, neighbor, &dist);
      if (dist > bestdist) {
        bestfacet= neighbor;
        bestdist= dist;
      }
    }
  }
  if (!bestfacet) {
    zinc_(Zbestlowerall);  /* invoked once per point in outsideset */
    zmax_(Zbestloweralln, qh->num_facets);
    /* [dec'15] Previously reported as QH6228 */
    trace3((qh, qh->ferr, 3025, "qh_findbestlower: all neighbors of facet %d are flipped or upper Delaunay.  Search all facets\n",
       upperfacet->id));
    /* rarely called */
    bestfacet= qh_findfacet_all(qh, point, &bestdist, &isoutside, numpart);
  }
  *bestdistp= bestdist;
  trace3((qh, qh->ferr, 3015, "qh_findbestlower: f%d dist %2.2g for f%d p%d\n",
          bestfacet->id, bestdist, upperfacet->id, qh_pointid(qh, point)));
  return bestfacet;
} /* findbestlower */

/*---------------------------------

  qh_findfacet_all(qh, 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(qhT *qh, 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(qh, point, facet, &dist);
    if (dist > *bestdist) {
      *bestdist= dist;
      bestfacet= facet;
      if (dist > qh->MINoutside) {
        *isoutside= True;
        break;
      }
    }
  }
  *numpart= totpart;
  trace3((qh, qh->ferr, 3016, "qh_findfacet_all: f%d dist %2.2g isoutside %d totpart %d\n",
          getid_(bestfacet), *bestdist, *isoutside, totpart));
  return bestfacet;
} /* findfacet_all */

/*---------------------------------

  qh_findgood(qh, 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(qhT *qh, facetT *facetlist, int goodhorizon) {
  facetT *facet, *bestfacet= NULL;
  realT angle, bestangle= REALmax, dist;
  int  numgood=0;

  FORALLfacet_(facetlist) {
    if (facet->good)
      numgood++;
  }
  if (qh->GOODvertex>0 && !qh->MERGING) {
    FORALLfacet_(facetlist) {
      if (!qh_isvertex(qh->GOODvertexp, facet->vertices)) {
        facet->good= False;
        numgood--;
      }
    }
  }
  if (qh->GOODpoint && numgood) {
    FORALLfacet_(facetlist) {
      if (facet->good && facet->normal) {
        zinc_(Zdistgood);
        qh_distplane(qh, qh->GOODpointp, facet, &dist);
        if ((qh->GOODpoint > 0) ^ (dist > 0.0)) {
          facet->good= False;
          numgood--;
        }
      }
    }
  }
  if (qh->GOODthreshold && (numgood || goodhorizon || qh->GOODclosest)) {
    FORALLfacet_(facetlist) {
      if (facet->good && facet->normal) {
        if (!qh_inthresholds(qh, facet->normal, &angle)) {
          facet->good= False;
          numgood--;
          if (angle < bestangle) {
            bestangle= angle;
            bestfacet= facet;
          }
        }
      }
    }
    if (!numgood && (!goodhorizon || qh->GOODclosest)) {
      if (qh->GOODclosest) {
        if (qh->GOODclosest->visible)
          qh->GOODclosest= NULL;
        else {
          qh_inthresholds(qh, qh->GOODclosest->normal, &angle);
          if (angle < bestangle)
            bestfacet= qh->GOODclosest;
        }
      }
      if (bestfacet && bestfacet != qh->GOODclosest) {
        if (qh->GOODclosest)
          qh->GOODclosest->good= False;
        qh->GOODclosest= bestfacet;
        bestfacet->good= True;
        numgood++;
        trace2((qh, qh->ferr, 2044, "qh_findgood: f%d is closest(%2.2g) to thresholds\n",
           bestfacet->id, bestangle));
        return numgood;
      }
    }else if (qh->GOODclosest) { /* numgood > 0 */
      qh->GOODclosest->good= False;
      qh->GOODclosest= NULL;
    }
  }
  zadd_(Zgoodfacet, numgood);
  trace2((qh, qh->ferr, 2045, "qh_findgood: found %d good facets with %d good horizon\n",
               numgood, goodhorizon));
  if (!numgood && qh->GOODvertex>0 && !qh->MERGING)
    return goodhorizon;
  return numgood;
} /* findgood */

/*---------------------------------

  qh_findgood_all(qh, facetlist )
    apply other constraints for good facets (used by qh.PRINTgood)
    if qh.GOODvertex
      facet includes (>0) or doesn't include (<0) point as vertex
      if last good facet and ONLYgood, prints warning and continues
    if qh.SPLITthresholds
      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(qhT *qh, facetT *facetlist) {
  facetT *facet, *bestfacet=NULL;
  realT angle, bestangle= REALmax;
  int  numgood=0, startgood;

  if (!qh->GOODvertex && !qh->GOODthreshold && !qh->GOODpoint
  && !qh->SPLITthresholds)
    return;
  if (!qh->ONLYgood)
    qh_findgood(qh, qh->facet_list, 0);
  FORALLfacet_(facetlist) {
    if (facet->good)
      numgood++;
  }
  if (qh->GOODvertex <0 || (qh->GOODvertex > 0 && qh->MERGING)) {
    FORALLfacet_(facetlist) {
      if (facet->good && ((qh->GOODvertex > 0) ^ !!qh_isvertex(qh->GOODvertexp, facet->vertices))) {
        if (!--numgood) {
          if (qh->ONLYgood) {
            qh_fprintf(qh, qh->ferr, 7064, "qhull warning: good vertex p%d does not match last good facet f%d.  Ignored.\n",
               qh_pointid(qh, qh->GOODvertexp), facet->id);
            return;
          }else if (qh->GOODvertex > 0)
            qh_fprintf(qh, qh->ferr, 7065, "qhull warning: point p%d is not a vertex('QV%d').\n",
                qh->GOODvertex-1, qh->GOODvertex-1);
          else
            qh_fprintf(qh, qh->ferr, 7066, "qhull warning: point p%d is a vertex for every facet('QV-%d').\n",
                -qh->GOODvertex - 1, -qh->GOODvertex - 1);
        }
        facet->good= False;
      }
    }
  }
  startgood= numgood;
  if (qh->SPLITthresholds) {
    FORALLfacet_(facetlist) {
      if (facet->good) {
        if (!qh_inthresholds(qh, facet->normal, &angle)) {
          facet->good= False;
          numgood--;
          if (angle < bestangle) {
            bestangle= angle;
            bestfacet= facet;
          }
        }
      }
    }
    if (!numgood && bestfacet) {
      bestfacet->good= True;
      numgood++;
      trace0((qh, qh->ferr, 23, "qh_findgood_all: f%d is closest(%2.2g) to thresholds\n",
           bestfacet->id, bestangle));
      return;
    }
  }
  qh->num_good= numgood;
  trace0((qh, qh->ferr, 24, "qh_findgood_all: %d good facets remain out of %d facets\n",
        numgood, startgood));
} /* findgood_all */

/*---------------------------------

  qh_furthestnext()
    set qh.facet_next to facet with furthest of all furthest points
    searches all facets on qh.facet_list

  notes:
    this may help avoid precision problems
*/
void qh_furthestnext(qhT *qh /* qh->facet_list */) {
  facetT *facet, *bestfacet= NULL;
  realT dist, bestdist= -REALmax;

  FORALLfacets {
    if (facet->outsideset) {
#if qh_COMPUTEfurthest
      pointT *furthest;
      furthest= (pointT*)qh_setlast(facet->outsideset);
      zinc_(Zcomputefurthest);
      qh_distplane(qh, furthest, facet, &dist);
#else
      dist= facet->furthestdist;
#endif
      if (dist > bestdist) {
        bestfacet= facet;
        bestdist= dist;
      }
    }
  }
  if (bestfacet) {
    qh_removefacet(qh, bestfacet);
    qh_prependfacet(qh, bestfacet, &qh->facet_next);
    trace1((qh, qh->ferr, 1029, "qh_furthestnext: made f%d next facet(dist %.2g)\n",
            bestfacet->id, bestdist));
  }
} /* furthestnext */

/*---------------------------------

  qh_furthestout(qh, facet )
    make furthest outside point the last point of outsideset

  returns:
    updates facet->outsideset
    clears facet->notfurthest
    sets facet->furthestdist

  design:
    determine best point of outsideset
    make it the last point of outsideset
*/
void qh_furthestout(qhT *qh, facetT *facet) {
  pointT *point, **pointp, *bestpoint= NULL;
  realT dist, bestdist= -REALmax;

  FOREACHpoint_(facet->outsideset) {
    qh_distplane(qh, point, facet, &dist);
    zinc_(Zcomputefurthest);
    if (dist > bestdist) {
      bestpoint= point;
      bestdist= dist;
    }
  }
  if (bestpoint) {
    qh_setdel(facet->outsideset, point);
    qh_setappend(qh, &facet->outsideset, point);
#if !qh_COMPUTEfurthest
    facet->furthestdist= bestdist;
#endif
  }
  facet->notfurthest= False;
  trace3((qh, qh->ferr, 3017, "qh_furthestout: p%d is furthest outside point of f%d\n",
          qh_pointid(qh, point), facet->id));
} /* furthestout */


/*---------------------------------

  qh_infiniteloop(qh, facet )
    report infinite loop error due to facet
*/
void qh_infiniteloop(qhT *qh, facetT *facet) {

  qh_fprintf(qh, qh->ferr, 6149, "qhull internal error (qh_infiniteloop): potential infinite loop detected\n");
  qh_errexit(qh, qh_ERRqhull, facet, NULL);
} /* qh_infiniteloop */

/*---------------------------------

  qh_initbuild()
    initialize hull and outside sets with point array
    qh.FIRSTpoint/qh.NUMpoints is point array
    if qh.GOODpoint
      adds qh.GOODpoint to initial hull

  returns:
    qh_facetlist with initial hull
    points partioned into outside sets, coplanar sets, or inside
    initializes qh.GOODpointp, qh.GOODvertexp,

  design:
    initialize global variables used during qh_buildhull
    determine precision constants and points with max/min coordinate values
      if qh.SCALElast, scale last coordinate(for 'd')
    build initial simplex
    partition input points into facets of initial simplex
    set up lists
    if qh.ONLYgood
      check consistency
      add qh.GOODvertex if defined
*/
void qh_initbuild(qhT *qh) {
  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, qh->GOODpoint-1);
  else if (qh->GOODpoint < 0)
    qh->GOODpointp= qh_point(qh, -qh->GOODpoint-1);
  if (qh->GOODvertex > 0)
    qh->GOODvertexp= qh_point(qh, qh->GOODvertex-1);
  else if (qh->GOODvertex < 0)
    qh->GOODvertexp= qh_point(qh, -qh->GOODvertex-1);
  if ((qh->GOODpoint
       && (qh->GOODpointp < qh->first_point  /* also catches !GOODpointp */
           || qh->GOODpointp > qh_point(qh, qh->num_points-1)))
    || (qh->GOODvertex
        && (qh->GOODvertexp < qh->first_point  /* also catches !GOODvertexp */
            || qh->GOODvertexp > qh_point(qh, qh->num_points-1)))) {
    qh_fprintf(qh, qh->ferr, 6150, "qhull input error: either QGn or QVn point is > p%d\n",
             qh->num_points-1);
    qh_errexit(qh, qh_ERRinput, NULL, NULL);
  }
  maxpoints= qh_maxmin(qh, qh->first_point, qh->num_points, qh->hull_dim);
  if (qh->SCALElast)
    qh_scalelast(qh, qh->first_point, qh->num_points, qh->hull_dim,
               qh->MINlastcoord, qh->MAXlastcoord, qh->MAXwidth);
  qh_detroundoff(qh);
  if (qh->DELAUNAY && qh->upper_threshold[qh->hull_dim-1] > REALmax/2
                  && qh->lower_threshold[qh->hull_dim-1] < -REALmax/2) {
    for (i=qh_PRINTEND; i--; ) {
      if (qh->PRINTout[i] == qh_PRINTgeom && qh->DROPdim < 0
          && !qh->GOODthreshold && !qh->SPLITthresholds)
        break;  /* in this case, don't set upper_threshold */
    }
    if (i < 0) {
      if (qh->UPPERdelaunay) { /* matches qh.upperdelaunay in qh_setfacetplane */
        qh->lower_threshold[qh->hull_dim-1]= qh->ANGLEround * qh_ZEROdelaunay;
        qh->GOODthreshold= True;
      }else {
        qh->upper_threshold[qh->hull_dim-1]= -qh->ANGLEround * qh_ZEROdelaunay;
        if (!qh->GOODthreshold)
          qh->SPLITthresholds= True; /* build upper-convex hull even if Qg */
          /* qh_initqhull_globals errors if Qg without Pdk/etc. */
      }
    }
  }
  vertices= qh_initialvertices(qh, qh->hull_dim, maxpoints, qh->first_point, qh->num_points);
  qh_initialhull(qh, vertices);  /* initial qh->facet_list */
  qh_partitionall(qh, vertices, qh->first_point, qh->num_points);
  if (qh->PRINToptions1st || qh->TRACElevel || qh->IStracing) {
    if (qh->TRACElevel || qh->IStracing)
      qh_fprintf(qh, qh->ferr, 8103, "\nTrace level %d for %s | %s\n",
         qh->IStracing ? qh->IStracing : qh->TRACElevel, qh->rbox_command, qh->qhull_command);
    qh_fprintf(qh, qh->ferr, 8104, "Options selected for Qhull %s:\n%s\n", qh_version, qh->qhull_options);
  }
  qh_resetlists(qh, False, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */);
  qh->facet_next= qh->facet_list;
  qh_furthestnext(qh /* qh->facet_list */);
  if (qh->PREmerge) {
    qh->cos_max= qh->premerge_cos;
    qh->centrum_radius= qh->premerge_centrum;
  }
  if (qh->ONLYgood) {
    if (qh->GOODvertex > 0 && qh->MERGING) {
      qh_fprintf(qh, qh->ferr, 6151, "qhull input error: 'Qg QVn' (only good vertex) does not work with merging.\nUse 'QJ' to joggle the input or 'Q0' to turn off merging.\n");
      qh_errexit(qh, qh_ERRinput, NULL, NULL);
    }
    if (!(qh->GOODthreshold || qh->GOODpoint
         || (!qh->MERGEexact && !qh->PREmerge && qh->GOODvertexp))) {
      qh_fprintf(qh, qh->ferr, 6152, "qhull input error: 'Qg' (ONLYgood) needs a good threshold('Pd0D0'), a\n\
good point(QGn or QG-n), or a good vertex with 'QJ' or 'Q0' (QVn).\n");
      qh_errexit(qh, qh_ERRinput, NULL, NULL);
    }
    if (qh->GOODvertex > 0  && !qh->MERGING  /* matches qh_partitionall */
        && !qh_isvertex(qh->GOODvertexp, vertices)) {
      facet= qh_findbestnew(qh, qh->GOODvertexp, qh->facet_list,
                          &dist, !qh_ALL, &isoutside, &numpart);
      zadd_(Zdistgood, numpart);
      if (!isoutside) {
        qh_fprintf(qh, qh->ferr, 6153, "qhull input error: point for QV%d is inside initial simplex.  It can not be made a vertex.\n",
               qh_pointid(qh, qh->GOODvertexp));
        qh_errexit(qh, qh_ERRinput, NULL, NULL);
      }
      if (!qh_addpoint(qh, qh->GOODvertexp, facet, False)) {
        qh_settempfree(qh, &vertices);
        qh_settempfree(qh, &maxpoints);
        return;
      }
    }
    qh_findgood(qh, qh->facet_list, 0);
  }
  qh_settempfree(qh, &vertices);
  qh_settempfree(qh, &maxpoints);
  trace1((qh, qh->ferr, 1030, "qh_initbuild: initial hull created and points partitioned\n"));
} /* initbuild */

/*---------------------------------

  qh_initialhull(qh, 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(qhT *qh, setT *vertices) {
  facetT *facet, *firstfacet, *neighbor, **neighborp;
  realT dist, angle, minangle= REALmax;
#ifndef qh_NOtrace
  int k;
#endif

  qh_createsimplex(qh, vertices);  /* qh->facet_list */
  qh_resetlists(qh, False, qh_RESETvisible);
  qh->facet_next= qh->facet_list;      /* advance facet when processed */
  qh->interior_point= qh_getcenter(qh, vertices);
  firstfacet= qh->facet_list;
  qh_setfacetplane(qh, firstfacet);
  zinc_(Znumvisibility); /* needs to be in printsummary */
  qh_distplane(qh, qh->interior_point, firstfacet, &dist);
  if (dist > 0) {
    FORALLfacets
      facet->toporient ^= (unsigned char)True;
  }
  FORALLfacets
    qh_setfacetplane(qh, facet);
  FORALLfacets {
    if (!qh_checkflipped(qh, facet, NULL, qh_ALL)) {/* due to axis-parallel facet */
      trace1((qh, qh->ferr, 1031, "qh_initialhull: initial orientation incorrect.  Correct all facets\n"));
      facet->flipped= False;
      FORALLfacets {
        facet->toporient ^= (unsigned char)True;
        qh_orientoutside(qh, facet);
      }
      break;
    }
  }
  FORALLfacets {
    if (!qh_checkflipped(qh, facet, NULL, !qh_ALL)) {  /* can happen with 'R0.1' */
      if (qh->DELAUNAY && ! qh->ATinfinity) {
        if (qh->UPPERdelaunay)
          qh_fprintf(qh, 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, 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, qh_ERRinput, NULL, NULL);
      }
      qh_precision(qh, "initial simplex is flat");
      qh_fprintf(qh, qh->ferr, 6154, "Qhull precision error: Initial simplex is flat (facet %d is coplanar with the interior point)\n",
                   facet->id);
      qh_errexit(qh, qh_ERRsingular, NULL, NULL);  /* calls qh_printhelp_singular */
    }
    FOREACHneighbor_(facet) {
      angle= qh_getangle(qh, facet->normal, neighbor->normal);
      minimize_( minangle, angle);
    }
  }
  if (minangle < qh_MAXnarrow && !qh->NOnarrow) {
    realT diff= 1.0 + minangle;

    qh->NARROWhull= True;
    qh_option(qh, "_narrow-hull", NULL, &diff);
    if (minangle < qh_WARNnarrow && !qh->RERUN && qh->PRINTprecision)
      qh_printhelp_narrowhull(qh, qh->ferr, minangle);
  }
  zzval_(Zprocessed)= qh->hull_dim+1;
  qh_checkpolygon(qh, qh->facet_list);
  qh_checkconvex(qh, qh->facet_list,   qh_DATAfault);
#ifndef qh_NOtrace
  if (qh->IStracing >= 1) {
    qh_fprintf(qh, qh->ferr, 8105, "qh_initialhull: simplex constructed, interior point:");
    for (k=0; k < qh->hull_dim; k++)
      qh_fprintf(qh, qh->ferr, 8106, " %6.4g", qh->interior_point[k]);
    qh_fprintf(qh, qh->ferr, 8107, "\n");
  }
#endif
} /* initialhull */

/*---------------------------------

  qh_initialvertices(qh, dim, maxpoints, points, numpoints )
    determines a non-singular set of initial vertices
    maxpoints may include duplicate points

  returns:
    temporary set of dim+1 vertices in descending order by vertex id
    if qh.RANDOMoutside && !qh.ALLpoints
      picks random points
    if dim >= qh_INITIALmax,
      uses min/max x and max points with non-zero determinants

  notes:
    unless qh.ALLpoints,
      uses maxpoints as long as determinate is non-zero
*/
setT *qh_initialvertices(qhT *qh, int dim, setT *maxpoints, pointT *points, int numpoints) {
  pointT *point, **pointp;
  setT *vertices, *simplex, *tested;
  realT randr;
  int idx, point_i, point_n, k;
  boolT nearzero= False;

  vertices= qh_settemp(qh, dim + 1);
  simplex= qh_settemp(qh, dim+1);
  if (qh->ALLpoints)
    qh_maxsimplex(qh, dim, NULL, points, numpoints, &simplex);
  else if (qh->RANDOMoutside) {
    while (qh_setsize(qh, simplex) != dim+1) {
      randr= qh_RANDOMint;
      randr= randr/(qh_RANDOMmax+1);
      idx= (int)floor(qh->num_points * randr);
      while (qh_setin(simplex, qh_point(qh, idx))) {
            idx++; /* in case qh_RANDOMint always returns the same value */
        idx= idx < qh->num_points ? idx : 0;
      }
      qh_setappend(qh, &simplex, qh_point(qh, idx));
    }
  }else if (qh->hull_dim >= qh_INITIALmax) {
    tested= qh_settemp(qh, dim+1);
    qh_setappend(qh, &simplex, SETfirst_(maxpoints));   /* max and min X coord */
    qh_setappend(qh, &simplex, SETsecond_(maxpoints));
    qh_maxsimplex(qh, fmin_(qh_INITIALsearch, dim), maxpoints, points, numpoints, &simplex);
    k= qh_setsize(qh, simplex);
    FOREACHpoint_i_(qh, maxpoints) {
      if (point_i & 0x1) {     /* first pick up max. coord. points */
        if (!qh_setin(simplex, point) && !qh_setin(tested, point)){
          qh_detsimplex(qh, point, simplex, k, &nearzero);
          if (nearzero)
            qh_setappend(qh, &tested, point);
          else {
            qh_setappend(qh, &simplex, point);
            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(qh, point, simplex, k, &nearzero);
        if (nearzero)
          qh_setappend(qh, &tested, point);
        else {
          qh_setappend(qh, &simplex, point);
          k++;
        }
      }
    }
    idx= 0;
    while (k != dim && (point= qh_point(qh, idx++))) {
      if (!qh_setin(simplex, point) && !qh_setin(tested, point)){
        qh_detsimplex(qh, point, simplex, k, &nearzero);
        if (!nearzero){
          qh_setappend(qh, &simplex, point);
          k++;
        }
      }
    }
    qh_settempfree(qh, &tested);
    qh_maxsimplex(qh, dim, maxpoints, points, numpoints, &simplex);
  }else
    qh_maxsimplex(qh, dim, maxpoints, points, numpoints, &simplex);
  FOREACHpoint_(simplex)
    qh_setaddnth(qh, &vertices, 0, qh_newvertex(qh, point)); /* descending order */
  qh_settempfree(qh, &simplex);
  return vertices;
} /* initialvertices */


/*---------------------------------

  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 */

/*---------------------------------

  qh_makenewfacets(qh, 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(qhT *qh, 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(qh, point);
  qh_appendvertex(qh, 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(qh, visible, apex, &numnew);
    }
    if (visible->simplicial)
      newfacet= qh_makenew_simplicial(qh, 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, qh->ferr, 1032, "qh_makenewfacets: created %d new facets from point p%d to horizon\n",
          numnew, qh_pointid(qh, point)));
  if (qh->IStracing >= 4)
    qh_printfacetlist(qh, qh->newfacet_list, NULL, qh_ALL);
  return apex;
} /* makenewfacets */

/*---------------------------------

  qh_matchduplicates(qh, 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(qhT *qh, 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(qh, hashsize, atfacet->vertices, qh->hull_dim, 1,
                     SETelem_(atfacet->vertices, atskip));
  trace2((qh, 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(qh, 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, 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, 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, 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(qh, facet, newfacet, &low, &high);
            dist2= qh_getdistance(qh, newfacet, facet, &low, &high);
            minimize_(mindist, dist2);
            if (mindist > maxdist) {
              maxdist= mindist;
              maxmatch= facet;
              maxskip= skip;
              maxmatch2= newfacet;
              maxskip2= newskip;
            }
            trace3((qh, 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, 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, qh_ERRqhull, newfacet, NULL);
        }
      }
    } /* end of for each new facet at hash */
    if (!makematch) {
      if (!maxmatch) {
        qh_fprintf(qh, 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, 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, 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(qh, "ridge with multiple neighbors");
      if (qh->IStracing >= 4)
        qh_errprint(qh, "DUPLICATED/MATCH", maxmatch, maxmatch2, NULL, NULL);
    }
  }
} /* matchduplicates */

/*---------------------------------

  qh_nearcoplanar()
    for all facets, remove near-inside points from facet->coplanarset
    coplanar points defined by innerplane from qh_outerinner()

  returns:
    if qh->KEEPcoplanar && !qh->KEEPinside
      facet->coplanarset only contains coplanar points
    if qh.JOGGLEmax
      drops inner plane by another qh.JOGGLEmax diagonal since a
        vertex could shift out while a coplanar point shifts in

  notes:
    used for qh.PREmerge and qh.JOGGLEmax
    must agree with computation of qh.NEARcoplanar in qh_detroundoff(qh)
  design:
    if not keeping coplanar or inside points
      free all coplanar sets
    else if not keeping both coplanar and inside points
      remove !coplanar or !inside points from coplanar sets
*/
void qh_nearcoplanar(qhT *qh /* qh.facet_list */) {
  facetT *facet;
  pointT *point, **pointp;
  int numpart;
  realT dist, innerplane;

  if (!qh->KEEPcoplanar && !qh->KEEPinside) {
    FORALLfacets {
      if (facet->coplanarset)
        qh_setfree(qh, &facet->coplanarset);
    }
  }else if (!qh->KEEPcoplanar || !qh->KEEPinside) {
    qh_outerinner(qh, NULL, NULL, &innerplane);
    if (qh->JOGGLEmax < REALmax/2)
      innerplane -= qh->JOGGLEmax * sqrt((realT)qh->hull_dim);
    numpart= 0;
    FORALLfacets {
      if (facet->coplanarset) {
        FOREACHpoint_(facet->coplanarset) {
          numpart++;
          qh_distplane(qh, point, facet, &dist);
          if (dist < innerplane) {
            if (!qh->KEEPinside)
              SETref_(point)= NULL;
          }else if (!qh->KEEPcoplanar)
            SETref_(point)= NULL;
        }
        qh_setcompact(qh, facet->coplanarset);
      }
    }
    zzadd_(Zcheckpart, numpart);
  }
} /* nearcoplanar */

/*---------------------------------

  qh_nearvertex(qh, facet, point, bestdist )
    return nearest vertex in facet to point

  returns:
    vertex and its distance

  notes:
    if qh.DELAUNAY
      distance is measured in the input set
    searches neighboring tricoplanar facets (requires vertexneighbors)
      Slow implementation.  Recomputes vertex set for each point.
    The vertex set could be stored in the qh.keepcentrum facet.
*/
vertexT *qh_nearvertex(qhT *qh, facetT *facet, pointT *point, realT *bestdistp) {
  realT bestdist= REALmax, dist;
  vertexT *bestvertex= NULL, *vertex, **vertexp, *apex;
  coordT *center;
  facetT *neighbor, **neighborp;
  setT *vertices;
  int dim= qh->hull_dim;

  if (qh->DELAUNAY)
    dim--;
  if (facet->tricoplanar) {
    if (!qh->VERTEXneighbors || !facet->center) {
      qh_fprintf(qh, qh->ferr, 6158, "qhull internal error (qh_nearvertex): qh.VERTEXneighbors and facet->center required for tricoplanar facets\n");
      qh_errexit(qh, qh_ERRqhull, facet, NULL);
    }
    vertices= qh_settemp(qh, qh->TEMPsize);
    apex= SETfirstt_(facet->vertices, vertexT);
    center= facet->center;
    FOREACHneighbor_(apex) {
      if (neighbor->center == center) {
        FOREACHvertex_(neighbor->vertices)
          qh_setappend(qh, &vertices, vertex);
      }
    }
  }else
    vertices= facet->vertices;
  FOREACHvertex_(vertices) {
    dist= qh_pointdist(vertex->point, point, -dim);
    if (dist < bestdist) {
      bestdist= dist;
      bestvertex= vertex;
    }
  }
  if (facet->tricoplanar)
    qh_settempfree(qh, &vertices);
  *bestdistp= sqrt(bestdist);
  if (!bestvertex) {
      qh_fprintf(qh, qh->ferr, 6261, "qhull internal error (qh_nearvertex): did not find bestvertex for f%d p%d\n", facet->id, qh_pointid(qh, point));
      qh_errexit(qh, qh_ERRqhull, facet, NULL);
  }
  trace3((qh, qh->ferr, 3019, "qh_nearvertex: v%d dist %2.2g for f%d p%d\n",
        bestvertex->id, *bestdistp, facet->id, qh_pointid(qh, point))); /* bestvertex!=0 by QH2161 */
  return bestvertex;
} /* nearvertex */

/*---------------------------------

  qh_newhashtable(qh, newsize )
    returns size of qh.hash_table of at least newsize slots

  notes:
    assumes qh.hash_table is NULL
    qh_HASHfactor determines the number of extra slots
    size is not divisible by 2, 3, or 5
*/
int qh_newhashtable(qhT *qh, int newsize) {
  int size;

  size= ((newsize+1)*qh_HASHfactor) | 0x1;  /* odd number */
  while (True) {
    if (newsize<0 || size<0) {
        qh_fprintf(qh, qh->qhmem.ferr, 6236, "qhull error (qh_newhashtable): negative request (%d) or size (%d).  Did int overflow due to high-D?\n", newsize, size); /* WARN64 */
        qh_errexit(qh, qhmem_ERRmem, NULL, NULL);
    }
    if ((size%3) && (size%5))
      break;
    size += 2;
    /* loop terminates because there is an infinite number of primes */
  }
  qh->hash_table= qh_setnew(qh, size);
  qh_setzero(qh, qh->hash_table, 0, size);
  return size;
} /* newhashtable */

/*---------------------------------

  qh_newvertex(qh, point )
    returns a new vertex for point
*/
vertexT *qh_newvertex(qhT *qh, pointT *point) {
  vertexT *vertex;

  zinc_(Ztotvertices);
  vertex= (vertexT *)qh_memalloc(qh, (int)sizeof(vertexT));
  memset((char *) vertex, (size_t)0, sizeof(vertexT));
  if (qh->vertex_id == UINT_MAX) {
    qh_memfree(qh, vertex, (int)sizeof(vertexT));
    qh_fprintf(qh, qh->ferr, 6159, "qhull error: more than 2^32 vertices.  vertexT.id field overflows.  Vertices would not be sorted correctly.\n");
    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
  }
  if (qh->vertex_id == qh->tracevertex_id)
    qh->tracevertex= vertex;
  vertex->id= qh->vertex_id++;
  vertex->point= point;
  trace4((qh, qh->ferr, 4060, "qh_newvertex: vertex p%d(v%d) created\n", qh_pointid(qh, vertex->point),
          vertex->id));
  return(vertex);
} /* newvertex */

/*---------------------------------

  qh_nextridge3d( atridge, facet, vertex )
    return next ridge and vertex for a 3d facet
    returns NULL on error
    [for QhullFacet::nextRidge3d] Does not call qh_errexit nor access qhT.

  notes:
    in qh_ORIENTclock order
    this is a O(n^2) implementation to trace all ridges
    be sure to stop on any 2nd visit
    same as QhullRidge::nextRidge3d
    does not use qhT or qh_errexit [QhullFacet.cpp]

  design:
    for each ridge
      exit if it is the ridge after atridge
*/
ridgeT *qh_nextridge3d(ridgeT *atridge, facetT *facet, vertexT **vertexp) {
  vertexT *atvertex, *vertex, *othervertex;
  ridgeT *ridge, **ridgep;

  if ((atridge->top == facet) ^ qh_ORIENTclock)
    atvertex= SETsecondt_(atridge->vertices, vertexT);
  else
    atvertex= SETfirstt_(atridge->vertices, vertexT);
  FOREACHridge_(facet->ridges) {
    if (ridge == atridge)
      continue;
    if ((ridge->top == facet) ^ qh_ORIENTclock) {
      othervertex= SETsecondt_(ridge->vertices, vertexT);
      vertex= SETfirstt_(ridge->vertices, vertexT);
    }else {
      vertex= SETsecondt_(ridge->vertices, vertexT);
      othervertex= SETfirstt_(ridge->vertices, vertexT);
    }
    if (vertex == atvertex) {
      if (vertexp)
        *vertexp= othervertex;
      return ridge;
    }
  }
  return NULL;
} /* nextridge3d */
#else /* qh_NOmerge */
void qh_matchduplicates(qhT *qh, facetT *atfacet, int atskip, int hashsize, int *hashcount) {
}
ridgeT *qh_nextridge3d(ridgeT *atridge, facetT *facet, vertexT **vertexp) {

  return NULL;
}
#endif /* qh_NOmerge */

/*---------------------------------

  qh_outcoplanar()
    move points from all facets' outsidesets to their coplanarsets

  notes:
    for post-processing under qh.NARROWhull

  design:
    for each facet
      for each outside point for facet
        partition point into coplanar set
*/
void qh_outcoplanar(qhT *qh /* facet_list */) {
  pointT *point, **pointp;
  facetT *facet;
  realT dist;

  trace1((qh, qh->ferr, 1033, "qh_outcoplanar: move outsideset to coplanarset for qh->NARROWhull\n"));
  FORALLfacets {
    FOREACHpoint_(facet->outsideset) {
      qh->num_outside--;
      if (qh->KEEPcoplanar || qh->KEEPnearinside) {
        qh_distplane(qh, point, facet, &dist);
        zinc_(Zpartition);
        qh_partitioncoplanar(qh, point, facet, &dist);
      }
    }
    qh_setfree(qh, &facet->outsideset);
  }
} /* outcoplanar */

/*---------------------------------

  qh_point(qh, id )
    return point for a point id, or NULL if unknown

  alternative code:
    return((pointT *)((unsigned   long)qh.first_point
           + (unsigned long)((id)*qh.normal_size)));
*/
pointT *qh_point(qhT *qh, int id) {

  if (id < 0)
    return NULL;
  if (id < qh->num_points)
    return qh->first_point + id * qh->hull_dim;
  id -= qh->num_points;
  if (id < qh_setsize(qh, qh->other_points))
    return SETelemt_(qh->other_points, id, pointT);
  return NULL;
} /* point */

/*---------------------------------

  qh_point_add(qh, set, point, elem )
    stores elem at set[point.id]

  returns:
    access function for qh_pointfacet and qh_pointvertex

  notes:
    checks point.id
*/
void qh_point_add(qhT *qh, setT *set, pointT *point, void *elem) {
  int id, size;

  SETreturnsize_(set, size);
  if ((id= qh_pointid(qh, point)) < 0)
    qh_fprintf(qh, qh->ferr, 7067, "qhull internal warning (point_add): unknown point %p id %d\n",
      point, id);
  else if (id >= size) {
    qh_fprintf(qh, qh->ferr, 6160, "qhull internal errror(point_add): point p%d is out of bounds(%d)\n",
             id, size);
    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
  }else
    SETelem_(set, id)= elem;
} /* point_add */


/*---------------------------------

  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_(qh, facets) { ... }
    SETelem_(facets, i)

  design:
    for each facet
      add each vertex
      add each coplanar point
      add each outside point
*/
setT *qh_pointfacet(qhT *qh /*qh.facet_list*/) {
  int numpoints= qh->num_points + qh_setsize(qh, qh->other_points);
  setT *facets;
  facetT *facet;
  vertexT *vertex, **vertexp;
  pointT *point, **pointp;

  facets= qh_settemp(qh, numpoints);
  qh_setzero(qh, facets, 0, numpoints);
  qh->vertex_visit++;
  FORALLfacets {
    FOREACHvertex_(facet->vertices) {
      if (vertex->visitid != qh->vertex_visit) {
        vertex->visitid= qh->vertex_visit;
        qh_point_add(qh, facets, vertex->point, facet);
      }
    }
    FOREACHpoint_(facet->coplanarset)
      qh_point_add(qh, facets, point, facet);
    FOREACHpoint_(facet->outsideset)
      qh_point_add(qh, facets, point, facet);
  }
  return facets;
} /* pointfacet */

/*---------------------------------

  qh_pointvertex(qh, )
    return temporary set of vertices indexed by point id
    entry is NULL if no vertex for a point
      this will include qh.GOODpointp

  access:
    FOREACHvertex_i_(qh, vertices) { ... }
    SETelem_(vertices, i)
*/
setT *qh_pointvertex(qhT *qh /*qh.facet_list*/) {
  int numpoints= qh->num_points + qh_setsize(qh, qh->other_points);
  setT *vertices;
  vertexT *vertex;

  vertices= qh_settemp(qh, numpoints);
  qh_setzero(qh, vertices, 0, numpoints);
  FORALLvertices
    qh_point_add(qh, vertices, vertex->point, vertex);
  return vertices;
} /* pointvertex */


/*---------------------------------

  qh_prependfacet(qh, facet, facetlist )
    prepend facet to the start of a facetlist

  returns:
    increments qh.numfacets
    updates facetlist, qh.facet_list, facet_next

  notes:
    be careful of prepending since it can lose a pointer.
      e.g., can lose _next by deleting and then prepending before _next
*/
void qh_prependfacet(qhT *qh, facetT *facet, facetT **facetlist) {
  facetT *prevfacet, *list;


  trace4((qh, qh->ferr, 4061, "qh_prependfacet: prepend f%d before f%d\n",
          facet->id, getid_(*facetlist)));
  if (!*facetlist)
    (*facetlist)= qh->facet_tail;
  list= *facetlist;
  prevfacet= list->previous;
  facet->previous= prevfacet;
  if (prevfacet)
    prevfacet->next= facet;
  list->previous= facet;
  facet->next= *facetlist;
  if (qh->facet_list == list)  /* this may change *facetlist */
    qh->facet_list= facet;
  if (qh->facet_next == list)
    qh->facet_next= facet;
  *facetlist= facet;
  qh->num_facets++;
} /* prependfacet */


/*---------------------------------

  qh_printhashtable(qh, fp )
    print hash table to fp

  notes:
    not in I/O to avoid bringing io_r.c in

  design:
    for each hash entry
      if defined
        if unmatched or will merge (NULL, qh_MERGEridge, qh_DUPLICATEridge)
          print entry and neighbors
*/
void qh_printhashtable(qhT *qh, FILE *fp) {
  facetT *facet, *neighbor;
  int id, facet_i, facet_n, neighbor_i= 0, neighbor_n= 0;
  vertexT *vertex, **vertexp;

  FOREACHfacet_i_(qh, qh->hash_table) {
    if (facet) {
      FOREACHneighbor_i_(qh, facet) {
        if (!neighbor || neighbor == qh_MERGEridge || neighbor == qh_DUPLICATEridge)
          break;
      }
      if (neighbor_i == neighbor_n)
        continue;
      qh_fprintf(qh, fp, 9283, "hash %d f%d ", facet_i, facet->id);
      FOREACHvertex_(facet->vertices)
        qh_fprintf(qh, fp, 9284, "v%d ", vertex->id);
      qh_fprintf(qh, fp, 9285, "\n neighbors:");
      FOREACHneighbor_i_(qh, facet) {
        if (neighbor == qh_MERGEridge)
          id= -3;
        else if (neighbor == qh_DUPLICATEridge)
          id= -2;
        else
          id= getid_(neighbor);
        qh_fprintf(qh, fp, 9286, " %d", id);
      }
      qh_fprintf(qh, fp, 9287, "\n");
    }
  }
} /* printhashtable */


/*---------------------------------

  qh_printlists(qh, fp )
    print out facet and vertex list for debugging (without 'f/v' tags)
*/
void qh_printlists(qhT *qh) {
  facetT *facet;
  vertexT *vertex;
  int count= 0;

  qh_fprintf(qh, qh->ferr, 8108, "qh_printlists: facets:");
  FORALLfacets {
    if (++count % 100 == 0)
      qh_fprintf(qh, qh->ferr, 8109, "\n     ");
    qh_fprintf(qh, qh->ferr, 8110, " %d", facet->id);
  }
  qh_fprintf(qh, qh->ferr, 8111, "\n  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, qh->ferr, 8112, "\n     ");
    qh_fprintf(qh, qh->ferr, 8113, " %d", vertex->id);
  }
  qh_fprintf(qh, qh->ferr, 8114, "\n");
} /* printlists */

/*---------------------------------

  qh_resetlists(qh, 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(qhT *qh, boolT stats, boolT resetVisible /*qh.newvertex_list newfacet_list visible_list*/) {
  vertexT *vertex;
  facetT *newfacet, *visible;
  int totnew=0, totver=0;

  if (stats) {
    FORALLvertex_(qh->newvertex_list)
      totver++;
    FORALLnew_facets
      totnew++;
    zadd_(Zvisvertextot, totver);
    zmax_(Zvisvertexmax, totver);
    zadd_(Znewfacettot, totnew);
    zmax_(Znewfacetmax, totnew);
  }
  FORALLvertex_(qh->newvertex_list)
    vertex->newlist= False;
  qh->newvertex_list= NULL;
  FORALLnew_facets
    newfacet->newfacet= False;
  qh->newfacet_list= NULL;
  if (resetVisible) {
    FORALLvisible_facets {
      visible->f.replace= NULL;
      visible->visible= False;
    }
    qh->num_visible= 0;
  }
  qh->visible_list= NULL; /* may still have visible facets via qh_triangulate */
  qh->NEWfacets= False;
} /* resetlists */

/*---------------------------------

  qh_setvoronoi_all(qh)
    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(qhT *qh) {
  facetT *facet;

  qh_clearcenters(qh, qh_ASvoronoi);
  qh_vertexneighbors(qh);

  FORALLfacets {
    if (!facet->normal || !facet->upperdelaunay || qh->UPPERdelaunay) {
      if (!facet->center)
        facet->center= qh_facetcenter(qh, facet->vertices);
    }
  }
} /* setvoronoi_all */

#ifndef qh_NOmerge

/*---------------------------------

  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(qhT *qh /*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, qh->ferr, 1034, "qh_triangulate: triangulate non-simplicial facets\n"));
  if (qh->hull_dim == 2)
    return;
  if (qh->VORONOI) {  /* otherwise lose Voronoi centers [could rebuild vertex set from tricoplanar] */
    qh_clearcenters(qh, qh_ASvoronoi);
    qh_vertexneighbors(qh);
  }
  qh->ONLYgood= False; /* for makenew_nonsimplicial */
  qh->visit_id++;
  qh->NEWfacets= True;
  qh->degen_mergeset= qh_settemp(qh, 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(qh, facet, &new_vertex_list);
  }
  trace2((qh, 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(qh, facet->ridges) > 0) {
        qh_fprintf(qh, qh->ferr, 6161, "qhull error (qh_triangulate): ridges still defined for f%d\n", facet->id);
        qh_errexit(qh, qh_ERRqhull, facet, NULL);
      }
      qh_setfree(qh, &facet->ridges);
    }
    if (SETfirst_(facet->vertices) == SETsecond_(facet->vertices)) {
      zinc_(Ztrinull);
      qh_triangulate_null(qh, facet);
    }
  }
  trace2((qh, qh->ferr, 2048, "qh_triangulate: delete %d or more mirror facets -- same vertices and neighbors\n", qh_setsize(qh, qh->degen_mergeset)));
  qh->visible_list= qh->facet_tail;
  while ((merge= (mergeT*)qh_setdellast(qh->degen_mergeset))) {
    facet1= merge->facet1;
    facet2= merge->facet2;
    mergetype= merge->type;
    qh_memfree(qh, merge, (int)sizeof(mergeT));
    if (mergetype == MRGmirror) {
      zinc_(Ztrimirror);
      qh_triangulate_mirror(qh, facet1, facet2);
    }
  }
  qh_settempfree(qh, &qh->degen_mergeset);
  trace2((qh, 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 /*qh.newvertex_list, empty newfacet_list and visible_list*/);
  qh_resetlists(qh, False, !qh_RESETvisible /*qh.newvertex_list, empty newfacet_list and visible_list*/);

  trace2((qh, qh->ferr, 2050, "qh_triangulate: identify degenerate tricoplanar facets from f%d\n", getid_(new_facet_list)));
  trace2((qh, 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_(qh, facet) {
        if (neighbor_i == 0) {  /* first iteration */
          if (neighbor->tricoplanar)
            orig_neighbor= neighbor->f.triowner;
          else
            orig_neighbor= neighbor;
        }else {
          if (neighbor->tricoplanar)
            otherfacet= neighbor->f.triowner;
          else
            otherfacet= neighbor;
          if (orig_neighbor == otherfacet) {
            zinc_(Ztridegen);
            facet->degenerate= True;
            break;
          }
        }
      }
    }
  }

  trace2((qh, 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(qh, facet);
        qh->num_visible--;
      }else {  /* a non-simplicial facet followed by its tricoplanars */
        if (visible && !owner) {
          /*  RBOX 200 s D5 t1001471447 | QHULL Qt C-0.01 Qx Qc Tv Qt -- f4483 had 6 vertices/neighbors and 8 ridges */
          trace2((qh, qh->ferr, 2053, "qh_triangulate: all tricoplanar facets degenerate for non-simplicial facet f%d\n",
                       visible->id));
          qh_delfacet(qh, visible);
          qh->num_visible--;
        }
        visible= facet;
        owner= NULL;
      }
    }else if (facet->tricoplanar) {
      if (facet->f.triowner != visible || visible==NULL) {
        qh_fprintf(qh, qh->ferr, 6162, "qhull error (qh_triangulate): tricoplanar facet f%d not owned by its visible, non-simplicial facet f%d\n", facet->id, getid_(visible));
        qh_errexit2(qh, qh_ERRqhull, facet, visible);
      }
      if (owner)
        facet->f.triowner= owner;
      else if (!facet->degenerate) {
        owner= facet;
        nextfacet= visible->next; /* rescan tricoplanar facets with owner, visible!=0 by QH6162 */
        facet->keepcentrum= True;  /* one facet owns ->normal, etc. */
        facet->coplanarset= visible->coplanarset;
        facet->outsideset= visible->outsideset;
        visible->coplanarset= NULL;
        visible->outsideset= NULL;
        if (!qh->TRInormals) { /* center and normal copied to tricoplanar facets */
          visible->center= NULL;
          visible->normal= NULL;
        }
        qh_delfacet(qh, visible);
        qh->num_visible--;
      }
    }
  }
  if (visible && !owner) {
    trace2((qh, qh->ferr, 2054, "qh_triangulate: all tricoplanar facets degenerate for last non-simplicial facet f%d\n",
                 visible->id));
    qh_delfacet(qh, visible);
    qh->num_visible--;
  }
  qh->NEWfacets= False;
  qh->ONLYgood= onlygood; /* restore value */
  if (qh->CHECKfrequently)
    qh_checkpolygon(qh, qh->facet_list);
  qh->hasTriangulation= True;
} /* triangulate */


/*---------------------------------

  qh_triangulate_facet(qh, facetA, &firstVertex )
    triangulate a non-simplicial facet
      if qh.CENTERtype=qh_ASvoronoi, sets its Voronoi center
  returns:
    qh.newfacet_list == simplicial facets
      facet->tricoplanar set and ->keepcentrum false
      facet->degenerate set if duplicated apex
      facet->f.trivisible set to facetA
      facet->center copied from facetA (created if qh_ASvoronoi)
        qh_eachvoronoi, qh_detvridge, qh_detvridge3 assume centers copied
      facet->normal,offset,maxoutside copied from facetA

  notes:
      only called by qh_triangulate
      qh_makenew_nonsimplicial uses neighbor->seen for the same
      if qh.TRInormals, newfacet->normal will need qh_free
        if qh.TRInormals and qh_AScentrum, newfacet->center will need qh_free
        keepcentrum is also set on Zwidefacet in qh_mergefacet
        freed by qh_clearcenters

  see also:
      qh_addpoint() -- add a point
      qh_makenewfacets() -- construct a cone of facets for a new vertex

  design:
      if qh_ASvoronoi,
         compute Voronoi center (facet->center)
      select first vertex (highest ID to preserve ID ordering of ->vertices)
      triangulate from vertex to ridges
      copy facet->center, normal, offset
      update vertex neighbors
*/
void qh_triangulate_facet(qhT *qh, facetT *facetA, vertexT **first_vertex) {
  facetT *newfacet;
  facetT *neighbor, **neighborp;
  vertexT *apex;
  int numnew=0;

  trace3((qh, qh->ferr, 3020, "qh_triangulate_facet: triangulate facet f%d\n", facetA->id));

  if (qh->IStracing >= 4)
    qh_printfacet(qh, 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(qh, facetA->vertices);
  }
  qh_willdelete(qh, facetA, NULL);
  qh->newfacet_list= qh->facet_tail;
  facetA->visitid= qh->visit_id;
  apex= SETfirstt_(facetA->vertices, vertexT);
  qh_makenew_nonsimplicial(qh, 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= (double*)qh_memalloc(qh, qh->normal_size);
        memcpy((char *)newfacet->normal, facetA->normal, qh->normal_size);
      }
      if (qh->CENTERtype == qh_AScentrum)
        newfacet->center= qh_getcentrum(qh, newfacet);
      else if (qh->CENTERtype == qh_ASvoronoi && facetA->center){
        newfacet->center= (double*)qh_memalloc(qh, qh->center_size);
        memcpy((char *)newfacet->center, facetA->center, qh->center_size);
      }
    }else {
      newfacet->keepcentrum= False;
      /* one facet will have keepcentrum=True at end of qh_triangulate */
      newfacet->normal= facetA->normal;
      newfacet->center= facetA->center;
    }
    newfacet->offset= facetA->offset;
#if qh_MAXoutside
    newfacet->maxoutside= facetA->maxoutside;
#endif
  }
  qh_matchnewfacets(qh /*qh.newfacet_list*/);
  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 /*qh.newfacet_list, qh.empty visible_list and qh.newvertex_list*/);
  qh_resetlists(qh, False, !qh_RESETvisible /*qh.newfacet_list, qh.empty visible_list and qh.newvertex_list*/);
} /* triangulate_facet */

/*---------------------------------

  qh_triangulate_link(qh, 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(qhT *qh, facetT *oldfacetA, facetT *facetA, facetT *oldfacetB, facetT *facetB) {
  int errmirror= False;

  trace3((qh, 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(qh, facetA, facetB, MRGmirror, NULL);
  }else if (qh_setin(facetB->neighbors, facetA))
    errmirror= True;
  if (errmirror) {
    qh_fprintf(qh, 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, qh_ERRqhull, facetA, facetB);
  }
  qh_setreplace(qh, facetB->neighbors, oldfacetB, facetA);
  qh_setreplace(qh, facetA->neighbors, oldfacetA, facetB);
} /* triangulate_link */

/*---------------------------------

  qh_triangulate_mirror(qh, 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(qhT *qh, facetT *facetA, facetT *facetB) {
  facetT *neighbor, *neighborB;
  int neighbor_i, neighbor_n;

  trace3((qh, qh->ferr, 3022, "qh_triangulate_mirror: delete mirrored facets f%d and f%d\n",
         facetA->id, facetB->id));
  FOREACHneighbor_i_(qh, facetA) {
    neighborB= SETelemt_(facetB->neighbors, neighbor_i, facetT);
    if (neighbor == neighborB)
      continue; /* occurs twice */
    qh_triangulate_link(qh, facetA, neighbor, facetB, neighborB);
  }
  qh_willdelete(qh, facetA, NULL);
  qh_willdelete(qh, facetB, NULL);
} /* triangulate_mirror */

/*---------------------------------

  qh_triangulate_null(qh, facetA)
    remove null facetA from qh_triangulate_facet()
      a null facet has vertex #1 (apex) == vertex #2
  returns:
    adds facetA to ->visible for deletion after qh_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(qhT *qh, facetT *facetA) {
  facetT *neighbor, *otherfacet;

  trace3((qh, qh->ferr, 3023, "qh_triangulate_null: delete null facet f%d\n", facetA->id));
  neighbor= SETfirstt_(facetA->neighbors, facetT);
  otherfacet= SETsecondt_(facetA->neighbors, facetT);
  qh_triangulate_link(qh, facetA, neighbor, facetA, otherfacet);
  qh_willdelete(qh, facetA, NULL);
} /* triangulate_null */

#else /* qh_NOmerge */
void qh_triangulate(qhT *qh) {
}
#endif /* qh_NOmerge */

   /*---------------------------------

  qh_vertexintersect(qh, vertexsetA, vertexsetB )
    intersects two vertex sets (inverse id ordered)
    vertexsetA is a temporary set at the top of qh->qhmem.tempstack

  returns:
    replaces vertexsetA with the intersection

  notes:
    could overwrite vertexsetA if currently too slow
*/
void qh_vertexintersect(qhT *qh, setT **vertexsetA,setT *vertexsetB) {
  setT *intersection;

  intersection= qh_vertexintersect_new(qh, *vertexsetA, vertexsetB);
  qh_settempfree(qh, vertexsetA);
  *vertexsetA= intersection;
  qh_settemppush(qh, intersection);
} /* vertexintersect */

/*---------------------------------

  qh_vertexintersect_new(qh, )
    intersects two vertex sets (inverse id ordered)

  returns:
    a new set
*/
setT *qh_vertexintersect_new(qhT *qh, setT *vertexsetA,setT *vertexsetB) {
  setT *intersection= qh_setnew(qh, qh->hull_dim - 1);
  vertexT **vertexA= SETaddr_(vertexsetA, vertexT);
  vertexT **vertexB= SETaddr_(vertexsetB, vertexT);

  while (*vertexA && *vertexB) {
    if (*vertexA  == *vertexB) {
      qh_setappend(qh, &intersection, *vertexA);
      vertexA++; vertexB++;
    }else {
      if ((*vertexA)->id > (*vertexB)->id)
        vertexA++;
      else
        vertexB++;
    }
  }
  return intersection;
} /* vertexintersect_new */

/*---------------------------------

  qh_vertexneighbors(qh)
    for each vertex in qh.facet_list,
      determine its neighboring facets

  returns:
    sets qh.VERTEXneighbors
      nop if qh.VERTEXneighbors already set
      qh_addpoint() will maintain them

  notes:
    assumes all vertex->neighbors are NULL

  design:
    for each facet
      for each vertex
        append facet to vertex->neighbors
*/
void qh_vertexneighbors(qhT *qh /*qh.facet_list*/) {
  facetT *facet;
  vertexT *vertex, **vertexp;

  if (qh->VERTEXneighbors)
    return;
  trace1((qh, qh->ferr, 1035, "qh_vertexneighbors: determining neighboring facets for each vertex\n"));
  qh->vertex_visit++;
  FORALLfacets {
    if (facet->visible)
      continue;
    FOREACHvertex_(facet->vertices) {
      if (vertex->visitid != qh->vertex_visit) {
        vertex->visitid= qh->vertex_visit;
        vertex->neighbors= qh_setnew(qh, qh->hull_dim);
      }
      qh_setappend(qh, &vertex->neighbors, facet);
    }
  }
  qh->VERTEXneighbors= True;
} /* vertexneighbors */

/*---------------------------------

  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 */
geometry/src/mem_r.h0000644000176200001440000002173413432323614014154 0ustar  liggesusers/*
  ---------------------------------

   mem_r.h
     prototypes for memory management functions

   see qh-mem_r.htm, mem_r.c and qset_r.h

   for error handling, writes message and calls
     qh_errexit(qhT *qh, qhmem_ERRmem, NULL, NULL) if insufficient memory
       and
     qh_errexit(qhT *qh, qhmem_ERRqhull, NULL, NULL) otherwise

   Copyright (c) 1993-2015 The Geometry Center.
   $Id: //main/2015/qhull/src/libqhull_r/mem_r.h#4 $$Change: 2079 $
   $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
*/

#ifndef qhDEFmem
#define qhDEFmem 1

#include 

#ifndef DEFsetT
#define DEFsetT 1
typedef struct setT setT;          /* defined in qset_r.h */
#endif

#ifndef DEFqhT
#define DEFqhT 1
typedef struct qhT qhT;          /* defined in libqhull_r.h */
#endif

/*---------------------------------

  qh_NOmem
    turn off quick-fit memory allocation

  notes:
    mem_r.c implements Quickfit memory allocation for about 20% time
    savings.  If it fails on your machine, try to locate the
    problem, and send the answer to qhull@qhull.org.  If this can
    not be done, define qh_NOmem to use malloc/free instead.

   #define qh_NOmem
*/

/*---------------------------------

qh_TRACEshort
Trace short and quick memory allocations at T5

*/
#define qh_TRACEshort

/*-------------------------------------------
    to avoid bus errors, memory allocation must consider alignment requirements.
    malloc() automatically takes care of alignment.   Since mem_r.c manages
    its own memory, we need to explicitly specify alignment in
    qh_meminitbuffers().

    A safe choice is sizeof(double).  sizeof(float) may be used if doubles
    do not occur in data structures and pointers are the same size.  Be careful
    of machines (e.g., DEC Alpha) with large pointers.  If gcc is available,
    use __alignof__(double) or fmax_(__alignof__(float), __alignof__(void *)).

   see qh_MEMalign in user.h for qhull's alignment
*/

#define qhmem_ERRmem 4    /* matches qh_ERRmem in libqhull_r.h */
#define qhmem_ERRqhull 5  /* matches qh_ERRqhull in libqhull_r.h */

/*----------------------------------

  ptr_intT
    for casting a void * to an integer-type that holds a pointer
    Used for integer expressions (e.g., computing qh_gethash() in poly_r.c)

  notes:
    WARN64 -- these notes indicate 64-bit issues
    On 64-bit machines, a pointer may be larger than an 'int'.
    qh_meminit()/mem_r.c checks that 'ptr_intT' holds a 'void*'
    ptr_intT is typically a signed value, but not necessarily so
    size_t is typically unsigned, but should match the parameter type
    Qhull uses int instead of size_t except for system calls such as malloc, qsort, qh_malloc, etc.
    This matches Qt convention and is easier to work with.
*/
#if (defined(__MINGW64__)) && defined(_WIN64)
typedef long long ptr_intT;
#elif (_MSC_VER) && defined(_WIN64)
typedef long long ptr_intT;
#else
typedef long ptr_intT;
#endif

/*----------------------------------

  qhmemT
    global memory structure for mem_r.c

 notes:
   users should ignore qhmem except for writing extensions
   qhmem is allocated in mem_r.c

   qhmem could be swapable like qh and qhstat, but then
   multiple qh's and qhmem's would need to keep in synch.
   A swapable qhmem would also waste memory buffers.  As long
   as memory operations are atomic, there is no problem with
   multiple qh structures being active at the same time.
   If you need separate address spaces, you can swap the
   contents of qh->qhmem.
*/
typedef struct qhmemT qhmemT;

/* Update qhmem in mem_r.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 ====================*/

/*----------------------------------

  qh_memalloc_(qh, insize, freelistp, object, type)
    returns object of size bytes
        assumes size<=qh->qhmem.LASTsize and void **freelistp is a temp
*/

#if defined qh_NOmem
#define qh_memalloc_(qh, insize, freelistp, object, type) {\
  (void)freelistp; /* Avoid warnings */ \
  object= (type*)qh_memalloc(qh, insize); }
#elif defined qh_TRACEshort
#define qh_memalloc_(qh, insize, freelistp, object, type) {\
  (void)freelistp; /* Avoid warnings */ \
  object= (type*)qh_memalloc(qh, insize); }
#else /* !qh_NOmem */

#define qh_memalloc_(qh, insize, freelistp, object, type) {\
  freelistp= qh->qhmem.freelists + qh->qhmem.indextable[insize];\
  if ((object= (type*)*freelistp)) {\
    qh->qhmem.totshort += qh->qhmem.sizetable[qh->qhmem.indextable[insize]]; \
    qh->qhmem.totfree -= qh->qhmem.sizetable[qh->qhmem.indextable[insize]]; \
    qh->qhmem.cntquick++;  \
    *freelistp= *((void **)*freelistp);\
  }else object= (type*)qh_memalloc(qh, insize);}
#endif

/*----------------------------------

  qh_memfree_(qh, object, insize, freelistp)
    free up an object

  notes:
    object may be NULL
    assumes size<=qh->qhmem.LASTsize and void **freelistp is a temp
*/
#if defined qh_NOmem
#define qh_memfree_(qh, object, insize, freelistp) {\
  (void)freelistp; /* Avoid warnings */ \
  qh_memfree(qh, object, insize); }
#elif defined qh_TRACEshort
#define qh_memfree_(qh, object, insize, freelistp) {\
  (void)freelistp; /* Avoid warnings */ \
  qh_memfree(qh, object, insize); }
#else /* !qh_NOmem */

#define qh_memfree_(qh, object, insize, freelistp) {\
  if (object) { \
    qh->qhmem.freeshort++;\
    freelistp= qh->qhmem.freelists + qh->qhmem.indextable[insize];\
    qh->qhmem.totshort -= qh->qhmem.sizetable[qh->qhmem.indextable[insize]]; \
    qh->qhmem.totfree += qh->qhmem.sizetable[qh->qhmem.indextable[insize]]; \
    *((void **)object)= *freelistp;\
    *freelistp= object;}}
#endif

/*=============== prototypes in alphabetical order ============*/

#ifdef __cplusplus
extern "C" {
#endif

void *qh_memalloc(qhT *qh, int insize);
void qh_memcheck(qhT *qh);
void qh_memfree(qhT *qh, void *object, int insize);
void qh_memfreeshort(qhT *qh, int *curlong, int *totlong);
void qh_meminit(qhT *qh, FILE *ferr);
void qh_meminitbuffers(qhT *qh, int tracelevel, int alignment, int numsizes,
                        int bufsize, int bufinit);
void qh_memsetup(qhT *qh);
void qh_memsize(qhT *qh, int size);
void qh_memstatistics(qhT *qh, FILE *fp);
void qh_memtotal(qhT *qh, int *totlong, int *curlong, int *totshort, int *curshort, int *maxlong, int *totbuffer);

#ifdef __cplusplus
} /* extern "C" */
#endif

#endif /* qhDEFmem */
geometry/src/qset_r.h0000644000176200001440000003614213432323614014351 0ustar  liggesusers/*
  ---------------------------------

   qset_r.h
     header file for qset_r.c that implements set

   see qh-set_r.htm and qset_r.c

   only uses mem_r.c, malloc/free

   for error handling, writes message and calls
      qh_errexit(qhT *qh, qhmem_ERRqhull, NULL, NULL);

   set operations satisfy the following properties:
    - sets have a max size, the actual size (if different) is stored at the end
    - every set is NULL terminated
    - sets may be sorted or unsorted, the caller must distinguish this

   Copyright (c) 1993-2015 The Geometry Center.
   $Id: //main/2015/qhull/src/libqhull_r/qset_r.h#4 $$Change: 2079 $
   $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
*/

#ifndef qhDEFset
#define qhDEFset 1

#include 

/*================= -structures- ===============*/

#ifndef DEFsetT
#define DEFsetT 1
typedef struct setT setT;   /* a set is a sorted or unsorted array of pointers */
#endif

#ifndef DEFqhT
#define DEFqhT 1
typedef struct qhT qhT;          /* defined in libqhull_r.h */
#endif

/* [jan'15] Decided not to use countT.  Most sets are small.  The code uses signed tests */

/*------------------------------------------

setT
  a set or list of pointers with maximum size and actual size.

variations:
  unsorted, unique   -- a list of unique pointers with NULL terminator
                           user guarantees uniqueness
  sorted             -- a sorted list of unique pointers with NULL terminator
                           qset_r.c guarantees uniqueness
  unsorted           -- a list of pointers terminated with NULL
  indexed            -- an array of pointers with NULL elements

structure for set of n elements:

        --------------
        |  maxsize
        --------------
        |  e[0] - a pointer, may be NULL for indexed sets
        --------------
        |  e[1]

        --------------
        |  ...
        --------------
        |  e[n-1]
        --------------
        |  e[n] = NULL
        --------------
        |  ...
        --------------
        |  e[maxsize] - n+1 or NULL (determines actual size of set)
        --------------

*/

/*-- setelemT -- internal type to allow both pointers and indices
*/
typedef union setelemT setelemT;
union setelemT {
  void    *p;
  int   i;         /* integer used for e[maxSize] */
};

struct setT {
  int maxsize;          /* maximum number of elements (except NULL) */
  setelemT e[1];        /* array of pointers, tail is NULL */
                        /* last slot (unless NULL) is actual size+1
                           e[maxsize]==NULL or e[e[maxsize]-1]==NULL */
                        /* this may generate a warning since e[] contains
                           maxsize elements */
};

/*=========== -constants- =========================*/

/*-------------------------------------

  SETelemsize
    size of a set element in bytes
*/
#define SETelemsize ((int)sizeof(setelemT))


/*=========== -macros- =========================*/

/*-------------------------------------

   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++);)

/*------------------------------------------

   FOREACHsetelement_i_(qh, type, set, variable)
     define indexed FOREACH iterator

   declare:
     type *variable, variable_n, variable_i;

   each iteration:
     variable is set element, may be NULL
     variable_i is index, variable_n is qh_setsize()

   to repeat an element:
     variable_i--; variable_n-- repeats for deleted element

   at exit:
     variable==NULL and variable_i==variable_n

   example:
     #define FOREACHfacet_i_( qh, facets ) FOREACHsetelement_i_( qh, facetT, facets, facet )

   WARNING:
     nested loops can't use the same variable (define another FOREACH)

     needs braces if nested inside another FOREACH
     this includes intervening blocks, e.g. FOREACH...{ if () FOREACH...} )
*/
#define FOREACHsetelement_i_(qh, type, set, variable) \
        if (((variable= NULL), set)) for (\
          variable##_i= 0, variable= (type *)((set)->e[0].p), \
                   variable##_n= qh_setsize(qh, set);\
          variable##_i < variable##_n;\
          variable= (type *)((set)->e[++variable##_i].p) )

/*----------------------------------------

   FOREACHsetelementreverse_(qh, type, set, variable)-
     define FOREACH iterator in reverse order

   declare:
     assumes *variable and **variablep are declared
     also declare 'int variabletemp'

   each iteration:
     variable is set element

   to repeat an element:
     variabletemp++; / *repeat* /

   at exit:
     variable is NULL

   example:
     #define FOREACHvertexreverse_( vertices ) FOREACHsetelementreverse_( vertexT, vertices, vertex )

   notes:
     use FOREACHsetelementreverse12_() to reverse first two elements
     WARNING: needs braces if nested inside another FOREACH
*/
#define FOREACHsetelementreverse_(qh, type, set, variable) \
        if (((variable= NULL), set)) for (\
           variable##temp= qh_setsize(qh, set)-1, variable= qh_setlast(qh, set);\
           variable; variable= \
           ((--variable##temp >= 0) ? SETelemt_(set, variable##temp, type) : NULL))

/*-------------------------------------

   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++))

/*-------------------------------------

   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)

/*-------------------------------------

   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)

/*-------------------------------------------

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

/*-----------------------------------------

   SETref_( elem )
     l.h.s. for modifying the current element in a FOREACH iteration

   example:
     SETref_(ridge)= anotherridge;
*/
#define SETref_(elem) (elem##p[-1])

/*-----------------------------------------

   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)

/*-----------------------------------------

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

/*-----------------------------------------

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

/*-----------------------------------------

   SETfirst_(set)
     return first element of set

*/
#define SETfirst_(set)             ((set)->e[0].p)

/*-----------------------------------------

   SETfirstt_(set, type)
     return first element of set as a type

*/
#define SETfirstt_(set, type)      ((type*)((set)->e[0].p))

/*-----------------------------------------

   SETsecond_(set)
     return second element of set

*/
#define SETsecond_(set)            ((set)->e[1].p)

/*-----------------------------------------

   SETsecondt_(set, type)
     return second element of set as a type
*/
#define SETsecondt_(set, type)     ((type*)((set)->e[1].p))

/*-----------------------------------------

   SETaddr_(set, type)
       return address of set's elements
*/
#define SETaddr_(set,type)         ((type **)(&((set)->e[0].p)))

/*-----------------------------------------

   SETreturnsize_(set, size)
     return size of a set

   notes:
      set must be defined
      use qh_setsize(qhT *qh, set) unless speed is critical
*/
#define SETreturnsize_(set, size) (((size)= ((set)->e[(set)->maxsize].i))?(--(size)):((size)= (set)->maxsize))

/*-----------------------------------------

   SETempty_(set)
     return true(1) if set is empty

   notes:
      set may be NULL
*/
#define SETempty_(set)            (!set || (SETfirst_(set) ? 0 : 1))

/*---------------------------------

  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]))

/*-----------------------------------------

   SETtruncate_(set, size)
     truncate set to size

   see:
     qh_settruncate()

*/
#define SETtruncate_(set, size) {set->e[set->maxsize].i= size+1; /* maybe overwritten */ \
      set->e[size].p= NULL;}

/*======= prototypes in alphabetical order ============*/

#ifdef __cplusplus
extern "C" {
#endif

void  qh_setaddsorted(qhT *qh, setT **setp, void *elem);
void  qh_setaddnth(qhT *qh, setT **setp, int nth, void *newelem);
void  qh_setappend(qhT *qh, setT **setp, void *elem);
void  qh_setappend_set(qhT *qh, setT **setp, setT *setA);
void  qh_setappend2ndlast(qhT *qh, setT **setp, void *elem);
void  qh_setcheck(qhT *qh, setT *set, const char *tname, unsigned id);
void  qh_setcompact(qhT *qh, setT *set);
setT *qh_setcopy(qhT *qh, setT *set, int extra);
void *qh_setdel(setT *set, void *elem);
void *qh_setdellast(setT *set);
void *qh_setdelnth(qhT *qh, setT *set, int nth);
void *qh_setdelnthsorted(qhT *qh, setT *set, int nth);
void *qh_setdelsorted(setT *set, void *newelem);
setT *qh_setduplicate(qhT *qh, setT *set, int elemsize);
void **qh_setendpointer(setT *set);
int   qh_setequal(setT *setA, setT *setB);
int   qh_setequal_except(setT *setA, void *skipelemA, setT *setB, void *skipelemB);
int   qh_setequal_skip(setT *setA, int skipA, setT *setB, int skipB);
void  qh_setfree(qhT *qh, setT **set);
void  qh_setfree2(qhT *qh, setT **setp, int elemsize);
void  qh_setfreelong(qhT *qh, setT **set);
int   qh_setin(setT *set, void *setelem);
int qh_setindex(setT *set, void *setelem);
void  qh_setlarger(qhT *qh, setT **setp);
void *qh_setlast(setT *set);
setT *qh_setnew(qhT *qh, int size);
setT *qh_setnew_delnthsorted(qhT *qh, setT *set, int size, int nth, int prepend);
void  qh_setprint(qhT *qh, FILE *fp, const char* string, setT *set);
void  qh_setreplace(qhT *qh, setT *set, void *oldelem, void *newelem);
int qh_setsize(qhT *qh, setT *set);
setT *qh_settemp(qhT *qh, int setsize);
void  qh_settempfree(qhT *qh, setT **set);
void  qh_settempfree_all(qhT *qh);
setT *qh_settemppop(qhT *qh);
void  qh_settemppush(qhT *qh, setT *set);
void  qh_settruncate(qhT *qh, setT *set, int size);
int   qh_setunique(qhT *qh, setT **set, void *elem);
void  qh_setzero(qhT *qh, setT *set, int idx, int size);

#ifdef __cplusplus
} /* extern "C" */
#endif

#endif /* qhDEFset */
geometry/vignettes/0000755000176200001440000000000014367304611014122 5ustar  liggesusersgeometry/vignettes/qhull/0000755000176200001440000000000014367311205015244 5ustar  liggesusersgeometry/vignettes/qhull/COPYING.txt0000644000176200001440000000314313431000557017112 0ustar  liggesusers                    Qhull, Copyright (c) 1993-2018
                    
                            C.B. Barber
                           Arlington, MA 
                          
                               and

       The National Science and Technology Research Center for
        Computation and Visualization of Geometric Structures
                        (The Geometry Center)
                       University of Minnesota

                       email: qhull@qhull.org

This software includes Qhull from C.B. Barber and The Geometry Center.  
Qhull is copyrighted as noted above.  Qhull is free software and may 
be obtained via http from www.qhull.org.  It may be freely copied, modified, 
and redistributed under the following conditions:

1. All copyright notices must remain intact in all files.

2. A copy of this text file must be distributed along with any copies 
   of Qhull that you redistribute; this includes copies that you have 
   modified, or copies of programs or other software products that 
   include Qhull.

3. If you modify Qhull, you must include a notice giving the
   name of the person performing the modification, the date of
   modification, and the reason for such modification.

4. When distributing modified versions of Qhull, or other software 
   products that include Qhull, you must provide notice that the original 
   source code may be obtained as noted above.

5. There is no warranty or other guarantee of fitness for Qhull, it is 
   provided solely "as is".  Bug reports or fixes may be sent to 
   qhull_bug@qhull.org; the authors may or may not act on them as 
   they desire.
geometry/vignettes/qhull/html/0000755000176200001440000000000013431000557016204 5ustar  liggesusersgeometry/vignettes/qhull/html/qdelaun.html0000644000176200001440000006546513431000557020543 0ustar  liggesusers



qdelaunay -- Delaunay triangulation




Up:
Home page for Qhull
Up: Qhull manual: Table of Contents
To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
To: synopsis • input • outputs • controls • graphics • notes • conventions • options

[delaunay]qdelaunay -- Delaunay triangulation

The Delaunay triangulation is the triangulation with empty circumspheres. It has many useful properties and applications. See the survey article by Aurenhammer ['91] and the detailed introduction by O'Rourke ['94].

Example: rbox r y c G0.1 D2 | qdelaunay s Fv TO result
Compute the 2-d Delaunay triangulation of a triangle and a small square. Write a summary to the console and unoriented regions to 'result'. Merge regions for cocircular input sites (i.e., the square).
 
Example: rbox r y c G0.1 D2 | qdelaunay s Fv Qt
Compute the 2-d Delaunay triangulation of a triangle and a small square. Write a summary and unoriented regions to the console. Produce triangulated output.
 
Example: rbox 10 D2 | qdelaunay QJ s i TO result
Compute the 2-d Delaunay triangulation of 10 random points. Joggle the input to guarantee triangular output. Write a summary to the console and the regions to 'result'.

Qhull computes the Delaunay triangulation by computing a convex hull. It lifts the input sites to a paraboloid by adding the sum of the squares of the coordinates. It scales the height of the paraboloid to improve numeric precision ('Qbb'). It computes the convex hull of the lifted sites, and projects the lower convex hull to the input.

Each region of the Delaunay triangulation corresponds to a facet of the lower half of the convex hull. Facets of the upper half of the convex hull correspond to the furthest-site Delaunay triangulation. See the examples, Delaunay and Voronoi diagrams.

See Qhull FAQ - Delaunay and Voronoi diagram questions.

By default, qdelaunay merges cocircular and cospherical regions. For example, the Delaunay triangulation of a square inside a diamond ('rbox D2 c d G4 | qdelaunay') contains one region for the square.

Use option 'Qz' if the input is circular, cospherical, or nearly so. It improves precision by adding a point "at infinity," above the corresponding paraboloid.

If you use 'Qt' (triangulated output), all Delaunay regions will be simplicial (e.g., triangles in 2-d). Some regions may be degenerate and have zero area. Triangulated output identifies coincident points.

If you use 'QJ' (joggled input), all Delaunay regions will be simplicial (e.g., triangles in 2-d). Coincident points will create small regions since the points are joggled apart. Joggled input is less accurate than triangulated output ('Qt'). See Merged facets or joggled input.

The output for 3-d Delaunay triangulations may be confusing if the input contains cospherical data. See the FAQ item Why are there extra points in a 4-d or higher convex hull? Avoid these problems with triangulated output ('Qt') or joggled input ('QJ').

The 'qdelaunay' program is equivalent to 'qhull d Qbb' in 2-d to 3-d, and 'qhull d Qbb Qx' in 4-d and higher. It disables the following Qhull options: d n v H U Qb QB Qc Qf Qg Qi Qm Qr QR Qv Qx TR E V FC Fi Fo Fp Ft FV Q0,etc.

Copyright © 1995-2018 C.B. Barber


»qdelaunay synopsis

qdelaunay- compute the Delaunay triangulation.
    input (stdin): dimension, number of points, point coordinates
    comments start with a non-numeric character

options (qdelaun.html):
    Qt   - triangulated output
    QJ   - joggle input instead of merging facets
    Qu   - furthest-site Delaunay triangulation
    Tv   - verify result: structure, convexity, and in-circle test
    .    - concise list of all options
    -    - one-line description of all options

output options (subset):
    s    - summary of results (default)
    i    - vertices incident to each Delaunay region
    Fx   - extreme points (vertices of the convex hull)
    o    - OFF format (shows the points lifted to a paraboloid)
    G    - Geomview output (2-d and 3-d points lifted to a paraboloid)
    m    - Mathematica output (2-d inputs lifted to a paraboloid)
    QVn  - print Delaunay regions that include point n, -n if not
    TO file- output results to file, may be enclosed in single quotes

examples:
    rbox c P0 D2 | qdelaunay s o          rbox c P0 D2 | qdelaunay i
    rbox c P0 D3 | qdelaunay Fv Qt        rbox c P0 D2 | qdelaunay s Qu Fv
    rbox c G1 d D2 | qdelaunay s i        rbox c G1 d D2 | qdelaunay s i Qt
    rbox M3,4 z 100 D2 | qdelaunay s      rbox M3,4 z 100 D2 | qdelaunay s Qt

»qdelaunay input

The input data on stdin consists of:

  • dimension
  • number of points
  • point coordinates

Use I/O redirection (e.g., qdelaunay < data.txt), a pipe (e.g., rbox 10 | qdelaunay), or the 'TI' option (e.g., qdelaunay TI data.txt).

For example, this is four cocircular points inside a square. Its Delaunay triangulation contains 8 triangles and one four-sided figure.

rbox s 4 W0 c G1 D2 > data
2 RBOX s 4 W0 c D2
8
-0.4941988586954018 -0.07594397977563715
-0.06448037284989526 0.4958248496365813
0.4911154367094632 0.09383830681375946
-0.348353580869097 -0.3586778257652367
    -1     -1
    -1      1
     1     -1
     1      1

qdelaunay s i < data


Delaunay triangulation by the convex hull of 8 points in 3-d

  Number of input sites: 8
  Number of Delaunay regions: 9
  Number of non-simplicial Delaunay regions: 1

Statistics for: RBOX s 4 W0 c D2 | QDELAUNAY s i

  Number of points processed: 8
  Number of hyperplanes created: 18
  Number of facets in hull: 10
  Number of distance tests for qhull: 33
  Number of merged facets: 2
  Number of distance tests for merging: 102
  CPU seconds to compute hull (after input): 0.028

9
1 7 5
6 3 4
2 3 6
7 2 6
2 7 1
0 5 4
3 0 4
0 1 5
1 0 3 2

»qdelaunay outputs

These options control the output of Delaunay triangulations:

Delaunay regions
i
list input sites for each Delaunay region. The first line is the number of regions. The remaining lines list the input sites for each region. The regions are oriented. In 3-d and higher, report cospherical sites by adding extra points. Use triangulated output ('Qt') to avoid non-simpicial regions. For the circle-in-square example, eight Delaunay regions are triangular and the ninth has four input sites.
Fv
list input sites for each Delaunay region. The first line is the number of regions. Each remaining line starts with the number of input sites. The regions are unoriented. For the circle-in-square example, eight Delaunay regions are triangular and the ninth has four input sites.
Fn
list neighboring regions for each Delaunay region. The first line is the number of regions. Each remaining line starts with the number of neighboring regions. Negative indices (e.g., -1) indicate regions outside of the Delaunay triangulation. For the circle-in-square example, the four regions on the square are neighbors to the region-at-infinity.
FN
list the Delaunay regions for each input site. The first line is the total number of input sites. Each remaining line starts with the number of Delaunay regions. Negative indices (e.g., -1) indicate regions outside of the Delaunay triangulation. For the circle-in-square example, each point on the circle belongs to four Delaunay regions. Use 'Qc FN' to include coincident input sites and deleted vertices.
Fa
print area for each Delaunay region. The first line is the number of regions. The areas follow, one line per region. For the circle-in-square example, the cocircular region has area 0.4.
 
 
Input sites
Fc
list coincident input sites for each Delaunay region. The first line is the number of regions. The remaining lines start with the number of coincident sites and deleted vertices. Deleted vertices indicate highly degenerate input (see'Fs'). A coincident site is assigned to one Delaunay region. Do not use 'QJ' with 'Fc'; the joggle will separate coincident sites.
FP
print coincident input sites with distance to nearest site (i.e., vertex). The first line is the number of coincident sites. Each remaining line starts with the point ID of an input site, followed by the point ID of a coincident point, its region, and distance. Includes deleted vertices which indicate highly degenerate input (see'Fs'). Do not use 'QJ' with 'FP'; the joggle will separate coincident sites.
Fx
list extreme points of the input sites. These points are on the boundary of the convex hull. The first line is the number of extreme points. Each point is listed, one per line. The circle-in-square example has four extreme points.
 
 
General
FA
compute total area for 's' and 'FS'
o
print lower facets of the corresponding convex hull (a paraboloid)
m
Mathematica output for the lower facets of the paraboloid (2-d triangulations).
FM
Maple output for the lower facets of the paraboloid (2-d triangulations).
G
Geomview output for the paraboloid (2-d or 3-d triangulations).
s
print summary for the Delaunay triangulation. Use 'Fs' and 'FS' for numeric data.

»qdelaunay controls

These options provide additional control:

Qt
triangulated output. Qhull triangulates non-simplicial facets. It may produce degenerate facets of zero area.
QJ
joggle the input to avoid cospherical and coincident sites. It is less accurate than triangulated output ('Qt').
Qu
compute the furthest-site Delaunay triangulation.
Qz
add a point above the paraboloid to reduce precision errors. Use it for nearly cocircular/cospherical input (e.g., 'rbox c | qdelaunay Qz'). The point is printed for options 'Ft' and 'o'.
QVn
select facets adjacent to input site n (marked 'good').
Tv
verify result.
TI file
input data from file. The filename may not use spaces or quotes.
TO file
output results to file. Use single quotes if the filename contains spaces (e.g., TO 'file with spaces.txt'
TFn
report progress after constructing n facets
PDk:1
include upper and lower facets in the output. Set k to the last dimension (e.g., 'PD2:1' for 2-d inputs).
f
facet dump. Print the data structure for each facet (i.e., Delaunay region).

»qdelaunay graphics

For 2-d and 3-d Delaunay triangulations, Geomview ('qdelaunay G') displays the corresponding convex hull (a paraboloid).

To view a 2-d Delaunay triangulation, use 'qdelaunay GrD2' to drop the last dimension. This is the same as viewing the hull without perspective (see Geomview's 'cameras' menu).

To view a 3-d Delaunay triangulation, use 'qdelaunay GrD3' to drop the last dimension. You may see extra edges. These are interior edges that Geomview moves towards the viewer (see 'lines closer' in Geomview's camera options). Use option 'Gt' to make the outer ridges transparent in 3-d. See Delaunay and Voronoi examples.

For 2-d Delaunay triangulations, Mathematica ('m') and Maple ('FM') output displays the lower facets of the corresponding convex hull (a paraboloid).

For 2-d, furthest-site Delaunay triangulations, Maple and Mathematica output ('Qu m') displays the upper facets of the corresponding convex hull (a paraboloid).

»qdelaunay notes

You can simplify the Delaunay triangulation by enclosing the input sites in a large square or cube. This is particularly recommended for cocircular or cospherical input data.

A non-simplicial Delaunay region indicates nearly cocircular or cospherical input sites. To avoid non-simplicial regions either triangulate the output ('Qt') or joggle the input ('QJ'). Triangulated output is more accurate than joggled input. Alternatively, use an exact arithmetic code.

Delaunay triangulations do not include facets that are coplanar with the convex hull of the input sites. A facet is coplanar if the last coefficient of its normal is nearly zero (see qh_ZEROdelaunay).

See Imprecision issues :: Delaunay triangulations for a discussion of precision issues. Deleted vertices indicate highly degenerate input. They are listed in the summary output and option 'Fs'.

To compute the Delaunay triangulation of points on a sphere, compute their convex hull. If the sphere is the unit sphere at the origin, the facet normals are the Voronoi vertices of the input. The points may be restricted to a hemisphere. [S. Fortune]

The 3-d Delaunay triangulation of regular points on a half spiral (e.g., 'rbox 100 l | qdelaunay') has quadratic size, while the Delaunay triangulation of random 3-d points is approximately linear for reasonably sized point sets.

With the Qhull library, you can use qh_findbestfacet in poly2.c to locate the facet that contains a point. You should first lift the point to the paraboloid (i.e., the last coordinate is the sum of the squares of the point's coordinates -- qh_setdelaunay). Do not use options 'Qbb', 'QbB', 'Qbk:n', or 'QBk:n' since these scale the last coordinate.

If a point is interior to the convex hull of the input set, it is interior to the adjacent vertices of the Delaunay triangulation. This is demonstrated by the following pipe for point 0:

    qdelaunay <data s FQ QV0 p | qconvex s Qb3:0B3:0 p

The first call to qdelaunay returns the neighboring points of point 0 in the Delaunay triangulation. The second call to qconvex returns the vertices of the convex hull of these points (after dropping the lifted coordinate). If point 0 is interior to the original point set, it is interior to the reduced point set.

»qdelaunay conventions

The following terminology is used for Delaunay triangulations in Qhull for dimension d. The underlying structure is the lower facets of a convex hull in dimension d+1. For further information, see data structures and convex hull conventions.

  • input site - a point in the input (one dimension lower than a point on the convex hull)
  • point - a point has d+1 coordinates. The last coordinate is the sum of the squares of the input site's coordinates
  • coplanar point - a coincident input site or a deleted vertex. Deleted vertices indicate highly degenerate input.
  • vertex - a point on the paraboloid. It corresponds to a unique input site.
  • point-at-infinity - a point added above the paraboloid by option 'Qz'
  • lower facet - a facet corresponding to a Delaunay region. The last coefficient of its normal is clearly negative.
  • upper facet - a facet corresponding to a furthest-site Delaunay region. The last coefficient of its normal is clearly positive.
  • Delaunay region - a lower facet projected to the input sites
  • upper Delaunay region - an upper facet projected to the input sites
  • non-simplicial facet - more than d input sites are cocircular or cospherical
  • good facet - a Delaunay region with optional restrictions by 'QVn', etc.

»qdelaunay options

qdelaunay- compute the Delaunay triangulation
    http://www.qhull.org

input (stdin):
    first lines: dimension and number of points (or vice-versa).
    other lines: point coordinates, best if one point per line
    comments:    start with a non-numeric character

options:
    Qt   - triangulated output
    QJ   - joggle input instead of merging facets
    Qu   - compute furthest-site Delaunay triangulation

Qhull control options:
    QJn  - randomly joggle input in range [-n,n]
    Qs   - search all points for the initial simplex
    Qz   - add point-at-infinity to Delaunay triangulation
    QGn  - print Delaunay region if visible from point n, -n if not
    QVn  - print Delaunay regions that include point n, -n if not

Trace options:
    T4   - trace at level n, 4=all, 5=mem/gauss, -1= events
    Tc   - check frequently during execution
    Ts   - print statistics
    Tv   - verify result: structure, convexity, and in-circle test
    Tz   - send all output to stdout
    TFn  - report summary when n or more facets created
    TI file - input data from file, no spaces or single quotes
    TO file - output results to file, may be enclosed in single quotes
    TPn  - turn on tracing when point n added to hull
     TMn - turn on tracing at merge n
     TWn - trace merge facets when width > n
    TVn  - stop qhull after adding point n, -n for before (see TCn)
     TCn - stop qhull after building cone for point n (see TVn)

Precision options:
    Cn   - radius of centrum (roundoff added).  Merge facets if non-convex
     An  - cosine of maximum angle.  Merge facets if cosine > n or non-convex
           C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge
    Rn   - randomly perturb computations by a factor of [1-n,1+n]
    Wn   - min facet width for outside point (before roundoff)

Output formats (may be combined; if none, produces a summary to stdout):
    f    - facet dump
    G    - Geomview output (see below)
    i    - vertices incident to each Delaunay region
    m    - Mathematica output (2-d only, lifted to a paraboloid)
    o    - OFF format (dim, points, and facets as a paraboloid)
    p    - point coordinates (lifted to a paraboloid)
    s    - summary (stderr)

More formats:
    Fa   - area for each Delaunay region
    FA   - compute total area for option 's'
    Fc   - count plus coincident points for each Delaunay region
    Fd   - use cdd format for input (homogeneous with offset first)
    FD   - use cdd format for numeric output (offset first)
    FF   - facet dump without ridges
    FI   - ID of each Delaunay region
    Fm   - merge count for each Delaunay region (511 max)
    FM   - Maple output (2-d only, lifted to a paraboloid)
    Fn   - count plus neighboring region for each Delaunay region
    FN   - count plus neighboring region for each point
    FO   - options and precision constants
    FP   - nearest point and distance for each coincident point
    FQ   - command used for qdelaunay
    Fs   - summary: #int (8), dimension, #points, tot vertices, tot facets,
                    for output: #vertices, #Delaunay regions,
                                #coincident points, #non-simplicial regions
                    #real (2), max outer plane, min vertex
    FS   - sizes:   #int (0)
                    #real (2), tot area, 0
    Fv   - count plus vertices for each Delaunay region
    Fx   - extreme points of Delaunay triangulation (on convex hull)

Geomview options (2-d and 3-d)
    Ga   - all points as dots
     Gp  -  coplanar points and vertices as radii
     Gv  -  vertices as spheres
    Gi   - inner planes only
     Gn  -  no planes
     Go  -  outer planes only
    Gc     - centrums
    Gh   - hyperplane intersections
    Gr   - ridges
    GDn  - drop dimension n in 3-d and 4-d output
    Gt   - transparent outer ridges to view 3-d Delaunay

Print options:
    PAn  - keep n largest Delaunay regions by area
    Pdk:n - drop facet if normal[k] <= n (default 0.0)
    PDk:n - drop facet if normal[k] >= n
    Pg   - print good Delaunay regions (needs 'QGn' or 'QVn')
    PFn  - keep Delaunay regions whose area is at least n
    PG   - print neighbors of good regions (needs 'QGn' or 'QVn')
    PMn  - keep n Delaunay regions with most merges
    Po   - force output.  If error, output neighborhood of facet
    Pp   - do not report precision problems

    .    - list of all options
    -    - one line descriptions of all options

Up: Home page for Qhull
Up: Qhull manual: Table of Contents
To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
To: synopsis • input • outputs • controls • graphics • notes • conventions • options


The Geometry Center Home Page

Comments to: qhull@qhull.org
Created: Sept. 25, 1995 --- Last modified: see top

geometry/vignettes/qhull/html/qh-optc.html0000644000176200001440000002734713431000557020462 0ustar liggesusers Qhull precision options

Up: Home page for Qhull
Up: Qhull manual: Table of Contents
To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions


[delaunay] Qhull precision options

This section lists the precision options for Qhull. These options are indicated by an upper-case letter followed by a number.

Copyright © 1995-2018 C.B. Barber


» Programs OptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions

Precision options

Most users will not need to set these options. They are best used for approximating a convex hull. They may also be used for testing Qhull's handling of precision errors.

By default, Qhull uses options 'C-0' for 2-d, 3-d and 4-d, and 'Qx' for 5-d and higher. These options use facet merging to handle precision errors. You may also use joggled input 'QJ' to avoid precision problems. For more information see Imprecision in Qhull.

 
General
Cn
centrum radius for post-merging
C-n
centrum radius for pre-merging
An
cosine of maximum angle for post-merging
A-n
cosine of maximum angle for pre-merging
Qx
exact pre-merges (allows coplanar facets)
C-0
handle all precision errors
Wn
min distance above plane for outside points
 
Experimental
Un
max distance below plane for a new, coplanar point
En
max roundoff error for distance computation
Vn
min distance above plane for a visible facet
Rn
randomly perturb computations by a factor of [1-n,1+n]

»A-n - cosine of maximum angle for pre-merging.

Pre-merging occurs while Qhull constructs the hull. It is indicated by 'C-n', 'A-n', or 'Qx'.

If the angle between a pair of facet normals is greater than n, Qhull merges one of the facets into a neighbor. It selects the facet that is closest to a neighboring facet.

For example, option 'A-0.99' merges facets during the construction of the hull. If the cosine of the angle between facets is greater than 0.99, one or the other facet is merged. Qhull accounts for the maximum roundoff error.

If 'A-n' is set without 'C-n', then 'C-0' is automatically set.

In 5-d and higher, you should set 'Qx' along with 'A-n'. It skips merges of coplanar facets until after the hull is constructed and before 'An' and 'Cn' are checked.

»An - cosine of maximum angle for post-merging.

Post merging occurs after the hull is constructed. For example, option 'A0.99' merges a facet if the cosine of the angle between facets is greater than 0.99. Qhull accounts for the maximum roundoff error.

If 'An' is set without 'Cn', then 'C0' is automatically set.

»C-0 - handle all precision errors

Qhull handles precision errors by merging facets. The 'C-0' option handles all precision errors in 2-d, 3-d, and 4-d. It is set by default. It may be used in higher dimensions, but sometimes the facet width grows rapidly. It is usually better to use 'Qx' in 5-d and higher. Use 'QJ' to joggle the input instead of merging facets. Use 'Q0' to turn both options off.

Qhull optimizes 'C-0' ("_zero-centrum") by testing vertices instead of centrums for adjacent simplices. This may be slower in higher dimensions if merges decrease the number of processed points. The optimization may be turned off by setting a small value such as 'C-1e-30'. See How Qhull handles imprecision.

»C-n - centrum radius for pre-merging

Pre-merging occurs while Qhull constructs the hull. It is indicated by 'C-n', 'A-n', or 'Qx'.

The centrum of a facet is a point on the facet for testing facet convexity. It is the average of the vertices projected to the facet's hyperplane. Two adjacent facets are convex if each centrum is clearly below the other facet.

If adjacent facets are non-convex, one of the facets is merged into a neighboring facet. Qhull merges the facet that is closest to a neighboring facet.

For option 'C-n', n is the centrum radius. For example, 'C-0.001' merges facets whenever the centrum is less than 0.001 from a neighboring hyperplane. Qhull accounts for roundoff error when testing the centrum.

In 5-d and higher, you should set 'Qx' along with 'C-n'. It skips merges of coplanar facets until after the hull is constructed and before 'An' and 'Cn' are checked.

»Cn - centrum radius for post-merging

Post-merging occurs after Qhull constructs the hull. It is indicated by 'Cn' or 'An'.

For option 'Cn', n is the centrum radius. For example, 'C0.001' merges facets when the centrum is less than 0.001 from a neighboring hyperplane. Qhull accounts for roundoff error when testing the centrum.

Both pre-merging and post-merging may be defined. If only post-merging is used ('Q0' with 'Cn'), Qhull may fail to produce a hull due to precision errors during the hull's construction.

»En - max roundoff error for distance computations

This allows the user to change the maximum roundoff error computed by Qhull. The value computed by Qhull may be overly pessimistic. If 'En' is set too small, then the output may not be convex. The statistic "max. distance of a new vertex to a facet" (from option 'Ts') is a reasonable upper bound for the actual roundoff error.

»Rn - randomly perturb computations

This option perturbs every distance, hyperplane, and angle computation by up to (+/- n * max_coord). It simulates the effect of roundoff errors. Unless 'En' is explicitly set, it is adjusted for 'Rn'. The command 'qhull Rn' will generate a convex hull despite the perturbations. See the Examples section for an example.

Options 'Rn C-n' have the effect of 'W2n' and 'C-2n'. To use time as the random number seed, use option 'QR-1'.

»Un - max distance for a new, coplanar point

This allows the user to set coplanarity. When pre-merging ('C-n ', 'A-n' or 'Qx'), Qhull merges a new point into any coplanar facets. The default value for 'Un' is 'Vn'.

»Vn - min distance for a visible facet

This allows the user to set facet visibility. When adding a point to the convex hull, Qhull determines all facets that are visible from the point. A facet is visible if the distance from the point to the facet is greater than 'Vn'.

Without merging, the default value for 'Vn' is the roundoff error ('En'). With merging, the default value is the pre-merge centrum ('C-n') in 2-d or 3-d, or three times that in other dimensions. If the outside width is specified with option 'Wn ', the maximum, default value for 'Vn' is 'Wn'.

Qhull warns if 'Vn' is greater than 'Wn' and furthest outside ('Qf') is not selected; this combination usually results in flipped facets (i.e., reversed normals).

»Wn - min distance above plane for outside points

Points are added to the convex hull only if they are clearly outside of a facet. A point is outside of a facet if its distance to the facet is greater than 'Wn'. Without pre-merging, the default value for 'Wn' is 'En '. If the user specifies pre-merging and does not set 'Wn', than 'Wn' is set to the maximum of 'C-n' and maxcoord*(1 - A-n).

This option is good for approximating a convex hull.

Options 'Qc' and 'Qi' use the minimum vertex to distinguish coplanar points from interior points.


Up: Home page for Qhull
Up: Qhull manual: Table of Contents
To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions


The Geometry Center Home Page

Comments to: qhull@qhull.org
Created: Sept. 25, 1995 --- Last modified: see top

geometry/vignettes/qhull/html/rbox.man0000644000176200001440000001044513431000556017656 0ustar liggesusers.\" This is the Unix manual page for rbox, written in nroff, the standard .\" manual formatter for Unix systems. To format it, type .\" .\" nroff -man rbox.man .\" .\" This will print a formatted copy to standard output. If you want .\" to ensure that the output is plain ascii, free of any control .\" characters that nroff uses for underlining etc, pipe the output .\" through "col -b": .\" .\" nroff -man rbox.man | col -b .\" .TH rbox 1 "August 10, 1998" "Geometry Center" .SH NAME rbox \- generate point distributions for qhull .SH SYNOPSIS Command "rbox" (w/o arguments) lists the options. .SH DESCRIPTION .PP rbox generates random or regular points according to the options given, and outputs the points to stdout. The points are generated in a cube, unless 's' or 'k' option is given. The format of the output is the following: first line contains the dimension and a comment, second line contains the number of points, and the following lines contain the points, one point per line. Points are represented by their coordinate values. .SH EXAMPLES .TP rbox 10 10 random points in the unit cube centered at the origin. .TP rbox 10 s D2 10 random points on a 2\[hy]d circle. .TP rbox 100 W0 100 random points on the surface of a cube. .TP rbox 1000 s D4 1000 random points on a 4\[hy]d sphere. .TP rbox c D5 O0.5 a 5\[hy]d hypercube with one corner at the origin. .TP rbox d D10 a 10\[hy]d diamond. .TP rbox x 1000 r W0 100 random points on the surface of a fixed simplex .TP rbox y D12 a 12\[hy]d simplex. .TP rbox l 10 10 random points along a spiral .TP rbox l 10 r 10 regular points along a spiral plus two end points .TP rbox 1000 L10000 D4 s 1000 random points on the surface of a narrow lens. .TP rbox c G2 d G3 a cube with coordinates +2/\-2 and a diamond with coordinates +3/\-3. .TP rbox 64 M3,4 z a rotated, {0,1,2,3} x {0,1,2,3} x {0,1,2,3} lattice (Mesh) of integer points. 'rbox 64 M1,0' is orthogonal. .TP rbox P0 P0 P0 P0 P0 5 copies of the origin in 3\-d. Try 'rbox P0 P0 P0 P0 P0 | qhull QJ'. .TP r 100 s Z1 G0.1 two cospherical 100\-gons plus another cospherical point. .TP 100 s Z1 a cone of points. .TP 100 s Z1e\-7 a narrow cone of points with many precision errors. .SH OPTIONS .TP n number of points .TP Dn dimension n\[hy]d (default 3\[hy]d) .TP Bn bounding box coordinates (default 0.5) .TP l spiral distribution, available only in 3\[hy]d .TP Ln lens distribution of radius n. May be used with 's', 'r', 'G', and 'W'. .TP Mn,m,r lattice (Mesh) rotated by {[n,\-m,0], [m,n,0], [0,0,r], ...}. Use 'Mm,n' for a rigid rotation with r = sqrt(n^2+m^2). 'M1,0' is an orthogonal lattice. For example, '27 M1,0' is {0,1,2} x {0,1,2} x {0,1,2}. '27 M3,4 z' is a rotated integer lattice. .TP s cospherical points randomly generated in a cube and projected to the unit sphere .TP x simplicial distribution. It is fixed for option 'r'. May be used with 'W'. .TP y simplicial distribution plus a simplex. Both 'x' and 'y' generate the same points. .TP Wn restrict points to distance n of the surface of a sphere or a cube .TP c add a unit cube to the output .TP c Gm add a cube with all combinations of +m and \-m to the output .TP d add a unit diamond to the output. .TP d Gm add a diamond made of 0, +m and \-m to the output .TP Cn,r,m add n nearly coincident points within radius r of m points .TP Pn,m,r add point [n,m,r] to the output first. Pad coordinates with 0.0. .TP n Remove the command line from the first line of output. .TP On offset the data by adding n to each coordinate. .TP t use time in seconds as the random number seed (default is command line). .TP tn set the random number seed to n. .TP z generate integer coordinates. Use 'Bn' to change the range. The default is 'B1e6' for six\[hy]digit coordinates. In R^4, seven\[hy]digit coordinates will overflow hyperplane normalization. .TP Zn s restrict points to a disk about the z+ axis and the sphere (default Z1.0). Includes the opposite pole. 'Z1e\-6' generates degenerate points under single precision. .TP Zn Gm s same as Zn with an empty center (default G0.5). .TP r s D2 generate a regular polygon .TP r s Z1 G0.1 generate a regular cone .SH BUGS Some combinations of arguments generate odd results. Report bugs to qhull_bug@qhull.org, other correspondence to qhull@qhull.org .SH SEE ALSO qhull(1) .SH AUTHOR .nf C. Bradford Barber bradb@shore.net .fi geometry/vignettes/qhull/html/qh-code.html0000644000176200001440000014032313431000557020415 0ustar liggesusers Qhull code

Up: Home page for Qhull
Up: Qhull manual: Table of Contents
To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
To: Qhull code: Table of Contents (please wait while loading)
Dn: Qhull functions, macros, and data structures


[4-d cube] Qhull code

This section discusses the code for Qhull.

Copyright © 1995-2018 C.B. Barber


»Qhull code: Table of Contents


»Reentrant Qhull

Qhull-2015 introduces reentrant Qhull (libqhull_r). Reentrant Qhull uses a qhT* argument instead of global data structures. The qhT* pointer is the first argument to most Qhull routines. It allows multiple instances of Qhull to run at the same time. It simplifies the C++ interface to Qhull.

New code should be written with libqhull_r. Existing users of libqhull should consider converting to libqhull_r. Although libqhull will be supported indefinitely, improvements may not be implemented. Reentrant qhull is 1-2% slower than non-reentrant qhull.

Note: Reentrant Qhull is not thread safe. Do not invoke Qhull routines with the same qhT* pointer from multiple threads.

»How to convert code to reentrant Qhull

C++ users need to convert to libqhull_r. The new C++ interface does a better, but not perfect, job of hiding Qhull's C data structures. The previous C++ interface was unusual due to Qhull's global data structures.

All other users should consider converting to libqhull_r. The conversion is straight forward. The original conversion of libqhull to libqhull_r required thousands of changes, mostly global search and replace. The first run of Qhull (unix_r.c) produced the same output, and nearly the same log files, as the original (unix.c).

Suggestions to help with conversion.

  • Compare qconvex_r.c with qconvex.c. Define a qhT object and a pointer it. The qhT* pointer is the first argument to most Qhull functions. Clear qh_qh-<NOerrext before calling qh_initflags(). Invoke QHULL_LIB_CHECK to check for a compatible Qhull library.
  • Compare user_eg2_r.c with user_eg2.c
  • Compare user_eg_r.c with user_eg.c. If you use qhT before invoking qh_init_A, call qh_zero() to clear the qhT object. user_eg_r.c includes multiple Qhull runs.
  • Review user_eg3_r.cpp. As with the other programs, invoke QHULL_LIB_CHECK. Simple C++ programs should compile as is.
  • Compare QhullFacet.cpp with the same file in Qhull-2012.1. UsingLibQhull was replaced with the macro QH_TRY_() and 'qh_qh-<NOerrext= true'.
  • For detailed notes on libqhull_r, see "libqhull_r (reentrant Qhull)" and "Source code changes for libqhull_r" in Changes.txt.
  • For detailed notes on libqhullcpp, see "C++ interface" and following sections in Changes.txt.
  • For regexps and conversion notes, see README_r.txt (unedited).

»Qhull on 64-bit computers

Qhull compiles for 64-bit hosts. Since the size of a pointer on a 64-bit host is double the size on a 32-bit host, memory consumption increases about 50% for simplicial facets and up-to 100% for non-simplicial facets.

You can check memory consumption with option Ts. It includes the size of each data structure:

  • 32-bit -- merge 24 ridge 20 vertex 28 facet 88 normal 24 ridge vertices 16 facet vertices or neighbors 20
  • 64-bit -- merge 32 ridge 32 vertex 48 facet 120 normal 32 ridge vertices 40 facet vertices or neighbors 48

For Qhull 2015, the maximum identifier for ridges, vertices, and facets was increased from 24-bits to 32-bits. This allows for larger convex hulls, but may increase the size of the corresponding data structures. The sizes for Qhull 2012.1 were

  • 32-bit -- merge 24 ridge 16 vertex 24 facet 88
  • 64-bit -- merge 32 ridge 32 vertex 40 facet 120

»Calling Qhull from C++ programs

Qhull 2015 uses reentrant Qhull for its C++ interface. If you used the C++ interface from qhull 2012.1, you may need to adjust how you initialize and use the Qhull classes. See How to convert code to reentrant Qhull.

Qhull's C++ interface allows you to explore the results of running Qhull. It provides access to Qhull's data structures. Most of the classes derive from the corresponding qhull data structure. For example, QhullFacet is an instance of Qhull's facetT.

You can retain most of the data in Qhull and use the C++ interface to explore its results. Each object contains a reference the Qhull's data structure (via QhullQh), making the C++ representation less memory efficient.

Besides using the C++ interface, you can also use libqhull_r directly. For example, the FOREACHfacet_(...) macro will visit each facet in turn.

The C++ interface to Qhull is incomplete. You may need to extend the interface. If so, you will need to understand Qhull's data structures and read the code. Example (c.f., user_eg3 eg-100). It prints the facets generated by Qhull.

    RboxPoints rbox;
    rbox.appendRandomPoints("100");
    Qhull qhull;
    qhull.runQhull("", rbox);
    QhullFacetList facets(qhull);
    cout<< facets;

The C++ iterface for RboxPoints redefines the fprintf() calls in rboxlib.c. Instead of writing its output to stdout, RboxPoints appends the output to a std::vector. The same technique may be used for calling Qhull from C++.

  • Run Qhull with option 'Ta' to annotate the output with qh_fprintf() identifiers.
  • Redefine qh_fprintf() for these identifiers.
  • See RboxPoints.cpp for an example.

Since the C++ interface uses reentrant Qhull, multiple threads may run Qhull at the same time. Each thread is one run of Qhull.

Do not have two threads accessing the same Qhull instance. Qhull is not thread-safe.

»CoordinateIterator

A CoordinateIterator or ConstCoordinateIterator [RboxPoints.cpp] is a std::vector<realT>::iterator for Rbox and Qhull coordinates. It is the result type of RboxPoints.coordinates().

Qhull does not use CoordinateIterator for its data structures. A point in Qhull is an array of reals instead of a std::vector. See QhullPoint.

»Qhull

Qhull is the top-level class for running Qhull. It initializes Qhull, runs the computation, and records errors. It provides access to the global data structure QhullQh, Qhull's facets, and vertices.

»QhullError

QhullError is derived from std::exception. It reports errors from Qhull and captures the output to stderr.

If error handling is not set up, Qhull exits with a code from 1 to 5. The codes are defined by qh_ERR* in libqhull_r.h. The exit is via qh_exit() in usermem_r.c. The C++ interface does not report the captured output in QhullError. Call Qhull::setErrorStream to send output to cerr instead.

»QhullFacet

A QhullFacet is a facet of the convex hull, a region of the Delaunay triangulation, a vertex of a Voronoi diagram, or an intersection of the halfspace intersection about a point. A QhullFacet has a set of QhullVertex, a set of QhullRidge, and a set of neighboring QhullFacets.

»QhullFacetList

A QhullFacetList is a linked list of QhullFacet. The result of Qhull.runQhull is a QhullFacetList stored in QhullQh.

»QhullFacetSet

A QhullFacetSet is a QhullSet of QhullFacet. QhullFacetSet may be ordered or unordered. The neighboring facets of a QhullFacet is a QhullFacetSet. The neighbors of a QhullFacet is a QhullFacetSet. The neighbors are ordered for simplicial facets, matching the opposite vertex of the facet.

»QhullIterator

QhullIterator contains macros for defining Java-style iterator templates from a STL-style iterator template.

»QhullLinkedList

A QhullLinkedLIst is a template for linked lists with next and previous pointers. QhullFacetList and QhullVertexList are QhullLinkedLists.

»QhullPoint

A QhullPoint is an array of point coordinates, typically doubles. The length of the array is QhullQh.hull_dim. The identifier of a QhullPoint is its 0-based index from QhullQh.first_point followed by QhullQh.other_points.

»QhullPointSet

A QhullPointSet is a QhullSet of QhullPoint. The QhullPointSet of a QhullFacet is its coplanar points.

»QhullQh

QhullQh is the root of Qhull's data structure. It contains initialized constants, sets, buffers, and variables. It contains an array and a set of QhullPoint, a list of QhullFacet, and a list of QhullVertex. The points are the input to Qhull. The facets and vertices are the result of running Qhull.

Qhull's functions access QhullQh through the global variable, qh_qh. The global data structures, qh_stat and qh_mem, record statistics and manage memory respectively.

»QhullRidge

A QhullRidge represents the edge between two QhullFacet's. It is always simplicial with qh.hull_dim-1 QhullVertex)'s.

»QhullRidgeSet

A QhullRidgeSet is a QhullSet of QhullRidge. Each QhullFacet contains a QhullRidgeSet.

»QhullSet

A QhullSet is a set of pointers to objects. QhullSets may be ordered or unordered. They are the core data structure for Qhull.

»QhullVertex

A QhullVertex is a vertex of the convex hull. A simplicial QhullFacet has qh.hull_dim-1 vertices. A QhullVertex contains a QhullPoint. It may list its neighboring QhullFacet's.

»QhullVertexList

A QhullVertexList is a QhullLinkedList of QhullVertex. The global data structure, QhullQh contains a QhullVertexList of all the vertices.

»QhullVertexSet

A QhullVertexSet is a QhullSet of QhullVertex. The QhullVertexSet of a QhullFacet is the vertices of the facet. It is ordered for simplicial facets and unordered for non-simplicial facets.

»RboxPoints

RboxPoints is a std::vector of point coordinates (QhullPoint). It's iterator is CoordinateIterator.

RboxPoints.appendRandomPoints() appends points from a variety of distributions such as uniformly distributed within a cube and random points on a sphere. It can also append a cube's vertices or specific points.

»Cpp questions for Qhull

Developing C++ code requires many conventions, idioms, and technical details. The following questions have either mystified the author or do not have a clear answer. See also C++ and Perl Guidelines. and FIXUP notes in the code. Please add notes to Qhull Wiki.
  • FIXUP QH11028 Should return reference, but get reference to temporary
    iterator Coordinates::operator++() { return iterator(++i); }
  • size() as size_t, size_type, or int
  • Should all containers have a reserve()?
  • Qhull.feasiblePoint interface
  • How to avoid copy constructor while logging, maybeThrowQhullMessage()
  • How to configure Qhull output. Trace and results should go to stdout/stderr
  • Qhull and RboxPoints messaging. e.g., ~Qhull, hasQhullMessage(). Rename them as QhullErrorMessage?
  • How to add additional output to an error message, e.g., qh_setprint
  • Is idx the best name for an index? It's rather cryptic, but BSD strings.h defines index().
  • Qhull::feasiblePoint Qhull::useOutputStream as field or getter?
  • Define virtual functions for user customization of Qhull (e.g., qh_fprintf, qh_memfree,etc.)
  • Figure out RoadError::global_log. clearQhullMessage currently clearGlobalLog
  • Should the false QhullFacet be NULL or empty? e.g., QhullFacet::tricoplanarOwner() and QhullFacetSet::end()
  • Should output format for floats be predefined (qh_REAL_1, 2.2g, 10.7g) or as currently set for stream
  • Should cout << !point.defined() be blank or 'undefined'
  • Infinite point as !defined()
  • qlist and qlinkedlist define pointer, reference, size_type, difference_type, const_pointer, const_reference for the class but not for iterator and const_iterator vector.h --
    reference operator[](difference_type _Off) const
  • When forwarding an implementation is base() an approriate name (e.g., Coordinates::iterator::base() as std::vector::iterator).
  • When forwarding an implementation, does not work "returning address of temporary"
  • Also --, +=, and -=
    iterator       &operator++() { return iterator(i++); }
  • if vector inheritance is bad, is QhullVertexSet OK?
  • Should QhullPointSet define pointer and reference data types?

»Calling Qhull from C programs

Warning: Qhull was not designed for calling from C programs. You may find the C++ interface easier to use. You will need to understand the data structures and read the code. Most users will find it easier to call Qhull as an external command.

For examples of calling Qhull, see GNU Octave's computational geometry code, and Qhull's user_eg_r.c, user_eg2_r.c, and user_r.c. To see how Qhull calls its library, read unix_r.c, qconvex.c, qdelaun.c, qhalf.c, and qvoronoi.c. The '*_r.c' files are reentrant, otherwise they are non-reentrant. Either version may be used. New code should use reentrant Qhull.

See Reentrant Qhull functions, macros, and data structures for internal documentation of Qhull. The documentation provides an overview and index. To use the library you will need to read and understand the code. For most users, it is better to write data to a file, call the qhull program, and read the results from the output file.

If you use non-reentrant Qhull, be aware of the macros "qh" and "qhstat", e.g., "qh hull_dim". They are defined in libqhull.h. They allow the global data structures to be pre-allocated (faster access) or dynamically allocated (allows multiple copies).

Qhull's Makefile produces a library, libqhull_r.a, for inclusion in your programs. First review libqhull_r.h. This defines the data structures used by Qhull and provides prototypes for the top-level functions. Most users will only need libqhull_r.h in their programs. For example, the Qhull program is defined with libqhull_r.h and unix_r.c. To access all functions, use qhull_ra.h. Include the file with "#include <libqhull_r/qhull_ra.h>". This avoids potential name conflicts.

If you use the Qhull library, you are on your own as far as bugs go. Start with small examples for which you know the output. If you get a bug, try to duplicate it with the Qhull program. The 'Tc' option will catch many problems as they occur. When an error occurs, use 'T4 TPn' to trace from the last point added to the hull. Compare your trace with the trace output from the Qhull program.

Errors in the Qhull library are more likely than errors in the Qhull program. These are usually due to feature interactions that do not occur in the Qhull program. Please report all errors that you find in the Qhull library. Please include suggestions for improvement.

»How to avoid exit(), fprintf(), stderr, and stdout

Qhull sends output to qh.fout and errors, log messages, and summaries to qh.ferr. qh.fout is normally stdout and qh.ferr is stderr. qh.fout may be redefined by option 'TO' or the caller. qh.ferr may be redirected to qh.fout by option 'Tz'.

Qhull does not use stderr, stdout, fprintf(), or exit() directly.

Qhull reports errors via qh_errexit() by writting a message to qh.ferr and invoking longjmp(). This returns the caller to the corresponding setjmp() (c.f., QH_TRY_ in QhullQh.h). If qh_errexit() is not available, Qhull functions call qh_exit(). qh_exit() normally calls exit(), but may be redefined by the user. An example is libqhullcpp/usermem_r-cpp.cpp. It redefines qh_exit() as a 'throw'.

If qh_meminit() or qh_new_qhull() is called with ferr==NULL, then they set ferr to stderr. Otherwise the Qhull libraries use qh->ferr and qh->qhmem.ferr for error output.

If an error occurs before qh->ferr is initialized, Qhull invokes qh_fprintf_stderr(). The user may redefine this function along with qh_exit(), qh_malloc(), and qh_free().

The Qhull libraries write output via qh_fprintf() [userprintf_r.c]. Otherwise, the Qhull libraries do not use stdout, fprintf(), or printf(). Like qh_exit(), the user may redefine qh_fprintf().

»sets and quick memory allocation

You can use mem_r.c and qset_r.c individually. Mem_r.c implements quick-fit memory allocation. It is faster than malloc/free in applications that allocate and deallocate lots of memory.

Qset_r.c implements sets and related collections. It's the inner loop of Qhull, so speed is more important than abstraction. Set iteration is particularly fast. qset_r.c just includes the functions needed for Qhull.

»Delaunay triangulations and point indices

Here some unchecked code to print the point indices of each Delaunay triangle. Use option 'QJ' if you want to avoid non-simplicial facets. Note that upper Delaunay regions are skipped. These facets correspond to the furthest-site Delaunay triangulation.

  facetT *facet;
  vertexT *vertex, **vertexp;

  FORALLfacets {
    if (!facet->upperdelaunay) {
      printf ("%d", qh_setsize (facet->vertices);
      FOREACHvertex_(facet->vertices)
        printf (" %d", qh_pointid (vertex->point));
      printf ("\n");
    }
  }

»locate a facet with qh_findbestfacet()

The routine qh_findbestfacet in poly2_r.c is particularly useful. It uses a directed search to locate the facet that is furthest below a point. For Delaunay triangulations, this facet is the Delaunay triangle that contains the lifted point. For convex hulls, the distance of a point to the convex hull is either the distance to this facet or the distance to a subface of the facet.

Warning: If triangulated output ('Qt') and the best facet is triangulated, qh_findbestfacet() returns one of the corresponding 'tricoplanar' facets. The actual best facet may be a different tricoplanar facet.

See qh_nearvertex() in poly2.c for sample code to visit each tricoplanar facet. To identify the correct tricoplanar facet, see Devillers, et. al., ['01] and Mucke, et al ['96]. If you implement this test in general dimension, please notify qhull@qhull.org.

qh_findbestfacet performs an exhaustive search if its directed search returns a facet that is above the point. This occurs when the point is inside the hull or if the curvature of the convex hull is less than the curvature of a sphere centered at the point (e.g., a point near a lens-shaped convex hull). When the later occurs, the distance function is bimodal and a directed search may return a facet on the far side of the convex hull.

Algorithms that retain the previously constructed hulls usually avoid an exhaustive search for the best facet. You may use a hierarchical decomposition of the convex hull [Dobkin and Kirkpatrick '90].

To use qh_findbestfacet with Delaunay triangulations, lift the point to a paraboloid by summing the squares of its coordinates (see qh_setdelaunay in geom2_r.c). Do not scale the input with options 'Qbk', 'QBk', 'QbB' or 'Qbb'. See Mucke, et al ['96] for a good point location algorithm.

The intersection of a ray with the convex hull may be found by locating the facet closest to a distant point on the ray. Intersecting the ray with the facet's hyperplane gives a new point to test.

»on-line construction with qh_addpoint()

The Qhull library may be used for the on-line construction of convex hulls, Delaunay triangulations, and halfspace intersections about a point. It may be slower than implementations that retain intermediate convex hulls (e.g., Clarkson's hull program). These implementations always use a directed search. For the on-line construction of convex hulls and halfspace intersections, Qhull may use an exhaustive search (qh_findbestfacet).

You may use qh_findbestfacet and qh_addpoint (libqhull.c) to add a point to a convex hull. Do not modify the point's coordinates since qh_addpoint does not make a copy of the coordinates. For Delaunay triangulations, you need to lift the point to a paraboloid by summing the squares of the coordinates (see qh_setdelaunay in geom2.c). Do not scale the input with options 'Qbk', 'QBk', 'QbB' or 'Qbb'. Do not deallocate the point's coordinates. You need to provide a facet that is below the point (qh_findbestfacet).

You can not delete points. Another limitation is that Qhull uses the initial set of points to determine the maximum roundoff error (via the upper and lower bounds for each coordinate).

For many applications, it is better to rebuild the hull from scratch for each new point. This is especially true if the point set is small or if many points are added at a time.

Calling qh_addpoint from your program may be slower than recomputing the convex hull with qh_qhull. This is especially true if the added points are not appended to the qh first_point array. In this case, Qhull must search a set to determine a point's ID. [R. Weber]

See user_eg.c for examples of the on-line construction of convex hulls, Delaunay triangulations, and halfspace intersections. The outline is:

initialize qhull with an initial set of points
qh_qhull();

for each additional point p
   append p to the end of the point array or allocate p separately
   lift p to the paraboloid by calling qh_setdelaunay
   facet= qh_findbestfacet (p, !qh_ALL, &bestdist, &isoutside);
   if (isoutside)
      if (!qh_addpoint (point, facet, False))
         break;  /* user requested an early exit with 'TVn' or 'TCn' */

call qh_check_maxout() to compute outer planes
terminate qhull

»Constrained Delaunay triangulation

With a fair amount of work, Qhull is suitable for constrained Delaunay triangulation. See Shewchuk, ACM Symposium on Computational Geometry, Minneapolis 1998.

Here's a quick way to add a constraint to a Delaunay triangulation: subdivide the constraint into pieces shorter than the minimum feature separation. You will need an independent check of the constraint in the output since the minimum feature separation may be incorrect. [H. Geron]

»Tricoplanar facets and option 'Qt'

Option 'Qt' triangulates non-simplicial facets (e.g., a square facet in 3-d or a cubical facet in 4-d). All facets share the same apex (i.e., the first vertex in facet->vertices). For each triangulated facet, Qhull sets facet->tricoplanar true and copies facet->center, facet->normal, facet->offset, and facet->maxoutside. One of the facets owns facet->normal; its facet->keepcentrum is true. If facet->isarea is false, facet->triowner points to the owning facet.

Qhull sets facet->degenerate if the facet's vertices belong to the same ridge of the non-simplicial facet.

To visit each tricoplanar facet of a non-simplicial facet, either visit all neighbors of the apex or recursively visit all neighbors of a tricoplanar facet. The tricoplanar facets will have the same facet->center.

See qh_detvridge for an example of ignoring tricoplanar facets.

»Voronoi vertices of a region

The following code iterates over all Voronoi vertices for each Voronoi region. Qhull computes Voronoi vertices from the convex hull that corresponds to a Delaunay triangulation. An input site corresponds to a vertex of the convex hull and a Voronoi vertex corresponds to an adjacent facet. A facet is "upperdelaunay" if it corresponds to a Voronoi vertex "at-infinity". Qhull uses qh_printvoronoi in io.c for 'qvoronoi o'

/* please review this code for correctness */
qh_setvoronoi_all();
FORALLvertices {
   site_id = qh_pointid (vertex->point);
   if (qh hull_dim == 3)
      qh_order_vertexneighbors(vertex);
   infinity_seen = 0;
   FOREACHneighbor_(vertex) {
      if (neighbor->upperdelaunay) {
        if (!infinity_seen) {
          infinity_seen = 1;
          ... process a Voronoi vertex "at infinity" ...
        }
      }else {
        voronoi_vertex = neighbor->center;
        ... your code goes here ...
      }
   }
}

»Voronoi vertices of a ridge

Qhull uses qh_printvdiagram() in io.c to print the ridges of a Voronoi diagram for option 'Fv'. The helper function qh_eachvoronoi() does the real work. It calls the callback 'printvridge' for each ridge of the Voronoi diagram.

You may call qh_printvdiagram2(), qh_eachvoronoi(), or qh_eachvoronoi_all() with your own function. If you do not need the total number of ridges, you can skip the first call to qh_printvdiagram2(). See qh_printvridge() and qh_printvnorm() in io.c for examples.

»vertex neighbors of a vertex

To visit all of the vertices that share an edge with a vertex:

  • Generate neighbors for each vertex with qh_vertexneighbors in poly2.c.
  • For simplicial facets, visit the vertices of each neighbor
  • For non-simplicial facets,
    • Generate ridges for neighbors with qh_makeridges in merge.c.
    • Generate ridges for a vertex with qh_vertexridges in merge.c.
    • Visit the vertices of these ridges.

For non-simplicial facets, the ridges form a simplicial decomposition of the (d-2)-faces between each pair of facets -- if you need 1-faces, you probably need to generate the full face graph of the convex hull.

»Performance of Qhull

Empirically, Qhull's performance is balanced in the sense that the average case happens on average. This may always be true if the precision of the input is limited to at most O(log n) bits. Empirically, the maximum number of vertices occurs at the end of constructing the hull.

Let n be the number of input points, v be the number of output vertices, and f_v be the maximum number of facets for a convex hull of v vertices. If both conditions hold, Qhull runs in O(n log v) in 2-d and 3-d and O(n f_v/v) otherwise. The function f_v increases rapidly with dimension. It is O(v^floor(d/2) / floor(d/2)!).

The time complexity for merging is unknown. Options 'C-0' and 'Qx' (defaults) handle precision problems due to floating-point arithmetic. They are optimized for simplicial outputs.

When running large data sets, you should monitor Qhull's performance with the 'TFn' option. The time per facet is approximately constant. In high-d with many merged facets, the size of the ridge sets grows rapidly. For example the product of 8-d simplices contains 18 facets and 500,000 ridges. This will increase the time needed per facet.

As dimension increases, the number of facets and ridges in a convex hull grows rapidly for the same number of vertices. For example, the convex hull of 300 cospherical points in 6-d has 30,000 facets.

If Qhull appears to stop processing facets, check the memory usage of Qhull. If more than 5-10% of Qhull is in virtual memory, its performance will degrade rapidly.

When building hulls in 20-d and higher, you can follow the progress of Qhull with option 'T1'. It reports each major event in processing a point.

To reduce memory requirements, recompile Qhull for single-precision reals (REALfloat in user.h). Single-precision does not work with joggle ('QJ'). Check qh_MEMalign in user.h and the match between free list sizes and data structure sizes (see the end of the statistics report from 'Ts'). If free list sizes do not match, you may be able to use a smaller qh_MEMalign. Setting qh_COMPUTEfurthest saves a small amount of memory, as does clearing qh_MAXoutside (both in user.h).

Shewchuk is working on a 3-d version of his triangle program. It is optimized for 3-d simplicial Delaunay triangulation and uses less memory than Qhull.

To reduce the size of the Qhull executable, consider qh_NOtrace and qh_KEEPstatistics 0 in user.h. By changing user.c you can also remove the input/output code in io.c. If you don't need facet merging, then version 1.01 of Qhull is much smaller. It contains some bugs that prevent Qhull from initializing in simple test cases. It is slower in high dimensions.

The precision options, 'Vn', 'Wn', 'Un'. 'A-n', 'C-n', 'An', 'Cn', and 'Qx', may have large effects on Qhull performance. You will need to experiment to find the best combination for your application.

The verify option ('Tv') checks every point after the hull is complete. If facet merging is used, it checks that every point is inside every facet. This can take a very long time if there are many points and many facets. You can interrupt the verify without losing your output. If facet merging is not used and there are many points and facets, Qhull uses a directed search instead of an exhaustive search. This should be fast enough for most point sets. Directed search is not used for facet merging because directed search was already used for updating the facets' outer planes.

The check-frequently option ('Tc') becomes expensive as the dimension increases. The verify option ('Tv') performs many of the same checks before outputting the results.

Options 'Q0' (no pre-merging), 'Q3' (no checks for redundant vertices), 'Q5' (no updates for outer planes), and 'Q8' (no near-interior points) increase Qhull's speed. The corresponding operations may not be needed in your application.

In 2-d and 3-d, a partial hull may be faster to produce. Option 'QgGn' only builds facets visible to point n. Option 'QgVn' only builds facets that contain point n. In higher-dimensions, this does not reduce the number of facets.

User.h includes a number of performance-related constants. Changes may improve Qhull performance on your data sets. To understand their effect on performance, you will need to read the corresponding code.

GNU gprof reports that the dominate cost for 3-d convex hull of cosperical points is qh_distplane(), mainly called from qh_findbestnew(). The dominate cost for 3-d Delaunay triangulation is creating new facets in qh_addpoint(), while qh_distplane() remains the most expensive function.

»Enhancements to Qhull

There are many ways in which Qhull can be improved.

[Jan 2016] Suggestions
------------
To do for a future verson of Qhull
 - Add a post-merge pass for Delaunay slivers.  Merge into a neighbor with a circumsphere that includes the opposite point. [M. Treacy]
 - Option to add a bounding box for Delaunay triangulations, e,g., nearly coincident points
 - Rescale output to match 'QbB' on input [J. Metz, 1/30/2014 12:21p]
 - Run through valgrind
 - Notes to compgeom on conformant triangulation and Voronoi volume
 - Implement weighted Delaunay triangulation and weighted Voronoi diagram [A. Liebscher]
   e.g., Sugihara, "Three-dimensional convex hull as a fruitful source of diagrams," Theoretical Computer Science, 2000, 235:325-337
 - testqset: test qh_setdelnth and move-to-front
 - Makefile: Re-review gcc/g++ warnings.  OK in 2011.
 - Break up -Wextra into its components or figure out how to override -Wunused-but-set-variable
   unused-but-set-variable is reporting incorrectly.  All instances are annotated.
 - CMakelists.txt:  Why are files duplicated for cmake build
 - CMakeLists.txt: configure the 'ctest' target

 - Can countT be defined as 'int', 'unsigned int', or 64-bit int?
   countT is currently defined as 'int' in qset_r.h
   Vertex ID and ridge ID perhaps should be countT, They are currently 'unsigned'
   Check use of 'int' vs. countT in all cpp code
   Check use of 'int' vs. countT in all c code
   qset_r.h defines countT -- duplicates code in user_r.h -- need to add to qset.h/user.h
   countT -1 used as a flag in Coordinates.mid(), QhullFacet->id()
   Also QhullPoints indexOf and lastIndexOf
   Also QhullPointSet indexOf and lastIndexOf
   Coordinates.indexOf assumes countT is signed (from end)
   Coordinates.lastIndexOf assumes countT is signed (from end)
   All error messages with countT are wrong, convert to int?
   RboxPoints.qh_fprintf_rbox, etc. message 9393 assumes countT but may be int, va_arg(args, countT);  Need to split

------------
To do for a furture version of the C++ interface
 - Document C++ using Doxygen conventions (//! and //!<)
 - Should Qhull manage the output formats for doubles?  QH11010 user_r.h defines qh_REAL_1 as %6.8g
 - Allocate memory for QhullSet using Qhull.qhmem.  Create default constructors for QhullVertexSet etc.  Also mid() etc.
 - Add interior point for automatic translation?
 - Write a program with concurrent Qhull
 - Write QhullStat and QhullStat_test
 - Add QList and vector instance of facetT*, etc.
 - Generalize QhullPointSetIterator
 - qh-code.html: Document changes to C++ interface.
      Organize C++ documentation into collection classes, etc.
 - Review all C++ classes and C++ tests
 - QhullVertexSet uses QhullSetBase::referenceSetT() to free it's memory.   Probably needed elsewhere
 - The Boost Graph Library provides C++ classes for graph data structures. It may help
   enhance Qhull's C++ interface [Dr. Dobb's 9/00 p. 29-38; OOPSLA '99 p. 399-414].

[Jan 2010] Suggestions
 - Generate vcproj from qtpro files
   cd qtpro && qmake -spec win32-msvc2005 -tp vc -recursive
   sed -i 's/C\:\/bash\/local\/qhull\/qtpro\///' qhull-all.sln
   Change qhullcpp to libqhull.dll
   Allow both builds on same host (keep /tmp separate)
 - C++ class for access to statistics, accumulate vs. add
 - Add dialog box to RoadError-- a virtual function?
 - Option 'Gt' does not make visible all facets of the mesh example, rbox 32 M1,0,1 | qhull d Gt
 - Merge small volume boundary cells into unbounded regions [Dominik Szczerba]
 - Postmerge with merge options
 - Add modify operators and MutablePointCoordinateIterator to PointCoordinates
 - Fix option Qt for conformant triangulations of merged facets
 - Investigate flipped facet -- rbox 100 s D3 t1263080158 | qhull R1e-3 Tcv Qc
 - Add doc comments to c++ code
 - Measure performance of Qhull, seconds per point by dimension
 - Report potential wraparound of 64-bit ints -- e.g., a large set or points

Documentation
- Qhull::addPoint().  Problems with qh_findbestfacet and otherpoints see
   qh-code.html#inc on-line construction with qh_addpoint()
- How to handle 64-bit possible loss of data.  WARN64, ptr_intT, size_t/int
- Show custom of qh_fprintf
- grep 'qh_mem ' x | sort | awk '{ print $2; }' | uniq -c | grep -vE ' (2|4|6|8|10|12|14|16|20|64|162)[^0-9]'
- qtpro/qhulltest contains .pro and Makefile.  Remove Makefiles by setting shadow directory to ../../tmp/projectname
- Rules for use of qh_qh and multi processes
    UsingQhull
    errorIfAnotherUser
    ~QhullPoints() needs ownership of qh_qh
    Does !qh_pointer work?
    When is qh_qh required?  Minimize the time.
   qhmem, qhstat.ferr
   qhull_inuse==1 when qhull globals active [not useful?]
   rbox_inuse==1 when rbox globals active
   - Multithreaded -- call largest dimension for infinityPoint() and origin()
 - Better documentation for qhmem totshort, freesize, etc.
 - how to change .h, .c, and .cpp to text/html.  OK in Opera
 - QhullVertex.dimension() is not quite correct, epensive
 - Check globalAngleEpsilon
 - Deprecate save_qhull()

[Dec 2003] Here is a partial list:
 - fix finddelaunay() in user_eg.c for tricoplanar facets
 - write a BGL, C++ interface to Qhull
     http://www.boost.org/libs/graph/doc/table_of_contents.html
 - change qh_save_qhull to swap the qhT structure instead of using pointers
 - change error handling and tracing to be independent of 'qh ferr'
 - determine the maximum width for a given set of parameters
 - prove that directed search locates all coplanar facets
 - in high-d merging, can a loop of facets become disconnected?
 - find a way to improve inner hulls in 5-d and higher
 - determine the best policy for facet visibility ('Vn')
 - determine the limitations of 'Qg'

Precision improvements:
 - For 'Qt', resolve cross-linked, butterfly ridges.
     May allow retriangulation in qh_addpoint().
 - for Delaunay triangulations ('d' or 'v') under joggled input ('QJ'),
     remove vertical facets whose lowest vertex may be coplanar with convex hull
 - review use of 'Qbb' with 'd QJ'.  Is MAXabs_coord better than MAXwidth?
 - check Sugihara and Iri's better in-sphere test [Canadian
     Conf. on Comp. Geo., 1989; Univ. of Tokyo RMI 89-05]
 - replace centrum with center of mass and facet area
 - handle numeric overflow in qh_normalize and elsewhere
 - merge flipped facets into non-flipped neighbors.
     currently they merge into best neighbor (appears ok)
 - determine min norm for Cramer's rule (qh_sethyperplane_det).  It looks high.
 - improve facet width for very narrow distributions

New features:
 - implement Matlab's tsearch() using Qhull
 - compute volume of Voronoi regions.  You need to determine the dual face
   graph in all dimensions [see Clarkson's hull program]
 - compute alpha shapes [see Clarkson's hull program]
 - implement deletion of Delaunay vertices
      see Devillers, ACM Symposium on Computational Geometry, Minneapolis 1999.
 - compute largest empty circle [see O'Rourke, chapter 5.5.3] [Hase]
 - list redundant (i.e., coincident) vertices [Spitz]
 - implement Mucke, et al, ['96] for point location in Delaunay triangulations
 - implement convex hull of moving points
 - implement constrained Delaunay diagrams
      see Shewchuk, ACM Symposium on Computational Geometry, Minneapolis 1998.
 - estimate outer volume of hull
 - automatically determine lower dimensional hulls
 - allow "color" data for input points
      need to insert a coordinate for Delaunay triangulations

Input/output improvements:
 - Support the VTK Visualization Toolkit, http://www.kitware.com/vtk.html
 - generate output data array for Qhull library [Gautier]
 - need improved DOS window with screen fonts, scrollbar, cut/paste
 - generate Geomview output for Voronoi ridges and unbounded rays
 - generate Geomview output for halfspace intersection
 - generate Geomview display of furthest-site Voronoi diagram
 - use 'GDn' to view 5-d facets in 4-d
 - convert Geomview output for other 3-d viewers
 - add interactive output option to avoid recomputing a hull
 - orient vertex neighbors for 'Fv' in 3-d and 2-d
 - track total number of ridges for summary and logging

Performance improvements:
 - optimize Qhull for 2-d Delaunay triangulations
 -   use O'Rourke's '94 vertex->duplicate_edge
 -   add bucketing
 -   better to specialize all of the code (ca. 2-3x faster w/o merging)
 - use updated LU decomposition to speed up hyperplane construction
 -        [Gill et al. 1974, Math. Comp. 28:505-35]
 - construct hyperplanes from the corresponding horizon/visible facets
 - for merging in high d, do not use vertex->neighbors

Please let us know about your applications and improvements.


Up: Home page for Qhull
Up: Qhull manual: Table of Contents
To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
To: Qhull code: Table of Contents
Dn: Qhull functions, macros, and data structures


The Geometry Center Home Page

Comments to: qhull@qhull.org
Created: Sept. 25, 1995 --- Last modified: see changes.txt

geometry/vignettes/qhull/html/qhalf.html0000644000176200001440000006564713431000557020207 0ustar liggesusers qhalf -- halfspace intersection about a point

Up: Home page for Qhull
Up: Qhull manual: Table of Contents
To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
To: synopsis • input • outputs • controls • graphics • notes • conventions • options


[halfspace]qhalf -- halfspace intersection about a point

The intersection of a set of halfspaces is a polytope. The polytope may be unbounded. See Preparata & Shamos ['85] for a discussion. In low dimensions, halfspace intersection may be used for linear programming.

Example: rbox c | qconvex FQ FV n | qhalf Fp

Print the intersection of the facets of a cube. rbox c generates the vertices of a cube. qconvex FV n returns of average of the cube's vertices (in this case, the origin) and the halfspaces that define the cube. qhalf Fp computes the intersection of the halfspaces about the origin. The intersection is the vertices of the original cube.

Example: rbox c d G0.55 | qconvex FQ FV n | qhalf Fp

Print the intersection of the facets of a cube and a diamond. There are 24 facets and 14 intersection points. Four facets define each diamond vertex. Six facets define each cube vertex.

Example: rbox c d G0.55 | qconvex FQ FV n | qhalf Fp Qt

Same as above except triangulate before computing the intersection points. Three facets define each intersection point. There are two duplicates of the diamond and four duplicates of the cube.

Example: rbox 10 s t10 | qconvex FQ FV n | qhalf Fp Fn

Print the intersection of the facets of the convex hull of 10 cospherical points. Include the intersection points and the neighboring intersections. As in the previous examples, the intersection points are nearly the same as the original input points.

In Qhull, a halfspace is defined by the points on or below a hyperplane. The distance of each point to the hyperplane is less than or equal to zero.

Qhull computes a halfspace intersection by the geometric duality between points and halfspaces. See halfspace examples, qhalf notes, and option 'p' of qhalf outputs.

Qhalf's outputs are the intersection points (Fp) and the neighboring intersection points (Fn). For random inputs, halfspace intersections are usually defined by more than d halfspaces. See the sphere example.

You can try triangulated output ('Qt') and joggled input ('QJ'). It demonstrates that triangulated output is more accurate than joggled input.

If you use 'Qt' (triangulated output), all halfspace intersections are simplicial (e.g., three halfspaces per intersection in 3-d). In 3-d, if more than three halfspaces intersect at the same point, triangulated output will produce duplicate intersections, one for each additional halfspace. See the third example, or add 'Qt' to the sphere example.

If you use 'QJ' (joggled input), all halfspace intersections are simplicial. This may lead to nearly identical intersections. For example, either replace 'Qt' with 'QJ' above, or add 'QJ' to the sphere example. See Merged facets or joggled input.

The 'qhalf' program is equivalent to 'qhull H' in 2-d to 4-d, and 'qhull H Qx' in 5-d and higher. It disables the following Qhull options: d n v Qbb QbB Qf Qg Qm Qr QR Qv Qx Qz TR E V Fa FA FC FD FS Ft FV Gt Q0,etc.

Copyright © 1995-2018 C.B. Barber


»qhalf synopsis

qhalf- halfspace intersection about a point.
    input (stdin): [dim, 1, interior point]
                   dim+1, n
                   halfspace coefficients + offset
    comments start with a non-numeric character

options (qhalf.html):
    Hn,n - specify coordinates of interior point
    Qt   - triangulated output
    QJ   - joggle input instead of merging facets
    Tv   - verify result: structure, convexity, and redundancy
    .    - concise list of all options
    -    - one-line description of all options

output options (subset):
    s    - summary of results (default)
    Fp   - intersection coordinates
    Fv   - non-redundant halfspaces incident to each intersection
    Fx   - non-redundant halfspaces
    o    - OFF file format (dual convex hull)
    G    - Geomview output (dual convex hull)
    m    - Mathematica output (dual convex hull)
    QVn  - print intersections for halfspace n, -n if not
    TO file - output results to file, may be enclosed in single quotes

examples:
    rbox d | qconvex n | qhalf s H0,0,0 Fp
    rbox c | qconvex FV n | qhalf s i
    rbox c | qconvex FV n | qhalf s o

»qhalf input

The input data on stdin consists of:

  • [optional] interior point
    • dimension
    • 1
    • coordinates of interior point
  • dimension + 1
  • number of halfspaces
  • halfspace coefficients followed by offset

Use I/O redirection (e.g., qhalf < data.txt), a pipe (e.g., rbox c | qconvex FV n | qhalf), or the 'TI' option (e.g., qhalf TI data.txt).

Qhull needs an interior point to compute the halfspace intersection. An interior point is clearly inside all of the halfspaces. A point is inside a halfspace if its distance to the corresponding hyperplane is negative.

The interior point may be listed at the beginning of the input (as shown above). If not, option 'Hn,n' defines the interior point as [n,n,0,...] where 0 is the default coordinate (e.g., 'H0' is the origin). Use linear programming if you do not know the interior point (see halfspace notes),

The input to qhalf is a set of halfspaces that are defined by their hyperplanes. Each halfspace is defined by d coefficients followed by a signed offset. This defines a linear inequality. The coefficients define a vector that is normal to the halfspace. The vector may have any length. If it has length one, the offset is the distance from the origin to the halfspace's boundary. Points in the halfspace have a negative distance to the hyperplane. The distance from the interior point to each halfspace is likewise negative.

The halfspace format is the same as Qhull's output options 'n', 'Fo', and 'Fi'. Use option 'Fd' to use cdd format for the halfspaces.

For example, here is the input for computing the intersection of halfplanes that form a cube.

rbox c | qconvex FQ FV n TO data

RBOX c | QCONVEX FQ FV n
3 1
     0      0      0
4
6
     0      0     -1   -0.5
     0     -1      0   -0.5
     1      0      0   -0.5
    -1      0      0   -0.5
     0      1      0   -0.5
     0      0      1   -0.5

qhalf s Fp < data


Halfspace intersection by the convex hull of 6 points in 3-d:

  Number of halfspaces: 6
  Number of non-redundant halfspaces: 6
  Number of intersection points: 8

Statistics for: RBOX c | QCONVEX FQ FV n | QHALF s Fp

  Number of points processed: 6
  Number of hyperplanes created: 11
  Number of distance tests for qhull: 11
  Number of merged facets: 1
  Number of distance tests for merging: 45
  CPU seconds to compute hull (after input):  0

3
3
8
  -0.5    0.5    0.5
   0.5    0.5    0.5
  -0.5    0.5   -0.5
   0.5    0.5   -0.5
   0.5   -0.5    0.5
  -0.5   -0.5    0.5
  -0.5   -0.5   -0.5
   0.5   -0.5   -0.5

»qhalf outputs

The following options control the output for halfspace intersection.

 
Intersections
FN
list intersection points for each non-redundant halfspace. The first line is the number of non-redundant halfspaces. Each remaining lines starts with the number of intersection points. For the cube example, each halfspace has four intersection points.
Fn
list neighboring intersections for each intersection point. The first line is the number of intersection points. Each remaining line starts with the number of neighboring intersections. For the cube example, each intersection point has three neighboring intersections.

In 3-d, a non-simplicial intersection has more than three neighboring intersections. For random data (e.g., the sphere example), non-simplicial intersections are the norm. Option 'Qt' produces three neighboring intersections per intersection by duplicating the intersection points. Option QJ' produces three neighboring intersections per intersection by joggling the hyperplanes and hence their intersections.

Fp
print intersection coordinates. The first line is the dimension and the second line is the number of intersection points. The following lines are the coordinates of each intersection.
FI
list intersection IDs. The first line is the number of intersections. The IDs follow, one per line.
 
 
Halfspaces
Fx
list non-redundant halfspaces. The first line is the number of non-redundant halfspaces. The other lines list one halfspace per line. A halfspace is non-redundant if it defines a facet of the intersection. Redundant halfspaces are ignored. For the cube example, all of the halfspaces are non-redundant.
Fv
list non-redundant halfspaces incident to each intersection point. The first line is the number of non-redundant halfspaces. Each remaining line starts with the number of non-redundant halfspaces. For the cube example, each intersection is incident to three halfspaces.
i
list non-redundant halfspaces incident to each intersection point. The first line is the number of intersection points. Each remaining line lists the incident, non-redundant halfspaces. For the cube example, each intersection is incident to three halfspaces.
Fc
list coplanar halfspaces for each intersection point. The first line is the number of intersection points. Each remaining line starts with the number of coplanar halfspaces. A coplanar halfspace is listed for one intersection point even though it is coplanar to multiple intersection points.
Qi Fc
list redundant halfspaces for each intersection point. The first line is the number of intersection points. Each remaining line starts with the number of redundant halfspaces. Use options 'Qc Qi Fc' to list coplanar and redundant halfspaces.
 
 
General
s
print summary for the halfspace intersection. Use 'Fs' if you need numeric data.
o
print vertices and facets of the dual convex hull. The first line is the dimension. The second line is the number of vertices, facets, and ridges. The vertex coordinates are next, followed by the facets, one per line.
p
print vertex coordinates of the dual convex hull. Each vertex corresponds to a non-redundant halfspace. Its coordinates are the negative of the hyperplane's coefficients divided by the offset plus the inner product of the coefficients and the interior point (-c/(b+a.p). Options 'p Qc' includes coplanar halfspaces. Options 'p Qi' includes redundant halfspaces.
m
Mathematica output for the dual convex hull in 2-d or 3-d.
FM
Maple output for the dual convex hull in 2-d or 3-d.
G
Geomview output for the dual convex hull in 2-d, 3-d, or 4-d.

»qhalf controls

These options provide additional control:

Qt
triangulated output. If a 3-d intersection is defined by more than three hyperplanes, Qhull produces duplicate intersections -- one for each extra hyperplane.
QJ
joggle the input instead of merging facets. In 3-d, this guarantees that each intersection is defined by three hyperplanes.
f
facet dump. Print the data structure for each intersection (i.e., facet)
TFn
report summary after constructing n intersections
QVn
select intersection points for halfspace n (marked 'good')
QGn
select intersection points that are visible to halfspace n (marked 'good'). Use -n for the remainder.
Qbk:0Bk:0
remove the k-th coordinate from the input. This computes the halfspace intersection in one lower dimension.
Tv
verify result
TI file
input data from file. The filename may not use spaces or quotes.
TO file
output results to file. Use single quotes if the filename contains spaces (e.g., TO 'file with spaces.txt'
Qs
search all points for the initial simplex. If Qhull can not construct an initial simplex, it reports a descriptive message. Usually, the point set is degenerate and one or more dimensions should be removed ('Qbk:0Bk:0'). If not, use option 'Qs'. It performs an exhaustive search for the best initial simplex. This is expensive is high dimensions.

»qhalf graphics

To view the results with Geomview, compute the convex hull of the intersection points ('qhull FQ H0 Fp | qhull G'). See Halfspace examples.

»qhalf notes

See halfspace intersection for precision issues related to qhalf.

If you do not know an interior point for the halfspaces, use linear programming to find one. Assume, n halfspaces defined by: aj*x1+bj*x2+cj*x3+dj<=0, j=1..n. Perform the following linear program:

max(x5) aj*x1+bj*x2+cj*x3+dj*x4+x5<=0, j=1..n

Then, if [x1,x2,x3,x4,x5] is an optimal solution with x4>0 and x5>0 we get:

aj*(x1/x4)+bj*(x2/x4)+cj*(x3/x4)+dj<=(-x5/x4) j=1..n and (-x5/x4)<0,

and conclude that the point [x1/x4,x2/x4,x3/x4] is in the interior of all the halfspaces. Since x5 is optimal, this point is "way in" the interior (good for precision errors).

After finding an interior point, the rest of the intersection algorithm is from Preparata & Shamos ['85, p. 316, "A simple case ..."]. Translate the halfspaces so that the interior point is the origin. Calculate the dual polytope. The dual polytope is the convex hull of the vertices dual to the original faces in regard to the unit sphere (i.e., halfspaces at distance d from the origin are dual to vertices at distance 1/d). Then calculate the resulting polytope, which is the dual of the dual polytope, and translate the origin back to the interior point [S. Spitz, S. Teller, D. Strawn].

»qhalf conventions

The following terminology is used for halfspace intersection in Qhull. This is the hardest structure to understand. The underlying structure is a convex hull with one vertex per non-redundant halfspace. See convex hull conventions and Qhull's data structures.

  • interior point - a point in the intersection of the halfspaces. Qhull needs an interior point to compute the intersection. See halfspace input.
  • halfspace - d coordinates for the normal and a signed offset. The distance to an interior point is negative.
  • non-redundant halfspace - a halfspace that defines an output facet
  • vertex - a dual vertex in the convex hull corresponding to a non-redundant halfspace
  • coplanar point - the dual point corresponding to a similar halfspace
  • interior point - the dual point corresponding to a redundant halfspace
  • intersection point- the intersection of d or more non-redundant halfspaces
  • facet - a dual facet in the convex hull corresponding to an intersection point
  • non-simplicial facet - more than d halfspaces intersect at a point
  • good facet - an intersection point that satisfies restriction 'QVn', etc.

»qhalf options

qhalf- compute the intersection of halfspaces about a point
    http://www.qhull.org

input (stdin):
    optional interior point: dimension, 1, coordinates
    first lines: dimension+1 and number of halfspaces
    other lines: halfspace coefficients followed by offset
    comments:    start with a non-numeric character

options:
    Hn,n - specify coordinates of interior point
    Qt   - triangulated ouput
    QJ   - joggle input instead of merging facets
    Qc   - keep coplanar halfspaces
    Qi   - keep other redundant halfspaces

Qhull control options:
    QJn  - randomly joggle input in range [-n,n]
    Qbk:0Bk:0 - remove k-th coordinate from input
    Qs   - search all halfspaces for the initial simplex
    QGn  - print intersection if redundant to halfspace n, -n for not
    QVn  - print intersections for halfspace n, -n if not

Trace options:
    T4   - trace at level n, 4=all, 5=mem/gauss, -1= events
    Tc   - check frequently during execution
    Ts   - print statistics
    Tv   - verify result: structure, convexity, and redundancy
    Tz   - send all output to stdout
    TFn  - report summary when n or more facets created
    TI file - input data from file, no spaces or single quotes
    TO file - output results to file, may be enclosed in single quotes
    TPn  - turn on tracing when halfspace n added to intersection
    TMn  - turn on tracing at merge n
    TWn  - trace merge facets when width > n
    TVn  - stop qhull after adding halfspace n, -n for before (see TCn)
    TCn  - stop qhull after building cone for halfspace n (see TVn)

Precision options:
    Cn   - radius of centrum (roundoff added).  Merge facets if non-convex
     An  - cosine of maximum angle.  Merge facets if cosine > n or non-convex
           C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge
    Rn   - randomly perturb computations by a factor of [1-n,1+n]
    Un   - max distance below plane for a new, coplanar halfspace
    Wn   - min facet width for outside halfspace (before roundoff)

Output formats (may be combined; if none, produces a summary to stdout):
    f    - facet dump
    G    - Geomview output (dual convex hull)
    i    - non-redundant halfspaces incident to each intersection
    m    - Mathematica output (dual convex hull)
    o    - OFF format (dual convex hull: dimension, points, and facets)
    p    - vertex coordinates of dual convex hull (coplanars if 'Qc' or 'Qi')
    s    - summary (stderr)

More formats:
    Fc   - count plus redundant halfspaces for each intersection
         -   Qc (default) for coplanar and Qi for other redundant
    Fd   - use cdd format for input (homogeneous with offset first)
    FF   - facet dump without ridges
    FI   - ID of each intersection
    Fm   - merge count for each intersection (511 max)
    FM   - Maple output (dual convex hull)
    Fn   - count plus neighboring intersections for each intersection
    FN   - count plus intersections for each non-redundant halfspace
    FO   - options and precision constants
    Fp   - dim, count, and intersection coordinates
    FP   - nearest halfspace and distance for each redundant halfspace
    FQ   - command used for qhalf
    Fs   - summary: #int (8), dim, #halfspaces, #non-redundant, #intersections
                      for output: #non-redundant, #intersections, #coplanar
                                  halfspaces, #non-simplicial intersections
                    #real (2), max outer plane, min vertex
    Fv   - count plus non-redundant halfspaces for each intersection
    Fx   - non-redundant halfspaces

Geomview output (2-d, 3-d and 4-d; dual convex hull)
    Ga   - all points (i.e., transformed halfspaces) as dots
     Gp  -  coplanar points and vertices as radii
     Gv  -  vertices (i.e., non-redundant halfspaces) as spheres
    Gi   - inner planes (i.e., halfspace intersections) only
     Gn  -  no planes
     Go  -  outer planes only
    Gc   - centrums
    Gh   - hyperplane intersections
    Gr   - ridges
    GDn  - drop dimension n in 3-d and 4-d output

Print options:
    PAn  - keep n largest facets (i.e., intersections) by area
    Pdk:n- drop facet if normal[k] <= n (default 0.0)
    PDk:n- drop facet if normal[k] >= n
    Pg   - print good facets (needs 'QGn' or 'QVn')
    PFn  - keep facets whose area is at least n
    PG   - print neighbors of good facets
    PMn  - keep n facets with most merges
    Po   - force output.  If error, output neighborhood of facet
    Pp   - do not report precision problems

    .    - list of all options
    -    - one line descriptions of all options

Up: Home page for Qhull
Up: Qhull manual: Table of Contents
To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
To: synopsis • input • outputs • controls • graphics • notes • conventions • options


The Geometry Center Home Page

Comments to: qhull@qhull.org
Created: Sept. 25, 1995 --- Last modified: see top

geometry/vignettes/qhull/html/qh--rand.gif0000644000176200001440000000744313431000556020311 0ustar liggesusersGIF87add=*2-E>,^0GI*ccNk_(*&^?DN2A>L0Be(E@IoJnI6-LFuBm+:DU2 )VT!~_ I!F>99=;`G #(5"nW]hlAN a]/D"E4fesjaEPF[I*1'RVv *fFBFIML4L*:#_@DK2-fmoM+U9+$DN=}uuUL&??=%)F$BU<;?|[5?4>-E;K87'F 4j{ov=eF5z4tzet+UGTF8l7:TMX}<9[BO:#'*UlC63(ng*7f".b-d-2hA3GF`XeHETH:^N+Sie?`:3ZK2M[]L4\";'Mk_hI;VK<:iG[.[vR<4-BFtydV>J1#'6@9E :&jc,C89T'yeUe-t@CcV2ht:-;?3RRRsBGJ>E-GT*zqEffr)>L!RfO>yE|wJZ|ZL(Z)04@*+7..Q2FIMT`)3 8877b3%:(/W,M3ilDg^xc`V;8@B@`JO2A=tjZAu*G`Ye5(N+\h,dd H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0c|&32*jĩW<}()CjW^6&ߕ+ʕomkuJ&NOU)Um^5>ZkK †A6FK.-e>}ccbnˌ[~)Ť+.fW9hlPD5e9[Fxmܚڴi+R$ xW.w 1'(Ơ&HPGJ9[OgqĀ7d`*,k $ݧO2>(Ac9 ,,~()%hSLU <$c x`"Ȣ$qʂAIY3P$'ָ@\'Z@5$rB )5 $83L8d쩩7jq5p-T3S.A4n^4ndbje-SƧ9KI5 l T:p~ {DR@lG7l)4R|F>nɶdZ"L̢Ha9.*3̘@$S (a,njE.TjJc OqH+lau་+%P3֜% O;͆.`4!Oa7!J˒|mI7sԻ쒪 rW;pTi20ؾ" ,I@N!nJݨ p 3:RL.-f p_@/oq yB\nPObq6 x'[(F6G%x:%$W&ސQ>2TIdK"Y:j_#HA gK(. ' \xEq@$(0_1(ҊGXYdf7 9KfbXȶU P0p \\%AhE3jZÒ!s VˍXke"q`-I90Sj$%jBes; E$".mm Uܒ_>MF"֤oX Ǧ[IxMnr8VBq06W>dc8 *x_(;a ^]c J80ұ ]$>B)}mjǵZCN0C' sy>-u(gl@!{B ah(%lQr;hpY y5>0~@ Glb?UK $  s@~~pzaVc|pp X@ c}q gz|n p 0xt 1|<ȃbHpGx0| Vh_`J Tfn{1J RhthPm BP6PHPX+xE (zsxVa@|j@Pc (  Y8 @$0 j@R8J Xv7s(` @ ÀR苄|hH ` f {Ҩ0 ؋@3p+@ _p@̨ ސ@(0}m v ppvXt 0 IH(G@ Qhull Downloads

Up: Qhull Home Page


[CONE] Qhull Downloads


Up: Qhull Home Page


[HOME] The Geometry Center Home Page

Comments to: qhull@qhull.org
geometry/vignettes/qhull/html/qh-faq.html0000644000176200001440000014667513431000557020272 0ustar liggesusers Qhull FAQ

Up: Home page for Qhull (http://www.qhull.org)
Up: Qhull manual: Table of Contents
To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
To: FAQ: Table of Contents (please wait while loading)


[4-d cube] Frequently Asked Questions about Qhull

If your question does not appear here, see:

Qhull is a general dimension code for computing convex hulls, Delaunay triangulations, halfspace intersections about a point, Voronoi diagrams, furthest-site Delaunay triangulations, and furthest-site Voronoi diagrams. These structures have applications in science, engineering, statistics, and mathematics. For a detailed introduction, see O'Rourke ['94], Computational Geometry in C.

There are separate programs for each application of Qhull. These programs disable experimental and inappropriate options. If you prefer, you may use Qhull directly. All programs run the same code.

Version 3.1 added triangulated output ('Qt'). It should be used for Delaunay triangulations instead of using joggled input ('QJ').

Brad Barber, Arlington MA, 2010/01/09

Copyright © 1998-2018 C.B. Barber


»FAQ: Table of Contents

Within each category, the most recently asked questions are first.

  • Startup questions
    • How do I run Qhull from Windows?
    • How do I enter points for Qhull?
    • How do I learn to use Qhull?
  • Convex hull questions
    • How do I report just the area and volume of a convex hull?
    • Why are there extra points in a 4-d or higher convex hull?
    • How do I report duplicate vertices?
  • Delaunay triangulation questions
    • How do I get rid of nearly flat Delaunay triangles?
    • How do I find the Delaunay triangle or Voronoi region that is closest to a point?
    • How do I compute the Delaunay triangulation of a non-convex object?
    • How do I mesh a volume from a set of triangulated surface points?
    • Can Qhull produce a triangular mesh for an object?
    • For 3-d Delaunay triangulations, how do I report the triangles of each tetrahedron?
    • How do I construct a 3-d Delaunay triangulation?
    • How do I get the triangles for a 2-d Delaunay triangulation and the vertices of its Voronoi diagram?
    • Can Qhull triangulate a hundred 16-d points?
  • Voronoi diagram questions
    • See also "Delaunay diagram questions". Qhull computes the Voronoi diagram from the Delaunay triagulation.
    • How do I compute the volume of a Voronoi region?
    • How do I get the radii of the empty spheres for each Voronoi vertex?
    • What is the Voronoi diagram of a square?
    • How do I construct the Voronoi diagram of cospherical points?
    • Can Qhull compute the unbounded rays of the Voronoi diagram?
  • Approximation questions
    • How do I approximate data with a simplex?
  • Halfspace questions
    • How do I compute the intersection of halfspaces with Qhull?
  • Qhull library questions
    • Is Qhull available for Mathematica, Matlab, or Maple?
    • Why are there too few ridges?
    • Can Qhull use coordinates without placing them in a data file?
    • How large are Qhull's data structures?
    • Can Qhull construct convex hulls and Delaunay triangulations one point at a time?
    • How do I visit the ridges of a Delaunay triangulation?
    • How do I visit the Delaunay facets?
    • When is a point outside or inside a facet?
    • How do I find the facet that is closest to a point?
    • How do I find the Delaunay triangle or Voronoi region that is closest to a point?
    • How do I list the vertices?
    • How do I test code that uses the Qhull library?
    • When I compute a plane equation from a facet, I sometimes get an outward-pointing normal and sometimes an inward-pointing normal

»Startup questions

»How do I run Qhull from Windows?

Qhull is a console program. You will first need a command window (i.e., a "command prompt"). You can double click on 'eg\Qhull-go.bat'.

  • Type 'qconvex', 'qdelaunay', 'qhalf', 'qvoronoi, 'qhull', and 'rbox' for a synopsis of each program.
  • Type 'rbox c D2 | qconvex s i' to compute the convex hull of a square.
  • Type 'rbox c D2 | qconvex s i TO results.txt' to write the results to the file 'results.txt'. A summary is still printed on the the console.
  • Type 'rbox c D2' to see the input format for qconvex.
  • Type 'qconvex < data.txt s i TO results.txt' to read input data from 'data.txt'.
  • If you want to enter data by hand, type 'qconvex s i TO results.txt' to read input data from the console. Type in the numbers and end with a ctrl-D.

»How do I enter points for Qhull?

Qhull takes its data from standard input. For example, create a file named 'data.txt' with the following contents:

2  #sample 2-d input
5  #number of points
1 2  #coordinates of points
-1.1 3
3 2.2
4 5
-10 -10

Then call qconvex with 'qconvex < data.txt'. It will print a summary of the convex hull. Use 'qconvex < data.txt o' to print the vertices and edges. See also input format.

You can generate sample data with rbox, e.g., 'rbox 10' generates 10 random points in 3-d. Use a pipe ('|') to run rbox and qhull together, e.g.,

rbox c | qconvex o

computes the convex hull of a cube.

»How do I learn to use Qhull?

First read:

Look at Qhull's on-line documentation:

  • 'qconvex' gives a synopsis of qconvex and its options
  • 'rbox' lists all of the options for generating point sets
  • 'qconvex - | more' lists the options for qconvex
  • 'qconvex .' gives a concise list of options
  • 'qdelaunay', 'qhalf', 'qvoronoi', and 'qhull' also have a synopsis and option list

Then try out the Qhull programs on small examples.

  • 'rbox c' lists the vertices of a cube
  • 'rbox c | qconvex' is the convex hull of a cube
  • 'rbox c | qconvex o' lists the vertices and facets of a cube
  • 'rbox c | qconvex Qt o' triangulates the cube
  • 'rbox c | qconvex QJ o' joggles the input and triangulates the cube
  • 'rbox c D2 | qconvex' generates the convex hull of a square
  • 'rbox c D4 | qconvex' generates the convex hull of a hypercube
  • 'rbox 6 s D2 | qconvex p Fx' lists 6 random points in a circle and lists the vertices of their convex hull in order
  • 'rbox c D2 c G2 | qdelaunay' computes the Delaunay triangulation of two embedded squares. It merges the cospherical facets.
  • 'rbox c D2 c G2 | qdelaunay Qt' computes the Delaunay triangulation of two embedded squares. It triangulates the cospherical facets.
  • 'rbox c D2 c G2 | qvoronoi o' computes the corresponding Voronoi vertices and regions.
  • 'rbox c D2 c G2 | qvoronio Fv' shows the Voronoi diagram for the previous example. Each line is one edge of the diagram. The first number is 4, the next two numbers list a pair of input sites, and the last two numbers list the corresponding pair of Voronoi vertices.

Install Geomview if you are running SGI Irix, Solaris, SunOS, Linux, HP, IBM RS/6000, DEC Alpha, or Next. You can then visualize the output of Qhull. Qhull comes with Geomview examples.

Then try Qhull with a small example of your application. Work out the results by hand. Then experiment with Qhull's options to find the ones that you need.

You will need to decide how Qhull should handle precision problems. It can triangulate the output ('Qt'), joggle the input ('QJ'), or merge facets (the default).

  • With joggle, Qhull produces simplicial (i.e., triangular) output by joggling the input. After joggle, no points are cocircular or cospherical.
  • With facet merging, Qhull produces a better approximation and does not modify the input.
  • With triangulated output, Qhull merges facets and triangulates the result.
  • See Merged facets or joggled input.

»Convex hull questions

»How do I report just the area and volume of a convex hull?

Use option 'FS'. For example,
C:\qhull>rbox 10 | qconvex FS
0
2 2.192915621644613 0.2027867899638665

C:\qhull>rbox 10 | qconvex FA

Convex hull of 10 points in 3-d:

  Number of vertices: 10
  Number of facets: 16

Statistics for: RBOX 10 | QCONVEX FA

  Number of points processed: 10
  Number of hyperplanes created: 28
  Number of distance tests for qhull: 44
  CPU seconds to compute hull (after input):  0
  Total facet area:   2.1929156
  Total volume:       0.20278679

»Why are there extra points in a 4-d or higher convex hull?

You may see extra points if you use options 'i' or 'Ft' without using triangulated output ('Qt'). The extra points occur when a facet is non-simplicial (i.e., a facet with more than d vertices). For example, Qhull reports the following for one facet of the convex hull of a hypercube. Option 'Pd0:0.5' returns the facet along the positive-x axis:

rbox c D4 | qconvex i Pd0:0.5
12
17 13 14 15
17 13 12 14
17 11 13 15
17 14 11 15
17 10 11 14
17 14 12 8
17 12 13 8
17 10 14 8
17 11 10 8
17 13 9 8
17 9 11 8
17 11 9 13

The 4-d hypercube has 16 vertices; so point "17" was added by qconvex. Qhull adds the point in order to report a simplicial decomposition of the facet. The point corresponds to the "centrum" which Qhull computes to test for convexity.

Triangulate the output ('Qt') to avoid the extra points. Since the hypercube is 4-d, each simplicial facet is a tetrahedron.

C:\qhull3.1>rbox c D4 | qconvex i Pd0:0.5 Qt
9
9 13 14 15
12 9 13 14
9 11 13 15
11 9 14 15
9 10 11 14
12 9 14 8
9 12 13 8
9 10 14 8
10 9 11 8

Use the 'Fv' option to print the vertices of simplicial and non-simplicial facets. For example, here is the same hypercube facet with option 'Fv' instead of 'i':

C:\qhull>rbox c D4 | qconvex Pd0:0.5 Fv
1
8 9 10 12 11 13 14 15 8

The coordinates of the extra point are printed with the 'Ft' option.

rbox c D4 | qconvex Pd0:0.5 Ft
4
17 12 3
  -0.5   -0.5   -0.5   -0.5
  -0.5   -0.5   -0.5    0.5
  -0.5   -0.5    0.5   -0.5
  -0.5   -0.5    0.5    0.5
  -0.5    0.5   -0.5   -0.5
  -0.5    0.5   -0.5    0.5
  -0.5    0.5    0.5   -0.5
  -0.5    0.5    0.5    0.5
   0.5   -0.5   -0.5   -0.5
   0.5   -0.5   -0.5    0.5
   0.5   -0.5    0.5   -0.5
   0.5   -0.5    0.5    0.5
   0.5    0.5   -0.5   -0.5
   0.5    0.5   -0.5    0.5
   0.5    0.5    0.5   -0.5
   0.5    0.5    0.5    0.5
   0.5      0      0      0
4 16 13 14 15
4 16 13 12 14
4 16 11 13 15
4 16 14 11 15
4 16 10 11 14
4 16 14 12 8
4 16 12 13 8
4 16 10 14 8
4 16 11 10 8
4 16 13 9 8
4 16 9 11 8
4 16 11 9 13

»How do I report duplicate vertices?

There's no direct way. You can use option 'FP' to report the distance to the nearest vertex for coplanar input points. Select the minimum distance for a duplicated vertex, and locate all input sites less than this distance.

For Delaunay triangulations, all coplanar points are nearly incident to a vertex. If you want a report of coincident input sites, do not use option 'QJ'. By adding a small random quantity to each input coordinate, it prevents coincident input sites.

»Delaunay triangulation questions

»How do I get rid of nearly flat Delaunay triangles?

Nearly flat triangles occur when boundary points are nearly collinear or coplanar. They also occur for nearly coincident points. Both events can easily occur when using joggle. For example (rbox 10 W0 D2 | qdelaunay QJ Fa) lists the areas of the Delaunay triangles of 10 points on the boundary of a square. Some of these triangles are nearly flat. This occurs when one point is joggled inside of two other points. In this case, nearly flat triangles do not occur with triangulated output (rbox 10 W0 D2 | qdelaunay Qt Fa).

Another example, (rbox c P0 P0 D2 | qdelaunay QJ Fa), computes the areas of the Delaunay triangles for the unit square and two instances of the origin. Four of the triangles have an area of 0.25 while two have an area of 2.0e-11. The later are due to the duplicated origin. With triangulated output (rbox c P0 P0 D2 | qdelaunay Qt Fa) there are four triangles of equal area.

Nearly flat triangles also occur without using joggle. For example, (rbox c P0 P0,0.4999999999 | qdelaunay Fa), computes the areas of the Delaunay triangles for the unit square, a nearly collinear point, and the origin. One triangle has an area of 3.3e-11.

Unfortunately, none of Qhull's merging options remove nearly flat Delaunay triangles due to nearly collinear or coplanar boundary points. The merging options concern the empty circumsphere property of Delaunay triangles. This is independent of the area of the Delaunay triangles. Qhull does handle nearly coincident points.

If you are calling Qhull from a program, you can merge slivers into an adjacent facet. In d dimensions with simplicial facets (e.g., from 'Qt'), each facet has d+1 neighbors. Each neighbor shares d vertices of the facet's d+1 vertices. Let the other vertex be the opposite vertex. For each neighboring facet, if its circumsphere includes the opposite.vertex, the two facets can be merged. [M. Treacy]

You can handle collinear or coplanar boundary points by enclosing the points in a box. For example, (rbox c P0 P0,0.4999999999 c G1 | qdelaunay Fa), surrounds the previous points with [(1,1), (1,-1), (-1,-1), (-1, 1)]. Its Delaunay triangulation does not include a nearly flat triangle. The box also simplifies the graphical output from Qhull.

Without joggle, Qhull lists coincident points as "coplanar" points. For example, (rbox c P0 P0 D2 | qdelaunay Fa), ignores the duplicated origin and lists four triangles of size 0.25. Use 'Fc' to list the coincident points (e.g., rbox c P0 P0 D2 | qdelaunay Fc).

There is no easy way to determine coincident points with joggle. Joggle removes all coincident, cocircular, and cospherical points before running Qhull. Instead use facet merging (the default) or triangulated output ('Qt').

»How do I compute the Delaunay triangulation of a non-convex object?

A similar question is "How do I mesh a volume from a set of triangulated surface points?"

This is an instance of the constrained Delaunay Triangulation problem. Qhull does not handle constraints. The boundary of the Delaunay triangulation is always convex. But if the input set contains enough points, the triangulation will include the boundary. The number of points needed depends on the input.

Shewchuk has developed a theory of constrained Delaunay triangulations. See his paper at the 1998 Computational Geometry Conference. Using these ideas, constraints could be added to Qhull. They would have many applications.

There is a large literature on mesh generation and many commercial offerings. For pointers see Owen's International Meshing Roundtable and Schneiders' Finite Element Mesh Generation page.

»Can Qhull produce a triangular mesh for an object?

Yes for convex objects, no for non-convex objects. For non-convex objects, it triangulates the concavities. Unless the object has many points on its surface, triangles may cross the surface.

»For 3-d Delaunay triangulations, how do I report the triangles of each tetrahedron?

For points in general position, a 3-d Delaunay triangulation generates tetrahedron. Each face of a tetrahedron is a triangle. For example, the 3-d Delaunay triangulation of random points on the surface of a cube, is a cellular structure of tetrahedron.

Use triangulated output ('qdelaunay Qt i') or joggled input ('qdelaunay QJ i') to generate the Delaunay triangulation. Option 'i' reports each tetrahedron. The triangles are every combination of 3 vertices. Each triangle is a "ridge" of the Delaunay triangulation.

For example,

        rbox 10 | qdelaunay Qt i
        14
        9 5 8 7
        0 9 8 7
        5 3 8 7
        3 0 8 7
        5 4 8 1
        4 6 8 1
        2 9 5 8
        4 2 5 8
        4 2 9 5
        6 2 4 8
        9 2 0 8
        2 6 0 8
        2 4 9 1
        2 6 4 1

is the Delaunay triangulation of 10 random points. Ridge 9-5-8 occurs twice. Once for tetrahedron 9 5 8 7 and the other for tetrahedron 2 9 5 8.

You can also use the Qhull library to generate the triangles. See "How do I visit the ridges of a Delaunay triangulation?"

»How do I construct a 3-d Delaunay triangulation?

For 3-d Delaunay triangulations with cospherical input sites, use triangulated output ('Qt') or joggled input ('QJ'). Otherwise option 'i' will triangulate non-simplicial facets by adding a point to the facet.

If you want non-simplicial output for cospherical sites, use option 'Fv' or 'o'. For option 'o', ignore the last coordinate. It is the lifted coordinate for the corresponding convex hull in 4-d.

The following example is a cube inside a tetrahedron. The 8-vertex facet is the cube. Ignore the last coordinates.

C:\qhull>rbox r y c G0.1 | qdelaunay Fv
4
12 20 44
   0.5      0      0 0.3055555555555555
   0    0.5      0 0.3055555555555555
   0      0    0.5 0.3055555555555555
  -0.5   -0.5   -0.5 0.9999999999999999
  -0.1   -0.1   -0.1 -6.938893903907228e-018
  -0.1   -0.1    0.1 -6.938893903907228e-018
  -0.1    0.1   -0.1 -6.938893903907228e-018
  -0.1    0.1    0.1 -6.938893903907228e-018
   0.1   -0.1   -0.1 -6.938893903907228e-018
   0.1   -0.1    0.1 -6.938893903907228e-018
   0.1    0.1   -0.1 -6.938893903907228e-018
   0.1    0.1    0.1 -6.938893903907228e-018
4 2 11 1 0
4 10 1 0 3
4 11 10 1 0
4 2 9 0 3
4 9 11 2 0
4 7 2 1 3
4 11 7 2 1
4 8 10 0 3
4 9 8 0 3
5 8 9 10 11 0
4 10 6 1 3
4 6 7 1 3
5 6 8 10 4 3
5 6 7 10 11 1
4 5 9 2 3
4 7 5 2 3
5 5 8 9 4 3
5 5 6 7 4 3
8 5 6 8 7 9 10 11 4
5 5 7 9 11 2

If you want simplicial output use options 'Qt i' or 'QJ i', e.g.,

rbox r y c G0.1 | qdelaunay Qt i
31
2 11 1 0
11 10 1 0
9 11 2 0
11 7 2 1
8 10 0 3
9 8 0 3
10 6 1 3
6 7 1 3
5 9 2 3
7 5 2 3
9 8 10 11
8 10 11 0
9 8 11 0
6 8 10 4
8 6 10 3
6 8 4 3
6 7 10 11
10 6 11 1
6 7 11 1
8 5 4 3
5 8 9 3
5 6 4 3
6 5 7 3
5 9 10 11
8 5 9 10
7 5 10 11
5 6 7 10
8 5 10 4
5 6 10 4
5 9 11 2
7 5 11 2

»How do I get the triangles for a 2-d Delaunay triangulation and the vertices of its Voronoi diagram?

To compute the Delaunay triangles indexed by the indices of the input sites, use

rbox 10 D2 | qdelaunay Qt i

To compute the Voronoi vertices and the Voronoi region for each input site, use

rbox 10 D2 | qvoronoi o

To compute each edge ("ridge") of the Voronoi diagram for each pair of adjacent input sites, use

rbox 10 D2 | qvoronoi Fv

To compute the area and volume of the Voronoi region for input site 5 (site 0 is the first one), use

rbox 10 D2 | qvoronoi QV5 p | qconvex s FS

To compute the lines ("hyperplanes") that define the Voronoi region for input site 5, use

rbox 10 D2 | qvoronoi QV5 p | qconvex n

or

rbox 10 D2 | qvoronoi QV5 Fi Fo

To list the extreme points of the input sites use

rbox 10 D2 | qdelaunay Fx

You will get the same point ids with

rbox 10 D2 | qconvex Fx

»Can Qhull triangulate a hundred 16-d points?

No. This is an immense structure. A triangulation of 19, 16-d points has 43 simplices. If you add one point at a time, the triangulation increased as follows: 43, 189, 523, 1289, 2830, 6071, 11410, 20487. The last triangulation for 26 points used 13 megabytes of memory. When Qhull uses virtual memory, it becomes too slow to use.

»Voronoi diagram questions

»How do I compute the volume of a Voronoi region?

For each Voronoi region, compute the convex hull of the region's Voronoi vertices. The volume of each convex hull is the volume of the corresponding Vornoi region.

For example, to compute the volume of the bounded Voronoi region about [0,0,0]: output the origin's Voronoi vertices and compute the volume of their convex hull. The last number from option 'FS' is the volume.

rbox P0 10 | qvoronoi QV0 p | qhull FS
0
2 1.448134756744281 0.1067973560800857

For another example, see How do I get the triangles for a 2-d Delaunay triangulation and the vertices of its Voronoi diagram?

This approach is slow if you are using the command line. A faster approcach is to call Qhull from a program. The fastest method is Clarkson's hull program. It computes the volume for all Voronoi regions.

An unbounded Voronoi region does not have a volume.

»How do I get the radii of the empty spheres for each Voronoi vertex?

Use option 'Fi' to list each bisector (i.e. Delaunay ridge). Then compute the minimum distance for each Voronoi vertex.

There's other ways to get the same information. Let me know if you find a better method.

»What is the Voronoi diagram of a square?

Consider a square,

C:\qhull>rbox c D2
2 RBOX c D2
4
  -0.5   -0.5
  -0.5    0.5
   0.5   -0.5
   0.5    0.5

There's two ways to compute the Voronoi diagram: with facet merging or with joggle. With facet merging, the result is:

C:\qhull>rbox c D2 | qvoronoi Qz

Voronoi diagram by the convex hull of 5 points in 3-d:

  Number of Voronoi regions and at-infinity: 5
  Number of Voronoi vertices: 1
  Number of facets in hull: 5

Statistics for: RBOX c D2 | QVORONOI Qz

  Number of points processed: 5
  Number of hyperplanes created: 7
  Number of distance tests for qhull: 8
  Number of merged facets: 1
  Number of distance tests for merging: 29
  CPU seconds to compute hull (after input):  0

C:\qhull>rbox c D2 | qvoronoi Qz o
2
2 5 1
-10.101 -10.101
     0      0
2 0 1
2 0 1
2 0 1
2 0 1
0

C:\qhull>rbox c D2 | qvoronoi Qz Fv
4
4 0 1 0 1
4 0 2 0 1
4 1 3 0 1
4 2 3 0 1

There is one Voronoi vertex at the origin and rays from the origin along each of the coordinate axes. The last line '4 2 3 0 1' means that there is a ray that bisects input points #2 and #3 from infinity (vertex 0) to the origin (vertex 1). Option 'Qz' adds an artificial point since the input is cocircular. Coordinates -10.101 indicate the vertex at infinity.

With triangulated output, the Voronoi vertex is duplicated:

C:\qhull3.1>rbox c D2 | qvoronoi Qt Qz

Voronoi diagram by the convex hull of 5 points in 3-d:

  Number of Voronoi regions and at-infinity: 5
  Number of Voronoi vertices: 2
  Number of triangulated facets: 1

Statistics for: RBOX c D2 | QVORONOI Qt Qz

  Number of points processed: 5
  Number of hyperplanes created: 7
  Number of facets in hull: 6
  Number of distance tests for qhull: 8
  Number of distance tests for merging: 33
  Number of distance tests for checking: 30
  Number of merged facets: 1
  CPU seconds to compute hull (after input): 0.05

C:\qhull3.1>rbox c D2 | qvoronoi Qt Qz o
2
3 5 1
-10.101 -10.101
     0      0
     0      0
3 2 0 1
2 1 0
2 2 0
3 2 0 1
0

C:\qhull3.1>rbox c D2 | qvoronoi Qt Qz Fv
4
4 0 2 0 2
4 0 1 0 1
4 1 3 0 1
4 2 3 0 2

With joggle, the input is no longer cocircular and the Voronoi vertex is split into two:

C:\qhull>rbox c D2 | qvoronoi Qt Qz

C:\qhull>rbox c D2 | qvoronoi QJ o
2
3 4 1
-10.101 -10.101
-4.71511718558304e-012 -1.775812830118184e-011
9.020340030474472e-012 -4.02267108512433e-012
2 0 1
3 2 1 0
3 2 0 1
2 2 0

C:\qhull>rbox c D2 | qvoronoi QJ Fv
5
4 0 2 0 1
4 0 1 0 1
4 1 2 1 2
4 1 3 0 2
4 2 3 0 2

Note that the Voronoi diagram includes the same rays as before plus a short edge between the two vertices.

»How do I construct the Voronoi diagram of cospherical points?

Three-d terrain data can be approximated with cospherical points. The Delaunay triangulation of cospherical points is the same as their convex hull. If the points lie on the unit sphere, the facet normals are the Voronoi vertices [via S. Fortune].

For example, consider the points {[1,0,0], [-1,0,0], [0,1,0], ...}. Their convex hull is:

rbox d G1 | qconvex o
3
6 8 12
     0      0     -1
     0      0      1
     0     -1      0
     0      1      0
    -1      0      0
     1      0      0
3 3 1 4
3 1 3 5
3 0 3 4
3 3 0 5
3 2 1 5
3 1 2 4
3 2 0 4
3 0 2 5

The facet normals are:

rbox d G1 | qconvex n
4
8
-0.5773502691896258  0.5773502691896258  0.5773502691896258 -0.5773502691896258
 0.5773502691896258  0.5773502691896258  0.5773502691896258 -0.5773502691896258
-0.5773502691896258  0.5773502691896258 -0.5773502691896258 -0.5773502691896258
 0.5773502691896258  0.5773502691896258 -0.5773502691896258 -0.5773502691896258
 0.5773502691896258 -0.5773502691896258  0.5773502691896258 -0.5773502691896258
-0.5773502691896258 -0.5773502691896258  0.5773502691896258 -0.5773502691896258
-0.5773502691896258 -0.5773502691896258 -0.5773502691896258 -0.5773502691896258
 0.5773502691896258 -0.5773502691896258 -0.5773502691896258 -0.5773502691896258

If you drop the offset from each line (the last number), each line is the Voronoi vertex for the corresponding facet. The neighboring facets for each point define the Voronoi region for each point. For example:

rbox d G1 | qconvex FN
6
4 7 3 2 6
4 5 0 1 4
4 7 4 5 6
4 3 1 0 2
4 6 2 0 5
4 7 3 1 4

The Voronoi vertices {7, 3, 2, 6} define the Voronoi region for point 0. Point 0 is [0,0,-1]. Its Voronoi vertices are

-0.5773502691896258  0.5773502691896258 -0.5773502691896258
 0.5773502691896258  0.5773502691896258 -0.5773502691896258
-0.5773502691896258 -0.5773502691896258 -0.5773502691896258
 0.5773502691896258 -0.5773502691896258 -0.5773502691896258

In this case, the Voronoi vertices are oriented, but in general they are unordered.

By taking the dual of the Delaunay triangulation, you can construct the Voronoi diagram. For cospherical points, the convex hull vertices for each facet, define the input sites for each Voronoi vertex. In 3-d, the input sites are oriented. For example:

rbox d G1 | qconvex i
8
3 1 4
1 3 5
0 3 4
3 0 5
2 1 5
1 2 4
2 0 4
0 2 5

The convex hull vertices for facet 0 are {3, 1, 4}. So Voronoi vertex 0 (i.e., [-0.577, 0.577, 0.577]) is the Voronoi vertex for input sites {3, 1, 4} (i.e., {[0,1,0], [0,0,1], [-1,0,0]}).

»Can Qhull compute the unbounded rays of the Voronoi diagram?

Use 'Fo' to compute the separating hyperplanes for unbounded Voronoi regions. The corresponding ray goes to infinity from the Voronoi vertices. If you enclose the input sites in a large enough box, the outermost bounded regions will represent the unbounded regions of the original points.

If you do not box the input sites, you can identify the unbounded regions. They list '0' as a vertex. Vertex 0 represents "infinity". Each unbounded ray includes vertex 0 in option 'Fv. See Voronoi graphics and Voronoi notes.

»Approximation questions

»How do I approximate data with a simplex

Qhull may be used to help select a simplex that approximates a data set. It will take experimentation. Geomview will help to visualize the results. This task may be difficult to do in 5-d and higher. Use rbox options 'x' and 'y' to produce random distributions within a simplex. Your methods work if you can recover the simplex.

Use Qhull's precision options to get a first approximation to the hull, say with 10 to 50 facets. For example, try 'C0.05' to remove small facets after constructing the hull. Use 'W0.05' to ignore points within 0.05 of a facet. Use 'PA5' to print the five largest facets by area.

Then use other methods to fit a simplex to this data. Remove outlying vertices with few nearby points. Look for large facets in different quadrants. You can use option 'Pd0d1d2' to print all the facets in a quadrant.

In 4-d and higher, use the outer planes (option 'Fo' or 'facet->maxoutside') since the hyperplane of an approximate facet may be below many of the input points.

For example, consider fitting a cube to 1000 uniformly random points in the unit cube. In this case, the first try was good:

rbox 1000 | qconvex W0.05 C0.05 PA6 Fo
4
6
0.35715408374381 0.08706467018177928 -0.9299788727015564 -0.5985514741284483
0.995841591359023 -0.02512604712761577 0.08756829720435189 -0.5258834069202866
0.02448099521570909 -0.02685210459017302 0.9993396046151313 -0.5158104982631999
-0.9990223929415094 -0.01261133513150079 0.04236994958247349 -0.509218270408407
-0.0128069014364698 -0.9998380680115362 0.01264203427283151 -0.5002512653670584
0.01120895057872914 0.01803671994177704 -0.9997744926535512 -0.5056824072956361

»Halfspace questions

»How do I compute the intersection of halfspaces with Qhull?

Qhull computes the halfspace intersection about a point. The point must be inside all of the halfspaces. Given a point, a duality turns a halfspace intersection problem into a convex hull problem.

Use linear programming if you do not know a point in the interior of the halfspaces. See the notes for qhalf. You will need a linear programming code. This may require a fair amount of work to implement.

»Qhull library questions

»Is Qhull available for Mathematica, Matlab, or Maple?

MATLAB

Z. You of MathWorks added qhull to MATLAB 6. See functions convhulln, delaunayn, griddata3, griddatan, tsearch, tsearchn, and voronoin. V. Brumberg update MATLAB R14 for Qhull 2003.1 and triangulated output.

Engwirda wrote mesh2d for unstructured mesh generation in MATLAB. It is based on the iterative method of Persson and generally results in better quality meshes than delaunay refinement.

Mathematica and Maple

See qh-math for a Delaunay interface to Mathematica. It includes projects for CodeWarrior on the Macintosh and Visual C++ on Win32 PCs.

See Mathematica ('m') and Maple ('FM') output options.

»Why are there too few ridges?

The following sample code may produce fewer ridges than expected:
  facetT *facetp;
  ridgeT *ridge, **ridgep;

  FORALLfacets {
    printf("facet f%d\n", facet->id);
    FOREACHridge_(facet->ridges) {
      printf("   ridge r%d between f%d and f%d\n", ridge->id, ridge->top->id, ridge->bottom->id);
    }
  }

Qhull does not create ridges for simplicial facets. Instead it computes ridges from facet->neighbors. To make ridges for a simplicial facet, use qh_makeridges() in merge.c. Usefacet->visit_id to visit each ridge once (instead of twice). For example,

  facetT *facet, *neighbor;
  ridgeT *ridge, **ridgep;

  qh visit_id++;
  FORALLfacets {
    printf("facet f%d\n", facet->id);
    qh_makeridges(facet);
    facet->visitId= qh visit_id;
    FOREACHridge_(facet->ridges) {
        neighbor= otherfacet_(ridge, visible);
        if (neighbor->visitid != qh visit_id)
            printf("   ridge r%d between f%d and f%d\n", ridge->id, ridge->top->id, ridge->bottom->id);
    }
  }

»Can Qhull use coordinates without placing them in a data file?

You may call Qhull from a program. Please use the reentrant Qhull library (libqhullstatic_r.a, libqhull_r.so, or qhull_r.dll). See user_eg.c and "Qhull-template" in user_r.c for examples.. See Qhull internals for an introduction to Qhull's reentrant library and its C++ interface.

Hint: Start with a small example for which you know the answer.

»How large are Qhull's data structures?

Qhull uses a general-dimension data structure. The size depends on the dimension. Use option 'Ts' to print out the memory statistics [e.g., 'rbox D2 10 | qconvex Ts'].

»Can Qhull construct convex hulls and Delaunay triangulations one point at a time?

The Qhull library may be used to construct convex hulls and Delaunay triangulations one point at a time. It may not be used for deleting points or moving points.

Qhull is designed for batch processing. Neither Clarkson's randomized incremental algorithm nor Qhull are designed for on-line operation. For many applications, it is better to reconstruct the convex hull or Delaunay triangulation from scratch for each new point.

With random point sets and on-line processing, Clarkson's algorithm should run faster than Qhull. Clarkson uses the intermediate facets to reject new, interior points, while Qhull, when used on-line, visits every facet to reject such points. If used on-line for n points, Clarkson may take O(n) times as much memory as the average off-line case, while Qhull's space requirement does not change.

If you triangulate the output before adding all the points (option 'Qt' and procedure qh_triangulate), you must set option 'Q11'. It duplicates the normals of triangulated facets and recomputes the centrums. This should be avoided for regular use since triangulated facets are not clearly convex with their neighbors. It appears to work most of the time, but fails for cases that Qhull normally handles well [see the test call to qh_triangulate in qh_addpoint].

»How do I visit the ridges of a Delaunay triangulation?

To visit the ridges of a Delaunay triangulation, visit each facet. Each ridge will appear twice since it belongs to two facets. In pseudo-code:

    for each facet of the triangulation
        if the facet is Delaunay (i.e., part of the lower convex hull)
            for each ridge of the facet
                if the ridge's neighboring facet has not been visited
                    ... process a ridge of the Delaunay triangulation ...

In undebugged, C code:

    qh visit_id++;
    FORALLfacets_(facetlist)
        if (!facet->upperdelaunay) {
            facet->visitid= qh visit_id;
            qh_makeridges(facet);
            FOREACHridge_(facet->ridges) {
                neighbor= otherfacet_(ridge, facet);
                if (neighbor->visitid != qh visit_id) {
                    /* Print ridge here with facet-id and neighbor-id */
                    /*fprintf(fp, "f%d\tf%d\t",facet->id,neighbor->ID);*/
                    FOREACHvertex_(ridge->vertices)
                        fprintf(fp,"%d ",qh_pointid (vertex->point) );
                    qh_printfacetNvertex_simplicial (fp, facet, format);
                    fprintf(fp," ");
                    if(neighbor->upperdelaunay)
                        fprintf(fp," -1 -1 -1 -1 ");
                    else
                        qh_printfacetNvertex_simplicial (fp, neighbor, format);
                    fprintf(fp,"\n");
                }
            }
        }
    }

Qhull should be redesigned as a class library, or at least as an API. It currently provides everything needed, but the programmer has to do a lot of work. Hopefully someone will write C++ wrapper classes or a Python module for Qhull.

»How do I visit the Delaunay regions?

Qhull constructs a Delaunay triangulation by lifting the input sites to a paraboloid. The Delaunay triangulation corresponds to the lower convex hull of the lifted points. To visit each facet of the lower convex hull, use:

    facetT *facet;

    ...
    FORALLfacets {
        if (!facet->upperdelaunay) {
            ... only facets for Delaunay regions ...
        }
    }

»When is a point outside or inside a facet?

A point is outside of a facet if it is clearly outside the facet's outer plane. The outer plane is defined by an offset (facet->maxoutside) from the facet's hyperplane.

    facetT *facet;
    pointT *point;
    realT dist;

    ...
    qh_distplane(point, facet, &dist);
    if (dist > facet->maxoutside + 2 * qh DISTround) {
        /* point is clearly outside of facet */
    }

A point is inside of a facet if it is clearly inside the facet's inner plane. The inner plane is computed as the maximum distance of a vertex to the facet. It may be computed for an individual facet, or you may use the maximum over all facets. For example:

    facetT *facet;
    pointT *point;
    realT dist;

    ...
    qh_distplane(point, facet, &dist);
    if (dist < qh min_vertex - 2 * qh DISTround) {
        /* point is clearly inside of facet */
    }

Both tests include two qh.DISTrounds because the computation of the furthest point from a facet may be off by qh.DISTround and the computation of the current distance to the facet may be off by qh.DISTround.

»How do I find the facet that is closest to a point?

Use qh_findbestfacet(). For example,

    coordT point[ DIM ];
    boolT isoutside;
    realT bestdist;
    facetT *facet;

    ... set coordinates for point ...

    facet= qh_findbestfacet (point, qh_ALL, &bestdist, &isoutside);

    /* 'facet' is the closest facet to 'point' */

qh_findbestfacet() performs a directed search for the facet furthest below the point. If the point lies inside this facet, qh_findbestfacet() performs an exhaustive search of all facets. An exhaustive search may be needed because a facet on the far side of a lens-shaped distribution may be closer to a point than all of the facet's neighbors. The exhaustive search may be skipped for spherical distributions.

Also see, "How do I find the Delaunay triangle that is closest to a point?"

»How do I find the Delaunay triangle or Voronoi region that is closest to a point?

A Delaunay triangulation subdivides the plane, or in general dimension, subdivides space. Given a point, how do you determine the subdivision containing the point? Or, given a set of points, how do you determine the subdivision containing each point of the set? Efficiency is important -- an exhaustive search of the subdivision is too slow.

First compute the Delaunay triangle with qh_new_qhull() in user_r.c or Qhull::runQhull(). Lift the point to the paraboloid by summing the squares of the coordinates. Use qh_findbestfacet() [poly2.c] to find the closest Delaunay triangle. Determine the closest vertex to find the corresponding Voronoi region. Do not use options 'Qbb', 'QbB', 'Qbk:n', or 'QBk:n' since these scale the last coordinate. Optimizations of qh_findbestfacet() should be possible for Delaunay triangulations.

You first need to lift the point to the paraboloid (i.e., the last coordinate is the sum of the squares of the point's coordinates). The routine, qh_setdelaunay() [geom2.c], lifts an array of points to the paraboloid. The following excerpt is from findclosest() in user_eg.c.

    coordT point[ DIM + 1];  /* one extra coordinate for lifting the point */
    boolT isoutside;
    realT bestdist;
    facetT *facet;

    ... set coordinates for point[] ...

    qh_setdelaunay (DIM+1, 1, point);
    facet= qh_findbestfacet (point, qh_ALL, &bestdist, &isoutside);
    /* 'facet' is the closest Delaunay triangle to 'point' */

The returned facet either contains the point or it is the closest Delaunay triangle along the convex hull of the input set.

Point location is an active research area in Computational Geometry. For a practical approach, see Mucke, et al, "Fast randomized point location without preprocessing in two- and three-dimensional Delaunay triangulations," Computational Geometry '96, p. 274-283, May 1996. For an introduction to planar point location see [O'Rourke '93]. Also see, "How do I find the facet that is closest to a point?"

To locate the closest Voronoi region, determine the closest vertex of the closest Delaunay triangle.

    realT dist, bestdist= REALmax;
        vertexT *bestvertex= NULL, *vertex, **vertexp;

    /* 'facet' is the closest Delaunay triangle to 'point' */

    FOREACHvertex_( facet->vertices ) {
        dist= qh_pointdist( point, vertex->point, DIM );
        if (dist < bestdist) {
            bestdist= dist;
            bestvertex= vertex;
        }
    }
    /* 'bestvertex' represents the Voronoi region closest to 'point'.  The corresponding
       input site is 'bestvertex->point' */

»How do I list the vertices?

To list the vertices (i.e., extreme points) of the convex hull use

    vertexT *vertex;

    FORALLvertices {
      ...
      // vertex->point is the coordinates of the vertex
      // qh_pointid(vertex->point) is the point ID of the vertex
      ...
    }
    

»How do I test code that uses the Qhull library?

Compare the output from your program with the output from the Qhull program. Use option 'T1' or 'T4' to trace what Qhull is doing. Prepare a small example for which you know the output. Run the example through the Qhull program and your code. Compare the trace outputs. If you do everything right, the two trace outputs should be almost the same. The trace output will also guide you to the functions that you need to review.

»When I compute a plane equation from a facet, I sometimes get an outward-pointing normal and sometimes an inward-pointing normal

Qhull orients simplicial facets, and prints oriented output for 'i', 'Ft', and other options. The orientation depends on both the vertex order and the flag facet->toporient.

Qhull does not orient non-simplicial facets. Instead it orients the facet's ridges. These are printed with the 'Qt' and 'Ft' option. The facet's hyperplane is oriented.


Up: Home page for Qhull
Up: Qhull manual: Table of Contents
To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
To: FAQ: Table of Contents


The Geometry Center Home Page

Comments to: qhull@qhull.org
Created: Sept. 25, 1995 --- Last modified: see top

geometry/vignettes/qhull/html/qh--half.gif0000644000176200001440000000475113431000556020276 0ustar liggesusersGIF87add.=-/HO%#-ZdbQ?qR0^S;D6gE2hTN;BGGPZLL9 \%+'/<>lN..hPHyJJHFNOJ91BW.\pX`NNOIGUM8No44II&B;X[@@XC2[:8']eqH8LPY\;.X_l)TKKeo|$.%HJO}84@CSSSQQQYkMMMkjRcmO1WgLKLSHIidEMOg'h7/BBfpR0>>7JJ7:0IBd48RW43#Zk$5'-19D`'=AgK46OS*-u&LHHGF?Og;4b9UYWKzvZ~EEQTeFG}FHH362-<,SXBBo.3#rS1HUXO?=JVVYA,Z*6,`@)MM`T@EBaAOS<=7-0[[\-g+.{JJ7YRSSG#Y:VY=8NOOfK?KMLn9rWN:i-FF|yNK/=-OV] (!Ie^:tQJ\ (Q-P/\DueHb1ɓ<`Æ%kV/kbFط`:דZ4 WM2sˤ+&'aDHA/ 7Lgs bKBxn=S~%ܭ~Uu/bDC[eрaQo"CNj' >Nk׮g GZ m OY6#=M˻Ф;X)b_XwM1pfaaّK;D~שbMl%:K< 5C5p|f.\̡rxf 8K=f _VR'Y@0mr )Fah6[#rB j5G1!!\q$,[G邗Q Qhull quick reference

Up: Home page for Qhull
Up: Qhull manual
To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
To: Qhull internals
To: Qhull functions, macros, and data structures
To: Qhull files
To: GeomGlobalIoMemMergePolyQhullSetStatUser


[cone] Qhull quick reference

This section lists all programs and options in Qhull.

Copyright © 1995-2018 C.B. Barber

 


Qhull programs

» ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions

qconvex -- convex hull
synopsis • input • outputs • controls • graphics • notes • conventions • options
 
qdelaunay -- Delaunay triangulation
synopsis • input • outputs • controls • graphics • notes • conventions • options
 
qdelaunay Qu -- furthest-site Delaunay triangulation
synopsis • input • outputs • controls • graphics • notes • conventions • options
 
qhalf -- halfspace intersection about a point
synopsis • input • outputs • controls • graphics • notes • conventions • options
 
qvoronoi -- Voronoi diagram
synopsis • input • outputs • controls • graphics • notes • conventions • options
 
qvoronoi Qu -- furthest-site Voronoi diagram
synopsis • input • outputs • controls • graphics • notes • conventions • options
 
rbox -- generate point distributions for qhull
synopsis • outputs • examples • notes • options
 
qhull -- convex hull and related structures
synopsis • input • outputs • controls • options
 
Qhull options

» ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions

'Fa' Farea 'FA' FArea-total 'Fc' Fcoplanars 'FC' FCentrums
'Fd' Fd-cdd-in 'FD' FD-cdd-out 'FF' FF-dump-xridge 'Fi' Finner
'Fi' Finner_bounded 'FI' FIDs 'Fm' Fmerges 'FM' FMaple
'Fn' Fneighbors 'FN' FNeigh-vertex 'Fo' Fouter 'Fo' Fouter_unbounded
'FO' FOptions 'Fp' Fpoint-intersect 'FP' FPoint_near 'FQ' FQhull
'Fs' Fsummary 'FS' FSize 'Ft' Ftriangles 'Fv' Fvertices
'Fv' Fvoronoi 'FV' FVertex-ave 'Fx' Fxtremes Merged facets or joggled input
 
'PAn' PArea-keep 'Pdk:n' Pdrop_low 'PDk:n' Pdrop_high 'Pg' Pgood
'PFn' PFacet_area_keep 'PG' PGood_neighbors 'PMn' PMerge-keep 'Po' Poutput_forced
'Po' Poutput_error 'Pp' Pprecision_not
 
'd' delaunay 'v' voronoi 'G' Geomview 'H' Halfspace
'f' facet_dump 'i' incidences 'm' mathematica 'n' normals
'o' OFF_format 'p' points 's' summary
 
'Gv' Gvertices 'Gp' Gpoints 'Ga' Gall_points 'Gn' Gno_planes
'Gi' Ginner 'Gc' Gcentrums 'Gh' Ghyperplanes 'Gr' Gridges
'Go' Gouter 'GDn' GDrop_dim 'Gt' Gtransparent
 
'T4' T4_trace 'Tc' Tcheck_often 'Ts' Tstatistics 'Tv' Tverify
'Tz' Tz_stdout 'TFn' TFacet_log 'TI file' TInput_file 'TPn' TPoint_trace
'TMn' TMerge_trace 'TO file' TOutput_file 'TRn' TRerun 'TWn' TWide_trace
'TV-n' TVertex_stop_before
'TVn' TVertex_stop_after 'TCn' TCone_stop_after
 
'A-n' Angle_max_pre 'An' Angle_max_post 'C-0' Centrum_roundoff 'C-n' Centrum_size_pre
'Cn' Centrum_size_post 'En' Error_round 'Rn' Random_dist 'Vn' Visible_min
'Un' Ucoplanar_max 'Wn' Wide_outside
 
'Qbk:n' Qbound_low 'QBk:n' QBound_high 'Qbk:0Bk:0' Qbound_drop 'QbB' QbB-scale-box
'Qbb' Qbb-scale-last 'Qc' Qcoplanar 'Qf' Qfurthest 'Qg' Qgood_only
'QGn' QGood_point 'Qi' Qinterior 'Qm' Qmax_out 'QJn' QJoggle
'Qr' Qrandom 'QRn' QRotate 'Qs' Qsearch_1st 'Qt' Qtriangulate
'Qu' QupperDelaunay 'QVn' QVertex_good 'Qv' Qvneighbors 'Qx' Qxact_merge
'Qz' Qzinfinite
 
'Q0' Q0_no_premerge 'Q1' Q1_no_angle 'Q2' Q2_no_independ 'Q3' Q3_no_redundant
'Q4' Q4_no_old 'Q5' Q5_no_check_out 'Q6' Q6_no_concave 'Q7' Q7_depth_first
'Q8' Q8_no_near_in 'Q9' Q9_pick_furthest 'Q10' Q10_no_narrow 'Q11' Q11_trinormals


Up: Home page for Qhull
Up: Qhull manual
To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
To: Qhull internals
To: Qhull functions, macros, and data structures
To: Qhull files
To: GeomGlobalIoMemMergePolyQhullSetStatUser


The Geometry Center Home Page

Comments to: qhull@qhull.org
Created: Sept. 25, 1995 --- Last modified: see top

geometry/vignettes/qhull/html/qconvex.html0000644000176200001440000006477013431000557020573 0ustar liggesusers qconvex -- convex hull Up: Home page for Qhull
Up: Qhull manual -- Table of Contents
To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
To: synopsis • input • outputs • controls • graphics • notes • conventions • options

[cone]qconvex -- convex hull

The convex hull of a set of points is the smallest convex set containing the points. See the detailed introduction by O'Rourke ['94]. See Description of Qhull and How Qhull adds a point.

Example: rbox 10 D3 | qconvex s o TO result
Compute the 3-d convex hull of 10 random points. Write a summary to the console and the points and facets to 'result'.
 
Example: rbox c | qconvex n
Print the normals for each facet of a cube.
 
Example: rbox c | qconvex i Qt
Print the triangulated facets of a cube.
 
Example: rbox y 500 W0 | qconvex
Compute the convex hull of a simplex with 500 points on its surface.
 
Example: rbox x W1e-12 1000 | qconvex QR0
Compute the convex hull of 1000 points near the surface of a randomly rotated simplex. Report the maximum thickness of a facet.
 
Example: rbox 1000 s | qconvex s FA
Compute the convex hull of 1000 cospherical points. Verify the results and print a summary with the total area and volume.
 
Example: rbox d D12 | qconvex QR0 FA
Compute the convex hull of a 12-d diamond. Randomly rotate the input. Note the large number of facets and the small volume.
 
Example: rbox c D7 | qconvex FA TF1000
Compute the convex hull of the 7-d hypercube. Report on progress every 1000 facets. Computing the convex hull of the 9-d hypercube takes too much time and space.
 
Example: rbox c d D2 | qconvex Qc s f Fx | more
Dump all fields of all facets for a square and a diamond. Also print a summary and a list of vertices. Note the coplanar points.
 

Except for rbox, all of the qhull programs compute a convex hull.

By default, Qhull merges coplanar facets. For example, the convex hull of a cube's vertices has six facets.

If you use 'Qt' (triangulated output), all facets will be simplicial (e.g., triangles in 2-d). For the cube example, it will have 12 facets. Some facets may be degenerate and have zero area.

If you use 'QJ' (joggled input), all facets will be simplicial. The corresponding vertices will be slightly perturbed and identical points will be joggled apart. Joggled input is less accurate that triangulated output.See Merged facets or joggled input.

The output for 4-d convex hulls may be confusing if the convex hull contains non-simplicial facets (e.g., a hypercube). See Why are there extra points in a 4-d or higher convex hull?

The 'qconvex' program is equivalent to 'qhull' in 2-d to 4-d, and 'qhull Qx' in 5-d and higher. It disables the following Qhull options: d v H Qbb Qf Qg Qm Qr Qu Qv Qx Qz TR E V Fp Gt Q0,etc.

Copyright © 1995-2018 C.B. Barber


»qconvex synopsis

qconvex- compute the convex hull.
    input (stdin): dimension, number of points, point coordinates
    comments start with a non-numeric character

options (qconvex.html):
    Qt   - triangulated output
    QJ   - joggle input instead of merging facets
    Tv   - verify result: structure, convexity, and point inclusion
    .    - concise list of all options
    -    - one-line description of all options

output options (subset):
    s    - summary of results (default)
    i    - vertices incident to each facet
    n    - normals with offsets
    p    - vertex coordinates (includes coplanar points if 'Qc')
    Fx   - extreme points (convex hull vertices)
    FA   - compute total area and volume
    o    - OFF format (dim, n, points, facets)
    G    - Geomview output (2-d, 3-d, and 4-d)
    m    - Mathematica output (2-d and 3-d)
    QVn  - print facets that include point n, -n if not
    TO file- output results to file, may be enclosed in single quotes

examples:
    rbox c D2 | qconvex s n                    rbox c D2 | qconvex i
    rbox c D2 | qconvex o                      rbox 1000 s | qconvex s Tv FA
    rbox c d D2 | qconvex s Qc Fx              rbox y 1000 W0 | qconvex s n
    rbox y 1000 W0 | qconvex s QJ              rbox d G1 D12 | qconvex QR0 FA Pp
    rbox c D7 | qconvex FA TF1000

»qconvex input

The input data on stdin consists of:

  • dimension
  • number of points
  • point coordinates

Use I/O redirection (e.g., qconvex < data.txt), a pipe (e.g., rbox 10 | qconvex), or the 'TI' option (e.g., qconvex TI data.txt).

Comments start with a non-numeric character. Error reporting is simpler if there is one point per line. Dimension and number of points may be reversed.

Here is the input for computing the convex hull of the unit cube. The output is the normals, one per facet.

rbox c > data

3 RBOX c
8
  -0.5   -0.5   -0.5
  -0.5   -0.5    0.5
  -0.5    0.5   -0.5
  -0.5    0.5    0.5
   0.5   -0.5   -0.5
   0.5   -0.5    0.5
   0.5    0.5   -0.5
   0.5    0.5    0.5

qconvex s n < data


Convex hull of 8 points in 3-d:

  Number of vertices: 8
  Number of facets: 6
  Number of non-simplicial facets: 6

Statistics for: RBOX c | QCONVEX s n

  Number of points processed: 8
  Number of hyperplanes created: 11
  Number of distance tests for qhull: 35
  Number of merged facets: 6
  Number of distance tests for merging: 84
  CPU seconds to compute hull (after input): 0.081

4
6
     0      0     -1   -0.5
     0     -1      0   -0.5
     1      0      0   -0.5
    -1      0      0   -0.5
     0      1      0   -0.5
     0      0      1   -0.5

»qconvex outputs

These options control the output of qconvex. They may be used individually or together.

 
Vertices
Fx
list extreme points (i.e., vertices). The first line is the number of extreme points. Each point is listed, one per line. The cube example has eight vertices.
Fv
list vertices for each facet. The first line is the number of facets. Each remaining line starts with the number of vertices. For the cube example, each facet has four vertices.
i
list vertices for each facet. The first line is the number of facets. The remaining lines list the vertices for each facet. In 4-d and higher, triangulate non-simplicial facets by adding an extra point.
 
 
Coordinates
o
print vertices and facets of the convex hull in OFF format. The first line is the dimension. The second line is the number of vertices, facets, and ridges. The vertex coordinates are next, followed by the facets. Each facet starts with the number of vertices. The cube example has four vertices per facet.
Ft
print a triangulation of the convex hull in OFF format. The first line is the dimension. The second line is the number of vertices and added points, followed by the number of facets and the number of ridges. The vertex coordinates are next, followed by the centrum coordinates. There is one centrum for each non-simplicial facet. The cube example has six centrums, one per square. Each facet starts with the number of vertices or centrums. In the cube example, each facet uses two vertices and one centrum.
p
print vertex coordinates. The first line is the dimension and the second line is the number of vertices. The following lines are the coordinates of each vertex. The cube example has eight vertices.
Qc p
print coordinates of vertices and coplanar points. The first line is the dimension. The second line is the number of vertices and coplanar points. The coordinates are next, one line per point. Use 'Qc Qi p' to print the coordinates of all points.
 
 
Facets
Fn
list neighboring facets for each facet. The first line is the number of facets. Each remaining line starts with the number of neighboring facets. The cube example has four neighbors per facet.
FN
list neighboring facets for each point. The first line is the total number of points. Each remaining line starts with the number of neighboring facets. Each vertex of the cube example has three neighboring facets. Use 'Qc Qi FN' to include coplanar and interior points.
Fa
print area for each facet. The first line is the number of facets. Facet area follows, one line per facet. For the cube example, each facet has area one.
FI
list facet IDs. The first line is the number of facets. The IDs follow, one per line.
 
 
Coplanar and interior points
Fc
list coplanar points for each facet. The first line is the number of facets. The remaining lines start with the number of coplanar points. A coplanar point is assigned to one facet.
Qi Fc
list interior points for each facet. The first line is the number of facets. The remaining lines start with the number of interior points. A coplanar point is assigned to one facet.
FP
print distance to nearest vertex for coplanar points. The first line is the number of coplanar points. Each remaining line starts with the point ID of a vertex, followed by the point ID of a coplanar point, its facet, and distance. Use 'Qc Qi FP' for coplanar and interior points.
 
 
Hyperplanes
n
print hyperplane for each facet. The first line is the dimension. The second line is the number of facets. Each remaining line is the hyperplane's coefficients followed by its offset.
Fo
print outer plane for each facet. The output plane is above all points. The first line is the dimension. The second line is the number of facets. Each remaining line is the outer plane's coefficients followed by its offset.
Fi
print inner plane for each facet. The inner plane of a facet is below its vertices. The first line is the dimension. The second line is the number of facets. Each remaining line is the inner plane's coefficients followed by its offset.
 
 
General
s
print summary for the convex hull. Use 'Fs' and 'FS' if you need numeric data.
FA
compute total area and volume for 's' and 'FS'
m
Mathematica output for the convex hull in 2-d or 3-d.
FM
Maple output for the convex hull in 2-d or 3-d.
G
Geomview output for the convex hull in 2-d, 3-d, or 4-d.
 
 
Scaling and rotation
Qbk:n
scale k'th coordinate to lower bound.
QBk:n
scale k'th coordinate to upper bound.
QbB
scale input to unit cube centered at the origin.
QRn
randomly rotate the input with a random seed of n. If n=0, the seed is the time. If n=-1, use time for the random seed, but do not rotate the input.
Qbk:0Bk:0
remove k'th coordinate from input. This computes the convex hull in one lower dimension.

»qconvex controls

These options provide additional control:

Qt
triangulated output. Qhull triangulates non-simplicial facets. It may produce degenerate facets of zero area.
QJ
joggle the input instead of merging facets. This guarantees simplicial facets (e.g., triangles in 3-d). It is less accurate than triangulated output ('Qt').
Qc
keep coplanar points
Qi
keep interior points
f
facet dump. Print the data structure for each facet.
QVn
select facets containing point n as a vertex,
QGn
select facets that are visible from point n (marked 'good'). Use -n for the remainder.
PDk:0
select facets with a negative coordinate for dimension k
TFn
report progress after constructing n facets
Tv
verify result
TI file
input data from file. The filename may not use spaces or quotes.
TO file
output results to file. Use single quotes if the filename contains spaces (e.g., TO 'file with spaces.txt'
Qs
search all points for the initial simplex. If Qhull can not construct an initial simplex, it reports a descriptive message. Usually, the point set is degenerate and one or more dimensions should be removed ('Qbk:0Bk:0'). If not, use option 'Qs'. It performs an exhaustive search for the best initial simplex. This is expensive is high dimensions.

»qconvex graphics

Display 2-d, 3-d, and 4-d convex hulls with Geomview ('G').

Display 2-d and 3-d convex hulls with Mathematica ('m').

To view 4-d convex hulls in 3-d, use 'Pd0d1d2d3' to select the positive octant and 'GrD2' to drop dimension 2.

»qconvex notes

Qhull always computes a convex hull. The convex hull may be used for other geometric structures. The general technique is to transform the structure into an equivalent convex hull problem. For example, the Delaunay triangulation is equivalent to the convex hull of the input sites after lifting the points to a paraboloid.

»qconvex conventions

The following terminology is used for convex hulls in Qhull. See Qhull's data structures.

  • point - d coordinates
  • vertex - extreme point of the input set
  • ridge - d-1 vertices between two neighboring facets
  • hyperplane - halfspace defined by a unit normal and offset
  • coplanar point - a nearly incident point to a hyperplane
  • centrum - a point on the hyperplane for testing convexity
  • facet - a facet with vertices, ridges, coplanar points, neighboring facets, and hyperplane
  • simplicial facet - a facet with d vertices, d ridges, and d neighbors
  • non-simplicial facet - a facet with more than d vertices
  • good facet - a facet selected by 'QVn', etc.

»qconvex options

qconvex- compute the convex hull
    http://www.qhull.org

input (stdin):
    first lines: dimension and number of points (or vice-versa).
    other lines: point coordinates, best if one point per line
    comments:    start with a non-numeric character

options:
    Qt   - triangulated output
    QJ   - joggle input instead of merging facets
    Qc   - keep coplanar points with nearest facet
    Qi   - keep interior points with nearest facet

Qhull control options:
    Qbk:n   - scale coord k so that low bound is n
      QBk:n - scale coord k so that upper bound is n (QBk is 0.5)
    QbB  - scale input to unit cube centered at the origin
    Qbk:0Bk:0 - remove k-th coordinate from input
    QJn  - randomly joggle input in range [-n,n]
    QRn  - random rotation (n=seed, n=0 time, n=-1 time/no rotate)
    Qs   - search all points for the initial simplex
    QGn  - good facet if visible from point n, -n for not visible
    QVn  - good facet if it includes point n, -n if not

Trace options:
    T4   - trace at level n, 4=all, 5=mem/gauss, -1= events
    Tc   - check frequently during execution
    Ts   - print statistics
    Tv   - verify result: structure, convexity, and point inclusion
    Tz   - send all output to stdout
    TFn  - report summary when n or more facets created
    TI file - input data from file, no spaces or single quotes
    TO file - output results to file, may be enclosed in single quotes
    TPn  - turn on tracing when point n added to hull
     TMn - turn on tracing at merge n
     TWn - trace merge facets when width > n
    TVn  - stop qhull after adding point n, -n for before (see TCn)
     TCn - stop qhull after building cone for point n (see TVn)

Precision options:
    Cn   - radius of centrum (roundoff added).  Merge facets if non-convex
     An  - cosine of maximum angle.  Merge facets if cosine > n or non-convex
           C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge
    Rn   - randomly perturb computations by a factor of [1-n,1+n]
    Un   - max distance below plane for a new, coplanar point
    Wn   - min facet width for outside point (before roundoff)

Output formats (may be combined; if none, produces a summary to stdout):
    f    - facet dump
    G    - Geomview output (see below)
    i    - vertices incident to each facet
    m    - Mathematica output (2-d and 3-d)
    n    - normals with offsets
    o    - OFF file format (dim, points and facets; Voronoi regions)
    p    - point coordinates
    s    - summary (stderr)

More formats:
    Fa   - area for each facet
    FA   - compute total area and volume for option 's'
    Fc   - count plus coplanar points for each facet
           use 'Qc' (default) for coplanar and 'Qi' for interior
    FC   - centrum for each facet
    Fd   - use cdd format for input (homogeneous with offset first)
    FD   - use cdd format for numeric output (offset first)
    FF   - facet dump without ridges
    Fi   - inner plane for each facet
    FI   - ID for each facet
    Fm   - merge count for each facet (511 max)
    FM   - Maple output (2-d and 3-d)
    Fn   - count plus neighboring facets for each facet
    FN   - count plus neighboring facets for each point
    Fo   - outer plane (or max_outside) for each facet
    FO   - options and precision constants
    FP   - nearest vertex for each coplanar point
    FQ   - command used for qconvex
    Fs   - summary: #int (8), dimension, #points, tot vertices, tot facets,
                      for output: #vertices, #facets,
                                  #coplanar points, #non-simplicial facets
                    #real (2), max outer plane, min vertex
    FS   - sizes:   #int (0)
                    #real(2) tot area, tot volume
    Ft   - triangulation with centrums for non-simplicial facets (OFF format)
    Fv   - count plus vertices for each facet
    FV   - average of vertices (a feasible point for 'H')
    Fx   - extreme points (in order for 2-d)

Geomview output (2-d, 3-d, and 4-d)
    Ga   - all points as dots
     Gp  -  coplanar points and vertices as radii
     Gv  -  vertices as spheres
    Gi   - inner planes only
     Gn  -  no planes
     Go  -  outer planes only
    Gc   - centrums
    Gh   - hyperplane intersections
    Gr   - ridges
    GDn  - drop dimension n in 3-d and 4-d output

Print options:
    PAn  - keep n largest facets by area
    Pdk:n - drop facet if normal[k] <= n (default 0.0)
    PDk:n - drop facet if normal[k] >= n
    Pg   - print good facets (needs 'QGn' or 'QVn')
    PFn  - keep facets whose area is at least n
    PG   - print neighbors of good facets
    PMn  - keep n facets with most merges
    Po   - force output.  If error, output neighborhood of facet
    Pp   - do not report precision problems

    .    - list of all options
    -    - one line descriptions of all options


Up: Home page for Qhull
Up: Qhull manual: Table of Contents
To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
To: synopsis • input • outputs • controls • graphics • notes • conventions • options


The Geometry Center Home Page

Comments to: qhull@qhull.org
Created: Sept. 25, 1995 --- Last modified: see top

geometry/vignettes/qhull/html/qh-optg.html0000644000176200001440000002557513431000557020467 0ustar liggesusers Qhull Geomview options (G)

Up: Home page for Qhull
Up: Qhull manual: Table of Contents
To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions


[delaunay] Qhull Geomview options (G)

This section lists the Geomview options for Qhull. These options are indicated by 'G' followed by a letter. See Output, Print, and Format for other output options.

Copyright © 1995-2018 C.B. Barber


» Programs OptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions

Geomview output options

Geomview is the graphical viewer for visualizing Qhull output in 2-d, 3-d and 4-d.

Geomview displays each facet of the convex hull. The color of a facet is determined by the coefficients of the facet's normal equation. For imprecise hulls, Geomview displays the inner and outer hull. Geomview can also display points, ridges, vertices, coplanar points, and facet intersections.

For 2-d Delaunay triangulations, Geomview displays the corresponding paraboloid. Geomview displays the 2-d Voronoi diagram. For halfspace intersections, it displays the dual convex hull.

 
General
G
display Geomview output
Gt
display transparent 3-d Delaunay triangulation
GDn
drop dimension n in 3-d and 4-d output
 
 
Specific
Ga
display all points as dots
Gc
display centrums (2-d, 3-d)
Gp
display coplanar points and vertices as radii
Gh
display hyperplane intersections
Gi
display inner planes only (2-d, 3-d)
Go
display outer planes only (2-d, 3-d)
Gr
display ridges (3-d)
Gv
display vertices as spheres
Gn
do not display planes

»G - produce output for viewing with Geomview

By default, option 'G' displays edges in 2-d, outer planes in 3-d, and ridges in 4-d.

A ridge can be explicit or implicit. An explicit ridge is a (d-1)-dimensional simplex between two facets. In 4-d, the explicit ridges are triangles. An implicit ridge is the topological intersection of two neighboring facets. It is the union of explicit ridges.

For non-simplicial 4-d facets, the explicit ridges can be quite complex. When displaying a ridge in 4-d, Qhull projects the ridge's vertices to one of its facets' hyperplanes. Use 'Gh' to project ridges to the intersection of both hyperplanes. This usually results in a cleaner display.

For 2-d Delaunay triangulations, Geomview displays the corresponding paraboloid. Geomview displays the 2-d Voronoi diagram. For halfspace intersections, it displays the dual convex hull.

»Ga - display all points as dots

Each input point is displayed as a green dot.

»Gc - display centrums (3-d)

The centrum is defined by a green radius sitting on a blue plane. The plane corresponds to the facet's hyperplane. If you sight along a facet's hyperplane, you will see that all neighboring centrums are below the facet. The radius is defined by 'C-n' or 'Cn'.

»GDn - drop dimension n in 3-d and 4-d output

The result is a 2-d or 3-d object. In 4-d, this corresponds to viewing the 4-d object from the nth axis without perspective. It's best to view 4-d objects in pieces. Use the 'Pdk' 'Pg' 'PG' 'QGn' and 'QVn' options to select a few facets. If one of the facets is perpendicular to an axis, then projecting along that axis will show the facet exactly as it is in 4-d. If you generate many facets, use Geomview's ginsu module to view the interior

To view multiple 4-d dimensions at once, output the object without 'GDn' and read it with Geomview's ndview. As you rotate the object in one set of dimensions, you can see how it changes in other sets of dimensions.

For additional control over 4-d objects, output the object without 'GDn' and read it with Geomview's 4dview. You can slice the object along any 4-d plane. You can also flip the halfspace that's deleted when slicing. By combining these features, you can get some interesting cross sections.

»Gh - display hyperplane intersections (3-d, 4-d)

In 3-d, the intersection is a black line. It lies on two neighboring hyperplanes, c.f., the blue squares associated with centrums ('Gc '). In 4-d, the ridges are projected to the intersection of both hyperplanes. If you turn on edges (Geomview's 'appearances' menu), each triangle corresponds to one ridge. The ridges may overlap each other.

»Gi - display inner planes only (2-d, 3-d)

The inner plane of a facet is below all of its vertices. It is parallel to the facet's hyperplane. The inner plane's color is the opposite of the outer plane's color, i.e., [1-r,1-g,1-b] . Its edges are determined by the vertices.

»Gn - do not display planes

By default, Geomview displays the precise plane (no merging) or both inner and output planes (if merging). If merging, Geomview does not display the inner plane if the the difference between inner and outer is too small.

»Go - display outer planes only (2-d, 3-d)

The outer plane of a facet is above all input points. It is parallel to the facet's hyperplane. Its color is determined by the facet's normal, and its edges are determined by the vertices.

»Gp - display coplanar points and vertices as radii

Coplanar points ('Qc'), interior points ('Qi'), outside points ('TCn' or 'TVn'), and vertices are displayed as red and yellow radii. The radii are perpendicular to the corresponding facet. Vertices are aligned with an interior point. The radii define a ball which corresponds to the imprecision of the point. The imprecision is the maximum of the roundoff error, the centrum radius, and maxcoord * (1 - A-n). It is at least 1/20'th of the maximum coordinate, and ignores post merging if pre-merging is done.

If 'Gv' (print vertices as spheres) is also selected, option 'Gp' displays coplanar points as radii. Select options Qc' and/or 'Qi'. Options 'Qc Gpv' displays coplanar points while 'Qci Gpv' displays coplanar and interior points. Option 'Qc' is automatically selected if 'Qi' is not selected with options 'Gpv'.

»Gr - display ridges (3-d)

A ridge connects the two vertices that are shared by neighboring facets. It is displayed in green. A ridge is the topological edge between two facets while the hyperplane intersection is the geometric edge between two facets. Ridges are always displayed in 4-d.

»Gt - transparent 3-d Delaunay

A 3-d Delaunay triangulation looks like a convex hull with interior facets. Option 'Gt' removes the outside ridges to reveal the outermost facets. It automatically sets options 'Gr' and 'GDn'. See example eg.17f.delaunay.3.

»Gv - display vertices as spheres (2-d, 3-d)

The radius of the sphere corresponds to the imprecision of the data. See 'Gp' for determining the radius.


Up: Home page for Qhull
Up: Qhull manual: Table of Contents
To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions


The Geometry Center Home Page

Comments to: qhull@qhull.org
Created: Sept. 25, 1995 --- Last modified: see top

geometry/vignettes/qhull/html/qhull-cpp.xml0000644000176200001440000002726213431000557020644 0ustar liggesusers

Qhull C++ -- C++ interface to Qhull

Copyright (c) 2009-2018, C.B. Barber

This draft document records some of the design decisions for Qhull C++. Convert it to HTML by road-faq.xsl from road-faq. Please send comments and suggestions to bradb@shore.net

Help
.
Qhull's collection APIs are modeled on Qt's collection API (QList, QVector, QHash) w/o QT_STRICT_ITERATORS. They support STL and Qt programming.

Some of Qhull's collection classes derive from STL classes. If so, please avoid additional STL functions and operators added by inheritance. These collection classes may be rewritten to derive from Qt classes instead. See Road's .

Qhull's collection API (where applicable). For documentation, see Qt's QList, QMap, QListIterator, QMapIterator, QMutableListIterator, and QMutableMapIterator
  • STL types [list, qlinkedlist, qlist, qvector, vector] -- const_iterator, iterator
  • STL types describing iterators [list, qlinkedlist, qlist, qvector, vector] -- const_pointer, const_reference, difference_type, pointer, reference, size_type, value_type. Pointer and reference types not defined if unavailable (not needed for <algorithm>)
  • const_iterator, iterator types -- difference_type, iterator_category, pointer, reference, value_type
  • Qt types [qlinkedlist, qlist, qvector] -- ConstIterator, Iterator, QhullclassIterator, MutableQhullclassIterator. Qt's foreach requires const_iterator.
  • Types for sets/maps [hash_map, QHash] -- key_compare, key_type, mapped_type
  • Constructor -- default constructor, copy constructor, assignment operator, destructor
  • Conversion -- to/from/as corresponding C, STL, and Qt constructs. Include toQList and toStdVector (may be filtered, e.g., QhullFacetSet). Do not define fromStdList and fromQList if container is not reference counted (i.e., acts like a value)
  • Get/set -- configuration options for class
  • STL-style iterator - begin, constBegin, constEnd, end, key, value, =, *, [], ->, ++, --, +, -, ==, !=, <, <=, >, >=, const_iterator(iterator), iterator COMPARE const_iterator. An iterator is an abstraction of a pointer. It is not aware of its container.
  • Java-style iterator [qiterator.h] - countRemaining, findNext, findPrevious, hasNext, hasPrevious, next, peekNext, peekPrevious, previous, toBack, toFront, = Coordinates
  • Mutable Java-style iterator adds - insert, remove, setValue, value
  • Element access -- back, first, front, last
  • Element access w/ index -- [], at (const& only), constData, data, mid, value
  • Read-only - (int)count, empty, isEmpty, (size_t)size. Count() and size() may be filtered. If so, they may be zero when !empty().
  • Read-only for sets/maps - capacity, key, keys, reserve, resize, values
  • Operator - ==, !=, +, +=, <<
  • Read-write -- append, clear, erase, insert, move, prepend, pop_back, pop_front, push_back, push_front, removeAll, removeAt, removeFirst, removeLast, replace, swap, takeAt, takeFirst, takeLast
  • Read-write for sets/maps -- insertMulti, squeeze, take, unite
  • Search -- contains(const T &), count(const T &), indexOf, lastIndexOf
  • Search for sets/maps -- constFind, lowerBound, upperBound
  • Stream I/O -- stream <<
STL list and vector -- For unfiltered access to each element.
  • Apache: Creating your own containers -- requirements for STL containers. Iterators should define the types from 'iterator_traits'.
  • STL types -- allocator_type, const_iterator, const_pointer, const_reference, const_reverse_iterator, difference_type, iterator, iterator_category, pointer, reference, reverse_iterator, size_type, value_type
  • STL constructors -- MyType(), MyType(count), MyType(count, value), MyType(first, last), MyType(MyType&),
  • STL getter/setters -- at (random_access only), back, begin, capacity, end, front, rbegin, rend, size, max_size
  • STL predicates -- empty
  • STL iterator types -- const_pointer, const_reference, difference_type, iterator_category, pointer, reference, value_type
  • STL iterator operators -- *, -<, ++, --, +=, -=, +, -, [], ==, !=, <, >, >=, <=
  • STL operators -- =, [] (random_access only), ==, !=, <, >, <=, >=
  • STL modifiers -- assign, clear, erase, insert, pop_back, push_back, reserve, resize, swap
Qt Qlist -- For unfiltered access to each element
  • Additional Qt types -- ConstIterator, Iterator, QListIterator, QMutableListIterator
  • Additional Qt get/set -- constBegin, constEnd, count, first, last, value (random_access only)
  • Additional Qt predicates -- isEmpty
  • Additional Qt -- mid (random_access only)
  • Additional Qt search -- contains, count(T&), indexOf (random_access only), lastIndeOf (random_access only)
  • Additional Qt modifiers -- append, insert(index,value) (random_access only), move (random_access only), pop_front, prepend, push_front, removeAll, removeAt (random_access only), removeFirst, removeLast, replace, swap by index, takeAt, takeFirst, takeLast
  • Additional Qt operators -- +, <<, +=, stream << and >>
  • Unsupported types by Qt -- allocator_type, const_reverse_iterator, reverse_iterator
  • Unsupported accessors by Qt -- max_size, rbegin, rend
  • Unsupported constructors by Qt -- multi-value constructors
  • unsupported modifiers by Qt -- assign, muli-value inserts, STL's swaps
STL map and Qt QMap. These use nearly the same API as list and vector classes. They add the following.
  • STL types -- key_compare, key_type, mapped_type
  • STL search -- equal_range, find, lower_bound, upper_bound
  • Qt removes -- equal_range, key_compare
  • Qt renames -- lowerBound, upperBound
  • Qt adds -- constFind, insertMulti, key, keys, take, uniqueKeys, unite, values
  • Not applicable to map and QMap -- at, back, pop_back, pop_front, push_back, push_front, swap
  • Not applicable to QMap -- append, first, last, lastIndexOf, mid, move, prepend, removeAll, removeAt, removeFirst, removeLast, replace, squeeze, takeAt, takeFirst, takeLast
  • Not applicable to map -- assign
Qt QHash. STL extensions provide similar classes, e.g., Microsoft's stdext::hash_set. THey are nearly the same as QMap
  • Not applicable to Qhash -- lowerBound, unite, upperBound,
  • Qt adds -- squeeze
  • check... -- Throw error on failure
  • try... -- Return false on failure. Do not throw errors.
  • ...Temporarily -- lifetime depends on source. e.g., toByteArrayTemporarily
  • ...p -- indicates pointer-to.
  • end... -- points to one beyond the last available
  • private functions -- No syntactic indication. They may become public later on.
  • Error messages -- Preceed error messages with the name of the class throwing the error (e.g. "ClassName: ..."). If this is an internal error, use "ClassName inconsistent: ..."
  • parameter order -- qhRunId, dimension, coordinates, count.
  • toClass -- Convert into a Class object (makes a deep copy)
  • qRunId -- Requires Qh installed. Some routines allow 0 for limited info (e.g., operator<<)
  • Disable methods in derived classes -- If the default constructor, copy constructor, or copy assignment is disabled, it should be also disabled in derived classes (better error messages).
  • Constructor order -- default constructor, other constructors, copy constructor, copy assignment, destructor
geometry/vignettes/qhull/html/qhull.man0000644000176200001440000011247513431000557020040 0ustar liggesusers.\" This is the Unix manual page for qhull, written in nroff, the standard .\" manual formatter for Unix systems. To format it, type .\" .\" nroff -man qhull.man .\" .\" This will print a formatted copy to standard output. If you want .\" to ensure that the output is plain ASCII, free of any control .\" characters that nroff uses for underlining etc, pipe the output .\" through "col -b": .\" .\" nroff -man qhull.man | col -b .\" .\" Warning: a leading quote "'" or dot "." will not format correctly .\" .TH qhull 1 "2003/12/30" "Geometry Center" .SH NAME qhull \- convex hull, Delaunay triangulation, Voronoi diagram, halfspace intersection about a point, hull volume, facet area .SH SYNOPSIS .nf qhull- compute convex hulls and related structures input (stdin): dimension, #points, point coordinates first comment (non-numeric) is listed in the summary halfspace: use dim plus one with offsets after coefficients options (qh-quick.htm): d - Delaunay triangulation by lifting points to a paraboloid v - Voronoi diagram via the Delaunay triangulation H1,1 - Halfspace intersection about [1,1,0,...] d Qu - Furthest-site Delaunay triangulation (upper convex hull) v Qu - Furthest-site Voronoi diagram Qt - triangulated output QJ - Joggle the input to avoid precision problems . - concise list of all options - - one-line description of all options Output options (subset): FA - compute total area and volume Fx - extreme points (convex hull vertices) G - Geomview output (2-d, 3-d and 4-d) Fp - halfspace intersection coordinates m - Mathematica output (2-d and 3-d) n - normals with offsets o - OFF file format (if Voronoi, outputs regions) TO file- output results to file, may be enclosed in single quotes f - print all fields of all facets s - summary of results (default) Tv - verify result: structure, convexity, and point inclusion p - vertex coordinates (centers for Voronoi) i - vertices incident to each facet example: rbox 1000 s | qhull Tv s FA .fi - html manual: index.htm - installation: README.txt - see also: COPYING.txt, REGISTER.txt, Changes.txt - WWW: - GIT: - mirror: - news: - Geomview: - news group: - FAQ: - email: qhull@qhull.org - bug reports: qhull_bug@qhull.org The sections are: - INTRODUCTION - DESCRIPTION, a description of Qhull - IMPRECISION, how Qhull handles imprecision - OPTIONS - Input and output options - Additional input/output formats - Precision options - Geomview options - Print options - Qhull options - Trace options - BUGS - E-MAIL - SEE ALSO - AUTHORS - ACKNOWLEGEMENTS This man page briefly describes all Qhull options. Please report any mismatches with Qhull's html manual (index.htm). .PP .SH INTRODUCTION Qhull is a general dimension code for computing convex hulls, Delaunay triangulations, Voronoi diagram, furthest\[hy]site Voronoi diagram, furthest\[hy]site Delaunay triangulations, and halfspace intersections about a point. It implements the Quickhull algorithm for computing the convex hull. Qhull handles round\[hy]off errors from floating point arithmetic. It can approximate a convex hull. The program includes options for hull volume, facet area, partial hulls, input transformations, randomization, tracing, multiple output formats, and execution statistics. The program can be called from within your application. You can view the results in 2\[hy]d, 3\[hy]d and 4\[hy]d with Geomview. .PP .SH DESCRIPTION .PP The format of input is the following: first line contains the dimension, second line contains the number of input points, and point coordinates follow. The dimension and number of points can be reversed. Comments and line breaks are ignored. A comment starts with a non\[hy]numeric character and continues to the end of line. The first comment is reported in summaries and statistics. Error reporting is better if there is one point per line. .PP The default printout option is a short summary. There are many other output formats. .PP Qhull implements the Quickhull algorithm for convex hull. This algorithm combines the 2\[hy]d Quickhull algorithm with the n\[hy]d beneath\[hy]beyond algorithm [c.f., Preparata & Shamos '85]. It is similar to the randomized algorithms of Clarkson and others [Clarkson et al. '93]. The main advantages of Quickhull are output sensitive performance, reduced space requirements, and automatic handling of precision problems. .PP The data structure produced by Qhull consists of vertices, ridges, and facets. A vertex is a point of the input set. A ridge is a set of d vertices and two neighboring facets. For example in 3\[hy]d, a ridge is an edge of the polyhedron. A facet is a set of ridges, a set of neighboring facets, a set of incident vertices, and a hyperplane equation. For simplicial facets, the ridges are defined by the vertices and neighboring facets. When Qhull merges two facets, it produces a non\[hy]simplicial facet. A non\[hy]simplicial facet has more than d neighbors and may share more than one ridge with a neighbor. .PP .SH IMPRECISION .PP Since Qhull uses floating point arithmetic, roundoff error may occur for each calculation. This causes problems for most geometric algorithms. .PP Qhull automatically sets option 'C\-0' in 2\[hy]d, 3\[hy]d, and 4\[hy]d, or option 'Qx' in 5\[hy]d and higher. These options handle precision problems by merging facets. Alternatively, use option 'QJ' to joggle the input. .PP With 'C\-0', Qhull merges non\[hy]convex facets while constructing the hull. The remaining facets are clearly convex. With 'Qx', Qhull merges coplanar horizon facets, flipped facets, concave facets and duplicated ridges. It merges coplanar facets after constructing the hull. With 'Qx', coplanar points may be missed, but it appears to be unlikely. .PP To guarantee triangular output, joggle the input with option 'QJ'. Facet merging will not occur. .SH OPTIONS .PP To get a list of the most important options, execute 'qhull' by itself. To get a complete list of options, execute 'qhull \-'. To get a complete, concise list of options, execute 'qhull .'. Options can be in any order. Capitalized options take an argument (except 'PG' and 'F' options). Single letters are used for output formats and precision constants. The other options are grouped into menus for other output formats ('F'), Geomview output ('G'), printing ('P'), Qhull control ('Q'), and tracing ('T'). .TP Main options: .TP default Compute the convex hull of the input points. Report a summary of the result. .TP d Compute the Delaunay triangulation by lifting the input points to a paraboloid. The 'o' option prints the input points and facets. The 'QJ' option guarantees triangular output. The 'Ft' option prints a triangulation. It adds points (the centrums) to non\[hy]simplicial facets. .TP v Compute the Voronoi diagram from the Delaunay triangulation. The 'p' option prints the Voronoi vertices. The 'o' option prints the Voronoi vertices and the vertices in each Voronoi region. It lists regions in site ID order. The 'Fv' option prints each ridge of the Voronoi diagram. The first or zero'th vertex indicates the infinity vertex. Its coordinates are qh_INFINITE (\-10.101). It indicates unbounded Voronoi regions or degenerate Delaunay triangles. .TP Hn,n,... Compute halfspace intersection about [n,n,0,...]. The input is a set of halfspaces defined in the same format as 'n', 'Fo', and 'Fi'. Use 'Fp' to print the intersection points. Use 'Fv' to list the intersection points for each halfspace. The other output formats display the dual convex hull. The point [n,n,n,...] is a feasible point for the halfspaces, i.e., a point that is inside all of the halfspaces (Hx+b <= 0). The default coordinate value is 0. The input may start with a feasible point. If so, use 'H' by itself. The input starts with a feasible point when the first number is the dimension, the second number is "1", and the coordinates complete a line. The 'FV' option produces a feasible point for a convex hull. .TP d Qu Compute the furthest\[hy]site Delaunay triangulation from the upper convex hull. The 'o' option prints the input points and facets. The 'QJ' option guarantees triangular otuput. You can also use 'Ft' to triangulate via the centrums of non\[hy]simplicial facets. .TP v Qu Compute the furthest\[hy]site Voronoi diagram. The 'p' option prints the Voronoi vertices. The 'o' option prints the Voronoi vertices and the vertices in each Voronoi region. The 'Fv' option prints each ridge of the Voronoi diagram. The first or zero'th vertex indicates the infinity vertex at infinity. Its coordinates are qh_INFINITE (\-10.101). It indicates unbounded Voronoi regions and degenerate Delaunay triangles. .PP .TP Input/Output options: .TP f Print out all facets and all fields of each facet. .TP G Output the hull in Geomview format. For imprecise hulls, Geomview displays the inner and outer hull. Geomview can also display points, ridges, vertices, coplanar points, and facet intersections. See below for a list of options. For Delaunay triangulations, 'G' displays the corresponding paraboloid. For halfspace intersection, 'G' displays the dual polytope. .TP i Output the incident vertices for each facet. Qhull prints the number of facets followed by the vertices of each facet. One facet is printed per line. The numbers are the 0\[hy]relative indices of the corresponding input points. The facets are oriented. In 4d and higher, Qhull triangulates non\[hy]simplicial facets. Each apex (the first vertex) is a created point that corresponds to the facet's centrum. Its index is greater than the indices of the input points. Each base corresponds to a simplicial ridge between two facets. To print the vertices without triangulation, use option 'Fv'. .TP m Output the hull in Mathematica format. Qhull writes a Mathematica file for 2\[hy]d and 3\[hy]d convex hulls and for 2\[hy]d Delaunay triangulations. Qhull produces a list of objects that you can assign to a variable in Mathematica, for example: "list= << ". If the object is 2\[hy]d, it can be visualized by "Show[Graphics[list]] ". For 3\[hy]d objects the command is "Show[Graphics3D[list]]". .TP n Output the normal equation for each facet. Qhull prints the dimension (plus one), the number of facets, and the normals for each facet. The facet's offset follows its normal coefficients. .TP o Output the facets in OFF file format. Qhull prints the dimension, number of points, number of facets, and number of ridges. Then it prints the coordinates of the input points and the vertices for each facet. Each facet is on a separate line. The first number is the number of vertices. The remainder are the indices of the corresponding points. The vertices are oriented in 2\[hy]d, 3\[hy]d, and in simplicial facets. For 2\[hy]d Voronoi diagrams, the vertices are sorted by adjacency, but not oriented. In 3\[hy]d and higher, the Voronoi vertices are sorted by index. See the 'v' option for more information. .TP p Output the coordinates of each vertex point. Qhull prints the dimension, the number of points, and the coordinates for each vertex. With the 'Gc' and 'Gi' options, it also prints coplanar and interior points. For Voronoi diagrams, it prints the coordinates of each Voronoi vertex. .TP s Print a summary to stderr. If no output options are specified at all, a summary goes to stdout. The summary lists the number of input points, the dimension, the number of vertices in the convex hull, the number of facets in the convex hull, the number of good facets (if 'Pg'), and statistics. The last two statistics (if needed) measure the maximum distance from a point or vertex to a facet. The number in parenthesis (e.g., 2.1x) is the ratio between the maximum distance and the worst\[hy]case distance due to merging two simplicial facets. .PP .TP Precision options .TP An Maximum angle given as a cosine. If the angle between a pair of facet normals is greater than n, Qhull merges one of the facets into a neighbor. If 'n' is negative, Qhull tests angles after adding each point to the hull (pre\[hy]merging). If 'n' is positive, Qhull tests angles after constructing the hull (post\[hy]merging). Both pre\[hy] and post\[hy]merging can be defined. Option 'C0' or 'C\-0' is set if the corresponding 'Cn' or 'C\-n' is not set. If 'Qx' is set, then 'A\-n' and 'C\-n' are checked after the hull is constructed and before 'An' and 'Cn' are checked. .TP Cn Centrum radius. If a centrum is less than n below a neighboring facet, Qhull merges one of the facets. If 'n' is negative or '\-0', Qhull tests and merges facets after adding each point to the hull. This is called "pre\[hy]merging". If 'n' is positive, Qhull tests for convexity after constructing the hull ("post\[hy]merging"). Both pre\[hy] and post\[hy]merging can be defined. For 5\[hy]d and higher, 'Qx' should be used instead of 'C\-n'. Otherwise, most or all facets may be merged together. .TP En Maximum roundoff error for distance computations. .TP Rn Randomly perturb distance computations up to +/\- n * max_coord. This option perturbs every distance, hyperplane, and angle computation. To use time as the random number seed, use option 'QR\-1'. .TP Vn Minimum distance for a facet to be visible. A facet is visible if the distance from the point to the facet is greater than 'Vn'. Without merging, the default value for 'Vn' is the round\[hy]off error ('En'). With merging, the default value is the pre\[hy]merge centrum ('C\-n') in 2\[hy]d or 3\[hy]d, or three times that in other dimensions. If the outside width is specified ('Wn'), the maximum, default value for 'Vn' is 'Wn'. .TP Un Maximum distance below a facet for a point to be coplanar to the facet. The default value is 'Vn'. .TP Wn Minimum outside width of the hull. Points are added to the convex hull only if they are clearly outside of a facet. A point is outside of a facet if its distance to the facet is greater than 'Wn'. The normal value for 'Wn' is 'En'. If the user specifies pre\[hy]merging and does not set 'Wn', than 'Wn' is set to the premerge 'Cn' and maxcoord*(1\-An). .PP .TP Additional input/output formats .TP Fa Print area for each facet. For Delaunay triangulations, the area is the area of the triangle. For Voronoi diagrams, the area is the area of the dual facet. Use 'PAn' for printing the n largest facets, and option 'PFn' for printing facets larger than 'n'. The area for non\[hy]simplicial facets is the sum of the areas for each ridge to the centrum. Vertices far below the facet's hyperplane are ignored. The reported area may be significantly less than the actual area. .TP FA Compute the total area and volume for option 's'. It is an approximation for non\[hy]simplicial facets (see 'Fa'). .TP Fc Print coplanar points for each facet. The output starts with the number of facets. Then each facet is printed one per line. Each line is the number of coplanar points followed by the point ids. Option 'Qi' includes the interior points. Each coplanar point (interior point) is assigned to the facet it is furthest above (resp., least below). .TP FC Print centrums for each facet. The output starts with the dimension followed by the number of facets. Then each facet centrum is printed, one per line. .TP Fd Read input in cdd format with homogeneous points. The input starts with comments. The first comment is reported in the summary. Data starts after a "begin" line. The next line is the number of points followed by the dimension+1 and "real" or "integer". Then the points are listed with a leading "1" or "1.0". The data ends with an "end" line. For halfspaces ('Fd Hn,n,...'), the input format is the same. Each halfspace starts with its offset. The sign of the offset is the opposite of Qhull's convention. .TP FD Print normals ('n', 'Fo', 'Fi') or points ('p') in cdd format. The first line is the command line that invoked Qhull. Data starts with a "begin" line. The next line is the number of normals or points followed by the dimension+1 and "real". Then the normals or points are listed with the offset before the coefficients. The offset for points is 1.0. The offset for normals has the opposite sign. The data ends with an "end" line. .TP FF Print facets (as in 'f') without printing the ridges. .TP Fi Print inner planes for each facet. The inner plane is below all vertices. .TP Fi Print separating hyperplanes for bounded, inner regions of the Voronoi diagram. The first line is the number of ridges. Then each hyperplane is printed, one per line. A line starts with the number of indices and floats. The first pair lists adjacent input sites, the next d floats are the normalized coefficients for the hyperplane, and the last float is the offset. The hyperplane is oriented toward 'QVn' (if defined), or the first input site of the pair. Use 'Tv' to verify that the hyperplanes are perpendicular bisectors. Use 'Fo' for unbounded regions, and 'Fv' for the corresponding Voronoi vertices. .TP FI Print facet identifiers. .TP Fm Print number of merges for each facet. At most 511 merges are reported for a facet. See 'PMn' for printing the facets with the most merges. .TP FM Output the hull in Maple format. Qhull writes a Maple file for 2\[hy]d and 3\[hy]d convex hulls and for 2\[hy]d Delaunay triangulations. Qhull produces a '.mpl' file for displaying with display3d(). .TP Fn Print neighbors for each facet. The output starts with the number of facets. Then each facet is printed one per line. Each line is the number of neighbors followed by an index for each neighbor. The indices match the other facet output formats. A negative index indicates an unprinted facet due to printing only good facets ('Pg'). It is the negation of the facet's ID (option 'FI'). For example, negative indices are used for facets "at infinity" in the Delaunay triangulation. .TP FN Print vertex neighbors or coplanar facet for each point. The first line is the number of points. Then each point is printed, one per line. If the point is coplanar, the line is "1" followed by the facet's ID. If the point is not a selected vertex, the line is "0". Otherwise, each line is the number of neighbors followed by the corresponding facet indices (see 'Fn'). .TP Fo Print outer planes for each facet in the same format as 'n'. The outer plane is above all points. .TP Fo Print separating hyperplanes for unbounded, outer regions of the Voronoi diagram. The first line is the number of ridges. Then each hyperplane is printed, one per line. A line starts with the number of indices and floats. The first pair lists adjacent input sites, the next d floats are the normalized coefficients for the hyperplane, and the last float is the offset. The hyperplane is oriented toward 'QVn' (if defined), or the first input site of the pair. Use 'Tv' to verify that the hyperplanes are perpendicular bisectors. Use 'Fi' for bounded regions, and 'Fv' for the corresponding Voronoi vertices. .TP FO List all options to stderr, including the default values. Additional 'FO's are printed to stdout. .TP Fp Print points for halfspace intersections (option 'Hn,n,...'). Each intersection corresponds to a facet of the dual polytope. The "infinity" point [\-10.101,\-10.101,...] indicates an unbounded intersection. .TP FP For each coplanar point ('Qc') print the point ID of the nearest vertex, the point ID, the facet ID, and the distance. .TP FQ Print command used for qhull and input. .TP Fs Print a summary. The first line consists of the number of integers ("8"), followed by the dimension, the number of points, the number of vertices, the number of facets, the number of vertices selected for output, the number of facets selected for output, the number of coplanar points selected for output, number of simplicial, unmerged facets in output The second line consists of the number of reals ("2"), followed by the maxmimum offset to an outer plane and and minimum offset to an inner plane. Roundoff is included. Later versions of Qhull may produce additional integers or reals. .TP FS Print the size of the hull. The first line consists of the number of integers ("0"). The second line consists of the number of reals ("2"), followed by the total facet area, and the total volume. Later versions of Qhull may produce additional integers or reals. The total volume measures the volume of the intersection of the halfspaces defined by each facet. Both area and volume are approximations for non\[hy]simplicial facets. See option 'Fa'. .TP Ft Print a triangulation with added points for non\[hy]simplicial facets. The first line is the dimension and the second line is the number of points and the number of facets. The points follow, one per line, then the facets follow as a list of point indices. With option 'Qz', the points include the point\[hy]at\[hy]infinity. .TP Fv Print vertices for each facet. The first line is the number of facets. Then each facet is printed, one per line. Each line is the number of vertices followed by the corresponding point ids. Vertices are listed in the order they were added to the hull (the last one is first). .TP Fv Print all ridges of a Voronoi diagram. The first line is the number of ridges. Then each ridge is printed, one per line. A line starts with the number of indices. The first pair lists adjacent input sites, the remaining indices list Voronoi vertices. Vertex '0' indicates the vertex\[hy]at\[hy]infinity (i.e., an unbounded ray). In 3\[hy]d, the vertices are listed in order. See 'Fi' and 'Fo' for separating hyperplanes. .TP FV Print average vertex. The average vertex is a feasible point for halfspace intersection. .TP Fx List extreme points (vertices) of the convex hull. The first line is the number of points. The other lines give the indices of the corresponding points. The first point is '0'. In 2\[hy]d, the points occur in counter\[hy]clockwise order; otherwise they occur in input order. For Delaunay triangulations, 'Fx' lists the extreme points of the input sites. The points are unordered. .PP .TP Geomview options .TP G Produce a file for viewing with Geomview. Without other options, Qhull displays edges in 2\[hy]d, outer planes in 3\[hy]d, and ridges in 4\[hy]d. A ridge can be explicit or implicit. An explicit ridge is a dim\-1 dimensional simplex between two facets. In 4\[hy]d, the explicit ridges are triangles. When displaying a ridge in 4\[hy]d, Qhull projects the ridge's vertices to one of its facets' hyperplanes. Use 'Gh' to project ridges to the intersection of both hyperplanes. .TP Ga Display all input points as dots. .TP Gc Display the centrum for each facet in 3\[hy]d. The centrum is defined by a green radius sitting on a blue plane. The plane corresponds to the facet's hyperplane. The radius is defined by 'C\-n' or 'Cn'. .TP GDn Drop dimension n in 3\[hy]d or 4\[hy]d. The result is a 2\[hy]d or 3\[hy]d object. .TP Gh Display hyperplane intersections in 3\[hy]d and 4\[hy]d. In 3\[hy]d, the intersection is a black line. It lies on two neighboring hyperplanes (c.f., the blue squares associated with centrums ('Gc')). In 4\[hy]d, the ridges are projected to the intersection of both hyperplanes. .TP Gi Display inner planes in 2\[hy]d and 3\[hy]d. The inner plane of a facet is below all of its vertices. It is parallel to the facet's hyperplane. The inner plane's color is the opposite (1\-r,1\-g,1\-b) of the outer plane. Its edges are determined by the vertices. .TP Gn Do not display inner or outer planes. By default, Geomview displays the precise plane (no merging) or both inner and output planes (merging). Under merging, Geomview does not display the inner plane if the the difference between inner and outer is too small. .TP Go Display outer planes in 2\[hy]d and 3\[hy]d. The outer plane of a facet is above all input points. It is parallel to the facet's hyperplane. Its color is determined by the facet's normal, and its edges are determined by the vertices. .TP Gp Display coplanar points and vertices as radii. A radius defines a ball which corresponds to the imprecision of the point. The imprecision is the maximum of the roundoff error, the centrum radius, and maxcoord * (1\-An). It is at least 1/20'th of the maximum coordinate, and ignores post\[hy]merging if pre\[hy]merging is done. .TP Gr Display ridges in 3\[hy]d. A ridge connects the two vertices that are shared by neighboring facets. Ridges are always displayed in 4\[hy]d. .TP Gt A 3\[hy]d Delaunay triangulation looks like a convex hull with interior facets. Option 'Gt' removes the outside ridges to reveal the outermost facets. It automatically sets options 'Gr' and 'GDn'. .TP Gv Display vertices as spheres. The radius of the sphere corresponds to the imprecision of the data. See 'Gp' for determining the radius. .PP .TP Print options .TP PAn Only the n largest facets are marked good for printing. Unless 'PG' is set, 'Pg' is automatically set. .TP Pdk:n Drop facet from output if normal[k] <= n. The option 'Pdk' uses the default value of 0 for n. .TP PDk:n Drop facet from output if normal[k] >= n. The option 'PDk' uses the default value of 0 for n. .TP PFn Only facets with area at least 'n' are marked good for printing. Unless 'PG' is set, 'Pg' is automatically set. .TP Pg Print only good facets. A good facet is either visible from a point (the 'QGn' option) or includes a point (the 'QVn' option). It also meets the requirements of 'Pdk' and 'PDk' options. Option 'Pg' is automatically set for options 'PAn' and 'PFn'. .TP PG Print neighbors of good facets. .TP PMn Only the n facets with the most merges are marked good for printing. Unless 'PG' is set, 'Pg' is automatically set. .TP Po Force output despite precision problems. Verify ('Tv') does not check coplanar points. Flipped facets are reported and concave facets are counted. If 'Po' is used, points are not partitioned into flipped facets and a flipped facet is always visible to a point. Also, if an error occurs before the completion of Qhull and tracing is not active, 'Po' outputs a neighborhood of the erroneous facets (if any). .TP Pp Do not report precision problems. .PP .TP Qhull control options .TP Qbk:0Bk:0 Drop dimension k from the input points. This allows the user to take convex hulls of sub\[hy]dimensional objects. It happens before the Delaunay and Voronoi transformation. .TP QbB Scale the input points to fit the unit cube. After scaling, the lower bound will be \-0.5 and the upper bound +0.5 in all dimensions. For Delaunay and Voronoi diagrams, scaling happens after projection to the paraboloid. Under precise arithmetic, scaling does not change the topology of the convex hull. .TP Qbb Scale the last coordinate to [0, m] where m is the maximum absolute value of the other coordinates. For Delaunay and Voronoi diagrams, scaling happens after projection to the paraboloid. It reduces roundoff error for inputs with integer coordinates. Under precise arithmetic, scaling does not change the topology of the convex hull. .TP Qbk:n Scale the k'th coordinate of the input points. After scaling, the lower bound of the input points will be n. 'Qbk' scales to \-0.5. .TP QBk:n Scale the k'th coordinate of the input points. After scaling, the upper bound will be n. 'QBk' scales to +0.5. .TP Qc Keep coplanar points with the nearest facet. Output formats 'p', 'f', 'Gp', 'Fc', 'FN', and 'FP' will print the points. .TP Qf Partition points to the furthest outside facet. .TP Qg Only build good facets. With the 'Qg' option, Qhull will only build those facets that it needs to determine the good facets in the output. See 'QGn', 'QVn', and 'PdD' for defining good facets, and 'Pg' and 'PG' for printing good facets and their neighbors. .TP QGn A facet is good (see 'Qg' and 'Pg') if it is visible from point n. If n < 0, a facet is good if it is not visible from point n. Point n is not added to the hull (unless 'TCn' or 'TPn'). With rbox, use the 'Pn,m,r' option to define your point; it will be point 0 (QG0). .TP Qi Keep interior points with the nearest facet. Output formats 'p', 'f', 'Gp', 'FN', 'FP', and 'Fc' will print the points. .TP QJn Joggle each input coordinate by adding a random number in [\-n,n]. If a precision error occurs, then qhull increases n and tries again. It does not increase n beyond a certain value, and it stops after a certain number of attempts [see user.h]. Option 'QJ' selects a default value for n. The output will be simplicial. For Delaunay triangulations, 'QJn' sets 'Qbb' to scale the last coordinate (not if 'Qbk:n' or 'QBk:n' is set). \'QJn' is deprecated for Voronoi diagrams. See also 'Qt'. .TP Qm Only process points that would otherwise increase max_outside. Other points are treated as coplanar or interior points. .TP Qr Process random outside points instead of furthest ones. This makes Qhull equivalent to the randomized incremental algorithms. CPU time is not reported since the randomization is inefficient. .TP QRn Randomly rotate the input points. If n=0, use time as the random number seed. If n>0, use n as the random number seed. If n=\-1, don't rotate but use time as the random number seed. For Delaunay triangulations ('d' and 'v'), rotate about the last axis. .TP Qs Search all points for the initial simplex. .TP Qt Triangulated output. Triangulate all non\[hy]simplicial facets. \'Qt' is deprecated for Voronoi diagrams. See also 'Qt'. .TP Qv Test vertex neighbors for convexity after post\[hy]merging. To use the 'Qv' option, you also need to set a merge option (e.g., 'Qx' or 'C\-0'). .TP QVn A good facet (see 'Qg' and 'Pg') includes point n. If n<0, then a good facet does not include point n. The point is either in the initial simplex or it is the first point added to the hull. Option 'QVn' may not be used with merging. .TP Qx Perform exact merges while building the hull. The "exact" merges are merging a point into a coplanar facet (defined by 'Vn', 'Un', and 'C\-n'), merging concave facets, merging duplicate ridges, and merging flipped facets. Coplanar merges and angle coplanar merges ('A\-n') are not performed. Concavity testing is delayed until a merge occurs. After the hull is built, all coplanar merges are performed (defined by 'C\-n' and 'A\-n'), then post\[hy]merges are performed (defined by 'Cn' and 'An'). .TP Qz Add a point "at infinity" that is above the paraboloid for Delaunay triangulations and Voronoi diagrams. This reduces precision problems and allows the triangulation of cospherical points. .PP .TP Qhull experiments and speedups .TP Q0 Turn off pre\[hy]merging as a default option. With 'Q0'/'Qx' and without explicit pre\[hy]merge options, Qhull ignores precision issues while constructing the convex hull. This may lead to precision errors. If so, a descriptive warning is generated. .TP Q1 With 'Q1', Qhull sorts merges by type (coplanar, angle coplanar, concave) instead of by angle. .TP Q2 With 'Q2', Qhull merges all facets at once instead of using independent sets of merges and then retesting. .TP Q3 With 'Q3', Qhull does not remove redundant vertices. .TP Q4 With 'Q4', Qhull avoids merges of an old facet into a new facet. .TP Q5 With 'Q5', Qhull does not correct outer planes at the end. The maximum outer plane is used instead. .TP Q6 With 'Q6', Qhull does not pre\[hy]merge concave or coplanar facets. .TP Q7 With 'Q7', Qhull processes facets in depth\[hy]first order instead of breadth\[hy]first order. .TP Q8 With 'Q8' and merging, Qhull does not retain near\[hy]interior points for adjusting outer planes. 'Qc' will probably retain all points that adjust outer planes. .TP Q9 With 'Q9', Qhull processes the furthest of all outside sets at each iteration. .TP Q10 With 'Q10', Qhull does not use special processing for narrow distributions. .TP Q11 With 'Q11', Qhull copies normals and recompute centrums for tricoplanar facets. .TP Q12 With 'Q12', Qhull does not report a very wide merge due to a duplicated ridge with nearly coincident vertices Q14 With 'Q14', Qhull does not rename vertices that create a duplicate ridge .PP .TP Trace options .TP Tn Trace at level n. Qhull includes full execution tracing. 'T\-1' traces events. 'T1' traces the overall execution of the program. 'T2' and 'T3' trace overall execution and geometric and topological events. 'T4' traces the algorithm. 'T5' includes information about memory allocation and Gaussian elimination. .TP Ta Annotate output with codes that identify the corresponding qh_fprintf() statement. .TP Tc Check frequently during execution. This will catch most inconsistency errors. .TP TCn Stop Qhull after building the cone of new facets for point n. The output for 'f' includes the cone and the old hull. See also 'TVn'. .TP TFn Report progress whenever more than n facets are created During post\[hy]merging, 'TFn' reports progress after more than n/2 merges. .TP TI file Input data from 'file'. The filename may not include spaces or quotes. .TP TO file Output results to 'file'. The name may be enclosed in single quotes. .TP TPn Turn on tracing when point n is added to the hull. Trace partitions of point n. If used with TWn, turn off tracing after adding point n to the hull. .TP TRn Rerun qhull n times. Usually used with 'QJn' to determine the probability that a given joggle will fail. .TP Ts Collect statistics and print to stderr at the end of execution. .TP Tv Verify the convex hull. This checks the topological structure, facet convexity, and point inclusion. If precision problems occurred, facet convexity is tested whether or not 'Tv' is selected. Option 'Tv' does not check point inclusion if forcing output with 'Po', or if 'Q5' is set. For point inclusion testing, Qhull verifies that all points are below all outer planes (facet\->maxoutside). Point inclusion is exhaustive if merging or if the facet\[hy]point product is small enough; otherwise Qhull verifies each point with a directed search (qh_findbest). Point inclusion testing occurs after producing output. It prints a message to stderr unless option 'Pp' is used. This allows the user to interrupt Qhull without changing the output. .TP TVn Stop Qhull after adding point n. If n < 0, stop Qhull before adding point n. Output shows the hull at this time. See also 'TCn' .TP TMn Turn on tracing at n'th merge. .TP TWn Trace merge facets when the width is greater than n. .TP Tz Redirect stderr to stdout. .PP .SH BUGS Please report bugs to Brad Barber at qhull_bug@qhull.org. If Qhull does not compile, it is due to an incompatibility between your system and ours. The first thing to check is that your compiler is ANSI standard. If it is, check the man page for the best options, or find someone to help you. If you locate the cause of your problem, please send email since it might help others. If Qhull compiles but crashes on the test case (rbox D4), there's still incompatibility between your system and ours. Typically it's been due to mem.c and memory alignment. You can use qh_NOmem in mem.h to turn off memory management. Please let us know if you figure out how to fix these problems. If you do find a problem, try to simplify it before reporting the error. Try different size inputs to locate the smallest one that causes an error. You're welcome to hunt through the code using the execution trace as a guide. This is especially true if you're incorporating Qhull into your own program. When you do report an error, please attach a data set to the end of your message. This allows us to see the error for ourselves. Qhull is maintained part\[hy]time. .PP .SH E\[hy]MAIL Please send correspondence to qhull@qhull.org and report bugs to qhull_bug@qhull.org. Let us know how you use Qhull. If you mention it in a paper, please send the reference and an abstract. If you would like to get Qhull announcements (e.g., a new version) and news (any bugs that get fixed, etc.), let us know and we will add you to our mailing list. If you would like to communicate with other Qhull users, we will add you to the qhull_users alias. For Internet news about geometric algorithms and convex hulls, look at comp.graphics.algorithms and sci.math.num\-analysis .SH SEE ALSO rbox(1) Barber, C. B., D.P. Dobkin, and H.T. Huhdanpaa, "The Quickhull Algorithm for Convex Hulls," ACM Trans. on Mathematical Software, 22(4):469\[en]483, Dec. 1996. http://portal.acm.org/citation.cfm?doid=235815.235821 http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.117.405 Clarkson, K.L., K. Mehlhorn, and R. Seidel, "Four results on randomized incremental construction," Computational Geometry: Theory and Applications, vol. 3, p. 185\[en]211, 1993. Preparata, F. and M. Shamos, Computational Geometry, Springer\[hy]Verlag, New York, 1985. .PP .SH AUTHORS .nf C. Bradford Barber Hannu Huhdanpaa bradb@shore.net hannu@qhull.org .fi .SH ACKNOWLEDGEMENTS A special thanks to Albert Marden, Victor Milenkovic, the Geometry Center, Harvard University, and Endocardial Solutions, Inc. for supporting this work. Qhull 1.0 and 2.0 were developed under National Science Foundation grants NSF/DMS\[hy]8920161 and NSF\[hy]CCR\[hy]91\[hy]15793 750\[hy]7504. David Dobkin guided the original work at Princeton University. If you find it useful, please let us know. The Geometry Center is supported by grant DMS\[hy]8920161 from the National Science Foundation, by grant DOE/DE\[hy]FG02\[hy]92ER25137 from the Department of Energy, by the University of Minnesota, and by Minnesota Technology, Inc. Qhull is available from http://www.qhull.org geometry/vignettes/qhull/html/qh-optt.html0000644000176200001440000002574613431000557020504 0ustar liggesusers Qhull trace options (T)

Up: Home page for Qhull
Up: Qhull manual: Table of Contents
To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions


[delaunay] Qhull trace options (T)

This section lists the trace options for Qhull. These options are indicated by 'T' followed by a letter.

Copyright © 1995-2018 C.B. Barber


» Programs OptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions

Trace options

 
General
Tz
output error information to stdout instead of stderr
TI file
input data from a file
TO file
output results to a file
Ts
print statistics
TFn
report progress whenever n or more facets created
TRn
rerun qhull n times
Tv
verify result: structure, convexity, and point inclusion
 
 
Debugging
Tc
check frequently during execution
TVn
stop qhull after adding point n
TCn
stop qhull after building cone for point n
TV-n
stop qhull before adding point n
T4
trace at level n, 4=all, 5=mem/gauss, -1= events
TWn
trace merge facets when width > n
TMn
turn on tracing at merge n
TPn
turn on tracing when point n added to hull

»Tc - check frequently during execution

Qhull includes frequent checks of its data structures. Option 'Tc' will catch most inconsistency errors. It is slow and should not be used for production runs. Option 'Tv' performs the same checks after the hull is constructed.

»TCn - stop qhull after building cone for point n

Qhull builds a cone from the point to its horizon facets. Option 'TCn' stops Qhull just after building the cone. The output for 'f' includes the cone and the old hull.'.

»TFn - report summary whenever n or more facets created

Option 'TFn' reports progress whenever more than n facets are created. The test occurs just before adding a new point to the hull. During post-merging, 'TFn' reports progress after more than n/2 merges.

»TI file - input data from file

Input data from 'file' instead of stdin. The filename may not contain spaces or use single quotes. You may use I/O redirection instead (e.g., 'rbox 10 | qdelaunay >results').

»TMn - turn on tracing at merge n

Turn on tracing at n'th merge.

»Tn - trace at level n

Qhull includes full execution tracing. 'T-1' traces events. 'T1' traces the overall execution of the program. 'T2' and 'T3' trace overall execution and geometric and topological events. 'T4' traces the algorithm. 'T5' includes information about memory allocation and Gaussian elimination. 'T1' is useful for logging progress of Qhull in high dimensions.

Option 'Tn' can produce large amounts of output. Use options 'TPn', 'TWn', and 'TMn' to selectively turn on tracing. Since all errors report the last processed point, option 'TPn' is particularly useful.

Different executions of the same program may produce different traces and different results. The reason is that Qhull uses hashing to match ridges of non-simplicial facets. For performance reasons, the hash computation uses memory addresses which may change across executions.

»TO file - output results to file

Redirect stdout to 'file'. The filename may be enclosed in single quotes. Unix and Windows NT users may use I/O redirection instead (e.g., 'rbox 10 | qdelaunay >results').

Windows95 users should always use 'TO file'. If they use I/O redirection, error output is not sent to the console. Qhull uses single quotes instead of double quotes because a missing double quote can freeze Windows95 (e.g., do not run, rbox 10 | qhull TO "x)

»TPn - turn on tracing when point n added to hull

Option 'TPn' turns on tracing when point n is added to the hull. It also traces partitions of point n. This option reduces the output size when tracing. It is the normal method to determine the cause of a Qhull error. All Qhull errors report the last point added.

Use options 'TPn TVn' to trace the addition of point n to the convex hull and stop when done.

If used with option 'TWn', 'TPn' turns off tracing after adding point n to the hull. Use options 'TPn TWn' to trace the addition of point n to the convex hull, partitions of point n, and wide merges.

»TRn - rerun qhull n times

Option 'TRn' reruns Qhull n times. It is usually used with 'QJn' to determine the probability that a given joggle will fail. The summary ('s') lists the failure rate and the precision errors that occurred. Option 'Ts' will report statistics for all of the runs. Trace and output options only apply to the last run. An event trace, 'T-1' reports events for all runs.

Tracing applies to the last run of Qhull. If an error is reported, the options list the run number as "_run". To trace this run, set 'TRn' to the same value.

»Ts - print statistics

Option 'Ts' collects statistics and prints them to stderr. For Delaunay triangulations, the angle statistics are restricted to the lower or upper envelope.

»Tv - verify result: structure, convexity, and point inclusion

Option 'Tv' checks the topological structure, convexity, and point inclusion. If precision problems occurred, facet convexity is tested whether or not 'Tv' is selected. Option 'Tv' does not check point inclusion if forcing output with 'Po', or if 'Q5' is set.

The convex hull of a set of points is the smallest polytope that includes the points. Option 'Tv' tests point inclusion. Qhull verifies that all points are below all outer planes (facet->maxoutside). Point inclusion is exhaustive if merging or if the facet-point product is small enough; otherwise Qhull verifies each point with a directed search (qh_findbest). To force an exhaustive test when using option 'C-0' (default), use 'C-1e-30' instead.

Point inclusion testing occurs after producing output. It prints a message to stderr unless option 'Pp' is used. This allows the user to interrupt Qhull without changing the output.

With 'qvoronoi Fi' and 'qvoronoi Fo', option 'Tv' collects statistics that verify all Voronoi vertices lie on the separating hyperplane, and for bounded regions, all separating hyperplanes are perpendicular bisectors.

»TV-n - stop qhull before adding point n

Qhull adds one point at a time to the convex hull. See how Qhull adds a point. Option 'TV-n' stops Qhull just before adding a new point. Output shows the hull at this time.

»TVn - stop qhull after adding point n

Option 'TVn' stops Qhull after it has added point n. Output shows the hull at this time.

»TWn - trace merge facets when width > n

Along with TMn, this option allows the user to determine the cause of a wide merge.

»Tz - send all output to stdout

Redirect stderr to stdout.


Up: Home page for Qhull
Up: Qhull manual: Table of Contents
To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions


The Geometry Center Home Page

Comments to: qhull@qhull.org
Created: Sept. 25, 1995 --- Last modified: see top

geometry/vignettes/qhull/html/normal_voronoi_knauss_oesterle.jpg0000644000176200001440000005656413431000556025256 0ustar liggesusersJFIF;CREATOR: gd-jpeg v1.0 (using IJG JPEG v62), quality = 80 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((8" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?9dyLV 4dv(7'ϩbܟSFOb}Mv1@ 4dv1@ iqFnO'ӶѶshӱFo>}M;mh2}M;mh2N1F4v4Q?U|nS@EhӶѶ iqFnOSN@}M9ih7SFObPr}Mv1@ 4\QN}M&Omm'>SN2}M;mh>}M;b:^}M.(7SG>1@ 49]r(rhb I1@\S=(3SF(QePE?bEIIe(IFq܏ktK$9>*qnlSgOBblYtg,q{N^/̂:,o$$~d=HA"GIEX8W*"cOpj,U2QO.G1OQ~)p=(:)E?b*LJ0(^m)e7Xs>#ծ;BB{䏩4Ԯ:.r"URKȢB$@O#SexNm5ppLB F?K{QLi,nC HM;,Qb"qF(bTXF*]b(K6`".*LQ,X&)qEr*MQ`%#M1^M&mj]mѶFmvѶ"Fڗmh-mvѶ"j]mъmh-mH:+-k{hۉ`0T)Ǒ)7a؃mR[F@rXo[i(Rm#u's7דhV׭ԟe.?xywk)nE;WMدmNTcSϔ؏Op+6*109vL[+Z*c\! )t0Aֽ3á|Ñ7JeuӒWe-q㇑yLwb&[bvnb[8$AݽX#ux<}C6XVtoņ=od8Ysx^a飶=`?U\5:6[c`{q}Zk+Io C$ȱƣԞLW2V[hIqKQ;'Q=?yCY;Gf؝폠E*'<);s$L9l~k gў6"ƺ D^yi26ԻhZE:UK1ɦ\4V၇UoP2=mkZty\\{T-?Tf-zFJm* ($–t*HgOO\8!%U=V,D:vqLk;F$cPy+m[N[!l~x &~fBǀ ZhnuP5? :vf;f>rM&vKK ȢH ;U23G66dWn'U-E.6Pm(^EJWI+&E.6[hRmE.6[hRmE.6[hRmE.&9`̬;O_.>Z߽(tm%gk,ߪ(P_$`7XH;Uγ˷C+O.bx5[h[GҼK Wa6wo9,E7B?\LHW2Ў E}7|Ag;%Ȓ+nx#q\ׇK6Z4Bܫ qߨn~g.H*ø# nEwd1ʌUS >C07IH3,_'WA(mS2Q {ޣZ?#RƎRnOwmбs5m9uOirq /}#Ɔ{ibiaY^6*WS]zi~dy, sЄsޱ.[T5(_I~X^)Br/p0~?QTn`y`9bHO$`7!fVCцOl;76ZZ Q>#`sp: H4[zFړw?aY8='hHŞ9H4˶; e<,l|S*ˢk#x\3.fϨL#BFF=X &}Z6G ȧ搑daֺ^B4x5;_[l8o9t:K5-Jq A4TFtϥm\Fe FzԞk˵ ۭFowQSeM>3$olr?6b%^{eB)O#oo#l㵏1T"Υ}rgEm+U5P ¤c1F*Օmbi$ER^A?Ϸ˯42A 2 w=6u8 He='r.V܀?>fMJ诇3 ^X8) i[ٖowpFGݕSKI Dp2}QCXm*a}AM<-*@eKRm3'01qFSsҔ:Ndg&h`9zedq3Jv^M2 x yVV =n%ZYwOݩcM?²i2eFPm;<%}yu CZrL1=wsݏ$3XXmvH(9VF W6.pKT*#TPKzǒiy:fK q) vp;.6եbHF*]m",RFr*MPb#F(Qf(?F*LRbLT)qRbRbbMofZhVxW8D5qi7:vs"&"UvR`N ֦MlR]O<>ttEGr>U隥ޚX[8܏{=p+LM)8Z[Ov׽s(}v[mo>>Me N(q1V/t}< uSnWMCi 5j~/5 ,dK LDQ ['=] $AV?F1M)Vf9bWHq%;Ers.I!PI;#V>s`cHҺm{Iγᘖ+]*0L{CƦRC$2*0J랇5oC兼(|d$bpIRON;T?z1xզ]s?Ss]Kݣ"I-ڮMaH#8~< GWmyVeT"`&рVd&.xvhګj$ZH7 wwֳ^Q ?UPsTr};/ޮm{g}My|HN20TdE )"#QI}NO~+G2 㟭ex2jַ5E*֩X'7ed7CYlv(AkQk?iYGh-r9Sk'pj1@~(b$ɣ94`Qn(;b1NbQf\SF( X. rE4FdRzҷ5J ?Z򟈷 &g_DBaꚝqWs\:;GVxs0taف?>^grz=II-00,NF_p 1MkDi:ʗOb ;a5ڵ?1J>T>v}UEŁnܮv3S\$k&z,aL2t+H΋6#O?j$u r[Iln^|=KOunEra,OMK~1\o立 ߺg2h2i3{ ,G?]zi_t98Mwn3!?]\ޙ_kW-3?d^c3C-kWzu5Cio&¨4I 3LmID?iF*0Rx5v}#^ C8|SPԵ˷Ү&n_ 9,}Ip59#1+g8~# -qdxl$cצ9|V$ikğ=;+ `EBJO9wzMxS7Fn\#\:(Ŀ. N5)DGQXl1nu@S~O^_gۤ^=n z1d2kY4Km.͏٭cG^;RqgΌs-G9EqB<3p 5vuZ:Ff+Gbvd8ztL,LT{`4;]OE摱RW<|u߄FyfuWb \"8;W?奼 rɖP2Fy ){×g 2&EwC ʯxVHWjor\C=?O߸\LyoECOW|u{\_+Ԯ!#b%6] 9o bd*pu?I-?k/d)0%UF>מΘu Gx_ֻ(u+GUBsobK'No\aA`%9d-5]?g>:fq#M!9%y?:^طꚎev c_?:u%m{n"-?#yZvgh JԊU1v)@欑ъSEQ@(P1EbQEo."{"1RM,V<,RGV={-:4ʱ%CkVi'H4:-pN2qer"O‡Q]| 9t dU/ j39jˑ#YxA%7]Ÿ7$ź8# Mܴu, kŨ9`?_\kH$`t֮cz$JlG3cuAWCMV-fQٸ>jHIG,+Et빭Vs ~'8Z.Ҵtms\Y4B2B+`1rqT/#>#O!q/=EI;/FYF 6H*Frnr >S$m$fkvU9=c[dWNP˝רivl:KvM|>1ҵ<}g=KMg$My8 (`4mEX(s;E_-3;^u&\\]O 93+o^7|2ۡ#L \Ιi-a$v{_R4纷q֨tlpy_\MrZq+`u':=b4F&xO1c\94bM^αRFTg& ]J>k%Ŕl*X}zuVcYO7ogiRܯ촕K}+yN:0;=zכI Zs]"W-sh 1۷:O \%ΒA:?[ahdz(=H5|w{-4Vr2 m Hl+¨ $¿_ʛ3#>3" dWD-xͺUg˛X~!AX1oǏj -.@d׆ޙks$O Csc9uPj?Q%M?s&v2z.# \#v t=4}KDZ[+W/eu` %dHe,} rFpx4yڔ1L>Xp[je`uI<dZ8V8_R]*)bcq\xNPY&-%~I;W>z@h;[dmSwd`O|qu#1W!Լ(å߮{o1K2%{{ }Hc dx>S@ D -)/KiaCMjPk%Z2 =r΂Bٴ;DkG#,¸g_}$z_kj`; owvQpJCD>Q@݁wɐdI+/ =E,!?zwO@jco}GRӚg%=Kp<rzltdWPRdu?B5亵fa# s3YwԒE#8?y yLol>T#v'\uƿjBYnF==wG4?~O5'FX@ʁy.#|qRH9#~ 2\  ǘ#gMʣ0~}i43{tӨIzo5/:Xh' 0GIt _ʏ cIs>":dKR?wb9j6c(ڵm,3ģɑ,pGx}zzֿ]h v2=S u/jI!7\ˎs?+5dWFiXgO|z 1v#i_*sf7$}+5[Z_&Y[E GFjvԿV j8dh?,pMADЩ;$uRs@SejWkWAb + O77iq8"71V )|i+DF6?!__5k0hcrhY:M,~Srk n^S[o*+ƻ.-_pbAmچ.Tb̞c@PSV|K<:fɄb7΀mk"|&O(& `c-]vcu oO+I7c*[lzYOs; <ʸ ޵5{_oa?sxyi? ex<'acY\n\2R9 ypZ=4]\6yY>Vzԭ$QbLE;a󯪞GQi̿jx&zr? 1HBNC/Պ^ՠYﹳt6Wybb_2Nv*WK}N\[uhp;}}˘55͛8v8ߵ[ vkGĐG'ɴ͜p`:V_R gVke_}X&F+=~swg-~yXsE}>YeB $ wA0u,i*31dٿqM2O T@;9l<#s:])Ey %Oa?LsY@sGo=>wY äIW[T 9 7#zVŦ\#1FI1ֹ [W^"ծK[Y~sr_?,hH=7\sm`!{0#2XHjvcV.OL@_wWke$7df NNuIutÚ :?1GQ?wS?#jחzeE_]8.02NA*|?{.G5ky$ȑ:4=x]_ wde8ƲG9'?ˍ?EVR͝|Ԑ y`=tR6=6&{&x'Uh~)_t񥦝qY1жA{= R.ldn'v}J.k6VO_0˞>+^Ǿ'mjo˃ Nnv;W=sÖɩkm"Ν "CXl'ϭ 0u0[# 285 Ɨ)yHqrxfO{ 6ldO,iw#;I&G}=m茿09!Pvx#S^Tm}4}# )g KKn FľcGHt WX?Nȕ`?jpv_ 35uh_*?Ƌ\ ivZlؖQnK)RZּN5OC#Fd00 |\Γ uEѮx&l H|kLfhi~W%m46|O~"9$ 1_wqnI @d!I;){O' wiYRNM5qwQU;=M%QP,fɷqQ܁>]i#޹tЃ~5ZZ܎N3b?CUN2?$c]:K zIkwm{!}wH?YLӴk-,?ј6o4:Ey8jN*th7/.ۇ# ʒɏ|R~Mu!g9*mdBr^v-녦Iw#,+2`>xa 34S[1FN -q<O*TO˪'(?fvyqk!HX[ciΫWQ[X-JK1#WYxN]OMMG_Hє(㆔qaH\ 1A7T5B Mfj ۖK|dy=cޣ]ҝ39m@ 8E$@G%‹c[13Y|%m<5L|cs-W/elnLZf싞~XƓcv1XH(A!Ԯe$#s;k- iʓΕT.#sUtnpἫ䅳봑x&i"PeH=,4KlZI$s cwŜ@* |1DqЌ#gwZj ql) oSahm8S?*+-ZZu a#G_Ju<,W2؊?pme91ïݔ ?,<2\,PKoBOW.]#6;V~ᗆ_ ӌGg=0,>=_g_|P{=,2Qdvk%0Li8f9kS~/T-&EœnZ=s@+d`AN2}9"Dcx" )f̸dG(<}\mYM򤩎D[a++ᔌضPDaԬ2>~|W[ekvSj%2c^o1Xu `lAV͝+wZI<;=rz-K>]l4{k[HՙG{ 5ީ e$ R3i+2ص"Dr@0* wX&d2-;zS`;v6ZXY.o!vWZ:G9"9X_[ ?,VΝ6wɐr^O r^BU [RH,bs8v׸bLorOrwQCH"DGĎhW3 I C}M܎}GW.=o "Ub0N$U#Tm.)Mldo߀ڿӠ#Z0ZXJGBK@\v횻cCw*[HnLkg8@qVb-GnA,as= Oy[HbصuFR2&rz~{$7,mGOOBmjkQ8**G%㳈ŧ Au5$Iɤ$Q@4y4fE3p@oKor諞&[4Ju9m y'$_[fm#>`z ľFz55?Mm7fZZۤ,~ 5~`/QE~FcN4Fx?W::'a0?)X vq\+Ed FqPH<c_lx<ʏMm33zM[1!,9a`mc6_%7<-babXzzV,bK(Ze6f)?V|z>ԯkEe ti [˭3SwE 0>\?_Ynt{#B\C#ذ{;5)N$fUS+ۋ`?l%K uu9fbʨH](}j'&KN8Q񷉆n&]8(bIr9 ~ϋ[rA!h`Ve`yL>aH9څB`3z\k&pH|^=IZ|O RYZ Qm֥zdW#prCLj/yvć;}Os]HF%4P}vj[f.'ߞec'ֺݙ)걢4YA>N?,_< ʑxKKjPDZۂiCi8[?ަt;JK`7G]Ne"Tl8Ì\,p Ӹ, cv>7@D %f~mx TSxQfLUЕ<$$v=>֡y?8imo{igc< D܊Ouoܓ'?8c'{TFNMx0j ι I&3+e [;}::ϥ]jyJ3@,\yJWѯ<ɴUx8{fDh-@IwS48n%g)hPԽ$e>^=9׊um{ė&$V*T:Ptдɮo"̒00*?°c#\gm  lo\EMo!IcӯNX%Yd9fR+ yyPje;KW/Pɴz,_S&iPEkm$nC;&ryՙ#4r6 ߑ2k +Cq`a5|)2_[['rTz 9,,q ,zּalnF HMzQ<7D-\}?fib{ morY?{ d` t5iEXw}C}V[vhGsl7%?盏ukrLu-74f@M(<[Iij."z@3'C?__ƚTM%&ʷZm6>l?FL2:{8?1Q<rs]D yeuhUJNH~W)Q$|d}ZVhg(Қ=lϟ-}?βn˕$RqL?Ydr/ތoQUoNwi]ɶ %cہӣ[ FVۆ6Ԛvjzڗvܪ@ xԴ)W~/pOCsE}b$Kens1[z.faO[ ;͐%E>X GPd˶Bw9+ SX:@c5r"F S=ɮDٲv@dW3iq'Kmui*dEU!l+4==X6+d|8?0 ]{[{-9aL6 " Lih>5>o/^KO"Sްucy6F-`SO±HF 䃕Onx?IjZǵ8 cR8NeUur۷sۺm?{#eHz`'ރo/xd~TX.]BYe ygt+YI)qpzWټ)ũh,d*s׀^7ߕt0ĦKUUUFGx&CLkҦq,zeʉJ HW~Sm;:o|+tMceKk%x\!8!sx'V͡msJ8{(ivp3ހ%ѥ:NH-!pVmw~u^ݫ7>f{km>~z}_úcv9b=Iܿϡ9Py';&۩*̌ː8^W^'J mzlBIjqmq*b 0>ԓۯ!m(^^u]*3V8p';#֏im#B䬇ЧFEuާo41j ivѪ./W08I_P+t;ukЇgt뛛=bO6:n z}y8/0ĶP?9cBo Q.o #C{g;!~y^Xb{u `TV#"~f̋'vGPXF?ҧ6.' X4:[\MEea2+ K'1bB8>WKç3FC~|biBܠ57UJ偠Mf̚d'A!? ЙnZ9b2]Z?Htn'9:V ˦_teʘnc=o!6 p@dq}EF8^(ѥ6k$3K=O8ۑPUId5=ZT`Pyjp<ǓQ֣jcFPmPLG DVHeV-YaL+@Y*&J£e+ Q:U֎cd@µ* cjʱEwsUFI5uo.܃m;ƧWA"@vK"o}kНrcm#6[7n'ҦҤLKH|e^7Y2ȭ9d|-gU@l$e?C<`jl;ᾲuZ\h R2Br0acb{5g.͎E a>J" E{ErU;OԮ#$۠'IeF4B2^*/جLqXbK_&wڅZ,k./a" pH`sm0~bM(Q92#z֬4طOSO(@¹fQ$UsTһiijji0TaD<6{g^r#ZK=Ek[˺[ˈ}}FGruY%ӶʼnGO+Ҽ@𾺿+Bǟ@Ce6:՘$,-İs{jy>/%`k{Gλf_,rvzLgݜ5xX·Ǩ37?'sRY΄K!tjgSp_cU+v HU*uf/oa3I]>ui9!dvQ] iq+/)V@7=Ϯl\KHDUGh!0qҡ{|k\iQI]ڲtcE=}xY~qq0!}GZʸЈɉ49.*'!Ci >$er}a+6<⻟7DVc5Y46,!<=QkiW|S/]O.ձuߕuE5I-a=MOk,77ZQ.aǣ kZ)f;8dxzz;Rm`1da?pilv4a+(=) Q`9fT?<~@ƿtz~ӊ,?XO@z+Y9ơ{9Cja]smKnSiRĝ}*\22};4Lr=G45k4E~3@ I+n rMO³I2/ ~B# qM,8mF9n-م^J?urGy5?ι֑yf<z3}7z P{ -݃\tu<QV,zJȪ2E0`M5#HE1ȦeSJZiZVV )M+V Jr-Z)Ld ijJBB*McZB mR hVRtM<տ..\J<{U.FC]&|PGd_+*ݽ+In5ׄ9 j *ofF+P0 tҘ)FL~_5q+EAmEe O}XKІ#5^]>&V]0XUi-g=`1ڋS$0K%H0UIteO+ZF ߌ׸5IifB֤?v3ɄOgtIfCѢV,R==Kaj!<$2S!Y#bjD*5JxE0jB=PJJB=(4݇Ҋ( JiCQE44PJnE4} PlEl}(e.j(oP!vJO,(a1QEg'EBҘc4QLC qҘP(cZ>:TMJ(6CE Bғ>Q@ivJ(';gP>!B'=)=,~*3J( =e(?geometry/vignettes/qhull/html/qh--cone.gif0000644000176200001440000000560213431000556020304 0ustar liggesusersGIF87add[~.TopPZ OXa>Uj3.OP*$?C1Lk5k4'#?9$k)KTRn(7+-aV_"/*&%Z@K\!JZ W=:5Q7]/F@(Y()]/D_)$&Kb>2;<}M7%,'X{-NY zvsUu*$j(f2?S=Q"9-\N:?I^C]B\AOz1L[!<6^caqpm_0T^"S\!86Oq*1.$PBCK$4''TVFEO:PRW.OQ(%MOk5?9LX >9&!608&U3+j>=}IU_"L`#E-$!T;QBGcZ{-2i*b>P:3-2-OP6%ikj3Q($qMY $j)put(D\01#:{O]C%.;B/)*"+{~{M5R\!QZ %FK33<520[@G3PD{7%k)LSSMq,-a.6/21.]B$#Xz[<=>9$g+<7Ls.HP_0Z*F?D^I.Qt,#TQM4=;$$GdQBAGCH;{K3=)&Q*&8[1/GP*HTVSUb0:)QSNPEd#'34Uw,>8Ne%\ZT'&U^" ::9s;!d*Ea=~L0+)*,dde H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗%-s_ & 5M"#[ u-[4S"ENUr"P:z:uv#gV=Im 6Rf$BտfՙދI^DS:\1 ƘWO/`%9s? بV6\vlr־MZ.!*gf͵ {lᄈ'[C vJ dP0|AnOP N~AL3j )B2nl}Uh8IQa"ލU%b8֝GFs &\hr/k\((y"*fib) h}fib!h&RUh漉Y |)daKLb 4s-R棁}ӌ~@y^PN$tsUɭ*Bd6yv>ݐb(kqъȥ^ Uᰬ4;j8֗C%6;gPNjqT,$ń& qh6@APQpc;LlŸkCM01$z TT`rLx\Wӧئk4U`JjIPǁ*. l% c)ҌTD Ph/qFC`݌fCpƽ+۴+I#uםr/|JG|, $L:|iktsD-P mhJ=ܰOC6 8<hJt9t|-H<VZU `6''C +62"M)uBV"MH>qK^bOGI7c4@nqy|5@%C` Ri`PȜ)Aڙ K1B_x(D!ɮ۳Ce(q!@U J s4iRl1~ޣx&m0K%i D+>>A,LFO@V$=ajD9HIQ#B"O]*1JP$YUZhP| KU@oa#F$C8w10$9h9Z( F_.*I0U0e@&PfR(h;=AGXCLkѪ(7IҀd*j)HBnJZ1Р X0$ 42,@]-@N)H?RN1 zQ1bԢF*Rz<i)XBIJ .@67en$GEP>)?TRufAkhC@m#1J :,([7Pc-4: ZfV& g hcȶ,nvu&3A>WuH\6^uK굳=oA@mڰ@KI :5Tmqt Xx`5X*V 1z $ dmay(01+ Wr 4k:#%Np6@'XU[(aM${Nn3Aվ$ l2ef+.|6M^;^$f @Npcu[h3T^B/ *A@QbKczӅFM hV_{>r.ZZ3Ԧû|a/.4XA g?.."=j0NpF* ф[ tBȫ Νu렧;H D—üh^`XGkYX OH4 o0N" A@\8@bZ sN.p|pЂV @OB^\PF&)!pE@;geometry/vignettes/qhull/html/rbox.html0000644000176200001440000002252513431000557020052 0ustar liggesusers rbox -- generate point distributions

Up: Home page for Qhull
Up: Qhull manual: Table of Contents
To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
To: synopsis • outputs • examples • notes • options


[CONE]rbox -- generate point distributions

rbox generates random or regular points according to the options given, and outputs the points to stdout. The points are generated in a cube, unless 's', 'x', or 'y' are given.

»rbox synopsis

rbox- generate various point distributions.  Default is random in cube.

args (any order, space separated):
  3000    number of random points in cube, lens, spiral, sphere or grid
  D3      dimension 3-d
  c       add a unit cube to the output ('c G2.0' sets size)
  d       add a unit diamond to the output ('d G2.0' sets size)
  l       generate a regular 3-d spiral
  r       generate a regular polygon, ('r s Z1 G0.1' makes a cone)
  s       generate cospherical points
  x       generate random points in simplex, may use 'r' or 'Wn'
  y       same as 'x', plus simplex
  Cn,r,m  add n nearly coincident points within radius r of m points
  Pn,m,r  add point [n,m,r] first, pads with 0

  Ln      lens distribution of radius n.  Also 's', 'r', 'G', 'W'.
  Mn,m,r  lattice (Mesh) rotated by [n,-m,0], [m,n,0], [0,0,r], ...
          '27 M1,0,1' is {0,1,2} x {0,1,2} x {0,1,2}.  Try 'M3,4 z'.
  W0.1    random distribution within 0.1 of the cube's or sphere's surface
  Z0.5 s  random points in a 0.5 disk projected to a sphere
  Z0.5 s G0.6 same as Z0.5 within a 0.6 gap

  Bn      bounding box coordinates, default 0.5
  h       output as homogeneous coordinates for cdd
  n       remove command line from the first line of output
  On      offset coordinates by n
  t       use time as the random number seed (default is command line)
  tn      use n as the random number seed
  z       print integer coordinates, default 'Bn' is 1e+06

»rbox outputs

The format of the output is the following: first line contains the dimension and a comment, second line contains the number of points, and the following lines contain the points, one point per line. Points are represented by their coordinate values.

For example, rbox c 10 D2 generates

2 RBOX c 10 D2
14
-0.4999921736307369 -0.3684622117955817
0.2556053225468894 -0.0413498678629751
0.0327672376602583 -0.2810408135699488
-0.452955383763607 0.17886471718444
0.1792964061529342 0.4346928963760779
-0.1164979223315585 0.01941637230982666
0.3309653464993139 -0.4654278894564396
-0.4465383649305798 0.02970019358182344
0.1711493843897706 -0.4923018137852678
-0.1165843490665633 -0.433157762450313
  -0.5   -0.5
  -0.5    0.5
   0.5   -0.5
   0.5    0.5

»rbox examples

       rbox 10
              10 random points in the unit cube centered  at  the
              origin.

       rbox 10 s D2
              10 random points on a 2-d circle.

       rbox 100 W0
              100 random points on the surface of a cube.

       rbox 1000 s D4
              1000 random points on a 4-d sphere.

       rbox c D5 O0.5
              a 5-d hypercube with one corner at the origin.

       rbox d D10
              a 10-d diamond.

       rbox x 1000 r W0
              100 random points on the surface of a fixed simplex

       rbox y D12
              a 12-d simplex.

       rbox l 10
              10 random points along a spiral

       rbox l 10 r
              10 regular points  along  a  spiral  plus  two  end
              points

       rbox 1000 L10000 D4 s
              1000 random points on the surface of a narrow lens.

           rbox 1000 L100000 s G1e-6
                  1000 random points near the edge of a narrow lens

       rbox c G2 d G3
              a cube with coordinates +2/-2 and  a  diamond  with
              coordinates +3/-3.

       rbox 64 M3,4 z
              a  rotated,  {0,1,2,3} x {0,1,2,3} x {0,1,2,3} lat-
              tice (Mesh) of integer points.

       rbox P0 P0 P0 P0 P0
              5 copies of the origin in 3-d.  Try 'rbox P0 P0  P0
              P0 P0 | qhull QJ'.

       r 100 s Z1 G0.1
              two  cospherical  100-gons plus another cospherical
              point.

       100 s Z1
              a cone of points.

       100 s Z1e-7
              a narrow cone of points with many precision errors.

»rbox notes

Some combinations of arguments generate odd results.

»rbox options

       n      number of points

       Dn     dimension n-d (default 3-d)

       Bn     bounding box coordinates (default 0.5)

       l      spiral distribution, available only in 3-d

       Ln     lens  distribution  of  radius n.  May be used with
              's', 'r', 'G', and 'W'.

       Mn,m,r lattice  (Mesh)  rotated  by  {[n,-m,0],   [m,n,0],
              [0,0,r],  ...}.   Use  'Mm,n'  for a rigid rotation
              with r = sqrt(n^2+m^2).  'M1,0'  is  an  orthogonal
              lattice.   For  example,  '27  M1,0'  is  {0,1,2} x
              {0,1,2} x {0,1,2}.

       s      cospherical points randomly generated in a cube and
              projected to the unit sphere

       x      simplicial  distribution.   It  is fixed for option
              'r'.  May be used with 'W'.

       y      simplicial distribution plus a simplex.   Both  'x'
              and 'y' generate the same points.

       Wn     restrict  points  to distance n of the surface of a
              sphere or a cube

       c      add a unit cube to the output

       c Gm   add a cube with all combinations of +m  and  -m  to
              the output

       d      add a unit diamond to the output.

       d Gm   add a diamond made of 0, +m and -m to the output

       Cn,r,m add n nearly coincident points within radius r of m points

       Pn,m,r add point [n,m,r] to the output first.  Pad coordi-
              nates with 0.0.

       n      Remove the command line from the first line of out-
              put.

       On     offset the data by adding n to each coordinate.

       t      use  time  in  seconds  as  the  random number seed
              (default is command line).

       tn     set the random number seed to n.

       z      generate integer coordinates.  Use 'Bn'  to  change
              the  range.   The  default  is 'B1e6' for six-digit
              coordinates.  In R^4, seven-digit coordinates  will
              overflow hyperplane normalization.

       Zn s   restrict points to a disk about the z+ axis and the
              sphere (default Z1.0).  Includes the opposite pole.
              'Z1e-6'  generates  degenerate  points under single
              precision.

       Zn Gm s
              same as Zn with an empty center (default G0.5).

       r s D2 generate a regular polygon

       r s Z1 G0.1
              generate a regular cone

Up: Home page for Qhull
Up: Qhull manual: Table of Contents
To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
To: synopsis • outputs • examples • notes • options


The Geometry Center Home Page

Comments to: qhull@qhull.org
Created: Sept. 25, 1995 --- Last modified: August 12, 1998

geometry/vignettes/qhull/html/qhull.html0000644000176200001440000004547213431000557020233 0ustar liggesusers qhull -- convex hull and related structures

Up: Home page for Qhull
Up: Qhull manual: Table of Contents
To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
To: synopsis • input • outputs • controls • options


[cone]qhull -- convex hull and related structures

The convex hull of a set of points is the smallest convex set containing the points. The Delaunay triangulation and furthest-site Delaunay triangulation are equivalent to a convex hull in one higher dimension. Halfspace intersection about a point is equivalent to a convex hull by polar duality.

The qhull program provides options to build these structures and to experiment with the process. Use the qconvex, qdelaunay, qhalf, and qvoronoi programs to build specific structures. You may use qhull instead. It takes the same options and uses the same code.

Example: rbox 1000 D3 | qhull C-1e-4 FO Ts
Compute the 3-d convex hull of 1000 random points. Centrums must be 10^-4 below neighboring hyperplanes. Print the options and precision constants. When done, print statistics. These options may be used with any of the Qhull programs.
 
Example: rbox 1000 D3 | qhull d Qbb R1e-4 Q0
Compute the 3-d Delaunay triangulation of 1000 random points. Randomly perturb all calculations by [0.9999,1.0001]. Do not correct precision problems. This leads to serious precision errors.

Use the following equivalences when calling qhull in 2-d to 4-d (a 3-d Delaunay triangulation is a 4-d convex hull):

Use the following equivalences when calling qhull in 5-d and higher (a 4-d Delaunay triangulation is a 5-d convex hull):

By default, Qhull merges coplanar facets. For example, the convex hull of a cube's vertices has six facets.

If you use 'Qt' (triangulated output), all facets will be simplicial (e.g., triangles in 2-d). For the cube example, it will have 12 facets. Some facets may be degenerate and have zero area.

If you use 'QJ' (joggled input), all facets will be simplicial. The corresponding vertices will be slightly perturbed. Joggled input is less accurate that triangulated output.See Merged facets or joggled input.

The output for 4-d convex hulls may be confusing if the convex hull contains non-simplicial facets (e.g., a hypercube). See Why are there extra points in a 4-d or higher convex hull?

Copyright © 1995-2018 C.B. Barber


»qhull synopsis

qhull- compute convex hulls and related structures.
    input (stdin): dimension, n, point coordinates
    comments start with a non-numeric character
    halfspace: use dim+1 and put offsets after coefficients

options (qh-quick.html):
    d    - Delaunay triangulation by lifting points to a paraboloid
    d Qu - furthest-site Delaunay triangulation (upper convex hull)
    v    - Voronoi diagram as the dual of the Delaunay triangulation
    v Qu - furthest-site Voronoi diagram
    H1,1 - Halfspace intersection about [1,1,0,...] via polar duality
    Qt   - triangulated output
    QJ   - joggle input instead of merging facets
    Tv   - verify result: structure, convexity, and point inclusion
    .    - concise list of all options
    -    - one-line description of all options

Output options (subset):
    s    - summary of results (default)
    i    - vertices incident to each facet
    n    - normals with offsets
    p    - vertex coordinates (if 'Qc', includes coplanar points)
           if 'v', Voronoi vertices
    Fp   - halfspace intersections
    Fx   - extreme points (convex hull vertices)
    FA   - compute total area and volume
    o    - OFF format (if 'v', outputs Voronoi regions)
    G    - Geomview output (2-d, 3-d and 4-d)
    m    - Mathematica output (2-d and 3-d)
    QVn  - print facets that include point n, -n if not
    TO file- output results to file, may be enclosed in single quotes

examples:
    rbox c d D2 | qhull Qc s f Fx | more      rbox 1000 s | qhull Tv s FA
    rbox 10 D2 | qhull d QJ s i TO result     rbox 10 D2 | qhull v Qbb Qt p
    rbox 10 D2 | qhull d Qu QJ m              rbox 10 D2 | qhull v Qu QJ o
    rbox c | qhull n                          rbox c | qhull FV n | qhull H Fp
    rbox d D12 | qhull QR0 FA                 rbox c D7 | qhull FA TF1000
    rbox y 1000 W0 | qhull                    rbox 10 | qhull v QJ o Fv

»qhull input

The input data on stdin consists of:

  • dimension
  • number of points
  • point coordinates

Use I/O redirection (e.g., qhull < data.txt), a pipe (e.g., rbox 10 | qhull), or the 'TI' option (e.g., qhull TI data.txt).

Comments start with a non-numeric character. Error reporting is simpler if there is one point per line. Dimension and number of points may be reversed. For halfspace intersection, an interior point may be prepended (see qhalf input).

Here is the input for computing the convex hull of the unit cube. The output is the normals, one per facet.

rbox c > data

3 RBOX c
8
  -0.5   -0.5   -0.5
  -0.5   -0.5    0.5
  -0.5    0.5   -0.5
  -0.5    0.5    0.5
   0.5   -0.5   -0.5
   0.5   -0.5    0.5
   0.5    0.5   -0.5
   0.5    0.5    0.5

qhull s n < data


Convex hull of 8 points in 3-d:

  Number of vertices: 8
  Number of facets: 6
  Number of non-simplicial facets: 6

Statistics for: RBOX c | QHULL s n

  Number of points processed: 8
  Number of hyperplanes created: 11
  Number of distance tests for qhull: 35
  Number of merged facets: 6
  Number of distance tests for merging: 84
  CPU seconds to compute hull (after input): 0.081

4
6
     0      0     -1   -0.5
     0     -1      0   -0.5
     1      0      0   -0.5
    -1      0      0   -0.5
     0      1      0   -0.5
     0      0      1   -0.5

»qhull outputs

These options control the output of qhull. They may be used individually or together.

 
General
qhull
compute the convex hull of the input points. See qconvex.
qhull d Qbb
compute the Delaunay triangulation by lifting the points to a paraboloid. Use option 'Qbb' to scale the paraboloid and improve numeric precision. See qdelaunay.
qhull v Qbb
compute the Voronoi diagram by computing the Delaunay triangulation. Use option 'Qbb' to scale the paraboloid and improve numeric precision. See qvoronoi.
qhull H
compute the halfspace intersection about a point via polar duality. The point is below the hyperplane that defines the halfspace. See qhalf.

For a full list of output options see

»qhull controls

For a full list of control options see

»qhull options

qhull- compute convex hulls and related structures.
    http://www.qhull.org

input (stdin):
    first lines: dimension and number of points (or vice-versa).
    other lines: point coordinates, best if one point per line
    comments:    start with a non-numeric character
    halfspaces:  use dim plus one and put offset after coefficients.
                 May be preceded by a single interior point ('H').

options:
    d    - Delaunay triangulation by lifting points to a paraboloid
    d Qu - furthest-site Delaunay triangulation (upper convex hull)
    v    - Voronoi diagram (dual of the Delaunay triangulation)
    v Qu - furthest-site Voronoi diagram
    Hn,n,... - halfspace intersection about point [n,n,0,...]
    Qt   - triangulated output
    QJ   - joggle input instead of merging facets
    Qc   - keep coplanar points with nearest facet
    Qi   - keep interior points with nearest facet

Qhull control options:
    Qbk:n   - scale coord k so that low bound is n
      QBk:n - scale coord k so that upper bound is n (QBk is 0.5)
    QbB  - scale input to unit cube centered at the origin
    Qbb  - scale last coordinate to [0,m] for Delaunay triangulations
    Qbk:0Bk:0 - remove k-th coordinate from input
    QJn  - randomly joggle input in range [-n,n]
    QRn  - random rotation (n=seed, n=0 time, n=-1 time/no rotate)
    Qf   - partition point to furthest outside facet
    Qg   - only build good facets (needs 'QGn', 'QVn', or 'PdD')
    Qm   - only process points that would increase max_outside
    Qr   - process random outside points instead of furthest ones
    Qs   - search all points for the initial simplex
    Qu   - for 'd' or 'v', compute upper hull without point at-infinity
              returns furthest-site Delaunay triangulation
    Qv   - test vertex neighbors for convexity
    Qx   - exact pre-merges (skips coplanar and anglomaniacs facets)
    Qz   - add point-at-infinity to Delaunay triangulation
    QGn  - good facet if visible from point n, -n for not visible
    QVn  - good facet if it includes point n, -n if not
    Q0   - turn off default p remerge with 'C-0'/'Qx'
    Q1     - sort merges by type instead of angle
    Q2   - merge all non-convex at once instead of independent sets
    Q3   - do not merge redundant vertices
    Q4   - avoid old>new merges
    Q5   - do not correct outer planes at end of qhull
    Q6   - do not pre-merge concave or coplanar facets
    Q7   - depth-first processing instead of breadth-first
    Q8   - do not process near-inside points
    Q9   - process furthest of furthest points
    Q10  - no special processing for narrow distributions
    Q11  - copy normals and recompute centrums for tricoplanar facets
    Q12  - do not error on wide merge due to duplicate ridge and nearly coincident points
    Q14  - do not rename vertices that create a duplicate ridge

Trace options:
    T4   - trace at level n, 4=all, 5=mem/gauss, -1= events
    Tc   - check frequently during execution
    Ts   - print statistics
    Tv   - verify result: structure, convexity, and point inclusion
    Tz   - send all output to stdout
    TFn  - report summary when n or more facets created
    TI file - input data from file, no spaces or single quotes
    TO file - output results to file, may be enclosed in single quotes
    TPn  - turn on tracing when point n added to hull
     TMn - turn on tracing at merge n
     TWn - trace merge facets when width > n
    TRn  - rerun qhull n times.  Use with 'QJn'
    TVn  - stop qhull after adding point n, -n for before (see TCn)
     TCn - stop qhull after building cone for point n (see TVn)

Precision options:
    Cn   - radius of centrum (roundoff added).  Merge facets if non-convex
     An  - cosine of maximum angle.  Merge facets if cosine > n or non-convex
           C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge
    En   - max roundoff error for distance computation
    Rn   - randomly perturb computations by a factor of [1-n,1+n]
    Vn   - min distance above plane for a visible facet (default 3C-n or En)
    Un   - max distance below plane for a new, coplanar point (default Vn)
    Wn   - min facet width for outside point (before roundoff, default 2Vn)

Output formats (may be combined; if none, produces a summary to stdout):
    f    - facet dump
    G    - Geomview output (see below)
    i    - vertices incident to each facet
    m    - Mathematica output (2-d and 3-d)
    o    - OFF format (dim, points and facets; Voronoi regions)
    n    - normals with offsets
    p    - vertex coordinates or Voronoi vertices (coplanar points if 'Qc')
    s    - summary (stderr)

More formats:
    Fa   - area for each facet
    FA   - compute total area and volume for option 's'
    Fc   - count plus coplanar points for each facet
           use 'Qc' (default) for coplanar and 'Qi' for interior
    FC   - centrum or Voronoi center for each facet
    Fd   - use cdd format for input (homogeneous with offset first)
    FD   - use cdd format for numeric output (offset first)
    FF   - facet dump without ridges
    Fi   - inner plane for each facet
           for 'v', separating hyperplanes for bounded Voronoi regions
    FI   - ID of each facet
    Fm   - merge count for each facet (511 max)
    FM   - Maple output (2-d and 3-d)
    Fn   - count plus neighboring facets for each facet
    FN   - count plus neighboring facets for each point
    Fo   - outer plane (or max_outside) for each facet
           for 'v', separating hyperplanes for unbounded Voronoi regions
    FO   - options and precision constants
    Fp   - dim, count, and intersection coordinates (halfspace only)
    FP   - nearest vertex and distance for each coplanar point
    FQ   - command used for qhull
    Fs   - summary: #int (8), dimension, #points, tot vertices, tot facets,
                      output: #vertices, #facets, #coplanars, #nonsimplicial
                    #real (2), max outer plane, min vertex
    FS   - sizes:   #int (0)
                    #real(2) tot area, tot volume
    Ft   - triangulation with centrums for non-simplicial facets (OFF format)
    Fv   - count plus vertices for each facet
           for 'v', Voronoi diagram as Voronoi vertices for pairs of sites
    FV   - average of vertices (a feasible point for 'H')
    Fx   - extreme points (in order for 2-d)

Geomview options (2-d, 3-d, and 4-d; 2-d Voronoi)
    Ga   - all points as dots
     Gp  -  coplanar points and vertices as radii
     Gv  -  vertices as spheres
    Gi   - inner planes only
     Gn  -  no planes
     Go  -  outer planes only
    Gc   - centrums
    Gh   - hyperplane intersections
    Gr   - ridges
    GDn  - drop dimension n in 3-d and 4-d output
    Gt   - for 3-d 'd', transparent outer ridges

Print options:
    PAn  - keep n largest facets by area
    Pdk:n - drop facet if normal[k] <= n (default 0.0)
    PDk:n - drop facet if normal[k] >= n
    Pg   - print good facets (needs 'QGn' or 'QVn')
    PFn  - keep facets whose area is at least n
    PG   - print neighbors of good facets
    PMn  - keep n facets with most merges
    Po   - force output.  If error, output neighborhood of facet
    Pp   - do not report precision problems

    .    - list of all options
    -    - one line descriptions of all options

Up: Home page for Qhull
Up: Qhull manual: Table of Contents
To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
To: synopsis • input • outputs • controls • options


The Geometry Center Home Page

Comments to: qhull@qhull.org
Created: Sept. 25, 1995 --- Last modified: see top

geometry/vignettes/qhull/html/qh-impre.html0000644000176200001440000010763013431000557020623 0ustar liggesusers Imprecision in Qhull

Up: Home page for Qhull
Up: Qhull manual: Table of Contents
To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
To: Qhull imprecision: Table of Contents (please wait while loading)


[4-d cube] Imprecision in Qhull

This section of the Qhull manual discusses the problems caused by coplanar points and why Qhull uses options 'C-0' or 'Qx' by default. If you ignore precision issues with option 'Q0', the output from Qhull can be arbitrarily bad. Qhull avoids precision problems if you merge facets (the default) or joggle the input ('QJ').

Use option 'Tv' to verify the output from Qhull. It verifies that adjacent facets are clearly convex. It verifies that all points are on or below all facets.

Qhull automatically tests for convexity if it detects precision errors while constructing the hull.

Copyright © 1995-2018 C.B. Barber


»Qhull imprecision: Table of Contents


»Precision problems

Since Qhull uses floating point arithmetic, roundoff error occurs with each calculation. This causes problems for geometric algorithms. Other floating point codes for convex hulls, Delaunay triangulations, and Voronoi diagrams also suffer from these problems. Qhull handles most of them.

There are several kinds of precision errors:

  • Representation error occurs when there are not enough digits to represent a number, e.g., 1/3.
  • Measurement error occurs when the input coordinates are from measurements.
  • Roundoff error occurs when a calculation is rounded to a fixed number of digits, e.g., a floating point calculation.
  • Approximation error occurs when the user wants an approximate result because the exact result contains too much detail.

Under imprecision, calculations may return erroneous results. For example, roundoff error can turn a small, positive number into a small, negative number. See Milenkovic ['93] for a discussion of strict robust geometry. Qhull does not meet Milenkovic's criterion for accuracy. Qhull's error bound is empirical instead of theoretical.

Qhull 1.0 checked for precision errors but did not handle them. The output could contain concave facets, facets with inverted orientation ("flipped" facets), more than two facets adjacent to a ridge, and two facets with exactly the same set of vertices.

Qhull 2.4 and later automatically handles errors due to machine round-off. Option 'C-0' or 'Qx' is set by default. In 5-d and higher, the output is clearly convex but an input point could be outside of the hull. This may be corrected by using option 'C-0', but then the output may contain wide facets.

Qhull 2.5 and later provides option 'QJ' to joggled input. Each input coordinate is modified by a small, random quantity. If a precision error occurs, a larger modification is tried. When no precision errors occur, Qhull is done.

Qhull 3.1 and later provides option 'Qt' for triangulated output. This removes the need for joggled input ('QJ'). Non-simplicial facets are triangulated. The facets may have zero area. Triangulated output is particularly useful for Delaunay triangulations.

By handling round-off errors, Qhull can provide a variety of output formats. For example, it can return the halfspace that defines each facet ('n'). The halfspaces include roundoff error. If the halfspaces were exact, their intersection would return the original extreme points. With imprecise halfspaces and exact arithmetic, nearly incident points may be returned for an original extreme point. By handling roundoff error, Qhull returns one intersection point for each of the original extreme points. Qhull may split or merge an extreme point, but this appears to be unlikely.

The following pipe implements the identity function for extreme points (with roundoff):

qconvex FV n | qhalf Fp

Bernd Gartner published his Miniball algorithm ["Fast and robust smallest enclosing balls", Algorithms - ESA '99, LNCS 1643]. It uses floating point arithmetic and a carefully designed primitive operation. It is practical to 20-D or higher, and identifies at least two points on the convex hull of the input set. Like Qhull, it is an incremental algorithm that processes points furthest from the intermediate result and ignores points that are close to the intermediate result.

»Merged facets or joggled input

This section discusses the choice between merged facets and joggled input. By default, Qhull uses merged facets to handle precision problems. With option 'QJ', the input is joggled. See examples of joggled input and triangulated output.

  • Use merged facets (the default) when you want non-simplicial output (e.g., the faces of a cube).
  • Use merged facets and triangulated output ('Qt') when you want simplicial output and coplanar facets (e.g., triangles for a Delaunay triangulation).
  • Use joggled input ('QJ') when you need clearly-convex, simplicial output.

The choice between merged facets and joggled input depends on the application. Both run about the same speed. Joggled input may be faster if the initial joggle is sufficiently large to avoid precision errors.

Most applications should used merged facets with triangulated output.

Use merged facets (the default, 'C-0') or triangulated output ('Qt') if

  • Your application supports non-simplicial facets, or it allows degenerate, simplicial facets (option 'Qt').
  • You do not want the input modified.
  • You want to set additional options for approximating the hull.
  • You use single precision arithmetic (realT).

Use joggled input ('QJ') if

  • Your application needs clearly convex, simplicial output
  • Your application supports perturbed input points and narrow triangles.
  • Seven significant digits is sufficient accuracy.

You may use both techniques or combine joggle with post-merging ('Cn').

Other researchers have used techniques similar to joggled input. Sullivan and Beichel [ref?] randomly perturb the input before computing the Delaunay triangulation. Corkum and Wyllie [news://comp.graphics, 1990] randomly rotate a polytope before testing point inclusion. Edelsbrunner and Mucke [Symp. Comp. Geo., 1988] and Yap [J. Comp. Sys. Sci., 1990] symbolically perturb the input to remove singularities.

Merged facets ('C-0') handles precision problems directly. If a precision problem occurs, Qhull merges one of the offending facets into one of its neighbors. Since all precision problems in Qhull are associated with one or more facets, Qhull will either fix the problem or attempt to merge the last remaining facets.

»Delaunay triangulations

Programs that use Delaunay triangulations traditionally assume a triangulated input. By default, qdelaunay merges regions with cocircular or cospherical input sites. If you want a simplicial triangulation use triangulated output ('Qt') or joggled input ('QJ').

For Delaunay triangulations, triangulated output should produce good results. All points are within roundoff error of a paraboloid. If two points are nearly incident, one will be a coplanar point. So all points are clearly separated and convex. If qhull reports deleted vertices, the triangulation may contain serious precision faults. Deleted vertices are reported in the summary ('s', 'Fs'

You should use option 'Qbb' with Delaunay triangulations. It scales the last coordinate and may reduce roundoff error. It is automatically set for qdelaunay, qvoronoi, and option 'QJ'.

Edelsbrunner, H, Geometry and Topology for Mesh Generation, Cambridge University Press, 2001. Good mathematical treatise on Delaunay triangulation and mesh generation for 2-d and 3-d surfaces. The chapter on surface simplification is particularly interesting. It is similar to facet merging in Qhull.

Veron and Leon published an algorithm for shape preserving polyhedral simplification with bounded error [Computers and Graphics, 22.5:565-585, 1998]. It remove nodes using front propagation and multiple remeshing.

»Halfspace intersection

The identity pipe for Qhull reveals some precision questions for halfspace intersections. The identity pipe creates the convex hull of a set of points and intersects the facets' hyperplanes. It should return the input points, but narrow distributions may drop points while offset distributions may add points. It may be better to normalize the input set about the origin. For example, compare the first results with the later two results: [T. Abraham]

rbox 100 s t | tee r | qconvex FV n | qhalf Fp | cat - r | /bin/sort -n | tail
rbox 100 L1e5 t | tee r | qconvex FV n | qhalf Fp | cat - r | /bin/sort -n | tail
rbox 100 s O10 t | tee r | qconvex FV n | qhalf Fp | cat - r | /bin/sort -n | tail

»Merged facets

Qhull detects precision problems when computing distances. A precision problem occurs if the distance computation is less than the maximum roundoff error. Qhull treats the result of a hyperplane computation as if it were exact.

Qhull handles precision problems by merging non-convex facets. The result of merging two facets is a thick facet defined by an inner plane and an outer plane. The inner and outer planes are offsets from the facet's hyperplane. The inner plane is clearly below the facet's vertices. At the end of Qhull, the outer planes are clearly above all input points. Any exact convex hull must lie between the inner and outer planes.

Qhull tests for convexity by computing a point for each facet. This point is called the facet's centrum. It is the arithmetic center of the facet's vertices projected to the facet's hyperplane. For simplicial facets with d vertices, the centrum is equivalent to the centroid or center of gravity.

Two neighboring facets are convex if each centrum is clearly below the other hyperplane. The 'Cn' or 'C-n' options sets the centrum's radius to n . A centrum is clearly below a hyperplane if the computed distance from the centrum to the hyperplane is greater than the centrum's radius plus two maximum roundoff errors. Two are required because the centrum can be the maximum roundoff error above its hyperplane and the distance computation can be high by the maximum roundoff error.

With the 'C-n' or 'A-n ' options, Qhull merges non-convex facets while constructing the hull. The remaining facets are clearly convex. With the 'Qx ' option, Qhull merges coplanar facets after constructing the hull. While constructing the hull, it merges coplanar horizon facets, flipped facets, concave facets and duplicated ridges. With 'Qx', coplanar points may be missed, but it appears to be unlikely.

If the user sets the 'An' or 'A-n' option, then all neighboring facets are clearly convex and the maximum (exact) cosine of an angle is n.

If 'C-0' or 'Qx' is used without other precision options (default), Qhull tests vertices instead of centrums for adjacent simplices. In 3-d, if simplex abc is adjacent to simplex bcd, Qhull tests that vertex a is clearly below simplex bcd , and vertex d is clearly below simplex abc. When building the hull, Qhull tests vertices if the horizon is simplicial and no merges occur.

»How Qhull merges facets

If two facets are not clearly convex, then Qhull removes one or the other facet by merging the facet into a neighbor. It selects the merge which minimizes the distance from the neighboring hyperplane to the facet's vertices. Qhull also performs merges when a facet has fewer than d neighbors (called a degenerate facet), when a facet's vertices are included in a neighboring facet's vertices (called a redundant facet), when a facet's orientation is flipped, or when a ridge occurs between more than two facets.

Qhull performs merges in a series of passes sorted by merge angle. Each pass merges those facets which haven't already been merged in that pass. After a pass, Qhull checks for redundant vertices. For example, if a vertex has only two neighbors in 3-d, the vertex is redundant and Qhull merges it into an adjacent vertex.

Merging two simplicial facets creates a non-simplicial facet of d+1 vertices. Additional merges create larger facets. When merging facet A into facet B, Qhull retains facet B's hyperplane. It merges the vertices, neighbors, and ridges of both facets. It recomputes the centrum if a wide merge has not occurred (qh_WIDEcoplanar) and the number of extra vertices is smaller than a constant (qh_MAXnewcentrum).

»Limitations of merged facets

  • Uneven dimensions -- If one coordinate has a larger absolute value than other coordinates, it may dominate the effect of roundoff errors on distance computations. You may use option 'QbB' to scale points to the unit cube. For Delaunay triangulations and Voronoi diagrams, qdelaunay and qvoronoi always set option 'Qbb'. It scales the last coordinate to [0,m] where m is the maximum width of the other coordinates. Option 'Qbb' is needed for Delaunay triangulations of integer coordinates and nearly cocircular points.

    For example, compare

            rbox 1000 W0 t | qconvex Qb2:-1e-14B2:1e-14
    
    with
            rbox 1000 W0 t | qconvex
    
    The distributions are the same but the first is compressed to a 2e-14 slab.

  • Post-merging of coplanar facets -- In 5-d and higher, option 'Qx' (default) delays merging of coplanar facets until post-merging. This may allow "dents" to occur in the intermediate convex hulls. A point may be poorly partitioned and force a poor approximation. See option 'Qx' for further discussion.

    This is difficult to produce in 5-d and higher. Option 'Q6' turns off merging of concave facets. This is similar to 'Qx'. It may lead to serious precision errors, for example,

            rbox 10000 W1e-13  | qhull Q6  Tv
    

  • Maximum facet width -- Qhull reports the maximum outer plane and inner planes (if more than roundoff error apart). There is no upper bound for either figure. This is an area for further research. Qhull does a good job of post-merging in all dimensions. Qhull does a good job of pre-merging in 2-d, 3-d, and 4-d. With the 'Qx' option, it does a good job in higher dimensions. In 5-d and higher, Qhull does poorly at detecting redundant vertices.

    In the summary ('s'), look at the ratio between the maximum facet width and the maximum width of a single merge, e.g., "(3.4x)". Qhull usually reports a ratio of four or lower in 3-d and six or lower in 4-d. If it reports a ratio greater than 10, this may indicate an implementation error. Narrow distributions (see following) may produce wide facets.

    For example, if special processing for narrow distributions is turned off ('Q10'), qhull may produce a wide facet:

             rbox 1000 L100000 s G1e-16 t1002074964 | qhull Tv Q10
    

  • Narrow distribution -- In 3-d, a narrow distribution may result in a poor approximation. For example, if you do not use qdelaunay nor option 'Qbb', the furthest-site Delaunay triangulation of nearly cocircular points may produce a poor approximation:
             rbox s 5000 W1e-13 D2 t1002151341 | qhull d Qt
             rbox 1000 s W1e-13 t1002231672 | qhull d Tv
    

    During construction of the hull, a point may be above two facets with opposite orientations that span the input set. Even though the point may be nearly coplanar with both facets, and can be distant from the precise convex hull of the input sites. Additional facets leave the point distant from a facet. To fix this problem, add option 'Qbb' (it scales the last coordinate). Option 'Qbb' is automatically set for qdelaunay and qvoronoi.

    Qhull generates a warning if the initial simplex is narrow. For narrow distributions, Qhull changes how it processes coplanar points -- it does not make a point coplanar until the hull is finished. Use option 'Q10' to try Qhull without special processing for narrow distributions. For example, special processing is needed for:

             rbox 1000 L100000 s G1e-16 t1002074964 | qhull Tv Q10
    

    You may turn off the warning message by reducing qh_WARNnarrow in user.h or by setting option 'Pp'.

    Similar problems occur for distributions with a large flat facet surrounded with many small facet at a sharp angle to the large facet. Qhull 3.1 fixes most of these problems, but a poor approximation can occur. A point may be left outside of the convex hull ('Tv'). Examples include the furthest-site Delaunay triangulation of nearly cocircular points plus the origin, and the convex hull of a cone of nearly cocircular points. The width of the band is 10^-13.

            rbox s 1000 W1e-13 P0 D2 t996799242 | qhull d Tv
            rbox 1000 s Z1 G1e-13 t1002152123 | qhull Tv
            rbox 1000 s Z1 G1e-13 t1002231668 | qhull Tv
    

  • Quadratic running time -- If the output contains large, non-simplicial facets, the running time for Qhull may be quadratic in the size of the triangulated output. For example, rbox 1000 s W1e-13 c G2 | qhull d is 4 times faster for 500 points. The convex hull contains two large nearly spherical facets and many nearly coplanar facets. Each new point retriangulates the spherical facet and repartitions the remaining points into all of the nearly coplanar facets. In this case, quadratic running time is avoided if you use qdelaunay, add option 'Qbb', or add the origin ('P0') to the input.

  • Nearly coincident points within 1e-13 -- Multiple, nearly coincident points within a 1e-13 ball of points in the unit cube may lead to wide facets or quadratic running time. For example, the convex hull a 1000 coincident, cospherical points in 4-D, or the 3-D Delaunay triangulation of nearly coincident points, may lead to very wide facets (e.g., 2267021951.3x).

    For Delaunay triangulations, the problem typically occurs for extreme points of the input set (i.e., on the edge between the upper and lower convex hull). After multiple facet merges, four facets may share the same, duplicate ridge and must be merged. Some of these facets may be long and narrow, leading to a very wide merged facet. If so, error QH6271 is reported. It may be overriden with option 'Q12'.

    Duplicate ridges occur when the horizon facets for a new point is "pinched". In a duplicate ridge, a subridge (e.g., a line segment in 3-d) is shared by two horizon facets. At least two of its vertices are nearly coincident. In poly_r.c, qh_matchnewfacets calls qh_matchneighbor. qh_matchneighbor identifies duplicate ridges and marks the corresponding facets. . In merge_r.c, qh_mark_dupridges identifies facets for merging across a duplicate ridge. qh_forcedmerges merges these facets. It checks for wide merges with qh_check_dupridge.

    It is easy to generate coincident points with option 'Cn,r,m' of rbox. It generates n points within an r ball for each of m input sites. For example, every point of the following distributions has a nearly coincident point within a 1e-13 ball. Substantially smaller or larger balls do not lead to pinched horizons.

            rbox 1000 C1,1e-13 D4 s t | qhull
            rbox 75 C1,1e-13 t | qhull d
    
    For Delaunay triangulations, a bounding box may alleviate this error (e.g., rbox 500 C1,1E-13 t c G1 | qhull d). A later release of qhull will avoid pinched horizons by merging duplicate subridges. A subridge is merged by merging adjacent vertices.

  • Facet with zero-area -- It is possible for a zero-area facet to be convex with its neighbors. This can occur if the hyperplanes of neighboring facets are above the facet's centrum, and the facet's hyperplane is above the neighboring centrums. Qhull computes the facet's hyperplane so that it passes through the facet's vertices. The vertices can be collinear.

  • No more facets -- Qhull reports an error if there are d+1 facets left and two of the facets are not clearly convex. This typically occurs when the convexity constraints are too strong or the input points are degenerate. The former is more likely in 5-d and higher -- especially with option 'C-n'.

  • Deleted cone -- Lots of merging can end up deleting all of the new facets for a point. This is a rare event that has only been seen while debugging the code.

  • Triangulated output leads to precision problems -- With sufficient merging, the ridges of a non-simplicial facet may have serious topological and geometric problems. A ridge may be between more than two neighboring facets. If so, their triangulation ('Qt') will fail since two facets have the same vertex set. Furthermore, a triangulated facet may have flipped orientation compared to its neighbors.
  • The triangulation process detects degenerate facets with only two neighbors. These are marked degenerate. They have zero area.

  • Coplanar points -- Option 'Qc' is determined by qh_check_maxout() after constructing the hull. Qhull needs to retain all possible coplanar points in the facets' coplanar sets. This depends on qh_RATIOnearInside in user.h. Furthermore, the cutoff for a coplanar point is arbitrarily set at the minimum vertex. If coplanar points are important to your application, remove the interior points by hand (set 'Qc Qi') or make qh_RATIOnearInside sufficiently large.

  • Maximum roundoff error -- Qhull computes the maximum roundoff error from the maximum coordinates of the point set. Usually the maximum roundoff error is a reasonable choice for all distance computations. The maximum roundoff error could be computed separately for each point or for each distance computation. This is expensive and it conflicts with option 'C-n'.

  • All flipped or upper Delaunay -- When a lot of merging occurs for Delaunay triangulations, a new point may lead to no good facets. For example, try a strong convexity constraint:
            rbox 1000 s t993602376 | qdelaunay C-1e-3
    

»Joggled input

Joggled input is a simple work-around for precision problems in computational geometry ["joggle: to shake or jar slightly," Amer. Heritage Dictionary]. Other names are jostled input or random perturbation. Qhull joggles the input by modifying each coordinate by a small random quantity. If a precision problem occurs, Qhull joggles the input with a larger quantity and the algorithm is restarted. This process continues until no precision problems occur. Unless all inputs incur precision problems, Qhull will terminate. Qhull adjusts the inner and outer planes to account for the joggled input.

Neither joggle nor merged facets has an upper bound for the width of the output facets, but both methods work well in practice. Joggled input is easier to justify. Precision errors occur when the points are nearly singular. For example, four points may be coplanar or three points may be collinear. Consider a line and an incident point. A precision error occurs if the point is within some epsilon of the line. Now joggle the point away from the line by a small, uniformly distributed, random quantity. If the point is changed by more than epsilon, the precision error is avoided. The probability of this event depends on the maximum joggle. Once the maximum joggle is larger than epsilon, doubling the maximum joggle will halve the probability of a precision error.

With actual data, an analysis would need to account for each point changing independently and other computations. It is easier to determine the probabilities empirically ('TRn') . For example, consider computing the convex hull of the unit cube centered on the origin. The arithmetic has 16 significant decimal digits.

Convex hull of unit cube

joggle error prob.
1.0e-15 0.983
2.0e-15 0.830
4.0e-15 0.561
8.0e-15 0.325
1.6e-14 0.185
3.2e-14 0.099
6.4e-14 0.051
1.3e-13 0.025
2.6e-13 0.010
5.1e-13 0.004
1.0e-12 0.002
2.0e-12 0.001

A larger joggle is needed for multiple points. Since the number of potential singularities increases, the probability of one or more precision errors increases. Here is an example.

Convex hull of 1000 points on unit cube

joggle error prob.
1.0e-12 0.870
2.0e-12 0.700
4.0e-12 0.450
8.0e-12 0.250
1.6e-11 0.110
3.2e-11 0.065
6.4e-11 0.030
1.3e-10 0.010
2.6e-10 0.008
5.1e-09 0.003

Other distributions behave similarly. No distribution should behave significantly worse. In Euclidean space, the probability measure of all singularities is zero. With floating point numbers, the probability of a singularity is non-zero. With sufficient digits, the probability of a singularity is extremely small for random data. For a sufficiently large joggle, all data is nearly random data.

Qhull uses an initial joggle of 30,000 times the maximum roundoff error for a distance computation. This avoids most potential singularities. If a failure occurs, Qhull retries at the initial joggle (in case bad luck occurred). If it occurs again, Qhull increases the joggle by ten-fold and tries again. This process repeats until the joggle is a hundredth of the width of the input points. Qhull reports an error after 100 attempts. This should never happen with double-precision arithmetic. Once the probability of success is non-zero, the probability of success increases about ten-fold at each iteration. The probability of repeated failures becomes extremely small.

Merged facets produces a significantly better approximation. Empirically, the maximum separation between inner and outer facets is about 30 times the maximum roundoff error for a distance computation. This is about 2,000 times better than joggled input. Most applications though will not notice the difference.

»Exact arithmetic

Exact arithmetic may be used instead of floating point. Singularities such as coplanar points can either be handled directly or the input can be symbolically perturbed. Using exact arithmetic is slower than using floating point arithmetic and the output may take more space. Chaining a sequence of operations increases the time and space required. Some operations are difficult to do.

Clarkson's hull program and Shewchuk's triangle program are practical implementations of exact arithmetic.

Clarkson limits the input precision to about fifteen digits. This reduces the number of nearly singular computations. When a determinant is nearly singular, he uses exact arithmetic to compute a precise result.

»Approximating a convex hull

Qhull may be used for approximating a convex hull. This is particularly valuable in 5-d and higher where hulls can be immense. You can use 'Qx C-n' to merge facets as the hull is being constructed. Then use 'Cn' and/or 'An' to merge small facets during post-processing. You can print the n largest facets with option 'PAn'. You can print facets whose area is at least n with option 'PFn'. You can output the outer planes and an interior point with 'FV Fo' and then compute their intersection with 'qhalf'.

To approximate a convex hull in 6-d and higher, use post-merging with 'Wn' (e.g., qhull W1e-1 C1e-2 TF2000). Pre-merging with a convexity constraint (e.g., qhull Qx C-1e-2) often produces a poor approximation or terminates with a simplex. Option 'QbB' may help to spread out the data.

You will need to experiment to determine a satisfactory set of options. Use rbox to generate test sets quickly and Geomview to view the results. You will probably want to write your own driver for Qhull using the Qhull library. For example, you could select the largest facet in each quadrant.


Up: Home page for Qhull
Up: Qhull manual: Table of Contents
To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
To: Qhull imprecision: Table of Contents


The Geometry Center Home Page

Comments to: qhull@qhull.org
Created: Sept. 25, 1995 --- Last modified: see top

geometry/vignettes/qhull/html/qh--geom.gif0000644000176200001440000000047613431000556020313 0ustar liggesusersGIF87a((LLL,((0Iٸ8'\Bl벧4_ @}9@,DgWl:{dPpZN^^F̈́a!a5gp'n}~Cil|Ui~WuJO_~LVqALrePnkq2g;[hkvkhƨe^j|tJxpFSz|c+@]   #WĊT Ə(^I2E;geometry/vignettes/qhull/html/qh-opto.html0000644000176200001440000003547213431000557020474 0ustar liggesusers Qhull output options

Up: Home page for Qhull
Up: Qhull manual: Table of Contents
To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions


[delaunay] Qhull output options

This section lists the output options for Qhull. These options are indicated by lower case characters. See Formats, Print, and Geomview for other output options.

Copyright © 1995-2018 C.B. Barber


» Programs OptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions

Output options

Qhull prints its output to standard out. All output is printed text. The default output is a summary (option 's'). Other outputs may be specified as follows.

f
print all fields of all facets
n
print hyperplane normals with offsets
m
print Mathematica output (2-d and 3-d)
o
print OFF file format (dim, points and facets)
s
print summary to stderr
p
print vertex and point coordinates
i
print vertices incident to each facet
 
 
Related options
F
additional input/output formats
G
Geomview output
P
Print options
Ft
print triangulation with added points
 

»f - print all fields of all facets

Print all fields of all facets. The facet is the primary data structure for Qhull.

Option 'f' is for debugging. Most of the fields are available via the 'F' options. If you need specialized information from Qhull, you can use the Qhull library or C++ interface.

Use the 'FF' option to print the facets but not the ridges.

»i - print vertices incident to each facet

The first line is the number of facets. The remaining lines list the vertices for each facet, one facet per line. The indices are 0-relative indices of the corresponding input points. The facets are oriented. Option 'Fv' displays an unoriented list of vertices with a vertex count per line. Options 'o' and 'Ft' displays coordinates for each vertex prior to the vertices for each facet.

Simplicial facets (e.g., triangles in 3-d) consist of d vertices. Non-simplicial facets in 3-d consist of 4 or more vertices. For example, a facet of a cube consists of 4 vertices. Use option 'Qt' to triangulate non-simplicial facets.

For 4-d and higher convex hulls and 3-d and higher Delaunay triangulations, d vertices are listed for all facets. A non-simplicial facet is triangulated with its centrum and each ridge. The index of the centrum is higher than any input point. Use option 'Fv' to list the vertices of non-simplicial facets as is. Use option 'Ft' to print the coordinates of the centrums as well as those of the input points.

»m - print Mathematica output

Qhull writes a Mathematica file for 2-d and 3-d convex hulls, 2-d and 3-d halfspace intersections, and 2-d Delaunay triangulations. Qhull produces a list of objects that you can assign to a variable in Mathematica, for example: "list= << <outputfilename> ". If the object is 2-d, it can be visualized by "Show[Graphics[list]] ". For 3-d objects the command is "Show[Graphics3D[list]] ". Now the object can be manipulated by commands of the form "Show[%, <parametername> -> <newvalue>]".

For Delaunay triangulation orthogonal projection is better. This can be specified, for example, by "BoxRatios: Show[%, BoxRatios -> {1, 1, 1e-8}]". To see the meaningful side of the 3-d object used to visualize 2-d Delaunay, you need to change the viewpoint: "Show[%, ViewPoint -> {0, 0, -1}]". By specifying different viewpoints you can slowly rotate objects.

For halfspace intersections, Qhull produces the dual convex hull.

See Is Qhull available for Mathematica? for URLs.

»n - print hyperplane normals with offsets

The first line is the dimension plus one. The second line is the number of facets. The remaining lines are the normals for each facet, one normal per line. The facet's offset follows its normal coefficients.

The normals point outward, i.e., the convex hull satisfies Ax <= -b where A is the matrix of coefficients and b is the vector of offsets.

A point is inside or below a hyperplane if its distance to the hyperplane is negative. A point is outside or above a hyperplane if its distance to the hyperplane is positive. Otherwise a point is on or coplanar to the hyperplane.

If cdd output is specified ('FD'), Qhull prints the command line, the keyword "begin", the number of facets, the dimension (plus one), the keyword "real", and the normals for each facet. The facet's negative offset precedes its normal coefficients (i.e., if the origin is an interior point, the offset is positive). Qhull ends the output with the keyword "end".

»o - print OFF file format

The output is:

  • The first line is the dimension
  • The second line is the number of points, the number of facets, and the number of ridges.
  • All of the input points follow, one per line.
  • Then Qhull prints the vertices for each facet. Each facet is on a separate line. The first number is the number of vertices. The remainder is the indices of the corresponding points. The vertices are oriented in 2-d, 3-d, and in simplicial facets.

Option 'Ft' prints the same information with added points for non-simplicial facets.

Option 'i' displays vertices without the point coordinates. Option 'p' displays the point coordinates without vertex and facet information.

In 3-d, Geomview can load the file directly if you delete the first line (e.g., by piping through 'tail +2').

For Voronoi diagrams (qvoronoi), option 'o' prints Voronoi vertices and Voronoi regions instead of input points and facets. The first vertex is the infinity vertex [-10.101, -10.101, ...]. Then, option 'o' lists the vertices in the Voronoi region for each input site. The regions appear in site ID order. In 2-d, the vertices of a Voronoi region are sorted by adjacency (non-oriented). In 3-d and higher, the Voronoi vertices are sorted by index. See the 'FN' option for listing Voronoi regions without listing Voronoi vertices.

If you are using the Qhull library, options 'v o' have the side effect of reordering the neighbors for a vertex.

»p - print vertex and point coordinates

The first line is the dimension. The second line is the number of vertices. The remaining lines are the vertices, one vertex per line. A vertex consists of its point coordinates

With the 'Gc' and 'Gi' options, option 'p' also prints coplanar and interior points respectively.

For qvoronoi, it prints the coordinates of each Voronoi vertex.

For qdelaunay, it prints the input sites as lifted to a paraboloid. For qhalf it prints the dual points. For both, option 'p' is the same as the first section of option 'o'.

Use 'Fx' to list the point ids of the extreme points (i.e., vertices).

If a subset of the facets is selected ('Pdk', 'PDk', 'Pg' options), option 'p' only prints vertices and points associated with those facets.

If cdd-output format is selected ('FD'), the first line is "begin". The second line is the number of vertices, the dimension plus one, and "real". The vertices follow with a leading "1". Output ends with "end".

»s - print summary to stderr

The default output of Qhull is a summary to stderr. Options 'FS' and 'Fs' produce the same information for programs. Note: Windows 95 and 98 treats stderr the same as stdout. Use option 'TO file' to separate stderr and stdout.

The summary lists the number of input points, the dimension, the number of vertices in the convex hull, and the number of facets in the convex hull. It lists the number of selected ("good") facets for options 'Pg', 'Pdk', qdelaunay, or qvoronoi (Delaunay triangulations only use the lower half of a convex hull). It lists the number of coplanar points. For Delaunay triangulations without 'Qc', it lists the total number of coplanar points. It lists the number of simplicial facets in the output.

The terminology depends on the output structure.

The summary lists these statistics:

  • number of points processed by Qhull
  • number of hyperplanes created
  • number of distance tests (not counting statistics, summary, and checking)
  • number of merged facets (if any)
  • number of distance tests for merging (if any)
  • CPU seconds to compute the hull
  • the maximum joggle for 'QJ'
    or, the probability of precision errors for 'QJ TRn'
  • total area and volume (if computed, see 'FS' 'FA' 'Fa' 'PAn')
  • max. distance of a point above a facet (if non-zero)
  • max. distance of a vertex below a facet (if non-zero)

The statistics include intermediate hulls. For example 'rbox d D4 | qhull' reports merged facets even though the final hull is simplicial.

Qhull starts counting CPU seconds after it has read and projected the input points. It stops counting before producing output. In the code, CPU seconds measures the execution time of function qhull() in libqhull.c. If the number of CPU seconds is clearly wrong, check qh_SECticks in user.h.

The last two figures measure the maximum distance from a point or vertex to a facet. They are not printed if less than roundoff or if not merging. They account for roundoff error in computing the distance (c.f., option 'Rn'). Use 'Fs' to report the maximum outer and inner plane.

A number may appear in parentheses after the maximum distance (e.g., 2.1x). It is the ratio between the maximum distance and the worst-case distance due to merging two simplicial facets. It should be small for 2-d, 3-d, and 4-d, and for higher dimensions with 'Qx'. It is not printed if less than 0.05.


Up: Home page for Qhull
Up: Qhull manual: Table of Contents
To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions


The Geometry Center Home Page

Comments to: qhull@qhull.org
Created: Sept. 25, 1995 --- Last modified: see top

geometry/vignettes/qhull/html/qh-optp.html0000644000176200001440000002312513431000557020465 0ustar liggesusers Qhull print options (P)

Up: Home page for Qhull
Up: Qhull manual: Table of Contents
To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions


[delaunay] Qhull print options (P)

This section lists the print options for Qhull. These options are indicated by 'P' followed by a letter. See Output, Geomview, and Format for other output options.

Copyright © 1995-2018 C.B. Barber


» Programs OptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions

Print options

 
General
Pp
do not report precision problems
Po
force output despite precision problems
Po
if error, output neighborhood of facet
 
 
Select
Pdk:n
print facets with normal[k] >= n (default 0.0)
PDk:n
print facets with normal[k] <= n
PFn
print facets whose area is at least n
Pg
print good facets only (needs 'QGn' or 'QVn ')
PMn
print n facets with most merges
PAn
print n largest facets by area
PG
print neighbors of good facets

»PAn - keep n largest facets by area

The n largest facets are marked good for printing. This may be useful for approximating a hull. Unless 'PG' is set, 'Pg' is automatically set.

»Pdk:n - print facet if normal[k] >= n

For a given output, print only those facets with normal[k] >= n and drop the others. For example, 'Pd0:0.5' prints facets with normal[0] >= 0.5 . The default value of n is zero. For example in 3-d, 'Pd0d1d2' prints facets in the positive octant.

If no facets match, the closest facet is returned.

On Windows 95, do not combine multiple options. A 'd' is considered part of a number. For example, use 'Pd0:0.5 Pd1:0.5' instead of 'Pd0:0.5d1:0.5'.

»PDk:n - print facet if normal[k] <= n

For a given output, print only those facets with normal[k] <= n and drop the others. For example, 'PD0:0.5' prints facets with normal[0] <= 0.5 . The default value of n is zero. For example in 3-d, 'PD0D1D2' displays facets in the negative octant.

If no facets match, the closest facet is returned.

In 2-d, 'd G PD2' displays the Delaunay triangulation instead of the corresponding paraboloid.

Be careful of placing 'Dk' or 'dk' immediately after a real number. Some compilers treat the 'D' as a double precision exponent.

»PFn - keep facets whose area is at least n

The facets with area at least n are marked good for printing. This may be useful for approximating a hull. Unless 'PG' is set, 'Pg' is automatically set.

»Pg - print good facets

Qhull can mark facets as "good". This is used to

  • mark the lower convex hull for Delaunay triangulations and Voronoi diagrams
  • mark the facets that are visible from a point (the 'QGn ' option)
  • mark the facets that contain a point (the 'QVn' option).
  • indicate facets with a large enough area (options 'PAn' and 'PFn')

Option 'Pg' only prints good facets that also meet 'Pdk' and 'PDk' options. It is automatically set for options 'PAn', 'PFn ', 'QGn', and 'QVn'.

»PG - print neighbors of good facets

Option 'PG' can be used with or without option 'Pg' to print the neighbors of good facets. For example, options 'QGn' and 'QVn' print the horizon facets for point n.

»PMn - keep n facets with most merges

The n facets with the most merges are marked good for printing. This may be useful for approximating a hull. Unless 'PG' is set, 'Pg' is automatically set.

Use option 'Fm' to print merges per facet.

»Po - force output despite precision problems

Use options 'Po' and 'Q0' if you can not merge facets, triangulate the output ('Qt'), or joggle the input (QJ).

Option 'Po' can not force output when duplicate ridges or duplicate facets occur. It may produce erroneous results. For these reasons, merged facets, joggled input, or exact arithmetic are better.

If you need a simplicial Delaunay triangulation, use joggled input 'QJ' or triangulated output 'Ft'.

Option 'Po' may be used without 'Q0' to remove some steps from Qhull or to output the neighborhood of an error.

Option 'Po' may be used with option 'Q5') to skip qh_check_maxout (i.e., do not determine the maximum outside distance). This can save a significant amount of time.

If option 'Po' is used,

  • most precision errors allow Qhull to continue.
  • verify ('Tv') does not check coplanar points.
  • points are not partitioned into flipped facets and a flipped facet is always visible to a point. This may delete flipped facets from the output.

»Po - if error, output neighborhood of facet

If an error occurs before the completion of Qhull and tracing is not active, 'Po' outputs a neighborhood of the erroneous facets (if any). It uses the current output options.

See 'Po' - force output despite precision problems.

»Pp - do not report precision problems

With option 'Pp', Qhull does not print statistics about precision problems, and it removes some of the warnings. It removes the narrow hull warning.


Up: Home page for Qhull
Up: Qhull manual: Table of Contents
To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions


The Geometry Center Home Page

Comments to: qhull@qhull.org
Created: Sept. 25, 1995 --- Last modified: see top

geometry/vignettes/qhull/html/index.html0000644000176200001440000012625313431000557020212 0ustar liggesusers Qhull manual

Up: Home page for Qhull
Up:News about Qhull
Up: FAQ about Qhull
To: Qhull manual: Table of Contents (please wait while loading)
To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions


[random-fixed] Qhull manual

Qhull is a general dimension code for computing convex hulls, Delaunay triangulations, halfspace intersections about a point, Voronoi diagrams, furthest-site Delaunay triangulations, and furthest-site Voronoi diagrams. These structures have applications in science, engineering, statistics, and mathematics. See Fukuda's introduction to convex hulls, Delaunay triangulations, Voronoi diagrams, and linear programming. For a detailed introduction, see O'Rourke ['94], Computational Geometry in C.

There are six programs. Except for rbox, they use the same code. Each program includes instructions and examples.

  • qconvex -- convex hulls
  • qdelaunay -- Delaunay triangulations and furthest-site Delaunay triangulations
  • qhalf -- halfspace intersections about a point
  • qhull -- all structures with additional options
  • qvoronoi -- Voronoi diagrams and furthest-site Voronoi diagrams
  • rbox -- generate point distributions for qhull

Qhull implements the Quickhull algorithm for computing the convex hull. Qhull includes options for hull volume, facet area, multiple output formats, and graphical output. It can approximate a convex hull.

Qhull handles roundoff errors from floating point arithmetic. It generates a convex hull with "thick" facets. A facet's outer plane is clearly above all of the points; its inner plane is clearly below the facet's vertices. Any exact convex hull must lie between the inner and outer plane.

Qhull uses merged facets, triangulated output, or joggled input. Triangulated output triangulates non-simplicial, merged facets. Joggled input also guarantees simplicial output, but it is less accurate than merged facets. For merged facets, Qhull reports the maximum outer and inner plane.

Brad Barber, Arlington, MA

Copyright © 1995-2018 C.B. Barber


»Qhull manual: Table of Contents

»When to use Qhull

Qhull constructs convex hulls, Delaunay triangulations, halfspace intersections about a point, Voronoi diagrams, furthest-site Delaunay triangulations, and furthest-site Voronoi diagrams.

For convex hulls and halfspace intersections, Qhull may be used for 2-d upto 8-d. For Voronoi diagrams and Delaunay triangulations, Qhull may be used for 2-d upto 7-d. In higher dimensions, the size of the output grows rapidly and Qhull does not work well with virtual memory. If n is the size of the input and d is the dimension (d>=3), the size of the output and execution time grows by n^(floor(d/2) [see Performance]. For example, do not try to build a 16-d convex hull of 1000 points. It will have on the order of 1,000,000,000,000,000,000,000,000 facets.

On a 600 MHz Pentium 3, Qhull computes the 2-d convex hull of 300,000 cocircular points in 11 seconds. It computes the 2-d Delaunay triangulation and 3-d convex hull of 120,000 points in 12 seconds. It computes the 3-d Delaunay triangulation and 4-d convex hull of 40,000 points in 18 seconds. It computes the 4-d Delaunay triangulation and 5-d convex hull of 6,000 points in 12 seconds. It computes the 5-d Delaunay triangulation and 6-d convex hull of 1,000 points in 12 seconds. It computes the 6-d Delaunay triangulation and 7-d convex hull of 300 points in 15 seconds. It computes the 7-d Delaunay triangulation and 8-d convex hull of 120 points in 15 seconds. It computes the 8-d Delaunay triangulation and 9-d convex hull of 70 points in 15 seconds. It computes the 9-d Delaunay triangulation and 10-d convex hull of 50 points in 17 seconds. The 10-d convex hull of 50 points has about 90,000 facets.

Qhull does not support constrained Delaunay triangulations, triangulation of non-convex surfaces, mesh generation of non-convex objects, or medium-sized inputs in 9-D and higher.

This is a big package with many options. It is one of the fastest available. It is the only 3-d code that handles precision problems due to floating point arithmetic. For example, it implements the identity function for extreme points (see Imprecision in Qhull).

[2016] A newly discovered, bad case for Qhull is multiple, nearly incident points within a 10^-13 ball of 3-d and higher Delaunay triangulations (input sites in the unit cube). Nearly incident points within substantially smaller or larger balls are OK. Error QH6271 is reported if a problem occurs. A future release of Qhull will handle this case. For more information, see "Nearly coincident points on an edge" in Limitations of merged facets

If you need a short code for convex hull, Delaunay triangulation, or Voronoi volumes consider Clarkson's hull program. If you need 2-d Delaunay triangulations consider Shewchuk's triangle program. It is much faster than Qhull and it allows constraints. Both programs use exact arithmetic. They are in http://www.netlib.org/voronoi/.

If your input is in general position (i.e., no coplanar or colinear points),

  • Tomilov's quickhull.hpp () or Qhull version 1.0 may meet your needs. Both programs detect precision problems, but do not handle them.

    CGAL is a library of efficient and reliable geometric algorithms. It uses C++ templates and the Boost library to produce dimension-specific code. This allows more efficient use of memory than Qhull's general-dimension code. CGAL simulates arbitrary precision while Qhull handles round-off error with thick facets. Compare the two approaches with Robustness Issues in CGAL, and Imprecision in Qhull.

    Leda is a library for writing computational geometry programs and other combinatorial algorithms. It includes routines for computing 3-d convex hulls, 2-d Delaunay triangulations, and 3-d Delaunay triangulations. It provides rational arithmetic and graphical output. It runs on most platforms.

    If your problem is in high dimensions with a few, non-simplicial facets, try Fukuda's cdd. It is much faster than Qhull for these distributions.

    Custom software for 2-d and 3-d convex hulls may be faster than Qhull. Custom software should use less memory. Qhull uses general-dimension data structures and code. The data structures support non-simplicial facets.

    Qhull is not suitable for mesh generation or triangulation of arbitrary surfaces. You may use Qhull if the surface is convex or completely visible from an interior point (e.g., a star-shaped polyhedron). First, project each site to a sphere that is centered at the interior point. Then, compute the convex hull of the projected sites. The facets of the convex hull correspond to a triangulation of the surface. For mesh generation of arbitrary surfaces, see Schneiders' Finite Element Mesh Generation.

    Qhull is not suitable for constrained Delaunay triangulations. With a lot of work, you can write a program that uses Qhull to add constraints by adding additional points to the triangulation.

    Qhull is not suitable for the subdivision of arbitrary objects. Use qdelaunay to subdivide a convex object.

  • »Description of Qhull

    »definition

    The convex hull of a point set P is the smallest convex set that contains P. If P is finite, the convex hull defines a matrix A and a vector b such that for all x in P, Ax+b <= [0,...].

    Qhull computes the convex hull in 2-d, 3-d, 4-d, and higher dimensions. Qhull represents a convex hull as a list of facets. Each facet has a set of vertices, a set of neighboring facets, and a halfspace. A halfspace is defined by a unit normal and an offset (i.e., a row of A and an element of b).

    Qhull accounts for round-off error. It returns "thick" facets defined by two parallel hyperplanes. The outer planes contain all input points. The inner planes exclude all output vertices. See Imprecise convex hulls.

    Qhull may be used for the Delaunay triangulation or the Voronoi diagram of a set of points. It may be used for the intersection of halfspaces.

    »input format

    The input data on stdin consists of:

    • first line contains the dimension
    • second line contains the number of input points
    • remaining lines contain point coordinates

    For example:

        3  #sample 3-d input
        5
        0.4 -0.5 1.0
        1000 -1e-5 -100
        0.3 0.2 0.1
        1.0 1.0 1.0
        0 0 0
    

    Input may be entered by hand. End the input with a control-D (^D) character.

    To input data from a file, use I/O redirection or 'TI file'. The filename may not include spaces or quotes.

    A comment starts with a non-numeric character and continues to the end of line. The first comment is reported in summaries and statistics. With multiple qhull commands, use option 'FQ' to place a comment in the output.

    The dimension and number of points can be reversed. Comments and line breaks are ignored. Error reporting is better if there is one point per line.

    »option format

    Use options to specify the output formats and control Qhull. The qhull program takes all options. The other programs use a subset of the options. They disallow experimental and inappropriate options.

    • qconvex == qhull
    • qdelaunay == qhull d Qbb
    • qhalf == qhull H
    • qvoronoi == qhull v Qbb

    Single letters are used for output formats and precision constants. The other options are grouped into menus for formats ('F'), Geomview ('G '), printing ('P'), Qhull control ('Q '), and tracing ('T'). The menu options may be listed together (e.g., 'GrD3' for 'Gr' and 'GD3'). Options may be in any order. Capitalized options take a numeric argument (except for 'PG' and 'F' options). Use option 'FO' to print the selected options.

    Qhull uses zero-relative indexing. If there are n points, the index of the first point is 0 and the index of the last point is n-1.

    The default options are:

    • summary output ('s')
    • merged facets ('C-0' in 2-d, 3-d, 4-d; 'Qx' in 5-d and up)

    Except for bounding box ('Qbk:n', etc.), drop facets ('Pdk:n', etc.), and Qhull command ('FQ'), only the last occurence of an option counts. Bounding box and drop facets may be repeated for each dimension. Option 'FQ' may be repeated any number of times.

    The Unix tcsh and ksh shells make it easy to try out different options. In Windows 95, use a command window with doskey and a window scroller (e.g., peruse).

    »output format

    To write the results to a file, use I/O redirection or 'TO file'. Windows 95 users should use 'TO file' or the console. If a filename is surrounded by single quotes, it may include spaces.

    The default output option is a short summary ('s') to stdout. There are many others (see output and formats). You can list vertex incidences, vertices and facets, vertex coordinates, or facet normals. You can view Qhull objects with Geomview, Mathematica, or Maple. You can print the internal data structures. You can call Qhull from your application (see Qhull library).

    For example, 'qhull o' lists the vertices and facets of the convex hull.

    Error messages and additional summaries ('s') go to stderr. Unless redirected, stderr is the console.

    »algorithm

    Qhull implements the Quickhull algorithm for convex hull [Barber et al. '96]. This algorithm combines the 2-d Quickhull algorithm with the n-d beneath-beyond algorithm [c.f., Preparata & Shamos '85]. It is similar to the randomized algorithms of Clarkson and others [Clarkson & Shor '89; Clarkson et al. '93; Mulmuley '94]. For a demonstration, see How Qhull adds a point. The main advantages of Quickhull are output sensitive performance (in terms of the number of extreme points), reduced space requirements, and floating-point error handling.

    »data structures

    Qhull produces the following data structures for dimension d:

    • A coordinate is a real number in floating point format.
    • A point is an array of d coordinates. With option 'QJ', the coordinates are joggled by a small amount.
    • A vertex is an input point.
    • A hyperplane is d normal coefficients and an offset. The length of the normal is one. The hyperplane defines a halfspace. If V is a normal, b is an offset, and x is a point inside the convex hull, then Vx+b <0.
    • An outer plane is a positive offset from a hyperplane. When Qhull is done, all points will be below all outer planes.
    • An inner plane is a negative offset from a hyperplane. When Qhull is done, all vertices will be above the corresponding inner planes.
    • An orientation is either 'top' or 'bottom'. It is the topological equivalent of a hyperplane's geometric orientation.
    • A simplicial facet is a set of d neighboring facets, a set of d vertices, a hyperplane equation, an inner plane, an outer plane, and an orientation. For example in 3-d, a simplicial facet is a triangle.
    • A centrum is a point on a facet's hyperplane. A centrum is the average of a facet's vertices. Neighboring facets are convex if each centrum is below the neighbor facet's hyperplane.
    • A ridge is a set of d-1 vertices, two neighboring facets, and an orientation. For example in 3-d, a ridge is a line segment.
    • A non-simplicial facet is a set of ridges, a hyperplane equation, a centrum, an outer plane, and an inner plane. The ridges determine a set of neighboring facets, a set of vertices, and an orientation. Qhull produces a non-simplicial facet when it merges two facets together. For example, a cube has six non-simplicial facets.

    For examples, use option 'f'. See polyhedron operations for further design documentation.

    »Imprecision in Qhull

    See Imprecision in Qhull and Merged facets or joggled input

    »Examples of Qhull

    See Examples of Qhull. Most of these examples require Geomview. Some of the examples have pictures .

    »Options for using Qhull

    See Options.

    »Qhull internals

    See Internals.

    »Geomview, Qhull's graphical viewer

    Geomview is an interactive geometry viewing program. Geomview provides a good visualization of Qhull's 2-d and 3-d results.

    Qhull includes Examples of Qhull that may be viewed with Geomview.

    Geomview can help visulalize a 3-d Delaunay triangulation or the surface of a 4-d convex hull, Use option 'QVn' to select the 3-D facets adjacent to a vertex.

    You may use Geomview to create movies that animate your objects (c.f., How can I create a video animation?). Geomview helped create the mathematical videos "Not Knot", "Outside In", and "The Shape of Space" from the Geometry Center.

    »Installing Geomview

    Geomview is an open source project under SourceForge.

    For build instructions see Downloading Geomview. Geomview builds under Linux, Unix, Macintosh OS X, and Windows.

    Geomview has installable packages for Debian and Ubuntu. The OS X build needs Xcode, an X11 SDK, and Lesstif or Motif. The Windows build uses Cygwin (see Building Geomview below for instructions).

    If using Xforms (e.g., for Geomview's External Modules), install the 'libXpm-devel' package from cygwin and move the xforms directory into your geomview directory, e.g.,
    mv xforms-1.2.4 geomview-1.9.5/xforms

    Geomview's ndview provides multiple views into 4-d and higher objects. This module is out-of-date (geomview-users: 4dview). Download NDview-sgi.tar.Z at newpieces and 4dview at Geomview/modules.

    »Using Geomview

    Use Geomview to view Examples of Qhull. You can spin the convex hull, fly a camera through its facets, and see how Qhull produces thick facets in response to round-off error.

    Follow these instructions to view 'eg,01.cube' from Examples of Qhull

    1. Launch an XTerm command shell
      • If needed, start the X terminal server, Use 'xinit' or 'startx' in /usr/X11R6/bin
        xinit -- -multiwindow -clipboard
        startx
      • Start an XTerm command shell. In Windows, click the Cygwin/bash icon on your desktop.
      • Set the DISPLAY variable, e.g.,
        export DISPLAY=:0
        export DISPLAY=:0 >>~/.bashenv
    2. Use Qhull's Geomview options to create a geomview object
      • rbox c D3 | qconvex G >eg.01.cube
      • On windows, convert the output to Unix text format with 'd2u'
        rbox c D3 | qconvex G | d2u >eg.01.cube
        d2u eg.*
    3. Run Geomview
      • Start Geomview with your example
        ./geomview eg.01.cube
      • Follow the instructions in Gemoview Tutorial
      • Geomview creates the Geomview control panel with Targets and External Module, the Geomview toolbar with buttons for controlling Geomview, and the Geomview camera window showing a cube.
      • Clear the camera window by selecting your object in the Targets list and 'Edit > Delete' or 'dd'
      • Load the Geomview files panel. Select 'Open' in the 'File' menu.
      • Set 'Filter' in the files panel to your example directory followed by '/*' (e.g., '/usr/local/qhull-2015.2/eg/*')
      • Click 'Filter' in the files panel to view your examples in the 'Files' list.
      • Load another example into the camera window by selecting it and clicking 'OK'.
      • Review the instructions for Interacting with Geomview
      • When viewing multiple objects at once, you may want to turn off normalization. In the 'Inspect > Apperance' control panel, set 'Normalize' to 'None'.

    Geomview defines GCL (a textual API for controlling Geomview) and OOGL (a textual file format for defining objects).

    • To control Geomview, you may use any program that reads and writes from stdin and stdout. For example, it could report Qhull's information about a vertex identified by a double-click 'pick' event.
    • GCL command language for controlling Geomview
    • OOGL file format for defining objects (tutorial).
    • External Modules for interacting with Geomview via GCL
    • Interact with your objects via pick commands in response to right-mouse double clicks. Enable pick events with the interest command.

    »Building Geomview for Windows

    Compile Geomview under Cygwin. For detailed instructions, see Building Savi and Geomview under Windows. These instructions are somewhat out-of-date. Updated instructions follow.

    How to compile Geomview under 32-bit Cygwin (October 2015)

    1. Note: L. Wood has run into multiple issues with Geomview on Cygwin. He recommends Virtualbox/Ubuntu and a one-click install of geomview via the Ubuntu package. See his Savi/Geomview link above.
    2. Install 32-bit Cygwin as follows. For additional guidance, see Cygwin's Installing and Updating Cygwin Packages and Setup cygwin.
      • Launch the cygwin installer.
      • Select a mirror from Cygwin mirrors (e.g., http://mirrors.kernel.org/sourceware/cygwin/ in California).
      • Select the packages to install. Besides the cygwin packages listed in the Savi/Windows instructions consider adding
        • Default -- libXm-devel (required for /usr/include/Xm/Xm.h)
        • Devel -- bashdb, gcc-core (in place of gcc), gdb
        • Lib -- libGL-devel, libGLU1 (required, obsolete), libGLU-devel (required, obsolete), libjpeg-devel(XForms), libXext-devel (required), libXpm-devel (Xforms) libGL and lib
        • Math -- bc
        • Net -- autossh, inetutils, openssh
        • System -- chere
        • Utils -- dos2unix (required for qhull), keychain
        • If installing perl, ActiveState Perl may be a better choice than cygwin's perl. Perl is not used by Geomview or Qhull.
        • Cygwin Package Search -- Search for cygwin programs and packages
      • Click 'Next' to download and install the packages.
      • If the download is incomplete, try again.
      • If you try again after a successful install, cygwin will uninstall and reinstall all modules..
      • Click on the 'Cywin Terminal' icon on the Desktop. It sets up a user directory in /home from /etc/skel/...
      • Mount your disk drives
        mount c: /c # Ignore the warning /c does not exist
    3. Consider installing the Road Bash scripts (/etc/road-*) from Road. They define aliases and functions for Unix command shells (Unix, Linux, Mac OS X, Windows),
      • Download Road Bash and unzip the downloaded file
      • Copy .../bash/etc/road-* to the Cywin /etc directory (by default, C:\cygwin\etc).
      • Using the cygwin terminal, convert the road scripts to Unix format
        d2u /etc/road-*
      • Try it
        source /etc/road-home.bashrc
      • Install it
        cp /etc/road-home.bashrc ~/.bashrc
    4. Launch the X terminal server from 'Start > All programs > Cygwin-X > Xwin Server'. Alternatively, run 'startx'
    5. Launch an XTerm shell
      • Right click the Cywin icon on the system tray in the Windows taskbar.
      • Select 'System Tools > XTerm'
    6. Download and extract Geomview -- Downloading Geomview
    7. Compile Geomview
      • ./configure
      • make
    8. If './configure' fails, check 'config.log' at the failing step. Look carefully for missing libraries, etc. The Geomview FAQ contains suggestions (e.g., "configure claims it can't find OpenGl").
    9. If 'make' fails, read the output carefully for error messages. Usually it is a missing include file or package. Locate and install the missing cygwin packages (Cygwin Package Search).

    »What to do if something goes wrong

    Please report bugs to qhull_bug@qhull.org . Please report if Qhull crashes. Please report if Qhull generates an "internal error". Please report if Qhull produces a poor approximate hull in 2-d, 3-d or 4-d. Please report documentation errors. Please report missing or incorrect links.

    If you do not understand something, try a small example. The rbox program is an easy way to generate test cases. The Geomview program helps to visualize the output from Qhull.

    If Qhull does not compile, it is due to an incompatibility between your system and ours. The first thing to check is that your compiler is ANSI standard. Qhull produces a compiler error if __STDC__ is not defined. You may need to set a flag (e.g., '-A' or '-ansi').

    If Qhull compiles but crashes on the test case (rbox D4), there's still incompatibility between your system and ours. Sometimes it is due to memory management. This can be turned off with qh_NOmem in mem.h. Please let us know if you figure out how to fix these problems.

    If you doubt the output from Qhull, add option 'Tv'. It checks that every point is inside the outer planes of the convex hull. It checks that every facet is convex with its neighbors. It checks the topology of the convex hull.

    Qhull should work on all inputs. It may report precision errors if you turn off merged facets with option 'Q0'. This can get as bad as facets with flipped orientation or two facets with the same vertices. You'll get a long help message if you run into such a case. They are easy to generate with rbox.

    If you do find a problem, try to simplify it before reporting the error. Try different size inputs to locate the smallest one that causes an error. You're welcome to hunt through the code using the execution trace ('T4') as a guide. This is especially true if you're incorporating Qhull into your own program.

    When you report an error, please attach a data set to the end of your message. Include the options that you used with Qhull, the results of option 'FO', and any messages generated by Qhull. This allows me to see the error for myself. Qhull is maintained part-time.

    »Email

    Please send correspondence to Brad Barber at qhull@qhull.org and report bugs to qhull_bug@qhull.org . Let me know how you use Qhull. If you mention it in a paper, please send a reference and abstract.

    If you would like to get Qhull announcements (e.g., a new version) and news (any bugs that get fixed, etc.), let us know and we will add you to our mailing list. For Internet news about geometric algorithms and convex hulls, look at comp.graphics.algorithms and sci.math.num-analysis. For Qhull news look at qhull-news.html.

    »Authors

       C. Bradford Barber                    Hannu Huhdanpaa
       bradb@shore.net                       hannu@qhull.org
    

    »Acknowledgments

    A special thanks to David Dobkin for his guidance. A special thanks to Albert Marden, Victor Milenkovic, the Geometry Center, and Harvard University for supporting this work.

    A special thanks to Mark Phillips, Robert Miner, and Stuart Levy for running the Geometry Center web site long after the Geometry Center closed. Stuart moved the web site to the University of Illinois at Champaign-Urbana. Mark and Robert are founders of Geometry Technologies. Mark, Stuart, and Tamara Munzner are the original authors of Geomview.

    A special thanks to Endocardial Solutions, Inc. of St. Paul, Minnesota for their support of the internal documentation (src/libqhull/index.html). They use Qhull to build 3-d models of heart chambers.

    Qhull 1.0 and 2.0 were developed under National Science Foundation grants NSF/DMS-8920161 and NSF-CCR-91-15793 750-7504. If you find it useful, please let us know.

    The Geometry Center was supported by grant DMS-8920161 from the National Science Foundation, by grant DOE/DE-FG02-92ER25137 from the Department of Energy, by the University of Minnesota, and by Minnesota Technology, Inc.

    »References

    Aurenhammer, F., "Voronoi diagrams -- A survey of a fundamental geometric data structure," ACM Computing Surveys, 1991, 23:345-405.

    Barber, C. B., D.P. Dobkin, and H.T. Huhdanpaa, "The Quickhull Algorithm for Convex Hulls," ACM Transactions on Mathematical Software, 22(4):469-483, Dec 1996, www.qhull.org [http://portal.acm.org; http://citeseerx.ist.psu.edu].

    Clarkson, K.L. and P.W. Shor, "Applications of random sampling in computational geometry, II", Discrete Computational Geometry, 4:387-421, 1989

    Clarkson, K.L., K. Mehlhorn, and R. Seidel, "Four results on randomized incremental construction," Computational Geometry: Theory and Applications, vol. 3, p. 185-211, 1993.

    Devillers, et. al., "Walking in a triangulation," ACM Symposium on Computational Geometry, June 3-5,2001, Medford MA.

    Dobkin, D.P. and D.G. Kirkpatrick, "Determining the separation of preprocessed polyhedra--a unified approach," in Proc. 17th Inter. Colloq. Automata Lang. Program., in Lecture Notes in Computer Science, Springer-Verlag, 443:400-413, 1990.

    Edelsbrunner, H, Geometry and Topology for Mesh Generation, Cambridge University Press, 2001.

    Gartner, B., "Fast and robust smallest enclosing balls", Algorithms - ESA '99, LNCS 1643.

    Golub, G.H. and van Loan, C.F., Matric Computations, Baltimore, Maryland, USA: John Hopkins Press, 1983

    Fortune, S., "Computational geometry," in R. Martin, editor, Directions in Geometric Computation, Information Geometers, 47 Stockers Avenue, Winchester, SO22 5LB, UK, ISBN 1-874728-02-X, 1993.

    Milenkovic, V., "Robust polygon modeling," Computer-Aided Design, vol. 25, p. 546-566, September 1993.

    Mucke, E.P., I. Saias, B. Zhu, Fast randomized point location without preprocessing in Two- and Three-dimensional Delaunay Triangulations, ACM Symposium on Computational Geometry, p. 274-283, 1996 [GeomDir].

    Mulmuley, K., Computational Geometry, An Introduction Through Randomized Algorithms, Prentice-Hall, NJ, 1994.

    O'Rourke, J., Computational Geometry in C, Cambridge University Press, 1994.

    Preparata, F. and M. Shamos, Computational Geometry, Springer-Verlag, New York, 1985.


    Up: Home page for Qhull
    Up:News about Qhull
    Up: FAQ about Qhull
    To: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
    Dn: Imprecision in Qhull
    Dn: Description of Qhull examples
    Dn: Qhull internals
    Dn: Qhull functions, macros, and data structures


    The Geometry Center Home Page

    Comments to: qhull@qhull.org
    Created: Sept. 25, 1995 --- Last modified: see top

    geometry/vignettes/qhull/html/qh-optq.html0000644000176200001440000010326513431000557020472 0ustar liggesusers Qhull control options (Q)

    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions


    [delaunay] Qhull control options (Q)

    This section lists the control options for Qhull. These options are indicated by 'Q' followed by a letter.

    Copyright © 1995-2018 C.B. Barber


    » Programs OptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions

    Qhull control options

     
    General
    Qu
    compute upper hull for furthest-site Delaunay triangulation
    Qc
    keep coplanar points with nearest facet
    Qi
    keep interior points with nearest facet
    QJ
    joggled input to avoid precision problems
    Qt
    triangulated output
     
     
    Precision handling
    Qz
    add a point-at-infinity for Delaunay triangulations
    Qx
    exact pre-merges (allows coplanar facets)
    Qs
    search all points for the initial simplex
    Qbb
    scale last coordinate to [0,m] for Delaunay
    Qv
    test vertex neighbors for convexity
     
     
    Transform input
    Qbk:0Bk:0
    drop dimension k from input
    QRn
    random rotation (n=seed, n=0 time, n=-1 time/no rotate)
    Qbk:n
    scale coord[k] to low bound of n (default -0.5)
    QBk:n
    scale coord[k] to upper bound of n (default 0.5)
    QbB
    scale input to fit the unit cube
     
     
    Select facets
    QVn
    good facet if it includes point n, -n if not
    QGn
    good facet if visible from point n, -n for not visible
    Qg
    only build good facets (needs 'QGn', 'QVn ', or 'Pdk')
     
     
    Experimental
    Q4
    avoid merging old facets into new facets
    Q5
    do not correct outer planes at end of qhull
    Q3
    do not merge redundant vertices
    Q6
    do not pre-merge concave or coplanar facets
    Q0
    do not pre-merge facets with 'C-0' or 'Qx'
    Q8
    ignore near-interior points
    Q2
    merge all non-convex at once instead of independent sets
    Qf
    partition point to furthest outside facet
    Q7
    process facets depth-first instead of breadth-first
    Q9
    process furthest of furthest points
    Q10
    no special processing for narrow distributions
    Q11
    copy normals and recompute centrums for tricoplanar facets
    Q12
    do not error on wide merge due to duplicate ridge and nearly coincident points
    Q14
    do not rename vertices that create a duplicate ridge
    Qm
    process points only if they would increase the max. outer plane
    Qr
    process random outside points instead of furthest one
    Q1
    sort merges by type instead of angle

    »Qbb - scale the last coordinate to [0,m] for Delaunay

    After scaling with option 'Qbb', the lower bound of the last coordinate will be 0 and the upper bound will be the maximum width of the other coordinates. Scaling happens after projecting the points to a paraboloid and scaling other coordinates.

    Option 'Qbb' is automatically set for qdelaunay and qvoronoi. Option 'Qbb' is automatically set for joggled input 'QJ'.

    Option 'Qbb' should be used for Delaunay triangulations with integer coordinates. Since the last coordinate is the sum of squares, it may be much larger than the other coordinates. For example, rbox 10000 D2 B1e8 | qhull d has precision problems while rbox 10000 D2 B1e8 | qhull d Qbb is OK.

    »QbB - scale the input to fit the unit cube

    After scaling with option 'QbB', the lower bound will be -0.5 and the upper bound +0.5 in all dimensions. For different bounds change qh_DEFAULTbox in user.h (0.5 is best for Geomview).

    For Delaunay and Voronoi diagrams, scaling happens after projection to the paraboloid. Under precise arithmetic, scaling does not change the topology of the convex hull. Scaling may reduce precision errors if coordinate values vary widely.

    »Qbk:n - scale coord[k] to low bound

    After scaling, the lower bound for dimension k of the input points will be n. 'Qbk' scales coord[k] to -0.5.

    »QBk:n - scale coord[k] to upper bound

    After scaling, the upper bound for dimension k of the input points will be n. 'QBk' scales coord[k] to 0.5.

    »Qbk:0Bk:0 - drop dimension k from the input points

    Drop dimension k from the input points. For example, 'Qb1:0B1:0' deletes the y-coordinate from all input points. This allows the user to take convex hulls of sub-dimensional objects. It happens before the Delaunay and Voronoi transformation. It happens after the halfspace transformation for both the data and the feasible point.

    »Qc - keep coplanar points with nearest facet

    During construction of the hull, a point is coplanar if it is between 'Wn' above and 'Un' below a facet's hyperplane. A different definition is used for output from Qhull.

    For output, a coplanar point is above the minimum vertex (i.e., above the inner plane). With joggle ('QJ'), a coplanar point includes points within one joggle of the inner plane.

    With option 'Qc', output formats 'p ', 'f', 'Gp', 'Fc', 'FN', and 'FP' will print the coplanar points. With options 'Qc Qi' these outputs include the interior points.

    For Delaunay triangulations (qdelaunay or qvoronoi), a coplanar point is a point that is nearly incident to a vertex. All input points are either vertices of the triangulation or coplanar.

    Qhull stores coplanar points with a facet. While constructing the hull, it retains all points within qh_RATIOnearInside (user.h) of a facet. In qh_check_maxout(), it uses these points to determine the outer plane for each facet. With option 'Qc', qh_check_maxout() retains points above the minimum vertex for the hull. Other points are removed. If qh_RATIOnearInside is wrong or if options 'Q5 Q8' are set, a coplanar point may be missed in the output (see Qhull limitations).

    »Qf - partition point to furthest outside facet

    After adding a new point to the convex hull, Qhull partitions the outside points and coplanar points of the old, visible facets. Without the 'f ' option and merging, it assigns a point to the first facet that it is outside ('Wn'). When merging, it assigns a point to the first facet that is more than several times outside (see qh_DISToutside in user.h).

    If option 'Qf' is selected, Qhull performs a directed search (no merging) or an exhaustive search (merging) of new facets. Option 'Qf' may reduce precision errors if pre-merging does not occur.

    Option 'Q9' processes the furthest of all furthest points.

    »Qg - only build good facets (needs 'QGn' 'QVn' or 'Pdk')

    Qhull has several options for defining and printing good facets. With the 'Qg' option, Qhull will only build those facets that it needs to determine the good facets in the output. This may speed up Qhull in 2-d and 3-d. It is useful for furthest-site Delaunay triangulations (qdelaunay Qu, invoke with 'qhull d Qbb Qu Qg'). It is not effective in higher dimensions because many facets see a given point and contain a given vertex. It may not work for all combinations.

    See 'QGn', 'QVn', and 'Pdk' for defining good facets, and 'Pg' and 'PG' for printing good facets and their neighbors. If pre-merging ('C-n') is not used and there are coplanar facets, then 'Qg Pg' may produce a different result than 'Pg'. Option Qg disables renaming vertices that create a duplicate ridge ('Q14').

    »QGn - good facet if visible from point n, -n for not visible

    With option 'QGn', a facet is good (see 'Qg' and 'Pg') if it is visible from point n. If n < 0, a facet is good if it is not visible from point n. Point n is not added to the hull (unless 'TCn' or 'TPn').

    With rbox, use the 'Pn,m,r' option to define your point; it will be point 0 ('QG0').

    »Qi - keep interior points with nearest facet

    Normally Qhull ignores points that are clearly interior to the convex hull. With option 'Qi', Qhull treats interior points the same as coplanar points. Option 'Qi' does not retain coplanar points. You will probably want 'Qc ' as well.

    Option 'Qi' is automatically set for 'qdelaunay Qc' and 'qvoronoi Qc'. If you use 'qdelaunay Qi' or 'qvoronoi Qi', option 's' reports all nearly incident points while option 'Fs' reports the number of interior points (should always be zero).

    With option 'Qi', output formats 'p', 'f','Gp', 'Fc', 'FN', and 'FP' include interior points.

    »QJ or QJn - joggled input to avoid precision errors

    Option 'QJ' or 'QJn' joggles each input coordinate by adding a random number in the range [-n,n]. If a precision error occurs, It tries again. If precision errors still occur, Qhull increases n ten-fold and tries again. The maximum value for increasing n is 0.01 times the maximum width of the input. Option 'QJ' selects a default value for n. User.h defines these parameters and a maximum number of retries. See Merged facets or joggled input.

    Users of joggled input should consider converting to triangulated output ('Qt'). Triangulated output is approximately 1000 times more accurate than joggled input.

    Option 'QJ' also sets 'Qbb' for Delaunay triangulations and Voronoi diagrams. It does not set 'Qbb' if 'Qbk:n' or 'QBk:n' are set.

    If 'QJn' is set, Qhull does not merge facets unless requested to. All facets are simplicial (triangular in 2-d). This may be important for your application. You may also use triangulated output ('Qt') or Option 'Ft'.

    Qhull adjusts the outer and inner planes for 'QJn' ('Fs'). They are increased by sqrt(d)*n to account for the maximum distance between a joggled point and the corresponding input point. Coplanar points ('Qc') require an additional sqrt(d)*n since vertices and coplanar points may be joggled in opposite directions.

    For Delaunay triangulations (qdelaunay), joggle happens before lifting the input sites to a paraboloid. Instead of 'QJ', you may use triangulated output ('Qt')

    This option is deprecated for Voronoi diagrams (qvoronoi). It triangulates cospherical points, leading to duplicated Voronoi vertices.

    By default, 'QJn' uses a fixed random number seed. To use time as the random number seed, select 'QR-1'. The summary ('s') will show the selected seed as 'QR-n'.

    With 'QJn', Qhull does not error on degenerate hyperplane computations. Except for Delaunay and Voronoi computations, Qhull does not error on coplanar points.

    Use option 'FO' to display the selected options. Option 'FO' displays the joggle and the joggle seed. If Qhull restarts, it will redisplay the options.

    Use option 'TRn' to estimate the probability that Qhull will fail for a given 'QJn'.

    »Qm - only process points that increase the maximum outer plane

    Qhull reports the maximum outer plane in its summary ('s'). With option 'Qm', Qhull does not process points that are below the current, maximum outer plane. This is equivalent to always adjusting 'Wn ' to the maximum distance of a coplanar point to a facet. It is ignored for points above the upper convex hull of a Delaunay triangulation. Option 'Qm' is no longer important for merging.

    »Qr - process random outside points instead of furthest ones

    Normally, Qhull processes the furthest point of a facet's outside points. Option 'Qr' instead selects a random outside point for processing. This makes Qhull equivalent to the randomized incremental algorithms.

    The original randomization algorithm by Clarkson and Shor ['89] used a conflict list which is equivalent to Qhull's outside set. Later randomized algorithms retained the previously constructed facets.

    To compare Qhull to the randomized algorithms with option 'Qr', compare "hyperplanes constructed" and "distance tests". Qhull does not report CPU time because the randomization is inefficient.

    »QRn - random rotation

    Option 'QRn' randomly rotates the input. For Delaunay triangulations (qdelaunay or qvoronoi), it rotates the lifted points about the last axis.

    If n=0, use time as the random number seed. If n>0, use n as the random number seed. If n=-1, don't rotate but use time as the random number seed. If n<-1, don't rotate but use n as the random number seed.

    »Qs - search all points for the initial simplex

    Qhull constructs an initial simplex from d+1 points. It selects points with the maximum and minimum coordinates and non-zero determinants. If this fails, it searches all other points. In 8-d and higher, Qhull selects points with the minimum x or maximum coordinate (see qh_initialvertices in poly2.c ). It rejects points with nearly zero determinants. This should work for almost all input sets.

    If Qhull can not construct an initial simplex, it reports a descriptive message. Usually, the point set is degenerate and one or more dimensions should be removed ('Qbk:0Bk:0'). If not, use option 'Qs'. It performs an exhaustive search for the best initial simplex. This is expensive is high dimensions.

    »Qt - triangulated output

    By default, qhull merges facets to handle precision errors. This produces non-simplicial facets (e.g., the convex hull of a cube has 6 square facets). Each facet is non-simplicial because it has four vertices.

    Use option 'Qt' to triangulate all non-simplicial facets before generating results. Alternatively, use joggled input ('QJ') to prevent non-simplical facets. Unless 'Pp' is set, qhull produces a warning if 'QJ' and 'Qt' are used together.

    For Delaunay triangulations (qdelaunay), triangulation occurs after lifting the input sites to a paraboloid and computing the convex hull.

    Option 'Qt' is deprecated for Voronoi diagrams (qvoronoi). It triangulates cospherical points, leading to duplicated Voronoi vertices.

    Option 'Qt' may produce degenerate facets with zero area.

    Facet area and hull volumes may differ with and without 'Qt'. The triangulations are different and different triangles may be ignored due to precision errors.

    With sufficient merging, the ridges of a non-simplicial facet may share more than two neighboring facets. If so, their triangulation ('Qt') will fail since two facets have the same vertex set.

    »Qu - compute upper hull for furthest-site Delaunay triangulation

    When computing a Delaunay triangulation (qdelaunay or qvoronoi), Qhull computes both the the convex hull of points on a paraboloid. It normally prints facets of the lower hull. These correspond to the Delaunay triangulation. With option 'Qu', Qhull prints facets of the upper hull. These correspond to the furthest-site Delaunay triangulation and the furthest-site Voronoi diagram.

    Option 'qhull d Qbb Qu Qg' may improve the speed of option 'Qu'. If you use the Qhull library, a faster method is 1) use Qhull to compute the convex hull of the input sites; 2) take the extreme points (vertices) of the convex hull; 3) add one interior point (e.g., 'FV', the average of d extreme points); 4) run 'qhull d Qbb Qu' or 'qhull v Qbb Qu' on these points.

    »Qv - test vertex neighbors for convexity

    Normally, Qhull tests all facet neighbors for convexity. Non-neighboring facets which share a vertex may not satisfy the convexity constraint. This occurs when a facet undercuts the centrum of another facet. They should still be convex. Option 'Qv' extends Qhull's convexity testing to all neighboring facets of each vertex. The extra testing occurs after the hull is constructed..

    »QVn - good facet if it includes point n, -n if not

    With option 'QVn', a facet is good ('Qg', 'Pg') if one of its vertices is point n. If n<0, a good facet does not include point n.

    If options 'PG' and 'Qg' are not set, option 'Pg' (print only good) is automatically set.

    Option 'QVn' behaves oddly with options 'Fx' and 'qvoronoi Fv'.

    If used with option 'Qg' (only process good facets), point n is either in the initial simplex or it is the first point added to the hull. Options 'QVn Qg' require either 'QJ' or 'Q0' (no merging).

    »Qx - exact pre-merges (allows coplanar facets)

    Option 'Qx' performs exact merges while building the hull. Options 'Qx' and 'C-0' are set by default in 5-d and higher. To disable this default, set option 'C-0' or another pre-merge option. Use option 'Q0' to disable all merging, including 'Qx' and 'C-0'.

    The "exact" pre-merges are merging a point into a coplanar facet (defined by 'Vn ', 'Un', and 'C-n'), merging concave facets, merging duplicate ridges, and merging flipped facets. Coplanar merges and angle coplanar merges ('A-n') are not performed. Concavity testing is delayed until a merge occurs.

    FIXUP -- isn't merging enabled once a merge happens?

    After the hull is built, all coplanar merges are performed (defined by 'C-n' and 'A-n'), then post-merges are performed (defined by 'Cn' and 'An'). If facet progress is logged ('TFn'), Qhull reports each phase and prints intermediate summaries and statistics ('Ts').

    Without 'Qx' in 5-d and higher, options 'C-n' and 'A-n' may merge too many facets. Since redundant vertices are not removed effectively, facets become increasingly wide.

    Option 'Qx' may report a wide facet. With 'Qx', coplanar facets are not merged. This can produce a "dent" in an intermediate hull. If a point is partitioned into a dent and it is below the surrounding facets but above other facets, one or more wide facets will occur. In practice, this is unlikely. To observe this effect, run Qhull with option 'Q6' which doesn't pre-merge concave facets. A concave facet makes a large dent in the intermediate hull.

    Option 'Qx' may set an outer plane below one of the input points. A coplanar point may be assigned to the wrong facet because of a "dent" in an intermediate hull. After constructing the hull, Qhull double checks all outer planes with qh_check_maxout in poly2.c . If a coplanar point is assigned to the wrong facet, qh_check_maxout may reach a local maximum instead of locating all coplanar facets. This appears to be unlikely.

    »Qz - add a point-at-infinity for Delaunay triangulations

    Option 'Qz' adds a point above the paraboloid of lifted sites for a Delaunay triangulation. It allows the Delaunay triangulation of cospherical sites. It reduces precision errors for nearly cospherical sites.

    »Q0 - no merging with C-0 and Qx

    Turn off default merge options 'C-0' and 'Qx'.

    With 'Q0' and without other pre-merge options, Qhull ignores precision issues while constructing the convex hull. This may lead to precision errors. If so, a descriptive warning is generated. See Precision issues.

    »Q1 - sort merges by type instead of angle

    Qhull sorts the coplanar facets before picking a subset of the facets to merge. It merges concave and flipped facets first. Then it merges facets that meet at a steep angle. With 'Q1', Qhull sorts merges by type (coplanar, angle coplanar, concave) instead of by angle. This may make the facets wider.

    »Q2 - merge all non-convex at once instead of independent sets

    With 'Q2', Qhull merges all facets at once instead of performing merges in independent sets. This may make the facets wider.

    »Q3 - do not merge redundant vertices

    With 'Q3', Qhull does not remove redundant vertices. In 6-d and higher, Qhull never removes redundant vertices (since vertices are highly interconnected). Option 'Q3' may be faster, but it may result in wider facets. Its effect is easiest to see in 3-d and 4-d.

    »Q4 - avoid merging old facets into new facets

    With 'Q4', Qhull avoids merges of an old facet into a new facet. This sometimes improves facet width and sometimes makes it worse.

    »Q5 - do not correct outer planes at end of qhull

    When merging facets or approximating a hull, Qhull tests coplanar points and outer planes after constructing the hull. It does this by performing a directed search (qh_findbest in geom.c). It includes points that are just inside the hull.

    With options 'Q5' or 'Po', Qhull does not test outer planes. The maximum outer plane is used instead. Coplanar points ('Qc') are defined by 'Un'. An input point may be outside of the maximum outer plane (this appears to be unlikely). An interior point may be above 'Un' from a hyperplane.

    Option 'Q5' may be used if outer planes are not needed. Outer planes are needed for options 's', 'G', 'Go ', 'Fs', 'Fo', 'FF', and 'f'.

    »Q6 - do not pre-merge concave or coplanar facets

    With 'Q6', Qhull does not pre-merge concave or coplanar facets. This demonstrates the effect of "dents" when using 'Qx'.

    »Q7 - depth-first processing instead of breadth-first

    With 'Q7', Qhull processes facets in depth-first order instead of breadth-first order. This may increase the locality of reference in low dimensions. If so, Qhull may be able to use virtual memory effectively.

    In 5-d and higher, many facets are visible from each unprocessed point. So each iteration may access a large proportion of allocated memory. This makes virtual memory ineffectual. Once real memory is used up, Qhull will spend most of its time waiting for I/O.

    Under 'Q7', Qhull runs slower and the facets may be wider.

    »Q8 - ignore near-interior points

    With 'Q8' and merging, Qhull does not process interior points that are near to a facet (as defined by qh_RATIOnearInside in user.h). This avoids partitioning steps. It may miss a coplanar point when adjusting outer hulls in qh_check_maxout(). The best value for qh_RATIOnearInside is not known. Options 'Q8 Qc' may be sufficient.

    »Q9 - process furthest of furthest points

    With 'Q9', Qhull processes the furthest point of all outside sets. This may reduce precision problems. The furthest point of all outside sets is not necessarily the furthest point from the convex hull.

    »Q10 - no special processing for narrow distributions

    With 'Q10', Qhull does not special-case narrow distributions. See Limitations of merged facets for more information.

    »Q11 - copy normals and recompute centrums for tricoplanar facets

    Option 'Qt' triangulates non-simplicial facets into "tricoplanar" facets. Normally tricoplanar facets share the same normal, centrum, and Voronoi vertex. They can not be merged or replaced. With option 'Q11', Qhull duplicates the normal and Voronoi vertex. It recomputes the centrum.

    Use 'Q11' if you use the Qhull library to add points incrementally and call qh_triangulate() after each point. Otherwise, Qhull will report an error when it tries to merge and replace a tricoplanar facet.

    With sufficient merging and new points, option 'Q11' may lead to precision problems such as duplicate ridges and concave facets. For example, if qh_triangulate() is added to qh_addpoint(), RBOX 1000 s W1e-12 t1001813667 P0 | QHULL d Q11 Tv, reports an error due to a duplicate ridge.

    »Q12 - do not error on wide merge due to duplicate ridge and nearly coincident points

    In 3-d and higher Delaunay Triangulations or 4-d and higher convex hulls, multiple, nearly coincident points may lead to very wide facets. An error is reported if a merge across a duplicate ridge would increase the facet width by 100x or more.

    Use option 'Q12' to log a warning instead of throwing an error.

    For Delaunay triangulations, a bounding box may alleviate this error (e.g., rbox 500 C1,1E-13 t c G1 | qhull d). This avoids the ill-defined edge between upper and lower convex hulls. The problem will be fixed in a future release of Qhull.

    To demonstrate the problem, use rbox option 'Cn,r,m' to generate nearly coincident points. For more information, see "Nearly coincident points on an edge" in Nearly coincident points on an edge.

    »Q14 - do not rename vertices that create a duplicate ridge

    In 3-d and higher, nearly adjacent vertices may lead to duplicate ridges and wide merges (see "Nearly coincident points within 1e-13" in Imprecision in Qhull).

    Duplicate ridges are removed by renaming one of the nearly adjacent vertices to another vertex. Facets containing this vertex may become redundant. If so, they are merged into an adjacent facet. At most FIXUP attempts will be made. The cost for detecting this situation is approximately FIXUP%. For each point added to the hull, Qhull delays attaching the cone of new facets to the horizon facets.

    This feature is turned off with option Q14. It is also turned off for option 'Qg' (Only build good facets), or if pre-merging is turned off (e.g., option 'Q0', do not pre-merge facets with 'C-0' or 'Qx').

    »Q15 - allow wide maxout

    FIXUP -- document Q15 and Q16 -- add to index and to 'qhull .'


    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions


    The Geometry Center Home Page

    Comments to: qhull@qhull.org
    Created: Sept. 25, 1995 --- Last modified: see top

    geometry/vignettes/qhull/html/qh-optf.html0000644000176200001440000007526513431000557020467 0ustar liggesusers Qhull format options (F)

    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions


    [delaunay] Qhull format options (F)

    This section lists the format options for Qhull. These options are indicated by 'F' followed by a letter. See Output, Print, and Geomview for other output options.

    Copyright © 1995-2018 C.B. Barber


    » Programs OptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions

    Additional input & output formats

    These options allow for automatic processing of Qhull output. Options 'i', 'o', 'n', and 'p' may also be used.

    Summary and control
    FA
    compute total area and volume for option 's'
    FV
    print average vertex (interior point for 'qhalf')
    FQ
    print command for qhull and input
    FO
    print options to stderr or stdout
    FS
    print sizes: total area and volume
    Fs
    print summary: dim, #points, total vertices and facets, #vertices, #facets, max outer and inner plane
    Fd
    use format for input (offset first)
    FD
    use cdd format for normals (offset first)
    FM
    print Maple output (2-d and 3-d)
    Facets, points, and vertices
    Fa
    print area for each facet
    FC
    print centrum for each facet
    Fc
    print coplanar points for each facet
    Fx
    print extreme points (i.e., vertices) of convex hull.
    FF
    print facets w/o ridges
    FI
    print ID for each facet
    Fi
    print inner planes for each facet
    Fm
    print merge count for each facet (511 max)
    FP
    print nearest vertex for coplanar points
    Fn
    print neighboring facets for each facet
    FN
    print neighboring facets for each point
    Fo
    print outer planes for each facet
    Ft
    print triangulation with added points
    Fv
    print vertices for each facet
    Delaunay, Voronoi, and halfspace
    Fx
    print extreme input sites of Delaunay triangulation or Voronoi diagram.
    Fp
    print points at halfspace intersections
    Fi
    print separating hyperplanes for inner, bounded Voronoi regions
    Fo
    print separating hyperplanes for outer, unbounded Voronoi regions
    Fv
    print Voronoi diagram as ridges for each input pair
    FC
    print Voronoi vertex ("center") for each facet

    »Fa - print area for each facet

    The first line is the number of facets. The remaining lines are the area for each facet, one facet per line. See 'FA' and 'FS' for computing the total area and volume.

    Use 'PAn' for printing the n largest facets. Use option 'PFn' for printing facets larger than n.

    For Delaunay triangulations, the area is the area of each Delaunay triangle. For Voronoi vertices, the area is the area of the dual facet to each vertex.

    Qhull uses the centrum and ridges to triangulate non-simplicial facets. The area for non-simplicial facets is the sum of the areas for each triangle. It is an approximation of the actual area. The ridge's vertices are projected to the facet's hyperplane. If a vertex is far below a facet (qh_WIDEcoplanar in user.h), the corresponding triangles are ignored.

    For non-simplicial facets, vertices are often below the facet's hyperplane. If so, the approximation is less than the actual value and it may be significantly less.

    »FA - compute total area and volume for option 's'

    With option 'FA', Qhull includes the total area and volume in the summary ('s'). Option 'FS' also includes the total area and volume. If facets are merged, the area and volume are approximations. Option 'FA' is automatically set for options 'Fa', 'PAn', and 'PFn'.

    With 'qdelaunay s FA', Qhull computes the total area of the Delaunay triangulation. This equals the volume of the convex hull of the data points. With options 'qdelaunay Qu s FA', Qhull computes the total area of the furthest-site Delaunay triangulation. This equals of the total area of the Delaunay triangulation.

    See 'Fa' for further details. Option 'FS' also computes the total area and volume.

    »Fc - print coplanar points for each facet

    The output starts with the number of facets. Then each facet is printed one per line. Each line is the number of coplanar points followed by the point ids.

    By default, option 'Fc' reports coplanar points ('Qc'). You may also use option 'Qi'. Options 'Qi Fc' prints interior points while 'Qci Fc' prints both coplanar and interior points.

    Each coplanar point or interior point is assigned to the facet it is furthest above (resp., least below).

    Use 'Qc p' to print vertex and coplanar point coordinates. Use 'Fv' to print vertices.

    »FC - print centrum or Voronoi vertex for each facet

    The output starts with the dimension followed by the number of facets. Then each facet centrum is printed, one per line. For qvoronoi, Voronoi vertices are printed instead.

    »Fd - use cdd format for input

    The input starts with comments. The first comment is reported in the summary. Data starts after a "begin" line. The next line is the number of points followed by the dimension plus one and "real" or "integer". Then the points are listed with a leading "1" or "1.0". The data ends with an "end" line.

    For halfspaces ('qhalf Fd'), the input format is the same. Each halfspace starts with its offset. The signs of the offset and coefficients are the opposite of Qhull's convention. The first two lines of the input may be an interior point in 'FV' format.

    »FD - use cdd format for normals

    Option 'FD' prints normals ('n', 'Fo', 'Fi') or points ('p') in cdd format. The first line is the command line that invoked Qhull. Data starts with a "begin" line. The next line is the number of normals or points followed by the dimension plus one and "real". Then the normals or points are listed with the offset before the coefficients. The offset for points is 1.0. For normals, the offset and coefficients use the opposite sign from Qhull. The data ends with an "end" line.

    »FF - print facets w/o ridges

    Option 'FF' prints all fields of all facets (as in 'f') without printing the ridges. This is useful in higher dimensions where a facet may have many ridges. For simplicial facets, options 'FF' and 'f ' are equivalent.

    »Fi - print inner planes for each facet

    The first line is the dimension plus one. The second line is the number of facets. The remainder is one inner plane per line. The format is the same as option 'n'.

    The inner plane is a plane that is below the facet's vertices. It is an offset from the facet's hyperplane. It includes a roundoff error for computing the vertex distance.

    Note that the inner planes for Geomview output ('Gi') include an additional offset for vertex visualization and roundoff error.

    »Fi - print separating hyperplanes for inner, bounded Voronoi regions

    With qvoronoi, 'Fi' prints the separating hyperplanes for inner, bounded regions of the Voronoi diagram. The first line is the number of ridges. Then each hyperplane is printed, one per line. A line starts with the number of indices and floats. The first pair of indices indicates an adjacent pair of input sites. The next d floats are the normalized coefficients for the hyperplane, and the last float is the offset. The hyperplane is oriented toward 'QVn' (if defined), or the first input site of the pair.

    Use 'Fo' for unbounded regions, and 'Fv' for the corresponding Voronoi vertices.

    Use 'Tv' to verify that the hyperplanes are perpendicular bisectors. It will list relevant statistics to stderr. The hyperplane is a perpendicular bisector if the midpoint of the input sites lies on the plane, all Voronoi vertices in the ridge lie on the plane, and the angle between the input sites and the plane is ninety degrees. This is true if all statistics are zero. Roundoff and computation errors make these non-zero. The deviations appear to be largest when the corresponding Delaunay triangles are large and thin; for example, the Voronoi diagram of nearly cospherical points.

    »FI - print ID for each facet

    Print facet identifiers. These are used internally and listed with options 'f' and 'FF'. Options 'Fn ' and 'FN' use facet identifiers for negative indices.

    »Fm - print merge count for each facet

    The first line is the number of facets. The remainder is the number of merges for each facet, one per line. At most 511 merges are reported for a facet. See 'PMn' for printing the facets with the most merges.

    »FM - print Maple output

    Qhull writes a Maple file for 2-d and 3-d convex hulls, 2-d and 3-d halfspace intersections, and 2-d Delaunay triangulations. Qhull produces a 2-d or 3-d plot.

    Warning: This option has not been tested in Maple.

    [From T. K. Abraham with help from M. R. Feinberg and N. Platinova.] The following steps apply while working within the Maple worksheet environment :

    1. Generate the data and store it as an array . For example, in 3-d, data generated in Maple is of the form : x[i],y[i],z[i]

    2. Create a single variable and assign the entire array of data points to this variable. Use the "seq" command within square brackets as shown in the following example. (The square brackets are essential for the rest of the steps to work.)

      >data:=[seq([x[i],y[i],z[i]],i=1..n)]:# here n is the number of data points

    3. Next we need to write the data to a file to be read by qhull. Before writing the data to a file, make sure that the qhull executable files and the data file lie in the same subdirectory. If the executable files are stored in the "C:\qhull3.1\" subdirectory, then save the file in the same subdirectory, say "C:\qhull3.1\datafile.txt". For the sake of integrity of the data file , it is best to first ensure that the data file does not exist before writing into the data file. This can be done by running a delete command first . To write the data to the file, use the "writedata" and the "writedata[APPEND]" commands as illustrated in the following example :

      >system("del c:\\qhull3.1\\datafile.txt");#To erase any previous versions of the file
      >writedata("c:\\qhull3.1\\datafile.txt ",[3, nops(data)]);#writing in qhull format
      >writedata[APPEND]("c:\\ qhull3.1\\datafile.txt ", data);#writing the data points

    4. Use the 'FM' option to produce Maple output. Store the output as a ".mpl" file. For example, using the file we created above, we type the following (in DOS environment)

      qconvex s FM <datafile.txt >dataplot.mpl

    5. To read 3-d output in Maple, we use the 'read' command followed by a 'display3d' command. For example (in Maple environment):

      >with (plots):
      >read `c:\\qhull3.1\\dataplot.mpl`:#IMPORTANT - Note that the punctuation mark used is ' and NOT '. The correct punctuation mark is the one next to the key for "1" (not the punctuation mark near the enter key)
      > qhullplot:=%:
      > display3d(qhullplot);

    For Delaunay triangulation orthogonal projection is better.

    For halfspace intersections, Qhull produces the dual convex hull.

    See Is Qhull available for Maple? for other URLs.

    »Fn - print neighboring facets for each facet

    The output starts with the number of facets. Then each facet is printed one per line. Each line is the number of neighbors followed by an index for each neighbor. The indices match the other facet output formats.

    For simplicial facets, each neighbor is opposite the corresponding vertex (option 'Fv'). Do not compare to option 'i'. Option 'i' orients facets by reversing the order of two vertices. For non-simplicial facets, the neighbors are unordered.

    A negative index indicates an unprinted facet due to printing only good facets ('Pg', qdelaunay, qvoronoi). It is the negation of the facet's ID (option 'FI'). For example, negative indices are used for facets "at infinity" in the Delaunay triangulation.

    »FN - print neighboring facets for each point

    The first line is the number of points. Then each point is printed, one per line. For unassigned points (either interior or coplanar), the line is "0". For assigned coplanar points ('Qc'), the line is "1" followed by the index of the facet that is furthest below the point. For assigned interior points ('Qi'), the line is "1" followed by the index of the facet that is least above the point. For vertices that do not belong to good facet, the line is "0"

    For vertices of good facets, the line is the number of neighboring facets followed by the facet indices. The indices correspond to the other 'F' formats. In 4-d and higher, the facets are sorted by index. In 3-d, the facets are in adjacency order (not oriented).

    A negative index indicates an unprinted facet due to printing only good facets (qdelaunay, qvoronoi, 'Pdk', 'Pg'). It is the negation of the facet's ID (' FI'). For example, negative indices are used for facets "at infinity" in the Delaunay triangulation.

    For Voronoi vertices, option 'FN' lists the vertices of the Voronoi region for each input site. Option 'FN' lists the regions in site ID order. Option 'FN' corresponds to the second half of option 'o'. To convert from 'FN' to 'o', replace negative indices with zero and increment non-negative indices by one.

    If you are using the Qhull library or C++ interface, option 'FN' has the side effect of reordering the neighbors for a vertex

    »Fo - print outer planes for each facet

    The first line is the dimension plus one. The second line is the number of facets. The remainder is one outer plane per line. The format is the same as option 'n'.

    The outer plane is a plane that is above all points. It is an offset from the facet's hyperplane. It includes a roundoff error for computing the point distance. When testing the outer plane (e.g., 'Tv'), another roundoff error should be added for the tested point.

    If outer planes are not checked ('Q5') or not computed (!qh_MAXoutside), the maximum, computed outside distance is used instead. This can be much larger than the actual outer planes.

    Note that the outer planes for Geomview output ('G') include an additional offset for vertex/point visualization, 'lines closer,' and roundoff error.

    »Fo - print separating hyperplanes for outer, unbounded Voronoi regions

    With qvoronoi, 'Fo' prints the separating hyperplanes for outer, unbounded regions of the Voronoi diagram. The first line is the number of ridges. Then each hyperplane is printed, one per line. A line starts with the number of indices and floats. The first pair of indices indicates an adjacent pair of input sites. The next d floats are the normalized coefficients for the hyperplane, and the last float is the offset. The hyperplane is oriented toward 'QVn' (if defined), or the first input site of the pair.

    Option 'Fo' gives the hyperplanes for the unbounded rays of the unbounded regions of the Voronoi diagram. Each hyperplane goes through the midpoint of the corresponding input sites. The rays are directed away from the input sites.

    Use 'Fi' for bounded regions, and 'Fv' for the corresponding Voronoi vertices. Use 'Tv' to verify that the corresponding Voronoi vertices lie on the hyperplane.

    »FO - print list of selected options

    Lists selected options and default values to stderr. Additional 'FO's are printed to stdout.

    »Fp - print points at halfspace intersections

    The first line is the number of intersection points. The remainder is one intersection point per line. A intersection point is the intersection of d or more halfspaces from 'qhalf'. It corresponds to a facet of the dual polytope. The "infinity" point [-10.101,-10.101,...] indicates an unbounded intersection.

    If [x,y,z] are the dual facet's normal coefficients and b<0 is its offset, the halfspace intersection occurs at [x/-b,y/-b,z/-b] plus the interior point. If b>=0, the halfspace intersection is unbounded.

    »FP - print nearest vertex for coplanar points

    The output starts with the number of coplanar points. Then each coplanar point is printed one per line. Each line is the point ID of the closest vertex, the point ID of the coplanar point, the corresponding facet ID, and the distance. Sort the lines to list the coplanar points nearest to each vertex.

    Use options 'Qc' and/or 'Qi' with 'FP'. Options 'Qc FP' prints coplanar points while 'Qci FP' prints coplanar and interior points. Option 'Qc' is automatically selected if 'Qi' is not selected.

    For Delaunay triangulations (qdelaunay or qvoronoi), a coplanar point is nearly incident to a vertex. The distance is the distance in the original point set.

    If imprecision problems are severe, Qhull will delete input sites when constructing the Delaunay triangulation. Option 'FP' will list these points along with coincident points.

    If there are many coplanar or coincident points and non-simplicial facets are triangulated ('Qt'), option 'FP' may be inefficient. It redetermines the original vertex set for each coplanar point.

    »FQ - print command for qhull and input

    Prints qhull and input command, e.g., "rbox 10 s | qhull FQ". Option 'FQ' may be repeated multiple times.

    »Fs - print summary

    The first line consists of number of integers ("10") followed by the:

    • dimension
    • number of points
    • number of vertices
    • number of facets
    • number of vertices selected for output
    • number of facets selected for output
    • number of coplanar points for selected facets
    • number of nonsimplicial or merged facets selected for output
    • number of deleted vertices
    • number of triangulated facets ('Qt')

    The second line consists of the number of reals ("2") followed by the:

    • maximum offset to an outer plane
    • minimum offset to an inner plane.
    Roundoff and joggle are included.

    For Delaunay triangulations and Voronoi diagrams, the number of deleted vertices should be zero. If greater than zero, then the input is highly degenerate and coplanar points are not necessarily coincident points. For example, 'RBOX 1000 s W1e-13 t995138628 | QHULL d Qbb' reports deleted vertices; the input is nearly cospherical.

    Later versions of Qhull may produce additional integers or reals.

    »FS - print sizes

    The first line consists of the number of integers ("0"). The second line consists of the number of reals ("2"), followed by the total facet area, and the total volume. Later versions of Qhull may produce additional integers or reals.

    The total volume measures the volume of the intersection of the halfspaces defined by each facet. It is computed from the facet area. Both area and volume are approximations for non-simplicial facets. See option 'Fa ' for further notes. Option 'FA ' also computes the total area and volume.

    »Ft - print triangulation

    Prints a triangulation with added points for non-simplicial facets. The output is

    • The first line is the dimension
    • The second line is the number of points, the number of facets, and the number of ridges.
    • All of the input points follow, one per line.
    • The centrums follow, one per non-simplicial facet
    • Then the facets follow as a list of point indices preceded by the number of points. The simplices are oriented.

    For convex hulls with simplicial facets, the output is the same as option 'o'.

    The added points are the centrums of the non-simplicial facets. Except for large facets, the centrum is the average vertex coordinate projected to the facet's hyperplane. Large facets may use an old centrum to avoid recomputing the centrum after each merge. In either case, the centrum is clearly below neighboring facets. See Precision issues.

    The new simplices will not be clearly convex with their neighbors and they will not satisfy the Delaunay property. They may even have a flipped orientation. Use triangulated input ('Qt') for Delaunay triangulations.

    For Delaunay triangulations with simplicial facets, the output is the same as option 'o' without the lifted coordinate. Since 'Ft' is invalid for merged Delaunay facets, option 'Ft' is not available for qdelaunay or qvoronoi. It may be used with joggled input ('QJ') or triangulated output ('Qt'), for example, rbox 10 c G 0.01 | qhull d QJ Ft

    If you add a point-at-infinity with 'Qz', it is printed after the input sites and before any centrums. It will not be used in a Delaunay facet.

    »Fv - print vertices for each facet

    The first line is the number of facets. Then each facet is printed, one per line. Each line is the number of vertices followed by the corresponding point ids. Vertices are listed in the order they were added to the hull (the last one added is the first listed).

    Option 'i' also lists the vertices, but it orients facets by reversing the order of two vertices. Option 'i' triangulates non-simplicial, 4-d and higher facets by adding vertices for the centrums.

    »Fv - print Voronoi diagram

    With qvoronoi, 'Fv' prints the Voronoi diagram or furthest-site Voronoi diagram. The first line is the number of ridges. Then each ridge is printed, one per line. The first number is the count of indices. The second pair of indices indicates a pair of input sites. The remaining indices list the corresponding ridge of Voronoi vertices. Vertex 0 is the vertex-at-infinity. It indicates an unbounded ray.

    All vertices of a ridge are coplanar. If the ridge is unbounded, add the midpoint of the pair of input sites. The unbounded ray is directed from the Voronoi vertices to infinity.

    Use 'Fo' for separating hyperplanes of outer, unbounded regions. Use 'Fi' for separating hyperplanes of inner, bounded regions.

    Option 'Fv' does not list ridges that require more than one midpoint. For example, the Voronoi diagram of cospherical points lists zero ridges (e.g., 'rbox 10 s | qvoronoi Fv Qz'). Other examples are the Voronoi diagrams of a rectangular mesh (e.g., 'rbox 27 M1,0 | qvoronoi Fv') or a point set with a rectangular corner (e.g., 'rbox P4,4,4 P4,2,4 P2,4,4 P4,4,2 10 | qvoronoi Fv'). Both cases miss unbounded rays at the corners. To determine these ridges, surround the points with a large cube (e.g., 'rbox 10 s c G2.0 | qvoronoi Fv Qz'). The cube needs to be large enough to bound all Voronoi regions of the original point set. Please report any other cases that are missed. If you can formally describe these cases or write code to handle them, please send email to qhull@qhull.org.

    »FV - print average vertex

    The average vertex is the average of all vertex coordinates. It is an interior point for halfspace intersection. The first line is the dimension and "1"; the second line is the coordinates. For example,

    qconvex FV n | qhalf Fp

    prints the extreme points of the original point set (roundoff included).

    »Fx - print extreme points (vertices) of convex hulls and Delaunay triangulations

    The first line is the number of points. The following lines give the index of the corresponding points. The first point is '0'.

    In 2-d, the extreme points (vertices) are listed in counterclockwise order (by qh_ORIENTclock in user.h).

    In 3-d and higher convex hulls, the extreme points (vertices) are sorted by index. This is the same order as option 'p' when it doesn't include coplanar or interior points.

    For Delaunay triangulations, 'Fx' lists the extreme points of the input sites (i.e., the vertices of their convex hull). The points are unordered.


    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions


    The Geometry Center Home Page

    Comments to: qhull@qhull.org
    Created: Sept. 25, 1995 --- Last modified: see top

    geometry/vignettes/qhull/html/qh--4d.gif0000644000176200001440000001042413431000556017665 0ustar liggesusersGIF87add>JJIQbKSNJILX\ZZZXXXVVSRQEF,GGHHMHKKJIQ]\NfOOdP[bVZPW[VVWZaX_Y`W^QRRW^Y.Z.+J^_JQ\]:<HHUVDFSdZOP:G?nZKLJKIJGH)KKMQJJJQE65GFIMHZf\bTUTSSSTQTUMUW]X^Y_LKV\V\RIRW=WV\W5W;OO#5HS3SW+WZ%Z6IJHI4KQKQUq[;=N!NK,AMMOOJMx!7IBA+LL89EF*J\qK(D67?POLLHFE[ZZDD4ONM8ITTSW SSRRF1LRRQQPPONNSDRMLL5)OLJKJLIKJJLHK-IJN,MIRKJMGF!\K4JJT\DE<"LL8:78DE(DwVNP\[ZNP\YZEDMOKMCCWUUO%JMKKLKJIL|JIHINHGF\c[bZa0LLNaOQU<>ZXRSSW^V]VGWKJW^FES9T@E[!\K7LP+Q\]!KJ9;NONoUSTNOOPDDNODIHKLJUe57FE,1DYZY[aY_TTTT|WOVO$JIOPOGTJJJILLLSjVJJJKHKLDL; LS`VK@K0ONJPNTRxX;<:<BC$EDLOpAEA@QQM68DC)GF\[[IEEXWWCDI>@UUTPOOOONJdjNMMMMLMKLV[AQIHLKKIL}JIILCK:MMKP\[KxZ;=KJWG$PDPPHHTgY 7G,dd9r$ FǘܹۖI BtÉ-FbbB 'M<ʗ.Ky0’(QΞ;5JH1HX\EJMa.9f#HDI$@(ϠC}zڑѐ᜺ۧ)a4x +ѽD8 Էma-)5ZOvs6b1tȺװckW&ۙry+My=N+7}3JWУP6Ǯ]쿗e=ܶm[W݁o>re9ȉ?lٶwmg\A#:1~eXeuRZM՘At5`#@0f?Ybq6|ThyH ȣ#X}&"(#Z\7"iV!!zh #]hՕXZ8#@ -u9D~|:" W_.F̜Tpw2hx}3 ]/I;shv-*xEʧ&Ph)lC9'-s4*~[gJZ)4I {|1+-> +K'9.ꩳB3 TnghfXf,-;7$cBy Y\\B e"$┱, )fqk#3+p2eZ2⤱دsq?#p@l ga4"q81k%i1L9mv*[:Y7m4`tuX" rH ޳Eս.,ll33qHb6 ֱ6̪=t*KSvcri.۴T"MwSFZ60؁s;!ՁsFy1E?C0con3Ɯ sԌ36_IHg!K[ӸARaO DdhaV_G,ˠs0-[ZJP!SKn-ZP/ ! hoxAPC .0f0 Z Pc2͎qC.%4y51OXTQ:B(@@kq q^̋&BzFs.Vq8@E0@5/p_g"4!}!GqA!b \FPi>DN^?:(QriGH!n xN ̦/Z Ґ\te:lB0& Ħ6 'Jq}0YlF !C ,Q"q a/K0QTH%FӨ q38B9(~Rc;xO hE-jјPhgz բI bphUN(NiFs@=՟/ZPvU%E-UDuQiN7*?;x4[TuPUEcִ֚4MQLݕҘa@Jt2mh5}͐oGiv]<*Xax9ETO:3>#L`B2!^ /4ġ@x^-H]t>8*ȭ|3v-4\A=caEaG}}O$}~MU 7/c4sRp РL7'l}@o~LZqG 8(Hp =@dmsoր7' їZ 0]p{of{z7d} ؁>pp(K*PYLՀD Wx h؏  0+&7 A0PTV H0 xz@s D gpIIoP)z 99y h#zXD@ b> 1ɂHo҈ 5 3q;HXA `Fseꇌj}0 k}0/ и{pQ<8voyЊo?,A &p S{ick7,yOcW}W ɠb0/Fwɋ>6vZ:9 F0/R 1HgK4IٜtйL`V}/G IMi, Pɜ)5Mُ@ɛYO 9L}幜ٜQYIiY0 `yܹe iY0Wiُ/MɔG湜P0:9 N0R 𚃘 1Z z@ ,;ʣf(y51 U Z ` MꤻgBdYT/`:yI}v0j,nfg Jѧ٠nZcɨ0ٝyڡɞL   ~J y7'z1ʪJ**" &Z?ګ-:/Jo9 `B l|PZ)kI:+6:&:ખ`銋Ю$j`FʦzRZ w:K 1媘 HZjʚ&  {Щf e;geometry/vignettes/qhull/html/rbox.txt0000644000176200001440000001214213431000556017716 0ustar liggesusers rbox(1) rbox(1) NAME rbox - generate point distributions for qhull SYNOPSIS Command "rbox" (w/o arguments) lists the options. DESCRIPTION rbox generates random or regular points according to the options given, and outputs the points to stdout. The points are generated in a cube, unless 's' or given. The format of the output is the following: first line contains the dimension and a comment, second line contains the num- ber of points, and the following lines contain the points, one point per line. Points are represented by their coor- dinate values. EXAMPLES rbox 10 10 random points in the unit cube centered at the origin. rbox 10 s D2 10 random points on a 2-d circle. rbox 100 W0 100 random points on the surface of a cube. rbox 1000 s D4 1000 random points on a 4-d sphere. rbox c D5 O0.5 a 5-d hypercube with one corner at the origin. rbox d D10 a 10-d diamond. rbox x 1000 r W0 100 random points on the surface of a fixed simplex rbox y D12 a 12-d simplex. rbox l 10 10 random points along a spiral rbox l 10 r 10 regular points along a spiral plus two end points rbox 1000 L10000 D4 s 1000 random points on the surface of a narrow lens. rbox c G2 d G3 a cube with coordinates +2/-2 and a diamond with Geometry Center August 10, 1998 1 rbox(1) rbox(1) coordinates +3/-3. rbox 64 M3,4 z a rotated, {0,1,2,3} x {0,1,2,3} x {0,1,2,3} lat- tice (Mesh) of integer points. rbox P0 P0 P0 P0 P0 5 copies of the origin in 3-d. Try 'rbox P0 P0 P0 P0 P0 | qhull QJ'. r 100 s Z1 G0.1 two cospherical 100-gons plus another cospherical point. 100 s Z1 a cone of points. 100 s Z1e-7 a narrow cone of points with many precision errors. OPTIONS n number of points Dn dimension n-d (default 3-d) Bn bounding box coordinates (default 0.5) l spiral distribution, available only in 3-d Ln lens distribution of radius n. May be used with 's', 'r', 'G', and 'W'. Mn,m,r lattice (Mesh) rotated by {[n,-m,0], [m,n,0], [0,0,r], ...}. Use 'Mm,n' for a rigid rotation with r = sqrt(n^2+m^2). 'M1,0' is an orthogonal lattice. For example, '27 M1,0' is {0,1,2} x {0,1,2} x {0,1,2}. s cospherical points randomly generated in a cube and projected to the unit sphere x simplicial distribution. It is fixed for option 'r'. May be used with 'W'. y simplicial distribution plus a simplex. Both 'x' and 'y' generate the same points. Wn restrict points to distance n of the surface of a sphere or a cube c add a unit cube to the output c Gm add a cube with all combinations of +m and -m to the output Geometry Center August 10, 1998 2 rbox(1) rbox(1) d add a unit diamond to the output. d Gm add a diamond made of 0, +m and -m to the output Cn,r,m add n nearly coincident points within radius r of m points Pn,m,r add point [n,m,r] to the output first. Pad coordi- nates with 0.0. n Remove the command line from the first line of out- put. On offset the data by adding n to each coordinate. t use time in seconds as the random number seed (default is command line). tn set the random number seed to n. z generate integer coordinates. Use 'Bn' to change the range. The default is 'B1e6' for six-digit coordinates. In R^4, seven-digit coordinates will overflow hyperplane normalization. Zn s restrict points to a disk about the z+ axis and the sphere (default Z1.0). Includes the opposite pole. 'Z1e-6' generates degenerate points under single precision. Zn Gm s same as Zn with an empty center (default G0.5). r s D2 generate a regular polygon r s Z1 G0.1 generate a regular cone BUGS Some combinations of arguments generate odd results. Report bugs to qhull_bug@qhull.org, other correspon- dence to qhull@qhull.org SEE ALSO qhull(1) AUTHOR C. Bradford Barber bradb@shore.net Geometry Center August 10, 1998 3 geometry/vignettes/qhull/html/qh-eg.html0000644000176200001440000007441513431000557020106 0ustar liggesusers Examples of Qhull

    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
    To: Qhull examples: Table of Contents (please wait while loading)


    [halfspace] Examples of Qhull

    This section of the Qhull manual will introduce you to Qhull and its options. Each example is a file for viewing with Geomview. You will need to use a Unix computer with a copy of Geomview.

    If you are not running Unix, you can view pictures for some of the examples. To understand Qhull without Geomview, try the examples in Programs and Programs/input. You can also try small examples that you compute by hand. Use rbox to generate examples.

    To generate the Geomview examples, execute the shell script eg/q_eg. It uses rbox. The shell script eg/q_egtest generates test examples, and eg/q_test exercises the code. If you find yourself viewing the inside of a 3-d example, use Geomview's normalization option on the 'obscure' menu.

    Copyright © 1995-2018 C.B. Barber


    »Qhull examples: Table of Contents



    »2-d and 3-d examples

    »rbox c D3 | qconvex G >eg.01.cube

    The first example is a cube in 3-d. The color of each facet indicates its normal. For example, normal [0,0,1] along the Z axis is (r=0.5, g=0.5, b=1.0). With the 'Dn' option in rbox, you can generate hypercubes in any dimension. Above 7-d the number of intermediate facets grows rapidly. Use 'TFn' to track qconvex's progress. Note that each facet is a square that qconvex merged from coplanar triangles.

    »rbox c d G3.0 | qconvex G >eg.02.diamond.cube

    The second example is a cube plus a diamond ('d') scaled by rbox's 'G' option. In higher dimensions, diamonds are much simpler than hypercubes.

    »rbox s 100 D3 | qconvex G >eg.03.sphere

    The rbox s option generates random points and projects them to the d-sphere. All points should be on the convex hull. Notice that random points look more clustered than you might expect. You can get a smoother distribution by merging facets and printing the vertices, e.g., rbox 1000 s | qconvex A-0.95 p | qconvex G >eg.99.

    »rbox s 100 D2 | qconvex G >eg.04.circle

    In 2-d, there are many ways to generate a convex hull. One of the earliest algorithms, and one of the fastest, is the 2-d Quickhull algorithm [c.f., Preparata & Shamos '85]. It was the model for Qhull.

    »rbox 10 l | qconvex G >eg.05.spiral

    One rotation of a spiral.

    »rbox 1000 D2 | qconvex C-0.03 Qc Gapcv >eg.06.merge.square

    This demonstrates how Qhull handles precision errors. Option 'C-0.03' requires a clearly convex angle between adjacent facets. Otherwise, Qhull merges the facets.

    This is the convex hull of random points in a square. The facets have thickness because they must be outside all points and must include their vertices. The colored lines represent the original points and the spheres represent the vertices. Floating in the middle of each facet is the centrum. Each centrum is at least 0.03 below the planes of its neighbors. This guarantees that the facets are convex.

    »rbox 1000 D3 | qconvex G >eg.07.box

    Here's the same distribution but in 3-d with Qhull handling machine roundoff errors. Note the large number of facets.

    »rbox c G0.4 s 500 | qconvex G >eg.08a.cube.sphere

    The sphere is just barely poking out of the cube. Try the same distribution with randomization turned on ('Qr'). This turns Qhull into a randomized incremental algorithm. To compare Qhull and randomization, look at the number of hyperplanes created and the number of points partitioned. Don't compare CPU times since Qhull's implementation of randomization is inefficient. The number of hyperplanes and partitionings indicate the dominant costs for Qhull. With randomization, you'll notice that the number of facets created is larger than before. This is especially true as you increase the number of points. It is because the randomized algorithm builds most of the sphere before it adds the cube's vertices.

    »rbox d G0.6 s 500 | qconvex G >eg.08b.diamond.sphere

    This is a combination of the diamond distribution and the sphere.

    »rbox 100 L3 G0.5 s | qconvex G >eg.09.lens

    Each half of the lens distribution lies on a sphere of radius three. A directed search for the furthest facet below a point (e.g., qh_findbest in geom.c) may fail if started from an arbitrary facet. For example, if the first facet is on the opposite side of the lens, a directed search will report that the point is inside the convex hull even though it is outside. This problem occurs whenever the curvature of the convex hull is less than a sphere centered at the test point.

    To prevent this problem, Qhull does not use directed search all the time. When Qhull processes a point on the edge of the lens, it partitions the remaining points with an exhaustive search instead of a directed search (see qh_findbestnew in geom2.c).

    »How Qhull adds a point

    »rbox 100 s P0.5,0.5,0.5 | qconvex Ga QG0 >eg.10a.sphere.visible

    The next 4 examples show how Qhull adds a point. The point [0.5,0.5,0.5] is at one corner of the bounding box. Qhull adds a point using the beneath-beyond algorithm. First Qhull finds all of the facets that are visible from the point. Qhull will replace these facets with new facets.

    »rbox 100 s P0.5,0.5,0.5|qconvex Ga QG-0 >eg.10b.sphere.beyond

    These are the facets that are not visible from the point. Qhull will keep these facets.

    »rbox 100 s P0.5,0.5,0.5 | qconvex PG Ga QG0 >eg.10c.sphere.horizon

    These facets are the horizon facets; they border the visible facets. The inside edges are the horizon ridges. Each horizon ridge will form the base for a new facet.

    »rbox 100 s P0.5,0.5,0.5 | qconvex Ga QV0 PgG >eg.10d.sphere.cone

    This is the cone of points from the new point to the horizon facets. Try combining this image with eg.10c.sphere.horizon and eg.10a.sphere.visible.

    »rbox 100 s P0.5,0.5,0.5 | qconvex Ga >eg.10e.sphere.new

    This is the convex hull after [0.5,0.5,0.5] has been added. Note that in actual practice, the above sequence would never happen. Unlike the randomized algorithms, Qhull always processes a point that is furthest in an outside set. A point like [0.5,0.5,0.5] would be one of the first points processed.

    »rbox 100 s P0.5,0.5,0.5 | qhull Ga QV0g Q0 >eg.14.sphere.corner

    The 'QVn', 'QGn ' and 'Pdk' options define good facets for Qhull. In this case 'QV0' defines the 0'th point [0.5,0.5,0.5] as the good vertex, and 'Qg' tells Qhull to only build facets that might be part of a good facet. This technique reduces output size in low dimensions. It does not work with facet merging (turned off with 'Q0')

    »Triangulated output or joggled input

    »rbox 500 W0 | qconvex QR0 Qc Gvp >eg.15a.surface

    This is the convex hull of 500 points on the surface of a cube. Note the large, non-simplicial facet for each face. Qhull merges non-convex facets.

    If the facets were not merged, Qhull would report precision problems. For example, turn off facet merging with option 'Q0'. Qhull may report concave facets, flipped facets, or other precision errors:

    rbox 500 W0 | qhull QR0 Q0

    »rbox 500 W0 | qconvex QR0 Qt Qc Gvp >eg.15b.triangle

    Like the previous examples, this is the convex hull of 500 points on the surface of a cube. Option 'Qt' triangulates the non-simplicial facets. Triangulated output is particularly helpful for Delaunay triangulations.

    »rbox 500 W0 | qconvex QR0 QJ5e-2 Qc Gvp >eg.15c.joggle

    This is the convex hull of 500 joggled points on the surface of a cube. The option 'QJ5e-2' sets a very large joggle to make the effect visible. Notice that all of the facets are triangles. If you rotate the cube, you'll see red-yellow lines for coplanar points.

    With option 'QJ', Qhull joggles the input to avoid precision problems. It adds a small random number to each input coordinate. If a precision error occurs, it increases the joggle and tries again. It repeats this process until no precision problems occur.

    Joggled input is a simple solution to precision problems in computational geometry. Qhull can also merge facets to handle precision problems. See Merged facets or joggled input.

    »Delaunay and Voronoi diagrams

    »qdelaunay Qt <eg.data.17 GnraD2 >eg.17a.delaunay.2

    The input file, eg.data.17, consists of a square, 15 random points within the outside half of the square, and 6 co-circular points centered on the square.

    The Delaunay triangulation is the triangulation with empty circumcircles. The input for this example is unusual because it includes six co-circular points. Every triangular subset of these points has the same circumcircle. Option 'Qt' triangulates the co-circular facet.

    »qdelaunay <eg.data.17 GnraD2 >eg.17b.delaunay.2i

    This is the same example without triangulated output ('Qt'). qdelaunay merges the non-unique Delaunay triangles into a hexagon.

    »qdelaunay <eg.data.17 Ga >eg.17c.delaunay.2-3

    This is how Qhull generated both diagrams. Use Geomview's 'obscure' menu to turn off normalization, and Geomview's 'cameras' menu to turn off perspective. Then load this object with one of the previous diagrams.

    The points are lifted to a paraboloid by summing the squares of each coordinate. These are the light blue points. Then the convex hull is taken. That's what you see here. If you look up the Z-axis, you'll see that points and edges coincide.

    »qvoronoi QJ <eg.data.17 Gna >eg.17d.voronoi.2

    The Voronoi diagram is the dual of the Delaunay triangulation. Here you see the original sites and the Voronoi vertices. Notice the each vertex is equidistant from three sites. The edges indicate the Voronoi region for a site. Qhull does not draw the unbounded edges. Instead, it draws extra edges to close the unbounded Voronoi regions. You may find it helpful to enclose the input points in a square. You can compute the unbounded rays from option 'Fo'.

    Instead of triangulated output ('Qt'), this example uses joggled input ('QJ'). Normally, you should use neither 'QJ' nor 'Qt' for Voronoi diagrams.

    »qvoronoi <eg.data.17 Gna >eg.17e.voronoi.2i

    This looks the same as the previous diagrams, but take a look at the data. Run 'qvoronoi p <eg/eg.data.17'. This prints the Voronoi vertices.

    With 'QJ', there are four nearly identical Voronoi vertices within 10^-11 of the origin. Option 'QJ' joggled the input. After the joggle, the cocircular input sites are no longer cocircular. The corresponding Voronoi vertices are similar but not identical.

    This example does not use options 'Qt' or 'QJ'. The cocircular input sites define one Voronoi vertex near the origin.

    Option 'Qt' would triangulate the corresponding Delaunay region into four triangles. Each triangle is assigned the same Voronoi vertex.

    » rbox c G0.1 d | qdelaunay Gt Qz <eg.17f.delaunay.3

    This is the 3-d Delaunay triangulation of a small cube inside a prism. Since the outside ridges are transparent, it shows the interior of the outermost facets. If you slice open the triangulation with Geomview's ginsu, you will see that the innermost facet is a cube. Note the use of 'Qz' to add a point "at infinity". This avoids a degenerate input due to cospherical points.

    »rbox 10 D2 d | qdelaunay Qu G >eg.18a.furthest.2-3

    The furthest-site Voronoi diagram contains Voronoi regions for points that are furthest from an input site. It is the dual of the furthest-site Delaunay triangulation. You can determine the furthest-site Delaunay triangulation from the convex hull of the lifted points (eg.17c.delaunay.2-3). The upper convex hull (blue) generates the furthest-site Delaunay triangulation.

    »rbox 10 D2 d | qdelaunay Qu Pd2 G >eg.18b.furthest-up.2-3

    This is the upper convex hull of the preceding example. The furthest-site Delaunay triangulation is the projection of the upper convex hull back to the input points. The furthest-site Voronoi vertices are the circumcenters of the furthest-site Delaunay triangles.

    »rbox 10 D2 d | qvoronoi Qu Gv >eg.18c.furthest.2

    This shows an incomplete furthest-site Voronoi diagram. It only shows regions with more than two vertices. The regions are artificially truncated. The actual regions are unbounded. You can print the regions' vertices with 'qvoronoi Qu o'.

    Use Geomview's 'obscure' menu to turn off normalization, and Geomview's 'cameras' menu to turn off perspective. Then load this with the upper convex hull.

    »rbox 10 D3 | qvoronoi QV5 p | qconvex G >eg.19.voronoi.region.3

    This shows the Voronoi region for input site 5 of a 3-d Voronoi diagram.

    »Facet merging for imprecision

    »rbox r s 20 Z1 G0.2 | qconvex G >eg.20.cone

    There are two things unusual about this cone. One is the large flat disk at one end and the other is the rectangles about the middle. That's how the points were generated, and if those points were exact, this is the correct hull. But rbox used floating point arithmetic to generate the data. So the precise convex hull should have been triangles instead of rectangles. By requiring convexity, Qhull has recovered the original design.

    »rbox 200 s | qhull Q0 R0.01 Gav Po >eg.21a.roundoff.errors

    This is the convex hull of 200 cospherical points with precision errors ignored ('Q0'). To demonstrate the effect of roundoff error, we've added a random perturbation ('R0.01') to every distance and hyperplane calculation. Qhull, like all other convex hull algorithms with floating point arithmetic, makes inconsistent decisions and generates wildly wrong results. In this case, one or more facets are flipped over. These facets have the wrong color. You can also turn on 'normals' in Geomview's appearances menu and turn off 'facing normals'. There should be some white lines pointing in the wrong direction. These correspond to flipped facets.

    Different machines may not produce this picture. If your machine generated a long error message, decrease the number of points or the random perturbation ('R0.01'). If it did not report flipped facets, increase the number of points or perturbation.

    »rbox 200 s | qconvex Qc R0.01 Gpav >eg.21b.roundoff.fixed

    Qhull handles the random perturbations and returns an imprecise sphere. In this case, the output is a weak approximation to the points. This is because a random perturbation of 'R0.01 ' is equivalent to losing all but 1.8 digits of precision. The outer planes float above the points because Qhull needs to allow for the maximum roundoff error.

    If you start with a smaller random perturbation, you can use joggle ('QJn') to avoid precision problems. You need to set n significantly larger than the random perturbation. For example, try 'rbox 200 s | qconvex Qc R1e-4 QJ1e-1'.

    »rbox 1000 s| qconvex C0.01 Qc Gcrp >eg.22a.merge.sphere.01

    »rbox 1000 s| qconvex C-0.01 Qc Gcrp >eg.22b.merge.sphere.-01

    »rbox 1000 s| qconvex C0.05 Qc Gcrpv >eg.22c.merge.sphere.05

    »rbox 1000 s| qconvex C-0.05 Qc Gcrpv >eg.22d.merge.sphere.-05

    The next four examples compare post-merging and pre-merging ('Cn' vs. 'C-n'). Qhull uses '-' as a flag to indicate pre-merging.

    Post-merging happens after the convex hull is built. During post-merging, Qhull repeatedly merges an independent set of non-convex facets. For a given set of parameters, the result is about as good as one can hope for.

    Pre-merging does the same thing as post-merging, except that it happens after adding each point to the convex hull. With pre-merging, Qhull guarantees a convex hull, but the facets are wider than those from post-merging. If a pre-merge option is not specified, Qhull handles machine round-off errors.

    You may see coplanar points appearing slightly outside the facets of the last example. This is becomes Geomview moves line segments forward toward the viewer. You can avoid the effect by setting 'lines closer' to '0' in Geomview's camera menu.

    »rbox 1000 | qconvex s Gcprvah C0.1 Qc >eg.23.merge.cube

    Here's the 3-d imprecise cube with all of the Geomview options. There's spheres for the vertices, radii for the coplanar points, dots for the interior points, hyperplane intersections, centrums, and inner and outer planes. The radii are shorter than the spheres because this uses post-merging ('C0.1') instead of pre-merging.

    »4-d objects

    With Qhull and Geomview you can develop an intuitive sense of 4-d surfaces. When you get into trouble, think of viewing the surface of a 3-d sphere in a 2-d plane.

    »rbox 5000 D4 | qconvex s GD0v Pd0:0.5 C-0.02 C0.1 >eg.24.merge.cube.4d-in-3d

    Here's one facet of the imprecise cube in 4-d. It's projected into 3-d (the 'GDn' option drops dimension n). Each ridge consists of two triangles between this facet and the neighboring facet. In this case, Geomview displays the topological ridges, i.e., as triangles between three vertices. That is why the cube looks lopsided.

    »rbox 5000 D4 | qconvex s C-0.02 C0.1 Gh >eg.30.4d.merge.cube

    Here is the equivalent in 4-d of the imprecise square and imprecise cube. It's the imprecise convex hull of 5000 random points in a hypercube. It's a full 4-d object so Geomview's ginsu does not work. If you view it in Geomview, you'll be inside the hypercube. To view 4-d objects directly, either load the 4dview module or the ndview module. For 4dview, you must have started Geomview in the same directory as the object. For ndview, initialize a set of windows with the prefab menu, and load the object through Geomview. The 4dview module includes an option for slicing along any hyperplane. If you do this in the x, y, or z plane, you'll see the inside of a hypercube.

    The 'Gh' option prints the geometric intersections between adjacent facets. Note the strong convexity constraint for post-merging ('C0.1'). It deletes the small facets.

    »rbox 20 D3 | qdelaunay G >eg.31.4d.delaunay

    The Delaunay triangulation of 3-d sites corresponds to a 4-d convex hull. You can't see 4-d directly but each facet is a 3-d object that you can project to 3-d. This is exactly the same as projecting a 2-d facet of a soccer ball onto a plane.

    Here we see all of the facets together. You can use Geomview's ndview to look at the object from several directions. Try turning on edges in the appearance menu. You'll notice that some edges seem to disappear. That's because the object is actually two sets of overlapping facets.

    You can slice the object apart using Geomview's 4dview. With 4dview, try slicing along the w axis to get a single set of facets and then slice along the x axis to look inside. Another interesting picture is to slice away the equator in the w dimension.

    »rbox 30 s D4 | qconvex s G Pd0d1d2D3

    This is the positive octant of the convex hull of 30 4-d points. When looking at 4-d, it's easier to look at just a few facets at once. If you picked a facet that was directly above you, then that facet looks exactly the same in 3-d as it looks in 4-d. If you pick several facets, then you need to imagine that the space of a facet is rotated relative to its neighbors. Try Geomview's ndview on this example.

    »Halfspace intersections

    »rbox 10 r s Z1 G0.3 | qconvex G >eg.33a.cone

    »rbox 10 r s Z1 G0.3 | qconvex FV n | qhalf G >eg.33b.cone.dual

    »rbox 10 r s Z1 G0.3 | qconvex FV n | qhalf Fp | qconvex G >eg.33c.cone.halfspace

    These examples illustrate halfspace intersection. The first picture is the convex hull of two 20-gons plus an apex. The second picture is the dual of the first. Try loading both at once. Each vertex of the second picture corresponds to a facet or halfspace of the first. The vertices with four edges correspond to a facet with four neighbors. Similarly the facets correspond to vertices. A facet's normal coefficients divided by its negative offset is the vertice's coordinates. The coordinates are the intersection of the original halfspaces.

    The third picture shows how Qhull can go back and forth between equivalent representations. It starts with a cone, generates the halfspaces that define each facet of the cone, and then intersects these halfspaces. Except for roundoff error, the third picture is a duplicate of the first.


    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
    To: Qhull examples: Table of Contents (please wait while loading)


    The Geometry Center Home Page

    Comments to: qhull@qhull.org
    Created: Sept. 25, 1995 --- Last modified: see top

    geometry/vignettes/qhull/html/qvoronoi.html0000644000176200001440000006703613431000557020762 0ustar liggesusers qvoronoi -- Voronoi diagram Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
    To: synopsis • input • outputs • controls • graphics • notes • conventions • options

    [voronoi]qvoronoi -- Voronoi diagram

    The Voronoi diagram is the nearest-neighbor map for a set of points. Each region contains those points that are nearer one input site than any other input site. It has many useful properties and applications. See the survey article by Aurenhammer ['91] and the detailed introduction by O'Rourke ['94]. The Voronoi diagram is the dual of the Delaunay triangulation.

    Example: rbox 10 D3 | qvoronoi s o TO result
    Compute the 3-d Voronoi diagram of 10 random points. Write a summary to the console and the Voronoi vertices and regions to 'result'. The first vertex of the result indicates unbounded regions.
     
    Example: rbox r y c G0.1 D2 | qvoronoi s o TO result
    Compute the 2-d Voronoi diagram of a triangle and a small square. Write a summary to the console and Voronoi vertices and regions to 'result'. Report a single Voronoi vertex for cocircular input sites. The first vertex of the result indicates unbounded regions. The origin is the Voronoi vertex for the square.
     
    Example: rbox r y c G0.1 D2 | qvoronoi Fv TO result
    Compute the 2-d Voronoi diagram of a triangle and a small square. Write a summary to the console and the Voronoi ridges to 'result'. Each ridge is the perpendicular bisector of a pair of input sites. Vertex "0" indicates unbounded ridges. Vertex "8" is the Voronoi vertex for the square.
     
    Example: rbox r y c G0.1 D2 | qvoronoi Fi
    Print the bounded, separating hyperplanes for the 2-d Voronoi diagram of a triangle and a small square. Note the four hyperplanes (i.e., lines) for Voronoi vertex "8". It is at the origin.

    Qhull computes the Voronoi diagram via the Delaunay triangulation. Each Voronoi vertex is the circumcenter of a facet of the Delaunay triangulation. Each Voronoi region corresponds to a vertex (i.e., input site) of the Delaunay triangulation.

    Qhull outputs the Voronoi vertices for each Voronoi region. With option 'Fv', it lists all ridges of the Voronoi diagram with the corresponding pairs of input sites. With options 'Fi' and 'Fo', it lists the bounded and unbounded separating hyperplanes. You can also output a single Voronoi region for further processing [see graphics].

    Use option 'Qz' if the input is circular, cospherical, or nearly so. It improves precision by adding a point "at infinity," above the corresponding paraboloid.

    See Qhull FAQ - Delaunay and Voronoi diagram questions.

    The 'qvonoroi' program is equivalent to 'qhull v Qbb' in 2-d to 3-d, and 'qhull v Qbb Qx' in 4-d and higher. It disables the following Qhull options: d n v Qbb QbB Qf Qg Qm Qr QR Qv Qx Qz TR E V Fa FA FC FD FS Ft FV Gt Q0,etc.

    Copyright © 1995-2018 C.B. Barber

    Voronoi image by KOOK Architecture, Silvan Oesterle and Michael Knauss.


    »qvoronoi synopsis

    qvoronoi- compute the Voronoi diagram.
        input (stdin): dimension, number of points, point coordinates
        comments start with a non-numeric character
    
    options (qh-voron.htm):
        Qu   - compute furthest-site Voronoi diagram
        Tv   - verify result: structure, convexity, and in-circle test
        .    - concise list of all options
        -    - one-line description of all options
    
    output options (subset):
        s    - summary of results (default)
        p    - Voronoi vertices
        o    - OFF file format (dim, Voronoi vertices, and Voronoi regions)
        FN   - count and Voronoi vertices for each Voronoi region
        Fv   - Voronoi diagram as Voronoi vertices between adjacent input sites
        Fi   - separating hyperplanes for bounded regions, 'Fo' for unbounded
        G    - Geomview output (2-d only)
        QVn  - Voronoi vertices for input point n, -n if not
        TO file- output results to file, may be enclosed in single quotes
    
    examples:
    rbox c P0 D2 | qvoronoi s o         rbox c P0 D2 | qvoronoi Fi
    rbox c P0 D2 | qvoronoi Fo          rbox c P0 D2 | qvoronoi Fv
    rbox c P0 D2 | qvoronoi s Qu Fv     rbox c P0 D2 | qvoronoi Qu Fo
    rbox c G1 d D2 | qvoronoi s p       rbox c P0 D2 | qvoronoi s Fv QV0
    

    »qvoronoi input

    The input data on stdin consists of:
    • dimension
    • number of points
    • point coordinates

    Use I/O redirection (e.g., qvoronoi < data.txt), a pipe (e.g., rbox 10 | qvoronoi), or the 'TI' option (e.g., qvoronoi TI data.txt).

    For example, this is four cocircular points inside a square. Their Voronoi diagram has nine vertices and eight regions. Notice the Voronoi vertex at the origin, and the Voronoi vertices (on each axis) for the four sides of the square.

    rbox s 4 W0 c G1 D2 > data
    2 RBOX s 4 W0 c D2
    8
    -0.4941988586954018 -0.07594397977563715
    -0.06448037284989526 0.4958248496365813
    0.4911154367094632 0.09383830681375946
    -0.348353580869097 -0.3586778257652367
        -1     -1
        -1      1
         1     -1
         1      1
    

    qvoronoi s p < data

    
    Voronoi diagram by the convex hull of 8 points in 3-d:
    
      Number of Voronoi regions: 8
      Number of Voronoi vertices: 9
      Number of non-simplicial Voronoi vertices: 1
    
    Statistics for: RBOX s 4 W0 c D2 | QVORONOI s p
    
      Number of points processed: 8
      Number of hyperplanes created: 18
      Number of facets in hull: 10
      Number of distance tests for qhull: 33
      Number of merged facets: 2
      Number of distance tests for merging: 102
      CPU seconds to compute hull (after input): 0.094
    
    2
    9
    4.217546450968612e-17 1.735507986399734
    -8.402566836762659e-17 -1.364368854147395
    0.3447488772716865 -0.6395484723719818
    1.719446929853986 2.136555906154247e-17
    0.4967882915039657 0.68662371396699
    -1.729928876283549 1.343733067524222e-17
    -0.8906163241424728 -0.4594150543829102
    -0.6656840313875723 0.5003013793414868
    -7.318364664277155e-19 -1.188217818408333e-16
    

    » qvoronoi outputs

    These options control the output of Voronoi diagrams.

     
    Voronoi vertices
    p
    print the coordinates of the Voronoi vertices. The first line is the dimension. The second line is the number of vertices. Each remaining line is a Voronoi vertex.
    Fn
    list the neighboring Voronoi vertices for each Voronoi vertex. The first line is the number of Voronoi vertices. Each remaining line starts with the number of neighboring vertices. Negative vertices (e.g., -1) indicate vertices outside of the Voronoi diagram. In the circle-in-box example, the Voronoi vertex at the origin has four neighbors.
    FN
    list the Voronoi vertices for each Voronoi region. The first line is the number of Voronoi regions. Each remaining line starts with the number of Voronoi vertices. Negative indices (e.g., -1) indicate vertices outside of the Voronoi diagram. In the circle-in-box example, the four bounded regions are defined by four Voronoi vertices.
     
     
    Voronoi regions
    o
    print the Voronoi regions in OFF format. The first line is the dimension. The second line is the number of vertices, the number of input sites, and "1". The third line represents the vertex-at-infinity. Its coordinates are "-10.101". The next lines are the coordinates of the Voronoi vertices. Each remaining line starts with the number of Voronoi vertices in a Voronoi region. In 2-d, the vertices are listed in adjacency order (unoriented). In 3-d and higher, the vertices are listed in numeric order. In the circle-in-square example, each bounded region includes the Voronoi vertex at the origin. Lines consisting of 0 indicate coplanar input sites or 'Qz'.
    Fi
    print separating hyperplanes for inner, bounded Voronoi regions. The first number is the number of separating hyperplanes. Each remaining line starts with 3+dim. The next two numbers are adjacent input sites. The next dim numbers are the coefficients of the separating hyperplane. The last number is its offset. Use 'Tv' to verify that the hyperplanes are perpendicular bisectors. It will list relevant statistics to stderr.
    Fo
    print separating hyperplanes for outer, unbounded Voronoi regions. The first number is the number of separating hyperplanes. Each remaining line starts with 3+dim. The next two numbers are adjacent input sites on the convex hull. The next dim numbers are the coefficients of the separating hyperplane. The last number is its offset. Use 'Tv' to verify that the hyperplanes are perpendicular bisectors. It will list relevant statistics to stderr,
     
     
    Input sites
    Fv
    list ridges of Voronoi vertices for pairs of input sites. The first line is the number of ridges. Each remaining line starts with two plus the number of Voronoi vertices in the ridge. The next two numbers are two adjacent input sites. The remaining numbers list the Voronoi vertices. As with option 'o', a 0 indicates the vertex-at-infinity and an unbounded, separating hyperplane. The perpendicular bisector (separating hyperplane) of the input sites is a flat through these vertices. In the circle-in-square example, the ridge for each edge of the square is unbounded.
    Fc
    list coincident input sites for each Voronoi vertex. The first line is the number of vertices. The remaining lines start with the number of coincident sites and deleted vertices. Deleted vertices indicate highly degenerate input (see'Fs'). A coincident site is assigned to one Voronoi vertex. Do not use 'QJ' with 'Fc'; the joggle will separate coincident sites.
    FP
    print coincident input sites with distance to nearest site (i.e., vertex). The first line is the number of coincident sites. Each remaining line starts with the point ID of an input site, followed by the point ID of a coincident point, its vertex, and distance. Includes deleted vertices which indicate highly degenerate input (see'Fs'). Do not use 'QJ' with 'FP'; the joggle will separate coincident sites.
     
     
    General
    s
    print summary of the Voronoi diagram. Use 'Fs' for numeric data.
    i
    list input sites for each Delaunay region. Use option 'Pp' to avoid the warning. The first line is the number of regions. The remaining lines list the input sites for each region. The regions are oriented. In the circle-in-square example, the cocircular region has four edges. In 3-d and higher, report cospherical sites by adding extra points.
    G
    Geomview output for 2-d Voronoi diagrams.

    » qvoronoi controls

    These options provide additional control:

    Qu
    compute the furthest-site Voronoi diagram.
    QVn
    select Voronoi vertices for input site n
    Qz
    add a point above the paraboloid to reduce precision errors. Use it for nearly cocircular/cospherical input (e.g., 'rbox c | qvoronoi Qz').
    Tv
    verify result
    TI file
    input data from file. The filename may not use spaces or quotes.
    TO file
    output results to file. Use single quotes if the filename contains spaces (e.g., TO 'file with spaces.txt'
    TFn
    report progress after constructing n facets
    PDk:1
    include upper and lower facets in the output. Set k to the last dimension (e.g., 'PD2:1' for 2-d inputs).
    f
    facet dump. Print the data structure for each facet (i.e., Voronoi vertex).

    » qvoronoi graphics

    In 2-d, Geomview output ('G') displays a Voronoi diagram with extra edges to close the unbounded Voronoi regions. To view the unbounded rays, enclose the input points in a square.

    You can also view individual Voronoi regions in 3-d. To view the Voronoi region for site 3 in Geomview, execute

    qvoronoi <data QV3 p | qconvex s G >output

    The qvoronoi command returns the Voronoi vertices for input site 3. The qconvex command computes their convex hull. This is the Voronoi region for input site 3. Its hyperplane normals (qconvex 'n') are the same as the separating hyperplanes from options 'Fi' and 'Fo' (up to roundoff error).

    See the Delaunay and Voronoi examples for 2-d and 3-d examples. Turn off normalization (on Geomview's 'obscure' menu) when comparing the Voronoi diagram with the corresponding Delaunay triangulation.

    »qvoronoi notes

    You can simplify the Voronoi diagram by enclosing the input sites in a large square or cube. This is particularly recommended for cocircular or cospherical input data.

    See Voronoi graphics for computing the convex hull of a Voronoi region.

    Voronoi diagrams do not include facets that are coplanar with the convex hull of the input sites. A facet is coplanar if the last coefficient of its normal is nearly zero (see qh_ZEROdelaunay).

    Unbounded regions can be confusing. For example, 'rbox c | qvoronoi Qz o' produces the Voronoi regions for the vertices of a cube centered at the origin. All regions are unbounded. The output is

    3
    2 9 1
    -10.101 -10.101 -10.101
         0      0      0
    2 0 1
    2 0 1
    2 0 1
    2 0 1
    2 0 1
    2 0 1
    2 0 1
    2 0 1
    0
    

    The first line is the dimension. The second line is the number of vertices and the number of regions. There is one region per input point plus a region for the point-at-infinity added by option 'Qz'. The next two lines lists the Voronoi vertices. The first vertex is the infinity vertex. It is indicate by the coordinates -10.101. The second vertex is the origin. The next nine lines list the regions. Each region lists two vertices -- the infinity vertex and the origin. The last line is "0" because no region is associated with the point-at-infinity. A "0" would also be listed for nearly incident input sites.

    To use option 'Fv', add an interior point. For example,

    rbox c P0 | qvoronoi Fv
    20
    5 0 7 1 3 5
    5 0 3 1 4 5
    5 0 5 1 2 3
    5 0 1 1 2 4
    5 0 6 2 3 6
    5 0 2 2 4 6
    5 0 4 4 5 6
    5 0 8 5 3 6
    5 1 2 0 2 4
    5 1 3 0 1 4
    5 1 5 0 1 2
    5 2 4 0 4 6
    5 2 6 0 2 6
    5 3 4 0 4 5
    5 3 7 0 1 5
    5 4 8 0 6 5
    5 5 6 0 2 3
    5 5 7 0 1 3
    5 6 8 0 6 3
    5 7 8 0 3 5
    

    The output consists of 20 ridges and each ridge lists a pair of input sites and a triplet of Voronoi vertices. The first eight ridges connect the origin ('P0'). The remainder list the edges of the cube. Each edge generates an unbounded ray through the midpoint. The corresponding separating planes ('Fo') follow each pair of coordinate axes.

    Options 'Qt' (triangulated output) and 'QJ' (joggled input) are deprecated. They may produce unexpected results. If you use these options, cocircular and cospherical input sites will produce duplicate or nearly duplicate Voronoi vertices. See also Merged facets or joggled input.

    »qvoronoi conventions

    The following terminology is used for Voronoi diagrams in Qhull. The underlying structure is a Delaunay triangulation from a convex hull in one higher dimension. Facets of the Delaunay triangulation correspond to vertices of the Voronoi diagram. Vertices of the Delaunay triangulation correspond to input sites. They also correspond to regions of the Voronoi diagram. See convex hull conventions, Delaunay conventions, and Qhull's data structures.

    • input site - a point in the input (one dimension lower than a point on the convex hull)
    • point - a point has d+1 coordinates. The last coordinate is the sum of the squares of the input site's coordinates
    • coplanar point - a nearly incident input site
    • vertex - a point on the paraboloid. It corresponds to a unique input site.
    • point-at-infinity - a point added above the paraboloid by option 'Qz'
    • Delaunay facet - a lower facet of the paraboloid. The last coefficient of its normal is clearly negative.
    • Voronoi vertex - the circumcenter of a Delaunay facet
    • Voronoi region - the Voronoi vertices for an input site. The region of Euclidean space nearest to an input site.
    • Voronoi diagram - the graph of the Voronoi regions. It includes the ridges (i.e., edges) between the regions.
    • vertex-at-infinity - the Voronoi vertex that indicates unbounded Voronoi regions in 'o' output format. Its coordinates are -10.101.
    • good facet - a Voronoi vertex with optional restrictions by 'QVn', etc.

    »qvoronoi options

    qvoronoi- compute the Voronoi diagram
        http://www.qhull.org
    
    input (stdin):
        first lines: dimension and number of points (or vice-versa).
        other lines: point coordinates, best if one point per line
        comments:    start with a non-numeric character
    
    options:
        Qu   - compute furthest-site Voronoi diagram
    
    Qhull control options:
        QJn  - randomly joggle input in range [-n,n]
        Qs   - search all points for the initial simplex
        Qz   - add point-at-infinity to Voronoi diagram
        QGn  - Voronoi vertices if visible from point n, -n if not
        QVn  - Voronoi vertices for input point n, -n if not
    
    Trace options:
        T4   - trace at level n, 4=all, 5=mem/gauss, -1= events
        Tc   - check frequently during execution
        Ts   - statistics
        Tv   - verify result: structure, convexity, and in-circle test
        Tz   - send all output to stdout
        TFn  - report summary when n or more facets created
        TI file - input data from file, no spaces or single quotes
        TO file - output results to file, may be enclosed in single quotes
        TPn  - turn on tracing when point n added to hull
         TMn - turn on tracing at merge n
         TWn - trace merge facets when width > n
        TVn  - stop qhull after adding point n, -n for before (see TCn)
         TCn - stop qhull after building cone for point n (see TVn)
    
    Precision options:
        Cn   - radius of centrum (roundoff added).  Merge facets if non-convex
         An  - cosine of maximum angle.  Merge facets if cosine > n or non-convex
               C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge
        Rn   - randomly perturb computations by a factor of [1-n,1+n]
        Wn   - min facet width for non-coincident point (before roundoff)
    
    Output formats (may be combined; if none, produces a summary to stdout):
        s    - summary to stderr
        p    - Voronoi vertices
        o    - OFF format (dim, Voronoi vertices, and Voronoi regions)
        i    - Delaunay regions (use 'Pp' to avoid warning)
        f    - facet dump
    
    More formats:
        Fc   - count plus coincident points (by Voronoi vertex)
        Fd   - use cdd format for input (homogeneous with offset first)
        FD   - use cdd format for output (offset first)
        FF   - facet dump without ridges
        Fi   - separating hyperplanes for bounded Voronoi regions
        FI   - ID for each Voronoi vertex
        Fm   - merge count for each Voronoi vertex (511 max)
        Fn   - count plus neighboring Voronoi vertices for each Voronoi vertex
        FN   - count and Voronoi vertices for each Voronoi region
        Fo   - separating hyperplanes for unbounded Voronoi regions
        FO   - options and precision constants
        FP   - nearest point and distance for each coincident point
        FQ   - command used for qvoronoi
        Fs   - summary: #int (8), dimension, #points, tot vertices, tot facets,
                        for output: #Voronoi regions, #Voronoi vertices,
                                    #coincident points, #non-simplicial regions
                        #real (2), max outer plane and min vertex
        Fv   - Voronoi diagram as Voronoi vertices between adjacent input sites
        Fx   - extreme points of Delaunay triangulation (on convex hull)
    
    Geomview options (2-d only)
        Ga   - all points as dots
         Gp  -  coplanar points and vertices as radii
         Gv  -  vertices as spheres
        Gi   - inner planes only
         Gn  -  no planes
         Go  -  outer planes only
        Gc   - centrums
        Gh   - hyperplane intersections
        Gr   - ridges
        GDn  - drop dimension n in 3-d and 4-d output
    
    Print options:
        PAn  - keep n largest Voronoi vertices by 'area'
        Pdk:n - drop facet if normal[k] <= n (default 0.0)
        PDk:n - drop facet if normal[k] >= n
        Pg   - print good Voronoi vertices (needs 'QGn' or 'QVn')
        PFn  - keep Voronoi vertices whose 'area' is at least n
        PG   - print neighbors of good Voronoi vertices
        PMn  - keep n Voronoi vertices with most merges
        Po   - force output.  If error, output neighborhood of facet
        Pp   - do not report precision problems
    
        .    - list of all options
        -    - one line descriptions of all options
    

    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
    To: synopsis • input • outputs • controls • graphics • notes • conventions • options


    The Geometry Center Home Page

    Comments to: qhull@qhull.org
    Created: Sept. 25, 1995 --- Last modified: see top

    geometry/vignettes/qhull/html/qh--dt.gif0000644000176200001440000000727413431000556017776 0ustar liggesusersGIF87add^+!6o$08%BL&7Go$Z5 #4hWIC"H4i9;QW5 Fw;<5!aU2qIc<%b<$WWLNM7YXo].XRPZ6pD_J*_:$ SwHJ [_>F=D-QQEwR<::<:AHbYN0G<@AP&FvW73TN2V1#C&IHHR5U )5x O-WW?!DY g&!2-+BuP('z="Nh[L!O &H]I[J+(pBO&AO%,<H)F'PL:MN,(EA@'zNVNT&9CG*FA--.IJ89CuDM1 pIKeLMmS0RUSXXb=$WXjOAz;McuUCn@v!NhCTTL5(_2;55;;<<==??A|A==@|@AzABxBDtD$$GnGHlHIjIN`NU1O^OP\PQZQRXRSVSTTTD$*PYF_> 7_> EQBL69O`FeIT!U[\: f1 V>EyJN+'Q3!/,R1M4&H*b?AO&n2!H*[8"I9.W:Y"F%R-P%!\Vd?$MM!]EYK5f<.G09;?#3@%FoHJgL R0u@UUb?%c=&\CVV^f,ddk H*\ȰÇ#JHŋ3jȱǏ CIɓ(S܈ʗ U%J%̛RAD"j}|iώ H&*tp$KJ'+D%ML6TċV) 99ilO6R2A+1ڮCu+e*Xb bp@ώ#;fS ښ*7Ĉ% 9ۍ&)Z q)L%ңk+G.z]ܲ .b&TCZdܶ?0uNuaXy-_NJ.cD3Mc`,-igkDЦNڵs8_(hۄ4z|bFwѨU5t934 ;lr]:!.( 1 8MdpdF{vcT6@Z\&tlaX&XUd&_PS `pAYxb61ġd8Y É! |pACN4`wh$.۸Î*Z8H 'L7(Xs"> $9DN]9 H G0 L:;HJ $w* dbJ/[2." (𒄯 ^up DcB4Ѷ|:j;/`(14ȯc0FR1ͧOkkx+ANI< 24w`BlB2QoŽ쬠LS rdEǬ JЈL65tG'zX"q]6FsT("P+r'&C+=ZFNp˛1` (3P)d ̠C$ A _څ߆CM* R(hRC % =k OJ#hR-L|N@ DhE-t^+A8jpbl ΰ M< h2Gb0j^@@0E5y3@&` ЀthV@7 X@nR8`E0{@ t @F<` DRQQ8"!q#B6;2 4Ad'$`sPj1R3Gr"ɍR =በ e(I g $PD$QN Х@pi qk&( 'Lw0b0'° CL! a1.H*b!N G,4ƶD8`wL25Q2xM@!! O,`eh1P,"YZAtu- jh%c0"v0<@/!ȠH1X$ϻ)0'l<$`Ma0>Tm,8*&X`@H!X؏5)Y_шlp@" )@r=K a"iZF h,٨f l ]XC8@x '%paH 6` D'&+|@*I -p%$xM'1)$ ڑT^n66<6oxB](#h9 BS_.8U0dAwP %PT E#[i!zXᐍ#smP0(-*Eg@ zq0@|D2Cbj0dHP5/h Pv(dp~Fև*A <#]0S -CU.  >}0xj~ )4 j #l_ŀ IaDC 7CȀe>pz 0U-Ph _$ *WPXp1<X 0t$0]h#S  8r|(/P*<2l 0a V Є >/PQXAЅP@!1k(N~7Qgp 5P 'A 7|#p1hC 4Y5P | /@e!  iseq8|c P v<8yp KY 5 a 5UW9A|0c061F ȀK#@t):rٙ;geometry/vignettes/qhull/html/qdelau_f.html0000644000176200001440000004357513431000557020670 0ustar liggesusers qdelaunay Qu -- furthest-site Delaunay triangulation Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
    To: synopsis • input • outputs • controls • graphics • notes • conventions • options

    [delaunay]qdelaunay Qu -- furthest-site Delaunay triangulation

    The furthest-site Delaunay triangulation corresponds to the upper facets of the Delaunay construction. Its vertices are the extreme points of the input sites. It is the dual of the furthest-site Voronoi diagram.

    Example: rbox 10 D2 | qdelaunay Qu Qt s i TO result
    Compute the 2-d, furthest-site Delaunay triangulation of 10 random points. Triangulate the output. Write a summary to the console and the regions to 'result'.
     
    Example: rbox 10 D2 | qdelaunay Qu QJ s i TO result
    Compute the 2-d, furthest-site Delaunay triangulation of 10 random points. Joggle the input to guarantee triangular output. Write a summary to the console and the regions to 'result'.
     
    Example: rbox r y c G1 D2 | qdelaunay Qu s Fv TO result
    Compute the 2-d, furthest-site Delaunay triangulation of a triangle inside a square. Write a summary to the console and unoriented regions to 'result'. Merge regions for cocircular input sites (e.g., the square). The square is the only furthest-site Delaunay region.

    As with the Delaunay triangulation, Qhull computes the furthest-site Delaunay triangulation by lifting the input sites to a paraboloid. The lower facets correspond to the Delaunay triangulation while the upper facets correspond to the furthest-site triangulation. Neither triangulation includes "vertical" facets (i.e., facets whose last hyperplane coefficient is nearly zero). Vertical facets correspond to input sites that are coplanar to the convex hull of the input. An example is points on the boundary of a lattice.

    By default, qdelaunay merges cocircular and cospherical regions. For example, the furthest-site Delaunay triangulation of a square inside a diamond ('rbox D2 c d G4 | qdelaunay Qu') consists of one region (the diamond).

    If you use 'Qt' (triangulated output), all furthest-site Delaunay regions will be simplicial (e.g., triangles in 2-d). Some regions may be degenerate and have zero area.

    If you use 'QJ' (joggled input), all furthest-site Delaunay regions will be simplicial (e.g., triangles in 2-d). Joggled input is less accurate than triangulated output ('Qt'). See Merged facets or joggled input.

    The output for 3-d, furthest-site Delaunay triangulations may be confusing if the input contains cospherical data. See the FAQ item Why are there extra points in a 4-d or higher convex hull? Avoid these problems with triangulated output ('Qt') or joggled input ('QJ').

    The 'qdelaunay' program is equivalent to 'qhull d Qbb' in 2-d to 3-d, and 'qhull d Qbb Qx' in 4-d and higher. It disables the following Qhull options: d n v H U Qb QB Qc Qf Qg Qi Qm Qr QR Qv Qx TR E V FC Fi Fo Fp FV Q0,etc.

    Copyright © 1995-2018 C.B. Barber


    »furthest-site qdelaunay synopsis

    See qdelaunay synopsis. The same program is used for both constructions. Use option 'Qu' for furthest-site Delaunay triangulations.

    »furthest-site qdelaunay input

    The input data on stdin consists of:

    • dimension
    • number of points
    • point coordinates

    Use I/O redirection (e.g., qdelaunay Qu < data.txt), a pipe (e.g., rbox 10 | qdelaunay Qu), or the 'TI' option (e.g., qdelaunay Qu TI data.txt).

    For example, this is a square containing four random points. Its furthest-site Delaunay triangulation contains one square.

    rbox c 4 D2 > data
    2 RBOX c 4 D2
    8
    -0.4999921736307369 -0.3684622117955817
    0.2556053225468894 -0.0413498678629751
    0.0327672376602583 -0.2810408135699488
    -0.452955383763607 0.17886471718444
      -0.5   -0.5
      -0.5    0.5
       0.5   -0.5
       0.5    0.5
    

    qdelaunay Qu i < data

    
    Furthest-site Delaunay triangulation by the convex hull of 8 points in 3-d:
    
      Number of input sites: 8
      Number of Delaunay regions: 1
      Number of non-simplicial Delaunay regions: 1
    
    Statistics for: RBOX c 4 D2 | QDELAUNAY s Qu i
    
      Number of points processed: 8
      Number of hyperplanes created: 20
      Number of facets in hull: 11
      Number of distance tests for qhull: 34
      Number of merged facets: 1
      Number of distance tests for merging: 107
      CPU seconds to compute hull (after input): 0.02
    
    1
    7 6 4 5
    

    »furthest-site qdelaunay outputs

    These options control the output of furthest-site Delaunay triangulations:

    furthest-site Delaunay regions
    i
    list input sites for each furthest-site Delaunay region. The first line is the number of regions. The remaining lines list the input sites for each region. The regions are oriented. In 3-d and higher, report cospherical sites by adding extra points. For the points-in-square example, the square is the only furthest-site Delaunay region.
    Fv
    list input sites for each furthest-site Delaunay region. The first line is the number of regions. Each remaining line starts with the number of input sites. The regions are unoriented. For the points-in-square example, the square is the only furthest-site Delaunay region.
    Ft
    print a triangulation of the furthest-site Delaunay regions in OFF format. The first line is the dimension. The second line is the number of input sites and added points, followed by the number of simplices and the number of ridges. The input coordinates are next, followed by the centrum coordinates. There is one centrum for each non-simplicial furthest-site Delaunay region. Each remaining line starts with dimension+1. The simplices are oriented. For the points-in-square example, the square has a centrum at the origin. It splits the square into four triangular regions.
    Fn
    list neighboring regions for each furthest-site Delaunay region. The first line is the number of regions. Each remaining line starts with the number of neighboring regions. Negative indices (e.g., -1) indicate regions outside of the furthest-site Delaunay triangulation. For the points-in-square example, the four neighboring regions are outside of the triangulation. They belong to the regular Delaunay triangulation.
    FN
    list the furthest-site Delaunay regions for each input site. The first line is the total number of input sites. Each remaining line starts with the number of furthest-site Delaunay regions. Negative indices (e.g., -1) indicate regions outside of the furthest-site Delaunay triangulation. For the points-in-square example, the four random points belong to no region while the square's vertices belong to region 0 and three regions outside of the furthest-site Delaunay triangulation.
    Fa
    print area for each furthest-site Delaunay region. The first line is the number of regions. The areas follow, one line per region. For the points-in-square example, the square has unit area.
     
     
    Input sites
    Fx
    list extreme points of the input sites. These points are vertices of the furthest-point Delaunay triangulation. They are on the boundary of the convex hull. The first line is the number of extreme points. Each point is listed, one per line. The points-in-square example has four extreme points.
     
     
    General
    FA
    compute total area for 's' and 'FS'. This is the same as the area of the convex hull.
    o
    print upper facets of the corresponding convex hull (a paraboloid)
    m
    Mathematica output for the upper facets of the paraboloid (2-d triangulations).
    FM
    Maple output for the upper facets of the paraboloid (2-d triangulations).
    G
    Geomview output for the paraboloid (2-d or 3-d triangulations).
    s
    print summary for the furthest-site Delaunay triangulation. Use 'Fs' and 'FS' for numeric data.

    »furthest-site qdelaunay controls

    These options provide additional control:

    Qu
    must be used for furthest-site Delaunay triangulation.
    Qt
    triangulated output. Qhull triangulates non-simplicial facets. It may produce degenerate facets of zero area.
    QJ
    joggle the input to avoid cospherical and coincident sites. It is less accurate than triangulated output ('Qt').
    QVn
    select facets adjacent to input site n (marked 'good').
    Tv
    verify result.
    TI file
    input data from file. The filename may not use spaces or quotes.
    TO file
    output results to file. Use single quotes if the filename contains spaces (e.g., TO 'file with spaces.txt'
    TFn
    report progress after constructing n facets
    PDk:1
    include upper and lower facets in the output. Set k to the last dimension (e.g., 'PD2:1' for 2-d inputs).
    f
    facet dump. Print the data structure for each facet (i.e., furthest-site Delaunay region).

    »furthest-site qdelaunay graphics

    See Delaunay graphics. They are the same except for Mathematica and Maple output.

    »furthest-site qdelaunay notes

    The furthest-site Delaunay triangulation does not record coincident input sites. Use qdelaunay instead.

    qdelaunay Qu does not work for purely cocircular or cospherical points (e.g., rbox c | qdelaunay Qu). Instead, use qdelaunay Qz -- when all points are vertices of the convex hull of the input sites, the Delaunay triangulation is the same as the furthest-site Delaunay triangulation.

    A non-simplicial, furthest-site Delaunay region indicates nearly cocircular or cospherical input sites. To avoid non-simplicial regions triangulate the output ('Qt') or joggle the input ('QJ'). Joggled input is less accurate than triangulated output. You may also triangulate non-simplicial regions with option 'Ft'. It adds the centrum to non-simplicial regions. Alternatively, use an exact arithmetic code.

    Furthest-site Delaunay triangulations do not include facets that are coplanar with the convex hull of the input sites. A facet is coplanar if the last coefficient of its normal is nearly zero (see qh_ZEROdelaunay).

    »furthest-site qdelaunay conventions

    The following terminology is used for furthest-site Delaunay triangulations in Qhull. The underlying structure is the upper facets of a convex hull in one higher dimension. See convex hull conventions, Delaunay conventions, and Qhull's data structures

    • input site - a point in the input (one dimension lower than a point on the convex hull)
    • point - d+1 coordinates. The last coordinate is the sum of the squares of the input site's coordinates
    • vertex - a point on the paraboloid. It corresponds to a unique input site.
    • furthest-site Delaunay facet - an upper facet of the paraboloid. The last coefficient of its normal is clearly positive.
    • furthest-site Delaunay region - a furthest-site Delaunay facet projected to the input sites
    • non-simplicial facet - more than d points are cocircular or cospherical
    • good facet - a furthest-site Delaunay facet with optional restrictions by 'QVn', etc.

    »furthest-site qdelaunay options

    See qdelaunay options. The same program is used for both constructions. Use option 'Qu' for furthest-site Delaunay triangulations.

    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
    To: synopsis • input • outputs • controls • graphics • notes • conventions • options


    The Geometry Center Home Page

    Comments to: qhull@qhull.org
    Created: Sept. 25, 1995 --- Last modified: see top

    geometry/vignettes/qhull/html/qvoron_f.html0000644000176200001440000004201713431000557020727 0ustar liggesusers qvoronoi Qu -- furthest-site Voronoi diagram Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
    To: synopsis • input • outputs • controls • graphics • notes • conventions • options

    [delaunay]qvoronoi Qu -- furthest-site Voronoi diagram

    The furthest-site Voronoi diagram is the furthest-neighbor map for a set of points. Each region contains those points that are further from one input site than any other input site. See the survey article by Aurenhammer ['91] and the brief introduction by O'Rourke ['94]. The furthest-site Voronoi diagram is the dual of the furthest-site Delaunay triangulation.

    Example: rbox 10 D2 | qvoronoi Qu s o TO result
    Compute the 2-d, furthest-site Voronoi diagram of 10 random points. Write a summary to the console and the Voronoi regions and vertices to 'result'. The first vertex of the result indicates unbounded regions. Almost all regions are unbounded.
    Example: rbox r y c G1 D2 | qvoronoi Qu s Fn TO result
    Compute the 2-d furthest-site Voronoi diagram of a square and a small triangle. Write a summary to the console and the Voronoi vertices for each input site to 'result'. The origin is the only furthest-site Voronoi vertex. The negative indices indicate vertices-at-infinity.

    Qhull computes the furthest-site Voronoi diagram via the furthest-site Delaunay triangulation. Each furthest-site Voronoi vertex is the circumcenter of an upper facet of the Delaunay triangulation. Each furthest-site Voronoi region corresponds to a vertex of the Delaunay triangulation (i.e., an input site).

    See Qhull FAQ - Delaunay and Voronoi diagram questions.

    The 'qvonoroi' program is equivalent to 'qhull v Qbb' in 2-d to 3-d, and 'qhull v Qbb Qx' in 4-d and higher. It disables the following Qhull options: d n m v H U Qb QB Qc Qf Qg Qi Qm Qr QR Qv Qx TR E V Fa FA FC Fp FS Ft FV Gt Q0,etc.

    Copyright © 1995-2018 C.B. Barber


    »furthest-site qvoronoi synopsis

    See qvoronoi synopsis. The same program is used for both constructions. Use option 'Qu' for furthest-site Voronoi diagrams.

    »furthest-site qvoronoi input

    The input data on stdin consists of:

    • dimension
    • number of points
    • point coordinates

    Use I/O redirection (e.g., qvoronoi Qu < data.txt), a pipe (e.g., rbox 10 | qvoronoi Qu), or the 'TI' option (e.g., qvoronoi TI data.txt Qu).

    For example, this is a square containing four random points. Its furthest-site Voronoi diagram has on vertex and four unbounded, separating hyperplanes (i.e., the coordinate axes)

    rbox c 4 D2 > data
    2 RBOX c 4 D2
    8
    -0.4999921736307369 -0.3684622117955817
    0.2556053225468894 -0.0413498678629751
    0.0327672376602583 -0.2810408135699488
    -0.452955383763607 0.17886471718444
      -0.5   -0.5
      -0.5    0.5
       0.5   -0.5
       0.5    0.5
    

    qvoronoi Qu s Fo < data

    
    Furthest-site Voronoi vertices by the convex hull of 8 points in 3-d:
    
      Number of Voronoi regions: 8
      Number of Voronoi vertices: 1
      Number of non-simplicial Voronoi vertices: 1
    
    Statistics for: RBOX c 4 D2 | QVORONOI Qu s Fo
    
      Number of points processed: 8
      Number of hyperplanes created: 20
      Number of facets in hull: 11
      Number of distance tests for qhull: 34
      Number of merged facets: 1
      Number of distance tests for merging: 107
      CPU seconds to compute hull (after input):  0
    
    4
    5 4 5      0      1      0
    5 4 6      1      0      0
    5 5 7      1      0      0
    5 6 7      0      1      0
    

    » furthest-site qvoronoi outputs

    These options control the output of furthest-site Voronoi diagrams.

     
    furthest-site Voronoi vertices
    p
    print the coordinates of the furthest-site Voronoi vertices. The first line is the dimension. The second line is the number of vertices. Each remaining line is a furthest-site Voronoi vertex. The points-in-square example has one furthest-site Voronoi vertex at the origin.
    Fn
    list the neighboring furthest-site Voronoi vertices for each furthest-site Voronoi vertex. The first line is the number of Voronoi vertices. Each remaining line starts with the number of neighboring vertices. Negative indices (e.g., -1) indicate vertices outside of the Voronoi diagram. In the points-in-square example, the Voronoi vertex at the origin has four neighbors-at-infinity.
    FN
    list the furthest-site Voronoi vertices for each furthest-site Voronoi region. The first line is the number of Voronoi regions. Each remaining line starts with the number of Voronoi vertices. Negative indices (e.g., -1) indicate vertices outside of the Voronoi diagram. In the points-in-square example, all regions share the Voronoi vertex at the origin.
     
     
    furthest-site Voronoi regions
    o
    print the furthest-site Voronoi regions in OFF format. The first line is the dimension. The second line is the number of vertices, the number of input sites, and "1". The third line represents the vertex-at-infinity. Its coordinates are "-10.101". The next lines are the coordinates of the furthest-site Voronoi vertices. Each remaining line starts with the number of Voronoi vertices in a Voronoi region. In 2-d, the vertices are listed in adjacency order (unoriented). In 3-d and higher, the vertices are listed in numeric order. In the points-in-square example, each unbounded region includes the Voronoi vertex at the origin. Lines consisting of 0 indicate interior input sites.
    Fi
    print separating hyperplanes for inner, bounded furthest-site Voronoi regions. The first number is the number of separating hyperplanes. Each remaining line starts with 3+dim. The next two numbers are adjacent input sites. The next dim numbers are the coefficients of the separating hyperplane. The last number is its offset. The are no bounded, separating hyperplanes for the points-in-square example.
    Fo
    print separating hyperplanes for outer, unbounded furthest-site Voronoi regions. The first number is the number of separating hyperplanes. Each remaining line starts with 3+dim. The next two numbers are adjacent input sites on the convex hull. The next dim numbers are the coefficients of the separating hyperplane. The last number is its offset. The points-in-square example has four unbounded, separating hyperplanes.
     
     
    Input sites
    Fv
    list ridges of furthest-site Voronoi vertices for pairs of input sites. The first line is the number of ridges. Each remaining line starts with two plus the number of Voronoi vertices in the ridge. The next two numbers are two adjacent input sites. The remaining numbers list the Voronoi vertices. As with option 'o', a 0 indicates the vertex-at-infinity and an unbounded, separating hyperplane. The perpendicular bisector (separating hyperplane) of the input sites is a flat through these vertices. In the points-in-square example, the ridge for each edge of the square is unbounded.
     
     
    General
    s
    print summary of the furthest-site Voronoi diagram. Use 'Fs' for numeric data.
    i
    list input sites for each furthest-site Delaunay region. Use option 'Pp' to avoid the warning. The first line is the number of regions. The remaining lines list the input sites for each region. The regions are oriented. In the points-in-square example, the square region has four input sites. In 3-d and higher, report cospherical sites by adding extra points.
    G
    Geomview output for 2-d furthest-site Voronoi diagrams.

    » furthest-site qvoronoi controls

    These options provide additional control:

    Qu
    must be used.
    QVn
    select furthest-site Voronoi vertices for input site n
    Tv
    verify result
    TI file
    input data from file. The filename may not use spaces or quotes.
    TO file
    output results to file. Use single quotes if the filename contains spaces (e.g., TO 'file with spaces.txt'
    TFn
    report progress after constructing n facets
    PDk:1
    include upper and lower facets in the output. Set k to the last dimension (e.g., 'PD2:1' for 2-d inputs).
    f
    facet dump. Print the data structure for each facet (i.e., furthest-site Voronoi vertex).

    » furthest-site qvoronoi graphics

    In 2-d, Geomview output ('G') displays a furthest-site Voronoi diagram with extra edges to close the unbounded furthest-site Voronoi regions. All regions will be unbounded. Since the points-in-box example has only one furthest-site Voronoi vertex, the Geomview output is one point.

    See the Delaunay and Voronoi examples for a 2-d example. Turn off normalization (on Geomview's 'obscure' menu) when comparing the furthest-site Voronoi diagram with the corresponding Voronoi diagram.

    »furthest-site qvoronoi notes

    See Voronoi notes.

    »furthest-site qvoronoi conventions

    The following terminology is used for furthest-site Voronoi diagrams in Qhull. The underlying structure is a furthest-site Delaunay triangulation from a convex hull in one higher dimension. Upper facets of the Delaunay triangulation correspond to vertices of the furthest-site Voronoi diagram. Vertices of the furthest-site Delaunay triangulation correspond to input sites. They also define regions of the furthest-site Voronoi diagram. All vertices are extreme points of the input sites. See qconvex conventions, furthest-site delaunay conventions, and Qhull's data structures.

    • input site - a point in the input (one dimension lower than a point on the convex hull)
    • point - a point has d+1 coordinates. The last coordinate is the sum of the squares of the input site's coordinates
    • vertex - a point on the upper facets of the paraboloid. It corresponds to a unique input site.
    • furthest-site Delaunay facet - an upper facet of the paraboloid. The last coefficient of its normal is clearly positive.
    • furthest-site Voronoi vertex - the circumcenter of a furthest-site Delaunay facet
    • furthest-site Voronoi region - the region of Euclidean space further from an input site than any other input site. Qhull lists the furthest-site Voronoi vertices that define each furthest-site Voronoi region.
    • furthest-site Voronoi diagram - the graph of the furthest-site Voronoi regions with the ridges (edges) between the regions.
    • infinity vertex - the Voronoi vertex for unbounded furthest-site Voronoi regions in 'o' output format. Its coordinates are -10.101.
    • good facet - an furthest-site Voronoi vertex with optional restrictions by 'QVn', etc.

    »furthest-site qvoronoi options

    See qvoronoi options. The same program is used for both constructions. Use option 'Qu' for furthest-site Voronoi diagrams.

    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
    To: synopsis • input • outputs • controls • graphics • notes • conventions • options


    The Geometry Center Home Page

    Comments to: qhull@qhull.org
    Created: Sept. 25, 1995 --- Last modified: see top

    geometry/vignettes/qhull/html/qhull.txt0000644000176200001440000014070013431000557020074 0ustar liggesusers qhull(1) qhull(1) NAME qhull - convex hull, Delaunay triangulation, Voronoi dia- gram, halfspace intersection about a point, hull volume, facet area SYNOPSIS qhull- compute convex hulls and related structures input (stdin): dimension, #points, point coordinates first comment (non-numeric) is listed in the summary halfspace: use dim plus one with offsets after coefficients options (qh-quick.htm): d - Delaunay triangulation by lifting points to a paraboloid v - Voronoi diagram via the Delaunay triangulation H1,1 - Halfspace intersection about [1,1,0,...] d Qu - Furthest-site Delaunay triangulation (upper convex hull) v Qu - Furthest-site Voronoi diagram QJ - Joggle the input to avoid precision problems . - concise list of all options - - one-line description of all options Output options (subset): FA - compute total area and volume Fx - extreme points (convex hull vertices) G - Geomview output (2-d, 3-d and 4-d) Fp - halfspace intersection coordinates m - Mathematica output (2-d and 3-d) n - normals with offsets o - OFF file format (if Voronoi, outputs regions) TO file- output results to file, may be enclosed in single quotes f - print all fields of all facets s - summary of results (default) Tv - verify result: structure, convexity, and point inclusion p - vertex coordinates i - vertices incident to each facet example: rbox 1000 s | qhull Tv s FA - html manual: index.htm - installation: README.txt - see also: COPYING.txt, REGISTER.txt, Changes.txt - WWW: - GIT: - mirror: - news: - Geomview: - news group: - FAQ: - email: qhull@qhull.org - bug reports: qhull_bug@qhull.org Geometry Center 2003/12/30 1 qhull(1) qhull(1) The sections are: - INTRODUCTION - DESCRIPTION, a description of Qhull - IMPRECISION, how Qhull handles imprecision - OPTIONS - Input and output options - Additional input/output formats - Precision options - Geomview options - Print options - Qhull options - Trace options - BUGS - E-MAIL - SEE ALSO - AUTHORS - ACKNOWLEGEMENTS This man page briefly describes all Qhull options. Please report any mismatches with Qhull's html manual (qh- man.htm). INTRODUCTION Qhull is a general dimension code for computing convex hulls, Delaunay triangulations, Voronoi diagram, furthest- site Voronoi diagram, furthest-site Delaunay triangula- tions, and halfspace intersections about a point. It implements the Quickhull algorithm for computing the con- vex hull. Qhull handles round-off errors from floating point arithmetic. It can approximate a convex hull. The program includes options for hull volume, facet area, partial hulls, input transformations, randomization, trac- ing, multiple output formats, and execution statistics. The program can be called from within your application. You can view the results in 2-d, 3-d and 4-d with Geomview. DESCRIPTION The format of input is the following: first line contains the dimension, second line contains the number of input points, and point coordinates follow. The dimension and number of points can be reversed. Comments and line breaks are ignored. A comment starts with a non-numeric character and continues to the end of line. The first comment is reported in summaries and statistics. Error reporting is better if there is one point per line. The default printout option is a short summary. There are many other output formats. Geometry Center 2003/12/30 2 qhull(1) qhull(1) Qhull implements the Quickhull algorithm for convex hull. This algorithm combines the 2-d Quickhull algorithm with the n-d beneath-beyond algorithm [c.f., Preparata & Shamos '85]. It is similar to the randomized algorithms of Clarkson and others [Clarkson et al. '93]. The main advantages of Quickhull are output sensitive performance, reduced space requirements, and automatic handling of pre- cision problems. The data structure produced by Qhull consists of vertices, ridges, and facets. A vertex is a point of the input set. A ridge is a set of d vertices and two neighboring facets. For example in 3-d, a ridge is an edge of the polyhedron. A facet is a set of ridges, a set of neighboring facets, a set of incident vertices, and a hyperplane equation. For simplicial facets, the ridges are defined by the vertices and neighboring facets. When Qhull merges two facets, it produces a non-simplicial facet. A non-simplicial facet has more than d neighbors and may share more than one ridge with a neighbor. IMPRECISION Since Qhull uses floating point arithmetic, roundoff error may occur for each calculation. This causes problems for most geometric algorithms. Qhull automatically sets option 'C-0' in 2-d, 3-d, and 4-d, or option 'Qx' in 5-d and higher. These options han- dle precision problems by merging facets. Alternatively, use option 'QJ' to joggle the input. With 'C-0', Qhull merges non-convex facets while con- structing the hull. The remaining facets are clearly con- vex. With 'Qx', Qhull merges coplanar horizon facets, flipped facets, concave facets and duplicated ridges. It merges coplanar facets after constructing the hull. With 'Qx', coplanar points may be missed, but it appears to be unlikely. To guarantee triangular output, joggle the input with option 'QJ'. Facet merging will not occur. OPTIONS To get a list of the most important options, execute 'qhull' by itself. To get a complete list of options, execute 'qhull -'. To get a complete, concise list of options, execute 'qhull .'. Options can be in any order. Capitalized options take an argument (except 'PG' and 'F' options). Single letters are used for output formats and precision constants. The other options are grouped into menus for other output for- mats ('F'), Geomview output ('G'), printing ('P'), Qhull Geometry Center 2003/12/30 3 qhull(1) qhull(1) control ('Q'), and tracing ('T'). Main options: default Compute the convex hull of the input points. Report a summary of the result. d Compute the Delaunay triangulation by lifting the input points to a paraboloid. The 'o' option prints the input points and facets. The 'QJ' option guarantees triangular output. The 'Ft' option prints a triangulation. It adds points (the centrums) to non-simplicial facets. v Compute the Voronoi diagram from the Delaunay tri- angulation. The 'p' option prints the Voronoi ver- tices. The 'o' option prints the Voronoi vertices and the vertices in each Voronoi region. It lists regions in site id order. The 'Fv' option prints each ridge of the Voronoi diagram. The first or zero'th vertex indicates the infinity vertex. Its coordinates are qh_INFINITE (-10.101). It indi- cates unbounded Voronoi regions or degenerate Delaunay triangles. Hn,n,... Compute halfspace intersection about [n,n,0,...]. The input is a set of halfspaces defined in the same format as 'n', 'Fo', and 'Fi'. Use 'Fp' to print the intersection points. Use 'Fv' to list the intersection points for each halfspace. The other output formats display the dual convex hull. The point [n,n,n,...] is a feasible point for the halfspaces, i.e., a point that is inside all of the halfspaces (Hx+b <= 0). The default coordinate value is 0. The input may start with a feasible point. If so, use 'H' by itself. The input starts with a feasi- ble point when the first number is the dimension, the second number is "1", and the coordinates com- plete a line. The 'FV' option produces a feasible point for a convex hull. d Qu Compute the furthest-site Delaunay triangulation from the upper convex hull. The 'o' option prints the input points and facets. The 'QJ' option guar- antees triangular otuput. You can also use facets. v Qu Compute the furthest-site Voronoi diagram. The 'p' option prints the Voronoi vertices. The 'o' option prints the Voronoi vertices and the vertices in Geometry Center 2003/12/30 4 qhull(1) qhull(1) each Voronoi region. The 'Fv' option prints each ridge of the Voronoi diagram. The first or zero'th vertex indicates the infinity vertex at infinity. Its coordinates are qh_INFINITE (-10.101). It indicates unbounded Voronoi regions and degenerate Delaunay triangles. Qt Triangulated output. Input/Output options: f Print out all facets and all fields of each facet. G Output the hull in Geomview format. For imprecise hulls, Geomview displays the inner and outer hull. Geomview can also display points, ridges, vertices, coplanar points, and facet intersections. See below for a list of options. For Delaunay triangulations, 'G' displays the cor- responding paraboloid. For halfspace intersection, 'G' displays the dual polytope. i Output the incident vertices for each facet. Qhull prints the number of facets followed by the ver- tices of each facet. One facet is printed per line. The numbers are the 0-relative indices of the corresponding input points. The facets are oriented. In 4-d and higher, Qhull triangulates non-simpli- cial facets. Each apex (the first vertex) is a created point that corresponds to the facet's cen- trum. Its index is greater than the indices of the input points. Each base corresponds to a simpli- cial ridge between two facets. To print the ver- tices without triangulation, use option 'Fv'. m Output the hull in Mathematica format. Qhull writes a Mathematica file for 2-d and 3-d convex hulls and for 2-d Delaunay triangulations. Qhull produces a list of objects that you can assign to a variable in Mathematica, for example: "list= << ". If the object is 2-d, it can be visualized by "Show[Graphics[list]] ". For 3-d objects the command is "Show[Graphics3D[list]]". n Output the normal equation for each facet. Qhull prints the dimension (plus one), the number of facets, and the normals for each facet. The facet's offset follows its normal coefficients. o Output the facets in OFF file format. Qhull prints the dimension, number of points, number of facets, and number of ridges. Then it prints the Geometry Center 2003/12/30 5 qhull(1) qhull(1) coordinates of the input points and the vertices for each facet. Each facet is on a separate line. The first number is the number of vertices. The remainder are the indices of the corresponding points. The vertices are oriented in 2-d, 3-d, and in simplicial facets. For 2-d Voronoi diagrams, the vertices are sorted by adjacency, but not oriented. In 3-d and higher, the Voronoi vertices are sorted by index. See the 'v' option for more information. p Output the coordinates of each vertex point. Qhull prints the dimension, the number of points, and the coordinates for each vertex. With the 'Gc' and 'Gi' options, it also prints coplanar and interior points. For Voronoi diagrams, it prints the coor- dinates of each Voronoi vertex. s Print a summary to stderr. If no output options are specified at all, a summary goes to stdout. The summary lists the number of input points, the dimension, the number of vertices in the convex hull, the number of facets in the convex hull, the number of good facets (if 'Pg'), and statistics. The last two statistics (if needed) measure the maximum distance from a point or vertex to a facet. The number in parenthesis (e.g., 2.1x) is the ratio between the maximum distance and the worst-case distance due to merging two simplicial facets. Precision options An Maximum angle given as a cosine. If the angle between a pair of facet normals is greater than n, Qhull merges one of the facets into a neighbor. If 'n' is negative, Qhull tests angles after adding each point to the hull (pre-merging). If 'n' is posi- tive, Qhull tests angles after constructing the hull (post-merging). Both pre- and post-merging can be defined. Option 'C0' or 'C-0' is set if the corresponding 'Cn' or 'C-n' is not set. If 'Qx' is set, then 'A- n' and 'C-n' are checked after the hull is con- structed and before 'An' and 'Cn' are checked. Cn Centrum radius. If a centrum is less than n below a neighboring facet, Qhull merges one of the facets. If 'n' is negative or '-0', Qhull tests and merges facets after adding each point to the hull. This is called "pre-merging". If 'n' is Geometry Center 2003/12/30 6 qhull(1) qhull(1) positive, Qhull tests for convexity after con- structing the hull ("post-merging"). Both pre- and post-merging can be defined. For 5-d and higher, 'Qx' should be used instead of 'C-n'. Otherwise, most or all facets may be merged together. En Maximum roundoff error for distance computations. Rn Randomly perturb distance computations up to +/- n * max_coord. This option perturbs every distance, hyperplane, and angle computation. To use time as the random number seed, use option 'QR-1'. Vn Minimum distance for a facet to be visible. A facet is visible if the distance from the point to the facet is greater than 'Vn'. Without merging, the default value for 'Vn' is the round-off error ('En'). With merging, the default value is the pre-merge centrum ('C-n') in 2-d or 3--d, or three times that in other dimensions. If the outside width is specified ('Wn'), the maximum, default value for 'Vn' is 'Wn'. Un Maximum distance below a facet for a point to be coplanar to the facet. The default value is 'Vn'. Wn Minimum outside width of the hull. Points are added to the convex hull only if they are clearly outside of a facet. A point is outside of a facet if its distance to the facet is greater than 'Wn'. The normal value for 'Wn' is 'En'. If the user specifies pre-merging and does not set 'Wn', than 'Wn' is set to the premerge 'Cn' and maxco- ord*(1-An). Additional input/output formats Fa Print area for each facet. For Delaunay triangula- tions, the area is the area of the triangle. For Voronoi diagrams, the area is the area of the dual facet. Use 'PAn' for printing the n largest facets, and option 'PFn' for printing facets larger than 'n'. The area for non-simplicial facets is the sum of the areas for each ridge to the centrum. Vertices far below the facet's hyperplane are ignored. The reported area may be significantly less than the actual area. Geometry Center 2003/12/30 7 qhull(1) qhull(1) FA Compute the total area and volume for option 's'. It is an approximation for non-simplicial facets (see 'Fa'). Fc Print coplanar points for each facet. The output starts with the number of facets. Then each facet is printed one per line. Each line is the number of coplanar points followed by the point ids. Option 'Qi' includes the interior points. Each coplanar point (interior point) is assigned to the facet it is furthest above (resp., least below). FC Print centrums for each facet. The output starts with the dimension followed by the number of facets. Then each facet centrum is printed, one per line. Fd Read input in cdd format with homogeneous points. The input starts with comments. The first comment is reported in the summary. Data starts after a "begin" line. The next line is the number of points followed by the dimension+1 and "real" or "integer". Then the points are listed with a leading "1" or "1.0". The data ends with an "end" line. For halfspaces ('Fd Hn,n,...'), the input format is the same. Each halfspace starts with its offset. The sign of the offset is the opposite of Qhull's convention. FD Print normals ('n', 'Fo', 'Fi') or points ('p') in cdd format. The first line is the command line that invoked Qhull. Data starts with a "begin" line. The next line is the number of normals or points followed by the dimension+1 and "real". Then the normals or points are listed with the offset before the coefficients. The offset for points is 1.0. The offset for normals has the opposite sign. The data ends with an "end" line. FF Print facets (as in 'f') without printing the ridges. Fi Print inner planes for each facet. The inner plane is below all vertices. Fi Print separating hyperplanes for bounded, inner regions of the Voronoi diagram. The first line is the number of ridges. Then each hyperplane is printed, one per line. A line starts with the num- ber of indices and floats. The first pair lists adjacent input sites, the next d floats are the normalized coefficients for the hyperplane, and the Geometry Center 2003/12/30 8 qhull(1) qhull(1) last float is the offset. The hyperplane is ori- ented toward verify that the hyperplanes are per- pendicular bisectors. Use 'Fo' for unbounded regions, and 'Fv' for the corresponding Voronoi vertices. FI Print facet identifiers. Fm Print number of merges for each facet. At most 511 merges are reported for a facet. See 'PMn' for printing the facets with the most merges. FM Output the hull in Maple format. See 'm' Fn Print neighbors for each facet. The output starts with the number of facets. Then each facet is printed one per line. Each line is the number of neighbors followed by an index for each neighbor. The indices match the other facet output formats. A negative index indicates an unprinted facet due to printing only good facets ('Pg'). It is the negation of the facet's id (option 'FI'). For example, negative indices are used for facets "at infinity" in the Delaunay triangulation. FN Print vertex neighbors or coplanar facet for each point. The first line is the number of points. Then each point is printed, one per line. If the point is coplanar, the line is "1" followed by the facet's id. If the point is not a selected vertex, the line is "0". Otherwise, each line is the num- ber of neighbors followed by the corresponding facet indices (see 'Fn'). Fo Print outer planes for each facet in the same for- mat as 'n'. The outer plane is above all points. Fo Print separating hyperplanes for unbounded, outer regions of the Voronoi diagram. The first line is the number of ridges. Then each hyperplane is printed, one per line. A line starts with the num- ber of indices and floats. The first pair lists adjacent input sites, the next d floats are the normalized coefficients for the hyperplane, and the last float is the offset. The hyperplane is ori- ented toward verify that the hyperplanes are per- pendicular bisectors. Use 'Fi' for bounded regions, and 'Fv' for the corresponding Voronoi vertices. FO List all options to stderr, including the default values. Additional 'FO's are printed to stdout. Fp Print points for halfspace intersections (option 'Hn,n,...'). Each intersection corresponds to a Geometry Center 2003/12/30 9 qhull(1) qhull(1) facet of the dual polytope. The "infinity" point [-10.101,-10.101,...] indicates an unbounded intersection. FP For each coplanar point ('Qc') print the point id of the nearest vertex, the point id, the facet id, and the distance. FQ Print command used for qhull and input. Fs Print a summary. The first line consists of the number of integers ("7"), followed by the dimen- sion, the number of points, the number of vertices, the number of facets, the number of vertices selected for output, the number of facets selected for output, the number of coplanar points selected for output. The second line consists of the number of reals ("2"), followed by the maxmimum offset to an outer plane and and minimum offset to an inner plane. Roundoff is included. Later versions of Qhull may produce additional integers or reals. FS Print the size of the hull. The first line con- sists of the number of integers ("0"). The second line consists of the number of reals ("2"), fol- lowed by the total facet area, and the total vol- ume. Later versions of Qhull may produce addi- tional integers or reals. The total volume measures the volume of the inter- section of the halfspaces defined by each facet. Both area and volume are approximations for non- simplicial facets. See option 'Fa'. Ft Print a triangulation with added points for non- simplicial facets. The first line is the dimension and the second line is the number of points and the number of facets. The points follow, one per line, then the facets follow as a list of point indices. With option points include the point-at-infinity. Fv Print vertices for each facet. The first line is the number of facets. Then each facet is printed, one per line. Each line is the number of vertices followed by the corresponding point ids. Vertices are listed in the order they were added to the hull (the last one is first). Fv Print all ridges of a Voronoi diagram. The first line is the number of ridges. Then each ridge is printed, one per line. A line starts with the num- ber of indices. The first pair lists adjacent Geometry Center 2003/12/30 10 qhull(1) qhull(1) input sites, the remaining indices list Voronoi vertices. Vertex '0' indicates the vertex-at- infinity (i.e., an unbounded ray). In 3-d, the vertices are listed in order. See 'Fi' and 'Fo' for separating hyperplanes. FV Print average vertex. The average vertex is a fea- sible point for halfspace intersection. Fx List extreme points (vertices) of the convex hull. The first line is the number of points. The other lines give the indices of the corresponding points. The first point is '0'. In 2-d, the points occur in counter-clockwise order; otherwise they occur in input order. For Delaunay triangulations, 'Fx' lists the extreme points of the input sites. The points are unordered. Geomview options G Produce a file for viewing with Geomview. Without other options, Qhull displays edges in 2-d, outer planes in 3-d, and ridges in 4-d. A ridge can be explicit or implicit. An explicit ridge is a dim-1 dimensional simplex between two facets. In 4-d, the explicit ridges are triangles. When displaying a ridge in 4-d, Qhull projects the ridge's vertices to one of its facets' hyperplanes. Use 'Gh' to project ridges to the intersection of both hyper- planes. Ga Display all input points as dots. Gc Display the centrum for each facet in 3-d. The centrum is defined by a green radius sitting on a blue plane. The plane corresponds to the facet's hyperplane. The radius is defined by 'C-n' or 'Cn'. GDn Drop dimension n in 3-d or 4-d. The result is a 2-d or 3-d object. Gh Display hyperplane intersections in 3-d and 4-d. In 3-d, the intersection is a black line. It lies on two neighboring hyperplanes (c.f., the blue squares associated with centrums ('Gc')). In 4-d, the ridges are projected to the intersection of both hyperplanes. Gi Display inner planes in 2-d and 3-d. The inner plane of a facet is below all of its vertices. It is parallel to the facet's hyperplane. The inner plane's color is the opposite (1-r,1-g,1-b) of the Geometry Center 2003/12/30 11 qhull(1) qhull(1) outer plane. Its edges are determined by the ver- tices. Gn Do not display inner or outer planes. By default, Geomview displays the precise plane (no merging) or both inner and output planes (merging). Under merging, Geomview does not display the inner plane if the the difference between inner and outer is too small. Go Display outer planes in 2-d and 3-d. The outer plane of a facet is above all input points. It is parallel to the facet's hyperplane. Its color is determined by the facet's normal, and its edges are determined by the vertices. Gp Display coplanar points and vertices as radii. A radius defines a ball which corresponds to the imprecision of the point. The imprecision is the maximum of the roundoff error, the centrum radius, and maxcoord * (1-An). It is at least 1/20'th of the maximum coordinate, and ignores post-merging if pre-merging is done. Gr Display ridges in 3-d. A ridge connects the two vertices that are shared by neighboring facets. Ridges are always displayed in 4-d. Gt A 3-d Delaunay triangulation looks like a convex hull with interior facets. Option 'Gt' removes the outside ridges to reveal the outermost facets. It automatically sets options 'Gr' and 'GDn'. Gv Display vertices as spheres. The radius of the sphere corresponds to the imprecision of the data. See 'Gp' for determining the radius. Print options PAn Only the n largest facets are marked good for printing. Unless 'PG' is set, 'Pg' is automati- cally set. Pdk:n Drop facet from output if normal[k] <= n. The option 'Pdk' uses the default value of 0 for n. PDk:n Drop facet from output if normal[k] >= n. The option 'PDk' uses the default value of 0 for n. PFn Only facets with area at least 'n' are marked good for printing. Unless 'PG' is set, 'Pg' is automat- ically set. Geometry Center 2003/12/30 12 qhull(1) qhull(1) Pg Print only good facets. A good facet is either visible from a point (the 'QGn' option) or includes a point (the 'QVn' option). It also meets the requirements of 'Pdk' and 'PDk' options. Option 'Pg' is automatically set for options 'PAn' and 'PFn'. PG Print neighbors of good facets. PMn Only the n facets with the most merges are marked good for printing. Unless 'PG' is set, 'Pg' is automatically set. Po Force output despite precision problems. Verify ('Tv') does not check coplanar points. Flipped facets are reported and concave facets are counted. If 'Po' is used, points are not partitioned into flipped facets and a flipped facet is always visible to a point. Also, if an error occurs before the completion of Qhull and tracing is not active, 'Po' outputs a neighborhood of the erroneous facets (if any). Pp Do not report precision problems. Qhull control options Qbk:0Bk:0 Drop dimension k from the input points. This allows the user to take convex hulls of sub-dimen- sional objects. It happens before the Delaunay and Voronoi transformation. QbB Scale the input points to fit the unit cube. After scaling, the lower bound will be -0.5 and the upper bound +0.5 in all dimensions. For Delaunay and Voronoi diagrams, scaling happens after projection to the paraboloid. Under precise arithmetic, scal- ing does not change the topology of the convex hull. Qbb Scale the last coordinate to [0, m] where m is the maximum absolute value of the other coordinates. For Delaunay and Voronoi diagrams, scaling happens after projection to the paraboloid. It reduces roundoff error for inputs with integer coordinates. Under precise arithmetic, scaling does not change the topology of the convex hull. Qbk:n Scale the k'th coordinate of the input points. After scaling, the lower bound of the input points will be n. 'Qbk' scales to -0.5. Geometry Center 2003/12/30 13 qhull(1) qhull(1) QBk:n Scale the k'th coordinate of the input points. After scaling, the upper bound will be n. 'QBk' scales to +0.5. Qc Keep coplanar points with the nearest facet. Out- put formats 'p', 'f', 'Gp', 'Fc', 'FN', and 'FP' will print the points. Qf Partition points to the furthest outside facet. Qg Only build good facets. With the 'Qg' option, Qhull will only build those facets that it needs to determine the good facets in the output. See 'QGn', 'QVn', and 'PdD' for defining good facets, and 'Pg' and 'PG' for printing good facets and their neighbors. QGn A facet is good (see 'Qg' and 'Pg') if it is visi- ble from point n. If n < 0, a facet is good if it is not visible from point n. Point n is not added to the hull (unless 'TCn' or 'TPn'). With rbox, use the 'Pn,m,r' option to define your point; it will be point 0 (QG0). Qi Keep interior points with the nearest facet. Out- put formats 'p', 'f', 'Gp', 'FN', 'FP', and 'Fc' will print the points. QJn Joggle each input coordinate by adding a random number in [-n,n]. If a precision error occurs, then qhull increases n and tries again. It does not increase n beyond a certain value, and it stops after a certain number of attempts [see user.h]. Option 'QJ' selects a default value for n. The output will be simplicial. For Delaunay triangula- tions, 'QJn' sets 'Qbb' to scale the last coordi- nate (not if 'Qbk:n' or 'QBk:n' is set). 'QJn' is deprecated for Voronoi diagrams. See also 'Qt'. Qm Only process points that would otherwise increase max_outside. Other points are treated as coplanar or interior points. Qr Process random outside points instead of furthest ones. This makes Qhull equivalent to the random- ized incremental algorithms. CPU time is not reported since the randomization is inefficient. QRn Randomly rotate the input points. If n=0, use time as the random number seed. If n>0, use n as the random number seed. If n=-1, don't rotate but use time as the random number seed. For Delaunay tri- angulations ('d' and 'v'), rotate about the last axis. Geometry Center 2003/12/30 14 qhull(1) qhull(1) Qs Search all points for the initial simplex. Qt Triangulated output. Triangulate non-simplicial facets. 'Qt' is deprecated for Voronoi diagrams. See also 'QJn' Qv Test vertex neighbors for convexity after post- merging. To use the 'Qv' option, you also need to set a merge option (e.g., 'Qx' or 'C-0'). QVn A good facet (see 'Qg' and 'Pg') includes point n. If n<0, then a good facet does not include point n. The point is either in the initial simplex or it is the first point added to the hull. Option 'QVn' may not be used with merging. Qx Perform exact merges while building the hull. The "exact" merges are merging a point into a coplanar facet (defined by 'Vn', 'Un', and 'C-n'), merging concave facets, merging duplicate ridges, and merg- ing flipped facets. Coplanar merges and angle coplanar merges ('A-n') are not performed. Concav- ity testing is delayed until a merge occurs. After the hull is built, all coplanar merges are performed (defined by 'C-n' and 'A-n'), then post- merges are performed (defined by 'Cn' and 'An'). Qz Add a point "at infinity" that is above the paraboloid for Delaunay triangulations and Voronoi diagrams. This reduces precision problems and allows the triangulation of cospherical points. Qhull experiments and speedups Q0 Turn off pre-merging as a default option. With 'Q0'/'Qx' and without explicit pre-merge options, Qhull ignores precision issues while constructing the convex hull. This may lead to precision errors. If so, a descriptive warning is generated. Q1 With 'Q1', Qhull sorts merges by type (coplanar, angle coplanar, concave) instead of by angle. Q2 With 'Q2', Qhull merges all facets at once instead of using independent sets of merges and then retesting. Q3 With 'Q3', Qhull does not remove redundant ver- tices. Q4 With 'Q4', Qhull avoids merges of an old facet into a new facet. Q5 With 'Q5', Qhull does not correct outer planes at the end. The maximum outer plane is used instead. Geometry Center 2003/12/30 15 qhull(1) qhull(1) Q6 With 'Q6', Qhull does not pre-merge concave or coplanar facets. Q7 With 'Q7', Qhull processes facets in depth-first order instead of breadth-first order. Q8 With 'Q8' and merging, Qhull does not retain near- interior points for adjusting outer planes. 'Qc' will probably retain all points that adjust outer planes. Q9 With 'Q9', Qhull processes the furthest of all out- side sets at each iteration. Q10 With 'Q10', Qhull does not use special processing for narrow distributions. Q11 With 'Q11', Qhull copies normals and recomputes centrums for tricoplanar facets. Q12 With 'Q12', Qhull does not report a very wide merge due to a duplicated ridge with nearly coincident vertices Q14 With 'Q14', Qhull does not rename vertices that create a duplicate ridge Trace options Tn Trace at level n. Qhull includes full execution tracing. 'T-1' traces events. 'T1' traces the overall execution of the program. 'T2' and 'T3' trace overall execution and geometric and topologi- cal events. 'T4' traces the algorithm. 'T5' includes information about memory allocation and Gaussian elimination. Ta Annotate output with codes that identify the corresponding qh_fprintf() statement. Tc Check frequently during execution. This will catch most inconsistency errors. TCn Stop Qhull after building the cone of new facets for point n. The output for 'f' includes the cone and the old hull. See also 'TVn'. TFn Report progress whenever more than n facets are created During post-merging, 'TFn' reports progress after more than n/2 merges. TI file Input data from 'file'. The filename may not include spaces or quotes. TO file Output results to 'file'. The name may be enclosed in single quotes. TPn Turn on tracing when point n is added to the hull. Trace partitions of point n. If used with TWn, turn off tracing after adding point n to the hull. TRn Rerun qhull n times. Usually used with 'QJn' to determine the probability that a given joggle will fail. Ts Collect statistics and print to stderr at the end of execution. Tv Verify the convex hull. This checks the topologi- cal structure, facet convexity, and point inclu- sion. If precision problems occurred, facet con- vexity is tested whether or not 'Tv' is selected. Option 'Tv' does not check point inclusion if Geometry Center 2003/12/30 16 qhull(1) qhull(1) forcing output with 'Po', or if 'Q5' is set. For point inclusion testing, Qhull verifies that all points are below all outer planes (facet->max- outside). Point inclusion is exhaustive if merging or if the facet-point product is small enough; oth- erwise Qhull verifies each point with a directed search (qh_findbest). Point inclusion testing occurs after producing out- put. It prints a message to stderr unless option 'Pp' is used. This allows the user to interrupt Qhull without changing the output. TVn Stop Qhull after adding point n. If n < 0, stop Qhull before adding point n. Output shows the hull at this time. See also 'TCn' TMn Turn on tracing at n'th merge. TWn Trace merge facets when the width is greater than n. Tz Redirect stderr to stdout. BUGS Please report bugs to Brad Barber at qhull_bug@qhull.org. If Qhull does not compile, it is due to an incompatibility between your system and ours. The first thing to check is that your compiler is ANSI standard. If it is, check the man page for the best options, or find someone to help you. If you locate the cause of your problem, please send email since it might help others. If Qhull compiles but crashes on the test case (rbox D4), there's still incompatibility between your system and ours. Typically it's been due to mem.c and memory align- ment. You can use qh_NOmem in mem.h to turn off memory management. Please let us know if you figure out how to fix these problems. If you do find a problem, try to simplify it before reporting the error. Try different size inputs to locate the smallest one that causes an error. You're welcome to hunt through the code using the execution trace as a guide. This is especially true if you're incorporating Qhull into your own program. When you do report an error, please attach a data set to the end of your message. This allows us to see the error for ourselves. Qhull is maintained part-time. Geometry Center 2003/12/30 17 qhull(1) qhull(1) E-MAIL Please send correspondence to qhull@qhull.org and report bugs to qhull_bug@qhull.org. Let us know how you use Qhull. If you mention it in a paper, please send the reference and an abstract. If you would like to get Qhull announcements (e.g., a new version) and news (any bugs that get fixed, etc.), let us know and we will add you to our mailing list. If you would like to communicate with other Qhull users, we will add you to the qhull_users alias. For Internet news about geometric algorithms and convex hulls, look at comp.graph- ics.algorithms and sci.math.num-analysis SEE ALSO rbox(1) Barber, C. B., D.P. Dobkin, and H.T. Huhdanpaa, "The Quickhull Algorithm for Convex Hulls," ACM Trans. on Math- ematical Software, 22(4):469-483, Dec. 1996. http://portal.acm.org/citation.cfm?doid=235815.235821 http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.117.405 Clarkson, K.L., K. Mehlhorn, and R. Seidel, "Four results on randomized incremental construction," Computational Geometry: Theory and Applications, vol. 3, p. 185-211, 1993. Preparata, F. and M. Shamos, Computational Geometry, Springer-Verlag, New York, 1985. AUTHORS C. Bradford Barber Hannu Huhdanpaa bradb@shore.net hannu@qhull.org ACKNOWLEDGEMENTS A special thanks to Albert Marden, Victor Milenkovic, the Geometry Center, Harvard University, and Endocardial Solu- tions, Inc. for supporting this work. Qhull 1.0 and 2.0 were developed under National Science Foundation grants NSF/DMS-8920161 and NSF-CCR-91-15793 750-7504. David Dobkin Geometry Center 2003/12/30 18 qhull(1) qhull(1) guided the original work at Princeton University. If you find it useful, please let us know. The Geometry Center was supported by grant DMS-8920161 from the National Science Foundation, by grant DOE/DE-FG02-92ER25137 from the Department of Energy, by the University of Minnesota, and by Minnesota Technology, Inc. Qhull is available from http://www.qhull.org Geometry Center 2003/12/30 19 geometry/vignettes/qhull/README.txt0000644000176200001440000005253113431000556016743 0ustar liggesusersName qhull, rbox 2015.2 2016/01/18 Convex hull, Delaunay triangulation, Voronoi diagrams, Halfspace intersection Documentation: html/index.htm Available from: (git@github.com:qhull/qhull.git) News and a paper: Version 1 (simplicial only): Purpose Qhull is a general dimension convex hull program that reads a set of points from stdin, and outputs the smallest convex set that contains the points to stdout. It also generates Delaunay triangulations, Voronoi diagrams, furthest-site Voronoi diagrams, and halfspace intersections about a point. Rbox is a useful tool in generating input for Qhull; it generates hypercubes, diamonds, cones, circles, simplices, spirals, lattices, and random points. Qhull produces graphical output for Geomview. This helps with understanding the output. Environment requirements Qhull and rbox should run on all 32-bit and 64-bit computers. Use an ANSI C or C++ compiler to compile the program. The software is self-contained. It comes with examples and test scripts. Qhull's C++ interface uses the STL. The C++ test program uses QTestLib from the Qt Framework. Qhull's C++ interface may change without notice. Eventually, it will move into the qhull shared library. Qhull is copyrighted software. Please read COPYING.txt and REGISTER.txt before using or distributing Qhull. To cite Qhull, please use Barber, C.B., Dobkin, D.P., and Huhdanpaa, H.T., "The Quickhull algorithm for convex hulls," ACM Trans. on Mathematical Software, 22(4):469-483, Dec 1996, http://www.qhull.org. To modify Qhull, particularly the C++ interface Qhull is on GitHub (http://github.com/qhull/qhull, git@github.com:qhull/qhull.git) For internal documentation, see html/qh-code.htm To install Qhull Qhull is precompiled for Windows 32-bit, otherwise it needs compilation. Qhull includes Makefiles for gcc and other targets, CMakeLists.txt for CMake, .sln/.vcproj/.vcxproj files for Microsoft Visual Studio, and .pro files for Qt Creator. It compiles under Windows with mingw. Install and build instructions follow. See the end of this document for a list of distributed files. ----------------- Installing Qhull on Windows 10, 8, 7 (32- or 64-bit), Windows XP, and Windows NT The zip file contains rbox.exe, qhull.exe, qconvex.exe, qdelaunay.exe, qhalf.exe, qvoronoi.exe, testqset.exe, user_eg*.exe, documentation files, and source files. Qhull.exe and user-eg3.exe are compiled with the reentrant library while the other executables use the non-reentrant library. To install Qhull: - Unzip the files into a directory (e.g., named 'qhull') - Click on QHULL-GO or open a command window into Qhull's bin directory. - Test with 'rbox D4 | qhull' To uninstall Qhull - Delete the qhull directory To learn about Qhull: - Execute 'qconvex' for a synopsis and examples. - Execute 'rbox 10 | qconvex' to compute the convex hull of 10 random points. - Execute 'rbox 10 | qconvex i TO file' to write results to 'file'. - Browse the documentation: qhull\html\index.htm - If an error occurs, Windows sends the error to stdout instead of stderr. Use 'TO xxx' to send normal output to xxx To improve the command window - Double-click the window bar to increase the size of the window - Right-click the window bar - Select Properties - Check QuickEdit Mode Select text with right-click or Enter Paste text with right-click - Change Font to Lucinda Console - Change Layout to Screen Buffer Height 999, Window Size Height 55 - Change Colors to Screen Background White, Screen Text Black - Click OK - Select 'Modify shortcut that started this window', then OK If you use qhull a lot, install a bash shell such as MSYS (www.mingw.org/wiki/msys), Road Bash (www.qhull.org/bash), or Cygwin (www.cygwin.com). ----------------- Installing Qhull on Unix with gcc To build Qhull, static libraries, shared library, and C++ interface - Download and extract Qhull (either GitHub, .tgz file, or .zip file) - make - export LD_LIBRARY_PATH=$PWD/lib:$LD_LIBRARY_PATH The Makefiles may be edited for other compilers. If 'testqset' exits with an error, qhull is broken A simple Makefile for Qhull is in src/libqhull and src/libqhull_r. To build the Qhull executables and libqhullstatic - Extract Qhull from qhull...tgz or qhull...zip - cd src/libqhull_r # cd src/libqhull - make ----------------- Installing Qhull with CMake 2.6 or later See CMakeLists.txt for examples and further build instructions To build Qhull, static libraries, shared library, and C++ interface - Download and extract Qhull (either GitHub, .tgz file, or .zip file) - cd build - cmake --help # List build generators - make -G "" .. && cmake .. - cmake .. - make - make install The ".." is important. It refers to the parent directory (i.e., qhull/) On Windows, CMake installs to C:/Program Files/qhull. 64-bit generators have a "Win64" tag. If creating a qhull package, please include a pkg-config file based on build/qhull*.pc.in If cmake fails with "No CMAKE_C_COMPILER could be found" - cmake was not able to find the build environment specified by -G "..." ----------------- Installing Qhull with Qt To build Qhull, including its C++ test (qhulltest) - Download and extract Qhull (either GitHub, .tgz file, or .zip file) - Load src/qhull-all.pro into QtCreator - Build ------------------- Working with Qhull's C++ interface See html/qh-code.htm#cpp for calling Qhull from C++ programs See html/qh-code.htm#reentrant for converting from Qhull-2012 Examples of using the C++ interface user_eg3_r.cpp qhulltest/*_test.cpp Qhull's C++ interface is likely to change. Stay current with GitHub. To clone Qhull's next branch from http://github.com/qhull/qhull git init git clone git@github.com:qhull/qhull.git cd qhull git checkout next ... git pull origin next Compile qhullcpp and libqhullstatic_r with the same compiler. Both libraries use the C routines setjmp() and longjmp() for error handling. They must be compiled with the same compiler. ------------------- Calling Qhull from C programs See html/qh-code.htm#library for calling Qhull from C programs See html/qh-code.htm#reentrant for converting from Qhull-2012 Warning: You will need to understand Qhull's data structures and read the code. Most users will find it easier to call Qhull as an external command. The new, reentrant 'C' code (src/libqhull_r), passes a pointer to qhT to most Qhull routines. This allows multiple instances of Qhull to run at the same time. It simplifies the C++ interface. The non-reentrant 'C' code (src/libqhull) looks unusual. It refers to Qhull's global data structure, qhT, through a 'qh' macro (e.g., 'qh ferr'). This allows the same code to use static memory or heap memory. If qh_QHpointer is defined, qh_qh is a pointer to an allocated qhT; otherwise qh_qh is a global static data structure of type qhT. ------------------ Compiling Qhull with Microsoft Visual C++ To compile 32-bit Qhull with Microsoft Visual C++ 2010 and later - Download and extract Qhull (either GitHub, .tgz file, or .zip file) - Load solution build/qhull-32.sln - Build target 'Win32' - Project qhulltest requires Qt for DevStudio (http://www.qt.io) Set the QTDIR environment variable to your Qt directory (e.g., c:/qt/5.2.0/5.2.0/msvc2012) If QTDIR is incorrect, precompile will fail with 'Can not locate the file specified' To compile 64-bit Qhull with Microsoft Visual C++ 2010 and later - 64-bit Qhull has larger data structures due to 64-bit pointers - Download and extract Qhull (either GitHub, .tgz file, or .zip file) - Load solution build/qhull-64.sln - Build target 'Win32' - Project qhulltest requires Qt for DevStudio (http://www.qt.io) Set the QTDIR environment variable to your Qt directory (e.g., c:/qt/5.2.0/5.2.0/msvc2012_64) If QTDIR is incorrect, precompile will fail with 'Can not locate the file specified' To compile Qhull with Microsoft Visual C++ 2005 (vcproj files) - Download and extract Qhull (either GitHub, .tgz file, or .zip file) - Load solution build/qhull.sln - Build target 'win32' (not 'x64') - Project qhulltest requires Qt for DevStudio (http://www.qt.io) Set the QTDIR environment variable to your Qt directory (e.g., c:/qt/4.7.4) If QTDIR is incorrect, precompile will fail with 'Can not locate the file specified' ----------------- Compiling Qhull with Qt Creator Qt (http://www.qt.io) is a C++ framework for Windows, Linux, and Macintosh Qhull uses QTestLib to test qhull's C++ interface (see src/qhulltest/) To compile Qhull with Qt Creator - Download and extract Qhull (either GitHub, .tgz file, or .zip file) - Download the Qt SDK - Start Qt Creator - Load src/qhull-all.pro - Build ----------------- Compiling Qhull with mingw on Windows To compile Qhull with MINGW - Download and extract Qhull (either GitHub, .tgz file, or .zip file) - Install Road Bash (http://www.qhull.org/bash) or install MSYS (http://www.mingw.org/wiki/msys) - Install MINGW-w64 (http://sourceforge.net/projects/mingw-w64). Mingw is included with Qt SDK. - make ----------------- Compiling Qhull with cygwin on Windows To compile Qhull with cygwin - Download and extract Qhull (either GitHub, .tgz file, or .zip file) - Install cygwin (http://www.cygwin.com) - Include packages for gcc, make, ar, and ln - make ----------------- Compiling from Makfile without gcc The file, qhull-src.tgz, contains documentation and source files for qhull and rbox. To unpack the tgz file - tar zxf qhull-src.tgz - cd qhull - Use qhull/Makefile Simpler Makefiles are qhull/src/libqhull/Makefile and qhull/src/libqhull_r/Makefile Compiling qhull and rbox with Makefile - in Makefile, check the CC, CCOPTS1, PRINTMAN, and PRINTC defines - the defaults are gcc and enscript - CCOPTS1 should include the ANSI flag. It defines __STDC__ - in user.h, check the definitions of qh_SECticks and qh_CPUclock. - use '#define qh_CLOCKtype 2' for timing runs longer than 1 hour - type: make - this builds: qhull qconvex qdelaunay qhalf qvoronoi rbox libqhull.a libqhull_r.a - type: make doc - this prints the man page - See also qhull/html/index.htm - if your compiler reports many errors, it is probably not a ANSI C compiler - you will need to set the -ansi switch or find another compiler - if your compiler warns about missing prototypes for fprintf() etc. - this is ok, your compiler should have these in stdio.h - if your compiler warns about missing prototypes for memset() etc. - include memory.h in qhull_a.h - if your compiler reports "global.c: storage size of 'qh_qh' isn't known" - delete the initializer "={0}" in global.c, stat.c and mem.c - if your compiler warns about "stat.c: improper initializer" - this is ok, the initializer is not used - if you have trouble building libqhull.a with 'ar' - try 'make -f Makefile.txt qhullx' - if the code compiles, the qhull test case will automatically execute - if an error occurs, there's an incompatibility between machines - If you can, try a different compiler - You can turn off the Qhull memory manager with qh_NOmem in mem.h - You can turn off compiler optimization (-O2 in Makefile) - If you find the source of the problem, please let us know - to install the programs and their man pages: - define MANDIR and BINDIR - type 'make install' - if you have Geomview (www.geomview.org) - try 'rbox 100 | qconvex G >a' and load 'a' into Geomview - run 'q_eg' for Geomview examples of Qhull output (see qh-eg.htm) ------------------ Compiling on other machines and compilers Qhull may compile with Borland C++ 5.0 bcc32. A Makefile is included. Execute 'cd src/libqhull; make -f Mborland'. If you use the Borland IDE, set the ANSI option in Options:Project:Compiler:Source:Language-compliance. Qhull may compile with Borland C++ 4.02 for Win32 and DOS Power Pack. Use 'cd src/libqhull; make -f Mborland -D_DPMI'. Qhull 1.0 compiles with Borland C++ 4.02. For rbox 1.0, use "bcc32 -WX -w- -O2-e -erbox -lc rbox.c". Use the same options for Qhull 1.0. [D. Zwick] If you have troubles with the memory manager, you can turn it off by defining qh_NOmem in mem.h. ----------------- Distributed files README.txt // Instructions for installing Qhull REGISTER.txt // Qhull registration COPYING.txt // Copyright notice QHULL-GO.lnk // Windows icon for eg/qhull-go.bat Announce.txt // Announcement CMakeLists.txt // CMake build file (2.6 or later) CMakeModules/CheckLFS.cmake // enables Large File Support in cmake File_id.diz // Package descriptor index.htm // Home page Makefile // Makefile for gcc and other compilers qhull*.md5sum // md5sum for all files bin/* // Qhull executables and dll (.zip only) build/qhull*.pc.in // pkg-config templates for qhull_r, qhull, and qhull_p build/qhull-32.sln // 32-bit DevStudio solution and project files (2010 and later) build/*-32.vcxproj build/qhull-64.sln // 64-bit DevStudio solution and project files (2010 and later) build/*-64.vcxproj build/qhull.sln // DevStudio solution and project files (2005 and 2009) build/*.vcproj eg/* // Test scripts and geomview files from q_eg html/index.htm // Manual html/qh-faq.htm // Frequently asked questions html/qh-get.htm // Download page html/qhull-cpp.xml // C++ style notes as a Road FAQ (www.qhull.org/road) src/Changes.txt // Change history for Qhull and rbox src/qhull-all.pro // Qt project eg/ q_eg // shell script for Geomview examples (eg.01.cube) q_egtest // shell script for Geomview test examples q_test // shell script to test qhull q_test-ok.txt // output from q_test qhulltest-ok.txt // output from qhulltest (Qt only) make-vcproj.sh // bash shell script to create vcproj and vcxprog files qhull-zip.sh // bash shell script for distribution files rbox consists of (bin, html): rbox.exe // Win32 executable (.zip only) rbox.htm // html manual rbox.man // Unix man page rbox.txt qhull consists of (bin, html): qconvex.exe // Win32 executables and dlls (.zip download only) qhull.exe // Built with the reentrant library (about 2% slower) qdelaunay.exe qhalf.exe qvoronoi.exe qhull_r.dll qhull-go.bat // command window qconvex.htm // html manual qdelaun.htm qdelau_f.htm qhalf.htm qvoronoi.htm qvoron_f.htm qh-eg.htm qh-code.htm qh-impre.htm index.htm qh-opt*.htm qh-quick.htm qh--*.gif // images for manual normal_voronoi_knauss_oesterle.jpg qhull.man // Unix man page qhull.txt bin/ msvcr80.dll // Visual C++ redistributable file (.zip download only) src/ qhull/unix.c // Qhull and rbox applications using non-reentrant libqhullstatic.a rbox/rbox.c qconvex/qconvex.c qhalf/qhalf.c qdelaunay/qdelaunay.c qvoronoi/qvoronoi.c qhull/unix_r.c // Qhull and rbox applications using reentrant libqhullstatic_r.a rbox/rbox_r.c qconvex/qconvex_r.c // Qhull applications built with reentrant libqhull_r/Makefile qhalf/qhalf_r.c qdelaunay/qdelaun_r.c qvoronoi/qvoronoi_r.c user_eg/user_eg_r.c // example of using qhull_r.dll from a user program user_eg2/user_eg2_r.c // example of using libqhullstatic_r.a from a user program user_eg3/user_eg3_r.cpp // example of Qhull's C++ interface libqhullcpp with libqhullstatic_r.a qhulltest/qhulltest.cpp // Test of Qhull's C++ interface using Qt's QTestLib qhull-*.pri // Include files for Qt projects testqset_r/testqset_r.c // Test of reentrant qset_r.c and mem_r.c testqset/testqset.c // Test of non-rentrant qset.c and mem.c src/libqhull libqhull.pro // Qt project for non-rentrant, shared library (qhull.dll) index.htm // design documentation for libqhull qh-*.htm qhull-exports.def // Export Definition file for Visual C++ Makefile // Simple gcc Makefile for qhull and libqhullstatic.a Mborland // Makefile for Borland C++ 5.0 libqhull.h // header file for qhull user.h // header file of user definable constants libqhull.c // Quickhull algorithm with partitioning user.c // user re-definable functions usermem.c userprintf.c userprintf_rbox.c qhull_a.h // include files for libqhull/*.c geom.c // geometric routines geom2.c geom.h global.c // global variables io.c // input-output routines io.h mem.c // memory routines, this is stand-alone code mem.h merge.c // merging of non-convex facets merge.h poly.c // polyhedron routines poly2.c poly.h qset.c // set routines, this only depends on mem.c qset.h random.c // utilities w/ Park & Miller's random number generator random.h rboxlib.c // point set generator for rbox stat.c // statistics stat.h src/libqhull_r libqhull_r.pro // Qt project for rentrant, shared library (qhull_r.dll) index.htm // design documentation for libqhull_r qh-*_r.htm qhull-exports_r.def // Export Definition file for Visual C++ Makefile // Simple gcc Makefile for qhull and libqhullstatic.a libqhull_r.h // header file for qhull user_r.h // header file of user definable constants libqhull_r.c // Quickhull algorithm wi_r.hpartitioning user_r.c // user re-definable functions usermem.c userprintf.c userprintf_rbox.c qhull_ra.h // include files for libqhull/*_r.c geom_r.c // geometric routines geom2.c geom_r.h global_r.c // global variables io_r.c // input-output routines io_r.h mem_r.c // memory routines, this is stand-alone code mem.h merge_r.c // merging of non-convex facets merge.h poly_r.c // polyhedron routines poly2.c poly_r.h qset_r.c // set routines, this only depends on mem_r.c qset.h random_r.c // utilities w/ Park & Miller's random number generator random.h rboxlib_r.c // point set generator for rbox stat_r.c // statistics stat.h src/libqhullcpp/ libqhullcpp.pro // Qt project for renentrant, static C++ library Qhull.cpp // Calls libqhull_r.c from C++ Qhull.h qt-qhull.cpp // Supporting methods for Qt Coordinates.cpp // input classes Coordinates.h PointCoordinates.cpp PointCoordinates.h RboxPoints.cpp // call rboxlib.c from C++ RboxPoints.h QhullFacet.cpp // data structure classes QhullFacet.h QhullHyperplane.cpp QhullHyperplane.h QhullPoint.cpp QhullPoint.h QhullQh.cpp QhullRidge.cpp QhullRidge.h QhullVertex.cpp QhullVertex.h QhullFacetList.cpp // collection classes QhullFacetList.h QhullFacetSet.cpp QhullFacetSet.h QhullIterator.h QhullLinkedList.h QhullPoints.cpp QhullPoints.h QhullPointSet.cpp QhullPointSet.h QhullSet.cpp QhullSet.h QhullSets.h QhullVertexSet.cpp QhullVertexSet.h functionObjects.h // supporting classes QhullError.cpp QhullError.h QhullQh.cpp QhullQh.h QhullStat.cpp QhullStat.h RoadError.cpp // Supporting base classes RoadError.h RoadLogEvent.cpp RoadLogEvent.h usermem_r-cpp.cpp // Optional override for qh_exit() to throw an error src/libqhullstatic/ libqhullstatic.pro // Qt project for non-reentrant, static library src/libqhullstatic_r/ libqhullstatic_r.pro // Qt project for reentrant, static library src/qhulltest/ qhulltest.pro // Qt project for test of C++ interface Coordinates_test.cpp // Test of each class PointCoordinates_test.cpp Qhull_test.cpp QhullFacet_test.cpp QhullFacetList_test.cpp QhullFacetSet_test.cpp QhullHyperplane_test.cpp QhullLinkedList_test.cpp QhullPoint_test.cpp QhullPoints_test.cpp QhullPointSet_test.cpp QhullRidge_test.cpp QhullSet_test.cpp QhullVertex_test.cpp QhullVertexSet_test.cpp RboxPoints_test.cpp RoadTest.cpp // Run multiple test files with QTestLib RoadTest.h ----------------- Authors: C. Bradford Barber Hannu Huhdanpaa (Version 1.0) bradb@shore.net hannu@qhull.org Qhull 1.0 and 2.0 were developed under NSF grants NSF/DMS-8920161 and NSF-CCR-91-15793 750-7504 at the Geometry Center and Harvard University. If you find Qhull useful, please let us know. geometry/vignettes/qhull/Announce.txt0000644000176200001440000000420713431000557017552 0ustar liggesusers Qhull 2015.2 2016/01/18 http://www.qhull.org git@github.com:qhull/qhull.git http://www.geomview.org Qhull computes convex hulls, Delaunay triangulations, Voronoi diagrams, furthest-site Voronoi diagrams, and halfspace intersections about a point. It runs in 2-d, 3-d, 4-d, or higher. It implements the Quickhull algorithm for computing convex hulls. Qhull handles round-off errors from floating point arithmetic. It can approximate a convex hull. The program includes options for hull volume, facet area, partial hulls, input transformations, randomization, tracing, multiple output formats, and execution statistics. The program can be called from within your application. You can view the results in 2-d, 3-d and 4-d with Geomview. To download Qhull: http://www.qhull.org/download git@github.com:qhull/qhull.git Download qhull-96.ps for: Barber, C. B., D.P. Dobkin, and H.T. Huhdanpaa, "The Quickhull Algorithm for Convex Hulls," ACM Trans. on Mathematical Software, 22(4):469-483, Dec. 1996. http://portal.acm.org/citation.cfm?doid=235815.235821 http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.117.405 Abstract: The convex hull of a set of points is the smallest convex set that contains the points. This article presents a practical convex hull algorithm that combines the two-dimensional Quickhull Algorithm with the general dimension Beneath-Beyond Algorithm. It is similar to the randomized, incremental algorithms for convex hull and Delaunay triangulation. We provide empirical evidence that the algorithm runs faster when the input contains non-extreme points, and that it uses less memory. Computational geometry algorithms have traditionally assumed that input sets are well behaved. When an algorithm is implemented with floating point arithmetic, this assumption can lead to serious errors. We briefly describe a solution to this problem when computing the convex hull in two, three, or four dimensions. The output is a set of "thick" facets that contain all possible exact convex hulls of the input. A variation is effective in five or more dimensions. geometry/vignettes/qhull/REGISTER.txt0000644000176200001440000000167613431000556017236 0ustar liggesusersDear Qhull User We would like to find out how you are using our software. Think of Qhull as a new kind of shareware: you share your science and successes with us, and we share our software and support with you. If you use Qhull, please send us a note telling us what you are doing with it. We need to know: (1) What you are working on - an abstract of your work would be fine. (2) How Qhull has helped you, for example, by increasing your productivity or allowing you to do things you could not do before. If Qhull had a direct bearing on your work, please tell us about this. We encourage you to cite Qhull in your publications. To cite Qhull, please use Barber, C.B., Dobkin, D.P., and Huhdanpaa, H.T., "The Quickhull algorithm for convex hulls," ACM Trans. on Mathematical Software, 22(4):469-483, Dec 1996, http://www.qhull.org. Please send e-mail to bradb@shore.net Thank you! geometry/vignettes/qhull/index.html0000644000176200001440000003523713431000556017246 0ustar liggesusers Qhull code for Convex Hull, Delaunay Triangulation, Voronoi Diagram, and Halfspace Intersection about a Point URL: http://www.qhull.org
    To: NewsDownloadCiteSeerImagesManualFAQProgramsOptions


    Qhull

    [CONE]
    Qhull computes the convex hull, Delaunay triangulation, Voronoi diagram, halfspace intersection about a point, furthest-site Delaunay triangulation, and furthest-site Voronoi diagram. The source code runs in 2-d, 3-d, 4-d, and higher dimensions. Qhull implements the Quickhull algorithm for computing the convex hull. It handles roundoff errors from floating point arithmetic. It computes volumes, surface areas, and approximations to the convex hull.

    Qhull does not support triangulation of non-convex surfaces, mesh generation of non-convex objects, medium-sized inputs in 9-D and higher, alpha shapes, weighted Voronoi diagrams, Voronoi volumes, or constrained Delaunay triangulations,

    Qhull 2015.2 introduces reentrant Qhull. It allows concurrent Qhull runs and simplifies the C++ interface to Qhull. If you call Qhull from your program, you should use reentrant Qhull (libqhull_r) instead of qh_QHpointer (libqhull). If you use Qhull 2003.1. please upgrade or apply poly.c-qh_gethash.patch.


    Introduction

    • Fukuda's introduction to convex hulls, Delaunay triangulations, Voronoi diagrams, and linear programming
    • Lambert's Java visualization of convex hull algorithms
    • LEDA Guide to geometry algorithms
    • MathWorld's Computational Geometry from Wolfram Research
    • Skiena's Computational Geometry from his Algorithm Design Manual.
    • Stony Brook Algorithm Repository, computational geometry

    Qhull Documentation and Support

    Related URLs

    FAQs and Newsgroups


    The program includes options for input transformations, randomization, tracing, multiple output formats, and execution statistics. The program can be called from within your application.

    You can view the results in 2-d, 3-d and 4-d with Geomview. An alternative is VTK.

    For an article about Qhull, download from ACM or CiteSeer:

    Barber, C.B., Dobkin, D.P., and Huhdanpaa, H.T., "The Quickhull algorithm for convex hulls," ACM Trans. on Mathematical Software, 22(4):469-483, Dec 1996, http://www.qhull.org

    Abstract:

    The convex hull of a set of points is the smallest convex set that contains the points. This article presents a practical convex hull algorithm that combines the two-dimensional Quickhull Algorithm with the general dimension Beneath-Beyond Algorithm. It is similar to the randomized, incremental algorithms for convex hull and Delaunay triangulation. We provide empirical evidence that the algorithm runs faster when the input contains non-extreme points, and that it uses less memory.

    Computational geometry algorithms have traditionally assumed that input sets are well behaved. When an algorithm is implemented with floating point arithmetic, this assumption can lead to serious errors. We briefly describe a solution to this problem when computing the convex hull in two, three, or four dimensions. The output is a set of "thick" facets that contain all possible exact convex hulls of the input. A variation is effective in five or more dimensions.


    Up: Past Software Projects of the Geometry Center
    URL: http://www.qhull.org
    To: NewsDownloadCiteSeerImagesManualFAQProgramsOptions


    [HOME] The Geometry Center Home Page

    Comments to: qhull@qhull.org
    Created: May 17 1995 --- geometry/vignettes/qhull/File_id.diz0000644000176200001440000000063113431000556017302 0ustar liggesusersQhull 2015.2 - Qhull computes convex hulls, Delaunay triangulations, halfspace inter- sections about a point, Voronoi diagrams, furthest-site Delaunay triangulations, and furthest-site Voronoi diagrams. Qhull works with 2-d, 3-d, 4-d, 5-d, and higher dimensions. It computes volumes, surface areas, and approximations. It runs in a command window under Windows 95/NT/XP/7. www.qhull.org, freeware. geometry/vignettes/qhull-eg-004.pdf0000644000176200001440000001415213431000557016630 0ustar liggesusers%PDF-1.4 %ρ\r 1 0 obj << /CreationDate (D:20190212154401) /ModDate (D:20190212154401) /Title (R Graphics Output) /Producer (R 3.5.2) /Creator (R) >> endobj 2 0 obj << /Type /Catalog /Pages 3 0 R >> endobj 7 0 obj << /Type /Page /Parent 3 0 R /Contents 8 0 R /Resources 4 0 R >> endobj 8 0 obj << /Length 2195 /Filter /FlateDecode >> stream x[ˮ\W|nm$ $ x!Y(")@~?Uܹcyr,4Cُ>v|pzq|8L=R)GO3U?>xp?}/)^q?ӫG>v{y{r>t =76m->~ӳَ g<9*Lo%?{>Jp {5vF=y^z4B8:$G'7Q7-J.ȽQ 'Ќw/=N3FuP9 :r뭦h Eu3Eٽdn> EѹBkKQ6?Y (kpY@=i`hS=t[}`A U/XLQ'HU\̨Hɨ.HLw{|)"&S2s /ry o&ܻ`LfQ3s0Kez/tƳ+C$X*9*|h?9pc.X\ 1:&Eg'8VaN'n>.`2@mOkCrl0q.ػ!:nplQ)$r"Yu@`-ž?tLLb1@ot R9㆑ y[2VDŽDD*qv+~ eSQZ8 I'(Aa@] 5L(ygl\ZqPw%cuΊvq *ϜG{ߋcmZ3HN!]5csyȪExP$H' kk%kaLrcu2gQ,ԨMݹl;KP1ʴʈRz֙BRŢfH~j,&_YT%*'- =F)X>)=/ʼ*u;4I8fXB0ɠc˥51<LO]RIrw]!'XMUys R 26u N Zk2Tu +3I{*ޖ3) XԲE7RRJq,4Rf-jejp'khJ, W&Rx1!vۄRZ7[n4QT4*GI",5/ QUm N)IlZ9LFKp݄ :LBJf[~[ hPDXE_W+ JHE_V@*mB/ Q15c0WD1y.ʽCיYȫNˊζ0V;J;X~W2#)ypnr7-W̫**,PV@|Pz[K,HFVS<4l&T/ J) 3T4[Nj멶eӈ'[#j_;s̫_`Q*ǟ 臨2=g<ϏO,v??>ex5~)u7nӛeXc?V붩 {g^M/ 3y|.n^uW]ͽxP0 ez>ՆFW_ϏOl}??^O.i]__ 8Cc ~v27~v6,~~2t~v2[JݺeJvlx>珏_~mK\FVWWW_;mk6.m?-}oҘ_wH*eaCäjEaAKUyPnzS^c6O=هUle7J%x#(=' !d 5/2ɫɘ5R.b.-o75Aޭ.7JK<_*o7^ Uendstream endobj 3 0 obj << /Type /Pages /Kids [ 7 0 R ] /Count 1 /MediaBox [0 0 432 432] >> endobj 4 0 obj << /ProcSet [/PDF /Text] /Font << /F1 10 0 R /F2 11 0 R >> /ExtGState << >> /ColorSpace << /sRGB 5 0 R >> >> endobj 5 0 obj [/ICCBased 6 0 R] endobj 6 0 obj << /Alternate /DeviceRGB /N 3 /Length 2596 /Filter /FlateDecode >> stream xwTSϽ7PkhRH H.*1 J"6DTpDQ2(C"QDqpId߼y͛~kg}ֺLX Xňg` lpBF|،l *?Y"1P\8=W%Oɘ4M0J"Y2Vs,[|e92<se'9`2&ctI@o|N6(.sSdl-c(2-yH_/XZ.$&\SM07#1ؙYrfYym";8980m-m(]v^DW~ emi]P`/u}q|^R,g+\Kk)/C_|Rax8t1C^7nfzDp 柇u$/ED˦L L[B@ٹЖX!@~(* {d+} G͋љς}WL$cGD2QZ4 E@@A(q`1D `'u46ptc48.`R0) @Rt CXCP%CBH@Rf[(t CQhz#0 Zl`O828.p|O×X ?:0FBx$ !i@ڐH[EE1PL ⢖V6QP>U(j MFkt,:.FW8c1L&ӎ9ƌaX: rbl1 {{{;}#tp8_\8"Ey.,X%%Gщ1-9ҀKl.oo/O$&'=JvMޞxǥ{=Vs\x ‰N柜>ucKz=s/ol|ϝ?y ^d]ps~:;/;]7|WpQoH!ɻVsnYs}ҽ~4] =>=:`;cܱ'?e~!ańD#G&}'/?^xI֓?+\wx20;5\ӯ_etWf^Qs-mw3+?~O~endstream endobj 9 0 obj << /Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences [ 45/minus 96/quoteleft 144/dotlessi /grave /acute /circumflex /tilde /macron /breve /dotaccent /dieresis /.notdef /ring /cedilla /.notdef /hungarumlaut /ogonek /caron /space] >> endobj 10 0 obj << /Type /Font /Subtype /Type1 /Name /F1 /BaseFont /ZapfDingbats >> endobj 11 0 obj << /Type /Font /Subtype /Type1 /Name /F2 /BaseFont /Helvetica /Encoding 9 0 R >> endobj xref 0 12 0000000000 65535 f 0000000021 00000 n 0000000163 00000 n 0000002559 00000 n 0000002642 00000 n 0000002766 00000 n 0000002799 00000 n 0000000212 00000 n 0000000292 00000 n 0000005494 00000 n 0000005751 00000 n 0000005835 00000 n trailer << /Size 12 /Info 1 0 R /Root 2 0 R >> startxref 5932 %%EOF geometry/vignettes/MODIFIED.txt0000644000176200001440000000105613431000556016035 0ustar liggesusersmodified function file line# date by reason ================= ==== ===== ==== == ====== qh_exit usermem_r.c 43 2012/04/03 David Sterratt To fix warnings about exit being called qh_printf userprintf_r.c 55 2018/05/21 David Sterratt Printing error messages qh_printf userprintf_r.c 63 2018/05/21 David Sterratt Printing error messages qh_printf userprintf_r.c 72 2018/05/21 David Sterratt Printing error messages geometry/vignettes/qhull-eg.Rnw0000644000176200001440000000603313431000557016323 0ustar liggesusers%\VignetteIndexEntry{Qhull examples} \documentclass{article} \usepackage{Sweave} \SweaveOpts{echo=TRUE} \usepackage{hyperref} \usepackage[british]{babel} \title{Qhull examples} \author{David C. Sterratt} \begin{document} \maketitle This document presents examples of the \texttt{geometry} package functions which implement functions using the \href{http://www.qhull.org}{Qhull library}. \section{Convex hulls in 2D} \label{qhull-eg:sec:convex-hull-2d} \subsection{Calling \texttt{convhulln} with one argument} \label{qhull-eg:sec:call-convh-with} With one argument, convhulln returns the indices of the points of the convex hull. <<>>= library(geometry) ps <-matrix(rnorm(30), , 2) ch <- convhulln(ps) head(ch) @ \subsection{Calling \texttt{convhulln} with \texttt{options}} \label{qhull-eg:sec:call-convh-with} We can supply Qhull options to \texttt{convhulln}; in this case it returns an object of class \texttt{convhulln} which is also a list. For example \texttt{FA} returns the generalised \texttt{area} and \texttt{vol}ume. Confusingly in 2D the generalised area is the length of the perimeter, and the generalised volume is the area. <<>>= ps <-matrix(rnorm(30), , 2) ch <- convhulln(ps, options="FA") print(ch$area) print(ch$vol) @ A \texttt{convhulln} object can also be plotted. <>= plot(ch) @ We can also find the normals to the ``facets'' of the convex hull: <<>>= ch <- convhulln(ps, options="n") head(ch$normals) @ Here the first two columns and the $x$ and $y$ direction of the normal, and the third column defines the position at which the face intersects that normal. \subsection{Testing if points are inside a convex hull with \texttt{inhulln}} \label{qhull-eg:sec:testing-if-points} The function \texttt{inhulln} can be used to test if points are inside a convex hull. Here the function \texttt{rbox} is a handy way to create points at random locations. <>= tp <- rbox(n=200, D=2, B=4) in_ch <- inhulln(ch, tp) plot(tp[!in_ch,], col="gray") points(tp[in_ch,], col="red") plot(ch, add=TRUE) @ \section{Delaunay triangulation in 2D} \label{qhull-eg:sec:dela-triang-2d} \subsection{Calling \texttt{delaunayn} with one argument} \label{qhull-eg:sec:call-delaunayn-with} With one argument, a set of points, \texttt{delaunayn} returns the indices of the points at each vertex of each triangle in the triangulation. <>= ps <- rbox(n=10, D=2) dt <- delaunayn(ps) head(dt) trimesh(dt, ps) points(ps) @ \subsection{Calling \texttt{delaunayn} with \texttt{options}} \label{qhull-eg:sec:call-dela-with} We can supply Qhull options to \texttt{delaunayn}; in this case it returns an object of class \texttt{delaunayn} which is also a list. For example \texttt{Fa} returns the generalised \texttt{area} of each triangle. In 2D the generalised area is the actual area; in 3D it would be the volume. <<>>= dt2 <- delaunayn(ps, options="Fa") print(dt2$areas) dt2 <- delaunayn(ps, options="Fn") print(dt2$neighbours) @ \end{document} % LocalWords: Qhull convhulln ps rnorm ume inhulln rbox tp gray dt % LocalWords: delaunayn trimesh Fn geometry/vignettes/qhull-eg-002.pdf0000644000176200001440000000703313431000557016626 0ustar liggesusers%PDF-1.4 %ρ\r 1 0 obj << /CreationDate (D:20190212155337) /ModDate (D:20190212155337) /Title (R Graphics Output) /Producer (R 3.5.2) /Creator (R) >> endobj 2 0 obj << /Type /Catalog /Pages 3 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ ] /Count 0 /MediaBox [0 0 432 432] >> endobj 4 0 obj << /ProcSet [/PDF /Text] /Font <<>> /ExtGState << >> /ColorSpace << /sRGB 5 0 R >> >> endobj 5 0 obj [/ICCBased 6 0 R] endobj 6 0 obj << /Alternate /DeviceRGB /N 3 /Length 2596 /Filter /FlateDecode >> stream xwTSϽ7PkhRH H.*1 J"6DTpDQ2(C"QDqpId߼y͛~kg}ֺLX Xňg` lpBF|،l *?Y"1P\8=W%Oɘ4M0J"Y2Vs,[|e92<se'9`2&ctI@o|N6(.sSdl-c(2-yH_/XZ.$&\SM07#1ؙYrfYym";8980m-m(]v^DW~ emi]P`/u}q|^R,g+\Kk)/C_|Rax8t1C^7nfzDp 柇u$/ED˦L L[B@ٹЖX!@~(* {d+} G͋љς}WL$cGD2QZ4 E@@A(q`1D `'u46ptc48.`R0) @Rt CXCP%CBH@Rf[(t CQhz#0 Zl`O828.p|O×X ?:0FBx$ !i@ڐH[EE1PL ⢖V6QP>U(j MFkt,:.FW8c1L&ӎ9ƌaX: rbl1 {{{;}#tp8_\8"Ey.,X%%Gщ1-9ҀKl.oo/O$&'=JvMޞxǥ{=Vs\x ‰N柜>ucKz=s/ol|ϝ?y ^d]ps~:;/;]7|WpQoH!ɻVsnYs}ҽ~4] =>=:`;cܱ'?e~!ańD#G&}'/?^xI֓?+\wx20;5\ӯ_etWf^Qs-mw3+?~O~endstream endobj 7 0 obj << /Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences [ 45/minus 96/quoteleft 144/dotlessi /grave /acute /circumflex /tilde /macron /breve /dotaccent /dieresis /.notdef /ring /cedilla /.notdef /hungarumlaut /ogonek /caron /space] >> endobj xref 0 8 0000000000 65535 f 0000000021 00000 n 0000000163 00000 n 0000000212 00000 n 0000000289 00000 n 0000000390 00000 n 0000000423 00000 n 0000003118 00000 n trailer << /Size 8 /Info 1 0 R /Root 2 0 R >> startxref 3375 %%EOF geometry/vignettes/.install_extras0000644000176200001440000000004113450441027017145 0ustar liggesusersLICENSE-NOTES MODIFIED.txt qhull$geometry/vignettes/LICENSE-NOTES0000644000176200001440000000040613431000556016005 0ustar liggesusersAll files in src/ apart from Rgeometry.h, Rconvhulln.c, Rdelaunayn.c Rtsearchn.c, Rtsearch_orig.c, Rtsearch.cpp, RcppExports.cpp, Rinhulln.c, Rhalfspacen.c and geometry_init.c are taken from or based on Qhull (http://www.qhull.org/), and covered by COPYING.txt. geometry/NEWS0000644000176200001440000004352414367304544012626 0ustar liggesusersCHANGES IN VERSION 0.4.7 - Released 2023/02/03 BUG FIXES * Fix for Issue #67: Compiled code should not call sprintf() https://github.com/davidcsterratt/geometry/issues/67 Thanks to the CRAN maintainers for reporting this. Update uses snprintf() function. * PR #64: Replace rgl.* with *3d https://github.com/davidcsterratt/geometry/pull/64 Thanks to Duncan Murdoch for replacing rgl.* functions with *3d functions * Issue #60: Negative neighbour IDs reported by delaunayn https://github.com/davidcsterratt/geometry/issues/60 Provide documentation to explain that the Qhull "Fn" option returns negative IDs. API CHANGE * Issue #29: Make behaviour of convhulln() and delaunayn() more consistent https://github.com/davidcsterratt/geometry/issues/29 Issue message to indicate full option to delaunayn is dprecated NEW FEATURE * Issue #29: Make behaviour of convhulln() and delaunayn() more consistent https://github.com/davidcsterratt/geometry/issues/29 plot.delaunayn() to plot triangulations CHANGES IN VERSION 0.4.6.1 - Released 2022/07/04 BUG FIXES * Fix for Issue #66: Replace the tripack package with the interp package https://github.com/davidcsterratt/geometry/issues/66 The Thanks to the CRAN maintainers for replacing references to the suggested tripack package with interp CHANGES IN VERSION 0.4.6 - Released 2022/04/18 BUG FIXES * PR #49: Update tests for testthat v3 compatibility https://github.com/davidcsterratt/geometry/pull/49 Thanks to Hugo Gruson for providing the pull request * PR #50: Reorder documentation lines https://github.com/davidcsterratt/geometry/pull/50 Thanks to Hugo Gruson for providing the pull request * PR #54: Fix typo https://github.com/davidcsterratt/geometry/pull/54 Thanks to Hugh Gruson for providing the pull request OTHER CHANGES * Fix for Issue #61: Legacy macros need replaced https://github.com/davidcsterratt/geometry/issues/61 Thanks to the Brian Ripley and the CRAN team for the alert CHANGES IN VERSION 0.4.5 - Released 2019/12/02 BUG FIX * Fix for Issue #47: Install failure with gcc 10 https://github.com/davidcsterratt/geometry/issues/47 Thanks to Brian Ripley for pointing this out CHANGES IN VERSION 0.4.4 - Released 2019/08/27 BUG FIX * Fix for Issue #45: LTO error https://github.com/davidcsterratt/retistruct/issues/45 Thanks to Brian Ripley for pointing this out and providing the fix CHANGES IN VERSION 0.4.3 - Released 2019/08/21 BUG FIXES * Fix for Issue #39: tsearchn() returns NA for some points within triangle https://github.com/davidcsterratt/geometry/issues/39 tsearchn() failed (=return NA) for some points that lie within the triangulation. Thanks to Jan Lause (https://github.com/jlause) for reporting the bug and Jean-Romain Roussel for providing the fix. * Fix for Issue #40: memory misuse in src/Rtsearchn.c https://github.com/davidcsterratt/geometry/issues/40 In src/Rtsearchn.c 'idmap' was allocated with max_facet_id elements but then a value to idmap[max_facet_id] was assigned, which was one element beyond the end of that allocation, potentially clobbering the memory allocation system. Thanks to Bill Dunlap (https://github.com/BillDunlap) for reporting this, and the suggested fix. * Fix for Issue #44: R_tmpnam()/free() causes crash in TERR https://github.com/davidcsterratt/geometry/issues/44 Because TERR (https://docs.tibco.com/products/tibco-enterprise-runtime-for-r) is not compiled with gcc (it uses either the Intel or Microsoft C++ compiler) on Windows, calling free() in a package's DLL for memory allocated with malloc() in TERR's own DLL causes a crash. The R API function R_tmpnam() causes this problem in the geometry package. Thanks to Bill Dunlap (https://github.com/BillDunlap) for reporting this, and the suggested fix. ENHANCEMENT * Fix for Issue #42: Improve error reporting from Qhull https://github.com/davidcsterratt/geometry/issues/42 Full error from Qhull is now returned, rather than just the first two lines. CHANGES IN VERSION 0.4.2 - Released 2019/07/05 BUG FIX * Fix for Issue #35: intersectn() reports zero volume for some overlapping 4D hulls https://github.com/davidcsterratt/geometry/issues/35 intersectn() failed on a particular example. Modifying the scale option given to lpSolve::lp in feasible.point() has fixed this problem. feasible.point() also now returns the lpSolve::lp() error message when it fails, rather than failing silently as before. Thanks to Maxime Logez for reporting the bug and Sam Buttrey, lpSolve maintainer, for insights into lpSolve. CHANGES IN VERSION 0.4.1 - Released 2019/03/27 BUG FIXES * Fix for Issue #34: intersectn() fails when overlapping region is not in positive quadrant https://github.com/davidcsterratt/geometry/issues/34 intersectn() would return no intersecting hull for input hulls whose overlapping region did not overlap with the postive quadrant (i.e. the region where all coordinates are non-negative). Thanks to Maxime Logez for reporting the bug. * Fix for Issue #33: nonsensical results from intersectn() in 4D https://github.com/davidcsterratt/geometry/issues/33 intersectn() gave demonstrably wrong results in some cases. This was Because of some heuristic (non-Qhull) code in halfspacen which was designed to get round cases in which Qhull fails with options "Tv" because the hull is very narrow. The solution is to remove the heuristic method, and also to fix Issue #34 (above). Before Issue #34 was fixed, it was found that adding the "QJ" option could also help, and this is now suggested in the error message about narrow hulls. Thanks to Maxime Logez for reporting the bug and supplying a reproducible example, now included in tests. CHANGES IN VERSION 0.4.0 - Released 2019/02/18 LICENSE CHANGES * Following requests by the CRAN maintainers and disucssion on the r-package-devel email list, the entire package is now released under GPL (>= 3). https://github.com/davidcsterratt/geometry/issues/27 API CHANGES * Default options to delaunayn have been changed https://github.com/davidcsterratt/geometry/issues/4 The Qc and Qz or Qx options have been added as defaults, making the default options "Qbb Qc Qt Qz" for 3 dimensions or less and "Qbb Qc Qt Qx" for 4 or more dimensions. This brings the R implementation into line with Octave, and very similar to matlab and scipy.spatial.Delaunayn . * New argument "output.options" for convhulln() and delaunayn(). https://github.com/davidcsterratt/geometry/issues/29 Qhull options which affect output should be put in this string, e.g. delaunayn(ps, output.options="Fn Fa") * The "full" option to delaunayn() is deprecated https://github.com/davidcsterratt/geometry/issues/29 Use output.options=TRUE instead. In geometry 0.4.0 using "full" does not give a message or warning; this will happen in future versions * distmesh2d() has a plot option https://github.com/davidcsterratt/geometry/issues/15 The new plot option (TRUE by default) allows plotting to during mesh generation to be suspended, which is useful for running tests. The use of cat() has been replaced by message(), meaning messages can be supressed, for example during tests. * extprod3d() has a "drop" option https://github.com/davidcsterratt/geometry/issues/16 Setting drop=FALSE guarantees that the output of extprod3d() is an array rather than a vector, even when presented with two vectors. NEW FUNCTIONS * New function inhulln() to test if points are in a hull https://github.com/davidcsterratt/geometry/issues/1 * New function halfspacen() to compute intersection points of halfspaces about an interior point https://github.com/davidcsterratt/geometry/issues/25 * New function intersectn() to compute intersection of convex hulls of two sets of points https://github.com/davidcsterratt/geometry/issues/26 * Functions cart2sph, sph2cart, cart2pol, pol2cart ported from Octave https://github.com/davidcsterratt/geometry/issues/14 NEW FEATURES * convhulln() can produce non-triangulated output https://github.com/davidcsterratt/geometry/issues/22 Thanks to Pavlo Mozharovskyi for pushing code to achive this * delaunayn() and convhulln() throw Qhull error messages when Qhull fails https://github.com/davidcsterratt/geometry/issues/28 BUG FIXES * Fix for Issue #6072: distmesh2d - convergence problem (https://r-forge.r-project.org/tracker/?func=detail&atid=4552&aid=6072&group_id=1149) Some parts of the original Matlab implementation of distmesh2d were omitted during the translation to R. The effect is not obvious on some problems, except for long convergence times due to excessive iterations - some do not converge at all. Graham Griffiths made changes to the code which appears to have fixed the problem as example problems now appear to run significantly faster - even faster than those I have run using Matlab. Graham's example code is now included in a test. Thanks to Graham Griffiths for reporting this problem and supplying the fix. * Fix for Issue #30: Move qhull docs to vignettes https://github.com/davidcsterratt/geometry/issues/30 This is a change in response to the way R deals with inst/doc, which broke the included Qhull docs. CODE IMPROVEMENTS * The 2D tsearch C code has been replaced by a much more efficient QuadTree algorithm written in C++ by Jean-Romain Roussel. The speedup with uniformly distribued mesh points and search points is of the order of 40x. Many thanks to Jean-Romain for the contribution. https://github.com/davidcsterratt/geometry/issues/9 https://github.com/davidcsterratt/geometry/pull/8 * The new reentrant Qhull library (current version v7.2.1) is used, as a precursor to allowing Qhull objects to be returned and operated on https://github.com/davidcsterratt/geometry/issues/3 * convhulln() now returns a pointer to the qhull object representing the hull. https://github.com/davidcsterratt/geometry/issues/2 This is used in the inhull() implementation * delaunayn() now returns a pointer to the qhull object representing the triangulation https://github.com/davidcsterratt/geometry/issues/7 * tsearchn() can take an delaunay object, which should give fast performance https://github.com/davidcsterratt/geometry/issues/6 NOTE: This feature is experimental and has only been tested on 3D triangulations * Test added for polyarea() Thanks to Toby Hocking for suggesting adding one CHANGES IN VERSION 0.3-6 - Released 2015/09/04 CODE IMPROVEMENTS * As per CRAN policies, the title in the DESCRIPTION file is now in title case. BUG FIXES * Fix for Issue #5738: segfault when run from directory lacking write permission (https://r-forge.r-project.org/tracker/index.php?func=detail&aid=5738&group_id=1149&atid=4552) Using "." as a temporary directory can cause problems if it is not writeable. Now the geometry package uses tempdir(), which R should guarantee to exist. Thanks to Laura Riggi for reporting this problem. * Put Robert B. Gramacy back in the author list, after he was accidentally omitted when changing to the Authors@R format on 2014/10/29 (version 0.3-5). Thanks to Robert B. Gramacy for reporting this, and apologies for the error. CHANGES IN VERSION 0.3-5 - Released 2014/11/06 API CHANGES * The default Qhull option to the delaunayn() function is now "Qt" rather than "QJ" and degenerate (zero-area) simplices are removed from the triangulation, so that all simplicies are non-degenerate. This is a follow-up to issue #2009: delaunayn can return degenerate simplicies by default (https://r-forge.r-project.org/tracker/index.php?func=detail&aid=1993&group_id=1149&atid=4552). Thanks to Rolf Turner for alerting me to the issue. * delaunayn() now throws an error if the number of points is less than the number of dimensions + 1. FEATURES * delaunayn() with the "full" option also returns the areas of facets. This is a byproduct of the API change above. BUG FIXES * Fix for Issue #2789: inline functions min/max generate linker error (https://r-forge.r-project.org/tracker/index.php?func=detail&aid=2789&group_id=1149&atid=4552) Thanks to Bernd Bischl for reporting the problem and suggesting the fix. * Partial Fix for Issue #5738: segfault when run from directory lacking write permission (https://r-forge.r-project.org/tracker/index.php?func=detail&aid=5738&group_id=1149&atid=4552) An error is now thrown, rather than a segfault. The underlying problem still needs to be dealth with. DOCUMENTATION IMPROVEMENTS * The meanings of "area" and "vol" in the convhulln return value have been clarified. Thanks to Michael Cole for raising the issue. * The documentation of "cart2bary" and "bary2cart" has been corrected and improved. Thanks to Francisco Mendoza Torres for raising the issue. * HTML documentation has been for validation errors with HTML tidy. Thanks to Kurt Hornik and the CRAN maintainers for alerting me to the problem. CODE IMPROVEMENTS * Tests are now all done using the testthat framework. * There is now a test to make sure output to file works. Thanks to Brian Ripley for identifying the problem while testing the pacakge for CRAN. CHANGES IN VERSION 0.3-4 - Released 2014/03/04 BUG FIXES * Fix for Issue #5406: Compilation flags in Makevars (https://r-forge.r-project.org/tracker/index.php?func=detail&aid=5406&group_id=1149&atid=4552) Thanks to Kurt Hornik and the CRAN maintainers for alerting me to the problem. CHANGES IN VERSION 0.3-3 - Released 2013/04/18 BUG FIXES * The License has been changed to GPL (>= 3) + file LICENSE and the LICENSE file made clearer. Thanks to Kurt Hornik and the CRAN maintainers for alerting me to the problem and helping with a solution. * Fix for Issue #1993: Error in solve.default in tsearchn (https://r-forge.r-project.org/tracker/index.php?func=detail&aid=1993&group_id=1149&atid=4552) Thanks to Bill Denny for reporting this and suggesting a way forward with a fix. Thanks to John Drake for alerting me to the recurrence of the problem and Jon Clayden on the R-devel list for pointing out the correct way of testing for ill-conditioned matrices. * Fix for Issue #2009: delaunayn can return degenerate simplicies by default (https://r-forge.r-project.org/tracker/index.php?func=detail&aid=1993&group_id=1149&atid=4552) This is a follow-on of #1993, so thanks to Bill Denny for highlighting the issue. * Fix for Issue #2007: Qt option given to Qhull (https://r-forge.r-project.org/tracker/index.php?func=detail&aid=2007&group_id=1149&atid=4552) * The use of the multicore package in tests has been replaced by use of the parallel package. Thanks to Kurk Hornik for pointing this out. * Removed non-API call to R_TempDir. Thanks to Kurk Hornik for pointing this out. CHANGES IN VERSION 0.3-2 - Released 2012/05/12 BUG FIXES * Fix for Issue #1968: convhulln crash with T0 option (https://r-forge.r-project.org/tracker/index.php?func=detail&aid=1968&group_id=1149&atid=4552) Thanks to Thomas Kirschstein, Oskar Knapik and Uwe Ligges for reporting this and to Duncan Murdoch for help on the r-devel mailing list. * Fix for Issue #1983: NA in an input to delaunayn crashes R (https://r-forge.r-project.org/tracker/index.php?func=detail&aid=1983&group_id=1149&atid=4552) Thanks to Bill Denny for reporting this and suggestiong a way forward with a fix. CHANGES IN VERSION 0.3-1 - Released 2012/05/01 BUG FIXES * Fix for Issue #1964: R crashes when using convhulln/delaunayn (https://r-forge.r-project.org/tracker/index.php?func=detail&aid=1964&group_id=1149&atid=4552). Thanks to Thomas Kirschstein, Oskar Knapik and Paolo Piras for reporting this. CHANGES IN VERSION 0.3-0 - Released 2012/04/17 NEW FEATURES * New function dot() to compute dot product. Syntax is shared with Octave dot() function. * New function polyarea() to compute area of polygons. Syntax is shared with Octave polyarea() function. BUG FIXES * Fix for Issue #1578: convhulln crashes when running 2 instances of R in the same directory: output to qhull_out.txt removed (https://r-forge.r-project.org/tracker/index.php?func=detail&aid=1578&group_id=1149&atid=4552). Thanks to Francois Rousset and Bernd Bischl for reporting this. * Fix for Issue #1923: Compilation on Windows 64 doesn't work (https://r-forge.r-project.org/tracker/index.php?func=detail&aid=1923&group_id=1149&atid=4552) * Fix for Issue #1924: Silent operation is not supported (https://r-forge.r-project.org/tracker/index.php?func=detail&aid=1924&group_id=1149&atid=4552) Thanks to Raphael Leblois for reporting this. * Fix for Issue #1930: delaunayn should always return a matrix (https://r-forge.r-project.org/tracker/index.php?func=detail&aid=1930&group_id=1149&atid=4552) * Fix for Issue #1915: geometry segfaults during tests (https://r-forge.r-project.org/tracker/index.php?func=detail&aid=1915&group_id=1149&atid=4552) * Fix for Issue #1947: convhulln crash when Qhull error reported (https://r-forge.r-project.org/tracker/index.php?func=detail&aid=1947&group_id=1149&atid=4552). Thanks to Petr Savicky for reporting this. OTHER CHANGES * Updated to qhull version 2011.2. * The default qhull option "QJ" for delaunayn() has been replaced by "Qt". One or other option is always provided to the qhull code (see https://r-forge.r-project.org/tracker/index.php?func=detail&aid=1915&group_id=1149&atid=4552). * Documentation improvements. * Added some tests. CHANGES IN VERSION 0.2-0 - Released 2011/09/09 NEW FEATURES * The tsearch and tsearchn functions have been ported from Octave. These functions have the same syntax as the Octave versions, though it also possible to get tsearch to output barycentric coordinates. tsearch only works for 2D simplexes (i.e. triangles), but is fast because it is written in C. By default, tsearchn uses the fast tsearch function when presented with 2D data. NEW MAINTAINER * David Sterratt has taken over from Bobby Gramacy. Thank-you to Bobby for his work on the package. geometry/R/0000755000176200001440000000000014366511252012313 5ustar liggesusersgeometry/R/inhulln.R0000644000176200001440000000261514227215261014110 0ustar liggesusers##' Test if points lie in convex hull ##' ##' Tests if a set of points lies within a convex hull, returning a ##' boolean vector in which each element is \code{TRUE} if the ##' corresponding point lies within the hull and \code{FALSE} if it ##' lies outwith the hull or on one of its facets. ##' ##' @param ch Convex hull produced using \code{\link{convhulln}} ##' @param p An \eqn{M}-by-\eqn{N} matrix of points to test. The rows ##' of \code{p} represent \eqn{M} points in \eqn{N}-dimensional ##' space. ##' @return A boolean vector with \eqn{M} elements ##' @author David Sterratt ##' @note \code{inhulln} was introduced in geometry 0.4.0, and is ##' still under development. It is worth checking results for ##' unexpected behaviour. ##' @seealso \code{\link{convhulln}}, \code{point.in.polygon} in \pkg{sp} ##' @export ##' @examples ##' p <- cbind(c(-1, -1, 1), c(-1, 1, -1)) ##' ch <- convhulln(p) ##' ## First point should be in the hull; last two outside ##' inhulln(ch, rbind(c(-0.5, -0.5), ##' c( 1 , 1), ##' c(10 , 0))) ##' ##' ## Test hypercube ##' p <- rbox(D=4, B=1) ##' ch <- convhulln(p) ##' tp <- cbind(seq(-1.9, 1.9, by=0.2), 0, 0, 0) ##' pin <- inhulln(ch, tp) ##' ## Points on x-axis should be in box only betw,een -1 and 1 ##' pin == (tp[,1] < 1 & tp[,1] > -1) inhulln <- function(ch, p) { return(.Call("C_inhulln", ch, p, PACKAGE="geometry")) } geometry/R/tetramesh.R0000644000176200001440000000356414366321774014452 0ustar liggesusers##' \code{tetramesh(T, X, col)} uses the \link[rgl]{rgl} package to ##' display the tetrahedrons defined in the m-by-4 matrix T as mesh. ##' Each row of \code{T} specifies a tetrahedron by giving the 4 ##' indices of its points in \code{X}. ##' ##' @title Render tetrahedron mesh (3D) ##' @param T T is a \code{m}-by-3 matrix in trimesh and \code{m}-by-4 in ##' tetramesh. A row of \code{T} contains indices into \code{X} of the vertices ##' of a triangle/tetrahedron. \code{T} is usually the output of delaunayn. ##' @param X X is an n-by-2/n-by-3 matrix. The rows of X represent \code{n} ##' points in 2D/3D space. ##' @param col The tetrahedron colour. See rgl documentation for details. ##' @param clear Should the current rendering device be cleared? ##' @param \dots Parameters to the rendering device. See the \link[rgl]{rgl} ##' package. ##' @author Raoul Grasman ##' @seealso \code{\link{trimesh}}, \code{\link[rgl]{rgl}}, \code{\link{delaunayn}}, ##' \code{\link{convhulln}}, \code{\link{surf.tri}} ##' @keywords hplot ##' @examples ##' \dontrun{ ##' # example delaunayn ##' d = c(-1,1) ##' pc = as.matrix(rbind(expand.grid(d,d,d),0)) ##' tc = delaunayn(pc) ##' ##' # example tetramesh ##' clr = rep(1,3) %o% (1:nrow(tc)+1) ##' rgl::view3d(60,fov=20) ##' rgl::light3d(270,60) ##' tetramesh(tc,pc,alpha=0.7,col=clr) ##' } ##' @export tetramesh <- function (T, X, col = grDevices::heat.colors(nrow(T)), clear = TRUE, ...) { if(requireNamespace("rgl") == FALSE) stop("the rgl package is required for tetramesh") if (!is.numeric(T) | !is.numeric(T)) stop("`T' and `X' should both be numeric.") if (ncol(T) != 4) stop("Expect first arg `T' to have 4 columns.") if (ncol(X) != 3) stop("Expect second arg `X' to have 3 columns.") t = t(rbind(T[, -1], T[, -2], T[, -3], T[, -4])) if (clear) rgl::clear3d() rgl::triangles3d(X[t, 1], X[t, 2], X[t, 3], col = col, ...) } geometry/R/cart2pol.R0000644000176200001440000000557113433536400014170 0ustar liggesusers## Copyright (C) 2000-2017 Kai Habel ## ## This file is part of Octave. ## ## Octave is free software; you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## Octave is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with Octave; see the file COPYING. If not, see ## . ## This file has been adapted for R by David C Sterratt ##' Transform Cartesian coordinates to polar or cylindrical coordinates. ##' ##' The inputs \code{x}, \code{y} (, and \code{z}) must be the same shape, or ##' scalar. If called with a single matrix argument then each row of \code{C} ##' represents the Cartesian coordinate (\code{x}, \code{y} (, \code{z})). ##' ##' @param x x-coordinates or matrix with three columns ##' @param y y-coordinates (optional, if \code{x}) is a matrix ##' @param z z-coordinates (optional, if \code{x}) is a matrix ##' @return A matrix \code{P} where each row represents one ##' polar/(cylindrical) coordinate (\code{theta}, \code{r}, (, ##' \code{z})). ##' @seealso \code{\link{pol2cart}}, \code{\link{cart2sph}}, ##' \code{\link{sph2cart}} ##' @author Kai Habel ##' @author David Sterratt ##' @export cart2pol <- function(x, y=NULL, z=NULL) { if (is.null(y) & is.null(z)) { if (!(is.numeric(x))) { stop("input must be matrix with 2 or 3 columns") } if (!(is.numeric (x) & is.matrix (x) & (ncol(x) == 2 | ncol(x) == 3))) { stop("matrix input must have 2 or 3 columns [X, Y (, Z)]"); } if (ncol(x) == 3) { z <- x[,3] } y <- x[,2] x <- x[,1] } else { if (is.null(z)) { if (!is.numeric (x) | !is.numeric (y)) { stop("X, Y must be numeric arrays of the same size, or scalar") } if ( !((length(x) == length(y)) | (length(x) == 1) | (length(y) == 1))) { stop("X, Y must be numeric arrays of the same size, or scalar") } } else { if (! is.numeric (x) | ! is.numeric (y) | ! is.numeric (z)) { stop("X, Y, Z must be numeric arrays of the same size, or scalar") } if ( !(((length(x) == length(y)) | (length(x) == 1) | (length(y) == 1)) & ((length(x) == length(z)) | (length(x) == 1) | (length(z) == 1)) & ((length(y) == length(z)) | (length(y) == 1) | (length(z) == 1)))) { stop("x, y, z must be matrices of the same size, or scalar") } } } theta <- atan2 (y, x) r <- sqrt(x^2 + y^2) if (is.null(z)) { return(cbind(theta=theta, r=r)) } return(cbind(theta=theta, r=r, z=z)) } geometry/R/rbox.R0000644000176200001440000000134213432317337013411 0ustar liggesusers##' Default is corners of a hypercube. ##' @title Generate various point distributions ##' @param n number of random points in hypercube ##' @param D number of dimensions of hypercube ##' @param B bounding box coordinate - faces will be \code{-B} and \code{B} from origin ##' @param C add a unit hypercube to the output - faces will be \code{-C} and \code{C} from origin ##' @return Matrix of points ##' @author David Sterratt ##' @export rbox <- function(n=3000, D=3, B=0.5, C=NA) { P <- matrix(0, 0, D) if (!is.na(C)) { P <- rbind(P, as.matrix(do.call(expand.grid, rep(list(c(-C, C)), D)))) } if (n > 0) { P <- rbind(P, matrix(stats::runif(n=n*D, min=-B, max=B), n, D)) } return(P) } geometry/R/distmeshnd.R0000644000176200001440000001706314366273067014620 0ustar liggesusers##' A simple mesh generator for non-convex regions in n-D space ##' ##' An unstructured simplex requires a choice of mesh points (vertex nodes) and ##' a triangulation. This is a simple and short algorithm that improves the ##' quality of a mesh by relocating the mesh points according to a relaxation ##' scheme of forces in a truss structure. The topology of the truss is reset ##' using Delaunay triangulation. A (sufficiently smooth) user supplied signed ##' distance function (\code{fd}) indicates if a given node is inside or ##' outside the region. Points outside the region are projected back to the ##' boundary. ##' ##' This is an implementation of original Matlab software of Per-Olof Persson. ##' ##' Excerpt (modified) from the reference below: ##' ##' \sQuote{The algorithm is based on a mechanical analogy between a triangular ##' mesh and a n-D truss structure. In the physical model, the edges of the ##' Delaunay triangles of a set of points correspond to bars of a truss. Each ##' bar has a force-displacement relationship \eqn{f(\ell, \ell_{0})}{F(L,L0)} ##' depending on its current length \eqn{\ell}{L} and its unextended length ##' \eqn{\ell_{0}}{L0}.} ##' ##' \sQuote{External forces on the structure come at the boundaries, on which ##' external forces have normal orientations. These external forces are just ##' large enough to prevent nodes from moving outside the boundary. The ##' position of the nodes are the unknowns, and are found by solving for a ##' static force equilibrium. The hope is that (when \code{fh = function(p) ##' return(rep(1,nrow(p)))}), the lengths of all the bars at equilibrium will ##' be nearly equal, giving a well-shaped triangular mesh.} ##' ##' See the references below for all details. Also, see the comments in the ##' source file of \code{distmesh2d}. ##' ##' @param fdist Vectorized signed distance function, for example ##' \code{\link{mesh.dsphere}}, accepting an \code{m}-by-\code{n} ##' matrix, where \code{m} is arbitrary, as the first argument. ##' @param fh Vectorized function, for example \code{\link{mesh.hunif}}, ##' that returns desired edge length as a function of position. ##' Accepts an \code{m}-by-\code{n} matrix, where \code{n} is ##' arbitrary, as its first argument. ##' @param h Initial distance between mesh nodes. ##' @param box \code{2}-by-\code{n} matrix that specifies the bounding box. ##' (See \link{distmesh2d} for an example.) ##' @param pfix \code{nfix}-by-2 matrix with fixed node positions. ##' @param \dots parameters that are passed to \code{fdist} and \code{fh} ##' @param ptol Algorithm stops when all node movements are smaller than ##' \code{dptol} ##' @param ttol Controls how far the points can move (relatively) before a ##' retriangulation with \code{\link{delaunayn}}. ##' @param deltat Size of the time step in Euler's method. ##' @param geps Tolerance in the geometry evaluations. ##' @param deps Stepsize \eqn{\Delta x} in numerical derivative computation for ##' distance function. ##' @return \code{m}-by-\code{n} matrix with node positions. ##' @section Wishlist : \itemize{ \item Implement in C/Fortran \item Translate ##' other functions of the Matlab package } ##' @author Raoul Grasman; translated from original Matlab sources of Per-Olof ##' Persson. ##' @seealso \code{\link{distmesh2d}}, \code{\link[interp]{tri.mesh}}, ##' \code{\link{delaunayn}}, \code{\link{mesh.dsphere}}, ##' \code{\link{mesh.hunif}},\cr \code{\link{mesh.diff}}, ##' \code{\link{mesh.union}}, \code{\link{mesh.intersect}} ##' @references \url{http://persson.berkeley.edu/distmesh/} ##' ##' \cite{P.-O. Persson, G. Strang, A Simple Mesh Generator in MATLAB. SIAM ##' Review, Volume 46 (2), pp. 329-345, June 2004} ##' @keywords math optimize dplot graphs ##' @examples ##' ##' \dontrun{ ##' # examples distmeshnd ##' require(rgl) ##' ##' fd = function(p, ...) sqrt((p^2)%*%c(1,1,1)) - 1 ##' # also predefined as `mesh.dsphere' ##' fh = function(p,...) rep(1,nrow(p)) ##' # also predefined as `mesh.hunif' ##' bbox = matrix(c(-1,1),2,3) ##' p = distmeshnd(fd,fh,0.2,bbox, maxiter=100) ##' # this may take a while: ##' # press Esc to get result of current iteration ##' } ##' ##' @export "distmeshnd" <- function (fdist, fh, h, box, pfix = array(dim = c(0, ncol(box))), ..., ptol = 0.001, ttol = 0.1, deltat = 0.1, geps = 0.1 * h, deps = sqrt(.Machine$double.eps) * h) { # %DISTMESHND N-D Mesh Generator using Distance Functions. dim = ncol(as.matrix(box)) L0mult = 1 + 0.4/2^(dim - 1) rownorm2 = function(x) drop(sqrt((x^2) %*% rep(1, ncol(x)))) # %1. Create initial distribution in bounding box if (dim == 1) { p = seq(box[1], box[2], by = h) } else { cbox = lapply(1:dim, function(ii) seq(box[1, ii], box[2, ii], by = h)) p = do.call("expand.grid", cbox) p = as.matrix(p) } # %2. Remove points outside the region, apply the rejection method p = p[fdist(p, ...) < geps, ] r0 = fh(p, ...) p = rbind(pfix, p[stats::runif(nrow(p)) < min(r0)^dim/r0^dim, ]) N = nrow(p) if (N <= dim + 1) stop("Not enough starting points inside boundary (is h0 too large?).") on.exit(return(invisible(p))) message("Press esc if the mesh seems fine but the algorithm hasn't converged.") utils::flush.console() count = 0 p0 = 1/.Machine$double.eps # mimick Matlab call ``localpairs=nchoosek(1:dim+1,2)'': localpairs = as.matrix(expand.grid(1:(dim + 1), 1:(dim + 1))) localpairs = localpairs[lower.tri(matrix(TRUE, dim + 1, dim + 1)), 2:1] while (TRUE) { if (max(rownorm2(p - p0)) > ttol * h) { # %3. Retriangulation by Delaunay: p0 = p t = delaunayn(p) pmid = matrix(0, nrow(t), dim) for (ii in 1:(dim + 1)) pmid = pmid + p[t[, ii], ]/(dim + 1) t = t[fdist(pmid, ...) < (-geps), ] pair = array(dim = c(0, 2)) for (ii in 1:nrow(localpairs)) { pair = rbind(pair, t[, localpairs[ii, ]]) } # %4. Describe each edge by a unique pair of nodes pair = Unique(pair, TRUE); # base-function `unique' is way too slow if (dim == 2) { trimesh(t, p[, 1:2]) } else if (dim == 3) { if (count%%5 == 0) { tetramesh(t, p) } } else { message("Retriangulation #", 15) utils::flush.console() } count = count + 1 } bars = p[pair[, 1], ] - p[pair[, 2], ] L = rownorm2(bars) L0 = fh((p[pair[, 1], ] + p[pair[, 2], ])/2, ...) L0 = L0 * L0mult * (sum(L^dim)/sum(L0^dim))^(1/dim) F = L0 - L F[F < 0] = 0 Fbar = cbind(bars, -bars) * matrix(F/L, nrow = nrow(bars), ncol = 2 * dim) ii = pair[, t(matrix(1:2, 2, dim))] jj = rep(1, nrow(pair)) %o% c(1:dim, 1:dim) s = c(Fbar) ns = length(s) dp = matrix(0, N, dim) dp[1:(dim * N)] = rowsum(s, ii[1:ns] + ns * (jj[1:ns] - 1)) if (nrow(pfix) > 0) dp[1:nrow(pfix), ] = 0 p = p + deltat * dp d = fdist(p, ...) ix = d > 0 gradd = matrix(0, sum(ix), dim) for (ii in 1:dim) { a = rep(0, dim) a[ii] = deps d1x = fdist(p[ix, ] + rep(1, sum(ix)) %o% a, ...) gradd[, ii] = (d1x - d[ix])/deps } p[ix, ] = p[ix, ] - (d[ix] %o% rep(1, dim)) * gradd maxdp = max(deltat * rownorm2(dp[d < (-geps), ])) if (maxdp < ptol * h) break } } geometry/R/mesh.intersect.R0000644000176200001440000000014513432317337015372 0ustar liggesusers"mesh.intersect" <- function (p, regionA, regionB, ...) matmax(regionA(p, ...), regionB(p, ...)) geometry/R/mesh.drectangle.R0000644000176200001440000000327013432317337015504 0ustar liggesusers##' Signed distance from points \code{p} to boundary of rectangle to ##' allow easy definition of regions in \code{\link{distmesh2d}}. ##' ##' @title Rectangle distance function ##' @param p A matrix with 2 columns, each row representing a point in ##' the plane. ##' @param x1 lower left corner of rectangle ##' @param y1 lower left corner of rectangle ##' @param x2 upper right corner of rectangle ##' @param y2 upper right corner of rectangle ##' @param \dots additional arguments (not used) ##' @return a vector of length \code{nrow(p)} containing the signed distances ##' @author Raoul Grasman; translated from original Matlab sources of Per-Olof ##' Persson. ##' @seealso \code{\link{distmesh2d}}, \code{\link{mesh.drectangle}}, ##' \code{\link{mesh.diff}}, \code{\link{mesh.intersect}}, ##' \code{\link{mesh.union}} ##' @references \url{http://persson.berkeley.edu/distmesh/} ##' ##' \cite{P.-O. Persson, G. Strang, A Simple Mesh Generator in MATLAB. SIAM ##' Review, Volume 46 (2), pp. 329-345, June 2004} ##' @keywords arith math ##' @examples ##' example(distmesh2d) ##' @export mesh.drectangle <- function (p, x1 = -1/2, y1 = -1/2, x2 = 1/2, y2 = 1/2, ...) { if (!is.matrix(p)) p = t(as.matrix(p)) d1 = y1 - p[, 2] d2 = -y2 + p[, 2] d3 = x1 - p[, 1] d4 = -x2 + p[, 1] d5 = sqrt(d1^2 + d3^2) d6 = sqrt(d1^2 + d4^2) d7 = sqrt(d2^2 + d3^2) d8 = sqrt(d2^2 + d4^2) matmin = function(...) apply(cbind(...), 1, min) d = -matmin(matmin(matmin(-d1, -d2), -d3), -d4) ix = d1 > 0 & d3 > 0 d[ix] = d5[ix] ix = d1 > 0 & d4 > 0 d[ix] = d6[ix] ix = d2 > 0 & d3 > 0 d[ix] = d7[ix] ix = d2 > 0 & d4 > 0 d[ix] = d8[ix] return(d) } geometry/R/mesh.hunif.R0000644000176200001440000000136313432317337014506 0ustar liggesusers##' Uniform desired edge length function of position to allow easy ##' definition of regions when passed as the \code{fh} argument of ##' \code{\link{distmesh2d}} or \code{\link{distmeshnd}}. ##' ##' @title Uniform desired edge length ##' @param p A \code{n}-by-\code{m} matrix, each row representing a ##' point in an \code{m}-dimensional space. ##' @param ... additional arguments (not used) ##' @return Vector of ones of length \code{n}. ##' @author Raoul Grasman; translated from original Matlab sources of Per-Olof ##' Persson. ##' @seealso \code{\link{distmesh2d}} and \code{\link{distmeshnd}}. ##' @export mesh.hunif <- function (p, ...) { if (!is.matrix(p)) stop("Input `p' should be matrix.") return(rep(1, nrow(p))) } geometry/R/qhull-options.R0000644000176200001440000000175514366510624015266 0ustar liggesusersqhull.options <- function(options, output.options, supported_output.options, full=FALSE) { if (full) { if (!is.null(output.options)) { stop("full and output.options should not be specified together") } output.options = TRUE ## Enable message in 0.4.1 ## Turn to warning in 0.4.7 message("delaunayn: \"full\" option is deprecated; adding \"Fa\" and \"Fn\" to options. Please update your code to use \"output.options=TRUE\" or set \"output.options\" to a string containing desired QHull options.") } if (is.null(output.options)) { output.options <- "" } if (is.logical(output.options)) { if (output.options) { output.options <- paste(supported_output.options, collapse=" ") } else { output.options <- "" } } if (!is.character(output.options)) { stop("output.options must be a string, logical or NULL") } ## Input sanitisation options <- paste(options, output.options, collapse=" ") return(options) } geometry/R/mesh.union.R0000644000176200001440000000014113432317337014516 0ustar liggesusers"mesh.union" <- function (p, regionA, regionB, ...) matmin(regionA(p, ...), regionB(p, ...)) geometry/R/polyarea.R0000644000176200001440000000375413511174150014254 0ustar liggesusers## Copyright (C) 1999, 2006, 2007, 2009 David M. Doolin ## bugs and limitations: ## Probably ought to be an optional check to make sure that ## traversing the vertices doesn't make any sides cross ## (Is simple closed curve the technical definition of this?). ##' Determines area of a polygon by triangle method. The variables ##' \code{x} and \code{y} define the vertex pairs, and must therefore ##' have the same shape. They can be either vectors or arrays. If ##' they are arrays then the columns of \code{x} and \code{y} are ##' treated separately and an area returned for each. ##' ##' If the optional \code{dim} argument is given, then \code{polyarea} ##' works along this dimension of the arrays \code{x} and \code{y}. ##' ##' @title Determines area of a polygon by triangle method. ##' @param x X coordinates of vertices. ##' @param y Y coordinates of vertices. ##' @param d Dimension of array to work along. ##' @return Area(s) of polygon(s). ##' @author David Sterratt based on the octave sources by David M. Doolin ##' @export ##' @importFrom magic shift ashift ##' @examples ##' x <- c(1, 1, 3, 3, 1) ##' y <- c(1, 3, 3, 1, 1) ##' polyarea(x, y) ##' polyarea(cbind(x, x), cbind(y, y)) ## c(4, 4) ##' polyarea(cbind(x, x), cbind(y, y), 1) ## c(4, 4) ##' polyarea(rbind(x, x), rbind(y, y), 2) ## c(4, 4) polyarea <- function(x, y, d=1) { if (is.vector(x) & is.vector(y)) { if (length(x) == length(y)) { a <- abs(sum(x*(shift(y, -1) - shift(y, 1))))/2 } else { stop("x and y must have the same shape") } } else { if (is.array(x) & is.array(y)) { if (length(dim(x)) != 2) { stop("Arrays must have two dimensions.") } if (all(dim(x) == dim(y))) { v <- c(0, 0) v[d] <- 1 a <- abs(apply(x*(ashift(y, -v) - ashift(y, v)), 3 - d, sum))/2 } else { stop("x and y must have the same shape") } } else { stop("x and y must be of same type") } } names(a) <- NULL return(a) } geometry/R/halfspacen.R0000644000176200001440000000700513532164477014553 0ustar liggesusers##' Compute halfspace intersection about a point ##' ##' @param p An \eqn{M}-by-\eqn{N+1} matrix. Each row of \code{p} ##' represents a halfspace by a \eqn{N}-dimensional normal to a ##' hyperplane and the offset of the hyperplane. ##' @param fp A \dQuote{feasible} point that is within the space ##' contained within all the halfspaces. ##' @param options String containing extra options, separated by ##' spaces, for the underlying Qhull command; see Qhull ##' documentation at \url{../doc/qhull/html/qhalf.html}. ##' ##' @return A \eqn{N}-column matrix containing the intersection ##' points of the hyperplanes \url{../doc/qhull/html/qhalf.html}. ##' ##' @author David Sterratt ##' @note \code{halfspacen} was introduced in geometry 0.4.0, and is ##' still under development. It is worth checking results for ##' unexpected behaviour. ##' @seealso \code{\link{convhulln}} ##' @references \cite{Barber, C.B., Dobkin, D.P., and Huhdanpaa, H.T., ##' \dQuote{The Quickhull algorithm for convex hulls,} \emph{ACM ##' Trans. on Mathematical Software,} Dec 1996.} ##' ##' \url{http://www.qhull.org} ##' @examples ##' p <- rbox(0, C=0.5) # Generate points on a unit cube centered around the origin ##' ch <- convhulln(p, "n") # Generate convex hull, including normals to facets, with "n" option ##' # Intersections of half planes ##' # These points should be the same as the orginal points ##' pn <- halfspacen(ch$normals, c(0, 0, 0)) ##' ##' @export ##' @useDynLib geometry halfspacen <- function (p, fp, options = "Tv") { tmp_stdout <- tempfile("Rf") tmp_stderr <- tempfile("Rf") on.exit(c(tmp_stdout, tmp_stderr)) ## Input sanitisation options <- paste(options, collapse=" ") ## Coerce the input to be matrix if (is.data.frame(p)) { p <- as.matrix(p) } ## Make sure we have real-valued input storage.mode(p) <- "double" ## We need to check for NAs in the input, as these will crash the C ## code. if (any(is.na(p))) { stop("The first argument should not contain any NAs") } ## Check dimensions if (ncol(p) - 1 != length(as.vector(fp))) { stop("Dimension of hyperspace is ", ncol(p) - 1, " but dimension of fixed point is ", length(as.vector(fp))) } ## In geometry 0.4.0, we tried to get around halspacen fails because ## of similar hyperplanes, by removing the most similar ones (i.e. ## those that had very acute angles to one another). However, this ## was ugly and turned out to unreliable , so it has been removed in ## geometry 0.4.1 and above. Users are recommended to supply the ## QJ option in ## The fixed point is passed as an option out <- tryCatch(.Call("C_halfspacen", p, as.character(paste(options, paste0("H",paste(fp, collapse=",")))), tmp_stdout, tmp_stderr, PACKAGE="geometry"), error=function(e) { if (grepl("^Received error code 2 from qhull.", e$message)) { e$message <- paste(e$message, "\nTry calling halfspacen with options=\"Tv QJ\"") } return(e) }) if (inherits(out, "error")) { stop(out$message) } return(out) } ## If there is an error, it could be because of two very similar halfspaces. ## n1 = ch1$normals[1,1:3] ## n2 = ch2$normals[1,1:3] ## d1 = ch1$normals[1,4] ## d2 = ch2$normals[1,4] ## solve(rbind(n1, n2, extprod3d(n1, n2)), c(d1, d2, 0)) ## sqrt(sum(solve(rbind(n1, n2, extprod3d(n1, n2)), c(-d1, -d2, 0))^2)) ## dot(n1+ n2, extprod3d(n1, n2)) geometry/R/matmax.R0000644000176200001440000000246313432317337013733 0ustar liggesusers##' Row-wise matrix functions ##' ##' Compute maximum or minimum of each row, or sort each row of a matrix, or a ##' set of (equal length) vectors. ##' ##' ##' @aliases matmax matmin matsort matorder ##' @param \dots A numeric matrix or a set of numeric vectors (that are ##' column-wise bind together into a matrix with \code{cbind}). ##' @return \code{matmin} and \code{matmax} return a vector of length ##' \code{nrow(cbind(...))}. \code{matsort} returns a matrix of dimension ##' \code{dim(cbind(...))} with in each row of \code{cbind(...)} sorted. ##' \code{matsort(x)} is a lot faster than, e.g., \code{t(apply(x,1,sort))}, ##' if \code{x} is tall (i.e., \code{nrow(x)}>>\code{ncol(x)} and ##' \code{ncol(x)}<30. If \code{ncol(x)}>30 then \code{matsort} simply calls ##' `\code{t(apply(x,1,sort))}'. \code{matorder} returns a permutation which ##' rearranges its first argument into ascending order, breaking ties by ##' further arguments. ##' @author Raoul Grasman ##' @keywords array arith ##' @examples ##' ##' example(Unique) ##' ##' @export matmax matmin matsort matorder "matmax" <- function (...) { x = cbind(...) if(!is.numeric(x)) stop("Input should by numeric.") if (!is.matrix(drop(x))) x = t(x) x[1:nrow(x) + nrow(x) * (max.col(x) - 1)] } geometry/R/mesh.dsphere.R0000644000176200001440000000202413432317337015022 0ustar liggesusers##' Signed distance from points \code{p} to boundary of sphere to ##' allow easy definition of regions in \code{\link{distmeshnd}}. ##' ##' @title Sphere distance function ##' @param p A matrix with 2 columns (3 in \code{mesh.dsphere}), each row ##' representing a point in the plane. ##' @param radius radius of sphere ##' @param ... additional arguments (not used) ##' @return A vector of length \code{nrow(p)} containing the signed ##' distances to the sphere ##' @author Raoul Grasman; translated from original Matlab sources of Per-Olof ##' Persson. ##' @seealso \code{\link{distmeshnd}} ##' @references \url{http://persson.berkeley.edu/distmesh/} ##' ##' \cite{P.-O. Persson, G. Strang, A Simple Mesh Generator in MATLAB. SIAM ##' Review, Volume 46 (2), pp. 329-345, June 2004} ##' @keywords arith math ##' @examples ##' ##' example(distmeshnd) ##' @export "mesh.dsphere" <- function (p, radius = 1, ...) { if (!is.matrix(p)) p = t(as.matrix(p)) sqrt((p^2) %*% rep(1, ncol(p))) - radius } geometry/R/intersectn.R0000644000176200001440000002632713476414562014635 0ustar liggesusers##' Compute convex hull of intersection of two sets of points ##' @param ps1 First set of points ##' @param ps2 Second set of points ##' @param tol Tolerance used to determine if a feasible point lies ##' within the convex hulls of both points and to round off the ##' points generated by the halfspace intersection, which sometimes ##' produces points very close together. ##' @param return.chs If \code{TRUE} (default) return the convex hulls ##' of the first and second sets of points, as well as the convex ##' hull of the intersection. ##' @param options Options passed to \code{\link{halfspacen}}. By ##' default this is \code{Tv}. ##' @param fp Coordinates of feasible point, i.e. a point known to lie ##' in the hulls of \code{ps1} and \code{ps2}. The feasible point is ##' required for \code{\link{halfspacen}} to find the intersection. ##' \code{intersectn} tries to find the feasible point automatically ##' using the linear program in \code{\link{feasible.point}}, but ##' currently the linear program fails on some examples where there ##' is an obvious solution. This option overrides the automatic ##' search for a feasible point ##' @param autoscale \emph{Experimental in v0.4.2} Automatically scale ##' the points to lie in a sensible numeric range. May help to ##' correct some numerical issues. ##' @return List containing named elements: \code{ch1}, the convex ##' hull of the first set of points, with volumes, areas and normals ##' (see \code{\link{convhulln}}; \code{ch2}, the convex hull of the ##' first set of points, with volumes, areas and normals; \code{ps}, ##' the intersection points of convex hulls \code{ch1} and ##' \code{ch2}; and \code{ch}, the convex hull of the intersection ##' points, with volumes, areas and normals. ##' @export ##' @examples ##' # Two overlapping boxes ##' ps1 <- rbox(0, C=0.5) ##' ps2 <- rbox(0, C=0.5) + 0.5 ##' out <- intersectn(ps1, ps2) ##' message("Volume of 1st convex hull: ", out$ch1$vol) ##' message("Volume of 2nd convex hull: ", out$ch2$vol) ##' message("Volume of intersection convex hull: ", out$ch$vol) ##' @author David Sterratt ##' @note \code{intersectn} was introduced in geometry 0.4.0, and is ##' still under development. It is worth checking results for ##' unexpected behaviour. ##' @seealso \code{\link{convhulln}}, \code{\link{halfspacen}}, ##' \code{\link{inhulln}}, \code{\link{feasible.point}} ##' @importFrom utils packageDescription intersectn <- function(ps1, ps2, tol=0, return.chs=TRUE, options="Tv", fp=NULL, autoscale=FALSE) { distinct <- any(apply(ps1, 2, min) > apply(ps2, 2, max)) || any(apply(ps1, 2, max) < apply(ps2, 2, min)) if (distinct & !return.chs) { return(list(ch=list(vol=0))) } ch1 <- convhulln(ps1, "n FA") ch2 <- convhulln(ps2, "n FA") if (distinct) { return(list(ch1=ch1, ch2=ch2, ch=list(vol=0))) } ch1s <- ch1 ch2s <- ch2 if (autoscale) { pmean <- colMeans(rbind(ps1, ps2)) ch1s <- convhulln(t(t(ps1) - pmean), "n FA") ch2s <- convhulln(t(t(ps2) - pmean), "n FA") if (!is.null(fp)) { fp <- fp - pmean } } ## Find feasible point in which points could overlap if (is.null(fp)) { fp <-tryCatch(feasible.point(ch1s, ch2s, tol=tol), error=function(e){ stop("feasible.point() failed with error ", e$message, "\n", "If you can find a feasible point (i.e. point that lies in both hulls)\n", "for this input, supply this with the \"fp\" option.\n", "Otherwise, report bug, including inputs to intersectn() at\n", utils::packageDescription("geometry", fields="BugReports"), "\n", "or to ", utils::packageDescription("geometry", fields="Maintainer")) }) if (all(is.na(fp))) { if (return.chs) { return(list(ch1=ch1, ch2=ch2, ch=list(vol=0))) } return(list(ch=list(vol=0))) } } else { ## fp supplied if (!is.numeric(fp)) { stop("fp should be numeric") } if (length(fp) != ncol(ps1)) { stop("fp should have same dimension as ps1 and ps2") } } ## Find intersections of halfspaces about feasible point. Catch error ## (code QH6023) when fixed point is not in intersection, due to ## precision issue. ps <- tryCatch(halfspacen(rbind(ch1s$normals, ch2s$normals), fp, options=options), error=function(e) { if (grepl("QH6023", e$message)) { return(NA) } errmess <- e$message if (!grepl("QJ", options)) { errmess <- paste(errmess, "\nTry calling intersectn with options=\"Tv QJ\"") } stop(errmess) }) if (all(is.na(ps)) || is.null(ps)) { if (return.chs) { return(list(ch1=ch1, ch2=ch2, ch=list(vol=0))) } return(list(ch=list(vol=0))) } if (autoscale) { ps <- t(t(ps) + pmean) } ## Occasionally the halfspace creates points very close together. We ## can impose a tolerance to merge them if (tol != 0) { ps <- round(ps, ceiling(-log10(abs(tol/2)))) ps <- ps[!duplicated(ps),] } ch <- convhulln(ps, "n FA") ## Check for gross volume errors if ((ch$vol > ch1$vol * (1 + 1E-4))) { warning("Volume of final intersection hull is bigger than first of the original hulls\n", "ch1 vol = ", ch1$vol, "\n", "ch vol = ", ch$vol, "\n", "Returning ch1") ch <- ch1 } if ((ch$vol > ch2$vol * (1 + 1E-4))) { warning("Volume of final intersection hull is bigger than first of the original hulls\n", "ch2 vol = ", ch2$vol, "\n", "ch vol = ", ch$vol, "\n", "Returning ch2") ch <- ch2 } if (return.chs) { out <- list(ch1=ch1, ch2=ch2, ps=ps, ch=ch) class(out) <- "intersectn" return(out) } return(list(ch=ch)) } ##' Find point in intersection of convex hulls ##' ##' Find point that lies somewhere in interesction of two convex ##' hulls. If such a point does not exist, return \code{NA}. The ##' feasible point is found using a linear program similar to the one ##' suggested at \url{../doc/qhull/html/qhalf.html#notes} ##' ##' @param ch1 First convex hull with normals ##' @param ch2 Second convex hull with normals ##' @param tol The point must be at least this far within the facets ##' of both convex hulls ##' @export feasible.point <- function(ch1, ch2, tol=0) { N <- ncol(ch1$p) # Number of dimensions M <- nrow(ch1$normals) + nrow(ch2$normals) # Total number of normals ## Find the point that bounds all points from below. Becuase ## lpSolve::lp() implicitly gives solutions in the positive ## quadrant, we need to subtract p0 from the search point, and then ## add it to the optimised solution. This will ensure that solutions ## not in the positive quadrant are found. p0 <- apply(rbind(ch1$p, ch2$p), 2, min) objective.in <- c(rep(0, N), 1) const.mat <- rbind(cbind(ch1$normals[,-(N + 1)], 1), cbind(ch2$normals[,-(N + 1)], 1), c(rep(0, N), -1)) ## p0 is incorporated into the matrix here const.rhs <- -c(const.mat[1:M, 1:N] %*% cbind(p0) + c(ch1$normals[,N + 1], ch2$normals[,N + 1]), tol) const.dir <- c(rep("<", length(const.rhs))) ## Scaling: The scale option of lpSolve::lp() determines options ## used for scaling, and is crucial to avoid errors in some edge ## cases. For list of options, see: ## http://lpsolve.sourceforge.net/5.1/set_scaling.htm ## http://lpsolve.sourceforge.net/5.1/scaling.htm ## See also https://github.com/davidcsterratt/geometry/issues/35 ## ## After testing on 10,000+ examples, it appears that to get ## maxiumum coverage, in some cases multiple combinations of options ## need to be tried. This code cycles through options, starting with ## the combinations most likely to work. ## DYNUPDATE == 0 may also work, but DYNUPDATE == 1 may work better ## for some 4D examples DYNUPDATE <- 1 ## Both options may help for (POWER2 in 0:1) { ## Both options may help for (QUADRATIC in 0:1) { ## 1 (SCALE_EXTREME) and 3 (SCALE_EXTREME) didn't work well in ## tests ## ## 4 (SCALE_GEOMETRIC) on one example (included in tests) caused ## some sort of infinite loop or race condition leading to the ## process up 100% ## ## 2 (SCALE_RANGE) and 7 (SCALE_CURTISREID) seem to work OK for ## most cases. SCALE_CURTISREID (with the 5.6.13 version of ## lpSolve, at least) has caused some sort of infinite loop or ## race condition leading to the process up 100% on 2 out of ## millions of intersections computed on some processors, hence ## it is put as the second option. for (MAIN in c(2, 7)) { ## Both options may help for (LOGARITHMIC in 0:1) { ## Equilibriate seemed to help for some cases, but caused a ## crash in the example of inst/extdata EQUILIBRIATE <- 0 scale <- MAIN + QUADRATIC * 8 + LOGARITHMIC * 16 + POWER2 * 32 + EQUILIBRIATE * 64 + DYNUPDATE * 256 opt <- lpSolve::lp(direction = "max", objective.in, const.mat, const.dir, const.rhs, scale=scale) ## See http://lpsolve.sourceforge.net/5.5/solve.htm for status codes ## Infeasible solution if (opt$status == 2) return(NA) ## Optimal if (opt$status == 0) return(opt$solution[1:N] + p0) } } } } ## Debugging output if (!is.null(getOption("geometry.debug"))) { opt <- linprog::writeMps("feasible-point.mps", cvec=objective.in, bvec=const.rhs, Amat=const.mat, "Feasible point") writeLines(gsub("_ ", "_", readLines("feasible-point-tmp.mps")), "feasible-point.mps") message("Debugging output saved to feasible-point.mps\n", "Test on command line by running\n", " lp_solve -max -fmps feasible-point.mps") } stop("lpSolve::lp() returned error code ", opt$status, "\n", "See http://lpsolve.sourceforge.net/5.5/solve.htm for explanation of errors.\n", "Rescaling your problem may help") } ##' @method plot intersectn ##' @export plot.intersectn <- function(x, y, ...) { args <- list(...) add <- FALSE if ("add" %in% names(args)) { add <- args$add args$add <- NULL } xlim <- ylim <- NULL if ("xlim" %in% names(args)) { xlim <- args$xlim args$xlim <- NULL } if ("ylim" %in% names(args)) { ylim <- args$ylim args$xlim <- NULL } if (ncol(x$p) == 2) { if (!add) { p <- rbind(x$ch1$p, x$ch2$p) if (is.null(xlim)) xlim <- range(p[,1]) if (is.null(ylim)) ylim <- range(p[,2]) plot.new() do.call(plot.window, c(list(xlim=xlim, ylim=ylim), args)) } plot(x$ch1, add=TRUE, lty=2) plot(x$ch2, add=TRUE, lty=2) plot(x$ch, add=TRUE, lwd=2) } } geometry/R/dotprod.R0000644000176200001440000000224613432317337014116 0ustar liggesusers##' If \code{x} and \code{y} are matrices, calculate the dot-product ##' along the first non-singleton dimension. If the optional argument ##' \code{d} is given, calculate the dot-product along this ##' dimension. ##' ##' @title Compute the dot product of two vectors ##' @param x Matrix of vectors ##' @param y Matrix of vectors ##' @param d Dimension along which to calculate the dot product ##' @return Vector with length of \code{d}th dimension ##' @author David Sterratt ##' @keywords arith math array ##' @export dot <- function(x, y, d=NULL) { if (is.vector(x)) x <- matrix(x, ncol = 1) if (is.vector(y)) y <- matrix(y, ncol = 1) ndim <- length(dim(x)) ## Determine dimension along which to sum if (is.null(d)) { di <- which(dim(x) > 1) if (length(di == 0)) { d <- 1 } else { d <- di[1] } } ## Check size of d if (d > ndim) { stop("d is larger than the number of dimensions of the data") } ## Use rowSums and colSums as they are more efficient than apply if (ndim == 2) { if (d == 2) { return(rowSums(x*y)) } if (d == 1) { return(colSums(x*y)) } } return(apply(x*y, d, sum)) } geometry/R/RcppExports.R0000644000176200001440000000047714366313450014737 0ustar liggesusers# Generated by using Rcpp::compileAttributes() -> do not edit by hand # Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 #' @importFrom Rcpp sourceCpp C_tsearch <- function(x, y, elem, xi, yi, bary = FALSE, eps = 1.0e-12) { .Call('_geometry_C_tsearch', PACKAGE = 'geometry', x, y, elem, xi, yi, bary, eps) } geometry/R/matmin.R0000644000176200001440000000033113432317337013721 0ustar liggesusers"matmin" <- function (...) { x = cbind(...) if(!is.numeric(x)) stop("Input should by numeric.") if (!is.matrix(drop(x))) x = t(x) x[1:nrow(x) + nrow(x) * (max.col(-x) - 1)] } geometry/R/convhulln.R0000644000176200001440000002064514366321774014465 0ustar liggesusers##' Compute smallest convex hull that encloses a set of points ##' ##' Returns information about the smallest convex complex of a set of ##' input points in \eqn{N}-dimensional space (the convex hull of the ##' points). By default, indices to points forming the facets of the ##' hull are returned; optionally normals to the facets and the ##' generalised surface area and volume can be returned. This function ##' interfaces the \href{http://www.qhull.org}{Qhull} library. ##' ##' @param p An \eqn{M}-by-\eqn{N} matrix. The rows of \code{p} ##' represent \eqn{M} points in \eqn{N}-dimensional space. ##' ##' @param options String containing extra options for the underlying ##' Qhull command; see details below and Qhull documentation at ##' \url{../doc/qhull/html/qconvex.html#synopsis}. ##' ##' @param output.options String containing Qhull options to generate ##' extra output. Currently \code{n} (normals) and \code{FA} ##' (generalised areas and volumes) are supported; see ##' \sQuote{Value} for details. If \code{output.options} is ##' \code{TRUE}, select all supported options. ##' ##' @param return.non.triangulated.facets logical defining whether the ##' output facets should be triangulated; \code{FALSE} by default. ##' ##' @return By default (\code{return.non.triangulated.facets} is ##' \code{FALSE}), return an \eqn{M}-by-\eqn{N} matrix in which each ##' row contains the indices of the points in \code{p} forming an ##' \eqn{N-1}-dimensional facet. e.g In 3 dimensions, there are 3 ##' indices in each row describing the vertices of 2-dimensional ##' triangles. ##' ##' If \code{return.non.triangulated.facets} is \code{TRUE} then the ##' number of columns equals the maximum number of vertices in a ##' facet, and each row defines a polygon corresponding to a facet ##' of the convex hull with its vertices followed by \code{NA}s ##' until the end of the row. ##' ##' If the \code{output.options} or \code{options} argument contains ##' \code{FA} or \code{n}, return a list with class \code{convhulln} ##' comprising the named elements: ##' \describe{ ##' \item{\code{p}}{The points passed to \code{convnhulln}} ##' \item{\code{hull}}{The convex hull, represented as a matrix indexing \code{p}, as ##' described above} ##' \item{\code{area}}{If \code{FA} is specified, the generalised area of ##' the hull. This is the surface area of a 3D hull or the length of ##' the perimeter of a 2D hull. ##' See \url{../doc/qhull/html/qh-optf.html#FA}.} ##' \item{\code{vol}}{If \code{FA} is specified, the generalised volume of ##' the hull. This is volume of a 3D hull or the area of a 2D hull. ##' See \url{../doc/qhull/html/qh-optf.html#FA}. } ##' \item{\code{normals}}{If \code{n} is specified, this is a matrix ##' hyperplane normals with offsets. See \url{../doc/qhull/html/qh-opto.html#n}.} ##' } ##' ##' @note This function was originally a port of the ##' \href{https://octave.org/}{Octave} convhulln function written ##' by Kai Habel. ##' ##' See further notes in \code{\link{delaunayn}}. ##' ##' @author Raoul Grasman, Robert B. Gramacy, Pavlo Mozharovskyi and ##' David Sterratt \email{david.c.sterratt@@ed.ac.uk} ##' @seealso \code{\link{intersectn}}, \code{\link{delaunayn}}, ##' \code{\link{surf.tri}}, \code{\link[interp]{convex.hull}} ##' @references \cite{Barber, C.B., Dobkin, D.P., and Huhdanpaa, H.T., ##' \dQuote{The Quickhull algorithm for convex hulls,} \emph{ACM ##' Trans. on Mathematical Software,} Dec 1996.} ##' ##' \url{http://www.qhull.org} ##' @keywords math dplot graphs ##' @examples ##' ## Points in a sphere ##' ps <- matrix(rnorm(3000), ncol=3) ##' ps <- sqrt(3)*ps/drop(sqrt((ps^2) %*% rep(1, 3))) ##' ts.surf <- t(convhulln(ps)) # see the qhull documentations for the options ##' \dontrun{ ##' rgl::triangles3d(ps[ts.surf,1],ps[ts.surf,2],ps[ts.surf,3],col="blue",alpha=.2) ##' for(i in 1:(8*360)) rgl::view3d(i/8) ##' } ##' ##' ## Square ##' pq <- rbox(0, C=0.5, D=2) ##' # Return indices only ##' convhulln(pq) ##' # Return convhulln object with normals, generalised area and volume ##' ch <- convhulln(pq, output.options=TRUE) ##' plot(ch) ##' ##' ## Cube ##' pc <- rbox(0, C=0.5, D=3) ##' # Return indices of triangles on surface ##' convhulln(pc) ##' # Return indices of squares on surface ##' convhulln(pc, return.non.triangulated.facets=TRUE) ##' @export ##' @useDynLib geometry convhulln <- function (p, options = "Tv", output.options=NULL, return.non.triangulated.facets = FALSE) { tmp_stdout <- tempfile("Rf") tmp_stderr <- tempfile("Rf") on.exit(unlink(c(tmp_stdout, tmp_stderr))) ## Combine and check options options <- tryCatch(qhull.options(options, output.options, supported_output.options <- c("n", "FA")), error=function(e) {stop(e)}) ## Coerce the input to be matrix if (is.data.frame(p)) { p <- as.matrix(p) } ## Make sure we have real-valued input storage.mode(p) <- "double" ## We need to check for NAs in the input, as these will crash the C ## code. if (any(is.na(p))) { stop("The first argument should not contain any NAs") } if (!return.non.triangulated.facets){ ## It is essential that delaunayn is called with either the QJ or Qt ## option. Otherwise it may return a non-triangulated structure, i.e ## one with more than dim+1 points per structure, where dim is the ## dimension in which the points p reside. if (!grepl("Qt", options) & !grepl("QJ", options)) { options <- paste(options, "Qt") } } out <- .Call("C_convhulln", p, as.character(options), as.integer(return.non.triangulated.facets), tmp_stdout, tmp_stderr, PACKAGE="geometry") # Remove NULL elements out[which(sapply(out, is.null))] <- NULL if (is.null(out$area) & is.null(out$vol) & is.null(out$normals)) { attr(out$hull, "convhulln") <- attr(out, "convhulln") return(out$hull) } class(out) <- "convhulln" out$p <- p return(out) } ##' @importFrom graphics plot ##' @method plot convhulln ##' @export plot.convhulln <- function(x, y, ...) { if (ncol(x$p) < 2 || ncol(x$p) > 3) stop("Only 2D and 3D convhullns can be plotted") args <- list(...) add <- FALSE if ("add" %in% names(args)) { add <- args$add args$add <- NULL } if (ncol(x$p) == 2) { if (!add) { plot(x$p[,1], x$p[,2], ...) } m <- x$hull p <- x$p do.call(segments, c(list(p[m[,1],1],p[m[,1],2],p[m[,2],1],p[m[,2],2]), args)) } if (ncol(x$p) == 3) { if(requireNamespace("rgl") == FALSE) stop("The rgl package is required for tetramesh") if (!add) rgl::clear3d() if (ncol(x$hull) == 3) { do.call(rgl::triangles3d, c(list(x$p[t(x$hull),1], x$p[t(x$hull),2], x$p[t(x$hull),3]), args)) } else { stop("At present only convhullns with triangulated facets can be plotted") } } } ##' Convert convhulln object to RGL mesh ##' ##' @param x \code{\link{convhulln}} object ##' @param ... Arguments to \code{\link[rgl]{qmesh3d}} or ##' \code{\link[rgl]{tmesh3d}} ##' @return \code{\link[rgl]{mesh3d}} object, which can be displayed ##' in RGL with \code{\link[rgl]{dot3d}}, \code{\link[rgl]{wire3d}} ##' or \code{\link[rgl]{shade3d}} ##' ##' @seealso \code{\link[rgl]{as.mesh3d}} ##' @export to.mesh3d <- function(x, ...) UseMethod("to.mesh3d") ##' @importFrom graphics plot ##' @method to.mesh3d convhulln ##' @export to.mesh3d.convhulln <- function(x, ...) { if(requireNamespace("rgl") == FALSE) stop("The rgl package is required for as.mesh.convhulln") if (ncol(x$p) != 3) { stop("Only convex hulls of points in 3D can be turned into meshes") } if (ncol(x$hull) == 4) { stop("At present only convhulls with triangulated facets can be converted to mesh3d") ## return(rgl::qmesh3d(t(x$p), t(x$hull), homogeneous=FALSE, ...)) } if (ncol(x$hull) == 3) { return(rgl::tmesh3d(t(x$p), t(x$hull), homogeneous=FALSE, ...)) } stop("Facets of hull must be triangles or quadrilaterals") } ## LocalWords: dQuote Qhull param itemize Kai Habel delaunayn Pavlo ## LocalWords: Grasman Gramacy Mozharovskyi Sterratt seealso tri ps ## LocalWords: interp distmesh intersectn Dobkin Huhdanpaa emph Tv ## LocalWords: Quickhull ACM dplot convhulln qhull rnorm ncol sqrt ## LocalWords: dontrun useDynLib tmp_stdout tmp_stderr tempdir sanitisation NAs na ## LocalWords: QJ grepl importFrom convhulls args requireNamespace ## LocalWords: rgl tetramesh eqn href sQuote hyperplane rbox ## LocalWords: convulln convhullns geometry/R/trimesh.R0000644000176200001440000000326713432317337014122 0ustar liggesusers##' \code{trimesh(T, p)} displays the triangles defined in the m-by-3 ##' matrix \code{T} and points \code{p} as a mesh. Each row of ##' \code{T} specifies a triangle by giving the 3 indices of its ##' points in \code{X}. ##' ##' @title Display triangles mesh (2D) ##' @param T T is a \code{m}-by-3 matrix. A row of \code{T} contains ##' indices into \code{X} of the vertices of a triangle. \code{T} is ##' usually the output of \code{\link{delaunayn}}. ##' @param p A vector or a matrix. ##' @param p2 if \code{p} is not a matrix \code{p} and \code{p2} are bind to a ##' matrix with \code{cbind}. ##' @param add Add to existing plot in current active device? ##' @param axis Draw axes? ##' @param boxed Plot box? ##' @param \dots Parameters to the rendering device. See the \link[rgl]{rgl} ##' package. ##' @author Raoul Grasman ##' @seealso \code{\link{tetramesh}}, \code{\link[rgl]{rgl}}, ##' \code{\link{delaunayn}}, \code{\link{convhulln}}, ##' \code{\link{surf.tri}} ##' @keywords hplot ##' @examples ##' #example trimesh ##' p = cbind(x=rnorm(30), y=rnorm(30)) ##' tt = delaunayn(p) ##' trimesh(tt,p) ##' @export ##' @importFrom graphics box plot.new plot.window segments trimesh <- function(T, p, p2, add=FALSE, axis=FALSE, boxed=FALSE, ...){ if(!is.matrix(p)){ p = cbind(p,p2) # automatically generates error if p2 not present } xlim = range(p[,1]) ylim = range(p[,2]) if(!add){ plot.new() plot.window(xlim, ylim, ...) } if(boxed){ box() } if(axis) { axis(1) axis(2) } m = rbind(T[,-1], T[, -2], T[, -3]) segments(p[m[,1],1],p[m[,1],2],p[m[,2],1],p[m[,2],2], ...) return(invisible(list(T = T, p = p))) } geometry/R/cart2sph.R0000644000176200001440000000511713717744722014201 0ustar liggesusers## Copyright (C) 2000, 2001, 2002, 2004, 2005, 2006, 2007, 2009, 2017 Kai Habel ## ## This file is part of Octave. ## ## Octave is free software; you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or (at ## your option) any later version. ## ## Octave is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with Octave; see the file COPYING. If not, see ## . ## This file has been adapted for R by David C Sterratt ##' Transform Cartesian to spherical coordinates ##' ##' If called with a single matrix argument then each row of \code{c} ##' represents the Cartesian coordinate (\code{x}, \code{y}, \code{z}). ##' ##' @param x x-coordinates or matrix with three columns ##' @param y y-coordinates (optional, if \code{x}) is a matrix ##' @param z z-coordinates (optional, if \code{x}) is a matrix ##' @return Matrix with columns: ##' \item{\code{theta}}{the angle relative to the positive x-axis} ##' \item{\code{phi}}{the angle relative to the xy-plane} ##' \item{\code{r}}{the distance to the origin \code{(0, 0, 0)}} ##' @seealso \code{\link{sph2cart}}, \code{\link{cart2pol}}, ##' \code{\link{pol2cart}} ##' @author Kai Habel ##' @author David Sterratt ##' @export cart2sph <- function(x, y=NULL, z=NULL) { if ((is.null(y) & !is.null(z)) | (is.null(z) & !is.null(y))) { stop("There should be 3 arguments (x, y, z) or one argument (x)") } if (is.null(y) & is.null(z)) { if (!(is.numeric(x))) { stop("input must be matrix with 3 columns [x, y, z]") } if (!(is.matrix(x) & ncol(x) == 3)) { stop("matrix input must have 3 columns [x, y, z]") } z <- x[,3] y <- x[,2] x <- x[,1] } else { if (!is.numeric(x) | !is.numeric(y) | !is.numeric (z)) stop("x, y, z must be numeric arrays of the same size, or scalar") if ( !(((length(x) == length(y)) | (length(x) == 1) | (length(y) == 1)) & ((length(x) == length(z)) | (length(x) == 1) | (length(z) == 1)) & ((length(y) == length(z)) | (length(y) == 1) | (length(z) == 1)))) { stop("x, y, z must be matrices of the same size, or scalar") } } theta <- atan2(y, x) phi <- atan2(z, sqrt(x^2 + y^2)) r <- sqrt(x^2 + y^2 + z^2) return(cbind(theta, phi, r)) } geometry/R/extprod3d.R0000644000176200001440000000253213433536400014350 0ustar liggesusers##' Compute external- or `cross'- product of 3D vectors. ##' ##' Computes the external product \deqn{ }{ (x2 * y3 - x3 * y2, x3 * y1 - x1 * ##' y3, x1 * y2 - x2 * y1) }\deqn{ \left(x_2 y_3 - x_3 y_2,\; x_3 y_1 - x_1 ##' y_3,\; x_1 y_2 - x_2 y_1 \right) }{ (x2 * y3 - x3 * y2, x3 * y1 - x1 * y3, ##' x1 * y2 - x2 * y1) }\deqn{ }{ (x2 * y3 - x3 * y2, x3 * y1 - x1 * y3, x1 * ##' y2 - x2 * y1) } of the 3D vectors in \bold{x} and \bold{y}. ##' ##' ##' @param x \code{n}-by-3 matrix. Each row is one \bold{x}-vector ##' @param y \code{n}-by-3 matrix. Each row is one \bold{y}-vector ##' @param drop logical. If \code{TRUE} and if the inputs are one row ##' matrices or vectors, then delete the dimensions of the array ##' returned. ##' @return If \code{n} is greater than 1 or \code{drop} is ##' \code{FALSE}, \code{n}-by-3 matrix; if \code{n} is 1 and ##' \code{drop} is \code{TRUE}, a vector of length 3. ##' @author Raoul Grasman ##' @keywords arith math array ##' @seealso \code{\link[base]{drop}} ##' @export "extprod3d" <- function (x, y, drop=TRUE) { x = matrix(x, ncol = 3) y = matrix(y, ncol = 3) z = cbind(x[, 2] * y[, 3] - x[, 3] * y[, 2], x[, 3] * y[, 1] - x[, 1] * y[, 3], x[, 1] * y[, 2] - x[, 2] * y[, 1]) if (drop) { return(drop(z)) } return(z) } geometry/R/mesh.dcircle.R0000644000176200001440000000220113432317337014772 0ustar liggesusers##' Signed distance from points \code{p} to boundary of circle to ##' allow easy definition of regions in \code{\link{distmesh2d}}. ##' ##' @title Circle distance function ##' @param p A matrix with 2 columns (3 in \code{mesh.dsphere}), each row ##' representing a point in the plane. ##' @param radius radius of circle ##' @param ... additional arguments (not used) ##' @return A vector of length \code{nrow(p)} containing the signed ##' distances to the circle ##' @author Raoul Grasman; translated from original Matlab sources of Per-Olof ##' Persson. ##' @seealso \code{\link{distmesh2d}}, \code{\link{mesh.drectangle}}, ##' \code{\link{mesh.diff}}, \code{\link{mesh.intersect}}, ##' \code{\link{mesh.union}} ##' @references \url{http://persson.berkeley.edu/distmesh/} ##' ##' \cite{P.-O. Persson, G. Strang, A Simple Mesh Generator in MATLAB. SIAM ##' Review, Volume 46 (2), pp. 329-345, June 2004} ##' @keywords arith math ##' @examples ##' ##' example(distmesh2d) ##' @export mesh.dcircle <- function (p, radius = 1, ...) { if (!is.matrix(p)) p <- t(as.matrix(p)) return(sqrt((p^2) %*% c(1, 1)) - radius) } geometry/R/entry.value.R0000644000176200001440000000403113433536400014704 0ustar liggesusers##' Retrieve or set a list of array element values ##' ##' \code{entry.value} retrieves or sets the values in an array \code{a} at the ##' positions indicated by the rows of a matrix \code{idx}. ##' ##' ##' @aliases entry.value entry.value<- ##' @param a An array. ##' @param idx Numerical matrix with the same number of columns as the number ##' of dimensions of \code{a}. Each row indices a cell in \code{a} of which ##' the value is to be retrieved or set. ##' @param value An array of length \code{nrow(idx)}. ##' @return \code{entry.value(a,idx)} returns a vector of values at the ##' indicated cells. \code{entry.value(a,idx) <- val} changes the indicated ##' cells of \code{a} to \code{val}. ##' @author Raoul Grasman ##' @keywords arith math array ##' @examples ##' ##' a = array(1:(4^4),c(4,4,4,4)) ##' entry.value(a,cbind(1:4,1:4,1:4,1:4)) ##' entry.value(a,cbind(1:4,1:4,1:4,1:4)) <- 0 ##' ##' entry.value(a, as.matrix(expand.grid(1:4,1:4,1:4,1:4))) ##' # same as `c(a[1:4,1:4,1:4,1:4])' which is same as `c(a)' ##' ##' @export entry.value entry.value<- "entry.value" <- function (a, idx) { if (!is.array(a)) stop("First argument `", deparse(substitute(a)), "' should be an array.") if (!is.matrix(idx)) stop("Second argument `", substitute(idx), "' should be a matrix.") n <- length(dim(a)) if (n != ncol(idx)) stop("Number of columns in", deparse(substitute(idx)), "is incompatible is dimension of", deparse(substitute(a))) a[(idx - 1) %*% c(1, cumprod(dim(a))[-n]) + 1] } "entry.value<-" <- function (a, idx, value) { if (!is.array(a)) stop("First argument `", deparse(substitute(a)), "' should be an array.") if (!is.matrix(idx)) stop("Second argument `", substitute(idx), "' should be a matrix.") n <- length(dim(a)) if (n != ncol(idx)) stop("Number of columns in", deparse(substitute(idx)), "is incompatible is dimension of", deparse(substitute(a))) a[(idx - 1) %*% c(1, cumprod(dim(a))[-n]) + 1] <- value return(a) } geometry/R/tsearch.R0000644000176200001440000003036614227217636014104 0ustar liggesusers##' Search for the enclosing Delaunay convex hull ##' ##' For \code{t <- delaunay(cbind(x, y))}, where \code{(x, y)} is a 2D set of ##' points, \code{tsearch(x, y, t, xi, yi)} finds the index in \code{t} ##' containing the points \code{(xi, yi)}. For points outside the convex hull ##' the index is \code{NA}. ##' ##' @param x X-coordinates of triangulation points ##' @param y Y-coordinates of triangulation points ##' @param t Triangulation, e.g. produced by \code{t <- ##' delaunayn(cbind(x, y))} ##' @param xi X-coordinates of points to test ##' @param yi Y-coordinates of points to test ##' @param bary If \code{TRUE} return barycentric coordinates as well ##' as index of triangle. ##' @param method One of \code{"quadtree"} or \code{"orig"}. The ##' Quadtree algorithm is much faster and new from version ##' 0.4.0. The \code{orig} option uses the tsearch algorithm adapted ##' from Octave code. Its use is deprecated and it may be removed ##' from a future version of the package. ##' @return If \code{bary} is \code{FALSE}, the index in \code{t} containing the points ##' \code{(xi, yi)}. For points outside the convex hull the index is \code{NA}. ##' If \code{bary} is \code{TRUE}, a list containing: ##' \describe{ ##' \item{list("idx")}{the index in \code{t} containing the points \code{(xi, yi)}} ##' \item{list("p")}{a 3-column matrix containing the barycentric coordinates with ##' respect to the enclosing triangle of each point \code{(xi, yi)}.} ##' } ##' @author Jean-Romain Roussel (Quadtree algorithm), David Sterratt (Octave-based implementation) ##' @note The original Octave function is Copyright (C) 2007-2012 ##' David Bateman ##' @seealso \code{\link{tsearchn}}, \code{\link{delaunayn}} ##' @export tsearch <- function(x, y, t, xi, yi, bary=FALSE, method="quadtree") { xtxt = deparse(substitute(x)) ytxt = deparse(substitute(y)) xitxt = deparse(substitute(xi)) yitxt = deparse(substitute(yi)) ttxt = deparse(substitute(t)) if (!is.vector(x)) {stop(paste(xtxt, "is not a vector"))} if (!is.vector(y)) {stop(paste(ytxt, "is not a vector"))} if (!is.matrix(t)) {stop(paste(ttxt, "is not a matrix"))} if (!is.vector(xi)) {stop(paste(xitxt, "is not a vector"))} if (!is.vector(yi)) {stop(paste(yitxt, "is not a vector"))} if (length(x) != length(y)) { stop(paste(xtxt, "is not same length as", ytxt)) } if (length(xi) != length(yi)) { stop(paste(xitxt, "is not same length as", yitxt)) } if (ncol(t) != 3) { stop(paste(ttxt, "does not have three columns")) } if (any(as.integer(t) != t)) { stop(paste(ttxt, "does not have integer elements")) } if (length(x) == 0) {stop(paste(xtxt, "is empty"))} if (length(y) == 0) {stop(paste(ytxt, "is empty"))} if (any(is.na(x))) {stop(paste(xtxt, "contains NAs"))} if (any(is.na(y))) {stop(paste(ytxt, "contains NAs"))} if (length(x) < 3 | length(y) < 3) { stop("A triangulation should have at least 3 points") } storage.mode(t) <- "integer" if (max(t) > length(x)) { stop(paste(ttxt, "has indexes greater than the number of points")) } if (min(t) <= 0) { stop(paste(ttxt, "has indexes which refer to non-existing points")) } if (length(xi) == 0 | length(yi) == 0) { if (!bary) return (integer(0)) else return (list(idx = integer(0), p = matrix(0,0,3))) } if (method == "quadtree") { out <- C_tsearch(x, y, t, xi, yi, bary) } else { out <- .Call("C_tsearch_orig", x, y, t, xi, yi, bary, PACKAGE="geometry") } if (bary) { names(out) <- c("idx", "p") } return(out) } ##' Search for the enclosing Delaunay convex hull ##' ##' For \code{t = delaunayn(x)}, where \code{x} is a set of points in \eqn{N} ##' dimensions, \code{tsearchn(x, t, xi)} finds the index in \code{t} ##' containing the points \code{xi}. For points outside the convex hull, ##' \code{idx} is \code{NA}. \code{tsearchn} also returns the barycentric ##' coordinates \code{p} of the enclosing triangles. ##' ##' If \code{x} is \code{NA} and the \code{t} is a ##' \code{delaunayn} object produced by ##' \code{\link{delaunayn}} with the \code{full} option, then use the ##' Qhull library to perform the search. Please note that this is ##' experimental in geometry version 0.4.0 and is only partly tested ##' for 3D hulls, and does not yet work for hulls of 4 dimensions and ##' above. ##' ##' @param x An \eqn{N}-column matrix, in which each row represents a ##' point in \eqn{N}-dimensional space. ##' @param t A matrix with \eqn{N+1} columns. A row of \code{t} ##' contains indices into \code{x} of the vertices of an ##' \eqn{N}-dimensional simplex. \code{t} is usually the output of ##' delaunayn. ##' @param xi An \eqn{M}-by-\eqn{N} matrix. The rows of \code{xi} ##' represent \eqn{M} points in \eqn{N}-dimensional space whose ##' positions in the mesh are being sought. ##' @param ... Additional arguments ##' @return A list containing: ##' \describe{ ##' \item{\code{idx}}{An \eqn{M}-long vector containing the indices ##' of the row of \code{t} in which each point in \code{xi} is found.} ##' \item{\code{p}}{An \eqn{M}-by-\eqn{N+1} matrix containing the ##' barycentric coordinates with respect to the enclosing simplex ##' of each point in \code{xi}.}} ##' @author David Sterratt ##' @note Based on the Octave function Copyright (C) 2007-2012 David ##' Bateman. ##' @seealso \code{\link{tsearch}}, \code{\link{delaunayn}} ##' @export tsearchn <- function(x, t, xi, ...) { if (any(is.na(x)) && inherits(t, "delaunayn")) { return(tsearchn_delaunayn(t, xi)) } fast <- TRUE if (!is.null(list(...)$fast) & is.logical(list(...)$fast)) fast <- list(...)$fast ## Check input if (!is.matrix(x)) {stop(paste(deparse(substitute(x)), "is not a matrix"))} if (!is.matrix(t)) {stop(paste(deparse(substitute(t)), "is not a matrix"))} if (!is.matrix(xi)) {stop(paste(deparse(substitute(xi)), "is not a matrix"))} n <- dim(x)[2] # Number of dimensions if (n==2 && fast) { return(tsearch(x[,1], x[,2], t, xi[,1], xi[,2], bary=TRUE)) } nt <- dim(t)[1] # Number of simplexes m <- dim(x)[1] # Number of points in simplex grid mi <- dim(xi)[1] # Number of points to search for ## If there are no points to search for, return an empty index ## vector and an empty coordinate matrix if (mi==0) { return(list(idx=c(), p=matrix(0, 0, n + 1))) } idx <- rep(NA, mi) p <- matrix(NA, mi, n + 1) ## Indicies of points that still need to be searched for ni <- 1:mi degenerate.simplices <- c() ## Go through each simplex in turn for (i in 1:nt) { ## Only calculate the Barycentric coordinates for points that have not ## already been found in a simplex. b <- suppressWarnings(cart2bary(x[t[i,],], xi[ni,,drop=FALSE])) if (is.null(b)) { degenerate.simplices <- c(degenerate.simplices, i) } else { ## Our points xi are in the current triangle if (all(b >= 0) && ## all (b <= 1)). However as we impose that sum(b,2) == 1 we only ## need to test all(b>=0). Note that we need to add a small margin ## for rounding errors intri <- apply(b >= -1e-12, 1, all) ## Set the simplex indicies of the points that have been found to ## this simplex idx[ni[intri]] <- i ## Set the baryocentric coordinates of the points that have been found p[ni[intri],] <- b[intri,] ## Remove these points from the search list ni <- ni[!intri] ## If there are no more points to search for, give up if (length(ni) == 0) { break } } } if (length(degenerate.simplices) > 0) { warning(paste("Degenerate simplices:", toString(degenerate.simplices))) } return(list(idx=idx, p=p)) } ##' Conversion of Cartesian to Barycentric coordinates. ##' ##' Given the Cartesian coordinates of one or more points, compute ##' the barycentric coordinates of these points with respect to a ##' simplex. ##' ##' Given a reference simplex in \eqn{N} dimensions represented by a ##' \eqn{N+1}-by-\eqn{N} matrix an arbitrary point \eqn{P} in ##' Cartesian coordinates, represented by a 1-by-\eqn{N} row vector, can be ##' written as ##' \deqn{P = \beta X} ##' where \eqn{\beta} is an \eqn{N+1} vector of the barycentric coordinates. ##' A criterion on \eqn{\beta} is that ##' \deqn{\sum_i\beta_i = 1} ##' Now partition the simplex into its first \eqn{N} rows \eqn{X_N} and ##' its \eqn{N+1}th row \eqn{X_{N+1}}. Partition the barycentric ##' coordinates into the first \eqn{N} columns \eqn{\beta_N} and the ##' \eqn{N+1}th column \eqn{\beta_{N+1}}. This allows us to write ##' \deqn{P_{N+1} - X_{N+1} = \beta_N X_N + \beta_{N+1} X_{N+1} - X_{N+1}} ##' which can be written ##' \deqn{P_{N+1} - X_{N+1} = \beta_N(X_N - 1_N X_{N+1})} ##' where \eqn{1_N} is an \eqn{N}-by-1 matrix of ones. We can then solve ##' for \eqn{\beta_N}: ##' \deqn{\beta_N = (P_{N+1} - X_{N+1})(X_N - 1_N X_{N+1})^{-1}} ##' and compute ##' \deqn{\beta_{N+1} = 1 - \sum_{i=1}^N\beta_i} ##' This can be generalised for multiple values of ##' \eqn{P}, one per row. ##' ##' @param X Reference simplex in \eqn{N} dimensions represented by a ##' \eqn{N+1}-by-\eqn{N} matrix ##' @param P \eqn{M}-by-\eqn{N} matrix in which each row is the Cartesian ##' coordinates of a point. ##' @return \eqn{M}-by-\eqn{N+1} matrix in which each row is the ##' barycentric coordinates of corresponding row of \code{P}. If the ##' simplex is degenerate a warning is issued and the function returns ##' \code{NULL}. ##' @author David Sterratt ##' @note Based on the Octave function by David Bateman. ##' @examples ##' ## Define simplex in 2D (i.e. a triangle) ##' X <- rbind(c(0, 0), ##' c(0, 1), ##' c(1, 0)) ##' ## Cartesian coordinates of points ##' P <- rbind(c(0.5, 0.5), ##' c(0.1, 0.8)) ##' ## Plot triangle and points ##' trimesh(rbind(1:3), X) ##' text(X[,1], X[,2], 1:3) # Label vertices ##' points(P) ##' cart2bary(X, P) ##' @seealso \code{\link{bary2cart}} ##' @export cart2bary <- function(X, P) { M <- nrow(P) N <- ncol(P) if (ncol(X) != N) { stop("Simplex X must have same number of columns as point matrix P") } if (nrow(X) != (N+1)) { stop("Simplex X must have N columns and N+1 rows") } X1 <- X[1:N,] - (matrix(1,N,1) %*% X[N+1,,drop=FALSE]) if (rcond(X1) < .Machine$double.eps) { warning("Degenerate simplex") return(NULL) } Beta <- (P - matrix(X[N+1,], M, N, byrow=TRUE)) %*% solve(X1) Beta <- cbind(Beta, 1 - apply(Beta, 1, sum)) return(Beta) } ##' Conversion of Barycentric to Cartesian coordinates ##' ##' Given the barycentric coordinates of one or more points with ##' respect to a simplex, compute the Cartesian coordinates of these ##' points. ##' ##' @param X Reference simplex in \eqn{N} dimensions represented by a ##' \eqn{N+1}-by-\eqn{N} matrix ##' @param Beta \eqn{M} points in barycentric coordinates with ##' respect to the simplex \code{X} represented by a ##' \eqn{M}-by-\eqn{N+1} matrix ##' @return \eqn{M}-by-\eqn{N} matrix in which each row is the ##' Cartesian coordinates of corresponding row of \code{Beta} ##' @examples ##' ## Define simplex in 2D (i.e. a triangle) ##' X <- rbind(c(0, 0), ##' c(0, 1), ##' c(1, 0)) ##' ## Cartesian cooridinates of points ##' beta <- rbind(c(0, 0.5, 0.5), ##' c(0.1, 0.8, 0.1)) ##' ## Plot triangle and points ##' trimesh(rbind(1:3), X) ##' text(X[,1], X[,2], 1:3) # Label vertices ##' P <- bary2cart(X, beta) ##' points(P) ##' @seealso \code{\link{cart2bary}} ##' @author David Sterratt ##' @export bary2cart <- function(X, Beta) { return(Beta %*% X) } tsearchn_delaunayn <- function(t, xi) { warning("tsearchn using the Qhull library is currently an experimental feature. It has been tested somewhat for 3D triangulations, but it does not work reliably for 4D triangulations. See https://github.com/davidcsterratt/geometry/issues/6") ts <- .Call("C_tsearchn", t, xi) p <- do.call(rbind, lapply(1:nrow(xi), function(i) { cart2bary(ts$P[t$tri[ts$idx[i],],], xi[i,,drop=FALSE]) })) ## C_tsearchn will return the *best* facet. Need to check it is ## actually in the triangulation outwith_facet_inds <- which(apply(p < 0, 1, any)) idx <- ts$idx idx[outwith_facet_inds] <- NA p[outwith_facet_inds,] <- NA return(list(idx=idx, p=p, P=ts$P)) } geometry/R/matorder.R0000644000176200001440000000027013432317337014253 0ustar liggesusers"matorder" <- function (...) { x = cbind(...) if(!is.numeric(x)) stop("Input should be numeric.") do.call("order", lapply(1:ncol(x), function(i) x[, i])) } geometry/R/trisplinter.R0000644000176200001440000000046313432317337015021 0ustar liggesusers"trisplinter" <- function(T,p,threshold=sqrt(.Machine$double.eps)){ rownorm2 = function(x) drop(sqrt((x^2)%*%c(1,1))) d1 = p[T[,1],] - p[T[,2],] d2 = p[T[,2],] - p[T[,3],] d1 = d1 / rownorm2(d1) d2 = d2 / rownorm2(d2) ar = d1[,1]*d2[,2] - d1[,2]*d2[,1] return(abs(ar) < threshold) } geometry/R/distmesh2d.R0000644000176200001440000002273314366273177014526 0ustar liggesusers##' A simple mesh generator for non-convex regions ##' ##' An unstructured simplex requires a choice of mesh points (vertex nodes) and ##' a triangulation. This is a simple and short algorithm that improves the ##' quality of a mesh by relocating the mesh points according to a relaxation ##' scheme of forces in a truss structure. The topology of the truss is reset ##' using Delaunay triangulation. A (sufficiently smooth) user supplied signed ##' distance function (\code{fd}) indicates if a given node is inside or ##' outside the region. Points outside the region are projected back to the ##' boundary. ##' ##' This is an implementation of original Matlab software of Per-Olof Persson. ##' ##' Excerpt (modified) from the reference below: ##' ##' \sQuote{The algorithm is based on a mechanical analogy between a triangular ##' mesh and a 2D truss structure. In the physical model, the edges of the ##' Delaunay triangles of a set of points correspond to bars of a truss. Each ##' bar has a force-displacement relationship \eqn{f(\ell, \ell_{0})}{F(L,L0)} ##' depending on its current length \eqn{\ell}{L} and its unextended length ##' \eqn{\ell_{0}}{L0}.} ##' ##' \sQuote{External forces on the structure come at the boundaries, on which ##' external forces have normal orientations. These external forces are just ##' large enough to prevent nodes from moving outside the boundary. The ##' position of the nodes are the unknowns, and are found by solving for a ##' static force equilibrium. The hope is that (when \code{fh = function(p) ##' return(rep(1,nrow(p)))}), the lengths of all the bars at equilibrium will ##' be nearly equal, giving a well-shaped triangular mesh.} ##' ##' See the references below for all details. Also, see the comments in the ##' source file. ##' ##' @param fd Vectorized signed distance function, for example ##' \code{\link{mesh.dcircle}} or \code{\link{mesh.diff}}, accepting ##' an \code{n}-by-\code{2} matrix, where \code{n} is arbitrary, as ##' the first argument. ##' @param fh Vectorized function, for example ##' \code{\link{mesh.hunif}}, that returns desired edge length as a ##' function of position. Accepts an \code{n}-by-\code{2} matrix, ##' where \code{n} is arbitrary, as its first argument. ##' @param h0 Initial distance between mesh nodes. (Ignored of ##' \code{p} is supplied) ##' @param bbox Bounding box \code{cbind(c(xmin,xmax), c(ymin,ymax))} ##' @param p An \code{n}-by-\code{2} matrix. The rows of \code{p} ##' represent locations of starting mesh nodes. ##' @param pfix \code{nfix}-by-2 matrix with fixed node positions. ##' @param \dots parameters to be passed to \code{fd} and/or \code{fh} ##' @param dptol Algorithm stops when all node movements are smaller ##' than \code{dptol} ##' @param ttol Controls how far the points can move (relatively) ##' before a retriangulation with \code{\link{delaunayn}}. ##' @param Fscale \dQuote{Internal pressure} in the edges. ##' @param deltat Size of the time step in Euler's method. ##' @param geps Tolerance in the geometry evaluations. ##' @param deps Stepsize \eqn{\Delta x} in numerical derivative ##' computation for distance function. ##' @param maxiter Maximum iterations. ##' @param plot logical. If \code{TRUE} (default), the mesh is ##' plotted as it is generated. ##' @return \code{n}-by-\code{2} matrix with node positions. ##' @section Wishlist : \itemize{ \item Implement in C/Fortran ##' \item Implement an \code{n}D version as provided in the Matlab ##' package \item Translate other functions of the Matlab package } ##' @author Raoul Grasman ##' @seealso \code{\link[interp]{tri.mesh}}, \code{\link{delaunayn}}, ##' \code{\link{mesh.dcircle}}, \code{\link{mesh.drectangle}}, ##' \code{\link{mesh.diff}}, \code{\link{mesh.union}}, ##' \code{\link{mesh.intersect}} ##' @references \url{http://persson.berkeley.edu/distmesh/} ##' ##' \cite{P.-O. Persson, G. Strang, A Simple Mesh Generator in MATLAB. SIAM ##' Review, Volume 46 (2), pp. 329-345, June 2004} ##' @keywords math optimize dplot graphs ##' @examples ##' ##' # examples distmesh2d ##' fd <- function(p, ...) sqrt((p^2)%*%c(1,1)) - 1 ##' # also predefined as `mesh.dcircle' ##' fh <- function(p,...) rep(1,nrow(p)) ##' bbox <- matrix(c(-1,1,-1,1),2,2) ##' p <- distmesh2d(fd,fh,0.2,bbox, maxiter=100) ##' # this may take a while: ##' # press Esc to get result of current iteration ##' ##' # example with non-convex region ##' fd <- function(p, ...) mesh.diff(p , mesh.drectangle, mesh.dcircle, radius=.3) ##' # fd defines difference of square and circle ##' ##' p <- distmesh2d(fd,fh,0.05,bbox,radius=0.3,maxiter=4) ##' p <- distmesh2d(fd,fh,0.05,bbox,radius=0.3, maxiter=10) ##' # continue on previous mesh ##' @export distmesh2d <- function(fd, fh, h0, bbox, p=NULL, pfix=array(0,dim=c(0,2)), ..., dptol=0.001, ttol=0.1, Fscale=1.2, deltat=0.2, geps=0.001*h0, deps=sqrt(.Machine$double.eps)*h0, maxiter=1000, plot=TRUE) { rownorm2 = function(x) drop(sqrt((x^2)%*%c(1, 1))) if(any(apply(bbox, 2, diff)==0)) stop("Supplied bounding box has zero area.") if(is.null(p)) { #%1 generate initial grid y = seq(bbox[1, 2],bbox[2, 2], by=h0*sqrt(3)/2) x = seq(bbox[1, 1], bbox[2, 1], by=h0) x = matrix(x,length(y),length(x),byrow=TRUE) x[seq(2,length(y),by=2),] = x[seq(2,length(y),by=2),] + h0/2 p = cbind(c(x),y) #%2 remove nodes outside boundary specified by fd (points evaluated negative are # considered to lie inside the boundary) p = p[fd(p, ...) < geps,] } ## For density control nfix <- nrow(pfix) count <- 0 densityctrlfreq <- 30 count2 <- 0 r0 = 1 / fh(p, ...)^2 # acceptance probability p = rbind(pfix, p[stats::runif(nrow(p)) ttol) { pold = p T = delaunayn(p) # generate a Delaunay triangulation pmid = (p[T[, 1],] + p[T[, 2],] + p[T[, 3],])/3 # calculate average of node locations as centers T = T[fd(pmid, ...) < (-geps), 1:3] # remove triangles with center outside region #%4 describe edges by uniqe pairs of nodes #bars = unique(rbind(T[, -1],T[, -2],T[, -3]), MARGIN=1); #bars = bars[order(bars[, 1],bars[, 2]),]; bars = rbind(T[, -3], T[, -2], T[, -1]) # select unique edges #too slow: bars = unique(matsort(bars), MARGIN=1) bars = Unique(matsort(bars)) # order the edges according to the node indices #%5 Graphical display if (plot) trimesh(T, p, asp=1) # a la Matlab } #%6 compute force F on the basis of edge lenghts barvec = p[bars[, 1],] - p[bars[, 2],] # bar vectors L = rownorm2(barvec) # their lengths # calculate desired lengths L0 by use of fh hbars = fh((p[bars[, 1],] + p[bars[, 2],])/2, ...) L0 = hbars * Fscale * sqrt(sum(L^2)/sum(hbars^2)) ###################################################### # Original Matlab code # % Density control - remove points that are too close # if mod(count,densityctrlfreq)==0 & any(L0>2*L) # p(setdiff(reshape(bars(L0>2*L,:),[],1),1:nfix),:)=[]; # N=size(p,1); pold=inf; # continue; # end ###################################################### # R translation # Density control - remove points that are too close tmp1 <- (count %% densityctrlfreq) tmp2 <- any(L0 > (2*L)) if (tmp1 == 0 & tmp2) { count2 <- count2 + 1 # reshape(bars(L0>2*L,:),[],1) tmp3 <- c(bars[L0 > (2*L),]) up <- which(duplicated(tmp3) == TRUE, arr.ind=TRUE) p <- p[-up,] N <- length(p[,1]) pold <- 1/.Machine$double.eps } ###################################################### F = drop(L0 - L) F[F < 0] = 0 Fvec = barvec * (F/L) Ftot = matrix(0, N, 2) ii = bars[, c(1, 1, 2, 2)] jj = rep(1, length(F)) %o% c(1, 2, 1, 2) s = c(cbind(Fvec, -Fvec)) ns = length(s) Ftot[1:(2*N)] = rowsum(s, ii[1:ns] + ns*(jj[1:ns] - 1)) # sum all forces on each node if (nrow(pfix) > 0) Ftot[1:nrow(pfix),] = 0 # Force = 0 at fixed points p = p + deltat*Ftot; #%7 excercise normal force at boundary: move overshoot points to nearest boundary point d = fd(p); ix= d > 0; # find points outside dgradx= (fd(cbind(p[ix, 1]+deps, p[ix, 2]),...) - d[ix])/deps; # Numerical dgrady= (fd(cbind(p[ix, 1], p[ix, 2]+deps),...) - d[ix])/deps; # gradient p[ix,] = p[ix,] - cbind(d[ix]*dgradx, d[ix]*dgrady); # Project back to boundary #%8 test for convergence if(max(rownorm2(deltat*Ftot[d < (-geps),])/h0) < dptol | iter>=maxiter) break; iter = iter + 1 } message(sprintf("Number of density control ops = %d",count2)) message(sprintf("Number of iterations = %d",iter)) if (iter >= maxiter) warning(" Maximum iterations reached. Relaxation process not \n completed") return(p) } geometry/R/mesh.diff.R0000644000176200001440000000274113432317337014306 0ustar liggesusers##' Compute the signed distances from points \code{p} to a region ##' defined by the difference, union or intersection of regions ##' specified by the functions \code{regionA} and \code{regionB}. ##' \code{regionA} and \code{regionB} must accept a matrix \code{p} ##' with 2 columns as their first argument, and must return a vector ##' of length \code{nrow(p)} containing the signed distances of the ##' supplied points in \code{p} to their respective regions. ##' ##' @title Difference, union and intersection operation on two regions ##' @return A vector of length \code{nrow(p)} containing the signed ##' distances to the boundary of the region. ##' @author Raoul Grasman; translated from original Matlab sources of Per-Olof ##' Persson. ##' @param p A matrix with 2 columns (3 in \code{mesh.dsphere}), each row ##' representing a point in the plane. ##' @param regionA vectorized function describing region A in the ##' union / intersection / difference ##' @param regionB vectorized function describing region B in the ##' union / intersection / difference ##' @param ... additional arguments passed to \code{regionA} and ##' \code{regionB} ##' @aliases mesh.diff mesh.union mesh.intersect ##' @seealso \code{\link{distmesh2d}}, \code{\link{mesh.dcircle}}, ##' \code{\link{mesh.drectangle}} \code{\link{mesh.dsphere}} ##' @export mesh.diff mesh.union mesh.intersect mesh.diff <-function (p, regionA, regionB, ...) { return(matmax(regionA(p, ...), -regionB(p, ...))) } geometry/R/matsort.R0000644000176200001440000000114013432317337014124 0ustar liggesusers"matsort" <- function (...) { x = cbind(...) if(!is.numeric(x)) stop("Input should by numeric.") res = array(dim = c(nrow(x), 0)) if (!is.matrix(drop(x))) return(x) else if (ncol(x) > 30) return(t(apply(x, 1, sort))) else while (is.matrix(drop(x))) { imc = max.col(x) x = t(x) imx = nrow(x) * (1:ncol(x) - 1) + imc xmax = x[imx] x = t(matrix(x[-imx], ncol = ncol(x))) res = cbind(res, xmax) } res = cbind(res, x) colnames(res) = NULL rownames(res) = NULL return(res) } geometry/R/delaunayn.R0000644000176200001440000001725714366511252014432 0ustar liggesusers##' Delaunay triangulation in N dimensions ##' ##' The Delaunay triangulation is a tessellation of the convex hull of ##' the points such that no \eqn{N}-sphere defined by the \eqn{N}- ##' triangles contains any other points from the set. ##' ##' @param p An \eqn{M}-by-\eqn{N} matrix whose rows represent \eqn{M} ##' points in \eqn{N}-dimensional space. ##' ##' @param options String containing extra control options for the ##' underlying Qhull command; see the Qhull documentation ##' (\url{../doc/qhull/html/qdelaun.html}) for the available ##' options. ##' ##' The \code{Qbb} option is always passed to Qhull. The remaining ##' default options are \code{Qcc Qc Qt Qz} for \eqn{N<4} and ##' \code{Qcc Qc Qt Qx} for \eqn{N>=4}. If neither of the \code{QJ} ##' or \code{Qt} options are supplied, the \code{Qt} option is ##' passed to Qhull. The \code{Qt} option ensures all Delaunay ##' regions are simplical (e.g., triangles in 2D). See ##' \url{../doc/qhull/html/qdelaun.html} for more details. Contrary ##' to the Qhull documentation, no degenerate (zero area) regions ##' are returned with the \code{Qt} option since the R function ##' removes them from the triangulation. ##' ##' \emph{If \code{options} is specified, the default options are ##' overridden.} It is recommended to use \code{output.options} for ##' options controlling the outputs. ##' ##' @param output.options String containing Qhull options to control ##' output. Currently \code{Fn} (neighbours) and \code{Fa} (areas) ##' are supported. Causes an object of return value for details. If ##' \code{output.options} is \code{TRUE}, select all supported ##' options. ##' ##' @param full Deprecated and will be removed in a future release. ##' Adds options \code{Fa} and \code{Fn}. ##' ##' @return If \code{output.options} is \code{NULL} (the default), ##' return the Delaunay triangulation as a matrix with \eqn{M} rows ##' and \eqn{N+1} columns in which each row contains a set of ##' indices to the input points \code{p}. Thus each row describes a ##' simplex of dimension \eqn{N}, e.g. a triangle in 2D or a ##' tetrahedron in 3D. ##' ##' If the \code{output.options} argument is \code{TRUE} or is a ##' string containing \code{Fn} or \code{Fa}, return a list with ##' class \code{delaunayn} comprising the named elements: ##' \describe{ ##' \item{\code{tri}}{The Delaunay triangulation described above} ##' \item{\code{areas}}{If \code{TRUE} or if \code{Fa} is specified, an ##' \eqn{M}-dimensional vector containing the generalised area of ##' each simplex (e.g. in 2D the areas of triangles; in 3D the volumes ##' of tetrahedra). See \url{../doc/qhull/html/qh-optf.html#Fa}.} ##' \item{\code{neighbours}}{If \code{TRUE} or if \code{Fn} is specified, ##' a list of neighbours of each simplex. Note that a negative number ##' corresponds to "facet" (="edge" in 2D or "face" in 3D) that has no ##' neighbour, as will be the case for some simplices on the boundary ##' of the triangulation. ##' See \url{../doc/qhull/html/qh-optf.html#Fn}} ##' } ##' ##' @note This function interfaces the Qhull library and is a port ##' from Octave (\url{https://octave.org/}) to R. Qhull computes ##' convex hulls, Delaunay triangulations, halfspace intersections ##' about a point, Voronoi diagrams, furthest-site Delaunay ##' triangulations, and furthest-site Voronoi diagrams. It runs in ##' 2D, 3D, 4D, and higher dimensions. It implements the ##' Quickhull algorithm for computing the convex hull. Qhull handles ##' round-off errors from floating point arithmetic. It computes ##' volumes, surface areas, and approximations to the convex ##' hull. See the Qhull documentation included in this distribution ##' (the doc directory \url{../doc/qhull/index.html}). ##' ##' Qhull does not support constrained Delaunay triangulations, triangulation ##' of non-convex surfaces, mesh generation of non-convex objects, or ##' medium-sized inputs in 9D and higher. A rudimentary algorithm for mesh ##' generation in non-convex regions using Delaunay triangulation is ##' implemented in \link{distmesh2d} (currently only 2D). ##' @author Raoul Grasman and Robert B. Gramacy; based on the ##' corresponding Octave sources of Kai Habel. ##' @seealso \code{\link[interp]{tri.mesh}}, \code{\link{convhulln}}, ##' \code{\link{surf.tri}}, \code{\link{distmesh2d}} ##' @references \cite{Barber, C.B., Dobkin, D.P., and Huhdanpaa, H.T., ##' \dQuote{The Quickhull algorithm for convex hulls,} \emph{ACM Trans. on ##' Mathematical Software,} Dec 1996.} ##' ##' \url{http://www.qhull.org} ##' @keywords math dplot graphs ##' @examples ##' ##' # example delaunayn ##' d <- c(-1,1) ##' pc <- as.matrix(rbind(expand.grid(d,d,d),0)) ##' tc <- delaunayn(pc) ##' ##' # example tetramesh ##' \dontrun{ ##' rgl::view3d(60) ##' rgl::light3d(120,60) ##' tetramesh(tc,pc, alpha=0.9) ##' } ##' ##' tc1 <- delaunayn(pc, output.options="Fa") ##' ## sum of generalised areas is total volume of cube ##' sum(tc1$areas) ##' ##' @export ##' @useDynLib geometry delaunayn <- function(p, options=NULL, output.options=NULL, full=FALSE) { tmp_stdout <- tempfile("Rf") tmp_stderr <- tempfile("Rf") on.exit(unlink(c(tmp_stdout, tmp_stderr))) ## Coerce the input to be matrix if (is.data.frame(p)) { p <- as.matrix(p) } ## Make sure we have real-valued input storage.mode(p) <- "double" ## We need to check for NAs in the input, as these will crash the C ## code. if (any(is.na(p))) { stop("The first argument should not contain any NAs") } ## Default options if (is.null(options)) { if (ncol(p) < 4) { options <- "Qt Qc Qz" } else { options <- "Qt Qc Qx" } } ## Combine and check options options <- tryCatch(qhull.options(options, output.options, supported_output.options <- c("Fa", "Fn"), full=full), error=function(e) {stop(e)}) ## It is essential that delaunayn is called with either the QJ or Qt ## option. Otherwise it may return a non-triangulated structure, i.e ## one with more than dim+1 points per structure, where dim is the ## dimension in which the points p reside. if (!grepl("Qt", options) & !grepl("QJ", options)) { options <- paste(options, "Qt") } out <- .Call("C_delaunayn", p, as.character(options), tmp_stdout, tmp_stderr, PACKAGE="geometry") # Remove NULL elements out[which(sapply(out, is.null))] <- NULL if (is.null(out$areas) & is.null(out$neighbours)) { attr(out$tri, "delaunayn") <- attr(out$tri, "delaunayn") return(out$tri) } class(out) <- "delaunayn" out$p <- p return(out) } ##' @importFrom graphics plot ##' @method plot delaunayn ##' @export plot.delaunayn <- function(x, y, ...) { if (ncol(x$p) < 2 || ncol(x$p) > 3) stop("Only 2D and 3D convhullns can be plotted") args <- list(...) add <- FALSE if ("add" %in% names(args)) { add <- args$add args$add <- NULL } if (ncol(x$p) == 2) { if (!add) { plot(x$p[,1], x$p[,2], ...) } m <- Unique(rbind(x$tri[,1:2], x$tri[,2:3], x$tri[,c(1,3)])) p <- x$p do.call(segments, c(list(p[m[,1],1],p[m[,1],2],p[m[,2],1],p[m[,2],2]), args)) } if (ncol(x$p) == 3) { do.call(tetramesh, c(list(x$tri, x$p), args)) } } ## LocalWords: param Qhull Fn delaunayn Qbb Qcc Qc Qz Qx QJ itemize ## LocalWords: tri Voronoi Quickhull distmesh Grasman Gramacy Kai ## LocalWords: Habel seealso interp convhulln Dobkin Huhdanpaa ACM ## LocalWords: dQuote emph dplot pc tc tetramesh dontrun useDynLib # LocalWords: eqn url math tmp stdout tempfile stderr unlink NAs na # LocalWords: tryCatch qhull grepl sapply attr importFrom convhullns # LocalWords: args rbind geometry/R/pol2cart.R0000644000176200001440000000567513433536400014175 0ustar liggesusers## Copyright (C) 2000-2017 Kai Habel ## ## This file is part of Octave. ## ## Octave is free software; you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## Octave is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with Octave; see the file COPYING. If not, see ## . ## This file has been adapted for R by David C Sterratt ##' Transform polar or cylindrical coordinates to Cartesian coordinates. ##' ##' The inputs \code{theta}, \code{r}, (and \code{z}) must be the same shape, or ##' scalar. If called with a single matrix argument then each row of \code{P} ##' represents the polar/(cylindrical) coordinate (\code{theta}, \code{r} ##' (, \code{z})). ##' ##' @param theta describes the angle relative to the positive x-axis. ##' @param r is the distance to the z-axis (0, 0, z). ##' @param z (optional) is the z-coordinate ##' @return a matrix \code{C} where each row represents one Cartesian ##' coordinate (\code{x}, \code{y} (, \code{z})). ##' @seealso \code{\link{cart2pol}}, \code{\link{sph2cart}}, ##' \code{\link{cart2sph}} ##' @author Kai Habel ##' @author David Sterratt ##' @export pol2cart <- function(theta, r=NULL, z=NULL) { if (is.null(r) & is.null(z)) { if (!(is.numeric(theta))) { stop("input must be matrix with 2 or 3 columns") } if (!(is.numeric (theta) & is.matrix (theta) & (ncol(theta) == 2 | ncol(theta) == 3))) { stop("matrix input must have 2 or 3 columns [THETA, R (, Z)]"); } if (ncol(theta) == 3) { z <- theta[,3] } r <- theta[,2] theta <- theta[,1] } else { if (is.null(z)) { if (!is.numeric (theta) | !is.numeric (r)) { stop("THETA, R must be numeric arrays of the same size, or scalar") } if ( !((length(theta) == length(r)) | (length(theta) == 1) | (length(r) == 1))) { stop("THETA, Y must be numeric arrays of the same size, or scalar") } } else { if (! is.numeric (theta) | ! is.numeric (r) | ! is.numeric (z)) { stop("THETA, R, Z must be numeric arrays of the same size, or scalar") } if ( !(((length(theta) == length(r)) | (length(theta) == 1) | (length(r) == 1)) & ((length(theta) == length(z)) | (length(theta) == 1) | (length(z) == 1)) & ((length(r) == length(z)) | (length(r) == 1) | (length(z) == 1)))) { stop("theta, r, z must be matrices of the same size, or scalar") } } } x <- r*cos(theta) y <- r*sin(theta) if (is.null(z)) { return(cbind(x=x, y=y)) } return(cbind(x=x, y=y, z=z)) } geometry/R/Unique.R0000644000176200001440000000234713432317337013713 0ustar liggesusers##' Extract Unique Rows ##' ##' \sQuote{Unique} returns a vector, data frame or array like 'x' but with ##' duplicate elements removed. ##' ##' ##' @param X Numerical matrix. ##' @param rows.are.sets If \sQuote{\code{TRUE}}, rows are treated as sets - ##' i.e., to define uniqueness, the order of the rows does not matter. ##' @return Matrix of the same number of columns as \code{x}, with the unique ##' rows in \code{x} sorted according to the columns of \code{x}. If ##' \code{rows.are.sets = TRUE} the rows are also sorted. ##' @note \sQuote{\code{Unique}} is (under circumstances) much quicker than the ##' more generic base function \sQuote{\code{unique}}. ##' @author Raoul Grasman ##' @keywords arith math array ##' @examples ##' ##' # `Unique' is faster than `unique' ##' x = matrix(sample(1:(4*8),4*8),ncol=4) ##' y = x[sample(1:nrow(x),3000,TRUE), ] ##' gc(); system.time(unique(y)) ##' gc(); system.time(Unique(y)) ##' ##' # ##' z = Unique(y) ##' x[matorder(x),] ##' z[matorder(z),] ##' ##' @export "Unique" <- function (X, rows.are.sets = FALSE) { if (rows.are.sets) X = matsort(X) X = X[matorder(X), ] dX = apply(X, 2, diff) uniq = c(TRUE, ((dX^2) %*% rep(1, ncol(dX))) > 0) X = X[uniq, ] return(X) } geometry/R/surf.tri.R0000644000176200001440000001147214366321774014227 0ustar liggesusers##' Find surface triangles from tetrahedral mesh ##' ##' Find surface triangles from tetrahedral mesh typically obtained ##' with \code{\link{delaunayn}}. ##' ##' \code{surf.tri} and \code{\link{convhulln}} serve a similar purpose in 3D, ##' but \code{surf.tri} also works for non-convex meshes obtained e.g. with ##' \code{\link{distmeshnd}}. It also does not produce currently unavoidable ##' diagnostic output on the console as \code{convhulln} does at the Rterm ##' console--i.e., \code{surf.tri} is silent. ##' ##' @param p An \code{n}-by-\code{3} matrix. The rows of \code{p} represent ##' \code{n} points in \code{dim}-dimensional space. ##' @param t Matrix with 4 columns, interpreted as output of ##' \code{\link{delaunayn}}. ##' @return An \code{m}-by-\code{3} index matrix of which each row defines a ##' triangle. The indices refer to the rows in \code{p}. ##' @note \code{surf.tri} was based on Matlab code for mesh of Per-Olof Persson ##' (\url{http://persson.berkeley.edu/distmesh/}). ##' @author Raoul Grasman ##' @seealso \code{\link[interp]{tri.mesh}}, \code{\link{convhulln}}, ##' \code{\link{surf.tri}}, \code{\link{distmesh2d}} ##' @keywords math optimize dplot ##' @examples ##' ##' \dontrun{ ##' # more extensive example of surf.tri ##' ##' # url's of publically available data: ##' data1.url = "http://neuroimage.usc.edu/USCPhantom/mesh_data.bin" ##' data2.url = "http://neuroimage.usc.edu/USCPhantom/CT_PCS_trans.bin" ##' ##' meshdata = R.matlab::readMat(url(data1.url)) ##' elec = R.matlab::readMat(url(data2.url))$eeg.ct2pcs/1000 ##' brain = meshdata$mesh.brain[,c(1,3,2)] ##' scalp = meshdata$mesh.scalp[,c(1,3,2)] ##' skull = meshdata$mesh.skull[,c(1,3,2)] ##' tbr = t(surf.tri(brain, delaunayn(brain))) ##' tsk = t(surf.tri(skull, delaunayn(skull))) ##' tsc = t(surf.tri(scalp, delaunayn(scalp))) ##' rgl::triangles3d(brain[tbr,1], brain[tbr,2], brain[tbr,3],col="gray") ##' rgl::triangles3d(skull[tsk,1], skull[tsk,2], skull[tsk,3],col="white", alpha=0.3) ##' rgl::triangles3d(scalp[tsc,1], scalp[tsc,2], scalp[tsc,3],col="#a53900", alpha=0.6) ##' rgl::view3d(-40,30,.4,zoom=.03) ##' lx = c(-.025,.025); ly = -c(.02,.02); ##' rgl::spheres3d(elec[,1],elec[,3],elec[,2],radius=.0025,col='gray') ##' rgl::spheres3d( lx, ly,.11,radius=.015,col="white") ##' rgl::spheres3d( lx, ly,.116,radius=.015*.7,col="brown") ##' rgl::spheres3d( lx, ly,.124,radius=.015*.25,col="black") ##' } ##' ##' @export "surf.tri" <- function(p,t){ # original by Per-Olof Persson (c) 2005 for MATLAB # ported to R and modified for efficiency by Raoul Grasman (c) 2005 # construct all faces faces = rbind(t[,-4], t[,-3], t[,-2], t[,-1]); node4 = rbind(t[, 4], t[, 3], t[, 2], t[, 1]); # #original translated from MATLAB: # # select the faces that occur only once --> these are the surface boundary faces # faces = t(apply(faces,1,sort)); # sort each row # foo = apply(faces,1,function(x) do.call("paste",as.list(x,sep=" "))); # makes a string from each row # vec = table(foo); # tabulates the number of occurences of each string # ix = sapply(names(vec[vec==1]),function(b) which(b==foo)) # obtain indices of faces with single occurence # tri = faces[ix,]; # node4 = node4[ix]; # we wish to achieve # > faces = t(apply(faces,1,sort)); # but this is much too slow, we therefore use max.col and the fact # that there are only 3 columns in faces i.max = 3*(1:nrow(faces)-1) + max.col(faces) i.min = 3*(1:nrow(faces)-1) + max.col(-faces) faces = t(faces) faces = cbind(faces[i.min], faces[-c(i.max,i.min)], faces[i.max]) ix = order(faces[,1], faces[,2], faces[,3]) # Next, we wish to detect duplicated rows in faces, that is, # > qx = duplicated(faces[ix,],MARGIN=1) # logical indicating duplicates # but this is also much to slow, we therefore use the fact that # faces[ix,] has the duplicate rows ordered beneath each other # and the fact that each row occurs exactly once or twice fo = apply(faces[ix,],2,diff) dup = (abs(fo) %*% rep(1,3)) == 0 # a row of only zeros indicates duplicate dup = c(FALSE,dup) # first is never a duplicate qx = diff(dup)==0 # only zero if two consecutive elems are not duplicates qx = c(qx, !dup[length(dup)]) # last row is either non-duplicate or should not be selected tri = faces[ix[qx],] # ix[qx] are indices of singly occuring faces node4 = node4[ix[qx]] # compute face orientations v1 = p[tri[,2],] - p[tri[,1],]; # edge vectors v2 = p[tri[,3],] - p[tri[,1],]; v3 = p[node4,] - p[tri[,1],]; ix = which( apply(extprod3d(v1,v2) * v3, 1, sum) > 0 ) tri[ix,c(2,3)] = tri[ix,c(3,2)] rownames(tri) = NULL tri } geometry/R/sph2cart.R0000644000176200001440000000524413433536400014165 0ustar liggesusers## Copyright (C) 2017 David Sterratt ## Copyright (C) 2000-2017 Kai Habel ## ## This file is part of Octave. ## ## Octave is free software; you can redistribute it and/or modify it){ ## under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## Octave is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for (more details.){ ## ## You should have received a copy of the GNU General Public License ## along with Octave; see the file COPYING. If not, see ## . ##' Transform spherical coordinates to Cartesian coordinates ##' ##' The inputs \code{theta}, \code{phi}, and \code{r} must be the same ##' shape, or scalar. If called with a single matrix argument then ##' each row of \code{S} represents the spherical coordinate ##' (\code{theta}, \code{phi}, \code{r}). ##' ##' @param theta describes the angle relative to the positive x-axis. ##' @param phi is the angle relative to the xy-plane. ##' @param r is the distance to the origin \code{(0, 0, 0)}. ##' ##' If only a single return argument is requested then return a matrix ##' \code{C} where each row represents one Cartesian coordinate ##' (\code{x}, \code{y}, \code{z}). ##' @seealso \code{\link{cart2sph}}, \code{\link{pol2cart}}, \code{\link{cart2pol}} ##' @author Kai Habel ##' @author David Sterratt ##' @export sph2cart <- function(theta, phi=NULL, r=NULL) { if ((is.null(phi) & !is.null(r)) | (is.null(r) & !is.null(phi))) { stop("There should be 3 arguments (theta, phi, r) or one argument (theta)") } if (is.null(phi) & is.null(r)) { if (!(is.numeric(theta))) { stop("input must be matrix with 3 columns [theta, phi, r]") } if (!(is.matrix(theta) & (ncol(theta) == 3))) { stop("matrix input must have 3 columns [theta, phi, r]") } r <- theta[,3] phi <- theta[,2] theta <- theta[,1] } else { if (!is.numeric(theta) | !is.numeric(phi) | !is.numeric (r)) stop("theta, phi, r must be numeric arrays of the same size, or scalar") if ( !(((length(theta) == length(phi)) | (length(theta) == 1) | (length(phi) == 1)) & ((length(theta) == length(r)) | (length(theta) == 1) | (length(r) == 1)) & ((length(phi) == length(r)) | (length(phi) == 1) | (length(r) == 1)))) { stop("theta, phi, r must be matrices of the same size, or scalar") } } x <- r*cos(phi)*cos(theta) y <- r*cos(phi)*sin(theta) z <- r*sin(phi) return(cbind(x, y, z)) } geometry/MD50000644000176200001440000003211114367311205012415 0ustar liggesusers2b373124fb0209f33a2f68e35a039256 *DESCRIPTION 068b549a6e5e2c90a18f87356b4f00a4 *NAMESPACE bb2058e3695faa46eb54818991024f16 *NEWS d100f679b938110d96307291fce64c41 *R/RcppExports.R 9c6b34a4cc5fd5384ff9576db63347e0 *R/Unique.R 6676c6f07f0bcf0743181ad81a5b13bc *R/cart2pol.R 422a3c2ca63eb50396f478d6ba49c445 *R/cart2sph.R d035a58e9357e2f8a2d464f54525066f *R/convhulln.R 122baacf4e7db712ba01c68560422996 *R/delaunayn.R f67b6e054d55c085ef68f4fde78db0d8 *R/distmesh2d.R 58839184c5c9f16e927f689276f9521b *R/distmeshnd.R f02e89ee5fbbee42592d159bb119ee29 *R/dotprod.R 53e68a5fd3ffa96307ac3eec3ed15241 *R/entry.value.R 136a9d0aa0feeea8162d52b492ca2576 *R/extprod3d.R 13663e9698eeef3705b2cf444b55df42 *R/halfspacen.R 5f62c32fecf8cefacd30b5df31fde42f *R/inhulln.R 9a409fbef9b11d787e3b27b57d6b6dd5 *R/intersectn.R c2917f4499966c4bff5722c3a20ee293 *R/matmax.R 7953e067c808a844a6c25661b1f1fb19 *R/matmin.R 8845a9403edba64246177b1dab548d13 *R/matorder.R 7e77a7bbe9672e5a0efb27c33d0e47b7 *R/matsort.R 7d2a5c12b42717a5ccdf00af6b2acd06 *R/mesh.dcircle.R 2c454daede65e5c6a297c26c0de3bdc8 *R/mesh.diff.R c5a88a07fa0bb752e0b0fbc6a645e583 *R/mesh.drectangle.R 2ae969c5c662d5c4467ef4e7b6896fbd *R/mesh.dsphere.R 51f8458e619561296c18480dd9999be9 *R/mesh.hunif.R 019be320da1d80cd299776aba8e99e9e *R/mesh.intersect.R 4b471fb760c3b594a694cb8c8f2da59a *R/mesh.union.R f5863ae2279fab13fcca746122d3fb46 *R/pol2cart.R fcb8ec599d4463dc3e54287a670a1fab *R/polyarea.R c12ac336deb0b3be98a34eedb85bb61c *R/qhull-options.R 1c1c60e6a45ddb705e6480e965ab73d9 *R/rbox.R 979d55326e8f9e1029e95bd5651dc207 *R/sph2cart.R 95f3f780f786fa8dd704c89a9a0a0398 *R/surf.tri.R babfa6ca9e4b76b0353281b26426dfa0 *R/tetramesh.R 0255bc872693f2a7d519c67bf89217b2 *R/trimesh.R d0a05bf107df68a7a13fdfc16f3ab4c1 *R/trisplinter.R bd79225e1f0911ec27614d244b9df7a3 *R/tsearch.R c42113f23655049f93981d196321cd96 *build/vignette.rds 29a536ff14c26f87f9db8a06086d57eb *demo/00Index 2c2720d298e89a313a8963f5edc84fe6 *demo/intersectn.R 3128acf4b0156e6c2b2d1e8ed9e3fcac *inst/WORDLIST cbb93306cc667ac02ab9ca62bbca9e15 *inst/doc/LICENSE-NOTES 088aa19a297b3d79bcd8af709b3cfac6 *inst/doc/MODIFIED.txt 74b4f6b9894c15d5da15f00fb59bcedb *inst/doc/qhull-eg.R e4da12d940671662c95f27cc01da1ddb *inst/doc/qhull-eg.Rnw 105dc12fdfe0e3d663398a4ca9b2fce1 *inst/doc/qhull-eg.pdf 7f91e2f84c11dd41a932b6d03a63ae3d *inst/doc/qhull/Announce.txt 0c45b9e641ac0592e3eda32dac223b9a *inst/doc/qhull/COPYING.txt 1ad9811271fd30b546ff4f5d3d0e4cc6 *inst/doc/qhull/File_id.diz 916939bb1fee5b8ca7826fbcf7b53232 *inst/doc/qhull/README.txt dfc60c06612b894244510f7efa338c08 *inst/doc/qhull/REGISTER.txt d04ffffe2036538259f6f18f9e659af0 *inst/doc/qhull/html/index.html 59e9b69f186c9e715192df9e146ebd21 *inst/doc/qhull/html/normal_voronoi_knauss_oesterle.jpg 1affac9c72c41537fea5fa89780f0318 *inst/doc/qhull/html/qconvex.html 1c8d3f71222b19f46d4b0347e354edde *inst/doc/qhull/html/qdelau_f.html 815d958264afdfbdce508f5a97d0f5b6 *inst/doc/qhull/html/qdelaun.html bb51e7810cc49e5aff1b910bc3126713 *inst/doc/qhull/html/qh--4d.gif 687f459f95e958da6c3ff0c83cafcf46 *inst/doc/qhull/html/qh--cone.gif dc51084bc7ba1f235c71d40c771ce9c8 *inst/doc/qhull/html/qh--dt.gif 829df3fbc9a3f36dbdb6389f768b5570 *inst/doc/qhull/html/qh--geom.gif 0587393a4591c5fbcd040af9a4f510c7 *inst/doc/qhull/html/qh--half.gif 0515fcc283b7dfeeaa7169218722880e *inst/doc/qhull/html/qh--rand.gif 3cccb4448a350c67dccdebabc6e502f4 *inst/doc/qhull/html/qh-code.html 468eedbf9a4b499b23fb07b59bf8ca1e *inst/doc/qhull/html/qh-eg.html 3954a1be01feb9205ba50d43c6c37004 *inst/doc/qhull/html/qh-faq.html d6dfff41b500332225a49a3c7c02394e *inst/doc/qhull/html/qh-get.html 4f76f094fcda18d051465318fd00b57c *inst/doc/qhull/html/qh-impre.html 09a33942d2cb7bf16a209a58c13e1c0d *inst/doc/qhull/html/qh-optc.html 29fd77307a8ad0015666734561923fd6 *inst/doc/qhull/html/qh-optf.html 11c57dc8af5460e2c7ce6cb6ca61c276 *inst/doc/qhull/html/qh-optg.html f46107f191bbb0b549d8e625d28e5cd3 *inst/doc/qhull/html/qh-opto.html 4070e649f36d30f61d726b109bc3d4da *inst/doc/qhull/html/qh-optp.html c5da4436993a54b4a9e06de7eb333c35 *inst/doc/qhull/html/qh-optq.html 26c5df2b1bb04197ce18d831c0a7f7a6 *inst/doc/qhull/html/qh-optt.html f2f83ddd6a8887462df4e58248cd577a *inst/doc/qhull/html/qh-quick.html aad307ec6aa3db6fe22083a57eeff474 *inst/doc/qhull/html/qhalf.html e8992f5b2de07cbf48583e1f6b9ad5e9 *inst/doc/qhull/html/qhull-cpp.xml 1ffc8589ba39142748d6756180faa8f9 *inst/doc/qhull/html/qhull.html fc4b6d8b5abec935f14dd71369718e9f *inst/doc/qhull/html/qhull.man 5e213194265bc30a85ef692ac37d7836 *inst/doc/qhull/html/qhull.txt 37c947aef88e14eb0db11f529e065e7e *inst/doc/qhull/html/qvoron_f.html defc4e4ae9e778fff5eaa5fd020f9a09 *inst/doc/qhull/html/qvoronoi.html 29136f662937febee30f6ed1f9ebba29 *inst/doc/qhull/html/rbox.html 4cbad586d2136f611dbaf7f47128cc86 *inst/doc/qhull/html/rbox.man 61cd54c422366e1ef1db2babdb3daff9 *inst/doc/qhull/html/rbox.txt 2eb8bc5b61fda76c7af2b05d5b983013 *inst/doc/qhull/index.html 70edf8a33ab665ac8d181b0f0a7c5496 *inst/extdata/error_15_620.RData ccb784cef19f015690156d6780ad1a6e *inst/extdata/halfspacen.RData 58ce4b940b18d18c73c8d6a2e9f911ef *inst/extdata/intersectn4D.RData 758b6dfb5a56d69ee98f6474793e1eba *inst/extdata/issue35-intersectn.RData f3bce9787d6a1583475c384de6041ca7 *inst/extdata/ordination.Rdata 4f62407c45fc0dbc915fc76e289f04eb *inst/extdata/overlap260-5034.RData 845b30a0f674ce6f53afda166c81c131 *inst/extdata/save-overlap149-9428.RData bbb03765b5230d3f247478cc86c80379 *inst/extdata/save-overlap32-176.RData c80140a229d66996a43c5bdaeba9ef7d *inst/extdata/save-overlap68-557.RData 120f155140c555c5b001863ccde95627 *man/Unique.Rd 07fe105f12d5ca8f759c262e2e957477 *man/bary2cart.Rd 970ab4a4e47777dfd940103ff2b23985 *man/cart2bary.Rd 0a8e16e3e143df04b6ef8875b9433a49 *man/cart2pol.Rd b51e6d8148da25fac20a3c1d8fd05303 *man/cart2sph.Rd 9b80ae9a1b822833c73cfffd1aca77f4 *man/convhulln.Rd b0d7fec3ad46a86f33a9f217a7a722f5 *man/delaunayn.Rd ada20b0e85ad504dd3c5ac9f487b511a *man/distmesh2d.Rd 15bbc0b6a76439450d957b4130f19726 *man/distmeshnd.Rd d0e930cee46465b667cc89e57beed520 *man/dot.Rd afff3cc2ac7de8b6ceda27ef8431aaf2 *man/entry.value.Rd c396f94a51fc1cd13296a8e0c1a19c22 *man/extprod3d.Rd a347008f8c0116f3634986d4c86e7b27 *man/feasible.point.Rd 593b6ddb499d3354dc486a1c4a0e6fe2 *man/halfspacen.Rd c8fd1fadfa33cb0a7bb18e102719328c *man/inhulln.Rd 7fcb5537b976dba7490b46dba00f139b *man/intersectn.Rd 1c60bf8b2f2ade2f4d1c640c7bc5c4c9 *man/matmax.Rd cb5183b698da11fa5690176793b1cfe9 *man/mesh.dcircle.Rd 4f11eee330ec96bd13383003e2648fa2 *man/mesh.diff.Rd a187c0aed06dac008908a53d5d1b52f4 *man/mesh.drectangle.Rd 6289e1380af8c9da6901ae4e9b137dd9 *man/mesh.dsphere.Rd b08538f269bdda5767085f1b3c31274e *man/mesh.hunif.Rd 16aadfcf20aa49101e47abcd754b0b60 *man/pol2cart.Rd 462775b2834730b47871d60a0bbb9be8 *man/polyarea.Rd 0c7bac3dc3ba975ac9fad2935e7b5d05 *man/rbox.Rd d90c27c663cc7691a3b918d061903a71 *man/sph2cart.Rd 41c20ea09b7956e3f5d8cbf777305b37 *man/surf.tri.Rd 2d672ec9d2497f941103d4ae5d3034e9 *man/tetramesh.Rd b51abc8a985602501e0c3816b2218e0a *man/to.mesh3d.Rd fa42ae047747c7605cbe6e0298cde115 *man/trimesh.Rd 8e2cbb2206ba988c4dde2298573ac198 *man/tsearch.Rd b61620430d94896c9caba6fc026c08e6 *man/tsearchn.Rd b31b497621bd7b1bd822e91f4a7b6d73 *src/Makevars 9981fe6bd0995ae751dc31c2e5db9ec8 *src/QuadTree.cpp 6e7a8219b074eed6919f14f8d6068e15 *src/QuadTree.h 1d1e7e0ebca3232def50568b15ee836b *src/Rconvhulln.c 0f03f1357a95a4b83dcf1146fc351d59 *src/RcppExports.cpp 19b8f52e6a759510a960b504856fc0d8 *src/Rdelaunayn.c dd21723d9f12c8063f8ece126de9253c *src/Rgeometry.c 8acf1c1bc44b83391141acfc86110efe *src/Rgeometry.h 54e0d1a7309e67e0d830fa14ac454c64 *src/Rhalfspacen.c 24d7e9d45718d70e5d36b7927a5077b5 *src/Rinhulln.c b142077766065048b5f6343851731de1 *src/Rtsearch.cpp 617d83080f7ff4e8f16ad9dcfef067af *src/Rtsearch_orig.c 009802108361388247e1b7c717498148 *src/Rtsearchn.c dacb518e4412655c3c27df15e33ae5ba *src/geom2_r.c 76fcfbadd94c49fb50b0ef9b6d9e2ee8 *src/geom_r.c c90953c847606b82877c7fcefe6a083e *src/geom_r.h 023f4e70e66fe514f68d6a5ffab2201f *src/geometry_init.c 80fe297f5a91ba798c43f8ff75e68f19 *src/global_r.c 5bf82d51319cea4e1f414e38c80b97f9 *src/io_r.c 0796f7f714f6513afcfee3f96979e74e *src/io_r.h 95d1f647ac69de2cf0bf89e96fecfb03 *src/libqhull_r.c de30eb5b591df4faaef252f9c54c0f1f *src/libqhull_r.h 038d7a5dff679d87702098365253843c *src/mem_r.c 08b2049d0fb28523b513fce0ed6b745e *src/mem_r.h 099ae8a68bcf4602132f83e28233f3c5 *src/merge_r.c 82e438df1dbcafc2ff53f73584dff283 *src/merge_r.h fd9d91be1cfc0ff6569e691a712cc93d *src/poly2_r.c f2edd613887ce24b5795385ca5eb9993 *src/poly_r.c 07aa2af2aca6afcf013feacee1562838 *src/poly_r.h edc8b7e725eb05bf2281a4acd2ace789 *src/qhull_ra.h a336b5eab84a1c2328228faa044a816c *src/qset_r.c 0b52d6428d6715f0f8eed57067e95d8f *src/qset_r.h 4f0784c09f34d366833873453a56e139 *src/random_r.c e6754fd6b73a3587c1ffee3d5691cf61 *src/random_r.h c520bcebb98a9d30b4918e9ef7c69343 *src/rboxlib_r.c bb0844368cac0b4ac0ec3fdbf74c823f *src/stat_r.c 33705926270dcb8a888faa17b6e1d7b2 *src/stat_r.h 6edaadfdf448900891c1b3491a44dcba *src/user_r.c 4934a9df295ff94365c1c92b6f5ae80e *src/user_r.h 313a49495c1fb848d7896df74c471ce4 *src/usermem_r.c fbebdf6efc1cd2d6a48213b838b976eb *src/userprintf_r.c 292925bff7c805c25238eb1799a8d619 *src/userprintf_rbox_r.c 0622a97a2aaa3c342f09636052c2d7f5 *tests/spelling.R 56604df63c661b6ff144cbaa297a33ea *tests/testthat.R bb752ab1be8abd752e160eb4f6447da4 *tests/testthat/test-cart2pol.R ff43d4c55f592fdaf2f7448d7e4f8c0c *tests/testthat/test-cart2sph.R 69f2f924c40c344cb53de3a145c6e041 *tests/testthat/test-convhulln.R aa8bd09a5ca51ec2d9619b1d15e2998b *tests/testthat/test-delaunayn.R 2cb0f78866e8c8721a7b480e30fb6716 *tests/testthat/test-distmesh2d.R 039e347fe28ddf0ccbd4f2cb8a475a55 *tests/testthat/test-extprod3d.R 97b50a3013ad39c4c9ab324aa169e785 *tests/testthat/test-halfspacen.R e2c95479bf74aa684ecbba9bbe7043d6 *tests/testthat/test-inhulln.R 7c5dbfcf2f9bad68a5c3349374e9441a *tests/testthat/test-intersectn.R f31602e6034e476ca45ae37dc1174c88 *tests/testthat/test-parallel.R f281e943e2b03f9779a928d579d1c212 *tests/testthat/test-pol2cart.R 2d4dfa8755e18a70a0684185285cb0f2 *tests/testthat/test-polyarea.R 17a871068bdea2a3060b7453d747ab74 *tests/testthat/test-sph2cart.R 97b23669d29291f045958d43f72caeb5 *tests/testthat/test-tsearch-tsearchn-comparison.R 0c5c384c9fbcb53e749aad1ee6df975e *tests/testthat/test-tsearch.R 3a847edb84b447e6cd7f6c52e141230e *tests/testthat/test-tsearchn.R cbb93306cc667ac02ab9ca62bbca9e15 *vignettes/LICENSE-NOTES 088aa19a297b3d79bcd8af709b3cfac6 *vignettes/MODIFIED.txt 68a2cd14c4b008ba087a727ca09b5bc4 *vignettes/qhull-eg-002.pdf d58115b01fb59320488283b1e5bdca89 *vignettes/qhull-eg-004.pdf e4da12d940671662c95f27cc01da1ddb *vignettes/qhull-eg.Rnw 7f91e2f84c11dd41a932b6d03a63ae3d *vignettes/qhull/Announce.txt 0c45b9e641ac0592e3eda32dac223b9a *vignettes/qhull/COPYING.txt 1ad9811271fd30b546ff4f5d3d0e4cc6 *vignettes/qhull/File_id.diz 916939bb1fee5b8ca7826fbcf7b53232 *vignettes/qhull/README.txt dfc60c06612b894244510f7efa338c08 *vignettes/qhull/REGISTER.txt d04ffffe2036538259f6f18f9e659af0 *vignettes/qhull/html/index.html 59e9b69f186c9e715192df9e146ebd21 *vignettes/qhull/html/normal_voronoi_knauss_oesterle.jpg 1affac9c72c41537fea5fa89780f0318 *vignettes/qhull/html/qconvex.html 1c8d3f71222b19f46d4b0347e354edde *vignettes/qhull/html/qdelau_f.html 815d958264afdfbdce508f5a97d0f5b6 *vignettes/qhull/html/qdelaun.html bb51e7810cc49e5aff1b910bc3126713 *vignettes/qhull/html/qh--4d.gif 687f459f95e958da6c3ff0c83cafcf46 *vignettes/qhull/html/qh--cone.gif dc51084bc7ba1f235c71d40c771ce9c8 *vignettes/qhull/html/qh--dt.gif 829df3fbc9a3f36dbdb6389f768b5570 *vignettes/qhull/html/qh--geom.gif 0587393a4591c5fbcd040af9a4f510c7 *vignettes/qhull/html/qh--half.gif 0515fcc283b7dfeeaa7169218722880e *vignettes/qhull/html/qh--rand.gif 3cccb4448a350c67dccdebabc6e502f4 *vignettes/qhull/html/qh-code.html 468eedbf9a4b499b23fb07b59bf8ca1e *vignettes/qhull/html/qh-eg.html 3954a1be01feb9205ba50d43c6c37004 *vignettes/qhull/html/qh-faq.html d6dfff41b500332225a49a3c7c02394e *vignettes/qhull/html/qh-get.html 4f76f094fcda18d051465318fd00b57c *vignettes/qhull/html/qh-impre.html 09a33942d2cb7bf16a209a58c13e1c0d *vignettes/qhull/html/qh-optc.html 29fd77307a8ad0015666734561923fd6 *vignettes/qhull/html/qh-optf.html 11c57dc8af5460e2c7ce6cb6ca61c276 *vignettes/qhull/html/qh-optg.html f46107f191bbb0b549d8e625d28e5cd3 *vignettes/qhull/html/qh-opto.html 4070e649f36d30f61d726b109bc3d4da *vignettes/qhull/html/qh-optp.html c5da4436993a54b4a9e06de7eb333c35 *vignettes/qhull/html/qh-optq.html 26c5df2b1bb04197ce18d831c0a7f7a6 *vignettes/qhull/html/qh-optt.html f2f83ddd6a8887462df4e58248cd577a *vignettes/qhull/html/qh-quick.html aad307ec6aa3db6fe22083a57eeff474 *vignettes/qhull/html/qhalf.html e8992f5b2de07cbf48583e1f6b9ad5e9 *vignettes/qhull/html/qhull-cpp.xml 1ffc8589ba39142748d6756180faa8f9 *vignettes/qhull/html/qhull.html fc4b6d8b5abec935f14dd71369718e9f *vignettes/qhull/html/qhull.man 5e213194265bc30a85ef692ac37d7836 *vignettes/qhull/html/qhull.txt 37c947aef88e14eb0db11f529e065e7e *vignettes/qhull/html/qvoron_f.html defc4e4ae9e778fff5eaa5fd020f9a09 *vignettes/qhull/html/qvoronoi.html 29136f662937febee30f6ed1f9ebba29 *vignettes/qhull/html/rbox.html 4cbad586d2136f611dbaf7f47128cc86 *vignettes/qhull/html/rbox.man 61cd54c422366e1ef1db2babdb3daff9 *vignettes/qhull/html/rbox.txt 2eb8bc5b61fda76c7af2b05d5b983013 *vignettes/qhull/index.html geometry/inst/0000755000176200001440000000000014367304611013067 5ustar liggesusersgeometry/inst/doc/0000755000176200001440000000000014367304611013634 5ustar liggesusersgeometry/inst/doc/qhull/0000755000176200001440000000000014367311205014756 5ustar liggesusersgeometry/inst/doc/qhull/COPYING.txt0000644000176200001440000000314313431000557016624 0ustar liggesusers Qhull, Copyright (c) 1993-2018 C.B. Barber Arlington, MA and The National Science and Technology Research Center for Computation and Visualization of Geometric Structures (The Geometry Center) University of Minnesota email: qhull@qhull.org This software includes Qhull from C.B. Barber and The Geometry Center. Qhull is copyrighted as noted above. Qhull is free software and may be obtained via http from www.qhull.org. It may be freely copied, modified, and redistributed under the following conditions: 1. All copyright notices must remain intact in all files. 2. A copy of this text file must be distributed along with any copies of Qhull that you redistribute; this includes copies that you have modified, or copies of programs or other software products that include Qhull. 3. If you modify Qhull, you must include a notice giving the name of the person performing the modification, the date of modification, and the reason for such modification. 4. When distributing modified versions of Qhull, or other software products that include Qhull, you must provide notice that the original source code may be obtained as noted above. 5. There is no warranty or other guarantee of fitness for Qhull, it is provided solely "as is". Bug reports or fixes may be sent to qhull_bug@qhull.org; the authors may or may not act on them as they desire. geometry/inst/doc/qhull/html/0000755000176200001440000000000013431000557015716 5ustar liggesusersgeometry/inst/doc/qhull/html/qdelaun.html0000644000176200001440000006546513431000557020255 0ustar liggesusers qdelaunay -- Delaunay triangulation Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
    To: synopsis • input • outputs • controls • graphics • notes • conventions • options


    [delaunay]qdelaunay -- Delaunay triangulation

    The Delaunay triangulation is the triangulation with empty circumspheres. It has many useful properties and applications. See the survey article by Aurenhammer ['91] and the detailed introduction by O'Rourke ['94].

    Example: rbox r y c G0.1 D2 | qdelaunay s Fv TO result
    Compute the 2-d Delaunay triangulation of a triangle and a small square. Write a summary to the console and unoriented regions to 'result'. Merge regions for cocircular input sites (i.e., the square).
     
    Example: rbox r y c G0.1 D2 | qdelaunay s Fv Qt
    Compute the 2-d Delaunay triangulation of a triangle and a small square. Write a summary and unoriented regions to the console. Produce triangulated output.
     
    Example: rbox 10 D2 | qdelaunay QJ s i TO result
    Compute the 2-d Delaunay triangulation of 10 random points. Joggle the input to guarantee triangular output. Write a summary to the console and the regions to 'result'.

    Qhull computes the Delaunay triangulation by computing a convex hull. It lifts the input sites to a paraboloid by adding the sum of the squares of the coordinates. It scales the height of the paraboloid to improve numeric precision ('Qbb'). It computes the convex hull of the lifted sites, and projects the lower convex hull to the input.

    Each region of the Delaunay triangulation corresponds to a facet of the lower half of the convex hull. Facets of the upper half of the convex hull correspond to the furthest-site Delaunay triangulation. See the examples, Delaunay and Voronoi diagrams.

    See Qhull FAQ - Delaunay and Voronoi diagram questions.

    By default, qdelaunay merges cocircular and cospherical regions. For example, the Delaunay triangulation of a square inside a diamond ('rbox D2 c d G4 | qdelaunay') contains one region for the square.

    Use option 'Qz' if the input is circular, cospherical, or nearly so. It improves precision by adding a point "at infinity," above the corresponding paraboloid.

    If you use 'Qt' (triangulated output), all Delaunay regions will be simplicial (e.g., triangles in 2-d). Some regions may be degenerate and have zero area. Triangulated output identifies coincident points.

    If you use 'QJ' (joggled input), all Delaunay regions will be simplicial (e.g., triangles in 2-d). Coincident points will create small regions since the points are joggled apart. Joggled input is less accurate than triangulated output ('Qt'). See Merged facets or joggled input.

    The output for 3-d Delaunay triangulations may be confusing if the input contains cospherical data. See the FAQ item Why are there extra points in a 4-d or higher convex hull? Avoid these problems with triangulated output ('Qt') or joggled input ('QJ').

    The 'qdelaunay' program is equivalent to 'qhull d Qbb' in 2-d to 3-d, and 'qhull d Qbb Qx' in 4-d and higher. It disables the following Qhull options: d n v H U Qb QB Qc Qf Qg Qi Qm Qr QR Qv Qx TR E V FC Fi Fo Fp Ft FV Q0,etc.

    Copyright © 1995-2018 C.B. Barber


    »qdelaunay synopsis

    qdelaunay- compute the Delaunay triangulation.
        input (stdin): dimension, number of points, point coordinates
        comments start with a non-numeric character
    
    options (qdelaun.html):
        Qt   - triangulated output
        QJ   - joggle input instead of merging facets
        Qu   - furthest-site Delaunay triangulation
        Tv   - verify result: structure, convexity, and in-circle test
        .    - concise list of all options
        -    - one-line description of all options
    
    output options (subset):
        s    - summary of results (default)
        i    - vertices incident to each Delaunay region
        Fx   - extreme points (vertices of the convex hull)
        o    - OFF format (shows the points lifted to a paraboloid)
        G    - Geomview output (2-d and 3-d points lifted to a paraboloid)
        m    - Mathematica output (2-d inputs lifted to a paraboloid)
        QVn  - print Delaunay regions that include point n, -n if not
        TO file- output results to file, may be enclosed in single quotes
    
    examples:
        rbox c P0 D2 | qdelaunay s o          rbox c P0 D2 | qdelaunay i
        rbox c P0 D3 | qdelaunay Fv Qt        rbox c P0 D2 | qdelaunay s Qu Fv
        rbox c G1 d D2 | qdelaunay s i        rbox c G1 d D2 | qdelaunay s i Qt
        rbox M3,4 z 100 D2 | qdelaunay s      rbox M3,4 z 100 D2 | qdelaunay s Qt
    

    »qdelaunay input

    The input data on stdin consists of:

    • dimension
    • number of points
    • point coordinates

    Use I/O redirection (e.g., qdelaunay < data.txt), a pipe (e.g., rbox 10 | qdelaunay), or the 'TI' option (e.g., qdelaunay TI data.txt).

    For example, this is four cocircular points inside a square. Its Delaunay triangulation contains 8 triangles and one four-sided figure.

    rbox s 4 W0 c G1 D2 > data
    2 RBOX s 4 W0 c D2
    8
    -0.4941988586954018 -0.07594397977563715
    -0.06448037284989526 0.4958248496365813
    0.4911154367094632 0.09383830681375946
    -0.348353580869097 -0.3586778257652367
        -1     -1
        -1      1
         1     -1
         1      1
    

    qdelaunay s i < data

    
    Delaunay triangulation by the convex hull of 8 points in 3-d
    
      Number of input sites: 8
      Number of Delaunay regions: 9
      Number of non-simplicial Delaunay regions: 1
    
    Statistics for: RBOX s 4 W0 c D2 | QDELAUNAY s i
    
      Number of points processed: 8
      Number of hyperplanes created: 18
      Number of facets in hull: 10
      Number of distance tests for qhull: 33
      Number of merged facets: 2
      Number of distance tests for merging: 102
      CPU seconds to compute hull (after input): 0.028
    
    9
    1 7 5
    6 3 4
    2 3 6
    7 2 6
    2 7 1
    0 5 4
    3 0 4
    0 1 5
    1 0 3 2
    

    »qdelaunay outputs

    These options control the output of Delaunay triangulations:

    Delaunay regions
    i
    list input sites for each Delaunay region. The first line is the number of regions. The remaining lines list the input sites for each region. The regions are oriented. In 3-d and higher, report cospherical sites by adding extra points. Use triangulated output ('Qt') to avoid non-simpicial regions. For the circle-in-square example, eight Delaunay regions are triangular and the ninth has four input sites.
    Fv
    list input sites for each Delaunay region. The first line is the number of regions. Each remaining line starts with the number of input sites. The regions are unoriented. For the circle-in-square example, eight Delaunay regions are triangular and the ninth has four input sites.
    Fn
    list neighboring regions for each Delaunay region. The first line is the number of regions. Each remaining line starts with the number of neighboring regions. Negative indices (e.g., -1) indicate regions outside of the Delaunay triangulation. For the circle-in-square example, the four regions on the square are neighbors to the region-at-infinity.
    FN
    list the Delaunay regions for each input site. The first line is the total number of input sites. Each remaining line starts with the number of Delaunay regions. Negative indices (e.g., -1) indicate regions outside of the Delaunay triangulation. For the circle-in-square example, each point on the circle belongs to four Delaunay regions. Use 'Qc FN' to include coincident input sites and deleted vertices.
    Fa
    print area for each Delaunay region. The first line is the number of regions. The areas follow, one line per region. For the circle-in-square example, the cocircular region has area 0.4.
     
     
    Input sites
    Fc
    list coincident input sites for each Delaunay region. The first line is the number of regions. The remaining lines start with the number of coincident sites and deleted vertices. Deleted vertices indicate highly degenerate input (see'Fs'). A coincident site is assigned to one Delaunay region. Do not use 'QJ' with 'Fc'; the joggle will separate coincident sites.
    FP
    print coincident input sites with distance to nearest site (i.e., vertex). The first line is the number of coincident sites. Each remaining line starts with the point ID of an input site, followed by the point ID of a coincident point, its region, and distance. Includes deleted vertices which indicate highly degenerate input (see'Fs'). Do not use 'QJ' with 'FP'; the joggle will separate coincident sites.
    Fx
    list extreme points of the input sites. These points are on the boundary of the convex hull. The first line is the number of extreme points. Each point is listed, one per line. The circle-in-square example has four extreme points.
     
     
    General
    FA
    compute total area for 's' and 'FS'
    o
    print lower facets of the corresponding convex hull (a paraboloid)
    m
    Mathematica output for the lower facets of the paraboloid (2-d triangulations).
    FM
    Maple output for the lower facets of the paraboloid (2-d triangulations).
    G
    Geomview output for the paraboloid (2-d or 3-d triangulations).
    s
    print summary for the Delaunay triangulation. Use 'Fs' and 'FS' for numeric data.

    »qdelaunay controls

    These options provide additional control:

    Qt
    triangulated output. Qhull triangulates non-simplicial facets. It may produce degenerate facets of zero area.
    QJ
    joggle the input to avoid cospherical and coincident sites. It is less accurate than triangulated output ('Qt').
    Qu
    compute the furthest-site Delaunay triangulation.
    Qz
    add a point above the paraboloid to reduce precision errors. Use it for nearly cocircular/cospherical input (e.g., 'rbox c | qdelaunay Qz'). The point is printed for options 'Ft' and 'o'.
    QVn
    select facets adjacent to input site n (marked 'good').
    Tv
    verify result.
    TI file
    input data from file. The filename may not use spaces or quotes.
    TO file
    output results to file. Use single quotes if the filename contains spaces (e.g., TO 'file with spaces.txt'
    TFn
    report progress after constructing n facets
    PDk:1
    include upper and lower facets in the output. Set k to the last dimension (e.g., 'PD2:1' for 2-d inputs).
    f
    facet dump. Print the data structure for each facet (i.e., Delaunay region).

    »qdelaunay graphics

    For 2-d and 3-d Delaunay triangulations, Geomview ('qdelaunay G') displays the corresponding convex hull (a paraboloid).

    To view a 2-d Delaunay triangulation, use 'qdelaunay GrD2' to drop the last dimension. This is the same as viewing the hull without perspective (see Geomview's 'cameras' menu).

    To view a 3-d Delaunay triangulation, use 'qdelaunay GrD3' to drop the last dimension. You may see extra edges. These are interior edges that Geomview moves towards the viewer (see 'lines closer' in Geomview's camera options). Use option 'Gt' to make the outer ridges transparent in 3-d. See Delaunay and Voronoi examples.

    For 2-d Delaunay triangulations, Mathematica ('m') and Maple ('FM') output displays the lower facets of the corresponding convex hull (a paraboloid).

    For 2-d, furthest-site Delaunay triangulations, Maple and Mathematica output ('Qu m') displays the upper facets of the corresponding convex hull (a paraboloid).

    »qdelaunay notes

    You can simplify the Delaunay triangulation by enclosing the input sites in a large square or cube. This is particularly recommended for cocircular or cospherical input data.

    A non-simplicial Delaunay region indicates nearly cocircular or cospherical input sites. To avoid non-simplicial regions either triangulate the output ('Qt') or joggle the input ('QJ'). Triangulated output is more accurate than joggled input. Alternatively, use an exact arithmetic code.

    Delaunay triangulations do not include facets that are coplanar with the convex hull of the input sites. A facet is coplanar if the last coefficient of its normal is nearly zero (see qh_ZEROdelaunay).

    See Imprecision issues :: Delaunay triangulations for a discussion of precision issues. Deleted vertices indicate highly degenerate input. They are listed in the summary output and option 'Fs'.

    To compute the Delaunay triangulation of points on a sphere, compute their convex hull. If the sphere is the unit sphere at the origin, the facet normals are the Voronoi vertices of the input. The points may be restricted to a hemisphere. [S. Fortune]

    The 3-d Delaunay triangulation of regular points on a half spiral (e.g., 'rbox 100 l | qdelaunay') has quadratic size, while the Delaunay triangulation of random 3-d points is approximately linear for reasonably sized point sets.

    With the Qhull library, you can use qh_findbestfacet in poly2.c to locate the facet that contains a point. You should first lift the point to the paraboloid (i.e., the last coordinate is the sum of the squares of the point's coordinates -- qh_setdelaunay). Do not use options 'Qbb', 'QbB', 'Qbk:n', or 'QBk:n' since these scale the last coordinate.

    If a point is interior to the convex hull of the input set, it is interior to the adjacent vertices of the Delaunay triangulation. This is demonstrated by the following pipe for point 0:

        qdelaunay <data s FQ QV0 p | qconvex s Qb3:0B3:0 p
    

    The first call to qdelaunay returns the neighboring points of point 0 in the Delaunay triangulation. The second call to qconvex returns the vertices of the convex hull of these points (after dropping the lifted coordinate). If point 0 is interior to the original point set, it is interior to the reduced point set.

    »qdelaunay conventions

    The following terminology is used for Delaunay triangulations in Qhull for dimension d. The underlying structure is the lower facets of a convex hull in dimension d+1. For further information, see data structures and convex hull conventions.

    • input site - a point in the input (one dimension lower than a point on the convex hull)
    • point - a point has d+1 coordinates. The last coordinate is the sum of the squares of the input site's coordinates
    • coplanar point - a coincident input site or a deleted vertex. Deleted vertices indicate highly degenerate input.
    • vertex - a point on the paraboloid. It corresponds to a unique input site.
    • point-at-infinity - a point added above the paraboloid by option 'Qz'
    • lower facet - a facet corresponding to a Delaunay region. The last coefficient of its normal is clearly negative.
    • upper facet - a facet corresponding to a furthest-site Delaunay region. The last coefficient of its normal is clearly positive.
    • Delaunay region - a lower facet projected to the input sites
    • upper Delaunay region - an upper facet projected to the input sites
    • non-simplicial facet - more than d input sites are cocircular or cospherical
    • good facet - a Delaunay region with optional restrictions by 'QVn', etc.

    »qdelaunay options

    qdelaunay- compute the Delaunay triangulation
        http://www.qhull.org
    
    input (stdin):
        first lines: dimension and number of points (or vice-versa).
        other lines: point coordinates, best if one point per line
        comments:    start with a non-numeric character
    
    options:
        Qt   - triangulated output
        QJ   - joggle input instead of merging facets
        Qu   - compute furthest-site Delaunay triangulation
    
    Qhull control options:
        QJn  - randomly joggle input in range [-n,n]
        Qs   - search all points for the initial simplex
        Qz   - add point-at-infinity to Delaunay triangulation
        QGn  - print Delaunay region if visible from point n, -n if not
        QVn  - print Delaunay regions that include point n, -n if not
    
    Trace options:
        T4   - trace at level n, 4=all, 5=mem/gauss, -1= events
        Tc   - check frequently during execution
        Ts   - print statistics
        Tv   - verify result: structure, convexity, and in-circle test
        Tz   - send all output to stdout
        TFn  - report summary when n or more facets created
        TI file - input data from file, no spaces or single quotes
        TO file - output results to file, may be enclosed in single quotes
        TPn  - turn on tracing when point n added to hull
         TMn - turn on tracing at merge n
         TWn - trace merge facets when width > n
        TVn  - stop qhull after adding point n, -n for before (see TCn)
         TCn - stop qhull after building cone for point n (see TVn)
    
    Precision options:
        Cn   - radius of centrum (roundoff added).  Merge facets if non-convex
         An  - cosine of maximum angle.  Merge facets if cosine > n or non-convex
               C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge
        Rn   - randomly perturb computations by a factor of [1-n,1+n]
        Wn   - min facet width for outside point (before roundoff)
    
    Output formats (may be combined; if none, produces a summary to stdout):
        f    - facet dump
        G    - Geomview output (see below)
        i    - vertices incident to each Delaunay region
        m    - Mathematica output (2-d only, lifted to a paraboloid)
        o    - OFF format (dim, points, and facets as a paraboloid)
        p    - point coordinates (lifted to a paraboloid)
        s    - summary (stderr)
    
    More formats:
        Fa   - area for each Delaunay region
        FA   - compute total area for option 's'
        Fc   - count plus coincident points for each Delaunay region
        Fd   - use cdd format for input (homogeneous with offset first)
        FD   - use cdd format for numeric output (offset first)
        FF   - facet dump without ridges
        FI   - ID of each Delaunay region
        Fm   - merge count for each Delaunay region (511 max)
        FM   - Maple output (2-d only, lifted to a paraboloid)
        Fn   - count plus neighboring region for each Delaunay region
        FN   - count plus neighboring region for each point
        FO   - options and precision constants
        FP   - nearest point and distance for each coincident point
        FQ   - command used for qdelaunay
        Fs   - summary: #int (8), dimension, #points, tot vertices, tot facets,
                        for output: #vertices, #Delaunay regions,
                                    #coincident points, #non-simplicial regions
                        #real (2), max outer plane, min vertex
        FS   - sizes:   #int (0)
                        #real (2), tot area, 0
        Fv   - count plus vertices for each Delaunay region
        Fx   - extreme points of Delaunay triangulation (on convex hull)
    
    Geomview options (2-d and 3-d)
        Ga   - all points as dots
         Gp  -  coplanar points and vertices as radii
         Gv  -  vertices as spheres
        Gi   - inner planes only
         Gn  -  no planes
         Go  -  outer planes only
        Gc     - centrums
        Gh   - hyperplane intersections
        Gr   - ridges
        GDn  - drop dimension n in 3-d and 4-d output
        Gt   - transparent outer ridges to view 3-d Delaunay
    
    Print options:
        PAn  - keep n largest Delaunay regions by area
        Pdk:n - drop facet if normal[k] <= n (default 0.0)
        PDk:n - drop facet if normal[k] >= n
        Pg   - print good Delaunay regions (needs 'QGn' or 'QVn')
        PFn  - keep Delaunay regions whose area is at least n
        PG   - print neighbors of good regions (needs 'QGn' or 'QVn')
        PMn  - keep n Delaunay regions with most merges
        Po   - force output.  If error, output neighborhood of facet
        Pp   - do not report precision problems
    
        .    - list of all options
        -    - one line descriptions of all options
    

    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
    To: synopsis • input • outputs • controls • graphics • notes • conventions • options


    The Geometry Center Home Page

    Comments to: qhull@qhull.org
    Created: Sept. 25, 1995 --- Last modified: see top

    geometry/inst/doc/qhull/html/qh-optc.html0000644000176200001440000002734713431000557020174 0ustar liggesusers Qhull precision options

    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions


    [delaunay] Qhull precision options

    This section lists the precision options for Qhull. These options are indicated by an upper-case letter followed by a number.

    Copyright © 1995-2018 C.B. Barber


    » Programs OptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions

    Precision options

    Most users will not need to set these options. They are best used for approximating a convex hull. They may also be used for testing Qhull's handling of precision errors.

    By default, Qhull uses options 'C-0' for 2-d, 3-d and 4-d, and 'Qx' for 5-d and higher. These options use facet merging to handle precision errors. You may also use joggled input 'QJ' to avoid precision problems. For more information see Imprecision in Qhull.

     
    General
    Cn
    centrum radius for post-merging
    C-n
    centrum radius for pre-merging
    An
    cosine of maximum angle for post-merging
    A-n
    cosine of maximum angle for pre-merging
    Qx
    exact pre-merges (allows coplanar facets)
    C-0
    handle all precision errors
    Wn
    min distance above plane for outside points
     
    Experimental
    Un
    max distance below plane for a new, coplanar point
    En
    max roundoff error for distance computation
    Vn
    min distance above plane for a visible facet
    Rn
    randomly perturb computations by a factor of [1-n,1+n]

    »A-n - cosine of maximum angle for pre-merging.

    Pre-merging occurs while Qhull constructs the hull. It is indicated by 'C-n', 'A-n', or 'Qx'.

    If the angle between a pair of facet normals is greater than n, Qhull merges one of the facets into a neighbor. It selects the facet that is closest to a neighboring facet.

    For example, option 'A-0.99' merges facets during the construction of the hull. If the cosine of the angle between facets is greater than 0.99, one or the other facet is merged. Qhull accounts for the maximum roundoff error.

    If 'A-n' is set without 'C-n', then 'C-0' is automatically set.

    In 5-d and higher, you should set 'Qx' along with 'A-n'. It skips merges of coplanar facets until after the hull is constructed and before 'An' and 'Cn' are checked.

    »An - cosine of maximum angle for post-merging.

    Post merging occurs after the hull is constructed. For example, option 'A0.99' merges a facet if the cosine of the angle between facets is greater than 0.99. Qhull accounts for the maximum roundoff error.

    If 'An' is set without 'Cn', then 'C0' is automatically set.

    »C-0 - handle all precision errors

    Qhull handles precision errors by merging facets. The 'C-0' option handles all precision errors in 2-d, 3-d, and 4-d. It is set by default. It may be used in higher dimensions, but sometimes the facet width grows rapidly. It is usually better to use 'Qx' in 5-d and higher. Use 'QJ' to joggle the input instead of merging facets. Use 'Q0' to turn both options off.

    Qhull optimizes 'C-0' ("_zero-centrum") by testing vertices instead of centrums for adjacent simplices. This may be slower in higher dimensions if merges decrease the number of processed points. The optimization may be turned off by setting a small value such as 'C-1e-30'. See How Qhull handles imprecision.

    »C-n - centrum radius for pre-merging

    Pre-merging occurs while Qhull constructs the hull. It is indicated by 'C-n', 'A-n', or 'Qx'.

    The centrum of a facet is a point on the facet for testing facet convexity. It is the average of the vertices projected to the facet's hyperplane. Two adjacent facets are convex if each centrum is clearly below the other facet.

    If adjacent facets are non-convex, one of the facets is merged into a neighboring facet. Qhull merges the facet that is closest to a neighboring facet.

    For option 'C-n', n is the centrum radius. For example, 'C-0.001' merges facets whenever the centrum is less than 0.001 from a neighboring hyperplane. Qhull accounts for roundoff error when testing the centrum.

    In 5-d and higher, you should set 'Qx' along with 'C-n'. It skips merges of coplanar facets until after the hull is constructed and before 'An' and 'Cn' are checked.

    »Cn - centrum radius for post-merging

    Post-merging occurs after Qhull constructs the hull. It is indicated by 'Cn' or 'An'.

    For option 'Cn', n is the centrum radius. For example, 'C0.001' merges facets when the centrum is less than 0.001 from a neighboring hyperplane. Qhull accounts for roundoff error when testing the centrum.

    Both pre-merging and post-merging may be defined. If only post-merging is used ('Q0' with 'Cn'), Qhull may fail to produce a hull due to precision errors during the hull's construction.

    »En - max roundoff error for distance computations

    This allows the user to change the maximum roundoff error computed by Qhull. The value computed by Qhull may be overly pessimistic. If 'En' is set too small, then the output may not be convex. The statistic "max. distance of a new vertex to a facet" (from option 'Ts') is a reasonable upper bound for the actual roundoff error.

    »Rn - randomly perturb computations

    This option perturbs every distance, hyperplane, and angle computation by up to (+/- n * max_coord). It simulates the effect of roundoff errors. Unless 'En' is explicitly set, it is adjusted for 'Rn'. The command 'qhull Rn' will generate a convex hull despite the perturbations. See the Examples section for an example.

    Options 'Rn C-n' have the effect of 'W2n' and 'C-2n'. To use time as the random number seed, use option 'QR-1'.

    »Un - max distance for a new, coplanar point

    This allows the user to set coplanarity. When pre-merging ('C-n ', 'A-n' or 'Qx'), Qhull merges a new point into any coplanar facets. The default value for 'Un' is 'Vn'.

    »Vn - min distance for a visible facet

    This allows the user to set facet visibility. When adding a point to the convex hull, Qhull determines all facets that are visible from the point. A facet is visible if the distance from the point to the facet is greater than 'Vn'.

    Without merging, the default value for 'Vn' is the roundoff error ('En'). With merging, the default value is the pre-merge centrum ('C-n') in 2-d or 3-d, or three times that in other dimensions. If the outside width is specified with option 'Wn ', the maximum, default value for 'Vn' is 'Wn'.

    Qhull warns if 'Vn' is greater than 'Wn' and furthest outside ('Qf') is not selected; this combination usually results in flipped facets (i.e., reversed normals).

    »Wn - min distance above plane for outside points

    Points are added to the convex hull only if they are clearly outside of a facet. A point is outside of a facet if its distance to the facet is greater than 'Wn'. Without pre-merging, the default value for 'Wn' is 'En '. If the user specifies pre-merging and does not set 'Wn', than 'Wn' is set to the maximum of 'C-n' and maxcoord*(1 - A-n).

    This option is good for approximating a convex hull.

    Options 'Qc' and 'Qi' use the minimum vertex to distinguish coplanar points from interior points.


    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions


    The Geometry Center Home Page

    Comments to: qhull@qhull.org
    Created: Sept. 25, 1995 --- Last modified: see top

    geometry/inst/doc/qhull/html/rbox.man0000644000176200001440000001044513431000556017370 0ustar liggesusers.\" This is the Unix manual page for rbox, written in nroff, the standard .\" manual formatter for Unix systems. To format it, type .\" .\" nroff -man rbox.man .\" .\" This will print a formatted copy to standard output. If you want .\" to ensure that the output is plain ascii, free of any control .\" characters that nroff uses for underlining etc, pipe the output .\" through "col -b": .\" .\" nroff -man rbox.man | col -b .\" .TH rbox 1 "August 10, 1998" "Geometry Center" .SH NAME rbox \- generate point distributions for qhull .SH SYNOPSIS Command "rbox" (w/o arguments) lists the options. .SH DESCRIPTION .PP rbox generates random or regular points according to the options given, and outputs the points to stdout. The points are generated in a cube, unless 's' or 'k' option is given. The format of the output is the following: first line contains the dimension and a comment, second line contains the number of points, and the following lines contain the points, one point per line. Points are represented by their coordinate values. .SH EXAMPLES .TP rbox 10 10 random points in the unit cube centered at the origin. .TP rbox 10 s D2 10 random points on a 2\[hy]d circle. .TP rbox 100 W0 100 random points on the surface of a cube. .TP rbox 1000 s D4 1000 random points on a 4\[hy]d sphere. .TP rbox c D5 O0.5 a 5\[hy]d hypercube with one corner at the origin. .TP rbox d D10 a 10\[hy]d diamond. .TP rbox x 1000 r W0 100 random points on the surface of a fixed simplex .TP rbox y D12 a 12\[hy]d simplex. .TP rbox l 10 10 random points along a spiral .TP rbox l 10 r 10 regular points along a spiral plus two end points .TP rbox 1000 L10000 D4 s 1000 random points on the surface of a narrow lens. .TP rbox c G2 d G3 a cube with coordinates +2/\-2 and a diamond with coordinates +3/\-3. .TP rbox 64 M3,4 z a rotated, {0,1,2,3} x {0,1,2,3} x {0,1,2,3} lattice (Mesh) of integer points. 'rbox 64 M1,0' is orthogonal. .TP rbox P0 P0 P0 P0 P0 5 copies of the origin in 3\-d. Try 'rbox P0 P0 P0 P0 P0 | qhull QJ'. .TP r 100 s Z1 G0.1 two cospherical 100\-gons plus another cospherical point. .TP 100 s Z1 a cone of points. .TP 100 s Z1e\-7 a narrow cone of points with many precision errors. .SH OPTIONS .TP n number of points .TP Dn dimension n\[hy]d (default 3\[hy]d) .TP Bn bounding box coordinates (default 0.5) .TP l spiral distribution, available only in 3\[hy]d .TP Ln lens distribution of radius n. May be used with 's', 'r', 'G', and 'W'. .TP Mn,m,r lattice (Mesh) rotated by {[n,\-m,0], [m,n,0], [0,0,r], ...}. Use 'Mm,n' for a rigid rotation with r = sqrt(n^2+m^2). 'M1,0' is an orthogonal lattice. For example, '27 M1,0' is {0,1,2} x {0,1,2} x {0,1,2}. '27 M3,4 z' is a rotated integer lattice. .TP s cospherical points randomly generated in a cube and projected to the unit sphere .TP x simplicial distribution. It is fixed for option 'r'. May be used with 'W'. .TP y simplicial distribution plus a simplex. Both 'x' and 'y' generate the same points. .TP Wn restrict points to distance n of the surface of a sphere or a cube .TP c add a unit cube to the output .TP c Gm add a cube with all combinations of +m and \-m to the output .TP d add a unit diamond to the output. .TP d Gm add a diamond made of 0, +m and \-m to the output .TP Cn,r,m add n nearly coincident points within radius r of m points .TP Pn,m,r add point [n,m,r] to the output first. Pad coordinates with 0.0. .TP n Remove the command line from the first line of output. .TP On offset the data by adding n to each coordinate. .TP t use time in seconds as the random number seed (default is command line). .TP tn set the random number seed to n. .TP z generate integer coordinates. Use 'Bn' to change the range. The default is 'B1e6' for six\[hy]digit coordinates. In R^4, seven\[hy]digit coordinates will overflow hyperplane normalization. .TP Zn s restrict points to a disk about the z+ axis and the sphere (default Z1.0). Includes the opposite pole. 'Z1e\-6' generates degenerate points under single precision. .TP Zn Gm s same as Zn with an empty center (default G0.5). .TP r s D2 generate a regular polygon .TP r s Z1 G0.1 generate a regular cone .SH BUGS Some combinations of arguments generate odd results. Report bugs to qhull_bug@qhull.org, other correspondence to qhull@qhull.org .SH SEE ALSO qhull(1) .SH AUTHOR .nf C. Bradford Barber bradb@shore.net .fi geometry/inst/doc/qhull/html/qh-code.html0000644000176200001440000014032313431000557020127 0ustar liggesusers Qhull code

    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
    To: Qhull code: Table of Contents (please wait while loading)
    Dn: Qhull functions, macros, and data structures


    [4-d cube] Qhull code

    This section discusses the code for Qhull.

    Copyright © 1995-2018 C.B. Barber


    »Qhull code: Table of Contents


    »Reentrant Qhull

    Qhull-2015 introduces reentrant Qhull (libqhull_r). Reentrant Qhull uses a qhT* argument instead of global data structures. The qhT* pointer is the first argument to most Qhull routines. It allows multiple instances of Qhull to run at the same time. It simplifies the C++ interface to Qhull.

    New code should be written with libqhull_r. Existing users of libqhull should consider converting to libqhull_r. Although libqhull will be supported indefinitely, improvements may not be implemented. Reentrant qhull is 1-2% slower than non-reentrant qhull.

    Note: Reentrant Qhull is not thread safe. Do not invoke Qhull routines with the same qhT* pointer from multiple threads.

    »How to convert code to reentrant Qhull

    C++ users need to convert to libqhull_r. The new C++ interface does a better, but not perfect, job of hiding Qhull's C data structures. The previous C++ interface was unusual due to Qhull's global data structures.

    All other users should consider converting to libqhull_r. The conversion is straight forward. The original conversion of libqhull to libqhull_r required thousands of changes, mostly global search and replace. The first run of Qhull (unix_r.c) produced the same output, and nearly the same log files, as the original (unix.c).

    Suggestions to help with conversion.

    • Compare qconvex_r.c with qconvex.c. Define a qhT object and a pointer it. The qhT* pointer is the first argument to most Qhull functions. Clear qh_qh-<NOerrext before calling qh_initflags(). Invoke QHULL_LIB_CHECK to check for a compatible Qhull library.
    • Compare user_eg2_r.c with user_eg2.c
    • Compare user_eg_r.c with user_eg.c. If you use qhT before invoking qh_init_A, call qh_zero() to clear the qhT object. user_eg_r.c includes multiple Qhull runs.
    • Review user_eg3_r.cpp. As with the other programs, invoke QHULL_LIB_CHECK. Simple C++ programs should compile as is.
    • Compare QhullFacet.cpp with the same file in Qhull-2012.1. UsingLibQhull was replaced with the macro QH_TRY_() and 'qh_qh-<NOerrext= true'.
    • For detailed notes on libqhull_r, see "libqhull_r (reentrant Qhull)" and "Source code changes for libqhull_r" in Changes.txt.
    • For detailed notes on libqhullcpp, see "C++ interface" and following sections in Changes.txt.
    • For regexps and conversion notes, see README_r.txt (unedited).

    »Qhull on 64-bit computers

    Qhull compiles for 64-bit hosts. Since the size of a pointer on a 64-bit host is double the size on a 32-bit host, memory consumption increases about 50% for simplicial facets and up-to 100% for non-simplicial facets.

    You can check memory consumption with option Ts. It includes the size of each data structure:

    • 32-bit -- merge 24 ridge 20 vertex 28 facet 88 normal 24 ridge vertices 16 facet vertices or neighbors 20
    • 64-bit -- merge 32 ridge 32 vertex 48 facet 120 normal 32 ridge vertices 40 facet vertices or neighbors 48

    For Qhull 2015, the maximum identifier for ridges, vertices, and facets was increased from 24-bits to 32-bits. This allows for larger convex hulls, but may increase the size of the corresponding data structures. The sizes for Qhull 2012.1 were

    • 32-bit -- merge 24 ridge 16 vertex 24 facet 88
    • 64-bit -- merge 32 ridge 32 vertex 40 facet 120

    »Calling Qhull from C++ programs

    Qhull 2015 uses reentrant Qhull for its C++ interface. If you used the C++ interface from qhull 2012.1, you may need to adjust how you initialize and use the Qhull classes. See How to convert code to reentrant Qhull.

    Qhull's C++ interface allows you to explore the results of running Qhull. It provides access to Qhull's data structures. Most of the classes derive from the corresponding qhull data structure. For example, QhullFacet is an instance of Qhull's facetT.

    You can retain most of the data in Qhull and use the C++ interface to explore its results. Each object contains a reference the Qhull's data structure (via QhullQh), making the C++ representation less memory efficient.

    Besides using the C++ interface, you can also use libqhull_r directly. For example, the FOREACHfacet_(...) macro will visit each facet in turn.

    The C++ interface to Qhull is incomplete. You may need to extend the interface. If so, you will need to understand Qhull's data structures and read the code. Example (c.f., user_eg3 eg-100). It prints the facets generated by Qhull.

        RboxPoints rbox;
        rbox.appendRandomPoints("100");
        Qhull qhull;
        qhull.runQhull("", rbox);
        QhullFacetList facets(qhull);
        cout<< facets;
    

    The C++ iterface for RboxPoints redefines the fprintf() calls in rboxlib.c. Instead of writing its output to stdout, RboxPoints appends the output to a std::vector. The same technique may be used for calling Qhull from C++.

    • Run Qhull with option 'Ta' to annotate the output with qh_fprintf() identifiers.
    • Redefine qh_fprintf() for these identifiers.
    • See RboxPoints.cpp for an example.

    Since the C++ interface uses reentrant Qhull, multiple threads may run Qhull at the same time. Each thread is one run of Qhull.

    Do not have two threads accessing the same Qhull instance. Qhull is not thread-safe.

    »CoordinateIterator

    A CoordinateIterator or ConstCoordinateIterator [RboxPoints.cpp] is a std::vector<realT>::iterator for Rbox and Qhull coordinates. It is the result type of RboxPoints.coordinates().

    Qhull does not use CoordinateIterator for its data structures. A point in Qhull is an array of reals instead of a std::vector. See QhullPoint.

    »Qhull

    Qhull is the top-level class for running Qhull. It initializes Qhull, runs the computation, and records errors. It provides access to the global data structure QhullQh, Qhull's facets, and vertices.

    »QhullError

    QhullError is derived from std::exception. It reports errors from Qhull and captures the output to stderr.

    If error handling is not set up, Qhull exits with a code from 1 to 5. The codes are defined by qh_ERR* in libqhull_r.h. The exit is via qh_exit() in usermem_r.c. The C++ interface does not report the captured output in QhullError. Call Qhull::setErrorStream to send output to cerr instead.

    »QhullFacet

    A QhullFacet is a facet of the convex hull, a region of the Delaunay triangulation, a vertex of a Voronoi diagram, or an intersection of the halfspace intersection about a point. A QhullFacet has a set of QhullVertex, a set of QhullRidge, and a set of neighboring QhullFacets.

    »QhullFacetList

    A QhullFacetList is a linked list of QhullFacet. The result of Qhull.runQhull is a QhullFacetList stored in QhullQh.

    »QhullFacetSet

    A QhullFacetSet is a QhullSet of QhullFacet. QhullFacetSet may be ordered or unordered. The neighboring facets of a QhullFacet is a QhullFacetSet. The neighbors of a QhullFacet is a QhullFacetSet. The neighbors are ordered for simplicial facets, matching the opposite vertex of the facet.

    »QhullIterator

    QhullIterator contains macros for defining Java-style iterator templates from a STL-style iterator template.

    »QhullLinkedList

    A QhullLinkedLIst is a template for linked lists with next and previous pointers. QhullFacetList and QhullVertexList are QhullLinkedLists.

    »QhullPoint

    A QhullPoint is an array of point coordinates, typically doubles. The length of the array is QhullQh.hull_dim. The identifier of a QhullPoint is its 0-based index from QhullQh.first_point followed by QhullQh.other_points.

    »QhullPointSet

    A QhullPointSet is a QhullSet of QhullPoint. The QhullPointSet of a QhullFacet is its coplanar points.

    »QhullQh

    QhullQh is the root of Qhull's data structure. It contains initialized constants, sets, buffers, and variables. It contains an array and a set of QhullPoint, a list of QhullFacet, and a list of QhullVertex. The points are the input to Qhull. The facets and vertices are the result of running Qhull.

    Qhull's functions access QhullQh through the global variable, qh_qh. The global data structures, qh_stat and qh_mem, record statistics and manage memory respectively.

    »QhullRidge

    A QhullRidge represents the edge between two QhullFacet's. It is always simplicial with qh.hull_dim-1 QhullVertex)'s.

    »QhullRidgeSet

    A QhullRidgeSet is a QhullSet of QhullRidge. Each QhullFacet contains a QhullRidgeSet.

    »QhullSet

    A QhullSet is a set of pointers to objects. QhullSets may be ordered or unordered. They are the core data structure for Qhull.

    »QhullVertex

    A QhullVertex is a vertex of the convex hull. A simplicial QhullFacet has qh.hull_dim-1 vertices. A QhullVertex contains a QhullPoint. It may list its neighboring QhullFacet's.

    »QhullVertexList

    A QhullVertexList is a QhullLinkedList of QhullVertex. The global data structure, QhullQh contains a QhullVertexList of all the vertices.

    »QhullVertexSet

    A QhullVertexSet is a QhullSet of QhullVertex. The QhullVertexSet of a QhullFacet is the vertices of the facet. It is ordered for simplicial facets and unordered for non-simplicial facets.

    »RboxPoints

    RboxPoints is a std::vector of point coordinates (QhullPoint). It's iterator is CoordinateIterator.

    RboxPoints.appendRandomPoints() appends points from a variety of distributions such as uniformly distributed within a cube and random points on a sphere. It can also append a cube's vertices or specific points.

    »Cpp questions for Qhull

    Developing C++ code requires many conventions, idioms, and technical details. The following questions have either mystified the author or do not have a clear answer. See also C++ and Perl Guidelines. and FIXUP notes in the code. Please add notes to Qhull Wiki.
    • FIXUP QH11028 Should return reference, but get reference to temporary
      iterator Coordinates::operator++() { return iterator(++i); }
    • size() as size_t, size_type, or int
    • Should all containers have a reserve()?
    • Qhull.feasiblePoint interface
    • How to avoid copy constructor while logging, maybeThrowQhullMessage()
    • How to configure Qhull output. Trace and results should go to stdout/stderr
    • Qhull and RboxPoints messaging. e.g., ~Qhull, hasQhullMessage(). Rename them as QhullErrorMessage?
    • How to add additional output to an error message, e.g., qh_setprint
    • Is idx the best name for an index? It's rather cryptic, but BSD strings.h defines index().
    • Qhull::feasiblePoint Qhull::useOutputStream as field or getter?
    • Define virtual functions for user customization of Qhull (e.g., qh_fprintf, qh_memfree,etc.)
    • Figure out RoadError::global_log. clearQhullMessage currently clearGlobalLog
    • Should the false QhullFacet be NULL or empty? e.g., QhullFacet::tricoplanarOwner() and QhullFacetSet::end()
    • Should output format for floats be predefined (qh_REAL_1, 2.2g, 10.7g) or as currently set for stream
    • Should cout << !point.defined() be blank or 'undefined'
    • Infinite point as !defined()
    • qlist and qlinkedlist define pointer, reference, size_type, difference_type, const_pointer, const_reference for the class but not for iterator and const_iterator vector.h --
      reference operator[](difference_type _Off) const
    • When forwarding an implementation is base() an approriate name (e.g., Coordinates::iterator::base() as std::vector::iterator).
    • When forwarding an implementation, does not work "returning address of temporary"
    • Also --, +=, and -=
      iterator       &operator++() { return iterator(i++); }
    • if vector inheritance is bad, is QhullVertexSet OK?
    • Should QhullPointSet define pointer and reference data types?

    »Calling Qhull from C programs

    Warning: Qhull was not designed for calling from C programs. You may find the C++ interface easier to use. You will need to understand the data structures and read the code. Most users will find it easier to call Qhull as an external command.

    For examples of calling Qhull, see GNU Octave's computational geometry code, and Qhull's user_eg_r.c, user_eg2_r.c, and user_r.c. To see how Qhull calls its library, read unix_r.c, qconvex.c, qdelaun.c, qhalf.c, and qvoronoi.c. The '*_r.c' files are reentrant, otherwise they are non-reentrant. Either version may be used. New code should use reentrant Qhull.

    See Reentrant Qhull functions, macros, and data structures for internal documentation of Qhull. The documentation provides an overview and index. To use the library you will need to read and understand the code. For most users, it is better to write data to a file, call the qhull program, and read the results from the output file.

    If you use non-reentrant Qhull, be aware of the macros "qh" and "qhstat", e.g., "qh hull_dim". They are defined in libqhull.h. They allow the global data structures to be pre-allocated (faster access) or dynamically allocated (allows multiple copies).

    Qhull's Makefile produces a library, libqhull_r.a, for inclusion in your programs. First review libqhull_r.h. This defines the data structures used by Qhull and provides prototypes for the top-level functions. Most users will only need libqhull_r.h in their programs. For example, the Qhull program is defined with libqhull_r.h and unix_r.c. To access all functions, use qhull_ra.h. Include the file with "#include <libqhull_r/qhull_ra.h>". This avoids potential name conflicts.

    If you use the Qhull library, you are on your own as far as bugs go. Start with small examples for which you know the output. If you get a bug, try to duplicate it with the Qhull program. The 'Tc' option will catch many problems as they occur. When an error occurs, use 'T4 TPn' to trace from the last point added to the hull. Compare your trace with the trace output from the Qhull program.

    Errors in the Qhull library are more likely than errors in the Qhull program. These are usually due to feature interactions that do not occur in the Qhull program. Please report all errors that you find in the Qhull library. Please include suggestions for improvement.

    »How to avoid exit(), fprintf(), stderr, and stdout

    Qhull sends output to qh.fout and errors, log messages, and summaries to qh.ferr. qh.fout is normally stdout and qh.ferr is stderr. qh.fout may be redefined by option 'TO' or the caller. qh.ferr may be redirected to qh.fout by option 'Tz'.

    Qhull does not use stderr, stdout, fprintf(), or exit() directly.

    Qhull reports errors via qh_errexit() by writting a message to qh.ferr and invoking longjmp(). This returns the caller to the corresponding setjmp() (c.f., QH_TRY_ in QhullQh.h). If qh_errexit() is not available, Qhull functions call qh_exit(). qh_exit() normally calls exit(), but may be redefined by the user. An example is libqhullcpp/usermem_r-cpp.cpp. It redefines qh_exit() as a 'throw'.

    If qh_meminit() or qh_new_qhull() is called with ferr==NULL, then they set ferr to stderr. Otherwise the Qhull libraries use qh->ferr and qh->qhmem.ferr for error output.

    If an error occurs before qh->ferr is initialized, Qhull invokes qh_fprintf_stderr(). The user may redefine this function along with qh_exit(), qh_malloc(), and qh_free().

    The Qhull libraries write output via qh_fprintf() [userprintf_r.c]. Otherwise, the Qhull libraries do not use stdout, fprintf(), or printf(). Like qh_exit(), the user may redefine qh_fprintf().

    »sets and quick memory allocation

    You can use mem_r.c and qset_r.c individually. Mem_r.c implements quick-fit memory allocation. It is faster than malloc/free in applications that allocate and deallocate lots of memory.

    Qset_r.c implements sets and related collections. It's the inner loop of Qhull, so speed is more important than abstraction. Set iteration is particularly fast. qset_r.c just includes the functions needed for Qhull.

    »Delaunay triangulations and point indices

    Here some unchecked code to print the point indices of each Delaunay triangle. Use option 'QJ' if you want to avoid non-simplicial facets. Note that upper Delaunay regions are skipped. These facets correspond to the furthest-site Delaunay triangulation.

      facetT *facet;
      vertexT *vertex, **vertexp;
    
      FORALLfacets {
        if (!facet->upperdelaunay) {
          printf ("%d", qh_setsize (facet->vertices);
          FOREACHvertex_(facet->vertices)
            printf (" %d", qh_pointid (vertex->point));
          printf ("\n");
        }
      }
    
    

    »locate a facet with qh_findbestfacet()

    The routine qh_findbestfacet in poly2_r.c is particularly useful. It uses a directed search to locate the facet that is furthest below a point. For Delaunay triangulations, this facet is the Delaunay triangle that contains the lifted point. For convex hulls, the distance of a point to the convex hull is either the distance to this facet or the distance to a subface of the facet.

    Warning: If triangulated output ('Qt') and the best facet is triangulated, qh_findbestfacet() returns one of the corresponding 'tricoplanar' facets. The actual best facet may be a different tricoplanar facet.

    See qh_nearvertex() in poly2.c for sample code to visit each tricoplanar facet. To identify the correct tricoplanar facet, see Devillers, et. al., ['01] and Mucke, et al ['96]. If you implement this test in general dimension, please notify qhull@qhull.org.

    qh_findbestfacet performs an exhaustive search if its directed search returns a facet that is above the point. This occurs when the point is inside the hull or if the curvature of the convex hull is less than the curvature of a sphere centered at the point (e.g., a point near a lens-shaped convex hull). When the later occurs, the distance function is bimodal and a directed search may return a facet on the far side of the convex hull.

    Algorithms that retain the previously constructed hulls usually avoid an exhaustive search for the best facet. You may use a hierarchical decomposition of the convex hull [Dobkin and Kirkpatrick '90].

    To use qh_findbestfacet with Delaunay triangulations, lift the point to a paraboloid by summing the squares of its coordinates (see qh_setdelaunay in geom2_r.c). Do not scale the input with options 'Qbk', 'QBk', 'QbB' or 'Qbb'. See Mucke, et al ['96] for a good point location algorithm.

    The intersection of a ray with the convex hull may be found by locating the facet closest to a distant point on the ray. Intersecting the ray with the facet's hyperplane gives a new point to test.

    »on-line construction with qh_addpoint()

    The Qhull library may be used for the on-line construction of convex hulls, Delaunay triangulations, and halfspace intersections about a point. It may be slower than implementations that retain intermediate convex hulls (e.g., Clarkson's hull program). These implementations always use a directed search. For the on-line construction of convex hulls and halfspace intersections, Qhull may use an exhaustive search (qh_findbestfacet).

    You may use qh_findbestfacet and qh_addpoint (libqhull.c) to add a point to a convex hull. Do not modify the point's coordinates since qh_addpoint does not make a copy of the coordinates. For Delaunay triangulations, you need to lift the point to a paraboloid by summing the squares of the coordinates (see qh_setdelaunay in geom2.c). Do not scale the input with options 'Qbk', 'QBk', 'QbB' or 'Qbb'. Do not deallocate the point's coordinates. You need to provide a facet that is below the point (qh_findbestfacet).

    You can not delete points. Another limitation is that Qhull uses the initial set of points to determine the maximum roundoff error (via the upper and lower bounds for each coordinate).

    For many applications, it is better to rebuild the hull from scratch for each new point. This is especially true if the point set is small or if many points are added at a time.

    Calling qh_addpoint from your program may be slower than recomputing the convex hull with qh_qhull. This is especially true if the added points are not appended to the qh first_point array. In this case, Qhull must search a set to determine a point's ID. [R. Weber]

    See user_eg.c for examples of the on-line construction of convex hulls, Delaunay triangulations, and halfspace intersections. The outline is:

    initialize qhull with an initial set of points
    qh_qhull();
    
    for each additional point p
       append p to the end of the point array or allocate p separately
       lift p to the paraboloid by calling qh_setdelaunay
       facet= qh_findbestfacet (p, !qh_ALL, &bestdist, &isoutside);
       if (isoutside)
          if (!qh_addpoint (point, facet, False))
             break;  /* user requested an early exit with 'TVn' or 'TCn' */
    
    call qh_check_maxout() to compute outer planes
    terminate qhull

    »Constrained Delaunay triangulation

    With a fair amount of work, Qhull is suitable for constrained Delaunay triangulation. See Shewchuk, ACM Symposium on Computational Geometry, Minneapolis 1998.

    Here's a quick way to add a constraint to a Delaunay triangulation: subdivide the constraint into pieces shorter than the minimum feature separation. You will need an independent check of the constraint in the output since the minimum feature separation may be incorrect. [H. Geron]

    »Tricoplanar facets and option 'Qt'

    Option 'Qt' triangulates non-simplicial facets (e.g., a square facet in 3-d or a cubical facet in 4-d). All facets share the same apex (i.e., the first vertex in facet->vertices). For each triangulated facet, Qhull sets facet->tricoplanar true and copies facet->center, facet->normal, facet->offset, and facet->maxoutside. One of the facets owns facet->normal; its facet->keepcentrum is true. If facet->isarea is false, facet->triowner points to the owning facet.

    Qhull sets facet->degenerate if the facet's vertices belong to the same ridge of the non-simplicial facet.

    To visit each tricoplanar facet of a non-simplicial facet, either visit all neighbors of the apex or recursively visit all neighbors of a tricoplanar facet. The tricoplanar facets will have the same facet->center.

    See qh_detvridge for an example of ignoring tricoplanar facets.

    »Voronoi vertices of a region

    The following code iterates over all Voronoi vertices for each Voronoi region. Qhull computes Voronoi vertices from the convex hull that corresponds to a Delaunay triangulation. An input site corresponds to a vertex of the convex hull and a Voronoi vertex corresponds to an adjacent facet. A facet is "upperdelaunay" if it corresponds to a Voronoi vertex "at-infinity". Qhull uses qh_printvoronoi in io.c for 'qvoronoi o'

    /* please review this code for correctness */
    qh_setvoronoi_all();
    FORALLvertices {
       site_id = qh_pointid (vertex->point);
       if (qh hull_dim == 3)
          qh_order_vertexneighbors(vertex);
       infinity_seen = 0;
       FOREACHneighbor_(vertex) {
          if (neighbor->upperdelaunay) {
            if (!infinity_seen) {
              infinity_seen = 1;
              ... process a Voronoi vertex "at infinity" ...
            }
          }else {
            voronoi_vertex = neighbor->center;
            ... your code goes here ...
          }
       }
    }
    

    »Voronoi vertices of a ridge

    Qhull uses qh_printvdiagram() in io.c to print the ridges of a Voronoi diagram for option 'Fv'. The helper function qh_eachvoronoi() does the real work. It calls the callback 'printvridge' for each ridge of the Voronoi diagram.

    You may call qh_printvdiagram2(), qh_eachvoronoi(), or qh_eachvoronoi_all() with your own function. If you do not need the total number of ridges, you can skip the first call to qh_printvdiagram2(). See qh_printvridge() and qh_printvnorm() in io.c for examples.

    »vertex neighbors of a vertex

    To visit all of the vertices that share an edge with a vertex:

    • Generate neighbors for each vertex with qh_vertexneighbors in poly2.c.
    • For simplicial facets, visit the vertices of each neighbor
    • For non-simplicial facets,
      • Generate ridges for neighbors with qh_makeridges in merge.c.
      • Generate ridges for a vertex with qh_vertexridges in merge.c.
      • Visit the vertices of these ridges.

    For non-simplicial facets, the ridges form a simplicial decomposition of the (d-2)-faces between each pair of facets -- if you need 1-faces, you probably need to generate the full face graph of the convex hull.

    »Performance of Qhull

    Empirically, Qhull's performance is balanced in the sense that the average case happens on average. This may always be true if the precision of the input is limited to at most O(log n) bits. Empirically, the maximum number of vertices occurs at the end of constructing the hull.

    Let n be the number of input points, v be the number of output vertices, and f_v be the maximum number of facets for a convex hull of v vertices. If both conditions hold, Qhull runs in O(n log v) in 2-d and 3-d and O(n f_v/v) otherwise. The function f_v increases rapidly with dimension. It is O(v^floor(d/2) / floor(d/2)!).

    The time complexity for merging is unknown. Options 'C-0' and 'Qx' (defaults) handle precision problems due to floating-point arithmetic. They are optimized for simplicial outputs.

    When running large data sets, you should monitor Qhull's performance with the 'TFn' option. The time per facet is approximately constant. In high-d with many merged facets, the size of the ridge sets grows rapidly. For example the product of 8-d simplices contains 18 facets and 500,000 ridges. This will increase the time needed per facet.

    As dimension increases, the number of facets and ridges in a convex hull grows rapidly for the same number of vertices. For example, the convex hull of 300 cospherical points in 6-d has 30,000 facets.

    If Qhull appears to stop processing facets, check the memory usage of Qhull. If more than 5-10% of Qhull is in virtual memory, its performance will degrade rapidly.

    When building hulls in 20-d and higher, you can follow the progress of Qhull with option 'T1'. It reports each major event in processing a point.

    To reduce memory requirements, recompile Qhull for single-precision reals (REALfloat in user.h). Single-precision does not work with joggle ('QJ'). Check qh_MEMalign in user.h and the match between free list sizes and data structure sizes (see the end of the statistics report from 'Ts'). If free list sizes do not match, you may be able to use a smaller qh_MEMalign. Setting qh_COMPUTEfurthest saves a small amount of memory, as does clearing qh_MAXoutside (both in user.h).

    Shewchuk is working on a 3-d version of his triangle program. It is optimized for 3-d simplicial Delaunay triangulation and uses less memory than Qhull.

    To reduce the size of the Qhull executable, consider qh_NOtrace and qh_KEEPstatistics 0 in user.h. By changing user.c you can also remove the input/output code in io.c. If you don't need facet merging, then version 1.01 of Qhull is much smaller. It contains some bugs that prevent Qhull from initializing in simple test cases. It is slower in high dimensions.

    The precision options, 'Vn', 'Wn', 'Un'. 'A-n', 'C-n', 'An', 'Cn', and 'Qx', may have large effects on Qhull performance. You will need to experiment to find the best combination for your application.

    The verify option ('Tv') checks every point after the hull is complete. If facet merging is used, it checks that every point is inside every facet. This can take a very long time if there are many points and many facets. You can interrupt the verify without losing your output. If facet merging is not used and there are many points and facets, Qhull uses a directed search instead of an exhaustive search. This should be fast enough for most point sets. Directed search is not used for facet merging because directed search was already used for updating the facets' outer planes.

    The check-frequently option ('Tc') becomes expensive as the dimension increases. The verify option ('Tv') performs many of the same checks before outputting the results.

    Options 'Q0' (no pre-merging), 'Q3' (no checks for redundant vertices), 'Q5' (no updates for outer planes), and 'Q8' (no near-interior points) increase Qhull's speed. The corresponding operations may not be needed in your application.

    In 2-d and 3-d, a partial hull may be faster to produce. Option 'QgGn' only builds facets visible to point n. Option 'QgVn' only builds facets that contain point n. In higher-dimensions, this does not reduce the number of facets.

    User.h includes a number of performance-related constants. Changes may improve Qhull performance on your data sets. To understand their effect on performance, you will need to read the corresponding code.

    GNU gprof reports that the dominate cost for 3-d convex hull of cosperical points is qh_distplane(), mainly called from qh_findbestnew(). The dominate cost for 3-d Delaunay triangulation is creating new facets in qh_addpoint(), while qh_distplane() remains the most expensive function.

    »Enhancements to Qhull

    There are many ways in which Qhull can be improved.

    [Jan 2016] Suggestions
    ------------
    To do for a future verson of Qhull
     - Add a post-merge pass for Delaunay slivers.  Merge into a neighbor with a circumsphere that includes the opposite point. [M. Treacy]
     - Option to add a bounding box for Delaunay triangulations, e,g., nearly coincident points
     - Rescale output to match 'QbB' on input [J. Metz, 1/30/2014 12:21p]
     - Run through valgrind
     - Notes to compgeom on conformant triangulation and Voronoi volume
     - Implement weighted Delaunay triangulation and weighted Voronoi diagram [A. Liebscher]
       e.g., Sugihara, "Three-dimensional convex hull as a fruitful source of diagrams," Theoretical Computer Science, 2000, 235:325-337
     - testqset: test qh_setdelnth and move-to-front
     - Makefile: Re-review gcc/g++ warnings.  OK in 2011.
     - Break up -Wextra into its components or figure out how to override -Wunused-but-set-variable
       unused-but-set-variable is reporting incorrectly.  All instances are annotated.
     - CMakelists.txt:  Why are files duplicated for cmake build
     - CMakeLists.txt: configure the 'ctest' target
    
     - Can countT be defined as 'int', 'unsigned int', or 64-bit int?
       countT is currently defined as 'int' in qset_r.h
       Vertex ID and ridge ID perhaps should be countT, They are currently 'unsigned'
       Check use of 'int' vs. countT in all cpp code
       Check use of 'int' vs. countT in all c code
       qset_r.h defines countT -- duplicates code in user_r.h -- need to add to qset.h/user.h
       countT -1 used as a flag in Coordinates.mid(), QhullFacet->id()
       Also QhullPoints indexOf and lastIndexOf
       Also QhullPointSet indexOf and lastIndexOf
       Coordinates.indexOf assumes countT is signed (from end)
       Coordinates.lastIndexOf assumes countT is signed (from end)
       All error messages with countT are wrong, convert to int?
       RboxPoints.qh_fprintf_rbox, etc. message 9393 assumes countT but may be int, va_arg(args, countT);  Need to split
    
    ------------
    To do for a furture version of the C++ interface
     - Document C++ using Doxygen conventions (//! and //!<)
     - Should Qhull manage the output formats for doubles?  QH11010 user_r.h defines qh_REAL_1 as %6.8g
     - Allocate memory for QhullSet using Qhull.qhmem.  Create default constructors for QhullVertexSet etc.  Also mid() etc.
     - Add interior point for automatic translation?
     - Write a program with concurrent Qhull
     - Write QhullStat and QhullStat_test
     - Add QList and vector instance of facetT*, etc.
     - Generalize QhullPointSetIterator
     - qh-code.html: Document changes to C++ interface.
          Organize C++ documentation into collection classes, etc.
     - Review all C++ classes and C++ tests
     - QhullVertexSet uses QhullSetBase::referenceSetT() to free it's memory.   Probably needed elsewhere
     - The Boost Graph Library provides C++ classes for graph data structures. It may help
       enhance Qhull's C++ interface [Dr. Dobb's 9/00 p. 29-38; OOPSLA '99 p. 399-414].
    
    [Jan 2010] Suggestions
     - Generate vcproj from qtpro files
       cd qtpro && qmake -spec win32-msvc2005 -tp vc -recursive
       sed -i 's/C\:\/bash\/local\/qhull\/qtpro\///' qhull-all.sln
       Change qhullcpp to libqhull.dll
       Allow both builds on same host (keep /tmp separate)
     - C++ class for access to statistics, accumulate vs. add
     - Add dialog box to RoadError-- a virtual function?
     - Option 'Gt' does not make visible all facets of the mesh example, rbox 32 M1,0,1 | qhull d Gt
     - Merge small volume boundary cells into unbounded regions [Dominik Szczerba]
     - Postmerge with merge options
     - Add modify operators and MutablePointCoordinateIterator to PointCoordinates
     - Fix option Qt for conformant triangulations of merged facets
     - Investigate flipped facet -- rbox 100 s D3 t1263080158 | qhull R1e-3 Tcv Qc
     - Add doc comments to c++ code
     - Measure performance of Qhull, seconds per point by dimension
     - Report potential wraparound of 64-bit ints -- e.g., a large set or points
    
    Documentation
    - Qhull::addPoint().  Problems with qh_findbestfacet and otherpoints see
       qh-code.html#inc on-line construction with qh_addpoint()
    - How to handle 64-bit possible loss of data.  WARN64, ptr_intT, size_t/int
    - Show custom of qh_fprintf
    - grep 'qh_mem ' x | sort | awk '{ print $2; }' | uniq -c | grep -vE ' (2|4|6|8|10|12|14|16|20|64|162)[^0-9]'
    - qtpro/qhulltest contains .pro and Makefile.  Remove Makefiles by setting shadow directory to ../../tmp/projectname
    - Rules for use of qh_qh and multi processes
        UsingQhull
        errorIfAnotherUser
        ~QhullPoints() needs ownership of qh_qh
        Does !qh_pointer work?
        When is qh_qh required?  Minimize the time.
       qhmem, qhstat.ferr
       qhull_inuse==1 when qhull globals active [not useful?]
       rbox_inuse==1 when rbox globals active
       - Multithreaded -- call largest dimension for infinityPoint() and origin()
     - Better documentation for qhmem totshort, freesize, etc.
     - how to change .h, .c, and .cpp to text/html.  OK in Opera
     - QhullVertex.dimension() is not quite correct, epensive
     - Check globalAngleEpsilon
     - Deprecate save_qhull()
    
    [Dec 2003] Here is a partial list:
     - fix finddelaunay() in user_eg.c for tricoplanar facets
     - write a BGL, C++ interface to Qhull
         http://www.boost.org/libs/graph/doc/table_of_contents.html
     - change qh_save_qhull to swap the qhT structure instead of using pointers
     - change error handling and tracing to be independent of 'qh ferr'
     - determine the maximum width for a given set of parameters
     - prove that directed search locates all coplanar facets
     - in high-d merging, can a loop of facets become disconnected?
     - find a way to improve inner hulls in 5-d and higher
     - determine the best policy for facet visibility ('Vn')
     - determine the limitations of 'Qg'
    
    Precision improvements:
     - For 'Qt', resolve cross-linked, butterfly ridges.
         May allow retriangulation in qh_addpoint().
     - for Delaunay triangulations ('d' or 'v') under joggled input ('QJ'),
         remove vertical facets whose lowest vertex may be coplanar with convex hull
     - review use of 'Qbb' with 'd QJ'.  Is MAXabs_coord better than MAXwidth?
     - check Sugihara and Iri's better in-sphere test [Canadian
         Conf. on Comp. Geo., 1989; Univ. of Tokyo RMI 89-05]
     - replace centrum with center of mass and facet area
     - handle numeric overflow in qh_normalize and elsewhere
     - merge flipped facets into non-flipped neighbors.
         currently they merge into best neighbor (appears ok)
     - determine min norm for Cramer's rule (qh_sethyperplane_det).  It looks high.
     - improve facet width for very narrow distributions
    
    New features:
     - implement Matlab's tsearch() using Qhull
     - compute volume of Voronoi regions.  You need to determine the dual face
       graph in all dimensions [see Clarkson's hull program]
     - compute alpha shapes [see Clarkson's hull program]
     - implement deletion of Delaunay vertices
          see Devillers, ACM Symposium on Computational Geometry, Minneapolis 1999.
     - compute largest empty circle [see O'Rourke, chapter 5.5.3] [Hase]
     - list redundant (i.e., coincident) vertices [Spitz]
     - implement Mucke, et al, ['96] for point location in Delaunay triangulations
     - implement convex hull of moving points
     - implement constrained Delaunay diagrams
          see Shewchuk, ACM Symposium on Computational Geometry, Minneapolis 1998.
     - estimate outer volume of hull
     - automatically determine lower dimensional hulls
     - allow "color" data for input points
          need to insert a coordinate for Delaunay triangulations
    
    Input/output improvements:
     - Support the VTK Visualization Toolkit, http://www.kitware.com/vtk.html
     - generate output data array for Qhull library [Gautier]
     - need improved DOS window with screen fonts, scrollbar, cut/paste
     - generate Geomview output for Voronoi ridges and unbounded rays
     - generate Geomview output for halfspace intersection
     - generate Geomview display of furthest-site Voronoi diagram
     - use 'GDn' to view 5-d facets in 4-d
     - convert Geomview output for other 3-d viewers
     - add interactive output option to avoid recomputing a hull
     - orient vertex neighbors for 'Fv' in 3-d and 2-d
     - track total number of ridges for summary and logging
    
    Performance improvements:
     - optimize Qhull for 2-d Delaunay triangulations
     -   use O'Rourke's '94 vertex->duplicate_edge
     -   add bucketing
     -   better to specialize all of the code (ca. 2-3x faster w/o merging)
     - use updated LU decomposition to speed up hyperplane construction
     -        [Gill et al. 1974, Math. Comp. 28:505-35]
     - construct hyperplanes from the corresponding horizon/visible facets
     - for merging in high d, do not use vertex->neighbors
    
    

    Please let us know about your applications and improvements.


    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
    To: Qhull code: Table of Contents
    Dn: Qhull functions, macros, and data structures


    The Geometry Center Home Page

    Comments to: qhull@qhull.org
    Created: Sept. 25, 1995 --- Last modified: see changes.txt

    geometry/inst/doc/qhull/html/qhalf.html0000644000176200001440000006564713431000557017721 0ustar liggesusers qhalf -- halfspace intersection about a point

    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
    To: synopsis • input • outputs • controls • graphics • notes • conventions • options


    [halfspace]qhalf -- halfspace intersection about a point

    The intersection of a set of halfspaces is a polytope. The polytope may be unbounded. See Preparata & Shamos ['85] for a discussion. In low dimensions, halfspace intersection may be used for linear programming.

    Example: rbox c | qconvex FQ FV n | qhalf Fp

    Print the intersection of the facets of a cube. rbox c generates the vertices of a cube. qconvex FV n returns of average of the cube's vertices (in this case, the origin) and the halfspaces that define the cube. qhalf Fp computes the intersection of the halfspaces about the origin. The intersection is the vertices of the original cube.

    Example: rbox c d G0.55 | qconvex FQ FV n | qhalf Fp

    Print the intersection of the facets of a cube and a diamond. There are 24 facets and 14 intersection points. Four facets define each diamond vertex. Six facets define each cube vertex.

    Example: rbox c d G0.55 | qconvex FQ FV n | qhalf Fp Qt

    Same as above except triangulate before computing the intersection points. Three facets define each intersection point. There are two duplicates of the diamond and four duplicates of the cube.

    Example: rbox 10 s t10 | qconvex FQ FV n | qhalf Fp Fn

    Print the intersection of the facets of the convex hull of 10 cospherical points. Include the intersection points and the neighboring intersections. As in the previous examples, the intersection points are nearly the same as the original input points.

    In Qhull, a halfspace is defined by the points on or below a hyperplane. The distance of each point to the hyperplane is less than or equal to zero.

    Qhull computes a halfspace intersection by the geometric duality between points and halfspaces. See halfspace examples, qhalf notes, and option 'p' of qhalf outputs.

    Qhalf's outputs are the intersection points (Fp) and the neighboring intersection points (Fn). For random inputs, halfspace intersections are usually defined by more than d halfspaces. See the sphere example.

    You can try triangulated output ('Qt') and joggled input ('QJ'). It demonstrates that triangulated output is more accurate than joggled input.

    If you use 'Qt' (triangulated output), all halfspace intersections are simplicial (e.g., three halfspaces per intersection in 3-d). In 3-d, if more than three halfspaces intersect at the same point, triangulated output will produce duplicate intersections, one for each additional halfspace. See the third example, or add 'Qt' to the sphere example.

    If you use 'QJ' (joggled input), all halfspace intersections are simplicial. This may lead to nearly identical intersections. For example, either replace 'Qt' with 'QJ' above, or add 'QJ' to the sphere example. See Merged facets or joggled input.

    The 'qhalf' program is equivalent to 'qhull H' in 2-d to 4-d, and 'qhull H Qx' in 5-d and higher. It disables the following Qhull options: d n v Qbb QbB Qf Qg Qm Qr QR Qv Qx Qz TR E V Fa FA FC FD FS Ft FV Gt Q0,etc.

    Copyright © 1995-2018 C.B. Barber


    »qhalf synopsis

    qhalf- halfspace intersection about a point.
        input (stdin): [dim, 1, interior point]
                       dim+1, n
                       halfspace coefficients + offset
        comments start with a non-numeric character
    
    options (qhalf.html):
        Hn,n - specify coordinates of interior point
        Qt   - triangulated output
        QJ   - joggle input instead of merging facets
        Tv   - verify result: structure, convexity, and redundancy
        .    - concise list of all options
        -    - one-line description of all options
    
    output options (subset):
        s    - summary of results (default)
        Fp   - intersection coordinates
        Fv   - non-redundant halfspaces incident to each intersection
        Fx   - non-redundant halfspaces
        o    - OFF file format (dual convex hull)
        G    - Geomview output (dual convex hull)
        m    - Mathematica output (dual convex hull)
        QVn  - print intersections for halfspace n, -n if not
        TO file - output results to file, may be enclosed in single quotes
    
    examples:
        rbox d | qconvex n | qhalf s H0,0,0 Fp
        rbox c | qconvex FV n | qhalf s i
        rbox c | qconvex FV n | qhalf s o
    

    »qhalf input

    The input data on stdin consists of:

    • [optional] interior point
      • dimension
      • 1
      • coordinates of interior point
    • dimension + 1
    • number of halfspaces
    • halfspace coefficients followed by offset

    Use I/O redirection (e.g., qhalf < data.txt), a pipe (e.g., rbox c | qconvex FV n | qhalf), or the 'TI' option (e.g., qhalf TI data.txt).

    Qhull needs an interior point to compute the halfspace intersection. An interior point is clearly inside all of the halfspaces. A point is inside a halfspace if its distance to the corresponding hyperplane is negative.

    The interior point may be listed at the beginning of the input (as shown above). If not, option 'Hn,n' defines the interior point as [n,n,0,...] where 0 is the default coordinate (e.g., 'H0' is the origin). Use linear programming if you do not know the interior point (see halfspace notes),

    The input to qhalf is a set of halfspaces that are defined by their hyperplanes. Each halfspace is defined by d coefficients followed by a signed offset. This defines a linear inequality. The coefficients define a vector that is normal to the halfspace. The vector may have any length. If it has length one, the offset is the distance from the origin to the halfspace's boundary. Points in the halfspace have a negative distance to the hyperplane. The distance from the interior point to each halfspace is likewise negative.

    The halfspace format is the same as Qhull's output options 'n', 'Fo', and 'Fi'. Use option 'Fd' to use cdd format for the halfspaces.

    For example, here is the input for computing the intersection of halfplanes that form a cube.

    rbox c | qconvex FQ FV n TO data

    RBOX c | QCONVEX FQ FV n
    3 1
         0      0      0
    4
    6
         0      0     -1   -0.5
         0     -1      0   -0.5
         1      0      0   -0.5
        -1      0      0   -0.5
         0      1      0   -0.5
         0      0      1   -0.5
    

    qhalf s Fp < data

    
    Halfspace intersection by the convex hull of 6 points in 3-d:
    
      Number of halfspaces: 6
      Number of non-redundant halfspaces: 6
      Number of intersection points: 8
    
    Statistics for: RBOX c | QCONVEX FQ FV n | QHALF s Fp
    
      Number of points processed: 6
      Number of hyperplanes created: 11
      Number of distance tests for qhull: 11
      Number of merged facets: 1
      Number of distance tests for merging: 45
      CPU seconds to compute hull (after input):  0
    
    3
    3
    8
      -0.5    0.5    0.5
       0.5    0.5    0.5
      -0.5    0.5   -0.5
       0.5    0.5   -0.5
       0.5   -0.5    0.5
      -0.5   -0.5    0.5
      -0.5   -0.5   -0.5
       0.5   -0.5   -0.5
    

    »qhalf outputs

    The following options control the output for halfspace intersection.

     
    Intersections
    FN
    list intersection points for each non-redundant halfspace. The first line is the number of non-redundant halfspaces. Each remaining lines starts with the number of intersection points. For the cube example, each halfspace has four intersection points.
    Fn
    list neighboring intersections for each intersection point. The first line is the number of intersection points. Each remaining line starts with the number of neighboring intersections. For the cube example, each intersection point has three neighboring intersections.

    In 3-d, a non-simplicial intersection has more than three neighboring intersections. For random data (e.g., the sphere example), non-simplicial intersections are the norm. Option 'Qt' produces three neighboring intersections per intersection by duplicating the intersection points. Option QJ' produces three neighboring intersections per intersection by joggling the hyperplanes and hence their intersections.

    Fp
    print intersection coordinates. The first line is the dimension and the second line is the number of intersection points. The following lines are the coordinates of each intersection.
    FI
    list intersection IDs. The first line is the number of intersections. The IDs follow, one per line.
     
     
    Halfspaces
    Fx
    list non-redundant halfspaces. The first line is the number of non-redundant halfspaces. The other lines list one halfspace per line. A halfspace is non-redundant if it defines a facet of the intersection. Redundant halfspaces are ignored. For the cube example, all of the halfspaces are non-redundant.
    Fv
    list non-redundant halfspaces incident to each intersection point. The first line is the number of non-redundant halfspaces. Each remaining line starts with the number of non-redundant halfspaces. For the cube example, each intersection is incident to three halfspaces.
    i
    list non-redundant halfspaces incident to each intersection point. The first line is the number of intersection points. Each remaining line lists the incident, non-redundant halfspaces. For the cube example, each intersection is incident to three halfspaces.
    Fc
    list coplanar halfspaces for each intersection point. The first line is the number of intersection points. Each remaining line starts with the number of coplanar halfspaces. A coplanar halfspace is listed for one intersection point even though it is coplanar to multiple intersection points.
    Qi Fc
    list redundant halfspaces for each intersection point. The first line is the number of intersection points. Each remaining line starts with the number of redundant halfspaces. Use options 'Qc Qi Fc' to list coplanar and redundant halfspaces.
     
     
    General
    s
    print summary for the halfspace intersection. Use 'Fs' if you need numeric data.
    o
    print vertices and facets of the dual convex hull. The first line is the dimension. The second line is the number of vertices, facets, and ridges. The vertex coordinates are next, followed by the facets, one per line.
    p
    print vertex coordinates of the dual convex hull. Each vertex corresponds to a non-redundant halfspace. Its coordinates are the negative of the hyperplane's coefficients divided by the offset plus the inner product of the coefficients and the interior point (-c/(b+a.p). Options 'p Qc' includes coplanar halfspaces. Options 'p Qi' includes redundant halfspaces.
    m
    Mathematica output for the dual convex hull in 2-d or 3-d.
    FM
    Maple output for the dual convex hull in 2-d or 3-d.
    G
    Geomview output for the dual convex hull in 2-d, 3-d, or 4-d.

    »qhalf controls

    These options provide additional control:

    Qt
    triangulated output. If a 3-d intersection is defined by more than three hyperplanes, Qhull produces duplicate intersections -- one for each extra hyperplane.
    QJ
    joggle the input instead of merging facets. In 3-d, this guarantees that each intersection is defined by three hyperplanes.
    f
    facet dump. Print the data structure for each intersection (i.e., facet)
    TFn
    report summary after constructing n intersections
    QVn
    select intersection points for halfspace n (marked 'good')
    QGn
    select intersection points that are visible to halfspace n (marked 'good'). Use -n for the remainder.
    Qbk:0Bk:0
    remove the k-th coordinate from the input. This computes the halfspace intersection in one lower dimension.
    Tv
    verify result
    TI file
    input data from file. The filename may not use spaces or quotes.
    TO file
    output results to file. Use single quotes if the filename contains spaces (e.g., TO 'file with spaces.txt'
    Qs
    search all points for the initial simplex. If Qhull can not construct an initial simplex, it reports a descriptive message. Usually, the point set is degenerate and one or more dimensions should be removed ('Qbk:0Bk:0'). If not, use option 'Qs'. It performs an exhaustive search for the best initial simplex. This is expensive is high dimensions.

    »qhalf graphics

    To view the results with Geomview, compute the convex hull of the intersection points ('qhull FQ H0 Fp | qhull G'). See Halfspace examples.

    »qhalf notes

    See halfspace intersection for precision issues related to qhalf.

    If you do not know an interior point for the halfspaces, use linear programming to find one. Assume, n halfspaces defined by: aj*x1+bj*x2+cj*x3+dj<=0, j=1..n. Perform the following linear program:

    max(x5) aj*x1+bj*x2+cj*x3+dj*x4+x5<=0, j=1..n

    Then, if [x1,x2,x3,x4,x5] is an optimal solution with x4>0 and x5>0 we get:

    aj*(x1/x4)+bj*(x2/x4)+cj*(x3/x4)+dj<=(-x5/x4) j=1..n and (-x5/x4)<0,

    and conclude that the point [x1/x4,x2/x4,x3/x4] is in the interior of all the halfspaces. Since x5 is optimal, this point is "way in" the interior (good for precision errors).

    After finding an interior point, the rest of the intersection algorithm is from Preparata & Shamos ['85, p. 316, "A simple case ..."]. Translate the halfspaces so that the interior point is the origin. Calculate the dual polytope. The dual polytope is the convex hull of the vertices dual to the original faces in regard to the unit sphere (i.e., halfspaces at distance d from the origin are dual to vertices at distance 1/d). Then calculate the resulting polytope, which is the dual of the dual polytope, and translate the origin back to the interior point [S. Spitz, S. Teller, D. Strawn].

    »qhalf conventions

    The following terminology is used for halfspace intersection in Qhull. This is the hardest structure to understand. The underlying structure is a convex hull with one vertex per non-redundant halfspace. See convex hull conventions and Qhull's data structures.

    • interior point - a point in the intersection of the halfspaces. Qhull needs an interior point to compute the intersection. See halfspace input.
    • halfspace - d coordinates for the normal and a signed offset. The distance to an interior point is negative.
    • non-redundant halfspace - a halfspace that defines an output facet
    • vertex - a dual vertex in the convex hull corresponding to a non-redundant halfspace
    • coplanar point - the dual point corresponding to a similar halfspace
    • interior point - the dual point corresponding to a redundant halfspace
    • intersection point- the intersection of d or more non-redundant halfspaces
    • facet - a dual facet in the convex hull corresponding to an intersection point
    • non-simplicial facet - more than d halfspaces intersect at a point
    • good facet - an intersection point that satisfies restriction 'QVn', etc.

    »qhalf options

    qhalf- compute the intersection of halfspaces about a point
        http://www.qhull.org
    
    input (stdin):
        optional interior point: dimension, 1, coordinates
        first lines: dimension+1 and number of halfspaces
        other lines: halfspace coefficients followed by offset
        comments:    start with a non-numeric character
    
    options:
        Hn,n - specify coordinates of interior point
        Qt   - triangulated ouput
        QJ   - joggle input instead of merging facets
        Qc   - keep coplanar halfspaces
        Qi   - keep other redundant halfspaces
    
    Qhull control options:
        QJn  - randomly joggle input in range [-n,n]
        Qbk:0Bk:0 - remove k-th coordinate from input
        Qs   - search all halfspaces for the initial simplex
        QGn  - print intersection if redundant to halfspace n, -n for not
        QVn  - print intersections for halfspace n, -n if not
    
    Trace options:
        T4   - trace at level n, 4=all, 5=mem/gauss, -1= events
        Tc   - check frequently during execution
        Ts   - print statistics
        Tv   - verify result: structure, convexity, and redundancy
        Tz   - send all output to stdout
        TFn  - report summary when n or more facets created
        TI file - input data from file, no spaces or single quotes
        TO file - output results to file, may be enclosed in single quotes
        TPn  - turn on tracing when halfspace n added to intersection
        TMn  - turn on tracing at merge n
        TWn  - trace merge facets when width > n
        TVn  - stop qhull after adding halfspace n, -n for before (see TCn)
        TCn  - stop qhull after building cone for halfspace n (see TVn)
    
    Precision options:
        Cn   - radius of centrum (roundoff added).  Merge facets if non-convex
         An  - cosine of maximum angle.  Merge facets if cosine > n or non-convex
               C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge
        Rn   - randomly perturb computations by a factor of [1-n,1+n]
        Un   - max distance below plane for a new, coplanar halfspace
        Wn   - min facet width for outside halfspace (before roundoff)
    
    Output formats (may be combined; if none, produces a summary to stdout):
        f    - facet dump
        G    - Geomview output (dual convex hull)
        i    - non-redundant halfspaces incident to each intersection
        m    - Mathematica output (dual convex hull)
        o    - OFF format (dual convex hull: dimension, points, and facets)
        p    - vertex coordinates of dual convex hull (coplanars if 'Qc' or 'Qi')
        s    - summary (stderr)
    
    More formats:
        Fc   - count plus redundant halfspaces for each intersection
             -   Qc (default) for coplanar and Qi for other redundant
        Fd   - use cdd format for input (homogeneous with offset first)
        FF   - facet dump without ridges
        FI   - ID of each intersection
        Fm   - merge count for each intersection (511 max)
        FM   - Maple output (dual convex hull)
        Fn   - count plus neighboring intersections for each intersection
        FN   - count plus intersections for each non-redundant halfspace
        FO   - options and precision constants
        Fp   - dim, count, and intersection coordinates
        FP   - nearest halfspace and distance for each redundant halfspace
        FQ   - command used for qhalf
        Fs   - summary: #int (8), dim, #halfspaces, #non-redundant, #intersections
                          for output: #non-redundant, #intersections, #coplanar
                                      halfspaces, #non-simplicial intersections
                        #real (2), max outer plane, min vertex
        Fv   - count plus non-redundant halfspaces for each intersection
        Fx   - non-redundant halfspaces
    
    Geomview output (2-d, 3-d and 4-d; dual convex hull)
        Ga   - all points (i.e., transformed halfspaces) as dots
         Gp  -  coplanar points and vertices as radii
         Gv  -  vertices (i.e., non-redundant halfspaces) as spheres
        Gi   - inner planes (i.e., halfspace intersections) only
         Gn  -  no planes
         Go  -  outer planes only
        Gc   - centrums
        Gh   - hyperplane intersections
        Gr   - ridges
        GDn  - drop dimension n in 3-d and 4-d output
    
    Print options:
        PAn  - keep n largest facets (i.e., intersections) by area
        Pdk:n- drop facet if normal[k] <= n (default 0.0)
        PDk:n- drop facet if normal[k] >= n
        Pg   - print good facets (needs 'QGn' or 'QVn')
        PFn  - keep facets whose area is at least n
        PG   - print neighbors of good facets
        PMn  - keep n facets with most merges
        Po   - force output.  If error, output neighborhood of facet
        Pp   - do not report precision problems
    
        .    - list of all options
        -    - one line descriptions of all options
    

    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
    To: synopsis • input • outputs • controls • graphics • notes • conventions • options


    The Geometry Center Home Page

    Comments to: qhull@qhull.org
    Created: Sept. 25, 1995 --- Last modified: see top

    geometry/inst/doc/qhull/html/qh--rand.gif0000644000176200001440000000744313431000556020023 0ustar liggesusersGIF87add=*2-E>,^0GI*ccNk_(*&^?DN2A>L0Be(E@IoJnI6-LFuBm+:DU2 )VT!~_ I!F>99=;`G #(5"nW]hlAN a]/D"E4fesjaEPF[I*1'RVv *fFBFIML4L*:#_@DK2-fmoM+U9+$DN=}uuUL&??=%)F$BU<;?|[5?4>-E;K87'F 4j{ov=eF5z4tzet+UGTF8l7:TMX}<9[BO:#'*UlC63(ng*7f".b-d-2hA3GF`XeHETH:^N+Sie?`:3ZK2M[]L4\";'Mk_hI;VK<:iG[.[vR<4-BFtydV>J1#'6@9E :&jc,C89T'yeUe-t@CcV2ht:-;?3RRRsBGJ>E-GT*zqEffr)>L!RfO>yE|wJZ|ZL(Z)04@*+7..Q2FIMT`)3 8877b3%:(/W,M3ilDg^xc`V;8@B@`JO2A=tjZAu*G`Ye5(N+\h,dd H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0c|&32*jĩW<}()CjW^6&ߕ+ʕomkuJ&NOU)Um^5>ZkK †A6FK.-e>}ccbnˌ[~)Ť+.fW9hlPD5e9[Fxmܚڴi+R$ xW.w 1'(Ơ&HPGJ9[OgqĀ7d`*,k $ݧO2>(Ac9 ,,~()%hSLU <$c x`"Ȣ$qʂAIY3P$'ָ@\'Z@5$rB )5 $83L8d쩩7jq5p-T3S.A4n^4ndbje-SƧ9KI5 l T:p~ {DR@lG7l)4R|F>nɶdZ"L̢Ha9.*3̘@$S (a,njE.TjJc OqH+lau་+%P3֜% O;͆.`4!Oa7!J˒|mI7sԻ쒪 rW;pTi20ؾ" ,I@N!nJݨ p 3:RL.-f p_@/oq yB\nPObq6 x'[(F6G%x:%$W&ސQ>2TIdK"Y:j_#HA gK(. ' \xEq@$(0_1(ҊGXYdf7 9KfbXȶU P0p \\%AhE3jZÒ!s VˍXke"q`-I90Sj$%jBes; E$".mm Uܒ_>MF"֤oX Ǧ[IxMnr8VBq06W>dc8 *x_(;a ^]c J80ұ ]$>B)}mjǵZCN0C' sy>-u(gl@!{B ah(%lQr;hpY y5>0~@ Glb?UK $  s@~~pzaVc|pp X@ c}q gz|n p 0xt 1|<ȃbHpGx0| Vh_`J Tfn{1J RhthPm BP6PHPX+xE (zsxVa@|j@Pc (  Y8 @$0 j@R8J Xv7s(` @ ÀR苄|hH ` f {Ҩ0 ؋@3p+@ _p@̨ ސ@(0}m v ppvXt 0 IH(G@ Qhull Downloads

    Up: Qhull Home Page


    [CONE] Qhull Downloads


    Up: Qhull Home Page


    [HOME] The Geometry Center Home Page

    Comments to: qhull@qhull.org
    geometry/inst/doc/qhull/html/qh-faq.html0000644000176200001440000014667513431000557020004 0ustar liggesusers Qhull FAQ

    Up: Home page for Qhull (http://www.qhull.org)
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
    To: FAQ: Table of Contents (please wait while loading)


    [4-d cube] Frequently Asked Questions about Qhull

    If your question does not appear here, see:

    Qhull is a general dimension code for computing convex hulls, Delaunay triangulations, halfspace intersections about a point, Voronoi diagrams, furthest-site Delaunay triangulations, and furthest-site Voronoi diagrams. These structures have applications in science, engineering, statistics, and mathematics. For a detailed introduction, see O'Rourke ['94], Computational Geometry in C.

    There are separate programs for each application of Qhull. These programs disable experimental and inappropriate options. If you prefer, you may use Qhull directly. All programs run the same code.

    Version 3.1 added triangulated output ('Qt'). It should be used for Delaunay triangulations instead of using joggled input ('QJ').

    Brad Barber, Arlington MA, 2010/01/09

    Copyright © 1998-2018 C.B. Barber


    »FAQ: Table of Contents

    Within each category, the most recently asked questions are first.

    • Startup questions
      • How do I run Qhull from Windows?
      • How do I enter points for Qhull?
      • How do I learn to use Qhull?
    • Convex hull questions
      • How do I report just the area and volume of a convex hull?
      • Why are there extra points in a 4-d or higher convex hull?
      • How do I report duplicate vertices?
    • Delaunay triangulation questions
      • How do I get rid of nearly flat Delaunay triangles?
      • How do I find the Delaunay triangle or Voronoi region that is closest to a point?
      • How do I compute the Delaunay triangulation of a non-convex object?
      • How do I mesh a volume from a set of triangulated surface points?
      • Can Qhull produce a triangular mesh for an object?
      • For 3-d Delaunay triangulations, how do I report the triangles of each tetrahedron?
      • How do I construct a 3-d Delaunay triangulation?
      • How do I get the triangles for a 2-d Delaunay triangulation and the vertices of its Voronoi diagram?
      • Can Qhull triangulate a hundred 16-d points?
    • Voronoi diagram questions
      • See also "Delaunay diagram questions". Qhull computes the Voronoi diagram from the Delaunay triagulation.
      • How do I compute the volume of a Voronoi region?
      • How do I get the radii of the empty spheres for each Voronoi vertex?
      • What is the Voronoi diagram of a square?
      • How do I construct the Voronoi diagram of cospherical points?
      • Can Qhull compute the unbounded rays of the Voronoi diagram?
    • Approximation questions
      • How do I approximate data with a simplex?
    • Halfspace questions
      • How do I compute the intersection of halfspaces with Qhull?
    • Qhull library questions
      • Is Qhull available for Mathematica, Matlab, or Maple?
      • Why are there too few ridges?
      • Can Qhull use coordinates without placing them in a data file?
      • How large are Qhull's data structures?
      • Can Qhull construct convex hulls and Delaunay triangulations one point at a time?
      • How do I visit the ridges of a Delaunay triangulation?
      • How do I visit the Delaunay facets?
      • When is a point outside or inside a facet?
      • How do I find the facet that is closest to a point?
      • How do I find the Delaunay triangle or Voronoi region that is closest to a point?
      • How do I list the vertices?
      • How do I test code that uses the Qhull library?
      • When I compute a plane equation from a facet, I sometimes get an outward-pointing normal and sometimes an inward-pointing normal

    »Startup questions

    »How do I run Qhull from Windows?

    Qhull is a console program. You will first need a command window (i.e., a "command prompt"). You can double click on 'eg\Qhull-go.bat'.

    • Type 'qconvex', 'qdelaunay', 'qhalf', 'qvoronoi, 'qhull', and 'rbox' for a synopsis of each program.
    • Type 'rbox c D2 | qconvex s i' to compute the convex hull of a square.
    • Type 'rbox c D2 | qconvex s i TO results.txt' to write the results to the file 'results.txt'. A summary is still printed on the the console.
    • Type 'rbox c D2' to see the input format for qconvex.
    • Type 'qconvex < data.txt s i TO results.txt' to read input data from 'data.txt'.
    • If you want to enter data by hand, type 'qconvex s i TO results.txt' to read input data from the console. Type in the numbers and end with a ctrl-D.

    »How do I enter points for Qhull?

    Qhull takes its data from standard input. For example, create a file named 'data.txt' with the following contents:

    2  #sample 2-d input
    5  #number of points
    1 2  #coordinates of points
    -1.1 3
    3 2.2
    4 5
    -10 -10
    

    Then call qconvex with 'qconvex < data.txt'. It will print a summary of the convex hull. Use 'qconvex < data.txt o' to print the vertices and edges. See also input format.

    You can generate sample data with rbox, e.g., 'rbox 10' generates 10 random points in 3-d. Use a pipe ('|') to run rbox and qhull together, e.g.,

    rbox c | qconvex o

    computes the convex hull of a cube.

    »How do I learn to use Qhull?

    First read:

    Look at Qhull's on-line documentation:

    • 'qconvex' gives a synopsis of qconvex and its options
    • 'rbox' lists all of the options for generating point sets
    • 'qconvex - | more' lists the options for qconvex
    • 'qconvex .' gives a concise list of options
    • 'qdelaunay', 'qhalf', 'qvoronoi', and 'qhull' also have a synopsis and option list

    Then try out the Qhull programs on small examples.

    • 'rbox c' lists the vertices of a cube
    • 'rbox c | qconvex' is the convex hull of a cube
    • 'rbox c | qconvex o' lists the vertices and facets of a cube
    • 'rbox c | qconvex Qt o' triangulates the cube
    • 'rbox c | qconvex QJ o' joggles the input and triangulates the cube
    • 'rbox c D2 | qconvex' generates the convex hull of a square
    • 'rbox c D4 | qconvex' generates the convex hull of a hypercube
    • 'rbox 6 s D2 | qconvex p Fx' lists 6 random points in a circle and lists the vertices of their convex hull in order
    • 'rbox c D2 c G2 | qdelaunay' computes the Delaunay triangulation of two embedded squares. It merges the cospherical facets.
    • 'rbox c D2 c G2 | qdelaunay Qt' computes the Delaunay triangulation of two embedded squares. It triangulates the cospherical facets.
    • 'rbox c D2 c G2 | qvoronoi o' computes the corresponding Voronoi vertices and regions.
    • 'rbox c D2 c G2 | qvoronio Fv' shows the Voronoi diagram for the previous example. Each line is one edge of the diagram. The first number is 4, the next two numbers list a pair of input sites, and the last two numbers list the corresponding pair of Voronoi vertices.

    Install Geomview if you are running SGI Irix, Solaris, SunOS, Linux, HP, IBM RS/6000, DEC Alpha, or Next. You can then visualize the output of Qhull. Qhull comes with Geomview examples.

    Then try Qhull with a small example of your application. Work out the results by hand. Then experiment with Qhull's options to find the ones that you need.

    You will need to decide how Qhull should handle precision problems. It can triangulate the output ('Qt'), joggle the input ('QJ'), or merge facets (the default).

    • With joggle, Qhull produces simplicial (i.e., triangular) output by joggling the input. After joggle, no points are cocircular or cospherical.
    • With facet merging, Qhull produces a better approximation and does not modify the input.
    • With triangulated output, Qhull merges facets and triangulates the result.
    • See Merged facets or joggled input.

    »Convex hull questions

    »How do I report just the area and volume of a convex hull?

    Use option 'FS'. For example,
    C:\qhull>rbox 10 | qconvex FS
    0
    2 2.192915621644613 0.2027867899638665
    
    C:\qhull>rbox 10 | qconvex FA
    
    Convex hull of 10 points in 3-d:
    
      Number of vertices: 10
      Number of facets: 16
    
    Statistics for: RBOX 10 | QCONVEX FA
    
      Number of points processed: 10
      Number of hyperplanes created: 28
      Number of distance tests for qhull: 44
      CPU seconds to compute hull (after input):  0
      Total facet area:   2.1929156
      Total volume:       0.20278679
    

    »Why are there extra points in a 4-d or higher convex hull?

    You may see extra points if you use options 'i' or 'Ft' without using triangulated output ('Qt'). The extra points occur when a facet is non-simplicial (i.e., a facet with more than d vertices). For example, Qhull reports the following for one facet of the convex hull of a hypercube. Option 'Pd0:0.5' returns the facet along the positive-x axis:

    rbox c D4 | qconvex i Pd0:0.5
    12
    17 13 14 15
    17 13 12 14
    17 11 13 15
    17 14 11 15
    17 10 11 14
    17 14 12 8
    17 12 13 8
    17 10 14 8
    17 11 10 8
    17 13 9 8
    17 9 11 8
    17 11 9 13
    

    The 4-d hypercube has 16 vertices; so point "17" was added by qconvex. Qhull adds the point in order to report a simplicial decomposition of the facet. The point corresponds to the "centrum" which Qhull computes to test for convexity.

    Triangulate the output ('Qt') to avoid the extra points. Since the hypercube is 4-d, each simplicial facet is a tetrahedron.

    C:\qhull3.1>rbox c D4 | qconvex i Pd0:0.5 Qt
    9
    9 13 14 15
    12 9 13 14
    9 11 13 15
    11 9 14 15
    9 10 11 14
    12 9 14 8
    9 12 13 8
    9 10 14 8
    10 9 11 8
    

    Use the 'Fv' option to print the vertices of simplicial and non-simplicial facets. For example, here is the same hypercube facet with option 'Fv' instead of 'i':

    C:\qhull>rbox c D4 | qconvex Pd0:0.5 Fv
    1
    8 9 10 12 11 13 14 15 8
    

    The coordinates of the extra point are printed with the 'Ft' option.

    rbox c D4 | qconvex Pd0:0.5 Ft
    4
    17 12 3
      -0.5   -0.5   -0.5   -0.5
      -0.5   -0.5   -0.5    0.5
      -0.5   -0.5    0.5   -0.5
      -0.5   -0.5    0.5    0.5
      -0.5    0.5   -0.5   -0.5
      -0.5    0.5   -0.5    0.5
      -0.5    0.5    0.5   -0.5
      -0.5    0.5    0.5    0.5
       0.5   -0.5   -0.5   -0.5
       0.5   -0.5   -0.5    0.5
       0.5   -0.5    0.5   -0.5
       0.5   -0.5    0.5    0.5
       0.5    0.5   -0.5   -0.5
       0.5    0.5   -0.5    0.5
       0.5    0.5    0.5   -0.5
       0.5    0.5    0.5    0.5
       0.5      0      0      0
    4 16 13 14 15
    4 16 13 12 14
    4 16 11 13 15
    4 16 14 11 15
    4 16 10 11 14
    4 16 14 12 8
    4 16 12 13 8
    4 16 10 14 8
    4 16 11 10 8
    4 16 13 9 8
    4 16 9 11 8
    4 16 11 9 13
    

    »How do I report duplicate vertices?

    There's no direct way. You can use option 'FP' to report the distance to the nearest vertex for coplanar input points. Select the minimum distance for a duplicated vertex, and locate all input sites less than this distance.

    For Delaunay triangulations, all coplanar points are nearly incident to a vertex. If you want a report of coincident input sites, do not use option 'QJ'. By adding a small random quantity to each input coordinate, it prevents coincident input sites.

    »Delaunay triangulation questions

    »How do I get rid of nearly flat Delaunay triangles?

    Nearly flat triangles occur when boundary points are nearly collinear or coplanar. They also occur for nearly coincident points. Both events can easily occur when using joggle. For example (rbox 10 W0 D2 | qdelaunay QJ Fa) lists the areas of the Delaunay triangles of 10 points on the boundary of a square. Some of these triangles are nearly flat. This occurs when one point is joggled inside of two other points. In this case, nearly flat triangles do not occur with triangulated output (rbox 10 W0 D2 | qdelaunay Qt Fa).

    Another example, (rbox c P0 P0 D2 | qdelaunay QJ Fa), computes the areas of the Delaunay triangles for the unit square and two instances of the origin. Four of the triangles have an area of 0.25 while two have an area of 2.0e-11. The later are due to the duplicated origin. With triangulated output (rbox c P0 P0 D2 | qdelaunay Qt Fa) there are four triangles of equal area.

    Nearly flat triangles also occur without using joggle. For example, (rbox c P0 P0,0.4999999999 | qdelaunay Fa), computes the areas of the Delaunay triangles for the unit square, a nearly collinear point, and the origin. One triangle has an area of 3.3e-11.

    Unfortunately, none of Qhull's merging options remove nearly flat Delaunay triangles due to nearly collinear or coplanar boundary points. The merging options concern the empty circumsphere property of Delaunay triangles. This is independent of the area of the Delaunay triangles. Qhull does handle nearly coincident points.

    If you are calling Qhull from a program, you can merge slivers into an adjacent facet. In d dimensions with simplicial facets (e.g., from 'Qt'), each facet has d+1 neighbors. Each neighbor shares d vertices of the facet's d+1 vertices. Let the other vertex be the opposite vertex. For each neighboring facet, if its circumsphere includes the opposite.vertex, the two facets can be merged. [M. Treacy]

    You can handle collinear or coplanar boundary points by enclosing the points in a box. For example, (rbox c P0 P0,0.4999999999 c G1 | qdelaunay Fa), surrounds the previous points with [(1,1), (1,-1), (-1,-1), (-1, 1)]. Its Delaunay triangulation does not include a nearly flat triangle. The box also simplifies the graphical output from Qhull.

    Without joggle, Qhull lists coincident points as "coplanar" points. For example, (rbox c P0 P0 D2 | qdelaunay Fa), ignores the duplicated origin and lists four triangles of size 0.25. Use 'Fc' to list the coincident points (e.g., rbox c P0 P0 D2 | qdelaunay Fc).

    There is no easy way to determine coincident points with joggle. Joggle removes all coincident, cocircular, and cospherical points before running Qhull. Instead use facet merging (the default) or triangulated output ('Qt').

    »How do I compute the Delaunay triangulation of a non-convex object?

    A similar question is "How do I mesh a volume from a set of triangulated surface points?"

    This is an instance of the constrained Delaunay Triangulation problem. Qhull does not handle constraints. The boundary of the Delaunay triangulation is always convex. But if the input set contains enough points, the triangulation will include the boundary. The number of points needed depends on the input.

    Shewchuk has developed a theory of constrained Delaunay triangulations. See his paper at the 1998 Computational Geometry Conference. Using these ideas, constraints could be added to Qhull. They would have many applications.

    There is a large literature on mesh generation and many commercial offerings. For pointers see Owen's International Meshing Roundtable and Schneiders' Finite Element Mesh Generation page.

    »Can Qhull produce a triangular mesh for an object?

    Yes for convex objects, no for non-convex objects. For non-convex objects, it triangulates the concavities. Unless the object has many points on its surface, triangles may cross the surface.

    »For 3-d Delaunay triangulations, how do I report the triangles of each tetrahedron?

    For points in general position, a 3-d Delaunay triangulation generates tetrahedron. Each face of a tetrahedron is a triangle. For example, the 3-d Delaunay triangulation of random points on the surface of a cube, is a cellular structure of tetrahedron.

    Use triangulated output ('qdelaunay Qt i') or joggled input ('qdelaunay QJ i') to generate the Delaunay triangulation. Option 'i' reports each tetrahedron. The triangles are every combination of 3 vertices. Each triangle is a "ridge" of the Delaunay triangulation.

    For example,

            rbox 10 | qdelaunay Qt i
            14
            9 5 8 7
            0 9 8 7
            5 3 8 7
            3 0 8 7
            5 4 8 1
            4 6 8 1
            2 9 5 8
            4 2 5 8
            4 2 9 5
            6 2 4 8
            9 2 0 8
            2 6 0 8
            2 4 9 1
            2 6 4 1
    

    is the Delaunay triangulation of 10 random points. Ridge 9-5-8 occurs twice. Once for tetrahedron 9 5 8 7 and the other for tetrahedron 2 9 5 8.

    You can also use the Qhull library to generate the triangles. See "How do I visit the ridges of a Delaunay triangulation?"

    »How do I construct a 3-d Delaunay triangulation?

    For 3-d Delaunay triangulations with cospherical input sites, use triangulated output ('Qt') or joggled input ('QJ'). Otherwise option 'i' will triangulate non-simplicial facets by adding a point to the facet.

    If you want non-simplicial output for cospherical sites, use option 'Fv' or 'o'. For option 'o', ignore the last coordinate. It is the lifted coordinate for the corresponding convex hull in 4-d.

    The following example is a cube inside a tetrahedron. The 8-vertex facet is the cube. Ignore the last coordinates.

    C:\qhull>rbox r y c G0.1 | qdelaunay Fv
    4
    12 20 44
       0.5      0      0 0.3055555555555555
       0    0.5      0 0.3055555555555555
       0      0    0.5 0.3055555555555555
      -0.5   -0.5   -0.5 0.9999999999999999
      -0.1   -0.1   -0.1 -6.938893903907228e-018
      -0.1   -0.1    0.1 -6.938893903907228e-018
      -0.1    0.1   -0.1 -6.938893903907228e-018
      -0.1    0.1    0.1 -6.938893903907228e-018
       0.1   -0.1   -0.1 -6.938893903907228e-018
       0.1   -0.1    0.1 -6.938893903907228e-018
       0.1    0.1   -0.1 -6.938893903907228e-018
       0.1    0.1    0.1 -6.938893903907228e-018
    4 2 11 1 0
    4 10 1 0 3
    4 11 10 1 0
    4 2 9 0 3
    4 9 11 2 0
    4 7 2 1 3
    4 11 7 2 1
    4 8 10 0 3
    4 9 8 0 3
    5 8 9 10 11 0
    4 10 6 1 3
    4 6 7 1 3
    5 6 8 10 4 3
    5 6 7 10 11 1
    4 5 9 2 3
    4 7 5 2 3
    5 5 8 9 4 3
    5 5 6 7 4 3
    8 5 6 8 7 9 10 11 4
    5 5 7 9 11 2
    

    If you want simplicial output use options 'Qt i' or 'QJ i', e.g.,

    rbox r y c G0.1 | qdelaunay Qt i
    31
    2 11 1 0
    11 10 1 0
    9 11 2 0
    11 7 2 1
    8 10 0 3
    9 8 0 3
    10 6 1 3
    6 7 1 3
    5 9 2 3
    7 5 2 3
    9 8 10 11
    8 10 11 0
    9 8 11 0
    6 8 10 4
    8 6 10 3
    6 8 4 3
    6 7 10 11
    10 6 11 1
    6 7 11 1
    8 5 4 3
    5 8 9 3
    5 6 4 3
    6 5 7 3
    5 9 10 11
    8 5 9 10
    7 5 10 11
    5 6 7 10
    8 5 10 4
    5 6 10 4
    5 9 11 2
    7 5 11 2
    

    »How do I get the triangles for a 2-d Delaunay triangulation and the vertices of its Voronoi diagram?

    To compute the Delaunay triangles indexed by the indices of the input sites, use

    rbox 10 D2 | qdelaunay Qt i

    To compute the Voronoi vertices and the Voronoi region for each input site, use

    rbox 10 D2 | qvoronoi o

    To compute each edge ("ridge") of the Voronoi diagram for each pair of adjacent input sites, use

    rbox 10 D2 | qvoronoi Fv

    To compute the area and volume of the Voronoi region for input site 5 (site 0 is the first one), use

    rbox 10 D2 | qvoronoi QV5 p | qconvex s FS

    To compute the lines ("hyperplanes") that define the Voronoi region for input site 5, use

    rbox 10 D2 | qvoronoi QV5 p | qconvex n

    or

    rbox 10 D2 | qvoronoi QV5 Fi Fo

    To list the extreme points of the input sites use

    rbox 10 D2 | qdelaunay Fx

    You will get the same point ids with

    rbox 10 D2 | qconvex Fx

    »Can Qhull triangulate a hundred 16-d points?

    No. This is an immense structure. A triangulation of 19, 16-d points has 43 simplices. If you add one point at a time, the triangulation increased as follows: 43, 189, 523, 1289, 2830, 6071, 11410, 20487. The last triangulation for 26 points used 13 megabytes of memory. When Qhull uses virtual memory, it becomes too slow to use.

    »Voronoi diagram questions

    »How do I compute the volume of a Voronoi region?

    For each Voronoi region, compute the convex hull of the region's Voronoi vertices. The volume of each convex hull is the volume of the corresponding Vornoi region.

    For example, to compute the volume of the bounded Voronoi region about [0,0,0]: output the origin's Voronoi vertices and compute the volume of their convex hull. The last number from option 'FS' is the volume.

    rbox P0 10 | qvoronoi QV0 p | qhull FS
    0
    2 1.448134756744281 0.1067973560800857
    

    For another example, see How do I get the triangles for a 2-d Delaunay triangulation and the vertices of its Voronoi diagram?

    This approach is slow if you are using the command line. A faster approcach is to call Qhull from a program. The fastest method is Clarkson's hull program. It computes the volume for all Voronoi regions.

    An unbounded Voronoi region does not have a volume.

    »How do I get the radii of the empty spheres for each Voronoi vertex?

    Use option 'Fi' to list each bisector (i.e. Delaunay ridge). Then compute the minimum distance for each Voronoi vertex.

    There's other ways to get the same information. Let me know if you find a better method.

    »What is the Voronoi diagram of a square?

    Consider a square,

    C:\qhull>rbox c D2
    2 RBOX c D2
    4
      -0.5   -0.5
      -0.5    0.5
       0.5   -0.5
       0.5    0.5
    

    There's two ways to compute the Voronoi diagram: with facet merging or with joggle. With facet merging, the result is:

    C:\qhull>rbox c D2 | qvoronoi Qz
    
    Voronoi diagram by the convex hull of 5 points in 3-d:
    
      Number of Voronoi regions and at-infinity: 5
      Number of Voronoi vertices: 1
      Number of facets in hull: 5
    
    Statistics for: RBOX c D2 | QVORONOI Qz
    
      Number of points processed: 5
      Number of hyperplanes created: 7
      Number of distance tests for qhull: 8
      Number of merged facets: 1
      Number of distance tests for merging: 29
      CPU seconds to compute hull (after input):  0
    
    C:\qhull>rbox c D2 | qvoronoi Qz o
    2
    2 5 1
    -10.101 -10.101
         0      0
    2 0 1
    2 0 1
    2 0 1
    2 0 1
    0
    
    C:\qhull>rbox c D2 | qvoronoi Qz Fv
    4
    4 0 1 0 1
    4 0 2 0 1
    4 1 3 0 1
    4 2 3 0 1
    

    There is one Voronoi vertex at the origin and rays from the origin along each of the coordinate axes. The last line '4 2 3 0 1' means that there is a ray that bisects input points #2 and #3 from infinity (vertex 0) to the origin (vertex 1). Option 'Qz' adds an artificial point since the input is cocircular. Coordinates -10.101 indicate the vertex at infinity.

    With triangulated output, the Voronoi vertex is duplicated:

    C:\qhull3.1>rbox c D2 | qvoronoi Qt Qz
    
    Voronoi diagram by the convex hull of 5 points in 3-d:
    
      Number of Voronoi regions and at-infinity: 5
      Number of Voronoi vertices: 2
      Number of triangulated facets: 1
    
    Statistics for: RBOX c D2 | QVORONOI Qt Qz
    
      Number of points processed: 5
      Number of hyperplanes created: 7
      Number of facets in hull: 6
      Number of distance tests for qhull: 8
      Number of distance tests for merging: 33
      Number of distance tests for checking: 30
      Number of merged facets: 1
      CPU seconds to compute hull (after input): 0.05
    
    C:\qhull3.1>rbox c D2 | qvoronoi Qt Qz o
    2
    3 5 1
    -10.101 -10.101
         0      0
         0      0
    3 2 0 1
    2 1 0
    2 2 0
    3 2 0 1
    0
    
    C:\qhull3.1>rbox c D2 | qvoronoi Qt Qz Fv
    4
    4 0 2 0 2
    4 0 1 0 1
    4 1 3 0 1
    4 2 3 0 2
    

    With joggle, the input is no longer cocircular and the Voronoi vertex is split into two:

    C:\qhull>rbox c D2 | qvoronoi Qt Qz
    
    C:\qhull>rbox c D2 | qvoronoi QJ o
    2
    3 4 1
    -10.101 -10.101
    -4.71511718558304e-012 -1.775812830118184e-011
    9.020340030474472e-012 -4.02267108512433e-012
    2 0 1
    3 2 1 0
    3 2 0 1
    2 2 0
    
    C:\qhull>rbox c D2 | qvoronoi QJ Fv
    5
    4 0 2 0 1
    4 0 1 0 1
    4 1 2 1 2
    4 1 3 0 2
    4 2 3 0 2
    

    Note that the Voronoi diagram includes the same rays as before plus a short edge between the two vertices.

    »How do I construct the Voronoi diagram of cospherical points?

    Three-d terrain data can be approximated with cospherical points. The Delaunay triangulation of cospherical points is the same as their convex hull. If the points lie on the unit sphere, the facet normals are the Voronoi vertices [via S. Fortune].

    For example, consider the points {[1,0,0], [-1,0,0], [0,1,0], ...}. Their convex hull is:

    rbox d G1 | qconvex o
    3
    6 8 12
         0      0     -1
         0      0      1
         0     -1      0
         0      1      0
        -1      0      0
         1      0      0
    3 3 1 4
    3 1 3 5
    3 0 3 4
    3 3 0 5
    3 2 1 5
    3 1 2 4
    3 2 0 4
    3 0 2 5
    

    The facet normals are:

    rbox d G1 | qconvex n
    4
    8
    -0.5773502691896258  0.5773502691896258  0.5773502691896258 -0.5773502691896258
     0.5773502691896258  0.5773502691896258  0.5773502691896258 -0.5773502691896258
    -0.5773502691896258  0.5773502691896258 -0.5773502691896258 -0.5773502691896258
     0.5773502691896258  0.5773502691896258 -0.5773502691896258 -0.5773502691896258
     0.5773502691896258 -0.5773502691896258  0.5773502691896258 -0.5773502691896258
    -0.5773502691896258 -0.5773502691896258  0.5773502691896258 -0.5773502691896258
    -0.5773502691896258 -0.5773502691896258 -0.5773502691896258 -0.5773502691896258
     0.5773502691896258 -0.5773502691896258 -0.5773502691896258 -0.5773502691896258
    

    If you drop the offset from each line (the last number), each line is the Voronoi vertex for the corresponding facet. The neighboring facets for each point define the Voronoi region for each point. For example:

    rbox d G1 | qconvex FN
    6
    4 7 3 2 6
    4 5 0 1 4
    4 7 4 5 6
    4 3 1 0 2
    4 6 2 0 5
    4 7 3 1 4
    

    The Voronoi vertices {7, 3, 2, 6} define the Voronoi region for point 0. Point 0 is [0,0,-1]. Its Voronoi vertices are

    -0.5773502691896258  0.5773502691896258 -0.5773502691896258
     0.5773502691896258  0.5773502691896258 -0.5773502691896258
    -0.5773502691896258 -0.5773502691896258 -0.5773502691896258
     0.5773502691896258 -0.5773502691896258 -0.5773502691896258
    

    In this case, the Voronoi vertices are oriented, but in general they are unordered.

    By taking the dual of the Delaunay triangulation, you can construct the Voronoi diagram. For cospherical points, the convex hull vertices for each facet, define the input sites for each Voronoi vertex. In 3-d, the input sites are oriented. For example:

    rbox d G1 | qconvex i
    8
    3 1 4
    1 3 5
    0 3 4
    3 0 5
    2 1 5
    1 2 4
    2 0 4
    0 2 5
    

    The convex hull vertices for facet 0 are {3, 1, 4}. So Voronoi vertex 0 (i.e., [-0.577, 0.577, 0.577]) is the Voronoi vertex for input sites {3, 1, 4} (i.e., {[0,1,0], [0,0,1], [-1,0,0]}).

    »Can Qhull compute the unbounded rays of the Voronoi diagram?

    Use 'Fo' to compute the separating hyperplanes for unbounded Voronoi regions. The corresponding ray goes to infinity from the Voronoi vertices. If you enclose the input sites in a large enough box, the outermost bounded regions will represent the unbounded regions of the original points.

    If you do not box the input sites, you can identify the unbounded regions. They list '0' as a vertex. Vertex 0 represents "infinity". Each unbounded ray includes vertex 0 in option 'Fv. See Voronoi graphics and Voronoi notes.

    »Approximation questions

    »How do I approximate data with a simplex

    Qhull may be used to help select a simplex that approximates a data set. It will take experimentation. Geomview will help to visualize the results. This task may be difficult to do in 5-d and higher. Use rbox options 'x' and 'y' to produce random distributions within a simplex. Your methods work if you can recover the simplex.

    Use Qhull's precision options to get a first approximation to the hull, say with 10 to 50 facets. For example, try 'C0.05' to remove small facets after constructing the hull. Use 'W0.05' to ignore points within 0.05 of a facet. Use 'PA5' to print the five largest facets by area.

    Then use other methods to fit a simplex to this data. Remove outlying vertices with few nearby points. Look for large facets in different quadrants. You can use option 'Pd0d1d2' to print all the facets in a quadrant.

    In 4-d and higher, use the outer planes (option 'Fo' or 'facet->maxoutside') since the hyperplane of an approximate facet may be below many of the input points.

    For example, consider fitting a cube to 1000 uniformly random points in the unit cube. In this case, the first try was good:

    rbox 1000 | qconvex W0.05 C0.05 PA6 Fo
    4
    6
    0.35715408374381 0.08706467018177928 -0.9299788727015564 -0.5985514741284483
    0.995841591359023 -0.02512604712761577 0.08756829720435189 -0.5258834069202866
    0.02448099521570909 -0.02685210459017302 0.9993396046151313 -0.5158104982631999
    -0.9990223929415094 -0.01261133513150079 0.04236994958247349 -0.509218270408407
    -0.0128069014364698 -0.9998380680115362 0.01264203427283151 -0.5002512653670584
    0.01120895057872914 0.01803671994177704 -0.9997744926535512 -0.5056824072956361
    

    »Halfspace questions

    »How do I compute the intersection of halfspaces with Qhull?

    Qhull computes the halfspace intersection about a point. The point must be inside all of the halfspaces. Given a point, a duality turns a halfspace intersection problem into a convex hull problem.

    Use linear programming if you do not know a point in the interior of the halfspaces. See the notes for qhalf. You will need a linear programming code. This may require a fair amount of work to implement.

    »Qhull library questions

    »Is Qhull available for Mathematica, Matlab, or Maple?

    MATLAB

    Z. You of MathWorks added qhull to MATLAB 6. See functions convhulln, delaunayn, griddata3, griddatan, tsearch, tsearchn, and voronoin. V. Brumberg update MATLAB R14 for Qhull 2003.1 and triangulated output.

    Engwirda wrote mesh2d for unstructured mesh generation in MATLAB. It is based on the iterative method of Persson and generally results in better quality meshes than delaunay refinement.

    Mathematica and Maple

    See qh-math for a Delaunay interface to Mathematica. It includes projects for CodeWarrior on the Macintosh and Visual C++ on Win32 PCs.

    See Mathematica ('m') and Maple ('FM') output options.

    »Why are there too few ridges?

    The following sample code may produce fewer ridges than expected:
      facetT *facetp;
      ridgeT *ridge, **ridgep;
    
      FORALLfacets {
        printf("facet f%d\n", facet->id);
        FOREACHridge_(facet->ridges) {
          printf("   ridge r%d between f%d and f%d\n", ridge->id, ridge->top->id, ridge->bottom->id);
        }
      }
    

    Qhull does not create ridges for simplicial facets. Instead it computes ridges from facet->neighbors. To make ridges for a simplicial facet, use qh_makeridges() in merge.c. Usefacet->visit_id to visit each ridge once (instead of twice). For example,

      facetT *facet, *neighbor;
      ridgeT *ridge, **ridgep;
    
      qh visit_id++;
      FORALLfacets {
        printf("facet f%d\n", facet->id);
        qh_makeridges(facet);
        facet->visitId= qh visit_id;
        FOREACHridge_(facet->ridges) {
            neighbor= otherfacet_(ridge, visible);
            if (neighbor->visitid != qh visit_id)
                printf("   ridge r%d between f%d and f%d\n", ridge->id, ridge->top->id, ridge->bottom->id);
        }
      }
    

    »Can Qhull use coordinates without placing them in a data file?

    You may call Qhull from a program. Please use the reentrant Qhull library (libqhullstatic_r.a, libqhull_r.so, or qhull_r.dll). See user_eg.c and "Qhull-template" in user_r.c for examples.. See Qhull internals for an introduction to Qhull's reentrant library and its C++ interface.

    Hint: Start with a small example for which you know the answer.

    »How large are Qhull's data structures?

    Qhull uses a general-dimension data structure. The size depends on the dimension. Use option 'Ts' to print out the memory statistics [e.g., 'rbox D2 10 | qconvex Ts'].

    »Can Qhull construct convex hulls and Delaunay triangulations one point at a time?

    The Qhull library may be used to construct convex hulls and Delaunay triangulations one point at a time. It may not be used for deleting points or moving points.

    Qhull is designed for batch processing. Neither Clarkson's randomized incremental algorithm nor Qhull are designed for on-line operation. For many applications, it is better to reconstruct the convex hull or Delaunay triangulation from scratch for each new point.

    With random point sets and on-line processing, Clarkson's algorithm should run faster than Qhull. Clarkson uses the intermediate facets to reject new, interior points, while Qhull, when used on-line, visits every facet to reject such points. If used on-line for n points, Clarkson may take O(n) times as much memory as the average off-line case, while Qhull's space requirement does not change.

    If you triangulate the output before adding all the points (option 'Qt' and procedure qh_triangulate), you must set option 'Q11'. It duplicates the normals of triangulated facets and recomputes the centrums. This should be avoided for regular use since triangulated facets are not clearly convex with their neighbors. It appears to work most of the time, but fails for cases that Qhull normally handles well [see the test call to qh_triangulate in qh_addpoint].

    »How do I visit the ridges of a Delaunay triangulation?

    To visit the ridges of a Delaunay triangulation, visit each facet. Each ridge will appear twice since it belongs to two facets. In pseudo-code:

        for each facet of the triangulation
            if the facet is Delaunay (i.e., part of the lower convex hull)
                for each ridge of the facet
                    if the ridge's neighboring facet has not been visited
                        ... process a ridge of the Delaunay triangulation ...
    

    In undebugged, C code:

        qh visit_id++;
        FORALLfacets_(facetlist)
            if (!facet->upperdelaunay) {
                facet->visitid= qh visit_id;
                qh_makeridges(facet);
                FOREACHridge_(facet->ridges) {
                    neighbor= otherfacet_(ridge, facet);
                    if (neighbor->visitid != qh visit_id) {
                        /* Print ridge here with facet-id and neighbor-id */
                        /*fprintf(fp, "f%d\tf%d\t",facet->id,neighbor->ID);*/
                        FOREACHvertex_(ridge->vertices)
                            fprintf(fp,"%d ",qh_pointid (vertex->point) );
                        qh_printfacetNvertex_simplicial (fp, facet, format);
                        fprintf(fp," ");
                        if(neighbor->upperdelaunay)
                            fprintf(fp," -1 -1 -1 -1 ");
                        else
                            qh_printfacetNvertex_simplicial (fp, neighbor, format);
                        fprintf(fp,"\n");
                    }
                }
            }
        }
    

    Qhull should be redesigned as a class library, or at least as an API. It currently provides everything needed, but the programmer has to do a lot of work. Hopefully someone will write C++ wrapper classes or a Python module for Qhull.

    »How do I visit the Delaunay regions?

    Qhull constructs a Delaunay triangulation by lifting the input sites to a paraboloid. The Delaunay triangulation corresponds to the lower convex hull of the lifted points. To visit each facet of the lower convex hull, use:

        facetT *facet;
    
        ...
        FORALLfacets {
            if (!facet->upperdelaunay) {
                ... only facets for Delaunay regions ...
            }
        }
    

    »When is a point outside or inside a facet?

    A point is outside of a facet if it is clearly outside the facet's outer plane. The outer plane is defined by an offset (facet->maxoutside) from the facet's hyperplane.

        facetT *facet;
        pointT *point;
        realT dist;
    
        ...
        qh_distplane(point, facet, &dist);
        if (dist > facet->maxoutside + 2 * qh DISTround) {
            /* point is clearly outside of facet */
        }
    

    A point is inside of a facet if it is clearly inside the facet's inner plane. The inner plane is computed as the maximum distance of a vertex to the facet. It may be computed for an individual facet, or you may use the maximum over all facets. For example:

        facetT *facet;
        pointT *point;
        realT dist;
    
        ...
        qh_distplane(point, facet, &dist);
        if (dist < qh min_vertex - 2 * qh DISTround) {
            /* point is clearly inside of facet */
        }
    

    Both tests include two qh.DISTrounds because the computation of the furthest point from a facet may be off by qh.DISTround and the computation of the current distance to the facet may be off by qh.DISTround.

    »How do I find the facet that is closest to a point?

    Use qh_findbestfacet(). For example,

        coordT point[ DIM ];
        boolT isoutside;
        realT bestdist;
        facetT *facet;
    
        ... set coordinates for point ...
    
        facet= qh_findbestfacet (point, qh_ALL, &bestdist, &isoutside);
    
        /* 'facet' is the closest facet to 'point' */
    

    qh_findbestfacet() performs a directed search for the facet furthest below the point. If the point lies inside this facet, qh_findbestfacet() performs an exhaustive search of all facets. An exhaustive search may be needed because a facet on the far side of a lens-shaped distribution may be closer to a point than all of the facet's neighbors. The exhaustive search may be skipped for spherical distributions.

    Also see, "How do I find the Delaunay triangle that is closest to a point?"

    »How do I find the Delaunay triangle or Voronoi region that is closest to a point?

    A Delaunay triangulation subdivides the plane, or in general dimension, subdivides space. Given a point, how do you determine the subdivision containing the point? Or, given a set of points, how do you determine the subdivision containing each point of the set? Efficiency is important -- an exhaustive search of the subdivision is too slow.

    First compute the Delaunay triangle with qh_new_qhull() in user_r.c or Qhull::runQhull(). Lift the point to the paraboloid by summing the squares of the coordinates. Use qh_findbestfacet() [poly2.c] to find the closest Delaunay triangle. Determine the closest vertex to find the corresponding Voronoi region. Do not use options 'Qbb', 'QbB', 'Qbk:n', or 'QBk:n' since these scale the last coordinate. Optimizations of qh_findbestfacet() should be possible for Delaunay triangulations.

    You first need to lift the point to the paraboloid (i.e., the last coordinate is the sum of the squares of the point's coordinates). The routine, qh_setdelaunay() [geom2.c], lifts an array of points to the paraboloid. The following excerpt is from findclosest() in user_eg.c.

        coordT point[ DIM + 1];  /* one extra coordinate for lifting the point */
        boolT isoutside;
        realT bestdist;
        facetT *facet;
    
        ... set coordinates for point[] ...
    
        qh_setdelaunay (DIM+1, 1, point);
        facet= qh_findbestfacet (point, qh_ALL, &bestdist, &isoutside);
        /* 'facet' is the closest Delaunay triangle to 'point' */
    

    The returned facet either contains the point or it is the closest Delaunay triangle along the convex hull of the input set.

    Point location is an active research area in Computational Geometry. For a practical approach, see Mucke, et al, "Fast randomized point location without preprocessing in two- and three-dimensional Delaunay triangulations," Computational Geometry '96, p. 274-283, May 1996. For an introduction to planar point location see [O'Rourke '93]. Also see, "How do I find the facet that is closest to a point?"

    To locate the closest Voronoi region, determine the closest vertex of the closest Delaunay triangle.

        realT dist, bestdist= REALmax;
            vertexT *bestvertex= NULL, *vertex, **vertexp;
    
        /* 'facet' is the closest Delaunay triangle to 'point' */
    
        FOREACHvertex_( facet->vertices ) {
            dist= qh_pointdist( point, vertex->point, DIM );
            if (dist < bestdist) {
                bestdist= dist;
                bestvertex= vertex;
            }
        }
        /* 'bestvertex' represents the Voronoi region closest to 'point'.  The corresponding
           input site is 'bestvertex->point' */
    

    »How do I list the vertices?

    To list the vertices (i.e., extreme points) of the convex hull use

        vertexT *vertex;
    
        FORALLvertices {
          ...
          // vertex->point is the coordinates of the vertex
          // qh_pointid(vertex->point) is the point ID of the vertex
          ...
        }
        

    »How do I test code that uses the Qhull library?

    Compare the output from your program with the output from the Qhull program. Use option 'T1' or 'T4' to trace what Qhull is doing. Prepare a small example for which you know the output. Run the example through the Qhull program and your code. Compare the trace outputs. If you do everything right, the two trace outputs should be almost the same. The trace output will also guide you to the functions that you need to review.

    »When I compute a plane equation from a facet, I sometimes get an outward-pointing normal and sometimes an inward-pointing normal

    Qhull orients simplicial facets, and prints oriented output for 'i', 'Ft', and other options. The orientation depends on both the vertex order and the flag facet->toporient.

    Qhull does not orient non-simplicial facets. Instead it orients the facet's ridges. These are printed with the 'Qt' and 'Ft' option. The facet's hyperplane is oriented.


    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
    To: FAQ: Table of Contents


    The Geometry Center Home Page

    Comments to: qhull@qhull.org
    Created: Sept. 25, 1995 --- Last modified: see top

    geometry/inst/doc/qhull/html/qh--half.gif0000644000176200001440000000475113431000556020010 0ustar liggesusersGIF87add.=-/HO%#-ZdbQ?qR0^S;D6gE2hTN;BGGPZLL9 \%+'/<>lN..hPHyJJHFNOJ91BW.\pX`NNOIGUM8No44II&B;X[@@XC2[:8']eqH8LPY\;.X_l)TKKeo|$.%HJO}84@CSSSQQQYkMMMkjRcmO1WgLKLSHIidEMOg'h7/BBfpR0>>7JJ7:0IBd48RW43#Zk$5'-19D`'=AgK46OS*-u&LHHGF?Og;4b9UYWKzvZ~EEQTeFG}FHH362-<,SXBBo.3#rS1HUXO?=JVVYA,Z*6,`@)MM`T@EBaAOS<=7-0[[\-g+.{JJ7YRSSG#Y:VY=8NOOfK?KMLn9rWN:i-FF|yNK/=-OV] (!Ie^:tQJ\ (Q-P/\DueHb1ɓ<`Æ%kV/kbFط`:דZ4 WM2sˤ+&'aDHA/ 7Lgs bKBxn=S~%ܭ~Uu/bDC[eрaQo"CNj' >Nk׮g GZ m OY6#=M˻Ф;X)b_XwM1pfaaّK;D~שbMl%:K< 5C5p|f.\̡rxf 8K=f _VR'Y@0mr )Fah6[#rB j5G1!!\q$,[G邗Q Qhull quick reference

    Up: Home page for Qhull
    Up: Qhull manual
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
    To: Qhull internals
    To: Qhull functions, macros, and data structures
    To: Qhull files
    To: GeomGlobalIoMemMergePolyQhullSetStatUser


    [cone] Qhull quick reference

    This section lists all programs and options in Qhull.

    Copyright © 1995-2018 C.B. Barber

     


    Qhull programs

    » ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions

    qconvex -- convex hull
    synopsis • input • outputs • controls • graphics • notes • conventions • options
     
    qdelaunay -- Delaunay triangulation
    synopsis • input • outputs • controls • graphics • notes • conventions • options
     
    qdelaunay Qu -- furthest-site Delaunay triangulation
    synopsis • input • outputs • controls • graphics • notes • conventions • options
     
    qhalf -- halfspace intersection about a point
    synopsis • input • outputs • controls • graphics • notes • conventions • options
     
    qvoronoi -- Voronoi diagram
    synopsis • input • outputs • controls • graphics • notes • conventions • options
     
    qvoronoi Qu -- furthest-site Voronoi diagram
    synopsis • input • outputs • controls • graphics • notes • conventions • options
     
    rbox -- generate point distributions for qhull
    synopsis • outputs • examples • notes • options
     
    qhull -- convex hull and related structures
    synopsis • input • outputs • controls • options
     
    Qhull options

    » ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions

    'Fa' Farea 'FA' FArea-total 'Fc' Fcoplanars 'FC' FCentrums
    'Fd' Fd-cdd-in 'FD' FD-cdd-out 'FF' FF-dump-xridge 'Fi' Finner
    'Fi' Finner_bounded 'FI' FIDs 'Fm' Fmerges 'FM' FMaple
    'Fn' Fneighbors 'FN' FNeigh-vertex 'Fo' Fouter 'Fo' Fouter_unbounded
    'FO' FOptions 'Fp' Fpoint-intersect 'FP' FPoint_near 'FQ' FQhull
    'Fs' Fsummary 'FS' FSize 'Ft' Ftriangles 'Fv' Fvertices
    'Fv' Fvoronoi 'FV' FVertex-ave 'Fx' Fxtremes Merged facets or joggled input
     
    'PAn' PArea-keep 'Pdk:n' Pdrop_low 'PDk:n' Pdrop_high 'Pg' Pgood
    'PFn' PFacet_area_keep 'PG' PGood_neighbors 'PMn' PMerge-keep 'Po' Poutput_forced
    'Po' Poutput_error 'Pp' Pprecision_not
     
    'd' delaunay 'v' voronoi 'G' Geomview 'H' Halfspace
    'f' facet_dump 'i' incidences 'm' mathematica 'n' normals
    'o' OFF_format 'p' points 's' summary
     
    'Gv' Gvertices 'Gp' Gpoints 'Ga' Gall_points 'Gn' Gno_planes
    'Gi' Ginner 'Gc' Gcentrums 'Gh' Ghyperplanes 'Gr' Gridges
    'Go' Gouter 'GDn' GDrop_dim 'Gt' Gtransparent
     
    'T4' T4_trace 'Tc' Tcheck_often 'Ts' Tstatistics 'Tv' Tverify
    'Tz' Tz_stdout 'TFn' TFacet_log 'TI file' TInput_file 'TPn' TPoint_trace
    'TMn' TMerge_trace 'TO file' TOutput_file 'TRn' TRerun 'TWn' TWide_trace
    'TV-n' TVertex_stop_before
    'TVn' TVertex_stop_after 'TCn' TCone_stop_after
     
    'A-n' Angle_max_pre 'An' Angle_max_post 'C-0' Centrum_roundoff 'C-n' Centrum_size_pre
    'Cn' Centrum_size_post 'En' Error_round 'Rn' Random_dist 'Vn' Visible_min
    'Un' Ucoplanar_max 'Wn' Wide_outside
     
    'Qbk:n' Qbound_low 'QBk:n' QBound_high 'Qbk:0Bk:0' Qbound_drop 'QbB' QbB-scale-box
    'Qbb' Qbb-scale-last 'Qc' Qcoplanar 'Qf' Qfurthest 'Qg' Qgood_only
    'QGn' QGood_point 'Qi' Qinterior 'Qm' Qmax_out 'QJn' QJoggle
    'Qr' Qrandom 'QRn' QRotate 'Qs' Qsearch_1st 'Qt' Qtriangulate
    'Qu' QupperDelaunay 'QVn' QVertex_good 'Qv' Qvneighbors 'Qx' Qxact_merge
    'Qz' Qzinfinite
     
    'Q0' Q0_no_premerge 'Q1' Q1_no_angle 'Q2' Q2_no_independ 'Q3' Q3_no_redundant
    'Q4' Q4_no_old 'Q5' Q5_no_check_out 'Q6' Q6_no_concave 'Q7' Q7_depth_first
    'Q8' Q8_no_near_in 'Q9' Q9_pick_furthest 'Q10' Q10_no_narrow 'Q11' Q11_trinormals


    Up: Home page for Qhull
    Up: Qhull manual
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
    To: Qhull internals
    To: Qhull functions, macros, and data structures
    To: Qhull files
    To: GeomGlobalIoMemMergePolyQhullSetStatUser


    The Geometry Center Home Page

    Comments to: qhull@qhull.org
    Created: Sept. 25, 1995 --- Last modified: see top

    geometry/inst/doc/qhull/html/qconvex.html0000644000176200001440000006477013431000557020305 0ustar liggesusers qconvex -- convex hull Up: Home page for Qhull
    Up: Qhull manual -- Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
    To: synopsis • input • outputs • controls • graphics • notes • conventions • options

    [cone]qconvex -- convex hull

    The convex hull of a set of points is the smallest convex set containing the points. See the detailed introduction by O'Rourke ['94]. See Description of Qhull and How Qhull adds a point.

    Example: rbox 10 D3 | qconvex s o TO result
    Compute the 3-d convex hull of 10 random points. Write a summary to the console and the points and facets to 'result'.
     
    Example: rbox c | qconvex n
    Print the normals for each facet of a cube.
     
    Example: rbox c | qconvex i Qt
    Print the triangulated facets of a cube.
     
    Example: rbox y 500 W0 | qconvex
    Compute the convex hull of a simplex with 500 points on its surface.
     
    Example: rbox x W1e-12 1000 | qconvex QR0
    Compute the convex hull of 1000 points near the surface of a randomly rotated simplex. Report the maximum thickness of a facet.
     
    Example: rbox 1000 s | qconvex s FA
    Compute the convex hull of 1000 cospherical points. Verify the results and print a summary with the total area and volume.
     
    Example: rbox d D12 | qconvex QR0 FA
    Compute the convex hull of a 12-d diamond. Randomly rotate the input. Note the large number of facets and the small volume.
     
    Example: rbox c D7 | qconvex FA TF1000
    Compute the convex hull of the 7-d hypercube. Report on progress every 1000 facets. Computing the convex hull of the 9-d hypercube takes too much time and space.
     
    Example: rbox c d D2 | qconvex Qc s f Fx | more
    Dump all fields of all facets for a square and a diamond. Also print a summary and a list of vertices. Note the coplanar points.
     

    Except for rbox, all of the qhull programs compute a convex hull.

    By default, Qhull merges coplanar facets. For example, the convex hull of a cube's vertices has six facets.

    If you use 'Qt' (triangulated output), all facets will be simplicial (e.g., triangles in 2-d). For the cube example, it will have 12 facets. Some facets may be degenerate and have zero area.

    If you use 'QJ' (joggled input), all facets will be simplicial. The corresponding vertices will be slightly perturbed and identical points will be joggled apart. Joggled input is less accurate that triangulated output.See Merged facets or joggled input.

    The output for 4-d convex hulls may be confusing if the convex hull contains non-simplicial facets (e.g., a hypercube). See Why are there extra points in a 4-d or higher convex hull?

    The 'qconvex' program is equivalent to 'qhull' in 2-d to 4-d, and 'qhull Qx' in 5-d and higher. It disables the following Qhull options: d v H Qbb Qf Qg Qm Qr Qu Qv Qx Qz TR E V Fp Gt Q0,etc.

    Copyright © 1995-2018 C.B. Barber


    »qconvex synopsis

    qconvex- compute the convex hull.
        input (stdin): dimension, number of points, point coordinates
        comments start with a non-numeric character
    
    options (qconvex.html):
        Qt   - triangulated output
        QJ   - joggle input instead of merging facets
        Tv   - verify result: structure, convexity, and point inclusion
        .    - concise list of all options
        -    - one-line description of all options
    
    output options (subset):
        s    - summary of results (default)
        i    - vertices incident to each facet
        n    - normals with offsets
        p    - vertex coordinates (includes coplanar points if 'Qc')
        Fx   - extreme points (convex hull vertices)
        FA   - compute total area and volume
        o    - OFF format (dim, n, points, facets)
        G    - Geomview output (2-d, 3-d, and 4-d)
        m    - Mathematica output (2-d and 3-d)
        QVn  - print facets that include point n, -n if not
        TO file- output results to file, may be enclosed in single quotes
    
    examples:
        rbox c D2 | qconvex s n                    rbox c D2 | qconvex i
        rbox c D2 | qconvex o                      rbox 1000 s | qconvex s Tv FA
        rbox c d D2 | qconvex s Qc Fx              rbox y 1000 W0 | qconvex s n
        rbox y 1000 W0 | qconvex s QJ              rbox d G1 D12 | qconvex QR0 FA Pp
        rbox c D7 | qconvex FA TF1000
    

    »qconvex input

    The input data on stdin consists of:

    • dimension
    • number of points
    • point coordinates

    Use I/O redirection (e.g., qconvex < data.txt), a pipe (e.g., rbox 10 | qconvex), or the 'TI' option (e.g., qconvex TI data.txt).

    Comments start with a non-numeric character. Error reporting is simpler if there is one point per line. Dimension and number of points may be reversed.

    Here is the input for computing the convex hull of the unit cube. The output is the normals, one per facet.

    rbox c > data

    3 RBOX c
    8
      -0.5   -0.5   -0.5
      -0.5   -0.5    0.5
      -0.5    0.5   -0.5
      -0.5    0.5    0.5
       0.5   -0.5   -0.5
       0.5   -0.5    0.5
       0.5    0.5   -0.5
       0.5    0.5    0.5
    

    qconvex s n < data

    
    Convex hull of 8 points in 3-d:
    
      Number of vertices: 8
      Number of facets: 6
      Number of non-simplicial facets: 6
    
    Statistics for: RBOX c | QCONVEX s n
    
      Number of points processed: 8
      Number of hyperplanes created: 11
      Number of distance tests for qhull: 35
      Number of merged facets: 6
      Number of distance tests for merging: 84
      CPU seconds to compute hull (after input): 0.081
    
    4
    6
         0      0     -1   -0.5
         0     -1      0   -0.5
         1      0      0   -0.5
        -1      0      0   -0.5
         0      1      0   -0.5
         0      0      1   -0.5
    

    »qconvex outputs

    These options control the output of qconvex. They may be used individually or together.

     
    Vertices
    Fx
    list extreme points (i.e., vertices). The first line is the number of extreme points. Each point is listed, one per line. The cube example has eight vertices.
    Fv
    list vertices for each facet. The first line is the number of facets. Each remaining line starts with the number of vertices. For the cube example, each facet has four vertices.
    i
    list vertices for each facet. The first line is the number of facets. The remaining lines list the vertices for each facet. In 4-d and higher, triangulate non-simplicial facets by adding an extra point.
     
     
    Coordinates
    o
    print vertices and facets of the convex hull in OFF format. The first line is the dimension. The second line is the number of vertices, facets, and ridges. The vertex coordinates are next, followed by the facets. Each facet starts with the number of vertices. The cube example has four vertices per facet.
    Ft
    print a triangulation of the convex hull in OFF format. The first line is the dimension. The second line is the number of vertices and added points, followed by the number of facets and the number of ridges. The vertex coordinates are next, followed by the centrum coordinates. There is one centrum for each non-simplicial facet. The cube example has six centrums, one per square. Each facet starts with the number of vertices or centrums. In the cube example, each facet uses two vertices and one centrum.
    p
    print vertex coordinates. The first line is the dimension and the second line is the number of vertices. The following lines are the coordinates of each vertex. The cube example has eight vertices.
    Qc p
    print coordinates of vertices and coplanar points. The first line is the dimension. The second line is the number of vertices and coplanar points. The coordinates are next, one line per point. Use 'Qc Qi p' to print the coordinates of all points.
     
     
    Facets
    Fn
    list neighboring facets for each facet. The first line is the number of facets. Each remaining line starts with the number of neighboring facets. The cube example has four neighbors per facet.
    FN
    list neighboring facets for each point. The first line is the total number of points. Each remaining line starts with the number of neighboring facets. Each vertex of the cube example has three neighboring facets. Use 'Qc Qi FN' to include coplanar and interior points.
    Fa
    print area for each facet. The first line is the number of facets. Facet area follows, one line per facet. For the cube example, each facet has area one.
    FI
    list facet IDs. The first line is the number of facets. The IDs follow, one per line.
     
     
    Coplanar and interior points
    Fc
    list coplanar points for each facet. The first line is the number of facets. The remaining lines start with the number of coplanar points. A coplanar point is assigned to one facet.
    Qi Fc
    list interior points for each facet. The first line is the number of facets. The remaining lines start with the number of interior points. A coplanar point is assigned to one facet.
    FP
    print distance to nearest vertex for coplanar points. The first line is the number of coplanar points. Each remaining line starts with the point ID of a vertex, followed by the point ID of a coplanar point, its facet, and distance. Use 'Qc Qi FP' for coplanar and interior points.
     
     
    Hyperplanes
    n
    print hyperplane for each facet. The first line is the dimension. The second line is the number of facets. Each remaining line is the hyperplane's coefficients followed by its offset.
    Fo
    print outer plane for each facet. The output plane is above all points. The first line is the dimension. The second line is the number of facets. Each remaining line is the outer plane's coefficients followed by its offset.
    Fi
    print inner plane for each facet. The inner plane of a facet is below its vertices. The first line is the dimension. The second line is the number of facets. Each remaining line is the inner plane's coefficients followed by its offset.
     
     
    General
    s
    print summary for the convex hull. Use 'Fs' and 'FS' if you need numeric data.
    FA
    compute total area and volume for 's' and 'FS'
    m
    Mathematica output for the convex hull in 2-d or 3-d.
    FM
    Maple output for the convex hull in 2-d or 3-d.
    G
    Geomview output for the convex hull in 2-d, 3-d, or 4-d.
     
     
    Scaling and rotation
    Qbk:n
    scale k'th coordinate to lower bound.
    QBk:n
    scale k'th coordinate to upper bound.
    QbB
    scale input to unit cube centered at the origin.
    QRn
    randomly rotate the input with a random seed of n. If n=0, the seed is the time. If n=-1, use time for the random seed, but do not rotate the input.
    Qbk:0Bk:0
    remove k'th coordinate from input. This computes the convex hull in one lower dimension.

    »qconvex controls

    These options provide additional control:

    Qt
    triangulated output. Qhull triangulates non-simplicial facets. It may produce degenerate facets of zero area.
    QJ
    joggle the input instead of merging facets. This guarantees simplicial facets (e.g., triangles in 3-d). It is less accurate than triangulated output ('Qt').
    Qc
    keep coplanar points
    Qi
    keep interior points
    f
    facet dump. Print the data structure for each facet.
    QVn
    select facets containing point n as a vertex,
    QGn
    select facets that are visible from point n (marked 'good'). Use -n for the remainder.
    PDk:0
    select facets with a negative coordinate for dimension k
    TFn
    report progress after constructing n facets
    Tv
    verify result
    TI file
    input data from file. The filename may not use spaces or quotes.
    TO file
    output results to file. Use single quotes if the filename contains spaces (e.g., TO 'file with spaces.txt'
    Qs
    search all points for the initial simplex. If Qhull can not construct an initial simplex, it reports a descriptive message. Usually, the point set is degenerate and one or more dimensions should be removed ('Qbk:0Bk:0'). If not, use option 'Qs'. It performs an exhaustive search for the best initial simplex. This is expensive is high dimensions.

    »qconvex graphics

    Display 2-d, 3-d, and 4-d convex hulls with Geomview ('G').

    Display 2-d and 3-d convex hulls with Mathematica ('m').

    To view 4-d convex hulls in 3-d, use 'Pd0d1d2d3' to select the positive octant and 'GrD2' to drop dimension 2.

    »qconvex notes

    Qhull always computes a convex hull. The convex hull may be used for other geometric structures. The general technique is to transform the structure into an equivalent convex hull problem. For example, the Delaunay triangulation is equivalent to the convex hull of the input sites after lifting the points to a paraboloid.

    »qconvex conventions

    The following terminology is used for convex hulls in Qhull. See Qhull's data structures.

    • point - d coordinates
    • vertex - extreme point of the input set
    • ridge - d-1 vertices between two neighboring facets
    • hyperplane - halfspace defined by a unit normal and offset
    • coplanar point - a nearly incident point to a hyperplane
    • centrum - a point on the hyperplane for testing convexity
    • facet - a facet with vertices, ridges, coplanar points, neighboring facets, and hyperplane
    • simplicial facet - a facet with d vertices, d ridges, and d neighbors
    • non-simplicial facet - a facet with more than d vertices
    • good facet - a facet selected by 'QVn', etc.

    »qconvex options

    qconvex- compute the convex hull
        http://www.qhull.org
    
    input (stdin):
        first lines: dimension and number of points (or vice-versa).
        other lines: point coordinates, best if one point per line
        comments:    start with a non-numeric character
    
    options:
        Qt   - triangulated output
        QJ   - joggle input instead of merging facets
        Qc   - keep coplanar points with nearest facet
        Qi   - keep interior points with nearest facet
    
    Qhull control options:
        Qbk:n   - scale coord k so that low bound is n
          QBk:n - scale coord k so that upper bound is n (QBk is 0.5)
        QbB  - scale input to unit cube centered at the origin
        Qbk:0Bk:0 - remove k-th coordinate from input
        QJn  - randomly joggle input in range [-n,n]
        QRn  - random rotation (n=seed, n=0 time, n=-1 time/no rotate)
        Qs   - search all points for the initial simplex
        QGn  - good facet if visible from point n, -n for not visible
        QVn  - good facet if it includes point n, -n if not
    
    Trace options:
        T4   - trace at level n, 4=all, 5=mem/gauss, -1= events
        Tc   - check frequently during execution
        Ts   - print statistics
        Tv   - verify result: structure, convexity, and point inclusion
        Tz   - send all output to stdout
        TFn  - report summary when n or more facets created
        TI file - input data from file, no spaces or single quotes
        TO file - output results to file, may be enclosed in single quotes
        TPn  - turn on tracing when point n added to hull
         TMn - turn on tracing at merge n
         TWn - trace merge facets when width > n
        TVn  - stop qhull after adding point n, -n for before (see TCn)
         TCn - stop qhull after building cone for point n (see TVn)
    
    Precision options:
        Cn   - radius of centrum (roundoff added).  Merge facets if non-convex
         An  - cosine of maximum angle.  Merge facets if cosine > n or non-convex
               C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge
        Rn   - randomly perturb computations by a factor of [1-n,1+n]
        Un   - max distance below plane for a new, coplanar point
        Wn   - min facet width for outside point (before roundoff)
    
    Output formats (may be combined; if none, produces a summary to stdout):
        f    - facet dump
        G    - Geomview output (see below)
        i    - vertices incident to each facet
        m    - Mathematica output (2-d and 3-d)
        n    - normals with offsets
        o    - OFF file format (dim, points and facets; Voronoi regions)
        p    - point coordinates
        s    - summary (stderr)
    
    More formats:
        Fa   - area for each facet
        FA   - compute total area and volume for option 's'
        Fc   - count plus coplanar points for each facet
               use 'Qc' (default) for coplanar and 'Qi' for interior
        FC   - centrum for each facet
        Fd   - use cdd format for input (homogeneous with offset first)
        FD   - use cdd format for numeric output (offset first)
        FF   - facet dump without ridges
        Fi   - inner plane for each facet
        FI   - ID for each facet
        Fm   - merge count for each facet (511 max)
        FM   - Maple output (2-d and 3-d)
        Fn   - count plus neighboring facets for each facet
        FN   - count plus neighboring facets for each point
        Fo   - outer plane (or max_outside) for each facet
        FO   - options and precision constants
        FP   - nearest vertex for each coplanar point
        FQ   - command used for qconvex
        Fs   - summary: #int (8), dimension, #points, tot vertices, tot facets,
                          for output: #vertices, #facets,
                                      #coplanar points, #non-simplicial facets
                        #real (2), max outer plane, min vertex
        FS   - sizes:   #int (0)
                        #real(2) tot area, tot volume
        Ft   - triangulation with centrums for non-simplicial facets (OFF format)
        Fv   - count plus vertices for each facet
        FV   - average of vertices (a feasible point for 'H')
        Fx   - extreme points (in order for 2-d)
    
    Geomview output (2-d, 3-d, and 4-d)
        Ga   - all points as dots
         Gp  -  coplanar points and vertices as radii
         Gv  -  vertices as spheres
        Gi   - inner planes only
         Gn  -  no planes
         Go  -  outer planes only
        Gc   - centrums
        Gh   - hyperplane intersections
        Gr   - ridges
        GDn  - drop dimension n in 3-d and 4-d output
    
    Print options:
        PAn  - keep n largest facets by area
        Pdk:n - drop facet if normal[k] <= n (default 0.0)
        PDk:n - drop facet if normal[k] >= n
        Pg   - print good facets (needs 'QGn' or 'QVn')
        PFn  - keep facets whose area is at least n
        PG   - print neighbors of good facets
        PMn  - keep n facets with most merges
        Po   - force output.  If error, output neighborhood of facet
        Pp   - do not report precision problems
    
        .    - list of all options
        -    - one line descriptions of all options
    
    

    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
    To: synopsis • input • outputs • controls • graphics • notes • conventions • options


    The Geometry Center Home Page

    Comments to: qhull@qhull.org
    Created: Sept. 25, 1995 --- Last modified: see top

    geometry/inst/doc/qhull/html/qh-optg.html0000644000176200001440000002557513431000557020201 0ustar liggesusers Qhull Geomview options (G)

    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions


    [delaunay] Qhull Geomview options (G)

    This section lists the Geomview options for Qhull. These options are indicated by 'G' followed by a letter. See Output, Print, and Format for other output options.

    Copyright © 1995-2018 C.B. Barber


    » Programs OptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions

    Geomview output options

    Geomview is the graphical viewer for visualizing Qhull output in 2-d, 3-d and 4-d.

    Geomview displays each facet of the convex hull. The color of a facet is determined by the coefficients of the facet's normal equation. For imprecise hulls, Geomview displays the inner and outer hull. Geomview can also display points, ridges, vertices, coplanar points, and facet intersections.

    For 2-d Delaunay triangulations, Geomview displays the corresponding paraboloid. Geomview displays the 2-d Voronoi diagram. For halfspace intersections, it displays the dual convex hull.

     
    General
    G
    display Geomview output
    Gt
    display transparent 3-d Delaunay triangulation
    GDn
    drop dimension n in 3-d and 4-d output
     
     
    Specific
    Ga
    display all points as dots
    Gc
    display centrums (2-d, 3-d)
    Gp
    display coplanar points and vertices as radii
    Gh
    display hyperplane intersections
    Gi
    display inner planes only (2-d, 3-d)
    Go
    display outer planes only (2-d, 3-d)
    Gr
    display ridges (3-d)
    Gv
    display vertices as spheres
    Gn
    do not display planes

    »G - produce output for viewing with Geomview

    By default, option 'G' displays edges in 2-d, outer planes in 3-d, and ridges in 4-d.

    A ridge can be explicit or implicit. An explicit ridge is a (d-1)-dimensional simplex between two facets. In 4-d, the explicit ridges are triangles. An implicit ridge is the topological intersection of two neighboring facets. It is the union of explicit ridges.

    For non-simplicial 4-d facets, the explicit ridges can be quite complex. When displaying a ridge in 4-d, Qhull projects the ridge's vertices to one of its facets' hyperplanes. Use 'Gh' to project ridges to the intersection of both hyperplanes. This usually results in a cleaner display.

    For 2-d Delaunay triangulations, Geomview displays the corresponding paraboloid. Geomview displays the 2-d Voronoi diagram. For halfspace intersections, it displays the dual convex hull.

    »Ga - display all points as dots

    Each input point is displayed as a green dot.

    »Gc - display centrums (3-d)

    The centrum is defined by a green radius sitting on a blue plane. The plane corresponds to the facet's hyperplane. If you sight along a facet's hyperplane, you will see that all neighboring centrums are below the facet. The radius is defined by 'C-n' or 'Cn'.

    »GDn - drop dimension n in 3-d and 4-d output

    The result is a 2-d or 3-d object. In 4-d, this corresponds to viewing the 4-d object from the nth axis without perspective. It's best to view 4-d objects in pieces. Use the 'Pdk' 'Pg' 'PG' 'QGn' and 'QVn' options to select a few facets. If one of the facets is perpendicular to an axis, then projecting along that axis will show the facet exactly as it is in 4-d. If you generate many facets, use Geomview's ginsu module to view the interior

    To view multiple 4-d dimensions at once, output the object without 'GDn' and read it with Geomview's ndview. As you rotate the object in one set of dimensions, you can see how it changes in other sets of dimensions.

    For additional control over 4-d objects, output the object without 'GDn' and read it with Geomview's 4dview. You can slice the object along any 4-d plane. You can also flip the halfspace that's deleted when slicing. By combining these features, you can get some interesting cross sections.

    »Gh - display hyperplane intersections (3-d, 4-d)

    In 3-d, the intersection is a black line. It lies on two neighboring hyperplanes, c.f., the blue squares associated with centrums ('Gc '). In 4-d, the ridges are projected to the intersection of both hyperplanes. If you turn on edges (Geomview's 'appearances' menu), each triangle corresponds to one ridge. The ridges may overlap each other.

    »Gi - display inner planes only (2-d, 3-d)

    The inner plane of a facet is below all of its vertices. It is parallel to the facet's hyperplane. The inner plane's color is the opposite of the outer plane's color, i.e., [1-r,1-g,1-b] . Its edges are determined by the vertices.

    »Gn - do not display planes

    By default, Geomview displays the precise plane (no merging) or both inner and output planes (if merging). If merging, Geomview does not display the inner plane if the the difference between inner and outer is too small.

    »Go - display outer planes only (2-d, 3-d)

    The outer plane of a facet is above all input points. It is parallel to the facet's hyperplane. Its color is determined by the facet's normal, and its edges are determined by the vertices.

    »Gp - display coplanar points and vertices as radii

    Coplanar points ('Qc'), interior points ('Qi'), outside points ('TCn' or 'TVn'), and vertices are displayed as red and yellow radii. The radii are perpendicular to the corresponding facet. Vertices are aligned with an interior point. The radii define a ball which corresponds to the imprecision of the point. The imprecision is the maximum of the roundoff error, the centrum radius, and maxcoord * (1 - A-n). It is at least 1/20'th of the maximum coordinate, and ignores post merging if pre-merging is done.

    If 'Gv' (print vertices as spheres) is also selected, option 'Gp' displays coplanar points as radii. Select options Qc' and/or 'Qi'. Options 'Qc Gpv' displays coplanar points while 'Qci Gpv' displays coplanar and interior points. Option 'Qc' is automatically selected if 'Qi' is not selected with options 'Gpv'.

    »Gr - display ridges (3-d)

    A ridge connects the two vertices that are shared by neighboring facets. It is displayed in green. A ridge is the topological edge between two facets while the hyperplane intersection is the geometric edge between two facets. Ridges are always displayed in 4-d.

    »Gt - transparent 3-d Delaunay

    A 3-d Delaunay triangulation looks like a convex hull with interior facets. Option 'Gt' removes the outside ridges to reveal the outermost facets. It automatically sets options 'Gr' and 'GDn'. See example eg.17f.delaunay.3.

    »Gv - display vertices as spheres (2-d, 3-d)

    The radius of the sphere corresponds to the imprecision of the data. See 'Gp' for determining the radius.


    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions


    The Geometry Center Home Page

    Comments to: qhull@qhull.org
    Created: Sept. 25, 1995 --- Last modified: see top

    geometry/inst/doc/qhull/html/qhull-cpp.xml0000644000176200001440000002726213431000557020356 0ustar liggesusers

    Qhull C++ -- C++ interface to Qhull

    Copyright (c) 2009-2018, C.B. Barber

    This draft document records some of the design decisions for Qhull C++. Convert it to HTML by road-faq.xsl from road-faq. Please send comments and suggestions to bradb@shore.net

    Help
    .
    Qhull's collection APIs are modeled on Qt's collection API (QList, QVector, QHash) w/o QT_STRICT_ITERATORS. They support STL and Qt programming.

    Some of Qhull's collection classes derive from STL classes. If so, please avoid additional STL functions and operators added by inheritance. These collection classes may be rewritten to derive from Qt classes instead. See Road's .

    Qhull's collection API (where applicable). For documentation, see Qt's QList, QMap, QListIterator, QMapIterator, QMutableListIterator, and QMutableMapIterator
    • STL types [list, qlinkedlist, qlist, qvector, vector] -- const_iterator, iterator
    • STL types describing iterators [list, qlinkedlist, qlist, qvector, vector] -- const_pointer, const_reference, difference_type, pointer, reference, size_type, value_type. Pointer and reference types not defined if unavailable (not needed for <algorithm>)
    • const_iterator, iterator types -- difference_type, iterator_category, pointer, reference, value_type
    • Qt types [qlinkedlist, qlist, qvector] -- ConstIterator, Iterator, QhullclassIterator, MutableQhullclassIterator. Qt's foreach requires const_iterator.
    • Types for sets/maps [hash_map, QHash] -- key_compare, key_type, mapped_type
    • Constructor -- default constructor, copy constructor, assignment operator, destructor
    • Conversion -- to/from/as corresponding C, STL, and Qt constructs. Include toQList and toStdVector (may be filtered, e.g., QhullFacetSet). Do not define fromStdList and fromQList if container is not reference counted (i.e., acts like a value)
    • Get/set -- configuration options for class
    • STL-style iterator - begin, constBegin, constEnd, end, key, value, =, *, [], ->, ++, --, +, -, ==, !=, <, <=, >, >=, const_iterator(iterator), iterator COMPARE const_iterator. An iterator is an abstraction of a pointer. It is not aware of its container.
    • Java-style iterator [qiterator.h] - countRemaining, findNext, findPrevious, hasNext, hasPrevious, next, peekNext, peekPrevious, previous, toBack, toFront, = Coordinates
    • Mutable Java-style iterator adds - insert, remove, setValue, value
    • Element access -- back, first, front, last
    • Element access w/ index -- [], at (const& only), constData, data, mid, value
    • Read-only - (int)count, empty, isEmpty, (size_t)size. Count() and size() may be filtered. If so, they may be zero when !empty().
    • Read-only for sets/maps - capacity, key, keys, reserve, resize, values
    • Operator - ==, !=, +, +=, <<
    • Read-write -- append, clear, erase, insert, move, prepend, pop_back, pop_front, push_back, push_front, removeAll, removeAt, removeFirst, removeLast, replace, swap, takeAt, takeFirst, takeLast
    • Read-write for sets/maps -- insertMulti, squeeze, take, unite
    • Search -- contains(const T &), count(const T &), indexOf, lastIndexOf
    • Search for sets/maps -- constFind, lowerBound, upperBound
    • Stream I/O -- stream <<
    STL list and vector -- For unfiltered access to each element.
    • Apache: Creating your own containers -- requirements for STL containers. Iterators should define the types from 'iterator_traits'.
    • STL types -- allocator_type, const_iterator, const_pointer, const_reference, const_reverse_iterator, difference_type, iterator, iterator_category, pointer, reference, reverse_iterator, size_type, value_type
    • STL constructors -- MyType(), MyType(count), MyType(count, value), MyType(first, last), MyType(MyType&),
    • STL getter/setters -- at (random_access only), back, begin, capacity, end, front, rbegin, rend, size, max_size
    • STL predicates -- empty
    • STL iterator types -- const_pointer, const_reference, difference_type, iterator_category, pointer, reference, value_type
    • STL iterator operators -- *, -<, ++, --, +=, -=, +, -, [], ==, !=, <, >, >=, <=
    • STL operators -- =, [] (random_access only), ==, !=, <, >, <=, >=
    • STL modifiers -- assign, clear, erase, insert, pop_back, push_back, reserve, resize, swap
    Qt Qlist -- For unfiltered access to each element
    • Additional Qt types -- ConstIterator, Iterator, QListIterator, QMutableListIterator
    • Additional Qt get/set -- constBegin, constEnd, count, first, last, value (random_access only)
    • Additional Qt predicates -- isEmpty
    • Additional Qt -- mid (random_access only)
    • Additional Qt search -- contains, count(T&), indexOf (random_access only), lastIndeOf (random_access only)
    • Additional Qt modifiers -- append, insert(index,value) (random_access only), move (random_access only), pop_front, prepend, push_front, removeAll, removeAt (random_access only), removeFirst, removeLast, replace, swap by index, takeAt, takeFirst, takeLast
    • Additional Qt operators -- +, <<, +=, stream << and >>
    • Unsupported types by Qt -- allocator_type, const_reverse_iterator, reverse_iterator
    • Unsupported accessors by Qt -- max_size, rbegin, rend
    • Unsupported constructors by Qt -- multi-value constructors
    • unsupported modifiers by Qt -- assign, muli-value inserts, STL's swaps
    STL map and Qt QMap. These use nearly the same API as list and vector classes. They add the following.
    • STL types -- key_compare, key_type, mapped_type
    • STL search -- equal_range, find, lower_bound, upper_bound
    • Qt removes -- equal_range, key_compare
    • Qt renames -- lowerBound, upperBound
    • Qt adds -- constFind, insertMulti, key, keys, take, uniqueKeys, unite, values
    • Not applicable to map and QMap -- at, back, pop_back, pop_front, push_back, push_front, swap
    • Not applicable to QMap -- append, first, last, lastIndexOf, mid, move, prepend, removeAll, removeAt, removeFirst, removeLast, replace, squeeze, takeAt, takeFirst, takeLast
    • Not applicable to map -- assign
    Qt QHash. STL extensions provide similar classes, e.g., Microsoft's stdext::hash_set. THey are nearly the same as QMap
    • Not applicable to Qhash -- lowerBound, unite, upperBound,
    • Qt adds -- squeeze
    • check... -- Throw error on failure
    • try... -- Return false on failure. Do not throw errors.
    • ...Temporarily -- lifetime depends on source. e.g., toByteArrayTemporarily
    • ...p -- indicates pointer-to.
    • end... -- points to one beyond the last available
    • private functions -- No syntactic indication. They may become public later on.
    • Error messages -- Preceed error messages with the name of the class throwing the error (e.g. "ClassName: ..."). If this is an internal error, use "ClassName inconsistent: ..."
    • parameter order -- qhRunId, dimension, coordinates, count.
    • toClass -- Convert into a Class object (makes a deep copy)
    • qRunId -- Requires Qh installed. Some routines allow 0 for limited info (e.g., operator<<)
    • Disable methods in derived classes -- If the default constructor, copy constructor, or copy assignment is disabled, it should be also disabled in derived classes (better error messages).
    • Constructor order -- default constructor, other constructors, copy constructor, copy assignment, destructor
    geometry/inst/doc/qhull/html/qhull.man0000644000176200001440000011247513431000557017552 0ustar liggesusers.\" This is the Unix manual page for qhull, written in nroff, the standard .\" manual formatter for Unix systems. To format it, type .\" .\" nroff -man qhull.man .\" .\" This will print a formatted copy to standard output. If you want .\" to ensure that the output is plain ASCII, free of any control .\" characters that nroff uses for underlining etc, pipe the output .\" through "col -b": .\" .\" nroff -man qhull.man | col -b .\" .\" Warning: a leading quote "'" or dot "." will not format correctly .\" .TH qhull 1 "2003/12/30" "Geometry Center" .SH NAME qhull \- convex hull, Delaunay triangulation, Voronoi diagram, halfspace intersection about a point, hull volume, facet area .SH SYNOPSIS .nf qhull- compute convex hulls and related structures input (stdin): dimension, #points, point coordinates first comment (non-numeric) is listed in the summary halfspace: use dim plus one with offsets after coefficients options (qh-quick.htm): d - Delaunay triangulation by lifting points to a paraboloid v - Voronoi diagram via the Delaunay triangulation H1,1 - Halfspace intersection about [1,1,0,...] d Qu - Furthest-site Delaunay triangulation (upper convex hull) v Qu - Furthest-site Voronoi diagram Qt - triangulated output QJ - Joggle the input to avoid precision problems . - concise list of all options - - one-line description of all options Output options (subset): FA - compute total area and volume Fx - extreme points (convex hull vertices) G - Geomview output (2-d, 3-d and 4-d) Fp - halfspace intersection coordinates m - Mathematica output (2-d and 3-d) n - normals with offsets o - OFF file format (if Voronoi, outputs regions) TO file- output results to file, may be enclosed in single quotes f - print all fields of all facets s - summary of results (default) Tv - verify result: structure, convexity, and point inclusion p - vertex coordinates (centers for Voronoi) i - vertices incident to each facet example: rbox 1000 s | qhull Tv s FA .fi - html manual: index.htm - installation: README.txt - see also: COPYING.txt, REGISTER.txt, Changes.txt - WWW: - GIT: - mirror: - news: - Geomview: - news group: - FAQ: - email: qhull@qhull.org - bug reports: qhull_bug@qhull.org The sections are: - INTRODUCTION - DESCRIPTION, a description of Qhull - IMPRECISION, how Qhull handles imprecision - OPTIONS - Input and output options - Additional input/output formats - Precision options - Geomview options - Print options - Qhull options - Trace options - BUGS - E-MAIL - SEE ALSO - AUTHORS - ACKNOWLEGEMENTS This man page briefly describes all Qhull options. Please report any mismatches with Qhull's html manual (index.htm). .PP .SH INTRODUCTION Qhull is a general dimension code for computing convex hulls, Delaunay triangulations, Voronoi diagram, furthest\[hy]site Voronoi diagram, furthest\[hy]site Delaunay triangulations, and halfspace intersections about a point. It implements the Quickhull algorithm for computing the convex hull. Qhull handles round\[hy]off errors from floating point arithmetic. It can approximate a convex hull. The program includes options for hull volume, facet area, partial hulls, input transformations, randomization, tracing, multiple output formats, and execution statistics. The program can be called from within your application. You can view the results in 2\[hy]d, 3\[hy]d and 4\[hy]d with Geomview. .PP .SH DESCRIPTION .PP The format of input is the following: first line contains the dimension, second line contains the number of input points, and point coordinates follow. The dimension and number of points can be reversed. Comments and line breaks are ignored. A comment starts with a non\[hy]numeric character and continues to the end of line. The first comment is reported in summaries and statistics. Error reporting is better if there is one point per line. .PP The default printout option is a short summary. There are many other output formats. .PP Qhull implements the Quickhull algorithm for convex hull. This algorithm combines the 2\[hy]d Quickhull algorithm with the n\[hy]d beneath\[hy]beyond algorithm [c.f., Preparata & Shamos '85]. It is similar to the randomized algorithms of Clarkson and others [Clarkson et al. '93]. The main advantages of Quickhull are output sensitive performance, reduced space requirements, and automatic handling of precision problems. .PP The data structure produced by Qhull consists of vertices, ridges, and facets. A vertex is a point of the input set. A ridge is a set of d vertices and two neighboring facets. For example in 3\[hy]d, a ridge is an edge of the polyhedron. A facet is a set of ridges, a set of neighboring facets, a set of incident vertices, and a hyperplane equation. For simplicial facets, the ridges are defined by the vertices and neighboring facets. When Qhull merges two facets, it produces a non\[hy]simplicial facet. A non\[hy]simplicial facet has more than d neighbors and may share more than one ridge with a neighbor. .PP .SH IMPRECISION .PP Since Qhull uses floating point arithmetic, roundoff error may occur for each calculation. This causes problems for most geometric algorithms. .PP Qhull automatically sets option 'C\-0' in 2\[hy]d, 3\[hy]d, and 4\[hy]d, or option 'Qx' in 5\[hy]d and higher. These options handle precision problems by merging facets. Alternatively, use option 'QJ' to joggle the input. .PP With 'C\-0', Qhull merges non\[hy]convex facets while constructing the hull. The remaining facets are clearly convex. With 'Qx', Qhull merges coplanar horizon facets, flipped facets, concave facets and duplicated ridges. It merges coplanar facets after constructing the hull. With 'Qx', coplanar points may be missed, but it appears to be unlikely. .PP To guarantee triangular output, joggle the input with option 'QJ'. Facet merging will not occur. .SH OPTIONS .PP To get a list of the most important options, execute 'qhull' by itself. To get a complete list of options, execute 'qhull \-'. To get a complete, concise list of options, execute 'qhull .'. Options can be in any order. Capitalized options take an argument (except 'PG' and 'F' options). Single letters are used for output formats and precision constants. The other options are grouped into menus for other output formats ('F'), Geomview output ('G'), printing ('P'), Qhull control ('Q'), and tracing ('T'). .TP Main options: .TP default Compute the convex hull of the input points. Report a summary of the result. .TP d Compute the Delaunay triangulation by lifting the input points to a paraboloid. The 'o' option prints the input points and facets. The 'QJ' option guarantees triangular output. The 'Ft' option prints a triangulation. It adds points (the centrums) to non\[hy]simplicial facets. .TP v Compute the Voronoi diagram from the Delaunay triangulation. The 'p' option prints the Voronoi vertices. The 'o' option prints the Voronoi vertices and the vertices in each Voronoi region. It lists regions in site ID order. The 'Fv' option prints each ridge of the Voronoi diagram. The first or zero'th vertex indicates the infinity vertex. Its coordinates are qh_INFINITE (\-10.101). It indicates unbounded Voronoi regions or degenerate Delaunay triangles. .TP Hn,n,... Compute halfspace intersection about [n,n,0,...]. The input is a set of halfspaces defined in the same format as 'n', 'Fo', and 'Fi'. Use 'Fp' to print the intersection points. Use 'Fv' to list the intersection points for each halfspace. The other output formats display the dual convex hull. The point [n,n,n,...] is a feasible point for the halfspaces, i.e., a point that is inside all of the halfspaces (Hx+b <= 0). The default coordinate value is 0. The input may start with a feasible point. If so, use 'H' by itself. The input starts with a feasible point when the first number is the dimension, the second number is "1", and the coordinates complete a line. The 'FV' option produces a feasible point for a convex hull. .TP d Qu Compute the furthest\[hy]site Delaunay triangulation from the upper convex hull. The 'o' option prints the input points and facets. The 'QJ' option guarantees triangular otuput. You can also use 'Ft' to triangulate via the centrums of non\[hy]simplicial facets. .TP v Qu Compute the furthest\[hy]site Voronoi diagram. The 'p' option prints the Voronoi vertices. The 'o' option prints the Voronoi vertices and the vertices in each Voronoi region. The 'Fv' option prints each ridge of the Voronoi diagram. The first or zero'th vertex indicates the infinity vertex at infinity. Its coordinates are qh_INFINITE (\-10.101). It indicates unbounded Voronoi regions and degenerate Delaunay triangles. .PP .TP Input/Output options: .TP f Print out all facets and all fields of each facet. .TP G Output the hull in Geomview format. For imprecise hulls, Geomview displays the inner and outer hull. Geomview can also display points, ridges, vertices, coplanar points, and facet intersections. See below for a list of options. For Delaunay triangulations, 'G' displays the corresponding paraboloid. For halfspace intersection, 'G' displays the dual polytope. .TP i Output the incident vertices for each facet. Qhull prints the number of facets followed by the vertices of each facet. One facet is printed per line. The numbers are the 0\[hy]relative indices of the corresponding input points. The facets are oriented. In 4d and higher, Qhull triangulates non\[hy]simplicial facets. Each apex (the first vertex) is a created point that corresponds to the facet's centrum. Its index is greater than the indices of the input points. Each base corresponds to a simplicial ridge between two facets. To print the vertices without triangulation, use option 'Fv'. .TP m Output the hull in Mathematica format. Qhull writes a Mathematica file for 2\[hy]d and 3\[hy]d convex hulls and for 2\[hy]d Delaunay triangulations. Qhull produces a list of objects that you can assign to a variable in Mathematica, for example: "list= << ". If the object is 2\[hy]d, it can be visualized by "Show[Graphics[list]] ". For 3\[hy]d objects the command is "Show[Graphics3D[list]]". .TP n Output the normal equation for each facet. Qhull prints the dimension (plus one), the number of facets, and the normals for each facet. The facet's offset follows its normal coefficients. .TP o Output the facets in OFF file format. Qhull prints the dimension, number of points, number of facets, and number of ridges. Then it prints the coordinates of the input points and the vertices for each facet. Each facet is on a separate line. The first number is the number of vertices. The remainder are the indices of the corresponding points. The vertices are oriented in 2\[hy]d, 3\[hy]d, and in simplicial facets. For 2\[hy]d Voronoi diagrams, the vertices are sorted by adjacency, but not oriented. In 3\[hy]d and higher, the Voronoi vertices are sorted by index. See the 'v' option for more information. .TP p Output the coordinates of each vertex point. Qhull prints the dimension, the number of points, and the coordinates for each vertex. With the 'Gc' and 'Gi' options, it also prints coplanar and interior points. For Voronoi diagrams, it prints the coordinates of each Voronoi vertex. .TP s Print a summary to stderr. If no output options are specified at all, a summary goes to stdout. The summary lists the number of input points, the dimension, the number of vertices in the convex hull, the number of facets in the convex hull, the number of good facets (if 'Pg'), and statistics. The last two statistics (if needed) measure the maximum distance from a point or vertex to a facet. The number in parenthesis (e.g., 2.1x) is the ratio between the maximum distance and the worst\[hy]case distance due to merging two simplicial facets. .PP .TP Precision options .TP An Maximum angle given as a cosine. If the angle between a pair of facet normals is greater than n, Qhull merges one of the facets into a neighbor. If 'n' is negative, Qhull tests angles after adding each point to the hull (pre\[hy]merging). If 'n' is positive, Qhull tests angles after constructing the hull (post\[hy]merging). Both pre\[hy] and post\[hy]merging can be defined. Option 'C0' or 'C\-0' is set if the corresponding 'Cn' or 'C\-n' is not set. If 'Qx' is set, then 'A\-n' and 'C\-n' are checked after the hull is constructed and before 'An' and 'Cn' are checked. .TP Cn Centrum radius. If a centrum is less than n below a neighboring facet, Qhull merges one of the facets. If 'n' is negative or '\-0', Qhull tests and merges facets after adding each point to the hull. This is called "pre\[hy]merging". If 'n' is positive, Qhull tests for convexity after constructing the hull ("post\[hy]merging"). Both pre\[hy] and post\[hy]merging can be defined. For 5\[hy]d and higher, 'Qx' should be used instead of 'C\-n'. Otherwise, most or all facets may be merged together. .TP En Maximum roundoff error for distance computations. .TP Rn Randomly perturb distance computations up to +/\- n * max_coord. This option perturbs every distance, hyperplane, and angle computation. To use time as the random number seed, use option 'QR\-1'. .TP Vn Minimum distance for a facet to be visible. A facet is visible if the distance from the point to the facet is greater than 'Vn'. Without merging, the default value for 'Vn' is the round\[hy]off error ('En'). With merging, the default value is the pre\[hy]merge centrum ('C\-n') in 2\[hy]d or 3\[hy]d, or three times that in other dimensions. If the outside width is specified ('Wn'), the maximum, default value for 'Vn' is 'Wn'. .TP Un Maximum distance below a facet for a point to be coplanar to the facet. The default value is 'Vn'. .TP Wn Minimum outside width of the hull. Points are added to the convex hull only if they are clearly outside of a facet. A point is outside of a facet if its distance to the facet is greater than 'Wn'. The normal value for 'Wn' is 'En'. If the user specifies pre\[hy]merging and does not set 'Wn', than 'Wn' is set to the premerge 'Cn' and maxcoord*(1\-An). .PP .TP Additional input/output formats .TP Fa Print area for each facet. For Delaunay triangulations, the area is the area of the triangle. For Voronoi diagrams, the area is the area of the dual facet. Use 'PAn' for printing the n largest facets, and option 'PFn' for printing facets larger than 'n'. The area for non\[hy]simplicial facets is the sum of the areas for each ridge to the centrum. Vertices far below the facet's hyperplane are ignored. The reported area may be significantly less than the actual area. .TP FA Compute the total area and volume for option 's'. It is an approximation for non\[hy]simplicial facets (see 'Fa'). .TP Fc Print coplanar points for each facet. The output starts with the number of facets. Then each facet is printed one per line. Each line is the number of coplanar points followed by the point ids. Option 'Qi' includes the interior points. Each coplanar point (interior point) is assigned to the facet it is furthest above (resp., least below). .TP FC Print centrums for each facet. The output starts with the dimension followed by the number of facets. Then each facet centrum is printed, one per line. .TP Fd Read input in cdd format with homogeneous points. The input starts with comments. The first comment is reported in the summary. Data starts after a "begin" line. The next line is the number of points followed by the dimension+1 and "real" or "integer". Then the points are listed with a leading "1" or "1.0". The data ends with an "end" line. For halfspaces ('Fd Hn,n,...'), the input format is the same. Each halfspace starts with its offset. The sign of the offset is the opposite of Qhull's convention. .TP FD Print normals ('n', 'Fo', 'Fi') or points ('p') in cdd format. The first line is the command line that invoked Qhull. Data starts with a "begin" line. The next line is the number of normals or points followed by the dimension+1 and "real". Then the normals or points are listed with the offset before the coefficients. The offset for points is 1.0. The offset for normals has the opposite sign. The data ends with an "end" line. .TP FF Print facets (as in 'f') without printing the ridges. .TP Fi Print inner planes for each facet. The inner plane is below all vertices. .TP Fi Print separating hyperplanes for bounded, inner regions of the Voronoi diagram. The first line is the number of ridges. Then each hyperplane is printed, one per line. A line starts with the number of indices and floats. The first pair lists adjacent input sites, the next d floats are the normalized coefficients for the hyperplane, and the last float is the offset. The hyperplane is oriented toward 'QVn' (if defined), or the first input site of the pair. Use 'Tv' to verify that the hyperplanes are perpendicular bisectors. Use 'Fo' for unbounded regions, and 'Fv' for the corresponding Voronoi vertices. .TP FI Print facet identifiers. .TP Fm Print number of merges for each facet. At most 511 merges are reported for a facet. See 'PMn' for printing the facets with the most merges. .TP FM Output the hull in Maple format. Qhull writes a Maple file for 2\[hy]d and 3\[hy]d convex hulls and for 2\[hy]d Delaunay triangulations. Qhull produces a '.mpl' file for displaying with display3d(). .TP Fn Print neighbors for each facet. The output starts with the number of facets. Then each facet is printed one per line. Each line is the number of neighbors followed by an index for each neighbor. The indices match the other facet output formats. A negative index indicates an unprinted facet due to printing only good facets ('Pg'). It is the negation of the facet's ID (option 'FI'). For example, negative indices are used for facets "at infinity" in the Delaunay triangulation. .TP FN Print vertex neighbors or coplanar facet for each point. The first line is the number of points. Then each point is printed, one per line. If the point is coplanar, the line is "1" followed by the facet's ID. If the point is not a selected vertex, the line is "0". Otherwise, each line is the number of neighbors followed by the corresponding facet indices (see 'Fn'). .TP Fo Print outer planes for each facet in the same format as 'n'. The outer plane is above all points. .TP Fo Print separating hyperplanes for unbounded, outer regions of the Voronoi diagram. The first line is the number of ridges. Then each hyperplane is printed, one per line. A line starts with the number of indices and floats. The first pair lists adjacent input sites, the next d floats are the normalized coefficients for the hyperplane, and the last float is the offset. The hyperplane is oriented toward 'QVn' (if defined), or the first input site of the pair. Use 'Tv' to verify that the hyperplanes are perpendicular bisectors. Use 'Fi' for bounded regions, and 'Fv' for the corresponding Voronoi vertices. .TP FO List all options to stderr, including the default values. Additional 'FO's are printed to stdout. .TP Fp Print points for halfspace intersections (option 'Hn,n,...'). Each intersection corresponds to a facet of the dual polytope. The "infinity" point [\-10.101,\-10.101,...] indicates an unbounded intersection. .TP FP For each coplanar point ('Qc') print the point ID of the nearest vertex, the point ID, the facet ID, and the distance. .TP FQ Print command used for qhull and input. .TP Fs Print a summary. The first line consists of the number of integers ("8"), followed by the dimension, the number of points, the number of vertices, the number of facets, the number of vertices selected for output, the number of facets selected for output, the number of coplanar points selected for output, number of simplicial, unmerged facets in output The second line consists of the number of reals ("2"), followed by the maxmimum offset to an outer plane and and minimum offset to an inner plane. Roundoff is included. Later versions of Qhull may produce additional integers or reals. .TP FS Print the size of the hull. The first line consists of the number of integers ("0"). The second line consists of the number of reals ("2"), followed by the total facet area, and the total volume. Later versions of Qhull may produce additional integers or reals. The total volume measures the volume of the intersection of the halfspaces defined by each facet. Both area and volume are approximations for non\[hy]simplicial facets. See option 'Fa'. .TP Ft Print a triangulation with added points for non\[hy]simplicial facets. The first line is the dimension and the second line is the number of points and the number of facets. The points follow, one per line, then the facets follow as a list of point indices. With option 'Qz', the points include the point\[hy]at\[hy]infinity. .TP Fv Print vertices for each facet. The first line is the number of facets. Then each facet is printed, one per line. Each line is the number of vertices followed by the corresponding point ids. Vertices are listed in the order they were added to the hull (the last one is first). .TP Fv Print all ridges of a Voronoi diagram. The first line is the number of ridges. Then each ridge is printed, one per line. A line starts with the number of indices. The first pair lists adjacent input sites, the remaining indices list Voronoi vertices. Vertex '0' indicates the vertex\[hy]at\[hy]infinity (i.e., an unbounded ray). In 3\[hy]d, the vertices are listed in order. See 'Fi' and 'Fo' for separating hyperplanes. .TP FV Print average vertex. The average vertex is a feasible point for halfspace intersection. .TP Fx List extreme points (vertices) of the convex hull. The first line is the number of points. The other lines give the indices of the corresponding points. The first point is '0'. In 2\[hy]d, the points occur in counter\[hy]clockwise order; otherwise they occur in input order. For Delaunay triangulations, 'Fx' lists the extreme points of the input sites. The points are unordered. .PP .TP Geomview options .TP G Produce a file for viewing with Geomview. Without other options, Qhull displays edges in 2\[hy]d, outer planes in 3\[hy]d, and ridges in 4\[hy]d. A ridge can be explicit or implicit. An explicit ridge is a dim\-1 dimensional simplex between two facets. In 4\[hy]d, the explicit ridges are triangles. When displaying a ridge in 4\[hy]d, Qhull projects the ridge's vertices to one of its facets' hyperplanes. Use 'Gh' to project ridges to the intersection of both hyperplanes. .TP Ga Display all input points as dots. .TP Gc Display the centrum for each facet in 3\[hy]d. The centrum is defined by a green radius sitting on a blue plane. The plane corresponds to the facet's hyperplane. The radius is defined by 'C\-n' or 'Cn'. .TP GDn Drop dimension n in 3\[hy]d or 4\[hy]d. The result is a 2\[hy]d or 3\[hy]d object. .TP Gh Display hyperplane intersections in 3\[hy]d and 4\[hy]d. In 3\[hy]d, the intersection is a black line. It lies on two neighboring hyperplanes (c.f., the blue squares associated with centrums ('Gc')). In 4\[hy]d, the ridges are projected to the intersection of both hyperplanes. .TP Gi Display inner planes in 2\[hy]d and 3\[hy]d. The inner plane of a facet is below all of its vertices. It is parallel to the facet's hyperplane. The inner plane's color is the opposite (1\-r,1\-g,1\-b) of the outer plane. Its edges are determined by the vertices. .TP Gn Do not display inner or outer planes. By default, Geomview displays the precise plane (no merging) or both inner and output planes (merging). Under merging, Geomview does not display the inner plane if the the difference between inner and outer is too small. .TP Go Display outer planes in 2\[hy]d and 3\[hy]d. The outer plane of a facet is above all input points. It is parallel to the facet's hyperplane. Its color is determined by the facet's normal, and its edges are determined by the vertices. .TP Gp Display coplanar points and vertices as radii. A radius defines a ball which corresponds to the imprecision of the point. The imprecision is the maximum of the roundoff error, the centrum radius, and maxcoord * (1\-An). It is at least 1/20'th of the maximum coordinate, and ignores post\[hy]merging if pre\[hy]merging is done. .TP Gr Display ridges in 3\[hy]d. A ridge connects the two vertices that are shared by neighboring facets. Ridges are always displayed in 4\[hy]d. .TP Gt A 3\[hy]d Delaunay triangulation looks like a convex hull with interior facets. Option 'Gt' removes the outside ridges to reveal the outermost facets. It automatically sets options 'Gr' and 'GDn'. .TP Gv Display vertices as spheres. The radius of the sphere corresponds to the imprecision of the data. See 'Gp' for determining the radius. .PP .TP Print options .TP PAn Only the n largest facets are marked good for printing. Unless 'PG' is set, 'Pg' is automatically set. .TP Pdk:n Drop facet from output if normal[k] <= n. The option 'Pdk' uses the default value of 0 for n. .TP PDk:n Drop facet from output if normal[k] >= n. The option 'PDk' uses the default value of 0 for n. .TP PFn Only facets with area at least 'n' are marked good for printing. Unless 'PG' is set, 'Pg' is automatically set. .TP Pg Print only good facets. A good facet is either visible from a point (the 'QGn' option) or includes a point (the 'QVn' option). It also meets the requirements of 'Pdk' and 'PDk' options. Option 'Pg' is automatically set for options 'PAn' and 'PFn'. .TP PG Print neighbors of good facets. .TP PMn Only the n facets with the most merges are marked good for printing. Unless 'PG' is set, 'Pg' is automatically set. .TP Po Force output despite precision problems. Verify ('Tv') does not check coplanar points. Flipped facets are reported and concave facets are counted. If 'Po' is used, points are not partitioned into flipped facets and a flipped facet is always visible to a point. Also, if an error occurs before the completion of Qhull and tracing is not active, 'Po' outputs a neighborhood of the erroneous facets (if any). .TP Pp Do not report precision problems. .PP .TP Qhull control options .TP Qbk:0Bk:0 Drop dimension k from the input points. This allows the user to take convex hulls of sub\[hy]dimensional objects. It happens before the Delaunay and Voronoi transformation. .TP QbB Scale the input points to fit the unit cube. After scaling, the lower bound will be \-0.5 and the upper bound +0.5 in all dimensions. For Delaunay and Voronoi diagrams, scaling happens after projection to the paraboloid. Under precise arithmetic, scaling does not change the topology of the convex hull. .TP Qbb Scale the last coordinate to [0, m] where m is the maximum absolute value of the other coordinates. For Delaunay and Voronoi diagrams, scaling happens after projection to the paraboloid. It reduces roundoff error for inputs with integer coordinates. Under precise arithmetic, scaling does not change the topology of the convex hull. .TP Qbk:n Scale the k'th coordinate of the input points. After scaling, the lower bound of the input points will be n. 'Qbk' scales to \-0.5. .TP QBk:n Scale the k'th coordinate of the input points. After scaling, the upper bound will be n. 'QBk' scales to +0.5. .TP Qc Keep coplanar points with the nearest facet. Output formats 'p', 'f', 'Gp', 'Fc', 'FN', and 'FP' will print the points. .TP Qf Partition points to the furthest outside facet. .TP Qg Only build good facets. With the 'Qg' option, Qhull will only build those facets that it needs to determine the good facets in the output. See 'QGn', 'QVn', and 'PdD' for defining good facets, and 'Pg' and 'PG' for printing good facets and their neighbors. .TP QGn A facet is good (see 'Qg' and 'Pg') if it is visible from point n. If n < 0, a facet is good if it is not visible from point n. Point n is not added to the hull (unless 'TCn' or 'TPn'). With rbox, use the 'Pn,m,r' option to define your point; it will be point 0 (QG0). .TP Qi Keep interior points with the nearest facet. Output formats 'p', 'f', 'Gp', 'FN', 'FP', and 'Fc' will print the points. .TP QJn Joggle each input coordinate by adding a random number in [\-n,n]. If a precision error occurs, then qhull increases n and tries again. It does not increase n beyond a certain value, and it stops after a certain number of attempts [see user.h]. Option 'QJ' selects a default value for n. The output will be simplicial. For Delaunay triangulations, 'QJn' sets 'Qbb' to scale the last coordinate (not if 'Qbk:n' or 'QBk:n' is set). \'QJn' is deprecated for Voronoi diagrams. See also 'Qt'. .TP Qm Only process points that would otherwise increase max_outside. Other points are treated as coplanar or interior points. .TP Qr Process random outside points instead of furthest ones. This makes Qhull equivalent to the randomized incremental algorithms. CPU time is not reported since the randomization is inefficient. .TP QRn Randomly rotate the input points. If n=0, use time as the random number seed. If n>0, use n as the random number seed. If n=\-1, don't rotate but use time as the random number seed. For Delaunay triangulations ('d' and 'v'), rotate about the last axis. .TP Qs Search all points for the initial simplex. .TP Qt Triangulated output. Triangulate all non\[hy]simplicial facets. \'Qt' is deprecated for Voronoi diagrams. See also 'Qt'. .TP Qv Test vertex neighbors for convexity after post\[hy]merging. To use the 'Qv' option, you also need to set a merge option (e.g., 'Qx' or 'C\-0'). .TP QVn A good facet (see 'Qg' and 'Pg') includes point n. If n<0, then a good facet does not include point n. The point is either in the initial simplex or it is the first point added to the hull. Option 'QVn' may not be used with merging. .TP Qx Perform exact merges while building the hull. The "exact" merges are merging a point into a coplanar facet (defined by 'Vn', 'Un', and 'C\-n'), merging concave facets, merging duplicate ridges, and merging flipped facets. Coplanar merges and angle coplanar merges ('A\-n') are not performed. Concavity testing is delayed until a merge occurs. After the hull is built, all coplanar merges are performed (defined by 'C\-n' and 'A\-n'), then post\[hy]merges are performed (defined by 'Cn' and 'An'). .TP Qz Add a point "at infinity" that is above the paraboloid for Delaunay triangulations and Voronoi diagrams. This reduces precision problems and allows the triangulation of cospherical points. .PP .TP Qhull experiments and speedups .TP Q0 Turn off pre\[hy]merging as a default option. With 'Q0'/'Qx' and without explicit pre\[hy]merge options, Qhull ignores precision issues while constructing the convex hull. This may lead to precision errors. If so, a descriptive warning is generated. .TP Q1 With 'Q1', Qhull sorts merges by type (coplanar, angle coplanar, concave) instead of by angle. .TP Q2 With 'Q2', Qhull merges all facets at once instead of using independent sets of merges and then retesting. .TP Q3 With 'Q3', Qhull does not remove redundant vertices. .TP Q4 With 'Q4', Qhull avoids merges of an old facet into a new facet. .TP Q5 With 'Q5', Qhull does not correct outer planes at the end. The maximum outer plane is used instead. .TP Q6 With 'Q6', Qhull does not pre\[hy]merge concave or coplanar facets. .TP Q7 With 'Q7', Qhull processes facets in depth\[hy]first order instead of breadth\[hy]first order. .TP Q8 With 'Q8' and merging, Qhull does not retain near\[hy]interior points for adjusting outer planes. 'Qc' will probably retain all points that adjust outer planes. .TP Q9 With 'Q9', Qhull processes the furthest of all outside sets at each iteration. .TP Q10 With 'Q10', Qhull does not use special processing for narrow distributions. .TP Q11 With 'Q11', Qhull copies normals and recompute centrums for tricoplanar facets. .TP Q12 With 'Q12', Qhull does not report a very wide merge due to a duplicated ridge with nearly coincident vertices Q14 With 'Q14', Qhull does not rename vertices that create a duplicate ridge .PP .TP Trace options .TP Tn Trace at level n. Qhull includes full execution tracing. 'T\-1' traces events. 'T1' traces the overall execution of the program. 'T2' and 'T3' trace overall execution and geometric and topological events. 'T4' traces the algorithm. 'T5' includes information about memory allocation and Gaussian elimination. .TP Ta Annotate output with codes that identify the corresponding qh_fprintf() statement. .TP Tc Check frequently during execution. This will catch most inconsistency errors. .TP TCn Stop Qhull after building the cone of new facets for point n. The output for 'f' includes the cone and the old hull. See also 'TVn'. .TP TFn Report progress whenever more than n facets are created During post\[hy]merging, 'TFn' reports progress after more than n/2 merges. .TP TI file Input data from 'file'. The filename may not include spaces or quotes. .TP TO file Output results to 'file'. The name may be enclosed in single quotes. .TP TPn Turn on tracing when point n is added to the hull. Trace partitions of point n. If used with TWn, turn off tracing after adding point n to the hull. .TP TRn Rerun qhull n times. Usually used with 'QJn' to determine the probability that a given joggle will fail. .TP Ts Collect statistics and print to stderr at the end of execution. .TP Tv Verify the convex hull. This checks the topological structure, facet convexity, and point inclusion. If precision problems occurred, facet convexity is tested whether or not 'Tv' is selected. Option 'Tv' does not check point inclusion if forcing output with 'Po', or if 'Q5' is set. For point inclusion testing, Qhull verifies that all points are below all outer planes (facet\->maxoutside). Point inclusion is exhaustive if merging or if the facet\[hy]point product is small enough; otherwise Qhull verifies each point with a directed search (qh_findbest). Point inclusion testing occurs after producing output. It prints a message to stderr unless option 'Pp' is used. This allows the user to interrupt Qhull without changing the output. .TP TVn Stop Qhull after adding point n. If n < 0, stop Qhull before adding point n. Output shows the hull at this time. See also 'TCn' .TP TMn Turn on tracing at n'th merge. .TP TWn Trace merge facets when the width is greater than n. .TP Tz Redirect stderr to stdout. .PP .SH BUGS Please report bugs to Brad Barber at qhull_bug@qhull.org. If Qhull does not compile, it is due to an incompatibility between your system and ours. The first thing to check is that your compiler is ANSI standard. If it is, check the man page for the best options, or find someone to help you. If you locate the cause of your problem, please send email since it might help others. If Qhull compiles but crashes on the test case (rbox D4), there's still incompatibility between your system and ours. Typically it's been due to mem.c and memory alignment. You can use qh_NOmem in mem.h to turn off memory management. Please let us know if you figure out how to fix these problems. If you do find a problem, try to simplify it before reporting the error. Try different size inputs to locate the smallest one that causes an error. You're welcome to hunt through the code using the execution trace as a guide. This is especially true if you're incorporating Qhull into your own program. When you do report an error, please attach a data set to the end of your message. This allows us to see the error for ourselves. Qhull is maintained part\[hy]time. .PP .SH E\[hy]MAIL Please send correspondence to qhull@qhull.org and report bugs to qhull_bug@qhull.org. Let us know how you use Qhull. If you mention it in a paper, please send the reference and an abstract. If you would like to get Qhull announcements (e.g., a new version) and news (any bugs that get fixed, etc.), let us know and we will add you to our mailing list. If you would like to communicate with other Qhull users, we will add you to the qhull_users alias. For Internet news about geometric algorithms and convex hulls, look at comp.graphics.algorithms and sci.math.num\-analysis .SH SEE ALSO rbox(1) Barber, C. B., D.P. Dobkin, and H.T. Huhdanpaa, "The Quickhull Algorithm for Convex Hulls," ACM Trans. on Mathematical Software, 22(4):469\[en]483, Dec. 1996. http://portal.acm.org/citation.cfm?doid=235815.235821 http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.117.405 Clarkson, K.L., K. Mehlhorn, and R. Seidel, "Four results on randomized incremental construction," Computational Geometry: Theory and Applications, vol. 3, p. 185\[en]211, 1993. Preparata, F. and M. Shamos, Computational Geometry, Springer\[hy]Verlag, New York, 1985. .PP .SH AUTHORS .nf C. Bradford Barber Hannu Huhdanpaa bradb@shore.net hannu@qhull.org .fi .SH ACKNOWLEDGEMENTS A special thanks to Albert Marden, Victor Milenkovic, the Geometry Center, Harvard University, and Endocardial Solutions, Inc. for supporting this work. Qhull 1.0 and 2.0 were developed under National Science Foundation grants NSF/DMS\[hy]8920161 and NSF\[hy]CCR\[hy]91\[hy]15793 750\[hy]7504. David Dobkin guided the original work at Princeton University. If you find it useful, please let us know. The Geometry Center is supported by grant DMS\[hy]8920161 from the National Science Foundation, by grant DOE/DE\[hy]FG02\[hy]92ER25137 from the Department of Energy, by the University of Minnesota, and by Minnesota Technology, Inc. Qhull is available from http://www.qhull.org geometry/inst/doc/qhull/html/qh-optt.html0000644000176200001440000002574613431000557020216 0ustar liggesusers Qhull trace options (T)

    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions


    [delaunay] Qhull trace options (T)

    This section lists the trace options for Qhull. These options are indicated by 'T' followed by a letter.

    Copyright © 1995-2018 C.B. Barber


    » Programs OptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions

    Trace options

     
    General
    Tz
    output error information to stdout instead of stderr
    TI file
    input data from a file
    TO file
    output results to a file
    Ts
    print statistics
    TFn
    report progress whenever n or more facets created
    TRn
    rerun qhull n times
    Tv
    verify result: structure, convexity, and point inclusion
     
     
    Debugging
    Tc
    check frequently during execution
    TVn
    stop qhull after adding point n
    TCn
    stop qhull after building cone for point n
    TV-n
    stop qhull before adding point n
    T4
    trace at level n, 4=all, 5=mem/gauss, -1= events
    TWn
    trace merge facets when width > n
    TMn
    turn on tracing at merge n
    TPn
    turn on tracing when point n added to hull

    »Tc - check frequently during execution

    Qhull includes frequent checks of its data structures. Option 'Tc' will catch most inconsistency errors. It is slow and should not be used for production runs. Option 'Tv' performs the same checks after the hull is constructed.

    »TCn - stop qhull after building cone for point n

    Qhull builds a cone from the point to its horizon facets. Option 'TCn' stops Qhull just after building the cone. The output for 'f' includes the cone and the old hull.'.

    »TFn - report summary whenever n or more facets created

    Option 'TFn' reports progress whenever more than n facets are created. The test occurs just before adding a new point to the hull. During post-merging, 'TFn' reports progress after more than n/2 merges.

    »TI file - input data from file

    Input data from 'file' instead of stdin. The filename may not contain spaces or use single quotes. You may use I/O redirection instead (e.g., 'rbox 10 | qdelaunay >results').

    »TMn - turn on tracing at merge n

    Turn on tracing at n'th merge.

    »Tn - trace at level n

    Qhull includes full execution tracing. 'T-1' traces events. 'T1' traces the overall execution of the program. 'T2' and 'T3' trace overall execution and geometric and topological events. 'T4' traces the algorithm. 'T5' includes information about memory allocation and Gaussian elimination. 'T1' is useful for logging progress of Qhull in high dimensions.

    Option 'Tn' can produce large amounts of output. Use options 'TPn', 'TWn', and 'TMn' to selectively turn on tracing. Since all errors report the last processed point, option 'TPn' is particularly useful.

    Different executions of the same program may produce different traces and different results. The reason is that Qhull uses hashing to match ridges of non-simplicial facets. For performance reasons, the hash computation uses memory addresses which may change across executions.

    »TO file - output results to file

    Redirect stdout to 'file'. The filename may be enclosed in single quotes. Unix and Windows NT users may use I/O redirection instead (e.g., 'rbox 10 | qdelaunay >results').

    Windows95 users should always use 'TO file'. If they use I/O redirection, error output is not sent to the console. Qhull uses single quotes instead of double quotes because a missing double quote can freeze Windows95 (e.g., do not run, rbox 10 | qhull TO "x)

    »TPn - turn on tracing when point n added to hull

    Option 'TPn' turns on tracing when point n is added to the hull. It also traces partitions of point n. This option reduces the output size when tracing. It is the normal method to determine the cause of a Qhull error. All Qhull errors report the last point added.

    Use options 'TPn TVn' to trace the addition of point n to the convex hull and stop when done.

    If used with option 'TWn', 'TPn' turns off tracing after adding point n to the hull. Use options 'TPn TWn' to trace the addition of point n to the convex hull, partitions of point n, and wide merges.

    »TRn - rerun qhull n times

    Option 'TRn' reruns Qhull n times. It is usually used with 'QJn' to determine the probability that a given joggle will fail. The summary ('s') lists the failure rate and the precision errors that occurred. Option 'Ts' will report statistics for all of the runs. Trace and output options only apply to the last run. An event trace, 'T-1' reports events for all runs.

    Tracing applies to the last run of Qhull. If an error is reported, the options list the run number as "_run". To trace this run, set 'TRn' to the same value.

    »Ts - print statistics

    Option 'Ts' collects statistics and prints them to stderr. For Delaunay triangulations, the angle statistics are restricted to the lower or upper envelope.

    »Tv - verify result: structure, convexity, and point inclusion

    Option 'Tv' checks the topological structure, convexity, and point inclusion. If precision problems occurred, facet convexity is tested whether or not 'Tv' is selected. Option 'Tv' does not check point inclusion if forcing output with 'Po', or if 'Q5' is set.

    The convex hull of a set of points is the smallest polytope that includes the points. Option 'Tv' tests point inclusion. Qhull verifies that all points are below all outer planes (facet->maxoutside). Point inclusion is exhaustive if merging or if the facet-point product is small enough; otherwise Qhull verifies each point with a directed search (qh_findbest). To force an exhaustive test when using option 'C-0' (default), use 'C-1e-30' instead.

    Point inclusion testing occurs after producing output. It prints a message to stderr unless option 'Pp' is used. This allows the user to interrupt Qhull without changing the output.

    With 'qvoronoi Fi' and 'qvoronoi Fo', option 'Tv' collects statistics that verify all Voronoi vertices lie on the separating hyperplane, and for bounded regions, all separating hyperplanes are perpendicular bisectors.

    »TV-n - stop qhull before adding point n

    Qhull adds one point at a time to the convex hull. See how Qhull adds a point. Option 'TV-n' stops Qhull just before adding a new point. Output shows the hull at this time.

    »TVn - stop qhull after adding point n

    Option 'TVn' stops Qhull after it has added point n. Output shows the hull at this time.

    »TWn - trace merge facets when width > n

    Along with TMn, this option allows the user to determine the cause of a wide merge.

    »Tz - send all output to stdout

    Redirect stderr to stdout.


    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions


    The Geometry Center Home Page

    Comments to: qhull@qhull.org
    Created: Sept. 25, 1995 --- Last modified: see top

    geometry/inst/doc/qhull/html/normal_voronoi_knauss_oesterle.jpg0000644000176200001440000005656413431000556024770 0ustar liggesusersJFIF;CREATOR: gd-jpeg v1.0 (using IJG JPEG v62), quality = 80 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((8" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?9dyLV 4dv(7'ϩbܟSFOb}Mv1@ 4dv1@ iqFnO'ӶѶshӱFo>}M;mh2}M;mh2N1F4v4Q?U|nS@EhӶѶ iqFnOSN@}M9ih7SFObPr}Mv1@ 4\QN}M&Omm'>SN2}M;mh>}M;b:^}M.(7SG>1@ 49]r(rhb I1@\S=(3SF(QePE?bEIIe(IFq܏ktK$9>*qnlSgOBblYtg,q{N^/̂:,o$$~d=HA"GIEX8W*"cOpj,U2QO.G1OQ~)p=(:)E?b*LJ0(^m)e7Xs>#ծ;BB{䏩4Ԯ:.r"URKȢB$@O#SexNm5ppLB F?K{QLi,nC HM;,Qb"qF(bTXF*]b(K6`".*LQ,X&)qEr*MQ`%#M1^M&mj]mѶFmvѶ"Fڗmh-mvѶ"j]mъmh-mH:+-k{hۉ`0T)Ǒ)7a؃mR[F@rXo[i(Rm#u's7דhV׭ԟe.?xywk)nE;WMدmNTcSϔ؏Op+6*109vL[+Z*c\! )t0Aֽ3á|Ñ7JeuӒWe-q㇑yLwb&[bvnb[8$AݽX#ux<}C6XVtoņ=od8Ysx^a飶=`?U\5:6[c`{q}Zk+Io C$ȱƣԞLW2V[hIqKQ;'Q=?yCY;Gf؝폠E*'<);s$L9l~k gў6"ƺ D^yi26ԻhZE:UK1ɦ\4V၇UoP2=mkZty\\{T-?Tf-zFJm* ($–t*HgOO\8!%U=V,D:vqLk;F$cPy+m[N[!l~x &~fBǀ ZhnuP5? :vf;f>rM&vKK ȢH ;U23G66dWn'U-E.6Pm(^EJWI+&E.6[hRmE.6[hRmE.6[hRmE.&9`̬;O_.>Z߽(tm%gk,ߪ(P_$`7XH;Uγ˷C+O.bx5[h[GҼK Wa6wo9,E7B?\LHW2Ў E}7|Ag;%Ȓ+nx#q\ׇK6Z4Bܫ qߨn~g.H*ø# nEwd1ʌUS >C07IH3,_'WA(mS2Q {ޣZ?#RƎRnOwmбs5m9uOirq /}#Ɔ{ibiaY^6*WS]zi~dy, sЄsޱ.[T5(_I~X^)Br/p0~?QTn`y`9bHO$`7!fVCцOl;76ZZ Q>#`sp: H4[zFړw?aY8='hHŞ9H4˶; e<,l|S*ˢk#x\3.fϨL#BFF=X &}Z6G ȧ搑daֺ^B4x5;_[l8o9t:K5-Jq A4TFtϥm\Fe FzԞk˵ ۭFowQSeM>3$olr?6b%^{eB)O#oo#l㵏1T"Υ}rgEm+U5P ¤c1F*Օmbi$ER^A?Ϸ˯42A 2 w=6u8 He='r.V܀?>fMJ诇3 ^X8) i[ٖowpFGݕSKI Dp2}QCXm*a}AM<-*@eKRm3'01qFSsҔ:Ndg&h`9zedq3Jv^M2 x yVV =n%ZYwOݩcM?²i2eFPm;<%}yu CZrL1=wsݏ$3XXmvH(9VF W6.pKT*#TPKzǒiy:fK q) vp;.6եbHF*]m",RFr*MPb#F(Qf(?F*LRbLT)qRbRbbMofZhVxW8D5qi7:vs"&"UvR`N ֦MlR]O<>ttEGr>U隥ޚX[8܏{=p+LM)8Z[Ov׽s(}v[mo>>Me N(q1V/t}< uSnWMCi 5j~/5 ,dK LDQ ['=] $AV?F1M)Vf9bWHq%;Ers.I!PI;#V>s`cHҺm{Iγᘖ+]*0L{CƦRC$2*0J랇5oC兼(|d$bpIRON;T?z1xզ]s?Ss]Kݣ"I-ڮMaH#8~< GWmyVeT"`&рVd&.xvhګj$ZH7 wwֳ^Q ?UPsTr};/ޮm{g}My|HN20TdE )"#QI}NO~+G2 㟭ex2jַ5E*֩X'7ed7CYlv(AkQk?iYGh-r9Sk'pj1@~(b$ɣ94`Qn(;b1NbQf\SF( X. rE4FdRzҷ5J ?Z򟈷 &g_DBaꚝqWs\:;GVxs0taف?>^grz=II-00,NF_p 1MkDi:ʗOb ;a5ڵ?1J>T>v}UEŁnܮv3S\$k&z,aL2t+H΋6#O?j$u r[Iln^|=KOunEra,OMK~1\o立 ߺg2h2i3{ ,G?]zi_t98Mwn3!?]\ޙ_kW-3?d^c3C-kWzu5Cio&¨4I 3LmID?iF*0Rx5v}#^ C8|SPԵ˷Ү&n_ 9,}Ip59#1+g8~# -qdxl$cצ9|V$ikğ=;+ `EBJO9wzMxS7Fn\#\:(Ŀ. N5)DGQXl1nu@S~O^_gۤ^=n z1d2kY4Km.͏٭cG^;RqgΌs-G9EqB<3p 5vuZ:Ff+Gbvd8ztL,LT{`4;]OE摱RW<|u߄FyfuWb \"8;W?奼 rɖP2Fy ){×g 2&EwC ʯxVHWjor\C=?O߸\LyoECOW|u{\_+Ԯ!#b%6] 9o bd*pu?I-?k/d)0%UF>מΘu Gx_ֻ(u+GUBsobK'No\aA`%9d-5]?g>:fq#M!9%y?:^طꚎev c_?:u%m{n"-?#yZvgh JԊU1v)@欑ъSEQ@(P1EbQEo."{"1RM,V<,RGV={-:4ʱ%CkVi'H4:-pN2qer"O‡Q]| 9t dU/ j39jˑ#YxA%7]Ÿ7$ź8# Mܴu, kŨ9`?_\kH$`t֮cz$JlG3cuAWCMV-fQٸ>jHIG,+Et빭Vs ~'8Z.Ҵtms\Y4B2B+`1rqT/#>#O!q/=EI;/FYF 6H*Frnr >S$m$fkvU9=c[dWNP˝רivl:KvM|>1ҵ<}g=KMg$My8 (`4mEX(s;E_-3;^u&\\]O 93+o^7|2ۡ#L \Ιi-a$v{_R4纷q֨tlpy_\MrZq+`u':=b4F&xO1c\94bM^αRFTg& ]J>k%Ŕl*X}zuVcYO7ogiRܯ촕K}+yN:0;=zכI Zs]"W-sh 1۷:O \%ΒA:?[ahdz(=H5|w{-4Vr2 m Hl+¨ $¿_ʛ3#>3" dWD-xͺUg˛X~!AX1oǏj -.@d׆ޙks$O Csc9uPj?Q%M?s&v2z.# \#v t=4}KDZ[+W/eu` %dHe,} rFpx4yڔ1L>Xp[je`uI<dZ8V8_R]*)bcq\xNPY&-%~I;W>z@h;[dmSwd`O|qu#1W!Լ(å߮{o1K2%{{ }Hc dx>S@ D -)/KiaCMjPk%Z2 =r΂Bٴ;DkG#,¸g_}$z_kj`; owvQpJCD>Q@݁wɐdI+/ =E,!?zwO@jco}GRӚg%=Kp<rzltdWPRdu?B5亵fa# s3YwԒE#8?y yLol>T#v'\uƿjBYnF==wG4?~O5'FX@ʁy.#|qRH9#~ 2\  ǘ#gMʣ0~}i43{tӨIzo5/:Xh' 0GIt _ʏ cIs>":dKR?wb9j6c(ڵm,3ģɑ,pGx}zzֿ]h v2=S u/jI!7\ˎs?+5dWFiXgO|z 1v#i_*sf7$}+5[Z_&Y[E GFjvԿV j8dh?,pMADЩ;$uRs@SejWkWAb + O77iq8"71V )|i+DF6?!__5k0hcrhY:M,~Srk n^S[o*+ƻ.-_pbAmچ.Tb̞c@PSV|K<:fɄb7΀mk"|&O(& `c-]vcu oO+I7c*[lzYOs; <ʸ ޵5{_oa?sxyi? ex<'acY\n\2R9 ypZ=4]\6yY>Vzԭ$QbLE;a󯪞GQi̿jx&zr? 1HBNC/Պ^ՠYﹳt6Wybb_2Nv*WK}N\[uhp;}}˘55͛8v8ߵ[ vkGĐG'ɴ͜p`:V_R gVke_}X&F+=~swg-~yXsE}>YeB $ wA0u,i*31dٿqM2O T@;9l<#s:])Ey %Oa?LsY@sGo=>wY äIW[T 9 7#zVŦ\#1FI1ֹ [W^"ծK[Y~sr_?,hH=7\sm`!{0#2XHjvcV.OL@_wWke$7df NNuIutÚ :?1GQ?wS?#jחzeE_]8.02NA*|?{.G5ky$ȑ:4=x]_ wde8ƲG9'?ˍ?EVR͝|Ԑ y`=tR6=6&{&x'Uh~)_t񥦝qY1жA{= R.ldn'v}J.k6VO_0˞>+^Ǿ'mjo˃ Nnv;W=sÖɩkm"Ν "CXl'ϭ 0u0[# 285 Ɨ)yHqrxfO{ 6ldO,iw#;I&G}=m茿09!Pvx#S^Tm}4}# )g KKn FľcGHt WX?Nȕ`?jpv_ 35uh_*?Ƌ\ ivZlؖQnK)RZּN5OC#Fd00 |\Γ uEѮx&l H|kLfhi~W%m46|O~"9$ 1_wqnI @d!I;){O' wiYRNM5qwQU;=M%QP,fɷqQ܁>]i#޹tЃ~5ZZ܎N3b?CUN2?$c]:K zIkwm{!}wH?YLӴk-,?ј6o4:Ey8jN*th7/.ۇ# ʒɏ|R~Mu!g9*mdBr^v-녦Iw#,+2`>xa 34S[1FN -q<O*TO˪'(?fvyqk!HX[ciΫWQ[X-JK1#WYxN]OMMG_Hє(㆔qaH\ 1A7T5B Mfj ۖK|dy=cޣ]ҝ39m@ 8E$@G%‹c[13Y|%m<5L|cs-W/elnLZf싞~XƓcv1XH(A!Ԯe$#s;k- iʓΕT.#sUtnpἫ䅳봑x&i"PeH=,4KlZI$s cwŜ@* |1DqЌ#gwZj ql) oSahm8S?*+-ZZu a#G_Ju<,W2؊?pme91ïݔ ?,<2\,PKoBOW.]#6;V~ᗆ_ ӌGg=0,>=_g_|P{=,2Qdvk%0Li8f9kS~/T-&EœnZ=s@+d`AN2}9"Dcx" )f̸dG(<}\mYM򤩎D[a++ᔌضPDaԬ2>~|W[ekvSj%2c^o1Xu `lAV͝+wZI<;=rz-K>]l4{k[HՙG{ 5ީ e$ R3i+2ص"Dr@0* wX&d2-;zS`;v6ZXY.o!vWZ:G9"9X_[ ?,VΝ6wɐr^O r^BU [RH,bs8v׸bLorOrwQCH"DGĎhW3 I C}M܎}GW.=o "Ub0N$U#Tm.)Mldo߀ڿӠ#Z0ZXJGBK@\v횻cCw*[HnLkg8@qVb-GnA,as= Oy[HbصuFR2&rz~{$7,mGOOBmjkQ8**G%㳈ŧ Au5$Iɤ$Q@4y4fE3p@oKor諞&[4Ju9m y'$_[fm#>`z ľFz55?Mm7fZZۤ,~ 5~`/QE~FcN4Fx?W::'a0?)X vq\+Ed FqPH<c_lx<ʏMm33zM[1!,9a`mc6_%7<-babXzzV,bK(Ze6f)?V|z>ԯkEe ti [˭3SwE 0>\?_Ynt{#B\C#ذ{;5)N$fUS+ۋ`?l%K uu9fbʨH](}j'&KN8Q񷉆n&]8(bIr9 ~ϋ[rA!h`Ve`yL>aH9څB`3z\k&pH|^=IZ|O RYZ Qm֥zdW#prCLj/yvć;}Os]HF%4P}vj[f.'ߞec'ֺݙ)걢4YA>N?,_< ʑxKKjPDZۂiCi8[?ަt;JK`7G]Ne"Tl8Ì\,p Ӹ, cv>7@D %f~mx TSxQfLUЕ<$$v=>֡y?8imo{igc< D܊Ouoܓ'?8c'{TFNMx0j ι I&3+e [;}::ϥ]jyJ3@,\yJWѯ<ɴUx8{fDh-@IwS48n%g)hPԽ$e>^=9׊um{ė&$V*T:Ptдɮo"̒00*?°c#\gm  lo\EMo!IcӯNX%Yd9fR+ yyPje;KW/Pɴz,_S&iPEkm$nC;&ryՙ#4r6 ߑ2k +Cq`a5|)2_[['rTz 9,,q ,zּalnF HMzQ<7D-\}?fib{ morY?{ d` t5iEXw}C}V[vhGsl7%?盏ukrLu-74f@M(<[Iij."z@3'C?__ƚTM%&ʷZm6>l?FL2:{8?1Q<rs]D yeuhUJNH~W)Q$|d}ZVhg(Қ=lϟ-}?βn˕$RqL?Ydr/ތoQUoNwi]ɶ %cہӣ[ FVۆ6Ԛvjzڗvܪ@ xԴ)W~/pOCsE}b$Kens1[z.faO[ ;͐%E>X GPd˶Bw9+ SX:@c5r"F S=ɮDٲv@dW3iq'Kmui*dEU!l+4==X6+d|8?0 ]{[{-9aL6 " Lih>5>o/^KO"Sްucy6F-`SO±HF 䃕Onx?IjZǵ8 cR8NeUur۷sۺm?{#eHz`'ރo/xd~TX.]BYe ygt+YI)qpzWټ)ũh,d*s׀^7ߕt0ĦKUUUFGx&CLkҦq,zeʉJ HW~Sm;:o|+tMceKk%x\!8!sx'V͡msJ8{(ivp3ހ%ѥ:NH-!pVmw~u^ݫ7>f{km>~z}_úcv9b=Iܿϡ9Py';&۩*̌ː8^W^'J mzlBIjqmq*b 0>ԓۯ!m(^^u]*3V8p';#֏im#B䬇ЧFEuާo41j ivѪ./W08I_P+t;ukЇgt뛛=bO6:n z}y8/0ĶP?9cBo Q.o #C{g;!~y^Xb{u `TV#"~f̋'vGPXF?ҧ6.' X4:[\MEea2+ K'1bB8>WKç3FC~|biBܠ57UJ偠Mf̚d'A!? ЙnZ9b2]Z?Htn'9:V ˦_teʘnc=o!6 p@dq}EF8^(ѥ6k$3K=O8ۑPUId5=ZT`Pyjp<ǓQ֣jcFPmPLG DVHeV-YaL+@Y*&J£e+ Q:U֎cd@µ* cjʱEwsUFI5uo.܃m;ƧWA"@vK"o}kНrcm#6[7n'ҦҤLKH|e^7Y2ȭ9d|-gU@l$e?C<`jl;ᾲuZ\h R2Br0acb{5g.͎E a>J" E{ErU;OԮ#$۠'IeF4B2^*/جLqXbK_&wڅZ,k./a" pH`sm0~bM(Q92#z֬4طOSO(@¹fQ$UsTһiijji0TaD<6{g^r#ZK=Ek[˺[ˈ}}FGruY%ӶʼnGO+Ҽ@𾺿+Bǟ@Ce6:՘$,-İs{jy>/%`k{Gλf_,rvzLgݜ5xX·Ǩ37?'sRY΄K!tjgSp_cU+v HU*uf/oa3I]>ui9!dvQ] iq+/)V@7=Ϯl\KHDUGh!0qҡ{|k\iQI]ڲtcE=}xY~qq0!}GZʸЈɉ49.*'!Ci >$er}a+6<⻟7DVc5Y46,!<=QkiW|S/]O.ձuߕuE5I-a=MOk,77ZQ.aǣ kZ)f;8dxzz;Rm`1da?pilv4a+(=) Q`9fT?<~@ƿtz~ӊ,?XO@z+Y9ơ{9Cja]smKnSiRĝ}*\22};4Lr=G45k4E~3@ I+n rMO³I2/ ~B# qM,8mF9n-م^J?urGy5?ι֑yf<z3}7z P{ -݃\tu<QV,zJȪ2E0`M5#HE1ȦeSJZiZVV )M+V Jr-Z)Ld ijJBB*McZB mR hVRtM<տ..\J<{U.FC]&|PGd_+*ݽ+In5ׄ9 j *ofF+P0 tҘ)FL~_5q+EAmEe O}XKІ#5^]>&V]0XUi-g=`1ڋS$0K%H0UIteO+ZF ߌ׸5IifB֤?v3ɄOgtIfCѢV,R==Kaj!<$2S!Y#bjD*5JxE0jB=PJJB=(4݇Ҋ( JiCQE44PJnE4} PlEl}(e.j(oP!vJO,(a1QEg'EBҘc4QLC qҘP(cZ>:TMJ(6CE Bғ>Q@ivJ(';gP>!B'=)=,~*3J( =e(?geometry/inst/doc/qhull/html/qh--cone.gif0000644000176200001440000000560213431000556020016 0ustar liggesusersGIF87add[~.TopPZ OXa>Uj3.OP*$?C1Lk5k4'#?9$k)KTRn(7+-aV_"/*&%Z@K\!JZ W=:5Q7]/F@(Y()]/D_)$&Kb>2;<}M7%,'X{-NY zvsUu*$j(f2?S=Q"9-\N:?I^C]B\AOz1L[!<6^caqpm_0T^"S\!86Oq*1.$PBCK$4''TVFEO:PRW.OQ(%MOk5?9LX >9&!608&U3+j>=}IU_"L`#E-$!T;QBGcZ{-2i*b>P:3-2-OP6%ikj3Q($qMY $j)put(D\01#:{O]C%.;B/)*"+{~{M5R\!QZ %FK33<520[@G3PD{7%k)LSSMq,-a.6/21.]B$#Xz[<=>9$g+<7Ls.HP_0Z*F?D^I.Qt,#TQM4=;$$GdQBAGCH;{K3=)&Q*&8[1/GP*HTVSUb0:)QSNPEd#'34Uw,>8Ne%\ZT'&U^" ::9s;!d*Ea=~L0+)*,dde H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗%-s_ & 5M"#[ u-[4S"ENUr"P:z:uv#gV=Im 6Rf$BտfՙދI^DS:\1 ƘWO/`%9s? بV6\vlr־MZ.!*gf͵ {lᄈ'[C vJ dP0|AnOP N~AL3j )B2nl}Uh8IQa"ލU%b8֝GFs &\hr/k\((y"*fib) h}fib!h&RUh漉Y |)daKLb 4s-R棁}ӌ~@y^PN$tsUɭ*Bd6yv>ݐb(kqъȥ^ Uᰬ4;j8֗C%6;gPNjqT,$ń& qh6@APQpc;LlŸkCM01$z TT`rLx\Wӧئk4U`JjIPǁ*. l% c)ҌTD Ph/qFC`݌fCpƽ+۴+I#uםr/|JG|, $L:|iktsD-P mhJ=ܰOC6 8<hJt9t|-H<VZU `6''C +62"M)uBV"MH>qK^bOGI7c4@nqy|5@%C` Ri`PȜ)Aڙ K1B_x(D!ɮ۳Ce(q!@U J s4iRl1~ޣx&m0K%i D+>>A,LFO@V$=ajD9HIQ#B"O]*1JP$YUZhP| KU@oa#F$C8w10$9h9Z( F_.*I0U0e@&PfR(h;=AGXCLkѪ(7IҀd*j)HBnJZ1Р X0$ 42,@]-@N)H?RN1 zQ1bԢF*Rz<i)XBIJ .@67en$GEP>)?TRufAkhC@m#1J :,([7Pc-4: ZfV& g hcȶ,nvu&3A>WuH\6^uK굳=oA@mڰ@KI :5Tmqt Xx`5X*V 1z $ dmay(01+ Wr 4k:#%Np6@'XU[(aM${Nn3Aվ$ l2ef+.|6M^;^$f @Npcu[h3T^B/ *A@QbKczӅFM hV_{>r.ZZ3Ԧû|a/.4XA g?.."=j0NpF* ф[ tBȫ Νu렧;H D—üh^`XGkYX OH4 o0N" A@\8@bZ sN.p|pЂV @OB^\PF&)!pE@;geometry/inst/doc/qhull/html/rbox.html0000644000176200001440000002252513431000557017564 0ustar liggesusers rbox -- generate point distributions

    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
    To: synopsis • outputs • examples • notes • options


    [CONE]rbox -- generate point distributions

    rbox generates random or regular points according to the options given, and outputs the points to stdout. The points are generated in a cube, unless 's', 'x', or 'y' are given.

    »rbox synopsis

    rbox- generate various point distributions.  Default is random in cube.
    
    args (any order, space separated):
      3000    number of random points in cube, lens, spiral, sphere or grid
      D3      dimension 3-d
      c       add a unit cube to the output ('c G2.0' sets size)
      d       add a unit diamond to the output ('d G2.0' sets size)
      l       generate a regular 3-d spiral
      r       generate a regular polygon, ('r s Z1 G0.1' makes a cone)
      s       generate cospherical points
      x       generate random points in simplex, may use 'r' or 'Wn'
      y       same as 'x', plus simplex
      Cn,r,m  add n nearly coincident points within radius r of m points
      Pn,m,r  add point [n,m,r] first, pads with 0
    
      Ln      lens distribution of radius n.  Also 's', 'r', 'G', 'W'.
      Mn,m,r  lattice (Mesh) rotated by [n,-m,0], [m,n,0], [0,0,r], ...
              '27 M1,0,1' is {0,1,2} x {0,1,2} x {0,1,2}.  Try 'M3,4 z'.
      W0.1    random distribution within 0.1 of the cube's or sphere's surface
      Z0.5 s  random points in a 0.5 disk projected to a sphere
      Z0.5 s G0.6 same as Z0.5 within a 0.6 gap
    
      Bn      bounding box coordinates, default 0.5
      h       output as homogeneous coordinates for cdd
      n       remove command line from the first line of output
      On      offset coordinates by n
      t       use time as the random number seed (default is command line)
      tn      use n as the random number seed
      z       print integer coordinates, default 'Bn' is 1e+06
    

    »rbox outputs

    The format of the output is the following: first line contains the dimension and a comment, second line contains the number of points, and the following lines contain the points, one point per line. Points are represented by their coordinate values.

    For example, rbox c 10 D2 generates

    2 RBOX c 10 D2
    14
    -0.4999921736307369 -0.3684622117955817
    0.2556053225468894 -0.0413498678629751
    0.0327672376602583 -0.2810408135699488
    -0.452955383763607 0.17886471718444
    0.1792964061529342 0.4346928963760779
    -0.1164979223315585 0.01941637230982666
    0.3309653464993139 -0.4654278894564396
    -0.4465383649305798 0.02970019358182344
    0.1711493843897706 -0.4923018137852678
    -0.1165843490665633 -0.433157762450313
      -0.5   -0.5
      -0.5    0.5
       0.5   -0.5
       0.5    0.5
    

    »rbox examples

           rbox 10
                  10 random points in the unit cube centered  at  the
                  origin.
    
           rbox 10 s D2
                  10 random points on a 2-d circle.
    
           rbox 100 W0
                  100 random points on the surface of a cube.
    
           rbox 1000 s D4
                  1000 random points on a 4-d sphere.
    
           rbox c D5 O0.5
                  a 5-d hypercube with one corner at the origin.
    
           rbox d D10
                  a 10-d diamond.
    
           rbox x 1000 r W0
                  100 random points on the surface of a fixed simplex
    
           rbox y D12
                  a 12-d simplex.
    
           rbox l 10
                  10 random points along a spiral
    
           rbox l 10 r
                  10 regular points  along  a  spiral  plus  two  end
                  points
    
           rbox 1000 L10000 D4 s
                  1000 random points on the surface of a narrow lens.
    
               rbox 1000 L100000 s G1e-6
                      1000 random points near the edge of a narrow lens
    
           rbox c G2 d G3
                  a cube with coordinates +2/-2 and  a  diamond  with
                  coordinates +3/-3.
    
           rbox 64 M3,4 z
                  a  rotated,  {0,1,2,3} x {0,1,2,3} x {0,1,2,3} lat-
                  tice (Mesh) of integer points.
    
           rbox P0 P0 P0 P0 P0
                  5 copies of the origin in 3-d.  Try 'rbox P0 P0  P0
                  P0 P0 | qhull QJ'.
    
           r 100 s Z1 G0.1
                  two  cospherical  100-gons plus another cospherical
                  point.
    
           100 s Z1
                  a cone of points.
    
           100 s Z1e-7
                  a narrow cone of points with many precision errors.
    

    »rbox notes

    Some combinations of arguments generate odd results.

    »rbox options

           n      number of points
    
           Dn     dimension n-d (default 3-d)
    
           Bn     bounding box coordinates (default 0.5)
    
           l      spiral distribution, available only in 3-d
    
           Ln     lens  distribution  of  radius n.  May be used with
                  's', 'r', 'G', and 'W'.
    
           Mn,m,r lattice  (Mesh)  rotated  by  {[n,-m,0],   [m,n,0],
                  [0,0,r],  ...}.   Use  'Mm,n'  for a rigid rotation
                  with r = sqrt(n^2+m^2).  'M1,0'  is  an  orthogonal
                  lattice.   For  example,  '27  M1,0'  is  {0,1,2} x
                  {0,1,2} x {0,1,2}.
    
           s      cospherical points randomly generated in a cube and
                  projected to the unit sphere
    
           x      simplicial  distribution.   It  is fixed for option
                  'r'.  May be used with 'W'.
    
           y      simplicial distribution plus a simplex.   Both  'x'
                  and 'y' generate the same points.
    
           Wn     restrict  points  to distance n of the surface of a
                  sphere or a cube
    
           c      add a unit cube to the output
    
           c Gm   add a cube with all combinations of +m  and  -m  to
                  the output
    
           d      add a unit diamond to the output.
    
           d Gm   add a diamond made of 0, +m and -m to the output
    
           Cn,r,m add n nearly coincident points within radius r of m points
    
           Pn,m,r add point [n,m,r] to the output first.  Pad coordi-
                  nates with 0.0.
    
           n      Remove the command line from the first line of out-
                  put.
    
           On     offset the data by adding n to each coordinate.
    
           t      use  time  in  seconds  as  the  random number seed
                  (default is command line).
    
           tn     set the random number seed to n.
    
           z      generate integer coordinates.  Use 'Bn'  to  change
                  the  range.   The  default  is 'B1e6' for six-digit
                  coordinates.  In R^4, seven-digit coordinates  will
                  overflow hyperplane normalization.
    
           Zn s   restrict points to a disk about the z+ axis and the
                  sphere (default Z1.0).  Includes the opposite pole.
                  'Z1e-6'  generates  degenerate  points under single
                  precision.
    
           Zn Gm s
                  same as Zn with an empty center (default G0.5).
    
           r s D2 generate a regular polygon
    
           r s Z1 G0.1
                  generate a regular cone
    

    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
    To: synopsis • outputs • examples • notes • options


    The Geometry Center Home Page

    Comments to: qhull@qhull.org
    Created: Sept. 25, 1995 --- Last modified: August 12, 1998

    geometry/inst/doc/qhull/html/qhull.html0000644000176200001440000004547213431000557017745 0ustar liggesusers qhull -- convex hull and related structures

    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
    To: synopsis • input • outputs • controls • options


    [cone]qhull -- convex hull and related structures

    The convex hull of a set of points is the smallest convex set containing the points. The Delaunay triangulation and furthest-site Delaunay triangulation are equivalent to a convex hull in one higher dimension. Halfspace intersection about a point is equivalent to a convex hull by polar duality.

    The qhull program provides options to build these structures and to experiment with the process. Use the qconvex, qdelaunay, qhalf, and qvoronoi programs to build specific structures. You may use qhull instead. It takes the same options and uses the same code.

    Example: rbox 1000 D3 | qhull C-1e-4 FO Ts
    Compute the 3-d convex hull of 1000 random points. Centrums must be 10^-4 below neighboring hyperplanes. Print the options and precision constants. When done, print statistics. These options may be used with any of the Qhull programs.
     
    Example: rbox 1000 D3 | qhull d Qbb R1e-4 Q0
    Compute the 3-d Delaunay triangulation of 1000 random points. Randomly perturb all calculations by [0.9999,1.0001]. Do not correct precision problems. This leads to serious precision errors.

    Use the following equivalences when calling qhull in 2-d to 4-d (a 3-d Delaunay triangulation is a 4-d convex hull):

    Use the following equivalences when calling qhull in 5-d and higher (a 4-d Delaunay triangulation is a 5-d convex hull):

    By default, Qhull merges coplanar facets. For example, the convex hull of a cube's vertices has six facets.

    If you use 'Qt' (triangulated output), all facets will be simplicial (e.g., triangles in 2-d). For the cube example, it will have 12 facets. Some facets may be degenerate and have zero area.

    If you use 'QJ' (joggled input), all facets will be simplicial. The corresponding vertices will be slightly perturbed. Joggled input is less accurate that triangulated output.See Merged facets or joggled input.

    The output for 4-d convex hulls may be confusing if the convex hull contains non-simplicial facets (e.g., a hypercube). See Why are there extra points in a 4-d or higher convex hull?

    Copyright © 1995-2018 C.B. Barber


    »qhull synopsis

    qhull- compute convex hulls and related structures.
        input (stdin): dimension, n, point coordinates
        comments start with a non-numeric character
        halfspace: use dim+1 and put offsets after coefficients
    
    options (qh-quick.html):
        d    - Delaunay triangulation by lifting points to a paraboloid
        d Qu - furthest-site Delaunay triangulation (upper convex hull)
        v    - Voronoi diagram as the dual of the Delaunay triangulation
        v Qu - furthest-site Voronoi diagram
        H1,1 - Halfspace intersection about [1,1,0,...] via polar duality
        Qt   - triangulated output
        QJ   - joggle input instead of merging facets
        Tv   - verify result: structure, convexity, and point inclusion
        .    - concise list of all options
        -    - one-line description of all options
    
    Output options (subset):
        s    - summary of results (default)
        i    - vertices incident to each facet
        n    - normals with offsets
        p    - vertex coordinates (if 'Qc', includes coplanar points)
               if 'v', Voronoi vertices
        Fp   - halfspace intersections
        Fx   - extreme points (convex hull vertices)
        FA   - compute total area and volume
        o    - OFF format (if 'v', outputs Voronoi regions)
        G    - Geomview output (2-d, 3-d and 4-d)
        m    - Mathematica output (2-d and 3-d)
        QVn  - print facets that include point n, -n if not
        TO file- output results to file, may be enclosed in single quotes
    
    examples:
        rbox c d D2 | qhull Qc s f Fx | more      rbox 1000 s | qhull Tv s FA
        rbox 10 D2 | qhull d QJ s i TO result     rbox 10 D2 | qhull v Qbb Qt p
        rbox 10 D2 | qhull d Qu QJ m              rbox 10 D2 | qhull v Qu QJ o
        rbox c | qhull n                          rbox c | qhull FV n | qhull H Fp
        rbox d D12 | qhull QR0 FA                 rbox c D7 | qhull FA TF1000
        rbox y 1000 W0 | qhull                    rbox 10 | qhull v QJ o Fv
    

    »qhull input

    The input data on stdin consists of:

    • dimension
    • number of points
    • point coordinates

    Use I/O redirection (e.g., qhull < data.txt), a pipe (e.g., rbox 10 | qhull), or the 'TI' option (e.g., qhull TI data.txt).

    Comments start with a non-numeric character. Error reporting is simpler if there is one point per line. Dimension and number of points may be reversed. For halfspace intersection, an interior point may be prepended (see qhalf input).

    Here is the input for computing the convex hull of the unit cube. The output is the normals, one per facet.

    rbox c > data

    3 RBOX c
    8
      -0.5   -0.5   -0.5
      -0.5   -0.5    0.5
      -0.5    0.5   -0.5
      -0.5    0.5    0.5
       0.5   -0.5   -0.5
       0.5   -0.5    0.5
       0.5    0.5   -0.5
       0.5    0.5    0.5
    

    qhull s n < data

    
    Convex hull of 8 points in 3-d:
    
      Number of vertices: 8
      Number of facets: 6
      Number of non-simplicial facets: 6
    
    Statistics for: RBOX c | QHULL s n
    
      Number of points processed: 8
      Number of hyperplanes created: 11
      Number of distance tests for qhull: 35
      Number of merged facets: 6
      Number of distance tests for merging: 84
      CPU seconds to compute hull (after input): 0.081
    
    4
    6
         0      0     -1   -0.5
         0     -1      0   -0.5
         1      0      0   -0.5
        -1      0      0   -0.5
         0      1      0   -0.5
         0      0      1   -0.5
    

    »qhull outputs

    These options control the output of qhull. They may be used individually or together.

     
    General
    qhull
    compute the convex hull of the input points. See qconvex.
    qhull d Qbb
    compute the Delaunay triangulation by lifting the points to a paraboloid. Use option 'Qbb' to scale the paraboloid and improve numeric precision. See qdelaunay.
    qhull v Qbb
    compute the Voronoi diagram by computing the Delaunay triangulation. Use option 'Qbb' to scale the paraboloid and improve numeric precision. See qvoronoi.
    qhull H
    compute the halfspace intersection about a point via polar duality. The point is below the hyperplane that defines the halfspace. See qhalf.

    For a full list of output options see

    »qhull controls

    For a full list of control options see

    »qhull options

    qhull- compute convex hulls and related structures.
        http://www.qhull.org
    
    input (stdin):
        first lines: dimension and number of points (or vice-versa).
        other lines: point coordinates, best if one point per line
        comments:    start with a non-numeric character
        halfspaces:  use dim plus one and put offset after coefficients.
                     May be preceded by a single interior point ('H').
    
    options:
        d    - Delaunay triangulation by lifting points to a paraboloid
        d Qu - furthest-site Delaunay triangulation (upper convex hull)
        v    - Voronoi diagram (dual of the Delaunay triangulation)
        v Qu - furthest-site Voronoi diagram
        Hn,n,... - halfspace intersection about point [n,n,0,...]
        Qt   - triangulated output
        QJ   - joggle input instead of merging facets
        Qc   - keep coplanar points with nearest facet
        Qi   - keep interior points with nearest facet
    
    Qhull control options:
        Qbk:n   - scale coord k so that low bound is n
          QBk:n - scale coord k so that upper bound is n (QBk is 0.5)
        QbB  - scale input to unit cube centered at the origin
        Qbb  - scale last coordinate to [0,m] for Delaunay triangulations
        Qbk:0Bk:0 - remove k-th coordinate from input
        QJn  - randomly joggle input in range [-n,n]
        QRn  - random rotation (n=seed, n=0 time, n=-1 time/no rotate)
        Qf   - partition point to furthest outside facet
        Qg   - only build good facets (needs 'QGn', 'QVn', or 'PdD')
        Qm   - only process points that would increase max_outside
        Qr   - process random outside points instead of furthest ones
        Qs   - search all points for the initial simplex
        Qu   - for 'd' or 'v', compute upper hull without point at-infinity
                  returns furthest-site Delaunay triangulation
        Qv   - test vertex neighbors for convexity
        Qx   - exact pre-merges (skips coplanar and anglomaniacs facets)
        Qz   - add point-at-infinity to Delaunay triangulation
        QGn  - good facet if visible from point n, -n for not visible
        QVn  - good facet if it includes point n, -n if not
        Q0   - turn off default p remerge with 'C-0'/'Qx'
        Q1     - sort merges by type instead of angle
        Q2   - merge all non-convex at once instead of independent sets
        Q3   - do not merge redundant vertices
        Q4   - avoid old>new merges
        Q5   - do not correct outer planes at end of qhull
        Q6   - do not pre-merge concave or coplanar facets
        Q7   - depth-first processing instead of breadth-first
        Q8   - do not process near-inside points
        Q9   - process furthest of furthest points
        Q10  - no special processing for narrow distributions
        Q11  - copy normals and recompute centrums for tricoplanar facets
        Q12  - do not error on wide merge due to duplicate ridge and nearly coincident points
        Q14  - do not rename vertices that create a duplicate ridge
    
    Trace options:
        T4   - trace at level n, 4=all, 5=mem/gauss, -1= events
        Tc   - check frequently during execution
        Ts   - print statistics
        Tv   - verify result: structure, convexity, and point inclusion
        Tz   - send all output to stdout
        TFn  - report summary when n or more facets created
        TI file - input data from file, no spaces or single quotes
        TO file - output results to file, may be enclosed in single quotes
        TPn  - turn on tracing when point n added to hull
         TMn - turn on tracing at merge n
         TWn - trace merge facets when width > n
        TRn  - rerun qhull n times.  Use with 'QJn'
        TVn  - stop qhull after adding point n, -n for before (see TCn)
         TCn - stop qhull after building cone for point n (see TVn)
    
    Precision options:
        Cn   - radius of centrum (roundoff added).  Merge facets if non-convex
         An  - cosine of maximum angle.  Merge facets if cosine > n or non-convex
               C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge
        En   - max roundoff error for distance computation
        Rn   - randomly perturb computations by a factor of [1-n,1+n]
        Vn   - min distance above plane for a visible facet (default 3C-n or En)
        Un   - max distance below plane for a new, coplanar point (default Vn)
        Wn   - min facet width for outside point (before roundoff, default 2Vn)
    
    Output formats (may be combined; if none, produces a summary to stdout):
        f    - facet dump
        G    - Geomview output (see below)
        i    - vertices incident to each facet
        m    - Mathematica output (2-d and 3-d)
        o    - OFF format (dim, points and facets; Voronoi regions)
        n    - normals with offsets
        p    - vertex coordinates or Voronoi vertices (coplanar points if 'Qc')
        s    - summary (stderr)
    
    More formats:
        Fa   - area for each facet
        FA   - compute total area and volume for option 's'
        Fc   - count plus coplanar points for each facet
               use 'Qc' (default) for coplanar and 'Qi' for interior
        FC   - centrum or Voronoi center for each facet
        Fd   - use cdd format for input (homogeneous with offset first)
        FD   - use cdd format for numeric output (offset first)
        FF   - facet dump without ridges
        Fi   - inner plane for each facet
               for 'v', separating hyperplanes for bounded Voronoi regions
        FI   - ID of each facet
        Fm   - merge count for each facet (511 max)
        FM   - Maple output (2-d and 3-d)
        Fn   - count plus neighboring facets for each facet
        FN   - count plus neighboring facets for each point
        Fo   - outer plane (or max_outside) for each facet
               for 'v', separating hyperplanes for unbounded Voronoi regions
        FO   - options and precision constants
        Fp   - dim, count, and intersection coordinates (halfspace only)
        FP   - nearest vertex and distance for each coplanar point
        FQ   - command used for qhull
        Fs   - summary: #int (8), dimension, #points, tot vertices, tot facets,
                          output: #vertices, #facets, #coplanars, #nonsimplicial
                        #real (2), max outer plane, min vertex
        FS   - sizes:   #int (0)
                        #real(2) tot area, tot volume
        Ft   - triangulation with centrums for non-simplicial facets (OFF format)
        Fv   - count plus vertices for each facet
               for 'v', Voronoi diagram as Voronoi vertices for pairs of sites
        FV   - average of vertices (a feasible point for 'H')
        Fx   - extreme points (in order for 2-d)
    
    Geomview options (2-d, 3-d, and 4-d; 2-d Voronoi)
        Ga   - all points as dots
         Gp  -  coplanar points and vertices as radii
         Gv  -  vertices as spheres
        Gi   - inner planes only
         Gn  -  no planes
         Go  -  outer planes only
        Gc   - centrums
        Gh   - hyperplane intersections
        Gr   - ridges
        GDn  - drop dimension n in 3-d and 4-d output
        Gt   - for 3-d 'd', transparent outer ridges
    
    Print options:
        PAn  - keep n largest facets by area
        Pdk:n - drop facet if normal[k] <= n (default 0.0)
        PDk:n - drop facet if normal[k] >= n
        Pg   - print good facets (needs 'QGn' or 'QVn')
        PFn  - keep facets whose area is at least n
        PG   - print neighbors of good facets
        PMn  - keep n facets with most merges
        Po   - force output.  If error, output neighborhood of facet
        Pp   - do not report precision problems
    
        .    - list of all options
        -    - one line descriptions of all options
    

    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
    To: synopsis • input • outputs • controls • options


    The Geometry Center Home Page

    Comments to: qhull@qhull.org
    Created: Sept. 25, 1995 --- Last modified: see top

    geometry/inst/doc/qhull/html/qh-impre.html0000644000176200001440000010763013431000557020335 0ustar liggesusers Imprecision in Qhull

    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
    To: Qhull imprecision: Table of Contents (please wait while loading)


    [4-d cube] Imprecision in Qhull

    This section of the Qhull manual discusses the problems caused by coplanar points and why Qhull uses options 'C-0' or 'Qx' by default. If you ignore precision issues with option 'Q0', the output from Qhull can be arbitrarily bad. Qhull avoids precision problems if you merge facets (the default) or joggle the input ('QJ').

    Use option 'Tv' to verify the output from Qhull. It verifies that adjacent facets are clearly convex. It verifies that all points are on or below all facets.

    Qhull automatically tests for convexity if it detects precision errors while constructing the hull.

    Copyright © 1995-2018 C.B. Barber


    »Qhull imprecision: Table of Contents


    »Precision problems

    Since Qhull uses floating point arithmetic, roundoff error occurs with each calculation. This causes problems for geometric algorithms. Other floating point codes for convex hulls, Delaunay triangulations, and Voronoi diagrams also suffer from these problems. Qhull handles most of them.

    There are several kinds of precision errors:

    • Representation error occurs when there are not enough digits to represent a number, e.g., 1/3.
    • Measurement error occurs when the input coordinates are from measurements.
    • Roundoff error occurs when a calculation is rounded to a fixed number of digits, e.g., a floating point calculation.
    • Approximation error occurs when the user wants an approximate result because the exact result contains too much detail.

    Under imprecision, calculations may return erroneous results. For example, roundoff error can turn a small, positive number into a small, negative number. See Milenkovic ['93] for a discussion of strict robust geometry. Qhull does not meet Milenkovic's criterion for accuracy. Qhull's error bound is empirical instead of theoretical.

    Qhull 1.0 checked for precision errors but did not handle them. The output could contain concave facets, facets with inverted orientation ("flipped" facets), more than two facets adjacent to a ridge, and two facets with exactly the same set of vertices.

    Qhull 2.4 and later automatically handles errors due to machine round-off. Option 'C-0' or 'Qx' is set by default. In 5-d and higher, the output is clearly convex but an input point could be outside of the hull. This may be corrected by using option 'C-0', but then the output may contain wide facets.

    Qhull 2.5 and later provides option 'QJ' to joggled input. Each input coordinate is modified by a small, random quantity. If a precision error occurs, a larger modification is tried. When no precision errors occur, Qhull is done.

    Qhull 3.1 and later provides option 'Qt' for triangulated output. This removes the need for joggled input ('QJ'). Non-simplicial facets are triangulated. The facets may have zero area. Triangulated output is particularly useful for Delaunay triangulations.

    By handling round-off errors, Qhull can provide a variety of output formats. For example, it can return the halfspace that defines each facet ('n'). The halfspaces include roundoff error. If the halfspaces were exact, their intersection would return the original extreme points. With imprecise halfspaces and exact arithmetic, nearly incident points may be returned for an original extreme point. By handling roundoff error, Qhull returns one intersection point for each of the original extreme points. Qhull may split or merge an extreme point, but this appears to be unlikely.

    The following pipe implements the identity function for extreme points (with roundoff):

    qconvex FV n | qhalf Fp

    Bernd Gartner published his Miniball algorithm ["Fast and robust smallest enclosing balls", Algorithms - ESA '99, LNCS 1643]. It uses floating point arithmetic and a carefully designed primitive operation. It is practical to 20-D or higher, and identifies at least two points on the convex hull of the input set. Like Qhull, it is an incremental algorithm that processes points furthest from the intermediate result and ignores points that are close to the intermediate result.

    »Merged facets or joggled input

    This section discusses the choice between merged facets and joggled input. By default, Qhull uses merged facets to handle precision problems. With option 'QJ', the input is joggled. See examples of joggled input and triangulated output.

    • Use merged facets (the default) when you want non-simplicial output (e.g., the faces of a cube).
    • Use merged facets and triangulated output ('Qt') when you want simplicial output and coplanar facets (e.g., triangles for a Delaunay triangulation).
    • Use joggled input ('QJ') when you need clearly-convex, simplicial output.

    The choice between merged facets and joggled input depends on the application. Both run about the same speed. Joggled input may be faster if the initial joggle is sufficiently large to avoid precision errors.

    Most applications should used merged facets with triangulated output.

    Use merged facets (the default, 'C-0') or triangulated output ('Qt') if

    • Your application supports non-simplicial facets, or it allows degenerate, simplicial facets (option 'Qt').
    • You do not want the input modified.
    • You want to set additional options for approximating the hull.
    • You use single precision arithmetic (realT).

    Use joggled input ('QJ') if

    • Your application needs clearly convex, simplicial output
    • Your application supports perturbed input points and narrow triangles.
    • Seven significant digits is sufficient accuracy.

    You may use both techniques or combine joggle with post-merging ('Cn').

    Other researchers have used techniques similar to joggled input. Sullivan and Beichel [ref?] randomly perturb the input before computing the Delaunay triangulation. Corkum and Wyllie [news://comp.graphics, 1990] randomly rotate a polytope before testing point inclusion. Edelsbrunner and Mucke [Symp. Comp. Geo., 1988] and Yap [J. Comp. Sys. Sci., 1990] symbolically perturb the input to remove singularities.

    Merged facets ('C-0') handles precision problems directly. If a precision problem occurs, Qhull merges one of the offending facets into one of its neighbors. Since all precision problems in Qhull are associated with one or more facets, Qhull will either fix the problem or attempt to merge the last remaining facets.

    »Delaunay triangulations

    Programs that use Delaunay triangulations traditionally assume a triangulated input. By default, qdelaunay merges regions with cocircular or cospherical input sites. If you want a simplicial triangulation use triangulated output ('Qt') or joggled input ('QJ').

    For Delaunay triangulations, triangulated output should produce good results. All points are within roundoff error of a paraboloid. If two points are nearly incident, one will be a coplanar point. So all points are clearly separated and convex. If qhull reports deleted vertices, the triangulation may contain serious precision faults. Deleted vertices are reported in the summary ('s', 'Fs'

    You should use option 'Qbb' with Delaunay triangulations. It scales the last coordinate and may reduce roundoff error. It is automatically set for qdelaunay, qvoronoi, and option 'QJ'.

    Edelsbrunner, H, Geometry and Topology for Mesh Generation, Cambridge University Press, 2001. Good mathematical treatise on Delaunay triangulation and mesh generation for 2-d and 3-d surfaces. The chapter on surface simplification is particularly interesting. It is similar to facet merging in Qhull.

    Veron and Leon published an algorithm for shape preserving polyhedral simplification with bounded error [Computers and Graphics, 22.5:565-585, 1998]. It remove nodes using front propagation and multiple remeshing.

    »Halfspace intersection

    The identity pipe for Qhull reveals some precision questions for halfspace intersections. The identity pipe creates the convex hull of a set of points and intersects the facets' hyperplanes. It should return the input points, but narrow distributions may drop points while offset distributions may add points. It may be better to normalize the input set about the origin. For example, compare the first results with the later two results: [T. Abraham]

    rbox 100 s t | tee r | qconvex FV n | qhalf Fp | cat - r | /bin/sort -n | tail
    rbox 100 L1e5 t | tee r | qconvex FV n | qhalf Fp | cat - r | /bin/sort -n | tail
    rbox 100 s O10 t | tee r | qconvex FV n | qhalf Fp | cat - r | /bin/sort -n | tail

    »Merged facets

    Qhull detects precision problems when computing distances. A precision problem occurs if the distance computation is less than the maximum roundoff error. Qhull treats the result of a hyperplane computation as if it were exact.

    Qhull handles precision problems by merging non-convex facets. The result of merging two facets is a thick facet defined by an inner plane and an outer plane. The inner and outer planes are offsets from the facet's hyperplane. The inner plane is clearly below the facet's vertices. At the end of Qhull, the outer planes are clearly above all input points. Any exact convex hull must lie between the inner and outer planes.

    Qhull tests for convexity by computing a point for each facet. This point is called the facet's centrum. It is the arithmetic center of the facet's vertices projected to the facet's hyperplane. For simplicial facets with d vertices, the centrum is equivalent to the centroid or center of gravity.

    Two neighboring facets are convex if each centrum is clearly below the other hyperplane. The 'Cn' or 'C-n' options sets the centrum's radius to n . A centrum is clearly below a hyperplane if the computed distance from the centrum to the hyperplane is greater than the centrum's radius plus two maximum roundoff errors. Two are required because the centrum can be the maximum roundoff error above its hyperplane and the distance computation can be high by the maximum roundoff error.

    With the 'C-n' or 'A-n ' options, Qhull merges non-convex facets while constructing the hull. The remaining facets are clearly convex. With the 'Qx ' option, Qhull merges coplanar facets after constructing the hull. While constructing the hull, it merges coplanar horizon facets, flipped facets, concave facets and duplicated ridges. With 'Qx', coplanar points may be missed, but it appears to be unlikely.

    If the user sets the 'An' or 'A-n' option, then all neighboring facets are clearly convex and the maximum (exact) cosine of an angle is n.

    If 'C-0' or 'Qx' is used without other precision options (default), Qhull tests vertices instead of centrums for adjacent simplices. In 3-d, if simplex abc is adjacent to simplex bcd, Qhull tests that vertex a is clearly below simplex bcd , and vertex d is clearly below simplex abc. When building the hull, Qhull tests vertices if the horizon is simplicial and no merges occur.

    »How Qhull merges facets

    If two facets are not clearly convex, then Qhull removes one or the other facet by merging the facet into a neighbor. It selects the merge which minimizes the distance from the neighboring hyperplane to the facet's vertices. Qhull also performs merges when a facet has fewer than d neighbors (called a degenerate facet), when a facet's vertices are included in a neighboring facet's vertices (called a redundant facet), when a facet's orientation is flipped, or when a ridge occurs between more than two facets.

    Qhull performs merges in a series of passes sorted by merge angle. Each pass merges those facets which haven't already been merged in that pass. After a pass, Qhull checks for redundant vertices. For example, if a vertex has only two neighbors in 3-d, the vertex is redundant and Qhull merges it into an adjacent vertex.

    Merging two simplicial facets creates a non-simplicial facet of d+1 vertices. Additional merges create larger facets. When merging facet A into facet B, Qhull retains facet B's hyperplane. It merges the vertices, neighbors, and ridges of both facets. It recomputes the centrum if a wide merge has not occurred (qh_WIDEcoplanar) and the number of extra vertices is smaller than a constant (qh_MAXnewcentrum).

    »Limitations of merged facets

    • Uneven dimensions -- If one coordinate has a larger absolute value than other coordinates, it may dominate the effect of roundoff errors on distance computations. You may use option 'QbB' to scale points to the unit cube. For Delaunay triangulations and Voronoi diagrams, qdelaunay and qvoronoi always set option 'Qbb'. It scales the last coordinate to [0,m] where m is the maximum width of the other coordinates. Option 'Qbb' is needed for Delaunay triangulations of integer coordinates and nearly cocircular points.

      For example, compare

              rbox 1000 W0 t | qconvex Qb2:-1e-14B2:1e-14
      
      with
              rbox 1000 W0 t | qconvex
      
      The distributions are the same but the first is compressed to a 2e-14 slab.

    • Post-merging of coplanar facets -- In 5-d and higher, option 'Qx' (default) delays merging of coplanar facets until post-merging. This may allow "dents" to occur in the intermediate convex hulls. A point may be poorly partitioned and force a poor approximation. See option 'Qx' for further discussion.

      This is difficult to produce in 5-d and higher. Option 'Q6' turns off merging of concave facets. This is similar to 'Qx'. It may lead to serious precision errors, for example,

              rbox 10000 W1e-13  | qhull Q6  Tv
      

    • Maximum facet width -- Qhull reports the maximum outer plane and inner planes (if more than roundoff error apart). There is no upper bound for either figure. This is an area for further research. Qhull does a good job of post-merging in all dimensions. Qhull does a good job of pre-merging in 2-d, 3-d, and 4-d. With the 'Qx' option, it does a good job in higher dimensions. In 5-d and higher, Qhull does poorly at detecting redundant vertices.

      In the summary ('s'), look at the ratio between the maximum facet width and the maximum width of a single merge, e.g., "(3.4x)". Qhull usually reports a ratio of four or lower in 3-d and six or lower in 4-d. If it reports a ratio greater than 10, this may indicate an implementation error. Narrow distributions (see following) may produce wide facets.

      For example, if special processing for narrow distributions is turned off ('Q10'), qhull may produce a wide facet:

               rbox 1000 L100000 s G1e-16 t1002074964 | qhull Tv Q10
      

    • Narrow distribution -- In 3-d, a narrow distribution may result in a poor approximation. For example, if you do not use qdelaunay nor option 'Qbb', the furthest-site Delaunay triangulation of nearly cocircular points may produce a poor approximation:
               rbox s 5000 W1e-13 D2 t1002151341 | qhull d Qt
               rbox 1000 s W1e-13 t1002231672 | qhull d Tv
      

      During construction of the hull, a point may be above two facets with opposite orientations that span the input set. Even though the point may be nearly coplanar with both facets, and can be distant from the precise convex hull of the input sites. Additional facets leave the point distant from a facet. To fix this problem, add option 'Qbb' (it scales the last coordinate). Option 'Qbb' is automatically set for qdelaunay and qvoronoi.

      Qhull generates a warning if the initial simplex is narrow. For narrow distributions, Qhull changes how it processes coplanar points -- it does not make a point coplanar until the hull is finished. Use option 'Q10' to try Qhull without special processing for narrow distributions. For example, special processing is needed for:

               rbox 1000 L100000 s G1e-16 t1002074964 | qhull Tv Q10
      

      You may turn off the warning message by reducing qh_WARNnarrow in user.h or by setting option 'Pp'.

      Similar problems occur for distributions with a large flat facet surrounded with many small facet at a sharp angle to the large facet. Qhull 3.1 fixes most of these problems, but a poor approximation can occur. A point may be left outside of the convex hull ('Tv'). Examples include the furthest-site Delaunay triangulation of nearly cocircular points plus the origin, and the convex hull of a cone of nearly cocircular points. The width of the band is 10^-13.

              rbox s 1000 W1e-13 P0 D2 t996799242 | qhull d Tv
              rbox 1000 s Z1 G1e-13 t1002152123 | qhull Tv
              rbox 1000 s Z1 G1e-13 t1002231668 | qhull Tv
      

    • Quadratic running time -- If the output contains large, non-simplicial facets, the running time for Qhull may be quadratic in the size of the triangulated output. For example, rbox 1000 s W1e-13 c G2 | qhull d is 4 times faster for 500 points. The convex hull contains two large nearly spherical facets and many nearly coplanar facets. Each new point retriangulates the spherical facet and repartitions the remaining points into all of the nearly coplanar facets. In this case, quadratic running time is avoided if you use qdelaunay, add option 'Qbb', or add the origin ('P0') to the input.

    • Nearly coincident points within 1e-13 -- Multiple, nearly coincident points within a 1e-13 ball of points in the unit cube may lead to wide facets or quadratic running time. For example, the convex hull a 1000 coincident, cospherical points in 4-D, or the 3-D Delaunay triangulation of nearly coincident points, may lead to very wide facets (e.g., 2267021951.3x).

      For Delaunay triangulations, the problem typically occurs for extreme points of the input set (i.e., on the edge between the upper and lower convex hull). After multiple facet merges, four facets may share the same, duplicate ridge and must be merged. Some of these facets may be long and narrow, leading to a very wide merged facet. If so, error QH6271 is reported. It may be overriden with option 'Q12'.

      Duplicate ridges occur when the horizon facets for a new point is "pinched". In a duplicate ridge, a subridge (e.g., a line segment in 3-d) is shared by two horizon facets. At least two of its vertices are nearly coincident. In poly_r.c, qh_matchnewfacets calls qh_matchneighbor. qh_matchneighbor identifies duplicate ridges and marks the corresponding facets. . In merge_r.c, qh_mark_dupridges identifies facets for merging across a duplicate ridge. qh_forcedmerges merges these facets. It checks for wide merges with qh_check_dupridge.

      It is easy to generate coincident points with option 'Cn,r,m' of rbox. It generates n points within an r ball for each of m input sites. For example, every point of the following distributions has a nearly coincident point within a 1e-13 ball. Substantially smaller or larger balls do not lead to pinched horizons.

              rbox 1000 C1,1e-13 D4 s t | qhull
              rbox 75 C1,1e-13 t | qhull d
      
      For Delaunay triangulations, a bounding box may alleviate this error (e.g., rbox 500 C1,1E-13 t c G1 | qhull d). A later release of qhull will avoid pinched horizons by merging duplicate subridges. A subridge is merged by merging adjacent vertices.

    • Facet with zero-area -- It is possible for a zero-area facet to be convex with its neighbors. This can occur if the hyperplanes of neighboring facets are above the facet's centrum, and the facet's hyperplane is above the neighboring centrums. Qhull computes the facet's hyperplane so that it passes through the facet's vertices. The vertices can be collinear.

    • No more facets -- Qhull reports an error if there are d+1 facets left and two of the facets are not clearly convex. This typically occurs when the convexity constraints are too strong or the input points are degenerate. The former is more likely in 5-d and higher -- especially with option 'C-n'.

    • Deleted cone -- Lots of merging can end up deleting all of the new facets for a point. This is a rare event that has only been seen while debugging the code.

    • Triangulated output leads to precision problems -- With sufficient merging, the ridges of a non-simplicial facet may have serious topological and geometric problems. A ridge may be between more than two neighboring facets. If so, their triangulation ('Qt') will fail since two facets have the same vertex set. Furthermore, a triangulated facet may have flipped orientation compared to its neighbors.
    • The triangulation process detects degenerate facets with only two neighbors. These are marked degenerate. They have zero area.

    • Coplanar points -- Option 'Qc' is determined by qh_check_maxout() after constructing the hull. Qhull needs to retain all possible coplanar points in the facets' coplanar sets. This depends on qh_RATIOnearInside in user.h. Furthermore, the cutoff for a coplanar point is arbitrarily set at the minimum vertex. If coplanar points are important to your application, remove the interior points by hand (set 'Qc Qi') or make qh_RATIOnearInside sufficiently large.

    • Maximum roundoff error -- Qhull computes the maximum roundoff error from the maximum coordinates of the point set. Usually the maximum roundoff error is a reasonable choice for all distance computations. The maximum roundoff error could be computed separately for each point or for each distance computation. This is expensive and it conflicts with option 'C-n'.

    • All flipped or upper Delaunay -- When a lot of merging occurs for Delaunay triangulations, a new point may lead to no good facets. For example, try a strong convexity constraint:
              rbox 1000 s t993602376 | qdelaunay C-1e-3
      

    »Joggled input

    Joggled input is a simple work-around for precision problems in computational geometry ["joggle: to shake or jar slightly," Amer. Heritage Dictionary]. Other names are jostled input or random perturbation. Qhull joggles the input by modifying each coordinate by a small random quantity. If a precision problem occurs, Qhull joggles the input with a larger quantity and the algorithm is restarted. This process continues until no precision problems occur. Unless all inputs incur precision problems, Qhull will terminate. Qhull adjusts the inner and outer planes to account for the joggled input.

    Neither joggle nor merged facets has an upper bound for the width of the output facets, but both methods work well in practice. Joggled input is easier to justify. Precision errors occur when the points are nearly singular. For example, four points may be coplanar or three points may be collinear. Consider a line and an incident point. A precision error occurs if the point is within some epsilon of the line. Now joggle the point away from the line by a small, uniformly distributed, random quantity. If the point is changed by more than epsilon, the precision error is avoided. The probability of this event depends on the maximum joggle. Once the maximum joggle is larger than epsilon, doubling the maximum joggle will halve the probability of a precision error.

    With actual data, an analysis would need to account for each point changing independently and other computations. It is easier to determine the probabilities empirically ('TRn') . For example, consider computing the convex hull of the unit cube centered on the origin. The arithmetic has 16 significant decimal digits.

    Convex hull of unit cube

    joggle error prob.
    1.0e-15 0.983
    2.0e-15 0.830
    4.0e-15 0.561
    8.0e-15 0.325
    1.6e-14 0.185
    3.2e-14 0.099
    6.4e-14 0.051
    1.3e-13 0.025
    2.6e-13 0.010
    5.1e-13 0.004
    1.0e-12 0.002
    2.0e-12 0.001

    A larger joggle is needed for multiple points. Since the number of potential singularities increases, the probability of one or more precision errors increases. Here is an example.

    Convex hull of 1000 points on unit cube

    joggle error prob.
    1.0e-12 0.870
    2.0e-12 0.700
    4.0e-12 0.450
    8.0e-12 0.250
    1.6e-11 0.110
    3.2e-11 0.065
    6.4e-11 0.030
    1.3e-10 0.010
    2.6e-10 0.008
    5.1e-09 0.003

    Other distributions behave similarly. No distribution should behave significantly worse. In Euclidean space, the probability measure of all singularities is zero. With floating point numbers, the probability of a singularity is non-zero. With sufficient digits, the probability of a singularity is extremely small for random data. For a sufficiently large joggle, all data is nearly random data.

    Qhull uses an initial joggle of 30,000 times the maximum roundoff error for a distance computation. This avoids most potential singularities. If a failure occurs, Qhull retries at the initial joggle (in case bad luck occurred). If it occurs again, Qhull increases the joggle by ten-fold and tries again. This process repeats until the joggle is a hundredth of the width of the input points. Qhull reports an error after 100 attempts. This should never happen with double-precision arithmetic. Once the probability of success is non-zero, the probability of success increases about ten-fold at each iteration. The probability of repeated failures becomes extremely small.

    Merged facets produces a significantly better approximation. Empirically, the maximum separation between inner and outer facets is about 30 times the maximum roundoff error for a distance computation. This is about 2,000 times better than joggled input. Most applications though will not notice the difference.

    »Exact arithmetic

    Exact arithmetic may be used instead of floating point. Singularities such as coplanar points can either be handled directly or the input can be symbolically perturbed. Using exact arithmetic is slower than using floating point arithmetic and the output may take more space. Chaining a sequence of operations increases the time and space required. Some operations are difficult to do.

    Clarkson's hull program and Shewchuk's triangle program are practical implementations of exact arithmetic.

    Clarkson limits the input precision to about fifteen digits. This reduces the number of nearly singular computations. When a determinant is nearly singular, he uses exact arithmetic to compute a precise result.

    »Approximating a convex hull

    Qhull may be used for approximating a convex hull. This is particularly valuable in 5-d and higher where hulls can be immense. You can use 'Qx C-n' to merge facets as the hull is being constructed. Then use 'Cn' and/or 'An' to merge small facets during post-processing. You can print the n largest facets with option 'PAn'. You can print facets whose area is at least n with option 'PFn'. You can output the outer planes and an interior point with 'FV Fo' and then compute their intersection with 'qhalf'.

    To approximate a convex hull in 6-d and higher, use post-merging with 'Wn' (e.g., qhull W1e-1 C1e-2 TF2000). Pre-merging with a convexity constraint (e.g., qhull Qx C-1e-2) often produces a poor approximation or terminates with a simplex. Option 'QbB' may help to spread out the data.

    You will need to experiment to determine a satisfactory set of options. Use rbox to generate test sets quickly and Geomview to view the results. You will probably want to write your own driver for Qhull using the Qhull library. For example, you could select the largest facet in each quadrant.


    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
    To: Qhull imprecision: Table of Contents


    The Geometry Center Home Page

    Comments to: qhull@qhull.org
    Created: Sept. 25, 1995 --- Last modified: see top

    geometry/inst/doc/qhull/html/qh--geom.gif0000644000176200001440000000047613431000556020025 0ustar liggesusersGIF87a((LLL,((0Iٸ8'\Bl벧4_ @}9@,DgWl:{dPpZN^^F̈́a!a5gp'n}~Cil|Ui~WuJO_~LVqALrePnkq2g;[hkvkhƨe^j|tJxpFSz|c+@]   #WĊT Ə(^I2E;geometry/inst/doc/qhull/html/qh-opto.html0000644000176200001440000003547213431000557020206 0ustar liggesusers Qhull output options

    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions


    [delaunay] Qhull output options

    This section lists the output options for Qhull. These options are indicated by lower case characters. See Formats, Print, and Geomview for other output options.

    Copyright © 1995-2018 C.B. Barber


    » Programs OptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions

    Output options

    Qhull prints its output to standard out. All output is printed text. The default output is a summary (option 's'). Other outputs may be specified as follows.

    f
    print all fields of all facets
    n
    print hyperplane normals with offsets
    m
    print Mathematica output (2-d and 3-d)
    o
    print OFF file format (dim, points and facets)
    s
    print summary to stderr
    p
    print vertex and point coordinates
    i
    print vertices incident to each facet
     
     
    Related options
    F
    additional input/output formats
    G
    Geomview output
    P
    Print options
    Ft
    print triangulation with added points
     

    »f - print all fields of all facets

    Print all fields of all facets. The facet is the primary data structure for Qhull.

    Option 'f' is for debugging. Most of the fields are available via the 'F' options. If you need specialized information from Qhull, you can use the Qhull library or C++ interface.

    Use the 'FF' option to print the facets but not the ridges.

    »i - print vertices incident to each facet

    The first line is the number of facets. The remaining lines list the vertices for each facet, one facet per line. The indices are 0-relative indices of the corresponding input points. The facets are oriented. Option 'Fv' displays an unoriented list of vertices with a vertex count per line. Options 'o' and 'Ft' displays coordinates for each vertex prior to the vertices for each facet.

    Simplicial facets (e.g., triangles in 3-d) consist of d vertices. Non-simplicial facets in 3-d consist of 4 or more vertices. For example, a facet of a cube consists of 4 vertices. Use option 'Qt' to triangulate non-simplicial facets.

    For 4-d and higher convex hulls and 3-d and higher Delaunay triangulations, d vertices are listed for all facets. A non-simplicial facet is triangulated with its centrum and each ridge. The index of the centrum is higher than any input point. Use option 'Fv' to list the vertices of non-simplicial facets as is. Use option 'Ft' to print the coordinates of the centrums as well as those of the input points.

    »m - print Mathematica output

    Qhull writes a Mathematica file for 2-d and 3-d convex hulls, 2-d and 3-d halfspace intersections, and 2-d Delaunay triangulations. Qhull produces a list of objects that you can assign to a variable in Mathematica, for example: "list= << <outputfilename> ". If the object is 2-d, it can be visualized by "Show[Graphics[list]] ". For 3-d objects the command is "Show[Graphics3D[list]] ". Now the object can be manipulated by commands of the form "Show[%, <parametername> -> <newvalue>]".

    For Delaunay triangulation orthogonal projection is better. This can be specified, for example, by "BoxRatios: Show[%, BoxRatios -> {1, 1, 1e-8}]". To see the meaningful side of the 3-d object used to visualize 2-d Delaunay, you need to change the viewpoint: "Show[%, ViewPoint -> {0, 0, -1}]". By specifying different viewpoints you can slowly rotate objects.

    For halfspace intersections, Qhull produces the dual convex hull.

    See Is Qhull available for Mathematica? for URLs.

    »n - print hyperplane normals with offsets

    The first line is the dimension plus one. The second line is the number of facets. The remaining lines are the normals for each facet, one normal per line. The facet's offset follows its normal coefficients.

    The normals point outward, i.e., the convex hull satisfies Ax <= -b where A is the matrix of coefficients and b is the vector of offsets.

    A point is inside or below a hyperplane if its distance to the hyperplane is negative. A point is outside or above a hyperplane if its distance to the hyperplane is positive. Otherwise a point is on or coplanar to the hyperplane.

    If cdd output is specified ('FD'), Qhull prints the command line, the keyword "begin", the number of facets, the dimension (plus one), the keyword "real", and the normals for each facet. The facet's negative offset precedes its normal coefficients (i.e., if the origin is an interior point, the offset is positive). Qhull ends the output with the keyword "end".

    »o - print OFF file format

    The output is:

    • The first line is the dimension
    • The second line is the number of points, the number of facets, and the number of ridges.
    • All of the input points follow, one per line.
    • Then Qhull prints the vertices for each facet. Each facet is on a separate line. The first number is the number of vertices. The remainder is the indices of the corresponding points. The vertices are oriented in 2-d, 3-d, and in simplicial facets.

    Option 'Ft' prints the same information with added points for non-simplicial facets.

    Option 'i' displays vertices without the point coordinates. Option 'p' displays the point coordinates without vertex and facet information.

    In 3-d, Geomview can load the file directly if you delete the first line (e.g., by piping through 'tail +2').

    For Voronoi diagrams (qvoronoi), option 'o' prints Voronoi vertices and Voronoi regions instead of input points and facets. The first vertex is the infinity vertex [-10.101, -10.101, ...]. Then, option 'o' lists the vertices in the Voronoi region for each input site. The regions appear in site ID order. In 2-d, the vertices of a Voronoi region are sorted by adjacency (non-oriented). In 3-d and higher, the Voronoi vertices are sorted by index. See the 'FN' option for listing Voronoi regions without listing Voronoi vertices.

    If you are using the Qhull library, options 'v o' have the side effect of reordering the neighbors for a vertex.

    »p - print vertex and point coordinates

    The first line is the dimension. The second line is the number of vertices. The remaining lines are the vertices, one vertex per line. A vertex consists of its point coordinates

    With the 'Gc' and 'Gi' options, option 'p' also prints coplanar and interior points respectively.

    For qvoronoi, it prints the coordinates of each Voronoi vertex.

    For qdelaunay, it prints the input sites as lifted to a paraboloid. For qhalf it prints the dual points. For both, option 'p' is the same as the first section of option 'o'.

    Use 'Fx' to list the point ids of the extreme points (i.e., vertices).

    If a subset of the facets is selected ('Pdk', 'PDk', 'Pg' options), option 'p' only prints vertices and points associated with those facets.

    If cdd-output format is selected ('FD'), the first line is "begin". The second line is the number of vertices, the dimension plus one, and "real". The vertices follow with a leading "1". Output ends with "end".

    »s - print summary to stderr

    The default output of Qhull is a summary to stderr. Options 'FS' and 'Fs' produce the same information for programs. Note: Windows 95 and 98 treats stderr the same as stdout. Use option 'TO file' to separate stderr and stdout.

    The summary lists the number of input points, the dimension, the number of vertices in the convex hull, and the number of facets in the convex hull. It lists the number of selected ("good") facets for options 'Pg', 'Pdk', qdelaunay, or qvoronoi (Delaunay triangulations only use the lower half of a convex hull). It lists the number of coplanar points. For Delaunay triangulations without 'Qc', it lists the total number of coplanar points. It lists the number of simplicial facets in the output.

    The terminology depends on the output structure.

    The summary lists these statistics:

    • number of points processed by Qhull
    • number of hyperplanes created
    • number of distance tests (not counting statistics, summary, and checking)
    • number of merged facets (if any)
    • number of distance tests for merging (if any)
    • CPU seconds to compute the hull
    • the maximum joggle for 'QJ'
      or, the probability of precision errors for 'QJ TRn'
    • total area and volume (if computed, see 'FS' 'FA' 'Fa' 'PAn')
    • max. distance of a point above a facet (if non-zero)
    • max. distance of a vertex below a facet (if non-zero)

    The statistics include intermediate hulls. For example 'rbox d D4 | qhull' reports merged facets even though the final hull is simplicial.

    Qhull starts counting CPU seconds after it has read and projected the input points. It stops counting before producing output. In the code, CPU seconds measures the execution time of function qhull() in libqhull.c. If the number of CPU seconds is clearly wrong, check qh_SECticks in user.h.

    The last two figures measure the maximum distance from a point or vertex to a facet. They are not printed if less than roundoff or if not merging. They account for roundoff error in computing the distance (c.f., option 'Rn'). Use 'Fs' to report the maximum outer and inner plane.

    A number may appear in parentheses after the maximum distance (e.g., 2.1x). It is the ratio between the maximum distance and the worst-case distance due to merging two simplicial facets. It should be small for 2-d, 3-d, and 4-d, and for higher dimensions with 'Qx'. It is not printed if less than 0.05.


    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions


    The Geometry Center Home Page

    Comments to: qhull@qhull.org
    Created: Sept. 25, 1995 --- Last modified: see top

    geometry/inst/doc/qhull/html/qh-optp.html0000644000176200001440000002312513431000557020177 0ustar liggesusers Qhull print options (P)

    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions


    [delaunay] Qhull print options (P)

    This section lists the print options for Qhull. These options are indicated by 'P' followed by a letter. See Output, Geomview, and Format for other output options.

    Copyright © 1995-2018 C.B. Barber


    » Programs OptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions

    Print options

     
    General
    Pp
    do not report precision problems
    Po
    force output despite precision problems
    Po
    if error, output neighborhood of facet
     
     
    Select
    Pdk:n
    print facets with normal[k] >= n (default 0.0)
    PDk:n
    print facets with normal[k] <= n
    PFn
    print facets whose area is at least n
    Pg
    print good facets only (needs 'QGn' or 'QVn ')
    PMn
    print n facets with most merges
    PAn
    print n largest facets by area
    PG
    print neighbors of good facets

    »PAn - keep n largest facets by area

    The n largest facets are marked good for printing. This may be useful for approximating a hull. Unless 'PG' is set, 'Pg' is automatically set.

    »Pdk:n - print facet if normal[k] >= n

    For a given output, print only those facets with normal[k] >= n and drop the others. For example, 'Pd0:0.5' prints facets with normal[0] >= 0.5 . The default value of n is zero. For example in 3-d, 'Pd0d1d2' prints facets in the positive octant.

    If no facets match, the closest facet is returned.

    On Windows 95, do not combine multiple options. A 'd' is considered part of a number. For example, use 'Pd0:0.5 Pd1:0.5' instead of 'Pd0:0.5d1:0.5'.

    »PDk:n - print facet if normal[k] <= n

    For a given output, print only those facets with normal[k] <= n and drop the others. For example, 'PD0:0.5' prints facets with normal[0] <= 0.5 . The default value of n is zero. For example in 3-d, 'PD0D1D2' displays facets in the negative octant.

    If no facets match, the closest facet is returned.

    In 2-d, 'd G PD2' displays the Delaunay triangulation instead of the corresponding paraboloid.

    Be careful of placing 'Dk' or 'dk' immediately after a real number. Some compilers treat the 'D' as a double precision exponent.

    »PFn - keep facets whose area is at least n

    The facets with area at least n are marked good for printing. This may be useful for approximating a hull. Unless 'PG' is set, 'Pg' is automatically set.

    »Pg - print good facets

    Qhull can mark facets as "good". This is used to

    • mark the lower convex hull for Delaunay triangulations and Voronoi diagrams
    • mark the facets that are visible from a point (the 'QGn ' option)
    • mark the facets that contain a point (the 'QVn' option).
    • indicate facets with a large enough area (options 'PAn' and 'PFn')

    Option 'Pg' only prints good facets that also meet 'Pdk' and 'PDk' options. It is automatically set for options 'PAn', 'PFn ', 'QGn', and 'QVn'.

    »PG - print neighbors of good facets

    Option 'PG' can be used with or without option 'Pg' to print the neighbors of good facets. For example, options 'QGn' and 'QVn' print the horizon facets for point n.

    »PMn - keep n facets with most merges

    The n facets with the most merges are marked good for printing. This may be useful for approximating a hull. Unless 'PG' is set, 'Pg' is automatically set.

    Use option 'Fm' to print merges per facet.

    »Po - force output despite precision problems

    Use options 'Po' and 'Q0' if you can not merge facets, triangulate the output ('Qt'), or joggle the input (QJ).

    Option 'Po' can not force output when duplicate ridges or duplicate facets occur. It may produce erroneous results. For these reasons, merged facets, joggled input, or exact arithmetic are better.

    If you need a simplicial Delaunay triangulation, use joggled input 'QJ' or triangulated output 'Ft'.

    Option 'Po' may be used without 'Q0' to remove some steps from Qhull or to output the neighborhood of an error.

    Option 'Po' may be used with option 'Q5') to skip qh_check_maxout (i.e., do not determine the maximum outside distance). This can save a significant amount of time.

    If option 'Po' is used,

    • most precision errors allow Qhull to continue.
    • verify ('Tv') does not check coplanar points.
    • points are not partitioned into flipped facets and a flipped facet is always visible to a point. This may delete flipped facets from the output.

    »Po - if error, output neighborhood of facet

    If an error occurs before the completion of Qhull and tracing is not active, 'Po' outputs a neighborhood of the erroneous facets (if any). It uses the current output options.

    See 'Po' - force output despite precision problems.

    »Pp - do not report precision problems

    With option 'Pp', Qhull does not print statistics about precision problems, and it removes some of the warnings. It removes the narrow hull warning.


    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions


    The Geometry Center Home Page

    Comments to: qhull@qhull.org
    Created: Sept. 25, 1995 --- Last modified: see top

    geometry/inst/doc/qhull/html/index.html0000644000176200001440000012625313431000557017724 0ustar liggesusers Qhull manual

    Up: Home page for Qhull
    Up:News about Qhull
    Up: FAQ about Qhull
    To: Qhull manual: Table of Contents (please wait while loading)
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions


    [random-fixed] Qhull manual

    Qhull is a general dimension code for computing convex hulls, Delaunay triangulations, halfspace intersections about a point, Voronoi diagrams, furthest-site Delaunay triangulations, and furthest-site Voronoi diagrams. These structures have applications in science, engineering, statistics, and mathematics. See Fukuda's introduction to convex hulls, Delaunay triangulations, Voronoi diagrams, and linear programming. For a detailed introduction, see O'Rourke ['94], Computational Geometry in C.

    There are six programs. Except for rbox, they use the same code. Each program includes instructions and examples.

    • qconvex -- convex hulls
    • qdelaunay -- Delaunay triangulations and furthest-site Delaunay triangulations
    • qhalf -- halfspace intersections about a point
    • qhull -- all structures with additional options
    • qvoronoi -- Voronoi diagrams and furthest-site Voronoi diagrams
    • rbox -- generate point distributions for qhull

    Qhull implements the Quickhull algorithm for computing the convex hull. Qhull includes options for hull volume, facet area, multiple output formats, and graphical output. It can approximate a convex hull.

    Qhull handles roundoff errors from floating point arithmetic. It generates a convex hull with "thick" facets. A facet's outer plane is clearly above all of the points; its inner plane is clearly below the facet's vertices. Any exact convex hull must lie between the inner and outer plane.

    Qhull uses merged facets, triangulated output, or joggled input. Triangulated output triangulates non-simplicial, merged facets. Joggled input also guarantees simplicial output, but it is less accurate than merged facets. For merged facets, Qhull reports the maximum outer and inner plane.

    Brad Barber, Arlington, MA

    Copyright © 1995-2018 C.B. Barber


    »Qhull manual: Table of Contents

    »When to use Qhull

    Qhull constructs convex hulls, Delaunay triangulations, halfspace intersections about a point, Voronoi diagrams, furthest-site Delaunay triangulations, and furthest-site Voronoi diagrams.

    For convex hulls and halfspace intersections, Qhull may be used for 2-d upto 8-d. For Voronoi diagrams and Delaunay triangulations, Qhull may be used for 2-d upto 7-d. In higher dimensions, the size of the output grows rapidly and Qhull does not work well with virtual memory. If n is the size of the input and d is the dimension (d>=3), the size of the output and execution time grows by n^(floor(d/2) [see Performance]. For example, do not try to build a 16-d convex hull of 1000 points. It will have on the order of 1,000,000,000,000,000,000,000,000 facets.

    On a 600 MHz Pentium 3, Qhull computes the 2-d convex hull of 300,000 cocircular points in 11 seconds. It computes the 2-d Delaunay triangulation and 3-d convex hull of 120,000 points in 12 seconds. It computes the 3-d Delaunay triangulation and 4-d convex hull of 40,000 points in 18 seconds. It computes the 4-d Delaunay triangulation and 5-d convex hull of 6,000 points in 12 seconds. It computes the 5-d Delaunay triangulation and 6-d convex hull of 1,000 points in 12 seconds. It computes the 6-d Delaunay triangulation and 7-d convex hull of 300 points in 15 seconds. It computes the 7-d Delaunay triangulation and 8-d convex hull of 120 points in 15 seconds. It computes the 8-d Delaunay triangulation and 9-d convex hull of 70 points in 15 seconds. It computes the 9-d Delaunay triangulation and 10-d convex hull of 50 points in 17 seconds. The 10-d convex hull of 50 points has about 90,000 facets.

    Qhull does not support constrained Delaunay triangulations, triangulation of non-convex surfaces, mesh generation of non-convex objects, or medium-sized inputs in 9-D and higher.

    This is a big package with many options. It is one of the fastest available. It is the only 3-d code that handles precision problems due to floating point arithmetic. For example, it implements the identity function for extreme points (see Imprecision in Qhull).

    [2016] A newly discovered, bad case for Qhull is multiple, nearly incident points within a 10^-13 ball of 3-d and higher Delaunay triangulations (input sites in the unit cube). Nearly incident points within substantially smaller or larger balls are OK. Error QH6271 is reported if a problem occurs. A future release of Qhull will handle this case. For more information, see "Nearly coincident points on an edge" in Limitations of merged facets

    If you need a short code for convex hull, Delaunay triangulation, or Voronoi volumes consider Clarkson's hull program. If you need 2-d Delaunay triangulations consider Shewchuk's triangle program. It is much faster than Qhull and it allows constraints. Both programs use exact arithmetic. They are in http://www.netlib.org/voronoi/.

    If your input is in general position (i.e., no coplanar or colinear points),

  • Tomilov's quickhull.hpp () or Qhull version 1.0 may meet your needs. Both programs detect precision problems, but do not handle them.

    CGAL is a library of efficient and reliable geometric algorithms. It uses C++ templates and the Boost library to produce dimension-specific code. This allows more efficient use of memory than Qhull's general-dimension code. CGAL simulates arbitrary precision while Qhull handles round-off error with thick facets. Compare the two approaches with Robustness Issues in CGAL, and Imprecision in Qhull.

    Leda is a library for writing computational geometry programs and other combinatorial algorithms. It includes routines for computing 3-d convex hulls, 2-d Delaunay triangulations, and 3-d Delaunay triangulations. It provides rational arithmetic and graphical output. It runs on most platforms.

    If your problem is in high dimensions with a few, non-simplicial facets, try Fukuda's cdd. It is much faster than Qhull for these distributions.

    Custom software for 2-d and 3-d convex hulls may be faster than Qhull. Custom software should use less memory. Qhull uses general-dimension data structures and code. The data structures support non-simplicial facets.

    Qhull is not suitable for mesh generation or triangulation of arbitrary surfaces. You may use Qhull if the surface is convex or completely visible from an interior point (e.g., a star-shaped polyhedron). First, project each site to a sphere that is centered at the interior point. Then, compute the convex hull of the projected sites. The facets of the convex hull correspond to a triangulation of the surface. For mesh generation of arbitrary surfaces, see Schneiders' Finite Element Mesh Generation.

    Qhull is not suitable for constrained Delaunay triangulations. With a lot of work, you can write a program that uses Qhull to add constraints by adding additional points to the triangulation.

    Qhull is not suitable for the subdivision of arbitrary objects. Use qdelaunay to subdivide a convex object.

  • »Description of Qhull

    »definition

    The convex hull of a point set P is the smallest convex set that contains P. If P is finite, the convex hull defines a matrix A and a vector b such that for all x in P, Ax+b <= [0,...].

    Qhull computes the convex hull in 2-d, 3-d, 4-d, and higher dimensions. Qhull represents a convex hull as a list of facets. Each facet has a set of vertices, a set of neighboring facets, and a halfspace. A halfspace is defined by a unit normal and an offset (i.e., a row of A and an element of b).

    Qhull accounts for round-off error. It returns "thick" facets defined by two parallel hyperplanes. The outer planes contain all input points. The inner planes exclude all output vertices. See Imprecise convex hulls.

    Qhull may be used for the Delaunay triangulation or the Voronoi diagram of a set of points. It may be used for the intersection of halfspaces.

    »input format

    The input data on stdin consists of:

    • first line contains the dimension
    • second line contains the number of input points
    • remaining lines contain point coordinates

    For example:

        3  #sample 3-d input
        5
        0.4 -0.5 1.0
        1000 -1e-5 -100
        0.3 0.2 0.1
        1.0 1.0 1.0
        0 0 0
    

    Input may be entered by hand. End the input with a control-D (^D) character.

    To input data from a file, use I/O redirection or 'TI file'. The filename may not include spaces or quotes.

    A comment starts with a non-numeric character and continues to the end of line. The first comment is reported in summaries and statistics. With multiple qhull commands, use option 'FQ' to place a comment in the output.

    The dimension and number of points can be reversed. Comments and line breaks are ignored. Error reporting is better if there is one point per line.

    »option format

    Use options to specify the output formats and control Qhull. The qhull program takes all options. The other programs use a subset of the options. They disallow experimental and inappropriate options.

    • qconvex == qhull
    • qdelaunay == qhull d Qbb
    • qhalf == qhull H
    • qvoronoi == qhull v Qbb

    Single letters are used for output formats and precision constants. The other options are grouped into menus for formats ('F'), Geomview ('G '), printing ('P'), Qhull control ('Q '), and tracing ('T'). The menu options may be listed together (e.g., 'GrD3' for 'Gr' and 'GD3'). Options may be in any order. Capitalized options take a numeric argument (except for 'PG' and 'F' options). Use option 'FO' to print the selected options.

    Qhull uses zero-relative indexing. If there are n points, the index of the first point is 0 and the index of the last point is n-1.

    The default options are:

    • summary output ('s')
    • merged facets ('C-0' in 2-d, 3-d, 4-d; 'Qx' in 5-d and up)

    Except for bounding box ('Qbk:n', etc.), drop facets ('Pdk:n', etc.), and Qhull command ('FQ'), only the last occurence of an option counts. Bounding box and drop facets may be repeated for each dimension. Option 'FQ' may be repeated any number of times.

    The Unix tcsh and ksh shells make it easy to try out different options. In Windows 95, use a command window with doskey and a window scroller (e.g., peruse).

    »output format

    To write the results to a file, use I/O redirection or 'TO file'. Windows 95 users should use 'TO file' or the console. If a filename is surrounded by single quotes, it may include spaces.

    The default output option is a short summary ('s') to stdout. There are many others (see output and formats). You can list vertex incidences, vertices and facets, vertex coordinates, or facet normals. You can view Qhull objects with Geomview, Mathematica, or Maple. You can print the internal data structures. You can call Qhull from your application (see Qhull library).

    For example, 'qhull o' lists the vertices and facets of the convex hull.

    Error messages and additional summaries ('s') go to stderr. Unless redirected, stderr is the console.

    »algorithm

    Qhull implements the Quickhull algorithm for convex hull [Barber et al. '96]. This algorithm combines the 2-d Quickhull algorithm with the n-d beneath-beyond algorithm [c.f., Preparata & Shamos '85]. It is similar to the randomized algorithms of Clarkson and others [Clarkson & Shor '89; Clarkson et al. '93; Mulmuley '94]. For a demonstration, see How Qhull adds a point. The main advantages of Quickhull are output sensitive performance (in terms of the number of extreme points), reduced space requirements, and floating-point error handling.

    »data structures

    Qhull produces the following data structures for dimension d:

    • A coordinate is a real number in floating point format.
    • A point is an array of d coordinates. With option 'QJ', the coordinates are joggled by a small amount.
    • A vertex is an input point.
    • A hyperplane is d normal coefficients and an offset. The length of the normal is one. The hyperplane defines a halfspace. If V is a normal, b is an offset, and x is a point inside the convex hull, then Vx+b <0.
    • An outer plane is a positive offset from a hyperplane. When Qhull is done, all points will be below all outer planes.
    • An inner plane is a negative offset from a hyperplane. When Qhull is done, all vertices will be above the corresponding inner planes.
    • An orientation is either 'top' or 'bottom'. It is the topological equivalent of a hyperplane's geometric orientation.
    • A simplicial facet is a set of d neighboring facets, a set of d vertices, a hyperplane equation, an inner plane, an outer plane, and an orientation. For example in 3-d, a simplicial facet is a triangle.
    • A centrum is a point on a facet's hyperplane. A centrum is the average of a facet's vertices. Neighboring facets are convex if each centrum is below the neighbor facet's hyperplane.
    • A ridge is a set of d-1 vertices, two neighboring facets, and an orientation. For example in 3-d, a ridge is a line segment.
    • A non-simplicial facet is a set of ridges, a hyperplane equation, a centrum, an outer plane, and an inner plane. The ridges determine a set of neighboring facets, a set of vertices, and an orientation. Qhull produces a non-simplicial facet when it merges two facets together. For example, a cube has six non-simplicial facets.

    For examples, use option 'f'. See polyhedron operations for further design documentation.

    »Imprecision in Qhull

    See Imprecision in Qhull and Merged facets or joggled input

    »Examples of Qhull

    See Examples of Qhull. Most of these examples require Geomview. Some of the examples have pictures .

    »Options for using Qhull

    See Options.

    »Qhull internals

    See Internals.

    »Geomview, Qhull's graphical viewer

    Geomview is an interactive geometry viewing program. Geomview provides a good visualization of Qhull's 2-d and 3-d results.

    Qhull includes Examples of Qhull that may be viewed with Geomview.

    Geomview can help visulalize a 3-d Delaunay triangulation or the surface of a 4-d convex hull, Use option 'QVn' to select the 3-D facets adjacent to a vertex.

    You may use Geomview to create movies that animate your objects (c.f., How can I create a video animation?). Geomview helped create the mathematical videos "Not Knot", "Outside In", and "The Shape of Space" from the Geometry Center.

    »Installing Geomview

    Geomview is an open source project under SourceForge.

    For build instructions see Downloading Geomview. Geomview builds under Linux, Unix, Macintosh OS X, and Windows.

    Geomview has installable packages for Debian and Ubuntu. The OS X build needs Xcode, an X11 SDK, and Lesstif or Motif. The Windows build uses Cygwin (see Building Geomview below for instructions).

    If using Xforms (e.g., for Geomview's External Modules), install the 'libXpm-devel' package from cygwin and move the xforms directory into your geomview directory, e.g.,
    mv xforms-1.2.4 geomview-1.9.5/xforms

    Geomview's ndview provides multiple views into 4-d and higher objects. This module is out-of-date (geomview-users: 4dview). Download NDview-sgi.tar.Z at newpieces and 4dview at Geomview/modules.

    »Using Geomview

    Use Geomview to view Examples of Qhull. You can spin the convex hull, fly a camera through its facets, and see how Qhull produces thick facets in response to round-off error.

    Follow these instructions to view 'eg,01.cube' from Examples of Qhull

    1. Launch an XTerm command shell
      • If needed, start the X terminal server, Use 'xinit' or 'startx' in /usr/X11R6/bin
        xinit -- -multiwindow -clipboard
        startx
      • Start an XTerm command shell. In Windows, click the Cygwin/bash icon on your desktop.
      • Set the DISPLAY variable, e.g.,
        export DISPLAY=:0
        export DISPLAY=:0 >>~/.bashenv
    2. Use Qhull's Geomview options to create a geomview object
      • rbox c D3 | qconvex G >eg.01.cube
      • On windows, convert the output to Unix text format with 'd2u'
        rbox c D3 | qconvex G | d2u >eg.01.cube
        d2u eg.*
    3. Run Geomview
      • Start Geomview with your example
        ./geomview eg.01.cube
      • Follow the instructions in Gemoview Tutorial
      • Geomview creates the Geomview control panel with Targets and External Module, the Geomview toolbar with buttons for controlling Geomview, and the Geomview camera window showing a cube.
      • Clear the camera window by selecting your object in the Targets list and 'Edit > Delete' or 'dd'
      • Load the Geomview files panel. Select 'Open' in the 'File' menu.
      • Set 'Filter' in the files panel to your example directory followed by '/*' (e.g., '/usr/local/qhull-2015.2/eg/*')
      • Click 'Filter' in the files panel to view your examples in the 'Files' list.
      • Load another example into the camera window by selecting it and clicking 'OK'.
      • Review the instructions for Interacting with Geomview
      • When viewing multiple objects at once, you may want to turn off normalization. In the 'Inspect > Apperance' control panel, set 'Normalize' to 'None'.

    Geomview defines GCL (a textual API for controlling Geomview) and OOGL (a textual file format for defining objects).

    • To control Geomview, you may use any program that reads and writes from stdin and stdout. For example, it could report Qhull's information about a vertex identified by a double-click 'pick' event.
    • GCL command language for controlling Geomview
    • OOGL file format for defining objects (tutorial).
    • External Modules for interacting with Geomview via GCL
    • Interact with your objects via pick commands in response to right-mouse double clicks. Enable pick events with the interest command.

    »Building Geomview for Windows

    Compile Geomview under Cygwin. For detailed instructions, see Building Savi and Geomview under Windows. These instructions are somewhat out-of-date. Updated instructions follow.

    How to compile Geomview under 32-bit Cygwin (October 2015)

    1. Note: L. Wood has run into multiple issues with Geomview on Cygwin. He recommends Virtualbox/Ubuntu and a one-click install of geomview via the Ubuntu package. See his Savi/Geomview link above.
    2. Install 32-bit Cygwin as follows. For additional guidance, see Cygwin's Installing and Updating Cygwin Packages and Setup cygwin.
      • Launch the cygwin installer.
      • Select a mirror from Cygwin mirrors (e.g., http://mirrors.kernel.org/sourceware/cygwin/ in California).
      • Select the packages to install. Besides the cygwin packages listed in the Savi/Windows instructions consider adding
        • Default -- libXm-devel (required for /usr/include/Xm/Xm.h)
        • Devel -- bashdb, gcc-core (in place of gcc), gdb
        • Lib -- libGL-devel, libGLU1 (required, obsolete), libGLU-devel (required, obsolete), libjpeg-devel(XForms), libXext-devel (required), libXpm-devel (Xforms) libGL and lib
        • Math -- bc
        • Net -- autossh, inetutils, openssh
        • System -- chere
        • Utils -- dos2unix (required for qhull), keychain
        • If installing perl, ActiveState Perl may be a better choice than cygwin's perl. Perl is not used by Geomview or Qhull.
        • Cygwin Package Search -- Search for cygwin programs and packages
      • Click 'Next' to download and install the packages.
      • If the download is incomplete, try again.
      • If you try again after a successful install, cygwin will uninstall and reinstall all modules..
      • Click on the 'Cywin Terminal' icon on the Desktop. It sets up a user directory in /home from /etc/skel/...
      • Mount your disk drives
        mount c: /c # Ignore the warning /c does not exist
    3. Consider installing the Road Bash scripts (/etc/road-*) from Road. They define aliases and functions for Unix command shells (Unix, Linux, Mac OS X, Windows),
      • Download Road Bash and unzip the downloaded file
      • Copy .../bash/etc/road-* to the Cywin /etc directory (by default, C:\cygwin\etc).
      • Using the cygwin terminal, convert the road scripts to Unix format
        d2u /etc/road-*
      • Try it
        source /etc/road-home.bashrc
      • Install it
        cp /etc/road-home.bashrc ~/.bashrc
    4. Launch the X terminal server from 'Start > All programs > Cygwin-X > Xwin Server'. Alternatively, run 'startx'
    5. Launch an XTerm shell
      • Right click the Cywin icon on the system tray in the Windows taskbar.
      • Select 'System Tools > XTerm'
    6. Download and extract Geomview -- Downloading Geomview
    7. Compile Geomview
      • ./configure
      • make
    8. If './configure' fails, check 'config.log' at the failing step. Look carefully for missing libraries, etc. The Geomview FAQ contains suggestions (e.g., "configure claims it can't find OpenGl").
    9. If 'make' fails, read the output carefully for error messages. Usually it is a missing include file or package. Locate and install the missing cygwin packages (Cygwin Package Search).

    »What to do if something goes wrong

    Please report bugs to qhull_bug@qhull.org . Please report if Qhull crashes. Please report if Qhull generates an "internal error". Please report if Qhull produces a poor approximate hull in 2-d, 3-d or 4-d. Please report documentation errors. Please report missing or incorrect links.

    If you do not understand something, try a small example. The rbox program is an easy way to generate test cases. The Geomview program helps to visualize the output from Qhull.

    If Qhull does not compile, it is due to an incompatibility between your system and ours. The first thing to check is that your compiler is ANSI standard. Qhull produces a compiler error if __STDC__ is not defined. You may need to set a flag (e.g., '-A' or '-ansi').

    If Qhull compiles but crashes on the test case (rbox D4), there's still incompatibility between your system and ours. Sometimes it is due to memory management. This can be turned off with qh_NOmem in mem.h. Please let us know if you figure out how to fix these problems.

    If you doubt the output from Qhull, add option 'Tv'. It checks that every point is inside the outer planes of the convex hull. It checks that every facet is convex with its neighbors. It checks the topology of the convex hull.

    Qhull should work on all inputs. It may report precision errors if you turn off merged facets with option 'Q0'. This can get as bad as facets with flipped orientation or two facets with the same vertices. You'll get a long help message if you run into such a case. They are easy to generate with rbox.

    If you do find a problem, try to simplify it before reporting the error. Try different size inputs to locate the smallest one that causes an error. You're welcome to hunt through the code using the execution trace ('T4') as a guide. This is especially true if you're incorporating Qhull into your own program.

    When you report an error, please attach a data set to the end of your message. Include the options that you used with Qhull, the results of option 'FO', and any messages generated by Qhull. This allows me to see the error for myself. Qhull is maintained part-time.

    »Email

    Please send correspondence to Brad Barber at qhull@qhull.org and report bugs to qhull_bug@qhull.org . Let me know how you use Qhull. If you mention it in a paper, please send a reference and abstract.

    If you would like to get Qhull announcements (e.g., a new version) and news (any bugs that get fixed, etc.), let us know and we will add you to our mailing list. For Internet news about geometric algorithms and convex hulls, look at comp.graphics.algorithms and sci.math.num-analysis. For Qhull news look at qhull-news.html.

    »Authors

       C. Bradford Barber                    Hannu Huhdanpaa
       bradb@shore.net                       hannu@qhull.org
    

    »Acknowledgments

    A special thanks to David Dobkin for his guidance. A special thanks to Albert Marden, Victor Milenkovic, the Geometry Center, and Harvard University for supporting this work.

    A special thanks to Mark Phillips, Robert Miner, and Stuart Levy for running the Geometry Center web site long after the Geometry Center closed. Stuart moved the web site to the University of Illinois at Champaign-Urbana. Mark and Robert are founders of Geometry Technologies. Mark, Stuart, and Tamara Munzner are the original authors of Geomview.

    A special thanks to Endocardial Solutions, Inc. of St. Paul, Minnesota for their support of the internal documentation (src/libqhull/index.html). They use Qhull to build 3-d models of heart chambers.

    Qhull 1.0 and 2.0 were developed under National Science Foundation grants NSF/DMS-8920161 and NSF-CCR-91-15793 750-7504. If you find it useful, please let us know.

    The Geometry Center was supported by grant DMS-8920161 from the National Science Foundation, by grant DOE/DE-FG02-92ER25137 from the Department of Energy, by the University of Minnesota, and by Minnesota Technology, Inc.

    »References

    Aurenhammer, F., "Voronoi diagrams -- A survey of a fundamental geometric data structure," ACM Computing Surveys, 1991, 23:345-405.

    Barber, C. B., D.P. Dobkin, and H.T. Huhdanpaa, "The Quickhull Algorithm for Convex Hulls," ACM Transactions on Mathematical Software, 22(4):469-483, Dec 1996, www.qhull.org [http://portal.acm.org; http://citeseerx.ist.psu.edu].

    Clarkson, K.L. and P.W. Shor, "Applications of random sampling in computational geometry, II", Discrete Computational Geometry, 4:387-421, 1989

    Clarkson, K.L., K. Mehlhorn, and R. Seidel, "Four results on randomized incremental construction," Computational Geometry: Theory and Applications, vol. 3, p. 185-211, 1993.

    Devillers, et. al., "Walking in a triangulation," ACM Symposium on Computational Geometry, June 3-5,2001, Medford MA.

    Dobkin, D.P. and D.G. Kirkpatrick, "Determining the separation of preprocessed polyhedra--a unified approach," in Proc. 17th Inter. Colloq. Automata Lang. Program., in Lecture Notes in Computer Science, Springer-Verlag, 443:400-413, 1990.

    Edelsbrunner, H, Geometry and Topology for Mesh Generation, Cambridge University Press, 2001.

    Gartner, B., "Fast and robust smallest enclosing balls", Algorithms - ESA '99, LNCS 1643.

    Golub, G.H. and van Loan, C.F., Matric Computations, Baltimore, Maryland, USA: John Hopkins Press, 1983

    Fortune, S., "Computational geometry," in R. Martin, editor, Directions in Geometric Computation, Information Geometers, 47 Stockers Avenue, Winchester, SO22 5LB, UK, ISBN 1-874728-02-X, 1993.

    Milenkovic, V., "Robust polygon modeling," Computer-Aided Design, vol. 25, p. 546-566, September 1993.

    Mucke, E.P., I. Saias, B. Zhu, Fast randomized point location without preprocessing in Two- and Three-dimensional Delaunay Triangulations, ACM Symposium on Computational Geometry, p. 274-283, 1996 [GeomDir].

    Mulmuley, K., Computational Geometry, An Introduction Through Randomized Algorithms, Prentice-Hall, NJ, 1994.

    O'Rourke, J., Computational Geometry in C, Cambridge University Press, 1994.

    Preparata, F. and M. Shamos, Computational Geometry, Springer-Verlag, New York, 1985.


    Up: Home page for Qhull
    Up:News about Qhull
    Up: FAQ about Qhull
    To: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
    Dn: Imprecision in Qhull
    Dn: Description of Qhull examples
    Dn: Qhull internals
    Dn: Qhull functions, macros, and data structures


    The Geometry Center Home Page

    Comments to: qhull@qhull.org
    Created: Sept. 25, 1995 --- Last modified: see top

    geometry/inst/doc/qhull/html/qh-optq.html0000644000176200001440000010326513431000557020204 0ustar liggesusers Qhull control options (Q)

    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions


    [delaunay] Qhull control options (Q)

    This section lists the control options for Qhull. These options are indicated by 'Q' followed by a letter.

    Copyright © 1995-2018 C.B. Barber


    » Programs OptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions

    Qhull control options

     
    General
    Qu
    compute upper hull for furthest-site Delaunay triangulation
    Qc
    keep coplanar points with nearest facet
    Qi
    keep interior points with nearest facet
    QJ
    joggled input to avoid precision problems
    Qt
    triangulated output
     
     
    Precision handling
    Qz
    add a point-at-infinity for Delaunay triangulations
    Qx
    exact pre-merges (allows coplanar facets)
    Qs
    search all points for the initial simplex
    Qbb
    scale last coordinate to [0,m] for Delaunay
    Qv
    test vertex neighbors for convexity
     
     
    Transform input
    Qbk:0Bk:0
    drop dimension k from input
    QRn
    random rotation (n=seed, n=0 time, n=-1 time/no rotate)
    Qbk:n
    scale coord[k] to low bound of n (default -0.5)
    QBk:n
    scale coord[k] to upper bound of n (default 0.5)
    QbB
    scale input to fit the unit cube
     
     
    Select facets
    QVn
    good facet if it includes point n, -n if not
    QGn
    good facet if visible from point n, -n for not visible
    Qg
    only build good facets (needs 'QGn', 'QVn ', or 'Pdk')
     
     
    Experimental
    Q4
    avoid merging old facets into new facets
    Q5
    do not correct outer planes at end of qhull
    Q3
    do not merge redundant vertices
    Q6
    do not pre-merge concave or coplanar facets
    Q0
    do not pre-merge facets with 'C-0' or 'Qx'
    Q8
    ignore near-interior points
    Q2
    merge all non-convex at once instead of independent sets
    Qf
    partition point to furthest outside facet
    Q7
    process facets depth-first instead of breadth-first
    Q9
    process furthest of furthest points
    Q10
    no special processing for narrow distributions
    Q11
    copy normals and recompute centrums for tricoplanar facets
    Q12
    do not error on wide merge due to duplicate ridge and nearly coincident points
    Q14
    do not rename vertices that create a duplicate ridge
    Qm
    process points only if they would increase the max. outer plane
    Qr
    process random outside points instead of furthest one
    Q1
    sort merges by type instead of angle

    »Qbb - scale the last coordinate to [0,m] for Delaunay

    After scaling with option 'Qbb', the lower bound of the last coordinate will be 0 and the upper bound will be the maximum width of the other coordinates. Scaling happens after projecting the points to a paraboloid and scaling other coordinates.

    Option 'Qbb' is automatically set for qdelaunay and qvoronoi. Option 'Qbb' is automatically set for joggled input 'QJ'.

    Option 'Qbb' should be used for Delaunay triangulations with integer coordinates. Since the last coordinate is the sum of squares, it may be much larger than the other coordinates. For example, rbox 10000 D2 B1e8 | qhull d has precision problems while rbox 10000 D2 B1e8 | qhull d Qbb is OK.

    »QbB - scale the input to fit the unit cube

    After scaling with option 'QbB', the lower bound will be -0.5 and the upper bound +0.5 in all dimensions. For different bounds change qh_DEFAULTbox in user.h (0.5 is best for Geomview).

    For Delaunay and Voronoi diagrams, scaling happens after projection to the paraboloid. Under precise arithmetic, scaling does not change the topology of the convex hull. Scaling may reduce precision errors if coordinate values vary widely.

    »Qbk:n - scale coord[k] to low bound

    After scaling, the lower bound for dimension k of the input points will be n. 'Qbk' scales coord[k] to -0.5.

    »QBk:n - scale coord[k] to upper bound

    After scaling, the upper bound for dimension k of the input points will be n. 'QBk' scales coord[k] to 0.5.

    »Qbk:0Bk:0 - drop dimension k from the input points

    Drop dimension k from the input points. For example, 'Qb1:0B1:0' deletes the y-coordinate from all input points. This allows the user to take convex hulls of sub-dimensional objects. It happens before the Delaunay and Voronoi transformation. It happens after the halfspace transformation for both the data and the feasible point.

    »Qc - keep coplanar points with nearest facet

    During construction of the hull, a point is coplanar if it is between 'Wn' above and 'Un' below a facet's hyperplane. A different definition is used for output from Qhull.

    For output, a coplanar point is above the minimum vertex (i.e., above the inner plane). With joggle ('QJ'), a coplanar point includes points within one joggle of the inner plane.

    With option 'Qc', output formats 'p ', 'f', 'Gp', 'Fc', 'FN', and 'FP' will print the coplanar points. With options 'Qc Qi' these outputs include the interior points.

    For Delaunay triangulations (qdelaunay or qvoronoi), a coplanar point is a point that is nearly incident to a vertex. All input points are either vertices of the triangulation or coplanar.

    Qhull stores coplanar points with a facet. While constructing the hull, it retains all points within qh_RATIOnearInside (user.h) of a facet. In qh_check_maxout(), it uses these points to determine the outer plane for each facet. With option 'Qc', qh_check_maxout() retains points above the minimum vertex for the hull. Other points are removed. If qh_RATIOnearInside is wrong or if options 'Q5 Q8' are set, a coplanar point may be missed in the output (see Qhull limitations).

    »Qf - partition point to furthest outside facet

    After adding a new point to the convex hull, Qhull partitions the outside points and coplanar points of the old, visible facets. Without the 'f ' option and merging, it assigns a point to the first facet that it is outside ('Wn'). When merging, it assigns a point to the first facet that is more than several times outside (see qh_DISToutside in user.h).

    If option 'Qf' is selected, Qhull performs a directed search (no merging) or an exhaustive search (merging) of new facets. Option 'Qf' may reduce precision errors if pre-merging does not occur.

    Option 'Q9' processes the furthest of all furthest points.

    »Qg - only build good facets (needs 'QGn' 'QVn' or 'Pdk')

    Qhull has several options for defining and printing good facets. With the 'Qg' option, Qhull will only build those facets that it needs to determine the good facets in the output. This may speed up Qhull in 2-d and 3-d. It is useful for furthest-site Delaunay triangulations (qdelaunay Qu, invoke with 'qhull d Qbb Qu Qg'). It is not effective in higher dimensions because many facets see a given point and contain a given vertex. It may not work for all combinations.

    See 'QGn', 'QVn', and 'Pdk' for defining good facets, and 'Pg' and 'PG' for printing good facets and their neighbors. If pre-merging ('C-n') is not used and there are coplanar facets, then 'Qg Pg' may produce a different result than 'Pg'. Option Qg disables renaming vertices that create a duplicate ridge ('Q14').

    »QGn - good facet if visible from point n, -n for not visible

    With option 'QGn', a facet is good (see 'Qg' and 'Pg') if it is visible from point n. If n < 0, a facet is good if it is not visible from point n. Point n is not added to the hull (unless 'TCn' or 'TPn').

    With rbox, use the 'Pn,m,r' option to define your point; it will be point 0 ('QG0').

    »Qi - keep interior points with nearest facet

    Normally Qhull ignores points that are clearly interior to the convex hull. With option 'Qi', Qhull treats interior points the same as coplanar points. Option 'Qi' does not retain coplanar points. You will probably want 'Qc ' as well.

    Option 'Qi' is automatically set for 'qdelaunay Qc' and 'qvoronoi Qc'. If you use 'qdelaunay Qi' or 'qvoronoi Qi', option 's' reports all nearly incident points while option 'Fs' reports the number of interior points (should always be zero).

    With option 'Qi', output formats 'p', 'f','Gp', 'Fc', 'FN', and 'FP' include interior points.

    »QJ or QJn - joggled input to avoid precision errors

    Option 'QJ' or 'QJn' joggles each input coordinate by adding a random number in the range [-n,n]. If a precision error occurs, It tries again. If precision errors still occur, Qhull increases n ten-fold and tries again. The maximum value for increasing n is 0.01 times the maximum width of the input. Option 'QJ' selects a default value for n. User.h defines these parameters and a maximum number of retries. See Merged facets or joggled input.

    Users of joggled input should consider converting to triangulated output ('Qt'). Triangulated output is approximately 1000 times more accurate than joggled input.

    Option 'QJ' also sets 'Qbb' for Delaunay triangulations and Voronoi diagrams. It does not set 'Qbb' if 'Qbk:n' or 'QBk:n' are set.

    If 'QJn' is set, Qhull does not merge facets unless requested to. All facets are simplicial (triangular in 2-d). This may be important for your application. You may also use triangulated output ('Qt') or Option 'Ft'.

    Qhull adjusts the outer and inner planes for 'QJn' ('Fs'). They are increased by sqrt(d)*n to account for the maximum distance between a joggled point and the corresponding input point. Coplanar points ('Qc') require an additional sqrt(d)*n since vertices and coplanar points may be joggled in opposite directions.

    For Delaunay triangulations (qdelaunay), joggle happens before lifting the input sites to a paraboloid. Instead of 'QJ', you may use triangulated output ('Qt')

    This option is deprecated for Voronoi diagrams (qvoronoi). It triangulates cospherical points, leading to duplicated Voronoi vertices.

    By default, 'QJn' uses a fixed random number seed. To use time as the random number seed, select 'QR-1'. The summary ('s') will show the selected seed as 'QR-n'.

    With 'QJn', Qhull does not error on degenerate hyperplane computations. Except for Delaunay and Voronoi computations, Qhull does not error on coplanar points.

    Use option 'FO' to display the selected options. Option 'FO' displays the joggle and the joggle seed. If Qhull restarts, it will redisplay the options.

    Use option 'TRn' to estimate the probability that Qhull will fail for a given 'QJn'.

    »Qm - only process points that increase the maximum outer plane

    Qhull reports the maximum outer plane in its summary ('s'). With option 'Qm', Qhull does not process points that are below the current, maximum outer plane. This is equivalent to always adjusting 'Wn ' to the maximum distance of a coplanar point to a facet. It is ignored for points above the upper convex hull of a Delaunay triangulation. Option 'Qm' is no longer important for merging.

    »Qr - process random outside points instead of furthest ones

    Normally, Qhull processes the furthest point of a facet's outside points. Option 'Qr' instead selects a random outside point for processing. This makes Qhull equivalent to the randomized incremental algorithms.

    The original randomization algorithm by Clarkson and Shor ['89] used a conflict list which is equivalent to Qhull's outside set. Later randomized algorithms retained the previously constructed facets.

    To compare Qhull to the randomized algorithms with option 'Qr', compare "hyperplanes constructed" and "distance tests". Qhull does not report CPU time because the randomization is inefficient.

    »QRn - random rotation

    Option 'QRn' randomly rotates the input. For Delaunay triangulations (qdelaunay or qvoronoi), it rotates the lifted points about the last axis.

    If n=0, use time as the random number seed. If n>0, use n as the random number seed. If n=-1, don't rotate but use time as the random number seed. If n<-1, don't rotate but use n as the random number seed.

    »Qs - search all points for the initial simplex

    Qhull constructs an initial simplex from d+1 points. It selects points with the maximum and minimum coordinates and non-zero determinants. If this fails, it searches all other points. In 8-d and higher, Qhull selects points with the minimum x or maximum coordinate (see qh_initialvertices in poly2.c ). It rejects points with nearly zero determinants. This should work for almost all input sets.

    If Qhull can not construct an initial simplex, it reports a descriptive message. Usually, the point set is degenerate and one or more dimensions should be removed ('Qbk:0Bk:0'). If not, use option 'Qs'. It performs an exhaustive search for the best initial simplex. This is expensive is high dimensions.

    »Qt - triangulated output

    By default, qhull merges facets to handle precision errors. This produces non-simplicial facets (e.g., the convex hull of a cube has 6 square facets). Each facet is non-simplicial because it has four vertices.

    Use option 'Qt' to triangulate all non-simplicial facets before generating results. Alternatively, use joggled input ('QJ') to prevent non-simplical facets. Unless 'Pp' is set, qhull produces a warning if 'QJ' and 'Qt' are used together.

    For Delaunay triangulations (qdelaunay), triangulation occurs after lifting the input sites to a paraboloid and computing the convex hull.

    Option 'Qt' is deprecated for Voronoi diagrams (qvoronoi). It triangulates cospherical points, leading to duplicated Voronoi vertices.

    Option 'Qt' may produce degenerate facets with zero area.

    Facet area and hull volumes may differ with and without 'Qt'. The triangulations are different and different triangles may be ignored due to precision errors.

    With sufficient merging, the ridges of a non-simplicial facet may share more than two neighboring facets. If so, their triangulation ('Qt') will fail since two facets have the same vertex set.

    »Qu - compute upper hull for furthest-site Delaunay triangulation

    When computing a Delaunay triangulation (qdelaunay or qvoronoi), Qhull computes both the the convex hull of points on a paraboloid. It normally prints facets of the lower hull. These correspond to the Delaunay triangulation. With option 'Qu', Qhull prints facets of the upper hull. These correspond to the furthest-site Delaunay triangulation and the furthest-site Voronoi diagram.

    Option 'qhull d Qbb Qu Qg' may improve the speed of option 'Qu'. If you use the Qhull library, a faster method is 1) use Qhull to compute the convex hull of the input sites; 2) take the extreme points (vertices) of the convex hull; 3) add one interior point (e.g., 'FV', the average of d extreme points); 4) run 'qhull d Qbb Qu' or 'qhull v Qbb Qu' on these points.

    »Qv - test vertex neighbors for convexity

    Normally, Qhull tests all facet neighbors for convexity. Non-neighboring facets which share a vertex may not satisfy the convexity constraint. This occurs when a facet undercuts the centrum of another facet. They should still be convex. Option 'Qv' extends Qhull's convexity testing to all neighboring facets of each vertex. The extra testing occurs after the hull is constructed..

    »QVn - good facet if it includes point n, -n if not

    With option 'QVn', a facet is good ('Qg', 'Pg') if one of its vertices is point n. If n<0, a good facet does not include point n.

    If options 'PG' and 'Qg' are not set, option 'Pg' (print only good) is automatically set.

    Option 'QVn' behaves oddly with options 'Fx' and 'qvoronoi Fv'.

    If used with option 'Qg' (only process good facets), point n is either in the initial simplex or it is the first point added to the hull. Options 'QVn Qg' require either 'QJ' or 'Q0' (no merging).

    »Qx - exact pre-merges (allows coplanar facets)

    Option 'Qx' performs exact merges while building the hull. Options 'Qx' and 'C-0' are set by default in 5-d and higher. To disable this default, set option 'C-0' or another pre-merge option. Use option 'Q0' to disable all merging, including 'Qx' and 'C-0'.

    The "exact" pre-merges are merging a point into a coplanar facet (defined by 'Vn ', 'Un', and 'C-n'), merging concave facets, merging duplicate ridges, and merging flipped facets. Coplanar merges and angle coplanar merges ('A-n') are not performed. Concavity testing is delayed until a merge occurs.

    FIXUP -- isn't merging enabled once a merge happens?

    After the hull is built, all coplanar merges are performed (defined by 'C-n' and 'A-n'), then post-merges are performed (defined by 'Cn' and 'An'). If facet progress is logged ('TFn'), Qhull reports each phase and prints intermediate summaries and statistics ('Ts').

    Without 'Qx' in 5-d and higher, options 'C-n' and 'A-n' may merge too many facets. Since redundant vertices are not removed effectively, facets become increasingly wide.

    Option 'Qx' may report a wide facet. With 'Qx', coplanar facets are not merged. This can produce a "dent" in an intermediate hull. If a point is partitioned into a dent and it is below the surrounding facets but above other facets, one or more wide facets will occur. In practice, this is unlikely. To observe this effect, run Qhull with option 'Q6' which doesn't pre-merge concave facets. A concave facet makes a large dent in the intermediate hull.

    Option 'Qx' may set an outer plane below one of the input points. A coplanar point may be assigned to the wrong facet because of a "dent" in an intermediate hull. After constructing the hull, Qhull double checks all outer planes with qh_check_maxout in poly2.c . If a coplanar point is assigned to the wrong facet, qh_check_maxout may reach a local maximum instead of locating all coplanar facets. This appears to be unlikely.

    »Qz - add a point-at-infinity for Delaunay triangulations

    Option 'Qz' adds a point above the paraboloid of lifted sites for a Delaunay triangulation. It allows the Delaunay triangulation of cospherical sites. It reduces precision errors for nearly cospherical sites.

    »Q0 - no merging with C-0 and Qx

    Turn off default merge options 'C-0' and 'Qx'.

    With 'Q0' and without other pre-merge options, Qhull ignores precision issues while constructing the convex hull. This may lead to precision errors. If so, a descriptive warning is generated. See Precision issues.

    »Q1 - sort merges by type instead of angle

    Qhull sorts the coplanar facets before picking a subset of the facets to merge. It merges concave and flipped facets first. Then it merges facets that meet at a steep angle. With 'Q1', Qhull sorts merges by type (coplanar, angle coplanar, concave) instead of by angle. This may make the facets wider.

    »Q2 - merge all non-convex at once instead of independent sets

    With 'Q2', Qhull merges all facets at once instead of performing merges in independent sets. This may make the facets wider.

    »Q3 - do not merge redundant vertices

    With 'Q3', Qhull does not remove redundant vertices. In 6-d and higher, Qhull never removes redundant vertices (since vertices are highly interconnected). Option 'Q3' may be faster, but it may result in wider facets. Its effect is easiest to see in 3-d and 4-d.

    »Q4 - avoid merging old facets into new facets

    With 'Q4', Qhull avoids merges of an old facet into a new facet. This sometimes improves facet width and sometimes makes it worse.

    »Q5 - do not correct outer planes at end of qhull

    When merging facets or approximating a hull, Qhull tests coplanar points and outer planes after constructing the hull. It does this by performing a directed search (qh_findbest in geom.c). It includes points that are just inside the hull.

    With options 'Q5' or 'Po', Qhull does not test outer planes. The maximum outer plane is used instead. Coplanar points ('Qc') are defined by 'Un'. An input point may be outside of the maximum outer plane (this appears to be unlikely). An interior point may be above 'Un' from a hyperplane.

    Option 'Q5' may be used if outer planes are not needed. Outer planes are needed for options 's', 'G', 'Go ', 'Fs', 'Fo', 'FF', and 'f'.

    »Q6 - do not pre-merge concave or coplanar facets

    With 'Q6', Qhull does not pre-merge concave or coplanar facets. This demonstrates the effect of "dents" when using 'Qx'.

    »Q7 - depth-first processing instead of breadth-first

    With 'Q7', Qhull processes facets in depth-first order instead of breadth-first order. This may increase the locality of reference in low dimensions. If so, Qhull may be able to use virtual memory effectively.

    In 5-d and higher, many facets are visible from each unprocessed point. So each iteration may access a large proportion of allocated memory. This makes virtual memory ineffectual. Once real memory is used up, Qhull will spend most of its time waiting for I/O.

    Under 'Q7', Qhull runs slower and the facets may be wider.

    »Q8 - ignore near-interior points

    With 'Q8' and merging, Qhull does not process interior points that are near to a facet (as defined by qh_RATIOnearInside in user.h). This avoids partitioning steps. It may miss a coplanar point when adjusting outer hulls in qh_check_maxout(). The best value for qh_RATIOnearInside is not known. Options 'Q8 Qc' may be sufficient.

    »Q9 - process furthest of furthest points

    With 'Q9', Qhull processes the furthest point of all outside sets. This may reduce precision problems. The furthest point of all outside sets is not necessarily the furthest point from the convex hull.

    »Q10 - no special processing for narrow distributions

    With 'Q10', Qhull does not special-case narrow distributions. See Limitations of merged facets for more information.

    »Q11 - copy normals and recompute centrums for tricoplanar facets

    Option 'Qt' triangulates non-simplicial facets into "tricoplanar" facets. Normally tricoplanar facets share the same normal, centrum, and Voronoi vertex. They can not be merged or replaced. With option 'Q11', Qhull duplicates the normal and Voronoi vertex. It recomputes the centrum.

    Use 'Q11' if you use the Qhull library to add points incrementally and call qh_triangulate() after each point. Otherwise, Qhull will report an error when it tries to merge and replace a tricoplanar facet.

    With sufficient merging and new points, option 'Q11' may lead to precision problems such as duplicate ridges and concave facets. For example, if qh_triangulate() is added to qh_addpoint(), RBOX 1000 s W1e-12 t1001813667 P0 | QHULL d Q11 Tv, reports an error due to a duplicate ridge.

    »Q12 - do not error on wide merge due to duplicate ridge and nearly coincident points

    In 3-d and higher Delaunay Triangulations or 4-d and higher convex hulls, multiple, nearly coincident points may lead to very wide facets. An error is reported if a merge across a duplicate ridge would increase the facet width by 100x or more.

    Use option 'Q12' to log a warning instead of throwing an error.

    For Delaunay triangulations, a bounding box may alleviate this error (e.g., rbox 500 C1,1E-13 t c G1 | qhull d). This avoids the ill-defined edge between upper and lower convex hulls. The problem will be fixed in a future release of Qhull.

    To demonstrate the problem, use rbox option 'Cn,r,m' to generate nearly coincident points. For more information, see "Nearly coincident points on an edge" in Nearly coincident points on an edge.

    »Q14 - do not rename vertices that create a duplicate ridge

    In 3-d and higher, nearly adjacent vertices may lead to duplicate ridges and wide merges (see "Nearly coincident points within 1e-13" in Imprecision in Qhull).

    Duplicate ridges are removed by renaming one of the nearly adjacent vertices to another vertex. Facets containing this vertex may become redundant. If so, they are merged into an adjacent facet. At most FIXUP attempts will be made. The cost for detecting this situation is approximately FIXUP%. For each point added to the hull, Qhull delays attaching the cone of new facets to the horizon facets.

    This feature is turned off with option Q14. It is also turned off for option 'Qg' (Only build good facets), or if pre-merging is turned off (e.g., option 'Q0', do not pre-merge facets with 'C-0' or 'Qx').

    »Q15 - allow wide maxout

    FIXUP -- document Q15 and Q16 -- add to index and to 'qhull .'


    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions


    The Geometry Center Home Page

    Comments to: qhull@qhull.org
    Created: Sept. 25, 1995 --- Last modified: see top

    geometry/inst/doc/qhull/html/qh-optf.html0000644000176200001440000007526513431000557020201 0ustar liggesusers Qhull format options (F)

    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions


    [delaunay] Qhull format options (F)

    This section lists the format options for Qhull. These options are indicated by 'F' followed by a letter. See Output, Print, and Geomview for other output options.

    Copyright © 1995-2018 C.B. Barber


    » Programs OptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions

    Additional input & output formats

    These options allow for automatic processing of Qhull output. Options 'i', 'o', 'n', and 'p' may also be used.

    Summary and control
    FA
    compute total area and volume for option 's'
    FV
    print average vertex (interior point for 'qhalf')
    FQ
    print command for qhull and input
    FO
    print options to stderr or stdout
    FS
    print sizes: total area and volume
    Fs
    print summary: dim, #points, total vertices and facets, #vertices, #facets, max outer and inner plane
    Fd
    use format for input (offset first)
    FD
    use cdd format for normals (offset first)
    FM
    print Maple output (2-d and 3-d)
    Facets, points, and vertices
    Fa
    print area for each facet
    FC
    print centrum for each facet
    Fc
    print coplanar points for each facet
    Fx
    print extreme points (i.e., vertices) of convex hull.
    FF
    print facets w/o ridges
    FI
    print ID for each facet
    Fi
    print inner planes for each facet
    Fm
    print merge count for each facet (511 max)
    FP
    print nearest vertex for coplanar points
    Fn
    print neighboring facets for each facet
    FN
    print neighboring facets for each point
    Fo
    print outer planes for each facet
    Ft
    print triangulation with added points
    Fv
    print vertices for each facet
    Delaunay, Voronoi, and halfspace
    Fx
    print extreme input sites of Delaunay triangulation or Voronoi diagram.
    Fp
    print points at halfspace intersections
    Fi
    print separating hyperplanes for inner, bounded Voronoi regions
    Fo
    print separating hyperplanes for outer, unbounded Voronoi regions
    Fv
    print Voronoi diagram as ridges for each input pair
    FC
    print Voronoi vertex ("center") for each facet

    »Fa - print area for each facet

    The first line is the number of facets. The remaining lines are the area for each facet, one facet per line. See 'FA' and 'FS' for computing the total area and volume.

    Use 'PAn' for printing the n largest facets. Use option 'PFn' for printing facets larger than n.

    For Delaunay triangulations, the area is the area of each Delaunay triangle. For Voronoi vertices, the area is the area of the dual facet to each vertex.

    Qhull uses the centrum and ridges to triangulate non-simplicial facets. The area for non-simplicial facets is the sum of the areas for each triangle. It is an approximation of the actual area. The ridge's vertices are projected to the facet's hyperplane. If a vertex is far below a facet (qh_WIDEcoplanar in user.h), the corresponding triangles are ignored.

    For non-simplicial facets, vertices are often below the facet's hyperplane. If so, the approximation is less than the actual value and it may be significantly less.

    »FA - compute total area and volume for option 's'

    With option 'FA', Qhull includes the total area and volume in the summary ('s'). Option 'FS' also includes the total area and volume. If facets are merged, the area and volume are approximations. Option 'FA' is automatically set for options 'Fa', 'PAn', and 'PFn'.

    With 'qdelaunay s FA', Qhull computes the total area of the Delaunay triangulation. This equals the volume of the convex hull of the data points. With options 'qdelaunay Qu s FA', Qhull computes the total area of the furthest-site Delaunay triangulation. This equals of the total area of the Delaunay triangulation.

    See 'Fa' for further details. Option 'FS' also computes the total area and volume.

    »Fc - print coplanar points for each facet

    The output starts with the number of facets. Then each facet is printed one per line. Each line is the number of coplanar points followed by the point ids.

    By default, option 'Fc' reports coplanar points ('Qc'). You may also use option 'Qi'. Options 'Qi Fc' prints interior points while 'Qci Fc' prints both coplanar and interior points.

    Each coplanar point or interior point is assigned to the facet it is furthest above (resp., least below).

    Use 'Qc p' to print vertex and coplanar point coordinates. Use 'Fv' to print vertices.

    »FC - print centrum or Voronoi vertex for each facet

    The output starts with the dimension followed by the number of facets. Then each facet centrum is printed, one per line. For qvoronoi, Voronoi vertices are printed instead.

    »Fd - use cdd format for input

    The input starts with comments. The first comment is reported in the summary. Data starts after a "begin" line. The next line is the number of points followed by the dimension plus one and "real" or "integer". Then the points are listed with a leading "1" or "1.0". The data ends with an "end" line.

    For halfspaces ('qhalf Fd'), the input format is the same. Each halfspace starts with its offset. The signs of the offset and coefficients are the opposite of Qhull's convention. The first two lines of the input may be an interior point in 'FV' format.

    »FD - use cdd format for normals

    Option 'FD' prints normals ('n', 'Fo', 'Fi') or points ('p') in cdd format. The first line is the command line that invoked Qhull. Data starts with a "begin" line. The next line is the number of normals or points followed by the dimension plus one and "real". Then the normals or points are listed with the offset before the coefficients. The offset for points is 1.0. For normals, the offset and coefficients use the opposite sign from Qhull. The data ends with an "end" line.

    »FF - print facets w/o ridges

    Option 'FF' prints all fields of all facets (as in 'f') without printing the ridges. This is useful in higher dimensions where a facet may have many ridges. For simplicial facets, options 'FF' and 'f ' are equivalent.

    »Fi - print inner planes for each facet

    The first line is the dimension plus one. The second line is the number of facets. The remainder is one inner plane per line. The format is the same as option 'n'.

    The inner plane is a plane that is below the facet's vertices. It is an offset from the facet's hyperplane. It includes a roundoff error for computing the vertex distance.

    Note that the inner planes for Geomview output ('Gi') include an additional offset for vertex visualization and roundoff error.

    »Fi - print separating hyperplanes for inner, bounded Voronoi regions

    With qvoronoi, 'Fi' prints the separating hyperplanes for inner, bounded regions of the Voronoi diagram. The first line is the number of ridges. Then each hyperplane is printed, one per line. A line starts with the number of indices and floats. The first pair of indices indicates an adjacent pair of input sites. The next d floats are the normalized coefficients for the hyperplane, and the last float is the offset. The hyperplane is oriented toward 'QVn' (if defined), or the first input site of the pair.

    Use 'Fo' for unbounded regions, and 'Fv' for the corresponding Voronoi vertices.

    Use 'Tv' to verify that the hyperplanes are perpendicular bisectors. It will list relevant statistics to stderr. The hyperplane is a perpendicular bisector if the midpoint of the input sites lies on the plane, all Voronoi vertices in the ridge lie on the plane, and the angle between the input sites and the plane is ninety degrees. This is true if all statistics are zero. Roundoff and computation errors make these non-zero. The deviations appear to be largest when the corresponding Delaunay triangles are large and thin; for example, the Voronoi diagram of nearly cospherical points.

    »FI - print ID for each facet

    Print facet identifiers. These are used internally and listed with options 'f' and 'FF'. Options 'Fn ' and 'FN' use facet identifiers for negative indices.

    »Fm - print merge count for each facet

    The first line is the number of facets. The remainder is the number of merges for each facet, one per line. At most 511 merges are reported for a facet. See 'PMn' for printing the facets with the most merges.

    »FM - print Maple output

    Qhull writes a Maple file for 2-d and 3-d convex hulls, 2-d and 3-d halfspace intersections, and 2-d Delaunay triangulations. Qhull produces a 2-d or 3-d plot.

    Warning: This option has not been tested in Maple.

    [From T. K. Abraham with help from M. R. Feinberg and N. Platinova.] The following steps apply while working within the Maple worksheet environment :

    1. Generate the data and store it as an array . For example, in 3-d, data generated in Maple is of the form : x[i],y[i],z[i]

    2. Create a single variable and assign the entire array of data points to this variable. Use the "seq" command within square brackets as shown in the following example. (The square brackets are essential for the rest of the steps to work.)

      >data:=[seq([x[i],y[i],z[i]],i=1..n)]:# here n is the number of data points

    3. Next we need to write the data to a file to be read by qhull. Before writing the data to a file, make sure that the qhull executable files and the data file lie in the same subdirectory. If the executable files are stored in the "C:\qhull3.1\" subdirectory, then save the file in the same subdirectory, say "C:\qhull3.1\datafile.txt". For the sake of integrity of the data file , it is best to first ensure that the data file does not exist before writing into the data file. This can be done by running a delete command first . To write the data to the file, use the "writedata" and the "writedata[APPEND]" commands as illustrated in the following example :

      >system("del c:\\qhull3.1\\datafile.txt");#To erase any previous versions of the file
      >writedata("c:\\qhull3.1\\datafile.txt ",[3, nops(data)]);#writing in qhull format
      >writedata[APPEND]("c:\\ qhull3.1\\datafile.txt ", data);#writing the data points

    4. Use the 'FM' option to produce Maple output. Store the output as a ".mpl" file. For example, using the file we created above, we type the following (in DOS environment)

      qconvex s FM <datafile.txt >dataplot.mpl

    5. To read 3-d output in Maple, we use the 'read' command followed by a 'display3d' command. For example (in Maple environment):

      >with (plots):
      >read `c:\\qhull3.1\\dataplot.mpl`:#IMPORTANT - Note that the punctuation mark used is ' and NOT '. The correct punctuation mark is the one next to the key for "1" (not the punctuation mark near the enter key)
      > qhullplot:=%:
      > display3d(qhullplot);

    For Delaunay triangulation orthogonal projection is better.

    For halfspace intersections, Qhull produces the dual convex hull.

    See Is Qhull available for Maple? for other URLs.

    »Fn - print neighboring facets for each facet

    The output starts with the number of facets. Then each facet is printed one per line. Each line is the number of neighbors followed by an index for each neighbor. The indices match the other facet output formats.

    For simplicial facets, each neighbor is opposite the corresponding vertex (option 'Fv'). Do not compare to option 'i'. Option 'i' orients facets by reversing the order of two vertices. For non-simplicial facets, the neighbors are unordered.

    A negative index indicates an unprinted facet due to printing only good facets ('Pg', qdelaunay, qvoronoi). It is the negation of the facet's ID (option 'FI'). For example, negative indices are used for facets "at infinity" in the Delaunay triangulation.

    »FN - print neighboring facets for each point

    The first line is the number of points. Then each point is printed, one per line. For unassigned points (either interior or coplanar), the line is "0". For assigned coplanar points ('Qc'), the line is "1" followed by the index of the facet that is furthest below the point. For assigned interior points ('Qi'), the line is "1" followed by the index of the facet that is least above the point. For vertices that do not belong to good facet, the line is "0"

    For vertices of good facets, the line is the number of neighboring facets followed by the facet indices. The indices correspond to the other 'F' formats. In 4-d and higher, the facets are sorted by index. In 3-d, the facets are in adjacency order (not oriented).

    A negative index indicates an unprinted facet due to printing only good facets (qdelaunay, qvoronoi, 'Pdk', 'Pg'). It is the negation of the facet's ID (' FI'). For example, negative indices are used for facets "at infinity" in the Delaunay triangulation.

    For Voronoi vertices, option 'FN' lists the vertices of the Voronoi region for each input site. Option 'FN' lists the regions in site ID order. Option 'FN' corresponds to the second half of option 'o'. To convert from 'FN' to 'o', replace negative indices with zero and increment non-negative indices by one.

    If you are using the Qhull library or C++ interface, option 'FN' has the side effect of reordering the neighbors for a vertex

    »Fo - print outer planes for each facet

    The first line is the dimension plus one. The second line is the number of facets. The remainder is one outer plane per line. The format is the same as option 'n'.

    The outer plane is a plane that is above all points. It is an offset from the facet's hyperplane. It includes a roundoff error for computing the point distance. When testing the outer plane (e.g., 'Tv'), another roundoff error should be added for the tested point.

    If outer planes are not checked ('Q5') or not computed (!qh_MAXoutside), the maximum, computed outside distance is used instead. This can be much larger than the actual outer planes.

    Note that the outer planes for Geomview output ('G') include an additional offset for vertex/point visualization, 'lines closer,' and roundoff error.

    »Fo - print separating hyperplanes for outer, unbounded Voronoi regions

    With qvoronoi, 'Fo' prints the separating hyperplanes for outer, unbounded regions of the Voronoi diagram. The first line is the number of ridges. Then each hyperplane is printed, one per line. A line starts with the number of indices and floats. The first pair of indices indicates an adjacent pair of input sites. The next d floats are the normalized coefficients for the hyperplane, and the last float is the offset. The hyperplane is oriented toward 'QVn' (if defined), or the first input site of the pair.

    Option 'Fo' gives the hyperplanes for the unbounded rays of the unbounded regions of the Voronoi diagram. Each hyperplane goes through the midpoint of the corresponding input sites. The rays are directed away from the input sites.

    Use 'Fi' for bounded regions, and 'Fv' for the corresponding Voronoi vertices. Use 'Tv' to verify that the corresponding Voronoi vertices lie on the hyperplane.

    »FO - print list of selected options

    Lists selected options and default values to stderr. Additional 'FO's are printed to stdout.

    »Fp - print points at halfspace intersections

    The first line is the number of intersection points. The remainder is one intersection point per line. A intersection point is the intersection of d or more halfspaces from 'qhalf'. It corresponds to a facet of the dual polytope. The "infinity" point [-10.101,-10.101,...] indicates an unbounded intersection.

    If [x,y,z] are the dual facet's normal coefficients and b<0 is its offset, the halfspace intersection occurs at [x/-b,y/-b,z/-b] plus the interior point. If b>=0, the halfspace intersection is unbounded.

    »FP - print nearest vertex for coplanar points

    The output starts with the number of coplanar points. Then each coplanar point is printed one per line. Each line is the point ID of the closest vertex, the point ID of the coplanar point, the corresponding facet ID, and the distance. Sort the lines to list the coplanar points nearest to each vertex.

    Use options 'Qc' and/or 'Qi' with 'FP'. Options 'Qc FP' prints coplanar points while 'Qci FP' prints coplanar and interior points. Option 'Qc' is automatically selected if 'Qi' is not selected.

    For Delaunay triangulations (qdelaunay or qvoronoi), a coplanar point is nearly incident to a vertex. The distance is the distance in the original point set.

    If imprecision problems are severe, Qhull will delete input sites when constructing the Delaunay triangulation. Option 'FP' will list these points along with coincident points.

    If there are many coplanar or coincident points and non-simplicial facets are triangulated ('Qt'), option 'FP' may be inefficient. It redetermines the original vertex set for each coplanar point.

    »FQ - print command for qhull and input

    Prints qhull and input command, e.g., "rbox 10 s | qhull FQ". Option 'FQ' may be repeated multiple times.

    »Fs - print summary

    The first line consists of number of integers ("10") followed by the:

    • dimension
    • number of points
    • number of vertices
    • number of facets
    • number of vertices selected for output
    • number of facets selected for output
    • number of coplanar points for selected facets
    • number of nonsimplicial or merged facets selected for output
    • number of deleted vertices
    • number of triangulated facets ('Qt')

    The second line consists of the number of reals ("2") followed by the:

    • maximum offset to an outer plane
    • minimum offset to an inner plane.
    Roundoff and joggle are included.

    For Delaunay triangulations and Voronoi diagrams, the number of deleted vertices should be zero. If greater than zero, then the input is highly degenerate and coplanar points are not necessarily coincident points. For example, 'RBOX 1000 s W1e-13 t995138628 | QHULL d Qbb' reports deleted vertices; the input is nearly cospherical.

    Later versions of Qhull may produce additional integers or reals.

    »FS - print sizes

    The first line consists of the number of integers ("0"). The second line consists of the number of reals ("2"), followed by the total facet area, and the total volume. Later versions of Qhull may produce additional integers or reals.

    The total volume measures the volume of the intersection of the halfspaces defined by each facet. It is computed from the facet area. Both area and volume are approximations for non-simplicial facets. See option 'Fa ' for further notes. Option 'FA ' also computes the total area and volume.

    »Ft - print triangulation

    Prints a triangulation with added points for non-simplicial facets. The output is

    • The first line is the dimension
    • The second line is the number of points, the number of facets, and the number of ridges.
    • All of the input points follow, one per line.
    • The centrums follow, one per non-simplicial facet
    • Then the facets follow as a list of point indices preceded by the number of points. The simplices are oriented.

    For convex hulls with simplicial facets, the output is the same as option 'o'.

    The added points are the centrums of the non-simplicial facets. Except for large facets, the centrum is the average vertex coordinate projected to the facet's hyperplane. Large facets may use an old centrum to avoid recomputing the centrum after each merge. In either case, the centrum is clearly below neighboring facets. See Precision issues.

    The new simplices will not be clearly convex with their neighbors and they will not satisfy the Delaunay property. They may even have a flipped orientation. Use triangulated input ('Qt') for Delaunay triangulations.

    For Delaunay triangulations with simplicial facets, the output is the same as option 'o' without the lifted coordinate. Since 'Ft' is invalid for merged Delaunay facets, option 'Ft' is not available for qdelaunay or qvoronoi. It may be used with joggled input ('QJ') or triangulated output ('Qt'), for example, rbox 10 c G 0.01 | qhull d QJ Ft

    If you add a point-at-infinity with 'Qz', it is printed after the input sites and before any centrums. It will not be used in a Delaunay facet.

    »Fv - print vertices for each facet

    The first line is the number of facets. Then each facet is printed, one per line. Each line is the number of vertices followed by the corresponding point ids. Vertices are listed in the order they were added to the hull (the last one added is the first listed).

    Option 'i' also lists the vertices, but it orients facets by reversing the order of two vertices. Option 'i' triangulates non-simplicial, 4-d and higher facets by adding vertices for the centrums.

    »Fv - print Voronoi diagram

    With qvoronoi, 'Fv' prints the Voronoi diagram or furthest-site Voronoi diagram. The first line is the number of ridges. Then each ridge is printed, one per line. The first number is the count of indices. The second pair of indices indicates a pair of input sites. The remaining indices list the corresponding ridge of Voronoi vertices. Vertex 0 is the vertex-at-infinity. It indicates an unbounded ray.

    All vertices of a ridge are coplanar. If the ridge is unbounded, add the midpoint of the pair of input sites. The unbounded ray is directed from the Voronoi vertices to infinity.

    Use 'Fo' for separating hyperplanes of outer, unbounded regions. Use 'Fi' for separating hyperplanes of inner, bounded regions.

    Option 'Fv' does not list ridges that require more than one midpoint. For example, the Voronoi diagram of cospherical points lists zero ridges (e.g., 'rbox 10 s | qvoronoi Fv Qz'). Other examples are the Voronoi diagrams of a rectangular mesh (e.g., 'rbox 27 M1,0 | qvoronoi Fv') or a point set with a rectangular corner (e.g., 'rbox P4,4,4 P4,2,4 P2,4,4 P4,4,2 10 | qvoronoi Fv'). Both cases miss unbounded rays at the corners. To determine these ridges, surround the points with a large cube (e.g., 'rbox 10 s c G2.0 | qvoronoi Fv Qz'). The cube needs to be large enough to bound all Voronoi regions of the original point set. Please report any other cases that are missed. If you can formally describe these cases or write code to handle them, please send email to qhull@qhull.org.

    »FV - print average vertex

    The average vertex is the average of all vertex coordinates. It is an interior point for halfspace intersection. The first line is the dimension and "1"; the second line is the coordinates. For example,

    qconvex FV n | qhalf Fp

    prints the extreme points of the original point set (roundoff included).

    »Fx - print extreme points (vertices) of convex hulls and Delaunay triangulations

    The first line is the number of points. The following lines give the index of the corresponding points. The first point is '0'.

    In 2-d, the extreme points (vertices) are listed in counterclockwise order (by qh_ORIENTclock in user.h).

    In 3-d and higher convex hulls, the extreme points (vertices) are sorted by index. This is the same order as option 'p' when it doesn't include coplanar or interior points.

    For Delaunay triangulations, 'Fx' lists the extreme points of the input sites (i.e., the vertices of their convex hull). The points are unordered.


    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions


    The Geometry Center Home Page

    Comments to: qhull@qhull.org
    Created: Sept. 25, 1995 --- Last modified: see top

    geometry/inst/doc/qhull/html/qh--4d.gif0000644000176200001440000001042413431000556017377 0ustar liggesusersGIF87add>JJIQbKSNJILX\ZZZXXXVVSRQEF,GGHHMHKKJIQ]\NfOOdP[bVZPW[VVWZaX_Y`W^QRRW^Y.Z.+J^_JQ\]:<HHUVDFSdZOP:G?nZKLJKIJGH)KKMQJJJQE65GFIMHZf\bTUTSSSTQTUMUW]X^Y_LKV\V\RIRW=WV\W5W;OO#5HS3SW+WZ%Z6IJHI4KQKQUq[;=N!NK,AMMOOJMx!7IBA+LL89EF*J\qK(D67?POLLHFE[ZZDD4ONM8ITTSW SSRRF1LRRQQPPONNSDRMLL5)OLJKJLIKJJLHK-IJN,MIRKJMGF!\K4JJT\DE<"LL8:78DE(DwVNP\[ZNP\YZEDMOKMCCWUUO%JMKKLKJIL|JIHINHGF\c[bZa0LLNaOQU<>ZXRSSW^V]VGWKJW^FES9T@E[!\K7LP+Q\]!KJ9;NONoUSTNOOPDDNODIHKLJUe57FE,1DYZY[aY_TTTT|WOVO$JIOPOGTJJJILLLSjVJJJKHKLDL; LS`VK@K0ONJPNTRxX;<:<BC$EDLOpAEA@QQM68DC)GF\[[IEEXWWCDI>@UUTPOOOONJdjNMMMMLMKLV[AQIHLKKIL}JIILCK:MMKP\[KxZ;=KJWG$PDPPHHTgY 7G,dd9r$ FǘܹۖI BtÉ-FbbB 'M<ʗ.Ky0’(QΞ;5JH1HX\EJMa.9f#HDI$@(ϠC}zڑѐ᜺ۧ)a4x +ѽD8 Էma-)5ZOvs6b1tȺװckW&ۙry+My=N+7}3JWУP6Ǯ]쿗e=ܶm[W݁o>re9ȉ?lٶwmg\A#:1~eXeuRZM՘At5`#@0f?Ybq6|ThyH ȣ#X}&"(#Z\7"iV!!zh #]hՕXZ8#@ -u9D~|:" W_.F̜Tpw2hx}3 ]/I;shv-*xEʧ&Ph)lC9'-s4*~[gJZ)4I {|1+-> +K'9.ꩳB3 TnghfXf,-;7$cBy Y\\B e"$┱, )fqk#3+p2eZ2⤱دsq?#p@l ga4"q81k%i1L9mv*[:Y7m4`tuX" rH ޳Eս.,ll33qHb6 ֱ6̪=t*KSvcri.۴T"MwSFZ60؁s;!ՁsFy1E?C0con3Ɯ sԌ36_IHg!K[ӸARaO DdhaV_G,ˠs0-[ZJP!SKn-ZP/ ! hoxAPC .0f0 Z Pc2͎qC.%4y51OXTQ:B(@@kq q^̋&BzFs.Vq8@E0@5/p_g"4!}!GqA!b \FPi>DN^?:(QriGH!n xN ̦/Z Ґ\te:lB0& Ħ6 'Jq}0YlF !C ,Q"q a/K0QTH%FӨ q38B9(~Rc;xO hE-jјPhgz բI bphUN(NiFs@=՟/ZPvU%E-UDuQiN7*?;x4[TuPUEcִ֚4MQLݕҘa@Jt2mh5}͐oGiv]<*Xax9ETO:3>#L`B2!^ /4ġ@x^-H]t>8*ȭ|3v-4\A=caEaG}}O$}~MU 7/c4sRp РL7'l}@o~LZqG 8(Hp =@dmsoր7' їZ 0]p{of{z7d} ؁>pp(K*PYLՀD Wx h؏  0+&7 A0PTV H0 xz@s D gpIIoP)z 99y h#zXD@ b> 1ɂHo҈ 5 3q;HXA `Fseꇌj}0 k}0/ и{pQ<8voyЊo?,A &p S{ick7,yOcW}W ɠb0/Fwɋ>6vZ:9 F0/R 1HgK4IٜtйL`V}/G IMi, Pɜ)5Mُ@ɛYO 9L}幜ٜQYIiY0 `yܹe iY0Wiُ/MɔG湜P0:9 N0R 𚃘 1Z z@ ,;ʣf(y51 U Z ` MꤻgBdYT/`:yI}v0j,nfg Jѧ٠nZcɨ0ٝyڡɞL   ~J y7'z1ʪJ**" &Z?ګ-:/Jo9 `B l|PZ)kI:+6:&:ખ`銋Ю$j`FʦzRZ w:K 1媘 HZjʚ&  {Щf e;geometry/inst/doc/qhull/html/rbox.txt0000644000176200001440000001214213431000556017430 0ustar liggesusers rbox(1) rbox(1) NAME rbox - generate point distributions for qhull SYNOPSIS Command "rbox" (w/o arguments) lists the options. DESCRIPTION rbox generates random or regular points according to the options given, and outputs the points to stdout. The points are generated in a cube, unless 's' or given. The format of the output is the following: first line contains the dimension and a comment, second line contains the num- ber of points, and the following lines contain the points, one point per line. Points are represented by their coor- dinate values. EXAMPLES rbox 10 10 random points in the unit cube centered at the origin. rbox 10 s D2 10 random points on a 2-d circle. rbox 100 W0 100 random points on the surface of a cube. rbox 1000 s D4 1000 random points on a 4-d sphere. rbox c D5 O0.5 a 5-d hypercube with one corner at the origin. rbox d D10 a 10-d diamond. rbox x 1000 r W0 100 random points on the surface of a fixed simplex rbox y D12 a 12-d simplex. rbox l 10 10 random points along a spiral rbox l 10 r 10 regular points along a spiral plus two end points rbox 1000 L10000 D4 s 1000 random points on the surface of a narrow lens. rbox c G2 d G3 a cube with coordinates +2/-2 and a diamond with Geometry Center August 10, 1998 1 rbox(1) rbox(1) coordinates +3/-3. rbox 64 M3,4 z a rotated, {0,1,2,3} x {0,1,2,3} x {0,1,2,3} lat- tice (Mesh) of integer points. rbox P0 P0 P0 P0 P0 5 copies of the origin in 3-d. Try 'rbox P0 P0 P0 P0 P0 | qhull QJ'. r 100 s Z1 G0.1 two cospherical 100-gons plus another cospherical point. 100 s Z1 a cone of points. 100 s Z1e-7 a narrow cone of points with many precision errors. OPTIONS n number of points Dn dimension n-d (default 3-d) Bn bounding box coordinates (default 0.5) l spiral distribution, available only in 3-d Ln lens distribution of radius n. May be used with 's', 'r', 'G', and 'W'. Mn,m,r lattice (Mesh) rotated by {[n,-m,0], [m,n,0], [0,0,r], ...}. Use 'Mm,n' for a rigid rotation with r = sqrt(n^2+m^2). 'M1,0' is an orthogonal lattice. For example, '27 M1,0' is {0,1,2} x {0,1,2} x {0,1,2}. s cospherical points randomly generated in a cube and projected to the unit sphere x simplicial distribution. It is fixed for option 'r'. May be used with 'W'. y simplicial distribution plus a simplex. Both 'x' and 'y' generate the same points. Wn restrict points to distance n of the surface of a sphere or a cube c add a unit cube to the output c Gm add a cube with all combinations of +m and -m to the output Geometry Center August 10, 1998 2 rbox(1) rbox(1) d add a unit diamond to the output. d Gm add a diamond made of 0, +m and -m to the output Cn,r,m add n nearly coincident points within radius r of m points Pn,m,r add point [n,m,r] to the output first. Pad coordi- nates with 0.0. n Remove the command line from the first line of out- put. On offset the data by adding n to each coordinate. t use time in seconds as the random number seed (default is command line). tn set the random number seed to n. z generate integer coordinates. Use 'Bn' to change the range. The default is 'B1e6' for six-digit coordinates. In R^4, seven-digit coordinates will overflow hyperplane normalization. Zn s restrict points to a disk about the z+ axis and the sphere (default Z1.0). Includes the opposite pole. 'Z1e-6' generates degenerate points under single precision. Zn Gm s same as Zn with an empty center (default G0.5). r s D2 generate a regular polygon r s Z1 G0.1 generate a regular cone BUGS Some combinations of arguments generate odd results. Report bugs to qhull_bug@qhull.org, other correspon- dence to qhull@qhull.org SEE ALSO qhull(1) AUTHOR C. Bradford Barber bradb@shore.net Geometry Center August 10, 1998 3 geometry/inst/doc/qhull/html/qh-eg.html0000644000176200001440000007441513431000557017620 0ustar liggesusers Examples of Qhull

    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
    To: Qhull examples: Table of Contents (please wait while loading)


    [halfspace] Examples of Qhull

    This section of the Qhull manual will introduce you to Qhull and its options. Each example is a file for viewing with Geomview. You will need to use a Unix computer with a copy of Geomview.

    If you are not running Unix, you can view pictures for some of the examples. To understand Qhull without Geomview, try the examples in Programs and Programs/input. You can also try small examples that you compute by hand. Use rbox to generate examples.

    To generate the Geomview examples, execute the shell script eg/q_eg. It uses rbox. The shell script eg/q_egtest generates test examples, and eg/q_test exercises the code. If you find yourself viewing the inside of a 3-d example, use Geomview's normalization option on the 'obscure' menu.

    Copyright © 1995-2018 C.B. Barber


    »Qhull examples: Table of Contents



    »2-d and 3-d examples

    »rbox c D3 | qconvex G >eg.01.cube

    The first example is a cube in 3-d. The color of each facet indicates its normal. For example, normal [0,0,1] along the Z axis is (r=0.5, g=0.5, b=1.0). With the 'Dn' option in rbox, you can generate hypercubes in any dimension. Above 7-d the number of intermediate facets grows rapidly. Use 'TFn' to track qconvex's progress. Note that each facet is a square that qconvex merged from coplanar triangles.

    »rbox c d G3.0 | qconvex G >eg.02.diamond.cube

    The second example is a cube plus a diamond ('d') scaled by rbox's 'G' option. In higher dimensions, diamonds are much simpler than hypercubes.

    »rbox s 100 D3 | qconvex G >eg.03.sphere

    The rbox s option generates random points and projects them to the d-sphere. All points should be on the convex hull. Notice that random points look more clustered than you might expect. You can get a smoother distribution by merging facets and printing the vertices, e.g., rbox 1000 s | qconvex A-0.95 p | qconvex G >eg.99.

    »rbox s 100 D2 | qconvex G >eg.04.circle

    In 2-d, there are many ways to generate a convex hull. One of the earliest algorithms, and one of the fastest, is the 2-d Quickhull algorithm [c.f., Preparata & Shamos '85]. It was the model for Qhull.

    »rbox 10 l | qconvex G >eg.05.spiral

    One rotation of a spiral.

    »rbox 1000 D2 | qconvex C-0.03 Qc Gapcv >eg.06.merge.square

    This demonstrates how Qhull handles precision errors. Option 'C-0.03' requires a clearly convex angle between adjacent facets. Otherwise, Qhull merges the facets.

    This is the convex hull of random points in a square. The facets have thickness because they must be outside all points and must include their vertices. The colored lines represent the original points and the spheres represent the vertices. Floating in the middle of each facet is the centrum. Each centrum is at least 0.03 below the planes of its neighbors. This guarantees that the facets are convex.

    »rbox 1000 D3 | qconvex G >eg.07.box

    Here's the same distribution but in 3-d with Qhull handling machine roundoff errors. Note the large number of facets.

    »rbox c G0.4 s 500 | qconvex G >eg.08a.cube.sphere

    The sphere is just barely poking out of the cube. Try the same distribution with randomization turned on ('Qr'). This turns Qhull into a randomized incremental algorithm. To compare Qhull and randomization, look at the number of hyperplanes created and the number of points partitioned. Don't compare CPU times since Qhull's implementation of randomization is inefficient. The number of hyperplanes and partitionings indicate the dominant costs for Qhull. With randomization, you'll notice that the number of facets created is larger than before. This is especially true as you increase the number of points. It is because the randomized algorithm builds most of the sphere before it adds the cube's vertices.

    »rbox d G0.6 s 500 | qconvex G >eg.08b.diamond.sphere

    This is a combination of the diamond distribution and the sphere.

    »rbox 100 L3 G0.5 s | qconvex G >eg.09.lens

    Each half of the lens distribution lies on a sphere of radius three. A directed search for the furthest facet below a point (e.g., qh_findbest in geom.c) may fail if started from an arbitrary facet. For example, if the first facet is on the opposite side of the lens, a directed search will report that the point is inside the convex hull even though it is outside. This problem occurs whenever the curvature of the convex hull is less than a sphere centered at the test point.

    To prevent this problem, Qhull does not use directed search all the time. When Qhull processes a point on the edge of the lens, it partitions the remaining points with an exhaustive search instead of a directed search (see qh_findbestnew in geom2.c).

    »How Qhull adds a point

    »rbox 100 s P0.5,0.5,0.5 | qconvex Ga QG0 >eg.10a.sphere.visible

    The next 4 examples show how Qhull adds a point. The point [0.5,0.5,0.5] is at one corner of the bounding box. Qhull adds a point using the beneath-beyond algorithm. First Qhull finds all of the facets that are visible from the point. Qhull will replace these facets with new facets.

    »rbox 100 s P0.5,0.5,0.5|qconvex Ga QG-0 >eg.10b.sphere.beyond

    These are the facets that are not visible from the point. Qhull will keep these facets.

    »rbox 100 s P0.5,0.5,0.5 | qconvex PG Ga QG0 >eg.10c.sphere.horizon

    These facets are the horizon facets; they border the visible facets. The inside edges are the horizon ridges. Each horizon ridge will form the base for a new facet.

    »rbox 100 s P0.5,0.5,0.5 | qconvex Ga QV0 PgG >eg.10d.sphere.cone

    This is the cone of points from the new point to the horizon facets. Try combining this image with eg.10c.sphere.horizon and eg.10a.sphere.visible.

    »rbox 100 s P0.5,0.5,0.5 | qconvex Ga >eg.10e.sphere.new

    This is the convex hull after [0.5,0.5,0.5] has been added. Note that in actual practice, the above sequence would never happen. Unlike the randomized algorithms, Qhull always processes a point that is furthest in an outside set. A point like [0.5,0.5,0.5] would be one of the first points processed.

    »rbox 100 s P0.5,0.5,0.5 | qhull Ga QV0g Q0 >eg.14.sphere.corner

    The 'QVn', 'QGn ' and 'Pdk' options define good facets for Qhull. In this case 'QV0' defines the 0'th point [0.5,0.5,0.5] as the good vertex, and 'Qg' tells Qhull to only build facets that might be part of a good facet. This technique reduces output size in low dimensions. It does not work with facet merging (turned off with 'Q0')

    »Triangulated output or joggled input

    »rbox 500 W0 | qconvex QR0 Qc Gvp >eg.15a.surface

    This is the convex hull of 500 points on the surface of a cube. Note the large, non-simplicial facet for each face. Qhull merges non-convex facets.

    If the facets were not merged, Qhull would report precision problems. For example, turn off facet merging with option 'Q0'. Qhull may report concave facets, flipped facets, or other precision errors:

    rbox 500 W0 | qhull QR0 Q0

    »rbox 500 W0 | qconvex QR0 Qt Qc Gvp >eg.15b.triangle

    Like the previous examples, this is the convex hull of 500 points on the surface of a cube. Option 'Qt' triangulates the non-simplicial facets. Triangulated output is particularly helpful for Delaunay triangulations.

    »rbox 500 W0 | qconvex QR0 QJ5e-2 Qc Gvp >eg.15c.joggle

    This is the convex hull of 500 joggled points on the surface of a cube. The option 'QJ5e-2' sets a very large joggle to make the effect visible. Notice that all of the facets are triangles. If you rotate the cube, you'll see red-yellow lines for coplanar points.

    With option 'QJ', Qhull joggles the input to avoid precision problems. It adds a small random number to each input coordinate. If a precision error occurs, it increases the joggle and tries again. It repeats this process until no precision problems occur.

    Joggled input is a simple solution to precision problems in computational geometry. Qhull can also merge facets to handle precision problems. See Merged facets or joggled input.

    »Delaunay and Voronoi diagrams

    »qdelaunay Qt <eg.data.17 GnraD2 >eg.17a.delaunay.2

    The input file, eg.data.17, consists of a square, 15 random points within the outside half of the square, and 6 co-circular points centered on the square.

    The Delaunay triangulation is the triangulation with empty circumcircles. The input for this example is unusual because it includes six co-circular points. Every triangular subset of these points has the same circumcircle. Option 'Qt' triangulates the co-circular facet.

    »qdelaunay <eg.data.17 GnraD2 >eg.17b.delaunay.2i

    This is the same example without triangulated output ('Qt'). qdelaunay merges the non-unique Delaunay triangles into a hexagon.

    »qdelaunay <eg.data.17 Ga >eg.17c.delaunay.2-3

    This is how Qhull generated both diagrams. Use Geomview's 'obscure' menu to turn off normalization, and Geomview's 'cameras' menu to turn off perspective. Then load this object with one of the previous diagrams.

    The points are lifted to a paraboloid by summing the squares of each coordinate. These are the light blue points. Then the convex hull is taken. That's what you see here. If you look up the Z-axis, you'll see that points and edges coincide.

    »qvoronoi QJ <eg.data.17 Gna >eg.17d.voronoi.2

    The Voronoi diagram is the dual of the Delaunay triangulation. Here you see the original sites and the Voronoi vertices. Notice the each vertex is equidistant from three sites. The edges indicate the Voronoi region for a site. Qhull does not draw the unbounded edges. Instead, it draws extra edges to close the unbounded Voronoi regions. You may find it helpful to enclose the input points in a square. You can compute the unbounded rays from option 'Fo'.

    Instead of triangulated output ('Qt'), this example uses joggled input ('QJ'). Normally, you should use neither 'QJ' nor 'Qt' for Voronoi diagrams.

    »qvoronoi <eg.data.17 Gna >eg.17e.voronoi.2i

    This looks the same as the previous diagrams, but take a look at the data. Run 'qvoronoi p <eg/eg.data.17'. This prints the Voronoi vertices.

    With 'QJ', there are four nearly identical Voronoi vertices within 10^-11 of the origin. Option 'QJ' joggled the input. After the joggle, the cocircular input sites are no longer cocircular. The corresponding Voronoi vertices are similar but not identical.

    This example does not use options 'Qt' or 'QJ'. The cocircular input sites define one Voronoi vertex near the origin.

    Option 'Qt' would triangulate the corresponding Delaunay region into four triangles. Each triangle is assigned the same Voronoi vertex.

    » rbox c G0.1 d | qdelaunay Gt Qz <eg.17f.delaunay.3

    This is the 3-d Delaunay triangulation of a small cube inside a prism. Since the outside ridges are transparent, it shows the interior of the outermost facets. If you slice open the triangulation with Geomview's ginsu, you will see that the innermost facet is a cube. Note the use of 'Qz' to add a point "at infinity". This avoids a degenerate input due to cospherical points.

    »rbox 10 D2 d | qdelaunay Qu G >eg.18a.furthest.2-3

    The furthest-site Voronoi diagram contains Voronoi regions for points that are furthest from an input site. It is the dual of the furthest-site Delaunay triangulation. You can determine the furthest-site Delaunay triangulation from the convex hull of the lifted points (eg.17c.delaunay.2-3). The upper convex hull (blue) generates the furthest-site Delaunay triangulation.

    »rbox 10 D2 d | qdelaunay Qu Pd2 G >eg.18b.furthest-up.2-3

    This is the upper convex hull of the preceding example. The furthest-site Delaunay triangulation is the projection of the upper convex hull back to the input points. The furthest-site Voronoi vertices are the circumcenters of the furthest-site Delaunay triangles.

    »rbox 10 D2 d | qvoronoi Qu Gv >eg.18c.furthest.2

    This shows an incomplete furthest-site Voronoi diagram. It only shows regions with more than two vertices. The regions are artificially truncated. The actual regions are unbounded. You can print the regions' vertices with 'qvoronoi Qu o'.

    Use Geomview's 'obscure' menu to turn off normalization, and Geomview's 'cameras' menu to turn off perspective. Then load this with the upper convex hull.

    »rbox 10 D3 | qvoronoi QV5 p | qconvex G >eg.19.voronoi.region.3

    This shows the Voronoi region for input site 5 of a 3-d Voronoi diagram.

    »Facet merging for imprecision

    »rbox r s 20 Z1 G0.2 | qconvex G >eg.20.cone

    There are two things unusual about this cone. One is the large flat disk at one end and the other is the rectangles about the middle. That's how the points were generated, and if those points were exact, this is the correct hull. But rbox used floating point arithmetic to generate the data. So the precise convex hull should have been triangles instead of rectangles. By requiring convexity, Qhull has recovered the original design.

    »rbox 200 s | qhull Q0 R0.01 Gav Po >eg.21a.roundoff.errors

    This is the convex hull of 200 cospherical points with precision errors ignored ('Q0'). To demonstrate the effect of roundoff error, we've added a random perturbation ('R0.01') to every distance and hyperplane calculation. Qhull, like all other convex hull algorithms with floating point arithmetic, makes inconsistent decisions and generates wildly wrong results. In this case, one or more facets are flipped over. These facets have the wrong color. You can also turn on 'normals' in Geomview's appearances menu and turn off 'facing normals'. There should be some white lines pointing in the wrong direction. These correspond to flipped facets.

    Different machines may not produce this picture. If your machine generated a long error message, decrease the number of points or the random perturbation ('R0.01'). If it did not report flipped facets, increase the number of points or perturbation.

    »rbox 200 s | qconvex Qc R0.01 Gpav >eg.21b.roundoff.fixed

    Qhull handles the random perturbations and returns an imprecise sphere. In this case, the output is a weak approximation to the points. This is because a random perturbation of 'R0.01 ' is equivalent to losing all but 1.8 digits of precision. The outer planes float above the points because Qhull needs to allow for the maximum roundoff error.

    If you start with a smaller random perturbation, you can use joggle ('QJn') to avoid precision problems. You need to set n significantly larger than the random perturbation. For example, try 'rbox 200 s | qconvex Qc R1e-4 QJ1e-1'.

    »rbox 1000 s| qconvex C0.01 Qc Gcrp >eg.22a.merge.sphere.01

    »rbox 1000 s| qconvex C-0.01 Qc Gcrp >eg.22b.merge.sphere.-01

    »rbox 1000 s| qconvex C0.05 Qc Gcrpv >eg.22c.merge.sphere.05

    »rbox 1000 s| qconvex C-0.05 Qc Gcrpv >eg.22d.merge.sphere.-05

    The next four examples compare post-merging and pre-merging ('Cn' vs. 'C-n'). Qhull uses '-' as a flag to indicate pre-merging.

    Post-merging happens after the convex hull is built. During post-merging, Qhull repeatedly merges an independent set of non-convex facets. For a given set of parameters, the result is about as good as one can hope for.

    Pre-merging does the same thing as post-merging, except that it happens after adding each point to the convex hull. With pre-merging, Qhull guarantees a convex hull, but the facets are wider than those from post-merging. If a pre-merge option is not specified, Qhull handles machine round-off errors.

    You may see coplanar points appearing slightly outside the facets of the last example. This is becomes Geomview moves line segments forward toward the viewer. You can avoid the effect by setting 'lines closer' to '0' in Geomview's camera menu.

    »rbox 1000 | qconvex s Gcprvah C0.1 Qc >eg.23.merge.cube

    Here's the 3-d imprecise cube with all of the Geomview options. There's spheres for the vertices, radii for the coplanar points, dots for the interior points, hyperplane intersections, centrums, and inner and outer planes. The radii are shorter than the spheres because this uses post-merging ('C0.1') instead of pre-merging.

    »4-d objects

    With Qhull and Geomview you can develop an intuitive sense of 4-d surfaces. When you get into trouble, think of viewing the surface of a 3-d sphere in a 2-d plane.

    »rbox 5000 D4 | qconvex s GD0v Pd0:0.5 C-0.02 C0.1 >eg.24.merge.cube.4d-in-3d

    Here's one facet of the imprecise cube in 4-d. It's projected into 3-d (the 'GDn' option drops dimension n). Each ridge consists of two triangles between this facet and the neighboring facet. In this case, Geomview displays the topological ridges, i.e., as triangles between three vertices. That is why the cube looks lopsided.

    »rbox 5000 D4 | qconvex s C-0.02 C0.1 Gh >eg.30.4d.merge.cube

    Here is the equivalent in 4-d of the imprecise square and imprecise cube. It's the imprecise convex hull of 5000 random points in a hypercube. It's a full 4-d object so Geomview's ginsu does not work. If you view it in Geomview, you'll be inside the hypercube. To view 4-d objects directly, either load the 4dview module or the ndview module. For 4dview, you must have started Geomview in the same directory as the object. For ndview, initialize a set of windows with the prefab menu, and load the object through Geomview. The 4dview module includes an option for slicing along any hyperplane. If you do this in the x, y, or z plane, you'll see the inside of a hypercube.

    The 'Gh' option prints the geometric intersections between adjacent facets. Note the strong convexity constraint for post-merging ('C0.1'). It deletes the small facets.

    »rbox 20 D3 | qdelaunay G >eg.31.4d.delaunay

    The Delaunay triangulation of 3-d sites corresponds to a 4-d convex hull. You can't see 4-d directly but each facet is a 3-d object that you can project to 3-d. This is exactly the same as projecting a 2-d facet of a soccer ball onto a plane.

    Here we see all of the facets together. You can use Geomview's ndview to look at the object from several directions. Try turning on edges in the appearance menu. You'll notice that some edges seem to disappear. That's because the object is actually two sets of overlapping facets.

    You can slice the object apart using Geomview's 4dview. With 4dview, try slicing along the w axis to get a single set of facets and then slice along the x axis to look inside. Another interesting picture is to slice away the equator in the w dimension.

    »rbox 30 s D4 | qconvex s G Pd0d1d2D3

    This is the positive octant of the convex hull of 30 4-d points. When looking at 4-d, it's easier to look at just a few facets at once. If you picked a facet that was directly above you, then that facet looks exactly the same in 3-d as it looks in 4-d. If you pick several facets, then you need to imagine that the space of a facet is rotated relative to its neighbors. Try Geomview's ndview on this example.

    »Halfspace intersections

    »rbox 10 r s Z1 G0.3 | qconvex G >eg.33a.cone

    »rbox 10 r s Z1 G0.3 | qconvex FV n | qhalf G >eg.33b.cone.dual

    »rbox 10 r s Z1 G0.3 | qconvex FV n | qhalf Fp | qconvex G >eg.33c.cone.halfspace

    These examples illustrate halfspace intersection. The first picture is the convex hull of two 20-gons plus an apex. The second picture is the dual of the first. Try loading both at once. Each vertex of the second picture corresponds to a facet or halfspace of the first. The vertices with four edges correspond to a facet with four neighbors. Similarly the facets correspond to vertices. A facet's normal coefficients divided by its negative offset is the vertice's coordinates. The coordinates are the intersection of the original halfspaces.

    The third picture shows how Qhull can go back and forth between equivalent representations. It starts with a cone, generates the halfspaces that define each facet of the cone, and then intersects these halfspaces. Except for roundoff error, the third picture is a duplicate of the first.


    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
    To: Qhull examples: Table of Contents (please wait while loading)


    The Geometry Center Home Page

    Comments to: qhull@qhull.org
    Created: Sept. 25, 1995 --- Last modified: see top

    geometry/inst/doc/qhull/html/qvoronoi.html0000644000176200001440000006703613431000557020474 0ustar liggesusers qvoronoi -- Voronoi diagram Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
    To: synopsis • input • outputs • controls • graphics • notes • conventions • options

    [voronoi]qvoronoi -- Voronoi diagram

    The Voronoi diagram is the nearest-neighbor map for a set of points. Each region contains those points that are nearer one input site than any other input site. It has many useful properties and applications. See the survey article by Aurenhammer ['91] and the detailed introduction by O'Rourke ['94]. The Voronoi diagram is the dual of the Delaunay triangulation.

    Example: rbox 10 D3 | qvoronoi s o TO result
    Compute the 3-d Voronoi diagram of 10 random points. Write a summary to the console and the Voronoi vertices and regions to 'result'. The first vertex of the result indicates unbounded regions.
     
    Example: rbox r y c G0.1 D2 | qvoronoi s o TO result
    Compute the 2-d Voronoi diagram of a triangle and a small square. Write a summary to the console and Voronoi vertices and regions to 'result'. Report a single Voronoi vertex for cocircular input sites. The first vertex of the result indicates unbounded regions. The origin is the Voronoi vertex for the square.
     
    Example: rbox r y c G0.1 D2 | qvoronoi Fv TO result
    Compute the 2-d Voronoi diagram of a triangle and a small square. Write a summary to the console and the Voronoi ridges to 'result'. Each ridge is the perpendicular bisector of a pair of input sites. Vertex "0" indicates unbounded ridges. Vertex "8" is the Voronoi vertex for the square.
     
    Example: rbox r y c G0.1 D2 | qvoronoi Fi
    Print the bounded, separating hyperplanes for the 2-d Voronoi diagram of a triangle and a small square. Note the four hyperplanes (i.e., lines) for Voronoi vertex "8". It is at the origin.

    Qhull computes the Voronoi diagram via the Delaunay triangulation. Each Voronoi vertex is the circumcenter of a facet of the Delaunay triangulation. Each Voronoi region corresponds to a vertex (i.e., input site) of the Delaunay triangulation.

    Qhull outputs the Voronoi vertices for each Voronoi region. With option 'Fv', it lists all ridges of the Voronoi diagram with the corresponding pairs of input sites. With options 'Fi' and 'Fo', it lists the bounded and unbounded separating hyperplanes. You can also output a single Voronoi region for further processing [see graphics].

    Use option 'Qz' if the input is circular, cospherical, or nearly so. It improves precision by adding a point "at infinity," above the corresponding paraboloid.

    See Qhull FAQ - Delaunay and Voronoi diagram questions.

    The 'qvonoroi' program is equivalent to 'qhull v Qbb' in 2-d to 3-d, and 'qhull v Qbb Qx' in 4-d and higher. It disables the following Qhull options: d n v Qbb QbB Qf Qg Qm Qr QR Qv Qx Qz TR E V Fa FA FC FD FS Ft FV Gt Q0,etc.

    Copyright © 1995-2018 C.B. Barber

    Voronoi image by KOOK Architecture, Silvan Oesterle and Michael Knauss.


    »qvoronoi synopsis

    qvoronoi- compute the Voronoi diagram.
        input (stdin): dimension, number of points, point coordinates
        comments start with a non-numeric character
    
    options (qh-voron.htm):
        Qu   - compute furthest-site Voronoi diagram
        Tv   - verify result: structure, convexity, and in-circle test
        .    - concise list of all options
        -    - one-line description of all options
    
    output options (subset):
        s    - summary of results (default)
        p    - Voronoi vertices
        o    - OFF file format (dim, Voronoi vertices, and Voronoi regions)
        FN   - count and Voronoi vertices for each Voronoi region
        Fv   - Voronoi diagram as Voronoi vertices between adjacent input sites
        Fi   - separating hyperplanes for bounded regions, 'Fo' for unbounded
        G    - Geomview output (2-d only)
        QVn  - Voronoi vertices for input point n, -n if not
        TO file- output results to file, may be enclosed in single quotes
    
    examples:
    rbox c P0 D2 | qvoronoi s o         rbox c P0 D2 | qvoronoi Fi
    rbox c P0 D2 | qvoronoi Fo          rbox c P0 D2 | qvoronoi Fv
    rbox c P0 D2 | qvoronoi s Qu Fv     rbox c P0 D2 | qvoronoi Qu Fo
    rbox c G1 d D2 | qvoronoi s p       rbox c P0 D2 | qvoronoi s Fv QV0
    

    »qvoronoi input

    The input data on stdin consists of:
    • dimension
    • number of points
    • point coordinates

    Use I/O redirection (e.g., qvoronoi < data.txt), a pipe (e.g., rbox 10 | qvoronoi), or the 'TI' option (e.g., qvoronoi TI data.txt).

    For example, this is four cocircular points inside a square. Their Voronoi diagram has nine vertices and eight regions. Notice the Voronoi vertex at the origin, and the Voronoi vertices (on each axis) for the four sides of the square.

    rbox s 4 W0 c G1 D2 > data
    2 RBOX s 4 W0 c D2
    8
    -0.4941988586954018 -0.07594397977563715
    -0.06448037284989526 0.4958248496365813
    0.4911154367094632 0.09383830681375946
    -0.348353580869097 -0.3586778257652367
        -1     -1
        -1      1
         1     -1
         1      1
    

    qvoronoi s p < data

    
    Voronoi diagram by the convex hull of 8 points in 3-d:
    
      Number of Voronoi regions: 8
      Number of Voronoi vertices: 9
      Number of non-simplicial Voronoi vertices: 1
    
    Statistics for: RBOX s 4 W0 c D2 | QVORONOI s p
    
      Number of points processed: 8
      Number of hyperplanes created: 18
      Number of facets in hull: 10
      Number of distance tests for qhull: 33
      Number of merged facets: 2
      Number of distance tests for merging: 102
      CPU seconds to compute hull (after input): 0.094
    
    2
    9
    4.217546450968612e-17 1.735507986399734
    -8.402566836762659e-17 -1.364368854147395
    0.3447488772716865 -0.6395484723719818
    1.719446929853986 2.136555906154247e-17
    0.4967882915039657 0.68662371396699
    -1.729928876283549 1.343733067524222e-17
    -0.8906163241424728 -0.4594150543829102
    -0.6656840313875723 0.5003013793414868
    -7.318364664277155e-19 -1.188217818408333e-16
    

    » qvoronoi outputs

    These options control the output of Voronoi diagrams.

     
    Voronoi vertices
    p
    print the coordinates of the Voronoi vertices. The first line is the dimension. The second line is the number of vertices. Each remaining line is a Voronoi vertex.
    Fn
    list the neighboring Voronoi vertices for each Voronoi vertex. The first line is the number of Voronoi vertices. Each remaining line starts with the number of neighboring vertices. Negative vertices (e.g., -1) indicate vertices outside of the Voronoi diagram. In the circle-in-box example, the Voronoi vertex at the origin has four neighbors.
    FN
    list the Voronoi vertices for each Voronoi region. The first line is the number of Voronoi regions. Each remaining line starts with the number of Voronoi vertices. Negative indices (e.g., -1) indicate vertices outside of the Voronoi diagram. In the circle-in-box example, the four bounded regions are defined by four Voronoi vertices.
     
     
    Voronoi regions
    o
    print the Voronoi regions in OFF format. The first line is the dimension. The second line is the number of vertices, the number of input sites, and "1". The third line represents the vertex-at-infinity. Its coordinates are "-10.101". The next lines are the coordinates of the Voronoi vertices. Each remaining line starts with the number of Voronoi vertices in a Voronoi region. In 2-d, the vertices are listed in adjacency order (unoriented). In 3-d and higher, the vertices are listed in numeric order. In the circle-in-square example, each bounded region includes the Voronoi vertex at the origin. Lines consisting of 0 indicate coplanar input sites or 'Qz'.
    Fi
    print separating hyperplanes for inner, bounded Voronoi regions. The first number is the number of separating hyperplanes. Each remaining line starts with 3+dim. The next two numbers are adjacent input sites. The next dim numbers are the coefficients of the separating hyperplane. The last number is its offset. Use 'Tv' to verify that the hyperplanes are perpendicular bisectors. It will list relevant statistics to stderr.
    Fo
    print separating hyperplanes for outer, unbounded Voronoi regions. The first number is the number of separating hyperplanes. Each remaining line starts with 3+dim. The next two numbers are adjacent input sites on the convex hull. The next dim numbers are the coefficients of the separating hyperplane. The last number is its offset. Use 'Tv' to verify that the hyperplanes are perpendicular bisectors. It will list relevant statistics to stderr,
     
     
    Input sites
    Fv
    list ridges of Voronoi vertices for pairs of input sites. The first line is the number of ridges. Each remaining line starts with two plus the number of Voronoi vertices in the ridge. The next two numbers are two adjacent input sites. The remaining numbers list the Voronoi vertices. As with option 'o', a 0 indicates the vertex-at-infinity and an unbounded, separating hyperplane. The perpendicular bisector (separating hyperplane) of the input sites is a flat through these vertices. In the circle-in-square example, the ridge for each edge of the square is unbounded.
    Fc
    list coincident input sites for each Voronoi vertex. The first line is the number of vertices. The remaining lines start with the number of coincident sites and deleted vertices. Deleted vertices indicate highly degenerate input (see'Fs'). A coincident site is assigned to one Voronoi vertex. Do not use 'QJ' with 'Fc'; the joggle will separate coincident sites.
    FP
    print coincident input sites with distance to nearest site (i.e., vertex). The first line is the number of coincident sites. Each remaining line starts with the point ID of an input site, followed by the point ID of a coincident point, its vertex, and distance. Includes deleted vertices which indicate highly degenerate input (see'Fs'). Do not use 'QJ' with 'FP'; the joggle will separate coincident sites.
     
     
    General
    s
    print summary of the Voronoi diagram. Use 'Fs' for numeric data.
    i
    list input sites for each Delaunay region. Use option 'Pp' to avoid the warning. The first line is the number of regions. The remaining lines list the input sites for each region. The regions are oriented. In the circle-in-square example, the cocircular region has four edges. In 3-d and higher, report cospherical sites by adding extra points.
    G
    Geomview output for 2-d Voronoi diagrams.

    » qvoronoi controls

    These options provide additional control:

    Qu
    compute the furthest-site Voronoi diagram.
    QVn
    select Voronoi vertices for input site n
    Qz
    add a point above the paraboloid to reduce precision errors. Use it for nearly cocircular/cospherical input (e.g., 'rbox c | qvoronoi Qz').
    Tv
    verify result
    TI file
    input data from file. The filename may not use spaces or quotes.
    TO file
    output results to file. Use single quotes if the filename contains spaces (e.g., TO 'file with spaces.txt'
    TFn
    report progress after constructing n facets
    PDk:1
    include upper and lower facets in the output. Set k to the last dimension (e.g., 'PD2:1' for 2-d inputs).
    f
    facet dump. Print the data structure for each facet (i.e., Voronoi vertex).

    » qvoronoi graphics

    In 2-d, Geomview output ('G') displays a Voronoi diagram with extra edges to close the unbounded Voronoi regions. To view the unbounded rays, enclose the input points in a square.

    You can also view individual Voronoi regions in 3-d. To view the Voronoi region for site 3 in Geomview, execute

    qvoronoi <data QV3 p | qconvex s G >output

    The qvoronoi command returns the Voronoi vertices for input site 3. The qconvex command computes their convex hull. This is the Voronoi region for input site 3. Its hyperplane normals (qconvex 'n') are the same as the separating hyperplanes from options 'Fi' and 'Fo' (up to roundoff error).

    See the Delaunay and Voronoi examples for 2-d and 3-d examples. Turn off normalization (on Geomview's 'obscure' menu) when comparing the Voronoi diagram with the corresponding Delaunay triangulation.

    »qvoronoi notes

    You can simplify the Voronoi diagram by enclosing the input sites in a large square or cube. This is particularly recommended for cocircular or cospherical input data.

    See Voronoi graphics for computing the convex hull of a Voronoi region.

    Voronoi diagrams do not include facets that are coplanar with the convex hull of the input sites. A facet is coplanar if the last coefficient of its normal is nearly zero (see qh_ZEROdelaunay).

    Unbounded regions can be confusing. For example, 'rbox c | qvoronoi Qz o' produces the Voronoi regions for the vertices of a cube centered at the origin. All regions are unbounded. The output is

    3
    2 9 1
    -10.101 -10.101 -10.101
         0      0      0
    2 0 1
    2 0 1
    2 0 1
    2 0 1
    2 0 1
    2 0 1
    2 0 1
    2 0 1
    0
    

    The first line is the dimension. The second line is the number of vertices and the number of regions. There is one region per input point plus a region for the point-at-infinity added by option 'Qz'. The next two lines lists the Voronoi vertices. The first vertex is the infinity vertex. It is indicate by the coordinates -10.101. The second vertex is the origin. The next nine lines list the regions. Each region lists two vertices -- the infinity vertex and the origin. The last line is "0" because no region is associated with the point-at-infinity. A "0" would also be listed for nearly incident input sites.

    To use option 'Fv', add an interior point. For example,

    rbox c P0 | qvoronoi Fv
    20
    5 0 7 1 3 5
    5 0 3 1 4 5
    5 0 5 1 2 3
    5 0 1 1 2 4
    5 0 6 2 3 6
    5 0 2 2 4 6
    5 0 4 4 5 6
    5 0 8 5 3 6
    5 1 2 0 2 4
    5 1 3 0 1 4
    5 1 5 0 1 2
    5 2 4 0 4 6
    5 2 6 0 2 6
    5 3 4 0 4 5
    5 3 7 0 1 5
    5 4 8 0 6 5
    5 5 6 0 2 3
    5 5 7 0 1 3
    5 6 8 0 6 3
    5 7 8 0 3 5
    

    The output consists of 20 ridges and each ridge lists a pair of input sites and a triplet of Voronoi vertices. The first eight ridges connect the origin ('P0'). The remainder list the edges of the cube. Each edge generates an unbounded ray through the midpoint. The corresponding separating planes ('Fo') follow each pair of coordinate axes.

    Options 'Qt' (triangulated output) and 'QJ' (joggled input) are deprecated. They may produce unexpected results. If you use these options, cocircular and cospherical input sites will produce duplicate or nearly duplicate Voronoi vertices. See also Merged facets or joggled input.

    »qvoronoi conventions

    The following terminology is used for Voronoi diagrams in Qhull. The underlying structure is a Delaunay triangulation from a convex hull in one higher dimension. Facets of the Delaunay triangulation correspond to vertices of the Voronoi diagram. Vertices of the Delaunay triangulation correspond to input sites. They also correspond to regions of the Voronoi diagram. See convex hull conventions, Delaunay conventions, and Qhull's data structures.

    • input site - a point in the input (one dimension lower than a point on the convex hull)
    • point - a point has d+1 coordinates. The last coordinate is the sum of the squares of the input site's coordinates
    • coplanar point - a nearly incident input site
    • vertex - a point on the paraboloid. It corresponds to a unique input site.
    • point-at-infinity - a point added above the paraboloid by option 'Qz'
    • Delaunay facet - a lower facet of the paraboloid. The last coefficient of its normal is clearly negative.
    • Voronoi vertex - the circumcenter of a Delaunay facet
    • Voronoi region - the Voronoi vertices for an input site. The region of Euclidean space nearest to an input site.
    • Voronoi diagram - the graph of the Voronoi regions. It includes the ridges (i.e., edges) between the regions.
    • vertex-at-infinity - the Voronoi vertex that indicates unbounded Voronoi regions in 'o' output format. Its coordinates are -10.101.
    • good facet - a Voronoi vertex with optional restrictions by 'QVn', etc.

    »qvoronoi options

    qvoronoi- compute the Voronoi diagram
        http://www.qhull.org
    
    input (stdin):
        first lines: dimension and number of points (or vice-versa).
        other lines: point coordinates, best if one point per line
        comments:    start with a non-numeric character
    
    options:
        Qu   - compute furthest-site Voronoi diagram
    
    Qhull control options:
        QJn  - randomly joggle input in range [-n,n]
        Qs   - search all points for the initial simplex
        Qz   - add point-at-infinity to Voronoi diagram
        QGn  - Voronoi vertices if visible from point n, -n if not
        QVn  - Voronoi vertices for input point n, -n if not
    
    Trace options:
        T4   - trace at level n, 4=all, 5=mem/gauss, -1= events
        Tc   - check frequently during execution
        Ts   - statistics
        Tv   - verify result: structure, convexity, and in-circle test
        Tz   - send all output to stdout
        TFn  - report summary when n or more facets created
        TI file - input data from file, no spaces or single quotes
        TO file - output results to file, may be enclosed in single quotes
        TPn  - turn on tracing when point n added to hull
         TMn - turn on tracing at merge n
         TWn - trace merge facets when width > n
        TVn  - stop qhull after adding point n, -n for before (see TCn)
         TCn - stop qhull after building cone for point n (see TVn)
    
    Precision options:
        Cn   - radius of centrum (roundoff added).  Merge facets if non-convex
         An  - cosine of maximum angle.  Merge facets if cosine > n or non-convex
               C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge
        Rn   - randomly perturb computations by a factor of [1-n,1+n]
        Wn   - min facet width for non-coincident point (before roundoff)
    
    Output formats (may be combined; if none, produces a summary to stdout):
        s    - summary to stderr
        p    - Voronoi vertices
        o    - OFF format (dim, Voronoi vertices, and Voronoi regions)
        i    - Delaunay regions (use 'Pp' to avoid warning)
        f    - facet dump
    
    More formats:
        Fc   - count plus coincident points (by Voronoi vertex)
        Fd   - use cdd format for input (homogeneous with offset first)
        FD   - use cdd format for output (offset first)
        FF   - facet dump without ridges
        Fi   - separating hyperplanes for bounded Voronoi regions
        FI   - ID for each Voronoi vertex
        Fm   - merge count for each Voronoi vertex (511 max)
        Fn   - count plus neighboring Voronoi vertices for each Voronoi vertex
        FN   - count and Voronoi vertices for each Voronoi region
        Fo   - separating hyperplanes for unbounded Voronoi regions
        FO   - options and precision constants
        FP   - nearest point and distance for each coincident point
        FQ   - command used for qvoronoi
        Fs   - summary: #int (8), dimension, #points, tot vertices, tot facets,
                        for output: #Voronoi regions, #Voronoi vertices,
                                    #coincident points, #non-simplicial regions
                        #real (2), max outer plane and min vertex
        Fv   - Voronoi diagram as Voronoi vertices between adjacent input sites
        Fx   - extreme points of Delaunay triangulation (on convex hull)
    
    Geomview options (2-d only)
        Ga   - all points as dots
         Gp  -  coplanar points and vertices as radii
         Gv  -  vertices as spheres
        Gi   - inner planes only
         Gn  -  no planes
         Go  -  outer planes only
        Gc   - centrums
        Gh   - hyperplane intersections
        Gr   - ridges
        GDn  - drop dimension n in 3-d and 4-d output
    
    Print options:
        PAn  - keep n largest Voronoi vertices by 'area'
        Pdk:n - drop facet if normal[k] <= n (default 0.0)
        PDk:n - drop facet if normal[k] >= n
        Pg   - print good Voronoi vertices (needs 'QGn' or 'QVn')
        PFn  - keep Voronoi vertices whose 'area' is at least n
        PG   - print neighbors of good Voronoi vertices
        PMn  - keep n Voronoi vertices with most merges
        Po   - force output.  If error, output neighborhood of facet
        Pp   - do not report precision problems
    
        .    - list of all options
        -    - one line descriptions of all options
    

    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
    To: synopsis • input • outputs • controls • graphics • notes • conventions • options


    The Geometry Center Home Page

    Comments to: qhull@qhull.org
    Created: Sept. 25, 1995 --- Last modified: see top

    geometry/inst/doc/qhull/html/qh--dt.gif0000644000176200001440000000727413431000556017510 0ustar liggesusersGIF87add^+!6o$08%BL&7Go$Z5 #4hWIC"H4i9;QW5 Fw;<5!aU2qIc<%b<$WWLNM7YXo].XRPZ6pD_J*_:$ SwHJ [_>F=D-QQEwR<::<:AHbYN0G<@AP&FvW73TN2V1#C&IHHR5U )5x O-WW?!DY g&!2-+BuP('z="Nh[L!O &H]I[J+(pBO&AO%,<H)F'PL:MN,(EA@'zNVNT&9CG*FA--.IJ89CuDM1 pIKeLMmS0RUSXXb=$WXjOAz;McuUCn@v!NhCTTL5(_2;55;;<<==??A|A==@|@AzABxBDtD$$GnGHlHIjIN`NU1O^OP\PQZQRXRSVSTTTD$*PYF_> 7_> EQBL69O`FeIT!U[\: f1 V>EyJN+'Q3!/,R1M4&H*b?AO&n2!H*[8"I9.W:Y"F%R-P%!\Vd?$MM!]EYK5f<.G09;?#3@%FoHJgL R0u@UUb?%c=&\CVV^f,ddk H*\ȰÇ#JHŋ3jȱǏ CIɓ(S܈ʗ U%J%̛RAD"j}|iώ H&*tp$KJ'+D%ML6TċV) 99ilO6R2A+1ڮCu+e*Xb bp@ώ#;fS ښ*7Ĉ% 9ۍ&)Z q)L%ңk+G.z]ܲ .b&TCZdܶ?0uNuaXy-_NJ.cD3Mc`,-igkDЦNڵs8_(hۄ4z|bFwѨU5t934 ;lr]:!.( 1 8MdpdF{vcT6@Z\&tlaX&XUd&_PS `pAYxb61ġd8Y É! |pACN4`wh$.۸Î*Z8H 'L7(Xs"> $9DN]9 H G0 L:;HJ $w* dbJ/[2." (𒄯 ^up DcB4Ѷ|:j;/`(14ȯc0FR1ͧOkkx+ANI< 24w`BlB2QoŽ쬠LS rdEǬ JЈL65tG'zX"q]6FsT("P+r'&C+=ZFNp˛1` (3P)d ̠C$ A _څ߆CM* R(hRC % =k OJ#hR-L|N@ DhE-t^+A8jpbl ΰ M< h2Gb0j^@@0E5y3@&` ЀthV@7 X@nR8`E0{@ t @F<` DRQQ8"!q#B6;2 4Ad'$`sPj1R3Gr"ɍR =በ e(I g $PD$QN Х@pi qk&( 'Lw0b0'° CL! a1.H*b!N G,4ƶD8`wL25Q2xM@!! O,`eh1P,"YZAtu- jh%c0"v0<@/!ȠH1X$ϻ)0'l<$`Ma0>Tm,8*&X`@H!X؏5)Y_шlp@" )@r=K a"iZF h,٨f l ]XC8@x '%paH 6` D'&+|@*I -p%$xM'1)$ ڑT^n66<6oxB](#h9 BS_.8U0dAwP %PT E#[i!zXᐍ#smP0(-*Eg@ zq0@|D2Cbj0dHP5/h Pv(dp~Fև*A <#]0S -CU.  >}0xj~ )4 j #l_ŀ IaDC 7CȀe>pz 0U-Ph _$ *WPXp1<X 0t$0]h#S  8r|(/P*<2l 0a V Є >/PQXAЅP@!1k(N~7Qgp 5P 'A 7|#p1hC 4Y5P | /@e!  iseq8|c P v<8yp KY 5 a 5UW9A|0c061F ȀK#@t):rٙ;geometry/inst/doc/qhull/html/qdelau_f.html0000644000176200001440000004357513431000557020402 0ustar liggesusers qdelaunay Qu -- furthest-site Delaunay triangulation Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
    To: synopsis • input • outputs • controls • graphics • notes • conventions • options

    [delaunay]qdelaunay Qu -- furthest-site Delaunay triangulation

    The furthest-site Delaunay triangulation corresponds to the upper facets of the Delaunay construction. Its vertices are the extreme points of the input sites. It is the dual of the furthest-site Voronoi diagram.

    Example: rbox 10 D2 | qdelaunay Qu Qt s i TO result
    Compute the 2-d, furthest-site Delaunay triangulation of 10 random points. Triangulate the output. Write a summary to the console and the regions to 'result'.
     
    Example: rbox 10 D2 | qdelaunay Qu QJ s i TO result
    Compute the 2-d, furthest-site Delaunay triangulation of 10 random points. Joggle the input to guarantee triangular output. Write a summary to the console and the regions to 'result'.
     
    Example: rbox r y c G1 D2 | qdelaunay Qu s Fv TO result
    Compute the 2-d, furthest-site Delaunay triangulation of a triangle inside a square. Write a summary to the console and unoriented regions to 'result'. Merge regions for cocircular input sites (e.g., the square). The square is the only furthest-site Delaunay region.

    As with the Delaunay triangulation, Qhull computes the furthest-site Delaunay triangulation by lifting the input sites to a paraboloid. The lower facets correspond to the Delaunay triangulation while the upper facets correspond to the furthest-site triangulation. Neither triangulation includes "vertical" facets (i.e., facets whose last hyperplane coefficient is nearly zero). Vertical facets correspond to input sites that are coplanar to the convex hull of the input. An example is points on the boundary of a lattice.

    By default, qdelaunay merges cocircular and cospherical regions. For example, the furthest-site Delaunay triangulation of a square inside a diamond ('rbox D2 c d G4 | qdelaunay Qu') consists of one region (the diamond).

    If you use 'Qt' (triangulated output), all furthest-site Delaunay regions will be simplicial (e.g., triangles in 2-d). Some regions may be degenerate and have zero area.

    If you use 'QJ' (joggled input), all furthest-site Delaunay regions will be simplicial (e.g., triangles in 2-d). Joggled input is less accurate than triangulated output ('Qt'). See Merged facets or joggled input.

    The output for 3-d, furthest-site Delaunay triangulations may be confusing if the input contains cospherical data. See the FAQ item Why are there extra points in a 4-d or higher convex hull? Avoid these problems with triangulated output ('Qt') or joggled input ('QJ').

    The 'qdelaunay' program is equivalent to 'qhull d Qbb' in 2-d to 3-d, and 'qhull d Qbb Qx' in 4-d and higher. It disables the following Qhull options: d n v H U Qb QB Qc Qf Qg Qi Qm Qr QR Qv Qx TR E V FC Fi Fo Fp FV Q0,etc.

    Copyright © 1995-2018 C.B. Barber


    »furthest-site qdelaunay synopsis

    See qdelaunay synopsis. The same program is used for both constructions. Use option 'Qu' for furthest-site Delaunay triangulations.

    »furthest-site qdelaunay input

    The input data on stdin consists of:

    • dimension
    • number of points
    • point coordinates

    Use I/O redirection (e.g., qdelaunay Qu < data.txt), a pipe (e.g., rbox 10 | qdelaunay Qu), or the 'TI' option (e.g., qdelaunay Qu TI data.txt).

    For example, this is a square containing four random points. Its furthest-site Delaunay triangulation contains one square.

    rbox c 4 D2 > data
    2 RBOX c 4 D2
    8
    -0.4999921736307369 -0.3684622117955817
    0.2556053225468894 -0.0413498678629751
    0.0327672376602583 -0.2810408135699488
    -0.452955383763607 0.17886471718444
      -0.5   -0.5
      -0.5    0.5
       0.5   -0.5
       0.5    0.5
    

    qdelaunay Qu i < data

    
    Furthest-site Delaunay triangulation by the convex hull of 8 points in 3-d:
    
      Number of input sites: 8
      Number of Delaunay regions: 1
      Number of non-simplicial Delaunay regions: 1
    
    Statistics for: RBOX c 4 D2 | QDELAUNAY s Qu i
    
      Number of points processed: 8
      Number of hyperplanes created: 20
      Number of facets in hull: 11
      Number of distance tests for qhull: 34
      Number of merged facets: 1
      Number of distance tests for merging: 107
      CPU seconds to compute hull (after input): 0.02
    
    1
    7 6 4 5
    

    »furthest-site qdelaunay outputs

    These options control the output of furthest-site Delaunay triangulations:

    furthest-site Delaunay regions
    i
    list input sites for each furthest-site Delaunay region. The first line is the number of regions. The remaining lines list the input sites for each region. The regions are oriented. In 3-d and higher, report cospherical sites by adding extra points. For the points-in-square example, the square is the only furthest-site Delaunay region.
    Fv
    list input sites for each furthest-site Delaunay region. The first line is the number of regions. Each remaining line starts with the number of input sites. The regions are unoriented. For the points-in-square example, the square is the only furthest-site Delaunay region.
    Ft
    print a triangulation of the furthest-site Delaunay regions in OFF format. The first line is the dimension. The second line is the number of input sites and added points, followed by the number of simplices and the number of ridges. The input coordinates are next, followed by the centrum coordinates. There is one centrum for each non-simplicial furthest-site Delaunay region. Each remaining line starts with dimension+1. The simplices are oriented. For the points-in-square example, the square has a centrum at the origin. It splits the square into four triangular regions.
    Fn
    list neighboring regions for each furthest-site Delaunay region. The first line is the number of regions. Each remaining line starts with the number of neighboring regions. Negative indices (e.g., -1) indicate regions outside of the furthest-site Delaunay triangulation. For the points-in-square example, the four neighboring regions are outside of the triangulation. They belong to the regular Delaunay triangulation.
    FN
    list the furthest-site Delaunay regions for each input site. The first line is the total number of input sites. Each remaining line starts with the number of furthest-site Delaunay regions. Negative indices (e.g., -1) indicate regions outside of the furthest-site Delaunay triangulation. For the points-in-square example, the four random points belong to no region while the square's vertices belong to region 0 and three regions outside of the furthest-site Delaunay triangulation.
    Fa
    print area for each furthest-site Delaunay region. The first line is the number of regions. The areas follow, one line per region. For the points-in-square example, the square has unit area.
     
     
    Input sites
    Fx
    list extreme points of the input sites. These points are vertices of the furthest-point Delaunay triangulation. They are on the boundary of the convex hull. The first line is the number of extreme points. Each point is listed, one per line. The points-in-square example has four extreme points.
     
     
    General
    FA
    compute total area for 's' and 'FS'. This is the same as the area of the convex hull.
    o
    print upper facets of the corresponding convex hull (a paraboloid)
    m
    Mathematica output for the upper facets of the paraboloid (2-d triangulations).
    FM
    Maple output for the upper facets of the paraboloid (2-d triangulations).
    G
    Geomview output for the paraboloid (2-d or 3-d triangulations).
    s
    print summary for the furthest-site Delaunay triangulation. Use 'Fs' and 'FS' for numeric data.

    »furthest-site qdelaunay controls

    These options provide additional control:

    Qu
    must be used for furthest-site Delaunay triangulation.
    Qt
    triangulated output. Qhull triangulates non-simplicial facets. It may produce degenerate facets of zero area.
    QJ
    joggle the input to avoid cospherical and coincident sites. It is less accurate than triangulated output ('Qt').
    QVn
    select facets adjacent to input site n (marked 'good').
    Tv
    verify result.
    TI file
    input data from file. The filename may not use spaces or quotes.
    TO file
    output results to file. Use single quotes if the filename contains spaces (e.g., TO 'file with spaces.txt'
    TFn
    report progress after constructing n facets
    PDk:1
    include upper and lower facets in the output. Set k to the last dimension (e.g., 'PD2:1' for 2-d inputs).
    f
    facet dump. Print the data structure for each facet (i.e., furthest-site Delaunay region).

    »furthest-site qdelaunay graphics

    See Delaunay graphics. They are the same except for Mathematica and Maple output.

    »furthest-site qdelaunay notes

    The furthest-site Delaunay triangulation does not record coincident input sites. Use qdelaunay instead.

    qdelaunay Qu does not work for purely cocircular or cospherical points (e.g., rbox c | qdelaunay Qu). Instead, use qdelaunay Qz -- when all points are vertices of the convex hull of the input sites, the Delaunay triangulation is the same as the furthest-site Delaunay triangulation.

    A non-simplicial, furthest-site Delaunay region indicates nearly cocircular or cospherical input sites. To avoid non-simplicial regions triangulate the output ('Qt') or joggle the input ('QJ'). Joggled input is less accurate than triangulated output. You may also triangulate non-simplicial regions with option 'Ft'. It adds the centrum to non-simplicial regions. Alternatively, use an exact arithmetic code.

    Furthest-site Delaunay triangulations do not include facets that are coplanar with the convex hull of the input sites. A facet is coplanar if the last coefficient of its normal is nearly zero (see qh_ZEROdelaunay).

    »furthest-site qdelaunay conventions

    The following terminology is used for furthest-site Delaunay triangulations in Qhull. The underlying structure is the upper facets of a convex hull in one higher dimension. See convex hull conventions, Delaunay conventions, and Qhull's data structures

    • input site - a point in the input (one dimension lower than a point on the convex hull)
    • point - d+1 coordinates. The last coordinate is the sum of the squares of the input site's coordinates
    • vertex - a point on the paraboloid. It corresponds to a unique input site.
    • furthest-site Delaunay facet - an upper facet of the paraboloid. The last coefficient of its normal is clearly positive.
    • furthest-site Delaunay region - a furthest-site Delaunay facet projected to the input sites
    • non-simplicial facet - more than d points are cocircular or cospherical
    • good facet - a furthest-site Delaunay facet with optional restrictions by 'QVn', etc.

    »furthest-site qdelaunay options

    See qdelaunay options. The same program is used for both constructions. Use option 'Qu' for furthest-site Delaunay triangulations.

    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
    To: synopsis • input • outputs • controls • graphics • notes • conventions • options


    The Geometry Center Home Page

    Comments to: qhull@qhull.org
    Created: Sept. 25, 1995 --- Last modified: see top

    geometry/inst/doc/qhull/html/qvoron_f.html0000644000176200001440000004201713431000557020441 0ustar liggesusers qvoronoi Qu -- furthest-site Voronoi diagram Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
    To: synopsis • input • outputs • controls • graphics • notes • conventions • options

    [delaunay]qvoronoi Qu -- furthest-site Voronoi diagram

    The furthest-site Voronoi diagram is the furthest-neighbor map for a set of points. Each region contains those points that are further from one input site than any other input site. See the survey article by Aurenhammer ['91] and the brief introduction by O'Rourke ['94]. The furthest-site Voronoi diagram is the dual of the furthest-site Delaunay triangulation.

    Example: rbox 10 D2 | qvoronoi Qu s o TO result
    Compute the 2-d, furthest-site Voronoi diagram of 10 random points. Write a summary to the console and the Voronoi regions and vertices to 'result'. The first vertex of the result indicates unbounded regions. Almost all regions are unbounded.
    Example: rbox r y c G1 D2 | qvoronoi Qu s Fn TO result
    Compute the 2-d furthest-site Voronoi diagram of a square and a small triangle. Write a summary to the console and the Voronoi vertices for each input site to 'result'. The origin is the only furthest-site Voronoi vertex. The negative indices indicate vertices-at-infinity.

    Qhull computes the furthest-site Voronoi diagram via the furthest-site Delaunay triangulation. Each furthest-site Voronoi vertex is the circumcenter of an upper facet of the Delaunay triangulation. Each furthest-site Voronoi region corresponds to a vertex of the Delaunay triangulation (i.e., an input site).

    See Qhull FAQ - Delaunay and Voronoi diagram questions.

    The 'qvonoroi' program is equivalent to 'qhull v Qbb' in 2-d to 3-d, and 'qhull v Qbb Qx' in 4-d and higher. It disables the following Qhull options: d n m v H U Qb QB Qc Qf Qg Qi Qm Qr QR Qv Qx TR E V Fa FA FC Fp FS Ft FV Gt Q0,etc.

    Copyright © 1995-2018 C.B. Barber


    »furthest-site qvoronoi synopsis

    See qvoronoi synopsis. The same program is used for both constructions. Use option 'Qu' for furthest-site Voronoi diagrams.

    »furthest-site qvoronoi input

    The input data on stdin consists of:

    • dimension
    • number of points
    • point coordinates

    Use I/O redirection (e.g., qvoronoi Qu < data.txt), a pipe (e.g., rbox 10 | qvoronoi Qu), or the 'TI' option (e.g., qvoronoi TI data.txt Qu).

    For example, this is a square containing four random points. Its furthest-site Voronoi diagram has on vertex and four unbounded, separating hyperplanes (i.e., the coordinate axes)

    rbox c 4 D2 > data
    2 RBOX c 4 D2
    8
    -0.4999921736307369 -0.3684622117955817
    0.2556053225468894 -0.0413498678629751
    0.0327672376602583 -0.2810408135699488
    -0.452955383763607 0.17886471718444
      -0.5   -0.5
      -0.5    0.5
       0.5   -0.5
       0.5    0.5
    

    qvoronoi Qu s Fo < data

    
    Furthest-site Voronoi vertices by the convex hull of 8 points in 3-d:
    
      Number of Voronoi regions: 8
      Number of Voronoi vertices: 1
      Number of non-simplicial Voronoi vertices: 1
    
    Statistics for: RBOX c 4 D2 | QVORONOI Qu s Fo
    
      Number of points processed: 8
      Number of hyperplanes created: 20
      Number of facets in hull: 11
      Number of distance tests for qhull: 34
      Number of merged facets: 1
      Number of distance tests for merging: 107
      CPU seconds to compute hull (after input):  0
    
    4
    5 4 5      0      1      0
    5 4 6      1      0      0
    5 5 7      1      0      0
    5 6 7      0      1      0
    

    » furthest-site qvoronoi outputs

    These options control the output of furthest-site Voronoi diagrams.

     
    furthest-site Voronoi vertices
    p
    print the coordinates of the furthest-site Voronoi vertices. The first line is the dimension. The second line is the number of vertices. Each remaining line is a furthest-site Voronoi vertex. The points-in-square example has one furthest-site Voronoi vertex at the origin.
    Fn
    list the neighboring furthest-site Voronoi vertices for each furthest-site Voronoi vertex. The first line is the number of Voronoi vertices. Each remaining line starts with the number of neighboring vertices. Negative indices (e.g., -1) indicate vertices outside of the Voronoi diagram. In the points-in-square example, the Voronoi vertex at the origin has four neighbors-at-infinity.
    FN
    list the furthest-site Voronoi vertices for each furthest-site Voronoi region. The first line is the number of Voronoi regions. Each remaining line starts with the number of Voronoi vertices. Negative indices (e.g., -1) indicate vertices outside of the Voronoi diagram. In the points-in-square example, all regions share the Voronoi vertex at the origin.
     
     
    furthest-site Voronoi regions
    o
    print the furthest-site Voronoi regions in OFF format. The first line is the dimension. The second line is the number of vertices, the number of input sites, and "1". The third line represents the vertex-at-infinity. Its coordinates are "-10.101". The next lines are the coordinates of the furthest-site Voronoi vertices. Each remaining line starts with the number of Voronoi vertices in a Voronoi region. In 2-d, the vertices are listed in adjacency order (unoriented). In 3-d and higher, the vertices are listed in numeric order. In the points-in-square example, each unbounded region includes the Voronoi vertex at the origin. Lines consisting of 0 indicate interior input sites.
    Fi
    print separating hyperplanes for inner, bounded furthest-site Voronoi regions. The first number is the number of separating hyperplanes. Each remaining line starts with 3+dim. The next two numbers are adjacent input sites. The next dim numbers are the coefficients of the separating hyperplane. The last number is its offset. The are no bounded, separating hyperplanes for the points-in-square example.
    Fo
    print separating hyperplanes for outer, unbounded furthest-site Voronoi regions. The first number is the number of separating hyperplanes. Each remaining line starts with 3+dim. The next two numbers are adjacent input sites on the convex hull. The next dim numbers are the coefficients of the separating hyperplane. The last number is its offset. The points-in-square example has four unbounded, separating hyperplanes.
     
     
    Input sites
    Fv
    list ridges of furthest-site Voronoi vertices for pairs of input sites. The first line is the number of ridges. Each remaining line starts with two plus the number of Voronoi vertices in the ridge. The next two numbers are two adjacent input sites. The remaining numbers list the Voronoi vertices. As with option 'o', a 0 indicates the vertex-at-infinity and an unbounded, separating hyperplane. The perpendicular bisector (separating hyperplane) of the input sites is a flat through these vertices. In the points-in-square example, the ridge for each edge of the square is unbounded.
     
     
    General
    s
    print summary of the furthest-site Voronoi diagram. Use 'Fs' for numeric data.
    i
    list input sites for each furthest-site Delaunay region. Use option 'Pp' to avoid the warning. The first line is the number of regions. The remaining lines list the input sites for each region. The regions are oriented. In the points-in-square example, the square region has four input sites. In 3-d and higher, report cospherical sites by adding extra points.
    G
    Geomview output for 2-d furthest-site Voronoi diagrams.

    » furthest-site qvoronoi controls

    These options provide additional control:

    Qu
    must be used.
    QVn
    select furthest-site Voronoi vertices for input site n
    Tv
    verify result
    TI file
    input data from file. The filename may not use spaces or quotes.
    TO file
    output results to file. Use single quotes if the filename contains spaces (e.g., TO 'file with spaces.txt'
    TFn
    report progress after constructing n facets
    PDk:1
    include upper and lower facets in the output. Set k to the last dimension (e.g., 'PD2:1' for 2-d inputs).
    f
    facet dump. Print the data structure for each facet (i.e., furthest-site Voronoi vertex).

    » furthest-site qvoronoi graphics

    In 2-d, Geomview output ('G') displays a furthest-site Voronoi diagram with extra edges to close the unbounded furthest-site Voronoi regions. All regions will be unbounded. Since the points-in-box example has only one furthest-site Voronoi vertex, the Geomview output is one point.

    See the Delaunay and Voronoi examples for a 2-d example. Turn off normalization (on Geomview's 'obscure' menu) when comparing the furthest-site Voronoi diagram with the corresponding Voronoi diagram.

    »furthest-site qvoronoi notes

    See Voronoi notes.

    »furthest-site qvoronoi conventions

    The following terminology is used for furthest-site Voronoi diagrams in Qhull. The underlying structure is a furthest-site Delaunay triangulation from a convex hull in one higher dimension. Upper facets of the Delaunay triangulation correspond to vertices of the furthest-site Voronoi diagram. Vertices of the furthest-site Delaunay triangulation correspond to input sites. They also define regions of the furthest-site Voronoi diagram. All vertices are extreme points of the input sites. See qconvex conventions, furthest-site delaunay conventions, and Qhull's data structures.

    • input site - a point in the input (one dimension lower than a point on the convex hull)
    • point - a point has d+1 coordinates. The last coordinate is the sum of the squares of the input site's coordinates
    • vertex - a point on the upper facets of the paraboloid. It corresponds to a unique input site.
    • furthest-site Delaunay facet - an upper facet of the paraboloid. The last coefficient of its normal is clearly positive.
    • furthest-site Voronoi vertex - the circumcenter of a furthest-site Delaunay facet
    • furthest-site Voronoi region - the region of Euclidean space further from an input site than any other input site. Qhull lists the furthest-site Voronoi vertices that define each furthest-site Voronoi region.
    • furthest-site Voronoi diagram - the graph of the furthest-site Voronoi regions with the ridges (edges) between the regions.
    • infinity vertex - the Voronoi vertex for unbounded furthest-site Voronoi regions in 'o' output format. Its coordinates are -10.101.
    • good facet - an furthest-site Voronoi vertex with optional restrictions by 'QVn', etc.

    »furthest-site qvoronoi options

    See qvoronoi options. The same program is used for both constructions. Use option 'Qu' for furthest-site Voronoi diagrams.

    Up: Home page for Qhull
    Up: Qhull manual: Table of Contents
    To: ProgramsOptionsOutputFormatsGeomviewPrintQhullPrecisionTraceFunctions
    To: synopsis • input • outputs • controls • graphics • notes • conventions • options


    The Geometry Center Home Page

    Comments to: qhull@qhull.org
    Created: Sept. 25, 1995 --- Last modified: see top

    geometry/inst/doc/qhull/html/qhull.txt0000644000176200001440000014070013431000557017606 0ustar liggesusers qhull(1) qhull(1) NAME qhull - convex hull, Delaunay triangulation, Voronoi dia- gram, halfspace intersection about a point, hull volume, facet area SYNOPSIS qhull- compute convex hulls and related structures input (stdin): dimension, #points, point coordinates first comment (non-numeric) is listed in the summary halfspace: use dim plus one with offsets after coefficients options (qh-quick.htm): d - Delaunay triangulation by lifting points to a paraboloid v - Voronoi diagram via the Delaunay triangulation H1,1 - Halfspace intersection about [1,1,0,...] d Qu - Furthest-site Delaunay triangulation (upper convex hull) v Qu - Furthest-site Voronoi diagram QJ - Joggle the input to avoid precision problems . - concise list of all options - - one-line description of all options Output options (subset): FA - compute total area and volume Fx - extreme points (convex hull vertices) G - Geomview output (2-d, 3-d and 4-d) Fp - halfspace intersection coordinates m - Mathematica output (2-d and 3-d) n - normals with offsets o - OFF file format (if Voronoi, outputs regions) TO file- output results to file, may be enclosed in single quotes f - print all fields of all facets s - summary of results (default) Tv - verify result: structure, convexity, and point inclusion p - vertex coordinates i - vertices incident to each facet example: rbox 1000 s | qhull Tv s FA - html manual: index.htm - installation: README.txt - see also: COPYING.txt, REGISTER.txt, Changes.txt - WWW: - GIT: - mirror: - news: - Geomview: - news group: - FAQ: - email: qhull@qhull.org - bug reports: qhull_bug@qhull.org Geometry Center 2003/12/30 1 qhull(1) qhull(1) The sections are: - INTRODUCTION - DESCRIPTION, a description of Qhull - IMPRECISION, how Qhull handles imprecision - OPTIONS - Input and output options - Additional input/output formats - Precision options - Geomview options - Print options - Qhull options - Trace options - BUGS - E-MAIL - SEE ALSO - AUTHORS - ACKNOWLEGEMENTS This man page briefly describes all Qhull options. Please report any mismatches with Qhull's html manual (qh- man.htm). INTRODUCTION Qhull is a general dimension code for computing convex hulls, Delaunay triangulations, Voronoi diagram, furthest- site Voronoi diagram, furthest-site Delaunay triangula- tions, and halfspace intersections about a point. It implements the Quickhull algorithm for computing the con- vex hull. Qhull handles round-off errors from floating point arithmetic. It can approximate a convex hull. The program includes options for hull volume, facet area, partial hulls, input transformations, randomization, trac- ing, multiple output formats, and execution statistics. The program can be called from within your application. You can view the results in 2-d, 3-d and 4-d with Geomview. DESCRIPTION The format of input is the following: first line contains the dimension, second line contains the number of input points, and point coordinates follow. The dimension and number of points can be reversed. Comments and line breaks are ignored. A comment starts with a non-numeric character and continues to the end of line. The first comment is reported in summaries and statistics. Error reporting is better if there is one point per line. The default printout option is a short summary. There are many other output formats. Geometry Center 2003/12/30 2 qhull(1) qhull(1) Qhull implements the Quickhull algorithm for convex hull. This algorithm combines the 2-d Quickhull algorithm with the n-d beneath-beyond algorithm [c.f., Preparata & Shamos '85]. It is similar to the randomized algorithms of Clarkson and others [Clarkson et al. '93]. The main advantages of Quickhull are output sensitive performance, reduced space requirements, and automatic handling of pre- cision problems. The data structure produced by Qhull consists of vertices, ridges, and facets. A vertex is a point of the input set. A ridge is a set of d vertices and two neighboring facets. For example in 3-d, a ridge is an edge of the polyhedron. A facet is a set of ridges, a set of neighboring facets, a set of incident vertices, and a hyperplane equation. For simplicial facets, the ridges are defined by the vertices and neighboring facets. When Qhull merges two facets, it produces a non-simplicial facet. A non-simplicial facet has more than d neighbors and may share more than one ridge with a neighbor. IMPRECISION Since Qhull uses floating point arithmetic, roundoff error may occur for each calculation. This causes problems for most geometric algorithms. Qhull automatically sets option 'C-0' in 2-d, 3-d, and 4-d, or option 'Qx' in 5-d and higher. These options han- dle precision problems by merging facets. Alternatively, use option 'QJ' to joggle the input. With 'C-0', Qhull merges non-convex facets while con- structing the hull. The remaining facets are clearly con- vex. With 'Qx', Qhull merges coplanar horizon facets, flipped facets, concave facets and duplicated ridges. It merges coplanar facets after constructing the hull. With 'Qx', coplanar points may be missed, but it appears to be unlikely. To guarantee triangular output, joggle the input with option 'QJ'. Facet merging will not occur. OPTIONS To get a list of the most important options, execute 'qhull' by itself. To get a complete list of options, execute 'qhull -'. To get a complete, concise list of options, execute 'qhull .'. Options can be in any order. Capitalized options take an argument (except 'PG' and 'F' options). Single letters are used for output formats and precision constants. The other options are grouped into menus for other output for- mats ('F'), Geomview output ('G'), printing ('P'), Qhull Geometry Center 2003/12/30 3 qhull(1) qhull(1) control ('Q'), and tracing ('T'). Main options: default Compute the convex hull of the input points. Report a summary of the result. d Compute the Delaunay triangulation by lifting the input points to a paraboloid. The 'o' option prints the input points and facets. The 'QJ' option guarantees triangular output. The 'Ft' option prints a triangulation. It adds points (the centrums) to non-simplicial facets. v Compute the Voronoi diagram from the Delaunay tri- angulation. The 'p' option prints the Voronoi ver- tices. The 'o' option prints the Voronoi vertices and the vertices in each Voronoi region. It lists regions in site id order. The 'Fv' option prints each ridge of the Voronoi diagram. The first or zero'th vertex indicates the infinity vertex. Its coordinates are qh_INFINITE (-10.101). It indi- cates unbounded Voronoi regions or degenerate Delaunay triangles. Hn,n,... Compute halfspace intersection about [n,n,0,...]. The input is a set of halfspaces defined in the same format as 'n', 'Fo', and 'Fi'. Use 'Fp' to print the intersection points. Use 'Fv' to list the intersection points for each halfspace. The other output formats display the dual convex hull. The point [n,n,n,...] is a feasible point for the halfspaces, i.e., a point that is inside all of the halfspaces (Hx+b <= 0). The default coordinate value is 0. The input may start with a feasible point. If so, use 'H' by itself. The input starts with a feasi- ble point when the first number is the dimension, the second number is "1", and the coordinates com- plete a line. The 'FV' option produces a feasible point for a convex hull. d Qu Compute the furthest-site Delaunay triangulation from the upper convex hull. The 'o' option prints the input points and facets. The 'QJ' option guar- antees triangular otuput. You can also use facets. v Qu Compute the furthest-site Voronoi diagram. The 'p' option prints the Voronoi vertices. The 'o' option prints the Voronoi vertices and the vertices in Geometry Center 2003/12/30 4 qhull(1) qhull(1) each Voronoi region. The 'Fv' option prints each ridge of the Voronoi diagram. The first or zero'th vertex indicates the infinity vertex at infinity. Its coordinates are qh_INFINITE (-10.101). It indicates unbounded Voronoi regions and degenerate Delaunay triangles. Qt Triangulated output. Input/Output options: f Print out all facets and all fields of each facet. G Output the hull in Geomview format. For imprecise hulls, Geomview displays the inner and outer hull. Geomview can also display points, ridges, vertices, coplanar points, and facet intersections. See below for a list of options. For Delaunay triangulations, 'G' displays the cor- responding paraboloid. For halfspace intersection, 'G' displays the dual polytope. i Output the incident vertices for each facet. Qhull prints the number of facets followed by the ver- tices of each facet. One facet is printed per line. The numbers are the 0-relative indices of the corresponding input points. The facets are oriented. In 4-d and higher, Qhull triangulates non-simpli- cial facets. Each apex (the first vertex) is a created point that corresponds to the facet's cen- trum. Its index is greater than the indices of the input points. Each base corresponds to a simpli- cial ridge between two facets. To print the ver- tices without triangulation, use option 'Fv'. m Output the hull in Mathematica format. Qhull writes a Mathematica file for 2-d and 3-d convex hulls and for 2-d Delaunay triangulations. Qhull produces a list of objects that you can assign to a variable in Mathematica, for example: "list= << ". If the object is 2-d, it can be visualized by "Show[Graphics[list]] ". For 3-d objects the command is "Show[Graphics3D[list]]". n Output the normal equation for each facet. Qhull prints the dimension (plus one), the number of facets, and the normals for each facet. The facet's offset follows its normal coefficients. o Output the facets in OFF file format. Qhull prints the dimension, number of points, number of facets, and number of ridges. Then it prints the Geometry Center 2003/12/30 5 qhull(1) qhull(1) coordinates of the input points and the vertices for each facet. Each facet is on a separate line. The first number is the number of vertices. The remainder are the indices of the corresponding points. The vertices are oriented in 2-d, 3-d, and in simplicial facets. For 2-d Voronoi diagrams, the vertices are sorted by adjacency, but not oriented. In 3-d and higher, the Voronoi vertices are sorted by index. See the 'v' option for more information. p Output the coordinates of each vertex point. Qhull prints the dimension, the number of points, and the coordinates for each vertex. With the 'Gc' and 'Gi' options, it also prints coplanar and interior points. For Voronoi diagrams, it prints the coor- dinates of each Voronoi vertex. s Print a summary to stderr. If no output options are specified at all, a summary goes to stdout. The summary lists the number of input points, the dimension, the number of vertices in the convex hull, the number of facets in the convex hull, the number of good facets (if 'Pg'), and statistics. The last two statistics (if needed) measure the maximum distance from a point or vertex to a facet. The number in parenthesis (e.g., 2.1x) is the ratio between the maximum distance and the worst-case distance due to merging two simplicial facets. Precision options An Maximum angle given as a cosine. If the angle between a pair of facet normals is greater than n, Qhull merges one of the facets into a neighbor. If 'n' is negative, Qhull tests angles after adding each point to the hull (pre-merging). If 'n' is posi- tive, Qhull tests angles after constructing the hull (post-merging). Both pre- and post-merging can be defined. Option 'C0' or 'C-0' is set if the corresponding 'Cn' or 'C-n' is not set. If 'Qx' is set, then 'A- n' and 'C-n' are checked after the hull is con- structed and before 'An' and 'Cn' are checked. Cn Centrum radius. If a centrum is less than n below a neighboring facet, Qhull merges one of the facets. If 'n' is negative or '-0', Qhull tests and merges facets after adding each point to the hull. This is called "pre-merging". If 'n' is Geometry Center 2003/12/30 6 qhull(1) qhull(1) positive, Qhull tests for convexity after con- structing the hull ("post-merging"). Both pre- and post-merging can be defined. For 5-d and higher, 'Qx' should be used instead of 'C-n'. Otherwise, most or all facets may be merged together. En Maximum roundoff error for distance computations. Rn Randomly perturb distance computations up to +/- n * max_coord. This option perturbs every distance, hyperplane, and angle computation. To use time as the random number seed, use option 'QR-1'. Vn Minimum distance for a facet to be visible. A facet is visible if the distance from the point to the facet is greater than 'Vn'. Without merging, the default value for 'Vn' is the round-off error ('En'). With merging, the default value is the pre-merge centrum ('C-n') in 2-d or 3--d, or three times that in other dimensions. If the outside width is specified ('Wn'), the maximum, default value for 'Vn' is 'Wn'. Un Maximum distance below a facet for a point to be coplanar to the facet. The default value is 'Vn'. Wn Minimum outside width of the hull. Points are added to the convex hull only if they are clearly outside of a facet. A point is outside of a facet if its distance to the facet is greater than 'Wn'. The normal value for 'Wn' is 'En'. If the user specifies pre-merging and does not set 'Wn', than 'Wn' is set to the premerge 'Cn' and maxco- ord*(1-An). Additional input/output formats Fa Print area for each facet. For Delaunay triangula- tions, the area is the area of the triangle. For Voronoi diagrams, the area is the area of the dual facet. Use 'PAn' for printing the n largest facets, and option 'PFn' for printing facets larger than 'n'. The area for non-simplicial facets is the sum of the areas for each ridge to the centrum. Vertices far below the facet's hyperplane are ignored. The reported area may be significantly less than the actual area. Geometry Center 2003/12/30 7 qhull(1) qhull(1) FA Compute the total area and volume for option 's'. It is an approximation for non-simplicial facets (see 'Fa'). Fc Print coplanar points for each facet. The output starts with the number of facets. Then each facet is printed one per line. Each line is the number of coplanar points followed by the point ids. Option 'Qi' includes the interior points. Each coplanar point (interior point) is assigned to the facet it is furthest above (resp., least below). FC Print centrums for each facet. The output starts with the dimension followed by the number of facets. Then each facet centrum is printed, one per line. Fd Read input in cdd format with homogeneous points. The input starts with comments. The first comment is reported in the summary. Data starts after a "begin" line. The next line is the number of points followed by the dimension+1 and "real" or "integer". Then the points are listed with a leading "1" or "1.0". The data ends with an "end" line. For halfspaces ('Fd Hn,n,...'), the input format is the same. Each halfspace starts with its offset. The sign of the offset is the opposite of Qhull's convention. FD Print normals ('n', 'Fo', 'Fi') or points ('p') in cdd format. The first line is the command line that invoked Qhull. Data starts with a "begin" line. The next line is the number of normals or points followed by the dimension+1 and "real". Then the normals or points are listed with the offset before the coefficients. The offset for points is 1.0. The offset for normals has the opposite sign. The data ends with an "end" line. FF Print facets (as in 'f') without printing the ridges. Fi Print inner planes for each facet. The inner plane is below all vertices. Fi Print separating hyperplanes for bounded, inner regions of the Voronoi diagram. The first line is the number of ridges. Then each hyperplane is printed, one per line. A line starts with the num- ber of indices and floats. The first pair lists adjacent input sites, the next d floats are the normalized coefficients for the hyperplane, and the Geometry Center 2003/12/30 8 qhull(1) qhull(1) last float is the offset. The hyperplane is ori- ented toward verify that the hyperplanes are per- pendicular bisectors. Use 'Fo' for unbounded regions, and 'Fv' for the corresponding Voronoi vertices. FI Print facet identifiers. Fm Print number of merges for each facet. At most 511 merges are reported for a facet. See 'PMn' for printing the facets with the most merges. FM Output the hull in Maple format. See 'm' Fn Print neighbors for each facet. The output starts with the number of facets. Then each facet is printed one per line. Each line is the number of neighbors followed by an index for each neighbor. The indices match the other facet output formats. A negative index indicates an unprinted facet due to printing only good facets ('Pg'). It is the negation of the facet's id (option 'FI'). For example, negative indices are used for facets "at infinity" in the Delaunay triangulation. FN Print vertex neighbors or coplanar facet for each point. The first line is the number of points. Then each point is printed, one per line. If the point is coplanar, the line is "1" followed by the facet's id. If the point is not a selected vertex, the line is "0". Otherwise, each line is the num- ber of neighbors followed by the corresponding facet indices (see 'Fn'). Fo Print outer planes for each facet in the same for- mat as 'n'. The outer plane is above all points. Fo Print separating hyperplanes for unbounded, outer regions of the Voronoi diagram. The first line is the number of ridges. Then each hyperplane is printed, one per line. A line starts with the num- ber of indices and floats. The first pair lists adjacent input sites, the next d floats are the normalized coefficients for the hyperplane, and the last float is the offset. The hyperplane is ori- ented toward verify that the hyperplanes are per- pendicular bisectors. Use 'Fi' for bounded regions, and 'Fv' for the corresponding Voronoi vertices. FO List all options to stderr, including the default values. Additional 'FO's are printed to stdout. Fp Print points for halfspace intersections (option 'Hn,n,...'). Each intersection corresponds to a Geometry Center 2003/12/30 9 qhull(1) qhull(1) facet of the dual polytope. The "infinity" point [-10.101,-10.101,...] indicates an unbounded intersection. FP For each coplanar point ('Qc') print the point id of the nearest vertex, the point id, the facet id, and the distance. FQ Print command used for qhull and input. Fs Print a summary. The first line consists of the number of integers ("7"), followed by the dimen- sion, the number of points, the number of vertices, the number of facets, the number of vertices selected for output, the number of facets selected for output, the number of coplanar points selected for output. The second line consists of the number of reals ("2"), followed by the maxmimum offset to an outer plane and and minimum offset to an inner plane. Roundoff is included. Later versions of Qhull may produce additional integers or reals. FS Print the size of the hull. The first line con- sists of the number of integers ("0"). The second line consists of the number of reals ("2"), fol- lowed by the total facet area, and the total vol- ume. Later versions of Qhull may produce addi- tional integers or reals. The total volume measures the volume of the inter- section of the halfspaces defined by each facet. Both area and volume are approximations for non- simplicial facets. See option 'Fa'. Ft Print a triangulation with added points for non- simplicial facets. The first line is the dimension and the second line is the number of points and the number of facets. The points follow, one per line, then the facets follow as a list of point indices. With option points include the point-at-infinity. Fv Print vertices for each facet. The first line is the number of facets. Then each facet is printed, one per line. Each line is the number of vertices followed by the corresponding point ids. Vertices are listed in the order they were added to the hull (the last one is first). Fv Print all ridges of a Voronoi diagram. The first line is the number of ridges. Then each ridge is printed, one per line. A line starts with the num- ber of indices. The first pair lists adjacent Geometry Center 2003/12/30 10 qhull(1) qhull(1) input sites, the remaining indices list Voronoi vertices. Vertex '0' indicates the vertex-at- infinity (i.e., an unbounded ray). In 3-d, the vertices are listed in order. See 'Fi' and 'Fo' for separating hyperplanes. FV Print average vertex. The average vertex is a fea- sible point for halfspace intersection. Fx List extreme points (vertices) of the convex hull. The first line is the number of points. The other lines give the indices of the corresponding points. The first point is '0'. In 2-d, the points occur in counter-clockwise order; otherwise they occur in input order. For Delaunay triangulations, 'Fx' lists the extreme points of the input sites. The points are unordered. Geomview options G Produce a file for viewing with Geomview. Without other options, Qhull displays edges in 2-d, outer planes in 3-d, and ridges in 4-d. A ridge can be explicit or implicit. An explicit ridge is a dim-1 dimensional simplex between two facets. In 4-d, the explicit ridges are triangles. When displaying a ridge in 4-d, Qhull projects the ridge's vertices to one of its facets' hyperplanes. Use 'Gh' to project ridges to the intersection of both hyper- planes. Ga Display all input points as dots. Gc Display the centrum for each facet in 3-d. The centrum is defined by a green radius sitting on a blue plane. The plane corresponds to the facet's hyperplane. The radius is defined by 'C-n' or 'Cn'. GDn Drop dimension n in 3-d or 4-d. The result is a 2-d or 3-d object. Gh Display hyperplane intersections in 3-d and 4-d. In 3-d, the intersection is a black line. It lies on two neighboring hyperplanes (c.f., the blue squares associated with centrums ('Gc')). In 4-d, the ridges are projected to the intersection of both hyperplanes. Gi Display inner planes in 2-d and 3-d. The inner plane of a facet is below all of its vertices. It is parallel to the facet's hyperplane. The inner plane's color is the opposite (1-r,1-g,1-b) of the Geometry Center 2003/12/30 11 qhull(1) qhull(1) outer plane. Its edges are determined by the ver- tices. Gn Do not display inner or outer planes. By default, Geomview displays the precise plane (no merging) or both inner and output planes (merging). Under merging, Geomview does not display the inner plane if the the difference between inner and outer is too small. Go Display outer planes in 2-d and 3-d. The outer plane of a facet is above all input points. It is parallel to the facet's hyperplane. Its color is determined by the facet's normal, and its edges are determined by the vertices. Gp Display coplanar points and vertices as radii. A radius defines a ball which corresponds to the imprecision of the point. The imprecision is the maximum of the roundoff error, the centrum radius, and maxcoord * (1-An). It is at least 1/20'th of the maximum coordinate, and ignores post-merging if pre-merging is done. Gr Display ridges in 3-d. A ridge connects the two vertices that are shared by neighboring facets. Ridges are always displayed in 4-d. Gt A 3-d Delaunay triangulation looks like a convex hull with interior facets. Option 'Gt' removes the outside ridges to reveal the outermost facets. It automatically sets options 'Gr' and 'GDn'. Gv Display vertices as spheres. The radius of the sphere corresponds to the imprecision of the data. See 'Gp' for determining the radius. Print options PAn Only the n largest facets are marked good for printing. Unless 'PG' is set, 'Pg' is automati- cally set. Pdk:n Drop facet from output if normal[k] <= n. The option 'Pdk' uses the default value of 0 for n. PDk:n Drop facet from output if normal[k] >= n. The option 'PDk' uses the default value of 0 for n. PFn Only facets with area at least 'n' are marked good for printing. Unless 'PG' is set, 'Pg' is automat- ically set. Geometry Center 2003/12/30 12 qhull(1) qhull(1) Pg Print only good facets. A good facet is either visible from a point (the 'QGn' option) or includes a point (the 'QVn' option). It also meets the requirements of 'Pdk' and 'PDk' options. Option 'Pg' is automatically set for options 'PAn' and 'PFn'. PG Print neighbors of good facets. PMn Only the n facets with the most merges are marked good for printing. Unless 'PG' is set, 'Pg' is automatically set. Po Force output despite precision problems. Verify ('Tv') does not check coplanar points. Flipped facets are reported and concave facets are counted. If 'Po' is used, points are not partitioned into flipped facets and a flipped facet is always visible to a point. Also, if an error occurs before the completion of Qhull and tracing is not active, 'Po' outputs a neighborhood of the erroneous facets (if any). Pp Do not report precision problems. Qhull control options Qbk:0Bk:0 Drop dimension k from the input points. This allows the user to take convex hulls of sub-dimen- sional objects. It happens before the Delaunay and Voronoi transformation. QbB Scale the input points to fit the unit cube. After scaling, the lower bound will be -0.5 and the upper bound +0.5 in all dimensions. For Delaunay and Voronoi diagrams, scaling happens after projection to the paraboloid. Under precise arithmetic, scal- ing does not change the topology of the convex hull. Qbb Scale the last coordinate to [0, m] where m is the maximum absolute value of the other coordinates. For Delaunay and Voronoi diagrams, scaling happens after projection to the paraboloid. It reduces roundoff error for inputs with integer coordinates. Under precise arithmetic, scaling does not change the topology of the convex hull. Qbk:n Scale the k'th coordinate of the input points. After scaling, the lower bound of the input points will be n. 'Qbk' scales to -0.5. Geometry Center 2003/12/30 13 qhull(1) qhull(1) QBk:n Scale the k'th coordinate of the input points. After scaling, the upper bound will be n. 'QBk' scales to +0.5. Qc Keep coplanar points with the nearest facet. Out- put formats 'p', 'f', 'Gp', 'Fc', 'FN', and 'FP' will print the points. Qf Partition points to the furthest outside facet. Qg Only build good facets. With the 'Qg' option, Qhull will only build those facets that it needs to determine the good facets in the output. See 'QGn', 'QVn', and 'PdD' for defining good facets, and 'Pg' and 'PG' for printing good facets and their neighbors. QGn A facet is good (see 'Qg' and 'Pg') if it is visi- ble from point n. If n < 0, a facet is good if it is not visible from point n. Point n is not added to the hull (unless 'TCn' or 'TPn'). With rbox, use the 'Pn,m,r' option to define your point; it will be point 0 (QG0). Qi Keep interior points with the nearest facet. Out- put formats 'p', 'f', 'Gp', 'FN', 'FP', and 'Fc' will print the points. QJn Joggle each input coordinate by adding a random number in [-n,n]. If a precision error occurs, then qhull increases n and tries again. It does not increase n beyond a certain value, and it stops after a certain number of attempts [see user.h]. Option 'QJ' selects a default value for n. The output will be simplicial. For Delaunay triangula- tions, 'QJn' sets 'Qbb' to scale the last coordi- nate (not if 'Qbk:n' or 'QBk:n' is set). 'QJn' is deprecated for Voronoi diagrams. See also 'Qt'. Qm Only process points that would otherwise increase max_outside. Other points are treated as coplanar or interior points. Qr Process random outside points instead of furthest ones. This makes Qhull equivalent to the random- ized incremental algorithms. CPU time is not reported since the randomization is inefficient. QRn Randomly rotate the input points. If n=0, use time as the random number seed. If n>0, use n as the random number seed. If n=-1, don't rotate but use time as the random number seed. For Delaunay tri- angulations ('d' and 'v'), rotate about the last axis. Geometry Center 2003/12/30 14 qhull(1) qhull(1) Qs Search all points for the initial simplex. Qt Triangulated output. Triangulate non-simplicial facets. 'Qt' is deprecated for Voronoi diagrams. See also 'QJn' Qv Test vertex neighbors for convexity after post- merging. To use the 'Qv' option, you also need to set a merge option (e.g., 'Qx' or 'C-0'). QVn A good facet (see 'Qg' and 'Pg') includes point n. If n<0, then a good facet does not include point n. The point is either in the initial simplex or it is the first point added to the hull. Option 'QVn' may not be used with merging. Qx Perform exact merges while building the hull. The "exact" merges are merging a point into a coplanar facet (defined by 'Vn', 'Un', and 'C-n'), merging concave facets, merging duplicate ridges, and merg- ing flipped facets. Coplanar merges and angle coplanar merges ('A-n') are not performed. Concav- ity testing is delayed until a merge occurs. After the hull is built, all coplanar merges are performed (defined by 'C-n' and 'A-n'), then post- merges are performed (defined by 'Cn' and 'An'). Qz Add a point "at infinity" that is above the paraboloid for Delaunay triangulations and Voronoi diagrams. This reduces precision problems and allows the triangulation of cospherical points. Qhull experiments and speedups Q0 Turn off pre-merging as a default option. With 'Q0'/'Qx' and without explicit pre-merge options, Qhull ignores precision issues while constructing the convex hull. This may lead to precision errors. If so, a descriptive warning is generated. Q1 With 'Q1', Qhull sorts merges by type (coplanar, angle coplanar, concave) instead of by angle. Q2 With 'Q2', Qhull merges all facets at once instead of using independent sets of merges and then retesting. Q3 With 'Q3', Qhull does not remove redundant ver- tices. Q4 With 'Q4', Qhull avoids merges of an old facet into a new facet. Q5 With 'Q5', Qhull does not correct outer planes at the end. The maximum outer plane is used instead. Geometry Center 2003/12/30 15 qhull(1) qhull(1) Q6 With 'Q6', Qhull does not pre-merge concave or coplanar facets. Q7 With 'Q7', Qhull processes facets in depth-first order instead of breadth-first order. Q8 With 'Q8' and merging, Qhull does not retain near- interior points for adjusting outer planes. 'Qc' will probably retain all points that adjust outer planes. Q9 With 'Q9', Qhull processes the furthest of all out- side sets at each iteration. Q10 With 'Q10', Qhull does not use special processing for narrow distributions. Q11 With 'Q11', Qhull copies normals and recomputes centrums for tricoplanar facets. Q12 With 'Q12', Qhull does not report a very wide merge due to a duplicated ridge with nearly coincident vertices Q14 With 'Q14', Qhull does not rename vertices that create a duplicate ridge Trace options Tn Trace at level n. Qhull includes full execution tracing. 'T-1' traces events. 'T1' traces the overall execution of the program. 'T2' and 'T3' trace overall execution and geometric and topologi- cal events. 'T4' traces the algorithm. 'T5' includes information about memory allocation and Gaussian elimination. Ta Annotate output with codes that identify the corresponding qh_fprintf() statement. Tc Check frequently during execution. This will catch most inconsistency errors. TCn Stop Qhull after building the cone of new facets for point n. The output for 'f' includes the cone and the old hull. See also 'TVn'. TFn Report progress whenever more than n facets are created During post-merging, 'TFn' reports progress after more than n/2 merges. TI file Input data from 'file'. The filename may not include spaces or quotes. TO file Output results to 'file'. The name may be enclosed in single quotes. TPn Turn on tracing when point n is added to the hull. Trace partitions of point n. If used with TWn, turn off tracing after adding point n to the hull. TRn Rerun qhull n times. Usually used with 'QJn' to determine the probability that a given joggle will fail. Ts Collect statistics and print to stderr at the end of execution. Tv Verify the convex hull. This checks the topologi- cal structure, facet convexity, and point inclu- sion. If precision problems occurred, facet con- vexity is tested whether or not 'Tv' is selected. Option 'Tv' does not check point inclusion if Geometry Center 2003/12/30 16 qhull(1) qhull(1) forcing output with 'Po', or if 'Q5' is set. For point inclusion testing, Qhull verifies that all points are below all outer planes (facet->max- outside). Point inclusion is exhaustive if merging or if the facet-point product is small enough; oth- erwise Qhull verifies each point with a directed search (qh_findbest). Point inclusion testing occurs after producing out- put. It prints a message to stderr unless option 'Pp' is used. This allows the user to interrupt Qhull without changing the output. TVn Stop Qhull after adding point n. If n < 0, stop Qhull before adding point n. Output shows the hull at this time. See also 'TCn' TMn Turn on tracing at n'th merge. TWn Trace merge facets when the width is greater than n. Tz Redirect stderr to stdout. BUGS Please report bugs to Brad Barber at qhull_bug@qhull.org. If Qhull does not compile, it is due to an incompatibility between your system and ours. The first thing to check is that your compiler is ANSI standard. If it is, check the man page for the best options, or find someone to help you. If you locate the cause of your problem, please send email since it might help others. If Qhull compiles but crashes on the test case (rbox D4), there's still incompatibility between your system and ours. Typically it's been due to mem.c and memory align- ment. You can use qh_NOmem in mem.h to turn off memory management. Please let us know if you figure out how to fix these problems. If you do find a problem, try to simplify it before reporting the error. Try different size inputs to locate the smallest one that causes an error. You're welcome to hunt through the code using the execution trace as a guide. This is especially true if you're incorporating Qhull into your own program. When you do report an error, please attach a data set to the end of your message. This allows us to see the error for ourselves. Qhull is maintained part-time. Geometry Center 2003/12/30 17 qhull(1) qhull(1) E-MAIL Please send correspondence to qhull@qhull.org and report bugs to qhull_bug@qhull.org. Let us know how you use Qhull. If you mention it in a paper, please send the reference and an abstract. If you would like to get Qhull announcements (e.g., a new version) and news (any bugs that get fixed, etc.), let us know and we will add you to our mailing list. If you would like to communicate with other Qhull users, we will add you to the qhull_users alias. For Internet news about geometric algorithms and convex hulls, look at comp.graph- ics.algorithms and sci.math.num-analysis SEE ALSO rbox(1) Barber, C. B., D.P. Dobkin, and H.T. Huhdanpaa, "The Quickhull Algorithm for Convex Hulls," ACM Trans. on Math- ematical Software, 22(4):469-483, Dec. 1996. http://portal.acm.org/citation.cfm?doid=235815.235821 http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.117.405 Clarkson, K.L., K. Mehlhorn, and R. Seidel, "Four results on randomized incremental construction," Computational Geometry: Theory and Applications, vol. 3, p. 185-211, 1993. Preparata, F. and M. Shamos, Computational Geometry, Springer-Verlag, New York, 1985. AUTHORS C. Bradford Barber Hannu Huhdanpaa bradb@shore.net hannu@qhull.org ACKNOWLEDGEMENTS A special thanks to Albert Marden, Victor Milenkovic, the Geometry Center, Harvard University, and Endocardial Solu- tions, Inc. for supporting this work. Qhull 1.0 and 2.0 were developed under National Science Foundation grants NSF/DMS-8920161 and NSF-CCR-91-15793 750-7504. David Dobkin Geometry Center 2003/12/30 18 qhull(1) qhull(1) guided the original work at Princeton University. If you find it useful, please let us know. The Geometry Center was supported by grant DMS-8920161 from the National Science Foundation, by grant DOE/DE-FG02-92ER25137 from the Department of Energy, by the University of Minnesota, and by Minnesota Technology, Inc. Qhull is available from http://www.qhull.org Geometry Center 2003/12/30 19 geometry/inst/doc/qhull/README.txt0000644000176200001440000005253113431000556016455 0ustar liggesusersName qhull, rbox 2015.2 2016/01/18 Convex hull, Delaunay triangulation, Voronoi diagrams, Halfspace intersection Documentation: html/index.htm Available from: (git@github.com:qhull/qhull.git) News and a paper: Version 1 (simplicial only): Purpose Qhull is a general dimension convex hull program that reads a set of points from stdin, and outputs the smallest convex set that contains the points to stdout. It also generates Delaunay triangulations, Voronoi diagrams, furthest-site Voronoi diagrams, and halfspace intersections about a point. Rbox is a useful tool in generating input for Qhull; it generates hypercubes, diamonds, cones, circles, simplices, spirals, lattices, and random points. Qhull produces graphical output for Geomview. This helps with understanding the output. Environment requirements Qhull and rbox should run on all 32-bit and 64-bit computers. Use an ANSI C or C++ compiler to compile the program. The software is self-contained. It comes with examples and test scripts. Qhull's C++ interface uses the STL. The C++ test program uses QTestLib from the Qt Framework. Qhull's C++ interface may change without notice. Eventually, it will move into the qhull shared library. Qhull is copyrighted software. Please read COPYING.txt and REGISTER.txt before using or distributing Qhull. To cite Qhull, please use Barber, C.B., Dobkin, D.P., and Huhdanpaa, H.T., "The Quickhull algorithm for convex hulls," ACM Trans. on Mathematical Software, 22(4):469-483, Dec 1996, http://www.qhull.org. To modify Qhull, particularly the C++ interface Qhull is on GitHub (http://github.com/qhull/qhull, git@github.com:qhull/qhull.git) For internal documentation, see html/qh-code.htm To install Qhull Qhull is precompiled for Windows 32-bit, otherwise it needs compilation. Qhull includes Makefiles for gcc and other targets, CMakeLists.txt for CMake, .sln/.vcproj/.vcxproj files for Microsoft Visual Studio, and .pro files for Qt Creator. It compiles under Windows with mingw. Install and build instructions follow. See the end of this document for a list of distributed files. ----------------- Installing Qhull on Windows 10, 8, 7 (32- or 64-bit), Windows XP, and Windows NT The zip file contains rbox.exe, qhull.exe, qconvex.exe, qdelaunay.exe, qhalf.exe, qvoronoi.exe, testqset.exe, user_eg*.exe, documentation files, and source files. Qhull.exe and user-eg3.exe are compiled with the reentrant library while the other executables use the non-reentrant library. To install Qhull: - Unzip the files into a directory (e.g., named 'qhull') - Click on QHULL-GO or open a command window into Qhull's bin directory. - Test with 'rbox D4 | qhull' To uninstall Qhull - Delete the qhull directory To learn about Qhull: - Execute 'qconvex' for a synopsis and examples. - Execute 'rbox 10 | qconvex' to compute the convex hull of 10 random points. - Execute 'rbox 10 | qconvex i TO file' to write results to 'file'. - Browse the documentation: qhull\html\index.htm - If an error occurs, Windows sends the error to stdout instead of stderr. Use 'TO xxx' to send normal output to xxx To improve the command window - Double-click the window bar to increase the size of the window - Right-click the window bar - Select Properties - Check QuickEdit Mode Select text with right-click or Enter Paste text with right-click - Change Font to Lucinda Console - Change Layout to Screen Buffer Height 999, Window Size Height 55 - Change Colors to Screen Background White, Screen Text Black - Click OK - Select 'Modify shortcut that started this window', then OK If you use qhull a lot, install a bash shell such as MSYS (www.mingw.org/wiki/msys), Road Bash (www.qhull.org/bash), or Cygwin (www.cygwin.com). ----------------- Installing Qhull on Unix with gcc To build Qhull, static libraries, shared library, and C++ interface - Download and extract Qhull (either GitHub, .tgz file, or .zip file) - make - export LD_LIBRARY_PATH=$PWD/lib:$LD_LIBRARY_PATH The Makefiles may be edited for other compilers. If 'testqset' exits with an error, qhull is broken A simple Makefile for Qhull is in src/libqhull and src/libqhull_r. To build the Qhull executables and libqhullstatic - Extract Qhull from qhull...tgz or qhull...zip - cd src/libqhull_r # cd src/libqhull - make ----------------- Installing Qhull with CMake 2.6 or later See CMakeLists.txt for examples and further build instructions To build Qhull, static libraries, shared library, and C++ interface - Download and extract Qhull (either GitHub, .tgz file, or .zip file) - cd build - cmake --help # List build generators - make -G "" .. && cmake .. - cmake .. - make - make install The ".." is important. It refers to the parent directory (i.e., qhull/) On Windows, CMake installs to C:/Program Files/qhull. 64-bit generators have a "Win64" tag. If creating a qhull package, please include a pkg-config file based on build/qhull*.pc.in If cmake fails with "No CMAKE_C_COMPILER could be found" - cmake was not able to find the build environment specified by -G "..." ----------------- Installing Qhull with Qt To build Qhull, including its C++ test (qhulltest) - Download and extract Qhull (either GitHub, .tgz file, or .zip file) - Load src/qhull-all.pro into QtCreator - Build ------------------- Working with Qhull's C++ interface See html/qh-code.htm#cpp for calling Qhull from C++ programs See html/qh-code.htm#reentrant for converting from Qhull-2012 Examples of using the C++ interface user_eg3_r.cpp qhulltest/*_test.cpp Qhull's C++ interface is likely to change. Stay current with GitHub. To clone Qhull's next branch from http://github.com/qhull/qhull git init git clone git@github.com:qhull/qhull.git cd qhull git checkout next ... git pull origin next Compile qhullcpp and libqhullstatic_r with the same compiler. Both libraries use the C routines setjmp() and longjmp() for error handling. They must be compiled with the same compiler. ------------------- Calling Qhull from C programs See html/qh-code.htm#library for calling Qhull from C programs See html/qh-code.htm#reentrant for converting from Qhull-2012 Warning: You will need to understand Qhull's data structures and read the code. Most users will find it easier to call Qhull as an external command. The new, reentrant 'C' code (src/libqhull_r), passes a pointer to qhT to most Qhull routines. This allows multiple instances of Qhull to run at the same time. It simplifies the C++ interface. The non-reentrant 'C' code (src/libqhull) looks unusual. It refers to Qhull's global data structure, qhT, through a 'qh' macro (e.g., 'qh ferr'). This allows the same code to use static memory or heap memory. If qh_QHpointer is defined, qh_qh is a pointer to an allocated qhT; otherwise qh_qh is a global static data structure of type qhT. ------------------ Compiling Qhull with Microsoft Visual C++ To compile 32-bit Qhull with Microsoft Visual C++ 2010 and later - Download and extract Qhull (either GitHub, .tgz file, or .zip file) - Load solution build/qhull-32.sln - Build target 'Win32' - Project qhulltest requires Qt for DevStudio (http://www.qt.io) Set the QTDIR environment variable to your Qt directory (e.g., c:/qt/5.2.0/5.2.0/msvc2012) If QTDIR is incorrect, precompile will fail with 'Can not locate the file specified' To compile 64-bit Qhull with Microsoft Visual C++ 2010 and later - 64-bit Qhull has larger data structures due to 64-bit pointers - Download and extract Qhull (either GitHub, .tgz file, or .zip file) - Load solution build/qhull-64.sln - Build target 'Win32' - Project qhulltest requires Qt for DevStudio (http://www.qt.io) Set the QTDIR environment variable to your Qt directory (e.g., c:/qt/5.2.0/5.2.0/msvc2012_64) If QTDIR is incorrect, precompile will fail with 'Can not locate the file specified' To compile Qhull with Microsoft Visual C++ 2005 (vcproj files) - Download and extract Qhull (either GitHub, .tgz file, or .zip file) - Load solution build/qhull.sln - Build target 'win32' (not 'x64') - Project qhulltest requires Qt for DevStudio (http://www.qt.io) Set the QTDIR environment variable to your Qt directory (e.g., c:/qt/4.7.4) If QTDIR is incorrect, precompile will fail with 'Can not locate the file specified' ----------------- Compiling Qhull with Qt Creator Qt (http://www.qt.io) is a C++ framework for Windows, Linux, and Macintosh Qhull uses QTestLib to test qhull's C++ interface (see src/qhulltest/) To compile Qhull with Qt Creator - Download and extract Qhull (either GitHub, .tgz file, or .zip file) - Download the Qt SDK - Start Qt Creator - Load src/qhull-all.pro - Build ----------------- Compiling Qhull with mingw on Windows To compile Qhull with MINGW - Download and extract Qhull (either GitHub, .tgz file, or .zip file) - Install Road Bash (http://www.qhull.org/bash) or install MSYS (http://www.mingw.org/wiki/msys) - Install MINGW-w64 (http://sourceforge.net/projects/mingw-w64). Mingw is included with Qt SDK. - make ----------------- Compiling Qhull with cygwin on Windows To compile Qhull with cygwin - Download and extract Qhull (either GitHub, .tgz file, or .zip file) - Install cygwin (http://www.cygwin.com) - Include packages for gcc, make, ar, and ln - make ----------------- Compiling from Makfile without gcc The file, qhull-src.tgz, contains documentation and source files for qhull and rbox. To unpack the tgz file - tar zxf qhull-src.tgz - cd qhull - Use qhull/Makefile Simpler Makefiles are qhull/src/libqhull/Makefile and qhull/src/libqhull_r/Makefile Compiling qhull and rbox with Makefile - in Makefile, check the CC, CCOPTS1, PRINTMAN, and PRINTC defines - the defaults are gcc and enscript - CCOPTS1 should include the ANSI flag. It defines __STDC__ - in user.h, check the definitions of qh_SECticks and qh_CPUclock. - use '#define qh_CLOCKtype 2' for timing runs longer than 1 hour - type: make - this builds: qhull qconvex qdelaunay qhalf qvoronoi rbox libqhull.a libqhull_r.a - type: make doc - this prints the man page - See also qhull/html/index.htm - if your compiler reports many errors, it is probably not a ANSI C compiler - you will need to set the -ansi switch or find another compiler - if your compiler warns about missing prototypes for fprintf() etc. - this is ok, your compiler should have these in stdio.h - if your compiler warns about missing prototypes for memset() etc. - include memory.h in qhull_a.h - if your compiler reports "global.c: storage size of 'qh_qh' isn't known" - delete the initializer "={0}" in global.c, stat.c and mem.c - if your compiler warns about "stat.c: improper initializer" - this is ok, the initializer is not used - if you have trouble building libqhull.a with 'ar' - try 'make -f Makefile.txt qhullx' - if the code compiles, the qhull test case will automatically execute - if an error occurs, there's an incompatibility between machines - If you can, try a different compiler - You can turn off the Qhull memory manager with qh_NOmem in mem.h - You can turn off compiler optimization (-O2 in Makefile) - If you find the source of the problem, please let us know - to install the programs and their man pages: - define MANDIR and BINDIR - type 'make install' - if you have Geomview (www.geomview.org) - try 'rbox 100 | qconvex G >a' and load 'a' into Geomview - run 'q_eg' for Geomview examples of Qhull output (see qh-eg.htm) ------------------ Compiling on other machines and compilers Qhull may compile with Borland C++ 5.0 bcc32. A Makefile is included. Execute 'cd src/libqhull; make -f Mborland'. If you use the Borland IDE, set the ANSI option in Options:Project:Compiler:Source:Language-compliance. Qhull may compile with Borland C++ 4.02 for Win32 and DOS Power Pack. Use 'cd src/libqhull; make -f Mborland -D_DPMI'. Qhull 1.0 compiles with Borland C++ 4.02. For rbox 1.0, use "bcc32 -WX -w- -O2-e -erbox -lc rbox.c". Use the same options for Qhull 1.0. [D. Zwick] If you have troubles with the memory manager, you can turn it off by defining qh_NOmem in mem.h. ----------------- Distributed files README.txt // Instructions for installing Qhull REGISTER.txt // Qhull registration COPYING.txt // Copyright notice QHULL-GO.lnk // Windows icon for eg/qhull-go.bat Announce.txt // Announcement CMakeLists.txt // CMake build file (2.6 or later) CMakeModules/CheckLFS.cmake // enables Large File Support in cmake File_id.diz // Package descriptor index.htm // Home page Makefile // Makefile for gcc and other compilers qhull*.md5sum // md5sum for all files bin/* // Qhull executables and dll (.zip only) build/qhull*.pc.in // pkg-config templates for qhull_r, qhull, and qhull_p build/qhull-32.sln // 32-bit DevStudio solution and project files (2010 and later) build/*-32.vcxproj build/qhull-64.sln // 64-bit DevStudio solution and project files (2010 and later) build/*-64.vcxproj build/qhull.sln // DevStudio solution and project files (2005 and 2009) build/*.vcproj eg/* // Test scripts and geomview files from q_eg html/index.htm // Manual html/qh-faq.htm // Frequently asked questions html/qh-get.htm // Download page html/qhull-cpp.xml // C++ style notes as a Road FAQ (www.qhull.org/road) src/Changes.txt // Change history for Qhull and rbox src/qhull-all.pro // Qt project eg/ q_eg // shell script for Geomview examples (eg.01.cube) q_egtest // shell script for Geomview test examples q_test // shell script to test qhull q_test-ok.txt // output from q_test qhulltest-ok.txt // output from qhulltest (Qt only) make-vcproj.sh // bash shell script to create vcproj and vcxprog files qhull-zip.sh // bash shell script for distribution files rbox consists of (bin, html): rbox.exe // Win32 executable (.zip only) rbox.htm // html manual rbox.man // Unix man page rbox.txt qhull consists of (bin, html): qconvex.exe // Win32 executables and dlls (.zip download only) qhull.exe // Built with the reentrant library (about 2% slower) qdelaunay.exe qhalf.exe qvoronoi.exe qhull_r.dll qhull-go.bat // command window qconvex.htm // html manual qdelaun.htm qdelau_f.htm qhalf.htm qvoronoi.htm qvoron_f.htm qh-eg.htm qh-code.htm qh-impre.htm index.htm qh-opt*.htm qh-quick.htm qh--*.gif // images for manual normal_voronoi_knauss_oesterle.jpg qhull.man // Unix man page qhull.txt bin/ msvcr80.dll // Visual C++ redistributable file (.zip download only) src/ qhull/unix.c // Qhull and rbox applications using non-reentrant libqhullstatic.a rbox/rbox.c qconvex/qconvex.c qhalf/qhalf.c qdelaunay/qdelaunay.c qvoronoi/qvoronoi.c qhull/unix_r.c // Qhull and rbox applications using reentrant libqhullstatic_r.a rbox/rbox_r.c qconvex/qconvex_r.c // Qhull applications built with reentrant libqhull_r/Makefile qhalf/qhalf_r.c qdelaunay/qdelaun_r.c qvoronoi/qvoronoi_r.c user_eg/user_eg_r.c // example of using qhull_r.dll from a user program user_eg2/user_eg2_r.c // example of using libqhullstatic_r.a from a user program user_eg3/user_eg3_r.cpp // example of Qhull's C++ interface libqhullcpp with libqhullstatic_r.a qhulltest/qhulltest.cpp // Test of Qhull's C++ interface using Qt's QTestLib qhull-*.pri // Include files for Qt projects testqset_r/testqset_r.c // Test of reentrant qset_r.c and mem_r.c testqset/testqset.c // Test of non-rentrant qset.c and mem.c src/libqhull libqhull.pro // Qt project for non-rentrant, shared library (qhull.dll) index.htm // design documentation for libqhull qh-*.htm qhull-exports.def // Export Definition file for Visual C++ Makefile // Simple gcc Makefile for qhull and libqhullstatic.a Mborland // Makefile for Borland C++ 5.0 libqhull.h // header file for qhull user.h // header file of user definable constants libqhull.c // Quickhull algorithm with partitioning user.c // user re-definable functions usermem.c userprintf.c userprintf_rbox.c qhull_a.h // include files for libqhull/*.c geom.c // geometric routines geom2.c geom.h global.c // global variables io.c // input-output routines io.h mem.c // memory routines, this is stand-alone code mem.h merge.c // merging of non-convex facets merge.h poly.c // polyhedron routines poly2.c poly.h qset.c // set routines, this only depends on mem.c qset.h random.c // utilities w/ Park & Miller's random number generator random.h rboxlib.c // point set generator for rbox stat.c // statistics stat.h src/libqhull_r libqhull_r.pro // Qt project for rentrant, shared library (qhull_r.dll) index.htm // design documentation for libqhull_r qh-*_r.htm qhull-exports_r.def // Export Definition file for Visual C++ Makefile // Simple gcc Makefile for qhull and libqhullstatic.a libqhull_r.h // header file for qhull user_r.h // header file of user definable constants libqhull_r.c // Quickhull algorithm wi_r.hpartitioning user_r.c // user re-definable functions usermem.c userprintf.c userprintf_rbox.c qhull_ra.h // include files for libqhull/*_r.c geom_r.c // geometric routines geom2.c geom_r.h global_r.c // global variables io_r.c // input-output routines io_r.h mem_r.c // memory routines, this is stand-alone code mem.h merge_r.c // merging of non-convex facets merge.h poly_r.c // polyhedron routines poly2.c poly_r.h qset_r.c // set routines, this only depends on mem_r.c qset.h random_r.c // utilities w/ Park & Miller's random number generator random.h rboxlib_r.c // point set generator for rbox stat_r.c // statistics stat.h src/libqhullcpp/ libqhullcpp.pro // Qt project for renentrant, static C++ library Qhull.cpp // Calls libqhull_r.c from C++ Qhull.h qt-qhull.cpp // Supporting methods for Qt Coordinates.cpp // input classes Coordinates.h PointCoordinates.cpp PointCoordinates.h RboxPoints.cpp // call rboxlib.c from C++ RboxPoints.h QhullFacet.cpp // data structure classes QhullFacet.h QhullHyperplane.cpp QhullHyperplane.h QhullPoint.cpp QhullPoint.h QhullQh.cpp QhullRidge.cpp QhullRidge.h QhullVertex.cpp QhullVertex.h QhullFacetList.cpp // collection classes QhullFacetList.h QhullFacetSet.cpp QhullFacetSet.h QhullIterator.h QhullLinkedList.h QhullPoints.cpp QhullPoints.h QhullPointSet.cpp QhullPointSet.h QhullSet.cpp QhullSet.h QhullSets.h QhullVertexSet.cpp QhullVertexSet.h functionObjects.h // supporting classes QhullError.cpp QhullError.h QhullQh.cpp QhullQh.h QhullStat.cpp QhullStat.h RoadError.cpp // Supporting base classes RoadError.h RoadLogEvent.cpp RoadLogEvent.h usermem_r-cpp.cpp // Optional override for qh_exit() to throw an error src/libqhullstatic/ libqhullstatic.pro // Qt project for non-reentrant, static library src/libqhullstatic_r/ libqhullstatic_r.pro // Qt project for reentrant, static library src/qhulltest/ qhulltest.pro // Qt project for test of C++ interface Coordinates_test.cpp // Test of each class PointCoordinates_test.cpp Qhull_test.cpp QhullFacet_test.cpp QhullFacetList_test.cpp QhullFacetSet_test.cpp QhullHyperplane_test.cpp QhullLinkedList_test.cpp QhullPoint_test.cpp QhullPoints_test.cpp QhullPointSet_test.cpp QhullRidge_test.cpp QhullSet_test.cpp QhullVertex_test.cpp QhullVertexSet_test.cpp RboxPoints_test.cpp RoadTest.cpp // Run multiple test files with QTestLib RoadTest.h ----------------- Authors: C. Bradford Barber Hannu Huhdanpaa (Version 1.0) bradb@shore.net hannu@qhull.org Qhull 1.0 and 2.0 were developed under NSF grants NSF/DMS-8920161 and NSF-CCR-91-15793 750-7504 at the Geometry Center and Harvard University. If you find Qhull useful, please let us know. geometry/inst/doc/qhull/Announce.txt0000644000176200001440000000420713431000557017264 0ustar liggesusers Qhull 2015.2 2016/01/18 http://www.qhull.org git@github.com:qhull/qhull.git http://www.geomview.org Qhull computes convex hulls, Delaunay triangulations, Voronoi diagrams, furthest-site Voronoi diagrams, and halfspace intersections about a point. It runs in 2-d, 3-d, 4-d, or higher. It implements the Quickhull algorithm for computing convex hulls. Qhull handles round-off errors from floating point arithmetic. It can approximate a convex hull. The program includes options for hull volume, facet area, partial hulls, input transformations, randomization, tracing, multiple output formats, and execution statistics. The program can be called from within your application. You can view the results in 2-d, 3-d and 4-d with Geomview. To download Qhull: http://www.qhull.org/download git@github.com:qhull/qhull.git Download qhull-96.ps for: Barber, C. B., D.P. Dobkin, and H.T. Huhdanpaa, "The Quickhull Algorithm for Convex Hulls," ACM Trans. on Mathematical Software, 22(4):469-483, Dec. 1996. http://portal.acm.org/citation.cfm?doid=235815.235821 http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.117.405 Abstract: The convex hull of a set of points is the smallest convex set that contains the points. This article presents a practical convex hull algorithm that combines the two-dimensional Quickhull Algorithm with the general dimension Beneath-Beyond Algorithm. It is similar to the randomized, incremental algorithms for convex hull and Delaunay triangulation. We provide empirical evidence that the algorithm runs faster when the input contains non-extreme points, and that it uses less memory. Computational geometry algorithms have traditionally assumed that input sets are well behaved. When an algorithm is implemented with floating point arithmetic, this assumption can lead to serious errors. We briefly describe a solution to this problem when computing the convex hull in two, three, or four dimensions. The output is a set of "thick" facets that contain all possible exact convex hulls of the input. A variation is effective in five or more dimensions. geometry/inst/doc/qhull/REGISTER.txt0000644000176200001440000000167613431000556016750 0ustar liggesusersDear Qhull User We would like to find out how you are using our software. Think of Qhull as a new kind of shareware: you share your science and successes with us, and we share our software and support with you. If you use Qhull, please send us a note telling us what you are doing with it. We need to know: (1) What you are working on - an abstract of your work would be fine. (2) How Qhull has helped you, for example, by increasing your productivity or allowing you to do things you could not do before. If Qhull had a direct bearing on your work, please tell us about this. We encourage you to cite Qhull in your publications. To cite Qhull, please use Barber, C.B., Dobkin, D.P., and Huhdanpaa, H.T., "The Quickhull algorithm for convex hulls," ACM Trans. on Mathematical Software, 22(4):469-483, Dec 1996, http://www.qhull.org. Please send e-mail to bradb@shore.net Thank you! geometry/inst/doc/qhull/index.html0000644000176200001440000003523713431000556016760 0ustar liggesusers Qhull code for Convex Hull, Delaunay Triangulation, Voronoi Diagram, and Halfspace Intersection about a Point URL: http://www.qhull.org
    To: NewsDownloadCiteSeerImagesManualFAQProgramsOptions


    Qhull

    [CONE]
    Qhull computes the convex hull, Delaunay triangulation, Voronoi diagram, halfspace intersection about a point, furthest-site Delaunay triangulation, and furthest-site Voronoi diagram. The source code runs in 2-d, 3-d, 4-d, and higher dimensions. Qhull implements the Quickhull algorithm for computing the convex hull. It handles roundoff errors from floating point arithmetic. It computes volumes, surface areas, and approximations to the convex hull.

    Qhull does not support triangulation of non-convex surfaces, mesh generation of non-convex objects, medium-sized inputs in 9-D and higher, alpha shapes, weighted Voronoi diagrams, Voronoi volumes, or constrained Delaunay triangulations,

    Qhull 2015.2 introduces reentrant Qhull. It allows concurrent Qhull runs and simplifies the C++ interface to Qhull. If you call Qhull from your program, you should use reentrant Qhull (libqhull_r) instead of qh_QHpointer (libqhull). If you use Qhull 2003.1. please upgrade or apply poly.c-qh_gethash.patch.


    Introduction

    • Fukuda's introduction to convex hulls, Delaunay triangulations, Voronoi diagrams, and linear programming
    • Lambert's Java visualization of convex hull algorithms
    • LEDA Guide to geometry algorithms
    • MathWorld's Computational Geometry from Wolfram Research
    • Skiena's Computational Geometry from his Algorithm Design Manual.
    • Stony Brook Algorithm Repository, computational geometry

    Qhull Documentation and Support

    Related URLs

    FAQs and Newsgroups


    The program includes options for input transformations, randomization, tracing, multiple output formats, and execution statistics. The program can be called from within your application.

    You can view the results in 2-d, 3-d and 4-d with Geomview. An alternative is VTK.

    For an article about Qhull, download from ACM or CiteSeer:

    Barber, C.B., Dobkin, D.P., and Huhdanpaa, H.T., "The Quickhull algorithm for convex hulls," ACM Trans. on Mathematical Software, 22(4):469-483, Dec 1996, http://www.qhull.org

    Abstract:

    The convex hull of a set of points is the smallest convex set that contains the points. This article presents a practical convex hull algorithm that combines the two-dimensional Quickhull Algorithm with the general dimension Beneath-Beyond Algorithm. It is similar to the randomized, incremental algorithms for convex hull and Delaunay triangulation. We provide empirical evidence that the algorithm runs faster when the input contains non-extreme points, and that it uses less memory.

    Computational geometry algorithms have traditionally assumed that input sets are well behaved. When an algorithm is implemented with floating point arithmetic, this assumption can lead to serious errors. We briefly describe a solution to this problem when computing the convex hull in two, three, or four dimensions. The output is a set of "thick" facets that contain all possible exact convex hulls of the input. A variation is effective in five or more dimensions.


    Up: Past Software Projects of the Geometry Center
    URL: http://www.qhull.org
    To: NewsDownloadCiteSeerImagesManualFAQProgramsOptions


    [HOME] The Geometry Center Home Page

    Comments to: qhull@qhull.org
    Created: May 17 1995 --- geometry/inst/doc/qhull/File_id.diz0000644000176200001440000000063113431000556017014 0ustar liggesusersQhull 2015.2 - Qhull computes convex hulls, Delaunay triangulations, halfspace inter- sections about a point, Voronoi diagrams, furthest-site Delaunay triangulations, and furthest-site Voronoi diagrams. Qhull works with 2-d, 3-d, 4-d, 5-d, and higher dimensions. It computes volumes, surface areas, and approximations. It runs in a command window under Windows 95/NT/XP/7. www.qhull.org, freeware. geometry/inst/doc/MODIFIED.txt0000644000176200001440000000105613431000556015547 0ustar liggesusersmodified function file line# date by reason ================= ==== ===== ==== == ====== qh_exit usermem_r.c 43 2012/04/03 David Sterratt To fix warnings about exit being called qh_printf userprintf_r.c 55 2018/05/21 David Sterratt Printing error messages qh_printf userprintf_r.c 63 2018/05/21 David Sterratt Printing error messages qh_printf userprintf_r.c 72 2018/05/21 David Sterratt Printing error messages geometry/inst/doc/qhull-eg.R0000644000176200001440000000315414367304611015500 0ustar liggesusers### R code from vignette source 'qhull-eg.Rnw' ################################################### ### code chunk number 1: qhull-eg.Rnw:27-31 ################################################### library(geometry) ps <-matrix(rnorm(30), , 2) ch <- convhulln(ps) head(ch) ################################################### ### code chunk number 2: qhull-eg.Rnw:43-47 ################################################### ps <-matrix(rnorm(30), , 2) ch <- convhulln(ps, options="FA") print(ch$area) print(ch$vol) ################################################### ### code chunk number 3: qhull-eg.Rnw:51-52 ################################################### plot(ch) ################################################### ### code chunk number 4: qhull-eg.Rnw:56-58 ################################################### ch <- convhulln(ps, options="n") head(ch$normals) ################################################### ### code chunk number 5: qhull-eg.Rnw:70-75 ################################################### tp <- rbox(n=200, D=2, B=4) in_ch <- inhulln(ch, tp) plot(tp[!in_ch,], col="gray") points(tp[in_ch,], col="red") plot(ch, add=TRUE) ################################################### ### code chunk number 6: qhull-eg.Rnw:86-91 ################################################### ps <- rbox(n=10, D=2) dt <- delaunayn(ps) head(dt) trimesh(dt, ps) points(ps) ################################################### ### code chunk number 7: qhull-eg.Rnw:102-106 ################################################### dt2 <- delaunayn(ps, options="Fa") print(dt2$areas) dt2 <- delaunayn(ps, options="Fn") print(dt2$neighbours) geometry/inst/doc/qhull-eg.Rnw0000644000176200001440000000603313431000557016035 0ustar liggesusers%\VignetteIndexEntry{Qhull examples} \documentclass{article} \usepackage{Sweave} \SweaveOpts{echo=TRUE} \usepackage{hyperref} \usepackage[british]{babel} \title{Qhull examples} \author{David C. Sterratt} \begin{document} \maketitle This document presents examples of the \texttt{geometry} package functions which implement functions using the \href{http://www.qhull.org}{Qhull library}. \section{Convex hulls in 2D} \label{qhull-eg:sec:convex-hull-2d} \subsection{Calling \texttt{convhulln} with one argument} \label{qhull-eg:sec:call-convh-with} With one argument, convhulln returns the indices of the points of the convex hull. <<>>= library(geometry) ps <-matrix(rnorm(30), , 2) ch <- convhulln(ps) head(ch) @ \subsection{Calling \texttt{convhulln} with \texttt{options}} \label{qhull-eg:sec:call-convh-with} We can supply Qhull options to \texttt{convhulln}; in this case it returns an object of class \texttt{convhulln} which is also a list. For example \texttt{FA} returns the generalised \texttt{area} and \texttt{vol}ume. Confusingly in 2D the generalised area is the length of the perimeter, and the generalised volume is the area. <<>>= ps <-matrix(rnorm(30), , 2) ch <- convhulln(ps, options="FA") print(ch$area) print(ch$vol) @ A \texttt{convhulln} object can also be plotted. <>= plot(ch) @ We can also find the normals to the ``facets'' of the convex hull: <<>>= ch <- convhulln(ps, options="n") head(ch$normals) @ Here the first two columns and the $x$ and $y$ direction of the normal, and the third column defines the position at which the face intersects that normal. \subsection{Testing if points are inside a convex hull with \texttt{inhulln}} \label{qhull-eg:sec:testing-if-points} The function \texttt{inhulln} can be used to test if points are inside a convex hull. Here the function \texttt{rbox} is a handy way to create points at random locations. <>= tp <- rbox(n=200, D=2, B=4) in_ch <- inhulln(ch, tp) plot(tp[!in_ch,], col="gray") points(tp[in_ch,], col="red") plot(ch, add=TRUE) @ \section{Delaunay triangulation in 2D} \label{qhull-eg:sec:dela-triang-2d} \subsection{Calling \texttt{delaunayn} with one argument} \label{qhull-eg:sec:call-delaunayn-with} With one argument, a set of points, \texttt{delaunayn} returns the indices of the points at each vertex of each triangle in the triangulation. <>= ps <- rbox(n=10, D=2) dt <- delaunayn(ps) head(dt) trimesh(dt, ps) points(ps) @ \subsection{Calling \texttt{delaunayn} with \texttt{options}} \label{qhull-eg:sec:call-dela-with} We can supply Qhull options to \texttt{delaunayn}; in this case it returns an object of class \texttt{delaunayn} which is also a list. For example \texttt{Fa} returns the generalised \texttt{area} of each triangle. In 2D the generalised area is the actual area; in 3D it would be the volume. <<>>= dt2 <- delaunayn(ps, options="Fa") print(dt2$areas) dt2 <- delaunayn(ps, options="Fn") print(dt2$neighbours) @ \end{document} % LocalWords: Qhull convhulln ps rnorm ume inhulln rbox tp gray dt % LocalWords: delaunayn trimesh Fn geometry/inst/doc/qhull-eg.pdf0000644000176200001440000061076314367304611016062 0ustar liggesusers%PDF-1.5 % 34 0 obj << /Length 1093 /Filter /FlateDecode >> stream xVK6$b>t=@6R+K$;ɿ Iɥ#jg& )E"zlҏUuzj,Lsy86n׿=K#V)ʓb ʌށ5ܓ>/Q " D-2RԟKᔋ[pi5""n0qG*/#6ڦBtۮ=W]W BO걂` v B}#@b3A15؈čM>\RayS`һSp`_zG߲]}YLZvvAa`6BX_,YbOIM?~4:a1?c_Fk0ۮ?,(R\W0Մ;@'\Cm5)a ݃{ ܰqj-Kl81X4P(UQV\F]ETKE$0`v^_Ds3iup-.ŋQ p ,)aD%p:/A#scD2 o TϚ?B\ {StNrۉN 6)a!|!ŷD(Lyma-..a@JiׇȶU/€C9+'apk0nYΥ$ܰۢ+rPsvn_krPD9M`,4pgolIxXFկ!C()g>eXM=[ga޾p E|a{kN **ͻDr;E?l}ӻƊ}ݎ ߠ!&DCgKŭ~][ endstream endobj 49 0 obj << /Length 769 /Filter /FlateDecode >> stream xuTM@ WD+̎=XnH8t{(iV)jd?x26a]~c?n1"uӨCB0ma(T$C+Tḭ{xQyjvdAZ|'!$G`L]*d\SЍIl8 BQ4S3aql~Q :AZf9EBXX&XG]uƧXΘhg1Wh}^|<˂&b?3cZh%O 3^Qm*gf+Ndzυ7U1n{ ]s#XeגY.U 0#24i|PN=InU.v?C;CmM}3LƯ,Vz}h,FfKpL$SXo1efʙsZ|\swHi4^4yYm KsS9C4 Ps4nKW> /ExtGState << >>/ColorSpace << /sRGB 54 0 R >>>> /Length 1466 /Filter /FlateDecode >> stream xXK^5 "  ѐc *Tf$UW!J9~QՙEslq'w{颻w.;WKq$ߓKs_os|?o^i7._.ѽĿK$Ź+Put_7 Gwߋ;>5!&)}|3[r.7j?-u-*QÇ dHTӏ5ľZ%籸دx$kئ׼{[֒Zuoײ$(^=yp oVI,n(~6%S޺/hJa;-KhGh*Z>EC\>yp oVI,.%V%S޲8Wxʖ!1* (0ŴŗE% nfst5 MT.=k$E?nxت׼{VPt` >5(Ő &Wk xpͥ囑Xklk^Ā6{@ >bQh%wlf K;htO{SdŒ;@L%j%mj[FƩZ;$ZHeRЕDW z\K-<1!-*[||3bkzKf9 2qfG`tU$xYIMطXY5. oAb-My)w[ׂ[#u%9yЮ&N q3|p U;KRd yЙoFlmUy d~.0?wQAh8orp $ X6Sc\PD獺Z =9s7"|]}H0qG[\d߂#)H,/>Lxƕx|zAIձpØ^fÑ2Ta p38R j ?Pwduj1Ju8og+w{]sFNNNivr|(>Z߳^q/RјƐb5=izvsf p3Hfp7l0Ї8rTȪȽ[՞mt^Ųs>Tc8l?Hn'O($R,>}uX\Jmyf÷w2P?{$4pI şv#9>hDr|3Wg5!}rXu[  endstream endobj 56 0 obj << /Alternate /DeviceRGB /N 3 /Length 2596 /Filter /FlateDecode >> stream xwTSϽ7PkhRH H.*1 J"6DTpDQ2(C"QDqpId߼y͛~kg}ֺLX Xňg` lpBF|،l *?Y"1P\8=W%Oɘ4M0J"Y2Vs,[|e92<se'9`2&ctI@o|N6(.sSdl-c(2-yH_/XZ.$&\SM07#1ؙYrfYym";8980m-m(]v^DW~ emi]P`/u}q|^R,g+\Kk)/C_|Rax8t1C^7nfzDp 柇u$/ED˦L L[B@ٹЖX!@~(* {d+} G͋љς}WL$cGD2QZ4 E@@A(q`1D `'u46ptc48.`R0) @Rt CXCP%CBH@Rf[(t CQhz#0 Zl`O828.p|O×X ?:0FBx$ !i@ڐH[EE1PL ⢖V6QP>U(j MFkt,:.FW8c1L&ӎ9ƌaX: rbl1 {{{;}#tp8_\8"Ey.,X%%Gщ1-9ҀKl.oo/O$&'=JvMޞxǥ{=Vs\x ‰N柜>ucKz=s/ol|ϝ?y ^d]ps~:;/;]7|WpQoH!ɻVsnYs}ҽ~4] =>=:`;cܱ'?e~!ańD#G&}'/?^xI֓?+\wx20;5\ӯ_etWf^Qs-mw3+?~O~ endstream endobj 60 0 obj << /Length 890 /Filter /FlateDecode >> stream xڅUߏ8~_ɑRӇ۝rܪ o6)F2aqsؼ]ʈ1+ţCĄ"Rʅet$mW%FtXHA07} 6ؔUPoqzm~4jR$ *ϯjXJG95XO1˨R_<6}X)lfT%2S]a:|yJ6*g{X衏`Bye*4}sІ-c=vc5F(EpS)rpuSR"sڐ4ey\¨KFngazCel'k{WY$?@_?K!J6V0<$f/!\s%d}+9FQz7|W7jwauѷwyyw zhǀ1[ UEr7%_n>68&I.Z/i߆jt3ץu!n͟R@ozI*^I8jW۪5Πء15p7.8 H4c,Si[J5p)=n ^4y"U*Ty]<&4M|)x83΄awb. ޾`JSS_s)^2T> /ExtGState << >>/ColorSpace << /sRGB 64 0 R >>>> /Length 12671 /Filter /FlateDecode >> stream x}K-;nYػKzLH 6AՈ#~J뢻a;{%YZU%QE_뷯y诫}j~]Uk׿__?8>^?o;^׿yQ^5r|^Z&9Wmj_%>‚L}%چݼZ󫞉m#ѺIjmvO{*]=p+f=]=&ρ3q㫝_eijRYCn+/|U{累T?ujˤq5׸6({>_}&ūٶKnK;ysk>~թ~icWr/6(xۿƦVے.aw\A#<\_|Zy\_G3{MumXQͫř;tj[r%n7>S^u63}KJw<~ow۶a XXSͫ$[AjM"n=z|'W{u7{UoSj%_φݼZﯰ=nRm[.sWW9whn_c)v=eɯ+ y8#~pcg|.ݤx5۶\v ۭ'V+1̦{tWx1&4ˌ%ęV\*FQȸ6)5}تG1߱9qw oGr_D_,AkaM]CN{&m[.[HA?]|;g|Ջ"/=z[Q|{]AuLlAolݠ.b?3aQ̘˴`&q"G3D+wJM2l pQnxgJ-6?#SQh9&R~[4Vtjq$vm&ūٶKnu]>]9TLEeDO_|d|ujqn̮=tlrY*n7e:{p;?3;Z_aaG|W T3F/D@+.c!JMIe qv99 Ϝ=ͧXOgLmD}]%$VT]nb t?l[r%nU(3.19p~'phڛS-m+iLN,tȟfۖ.aw {3kw%/upH5J)~aMA7Ƭ"%f?-"5 #bm9GftFwzLqTm'M=IAd]nR|ض崋O=11/p豈q}~aSg}CoMaf/b))‘V1"` x)۔٪Qd~sq ęf^PE'^LQہɨkn( y&ն岔:0FCS#[Bh`&1;|΁ݎ&́iXI}+Rm[N'25ϡ`9qvSX1ؤ['5WIMٶr,c1Iv-tcjy/ 7tLtfU2VI!nU}qǔqL"6gi㈎ t~Ws`|מfۖRaw4ƧS4@C=t]ϔ#ܖXQm04VT"N ;Ⱦtm[NvtT\͹׈u~Ա0B=ƚ;Q[wKm7w~moxİO)rA(3'[[sҏS]vw~,qƶxR7(Vے.awu/AE _01dڋҒ.(fZVa_IQ7۶|Wv[m%o,7À`0IC%eT8ЊKh17Ʒc 0G`>l8A Aic9ݠXn(G|;s:,)T){unru@ٛC߳^^ ϾbdQ5ҚXSݞ}r'&a.ٻmev{{0dRCxbfKimeH#X)/k8BjN,_ K7)iض~U@o 4xg1+ޏ%o5ݼZ&bO,Z7)yGے.b?^9G/q:cFDʳGN) +ag,ݤ<{] NCrs)vr܅M7XpSfۖ.aw=%kD;G ='Eby!IMYTE7sJ=#J݇gٶ䲋=ԗ=4o tXo('^VDۃ78$ [18-v[2N"Nsq{ڛSrv`Šn^-΅ea"Mۖܖa]c;ZN΃L([myL9SW/܍9ckkm[.sa+j2xj?jO  k9kEؘ,ݤm[Nv\.iwӎXb=NWvSAqĊnO_Ijmv{cBs?ُsbĸ.1э3WFcLcM h0 0Jwg%]~pfz7L4d#^oK Zjae,$.q۔.b?^c"s7{Y˔KA)*s#%0Xwzs"n̩zq0Fčo1~D.z/@H-K_ؿ[w/ٶKn0'8\ɡIN*z3^!R{aEQ\\# &M> ۶\a3OdwN{wHxܤ5_X~#|gAL!|cOUۖ.aw7?#N+Fa<' _J??8I劑9V}0[+FnrenVYocp"F,Ik̇TN҆u8LfC ^j%UI-AEr¤?u$a> *L!9˞( k/G> mOv aXt(9 &E$@ހ((OLm-/ĚnO)$ň`wd>c,^璟p u抑9$rmK.^^%|/=3ybk)+f9ؕ!,ZR(^-]~ؽwf6FKj#{4&<gz/ke-RnK[TF[9toÏ#RkߔX5Vr͡{k,]_^ۖ\v ۽_Z83M){'JȒ+,c_XS-)T|ƖfR7(O"n}/8yx2L_n\N ƚ*vI)Έ-ΨVے.a?^&M:^pStnFu$Ms҂KwD-v}ɠ%峻 kܜaYbm[f)]~~ s#r4/T#)[]8cf0HZnRYCn ɁS;~PEq8>9"Ky8=Ijm,vW)ĝ}s /^rk]Q yt+JmK.v[z9Oc6~sgχ'ے#=7>h;Q/e˲meW.Qn'SՆG'y;?O3^Kޜ-Ns#kuڹmmvƫ e#8f]%v.ľycIQ7{nY&ūٶTnEgd]ΔTe tt\gc]XSmEk~yʜ{նKnwܗ2qJMΉA ͐V%g׀=[`ɀٶK]1{H.\-S1,pf,a9bTfW>H%զFVϸ#/қsssBϚܜpS|Q*o XQya&l[r%nK|Y.q1û3f}w#r2VTnL6e{=,@Wntۖ.awW5 s\M^BXQ%@9),n^\v /Lk6%Ήpf{wRhGzaM&[L3wے.b?fnD_D|3X=#n{HGX$(#) Ƒ87%\k Iks mJJklu {Fom5g e{aYf_ZO)s&,)\L"snRm[.KpK݈1sN<象%a?׶F-0iV魻zڟmSn{Nv+F;&ӽUW]9aMacb&mKnThr>Nb^؝V<[$YADonK%l YC܇k?j Cc0 q7Oϸy/@cebE9i(ltr|踖\v =Xڣ8Q@DG^_%=>x5BKMꜦH l> &HU--%n"Ȯ,dov#=f1C;KՊ`q؋Y-}j۶\ 䐽.qnnȸ񺄭U }J__o,+ߚ\𜄝fۖRawA"=A1S>(;ipTkJ#3bM\jK٦vwTV,R9of=NRR\B/xc%taM3 &m%]~pœ59Ypz#3agc+`bE XJ؊uu۔.b?춧l΃H u|dZXR/8gtl[r[J[MNݣ{rmZSԭ;M=I\7GYTedLm3Iv){k[Y-zm7fYF_."ں!ƃLL\^08+֩MIe qi֐9xʱW7HC~"joSd9)؊D`뮙r-]v;xrJ:X^B~1DZ^ d_ܳ$s%Erۖ\v PaвJXү¯: E`6rTL9\oxnRm[^\T3ۦu;㦘@elԷ|ї $&ux1- K7)l EO+]$E$gQcrք3Vԙ93m Iw]n[r%nǶ)Gn5QAR|vš:<j&AhRdn@p{ X$Ő Uũ֢8M{mXUԶݡ 6x$(J!ZV4g2V)p%^T+_VTT傪u\tے.awצȒ`)8m RxOf[saM `j,![VxYmKn[צ[%% 0`#L |C1n+ g"\ PZۤT1_D{LFt`w RLT>g:npXJ[M"n0j`ŀauq|cnYXr+j%8S{0K]NٶKEp窒4Hf??A+)5U3\a36/ݤmޒ.aw}/ȸls &x95g&;;š:$g s1bw KJ0y=Xs̉]LE`ZI$MgɹXRmAQk&ūٶTn<+Mq2lzLz@[C!I 'T1ʎW1G֚RզVnqaw9PanYLELHr*g"u=ugj{-vۡl8ݱP:9p?c;\QF=LqxZ7G]aEA7=GbcMWme Oᒗf`8^XJl&4nFMa0nsi%mJJkUHTc"'.t,<c:H#F M!DaO0*?tNQ6[ r=C ҴhGK1IqrcE]*¶mg1ūٶI8A"ފ(] 9{8e&i&%m>}lIܞqbRG܉JB1h [܇VC531.becuƈ@2[LqF2Akx&V\f)AJ7kz:kNAmYGz"kQKްg j'"Ԝ3mK.>5,z.x@ڦ_r5/}rsDŽ]=/˶%]v;0̏{.isX#A >-'e10&ҺJMIi qNGDA{15jk G~k7Id9 y1AJ틻N#2kՒ{CRXp(LxRKsT!}"-ݤbmmv{!3:s[SS^ϒ3o,)RYz.ݤx5۶\ %\冚,77ܐ-H:ƞIU$؃k}*j[r%nJ ͒T~5TP˔XrԩHɩ+{[QºI9=`KNvĚʑ)>#Ȗ Xlal˳u1q"}$5Ij-n};sg,U|[Q4g6;cg,t%u1sntž'nRmKnK{c9&R b<# & +s>PMIyFm[.vÓi"}knHf/%ujqPت}\MWʆv\+dsUԏytx<7Xk>g@,nQm[n!x&vžC.ntGJ'Q)~L#9Š:3`okpQκ{ݶKn}'zƹۡD3rl6;,'%EݼZ{6.ݤ{FۖRaw 9UkTZuN79WϬ5:;δ:pe3ujq*7 ֽJmeW&Iן-p8L7ᗨJark,)\ÝEQ࡝Raw't:T"o쭨Y*=Wk.ttgl[rEk)Q:19 ~Sw:ݔs 3VT̘|6E^'3mex}999GDpSkϘ%Wrr99'botnRaNvc+bK}vC>p{\M[΂vƊ^K΁Bϸtlr%n#<@ ~Bl%WQjc .g+j䒑{!7kmK.21ly/zL6S4}|˫g1'5Tr [9LKLvےwB+^u7sp(W MLJ[L1 ]pTc^a߱u!]~ؽ[#N U.j4@5R OzR7. U6n'PdےˮƦ D<1@} ̓gH4oR4l3m;VڼmJJklϓ8TZymc-dU0SrXB5uSacUAO[FvV am-KN8SKb 'oQq2$f΍7B^}gHm[N^⃧@]GN+n:W1ũ+kZe55r2˛m^v۔.b?^R#tt>>7p.3入DŽ577numev{aq($&N+`d"_՜fjZY@8'n^-,v'ݤ-eRb?sy>v<|I'NJX9`_K2cK%=ZpNCZeږܖ*s{PAߋ9OyϬ)~f9ϝ1u`QKbo\Էzr,,v{+H'`0*r! 0`r1'}x{R ${mKo-q H?&0o_//߼?(gB ][맟IqtW \vADR r >4/mJ W ~/T^_HO7p:KP~?ᅡ?~G2ϱ|م n0P7/RJ o39d15" |@~S_&o?EG >'DcEOoO>>dpr?7O/vB!%Ԅyo~g7[-0.jꚖ-ڏ7߿~_p3*$fD&ʿ<,q0>)zkx*LNfxt>3ۖ\v |{,S`xyF =k=ZQ+X+h}&6*nRxFۖ.cwL ǹS<+']EYƒn/<,zz`ݤx%v;􂤞XhJT5;Y#!`fH1mK.{PҊR,m/(6*5 uKgYTqjRrxԯS,ݤm[ 53C~ MD) Ob-T<9Vfm0lrE묄;NtV`/e|a'Y+f/kN ld&ۡva¯Y)0ɹQ2ANlj%)KkK$WaENIil1ݝmw SU%úL8VRRv)x)GĊ:s>glGQh%]v^ FH~|ΙNdp>wnb'OVTu<99[tۖ.awsA{<Gwcq$~;Q?'oj^Yؤ's׽-ݤ2\J_^]%ӫ5@q_Gq{dRAYXS#Åfcl5mmv{BVӘue़ ~%oXTͲJ wconݤ (]~ؽul88Hh _56w.%*TOkkj*=>#Ԡ.ݤ'Gۖ.a?^s1M'-JXԮӔb1,Z>X9@XQƗt `rYweے.aw<rUrNn01#y >;wPa["JAS Н;mmWynLlaab;rkEHU2\TnHG˭ֽVmev;jsn%/sÍ0K>m%$ endstream endobj 66 0 obj << /Alternate /DeviceRGB /N 3 /Length 2596 /Filter /FlateDecode >> stream xwTSϽ7PkhRH H.*1 J"6DTpDQ2(C"QDqpId߼y͛~kg}ֺLX Xňg` lpBF|،l *?Y"1P\8=W%Oɘ4M0J"Y2Vs,[|e92<se'9`2&ctI@o|N6(.sSdl-c(2-yH_/XZ.$&\SM07#1ؙYrfYym";8980m-m(]v^DW~ emi]P`/u}q|^R,g+\Kk)/C_|Rax8t1C^7nfzDp 柇u$/ED˦L L[B@ٹЖX!@~(* {d+} G͋љς}WL$cGD2QZ4 E@@A(q`1D `'u46ptc48.`R0) @Rt CXCP%CBH@Rf[(t CQhz#0 Zl`O828.p|O×X ?:0FBx$ !i@ڐH[EE1PL ⢖V6QP>U(j MFkt,:.FW8c1L&ӎ9ƌaX: rbl1 {{{;}#tp8_\8"Ey.,X%%Gщ1-9ҀKl.oo/O$&'=JvMޞxǥ{=Vs\x ‰N柜>ucKz=s/ol|ϝ?y ^d]ps~:;/;]7|WpQoH!ɻVsnYs}ҽ~4] =>=:`;cܱ'?e~!ańD#G&}'/?^xI֓?+\wx20;5\ӯ_etWf^Qs-mw3+?~O~ endstream endobj 70 0 obj << /Length 807 /Filter /FlateDecode >> stream xڝUKo0 W2P'8t]6 ]^&;u,N6{QGIW䉣N ,@V Prܑ;qqf9G1"ӌ+# g]’s`i{=Wsр2<\8AIVŗՠ'b4Ef(\fϫ2 iVTxSAĜ#7yU&&GZM՛*?:Bς(G-Xv9v@5l6;)KƵ,kCji&#FA0{oSaɱvo&:{o77<$ ezD@HIt۲xEЕ]XEw!on_ UI7gp^Yqm"lp.9r(32!%nj-?s!&ʍ|Z%N"wzSxa-%JS¦h>f/Dc^M06UpelH=Xa0IooBTw~~jOպAx 7Oa4>Wngahm`SkwFgB01 J %`ƒ21=@YN Jp9!" )l,Bɭq ZFD8^ST"rE"ON59QNpġ+ endstream endobj 67 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (/tmp/RtmpcpzGjq/Rbuild58c3f67ab0d82/geometry/vignettes/qhull-eg-006.pdf) /PTEX.PageNumber 1 /PTEX.InfoDict 72 0 R /BBox [0 0 432 432] /Resources << /ProcSet [ /PDF /Text ] /Font << >> /ExtGState << >>/ColorSpace << /sRGB 73 0 R >>>> /Length 876 /Filter /FlateDecode >> stream xVSA W ̼-[ ҒQAZ(}< (fusl3s-ͯa3h90uHϛmxٜR`1[8A=dpb81p`B9`!UX>*J.OJk"G)m1%('/P f\E`,ՁwMrJ%e댋Ξޭ˶.({\6:%}: ;J$KKȢ+<#$#8âUı-_ӌ-Hؒ]>nIY- Epc;]87{֮c [- KZ4c b~KguZУrZ U 7.~Tzހaؾ5Fu.VFT)FtgV Y-!=iΓƈەKmχVZhH. [s_RZ%Y G:k r= 8Wh>O*T1PUq,{@SRZ|G*f<3GBi|ЅZE[jsY؂-_RZgZX- @Pl걝48B+7Sۥ4b3V֥ڵnsZ*wƔΦ%W菓 y0_yVZ&3. [rw֥ڵni?N@4-twb^NDD+US%gJy[kխ|iXvsηC9>q躠 jw* m5-my$lRW׮u> stream xwTSϽ7PkhRH H.*1 J"6DTpDQ2(C"QDqpId߼y͛~kg}ֺLX Xňg` lpBF|،l *?Y"1P\8=W%Oɘ4M0J"Y2Vs,[|e92<se'9`2&ctI@o|N6(.sSdl-c(2-yH_/XZ.$&\SM07#1ؙYrfYym";8980m-m(]v^DW~ emi]P`/u}q|^R,g+\Kk)/C_|Rax8t1C^7nfzDp 柇u$/ED˦L L[B@ٹЖX!@~(* {d+} G͋љς}WL$cGD2QZ4 E@@A(q`1D `'u46ptc48.`R0) @Rt CXCP%CBH@Rf[(t CQhz#0 Zl`O828.p|O×X ?:0FBx$ !i@ڐH[EE1PL ⢖V6QP>U(j MFkt,:.FW8c1L&ӎ9ƌaX: rbl1 {{{;}#tp8_\8"Ey.,X%%Gщ1-9ҀKl.oo/O$&'=JvMޞxǥ{=Vs\x ‰N柜>ucKz=s/ol|ϝ?y ^d]ps~:;/;]7|WpQoH!ɻVsnYs}ҽ~4] =>=:`;cܱ'?e~!ańD#G&}'/?^xI֓?+\wx20;5\ӯ_etWf^Qs-mw3+?~O~ endstream endobj 77 0 obj << /Length 229 /Filter /FlateDecode >> stream xڍ1 E Plkvez#5 3y(*xo#'-zR,)`ΆI5f.m9z+apO @43l+8>@N})L5|NG"q4xoFS;hp}qJ9耠\X\R WjL<S endstream endobj 89 0 obj << /Length1 1409 /Length2 6289 /Length3 0 /Length 7251 /Filter /FlateDecode >> stream xڍwT6ҤD$4W 5$!PwޛRދ4 RR( _xZ߷Vg3{?ϻްy+g*#^P@A> Pq'c70\? (()18P @"⠇@ @@n0[@HO%=|; E `8@ qv G NI )vA!P\0@ܠ_#.? `;;0ƥ#W n*_AݳpQŴ8Bf9P}|*+#'ο8 NZ 5]--G0`8JBahe q6  >Ba1, b8Ap4h0/ ʼn}(!_Q('q0 -7|pC~]4HclyB\ۿz@!dDcuPq;ʐ$v걉P% [ij1^?+sJdHұ][8{Ӟr k!/=Mᕱ#xύYċPx#);Hel)Ww~Np)V~ܢQYI$fɦ`Ex~gKQ)w5$N+(zfD찅`$VͼqS䶙NerSvӶd+Z{aywgbCD(7 3rF>a;J+p6L+959pD#ECOxo>unv!+xh36|_9gr :HΈ5P ^w{ +tbMcʵ"M]N~ W IR,zWx,Zh |ʰ-.>QD!/7#M/uk疜ӾA GnbnɐXWnIۋ&Ǘ|pC9EO]E8SO~ϫm2b0eY ޛӜ1htE-7DrV1}R,O'k澠=#^ V6+KKխy&ӞwY/ge]#+u⸂ٷmvܣ_J(Lgu&h^[r|\h_|d֔ m]d덻i6Č蘸Sm؞f~C > ޑ KlŠYk[hgVpc㒌a| b\1i!1hϞ9>O4䉗6nQϜ yFP|3G"22K <>֜Z(qL9#_^R:.cMf9YwfmI 3(tFs{fBU ju'|ynik (ZiH#Xq(N*lî^rQ>yK=~dCiteuMee^64w8Nz6]YNBeB'i53r#I9 J W^7d R@ڬ8Ts(qF8>=)FE|Omi/J5bCb<8 p(.ډ?Bڻ⓺ϤUF_|26>tVscݡV700c |j\g2#C UFlv9GCʸ!H._ _wXel"[ag~ |Ov" ̤d>U56IN U[)yu\2Fk,'<}N\3Z̠]ju';5Y3;z)|-jZQ./)\_p>EϒIń=c>{j@g/=$DHJCZ{I0|$W[[|U_p=~}J:Uχk+??93R]d;Ӡ1?x> aV7Y]9Ӵ3ͣz!D{ED0evԈ? ?4#gx (r& s&2a3ѯ> B_lrʇ#$`S2{KPblk3T9[+rM|دO~*#C'"@ ?Y~wzsgP||O^*M=Ng5X]"ge~x~ u>qN=U Iz4A .eSz5{._3pf{8&'joHuY8|rhڌˢ-o{\ŅPl?rXR1ixx;AomXBT>% \'{j@u#MTBTtۀw_kfm̷cv C^N4"C_RrNZh- 9/IrSi+Oe|8G ;aD."[U8dnn.3Z'e-ɊEIEj& R,0(J* 3' ^|m^hh?oHl.*Y S\{{0oPjU)N9o[rUS ɬ*] ]z48[3&Qq uuZrCX5'6;jg'Q*D$y+r\!E4?snPfkzҞݒK *hԜә9цUabxE/@ypks{m5zQ(I/󙟁7F2$)aAn{#_KV)KU ƌ <9)٧(~ `S祮Duֹ(#9CўnD,7}RS,\goPB/. .!6hއg #{% QIH%6aZ%5yBNgizxzλD.=X.Ra){$͊9>|H&ZkK!⮄=}9};wKIǨnVlul՞-;;c`_G B2J~Gn,:ݛُ=V'W5'd$y8f((7m3+N|闹uTo Դ=#Ei+HY0augX#9IiBC"!u=PeS(C}#-`I]aj$X/mm{P}/,i0U0m:,\W(0:9yRDS?ZvK]&PI[ {nNiI@*M!gEtHB姖Dee(Ƌc2TLlb\hu/&|Ef@Q =V^>,]>9YUO_F;0 I{<U?XJӁ:~MWjƅSlMЧɪ1UNȇ+z#,Rna*h|*M\0.F󜥚K/\(9@>J?Z3qh5zF*'ڜ,5Fʹ#;j0e),3;B%., |狀U0z9._L|<՛II_k@Hϼdj>}Cca{# =IoEYoճVw6:5ez}iM+卉b|IRw+kߣ%J{A#GYY)wNלP=" fJjlK (&st|&)?~'_ҷVjO[$iS@DbF*1:!L#;B|:l_~4؋jXmkO Ɓ;0^b] B/Ѯm"]qcӈ8:M[P|eͷE޷'\/'1Zӳa>BD5IG\_J%Չ'+yk.SZ1'nNMq!M6Pq9l޵rW/S{t[pCv7ֺ&L_5nU[`a@h\--)T!>F=+ԊIjtL {(wݳ1M? (ּWCLu1cr >zf0^-_-4f*G"AߒE{?ϓ+Hp2 Xdw/h="B~|%l* ;ճXO!!2"r %E֧)|ޫ%PWkMA5lA.yaިYE}2+mgs,kvʻ=vN(%hR/VG7ugׇq񨐘&ZtO~y>.xho(ʛRrSAXg|(h09#;孷>8ę+fj}? J> stream xmzspfetlaǶmzm۶N:ضm۶9߽woj?k^{=Nz:z؛213rTą5,p"FΖvFΦ S=  bghin R;P62tY۹Z-N..-Lf֦E-)y @blm ZMmLfv889?lrbB 1U @Vr[g'Ц@cMG p,' `b t[1K7)[3;&.MC~4v71Pػ8:LLmv&1wgS[Sl,=r6G![dOID͌q ϿV7gcRP5wZhgbikPqGQ#G ;hd/rFΎF- 㿞Zz%,lEcab0s|/@GGS[,N@%; OUZShXL9$̔d׵倅%*nt1Ŷ]#Q)D4qorsߪ7p+uI~ ؄obCuY]܏B5dBBQ6S6y_<Թ.r{+Ua2AzT<6K@ a ^=- A.6}(݌4/249F`Ox\lHZLC2fеeJǥ|wwx-cOHxA=ׇKal #?X)A\˧(fBݬ ?jW_3du΍WIBBp R/Jܘa-E^RڋR\ܫ |iATgeи E#qQ5+7Heq-MɊŖJu1O[݊“f~hK跨::?A+B%Clq 6&nYU/ HfܛrdTlaRC&LaXHI+65o)&?9>4?Ar+k0UF>C N,i8XxBQmhp! N9OVm6mf;e3v8VY56/O#+jQiMxȿ,iVj1E!kJ0-t؎Yg=mvRƳfGl\US+)"5b_u!XǫGFA6ud)hOezY(5^O7 *$ "'-(ISY9؉|BJMԜ{,X9(nFu Rp?17uu,RL&rsȱUڊˏA8/oD1@KttGytmGᏲnVYÙJ/1m`G槗*iyQd!8r _-efSUsX }$:PG/i~)-B"h%GN0~f+H},g?)#[j g8Ajh ?+k`o [>޲MgoRla;xP8'lNF{]dˠ7uڴoI1 > @5i&Vcыfihc=X[eCZ+Yx5a[ CZ45C;sG;; ʙ 5or Lj./4)r].Qֱ2 '72pO9^{]n2׎BvE끌🝉{HD*SֿiZsdæ9r,"ٲiF!7Wci}e ͍ubό!Ƙ 4GĊb*,P0?.bOˆRTzRP0R CbL10pKNz s+RԮdC8ԋ-/׎ q~L^V7Dբ.7z5_7Yok-hI!πձbGYk+|3D,Ez܌ggo ͦ?idH%ƻ<%5$6;@d$w:ejf`aTy4OEWVG+e+*~STZc '~46@MلZS?2n$ }C2y0]2myPz0?ڪT\K`yJ-TKk6.+a qvfh?'q4\;03#m H]x7 '&LŤy?LIaobhѯ"ԲECfmӁFtE 8,WY"I;5由Lʙgk+qm"!1%b!Yn߬i>!V P9ЊXrGS&_yѴCHcPm2BLc]:X>M,DEs=HU*;cs.¡,^ OZ X||⬚39fS V{QI8y=]Y0(CdbKEyZ+DT jϙ)Ѕax&lF:rt#W#8hX8jJ;G(7V= ([yؽj`fJYUhB?}Ʋ^'S y2tdI^ˊjOf10;q*&QMt&i&3DpORY%Pوttv߆%hWPGW ^$^ ns5rMҞM [|%0 8wI8Qݩ;mWJ ~wyDyܬ`d6?Y$%jҧ#M>~-'LEzn4ڨߩĔ5p] ǝc',d\]+ONBG+T4aF6wenęnCsx ݽZggNV<>?x/T=orj|U.Z3fe 7 /Rg>Mc݊ɶ[2{>hJhEATh L17^W2 J_ _K(P{fSNtW\q0.,͜Y*[^z*е<(9R:B(;qE1 .z8Ŝ-'lG'cf(ul5OyBPq /0T :/)>_j3sZ[8%?ӄ^-'bdhZJ=q s3 om˅JcSohp cuH2QVBGZv4@WDOl9r5~)(U8CF^ՀeE+Cm6"I`^~ |0&HR௓ E,y>xBgq% s2y4r=).Vl,cO\]åuU'6'R.E\YXBJй>T8ENz&|shCy _PweYLMnHښ̢qtaCEIT+kJT0v}d 3Agal",@&8Hi {Rۋ^IiigrR9uV;_/(+?7gFcUSҝ$qBSCWl)[w_^sRZ}铎$;qu7޳^$hGC`6Uu / 2.Sy-vi )%/@[Gcm[>Q:Br14V Xي2R >¶]S{ґ:H-G]8kjEM^xe40/ka, A;ώa@N+wdHЂ`haׁ`8eZԜ;0(q{wP{Jw> 6]$Ot0W>^2Йqn)w }1ɖJǍfx16W;  >v11{+h⛪ o؆^|*4kcRG/0 W?|-G?nc"mlvgAr@rff6%5  hP# q#XT |Mq/׈¤wØ=eW+`9'ߴC;baZ;A?^P(8i4B^ۿAxwF۲ɲnD8i0 z˘tՈ.϶HSgpɭ i4~4Hsfo6m! dpG༂GM&nRS0= ;gb~UWOJ۩n~SW`g^lfos TJfߐ/!(Pb/[\t{]^,^Z5 y 79S_*d{jape0Yњƶ U(J) N #tX;iJnOJU?X& ;hg{1a JD *#큱ItI2cYMJ3)16C{ ewa9hR4GeEc+S<O*`{*yv}j[)%%&'O/Z0%)^Z6c".,2n8kAp `Ǒ. YatPEHӤnݠVDM>GIqiy7y3_דAԚ^J3gQJ>F@&}.v{f+2[8 [;jMNr]%z+yR2#j*Jس˘x ӝeȴ(Jwzrcz|I0jH<ԐVo54FT IHgC0MQ7wc$wܑyV|߈ LRBaZg]rXS*ϩugy&6w;d|Yܭx288~,~eiң 8SթqY{{ Oi9'g!=~*k⍛/AROiAoRJˉfAźCXsyjܫ,By q`eRY~df.K.I"ڬMYcln062?|F4Vޡݧp@dxOyl{!6sg#W44k(L01`~͟3yNv8-5G>Ҹ2ѶVi=RPm{7Lshw(Z#+#H+$+ :mލ-Qj_[ZަilJsҪ읔n}AoFPikգ{ߍS;@F֥VPp*{Bd+|B`|@ASXHiK)%!dC)F LZ*ύy_R.olb(+U2* r{ OW`_qICqvsD;&pGh=86c^և0 ;m jJ\vu99+tew78Y̮LV$Ef3?"-G{i]lZP50R#4pl+\6IW. H-IO.8\FK"yu.Z+d1nY[EyCRϊz5' ѰMc;YPBα;/KQL=OλG3ECei8HjS]' %Et@vWQ/-@ &kLcvy\12 sDmXlgbڎl{*IYzmng34,*ȌO\G,fa%QoIa'1tnEEtQ^rr=@^o{Ƴhwtg&ħNe)et1 T&8KܧJƦDϵh_r_6!#W#?վ.T#\.HA~w@~ܚ-f TWU)cdpz bfڐ8G6O g}C}p&0 06VQH8y[P0k0k(sc|a l~]~9;0yMc^b7^" KRS&ߥ ݲ]8D Ӝ\e`.$+Ma<T\$GI HxnKq[ Ah"ZlLa EP m1>Q_bDOhxyϯ~g@qE[>S.r}-[&1EoqHu84-6E-%k80Zpn(PWWu1B!GNb(5:G &Ҟ'_\9ir%eȖ$"xv_`Y+h&> ֖BD{O+.~ .` 1X$7]d4@SSZB9VA(Oo&]EI@ű/p p`gk~:ɦ}*l ֑z|32/Z8c1T$/Ea",`x0={:gTrw(q9y5.5fj!xKN^`m.wk찣 O1t[x6$YE+R[Pj%y^r/"{ ?>̐M}BE@s"֋e#5$~ "6cs+Pſ3x*y?[Rn"Gz1Qw$JQ,?:%*o*'SMdު_<& Dw @Cߢ}qq?[{$>;nW>la= ~O]3.*B=ÿY+&Z}:uSW<( ." EY6aňZ eŚ{"g5&CF ~U!N*]5w % 6%q+bcT|^qxU06-Q|rIzG¹F!54L!a1RўN,@ǞwB^}׵#ʈ},}(I \cu*1NY5FrC9\Am] 6_dF_! ,=Vbٹ]F0;V]'ϰp$\8-Y"q6N EYg/jxYj)YdCBc" &ҿvpA6 SP./"}X O|d~ /32z}א ) >آd:s YmR~UL4yc4"~vjr(" ⺿~D:!MZl\I#Bq)kkdViETE[? QLo\]u:b\^VҪ"RaB$^i%h:i}v.#ǗW y (?Z#QkHn>M$x/Vcb˟ zW ~ uFO(,leI (o,g~]dεNs>~l2:5kxVN}63VŽ oA,ȓߌ-&sǗm^sa'{M!.ӿnZ7+эPS,*oej@iC:[lN/5sUeU–CAhM(yI wy}LO@SF-Tt(3+̒ LoL v1?u-|r5uovġKKW,#TsB+o4N3V9=I#[V-j%̈́96ηҫਛX z;BSOYkd+(Vrx^u{*ߧt0V6]43Gq4vv%k\PN_v2vhOn BP(\LUpP\k%Ot t[TGFI ^%QsR%Q²xhI1, ۇ2(dϓt_}\HPoĽs-A^}^"a󉽕!P=ՒE9Z tHjm p|-gxdIATȷKa6xJ@;kbQݬ*mm .]a"S 1_|kAK/p( mcGAOClx}[pྻb^ >LB%WzPrw5HRh߁qǘ1Gh}: ! Nʱ#y3I a p_X-9nӵ h9ڠ)G=TA H-qC;D{|cܴoB_3T<9x"DR)<.LX8bX 2YyiU*gy9N[+&qĭF` hH.c#kooueOVjKn^U=2^Mhk^wmAXs$1&dl,pu VNo'r6sR(ſ/ki`&gD~s-h-d4\Xm!#QnSLkzi3SF΁ӗho &y9{tig9+Ӕ,G/uw(H}0؅ZU`Sr\KdοޟX>+fւ*Ѥl.V-?D۬~5z>h{ͩx _|V3o<2E"o#\$(s#ˎvw^羊KP&OOddm/9 `vt `=Ȩ}Kx E @Zpעs;2ӂvV2&_ۘmک wP4X1rO=eV<ӣ5B44q9˜~vV*Q+= '|QȧרtYŶi0wb-u'GIyXP\NbFgAnBܗo i%z+.T(N\1X AL2"2;re~/iE /i%PpgMl5t$_FS3J;tueT?όkkvFb&#+)EfcZt *|cJxּtV~l'Z~Mr6tS;Nȯ'?x CaD,U+W14QaT ~zh&=sAhxVP"Af#6\6ék * J0ŎeH bǐD10Ƃ|-g#MzJ`.txk}7ׅ5^ǭl(WEP}< <\+;= .pLjMЗ[ChP%v 3h,lEa-cF1%격({Y׵)"JЄuiVyCt6V"Ns<&6{L_Fra_aw[Ftppk40"F:dO)rHfcS[aYQ51FP;^TXǂi4=F"yE(XLST6uPmf|RDQt6umĽvn%/m^t/c?=GƚWCS`j繻LeJ\/즢>?vU2HX}tDd$G*'5~E{]š-߶RI(v_`vXVBמb5uM gXq Rd23˙m77kAP凋p3o 3]욤\#*F+Q==&Cl۱,/ í'\DZ,0u #ܰV,pU|׸w.%;Z=[.֛(gR,5fK9uMQjRdaUp >ݿPJF9>߀Ir5PMRI*%z=NGx,Mz ͏ Dt"N d n4SJDAX )QW4 Cu XF5*n8QC[M[p;yFͲ>VC}; 8dOB/2YXԯO;VVm$B ZOAXH/f| 4}R]͚(1ӴwejO,$V.GgM]9,e|H!a:57JX^?a)ggD%U57~tq^/Jdlȼx7C+Pg͗9&a 3F3Fĥ&<Ҧow$v1Bkd!ٸdo K]c5G+W\-KVt4o7IiDV!(7|r2I[!xw.bPv_ ]"ymY4&/HJn gkjlDѲшR_>qv #Gf _2rTR J _WYfXL|j'n=Γ)D$3j[Cl7 Q\qZa+^l(jftq׎D c+tw:^p1O;0D¹BjyL]S_2yaM}k t~Av$(,a+᛫MJɃg=Dc\"W?8b'J) (yIiꗝSb5L|v6¦wYk@3;({7H=T%Azau,`iyMlB|ҬL fϕ5/ k?9=U^~F~E﵄P6!٩,Hʟ endstream endobj 93 0 obj << /Length1 727 /Length2 11715 /Length3 0 /Length 12327 /Filter /FlateDecode >> stream xmwePݲ5`mp0 w n5xpsӲz]@#cutsssp e$tyy\(+i%95$ 9@Qo9Y/lf.lALk7o`1s-!P4&;l_Bj 0[E*&I{;W೽9 5H{`Av[/.z|ZU! lq+Tĵ>5wVjn߆O]P3{sWQU@g8A<\Z} %!ap!n>g:`3E{3`r? DIMEnإݗA+ss Uۿszk߭uJZ%ض#Ɔ3ט?T>n?p^yFO2VAw*'jixGXG#`["H$dP!*'z6*O3@@ܢenX7w8av`g5Gjg{8!8jAfL$BWM^P;dƱ⍧0ݭNe6FEl| 4Нy2m Pj=/.eG:$WL|6!Ba2}▋,vŪ1T5+:--/i`f=~W DǃIף"Ջr7Lm/RpDpŗq{ eHJ~x RcTXv,콼%b =g -ij2l+,#Bgc58q4`5k"7iD E$d*(5>QTڸ 2f,5Bս /F#mǔ 8nV!a`7Q'eY^8SFGWAefr\pf*5>'s {0pT@{y49즇}*<0L]`Mb{x+V\;轃"(üִ=.˳2WQ+QhA >9,I),xaGjf7io12!ߐԂ|>1G~𭞹t97U"3} U>A|%ICDs;z+ȋ]$dj$H7TЀS5ӛzϔ4D}@2<=4&W1KYv7 ~:ՁyYh P3ϡ+ݦlHou\ll(됆uDx}t._`h+'e]Bz`7P[ ʥŊ2}VaD[Hwlްy+^J]F[Sh6y_p3FJ0{nWꖢ:P]n3MdʼJdsNy7,?1@*c1a`CKHKN*N?:oC]+2lҦ9JoVe(i5VΛ|'oWyFn[ V<.15g^,T[GVDWjA/|_s?'lw<6Hq%ÏJ;BqR敔yjkfo$0TCTՂNB ϜnT1zfX9i-N' G\{''tA!ubVTk ;/F_aslD9 lJĹ-##zh[y 2l1?9F뻁@J>q{RsnCZ/FnҒ pqVOL{T x!®X#;D۾#%x/K FaƯP^;1)oj跞K|4;B9a>MɼiN2&`#,ǧ:vRtbPD7!F\qN hv6s_+'Q+'L"3}yӎ9$-Gt*YPOލa}3o~vGlw^@PEF q6{OZ ps?<X zfG["5E8!ʵڌ C%*1և#+BJ`֕z\QƸG%$ݸd+SGYi.A}q$>FoŒD@X'F;'ap3}Z?n{$ӘWOb^~eΛP[=0@^|m vNfgo*MוWqt"EuT-(LS_`q! Z[nG4e(ի{o&Ԣ:||82t86V4fh8,پAMuw̶# h.[itV!1E ĚD5:`I-ņYC&V ~X!P_L Jso9"~p[=ٝ`bWI:zU<9P&bM$H"3}4 5BT8UO} I9 + P,މ,_˔7;cu"PO4mFum]US+9 x#U6Śânf jnd&z2<` B*Vf[? q}m4vhD_ICG;m,̾[dx R_tLGd% CPQU}m;CX$tHBYjpnf%,$'M`eKcm=2X؛p ׂ ,^ua"b8_uWdnx6"S2.>Nz9tJ'%#!ZHk=[H`7pd߇),h}z=EveWJcF?D{jL{G,yP]c2bg[:}#clV-]zYЯ+6ԬaOt2q}]# ݰ"F:1V*'ܝ;݋#m~r,JJ6CeLNY= wq+i (w&<1꾷r5[ hIXc}t|{!)i=Oͨ@S--V͋;I-c[Z8aU68kmSiT[;Пmx5.mXeKuw\ߩ E^}BO׉q`2ňʢ,}γ5J, 'W{*f<Q@@}d81V" ye' yVL J?ƕ8H15Pԩ ھ$V\GyÂEZ!L Nf!@͑ܣ{@K toMqVM"eЪ*P4ǻz*"mUaH@6O#x9Jb,h |x(olXPda j&eɃhm:0MlgGPs/2H3g%oNc|J챈ƹgi2SGc37KLo~n%X<i`g6KB̛QCӆ!I=Ñ,lMHޘڊ:Ux82tg2Nw}In`,+J!ű# DA~c>H`&W,qQ!XSEzrnMa+mS}t8 P``6~p3;(/WQg%Uy%Xl$ʤz/g#2̏?#j#h^D` Onn2 Q4.km[EGb<>a t+Gg/'~֋laVmH@`M Uy40ϳ[W5*١0mD1l!7}gѳ.!Ϊ:!>A|%㪕Hi}"rivrfC"U2/cs2aڊVA)Lr]5K.9OF# |3@8$D8"WU# $Z M'>aA")4~N|A Q7%^*2@i/jn+okLR2n3D6걶+CC)eJ'VpihIў]D=If}NG V1*՗p}_4,puүMB1R雁RTҴp@E6~FoDؖwYǓgXh*S* QUkN} !ny<{6_ T$asKܼ1ogz6^j >[$F-Ȫ:-p:3B^N(NBܐt!ӘZUQ_]*L $?gO)qgA~ᾘŴ;* t=iCSrJ15 c ; ? 0QvPf$ ' iYf_Ufol S4Ba r'Ge77yՌdxYi3=~BI+$rM54$7T5m 7/L"??ln;lJ Kf_42PS'Aat=ޡyե ?7\1U?%9b@}˾ZGh3ـDs-ᘜL)rΦI!vv5WH`GLԆß2,LTzտc*.rDND8srԯ4u\F{ƥx=ݐW9 M{%Kawl z|N;4Պz?G6gX&ҙaD8'Q4pÏl|ұe PU~kM. %9Ñt)XwVV7XF#M'{%9?pA'Q}+>й4/CRwn'ǯ:'o2dֹ ;kC){>IUSNGfstJn2֨֎sy/nios$dUȣ+ bUvcs8`}'5YM9 IG'& 9^1:&}xS.Q-A-Pooˀ!ڍȿPWN#]K(XbwC4Wװ {o[VT8z'zRTڞCYi@!9VvWh;%K#˞]-s}gdmD&}Z=𓦰HYbce>PWR̄$LIk;$1Cl$ikzC͞8JWsSNvģQ6ۆbyY>:yZnAz?[{wңKFYoeybhƣ*ȱď$+θOe-b77&7MMԙ4NMF}oϰ+%Jxy@glAQFPG r=O,Hf;SHFɴ[3]-+#pa@vQKVU#5I2B+Fg40ь! J %4])ឈ%=5 ^bv&[E@?[GQ0. Ы1 3EG15oŘ/⻗JX'~AEDGo16Kd0f>7NTd*zj1S+5UNpo \eӇ|[9- w0_iD.?TZ^~ۃl"m{U9n{#s/8Tr}RLg:Vr>CDv]Q_{:]|0ߍ0F?\-#fkޓ!1 \(+p %uOdDhX^7fE$):/FC{l{)CHзzN$>(6QoS[U6~[PB1d-{[B>p6RW P/ 7%awtG3fG䓶 7L: ;I"gZxuT]/(pżN a5ס郅$q'Gqj{Y 7{Β3BPFΊk@Y%J߂[̆D15|C^nŏq }Z;%$"u7hPj ׫xSQf<-H9M{0tX%.I\N6 +M/wlJw"$ۑ Dn$_Ӵph}- IQS'L\@=M68 .,则vٚ|v~먪_ SzD q+$j=}&Ept2:xh>@q6a":Q<[LBQ&y`X7R-M6Eo3կ89J+po ԬA%AރkOAjR PF=(Sf3֨7I$ʒwFI(m˯5]noGz{VIG]>Upd&)=Gnh8Q+,V],-t{|]U7.\nlE?^D][>& ?.)l]2J66b L1 "EiM#rϪON)]5:j,R2(XXe̷|y0 C|C$Z9m^ m/oX/$<;vr<0ilLsT3=~Ƣ Xn9;0x Ȱ6H> T$fo9d*-/~l-Uߊ*j)6q;٠}ڄ{«TBg3_8ĸ,ZKNp! -R2=͓w h/u[F!.smޖ0Y+Q¥Em4Xm:3϶}#`ULy Bxn ˥};+$'9N ]x?괍wk}|xtn)[1H찚RT픡00'K0叻8/9oߝ#z^B%^uBpI4o1@֙BcXCLt5+䰪WKSb<7X!(I'. l&Wo7\1(IpxHtmOm1 xg_Uױ'~.Oa_ Xt,ZTͽ%53JFq⟘Aל9m+j,і7BOb!>]T1kD< Ҙ]0Su S4^~V[ڏ*/c nO~] 6f$ܗ!`f₃r]ͤ_}r}!*nZ-?!U\mց=źL 0iiCe!kdB ƛ@O ,#\6]9 ѶP#șN qKe RN(NeסO):V!Ԏk=ojMcۻܴk:YQ'lc8}ԮPfONĭdmhq 37= Y%v J:8}HYbGK"et0kbnxO Y&z փ/0{k"-G̉O]ER*f#^WA]}L\mu(s&Tw.hXMzv09ަwֶސx~"W%D`AfpwZ~޻zH百N6ع.~Gr Il Ca;YbHv@0 |b+ztW":;q}/wc*o Fңt/\P hWml6(*%OV!~ [9s;OOɯ1>hhACص9#gQ0ywIs9ZdSwO%nEZ֘l6,=R-;\eѢ̆~,- M'E1 D]Kcxq<*ѭ4FO(R4g^IsJEѷSP9'Ed阣^riѡj̏ev  {VU@f4!Lժ{+V Ĩh- X".5@-7rr!9Gw+uvy(իF/ͻd0^ޫ)H޶N<%8 isau`풅 O[r_j tu}Q"j$LKI;M^3է 6{ƐԊyo U)1I,:ީYk뭲M $ʴbd H-N0 DRKL.ӵC,kUϐ% *{ߨ䭳k}oJ1 endstream endobj 95 0 obj << /Length1 721 /Length2 17075 /Length3 0 /Length 17679 /Filter /FlateDecode >> stream xlcp.L-vضm۶m۶m'Irb۶̝U}6֮^{&#wwUr4ccg+1122Y`D͌\E\͸f3G3 lea 4wnkdjeg Pwup2𺻻 ;Sbfp4[ٚD%j 3{3g#[ @Ō ` 0q7'w7sv@NLUH\A^ & *07JSn_\g2,z&&+_Iٛ;6usoB.P!R `jlPv3/RF dor Y[y*Z#?;as33rO?zs^w ʚ4ML-*hilӊFV4/gl ax0k(aaO:&N. 3klfofm':=_dfvPf xoհν*NL1}QT,Q2kIֻR[S5:MҬeXHDa. eqUaֽ7 Ǝ}˼G8QDWi7Rj_E Cr[{'-U(${G9%+k(jn'H[_T?l6w'$ȊB*%jhs7 ml [aA7v>F=/CrGn\ TSm |Թ:4+<ɓ͐8$eCM&d$ Xi }n!rKm$F8yo\ 3/.ײݠ*EbSC9h,0fU\.Kb<+-Q Ҫ89W ר~Yfe6h Db:lki/YYت=9ɖII 2POBġK_~:VG/EGzZobW܄J(51e,D.;Byx|j#)ӕ4u/9& S e il+3Rz;#Hvx<ɈLQ02FzPN[RB6|iGeڙN^;ˮ.mseD/Q!]!p BB_ƳU٥Tdj׌Q&gNm-R~"by ֍H;_7az Z!<: VqhRYC'@VtI~/ԋe{ȕ2@-N ރIKZ [#N몁>7K *wB/Ԡg@fbMdBJZch$3V^Z#oc&R,i*C 6Jcػ?˂JLb0OĿ>=.3ЊU!DIxS_1>0kE \ڠ؆EB)vp> vڏ?2$be2HT(C"%>Cڂy b_8nmaxʖWZؔ&;GtN;DZUcn۽ȓM2X;Z#\%R=U)TcGF+xЇP7&p W,Q7m"jhu~.@܉D Me.*O 1d?byjAm6449p31 Kk*q|:qPЗOR#߼'L-u+sep.zJoN* ]%bcORAU3;ZH1߱f6V;a5]8?0f~^",dh_Zo鬗q9gG@kGϓ J([z*_Ǻf9jDү];zg"`;3l3MAc<WЊƞ$Bm~~|5t=HM=:U40p8yT :9ԥs>]]pHꉂhGzZTƠǽ6Á/5^5o0㵚w KŊk9l^X#KJ ;ӻ2oN MvAbHX]͌vg=.>Cч0݊8 טKQ*P?hâ9XQE:ʗ~D7p-"/#;5XaƳ$E`8.&"9a\ՉkP7eřeVFM pz݌HRgP^1eb|s\~LfYr\_Z.>r蠚~:Ґ_?¤$kH?qO09sfmOmP #E*#p1r"RJIg׎ rng^jϝWajbǭ.?9v]-B\9Baa~|pB곝m0_Vrqj!MNfa];U=ɯ%tQ0e}0f\ek|`bĹk7=A>ω Ti9C>;09]e:~k^C`gJ\>ebmgXo ?R0zы$ac-*sGMvSk6K. bӲ@V0Zn YRO v邺1AMnԃ@eEh*Yz=e y  1LrK 褷 _иwPz;oægᓈo nFYĊP`RI ;_mx f02T|u%UKM {K [o$)KV9͹#l&Nft]d2nɁgHb' ].wE"\`G3"t7,CeM׬2:ih8|]UnOkH\#d: @u*+Y^mAYfQv@ Eohx5'!,R4 /Ec^8u_ɱ v"{0Ct4|RbpPu,y+wq7C7\d/'WSӪۦ[Er:Wɸ-ݽ&1}};ۘ*M:K$d>H2kg9vp}w :R}חa@4 w}TG={y#AkhZWE?t(+V҉wYPTk *|neGylq`\tneWi)6K&ވXN<.cۂa?Wp(B,\fevx%3~r9pgh|)Xl܈L7\K50J3D?i7H[#,I/ $J{$e? \đ"#[}HjaJ3s&#Uqھf+"RC[vbبHP[ĵC_Zgex $drHj‚.E&"VA'ۻ>ybh0ȕA5:Rw$ޞ+5­HMI>T QPtA.D$iގ#4K > 6gbYB_?r?|"A*yȊeK3NxJhkzy$s ٮF'/ EI?R dhhl75y2á6&25Dt69sxw3gx}QŪz3]oScg y,քŌ9tQ-k m99%f@zn#Ӌ, A7nˌ##z/m#8%h:e⡏RTС%[~0 fRrsy1cEMWyZ5qrH⽇zrҠ4ɸ)1]<Ӻ^n`_ g?1޴̠OP$!eI `ؼs≂~"H^8毩y?rPɎ|S" *‚!. #Q$ LWOW>ۜ0bIN@n支ԩ/2lxI.<fz)aZWY"kTh@vߠwz$Ʌ\MzN`Bm? 1 wqaKZV!)9Ba tdUK剦i"ԣ%`HHY ؁s (hӫs"+ Ъ^{)f?rɫkЂ-E*[1a΢,y tJWp炉CBsx=!&F8mFVSN`%'-bæ`gGqCUupVKd'aϺigC&c*|Iv W9f\(ɏ՞F_tU>oquG^8P2,I5cSX\Vm\#\^:qND[XD,wU}N!?nPQ1qw+V&^8Ht 4Wj!3jbSPi1!!U k%7ڹ]>񅄠\ ގ}CxQaѡՅoEV=~<` g KƻwB1Mcm>Yeh Ǎfz%eQz)1Sy5ݢm>v&$y ϟ?a0idOP͝@R缬o+ M+g)XkoA2}%d*ylcI]N&SLζf6}sḪ%qΕ5t.yo+r ya^fI NZ$y\ӭ= G{ ΫjT-p,Ԏ[BjE7XLFD{'"_gKy1;r܄fw,TT%?F]dž R@!7.Pk,[З#V@f%IcM͂gk`X@0bA5S Y!mUNӼί\*[ י-@R"j&C"2>')?~ș LD`UsΏZ:_Ū6v.u>%fi>@q~{5t]^E9YM!`{{L t~ }+b*ӓ> =((/TĖ};U=*&چz^(DրKʨu7 ^3̋('*TC'"&{P6S?K^ enߘ ~K>EGTl2Mj\*#45y--"_[SYjE&='L2wv6.ԑ{(=R8J}JXn]H+,~C6__jxŚJ#y6g0 gk#ǁrs%Y8/W['*%˔r@LȖ/XwTѫ݉Qϒkt֬I( K0=|F8(btz̑"=T3V5WbO2 VA^xrM28 G@}hW$M"~N?b> !$ .04=%5,/W`zJ`!|LF֜cʋDڸf2Z{}! ~U{ ovy.3.yD"@W1? ߎv^2$ݒ&Qv6]."ϩ$7!l?%IT_VF^cz"(T{EeTؠǑ9Ke>w$(}/1uT茵ߵb9nDa\NюՙY2HVEm(Y`,Q űr'ns\dH$BQv_Jk|xݦ~ i~x u7vC#B$aA`clJ;og'QhWS w\;gMsva-\gjyv_4^blѐ&Bs_VK|}]Fz@&ܺ%UIɝ[QQ?o)<>zGAD1S#J֠zТ?ߌJwR-Š[Bռ|ퟏ-Hx~<qu/~i}T*mGz#_Csk Y-@MXMP+c!&45p>>ԑ4ziB%6owNg^ʍl&lfRw‘Zkn7&? AUH[tb)XĚnA4fyIB6cݵF{a6v&̭m(T ~2T=jj.ZQkߒ$*YrEmp?M ?mp?u'a0%?hmK''6<GGSKK"뢨 K@j7Fn}i+ts,xu Lo'³GlYK52lRfے#BIx30BOMg{x5@Oc{WN(XRJe^#DOijl2nc^if5ǪK䫧KKz<6]_r1\ Nc #pY"$yP)xKuɺT\baV:%.edsCF'*7gTĹPgEGUϲnXF!.砧}VU4iWJ;I.c j}z7h FwyU=xNրFRpLx:CE%18*Ǟ$9x.q׍@|gaCO Un2IؓJrR9NX hrl71i| _DBfJSvIj}G@nCa[8pE=O }ff+kh>[,uޝ[kfJo #lR}c-lVá̟)&L,ˣfKrdѿ#$);x,v^z%;Z:q2GIBhZT!;bS-T{B`x=r˷uz{$g|gXA%+x2O Ώ:ia '5~!Xo:쀗֢4a)aFSs1+3[m$Cfxuq9 :S=)`C;uz-Ջ$rtcPdjw*sbu;36QIuTH )U Y*Sj&(,JA-s!NߞAi+#,09Pi =oB\ZȮ.0]-'XAte\k`UyXPB  DuSG0D hlkq0nK{< -y̠{dpC!c|Cj=uVl[n?Wgd >_8zU;Xauw"f\6B>oYηQC 3Q@[#"T#2 Y#JѰoI>tor#teiH,=HQ/L5y{ UpFX|,.GU5F5tk6jsx5{Ɛ[䔎gӿ^iƀWܪӥ skXk%Ckü~ﵚV/-R<_bّ{́Nr1rT9q^p:]D(פ2iB[_*3>~XN\ʄIզt] r|10C}8i4AKV7 I;+=T&v!̅Q)i3CMy]|(M0/+?ʪǤ-ywn7BIpس)O-¶}r˷lIi0Ʋ5JdžfU@4:Un@-c3|M$0 fS#LKgެ6y8r"'- -V)#5Ժ~6,Ԁl~_xYIzICX<YnX5r98$}o6=Z/N,5[\b#>]cSUrƫ0?9"q̨2JJ"H^_%XJBPJÅr |kƐ&Rœօ3Ū#1c0 27U+I\oSN8lFsdnL%ؘyC`RK/>(T"rQ~e$lv &+<åbU|bNM៕ӹDǗ0@y՛sҰ0eCa?{boz$ $`=w5İ݂򻑥1Fw윆k 𞖺nwc7 'iVrN"l]Y7G4oTD i/Q٪/hʮUʹ@| l?\I˗L^?m/wX˰=Z%R*W]@ux՝Xd?Q]DSRmp'G*"Ƽ#U>)Ja-:rDGSԍ ҃R$PxegEznau42w N2uxmήJD6)B&#{dS6LQ4,s ִ.kȮ3nVdu7-lH$JGF.)3eh]foJ]A c0,#N~'n9i_%i-4K2&%o)i4c ڷ魌Zp氈3AЄbH2unsc AF~kFx'*hg]E|+U؛" r4Z5 o¶_YnX!c3]TseLwqoַ^7N}=e7ʅ]N);z@`p3`in%FqxmyƢyQik^M)>^ I ;<n S9O1֚l]9;{ř4nnɢ.jͰퟲ7^PVc>p ȹ!ȍ\ى#mE#; e} N\sP.x13gdnov&:>~ٱЭ>rlF7Gs^ONJSq֋,Zb0%=Fȝi i2wc߆(0etaPf_]" [ zqnwV÷h *߀Uµ̦J'LfaLΰͥF*NAr_qX܍]_"!ܿx4pJP} kFw3MÎ#-Ʌ<@}P?xїEp2Uh)_zWCY(]҂Ug<~]薠,(/_ݚ Ӗ n< BW ef9x# agg6x(e25&QWA7 脱iūkЉPu6[Dϓ $iB fJ2҅.yņHT) +64myh9"^B}g(Tf̩ [b,?͛ ׃3r('^=e[gu{l6l5o,X0 oD{Y#fbц&0ʦgHӳBhcqJ1=6ħuD!t4Vs^ œhZ|\v`Ip~.- ~HdWW~ _&k*+ZJ%@f!$p=y8s9aB"S\0joh\t;X~ [Ձ\axΩfv~  nnt:P\,v J`hRTb= ɋ\$hr(OʽLb pJEXe$&Y96fHL RKn}Jq`u`t NGR-&:(e0E]irb rg[2vz\|HZb+5Q')ḭN:G`DS`k䢀4 vg bL< *uT'ODy1 ʶFgNXrUoh˨pG0grAZv~M' 9};׳؅5n՗ܴe.~q 篿y`˳VӋVi>Bh9O4҇NQθ}|45uWm;f` N+ڞDpLdDźh6Ī#&i T\r-q3FC4\EUSчX.|ҨOXУyG]$Ȑ*+-k v2\5̪kdc15QejiPEWQܝQ%vm703]QA@I}2w6M,bk/o650#ɫ:%Y#=EL56,wtw2ɯäW#cp쨀x&/ p/Uq ig֟]5=rlF/؈__2R{dY%{L5d| _*w2 RO>vI`r;L1iݸF U1lEc&BU\Ŀpp#XՍ+MWNG,G`- U˨vP5TN\2NkXzS+1-f!jӊ7F=e<U $K?E=vgR<2ܘrs#Amxc{_岫xGiN8C`(eT*X^ ^J J%2L0O-UȒP>~LF\|.2b|GR=Ux_~NkS~c8o;wvl"X뵃iP+4[?I"8/SvYunz Ⱦ*R޼5َ\&)s ^a&y՝ (Ȋ?dbۧx#n>idT+[)+ss˫Rs ޟjsX/@ohq;!BdoIXܓˎŒd d%9$C5f+~]H$&1m&,hF"?AK G2j#VEY6>o!FL ƨO^&u}>~ zd1w6wOY*>%M"rHSu#Lu%a]Nb~&D2ΕP"n_*m溬OZ1BFsw(1b?nPwGň)-ގ좺ީ-:gl;D쿽E6h W*^ ר=Z;l!Se#E`.Xϥ. .NVp7J =h >§?ꃠϝ $T@䙎dy@3ex8`sՕɴ8z2Mٳ|) _KP`"%,N6m9XVIG^hEI SPE iLj= 1ܩ`\"]'Fj+ѼWs |f9>VS9*3-[!+R%Mhd䊒lYc-xg1 b_7CPz VQq]g(s+\ LbuE۹C\؋#+xriSsKkB/Ǟ@U*ֱW1[ZPwl TD2Nˏh$ a^A5E:>1 e;㷲#8ʵPnb,P 49xk=\5& MHdlSPZ1dEOpM^oOŐD[Tq$9f/5WG& Rib7eYo!՜u r:e?4o)k>WL@I`;f̎BNZwvAUds161EIȺP> stream xmvP]M5 www r;\]{ͼS[֮^{:ME&iobcbcfI*X9Ĝ@@K{;q 2l쀿6D* ք @hjikдw4 :B@ `fi))(Jh5R ; 64[At3{'Ϳ893C򗘙-@AB]DRIQ !Ƣ.ڙ۹8ELl#忖 fm1#L-M\ sK;D&cgf)?Ր` 2(mAZ1{[ `o rZxqqArY:KZL-]Jm+?q-пEdj w=O?i*1wIKؙ؛Zڙ\j t2o22& @'Kw+ƃ_?DEݽxL6ο^Nv d{ @ {`Ԧr_©  # O]z{7ynēƂ[g27XX4YS1Eop hM|[Eˈk@e1̜jYRA )h^􍸥;;):} wdCEHקeȰ-K*|C0[.͹V+6?&AZh*+42!m?,'7[bz*#G&?AZO֒\VLcm2NR):#_nxhH FRaXNE \+PÇ+:qH_= F8~2V2Zh}vIw>kT;zqfzҕ`ǐܙ&aہT6e9fK*I:`*[mk IlHiwBr#+]W7 H?v{Ғ6| v={zYoj|73!~.>ܰ^|ǚ۹Z>%}0~Ԣ,32VޞEК.2Q*;+v0{CzPm mmĒ_ݿX%1b[hkHWe(,!I]:z =Rr-E)O ɟ=TX;.f5H$R^#.WlUr<ʴr}凧Do`UH=B [hsXk^<.CX[q>3J)5 6!*>wJ4G\Kߝ{Jӷv?@sekܧfҙIΓXmObG#y q{|AiT]Q48v7<{p$$ƭ/u  5Jv['xVOnVf[ʝJYP"Í%7SvuܤoOq*_H2R,pe-GT+u_(O +KvŇחuݙNmgx'=M:;˗UH\>3J1$BʉI<jҨU s F0 6BSn~4\"EنE3I&Z׽ 0Q咴Jڡ@ĸ9M~ jX=΀h$h5|nR]4hIW:ݚ2C:Ԥ[6,-y Ƚ E s߂gi-KeM%UE-}ug 42SMD$.$0G$Cyf987* gI(@鯿?,!2XӺEf#C\v,~jWo1![(ȗBrLZv i0|EcCH>š- y5>/ĶiͣOS>ɀ߯Zv\E CV Inد]lP΅lX7Zyxfw#Cê"+ww๗QR-L-ȸDʪ=qfBĩxK=Us-6J,M" S9B8ZEf!W[ )X qH|I" $k| $^I&3B:C:.WyR D=Wyب90=+l0\Y&7i^Pev=d=DaNal/kj]DyfZbnś%KnjXxڃ+fx9-,oBf234*:K:Aw$!iEKvO]O'x[h?x:ւgZ;35[Bi("42dU+ꋦ9\ ez:Gse5!T`iʻpN\Ka3_Ki!zm?v??P&ֺ .fs1t}'PCxdžNb_ bQ|! m!,mIPc ? =_g%h/&Av4qw&|xw_/?2KUɑzi2ȫ/|] \ 1|Pܐh<9;%7:gl貀"<)UQvO„rꗎ4vCWDgFYaaҋR.*K0سިfnӥ~Q4Ksl!^/SPh7Obxhe?Ö'Y/rLe*XQ'6|v>̑$Eۖњ.W'.'8QAx4! Oٛ&ܩ%LzJeN-LXѕ+n]5RxdKb`*͊tP ,Zڲa+|ZSh\mZDPiO_#"f`9w O 0h{_h k`>6Xt#ZvdY .4ې|ڿڛK• <;|Fg|Z2ܕjEgmgvoPzG #oERڈDɛyG8x}gW ƦHdY[U]h;p)'`%1Ef X42y\)pqޠ8 r\꘡?JnK`2J"}p=忠P"[NeeeF*z339Ng$fn\mN~t" 'Xo{L$$'-; 3gn†*1! Si4ͺ%dq&&P;&B8\o@10B',Mک+u q8<⹓@عQQf쫜茵.ڂz F6#Wßk4rFqxc[.P|gפX]:ۗ'Q<"E}qbdm nK½p/:=5hluYK7v[ ߈ı~#h;uğ[aֹ&5V̄? >ruv=qx׵D0/ZJ3njRAzYZP· =,}[}LhPRMQk?GP&Ӎb`n'T.sl"]KuT=w%c-}ҏ%<Żvs Kff6:ϊu3ZyЮ2~uD< مʢz`EU̅[C!eR8O#_h1#Զs["]Uu dI~hJ/Tp@+jܤ|j5(E_p9 pἱr@&v*ĭz`քv[qvc!Κ7 .CUcWyM!E++ʁlyxB? 5O6QuFPl/^DF[#0ʄxm 0MiZ݃"e2 kOV/cBfM#8EvG Czs;-]L%X7 hlYpJxcHT&pSrAkfg. `ĮϻQ_YWRE7~\PՂ<ްvkCaU8I9}{]6a0=f[)>>BGḾj4u `em޴af-(dɛ5s`hPTMwO]fzx@dpdh~s}QCGMqrS+~> :F w{h1MK9b|z7 n4ű╷TS q_>zOO~iz}<_>tmCe G)Q {*Lj.pHndxFqÏ>1гseы= j)\1vgDs)%Yt fLٝz?E6}v.?$%')vd ; mEE E4- I%ƾmp2jDqfݏ2N:?Obcʃ27? W {Anvf% "]5.Ĉo!𽳳iԕyRhaaCZf>$1wA;vPU.2F> N}2xI,je)˸NHze+0yfFoDJR(Yܾ^Pr@c (92wbE0v7c1>jJat)6iȁmݥ I|(: WPY 7rb-ԋwe)~nIQ,G,;y1m+ 6&! fZtx<ߝ17J׎ l0qgN||ON|W]2f\/,eG 8ecm,1b lȬnl _`y챚XV/2amw[+,j$5 liu _"F7|}19O=Bl's~K#KrQsfO|oZ#XddRu՚Ns[KwB) h s4[ʠ*;^ɅǀJ>{[,W^bK) hi>FXIoϠ#Qwe&Gr^f u,JPu\g`Kh&?М*lJlGm{b9_I5h8Op~4)i"EU:\TG^ T+#@+W/Z&.qVI,:ҐrŧۂQ+Qc4z4t.XADzY?t xG48pR*ruTt֔벵;e6ށ{|q~K8Pai"[ :v%Ӟi,DIF9, %0uN |SE=>E|"|# M)Z:[qoC#X~-ѽu;@?&F2 75 / ;_04ӰGD8B[ujy 5\tLMu$~և+[w-V _ܕhk ,L?zFMOdɯ\A ??YJ 0<[7Km3 nH|L9ً},Պ{H]=>EͿ_GuqmtjɪǾ8:y!5 =86~S#j5 -8:-?w!Y緳ꀈB,PKhI tm|E)w0>rqQtKգ;z{vY4 1t@dV)[f)6sL`flC6vVG`_ h줸 yb[,j/PScf wL#CQvkՠ~>J?i~)ct!MeJ|^ԯk9_1LU-2z q x/Jv(;j.Vd)ȇe/Cvo+M4F?z]s q #ȋq-^k 0}yק+]P=Vs&b}==xH@m3}EU71li/y*ՙ~nh?C] `*2&_*[:ML}wRHN?M듇5Y0L>*q ߿TV>4gyF[ath3W8'~*I[3O-1xٴTi0e4%_h~k[SD]N!>rt+ܦDK4}G}k8iaE>P_c糥w^XeF򟯛X8QYdy"ݤ\wBn A-OO{$f/M^4z; 5[(=rc͘ '#Cl`BWF]}G듙Ն2uØ{'5P1ff8y^6& 'Ífz3`χh:֮GmP U}QrN,[H/j+A*m)n3 -^|4LV4^2;W%-|Pߺ=*#׍UeqK"gG&~cAOrS~;blO*!f ^Y 0Akp-Q#nz#oSj)_J={90߮]\W(:_%>;ҊmX. @"fd/4hYjkٲݩ[, ,iMIT8GG,PByc,77` ϲ-*Yl{c3xt9,V^(X\,L"z֯8&_qd@(x7L$j- H#(HgyG-;,@2g. >^gsH܁ g8( XCϲjH{*N :j&ת n|lR}X> ~A쎲5qPhߦà*篢8_f7%݁I]* 2 4Hs4"%J}6gY^3 Sy/OG;GS^)8l "M;F6g:49G}Z4׋9wG̤K}s%7Y@RDu3X!OO-US@OH~uɝK;hLg0-k)^?r_wS@HHK~os}yd>3Esi~FJwL|ީuBkrVov09l8Ϫ(ksoFӜQY\cCp cKrCsh2RHmo"}ڕ h=[ȑ8n@'>Hw sV NgHߥĚ!2qEF$5Y W_,ҳPW=nu)ozUQ e]uh'^NUcs[)HRE豦l٠kKqn'܂szt%)X&fh< Ov! .aZn!J}i}Z' %1RAQ?'u-GSnBkzIɦtIvv}e {ud`5K z%'[֤S *k)C;3}h\qb+CV'-{UY}Nr[Kc d9U%`v_w`j#"̯4cMEq|W;si B4+@"AF\2 Z-'Oj:o 6zqЈs~!{Lc9D<cz"|gѱkl\~@s΃-,B>gbc9\$SkͻAW6j!B_BnT6gGuu\t"e9eP]+xܯJ_Nn ?KVp2\N1i~''3WÊV^wDK![wmI+|`/\.ZGOAǖk_#)~4hyM AP%W "13Ȋ^4jZi͑Z~muBDPVRL_fc̆Y>&ebIusl з,s,-E7Pw?Z2R5!Ķ\afcQ\*I#\aџ^e,ʫAM?uicDی);!+X q@8Xc2ZYN唺9o 1v9EwiA 2#ۗ3XG[8&nNVy|tY $Ws> G4Sm58&Fv65EN_!5m.=~Mpr(9jI${lҙx?IncՕH<猈acG![&L.ێjɲBq+q>K9̈8oJs9w?zM wߡ궙kZC >6[n)<4i#.eVU,8{1 #gvln R\h)'JC`kpG+C0P?%搋?5Txjp<~g4@}!I m !u3+w dKHQ %:зɇi<_X )ǂL%2Z HI*i2@fCN^Gf(-~D> ;K.4< l5h2hY!E7Ȓξ~U(Ҧ>QQC Q"W)g m¨@v(N".l䦜FezfD{!Fwe;W{V"S(H J&ʸMs*1~#vXHB536t8¨#=.+ *n%SS!Wh ꈡ*!"Gsl:kAEVV,voږf)Z;ߔk(kuZ7k 5\?Kϓ0ޥU6Oב@j,S'2D/Bm^6Cx>AeXF E;~4Biwl5,kƞ+ekfcpyOW5 Mg&o:C?1J*pZ.Xs]mbHŷd0u, qoHsK Hr̮%в cōe)zU5-JtZ=NJDw{S+R`'~cJK͉]ފp@D3v)EQ~?OzVAF{e2C'emjA Im{N/Qc:rU:oaa2']C&L ߾8=$s Ϫ2h)/!VduR`ۭ6oIװH8FC]c endstream endobj 99 0 obj << /Length1 721 /Length2 9653 /Length3 0 /Length 10250 /Filter /FlateDecode >> stream xmxeP]ݲ-66wgNNpw BЛ{έWj=1\`5O  #W< +7 B dbe3v}j̀  +(jdea6jYY95mݬL-|nnnBnήNTA %hne **iK+H%ԁ 0jbke 2A4@s{'=_v9fdoWPTP2f@9` S'MGtodkNXXfV.@_I큜\S+> _if lW[[c;Z79*vl=eHAl9CVV 3%+V\{Y׏7jf#ρL:Jt]`S{3+P寗Nf)+[{{#?d\`HX"" ,\@6n ;kcYH iuޔ7:%Ox nvHnreZjNx&Єb[8r41CLT¯vY+ uKj${vYc9?$KQEG,Vυ$>V; hƬehѕ*TxA5в2 \dêQz 9UpzC@CW5}/ûDE%xb֓)yQ{qӚ434 _ZLCu7PSU>+j7יg)3:8źT󽲫 9"13c<"Ź -x\d gz6n,ۂGԀo3,!つᠰ>ё?>L*v:KjڏK~ㄅ TcWfEJcDMVαja1>*&>MfTz$yMa8bc?ȼeXZlf9HV F%$ujAC!j¾h~B't Iedr5t_&a!VLK4>-G@f^~:o;'T-9W@pcͬGUN%'=ϝG8!dYՌ qIjƠd@q?kN?IX" .le*,B0r1/Ndc_j3El $:.BD] 8ewRH=S2B"> ל% 2 &ۻi;FC IsxI̞ ٢ IӬ>aoCCM@C4kե&kcA/Y-> ݁ȹGj&^ yeꡕ$n@'`_szIgXνڼI:JtQhgzUٶ\Ri 2zMH7)FQ2يZ1bl+a>RYIT.*xx\vV$ǿ +h-1Awq>#:.ju<@߸&) کDJs%8ZקTazg.s]cͦ| 95YI\\(а' x纤(*(oތԁǃrt,* B0Rg.rBouͶ|D`0Mdq܉(Tk1:v?I7VE:"moW%*78J(U=;@&Sy:#Io:9 :3(7x4rwֽϧ=٦#eg$͌(Nwaξ0`X ,L>7jyչH% IJ/0:x^|ˤS`Mf,l$uA o)-96n/<~=-؉͘7jfZ4ꚿf<2:-1*(Gyu5GӶ!T_`u?KSi|H!>g=-.)v{ wKg*t?16%n&t!D*ՂߐEiz0T|4/̾H K 5|lӗsN[ +!0nLBZ683vFL)J,0E68͹ ɌxW-˯Ep`Ķ[kAH5FK9cXk _|m*gf|s0Ol^hVe7/CO<[Q7B>C! 2=3Fo31bsykLZF2$%.+@x 8'_f7 ~8a4~t)K:;rsNAxXf`GYzȱ@œ,fAr?~;[gо?CG*.tDHlUz ǬSE~ i"p=ʟz[C/,*snA>qͽ:-֟}VL~þ` c\`t33?&ͪ[>9x],--"u@LDmڟGNZ9>>2©/[f8ddBDUgkhY fh>{ 籠ϙ7W'^CCmB%њo=ǢgvWAtASf}-X%8hWE{8]Q׏sV`B )tڜ^(Tsb.i(S mݐ-U 'd:RO7d05}LʦDd@ Ut9 ^[SDz@%o,cBG+e#e٬vb5~ K'YzaxZe7Eh 'uڑ!yCQz<"bI#*6t!!ݍ!0ÈpR%lON1)W'tTCbaY<ъ%Fݬ9vj9`*{o'u/ƥ$BHn{3 eR194S| (/Z(RםINf-;7Rx ! nd. Nֺx>B"3 hSNYΫ0@tg2˲Ԁ$84Y"lxUydɮOlXhwoKu=y 2T-◲/O K }itˈËɅF@$[e٪Rzͳv}hl yM T=nGmR a!elpt)ڠ@:t=!!=D+> T[ *[<щsHCʉR/QitN )^jV)WYdDb8w@\JOx",MY4mzoر=)/ųJ|8~dmh02n=?W_-u,\|)^`nG-DSwgUTUDoSiL:ufVֹ%;P)è^ g:@N^K^5_唣~CM'Ѱ2E-)Cx@s+9Ɯ;Na;Zi :}W٣9=ɖ{I%96Ȏq&`KD2L_fVlU?;VŸ^lh})TڭJp_E>|d)SR!^@w@ 3(<!iW;{')Piwp;<;=vY ίYeYj΃ӟ-V::F~o}C.)ONEZ( >~. ( lrvU:%C&7Qʎ8Ivu:(bP<bF38e-yokxF*^m)#CjFkf _)&/oaVmsþ&2__GGTdˬ=2bDL@m C=#GћKcMrG3LLR", vgP+pqqY2cR57ѩ}d\\5ܙEn2st=H`W:Z L1H ~{ҭ v_2]Fyg𮑞_C~yuGq%FWv!arSUvq]w:!zO.!}z c{W 3;_}p 3ʀr8<3B0"ӹm_$S$d FxԱ/lU":m\ëaUt!,vJ:rR&ceS`+ @>˅%sUv1Q)+ )D$F$K} n{d |f\S(QQ?e:frB>?PqЩ,76,5O"ogPZDg5S$)2tGS̍#b7W||*Ց*ifȘtKykzճYT.,nLFY7Q=DmsW =zۖԶ R^oNX&6lpu&PdoyGE/)Rol,U)G?^NJN {'HdŵC^pI"w[4=\ 5 3=*e\b+B~o?1kFR N]ߺ6G5_4K\[(nB' Fqvl q7c;xC&. io-õ”ޗhܧ{Y\sπ>o᭏T$G+!B`Q(A.?y{hҼtx 1T=&in4 9Q(VmTe*9 貉_KAM%=Cu_XAhds|ysݕ@8gfIN+J Zc_6Qazesm^#Y+ ήUu'SԸgMRt0D'F;m I.a1;*?W46 =QPl\9o*׵DAȋ'Euv(Nܣ T 3nO^BVoUSBB  g& $KDqOp!z$m~+J\5Ef3s }EO6m<~ (B>1,~[ba>c:/ϴsH@nlrP:Vb>܊rG$d^AD {8> ΗO_C/E@ډ#ЎbG?K[~ }ʽЈ62Ip}r baI1Jir}|XS>$LZߍ S- = r܋E[wv_$/U@( l+~(Ќ %hטX"бMǍљ""^Y Uk]l`ESohzn g$w,3nIr:3׽h03㶽9S|1dsm~Ǎj=ۘd:Gix#> {(É@_X/.%*VnV?+٢_;_Wiު`^ GB0M\NjY٣&L,<]S}X-M>:8 ++vwnWPoh NLgĔfbSnU5{WzRf`^"5:w66/2`܎Oi&)sF^ ܆5[  [/PSifQ>5|Ggmon`u2G䕭/_E닾bee*IH,p^= Њi) WqE& r.4|LWԆIwp=%#̑$2S <59!̂6eHE,:,QnsMj:"ȁ-vC&ۃYݕLA=TOf욟' TeL'1)+C/1gUp˃ ]}Y heײ;.6}a3=Cp+eeG"m KG:X?Nemj6ێ22(K:v#?cMgNV$@_хM1pZ7cw<}=WU h{go"z*8 n #rCܑ/ 9{*fwdHC^V3itP}چ[  %MiBaTmԟYj@JvƏOjG,v K[`D.V7vѨR&rLY&-*?V8΄Es'(|ўшNk"SlB-ys:Kdz-ؓ_Q۠ȕ2 *>SYhQg86xn}6s3\+ebiQ5޿Ej!մGں"|WcGHX CrƍO|h|M$FRbnG'uqɰ5.БT$uњKغܾSS'U!O"ŏU7Ȋݽ4H6 t~^OY'_E ,.K%[cE)5d. t؆#ECo Pb\v*ϥS8z ;LsXg DGv[-T@R?HmWPC՗0uC|xY~8m2ߋ!eΌ^l_ blu sI eYP2:&0^F0׻nZ+!S><~ߺi[R<,P u qWko֯%I4]4d18cCBx|Z~RYN"0˕,LL,% 28h3W_^i!PXCҧGkSH-BUւ> stream xlcpnݶ5vضm۶m^mcvb^y߽w߭[jTkc;ػz9113rTUT,0dd"fFVFf 3S#CqprtPPP52r;:[Xx]IR13Z̭l" ZRJ y5-@ kebfbF0wpG8؛Z ػ9 '*$ aPٛd%Iwu'_\+W2_$z&&+_Iٛ;c6us?\C*?nFvfJ;G7W3g=_{8[ˢbkdO%bdge"\!EYcr43Ur{Wg75'93S+7a?tOYACFKXYo@nMk[?`3.gl a׸0kQ>tL:6&Nv'  =Xfff&0+&KPQdiÙ"Y] DݐaXU9Eh6Rg9'q ˲!rT0]~ë( p )SͫZu7?K:C# F .{3fϝ!*)3ɵ9B_iu2dA\p|^gK&i*^jid '+r=~. WH04nk`)xc6qKvJ!1.bŒ?#& ~pjFpG,yOSy^u4Z YM^>PP5#Jx|QRkHr DH+eZZeRԆ:'tDD{ \ú>»a'wRoQE H7{pKђ~Qr\Agy`_Xbօ%]R>9IM5ٰvew+>L6M^PP$o8?bS1tvtΒ"8m}D%I}'w4"_ ƦC ԀUEn°3v.up@_B^ۡ._BBwҌ|DƇy['ʝQxI%?r\cY-8.OLbF îmf<]M͸wm6|<2jdSjy rW3hKaShi7ͯ3a \nHye֖EtSG55mvT>!=Lu(+`5{ Vה4y7R\mMTZ?aWtbQh~9ߟ9Yh!KH[DKf/)lxuc61NJ$]kd {_jQ^a!nra/@-7J},d6 jw*$ (~QiZIkΫM4O2!UUğ` dH9HI#iii PΜÐ*[@YY,vN6{ؚԞN 3|֌Oud{i(i"|PQ:\y,4r"ULmڑcS DG3X x kS#ifєj/PWZ^ _Z g+,c~u<`LL&s:@ ';Y91N{Tbmc}D3ZV4;89e(쨕"$inViCٿ%pZlhBzLF&:mBb =$yw=$mϜ£ HPt:(\pKcPS֖F^_+.tآhpi~10۝PI+[^*awo>]ՌUc6Q-;FjwIT+ O h~D-ȶ:1{16{;FzMB:GQwr6&*~%`ɘܣ+K2.,iÃK4KK_]Ia /D9١y#M4OtNIG;>℞Ha hlg僚]D2U&rkK>I]ec--^p+3ӍX!;2C<>jRT^ *R+Pqh|x KJxeObR + p,¢-MyKj"_*p[ܡ!'t F~`Ow#_AM()3Cy7d5Usgtg<@dr 1=ƭalH˻9f$E`X1z>-3èĵCFBN;{\m )c )X} K&fAUoA"/ XFY&- OD Cg0iO"kneTRcf t"Go/XlezV{f/?Mn)mJzAs.sUj#Y٘({Y`b%"+j"( 0q) |q*c; }?Y6 "`ҶBF4 㻃_p{T>R?۳Nr93q**"r&36,<ΆmE *a; GBڙ%h搇@hGNtm2S\G_0!0œSސy4"!^؛=yqY2,c'a6+DUѾ u[^.)@@^Qn (W<-L߲~GrjFZt+y|YZ@`h ft#Xh6>~l*eb]Fc\^ VL-d4n{+gk2ţ} 4ё`Mf~jc~O{˙|}Dn&i9"11&DYCw鬮aZ~B\BXϝ,LJCBzM,3^XoG= E|9Mxę]}f#꽨_$VERwьJڬ"@"~-<CrMT%][Ff>@C"`:_֚, B(pORNp׵_z?8#,[@ղ?PP; nO:O~ |E.oi(g+#Jg! o d(zMrwPweU.}B6:~I58&]j8~OЗpyws,A0X8_l\+kkyQkEc8>PwRC^%Ku Wf ]i_G6xj Y54RRNe~Bk9 E4S(`N@@`1]7C S*w:z҇BY.Go^0/{aqci-v"\v @cMmx]9nY%-.nxOLVAcr dJ1?gEƌ%/֭@>Ê)QUonZh=,SZ ̂9F#as=zqڠ:g1"y OuloץN16v*Acvcq*'}~֤ǎo6s%*Xɹip1M6 c0(D[51&q^qۑ_ӛ\=`9EVA 9NpLy 7^1څ8_1OKgkY _Sgl~%$ oG bP"PƃڜG7d#%sNJ4:̍k۟ҞRR@G u޺?.$G>ҵ璎rnPkZ|;M!C܉AI|IKdlsq<n/3Y0G~n~V je> Ēj!soV,g:Oqh>JhzY/ I#}u$[^uy$GwDl_5%$cGas P+?IK7poN9UˠQYl^7!2l?.w8XNۥÏ+":j/HQ೨X#d[ qQD@h*ܿ\ތF<#{z:r}[3O jv yNqOkH33_K X ]sዾ5˦"thU1pAH1Q;rd+ ʚXTd@sZvPǤYFͿFIr8| ?yW2۰O[-0ɶHT͹CFe{?}>Ǣ&>qK$u18*HqT0qRWg5g~]ܸò _0C0)K^@Hl5!hZeľ,Gjzש#$!XG~ϑ8Xi}]ZRG fk:nTߒ#k) K[p 5|~I:Y.0Ӑlt"_49ṵuAɹyt>VKB%e8g;xi"m~^.lgR >`Zr"1}g3aY$8FӼJ.U醥sM>Y$U&U-3CHR㥠( \צȄA1|z;KO:yA:1JQs:}Y1Nb:v,dd(nRq]4=|`M!hoIq+S]wX\jUؽ=Tm~Ub)s#onti})~Ps+x OcL&&ћܤ4s*Qx%ZG4msϯҏbaJ,FRt#2sd?x#j C`?ɯߧaL+P87:2oie#c~3W/Vpkh+чȦ,]\gT*R9BqH:;*,0+.#p3>AY­^Ӌ~[U*du< ka6⧔Y0b0ۜ0٭4q5 W^l%J/|_lLM2ڄFD"act$tէU=cl){c`\I]iƭ$}!?4^׏\aF M/_RpC1OA:ŨW;ܒBxd@'kAY-g /0Fs b .[s%^wz{@LIPwlS}@QBRkhA15by"ŧfW䘈3VNbq쟊8z2l-H#ޝcu(O[@˯geu i^8ĺ+jGIUa!ՍZ~tV_!ЂJԌC/֐BbSѠ<_+p+9cXteY_|T $+ち"Nl+>%gC|Ad1Cv0byJٱ6W I1g*:9n*8}I5B܋Hқ['a -A8qhA~l tCrDJ$NKx %7љf<4nHx୞zq4d~y[fó^/ɂHhx=\P=sW?ύv 0_8;)*av{g" yF>hi7F"xK.~9ogJpG@cC{>8 M-(#p6>w!rG/S:7qkLK5Ϻ 6_4p{Bh᧔TMd$did=l=qEowÕXD$&N Pk;57[rkImA-hq1Y?T,7Ipw7y:kIQnyd?ĊZvݢUDi!L)üJl5 $Fjҏ6(tĿYK%6zN|Alcd`2dS\M6nTW"f[Oj[xQ,sG.U^ygL&onAO9~G8mP=nVuS$z̮SJBqY<'d搋`ʼY/z ZriR#ڋ f=J1sf6_h ~ \6T^;gh\#G&4ZfsN [ I~&i4Irh%X$^2IrCGA4rLP3Y_[s-g==>VЇWvQJgo!ܺw߆{ڊ[Dt;3k?Gv!%[S i^Q j&?UkX IYU pg7M)=GTā&$b]>`z2֎ 7Z`>/pz4n;O2 coI~5B%B-M\;㲆Ѭp1RTg4z$,hʢt;~|/{>l ~|H@љ:K3@)4̀ag1x?2 4OQξNs$֬ŏl'iOe9Giʹ3Ffy7i>LߕBqn=O$MT On4;~&UWl4ށ:fNMBZf&`Һ^W:_-~]!L0qhQ`d{\jtaXL;9췥u%c2??'vny7-4;+ʌ$Z~}rR08&[.7Aڵ-ʰAH=#3 AXr!9XsGX$$m-_r3=(A(RiP(H Tq`PYm$zמaW8թ5,!Ԕ;uMNP8:!D/zȯΫH0WrZ@o * 좊6i y_L%6Bϭ?{i; 4dAsBк67qp|':DGt  3̏'q)AkҬFOʜ=u'g :x]uF",Ob]vo a2H"<ڪEn.KYFW0X# 8QQ'wL3te.4|Bz*tϜ0H*b{D"&kDu!:zǩPC"ĮgdL;\K"+!6\3鴥.~h&+^b+6L7y?h϶ ;dn-i$rHnLʜ¤%ɜ(pHzzyiYѷ}\;{>LUtMEi,1LN:բ[!!NExwESi' X[;Mqq½7ʙ\I |f2%_ssԕh,U Ӣ?m- +uZV4&ywJn:D*=W8VnGw2@Z"]6] T s OU1EaH#[ؼ?|@Ю',kЈ*Zr{%잩}{G)Of[ %DvgAc:ZQk]WB&*Q }\).Lw^qO.= ,8^C0jUh'܋p]kS7 N+Vn4f,F^1I`ȋee\ALJUEшi[7LJÅ"mt~v>hxx_ l-Bz07H&~N E %qup|ai©2 R$ ҽFr6ܹKxxXkgΊh=3;ͭCd x(…)e(v7M@ߞݶ ۮ#7Z]UpA:ǓX)>{3PW'YD"ԙƏG7tK_=c2iIe_ヤғftuֆ<9E#)u1꩏6l€3B%#㝃 a\ZL }&UTN_:q z.=<+Isyg&9\?H|+$!KuFf\? )_ +wD^鞍+ߠlWX#v&|x^洴 > WJ^^4;|V-yiҮM}Hzkplux %LLlͶҠQ7|z2gQn-`#EO LKYP˪.|jo~ae=:Q.3d#H7p3dU=kjP{f 1ISm"E/Ѳ ]Wz6ױPAy0H)gä /.8qoU蝅WZUQ>NAN] f3.28G|*"ReDn25*_|߁FuqɨKTL?Ț@@$;i2 P'Z -cBQ&,Б֚5K`)Q$cjvnɘy9lk?04Вg?8ю[٥2jdK8`:7R%;|`@3hY *>) 태^*1 Չ-S{~:g|Wk5dh1w=Muٴ>b\B-GSEG 2~hmbS>By (]$ZG_DoE^l6En)k \ipZA9bl26eͩj` Vkp8{FbkzX IboGla@RhHKpq• G^8P2,ՃS5X8S|C M<'ĵQ_ԅaeC|gOөC k]fC踖! CN LS[CoRZheE2|p;yjYi6Ʊ#fҁd00+<+[ 4a@ak%+Xy]iKPenxpIѡ}nTjych{l?dU"qF0 ;Byo'!_^?;MLm|Ihg'Ȥ.4jţKAMdpU&)H0s?~z׌@ lM<5J1]G'U~ƅ߉3>9.v^=:ArTiGJ,zLehz݂%XEk,âv]fF3NPy˵ [5mJ\|%P,tKCc.2.7y |.IN8LI! U-Ԥa߿ps"IY9+=juϡ Ws! CXcoĝtv?jr<-HbpR7lb Und,ժcxG˲q^G[ݔIoO(OR2Yꕁ IDM9ZMsY>ƿ_T֙RFF ?y%pV郂кO&besʁF6gHH%H윽I$`aft_t۬?M3\]+\=έ5n)O5M "9 Vtj&=Ϯ#?0QoU`IqIzаwt^^5uV L"rG7[F0~s;nD))5Kp,vl[p&o ݗx)}ԑ8 qE!jSuΩl1Fxς6?E|v_3gڈ5n=Zג9$ *>’&'^stkz@Q$j9T?jľ.6ҚΝkU6ׇ/V;b e dԮ_5I4` Ȑ-ϳ9:}Fx%#\c̒*# *1U*?mqLV OUӶDl_hl7L!gR݀A_8{=qrzqsSztiQKZYXA6#C h,ϡ|MZ禴r bs-Xs ;=[Y'1jdzƂFQ 7Eqhl;^=)1וyFRh|3{zUDA)"B.#.FH ۡ3Y.Ο] χTz8A[zQi-]',uƸ{L4vS:!Tjrsgf^fKjx>tNs&ʧ'W:PN^#X\O;8-ܖD*Ǿkw.{%DakEU7f-Nfr"Xx}2 ߎgz5a@.k6Ecv!] )#,JtZ&.R4ޝzxЗ?cbrHܢJϮh4Z]Q[=ZD-iQSXd4ϩ 0mk֗ f6] ZFh<%fiv(sq=t.cqtah8LzlmΦ7+% @_6]MSLo$*^`DWj'CnVMOس. \[㎡_$$0ɡ8.ۋ\5ǜ t 4]cr4KFCzᰀR|e1#g ZeUNEEB8dfx_!c'{RތXt'jnaK%'S^\#FFFYg9Wg^kI NO6;ҵY Qe2gT(&f9#{^{W7*}&e'! ކYS.$C!abs6FLas=uۑP`se4FoZ\ڱss6.2\(!$VNK,:Dd].$(%gT5K u{L컊#7hHsxKsmc.(iyOA_A" o*"ĈBbI0hַ*PV $X `I6hʢ07sM{BrfpRTR؊1hG `a)%[`v=j6!d}UY4MTdOLad*-/ջfᠩ $Y-U#m-B!S?teژ^%k$9trOj^, %^+\du%\Gf5HK)Ygg|܏t0XO-:;eؑyBW(O9r~X0[^bK96W("h b%,a7%^W]2:(HaPRОqK~̮Iڅ|ǵpJyԉx8!J昊 zW *"ȅFoe(Df2+j'"<=6ܩR_Yy+1b m"rI1TwMnJcq\4x9%?mNDx)Ji/weA.|v䇄>m,О=p#0y.и::#gApP4ӓmhl!p3a2ű%Vޔv L^ ?"#ORߒO=kn~YNLooFZ$Tx=#ʍ8 /I8%Kp,Tr-YzmJ""s|o*[;{FpIYQS&Ɩ qD&v d}ſ.vy$O𽙚"ԿPڜbpƧ/ [y0r 3%_k znB:3-,"utl-1f(a. ny.b|7\XЕ5){4 q},Uq@ ؽW&K߿|&F 泋}J1顄Jv22DK)pW\dy{@Oe|]QwW^ƤykkSOuSV蛔l[B_*ZAúa8cv77tj--0Hν(A+yMI]5/2M( =NV>-c +_0?gn΂=!hDޜw'.1<^k i9ýF<ޟ 2,>ٳ+j!WEq=2?$f##|dF}@ҺM VlG[rEPh=h损|. <9n'!h)F8?03?2hj+_^qț eSxVT+ȥ6Bt XH#˞t9tG%ΖR\N.~x.6xSxwo-7d'GHSzk~X2q p6QJat\S=$V!wfٹ)\?,DUxט. \Y}ֻ?챮zSR*dr 5߆/FQFD7y^̨ >Mlt1NczGZ/Q|+<\[k SQis;叠wsmRrFRur!~Ds׬ ss.RW1\k1Kb'jJK)JF?jRW-w*!M]uo ZN3O316o8pAPEQ'FԝaT7otpOګl2["z FƘ_>~K~c$/@ZVU<#9B ѕ׍opOQѝB39%!R` WґFJ5-@y/It7b`=7]74sŽ޿v*#κ' hǹ-T96 'zf\ |7u'X~ˋ鉶DcU|2ӫgհb&g[)Ci"0ү%* /5g:FIHbVM~3329vWA'izRd1bK.u(V\0;QOI^N-k |s4@3[=3ٽVSłzwSTFmL!>;3 rfu:$CX(\5]a7{_jmٮ?3Bc=WV%I*E o)'IT^ZPӚTĆ eD(wG)]3#bQjJ4;Z6b`*2K9Iκ9*C6:[#{vdX]TxY5uJiQbM<; !'ᨾc"m%DȫwϜ =g1`_;Ko~,0cP  EՉPZ>io|2R-6oԶ͔(İPF3.qo Rw| ڤ K7>:ye+42>6ZM7d2" vlhژ/b6:JE.oGl9XaC0N^vu=wzI, ba+J߼3 Pn xS->y6Ͽ ^ ~GjgN~+  M+KUڜ( ̼$_M;`&改)m"$4:8x"F&b38&]a80G vw_,c2/|' ϳ܃STwF~.EĽC<5]ywnlE;Ш? UvƼEz-2pd0;ߗI1yn"!px]'K!} 4q k* Qs8qp8̬lYkWl\Jt _|ԀVմFE6AH4 A MԱB?%piBBhU\P"7.$ l9ͫ Ɗ~!t?B1jp3{zc/fHl<d y+ !w;OP[zf>9٥*{HসA=1u#OqI2SQQ'5WM>@# <@d!h7!nd8l8b)cWHC1~#>_f]Z.&!蒄38{txl(<`{k {+;Fd&z q;\!ny)2wby?A+ a5QG UFH+QRLp sk:f ɇ2ƆoH9ͻ`$S Jր,d"ВyY#\--<ڷ ~&) {;Fћ}^B.Ⱥ,&c*1/ʑ6]$>lmn~1)y-~ܒ\3nκjM/ J跏{&w|رpʲ:zz2$\ Tz2p/#(3IK)yeR2s*x5ip迼US@%3Z bPX=Ŝ]tZ."QȨ%h-iP*n{<&9icf Pꘚ4Prs 8q]2qE C(Na0]s R7T#J{{vʮ E#QYUV<5؋/o!\ |:Mų%=N 5=Lh`0mfqoA)<ښQYۦ5qBx8lIv(uޟ9f`wP!]œUz-ۦLʹhUU aw]*Hl?~P/n3k0hq  2_sЉt x:lfT%ygwֻuqT@+S&@ 8ɰW_L_'NC_r;uk i9 &JڥpV G9}֖(>SM!zwҳItr>6o*ڀ_ŧAsɔ~`vL~,$/i[|L6FM?V&cUZ-+ +_ L'FPE[yrq=s}ARq3_/SSjI#ޒUOp7 (Ƒ@2IyJ);b̛RM2+% _J x_.ʢ׎+8yyFd^{ s$0]F_(8fur;+]0xDz6[uB*_wdM Vkٽ =^59=˽9 d6*3QaAx%DvY] Cj5G)ssG {Gҗ4")ZkDUrb%a3Vo[]bMp:RN)D!De%15;O.U94{ȧ v<Q,"%v6'Yys q0 V\f Zݔrڶa+%i A"{lXu?w9q/ZpFu\N-zW")#BZʸWfTв^3Zơ{lVOW1c3apN;=]1n\d`绯W^PWN} fnNI ߼W@ !е~.qۦ)fV\]^PsukhֹxMj9]Ekh> bw+:2ni\K *d kg(ړd2ԬكG[ ]{뭀6_Ķc of1OjVNa*[. Ė݇s0^ƑTbIL3v4 38 n[ٗq"CZY[FJ:+QG&5wr!6kڷwRd1{eYQ+?mdZ6b |хmFP\H:)eC5zLstyS)wR"5q`1j1~g y_4~zg~O Yag@UoC?r-jQzϰ!1jc}ޛ?+-)W))anھ]١E ⽋9/R~"I TOP;Gǟ0@Zl2{_ 2ܷ: gL 5g?9uwd8-n@} 9#oSfy˃Z#ٗj;F,e&w6Ց76|=3 `|=aK K*Gpbi/*/1D­бsŚT2#*E UQ&V6E;6wz׺f=(Nw]H -v^]#Ol(eBidVU>.~]KaļKaٹ5WVkCyADV=4X8RzMW.NaI=5Q]Q{0Q%N; Dڐ|߈Y>?u68ۜgPgEf9OٝC(|O*leVQU7PԿ]1$\.O1cW]K\|gy`nCepGϜ͗ sX= zHl㯀E=P4&4/D,% wy:ոdj#-HYܛ!*#UFk r;̨v O 4[g}Qcu:a߀ڳeu%I) ){f} U:gVg ]ԍ-C: W٠4鵷g~aޥ ;1Pvb=WN`HtɩN'>jPcy"p6 *u QoxyĨ-*[]CFUG8L7sez +vF3NT1R)r˜<_I3Tai` Ԉ@mqe%Pp}a{ѥ9o;~ œ-j1HҼg8\ЯY9Lz!+M{'îc K{n4]qv??^'< ‘.]Z缠Y>(\OlO i[36*1>H&%f)fj"r1΅ g{K*oY)Ip}"~?L>xnVLT G+ΆVY6+A`EW/_joh*iMU[iB>W YRJbEs6E%&UR秶z؞e#U83IyOrng(Pļ?TuECOa>J_ BJ5t XCj @oWs hCN+^]RG0h+ "EǗm]bX$"aiBU?&'X8F cpsmIxft35.>qFhgV#(r' z/76&ʄdC|ϰmؚD`6߻!R&P6^lF%xˎQO}Ӄ&K2SA F ;yqzƼkU9iH6TU_Jʜ`]Ksbnfdx_5Hf[WUBT"Au^f3뭵Á G :cT$^͍f)~4XlR`G[iWP6%q}17^!̀3-a"P--0 xBnEfPii"ɖ0*bװ,o"IZ:d%.nY/!9Ⴠ㑦w\\SHE`Rڡ 3;l P85r)8EU>? ֕,]euIGE&]AEe 9qR=imE:rDS^jS4 TJ\?p:IAN:gfR#BxK:)pnzFSd @EՏHg.f_^/h9娶#ʙЎXwᬞfgP,qTޠ nHuSR49.E 猶BGtR(ϖͶo Qsࠡ5!C=1AxʼɡR+ra.ްXÓifJn^ .kbcjÊ HL%^!6yh3S֞.9e3rLZ`4,)wPZ*{^ Z*iKV:CÎ/Q4`ʉݭQUP\46nЧ} -i{!-ncɋM/iK>lF@|ڼ^\^n{; .GcѧNZ։ȧk݄xΔ9?|+z.u~(e "ji]1nn^Q8Y653|r@5&н>i[K[Ŧ3&JOT/r'zj =5#`Q sh4>ڊmDs*0D)ۅBIwȗ 'SsRz2KߎJr`3?`w& [.I?GK_{tXM+YsI%" 0||ϞpI S+(s-@R`2p4m?W6 |K0,q#9f<ŖR4^QHz%Ju10pm~B^,UaL?c 3aw rNߐ 7z>(Eg%%,#nπO^P_+^81Avp- j!QUСחVo; A]ܥQGl_O߫iNGbU(lVBo.XM! NO}3e{kM')CTyMGNVH2R _K^ACW5Q6%/wVAfCh8pD'aPN)cZlHq6%H`6=h<̄\ ,\cVo -?0+t xߟ }~s]5)M >+40nM(ig#,,m^.Wr(sd(C* >"_E_;EHaYc4m*J%v<.Œ2~wNJ(܎yUo6OW#j7[bԅN 9s^~{Q:[b3V8d0/o?Lɿ3|BRlG%a}? "iDB 7o@k\֎ᄟmCJRDš⏎d~V_kvڏBVF| _+{aDs_}K R_4Q R!#z׸$!E,#\oOyp ~'v?i"5( <ڴ]gy%Nb-z`v:"Tv*%*IeX.e<`QRr.zxjb#]G~!v1LyϹrC_B}+yFgGwsĖp*JT} H^wO蛽S)X*u e&2l `M,V%%X /5X3?@=!r4jS^&{N5#}T,-]W2k u$ؕ$n[A*G䶢cr'3Pݜ|o4, DX,tibFCMhG-MdyjU͗%] h<Yš endstream endobj 103 0 obj << /Length1 725 /Length2 28897 /Length3 0 /Length 29391 /Filter /FlateDecode >> stream xlsp.v$ۚN&۶m۶m{b_}uX}U$6N*v@fzf& 3\hdnk#j@;3  bk`nj2 Eљ$e d[" R* yU``Pt627Țm[092 `&91!qy +O#?@#'UGp_%`ln017)[v?TpH 0le%o` PZ9;r@wu0?V7B2lL?&sGqs7?|;98cVgnI?jlkcGǨ[XLn1#[csS?|8nEt`0?] a?ugaгr0Y9\\ho74[[5 Hm )+W]f]B523;»WsE 7۽ܑo&5"ONE,Ul)և^CƵ6 sJ__ Pb9/C`/Dَ[h#ACܧF3 .6x[ E!3+(; ̯Pg: v b݃JtZ>~cY(' Gp!SS5"v뉏;pwgFMM>iRg[zA.Csz'g]fI~Cs{c|U x.^o2-"qj\WӆG Ćq ii[tB9#1i.M@<ҟ~wg#~2Ę9kFS̠P I97*"myJYHc%wF;.Ei^P"Ъ mnOظA5Sc!]7㻯)!X %cK&;_ǿQ+ x tpmo\-jYL=jyl7xsZ+C[EѴ`qB槿"Cb+jL4Տs&! 6g_)ժ}Й.qqNƭAfGTKiLWiֲߴOFg:%zhcwa݃*]pq6-+9AwUT+U=#Kןk[Pu_d gM T#c@Nw&$}6gdRϛϕ"7+Y<~. K3*.&>L-Y!#;c5c}|}!Wl:&kY6z$0.8fWZ5OU)f0Yz29&>ԫs̕U~8;Bݦ8)lY+ #vrQ%u8eyoՍ,%vq%ky0 S5lRL2 = ѥA%Y/ mwb1,|b:\ @]1F*biNKFv-Tت(AB 뮰\yA}܋)~q4n>A rUh"%8+, :y~TqMa{5MIg: on37:SX'GS& _!7j7$^ǷdfY{"k["RG#tjrG( :؟)ݨtsj7in{ 77j?xuhJ쥁ɳ2̓ݻmTab$iz7*@'o8}i% # kvA6"ͳlmhroA|}F-䒃A+i #xDOjloύ7QU8dK[C뢝_t\4r֮GmEg8$kυD#mHB#Tt88k"_y'w'Xq2:j,4~-.\W'y>2?Mt0Maל+$dKSAƾӿ(m:f4ִ mi}j kG ;!72oFp @hm$Up!b8Eܪ! w,,VwB6AijUt#Z-q]H+nM_ӇSqʇީ{·Q|ATd1v-TVrѳyV} }-3/+Ɏ(B,k3s&&lKV( -$O8F:?Ml%S\\:= qդ AGTx.WURXH4l^ܝ,nBD߲\|8̪c/w7NcsP&ʽë2bQkZQ0g'⏂@Ik>WtM8&SMvaƋ:-'4+n((܅۔ `bioTjҢ"M+lsrJ HfSH6#jt_U@' $34N~tw/}n۩k8%7B!¡8*)Q#iZLҍ I ?zڠ"^IDAolncb/J 7 X`rhj?KR'Rsb!j#ͮUH k2pLڴK5Ng.`ٰ|q߉íOO14. q}\e "||`RIJsΎCKEȻ-W"LIA5&^=U8^ϼ$N4M?_,މqiK@\Ѓ.x"/]dbO*A;qȳ.]k a|!mP#.gt_xqz\kcQS+_ԊS=l&\ e^{rWKŁ7v \>*{ȉQ^ 2}*d\lnKk>>r{\>^UP"#z:nE\L/rr:9x6]SD ) 16!E!`H<;ö_b0SPQa'9cat&&=88o&@LtGeHkz6v V; ik;!4*([ &hdP?xITZXt [ ~: URV8br0W8<ΦG9%7>?L/ _K@9+bVt ]>-`K14rI˜ocSh,ϖ}L<7B$o&ҀOͤv~P{.q 5ڿ WRǐo3ΧEC5xwVS-uc.sw\Yy*Y9ϵl(9|C!FO\RwnmFX\~$HGj \o+!&NznJr?at c\0 ah4PgLh;XшEt-Y*J핺.+Vqċ#LAޘaW.tk>i"8h:Q'<]#}p)W +af7ܵ[瞘pP1mYߙ(&*Ba_,5З'c젦pwwsrٟ~}ayxѡR%;xԱSuzŏE+{7^+!!kJZW^?xS\ d{6ƈd :wI[:CHg-Hq8*7(l +>sNMءvxx#+ƙ6̞)H^9uO3#Y)*{6nmdi&Hk1$_W;bZ)cXأm~mW}]}nW8vӥzRn2jd?7w;pK\=0bŶY1.<^XqReZFؘ | D|&0u.:?A ]sx,}2]w]^rTi4w5[~aW<ϋe1%mMru.ɩiכv'3F^{w0 WG)Q8dsg-d%@6skp`L{\휇CK_ǧSe]Cyc[%v/#aacⷜ6Ⱦ@3KDWS]AQy$ ӢBmt_w|0Z8qn+&6,xyUYwp ҏ rowzX <&JG)癚FC(n$3ǠN\}X>8R?Ù@1rS@R*]D8Ȯ܊TB<s4͑?:SA;gTdj놃ĶGT6aRV~{Y7C K>//&uw駞6 Ky2=\GIy7ȜӇ*tŝj8^\Ao8.d wdf|wt;=>ȼ&& ٿ^M$FVXh3MU%É6(9:xHM,ou@w:PR6pb# pcdnp:L[]I될-M.u)k. r6øoHC)ջCJ)suK~̼25I{!y(ێ!T0xqϾ%IM41+w/sH#BQ0+~@HܘNE`f^Y+K?P̅b eEUݟ{BfIK@3l}ZgSڹ<*L O$#d eL]f֤yJ8o wQL2[oa<*]eSU rsTʇC)%RWR^@Uc`^-L DT%N>ƌ^tѺ\0b"̩hĆ",wa6` BQ=>mm?o>ub[nxv}|`~6hgD*ߏ>/e%p;4S=!9V_BK2w3= v\ĥT[,JEMA[Edoi2 N0XDK(O"֯|ɭ@mgoHQ|C—_ꤊ"1JCQ?{}U]:`%IOpi/KT* bI? Xpp{LPj=zbىy L#q6U~Sgľ)GW`i%EH {rz6Mj>=Q(FQ֢£VRȬp(~$go[fq;)\0Hy/XcǂTL iZw+q˘5PXՇ$ԴHre87>٭x}ԟ¼:9|i^j0v5>pR8}kK~$?J,mU8wRk6Ķwsk!6b9s!(p;&;X K Y ڛHGўKZ8jOK3'xJbCE:a)/OR+/1s_fRy*C0Vz3ta\sc>% J7#Btl {(vw _q?aU .d'p w4`w`m[PTUݩS;&Ja9r_1a*0sybf"n͚faq>LY3˶a MfVR8?2] 4p <{E.1^^q{~?1qƱgS<4nL_/{N3ڸ+{~#lT\5FMrT=7t=-6Vb+9P:H* -;@V z BI4/ a j̐kW:@cU_= t=3#E`Әx%z .r}G~є^f39+4c<0|jA"e. *-u ;Y*o"s P5 ebԽOe':?hl4nC85Hr}P8Wt:+: Q^+q'Ii%/sw`Ͱa@¨1LEG{%^) H+IRL5Q&@xCdN~6{GVnWlI#Nh#zף$([zoXG{t-rfG0%ȥYb9Em~\rCH֌@˹W uV_8@w!q"x昕&a&$6E '`2Yly/F'@f˄__Xb]NDgق4;ن ]tMB~&|4dni}ޥ؊ n9hFI;JH׳ L"UŨ]asc )""²:CAnVmD2BJV^9D8vH2I`gI|+le%K#8DQO}4&-EkniM7>Ͷtj2~ MҖ c|>Ħ̢"OES@D%JC *$9v)@ٗw!*V%KtJC]W\F׌g:^ m WMJNzRf_Ѐct5kKXut>`*QVD<3ZqDb.c?rN=(5 NJTaPytocDl;^Zo$=[w{EngR\*Fns5ex1X&ȭ8Ǖb?lz r.KU1G[Ċ+, *7 `䯢Ao_BPLwa/{WKНjؾ#͝-@r(UavF=\?mBa2A+7dbln6qy$+ &EKY*MJ]nTj,%l+5.~>␇+Y6)8OauO zTN0OEq`}Pv.]dް[S%aD&{3^)Y1F3aLyYdMf;hBr hAUңss@6!lFJ*9,f͚Ʊ'-{_!ܓ5_Hv'sSyZlLCvL+y4 #4ُ "n3IGR].Ð6kKRzJST*P4wlZ؇_+0s[4=*&nwƥm[7a ~]O>L 4}k'_S8 nmL=\c8_Kej'ǁRnFbF9< np,,Dh "0+ 7hI:H= dZq<|B8 .mXe<2YX<՛1 ^`{Op^I\!]ʴQir5̙cɽr󤎎 T+6X sjWH/)I oۓ,9RH($hG 0~f+ CrEKLlj}y#ɓsvAI[JI 8-6 |3ufgTUDoP,V,HGq/n6x *okX!.Ap,U<9mEhm6ȄUSwOO2;GBVF%hbeDO+̧:ppgiKȬ6G!!eBrh1)+>sPƹR?L|2UG%6 ZuvJR1HL{~EU#Tm(-O:o> k{ rsXIVY0P,ՇȠqL#>Rb1@ 677EOFAAט{/a0>[ 4nľUARc*XޓŮ>I/狘h NM*`v|ߍ~9|PH7`ln0h܄ex:,#7Cg19&FĕF6>Оڐ4 6(?//V>U[ӣ7kR/Hŀvbu6ѥ]x+urQB1Y}Ҷ>i>Ŏ\,!͝ܞŠmRoOܬkpf2z;bnBM`jf\E܇Z7A s<-)| . GC؞}i`kB%e9L+︘4$%ŋ >h({ -K?x<^& /ӇՉ릲 |P#p]>9]Nհ;LՔXcib/UMRۙSΥNgȏ ꅠ2cFjCH_ȞԡE'{ Z\rkwT#ද|@k=/.wYQDm%TO׏W ˉQs@C'G yC,g,o%Jw_ХMfI(sXI; e=?%/AnWzoT!2MBxuN2tp`bZנ4'?jb+R= FyD}bޫSb1u A2p{W'a:GN꣍krdC^.=r 萜5@XDsC9k.W99hL(#EXI$ | bܲ1)/m3=ܾ":^rb>U )3䳻: zb@/ h_-gA]3[D*,`=9ʌe4P;qB;;t%oPP1:)`Ïpe3,rEĮʚNdu9,LLinr^&t\ ɜ|{)>nwx0-\EcOG;J:;]}L"'2W-DJoL$hi+j'U|^l CѾ0z wsЪq @7PI T\҄ 6tY3-O(@4swl3F[v~8k/v1+4@pF&'[9E=&;K<> -F/ tg{ݳV( )Ÿ],8|5O{nfBN^_iBKn!&<,YFJynq}KxݍbN/utgu2/yT零A!O1NZUJaW}S "2f;;ZE?͈ѵy:idH-p‡?srL#菤 v%ۧAOEOJ33 2@H48X@Jbu(o E04+[%ߛpQH%0/tfR+,h5/B; @ZBM*WT.j)ZpHTGnVij hi s(x聠$q $uUuo>a/P+֠)Ixi;U}:_AJ7zjF&NE|dv%9>'!B>8$5^5c*h&e,attc;6 ļׄM/3>E'nwj>˘Ty`d_\FJn 6{U2 %Dc^!p =ypȖ6@1-2z-> fW@0F6]ڰ d7jf CBiZ8}>CN..y)lQ..DDh",a`h7yHnźDUe,I/y'$QXځJ*\)3B`~l }M' C.QuhΘNVa$UB$e CKH "k̈ #p">\8YXZw4[?o"]c2nyIpbQ[]3A߀,tg_b}0 o$Fdd= ,/NMG*A!Q>/kL"`0]ë>8Ev0J3Y$J$*>x{,;̫qŨF@ ъOed=WCհm+X<:TTHp+u;p(>e;_6OD)yIϕO$"vmW5pUeL& l/feQ E[Y`+Hi|2D='Gq9?MDIX55E0yN1q35}wlMR&F',v.mȇSJ]T19ҏ?R csD``@sAh'Z-nGm3*RL-Ş3r0M$Ð\-809mR坋YT yoQ#H u\T6,*1_VE /ea{Ss*ڜ+af۶m6&m۶mc6'm;.jUK 063J3s2M"F:nK)KGYCJK ,.:B^-(~ 8klZ.O&XnJ̵0gT{Wt-uɼ8z >eyx/W9)}\6B}R═F^lQԐ*ṱ+ip+MS9|Kѡf%.&Bٍ{s);3%/4J!|[s`Itz)W:>6iWˑ'gl;h8CEc,%R`y7{Z$D?u#rBC,߀1ii V[/;z$辡 pHP̲4 l](x?V pC Ͱ2`C(ܵ44'q(e[v]#({̐zkyMvwzXɷZ =FHN 9wsv EХi@lZDa.ѡEHdS̸}ށ*r"A*S5uZ?i7䳣1GĽu ^E3n 0 F=G`WFMDcUVܞHG୶lm0[PYjSѰk1([I2C/I0z Tp k!.&DٙYfWle!B$ jlyceR.Μ[6J:GZϦn|eǗ%CHlr/"-w_bE>@oqmqS8U`&Oka^?9f{֎2f vy801Z.Lk!l5"pPRtFFg7|X1ϊ>o |? nߠK-m ZM;q, 1G][[(SS\0ʣN ;=F#%Q6xU{4ɳ[$!/S9~4'YijecR-Y`v(Nצ=?fѽD컼b5y y:?[ q=.K[3$-5Q4T!$ɒY֮Ib}{ǜ>GZо, r'(U5G!Z޳& +biGe(@#^=^OUl[!3cǠ@?|%|E~.H>BJiguXH; a1l֬d A a3M"ɫpIuO lxpȾj}ǥ뀟qRh|,2!֞>ڃ?@@.uҚ`ㆫf' @YO\uC^ /(a /VY2, P<DM6"Y``k-7DW@i3nK>8^4ʴE dwTVQˊ$T`t9aϥ3J#o҈%Ò9n!_Ym Db==T0vTMrl^N8%*ޒ0;Č9c3_.χ.\McCUT]rUWJL ?!FlVaPFBY:pHQ#-}ld#!v:ӋM*O֢᣹%C#%_fp|XpCO[ֲ +>lҎ #ۅ#(ũ^qh3 fjLN:e4)YFb7ZT{=X`aE UJiţz0gݕF`t$YRU>Ɂs`ȕ;:I5A@"I6 y>Tlg BJx*ZO-]P뵔;-9aB}C8%,ftB^6KEԈ@?n 8n7j`zVf#E3279]%Ə"M9ͨa)mБ81V%ٖ]`H|;;! .p؞eg$9;`ԐS$R~0>a͆\5|ϧc5ep`y^45Rk_^. b|S#,u^ۅJ2g`65Ƙ{Y oEB{s aПIӔ҄WV}Ƕ;JYtF^dB#``e, `'ī<N%{}l1]\ALC@1Elrkp}HZztc` ةcxh tQ~dzn=0t ;>CWg< |ńjP? dTY&'p{TmKn֬ (%773%Pn!G|& b\My$ב6,|q&uV( NP{q|\p-lHG^q3,3y]]1xi+uCAoa*RO'~DMHtVԭ}gayZ,mp&"0RL(:0)$|e7ߍX q/keǍƊ'\0%&؆4E| E m5dd}'YbM _/΋zŶ V$Qv)(#2P J,{Inqi]U,[!t[Jj$ " oax.nwT\y&L?m[HjzmIg>#'r a#UגFL6!)F{ i L%/U`E#Ew-1,HC[qKz?)bh!E͓$S?`i(t/Jo`^∃02YnJg؛%i'uDd6I`?9~wKf#efPC4Jwq@}T9B J@Gj6 ~a$9oY-ڬ:q6 G%+"0IJHT>raZ?($q%}nb?]gP~d6RGi>,2ȝ2'HʜoD'olq󠅮Oϝ`Z!hIKrfk0 ۂy@RcYTyzx:QFg+?e8,cKgPF3Kƹ #$yA5y);*O&%ym9k"c}r)?Mv:H*sZ\j狦њs&Dڲ{s%z:K!hci *7ˆrtZ1 =cau4&Q|eSER~x5G/D(~b8~獡: 22LfZ0LF'OݡؗGA$ĸ=0guu(d m$/x374L'ujHL(2)Nh2^YM?чk EL) ʴ+UVa0$05xfQqñ\p\h֙b>ĦklbfWJN )-40N:BI'VB ZzYxLZ}V5u  tFM;0E={4ʺE05/Yɟ#E'(n%koNpGT@%S-l4:g#mQo'zӺE,[@yd!o{E!u'Iy1UfmZJzqi[=յ(33qgmd^WEoh>3O nF_ĸ`onm$h,›caM.!:h+"g6e,u* IC_b'{B1 = *Jq/2QRzOQԌAq\^\Y_ʾF^w nuh?R ^oX=NپyFʩw@Q3|/ n||/c>ub,d=CQ'F-^nT"N34ک#KAWM [PF}P5qIi/c&E)SaOo4AX[+EBw 7&^/'vtZ 2&YU(}*nd]Ha3ȇ+t"X=QS$spe7:ջ?f)?9V5wdAc;#_|P_Z֦נLb@FԬj"+N ~~# _@'0)D - `MxbAY}PUǧ E-&?,g :yL>c*1@QVWc`,vede(۪V(PtKgnXwSsAG?0x9UT`("@M[k*/ @J$"nSh 'Zґbx1٭v޲LXT^@=ͣ-I^``cuip@F\4 O,ѡwO6orQh}qh lJٚ 1„?rAv7Fa\|󾇞U?=w[#Y )tGiƖ`[2cތkKB']nG'd9}̂[]3޷-^B)io]Ȋz3LGSNڀ^Ţv4K p2(fodi2/CRxShv"tjsq!| dUB#ZzljLj}P"[_3RKMb5 C]zTg~kw$B;(GkDDu?[_c~5=l +m&vnD] 0m::dB L Mv|iktY`/EwW8y/-yn6du;i ;'R&i;ɨ8t$cc+l<3b (>}PbjVr݀d_,ГS Љ\anp|3]MBnq ICQE9zHZm4 ᶅHj\VfCX&muzʎWy@̱5QJ(j18kܴJdd 帅ӊ:,-f֊D[Ag7ߣƆH]+r->R*W&Rjm4 6 H<6&r'Q!w#cDjG)AḞf}E,<ҝz[u?C;Ϙi/ [X@v=7a;Z,ľG^Lj8O>9!رbjqHWBXi%T4|e(xCm hұ5K>Tu`hBSH¶$Z2(on:\1_ ]%KzI)~i(HvoF!5kL6%X<&4ID6Svt 7hЈoV Q֪>yY&(}~wQQ?lS9 WE- 6{{R\s?rD0[#☤j+֏/>-X'(I;@mؼ۹h+pr~@2 w#UzG?Hb[T]#JqvuC!Ya߬}|.& _Uw",t)\ A^0BBpEq"x0q.^_'v 'TB6Xr Ҵ#j8>ctMaUa^29G姜a5y8b(A__vJ(.e9[}UP@H­%ʧsM0&uҞY#Zl`Hb/f@|"7 "s>PpEjU:I܆ ΋DDvbh_q;}Wx,jrnιn,^uRsz9Ece`Oe%܆, ަaZ.qa\ų~\#!7g9"نK<=1n$Wԉə*Wf!]eVPv9/`xU74f?+wH1"-䚜ٗߒv #*v`/Rʵ埣e?HjU yڂVܥaĚ&= N1I| e^ :=J*ľo Sj@zj: ȕ#>3X $DdϲMӃJ3t=`#^+cx) #LZ21$m'I3#D ^kVdPg͜)Ò*Wa W[lz:&Xk@;jMDsjT'{lÕM G#]*]^l-rchdE&z^+EشDTAÙ1 G1Ht\w%ُ0w/8G c*p0/͔=NMi1Ǹv%V Ur8l\ʼ{&xrw=c7ݘE>q[ag= (qE?_@fo;qiCy]LWIxBGJHB)0L9 !۶mWi#lhټlI$!rt+P)Z1R: trd|loV?{*| Ȼ(//LTr܌)#T56rxwJ8MC!H*ǵ> 5}ZϣhQΦw@:䅽I]ї?f #0=@؋W4D'gQ .fNI篘ZsF[lsnUDj )u2,Sc֓tPAU%'VDr_Gφ/y'r9WEE̵4HT7%\BvZǭzgG _'C:hoƉ`'?|H i[jٺ=4kRTq`8͌M7Qm2v{ƶ Q`6 @bSU$4jlQ:ؿb|~H" 1e@.0dh>j]lNnHv_s=fYrjV^]APdL>kDWR \Ntů\f8U:\};l-nSBd]7G#U.> ЌYA I9~J)js[+XG{S~M ,*y.Aʑ *aP}_iJhЖ=K']]cC&0jG^w50-zX<P-HkJ%?ϩiVMHÎgt`Z'އ?ۜMҤ!i; "z"(Hώ>S0셗,̘Щ/O >e+Cƫ7ꈠ+[XL"y6$u>e #N} X !һn憿el353;C2*ޜ3MPē߁|u$dt~[j`gX#XlT3MG2zͽL2pnx7נp#kRk^Zuk B4?KbƼuHVVP)0,%GKᴛ֋zTY\+V+Bp:Ytva|,LtD*)ܓsH{H)601!n@ՖhlEc^ݹh[ԀA]knZhOyxR;2MwhՌB~Bx|KBʹ:#! E\ORMLLb p, ew"N-U8:WjvsP|I:^;̀J&vb\zdʟD`Z WF0P6Ne\o-٩sǣ1/QCt.ӝm]W-M}NӉhɕ{q ifĘHWFuSy;XŪI6?h"yO~[)9~''w,Q Ș/_=B?ף)nZ܇ݭQU%ZZ1H #qd*]ZI1FthD ;y,A )ݜLAV0Z/2ZT!W6z.F0 3_:csCb8םϊ1UhqhWv oۊ׸HgJr()*}8t,4oq Կu͑cgK_\SKI|c\@u0I7BL5.Ĉjm+D{cFs5a]\,P}:l;ZRV 8=7V%O]2Xc'\۫~|l;=nu endstream endobj 105 0 obj << /Length1 725 /Length2 24665 /Length3 0 /Length 25228 /Filter /FlateDecode >> stream xlcp-;;$wl۶m玭m۶ѱm۶m}{XsV-Q['e7;  -='@ITY@OGB"4p26prԀ% dk`nj 7wje`lnmPu172p8::8H 8&V@\LV :X ̍F@G ``dkcl/N&`tp5@FDY@TNV "D,01Hɑ4_QDz2:Ǣc`9 6ptMSr )@VV@ ck tK?D 56r?bؘ#B探@cysvrp'OodYGjlkcvG /*J?Cﴈ)@= 'ﴼ&\;Q89i5"k_ #=#`g_t6NZ{@W꒭WEJSpHL9$̔ d׵%2vi?CmFB D$AKQwBk]*,uحSڪ<sFY} C~i[/$Vjp y,(ʐew,$7`K, D3w 7SK_-RDj8ꋲFy ΍șCj.V??~ q^iDS+ R NW2jnl$j'HT1D䆷POI Y3f>9_ g2pL}X[ MU )ɣddsEp.fzRN$zHgOCօ57=j;ql42m^:g>084{0u/D4 x + 4Mܼy !x$E/G&iݏYyVp5V2sѳ5DeXCSf^; Wxy" Y@T`,sQ*ȸ &RZzD\e&D??`bٲW0[iodC GG1Wl0f1;)x^ o0IsROQo̥{97KCO"lrؗ(6g,Q]w{b6X w}'_eS ^_yާXbz0N9jIawsl`2>b[#='欹kj 7Bѿ{V[SRՃ aUue'y\lfaHASu]*LAQ]A=c؀4,)K,z}Hbk GE)dV@6oܴRy|EL|U;'񪿧_ Q+`k -{VNJF Hs2ɣc(6,YxL픨s\4v[6]{j.Jnύ6F z(X UDWGH7CUlSRߤ:t5$65_CgM˽鱜!Ry`D"4,^nZM8aqiQT6U6uS /-1Z;̓7܈O/­y4{e¿iOl8kUJMOi5sp'YEL_AJ|DaEQ},E0,AVj w' >yWt^n=aet~+AAo{s=Ft=$с2}Deٓh1ʓ5s?!|Od Yq؜ 7A<0M,~G#;ۻ W%1hK^\aq1[q&{ Lxa貉g g2I#|-l\.`rKeoMꚧ=pc5 `A"ft_G cU=P8]tۋZ8y"NʞbwZۙ}ct`x6aBmB!|1}Z|J3}#:~RG.՝;"&8܉UIUK3A_$celƴdaǒT9s%ӡ?O]TP Rf{sb>3 SLuϕa37zg* %kc0JUr^ƻP >B,F ܑ+s( t:y~y$ֳQf'3`e?N3ЉfII uK kpSsr{1P{po9e2+sZ y]EBk>0GN=4tN?N-g\ڙ\ =FyjKP +A=ffh`W(g%@}b8"goB"zx_xW0N#/3x{EJ\ORDsk![0bxF ugC՘_%vL^q\$Ah7}DtW*O2֐Ĝ9G%~vBṮyc#W(!,v`!G'2<ewYOxUnJ@K%W{ z`-8vG$m;Ƕ+"DmqzSBc\_omtGDKw`xYpFH⡵ƉxQdrAW0:kZC'SՑr"P%pA6r Ru aZ(-lݍQ("ȉ̓y.~1UY "l t?~"3L3t|ܝmKRW+ή6foݜtۛeg*z<3[F7{/U^O\5jU/l3F $X.%N݉w޺V(C8˘ZQ{$r>}rݖhC6 6daѪԑ-<.kDM8C=*8a07ztt!(Q1Xñ)M5ٝ5_e^| e! UpF? !h202XwFpͩ"yI"d8ۢ D4kF y=oH`! _|O X/a _c$fU|swX(WT5sOcu=($G2mVt{&OW% tzV^ vs{4\ 3]O? 1>Ygzu('iWk'$-aWg9uotz)Dɣ[QJM+&.L0Z&ʛڄ:/aP,GJZ}\(ڞ=P A n#Qm pm4YK\FT& #yU{-g;3fƌ Qn{k{n>傪Kd=U*S+Ƕ=?OETuw7X+1HTv 9dn/Wᨨ7d`⋫Z(;:a'ï_)d4Mbk}:peL7 E|%4kƳ^Js0 |WKR'ZDe)QlSuh:[oX[:vK9~I]jpIh.\`ҏ+?ݨ(r7}S fvՎ\F&T_[MK&@6TNS1!X '$G>D.RujZd)s6A ]^ʤ!?eBI^n9Á&Mt8>3` G a9P:v>q\EK': (O7+Ed!LZp |مP 3vV%;JRbsBzy1Y+@ F<џʦ:VYje`mw=(߃#yfVL4NoǾ(4aM:xY{ZU>qVҩX}tjy2e9Z|ܯ'߳o| TGxW ycV|7򁛺Ԟ[+ F9I)3kO]_xCDR'L3a9N#6z~;Ї"~5?inA BW N XtXg}'ڵH~;[jb7<'坄t^X|K|< Np)^O^˜@WM %.$c51cufق?9o1^1&{abdUD??dAo/\O-|؃y.V.KC,pi1vE Ѹ`<=K}= a'i#=+Be( ]bq3fU` _sg-RwiY:%n/\%')a}uAk) %ի[";3 }!TM4;TVވX9>9q-[*;"0@?z w+̙'.;-hTZ=`iD`ހ}SC,OK QBn ٗ85rK;Sƙ\h"cW:;%is׺^_̈́}&A;_}/[|{Pbg?^k1pJt,/\~K$ 10lrjS=nOBh柗5!??2vPMe2~;HNˬ7JL_@1`&t*YK_n$\y3\{ 7 !GSpxΒyFf}ނDTϒdHBcK(|AvӚ0rڅ(+Cpv5Ól8>=0! ' Hi72+* ^$lo"v 7Eٻ컲];XS~|VL\%0FMqX֛m"| `| %>\t*ҢSNHAd\@> qVJy?B$Vqz j؆7Hc_o 飔oIPMт>+s'v:/TDrʴpKOC8wm2:o-r1.`3-Yljm- ;~g"'\W&; Q&H=#5jK)||rorDӞG+9kQ㲛hFW2X1?-7)lW:R<\BTLXSzckQbaSۼb耵@Nɴ.BAzu{P%h4YS [$#VE l3Sl3 j_pju4u}^YԈꈣ)0ܬ/5v(`]s!(+y-ykt!:V_|Xח2k |H4dVaf~8Ӌޏ`U!Tmy/? xa5cbښ4WhX3Fdel쿑u^ / #;=t"a)t݂GNf.95Ӈ6hȱsWK $?nJ$9E&Vә2/SX:Lݭ ͟I޹z?gat4^a&[gsB3~y-dr{xLf˜gJ_D\@]5MK)"l♣FBq7^8={bՂp{ґb(eT'ćKDhzAN L`D$Y5F BAvW'S9, ?jzQtZ_iG*!˃o79' vG]' NǸor7ŝ e]ՑcHj}u H`8ѼAV&[V.L4KGcGi24ؽ8 n0 ^m5.r1|\OBV 7d~2a1apѥ_b8.gq>Q[z λ(h.hU7]n\]įYLT<@WԋENjgMt wEhS-:N* ӎz;} ;Ip?X5I]w>,QC :> h[yar.@![XrW|tG.vUKf,R`$#dc<##T3vf8m7X=ԯIT$Qu׷FgrҺ?gu*7`Ќҍx_؃B8AfYzв k&K=`"Li݄\/j 2$ >6нc1h$Vx1ڂm9i<Haf#em_YEubj,!k-LBtc&JNVlԨT-4er_>;~:ŢO*U_* ߳c̆9rWD}6 44(ÃԤ%e- T/y'ǫ"_~ :OV4{2SE1W~Bfs}Zتn+s׮d/t[M.Z:[yBL 3pV8ekGmG>&A0!&CDmi12&Bh;]o(R:I v9 OD["$3JQʇ~qj᥼`>DQ8Xml^#BTϓN8-D[zPGguLgwv8yqVFF[P z${"sq=ql>ΫA显7- ᔿ KA\ 9O77.4d,[@q3.A7׬¹Ar.;yBAGO /3 ?jbm B4 % lfm$o[v !RtVpd'b JDU&D2=oDf1>zn:̜W>!)Ɋ>.3dgdV35W6 DҺ@#/[円;7|oOHd.ih=j1;ӌ##zosu:SeϦ~"G/M0L`k9=yuSR  +BV]]i}!gR'?1&l !JEF=REӠ*X?+?kvϨ64Ԇ rb]@>UK |~5{ڢbAQ] .F7z4[7\Rqn@!BUZ2.O<_{:a-(u®j8!)OZ2/mhtlF .EAmgPW^^pJ7VCLMlMw)Cd\iHN 0)FvC PW;z!V$ 3g^,HO Ol\[5z+FDۘҿb}*{jN9c{`qA*°˞`]@'>ZZLeYKk~ϽV >'iTS=TEAx*o˹"8VyLz\|g˪bY3(-}+,}-bZDsu-F̭9:e6 ݳ!A'HY E/(SJYX 9.ܤu҄Ky:b#v*&("хYE/oyJ㙨s#[-y0“R;s}y"i,r ~S0QV&GՕ/l|Q?qhhal>E٭#c3Vy ["o[z$fzb>a <\mi 8I0mWFp4TG6|/x0҈Oq4^mb~%9˿( &Dp }@tǫ{id4ֲ uJ8 {O;(`K_ZdB'հI"VbW\jWPv6i76z#^{/C xi+ZKxˑ>ȿ†ȸgQ(5}ۺ[w>i܅lxDp& SU vM }:"Glwɘ/i,BughVf0tbnQN^%@h2%ϛPtbAQϋL3IN-v,jWOJreQz(MQVbܛ(O_%(VsǸw˂+iH08IKK0vJu&ͧp9XgKt¬V翶&!1.QZ̕!9ȗb'Aщvk~?9]ra1l] _%*YmBK+ B;x2%GRcfA5n:(0Ģ0-|+msQi%#VS''U% .MY GSnI:\ZNp\ Y3AhxE'<ɱI-ՂYQ\5EaZ#Ԉgi,V䯘P|(~}UîK0e\ҏ~mDTJYg}yAyrR^f䇾v9Dtm=2/ @~. TK :eT2=MCrzqR36I.fqțt Uʻs7p: N%ff[Eo:[c #\Zq'SSqw4&U{̗p]-7mAq;'ZɋE3wRj6)6T!E6i]F`&Ad]_|;b4ħ3cbfU%MU%%'3]5釷pӬӖg ̢^ycvh68^qBfs٠0V7Vvv+̆ QVy5y=\Z <{}{IŶO~7I_;ӆ3qWyPʮq6ͲGa$K[qV= 'Q{4NyB9M5+4ts *R4x쐵!<&°JT<1k~o29,̚O: N8 7-fu2?Q!FЩdMNLf{d9`ψhglsU$s@՗/#B׫f* ^h2vCPr᳘ùD\+@IOnAPbxwaK֪]̏i2\uG_WS|ίhL@tZgM3V;So>7tz-Za;6^@%՛w#aЈ'Uѓnoy^Lx uŵW^X5 iGϹyJ[XK2ʪtQ15RN=CP˨K7V H<RVO8.qyӷ퀼_ELJOgxwrGXB_]^Cvd$52I6lC!6Yì=U>'_S3G wrjY6Oڦ/v76? ~J|Ѹ@727_}6 ,-̊2w8NUq~xTW1g nfڌGQ 9JYg'I\%AXU# Ų ?jJۂ)^/q`4RdG27zw+ϼuvhLzN+Rw@"p[c?'|l՛jQYb8a8 V;^3@.aȉ+omQm{ك`k GK^cr^p`Tk6iN囊06UgK!ן{WcK'ۚးT)./])LUlҨZ֭&լ#QY}H 2f%Ъ܊ NsJ3w_:m I!(63/ž1m; 3c1_Â. y`H`},"yu|TKQ L][:OzGqJAq^v,a&2xA-}!/vr@)p,A#ueRZYk*(cU6ZfXq@DŹzU5}S( tF5I,HşőȰ8KEI\(C9&7ɍSzI>I ڃENNNM6݄ۈ -ţm#2Maƌ^2it#T ܅=x7baF/FЄH@< DZ>5(ᔼ:k>|X:d c]Bl%=S~\=\V*(ֺq@K .X Z: ʐ7$L@ʦ+W[YJmkPhuj҉:e,S'oxuшޯsXW߱PhLp\ P)VѢ']kBanP*fTBg!"SPمPn:+Ne ߫`TMs[f4Cl\aĺ6!.،cĻ등A-2&ql_9h!4ywv(J?W}qSd7¨@!{K3 `PcsL26nʙ߆n2@4&rj /ps3N(iWщ^H {SAX=yy3TJ"L/B@_Դ e~^s0i=~lzGh"̓ܘ 61O/PEVry qfRUzg rAA-sΝpՋ iveSZpZ|oJI\ , */U Yq9%`ޚ!u7ş)a6l:*ے$%69 N9gxk+F+&#u[Eub@tP[qK[Z:dO K`<|Lh{e k.\8]#^E|Vy{m225z0!>{εRb+?!:nCݚ?E%É_!%GŒ]M ̳#`pA[qf7bYkc*֧c2q<Ӎ-s|^l&]ۉ7H0N9]֋༝;6U>mQa_\` 3tdd9ߨyF$ VXR=& &xE(7+y\nJB@XNs"pfcxv{V imvEbze2;~Cuz YjrXhMĪ<^0^OgҷՃn|!x䟋 MXΉy%D dakVFW)>Fn6j1ΛD$`玶zHmb9͆h5pJIa]8H p ^\fa: _r!U۲IΗ`jFj5.bt%g`]b5Ek9#'s \B;]2nQpz Hʾ7e~[h jpgD1G4p"n N͆粈N>GTPCHܵ:1%k.Z^ $cuWD0C鎶?17.pJ1Ο؎[CC%eН8jApcGh;}I 3Fm+aF{5{CJ>81f aϻN~$A\ ɠP9ụ[<"7*2%xFdBvoȳl*Jډl>XhJFE)a4ZӫQ(lF>X!ƱYL27Y;;jQ#0혩0W8w3C`{@E U EV`.QāhHz^K^IoםFcZx|e tŇm1}}vU(0#qEzC)䡄 J;##xRG*Ńl+uq}Z ugD"Zu`8D [&7B @1: UTH),d+D{W9b܏##k(7"ϳ?7y:TSul7S3CSwo4BajLY%K޷gL/SN/CqXrCg()qpXژaSY*X ۼbY^GP_?3Zsy6v=ރTX|"#GHOKMO#t}Dؔ|C´o""+̐ECs ~Q/I2I^AֆH]4SES }iîf]7#umTjΆ 2ǖ‘p+l5 QP1pMMi~2ħZ(_SW<\a1^K$g^Bf;W$0 zG+#0ڧc:| EXr'Rb%MӜB6dKWs d뭹VTmoa3WVZմaHo ȓ &agc)CJ"ӷGKl/$WB &nk`<MۺوGrDpOңT(Oy&K꤈8<0)*XG&i=h\W=]K 9>TQG3 q]L:hCep9# $Xb&A~B N6r$*s.Pҝ7+&V>s`^3<1D;۸hr/50]M2J[efmo/SB튣$;y}sqFE_o0?0gPG`k ڵq e ^ :g~Kh 3=6%L7̠͘e jg"VĮpT❩*AJ"L|94y;C_)PPk+l'vw`W2H9sjCh|:\E(+?WCϾz ƦHٟ@J/9 3D}$pxxa[{ᄀWl0_o$\kќ]=$vYS5vE*!ᘂf>Kvnn Qd_R48O%P.R;I<"l+!>DSDUjYOk<:y~M;uaVQ0mg~·V`1x_Je2؎6y2*)j2_CQ']DX%.mM.C͕_*ssp'+ьgSmڻࢅ2~ebMHRұYbLzu=eZTi"? v ֢PN?Go)->Tq)?M7s 3 }n'T^m_ 8nM+a{WO ͎g2^NK53A)܇ELlO 8{So@LUaӥ2`7 AA.FJ\ Ht<S TC`97 nPn īBP&:~O9FAc|`Qu0t9 ^H|/g*MW1ʽQ^̃t{mpu.=KbAH@ړ ECBGkWj: FNO&,:sT74Yf` uc\:U%MGui%3޹iQn"k*ݭg4,DŽJ\ UV|j}oƝD@j gV~ |1Y@/rCİ ͟uå=^<آˈd#@xl#E'! >IHiwy o#4[2^S4> u&tKD  REvFP3ja8!],_#<JF hJՆ ]3DL->by)S: 7IȮY ͵Ja2}?N6t-re<7 eN=z)!9xrE#/1+.$/1T"O5 Δ~xF"\Gg˼Sr@"Y`b>x fۣX#pwCUU4:{lo.P ' u;Lyo.W3ް dpH |5-MΡw:GԕF@W>ȐgO; r[: rC* ܺYS|H)eM\,؂R3@ю*:_0ϙGDVh8DZzyDdlU@p# 5{ᰪ H "iO}y*#>:ik3"mY71&}FdՎm1Fr|]vugf Rx.({8f?S!9*+ ?|O IG,ByyJ.(A<`fiX}h7\i:XOӧ/*$Y%v $GZfPKvr_ʪgPt,,yͰ)\|-J$X!7o7b!j$S.JN_dZզZ!}z ֨,; Xbv­Lv[zf/+oFШ:qXE>ú?,y2 {0xT$%aVtd3Pޝ2Ek!oY )WG:/eѼr|; L;c>rT{R@:sMn]R}#fT2$ XTuObrGZ JE(:!;Dpӹ]Cqqك(z~p o_k=C53i1d9%RY[Aڰ8=1ܖ#E.E+6f9!,c*g3q)>q dm"7X7Y i hlQu\ྛxX}IҤ9{Q[m ;dȃ2ijr*X ܯ*+iEıAavEB[ɜ% xQNgf<"gEoad8;0/Gxm'u,ؔV^{?tG]3c*2eIRoTibD!uqIagH4N$O̎:2n'",@  8ݕΓ6d*~um''E{޻A}gh'rd= xK?>}dϖ)c$V,%C%e[^<a3FȝӃaǔP*xker2B7Г>j&BAX]!4LsM#ȓj2|X0A'Q_% EdaE,3hΌ?} a6!W:moI9MX>g4pBZ7` tHYs/-反>J!<у) 4Xy" gM(la)k:\n(hhӥ_djĂ)jÕ((z#jPˤ)/l@eV?#Gi`hM*~p1vk#DK>Wxe3[4/GU,IܽKXwx%gށz ISr_|蘑 훋ȕC8gqӔYfJp V;>d ;~3;C\/R'MdM5w~ŋuacch8- G NccERr/"9^ -еbl[C@^(?ջ)4 \/ zh.~!PdI"B:?K0jB( 0YuPk,)@*ȕ>KX20E,_ m䞃kjmkus=$$+_vaj~I$uMRoi^FS֪88ZLy)Ƞ؛O0;fnϲ!)_~Y]"ۊzHX7uo/bJ'wb^Uez.ڶlF`t)Cn~ɇ@Ro}L+׉G۷AG5,Q԰ƘջF/0ږs^7BH9dHم .\Y A)B(l *nI9Rr(zR?nq+;2!$tOH7]wqGgA&˶lǸ:ؗ6@ z|>>L 9thJ *עyRLrfF"=փZYߔG_qX-NuMGج49%$eW39~oK ei#3!s'ĮϧNb"b߱TR.OLA0- (3H³Tui%G>;|Tf.cc]hu%AH>S$cWo<@Q{-@5:ڹڢW.RA:49 J*LPgL:[㍶Ͳ-H`*PhOґ_WMk?]n@FhC?8Z3SL}3 SǍq@Jƻ\c Qj<h)c}+Jf l _/Fp!\Ζma ti趋$rx֬&a/ n>F'L9(L/QĈ#wpfbl]gKoƫ/^Ll$WS精c*-ʆ%q4?oz)K^:(u0(HQksSx)_՚AH"oM~LϽ=gU2H?@jNN(P1 D ttJg r4ŜRKf M0Mt3yEjL _h_9T1 _\./]6ysy0 endstream endobj 107 0 obj << /Length 696 /Filter /FlateDecode >> stream xmTMo0Wx$ ! 8l[jWHL7IPV=M̼ su;Uٛ=w]yil;<[[j<=?׾+v`&ߴț<^*;~&Q>MS >_P{=s@dkx;`VY`s4JaQܡn.Uu9\Y6><ٴ.Z.4>Dӗ}~r:-d0VWk,8yLһʮӮђ[*mLr?q 5F8@=@)& 8Rx uD\j2HV0CzL] bctI g$`htы0\F0s jd< I6zg W qȐ+#k .bsrbmXK7ǵH7Gnb>&jؐu1VljOu$՟qWS/%1{\xB!K(hHTЖ枃Jρϯv=k2UKς_:~$/ ~E+7ˢ/ l(/} -+ZXukoԝE?ZKq endstream endobj 108 0 obj << /Length 852 /Filter /FlateDecode >> stream x}UMk@WlxW1$ |hPzuj%#%m 9ؼ}3IڽضߺItՓ;q\]U}s9|\qtY.Wժ۟oO+>G|V|~+>C1 V|B|FB|/g)g1{!>_|&~'a9i0K!cB{XTK5;)NŽbPq<${y儢 1 螡SsWѱ?"~t(Vu endstream endobj 109 0 obj << /Length 852 /Filter /FlateDecode >> stream x}UMk@WlxW1$ |hPzuj%#%m 9ؼ}3IڽضߺItՓ;q\]U}s9|\qtY.Wժ۟oG|F/+>㽴3Z~Z83f3[:٭ ߬Lg3t33 ~!>CO!>S 33>IY ?BXIAup*Çq&#{U-'H8qe%@ 8{Y;lFz?< endstream endobj 110 0 obj << /Length 851 /Filter /FlateDecode >> stream x}Un0+CW`$ MEDbɐCUA6ّD^}{l; imXc3t5n/.XjR˺^o3򼟸kյ uA )`JbD>`2$`TY'``9&Dkx,+0*NXXQQ3c 7M/߻Q 𭑦 btX& #q,pg'~ι58|%Nb'QDa 8g"h~ ' ~XkzǚOx! !=iaM4c̓ʳGym: C[1Flx L^"K~2&NCC&^P_,KV0d 1էMw"CgcY ~Y =9O('=g)YB|֙Bs:Sb+>cF+>3qg3K+>#>cv+>CӊϘ݊͊T_|~+>Cg!>o!>_33Ϙ/>?㓁41K!=,ߊTG^1|8Gh=¼WrBщWI\_tЩUtȢa5}n endstream endobj 111 0 obj << /Length 851 /Filter /FlateDecode >> stream x}Un0+CW`$ MEDbɐCUA6ّD^}{l;y| #:##0)%T\`YQqJƚ`ci|1Mލbo4m `2WQ/cW888sέ-./qJ;&\ k(d?F#h0\?IúXs>Tg ]IncT5obY:socsOPcYB?9Os֙3\Q.4ٰX3Z9#>^Z} ?L[ V|V|oV|3[: } B|)W|L| ,Y a!SMV,鸞:?8C8G潪N$ĸBO2{Nu]ޯpDQ endstream endobj 112 0 obj << /Length 851 /Filter /FlateDecode >> stream x}Un0CƆ"Rjn"73iwWUofx \iEܰpNMk l4\? ?5=cMu/x1g1=ia4c̓˳G6ڀ cxmcfƨog!/lmΘ8+^P_-C#[34IN؆1t?E߂ޡSrY ֟gg9433.XgB3\iafq3fts ,>G|F/'>t3:~:83fw3;:ٝ ߜLgw3t33 ~)>CO)>SK3- ,YJa)SMV襤:?85JC4G潺N$ĸBO<{Nu޿_E,vFo?; endstream endobj 113 0 obj << /Length 851 /Filter /FlateDecode >> stream x}Un0+CW`$ MEDbɐCuA6ّD^}{l?YtգиY}w 9]7puewSClݤMӍ'oܺ sR^}5s89 4Rӫ~R~K}O7Sk`."wAg LC3Ɋw۷qrMR8 o&݋L'lϧӫJnz_~NͿvr*aMߺkܰ^\zu \g$y=W/Q &)H8@hcRe]*q8cMC0c F F 1e|qi.Ke0^Ǣ9^'-pʹ)pq[[G]p_/+v5MĹPN~v-G`~uX}/S/w"': fyRy(#c^g!ch"ƨ-kC^d cRx~h K^| МQV14Nd5cY9Y?C9돡'g ?%>O:ShYggΈrYgDg>[bghX|&^V|{ig33qgng3tZ[Yog,g-g B|B|\3gg|2?f)O5[TT+?MGZN(:p y?0K:q:~Gw2 endstream endobj 114 0 obj << /Length 851 /Filter /FlateDecode >> stream x}Un0+CW`$ MEDbɐCUA6ّD^}{l?Ytգ;q\]U}s9n\i|ٺQ]jOtusR^}5s89Ηfi<WOlKQn8N[-#;skQ70j(+o!$oΣ;n},j7Fzoкнzr::Q:XTߙOo'BZ;vv݋ ZԲW<'suB`ilB =@ )U 9yI(ѥ S*043``MSin|kiCXc, pDˆzA:x0)ljsn l9u}SrI4"nXCA8%&ٵ6AIǚc:7^EHOupQF^odž1BЖEQ?[0^׆ƨАԗ0 9+ãbLi~jЙ}s~zrCOe fYJ|֟uМ8gΈrYφ}ŊϘъ1LҊkgigϘ݊og3f3|3ߊY[3 =L3f/gd ,' f)Rx jb&'W *~8d0UPt" ~7a3t> stream x}Un0+CW`$ MEDbɐCUA6ّD^}{l?Ytգ;q\]U}s9n\i|ٺQ]jOtusR^}5s8PYs/'7F3Hy:K-ٖ?p>ݝ2ZkXwm1985];B U{hFYs!yvq`TGy7{ E]&AjZu/?vG_L|z;9ڰƦokܰ^\zu \g$y?qמk(_KdR$| 4hd52HHNsL F.8XV`TR!fn"_LS5w[#McL#F X1+N978Nsk`q KpN8q )q4ϮEp O.?5Yş81Bx.BzҬÀhƘ'g 2xk=6u2,bق6E0F,eL燆LY` YecODV3Μ蛳;zr֟P.O0{S3ux9(uF: }6,V|ƌV|gegV|F_+>O+>G|V|~+>C1 V|B|FB|/g)g1{!>_|&~'a9i0K!cB{XTK5;)NŽbPq<${y儢 1 螡SsWѩ?!~tM(Vu endstream endobj 123 0 obj << /Producer (pdfTeX-1.40.22) /Author()/Title()/Subject()/Creator(LaTeX with hyperref)/Keywords() /CreationDate (D:20230203222408Z) /ModDate (D:20230203222408Z) /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.141592653-2.6-1.40.22 (TeX Live 2022/dev/Debian) kpathsea version 6.3.4/dev) >> endobj 2 0 obj << /Type /ObjStm /N 92 /First 752 /Length 3579 /Filter /FlateDecode >> stream x[[s۶~ׯc<W`'ǎӜ47۹ʒJQ_(őɜ ]Xd%L f4LILjf%, ҳTY$KSI)q "HMioL\%2 |:g> |S.`$fZjR"a FA,eI+DHSRkjz fVAKIB %\A4 Lqj,!AY σ]9L)Hh* 9ˌ3LaZ! T4F:+fR yK+TJS`8k,`%| Ka`%8 db\ P;A0 c> CPg|CjăT)դIA*qP2gg+I"RЃ JусJQ1$^HFWC"( 1Gcєqܜ0z*0TӮg?O?ff*&<`?лvW!5j|&35 #zM͜QEaQ2ЈU;ՒU{7IڑSðjcK^y)+W_QT6cE+ues̺\ו 6dL$Bl|s͚ffb~.袃ٹ!FFҞjlT4n.mrY1;j_gu|6QvIQ]&i*ɸ %!i>A>z%@"{8 Y]%p<{hʋ^g0'0/'yOn]ԃW}b}KŖ}MeR}c?T*ƿ 0קOuUMӧO?QR^@Zq m}Rlj*b :ZH*oQd߫pQB]8Q$Pq9 GE' {>EĠ2jѧZo1YNg9:>a<\~%Ljj52Y{S-ЊJ1wnֱa=.u1j:r@ 烼Q}qP5!AW52u,e?nhL!޳YGd=h< B#B2T}^&YGv 5[dzb9|\\^0=o| 1T(p2|8Ycγ(QWhlP">9p@ bVx=/yx=_ef+> #~i6qmni ev÷֊M{7Z^ H]"؊N3Nҝ7.&\Zȼ5,[7v5zv]n-Xvre.MvH<ҾfGy뫞^fn^t^tɟ" =vE'(`T] Jb-#Ԏwm#zg&ZbҾF}<5 ӾyOI866K2rjVIG*q ;%t\J}zb(^ ]B" Ɛ?zѳgO(113}}wCՁpFr<ʦ?5n.c% >Q18_}_pV7og8TFK0J:8I"_铳G_~wvTb>Jiv_swRvBbN"ZV爟_k^c>qΧ|dK><է ܶE*ś??z4˜m~aDaolc"oAzJzyq(} % Q-Yx5Kiv#:_i-JO£W0`2dup\d^]^c7 ?g$1QgMY+fN1񿞾E4ňàIfF> %s˼Z w^N:ʻbɋèS~PbyzA[**u)>_Y7ZC>)-)dԳ.to'eG=mSJl N9}"^'(y"4I5FY ̳?F"޼lو#nOaЋ3,mÚ=r6y\BMWo<| 8J33v[Nv?  [V 6w1+>fEz7Ѥk&5cH-M4VXnX{s@,L'#)X /Zi Zm2Y7ʕ^%*+m+mcа.cr5ۘ͢aT WMŢъTM^ϰt׳|¼4}VOH endstream endobj 124 0 obj << /Type /XRef /Index [0 125] /Size 125 /W [1 3 1] /Root 122 0 R /Info 123 0 R /ID [<97A9AAC4B06439F97D750D06C42D1742> <97A9AAC4B06439F97D750D06C42D1742>] /Length 324 /Filter /FlateDecode >> stream xҷJA@55sלsmEAQ|;@{_PBL{8+"x>* @dX@ ,@(AC8D@1A$DA B4@@,ā!UEH1aI)͆4K`!/CW 9*+ !  Tf]2/+*TFjTvlzh&hh6hW [N#.UjªGuתWĪO3~==z/Sk~r|^;mcaVa aC}~J3 endstream endobj startxref 200623 %%EOF geometry/inst/doc/LICENSE-NOTES0000644000176200001440000000040613431000556015517 0ustar liggesusersAll files in src/ apart from Rgeometry.h, Rconvhulln.c, Rdelaunayn.c Rtsearchn.c, Rtsearch_orig.c, Rtsearch.cpp, RcppExports.cpp, Rinhulln.c, Rhalfspacen.c and geometry_init.c are taken from or based on Qhull (http://www.qhull.org/), and covered by COPYING.txt. geometry/inst/extdata/0000755000176200001440000000000013462263674014531 5ustar liggesusersgeometry/inst/extdata/ordination.Rdata0000644000176200001440000000064312424764776017665 0ustar liggesusers r0b```b`fdd`b2Y# '/JK,c``I1mwƺ} K CUZeTn;JPsi}m7#v1?{D-5SyٿT׼?0\\@bHq4%Psx*dBR Ugeometry/inst/extdata/intersectn4D.RData0000644000176200001440000000757013454046274020021 0ustar liggesusersmy8m6eΐ)e*2<4_&$$BHeNRHJ%hx~Hɔy {}Ϲuuu?wد'H$AaAVH`a"Hb O_X^8A.'% ;RZ]8xt?8x"!Ft 'M|2tUt-"Vx @[~}0t͓H{?z.C LLݮ,:@讉o΅teljjn6㢶Q^T,G u >;lwM*r@h-ORjm,LFaкg6oTXkgز#a:+& 9 _A]_#[B}LK~` n ĵzx>-d{Z?ߝS,6F]jB "H_ Ω /=sk>l 3+m`~}:N8Zw+D=(By cU2ù9 'TԐ&*Vi:܃g3\:yr'0Z},B`L%!",UhgٻJmrbEOTCH_\:S*AWsΗq4e)U LԼCAǑbȼg:!_n%8$\`&9{ ź?ga8txd./bF-|'F뼝c=``-qlFna^h  , }S8H7]/q29Aeܞ"rFf¦os*9ԍ h膱\WEvRql~I2K$؍]aX:Y &X$z@_r\@teo(ݴ ovZOÂcQ Ϩ!‹5^(ԟHQnSWϛۢ{Br |[j}V%>ڗls!<).tʑ*jeBlB|.?ry9;qZ hrQ3 OK12h7>54|:ͣ,ߛf?6)}>Co'!P]Ó6? y6g恌Vhw0_=W_X`J#fIn3mJ~ ')kp[}5Te]*MNIcI5>{l_" 3ѥ_-Y#s4ݦն{!|] U'cq c`(Z1MQ%چgv =~FMh銘 A`Rvq^Rje*p+*3@v0/WN|MN\ 8T:́uА{W¨֘Dh3L>= Yi80iv_ף=D422O7cd3!,n;A"4kVo:{8svibQGIHO5- ZrӴ21`K:U^mɳη Yvhp+w_C8v^ &v5_kߣoPH*_u;^5Ô.\\􂍾Ù4(mܛjD w{C dq` Ն%L=d>ގ[O˞kl2&m!pk@zM+J:Ka!otH) A}#Q!OhO4ɝNiM) `KSTtanc u }/J㭽@`K?'1y"qC͚:)Ne)ݩγdo܏t뗄ayUs5Uizo6$㺅|>-߃w+87qhe#؛x%q8a;ӺZbچnfQe)6nDp \ip]W׆' ah}~K1zxv phۀy`Ϛtoj&US)0%}TNr(x#Df$P~H0ԺwK\z%s!?:1[ص9NaJJXڍGhgjsف s(2Io-y$m"J"]'.a aIMjٝ?:8# ?6-yԖH '|#Ny࿠Y-|U7ߦ0KawzPh,r%BEӡ|óؐoʿ[< i|G)d)iE>eF(t+&p$|W{M/,s҄HLذYvX7BH,ZI~6K7э4_ageometry/inst/extdata/save-overlap68-557.RData0000644000176200001440000000055513461010245020532 0ustar liggesusers r0b```b`faa`b2Y# 'HHY<z,j2]St'@(.5`8PB)/p8T`,S2s/q@9b [ B0VPD7ޔy3CÁCI~ =rOTi}@_n"Blzt\ l67ӕ˶r(~rtvNJ[?g(oy_A9JKe92W{)[to2D@)r?Rq@%9@ mF$KWAS˜ 5]%L k4:Qgeometry/inst/extdata/issue35-intersectn.RData0000644000176200001440000021403413454046274021122 0ustar liggesusersw4{D$(n)-EBRZF-"[ y{#{io?t^(9҆y@O6Cً߳Ka6NCWlI4g.[|2_ Džz>fvt)Î&`_=[$ssxް7"ƥ0o$ 7@d!º*&&u>8<:v&z3-=1)jpwPq$t'48=̯bJϳ& S۶}ˇݚz"߮g{ ?WZ0Y-}V]}̽_h6??eFldF X-a M԰,hcvo.l[$L4ǞvGep8d\j@A+(Q CD%DSfmvUZ\㵅CF*$sݤx=I{K%3 <׮nBs&6Vm`t`c#-A0,x`y~x͛`/IUJC]3B3NοNJ6{0W]H-5cRBYƲ[j_avF߳[g>I݋ۖ3f@]cohtgK6dLItpLW)6ފu5>kNA%PcwcG!EQ;nR>4nF+ ;!H)' 'fM1 s $K 3Zc7%׏i և"Xzm h7ERdbCHO}{y9A<0O0y iÀ:a8fW* g֝J n_3u{ooiׄJmASn\Y@0ԷE]N]LzwLe xmUu1,GX"$qx>i՜ =9m"dVۜ9&6}T/$**0c1CX۰͝0xU28>0F:~ݾ}X+^{S%ɲ]<b>K) Kq?F "w1M0_ggSrWcNG 1Z% U{"TSV ~/?hGp #*AgHH%hQ,54>]W#m1B2dSz7fֆP@%r\.p&SN4yl(Qa8*8t>nđ3/CAǍ_o\9X rk=XW)(<Dzj]U-Jcͧ&Pe$$ -paMQP,XYl׭ CSd1z3 eͱFW[`,c>wq{7-ه񒙠Y{ z$(m0j ),eOP@9OS 7< cA#GZ[i`u Fh+-rD@gSV9@rrCI&< y~, IjBf+fݧC(ᴌu'8WOpPp5Vn^>c4]Sg[IxBlwv(`tV˺4a.|;`~Ya+~GtLM|WQ5imʵ?ٽe.-lg:D} .] d(aY^yjͅ^Ůq,#oOp=ʨ geVا8bɨR\-qYg%_CM{UK%x&S у"[6[7ÓXvb޵!w3B^lf޶DfE `π$yvއIg|lN =zuU{=˿J 7-L{2 ]r Z/8v@a>g0/(.iwλ պs`f)m6|9<Ѩ&s[!ǫ=Z&aFsRE*2c+8ل$(Sk} h}90Q6A1ۡoXs*d=6M}zT$k4&`]秓jTDc8Vϛ>q;\D ^8D`wM}"BWǗraXs35qe|1 o|dMr.`FwO1AB9Fogc'5RHʞ֑:Q4Mvۏk:ӫ)cNcZEpi(A'^-|K{ Y%)|R3/mq>ә>%h;䛱M0z+/MdL)c;r6|)/F8tua|S7B߄h0Bo܈ЛЪn|ƈ mYԁ~/W^B!s<-G_,a_7_uuԄ٤Φ%x<ԡ 8<|GVj*(f;ϟ86.kb|V炕W8$HTI82$XUOOyٸ>2o:uXZ.=漄Rn^&49ž|Tq=]Ah)³QRa&٭\xVEY&j #1k)݌}נHָ7־513+D`f [(,=DqVIb.AA3Si!B __@i~ g?r A @]'ڳE ~*ߊ{ͧQz7aZm8D̈́CDo֐*z2QʟHł&1>*܅]jbwq@Sci01?ur'cJǭc+~E]gZ~L> n9ZW]o^2vblݓF@U|f>ν 膲zg_)#1ZŋrC˶2ýt,m-w S`690Gr HwӃ4)+)qoSȾ YQ3chzRicsTvy|$1՞>opǬH.' /*$h-3 M ]p}^~tEE[}SrO\HĉA/xRz|A-K('a6r}&N5ON,{M@⅖ msR }==t·`o"^Q)]yd76O;wcTBje)]ciۡɈz:6!Y :EvZȬH}&-FzS'UշGNC ȓ8mceH,j}Ď:Q,70ٛ45Ī݌pvk=g8qB,LZfL=,JkX!: 969}{ORva /jlt:qf{kԱa)ܷJ^ȃbBL ]Y?ؗP[poYX$Mj.nkcKiO>U xjw,M­sR'ϦsΛ .aUTHٳ]t/3KIƁm]mBGcG]ۼ?xY4gPs|/N[tg!M!5WljK);!yIF'~Ǭ •Ve';/OO:wt`+[`1dc8{;tU(?.ds.^8fY\YR>,!4__׌F 1a55P3g+gkR#l.f<OwGJv8!/m/Am%{4U8z`C1})Q6aՃw6 X~ ܥi!N`;Tl@y}GrVBB<""*+C*>*of>gxgNb;Ľj_3MlTqCg3/N'k莏Co;8X_Xrj%Me# apG+?g]߃Kߞ~vyOƹ~WqQUM(¡ rsITn&$6aM0u7kL[~B*5%q8xs\4m̎?Z5Oғd1[xplݟ>c]ؙpf+RoU#1F eä@( M{^__!C'6{Hańia5plȕ{t[^ms%F,Uͦ#(; hޣY"8~K{,Ͷ䞻SC|Q7֏Yz N]KemcW{`; s9jo<| >TKz~8Q!'!jcqATIM'(S0J3Ulbշ탓ڎ( _Y/H3۽!ݻ}#|]5!#",(g?q%Ƅ-3pz%]'L 3z_EG)U}0`YD )^IP+&Yiʅ}*g56BL4;_qUѹPIɣDk ji\Nkѹ|224^']K7}|+(|Pver=;&>{+9܇~xyYZӏ5j*b] A(^gMF%ǠYٳ>pX&KCv{tP3@+ASս0 s”b =e)C$lV,c {@+Jn.<I@1۶o;sx꾖DBVe2sMj]ݓvN./Ag3nAש>}'+5 \.b9ck=t 節A81-Ǡ~A ?t42V5yb(gRRIWK®@y>B? sXni[?ahgwN*JWPBKz`Easѱ7If tw ̾U Hr: ÝŰ}`Ox3`N0nV`bz25T: ӂoF\`3>asJ8A8;R? Fj1υţ ڛ:{t0KR46ZK*dĿb@='QTևFE=X~T;% dLia:G$L$S{ ɹw0+fP %,_q` O3.BH- ş0&yVbɏo9]_4U w=Q S/&xj%65̛I4bpEy5╙ ]PoYgz|GsnxrO^ج^;^ <*±sAO@ͽ#+ϯ͡Hx FBסЍs806Lq)L^ftX{wtoo 4CzK=$`Lly+\{CotJs; K9PM0 44`uQqAb`@$^GdJzh 6 3Eމ|2(UC ._Y؋yEZJQVh˨x;,@AX2fTм)}qS|@Sߥ/}N.?=fw jM6al]'$NB=vkhN*fF4He'hUŠs쳥0wgt)tq腜ߦ ge&1)rr"2Uŝ!54%.kukA'!u*4$b0.H1ME'V"_ǟmEP5wa|sb-_ #Yq25 x4|=?_ǂ~ќXx+WRG ߫0caDZsbH86h+{_WƻqjhVEt2ײZvcJyҷ0wSP?;:Ê$LR (7 vwK+Ǔ #\vn[8a,F#qL5l:xX{;נѬEzmNsśR1=X߄U&?)xIY(ۋB^rW *cEe}ƿ]d%8eZ2X4i-aK*t^6%>_(K`az$c|ؤl?Aب/M }IiTBz^^qTI#ς Λ*LX~x{SG+4oXq’{ӂ=fs9qm]n Zg-ZqwWH5Ve:S÷bʼnGMaf# oBuLdZ;H/gtV{\`א{jj*oH?x ʿqh^ [_7ݍ$gṗlzPpruIEp9EZv&εMEBp![Xt?ݎb/L8%cl=E'^'l+ύLp*nsVؖţwd;paf%lmv`VEw8jGK+:o?9-KLգ|"w|Vs~JWoDeC9lXnz9;keŁ wb?gtm.8h Zyn~ i魫]j,}7.sV:ލƦY\IO%-"|*"~Fe3"yK۾yas];/H0n%ou' J/6ko$8׷<){(Aba=|oଂLG,] z̓yp4F@x.I huș0Pb!˄7<'XkupQXQ{0'vo{zXoek&`_Mojf}zDqV SDa!^7h)Yȍ>7IXU+b~jrI nn.~y_Vֱq)#"F>3.?fyPKљ.P D!VQ}~[]ع(X3ikf_( ͼK^rb.K0`nT`'+Cn_]k/0;7Y/w&ry6A^y(^؋BSjs0hT̟F׬R:7WRANŠ,W-lx2]>x?y <_vAׄ"ƟG:@R_Vk_X58r;c'g{ḕIn,WW̗JW[dc1뱯bvXvіX>hn +_osp"pḲwTR&ݗ"zTewa't$,_tbx;&3cp9mv;. <{P?! wbS3pMjxzG_.'ߴu,7}OSϙЫؕ(s%N6`ñh+a@55ehJqsO}*:~姊s{J;oݍ}%W;7j,%^p8r]>6:b)~3 +>Su~mV˯5Àcnl$؋CL?sp~n8(_Y.4RMy5/5\9zM<TYbNr2ZO$N_ڧ_~tb,?]UOBASoI?v/? _H nnr7f\ML$+~8(utW`2z O>c\?{qM I v%%p?់6^yñGUH4le}m&-X^kwV~8S%E,UbCu4' >4lKX2m<8Oτk>/r8:NUN=ljdAO5Htۇ{drzI֛uc{Sjl8rGaܮ2H|Qw_E=.NbfDv+Ln'MIڞ 3i8ؔd&NjςqyS%8dHC (OڱtnIe{{x Xݟ!=K{Y,c@Vexz.oa ݏ7\g.@H|Ƙ\=G91Y`BGr)HƮTL$[k.VβARp2R>x';*`ۣ=Jyn*kM`ecٮ[y3e} MvTަbHaW .pQR,MթULS$ܢL;bIW@V~Gߕį6o3Sf6*h:T`NqGqV=Wj2B=uC$rs!D(((/^5B`䥣_/L6}a커G"0rRC9/{[y NakñwO1SE|B ;{su,Uc ]mW &q%=up͌k$cѽge"VIM Z;XÆu3!glNě]$Xcq8ih6I\eb?~Vݡ- 61e~Zz8(s:b=7ߙB9(w c@f׭ yP7~d '){_:k,bwm$HgҖm>Sk#W/ާ &$H#d͈cR!H3Frи軦ؑT!YL S:081{j ;뭷Z]=Ž #F 5ֽ3S:Ww./;; =`Z;Nuc }#59 _G}寵v k=ogS!Jwr㦗wX^/̶< {vVm Q^>Vx78|mOkVYO7P[n'Q֐J7wci^0.IFYmS3djuf,q~NÔ367t.kih.?ih6; :EN>~qU+S\{%cR߫Ii2W\bܱ84A0#G{Yo|d/l9i[NXm^7CYP/ nmVZo ڂGLlXTG7d@ $Sd#cj`<P!Qi0՞U`]V~X˹d{{YoOuwXIv^W`TR09`~D}7%IcOOɅFP⋪Yjw]At'> 6-}iLPm:EМNx6`9J9CP _`$o'>Lu_$"<'pcӟVW}Q8>ؙ)xQh bD}8mu c+>i7bEЮǒg {aDё<}-{B»  PBV-4 GlV Ċ)t2y}q%MkDH׉9-J@u8=$g*Ӵi?!޼$7L:vUBV0$$T\ot ge^#L#SIX &ca4P5IwR''Amka{F&~\|m cHw$ͫ$ N$mˎn텉#39";}L4#FPt4\srta EVO,5{jL;<uWSeCE/kJWX7>`{x:2umFϯ,MVmDٲ]:L}8޿w}KɄU.*Tzo*%j(έ;pic.௔8q*?wwfH0f/g8b^y֦9B5fW0sYՏm*n}Pf^q4 24\]?:[Liy鉣3q&wtC /ꅡ'~5bS'P/X  %H,_}߇wiS[f }?lɟo1,#蛲TX9N|czVI8\|UϞzuT\n$p"ez5mEoH8h2|g{oǡN6Yq`)ߜ3 Ll]w 襌^$b`$< 楇4 8;fUAϛ8A-lm۴߼Zv*:9/1!+wSY=*|L ]C#V""'j OZJU{ s13O|56WMZi}$z8?x(xCj8Q9qo#67ܻJP~F`&ŕW=?@pQFp GWI&8N>rDp=Z7"&m{BJj~=X_}eXV=ianfcҸ!3VIp T~5h]8ŧ.|D={C~1Y' C&m*Pmץ`z\V k=o$BᩰXGԬ{]k*:<`^dH`:DAc)۰GY|%p0|6Z%{UbXO4¡~ `\ZwSgLQvJ\_fHWOzmB_k)9mQlRqF3vLL,ȎzOG5X{vmf7{Aog5mJxmVFU׼݃57V |b$ՑVF c UY f׶]EU5 v&l`\Zb]#bW'eҖGQUR* -T]FEE+h}-Jzemu킞AGmTP%H *)[ ?lr= yedžph;+8 ~ ׎S?nA@ٲ8$[EG9ʶvn VUL^ju9R2}ר\ku͆7;IA&cW_#dkUne>Qu `fqk$m>sD,o/JA{ӵQ Soe} uElݺC΋H48]KsҒ$-FC?$>1 *Lύ6'?p ցIPUKuR8Q@a(M*? eyrOroS~84wfQHH[RKDn_rCmB=7Ƚ"'aJ£8ÑWjF0V _IUuY̽jxj?|Pv{F%&Q? {{MٱGxD wn"31HŅ"TU=&ݧ]%5YyGШKuH|=>^źFkv~KYи[%үܹFn.Ug% қ_!tpz7Q~w!?'2&wd$85ߨNu5CN ; aJyv\ H%l^G .Q '!2AIZT>Eke[;R8A Q *Jh ׇ+Z.LWޫ_i^AVt `N3NZMjqX;>sn7[o3 yB36hJb\V+}he0& ܛ΍}wmFL\'mNP6&Z ]2 &8`JW 7H5OJ ; zH;*Ѽȥޫ.%W%NިG8)Dz҈+XRMwKw2s~YQF!k\dCZ#Xnp 894 :6o"xv/?VqsI_:SU;#u*VQnzAvվoDB cf5k_p~ilk4k4CILܾ;K$ :ueE sجPzNε ^4C젟, Ď_ދ'Q>4Y& 0{ ^ƉTS/PV dAɶL!UjC Iklܥ'&P-*Tg.}ȽRNk#EI,_T-q)wpY[ݶD sg7եue;MlI/Xrm5lA &.x >CŮ=Ǧ$`db^Zaƈ62hXqQ̫{Io}z ='N>ĚsaCm|lMHLLE5&X^qBdXH=o E C: gd&9!]bs nW+ YAq/.\Oj ^X~5;3ɷ{lþMdͺf-f{-/j-Z~ز{`#D}{c  ȺJLJoRֿHT|hJ"soZ/ ~kP~ݭ\f H٫=5mZI/{mOμBr"^\#igM' 'W t'4%F%?: MhHtL. I ;%HOLxҐh<`7+nٹB*q C/ʎ쇠?O7 ƭp9c+ٜwZ,b] Sw<){~WCQP4ucgFI7e&ygYYO\fEXދ"n驸 '1}ɕLS檙#)ܳ@pd܄.wiT5;Òf*NpCێ/G:1-MR6)̪YbLu[[sƮmP=:f|_Ucdgh-z77 X#kGiB F#ONaqUsYհ@P(p^«nV9ċ TB-"2\$aǞ:_ě)s,h6._"ƻnxE7JC@Y)b(*^Uz%``<\zUjF}[*^;6C]```W w;~!'0IG˘Μos_wlLۖ.ݘyБJB?T924blԽ3* bBn}qT2^KW㌃;lKr5w˟N0U]X=6sALXVB%:H#TAtXÅv;.1ٻoSR0%؜\PoשݜͳJǬ*'u{_$Rl{Cv㍿ݑNΑm}`hʥDjl}VLǎ2B>wL=}>@1KݼcoxK Qw{"nVx09˥w AsI^;?.J/ʼ˽k}BBOYok1aIýWxL-BuRxpG\?+y}w4=A3f.JůՋ~Y_Gfʋg\>E^W oztW){#fԾ&|a1= >|7\KGz5J^(ķ: [ .2xiC*-Lu{m!mO9P:Zv-I@=aO[%*X&F|~fIM`ehjU=aTXo݊dgANjYT[љaSkT?WBU`~/GW^F%uۘpScCB++A M\:`W#bɆn/+}!߈EFm- @NN|:3{zgF_) H90 >`-P9-m Fm Euy뉈 =^`B~xE]~gc6wvĝCg+6a` h+-Ec4y乚&@Xu‹ :?uIOf88ryC(3 s]K<|/*߽y ۾߰ iXjĢ{nU1`/CZ5<;_EAhr;a 5_Trˋ/J&T} [t)zZ9* υ*ג SO<pahejjj>*H Tṁuk63eL6CYj]'$nl׆c\ԂiGFnŋ4Y$W !GMej r+0$3( KQmGmGٷЩzvݯs#aEjځ:MCJ20\~ M>mS`iS6l8Cq-1.c#ﴚpw6Ve Nœ^-U( ?m<[KGt?8edz]G1I(?4i~q+pr-2DZ9_& ,8tV!>ԓLǻ'0=ֵ. ҷd7ώ݂ ~d\,{)\ 9gz bqz2#?XySiqeՔްUt({\~-&j90 :Y- F %[ }?n{2j#ƳNL/tjBK%Uj{y]pޝ}-}?@Mʖդ59_Cgzfq`p8U8Ůx~$vtQd?X,wuݔ y ,v)}v#.L]]`Ֆ1;Tl;m#8mrܨk ނ^6ukxk+!e!`:_"VS?~$_6]/Y'Jc_@ԱSK:W 8mZdI9Dξ5ҳ ]/L$›lFHLye0̈́Ҡ#&0~- M߂%uq]0ے_4=[nߦRX%H&j<;X>AC3ϩڭ޴(!;EKU\KTI;rJ.Pl)ӏ B J EKt]d`lr,]'^;f3wVbS1QeZє*-iݩk;۬e? #h5?,#wj_%XJxDWS*} k"(BCЖ$I]&Vlje-7|cy-/x'ؕ*mS\K4m:pE ;X{H9+wW-w=t-%[&ϩnyn8v)g$~F T*Ilv{_MV.Pzx=G8L% ns&_)˄dt e7_aUa :N@'u*8S4LL{<3x ǘ4DOS.l`o8W/Cxܭ4>O* =?6;B˰֭i>{-ASIw #TQf#u@S5 B(W`Wȍ[M`ڇҙJԴ7Ӈlג5}rb`\S?i-IC&v긒[[%jݼ/+`Tp"6`t-Bx5#؏הF+$@pÐ` 6Tz9ȿce)?D'"."n6k&价{~e$U+([}Z"g%psUPp*H4P]0&&}i B`t9{<]M@mS!(q(;V:LkߋudNX\3ޣ79T݂J˖S`Li{R4faD*v&Vպ"NLovAQ`j=t5!5֭?R=WݷVQeCX6?3ur-ӣi'{kt6H-&~H[p} gFۯ|(_DbF$T8Kp!WAٙFjTjf۽#;oi}R2ZKy 2$vbofkHsZM aY. [!]^,c]8]t:haMǐ,8;2ꬌ/,yp! ce`~H%t.,7jwi\۴0¨/Wkrf# ְč]8$B{]ߋ#U43hB'p邛&{,-T#.5.M8}έ숹.iN XYϕwYJ_QTs9ӫwG\l40)pxCYza0͵ IXz:;Z:pxQn{HW8aX.3b_:8Z U.DӘ6Ft^oDt5xOhhR8Wdӽ#]P'GHbFCٞbۡ{usC&53;=OvJ@:s+YV()a,,>M OV^M@_lXKAž𥆞!8bSn'[>O b2/m?ػ+8TF9Ra_I[C*}p\Y+ض iν*8aG_ɩ֥I>Н}ݑf򻭞K{H- !qzgL'_}}6Iܖ(* ;$=dUJWMg׻釭J sSPXkF)Ʀ[Yyo:eQE-n;G>vۈ/,S)4е>ui6{̶2n# t0Gt6$B3V lYMnݬM2$dv{w NMXV4\Ɇ/!$f]))`َ'7L.X7ܶ'qі~ \3\9l+c}p=k_I6'B:5UCa<+48R3dvy;k+]Vn6k'v!^K^kW^K?^6gu .WoH ~i{oR _~ kl۫ :I}W!} D)W,|9X*/n_rPDZ쮧pY>#'@Or$wIBȖ[YT iٖҍ=0iLVԯ'O~AYREʸ]"dX'33ɪ +=1WZV7Eܧy BV+LRc/P;t˝hZ-?G=GCNRŲtS2b+w4;z71 WAf8 >YEOLslܯ )O5.H1{˥2·xN=ᮑ+,͡TX}(bb)mQ;mž-6rOcͅK= ެ% gN.;t|FatK\g^Iy=>\tw!8"Dm+eBZE<.kގ"!uHWmͱLDrJ&W)*l/d}f[5ӓaYӵ"Y? l'&-֒#|_3Hxe\l5mpWnV .ZM3bBpn$?"qcI]!GSHؾ%˃f^fX`顯C"R%{}ff}dƫw~_כCVi&!8W}KO8[m {)y:x{ŗ.NdRT=؛-Ljcniv@'}Žl)()onj:.Ñ͟ LE!8q/oq6#yvPB{jSޜnBǃ8hT;3Ԃ(8? #i BT㽲L+y\ "[8}\ 3xM18}2SͲ8p燖+˱Xdq8~j=bPwF2N['|=dQ{: L3۴9C?³;5l,?zAjmJ<= { ^&arlP"~~+O}ݿ,Aː{גЭwݠmooD$Y·˜躠uRYݳT4P{ΪVј]($t4u~ M3]]0K, ?=F3o fWV^;[jr(}NoIZ'k f!Hf{S-©#'`93%8S?4 6EBD[*TIL%")K1L!2<<޶y|k=ֽ߳j]:8>~߆D*9 UUS]z[op1K]-2d8V*ko7M&R7 ӿ[bo7''32oɉ}D|\.8bȪ}U3엶xmV>G3[Ű9Bd4xnk/?,yx~z_ّ-yI~!Uhr־aN2 c*-ɒڅH}9^^bH@CE*WӯBչ O<$-'<6UsO:1Md~kݧ ωӅmp3+~ܬŀd+s0S7゠ Y_U|!<{i{K&?> |>_%q{Q3?PrOQ"v}w  \WM)KنcA*_&웑:a/ ~%aQuGp|>DW/k7%c2au5rD4Zq3!C`-o ϿkvDti()Dhuyq+f݇@,{cu$@;j%պhN+- ΀J/0S5as~UT]WNMWʶwuno-+A1( 7ULuhb!m#59KkXWD|j[|gnc)Kͪݳ^_'䴖"hJ{|&SEHyqmg0UCSv$` \#ft8~hש4"]`nIBfQRKRۚvSi:_V̾og㴫 }t:5i+og0Ѓye&=yu@A8y)a[ご5i j}{%[;vd[aЛ,ٛF}3 n~ݓ&JgԘrpcNxİI\ѭ =9T%-knϭLpmJy*l7tLkbx1;ҸtL紷.{MO ʯxGXUfjhBV޼s#Zb+?4k#Yud<}Uj4>}C+q`.riyBw8m:RL>SoLNoۈ$we'ljgZj7t8*aq}'`C؅gW;'V.-A$2^~L2%<21wC ί8P,RJۭ \E+RH8ɲwwl+%i?Hvh..'ON &\T G\NCZBkanąbv<'@_|l3/,Z9mJ4ʞ |nt͙^v8i[:, BK"ݹaÜ8?v%nc>0pD9 K'ݗYÅςhԱ{HԫXąPݕXTsw]UZ^q^%7Bv2#Hʞ&8\'-=פX};{^8~NGg0ZJ8x"S ;4Aݾw,mĝ:̩7Lcl.mjT:ǜM= T a`a-3VUc9ˢrP>-`yokM'۬zZ&@L-(G!06iܱupz$ wťI*x߾ʬ1k^1hSX ^=IxjF뉝DR麖^7-|O%c8Ws:[>?,|D)X ڃ$!In;sޡԔ/ ~;Yhd4bbn\׾z]sq |x͘,Mp¥hc@uJq>-?lv2E$!u{oDmz Fx[(V!.%<Um7e4'Һ+.k5uNrV;"W" *LfM2R>=#䌫<:qFp( 5l=M;#Rt; -M`+j~9^r!9:%a[NaO,.F68DRI˜Gd?p۲F?ߓl#LfսӌA+`_P/&0yDI Meֿ0< )Om< G&Dx봷o 9%aMύEKyoM䮛e~H._2y_3ni4XvSŜq-NU&A'xF,;3g˸ \jIc{m-Ryuq.~kC2ɲ/u㛠%hU}/}0⼺tz *ˁn֟"jgTbmZYsaߪ8gkHJoݣ'qղ,̫\$e' wAK7ŽY>Sap+LRK41^WESG?HVU P;J_e5R|X j}˞xⴹ a{=#P'M|/?G-b̼k u̿ lY)x*p:縙q ;(`] 7G.?1HF =Ӂ!vMCu[Wk-BN<$mq;_P'ze ZU1Gjǝ 8~hv+o*Bza"hf%poO%|i"Q琒e#q?_p=2"_-sZ!R:r&R=+IXQ3fO珫UTP.kJ@I>?`Se(.wG}?}&@+vbQ.k{zxm!1+LPV S03050/Xh6Ȉ|Җ痮;)֯/YxΒMmX?$|)=S:"68Lןǁ~38sC;S*R@r`#4iR !A g%ef”jJpw["kA:"Um;q~8@*zr| i^5'i`X%jkl+~sǮişkN48p, fD_;|q| .&tt2=ZGq*qXl2;'D(A՝"ϛq]/W7 ؕC5Y"ʷNL$Gia4KA[.柦l HRtal\h!X(˵H"rӏ0ˆ׹@?U|dãaap!֓;pYn\;ȨKʁ6N.&RU9G{ƾ+>1g%J_WX102҇_/Ė^>J[K$z#-v`Ʋ'rpCR)XzjqK(gVU6n65! c\3F_}Mn<߅?MEL}=s74GUl[Oj\>Zr+0IgB}^7|C0[{K<6L~rp'pnw4d~3sl_Ź ;éݣ96 pm"NSZD=||P,2skNB*aT_%oK;nLxsx\筳FjOÆ~/wDѷDj$!؈' ^Дrm>֤v>nC4 9~ᙀwnci;9{;(2A+3H|`'K^bP{s?s]v8r)L*S =:k5Cw֊B矅ݰ/߃hX ; )p>\w߉qiOr^ϱC/հX+Y#5/R92y< wѿؠuX睼bKJ`q.!'IRrHpǠ'6H,{]>ɥcmƐKbBHso }іQ'zΫd5kLo8Y[H%]_Knw7rۤ4f&ɍ#;>HI,9Gﻵsb%?X{Og0熞/&-?&pU!8&d/6uY)< V86=ގTbp1Wnl(lxO^a{ӵkl7Vn=ZieEyvi!Wg 4͈49v,/Y WU9ςҧjl}a8xW}g`$<ݩ ,F,i~-|_k :r6'`o_5;z,F{0y~SA.] {˛o\cɳ64+5bΗd*+1;3}WJ /=lzlҖgppNspv,~ iFڋ`By 66S,nH}{Aƹ_O/z|i%-Gf؟0?O|lG2EUqDrobU qu\,>ô,>K009Y~>%ƽWLu^/m<8j6W_zJhSGj\JtSO]TOIdgּtS֯h{KDľw )R{ͱybB; ](SôB^X1cMM#I4wرrָ۝.~rIu(l,Ұro/W|FlK$?Hߟoqǩ-Rݳ @q.^e*)MG #v zA~'0OjC48t’[,w&ihUՉDZYŠ n(KȊw͎(igT]#M,} OzRyg9<*@D+{qWm}XƵx,v' GD/±~xH`yiLԂ'bcr~k3̡9㬄dPSLP)Obia.9 e,D1V"S]Xvk(g_a`l tQY`yr扸@62?֞fr˶z/>$TL^#~nri'}pbM ~"B>\M ŅWUnHѝ\.Y?E潹Y< 7to8ye-k^>JdΉDPъ)\MVJoǵ';>mj71Gru1=Gp\ Q ; vϦ0+YS ƅ]2yO<&6MH;mJ~{qrߞ 8_jasq gX ОgcHء%YKB[aL麵;kzq#raңpXK.lָj{F`x\3+% ;@wu3@;xɴ"zS%wv/v.Gy<ƞv3-.d\싱?`%:M4‰}TL?pzvn;Q2qys{ ή|^i#3ΏZHa0$83V&8%cTŋEulFd@/|y~X< վ VIW+8rWp7d6+Gqe}}DKFfOecSm_`t?/+ @ x6wV$ Ճ=aae͇B*W[a|4''e>"PǏ/n"`ZZtrOG,|^ O'}؀} RpZ T75TS=8F])>TJpnQ}z5!ܙ͟bK#ҸH1)GmȤ^vЪS} _DB!.psWuImP bM݋ת g&_LGT0^v/C߾[pLOVUmD iL@.Y8kɎyWgl[c@m1ryJh.Ŭ [.j`Q]?ja+mKxp LciXEڱ0륕3ifU0#Bk{`O"ҏj݅ut!X9xJ/۰dXiʖΡlL塷HL/9Ë RK^V8^.-ozU+^=m*V_&6r8|5@ (Nl v]۠3Sjw,kԹspX5B:hb 3GVgaLG\ ̪,0w , # !9\sEJ&ޗaqM:вw6˅`oVÜ@k}*]<E `v*VLB):0%H0 R[k$¨$r~X{yŕ@zCPUK=ٝ,/?23N.F\Aۡ Z.k't9Bf#j&pHLWI70wH?3=Ջs%7E"u_sin45 N>d,~U']zU}Oߏ*L#yA~N 'r>lIje"/-/ /Ч O|, Q\dI=8gjWPI]җn y|G-{6QuP|O<h> ~xyߛY6ƣJPmp LD'LO2CHsRJ7ӊ*xq/Bxu0K[,}U67ꚺn@^9=č&G? tG b{Kz;Vx^;ƆKso:ҘR dMu0l'2dsmIfߕ5{f44vq_5-\1Rm D"nwL;zHռ_s?mv!S/fO0[?@ i_B=1Y*"=͕q`Ddjy>8iG7,t _l qc(Oi9Ѹ-z&(Hݨ%RSJE|a.'-z9 RF\'h5XȸIiy}DFd::ZMQk5vݵ}!\W%WJiaªpы$/)HYp?璲[tfLf?f\HG|]×d[Y$P3Ao1LvHj$ ?o{`͙z?O(Fk"%>cUt 3|uhN Z"ɞ; GHB=M|`g=ɉB꯿N Fo-i; }"A*h?ȧ,w;&wG7#0Uns?]=s#o`PXYeeH+5u:K.a[{~01 FvBw!63uFf`r+K9AcC6;lwHool|`r;jJ0>5>;PhlDK E{z˂1*[$-?̜u9JŎ>lKe w)\4SN۾ \0U(0*0` ?/*SQőhUClk_VVAe*^? ǛaAB,U0K誜$p>6E`J<8Ie)u}YBr)^1IC&T=G#'7M'{q‰ѿA9%f]$7n"ٷj~>&Q߱o|W7놽qĊ+ uA@'jH1սs+N.gMIc=Q{^W l1j<3 ZOl@9Q?UmЄM r~Pv_ c#+IJ%3_r9$ AgU0!c0|ٔ$5#L!r#ܖs_>b_pBS(׳#* ]JXAO&R cJ_ےy#"B[aH\ $ (+!ì]돣 t~_>{ǟU`kr@.X9}Ժ3.0*=tsK R,ge0}UM"Z=eP<̬LqJo L}PC5V}i[>25q&Z^f2 =_O$BaqT-V_m4o&CS${}iHUCjTT ƞo~©.: h&s x9_W|wV&`/Ƹ%ֱ79!Yx1_|iuoS&wmy nת[>׻k &"]<rvN^!)0B˽+ =;m/hp` nb=ͷCu%ev,_c/cdr]cX15]L{**JbUp{$x4s>i8ԉވ=XI?IF؞0TcэP8M8e#ja8oc꩘W@U q,|[ӱśg_ cS[{tRbR[W'_3O='Wf/s>=(߇|q=38`#xD. g(a )Mn|K8n;ic  ok)2REs[=3s{4y}?~qU V~ĺ"MMpj4Y ]C:)Io;8E+"|hǒN!~`K}\,1/+b7maߒ}wek~ɪ܃I;2vAKat| ?X(&O ech7YH 'Q ۅӆ^wCk?fW}W  -obxU{W*Un:Ҥ=$~_ g5 i:%3%&D3PixE/6gXzF u;X{`|:F(ɗzb?,UDT0FnI&\/4fgְ'!`22?-O3F76AoBZꮳYI ZW$yvS<(ІեJu>ǣ%c Nx50 ; f/gMnI|_Tn <ڧCH@ ]ڍ]v0Ye#SC0i=i`="!XX57 iVu/H0l[ʀY~-Fjq0v~ $٣sy_zu_NŸ|gۭ(x*R9 e^x]@Gm3 ؛|F_]|\?5I'Âըؑ@`)Qc;Fs}ihtt¸eo@xE\ׁԒS0WoHI1 :¨G kLf 8gpqBCwbtNYsݬX9 g6@=EKPFnq+0z=* h!/ RYaL8a35?ғI Zkj`Q(so~澆\i= GKҞb?i5V̔Səľ2'F"b1LF#&EDEir{+/XbGr/w,z|Yn6ȿX߻|l;#+LU:+w]r&NPsw"RznqCſ}`tz'V h g5qdc;d36O79|zOwЭS6ubJ{bۘ[gbgBe=9^/` hBFcODWDw}-cffG@)&;"Mu_w%r|xb;'-3rte\u!t&r~WJ rn'{zD zgK4V G 4 z4D*]N <ã!eO\o9 j h陓w4&̻lصd v6H XzsE$ 8VsϞwo.EGK%9>ف*X?JkY@aZ Kv}As Q˯hw˽7͡Ɛe0 ֲU N*Js'`(r *+˹ss_`A7t3FpzlIdcMܖז!F_ܡ+Id9bfEd!~W2z|"|.i"Mι@DAE^ BD;y*BD&[*JDֲC7pɉMD+ψl{y+qM"uK&} ͽS8ϙ5=:&-E[:cNoI O-K0cu3S V~$Y@'ѧ])Rmc4۾M4!ʣCA@D^(F'S۠&ҧat\MĖ/+t cP((8ԇPAoЫ6Аi* Nu߀.cJ0z`>/\qNW4 d ]Eg a?`,a,|/ {Tm~j=UQd ӸQZwjŋ?@GH ɫ6zdf3Nܔ]0YT1;?xVE:"|7C7 @O]* jR(d;9>8⯱kE#]գ\aiL/Lp[6'ZΑ"lX%C!];J$UzΚ/ڞaL񹺊'S//mC<j,Q 4g#`0ȱ7;v/k14xZ5nYW ##rWjd2ʣO|st8`MkOX>|-<(*aTqv㼳,^o5iI1Cּi=Kqypaǫ$\LHpb;ޚQh;ˤ"I3[iV *x#' dg A0gQ=J0iqztp|5:x CTܡ0VCEf.t&0ypVxYb?>9&X`N_?IM[.vo;\+PsjֽR5|0!3KlzK8 a%.9?(Sktջ`B>#Lu(0Ygtz)P[-Kc?guZg=0<Ԥ! lc&WqP4&|Yt[z,uy?34_u S<+y)\F>װ>-0sS #èA(:d/Q ÂqG2:bݸۉПڰV`qeq(h5v;j~^ߣӹOɝzogע!nVU2ɧ 4W''%z~Ž㛯E1k۴>QVrD =(Ό+#Xhu+.5?-8e*W#\qj"N,|qS~蓅ו4'~f(pŵ#- q`.t#HPH 4N.[XRތjQ)1} ]RX\If@L侀mkj}$^,i5w'50bYa?Nc {1q}pBӳg0Ls6ʴ-@ 7Q#L¢_k!@ ~btIhMaC[P+L+d8a_qY آqfzD6ow9YǖoM)30-OSGlTk㖨0\%qC|05R@ GNt x>c]Bz'R0u@$Vl܋tFROv +_. nTC? u;6}߄~t3SE׌}o+R%Q 'έ8v RbiU^jz'sX_)?{?/KM:O"o~%%Vo@~sK0;o(,}6NL#,q{] 4|m6R.Zx4qVZfyH;9_ʻ,A 5oKl?9ň-8}P$;9Y,l k>gl^h/VC]馣{ѓm&#B^0j܄"?Gcf{fnUrnvxb#-#6IyQBeʗz@'\ix(Q2|HGLd+҉TgdҰ=?LCǓP7KrT`p{*ƫ""}e(5׍…_>7H}3jn"×F6C/"ҖK*Αy7T{Heemg?{>; z٫D:Y9b:@v{QSHdn\o<N0/ڈwuizi&b@Iչ+'TW(NDo2^uHr90zbcUuUqΓK%{wTŚDY/l}qSu:-wM#Oґ Xor7Rݡ3=(h|=y_nS`Vo }x-7#n7uހKo: q,{:}n+r6lEa'$q?9#n7}D!~$n^ $ц Xٹ|%V2ܙX_Lw_vRk:Z.o]yvdy r{ݲ47U+^~%P!7ۢ7Wajߙbxg,A~3s|_Я0xBQ\iT~. x[ޏ@P.,~6-Ak 8v<`2Vfk쳜  \KRZYom 8}i51uh|5-EH-`~Juf4K`DZڛS6sg`=k+ݛ CKdzvԖ?:} jA=>|p=x쮽Ma+D2Lu H崂y%|r_iQ^h0C0qKiv?(^SBil8Hia}?{aFԵlr @/\]6xvDE/Ao=yuu@>^ sW g4+hNM?2@,nAԺ{;[;~5Zz,R)6X< i??{K\NBSuDO}Kfii1`<1~R H;#`.a_8jknBU؍iq{7Hڻ*JӃS섣K7Ep՚ zf P5zgWg?th`H;%N4| A{c>t s|CsS(K)hU;Jr8-&ǻ]AX_CP`e:JBaXO-8YTH~IGOMA݁nB 2oo# 0Y1qD@5N }HdK9\:%7һby.j 7+FCmo܋wlQ߰# !u7ɮ]}D}A\}fp nu.*Xhj>\4hƾg =,FX/5\#9?9v(˸~QC+N2Ixz\>,"gRV0i )v:}= 4+ŅT%gH>L+ B^.Cv )d|Sio̶݅#:E IqQ"GjZ&#6V&(7g fVed>ӆRжWD/~l10&ܧ.Y_,bWK`| OKK:2]k6vzMV?badP%^Dݱ?ϏCtި3^mzO_썄5`M`A}?Ld]bk 36}舔gWu>D#.Oa/pxx4(r|vyF/~fG|h@$Tch" ;& =!-W˫qDJXW^$vVY޷ ?x r+$MvSw=^|I_$eI[XD|[t.i e8pZ+/^=c' ᔪY׭u_z {bU[/"k%p剙k艏"ƶ8+˰Yl\s/]׬c8R?/o?M-miwAxlA2&ͬ-R* ߗ`ZYGeh8>[CL騰qETZ ;ufLᅅ;=Mcr97l}G9{Si5Hzz/Hy5%Vgp)Ս_/TL8HEÇi`YP?P_k,',#`!:A$H54$ܱH7e¢< *?ԻUV* r{=p+0!KXc7G3~UƵGc:J@amCo: ʶBEF"p3RW.).1^;"3+{ûO پBڼhoɐJzb\t]?fe0y{yw7 M=yS[4V2ڇJ5iu>!K ȕ =`>oR_úX I]x1rk%n%[TSp8IuژǢ[jbyvGZsK4D?ElпGshzlyS*xHAy0(L "Ƿ&'ҭݯK܃hJd-l0ofsR4=I!;Թ?u|BdŐUWȪ~J% Z)nj"RZM T_$r; ιԠm5HHhڇge7G7,.|.?o^!lOR+cלTxVt'-_N! %xbűoġʩ8į5㊽#R\ ,kw:f28|SG7M۽A=Q#RH~s0d|X T+_fAsu`rXS۷^Gn&b s_) Ȫ*Qc2QCF$^2׶aNg3^P[sC+SI[^> f?[=~+cM J1fJ3@2ُ[\ns"Lt9^~f'RFVnͦC'BP$U3IW5ZJWYe05b-~rd8fqZP{" y'Jm O\<̓tpQ.0DHr9$rgF}, 8 ]&nGaAHko~^k5AX ņmZ*0s}' Y'|?8ߕCZ_fj=yS| fqŊ^hKx1hez;Rk}a@9, ݧzf c TBFlυ/ݤ+ 0Z2zgHiI$9lgz㄄f6eL J#It = l"KϜLjQog pRL湌7 *7F")A铁sSMmqIxx}z/~Oٝ|ߦnܒT0W$K=iO>DZuC9WX_tur&P'> DF+°ie0}K5[t ~` _8o@l|S;H*'ݾA9Y$C+vr%5[':A/agҺ:z&$ qͨQ^&),֯&R9>\EJYp'vGug>UOO'ȲnD~|RnYB 7 pBѳqZ/zscΞW^FCۏ Qoר7aznp-2J)v ָ.*8YVk8 m!Mo )Q>+gh>_X]ojzSf,kVdZbwãFD_TلK4AetM~OO&Vdf;N\κSM%lq(쪳U.s wىi }[ǔ/F@F߶vS$38Dx-?8G=F7@Q%- ]2"nÈXAl ;̕jg!mU u8Q$/9-bL^qc؟d>fe5%?y,PM#sH[:8BtoscHܰ3y=0ʬ.]gfȠrk-}Oc21e*Y΀gp 3?. ({&7xSs _ajlRwl8ubg gNcZKeDž} =R,'tǥX7HkT*qXvt+w]{ixF}4&+/^6iW8-%G<Ŕ/j%̞|iQP^$*nBrUP{$zG{ Nl };n]V,tB}=@Ksr:1"AsL{~5oTH(NT&k™;4^`^N"V.Sόջ{a~܎=g5 ^P8zzn;pLu&L ܇}tJ:_.cAج93.k:a3g+_""zG֬`‡c*kYnk Xȅױ۾تPk'ֱm 7w×<3uT>"c}<'~R%烪AP_3SկEvZب40 vH\yLxpt@K엣Xq*clZNNq'fʱiWV=ąukYʢpIbcע!'. ./zܴ*]ٴkI1B ; 3cP& ><<<]ǽZİiwwp:Ã&45v~.<y^ejkGq}"&.7 E]KMhHП,#8YnvGDLZo͑ƳQB"eHH~k+DŽ)#uL 8`CaF_pDc fPǡS*q98$~-dxvZ4ɤn qnHWrs؅/(Y@Cj̡%Vlq}ZG)*Mtw +ӚmBhʷxpwHÀ[36!"4~ƌ | ΘcC:uYS?h[a3wkl\J}c9J{-X|)ְݘ ?7nBӤcs,`|(sS0n [dh/ 4n.fBʊ0C"Y%Y<}'H`kp`#a}_m[_.BkBg 4p^gDg81 Gmc0ʞ$+KqĕpSgs`;gߨ p+rg,9 ݼ[|y̾E1,,}ŁGߞ:~B4/yptԳcXUꋃooTgXc>c7YY}H9fL|KrߚolF;`hX~i_ɰ¢wܜ) ݇]֟Q>螖SsrpcЉWb8^+|g)(iZ!#d!{[dgW q:~'q%Op׸TL΂-k2b;VhƉo<[)WSiSl08 J;![4ׄW-"K7`D޷X{j.`5f l ꂂ)6Nr!br+w [}<5WN=S<}Na?UH>}.`wHf b["dO>ˎb)S UgV~anpMMn݆m2Ze83bx;_K=Fr2s-vV Dnaq(J_! d|&s\9Ou6ylNbe㛐Zq3vNX qhȿCJͶZ[8HocXp^e6-x?t}^c:@mž<֘w<ѹ9î{2O5֐( m '3+A+@Ŧ .J ŶݪƁ,=ʁ " 6q:0ubRRpge(⻯ܹd}f}eXlz}NQNSp9[OvGRl=g$ڃ#v.1˳ a]þC8=PZ}Dj'E$~0[=izCr^8\1evz7;8y:\sЈbG8UΣJ_ҁC7Z0r2An;~8dc~v^ucSC h^ۄ c-.lYt+{jT ra_T l}wv/"v%_&%#\dao|\[Y [֜2HW\wQHStZ}]Xh\7tW[ߋ y@(-'~Y*b!88S5AnzəM_Hrb~BKS?5=hukeرڥnsU(JNXx܃Eg A+ٴpI$O(]3š_ktV1[{JE1 ϋ>Ձ냑*'x` x_5RuOm}2]Lm+1_oޚ!fZa,bCt-{iqVAa.zE)3*U ;`LNd;FFwԚS[9B f=u pZ?_[3M0'lHvهc e J-g<Ӫ}r^V3_1-T{7>΍+0Gؕ`on 6Ҟ9S,J4 :u1,{M 8~K.Nhz1 ӿcL2ټpx=tRpo^vBE8I?M`a ^B2uˆ^$vVbM>.6 g[8ji ̬jUm껐lv/Rr cwViGLf?:bRO86W|^NGd4CE|XѬ#/[DdP#Ddޏ5Dl O2;=.e䊠*_ פB?|&k0alrjuaX!,4|B 0ujH^wO=q݊f K[VHVO52 {$ÀN\@~xewSA`:駽0,ű|Ŀ~*|wlvyH\嫻STrqM;/h7,|hqsٞäL"+[a%p}㓕Nyu=rXm.\?ֈ^>wuѻOޕ 8{W!V~,Caa'vTN_Ksq=Vy]!`jٗ#T7gN B!W@bS#H&MS)!_g@s"%kO-ه-r׿`K2S{>ŻGʬ+aCNK:&gjc]λNe8vRV\6LPI-j#qT>^ȥYK\\^ƲWn @#i3>\rLkktYQVƲ[9Fܨ6ێބ"iO=6_@2v$m;<h($~M~BPʩaN* Yaiwẝ$10o>˪AumKF%ݶرR;nIވ NC0GVݓ8$!9y~:iyb~*Z :_^ j}8%!!qd9CG e4Dp|^ 韓n!bKP V <iʃcP'vf<ߟ,(cHI.| уdo6vp>xGcRs/~M&{DjGJ?K#FG{`-Æ[z_av)\!igbo3H=&6ixHM@ZgU[qJĜ dl<O_Xa/U9vwƌ Y;6tfT]LnAʣ8yWwB[Ds<0ӵۘ.ބ ~;[CY[=2EvvH*~hc?c&I>0d< :'?MܻX~ڣOdA}y##nl v2*sqaq8F +D`c,P& IW^,b?\ʎ'x/DWydu</y}ٺt 4 iEpnZ#[{_z9"ikBC_$D֦[a4p"m~IϊQW dvPo<'$.gInr]~v뵭v+Q_%K0ZoV0DQvl8L_ Z%K~]#Ƒ8ě$c2H z,}3鵮 bQÍczN CE,lR"xaP!-͎.cgfn9MPn.e9lS Z'Hi~el`M[IihoIGjl08+u^kciЀP9vk]RY{.⠏V5ӣX!]:|GqPj#M6!-e yWf@37:͑Y_NǚH{to@ W.[++%qcAy}cq+v70%WqÁџod Nc ǓRl>>`o.} @JU"mBaP%z#F$CoF cQ{7k$!X1y$k2|SN_<'4aX抴]3"u첡^jnr"SCPY5j8nԅZxa֔wm8esWMi|>㜖ߜ)$w }ݏq#li8<]wTͰ^޳*ͤ`T&tA[JgvCoo!(ɫ6uoQ@]T͆'?Mvmބ`ɸ;u4VJg<ĊwN[#5Xn6k?u]S9o&YIb{~]$Yʓyʽ 7سU`&jH (&S(ŧmzՍ8}*7.^¡=kIw0XZ#FC=Ntch 1fr965$"$wqq,5\Z}˭;YH5&CZZ&6X<6Y/)U]ދߟR StM~^3X^S> KUHQĺˎp~E,xzD(d2Gh4ퟚuq=\\XH~ Qz UqN ){?P5{lԮ%~} ӿ{ .,y6)3"1ȿ6_#2p6} Rzc^T<y^G v.é٪kH$l!_DVNExᰭZTI֟zשڄ1ݬ 3=; upaX^O~ucU="=,AqL6qJ}%٢yPZs HZ.uq6KJc[dVU޳9&C!l u=Sǃe3>yH79r\Dvn.7^a$bt[OLAa姞s8XTVq3L0ݣN{{U̇,I8rAF1H+_4st_OF.Ζ3l>GŚ1}NFl~T'5 L1ˆz,6l+dI1Ոd* /rR~ꗐwT6q,'{٠!WR, )sWqFe/r8st4Ҹ iEq@zBח,!oLk 4H.>%@kg8 (/4OyqaV50-|nV8US)Ċ z4gqf:/ipx9;JksC D3V"fMDʰTR6]]/ݦ-/_gLU'L9v\|= RRټ3g| 7>7:+9<g8}޹[$2@{}dAbɸkvulʗ2mea f$ x DacFPFlét|É/Ig}w/LYIi坚 Hjq[y\ 4tq3v<"<|yGs|V.(6yoL`;#icl6 [8W={t3p.QCHu#YYp0/@1^v%B9uQǺͤ^",!Wƙ?"&3CY]=a/9Iޕz3/-H<)ǨiOpV08Yv(y\ ({33[|> ,!~bMs W`U6mډbDaԲ94I#3dhG5uއmLJa:"w%}zۃcyz0ϨZ` 9Qt>WT7>$͓j>y8{fmrVrWߝPW\"Px}2x}Ĝa!Ʃ_b&862@ /5H,=r8 L7gbwmz1sefS aHmvlGhl6ZLnx"vԾ^R/ 41Y=*WD.aE|`<) >܏ǹ]!&8ћU PnVDm|/0uȖk)2>=.IXY}Y;fCЪ6JyFʋ@g#>R6=5c^ѯB8끸1nx]fNn-OЄF,˕ ~H|mc$ՍTlmĆ0|X=dw_x^n8їKX3$Dm{ 셜=pxw\/RAP&'+v+5 5NAL!CiDæHYƑ[ |'Cйt: ;pMՕEbnF`EY>7zI>5BmS *o?*Az7nݡ󚌿܇vA&K';>ԋrSaް4Z*,%;G"aVJ+sJCEWzLaʈ-lʷNLPoeS! <Ⱦf #%z2 ;INRsHp >J0F@ARbNSݺS2{t"N~Y:݈rJsM8o8r؋fDl'㐖p<bP!$gܓ$@kS& <im! IwԁdKO9N=&]:ޢ5YcG8O!}ڶ"|c(m/)qn$/HRʲp0:W,;x1}3M]?<#g'=7DiwuIi @!)rұ*?>\Ci=,H9h3ǭD6f ޜ$]uK͊G_H|U)=׸IUn`fx\7}ݥEo3zSfwwmg7sę'2<8ճ:x+M Eύba޾L}ԜeG)v$/я CPeΏEtm¬H.0}I i'n6}Eu^/<ە\11Y_ç*p)\#_#yOŴ𝁶+q.Z%[OLkrZtXǧlgL[QqVXً /jBض19Pe}p'@)ߑhe]QJW۶Ks c'c4Cg.Rs&3M[V6PޡܒG,=- Z9lĔQ^}6[$vz{K=e a"?qL2B k9c~ubSud[ceB{;%rv8I*F^=!xvIcE )7R)\HU(0; }H!?:v{Zhƚ┉KA$}:iąGB"]}2O5a2,VT`uϊ/׍= [}f^8px/~%o*qpՍN9Dw~Y]m#o/8^:dfXquчTNjO`SoA{X3Uo _xrHV%ndC =4x{3xy*8PS۠r/E6z|= 4yHIl%z5Ƀ@M7yv [7P&|AO/BTBّ;DЬB*mYDۦrA9.f9m;⨭w`'8@1UUijJr\ZHf,w0®M}/?mϮӖY zg8FXIE2YD†/zvl{ ,g];\Up›سc.ClXԺ yNFWa(6mK{lԆK7omdU "[zNBQwl`"?t;N?Xnf8o->8|.GgrPN}t;eއwF^0_ΰH4|8 m{%!3o-unNlABpP06/Me~qD\Xw' =Fw9`MGtY(f:t9̛]Jmpm;=pՈs3/b#3WA0Kĩ7WEzә n9&p550B>3VI]8 `b8Li)*4%?ޟqfCL J(Ze*N3X/1m?TN֋ e0/,p5ߣ|ݐ.4#CIeG< *cpTYpPέR _L/&jvh*=aYcGk8JᄋnZ2fDC \ AP?4eLi@|a{Z\s]P*x^ȏgeometry/inst/extdata/overlap260-5034.RData0000644000176200001440000000060413460563614017732 0ustar liggesusers r0b```b`fef`b2Y# 'HHY<>-`p^7!塀=MH;CP( WZu!Ҡt"F9%3H )*KM-X-D cQ e0@TyM U9J>s&-fuIXr臃]N;xl4,&iA{m|r;~z{Ic>(8[di4hu@+?ѭ'eEAX6Cs7 wً8D@{ yv'6#!zm O"*X0.va{gLŽvf.R o3$^L9@Ă.^Y|HKJVyIHd~̝ԙ39gv-WwĕD2`&p)IeO(iE]F4SK64N1Q'9D;٭H:l;D&Aj+!('N-pMp1H;YYDT<6-]Rn! 9 BjHGQ$jB[t%Q upH:2&U2G}vem@*'s-  4u(N' #QmsMje,IA iAGt = )S/PRtRTώ`Uኆok ') :sI(N.;`F& n^1q'"}<Az٫36vtKDA6#qEq䟇 ~2V$]{A{/,酷ځOCB^ʫu@*R*ޜ0i>l/'VS2:芌syX