Draw an isosurface with Python Plotly with the help of Pyvista
Plotly isosurfaces are relatively new. I have not tried yet in Python. I tried in R and I found them a bit slow. In this post I showed how to plot an isosurface in R Plotly with the help of the misc3d package: by constructing a triangular 3D mesh and using the
mesh3d type in Plotly. Here I will show how to do something similar in Python, with the help of the PyVista package. I don’t know whether this is faster than the built-in way to draw an isosurface in Plotly, but there’s at least one advantage: the one I mentioned here, that is, the possibility to clip the isosurface to a region (usually a ball). I don’t think this is possible with the Plotly way (I may be wrong).
Let’s go. I gonna draw the Togliatti surface again. First, the mesh clipped to a box:
from math import sqrt import numpy as np import pyvista as pv import plotly.graph_objects as go import plotly.io as pio # to save the graphics as html 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 = ) isosurf = isosurf.extract_geometry()mesh0
Now we clip the Togliatti surface to a ball. The mesh we obtain is named
mesh. It is triangular:
True. If it were not, we would need to run
mesh.triangulate. Finally we extract the triangular faces from this mesh. The easiest way is to reshape the vector
mesh.faces to a matrix.
# surface clipped to the ball of radius 4.8, with the help of `clip_scalar`: "dist"] = np.linalg.norm(mesh0.points, axis = 1) mesh0[= mesh0.clip_scalar("dist", value = 4.8) mesh # True mesh.is_all_triangles # mesh.triangulate() = mesh.points points = mesh.faces.reshape(-1, 4)triangles
Now it’s a child game to plot the isosurface with Plotly:
= go.Figure(data=[ fig go.Mesh3d(=points[:, 0], x=points[:, 1], y=points[:, 2], z= [[0, 'gold'], colorscale 0.5, 'mediumturquoise'], [1, 'magenta']], [# Intensity of each vertex, which will be interpolated and color-coded = np.linspace(0, 1, len(mesh.points)), intensity # i, j and k give the vertices of the triangles = triangles[:, 1], i = triangles[:, 2], j = triangles[:, 3], k = False showscale ) ]) fig.show()