diff --git a/src/bonsai/bonsai/bim/module/model/mep.py b/src/bonsai/bonsai/bim/module/model/mep.py index 72d7a8e2f7..6744448f08 100644 --- a/src/bonsai/bonsai/bim/module/model/mep.py +++ b/src/bonsai/bonsai/bim/module/model/mep.py @@ -921,11 +921,11 @@ class MEPAddBend(bpy.types.Operator, tool.Ifc.Operator): start_element = tool.Ifc.get_entity(start_object) end_element = tool.Ifc.get_entity(end_object) if not start_element or not end_element: - self.report({"ERROR"}, f"Two IFC elements should be selected for the bend.") + self.report({"ERROR"}, "Two IFC elements should be selected for the bend.") return {"CANCELLED"} else: - self.report({"ERROR"}, f"Two IFC elements should be provided for the bend.") + self.report({"ERROR"}, "Two IFC elements should be provided for the bend.") return {"CANCELLED"} # check rotation difference @@ -1248,10 +1248,14 @@ class MEPAddBend(bpy.types.Operator, tool.Ifc.Operator): return matrix fitting_obj.matrix_world = get_fitting_matrix() + tool.Model.sync_object_ifc_position(fitting_obj) # add ports and connect them ports = tool.System.get_ports(tool.Ifc.get_entity(fitting_obj)) - if not start_port_match: + start_co = ifcopenshell.util.placement.get_local_placement(start_port.ObjectPlacement)[:,3] + port0_co = ifcopenshell.util.placement.get_local_placement(ports[0].ObjectPlacement)[:,3] + # We cannot use start_port_match because tool.System.get_ports is unordered + if not np.allclose(start_co, port0_co): start_port, end_port = end_port, start_port tool.Ifc.run("system.connect_port", port1=ports[0], port2=start_port, direction="NOTDEFINED") tool.Ifc.run("system.connect_port", port1=ports[1], port2=end_port, direction="NOTDEFINED") diff --git a/src/bonsai/bonsai/bim/module/model/product.py b/src/bonsai/bonsai/bim/module/model/product.py index 9a6c6a2a97..12bae7ae18 100644 --- a/src/bonsai/bonsai/bim/module/model/product.py +++ b/src/bonsai/bonsai/bim/module/model/product.py @@ -495,15 +495,16 @@ class AddOccurrence(bpy.types.Operator, tool.Ifc.Operator): elif props.rl_mode == "CURSOR": pass + tool.Model.sync_object_ifc_position(obj) + unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) for port in ifcopenshell.util.system.get_ports(relating_type): mat = Matrix(ifcopenshell.util.placement.get_local_placement(port.ObjectPlacement)) mat.translation *= unit_scale mat = obj.matrix_world @ mat - new_port = tool.Ifc.run("root.create_entity", ifc_class="IfcDistributionPort") + new_port = tool.Ifc.run("system.add_port", element=element) new_port.PredefinedType = port.PredefinedType new_port.SystemType = port.SystemType - tool.Ifc.run("system.assign_port", element=element, port=new_port) tool.Ifc.run("geometry.edit_object_placement", product=new_port, matrix=mat, is_si=True) if ifc_class == "IfcDoorType" and len(context.selected_objects) >= 1: diff --git a/src/bonsai/test/bim/feature/system.feature b/src/bonsai/test/bim/feature/system.feature index f9ebbcf915..6b900a0c04 100644 --- a/src/bonsai/test/bim/feature/system.feature +++ b/src/bonsai/test/bim/feature/system.feature @@ -159,6 +159,45 @@ Scenario: Connect MEP elements And the object "IfcActuator/Actuator" is at "5.5,0.0,1.0" And the variable "connected_elements" is "set(tool.System.get_connected_elements({ifc}.by_type('IfcActuator')[0]))" +Scenario: Add bend - and regenerate with no changes + Given an empty IFC project + And I create default MEP types + And the variable "segment_types" is "[str(e.id()) for e in {ifc}.by_type('IfcDuctSegmentType')]" + And the variable "actuator_type_id" is "{ifc}.by_type('IfcActuatorType')[0].id()" + + # segment1 + And I set "scene.BIMModelProperties.ifc_class" to "IfcDuctSegmentType" + And I set "scene.BIMModelProperties.relating_type_id" to "{segment_types}[0]" + And I set "scene.BIMModelProperties.extrusion_depth" to "5.0" + And I press "bim.add_occurrence" + And I rename the object "IfcDuctSegment/DuctSegment" to "IfcDuctSegment/Seg1" + + # segment2 + And I set "scene.BIMModelProperties.relating_type_id" to "{segment_types}[0]" + And I press "bim.add_occurrence" + And I rename the object "IfcDuctSegment/DuctSegment" to "IfcDuctSegment/Seg2" + And the object "IfcDuctSegment/Seg2" is rotated by "0,0,90" deg + + # bend between segments 1 and 2 + When the object "IfcDuctSegment/Seg1" is selected + And additionally the object "IfcDuctSegment/Seg2" is selected + And I press "bim.mep_add_bend" + + Then the object "IfcDuctSegment/Seg1" is at "0.5,0,1" + And the object "IfcDuctSegment/Seg2" is at "0,0.5,1" + And the object "IfcDuctFitting/DuctFitting" is at "0,0.5,1" + And the object "IfcDuctFitting/DuctFitting" dimensions are "0.7,0.2,0.7" + And the object "IfcDuctSegment/Seg1" dimensions are "0.4,0.2,4.5" + And the object "IfcDuctSegment/Seg2" dimensions are "0.4,0.2,4.5" + + When I press "bim.regenerate_distribution_element" + Then the object "IfcDuctSegment/Seg1" is at "0.5,0,1" + And the object "IfcDuctSegment/Seg2" is at "0,0.5,1" + And the object "IfcDuctFitting/DuctFitting" is at "0,0.5,1" + And the object "IfcDuctFitting/DuctFitting" dimensions are "0.7,0.2,0.7" + And the object "IfcDuctSegment/Seg1" dimensions are "0.4,0.2,4.5" + And the object "IfcDuctSegment/Seg2" dimensions are "0.4,0.2,4.5" + Scenario: Connect MEP elements and regenerate Given an empty IFC project And I create default MEP types diff --git a/src/bonsai/test/bim/test_feature.py b/src/bonsai/test/bim/test_feature.py index 9cc71bd4f5..4a37caa2e1 100644 --- a/src/bonsai/test/bim/test_feature.py +++ b/src/bonsai/test/bim/test_feature.py @@ -1200,7 +1200,7 @@ def the_object_name_is_at_location(name, location): obj_location = the_object_name_exists(name).location assert ( obj_location - Vector([float(co) for co in location.split(",")]) - ).length < 0.1, f"Object is at {obj_location}" + ).length < 0.1, f"Object is at {obj_location} instead of {location}" @then(parsers.parse('the object "{name}" has a vertex at "{location}"')) @@ -1451,6 +1451,13 @@ def run_test_code(): pass +@given(parsers.parse("I fail")) +@when(parsers.parse("I fail")) +@then(parsers.parse("I fail")) +def i_fail(): + assert False + + @given(parsers.parse("I save sample test files")) @when(parsers.parse("I save sample test files")) @then(parsers.parse("I save sample test files"))