Mesh3D shading bug

Mesh3D seems to have some bug or numerical roundoff error, presumably related to computing normals. Can someone provide more information or suggest a workaround?

This does not happen at lower resolution and at higher resolutions the entire surface is affected:

Without flatshading=False problem occur even at lower resolutions and look like this:

Here is code for a low-resolution version:

import plotly.graph_objects as go
import numpy as np

vertices = np.array([[-1.0, -1.0, 0.0], [-1.0, 1.0, 0.0], [1.0, -1.0, 0.0], [1.0, 1.0, 0.0], [-1.0, 0.0, 1.0], [0.0, 0.0, 0.0], [0.0, -1.0, -1.0], [1.0, 0.0, 1.0], [0.0, 1.0, -1.0], [-1.0, -0.5, 0.75], [-0.5, -0.5, 0.0], [-0.5, -1.0, -0.75], [-0.5, 0.5, 0.0], [-0.5, 0.0, 0.25], [-1.0, 0.5, 0.75], [0.5, -1.0, -0.75], [0.0, -0.5, -0.25], [0.5, -0.5, 0.0], [0.0, 0.5, -0.25], [-0.5, 1.0, -0.75], [1.0, -0.5, 0.75], [0.5, 0.0, 0.25], [0.5, 1.0, -0.75], [0.5, 0.5, 0.0], [1.0, 0.5, 0.75]])
faces = np.array([[0, 9, 11], [4, 10, 9], [6, 11, 10], [9, 10, 11], [1, 12, 14], [5, 13, 12], [4, 14, 13], [12, 13, 14], [2, 15, 17], [6, 16, 15], [5, 17, 16], [15, 16, 17], [4, 13, 10], [5, 16, 13], [6, 10, 16], [13, 16, 10], [1, 12, 19], [5, 18, 12], [8, 19, 18], [12, 18, 19], [2, 20, 17], [7, 21, 20], [5, 17, 21], [20, 21, 17], [3, 22, 24], [8, 23, 22], [7, 24, 23], [22, 23, 24], [5, 21, 18], [7, 23, 21], [8, 18, 23], [21, 23, 18]])

x, y, z = vertices.T
i, j, k = faces.T

fig = go.Figure(data=[go.Mesh3d(x=x, y=y, z=z,
                                i=i, j=j, k=k,
                                flatshading=False)])
fig.show()

Any ideas?

@robert-lieck Your example gives a too coarse triangulation, i.e. the number of vertices and triangles is too low. To get a nice triangulated surface you should triangulate it via scipy.spatial.Delaunay:

import numpy as np
from scipy.spatial import Delaunay

x= np.linspace(-1,1, 20)
x, y = np.meshgrid(x,x)
x = x.flatten()
y = y.flatten()
z = x*y
#define 2D points, as input data for the Delaunay triangulation of the square S=[-1,1]x[-1,1]
points2D=np.vstack([x,y]).T
tri = Delaunay(points2D)#triangulate the square S=[-1,1]
i, j, k = tri.simplices.T
fig = go.Figure(go.Mesh3d(x=x, y=y, z=z, i=i, j=j, k=k, intensity=z, colorscale="deep_r", colorbar_thickness=24))
fig.update_layout(width=500, height=500, font_family="balto", font_size=11)
fig.show()

tri.simplices is a np.array of integers, of shape (ntri,3), where ntri is the number of triangles generated by scipy.spatial.Delaunay.
Each row in this array contains three indices, i, j, k, such that points2D[i,:], points2D[j,:], points2D[k,:] are vertices of a triangle in the Delaunay triangulation of the square S.
The same simplices define the triangles on the surface z=f(x,y).

Thanks @empet for your reply. Delaunay triangulation is indeed a great way to get a nice triangulation from vertices, but my question is about the shading. I am happy with the triangulation (it could be improved, but that’s not the point here), the problem is the incorrect shading at the centre. Any ideas about that?

I worked with Mesh3d since its launching and haven’t noticed such a bug when the surface exhibits a saddle, like in your posted images. Have you set intensity? It triggers color interpolation.

I have not used intensity. It is also not related to it being a saddle, the same happens with a simple parabola

However, it is related not just to these couple of triangles, but to the overall surface: if I remove all triangles except the problematic ones and a couple of more around (so the z-range is much smaller), the bug does not occur. My best guess is that it is related to numerical errors in surface normals or something, definitely seems to be a bug in Plotly.js.

Created an issue on GitHub: Mesh3D rendering bug · Issue #5183 · plotly/plotly.py · GitHub

@robert-lieck You posted some buggy plots, but data in your code are not fror those surfaces. Could you please provide data that led to that cross on the surface? As it looks, I’m sure that the cause of that strange looking patch about the saddle point is also the coarse triangulation.

Sure, no problem, I have condensed it down to a minimal example. Interestingly, @empet, you are right that it depends on the coarseness of the triangulation, but the other way around: coarser ones do not have artifacts, it’s the finer ones that do. When decreasing from n=34 to n=32 below the artifacts do not occur.

import plotly.graph_objects as go
import numpy as np

n = 34
x, y = np.meshgrid(np.linspace(-1, 1, n),
                   np.linspace(-1, 1, n))
x = x.flatten()
y = y.flatten()
z = - x**2 - y**2

idx = np.arange(n**2)
i = np.concatenate([idx[np.logical_and(x<1, y<1)]]*2)
j = np.concatenate([idx[np.logical_and(x>-1, y<1)],
                    idx[np.logical_and(x<1, y>-1)]])
k = np.concatenate([idx[np.logical_and(x>-1, y>-1)]]*2)

go.Figure(data=[go.Mesh3d(x=x, y=y, z=z, i=i, j=j, k=k, flatshading=True, color='lightblue')]).show()

To remove the artifacts, just define:

fig = go.Figure(go.Mesh3d(x=x, y=y, z=z, i=i, j=j, k=k, 
                    flatshading=True, color="lightblue",   
                    lighting=dict(vertexnormalsepsilon=0,
                                facenormalsepsilon= 0)))

Many thanks, this is really helpful!

Ironically, the documentation of vertexnormalsepsilon and facenormalsepsilon says this “avoids math issues arising from degenerate geometry” – but apparently did the opposite here.

Any advice how to avoid trouble in the future? Or is the solution to manually check for artifacts and play around with those value if problems occur? I feel there surely must be a way to catch these problem automatically.

0-value for both ensures that no artifact will be exhibited when flatshading=True or False (False is the default setting)

OK, thanks – I wonder why this is not the default value