# Clipping an isosurface to a ball, and more

We will firstly show how to clip an isosurface to a ball with R, and then, more generally, how to clip a surface to an arbitrary region. In the last part we show how to achieve the same with Python.

# Using R

## The Togliatti isosurface, clipped to a box

The Togliatti surface is the isosurface \(f(x, y, z) = 0\), where the function \(f\) is defined as follows in R:

```
function(x,y,z){
f <-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
}
```

To plot an isosurface in R, there is the **misc3d** package. Below we plot the Togliatti surface clipped to the box \([-5,5] \times [-5,5] \times [-4,4]\).

```
library(misc3d)
# make grid
220L; ny <- 220L; nz <- 220L
nx <- seq(-5, 5, length.out = nx)
x <- seq(-5, 5, length.out = ny)
y <- seq(-4, 4, length.out = nz)
z <- expand.grid(x = x, y = y, z = z)
G <-# calculate voxel
array(with(G, f(x, y, z)), dim = c(nx, ny, nz))
voxel <-# compute isosurface
surf1 <- computeContour3d(voxel, maxvol = max(voxel), level = 0, x = x, y = y, z = z)
# make triangles
makeTriangles(surf1, smooth = TRUE) triangles <-
```

`drawScene.rgl(triangles, color = "yellowgreen")`

It is fun, but you will see later that it is prettier when clipped to a ball. And it is desirable to get rid of the isolated components at the top.

The simplest way to clip the surface to a ball consists in using the `mask`

argument of the function `computeContour3d`

. We use \(4.8\) as the radius.

```
array(with(G, x^2+y^2+z^2 < 4.8^2), dim = c(nx, ny, nz))
mask <- computeContour3d(
surf2 <-maxvol = max(voxel), level = 0, mask = mask, x = x, y = y, z = z
voxel,
) makeTriangles(surf2, smooth = TRUE)
triangles <-drawScene.rgl(triangles, color = "orange")
```

That’s not bad, but you see the problem: the borders are not smooth.

## Resorting to spherical coordinates

Using spherical coordinates will allow us to get the surface clipped to the ball with smooth borders. Here is the method:

```
# Togliatti surface equation with spherical coordinates
function(ρ, θ, ϕ){
h <- ρ * cos(θ) * sin(ϕ)
x <- ρ * sin(θ) * sin(ϕ)
y <- ρ * cos(ϕ)
z <-f(x, y, z)
}# make grid
300L; nθ <- 300L; nϕ <- 300L
nρ <- seq(0, 4.8, length.out = nρ) # ρ runs from 0 to the desired radius
ρ <- seq(0, 2*pi, length.out = nθ)
θ <- seq(0, pi, length.out = nϕ)
ϕ <- expand.grid(ρ=ρ, θ=θ, ϕ=ϕ)
G <-# calculate voxel
array(with(G, h(ρ, θ, ϕ)), dim = c(nρ, nθ, nϕ))
voxel <-# calculate isosurface
surf3 <- computeContour3d(voxel, maxvol = max(voxel), level = 0, x = ρ, y = θ, z = ϕ)
# transform to Cartesian coordinates
t(apply(surf3, 1L, function(ρθϕ){
surf3 <- ρθϕ[1L]; θ <- ρθϕ[2L]; ϕ <- ρθϕ[3L]
ρ <-c(
* cos(θ) * sin(ϕ),
ρ * sin(θ) * sin(ϕ),
ρ * cos(ϕ)
ρ
)
}))# draw isosurface
drawScene.rgl(makeTriangles(surf3, smooth=TRUE), color = "violetred")
```

Now the surface is pretty nice. The borders are smooth.

## Another way: using the rgl package

Nowadays, in the **rgl** package, there is the function `clipMesh3d`

which allows to *clip a mesh to a general region*. In order to use it, we need a `mesh3d`

object. There is an unexported function in **misc3d** which allows to easily get a `mesh3d`

object; it is called `t2ve`

.

We start with the isosurface clipped to the box:

```
makeTriangles(surf1)
triangles <-# convert to rgl `mesh3d`
misc3d:::t2ve(triangles)
mesh <- tmesh3d(mesh$vb, mesh$ib) rglmesh <-
```

To define the region to which we want to clip the mesh, one has to introduce a function and to give a bound for this function. Here the region is \(x^2 + y^2 + z^2 < 4.8^2\), so we proceed as follows:

```
function(x, y, z) x^2 + y^2 + z^2 # or function(xyz) rowSums(xyz^2)
fn <- addNormals(clipMesh3d(
clippedmesh <-bound = 4.8^2, greater = FALSE
rglmesh, fn, ))
```

It is a bit slow. Probably the algorithm is not very easy. But we get our desired result:

`shade3d(clippedmesh, color = "magenta")`

# Using Python

With the wonderful Python library **PyVista**, we proceed as follows to create the mesh of the Togliatti isosurface:

```
from math import sqrt
import numpy as np
import pyvista as pv
def f(x, y, z):
return (
64
* (x - 1)
* (
** 4
x - 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
)
# generate data grid for computing the values
= np.mgrid[(-5):5:250j, (-5):5:250j, (-4):4:250j]
X, Y, Z # create a structured grid
= pv.StructuredGrid(X, Y, Z)
grid # compute and assign the values
= f(X, Y, Z)
values "values"] = values.ravel(order="F")
grid.point_data[# compute the isosurface f(x, y, z) = 0
= grid.contour(isosurfaces=[0])
isosurf = isosurf.extract_geometry() mesh
```

To plot the mesh clipped to the box:

```
# surface clipped to the box:
=True, color="yellowgreen", specular=15) mesh.plot(smooth_shading
```

The `remove_points`

method is similar to the `mask`

approach with **misc3d** (it produces non-smooth borders):

```
# surface clipped to the ball of radius 4.8, with the help of `remove_points`:
= np.linalg.norm(mesh.points, axis=1)
lengths = lengths >= 4.8
toremove = mesh.remove_points(toremove)
masked_mesh, idx =True, color="orange", specular=15) masked_mesh.plot(smooth_shading
```

To get smooth borders, you have to use the `clip_scalar`

method:

```
# surface clipped to the ball of radius 4.8, with the help of `clip_scalar`:
"dist"] = lengths
mesh[= mesh.clip_scalar("dist", value=4.8)
clipped_mesh
clipped_mesh.plot(=True, cmap="inferno", window_size=[512, 512],
smooth_shading=False, specular=15, show_axes=False, zoom=1.2,
show_scalar_bar="#363940"
background )
```