Exemple #1
0
def delaunay_from_points(points, boundary=None, holes=None, tiny=1e-12):
    """Computes the delaunay triangulation for a list of points.

    Parameters
    ----------
    points : sequence of tuple
        XYZ coordinates of the original points.
    boundary : sequence of tuples
        list of ordered points describing the outer boundary (optional)
    holes : list of sequences of tuples
        list of polygons (ordered points describing internal holes (optional)

    Returns
    -------
    list
        The faces of the triangulation.
        Each face is a triplet of indices referring to the list of point coordinates.

    Notes
    -----
    For more info, see [1]_.

    References
    ----------
    .. [1] Sloan, S. W., 1987 *A fast algorithm for constructing Delaunay triangulations in the plane*
           Advances in Engineering Software 9(1): 34-55, 1978.

    Example
    -------
    .. plot::
        :include-source:

        from compas.datastructures import Mesh
        from compas.geometry import pointcloud_xy
        from compas.geometry import delaunay_from_points
        from compas_plotters import MeshPlotter

        points = pointcloud_xy(20, (0, 50))
        faces = delaunay_from_points(points)

        delaunay = Mesh.from_vertices_and_faces(points, faces)

        plotter = MeshPlotter(delaunay)
        plotter.draw_vertices(radius=0.1)
        plotter.draw_faces()
        plotter.show()

    """
    from compas.datastructures import Mesh
    from compas.datastructures import trimesh_swap_edge

    def super_triangle(coords):
        centpt = centroid_points(coords)
        bbpts = bounding_box(coords)
        dis = distance_point_point(bbpts[0], bbpts[2])
        dis = dis * 300
        v1 = (0 * dis, 2 * dis, 0)
        v2 = (1.73205 * dis, -1.0000000000001 * dis, 0)  # due to numerical issues
        v3 = (-1.73205 * dis, -1 * dis, 0)
        pt1 = add_vectors(centpt, v1)
        pt2 = add_vectors(centpt, v2)
        pt3 = add_vectors(centpt, v3)
        return pt1, pt2, pt3

    mesh = Mesh()

    # to avoid numerical issues for perfectly structured point sets
    points = [(point[0] + random.uniform(-tiny, tiny), point[1] + random.uniform(-tiny, tiny), 0.0) for point in points]

    # create super triangle
    pt1, pt2, pt3 = super_triangle(points)

    # add super triangle vertices to mesh
    n = len(points)
    super_keys = n, n + 1, n + 2

    mesh.add_vertex(super_keys[0], {'x': pt1[0], 'y': pt1[1], 'z': pt1[2]})
    mesh.add_vertex(super_keys[1], {'x': pt2[0], 'y': pt2[1], 'z': pt2[2]})
    mesh.add_vertex(super_keys[2], {'x': pt3[0], 'y': pt3[1], 'z': pt3[2]})

    mesh.add_face(super_keys)

    # iterate over points
    for i, pt in enumerate(points):
        key = i

        # newtris should be intialised here

        # check in which triangle this point falls
        for fkey in list(mesh.faces()):
            # abc = mesh.face_coordinates(fkey) #This is slower
            # This is faster:
            keya, keyb, keyc = mesh.face_vertices(fkey)

            dicta = mesh.vertex[keya]
            dictb = mesh.vertex[keyb]
            dictc = mesh.vertex[keyc]

            a = [dicta['x'], dicta['y']]
            b = [dictb['x'], dictb['y']]
            c = [dictc['x'], dictc['y']]

            if is_point_in_triangle_xy(pt, [a, b, c], True):
                # generate 3 new triangles (faces) and delete surrounding triangle
                key, newtris = mesh.insert_vertex(fkey, key=key, xyz=pt, return_fkeys=True)
                break

        while newtris:
            fkey = newtris.pop()

            # get opposite_face
            keys = mesh.face_vertices(fkey)
            s = list(set(keys) - set([key]))
            u, v = s[0], s[1]
            fkey1 = mesh.halfedge[u][v]

            if fkey1 != fkey:
                fkey_op, u, v = fkey1, u, v
            else:
                fkey_op, u, v = mesh.halfedge[v][u], u, v

            if fkey_op:
                keya, keyb, keyc = mesh.face_vertices(fkey_op)
                dicta = mesh.vertex[keya]
                a = [dicta['x'], dicta['y']]
                dictb = mesh.vertex[keyb]
                b = [dictb['x'], dictb['y']]
                dictc = mesh.vertex[keyc]
                c = [dictc['x'], dictc['y']]

                circle = circle_from_points_xy(a, b, c)

                if is_point_in_circle_xy(pt, circle):
                    fkey, fkey_op = trimesh_swap_edge(mesh, u, v)
                    newtris.append(fkey)
                    newtris.append(fkey_op)

    # Delete faces adjacent to supertriangle
    for key in super_keys:
        mesh.delete_vertex(key)

    # Delete faces outside of boundary
    if boundary:
        for fkey in list(mesh.faces()):
            centroid = mesh.face_centroid(fkey)
            if not is_point_in_polygon_xy(centroid, boundary):
                mesh.delete_face(fkey)

    # Delete faces inside of inside boundaries
    if holes:
        for polygon in holes:
            for fkey in list(mesh.faces()):
                centroid = mesh.face_centroid(fkey)
                if is_point_in_polygon_xy(centroid, polygon):
                    mesh.delete_face(fkey)

    return [mesh.face_vertices(fkey) for fkey in mesh.faces()]
Exemple #2
0
def delaunay_from_points(points, boundary=None, holes=None, tiny=1e-12):
    """Computes the delaunay triangulation for a list of points.

    Parameters
    ----------
    points : sequence[[float, float, float] | :class:`compas.geometry.Point`]
        XYZ coordinates of the original points.
    boundary : sequence[[float, float, float] | :class:`compas.geometry.Point`] | :class:`compas.geometry.Polygon`, optional
        List of ordered points describing the outer boundary.
    holes : sequence[sequence[[float, float, float] | :class:`compas.geometry.Point`] | :class:`compas.geometry.Polygon`], optional
        List of polygons (ordered points describing internal holes.

    Returns
    -------
    list[[int, int, int]]
        The faces of the triangulation.
        Each face is a triplet of indices referring to the list of point coordinates.

    Notes
    -----
    For more info, see [1]_.

    References
    ----------
    .. [1] Sloan, S. W., 1987 *A fast algorithm for constructing Delaunay triangulations in the plane*
           Advances in Engineering Software 9(1): 34-55, 1978.

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

    """
    from compas.datastructures import Mesh
    from compas.datastructures import trimesh_swap_edge

    def super_triangle(coords, ccw=True):
        centpt = centroid_points(coords)
        bbpts = bounding_box(coords)
        dis = distance_point_point(bbpts[0], bbpts[2])
        dis = dis * 300
        v1 = (0 * dis, 2 * dis, 0)
        v2 = (1.73205 * dis, -1.0000000000001 * dis, 0
              )  # due to numerical issues
        v3 = (-1.73205 * dis, -1 * dis, 0)
        pt1 = add_vectors(centpt, v1)
        pt2 = add_vectors(centpt, v2)
        pt3 = add_vectors(centpt, v3)
        if ccw:
            return pt1, pt3, pt2
        return pt1, pt2, pt3

    mesh = Mesh()

    # to avoid numerical issues for perfectly structured point sets
    points = [(point[0] + random.uniform(-tiny, tiny),
               point[1] + random.uniform(-tiny, tiny), 0.0)
              for point in points]

    # create super triangle
    pt1, pt2, pt3 = super_triangle(points)

    # add super triangle vertices to mesh
    n = len(points)
    super_keys = n, n + 1, n + 2

    mesh.add_vertex(super_keys[0], {'x': pt1[0], 'y': pt1[1], 'z': pt1[2]})
    mesh.add_vertex(super_keys[1], {'x': pt2[0], 'y': pt2[1], 'z': pt2[2]})
    mesh.add_vertex(super_keys[2], {'x': pt3[0], 'y': pt3[1], 'z': pt3[2]})

    mesh.add_face(super_keys)

    # iterate over points
    for key, point in enumerate(points):
        # newtris should be intialised here

        # check in which triangle this point falls
        for fkey in list(mesh.faces()):
            abc = mesh.face_coordinates(fkey)

            if is_point_in_triangle_xy(point, abc, True):
                # generate 3 new triangles (faces) and delete surrounding triangle
                key, newtris = mesh.insert_vertex(fkey,
                                                  key=key,
                                                  xyz=point,
                                                  return_fkeys=True)
                break

        while newtris:
            fkey = newtris.pop()

            face = mesh.face_vertices(fkey)
            i = face.index(key)
            u = face[i - 2]
            v = face[i - 1]

            nbr = mesh.halfedge[v][u]

            if nbr is not None:
                a, b, c = mesh.face_coordinates(nbr)
                circle = circle_from_points_xy(a, b, c)

                if is_point_in_circle_xy(point, circle):
                    fkey, nbr = trimesh_swap_edge(mesh, u, v)
                    newtris.append(fkey)
                    newtris.append(nbr)

    # Delete faces adjacent to supertriangle
    for key in super_keys:
        mesh.delete_vertex(key)

    # Delete faces outside of boundary
    if boundary:
        for fkey in list(mesh.faces()):
            centroid = mesh.face_centroid(fkey)
            if not is_point_in_polygon_xy(centroid, boundary):
                mesh.delete_face(fkey)

    # Delete faces inside of inside boundaries
    if holes:
        for polygon in holes:
            for fkey in list(mesh.faces()):
                centroid = mesh.face_centroid(fkey)
                if is_point_in_polygon_xy(centroid, polygon):
                    mesh.delete_face(fkey)

    return [mesh.face_vertices(fkey) for fkey in mesh.faces()]
Exemple #3
0
def voronoi_from_delaunay(delaunay):
    """Construct the Voronoi dual of the triangulation of a set of points.

    Parameters
    ----------
    delaunay : Mesh
        A delaunay mesh.

    Returns
    -------
    Mesh
        The corresponding voronoi mesh.

    Warning
    -------
    This function does not work properly if all vertices of the delaunay
    are on the boundary.

    Example
    -------
    .. plot::
        :include-source:

        from compas.datastructures import Mesh
        from compas.topology import trimesh_remesh
        from compas.topology import delaunay_from_points
        from compas.topology import voronoi_from_delaunay

        from compas.geometry import pointcloud_xy

        from compas.plotters import MeshPlotter

        points = pointcloud_xy(10, (0, 10))
        faces = delaunay_from_points(points)
        delaunay = Mesh.from_vertices_and_faces(points, faces)

        trimesh_remesh(delaunay, 1.0, allow_boundary_split=True)

        points = [delaunay.vertex_coordinates(key) for key in delaunay.vertices()]
        faces = delaunay_from_points(points)
        delaunay = Mesh.from_vertices_and_faces(points, faces)

        voronoi  = voronoi_from_delaunay(delaunay)

        lines = []
        for u, v in voronoi.edges():
            lines.append({
                'start': voronoi.vertex_coordinates(u, 'xy'),
                'end'  : voronoi.vertex_coordinates(v, 'xy'),
                'width': 1.0
            })

        plotter = MeshPlotter(delaunay, figsize=(10, 6))

        plotter.draw_lines(lines)

        plotter.draw_vertices(
            radius=0.075,
            facecolor={key: '#0092d2' for key in delaunay.vertices() if key not in delaunay.vertices_on_boundary()})

        plotter.draw_edges(color='#cccccc')

        plotter.show()

    """
    voronoi = mesh_dual(delaunay)

    for key in voronoi.vertices():
        a, b, c = delaunay.face_coordinates(key)
        center, radius, normal = circle_from_points_xy(a, b, c)
        voronoi.vertex[key]['x'] = center[0]
        voronoi.vertex[key]['y'] = center[1]
        voronoi.vertex[key]['z'] = center[2]

    return voronoi
def mesh_delaunay_from_points(points, polygon=None, polygons=None):
    """Computes the delaunay triangulation for a list of points.

    Parameters:
        points (sequence of tuple): XYZ coordinates of the original points.
        polygon (sequence of tuples): list of ordered points describing the outer boundary (optional)
        polygons (list of sequences of tuples): list of polygons (ordered points describing internal holes (optional)

    Returns:
        list of lists: list of faces (face = list of vertex indices as integers)

    References:
        Sloan, S. W. (1987) A fast algorithm for constructing Delaunay triangulations in the plane

    Example:

        .. plot::
            :include-source:

            import compas
            from compas.datastructures.mesh import Mesh
            from compas.visualization.plotters import MeshPlotter
            from compas.datastructures.mesh.algorithms import mesh_delaunay_from_points

            mesh = Mesh.from_obj(compas.get_data('faces.obj'))

            vertices = [mesh.vertex_coordinates(key) for key in mesh]
            faces = mesh_delaunay_from_points(vertices)

            delaunay = Mesh.from_vertices_and_faces(vertices, faces)

            plotter = MeshPlotter(delaunay)

            plotter.draw_vertices(radius=0.1)
            plotter.draw_faces()

            plotter.show()

    """
    def super_triangle(coords):
        centpt = centroid_points(coords)
        bbpts = bounding_box(coords)
        dis = distance_point_point(bbpts[0], bbpts[2])
        dis = dis * 300
        v1 = (0 * dis, 2 * dis, 0)
        v2 = (1.73205 * dis, -1.0000000000001 * dis, 0
              )  # due to numerical issues
        v3 = (-1.73205 * dis, -1 * dis, 0)
        pt1 = add_vectors(centpt, v1)
        pt2 = add_vectors(centpt, v2)
        pt3 = add_vectors(centpt, v3)
        return pt1, pt2, pt3

    mesh = Mesh()

    # to avoid numerical issues for perfectly structured point sets
    tiny = 1e-8
    pts = [(point[0] + random.uniform(-tiny, tiny),
            point[1] + random.uniform(-tiny, tiny), 0.0) for point in points]

    # create super triangle
    pt1, pt2, pt3 = super_triangle(points)

    # add super triangle vertices to mesh
    n = len(points)
    super_keys = n, n + 1, n + 2

    mesh.add_vertex(super_keys[0], {'x': pt1[0], 'y': pt1[1], 'z': pt1[2]})
    mesh.add_vertex(super_keys[1], {'x': pt2[0], 'y': pt2[1], 'z': pt2[2]})
    mesh.add_vertex(super_keys[2], {'x': pt3[0], 'y': pt3[1], 'z': pt3[2]})

    mesh.add_face(super_keys)

    # iterate over points
    for i, pt in enumerate(pts):
        key = i

        # check in which triangle this point falls
        for fkey in list(mesh.faces()):
            # abc = mesh.face_coordinates(fkey) #This is slower
            # This is faster:
            keya, keyb, keyc = mesh.face_vertices(fkey)

            dicta = mesh.vertex[keya]
            dictb = mesh.vertex[keyb]
            dictc = mesh.vertex[keyc]

            a = [dicta['x'], dicta['y']]
            b = [dictb['x'], dictb['y']]
            c = [dictc['x'], dictc['y']]

            if is_point_in_triangle_xy(pt, [a, b, c]):
                # generate 3 new triangles (faces) and delete surrounding triangle
                newtris = mesh.insert_vertex(fkey, key=key, xyz=pt)
                break

        while newtris:
            fkey = newtris.pop()

            # get opposite_face
            keys = mesh.face_vertices(fkey)
            s = list(set(keys) - set([key]))
            u, v = s[0], s[1]
            fkey1 = mesh.halfedge[u][v]

            if fkey1 != fkey:
                fkey_op, u, v = fkey1, u, v
            else:
                fkey_op, u, v = mesh.halfedge[v][u], u, v

            if fkey_op:
                keya, keyb, keyc = mesh.face_vertices(fkey_op)
                dicta = mesh.vertex[keya]
                a = [dicta['x'], dicta['y']]
                dictb = mesh.vertex[keyb]
                b = [dictb['x'], dictb['y']]
                dictc = mesh.vertex[keyc]
                c = [dictc['x'], dictc['y']]

                circle = circle_from_points_xy(a, b, c)

                if is_point_in_circle_xy(pt, circle):
                    fkey, fkey_op = trimesh_swap_edge(mesh, u, v)
                    newtris.append(fkey)
                    newtris.append(fkey_op)

    # Delete faces adjacent to supertriangle
    for key in super_keys:
        mesh.remove_vertex(key)

    # Delete faces outside of boundary
    if polygon:
        for fkey in list(mesh.faces()):
            cent = mesh.face_centroid(fkey)
            if not is_point_in_polygon_xy(cent, polygon):
                mesh.delete_face(fkey)

    # Delete faces inside of inside boundaries
    if polygons:
        for polygon in polygons:
            for fkey in list(mesh.faces()):
                cent = mesh.face_centroid(fkey)
                if is_point_in_polygon_xy(cent, polygon):
                    mesh.delete_face(fkey)

    return [[int(key) for key in mesh.face_vertices(fkey, True)]
            for fkey in mesh.faces()]
def mesh_voronoi_from_points(points,
                             boundary=None,
                             holes=None,
                             return_delaunay=False):
    """Construct the Voronoi dual of the triangulation of a set of points.

    Parameters:
        points
        boundary
        holes
        return_delaunay

    Example:

        .. plot::
            :include-source:

            from numpy import random
            from numpy import hstack
            from numpy import zeros

            from compas.datastructures.mesh import Mesh
            from compas.visualization.plotters import MeshPlotter
            from compas.datastructures.mesh.algorithms import trimesh_optimise_topology
            from compas.datastructures.mesh.algorithms import mesh_delaunay_from_points
            from compas.datastructures.mesh.algorithms import mesh_voronoi_from_points

            points = hstack((10.0 * random.random_sample((20, 2)), zeros((20, 1)))).tolist()
            mesh = Mesh.from_vertices_and_faces(points, mesh_delaunay_from_points(points))

            trimesh_optimise_topology(mesh, 1.0, allow_boundary_split=True)

            points = [mesh.vertex_coordinates(key) for key in mesh]

            voronoi, delaunay = mesh_voronoi_from_points(points, return_delaunay=True)

            lines = []
            for u, v in voronoi.edges():
                lines.append({
                    'start': voronoi.vertex_coordinates(u, 'xy'),
                    'end'  : voronoi.vertex_coordinates(v, 'xy')
                })

            boundary = set(delaunay.vertices_on_boundary())

            plotter = MeshPlotter(delaunay)

            plotter.draw_xlines(lines)

            plotter.draw_vertices(radius=0.075, facecolor={key: '#0092d2' for key in delaunay if key not in boundary})
            plotter.draw_edges(color='#cccccc')

            plotter.show()

    """
    delaunay = Mesh.from_vertices_and_faces(points,
                                            mesh_delaunay_from_points(points))
    voronoi = mesh_dual(delaunay)
    for key in voronoi:
        a, b, c = delaunay.face_coordinates(key)
        center, radius, normal = circle_from_points_xy(a, b, c)
        voronoi.vertex[key]['x'] = center[0]
        voronoi.vertex[key]['y'] = center[1]
        voronoi.vertex[key]['z'] = center[2]
    if return_delaunay:
        return voronoi, delaunay
    return voronoi
Exemple #6
0
def discretise_faces(vertices,
                     faces,
                     target,
                     min_angle=15,
                     factor=3,
                     iterations=100,
                     refine=True):
    """ Make an FE mesh from input coarse mesh data.

    Parameters
    ----------
    vertices : list
        Co-ordinates of coarse mesh vertices.
    faces : list
        Vertex numbers of each face of the coarse mesh.
    target : float
        Target length of each triangle.
    min_angle : float
        Minimum internal angle of triangles.
    factor : float
        Factor on the maximum area of each triangle.
    iterations : int
        Number of iterations per face.
    refine : bool
        Refine beyond Delaunay.

    Returns
    -------
    list
        Vertices of discretised faces.
    list
        Triangles of discretised faces.

    """

    points_all = []
    faces_all = []

    Amax = factor * 0.5 * target**2

    for count, face in enumerate(faces):
        print('Face {0}/{1}'.format(count + 1, len(faces)))

        # Seed

        face.append(face[0])
        points = []
        for u, v in zip(face[:-1], face[1:]):
            sp = vertices[u]
            ep = vertices[v]
            vec = subtract_vectors(ep, sp)
            l = length_vector(vec)
            n = max([1, int(l / target)])
            for j in range(n):
                points.append(add_vectors(sp, scale_vector(vec, j / n)))

        # Starting orientation

        centroid = centroid_points(points)
        vec1 = subtract_vectors(points[1], points[0])
        vecc = subtract_vectors(centroid, points[0])
        vecn = cross_vectors(vec1, vecc)

        # Rotate about x

        points = array(points).transpose()
        phi = -arctan2(vecn[2], vecn[1]) + pi / 2
        Rx = array([[1., 0., 0.], [0., cos(phi), -sin(phi)],
                    [0., sin(phi), cos(phi)]])
        vecn_x = dot(Rx, array(vecn)[:, newaxis])
        points_x = dot(Rx, points)
        Rxinv = inv(Rx)

        # Rotate about y

        psi = +arctan2(vecn_x[2, 0], vecn_x[0, 0]) - pi / 2
        Ry = array([[cos(psi), 0., sin(psi)], [0., 1., 0.],
                    [-sin(psi), 0., cos(psi)]])
        points_y = dot(Ry, points_x)
        Ryinv = inv(Ry)

        # Store

        Vs = points_y.transpose()
        DTs = Delaunay(Vs[:, :2], furthest_site=False, incremental=False)
        tris = DTs.simplices
        points_xs = dot(Ryinv, Vs.transpose())
        points_new = [list(i) for i in list(dot(Rxinv, points_xs).transpose())]
        faces_new = [[int(i) for i in tri] for tri in list(tris)]

        # Refine

        if refine:

            V = points_y.transpose()
            z = float(V[0, 2])

            it = 0
            while it < iterations:
                DT = Delaunay(V[:, :2], furthest_site=False, incremental=False)
                tris = DT.simplices
                for u, v, w in tris:
                    p1 = [float(i) for i in V[u, :2]]
                    p2 = [float(i) for i in V[v, :2]]
                    p3 = [float(i) for i in V[w, :2]]
                    # th1 = angles_points_xy(p1, p2, p3)[0] * 180 / pi
                    # th2 = angles_points_xy(p2, p3, p1)[0] * 180 / pi
                    # th3 = angles_points_xy(p3, p1, p2)[0] * 180 / pi
                    # print(th1, th2, th3)  ] leads to some 0 and 180
                    # thm = min([th1, th2, th3])
                    res = circle_from_points_xy(p1, p2, p3)
                    if res:
                        c, r, _ = res
                        c[2] = z
                        A = area_polygon_xy([p1, p2, p3])
                        # if (thm < min_angle) or (A > Amax):
                        if A > Amax:
                            dist = distance_matrix(array([c]),
                                                   V,
                                                   threshold=10**5)
                            ins = len(dist[dist <= r])
                            if ins <= 3:
                                V = vstack([V, array([c])])
                                break
                    else:
                        continue
                it += 1

            print('Iterations {0}'.format(it))

            points_x = dot(Ryinv, V.transpose())
            points_new = [
                list(i) for i in list(dot(Rxinv, points_x).transpose())
            ]
            faces_new = [[int(i) for i in tri] for tri in list(tris)]

        points_all.append(points_new)
        faces_all.append(faces_new)

    return points_all, faces_all