mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-12 18:43:26 +00:00
Fix #4175. Add new UI for managing IFC object and edit modes.
This commit is contained in:
@@ -83,6 +83,7 @@ def register():
|
||||
bpy.types.OUTLINER_MT_object.append(ui.outliner_menu)
|
||||
bpy.types.VIEW3D_MT_object_context_menu.append(ui.object_menu)
|
||||
bpy.types.VIEW3D_MT_edit_mesh.append(ui.edit_mesh_menu)
|
||||
bpy.types.VIEW3D_HT_header.append(ui.mode_menu)
|
||||
wm = bpy.context.window_manager
|
||||
if wm.keyconfigs.addon:
|
||||
km = wm.keyconfigs.addon.keymaps.new(name="Object Mode", space_type="EMPTY")
|
||||
@@ -129,6 +130,7 @@ def unregister():
|
||||
bpy.types.OUTLINER_MT_object.remove(ui.outliner_menu)
|
||||
bpy.types.VIEW3D_MT_object_context_menu.remove(ui.outliner_menu)
|
||||
bpy.types.VIEW3D_MT_edit_mesh.remove(ui.edit_mesh_menu)
|
||||
bpy.types.VIEW3D_HT_header.remove(ui.mode_menu)
|
||||
del bpy.types.Scene.BIMGeometryProperties
|
||||
del bpy.types.Object.BIMGeometryProperties
|
||||
wm = bpy.context.window_manager
|
||||
|
||||
@@ -822,7 +822,7 @@ class OverrideDuplicateMove(bpy.types.Operator):
|
||||
|
||||
# Recreate aggregate relationship
|
||||
for old in old_to_new.keys():
|
||||
if old.is_a("IfcElementAssembly"):
|
||||
if old.is_a("IfcElementAssembly"):
|
||||
tool.Root.recreate_aggregate(old_to_new)
|
||||
|
||||
# Remove connections with old objects and recreates paths
|
||||
@@ -870,7 +870,6 @@ class OverrideDuplicateMove(bpy.types.Operator):
|
||||
|
||||
return arrays_to_create, array_children
|
||||
|
||||
|
||||
def remove_old_connections(old_to_new):
|
||||
for new in old_to_new.values():
|
||||
for connection in new[0].ConnectedTo:
|
||||
@@ -934,7 +933,7 @@ class DuplicateMoveLinkedAggregate(bpy.types.Operator):
|
||||
self.pset_name = "BBIM_Linked_Aggregate"
|
||||
old_to_new = {}
|
||||
|
||||
def select_objects_and_add_data(element):
|
||||
def select_objects_and_add_data(element):
|
||||
add_linked_aggregate_group(element)
|
||||
obj = tool.Ifc.get_object(element)
|
||||
obj.select_set(True)
|
||||
@@ -947,7 +946,7 @@ class DuplicateMoveLinkedAggregate(bpy.types.Operator):
|
||||
|
||||
add_linked_aggregate_pset(part, index)
|
||||
index += 1
|
||||
|
||||
|
||||
obj = tool.Ifc.get_object(part)
|
||||
obj.select_set(True)
|
||||
|
||||
@@ -960,19 +959,15 @@ class DuplicateMoveLinkedAggregate(bpy.types.Operator):
|
||||
]
|
||||
if self.group_name in product_groups_name:
|
||||
return
|
||||
|
||||
|
||||
linked_aggregate_group = ifcopenshell.api.run("group.add_group", tool.Ifc.get(), Name=self.group_name)
|
||||
ifcopenshell.api.run(
|
||||
"group.assign_group", tool.Ifc.get(), products=[element], group=linked_aggregate_group
|
||||
)
|
||||
ifcopenshell.api.run("group.assign_group", tool.Ifc.get(), products=[element], group=linked_aggregate_group)
|
||||
|
||||
def add_linked_aggregate_pset(part, index):
|
||||
pset = ifcopenshell.util.element.get_pset(part, self.pset_name)
|
||||
|
||||
|
||||
if not pset:
|
||||
pset = ifcopenshell.api.run(
|
||||
"pset.add_pset", tool.Ifc.get(), product=part, name=self.pset_name
|
||||
)
|
||||
pset = ifcopenshell.api.run("pset.add_pset", tool.Ifc.get(), product=part, name=self.pset_name)
|
||||
else:
|
||||
pset = tool.Ifc.get().by_id(pset["id"])
|
||||
|
||||
@@ -984,8 +979,7 @@ class DuplicateMoveLinkedAggregate(bpy.types.Operator):
|
||||
)
|
||||
|
||||
return index
|
||||
|
||||
|
||||
|
||||
if len(context.selected_objects) != 1:
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -1002,18 +996,15 @@ class DuplicateMoveLinkedAggregate(bpy.types.Operator):
|
||||
self.report({"INFO"}, "Object is not part of a IfcElementAssembly.")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
select_objects_and_add_data(selected_element)
|
||||
|
||||
old_to_new = OverrideDuplicateMove.execute_ifc_duplicate_operator(self, context, linked=True)
|
||||
|
||||
|
||||
# Recreate aggregate relationship
|
||||
for old in old_to_new.keys():
|
||||
if old.is_a("IfcElementAssembly"):
|
||||
if old.is_a("IfcElementAssembly"):
|
||||
tool.Root.recreate_aggregate(old_to_new)
|
||||
|
||||
|
||||
blenderbim.bim.handler.refresh_ui_data()
|
||||
|
||||
return old_to_new
|
||||
@@ -1038,7 +1029,7 @@ class RefreshLinkedAggregate(bpy.types.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
self.new_active_obj = None
|
||||
self.group_name = 'BBIM_Linked_Aggregate'
|
||||
self.group_name = "BBIM_Linked_Aggregate"
|
||||
self.pset_name = "BBIM_Linked_Aggregate"
|
||||
refresh_start_time = time()
|
||||
old_to_new = {}
|
||||
@@ -1050,9 +1041,9 @@ class RefreshLinkedAggregate(bpy.types.Operator):
|
||||
for part in parts:
|
||||
if part.is_a("IfcElementAssembly"):
|
||||
delete_objects(part)
|
||||
|
||||
|
||||
tool.Geometry.delete_ifc_object(tool.Ifc.get_object(part))
|
||||
|
||||
|
||||
tool.Geometry.delete_ifc_object(tool.Ifc.get_object(element))
|
||||
|
||||
def get_element_assembly(element):
|
||||
@@ -1093,7 +1084,7 @@ class RefreshLinkedAggregate(bpy.types.Operator):
|
||||
except:
|
||||
self.report({"INFO"}, "Object is not part of a Linked Aggregate.")
|
||||
return None, None
|
||||
|
||||
|
||||
return list(set(linked_aggregate_groups)), selected_parents
|
||||
|
||||
|
||||
@@ -1104,7 +1095,7 @@ class RefreshLinkedAggregate(bpy.types.Operator):
|
||||
if not active_element:
|
||||
self.report({"INFO"}, "Object has no Ifc metadata.")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
active_element = get_element_assembly(active_element)
|
||||
selected_objs = context.selected_objects
|
||||
linked_aggregate_groups, selected_parents = handle_selection(selected_objs)
|
||||
@@ -1113,7 +1104,10 @@ class RefreshLinkedAggregate(bpy.types.Operator):
|
||||
|
||||
if len(linked_aggregate_groups) > 1:
|
||||
if len(selected_parents) != len(linked_aggregate_groups):
|
||||
self.report({"INFO"}, "Select only one object from each Linked Aggregate or multiple objects from the same Linked Aggregate.")
|
||||
self.report(
|
||||
{"INFO"},
|
||||
"Select only one object from each Linked Aggregate or multiple objects from the same Linked Aggregate.",
|
||||
)
|
||||
return {"FINISHED"}
|
||||
|
||||
for group in linked_aggregate_groups:
|
||||
@@ -1121,15 +1115,15 @@ class RefreshLinkedAggregate(bpy.types.Operator):
|
||||
if len(linked_aggregate_groups) > 1:
|
||||
base_instance = [e for e in elements if e in selected_parents][0]
|
||||
instances_to_refresh = elements
|
||||
|
||||
|
||||
elif (len(linked_aggregate_groups) == 1) and (len(selected_parents) > 1):
|
||||
base_instance = active_element
|
||||
instances_to_refresh = [element for element in elements if element in selected_parents]
|
||||
|
||||
|
||||
else:
|
||||
base_instance = active_element
|
||||
instances_to_refresh = elements
|
||||
|
||||
|
||||
for element in instances_to_refresh:
|
||||
if element.GlobalId == base_instance.GlobalId:
|
||||
continue
|
||||
@@ -1140,34 +1134,34 @@ class RefreshLinkedAggregate(bpy.types.Operator):
|
||||
selected_matrix = selected_obj.matrix_world
|
||||
object_duplicate = tool.Ifc.get_object(element)
|
||||
duplicate_matrix = object_duplicate.matrix_world.decompose()
|
||||
|
||||
|
||||
delete_objects(element)
|
||||
|
||||
|
||||
for obj in context.selected_objects:
|
||||
obj.select_set(False)
|
||||
|
||||
|
||||
tool.Ifc.get_object(base_instance).select_set(True)
|
||||
|
||||
|
||||
old_to_new = DuplicateMoveLinkedAggregate.execute_ifc_duplicate_linked_aggregate_operator(self, context)
|
||||
for old, new in old_to_new.items():
|
||||
new_obj = tool.Ifc.get_object(new[0])
|
||||
new_base_matrix = Matrix.LocRotScale(*duplicate_matrix)
|
||||
matrix_diff = Matrix.inverted(selected_matrix) @ new_obj.matrix_world
|
||||
matrix_diff = Matrix.inverted(selected_matrix) @ new_obj.matrix_world
|
||||
new_obj_matrix = new_base_matrix @ matrix_diff
|
||||
new_obj.matrix_world = new_obj_matrix
|
||||
|
||||
|
||||
if element_aggregate and new[0].is_a("IfcElementAssembly"):
|
||||
new_aggregate = ifcopenshell.util.element.get_aggregate(new[0])
|
||||
|
||||
|
||||
if not new_aggregate:
|
||||
blenderbim.core.aggregate.assign_object(
|
||||
tool.Ifc,
|
||||
tool.Aggregate,
|
||||
tool.Collector,
|
||||
relating_obj=tool.Ifc.get_object(element_aggregate),
|
||||
related_obj=tool.Ifc.get_object(new[0]),
|
||||
)
|
||||
|
||||
tool.Ifc,
|
||||
tool.Aggregate,
|
||||
tool.Collector,
|
||||
relating_obj=tool.Ifc.get_object(element_aggregate),
|
||||
related_obj=tool.Ifc.get_object(new[0]),
|
||||
)
|
||||
|
||||
blenderbim.bim.handler.refresh_ui_data()
|
||||
|
||||
operator_time = time() - refresh_start_time
|
||||
@@ -1393,7 +1387,12 @@ class OverrideModeSetEdit(bpy.types.Operator):
|
||||
# The active object is non-mesh-like. Set a valid object (or None) as active
|
||||
context.view_layer.objects.active = context.selected_objects[0] if context.selected_objects else None
|
||||
if context.active_object:
|
||||
return tool.Blender.toggle_edit_mode(context)
|
||||
tool.Blender.toggle_edit_mode(context)
|
||||
context.scene.BIMGeometryProperties.is_changing_mode = True
|
||||
if context.scene.BIMGeometryProperties.mode != "EDIT":
|
||||
context.scene.BIMGeometryProperties.mode = "EDIT"
|
||||
context.scene.BIMGeometryProperties.is_changing_mode = False
|
||||
return {"FINISHED"}
|
||||
|
||||
# Restore the selection if nothing worked
|
||||
for obj in selected_objs:
|
||||
@@ -1464,6 +1463,11 @@ class OverrideModeSetObject(bpy.types.Operator):
|
||||
|
||||
bpy.ops.object.mode_set(mode="EDIT", toggle=True)
|
||||
|
||||
context.scene.BIMGeometryProperties.is_changing_mode = True
|
||||
if context.scene.BIMGeometryProperties.mode != "OBJECT":
|
||||
context.scene.BIMGeometryProperties.mode = "OBJECT"
|
||||
context.scene.BIMGeometryProperties.is_changing_mode = False
|
||||
|
||||
if not tool.Ifc.get():
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -38,6 +38,15 @@ def get_contexts(self, context):
|
||||
return RepresentationsData.data["contexts"]
|
||||
|
||||
|
||||
def update_mode(self, context):
|
||||
if self.is_changing_mode:
|
||||
return
|
||||
if self.mode == "OBJECT":
|
||||
bpy.ops.bim.override_mode_set_object("INVOKE_DEFAULT")
|
||||
elif self.mode == "EDIT":
|
||||
bpy.ops.bim.override_mode_set_edit("INVOKE_DEFAULT")
|
||||
|
||||
|
||||
class RepresentationItem(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
surface_style: StringProperty(name="Surface Style")
|
||||
@@ -59,3 +68,13 @@ class BIMGeometryProperties(PropertyGroup):
|
||||
should_force_faceted_brep: BoolProperty(name="Force Faceted Breps", default=False)
|
||||
# Navisworks workaround
|
||||
should_force_triangulation: BoolProperty(name="Force Triangulation", default=False)
|
||||
is_changing_mode: BoolProperty(name="Is Changing Mode", default=False)
|
||||
mode: EnumProperty(
|
||||
default="OBJECT",
|
||||
items=[
|
||||
("OBJECT", "IFC Object Mode", "", "OBJECT_DATAMODE", 0),
|
||||
("EDIT", "IFC Edit Mode", "", "EDITMODE_HLT", 1),
|
||||
],
|
||||
name="IFC Interaction Mode",
|
||||
update=update_mode,
|
||||
)
|
||||
|
||||
@@ -31,6 +31,11 @@ from blenderbim.bim.module.geometry.data import (
|
||||
)
|
||||
|
||||
|
||||
def mode_menu(self, context):
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(context.scene.BIMGeometryProperties, "mode", text="", icon_value=blenderbim.bim.icons["IFC"].icon_id)
|
||||
|
||||
|
||||
def object_menu(self, context):
|
||||
self.layout.separator()
|
||||
self.layout.operator("bim.override_object_duplicate_move", icon="PLUGIN")
|
||||
|
||||
Reference in New Issue
Block a user