Rebuild wall engine (again) to allow for sloped wall clippings.

This commit is contained in:
Dion Moult
2022-10-18 20:02:00 +11:00
parent 9ef65c9cc5
commit f045e86a38
2 changed files with 132 additions and 158 deletions
@@ -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
+130 -156
View File
@@ -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