def singularity_polyedge_decomposition(self): """Returns a quad patch decomposition of the mesh based on the singularity polyedges, including boundaries and additionnal splits on the boundaries. Returns ------- list The polyedges forming the decomposition. """ if self.data['attributes']['polyedges'] == {}: self.collect_polyedges() polyedges = [polyedge for key, polyedge in self.polyedges(data=True) if (self.is_vertex_singular(polyedge[0]) or self.is_vertex_singular(polyedge[-1])) and not self.is_edge_on_boundary(polyedge[0], polyedge[1])] # split boundaries all_splits = [vkey for polyedge in polyedges for vkey in polyedge] for boundary in self.boundaries(): splits = [vkey for vkey in boundary if vkey in all_splits] new_splits = [] if len(splits) == 0: new_splits += [vkey for vkey in list(itemgetter(0, int(floor(len(boundary) / 3)), int(floor(len(boundary) * 2 / 3)))(boundary))] elif len(splits) == 1: i = boundary.index(splits[0]) new_splits += list(itemgetter(i - int(floor(len(boundary) * 2 / 3)), i - int(floor(len(boundary) / 3)))(boundary)) elif len(splits) == 2: one, two = list_split(boundary + boundary[:1], [boundary.index(vkey) for vkey in splits]) half = one if len(one) > len(two) else two new_splits.append(half[int(floor(len(half) / 2))]) for vkey in new_splits: for nbr in self.vertex_neighbors(vkey): if not self.is_edge_on_boundary(vkey, nbr): new_polyedge = self.collect_polyedge(vkey, nbr) polyedges.append(new_polyedge) all_splits = list(set(all_splits + new_polyedge)) break # add boundaries polyedges += [polyedge for key, polyedge in self.polyedges(data=True) if self.is_edge_on_boundary(polyedge[0], polyedge[1])] # get intersections between polyedges for split vertices = [vkey for polyedge in polyedges for vkey in set(polyedge)] split_vertices = [vkey for vkey in self.vertices() if vertices.count(vkey) > 1] # split singularity polyedges return [split_polyedge for polyedge in polyedges for split_polyedge in list_split(polyedge, [polyedge.index(vkey) for vkey in split_vertices if vkey in polyedge])]
def singularity_polyedges(self): """Collect the polyedges connected to singularities. Returns ------- list The polyedges connected to singularities. """ poles = set(self.poles()) # keep only polyedges connected to singularities or along the boundary polyedges = [ polyedge for key, polyedge in self.polyedges(data=True) if (self.is_vertex_singular(polyedge[0]) and not self.is_pole(polyedge[0])) or (self.is_vertex_singular( polyedge[-1]) and not self.is_pole(polyedge[-1])) or self.is_edge_on_boundary(polyedge[0], polyedge[1]) ] # get intersections between polyedges for split vertices = [vkey for polyedge in polyedges for vkey in set(polyedge)] split_vertices = [ vkey for vkey in self.vertices() if vertices.count(vkey) > 1 ] # split singularity polyedges return [ split_polyedge for polyedge in polyedges for split_polyedge in list_split(polyedge, [ polyedge.index(vkey) for vkey in split_vertices if vkey in polyedge ]) ]
def branches_splitting_collapsed_boundaries(self): """Add new branches to fix the problem of boundaries with less than three splits that would be collapsed in the decomposition mesh. Returns ------- new_branches : list List of polylines as list of point XYZ-coordinates. """ new_branches = [] all_splits = set( list(self.corner_vertices()) + list(self.split_vertices())) for polyedge in [bdry + bdry[0:] for bdry in self.boundaries()]: splits = set([vkey for vkey in polyedge if vkey in all_splits]) new_splits = [] if len(splits) == 0: new_splits += [ vkey for vkey in list( itemgetter(0, int(floor(len(polyedge) / 3)), int(floor(len(polyedge) * 2 / 3)))(polyedge)) ] elif len(splits) == 1: i = polyedge.index(splits[0]) new_splits += list( itemgetter(i - int(floor(len(polyedge) * 2 / 3)), i - int(floor(len(polyedge) / 3)))(polyedge)) elif len(splits) == 2: one, two = list_split( polyedge, [polyedge.index(vkey) for vkey in splits]) half = one if len(one) > len(two) else two new_splits.append(half[int(floor(len(half) / 2))]) for vkey in new_splits: fkey = list(self.vertex_faces(vkey))[0] for edge in self.face_halfedges(fkey): if vkey in edge and not self.is_edge_on_boundary(*edge): new_branches += [[ trimesh_face_circle(self, fkey)[0], self.vertex_coordinates(vkey_2) ] for vkey_2 in edge] all_splits.update(edge) break return new_branches
def branches_boundary(self): """Get new branch polylines from the Delaunay mesh boundaries split at the corner and plit vertices. Not part of the topological skeleton. Returns ------- list List of polylines as list of point XYZ-coordinates. """ boundaries = [bdry + bdry[0:] for bdry in self.boundaries()] splits = self.corner_vertices() + self.split_vertices() split_boundaries = [ split_boundary for boundary in boundaries for split_boundary in list_split(boundary, [ boundary.index(split) for split in splits if split in boundary ]) ] return [[self.vertex_coordinates(vkey) for vkey in boundary] for boundary in split_boundaries]
def func_1(mesh, fix_xyz, kmax, damping): # geometrical processing: smooth to widen the strip with constraints at # kinks and along boundaries def callback(k, args): mesh, fixed, split_boundaries, split_boundaries_geom = args for vkey in mesh.vertices_on_boundary(): if vkey not in fixed: for i, boundary in enumerate(split_boundaries): if vkey in boundary: xyz, dist = closest_point_on_polyline( split_boundaries_geom[i], mesh.vertex_coordinates(vkey)) attr = mesh.vertex[vkey] attr['x'], attr['y'], attr['z'] = xyz break fix_map = {geometric_key(xyz): [] for xyz in fix_xyz} for vkey in mesh.vertices(): geom_key = geometric_key(mesh.vertex_coordinates(vkey)) if geom_key in fix_map: fix_map[geom_key].append(vkey) fixed = [] for vertices in fix_map.values(): boundary_vertices = [ vkey for vkey in vertices if mesh.is_vertex_on_boundary(vkey) ] if len(boundary_vertices) == 0: print('not adapted to fixed non-boundary vertices') elif len(boundary_vertices) == 1: fixed += boundary_vertices else: corner_vertices = [ vkey for vkey in boundary_vertices if mesh.vertex_degree(vkey) == 2 ] if len(corner_vertices) == 1: fixed += corner_vertices else: pass #print('not generalised yet') split_boundaries = [] for boundary in mesh.boundaries(): boundary.append(boundary[0]) indices = [boundary.index(vkey) for vkey in fixed if vkey in boundary] split_boundaries += list_split(boundary, indices) split_boundaries_geom = { i: [mesh.vertex_coordinates(vkey) for vkey in boundary] for i, boundary in enumerate(split_boundaries) } callback_args = mesh, fixed, split_boundaries, split_boundaries_geom mesh_smooth_centroid(mesh, fixed, kmax=kmax, damping=damping, callback=callback, callback_args=callback_args)
def quadrangulate_face(mesh, fkey, sources): face_vertices = mesh.face_vertices(fkey)[:] # differentiate sources and non sources sources = [vkey for vkey in face_vertices if vkey in sources] non_sources = [vkey for vkey in face_vertices if vkey not in sources] new_sources = [] if len(non_sources) == 4: a, b, c, d = non_sources ab, bc, cd, da = list_split( face_vertices + face_vertices[:1], [face_vertices.index(vkey) for vkey in non_sources]) # add missing vertices for i, edges in enumerate([[ab, cd], [bc, da]]): uv, wx = edges # all cases if len(uv) == len(wx): # no subdivision needed continue elif len(uv) == 2 and len(wx) != 2: # subdivide uv n = len(wx) - len(uv) + 1 new_points = [ mesh.edge_point(uv[0], uv[1], float(k) / float(n)) for k in range(n + 1) ][1:-1] new_vertices = [ mesh.add_vertex(attr_dict={ xyz: value for xyz, value in zip(['x', 'y', 'z'], point) }) for point in new_points ] new_sources += new_vertices if i == 0: ab = [uv[0]] + new_vertices + [uv[-1]] elif i == 1: bc = [uv[0]] + new_vertices + [uv[-1]] update_adjacent_face(mesh, uv[1], uv[0], list(reversed(new_vertices))) elif len(uv) != 2 and len(wx) == 2: # subdivide wx n = len(uv) - len(wx) + 1 new_points = [ mesh.edge_point(wx[0], wx[1], float(k) / float(n)) for k in range(n + 1) ][1:-1] new_vertices = [ mesh.add_vertex(attr_dict={ xyz: value for xyz, value in zip(['x', 'y', 'z'], point) }) for point in new_points ] new_sources += new_vertices if i == 0: cd = [wx[0]] + new_vertices + [wx[-1]] elif i == 1: da = [wx[0]] + new_vertices + [wx[-1]] # update adjacent faces update_adjacent_face(mesh, wx[1], wx[0], list(reversed(new_vertices))) elif len(uv) != 2 and len(wx) != 2 and len(uv) != len(wx): pass # apply Takayama's work #print('not implemented yet') mesh.delete_face(fkey) discrete_coons_patch_mesh(mesh, ab, bc, list(reversed(cd)), list(reversed(da))) else: pass #print('not generalised yet') return new_sources
def automated_smoothing_constraints(mesh, points=None, curves=None, surface=None, mesh2=None): """Apply automatically point, curve and surface constraints to the vertices of a mesh to smooth. Parameters ---------- mesh : Mesh The mesh to apply the constraints to for smoothing. points : list List of XYZ coordinates on which to constrain mesh vertices. Default is None. curves : list List of RhinoCurve objects on which to constrain mesh vertices. Default is None. surface : RhinoSurface A RhinoSurface object on which to constrain mesh vertices. Default is None. mesh2 : RhinoMesh A RhinoMesh object on which to constrain mesh vertices. Default is None. Returns ------- constraints : dict A dictionary of mesh constraints for smoothing as vertex keys pointing to point, curve or surface objects. """ constraints = {} constrained_vertices = {} vertices = list(mesh.vertices()) vertex_coordinates = [ mesh.vertex_coordinates(vkey) for vkey in mesh.vertices() ] if points is not None and len(points) != 0: constrained_vertices.update({ vertices[closest_point_in_cloud(rs.PointCoordinates(point), vertex_coordinates)[2]]: point for point in points }) if mesh2 is not None: constraints.update({vkey: mesh2 for vkey in mesh.vertices()}) if surface is not None: constraints.update({vkey: surface for vkey in mesh.vertices()}) if curves is not None and len(curves) != 0: boundaries = [ split_boundary for boundary in mesh.boundaries() for split_boundary in list_split(boundary, [ boundary.index(vkey) for vkey in constrained_vertices.keys() if vkey in boundary ]) ] boundary_midpoints = [ Polyline([mesh.vertex_coordinates(vkey) for vkey in boundary]).point(t=.5) for boundary in boundaries ] curve_midpoints = [ rs.EvaluateCurve(curve, rs.CurveParameter(curve, .5)) for curve in curves ] midpoint_map = { i: closest_point_in_cloud(boundary_midpoint, curve_midpoints)[2] for i, boundary_midpoint in enumerate(boundary_midpoints) } constraints.update({ vkey: curves[midpoint_map[i]] for i, boundary in enumerate(boundaries) for vkey in boundary }) if points is not None: constraints.update(constrained_vertices) return constraints