mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 09:21:46 +00:00
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:
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user