mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 09:21:46 +00:00
Support faceset face with voids in shape_builder
This commit is contained in:
@@ -1404,7 +1404,7 @@ class ShapeBuilder:
|
||||
return self.file.createIfcTriangulatedFaceSet(Coordinates=ifc_points, CoordIndex=ifc_faces)
|
||||
|
||||
def polygonal_face_set(
|
||||
self, points: SequenceOfVectors, faces: Sequence[Sequence[int]]
|
||||
self, points: SequenceOfVectors, faces: Sequence[Union[Sequence[int], Sequence[Sequence[int]]]]
|
||||
) -> ifcopenshell.entity_instance:
|
||||
"""
|
||||
Generate an IfcPolygonalFaceSet
|
||||
@@ -1413,10 +1413,33 @@ class ShapeBuilder:
|
||||
|
||||
:param points: list of 3d coordinates
|
||||
:param faces: list of faces consisted of point indices (points indices starting from 0)
|
||||
in case of multiple sequences per face, the subsequent ones are inner voids
|
||||
:return: IfcPolygonalFaceSet
|
||||
"""
|
||||
|
||||
def is_sequence_of_ints(x):
|
||||
return isinstance(x, Sequence) and not isinstance(x, (str, bytes)) and all(isinstance(el, int) for el in x)
|
||||
|
||||
def is_sequence_of_sequence_of_ints(x):
|
||||
return (
|
||||
isinstance(x, Sequence) and not isinstance(x, (str, bytes)) and all(is_sequence_of_ints(el) for el in x)
|
||||
)
|
||||
|
||||
def incr(face):
|
||||
return [i + 1 for i in face]
|
||||
|
||||
if not all(is_sequence_of_ints(f) or is_sequence_of_sequence_of_ints(f) for f in faces):
|
||||
raise ValueError("Expected a sequence of int or sequence of sequence of int for each face")
|
||||
|
||||
ifc_points = self.file.createIfcCartesianPointList3D(ifc_safe_vector_type(points))
|
||||
ifc_faces = [self.file.createIfcIndexedPolygonalFace([i + 1 for i in face]) for face in faces]
|
||||
ifc_faces = [
|
||||
(
|
||||
self.file.createIfcIndexedPolygonalFace(incr(face))
|
||||
if is_sequence_of_ints(face)
|
||||
else self.file.createIfcIndexedPolygonalFaceWithVoids(incr(face[0]), list(map(incr, face[1:])))
|
||||
)
|
||||
for face in faces
|
||||
]
|
||||
return self.file.createIfcPolygonalFaceSet(Coordinates=ifc_points, Faces=ifc_faces)
|
||||
|
||||
def extrude_face_set(
|
||||
|
||||
@@ -373,3 +373,55 @@ class TestCalculateTransitions(test.bootstrap.IFC4):
|
||||
# method C
|
||||
params["offset"][0] = 10.0
|
||||
self.calculate_and_test(params, None)
|
||||
|
||||
|
||||
class TestFaceset(test.bootstrap.IFC4):
|
||||
@pytest.mark.parametrize("with_inner", [False, True])
|
||||
def test_polygonal_face_set_simple_and_with_voids(self, with_inner):
|
||||
self.builder = ShapeBuilder(self.file)
|
||||
|
||||
v0 = (0.0, 0.0, 0.0)
|
||||
v1 = (4.0, 0.0, 0.0)
|
||||
v2 = (4.0, 4.0, 0.0)
|
||||
v3 = (0.0, 4.0, 0.0)
|
||||
|
||||
v4 = (1.0, 1.0, 0.0)
|
||||
v5 = (3.0, 1.0, 0.0)
|
||||
v6 = (3.0, 3.0, 0.0)
|
||||
v7 = (1.0, 3.0, 0.0)
|
||||
|
||||
if with_inner:
|
||||
points = [v0, v1, v2, v3, v4, v5, v6, v7]
|
||||
|
||||
faces = [
|
||||
[[0, 1, 2, 3], [4, 5, 6, 7]], # outer loop with inner hole
|
||||
]
|
||||
else:
|
||||
points = [v0, v1, v2, v3]
|
||||
|
||||
faces = [[0, 1, 2, 3]] # only outer loop
|
||||
|
||||
result = self.builder.polygonal_face_set(points, faces)
|
||||
|
||||
assert result.is_a("IfcPolygonalFaceSet")
|
||||
assert result.Coordinates.is_a("IfcCartesianPointList3D")
|
||||
assert len(result.Faces) == 1
|
||||
if with_inner:
|
||||
assert result.Faces[0].is_a("IfcIndexedPolygonalFaceWithVoids")
|
||||
else:
|
||||
assert result.Faces[0].is_a("IfcIndexedPolygonalFace")
|
||||
|
||||
shp = ifcopenshell.geom.create_shape(ifcopenshell.geom.settings(), result)
|
||||
if with_inner:
|
||||
assert ifcopenshell.util.shape.get_area(shp) == pytest.approx(12.0)
|
||||
else:
|
||||
assert ifcopenshell.util.shape.get_area(shp) == pytest.approx(16.0)
|
||||
|
||||
def test_polygonal_face_set_invalid_face_types(self):
|
||||
self.builder = ShapeBuilder(self.file)
|
||||
with pytest.raises(ValueError, match="Expected a sequence of int or sequence of sequence of int"):
|
||||
self.builder.polygonal_face_set([], ["123"])
|
||||
with pytest.raises(ValueError, match="Expected a sequence of int or sequence of sequence of int"):
|
||||
self.builder.polygonal_face_set([], [[1.0, 2.0, 3.0]])
|
||||
with pytest.raises(ValueError, match="Expected a sequence of int or sequence of sequence of int"):
|
||||
self.builder.polygonal_face_set([], [[[[1, 2], 3], [4, 5, 6]]])
|
||||
|
||||
Reference in New Issue
Block a user