mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-18 22:33:33 +00:00
Fix bug where adding / removing layers never triggered a regeneration of walls.
Continue our slow, ongoing purge of listeners.
This commit is contained in:
@@ -30,6 +30,7 @@ import bonsai.core.material as core
|
|||||||
import bonsai.bim.module.model.profile as model_profile
|
import bonsai.bim.module.model.profile as model_profile
|
||||||
from bonsai.bim.ifc import IfcStore
|
from bonsai.bim.ifc import IfcStore
|
||||||
from typing import Any, Union, TYPE_CHECKING
|
from typing import Any, Union, TYPE_CHECKING
|
||||||
|
from bonsai.bim.module.model import wall, slab
|
||||||
|
|
||||||
|
|
||||||
class LoadMaterials(bpy.types.Operator):
|
class LoadMaterials(bpy.types.Operator):
|
||||||
@@ -361,15 +362,14 @@ class AddLayer(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||||
self.file = IfcStore.get_file()
|
layer_set = tool.Ifc.get().by_id(self.layer_set)
|
||||||
layer = ifcopenshell.api.run(
|
ifcopenshell.api.material.add_layer(
|
||||||
"material.add_layer",
|
tool.Ifc.get(),
|
||||||
self.file,
|
layer_set=layer_set,
|
||||||
**{
|
material=tool.Ifc.get().by_id(int(obj.BIMObjectMaterialProperties.material)),
|
||||||
"layer_set": self.file.by_id(self.layer_set),
|
|
||||||
"material": self.file.by_id(int(obj.BIMObjectMaterialProperties.material)),
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
slab.DumbSlabPlaner().regenerate_from_layer_set(layer_set)
|
||||||
|
wall.DumbWallPlaner().regenerate_from_layer_set(layer_set)
|
||||||
|
|
||||||
|
|
||||||
class ReorderMaterialSetItem(bpy.types.Operator, tool.Ifc.Operator):
|
class ReorderMaterialSetItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
@@ -405,11 +405,15 @@ class RemoveLayer(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
layer = tool.Ifc.get().by_id(self.layer)
|
layer = tool.Ifc.get().by_id(self.layer)
|
||||||
|
material_sets = set(layer.ToMaterialLayerSet)
|
||||||
for material_set in layer.ToMaterialLayerSet:
|
for material_set in layer.ToMaterialLayerSet:
|
||||||
if len(material_set.MaterialLayers) == 1:
|
if len(material_set.MaterialLayers) == 1:
|
||||||
self.report({"ERROR"}, "At least one layer must exist")
|
self.report({"ERROR"}, "At least one layer must exist")
|
||||||
return {"CANCELLED"}
|
return {"CANCELLED"}
|
||||||
ifcopenshell.api.run("material.remove_layer", tool.Ifc.get(), layer=layer)
|
ifcopenshell.api.run("material.remove_layer", tool.Ifc.get(), layer=layer)
|
||||||
|
for material_set in material_sets:
|
||||||
|
slab.DumbSlabPlaner().regenerate_from_layer_set(material_set)
|
||||||
|
wall.DumbWallPlaner().regenerate_from_layer_set(material_set)
|
||||||
|
|
||||||
|
|
||||||
class DuplicateLayer(bpy.types.Operator, tool.Ifc.Operator):
|
class DuplicateLayer(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
@@ -707,32 +711,28 @@ class EditMaterialSetItem(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
attributes = bonsai.bim.helper.export_attributes(props.material_set_item_attributes)
|
attributes = bonsai.bim.helper.export_attributes(props.material_set_item_attributes)
|
||||||
|
|
||||||
if material.is_a("IfcMaterialConstituentSet"):
|
if material.is_a("IfcMaterialConstituentSet"):
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.material.edit_constituent(
|
||||||
"material.edit_constituent",
|
|
||||||
self.file,
|
self.file,
|
||||||
**{
|
constituent=self.file.by_id(self.material_set_item),
|
||||||
"constituent": self.file.by_id(self.material_set_item),
|
attributes=attributes,
|
||||||
"attributes": attributes,
|
material=self.file.by_id(int(obj.BIMObjectMaterialProperties.material_set_item_material)),
|
||||||
"material": self.file.by_id(int(obj.BIMObjectMaterialProperties.material_set_item_material)),
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
elif material.is_a("IfcMaterialLayerSet"):
|
elif material.is_a("IfcMaterialLayerSet"):
|
||||||
ifcopenshell.api.run(
|
layer = self.file.by_id(self.material_set_item)
|
||||||
"material.edit_layer",
|
ifcopenshell.api.material.edit_layer(
|
||||||
self.file,
|
self.file,
|
||||||
**{
|
layer=layer,
|
||||||
"layer": self.file.by_id(self.material_set_item),
|
attributes=attributes,
|
||||||
"attributes": attributes,
|
material=self.file.by_id(int(obj.BIMObjectMaterialProperties.material_set_item_material)),
|
||||||
"material": self.file.by_id(int(obj.BIMObjectMaterialProperties.material_set_item_material)),
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
slab.DumbSlabPlaner().regenerate_from_layer(layer)
|
||||||
|
wall.DumbWallPlaner().regenerate_from_layer(layer)
|
||||||
elif material.is_a("IfcMaterialProfileSet"):
|
elif material.is_a("IfcMaterialProfileSet"):
|
||||||
profile_def = None
|
profile_def = None
|
||||||
if mprops.profiles:
|
if mprops.profiles:
|
||||||
profile_def = tool.Ifc.get().by_id(int(mprops.profiles))
|
profile_def = tool.Ifc.get().by_id(int(mprops.profiles))
|
||||||
|
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.material.edit_profile(
|
||||||
"material.edit_profile",
|
|
||||||
self.file,
|
self.file,
|
||||||
profile=self.file.by_id(self.material_set_item),
|
profile=self.file.by_id(self.material_set_item),
|
||||||
attributes=attributes,
|
attributes=attributes,
|
||||||
|
|||||||
@@ -26,6 +26,9 @@ from bpy.app.handlers import persistent
|
|||||||
|
|
||||||
@persistent
|
@persistent
|
||||||
def load_post(*args):
|
def load_post(*args):
|
||||||
|
# TODO: the goal is to slowly remove these API listeners. In hindsight it
|
||||||
|
# isn't a good idea because it leads to domino events being triggered. It's
|
||||||
|
# less buggy to explicitly code the logic in core.
|
||||||
ifcopenshell.api.add_pre_listener("style.edit_presentation_style", "Bonsai.Root.SyncStyleName", root.sync_name)
|
ifcopenshell.api.add_pre_listener("style.edit_presentation_style", "Bonsai.Root.SyncStyleName", root.sync_name)
|
||||||
|
|
||||||
ifcopenshell.api.add_post_listener(
|
ifcopenshell.api.add_post_listener(
|
||||||
@@ -42,22 +45,10 @@ def load_post(*args):
|
|||||||
product.regenerate_profile_usage,
|
product.regenerate_profile_usage,
|
||||||
)
|
)
|
||||||
|
|
||||||
ifcopenshell.api.add_post_listener(
|
|
||||||
"material.edit_layer", "Bonsai.DumbWall.RegenerateFromLayer", wall.DumbWallPlaner().regenerate_from_layer
|
|
||||||
)
|
|
||||||
ifcopenshell.api.add_post_listener(
|
ifcopenshell.api.add_post_listener(
|
||||||
"type.assign_type", "Bonsai.DumbWall.RegenerateFromType", wall.DumbWallPlaner().regenerate_from_type
|
"type.assign_type", "Bonsai.DumbWall.RegenerateFromType", wall.DumbWallPlaner().regenerate_from_type
|
||||||
)
|
)
|
||||||
|
|
||||||
ifcopenshell.api.add_post_listener(
|
|
||||||
"material.add_layer", "Bonsai.DumbSlab.RegenerateFromLayer", slab.DumbSlabPlaner().regenerate_from_layer
|
|
||||||
)
|
|
||||||
ifcopenshell.api.add_post_listener(
|
|
||||||
"material.remove_layer", "Bonsai.DumbSlab.RegenerateFromLayer", slab.DumbSlabPlaner().regenerate_from_layer
|
|
||||||
)
|
|
||||||
ifcopenshell.api.add_post_listener(
|
|
||||||
"material.edit_layer", "Bonsai.DumbSlab.RegenerateFromLayer", slab.DumbSlabPlaner().regenerate_from_layer
|
|
||||||
)
|
|
||||||
ifcopenshell.api.add_post_listener(
|
ifcopenshell.api.add_post_listener(
|
||||||
"material.edit_layer_usage",
|
"material.edit_layer_usage",
|
||||||
"Bonsai.DumbSlab.RegenerateFromLayerSetUsage",
|
"Bonsai.DumbSlab.RegenerateFromLayerSetUsage",
|
||||||
|
|||||||
@@ -202,10 +202,7 @@ class DumbSlabPlaner:
|
|||||||
obj = bpy.context.active_object
|
obj = bpy.context.active_object
|
||||||
element = tool.Ifc.get_entity(obj)
|
element = tool.Ifc.get_entity(obj)
|
||||||
|
|
||||||
if tool.Model.get_usage_type(element) != "LAYER3":
|
# Called from material.add_layer or material.remove_layer
|
||||||
return
|
|
||||||
|
|
||||||
# Called from materil.add_layer or material.remove_layer
|
|
||||||
material = ifcopenshell.util.element.get_material(element)
|
material = ifcopenshell.util.element.get_material(element)
|
||||||
material_set_usage = tool.Ifc.get().by_id(material.id())
|
material_set_usage = tool.Ifc.get().by_id(material.id())
|
||||||
if not getattr(material_set_usage, "ForLayerSet", False):
|
if not getattr(material_set_usage, "ForLayerSet", False):
|
||||||
@@ -218,38 +215,21 @@ class DumbSlabPlaner:
|
|||||||
|
|
||||||
self.change_thickness(element, total_thickness)
|
self.change_thickness(element, total_thickness)
|
||||||
|
|
||||||
def regenerate_from_layer(self, usecase_path, ifc_file, settings):
|
def regenerate_from_layer(self, layer: ifcopenshell.entity_instance) -> None:
|
||||||
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(ifc_file)
|
for layer_set in layer.ToMaterialLayerSet:
|
||||||
|
self.regenerate_from_layer_set(layer_set)
|
||||||
try:
|
|
||||||
# Called from materil.edit_layer
|
|
||||||
layer = settings["layer"]
|
|
||||||
thickness = settings["attributes"].get("LayerThickness")
|
|
||||||
if thickness is None:
|
|
||||||
return
|
|
||||||
layer_set = layer.ToMaterialLayerSet[0]
|
|
||||||
|
|
||||||
except:
|
|
||||||
# Called from materil.add_layer or material.remove_layer
|
|
||||||
obj = bpy.context.active_object
|
|
||||||
element = tool.Ifc.get_entity(obj)
|
|
||||||
if not element:
|
|
||||||
return
|
|
||||||
material = ifcopenshell.util.element.get_material(element)
|
|
||||||
material_set_usage = tool.Ifc.get().by_id(material.id())
|
|
||||||
if not getattr(material_set_usage, "ForLayerSet", False):
|
|
||||||
return
|
|
||||||
layer_set = material_set_usage.ForLayerSet
|
|
||||||
|
|
||||||
|
def regenerate_from_layer_set(self, layer_set: ifcopenshell.entity_instance) -> None:
|
||||||
|
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||||
total_thickness = sum([l.LayerThickness for l in layer_set.MaterialLayers])
|
total_thickness = sum([l.LayerThickness for l in layer_set.MaterialLayers])
|
||||||
if not total_thickness:
|
if not total_thickness:
|
||||||
return
|
return
|
||||||
|
|
||||||
for inverse in ifc_file.get_inverse(layer_set):
|
for inverse in tool.Ifc.get().get_inverse(layer_set):
|
||||||
if not inverse.is_a("IfcMaterialLayerSetUsage") or inverse.LayerSetDirection != "AXIS3":
|
if not inverse.is_a("IfcMaterialLayerSetUsage") or inverse.LayerSetDirection != "AXIS3":
|
||||||
continue
|
continue
|
||||||
if ifc_file.schema == "IFC2X3":
|
if tool.Ifc.get().schema == "IFC2X3":
|
||||||
for rel in ifc_file.get_inverse(inverse):
|
for rel in tool.Ifc.get().get_inverse(inverse):
|
||||||
if not rel.is_a("IfcRelAssociatesMaterial"):
|
if not rel.is_a("IfcRelAssociatesMaterial"):
|
||||||
continue
|
continue
|
||||||
for element in rel.RelatedObjects:
|
for element in rel.RelatedObjects:
|
||||||
@@ -300,6 +280,8 @@ class DumbSlabPlaner:
|
|||||||
self.change_thickness(element, total_thickness)
|
self.change_thickness(element, total_thickness)
|
||||||
|
|
||||||
def change_thickness(self, element: ifcopenshell.entity_instance, thickness: float) -> None:
|
def change_thickness(self, element: ifcopenshell.entity_instance, thickness: float) -> None:
|
||||||
|
if tool.Model.get_usage_type(element) != "LAYER3":
|
||||||
|
return
|
||||||
layer_params = tool.Model.get_material_layer_parameters(element)
|
layer_params = tool.Model.get_material_layer_parameters(element)
|
||||||
body_context = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW")
|
body_context = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW")
|
||||||
obj = tool.Ifc.get_object(element)
|
obj = tool.Ifc.get_object(element)
|
||||||
|
|||||||
@@ -860,26 +860,26 @@ class DumbWallGenerator:
|
|||||||
|
|
||||||
|
|
||||||
class DumbWallPlaner:
|
class DumbWallPlaner:
|
||||||
def regenerate_from_layer(self, usecase_path, ifc_file, settings):
|
def regenerate_from_layer(self, layer: ifcopenshell.entity_instance) -> None:
|
||||||
if settings["attributes"].get("LayerThickness") is None:
|
|
||||||
return
|
|
||||||
walls = []
|
|
||||||
layer = settings["layer"]
|
|
||||||
for layer_set in layer.ToMaterialLayerSet:
|
for layer_set in layer.ToMaterialLayerSet:
|
||||||
total_thickness = sum([l.LayerThickness for l in layer_set.MaterialLayers])
|
self.regenerate_from_layer_set(layer_set)
|
||||||
if not total_thickness:
|
|
||||||
|
def regenerate_from_layer_set(self, layer_set: ifcopenshell.entity_instance) -> None:
|
||||||
|
walls = []
|
||||||
|
total_thickness = sum([l.LayerThickness for l in layer_set.MaterialLayers])
|
||||||
|
if not total_thickness:
|
||||||
|
return
|
||||||
|
for inverse in tool.Ifc.get().get_inverse(layer_set):
|
||||||
|
if not inverse.is_a("IfcMaterialLayerSetUsage") or inverse.LayerSetDirection != "AXIS2":
|
||||||
continue
|
continue
|
||||||
for inverse in ifc_file.get_inverse(layer_set):
|
if tool.Ifc.get().schema == "IFC2X3":
|
||||||
if not inverse.is_a("IfcMaterialLayerSetUsage") or inverse.LayerSetDirection != "AXIS2":
|
for rel in tool.Ifc.get().get_inverse(inverse):
|
||||||
continue
|
if not rel.is_a("IfcRelAssociatesMaterial"):
|
||||||
if ifc_file.schema == "IFC2X3":
|
continue
|
||||||
for rel in ifc_file.get_inverse(inverse):
|
walls.extend([tool.Ifc.get_object(e) for e in rel.RelatedObjects])
|
||||||
if not rel.is_a("IfcRelAssociatesMaterial"):
|
else:
|
||||||
continue
|
for rel in inverse.AssociatedTo:
|
||||||
walls.extend([tool.Ifc.get_object(e) for e in rel.RelatedObjects])
|
walls.extend([tool.Ifc.get_object(e) for e in rel.RelatedObjects])
|
||||||
else:
|
|
||||||
for rel in inverse.AssociatedTo:
|
|
||||||
walls.extend([tool.Ifc.get_object(e) for e in rel.RelatedObjects])
|
|
||||||
DumbWallRecalculator().recalculate([w for w in set(walls) if w])
|
DumbWallRecalculator().recalculate([w for w in set(walls) if w])
|
||||||
|
|
||||||
def regenerate_from_type(self, usecase_path, ifc_file, settings):
|
def regenerate_from_type(self, usecase_path, ifc_file, settings):
|
||||||
|
|||||||
Reference in New Issue
Block a user