mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-30 03:59:55 +00:00
See #1227. Basic implementation of sloped walls (joins not yet considered)
This commit is contained in:
@@ -1043,7 +1043,8 @@ class Loader(bonsai.core.tool.Loader):
|
|||||||
bm.from_mesh(mesh)
|
bm.from_mesh(mesh)
|
||||||
prev_co = None
|
prev_co = None
|
||||||
co = Vector((0.0, offset, 0.0))
|
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
|
# Cache this
|
||||||
body = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW")
|
body = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW")
|
||||||
styles = {}
|
styles = {}
|
||||||
@@ -1053,7 +1054,8 @@ class Loader(bonsai.core.tool.Loader):
|
|||||||
styles[style] = i
|
styles[style] = i
|
||||||
for layer in layer_set.MaterialLayers[:-1]:
|
for layer in layer_set.MaterialLayers[:-1]:
|
||||||
prev_co = co.copy()
|
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(
|
bisect_geom = bmesh.ops.bisect_plane(
|
||||||
bm, geom=bm.verts[:] + bm.edges[:] + bm.faces[:], dist=0.0001, plane_co=co, plane_no=no
|
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))
|
mesh.materials.append(tool.Ifc.get_object(style))
|
||||||
for face in bisect_geom["geom"]:
|
for face in bisect_geom["geom"]:
|
||||||
if isinstance(face, bmesh.types.BMFace):
|
if isinstance(face, bmesh.types.BMFace):
|
||||||
center = face.calc_center_bounds() * sense_factor
|
center = face.calc_center_median()
|
||||||
if center.y < co.y and center.y > prev_co.y:
|
# 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
|
face.material_index = material_index
|
||||||
has_layer_styles = True
|
has_layer_styles = True
|
||||||
|
|
||||||
@@ -1077,8 +1080,9 @@ class Loader(bonsai.core.tool.Loader):
|
|||||||
mesh.materials.append(tool.Ifc.get_object(style))
|
mesh.materials.append(tool.Ifc.get_object(style))
|
||||||
for face in bisect_geom["geom"]:
|
for face in bisect_geom["geom"]:
|
||||||
if isinstance(face, bmesh.types.BMFace):
|
if isinstance(face, bmesh.types.BMFace):
|
||||||
center = face.calc_center_bounds() * sense_factor
|
center = face.calc_center_median() * sense_factor
|
||||||
if center.y > co.y:
|
# if center.y > co.y:
|
||||||
|
if (center - co).dot(no) >= 0:
|
||||||
face.material_index = material_index
|
face.material_index = material_index
|
||||||
has_layer_styles = True
|
has_layer_styles = True
|
||||||
|
|
||||||
@@ -1087,6 +1091,14 @@ class Loader(bonsai.core.tool.Loader):
|
|||||||
mesh["has_layer_styles"] = has_layer_styles
|
mesh["has_layer_styles"] = has_layer_styles
|
||||||
return mesh
|
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
|
@classmethod
|
||||||
def create_mesh_from_shape(
|
def create_mesh_from_shape(
|
||||||
cls,
|
cls,
|
||||||
|
|||||||
+26
-12
@@ -14,6 +14,7 @@ import ifcopenshell.util.element
|
|||||||
# from ifcopenshell.util.shape_builder import VectorType, SequenceOfVectors
|
# from ifcopenshell.util.shape_builder import VectorType, SequenceOfVectors
|
||||||
from itertools import cycle
|
from itertools import cycle
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
from math import sin, cos, radians
|
||||||
|
|
||||||
f = ifcopenshell.api.project.create_file()
|
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)
|
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
|
offset *= 1.5
|
||||||
wall_type_a = ifcopenshell.api.root.create_entity(f, ifc_class="IfcWallType", name="A")
|
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")
|
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",
|
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_b)
|
||||||
Foo(f, body, axis).regenerate(wall_c)
|
Foo(f, body, axis).regenerate(wall_c)
|
||||||
|
|
||||||
@@ -238,7 +239,7 @@ class Foo:
|
|||||||
self.body = body
|
self.body = body
|
||||||
self.axis = axis
|
self.axis = axis
|
||||||
|
|
||||||
def regenerate(self, wall):
|
def regenerate(self, wall, angle=None):
|
||||||
print("-" * 100)
|
print("-" * 100)
|
||||||
print(wall)
|
print(wall)
|
||||||
layers = self.get_layers(wall)
|
layers = self.get_layers(wall)
|
||||||
@@ -246,7 +247,8 @@ class Foo:
|
|||||||
return
|
return
|
||||||
reference = self.get_reference_line(wall)
|
reference = self.get_reference_line(wall)
|
||||||
self.reference_p1, self.reference_p2 = reference
|
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.miny = axes[0][0][1]
|
||||||
self.maxy = axes[-1][0][1]
|
self.maxy = axes[-1][0][1]
|
||||||
self.end_point = None
|
self.end_point = None
|
||||||
@@ -342,7 +344,9 @@ class Foo:
|
|||||||
else:
|
else:
|
||||||
profile = profiles[0]
|
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])
|
rep = builder.get_representation(self.body, items=[item])
|
||||||
if old_rep := ifcopenshell.util.representation.get_representation(wall, self.body):
|
if old_rep := ifcopenshell.util.representation.get_representation(wall, self.body):
|
||||||
ifcopenshell.util.element.replace_element(old_rep, rep)
|
ifcopenshell.util.element.replace_element(old_rep, rep)
|
||||||
@@ -367,8 +371,8 @@ class Foo:
|
|||||||
# axes = self.get_axes(wall2, layers2)
|
# axes = self.get_axes(wall2, layers2)
|
||||||
reference1 = self.get_reference_line(wall1)
|
reference1 = self.get_reference_line(wall1)
|
||||||
reference2 = self.get_reference_line(wall2)
|
reference2 = self.get_reference_line(wall2)
|
||||||
axes1 = self.get_axes(wall1, reference1, layers1)
|
axes1 = self.get_axes(wall1, reference1, layers1, self.angle)
|
||||||
axes2 = self.get_axes(wall2, reference2, layers2)
|
axes2 = self.get_axes(wall2, reference2, layers2, self.get_angle(wall2))
|
||||||
matrix1i = np.linalg.inv(ifcopenshell.util.placement.get_local_placement(wall1.ObjectPlacement))
|
matrix1i = np.linalg.inv(ifcopenshell.util.placement.get_local_placement(wall1.ObjectPlacement))
|
||||||
matrix2 = ifcopenshell.util.placement.get_local_placement(wall2.ObjectPlacement)
|
matrix2 = ifcopenshell.util.placement.get_local_placement(wall2.ObjectPlacement)
|
||||||
print(axes1)
|
print(axes1)
|
||||||
@@ -444,14 +448,14 @@ class Foo:
|
|||||||
segment = []
|
segment = []
|
||||||
for point in points:
|
for point in points:
|
||||||
segment.append(point)
|
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
|
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
|
if segment[0][1] > segment[-1][1]: # Go in the +Y direction
|
||||||
segment.reverse()
|
segment.reverse()
|
||||||
self.split_points.append(segment)
|
self.split_points.append(segment)
|
||||||
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][1] == self.maxy: # Go in the +X direction
|
||||||
if segment[0][0] > segment[-1][0]:
|
if segment[0][0] > segment[-1][0]:
|
||||||
segment.reverse()
|
segment.reverse()
|
||||||
@@ -577,7 +581,16 @@ class Foo:
|
|||||||
return [np.array(points[1]), np.array(points[0])]
|
return [np.array(points[1]), np.array(points[0])]
|
||||||
return [np.array((0.0, 0.0)), np.array((1.0, 0.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]]
|
axes = [[p.copy() for p in reference]]
|
||||||
# Apply usage to convert the Reference line into MlsBase
|
# Apply usage to convert the Reference line into MlsBase
|
||||||
sense_factor = 1
|
sense_factor = 1
|
||||||
@@ -587,7 +600,8 @@ class Foo:
|
|||||||
sense_factor = 1 if usage.DirectionSense == "POSITIVE" else -1
|
sense_factor = 1 if usage.DirectionSense == "POSITIVE" else -1
|
||||||
|
|
||||||
for layer in layers:
|
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
|
return axes
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user