"""
Module for the Region types.
"""
import abc
import typing
import numpy as np
from .. import config
from ..config import refs, types
from ..reporting import warn
from ._layout import Layout
if typing.TYPE_CHECKING:
from ..core import Scaffold
from .partition import Partition
[docs]
@config.dynamic(attr_name="type", required=False, default="group", auto_classmap=True)
class Region(abc.ABC):
"""
Base region.
When arranging will simply call arrange/layout on its children but won't cause any
changes itself.
"""
scaffold: "Scaffold"
name: str = config.attr(key=True)
children: list[typing.Union["Region", "Partition"]] = config.reflist(
refs.regional_ref, backref="region", required=True
)
@property
def data(self):
# The data property is read-only to users, but `_data` is assigned
# during the layout process
return self._data
[docs]
def get_dependencies(self):
return self.children.copy()
def __boot__(self):
pass
[docs]
def get_layout(self, hint):
layouts = [dep.get_layout(hint) for dep in self.get_dependencies()]
return Layout(hint.data.copy(), owner=self, children=layouts)
[docs]
def do_layout(self, hint):
layout = self.get_layout(hint)
layout.accept()
[docs]
@abc.abstractmethod
def rotate(self, rotation): # pragma: nocover
pass
[docs]
@abc.abstractmethod
def translate(self, offset): # pragma: nocover
pass
[docs]
@abc.abstractmethod
def scale(self, factors): # pragma: nocover
pass
[docs]
@config.node
class RegionGroup(Region, classmap_entry="group"):
[docs]
def rotate(self, rotation):
for child in self.children:
child.rotate(rotation)
[docs]
def translate(self, offset):
for child in self.children:
child.translate(offset)
[docs]
def scale(self, factors):
for child in self.children:
child.scale(factors)
[docs]
@config.node
class Stack(RegionGroup, classmap_entry="stack"):
"""
Stack components on top of each other based on their ``stack_index`` and adjust its
own height accordingly.
"""
axis: typing.Union[typing.Literal["x"], typing.Literal["y"], typing.Literal["z"]] = (
config.attr(type=types.in_(["x", "y", "z"]), default="z")
)
[docs]
def get_layout(self, hint):
layout = super().get_layout(hint)
stack_size = 0
axis_idx = ("x", "y", "z").index(self.axis)
trans_eye = np.zeros(3)
trans_eye[axis_idx] = 1
for child in layout.children:
if child.data is None:
warn(f"Skipped layout arrangement of {child._owner.name} in {self.name}")
continue
translation = (
layout.data.ldc[axis_idx] + stack_size - child.data.ldc
) * trans_eye
if not np.allclose(0, translation):
child.propose_translate(translation)
stack_size += child.data.dimensions[axis_idx]
ldc = layout.data.ldc
mdc = layout.data.mdc
mdc[axis_idx] = ldc[axis_idx] + stack_size
return layout
[docs]
def rotate(self, rotation):
for child in self.children:
child.rotate(rotation)
[docs]
def translate(self, offset):
for child in self.children:
child.translate(offset)
[docs]
def scale(self, factors):
for child in self.children:
child.scale(factors)
__all__ = ["Region", "RegionGroup", "Stack"]