mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-08 17:01:40 +00:00
See #6537. Improve how aggregate mode behaves when dealing with multiple levels of aggregates.
This commit is contained in:
@@ -386,7 +386,7 @@ class BIM_OT_disable_aggregate_mode(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
bonsai.core.aggregate.disable_aggregate_mode(tool.Aggregate)
|
||||
bonsai.core.aggregate.exit_aggregate_mode(tool.Aggregate)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
@@ -119,6 +119,7 @@ class Objects(bpy.types.PropertyGroup):
|
||||
class BIMAggregateProperties(PropertyGroup):
|
||||
in_aggregate_mode: BoolProperty(name="In Edit Mode", update=update_aggregate_mode_decorator)
|
||||
editing_aggregate: PointerProperty(name="Editing Aggregate", type=bpy.types.Object)
|
||||
previous_editing_aggregate: PointerProperty(name="Editing Aggregate", type=bpy.types.Object)
|
||||
editing_objects: CollectionProperty(type=Objects)
|
||||
not_editing_objects: CollectionProperty(type=Objects)
|
||||
aggregate_decorator: BoolProperty(
|
||||
@@ -126,10 +127,16 @@ class BIMAggregateProperties(PropertyGroup):
|
||||
default=False,
|
||||
update=update_aggregate_decorator,
|
||||
)
|
||||
previous_state: BoolProperty(
|
||||
name="True if it was previously in aggregate mode",
|
||||
default=False,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
in_aggregate_mode: bool
|
||||
editing_aggregate: Union[bpy.types.Object, None]
|
||||
previous_editing_aggregate: Union[bpy.types.Object, None]
|
||||
editing_objects: bpy.types.bpy_prop_collection_idprop[Objects]
|
||||
not_editing_objects: bpy.types.bpy_prop_collection_idprop[Objects]
|
||||
aggregate_decorator: bool
|
||||
previous_state: bool
|
||||
|
||||
@@ -1957,9 +1957,8 @@ class OverrideModeSetEdit(bpy.types.Operator, tool.Ifc.Operator):
|
||||
nprops = tool.Nest.get_nest_props()
|
||||
|
||||
if self.has_aggregates(selected_objs):
|
||||
if not aprops.in_aggregate_mode:
|
||||
bonsai.core.aggregate.enable_aggregate_mode(tool.Aggregate, context.active_object)
|
||||
return {"FINISHED"}
|
||||
bonsai.core.aggregate.enter_aggregate_mode(tool.Aggregate, context.active_object)
|
||||
return {"FINISHED"}
|
||||
|
||||
if self.has_nests(selected_objs):
|
||||
if not nprops.in_nest_mode:
|
||||
@@ -1970,7 +1969,11 @@ class OverrideModeSetEdit(bpy.types.Operator, tool.Ifc.Operator):
|
||||
self.handle_single_object(context, context.active_object)
|
||||
elif len(selected_objs) == 0:
|
||||
if aprops.in_aggregate_mode:
|
||||
bonsai.core.aggregate.disable_aggregate_mode(tool.Aggregate)
|
||||
gprops = tool.Geometry.get_geometry_props()
|
||||
if gprops.representation_obj:
|
||||
tool.Geometry.disable_item_mode()
|
||||
else:
|
||||
bonsai.core.aggregate.exit_aggregate_mode(tool.Aggregate)
|
||||
return {"FINISHED"}
|
||||
if nprops.in_nest_mode:
|
||||
bonsai.core.nest.disable_nest_mode(tool.Nest)
|
||||
@@ -2105,6 +2108,10 @@ class OverrideModeSetEdit(bpy.types.Operator, tool.Ifc.Operator):
|
||||
props = tool.Aggregate.get_aggregate_props()
|
||||
if (aggregate or parts) and not props.in_aggregate_mode:
|
||||
return True
|
||||
elif element != tool.Ifc.get_entity(props.editing_aggregate) and aggregate != tool.Ifc.get_entity(props.editing_aggregate):
|
||||
return True
|
||||
elif parts and aggregate == tool.Ifc.get_entity(props.editing_aggregate):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@@ -3291,10 +3298,12 @@ class OverrideMove(bpy.types.Operator):
|
||||
parts = ifcopenshell.util.element.get_parts(element)
|
||||
if parts:
|
||||
aggregates_to_move.append(tool.Ifc.get_object(element))
|
||||
continue
|
||||
if not parts and props.in_aggregate_mode:
|
||||
aggregates_to_move.extend(list(tool.Aggregate.get_parts_recursively(element)))
|
||||
|
||||
continue
|
||||
aggregate = ifcopenshell.util.element.get_aggregate(element)
|
||||
if not parts and props.in_aggregate_mode and aggregate == tool.Ifc.get_entity(props.editing_aggregate):
|
||||
continue
|
||||
if aggregate:
|
||||
aggregates_to_move.append(tool.Ifc.get_object(aggregate))
|
||||
obj.select_set(False)
|
||||
|
||||
@@ -87,18 +87,24 @@ def add_part_to_object(
|
||||
blender.set_active_object(obj)
|
||||
|
||||
|
||||
def enable_aggregate_mode(
|
||||
def enter_aggregate_mode(
|
||||
aggregator: tool.Aggregate,
|
||||
obj: bpy.types.Object,
|
||||
):
|
||||
if aggregator.get_aggregate_mode():
|
||||
disable_aggregate_mode(aggregator)
|
||||
aggregator.update_previous_aggregate_mode_state()
|
||||
if aggregator.get_higher_aggregate():
|
||||
aggregator.disable_aggregate_mode()
|
||||
|
||||
aggregator.enable_aggregate_mode(obj)
|
||||
|
||||
|
||||
def disable_aggregate_mode(aggregator: tool.Aggregate):
|
||||
aggregator.disable_aggregate_mode()
|
||||
def exit_aggregate_mode(aggregator: tool.Aggregate):
|
||||
aggregator.update_previous_aggregate_mode_state()
|
||||
if new_obj := aggregator.get_higher_aggregate():
|
||||
aggregator.disable_aggregate_mode()
|
||||
aggregator.enable_aggregate_mode(new_obj)
|
||||
else:
|
||||
aggregator.disable_aggregate_mode()
|
||||
|
||||
|
||||
class IncompatibleAggregateError(Exception):
|
||||
|
||||
@@ -88,6 +88,20 @@ class Aggregate(bonsai.core.tool.Aggregate):
|
||||
if rel.is_a("IfcRelAggregates"):
|
||||
return rel.RelatingObject
|
||||
|
||||
@classmethod
|
||||
def get_aggregates_recursively(cls, element: ifcopenshell.entity_instance) -> set[ifcopenshell.entity_instance]:
|
||||
"""Get elements aggregates recursively, resulting set includes `element`."""
|
||||
aggregates = list()
|
||||
queue = {element}
|
||||
while queue:
|
||||
element = queue.pop()
|
||||
aggregate = ifcopenshell.util.element.get_aggregate(element)
|
||||
if aggregate:
|
||||
queue.update({aggregate})
|
||||
if ifcopenshell.util.element.get_parts(element):
|
||||
aggregates.append(element)
|
||||
return aggregates
|
||||
|
||||
@classmethod
|
||||
def get_parts_recursively(cls, element: ifcopenshell.entity_instance) -> set[ifcopenshell.entity_instance]:
|
||||
"""Get elements parts recursively, resulting set includes `element`."""
|
||||
@@ -100,9 +114,17 @@ class Aggregate(bonsai.core.tool.Aggregate):
|
||||
return parts
|
||||
|
||||
@classmethod
|
||||
def get_aggregate_mode(cls) -> bool:
|
||||
def get_higher_aggregate(cls) -> ifcopenshell.entity_instance:
|
||||
props = cls.get_aggregate_props()
|
||||
return props.in_aggregate_mode
|
||||
editing_aggregate = tool.Ifc.get_entity(props.editing_aggregate)
|
||||
higher_aggregate = ifcopenshell.util.element.get_aggregate(editing_aggregate)
|
||||
return tool.Ifc.get_object(higher_aggregate) if higher_aggregate else None
|
||||
|
||||
@classmethod
|
||||
def update_previous_aggregate_mode_state(cls):
|
||||
props = cls.get_aggregate_props()
|
||||
props.previous_state = props.in_aggregate_mode
|
||||
props.previous_editing_aggregate = props.editing_aggregate
|
||||
|
||||
@classmethod
|
||||
def enable_aggregate_mode(cls, active_object: bpy.types.Object) -> set[Literal["FINISHED"]]:
|
||||
@@ -112,12 +134,24 @@ class Aggregate(bonsai.core.tool.Aggregate):
|
||||
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)
|
||||
# Defines the aggregate based on previous state
|
||||
# Controls whether the user is entering a deeper level or
|
||||
# exiting to a higher level. See core/aggregate.py
|
||||
aggregates = tool.Aggregate.get_aggregates_recursively(element)
|
||||
aggregate = aggregates[-1]
|
||||
if props.previous_state:
|
||||
previous_aggregate = tool.Ifc.get_entity(props.previous_editing_aggregate)
|
||||
if previous_aggregate in aggregates:
|
||||
reference_index = aggregates.index(previous_aggregate)
|
||||
aggregate = aggregates[reference_index - 1]
|
||||
else:
|
||||
aggregate = aggregates[0]
|
||||
|
||||
parts = tool.Aggregate.get_parts_recursively(element)
|
||||
if not aggregate and not parts:
|
||||
return {"FINISHED"}
|
||||
if not parts:
|
||||
parts = ifcopenshell.util.element.get_parts(aggregate)
|
||||
parts = tool.Aggregate.get_parts_recursively(aggregate)
|
||||
if parts:
|
||||
props.editing_aggregate = tool.Ifc.get_object(aggregate) if aggregate else tool.Ifc.get_object(element)
|
||||
parts_objs = [tool.Ifc.get_object(part) for part in parts]
|
||||
@@ -157,7 +191,6 @@ class Aggregate(bonsai.core.tool.Aggregate):
|
||||
if not element:
|
||||
continue
|
||||
|
||||
parts = ifcopenshell.util.element.get_parts(tool.Ifc.get_entity(props.editing_aggregate))
|
||||
if context.space_data.local_view:
|
||||
bpy.ops.view3d.localview()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user