def assembly_hull(assembly, keys=None, unify=True): """Construct the convex hull of an assembly. Parameters ---------- assembly : Assembly The assembly data structure. keys: list, optional The identifiers of the blocks to include in the hull calculation. Defaults to all blocks. unify : bool, optional Unify the face cycles of the hull. Default is ``True``. Returns ------- tuple The vertices and faces of the hull. Examples -------- .. code-block:: python import compas_assembly from compas.datastructures import Mesh from compas.viewers import MeshViewer from compas_assembly.datastructures import Assembly assembly = Assembly.from_json(compas_assembly.get('assembly.json')) vertices, faces = assembly_hull(assembly) hull = Mesh.from_vertices_and_faces(vertices, faces) viewer = MeshViewer() viewer.mesh = hull viewer.show() """ keys = keys or list(assembly.vertices()) points = [] for key in keys: block = assembly.blocks[key] points.extend(block.get_vertices_attributes('xyz')) faces = convex_hull(points) vertices = list(set(flatten(faces))) i_index = {i: index for index, i in enumerate(vertices)} vertices = [points[index] for index in vertices] faces = [[i_index[i] for i in face] for face in faces] if unify: faces = unify_cycles(vertices, faces) return vertices, faces
def get_convex_hull_mesh(points): faces = convex_hull(points) vertices = list(set(flatten(faces))) i_index = {i: index for index, i in enumerate(vertices)} vertices = [points[index] for index in vertices] faces = [[i_index[i] for i in face] for face in faces] faces = unify_cycles(vertices, faces) mesh = Mesh.from_vertices_and_faces(vertices, faces) return mesh
def volmesh_merge_adjacent_halffaces(volmesh, hfkeys): # check halffaces ---------------------------------------------------------- for hfkey in hfkeys: if not volmesh.is_halfface_on_boundary(hfkey): raise ValueError('Halfface {} is interior.'.format(hfkey)) if not _are_halffaces_chained(volmesh, hfkeys): raise ValueError('These halffaces are not chained.') # -------------------------------------------------------------------------- halffaces = [volmesh.halfface[hfkey] for hfkey in hfkeys] # -------------------------------------------------------------------------- vkeys = set() for hfkey in hfkeys: for key in volmesh.halfface_vertices(hfkey): vkeys.add(key) vkeys = list(vkeys) points = [volmesh.vertex_coordinates(vkey) for vkey in vkeys] faces_by_index = convex_hull(points) faces_by_vkeys = [] for face in faces_by_index: faces_by_vkeys.append([vkeys[index] for index in face]) # make temp cell mesh ------------------------------------------------------ cell = Mesh() for i in range(len(vkeys)): key = vkeys[i] x, y, z = points[i] cell.add_vertex(key=key, x=x, y=y, z=z) for face in faces_by_vkeys: cell.add_face(face) # merge coplanar faces ----------------------------------------------------- cell_merge_coplanar_adjacent_faces(cell) # get correct direction of faces ------------------------------------------- faces = [cell.face[fkey] for fkey in cell.face] if halffaces[0] in faces: new_faces = [face[::-1] for face in faces] else: new_faces = faces volmesh.add_cell(new_faces) return volmesh
def assembly_hull(assembly, keys=None, unify=True): """Construct the convex hull of an assembly. Parameters ---------- assembly : Assembly The assembly data structure. keys: list, optional The identifiers of the blocks to include in the hull calculation. Defaults to all blocks. unify : bool, optional Unify the face cycles of the hull. Default is ``True``. Returns ------- tuple The vertices and faces of the hull. Examples -------- >>> """ keys = keys or list(assembly.nodes()) points = [] for key in keys: block = assembly.blocks[key] points.extend(block.vertices_attributes('xyz')) faces = convex_hull(points) vertices = list(set(flatten(faces))) i_index = {i: index for index, i in enumerate(vertices)} vertices = [points[index] for index in vertices] faces = [[i_index[i] for i in face] for face in faces] if unify: faces = unify_cycles(vertices, faces) return vertices, faces
def from_skeleton(cls, lines, radius=1): network = Network.from_lines(lines) tube_extremities = {} nodes = [] for vkey in network.vertices(): if len(network.vertex_neighbors(vkey)) > 1: points = [ network.edge_point(vkey, nbr, t=float(radius) / network.edge_length(vkey, nbr)) for nbr in network.vertex_neighbors(vkey) ] faces = convex_hull(points) mesh = cls.from_vertices_and_faces(points, faces) meshes = [] for fkey in mesh.faces(): vertices = [ mesh.edge_midpoint(u, v) for u, v in mesh.face_halfedges(fkey) ] faces = [[0, 1, 2]] meshes.append(cls.from_vertices_and_faces(vertices, faces)) for vkey_2 in mesh.vertices(): tops = [] bottoms = [] n = normalize_vector( subtract_vectors(mesh.vertex_coordinates(vkey_2), network.vertex_coordinates(vkey))) for i in range(len(mesh.vertex_neighbors(vkey_2))): pt_0 = mesh.edge_midpoint( vkey_2, mesh.vertex_neighbors(vkey_2, ordered=True)[i - 1]) bottoms.append(pt_0) pt_1 = mesh.edge_midpoint( vkey_2, mesh.vertex_neighbors(vkey_2, ordered=True)[i]) pt_2 = midpoint_line([pt_0, pt_1]) pt_2 = add_vectors( scale_vector(n, distance_point_point(pt_0, pt_1)), pt_2) tops.append(pt_2) vertices = [pt_0, pt_2, pt_1] faces = [[0, 1, 2]] meshes.append( cls.from_vertices_and_faces(vertices, faces)) for i in range(len(tops)): vertices = [tops[i - 1], tops[i], bottoms[i]] faces = [[0, 1, 2]] meshes.append( cls.from_vertices_and_faces(vertices, faces)) #print network.vertex_neighbors(vkey), network.vertex_neighbors(vkey)[vkey_2] tube_extremities[( vkey, network.vertex_neighbors(vkey)[vkey_2])] = tops mesh = meshes_join_and_weld(meshes) #dense_mesh = trimesh_subdivide_loop(mesh, k = 3) nodes.append(mesh) return nodes[0] meshes_2 = [] for u, v in network.edges(): if len(network.vertex_neighbors(u)) > 1 and len( network.vertex_neighbors(v)) > 1: #print len(tube_extremities[(u, v)]) #print len(tube_extremities[(v, u)]) if len(tube_extremities[(u, v)]) == len(tube_extremities[(v, u)]): n = len(tube_extremities[(u, v)]) l = network.edge_length(u, v) - 2 * radius m = math.floor(l / radius) + 1 pt_uv = tube_extremities[(u, v)] pt_vu = list(reversed(tube_extremities[(v, u)])) dmin = -1 imin = None for i in range(n): distance = sum([ distance_point_point(pt_uv[j], pt_vu[i + j - len(pt_vu)]) for j in range(n) ]) if dmin < 0 or distance < dmin: dmin = distance imin = i pt_vu = [pt_vu[imin + j - len(pt_vu)] for j in range(n)] array = [pt_uv] for i in range(int(m)): polygon = [] for j in range(int(n)): u = pt_uv[j] v = pt_vu[j] polygon.append( add_vectors( scale_vector(u, (float(m) - 1 - float(i)) / float(m - 1)), scale_vector(v, float(i) / float(m - 1)))) array.append(polygon) array.append(pt_vu) #print len(array), len(array[0]), len(array[1]), len(array[2]), len(array[3]) for i in range(int(n)): for j in range(int(m)): vertices = [ array[i - 1][j - 1], array[i - 1][j], array[i][j] ] faces = [[0, 1, 2]] meshes_2.append( Mesh.from_vertices_and_faces(vertices, faces)) vertices, faces = join_and_weld_meshes(meshes_2) #meshes_2 = rs.AddMesh(vertices, faces) meshes = [] for node in nodes: vertices, faces = node.to_vertices_and_faces() meshes.append(rs.AddMesh(vertices, faces))
def volmesh_ud(volmesh, network, scale=0.5): """Computes temporary vertex coordinates for every halfface of a volmesh, for the visualisation of the unified diagram. Parameters ---------- volmesh : VolMesh A volmesh object representing a polyhedral force diagram. network : Network A network object representing a polyhedral form diagram. scale : float Unified diagram scale factor, Returns ------- halffaces : dictionary A dictionary of dictionaries: hfkey-{vkey: (x, y, z)}. prisms : dictionary A dictinoary of dictinoaries: uv - [face coordinates] Raises ------ Exception If scale is 0, which means the unified diagram is equivalent to the polyhedral force diagram. Exception If scale is 0, which means the unified diagram is equivalent to the polyhedral form diagram. Notes ----- - The prisms are implemented as convex hull of two halffaces for simplicity and to resolve any small geometric errors. - Unified diagram with a scale of 0 is equivalent to the polyhedral force diagram, while a scale of 1 is equivalent to the polyhedral form diagram. """ # -------------------------------------------------------------------------- # 0. evaluate unified diagram scale # -------------------------------------------------------------------------- if scale == 0: raise Exception( "A unified diagram with a scale of 0 is equivalent to the polyhedral force diagram." ) if scale == 1: raise Exception( "A unified diagram with a scale of 1 is equivalent to the polyhedral form diagram." ) assert 0 < scale and scale < 1, "Scale needs to be between 0 and 1." # -------------------------------------------------------------------------- # 1. current positions of diagrams # -------------------------------------------------------------------------- volmesh_center = volmesh.centroid() network_center = network.datastructure_centroid() translation = subtract_vectors(volmesh_center, network_center) # -------------------------------------------------------------------------- # 2. get base points # -------------------------------------------------------------------------- base_xyz = {} for vkey in network.nodes(): init_xyz = network.node_coordinates(vkey) base_xyz[vkey] = add_vectors(init_xyz, translation) # -------------------------------------------------------------------------- # 3. compute scaled halffaces # -------------------------------------------------------------------------- # halffaces = {} # for ckey in volmesh.cells(): # cell_hfs = volmesh.cell_faces(ckey) # for hfkey in cell_hfs: # hf_vertices = {} # for vkey in volmesh.halfface_vertices(hfkey): # xyz = volmesh.vertex_coordinates(vkey) # arm = scale_vector(subtract_vectors(xyz, base_xyz[ckey]), scale) # hf_vertices[vkey] = add_vectors(base_xyz[ckey], arm) # halffaces[hfkey] = hf_vertices scaled_halffaces = {} cells = {} for cell in volmesh.cells(): gkey_xyz = {} faces = [] for face in volmesh.cell_faces(cell): new_face = [] scaled_face_xyz = [] for vertex in volmesh.face_vertices(face): xyz = volmesh.vertex_coordinates(vertex) arm = scale_vector(subtract_vectors(xyz, base_xyz[cell]), scale) scaled_xyz = add_vectors(base_xyz[cell], arm) gkey = geometric_key(scaled_xyz) gkey_xyz[gkey] = scaled_xyz new_face.append(gkey) scaled_face_xyz.append(scaled_xyz) scaled_halffaces[face] = scaled_face_xyz faces.append(new_face) gkey_index = dict((gkey, index) for index, gkey in enumerate(gkey_xyz)) vertices = [list(xyz) for gkey, xyz in gkey_xyz.items()] scaled_faces = [[gkey_index[gkey] for gkey in face] for face in faces] cells[cell] = {'vertices': vertices, 'faces': scaled_faces} # cells = {} # for cell in volmesh.cells(): # vertices = volmesh.cell_vertices(cell) # faces = volmesh.cell_faces(cell) # vertex_index = dict((vertex, index) for index, vertex in enumerate(vertices)) # scaled_vertices = [] # for vertex in vertices: # xyz = volmesh.vertex_coordinates(vertex) # arm = scale_vector(subtract_vectors(xyz, base_xyz[cell]), scale) # scaled_vertices.append(add_vectors(base_xyz[cell], arm)) # faces = [[vertex_index[vertex] for vertex in volmesh.halfface_vertices(face)] for face in faces] # cells[cell] = {'vertices': scaled_vertices, 'faces': faces} # -------------------------------------------------------------------------- # 4. compute prism faces # -------------------------------------------------------------------------- # prism_faces = {} prism_cells = {} for u, v in network.edges(): u_hfkey, v_hfkey = volmesh.cell_pair_halffaces(u, v) u_pts = scaled_halffaces[u_hfkey] v_pts = scaled_halffaces[v_hfkey] pts = u_pts + v_pts prism = convex_hull(pts) # face as indices of pt_list prism_cells[(u, v)] = {'vertices': pts, 'faces': prism} # for u, v in network.edges(): # u_hfkey, v_hfkey = volmesh.cell_pair_halffaces(u, v) # u_pts = halffaces[u_hfkey].values() # v_pts = halffaces[v_hfkey].values() # pt_list = u_pts + v_pts # prism = convex_hull(u_pts + v_pts) # face as indices of pt_list # face_list = [] # for face in prism: # face_xyz = [pt_list[i] for i in face] # get face xyz in order # face_list.append(face_xyz) # prism_faces[(u, v)] = face_list # prism_cells[(u, v)] = {'vertices': pt_list, 'faces': face_list} # -------------------------------------------------------------------------- # return halffaces, prism_faces return cells, prism_cells
def trimesh_skeleton(cls, lines, radius=1): network = Network.from_lines(lines) tube_extremities = {} nodes = [] for vkey in network.nodes(): if len(network.adjacency[vkey]) > 1: points = { nbr: network.edge_point(vkey, nbr, t=float(radius) / network.edge_length(vkey, nbr)) for nbr in network.adjacency[vkey] } idx_to_key = { i: key for i, key in enumerate(network.adjacency[vkey]) } faces = convex_hull(list(points.values())) faces = [[idx_to_key[idx] for idx in face] for face in faces] mesh = cls.from_vertices_and_faces(points, faces) nodes.append(mesh) meshes = [] for fkey in mesh.faces(): vertices = [ mesh.edge_midpoint(u, v) for u, v in mesh.face_halfedges(fkey) ] faces = [[0, 1, 2]] meshes.append(cls.from_vertices_and_faces(vertices, faces)) for vkey_2 in mesh.vertices(): tops = [] bottoms = [] n = normalize_vector( subtract_vectors(mesh.vertex_coordinates(vkey_2), network.node_coordinates(vkey))) for i in range(len(mesh.vertex_neighbors(vkey_2))): pt_0 = mesh.edge_midpoint( vkey_2, mesh.vertex_neighbors(vkey_2, ordered=True)[i - 1]) bottoms.append(pt_0) pt_1 = mesh.edge_midpoint( vkey_2, mesh.vertex_neighbors(vkey_2, ordered=True)[i]) pt_2 = midpoint_line([pt_0, pt_1]) pt_2 = add_vectors( scale_vector(n, distance_point_point(pt_0, pt_1)), pt_2) tops.append(pt_2) vertices = [pt_0, pt_2, pt_1] faces = [[0, 1, 2]] meshes.append(cls.from_vertices_and_faces(vertices, faces)) for i in range(len(tops)): vertices = [tops[i - 1], tops[i], bottoms[i]] faces = [[0, 1, 2]] meshes.append(cls.from_vertices_and_faces(vertices, faces)) tube_extremities[(vkey, vkey_2)] = tops mesh = meshes_join_and_weld(meshes) nodes.append(mesh) all_nodes = meshes_join_and_weld(nodes) # leaf node ring for u in network.nodes(): if len(network.adjacency[u]) == 1: v = next(iter(network.adjacency[u].keys())) ring_v = tube_extremities[(v, u)] ring_u = [ add_vectors(pt, network.edge_vector(v, u)) for pt in ring_v[::-1] ] tube_extremities[(u, v)] = ring_u beams = [] for u, v in network.edges(): geom_key_map = { geometric_key(all_nodes.vertex_coordinates(vkey)): vkey for vkey in all_nodes.vertices() } if len(tube_extremities[(u, v)]) != len(tube_extremities[(v, u)]): if len(tube_extremities[(u, v)]) < len(tube_extremities[(v, u)]): a, b = u, v else: a, b = v, u n = len(tube_extremities[(b, a)]) - len(tube_extremities[(a, b)]) for i in range(n): bdry_vkey = geom_key_map[geometric_key( tube_extremities[(a, b)][0])] nbr_vkey = None for nbr in all_nodes.vertex_neighbors(bdry_vkey): if not all_nodes.is_edge_on_boundary(bdry_vkey, nbr): nbr_vkey = nbr k = tube_extremities[(a, b)].index( all_nodes.vertex_coordinates(bdry_vkey)) new_vkey = insert_triface_on_boundary(all_nodes, bdry_vkey, nbr_vkey) tube_extremities[(a, b)].insert( k + 1 - len(tube_extremities[(a, b)]), all_nodes.vertex_coordinates(new_vkey)) if len(tube_extremities[(u, v)]) == len(tube_extremities[(v, u)]): n = len(tube_extremities[(u, v)]) l = network.edge_length(u, v) - 2 * radius m = (floor(l / radius) + 1) * 2 pt_uv = tube_extremities[(u, v)] pt_vu = list(reversed(tube_extremities[(v, u)])) dmin = -1 imin = None for i in range(n): distance = sum([ distance_point_point(pt_uv[j], pt_vu[i + j - len(pt_vu)]) for j in range(n) ]) if dmin < 0 or distance < dmin: dmin = distance imin = i pt_vu = [pt_vu[imin + j - len(pt_vu)] for j in range(n)] # ab = pt_uv# + pt_uv[0:] # dc = pt_vu# + pt_vu[0:] # line = Polyline([ab[0], dc[0]]) # ad = [line.point(i / (m - 1)) for i in range(m)] # bc = ad # vertices, faces = discrete_coons_patch(ab, bc, dc, ad) # tri_faces = [] # for (a, b, c, d) in faces: # tri_faces += [[a, b, c], [a, c, d]] # reverse? # beams.append(Mesh.from_vertices_and_faces(vertices, tri_faces)) array = [pt_uv] for i in range(int(m)): polygon = [] for j in range(int(n)): u = pt_uv[j] v = pt_vu[j] polygon.append( add_vectors( scale_vector(u, (float(m) - 1 - float(i)) / float(m - 1)), scale_vector(v, float(i) / float(m - 1)))) array.append(polygon) array.append(pt_vu) for i in range(1, int(m + 2)): for j in range(int(n)): vertices = [ array[i - 1][j - 1], array[i - 1][j], array[i][j - 1], array[i][j] ] # create staggered pattern? faces = [[3, 1, 0], [2, 3, 0]] beams.append(cls.from_vertices_and_faces(vertices, faces)) # return all_nodes # #return meshes_join_and_weld(beams) return meshes_join_and_weld([all_nodes] + beams)
def volmesh_ud(volmesh, network, scale=0.5): """Computes temporary vertex coordinates for every halfface of a volmesh, for the visualisation of the unified diagram. Parameters ---------- volmesh : VolMesh A volmesh object representing a polyhedral force diagram. network : Network A network object representing a polyhedral form diagram. scale : float Unified diagram scale factor, Returns ------- halffaces : dictionary A dictionary of dictionaries: hfkey-{vkey: (x, y, z)}. prisms : dictionary A dictinoary of dictinoaries: uv - [face coordinates] Raises ------ Exception If scale is 0, which means the unified diagram is equivalent to the polyhedral force diagram. Exception If scale is 0, which means the unified diagram is equivalent to the polyhedral form diagram. Notes ----- - The prisms are implemented as convex hull of two halffaces for simplicity and to resolve any small geometric errors. - Unified diagram with a scale of 0 is equivalent to the polyhedral force diagram, while a scale of 1 is equivalent to the polyhedral form diagram. """ # -------------------------------------------------------------------------- # 0. evaluate unified diagram scale # -------------------------------------------------------------------------- if scale == 0: raise Exception("A unified diagram with a scale of 0 is equivalent to the polyhedral force diagram.") if scale == 1: raise Exception("A unified diagram with a scale of 1 is equivalent to the polyhedral form diagram.") assert 0 < scale and scale < 1, "Scale needs to be between 0 and 1." # -------------------------------------------------------------------------- # 1. current positions of diagrams # -------------------------------------------------------------------------- volmesh_center = volmesh.centroid() network_center = network.datastructure_centroid() translation = subtract_vectors(volmesh_center, network_center) # -------------------------------------------------------------------------- # 2. get base points # -------------------------------------------------------------------------- base_xyz = {} for vkey in network.vertex: init_xyz = network.vertex_coordinates(vkey) base_xyz[vkey] = add_vectors(init_xyz, translation) # -------------------------------------------------------------------------- # 3. compute scaled halffaces # -------------------------------------------------------------------------- halffaces = {} for ckey in volmesh.cell: cell_hfs = volmesh.cell_halffaces(ckey) for hfkey in cell_hfs: hf_vertices = {} for vkey in volmesh.halfface_vertices(hfkey): xyz = volmesh.vertex_coordinates(vkey) arm = scale_vector(subtract_vectors(xyz, base_xyz[ckey]), scale) hf_vertices[vkey] = add_vectors(base_xyz[ckey], arm) halffaces[hfkey] = hf_vertices # -------------------------------------------------------------------------- # 4. compute prism faces # -------------------------------------------------------------------------- prism_faces = {} for u, v in network.edges(): u_hfkey, v_hfkey = volmesh.cell_pair_halffaces(u, v) u_pts = halffaces[u_hfkey].values() v_pts = halffaces[v_hfkey].values() pt_list = u_pts + v_pts prism = convex_hull(u_pts + v_pts) # face as indices of pt_list face_list = [] for face in prism: face_xyz = [pt_list[i] for i in face] # get face xyz in order face_list.append(face_xyz) prism_faces[(u, v)] = face_list # -------------------------------------------------------------------------- return halffaces, prism_faces