def test_close_mesh(self): mesh_a = self.cutSphere_C.copy() boundary_prev = stop.mesh_boundary(mesh_a) # Correctness assert (len(boundary_prev) == 2) mesh_a_closed, nvertices_added = stop.close_mesh(mesh_a) # Coherence assert (len(mesh_a_closed.vertices) == len(mesh_a.vertices) + nvertices_added) boundary_closed = stop.mesh_boundary(mesh_a_closed) # Mesh is now closed assert (len(boundary_closed) == 0) # Non modification of the vertices which are not on the boundaries sbase = (set(range(len(mesh_a.vertices)))) sbound1 = (set(boundary_prev[0])) sbound2 = (set(boundary_prev[1])) sbound = sbound1.union(sbound2) snot_bound = (sbase.difference(sbound)) for i in snot_bound: assert (mesh_a.vertices[i] == mesh_a_closed.vertices[i]).all()
def test_boundaries_triangles(self): mesh_a = distinct_triangles(5) mesh_b = random_triangles(5) open_mesh_boundary_a = stop.mesh_boundary(mesh_a) open_mesh_boundary_b = stop.mesh_boundary(mesh_b) iterable_a = list(itertools.combinations(open_mesh_boundary_a, 2)) iterable_b = list(itertools.combinations(open_mesh_boundary_b, 2)) # Uniqueness for b1, b2 in iterable_a: assert set(b1).intersection(set(b2)) == set() for b1, b2 in iterable_b: assert set(b1).intersection(set(b2)) == set()
def disk_conformal_mapping(mesh, lap_type='conformal', boundary=None, boundary_coords=None): """ Computes comformal mapping of a mesh to a disk, see the following references: Ulrich Pinkall and Konrad Polthier, “Computing Discrete Minimal Surfaces and Their Conjugates,” Experimental Mathematics, 1993, 1–33. and Mathieu Desbrun, Mark Meyer, and Pierre Alliez, “Intrinsic Parameterizations of Surface Meshes,” Computer Graphics Forum 21, no. 3 (2002): 209–18, https://doi.org/10.1111/1467-8659.00580. :param mesh: a trimesh object :param lap_type: type of mesh Laplacian to be used, see the function differential_geometry/compute_mesh_weights for more informations :param boundary: boundary of the mesh, resulting from the function topology/mesh_boundary :param boundary_coords: coordindates of the boundary vertices on the output disk, if None then uniform sampling :return: a trimesh object, planar disk representation of the input mesh """ if boundary is None: boundary_t = stop.mesh_boundary(mesh) boundary = boundary_t[0] boundary = np.array(boundary) if boundary_coords is None: p = boundary.size t = np.arange(0, 2 * np.math.pi, (2 * np.math.pi / p)) boundary_coords = np.array([np.cos(t), np.sin(t)]) L, LB = sdg.compute_mesh_laplacian(mesh, lap_type=lap_type) Nv = len(mesh.vertices) # np.array(mesh.vertex()).shape[0] print('Boundary Size:', boundary.shape) print('Laplacian Size:', L.shape) for i in boundary: L[i, :] = 0 L[i, i] = 1 L = L.tocsr() Rx = np.zeros(Nv) Ry = np.zeros(Nv) Rx[boundary] = boundary_coords[0, :] Ry[boundary] = boundary_coords[1, :] x, info = lgmres(L, Rx, tol=solver_tolerance) y, info = lgmres(L, Ry, tol=solver_tolerance) z = np.zeros(Nv) return trimesh.Trimesh(faces=mesh.faces, vertices=np.array([x, y, z]).T, metadata=mesh.metadata, process=False)
def test_remove_mesh_boundary_faces(self): mesh_a = self.cutSphere_A.copy() mesh_a_save = mesh_a.copy() boundary = stop.mesh_boundary(mesh_a) mesh_processed = stop.remove_mesh_boundary_faces(mesh_a, face_vertex_number=1) # Non modification assert (mesh_a.vertices == mesh_a_save.vertices).all() # Correctness # check that when removing all faces with any vertex on the boundary, # all the boundary vertices are removed from the mesh print(len(boundary[0])) print(len(mesh_a.vertices)) print(len(mesh_processed.vertices)) assert (len(mesh_processed.vertices) == len(mesh_a.vertices) - len(boundary[0]))
def disk_conformal_mapping(mesh, lap_type='conformal', boundary=None, boundary_coords=None): """ compute comformal mapping of a mesh to a disk ADD ref :param mesh: a trimesh object :param lap_type: type of mesh Laplacian to be used, see the function differential_geometry/compute_mesh_weights for more informations :param boundary: boundary of the mesh, resulting from the function topology/mesh_boundary :param boundary_coords: coordindates of the boundary vertices on the output disk, if None then uniform sampling :return: a trimesh object, planar disk representation of the input mesh """ if boundary is None: boundary_t = stop.mesh_boundary(mesh) boundary = boundary_t[0] boundary = np.array(boundary) if boundary_coords is None: p = boundary.size t = np.arange(0, 2 * np.math.pi, (2 * np.math.pi / p)) boundary_coords = np.array([np.cos(t), np.sin(t)]) L, LB = sdg.compute_mesh_laplacian(mesh, lap_type=lap_type) Nv = len(mesh.vertices) # np.array(mesh.vertex()).shape[0] print('Boundary Size:', boundary.shape) print('Laplacian Size:', L.shape) for i in boundary: L[i, :] = 0 L[i, i] = 1 L = L.tocsr() Rx = np.zeros(Nv) Ry = np.zeros(Nv) Rx[boundary] = boundary_coords[0, :] Ry[boundary] = boundary_coords[1, :] x, info = lgmres(L, Rx, tol=solver_tolerance) y, info = lgmres(L, Ry, tol=solver_tolerance) z = np.zeros(Nv) return trimesh.Trimesh(faces=mesh.faces, vertices=np.array([x, y, z]).T, metadata=mesh.metadata, process=False)
def test_boundaries_concat_copy(self): mesh_a = self.cutSphere_A boundary = stop.mesh_boundary(mesh_a) b1_save = boundary[0].copy() b1 = boundary[0] b2 = boundary[1] concat = stop.cat_boundary(b1, b2) # Non modification # assert(len(b1) == len(b1_save)) # assert((b1==b1_save)) inter_1 = stop.boundaries_intersection([concat, b2]) inter_1 = inter_1[0][2] assert (set(inter_1) == set(b2))
def test_angle_norm_boundary(self): ### CREATE A DISK IN 3D radius = 2 mesh_a = trimesh.path.creation.circle(radius) coords2D, faces = mesh_a.triangulate() coords3D = [] for i, v in enumerate(coords2D): coords3D.append([v[0], v[1], 0]) coords3D = np.array(coords3D) mesh_a = trimesh.Trimesh(faces=faces, vertices=coords3D, process=False) ### COMPUTE BOUNDARY boundary = stop.mesh_boundary(mesh_a) assert (len(boundary) == 1) ### COMPUTE ANGLES AND NORM VARIATIONS angles, norms = stop.boundary_angles(boundary[0], coords3D) angles = abs(180 - angles) sum_angles, sum_norms = sum(angles), sum(norms) three_angles, three_norms = stop.boundary_angles([0, 1, 2], coords3D) three_angles = abs(180 - three_angles) perimeter = radius * np.pi * 2 precision_A = .001 precision_B = .001 assert (np.isclose(three_norms[1], perimeter / len(coords3D), precision_A)) assert (np.isclose(three_angles[1], 360 / len(coords3D), precision_B)) assert (np.isclose(sum_angles, 360, precision_A)) assert (np.isclose(sum_norms, perimeter, precision_B))
def test_boundaries_intersection_copy(self): mesh_a = self.cutSphere_A boundary = stop.mesh_boundary(mesh_a) b1 = boundary[0] b2 = boundary[1] b2_save = b2.copy() concat = b1 + b2 inter_1 = stop.boundaries_intersection([concat, b2]) inter_1 = inter_1[0][2] # Non modification assert (b2 == b2_save) # Type assert (isinstance(inter_1, list)) # Correctness assert (set(inter_1) == set(b2))
def test_angle_norm_boundary(self): # GET A DISK IN 3D radius = 2 mesh_a = self.disk_radius_2 coords3D = mesh_a.vertices # COMPUTE BOUNDARY boundary = stop.mesh_boundary(mesh_a) # Correctness assert (len(boundary) == 1) # COMPUTE ANGLE AND NORM VARIATIONS angles, norms = stop.boundary_angles(boundary[0], coords3D) angles = abs(180 - angles) sum_angles, sum_norms = sum(angles), sum(norms) # Computation on three arbitrary linked vertices three_angles, three_norms = stop.boundary_angles([0, 1, 2], coords3D) three_angles = abs(180 - three_angles) perimeter = radius * np.pi * 2 precision_A = .001 precision_B = .001 # Coherence of the angle sum and norm sum assert (np.isclose(sum_angles, 360, precision_A)) assert (np.isclose(sum_norms, perimeter, precision_B)) # Coherence of a partial angle and a partial norm variation assert (np.isclose(three_angles[1], 360 / len(coords3D), precision_A)) assert (np.isclose(three_norms[1], perimeter / len(coords3D), precision_B))
def test_boundaries_basic(self): mesh_a = self.cutSphere_A mesh_a_save = mesh_a.copy() boundary = stop.mesh_boundary(mesh_a) # Non modification assert (mesh_a.vertices == mesh_a_save.vertices).all() assert (mesh_a.faces == mesh_a_save.faces).all() # Correctness assert (len(boundary) == 2) # Type assert (isinstance(boundary, list)) assert (isinstance(boundary[0], list)) iterable = list(itertools.combinations(boundary, 2)) # Uniqueness for b1, b2 in iterable: assert set(b1).intersection(set(b2)) == set()
import slam.io as sio import slam.topology as stop # import slam.plot as splt import slam.generate_parametric_surfaces as sps import numpy as np if __name__ == '__main__': # here is how to get the vertices that define the boundary of an open mesh Ks = [[1, 1]] X, Y, faces, Zs = sps.generate_quadric(Ks, nstep=10) Z = Zs[0] coords = np.array([X, Y, Z]).transpose() open_mesh = trimesh.Trimesh(faces=faces, vertices=coords, process=False) open_mesh_boundary = stop.mesh_boundary(open_mesh) print(open_mesh_boundary) scene_list = [open_mesh] for bound in open_mesh_boundary: points = open_mesh.vertices[bound] cloud_boundary = trimesh.points.PointCloud(points) cloud_colors = np.array( [trimesh.visual.random_color() for i in points]) cloud_boundary.vertices_color = cloud_colors scene_list.append(cloud_boundary) scene = trimesh.Scene(scene_list) scene.show() # here is how to get the vertices that define the boundary of # a texture on a mesh mesh = sio.load_mesh('data/example_mesh.gii')
from vispy.scene import Line from visbrain.objects import VispyObj, SourceObj ############################################################################### # here is how to get the vertices that define the boundary of an open mesh K = [-1, -1] open_mesh = sps.generate_quadric(K, nstep=5) ############################################################################### # Identify the vertices lying on the boundary of the mesh and order # them to get a path traveling across boundary vertices # The output is a list of potentially more than one boudaries # depending on the topology of the input mesh. # Here the mesh has a single boundary open_mesh_boundary = stop.mesh_boundary(open_mesh) print(open_mesh_boundary) ############################################################################### # show the result # WARNING : BrainObj should be added first before visb_sc = splt.visbrain_plot(mesh=open_mesh, caption='open mesh') # create points with vispy for bound in open_mesh_boundary: points = open_mesh.vertices[bound] s_rad = SourceObj('rad', points, color='red', symbol='square', radius_min=10) visb_sc.add_to_subplot(s_rad) lines = Line(pos=open_mesh.vertices[bound], width=10, color='b') # wrap the vispy object using visbrain
def test_boundaries_concat_basic(self): mesh_a = self.cutSphere_A boundary = stop.mesh_boundary(mesh_a) assert (type(boundary) == list)