Source code for compas_rhino.utilities.layers


from __future__ import print_function
from __future__ import absolute_import
from __future__ import division

from collections import deque

import rhinoscriptsyntax as rs
import scriptcontext as sc

find_object = sc.doc.Objects.Find

try:
    purge_object = sc.doc.Objects.Purge
except AttributeError:
    purge_object = None


__all__ = [
    'create_layers_from_path',
    'create_layers_from_paths',
    'create_layers_from_dict',
    'create_layers',
    'clear_layer',
    'clear_current_layer',
    'clear_layers',
    'delete_layers',
]


# ==============================================================================
# helpers
# ==============================================================================


def show_hidden_objects_on_layer(name):
    rs.ShowObjects([guid for guid in rs.HiddenObjects() if rs.ObjectLayer(guid) == name])


def find_objects_on_layer(name, include_hidden=True, include_children=True):
    if include_hidden:
        show_hidden_objects_on_layer(name)
    to_delete = rs.ObjectsByLayer(name)
    if include_children:
        to_visit = deque(rs.LayerChildren(name))
        while to_visit:
            name = to_visit.popleft()
            if include_hidden:
                show_hidden_objects_on_layer(name)
            to_delete += rs.ObjectsByLayer(name)
            if rs.LayerChildCount(name):
                to_visit.extend(rs.LayerChildren(name))
    return to_delete


def delete_objects_on_layer(name, include_hidden=True, include_children=False, purge=True):
    guids = find_objects_on_layer(name, include_hidden, include_children)
    if purge and purge_object:
        rs.EnableRedraw(False)
        for guid in guids:
            obj = find_object(guid)
            if not obj:
                continue
            purge_object(obj.RuntimeSerialNumber)
        rs.EnableRedraw(True)
    else:
        rs.DeleteObjects(guids)


# ==============================================================================
# create
# ==============================================================================

def create_layers_from_path(path, separator='::'):
    """Create a nested layer structure from a hierarchical path string.

    Parameters
    ----------
    path : str
        The path string.
    separator : str, optional

    Examples
    --------
    The following snippet will created 3 nested layers,
    with "COMPAS" at the root level, "Datastructures" at the first nested level, and "Mesh" at the deepest level.

    * COMPAS
      * Datastructures
        * Mesh

    .. code-block:: python

        create_layers_from_path("COMPAS::Datastructures::Mesh")

    """
    names = path.split(separator)
    parent = None
    for name in names:
        if parent:
            name = parent + separator + name
        if not rs.IsLayer(name):
            rs.AddLayer(name)
        parent = name


def create_layers_from_paths(names, separator='::'):
    """Create nested layers from a lst of hierarchical path strings.

    Parameters
    ----------
    names : list of str
        The path strings of the nested layer structures.
    separator : str, optional

    Examples
    --------
    The following snippet will created 2 nested layer structures:

    * COMPAS
      * Datastructures
        * Mesh
        * Network
      * Geometry
        * Point
        * Vector

    .. code-block:: python

        create_layers_from_paths([
            "COMPAS::Datastructures::Mesh",
            "COMPAS::Datastructures::Network",
            "COMPAS::Geometry::Point",
            "COMPAS::Geometry::Vector",
        ])

    """
    for name in names:
        create_layers_from_path(name, separator=separator)


def create_layers_from_dict(layers):
    """Create Rhino layers from a dictionary.

    Parameters
    ----------
    layers : dict
        A dictionary of nested layer definitions.
        The keys of the dict are the layer names.
        The corresponding values define optional layer properties and nested layers.

    Examples
    --------
    .. code-block:: python

        layers = {'COMPAS', {'layers': {
            'Datastructures': {'color': (255, 0, 0), 'layers': {
                'Mesh': {},
                'Network': {}
            }},
            'Geometry': {'color': (0, 0, 255),'layers': {
                'Point': {},
                'Vector': {}
            }},
        }}}

        create_layers_from_dict(layers)

    """
    def recurse(layers, parent=None):
        for name in layers:
            if not name:
                continue
            fullname = name
            if parent:
                fullname = parent + '::' + name
            try:
                attr = layers[name]
            except TypeError:
                attr = {}
            attr = attr or {}
            color = attr.get('color', (0, 0, 0))
            visible = attr.get('visible', True)
            locked = attr.get('locked', False)
            if not rs.IsLayer(fullname):
                rs.AddLayer(fullname, color, visible, locked)
            if 'layers' in attr:
                recurse(attr['layers'], fullname)
    rs.EnableRedraw(False)
    recurse(layers)
    rs.EnableRedraw(True)


create_layers = create_layers_from_dict


# ==============================================================================
# clear
# ==============================================================================

def clear_layer(name, include_hidden=True, include_children=True, purge=True):
    """Delete all objects of a layer.

    Parameters
    ----------
    name : str
        The full, hierarchical name of the layer.
    include_hidden : bool, optional
        Include all hidden objects.
        Default is ``True``.
    include_children : bool, optional
        Include the objects of any child layers.
        Default is ``True``.
    purge : bool, optional
        Purge history after deleting.
        Default is ``True``.
    """
    if not rs.IsLayer(name):
        return
    guids = find_objects_on_layer(name, include_hidden, include_children)
    rs.EnableRedraw(False)
    if purge and purge_object:
        for guid in guids:
            obj = find_object(guid)
            if not obj:
                continue
            purge_object(obj.RuntimeSerialNumber)
    else:
        rs.DeleteObjects(guids)
    rs.EnableRedraw(True)


def clear_current_layer():
    """Delete all objects from the current layer."""
    layer = rs.CurrentLayer()
    clear_layer(layer)


[docs]def clear_layers(layers, include_children=True, include_hidden=True, purge=True): """Delete the objects of the specified layers. Parameters ---------- layers : list of str A list of layer names as fully qualified hierarchical paths. include_children : bool, optional Include the objects of any child layers. Default is ``True``. include_hidden : bool, optional Include all hidden objects. Default is ``True``. purge : bool, optional Purge history after deleting. Default is ``True``. """ rs.EnableRedraw(False) to_delete = [] for name in layers: if rs.IsLayer(name): to_delete += find_objects_on_layer(name, include_hidden, include_children) if purge and purge_object: for guid in to_delete: obj = find_object(guid) if not obj: continue purge_object(obj.RuntimeSerialNumber) else: rs.DeleteObjects(to_delete) rs.EnableRedraw(True)
# ============================================================================== # delete # ==============================================================================
[docs]def delete_layers(layers): """Delete layers and all contained objects. Parameters ---------- layers : :obj:`dict` or :obj:`list` of :obj:`str` Can be given as either a list of strings or as a dictionary. When given as a list the list elements should be name of layers given with ``"::"`` as a separator between hierarchies. It can also be defined as a dictionary of layers with the keys representing layer names, and the values also dictionaries defining optional nested layers. Examples -------- .. code-block:: python layers = {'COMPAS': {'layers': {'Datastructures': {'layers': {'Mesh': {}, 'Network': {}}}}}} create_layers(layers) delete_layers(['COMPAS::Datastructures::Network']) delete_layers({'COMPAS': {'layers': {'Datastructures': {'layers': {'Mesh': {}}}}}}) """ to_delete = [] def recurse(layers, parent=None): for name in layers: if not name: continue fullname = name if parent: fullname = parent + '::' + name try: attr = layers[name] except TypeError: attr = {} if 'layers' in attr: recurse(attr['layers'], fullname) to_delete.append(fullname) rs.EnableRedraw(False) recurse(layers) for layer in to_delete: if rs.IsLayer(layer): if rs.IsLayerCurrent(layer): print("Can't delete {} as it is the current layer".format(layer)) continue rs.PurgeLayer(layer) rs.EnableRedraw(True)
# ============================================================================== # Main # ============================================================================== if __name__ == "__main__": pass