See #5888. Fix bug where polyline tools weren't part of the undo system.

This commit is contained in:
Dion Moult
2025-02-16 11:48:51 +11:00
parent 774a770c48
commit b226a79ee8
8 changed files with 49 additions and 21 deletions
+11 -4
View File
@@ -407,7 +407,12 @@ class IfcStore:
obj.BIMObjectProperties.ifc_definition_id = 0 obj.BIMObjectProperties.ifc_definition_id = 0
@staticmethod @staticmethod
def execute_ifc_operator(operator: tool.Ifc.Operator, context: bpy.types.Context, is_invoke=False) -> set[str]: def execute_ifc_operator(
operator: tool.Ifc.Operator,
context: bpy.types.Context,
event=None,
method: Literal["EXECUTE", "INVOKE", "MODAL"] = "EXECUTE",
) -> set[str]:
bonsai.last_actions.append({"type": "operator", "name": operator.bl_idname}) bonsai.last_actions.append({"type": "operator", "name": operator.bl_idname})
bpy.context.scene.BIMProperties.is_dirty = True bpy.context.scene.BIMProperties.is_dirty = True
is_top_level_operator = not bool(IfcStore.current_transaction) is_top_level_operator = not bool(IfcStore.current_transaction)
@@ -436,10 +441,12 @@ class IfcStore:
bonsai.bim.handler.refresh_ui_data() bonsai.bim.handler.refresh_ui_data()
try: try:
if is_invoke: if method == "EXECUTE":
result = getattr(operator, "_invoke")(context, None)
else:
result = getattr(operator, "_execute")(context) result = getattr(operator, "_execute")(context)
elif method == "INVOKE":
result = getattr(operator, "_invoke")(context, event)
elif method == "MODAL":
result = getattr(operator, "_modal")(context, event)
except: except:
bonsai.last_error = traceback.format_exc() bonsai.last_error = traceback.format_exc()
# Try to ensure undo will work since Blender undo does work in case of errors. # Try to ensure undo will work since Blender undo does work in case of errors.
@@ -1913,7 +1913,7 @@ class OverrideModeSetEdit(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def invoke(self, context, event): def invoke(self, context, event):
return IfcStore.execute_ifc_operator(self, context, is_invoke=True) return IfcStore.execute_ifc_operator(self, context, event, method="INVOKE")
def _invoke(self, context, event): def _invoke(self, context, event):
if not tool.Ifc.get(): if not tool.Ifc.get():
@@ -2089,7 +2089,7 @@ class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator):
should_save: bpy.props.BoolProperty(name="Should Save", default=True) should_save: bpy.props.BoolProperty(name="Should Save", default=True)
def invoke(self, context, event): def invoke(self, context, event):
return IfcStore.execute_ifc_operator(self, context, is_invoke=True) return IfcStore.execute_ifc_operator(self, context, event, method="INVOKE")
def _invoke(self, context: bpy.types.Context, event: bpy.types.Event) -> set[str]: def _invoke(self, context: bpy.types.Context, event: bpy.types.Event) -> set[str]:
if not tool.Ifc.get(): if not tool.Ifc.get():
@@ -154,7 +154,7 @@ class AddDefaultType(bpy.types.Operator, tool.Ifc.Operator):
bpy.ops.bim.add_element() bpy.ops.bim.add_element()
class AddOccurrence(bpy.types.Operator, PolylineOperator): class AddOccurrence(bpy.types.Operator, PolylineOperator, tool.Ifc.Operator):
bl_idname = "bim.add_occurrence" bl_idname = "bim.add_occurrence"
bl_label = "Add Occurrence" bl_label = "Add Occurrence"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
@@ -199,6 +199,9 @@ class AddOccurrence(bpy.types.Operator, PolylineOperator):
snap_obj.select_set(False) snap_obj.select_set(False)
def modal(self, context, event): def modal(self, context, event):
return IfcStore.execute_ifc_operator(self, context, event, method="MODAL")
def _modal(self, context, event):
# Ensure state of BIM tool props is valid # Ensure state of BIM tool props is valid
props = tool.Model.get_model_props() props = tool.Model.get_model_props()
relating_type_id = tool.Blender.get_enum_safe(props, "relating_type_id") relating_type_id = tool.Blender.get_enum_safe(props, "relating_type_id")
@@ -251,6 +254,9 @@ class AddOccurrence(bpy.types.Operator, PolylineOperator):
return {"RUNNING_MODAL"} return {"RUNNING_MODAL"}
def invoke(self, context, event): def invoke(self, context, event):
return IfcStore.execute_ifc_operator(self, context, event, method="INVOKE")
def _invoke(self, context, event):
super().invoke(context, event) super().invoke(context, event)
ProductDecorator.install(context) ProductDecorator.install(context)
self.tool_state.use_default_container = True self.tool_state.use_default_container = True
+10 -4
View File
@@ -32,9 +32,9 @@ import bonsai.core.type
import bonsai.core.geometry import bonsai.core.geometry
import bonsai.core.material import bonsai.core.material
import bonsai.core.root import bonsai.core.root
from math import pi, degrees, inf, atan2 from bonsai.bim.ifc import IfcStore
from mathutils import Vector, Matrix, Quaternion from math import pi, degrees, atan2
from bonsai.bim.module.geometry.helper import Helper from mathutils import Vector, Matrix
from bonsai.bim.module.model.wall import DumbWallRecalculator from bonsai.bim.module.model.wall import DumbWallRecalculator
from bonsai.bim.module.model.decorator import ProfileDecorator, PolylineDecorator, ProductDecorator from bonsai.bim.module.model.decorator import ProfileDecorator, PolylineDecorator, ProductDecorator
from bonsai.bim.module.model.polyline import PolylineOperator from bonsai.bim.module.model.polyline import PolylineOperator
@@ -1109,7 +1109,7 @@ class EditExtrusionAxis(bpy.types.Operator, tool.Ifc.Operator):
return {"FINISHED"} return {"FINISHED"}
class DrawPolylineProfile(bpy.types.Operator, PolylineOperator): class DrawPolylineProfile(bpy.types.Operator, PolylineOperator, tool.Ifc.Operator):
bl_idname = "bim.draw_polyline_profile" bl_idname = "bim.draw_polyline_profile"
bl_label = "Draw Polyline Profile" bl_label = "Draw Polyline Profile"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
@@ -1142,6 +1142,9 @@ class DrawPolylineProfile(bpy.types.Operator, PolylineOperator):
DumbProfileJoiner().join_V(profile2["obj"], profile1["obj"]) DumbProfileJoiner().join_V(profile2["obj"], profile1["obj"])
def modal(self, context, event): def modal(self, context, event):
return IfcStore.execute_ifc_operator(self, context, event, method="MODAL")
def _modal(self, context, event):
if not self.relating_type: if not self.relating_type:
self.report({"WARNING"}, "You need to select a profile type.") self.report({"WARNING"}, "You need to select a profile type.")
PolylineDecorator.uninstall() PolylineDecorator.uninstall()
@@ -1192,6 +1195,9 @@ class DrawPolylineProfile(bpy.types.Operator, PolylineOperator):
return {"RUNNING_MODAL"} return {"RUNNING_MODAL"}
def invoke(self, context, event): def invoke(self, context, event):
return IfcStore.execute_ifc_operator(self, context, event, method="INVOKE")
def _invoke(self, context, event):
super().invoke(context, event) super().invoke(context, event)
ProductDecorator.install(context) ProductDecorator.install(context)
self.tool_state.use_default_container = True self.tool_state.use_default_container = True
+8 -3
View File
@@ -31,9 +31,8 @@ import bonsai.core.geometry
import bonsai.core.root import bonsai.core.root
import bonsai.tool as tool import bonsai.tool as tool
from bonsai.bim.ifc import IfcStore from bonsai.bim.ifc import IfcStore
from math import cos, radians from math import cos
from mathutils import Vector, Matrix from mathutils import Vector, Matrix
from bonsai.bim.module.geometry.helper import Helper
from bonsai.bim.module.model.decorator import ProfileDecorator, PolylineDecorator, ProductDecorator from bonsai.bim.module.model.decorator import ProfileDecorator, PolylineDecorator, ProductDecorator
from bonsai.bim.module.model.polyline import PolylineOperator from bonsai.bim.module.model.polyline import PolylineOperator
from bonsai.bim.module.model.wall import DumbWallRecalculator from bonsai.bim.module.model.wall import DumbWallRecalculator
@@ -864,7 +863,7 @@ class AddSlabFromWall(bpy.types.Operator, tool.Ifc.Operator):
return {"FINISHED"} return {"FINISHED"}
class DrawPolylineSlab(bpy.types.Operator, PolylineOperator): class DrawPolylineSlab(bpy.types.Operator, PolylineOperator, tool.Ifc.Operator):
bl_idname = "bim.draw_polyline_slab" bl_idname = "bim.draw_polyline_slab"
bl_label = "Draw Polyline Slab" bl_label = "Draw Polyline Slab"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
@@ -905,6 +904,9 @@ class DrawPolylineSlab(bpy.types.Operator, PolylineOperator):
DumbSlabPlaner().regenerate_from_occurence(element, material_set_usage) DumbSlabPlaner().regenerate_from_occurence(element, material_set_usage)
def modal(self, context, event): def modal(self, context, event):
return IfcStore.execute_ifc_operator(self, context, event, method="MODAL")
def _modal(self, context, event):
if not self.relating_type: if not self.relating_type:
self.report({"WARNING"}, "You need to select a slab type.") self.report({"WARNING"}, "You need to select a slab type.")
PolylineDecorator.uninstall() PolylineDecorator.uninstall()
@@ -975,6 +977,9 @@ class DrawPolylineSlab(bpy.types.Operator, PolylineOperator):
return {"RUNNING_MODAL"} return {"RUNNING_MODAL"}
def invoke(self, context, event): def invoke(self, context, event):
return IfcStore.execute_ifc_operator(self, context, event, method="INVOKE")
def _invoke(self, context, event):
super().invoke(context, event) super().invoke(context, event)
ProductDecorator.install(context) ProductDecorator.install(context)
self.tool_state.use_default_container = True self.tool_state.use_default_container = True
+7 -1
View File
@@ -350,7 +350,7 @@ class AddWallsFromSlab(bpy.types.Operator, tool.Ifc.Operator):
DumbWallJoiner().join_V(wall2["obj"], wall1["obj"]) DumbWallJoiner().join_V(wall2["obj"], wall1["obj"])
class DrawPolylineWall(bpy.types.Operator, PolylineOperator): class DrawPolylineWall(bpy.types.Operator, PolylineOperator, tool.Ifc.Operator):
bl_idname = "bim.draw_polyline_wall" bl_idname = "bim.draw_polyline_wall"
bl_label = "Draw Polyline Wall" bl_label = "Draw Polyline Wall"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
@@ -399,6 +399,9 @@ class DrawPolylineWall(bpy.types.Operator, PolylineOperator):
DumbWallJoiner().join_V(wall2["obj"], wall1["obj"]) DumbWallJoiner().join_V(wall2["obj"], wall1["obj"])
def modal(self, context, event): def modal(self, context, event):
return IfcStore.execute_ifc_operator(self, context, event, method="MODAL")
def _modal(self, context, event):
if not self.relating_type: if not self.relating_type:
self.report({"WARNING"}, "You need to select a wall type.") self.report({"WARNING"}, "You need to select a wall type.")
PolylineDecorator.uninstall() PolylineDecorator.uninstall()
@@ -471,6 +474,9 @@ class DrawPolylineWall(bpy.types.Operator, PolylineOperator):
return {"RUNNING_MODAL"} return {"RUNNING_MODAL"}
def invoke(self, context, event): def invoke(self, context, event):
return IfcStore.execute_ifc_operator(self, context, event, method="INVOKE")
def _invoke(self, context, event):
super().invoke(context, event) super().invoke(context, event)
ProductDecorator.install(context) ProductDecorator.install(context)
self.tool_state.use_default_container = True self.tool_state.use_default_container = True
@@ -355,7 +355,7 @@ class AddElement(bpy.types.Operator, tool.Ifc.Operator):
ifc_class: bpy.props.StringProperty(options={"SKIP_SAVE"}) ifc_class: bpy.props.StringProperty(options={"SKIP_SAVE"})
def invoke(self, context, event): def invoke(self, context, event):
return IfcStore.execute_ifc_operator(self, context, is_invoke=True) return IfcStore.execute_ifc_operator(self, context, event, method="INVOKE")
def _invoke(self, context, event): def _invoke(self, context, event):
props = context.scene.BIMRootProperties props = context.scene.BIMRootProperties
@@ -102,9 +102,7 @@ def set_shape_aspect_constituents(
if should_create_new_material_set: if should_create_new_material_set:
material_set = ifcopenshell.api.material.add_material_set(file, set_type="IfcMaterialConstituentSet") material_set = ifcopenshell.api.material.add_material_set(file, set_type="IfcMaterialConstituentSet")
for name, material in materials.items(): for name, material in materials.items():
ifcopenshell.api.material.add_constituent( ifcopenshell.api.material.add_constituent(file, constituent_set=material_set, material=material, name=name)
file, constituent_set=material_set, material=material, name=name
)
ifcopenshell.api.material.assign_material(file, products=[element], material=material_set) ifcopenshell.api.material.assign_material(file, products=[element], material=material_set)
styles = {n: ifcopenshell.util.representation.get_material_style(m, context) for n, m in materials.items()} styles = {n: ifcopenshell.util.representation.get_material_style(m, context) for n, m in materials.items()}