Fix #4854. Make IFC edit mode toggle dynamic to prevent editing uneditable things.

This commit is contained in:
Dion Moult
2024-06-15 16:40:53 +10:00
parent 1db26aab15
commit 3a6cf0e5ae
4 changed files with 61 additions and 37 deletions
@@ -24,6 +24,7 @@ from mathutils import Vector
def refresh(): def refresh():
ViewportData.is_loaded = False
PlacementData.is_loaded = False PlacementData.is_loaded = False
DerivedCoordinatesData.is_loaded = False DerivedCoordinatesData.is_loaded = False
RepresentationsData.is_loaded = False RepresentationsData.is_loaded = False
@@ -31,6 +32,28 @@ def refresh():
ConnectionsData.is_loaded = False ConnectionsData.is_loaded = False
class ViewportData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.is_loaded = True
cls.data = {"mode": cls.mode()}
@classmethod
def mode(cls):
obj = bpy.context.active_object
obj_mode = [("OBJECT", "IFC Object Mode", "", "OBJECT_DATAMODE", 0)]
mesh_modes = [
("OBJECT", "IFC Object Mode", "", "OBJECT_DATAMODE", 0),
("EDIT", "IFC Edit Mode", "", "EDITMODE_HLT", 1),
]
if not obj or not tool.Blender.is_editable(obj):
return obj_mode
return mesh_modes
class RepresentationsData: class RepresentationsData:
data = {} data = {}
is_loaded = False is_loaded = False
@@ -334,11 +357,13 @@ class PlacementData:
obj = bpy.context.active_object obj = bpy.context.active_object
if obj and props.has_blender_offset: if obj and props.has_blender_offset:
xyz = cls.original_xyz(obj) xyz = cls.original_xyz(obj)
cls.data.update({ cls.data.update(
"original_x": str(xyz[0]), {
"original_y": str(xyz[1]), "original_x": str(xyz[0]),
"original_z": str(xyz[2]), "original_y": str(xyz[1]),
}) "original_z": str(xyz[2]),
}
)
cls.is_loaded = True cls.is_loaded = True
@classmethod @classmethod
@@ -1531,7 +1531,7 @@ class OverrideModeSetEdit(bpy.types.Operator):
return IfcStore.execute_ifc_operator(self, context) return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context): def _execute(self, context):
selected_objs = context.selected_objects or ([context.active_object] if context.active_object else []) selected_objs = tool.Blender.get_selected_objects()
active_obj = context.active_object active_obj = context.active_object
if context.active_object: if context.active_object:
@@ -1542,23 +1542,13 @@ class OverrideModeSetEdit(bpy.types.Operator):
return bpy.ops.bim.enable_editing_boundary_geometry() return bpy.ops.bim.enable_editing_boundary_geometry()
for obj in selected_objs: for obj in selected_objs:
if not obj: if not tool.Blender.is_editable(obj):
continue self.report({"INFO"}, f"Object cannot be edited: {obj.name}")
obj_supports_edit_mode, message = tool.Blender.object_supports_edit_mode(obj)
if not obj_supports_edit_mode:
self.report({"INFO"}, message)
obj.select_set(False) obj.select_set(False)
continue continue
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
if not element: if not element:
continue continue
usage_type = tool.Model.get_usage_type(element)
if usage_type is not None and usage_type not in ("PROFILE", "LAYER3"):
# 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
representation = tool.Geometry.get_active_representation(obj) representation = tool.Geometry.get_active_representation(obj)
if not representation: if not representation:
continue continue
@@ -1566,6 +1556,7 @@ class OverrideModeSetEdit(bpy.types.Operator):
obj.select_set(False) obj.select_set(False)
continue continue
usage_type = tool.Model.get_usage_type(element)
is_profile = True is_profile = True
representation_class = tool.Geometry.get_ifc_representation_class(element, representation) representation_class = tool.Geometry.get_ifc_representation_class(element, representation)
if usage_type == "PROFILE": if usage_type == "PROFILE":
@@ -19,7 +19,7 @@
import bpy import bpy
import blenderbim.tool as tool import blenderbim.tool as tool
from blenderbim.bim.prop import StrProperty, Attribute from blenderbim.bim.prop import StrProperty, Attribute
from blenderbim.bim.module.geometry.data import RepresentationsData from blenderbim.bim.module.geometry.data import RepresentationsData, ViewportData
from bpy.types import PropertyGroup from bpy.types import PropertyGroup
from bpy.props import ( from bpy.props import (
PointerProperty, PointerProperty,
@@ -48,6 +48,12 @@ def update_mode(self, context):
bpy.ops.bim.override_mode_set_edit("INVOKE_DEFAULT") bpy.ops.bim.override_mode_set_edit("INVOKE_DEFAULT")
def get_mode(self, context):
if not ViewportData.is_loaded:
ViewportData.load()
return ViewportData.data["mode"]
def get_styles(self, context): def get_styles(self, context):
# postponed import to avoid circular import # postponed import to avoid circular import
from blenderbim.bim.module.material.data import MaterialsData from blenderbim.bim.module.material.data import MaterialsData
@@ -129,13 +135,5 @@ class BIMGeometryProperties(PropertyGroup):
# Navisworks workaround # Navisworks workaround
should_force_triangulation: BoolProperty(name="Force Triangulation", default=False) should_force_triangulation: BoolProperty(name="Force Triangulation", default=False)
is_changing_mode: BoolProperty(name="Is Changing Mode", default=False) is_changing_mode: BoolProperty(name="Is Changing Mode", default=False)
mode: EnumProperty( mode: EnumProperty(items=get_mode, name="IFC Interaction Mode", update=update_mode)
default="OBJECT",
items=[
("OBJECT", "IFC Object Mode", "", "OBJECT_DATAMODE", 0),
("EDIT", "IFC Edit Mode", "", "EDITMODE_HLT", 1),
],
name="IFC Interaction Mode",
update=update_mode,
)
representation_from_object: PointerProperty(type=bpy.types.Object) representation_from_object: PointerProperty(type=bpy.types.Object)
+19 -9
View File
@@ -131,8 +131,12 @@ class Blender(blenderbim.core.tool.Blender):
@classmethod @classmethod
def get_selected_objects(cls) -> set[bpy.types.Object]: def get_selected_objects(cls) -> set[bpy.types.Object]:
if bpy.context.selected_objects: if bpy.context.selected_objects:
return set(bpy.context.selected_objects + [bpy.context.active_object]) if active_obj := bpy.context.active_object:
return set([bpy.context.active_object]) return set(bpy.context.selected_objects + [active_obj])
return set(bpy.context.selected_objects)
if active_obj := bpy.context.active_object:
return {active_obj}
return set()
@classmethod @classmethod
def create_ifc_object( def create_ifc_object(
@@ -666,8 +670,7 @@ class Blender(blenderbim.core.tool.Blender):
return bpy.ops.object.mode_set(mode="EDIT", toggle=True) return bpy.ops.object.mode_set(mode="EDIT", toggle=True)
elif ao.type in cls.OBJECT_TYPES_THAT_SUPPORT_EDIT_GPENCIL_MODE: elif ao.type in cls.OBJECT_TYPES_THAT_SUPPORT_EDIT_GPENCIL_MODE:
return bpy.ops.object.mode_set(mode="EDIT_GPENCIL", toggle=True) return bpy.ops.object.mode_set(mode="EDIT_GPENCIL", toggle=True)
else: return {"CANCELLED"}
return {"CANCELLED"}
@classmethod @classmethod
def is_object_an_ifc_class(cls, obj: bpy.types.Object, classes: Iterable[str]) -> bool: def is_object_an_ifc_class(cls, obj: bpy.types.Object, classes: Iterable[str]) -> bool:
@@ -766,12 +769,19 @@ class Blender(blenderbim.core.tool.Blender):
return collections_mapping return collections_mapping
@classmethod @classmethod
def object_supports_edit_mode(cls, obj): def is_editable(cls, obj):
if not obj.data: if obj.type not in cls.OBJECT_TYPES_THAT_SUPPORT_EDIT_MODE:
return False, "Can't Edit Empty Object" return False
if not (element := tool.Ifc.get_entity(obj)):
return True
if obj in bpy.context.scene.BIMProjectProperties.clipping_planes_objs: if obj in bpy.context.scene.BIMProjectProperties.clipping_planes_objs:
return False, "Can't Edit Clipping Plane Geometry" return False
return True, "" usage_type = tool.Model.get_usage_type(element)
if usage_type in ("LAYER1", "LAYER2"):
# At the moment, these type types of parametric elements (walls,
# and "blocks") cannot be edited as a mesh-like object.
return False
return True
class Modifier: class Modifier:
@classmethod @classmethod