You can now connect, disconnect, and set flow directions of distribution ports in Blender.

This commit is contained in:
Dion Moult
2022-02-03 12:04:20 +11:00
parent d333cb1b78
commit 94ff8349d3
11 changed files with 145 additions and 2 deletions
@@ -23,14 +23,17 @@ classes = (
operator.AddPort,
operator.AddSystem,
operator.AssignSystem,
operator.ConnectPort,
operator.DisableEditingSystem,
operator.DisableSystemEditingUI,
operator.DisconnectPort,
operator.EditSystem,
operator.EnableEditingSystem,
operator.HidePorts,
operator.LoadSystems,
operator.RemoveSystem,
operator.SelectSystemProducts,
operator.SetFlowDirection,
operator.ShowPorts,
operator.UnassignSystem,
prop.System,
@@ -38,6 +41,7 @@ classes = (
ui.BIM_PT_systems,
ui.BIM_PT_object_systems,
ui.BIM_PT_ports,
ui.BIM_PT_port,
ui.BIM_UL_systems,
ui.BIM_UL_object_systems,
)
@@ -159,3 +159,39 @@ class AddPort(bpy.types.Operator, Operator):
def _execute(self, context):
core.add_port(tool.Ifc, tool.System, element=tool.Ifc.get_entity(context.active_object))
class ConnectPort(bpy.types.Operator, Operator):
bl_idname = "bim.connect_port"
bl_label = "Connect Ports"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
return len(context.selected_objects) == 2
def _execute(self, context):
obj1 = context.active_object
obj2 = context.selected_objects[0] if context.selected_objects[1] == obj1 else context.selected_objects[1]
core.connect_port(tool.Ifc, port1=tool.Ifc.get_entity(obj1), port2=tool.Ifc.get_entity(obj2))
class DisconnectPort(bpy.types.Operator, Operator):
bl_idname = "bim.disconnect_port"
bl_label = "Disconnect Ports"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
core.disconnect_port(tool.Ifc, port=tool.Ifc.get_entity(context.active_object))
class SetFlowDirection(bpy.types.Operator, Operator):
bl_idname = "bim.set_flow_direction"
bl_label = "Set Flow Direction"
bl_options = {"REGISTER", "UNDO"}
direction: bpy.props.StringProperty()
def _execute(self, context):
core.set_flow_direction(
tool.Ifc, tool.System, port=tool.Ifc.get_entity(context.active_object), direction=self.direction
)
@@ -156,6 +156,35 @@ class BIM_PT_ports(Panel):
row.operator("bim.add_port", icon="ADD", text="")
class BIM_PT_port(Panel):
bl_label = "IFC Port"
bl_idname = "BIM_PT_port"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
bl_parent_id = "BIM_PT_services_object"
@classmethod
def poll(cls, context):
if not context.active_object:
return False
element = tool.Ifc.get_entity(context.active_object)
if not element or not element.is_a("IfcPort"):
return False
return True
def draw(self, context):
self.props = context.scene.BIMSystemProperties
row = self.layout.row(align=True)
row.operator("bim.connect_port", icon="PLUGIN", text="")
row.operator("bim.disconnect_port", icon="UNLINKED", text="")
row.operator("bim.set_flow_direction", icon="FORWARD", text="").direction = "SOURCE"
row.operator("bim.set_flow_direction", icon="BACK", text="").direction = "SINK"
row.operator("bim.set_flow_direction", icon="ARROW_LEFTRIGHT", text="").direction = "SOURCEANDSINK"
row.operator("bim.set_flow_direction", icon="RESTRICT_INSTANCED_ON", text="").direction = "NOTDEFINED"
class BIM_UL_systems(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
system_icons = {
+15
View File
@@ -81,3 +81,18 @@ def add_port(ifc, system, element=None):
obj = system.create_empty_at_cursor_with_element_orientation(element)
port = system.run_root_assign_class(obj=obj, ifc_class="IfcDistributionPort")
ifc.run("system.assign_port", element=element, port=port)
def connect_port(ifc, port1=None, port2=None):
ifc.run("system.connect_port", port1=port1, port2=port2)
def disconnect_port(ifc, port=None):
ifc.run("system.disconnect_port", port=port)
def set_flow_direction(ifc, system, port=None, direction=None):
port2 = system.get_connected_port(port)
if not port2:
return
ifc.run("system.connect_port", port1=port, port2=port2, direction=direction)
+1
View File
@@ -390,6 +390,7 @@ class System:
def disable_system_editing_ui(cls): pass
def enable_system_editing_ui(cls): pass
def export_system_attributes(cls): pass
def get_connected_port(cls, port): pass
def get_ports(cls, element): pass
def import_system_attributes(cls, system): pass
def import_systems(cls): pass
+4
View File
@@ -56,6 +56,10 @@ class System(blenderbim.core.tool.System):
def export_system_attributes(cls):
return blenderbim.bim.helper.export_attributes(bpy.context.scene.BIMSystemProperties.system_attributes)
@classmethod
def get_connected_port(cls, port):
return ifcopenshell.util.system.get_connected_port(port)
@classmethod
def get_ports(cls, element):
return ifcopenshell.util.system.get_ports(element)
+11
View File
@@ -113,3 +113,14 @@ class TestAddPort:
system.run_root_assign_class(obj="obj", ifc_class="IfcDistributionPort").should_be_called().will_return("port")
ifc.run("system.assign_port", element="element", port="port").should_be_called()
subject.add_port(ifc, system, element="element")
class TestSetFlowDirection:
def test_run(self, ifc, system):
system.get_connected_port("port").should_be_called().will_return("port2")
ifc.run("system.connect_port", port1="port", port2="port2", direction="direction").should_be_called()
subject.set_flow_direction(ifc, system, port="port", direction="direction")
def test_do_not_set_a_direction_if_port_is_not_connected(self, ifc, system):
system.get_connected_port("port").should_be_called().will_return(None)
subject.set_flow_direction(ifc, system, port="port", direction="direction")
Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

+11 -2
View File
@@ -36,9 +36,8 @@ class TestCreateEmptyAtCursorWithElementOrientation(NewFile):
obj = bpy.data.objects.new("Object", None)
element = ifc.createIfcWall()
tool.Ifc.link(element, obj)
bpy.context.scene.cursor.matrix.translation[0] = 5
obj = subject.create_empty_at_cursor_with_element_orientation(element)
assert obj.matrix_world.translation[0] == 5
assert obj.matrix_world == bpy.context.scene.cursor.matrix
class TestDeleteElementObjects(NewFile):
@@ -83,6 +82,16 @@ class TestExportSystemAttributes(NewFile):
}
class TestGetConnectedPort(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
port1 = ifcopenshell.api.run("system.add_port", ifc)
port2 = ifcopenshell.api.run("system.add_port", ifc)
ifcopenshell.api.run("system.connect_port", ifc, port1=port1, port2=port2)
assert subject.get_connected_port(port1) == port2
assert subject.get_connected_port(port2) == port1
class TestGetPorts(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
@@ -43,3 +43,28 @@ def get_ports(element):
for rel in element.HasPorts or []:
results.append(rel.RelatingPort)
return results
def get_connected_port(port):
for rel in port.ConnectedTo:
return rel.RelatedPort
for rel in port.ConnectedFrom:
return rel.RelatingPort
def get_connected_to(element):
# Note: this code is for IFC2X3. IFC4 has a different approach.
results = []
for rel in element.HasPorts:
for rel2 in rel.RelatingPort.ConnectedTo:
results.extend([r.RelatedElement for r in rel2.RelatedPort.ContainedIn if r.RelatedElement != element])
return results
def get_connected_from(element):
# Note: this code is for IFC2X3. IFC4 has a different approach.
results = []
for rel in element.HasPorts:
for rel2 in rel.RelatingPort.ConnectedFrom:
results.extend([r.RelatedElement for r in rel2.RelatingPort.ContainedIn if r.RelatedElement != element])
return results
@@ -61,3 +61,12 @@ class TestGetPortsIFC2X3(test.bootstrap.IFC2X3):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcFlowSegment")
ifcopenshell.api.run("system.assign_port", self.file, element=element, port=port)
assert subject.get_ports(element) == [port]
class TestGetConnectedPort(test.bootstrap.IFC4):
def test_run(self):
port1 = ifcopenshell.api.run("system.add_port", self.file)
port2 = ifcopenshell.api.run("system.add_port", self.file)
ifcopenshell.api.run("system.connect_port", self.file, port1=port1, port2=port2)
assert subject.get_connected_port(port1) == port2
assert subject.get_connected_port(port2) == port1