def totalMass(self): points = [[-self.a, -self.a, -self.a], [-self.a, -self.a, self.a], [-self.a, self.a, -self.a], [-self.a, self.a, self.a], [self.a, -self.a, -self.a], [self.a, -self.a, self.a], [self.a, self.a, -self.a], [self.a, self.a, self.a]] facets = [[0, 1, 3, 2], [2, 3, 7, 6], [4, 5, 7, 6], [0, 1, 5, 4], [0, 2, 6, 4], [1, 3, 7, 5]] baseTetra = scipy.spatial.Delaunay(points).simplices triangles = [] for facet in facets: triangleMesh = pymesh.triangle() triangleMesh.points = np.array(points)[facet] triangleMesh.verbosity = 0 triangleMesh.max_num_steiner_points = 0 triangleMesh.run() triangles.append(np.array(facet)[triangleMesh.mesh.faces]) triangles = np.vstack(triangles) tetgen = pymesh.tetgen() tetgen.points = points tetgen.triangles = triangles tetgen.tetrahedra = baseTetra tetgen.verbosity = 0 tetgen.max_tet_volume = 0.05 tetgen.run() mesh = tetgen.mesh tetra = np.array(mesh.vertices)[np.array(mesh.elements)].transpose( [1, 0, 2]) scheme = quadpy.t3._keast.keast_9() integrals = scheme.integrate(partial(self.rho, i=0), tetra) return integrals.sum()
def test_cube_refine(self): vertices = np.array([[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [1.0, 1.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0, 1.0], [1.0, 1.0, 1.0], [0.0, 1.0, 1.0]]) # A cube can be splitted into 5 tets or 6 tets without adding steiner # points. We start with 5 tets and see if tetgen can refine it to the 6 # tet configuration. tets = np.array([[0, 1, 2, 5], [0, 2, 3, 7], [4, 7, 5, 0], [5, 7, 6, 2], [0, 5, 2, 7]], dtype=int) mesh = pymesh.form_mesh(vertices, np.array([]), tets) mesh.add_attribute("voxel_volume") volumes = mesh.get_attribute("voxel_volume") self.assertAlmostEqual(1.0, np.sum(volumes)) tetgen = pymesh.tetgen() tetgen.points = mesh.vertices tetgen.triangles = mesh.faces tetgen.tetrahedra = mesh.voxels tetgen.max_tet_volume = 0.17 tetgen.max_num_steiner_points = 0 tetgen.split_boundary = False tetgen.verbosity = 0 tetgen.run() mesh = tetgen.mesh mesh.add_attribute("voxel_volume") volumes = mesh.get_attribute("voxel_volume") self.assertAlmostEqual(1.0, np.sum(volumes)) # No tetgen cannot recover the 6 tet configuration... # So tet_volume constaint is actually not satisfied by the result. self.assertEqual(8, len(tetgen.vertices)) self.assertEqual(5, len(tetgen.voxels))
def test_weighted(self): tetgen = pymesh.tetgen() tetgen.points = np.array([[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0], [0.1, 0.1, 0.1]]) tetgen.point_weights = np.array([1.0, 1.0, 1.0, 1.0, 0.0]) tetgen.verbosity = 0 tetgen.weighted_delaunay = True tetgen.run() self.assertEqual(5, len(tetgen.vertices)) self.assertEqual(1, len(tetgen.voxels)) # Rerun but not weighted. tetgen.verbosity = 0 tetgen.weighted_delaunay = False tetgen.run() self.assertEqual(5, len(tetgen.vertices)) self.assertEqual(4, len(tetgen.voxels)) # Rerun with 0 weights. tetgen.point_weights = np.array([0.0, 0.0, 0.0, 0.0, 0.0]) tetgen.verbosity = 0 tetgen.weighted_delaunay = True tetgen.run() self.assertEqual(5, len(tetgen.vertices)) self.assertEqual(4, len(tetgen.voxels))
def test_volume_constraint(self): tetgen = pymesh.tetgen() tetgen.points = np.array([[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [1.0, 1.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0, 1.0], [1.0, 1.0, 1.0], [0.0, 1.0, 1.0]]) tetgen.triangles = np.array([ [0, 2, 1], [0, 3, 2], [5, 6, 7], [5, 7, 4], [1, 2, 5], [2, 6, 5], [0, 4, 3], [3, 4, 7], [0, 1, 5], [0, 5, 4], [2, 3, 6], [3, 7, 6], ]) tetgen.verbosity = 0 tetgen.keep_convex_hull = True tetgen.split_boundary = True tetgen.max_tet_volume = 0.1 tetgen.run() mesh = tetgen.mesh mesh.add_attribute("voxel_volume") volumes = mesh.get_attribute("voxel_volume") self.assertLess(8, len(tetgen.vertices)) self.assertLess(6, len(tetgen.voxels)) self.assertAlmostEqual(1.0, np.sum(volumes))
def test_linear_function(self): mesh = pymesh.generate_icosphere(1.0, np.zeros(3), 2) tetgen = pymesh.tetgen() tetgen.points = mesh.vertices tetgen.triangles = mesh.faces tetgen.max_tet_volume = 0.1 tetgen.verbosity = 0 tetgen.run() mesh = tetgen.mesh self.assertLess(0, mesh.num_voxels) # Test solver to reproduce linear coordinate functions. solver = pymesh.HarmonicSolver.create(mesh) bd_indices = np.unique(mesh.faces.ravel()) bd_values = mesh.vertices[bd_indices, 0] solver.boundary_indices = bd_indices solver.boundary_values = bd_values solver.pre_process() solver.solve() sol = solver.solution.ravel() self.assertEqual(mesh.num_vertices, len(sol)) self.assert_array_almost_equal(mesh.vertices[:, 0], sol, 12)
def generateMesh(n_points): points = [] # Simple 2D for initial testing for i in range(n_points): r = random.gauss(10,0.05) theta = random.vonmisesvariate(math.pi,0) phi = random.vonmisesvariate(math.pi, 0) x= r * math.sin(theta) * math.cos(phi) y= r * math.sin(theta) * math.sin(phi) z= r * math.cos(theta) points.append([x,y,z]) # Now just to check we have the target number of unique points nppoints = np.asarray(points) meshgen = pymesh.tetgen(); meshgen.points = nppoints; meshgen.merge_coplanar = True meshgen.coarsening = False meshgen.verbosity = 0 meshgen.run() mesh = pymesh.subdivide(meshgen.mesh, order = 5, method="loop") mesh = meshgen.mesh return(mesh)
def test_weighted(self): tetgen = pymesh.tetgen(); tetgen.points = np.array([ [0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0], [0.1, 0.1, 0.1] ]); tetgen.point_weights = np.array([1.0, 1.0, 1.0, 1.0, 0.0]); tetgen.verbosity = 0; tetgen.weighted_delaunay = True; tetgen.run(); self.assertEqual(5, len(tetgen.vertices)); self.assertEqual(1, len(tetgen.voxels)); # Rerun but not weighted. tetgen.verbosity = 0; tetgen.weighted_delaunay = False; tetgen.run(); self.assertEqual(5, len(tetgen.vertices)); self.assertEqual(4, len(tetgen.voxels)); # Rerun with 0 weights. tetgen.point_weights = np.array([0.0, 0.0, 0.0, 0.0, 0.0]); tetgen.verbosity = 0; tetgen.weighted_delaunay = True; tetgen.run(); self.assertEqual(5, len(tetgen.vertices)); self.assertEqual(4, len(tetgen.voxels));
def test_radial_function(self): mesh = pymesh.generate_icosphere(1.0, [1.0, 1.0, 1.0], 2) tetgen = pymesh.tetgen() tetgen.points = mesh.vertices tetgen.triangles = mesh.faces tetgen.max_tet_volume = 0.001 tetgen.verbosity = 0 tetgen.run() mesh = tetgen.mesh self.assertLess(0, mesh.num_voxels) # Test solver to reproduce linear coordinate functions. solver = pymesh.HarmonicSolver.create(mesh) bd_indices = np.unique(mesh.faces.ravel()) target_solution = 1.0 / numpy.linalg.norm(mesh.vertices, axis=1) bd_values = target_solution[bd_indices] solver.boundary_indices = bd_indices solver.boundary_values = bd_values solver.order = 1 solver.pre_process() solver.solve() sol = solver.solution.ravel() #mesh.add_attribute("target_solution"); #mesh.set_attribute("target_solution", target_solution); #mesh.add_attribute("solution"); #mesh.set_attribute("solution", sol); #pymesh.save_mesh("tmp.msh", mesh, *mesh.attribute_names); self.assertEqual(mesh.num_vertices, len(sol)) self.assert_array_almost_equal(target_solution, sol, 2)
def main(): args = parse_args(); mesh = pymesh.load_mesh(args.in_mesh); if (args.auto_size): bbox_min, bbox_max = mesh.bbox; target_edge_len = np.sum(bbox_max - bbox_min) / 30.0 * 4.0 / 3.0; target_volume = target_edge_len ** 3 * math.sqrt(2) / 12.0; flags = "{}a{}".format(args.flags, target_volume); else: flags = args.flags; tet_mesh = pymesh.tetgen(mesh, flags); pymesh.save_mesh(args.out_mesh, tet_mesh);
def test_point_cloud(self): tetgen = pymesh.tetgen() tetgen.points = np.array([[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [1.0, 1.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0, 1.0], [1.0, 1.0, 1.0], [0.0, 1.0, 1.0]]) tetgen.verbosity = 0 tetgen.keep_convex_hull = True tetgen.split_boundary = False tetgen.run() mesh = tetgen.mesh mesh.add_attribute("voxel_volume") volumes = mesh.get_attribute("voxel_volume") self.assertEqual(8, len(tetgen.vertices)) self.assertAlmostEqual(1.0, np.sum(volumes))
def test_cube_refine(self): vertices = np.array([ [0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [1.0, 1.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0, 1.0], [1.0, 1.0, 1.0], [0.0, 1.0, 1.0] ]); # A cube can be splitted into 5 tets or 6 tets without adding steiner # points. We start with 5 tets and see if tetgen can refine it to the 6 # tet configuration. tets = np.array([ [0, 1, 2, 5], [0, 2, 3, 7], [4, 7, 5, 0], [5, 7, 6, 2], [0, 5, 2, 7] ], dtype=int); mesh = pymesh.form_mesh(vertices, np.array([]), tets); mesh.add_attribute("voxel_volume"); volumes = mesh.get_attribute("voxel_volume"); self.assertAlmostEqual(1.0, np.sum(volumes)); tetgen = pymesh.tetgen(); tetgen.points = mesh.vertices; tetgen.triangles = mesh.faces; tetgen.tetrahedra = mesh.voxels; tetgen.max_tet_volume = 0.17; tetgen.max_num_steiner_points = 0; tetgen.split_boundary = False; tetgen.verbosity = 0; tetgen.run(); mesh = tetgen.mesh; mesh.add_attribute("voxel_volume"); volumes = mesh.get_attribute("voxel_volume"); self.assertAlmostEqual(1.0, np.sum(volumes)); # No tetgen cannot recover the 6 tet configuration... # So tet_volume constaint is actually not satisfied by the result. self.assertEqual(8, len(tetgen.vertices)); self.assertEqual(5, len(tetgen.voxels));
def test_point_cloud(self): tetgen = pymesh.tetgen(); tetgen.points = np.array([ [0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [1.0, 1.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0, 1.0], [1.0, 1.0, 1.0], [0.0, 1.0, 1.0] ]); tetgen.verbosity = 0; tetgen.keep_convex_hull = True; tetgen.split_boundary = False; tetgen.run(); mesh = tetgen.mesh; mesh.add_attribute("voxel_volume"); volumes = mesh.get_attribute("voxel_volume"); self.assertEqual(8, len(tetgen.vertices)); self.assertAlmostEqual(1.0, np.sum(volumes));
def integrateOverCell(self, tup, f, scheme=quadpy.t3._keast.keast_9()): i, tup2 = tup vertices, facets = tup2 if (vertices == np.array(None)).all(): if f(self.X[0].reshape(3, 1, 1), 0).shape == (1, 1): return float(0) else: return np.zeros(3) tetrahedrons = scipy.spatial.Delaunay(vertices).simplices volumes = self.tetraVolume(vertices[tetrahedrons]) goodTetra = tetrahedrons[ np.logical_not(np.isclose(volumes, 0, atol=1e-11, rtol=1.e-5)), :] triangles = [] for facet in facets: triangleMesh = pymesh.triangle() triangleMesh.verbosity = 0 triangleMesh.points = np.array(vertices)[facet] triangleMesh.max_num_steiner_points = 0 triangleMesh.run() triangles.append(np.array(facet)[triangleMesh.mesh.faces]) triangles = np.vstack(triangles) tetgen = pymesh.tetgen() tetgen.points = vertices tetgen.verbosity = 0 tetgen.triangles = triangles tetgen.max_tet_volume = 0.05 tetgen.tetrahedra = goodTetra tetgen.run() mesh = tetgen.mesh tetra = np.array(mesh.vertices)[np.array(mesh.elements)] volumes = self.tetraVolume(tetra) goodTetra = tetra[ np.logical_not(np.isclose(volumes, 0, atol=1e-8, rtol=1e-8) ), :].transpose([1, 0, 2]) integrals = scheme.integrate(partial(f, i=i), goodTetra) if len(integrals.shape) == 2: return integrals.sum(1) return integrals.sum(0)
def test_volume_constraint(self): tetgen = pymesh.tetgen(); tetgen.points = np.array([ [0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [1.0, 1.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0, 1.0], [1.0, 1.0, 1.0], [0.0, 1.0, 1.0] ]); tetgen.triangles = np.array([ [0, 2, 1], [0, 3, 2], [5, 6, 7], [5, 7, 4], [1, 2, 5], [2, 6, 5], [0, 4, 3], [3, 4, 7], [0, 1, 5], [0, 5, 4], [2, 3, 6], [3, 7, 6], ]); tetgen.verbosity = 0; tetgen.keep_convex_hull = True; tetgen.split_boundary = True; tetgen.max_tet_volume = 0.1; tetgen.run(); mesh = tetgen.mesh; mesh.add_attribute("voxel_volume"); volumes = mesh.get_attribute("voxel_volume"); self.assertLess(8, len(tetgen.vertices)); self.assertLess(6, len(tetgen.voxels)); self.assertAlmostEqual(1.0, np.sum(volumes));
def main(tetra_path, savedir, create_adjlists, adjnodes, rates): print "######### Tetrahedralize #########" mesh = pymesh.load_mesh(tetra_path) mesh.add_attribute("vertex_normal") mesh.get_attribute("vertex_normal") mesh.get_vertex_attribute("vertex_normal") #some meshes may have self intersection so tetgen cannot deal with it. #Before tetrahedralization here is the self intersection cleanup self_mesh = pymesh.resolve_self_intersection(mesh, engine='auto') self_mesh.add_attribute("vertex_normal") self_mesh.get_attribute("vertex_normal") self_mesh.get_vertex_attribute("vertex_normal") print("Starting Tetgen.............................") tetgen = pymesh.tetgen() tetgen.points = self_mesh.vertices tetgen.triangles = self_mesh.faces # tetgen.max_tet_volume = 0.000001 #Very detailed tetgen.max_tet_volume = 0.0000005 #Very detailed tetgen.verbosity = 0 tetgen.run() outmesh = tetgen.mesh print(outmesh.num_vertices, outmesh.num_faces, outmesh.num_voxels) print(outmesh.dim, outmesh.vertex_per_face, outmesh.vertex_per_voxel) # Save tetrahedral mesh if not os.path.exists(savedir): os.makedirs(savedir) pymesh.meshio.save_mesh(savedir + "/CoarseTetra.ply", outmesh) # Option: Create adjlists for tetranet if create_adjlists: print "#########Create adjlists#########" # Reduce vertices for full connection coarse1 = reduce_points(outmesh.vertices, rates[0]) utils.save_points(coarse1, savedir + "/coarse1.ply") coarse2 = reduce_points(coarse1, rates[1]) utils.save_points(coarse2, savedir + "/coarse2.ply") coarse3 = reduce_points(coarse2, rates[2]) utils.save_points(coarse3, savedir + "/coarse3.ply") coarse4 = reduce_points(coarse3, rates[3]) utils.save_points(coarse4, savedir + "/coarse4.ply") print("Generate node adj list") # Note: utils.make_adjlist returns a list of n nearest nodes (L2 norm) print "Node reduction rates: ", print(rates) n = adjnodes print("Adjacent Nodes: " + str(n)) # For partial connection print("coarse 4 to 3: {} -> {}".format(len(coarse4), len(coarse3))) adjlist = utils.make_adjlist(coarse4, coarse3, n, True) utils.list2csv(adjlist, savedir + "/adjlist_4to3.csv") print("coarse 3 to 2: {} -> {}".format(len(coarse3), len(coarse2))) adjlist = utils.make_adjlist(coarse3, coarse2, n, True) utils.list2csv(adjlist, savedir + "/adjlist_3to2.csv") print("coarse 2 to 1: {} -> {}".format(len(coarse2), len(coarse1))) adjlist = utils.make_adjlist(coarse2, coarse1, n, True) utils.list2csv(adjlist, savedir + "/adjlist_2to1.csv") print("coarse 1 to original: {} -> {}".format(len(coarse1), len(outmesh.vertices))) adjlist = utils.make_adjlist(coarse1, outmesh.vertices, n, True) utils.list2csv(adjlist, savedir + "/adjlist_1to0.csv")
def process_obj(obj_file, dir_in, dir_out, grid_size, max_faces, make_convex=False, fit_cylinder=False, quality='low'): mesh_path = join(dir_in, obj_file) print('---------------------------------------------------------------------------') print('Processing: ' + obj_file) print('---------------------------------------------------------------------------') # load the mesh print('Loading mesh...') mesh = pymesh.load_mesh(mesh_path) print('Mesh verts: ' + str(mesh.vertices.shape)) print('Mesh faces: ' + str(mesh.faces.shape)) # filter out those that have too many faces if (mesh.faces.shape[0] > max_faces): print('Over max faces...continuing to next shape!') return # cleanup surf_mesh = mesh if make_convex: # first tet to get just outer surface print('Tetrahedralizing mesh...') tetgen = pymesh.tetgen() tetgen.points = mesh.vertices; # Input points tetgen.triangles = np.empty(0) tetgen.verbosity = 0 tetgen.run(); # Execute tetgen surf_mesh = tetgen.mesh elif fit_cylinder: # same top and bottom radius # want to center around origin # first find radius (max of all x and z) # print (mesh.bbox) # print(mesh.bbox[0][0]) cyl_rad = max([abs(mesh.bbox[0][0]), abs(mesh.bbox[1][0]), abs(mesh.bbox[0][2]), abs(mesh.bbox[1][2])]) # find height max y - min y cyl_half_height = (mesh.bbox[1][1] - mesh.bbox[0][1]) / 2.0 surf_mesh = pymesh.generate_cylinder(np.array([0, -cyl_half_height, 0]), np.array([0, cyl_half_height, 0]), cyl_rad, cyl_rad) # print('Tet verts: ' + str(tet_mesh.vertices.shape)) # print('Tet faces: ' + str(tet_mesh.faces.shape)) # remesh to improve vertex distribution for moment calculation # print('Remeshing...') # surf_mesh = tet_mesh #fix_mesh(tet_mesh, quality) print('Surface verts: ' + str(surf_mesh.vertices.shape)) print('Surface faces: ' + str(surf_mesh.faces.shape)) # voxelize to find volume print('Voxelizing...') grid = pymesh.VoxelGrid(grid_size, surf_mesh.dim) grid.insert_mesh(surf_mesh) grid.create_grid() vox_mesh = grid.mesh # save sim information # the number of voxels will be used for the volume (mass) num_voxels = vox_mesh.voxels.shape[0] vol = num_voxels * grid_size*grid_size*grid_size # move mesh to true COM based on voxelization (centroid of the center of all voxels) centroid = np.array([0., 0., 0.]) for i in range(0, num_voxels): # find average position of all vertices defining voxel vox_pos = np.mean(vox_mesh.vertices[vox_mesh.voxels[i], :], axis=0) centroid += vox_pos centroid /= num_voxels print('Centroid: (%f, %f, %f)' % (centroid[0], centroid[1], centroid[2])) centroid_vox_mesh = pymesh.form_mesh(vox_mesh.vertices - np.reshape(centroid, (1, -1)), vox_mesh.faces, vox_mesh.voxels) centroid_surf_mesh = pymesh.form_mesh(surf_mesh.vertices - np.reshape(centroid, (1, -1)), surf_mesh.faces) # also calculate moment of inertia around principal axes for a DENSITY of 1 (mass = volume) inertia = np.array([0., 0., 0.]) point_mass = vol / float(num_voxels) print('Point mass: %f' % (point_mass)) for i in range(0, num_voxels): # find average position of all vertices defining voxel vox_pos = np.mean(centroid_vox_mesh.vertices[centroid_vox_mesh.voxels[i], :], axis=0) x2 = vox_pos[0]*vox_pos[0] y2 = vox_pos[1]*vox_pos[1] z2 = vox_pos[2]*vox_pos[2] # inertia += np.array([point_mass*(y2+z2),point_mass*(x2+z2),point_mass*(x2+y2)]) inertia += np.array([x2*point_mass,y2*point_mass,z2*point_mass]) print('Num voxels: ' + str(num_voxels)) print('Volume: ' + str(vol)) print('Moment of inertia: (%f, %f, %f)' % (inertia[0], inertia[1], inertia[2])) json_dict = {'num_vox' : num_voxels, 'vol' : vol, 'inertia' : inertia.tolist()} json_out_path = join(dir_out, obj_file.replace('.obj', '.json')) with open(json_out_path, 'w') as outfile: json_str = json.dump(json_dict, outfile) # save surface mesh mesh_out_path = join(dir_out, obj_file) pymesh.save_mesh(mesh_out_path, centroid_surf_mesh) # sample point cloud on surface mesh print('Sampling point cloud...') points_out_path = join(dir_out, obj_file.replace('.obj', '.pts')) subprocess.check_output(['./MeshSample', '-n1024', '-s3', mesh_out_path, points_out_path])