See #2887. Save mesh dialog now only shown when mesh is edited.

This commit is contained in:
Dion Moult
2023-03-24 14:33:13 +11:00
parent fab4133df6
commit ab34fda42c
3 changed files with 125 additions and 98 deletions
@@ -27,7 +27,8 @@ classes = (
operator.OverrideDelete, operator.OverrideDelete,
operator.OverrideDuplicateMove, operator.OverrideDuplicateMove,
operator.OverrideDuplicateMoveLinked, operator.OverrideDuplicateMoveLinked,
operator.OverrideModeSet, operator.OverrideModeSetEdit,
operator.OverrideModeSetObject,
operator.OverrideOutlinerDelete, operator.OverrideOutlinerDelete,
operator.OverridePasteBuffer, operator.OverridePasteBuffer,
operator.RemoveConnection, 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", "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_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_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", "X", "PRESS")
kmi = km.keymap_items.new("bim.override_object_delete", "DEL", "PRESS") kmi = km.keymap_items.new("bim.override_object_delete", "DEL", "PRESS")
kmi.properties.confirm = False kmi.properties.confirm = False
km = wm.keyconfigs.addon.keymaps.new(name="Mesh", space_type="EMPTY") 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") km = wm.keyconfigs.addon.keymaps.new(name="Outliner", space_type="OUTLINER")
kmi = km.keymap_items.new("bim.override_paste_buffer", "V", "PRESS", ctrl=True) kmi = km.keymap_items.new("bim.override_paste_buffer", "V", "PRESS", ctrl=True)
@@ -18,8 +18,6 @@
import bpy import bpy
import bmesh import bmesh
import struct
import hashlib
import logging import logging
import numpy as np import numpy as np
import ifcopenshell import ifcopenshell
@@ -624,11 +622,10 @@ class OverridePasteBuffer(bpy.types.Operator):
return {"FINISHED"} return {"FINISHED"}
class OverrideModeSet(bpy.types.Operator): class OverrideModeSetEdit(bpy.types.Operator):
bl_idname = "bim.override_mode_set" bl_idname = "bim.override_mode_set_edit"
bl_label = "IFC Mode Set" bl_label = "IFC Mode Set Edit"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
should_save: bpy.props.BoolProperty(name="Should Save", default=True)
def execute(self, context): def execute(self, context):
return IfcStore.execute_ifc_operator(self, context) return IfcStore.execute_ifc_operator(self, context)
@@ -646,105 +643,111 @@ class OverrideModeSet(bpy.types.Operator):
if not element: if not element:
continue continue
if self.active_mode == "EDIT": # We are switching from OBJECT to EDIT mode.
# We are switching from EDIT to OBJECT mode. usage_type = tool.Model.get_usage_type(element)
if obj.data.BIMMeshProperties.ifc_definition_id: if usage_type:
representation = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id) # Parametric objects shall not be edited as meshes as they
if representation.RepresentationType in ("Tessellation", "Brep"): # can be modified to be incompatible with the parametric
edited_objs.append(obj) # constraints.
else: obj.select_set(False)
# We are switching from OBJECT to EDIT mode. continue
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: if obj.data.BIMMeshProperties.ifc_definition_id:
representation = tool.Ifc.get().by_id(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 representation.RepresentationType in ("Tessellation", "Brep"):
if element.HasOpenings: if element.HasOpenings:
# Mesh elements with openings must disable openings # Mesh elements with openings must disable openings
# so that you can edit the original topology. # so that you can edit the original topology.
core.switch_representation( core.switch_representation(
tool.Ifc, tool.Ifc,
tool.Geometry, tool.Geometry,
obj=obj, obj=obj,
representation=representation, representation=representation,
should_reload=True, should_reload=True,
is_global=True, is_global=True,
should_sync_changes_first=False, should_sync_changes_first=False,
apply_openings=False, apply_openings=False,
) )
obj.data.BIMMeshProperties.mesh_checksum = self.get_mesh_checksum(obj.data) obj.data.BIMMeshProperties.mesh_checksum = tool.Geometry.get_mesh_checksum(obj.data)
if not context.selected_objects: if not context.selected_objects:
return {"FINISHED"} return {"FINISHED"}
bpy.ops.object.mode_set(mode="EDIT", toggle=True) 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"} 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): def invoke(self, context, event):
if not tool.Ifc.get(): if not tool.Ifc.get():
return bpy.ops.object.mode_set(mode="EDIT", toggle=True) 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) 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 class OverrideModeSetObject(bpy.types.Operator):
data_bytes = b'' bl_idname = "bim.override_mode_set_object"
for v in vertices: bl_label = "IFC Mode Set Object"
data_bytes += struct.pack('3f', *v.co) bl_options = {"REGISTER", "UNDO"}
for e in edges: should_save: bpy.props.BoolProperty(name="Should Save", default=True)
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 def execute(self, context):
hasher = hashlib.sha1() return IfcStore.execute_ifc_operator(self, context)
hasher.update(data_bytes)
return hasher.hexdigest() 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)
@@ -17,6 +17,8 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>. # along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy import bpy
import struct
import hashlib
import logging import logging
import numpy as np import numpy as np
import ifcopenshell import ifcopenshell
@@ -120,6 +122,27 @@ class Geometry(blenderbim.core.tool.Geometry):
return "IfcExtrudedAreaSolid/IfcCircleProfileDef" return "IfcExtrudedAreaSolid/IfcCircleProfileDef"
return "IfcExtrudedAreaSolid/IfcArbitraryProfileDefWithVoids" 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 @classmethod
def get_object_data(cls, obj): def get_object_data(cls, obj):
return obj.data return obj.data