Exemplo n.º 1
0
def test_add_vertex():
    network = Network()
    assert network.add_vertex() == 0
    assert network.add_vertex(x=0, y=0, z=0) == 1
    assert network.add_vertex(key=2) == 2
    assert network.add_vertex(key=0, x=1) == 0
Exemplo n.º 2
0
print('max angle', min(angles.values()))
print('min angle', max(angles.values()))

# ==========================================================================
# Create Face Adjacency Network - keys from 0 to N!
# ==========================================================================

n = mesh.number_of_faces()

network = Network()

for fkey in mesh.faces():
    xyz = centroids[fkey]
    attr_dict = {k: v for k, v in zip('xyz', xyz)}
    network.add_vertex(key=fkey, attr_dict=attr_dict)

for fkey in mesh.faces():
    nbrs = mesh.face_neighbors(fkey)
    for nbr in nbrs:
        if fkey == nbr:
            continue

        angle_diff = math.fabs(angles[fkey] - angles[nbr])

        try:
            ad = network.get_edge_attribute((fkey, nbr), 'angle_diff')
            if ad:
                continue
        except:
            network.add_edge(fkey, nbr, attr_dict={'angle_diff': angle_diff})
Exemplo n.º 3
0
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 vertex 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_attribute=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_vertex_attributes.update({
            'is_planned': False,
            'is_placed': False
        })

        if default_element_attribute is not None:
            self.network.default_vertex_attributes.update(
                default_element_attribute)

        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_vertices()

    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 vertices
        vertex = {}
        for vkey, vdata in d['vertex'].items():
            vertex[vkey] = {
                key: vdata[key]
                for key in vdata.keys() if key != 'element'
            }
            vertex[vkey]['element'] = vdata['element'].to_data()

        d['vertex'] = vertex

        return d

    @data.setter
    def data(self, data):
        # Deserialize elements from vertex dictionary
        for _vkey, vdata in data['vertex'].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_vertex(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):
        """Get an element by its key."""
        return self.network.vertex[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.vertices(True):
                yield vkey, vattr['element'], vattr
        else:
            for vkey in self.network.vertices(data):
                yield vkey, self.network.vertex[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)
Exemplo n.º 4
0
def egi_from_vectors(vectordict, origin, tol=0.001):
    """Construct an egi from a set of vectors.

    Parameters
    ----------
    vectordict : dict
        A dectionary of key-vector pairs.
    origin : list
        The coordinates of the centroid.
    tol : float, optional
        Tolerance for evaluating antipodal.

    Returns
    -------
    egi : mesh
        A mesh object representing the egi.

    Raises
    ------
    Exception
        If there are less than four vectors.

    Notes
    -----
    This algorithm is dependent on Rhinoceros objects; the adjacency arcs are implemented using Rhino.Geometry.Arc, and the cross-adjacencies (arc-arc intersections) are computed using Rhino.Geometry.Intersect.Intersection.CurveCurve.

    Warning
    -------
    - This algorithm does not address scenarios where multiple parallel (collinear) vectors are present.

    References
    ----------
    - Horn, B.K.P. (1984). *Extended Gaussian images*.
    - Moni, S. (1990, June). *A closed-form solution for the reconstruction
    of a convex polyhedron from its extended gaussian image.*
    - Lee, J., T. Van Mele, and P. Block (2018). *Disjointed force polyhedra.*

    """
    if len(vectordict) < 4:
        raise Exception('Four or more vectors are needed for the construction of egi.')

    egi = Network()

    # --------------------------------------------------------------------------
    #   1. add vertices from vectors
    # --------------------------------------------------------------------------
    vertex_geokeys = {}

    for vkey in vectordict:
        normal     = normalize_vector(vectordict[vkey])
        vertex_xyz = add_vectors(normal, origin)
        vertex_geokeys[geometric_key(normal)] = vkey
        egi.add_vertex(x=vertex_xyz[0],
                       y=vertex_xyz[1],
                       z=vertex_xyz[2],
                       key=vkey,
                       attr_dict={'type'  : 'face',
                                  'normal': normal,
                                  'nbrs'  : []})

    # --------------------------------------------------------------------------
    #   2.  Identify main adjacencies
    # --------------------------------------------------------------------------
    vkey_pairs = set()

    for vkey in egi.vertex:
        v_crs_dict = {}

        for nbr_vkey in egi.vertex:

            if nbr_vkey is not vkey:

                n1 = egi.vertex[vkey]['normal']
                n2 = egi.vertex[nbr_vkey]['normal']

                # This checks if the normals are opposite ----------------------
                dot = dot_vectors(n1, n2)

                if dot > 1 - tol:
                    raise Exception("Coincident vectors detected.")

                elif dot > -1 + tol:

                    this_crs = cross_vectors(n1, n2)
                    unit_crs = normalize_vector(this_crs)

                    crs_gkey = geometric_key(unit_crs)

                    # Check to see if any other normals are coplanar
                    if crs_gkey not in v_crs_dict:
                        v_crs_dict[crs_gkey] = nbr_vkey

                    # If multiple arcs are coplanar, choose the closer one
                    elif crs_gkey in v_crs_dict:
                        this_dist = distance(egi.vertex_coordinates(vkey),
                                             egi.vertex_coordinates(nbr_vkey))
                        test_dist = distance(egi.vertex_coordinates(vkey),
                                             egi.vertex_coordinates(v_crs_dict[crs_gkey]))
                        if this_dist < test_dist:
                            del v_crs_dict[crs_gkey]
                            v_crs_dict[crs_gkey] = nbr_vkey

        # Add to overall connectivity dict -------------------------------------
        for crs_gkey in v_crs_dict:
            nbr_vkey = v_crs_dict[crs_gkey]
            pair     = frozenset([vkey, nbr_vkey])
            vkey_pairs.add(pair)

    # --------------------------------------------------------------------------
    #   3.  Main adjacency arcs
    # --------------------------------------------------------------------------
    arcs = {}

    for pair in vkey_pairs:
        u, v = list(pair)
        arc  = _draw_arc(egi.vertex[u]['normal'],
                         egi.vertex[v]['normal'],
                         origin)

        if len(arcs) == 0:
            arc_key = 0
        else:
            arc_key = max(int(x) for x in arcs.keys()) + 1
        arcs[arc_key] = {'arc'      : arc,
                         'vkeys'    : [u, v],
                         'end_vkeys': [u, v],
                         'int_vkeys': {}, }

    # --------------------------------------------------------------------------
    #   3.  arc intersections --> cross adjacencies
    # --------------------------------------------------------------------------
    arc_pairs_seen = set()
    for arckey_1 in arcs:
        for arckey_2 in arcs:
            if arckey_1 != arckey_2:
                arc_pair = frozenset([arckey_1, arckey_2])
                if arc_pair not in arc_pairs_seen:
                    arc_1 = arcs[arckey_1]['arc']
                    arc_2 = arcs[arckey_2]['arc']
                    intersection = _curve_curve_intx(arc_1, arc_2)
                    if intersection:
                        new_vkey   = max(int(vkey) for vkey in egi.vertex.keys()) + 1
                        new_normal = subtract_vectors(intersection, origin)
                        new_normal = normalize_vector(new_normal)
                        new_vertex_geokey = geometric_key(new_normal, precision='3f')

                        # if intersection is not an endpoint -------------------
                        if new_vertex_geokey not in vertex_geokeys.keys():
                            vertex_geokeys[new_vertex_geokey] = new_vkey
                            egi.add_vertex(x=intersection[0],
                                           y=intersection[1],
                                           z=intersection[2],
                                           key=new_vkey,
                                           attr_dict={'type'      : 'zero',
                                                      'normal'    : new_normal,
                                                      'magnitude' : 0,
                                                      'nbrs'      : []})
                            arcs[arckey_1]['vkeys'].append(new_vkey)
                            arcs[arckey_2]['vkeys'].append(new_vkey)
                            arcs[arckey_1]['int_vkeys'][new_vkey] = arckey_2
                            arcs[arckey_2]['int_vkeys'][new_vkey] = arckey_1

                        # if intersection already exists -----------------------
                        elif new_vertex_geokey in vertex_geokeys.keys():
                            vkey = vertex_geokeys[new_vertex_geokey]
                            if vkey not in arcs[arckey_1]['vkeys']:
                                arcs[arckey_1]['vkeys'].append(vkey)
                                arcs[arckey_1]['int_vkeys'][vkey] = arckey_2
                            if vkey not in arcs[arckey_2]['vkeys']:
                                arcs[arckey_2]['vkeys'].append(vkey)
                                arcs[arckey_2]['int_vkeys'][vkey] = arckey_1
                        arc_pairs_seen.add(arc_pair)

    # --------------------------------------------------------------------------
    #   5.  Reorder vertices along each arc and add edges to EGI network
    # --------------------------------------------------------------------------
    for arckey in arcs:
        vkeys = arcs[arckey]['vkeys']
        if len(vkeys) > 2:
            pt_list = [egi.vertex_coordinates(key) for key in vkeys]
            arcs[arckey]['vkeys'] = _reorder_pts_on_arc(pt_list,
                                                        arcs[arckey]['vkeys'],
                                                        arcs[arckey]['arc'])[1]
            edge_type = 'cross'
        else:
            edge_type = 'main'
        for i in range(len(arcs[arckey]['vkeys']) - 1):
            vkey_1 = arcs[arckey]['vkeys'][i]
            vkey_2 = arcs[arckey]['vkeys'][i + 1]
            egi.vertex[vkey_1]['nbrs'] += [vkey_2]
            egi.vertex[vkey_2]['nbrs'] += [vkey_1]
            egi.add_edge(vkey_1, vkey_2)

    # --------------------------------------------------------------------------
    #   6.  For each vertex, sort nbrs in ccw order
    # --------------------------------------------------------------------------
    _egi_sort_v_nbrs(egi)

    # --------------------------------------------------------------------------
    #   7.  Add EGI Network faces
    # --------------------------------------------------------------------------
    egi_mesh = EGI()
    for vkey in egi.vertex:
        egi_mesh.vertex[vkey] = egi.vertex[vkey]

    egi_mesh.attributes['name'] = 'egi'
    egi_mesh.attributes['origin'] = list(origin)

    _egi_find_faces(egi, egi_mesh)

    return egi_mesh