ifc.unlink fixes after 3df75e9af

Also some changes related to #4843

add
This commit is contained in:
Andrej730
2024-06-14 12:25:40 +05:00
parent d0f612afc2
commit 75f8e75a8e
17 changed files with 64 additions and 84 deletions
+8 -3
View File
@@ -322,9 +322,7 @@ class IfcStore:
raise TypeError("Only one argument must be provided - element or obj.") raise TypeError("Only one argument must be provided - element or obj.")
if obj: if obj:
if isinstance(obj, bpy.types.Material): IfcStore.purge_blender_ifc_data(obj)
obj.BIMMaterialProperties.ifc_style_id = 0
obj.BIMObjectProperties.ifc_definition_id = 0
return return
assert element # Type checker. assert element # Type checker.
@@ -333,6 +331,7 @@ class IfcStore:
potential_obj = IfcStore.id_map[element.id()] potential_obj = IfcStore.id_map[element.id()]
if tool.Blender.is_valid_data_block(potential_obj): if tool.Blender.is_valid_data_block(potential_obj):
obj = potential_obj obj = potential_obj
IfcStore.purge_blender_ifc_data(obj)
except: except:
pass pass
@@ -355,6 +354,12 @@ class IfcStore:
Operation(rollback=IfcStore.rollback_unlink_element, commit=IfcStore.commit_unlink_element, data=data) Operation(rollback=IfcStore.rollback_unlink_element, commit=IfcStore.commit_unlink_element, data=data)
) )
@staticmethod
def purge_blender_ifc_data(obj: IFC_CONNECTED_TYPE) -> None:
if isinstance(obj, bpy.types.Material):
obj.BIMMaterialProperties.ifc_style_id = 0
obj.BIMObjectProperties.ifc_definition_id = 0
@staticmethod @staticmethod
def execute_ifc_operator(operator: bpy.types.Operator, context: bpy.types.Context, is_invoke=False): def execute_ifc_operator(operator: bpy.types.Operator, context: bpy.types.Context, is_invoke=False):
blenderbim.last_actions.append({"type": "operator", "name": operator.bl_idname}) blenderbim.last_actions.append({"type": "operator", "name": operator.bl_idname})
@@ -551,9 +551,9 @@ class HideBoundaries(bpy.types.Operator, tool.Ifc.Operator):
for boundary in element.BoundedBy or []: for boundary in element.BoundedBy or []:
boundary_obj = tool.Ifc.get_object(boundary) boundary_obj = tool.Ifc.get_object(boundary)
if boundary_obj: if boundary_obj:
to_delete.add(boundary_obj) to_delete.add((boundary, boundary_obj))
for boundary_obj in to_delete: for boundary, boundary_obj in to_delete:
tool.Ifc.unlink(obj=boundary_obj) tool.Ifc.unlink(element=boundary)
bpy.data.objects.remove(boundary_obj) bpy.data.objects.remove(boundary_obj)
context.scene.BIMBoundaryProperties.boundaries.clear() context.scene.BIMBoundaryProperties.boundaries.clear()
return {"FINISHED"} return {"FINISHED"}
@@ -182,7 +182,7 @@ class RemoveMaterial(bpy.types.Operator, tool.Ifc.Operator):
material: bpy.props.IntProperty() material: bpy.props.IntProperty()
def _execute(self, context): def _execute(self, context):
res = core.remove_material(tool.Ifc, tool.Material, tool.Style, material=tool.Ifc.get().by_id(self.material)) res = core.remove_material(tool.Ifc, tool.Material, material=tool.Ifc.get().by_id(self.material))
if not res: if not res:
self.report({"ERROR"}, "Material is used in material sets and cannot be removed.") self.report({"ERROR"}, "Material is used in material sets and cannot be removed.")
return {"CANCELLED"} return {"CANCELLED"}
@@ -224,7 +224,7 @@ class FilledOpeningGenerator:
else: else:
opening_obj = tool.Ifc.get_object(opening) opening_obj = tool.Ifc.get_object(opening)
if opening_obj: if opening_obj:
tool.Ifc.unlink(obj=opening_obj) tool.Ifc.unlink(element=opening)
bpy.data.objects.remove(opening_obj) bpy.data.objects.remove(opening_obj)
filling_obj = tool.Ifc.get_object(filling) filling_obj = tool.Ifc.get_object(filling)
@@ -772,9 +772,9 @@ class HideOpenings(Operator, tool.Ifc.Operator):
for opening in openings: for opening in openings:
opening_obj = tool.Ifc.get_object(opening) opening_obj = tool.Ifc.get_object(opening)
if opening_obj: if opening_obj:
to_delete.add(opening_obj) to_delete.add((opening, opening_obj))
for opening_obj in to_delete: for opening, opening_obj in to_delete:
tool.Ifc.unlink(obj=opening_obj) tool.Ifc.unlink(element=opening)
bpy.data.objects.remove(opening_obj) bpy.data.objects.remove(opening_obj)
tool.Model.clear_scene_openings() tool.Model.clear_scene_openings()
return {"FINISHED"} return {"FINISHED"}
@@ -824,7 +824,7 @@ class EditOpenings(Operator, tool.Ifc.Operator):
building_objs.update( building_objs.update(
self.get_all_building_objects_of_similar_openings(opening) self.get_all_building_objects_of_similar_openings(opening)
) # NB this has nothing to do with clone similar_opening ) # NB this has nothing to do with clone similar_opening
tool.Ifc.unlink(element=opening, obj=opening_obj) tool.Ifc.unlink(element=opening)
bpy.data.objects.remove(opening_obj) bpy.data.objects.remove(opening_obj)
tool.Model.reload_body_representation(building_objs) tool.Model.reload_body_representation(building_objs)
@@ -80,7 +80,7 @@ class RemoveStyle(bpy.types.Operator, tool.Ifc.Operator):
style: bpy.props.IntProperty() style: bpy.props.IntProperty()
def _execute(self, context): def _execute(self, context):
core.remove_style(tool.Ifc, tool.Material, tool.Style, style=tool.Ifc.get().by_id(self.style)) core.remove_style(tool.Ifc, tool.Style, style=tool.Ifc.get().by_id(self.style))
class AddStyle(bpy.types.Operator, tool.Ifc.Operator): class AddStyle(bpy.types.Operator, tool.Ifc.Operator):
@@ -74,6 +74,7 @@ class AddOpening(bpy.types.Operator, tool.Ifc.Operator):
obj1, obj2 = obj2, obj1 obj1, obj2 = obj2, obj1
element1, element2 = element2, element1 element1, element2 = element2, element1
# element1 - voided element, element2 - opening.
if element1.is_a("IfcOpeningElement"): if element1.is_a("IfcOpeningElement"):
self.report({"INFO"}, "You can't add an opening to another opening.") self.report({"INFO"}, "You can't add an opening to another opening.")
continue continue
@@ -127,7 +128,7 @@ class AddOpening(bpy.types.Operator, tool.Ifc.Operator):
) )
if not has_visible_openings: if not has_visible_openings:
tool.Ifc.unlink(obj=obj2) tool.Ifc.unlink(element=element2)
bpy.data.objects.remove(obj2) bpy.data.objects.remove(obj2)
context.view_layer.objects.active = obj1 context.view_layer.objects.active = obj1
@@ -148,7 +149,7 @@ class RemoveOpening(bpy.types.Operator, tool.Ifc.Operator):
if opening_obj: if opening_obj:
opening_obj.name = "/".join(opening_obj.name.split("/")[1:]) opening_obj.name = "/".join(opening_obj.name.split("/")[1:])
tool.Ifc.unlink(obj=opening_obj) tool.Ifc.unlink(element=opening)
ifcopenshell.api.run("void.remove_opening", tool.Ifc.get(), opening=opening) ifcopenshell.api.run("void.remove_opening", tool.Ifc.get(), opening=opening)
+6 -7
View File
@@ -58,17 +58,16 @@ def add_material_set(ifc: tool.Ifc, material: tool.Material, set_type: str) -> i
def remove_material( def remove_material(
ifc: tool.Ifc, material_tool: tool.Material, style: tool.Style, material: ifcopenshell.entity_instance ifc: tool.Ifc, material_tool: tool.Material, material: ifcopenshell.entity_instance
) -> bool: ) -> bool:
"""returns True after deleting False,\n """Remove an IFC material.
returns False if material used in material sets and cannot be removed"""
Return True if deletion succeeded,\n
return False if material is used in material sets and cannot be removed.
"""
if material_tool.is_material_used_in_sets(material): if material_tool.is_material_used_in_sets(material):
return False return False
obj = ifc.get_object(material)
ifc.unlink(element=material)
ifc.run("material.remove_material", material=material) ifc.run("material.remove_material", material=material)
if obj and not style.get_style(obj):
material_tool.delete_object(obj)
if material_tool.is_editing_materials(): if material_tool.is_editing_materials():
material_tool.import_material_definitions(material_tool.get_active_material_type()) material_tool.import_material_definitions(material_tool.get_active_material_type())
return True return True
+4 -6
View File
@@ -59,13 +59,12 @@ def update_external_style(
def remove_style( def remove_style(
ifc: tool.Ifc, material: tool.Material, style_tool: tool.Style, style: ifcopenshell.entity_instance ifc: tool.Ifc, style_tool: tool.Style, style: ifcopenshell.entity_instance
) -> None: ) -> None:
obj = ifc.get_object(style) obj = ifc.get_object(style)
ifc.unlink(obj=obj, element=style) ifc.unlink(element=style)
ifc.run("style.remove_style", style=style) ifc.run("style.remove_style", style=style)
if obj and not ifc.get_entity(obj): style_tool.delete_object(obj)
material.delete_object(obj)
if style_tool.is_editing_styles(): if style_tool.is_editing_styles():
style_tool.import_presentation_styles(style_tool.get_active_style_type()) style_tool.import_presentation_styles(style_tool.get_active_style_type())
@@ -130,8 +129,7 @@ def update_style_textures(
def unlink_style(ifc: tool.Ifc, style: ifcopenshell.entity_instance) -> None: def unlink_style(ifc: tool.Ifc, style: ifcopenshell.entity_instance) -> None:
obj = ifc.get_object(style) ifc.unlink(element=style)
ifc.unlink(obj=obj, element=style)
def enable_editing_style(style: tool.Style, obj: bpy.types.Material) -> None: def enable_editing_style(style: tool.Style, obj: bpy.types.Material) -> None:
+1 -1
View File
@@ -39,6 +39,6 @@ def purge_unused_types(ifc, type):
ifc.run("root.remove_product", product=element_type) ifc.run("root.remove_product", product=element_type)
purged_types += 1 purged_types += 1
if obj: if obj:
ifc.unlink(obj=obj) ifc.unlink(element=element_type)
type.remove_object(obj) type.remove_object(obj)
return purged_types return purged_types
+4 -1
View File
@@ -656,9 +656,12 @@ class Geometry(blenderbim.core.tool.Geometry):
def replace_object_with_empty(cls, obj): def replace_object_with_empty(cls, obj):
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
name = obj.name name = obj.name
tool.Ifc.unlink(obj=obj, element=element) if element:
tool.Ifc.unlink(element=element)
obj.name = ifcopenshell.guid.new() obj.name = ifcopenshell.guid.new()
new_obj = bpy.data.objects.new(name, None) new_obj = bpy.data.objects.new(name, None)
if element: if element:
tool.Ifc.link(element, new_obj) tool.Ifc.link(element, new_obj)
for collection in obj.users_collection: for collection in obj.users_collection:
+1
View File
@@ -191,6 +191,7 @@ class Ifc(blenderbim.core.tool.Ifc):
def unlink( def unlink(
cls, element: Optional[ifcopenshell.entity_instance] = None, obj: Optional[IFC_CONNECTED_TYPE] = None cls, element: Optional[ifcopenshell.entity_instance] = None, obj: Optional[IFC_CONNECTED_TYPE] = None
) -> None: ) -> None:
"""See IfcStore.unlink_element doc for details."""
IfcStore.unlink_element(element, obj) IfcStore.unlink_element(element, obj)
@classmethod @classmethod
@@ -38,10 +38,6 @@ class Material(blenderbim.core.tool.Material):
def add_default_material_object(cls, name: Union[str, None]) -> bpy.types.Material: def add_default_material_object(cls, name: Union[str, None]) -> bpy.types.Material:
return bpy.data.materials.new(name or "Default") return bpy.data.materials.new(name or "Default")
@classmethod
def delete_object(cls, obj: bpy.types.Material) -> None:
bpy.data.materials.remove(obj)
@classmethod @classmethod
def disable_editing_materials(cls) -> None: def disable_editing_materials(cls) -> None:
bpy.context.scene.BIMMaterialProperties.is_editing = False bpy.context.scene.BIMMaterialProperties.is_editing = False
+8 -3
View File
@@ -344,12 +344,17 @@ class Root(blenderbim.core.tool.Root):
@classmethod @classmethod
def unlink_object(cls, obj: bpy.types.Object) -> None: def unlink_object(cls, obj: bpy.types.Object) -> None:
"""Purge all IFC data associated with a Blender object.
This method is safe to run on objects from other Blender sessions
as it won't try to find related IFC elements from the IFC data
to unlink them.
"""
tool.Ifc.unlink(obj=obj) tool.Ifc.unlink(obj=obj)
if hasattr(obj.data, "BIMMeshProperties"): if hasattr(obj.data, "BIMMeshProperties"):
obj.data.BIMMeshProperties.ifc_definition_id = 0 obj.data.BIMMeshProperties.ifc_definition_id = 0
for material_slot in obj.material_slots: for material_slot in obj.material_slots:
if material_slot.material: if material := material_slot.material:
blenderbim.core.style.unlink_style(tool.Ifc, style=material_slot.material) tool.Ifc.unlink(obj=material)
blenderbim.core.material.unlink_material(tool.Ifc, obj=material_slot.material)
if "Ifc" in obj.name and "/" in obj.name: if "Ifc" in obj.name and "/" in obj.name:
obj.name = obj.name.split("/", 1)[1] obj.name = obj.name.split("/", 1)[1]
+4
View File
@@ -49,6 +49,10 @@ class Style(blenderbim.core.tool.Style):
def can_support_rendering_style(cls, obj: bpy.types.Material) -> bool: def can_support_rendering_style(cls, obj: bpy.types.Material) -> bool:
return obj.use_nodes and hasattr(obj.node_tree, "nodes") return obj.use_nodes and hasattr(obj.node_tree, "nodes")
@classmethod
def delete_object(cls, obj: bpy.types.Material) -> None:
bpy.data.materials.remove(obj)
@classmethod @classmethod
def disable_editing(cls, obj: bpy.types.Material) -> None: def disable_editing(cls, obj: bpy.types.Material) -> None:
obj.BIMStyleProperties.is_editing = False obj.BIMStyleProperties.is_editing = False
+6 -29
View File
@@ -87,46 +87,23 @@ class TestAddMaterialSet:
class TestRemoveMaterial: class TestRemoveMaterial:
def test_removing_a_material(self, ifc, material, style): def test_removing_a_material(self, ifc, material):
material.is_material_used_in_sets("material").should_be_called().will_return(False) material.is_material_used_in_sets("material").should_be_called().will_return(False)
ifc.get_object("material").should_be_called().will_return(None)
ifc.unlink(element="material").should_be_called()
ifc.run("material.remove_material", material="material").should_be_called() ifc.run("material.remove_material", material="material").should_be_called()
material.is_editing_materials().should_be_called().will_return(False) material.is_editing_materials().should_be_called().will_return(False)
subject.remove_material(ifc, material, style, material="material") subject.remove_material(ifc, material, material="material")
def test_removing_a_material_and_reloading_imported_materials(self, ifc, material, style): def test_removing_a_material_and_reloading_imported_materials(self, ifc, material):
material.is_material_used_in_sets("material").should_be_called().will_return(False) material.is_material_used_in_sets("material").should_be_called().will_return(False)
ifc.get_object("material").should_be_called().will_return(None)
ifc.unlink(element="material").should_be_called()
ifc.run("material.remove_material", material="material").should_be_called() ifc.run("material.remove_material", material="material").should_be_called()
material.is_editing_materials().should_be_called().will_return(True) material.is_editing_materials().should_be_called().will_return(True)
material.get_active_material_type().should_be_called().will_return("material_type") material.get_active_material_type().should_be_called().will_return("material_type")
material.import_material_definitions("material_type").should_be_called() material.import_material_definitions("material_type").should_be_called()
subject.remove_material(ifc, material, style, material="material") subject.remove_material(ifc, material, material="material")
def test_removing_a_material_object_if_it_has_no_style(self, ifc, material, style): def test_not_removing_a_material_if_it_is_used_in_a_material_set(self, ifc, material):
material.is_material_used_in_sets("material").should_be_called().will_return(False)
ifc.get_object("material").should_be_called().will_return("obj")
ifc.unlink(element="material").should_be_called()
ifc.run("material.remove_material", material="material").should_be_called()
style.get_style("obj").should_be_called().will_return(None)
material.delete_object("obj").should_be_called()
material.is_editing_materials().should_be_called().will_return(False)
subject.remove_material(ifc, material, style, material="material")
def test_preserving_a_material_object_if_it_is_still_used_as_a_style(self, ifc, material, style):
material.is_material_used_in_sets("material").should_be_called().will_return(False)
ifc.get_object("material").should_be_called().will_return("obj")
ifc.unlink(element="material").should_be_called()
ifc.run("material.remove_material", material="material").should_be_called()
style.get_style("obj").should_be_called().will_return("style")
material.is_editing_materials().should_be_called().will_return(False)
subject.remove_material(ifc, material, style, material="material")
def test_not_removing_a_material_if_it_is_used_in_a_material_set(self, ifc, material, style):
material.is_material_used_in_sets("material").should_be_called().will_return(True) material.is_material_used_in_sets("material").should_be_called().will_return(True)
subject.remove_material(ifc, material, style, material="material") subject.remove_material(ifc, material, material="material")
class TestRemoveMaterialSet: class TestRemoveMaterialSet:
+8 -17
View File
@@ -53,32 +53,23 @@ class TestAddStyle:
class TestRemoveStyle: class TestRemoveStyle:
def test_removing_a_style(self, ifc, material, style): def test_removing_a_style(self, ifc, style):
ifc.get_object("style").should_be_called().will_return("obj") ifc.get_object("style").should_be_called().will_return("obj")
ifc.unlink(obj="obj", element="style").should_be_called() ifc.unlink(element="style").should_be_called()
ifc.run("style.remove_style", style="style").should_be_called() ifc.run("style.remove_style", style="style").should_be_called()
ifc.get_entity("obj").should_be_called().will_return("material") style.delete_object("obj").should_be_called()
style.is_editing_styles().should_be_called().will_return(False) style.is_editing_styles().should_be_called().will_return(False)
subject.remove_style(ifc, material, style, style="style") subject.remove_style(ifc, style, style="style")
def test_removing_a_style_and_reloading_imported_styles(self, ifc, material, style): def test_removing_a_style_and_reloading_imported_styles(self, ifc, style):
ifc.get_object("style").should_be_called().will_return("obj") ifc.get_object("style").should_be_called().will_return("obj")
ifc.unlink(obj="obj", element="style").should_be_called() ifc.unlink(element="style").should_be_called()
ifc.run("style.remove_style", style="style").should_be_called() ifc.run("style.remove_style", style="style").should_be_called()
ifc.get_entity("obj").should_be_called().will_return("material") style.delete_object("obj").should_be_called()
style.is_editing_styles().should_be_called().will_return(True) style.is_editing_styles().should_be_called().will_return(True)
style.get_active_style_type().should_be_called().will_return("style_type") style.get_active_style_type().should_be_called().will_return("style_type")
style.import_presentation_styles("style_type").should_be_called() style.import_presentation_styles("style_type").should_be_called()
subject.remove_style(ifc, material, style, style="style") subject.remove_style(ifc, style, style="style")
def test_removing_an_object_if_it_is_not_still_used_for_a_material(self, ifc, material, style):
ifc.get_object("style").should_be_called().will_return("obj")
ifc.unlink(obj="obj", element="style").should_be_called()
ifc.run("style.remove_style", style="style").should_be_called()
ifc.get_entity("obj").should_be_called().will_return(None)
material.delete_object("obj").should_be_called()
style.is_editing_styles().should_be_called().will_return(False)
subject.remove_style(ifc, material, style, style="style")
class TestUpdateStyleColours: class TestUpdateStyleColours:
+1 -1
View File
@@ -47,6 +47,6 @@ class TestPurgeUnusedTypes:
type.get_type_occurrences("element_type").should_be_called().will_return([]) type.get_type_occurrences("element_type").should_be_called().will_return([])
ifc.run("root.remove_product", product="element_type").should_be_called() ifc.run("root.remove_product", product="element_type").should_be_called()
ifc.get_object("element_type").should_be_called().will_return("obj") ifc.get_object("element_type").should_be_called().will_return("obj")
ifc.unlink(obj="obj").should_be_called() ifc.unlink(element="element_type").should_be_called()
type.remove_object("obj").should_be_called() type.remove_object("obj").should_be_called()
subject.purge_unused_types(ifc, type) subject.purge_unused_types(ifc, type)