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
@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})
bpy.context.scene.BIMProperties.is_dirty = True
is_top_level_operator = not bool(IfcStore.current_transaction)
@@ -436,10 +441,12 @@ class IfcStore:
bonsai.bim.handler.refresh_ui_data()
try:
if is_invoke:
result = getattr(operator, "_invoke")(context, None)
else:
if method == "EXECUTE":
result = getattr(operator, "_execute")(context)
elif method == "INVOKE":
result = getattr(operator, "_invoke")(context, event)
elif method == "MODAL":
result = getattr(operator, "_modal")(context, event)
except:
bonsai.last_error = traceback.format_exc()
# 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"}
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):
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)
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]:
if not tool.Ifc.get():
@@ -154,7 +154,7 @@ class AddDefaultType(bpy.types.Operator, tool.Ifc.Operator):
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_label = "Add Occurrence"
bl_options = {"REGISTER", "UNDO"}
@@ -199,6 +199,9 @@ class AddOccurrence(bpy.types.Operator, PolylineOperator):
snap_obj.select_set(False)
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
props = tool.Model.get_model_props()
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"}
def invoke(self, context, event):
return IfcStore.execute_ifc_operator(self, context, event, method="INVOKE")
def _invoke(self, context, event):
super().invoke(context, event)
ProductDecorator.install(context)
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.material
import bonsai.core.root
from math import pi, degrees, inf, atan2
from mathutils import Vector, Matrix, Quaternion
from bonsai.bim.module.geometry.helper import Helper
from bonsai.bim.ifc import IfcStore
from math import pi, degrees, atan2
from mathutils import Vector, Matrix
from bonsai.bim.module.model.wall import DumbWallRecalculator
from bonsai.bim.module.model.decorator import ProfileDecorator, PolylineDecorator, ProductDecorator
from bonsai.bim.module.model.polyline import PolylineOperator
@@ -1109,7 +1109,7 @@ class EditExtrusionAxis(bpy.types.Operator, tool.Ifc.Operator):
return {"FINISHED"}
class DrawPolylineProfile(bpy.types.Operator, PolylineOperator):
class DrawPolylineProfile(bpy.types.Operator, PolylineOperator, tool.Ifc.Operator):
bl_idname = "bim.draw_polyline_profile"
bl_label = "Draw Polyline Profile"
bl_options = {"REGISTER", "UNDO"}
@@ -1142,6 +1142,9 @@ class DrawPolylineProfile(bpy.types.Operator, PolylineOperator):
DumbProfileJoiner().join_V(profile2["obj"], profile1["obj"])
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:
self.report({"WARNING"}, "You need to select a profile type.")
PolylineDecorator.uninstall()
@@ -1192,6 +1195,9 @@ class DrawPolylineProfile(bpy.types.Operator, PolylineOperator):
return {"RUNNING_MODAL"}
def invoke(self, context, event):
return IfcStore.execute_ifc_operator(self, context, event, method="INVOKE")
def _invoke(self, context, event):
super().invoke(context, event)
ProductDecorator.install(context)
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.tool as tool
from bonsai.bim.ifc import IfcStore
from math import cos, radians
from math import cos
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.polyline import PolylineOperator
from bonsai.bim.module.model.wall import DumbWallRecalculator
@@ -864,7 +863,7 @@ class AddSlabFromWall(bpy.types.Operator, tool.Ifc.Operator):
return {"FINISHED"}
class DrawPolylineSlab(bpy.types.Operator, PolylineOperator):
class DrawPolylineSlab(bpy.types.Operator, PolylineOperator, tool.Ifc.Operator):
bl_idname = "bim.draw_polyline_slab"
bl_label = "Draw Polyline Slab"
bl_options = {"REGISTER", "UNDO"}
@@ -905,6 +904,9 @@ class DrawPolylineSlab(bpy.types.Operator, PolylineOperator):
DumbSlabPlaner().regenerate_from_occurence(element, material_set_usage)
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:
self.report({"WARNING"}, "You need to select a slab type.")
PolylineDecorator.uninstall()
@@ -975,6 +977,9 @@ class DrawPolylineSlab(bpy.types.Operator, PolylineOperator):
return {"RUNNING_MODAL"}
def invoke(self, context, event):
return IfcStore.execute_ifc_operator(self, context, event, method="INVOKE")
def _invoke(self, context, event):
super().invoke(context, event)
ProductDecorator.install(context)
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"])
class DrawPolylineWall(bpy.types.Operator, PolylineOperator):
class DrawPolylineWall(bpy.types.Operator, PolylineOperator, tool.Ifc.Operator):
bl_idname = "bim.draw_polyline_wall"
bl_label = "Draw Polyline Wall"
bl_options = {"REGISTER", "UNDO"}
@@ -399,6 +399,9 @@ class DrawPolylineWall(bpy.types.Operator, PolylineOperator):
DumbWallJoiner().join_V(wall2["obj"], wall1["obj"])
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:
self.report({"WARNING"}, "You need to select a wall type.")
PolylineDecorator.uninstall()
@@ -471,6 +474,9 @@ class DrawPolylineWall(bpy.types.Operator, PolylineOperator):
return {"RUNNING_MODAL"}
def invoke(self, context, event):
return IfcStore.execute_ifc_operator(self, context, event, method="INVOKE")
def _invoke(self, context, event):
super().invoke(context, event)
ProductDecorator.install(context)
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"})
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):
props = context.scene.BIMRootProperties
@@ -44,7 +44,7 @@ def set_shape_aspect_constituents(
A material may be associated with a style (i.e. colour). For example, a
grey style for the "Aluminium" material and a transparent blue style for
the "Laminated Low-e Glass" material.
the "Laminated Low-e Glass" material.
These three concepts of material constituents, shape aspects, and
associated styles are correlated. For example, if the name (e.g. "Framing")
@@ -75,7 +75,7 @@ def set_shape_aspect_constituents(
# Create two materials
aluminium = ifcopenshell.api.material.add_material(model, name="AL01", category="aluminium")
glass = ifcopenshell.api.material.add_material(model, name="GLZ01", category="glass")
# Auto assign material constituents and styles to items based on shape aspects
ifcopenshell.api.material.set_shape_aspect_constituents(
model, element=window, context=body, materials={
@@ -102,9 +102,7 @@ def set_shape_aspect_constituents(
if should_create_new_material_set:
material_set = ifcopenshell.api.material.add_material_set(file, set_type="IfcMaterialConstituentSet")
for name, material in materials.items():
ifcopenshell.api.material.add_constituent(
file, constituent_set=material_set, material=material, name=name
)
ifcopenshell.api.material.add_constituent(file, constituent_set=material_set, material=material, name=name)
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()}