Reputation: 1
I am wanting to automate the cleaning up of 3D scans in .stl format. I have a script that will import the scan, rotate the mesh and align with the floor. What i want to do from here is, select the EDGE of the mesh. Then extrude to FLOOR or just beyond. Then slice at floor level closing the mesh and making it water tight. Here's the first part...
def open_file_dialog():
root = tk.Tk()
root.withdraw()
file_path = filedialog.askopenfilename(
title="Select a file",
filetypes=(("All files", "*.*"),)
)
return file_path
def save_file_dialog():
root = tk.Tk()
root.withdraw()
file_path = filedialog.asksaveasfilename(
title="Save as",
filetypes=(("All files", "*.*"),),
defaultextension=""
)
return file_path
def load_mesh(mesh_path):
# Load the mesh from the file using trimesh
mesh = trimesh.load_mesh(mesh_path)
return mesh
def rotate_mesh_to_floor(mesh_path):
mesh = load_mesh(mesh_path)
bounding_box = mesh.bounding_box_oriented
bottom_normal = bounding_box.face_normals[0]
z_vector = np.array([0, 0, 1])
if not np.all(bottom_normal == z_vector):
rotation_axis = np.cross(bottom_normal, z_vector)
angle = np.arccos(np.dot(bottom_normal, z_vector))
rotation_matrix = trimesh.transformations.rotation_matrix(angle, rotation_axis)
mesh = mesh.apply_transform(rotation_matrix)
translation_distance = -np.min(mesh.vertices[:, 2])
translation_vector = np.array([0, 0, translation_distance])
mesh = mesh.apply_translation(translation_vector)
return mesh
mesh_path = open_file_dialog()
rotated_mesh = rotate_mesh_to_floor(mesh_path)
save_path = save_file_dialog()
if not save_path.endswith(('.stl', '.obj', '.ply')):
save_path += '.stl'
rotated_mesh.export(save_path)
Here is my attempt, I included a render window to help track the progress, but however i try to extrude it makes a mess.I also tried changing the file type.
import tkinter as tk
from tkinter import filedialog
import trimesh
import numpy as np
import pyrender
def open_file_dialog():
root = tk.Tk()
root.withdraw()
file_path = filedialog.askopenfilename(
title="Select a file",
filetypes=(("All files", "*.*"),)
)
return file_path
def save_file_dialog():
root = tk.Tk()
root.withdraw()
file_path = filedialog.asksaveasfilename(
title="Save as",
filetypes=(("All files", "*.*"),),
defaultextension=""
)
return file_path
def load_mesh(mesh_path):
mesh = trimesh.load_mesh(mesh_path)
return mesh
def rotate_mesh_to_floor(mesh_path):
mesh = load_mesh(mesh_path)
bounding_box = mesh.bounding_box_oriented
bottom_normal = bounding_box.face_normals[0]
z_vector = np.array([0, 0, 1])
if not np.all(bottom_normal == z_vector):
rotation_axis = np.cross(bottom_normal, z_vector)
angle = np.arccos(np.dot(bottom_normal, z_vector))
rotation_matrix = trimesh.transformations.rotation_matrix(angle, rotation_axis)
mesh = mesh.apply_transform(rotation_matrix)
translation_distance = -np.min(mesh.vertices[:, 2])
translation_vector = np.array([0, 0, translation_distance])
mesh = mesh.apply_translation(translation_vector)
return mesh
from scipy.spatial import ConvexHull
import trimesh
def extrude_to_floor(mesh):
outlines = mesh.outline()
if outlines.vertices.shape[0] == 0: # Check if the vertices array is empty
raise Exception("Vertices array is empty. Encountered empty mesh outline. Skipping extrusion.")
# ... (rest of your code)
origin = outlines.vertices[0]
# ...
# Continue with the rest of your code as is.
projections = outlines.vertices - origin
projections[:, 2] = 0
extrusion_height = -10 # A constant extrusion height, adjust as needed
hull = ConvexHull(projections[:, :2])
polygon = trimesh.path.polygons.Polygon(np.array(projections[hull.vertices]))
path2D = trimesh.path.Path2D(polygon)
extruded = path2D.extrude(height=extrusion_height)
extruded = trimesh.Trimesh(vertices=extruded.vertices, faces=extruded.faces)
result = trimesh.util.concatenate(mesh, extruded)
return result
def add_lights_to_scene(scene):
light_positions = [(300, -100, 300), (100, -300, 100), (-100, 100, 300)]
light_colors = [(255, 255, 255), (255, 255, 255), (255, 255, 255)]
for pos, col in zip(light_positions, light_colors):
light = pyrender.PointLight(color=np.array(col) / 255, intensity=1000.0)
translation_matrix = np.eye(4)
translation_matrix[0:3, 3] = pos
scene.add(light, pose=translation_matrix)
def display_mesh_with_pyrender(mesh):
scene = pyrender.Scene()
add_lights_to_scene(scene)
pyrender_mesh = pyrender.Mesh.from_trimesh(mesh)
scene.add(pyrender_mesh)
pyrender.Viewer(scene, use_raymond_lighting=True)
mesh_path = open_file_dialog()
rotated_mesh = rotate_mesh_to_floor(mesh_path)
extruded_mesh = extrude_to_floor(rotated_mesh)
extruded_mesh.export("temp.glb")
scene = trimesh.load("temp.glb")
# Extract Trimesh instance from Scene
trimesh_mesh = list(scene.geometry.values())[0]
# Display our Trimesh mesh
display_mesh_with_pyrender(trimesh_mesh)
# Convert our Trimesh mesh to a Pyrender mesh
pyrender_mesh = pyrender.Mesh.from_trimesh(trimesh_mesh)
positions = pyrender_mesh.primitives[0].positions
faces = pyrender_mesh.primitives[0].indices
# Convert our Pyrender mesh back to a Trimesh instance
rotated_mesh_back = trimesh.Trimesh(positions, faces, process=False)
save_path = save_file_dialog()
if not save_path.endswith('.stl'):
save_path += '.stl'
rotated_mesh_back.export(save_path, file_type='stl')
Upvotes: 0
Views: 85