mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 18:16:40 +00:00
Dumb walls now auto update thicknesses if the wall layer thickness changes
This commit is contained in:
@@ -133,11 +133,18 @@ class DumbWallAligner:
|
||||
self.reference_wall = reference_wall
|
||||
|
||||
def align_centerline(self):
|
||||
self.align(self.reference_wall.matrix_world.translation, self.reference_wall.matrix_world @ Vector((1, 0, 0)))
|
||||
width = (Vector(self.wall.bound_box[3]) - Vector(self.wall.bound_box[0])).y
|
||||
reference_width = (Vector(self.reference_wall.bound_box[3]) - Vector(self.reference_wall.bound_box[0])).y
|
||||
offset = self.wall.matrix_world.to_quaternion() @ Vector((0, (reference_width / 2) - (width / 2), 0))
|
||||
self.align(
|
||||
self.reference_wall.matrix_world @ Vector(self.reference_wall.bound_box[0]),
|
||||
self.reference_wall.matrix_world @ Vector(self.reference_wall.bound_box[4]),
|
||||
offset,
|
||||
)
|
||||
|
||||
def align_exterior(self):
|
||||
width = (Vector(self.wall.bound_box[3]) - Vector(self.wall.bound_box[0])).y
|
||||
offset = self.wall.matrix_world.to_quaternion() @ Vector((0, -width / 2, 0))
|
||||
wall_width = (Vector(self.wall.bound_box[3]) - Vector(self.wall.bound_box[0])).y
|
||||
offset = self.wall.matrix_world.to_quaternion() @ Vector((0, -wall_width, 0))
|
||||
self.align(
|
||||
self.reference_wall.matrix_world @ Vector(self.reference_wall.bound_box[3]),
|
||||
self.reference_wall.matrix_world @ Vector(self.reference_wall.bound_box[7]),
|
||||
@@ -145,13 +152,7 @@ class DumbWallAligner:
|
||||
)
|
||||
|
||||
def align_interior(self):
|
||||
width = (Vector(self.wall.bound_box[3]) - Vector(self.wall.bound_box[0])).y
|
||||
offset = self.wall.matrix_world.to_quaternion() @ Vector((0, width / 2, 0))
|
||||
self.align(
|
||||
self.reference_wall.matrix_world @ Vector(self.reference_wall.bound_box[0]),
|
||||
self.reference_wall.matrix_world @ Vector(self.reference_wall.bound_box[4]),
|
||||
offset,
|
||||
)
|
||||
self.align(self.reference_wall.matrix_world.translation, self.reference_wall.matrix_world @ Vector((1, 0, 0)))
|
||||
|
||||
def align(self, start, end, offset=None):
|
||||
if offset is None:
|
||||
|
||||
@@ -7,6 +7,7 @@ import ifcopenshell.util.element
|
||||
import ifcopenshell.util.representation
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from ifcopenshell.api.pset.data import Data as PsetData
|
||||
from math import pi
|
||||
from mathutils import Vector
|
||||
|
||||
|
||||
@@ -104,6 +105,94 @@ def calculate_dumb_quantities(usecase_path, ifc_file, **settings):
|
||||
PsetData.load(ifc_file, obj.BIMObjectProperties.ifc_definition_id)
|
||||
|
||||
|
||||
class DumbWallPlaner:
|
||||
def regenerate_dumb_wall_thicknesses(self, usecase_path, ifc_file, **settings):
|
||||
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(ifc_file)
|
||||
layer = settings["layer"]
|
||||
thickness = settings["attributes"].get("LayerThickness")
|
||||
if thickness is None or layer.LayerThickness == thickness:
|
||||
return
|
||||
delta_thickness = thickness - layer.LayerThickness
|
||||
for layer_set in layer.ToMaterialLayerSet:
|
||||
total_thickness = sum([l.LayerThickness for l in layer_set.MaterialLayers]) * self.unit_scale
|
||||
if not total_thickness:
|
||||
continue
|
||||
for inverse in ifc_file.get_inverse(layer_set):
|
||||
if not inverse.is_a("IfcMaterialLayerSetUsage"):
|
||||
continue
|
||||
if ifc_file.schema == "IFC2X3":
|
||||
for rel in ifc_file.get_inverse(inverse):
|
||||
if not rel.is_a("IfcRelAssociatesMaterial"):
|
||||
continue
|
||||
for element in rel.RelatedObjects:
|
||||
self.regenerate_wall_thickness(element, delta_thickness)
|
||||
else:
|
||||
for rel in inverse.AssociatedTo:
|
||||
for element in rel.RelatedObjects:
|
||||
self.regenerate_wall_thickness(element, delta_thickness)
|
||||
|
||||
def regenerate_wall_thickness(self, element, delta_thickness):
|
||||
parametric = ifcopenshell.util.element.get_psets(element).get("EPset_Parametric")
|
||||
if not parametric or parametric["Engine"] != "BlenderBIM.DumbWall":
|
||||
return
|
||||
try:
|
||||
obj = IfcStore.id_map[element.id()]
|
||||
obj.name # In case the object has been deleted
|
||||
except:
|
||||
return
|
||||
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(obj.data)
|
||||
bmesh.ops.dissolve_limit(bm, angle_limit=pi / 180 * 1, verts=bm.verts, edges=bm.edges)
|
||||
|
||||
min_face, max_face = self.get_wall_end_faces(obj, bm)
|
||||
|
||||
self.thicken_face(min_face, delta_thickness)
|
||||
self.thicken_face(max_face, delta_thickness)
|
||||
|
||||
bm.to_mesh(obj.data)
|
||||
obj.data.update()
|
||||
bm.free()
|
||||
|
||||
def thicken_face(self, face, delta_thickness):
|
||||
slide_magnitude = abs(delta_thickness) / 2 * self.unit_scale
|
||||
for vert in face.verts:
|
||||
slide_vector = None
|
||||
for edge in vert.link_edges:
|
||||
other_vert = edge.verts[1] if edge.verts[0] == vert else edge.verts[0]
|
||||
if delta_thickness > 0:
|
||||
potential_slide_vector = vert.co - other_vert.co
|
||||
else:
|
||||
potential_slide_vector = other_vert.co - vert.co
|
||||
if abs(potential_slide_vector.x) > 0.9 or abs(potential_slide_vector.z) > 0.9:
|
||||
continue
|
||||
slide_vector = potential_slide_vector
|
||||
break
|
||||
if not slide_vector:
|
||||
continue
|
||||
slide_vector *= (slide_magnitude / abs(slide_vector.y))
|
||||
vert.co += slide_vector
|
||||
|
||||
# An end face is a quad that is on one end of the wall or the other. It must
|
||||
# have at least one vertex on either extreme X-axis, and a non-insignificant
|
||||
# X component of its face normal
|
||||
def get_wall_end_faces(self, wall, bm):
|
||||
min_face = None
|
||||
max_face = None
|
||||
min_x = min([v[0] for v in wall.bound_box])
|
||||
max_x = max([v[0] for v in wall.bound_box])
|
||||
bm.faces.ensure_lookup_table()
|
||||
for f in bm.faces:
|
||||
for v in f.verts:
|
||||
if v.co.x == min_x and abs(f.normal.x) > 0.1:
|
||||
min_face = f
|
||||
elif v.co.x == max_x and abs(f.normal.x) > 0.1:
|
||||
max_face = f
|
||||
if min_face and max_face:
|
||||
break
|
||||
return min_face, max_face
|
||||
|
||||
|
||||
class ActivateParametricEngine(bpy.types.Operator):
|
||||
bl_idname = "bim.activate_parametric_engine"
|
||||
bl_label = "Activate Parametric Engine"
|
||||
@@ -116,4 +205,7 @@ class ActivateParametricEngine(bpy.types.Operator):
|
||||
ifcopenshell.api.add_post_listener(
|
||||
"geometry.add_representation", IfcStore.get_file(), calculate_dumb_quantities
|
||||
)
|
||||
ifcopenshell.api.add_pre_listener(
|
||||
"material.edit_layer", IfcStore.get_file(), DumbWallPlaner().regenerate_dumb_wall_thicknesses
|
||||
)
|
||||
return {"FINISHED"}
|
||||
|
||||
Reference in New Issue
Block a user