def two_tetrahedrons(): vertices_1 = np.array([[0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 1]]) vertices_2 = np.array([[2, 0, 0], [3, 0, 0], [2, 1, 0], [2, 0, 2]]) faces_1 = np.array([[0, 1, 2], [0, 2, 3], [0, 1, 3], [1, 2, 3], [0, 2, 1], [0, 3, 2], [0, 3, 1], [1, 3, 2]]) faces_2 = np.array([[0, 1, 2], [0, 2, 3], [0, 1, 3], [1, 2, 3], [0, 2, 1], [0, 3, 2], [0, 3, 1], [1, 3, 2]]) mesh_1 = Trimesh(vertices_1, faces_1) mesh_2 = Trimesh(vertices_2, faces_2) return mesh_1, mesh_2
def box(size=(1, 1, 1), max_edge=0.1): box = trimesh.creation.box(extents=size) vertices, faces = subdivide_to_size(box.vertices, box.faces, max_edge) mesh = Trimesh(vertices, faces) return mesh
def set_new_mesh_vertices(mesh, vertices): mesh_new = Trimesh(vertices=vertices.copy(), faces=mesh.faces, process=False) return mesh_new
def build(self): self._load_last_vertices() faces_count = (self.vertices.shape[0]) * ( self.vertices.shape[1]) * 2 - 6 self.faces = np.zeros((faces_count, 3), dtype=int) self.face_normals = np.zeros((faces_count, 3)) self.next_face = 0 for i in range(self.vertices.shape[0] - 1): for j in range(self.vertices.shape[1] - 1): self._add_face(i, j, i + 1, j, i, j + 1) if i != self.vertices.shape[0] - 2 or j != self.vertices.shape[ 0] - 2: self._add_face(i, j + 1, i + 1, j, i + 1, j + 1) else: self._add_face(i, j + 1, i + 1, j, 0, 0) for i in range(0, self.vertices.shape[0] - 1): if i > 0: self._add_face(i, 0, 0, 0, i + 1, 0) if i < self.vertices.shape[0] - 2: self._add_face(i, -1, i + 1, -1, 0, 0) for j in range(0, self.vertices.shape[1] - 1): if j > 0: self._add_face(0, j, 0, j + 1, 0, 0) if j < self.vertices.shape[1] - 2: self._add_face(-1, j, 0, 0, -1, j + 1) return Trimesh(vertices=self.vertices.reshape(-1, 3), faces=self.faces, face_normals=self.face_normals, process=False)
def add_texture_to_mesh(mesh, K, image): f, ox, oy = K verts, faces = mesh.get_mesh_verts_faces(0) verts = verts.tolist() pix_pos = [] for v in verts: x, y, z = v i = -x * f / z + K[1] j = -y * f / z + K[2] pix_pos.append([j, i]) colors = [] for i in pix_pos: try: colors.append(list(image[int(i[0])][int(i[1])]) + [1]) except IndexError: print(f"Skipping index: {i}") pass textured_mesh = Trimesh(vertices=verts, faces=faces, vertex_colors=colors) return textured_mesh
def add_noise_to_mesh(mesh, noise): new_vertices = mesh.vertices.copy() new_vertices = new_vertices + noise new_mesh = Trimesh(new_vertices, mesh.faces, process=False) return new_mesh
def apply_transform(mesh, transforms): new_vertices = np.array(mesh.vertices.copy()) new_vertices = np.stack( [new_vertices, np.ones(len(new_vertices))[:, None]], axis=1) new_vertices = np.matmul(transforms, new_vertices) new_mesh = Trimesh(new_vertices, mesh.faces, process=False) return new_mesh
def mesh_subsample(mesh, vertices): num_vertices = len(vertices) new_faces = [] for face in mesh.faces: if (face[0] < num_vertices) and (face[1] < num_vertices) and ( face[2] < num_vertices): new_faces += [face] new_mesh = Trimesh(vertices, new_faces) return new_mesh
def add_uv_texture_to_mesh(mesh, K, image, mask): f, ox, oy = K verts, faces = mesh.get_mesh_verts_faces(0) verts = verts.tolist() mesh = Trimesh(vertices=verts, faces=faces) mesh = filter_laplacian(mesh) verts = mesh.vertices faces = mesh.faces front_view_faces = MeshRCNNModel.get_front_view_faces(mesh) front_view_vertices = np.unique(np.array(front_view_faces)) uv_map = [] for vertex_index in range(len(verts)): if vertex_index in front_view_vertices: x, y, z = verts[vertex_index] i = int(-x * f / z + K[1]) / image.shape[1] j = int(y * f / z + K[2]) / image.shape[0] else: i = 0 j = 0 uv_map.append([i, j]) # Average color calculation inside the mask color_mask = ~np.repeat(mask[:, :, np.newaxis], 3, axis=2) average_color = np.ma.array(image, mask=color_mask).mean(axis=0).mean(axis=0) # Assign average color to pixels outside of the mask image[:, :, 0] = np.where(~mask, average_color[0], image[:, :, 0]) image[:, :, 1] = np.where(~mask, average_color[1], image[:, :, 1]) image[:, :, 2] = np.where(~mask, average_color[2], image[:, :, 2]) image_ = Image.fromarray(image) texture = TextureVisuals(uv=uv_map, image=image_) textured_mesh = Trimesh(vertices=verts, faces=faces, visual=texture) return textured_mesh
def plane(width=2, length=2, num_points=2500): x = np.linspace(0, length, num=int(np.sqrt(num_points))) y = np.linspace(0, width, num=int(np.sqrt(num_points))) x, y = np.meshgrid(x, y) z = np.zeros_like(x) tri = mtri.Triangulation(x.flatten(), y.flatten()) faces = tri.triangles faces_dual = faces[:, [0, 2, 1]] faces = np.vstack([faces, faces_dual]) vertices = np.array([x.flatten(), y.flatten(), z.flatten()]).T plane = Trimesh(vertices=vertices, faces=faces) return plane
def remove_duplicate_vertices(mesh): unique_vertices, unique_inverse = np.unique(mesh.vertices, axis=0, return_inverse=True) old2new_vertices = dict(zip(np.arange(len(mesh.vertices)), unique_inverse)) new_faces = np.copy(mesh.faces) for i, face in enumerate(new_faces): new_face = np.array([ old2new_vertices[face[0]], old2new_vertices[face[1]], old2new_vertices[face[2]] ]) new_faces[i] = new_face new_mesh = Trimesh(unique_vertices, new_faces, process=False) return new_mesh, old2new_vertices
def monkey_saddle(num_points=2500): def f(x, y): return x**3 - 3 * x * y**2 x = np.linspace(-1, 1, num=int(np.sqrt(num_points))) y = np.linspace(-1, 1, num=int(np.sqrt(num_points))) x, y = np.meshgrid(x, y) z = f(x, y) tri = mtri.Triangulation(x.flatten(), y.flatten()) faces = tri.triangles faces_dual = faces[:, [0, 2, 1]] faces = np.vstack([faces, faces_dual]) vertices = np.array([x.flatten(), y.flatten(), z.flatten()]).T saddle = Trimesh(vertices=vertices, faces=faces) return saddle
def concatenate_with_transforms(parts, transforms): concatenated_mesh = trimesh.util.concatenate(parts) concatenated_transforms = torch.cat(transforms, dim=0) unique_vertices, unique_inverse = np.unique(concatenated_mesh.vertices, axis=0, return_inverse=True) old2new_vertices = dict( zip(np.arange(len(concatenated_mesh.vertices)), unique_inverse)) new_faces = np.copy(concatenated_mesh.faces) for i, face in enumerate(new_faces): new_face = np.array([ old2new_vertices[face[0]], old2new_vertices[face[1]], old2new_vertices[face[2]] ]) new_faces[i] = new_face new_transforms = torch.zeros((len(unique_vertices), 4, 4)) for i in range(len(concatenated_transforms)): new_transforms[old2new_vertices[i]] = concatenated_transforms[i] new_mesh = Trimesh(unique_vertices, new_faces, process=False) return new_mesh, new_transforms
def extrude_triangulation(vertices, faces, height, cap=True, base=True, transform=None): """ Based on Trimesh extrude_triangulation, but allows to exclude cap and base. """ vertices = np.asanyarray(vertices, dtype=np.float64) height = float(height) faces = np.asanyarray(faces, dtype=np.int64) if not util.is_shape(vertices, (-1, 2)): raise ValueError('Vertices must be (n,2)') if not util.is_shape(faces, (-1, 3)): raise ValueError('Faces must be (n,3)') if np.abs(height) < constants.tol.merge: raise ValueError('Height must be nonzero!') # Make sure triangulation winding is pointing up normal_test = triangles.normals([util.stack_3D(vertices[faces[0]])])[0] normal_dot = np.dot(normal_test, [0.0, 0.0, np.sign(height)])[0] # Make sure the triangulation is aligned with the sign of # the height we've been passed if normal_dot < 0.0: faces = np.fliplr(faces) # stack the (n,3) faces into (3*n, 2) edges edges = geometry.faces_to_edges(faces) edges_sorted = np.sort(edges, axis=1) # Edges which only occur once are on the boundary of the polygon # since the triangulation may have subdivided the boundary of the # shapely polygon, we need to find it again edges_unique = grouping.group_rows(edges_sorted, require_count=1) # (n, 2, 2) set of line segments (positions, not references) boundary = vertices[edges[edges_unique]] # We are creating two vertical triangles for every 2D line segment # on the boundary of the 2D triangulation vertical = np.tile(boundary.reshape((-1, 2)), 2).reshape((-1, 2)) vertical = np.column_stack( (vertical, np.tile([0, height, 0, height], len(boundary)))) vertical_faces = np.tile([3, 1, 2, 2, 1, 0], (len(boundary), 1)) vertical_faces += np.arange(len(boundary)).reshape((-1, 1)) * 4 vertical_faces = vertical_faces.reshape((-1, 3)) # Stack the (n,2) vertices with zeros to make them (n, 3) vertices_3D = util.stack_3D(vertices) # A sequence of zero- indexed faces, which will then be appended # with offsets to create the final mesh if not base and not cap: vertices_seq = [vertical] faces_seq = [vertical_faces] elif not base and cap: vertices_seq = [vertices_3D.copy() + [0.0, 0, height], vertical] faces_seq = [faces.copy(), vertical_faces] elif base and not cap: vertices_seq = [vertices_3D, vertical] faces_seq = [faces[:, ::-1], vertical_faces] else: vertices_seq = [ vertices_3D, vertices_3D.copy() + [0.0, 0, height], vertical ] faces_seq = [faces[:, ::-1], faces.copy(), vertical_faces] # Append sequences into flat nicely indexed arrays vertices, faces = util.append_faces(vertices_seq, faces_seq) # Apply transform here to avoid later bookkeeping if transform is not None: vertices = transformations.transform_points(vertices, transform) # If the transform flips the winding flip faces back so that the normals will be facing outwards if transformations.flips_winding(transform): faces = np.ascontiguousarray( np.fliplr(faces)) # fliplr makes arrays non-contiguous # create mesh object with passed keywords mesh = Trimesh(vertices=vertices, faces=faces) return mesh
def extrude_coords(coords_a, coords_b, distance, base_height=0): vertices = [] vertices.extend([(x, y, base_height) for x, y, *z in coords_a]) vertices_b_idx = len(vertices) vertices.extend([(x, y, base_height + distance) for x, y, *z in coords_b]) shape_a_idx = 0 shape_b_idx = 0 def va(shape_a_idx): return vertices[shape_a_idx] def vb(shape_b_idx): return vertices[(shape_b_idx) + vertices_b_idx] def ang(v): return (math.atan2(v[1], v[0]) + (math.pi * 2)) % (math.pi * 2) def diff(va, vb): return [va[0] - vb[0], va[1] - vb[1], va[2] - vb[2]] def distsqr(v): return v[0] * v[0] + v[1] * v[1] + v[2] * v[2] faces = [] finished_a = False finished_b = False last_tri = None while not (finished_a and finished_b): la = distsqr(diff(va(shape_a_idx + 1), vb(shape_b_idx))) if ( shape_a_idx < len(coords_a) - 1) else float("inf") lb = distsqr(diff(vb(shape_b_idx + 1), va(shape_a_idx))) if ( shape_b_idx < len(coords_b) - 1) else float("inf") aa = ang(va(shape_a_idx)) aan = ang(va(shape_a_idx + 1)) if (shape_a_idx < len(coords_a) - 1) else float("inf") ab = ang(vb(shape_b_idx)) abn = ang(vb(shape_b_idx + 1)) if (shape_b_idx < len(coords_b) - 1) else float("inf") #norm = 'l2' norm = 'l2' if norm == 'angle': advance_b = (abs(abn - aa) < abs(aan - ab)) elif norm == 'l2': advance_b = lb < la if advance_b or finished_a: ntri = [ shape_a_idx, shape_b_idx + vertices_b_idx, (shape_b_idx + 1) + vertices_b_idx ] shape_b_idx += 1 elif not advance_b or finished_b: ntri = [ shape_a_idx, shape_b_idx + vertices_b_idx, (shape_a_idx + 1) ] shape_a_idx += 1 else: raise AssertionError() if last_tri == ntri: break faces.append(ntri) last_tri = ntri #print(ntri) if shape_a_idx >= len(coords_a) - 1: finished_a = True if shape_b_idx >= len(coords_b) - 1: finished_b = True #print(vertices) #print(faces) return Trimesh(vertices, faces)
def extrude_step(obj, shape, offset, cap=True, method=EXTRUSION_METHOD_WRAP): """ Extrude a shape into another. """ last_shape = obj.extra['_extrusion_last_shape'] #obj = last_shape.individualize() #shape = shape.individualize() #obj_a.assert_single() #obj_b.assert_single() geom_a = last_shape.geom geom_b = shape.geom result = obj.copy() if geom_a is None or geom_a.is_empty: logger.debug( "Should be extruding from point or line, ignoring and returning argument." ) # Previous step was empty, avoid destroy the object # TODO: this can be improved, previous could be point or line return result elif geom_a.type in ('MultiPolygon', 'GeometryCollection'): logger.warn( "Cannot extrude a step from a 'MultiPolygon' or 'GeometryCollection'." ) return result if geom_b is None: logger.warn( "Cannot extrude-step to None (ignoring and returning argument).") return result elif geom_b.type in ('MultiPolygon', 'GeometryCollection'): logger.warn( "Cannot extrude a step to a 'MultiPolygon' or 'GeometryCollection' (skipping/flattening)." ) geom_b = Point() elif geom_b.is_empty and method == EXTRUSION_METHOD_WRAP: logger.debug("Extruding to point (should be using line too).") geom_b = geom_a.centroid elif geom_b.type == "LineString" and method == EXTRUSION_METHOD_WRAP: geom_b = Polygon(list(geom_b.coords) + [geom_b.coords[0]]) elif geom_b.is_empty and method == EXTRUSION_METHOD_SUBTRACT: logger.debug( "Cannot extrude subtract to empty geometry. Skipping / flattening." ) elif geom_b.type == "LineString" and method == EXTRUSION_METHOD_SUBTRACT: logger.info( "Cannot extrude subtract to linestring. Skipping / flattening.") geom_b = Point() vertices = list(result.mesh.vertices) if result.mesh else [] faces = list(result.mesh.faces) if result.mesh else [] # Remove previous last cap before extruding. last_cap_idx = result.extra.get('_extrusion_last_cap_idx', None) if last_cap_idx is not None: faces = faces[:last_cap_idx] if not geom_b.is_empty: result.extra['_extrusion_last_shape'] = shape result.extra['_extrusion_last_offset'] = obj.extra.get( '_extrusion_last_offset', 0) + offset if not (geom_a.is_empty or geom_b.is_empty): mesh = None if method == EXTRUSION_METHOD_WRAP: mesh = extrude_between_geoms_wrap( geom_a, geom_b, offset, obj.extra.get('_extrusion_last_offset', 0)) elif method == EXTRUSION_METHOD_SUBTRACT: try: mesh = extrude_between_geoms_subtract( last_shape, shape, offset, obj.extra.get('_extrusion_last_offset', 0)) except DDDException as e: logger.error( "Could not extrude subtract between geometries (%s): %s", obj, e) mesh = None result.extra['_extrusion_last_shape'] = last_shape result.extra['_extrusion_last_offset'] = result.extra[ '_extrusion_last_offset'] - offset else: raise DDDException("Invalid extrusion method: %s", method) if mesh: faces = faces + [[ f[0] + len(vertices), f[1] + len(vertices), f[2] + len(vertices) ] for f in mesh.faces] vertices = vertices + list(mesh.vertices) result.extra[ '_extrusion_steps'] = result.extra['_extrusion_steps'] + 1 result.extra['_extrusion_last_cap_idx'] = len(faces) if cap and not result.extra['_extrusion_last_shape'].geom.is_empty: #print(result.extra['_extrusion_last_shape']) #result.extra['_extrusion_last_shape'].dump() #print(result.extra['_extrusion_last_shape'].geom) try: cap_mesh = result.extra['_extrusion_last_shape'].triangulate( ).translate([0, 0, result.extra.get('_extrusion_last_offset', 0)]) if cap_mesh and cap_mesh.mesh: faces = faces + [[ f[0] + len(vertices), f[1] + len(vertices), f[2] + len(vertices) ] for f in cap_mesh.mesh.faces] vertices = vertices + list(cap_mesh.mesh.vertices) else: result.extra['_extrusion_last_offset'] = result.extra[ '_extrusion_last_offset'] - offset cap_mesh = last_shape.triangulate().translate( [0, 0, result.extra['_extrusion_last_offset']]) faces = faces + [[ f[0] + len(vertices), f[1] + len(vertices), f[2] + len(vertices) ] for f in cap_mesh.mesh.faces] vertices = vertices + list(cap_mesh.mesh.vertices) except Exception as e: logger.error( "Could not generate extrude cap triangulation (%s): %s", result.extra['_extrusion_last_shape'], e) cap_mesh = None # Merge if len(vertices) > 0 and len(faces) > 0: mesh = Trimesh(vertices, faces) mesh.merge_vertices() #mesh.fix_normals() result.mesh = mesh return result
def extrude_coords(coords_a, coords_b, distance, base_height=0): # Note that last vertex in polygons is repeated, we ignore it and use modulus for referring to vertex indices # This reuses the vertices when closing the loop is_poly_a = (coords_a[0] == coords_a[-1]) and len(coords_a) > 1 is_poly_b = (coords_b[0] == coords_b[-1]) and len(coords_b) > 1 modulus_a = (len(coords_a) - 1) if is_poly_a else len(coords_a) modulus_b = (len(coords_b) - 1) if is_poly_b else len(coords_b) vertices = [] vertices.extend([(x, y, base_height) for x, y, *z in (coords_a[:-1] if is_poly_a else coords_a) ]) vertices_b_idx = len(vertices) vertices.extend([(x, y, base_height + distance) for x, y, *z in (coords_b[:-1] if is_poly_b else coords_b) ]) shape_a_idx = 0 shape_b_idx = 0 def va(shape_a_idx): return vertices[shape_a_idx % modulus_a] def vb(shape_b_idx): return vertices[(shape_b_idx % modulus_b) + vertices_b_idx] def ang(v): return (math.atan2(v[1], v[0]) + (math.pi * 2)) % (math.pi * 2) def diff(va, vb): return [va[0] - vb[0], va[1] - vb[1], va[2] - vb[2]] def distsqr(v): return v[0] * v[0] + v[1] * v[1] + v[2] * v[2] faces = [] finished_a = False finished_b = False last_tri = None while not (finished_a and finished_b): la = distsqr(diff(va(shape_a_idx + 1), vb(shape_b_idx))) if ( shape_a_idx < modulus_a) else float("inf") lb = distsqr(diff(vb(shape_b_idx + 1), va(shape_a_idx))) if ( shape_b_idx < modulus_b) else float("inf") aa = ang(va(shape_a_idx)) aan = ang(va(shape_a_idx + 1)) if (shape_a_idx < len(coords_a) - 1) else float("inf") ab = ang(vb(shape_b_idx)) abn = ang(vb(shape_b_idx + 1)) if (shape_b_idx < len(coords_b) - 1) else float("inf") #norm = 'l2' norm = 'l2' if norm == 'angle': advance_b = (abs(abn - aa) < abs(aan - ab)) elif norm == 'l2': advance_b = lb < la # Vertex modulus is used to handle repeated first-last vertex in polys if advance_b or finished_a: ntri = [ shape_a_idx % modulus_a, shape_b_idx % modulus_b + vertices_b_idx, (shape_b_idx + 1) % modulus_b + vertices_b_idx ] shape_b_idx += 1 elif not advance_b or finished_b: ntri = [ shape_a_idx % modulus_a, shape_b_idx % modulus_b + vertices_b_idx, (shape_a_idx + 1) % modulus_a ] shape_a_idx += 1 else: raise AssertionError() if last_tri == ntri: break #print(ntri) last_tri = ntri if ntri[0] != ntri[1] and ntri[0] != ntri[2] and ntri[1] != ntri[2]: faces.append(ntri) if shape_a_idx >= len(coords_a) - 1: finished_a = True if shape_b_idx >= len(coords_b) - 1: finished_b = True return Trimesh(vertices, faces)
def SplitByPlaneOneSide(mesh, plane_normal, plane_origin, cap=False, cached_dots=None, reversed=False, **kwargs): """ Slice a mesh with a plane, returning a new mesh that is the portion of the original mesh to the positive normal side of the plane Parameters --------- mesh : Trimesh object Source mesh to slice plane_normal : (3,) float Normal vector of plane to intersect with mesh plane_origin : (3,) float Point on plane to intersect with mesh cap : bool If True, cap the result with a triangulated polygon cached_dots : (n, 3) float If an external function has stored dot products pass them here to avoid recomputing kwargs : dict Passed to the newly created sliced mesh Returns ---------- new_vertices : (n, 3) float Vertices of sliced mesh new_faces : (n, 3) int Faces of sliced mesh """ # check input for none if mesh is None: return None # check input plane plane_normal = np.asanyarray(plane_normal, dtype=np.float64) plane_origin = np.asanyarray(plane_origin, dtype=np.float64) # check to make sure origins and normals have acceptable shape shape_ok = ( (plane_origin.shape == (3, ) or util.is_shape(plane_origin, (-1, 3))) and (plane_normal.shape == (3, ) or util.is_shape(plane_normal, (-1, 3))) and plane_origin.shape == plane_normal.shape) if not shape_ok: raise ValueError('plane origins and normals must be (n, 3)!') # start with copy of original mesh, faces, and vertices sliced_mesh = mesh.copy() vertices = mesh.vertices.copy() faces = mesh.faces.copy() # slice away specified planes for origin, normal in zip(plane_origin.reshape((-1, 3)), plane_normal.reshape((-1, 3))): # calculate dots here if not passed in to save time # in case of cap if cached_dots is None: # dot product of each vertex with the plane normal indexed by face # so for each face the dot product of each vertex is a row # shape is the same as faces (n,3) dots = np.einsum('i,ij->j', normal, (vertices - origin).T)[faces] else: dots = cached_dots # save the new vertices and faces vertices, faces = intersections.slice_faces_plane(vertices=vertices, faces=faces, plane_normal=normal, plane_origin=origin, cached_dots=dots) # check if cap arg specified if cap: # check if mesh is watertight (can't cap if not) if not sliced_mesh.is_watertight: raise ValueError('Input mesh must be watertight to cap slice') path = sliced_mesh.section(plane_normal=normal, plane_origin=origin) if not path: if reversed: return sliced_mesh else: return None # transform Path3D onto XY plane for triangulation on_plane, to_3D = path.to_planar() # triangulate each closed region of 2D cap # without adding any new vertices v, f = [], [] for polygon in on_plane.polygons_full: t = triangulate_polygon(polygon, triangle_args='pY', engine='triangle') v.append(t[0]) f.append(t[1]) if tol.strict: # in unit tests make sure that our triangulation didn't # insert any new vertices which would break watertightness from scipy.spatial import cKDTree # get all interior and exterior points on tree check = [np.array(polygon.exterior.coords)] check.extend(np.array(i.coords) for i in polygon.interiors) tree = cKDTree(np.vstack(check)) # every new vertex should be on an old vertex assert np.allclose(tree.query(v[-1])[0], 0.0) # append regions and reindex vf, ff = util.append_faces(v, f) # make vertices 3D and transform back to mesh frame vf = tf.transform_points(np.column_stack((vf, np.zeros(len(vf)))), to_3D) # check to see if our new faces are aligned with our normal #check = windings_aligned(vf[ff], normal) # ## if 50% of our new faces are aligned with the normal flip #if check.astype(np.float64).mean() > 0.5: # ff = np.fliplr(ff) # add cap vertices and faces and reindex vertices, faces = util.append_faces([vertices, vf], [faces, ff]) # Update mesh with cap (processing needed to merge vertices) sliced_mesh = Trimesh(vertices=vertices, faces=faces) vertices, faces = sliced_mesh.vertices.copy( ), sliced_mesh.faces.copy() return sliced_mesh
def _getModelMesh(self, points): return Trimesh(vertices=points.T, faces=self.triangles)