From 97088d6f3c0656c6d9984a06b045facbca0501df Mon Sep 17 00:00:00 2001 From: gumyr Date: Sat, 13 Jan 2024 11:06:09 -0500 Subject: [PATCH] Fix import of invalid triangles Issue #472 --- src/build123d/mesher.py | 15 +++++++++++---- tests/cyl_w_rect_hole.stl | Bin 0 -> 39084 bytes tests/test_mesher.py | 11 +++++++++++ 3 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 tests/cyl_w_rect_hole.stl diff --git a/src/build123d/mesher.py b/src/build123d/mesher.py index 6e4d7b3..ce3e240 100644 --- a/src/build123d/mesher.py +++ b/src/build123d/mesher.py @@ -82,6 +82,7 @@ license: # pylint: disable=no-name-in-module, import-error import copy import ctypes +import itertools import math import os import sys @@ -96,9 +97,12 @@ from OCP.BRepBuilderAPI import ( 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 OCP.TopLoc import TopLoc_Location 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 @@ class Mesher: # 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 0000000000000000000000000000000000000000..11d198ff1527b85542040df519f707442987acc8 GIT binary patch literal 39084 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 hK2VB*iG$b>9k9C^VX8ppf>eQ8mmpOzaS)q<0RR