adjust obstruction position on changing segment length

Now when you change the segment's length connected obstructions will adjust. Same for changing obstruction's length. You can also move obstructions/segment around and then use "Regenerate" to make them fit.
Small demo - https://imgur.com/a/atm8Mdx
This commit is contained in:
Andrej730
2023-08-02 16:28:45 +05:00
parent 92df1b351c
commit 45f81b4780
4 changed files with 74 additions and 14 deletions
@@ -41,6 +41,7 @@ float_is_zero = lambda f: 0.0001 >= f >= -0.0001
class FitFlowSegments(bpy.types.Operator, tool.Ifc.Operator): class FitFlowSegments(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.fit_flow_segments" bl_idname = "bim.fit_flow_segments"
bl_description = "Add a fitting based on currently selected elements and cursor"
bl_label = "Fit Flow Segments" bl_label = "Fit Flow Segments"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
@@ -135,24 +136,69 @@ class MEPGenerator:
self.file = tool.Ifc.get() self.file = tool.Ifc.get()
self.collection = bpy.context.view_layer.active_layer_collection.collection self.collection = bpy.context.view_layer.active_layer_collection.collection
element = tool.Ifc.get_entity(obj) segment = tool.Ifc.get_entity(obj)
representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW") representation = ifcopenshell.util.representation.get_representation(segment, "Model", "Body", "MODEL_VIEW")
extrusion = tool.Model.get_extrusion(representation) extrusion = tool.Model.get_extrusion(representation)
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
length = extrusion.Depth * si_conversion length = extrusion.Depth * si_conversion
end_port_matrix = Matrix.Translation((0, 0, length)) start_port_matrix = obj.matrix_world @ Matrix()
end_port_matrix = obj.matrix_world @ Matrix.Translation((0, 0, length))
ports = tool.System.get_ports(element) ports = tool.System.get_ports(segment)
if not ports: if segment.is_a("IfcFlowSegment") and not ports:
start_port_matrix = Matrix()
for mat in [start_port_matrix, end_port_matrix]: for mat in [start_port_matrix, end_port_matrix]:
port = tool.Ifc.run("system.add_port", element=element) # TODO: specify PredefinedType based on the segment type
port = tool.Ifc.run("system.add_port", element=segment)
port.FlowDirection = "NOTDEFINED" port.FlowDirection = "NOTDEFINED"
tool.Ifc.run("geometry.edit_object_placement", product=port, matrix=obj.matrix_world @ mat, is_si=True) tool.Ifc.run("geometry.edit_object_placement", product=port, matrix=mat, is_si=True)
return return
end_port = self.get_segment_data(element)["end_port"] # adjust current segment ports and related flow segments
tool.Model.edit_element_placement(end_port, obj.matrix_world @ end_port_matrix) segment_data = self.get_segment_data(segment)
for port_position in ("start_port", "end_port"):
port = segment_data.get(port_position, None)
if not port:
continue
# no need to correct start port position - it's corrected automatically
# as DumbProfileJoiner already moved the general segment position in that case
if port_position == "end_port":
tool.Model.edit_element_placement(port, end_port_matrix)
connected_port = tool.System.get_connected_port(port)
if not connected_port:
continue
# handle only obstructions for now
connected_element = tool.System.get_port_relating_element(connected_port)
def get_predefined_type(element):
element_type = ifcopenshell.util.element.get_type(element)
if element_type:
return element_type.PredefinedType
return element.PredefinedType
connected_obj = tool.Ifc.get_object(connected_element)
connected_element_length = connected_obj.dimensions.z
if (segment.is_a("IfcFlowSegment") and get_predefined_type(connected_element) == "OBSTRUCTION") or (
segment.is_a("IfcFlowFitting") and connected_element.is_a("IfcFlowSegment")
):
if port_position == "start_port":
if segment.is_a("IfcFlowFitting"):
profile_joiner = DumbProfileJoiner()
connected_element_length = (
tool.Model.get_flow_segment_axis(connected_obj)[0]
- tool.Model.get_flow_segment_axis(obj)[0]
).length
connected_port_matrix = start_port_matrix @ Matrix.Translation((0, 0, -connected_element_length))
else:
connected_port_matrix = end_port_matrix
connected_obj.matrix_world = connected_port_matrix
if port_position == "start_port" and segment.is_a("IfcFlowFitting"):
profile_joiner = DumbProfileJoiner()
profile_joiner.set_depth(connected_obj, connected_element_length)
def get_segment_data(self, segment): def get_segment_data(self, segment):
ports = tool.System.get_ports(segment) ports = tool.System.get_ports(segment)
@@ -275,6 +321,7 @@ class MEPGenerator:
profile_joiner.set_depth(obstruction_obj, length) profile_joiner.set_depth(obstruction_obj, length)
obstruction = tool.Ifc.get_entity(obstruction_obj) obstruction = tool.Ifc.get_entity(obstruction_obj)
# TODO: specify PredefinedType based on the segment type
obstruction_port = tool.Ifc.run("system.add_port", element=obstruction) obstruction_port = tool.Ifc.run("system.add_port", element=obstruction)
port_local_position = Matrix.Translation((0, 0, length)) if at_segment_start else Matrix() port_local_position = Matrix.Translation((0, 0, length)) if at_segment_start else Matrix()
tool.Ifc.run( tool.Ifc.run(
@@ -491,7 +491,7 @@ class DumbProfileJoiner:
should_sync_changes_first=False, should_sync_changes_first=False,
) )
tool.Geometry.record_object_materials(obj) tool.Geometry.record_object_materials(obj)
if element.is_a("IfcFlowSegment"): if element.is_a("IfcFlowSegment") or element.is_a("IfcFlowFitting"):
# lazy import to avoid circular import errors # lazy import to avoid circular import errors
from blenderbim.bim.module.model.mep import MEPGenerator from blenderbim.bim.module.model.mep import MEPGenerator
@@ -803,14 +803,23 @@ class RecalculateProfile(bpy.types.Operator, tool.Ifc.Operator):
class DumbProfileRecalculator: class DumbProfileRecalculator:
def recalculate(self, profiles): def recalculate(self, profiles):
"`profiles` is a list of blender profile objects"
queue = set() queue = set()
# also recalculate all connected elements
for profile in profiles: for profile in profiles:
element = tool.Ifc.get_entity(profile) element = tool.Ifc.get_entity(profile)
queue.add((element, profile)) queue.add((element, profile))
connected_elements = []
for rel in getattr(element, "ConnectedTo", []): for rel in getattr(element, "ConnectedTo", []):
queue.add((rel.RelatedElement, tool.Ifc.get_object(rel.RelatedElement))) connected_elements.append(rel.RelatedElement)
for rel in getattr(element, "ConnectedFrom", []): for rel in getattr(element, "ConnectedFrom", []):
queue.add((rel.RelatingElement, tool.Ifc.get_object(rel.RelatingElement))) connected_elements.append(rel.RelatingElement)
for element in connected_elements:
queue.add((element, tool.Ifc.get_object(element)))
joiner = DumbProfileJoiner() joiner = DumbProfileJoiner()
for element, profile in queue: for element, profile in queue:
if profile: if profile:
@@ -168,6 +168,7 @@ class HidePorts(bpy.types.Operator, Operator):
class AddPort(bpy.types.Operator, Operator): class AddPort(bpy.types.Operator, Operator):
bl_idname = "bim.add_port" bl_idname = "bim.add_port"
bl_description = "Add port at current cursor position"
bl_label = "Add Port" bl_label = "Add Port"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
+4 -1
View File
@@ -64,6 +64,10 @@ class System(blenderbim.core.tool.System):
def get_ports(cls, element): def get_ports(cls, element):
return ifcopenshell.util.system.get_ports(element) return ifcopenshell.util.system.get_ports(element)
@classmethod
def get_port_relating_element(cls, port):
return port.Nests[0].RelatingObject
@classmethod @classmethod
def import_system_attributes(cls, system): def import_system_attributes(cls, system):
props = bpy.context.scene.BIMSystemProperties props = bpy.context.scene.BIMSystemProperties
@@ -129,7 +133,6 @@ class System(blenderbim.core.tool.System):
ifc_representation_class=ifc_representation_class, ifc_representation_class=ifc_representation_class,
) )
@classmethod @classmethod
def select_system_products(cls, system): def select_system_products(cls, system):
tool.Spatial.select_products(ifcopenshell.util.system.get_system_elements(system)) tool.Spatial.select_products(ifcopenshell.util.system.get_system_elements(system))