diff --git a/src/blenderbim/blenderbim/bim/module/drawing/operator.py b/src/blenderbim/blenderbim/bim/module/drawing/operator.py index 7d04b219d0..6ef13d829b 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/operator.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/operator.py @@ -396,7 +396,7 @@ class CreateDrawing(bpy.types.Operator): def get_classes(self, element, position, svg_writer): classes = [position, element.is_a()] - material = ifcopenshell.util.element.get_material(element) + material = ifcopenshell.util.element.get_material(element, should_skip_usage=True) if material: classes.append("material-{}".format(re.sub("[^0-9a-zA-Z]+", "", self.get_material_name(material)))) classes.append("globalid-{}".format(element.GlobalId)) diff --git a/src/blenderbim/blenderbim/bim/module/geometry/operator.py b/src/blenderbim/blenderbim/bim/module/geometry/operator.py index eacc3f7676..e43860125b 100644 --- a/src/blenderbim/blenderbim/bim/module/geometry/operator.py +++ b/src/blenderbim/blenderbim/bim/module/geometry/operator.py @@ -58,7 +58,7 @@ class AddRepresentation(bpy.types.Operator): obj: bpy.props.StringProperty() context_id: bpy.props.IntProperty() ifc_representation_class: bpy.props.StringProperty() - profile_set: bpy.props.IntProperty() + profile_set_usage: bpy.props.IntProperty() def execute(self, context): obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object @@ -94,7 +94,7 @@ class AddRepresentation(bpy.types.Operator): "should_force_faceted_brep": context.scene.BIMGeometryProperties.should_force_faceted_brep, "should_force_triangulation": context.scene.BIMGeometryProperties.should_force_triangulation, "ifc_representation_class": self.ifc_representation_class, - "profile_set": self.file.by_id(self.profile_set) if self.profile_set else None + "profile_set_usage": self.file.by_id(self.profile_set_usage) if self.profile_set_usage else None } result = ifcopenshell.api.run("geometry.add_representation", self.file, **representation_data) diff --git a/src/blenderbim/blenderbim/bim/module/model/column.py b/src/blenderbim/blenderbim/bim/module/model/column.py index 954375329c..42db1491a4 100644 --- a/src/blenderbim/blenderbim/bim/module/model/column.py +++ b/src/blenderbim/blenderbim/bim/module/model/column.py @@ -46,11 +46,11 @@ def ensure_solid(usecase_path, ifc_file, settings): if not parametric or parametric["Engine"] != "BlenderBIM.DumbColumn": return material = ifcopenshell.util.element.get_material(product) - if material and material.is_a("IfcMaterialProfileSet"): - settings["profile_set"] = material + if material and material.is_a("IfcMaterialProfileSetUsage"): + settings["profile_set_usage"] = material else: return - settings["ifc_representation_class"] = "IfcExtrudedAreaSolid/IfcMaterialProfileSet" + settings["ifc_representation_class"] = "IfcExtrudedAreaSolid/IfcMaterialProfileSetUsage" class DumbColumnGenerator: @@ -110,20 +110,51 @@ class DumbColumnGenerator: bpy.ops.bim.assign_class( obj=obj.name, ifc_class="IfcColumn", predefined_type="COLUMN", should_add_representation=False ) + element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id) bpy.ops.bim.assign_type(relating_type=self.relating_type.id(), related_object=obj.name) + profile_set_usage = ifcopenshell.util.element.get_material(element) bpy.ops.bim.add_representation( obj=obj.name, context_id=ifcopenshell.util.representation.get_context(self.file, "Model", "Body", "MODEL_VIEW").id(), - ifc_representation_class="IfcExtrudedAreaSolid/IfcMaterialProfileSet", - profile_set=self.profile_set.id(), - ) - representation = ifcopenshell.util.representation.get_representation( - self.file.by_id(obj.BIMObjectProperties.ifc_definition_id), "Model", "Body", "MODEL_VIEW" + ifc_representation_class="IfcExtrudedAreaSolid/IfcMaterialProfileSetUsage", + profile_set_usage=profile_set_usage.id(), ) + representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW") bpy.ops.bim.switch_representation(obj=obj.name, ifc_definition_id=representation.id(), should_reload=True) - element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id) pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="EPset_Parametric") ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"Engine": "BlenderBIM.DumbColumn"}) MaterialData.load(self.file) obj.select_set(True) return obj + + +class DumbColumnRegenerator: + def regenerate_from_profile(self, usecase_path, ifc_file, settings): + self.file = IfcStore.get_file() + self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(ifc_file) + profile = settings["profile"].Profile + if not profile: + return + for profile_set in [ + mp.ToMaterialProfileSet[0] for mp in self.file.get_inverse(profile) if mp.is_a("IfcMaterialProfile") + ]: + for inverse in ifc_file.get_inverse(profile_set): + if not inverse.is_a("IfcMaterialProfileSetUsage"): + 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.change_profile(element) + else: + for rel in inverse.AssociatedTo: + for element in rel.RelatedObjects: + self.change_profile(element) + + def change_profile(self, element): + obj = IfcStore.get_element(element.id()) + if not obj: + return + representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW") + bpy.ops.bim.switch_representation(obj=obj.name, ifc_definition_id=representation.id(), should_reload=True) diff --git a/src/blenderbim/blenderbim/bim/module/model/handler.py b/src/blenderbim/blenderbim/bim/module/model/handler.py index d86fdbef3f..e398e14a21 100644 --- a/src/blenderbim/blenderbim/bim/module/model/handler.py +++ b/src/blenderbim/blenderbim/bim/module/model/handler.py @@ -50,3 +50,8 @@ def load_post(*args): ifcopenshell.api.add_pre_listener( "geometry.add_representation", "BlenderBIM.DumbColumn.EnsureSolid", column.ensure_solid ) + ifcopenshell.api.add_post_listener( + "material.edit_profile", + "BlenderBIM.DumbColumn.RegenerateFromProfile", + column.DumbColumnRegenerator().regenerate_from_profile, + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py index 162d415214..53be859417 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py @@ -30,9 +30,9 @@ class Usecase: # IfcExtrudedAreaSolid/IfcCircleProfileDef # IfcExtrudedAreaSolid/IfcArbitraryClosedProfileDef # IfcExtrudedAreaSolid/IfcArbitraryProfileDefWithVoids - # IfcExtrudedAreaSolid/IfcMaterialProfileSet + # IfcExtrudedAreaSolid/IfcMaterialProfileSetUsage "ifc_representation_class": None, # Whether to cast a mesh into a particular class - "profile_set": None, # The material profile set if the extrusion requires it + "profile_set_usage": None, # The material profile set if the extrusion requires it } self.ifc_vertices = [] for key, value in settings.items(): @@ -220,7 +220,7 @@ class Usecase: return self.create_arbitrary_extrusion_representation() elif self.settings["ifc_representation_class"] == "IfcExtrudedAreaSolid/IfcArbitraryProfileDefWithVoids": return self.create_arbitrary_void_extrusion_representation() - elif self.settings["ifc_representation_class"] == "IfcExtrudedAreaSolid/IfcMaterialProfileSet": + elif self.settings["ifc_representation_class"] == "IfcExtrudedAreaSolid/IfcMaterialProfileSetUsage": return self.create_material_profile_set_extrusion_representation() return self.create_mesh_representation() @@ -410,10 +410,8 @@ class Usecase: ) def create_material_profile_set_extrusion_representation(self): - profile_def = ( - self.settings["profile_set"].CompositeProfile - or self.settings["profile_set"].MaterialProfiles[0].Profile - ) + profile_set = self.settings["profile_set_usage"].ForProfileSet + profile_def = profile_set.CompositeProfile or profile_set.MaterialProfiles[0].Profile position = None if self.file.schema == "IFC2X3": position = self.file.createIfcAxis2Placement3D( diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/assign_profile.py b/src/ifcopenshell-python/ifcopenshell/api/material/assign_profile.py index 43580552c4..99ad9ec786 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/assign_profile.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/assign_profile.py @@ -1,3 +1,6 @@ +import ifcopenshell.util.representation + + class Usecase: def __init__(self, file, **settings): self.file = file @@ -6,9 +9,28 @@ class Usecase: self.settings[key] = value def execute(self): - if ( - self.settings["material_profile"].Profile - and len(self.file.get_inverse(self.settings["material_profile"].Profile)) == 1 - ): - self.file.remove(self.settings["material_profile"].Profile) + old_profile = self.settings["material_profile"].Profile self.settings["material_profile"].Profile = self.settings["profile"] + for profile_set in self.settings["material_profile"].ToMaterialProfileSet: + for inverse in self.file.get_inverse(profile_set): + if not inverse.is_a("IfcMaterialProfileSetUsage"): + continue + if self.file.schema == "IFC2X3": + for rel in self.file.get_inverse(inverse): + if not rel.is_a("IfcRelAssociatesMaterial"): + continue + for element in rel.RelatedObjects: + self.change_profile(element) + else: + for rel in inverse.AssociatedTo: + for element in rel.RelatedObjects: + self.change_profile(element) + + if old_profile and len(self.file.get_inverse(old_profile)) == 0: + self.file.remove(old_profile) + + def change_profile(self, element): + representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW") + for subelement in self.file.traverse(representation): + if subelement.is_a("IfcSweptAreaSolid"): + subelement.SweptArea = self.settings["profile"] diff --git a/src/ifcopenshell-python/ifcopenshell/util/element.py b/src/ifcopenshell-python/ifcopenshell/util/element.py index 86af191a42..53e2bdba23 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/element.py +++ b/src/ifcopenshell-python/ifcopenshell/util/element.py @@ -62,14 +62,15 @@ def get_type(element): return relationship.RelatingType -def get_material(element): +def get_material(element, should_skip_usage=False): if hasattr(element, "HasAssociations") and element.HasAssociations: for relationship in element.HasAssociations: if relationship.is_a("IfcRelAssociatesMaterial"): - if relationship.RelatingMaterial.is_a("IfcMaterialLayerSetUsage"): - return relationship.RelatingMaterial.ForLayerSet - elif relationship.RelatingMaterial.is_a("IfcMaterialProfileSetUsage"): - return relationship.RelatingMaterial.ForProfileSet + if should_skip_usage: + if relationship.RelatingMaterial.is_a("IfcMaterialLayerSetUsage"): + return relationship.RelatingMaterial.ForLayerSet + elif relationship.RelatingMaterial.is_a("IfcMaterialProfileSetUsage"): + return relationship.RelatingMaterial.ForProfileSet return relationship.RelatingMaterial relating_type = get_type(element) if hasattr(relating_type, "HasAssociations") and relating_type.HasAssociations: diff --git a/src/ifcopenshell-python/ifcopenshell/util/selector.py b/src/ifcopenshell-python/ifcopenshell/util/selector.py index 5317dec333..d56d69ce83 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/selector.py +++ b/src/ifcopenshell-python/ifcopenshell/util/selector.py @@ -221,7 +221,7 @@ class Selector: key = ".".join(key.split(".")[1:]) elif "." in key and key.split(".")[0] == "material": try: - element = ifcopenshell.util.element.get_material(element) + element = ifcopenshell.util.element.get_material(element, should_skip_usage=True) if not element: return None except: