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()