From c551275db2f0b61914ac7fa99a1698e4d6de1f11 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Fri, 22 Oct 2021 16:23:36 +1100 Subject: [PATCH] Fix bug where switching to an object with modifiers applied would double the effect of modifiers. --- .../bim/module/geometry/operator.py | 2 +- .../blenderbim/bim/module/model/handler.py | 9 --------- .../blenderbim/bim/module/model/profile.py | 11 ---------- .../blenderbim/bim/module/model/slab.py | 8 -------- .../blenderbim/bim/module/model/wall.py | 8 -------- src/blenderbim/blenderbim/core/geometry.py | 3 +-- src/blenderbim/blenderbim/core/tool.py | 2 +- src/blenderbim/blenderbim/tool/geometry.py | 5 ++--- .../test/bim/feature/geometry.feature | 20 +++++++++++++++++++ src/blenderbim/test/bim/test_feature.py | 10 ++++++++++ src/blenderbim/test/core/test_geometry.py | 4 +++- src/blenderbim/test/tool/test_geometry.py | 4 ++-- 12 files changed, 40 insertions(+), 46 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/geometry/operator.py b/src/blenderbim/blenderbim/bim/module/geometry/operator.py index 0845274658..b1a2b4e430 100644 --- a/src/blenderbim/blenderbim/bim/module/geometry/operator.py +++ b/src/blenderbim/blenderbim/bim/module/geometry/operator.py @@ -251,7 +251,7 @@ class UpdateRepresentation(bpy.types.Operator): def auto_detect_ifc_representation_class(self, element, representation, data): material = ifcopenshell.util.element.get_material(element) - if material.is_a("IfcMaterialProfileSetUsage"): + if material and material.is_a("IfcMaterialProfileSetUsage"): data["ifc_representation_class"] = "IfcExtrudedAreaSolid/IfcMaterialProfileSetUsage" data["profile_set_usage"] = material return diff --git a/src/blenderbim/blenderbim/bim/module/model/handler.py b/src/blenderbim/blenderbim/bim/module/model/handler.py index 7e2fcc59ca..894a3873cb 100644 --- a/src/blenderbim/blenderbim/bim/module/model/handler.py +++ b/src/blenderbim/blenderbim/bim/module/model/handler.py @@ -60,9 +60,6 @@ def load_post(*args): IfcStore.add_element_listener(slab.element_listener) IfcStore.add_element_listener(profile.element_listener) - ifcopenshell.api.add_pre_listener( - "geometry.add_representation", "BlenderBIM.DumbWall.EnsureSolid", wall.ensure_solid - ) ifcopenshell.api.add_post_listener( "geometry.add_representation", "BlenderBIM.DumbWall.GenerateAxis", wall.generate_axis ) @@ -76,9 +73,6 @@ def load_post(*args): "type.assign_type", "BlenderBIM.DumbWall.RegenerateFromType", wall.DumbWallPlaner().regenerate_from_type ) - ifcopenshell.api.add_pre_listener( - "geometry.add_representation", "BlenderBIM.DumbSlab.EnsureSolid", slab.ensure_solid - ) ifcopenshell.api.add_post_listener( "geometry.add_representation", "BlenderBIM.DumbSlab.GenerateFootprint", slab.generate_footprint ) @@ -92,9 +86,6 @@ def load_post(*args): "type.assign_type", "BlenderBIM.DumbSlab.RegenerateFromType", slab.DumbSlabPlaner().regenerate_from_type ) - ifcopenshell.api.add_pre_listener( - "geometry.add_representation", "BlenderBIM.DumbProfile.EnsureSolid", profile.ensure_solid - ) ifcopenshell.api.add_pre_listener( "material.edit_profile", "BlenderBIM.DumbProfile.SyncObjectFromProfile", diff --git a/src/blenderbim/blenderbim/bim/module/model/profile.py b/src/blenderbim/blenderbim/bim/module/model/profile.py index d97ddd5023..9f3c9f74ff 100644 --- a/src/blenderbim/blenderbim/bim/module/model/profile.py +++ b/src/blenderbim/blenderbim/bim/module/model/profile.py @@ -92,17 +92,6 @@ def mode_callback(obj, data): obj.matrix_world.translation = new_origin -def ensure_solid(usecase_path, ifc_file, settings): - product = ifc_file.by_id(settings["blender_object"].BIMObjectProperties.ifc_definition_id) - parametric = ifcopenshell.util.element.get_psets(product).get("EPset_Parametric") - if not parametric or parametric["Engine"] != "BlenderBIM.DumbProfile": - return - material = ifcopenshell.util.element.get_material(product) - if material and material.is_a("IfcMaterialProfileSetUsage"): - settings["profile_set_usage"] = material - settings["ifc_representation_class"] = "IfcExtrudedAreaSolid/IfcMaterialProfileSetUsage" - - class DumbProfileGenerator: def __init__(self, relating_type): self.relating_type = relating_type diff --git a/src/blenderbim/blenderbim/bim/module/model/slab.py b/src/blenderbim/blenderbim/bim/module/model/slab.py index 0436572309..ec9978975b 100644 --- a/src/blenderbim/blenderbim/bim/module/model/slab.py +++ b/src/blenderbim/blenderbim/bim/module/model/slab.py @@ -100,14 +100,6 @@ def ensure_solidify_modifier(obj): return modifier -def ensure_solid(usecase_path, ifc_file, settings): - product = ifc_file.by_id(settings["blender_object"].BIMObjectProperties.ifc_definition_id) - parametric = ifcopenshell.util.element.get_psets(product).get("EPset_Parametric") - if not parametric or parametric["Engine"] != "BlenderBIM.DumbLayer3": - return - settings["ifc_representation_class"] = "IfcExtrudedAreaSolid/IfcArbitraryProfileDefWithVoids" - - def generate_footprint(usecase_path, ifc_file, settings): footprint_context = ifcopenshell.util.representation.get_context(ifc_file, "Plan", "FootPrint", "SKETCH_VIEW") if not footprint_context: diff --git a/src/blenderbim/blenderbim/bim/module/model/wall.py b/src/blenderbim/blenderbim/bim/module/model/wall.py index d47d4e80fa..d3a90cd9b6 100644 --- a/src/blenderbim/blenderbim/bim/module/model/wall.py +++ b/src/blenderbim/blenderbim/bim/module/model/wall.py @@ -816,14 +816,6 @@ class DumbWallGenerator: return obj -def ensure_solid(usecase_path, ifc_file, settings): - product = ifc_file.by_id(settings["blender_object"].BIMObjectProperties.ifc_definition_id) - parametric = ifcopenshell.util.element.get_psets(product).get("EPset_Parametric") - if not parametric or parametric["Engine"] != "BlenderBIM.DumbLayer2": - return - settings["ifc_representation_class"] = "IfcExtrudedAreaSolid/IfcArbitraryClosedProfileDef" - - def generate_axis(usecase_path, ifc_file, settings): axis_context = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Axis", "GRAPH_VIEW") if not axis_context: diff --git a/src/blenderbim/blenderbim/core/geometry.py b/src/blenderbim/blenderbim/core/geometry.py index 5414041ffe..b1fe3977f9 100644 --- a/src/blenderbim/blenderbim/core/geometry.py +++ b/src/blenderbim/blenderbim/core/geometry.py @@ -85,8 +85,7 @@ def switch_representation( geometry.link(representation, data) geometry.change_object_data(obj, data, is_global=is_global) + geometry.clear_modifiers(obj) if enable_dynamic_voids and geometry.is_body_representation(representation): geometry.create_dynamic_voids(obj) - else: - geometry.clear_dynamic_voids(obj) diff --git a/src/blenderbim/blenderbim/core/tool.py b/src/blenderbim/blenderbim/core/tool.py index 2cb740e11c..7e5a2742f3 100644 --- a/src/blenderbim/blenderbim/core/tool.py +++ b/src/blenderbim/blenderbim/core/tool.py @@ -58,7 +58,7 @@ class Context: @interface class Geometry: def change_object_data(cls, obj, data, is_global=False): pass - def clear_dynamic_voids(cls, obj): pass + def clear_modifiers(cls, obj): pass def create_dynamic_voids(cls, obj): pass def does_object_have_mesh_with_faces(cls, obj): pass def duplicate_object_data(cls, obj): pass diff --git a/src/blenderbim/blenderbim/tool/geometry.py b/src/blenderbim/blenderbim/tool/geometry.py index 5ab4434d08..80e352fe79 100644 --- a/src/blenderbim/blenderbim/tool/geometry.py +++ b/src/blenderbim/blenderbim/tool/geometry.py @@ -35,10 +35,9 @@ class Geometry(blenderbim.core.tool.Geometry): obj.data = data @classmethod - def clear_dynamic_voids(cls, obj): + def clear_modifiers(cls, obj): for modifier in obj.modifiers: - if modifier.type == "BOOLEAN" and "IfcOpeningElement" in modifier.name: - obj.modifiers.remove(modifier) + obj.modifiers.remove(modifier) @classmethod def create_dynamic_voids(cls, obj): diff --git a/src/blenderbim/test/bim/feature/geometry.feature b/src/blenderbim/test/bim/feature/geometry.feature index d76a5cb5d8..b8c7cccedc 100644 --- a/src/blenderbim/test/bim/feature/geometry.feature +++ b/src/blenderbim/test/bim/feature/geometry.feature @@ -33,6 +33,26 @@ Scenario: Switch representation And I press "bim.switch_representation(obj='IfcWall/Cube', ifc_definition_id={representation})" Then nothing happens +Scenario: Switch representation - existing Blender modifiers must be purged + Given an empty IFC project + And I add a cube + And the object "Cube" is selected + And I add an array modifier + And I set "scene.BIMRootProperties.ifc_class" to "IfcWall" + And I press "bim.assign_class" + When the variable "representation" is "{ifc}.by_type('IfcShapeRepresentation')[0].id()" + And I press "bim.switch_representation(obj='IfcWall/Cube', ifc_definition_id={representation})" + Then the object "IfcWall/Cube" has no modifiers + +Scenario: Update representation + Given an empty IFC project + And I add a cube + And the object "Cube" is selected + And I set "scene.BIMRootProperties.ifc_class" to "IfcWall" + And I press "bim.assign_class" + And I press "bim.update_representation(obj='IfcWall/Cube')" + Then the object "IfcWall/Cube" has a "Tessellation" representation of "Model/Body/MODEL_VIEW" + Scenario: Copy representation Given an empty IFC project And I add a cube diff --git a/src/blenderbim/test/bim/test_feature.py b/src/blenderbim/test/bim/test_feature.py index d70dd54651..74656dbd25 100644 --- a/src/blenderbim/test/bim/test_feature.py +++ b/src/blenderbim/test/bim/test_feature.py @@ -94,6 +94,11 @@ def i_add_a_material(): bpy.context.active_object.active_material = bpy.data.materials.new("Material") +@given("I add an array modifier") +def i_add_a_cube(): + bpy.ops.object.modifier_add(type="ARRAY") + + @when(parsers.parse('I add a cube of size "{size}" at "{location}"')) def i_add_a_cube_of_size_size_at_location(size, location): bpy.ops.mesh.primitive_cube_add(size=float(size), location=[float(co) for co in location.split(",")]) @@ -424,3 +429,8 @@ def the_file_name_should_contain_value(name, value): name = replace_variables(name) with open(name, "r") as f: assert value in f.read() + + +@then(parsers.parse('the object "{name}" has no modifiers')) +def the_object_name_has_no_modifiers(name): + assert len(the_object_name_exists(name).modifiers) == 0 diff --git a/src/blenderbim/test/core/test_geometry.py b/src/blenderbim/test/core/test_geometry.py index 01652a46b3..4246264e4e 100644 --- a/src/blenderbim/test/core/test_geometry.py +++ b/src/blenderbim/test/core/test_geometry.py @@ -189,6 +189,7 @@ class TestSwitchRepresentation: geometry.rename_object("new_data", "name").should_be_called() geometry.link("representation", "new_data").should_be_called() geometry.change_object_data("obj", "new_data", is_global=True).should_be_called() + geometry.clear_modifiers("obj").should_be_called() geometry.is_body_representation("representation").should_be_called().will_return(True) geometry.create_dynamic_voids("obj").should_be_called() subject.switch_representation( @@ -204,6 +205,7 @@ class TestSwitchRepresentation: geometry.resolve_mapped_representation("mapped_rep").should_be_called().will_return("representation") geometry.get_representation_data("representation").should_be_called().will_return("data") geometry.change_object_data("obj", "data", is_global=True).should_be_called() + geometry.clear_modifiers("obj").should_be_called() geometry.is_body_representation("representation").should_be_called().will_return(True) geometry.create_dynamic_voids("obj").should_be_called() subject.switch_representation( @@ -219,7 +221,7 @@ class TestSwitchRepresentation: geometry.resolve_mapped_representation("mapped_rep").should_be_called().will_return("representation") geometry.get_representation_data("representation").should_be_called().will_return("data") geometry.change_object_data("obj", "data", is_global=False).should_be_called() - geometry.clear_dynamic_voids("obj").should_be_called() + geometry.clear_modifiers("obj").should_be_called() subject.switch_representation( geometry, obj="obj", diff --git a/src/blenderbim/test/tool/test_geometry.py b/src/blenderbim/test/tool/test_geometry.py index 5cdef17800..c323196f66 100644 --- a/src/blenderbim/test/tool/test_geometry.py +++ b/src/blenderbim/test/tool/test_geometry.py @@ -53,11 +53,11 @@ class TestChangeObjectData(test.bim.bootstrap.NewFile): assert obj2.data == data2 -class TestClearDynamicVoids(test.bim.bootstrap.NewFile): +class TestClearModifiers(test.bim.bootstrap.NewFile): def test_run(self): obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh")) obj.modifiers.new("IfcOpeningElement", "BOOLEAN") - subject.clear_dynamic_voids(obj) + subject.clear_modifiers(obj) assert len(obj.modifiers) == 0