示例#1
0
    def draw_collection(collection,
                        names=None,
                        colors=None,
                        layer=None,
                        clear=False,
                        add_to_group=False,
                        group_name=None):
        """Draw a collection of circles.

        Parameters
        ----------
        collection : list of :class:`compas.geometry.Circle`
            A collection of circles.
        names : list of str, optional
            Individual names for the circles.
        colors : color or list of color, optional
            A color specification for the circles as a single color or a list of individual colors.
        layer : str, optional
            A layer path.
        clear : bool, optional
            Clear the layer before drawing.
        add_to_group : bool, optional
            Add the circles to a group.
        group_name : str, optional
            Name of the group.

        Returns
        -------
        list
            The GUIDs of the created Rhino objects.
        """
        circles = []
        for circle in collection:
            circles.append({
                'plane': [list(circle[0][0]),
                          list(circle[0][1])],
                'radius': circle[1]
            })
        if colors:
            if isinstance(colors[0], (int, float)):
                colors = iterable_like(collection, [colors], colors)
            else:
                colors = iterable_like(collection, colors, colors[0])
            for point, rgb in zip(circles, colors):
                circle['color'] = rgb
        if names:
            if isinstance(names, basestring):
                names = iterable_like(collection, [names], names)
            else:
                names = iterable_like(collection, names, names[0])
            for circle, name in zip(circles, names):
                circle['name'] = name
        guids = compas_rhino.draw_circles(circles, layer=layer, clear=clear)
        if not add_to_group:
            return guids
        group = compas_rhino.rs.AddGroup(group_name)
        if group:
            compas_rhino.rs.AddObjectsToGroup(guids, group)
        return group
示例#2
0
    def draw_collection(collection,
                        names=None,
                        colors=None,
                        layer=None,
                        clear=False,
                        add_to_group=False,
                        group_name=None):
        """Draw a collection of lines.

        Parameters
        ----------
        collection: list of compas.geometry.Line
            A collection of ``Line`` objects.
        names : list of str, optional
            Individual names for the lines.
        colors : color or list of color, optional
            A color specification for the lines as a single color or a list of individual colors.
        layer : str, optional
            A layer path.
        clear : bool, optional
            Clear the layer before drawing.
        add_to_group : bool, optional
            Add the frames to a group.
        group_name : str, optional
            Name of the group.

        Returns
        -------
        guids: list
            A list of GUIDs if the collection is not grouped.
        groupname: str
            The name of the group if the collection objects are grouped.

        """
        lines = [{
            'start': list(line[0]),
            'end': list(line[1])
        } for line in collection]
        if colors:
            if isinstance(colors[0], (int, float)):
                colors = iterable_like(collection, [colors], colors)
            else:
                colors = iterable_like(collection, colors, colors[0])
            for line, rgb in zip(lines, colors):
                line['color'] = rgb
        if names:
            if isinstance(names, basestring):
                names = iterable_like(collection, [names], names)
            else:
                names = iterable_like(collection, names, names[0])
            for line, name in zip(lines, names):
                line['name'] = name
        guids = compas_rhino.draw_lines(lines, layer=layer, clear=clear)
        if not add_to_group:
            return guids
        group = compas_rhino.rs.AddGroup(group_name)
        if group:
            compas_rhino.rs.AddObjectsToGroup(guids, group)
        return group
示例#3
0
def offset_polyline(polyline, distance, normal=[0.0, 0.0, 1.0], tol=1e-6):
    """Offset a polyline by a distance.

    Parameters
    ----------
    polyline : list of point
        The XYZ coordinates of the vertices of a polyline.
    distance : float or list of tuples of floats
        The offset distance as float.
        A single value determines a constant offset globally.
        Alternatively, pairs of local offset values per line segment can be used to create variable offsets.
        Distance > 0: offset to the "left", distance < 0: offset to the "right".
    normal : vector
        The normal of the offset plane.

    Returns
    -------
    offset polyline : list of point
        The XYZ coordinates of the resulting polyline.

    """

    if not is_item_iterable(distance):
        distance = [distance]
    distances = iterable_like(polyline, distance, distance[-1])
    segments = offset_segments(polyline, distances, normal)

    offset = [segments[0][0]]
    for s1, s2 in pairwise(segments):
        point = intersect(s1, s2, tol)
        offset.append(point)
    offset.append(segments[-1][1])

    return offset
示例#4
0
 def size(self, size):
     size = size or self.default_size
     try:
         len(size)
     except TypeError:
         size = [size]
     size = iterable_like(self.points, size, self.default_size)
     self._size = list(size)
示例#5
0
 def color(self, color):
     color = color or self.default_color
     if not is_sequence_of_iterable(color):
         color = [color]
     self._color = [
         FromArgb(*c)
         for c in iterable_like(self.lines, color, self.default_color)
     ]
示例#6
0
 def color(self, color):
     color = color or self.default_color
     if not is_sequence_of_iterable(color):
         color = [color]
     color = [
         FromArgb(*color_to_rgb(c))
         for c in iterable_like(self.points, color, self.default_color)
     ]
     self._color = color
示例#7
0
 def thickness(self, thickness):
     thickness = thickness or self.default_thickness
     try:
         len(thickness)
     except TypeError:
         thickness = [thickness]
     thickness = iterable_like(self.lines, thickness,
                               self.default_thickness)
     self._thickness = list(thickness)
示例#8
0
 def color(self, color):
     if not color:
         return
     if not is_sequence_of_iterable(color):
         color = [color]
     color = [
         FromArgb(*c)
         for c in iterable_like(self.faces, color, self.default_color)
     ]
     self._color = color
示例#9
0
def offset_line(line, distance, normal=[0.0, 0.0, 1.0]):
    """Offset a line by a distance.

    Parameters
    ----------
    line : tuple
        Two points defining the line.
    distances : float or list of floats
        The offset distance as float.
        A single value determines a constant offset. Alternatively, two
        offset values for the start and end point of the line can be used to
        a create variable offset.
    normal : vector
        The normal of the offset plane.

    Returns
    -------
    offset line : tuple
        Two points defining the offset line.

    Notes
    -----
    The offset direction is chosen such that if the line were along the positve
    X axis and the normal of the offset plane is along the positive Z axis, the
    offset line is in the direction of the postive Y axis.

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

        line = [(0.0, 0.0, 0.0), (3.0, 3.0, 0.0)]

        distance = 0.2 # constant offset
        line_offset = offset_line(line, distance)
        print(line_offset)

        distance = [0.2, 0.1] # variable offset
        line_offset = offset_line(line, distance)
        print(line_offset)

    """

    a, b = line
    ab = subtract_vectors(b, a)
    direction = normalize_vector(cross_vectors(normal, ab))

    if not is_item_iterable(distance):
        distance = [distance]
    distances = list(iterable_like(line, distance, distance[-1]))

    u = scale_vector(direction, distances[0])
    v = scale_vector(direction, distances[1])
    c = add_vectors(a, u)
    d = add_vectors(b, v)
    return c, d
示例#10
0
 def color(self, color):
     if not color:
         return
     if not is_sequence_of_iterable(color[0]):
         # the first item in the list should be a tuple of colors
         # if not, wrap the tuple
         color = [color]
     color = [(FromArgb(*bg), FromArgb(*text))
              for bg, text in iterable_like(self.labels, color, (
                  self.default_color, self.default_textcolor))]
     self._color = color
示例#11
0
def offset_polygon(polygon, distance, tol=1e-6):
    """Offset a polygon (closed) by a distance.

    Parameters
    ----------
    polygon : sequence[point] | :class:`compas.geometry.Polygon`
        The XYZ coordinates of the corners of the polygon.
        The first and last coordinates must not be identical.
    distance : float | list[tuple[float, float]]
        The offset distance as float.
        A single value determines a constant offset globally.
        A list of pairs of local offset values per line segment can be used to create variable offsets.
    tol : float, optional
        A tolerance value for intersection calculations.

    Returns
    -------
    list[[float, float, float]]
        The XYZ coordinates of the corners of the offset polygon.
        The first and last coordinates are identical.

    Notes
    -----
    The offset direction is determined by the normal of the polygon.
    If the polygon is in the XY plane and the normal is along the positive Z axis,
    positive offset distances will result in an offset towards the inside of the
    polygon.

    The algorithm works also for spatial polygons that do not perfectly fit a plane.

    Examples
    --------
    >>>

    """
    normal = normal_polygon(polygon)

    if not is_item_iterable(distance):
        distance = [distance]
    distances = iterable_like(polygon, distance, distance[-1])

    polygon = polygon + polygon[:1]
    segments = offset_segments(polygon, distances, normal)

    offset = []
    for s1, s2 in pairwise(segments[-1:] + segments):
        point = intersect(s1, s2, tol)
        offset.append(point)

    return offset
示例#12
0
def offset_polyline(polyline, distance, normal=[0.0, 0.0, 1.0], tol=1e-6):
    """Offset a polyline by a distance.

    Parameters
    ----------
    polyline : sequence[point] | :class:`compas.geometry.Polyline`
        The XYZ coordinates of the vertices of a polyline.
    distance : float | list[tuple[float, float]]
        The offset distance as float.
        A single value determines a constant offset globally.
        Alternatively, pairs of local offset values per line segment can be used to create variable offsets.
    normal : [float, float, float] | :class:`compas.geometry.Vector`, optional
        The normal of the offset plane.
    tol : float, optional
        A tolerance value for intersection calculations.

    Returns
    -------
    list[[float, float, float]]
        The XYZ coordinates of the resulting polyline.

    Notes
    -----
    The offset direction is determined by the provided normal vector.
    If the polyline is in the XY plane and the normal is along the positive Z axis,
    positive offset distances will result in counterclockwise offsets,
    and negative values in clockwise direction.

    Examples
    --------
    >>>

    """

    if not is_item_iterable(distance):
        distance = [distance]
    distances = iterable_like(polyline, distance, distance[-1])
    segments = offset_segments(polyline, distances, normal)

    offset = [segments[0][0]]
    for s1, s2 in pairwise(segments):
        point = intersect(s1, s2, tol)
        offset.append(point)
    offset.append(segments[-1][1])

    return offset
示例#13
0
    def draw_collection(collection, color=None, layer=None, clear=False, group_collection=False, group_name=None):
        """Draw a collection of points.

        Parameters
        ----------
        collection: list of compas.geometry.Point
            A collection of ``Point`` objects.
        color: tuple or list of tuple (optional)
            Color specification of the points.
            If one RGB color is provided, it will be applied to all points.
            If a list of RGB colors is provided, these colors are applied to the corresponding points.
            A list of colors should have the same length as the collection, with one color per item.
            Default value is ``None`` in which case the default point color of the artist is used.
        layer: str (optional)
            The layer in which the objects of the collection should be created.
            Default is ``None``, in which case the default layer setting of the artist is used.
        clear: bool (optional)
            Clear the layer before drawing.
            Default is ``False``.
        group_collection: bool (optional)
            Flag for grouping the objects of the collection.
            Default is ``False``.
        group_name: str (optional).
            The name of the group.
            Default is ``None``.

        Returns
        -------
        guids: list
            A list of GUIDs if the collection is not grouped.
        groupname: str
            The name of the group if the collection objects are grouped.

        """
        points = []
        colors = iterable_like(collection, color)
        for point, rgb in zip(collection, colors):
            points.append({'pos': list(point), 'color': rgb})
        guids = compas_rhino.draw_points(points, layer=layer, clear=clear)
        if not group_collection:
            return guids
        group = compas_rhino.rs.AddGroup(group_name)
        if group:
            compas_rhino.rs.AddObjectsToGroup(guids, group)
        return group
示例#14
0
def offset_line(line, distance, normal=[0.0, 0.0, 1.0]):
    """Offset a line by a distance.

    Parameters
    ----------
    line : [point, point] | :class:`compas.geometry.Line`
        A line defined by two points.
    distances : float or list[float]
        The offset distance as float.
        A single value determines a constant offset.
        A list of two offset values can be used to a create variable offset at the start and end.
    normal : [float, float, float] | :class:`compas.geometry.Vector`, optional
        The normal of the offset plane.

    Returns
    -------
    tuple[[float, float, float], [float, float, float]]
        Two points defining the offset line.

    Notes
    -----
    The offset direction is chosen such that if the line were along the positve
    X axis and the normal of the offset plane is along the positive Z axis, the
    offset line is in the direction of the postive Y axis.

    Examples
    --------
    >>>

    """

    a, b = line
    ab = subtract_vectors(b, a)
    direction = normalize_vector(cross_vectors(normal, ab))

    if not is_item_iterable(distance):
        distance = [distance]
    distances = list(iterable_like(line, distance, distance[-1]))

    u = scale_vector(direction, distances[0])
    v = scale_vector(direction, distances[1])
    c = add_vectors(a, u)
    d = add_vectors(b, v)
    return c, d
def test_iterable_like_string_and_float(target, base, fillvalue):
    a = list(iterable_like(target, base, fillvalue))
    assert a == [0.5, 0.5, 0.5, 0.5, 0.5]
示例#16
0
def offset_polygon(polygon, distance, tol=1e-6):
    """Offset a polygon (closed) by a distance.

    Parameters
    ----------
    polygon : list of point
        The XYZ coordinates of the corners of the polygon.
        The first and last coordinates must not be identical.
    distance : float or list of float
        The offset distance as float.
        A single value determines a constant offset globally.
        Alternatively, pairs of local offset values per line segment can be used to create variable offsets.
        Distance > 0: offset to the outside, distance < 0: offset to the inside.

    Returns
    -------
    offset polygon : list of point
        The XYZ coordinates of the corners of the offset polygon.
        The first and last coordinates are identical.

    Notes
    -----
    The offset direction is determined by the normal of the polygon.
    If the polygon is in the XY plane and the normal is along the positive Z axis,
    positive offset distances will result in an offset towards the inside of the
    polygon.

    The algorithm works also for spatial polygons that do not perfectly fit a plane.

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

        polygon = [
            (0.0, 0.0, 0.0),
            (3.0, 0.0, 1.0),
            (3.0, 3.0, 2.0),
            (1.5, 1.5, 2.0),
            (0.0, 3.0, 1.0),
            (0.0, 0.0, 0.0)
            ]

        distance = 0.5 # constant offset
        polygon_offset = offset_polygon(polygon, distance)
        print(polygon_offset)

        distance = [
            (0.1, 0.2),
            (0.2, 0.3),
            (0.3, 0.4),
            (0.4, 0.3),
            (0.3, 0.1)
            ] # variable offset
        polygon_offset = offset_polygon(polygon, distance)
        print(polygon_offset)

    """
    normal = normal_polygon(polygon)

    if not is_item_iterable(distance):
        distance = [distance]
    distances = iterable_like(polygon, distance, distance[-1])

    polygon = polygon + polygon[:1]
    segments = offset_segments(polygon, distances, normal)

    offset = []
    for s1, s2 in pairwise(segments[-1:] + segments):
        point = intersect(s1, s2, tol)
        offset.append(point)

    return offset
def test_iterable_cap_generator(mesh_a, mesh_b):
    ma = Mesh.from_obj(compas.get(mesh_a))
    mb = Mesh.from_obj(compas.get(mesh_b))
    a = list(iterable_like(ma.faces(), mb.faces()))
    assert len(a) == len(list(ma.faces()))
def test_iterable_like_generator_and_list(target, base, fillvalue):
    a = list(iterable_like(target, base, fillvalue))
    assert a == ['a', 'b']
示例#19
0
    def draw_collection(collection,
                        color=None,
                        name=None,
                        layer=None,
                        clear=False,
                        group_collection=False,
                        group_name=None):
        """Draw a collection of points.

        Parameters
        ----------
        collection : list of compas.geometry.Point
            A collection of ``Point`` objects.
        color : tuple or list of tuple (optional)
            Color specification of the points.
            If one RGB color is provided, it will be applied to all points.
            If a list of RGB colors is provided, these colors are applied to the corresponding points.
            A list of colors should have the same length as the collection, with one color per item.
            Default value is ``None`` in which case the default point color of the artist is used.
        name : str or list of str (optional)
            Name of each point.
            If only one name is provided, this name will be used for all points.
            If a list of names is provided, the list should contain a name for each point in the collection.
        layer : str (optional)
            The layer in which the objects of the collection should be created.
            Default is ``None``, in which case the default layer setting of the artist is used.
        clear : bool (optional)
            Clear the layer before drawing.
            Default is ``False``.
        group_collection : bool (optional)
            Flag for grouping the objects of the collection.
            Default is ``False``.
        group_name : str (optional).
            The name of the group.
            Default is ``None``.

        Returns
        -------
        guids: list
            A list of GUIDs if the collection is not grouped.
        groupname: str
            The name of the group if the collection objects are grouped.

        """
        compas_rhino.rs.EnableRedraw(False)
        points = [{'pos': list(point)} for point in collection]
        if color:
            if isinstance(color[0], (int, float)):
                colors = iterable_like(collection, [color], color)
            else:
                colors = iterable_like(collection, color, color[0])
            for point, rgb in zip(points, colors):
                point['color'] = rgb
        if name:
            if isinstance(name, basestring):
                names = iterable_like(collection, [name], name)
            else:
                names = iterable_like(collection, name, name[0])
            for point, name in zip(points, names):
                point['name'] = name
        guids = compas_rhino.draw_points(points,
                                         layer=layer,
                                         clear=clear,
                                         redraw=False)
        if not group_collection:
            return guids
        group = compas_rhino.rs.AddGroup(group_name)
        if group:
            compas_rhino.rs.AddObjectsToGroup(guids, group)
        compas_rhino.rs.EnableRedraw(True)
        return group
示例#20
0
def mesh_subdivide_frames(mesh, offset, add_windows=False):
    """Subdivide a mesh by creating offset frames and windows on its faces.

    Parameters
    ----------
    mesh : :class:`compas.datastructures.Mesh`
        The mesh object to be subdivided.
    offset : float | dict[int, float]
        The offset distance to create the frames.
        A single value will result in a constant offset everywhere.
        A dictionary mapping faces to offset values will be processed accordingly.
    add_windows : bool, optional
        If True, add a window face in the frame opening.

    Returns
    -------
    :class:`compas.datastructures.Mesh`
        A new subdivided mesh.

    """
    cls = type(mesh)
    SubdMesh = subd_factory(cls)

    subd = SubdMesh()

    # 0. pre-compute offset distances
    if not isinstance(offset, dict):
        distances = iterable_like(mesh.faces(), [offset], offset)
        offset = {fkey: od for fkey, od in zip(mesh.faces(), distances)}

    # 1. add vertices
    newkeys = {}
    for vkey, attr in mesh.vertices(True):
        newkeys[vkey] = subd.add_vertex(*mesh.vertex_coordinates(vkey))

    # 2. add faces
    for fkey in mesh.faces():
        face = [newkeys[vkey] for vkey in mesh.face_vertices(fkey)]
        d = offset.get(fkey)

        # 2a. add face and break if no offset is found
        if d is None:
            subd.add_face(face)
            continue

        polygon = offset_polygon(mesh.face_coordinates(fkey), d)

        # 2a. add offset vertices
        window = []
        for xyz in polygon:
            x, y, z = xyz
            new_vkey = subd.add_vertex(x=x, y=y, z=z)
            window.append(new_vkey)

        # 2b. frame faces
        face = face + face[:1]
        window = window + window[:1]
        for sa, sb in zip(pairwise(face), pairwise(window)):
            subd.add_face([sa[0], sa[1], sb[1], sb[0]])

        # 2c. window face
        if add_windows:
            subd.add_face(window)

    return cls.from_data(subd.data)
示例#21
0
def mesh_subdivide_frames(mesh, offset, add_windows=False):
    """Subdivide a mesh by creating offset frames and windows on its faces.

    Parameters
    ----------
    mesh : Mesh
        The mesh object to be subdivided.
    offset : float or dict
        The offset distance to create the frames.
        A single value will result in a constant offset everywhere.
        A dictionary mapping facekey: offset will be processed accordingly.
    add_windows : boolean
        Optional. Flag to add window face. Default is ``False``.

    Returns
    -------
    Mesh
        A new subdivided mesh.

    Examples
    --------
    >>>

    """

    subd = SubdMesh()

    # 0. pre-compute offset distances
    if not isinstance(offset, dict):
        distances = iterable_like(mesh.faces(), [offset], offset)
        offset = {fkey: od for fkey, od in zip(mesh.faces(), distances)}

    # 1. add vertices
    newkeys = {}
    for vkey, attr in mesh.vertices(True):
        newkeys[vkey] = subd.add_vertex(*mesh.vertex_coordinates(vkey))

    # 2. add faces
    for fkey in mesh.faces():
        face = [newkeys[vkey] for vkey in mesh.face_vertices(fkey)]
        d = offset.get(fkey)

        # 2a. add face and break if no offset is found
        if d is None:
            subd.add_face(face)
            continue

        polygon = offset_polygon(mesh.face_coordinates(fkey), d)

        # 2a. add offset vertices
        window = []
        for xyz in polygon:
            x, y, z = xyz
            new_vkey = subd.add_vertex(x=x, y=y, z=z)
            window.append(new_vkey)

        # 2b. frame faces
        face = face + face[:1]
        window = window + window[:1]
        for sa, sb in zip(pairwise(face), pairwise(window)):
            subd.add_face([sa[0], sa[1], sb[1], sb[0]])

        # 2c. window face
        if add_windows:
            subd.add_face(window)

    return subd
def test_iterable_like_list_and_dict(target, base, fillvalue):
    a = list(iterable_like(target, base, fillvalue))
    assert a == ['key_1', 'key_2', 'key_3']