Learn R Programming

cgalMeshes (version 2.2.0)

cgalMesh: R6 class to represent a CGAL mesh

Description

R6 class to represent a CGAL mesh.

Arguments

Methods

Public methods


Method new()

Creates a new cgalMesh object.

Usage

cgalMesh$new(mesh, vertices, faces, normals = NULL, clean = FALSE)

Arguments

mesh

there are four possibilities for this argument: it can be missing, in which case the arguments vertices and faces must be given, or it can be the path to a mesh file (accepted formats: off, obj, stl, ply, ts, vtp), or it can be a rgl mesh (i.e. a mesh3d object), or it can be a list containing (at least) the fields vertices (numeric matrix with three columns) and faces (matrix of integers or list of vectors of integers), and optionally a field normals (numeric matrix with three columns); if this argument is a rgl mesh containing some colors, these colors will be assigned to the created cgalMesh object

vertices

if mesh is missing, must be a numeric matrix with three columns

faces

if mesh is missing, must be either a matrix of integers (each row gives the vertex indices of a face) or a list of vectors of integers (each one gives the vertex indices of a face)

normals

if mesh is missing, must be NULL or a numeric matrix with three columns and as many rows as vertices

clean

Boolean value indicating whether to clean the mesh (merge duplicated vertices and duplicated faces, remove isolated vertices); set to FALSE if you know your mesh is already clean

Returns

A cgalMesh object.

Examples

library(cgalMeshes)
meshFile <- system.file(
  "extdata", "bigPolyhedron.off", package = "cgalMeshes"
)
mesh <- cgalMesh$new(meshFile)
rglmesh <- mesh$getMesh()
library(rgl)
open3d(windowRect = 50 + c(0, 0, 512, 512), zoom = 0.9)
shade3d(rglmesh, color = "tomato")
plotEdges(
  mesh$getVertices(), mesh$getEdges(), color = "darkred"
)

# this one has colors: #### meshFile <- system.file( "extdata", "pentagrammicDipyramid.ply", package = "cgalMeshes" ) mesh <- cgalMesh$new(meshFile) rmesh <- mesh$getMesh() library(rgl) open3d(windowRect = 50 + c(0, 0, 512, 512), zoom = 0.85) shade3d(rmesh, meshColor = "faces")


Method print()

Print a cgalMesh object.

Usage

cgalMesh$print(...)

Arguments

...

ignored

Returns

No value returned, just prints some information about the mesh.


Method area()

Compute the area of the mesh. The mesh must be triangle and must not self-intersect.

Usage

cgalMesh$area()

Returns

A number, the mesh area.

Examples

library(rgl)
mesh <- cgalMesh$new(cube3d())$triangulate()
mesh$area()


Method assignFaceColors()

Assign colors (or any character strings) to the faces of the mesh.

Usage

cgalMesh$assignFaceColors(colors)

Arguments

colors

a character vector whose length equals the number of faces, or a single character string to be assigned to each face

Returns

The current cgalMesh object, invisibly.


Method assignFaceScalars()

Assign scalars to the faces of the mesh.

Usage

cgalMesh$assignFaceScalars(scalars)

Arguments

scalars

a numeric vector whose length equals the number of faces

Returns

The current cgalMesh object, invisibly.


Method assignNormals()

Assign per-vertex normals to the mesh.

Usage

cgalMesh$assignNormals(normals)

Arguments

normals

a numeric matrix with three columns and as many rows as the number of vertices

Returns

The current cgalMesh object, invisibly.


Method assignVertexColors()

Assign colors (or any character strings) to the vertices of the mesh.

Usage

cgalMesh$assignVertexColors(colors)

Arguments

colors

a character vector whose length equals the number of vertices

Returns

The current cgalMesh object, invisibly.


Method assignVertexScalars()

Assign scalars to the vertices of the mesh.

Usage

cgalMesh$assignVertexScalars(scalars)

Arguments

scalars

a numeric vector whose length equals the number of vertices

Returns

The current cgalMesh object, invisibly.


Method boundingBox()

Bounding box of the mesh.

Usage

cgalMesh$boundingBox()

Returns

A list containing the smallest corner point and the largest corner point of the bounding box, named lcorner and ucorner respectively. Use isoCuboidMesh to get a mesh of this bounding box.

Examples

library(cgalMeshes)
library(rgl)
rmesh <- cyclideMesh(a = 97, c = 32, mu = 57)
mesh <- cgalMesh$new(rmesh)
bbox <- mesh$boundingBox()
bxmesh <- isoCuboidMesh(bbox[["lcorner"]], bbox[["ucorner"]])
open3d(windowRect = 50 + c(0, 0, 512, 512))
view3d(0, -60)
shade3d(rmesh, color = "gold")
wire3d(bxmesh, color = "black")


Method boundsVolume()

Check whether the mesh bounds a volume. The mesh must be triangle.

Usage

cgalMesh$boundsVolume()

Returns

A Boolean value, whether the mesh bounds a volume.

Examples

library(rgl)
mesh <- cgalMesh$new(tetrahedron3d())
mesh$boundsVolume() # TRUE
mesh$reverseOrientation()
mesh$boundsVolume() # TRUE


Method CatmullClark()

Performs the Catmull-Clark subdivision and deformation. The mesh must be triangle.

Usage

cgalMesh$CatmullClark(iterations = 1)

Arguments

iterations

number of iterations

Returns

The modified reference mesh, invisibly.

Examples

library(cgalMeshes)
library(rgl)
hopfMesh <- HopfTorusMesh(nu = 80, nv = 40)
mesh <- cgalMesh$new(hopfMesh)
mesh$CatmullClark(iterations = 2)
mesh$computeNormals()
rmesh <- mesh$getMesh()
# plot
open3d(windowRect = 50 + c(0, 0, 800, 400))
mfrow3d(1, 2)
view3d(0, 0, zoom = 0.9)
shade3d(hopfMesh, color = "red")
wire3d(hopfMesh, color = "black")
next3d()
view3d(0, 0, zoom = 0.9)
shade3d(rmesh, color = "red")
wire3d(rmesh, color = "black")


Method centroid()

Centroid of the mesh. The mesh must be triangle.

Usage

cgalMesh$centroid()

Returns

The Cartesian coordinates of the centroid of the mesh.

Examples

library(cgalMeshes)
library(rgl)
mesh <- cgalMesh$new(icosahedron3d())
mesh$centroid()


Method clip()

Clip mesh to the volume bounded by another mesh. The mesh must be triangle. Face properties (colors and scalars) are preserved. WARNING: the reference mesh is then replaced by its clipped version.

Usage

cgalMesh$clip(clipper, clipVolume)

Arguments

clipper

a cgalMesh object; it must represent a closed triangle mesh which doesn't self-intersect

clipVolume

Boolean, whether the clipping has to be done on the volume bounded by the reference mesh rather than on its surface (i.e. the reference mesh will be kept closed if it is closed); if TRUE, the mesh to be clipped must not self-intersect

Returns

The reference mesh is always replaced by the result of the clipping. If clipVolume=TRUE, this function returns two cgalMesh objects: the two parts of the clipped mesh contained in the reference mesh and the clipping mesh respectively. Otherwise, this function returns the modified reference mesh.

Examples

# cube clipped to sphere ####
library(cgalMeshes)
library(rgl)
mesh    <- cgalMesh$new(cube3d())$triangulate()
clipper <- cgalMesh$new(sphereMesh(r= sqrt(2)))
mesh$assignFaceColors("blue")
clipper$assignFaceColors("red")
meshes <- mesh$clip(clipper, clipVolume = TRUE)
mesh1 <- meshes[[1]]
mesh2 <- meshes[[2]]
mesh2$computeNormals()
rglmesh1 <- mesh1$getMesh()
rglmesh2 <- mesh2$getMesh()
open3d(windowRect = 50 + c(0, 0, 512, 512))
view3d(45, 45, zoom = 0.9)
shade3d(rglmesh1, meshColor = "faces")
shade3d(rglmesh2, meshColor = "faces")

# Togliatti surface clipped to a ball #### library(rmarchingcubes) library(rgl) library(cgalMeshes) # Togliatti surface equation: f(x,y,z) = 0 f <- function(x, y, z) { 64*(x-1) * (x^4 - 4*x^3 - 10*x^2*y^2 - 4*x^2 + 16*x - 20*x*y^2 + 5*y^4 + 16 - 20*y^2) - 5*sqrt(5-sqrt(5))*(2*z - sqrt(5-sqrt(5))) * (4*(x^2 + y^2 - z^2) + (1 + 3*sqrt(5)))^2 } # grid n <- 200L x <- y <- seq(-5, 5, length.out = n) z <- seq(-4, 4, length.out = n) Grid <- expand.grid(X = x, Y = y, Z = z) # calculate voxel voxel <- array(with(Grid, f(X, Y, Z)), dim = c(n, n, n)) # calculate isosurface contour_shape <- contour3d( griddata = voxel, level = 0, x = x, y = y, z = z ) # make rgl mesh (plotted later) rglMesh <- tmesh3d( vertices = t(contour_shape[["vertices"]]), indices = t(contour_shape[["triangles"]]), normals = contour_shape[["normals"]], homogeneous = FALSE ) # make CGAL mesh mesh <- cgalMesh$new(rglMesh) # clip to sphere of radius 4.8 sphere <- sphereMesh(r = 4.8) clipper <- cgalMesh$new(sphere) mesh$clip(clipper, clipVolume = FALSE) rglClippedMesh <- mesh$getMesh() # plot open3d(windowRect = 50 + c(0, 0, 900, 450)) mfrow3d(1L, 2L) view3d(0, -70, zoom = 0.8) shade3d(rglMesh, color = "firebrick") next3d() view3d(0, -70, zoom = 0.8) shade3d(rglClippedMesh, color = "firebrick") shade3d(sphere, color = "yellow", alpha = 0.15)


Method clipToPlane()

Clip the mesh to a plane. The mesh must be triangle. Face properties (colors, scalars) are preserved.

Usage

cgalMesh$clipToPlane(planePoint, planeNormal, clipVolume)

Arguments

planePoint

numeric vector of length three, a point belonging to the plane

planeNormal

numeric vector of length three, a vector orthogonal to the plane

clipVolume

Boolean, whether to clip on the volume

Returns

If clipVolume=FALSE, the modified reference mesh is invisibly returned. If clipVolume=TRUE, a list of two cgalMesh objects is returned: the first one is the part of the clipped mesh corresponding to the original mesh, the second one is the part of the clipped mesh corresponding to the plane.

Examples

library(cgalMeshes)
library(rgl)
rmesh <- sphereMesh()
mesh <- cgalMesh$new(rmesh)
nfaces <- nrow(mesh$getFaces())
if(require("randomcoloR")) {
  colors <- 
    randomColor(nfaces, hue = "random", luminosity = "dark")
} else {
  colors <- rainbow(nfaces)
}
mesh$assignFaceColors(colors)
meshes <- mesh$clipToPlane(
  planePoint  = c(0, 0, 0), 
  planeNormal = c(0, 0, 1), 
  clipVolume = TRUE
)
mesh1 <- meshes[[1]]
mesh2 <- meshes[[2]]
mesh1$computeNormals()
rClippedMesh1 <- mesh1$getMesh()
rClippedMesh2 <- mesh2$getMesh()
open3d(windowRect = 50 + c(0, 0, 512, 512))
view3d(70, 0)
shade3d(rClippedMesh1, meshColor = "faces")
shade3d(rClippedMesh2, color = "orange")


Method clipToIsoCuboid()

Clip the mesh to an iso-oriented cuboid. The mesh must be triangle. Face properties (colors, scalars) are preserved.

Usage

cgalMesh$clipToIsoCuboid(lcorner, ucorner, clipVolume)

Arguments

lcorner, ucorner

two diagonally opposite vertices of the iso-oriented cuboid, the smallest and the largest (see isoCuboidMesh)

clipVolume

Boolean, whether to clip on the volume

Returns

If clipVolume=FALSE, the modified reference mesh is invisibly returned. If clipVolume=TRUE, a list of two cgalMesh objects is returned: the first one is the part of the clipped mesh corresponding to the original mesh, the second one is the part of the clipped mesh corresponding to the cuboid.

Examples

library(cgalMeshes)
library(rgl)
rmesh <- HopfTorusMesh(nu = 200, nv = 200)
mesh <- cgalMesh$new(rmesh)
mesh$assignFaceColors("orangered")
lcorner <- c(-7, -7, -5)
ucorner <- c(7, 6, 5)
bxmesh <- isoCuboidMesh(lcorner, ucorner)
mesh$clipToIsoCuboid(
  lcorner, ucorner, clipVolume = FALSE
)
mesh$computeNormals()
rClippedMesh <- mesh$getMesh()
open3d(windowRect = 50 + c(0, 0, 512, 512))
view3d(-40, 0)
shade3d(rClippedMesh, meshColor = "faces")
shade3d(bxmesh, color = "cyan", alpha = 0.3)


Method computeNormals()

Compute per-vertex normals of the mesh.

Usage

cgalMesh$computeNormals()

Returns

The current cgalMesh object, invisibly. To get the normals, use the getNormals method.


Method connectedComponents()

Decomposition into connected components. All face properties (colors, scalars) and vertex properties (colors, scalars, normals) are preserved.

Usage

cgalMesh$connectedComponents(triangulate = FALSE)

Arguments

triangulate

Boolean, whether to triangulate the connected components.

Returns

A list of cgalMesh objects, one for each connected component.

Examples

library(cgalMeshes)
library(rmarchingcubes)
# isosurface function (slice of a seven-dimensional toratope)
f <- function(x, y, z, a) {
  (sqrt(
    (sqrt((sqrt((x*sin(a))^2 + (z*cos(a))^2) - 5)^2 + (y*sin(a))^2) - 2.5)^2 + 
      (x*cos(a))^2) - 1.25
  )^2 + (sqrt((sqrt((z*sin(a))^2 + (y*cos(a))^2) - 2.5)^2) - 1.25)^2
}
# make grid
n <- 200L
x <- seq(-10, 10, len = n)
y <- seq(-10, 10, len = n)
z <- seq(-10, 10, len = n)
Grid <- expand.grid(X = x, Y = y, Z = z)
# compute isosurface
voxel <- array(with(Grid, f(X, Y, Z, a = pi/2)), dim = c(n, n, n))
isosurface <- contour3d(voxel, level = 0.25, x = x, y = y, z = z)
# make CGAL mesh
mesh <- cgalMesh$new(
  vertices = isosurface[["vertices"]], 
  faces = isosurface[["triangles"]],
  normals = isosurface[["normals"]]
)
# connected components
components <- mesh$connectedComponents()
ncc <- length(components)
# plot
library(rgl)
colors <- rainbow(ncc)
open3d(windowRect = 50 + c(0, 0, 512, 512))
view3d(30, 50)
for(i in 1L:ncc) {
  rglMesh <- components[[i]]$getMesh()
  shade3d(rglMesh, color = colors[i])
}


Method convexParts()

Decomposition into convex parts. The mesh must be triangle.

Usage

cgalMesh$convexParts(triangulate = TRUE)

Arguments

triangulate

Boolean, whether to triangulate the convex parts

Returns

A list of cgalMesh objects, one for each convex part.

Examples

library(cgalMeshes)
library(rgl)
mesh <- cgalMesh$new(pentagrammicPrism)$triangulate()
cxparts <- mesh$convexParts()
ncxparts <- length(cxparts)
colors <- hcl.colors(ncxparts, palette = "plasma")
open3d(windowRect = 50 + c(0, 0, 512, 512))
view3d(20, -20, zoom = 0.8)
for(i in 1L:ncxparts) {
  cxmesh <- cxparts[[i]]$getMesh()
  shade3d(cxmesh, color = colors[i])
}


Method copy()

Copy the mesh. This can change the order of the vertices.

Usage

cgalMesh$copy()

Returns

A new cgalMesh object.

Examples

library(rgl)
mesh <- cgalMesh$new(cube3d())
tmesh <- mesh$copy()$triangulate()
tmesh$isTriangle() # TRUE
mesh$isTriangle() # FALSE


Method distance()

Distance from one or more points to the mesh. The mesh must be triangle.

Usage

cgalMesh$distance(points)

Arguments

points

either one point given as a numeric vector or several points given as a numeric matrix with three columns

Returns

A numeric vector providing the distances between the given point(s) to the mesh.

Examples

# cube example ####
library(cgalMeshes)
mesh <- cgalMesh$new(rgl::cube3d())$triangulate()
points <- rbind(
  c(0, 0, 0),
  c(1, 1, 1)
)
mesh$distance(points) # should be 1 and 0

# cyclide example #### library(cgalMeshes) a <- 100; c <- 30; mu <- 80 mesh <- cgalMesh$new(cyclideMesh(a, c, mu, nu = 100L, nv = 100L)) O2 <- c(c, 0, 0) # should be a - mu = 20 (see ?cyclideMesh): mesh$distance(O2)


Method DooSabin()

Performs the Doo-Sabin subdivision and deformation.

Usage

cgalMesh$DooSabin(iterations = 1)

Arguments

iterations

number of iterations

Returns

The modified reference mesh, invisibly.

Examples

library(cgalMeshes)
library(rgl)
hopfMesh <- HopfTorusMesh(nu = 80, nv = 40)
mesh <- cgalMesh$new(hopfMesh)
mesh$DooSabin(iterations = 2)
mesh$triangulate()
mesh$computeNormals()
rmesh <- mesh$getMesh()
# plot
open3d(windowRect = 50 + c(0, 0, 800, 400))
mfrow3d(1, 2)
view3d(0, 0, zoom = 0.9)
shade3d(hopfMesh, color = "brown")
wire3d(hopfMesh, color = "black")
next3d()
view3d(0, 0, zoom = 0.9)
shade3d(rmesh, color = "brown")
wire3d(rmesh, color = "black")


Method facesAroundVertex()

Faces containing a given vertex.

Usage

cgalMesh$facesAroundVertex(v)

Arguments

v

a vertex index

Returns

An integer vector, the indices of the faces containing v.


Method fair()

Fair a region of the mesh, i.e. make it smooth. The mesh must be triangle. This modifies the reference mesh. All face properties and vertex properties except the normals are preserved.

Usage

cgalMesh$fair(indices)

Arguments

indices

the indices of the vertices in the region to be faired

Returns

The modified cgalMesh object.

Examples

library(cgalMeshes)
rglHopf <- HopfTorusMesh(nu = 100, nv = 100)
hopf <- cgalMesh$new(rglHopf)
# squared norms of the vertices
normsq <- apply(hopf$getVertices(), 1L, crossprod)
# fair the region where the squared norm is > 19
indices <- which(normsq > 19)
hopf$fair(indices)
rglHopf_faired <- hopf$getMesh()
# plot
library(rgl)
open3d(windowRect = 50 + c(0, 0, 900, 450))
mfrow3d(1L, 2L)
view3d(0, 0, zoom = 0.8)
shade3d(rglHopf, color = "orangered")
next3d()
view3d(0, 0, zoom = 0.8)
shade3d(rglHopf_faired, color = "orangered")


Method fillBoundaryHole()

Fill a hole in the mesh. The face properties and the vertex properties are preserved.

Usage

cgalMesh$fillBoundaryHole(border, fair = TRUE)

Arguments

border

index of the boundary cycle forming the hole to be filled; the boundary cycles can be identified with $getBorders()

fair

Boolean, whether to fair (i.e. smooth) the filled hole

Returns

The filled hole as a cgalMesh object. The reference mesh is updated.

Examples

library(cgalMeshes)
library(rgl)
# make a sphere
sphere <- sphereMesh()
mesh <- cgalMesh$new(sphere)
# make a hole in this sphere
mesh$clipToPlane(
  planePoint  = c(0.5, 0, 0),
  planeNormal = c(1, 0, 0),
  clipVolume  = FALSE
)
mesh$computeNormals()
rmesh <- mesh$getMesh()
# fill the hole
hole <- mesh$fillBoundaryHole(1, fair = TRUE)
hole$computeNormals()
rhole <- hole$getMesh()
# plot
open3d(windowRect = 50 + c(0, 0, 512, 512))
view3d(30, 30)
shade3d(rmesh, color = "red")
shade3d(rhole, color = "blue")


Method fillFaceColors()

Replace missing face colors with a color.

Usage

cgalMesh$fillFaceColors(color)

Arguments

color

the color to replace the missing face colors

Returns

The reference mesh, invisibly.


Method filterMesh()

Split the mesh into two meshes according to a given set of selected faces. Face properties are preserved.

Usage

cgalMesh$filterMesh(faces)

Arguments

faces

a vector of face indices

Returns

Two cgalMesh objects. The first one is the mesh consisting of the faces of the reference mesh given in the faces argument. The second one is the complementary mesh.

Examples

library(rgl)
library(cgalMeshes)
rmesh <- HopfTorusMesh(nu = 80, nv = 60)
mesh <- cgalMesh$new(rmesh)
areas <- mesh$getFacesInfo()[, "area"]
bigFaces <- which(areas > 1)
meshes <- mesh$filterMesh(bigFaces)
rmesh1 <- meshes[[1]]$getMesh()
rmesh2 <- meshes[[2]]$getMesh()
open3d(windowRect = 50 + c(0, 0, 512, 512))
view3d(0, 0)
shade3d(rmesh1, color = "red")
shade3d(rmesh2, color = "blue")
wire3d(rmesh)


Method fixManifoldness()

Duplicate non-manifold vertices.

Usage

cgalMesh$fixManifoldness()

Returns

The possibly modified reference mesh, invisibly.


Method geoDists()

Estimated geodesic distances between vertices. The mesh must be triangle.

Usage

cgalMesh$geoDists(index)

Arguments

index

index of the source vertex

Returns

The estimated geodesic distances from the source vertex to each vertex.

Examples

# torus ####
library(cgalMeshes)
library(rgl)
rglmesh <- torusMesh(R = 3, r = 2, nu = 90, nv = 60)
mesh <- cgalMesh$new(rglmesh)
# estimated geodesic distances
geodists <- mesh$geoDists(1L)
# normalization to (0, 1)
geodists <- geodists / max(geodists)
# color each vertex according to its geodesic distance from the source
fcolor <- colorRamp(viridisLite::turbo(200L))
colors <- fcolor(geodists)
colors <- rgb(colors[, 1L], colors[, 2L], colors[, 3L], maxColorValue = 255)
rglmesh[["material"]] <- list("color" = colors)
# plot
open3d(windowRect = 50 + c(0, 0, 512, 512), zoom = 0.8)
shade3d(rglmesh)
wire3d(rglmesh, color = "black")
if(!rgl.useNULL()) {
  play3d(spin3d(axis = c(1, 1, 1), rpm = 5), duration = 20)  
}

# a trefoil knot (taken from `?rgl::cylinder3d`) #### library(cgalMeshes) library(rgl) theta <- seq(0, 2*pi, length.out = 50L) knot <- cylinder3d( center = cbind( sin(theta) + 2*sin(2*theta), 2*sin(3*theta), cos(theta) - 2*cos(2*theta)), e1 = cbind( cos(theta) + 4*cos(2*theta), 6*cos(3*theta), sin(theta) + 4*sin(2*theta)), radius = 0.8, closed = TRUE) knot <- subdivision3d(knot, depth = 2) mesh <- cgalMesh$new(knot)$triangulate() rglmesh <- mesh$getMesh() # estimated geodesic distances geodists <- mesh$geoDists(1L) # normalization to (0, 1) geodists <- geodists / max(geodists) # color each vertex according to its geodesic distance from the source fcolor <- colorRamp(viridisLite::inferno(200L)) colors <- fcolor(geodists) colors <- rgb(colors[, 1L], colors[, 2L], colors[, 3L], maxColorValue = 255) rglmesh[["material"]] <- list("color" = colors) # plot open3d(windowRect = 50 + c(0, 0, 512, 512), zoom = 0.8) shade3d(rglmesh) if(!rgl.useNULL()) { play3d(spin3d(axis = c(1, 1, 0), rpm = 5), duration = 20) }


Method getBorders()

Get the borders of the mesh.

Usage

cgalMesh$getBorders()

Returns

A list of matrices representing the boundary cycles. Each matrix has three columns: "edge", an edge index, and "v1" and "v2", the vertex indices of this edge.

Examples

library(cgalMeshes)
library(rgl)
# isosurface f=0
f <- function(x, y, z) {
  sin_x <- sin(x)
  sin_y <- sin(y)
  sin_z <- sin(z)
  cos_x <- cos(x)
  cos_y <- cos(y)
  cos_z <- cos(z)
  d <- sqrt(
    (-sin_x * sin_y + cos_x * cos_z) ** 2
    + (-sin_y * sin_z + cos_y * cos_x) ** 2
    + (-sin_z * sin_x + cos_z * cos_y) ** 2
  )
  (
    cos(
      x - (-sin_x * sin_y + cos_x * cos_z) / d
    )
    * sin(
      y - (-sin_y * sin_z + cos_y * cos_x) / d
    )
    + cos(
      y - (-sin_y * sin_z + cos_y * cos_x) / d
    )
    * sin(
      z - (-sin_z * sin_x + cos_z * cos_y)/ d
    )
    + cos(
      z - (-sin_z * sin_x + cos_z * cos_y) / d
    )
    * sin(
      x - (-sin_x * sin_y + cos_x * cos_z) / d
    )
  ) * (
    (
      cos(
        x + (-sin_x * sin_y + cos_x * cos_z) / d
      )
      * sin(
        y + (-sin_y * sin_z + cos_y * cos_x) / d
      )
      + cos(
        y + (-sin_y * sin_z + cos_y * cos_x) / d
      )
      * sin(
        z + (-sin_z * sin_x + cos_z * cos_y) / d
      )
      + cos(
        z + (-sin_z * sin_x + cos_z * cos_y) / d
      )
      * sin(
        x + (-sin_x * sin_y + cos_x * cos_z) / d
      )
    )
  )
}
# construct the isosurface f=0
ngrid <- 200L
x <- y <- z <- seq(-8.1, 8.1, 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
mesh <- cgalMesh$new(
  list(
    "vertices" = contour_shape[["vertices"]],
    "faces"    = contour_shape[["triangles"]]
  )
)
# clip the mesh to the ball of radius 8
spheremesh <- cgalMesh$new(sphereMesh(r = 8))
mesh$clip(spheremesh, clipVolume = FALSE)
# compute normals
mesh$computeNormals()
# we will plot the borders
borders <- mesh$getBorders()
# plot
rmesh <- mesh$getMesh()
open3d(windowRect = c(50, 50, 562, 562), zoom = 0.7)
shade3d(rmesh, color = "darkred")
vertices <- mesh$getVertices()
for(border in borders){
  plotEdges(
    vertices, border[, c("v1", "v2")], color = "gold",
    lwd = 3, edgesAsTubes = FALSE, verticesAsSpheres = FALSE
  )
}


Method getEdges()

Get the edges of the mesh.

Usage

cgalMesh$getEdges()

Returns

A dataframe with seven columns; the first two ones give the vertex indices of each edge (one edge per row), the third one gives the lengths of each edge, the fourth one indicates whether the edges is a border edge, the fifth one gives the dihedral angles in degrees between the two faces adjacent to each edge, and the last two ones gives the indices of the faces the edge belongs to (the second index is NA if the edge is a border edge).

Examples

library(rgl)
mesh <- cgalMesh$new(dodecahedron3d())
head(mesh$getEdges())


Method getFaces()

Get the faces of the mesh.

Usage

cgalMesh$getFaces()

Returns

The faces in a matrix if the mesh is triangle or quad, otherwise in a list.


Method getFacesInfo()

Get the centroids, the circumcenters, and the areas of the faces, for a triangle mesh only.

Usage

cgalMesh$getFacesInfo()

Returns

A matrix with seven columns: the first three ones provide the Cartesian coordinates of the centroids, the three next ones provide the Cartesian coordinates of the circumcenters, and the last one provides the areas.


Method getFaceColors()

Get the face colors (if there are).

Usage

cgalMesh$getFaceColors()

Returns

The vector of colors (or any character vector) attached to the faces of the mesh, or NULL if nothing is assigned to the faces.


Method getFaceScalars()

Get the face scalars (if there are).

Usage

cgalMesh$getFaceScalars()

Returns

The vector of scalars attached to the faces of the mesh, or NULL if nothing is assigned to the faces.


Method getVertexColors()

Get the vertex colors (if there are).

Usage

cgalMesh$getVertexColors()

Returns

The vector of colors (or any character vector) attached to the vertices of the mesh, or NULL if nothing is assigned to the vertices.


Method getVertexScalars()

Get the vertex scalars (if there are).

Usage

cgalMesh$getVertexScalars()

Returns

The vector of scalars attached to the vertices of the mesh, or NULL if nothing is assigned to the vertices.


Method getNormals()

Get the per-vertex normals (if there are).

Usage

cgalMesh$getNormals()

Returns

The matrix of per-vertex normals if they have been given or computed (see computeNormals, or NULL otherwise.


Method getMesh()

Get the mesh.

Usage

cgalMesh$getMesh(rgl = TRUE, ...)

Arguments

rgl

Boolean, whether to return a rgl mesh if possible, i.e. if the mesh only has triangular or quadrilateral faces

...

arguments passed to mesh3d (if a rgl mesh is returned)

Returns

A rgl mesh or a list with two or three fields: vertices, faces, and normals if XXXXXXXXXXXXXXXXXXXXXXXXX the argument normals is set to TRUE

Examples

library(rgl)
mesh <- cgalMesh$new(cube3d())$triangulate()
mesh$getMesh()


Method getVertices()

Get the vertices of the mesh.

Usage

cgalMesh$getVertices()

Returns

The vertices in a matrix.


Method HausdorffApproximate()

Approximate Hausdorff distance between the reference mesh and another mesh.

Usage

cgalMesh$HausdorffApproximate(mesh2, symmetric = TRUE)

Arguments

mesh2

a cgalMesh object

symmetric

Boolean, whether to consider the symmetric Hausdorff distance.

Returns

A number. The algorithm uses some simulations and thus the result can vary.


Method HausdorffEstimate()

Estimate of Hausdorff distance between the reference mesh and another mesh.

Usage

cgalMesh$HausdorffEstimate(mesh2, errorBound = 1e-04, symmetric = TRUE)

Arguments

mesh2

a cgalMesh object

errorBound

a positive number, a bound on the error of the estimate

symmetric

Boolean, whether to consider the symmetric Hausdorff distance.

Returns

A number.


Method intersection()

Intersection with another mesh.

Usage

cgalMesh$intersection(mesh2)

Arguments

mesh2

a cgalMesh object

Returns

A cgalMesh object.

Examples

library(cgalMeshes)
library(rgl)
# take two cubes
rglmesh1 <- cube3d()
rglmesh2 <- translate3d(cube3d(), 1, 1, 1)
mesh1 <- cgalMesh$new(rglmesh1)
mesh2 <- cgalMesh$new(rglmesh2)
# the two meshes must be triangle
mesh1$triangulate()
mesh2$triangulate()
# intersection
imesh <- mesh1$intersection(mesh2)
rglimesh <- imesh$getMesh()
# extract edges for plotting
extEdges <- exteriorEdges(imesh$getEdges())
# plot
open3d(windowRect = 50 + c(0, 0, 512, 512), zoom = 0.9)
shade3d(rglimesh, color = "red")
plotEdges(imesh$getVertices(), extEdges)
shade3d(rglmesh1, color = "yellow", alpha = 0.2)
shade3d(rglmesh2, color = "cyan", alpha = 0.2)


Method isClosed()

Check whether the mesh is closed.

Usage

cgalMesh$isClosed()

Returns

A Boolean value, whether the mesh is closed.


Method isotropicRemeshing()

Isotropic remeshing.

Usage

cgalMesh$isotropicRemeshing(targetEdgeLength, iterations = 1, relaxSteps = 1)

Arguments

targetEdgeLength

positive number, the target edge length of the remeshed mesh

iterations

number of iterations, a positive integer

relaxSteps

number of relaxation steps, a positive integer

Returns

The modified cgalMesh object, invisibly.

Examples

library(cgalMeshes)
library(rgl)
mesh <- cgalMesh$new(HopfTorusMesh(nu = 80, nv = 50))
mesh$isotropicRemeshing(targetEdgeLength = 0.7)
# squared norms of the vertices
normsq <- apply(mesh$getVertices(), 1L, crossprod)
# fair the region where the squared norm is > 19
mesh$fair(which(normsq > 19))
# plot
mesh$computeNormals()
rmesh <- mesh$getMesh()
open3d(windowRect = 50 + c(0, 0, 512, 512))
view3d(0, 0)
shade3d(rmesh, color = "maroon")
wire3d(rmesh)


Method isOutwardOriented()

Check whether the mesh is outward oriented. The mesh must be triangle.

Usage

cgalMesh$isOutwardOriented()

Returns

A Boolean value, whether the mesh is outward oriented.

Examples

library(rgl)
mesh <- cgalMesh$new(tetrahedron3d())
mesh$isOutwardOriented() # TRUE
mesh$reverseOrientation()
mesh$isOutwardOriented() # FALSE


Method isTriangle()

Check whether the mesh is triangle.

Usage

cgalMesh$isTriangle()

Returns

A Boolean value, whether the mesh is triangle.

Examples

library(rgl)
mesh <- cgalMesh$new(cube3d())
mesh$isTriangle()


Method isValid()

Check whether the mesh is valid.

Usage

cgalMesh$isValid()

Returns

A Boolean value, whether the mesh is valid.


Method isValidFaceGraph()

Check whether the mesh is valid.

Usage

cgalMesh$isValidFaceGraph()

Returns

A Boolean value, whether the mesh is valid.


Method isValidHalfedgeGraph()

Check whether the mesh is valid.

Usage

cgalMesh$isValidHalfedgeGraph()

Returns

A Boolean value, whether the mesh is valid.


Method isValidPolygonMesh()

Check whether the mesh is valid.

Usage

cgalMesh$isValidPolygonMesh()

Returns

A Boolean value, whether the mesh is valid.


Method LoopSubdivision()

Performs the Loop subdivision and deformation.

Usage

cgalMesh$LoopSubdivision(iterations = 1)

Arguments

iterations

number of iterations

Returns

The modified reference mesh, invisibly.

Examples

library(cgalMeshes)
library(rgl)
hopfMesh <- HopfTorusMesh(nu = 80, nv = 40)
mesh <- cgalMesh$new(hopfMesh)
mesh$LoopSubdivision(iterations = 2)
mesh$computeNormals()
rmesh <- mesh$getMesh()
# plot
open3d(windowRect = 50 + c(0, 0, 800, 400))
mfrow3d(1, 2)
view3d(0, 0, zoom = 0.9)
shade3d(hopfMesh, color = "gold")
wire3d(hopfMesh, color = "black")
next3d()
view3d(0, 0, zoom = 0.9)
shade3d(rmesh, color = "gold")
wire3d(rmesh, color = "black")


Method merge()

Merge the mesh and another mesh.

Usage

cgalMesh$merge(mesh2)

Arguments

mesh2

a cgalMesh object

Returns

The updated reference mesh, invisibly.

Examples

library(cgalMeshes)
library(rgl)
mesh1 <- cgalMesh$new(sphereMesh())
mesh1$assignFaceColors("red")
mesh2 <- cgalMesh$new(sphereMesh(x = 3))
mesh2$assignFaceColors("blue")
mesh1$merge(mesh2)
rmesh <- mesh1$getMesh()
open3d(windowRect = c(50, 50, 562, 562))
shade3d(rmesh, meshColor = "faces")


Method orientToBoundVolume()

Reorient the connected components of the mesh in order that it bounds a volume. The mesh must be triangle.

Usage

cgalMesh$orientToBoundVolume()

Returns

The modified cgalMesh object, invisibly. WARNING: even if you store the result in a new variable, the original mesh is modified.

Examples

# two disjoint tetrahedra ####
vertices <- rbind(
  c(0, 0, 0),
  c(2, 2, 0),
  c(2, 0, 2),
  c(0, 2, 2),
  c(3, 3, 3),
  c(5, 5, 3),
  c(5, 3, 5),
  c(3, 5, 5)
)
faces <- rbind(
  c(3, 2, 1),
  c(3, 4, 2),
  c(1, 2, 4),
  c(4, 3, 1),
  c(5, 6, 7),
  c(6, 8, 7),
  c(8, 6, 5),
  c(5, 7, 8)
)
mesh <- cgalMesh$new(vertices = vertices, faces = faces)
mesh$boundsVolume() # FALSE
mesh$orientToBoundVolume()
mesh$boundsVolume() # TRUE


Method removeSelfIntersections()

Remove self-intersections (experimental). The mesh must be triangle.

Usage

cgalMesh$removeSelfIntersections()

Returns

The modified cgalMesh object, invisibly.


Method reverseOrientation()

Reverse the orientation of the faces of the mesh.

Usage

cgalMesh$reverseOrientation()

Returns

The modified cgalMesh object, invisibly. WARNING: even if you store the result in a new variable, the original mesh is modified.

Examples

library(rgl)
mesh <- cgalMesh$new(tetrahedron3d())
mesh$isOutwardOriented() # TRUE
mesh$reverseOrientation()
mesh$isOutwardOriented() # FALSE


Method sample()

Random sampling on the mesh. The mesh must be triangle.

Usage

cgalMesh$sample(nsims)

Arguments

nsims

integer, the desired number of simulations

Returns

A nsims x 3 matrix containing the simulations.

Examples

Run this code

## ------------------------------------------------
## Method `cgalMesh$new`
## ------------------------------------------------

library(cgalMeshes)
meshFile <- system.file(
  "extdata", "bigPolyhedron.off", package = "cgalMeshes"
)
mesh <- cgalMesh$new(meshFile)
rglmesh <- mesh$getMesh()
library(rgl)
open3d(windowRect = 50 + c(0, 0, 512, 512), zoom = 0.9)
shade3d(rglmesh, color = "tomato")
plotEdges(
  mesh$getVertices(), mesh$getEdges(), color = "darkred"
)

# this one has colors: ####
meshFile <- system.file(
  "extdata", "pentagrammicDipyramid.ply", package = "cgalMeshes"
)
mesh <- cgalMesh$new(meshFile)
rmesh <- mesh$getMesh()
library(rgl)
open3d(windowRect = 50 + c(0, 0, 512, 512), zoom = 0.85)
shade3d(rmesh, meshColor = "faces")

## ------------------------------------------------
## Method `cgalMesh$area`
## ------------------------------------------------

library(rgl)
mesh <- cgalMesh$new(cube3d())$triangulate()
mesh$area()

## ------------------------------------------------
## Method `cgalMesh$boundingBox`
## ------------------------------------------------

library(cgalMeshes)
library(rgl)
rmesh <- cyclideMesh(a = 97, c = 32, mu = 57)
mesh <- cgalMesh$new(rmesh)
bbox <- mesh$boundingBox()
bxmesh <- isoCuboidMesh(bbox[["lcorner"]], bbox[["ucorner"]])
open3d(windowRect = 50 + c(0, 0, 512, 512))
view3d(0, -60)
shade3d(rmesh, color = "gold")
wire3d(bxmesh, color = "black")

## ------------------------------------------------
## Method `cgalMesh$boundsVolume`
## ------------------------------------------------

library(rgl)
mesh <- cgalMesh$new(tetrahedron3d())
mesh$boundsVolume() # TRUE
mesh$reverseOrientation()
mesh$boundsVolume() # TRUE

## ------------------------------------------------
## Method `cgalMesh$CatmullClark`
## ------------------------------------------------

library(cgalMeshes)
library(rgl)
hopfMesh <- HopfTorusMesh(nu = 80, nv = 40)
mesh <- cgalMesh$new(hopfMesh)
mesh$CatmullClark(iterations = 2)
mesh$computeNormals()
rmesh <- mesh$getMesh()
# plot
open3d(windowRect = 50 + c(0, 0, 800, 400))
mfrow3d(1, 2)
view3d(0, 0, zoom = 0.9)
shade3d(hopfMesh, color = "red")
wire3d(hopfMesh, color = "black")
next3d()
view3d(0, 0, zoom = 0.9)
shade3d(rmesh, color = "red")
wire3d(rmesh, color = "black")

## ------------------------------------------------
## Method `cgalMesh$centroid`
## ------------------------------------------------

library(cgalMeshes)
library(rgl)
mesh <- cgalMesh$new(icosahedron3d())
mesh$centroid()

## ------------------------------------------------
## Method `cgalMesh$clip`
## ------------------------------------------------

# cube clipped to sphere ####
library(cgalMeshes)
library(rgl)
mesh    <- cgalMesh$new(cube3d())$triangulate()
clipper <- cgalMesh$new(sphereMesh(r= sqrt(2)))
mesh$assignFaceColors("blue")
clipper$assignFaceColors("red")
meshes <- mesh$clip(clipper, clipVolume = TRUE)
mesh1 <- meshes[[1]]
mesh2 <- meshes[[2]]
mesh2$computeNormals()
rglmesh1 <- mesh1$getMesh()
rglmesh2 <- mesh2$getMesh()
open3d(windowRect = 50 + c(0, 0, 512, 512))
view3d(45, 45, zoom = 0.9)
shade3d(rglmesh1, meshColor = "faces")
shade3d(rglmesh2, meshColor = "faces")

# Togliatti surface clipped to a ball ####
library(rmarchingcubes)
library(rgl)
library(cgalMeshes)
# Togliatti surface equation: f(x,y,z) = 0
f <- function(x, y, z) {
  64*(x-1) *
    (x^4 - 4*x^3 - 10*x^2*y^2 - 4*x^2 + 16*x - 20*x*y^2 + 5*y^4 + 16 - 20*y^2) - 
    5*sqrt(5-sqrt(5))*(2*z - sqrt(5-sqrt(5))) * 
    (4*(x^2 + y^2 - z^2) + (1 + 3*sqrt(5)))^2
}
# grid
n <- 200L
x <- y <- seq(-5, 5, length.out = n)
z <- seq(-4, 4, length.out = n)
Grid <- expand.grid(X = x, Y = y, Z = z)
# calculate voxel
voxel <- array(with(Grid, f(X, Y, Z)), dim = c(n, n, n))
# calculate isosurface
contour_shape <- contour3d(
  griddata = voxel, level = 0, x = x, y = y, z = z
)
# make rgl mesh (plotted later)
rglMesh <- tmesh3d(
  vertices = t(contour_shape[["vertices"]]),
  indices  = t(contour_shape[["triangles"]]),
  normals  = contour_shape[["normals"]],
  homogeneous = FALSE
)
# make CGAL mesh
mesh <- cgalMesh$new(rglMesh)
# clip to sphere of radius 4.8
sphere <- sphereMesh(r = 4.8)
clipper <- cgalMesh$new(sphere)
mesh$clip(clipper, clipVolume = FALSE)
rglClippedMesh <- mesh$getMesh()
# plot
open3d(windowRect = 50 + c(0, 0, 900, 450))
mfrow3d(1L, 2L)
view3d(0, -70, zoom = 0.8)
shade3d(rglMesh, color = "firebrick")
next3d()
view3d(0, -70, zoom = 0.8)
shade3d(rglClippedMesh, color = "firebrick")
shade3d(sphere, color = "yellow", alpha = 0.15)

## ------------------------------------------------
## Method `cgalMesh$clipToPlane`
## ------------------------------------------------

library(cgalMeshes)
library(rgl)
rmesh <- sphereMesh()
mesh <- cgalMesh$new(rmesh)
nfaces <- nrow(mesh$getFaces())
if(require("randomcoloR")) {
  colors <- 
    randomColor(nfaces, hue = "random", luminosity = "dark")
} else {
  colors <- rainbow(nfaces)
}
mesh$assignFaceColors(colors)
meshes <- mesh$clipToPlane(
  planePoint  = c(0, 0, 0), 
  planeNormal = c(0, 0, 1), 
  clipVolume = TRUE
)
mesh1 <- meshes[[1]]
mesh2 <- meshes[[2]]
mesh1$computeNormals()
rClippedMesh1 <- mesh1$getMesh()
rClippedMesh2 <- mesh2$getMesh()
open3d(windowRect = 50 + c(0, 0, 512, 512))
view3d(70, 0)
shade3d(rClippedMesh1, meshColor = "faces")
shade3d(rClippedMesh2, color = "orange")

## ------------------------------------------------
## Method `cgalMesh$clipToIsoCuboid`
## ------------------------------------------------

library(cgalMeshes)
library(rgl)
rmesh <- HopfTorusMesh(nu = 200, nv = 200)
mesh <- cgalMesh$new(rmesh)
mesh$assignFaceColors("orangered")
lcorner <- c(-7, -7, -5)
ucorner <- c(7, 6, 5)
bxmesh <- isoCuboidMesh(lcorner, ucorner)
mesh$clipToIsoCuboid(
  lcorner, ucorner, clipVolume = FALSE
)
mesh$computeNormals()
rClippedMesh <- mesh$getMesh()
open3d(windowRect = 50 + c(0, 0, 512, 512))
view3d(-40, 0)
shade3d(rClippedMesh, meshColor = "faces")
shade3d(bxmesh, color = "cyan", alpha = 0.3)

## ------------------------------------------------
## Method `cgalMesh$connectedComponents`
## ------------------------------------------------

library(cgalMeshes)
library(rmarchingcubes)
# isosurface function (slice of a seven-dimensional toratope)
f <- function(x, y, z, a) {
  (sqrt(
    (sqrt((sqrt((x*sin(a))^2 + (z*cos(a))^2) - 5)^2 + (y*sin(a))^2) - 2.5)^2 + 
      (x*cos(a))^2) - 1.25
  )^2 + (sqrt((sqrt((z*sin(a))^2 + (y*cos(a))^2) - 2.5)^2) - 1.25)^2
}
# make grid
n <- 200L
x <- seq(-10, 10, len = n)
y <- seq(-10, 10, len = n)
z <- seq(-10, 10, len = n)
Grid <- expand.grid(X = x, Y = y, Z = z)
# compute isosurface
voxel <- array(with(Grid, f(X, Y, Z, a = pi/2)), dim = c(n, n, n))
isosurface <- contour3d(voxel, level = 0.25, x = x, y = y, z = z)
# make CGAL mesh
mesh <- cgalMesh$new(
  vertices = isosurface[["vertices"]], 
  faces = isosurface[["triangles"]],
  normals = isosurface[["normals"]]
)
# connected components
components <- mesh$connectedComponents()
ncc <- length(components)
# plot
library(rgl)
colors <- rainbow(ncc)
open3d(windowRect = 50 + c(0, 0, 512, 512))
view3d(30, 50)
for(i in 1L:ncc) {
  rglMesh <- components[[i]]$getMesh()
  shade3d(rglMesh, color = colors[i])
}

## ------------------------------------------------
## Method `cgalMesh$convexParts`
## ------------------------------------------------

library(cgalMeshes)
library(rgl)
mesh <- cgalMesh$new(pentagrammicPrism)$triangulate()
cxparts <- mesh$convexParts()
ncxparts <- length(cxparts)
colors <- hcl.colors(ncxparts, palette = "plasma")
open3d(windowRect = 50 + c(0, 0, 512, 512))
view3d(20, -20, zoom = 0.8)
for(i in 1L:ncxparts) {
  cxmesh <- cxparts[[i]]$getMesh()
  shade3d(cxmesh, color = colors[i])
}

## ------------------------------------------------
## Method `cgalMesh$copy`
## ------------------------------------------------

library(rgl)
mesh <- cgalMesh$new(cube3d())
tmesh <- mesh$copy()$triangulate()
tmesh$isTriangle() # TRUE
mesh$isTriangle() # FALSE

## ------------------------------------------------
## Method `cgalMesh$distance`
## ------------------------------------------------

# cube example ####
library(cgalMeshes)
mesh <- cgalMesh$new(rgl::cube3d())$triangulate()
points <- rbind(
  c(0, 0, 0),
  c(1, 1, 1)
)
mesh$distance(points) # should be 1 and 0

# cyclide example ####
library(cgalMeshes)
a <- 100; c <- 30; mu <- 80
mesh <- cgalMesh$new(cyclideMesh(a, c, mu, nu = 100L, nv = 100L))
O2 <- c(c, 0, 0)
# should be a - mu = 20 (see ?cyclideMesh):
mesh$distance(O2)    

## ------------------------------------------------
## Method `cgalMesh$DooSabin`
## ------------------------------------------------

library(cgalMeshes)
library(rgl)
hopfMesh <- HopfTorusMesh(nu = 80, nv = 40)
mesh <- cgalMesh$new(hopfMesh)
mesh$DooSabin(iterations = 2)
mesh$triangulate()
mesh$computeNormals()
rmesh <- mesh$getMesh()
# plot
open3d(windowRect = 50 + c(0, 0, 800, 400))
mfrow3d(1, 2)
view3d(0, 0, zoom = 0.9)
shade3d(hopfMesh, color = "brown")
wire3d(hopfMesh, color = "black")
next3d()
view3d(0, 0, zoom = 0.9)
shade3d(rmesh, color = "brown")
wire3d(rmesh, color = "black")

## ------------------------------------------------
## Method `cgalMesh$fair`
## ------------------------------------------------

library(cgalMeshes)
rglHopf <- HopfTorusMesh(nu = 100, nv = 100)
hopf <- cgalMesh$new(rglHopf)
# squared norms of the vertices
normsq <- apply(hopf$getVertices(), 1L, crossprod)
# fair the region where the squared norm is > 19
indices <- which(normsq > 19)
hopf$fair(indices)
rglHopf_faired <- hopf$getMesh()
# plot
library(rgl)
open3d(windowRect = 50 + c(0, 0, 900, 450))
mfrow3d(1L, 2L)
view3d(0, 0, zoom = 0.8)
shade3d(rglHopf, color = "orangered")
next3d()
view3d(0, 0, zoom = 0.8)
shade3d(rglHopf_faired, color = "orangered")

## ------------------------------------------------
## Method `cgalMesh$fillBoundaryHole`
## ------------------------------------------------

library(cgalMeshes)
library(rgl)
# make a sphere
sphere <- sphereMesh()
mesh <- cgalMesh$new(sphere)
# make a hole in this sphere
mesh$clipToPlane(
  planePoint  = c(0.5, 0, 0),
  planeNormal = c(1, 0, 0),
  clipVolume  = FALSE
)
mesh$computeNormals()
rmesh <- mesh$getMesh()
# fill the hole
hole <- mesh$fillBoundaryHole(1, fair = TRUE)
hole$computeNormals()
rhole <- hole$getMesh()
# plot
open3d(windowRect = 50 + c(0, 0, 512, 512))
view3d(30, 30)
shade3d(rmesh, color = "red")
shade3d(rhole, color = "blue")

## ------------------------------------------------
## Method `cgalMesh$filterMesh`
## ------------------------------------------------

library(rgl)
library(cgalMeshes)
rmesh <- HopfTorusMesh(nu = 80, nv = 60)
mesh <- cgalMesh$new(rmesh)
areas <- mesh$getFacesInfo()[, "area"]
bigFaces <- which(areas > 1)
meshes <- mesh$filterMesh(bigFaces)
rmesh1 <- meshes[[1]]$getMesh()
rmesh2 <- meshes[[2]]$getMesh()
open3d(windowRect = 50 + c(0, 0, 512, 512))
view3d(0, 0)
shade3d(rmesh1, color = "red")
shade3d(rmesh2, color = "blue")
wire3d(rmesh)

## ------------------------------------------------
## Method `cgalMesh$geoDists`
## ------------------------------------------------

# torus ####
library(cgalMeshes)
library(rgl)
rglmesh <- torusMesh(R = 3, r = 2, nu = 90, nv = 60)
mesh <- cgalMesh$new(rglmesh)
# estimated geodesic distances
geodists <- mesh$geoDists(1L)
# normalization to (0, 1)
geodists <- geodists / max(geodists)
# color each vertex according to its geodesic distance from the source
fcolor <- colorRamp(viridisLite::turbo(200L))
colors <- fcolor(geodists)
colors <- rgb(colors[, 1L], colors[, 2L], colors[, 3L], maxColorValue = 255)
rglmesh[["material"]] <- list("color" = colors)
# plot
open3d(windowRect = 50 + c(0, 0, 512, 512), zoom = 0.8)
shade3d(rglmesh)
wire3d(rglmesh, color = "black")
if(!rgl.useNULL()) {
  play3d(spin3d(axis = c(1, 1, 1), rpm = 5), duration = 20)  
}

# a trefoil knot (taken from `?rgl::cylinder3d`) ####
library(cgalMeshes)
library(rgl)
theta <- seq(0, 2*pi, length.out = 50L)
knot <- cylinder3d(
  center = cbind(
    sin(theta) + 2*sin(2*theta), 
    2*sin(3*theta), 
    cos(theta) - 2*cos(2*theta)),
  e1 = cbind(
    cos(theta) + 4*cos(2*theta), 
    6*cos(3*theta), 
    sin(theta) + 4*sin(2*theta)),
  radius = 0.8, 
  closed = TRUE)
knot <- subdivision3d(knot, depth = 2)
mesh <- cgalMesh$new(knot)$triangulate()
rglmesh <- mesh$getMesh()
# estimated geodesic distances
geodists <- mesh$geoDists(1L)
# normalization to (0, 1)
geodists <- geodists / max(geodists)
# color each vertex according to its geodesic distance from the source
fcolor <- colorRamp(viridisLite::inferno(200L))
colors <- fcolor(geodists)
colors <- rgb(colors[, 1L], colors[, 2L], colors[, 3L], maxColorValue = 255)
rglmesh[["material"]] <- list("color" = colors)
# plot
open3d(windowRect = 50 + c(0, 0, 512, 512), zoom = 0.8)
shade3d(rglmesh)
if(!rgl.useNULL()) {
  play3d(spin3d(axis = c(1, 1, 0), rpm = 5), duration = 20)  
}

## ------------------------------------------------
## Method `cgalMesh$getBorders`
## ------------------------------------------------

library(cgalMeshes)
library(rgl)
# isosurface f=0
f <- function(x, y, z) {
  sin_x <- sin(x)
  sin_y <- sin(y)
  sin_z <- sin(z)
  cos_x <- cos(x)
  cos_y <- cos(y)
  cos_z <- cos(z)
  d <- sqrt(
    (-sin_x * sin_y + cos_x * cos_z) ** 2
    + (-sin_y * sin_z + cos_y * cos_x) ** 2
    + (-sin_z * sin_x + cos_z * cos_y) ** 2
  )
  (
    cos(
      x - (-sin_x * sin_y + cos_x * cos_z) / d
    )
    * sin(
      y - (-sin_y * sin_z + cos_y * cos_x) / d
    )
    + cos(
      y - (-sin_y * sin_z + cos_y * cos_x) / d
    )
    * sin(
      z - (-sin_z * sin_x + cos_z * cos_y)/ d
    )
    + cos(
      z - (-sin_z * sin_x + cos_z * cos_y) / d
    )
    * sin(
      x - (-sin_x * sin_y + cos_x * cos_z) / d
    )
  ) * (
    (
      cos(
        x + (-sin_x * sin_y + cos_x * cos_z) / d
      )
      * sin(
        y + (-sin_y * sin_z + cos_y * cos_x) / d
      )
      + cos(
        y + (-sin_y * sin_z + cos_y * cos_x) / d
      )
      * sin(
        z + (-sin_z * sin_x + cos_z * cos_y) / d
      )
      + cos(
        z + (-sin_z * sin_x + cos_z * cos_y) / d
      )
      * sin(
        x + (-sin_x * sin_y + cos_x * cos_z) / d
      )
    )
  )
}
# construct the isosurface f=0
ngrid <- 200L
x <- y <- z <- seq(-8.1, 8.1, 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
mesh <- cgalMesh$new(
  list(
    "vertices" = contour_shape[["vertices"]],
    "faces"    = contour_shape[["triangles"]]
  )
)
# clip the mesh to the ball of radius 8
spheremesh <- cgalMesh$new(sphereMesh(r = 8))
mesh$clip(spheremesh, clipVolume = FALSE)
# compute normals
mesh$computeNormals()
# we will plot the borders
borders <- mesh$getBorders()
# plot
rmesh <- mesh$getMesh()
open3d(windowRect = c(50, 50, 562, 562), zoom = 0.7)
shade3d(rmesh, color = "darkred")
vertices <- mesh$getVertices()
for(border in borders){
  plotEdges(
    vertices, border[, c("v1", "v2")], color = "gold",
    lwd = 3, edgesAsTubes = FALSE, verticesAsSpheres = FALSE
  )
}

## ------------------------------------------------
## Method `cgalMesh$getEdges`
## ------------------------------------------------

library(rgl)
mesh <- cgalMesh$new(dodecahedron3d())
head(mesh$getEdges())

## ------------------------------------------------
## Method `cgalMesh$getMesh`
## ------------------------------------------------

library(rgl)
mesh <- cgalMesh$new(cube3d())$triangulate()
mesh$getMesh()

## ------------------------------------------------
## Method `cgalMesh$intersection`
## ------------------------------------------------

library(cgalMeshes)
library(rgl)
# take two cubes
rglmesh1 <- cube3d()
rglmesh2 <- translate3d(cube3d(), 1, 1, 1)
mesh1 <- cgalMesh$new(rglmesh1)
mesh2 <- cgalMesh$new(rglmesh2)
# the two meshes must be triangle
mesh1$triangulate()
mesh2$triangulate()
# intersection
imesh <- mesh1$intersection(mesh2)
rglimesh <- imesh$getMesh()
# extract edges for plotting
extEdges <- exteriorEdges(imesh$getEdges())
# plot
open3d(windowRect = 50 + c(0, 0, 512, 512), zoom = 0.9)
shade3d(rglimesh, color = "red")
plotEdges(imesh$getVertices(), extEdges)
shade3d(rglmesh1, color = "yellow", alpha = 0.2)
shade3d(rglmesh2, color = "cyan", alpha = 0.2)

## ------------------------------------------------
## Method `cgalMesh$isotropicRemeshing`
## ------------------------------------------------

library(cgalMeshes)
library(rgl)
mesh <- cgalMesh$new(HopfTorusMesh(nu = 80, nv = 50))
mesh$isotropicRemeshing(targetEdgeLength = 0.7)
# squared norms of the vertices
normsq <- apply(mesh$getVertices(), 1L, crossprod)
# fair the region where the squared norm is > 19
mesh$fair(which(normsq > 19))
# plot
mesh$computeNormals()
rmesh <- mesh$getMesh()
open3d(windowRect = 50 + c(0, 0, 512, 512))
view3d(0, 0)
shade3d(rmesh, color = "maroon")
wire3d(rmesh)

## ------------------------------------------------
## Method `cgalMesh$isOutwardOriented`
## ------------------------------------------------

library(rgl)
mesh <- cgalMesh$new(tetrahedron3d())
mesh$isOutwardOriented() # TRUE
mesh$reverseOrientation()
mesh$isOutwardOriented() # FALSE

## ------------------------------------------------
## Method `cgalMesh$isTriangle`
## ------------------------------------------------

library(rgl)
mesh <- cgalMesh$new(cube3d())
mesh$isTriangle()

## ------------------------------------------------
## Method `cgalMesh$LoopSubdivision`
## ------------------------------------------------

library(cgalMeshes)
library(rgl)
hopfMesh <- HopfTorusMesh(nu = 80, nv = 40)
mesh <- cgalMesh$new(hopfMesh)
mesh$LoopSubdivision(iterations = 2)
mesh$computeNormals()
rmesh <- mesh$getMesh()
# plot
open3d(windowRect = 50 + c(0, 0, 800, 400))
mfrow3d(1, 2)
view3d(0, 0, zoom = 0.9)
shade3d(hopfMesh, color = "gold")
wire3d(hopfMesh, color = "black")
next3d()
view3d(0, 0, zoom = 0.9)
shade3d(rmesh, color = "gold")
wire3d(rmesh, color = "black")

## ------------------------------------------------
## Method `cgalMesh$merge`
## ------------------------------------------------

library(cgalMeshes)
library(rgl)
mesh1 <- cgalMesh$new(sphereMesh())
mesh1$assignFaceColors("red")
mesh2 <- cgalMesh$new(sphereMesh(x = 3))
mesh2$assignFaceColors("blue")
mesh1$merge(mesh2)
rmesh <- mesh1$getMesh()
open3d(windowRect = c(50, 50, 562, 562))
shade3d(rmesh, meshColor = "faces")

## ------------------------------------------------
## Method `cgalMesh$orientToBoundVolume`
## ------------------------------------------------

# two disjoint tetrahedra ####
vertices <- rbind(
  c(0, 0, 0),
  c(2, 2, 0),
  c(2, 0, 2),
  c(0, 2, 2),
  c(3, 3, 3),
  c(5, 5, 3),
  c(5, 3, 5),
  c(3, 5, 5)
)
faces <- rbind(
  c(3, 2, 1),
  c(3, 4, 2),
  c(1, 2, 4),
  c(4, 3, 1),
  c(5, 6, 7),
  c(6, 8, 7),
  c(8, 6, 5),
  c(5, 7, 8)
)
mesh <- cgalMesh$new(vertices = vertices, faces = faces)
mesh$boundsVolume() # FALSE
mesh$orientToBoundVolume()
mesh$boundsVolume() # TRUE

## ------------------------------------------------
## Method `cgalMesh$reverseOrientation`
## ------------------------------------------------

library(rgl)
mesh <- cgalMesh$new(tetrahedron3d())
mesh$isOutwardOriented() # TRUE
mesh$reverseOrientation()
mesh$isOutwardOriented() # FALSE

## ------------------------------------------------
## Method `cgalMesh$selfIntersects`
## ------------------------------------------------

library(rgl)
mesh <- cgalMesh$new(dodecahedron3d())
mesh$selfIntersects()

## ------------------------------------------------
## Method `cgalMesh$sharpEdges`
## ------------------------------------------------

library(cgalMeshes)
library(rgl)
# astroidal ellipsoid
f <- function(u, v) {
  rbind(
    cos(u)^3 * cos(v)^3,
    sin(u)^3 * cos(v)^3,
    sin(v)^3
  )
}
rmesh <- parametricMesh(
  f, urange = c(0, 2*pi), vrange = c(0, 2*pi), 
  periodic = c(TRUE, TRUE), nu = 120, nv = 110
)
mesh <- cgalMesh$new(rmesh)
sharpEdges <- mesh$sharpEdges(30)
# plot
open3d(windowRect = 50 + c(0, 0, 512, 512), zoom = 0.8)
shade3d(addNormals(rmesh), color = "chartreuse")
plotEdges(
  mesh$getVertices(), sharpEdges[, c("v1", "v2")], 
  edgesAsTubes = FALSE, lwd = 5, verticesAsSpheres = FALSE
)

## ------------------------------------------------
## Method `cgalMesh$Sqrt3Subdivision`
## ------------------------------------------------

library(cgalMeshes)
library(rgl)
hopfMesh <- HopfTorusMesh(nu = 80, nv = 40)
mesh <- cgalMesh$new(hopfMesh)
mesh$Sqrt3Subdivision(iterations = 2)
mesh$computeNormals()
rmesh <- mesh$getMesh()
# plot
open3d(windowRect = 50 + c(0, 0, 800, 400))
mfrow3d(1, 2)
view3d(0, 0, zoom = 0.9)
shade3d(hopfMesh, color = "cyan")
wire3d(hopfMesh, color = "black")
next3d()
view3d(0, 0, zoom = 0.9)
shade3d(rmesh, color = "cyan")
wire3d(rmesh, color = "black")

## ------------------------------------------------
## Method `cgalMesh$subtract`
## ------------------------------------------------

library(cgalMeshes)
library(rgl)
# take two cubes
rglmesh1 <- cube3d()
rglmesh2 <- translate3d(cube3d(), 1, 1, 1)
mesh1 <- cgalMesh$new(rglmesh1)
mesh2 <- cgalMesh$new(rglmesh2)
# the two meshes must be triangle
mesh1$triangulate()
mesh2$triangulate()
# assign colors
mesh1$assignFaceColors("red")
mesh2$assignFaceColors("navy")
# difference
mesh <- mesh1$subtract(mesh2)
rglmesh <- mesh$getMesh()
# extract edges for plotting
extEdges <- exteriorEdges(mesh$getEdges())
# plot
open3d(windowRect = 50 + c(0, 0, 512, 512), zoom = 0.9)
shade3d(rglmesh, meshColor = "faces")
plotEdges(mesh$getVertices(), extEdges)
shade3d(rglmesh2, color = "cyan", alpha = 0.2)

## ------------------------------------------------
## Method `cgalMesh$triangulate`
## ------------------------------------------------

library(rgl)
mesh <- cgalMesh$new(cube3d())
mesh$isTriangle() # FALSE
# warning: triangulating the mesh modifies it
mesh$triangulate()
mesh$isTriangle() # TRUE

## ------------------------------------------------
## Method `cgalMesh$union`
## ------------------------------------------------

library(cgalMeshes)
library(rgl)
# take two cubes
rglmesh1 <- cube3d()
rglmesh2 <- translate3d(cube3d(), 1, 1, 1)
mesh1 <- cgalMesh$new(rglmesh1)
mesh2 <- cgalMesh$new(rglmesh2)
# the two meshes must be triangle
mesh1$triangulate()
mesh2$triangulate()
# assign a color to the faces; they will be retrieved in the union
mesh1$assignFaceColors("yellow")
mesh2$assignFaceColors("navy")
# union
umesh <- mesh1$union(mesh2)
rglumesh <- umesh$getMesh()
# extract edges for plotting
extEdges <- exteriorEdges(umesh$getEdges())
# plot
open3d(windowRect = 50 + c(0, 0, 512, 512), zoom = 0.9)
shade3d(rglumesh, meshColor = "faces")
plotEdges(umesh$getVertices(), extEdges)

## ------------------------------------------------
## Method `cgalMesh$volume`
## ------------------------------------------------

library(rgl)
mesh <- cgalMesh$new(cube3d())$triangulate()
mesh$volume()

## ------------------------------------------------
## Method `cgalMesh$whereIs`
## ------------------------------------------------

library(cgalMeshes)
mesh <- cgalMesh$new(sphereMesh())
pt1 <- c(0, 0, 0) # inside
pt2 <- c(2, 0, 0) # outside
mesh$whereIs(rbind(pt1, pt2))

Run the code above in your browser using DataLab