Files
automate-function-bilt-work…/Utilities/helpers.py
T
Jonathon Broughton df589bf9c2 Better flatten
2024-06-21 16:01:07 +01:00

120 lines
4.6 KiB
Python

"""Helper module for a speckle object tree flattening."""
from collections.abc import Iterable
from typing import Optional, Tuple, List
from specklepy.objects import Base
from specklepy.objects.other import Instance, Transform
def speckle_print(log_string: str = "banana") -> None:
print("\033[92m" + str(log_string) + "\033[0m")
def flatten_base(base: Base) -> Iterable[Base]:
"""Flatten a base object into an iterable of bases."""
elements = getattr(base, "elements", getattr(base, "@elements", None))
if elements is not None:
for element in elements:
yield from flatten_base(element)
yield base
def flatten_base_thorough(base: Base, parent_type: str = None) -> Iterable[Base]:
"""Take a base and flatten it to an iterable of bases.
Args:
base: The base object to flatten.
parent_type: The type of the parent object, if any.
Yields:
Base: A flattened base object.
"""
if isinstance(base, Base):
base["parent_type"] = parent_type
elements = getattr(base, "elements", getattr(base, "@elements", None))
if elements:
try:
for element in elements:
# Recursively yield flattened elements of the child
yield from flatten_base_thorough(element, base.speckle_type)
except KeyError:
pass
elif hasattr(base, "@Lines"):
categories = base.get_dynamic_member_names()
# could be old revit
try:
for category in categories:
print(category)
if category.startswith("@"):
category_object: Base = getattr(base, category)[0]
yield from flatten_base_thorough(
category_object, category_object.speckle_type
)
except KeyError:
pass
yield base
def extract_base_and_transform(
base: Base,
inherited_instance_id: Optional[str] = None,
transform_list: Optional[List[Transform]] = None,
) -> Tuple[Base, str, Optional[List[Transform]]]:
"""
Traverses Speckle object hierarchies to yield `Base` objects and their transformations.
Tailored to Speckle's AEC data structures, it covers the newer hierarchical structures
with Collections and also with patterns found in older Revit specific data.
Parameters:
- base (Base): The starting point `Base` object for traversal.
- inherited_instance_id (str, optional): The inherited identifier for `Base` objects without a unique ID.
- transform_list (List[Transform], optional): Accumulated list of transformations from parent to child objects.
Yields:
- tuple: A `Base` object, its identifier, and a list of applicable `Transform` objects or None.
The id of the `Base` object is either the inherited identifier for a definition from an instance
or the one defined in the object.
"""
# Derive the identifier for the current `Base` object, defaulting to an inherited one if needed.
current_id = getattr(base, "id", inherited_instance_id)
transform_list = transform_list or []
if isinstance(base, Instance):
# Append transformation data and dive into the definition of `Instance` objects.
if base.transform:
transform_list.append(base.transform)
if base.definition:
yield from extract_base_and_transform(
base.definition, current_id, transform_list.copy()
)
else:
# Initial yield for the current `Base` object.
yield base, current_id, transform_list
# Process 'elements' and '@elements', typical containers for `Base` objects in AEC models.
elements_attr = getattr(base, "elements", []) or getattr(base, "@elements", [])
for element in elements_attr:
if isinstance(element, Base):
# Recurse into each `Base` object within 'elements' or '@elements'.
yield from extract_base_and_transform(
element, current_id, transform_list.copy()
)
# Recursively process '@'-prefixed properties that are Base objects with 'elements'.
# This is a common pattern in older Speckle data models, such as those used for Revit commits.
for attr_name in dir(base):
if attr_name.startswith("@"):
attr_value = getattr(base, attr_name)
# If the attribute is a Base object containing 'elements', recurse into it.
if isinstance(attr_value, Base) and hasattr(attr_value, "elements"):
yield from extract_base_and_transform(
attr_value, current_id, transform_list.copy()
)