Decomposition mode now selects all children (not parenting) to move a host and all children easily

This commit is contained in:
Dion Moult
2022-10-08 23:42:03 +11:00
parent 37028857b4
commit 4110b81270
5 changed files with 41 additions and 56 deletions
@@ -265,10 +265,12 @@ class BimTool(WorkSpaceTool):
row.label(text="Mode")
row = layout.row(align=True)
row.label(text="", icon="EVENT_ALT")
row.label(text="Opening", icon="EVENT_O")
row.label(text="", icon="EVENT_O")
row.operator("bim.hotkey", text="Void").hotkey = "A_O"
row = layout.row(align=True)
row.label(text="", icon="EVENT_ALT")
row.label(text="Decomposition", icon="EVENT_D")
row.label(text="", icon="EVENT_D")
row.operator("bim.hotkey", text="Decomposition").hotkey = "A_D"
class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
@@ -401,7 +403,7 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
self.props.z = self.z
def hotkey_A_D(self):
bpy.ops.bim.toggle_decomposition_parenting()
bpy.ops.bim.select_decomposition()
def hotkey_A_O(self):
if AuthoringData.data["has_visible_openings"]:
@@ -20,11 +20,11 @@ import bpy
from . import ui, prop, operator
classes = (
operator.AddOpening,
operator.RemoveOpening,
operator.AddFilling,
operator.AddOpening,
operator.RemoveFilling,
operator.ToggleDecompositionParenting,
operator.RemoveOpening,
operator.SelectDecomposition,
prop.VoidProperties,
ui.BIM_PT_voids,
ui.BIM_PT_booleans,
@@ -182,55 +182,18 @@ class RemoveFilling(bpy.types.Operator):
return {"FINISHED"}
class ToggleDecompositionParenting(bpy.types.Operator):
bl_idname = "bim.toggle_decomposition_parenting"
bl_label = "Toggle Decomposition Parenting"
class SelectDecomposition(bpy.types.Operator):
bl_idname = "bim.select_decomposition"
bl_label = "Select Decomposition"
bl_options = {"REGISTER", "UNDO"}
obj: bpy.props.StringProperty()
def execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
self.file = IfcStore.get_file()
is_parenting = None
self.decompositions = {}
self.load_decompositions(obj)
for parent, children in self.decompositions.items():
bpy.ops.bim.dynamically_void_product(obj=parent.name)
for child in children:
bpy.ops.bim.dynamically_void_product(obj=child.name)
if is_parenting is None:
is_parenting = not bool(child.parent)
if is_parenting:
child.parent = parent
child.matrix_parent_inverse = parent.matrix_world.inverted()
else:
parent_matrix_world = child.matrix_world.copy()
child.parent = None
child.matrix_world = parent_matrix_world
for obj in context.selected_objects:
element = tool.Ifc.get_entity(obj)
if not element:
continue
for subelement in ifcopenshell.util.element.get_decomposition(element):
subobj = tool.Ifc.get_object(subelement)
if subobj:
subobj.select_set(True)
return {"FINISHED"}
def load_decompositions(self, parent_obj):
element = self.file.by_id(parent_obj.BIMObjectProperties.ifc_definition_id)
for rel in self.file.get_inverse(element):
if rel.is_a("IfcRelDecomposes"):
if rel[4] != element:
continue
if isinstance(rel[5], tuple):
for related_object in rel[5]:
self.add_decomposition(parent_obj, related_object)
else:
self.add_decomposition(parent_obj, rel[5])
elif rel.is_a("IfcRelFillsElement"):
if rel.RelatingOpeningElement != element:
continue
self.add_decomposition(parent_obj, rel.RelatedBuildingElement)
def add_decomposition(self, parent_obj, child_element):
child_obj = IfcStore.get_element(child_element.id())
if child_obj:
self.decompositions.setdefault(parent_obj, []).append(child_obj)
self.load_decompositions(child_obj)
@@ -323,7 +323,9 @@ def get_container(element, should_get_direct=False):
def get_referenced_structures(element):
"""
Retreives a list of referenced structural elements
Retreives a list of referenced spatial elements, typically useful for
multistorey elements or elements that span multiple spaces or in-between
spaces.
:param element: The IFC element
:type element: ifcopenshell.entity_instance.entity_instance
@@ -340,7 +342,9 @@ def get_referenced_structures(element):
def get_decomposition(element):
"""
Retrieves the decomposition of an element.
Retrieves all subelements of an element based on the spatial decomposition
hierarchy. This includes all subspaces and elements contained in subspaces,
parts of an aggreate, all openings, and all fills of any openings.
:param element: The IFC element
:return: The decomposition of the element
@@ -360,6 +364,12 @@ def get_decomposition(element):
for rel in getattr(element, "IsDecomposedBy", []):
queue.extend(rel.RelatedObjects)
results.extend(rel.RelatedObjects)
for rel in getattr(element, "HasOpenings", []):
queue.append(rel.RelatedOpeningElement)
results.append(rel.RelatedOpeningElement)
for rel in getattr(element, "HasFillings", []):
queue.append(rel.RelatedBuildingElement)
results.append(rel.RelatedBuildingElement)
return results
@@ -561,6 +561,16 @@ class TestGetDecompositionIFC4(test.bootstrap.IFC4):
assert element in results
assert subelement in results
def test_getting_openings_and_fills_of_an_element(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcOpeningElement")
subsubelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWindow")
ifcopenshell.api.run("void.add_opening", self.file, element=element, opening=subelement)
ifcopenshell.api.run("void.add_filling", self.file, element=subsubelement, opening=subelement)
results = subject.get_decomposition(element)
assert subelement in results
assert subsubelement in results
class TestGetAggregateIFC4(test.bootstrap.IFC4):
def test_getting_the_containing_aggregate_of_a_subelement(self):