def trivial_mesher(self): solids = self.inputs[self["shape_type"]].sv_get() verts = [] faces = [] for solid in solids: if self.shape_type == 'Solid': shape = solid else: shape = solid.face new_verts, new_edges, new_faces = mesh_from_solid_faces(shape) new_verts, new_edges, new_faces = recalc_normals( new_verts, new_edges, new_faces) verts.append(new_verts) faces.append(new_faces) return verts, faces
def trivial_mesher(self): """ this mode will produce a variety of polygon types (tris, quads, ngons...) """ solids = self.inputs[self["shape_type"]].sv_get() verts = [] faces = [] for solid in solids: if self.shape_type == 'Solid': shape = solid else: shape = solid.face new_verts, new_edges, new_faces = mesh_from_solid_faces(shape) new_verts, new_edges, new_faces = recalc_normals( new_verts, new_edges, new_faces) verts.append(new_verts) faces.append(new_faces) return verts, faces
def process(self): if not any(socket.is_linked for socket in self.outputs): return verts_in = self.inputs['Vertices'].sv_get(deepcopy=False) faces_in = self.inputs['Faces'].sv_get(deepcopy=False) sites_in = self.inputs['Sites'].sv_get(deepcopy=False) #thickness_in = self.inputs['Thickness'].sv_get() spacing_in = self.inputs['Spacing'].sv_get(deepcopy=False) verts_in = ensure_nesting_level(verts_in, 4) input_level = get_data_nesting_level(sites_in) sites_in = ensure_nesting_level(sites_in, 4) faces_in = ensure_nesting_level(faces_in, 4) #thickness_in = ensure_nesting_level(thickness_in, 2) spacing_in = ensure_nesting_level(spacing_in, 2) nested_output = input_level > 3 precision = 10 ** (-self.accuracy) verts_out = [] edges_out = [] faces_out = [] sites_out = [] for params in zip_long_repeat(verts_in, faces_in, sites_in, spacing_in): new_verts = [] new_edges = [] new_faces = [] new_sites = [] for verts, faces, sites, spacing in zip_long_repeat(*params): verts, edges, faces, sites = voronoi_on_mesh(verts, faces, sites, thickness=0, spacing = spacing, #clip_inner = self.clip_inner, clip_outer = self.clip_outer, do_clip=True, clipping=None, mode = self.mode, precision = precision) if self.mode == 'VOLUME' and self.normals: verts, edges, faces = recalc_normals(verts, edges, faces, loop=True) if self.join_mode == 'FLAT': new_verts.extend(verts) new_edges.extend(edges) new_faces.extend(faces) new_sites.extend(sites) elif self.join_mode == 'SEPARATE': new_verts.append(verts) new_edges.append(edges) new_faces.append(faces) new_sites.append(sites) else: # JOIN verts, edges, faces = mesh_join(verts, edges, faces) new_verts.append(verts) new_edges.append(edges) new_faces.append(faces) new_sites.append(sites) if nested_output: verts_out.append(new_verts) edges_out.append(new_edges) faces_out.append(new_faces) sites_out.append(new_sites) else: verts_out.extend(new_verts) edges_out.extend(new_edges) faces_out.extend(new_faces) sites_out.extend(new_sites) self.outputs['Vertices'].sv_set(verts_out) self.outputs['Edges'].sv_set(edges_out) self.outputs['Faces'].sv_set(faces_out)
def alpha_shape(verts, alpha, fix_normals=True, volume_threshold=1e-4): """ Compute the alpha shape (concave hull) of a set of 3D points. Parameters: verts - np.array of shape (n, 3) points. alpha - alpha value. return outer surface edge indices and triangle indices """ def calc_volume(tetra_verts): a = tetra_verts[:, 0, :] b = tetra_verts[:, 1, :] c = tetra_verts[:, 2, :] d = tetra_verts[:, 3, :] e1 = b - a e2 = c - a e3 = d - a e1 /= np.linalg.norm(e1, axis=1, keepdims=True) e2 /= np.linalg.norm(e2, axis=1, keepdims=True) e3 /= np.linalg.norm(e3, axis=1, keepdims=True) volume = np.cross(e1, e2) volume = (volume * e3).sum(axis=1) / 6 return abs(volume) alpha2 = alpha**2 tetra = Delaunay(verts) # Find radius of the circumsphere. # By definition, radius of the sphere fitting inside the tetrahedral needs # to be smaller than alpha value # http://mathworld.wolfram.com/Circumsphere.html tetra_verts = np.take(verts, tetra.simplices, axis=0) # (n_simplices, 4, 3) normsq = np.sum(tetra_verts**2, axis=2)[:, :, None] ones = np.ones((tetra_verts.shape[0], tetra_verts.shape[1], 1)) a = np.linalg.det(np.concatenate((tetra_verts, ones), axis=2)) Dx = np.linalg.det( np.concatenate((normsq, tetra_verts[:, :, [1, 2]], ones), axis=2)) Dy = -np.linalg.det( np.concatenate((normsq, tetra_verts[:, :, [0, 2]], ones), axis=2)) Dz = np.linalg.det( np.concatenate((normsq, tetra_verts[:, :, [0, 1]], ones), axis=2)) c = np.linalg.det(np.concatenate((normsq, tetra_verts), axis=2)) r2 = (Dx**2 + Dy**2 + Dz**2 - 4 * a * c) / (4 * np.abs(a)**2) small_radius = r2 < alpha2 volumes = calc_volume(tetra_verts) non_planar = volumes >= volume_threshold good = np.logical_and(small_radius, non_planar) # Find tetrahedrals tetras = tetra.simplices[good, :] # triangles triangle_combinations = np.array([(0, 1, 2), (0, 1, 3), (0, 2, 3), (1, 2, 3)]) triangles = tetras[:, triangle_combinations].reshape(-1, 3) triangles = np.sort(triangles, axis=1) # Remove triangles that occurs twice, because they are within shapes triangles_dict = defaultdict(int) for tri in triangles: triangles_dict[tuple(tri)] += 1 triangles = np.array( [tri for tri in triangles_dict if triangles_dict[tri] == 1]) #edges edges_comb = np.array([(0, 1), (0, 2), (1, 2)]) edges = triangles[:, edges_comb].reshape(-1, 2) edges = np.sort(edges, axis=1) edges = np.unique(edges, axis=0) edges = edges.tolist() triangles = triangles.tolist() used_vert_idxs = set() for triangle in triangles: used_vert_idxs.update(set(triangle)) verts_mask = [i in used_vert_idxs for i in range(len(verts))] verts, edges, faces = mask_vertices(verts, edges, triangles, verts_mask) if fix_normals: verts, edges, faces = recalc_normals(verts, edges, faces) return verts, edges, faces
def process(self): if not any(socket.is_linked for socket in self.outputs): return surface_in = self.inputs['Surface'].sv_get() uvpoints_in = self.inputs['UVPoints'].sv_get() maxsides_in = self.inputs['MaxSides'].sv_get() thickness_in = self.inputs['Thickness'].sv_get() clipping_in = self.inputs['Clipping'].sv_get() surface_in = ensure_nesting_level(surface_in, 2, data_types=(SvSurface, )) input_level = get_data_nesting_level(uvpoints_in) uvpoints_in = ensure_nesting_level(uvpoints_in, 4) maxsides_in = ensure_nesting_level(maxsides_in, 2) thickness_in = ensure_nesting_level(thickness_in, 2) clipping_in = ensure_nesting_level(clipping_in, 2) nested_output = input_level > 3 verts_out = [] edges_out = [] faces_out = [] uvverts_out = [] for params in zip_long_repeat(surface_in, uvpoints_in, maxsides_in, thickness_in, clipping_in): new_verts = [] new_edges = [] new_faces = [] new_uvverts = [] for surface, uvpoints, maxsides, thickness, clipping in zip_long_repeat( *params): if self.mode == 'UV': uvverts, verts, edges, faces = self.voronoi_uv( surface, uvpoints, maxsides) new_uvverts.append(uvverts) else: verts, edges, faces = voronoi_on_surface( surface, uvpoints, thickness, self.do_clip, clipping, self.mode == 'REGIONS') if (self.mode in {'RIDGES', 'REGIONS'} or self.make_faces) and self.normals: verts, edges, faces = recalc_normals( verts, edges, faces, loop=(self.mode in {'REGIONS', 'RIDGES'})) new_verts.append(verts) new_edges.append(edges) new_faces.append(faces) if self.mode in {'RIDGES', 'REGIONS'} and self.flat_output: new_verts = sum(new_verts, []) new_edges = sum(new_edges, []) new_faces = sum(new_faces, []) if nested_output: verts_out.append(new_verts) edges_out.append(new_edges) faces_out.append(new_faces) if self.mode == 'UV': uvverts_out.append(new_uvverts) else: verts_out.extend(new_verts) edges_out.extend(new_edges) faces_out.extend(new_faces) if self.mode == 'UV': uvverts_out.extend(new_uvverts) self.outputs['Vertices'].sv_set(verts_out) self.outputs['Edges'].sv_set(edges_out) self.outputs['Faces'].sv_set(faces_out) self.outputs['UVVertices'].sv_set(uvverts_out)