mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-27 02:31:09 +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_label = "Join Wall"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
bl_description = """ Trim/Extend the selected walls to the last selected wall:
|
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
|
'L' mode: Butt join two selected walls
|
||||||
'V' mode: Mitre join two selected wall
|
'V' mode: Mitre join two selected wall
|
||||||
'' (empty) mode: Unjoin selected walls
|
'' (empty) mode: Unjoin selected walls
|
||||||
@@ -107,10 +107,18 @@ class JoinWall(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
if len(selected_objs) < 2:
|
if len(selected_objs) < 2:
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
if self.join_type == "T":
|
if self.join_type == "T":
|
||||||
for obj in selected_objs:
|
elements = [tool.Ifc.get_entity(o) for o in context.selected_objects]
|
||||||
if obj == context.active_object:
|
targets = [e for e in elements if e and e.is_a() in ("IfcSlab", "IfcSlabStandardCase", "IfcRoof")]
|
||||||
continue
|
if targets:
|
||||||
joiner.join_T(obj, context.active_object)
|
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"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
@@ -278,7 +286,7 @@ class ChangeExtrusionXAngle(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
is_global=True,
|
is_global=True,
|
||||||
should_sync_changes_first=False,
|
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
|
obj.matrix_world = rotation
|
||||||
if wall_objs:
|
if wall_objs:
|
||||||
DumbWallRecalculator().recalculate(wall_objs)
|
DumbWallRecalculator().recalculate(wall_objs)
|
||||||
@@ -611,7 +619,9 @@ class DumbWallGenerator:
|
|||||||
"geometry.add_wall_representation",
|
"geometry.add_wall_representation",
|
||||||
tool.Ifc.get(),
|
tool.Ifc.get(),
|
||||||
context=self.body_context,
|
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"],
|
offset=self.layers["offset"],
|
||||||
length=self.length,
|
length=self.length,
|
||||||
height=self.height,
|
height=self.height,
|
||||||
@@ -920,6 +930,20 @@ class DumbWallJoiner:
|
|||||||
blenderbim.core.root.copy_class(tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=wall2)
|
blenderbim.core.root.copy_class(tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=wall2)
|
||||||
return 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):
|
def join_L(self, wall1, wall2):
|
||||||
element1 = tool.Ifc.get_entity(wall1)
|
element1 = tool.Ifc.get_entity(wall1)
|
||||||
element2 = tool.Ifc.get_entity(wall2)
|
element2 = tool.Ifc.get_entity(wall2)
|
||||||
@@ -1043,22 +1067,37 @@ class DumbWallJoiner:
|
|||||||
layers = tool.Model.get_material_layer_parameters(element)
|
layers = tool.Model.get_material_layer_parameters(element)
|
||||||
|
|
||||||
for rel in element.ConnectedTo:
|
for rel in element.ConnectedTo:
|
||||||
connection = rel.RelatingConnectionType
|
if rel.is_a("IfcRelConnectsPathElements"):
|
||||||
other = tool.Ifc.get_object(rel.RelatedElement)
|
connection = rel.RelatingConnectionType
|
||||||
if connection not in ["ATPATH", "NOTDEFINED"]:
|
other = tool.Ifc.get_object(rel.RelatedElement)
|
||||||
self.join(
|
if connection not in ["ATPATH", "NOTDEFINED"]:
|
||||||
obj, other, connection, rel.RelatedConnectionType, is_relating=True, description=rel.Description
|
self.join(
|
||||||
)
|
obj, other, connection, rel.RelatedConnectionType, is_relating=True, description=rel.Description
|
||||||
|
)
|
||||||
for rel in element.ConnectedFrom:
|
for rel in element.ConnectedFrom:
|
||||||
connection = rel.RelatedConnectionType
|
if rel.is_a("IfcRelConnectsPathElements"):
|
||||||
other = tool.Ifc.get_object(rel.RelatingElement)
|
connection = rel.RelatedConnectionType
|
||||||
if connection not in ["ATPATH", "NOTDEFINED"]:
|
other = tool.Ifc.get_object(rel.RelatingElement)
|
||||||
self.join(
|
if connection not in ["ATPATH", "NOTDEFINED"]:
|
||||||
obj, other, connection, rel.RelatingConnectionType, is_relating=False, description=rel.Description
|
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 = copy.deepcopy(obj.matrix_world)
|
||||||
new_matrix.col[3] = self.body[0].to_4d().copy()
|
|
||||||
new_matrix.invert()
|
new_matrix.invert()
|
||||||
|
|
||||||
for clipping in self.clippings:
|
for clipping in self.clippings:
|
||||||
@@ -1092,7 +1131,9 @@ class DumbWallJoiner:
|
|||||||
height=height,
|
height=height,
|
||||||
x_angle=x_angle,
|
x_angle=x_angle,
|
||||||
offset=layers["offset"],
|
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,
|
clippings=self.clippings,
|
||||||
booleans=tool.Model.get_manual_booleans(element),
|
booleans=tool.Model.get_manual_booleans(element),
|
||||||
)
|
)
|
||||||
@@ -1109,10 +1150,6 @@ class DumbWallJoiner:
|
|||||||
"geometry.assign_representation", tool.Ifc.get(), product=element, representation=new_body
|
"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):
|
if tool.Ifc.is_moved(obj):
|
||||||
# Openings should move with the host overall ...
|
# Openings should move with the host overall ...
|
||||||
# ... except their position should stay the same along the local X axis of the wall
|
# ... 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):
|
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]
|
item = representation.Items[0]
|
||||||
while True:
|
while True:
|
||||||
if item.is_a("IfcExtrudedAreaSolid"):
|
if item.is_a("IfcExtrudedAreaSolid"):
|
||||||
|
results["item"] = item
|
||||||
results["height"] = item.Depth * self.unit_scale
|
results["height"] = item.Depth * self.unit_scale
|
||||||
x, y, z = item.ExtrudedDirection.DirectionRatios
|
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):
|
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
|
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