Computes the smallest convex closed surface that contains a set of points
(for a surface, its vertices), with the Quickhull implementation of
the bundled VCG library or with a port of the quickhull3d
library to 'C++'.
Usage
vcg_convex_hull(x, method = c("vcg", "quickhull3d"), tolerance = NA)Arguments
- x
a matrix of point coordinates with 3 columns and one row per point, or a surface (see 'Coercing Surface Inputs' below), in which case its vertices are used and its faces ignored
- method
algorithm:
"vcg"(default) for theQuickhullofVCG(Barberand colleagues, 1996), or"quickhull3d"for a port of thequickhull3dlibrary, which followsLloyd'sQuickHull3Dand merges neighboring faces that lie in nearly the same plane; see 'Details'- tolerance
non-negative distance, in the units of
x: a point closer than this to the plane of a hull face counts as lying on the face rather than outside it. The defaultNAuses the method's own value:1e-10times the diagonal of the bounding box for"vcg", and for"quickhull3d"the rounding error of the coordinates, three times the machine precision times the sum, over the three axes, of the largest absolute coordinate. Values below that rounding error are raised to it, because below it the visibility tests contradict each other
Value
A closed triangular mesh of class 'mesh3d' with:
vbcoordinates of the hull vertices, each one exactly an input point, in the input order
ittriangular faces, ordered counter-clockwise when seen from outside (face normals point outward)
normalsarea-weighted vertex normal vectors
and attribute "vertex_index": for each hull vertex, the row of
x (or the vertex index of the surface) it comes from, increasing;
attr(hull, "vertex_index")[hull$it] indexes the faces by input
point.
Details
Both methods start from a tetrahedron of extreme points and repeatedly add
the point farthest outside a face, replacing the faces that point can see.
Points within tolerance of a face plane count as on the face.
The VCG implementation keeps every face a triangle, so a flat region
of the hull (common for points on a voxel grid) is split into arbitrary
triangles, and it relies on the tolerance alone to stay consistent under
rounding. The quickhull3d method also merges neighboring faces that
lie in one plane or are not convex within the tolerance into polygons,
which it splits into triangles fanning out from one corner; such a fan can
contain triangles of zero area when a polygon has three corners on a line.
Points that are identical, or all lie on one line or in one plane, enclose
no volume and raise an error. So does a result that fails the check for a
closed, consistently oriented surface, a numerical failure that a larger
tolerance or the other method can avoid.
Coercing Surface Inputs
The surface objects are converted to 'mesh3d' object before
applying further calculations.
When surface is a surface ieegio object, the returned
mesh3d$vb contains vertices that have been left-multiplied by
surface$geometry$transforms[[1]] (the first transform stored in the
geometry, typically the ScannerAnat or voxel-to-world transform).
Breaking change: Earlier versions (before 0.2.6) of ravetools
returned the raw surface$geometry$vertices without applying any
transform, so downstream code often multiplied by
surface$geometry$transforms[[1]] (or an equivalent) manually before
working in world space. Such code will now double
apply the transform and produce incorrect coordinates. If you previously
applied a transform from surface$geometry$transforms by hand after
calling a ravetools mesh function on an 'ieegio_surface',
remove that manual step.
Surfaces with an empty or missing geometry$transforms list (for
example, surfaces produced by ieegio's volume_to_surface,
which stores an identity transform) are unaffected.
If geometry$transforms contains multiple transforms targeting
different coordinate spaces, only the first one is used. Callers that need
a specific target space should select and apply that transform themselves
before calling ravetools mesh functions.
References
Barber CB, Dobkin DP, Huhdanpaa H (1996). The
quickhull algorithm for convex hulls. ACM Transactions on
Mathematical Software, 22(4), 469-483.
Lloyd JE. QuickHull3D: a robust 3D convex hull algorithm in
Java. https://www.cs.ubc.ca/~lloyd/java/quickhull3d.html
Poppe M. quickhull3d: a robust quickhull
implementation to find the convex hull of a set of 3D points.
https://github.com/mauriciopoppe/quickhull3d
Examples
# the 8 corners of a unit cube and 100 points inside it
set.seed(1)
x <- rbind(
as.matrix(expand.grid(0:1, 0:1, 0:1)),
matrix(runif(300), ncol = 3)
)
hull <- vcg_convex_hull(x)
# the rows of `x` on the hull are the corners
attr(hull, "vertex_index")
#> [1] 1 2 3 4 5 6 7 8
vcg_mesh_volume(hull)
#> [1] 1
if(is_not_cran()) {
data("left_hippocampus_mask")
mesh <- vcg_isosurface(left_hippocampus_mask)
hull <- vcg_convex_hull(mesh, method = "quickhull3d")
rgl_view({
rgl_call("shade3d", mesh, col = 2)
rgl_call("wire3d", hull, col = 3)
})
}
#> Package `rgl` is not installed. Please install `rgl` to use this function.
#> Error in loadNamespace(name): there is no package called ‘rgl’