See #1227. Basic implementation of sloped walls (joins not yet considered)

This commit is contained in:
Dion Moult
2025-03-05 22:10:33 +11:00
parent 3fc312752b
commit 7c6e124fe9
2 changed files with 44 additions and 18 deletions
+18 -6
View File
@@ -1043,7 +1043,8 @@ class Loader(bonsai.core.tool.Loader):
bm.from_mesh(mesh)
prev_co = None
co = Vector((0.0, offset, 0.0))
no = Vector((0.0, 1.0, 0.0))
# no = Vector((0.0, 1.0, 0.0))
no = (cls.get_extrusion_vector(element).cross(Vector([1., 0., 0.]))).normalized()
# Cache this
body = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW")
styles = {}
@@ -1053,7 +1054,8 @@ class Loader(bonsai.core.tool.Loader):
styles[style] = i
for layer in layer_set.MaterialLayers[:-1]:
prev_co = co.copy()
co.y += layer.LayerThickness * cls.unit_scale * sense_factor
co += no * layer.LayerThickness * cls.unit_scale * sense_factor
# co.y += layer.LayerThickness * cls.unit_scale * sense_factor
bisect_geom = bmesh.ops.bisect_plane(
bm, geom=bm.verts[:] + bm.edges[:] + bm.faces[:], dist=0.0001, plane_co=co, plane_no=no
)
@@ -1064,8 +1066,9 @@ class Loader(bonsai.core.tool.Loader):
mesh.materials.append(tool.Ifc.get_object(style))
for face in bisect_geom["geom"]:
if isinstance(face, bmesh.types.BMFace):
center = face.calc_center_bounds() * sense_factor
if center.y < co.y and center.y > prev_co.y:
center = face.calc_center_median()
# if center.y < co.y and center.y > prev_co.y:
if (center - co).dot(no) < 0 and (center - prev_co).dot(no) >= 0:
face.material_index = material_index
has_layer_styles = True
@@ -1077,8 +1080,9 @@ class Loader(bonsai.core.tool.Loader):
mesh.materials.append(tool.Ifc.get_object(style))
for face in bisect_geom["geom"]:
if isinstance(face, bmesh.types.BMFace):
center = face.calc_center_bounds() * sense_factor
if center.y > co.y:
center = face.calc_center_median() * sense_factor
# if center.y > co.y:
if (center - co).dot(no) >= 0:
face.material_index = material_index
has_layer_styles = True
@@ -1087,6 +1091,14 @@ class Loader(bonsai.core.tool.Loader):
mesh["has_layer_styles"] = has_layer_styles
return mesh
@classmethod
def get_extrusion_vector(cls, wall):
if body := ifcopenshell.util.representation.get_representation(wall, "Model", "Body", "MODEL_VIEW"):
for item in ifcopenshell.util.representation.resolve_representation(body).Items:
if item.is_a("IfcExtrudedAreaSolid"):
return Vector(item.ExtrudedDirection.DirectionRatios)
return Vector([0., 0., 1.])
@classmethod
def create_mesh_from_shape(
cls,
+26 -12
View File
@@ -14,6 +14,7 @@ import ifcopenshell.util.element
# from ifcopenshell.util.shape_builder import VectorType, SequenceOfVectors
from itertools import cycle
from collections import namedtuple
from math import sin, cos, radians
f = ifcopenshell.api.project.create_file()
@@ -46,7 +47,7 @@ ifcopenshell.api.style.add_surface_style(f, style=style, ifc_class="IfcSurfaceSt
ifcopenshell.api.style.assign_material_style(f, material=material2, style=style, context=body)
def test_wall(offset, p1, p2, p3, p4):
def test_wall(offset, p1, p2, p3, p4, a1=None, a2=None):
offset *= 1.5
wall_type_a = ifcopenshell.api.root.create_entity(f, ifc_class="IfcWallType", name="A")
wall_type_b = ifcopenshell.api.root.create_entity(f, ifc_class="IfcWallType", name="B")
@@ -162,7 +163,7 @@ def test_wall(offset, p1, p2, p3, p4):
related_connection="ATSTART",
)
Foo(f, body, axis).regenerate(wall_a)
Foo(f, body, axis).regenerate(wall_a, angle=a1)
Foo(f, body, axis).regenerate(wall_b)
Foo(f, body, axis).regenerate(wall_c)
@@ -238,7 +239,7 @@ class Foo:
self.body = body
self.axis = axis
def regenerate(self, wall):
def regenerate(self, wall, angle=None):
print("-" * 100)
print(wall)
layers = self.get_layers(wall)
@@ -246,7 +247,8 @@ class Foo:
return
reference = self.get_reference_line(wall)
self.reference_p1, self.reference_p2 = reference
axes = self.get_axes(wall, reference, layers)
self.angle = angle or self.get_angle(wall)
axes = self.get_axes(wall, reference, layers, self.angle)
self.miny = axes[0][0][1]
self.maxy = axes[-1][0][1]
self.end_point = None
@@ -342,7 +344,9 @@ class Foo:
else:
profile = profiles[0]
item = builder.extrude(profile, magnitude=1.0)
item = builder.extrude(
profile, magnitude=1.0, extrusion_vector=np.array([0.0, sin(self.angle), cos(self.angle)])
)
rep = builder.get_representation(self.body, items=[item])
if old_rep := ifcopenshell.util.representation.get_representation(wall, self.body):
ifcopenshell.util.element.replace_element(old_rep, rep)
@@ -367,8 +371,8 @@ class Foo:
# axes = self.get_axes(wall2, layers2)
reference1 = self.get_reference_line(wall1)
reference2 = self.get_reference_line(wall2)
axes1 = self.get_axes(wall1, reference1, layers1)
axes2 = self.get_axes(wall2, reference2, layers2)
axes1 = self.get_axes(wall1, reference1, layers1, self.angle)
axes2 = self.get_axes(wall2, reference2, layers2, self.get_angle(wall2))
matrix1i = np.linalg.inv(ifcopenshell.util.placement.get_local_placement(wall1.ObjectPlacement))
matrix2 = ifcopenshell.util.placement.get_local_placement(wall2.ObjectPlacement)
print(axes1)
@@ -444,14 +448,14 @@ class Foo:
segment = []
for point in points:
segment.append(point)
if len(segment) == 1: # Not enough points to categorise the segment
if len(segment) == 1: # Not enough points to categorise the segment
continue
elif {segment[0][1], segment[-1][1]} == split_ys: # This segment splits the wall
elif {segment[0][1], segment[-1][1]} == split_ys: # This segment splits the wall
if segment[0][1] > segment[-1][1]: # Go in the +Y direction
segment.reverse()
self.split_points.append(segment)
segment = []
elif segment[0][1] == segment[-1][1]: # This segment cuts some of the wall
elif segment[0][1] == segment[-1][1]: # This segment cuts some of the wall
if segment[0][1] == self.maxy: # Go in the +X direction
if segment[0][0] > segment[-1][0]:
segment.reverse()
@@ -577,7 +581,16 @@ class Foo:
return [np.array(points[1]), np.array(points[0])]
return [np.array((0.0, 0.0)), np.array((1.0, 0.0))]
def get_axes(self, wall, reference, layers: list[PrioritisedLayer]):
def get_angle(self, wall):
if body := ifcopenshell.util.representation.get_representation(wall, "Model", "Body", "MODEL_VIEW"):
for item in ifcopenshell.util.representation.resolve_representation(body).Items:
if item.is_a("IfcExtrudedAreaSolid"):
return ifcopenshell.util.shape_builder.np_angle_signed(
np.array((0.0, 1.0)), np.array(item.ExtrudedDirection.DirectionRatios[1:])
)
return 0.0
def get_axes(self, wall, reference, layers: list[PrioritisedLayer], angle: float):
axes = [[p.copy() for p in reference]]
# Apply usage to convert the Reference line into MlsBase
sense_factor = 1
@@ -587,7 +600,8 @@ class Foo:
sense_factor = 1 if usage.DirectionSense == "POSITIVE" else -1
for layer in layers:
axes.append([p.copy() + np.array((0.0, layer.thickness * sense_factor)) for p in axes[-1]])
y_offset = (layer.thickness * sense_factor) / cos(angle)
axes.append([p.copy() + np.array((0.0, y_offset)) for p in axes[-1]])
return axes