From 8a9ca9ac600e5d270c3344c29f16542ec37ecec2 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Fri, 5 Jul 2024 17:36:07 +0500 Subject: [PATCH] remove bbim material unassignment listener #4843 Listeners are implicit and get in the way sometimes, we should work on our core methods and make explicit calls. --- .../bim/module/geometry/operator.py | 2 ++ .../blenderbim/bim/module/model/handler.py | 4 --- .../blenderbim/bim/module/model/product.py | 20 ------------- src/blenderbim/blenderbim/core/material.py | 30 +++++++++++-------- src/blenderbim/blenderbim/tool/material.py | 15 ++++++++++ src/blenderbim/test/tool/test_model.py | 5 ++++ 6 files changed, 40 insertions(+), 36 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/geometry/operator.py b/src/blenderbim/blenderbim/bim/module/geometry/operator.py index 4390cdf99a..e2344dbe57 100644 --- a/src/blenderbim/blenderbim/bim/module/geometry/operator.py +++ b/src/blenderbim/blenderbim/bim/module/geometry/operator.py @@ -406,7 +406,9 @@ class UpdateRepresentation(bpy.types.Operator, Operator): element_type = ifcopenshell.util.element.get_type(product) if element_type: # Some invalid IFCs use material sets without a type. ifcopenshell.api.run("material.unassign_material", tool.Ifc.get(), products=[element_type]) + tool.Material.ensure_material_unassigned([element_type]) ifcopenshell.api.run("material.unassign_material", tool.Ifc.get(), products=[product]) + tool.Material.ensure_material_unassigned([product]) else: # These objects are parametrically based on an axis and should not be modified as a mesh return diff --git a/src/blenderbim/blenderbim/bim/module/model/handler.py b/src/blenderbim/blenderbim/bim/module/model/handler.py index b694117c7c..181eeb7146 100644 --- a/src/blenderbim/blenderbim/bim/module/model/handler.py +++ b/src/blenderbim/blenderbim/bim/module/model/handler.py @@ -50,10 +50,6 @@ def load_post(*args): usecase, "BlenderBIM.Product.EnsureMaterialAssigned", product.ensure_material_assigned ) - ifcopenshell.api.add_post_listener( - "material.unassign_material", "BlenderBIM.Product.EnsureMaterialUnassigned", product.ensure_material_unassigned - ) - ifcopenshell.api.add_post_listener( "material.edit_profile_usage", "BlenderBIM.Product.RegenerateProfileUsage", diff --git a/src/blenderbim/blenderbim/bim/module/model/product.py b/src/blenderbim/blenderbim/bim/module/model/product.py index a371cbadcd..86e82a4582 100644 --- a/src/blenderbim/blenderbim/bim/module/model/product.py +++ b/src/blenderbim/blenderbim/bim/module/model/product.py @@ -542,23 +542,3 @@ def ensure_material_assigned(usecase_path: str, ifc_file: ifcopenshell.file, set elements.extend(tool.Model.get_occurrences_without_material_override(element)) tool.Model.apply_ifc_material_changes(elements, assigned_material=settings["material"]) - - -def ensure_material_unassigned(usecase_path: str, ifc_file: ifcopenshell.file, settings: dict[str, Any]) -> None: - return # TODO ensure this now works with the new approach of styles - elements = settings["products"] - - # unassign_material could be called when product is about to get removed - # and representation might be already removed. - elements = [ - e - for e in elements - if (obj := tool.Ifc.get_object(e)) - and obj.data - and tool.Ifc.get_entity_by_id(obj.data.BIMMeshProperties.ifc_definition_id) - ] - - for element in elements[:]: - if element.is_a("IfcElementType"): - elements.extend(tool.Model.get_occurrences_without_material_override(element)) - tool.Model.apply_ifc_material_changes(elements) diff --git a/src/blenderbim/blenderbim/core/material.py b/src/blenderbim/blenderbim/core/material.py index 3389658129..119dfe77fd 100644 --- a/src/blenderbim/blenderbim/core/material.py +++ b/src/blenderbim/blenderbim/core/material.py @@ -116,21 +116,27 @@ def assign_material( def unassign_material(ifc: tool.Ifc, material_tool: tool.Material, objects: list[bpy.types.Object]) -> None: for obj in objects: element = ifc.get_entity(obj) - if element: - material = material_tool.get_material(element, should_inherit=False) - inherited_material = material_tool.get_material(element, should_inherit=True) - if material: - if "Usage" in material.is_a(): - element_type = material_tool.get_type(element) - ifc.run("material.unassign_material", products=[element_type]) - else: - ifc.run("material.unassign_material", products=[element]) - elif inherited_material: + if not element: + continue + material = material_tool.get_material(element, should_inherit=False) + inherited_material = material_tool.get_material(element, should_inherit=True) + if material: + if "Usage" in material.is_a(): element_type = material_tool.get_type(element) + assert element_type # Type checker. ifc.run("material.unassign_material", products=[element_type]) + material_tool.ensure_material_unassigned(elements=[element_type]) else: - # Has no material and has no inherited material, nothing to unassign. - pass + ifc.run("material.unassign_material", products=[element]) + material_tool.ensure_material_unassigned(elements=[element]) + elif inherited_material: + element_type = material_tool.get_type(element) + assert element_type # Type checker. + ifc.run("material.unassign_material", products=[element_type]) + material_tool.ensure_material_unassigned(elements=[element_type]) + else: + # Has no material and has no inherited material, nothing to unassign. + pass def patch_non_parametric_mep_segment( diff --git a/src/blenderbim/blenderbim/tool/material.py b/src/blenderbim/blenderbim/tool/material.py index f62fbae248..9a0864a252 100644 --- a/src/blenderbim/blenderbim/tool/material.py +++ b/src/blenderbim/blenderbim/tool/material.py @@ -284,3 +284,18 @@ class Material(blenderbim.core.tool.Material): for style in item.Styles: if style.is_a("IfcSurfaceStyle"): return style + + @classmethod + def ensure_material_unassigned(cls, elements: list[ifcopenshell.entity_instance]) -> None: + """Ensure blender materials are updated after a material unassignment. + + E.g. during a material unassignment some material style may not apply anymore + or some other may be applied now since it's no longer overridden, + therefore we need to make sure blender materials reflect correct styles. + + Designed to be called after material.unassign_material API call.""" + elements = elements.copy() # Avoid argument mutation. + for element in elements[:]: + if element.is_a("IfcElementType"): + elements.extend(tool.Model.get_occurrences_without_material_override(element)) + tool.Model.apply_ifc_material_changes(elements) diff --git a/src/blenderbim/test/tool/test_model.py b/src/blenderbim/test/tool/test_model.py index 430d405c1d..d4cdc7ef72 100644 --- a/src/blenderbim/test/tool/test_model.py +++ b/src/blenderbim/test/tool/test_model.py @@ -546,6 +546,7 @@ class TestApplyIfcMaterialChanges(NewFile): assert self.get_used_styles(obj) == expected ifcopenshell.api.material.unassign_material(ifc_file, products=[element_type]) + tool.Material.ensure_material_unassigned([element_type]) assert self.get_used_styles(tool.Ifc.get_object(element_type)) == set() for element in ifc_file.by_type("IfcActuator"): obj = tool.Ifc.get_object(element) @@ -573,6 +574,7 @@ class TestApplyIfcMaterialChanges(NewFile): assert self.get_used_styles(obj) == {green_style} ifcopenshell.api.material.unassign_material(ifc_file, products=[element_type]) + tool.Material.ensure_material_unassigned([element_type]) assert self.get_used_styles(tool.Ifc.get_object(element_type)) == {green_style} for element in ifc_file.by_type("IfcActuator"): obj = tool.Ifc.get_object(element) @@ -613,6 +615,7 @@ class TestApplyIfcMaterialChanges(NewFile): ifcopenshell.api.material.assign_material(ifc_file, products=[element], material=red_material) assert self.get_used_styles(obj) == {green_style, red_style} ifcopenshell.api.material.unassign_material(ifc_file, products=[element]) + tool.Material.ensure_material_unassigned([element]) mesh = self.get_mesh(obj) assert len(mesh.materials) == 2 assert set(mesh.materials) == {bpy.data.materials["Green"], None} @@ -630,6 +633,7 @@ class TestApplyIfcMaterialChanges(NewFile): assert set(get_material_indices(mesh)) == {mesh.materials.find("Red")} ifcopenshell.api.material.unassign_material(ifc_file, products=[element]) + tool.Material.ensure_material_unassigned([element]) mesh = self.get_mesh(obj) assert len(mesh.materials) == 2 assert set(mesh.materials) == {bpy.data.materials["Red"], None} @@ -651,4 +655,5 @@ class TestApplyIfcMaterialChanges(NewFile): assert self.get_mesh(obj).materials[:] == [] ifcopenshell.api.material.unassign_material(ifc_file, products=[element]) + tool.Material.ensure_material_unassigned([element]) assert self.get_mesh(obj).materials[:] == [bpy.data.materials["Red"]]