mikedh / trimesh

Python library for loading and using triangular meshes.
https://trimesh.org
MIT License
3k stars 580 forks source link

How to save a mesh as a .msh file in trimesh #968

Open isurusuranga opened 4 years ago

isurusuranga commented 4 years ago

My question might be a bit simple but I haven't found a solution after searching the documentation. I have successfully loaded a mesh in the format of .msh, but I cannot re-write it back to .msh format. Any solution to that please?

The code is as below.

ref_mesh_path = './reference_ellipsoid.msh'
reference_mesh = trimesh.load(ref_mesh_path, process=False)
reference_mesh.export('./_mesh.msh')
mikedh commented 4 years ago

Hey, it looks like the gmsh.to_volume is exporting tetrahedrons, which trimesh doesn't support so they get lost on reload. For the msh format you might have better luck directly using meshio or gmsh:

In [2]: trimesh.interfaces.gmsh.to_volume?
Signature: trimesh.interfaces.gmsh.to_volume(mesh, file_name=None, max_element=None, mesher_id=1)
Docstring:
Convert a surface mesh to a 3D volume mesh generated by gmsh.

An easy way to install the gmsh sdk is through the gmsh-sdk
package on pypi, which downloads and sets up gmsh:
    pip install gmsh-sdk

Algorithm details, although check gmsh docs for more information:
The "Delaunay" algorithm is split into three separate steps.
First, an initial mesh of the union of all the volumes in the model is performed,
without inserting points in the volume. The surface mesh is then recovered using H.
Si's boundary recovery algorithm Tetgen/BR. Then a three-dimensional version of the
2D Delaunay algorithm described above is applied to insert points in the volume to
respect the mesh size constraints.

The Frontal" algorithm uses J. Schoeberl's Netgen algorithm.
The "HXT" algorithm is a new efficient and parallel reimplementaton
of the Delaunay algorithm.
The "MMG3D" algorithm (experimental) allows to generate
anisotropic tetrahedralizations

Parameters
--------------
mesh : trimesh.Trimesh
  Surface mesh of input geometry
file_name : str or None
  Location to save output, in .msh (gmsh) or .bdf (Nastran) format
max_element : float or None
  Maximum length of an element in the volume mesh
mesher_id : int
  3D unstructured algorithms:
  1: Delaunay, 4: Frontal, 7: MMG3D, 10: HXT

Returns
------------
data : None or bytes
  MSH data, only returned if file_name is None
File:      /dropbox/drop/Dropbox/robotics/trimesh/trimesh/interfaces/gmsh.py
Type:      function

In [3]: trimesh.interfaces.gmsh.to_volume(m, 'hi.msh')
DEBUG            export.py:73    Exporting 1280 faces as STL
DEBUG              base.py:361   generating face normals
Info    : Reading '/tmp/tmpth329pt5.stl'...
Info    : Mesh is in binary format
Info    : 1280 facets in solid 0 
Info    : Done reading '/tmp/tmpth329pt5.stl'
Info    : Meshing 1D...
Info    : Done meshing 1D (Wall 2.14577e-06s, CPU 8e-06s)
Info    : Meshing 2D...
Info    : Done meshing 2D (Wall 0.00142097s, CPU 0.000123s)
Info    : Meshing 3D...
Info    : 3D Meshing 1 volume with 1 connected component
Info    : Tetrahedrizing 642 nodes...
Info    : Done tetrahedrizing 650 nodes (Wall 0.0103331s, CPU 0.008251s)
Info    : Reconstructing mesh...
Info    :  - Creating surface mesh
Info    :  - Identifying boundary edges
Info    :  - Recovering boundary
Info    : Done reconstructing mesh (Wall 0.0198691s, CPU 0.014756s)
Info    : Found volume 1
Info    : It. 0 - 0 nodes created - worst tet radius 6.65874 (nodes removed 0 0)
Info    : It. 500 - 500 nodes created - worst tet radius 1.55635 (nodes removed 0 0)
Info    : It. 1000 - 1000 nodes created - worst tet radius 1.27824 (nodes removed 0 0)
Info    : It. 1500 - 1500 nodes created - worst tet radius 1.17763 (nodes removed 0 0)
Info    : It. 2000 - 2000 nodes created - worst tet radius 1.05446 (nodes removed 0 0)
Info    : 3D refinement terminated (3029 nodes total):
Info    :  - 0 Delaunay cavities modified for star shapeness
Info    :  - 0 nodes could not be inserted
Info    :  - 17034 tetrahedra created in 0.0882268 sec. (193070 tets/s)
Info    : Done meshing 3D (Wall 0.148516s, CPU 0.126292s)
Info    : Optimizing mesh...
Info    : Optimizing volume 1
Info    : Optimization starts (volume = 4.15274) with worst = 0.00921056 / average = 0.792284:
Info    : 0.00 < quality < 0.10 :        36 elements
Info    : 0.10 < quality < 0.20 :        87 elements
Info    : 0.20 < quality < 0.30 :       184 elements
Info    : 0.30 < quality < 0.40 :       284 elements
Info    : 0.40 < quality < 0.50 :       452 elements
Info    : 0.50 < quality < 0.60 :       719 elements
Info    : 0.60 < quality < 0.70 :      1428 elements
Info    : 0.70 < quality < 0.80 :      3538 elements
Info    : 0.80 < quality < 0.90 :      6434 elements
Info    : 0.90 < quality < 1.00 :      3872 elements
Info    : 304 edge swaps, 11 node relocations (volume = 4.15274): worst = 0.277607 / average = 0.803557 (Wall 0.00698996s, CPU 0.006923s)
Info    : 305 edge swaps, 11 node relocations (volume = 4.15274): worst = 0.300874 / average = 0.803589 (Wall 0.00817895s, CPU 0.007987s)
Info    : No ill-shaped tets in the mesh :-)
Info    : 0.00 < quality < 0.10 :         0 elements
Info    : 0.10 < quality < 0.20 :         0 elements
Info    : 0.20 < quality < 0.30 :         0 elements
Info    : 0.30 < quality < 0.40 :       271 elements
Info    : 0.40 < quality < 0.50 :       440 elements
Info    : 0.50 < quality < 0.60 :       718 elements
Info    : 0.60 < quality < 0.70 :      1396 elements
Info    : 0.70 < quality < 0.80 :      3595 elements
Info    : 0.80 < quality < 0.90 :      6512 elements
Info    : 0.90 < quality < 1.00 :      3832 elements
Info    : Done optimizing mesh (Wall 0.0248871s, CPU 0.020157s)
Info    : 3029 nodes 18044 elements
Info    : Writing 'hi.msh'...
Info    : Done writing 'hi.msh'

In [4]: m = trimesh.load('hi.msh')
DEBUG              load.py:227   loaded <trimesh.Trimesh(vertices.shape=(3029, 3), faces.shape=(0, 3))> using load_meshio
DEBUG         constants.py:137   load_mesh executed in 0.0303 seconds.

In [5]: m
Out[5]: <trimesh.Trimesh(vertices.shape=(3029, 3), faces.shape=(0, 3))>

In [6]: import meshio

In [7]: meshio.read?
Signature: meshio.read(filename, file_format=None)
Docstring:
Reads an unstructured mesh with added data.

:param filenames: The files/PathLikes to read from.
:type filenames: str

:returns mesh{2,3}d: The mesh data.
File:      ~/miniconda/lib/python3.6/site-packages/meshio/_helpers.py
Type:      function

In [8]: a = meshio.read('hi.msh')

In [9]: a
Out[9]: 
<meshio mesh object>
  Number of points: 3029
  Number of cells:
    tetra: 16764
  Cell sets: Nastran_bdf
  Cell data: gmsh:physical, gmsh:geometrical

In [10]: a.cell_data
Out[10]: 
{'gmsh:geometrical': [array([1, 1, 1, ..., 1, 1, 1])],
 'gmsh:physical': [array([1, 1, 1, ..., 1, 1, 1])]}
isurusuranga commented 4 years ago

I have already used gmsh to first produce volumetric mesh (i.e. .msh file). And then I tried to read it using trimesh and I have got below output.

<trimesh.Trimesh(vertices.shape=(4883, 3), faces.shape=(4704, 3))> I'm using this because pytorch geometric package supports trimesh to read and feed them to graph neural nets easily. You mean that trimesh does not support 3D volumetric meshes and it only support surface meshes right ?

mikedh commented 4 years ago

Ah yeah, trimesh only supports surface meshes. If you load the msh file with just meshio does it have triangular faces?

isurusuranga commented 4 years ago

Yes it has. See below output from meshio.

<meshio mesh object>
  Number of points: 4883
  Number of cells:
    triangle: 4704
    tetra: 22687
  Cell data: gmsh:physical, gmsh:geometrical