Dumb walls now auto update thicknesses if the wall layer thickness changes

This commit is contained in:
Dion Moult
2021-05-26 11:36:06 +10:00
parent 96465bed8f
commit f5f314a58f
2 changed files with 103 additions and 10 deletions
@@ -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"}