diff --git a/src/build123d/mesher.py b/src/build123d/mesher.py index 6e4d7b35..ce3e2402 100644 --- a/src/build123d/mesher.py +++ b/src/build123d/mesher.py @@ -82,6 +82,7 @@ # pylint: disable=no-name-in-module, import-error import copy import ctypes +import itertools import math import os import sys @@ -96,9 +97,12 @@ BRepBuilderAPI_MakeSolid, BRepBuilderAPI_Sewing, ) +from OCP.BRepGProp import BRepGProp from OCP.BRepMesh import BRepMesh_IncrementalMesh from OCP.gp import gp_Pnt import OCP.TopAbs as ta +from OCP.GProp import GProp_GProps +from OCP.ShapeFix import ShapeFix_Shape from OCP.TopAbs import TopAbs_ShapeEnum from OCP.TopExp import TopExp_Explorer from OCP.TopoDS import TopoDS_Compound @@ -106,8 +110,8 @@ from py_lib3mf import Lib3MF from build123d.build_enums import MeshType, Unit -from build123d.geometry import Color, TOLERANCE -from build123d.topology import Compound, Shape, Shell, Solid, downcast +from build123d.geometry import Color, TOLERANCE, Vector +from build123d.topology import Compound, Face, Shape, Shell, Solid, downcast class Mesher: @@ -455,8 +459,11 @@ def _get_shape(self, mesh_3mf: Lib3MF.MeshObject) -> Shape: # Create the triangular face using the polygon polygon_builder = BRepBuilderAPI_MakePolygon(*ocp_vertices, Close=True) face_builder = BRepBuilderAPI_MakeFace(polygon_builder.Wire()) - # Add new Face to Shell - shell_builder.Add(face_builder.Face()) + facet = face_builder.Face() + facet_properties = GProp_GProps() + BRepGProp.SurfaceProperties_s(facet, facet_properties) + if facet_properties.Mass() != 0: # Area==0 is an invalid facet + shell_builder.Add(facet) # Create the Shell(s) - if the object has voids there will be multiple shell_builder.Perform() diff --git a/tests/cyl_w_rect_hole.stl b/tests/cyl_w_rect_hole.stl new file mode 100644 index 00000000..11d198ff Binary files /dev/null and b/tests/cyl_w_rect_hole.stl differ diff --git a/tests/test_mesher.py b/tests/test_mesher.py index 49bc42a7..f195ac3a 100644 --- a/tests/test_mesher.py +++ b/tests/test_mesher.py @@ -190,5 +190,16 @@ def test_hollow_import(self): 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() + 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()