mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-24 17:29:57 +00:00
See #2887. Save mesh dialog now only shown when mesh is edited.
This commit is contained in:
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user