Example #1
0
def intersection_polyline_box_xy(polyline, box, tol=1e-6):
    """Compute the intersection between a polyline and a box in the XY plane.

    Parameters
    ----------
    polyline : sequence[point] | :class:`compas.geometry.Polyline`
        A polyline defined by a sequence of points, with at least XY coordinates.
    box : [point, point, point, point]
        A box defined by a sequence of 4 points, with at least XY coordinates.
    tol : float, optional
        A tolerance value for point comparison.

    Returns
    -------
    list[[float, float, 0.0]]
        A list of intersection points.

    """
    precision = compas.PRECISION
    compas.set_precision(tol)
    points = []
    for side in pairwise(box + box[:1]):
        for segment in pairwise(polyline):
            x = intersection_segment_segment_xy(side, segment, tol=tol)
            if x:
                points.append(x)
    points = {geometric_key(point): point for point in points}
    compas.PRECISION = precision
    return list(points.values())
Example #2
0
def _face_adjacency(xyz, faces, nmax=10, radius=2.0):
    points = [centroid_points([xyz[index] for index in face]) for face in faces]
    tree = KDTree(points)
    closest = [tree.nearest_neighbors(point, nmax) for point in points]
    closest = [[index for _, index, _ in nnbrs] for nnbrs in closest]
    adjacency = {}
    for face, vertices in enumerate(faces):
        nbrs = []
        found = set()
        nnbrs = set(closest[face])
        for u, v in pairwise(vertices + vertices[0:1]):
            for nbr in nnbrs:
                if nbr == face:
                    continue
                if nbr in found:
                    continue
                for a, b in pairwise(faces[nbr] + faces[nbr][0:1]):
                    if v == a and u == b:
                        nbrs.append(nbr)
                        found.add(nbr)
                        break
                for a, b in pairwise(faces[nbr] + faces[nbr][0:1]):
                    if u == a and v == b:
                        nbrs.append(nbr)
                        found.add(nbr)
                        break
        adjacency[face] = nbrs
    return adjacency
Example #3
0
def intersection_polyline_box_xy(polyline, box, tol=1e-6):
    """Compute the intersection between a polyline and a box in the XY plane.

    Parameters
    ----------
    polyline : list of points or :class:`compas.geometry.Polyline`
    box : list of 4 points
    tol : float, optional
        A tolerance value for point comparison.
        Default is ``1e-6``.

    Returns
    -------
    list
        A list of intersection points.
    """
    precision = compas.PRECISION
    compas.set_precision(tol)
    points = []
    for side in pairwise(box + box[:1]):
        for segment in pairwise(polyline):
            x = intersection_segment_segment_xy(side, segment, tol=tol)
            if x:
                points.append(x)
    points = {geometric_key(point): point for point in points}
    compas.PRECISION = precision
    return list(points.values())
Example #4
0
def face_adjacency(xyz, faces):
    f = len(faces)

    if f > 100:
        return _face_adjacency(xyz, faces)

    adjacency = {}

    for face, vertices in enumerate(faces):
        nbrs = []
        found = set()

        for u, v in pairwise(vertices + vertices[0:1]):
            for nbr, _ in enumerate(faces):

                if nbr == face:
                    continue
                if nbr in found:
                    continue

                for a, b in pairwise(faces[nbr] + faces[nbr][0:1]):
                    if v == a and u == b:
                        nbrs.append(nbr)
                        found.add(nbr)
                        break

                for a, b in pairwise(faces[nbr] + faces[nbr][0:1]):
                    if u == a and v == b:
                        nbrs.append(nbr)
                        found.add(nbr)
                        break

        adjacency[face] = nbrs

    return adjacency
Example #5
0
def _face_adjacency(xyz, faces, nmax=10, radius=2.0):
    points = [centroid_points([xyz[index] for index in face]) for face in faces]
    k = min(len(faces), nmax)
    tree = cKDTree(points)
    _, closest = tree.query(points, k=k, n_jobs=-1)
    adjacency = {}
    for face, vertices in enumerate(faces):
        nbrs = []
        found = set()
        nnbrs = set(closest[face])
        for u, v in pairwise(vertices + vertices[0:1]):
            for nbr in nnbrs:
                if nbr == face:
                    continue
                if nbr in found:
                    continue
                for a, b in pairwise(faces[nbr] + faces[nbr][0:1]):
                    if v == a and u == b:
                        nbrs.append(nbr)
                        found.add(nbr)
                        break
                for a, b in pairwise(faces[nbr] + faces[nbr][0:1]):
                    if u == a and v == b:
                        nbrs.append(nbr)
                        found.add(nbr)
                        break
        adjacency[face] = nbrs
    return adjacency
Example #6
0
def draw_box(bbox, color, layer):
    lines = []
    for a, b in pairwise(bbox[:4] + bbox[:1]):
        lines.append({'start': a, 'end': b, 'color': color})
    for a, b in pairwise(bbox[4:] + bbox[4:5]):
        lines.append({'start': a, 'end': b, 'color': color})
    for a, b in zip(bbox[:4], bbox[4:]):
        lines.append({'start': a, 'end': b, 'color': color})
    compas_rhino.draw_lines(lines, layer=layer, clear=False)
Example #7
0
def face_adjacency(xyz, faces):
    """Construct an adjacency dictionary of the given faces, assuming that the faces have arbitrary orientation.

    Parameters
    ----------
    xyz : sequence[[float, float, float] | :class:`compas.geometry.Point`]
        The coordinates of the face vertices.
    faces : sequence[sequence[int]]
        A list of faces with each face defined by a list of indices into the list of xyz coordinates.

    Returns
    -------
    dict[int, list[int]]
        For every face a list of neighbouring faces.

    Notes
    -----
    If the number of faces is larger than one hundred (100),
    this function uses Rhino's RTree for limiting the search for neighbours
    to the immediate surroundings of any given face.

    Examples
    --------
    >>> vertices = [[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [1.0, 1.0, 0.0], [0.0, 1.0, 1.0]]
    >>> faces = [[0, 1, 2], [0, 3, 2]]
    >>> face_adjacency(vertices, faces)
    {0: [1], 1: [0]}

    """
    f = len(faces)
    if f > 100:
        return _face_adjacency(xyz, faces)
    adjacency = {}
    for face, vertices in enumerate(faces):
        nbrs = []
        found = set()
        for u, v in pairwise(vertices + vertices[0:1]):
            for nbr, _ in enumerate(faces):
                if nbr == face:
                    continue
                if nbr in found:
                    continue
                for a, b in pairwise(faces[nbr] + faces[nbr][0:1]):
                    if v == a and u == b:
                        nbrs.append(nbr)
                        found.add(nbr)
                        break
                for a, b in pairwise(faces[nbr] + faces[nbr][0:1]):
                    if u == a and v == b:
                        nbrs.append(nbr)
                        found.add(nbr)
                        break
        adjacency[face] = nbrs
    return adjacency
Example #8
0
def draw_cloud(cloud, bbox, color, layer):
    points = [{'pos': xyz, 'color': color} for xyz in cloud]
    lines = []
    for a, b in pairwise(bbox[:4] + bbox[:1]):
        lines.append({'start': a, 'end': b, 'color': color})
    for a, b in pairwise(bbox[4:] + bbox[4:5]):
        lines.append({'start': a, 'end': b, 'color': color})
    for a, b in zip(bbox[:4], bbox[4:]):
        lines.append({'start': a, 'end': b, 'color': color})
    compas_rhino.draw_points(points, layer=layer, clear=True)
    compas_rhino.draw_lines(lines, layer=layer, clear=False)
Example #9
0
def face_adjacency_rhino(xyz, faces):
    """Construct an adjacency dictionary of the given faces, assuming that the faces have arbitrary orientation.

    Parameters
    ----------
    xyz : list
        The coordinates of the face vertices.
    faces : list
        The indices of the face vertices in the coordinates list.

    Returns
    -------
    dict
        For every face a list of neighbouring faces.

    Examples
    --------
    >>> vertices = [[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [1.0, 1.0, 0.0], [0.0, 1.0, 1.0]]
    >>> faces = [[0, 1, 2], [0, 3, 2]]
    >>> face_adjacency_rhino(vertices, faces)  # doctest: +SKIP
    {0: [1], 1: [0]}

    Notes
    -----
    This function uses Rhino's RTree for limiting the search for neighbours
    to the immediate surroundings of any given face
    if the number of faces is greater than 100.
    """
    f = len(faces)
    if f > 100:
        return _face_adjacency(xyz, faces)
    adjacency = {}
    for face, vertices in enumerate(faces):
        nbrs = []
        found = set()
        for u, v in pairwise(vertices + vertices[0:1]):
            for nbr, _ in enumerate(faces):
                if nbr == face:
                    continue
                if nbr in found:
                    continue
                for a, b in pairwise(faces[nbr] + faces[nbr][0:1]):
                    if v == a and u == b:
                        nbrs.append(nbr)
                        found.add(nbr)
                        break
                for a, b in pairwise(faces[nbr] + faces[nbr][0:1]):
                    if u == a and v == b:
                        nbrs.append(nbr)
                        found.add(nbr)
                        break
        adjacency[face] = nbrs
    return adjacency
Example #10
0
def _face_adjacency(xyz, faces, nmax=10, radius=2.0):
    """"""
    points = [
        centroid_points([xyz[index] for index in face]) for face in faces
    ]

    tree = RTree()
    for i, point in enumerate(points):
        tree.Insert(Point3d(*point), i)

    def callback(sender, e):
        data = e.Tag
        data.append(e.Id)

    closest = []
    for i, point in enumerate(points):
        sphere = Sphere(Point3d(*point), radius)
        data = []
        tree.Search(sphere, callback, data)
        closest.append(data)

    adjacency = {}

    for face, vertices in enumerate(faces):
        nbrs = []
        found = set()

        nnbrs = set(closest[face])

        for u, v in pairwise(vertices + vertices[0:1]):
            for nbr in nnbrs:

                if nbr == face:
                    continue
                if nbr in found:
                    continue

                for a, b in pairwise(faces[nbr] + faces[nbr][0:1]):
                    if v == a and u == b:
                        nbrs.append(nbr)
                        found.add(nbr)
                        break

                for a, b in pairwise(faces[nbr] + faces[nbr][0:1]):
                    if u == a and v == b:
                        nbrs.append(nbr)
                        found.add(nbr)
                        break

        adjacency[face] = nbrs

    return adjacency
Example #11
0
    def draw_mesh(self, color=None, disjoint=False):
        """Draw the mesh as a consolidated RhinoMesh.

        Notes
        -----
        The mesh should be a valid Rhino Mesh object, which means it should have
        only triangular or quadrilateral faces.

        """
        key_index = self.mesh.key_index()
        vertices = self.mesh.get_vertices_attributes('xyz')
        faces = [[key_index[key] for key in self.mesh.face_vertices(fkey)] for fkey in self.mesh.faces()]
        new_faces = []
        for face in faces:
            f = len(face)
            if f == 3:
                new_faces.append(face + face[-1:])
            elif f == 4:
                new_faces.append(face)
            elif f > 4:
                centroid = len(vertices)
                vertices.append(centroid_polygon([vertices[index] for index in face]))
                for a, b in pairwise(face + face[0:1]):
                    new_faces.append([centroid, a, b, b])
            else:
                continue
        layer = self.layer
        name = "{}.mesh".format(self.mesh.name)
        return compas_rhino.draw_mesh(vertices, new_faces, layer=layer, name=name, color=color, disjoint=disjoint)
Example #12
0
    def face_flatness(self, fkey, maxdev=0.02):
        """Compute the flatness of the mesh face.

        Parameters
        ----------
        fkey : int
            The identifier of the face.
        maxdev : float, optional
            A maximum value for the allowed deviation from flatness.
            Default is ``0.02``.

        Returns
        -------
        float
            The flatness.

        Notes
        -----
        Flatness is computed as the ratio of the distance between the diagonals
        of the face to the average edge length. A practical limit on this value
        realted to manufacturing is 0.02 (2%).

        Warnings
        --------
        This method only makes sense for quadrilateral faces.
        """
        vertices = self.face_vertices(fkey)
        f = len(vertices)
        points = self.vertices_attributes('xyz', keys=vertices)
        lengths = [distance_point_point(a, b) for a, b in pairwise(points + points[:1])]
        length = sum(lengths) / f
        d = distance_line_line((points[0], points[2]), (points[1], points[3]))
        return (d / length) / maxdev
Example #13
0
def intersection_polyline_plane(polyline,
                                plane,
                                expected_number_of_intersections=None,
                                tol=1e-6):
    """Calculate the intersection point of a plane with a polyline. Reduce expected_number_of_intersections to speed up.

    Parameters
    ----------
    polyline : :class:`compas.geometry.Polyline` or sequence of points
        Polyline to test intersection.
    plane : :class:`compas.geometry.Plane` or point and vector
        Plane to compute intersection.
    expected_number_of_intersections : integer, optional
        Number of useful or expected intersections.
        Default is the number of lines conforming the polyline.
    tol : float, optional
        A tolerance for membership verification.
        Default is ``1e-6``.

    Returns
    -------
    list of points

    """
    if not expected_number_of_intersections:
        expected_number_of_intersections = len(polyline)
    intersections = []
    for segment in pairwise(polyline):
        if len(intersections) == expected_number_of_intersections:
            break
        point = intersection_segment_plane(segment, plane, tol)
        if point:
            intersections.append(point)
    return intersections
Example #14
0
def mesh_weld(mesh, precision=None, cls=None):
    """Weld vertices of a mesh within some precision distance.

    Parameters
    ----------
    mesh : Mesh
        A mesh.
    precision: str (None)
        Tolerance distance for welding.
    cls : type (None)
        Type of the welded mesh.
        This defaults to the type of the first mesh in the list.

    Returns
    -------
    mesh
        The welded mesh.

    """
    if cls is None:
        cls = type(mesh)

    geo = geometric_key
    key_xyz = {key: mesh.vertex_coordinates(key) for key in mesh.vertices()}
    gkey_key = {geo(xyz, precision): key for key, xyz in key_xyz.items()}
    gkey_index = {gkey: index for index, gkey in enumerate(gkey_key)}
    vertices = [key_xyz[key] for gkey, key in gkey_key.items()]
    faces = [[gkey_index[geo(key_xyz[key], precision)] for key in mesh.face_vertices(fkey)] for fkey in mesh.faces()]
    faces[:] = [[u for u, v in pairwise(face + face[:1]) if u != v] for face in faces]

    return cls.from_vertices_and_faces(vertices, faces)
Example #15
0
def offset_segments(point_list, distances, normal):
    """
    """
    segments = []
    for line, distance in zip(pairwise(point_list), distances):
        segments.append(offset_line(line, distance, normal))
    return segments
Example #16
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
Example #17
0
	def collect_polyedges(self):
		"""Collect the polyedges accross four-valent vertices between boundaries and/or singularities and store it in the mesh data attributes.

		Parameters
		----------

		Returns
		-------
		polyedges : list
			List of quad polyedges as list of vertices.

		"""

		edges = list(self.edges())

		nb_polyedges = -1
		while len(edges) > 0:
			nb_polyedges += 1
			
			# collect new polyedge
			u0, v0 = edges.pop()
			polyedge = self.collect_polyedge(u0, v0)
			self.data['attributes']['polyedges'].update({nb_polyedges: polyedge})

			# remove collected edges
			for u, v in pairwise(polyedge):
				if (u, v) in edges:
					edges.remove((u, v))
				elif (v, u) in edges:
					edges.remove((v, u))

		return self.polyedges(data=True)
def closest_point_on_polyline(polyline, c):
    """Closest point on polyline.
    If there are multiple closest points, the one from the first polyline segment is yielded.

    Parameters
    ----------
    polyline: list
        List of polyline point coordinates.
    p: list
        Point coordinates.

    Returns
    -------
    tuple
        The projected point coordinates and the distance from the input point.
    """

    proj_p = None
    min_distance = None
    for a, b in pairwise(polyline):
        p, distance = closest_point_on_segment(a, b, c)
        if proj_p is None or min_distance > distance:
            proj_p = p
            min_distance = distance
    return proj_p, min_distance
Example #19
0
    def vertex_curvature(self, vkey):
        """Dimensionless vertex curvature.

        Parameters
        ----------
        fkey : int
            The face key.

        Returns
        -------
        float
            The dimensionless curvature.

        References
        ----------
        Based on [1]_

        .. [1] Botsch, Mario, et al. *Polygon mesh processing.* AK Peters/CRC Press, 2010.
        """
        C = 0
        for u, v in pairwise(
                self.vertex_neighbors(vkey, ordered=True) +
                self.vertex_neighbors(vkey, ordered=True)[:1]):
            C += angle_points(self.vertex_coordinates(vkey),
                              self.vertex_coordinates(u),
                              self.vertex_coordinates(v))
        return 2 * pi - C
Example #20
0
def closest_point_on_polyline_xy(point, polyline):
    """Compute closest point on a polyline to a given point,
    assuming they both lie in the XY-plane.

    Parameters
    ----------
    point : sequence of float
        XY(Z) coordinates of a 2D or 3D point (Z will be ignored).
    polygon : sequence
        A sequence of XY(Z) coordinates of 2D or 3D points
        (Z will be ignored) representing the locations of the corners of a polygon.
        The vertices are assumed to be in order. The polygon is assumed to be closed:
        the first and last vertex in the sequence should not be the same.

    Returns
    -------
    list
        XYZ coordinates of closest point (Z = 0.0).

    """
    cloud = []

    for segment in pairwise(polyline):
        cloud.append(closest_point_on_segment_xy(point, segment))

    return closest_point_in_cloud_xy(point, cloud)[1]
Example #21
0
def closest_point_on_polyline_xy(point, polyline):
    """Compute closest point on a polyline to a given point,
    assuming they both lie in the XY-plane.

    Parameters
    ----------
    point : sequence of float
        XY(Z) coordinates of a 2D or 3D point (Z will be ignored).
    polyline : list of points or :class:`compas.geometry.Polyline`
        A sequence of XY(Z) coordinates of 2D or 3D points (Z will be ignored)
        representing the locations of the corners of a polyline.
        The vertices are assumed to be in order.

    Returns
    -------
    list
        XYZ coordinates of closest point (Z = 0.0).

    """
    cloud = []

    for segment in pairwise(polyline):
        cloud.append(closest_point_on_segment_xy(point, segment))

    return closest_point_in_cloud_xy(point, cloud)[1]
Example #22
0
def mesh_unweld_vertices(mesh, fkey, where=None):
    """Unweld a face of the mesh.

    Parameters
    ----------
    mesh : Mesh
        A mesh object.
    fkey : hashable
        The identifier of a face.
    where : list (None)
        A list of vertices to unweld.
        Default is to unweld all vertices of the face.

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

    """
    face = []
    vertices = mesh.face_vertices(fkey)

    if not where:
        where = vertices

    for u, v in pairwise(vertices + vertices[0:1]):
        if u in where:
            x, y, z = mesh.vertex_coordinates(u)
            u = mesh.add_vertex(x=x, y=y, z=z)
        if u in where or v in where:
            mesh.halfedge[v][u] = None
        face.append(u)

    mesh.add_face(face, fkey=fkey)

    return face
Example #23
0
 def edges_on_boundaries(self):
     """"""
     vertexgroups = self.vertices_on_boundaries()
     edgegroups = []
     for vertices in vertexgroups:
         edgegroups.append(list(pairwise(vertices)))
     return edgegroups
Example #24
0
    def mesh(self, mesh):
        self._mesh = mesh

        key_index = mesh.key_index()
        xyz = mesh.vertices_attributes('xyz')
        faces = []

        for fkey in mesh.faces():
            fvertices = [key_index[key] for key in mesh.face_vertices(fkey)]

            f = len(fvertices)
            if f < 3:
                pass
            elif f == 3:
                faces.append(fvertices)
            elif f == 4:
                a, b, c, d = fvertices
                faces.append([a, b, c])
                faces.append([c, d, a])
            else:
                o = mesh.face_centroid(fkey)
                v = len(xyz)
                xyz.append(o)
                for a, b in pairwise(fvertices + fvertices[0:1]):
                    faces.append([a, b, v])

        self._xyz = xyz
        self._faces = faces
Example #25
0
def kagome_polyedge_colouring(kagome):

    polyedges = kagome.polyedge_data

    edge_to_polyedge_index = {vkey: {} for vkey in kagome.vertices()}
    for i, polyedge in enumerate(polyedges):
        for u, v in pairwise(polyedge):
            edge_to_polyedge_index[u][v] = i
            edge_to_polyedge_index[v][u] = i

    vertices = [
        centroid_points([kagome.vertex_coordinates(vkey) for vkey in polyedge])
        for polyedge in polyedges
    ]

    edges = []
    for idx, polyedge in enumerate(polyedges):
        for vkey in polyedge:
            for vkey_2 in kagome.vertex_neighbors(vkey):
                idx_2 = edge_to_polyedge_index[vkey][vkey_2]
                if idx_2 != idx and idx < idx_2 and (idx, idx_2) not in edges:
                    edges.append((idx, idx_2))

    polyedge_network = Network.from_nodes_and_edges(vertices, edges)

    key_to_colour = vertex_coloring(polyedge_network.adjacency)

    return [key_to_colour[key] for key in sorted(key_to_colour.keys())]
Example #26
0
    def draw(self, color=None):
        """Draw the mesh as a RhinoMesh.

        Parameters
        ----------
        color : 3-tuple, optional
            RGB color components in integer format (0-255).

        Returns
        -------
        :class:`Rhino.Geometry.Mesh`

        """
        vertex_index = self.mesh.key_index()
        vertices = self.mesh.vertices_attributes('xyz')
        faces = [[
            vertex_index[vertex] for vertex in self.mesh.face_vertices(face)
        ] for face in self.mesh.faces()]
        new_faces = []
        for face in faces:
            f = len(face)
            if f == 3:
                new_faces.append(face + [face[-1]])
            elif f == 4:
                new_faces.append(face)
            elif f > 4:
                centroid = len(vertices)
                vertices.append(
                    centroid_polygon([vertices[index] for index in face]))
                for a, b in pairwise(face + face[0:1]):
                    new_faces.append([centroid, a, b, b])
            else:
                continue
        return compas_ghpython.draw_mesh(vertices, new_faces, color)
Example #27
0
 def lines(self):
     """list of :class:`compas.geometry.Line` : The lines of the polygon."""
     if not self._lines:
         self._lines = [
             Line(a, b) for a, b in pairwise(self.points + self.points[:1])
         ]
     return self._lines
Example #28
0
def my_edges_on_boundaries(mesh):

    vertexgroups = mesh.vertices_on_boundaries()
    edgegroups = []
    for vertices in vertexgroups:
        edgegroups.append(list(pairwise(vertices + vertices[:1])))
    return edgegroups
Example #29
0
def clean_faces(faces):

    for face in faces:
        for u, v in pairwise(face + face[:1]):
            if u == v:
                face.remove(u)
                break
Example #30
0
def intersection_line_box_xy(line, box, tol=1e-6):
    """Compute the intersection between a line and a box in the XY plane.

    Parameters
    ----------
    line : list of 2 points or :class:`compas.geometry.Line`
    box : list of 4 points
    tol : float, optional
        A tolerance value for point comparison.
        Default is ``1e-6``.

    Returns
    -------
    list
        A list of at most two intersection points.
    """
    points = []
    for segment in pairwise(box + box[:1]):
        x = intersection_line_segment_xy(line, segment, tol=tol)
        if x:
            points.append(x)
    if len(points) < 3:
        return points
    if len(points) == 3:
        a, b, c = points
        if allclose(a, b, tol=tol):
            return [a, c]
        if allclose(b, c, tol=tol):
            return [a, b]
        return [b, c]