Source code for polpo.preprocessing.mesh._trimesh

import os

import numpy as np
import trimesh

from polpo.preprocessing.base import PreprocessingStep


[docs] class TrimeshFromData(PreprocessingStep):
[docs] def apply(self, mesh): if len(mesh) == 3: vertices, faces, colors = mesh else: vertices, faces = mesh colors = None return trimesh.Trimesh(vertices=vertices, faces=faces, vertex_colors=colors)
[docs] class DataFromTrimesh(PreprocessingStep):
[docs] def apply(self, mesh): return ( np.array(mesh.vertices), np.array(mesh.faces), np.array(mesh.visual.vertex_colors), )
[docs] class TrimeshFromPv(PreprocessingStep):
[docs] def apply(self, poly_data): vertex_colors = ( poly_data["colors"] if "colors" in poly_data.array_names else None ) faces_as_array = poly_data.faces.reshape((poly_data.n_faces, 4))[:, 1:] return trimesh.Trimesh( poly_data.points, faces_as_array, vertex_colors=vertex_colors )
[docs] class TrimeshFaceRemoverByArea(PreprocessingStep): # TODO: generalize? def __init__(self, threshold=0.01, inplace=True): self.threshold = threshold self.inplace = inplace
[docs] def apply(self, mesh): if not self.inplace: mesh = mesh.copy() face_mask = ~np.less(mesh.area_faces, self.threshold) mesh.update_faces(face_mask) return mesh
[docs] class TrimeshDegenerateFacesRemover(PreprocessingStep): """Trimesh degenerate faces remover. https://trimesh.org/trimesh.base.html#trimesh.base.Trimesh.nondegenerate_faces Parameters ---------- height: float Identifies faces with an oriented bounding box shorter than this on one side. """ def __init__(self, height=1e-08, inplace=True): self.height = height self.inplace = inplace
[docs] def apply(self, mesh): if not self.inplace: mesh = mesh.copy() faces = mesh.nondegenerate_faces(height=self.height) mesh.update_faces(faces) return mesh
[docs] class TrimeshDecimator(PreprocessingStep): """Trimesh simplify quadratic decimation. Parameters ---------- percent : float A number between 0.0 and 1.0 for how much. face_count : int Target number of faces desired in the resulting mesh. agression: int An integer between 0 and 10, the scale being roughly 0 is “slow and good” and 10 being “fast and bad.” """ # NB: uses fast-simplification def __init__(self, percent=None, face_count=None, agression=None): super().__init__() self.percent = percent self.face_count = face_count self.agression = agression
[docs] def apply(self, mesh): # TODO: issue with colors? decimated_mesh = mesh.simplify_quadric_decimation( percent=self.percent, face_count=self.face_count, aggression=self.agression, ) # # TODO: delete # colors_ = np.array(decimated_mesh.visual.vertex_colors) # print("unique colors after decimation:", len(np.unique(colors_, axis=0))) return decimated_mesh
[docs] class TrimeshLargestComponentSelector(PreprocessingStep): def __init__(self, only_watertight=False): self.only_watertight = only_watertight
[docs] def apply(self, mesh): components = mesh.split(only_watertight=self.only_watertight) if len(components) == 0: return mesh components.sort(key=lambda component: len(component.faces), reverse=True) return components[0]
[docs] class TrimeshToPly(PreprocessingStep): def __init__( self, dirname="", encoding="binary", vertex_normal=None, include_attributes=True, ): # TODO: create dir if does not exist? self.dirname = dirname self.encoding = encoding self.vertex_normal = vertex_normal self.include_attributes = include_attributes # TODO: add override?
[docs] def apply(self, data): filename, mesh = data ext = ".ply" if not filename.endswith(ext): filename += ext path = os.path.join(self.dirname, filename) ply_text = trimesh.exchange.ply.export_ply( mesh, encoding=self.encoding, include_attributes=self.include_attributes ) with open(path, "wb") as file: file.write(ply_text) return path
[docs] class TrimeshReader(PreprocessingStep): # TODO: update
[docs] def apply(self, path): return trimesh.load(path)
[docs] class TrimeshLaplacianSmoothing(PreprocessingStep): """ https://trimesh.org/trimesh.smoothing.html#trimesh.smoothing.filter_laplacian """ def __init__( self, lamb=0.5, iterations=10, implicit_time_integration=False, volume_constraint=True, laplacian_operator=None, inplace=True, ): self.lamb = lamb self.iterations = iterations self.implicit_time_integration = implicit_time_integration self.volume_constraint = volume_constraint self.laplacian_operator = laplacian_operator self.inplace = inplace
[docs] def apply(self, mesh): if not self.inplace: mesh = mesh.copy() trimesh.smoothing.filter_laplacian( mesh, lamb=self.lamb, iterations=self.iterations, implicit_time_integration=self.implicit_time_integration, volume_constraint=self.volume_constraint, laplacian_operator=self.laplacian_operator, ) return mesh
[docs] class TrimeshClone(PreprocessingStep):
[docs] def apply(self, mesh): return mesh.copy()