Concepts¶
The three modules¶
MMG is three remeshers sharing one infrastructure; mmg-wasm builds all of them into a single WASM binary:
| Module | Mesh type | Entry points |
|---|---|---|
mmg2d |
planar triangular (+ quads) | remesh (mmg2dlib), generate (mmg2dmesh), levelset (mmg2dls), move (mmg2dmov) |
mmgs |
triangulated surfaces in 3D | remesh (mmgslib), levelset (mmgsls) |
mmg3d |
tetrahedral volumes (+ prisms) | remesh (mmg3dlib), levelset (mmg3dls), move (mmg3dmov) |
Each module namespace carries its full generated API
(mmg3d.setVertex, mmg2d.getTriangles, …) plus its parameter constants
(mmg3d.IPARAM_verbose, mmg3d.DPARAM_hausd, …). Shared constants
(MMG5_SUCCESS, MMG5_Scalar, MMG5_Vertex, format codes) live on the
root API object.
Handles¶
The MMG C API works on two structures:
MMG5_pMesh— the mesh: vertices, elements, adjacency, parameters;MMG5_pSol— a solution field: metric (size map), level-set values, or displacement vectors.
In JavaScript these are opaque numeric handles (branded as MeshHandle /
SolHandle in TypeScript). You never touch their contents; you pass them to
API functions. A handle is technically the address of a small heap slot
holding the C struct pointer, which lets MMG's own free functions NULL the
pointer exactly as they do in C — but you can treat it as an opaque token.
Lifecycle¶
const h = mmg3d.init(); // { mesh, met }
const h2 = mmg3d.init({ levelset: true }); // { mesh, met, ls }
const h3 = mmg3d.init({ displacement: true }); // { mesh, met, disp }
// ... use ...
mmg3d.free(h); // MMG3D_Free_all + release handles
init()wraps the (variadic)MMG3D_Init_meshC call through a fixed shim; it allocates and initializes the structures.free()wrapsMMG3D_Free_alland releases the handle slots. Always call it — WASM memory is not garbage-collected.- Multiple concurrent handle sets are fine; the WASM instance is shared but each mesh is independent.
There is no global initialize/finalize pair like Gmsh has: after
await initialize() (which only loads the WASM), lifecycle is entirely
per-mesh.
Data flow¶
Every workflow is the same three phases, mirroring the C API:
- Describe:
setMeshSize(allocates), then per-entity setters (setVertex,setTetrahedron, …) or bulk setters (setVertices,setTetrahedra, … with typed arrays). Optionally attach a solution:setSolSize+setScalarSol/setTensorSol/… onh.met(metric) orh.ls(level-set). - Run: set parameters (
setIparameter/setDparameter), then call an entry point (remesh/levelset/move/generate). - Harvest:
getMeshSize, then per-entity getters (getVertex) or bulk getters (getVertices(h.mesh, np)→ typed arrays).
Indices are 1-based
MMG numbers vertices and elements from 1, and bulk arrays are laid out
accordingly (the first vertex occupies vertices[0..2] but is vertex
1 when referenced from a tetrahedron).
Parameters¶
Integer parameters via setIparameter(mesh, sol, IPARAM_*, value), real
parameters via setDparameter(mesh, sol, DPARAM_*, value). The most used:
| Parameter | Meaning |
|---|---|
IPARAM_verbose |
verbosity, -1 silences all output |
DPARAM_hausd |
Hausdorff distance controlling boundary approximation |
DPARAM_hmin / DPARAM_hmax |
min/max edge lengths |
DPARAM_hsiz |
constant target edge length |
DPARAM_hgrad |
mesh gradation |
IPARAM_iso |
enable level-set discretization mode |
IPARAM_optim |
optimize while keeping edge sizes |
The full per-module lists are on each module object (every IPARAM_* /
DPARAM_* enum entry) and in the API reference.
Solutions (metrics, level-sets, displacements)¶
A MMG5_pSol stores per-vertex values whose meaning depends on the entry
point: for remesh it is the metric (scalar isotropic size or tensor
anisotropic metric), for levelset the implicit function values, for move
the displacement vectors. Declare with
setSolSize(mesh, sol, MMG5_Vertex, np, MMG5_Scalar | MMG5_Vector | MMG5_Tensor)
then fill with the matching setters.