user24839124
user24839124

Reputation: 1

I want to select the open edge of a mesh and extrude to floor, then close the mesh with a plane to make it watertight. Python

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

Answers (0)

Related Questions