mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
Fix undo bugs with edited_objs
Now it's stored as a collection property in the project properties, so in the most cases we delegate to Blender to ensure it's handled properly on undo and redo.
This commit is contained in:
@@ -45,10 +45,14 @@ class OperationData(TypedDict):
|
||||
obj: str
|
||||
|
||||
|
||||
class EditObjectOperationData(TypedDict):
|
||||
obj: str
|
||||
|
||||
|
||||
class Operation(TypedDict):
|
||||
rollback: Callable
|
||||
commit: Callable
|
||||
data: Union[OperationData, None]
|
||||
data: Union[OperationData, EditObjectOperationData, None]
|
||||
|
||||
|
||||
class TransactionStep(TypedDict):
|
||||
@@ -295,7 +299,28 @@ class IfcStore:
|
||||
if "guid" in data:
|
||||
IfcStore.guid_map[data["guid"]] = obj
|
||||
tool.Ifc.setup_listeners(obj)
|
||||
# TODO We're handling id_map and guid_map, but what about edited_objs? This might cause big problems.
|
||||
|
||||
@staticmethod
|
||||
def history_edit_object(obj: bpy.types.Object, *, finish_editing: bool) -> None:
|
||||
if not IfcStore.history:
|
||||
return
|
||||
|
||||
commit, rollback = IfcStore.commit_edit_object, IfcStore.rollback_edit_object
|
||||
if finish_editing:
|
||||
commit, rollback = rollback, commit
|
||||
|
||||
data = EditObjectOperationData(obj=obj.name)
|
||||
IfcStore.history[-1]["operations"].append(Operation(rollback=rollback, commit=commit, data=data))
|
||||
|
||||
@staticmethod
|
||||
def commit_edit_object(data: EditObjectOperationData) -> None:
|
||||
obj = bpy.data.objects[data["obj"]]
|
||||
IfcStore.edited_objs.add(obj)
|
||||
|
||||
@staticmethod
|
||||
def rollback_edit_object(data: EditObjectOperationData) -> None:
|
||||
obj = bpy.data.objects[data["obj"]]
|
||||
IfcStore.edited_objs.discard(obj)
|
||||
|
||||
@staticmethod
|
||||
def rollback_unlink_element(data: OperationData) -> None:
|
||||
|
||||
@@ -64,6 +64,7 @@ classes = (
|
||||
prop.LibraryElement,
|
||||
prop.FilterCategory,
|
||||
prop.Link,
|
||||
prop.EditedObj,
|
||||
prop.BIMProjectProperties,
|
||||
prop.MeasureToolSettings,
|
||||
ui.BIM_MT_new_project,
|
||||
|
||||
@@ -126,6 +126,10 @@ class Link(PropertyGroup):
|
||||
)
|
||||
|
||||
|
||||
class EditedObj(PropertyGroup):
|
||||
obj: PointerProperty(type=bpy.types.Object)
|
||||
|
||||
|
||||
class BIMProjectProperties(PropertyGroup):
|
||||
is_editing: BoolProperty(name="Is Editing", default=False)
|
||||
is_loading: BoolProperty(name="Is Loading", default=False)
|
||||
@@ -226,6 +230,7 @@ class BIMProjectProperties(PropertyGroup):
|
||||
queried_obj_root: bpy.props.PointerProperty(type=bpy.types.Object)
|
||||
clipping_planes: bpy.props.CollectionProperty(type=ObjProperty)
|
||||
clipping_planes_active: bpy.props.IntProperty(min=0, default=0, max=5)
|
||||
edited_objs: bpy.props.CollectionProperty(type=EditedObj)
|
||||
|
||||
@property
|
||||
def clipping_planes_objs(self):
|
||||
|
||||
@@ -176,6 +176,16 @@ class Ifc(bonsai.core.tool.Ifc):
|
||||
IfcStore.id_map[style.id()] = obj
|
||||
cls.setup_listeners(obj)
|
||||
|
||||
IfcStore.edited_objs = set()
|
||||
edited_objs = bpy.context.scene.BIMProjectProperties.edited_objs
|
||||
for i in range(len(edited_objs))[::-1]:
|
||||
obj = edited_objs[i].obj
|
||||
if obj:
|
||||
IfcStore.edited_objs.add(obj)
|
||||
else:
|
||||
# Object was removed.
|
||||
edited_objs.remove(i)
|
||||
|
||||
@classmethod
|
||||
def setup_listeners(cls, obj: IFC_CONNECTED_TYPE) -> None:
|
||||
if isinstance(obj, bpy.types.Object):
|
||||
@@ -200,17 +210,17 @@ class Ifc(bonsai.core.tool.Ifc):
|
||||
changed geometry to IFC, we mark it as changed and then it's saved later
|
||||
(typically during project save or switch_representation(should_sync_changes_first=True)).
|
||||
|
||||
Currently, underlying storage for edited objects, IfcStore.edited_objs, is not tracked
|
||||
by undo system. So, it's error prone:
|
||||
- undo after object was marked as edited, will keep it edited, adding unnecessary sync
|
||||
- undo after object was unmarked as edited, will keep it unmarked, so edit geometry may be lost
|
||||
|
||||
Other caveat of using edited objects is that it won't have an effect for objects with openings,
|
||||
since we can't deduce non-openings representation from edited representation with openings.
|
||||
|
||||
So, it's preferable not to use edited objects if object can have an opening. It's still can be used for spaces.
|
||||
"""
|
||||
if obj in IfcStore.edited_objs:
|
||||
return
|
||||
edited_objs = bpy.context.scene.BIMProjectProperties.edited_objs
|
||||
edited_objs.add().obj = obj
|
||||
IfcStore.edited_objs.add(obj)
|
||||
IfcStore.history_edit_object(obj, finish_editing=False)
|
||||
|
||||
@classmethod
|
||||
def finish_edit(cls, obj: bpy.types.Object) -> None:
|
||||
@@ -218,7 +228,12 @@ class Ifc(bonsai.core.tool.Ifc):
|
||||
|
||||
Method is safe to use on an object that wasn't marked as edited before.
|
||||
"""
|
||||
if obj not in IfcStore.edited_objs:
|
||||
return
|
||||
edited_objs = bpy.context.scene.BIMProjectProperties.edited_objs
|
||||
edited_objs.remove(next(i for i, o in enumerate(edited_objs) if o.obj == obj))
|
||||
IfcStore.edited_objs.discard(obj)
|
||||
IfcStore.history_edit_object(obj, finish_editing=True)
|
||||
|
||||
@classmethod
|
||||
def resolve_uri(cls, uri: str) -> str:
|
||||
|
||||
Reference in New Issue
Block a user