def single_face_delaunay(face_verts, add_verts, epsilon=1e-6, exclude_boundary=True): n = len(face_verts) face = list(range(n)) edges = [(i, i + 1) for i in range(n - 1)] + [(n - 1, 0)] plane = linear_approximation(face_verts).most_similar_plane() face_verts_2d = [plane.point_uv_projection(v) for v in face_verts] if exclude_boundary: add_verts = [ v for v in add_verts if not is_on_face_edge(v, face_verts, epsilon) ] add_verts_2d = [plane.point_uv_projection(v) for v in add_verts] TRIANGLES = 1 res = delaunay_2d_cdt(face_verts_2d + add_verts_2d, edges, [face], TRIANGLES, epsilon) new_verts_2d = res[0] new_edges = res[1] new_faces = res[2] new_add_verts = [ tuple(plane.evaluate(p[0], p[1], normalize=True)) for p in new_verts_2d[n:] ] return face_verts + new_add_verts, new_edges, new_faces
def delaunay_triangulate(context, obj, output_type, epsilon): depsgraph = context.evaluated_depsgraph_get() obj_eval = obj.evaluated_get(depsgraph) mesh_eval = obj_eval.to_mesh() vert_coords = [vtx.co.to_2d() for vtx in mesh_eval.vertices] edges = [edge.vertices for edge in mesh_eval.edges] faces = [[mesh_eval.loops[loop_index].vertex_index for loop_index in face.loop_indices] for face in mesh_eval.polygons] (out_coords, out_edges, out_faces, orig_verts, orig_edges, orig_faces) = delaunay_2d_cdt(vert_coords, edges, faces, output_type, epsilon) obj_eval.to_mesh_clear() #bpy.data.meshes.remove(mesh_eval) return ([co.to_3d() for co in out_coords], out_edges, out_faces)
def delaunay_triangulatrion(samples_u, samples_v, us_list, vs_list, u_coeff, v_coeff, epsilon): if delaunay_2d_cdt is None: # Pure-python implementation points_uv = [ Site(u * u_coeff, v * v_coeff) for u, v in zip(us_list, vs_list) ] faces = computeDelaunayTriangulation(points_uv) return faces else: points_scaled = [(u * u_coeff, v * v_coeff) for u, v in zip(us_list, vs_list)] INNER = 1 # delaunay_2d_cdt function wont' work if we do not provide neither edges nor faces. # So let's just construct the outer faces of the rectangular grid # (indices in `edges' depend on the fact that in `adaptive_subdivide` we # add randomly generated points to the end of us_list/vs_list). edges = make_outer_edges(samples_v, samples_u) vert_coords, edges, faces, orig_verts, orig_edges, orig_faces = delaunay_2d_cdt( points_scaled, edges, [], INNER, epsilon) return faces
def execute(self, context): w = context.window w.cursor_set('WAIT') t0 = perf_clock() #Get selected obj objs = context.selected_objects if len(objs) == 0 or len(objs) > 1: self.report({'INFO'}, "Selection is empty or too much object selected") return {'CANCELLED'} obj = objs[0] if obj.type != 'MESH': self.report({'INFO'}, "Selection isn't a mesh") return {'CANCELLED'} #Get points coodinates #bpy.ops.object.transform_apply(rotation=True, scale=True) r = obj.rotation_euler s = obj.scale mesh = obj.data if NATIVE: ''' Use native Delaunay triangulation function : delaunay_2d_cdt(verts, edges, faces, output_type, epsilon) >> [verts, edges, faces, orig_verts, orig_edges, orig_faces] The three returned orig lists give, for each of verts, edges, and faces, the list of input element indices corresponding to the positionally same output element. For edges, the orig indices start with the input edges and then continue with the edges implied by each of the faces (n of them for an n-gon). Output type : # 0 => triangles with convex hull. # 1 => triangles inside constraints. # 2 => the input constraints, intersected. # 3 => like 2 but with extra edges to make valid BMesh faces. ''' log.info("Triangulate {} points...".format(len(mesh.vertices))) verts, edges, faces, overts, oedges, ofaces = delaunay_2d_cdt( [v.co.to_2d() for v in mesh.vertices], [], [], 0, 0.1) verts = [(v.x, v.y, mesh.vertices[overts[i][0]].co.z) for i, v in enumerate(verts)] #retrieve z values log.info("Getting {} triangles".format(len(faces))) log.info("Create mesh...") tinMesh = bpy.data.meshes.new("TIN") tinMesh.from_pydata(verts, edges, faces) tinMesh.update() else: vertsPts = [vertex.co for vertex in mesh.vertices] #Remove duplicate verts = [[vert.x, vert.y, vert.z] for vert in vertsPts] nDupli, nZcolinear = unique(verts) nVerts = len(verts) log.info("{} duplicates points ignored".format(nDupli)) log.info("{} z colinear points excluded".format(nZcolinear)) if nVerts < 3: self.report({'ERROR'}, "Not enough points") return {'CANCELLED'} #Check colinear xValues = [pt[0] for pt in verts] yValues = [pt[1] for pt in verts] if checkEqual(xValues) or checkEqual(yValues): self.report({'ERROR'}, "Points are colinear") return {'CANCELLED'} #Triangulate log.info("Triangulate {} points...".format(nVerts)) vertsPts = [Point(vert[0], vert[1], vert[2]) for vert in verts] faces = computeDelaunayTriangulation(vertsPts) faces = [ tuple(reversed(tri)) for tri in faces ] #reverse point order --> if all triangles are specified anticlockwise then all faces up log.info("Getting {} triangles".format(len(faces))) #Create new mesh structure log.info("Create mesh...") tinMesh = bpy.data.meshes.new("TIN") #create a new mesh tinMesh.from_pydata(verts, [], faces) #Fill the mesh with triangles tinMesh.update(calc_edges=True) #Update mesh with new data #Create an object with that mesh tinObj = bpy.data.objects.new("TIN", tinMesh) #Place object tinObj.location = obj.location.copy() tinObj.rotation_euler = r tinObj.scale = s #Update scene context.scene.collection.objects.link(tinObj) #Link object to scene context.view_layer.objects.active = tinObj tinObj.select_set(True) obj.select_set(False) #Report t = round(perf_clock() - t0, 2) msg = "{} triangles created in {} seconds".format(len(faces), t) self.report({'INFO'}, msg) #log.info(msg) #duplicate log return {'FINISHED'}