Offset mesh surface

Hi, and Happy Easter greetings from spring sunny Stockholm,

I have a question regarding a functionality which I think is present in the library but I lack the sufficient understanding on how to get it to work (or even where to start).

I have succeeded so far with predictable and controllable (from your API) 3D-remeshing of all kinds (thanks very helpful content on your website and forum!) but I got stuck at attempt to create something seemingly obvious like a mesh which is at a constant offset (distance) from a given one. It looks like the levelset is the answer, but I just can’t get my head around it how to get something workable, preferably via API.

Input could be one of these:

  1. 3D mesh surface which defines an enclosed volume with no boundaries and no particular topology other than coordinates and indices defining triangles

  2. 3D mesh which defines an open surface with one or more boundaries loops (i.e. a cylinder without caps would have 2 boundary loops), and no particular topology other than coordinates and indices defining its triangles

Expected result:

In the first case: a new 3D mesh where the distance to the old one is constant, in other words a “parallel” mesh enclosing a larger (or smaller) volume, depending on the signed distance given in the same units as coordinates.

In the second case: a new 3D mesh where the distance to the old one is constant, in other words a mesh enclosing a volume around the old mesh.

I have no previous experience in volumetric meshing, I just want to produce what intuitively feels would be an implicit mesh created by a levelset, but I don’t know how to create one to begin with if this is the way to go about it.


Hi Chris and Happy Easter too,

The spring is shiny in Pau too ;-).

Discretization of 1 level-set value

As Mmg doesn’t deal with mesh generation, you are right, the only way to do what you want is to pass through an isovalue discretization:

  1. You will need to start from a volume mesh (for example, from a cube (2.9 MB) mesh) and from a closed surface (your first input, I join the surface mesh of a cylinder (30.6 KB) );

  2. From these data, you can compute the signed distance function to the surface at the nodes of the volume mesh. Mmg doesn’t provide such a tool but you can try the mshdist software (written by Charles Dapogny and Pascal Frey). Unfortunately, this software doesn’t provide API functions. To call mshdist by command line:
    mshdist cube.mesh cylinder.mesh
    It creates a cube.sol (303.3 KB) file that contains the signed distance to the cylinder surface.

  3. You can use Mmg to discretize the wanted isovalue. An example with the API is available in the libexamples/mmg3d/IsosurfDiscretization_lsOnly/main.[c|F90] files.
    There are few differences with the mesh adaptation mode:

    • you must initialize a data structure to store the level-set function (instead of the size map):
    • and call the MMG3D_mmg3dls(mmgMesh,mmgLs,NULL) function instead of the MMG3D_mmg3dlib one;
    • to avoid the automatic detection of spurious sharp edges, it is better to disable the sharp angle detection (not shown in the library example):
    • you can choose the level-set to be discretized (default is 0), to discretize the 0.2 value:
  4. you obtain the wanted surface mesh and the external and internal volumes. On my example, the 0 level-set look like this (you can have better results if you adapt the initial volume mesh over the level-set to discretize):

I am not sure if you only want to create a mesh at a given shift of the initial one (if yes, you just have to play with the level-set value) or if you want both meshes (initial and shifted one) inside the same volume mesh.

Discretization of a second level-set with preservation of the first one

For this second case, Mmg3d is not totally ready but I have tried to implement something in the feature/multi-mat3d branch:

  1. You have to create an exterior and interior volume mesh for your initial surface mesh with different references. It can be created using the level-set discretization of Mmg or with another tool. Here I will suppose that we start from the mesh obtained at stage 4.

  2. Then you must specify the mapping between the inital references and their new values if they are splitted (you can also ask to not split a given ref but it is useless in your case). You can do it using a parameter file named <testcase>.mmg3d file (<testcase> must be replaced by the name of yout initial mesh without extension (in my example cube.o.mmg3d) for a command line call or via API functions for a library call.
    The parameter file must have the following format:

3 4 5
2 6 7

Which is equivalent to the following API calls:


It says that you have 2 different references, the reference 3 is splitted into the reference 2 (interior mesh) and the reference 3 (exterior mesh) and the reference 2 is splitted into 4 (interior) and 5 (exterior).

  1. The last step consists to discretize the new level-set that you want in your mesh.

Note that it is a very very experimental feature (implemented last week and I am confined with small children ;-)). In particular, for now I have done nothing regarding the topology checks so you can end with errors like this:

   *** Topological problem: non manifold surface at point 11907

In this case, it worth to try to adapt the output mesh with a classic call of mesh adaptation (it can work).

To conclude this long answer:

The drawbacks of this method are that:

  • you need to compute a signed distance function so I don’t know how to do that from an open boundary (your second input);
  • as the distance function is smooth, you will not be able to capture the sharp angles of you cylinder (the 2 boundary loops);
  • you can meet bugs because it is freshly implemented.

I hope that it will help you.



Hi Algiane,

Thanks a million!
Your answer is a big help to me, I knew I was missing something essential here, but you just opened a whole new world to me. I’ll be back with more feedback after exploring it more in depth :slight_smile: