pythonmeshpyvista

Multibody Surface to Volumetric Mesh in PyVista


I have a project that involves importing surfaces meshes into PyVista and converting them to volumetric meshes using tetgen. Some of these surface meshes contain multiple bodies, being essentially split along a surface. These cannot be tetrahedralized because they are non-manifold, but I would like to find a way to repair them in a way that preserves both sections. Below is an example showing the type of mesh I am dealing with, and how one section is lost when meshfix.repair is used. (Apologies for the large data set, this problem doesn't seem to happen with simpler meshes)

Given an input mesh like this, how can I end up with a tetrahedralization of both sections?

Edit: Some more thoughts.

import pyvista as pv
import tetgen
import pymeshfix
import numpy as np

points = np.array([[ 76.84349  ,   2.718057 ,   2.718057 ],
                 [ 82.79519  ,   1.491148 ,   1.491148 ],
                 [  0.       ,   0.       ,   0.       ],
                 [ 70.97732  ,   4.304215 ,   4.304215 ],
                 [ 88.81041  ,   0.6280057,   0.6280057],
                 [ 94.3964   ,   0.1571253,   0.1571253],
                 [100.       ,   0.       ,   0.       ],
                 [ 54.10642  ,  11.15306  ,  11.15306  ],
                 [ 59.58774  ,   8.529518 ,   8.529518 ],
                 [ 65.21831  ,   6.243754 ,   6.243754 ],
                 [ 38.75704  ,  20.94748  ,  20.94748  ],
                 [ 43.6718   ,  17.37353  ,  17.37353  ],
                 [ 48.79456  ,  14.1047   ,  14.1047   ],
                 [ 25.43809  ,  33.36277  ,  33.36277  ],
                 [ 29.62332  ,  28.95689  ,  28.95689  ],
                 [ 34.06845  ,  24.81337  ,  24.81337  ],
                 [ 14.59111  ,  47.9873   ,  47.9873   ],
                 [ 17.90807  ,  42.89558  ,  42.89558  ],
                 [ 21.52819  ,  38.01472  ,  38.01472  ],
                 [  6.575722 ,  64.33623  ,  64.33623  ],
                 [  8.914448 ,  58.72746  ,  58.72746  ],
                 [ 11.58954  ,  53.2711   ,  53.2711   ],
                 [  1.657672 ,  81.86754  ,  81.86754  ],
                 [  2.940621 ,  75.92768  ,  75.92768  ],
                 [  4.581992 ,  70.07671  ,  70.07671  ],
                 [  0.       , 100.       , 100.       ],
                 [  0.1846403,  93.92597  ,  93.92597  ],
                 [  0.7378794,  87.87437  ,  87.87437  ],
                 [  0.       ,   0.       , 100.       ],
                 [ 88.81041  ,   0.       ,   0.6280057],
                 [ 76.84349  ,   0.       ,   2.718057 ],
                 [ 65.21831  ,   0.       ,   6.243754 ],
                 [ 54.10642  ,   0.       ,  11.15306  ],
                 [ 43.6718   ,   0.       ,  17.37353  ],
                 [ 34.06845  ,   0.       ,  24.81337  ],
                 [ 25.43809  ,   0.       ,  33.36277  ],
                 [ 17.90807  ,   0.       ,  42.89558  ],
                 [ 11.58954  ,   0.       ,  53.2711   ],
                 [  6.575722 ,   0.       ,  64.33623  ],
                 [  2.940621 ,   0.       ,  75.92768  ],
                 [  0.7378794,   0.       ,  87.87437  ],
                 [ 87.87437  ,   0.7378794,   0.7378794],
                 [ 93.92597  ,   0.1846403,   0.1846403],
                 [ 81.86754  ,   1.657672 ,   1.657672 ],
                 [ 75.92768  ,   2.940621 ,   2.940621 ],
                 [ 70.07671  ,   4.581992 ,   4.581992 ],
                 [ 64.33623  ,   6.575722 ,   6.575722 ],
                 [ 58.72746  ,   8.914448 ,   8.914448 ],
                 [ 53.2711   ,  11.58954  ,  11.58954  ],
                 [ 47.9873   ,  14.59111  ,  14.59111  ],
                 [ 42.89558  ,  17.90807  ,  17.90807  ],
                 [ 38.01472  ,  21.52819  ,  21.52819  ],
                 [ 33.36277  ,  25.43809  ,  25.43809  ],
                 [ 28.95689  ,  29.62332  ,  29.62332  ],
                 [ 24.81337  ,  34.06845  ,  34.06845  ],
                 [ 20.94748  ,  38.75704  ,  38.75704  ],
                 [ 17.37353  ,  43.6718   ,  43.6718   ],
                 [ 14.1047   ,  48.79456  ,  48.79456  ],
                 [ 11.15306  ,  54.10642  ,  54.10642  ],
                 [  8.529518 ,  59.58774  ,  59.58774  ],
                 [  6.243754 ,  65.21831  ,  65.21831  ],
                 [  4.304215 ,  70.97732  ,  70.97732  ],
                 [  2.718057 ,  76.84349  ,  76.84349  ],
                 [  1.491148 ,  82.79519  ,  82.79519  ],
                 [  0.6280057,  88.81041  ,  88.81041  ],
                 [  0.1571253,  94.3964   ,  94.3964   ],
                 [  0.       , 100.       ,   0.       ],
                 [100.       , 100.       ,   0.       ],
                 [ 87.87437  , 100.       ,   0.7378794],
                 [ 75.92768  , 100.       ,   2.940621 ],
                 [ 64.33623  , 100.       ,   6.575722 ],
                 [ 53.2711   , 100.       ,  11.58954  ],
                 [ 42.89558  , 100.       ,  17.90807  ],
                 [ 33.36277  , 100.       ,  25.43809  ],
                 [ 24.81337  , 100.       ,  34.06845  ],
                 [ 17.37353  , 100.       ,  43.6718   ],
                 [ 11.15306  , 100.       ,  54.10642  ],
                 [  6.243754 , 100.       ,  65.21831  ],
                 [  2.718057 , 100.       ,  76.84349  ],
                 [  0.6280057, 100.       ,  88.81041  ]], dtype=float)

faces = np.array([ 3,  0,  1,  2,  3,  0,  2,  3,  3,  1,  4,  2,  3,  2,  4,  5,  3,
        2,  5,  6,  3,  7,  8,  2,  3,  2,  8,  9,  3,  2,  9,  3,  3, 10,
       11,  2,  3,  2, 11, 12,  3,  2, 12,  7,  3, 13, 14,  2,  3,  2, 14,
       15,  3,  2, 15, 10,  3, 16, 17,  2,  3,  2, 17, 18,  3,  2, 18, 13,
        3, 19, 20,  2,  3,  2, 20, 21,  3,  2, 21, 16,  3, 22, 23,  2,  3,
        2, 23, 24,  3,  2, 24, 19,  3, 25, 26,  2,  3,  2, 26, 27,  3,  2,
       27, 22,  3, 25,  2, 28,  3,  2,  6, 29,  3, 29, 30,  2,  3,  2, 30,
       31,  3,  2, 31, 32,  3, 32, 33,  2,  3,  2, 33, 34,  3,  2, 34, 35,
        3, 35, 36,  2,  3,  2, 36, 37,  3,  2, 37, 38,  3, 38, 39,  2,  3,
        2, 39, 40,  3,  2, 40, 28,  3, 40, 27, 28,  3, 28, 27, 26,  3, 28,
       26, 25,  3, 39, 38, 19,  3, 19, 24, 39,  3, 39, 24, 23,  3, 39, 23,
       40,  3, 40, 23, 22,  3, 40, 22, 27,  3, 37, 36, 17,  3, 17, 16, 37,
        3, 37, 16, 21,  3, 37, 21, 38,  3, 38, 21, 20,  3, 38, 20, 19,  3,
       35, 34, 15,  3, 15, 14, 35,  3, 35, 14, 13,  3, 35, 13, 36,  3, 36,
       13, 18,  3, 36, 18, 17,  3, 33, 32,  7,  3,  7, 12, 33,  3, 33, 12,
       11,  3, 33, 11, 34,  3, 34, 11, 10,  3, 34, 10, 15,  3, 31, 30,  0,
        3,  0,  3, 31,  3, 31,  3,  9,  3, 31,  9, 32,  3, 32,  9,  8,  3,
       32,  8,  7,  3,  6,  5, 29,  3, 29,  5,  4,  3, 29,  4, 30,  3, 30,
        4,  1,  3, 30,  1,  0,  3, 41,  2, 42,  3, 42,  2,  6,  3, 41, 43,
        2,  3,  2, 43, 44,  3,  2, 44, 45,  3, 45, 46,  2,  3,  2, 46, 47,
        3,  2, 47, 48,  3, 48, 49,  2,  3,  2, 49, 50,  3,  2, 50, 51,  3,
       51, 52,  2,  3,  2, 52, 53,  3,  2, 53, 54,  3, 54, 55,  2,  3,  2,
       55, 56,  3,  2, 56, 57,  3, 57, 58,  2,  3,  2, 58, 59,  3,  2, 59,
       60,  3, 60, 61,  2,  3,  2, 61, 62,  3,  2, 62, 63,  3, 63, 64,  2,
        3,  2, 64, 65,  3,  2, 65, 25,  3, 25, 66,  2,  3,  6, 67, 42,  3,
       42, 67, 68,  3, 42, 68, 41,  3, 41, 68, 43,  3, 43, 68, 69,  3, 43,
       69, 44,  3, 44, 69, 45,  3, 45, 69, 70,  3, 45, 70, 46,  3, 46, 70,
       47,  3, 47, 70, 71,  3, 47, 71, 48,  3, 48, 71, 49,  3, 49, 71, 72,
        3, 49, 72, 50,  3, 73, 74, 54,  3, 54, 53, 73,  3, 73, 53, 52,  3,
       73, 52, 72,  3, 72, 52, 51,  3, 72, 51, 50,  3, 75, 76, 58,  3, 58,
       57, 75,  3, 75, 57, 56,  3, 75, 56, 74,  3, 74, 56, 55,  3, 74, 55,
       54,  3, 77, 78, 62,  3, 62, 61, 77,  3, 77, 61, 60,  3, 77, 60, 76,
        3, 76, 60, 59,  3, 76, 59, 58,  3, 25, 65, 79,  3, 79, 65, 64,  3,
       79, 64, 78,  3, 78, 64, 63,  3, 78, 63, 62,  3, 66, 25, 79,  3, 79,
       78, 66,  3, 66, 78, 77,  3, 66, 77, 76,  3, 76, 75, 66,  3, 66, 75,
       74,  3, 66, 74, 73,  3, 73, 72, 66,  3, 66, 72, 71,  3, 66, 71, 70,
        3, 70, 69, 66,  3, 66, 69, 68,  3, 66, 68, 67,  3,  2, 66,  6,  3,
        6, 66, 67], dtype=int)


mesh = pv.PolyData(points, faces)
mesh.rotate_x(90, inplace=True)
mesh.rotate_z(90, inplace=True)
tet = tetgen.TetGen(mesh)
meshfix = pymeshfix.MeshFix(tet.v, tet.f)
holes = meshfix.extract_holes()
meshfix.repair()
tet.v, tet.f = meshfix.v, meshfix.f
assert(tet.tetrahedralize())

p = pv.Plotter()
p.add_mesh(mesh, style="wireframe", color="k", label="Original Surface")
p.add_mesh(holes, color='r', label="Holes")
p.add_mesh(meshfix.mesh, label="Repaired surface")
p.add_legend()
p.show()

Solution

  • I ended up writing a library that can extract enclosed surfaces of a mesh, among a few other useful things. Documentation is available here: https://acreegan.github.io/pyvista_tools/

    Below is a plot showing a surface mesh before and after identifying enclosed regions.

    enter image description here