mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 09:21:46 +00:00
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:
committed by
Bruno Perdigão
parent
6f11f74aac
commit
3dcded819f
@@ -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"}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user