# Meshes / Surfaces¶

Meshes (or surfaces) in ipyvolume consist of triangles, and are defined by their coordinate (vertices) and faces/triangles, which refer to three vertices.

```
In [1]:
```

```
import ipyvolume.pylab as p3
import numpy as np
```

## Triangle meshes¶

Lets first construct a ‘solid’, a tetrahedron, consisting out of 4 vertices, and 4 faces (triangles) using plot_trisurf

```
In [2]:
```

```
s = 1/2**0.5
# 4 vertices for the tetrahedron
x = np.array([1., -1, 0, 0])
y = np.array([0, 0, 1., -1])
z = np.array([-s, -s, s, s])
# and 4 surfaces (triangles), where the number refer to the vertex index
triangles = [(0, 1, 2), (0, 1, 3), (0, 2, 3), (1,3,2)]
```

```
In [3]:
```

```
p3.figure()
# we draw the tetrahedron
p3.plot_trisurf(x, y, z, triangles=triangles, color='orange')
# and also mark the vertices
p3.scatter(x, y, z, marker='sphere', color='blue')
p3.xyzlim(-2, 2)
p3.show()
```

## Surfaces¶

To draw parametric surfaces, which go from \(\Bbb{R}^2 \rightarrow \Bbb{R}^3\), it’s convenient to use plot_surface, which takes 2d numpy arrays as arguments, assuming they form a regular grid (meaning you do not need to provide the triangles, since they can be inferred from the shape of the arrays). Note that plot_wireframe has a similar api, as does plot_mesh which can do both the surface and wireframe at the same time.

```
In [4]:
```

```
# f(u, v) -> (u, v, u*v**2)
a = np.arange(-5, 5)
U, V = np.meshgrid(a, a)
X = U
Y = V
Z = X*Y**2
p3.figure()
p3.plot_surface(X, Z, Y, color="orange")
p3.plot_wireframe(X, Z, Y, color="black")
p3.show()
```

## Colors¶

Vertices can take colors as well, as the example below (adapted from matplotlib) shows.

```
In [5]:
```

```
X = np.arange(-5, 5, 0.25*1)
Y = np.arange(-5, 5, 0.25*1)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)
```

```
In [6]:
```

```
from matplotlib import cm
colormap = cm.coolwarm
znorm = Z - Z.min()
znorm /= znorm.ptp()
znorm.min(), znorm.max()
color = colormap(znorm)
```

```
In [7]:
```

```
p3.figure()
mesh = p3.plot_surface(X, Z, Y, color=color[...,0:3])
p3.show()
```

## Texture mapping¶

Texture mapping can be done by providing a PIL image, and UV coordiante (texture coordinates, between 0 and 1). Note that like almost anything in ipyvolume, these u & v coordinates can be animated, as well as the textures.

```
In [8]:
```

```
import PIL.Image
image = PIL.Image.open('data/jupyter.png')
```

```
In [9]:
```

```
fig = p3.figure()
p3.style.use('dark')
# we create a sequence of 8 u v coordinates so that the texture moves across the surface.
u = np.array([X/5 +np.sin(k/8*np.pi)*4. for k in range(8)])
v = np.array([-Y/5*(1-k/7.) + Z*(k/7.) for k in range(8)])
mesh = p3.plot_mesh(X, Z, Y, u=u, v=v, texture=image, wireframe=False)
p3.animation_control(mesh, interval=800, sequence_length=8)
p3.show()
```

We now make a small movie / animated gif of 30 frames.

```
In [10]:
```

```
frames = 30
p3.movie('movie.gif', frames=frames)
```

And play that movie on a square

```
In [11]:
```

```
p3.figure()
x = np.array([-1., 1, 1, -1])
y = np.array([-1, -1, 1., 1])
z = np.array([0., 0, 0., 0])
u = x / 2 + 0.5
v = y / 2 + 0.5
# square
triangles = [(0, 1, 2), (0, 2, 3)]
m = p3.plot_trisurf(x, y, z, triangles=triangles, u=u, v=v, texture=PIL.Image.open('movie.gif'))
p3.animation_control(m, sequence_length=frames)
p3.show()
```