Added import_stl as Solid

This commit is contained in:
gumyr 2023-07-09 15:53:03 -04:00
parent 55b6119104
commit 5f4e14da82
3 changed files with 90 additions and 11 deletions

View file

@ -43,7 +43,8 @@ dependencies = [
"numpy >= 1.24.1, <2",
"svgpathtools >= 1.5.1, <2",
"anytree >= 2.8.0, <3",
"ezdxf >= 1.0.0, < 2"
"ezdxf >= 1.0.0, < 2",
"numpy-stl >= 3.0.0, <4"
]
[tool.setuptools.packages.find]

View file

@ -30,15 +30,26 @@ license:
import os
from math import degrees
from stl.mesh import Mesh
from svgpathtools import svg2paths
from typing import Union
from OCP.TopoDS import TopoDS_Face, TopoDS_Shape
from OCP.BRep import BRep_Builder
from OCP.BRepTools import BRepTools
from OCP.STEPControl import STEPControl_Reader
import OCP.IFSelect
from OCP.RWStl import RWStl
from OCP.BRepBuilderAPI import (
BRepBuilderAPI_MakeEdge,
BRepBuilderAPI_MakeFace,
BRepBuilderAPI_MakeSolid,
BRepBuilderAPI_MakeVertex,
BRepBuilderAPI_MakeWire,
BRepBuilderAPI_Sewing,
)
from OCP.gp import gp_Pnt
from build123d.topology import Compound, Edge, Face, Shape, ShapeList
from build123d.topology import Compound, Edge, Face, Shape, ShapeList, Solid, downcast
def import_brep(file_name: str) -> Shape:
@ -98,27 +109,78 @@ def import_step(file_name: str) -> Compound:
return Compound.make_compound(solids)
def import_stl(file_name: str) -> Face:
def import_stl(file_name: str, for_reference: bool = True) -> Union[Face, Solid]:
"""import_stl
Extract shape from an STL file and return them as a Face object.
Extract shape from an STL file and return them as a Solid object.
Args:
file_name (str): file path of STL file to import
for_reference (bool, optional): only create an uneditable mesh object
for use as a reference. Otherwise, create an editable Solid object.
Note that creating a reference is very fast while creating an editable
model may take minutes depending on the size of the STL file.
Defaults to True.
Raises:
ValueError: Could not import file
Returns:
Face: contents of STL file
Union[Face, Solid]: STL model
"""
# Now read and return the shape
reader = RWStl.ReadFile_s(file_name)
face = TopoDS_Face()
if for_reference:
# Now read and return the shape
reader = RWStl.ReadFile_s(file_name)
face = TopoDS_Face()
BRep_Builder().MakeFace(face, reader)
stl_obj = Face.cast(face)
else:
# Read the file with numpy-stl
try:
stl_mesh = Mesh.from_file(file_name)
except:
raise ValueError("Invalid file")
BRep_Builder().MakeFace(face, reader)
faces = []
return Face.cast(face)
for facet in stl_mesh.vectors:
# Create OCC vertices
ocp_vertices = [
downcast(BRepBuilderAPI_MakeVertex(gp_Pnt(x, y, z)).Vertex())
for x, y, z in facet
]
# Create OCC edges
ocp_edges = [
BRepBuilderAPI_MakeEdge(v1, v2).Edge()
for v1, v2 in zip(ocp_vertices, ocp_vertices[1:] + [ocp_vertices[0]])
]
# Create OCC wire
wire_builder = BRepBuilderAPI_MakeWire()
for edge in ocp_edges:
wire_builder.Add(edge)
ocp_wire = wire_builder.Wire()
# Create OCC face
face_builder = BRepBuilderAPI_MakeFace(ocp_wire)
ocp_face = face_builder.Face()
# Store the faces
faces.append(ocp_face)
# Create a shell
shell_builder = BRepBuilderAPI_Sewing()
for face in faces:
shell_builder.Add(face)
shell_builder.Perform()
occ_shell = downcast(shell_builder.SewedShape())
# Create a solid
solid_builder = BRepBuilderAPI_MakeSolid(occ_shell)
stl_obj = Solid(solid_builder.Solid())
return stl_obj
def import_svg_as_buildline_code(file_name: str) -> tuple[str, str]:

View file

@ -1141,7 +1141,7 @@ class TestFunctions(unittest.TestCase):
self.assertEqual(plug[0], cyl)
class TestImportExport(unittest.TestCase):
class TestImportExport(DirectApiTestCase):
def test_import_export(self):
original_box = Solid.make_box(1, 1, 1)
original_box.export_step("test_box.step")
@ -1157,6 +1157,22 @@ class TestImportExport(unittest.TestCase):
with self.assertRaises(ValueError):
step_box = import_step("test_box.step")
def test_import_stl(self):
original_box = Solid.make_box(1, 2, 3)
original_box.export_stl("test_box.stl")
stl_box = import_stl("test_box.stl", for_reference=False).clean()
self.assertEqual(len(stl_box.vertices()), 8)
self.assertEqual(len(stl_box.edges()), 12)
self.assertEqual(len(stl_box.faces()), 6)
self.assertAlmostEqual(stl_box.volume, 1 * 2 * 3, 5)
stl_box = import_stl("test_box.stl", for_reference=True)
self.assertVectorAlmostEquals(stl_box.position, (0, 0, 0), 5)
os.remove("test_box.stl")
with self.assertRaises(ValueError):
import_stl("test_box.stl", for_reference=False)
class TestJoints(DirectApiTestCase):
def test_rigid_joint(self):