3D alpha wrapping with R

[This article was first published on Saturn Elephant, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

The RcppCGAL package now contains version 5.5.2 of the CGAL library. There is something new in this version: the 3D alpha wrapping. This is a bit similar to the 3D alpha hull but this can work better.

So I implemented the alpha wrapping in my package cgalMeshes (currently it is only in the github branch of the repository).

To illustrate the alpha wrapping, I will take a pseudo-gyroid. It is an isosurface, and it is qualified as “pseudo” because the true gyroid has a more complicated isosurface equation. The pseudo-gyroid is an approximation of the gyroid.

# the pseudo-gyroid is the isosurface f(x,y,z)=0
f <- function(x, y, z) {
  cos(x) * sin(y) + cos(y) * sin(z) + cos(z) * sin(x)
}
# construct the isosurface f=0
ngrid <- 70L
x <- y <- z <- seq(-5, 5, length.out = ngrid)
Grid <- expand.grid(X = x, Y = y, Z = z)
voxel <- array(
  with(Grid, f(X, Y, Z)), dim = c(ngrid, ngrid, ngrid)
)
library(rmarchingcubes)
contour_shape <- contour3d(
  griddata = voxel, level = 0, x = x, y = y, z = z
)
# make mesh
library(rgl)
rglMesh <- tmesh3d(
  vertices = t(contour_shape[["vertices"]]),
  indices  = t(contour_shape[["triangles"]]),
  normals  = contour_shape[["normals"]]
)
open3d(windowRect = 50 + c(0, 0, 512, 512))
view3d(-40, 35)
shade3d(rglMesh, color = "orange")


bdry <- getBoundary3d(rglMesh, color = "black", lwd = 3)
open3d(windowRect = 50 + c(0, 0, 512, 512))
view3d(-40, 35)
shade3d(rglMesh, color = "orange")
shade3d(bdry)

Maybe adding the boundary to the plot is helpful for visualization:

bdry <- getBoundary3d(rglMesh, color = "black", lwd = 3)
open3d(windowRect = 50 + c(0, 0, 512, 512))
view3d(-40, 35)
shade3d(rglMesh, color = "orange")
shade3d(bdry)

This pseudo-gyroid is inscribed in the cube \((-5,5)\times(-5,5)\times(-5,5)\). I prefer the spherical pseudo-gyroid, obtained by clipping the cubical pseudo-gyroid to a sphere:

# returns the squared norms of the vertices
sqnorm <- function(vertices) { 
  apply(vertices, 1L, function(row) crossprod(row))
}
# clipping
rglMesh2 <- clipMesh3d(rglMesh, sqnorm, bound = 25, greater = FALSE)
# extract boundary
bdry <- getBoundary3d(rglMesh2, color = "black", lwd = 3)
open3d(windowRect = 50 + c(0, 0, 512, 512), zoom = 0.7)
shade3d(rglMesh2, color = "orangered")
shade3d(bdry)

This mesh has \(21371\) vertices and \(40919\) triangles:

rglMesh2
##  mesh3d object with 21731 vertices, 40919 triangles.

Let’s extract its vertices:

vertices <- t(rglMesh2$vb[-4L, ])

And now we will try to reconstruct the surface from its vertices only.

The advanced front surface reconstruction fails:

library(cgalMeshes)
AFSmesh <- AFSreconstruction(vertices)
AFSmesh$computeNormals()
open3d(windowRect = 50 + c(0, 0, 512, 512), zoom = 0.7)
shade3d(AFSmesh$getMesh(), color = "red")

The pseudo-gyroid does not bound a volume (it has no width), so the Poisson reconstruction cannot be applied.

Let’s try the alpha hull.

library(AlphaHull3D)
ahull <- fullAhull3d(vertices)
amesh <- setAlpha(ahull, alpha = 1.8)

This value of \(\alpha\) is both too big (we get some faces that shouldn’t be there) and too small (we get some holes):

open3d(windowRect = 50 + c(0, 0, 512, 512), zoom = 0.7)
shade3d(amesh, color = "green")

So there’s no way.

Finally, the alpha wrapping comes to our rescue. The alpha wrapping depends on two parameters:

  • \(\alpha\): the smallest \(\alpha\), the smallest triangles in the output mesh;

  • offset: the distance between the input and the output.

In the alphaWrap function, we give the relative \(\alpha\) and the relative offset. Then \(\alpha\) and the offset are both obtained by dividing the length of the diagonal of the bounding box of the vertices by their relative version. You have to play with these parameters to get a nice mesh. A too small \(\alpha\) yields some holes in the mesh, and the mesh has less details when \(\alpha\) increases.

wrapMesh <- alphaWrap(vertices, ralpha = 100, roffset = 1000) 
wrapMesh$computeNormals()
wrapRglMesh <- wrapMesh$getMesh()
open3d(windowRect = 50 + c(0, 0, 512, 512), zoom = 0.7)
shade3d(wrapRglMesh, color = "orangered")

Note that we cannot add the boundary to this mesh: it has no boundary. It has a width, controlled by the offset parameter.

To leave a comment for the author, please follow the link and comment on their blog: Saturn Elephant.

R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Never miss an update!
Subscribe to R-bloggers to receive
e-mails with the latest R posts.
(You will not see this message again.)

Click here to close (This popup will not appear again)