mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-06 07:51:47 +00:00
Fix #3673. Bug where a split wall broke flipped doors.
Instead of regenerating fills upon splitting walls, placements are preserved exactly.
This commit is contained in:
@@ -1019,7 +1019,6 @@ class DumbWallJoiner:
|
||||
bonsai.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=wall1)
|
||||
|
||||
axis1 = tool.Model.get_wall_axis(wall1)
|
||||
axis2 = copy.deepcopy(axis1)
|
||||
intersect, cut_percentage = mathutils.geometry.intersect_point_line(target.to_2d(), *axis1["reference"])
|
||||
if cut_percentage < 0 or cut_percentage > 1 or tool.Cad.is_x(cut_percentage, (0, 1)):
|
||||
return
|
||||
@@ -1080,12 +1079,28 @@ class DumbWallJoiner:
|
||||
# During the duplication process, filled voids are not copied. So we
|
||||
# only need to check fillings on the original element1.
|
||||
for opening in [r.RelatedOpeningElement for r in element1.HasOpenings if r.RelatedOpeningElement.HasFillings]:
|
||||
filling_obj = tool.Ifc.get_object(opening.HasFillings[0].RelatedBuildingElement)
|
||||
rel = opening.HasFillings[0]
|
||||
filling = rel.RelatedBuildingElement
|
||||
filling_obj = tool.Ifc.get_object(filling)
|
||||
filling_location = filling_obj.matrix_world.translation
|
||||
_, filling_position = mathutils.geometry.intersect_point_line(filling_location.to_2d(), *axis1["reference"])
|
||||
if filling_position > cut_percentage:
|
||||
# The filling should be moved from element1 to element2.
|
||||
FilledOpeningGenerator().generate(filling_obj, wall2, target=filling_obj.matrix_world.translation)
|
||||
new_opening = ifcopenshell.api.root.copy_class(tool.Ifc.get(), product=opening)
|
||||
new_opening.VoidsElements[0].RelatingBuildingElement = element2
|
||||
if new_opening.ObjectPlacement and new_opening.ObjectPlacement.is_a("IfcLocalPlacement"):
|
||||
if element2.ObjectPlacement:
|
||||
new_opening.ObjectPlacement.PlacementRelTo = element2.ObjectPlacement
|
||||
# For now, we do copy opening representations
|
||||
if opening.Representation:
|
||||
new_opening.Representation = ifcopenshell.util.element.copy_deep(
|
||||
tool.Ifc.get(), opening.Representation, exclude=["IfcGeometricRepresentationContext"]
|
||||
)
|
||||
|
||||
rel.RelatedBuildingElement = element2
|
||||
|
||||
# Remove the old opening
|
||||
ifcopenshell.api.run("feature.remove_feature", tool.Ifc.get(), feature=opening)
|
||||
|
||||
p1, p2 = ifcopenshell.util.representation.get_reference_line(element1)
|
||||
p3 = (wall1.matrix_world.inverted() @ intersect.to_3d()).to_2d() / unit_scale
|
||||
|
||||
@@ -224,6 +224,68 @@ Scenario: Regenerate a wall - after doing nothing interesting
|
||||
And the object "IfcWall/Wall" dimensions are "1,0.1,3"
|
||||
And the object "IfcWall/Wall" bottom left corner is at "0,0,0"
|
||||
|
||||
Scenario: Insert door into wall
|
||||
Given an empty IFC project
|
||||
And I load the demo construction library
|
||||
And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType"
|
||||
And the variable "element_type" is "[e for e in {ifc}.by_type('IfcWallType') if e.Name == 'WAL100'][0].id()"
|
||||
And I set "scene.BIMModelProperties.relating_type_id" to "{element_type}"
|
||||
And I press "bim.add_occurrence"
|
||||
And the object "IfcWall/Wall" is selected
|
||||
And the cursor is at "10,0,0"
|
||||
# Extend the wall
|
||||
And I press "bim.hotkey(hotkey='S_E')"
|
||||
When I set "scene.BIMModelProperties.ifc_class" to "IfcDoorType"
|
||||
And the variable "element_type" is "[e for e in {ifc}.by_type('IfcDoorType') if e.Name == 'DT01'][0].id()"
|
||||
And the cursor is at "7,0,0"
|
||||
And I press "bim.add_occurrence"
|
||||
Then the object "IfcDoor/Door" is at "7,0,0"
|
||||
And the object "IfcWall/Wall" is filled by "IfcDoor/Door"
|
||||
|
||||
Scenario: Flip a door inserted in a wall
|
||||
Given an empty IFC project
|
||||
And I load the demo construction library
|
||||
And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType"
|
||||
And the variable "element_type" is "[e for e in {ifc}.by_type('IfcWallType') if e.Name == 'WAL100'][0].id()"
|
||||
And I set "scene.BIMModelProperties.relating_type_id" to "{element_type}"
|
||||
And I press "bim.add_occurrence"
|
||||
And the object "IfcWall/Wall" is selected
|
||||
And the cursor is at "10,0,0"
|
||||
# Extend the wall
|
||||
And I press "bim.hotkey(hotkey='S_E')"
|
||||
# Insert a door
|
||||
And I set "scene.BIMModelProperties.ifc_class" to "IfcDoorType"
|
||||
And the variable "element_type" is "[e for e in {ifc}.by_type('IfcDoorType') if e.Name == 'DT01'][0].id()"
|
||||
And the cursor is at "7,0,0"
|
||||
And I press "bim.add_occurrence"
|
||||
When the object "IfcDoor/Door" is selected
|
||||
And I press "bim.hotkey(hotkey='S_F')"
|
||||
Then the object "IfcDoor/Door" is at "8.01,0.1,0"
|
||||
|
||||
Scenario: Split a wall which has a flipped door
|
||||
Given an empty IFC project
|
||||
And I load the demo construction library
|
||||
And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType"
|
||||
And the variable "element_type" is "[e for e in {ifc}.by_type('IfcWallType') if e.Name == 'WAL100'][0].id()"
|
||||
And I set "scene.BIMModelProperties.relating_type_id" to "{element_type}"
|
||||
And I press "bim.add_occurrence"
|
||||
And the object "IfcWall/Wall" is selected
|
||||
And the cursor is at "10,0,0"
|
||||
# Extend the wall
|
||||
And I press "bim.hotkey(hotkey='S_E')"
|
||||
# Insert a door
|
||||
And I set "scene.BIMModelProperties.ifc_class" to "IfcDoorType"
|
||||
And the variable "element_type" is "[e for e in {ifc}.by_type('IfcDoorType') if e.Name == 'DT01'][0].id()"
|
||||
And the cursor is at "7,0,0"
|
||||
And I press "bim.add_occurrence"
|
||||
# Flip the door
|
||||
And the object "IfcDoor/Door" is selected
|
||||
And I press "bim.hotkey(hotkey='S_F')"
|
||||
When the cursor is at "5,0,0"
|
||||
And the object "IfcWall/Wall" is selected
|
||||
And I press "bim.hotkey(hotkey='S_K')"
|
||||
Then the object "IfcDoor/Door" is at "8.01,0.1,0"
|
||||
|
||||
Scenario: Add a slab
|
||||
Given an empty IFC project
|
||||
And I load the demo construction library
|
||||
|
||||
@@ -1057,6 +1057,25 @@ def the_object_name_has_number_vertices(name, number):
|
||||
assert total == int(number), f"We found {total} vertices"
|
||||
|
||||
|
||||
@then(parsers.parse('the object "{name}" is filled by "{name2}"'))
|
||||
def the_object_name_is_filled_by_filling(name, name2):
|
||||
ifc = tool.Ifc.get()
|
||||
element = ifc.by_id(tool.Blender.get_ifc_definition_id(the_object_name_exists(name)))
|
||||
debug = {}
|
||||
for rel in ifcopenshell.util.element.get_openings(element):
|
||||
filling = None
|
||||
opening = rel.RelatedOpeningElement
|
||||
for rel2 in opening.HasFillings or []:
|
||||
filling = rel2.RelatedBuildingElement
|
||||
break
|
||||
debug[opening] = filling
|
||||
filling_obj = tool.Ifc.get_object(filling)
|
||||
if filling_obj and filling_obj.name == name2:
|
||||
return True
|
||||
debug = "\n".join([f"{k} filled by {v}" for k, v in debug.items()])
|
||||
assert False, f"Object {name} is not filled by {name2}.\n{debug}"
|
||||
|
||||
|
||||
@then(parsers.parse('the void "{name}" is filled by "{filling}"'))
|
||||
def the_void_name_is_filled_by_filling(name, filling):
|
||||
ifc = tool.Ifc.get()
|
||||
@@ -1357,6 +1376,7 @@ def i_add_a_construction_library():
|
||||
|
||||
|
||||
@given(parsers.parse('the cursor is at "{location}"'))
|
||||
@when(parsers.parse('the cursor is at "{location}"'))
|
||||
def the_cursor_is_at_location(location):
|
||||
bpy.context.scene.cursor.location = [float(co) for co in location.split(",")]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user