3D mesh
Overview
A 3D mesh (trace type mesh3d) draws a surface made of triangles in a 3D scene: a CAD part, a scanned object, a terrain, the hull around a point cloud. You give the vertices (x, y, z) and either the triangles (i, j, k) or let the trace derive them from the points: a Delaunay triangulation for a single surface layer, the convex hull, or an alpha shape that follows concave outlines. The mesh can be colored by a value per vertex or per triangle through a colorscale, by CSS colors per vertex or per triangle, or in one color, and is lit with Plotly's lighting model.
Pick a different chart when:
- the surface is a function
z = f(x, y)on a grid: asurfacetrace is simpler and draws the grid directly; - you only want to show the points: use a
scatter3dtrace; - the data is 2D: a heatmap or a contour plot is easier to read than a surface seen in perspective.
Minimal example
import { createChart } from '@mk7s/holochart';
createChart(document.getElementById('chart')!, {
data: [
{
type: 'mesh3d',
x: [0, 1, 0, 0],
y: [0, 0, 1, 0],
z: [0, 0, 0, 1],
i: [0, 0, 0, 1],
j: [1, 1, 2, 2],
k: [2, 3, 3, 3],
},
],
});Triangle m joins the vertices i[m], j[m] and k[m] (indices into x, y, z): four triangles make a tetrahedron. The live example draws an icosahedron (12 vertices, 20 triangles) in the trace's colorway color, smooth shaded:
mesh3d/basicData format
x,y,z: the vertices: numbers, dates or categories, as on any scene axis. The three arrays must have the same length, else the trace is not drawn (Plotly).i,j,k: the triangles, as vertex indices. Give all three or none. Indices are rounded; if one is out of range (or the three lengths differ) the whole mesh is not drawn, as in Plotly. Triangles with a missing vertex (null,NaN) are left out.Without
i,j,k,alphahulldecides how triangles are derived from the vertices:-1(default): a 2D Delaunay triangulation of the points projected alongdelaunayaxis('z': of their x–y positions;'x': y–z;'y': z–x). Suits a single surface layer seen along that axis, such as a terrain from scattered samples.0: the convex hull of the points (points inside it are left out).> 0: the alpha shape with this alpha: the Delaunay tetrahedra of the points whose circumradius is below1 / alphahull, and the triangles on their boundary. It follows concave shapes the convex hull fills in.
As in Plotly, these work on coordinates scaled to the unit cube per axis (each axis divided by the data's span), so
alphahulldoesn't depend on the data's units, and a Delaunay triangulation of long, flat data isn't skewed by the axis scales. Coplanar points (for the hull) or collinear ones (for Delaunay) give no triangles.intensity: values mapped through the colorscale, one per vertex (intensitymode: 'vertex', default) or one per triangle ('cell').vertexcolor/facecolor: a CSS color per vertex / per triangle.
Variations
Delaunay triangulation of scattered points
With neither i, j, k nor alphahull, 300 random samples of a terrain become a surface: the default alphahull: -1 triangulates their x–y positions. intensity: z colors it by height.
mesh3d/delaunayConvex hull
alphahull: 0 wraps a point cloud in its convex hull. flatshading shows the facets and opacity draws it translucent (its triangles are sorted back to front when the view changes).
mesh3d/convex-hullAlpha shape
A positive alphahull keeps the concave outline of the points: here 900 points filling a torus, whose hole the convex hull would close. Larger values carve tighter (and may split the shape or leave holes where points are sparse); smaller values approach the convex hull.
mesh3d/alpha-shapeIntensity per vertex or per triangle
Per-vertex intensity is interpolated across each triangle (the colorscale is sampled per pixel); intensitymode: 'cell' gives each triangle one value, and one color. Both meshes here share a coloraxis, and with it one colorbar and one color domain.
mesh3d/intensityVertex and face colors
vertexcolor blends CSS colors across the triangles, facecolor paints each triangle.
mesh3d/colorsFlat shading
flatshading: true lights each triangle with its own normal: the low-poly sphere on the right shows its facets, the one on the left interpolates normals across them.
mesh3d/flatshadingLighting
lighting sets the coefficients of Plotly's lighting model: ambient (the share of the color shown whatever the light), diffuse, specular (highlights), roughness (their spread) and fresnel (brighter at grazing angles). lightposition places the light in clip space, so it moves with the view (default far to the upper right).
mesh3d/lightingHover contour
contour.show draws, while hovering, the contour line through the hovered point: the level set of intensity (of z without intensity) at its value, in contour.color and contour.width.
mesh3d/contourStyling
- Color precedence (Plotly's):
intensity(throughcolorscale,cmin/cmax/cmid/cauto,reversescale, or a sharedcoloraxis), thenvertexcolor, thenfacecolor, thencolor(default: the trace's colorway color). Only the first one given is used. Without an explicitcolorscale, intensities pick the layout's automatic ramps (autocolorscale): sequential for non-negative values, diverging across zero. - Colorbar. A mesh with
intensityshows a colorbar (showscale, default on;colorbar). The defaultholochartlook slims it;plotly-classickeeps Plotly's. - Opacity.
opacity(and alpha in the colors or the colorscale) draws the mesh translucent, without depth writes and with its triangles sorted back to front whenever the view changes. - Shading.
flatshading,lighting.{ambient, diffuse, specular, roughness, fresnel, vertexnormalsepsilon, facenormalsepsilon}andlightposition.{x, y, z}with Plotly's mesh3d defaults (ambient 0.8, diffuse 0.8, specular 0.05, roughness 0.5, fresnel 0.2; the light at(1e5, 1e5, 0)). - Legend. Meshes are described by their colorbar and have no legend entry unless
showlegend: true, as in Plotly.
Interactivity
Hover. With per-vertex colors the label shows the hovered triangle's vertex nearest to the pointer:
x,y,z(formatted like the scene's axes, or byxhoverformat,yhoverformat,zhoverformat) and itstext/hovertext. With per-triangle colors (intensitymode: 'cell'orfacecolor) it shows the triangle's centroid and the triangle's text.hovertemplatecan use%{x},%{y},%{z},%{text}and%{intensity};hoverinfotakesx,y,z,textandname.Events.
hoverandclickpoints carryx,y,z,intensityandpointNumber(the vertex, or the triangle with per-triangle colors):tsimport { createChart } from '@mk7s/holochart'; const chart = createChart(document.getElementById('chart')!, { data: [{ type: 'mesh3d', x: [0, 1, 0], y: [0, 0, 1], z: [0, 0, 1], i: [0], j: [1], k: [2] }], }); chart.on('click', (e) => console.log(e.points[0]?.pointNumber));Camera. Orbit, zoom and pan are the scene's (see 3D scenes).
3D-native options
material(Holochart extension) swaps Plotly's lighting model for a three.js material:material.type'flat'(unlit),'lambert','phong','standard','physical','toon','matcap', …, lit by the scene's lights (scene.lighting), with shadows. See materials and lighting.- A scene's
lightingrig, when set, lights every mesh of the scene; each mesh'slightingcoefficients then scale its lights.
Performance notes
- The mesh is one draw call. Positions are stored relative to the mesh's center in 32-bit floats (with a 64-bit origin), so large coordinates (dates, geographic units) keep their precision.
- Per-triangle values (
intensitymode: 'cell',facecolor) andflatshadingstore three vertices per triangle instead of sharing them: about three times the memory of a smooth mesh. - Derived triangles are computed once per data change on the CPU. On a laptop, a Delaunay triangulation of 10,000 points takes about 50 ms and their convex hull under 10 ms; an alpha shape, which needs the 3D Delaunay tetrahedralization, about half a second. Give
i,j,kfor large meshes. - Translucent meshes re-sort their triangles whenever the camera moves (a linear-time sort, up to 500,000 triangles).
- The mesh code loads the first time a scene draws a mesh, so 2D charts never pay for it.
Accessibility notes
- Screen readers: the chart is a
<canvas>; the hidden description (see the accessibility guide) names the trace. - Keyboard: Shift + arrow keys orbit the camera,
+/-move it in and out and0resets it. There is no keyboard navigation between the trace's own points yet. See the keys by chart family. - Color: intensity colors rely on the colorscale: prefer ramps monotonic in lightness (the default ones are), and add a titled colorbar.
Attribute reference
See the mesh3d attribute reference for every attribute, its type, and its default. Shared color axes are under coloraxis in the layout reference.
Related charts
- Cone: a 3D vector field drawn as cones, in the same scenes and lighting
- 3D scenes: cameras, axes and controls of every 3D trace
- Contour plot: level lines of a grid in 2D
Plotly migration notes
- Attribute names and defaults match Plotly's
mesh3d:x,y,z,i,j,k,alphahull,delaunayaxis,intensity,intensitymode,vertexcolor,facecolor,color, the colorscale attributes,opacity,flatshading,contour,lighting,lightposition,text,hovertext,hovertemplate,hoverinfoand the hover formats. - Not supported yet:
xcalendar/ycalendar/zcalendar,hovertemplatefallback, and the mesh file loaders (STL, OBJ, PLY, glTF). - Differences:
- Triangles derived with
alphahullscale each axis by the trace's own data span; Plotly uses the span of every trace in the scene, which differs only with several traces in one scene. - With per-triangle colors, hover shows the triangle's centroid in data coordinates (Plotly shows it in its internal scaled units).
%{intensity}is available inhovertemplate(Plotly doesn't expose it), andcontour.widthis honored (Plotly always draws 1 px). The hover contour of a mesh with per-cell intensity follows z (Plotly contours the cell values as if they were per vertex).
- Triangles derived with