Skip to content

Meshplot Tutorials

Meshplot is a simple, and fast 2d and 3d mesh and point cloud viewer based on pythreejs.

Installing Meshplot

Meshplot can be installed from Conda forge with conda install -c conda-forge meshplot and imported as follows:

import numpy as np
import meshplot as mp

Mesh Representation

Meshplot uses numpy to encode vectors and matrices. A triangular mesh is encoded as a pair of matrices:

v: np.array
f: np.array

data = np.load('data.npz')
v, f, n, fs = data["v"], data["f"], data["n"], data["fs"]
v1, f1, v2, f2 = data["v1"], data["f1"], data["v2"], data["f2"]

Visualizing Surfaces

We can visualize surfaces, their properties and additional debugging information through the plot function. Let’s visualize the previously loaded triangle mesh:

mp.plot(v, f)

Scalar field visualization

Colors and normals can be associated to faces or vertices using the same plot function with three parameters.

The key parameter c represents the vertex or face colors and can be one of the following:

  1. A #v by 1 vector with one function value per vertex, which gets normalized and converted into vertex color values using the viridis colormap.
  2. A #v by 3 vector with RGB color values per vertex. The color values should be in the range 0.0-1.0.
  3. A single color value for all vertices in the form of a numpy array [R, G, B] in the range 0.0-1.0.
  4. A #f by 1 vector with one function value per face, which gets normalized and converted into face color values using the viridis colormap.
  5. A #f by 3 vector with RGB color values per face. The color values should be in the range 0.0-1.0.

The following four examples show vertex function colors (in this case just the y-coordinate), vertex normals as colors per vertex, random colors per face and face function colors (in this case the size of the faces):

d = mp.subplot(v, f, c=v[:, 1], s=[2, 2, 0])
mp.subplot(v, f, c=n, s=[2, 2, 1], data=d)
mp.subplot(v, f, c=np.random.rand(*f.shape), s=[2, 2, 2], data=d)
mp.subplot(v, f, c=fs, s=[2, 2, 3], data=d)

Visualizing Point Clouds

We can also visualize point clouds, their properties and additional debugging information through the plot function, by just leaving the faces array empty:

mp.plot(v)

Similar to the surface plot, we can also set color values for all points in the point cloud. This can be done either by passing function values or directly by passing colors:

d = mp.subplot(v, c=v[:, 1], s=[1, 2, 0], shading={"point_size": 0.03})
mp.subplot(v, c=np.random.rand(*v.shape), s=[1, 2, 1], data=d, shading={"point_size": 0.03})

Overlays, Textures and Shading

In addition to plotting the surface, the viewer supports the visualization of bounding boxes, points and lines. These overlays can be very helpful while developing geometric processing algorithms to plot debug information.

The following example draws a point of a given color for each row of v_box. The point is placed at the coordinates specified in each row of v_box, which is a #v_box by 3 matrix. In addition, edges of a given color are drawn for the vertices v_box with the indices f_box:

m = np.min(v, axis=0)
ma = np.max(v, axis=0)

# Corners of the bounding box
v_box = np.array([[m[0], m[1], m[2]], [ma[0], m[1], m[2]], [ma[0], ma[1], m[2]], [m[0], ma[1], m[2]],
                  [m[0], m[1], ma[2]], [ma[0], m[1], ma[2]], [ma[0], ma[1], ma[2]], [m[0], ma[1], ma[2]]])

# Edges of the bounding box
f_box = np.array([[0, 1], [1, 2], [2, 3], [3, 0], [4, 5], [5, 6], [6, 7], 
                  [7, 4], [0, 4], [1, 5], [2, 6], [7, 3]], dtype=np.int)

p = mp.plot(v, f, return_plot=True)

p.add_edges(v_box, f_box, shading={"line_color": "red"});
p.add_points(v_box, shading={"point_color": "green"})

The viewer allows for many customization options, which are presented below:

mi = np.min(v, axis=0)
ma = np.max(v, axis=0)

shading = {"flat":True, # Flat or smooth shading of triangles
           "wireframe":False, "wire_width": 0.03, "wire_color": "black", # Wireframe rendering
           "width": 600, "height": 600, # Size of the viewer canvas
           "antialias": True, # Antialising, might not work on all GPUs
           "scale": 2.0, # Scaling of the model
           "side": "DoubleSide", # FrontSide, BackSide or DoubleSide rendering of the triangles
           "colormap": "viridis", "normalize": [None, None], # Colormap and normalization for colors
           "background": "#ffffff", # Background color of the canvas
           "line_width": 1.0, "line_color": "black", # Line properties of overlay lines
           "bbox": False, # Enable plotting of bounding box
           "point_color": "red", "point_size": 0.01 # Point properties of overlay points
          }

p = mp.plot(v, f, shading=shading, return_plot=True)

# Instead of adding edges in the form of (v, f), also lines of the form (start, end) can be added
p.add_lines(v[f[:,0]], v[f[:,1]], shading={"line_color": "red"});

# The vertex positions can be updated as well
v += 0.003 * np.random.rand(v.shape[0], v.shape[1])
# The plotted objects get increasing ids. In this case the mesh object has id 0, and the lines object id 1.
p.update_object(vertices=v)

Events and Widgets

The viewer supports to use interactive widgets from the ipywidgets package to manipulate the plot.

v = [v1, v2]
f = [f1, f2]
p = mp.plot(v1, f1, return_plot=True)

@mp.interact(mesh=[('bump', 0), ('fertility', 1)])
def ff(mesh):
    mp.plot(v[mesh], f[mesh], plot=p)
p

Offline Plotting

Besides interactive plotting in Jupyter Notebooks, meshplot supports to plot objects in offline html pages. The offline mode is automatically selected, if meshplot is run outside of a Jupyter Notebook. Within Jupyter Notebooks, one can manually switch to offline mode as follows:

#mp.offline()
mp.plot(v1, f1, c=v1[:, 1])

Without parameters, the plot is stored with the name <UUID>.html. It is possible to save Jupyter plots after they are generated and to chose the filename as follows:

#mp.jupyter()
p = mp.plot(v1, f1, c=np.random.rand(*f1.shape), return_plot=True)
p.add_mesh(v1 + 5, f1, c=v1[:,1]);
p.add_points(v1 - 5, c=v1[:,2], shading={"point_size": 1.0})
#p.save("test.html")
p