mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 18:16:40 +00:00
You can now clip walls by sloped slabs / roofs.
This commit is contained in:
@@ -76,7 +76,7 @@ class JoinWall(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_label = "Join Wall"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = """ Trim/Extend the selected walls to the last selected wall:
|
||||
'T' mode: Trim/Extend to a selected wall or 3D target
|
||||
'T' mode: Trim/Extend to a selected wall, slab, or 3D target
|
||||
'L' mode: Butt join two selected walls
|
||||
'V' mode: Mitre join two selected wall
|
||||
'' (empty) mode: Unjoin selected walls
|
||||
@@ -107,10 +107,18 @@ class JoinWall(bpy.types.Operator, tool.Ifc.Operator):
|
||||
if len(selected_objs) < 2:
|
||||
return {"FINISHED"}
|
||||
if self.join_type == "T":
|
||||
for obj in selected_objs:
|
||||
if obj == context.active_object:
|
||||
continue
|
||||
joiner.join_T(obj, context.active_object)
|
||||
elements = [tool.Ifc.get_entity(o) for o in context.selected_objects]
|
||||
targets = [e for e in elements if e and e.is_a() in ("IfcSlab", "IfcSlabStandardCase", "IfcRoof")]
|
||||
if targets:
|
||||
target = tool.Ifc.get_object(targets[0])
|
||||
for element in elements:
|
||||
if element.is_a("IfcWall"):
|
||||
joiner.join_Z(tool.Ifc.get_object(element), target)
|
||||
else:
|
||||
for obj in selected_objs:
|
||||
if obj == context.active_object:
|
||||
continue
|
||||
joiner.join_T(obj, context.active_object)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -278,7 +286,7 @@ class ChangeExtrusionXAngle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
is_global=True,
|
||||
should_sync_changes_first=False,
|
||||
)
|
||||
rotation = mathutils.Matrix.Rotation(x_angle, 4, 'X')
|
||||
rotation = mathutils.Matrix.Rotation(x_angle, 4, "X")
|
||||
obj.matrix_world = rotation
|
||||
if wall_objs:
|
||||
DumbWallRecalculator().recalculate(wall_objs)
|
||||
@@ -611,7 +619,9 @@ class DumbWallGenerator:
|
||||
"geometry.add_wall_representation",
|
||||
tool.Ifc.get(),
|
||||
context=self.body_context,
|
||||
thickness=self.layers["thickness"] * 1/cos(self.x_angle), #1/cos(self.x_angle) maintains the correct MaterialLayer thickness upon rotation
|
||||
thickness=self.layers["thickness"]
|
||||
* 1
|
||||
/ cos(self.x_angle), # 1/cos(self.x_angle) maintains the correct MaterialLayer thickness upon rotation
|
||||
offset=self.layers["offset"],
|
||||
length=self.length,
|
||||
height=self.height,
|
||||
@@ -920,6 +930,20 @@ class DumbWallJoiner:
|
||||
blenderbim.core.root.copy_class(tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=wall2)
|
||||
return wall2
|
||||
|
||||
def join_Z(self, wall1, slab2):
|
||||
element1 = tool.Ifc.get_entity(wall1)
|
||||
element2 = tool.Ifc.get_entity(slab2)
|
||||
|
||||
ifcopenshell.api.run(
|
||||
"geometry.connect_element",
|
||||
tool.Ifc.get(),
|
||||
relating_element=element2,
|
||||
related_element=element1,
|
||||
description="TOP",
|
||||
)
|
||||
|
||||
self.recreate_wall(element1, wall1)
|
||||
|
||||
def join_L(self, wall1, wall2):
|
||||
element1 = tool.Ifc.get_entity(wall1)
|
||||
element2 = tool.Ifc.get_entity(wall2)
|
||||
@@ -1043,22 +1067,37 @@ class DumbWallJoiner:
|
||||
layers = tool.Model.get_material_layer_parameters(element)
|
||||
|
||||
for rel in element.ConnectedTo:
|
||||
connection = rel.RelatingConnectionType
|
||||
other = tool.Ifc.get_object(rel.RelatedElement)
|
||||
if connection not in ["ATPATH", "NOTDEFINED"]:
|
||||
self.join(
|
||||
obj, other, connection, rel.RelatedConnectionType, is_relating=True, description=rel.Description
|
||||
)
|
||||
if rel.is_a("IfcRelConnectsPathElements"):
|
||||
connection = rel.RelatingConnectionType
|
||||
other = tool.Ifc.get_object(rel.RelatedElement)
|
||||
if connection not in ["ATPATH", "NOTDEFINED"]:
|
||||
self.join(
|
||||
obj, other, connection, rel.RelatedConnectionType, is_relating=True, description=rel.Description
|
||||
)
|
||||
for rel in element.ConnectedFrom:
|
||||
connection = rel.RelatedConnectionType
|
||||
other = tool.Ifc.get_object(rel.RelatingElement)
|
||||
if connection not in ["ATPATH", "NOTDEFINED"]:
|
||||
self.join(
|
||||
obj, other, connection, rel.RelatingConnectionType, is_relating=False, description=rel.Description
|
||||
)
|
||||
if rel.is_a("IfcRelConnectsPathElements"):
|
||||
connection = rel.RelatedConnectionType
|
||||
other = tool.Ifc.get_object(rel.RelatingElement)
|
||||
if connection not in ["ATPATH", "NOTDEFINED"]:
|
||||
self.join(
|
||||
obj,
|
||||
other,
|
||||
connection,
|
||||
rel.RelatingConnectionType,
|
||||
is_relating=False,
|
||||
description=rel.Description,
|
||||
)
|
||||
|
||||
previous_matrix = obj.matrix_world.copy()
|
||||
previous_origin = previous_matrix.col[3].to_2d()
|
||||
obj.matrix_world[0][3], obj.matrix_world[1][3] = self.body[0]
|
||||
bpy.context.view_layer.update()
|
||||
|
||||
for rel in element.ConnectedFrom:
|
||||
if rel.is_a() == "IfcRelConnectsElements":
|
||||
height = self.clip(obj, tool.Ifc.get_object(rel.RelatingElement))
|
||||
|
||||
new_matrix = copy.deepcopy(obj.matrix_world)
|
||||
new_matrix.col[3] = self.body[0].to_4d().copy()
|
||||
new_matrix.invert()
|
||||
|
||||
for clipping in self.clippings:
|
||||
@@ -1092,7 +1131,9 @@ class DumbWallJoiner:
|
||||
height=height,
|
||||
x_angle=x_angle,
|
||||
offset=layers["offset"],
|
||||
thickness=layers["thickness"] * 1/cos(x_angle), #1/cos(x_angle) maintains the correct MaterialLayer thickness upon rotation
|
||||
thickness=layers["thickness"]
|
||||
* 1
|
||||
/ cos(x_angle), # 1/cos(x_angle) maintains the correct MaterialLayer thickness upon rotation
|
||||
clippings=self.clippings,
|
||||
booleans=tool.Model.get_manual_booleans(element),
|
||||
)
|
||||
@@ -1109,10 +1150,6 @@ class DumbWallJoiner:
|
||||
"geometry.assign_representation", tool.Ifc.get(), product=element, representation=new_body
|
||||
)
|
||||
|
||||
previous_matrix = obj.matrix_world.copy()
|
||||
previous_origin = previous_matrix.col[3].to_2d()
|
||||
obj.matrix_world[0][3], obj.matrix_world[1][3] = self.body[0]
|
||||
bpy.context.view_layer.update()
|
||||
if tool.Ifc.is_moved(obj):
|
||||
# Openings should move with the host overall ...
|
||||
# ... except their position should stay the same along the local X axis of the wall
|
||||
@@ -1152,10 +1189,11 @@ class DumbWallJoiner:
|
||||
)
|
||||
|
||||
def get_extrusion_data(self, representation):
|
||||
results = {"height": 3.0, "x_angle": 0, "is_sloped": False, "direction": Vector((0, 0, 1))}
|
||||
results = {"item": None, "height": 3.0, "x_angle": 0, "is_sloped": False, "direction": Vector((0, 0, 1))}
|
||||
item = representation.Items[0]
|
||||
while True:
|
||||
if item.is_a("IfcExtrudedAreaSolid"):
|
||||
results["item"] = item
|
||||
results["height"] = item.Depth * self.unit_scale
|
||||
x, y, z = item.ExtrudedDirection.DirectionRatios
|
||||
if not tool.Cad.is_x(x, 0) or not tool.Cad.is_x(y, 0) or not tool.Cad.is_x(z, 1):
|
||||
@@ -1336,3 +1374,41 @@ class DumbWallJoiner:
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
def clip(self, wall1, slab2):
|
||||
element1 = tool.Ifc.get_entity(wall1)
|
||||
element2 = tool.Ifc.get_entity(slab2)
|
||||
|
||||
layers1 = tool.Model.get_material_layer_parameters(element1)
|
||||
axis1 = tool.Model.get_wall_axis(wall1, layers1)
|
||||
|
||||
bases = [axis1["base"][0].to_3d(), axis1["base"][1].to_3d(), axis1["side"][0].to_3d(), axis1["side"][1].to_3d()]
|
||||
|
||||
extrusion = self.get_extrusion_data(tool.Ifc.get().by_id(wall1.data.BIMMeshProperties.ifc_definition_id))
|
||||
d = wall1.matrix_world.to_quaternion() @ extrusion["direction"]
|
||||
|
||||
slab_pt = slab2.matrix_world @ Vector((0, 0, 0))
|
||||
slab_dir = slab2.matrix_world.to_quaternion() @ Vector((0, 0, -1))
|
||||
|
||||
tops = [mathutils.geometry.intersect_line_plane(b, b + d, slab_pt, slab_dir) for b in bases]
|
||||
|
||||
i_bottom = None
|
||||
i_top = None
|
||||
for i, co in enumerate(tops):
|
||||
if i_top is None or co[2] > i_top[2]:
|
||||
i_top = co
|
||||
i_bottom = bases[i]
|
||||
|
||||
quaternion = slab2.matrix_world.to_quaternion()
|
||||
x_axis = quaternion @ Vector((1, 0, 0))
|
||||
y_axis = quaternion @ Vector((0, 1, 0))
|
||||
z_axis = quaternion @ Vector((0, 0, 1))
|
||||
self.clippings.append(
|
||||
{
|
||||
"type": "IfcBooleanClippingResult",
|
||||
"operand_type": "IfcHalfSpaceSolid",
|
||||
"matrix": self.create_matrix(i_top, x_axis, y_axis, z_axis),
|
||||
}
|
||||
)
|
||||
|
||||
return (i_top - i_bottom).length
|
||||
|
||||
Reference in New Issue
Block a user