def 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.datastructures.mesh.algorithms import delaunay_from_points mesh = Mesh.from_obj(compas.get_data('faces.obj')) vertices = [mesh.vertex_coordinates(key) for key in mesh] faces = delaunay_from_points(vertices) delaunay = Mesh.from_vertices_and_faces(vertices, faces) delaunay.plot( vertexsize=0.1 ) """ 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_2d(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_2d(a, b, c) if is_point_in_circle_2d(pt, circle): fkey, fkey_op = swap_edge_trimesh(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_2d(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_2d(cent, polygon): mesh.delete_face(fkey) return [[int(key) for key in mesh.face_vertices(fkey, True)] for fkey in mesh.faces()]
def delaunay_from_points_2(points, boundary=None, holes=None, tiny=1e-12): """Computes the delaunay triangulation for a list of points. Difference from compas version: faces outside the boundaries are detected based on the position of their circumcircle. 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.geometry import pointcloud_xy from compas.datastructures import Mesh from compas.topology import delaunay_from_points from compas.plotters import MeshPlotter points = pointcloud_xy(10, (0, 10)) 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 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 = mesh.swap_edge_tri(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) centre, radius, normal = face_circle(mesh, fkey) if not is_point_in_polygon_xy(centre, 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) centre, radius, normal = face_circle(mesh, fkey) if is_point_in_polygon_xy(centre, polygon): mesh.delete_face(fkey) return [mesh.face_vertices(fkey) for fkey in mesh.faces()]