mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 18:16:40 +00:00
You can now connect, disconnect, and set flow directions of distribution ports in Blender.
This commit is contained in:
@@ -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 = {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 |
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user