Implement recursive toggling for elements in spatial panel

This commit is contained in:
Dion Moult
2024-08-29 21:57:45 +10:00
parent f3ab214752
commit 32b37b8823
3 changed files with 59 additions and 16 deletions
@@ -247,10 +247,17 @@ class ToggleContainerElement(bpy.types.Operator):
bl_idname = "bim.toggle_container_element" bl_idname = "bim.toggle_container_element"
bl_label = "Toggle Container Element" bl_label = "Toggle Container Element"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
bl_description = "Toggle children\nALT+CLICK to recursively toggle children"
element_index: bpy.props.IntProperty() element_index: bpy.props.IntProperty()
is_recursive: bpy.props.BoolProperty(name="Is Recursive", default=False, options={"SKIP_SAVE"})
def invoke(self, context, event):
if event.type == "LEFTMOUSE" and event.alt:
self.is_recursive = True
return self.execute(context)
def execute(self, context): def execute(self, context):
core.toggle_container_element(tool.Spatial, element_index=self.element_index) core.toggle_container_element(tool.Spatial, element_index=self.element_index, is_recursive=self.is_recursive)
return {"FINISHED"} return {"FINISHED"}
+2 -2
View File
@@ -141,8 +141,8 @@ def delete_container(
spatial.import_spatial_decomposition() spatial.import_spatial_decomposition()
def toggle_container_element(spatial: tool.Spatial, element_index: int) -> None: def toggle_container_element(spatial: tool.Spatial, element_index: int, is_recursive: bool) -> None:
spatial.toggle_container_element(element_index) spatial.toggle_container_element(element_index, is_recursive=is_recursive)
spatial.load_contained_elements() spatial.load_contained_elements()
+49 -13
View File
@@ -199,14 +199,8 @@ class Spatial(bonsai.core.tool.Spatial):
src_obj.location.xy = destination_obj.location.xy src_obj.location.xy = destination_obj.location.xy
@classmethod @classmethod
def load_contained_elements(cls) -> None: def get_grouped_elements_in_container(cls, container: ifcopenshell.entity_instance) -> dict:
props = bpy.context.scene.BIMSpatialDecompositionProperties props = bpy.context.scene.BIMSpatialDecompositionProperties
props.elements.clear()
if not (container := props.active_container):
return
container = tool.Ifc.get().by_id(container.ifc_definition_id)
results: defaultdict[str, dict[int, Any]] = defaultdict(dict) results: defaultdict[str, dict[int, Any]] = defaultdict(dict)
if props.should_include_children: if props.should_include_children:
elements = ifcopenshell.util.element.get_decomposition(container, is_recursive=True) elements = ifcopenshell.util.element.get_decomposition(container, is_recursive=True)
@@ -222,25 +216,39 @@ class Spatial(bonsai.core.tool.Spatial):
class_data = results.setdefault(ifc_class, {}) class_data = results.setdefault(ifc_class, {})
type_data = class_data.setdefault(ifc_definition_id, {"type_name": type_name, "elements": []}) type_data = class_data.setdefault(ifc_definition_id, {"type_name": type_name, "elements": []})
type_data["elements"].append(element) type_data["elements"].append(element)
return results
@classmethod
def load_contained_elements(cls) -> None:
props = bpy.context.scene.BIMSpatialDecompositionProperties
props.elements.clear()
if not (container := props.active_container):
return
container = tool.Ifc.get().by_id(container.ifc_definition_id)
results = cls.get_grouped_elements_in_container(container)
expanded_elements = json.loads(props.expanded_elements) expanded_elements = json.loads(props.expanded_elements)
expanded_classes = expanded_elements.get("CLASS", []) expanded_classes = expanded_elements.get("CLASS", [])
expanded_ifc_ids = expanded_elements.get("IFC_ID", []) expanded_ifc_ids = expanded_elements.get("IFC_ID", [])
expanded_untyped = expanded_elements.get("UNTYPED_CLASSES", []) expanded_untyped = expanded_elements.get("UNTYPED_CLASSES", [])
expanded_classes_r = expanded_elements.get("CLASS_R", [])
total_elements = 0 total_elements = 0
for ifc_class in sorted(results.keys()): for ifc_class in sorted(results.keys()):
new = props.elements.add() new = props.elements.add()
new.name = ifc_class new.name = ifc_class
new.type = "CLASS" new.type = "CLASS"
class_is_expanded = ifc_class in expanded_classes class_is_expanded = ifc_class in expanded_classes
new.is_expanded = class_is_expanded class_is_expanded_r = ifc_class in expanded_classes_r
new.is_expanded = class_is_expanded or class_is_expanded_r
total = 0 total = 0
for ifc_definition_id in sorted( for ifc_definition_id in sorted(
results[ifc_class].keys(), key=lambda x: results[ifc_class][x]["type_name"] results[ifc_class].keys(), key=lambda x: results[ifc_class][x]["type_name"]
): ):
type_data = results[ifc_class][ifc_definition_id] type_data = results[ifc_class][ifc_definition_id]
total2 = len(type_data["elements"]) total2 = len(type_data["elements"])
if class_is_expanded: if new.is_expanded:
new2 = props.elements.add() new2 = props.elements.add()
new2.type = "TYPE" new2.type = "TYPE"
new2.name = type_data["type_name"] new2.name = type_data["type_name"]
@@ -251,9 +259,9 @@ class Spatial(bonsai.core.tool.Spatial):
type_is_expanded = ifc_class in expanded_untyped type_is_expanded = ifc_class in expanded_untyped
else: else:
type_is_expanded = ifc_definition_id in expanded_ifc_ids type_is_expanded = ifc_definition_id in expanded_ifc_ids
new2.is_expanded = type_is_expanded new2.is_expanded = type_is_expanded or class_is_expanded_r
if type_is_expanded: if new2.is_expanded:
for element in type_data["elements"]: for element in type_data["elements"]:
occurrence = props.elements.add() occurrence = props.elements.add()
occurrence.name = element.Name or "Unnamed" occurrence.name = element.Name or "Unnamed"
@@ -363,25 +371,53 @@ class Spatial(bonsai.core.tool.Spatial):
props.contracted_containers = json.dumps(contracted_containers) props.contracted_containers = json.dumps(contracted_containers)
@classmethod @classmethod
def toggle_container_element(cls, element_index: int) -> None: def toggle_container_element(cls, element_index: int, is_recursive: bool) -> None:
props = bpy.context.scene.BIMSpatialDecompositionProperties props = bpy.context.scene.BIMSpatialDecompositionProperties
expanded_elements: dict[str, list[Union[str, int]]] = json.loads(props.expanded_elements) expanded_elements: dict[str, list[Union[str, int]]] = json.loads(props.expanded_elements)
element = props.elements[element_index] element = props.elements[element_index]
if element.type == "CLASS": if element.type == "CLASS":
element_type = "CLASS" element_type = "CLASS"
filtered_item = element.name filtered_item = element.name
else: elif element.type == "TYPE":
if element.ifc_definition_id == 0: if element.ifc_definition_id == 0:
element_type = "UNTYPED_CLASSES" element_type = "UNTYPED_CLASSES"
filtered_item = element.ifc_class filtered_item = element.ifc_class
else: else:
element_type = "IFC_ID" element_type = "IFC_ID"
filtered_item = element.ifc_definition_id filtered_item = element.ifc_definition_id
else:
return
expanded_elements_list: list[Union[str, int]] = expanded_elements.setdefault(element_type, []) expanded_elements_list: list[Union[str, int]] = expanded_elements.setdefault(element_type, [])
if filtered_item in expanded_elements_list: if filtered_item in expanded_elements_list:
expanded_elements_list.remove(filtered_item) expanded_elements_list.remove(filtered_item)
should_expand = False
else: else:
expanded_elements_list.append(filtered_item) expanded_elements_list.append(filtered_item)
should_expand = True
if is_recursive and element.type == "CLASS":
container = tool.Ifc.get().by_id(props.active_container.ifc_definition_id)
results = cls.get_grouped_elements_in_container(container)
for ifc_class in results.keys():
if ifc_class != element.name:
continue
for ifc_definition_id in results[ifc_class].keys():
if ifc_definition_id == 0:
element_type = "UNTYPED_CLASSES"
filtered_item = ifc_class
else:
element_type = "IFC_ID"
filtered_item = ifc_definition_id
expanded_elements_list = expanded_elements.setdefault(element_type, [])
if should_expand is False and filtered_item in expanded_elements_list:
expanded_elements_list.remove(filtered_item)
elif should_expand is True and filtered_item not in expanded_elements_list:
expanded_elements_list.append(filtered_item)
props.expanded_elements = json.dumps(expanded_elements) props.expanded_elements = json.dumps(expanded_elements)
# HERE STARTS SPATIAL TOOL # HERE STARTS SPATIAL TOOL