diff --git a/src/blenderbim/blenderbim/bim/module/geometry/__init__.py b/src/blenderbim/blenderbim/bim/module/geometry/__init__.py index 665a8ee6ad..06262b00b0 100644 --- a/src/blenderbim/blenderbim/bim/module/geometry/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/geometry/__init__.py @@ -27,7 +27,8 @@ classes = ( operator.OverrideDelete, operator.OverrideDuplicateMove, operator.OverrideDuplicateMoveLinked, - operator.OverrideModeSet, + operator.OverrideModeSetEdit, + operator.OverrideModeSetObject, operator.OverrideOutlinerDelete, operator.OverridePasteBuffer, operator.RemoveConnection, @@ -60,13 +61,13 @@ def register(): kmi = km.keymap_items.new("bim.override_object_duplicate_move", "D", "PRESS", shift=True) kmi = km.keymap_items.new("bim.override_object_duplicate_move_linked", "D", "PRESS", alt=True) kmi = km.keymap_items.new("bim.override_paste_buffer", "V", "PRESS", ctrl=True) - kmi = km.keymap_items.new("bim.override_mode_set", "TAB", "PRESS") + kmi = km.keymap_items.new("bim.override_mode_set_edit", "TAB", "PRESS") kmi = km.keymap_items.new("bim.override_object_delete", "X", "PRESS") kmi = km.keymap_items.new("bim.override_object_delete", "DEL", "PRESS") kmi.properties.confirm = False km = wm.keyconfigs.addon.keymaps.new(name="Mesh", space_type="EMPTY") - kmi = km.keymap_items.new("bim.override_mode_set", "TAB", "PRESS") + kmi = km.keymap_items.new("bim.override_mode_set_object", "TAB", "PRESS") km = wm.keyconfigs.addon.keymaps.new(name="Outliner", space_type="OUTLINER") kmi = km.keymap_items.new("bim.override_paste_buffer", "V", "PRESS", ctrl=True) diff --git a/src/blenderbim/blenderbim/bim/module/geometry/operator.py b/src/blenderbim/blenderbim/bim/module/geometry/operator.py index 32fdadca2e..3674fe4f62 100644 --- a/src/blenderbim/blenderbim/bim/module/geometry/operator.py +++ b/src/blenderbim/blenderbim/bim/module/geometry/operator.py @@ -18,8 +18,6 @@ import bpy import bmesh -import struct -import hashlib import logging import numpy as np import ifcopenshell @@ -624,11 +622,10 @@ class OverridePasteBuffer(bpy.types.Operator): return {"FINISHED"} -class OverrideModeSet(bpy.types.Operator): - bl_idname = "bim.override_mode_set" - bl_label = "IFC Mode Set" +class OverrideModeSetEdit(bpy.types.Operator): + bl_idname = "bim.override_mode_set_edit" + bl_label = "IFC Mode Set Edit" bl_options = {"REGISTER", "UNDO"} - should_save: bpy.props.BoolProperty(name="Should Save", default=True) def execute(self, context): return IfcStore.execute_ifc_operator(self, context) @@ -646,105 +643,111 @@ class OverrideModeSet(bpy.types.Operator): if not element: continue - if self.active_mode == "EDIT": - # We are switching from EDIT to OBJECT mode. - if obj.data.BIMMeshProperties.ifc_definition_id: - representation = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id) - if representation.RepresentationType in ("Tessellation", "Brep"): - edited_objs.append(obj) - else: - # We are switching from OBJECT to EDIT mode. - usage_type = tool.Model.get_usage_type(element) - if usage_type: - # Parametric objects shall not be edited as meshes as they - # can be modified to be incompatible with the parametric - # constraints. - obj.select_set(False) - continue + # We are switching from OBJECT to EDIT mode. + usage_type = tool.Model.get_usage_type(element) + if usage_type: + # Parametric objects shall not be edited as meshes as they + # can be modified to be incompatible with the parametric + # constraints. + obj.select_set(False) + continue - if obj.data.BIMMeshProperties.ifc_definition_id: - representation = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id) - if representation.RepresentationType in ("Tessellation", "Brep"): - if element.HasOpenings: - # Mesh elements with openings must disable openings - # so that you can edit the original topology. - core.switch_representation( - tool.Ifc, - tool.Geometry, - obj=obj, - representation=representation, - should_reload=True, - is_global=True, - should_sync_changes_first=False, - apply_openings=False, - ) - obj.data.BIMMeshProperties.mesh_checksum = self.get_mesh_checksum(obj.data) + if obj.data.BIMMeshProperties.ifc_definition_id: + representation = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id) + if representation.RepresentationType in ("Tessellation", "Brep"): + if element.HasOpenings: + # Mesh elements with openings must disable openings + # so that you can edit the original topology. + core.switch_representation( + tool.Ifc, + tool.Geometry, + obj=obj, + representation=representation, + should_reload=True, + is_global=True, + should_sync_changes_first=False, + apply_openings=False, + ) + obj.data.BIMMeshProperties.mesh_checksum = tool.Geometry.get_mesh_checksum(obj.data) if not context.selected_objects: return {"FINISHED"} bpy.ops.object.mode_set(mode="EDIT", toggle=True) - for obj in edited_objs: - if self.should_save and obj.data.BIMMeshProperties.mesh_checksum != self.get_mesh_checksum(obj.data): - bpy.ops.bim.update_representation(obj=obj.name, ifc_representation_class="") - if element.HasOpenings: - representation = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id) - # Mesh elements with openings must disable openings - # so that you can edit the original topology. - core.switch_representation( - tool.Ifc, - tool.Geometry, - obj=obj, - representation=representation, - should_reload=True, - is_global=True, - should_sync_changes_first=False, - apply_openings=True, - ) - else: - representation = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id) - core.switch_representation( - tool.Ifc, - tool.Geometry, - obj=obj, - representation=representation, - should_reload=True, - is_global=True, - should_sync_changes_first=False, - apply_openings=True, - ) - return {"FINISHED"} - def draw(self, context): - row = self.layout.row(align=True) - if self.active_mode == "EDIT": - row.prop(self, "should_save") - def invoke(self, context, event): if not tool.Ifc.get(): return bpy.ops.object.mode_set(mode="EDIT", toggle=True) - objs = context.selected_objects or [context.active_object] - obj = objs[0] - self.active_mode = obj.mode - if self.active_mode == "EDIT": - return context.window_manager.invoke_props_dialog(self) return self.execute(context) - def get_mesh_checksum(self, mesh): - # Get mesh data - vertices = mesh.vertices[:] - edges = mesh.edges[:] - faces = mesh.polygons[:] - # Convert mesh data to bytes - data_bytes = b'' - for v in vertices: - data_bytes += struct.pack('3f', *v.co) - for e in edges: - data_bytes += struct.pack('2i', *e.vertices) - for f in faces: - data_bytes += struct.pack('%di' % len(f.vertices), *f.vertices) +class OverrideModeSetObject(bpy.types.Operator): + bl_idname = "bim.override_mode_set_object" + bl_label = "IFC Mode Set Object" + bl_options = {"REGISTER", "UNDO"} + should_save: bpy.props.BoolProperty(name="Should Save", default=True) - # Generate hash of mesh data - hasher = hashlib.sha1() - hasher.update(data_bytes) - return hasher.hexdigest() + def execute(self, context): + return IfcStore.execute_ifc_operator(self, context) + + def _execute(self, context): + for obj in self.edited_objs: + if self.should_save: + bpy.ops.bim.update_representation(obj=obj.name, ifc_representation_class="") + if tool.Ifc.get_entity(obj).HasOpenings: + self.reload_representation(obj) + else: + self.reload_representation(obj) + + for obj in self.unchanged_objs_with_openings: + self.reload_representation(obj) + return {"FINISHED"} + + def reload_representation(self, obj): + representation = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id) + core.switch_representation( + tool.Ifc, + tool.Geometry, + obj=obj, + representation=representation, + should_reload=True, + is_global=True, + should_sync_changes_first=False, + apply_openings=True, + ) + + def draw(self, context): + row = self.layout.row(align=True) + row.prop(self, "should_save") + + def invoke(self, context, event): + bpy.ops.object.mode_set(mode="EDIT", toggle=True) + + if not tool.Ifc.get(): + return + + objs = context.selected_objects or [context.active_object] + + self.edited_objs = [] + self.unchanged_objs_with_openings = [] + + for obj in objs: + if not obj: + continue + + element = tool.Ifc.get_entity(obj) + if not element: + continue + + if obj.data.BIMMeshProperties.ifc_definition_id: + representation = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id) + if representation.RepresentationType in ( + "Tessellation", + "Brep", + ) and obj.data.BIMMeshProperties.mesh_checksum != tool.Geometry.get_mesh_checksum(obj.data): + self.edited_objs.append(obj) + elif element.HasOpenings: + self.unchanged_objs_with_openings.append(obj) + + if self.edited_objs: + return context.window_manager.invoke_props_dialog(self) + return self.execute(context) diff --git a/src/blenderbim/blenderbim/tool/geometry.py b/src/blenderbim/blenderbim/tool/geometry.py index fca0b0dc24..2df24d3fa8 100644 --- a/src/blenderbim/blenderbim/tool/geometry.py +++ b/src/blenderbim/blenderbim/tool/geometry.py @@ -17,6 +17,8 @@ # along with BlenderBIM Add-on. If not, see . import bpy +import struct +import hashlib import logging import numpy as np import ifcopenshell @@ -120,6 +122,27 @@ class Geometry(blenderbim.core.tool.Geometry): return "IfcExtrudedAreaSolid/IfcCircleProfileDef" return "IfcExtrudedAreaSolid/IfcArbitraryProfileDefWithVoids" + @classmethod + def get_mesh_checksum(cls, mesh): + # Get mesh data + vertices = mesh.vertices[:] + edges = mesh.edges[:] + faces = mesh.polygons[:] + + # Convert mesh data to bytes + data_bytes = b'' + for v in vertices: + data_bytes += struct.pack('3f', *v.co) + for e in edges: + data_bytes += struct.pack('2i', *e.vertices) + for f in faces: + data_bytes += struct.pack('%di' % len(f.vertices), *f.vertices) + + # Generate hash of mesh data + hasher = hashlib.sha1() + hasher.update(data_bytes) + return hasher.hexdigest() + @classmethod def get_object_data(cls, obj): return obj.data