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()