Beispiel #1
0
 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()
Beispiel #2
0
    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))
Beispiel #3
0
    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))
Beispiel #4
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 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)
Beispiel #6
0
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)
Beispiel #7
0
    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)
Beispiel #9
0
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);
Beispiel #10
0
    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))
Beispiel #11
0
    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));
Beispiel #12
0
    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));
Beispiel #13
0
 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)
Beispiel #14
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));
Beispiel #15
0
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])