mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-12 06:32:09 +00:00
Rebuild wall engine (again) to allow for sloped wall clippings.
This commit is contained in:
@@ -519,7 +519,7 @@ class DumbProfileJoiner:
|
|||||||
axis2 = self.get_profile_axis(profile2)
|
axis2 = self.get_profile_axis(profile2)
|
||||||
|
|
||||||
angle = tool.Cad.angle_edges(axis1, axis2, signed=False, degrees=True)
|
angle = tool.Cad.angle_edges(axis1, axis2, signed=False, degrees=True)
|
||||||
if tool.Cad.is_x(angle, (0, 180)):
|
if tool.Cad.is_x(angle, (0, 180), tolerance=0.001):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
intersect, _ = tool.Cad.intersect_edges(axis1, axis2)
|
intersect, _ = tool.Cad.intersect_edges(axis1, axis2)
|
||||||
@@ -527,7 +527,7 @@ class DumbProfileJoiner:
|
|||||||
proposed_axis = [self.axis[0], intersect] if connection1 == "ATEND" else [intersect, self.axis[1]]
|
proposed_axis = [self.axis[0], intersect] if connection1 == "ATEND" else [intersect, self.axis[1]]
|
||||||
|
|
||||||
if tool.Cad.is_x(tool.Cad.angle_edges(self.axis, proposed_axis, degrees=True), 180):
|
if tool.Cad.is_x(tool.Cad.angle_edges(self.axis, proposed_axis, degrees=True), 180):
|
||||||
# The user has moved the wall into an invalid position that cannot connect at the desired end
|
# The user has moved the element into an invalid position that cannot connect at the desired end
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self.axis[1 if connection1 == "ATEND" else 0] = intersect
|
self.axis[1 if connection1 == "ATEND" else 0] = intersect
|
||||||
|
|||||||
@@ -1143,14 +1143,16 @@ class DumbWallJoiner:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def get_extrusion_data(self, representation):
|
def get_extrusion_data(self, representation):
|
||||||
results = {"height": 3.0, "x_angle": 0}
|
results = {"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["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):
|
||||||
results["x_angle"] = Vector((0, 1)).angle(Vector((y, z)))
|
results["direction"] = Vector(item.ExtrudedDirection.DirectionRatios)
|
||||||
|
results["x_angle"] = Vector((0, 1)).angle_signed(Vector((y, z)))
|
||||||
|
results["is_sloped"] = True
|
||||||
break
|
break
|
||||||
elif item.is_a("IfcBooleanClippingResult"):
|
elif item.is_a("IfcBooleanClippingResult"):
|
||||||
item = item.FirstOperand
|
item = item.FirstOperand
|
||||||
@@ -1165,6 +1167,18 @@ class DumbWallJoiner:
|
|||||||
layers2 = tool.Model.get_material_layer_parameters(element2)
|
layers2 = tool.Model.get_material_layer_parameters(element2)
|
||||||
axis1 = tool.Model.get_wall_axis(wall1, layers1)
|
axis1 = tool.Model.get_wall_axis(wall1, layers1)
|
||||||
axis2 = tool.Model.get_wall_axis(wall2, layers2)
|
axis2 = tool.Model.get_wall_axis(wall2, layers2)
|
||||||
|
body1 = ifcopenshell.util.representation.get_representation(element1, "Model", "Body", "MODEL_VIEW")
|
||||||
|
body2 = ifcopenshell.util.representation.get_representation(element2, "Model", "Body", "MODEL_VIEW")
|
||||||
|
extrusion1 = self.get_extrusion_data(body1)
|
||||||
|
extrusion2 = self.get_extrusion_data(body2)
|
||||||
|
direction1 = (wall1.matrix_world.to_quaternion() @ extrusion1["direction"]).normalized()
|
||||||
|
direction2 = (wall2.matrix_world.to_quaternion() @ extrusion2["direction"]).normalized()
|
||||||
|
height1 = extrusion1["height"] * self.unit_scale
|
||||||
|
height2 = extrusion2["height"] * self.unit_scale
|
||||||
|
depth1 = direction1 * height1
|
||||||
|
depth2 = direction2 * height2
|
||||||
|
normal1 = (axis1["base"][1] - axis1["base"][0]).to_3d().normalized().cross(direction1)
|
||||||
|
normal2 = (axis2["base"][1] - axis2["base"][0]).to_3d().normalized().cross(direction2)
|
||||||
|
|
||||||
angle = tool.Cad.angle_edges(axis1["reference"], axis2["reference"], signed=True, degrees=True)
|
angle = tool.Cad.angle_edges(axis1["reference"], axis2["reference"], signed=True, degrees=True)
|
||||||
if tool.Cad.is_x(abs(angle), (0, 180), tolerance=0.001):
|
if tool.Cad.is_x(abs(angle), (0, 180), tolerance=0.001):
|
||||||
@@ -1185,171 +1199,131 @@ class DumbWallJoiner:
|
|||||||
|
|
||||||
self.axis[1 if connection1 == "ATEND" else 0] = intersect
|
self.axis[1 if connection1 == "ATEND" else 0] = intersect
|
||||||
|
|
||||||
# Work out body extents
|
# Work out body
|
||||||
intersect1, _ = tool.Cad.intersect_edges(axis1["base"], axis2["base"])
|
|
||||||
intersect2, _ = tool.Cad.intersect_edges(axis1["base"], axis2["side"])
|
# Bottom and top plane point
|
||||||
intersect3, _ = tool.Cad.intersect_edges(axis1["side"], axis2["base"])
|
bp1 = wall1.matrix_world @ Vector(wall1.bound_box[0])
|
||||||
intersect4, _ = tool.Cad.intersect_edges(axis1["side"], axis2["side"])
|
bp2 = wall2.matrix_world @ Vector(wall2.bound_box[0])
|
||||||
|
tp1 = wall1.matrix_world @ Vector(wall1.bound_box[1])
|
||||||
|
|
||||||
|
# Axis lines on bottom, for base and side axes
|
||||||
|
bba1 = (Vector((*axis1["base"][0], bp1[2])), Vector((*axis1["base"][1], bp1[2])))
|
||||||
|
bsa1 = (Vector((*axis1["side"][0], bp1[2])), Vector((*axis1["side"][1], bp1[2])))
|
||||||
|
bba2 = (Vector((*axis2["base"][0], bp2[2])), Vector((*axis2["base"][1], bp2[2])))
|
||||||
|
bsa2 = (Vector((*axis2["side"][0], bp2[2])), Vector((*axis2["side"][1], bp2[2])))
|
||||||
|
|
||||||
|
# Intersecting the walls sides defined by planes gives 4 lines of intersection
|
||||||
|
# Line point, and line direction
|
||||||
|
lp1, ld1 = mathutils.geometry.intersect_plane_plane(bba1[0], normal1, bba2[0], normal2)
|
||||||
|
lp2, ld2 = mathutils.geometry.intersect_plane_plane(bba1[0], normal1, bsa2[0], normal2)
|
||||||
|
lp3, ld3 = mathutils.geometry.intersect_plane_plane(bsa1[0], normal1, bba2[0], normal2)
|
||||||
|
lp4, ld4 = mathutils.geometry.intersect_plane_plane(bsa1[0], normal1, bsa2[0], normal2)
|
||||||
|
|
||||||
|
# Intersecting the 4 lines gives the 8 possible verts of intersection
|
||||||
|
# 4 on bottom, and 4 on top. 4 on our base line, 4 on our side line.
|
||||||
|
bb1 = mathutils.geometry.intersect_line_plane(lp1, lp1 + ld1, bp1, Vector((0, 0, 1)))
|
||||||
|
bb2 = mathutils.geometry.intersect_line_plane(lp2, lp2 + ld2, bp1, Vector((0, 0, 1)))
|
||||||
|
bs1 = mathutils.geometry.intersect_line_plane(lp3, lp3 + ld3, bp1, Vector((0, 0, 1)))
|
||||||
|
bs2 = mathutils.geometry.intersect_line_plane(lp4, lp4 + ld4, bp1, Vector((0, 0, 1)))
|
||||||
|
tb1 = mathutils.geometry.intersect_line_plane(lp1, lp1 + ld1, tp1, Vector((0, 0, 1)))
|
||||||
|
tb2 = mathutils.geometry.intersect_line_plane(lp2, lp2 + ld2, tp1, Vector((0, 0, 1)))
|
||||||
|
ts1 = mathutils.geometry.intersect_line_plane(lp3, lp3 + ld3, tp1, Vector((0, 0, 1)))
|
||||||
|
ts2 = mathutils.geometry.intersect_line_plane(lp4, lp4 + ld4, tp1, Vector((0, 0, 1)))
|
||||||
|
|
||||||
|
# Let's distinguish the 8 points by whether they are nearer or further away from the other end
|
||||||
|
# These 8 points will be used to find the final body position and clippings.
|
||||||
|
i = 0 if connection1 == "ATEND" else 1
|
||||||
|
j = 1 if connection1 == "ATEND" else 0
|
||||||
|
bbn = tool.Cad.closest_vector(axis1["base"][i].to_3d(), (bb1, bb2))
|
||||||
|
bbf = bb2 if bbn == bb1 else bb1
|
||||||
|
bsn = tool.Cad.closest_vector(axis1["side"][i].to_3d(), (bs1, bs2))
|
||||||
|
bsf = bs2 if bsn == bs1 else bs1
|
||||||
|
tbn = tool.Cad.closest_vector(axis1["base"][i].to_3d(), (tb1, tb2))
|
||||||
|
tbf = tb2 if tbn == tb1 else tb1
|
||||||
|
tsn = tool.Cad.closest_vector(axis1["side"][i].to_3d(), (ts1, ts2))
|
||||||
|
tsf = ts2 if tsn == ts1 else ts1
|
||||||
|
|
||||||
if description == "MITRE":
|
if description == "MITRE":
|
||||||
# Mitre joints are an unofficial convention
|
# Mitre joints are an unofficial convention
|
||||||
if connection1 == "ATEND":
|
bsf_ = tool.Cad.point_on_edge(bsf, bba1)
|
||||||
base_target = tool.Cad.furthest_vector(axis1["base"][0], (intersect1, intersect2))
|
tbf_ = tool.Cad.point_on_edge(tbf, bba1)
|
||||||
if tool.Cad.is_x(abs(angle), (90, 270), tolerance=0.001):
|
tsf_ = tool.Cad.point_on_edge(tsf, bba1)
|
||||||
self.body[1] = tool.Cad.point_on_edge(base_target, axis1["reference"])
|
new_body = tool.Cad.furthest_vector(bba1[i], (bbf, bsf_)).copy()
|
||||||
else:
|
new_body = tool.Cad.furthest_vector(bba1[i], (new_body, tbf_)).copy()
|
||||||
side_target = tool.Cad.furthest_vector(axis1["side"][0], (intersect3, intersect4))
|
new_body = tool.Cad.furthest_vector(bba1[i], (new_body, tsf_)).copy()
|
||||||
base_percent = tool.Cad.edge_percent(base_target, axis1["base"])
|
self.body[j] = new_body.to_2d()
|
||||||
side_percent = tool.Cad.edge_percent(side_target, axis1["side"])
|
|
||||||
if base_percent > side_percent:
|
|
||||||
self.body[1] = tool.Cad.point_on_edge(base_target, axis1["reference"])
|
|
||||||
else:
|
|
||||||
self.body[1] = tool.Cad.point_on_edge(side_target, axis1["reference"])
|
|
||||||
|
|
||||||
# Mitre clip
|
if connection1 == connection2:
|
||||||
if connection1 == connection2:
|
if (connection1 == "ATEND" and angle > 0) or (connection1 != "ATEND" and angle < 0):
|
||||||
if angle < 0:
|
pt = bbf.to_2d().to_3d()
|
||||||
clip1 = tool.Cad.closest_vector(axis1["base"][0], (intersect1, intersect2))
|
x_axis = bsn - bbf
|
||||||
clip2 = tool.Cad.furthest_vector(axis1["side"][0], (intersect3, intersect4))
|
y_axis = tbf - bbf
|
||||||
else:
|
|
||||||
clip1 = tool.Cad.furthest_vector(axis1["base"][0], (intersect1, intersect2))
|
|
||||||
clip2 = tool.Cad.closest_vector(axis1["side"][0], (intersect3, intersect4))
|
|
||||||
else:
|
else:
|
||||||
if angle < 0:
|
pt = bbn.to_2d().to_3d()
|
||||||
clip1 = tool.Cad.furthest_vector(axis1["base"][0], (intersect1, intersect2))
|
x_axis = bsf - bbn
|
||||||
clip2 = tool.Cad.closest_vector(axis1["side"][0], (intersect3, intersect4))
|
y_axis = tbn - bbn
|
||||||
else:
|
|
||||||
clip1 = tool.Cad.closest_vector(axis1["base"][0], (intersect1, intersect2))
|
|
||||||
clip2 = tool.Cad.furthest_vector(axis1["side"][0], (intersect3, intersect4))
|
|
||||||
y_axis = Vector((0, 0, -1))
|
|
||||||
x_axis = (clip1 - clip2).normalized().to_3d()
|
|
||||||
z_axis = x_axis.cross(y_axis)
|
|
||||||
self.clippings.append(
|
|
||||||
{
|
|
||||||
"type": "IfcBooleanClippingResult",
|
|
||||||
"operand_type": "IfcHalfSpaceSolid",
|
|
||||||
"matrix": self.create_matrix(clip1.to_3d(), x_axis, y_axis, z_axis),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
base_target = tool.Cad.furthest_vector(axis1["base"][1], (intersect1, intersect2))
|
if (connection1 == "ATEND" and angle < 0) or (connection1 != "ATEND" and angle > 0):
|
||||||
if tool.Cad.is_x(abs(angle), (90, 270), tolerance=0.001):
|
pt = bbf.to_2d().to_3d()
|
||||||
self.body[0] = tool.Cad.point_on_edge(base_target, axis1["reference"])
|
x_axis = bsn - bbf
|
||||||
|
y_axis = tbf - bbf
|
||||||
else:
|
else:
|
||||||
side_target = tool.Cad.furthest_vector(axis1["side"][1], (intersect3, intersect4))
|
pt = bbn.to_2d().to_3d()
|
||||||
base_percent = tool.Cad.edge_percent(base_target, axis1["base"])
|
x_axis = bsf - bbn
|
||||||
side_percent = tool.Cad.edge_percent(side_target, axis1["side"])
|
y_axis = tbn - bbn
|
||||||
if base_percent < side_percent:
|
|
||||||
self.body[0] = tool.Cad.point_on_edge(base_target, axis1["reference"])
|
|
||||||
else:
|
|
||||||
self.body[0] = tool.Cad.point_on_edge(side_target, axis1["reference"])
|
|
||||||
|
|
||||||
# Mitre clip
|
if connection1 != "ATEND":
|
||||||
if connection1 == connection2:
|
y_axis *= -1
|
||||||
if angle < 0:
|
z_axis = x_axis.cross(y_axis)
|
||||||
clip1 = tool.Cad.furthest_vector(axis1["base"][1], (intersect1, intersect2))
|
y_axis = z_axis.cross(x_axis)
|
||||||
clip2 = tool.Cad.closest_vector(axis1["side"][1], (intersect3, intersect4))
|
|
||||||
else:
|
self.clippings.append(
|
||||||
clip1 = tool.Cad.closest_vector(axis1["base"][1], (intersect1, intersect2))
|
{
|
||||||
clip2 = tool.Cad.furthest_vector(axis1["side"][1], (intersect3, intersect4))
|
"type": "IfcBooleanClippingResult",
|
||||||
y_axis = Vector((0, 0, 1))
|
"operand_type": "IfcHalfSpaceSolid",
|
||||||
else:
|
"matrix": self.create_matrix(pt, x_axis, y_axis, z_axis),
|
||||||
if angle < 0:
|
}
|
||||||
clip1 = tool.Cad.closest_vector(axis1["base"][1], (intersect1, intersect2))
|
)
|
||||||
clip2 = tool.Cad.furthest_vector(axis1["side"][1], (intersect3, intersect4))
|
|
||||||
else:
|
|
||||||
clip1 = tool.Cad.furthest_vector(axis1["base"][1], (intersect1, intersect2))
|
|
||||||
clip2 = tool.Cad.closest_vector(axis1["side"][1], (intersect3, intersect4))
|
|
||||||
y_axis = Vector((0, 0, 1))
|
|
||||||
x_axis = (clip1 - clip2).normalized().to_3d()
|
|
||||||
z_axis = x_axis.cross(y_axis)
|
|
||||||
self.clippings.append(
|
|
||||||
{
|
|
||||||
"type": "IfcBooleanClippingResult",
|
|
||||||
"operand_type": "IfcHalfSpaceSolid",
|
|
||||||
"matrix": self.create_matrix(clip1.to_3d(), x_axis, y_axis, z_axis),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
# This is the standard L and T joints described by IFC
|
# This is the standard L and T joints described by IFC
|
||||||
if connection1 == "ATEND":
|
if (
|
||||||
|
tool.Cad.is_x(abs(angle), (90, 270), tolerance=0.001)
|
||||||
|
and not extrusion1["is_sloped"]
|
||||||
|
and not extrusion2["is_sloped"]
|
||||||
|
):
|
||||||
if is_relating:
|
if is_relating:
|
||||||
base_target = tool.Cad.furthest_vector(axis1["base"][0], (intersect1, intersect2))
|
self.body[j] = bbf.to_2d()
|
||||||
else:
|
else:
|
||||||
base_target = tool.Cad.closest_vector(axis1["base"][0], (intersect1, intersect2))
|
self.body[j] = bbn.to_2d()
|
||||||
if tool.Cad.is_x(abs(angle), (90, 270), tolerance=0.001):
|
return True
|
||||||
self.body[1] = tool.Cad.point_on_edge(base_target, axis1["reference"])
|
|
||||||
else:
|
bsf_ = tool.Cad.point_on_edge(bsf, bba1)
|
||||||
if is_relating:
|
tbf_ = tool.Cad.point_on_edge(tbf, bba1)
|
||||||
side_target = tool.Cad.furthest_vector(axis1["side"][0], (intersect3, intersect4))
|
tsf_ = tool.Cad.point_on_edge(tsf, bba1)
|
||||||
else:
|
new_body = tool.Cad.furthest_vector(bba1[i], (bbf, bsf_)).copy()
|
||||||
side_target = tool.Cad.closest_vector(axis1["side"][0], (intersect3, intersect4))
|
new_body = tool.Cad.furthest_vector(bba1[i], (new_body, tbf_)).copy()
|
||||||
base_percent = tool.Cad.edge_percent(base_target, axis1["base"])
|
new_body = tool.Cad.furthest_vector(bba1[i], (new_body, tsf_)).copy()
|
||||||
side_percent = tool.Cad.edge_percent(side_target, axis1["side"])
|
self.body[j] = new_body.to_2d()
|
||||||
if base_percent > side_percent:
|
|
||||||
self.body[1] = tool.Cad.point_on_edge(base_target, axis1["reference"])
|
if is_relating:
|
||||||
clip = side_target.to_3d()
|
pt = bbf.to_2d().to_3d()
|
||||||
x_axis = (side_target - base_target).normalized().to_3d()
|
x_axis = bsf - bbf
|
||||||
y_axis = Vector((0, 0, 1))
|
y_axis = tbf - bbf
|
||||||
z_axis = x_axis.cross(y_axis)
|
|
||||||
self.clippings.append(
|
|
||||||
{
|
|
||||||
"type": "IfcBooleanClippingResult",
|
|
||||||
"operand_type": "IfcHalfSpaceSolid",
|
|
||||||
"matrix": self.create_matrix(clip, x_axis, y_axis, z_axis),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
self.body[1] = tool.Cad.point_on_edge(side_target, axis1["reference"])
|
|
||||||
clip = base_target.to_3d()
|
|
||||||
x_axis = (side_target - base_target).normalized().to_3d()
|
|
||||||
y_axis = Vector((0, 0, 1))
|
|
||||||
z_axis = x_axis.cross(y_axis)
|
|
||||||
self.clippings.append(
|
|
||||||
{
|
|
||||||
"type": "IfcBooleanClippingResult",
|
|
||||||
"operand_type": "IfcHalfSpaceSolid",
|
|
||||||
"matrix": self.create_matrix(clip, x_axis, y_axis, z_axis),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
if is_relating:
|
pt = bbn.to_2d().to_3d()
|
||||||
base_target = tool.Cad.furthest_vector(axis1["base"][1], (intersect1, intersect2))
|
x_axis = bsn - bbn
|
||||||
else:
|
y_axis = tbn - bbn
|
||||||
base_target = tool.Cad.closest_vector(axis1["base"][1], (intersect1, intersect2))
|
if connection1 != "ATEND":
|
||||||
if tool.Cad.is_x(abs(angle), (90, 270), tolerance=0.001):
|
y_axis *= -1
|
||||||
self.body[0] = tool.Cad.point_on_edge(base_target, axis1["reference"])
|
z_axis = x_axis.cross(y_axis)
|
||||||
else:
|
y_axis = z_axis.cross(x_axis)
|
||||||
if is_relating:
|
|
||||||
side_target = tool.Cad.furthest_vector(axis1["side"][1], (intersect3, intersect4))
|
self.clippings.append(
|
||||||
else:
|
{
|
||||||
side_target = tool.Cad.closest_vector(axis1["side"][1], (intersect3, intersect4))
|
"type": "IfcBooleanClippingResult",
|
||||||
base_percent = tool.Cad.edge_percent(base_target, axis1["base"])
|
"operand_type": "IfcHalfSpaceSolid",
|
||||||
side_percent = tool.Cad.edge_percent(side_target, axis1["side"])
|
"matrix": self.create_matrix(pt, x_axis, y_axis, z_axis),
|
||||||
if base_percent < side_percent:
|
}
|
||||||
self.body[0] = tool.Cad.point_on_edge(base_target, axis1["reference"])
|
)
|
||||||
clip = side_target.to_3d()
|
|
||||||
x_axis = (side_target - base_target).normalized().to_3d()
|
|
||||||
y_axis = Vector((0, 0, 1))
|
|
||||||
z_axis = y_axis.cross(x_axis)
|
|
||||||
self.clippings.append(
|
|
||||||
{
|
|
||||||
"type": "IfcBooleanClippingResult",
|
|
||||||
"operand_type": "IfcHalfSpaceSolid",
|
|
||||||
"matrix": self.create_matrix(clip, x_axis, y_axis, z_axis),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
self.body[0] = tool.Cad.point_on_edge(side_target, axis1["reference"])
|
|
||||||
clip = base_target.to_3d()
|
|
||||||
x_axis = (side_target - base_target).normalized().to_3d()
|
|
||||||
y_axis = Vector((0, 0, 1))
|
|
||||||
z_axis = y_axis.cross(x_axis)
|
|
||||||
self.clippings.append(
|
|
||||||
{
|
|
||||||
"type": "IfcBooleanClippingResult",
|
|
||||||
"operand_type": "IfcHalfSpaceSolid",
|
|
||||||
"matrix": self.create_matrix(clip, x_axis, y_axis, z_axis),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
return True
|
return True
|
||||||
|
|||||||
Reference in New Issue
Block a user