Source code for nndt.space2.abstracts

import re
from abc import abstractmethod
from functools import wraps
from typing import Optional, Union

from anytree import NodeMixin, PostOrderIter, Resolver
from colorama import Fore

FORBIDDEN_NAME = [
    "separator",
    "parent",
    "__check_loop",
    "__detach",
    "__attach",
    "__children_or_empty",
    "children",
    "__check_children",
    "children_setter",
    "children_deleter",
    "_pre_detach_children",
    "_post_detach_children",
    "_pre_attach_children",
    "_post_attach_children",
    "path",
    "iter_path_reverse",
    "_path",
    "ancestors",
    "anchestors",
    "descendants",
    "root",
    "siblings",
    "leaves",
    "is_leaf",
    "is_root",
    "height",
    "depth",
    "_pre_detach",
    "_post_detach",
    "_pre_attach",
    "_post_attach",
] + ["name", "parent", "_print_color", "_nodetype"]

NODE_METHOD_DICT = {}

DICT_NODETYPE_PRIORITY = {
    "S": 100,
    "G": 90,
    "O3D": 80,
    "IR": 70,
    "FS": 60,
    "TR": 50,
    "MS": 40,
    "M": 30,
}


[docs]def node_method(helpstr=None): """ This decorator mark method for attachments to the space model tree :param helpstr: Short information about a performed transformation of data :return: wrapper """ def decorator_wrapper(fn): classname = str(fn.__qualname__).split(".")[0] if classname not in NODE_METHOD_DICT: NODE_METHOD_DICT[classname] = {} NODE_METHOD_DICT[classname][str(fn.__name__)] = helpstr @wraps(fn) def wrapper(*args, **kwargs): return fn(*args, **kwargs) return wrapper return decorator_wrapper
def _name_to_safename(name: str) -> str: safe_name = re.sub("\W|^(?=\d)", "_", name) if safe_name in FORBIDDEN_NAME: safe_name = safe_name + "_" if safe_name in FORBIDDEN_NAME: safe_name = safe_name + "_" if safe_name in FORBIDDEN_NAME: raise ValueError(f"{name} cannot be safely renamed.") return safe_name
[docs]class AbstractTreeElement(NodeMixin): """ Abstract element of the space model tree """
[docs] def __init__( self, name: str, _print_color: str = Fore.RESET, _nodetype: str = "UNDEFINED", parent=None, ): """ Create an abstract element of space model tree :param name: name of the tree node :param _print_color: color of this node for print() :param _nodetype: type of the code in form of string literal :param parent: parent node """ super(NodeMixin, self).__init__() self.name = _name_to_safename(name) self.parent = parent self._print_color = _print_color self._nodetype = _nodetype self._resolver = Resolver("name")
def _container_only_list(self): return [ch for ch in self.children if isinstance(ch, IterAccessMixin)] def __len__(self): return len(self._container_only_list()) def __iter__(self): return iter(self._container_only_list()) def __getitem__(self, request_: Union[int, str]): if isinstance(request_, int): children_without_methods = self._container_only_list() return children_without_methods[request_] elif isinstance(request_, str): return self._resolver.get(self, request_) else: raise NotImplementedError() def __repr__(self): return self._print_color + f"{self._nodetype}:{self.name}" + Fore.RESET def _post_attach(self, parent): if isinstance(self, AbstractBBoxNode): if parent is not None: setattr(parent, self.name, self) def _post_detach(self, parent): if parent is not None: if hasattr(parent, self.name): delattr(parent, self.name)
[docs]class AbstractBBoxNode(AbstractTreeElement): """ Element of the space model tree with a boundary box """
[docs] def __init__( self, name: str, bbox=((0.0, 0.0, 0.0), (0.0, 0.0, 0.0)), _print_color=Fore.RESET, _nodetype="UNDEFINED", parent=None, ): """ Create an element of the space model tree with a boundary box :param name: name of the tree node :param bbox: boundary box in form ((X_min, Y_min, Z_min), (X_max, Y_max, Z_max)) :param parent: parent node """ super(AbstractBBoxNode, self).__init__( name, _print_color=_print_color, _nodetype=_nodetype, parent=parent ) self.bbox = bbox
def __repr__(self): return ( self._print_color + f"{self._nodetype}:{self.name}" + Fore.WHITE + f" {self._print_bbox()}" + Fore.RESET ) def _print_bbox(self): a = self.bbox return f"(({a[0][0]:.02f}, {a[0][1]:.02f}, {a[0][2]:.02f}), ({a[1][0]:.02f}, {a[1][1]:.02f}, {a[1][2]:.02f}))"
[docs] @node_method("print(default|source|full)") def print(self, mode: Optional[str] = "default"): """ Print tree view for this element and all children's elements Args: mode (Optional[str], optional): "default" print 3D objects and methods; "source" prints only space, groups, and file sources; "full" prints all nodes of the tree; Defaults to "default". Returns: str: tree representation string """ from nndt.space2.print_tree import _pretty_print return _pretty_print(self, mode)
[docs] @node_method("plot(default, filepath=None)") def plot( self, mode: Optional[str] = "default", filepath: Optional[str] = None, **kwargs ): """ Iterate over all Object3D in the tree and show the plot if the filepath is None. Save the plot in a file if the filepath is None. Args: mode (Optional[str], optional): Only the "default" mode is supported yet. Defaults to "default". filepath (Optional[str], optional): File name. If it exists save there otherwise show it. Defaults to None. """ from nndt.space2.plot_tree import _plot _plot(self, mode, filepath, **kwargs)
[docs] @node_method("unload_from_memory()") def unload_from_memory(self): from nndt.space2.filesource import FileSource for node in PostOrderIter(self): if isinstance(node, FileSource) and node._loader is not None: node._loader.unload_data()
[docs]class IterAccessMixin: pass
[docs]class AbstractLoader:
[docs] def calc_bbox(self) -> ((float, float, float), (float, float, float)): """Return the boundary box size of a 3D object. Returns: (tuple), (tuple): boundary box: (Xmin, Xmax, Ymin), (Ymax, Zmin, Zmax) """ return (0.0, 0.0, 0.0), (0.0, 0.0, 0.0)
[docs] @abstractmethod def load_data(self): """Load data to memory""" pass
[docs] @abstractmethod def unload_data(self): """Remove references to the uploaded data. This method NO NOT call the garbage collectors""" pass
[docs] @abstractmethod def is_load(self) -> bool: """Check if the data is already loaded""" pass