Add new way to move objects that are a part of an aggregate.

Now, when trying to move an object that is a part of an aggregate, the
`OverrideMoveAggregate` will be called, meaning that it will move the whole
aggregation. It works by creating constraints and moving the aggregate instead
of the selected object.

It also introduces the idea of editing an aggregate (no hotkey yet). When active, the user
will be able to move the parts of the aggregate instead of the whole aggregation: https://imgur.com/IcCGtPN
This commit is contained in:
Bruno Perdigão
2024-11-22 11:05:09 -03:00
committed by Bruno Perdigão
parent 6f11f74aac
commit 3dcded819f
6 changed files with 150 additions and 5 deletions
@@ -30,7 +30,11 @@ classes = (
operator.BIM_OT_aggregate_unassign_object,
operator.BIM_OT_break_link_to_other_aggregates,
operator.BIM_OT_select_linked_aggregates,
operator.BIM_OT_aggregate_mode_set_edit,
operator.BIM_OT_disable_aggregate_mode_set_edit,
prop.BIMObjectAggregateProperties,
prop.NotEditingObjects,
prop.BIMAggregateProperties,
ui.BIM_PT_aggregate,
ui.BIM_PT_linked_aggregate,
)
@@ -38,7 +42,9 @@ classes = (
def register():
bpy.types.Object.BIMObjectAggregateProperties = bpy.props.PointerProperty(type=prop.BIMObjectAggregateProperties)
bpy.types.Scene.BIMAggregateProperties = bpy.props.PointerProperty(type=prop.BIMAggregateProperties)
def unregister():
del bpy.types.Object.BIMObjectAggregateProperties
del bpy.types.Scene.BIMAggregateProperties
@@ -23,6 +23,8 @@ import ifcopenshell.util.element
import bonsai.tool as tool
import bonsai.core.aggregate as core
import bonsai.core.spatial
from bonsai.bim.ifc import IfcStore
from bonsai.bim.module.geometry.decorator import ItemDecorator
class BIM_OT_aggregate_assign_object(bpy.types.Operator, tool.Ifc.Operator):
@@ -369,3 +371,77 @@ class BIM_OT_select_linked_aggregates(bpy.types.Operator):
obj.select_set(True)
return {"FINISHED"}
class BIM_OT_aggregate_mode_set_edit(bpy.types.Operator, tool.Ifc.Operator):
bl_description = "Switch from Object to Aggregate Edit mode"
bl_idname = "bim.aggregate_mode_set_edit"
bl_label = "IFC Aggregate Mode Set Edit"
bl_options = {"REGISTER", "UNDO"}
def invoke(self, context, event):
return IfcStore.execute_ifc_operator(self, context, is_invoke=True)
def _invoke(self, context, event):
if not tool.Ifc.get():
return {"FINISHED"}
return self.execute(context)
def _execute(self, context):
active_object = context.active_object
props = context.scene.BIMAggregateProperties
if props.in_edit_mode == True:
BIM_OT_disable_aggregate_mode_set_edit._execute(self, context)
element = tool.Ifc.get_entity(active_object)
if not element:
return {"FINISHED"}
aggregate = ifcopenshell.util.element.get_aggregate(element)
parts = ifcopenshell.util.element.get_parts(element)
if not aggregate and not parts:
return {"FINISHED"}
if not parts:
parts = ifcopenshell.util.element.get_parts(aggregate)
if parts:
parts_objs = [tool.Ifc.get_object(part) for part in parts]
objs = []
visible_objects = tool.Raycast.get_visible_objects(context)
for obj in visible_objects:
if obj.visible_in_viewport_get(context.space_data):
objs.append(obj.original)
for obj in objs:
if obj.original not in parts_objs:
if not obj.data:
continue
obj.original.display_type = "BOUNDS"
data = ItemDecorator.get_obj_data(obj.original)
not_editing_obj = props.not_editing_objects.add()
not_editing_obj.obj = obj.original
props.in_edit_mode = True
return {"FINISHED"}
class BIM_OT_disable_aggregate_mode_set_edit(bpy.types.Operator, tool.Ifc.Operator):
bl_description = "Switch from Aggregate to Object Edit mode"
bl_idname = "bim.disable_aggregate_mode_set_edit"
bl_label = "IFC Disable Aggregate Mode Set Edit"
bl_options = {"REGISTER", "UNDO"}
def invoke(self, context, event):
return IfcStore.execute_ifc_operator(self, context, is_invoke=True)
def _invoke(self, context, event):
if not tool.Ifc.get():
return {"FINISHED"}
return self.execute(context)
def _execute(self, context):
props = context.scene.BIMAggregateProperties
objs = [o.obj for o in props.not_editing_objects]
for obj in objs:
obj.original.display_type = "TEXTURED"
element = tool.Ifc.get_entity(obj)
if not element:
continue
props.in_edit_mode = False
props.not_editing_objects.clear()
@@ -82,3 +82,11 @@ class BIMObjectAggregateProperties(PropertyGroup):
type=bpy.types.Object,
poll=poll_related_object,
)
class NotEditingObjects(bpy.types.PropertyGroup):
obj: PointerProperty(type=bpy.types.Object)
class BIMAggregateProperties(PropertyGroup):
in_edit_mode: BoolProperty(name="In Edit Mode")
not_editing_objects: CollectionProperty(type=NotEditingObjects)
@@ -50,6 +50,8 @@ classes = (
operator.OverrideMeshSeparate,
operator.OverrideModeSetEdit,
operator.OverrideModeSetObject,
operator.OverrideMoveAggregate,
operator.OverrideMoveAggregateMacro,
operator.OverrideOriginSet,
operator.OverrideOutlinerDelete,
operator.OverridePasteBuffer,
@@ -94,6 +96,8 @@ def register():
operator.OverrideDuplicateMoveLinkedMacro.define("TRANSFORM_OT_translate")
operator.DuplicateMoveLinkedAggregateMacro.define("BIM_OT_object_duplicate_move_linked_aggregate")
operator.DuplicateMoveLinkedAggregateMacro.define("TRANSFORM_OT_translate")
operator.OverrideMoveAggregateMacro.define("BIM_OT_override_move_aggregate")
operator.OverrideMoveAggregateMacro.define("TRANSFORM_OT_translate")
bpy.types.Object.BIMGeometryProperties = bpy.props.PointerProperty(type=prop.BIMObjectGeometryProperties)
bpy.types.Scene.BIMGeometryProperties = bpy.props.PointerProperty(type=prop.BIMGeometryProperties)
@@ -116,6 +120,8 @@ def register():
"bim.object_duplicate_move_linked_aggregate_macro", "D", "PRESS", ctrl=True, shift=True
)
addon_keymaps.append((km, kmi))
kmi = km.keymap_items.new("bim.override_move_aggregate_macro", "G", "PRESS")
addon_keymaps.append((km, kmi))
kmi = km.keymap_items.new("bim.override_paste_buffer", "V", "PRESS", ctrl=True)
addon_keymaps.append((km, kmi))
kmi = km.keymap_items.new("bim.override_mode_set_edit", "TAB", "PRESS")
@@ -2864,3 +2864,51 @@ class AddCurvelikeItem(bpy.types.Operator, tool.Ifc.Operator):
obj.data.BIMMeshProperties.ifc_definition_id = item.id()
tool.Geometry.import_item(obj)
tool.Geometry.import_item_attributes(obj)
class OverrideMoveAggregateMacro(bpy.types.Macro):
bl_idname = "bim.override_move_aggregate_macro"
bl_label = "IFC Move Aggregate"
bl_options = {"REGISTER", "UNDO"}
class OverrideMoveAggregate(bpy.types.Operator):
bl_idname = "bim.override_move_aggregate"
bl_label = "IFC Move Aggregate"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
return len(context.selected_objects) > 0
def execute(self, context):
# Deep magick from the dawn of time
if IfcStore.get_file():
IfcStore.execute_ifc_operator(self, context)
if self.new_active_obj:
context.view_layer.objects.active = self.new_active_obj
return {"FINISHED"}
return {"FINISHED"}
def _execute(self, context):
props = context.scene.BIMAggregateProperties
not_editing_objs = [o.obj for o in props.not_editing_objects]
aggregates_to_move = []
for obj in context.selected_objects:
self.new_active_obj = None
if obj in not_editing_objs:
obj.select_set(False)
print("foi")
break
element = tool.Ifc.get_entity(obj)
if not element or props.in_edit_mode:
continue
aggregate = ifcopenshell.util.element.get_aggregate(element)
if aggregate:
aggregates_to_move.append(tool.Ifc.get_object(aggregate))
obj.select_set(False)
aggregates_to_move = set(aggregates_to_move)
for obj in aggregates_to_move:
obj.select_set(True)
self.new_active_obj = obj
return {"FINISHED"}
+6 -5
View File
@@ -90,12 +90,13 @@ class Aggregate(bonsai.core.tool.Aggregate):
parts = ifcopenshell.util.element.get_parts(agg_element)
parts_objs = [tool.Ifc.get_object(p) for p in parts]
for obj in parts_objs:
if not obj.data:
continue
tool.Geometry.lock_object(obj)
constraint = next((c for c in obj.constraints if c.type == "CHILD_OF"), None)
if constraint:
obj.constraints.remove(constraint)
try:
bpy.context.view_layer.objects.active = obj
bpy.ops.constraint.apply(constraint="Child Of")
except:
pass
constraint = obj.constraints.new("CHILD_OF")
constraint.target = aggregate
@@ -103,5 +104,5 @@ class Aggregate(bonsai.core.tool.Aggregate):
def apply_constraints(cls, part: bpy.types.Object):
constraint = next((c for c in part.constraints if c.type == "CHILD_OF"), None)
if constraint:
bpy.context.view_layer.objects.active = part
bpy.ops.constraint.apply(constraint=constraint.name)
tool.Geometry.unlock_object(part)