Example #1
0
def test_json_network():
    before = Network()
    a = before.add_node()
    b = before.add_node()
    before.add_edge(a, b)
    after = compas.json_loads(compas.json_dumps(before))
    assert before.dtype == after.dtype
    assert before.attributes == after.attributes
    assert all(before.has_node(node) for node in after.nodes())
    assert all(after.has_node(node) for node in before.nodes())
    assert all(before.has_edge(*edge) for edge in after.edges())
    assert all(after.has_edge(*edge) for edge in before.edges())
Example #2
0
from itertools import combinations

from compas.datastructures import Network
from compas.utilities import linspace, meshgrid
from compas_rhino.artists import NetworkArtist

X, Y = meshgrid(linspace(0, 10, 10), linspace(0, 5, 5))

points = []
for z in linspace(0, 3, 3):
    for xs, ys in zip(X, Y):
        for x, y in zip(xs, ys):
            points.append([x, y, z])

network = Network()

for point in points:
    network.add_node(x=point[0], y=point[1], z=point[2])

for a, b in combinations(network.nodes(), 2):
    if network.node_attribute(a, 'z') != network.node_attribute(b, 'z'):
        network.add_edge(a, b)

artist = NetworkArtist(network, layer="ITA20::Network")
artist.clear_layer()
artist.draw_nodes()
artist.draw_edges()
Example #3
0
import random

from compas_rhino.artists import NetworkArtist
from compas.datastructures import Network

network = Network()

network.add_edge(1, 2)
network.add_edge(2, 3)
network.add_edge(1, 4)
network.add_edge(4, 5)
network.add_edge(4, 6)

for node in network.nodes():
    x = random.choice(range(5))
    y = random.choice(range(5))
    z = random.choice(range(5))
    network.node_attributes(node, 'xyz', [x, y, z])

print(network.summary())

text = {node: str(node) for node in network.nodes()}

artist = NetworkArtist(network, layer='network')
artist.clear_layer()
artist.draw_nodelabels(text)
artist.draw()
artist.redraw()
Example #4
0
b = network.add_node(x=10, y=0, z=10, is_anchor=True)
c = network.add_node(x=10, y=10, z=0, is_anchor=True)
d = network.add_node(x=0, y=10, z=10, is_anchor=True)

e = network.add_node(x=5, y=5, z=0)

network.add_edge(a, e, q=random.randint(1, 10))
network.add_edge(b, e, q=random.randint(1, 10))
network.add_edge(c, e, q=random.randint(1, 10))
network.add_edge(d, e, q=random.randint(1, 10))

# numerical data

n = network.number_of_nodes()

node_index = {node: index for index, node in enumerate(network.nodes())}

fixed = list(network.nodes_where({'is_anchor': True}))
free = list(network.nodes_where({'is_anchor': False}))

fixed[:] = [node_index[node] for node in fixed]
free[:] = [node_index[node] for node in free]

edges = [(node_index[u], node_index[v]) for u, v in network.edges()]

X = network.nodes_attributes('xyz')
R = network.nodes_attributes(['rx', 'ry', 'rz'])
P = network.nodes_attributes(['px', 'py', 'pz'])
Q = network.edges_attribute('q')

# compute equilibrium
Example #5
0
import random

from compas_rhino.artists import NetworkArtist
from compas.datastructures import Network

network = Network()

last_node = None
for i in range(12):
    node = network.add_node(x=i // 3, y=i % 3, z=0)
    network.node_attribute(node, 'weight', random.choice(range(20)))

    if last_node:
        network.add_edge(node, i - 1)
    last_node = node

print(network.summary())
print(network.to_data())

text = {
    node: network.node_attribute(node, 'weight')
    for node in network.nodes()
}

artist = NetworkArtist(network, layer='network')
artist.clear_layer()
artist.draw_nodelabels(text)
artist.draw()
artist.redraw()
class Assembly(FromToData, FromToJson):
    """A data structure for discrete element assemblies.

    An assembly is essentially a network of assembly elements.
    Each element is represented by a node of the network.
    Each interface or connection between elements is represented by an edge of the network.

    Attributes
    ----------
    network : :class:`compas.Network`, optional
    elements : list of :class:`Element`, optional
        A list of assembly elements.
    attributes : dict, optional
        User-defined attributes of the assembly.
        Built-in attributes are:
        * name (str) : ``'Assembly'``
    default_element_attribute : dict, optional
        User-defined default attributes of the elements of the assembly.
        The built-in attributes are:
        * is_planned (bool) : ``False``
        * is_placed (bool) : ``False``
    default_connection_attributes : dict, optional
        User-defined default attributes of the connections of the assembly.

    Examples
    --------
    >>> assembly = Assembly()
    >>> for i in range(2):
    >>>     element = Element.from_box(Box(Frame.worldXY(), 10, 5, 2))
    >>>     assembly.add_element(element)
    """
    def __init__(self,
                 elements=None,
                 attributes=None,
                 default_element_attributes=None,
                 default_connection_attributes=None):

        self.network = Network()
        self.network.attributes.update({'name': 'Assembly'})

        if attributes is not None:
            self.network.attributes.update(attributes)

        self.network.default_node_attributes.update({
            'is_planned': False,
            'is_placed': False
        })

        if default_element_attributes is not None:
            self.network.default_node_attributes.update(
                default_element_attributes)

        if default_connection_attributes is not None:
            self.network.default_edge_attributes.update(
                default_connection_attributes)

        if elements:
            for element in elements:
                self.add_element(element)

    @property
    def name(self):
        """str : The name of the assembly."""
        return self.network.attributes.get('name', None)

    @name.setter
    def name(self, value):
        self.network.attributes['name'] = value

    def number_of_elements(self):
        """Compute the number of elements of the assembly.

        Returns
        -------
        int
            The number of elements.

        """
        return self.network.number_of_nodes()

    def number_of_connections(self):
        """Compute the number of connections of the assembly.

        Returns
        -------
        int
            the number of connections.

        """
        return self.network.number_of_edges()

    @property
    def data(self):
        """Return a data dictionary of the assembly.
        """
        # Network data does not recursively serialize to data...
        d = self.network.data
        # so we need to trigger that for elements stored in nodes
        node = {}
        for vkey, vdata in d['data']['node'].items():
            node[vkey] = {
                key: vdata[key]
                for key in vdata.keys() if key != 'element'
            }
            node[vkey]['element'] = vdata['element'].to_data()
        d['data']['node'] = node
        return d

    @data.setter
    def data(self, data):
        # Deserialize elements from node dictionary
        for _vkey, vdata in data['data']['node'].items():
            vdata['element'] = Element.from_data(vdata['element'])

        self.network = Network.from_data(data)

    def clear(self):
        """Clear all the assembly data."""
        self.network.clear()

    def add_element(self, element, key=None, attr_dict={}, **kwattr):
        """Add an element to the assembly.

        Parameters
        ----------
        element : Element
            The element to add.
        attr_dict : dict, optional
            A dictionary of element attributes. Default is ``None``.

        Returns
        -------
        hashable
            The identifier of the element.
        """
        attr_dict.update(kwattr)
        x, y, z = element.frame.point
        key = self.network.add_node(key=key,
                                    attr_dict=attr_dict,
                                    x=x,
                                    y=y,
                                    z=z,
                                    element=element)
        return key

    def add_connection(self, u, v, attr_dict=None, **kwattr):
        """Add a connection between two elements and specify its attributes.

        Parameters
        ----------
        u : hashable
            The identifier of the first element of the connection.
        v : hashable
            The identifier of the second element of the connection.
        attr_dict : dict, optional
            A dictionary of connection attributes.
        kwattr
            Other connection attributes as additional keyword arguments.

        Returns
        -------
        tuple
            The identifiers of the elements.
        """
        return self.network.add_edge(u, v, attr_dict, **kwattr)

    def transform(self, transformation):
        """Transforms this assembly.

        Parameters
        ----------
        transformation : :class:`Transformation`

        Returns
        -------
        None
        """
        for _k, element in self.elements(data=False):
            element.transform(transformation)

    def transformed(self, transformation):
        """Returns a transformed copy of this assembly.

        Parameters
        ----------
        transformation : :class:`Transformation`

        Returns
        -------
        Assembly
        """
        assembly = self.copy()
        assembly.transform(transformation)
        return assembly

    def copy(self):
        """Returns a copy of this assembly.
        """
        raise NotImplementedError

    def element(self, key, data=False):
        """Get an element by its key."""
        if data:
            return self.network.node[key]['element'], self.network.node[key]
        else:
            return self.network.node[key]['element']

    def elements(self, data=False):
        """Iterate over the elements of the assembly.

        Parameters
        ----------
        data : bool, optional
            If ``True``, yield both the identifier and the attributes.

        Yields
        ------
        2-tuple
            The next element as a (key, element) tuple, if ``data`` is ``False``.
        3-tuple
            The next element as a (key, element, attr) tuple, if ``data`` is ``True``.

        """
        if data:
            for vkey, vattr in self.network.nodes(True):
                yield vkey, vattr['element'], vattr
        else:
            for vkey in self.network.nodes(data):
                yield vkey, self.network.node[vkey]['element']

    def connections(self, data=False):
        """Iterate over the connections of the network.

        Parameters
        ----------
        data : bool, optional
            If ``True``, yield both the identifier and the attributes.

        Yields
        ------
        2-tuple
            The next connection identifier (u, v), if ``data`` is ``False``.
        3-tuple
            The next connection as a (u, v, attr) tuple, if ``data`` is ``True``.

        """
        return self.network.edges(data)
Example #7
0
class AMModel(FromToData, FromToJson):
    """A data structure for non-discrete element fabrication.

    A model is essentially a network of nodes.
    Each geometrical node is represented by a layer in fabrication.
    The sequence of layers in fabrication is represented by an edge of the network.

    Attributes
    ----------
    network : :class:`compas.Network`, optional
    layers : list of :class:`Layer`, optional
        A list of model layers.
    attributes : dict, optional
        User-defined attributes of the model.
        Built-in attributes are:
        * name (str) : ``'AMModel'``
    default_layer_attribute : dict, optional
        User-defined default attributes of the layers of the model.
        The built-in attributes are:
        * is_planned (bool) : ``False``
        * is_placed (bool) : ``False``

    Examples
    --------

    """
    def __init__(self,
                 layers=None,
                 attributes=None,
                 default_layer_attribute=None,
                 default_connection_attributes=None):

        self.network = Network()
        self.network.attributes.update({'name': 'AMModel'})

        if attributes is not None:
            self.network.attributes.update(attributes)

        self.network.default_node_attributes.update({
            'is_planned': False,
            'is_placed': False
        })

        if default_layer_attribute is not None:
            self.network.default_node_attributes.update(
                default_layer_attribute)

        if default_connection_attributes is not None:
            self.network.default_edge_attributes.update(
                default_connection_attributes)

        if layers:
            for layer in layers:
                self.add_layer(layer)

    @property
    def name(self):
        """str : The name of the model."""
        return self.network.attributes.get('name', None)

    @name.setter
    def name(self, value):
        self.network.attributes['name'] = value

    def number_of_layers(self):
        """Compute the number of layers of the model.

        Returns
        -------
        int
            The number of nodes.

        """
        return self.network.number_of_nodes()

    def number_of_connections(self):
        """Compute the number of connections of the model.

        Returns
        -------
        int
            the number of connections.

        """
        return self.network.number_of_edges()

    @property
    def data(self):
        """Return a data dictionary of the model.
        """
        # Network data does not recursively serialize to data...
        d = self.network.data
        #print(d.keys())
        # so we need to trigger that for layers stored in nodes
        node = {}
        for vkey, vdata in d['data']['node'].items():
            node[vkey] = {
                key: vdata[key]
                for key in vdata.keys() if key != 'layer'
            }
            node[vkey]['layer'] = vdata['layer'].to_data()

        d['data']['node'] = node
        return d

    @data.setter
    def data(self, data):
        # Deserialize elements from node dictionary
        for _vkey, vdata in data['data']['node'].items():
            vdata['layer'] = Layer.from_data(vdata['layer'])

        self.network = Network.from_data(data)

    def clear(self):
        """Clear all the model data."""
        self.network.clear()

    def add_layer(self, layer, key=None, attr_dict={}, **kwattr):
        """Add an element to the model.

        Parameters
        ----------
        layer : Layer
            The layer to add.
        attr_dict : dict, optional
            A dictionary of layer attributes. Default is ``None``.

        Returns
        -------
        hashable
            The identifier of the element.
        """
        attr_dict.update(kwattr)
        key = self.network.add_node(key=key, attr_dict=attr_dict, layer=layer)
        return key

    def add_edge(self, u, v, attr_dict=None, **kwattr):
        """Add a connection between two elements and specify its attributes.

        Parameters
        ----------
        u : hashable
            The identifier of the first element of the connection.
        v : hashable
            The identifier of the second element of the connection.
        attr_dict : dict, optional
            A dictionary of connection attributes.
        kwattr
            Other connection attributes as additional keyword arguments.

        Returns
        -------
        tuple
            The identifiers of the elements.
        """
        return self.network.add_edge(u, v, attr_dict, **kwattr)

    def transform(self, transformation):
        """Transforms this model.

        Parameters
        ----------
        transformation : :class:`Transformation`

        Returns
        -------
        None
        """
        for _k, layer in self.layers(data=False):
            layer.transform(transformation)

    def transformed(self, transformation):
        """Returns a transformed copy of this model.

        Parameters
        ----------
        transformation : :class:`Transformation`

        Returns
        -------
        Assembly
        """
        model = self.copy()
        model.transform(transformation)
        return model

    def copy(self):
        """Returns a copy of this model.
        """
        layers = []
        for key, layer in self.layers():
            layers.append(layer.copy())
        return AMModel(layers, self.network.attributes)

    def layer(self, key, data=False):
        """Get an layer by its key."""
        if data:
            return self.network.node[key]['layer'], self.network.node[key]
        else:
            return self.network.node[key]['layer']

    def layers(self, data=False):
        """Iterate over the elements of the model.

        Parameters
        ----------
        data : bool, optional
            If ``True``, yield both the identifier and the attributes.

        Yields
        ------
        2-tuple
            The next element as a (key, element) tuple, if ``data`` is ``False``.
        3-tuple
            The next element as a (key, element, attr) tuple, if ``data`` is ``True``.

        """
        if data:
            for vkey, vattr in self.network.nodes(True):
                yield vkey, vattr['layer'], vattr
        else:
            for vkey in self.network.nodes(data):
                yield vkey, self.network.node[vkey]['layer']

    def connections(self, data=False):
        """Iterate over the connections of the network.

        Parameters
        ----------
        data : bool, optional
            If ``True``, yield both the identifier and the attributes.

        Yields
        ------
        2-tuple
            The next connection identifier (u, v), if ``data`` is ``False``.
        3-tuple
            The next connection as a (u, v, attr) tuple, if ``data`` is ``True``.

        """
        return self.network.edges(data)
Example #8
0
class Layer:
    """Data structure representing a discrete set of nodes of an model.

    Attributes
    ----------
    network : :class:`compas.Network`, optional
    nodes : :list:class:`Node`, optional
        Nodes discribing the layer geometry
    attributes : dict, optional
        User-defined attributes of the model.
        Built-in attributes are:
        * name (str) : ``'Layer'``
    trajectory : :class:`compas_fab.robots.JointTrajectory`
        The robot trajectory in joint space
    path : :list: :class:`compas.geometry.Frame`
        The robot tool path in cartesian space

    Examples
    --------

    """

    def __init__(self, nodes=None, attributes=None, edges=None):
        self.network = Network()
        self.network.attributes.update({'name': 'Layer',
                                        'is_constructed': False})
        self.is_constructed = False

        if attributes is not None:
            self.network.attributes.update(attributes)
        
        if nodes:
            for node in nodes:
                self.add_node(node)

        self.trajectory = None

    @classmethod
    def from_nodes(cls, nodes):
        """Class method for constructing a layer from a Node objects.

        Parameters
        ----------
        nodes :list: :class:`Node`
            List of Node objects.
        """
        return cls(nodes)

    @property
    def name(self):
        """str : The name of the layer."""
        return self.network.attributes.get('name', None)

    @name.setter
    def name(self, value):
        self.network.attributes['name'] = value
    
    def number_of_nodes(self):
        return self.network.number_of_nodes()
    
    def number_of_edges(self):
        return self.network.number_of_edges()

    def node(self, key, data=False):
        if data:
            return self.network.node[key]['node'], self.network.node[key]
        else:
            return self.network.node[key]['node']

    def nodes(self, data=False):
        if data:
            for vkey, vattr in self.network.nodes(True):
                yield vkey, vattr['node'], vattr
        else:
            for vkey in self.network.nodes(data):
                yield vkey, self.network.node[vkey]['node']

    def edges(self, data=False):
        return self.network.edges(data)

    @property
    def path(self):
        return [node.frame for key, node in self.nodes(False)]

    @path.setter
    def path(self, p):
        self.__path = p

    @classmethod
    def from_data(cls, data):
        """Construct an layer from its data representation.

        Parameters
        ----------
        data : :obj:`dict`
            The data dictionary.

        Returns
        -------
        Layer
            The constructed layer.
        """
        layer = cls()
        layer.data = data
        return layer

    def to_data(self):
        """
        docstring
        """
        return self.data

    @property
    def data(self):
        """Returns the data dictionary that represents the layer.

        Returns
        -------
        dict
            The layer data.

        Examples
        --------
        >>> layer = Layer()
        >>> print(layer.data)
        """
        d = self.network.data
        
        node = {}
        for vkey, vdata in d['data']['node'].items():
            node[vkey] = {key: vdata[key] for key in vdata.keys() if key != 'node'}
            node[vkey]['node'] = vdata['node'].to_data()

        d['data']['node'] = node
        d['data']['is_constructed'] = self.is_constructed
        if self.trajectory:
            d['data']['attributes']['trajectory'] = [f.to_data() for f in self.trajectory]
        if self.path:
            d['data']['attributes']['path'] = [f.to_data() for f in self.path]

        return d

    @data.setter
    def data(self, data):
        for _vkey, vdata in data['data']['node'].items():
            vdata['node'] = Node.from_data(vdata['node'])
        
        if 'is_constructed' in data:
            self.is_constructed = _deserialize_from_data(data['data']['attributes']['is_constructed'])
        if 'trajectory' in data:
            self.trajectory = _deserialize_from_data(data['data']['attributes']['trajectory'])
        if 'path' in data:
            self.path = [Frame.from_data(d) for d in data['data']['attributes']['path']]
        
        self.network = Network.from_data(data)

    def add_node(self, node, key=None, attr_dict={}, **kwattr):
        attr_dict.update(kwattr)
        key = self.network.add_node(key=key, attr_dict=attr_dict, node=node)
        return key
    
    def add_edge(self, u, v, attr_dict=None, **kwattr):
        return self.network.add_edge(u, v, attr_dict, **kwattr)

    def transform(self, transformation):
        for key, node in self.nodes(data=False):
            node.transform(transformation)

    def transformed(self, transformation):
        layer = self.copy()
        layer.transform(transformation)
        return layer

    def copy(self):
        """Returns a copy of this layer.

        Returns
        -------
        Layer
        """
        nodes = []
        for key, node in self.nodes():
            nodes.append(node.copy())
        return Layer(nodes, self.network.attributes)