See #5919. API now has support for features, not just openings. Void module is renamed to feature.

This commit is contained in:
Dion Moult
2025-01-28 14:18:17 +11:00
parent 5300f4c78f
commit 03bfd828e2
21 changed files with 326 additions and 132 deletions
@@ -18,24 +18,44 @@
import numpy
import test.bootstrap
import ifcopenshell.api.void
import ifcopenshell.api.feature
import ifcopenshell.api.root
import ifcopenshell.api.unit
import ifcopenshell.api.geometry
class TestAddOpening(test.bootstrap.IFC4):
class TestAddFeature(test.bootstrap.IFC4X3):
def test_adding_a_surface_feature(self):
wall = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcWall")
feature = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcSurfaceFeature")
ifcopenshell.api.feature.add_feature(self.file, feature=feature, element=wall)
assert wall.HasSurfaceFeatures[0].RelatedSurfaceFeatures == (feature,)
class TestAddFeature(test.bootstrap.IFC4):
def test_adding_an_opening(self):
wall = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcWall")
opening = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcOpeningElement")
ifcopenshell.api.void.add_opening(self.file, opening=opening, element=wall)
ifcopenshell.api.feature.add_feature(self.file, feature=opening, element=wall)
assert wall.HasOpenings[0].RelatedOpeningElement == opening
def test_adding_a_projection(self):
wall = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcWall")
projection = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcProjectionElement")
ifcopenshell.api.feature.add_feature(self.file, feature=projection, element=wall)
assert wall.HasProjections[0].RelatedFeatureElement == projection
def test_adding_a_surface_feature(self):
wall = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcWall")
feature = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcSurfaceFeature")
ifcopenshell.api.feature.add_feature(self.file, feature=feature, element=wall)
assert wall.IsDecomposedBy[0].RelatedObjects == (feature,)
def test_adding_an_opening_twice(self):
wall = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcWall")
opening = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcOpeningElement")
ifcopenshell.api.void.add_opening(self.file, opening=opening, element=wall)
ifcopenshell.api.void.add_opening(self.file, opening=opening, element=wall)
ifcopenshell.api.feature.add_feature(self.file, feature=opening, element=wall)
ifcopenshell.api.feature.add_feature(self.file, feature=opening, element=wall)
assert wall.HasOpenings[0].RelatedOpeningElement == opening
assert len(wall.HasOpenings) == 1
@@ -43,8 +63,8 @@ class TestAddOpening(test.bootstrap.IFC4):
slab = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcSlab")
wall = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcWall")
opening = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcOpeningElement")
ifcopenshell.api.void.add_opening(self.file, opening=opening, element=slab)
ifcopenshell.api.void.add_opening(self.file, opening=opening, element=wall)
ifcopenshell.api.feature.add_feature(self.file, feature=opening, element=slab)
ifcopenshell.api.feature.add_feature(self.file, feature=opening, element=wall)
assert not slab.HasOpenings
assert wall.HasOpenings[0].RelatedOpeningElement == opening
@@ -63,7 +83,7 @@ class TestAddOpening(test.bootstrap.IFC4):
)
ifcopenshell.api.geometry.edit_object_placement(self.file, product=wall, matrix=matrix1.copy(), is_si=False)
ifcopenshell.api.geometry.edit_object_placement(self.file, product=opening, matrix=matrix1.copy(), is_si=False)
ifcopenshell.api.void.add_opening(self.file, opening=opening, element=wall)
ifcopenshell.api.feature.add_feature(self.file, feature=opening, element=wall)
assert opening.ObjectPlacement.PlacementRelTo.PlacesObject[0] == wall
assert numpy.array_equal(ifcopenshell.util.placement.get_local_placement(opening.ObjectPlacement), matrix1)
@@ -74,9 +94,10 @@ class TestAddOpening(test.bootstrap.IFC4):
opening = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcOpeningElement")
placement = self.file.createIfcGridPlacement()
opening.ObjectPlacement = placement
ifcopenshell.api.void.add_opening(self.file, opening=opening, element=wall)
ifcopenshell.api.feature.add_feature(self.file, feature=opening, element=wall)
assert opening.ObjectPlacement == placement
class TestAddOpeningIFC2X3(test.bootstrap.IFC2X3, TestAddOpening):
pass
class TestAddFeatureIFC2X3(test.bootstrap.IFC2X3, TestAddFeature):
def test_adding_a_surface_feature(self):
pass
@@ -17,7 +17,7 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import test.bootstrap
import ifcopenshell.api.void
import ifcopenshell.api.feature
import ifcopenshell.api.root
@@ -25,14 +25,14 @@ class TestAddFilling(test.bootstrap.IFC4):
def test_adding_a_filling(self):
opening = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcOpeningElement")
door = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcDoor")
ifcopenshell.api.void.add_filling(self.file, opening=opening, element=door)
ifcopenshell.api.feature.add_filling(self.file, opening=opening, element=door)
assert door.FillsVoids[0].RelatingOpeningElement == opening
def test_adding_a_filling_twice(self):
opening = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcOpeningElement")
door = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcDoor")
ifcopenshell.api.void.add_filling(self.file, opening=opening, element=door)
ifcopenshell.api.void.add_filling(self.file, opening=opening, element=door)
ifcopenshell.api.feature.add_filling(self.file, opening=opening, element=door)
ifcopenshell.api.feature.add_filling(self.file, opening=opening, element=door)
assert door.FillsVoids[0].RelatingOpeningElement == opening
assert len(opening.HasFillings) == 1
@@ -40,8 +40,8 @@ class TestAddFilling(test.bootstrap.IFC4):
door = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcDoor")
opening1 = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcOpeningElement")
opening2 = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcOpeningElement")
ifcopenshell.api.void.add_filling(self.file, opening=opening1, element=door)
ifcopenshell.api.void.add_filling(self.file, opening=opening2, element=door)
ifcopenshell.api.feature.add_filling(self.file, opening=opening1, element=door)
ifcopenshell.api.feature.add_filling(self.file, opening=opening2, element=door)
assert not opening1.HasFillings
assert opening2.HasFillings[0].RelatedBuildingElement == door
@@ -17,21 +17,21 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import test.bootstrap
import ifcopenshell.api.void
import ifcopenshell.api.feature
import ifcopenshell.api.root
class TestRemoveOpening(test.bootstrap.IFC4):
def test_removing_a_simple_opening(self):
opening = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcOpeningElement")
ifcopenshell.api.void.remove_opening(self.file, opening=opening)
class TestRemoveFeature(test.bootstrap.IFC4):
def test_removing_a_simple_feature(self):
feature = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcOpeningElement")
ifcopenshell.api.feature.remove_feature(self.file, feature=feature)
assert len(list(self.file)) == 0
def test_removing_an_opening_voiding_a_wall(self):
wall = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcWall")
opening = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcOpeningElement")
ifcopenshell.api.void.add_opening(self.file, opening=opening, element=wall)
ifcopenshell.api.void.remove_opening(self.file, opening=opening)
ifcopenshell.api.feature.add_feature(self.file, feature=opening, element=wall)
ifcopenshell.api.feature.remove_feature(self.file, feature=opening)
assert len(self.file.by_type("IfcOpeningElement")) == 0
assert len(self.file.by_type("IfcRelVoidsElement")) == 0
assert wall
@@ -40,9 +40,9 @@ class TestRemoveOpening(test.bootstrap.IFC4):
wall = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcWall")
opening = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcOpeningElement")
door = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcDoor")
ifcopenshell.api.void.add_opening(self.file, opening=opening, element=wall)
ifcopenshell.api.void.add_filling(self.file, opening=opening, element=door)
ifcopenshell.api.void.remove_opening(self.file, opening=opening)
ifcopenshell.api.feature.add_feature(self.file, feature=opening, element=wall)
ifcopenshell.api.feature.add_filling(self.file, opening=opening, element=door)
ifcopenshell.api.feature.remove_feature(self.file, feature=opening)
assert len(self.file.by_type("IfcOpeningElement")) == 0
assert len(self.file.by_type("IfcRelVoidsElement")) == 0
assert len(self.file.by_type("IfcRelFillsElement")) == 0
@@ -50,5 +50,5 @@ class TestRemoveOpening(test.bootstrap.IFC4):
assert door
class TestRemoveOpeningIFC2X3(test.bootstrap.IFC2X3, TestRemoveOpening):
class TestRemoveFeatureIFC2X3(test.bootstrap.IFC2X3, TestRemoveFeature):
pass
@@ -19,7 +19,7 @@
import numpy
import pytest
import test.bootstrap
import ifcopenshell.api.void
import ifcopenshell.api.feature
import ifcopenshell.api.root
import ifcopenshell.api.unit
import ifcopenshell.api.system
@@ -303,7 +303,7 @@ class TestEditObjectPlacement(test.bootstrap.IFC4):
(0.0, 0.0, 0.0, 1.0),
)
)
ifcopenshell.api.void.add_opening(self.file, opening=subelement, element=element)
ifcopenshell.api.feature.add_feature(self.file, feature=subelement, element=element)
ifcopenshell.api.geometry.edit_object_placement(self.file, product=element, matrix=matrix.copy(), is_si=False)
ifcopenshell.api.geometry.edit_object_placement(
self.file, product=subelement, matrix=submatrix.copy(), is_si=False
@@ -337,8 +337,8 @@ class TestEditObjectPlacement(test.bootstrap.IFC4):
(0.0, 0.0, 0.0, 1.0),
)
)
ifcopenshell.api.void.add_opening(self.file, opening=element, element=wall)
ifcopenshell.api.void.add_filling(self.file, element=subelement, opening=element)
ifcopenshell.api.feature.add_feature(self.file, feature=element, element=wall)
ifcopenshell.api.feature.add_filling(self.file, element=subelement, opening=element)
ifcopenshell.api.geometry.edit_object_placement(self.file, product=site, matrix=numpy.eye(4), is_si=False)
ifcopenshell.api.geometry.edit_object_placement(self.file, product=wall, matrix=numpy.eye(4), is_si=False)
ifcopenshell.api.geometry.edit_object_placement(self.file, product=element, matrix=matrix.copy(), is_si=False)
@@ -564,8 +564,8 @@ class TestEditObjectPlacement(test.bootstrap.IFC4):
shifted_submatrix = numpy.eye(4)
shifted_submatrix[:3, 3] = (1, 3, 5)
ifcopenshell.api.void.add_opening(self.file, opening=subelement, element=element)
ifcopenshell.api.void.add_filling(self.file, opening=subelement, element=subsubelement)
ifcopenshell.api.feature.add_feature(self.file, feature=subelement, element=element)
ifcopenshell.api.feature.add_filling(self.file, opening=subelement, element=subsubelement)
previous_placement_id = ifcopenshell.api.geometry.edit_object_placement(
self.file, product=element, matrix=matrix.copy(), is_si=False
).id()
@@ -21,7 +21,7 @@ import test.bootstrap
import ifcopenshell.api.unit
import ifcopenshell.api.type
import ifcopenshell.api.root
import ifcopenshell.api.void
import ifcopenshell.api.feature
import ifcopenshell.api.pset
import ifcopenshell.api.group
import ifcopenshell.api.system
@@ -134,7 +134,7 @@ class TestCopyClass(test.bootstrap.IFC4):
# IfcOpeningElement opening
wall = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcWall")
opening = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcOpeningElement")
ifcopenshell.api.void.add_opening(self.file, opening=opening, element=wall)
ifcopenshell.api.feature.add_feature(self.file, feature=opening, element=wall)
new = ifcopenshell.api.root.copy_class(self.file, product=wall)
assert wall.HasOpenings[0] != new.HasOpenings[0]
assert wall.HasOpenings[0].RelatedOpeningElement == opening
@@ -144,7 +144,7 @@ class TestCopyClass(test.bootstrap.IFC4):
# IfcVoidingFeature opening
plate = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcPlate")
opening = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcVoidingFeature")
ifcopenshell.api.void.add_opening(self.file, opening=opening, element=plate)
ifcopenshell.api.feature.add_feature(self.file, feature=opening, element=plate)
new = ifcopenshell.api.root.copy_class(self.file, product=plate)
assert plate.HasOpenings[0] != new.HasOpenings[0]
assert plate.HasOpenings[0].RelatedOpeningElement == opening
@@ -155,15 +155,15 @@ class TestCopyClass(test.bootstrap.IFC4):
wall = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcWall")
opening = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcOpeningElement")
window = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcWindow")
ifcopenshell.api.void.add_opening(self.file, opening=opening, element=wall)
ifcopenshell.api.void.add_filling(self.file, opening=opening, element=window)
ifcopenshell.api.feature.add_feature(self.file, feature=opening, element=wall)
ifcopenshell.api.feature.add_filling(self.file, opening=opening, element=window)
new = ifcopenshell.api.root.copy_class(self.file, product=wall)
assert not new.HasOpenings
def test_copying_an_opening_voiding_an_element(self):
wall = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcWall")
opening = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcOpeningElement")
ifcopenshell.api.void.add_opening(self.file, opening=opening, element=wall)
ifcopenshell.api.feature.add_feature(self.file, feature=opening, element=wall)
new = ifcopenshell.api.root.copy_class(self.file, product=opening)
assert opening.VoidsElements[0] != new.VoidsElements[0]
assert new.VoidsElements[0].RelatingBuildingElement == wall
@@ -171,14 +171,14 @@ class TestCopyClass(test.bootstrap.IFC4):
def test_copying_an_opening_with_a_filling(self):
door = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcDoor")
opening = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcOpeningElement")
ifcopenshell.api.void.add_filling(self.file, opening=opening, element=door)
ifcopenshell.api.feature.add_filling(self.file, opening=opening, element=door)
new = ifcopenshell.api.root.copy_class(self.file, product=opening)
assert not new.HasFillings
def test_copying_a_filling(self):
door = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcDoor")
opening = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcOpeningElement")
ifcopenshell.api.void.add_filling(self.file, opening=opening, element=door)
ifcopenshell.api.feature.add_filling(self.file, opening=opening, element=door)
new = ifcopenshell.api.root.copy_class(self.file, product=door)
assert not new.FillsVoids
@@ -20,7 +20,7 @@ import test.bootstrap
import ifcopenshell.api.unit
import ifcopenshell.api.nest
import ifcopenshell.api.root
import ifcopenshell.api.void
import ifcopenshell.api.feature
import ifcopenshell.api.grid
import ifcopenshell.api.type
import ifcopenshell.api.pset
@@ -159,7 +159,7 @@ class TestRemoveProduct(test.bootstrap.IFC4):
def test_removing_all_openings_of_an_element(self):
element = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcWall")
opening = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcOpeningElement")
ifcopenshell.api.void.add_opening(self.file, opening=opening, element=element)
ifcopenshell.api.feature.add_feature(self.file, feature=opening, element=element)
ifcopenshell.api.root.remove_product(self.file, product=element)
assert len(list(self.file)) == 0
assert len(self.file.by_type("IfcWall")) == 0
@@ -180,7 +180,7 @@ class TestRemoveProduct(test.bootstrap.IFC4):
element = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcWall")
total_entities = len(list(self.file))
opening = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcOpeningElement")
ifcopenshell.api.void.add_opening(self.file, opening=opening, element=element)
ifcopenshell.api.feature.add_feature(self.file, feature=opening, element=element)
ifcopenshell.api.root.remove_product(self.file, product=opening)
assert len(list(self.file)) == total_entities
assert len(self.file.by_type("IfcOpeningElement")) == 0
@@ -191,8 +191,8 @@ class TestRemoveProduct(test.bootstrap.IFC4):
opening = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcOpeningElement")
filling = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcDoor")
total_entities = len(list(self.file))
ifcopenshell.api.void.add_opening(self.file, opening=opening, element=element)
ifcopenshell.api.void.add_filling(self.file, opening=opening, element=filling)
ifcopenshell.api.feature.add_feature(self.file, feature=opening, element=element)
ifcopenshell.api.feature.add_filling(self.file, opening=opening, element=filling)
ifcopenshell.api.root.remove_product(self.file, product=filling)
assert len(list(self.file)) == total_entities
assert len(self.file.by_type("IfcDoor")) == 0
@@ -19,7 +19,7 @@
import ifcopenshell.api.profile
import pytest
import test.bootstrap
import ifcopenshell.api.void
import ifcopenshell.api.feature
import ifcopenshell.api.pset
import ifcopenshell.api.root
import ifcopenshell.api.type
@@ -841,8 +841,8 @@ class TestGetDecompositionIFC4(test.bootstrap.IFC4):
element = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcWall")
subelement = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcOpeningElement")
subsubelement = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcWindow")
ifcopenshell.api.void.add_opening(self.file, element=element, opening=subelement)
ifcopenshell.api.void.add_filling(self.file, element=subsubelement, opening=subelement)
ifcopenshell.api.feature.add_feature(self.file, element=element, feature=subelement)
ifcopenshell.api.feature.add_filling(self.file, element=subsubelement, opening=subelement)
results = subject.get_decomposition(element)
assert subelement in results
assert subsubelement in results