diff --git a/src/bonsai/bonsai/bim/handler.py b/src/bonsai/bonsai/bim/handler.py index e0679a18c2..2f46d51e66 100644 --- a/src/bonsai/bonsai/bim/handler.py +++ b/src/bonsai/bonsai/bim/handler.py @@ -24,17 +24,15 @@ import ifcopenshell.util.unit import ifcopenshell.api.owner.settings import bonsai.bim import bonsai.tool as tool -import bonsai.core.owner as core_owner from bpy.app.handlers import persistent from bonsai.bim.ifc import IfcStore -from bonsai.bim.module.owner.prop import get_user_person, get_user_organisation from bonsai.bim.module.model.data import AuthoringData from bonsai.bim.module.aggregate.decorator import AggregateDecorator from bonsai.bim.module.georeference.decorator import GeoreferenceDecorator from bonsai.bim.module.model.decorator import WallAxisDecorator, SlabDirectionDecorator from bonsai.bim.module.nest.decorator import NestDecorator from mathutils import Vector -from math import cos, degrees +from math import cos from typing import Union, Callable diff --git a/src/bonsai/bonsai/bim/ifc.py b/src/bonsai/bonsai/bim/ifc.py index 281d39df8e..1cac1d5cb1 100644 --- a/src/bonsai/bonsai/bim/ifc.py +++ b/src/bonsai/bonsai/bim/ifc.py @@ -33,7 +33,7 @@ import bonsai.bim.handler import bonsai.tool as tool from pathlib import Path from bonsai.tool.brick import BrickStore -from typing import Set, Union, Optional, TypedDict, Callable, NotRequired, cast +from typing import Set, Union, Optional, TypedDict, Callable, NotRequired IFC_CONNECTED_TYPE = Union[bpy.types.Material, bpy.types.Object] @@ -106,10 +106,7 @@ class IfcStore: def get_file(): if IfcStore.file is None: props = tool.Blender.get_bim_props() - IfcStore.path = props.ifc_file - # Interpret relative paths as relative to .blend file. - if IfcStore.path and not os.path.isabs(IfcStore.path): - IfcStore.path = os.path.abspath(os.path.join(bpy.path.abspath("//"), IfcStore.path)) + IfcStore.set_path(props.ifc_file) if IfcStore.path: try: IfcStore.load_file(IfcStore.path) @@ -117,6 +114,13 @@ class IfcStore: print(f"Failed to load file {IfcStore.path}. Error details: {e}") return IfcStore.file + @staticmethod + def set_path(value): + IfcStore.path = value + # Interpret relative paths as relative to .blend file. + if IfcStore.path and not os.path.isabs(IfcStore.path): + IfcStore.path = os.path.abspath(os.path.join(bpy.path.abspath("//"), IfcStore.path)) + @staticmethod def get_cache(): if IfcStore.cache is None and IfcStore.path: diff --git a/src/bonsai/bonsai/bim/module/project/operator.py b/src/bonsai/bonsai/bim/module/project/operator.py index 36abbe8ffd..8a26654cbe 100644 --- a/src/bonsai/bonsai/bim/module/project/operator.py +++ b/src/bonsai/bonsai/bim/module/project/operator.py @@ -985,8 +985,7 @@ class LoadProject(bpy.types.Operator, IFCFileSelector): if not self.is_advanced and not self.should_start_fresh_session: bpy.ops.bim.convert_to_blender() - bim_props = tool.Blender.get_bim_props() - bim_props.ifc_file = filepath + tool.Ifc.set_path(filepath) if not tool.Ifc.get(): self.report( {"ERROR"}, @@ -1650,7 +1649,7 @@ class ExportIFC(bpy.types.Operator): output_file = os.path.relpath(output_file, bpy.path.abspath("//")) bim_props = tool.Blender.get_bim_props() if bim_props.ifc_file != output_file and extension not in ("ifczip", "ifcjson"): - bim_props.ifc_file = output_file + tool.Ifc.set_path(output_file) save_blend_file = bool(bpy.data.is_saved and bpy.data.is_dirty and bpy.data.filepath) if save_blend_file: bpy.ops.wm.save_mainfile(filepath=bpy.data.filepath) diff --git a/src/bonsai/bonsai/bim/operator.py b/src/bonsai/bonsai/bim/operator.py index 1690b9497f..3e4f651daa 100644 --- a/src/bonsai/bonsai/bim/operator.py +++ b/src/bonsai/bonsai/bim/operator.py @@ -30,6 +30,7 @@ import webbrowser import ifcopenshell import bonsai.bim import bonsai.tool as tool +import bonsai.bim.handler from bonsai.bim import import_ifc from bonsai.bim.prop import StrProperty from bonsai.bim.ui import IFCFileSelector @@ -222,6 +223,8 @@ class SelectIfcFile(bpy.types.Operator, IFCFileSelector): if self.is_existing_ifc_file(): props = tool.Blender.get_bim_props() props.ifc_file = self.get_filepath() + bonsai.bim.handler.loadIfcStore(bpy.context.scene) + tool.Blender.clear_undo_history() return {"FINISHED"} def invoke(self, context, event): diff --git a/src/bonsai/bonsai/bim/prop.py b/src/bonsai/bonsai/bim/prop.py index 9a69807c23..af378bb8f4 100644 --- a/src/bonsai/bonsai/bim/prop.py +++ b/src/bonsai/bonsai/bim/prop.py @@ -137,12 +137,6 @@ def update_cache_dir(self: "BIMProperties", context: bpy.types.Context) -> None: bonsai.bim.schema.ifc.cache_dir = bim_props.cache_dir -def update_ifc_file(self: "BIMProperties", context: bpy.types.Context) -> None: - bim_props = tool.Blender.get_bim_props() - if bim_props.ifc_file: - bonsai.bim.handler.loadIfcStore(context.scene) - - def update_section_color(self: "BIMProperties", context: bpy.types.Context) -> None: section_node_group = bpy.data.node_groups.get("Section Override") if section_node_group is None: @@ -506,7 +500,7 @@ class BIMProperties(PropertyGroup): ) has_blend_warning: BoolProperty(name="Has Blend Warning", default=False) pset_dir: StringProperty(default=os.path.join("psets") + os.path.sep, name="Default Psets Directory") - ifc_file: StringProperty(name="IFC File", update=update_ifc_file) + ifc_file: StringProperty(name="IFC File") last_transaction: StringProperty(name="Last Transaction") should_section_selected_objects: BoolProperty(name="Section Selected Objects", default=False) section_plane_colour: FloatVectorProperty( diff --git a/src/bonsai/bonsai/tool/blender.py b/src/bonsai/bonsai/tool/blender.py index da85bb734f..8ac081eb93 100644 --- a/src/bonsai/bonsai/tool/blender.py +++ b/src/bonsai/bonsai/tool/blender.py @@ -1616,3 +1616,16 @@ class Blender(bonsai.core.tool.Blender): if 0 <= index < len(collection): return collection[index] return None + + @classmethod + def clear_undo_history(cls) -> None: + """Clears the Blender history, Bonsai history, and IfcOpenShell history""" + old_undo_steps = bpy.context.preferences.edit.undo_steps + bpy.context.preferences.edit.undo_steps = 2 + for i in range(3): + bpy.ops.ed.undo_push(message="Undo history cleared") + bpy.context.preferences.edit.undo_steps = old_undo_steps + tool.Ifc.clear_history() + old_history_size = tool.Ifc.get().history_size + tool.Ifc.get().set_history_size(0) + tool.Ifc.get().set_history_size(old_history_size) diff --git a/src/bonsai/bonsai/tool/ifc.py b/src/bonsai/bonsai/tool/ifc.py index 83c05101ef..0474fa52f5 100644 --- a/src/bonsai/bonsai/tool/ifc.py +++ b/src/bonsai/bonsai/tool/ifc.py @@ -56,6 +56,12 @@ class Ifc(bonsai.core.tool.Ifc): def get(cls) -> ifcopenshell.file: return IfcStore.get_file() + @classmethod + def set_path(cls, value: str) -> None: + bim_props = tool.Blender.get_bim_props() + bim_props.ifc_file = value + IfcStore.set_path(value) + @classmethod def get_path(cls) -> str: """Get absolute filepath to the IFC file, return empty string if file is not saved.""" @@ -66,6 +72,12 @@ class Ifc(bonsai.core.tool.Ifc): if IfcStore.get_file(): return IfcStore.get_file().schema + @classmethod + def clear_history(cls) -> None: + IfcStore.last_transaction = "" + IfcStore.history = [] + IfcStore.future = [] + @classmethod def is_edited(cls, obj: bpy.types.Object, *, ignore_scale: bool = False) -> bool: return (not ignore_scale and tool.Geometry.is_scaled(obj)) or obj in IfcStore.edited_objs