def face_subdiv_frame(self, fkey, rel_pos=None, move_z=None, **kwattr): if rel_pos == 1 or rel_pos == [1, 1, 1]: return self.face_subdiv_pyramid(fkey, move_z=move_z, **kwattr) face_center_pt = Point(*self.face_center(fkey)) face_normal = Vector(*self.face_normal(fkey)) face_coordinates = self.face_coordinates(fkey) face_halfedges = self.face_halfedges(fkey) self.delete_face(fkey) if move_z is None: move_z = cycle(to_list(0)) if not isinstance(move_z, cycle): move_z = cycle(to_list(move_z)) if rel_pos is None: rel_pos = cycle(to_list(0.5)) if not isinstance(rel_pos, cycle): rel_pos = cycle(to_list(rel_pos)) new_vkeys = [] for x, y, z in face_coordinates: pt = Point(x, y, z) factor = next(rel_pos) v = face_center_pt - pt pt += v * factor if move_z: z_factor = next(move_z) pt += face_normal * z_factor new_vkeys.append(self.add_vertex(x=pt.x, y=pt.y, z=pt.z)) new_fkeys = [] for i, uv in enumerate(face_halfedges): u, v = uv vkeys = [] vkeys.append(u) vkeys.append(v) vkeys.append(new_vkeys[(i + 1) % len(new_vkeys)]) vkeys.append(new_vkeys[i]) new_fkeys.append(self.add_face(vkeys)) # add new center face new_fkeys.append(self.add_face(new_vkeys)) return new_vkeys, new_fkeys
def _get_next_cycle(var): var = next(var) if hasattr(var, "__next__"): return var if not isinstance(var, Sequence): var = to_list(var) return cycle(var)
def ok_subd( self, old_vkeys, new_vkeys, max_degree=None, min_angle=None, edge_length_domain=None, **kwargs ): new_vkeys = to_list(new_vkeys) if max_degree: if not self.ok_degree(old_vkeys, max_degree): return False if min_angle: if not self.ok_edges_angles(flatten([old_vkeys + new_vkeys]), min_angle): return False if edge_length_domain: ekeys = [self.connected_edges(vkey) for vkey in new_vkeys] ekeys = flatten(ekeys) uv_sets = set(frozenset((u, v)) for u, v in ekeys) # unique edges if not self.ok_edge_length(uv_sets, edge_length_domain): return False return True
def connect_nodes( self, start_nkey, end_nkeys, max_degree=None, min_angle=None, max_n_conn=None, ): """Create a line node to node.""" u = start_nkey end_nkeys = to_list(end_nkeys) n_conns = len(list(self.nodes_where({"created_from": self.FROM_CONN}))) for v in end_nkeys: if v is None: continue if max_n_conn: if n_conns > max_n_conn: break # max_degree-1 because we will add one if self.ok_conn(u, v, max_degree - 1, min_angle): self.add_edge( u, v, created_from=self.FROM_CONN, # parent_fkey=(start_fkey, end_fkey), ) n_conns += 1
def ok_degree(self, keys, max_degree): if isinstance(self, Network): degree_func = self.degree else: degree_func = self.vertex_degree keys = to_list(keys) for key in keys: if degree_func(key) > max_degree: return False return True
def _move_center_pt(self, pt, fkey, rel_pos, move_z): if not rel_pos and not move_z: return pt if rel_pos: rel_pos = cycle(to_list(rel_pos)) for v_xyz in self.face_coordinates(fkey): v = pt - Point(*v_xyz) factor = next(rel_pos) pt += v * factor if move_z: # set up max dist normal = Vector(*self.face_normal(fkey, unitized=True)) z_vec = normal * move_z pt += z_vec return pt
def ok_subd( self, old_vkeys, new_vkeys, new_fkeys, degree_domain=None, min_angle=None, edge_length_domain=None, max_edge_ratio_diff=None, **kwargs ): new_vkeys = to_list(new_vkeys) if degree_domain: if not self.ok_degrees(old_vkeys, degree_domain): # print("Not ok_subd due to vertex degrees") return False if min_angle: if not self.ok_edges_angles(flatten([old_vkeys + new_vkeys]), min_angle): # print("Not ok_subd due to edge angles.") return False if edge_length_domain: ekeys = [self.connected_edges(vkey) for vkey in new_vkeys] ekeys = flatten(ekeys) uv_sets = set(frozenset((u, v)) for u, v in ekeys) # unique edges if not self.ok_edge_lengths(uv_sets, edge_length_domain): # print("Not ok_subd due to edge length constraints.") return False if max_edge_ratio_diff: for fkey in new_fkeys: if not self.ok_edge_length_ratios(fkey, max_edge_ratio_diff): return False return True
def face_subdiv_frame(self, fkey, rel_pos=None, move_z=None, **kwattr): """Subdivide a face by offsetting its edges inwards Creates ``verts+1`` new faces. Parameters ---------- fkey : :obj:`int` rel_pos: :obj:`list` of :obj:`float` rel_pos: :obj:`float` Returns ------- :obj:`list` of :obj:`int` Keys of newly created vertices. :obj:`list` of :obj:`int` Keys of newly created faces. :obj:`tuple` of :obj:`int` and :obj:`tuple` of :obj:`int` Face keys of removed faces and their vertex keys. """ if rel_pos == 1 or rel_pos == [1, 1, 1]: return self.face_subdiv_pyramid(fkey, move_z=move_z, **kwattr) face_center_pt = Point(*self.face_center(fkey)) face_normal = Vector(*self.face_normal(fkey)) face_coordinates = self.face_coordinates(fkey) face_halfedges = self.face_halfedges(fkey) deleted_faces = [(fkey, self.face_vertices(fkey))] self.delete_face(fkey) if move_z is None: move_z = cycle(to_list(0)) if not isinstance(move_z, cycle): move_z = cycle(to_list(move_z)) if rel_pos is None: rel_pos = cycle(to_list(0.5)) if not isinstance(rel_pos, cycle): rel_pos = cycle(to_list(rel_pos)) new_vkeys = [] for x, y, z in face_coordinates: pt = Point(x, y, z) factor = next(rel_pos) v = face_center_pt - pt pt += v * factor if move_z: z_factor = next(move_z) pt += face_normal * z_factor new_vkeys.append(self.add_vertex(x=pt.x, y=pt.y, z=pt.z)) new_fkeys = [] for i, uv in enumerate(face_halfedges): u, v = uv vkeys = [] vkeys.append(u) vkeys.append(v) vkeys.append(new_vkeys[(i + 1) % len(new_vkeys)]) vkeys.append(new_vkeys[i]) new_fkeys.append(self.add_face(vkeys)) # add new center face new_fkeys.append(self.add_face(new_vkeys)) return new_vkeys, new_fkeys, deleted_faces
def subdiv_faces( self, fkeys, n_iters=None, scheme=[0], rel_pos=None, move_z=None, max_iters=None, **kwargs ): subdiv_funcs = [ self.face_subdiv_pyramid, self.face_subdiv_mid2center, self.face_subdiv_mids2mids, self.face_subdiv_split_in_half, self.face_subdiv_split_mid2mid, self.face_subdiv_frame, ] repeating_n_iters = cycle(n_iters) if n_iters else None subdiv_cycler = cycle(scheme) repeating_rel_pos = cycle(to_list(rel_pos)) if rel_pos else None repeating_move_z = cycle(to_list(move_z)) if move_z else None fkeys = set(fkeys) while len(fkeys) > 0: fkey = fkeys.pop() if n_iters: n_iters = next(repeating_n_iters) elif self.face_attribute(fkey, "n_iters"): n_iters = self.face_attribute(fkey, "n_iters") else: n_iters = 0 if max_iters: n_iters = n_iters if n_iters < max_iters else max_iters if repeating_rel_pos: kwargs.update({"rel_pos": next(repeating_rel_pos)}) if repeating_move_z: kwargs.update({"move_z": next(repeating_move_z)}) subdiv_func_per_parent_face = self._get_next_cycle(subdiv_cycler) i = 0 next_to_subd = set([fkey]) while i < n_iters: to_subd = next_to_subd next_to_subd = set() while len(to_subd) > 0: parent_fkey = to_subd.pop() parent_face_verts = self.face_vertices(parent_fkey) part = self.face_attribute(parent_fkey, "part") # parent_attrs = self.face_attributes(parent_fkey) subd_func_idx = next(subdiv_func_per_parent_face) subdiv_func = subdiv_funcs[subd_func_idx] new_vkeys, new_fkeys, deleted_faces = subdiv_func( parent_fkey, **kwargs ) for vkey in new_vkeys: self.vertex_attribute(vkey, "part", part) if not self.ok_subd( parent_face_verts, new_vkeys, new_fkeys, **kwargs ): self.undo_face_subd( new_fkeys, new_vkeys, deleted_faces, ) continue deleted_fkeys = set([fkey for fkey, _ in deleted_faces]) # add new fkeys to set for next iteration next_to_subd |= set(new_fkeys) next_to_subd -= deleted_fkeys # remove deleted faces from input list of fkeys fkeys -= deleted_fkeys # remove deleted faces from list for this iteration to_subd -= deleted_fkeys i += 1 self.cull_vertices()
def ok_edges_angles(self, keys, min_angle, **kwargs): vkeys = to_list(keys) for vkey in vkeys: if not self.ok_edge_angles(vkey, min_angle, **kwargs): return False return True
def subdiv_faces( self, fkeys, n_iters, scheme=[0], rel_pos=None, move_z=None, **kwargs ): subdiv_funcs = [ self.face_subdiv_pyramid, self.face_subdiv_mid_cent, self.face_subdiv_mids, self.face_subdiv_split, self.face_subdiv_frame, ] repeating_n_iters = cycle(n_iters) subdiv_cycler = cycle(scheme) repeating_rel_pos = cycle(to_list(rel_pos)) if rel_pos else None repeating_move_z = cycle(to_list(move_z)) if move_z else None new_subd_fkeys = [] for fkey in fkeys: n_iters = next(repeating_n_iters) if repeating_rel_pos: kwargs.update({"rel_pos": next(repeating_rel_pos)}) if repeating_move_z: kwargs.update({"move_z": next(repeating_move_z)}) subdiv_parent_face = next(subdiv_cycler) if not hasattr(subdiv_parent_face, "__next__"): subdiv_parent_face = cycle(to_list(subdiv_parent_face)) i = 0 next_to_subd = [fkey] while i < n_iters: to_subd = next_to_subd next_to_subd = [] subdiv_iteration = next(subdiv_parent_face) if not hasattr(subdiv_iteration, "__next__"): subdiv_iteration = cycle(to_list(subdiv_iteration)) for parent_fkey in to_subd: parent_face_verts = self.face_vertices(parent_fkey) parent_attrs = self.face_attributes(parent_fkey) subdiv_child_face = next(subdiv_iteration) subdiv_func = subdiv_funcs[subdiv_child_face] new_vkeys, new_fkeys = subdiv_func(parent_fkey, **kwargs) if not self.ok_subd(parent_face_verts, new_vkeys, **kwargs): self.undo_face_subd( new_fkeys, parent_fkey, parent_face_verts, parent_attrs ) continue next_to_subd += new_fkeys new_subd_fkeys += new_fkeys i += 1 self.cull_vertices() return [fkey for fkey in new_fkeys if self.has_face(fkey)]
def ok_degrees(self, keys, degree_domain): keys = to_list(keys) for key in keys: if not self.ok_degree(key, degree_domain): return False return True