diff --git a/src/blenderbim/blenderbim/bim/module/layer/operator.py b/src/blenderbim/blenderbim/bim/module/layer/operator.py index c3c918e907..ccdf666d60 100644 --- a/src/blenderbim/blenderbim/bim/module/layer/operator.py +++ b/src/blenderbim/blenderbim/bim/module/layer/operator.py @@ -151,7 +151,10 @@ class AssignPresentationLayer(bpy.types.Operator): ifcopenshell.api.run( "layer.assign_layer", self.file, - **{"item": self.file.by_id(item.BIMMeshProperties.ifc_definition_id), "layer": self.file.by_id(self.layer)} + **{ + "items": [self.file.by_id(item.BIMMeshProperties.ifc_definition_id)], + "layer": self.file.by_id(self.layer), + } ) return {"FINISHED"} diff --git a/src/ifcopenshell-python/ifcopenshell/api/layer/assign_layer.py b/src/ifcopenshell-python/ifcopenshell/api/layer/assign_layer.py index 2cdcb58269..21a4ceb2bf 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/layer/assign_layer.py +++ b/src/ifcopenshell-python/ifcopenshell/api/layer/assign_layer.py @@ -20,8 +20,10 @@ import ifcopenshell class Usecase: - def __init__(self, file, item=None, layer=None): - """Assigns a representation item to a layer + def __init__( + self, file: ifcopenshell.file, items: list[ifcopenshell.entity_instance], layer: ifcopenshell.entity_instance + ): + """Assigns representation items to a layer In IFC, instead of objects being assigned to layers, representation items are assigned to layers. Representation items are portions of the @@ -30,9 +32,9 @@ class Usecase: its frame) assigned to one layer, and another portion (e.g. the glazing panels) assigned to another layer. - :param item: The IfcRepresentationItem to assign to the layer. This - should be one of the items in the object's IfcShapeRepresentation. - :type item: ifcopenshell.entity_instance.entity_instance + :param items: The list of IfcRepresentationItems to assign to the layer. This + should be the items from the object's IfcShapeRepresentation. + :type items: list[ifcopenshell.entity_instance.entity_instance] :param layer: The IfcPresentationLayerAssignment layer to assign the item to. :type layer: ifcopenshell.entity_instance.entity_instance @@ -62,15 +64,19 @@ class Usecase: # And assign our wall representation item (in this example, there is # only one item) to the layer. - ifcopenshell.api.run("layer.assign_layer", model, item=representation.Items[0], layer=layer) + ifcopenshell.api.run("layer.assign_layer", model, items=[representation.Items[0]], layer=layer) """ self.file = file self.settings = { - "item": item, + "items": items, "layer": layer, } - def execute(self): - assigned_items = set(self.settings["layer"].AssignedItems or []) - assigned_items.add(self.settings["item"]) - self.settings["layer"].AssignedItems = list(assigned_items) + def execute(self) -> None: + # support AssignedItems == None since layer might just got created + layer = self.settings["layer"] + assigned_items = set(layer.AssignedItems or []) + items = set(self.settings["items"]) + if items.issubset(assigned_items): + return + layer.AssignedItems = list(assigned_items | items) diff --git a/src/ifcopenshell-python/ifcopenshell/api/layer/unassign_layer.py b/src/ifcopenshell-python/ifcopenshell/api/layer/unassign_layer.py index 229c91a012..4cb941d35e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/layer/unassign_layer.py +++ b/src/ifcopenshell-python/ifcopenshell/api/layer/unassign_layer.py @@ -54,7 +54,7 @@ class Usecase: # And assign our wall representation item (in this example, there is # only one item) to the layer. - ifcopenshell.api.run("layer.assign_layer", model, item=representation.Items[0], layer=layer) + ifcopenshell.api.run("layer.assign_layer", model, items=[representation.Items[0]], layer=layer) # Let's undo it! ifcopenshell.api.run("layer.unassign_layer", model, item=representation.Items[0], layer=layer) diff --git a/src/ifcopenshell-python/test/api/layer/__init__.py b/src/ifcopenshell-python/test/api/layer/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/ifcopenshell-python/test/api/layer/test_assign_layer.py b/src/ifcopenshell-python/test/api/layer/test_assign_layer.py new file mode 100644 index 0000000000..f2d4eca384 --- /dev/null +++ b/src/ifcopenshell-python/test/api/layer/test_assign_layer.py @@ -0,0 +1,39 @@ +# IfcOpenShell - IFC toolkit and geometry engine +# Copyright (C) 2022 Dion Moult +# +# This file is part of IfcOpenShell. +# +# IfcOpenShell is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# IfcOpenShell is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with IfcOpenShell. If not, see . + +import test.bootstrap +import ifcopenshell.api + + +class TestAssignLayer(test.bootstrap.IFC4): + def test_assign_layer_to_items(self): + items = [self.file.createIfcExtrudedAreaSolid() for i in range(2)] + layer = self.file.createIfcPresentationLayerAssignment() + + ifcopenshell.api.run("layer.assign_layer", self.file, items=items, layer=layer) + assert len(layer.AssignedItems) == 2 + assert set(layer.AssignedItems) == set(items) + + def test_assign_additional_items(self): + items = [self.file.createIfcExtrudedAreaSolid() for i in range(4)] + layer = self.file.createIfcPresentationLayerAssignment() + + ifcopenshell.api.run("layer.assign_layer", self.file, items=items[:2], layer=layer) + ifcopenshell.api.run("layer.assign_layer", self.file, items=items[2:], layer=layer) + assert len(layer.AssignedItems) == 4 + assert set(layer.AssignedItems) == set(items) diff --git a/src/ifcopenshell-python/test/util/test_element.py b/src/ifcopenshell-python/test/util/test_element.py index aa685f1489..6e70c64473 100644 --- a/src/ifcopenshell-python/test/util/test_element.py +++ b/src/ifcopenshell-python/test/util/test_element.py @@ -634,7 +634,7 @@ class TestGetElementsByLayer(test.bootstrap.IFC4): layer = ifcopenshell.api.run("layer.add_layer", self.file) representation = self.file.createIfcShapeRepresentation() element.Representation = self.file.createIfcProductDefinitionShape(Representations=[representation]) - ifcopenshell.api.run("layer.assign_layer", self.file, item=representation, layer=layer) + ifcopenshell.api.run("layer.assign_layer", self.file, items=[representation], layer=layer) assert list(subject.get_elements_by_layer(self.file, layer)) == [element] @@ -644,7 +644,7 @@ class TestGetlayers(test.bootstrap.IFC4): layer = ifcopenshell.api.run("layer.add_layer", self.file) representation = self.file.createIfcShapeRepresentation() element.Representation = self.file.createIfcProductDefinitionShape(Representations=[representation]) - ifcopenshell.api.run("layer.assign_layer", self.file, item=representation, layer=layer) + ifcopenshell.api.run("layer.assign_layer", self.file, items=[representation], layer=layer) assert subject.get_layers(self.file, element) == [layer] def test_getting_the_layer_of_a_product_item(self): @@ -653,7 +653,7 @@ class TestGetlayers(test.bootstrap.IFC4): item = self.file.createIfcExtrudedAreaSolid() representation = self.file.createIfcShapeRepresentation(Items=[item]) element.Representation = self.file.createIfcProductDefinitionShape(Representations=[representation]) - ifcopenshell.api.run("layer.assign_layer", self.file, item=item, layer=layer) + ifcopenshell.api.run("layer.assign_layer", self.file, items=[item], layer=layer) assert subject.get_layers(self.file, element) == [layer] def test_getting_the_layer_of_a_type_product_representation(self): @@ -661,7 +661,7 @@ class TestGetlayers(test.bootstrap.IFC4): layer = ifcopenshell.api.run("layer.add_layer", self.file) representation = self.file.createIfcShapeRepresentation() element.RepresentationMaps = [self.file.createIfcRepresentationMap(MappedRepresentation=representation)] - ifcopenshell.api.run("layer.assign_layer", self.file, item=representation, layer=layer) + ifcopenshell.api.run("layer.assign_layer", self.file, items=[representation], layer=layer) assert subject.get_layers(self.file, element) == [layer] def test_getting_the_layer_of_a_type_product_item(self): @@ -670,7 +670,7 @@ class TestGetlayers(test.bootstrap.IFC4): item = self.file.createIfcExtrudedAreaSolid() representation = self.file.createIfcShapeRepresentation(Items=[item]) element.RepresentationMaps = [self.file.createIfcRepresentationMap(MappedRepresentation=representation)] - ifcopenshell.api.run("layer.assign_layer", self.file, item=item, layer=layer) + ifcopenshell.api.run("layer.assign_layer", self.file, items=[item], layer=layer) assert subject.get_layers(self.file, element) == [layer] @@ -681,7 +681,7 @@ class TestGetlayersIFC2X3(test.bootstrap.IFC2X3): item = self.file.createIfcExtrudedAreaSolid() representation = self.file.createIfcShapeRepresentation(Items=[item]) element.Representation = self.file.createIfcProductDefinitionShape(Representations=[representation]) - ifcopenshell.api.run("layer.assign_layer", self.file, item=item, layer=layer) + ifcopenshell.api.run("layer.assign_layer", self.file, items=[item], layer=layer) assert subject.get_layers(self.file, element) == [layer] def test_getting_the_layer_of_a_type_product_item(self): @@ -690,7 +690,7 @@ class TestGetlayersIFC2X3(test.bootstrap.IFC2X3): item = self.file.createIfcExtrudedAreaSolid() representation = self.file.createIfcShapeRepresentation(Items=[item]) element.RepresentationMaps = [self.file.createIfcRepresentationMap(MappedRepresentation=representation)] - ifcopenshell.api.run("layer.assign_layer", self.file, item=item, layer=layer) + ifcopenshell.api.run("layer.assign_layer", self.file, items=[item], layer=layer) assert subject.get_layers(self.file, element) == [layer]