mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
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:
@@ -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"}
|
||||||
|
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
Reference in New Issue
Block a user