mirror of
https://github.com/gumyr/build123d.git
synced 2025-12-06 02:30:55 -08:00
209 lines
8.1 KiB
Python
209 lines
8.1 KiB
Python
import unittest, uuid
|
|
from build123d.build_enums import MeshType, Unit
|
|
from build123d.build_part import BuildPart
|
|
from build123d.build_sketch import BuildSketch
|
|
from build123d.exporters3d import export_stl
|
|
from build123d.objects_part import Box, Cylinder
|
|
from build123d.objects_sketch import Rectangle
|
|
from build123d.operations_part import extrude
|
|
from build123d.topology import Compound, Solid
|
|
from build123d.geometry import Axis, Color, Location, Vector, VectorLike
|
|
from build123d.mesher import Mesher
|
|
|
|
|
|
class DirectApiTestCase(unittest.TestCase):
|
|
def assertTupleAlmostEquals(
|
|
self,
|
|
first: tuple[float, ...],
|
|
second: tuple[float, ...],
|
|
places: int,
|
|
msg: str = None,
|
|
):
|
|
"""Check Tuples"""
|
|
self.assertEqual(len(second), len(first))
|
|
for i, j in zip(second, first):
|
|
self.assertAlmostEqual(i, j, places, msg=msg)
|
|
|
|
def assertVectorAlmostEquals(
|
|
self, first: Vector, second: VectorLike, places: int, msg: str = None
|
|
):
|
|
second_vector = Vector(second)
|
|
self.assertAlmostEqual(first.X, second_vector.X, places, msg=msg)
|
|
self.assertAlmostEqual(first.Y, second_vector.Y, places, msg=msg)
|
|
self.assertAlmostEqual(first.Z, second_vector.Z, places, msg=msg)
|
|
|
|
|
|
class TestProperties(unittest.TestCase):
|
|
def test_version(self):
|
|
exporter = Mesher()
|
|
self.assertEqual(exporter.library_version, "2.3.1")
|
|
|
|
def test_units(self):
|
|
for unit in Unit:
|
|
exporter = Mesher(unit=unit)
|
|
exporter.add_shape(Solid.make_box(1, 1, 1))
|
|
exporter.write("test.3mf")
|
|
importer = Mesher()
|
|
_shape = importer.read("test.3mf")
|
|
self.assertEqual(unit, importer.model_unit)
|
|
|
|
def test_vertex_and_triangle_counts(self):
|
|
exporter = Mesher()
|
|
exporter.add_shape(Solid.make_box(1, 1, 1))
|
|
self.assertEqual(exporter.vertex_counts[0], 8)
|
|
self.assertEqual(exporter.triangle_counts[0], 12)
|
|
|
|
def test_mesh_counts(self):
|
|
exporter = Mesher()
|
|
exporter.add_shape(Solid.make_box(1, 1, 1))
|
|
exporter.add_shape(Solid.make_cone(1, 0, 2))
|
|
self.assertEqual(exporter.mesh_count, 2)
|
|
|
|
|
|
class TestMetaData(unittest.TestCase):
|
|
def test_add_meta_data(self):
|
|
exporter = Mesher()
|
|
exporter.add_shape(Solid.make_box(1, 1, 1))
|
|
exporter.add_meta_data("test_space", "test0", "some data", "str", True)
|
|
exporter.add_meta_data("test_space", "test1", "more data", "str", True)
|
|
exporter.write("test.3mf")
|
|
importer = Mesher()
|
|
_shape = importer.read("test.3mf")
|
|
imported_meta_data: list[dict] = importer.get_meta_data()
|
|
self.assertEqual(imported_meta_data[0]["name_space"], "test_space")
|
|
self.assertEqual(imported_meta_data[0]["name"], "test0")
|
|
self.assertEqual(imported_meta_data[0]["value"], "some data")
|
|
self.assertEqual(imported_meta_data[0]["type"], "str")
|
|
self.assertEqual(imported_meta_data[1]["name_space"], "test_space")
|
|
self.assertEqual(imported_meta_data[1]["name"], "test1")
|
|
self.assertEqual(imported_meta_data[1]["value"], "more data")
|
|
self.assertEqual(imported_meta_data[1]["type"], "str")
|
|
|
|
def test_add_code(self):
|
|
exporter = Mesher()
|
|
exporter.add_shape(Solid.make_box(1, 1, 1))
|
|
exporter.add_code_to_metadata()
|
|
exporter.write("test.3mf")
|
|
importer = Mesher()
|
|
_shape = importer.read("test.3mf")
|
|
source_code = importer.get_meta_data_by_key("build123d", "test_mesher.py")
|
|
self.assertEqual(len(source_code), 2)
|
|
self.assertEqual(source_code["type"], "python")
|
|
self.assertGreater(len(source_code["value"]), 10)
|
|
|
|
|
|
class TestMeshProperties(unittest.TestCase):
|
|
def test_properties(self):
|
|
# Note: MeshType.OTHER can't be used with a Solid shape
|
|
for mesh_type in [MeshType.MODEL, MeshType.SUPPORT, MeshType.SOLIDSUPPORT]:
|
|
with self.subTest("MeshTYpe", mesh_type=mesh_type):
|
|
exporter = Mesher()
|
|
if mesh_type != MeshType.SUPPORT:
|
|
test_uuid = uuid.uuid1()
|
|
else:
|
|
test_uuid = None
|
|
name = "test" + mesh_type.name
|
|
shape = Solid.make_box(1, 1, 1)
|
|
shape.label = name
|
|
exporter.add_shape(
|
|
shape,
|
|
mesh_type=mesh_type,
|
|
part_number=str(mesh_type.value),
|
|
uuid_value=test_uuid,
|
|
)
|
|
exporter.write("test.3mf")
|
|
importer = Mesher()
|
|
shape = importer.read("test.3mf")
|
|
self.assertEqual(shape[0].label, name)
|
|
self.assertEqual(importer.mesh_count, 1)
|
|
properties = importer.get_mesh_properties()
|
|
self.assertEqual(properties[0]["name"], name)
|
|
self.assertEqual(properties[0]["part_number"], str(mesh_type.value))
|
|
self.assertEqual(properties[0]["type"], mesh_type.name)
|
|
if mesh_type != MeshType.SUPPORT:
|
|
self.assertEqual(properties[0]["uuid"], str(test_uuid))
|
|
|
|
|
|
class TestAddShape(DirectApiTestCase):
|
|
def test_add_shape(self):
|
|
exporter = Mesher()
|
|
blue_shape = Solid.make_box(1, 1, 1)
|
|
blue_shape.color = Color("blue")
|
|
blue_shape.label = "blue"
|
|
red_shape = Solid.make_cone(1, 0, 2).locate(Location((0, -1, 0)))
|
|
red_shape.color = Color("red")
|
|
red_shape.label = "red"
|
|
exporter.add_shape([blue_shape, red_shape])
|
|
exporter.write("test.3mf")
|
|
importer = Mesher()
|
|
box, cone = importer.read("test.3mf")
|
|
self.assertVectorAlmostEquals(box.bounding_box().size, (1, 1, 1), 2)
|
|
self.assertVectorAlmostEquals(box.bounding_box().size, (1, 1, 1), 2)
|
|
self.assertEqual(len(box.clean().faces()), 6)
|
|
self.assertEqual(box.label, "blue")
|
|
self.assertEqual(cone.label, "red")
|
|
self.assertTupleAlmostEquals(box.color.to_tuple(), (0, 0, 1, 1), 5)
|
|
self.assertTupleAlmostEquals(cone.color.to_tuple(), (1, 0, 0, 1), 5)
|
|
|
|
def test_add_compound(self):
|
|
exporter = Mesher()
|
|
box = Solid.make_box(1, 1, 1)
|
|
cone = Solid.make_cone(1, 0, 2).locate(Location((0, -1, 0)))
|
|
shape_assembly = Compound([box, cone])
|
|
exporter.add_shape(shape_assembly)
|
|
exporter.write("test.3mf")
|
|
importer = Mesher()
|
|
shapes = importer.read("test.3mf")
|
|
self.assertEqual(importer.mesh_count, 2)
|
|
|
|
|
|
class TestErrorChecking(unittest.TestCase):
|
|
def test_read_invalid_file(self):
|
|
with self.assertRaises(ValueError):
|
|
importer = Mesher()
|
|
importer.read("unknown_file.type")
|
|
|
|
def test_write_invalid_file(self):
|
|
exporter = Mesher()
|
|
exporter.add_shape(Solid.make_box(1, 1, 1))
|
|
with self.assertRaises(ValueError):
|
|
exporter.write("unknown_file.type")
|
|
|
|
|
|
class TestDuplicateVertices(unittest.TestCase):
|
|
def test_duplicate_vertices(self):
|
|
with BuildPart() as funky_box:
|
|
Box(3, 3, 3)
|
|
with BuildSketch(funky_box.faces().sort_by(Axis.Z)[-1]):
|
|
Rectangle(1, 2, rotation=45)
|
|
extrude(amount=1)
|
|
|
|
exporter = Mesher(unit=Unit.CM)
|
|
exporter.add_shape(funky_box.part) # dual vertices raise exception here
|
|
|
|
|
|
class TestHollowImport(unittest.TestCase):
|
|
def test_hollow_import(self):
|
|
test_shape = Cylinder(5, 10) - Box(5, 5, 5)
|
|
export_stl(test_shape, "test.stl")
|
|
importer = Mesher()
|
|
stl = importer.read("test.stl")
|
|
self.assertTrue(stl[0].is_valid())
|
|
|
|
|
|
class TestImportDegenerateTriangles(unittest.TestCase):
|
|
def test_degenerate_import(self):
|
|
"""Some STLs may contain 'triangles' where all three points are on a line"""
|
|
importer = Mesher()
|
|
try:
|
|
stl = importer.read("tests/cyl_w_rect_hole.stl")[0]
|
|
except:
|
|
stl = importer.read("cyl_w_rect_hole.stl")[0]
|
|
self.assertEqual(type(stl), Solid)
|
|
self.assertTrue(stl.is_manifold)
|
|
self.assertTrue(stl.is_valid())
|
|
self.assertEqual(sum(f.area == 0 for f in stl.faces()), 0)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|