I am using pyvista to mesh 3D scan data of terrain and save them to stl files to save in blender. Some of the .xyz files contain data for a single continuous area while others include a number of smaller areas, with no data inbetwean.
I have managed to get this to work for the continuous regions:
import numpy as np
import pyvista as pv
xyz_path = r"Path_to_file.xyz"
stl_path = r"Path_to_file.stlr"
data = np.loadtxt(xyz_path)
cloud = pv.PolyData(data)
surf = cloud.delaunay_2d()
surf.plot(show_edges=True)
surf.save(stl_name)
for the discontinuous reqions I got large faces linking the different areas that I would like to delete. I found a way to do this based on the area of the face.
import numpy as np
import pyvista as pv
xyz_path = r"Path_to_file.xyz"
stl_path = r"Path_to_file.stlr"
data = np.loadtxt(xyz_path)
cloud = pv.PolyData(data)
surf = cloud.delaunay_2d()
cell_sizes = surf.compute_cell_sizes(length=False, area=True, volume=False)
areas = cell_sizes['Area']
max_area_threshold = 100 # Set the area threshold
# Create a mask for cells that meet the area criteria
valid_cells = areas < max_area_threshold
# Use the mask to filter out large triangles
filtered_mesh = surf.extract_cells(valid_cells)
filtered_mesh.plot(show_edges=True, scalars='Area')
surf.save(stl_name)
From the plot I can see that the correct elements have been deleted. However, this process also converts the data structure from core.pointset.PolyData (which can be saved to stl) to core.pointset.UnstructuredGrid (which cant be saved to stl).
Is there a way I con convert the core.pointset.UnstructuredGrid object to a format that can be saved to stl or delete the unwanted elements in a way that I can still save the surface to stl.
My current workaround is to create the STL file leaving in the unwanted faces then delete these in MeshLab but if possible I would like to avoid this step if there is a simple solution.
Based on the input from Andras I used extract_surface()
to extract the surface and save to stl file.
import numpy as np import pyvista as pv
xyz_path = r"Path_to_file.xyz" stl_path = r"Path_to_file.stlr"
data = np.loadtxt(xyz_path) cloud = pv.PolyData(data) surf = cloud.delaunay_2d()
cell_sizes = surf.compute_cell_sizes(length=False, area=True, volume=False) areas = cell_sizes['Area'] max_area_threshold = 100 # Set the area threshold
#Create a mask for cells that meet the area criteria
valid_cells = areas < max_area_threshold
#Use the mask to filter out large triangles
filtered_mesh = surf.extract_cells(valid_cells) filtered_mesh.plot(show_edges=True, scalars='Area')
#Extract surface
filtered_mesh_surface = filtered_mesh.extract_surface()
filtered_mesh_surface.save(stl_path_filtered)
This is covered in the documentation here: https://docs.pyvista.org/version/stable/examples/01-filter/extract-surface.html.