diff --git a/src/blenderbim/blenderbim/bim/module/brick/__init__.py b/src/blenderbim/blenderbim/bim/module/brick/__init__.py index 26eaf4fb8d..f248848480 100644 --- a/src/blenderbim/blenderbim/bim/module/brick/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/brick/__init__.py @@ -21,6 +21,7 @@ from . import ui, prop, operator classes = ( operator.AddBrick, + operator.AddBrickFeed, operator.AssignBrickReference, operator.CloseBrickProject, operator.ConvertBrickProject, diff --git a/src/blenderbim/blenderbim/bim/module/brick/operator.py b/src/blenderbim/blenderbim/bim/module/brick/operator.py index 6cee0af3ad..dd5cf9eef3 100644 --- a/src/blenderbim/blenderbim/bim/module/brick/operator.py +++ b/src/blenderbim/blenderbim/bim/module/brick/operator.py @@ -118,7 +118,7 @@ class AddBrick(bpy.types.Operator, Operator): props = context.scene.BIMBrickProperties library = None if props.libraries: - library=tool.Ifc.get().by_id(int(props.libraries)) + library = tool.Ifc.get().by_id(int(props.libraries)) core.add_brick( tool.Ifc, tool.Brick, @@ -127,3 +127,17 @@ class AddBrick(bpy.types.Operator, Operator): brick_class=props.brick_equipment_class, library=library, ) + + +class AddBrickFeed(bpy.types.Operator, Operator): + bl_idname = "bim.add_brick_feed" + bl_label = "Add Brick Feed" + bl_options = {"REGISTER", "UNDO"} + + def _execute(self, context): + core.add_brick_feed( + tool.Ifc, + tool.Brick, + source=[o for o in context.selected_objects if o != context.active_object][0], + destination=context.active_object, + ) diff --git a/src/blenderbim/blenderbim/bim/module/brick/ui.py b/src/blenderbim/blenderbim/bim/module/brick/ui.py index 1ebebdf9d1..89411f5fae 100644 --- a/src/blenderbim/blenderbim/bim/module/brick/ui.py +++ b/src/blenderbim/blenderbim/bim/module/brick/ui.py @@ -49,6 +49,8 @@ class BIM_PT_brickschema(Panel): row.prop(self.props, "namespace", text="") row.prop(self.props, "brick_equipment_class", text="") row.operator("bim.add_brick", text="", icon="ADD") + row.operator("bim.add_brick_feed", text="", icon="PLUGIN") + self.layout.template_list("BIM_UL_bricks", "", self.props, "bricks", self.props, "active_brick_index") for attribute in BrickschemaData.data["attributes"]: diff --git a/src/blenderbim/blenderbim/core/brick.py b/src/blenderbim/blenderbim/core/brick.py index fc03bb3724..072763f358 100644 --- a/src/blenderbim/blenderbim/core/brick.py +++ b/src/blenderbim/blenderbim/core/brick.py @@ -73,3 +73,9 @@ def add_brick(ifc, brick, obj=None, namespace=None, brick_class=None, library=No brick_uri = brick.add_brick(product, namespace, brick_class) if library: brick.run_assign_brick_reference(obj=obj, library=library, brick_uri=brick_uri) + + +def add_brick_feed(ifc, brick, source=None, destination=None): + source_element = ifc.get_entity(source) + destination_element = ifc.get_entity(destination) + brick.add_feed(brick.get_brick(source_element), brick.get_brick(destination_element)) diff --git a/src/blenderbim/blenderbim/core/tool.py b/src/blenderbim/blenderbim/core/tool.py index 719314af71..6786ac1590 100644 --- a/src/blenderbim/blenderbim/core/tool.py +++ b/src/blenderbim/blenderbim/core/tool.py @@ -47,9 +47,11 @@ class Brick: def add_brick_breadcrumb(cls): pass def add_brickifc_project(cls, namespace): pass def add_brickifc_reference(cls, brick, element, project): pass + def add_feed(cls, source, destination): pass def clear_brick_browser(cls): pass def clear_project(cls): pass def export_brick_attributes(cls, brick_uri): pass + def get_brick(cls, element): pass def get_brick_path(cls): pass def get_brick_path_name(cls): pass def get_brickifc_project(cls): pass diff --git a/src/blenderbim/blenderbim/tool/brick.py b/src/blenderbim/blenderbim/tool/brick.py index ee5c014d6c..6386923358 100644 --- a/src/blenderbim/blenderbim/tool/brick.py +++ b/src/blenderbim/blenderbim/tool/brick.py @@ -79,6 +79,11 @@ class Brick(blenderbim.core.tool.Brick): ) ) + @classmethod + def add_feed(cls, source, destination): + ns_brick = Namespace("https://brickschema.org/schema/Brick#") + BrickStore.graph.add((URIRef(source), ns_brick["feeds"], URIRef(destination))) + @classmethod def clear_brick_browser(cls): bpy.context.scene.BIMBrickProperties.bricks.clear() @@ -96,6 +101,15 @@ class Brick(blenderbim.core.tool.Brick): else: return {"Identification": brick_uri, "Name": brick_uri.split("#")[-1]} + @classmethod + def get_brick(cls, element): + for rel in element.HasAssociations: + if rel.is_a("IfcRelAssociatesLibrary"): + if tool.Ifc.get_schema() == "IFC2X3" and "#" in rel.RelatingLibrary.ItemReference: + return rel.RelatingLibrary.ItemReference + if tool.Ifc.get_schema() != "IFC2X3" and "#" in rel.RelatingLibrary.Identification: + return rel.RelatingLibrary.Identification + @classmethod def get_brick_path(cls): return BrickStore.path diff --git a/src/blenderbim/test/bim/feature/brick.feature b/src/blenderbim/test/bim/feature/brick.feature index 582bc4ca3d..4913d5bc3f 100644 --- a/src/blenderbim/test/bim/feature/brick.feature +++ b/src/blenderbim/test/bim/feature/brick.feature @@ -74,3 +74,25 @@ Scenario: Add brick - with a brick IFC library And I set "scene.BIMBrickProperties.namespace" to "http://example.org/digitaltwin#" When I press "bim.add_brick" Then nothing happens + +Scenario: Add brick feed + Given an empty IFC project + And I press "bim.load_brick_project(filepath='{cwd}/test/files/spaces.ttl')" + And I press "bim.convert_brick_project" + And I set "scene.BIMBrickProperties.namespace" to "http://example.org/digitaltwin#" + And I add a cube + And the object "Cube" is selected + And I set "scene.BIMRootProperties.ifc_class" to "IfcUnitaryEquipment" + And I set "scene.BIMRootProperties.ifc_predefined_type" to "AIRHANDLER" + And I press "bim.assign_class" + And I press "bim.add_brick" + And I add a cube + And the object "Cube" is selected + And I set "scene.BIMRootProperties.ifc_class" to "IfcAirTerminalBox" + And I set "scene.BIMRootProperties.ifc_predefined_type" to "VARIABLEFLOWPRESSUREDEPENDANT" + And I press "bim.assign_class" + And I press "bim.add_brick" + And the object "IfcUnitaryEquipment/Cube" is selected + And additionally the object "IfcAirTerminalBox/Cube" is selected + When I press "bim.add_brick_feed" + Then nothing happens diff --git a/src/blenderbim/test/core/test_brick.py b/src/blenderbim/test/core/test_brick.py index 72935640bb..f3963dc63b 100644 --- a/src/blenderbim/test/core/test_brick.py +++ b/src/blenderbim/test/core/test_brick.py @@ -126,3 +126,13 @@ class TestAddBrick: brick.add_brick("product", "namespace", "brick_class").should_be_called().will_return("brick_uri") brick.run_assign_brick_reference(obj="obj", library="library", brick_uri="brick_uri").should_be_called() subject.add_brick(ifc, brick, obj="obj", namespace="namespace", brick_class="brick_class", library="library") + + +class TestAddBrickFeed: + def test_run(self, ifc, brick): + ifc.get_entity("source").should_be_called().will_return("source_element") + ifc.get_entity("destination").should_be_called().will_return("destination_element") + brick.get_brick("source_element").should_be_called().will_return("source_brick") + brick.get_brick("destination_element").should_be_called().will_return("destination_brick") + brick.add_feed("source_brick", "destination_brick").should_be_called() + subject.add_brick_feed(ifc, brick, source="source", destination="destination") diff --git a/src/blenderbim/test/tool/test_brick.py b/src/blenderbim/test/tool/test_brick.py index b16d61b830..0d0baa4ecc 100644 --- a/src/blenderbim/test/tool/test_brick.py +++ b/src/blenderbim/test/tool/test_brick.py @@ -121,6 +121,21 @@ class TestAddBrickifcReference(NewFile): ) +class TestAddFeed(NewFile): + def test_run(self): + BrickStore.graph = brickschema.Graph() + subject.add_feed("http://example.org/digitaltwin#source", "http://example.org/digitaltwin#destination") + assert list( + BrickStore.graph.triples( + ( + URIRef("http://example.org/digitaltwin#source"), + URIRef("https://brickschema.org/schema/Brick#feeds"), + URIRef("http://example.org/digitaltwin#destination"), + ) + ) + ) + + class TestClearBrickBrowser(NewFile): def test_run(self): bpy.context.scene.BIMBrickProperties.bricks.add() @@ -141,11 +156,34 @@ class TestClearProject(NewFile): class TestExportBrickAttributes(NewFile): def test_run(self): - assert subject.export_brick_attributes("http://example.org/digitaltwin#floor") == {"Identification": "http://example.org/digitaltwin#floor", "Name": "floor"} + assert subject.export_brick_attributes("http://example.org/digitaltwin#floor") == { + "Identification": "http://example.org/digitaltwin#floor", + "Name": "floor", + } def test_run_ifc2x3(self): tool.Ifc.set(ifcopenshell.file(schema="IFC2X3")) - assert subject.export_brick_attributes("http://example.org/digitaltwin#floor") == {"ItemReference": "http://example.org/digitaltwin#floor", "Name": "floor"} + assert subject.export_brick_attributes("http://example.org/digitaltwin#floor") == { + "ItemReference": "http://example.org/digitaltwin#floor", + "Name": "floor", + } + + +class TestGetBrick(NewFile): + def test_run(self): + ifc = ifcopenshell.file() + element = ifc.createIfcChiller() + library = ifc.createIfcLibraryReference(Identification="http://example.org/digitaltwin#globalid") + ifc.createIfcRelAssociatesLibrary(RelatedObjects=[element], RelatingLibrary=library) + assert subject.get_brick(element) == "http://example.org/digitaltwin#globalid" + + def test_run_ifc2x3(self): + ifc = ifcopenshell.file(schema="IFC2X3") + tool.Ifc.set(ifc) + element = ifc.createIfcEnergyConversionDevice() + library = ifc.createIfcLibraryReference(ItemReference="http://example.org/digitaltwin#globalid") + ifc.createIfcRelAssociatesLibrary(RelatedObjects=[element], RelatingLibrary=library) + assert subject.get_brick(element) == "http://example.org/digitaltwin#globalid" class TestGetBrickPath(NewFile): @@ -180,7 +218,9 @@ class TestGetLibraryBrickReference(NewFile): def test_run(self): ifc = ifcopenshell.file() library = ifc.createIfcLibraryInformation() - reference = ifc.createIfcLibraryReference(Identification="http://example.org/digitaltwin#floor", ReferencedLibrary=library) + reference = ifc.createIfcLibraryReference( + Identification="http://example.org/digitaltwin#floor", ReferencedLibrary=library + ) assert subject.get_library_brick_reference(library, "http://example.org/digitaltwin#floor") == reference def test_run_ifc2x3(self):