UI fixes for railing and roof modifiers

Changes:
1) Added editing railing / roof path as hotkey to bim / cad tools.
2) Added profile decorator
3) Some switching modes and tools for convenience
4) Fixed nasty bug in tool.Blender.apply_bmesh - method wasn't updating current mesh if it was in edit mode.
This commit is contained in:
Andrej730
2023-03-08 16:56:29 +05:00
parent 4282014bfa
commit 4ad3d65628
8 changed files with 142 additions and 13 deletions
@@ -21,7 +21,7 @@ import bpy
import blenderbim.tool as tool
import blenderbim.bim.module.type.prop as type_prop
from bpy.types import WorkSpaceTool
from blenderbim.bim.module.model.data import AuthoringData
from blenderbim.bim.module.model.data import AuthoringData, RailingData, RoofData
class CadTool(WorkSpaceTool):
@@ -102,6 +102,7 @@ class CadTool(WorkSpaceTool):
row.label(text="", icon="EVENT_SHIFT")
row.label(text="", icon="EVENT_X")
row.operator("bim.reset_vertex", text="Reset Vertex")
elif hasattr(obj.data, "BIMMeshProperties") and obj.data.BIMMeshProperties.subshape_type == "AXIS":
row = layout.row(align=True)
row.label(text="", icon="EVENT_SHIFT")
@@ -128,7 +129,30 @@ class CadTool(WorkSpaceTool):
row.label(text="", icon="EVENT_SHIFT")
row.label(text="", icon="EVENT_O")
row.operator("bim.cad_hotkey", text="Offset").hotkey = "S_O"
else:
if (
(RailingData.is_loaded or not RailingData.load())
and RailingData.data["parameters"]
and context.active_object.BIMRailingProperties.is_editing_path
):
row = layout.row(align=True)
row.label(text="", icon="EVENT_SHIFT")
row.label(text="", icon="EVENT_Q")
row.operator("bim.hotkey", text="Apply Railing Path").hotkey = "S_Q"
row.operator("bim.cancel_editing_railing_path", icon="CANCEL", text="")
elif (
(RoofData.is_loaded or not RoofData.load())
and RoofData.data["parameters"]
and context.active_object.BIMRoofProperties.is_editing_path
):
row = layout.row(align=True)
row.label(text="", icon="EVENT_SHIFT")
row.label(text="", icon="EVENT_Q")
row.operator("bim.hotkey", text="Apply Roof Path").hotkey = "S_Q"
row.operator("bim.cancel_editing_roof_path", icon="CANCEL", text="")
row = layout.row(align=True)
row.label(text="", icon="EVENT_SHIFT")
row.label(text="", icon="EVENT_E")
@@ -219,6 +243,21 @@ class CadHotkey(bpy.types.Operator):
bpy.ops.bim.edit_extrusion_profile()
elif bpy.context.active_object.data.BIMMeshProperties.subshape_type == "AXIS":
bpy.ops.bim.edit_extrusion_axis()
elif (
(RailingData.is_loaded or not RailingData.load())
and RailingData.data["parameters"]
and bpy.context.active_object.BIMRailingProperties.is_editing_path
):
bpy.ops.bim.finish_editing_railing_path()
elif (
(RoofData.is_loaded or not RoofData.load())
and RoofData.data["parameters"]
and bpy.context.active_object.BIMRoofProperties.is_editing_path
):
bpy.ops.bim.finish_editing_roof_path()
else:
bpy.ops.bim.edit_arbitrary_profile()
@@ -118,6 +118,7 @@ def update_door_modifier_representation(context):
update_simple_openings(element, props.overall_width, props.overall_height)
# TODO: move it out to tools
def bm_sort_out_geom(geom_data):
geom_dict = {"verts": [], "edges": [], "faces": []}
@@ -30,6 +30,7 @@ from blenderbim.bim.helper import convert_property_group_from_si
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.model.door import bm_sort_out_geom
from blenderbim.bim.module.model.data import RailingData, refresh
from blenderbim.bim.module.model.decorator import ProfileDecorator
from mathutils import Vector, Matrix
from pprint import pprint
@@ -40,6 +41,18 @@ import json
# https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcRailingType.htm
# TODO: move it out to tools
def blender_get_viewport_context():
"""Get viewport area context for context overriding.
It's a bit naive since it's just taking the first available `VIEW_3D` area
when in real life you can have a couple of those.
"""
area = next(area for area in bpy.context.screen.areas if area.type == "VIEW_3D")
context_override = {"area": area}
return context_override
def bm_split_edge_at_offset(edge, offset):
v0, v1 = edge.verts
@@ -352,10 +365,14 @@ class EnableEditingRailingPath(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context):
obj = context.active_object
props = obj.BIMRailingProperties
element = tool.Ifc.get_entity(obj)
props.is_editing_path = True
update_railing_modifier_bmesh(context)
if bpy.context.object.mode != "EDIT":
bpy.ops.object.mode_set(mode="EDIT")
bpy.ops.wm.tool_set_by_id(blender_get_viewport_context(), name="bim.cad_tool")
ProfileDecorator.install(context)
return {"FINISHED"}
@@ -366,10 +383,14 @@ class CancelEditingRailingPath(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context):
obj = context.active_object
element = tool.Ifc.get_entity(obj)
props = obj.BIMRailingProperties
update_railing_modifier_bmesh(context)
ProfileDecorator.uninstall()
props.is_editing_path = False
update_railing_modifier_bmesh(context)
if bpy.context.object.mode == "EDIT":
bpy.ops.object.mode_set(mode="OBJECT")
return {"FINISHED"}
@@ -386,11 +407,14 @@ class FinishEditingRailingPath(bpy.types.Operator, tool.Ifc.Operator):
railing_data = props.get_general_kwargs()
path_data = get_path_data(obj)
railing_data["path_data"] = path_data
ProfileDecorator.uninstall()
props.is_editing_path = False
update_bbim_railing_pset(element, railing_data)
refresh() # need RailingData to be updated before update_railing_modifier_bmesh
refresh() # RailingData has to be updated before run update_railing_modifier_bmesh
update_railing_modifier_bmesh(context)
if bpy.context.object.mode == "EDIT":
bpy.ops.object.mode_set(mode="OBJECT")
return {"FINISHED"}
@@ -25,7 +25,9 @@ import blenderbim
import blenderbim.tool as tool
from blenderbim.bim.helper import convert_property_group_from_si
from blenderbim.bim.module.model.door import bm_sort_out_geom
from blenderbim.bim.module.model.railing import blender_get_viewport_context
from blenderbim.bim.module.model.data import RoofData, refresh
from blenderbim.bim.module.model.decorator import ProfileDecorator
import json
from math import tan, radians
@@ -135,13 +137,15 @@ def update_roof_modifier_ifc_data(context):
obj = context.active_object
props = obj.BIMRoofProperties
element = tool.Ifc.get_entity(obj)
ifc_file = tool.Ifc.get()
# type attributes
element.PredefinedType = props.roof_type
# occurences attributes
# occurences = tool.Ifc.get_all_element_occurences(element)
# TODO: add Qto_RoofBaseQuantities, need to calculate GrossArea, NetArea, ProjectedArea
# https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/Qto_RoofBaseQuantities.htm
def update_bbim_roof_pset(element, roof_data):
pset = tool.Pset.get_element_pset(element, "BBIM_Roof")
@@ -378,10 +382,14 @@ class EnableEditingRoofPath(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context):
obj = context.active_object
props = obj.BIMRoofProperties
element = tool.Ifc.get_entity(obj)
props.is_editing_path = True
update_roof_modifier_bmesh(context)
if bpy.context.object.mode != "EDIT":
bpy.ops.object.mode_set(mode="EDIT")
bpy.ops.wm.tool_set_by_id(blender_get_viewport_context(), name="bim.cad_tool")
ProfileDecorator.install(context)
return {"FINISHED"}
@@ -392,10 +400,14 @@ class CancelEditingRoofPath(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context):
obj = context.active_object
element = tool.Ifc.get_entity(obj)
props = obj.BIMRoofProperties
update_roof_modifier_bmesh(context)
ProfileDecorator.uninstall()
props.is_editing_path = False
update_roof_modifier_bmesh(context)
if bpy.context.object.mode == "EDIT":
bpy.ops.object.mode_set(mode="OBJECT")
return {"FINISHED"}
@@ -412,11 +424,14 @@ class FinishEditingRoofPath(bpy.types.Operator, tool.Ifc.Operator):
roof_data = props.get_general_kwargs()
path_data = get_path_data(obj)
roof_data["path_data"] = path_data
ProfileDecorator.uninstall()
props.is_editing_path = False
update_bbim_roof_pset(element, roof_data)
refresh() # need RoofData to be updated before update_roof_modifier_bmesh
refresh() # RoofData has to be updated before run update_roof_modifier_bmesh
update_roof_modifier_bmesh(context)
if bpy.context.object.mode == "EDIT":
bpy.ops.object.mode_set(mode="OBJECT")
return {"FINISHED"}
@@ -612,6 +612,7 @@ class EnableEditingExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator):
bpy.ops.object.mode_set(mode="EDIT")
ProfileDecorator.install(context)
# TODO: test it from properties panel?
if not bpy.app.background:
bpy.ops.wm.tool_set_by_id(name="bim.cad_tool")
return {"FINISHED"}
@@ -577,7 +577,7 @@ class BIM_PT_railing(bpy.types.Panel):
update_railing_modifier_bmesh(context)
elif props.is_editing_path:
row.operator("bim.finish_editing_railing_path", icon="CHECKMARK", text="Finish editing path")
row.operator("bim.finish_editing_railing_path", icon="CHECKMARK", text="")
row.operator("bim.cancel_editing_railing_path", icon="CANCEL", text="")
else:
@@ -636,7 +636,7 @@ class BIM_PT_roof(bpy.types.Panel):
update_roof_modifier_bmesh(context)
elif props.is_editing_path:
row.operator("bim.finish_editing_roof_path", icon="CHECKMARK", text="Finish editing path")
row.operator("bim.finish_editing_roof_path", icon="CHECKMARK", text="")
row.operator("bim.cancel_editing_roof_path", icon="CANCEL", text="")
else:
@@ -24,7 +24,7 @@ import blenderbim.tool as tool
import blenderbim.bim.module.type.prop as type_prop
from blenderbim.bim.helper import prop_with_search, close_operator_panel
from bpy.types import WorkSpaceTool
from blenderbim.bim.module.model.data import AuthoringData
from blenderbim.bim.module.model.data import AuthoringData, RailingData, RoofData
from blenderbim.bim.module.model import prop
@@ -225,6 +225,7 @@ class BimToolUI:
row.operator("bim.join_wall", icon="X", text="").join_type = ""
elif AuthoringData.data["active_material_usage"] == "LAYER3":
# unnecessary check because BIM Tool is not available in EDIT mode?
if context.active_object.mode == "OBJECT":
row = cls.layout.row(align=True)
row.label(text="", icon="EVENT_SHIFT")
@@ -274,6 +275,7 @@ class BimToolUI:
row.label(text="", icon="EVENT_G")
add_layout_hotkey_operator(row, "Regen", "S_G", bpy.ops.bim.recalculate_profile.__doc__)
row.operator("bim.extend_profile", icon="X", text="").join_type = ""
elif AuthoringData.data["active_class"] in (
"IfcWindow",
"IfcWindowStandardCase",
@@ -295,11 +297,13 @@ class BimToolUI:
row.label(text="", icon="EVENT_SHIFT")
row.label(text="", icon="EVENT_F")
row.operator("bim.hotkey", text="Flip").hotkey = "S_F"
elif AuthoringData.data["active_class"] in ("IfcSpace",):
row = cls.layout.row(align=True)
row.label(text="", icon="EVENT_SHIFT")
row.label(text="", icon="EVENT_G")
add_layout_hotkey_operator(row, "Regen", "S_G", bpy.ops.bim.generate_space.__doc__)
elif AuthoringData.data["active_class"] in (
"IfcCableCarrierSegmentType",
"IfcCableSegmentType",
@@ -310,6 +314,26 @@ class BimToolUI:
row.label(text="", icon="EVENT_SHIFT")
row.label(text="Extend", icon="EVENT_E")
elif (
(RailingData.is_loaded or not RailingData.load())
and RailingData.data["parameters"]
and not context.active_object.BIMRailingProperties.is_editing_path
):
row = cls.layout.row(align=True)
row.label(text="", icon="EVENT_SHIFT")
row.label(text="", icon="EVENT_E")
row.operator("bim.hotkey", text="Edit Railing Path").hotkey = "S_E"
elif (
(RoofData.is_loaded or not RoofData.load())
and RoofData.data["parameters"]
and not context.active_object.BIMRoofProperties.is_editing_path
):
row = cls.layout.row(align=True)
row.label(text="", icon="EVENT_SHIFT")
row.label(text="", icon="EVENT_E")
row.operator("bim.hotkey", text="Edit Roof Path").hotkey = "S_E"
row = cls.layout.row(align=True)
row.label(text="", icon="EVENT_SHIFT")
row.label(text="", icon="EVENT_O")
@@ -317,6 +341,7 @@ class BimToolUI:
row.operator("bim.add_opening", text="Apply Void")
else:
row.operator("bim.add_potential_opening", text="Add Void")
if AuthoringData.data["is_voidable_element"]:
if AuthoringData.data["has_visible_openings"]:
row.operator("bim.edit_openings", icon="CHECKMARK", text="")
@@ -462,6 +487,7 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
def hotkey_S_Q(self):
if not bpy.context.selected_objects:
return
bpy.ops.bim.calculate_all_quantities()
def hotkey_S_C(self):
@@ -499,27 +525,49 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
elif self.active_material_usage == "PROFILE":
# Extend PROFILE to cursor
bpy.ops.bim.extend_profile(join_type="T")
elif self.active_material_usage == "LAYER2" and selected_usages.get("PROFILE", []):
# Extend PROFILEs to LAYER2
[o.select_set(False) for o in selected_usages.get("LAYER3", [])]
[o.select_set(False) for o in selected_usages.get("LAYER2", []) if o != bpy.context.active_object]
bpy.ops.bim.extend_profile(join_type="T")
elif self.active_material_usage == "LAYER3" and selected_usages.get("LAYER2", []):
# Extend LAYER2s to LAYER3
[o.select_set(False) for o in selected_usages.get("PROFILE", [])]
[o.select_set(False) for o in selected_usages.get("LAYER3", []) if o != bpy.context.active_object]
bpy.ops.bim.join_wall(join_type="T")
elif self.active_material_usage == "LAYER2":
# Extend LAYER2s to LAYER2
[o.select_set(False) for o in selected_usages.get("LAYER3", [])]
[o.select_set(False) for o in selected_usages.get("PROFILE", [])]
bpy.ops.bim.join_wall(join_type="T")
elif self.active_material_usage == "PROFILE":
# Extend PROFILEs to PROFILE
[o.select_set(False) for o in selected_usages.get("LAYER3", [])]
[o.select_set(False) for o in selected_usages.get("LAYER2", [])]
bpy.ops.bim.extend_profile(join_type="T")
elif (
(RailingData.is_loaded or not RailingData.load())
and RailingData.data["parameters"]
and not bpy.context.active_object.BIMRailingProperties.is_editing_path
):
# undo the unselection done above because railing has no usage type 🙃
bpy.context.object.select_set(True)
bpy.ops.bim.enable_editing_railing_path()
elif (
(RoofData.is_loaded or not RoofData.load())
and RoofData.data["parameters"]
and not bpy.context.active_object.BIMRoofProperties.is_editing_path
):
# undo the unselection done above because roof has no usage type
bpy.context.object.select_set(True)
bpy.ops.bim.enable_editing_roof_path()
def hotkey_S_F(self):
if not bpy.context.selected_objects:
return
@@ -106,6 +106,7 @@ class Blender:
"For applying bmesh in edit mode bmesh should be acquired with `bmesh.from_edit_mesh(me)`."
)
bmesh.update_edit_mesh(mesh)
bpy.context.object.update_from_editmode()
else:
bm.to_mesh(mesh)