def centroid(self): """Calculate the mesh centroid. Returns ------- list The coordinates of the mesh centroid. """ return scale_vector(sum_vectors([scale_vector(self.face_centroid(fkey), self.face_area(fkey)) for fkey in self.faces()]), 1. / self.area())
def normal(self): """Calculate the average mesh normal. Returns ------- list The coordinates of the mesh normal. """ return scale_vector(sum_vectors([scale_vector(self.face_normal(fkey), self.face_area(fkey)) for fkey in self.faces()]), 1. / self.area())
def RunCommand(is_interactive): scene = get_scene() if not scene: return pattern = scene.get("pattern")[0] if not pattern: print("There is no Pattern in the scene.") return if not list(pattern.datastructure.vertices_where({'is_anchor': True})): print( "Pattern has no anchor vertices! Please define anchor (support) vertices." ) return form = FormDiagram.from_pattern(pattern.datastructure) form.vertices_attribute('is_fixed', False) normals = [ form.face_normal(face) for face in form.faces_where({'_is_loaded': True}) ] scale = 1 / len(normals) normal = scale_vector(sum_vectors(normals), scale) if normal[2] < 0: form.flip_cycles() fixed = list(pattern.datastructure.vertices_where({'is_fixed': True})) if fixed: for key in fixed: if form.has_vertex(key): form.vertex_attribute(key, 'is_anchor', True) thrust = form.copy(cls=ThrustDiagram) bbox_form = form.bounding_box_xy() diagonal = length_vector(subtract_vectors(bbox_form[2], bbox_form[0])) zmax = 0.25 * diagonal scene.settings['Solvers']['tna.vertical.zmax'] = round(zmax, 1) scene.clear() scene.add(form, name='form') scene.add(thrust, name='thrust') scene.update() print('FormDiagram object successfully created.')
def face_strip_insert(cls, mesh, vertex_path, pole_extremities, factor=.33): # duplicate twice vertices on vertex path duplicated_vertices = {} for vkey in vertex_path: # if closed vertex path, end already included if vkey in duplicated_vertices: continue x, y, z = mesh.vertex_coordinates(vkey) vkey_1 = mesh.add_vertex(attr_dict={'x': x, 'y': y, 'z': z}) vkey_2 = mesh.add_vertex(attr_dict={'x': x, 'y': y, 'z': z}) duplicated_vertices[vkey] = [vkey_1, vkey_2] new_faces = [] # move along edge path for i in range(0, len(vertex_path) - 1): u = vertex_path[i] v = vertex_path[i + 1] if v in mesh.halfedge[u]: fkey_left = mesh.halfedge[u][v] else: fkey_left = None if u in mesh.halfedge[v]: fkey_right = mesh.halfedge[v][u] else: fkey_right = None # update faces adjacent to vertex path if fkey_left is not None: face_vertices = [ duplicated_vertices[vkey][0] if vkey in duplicated_vertices else vkey for vkey in mesh.face_vertices(fkey_left) ] mesh.delete_face(fkey_left) mesh.add_face(face_vertices, fkey_left) if fkey_right is not None: face_vertices = [ duplicated_vertices[vkey][1] if vkey in duplicated_vertices else vkey for vkey in mesh.face_vertices(fkey_right) ] mesh.delete_face(fkey_right) mesh.add_face(face_vertices, fkey_right) # add strip face_vertices = [ duplicated_vertices[u][1], duplicated_vertices[v][1], duplicated_vertices[v][0], duplicated_vertices[u][0] ] new_faces.append(mesh.add_face(face_vertices)) # delete old vertices # remove end if closed vertex path if vertex_path[0] == vertex_path[-1]: culled_verted_vertex_path = vertex_path[:-1] else: culled_verted_vertex_path = vertex_path for vkey in culled_verted_vertex_path: if len(mesh.vertex_faces(vkey)) > 0: faces = [ mesh.halfedge[vkey][a] for a in mesh.vertex_neighbors(vkey) if mesh.halfedge[vkey][a] is not None ] count = len(faces) * 2 while len(faces) > 0 and count > 0: for fkey in faces: for fkey_2 in [ fkey_3 for vkey_2 in mesh.face_vertices(fkey) for fkey_3 in mesh.vertex_faces(vkey_2) ]: if duplicated_vertices[vkey][0] in mesh.face_vertices( fkey_2): duplicate = duplicated_vertices[vkey][0] face_vertices = [ duplicate if key == vkey else key for key in mesh.face_vertices(fkey) ] mesh.delete_face(fkey) mesh.add_face(face_vertices, fkey) faces.remove(fkey) break if duplicated_vertices[vkey][1] in mesh.face_vertices( fkey_2): duplicate = duplicated_vertices[vkey][1] face_vertices = [ duplicate if key == vkey else key for key in mesh.face_vertices(fkey) ] mesh.delete_face(fkey) mesh.add_face(face_vertices, fkey) faces.remove(fkey) break if fkey not in faces: break mesh.delete_vertex(vkey) # transform pole extemities for vkey, is_pole in zip([vertex_path[0], vertex_path[-1]], pole_extremities): if is_pole: u, v = duplicated_vertices[vkey] for fkey in mesh.vertex_faces(v): if fkey is not None: new_face_vertices = [ u if vkey_2 == v else vkey_2 for vkey_2 in mesh.face_vertices(fkey) ] mesh.delete_face(fkey) mesh.add_face(new_face_vertices, fkey) duplicated_vertices[vkey].remove(v) mesh.delete_vertex(v) # move duplicated superimposed vertices towards neighbours' centroid moves = {} for vkey in [ vkey for vkeys in duplicated_vertices.values() for vkey in vkeys ]: #if mesh.is_vertex_on_boundary(vkey): # continue xyz = scale_vector( sum_vectors([ mesh.vertex_coordinates(nbr) for nbr in mesh.vertex_neighbors(vkey) ]), 1. / len(mesh.vertex_neighbors(vkey))) moves[vkey] = xyz for vkey, xyz in moves.items(): x, y, z = xyz attr = mesh.vertex[vkey] attr['x'] += factor * (x - attr['x']) attr['y'] += factor * (y - attr['y']) attr['z'] += factor * (z - attr['z']) return new_faces
def face_strip_insert_2(cls, mesh, vertex_path, factor=.33): # check if vertex_path is a loop if vertex_path[0] == vertex_path[-1]: loop = True else: loop = False edge_path = [[vertex_path[i], vertex_path[i + 1]] for i in range(len(vertex_path) - 1)] if loop: del edge_path[-1] # duplicate the vertices along the edge_path by unwelding the mesh duplicates = unweld_mesh_along_edge_path(mesh, edge_path) duplicate_list = [i for uv in duplicates for i in uv] # move the duplicated vertices towards the centroid of the adjacent faces for uv in duplicates: for vkey in uv: # if was on boundary before unwelding, move along boundary edge on_boundary = False for nbr in mesh.vertex_neighbors(vkey): if mesh.is_edge_on_boundary(vkey, nbr) and nbr not in duplicate_list: x, y, z = mesh.edge_point(vkey, nbr, factor) on_boundary = True if not on_boundary: centroids = [ mesh.face_centroid(fkey) for fkey in mesh.vertex_faces(vkey) ] areas = [ mesh.face_area(fkey) for fkey in mesh.vertex_faces(vkey) ] #if len(centroids) != 0: x, y, z = sum_vectors([ scale_vector(centroid, area / sum(areas)) for centroid, area in zip(centroids, areas) ]) attr = mesh.vertex[vkey] # factor related to the width of the new face strip compared to the adjacent ones attr['x'] += factor * (x - attr['x']) attr['y'] += factor * (y - attr['y']) attr['z'] += factor * (z - attr['z']) # add new face strip for i in range(len(duplicates) - 1): a = duplicates[i][1] b = duplicates[i][0] c = duplicates[i + 1][0] d = duplicates[i + 1][1] mesh.add_face([a, b, c, d]) # close if loop if loop: a = duplicates[-1][1] b = duplicates[i - 1][0] c = duplicates[0][0] d = duplicates[0][1] mesh.add_face([a, b, c, d]) return 0
def discrete_coons_patch(ab, bc, dc, ad): """Creates a coons patch from a set of four or three boundary polylines (ab, bc, dc, ad). Parameters ---------- polylines : sequence The XYZ coordinates of the vertices of the polyline. The vertices are assumed to be in order. The polyline is assumed to be open: Returns ------- points : list of tuples The points of the coons patch. faces : list of lists List of faces (face = list of vertex indices as integers) Notes ----- Direction and order of polylines:: b -----> c ^ ^ | | | | | | a -----> d One polyline can be None to create a triangular patch (Warning! This will result in duplicate vertices) For more information see [1]_ and [2]_. References ---------- .. [1] Wikipedia. *Coons patch*. Available at: https://en.wikipedia.org/wiki/Coons_patch. .. [2] Robert Ferreol. *Patch de Coons*. Available at: https://www.mathcurve.com/surfaces/patchcoons/patchcoons.shtml Examples -------- .. code-block:: python # See Also -------- * :func:`compas.datastructures.mesh_cull_duplicate_vertices` """ if not ab: ab = [ad[0]] * len(dc) if not bc: bc = [ab[-1]] * len(ad) if not dc: dc = [bc[-1]] * len(ab) if not ad: ad = [dc[0]] * len(bc) n = len(ab) m = len(bc) n_norm = normalize_values(range(n)) m_norm = normalize_values(range(m)) array = [[0] * m for i in range(n)] for i, ki in enumerate(n_norm): for j, kj in enumerate(m_norm): # first function: linear interpolation of first two opposite curves lin_interp_ab_dc = add_vectors(scale_vector(ab[i], (1 - kj)), scale_vector(dc[i], kj)) # second function: linear interpolation of other two opposite curves lin_interp_bc_ad = add_vectors(scale_vector(ad[j], (1 - ki)), scale_vector(bc[j], ki)) # third function: linear interpolation of four corners resulting a hypar a = scale_vector(ab[0], (1 - ki) * (1 - kj)) b = scale_vector(bc[0], ki * (1 - kj)) c = scale_vector(dc[-1], ki * kj) d = scale_vector(ad[-1], (1 - ki) * kj) lin_interp_a_b_c_d = sum_vectors([a, b, c, d]) # coons patch = first + second - third functions array[i][j] = subtract_vectors( add_vectors(lin_interp_ab_dc, lin_interp_bc_ad), lin_interp_a_b_c_d) # create vertex list vertices = [] for i in range(n): vertices += array[i] # create face vertex list face_vertices = [] for i in range(n - 1): for j in range(m - 1): face_vertices.append([ i * m + j, i * m + j + 1, (i + 1) * m + j + 1, (i + 1) * m + j ]) return vertices, face_vertices
def draw_frictions(self, scale=None, eps=None, mode=0): """Draw the contact frictions at the interfaces. Parameters ---------- scale : float, optional The scale at which the forces should be drawn. Default is `0.1`. eps : float, optional A tolerance for drawing small force vectors. Force vectors with a scaled length smaller than this tolerance are not drawn. Default is `1e-3`. mode : int, optional Display mode: 0 normal, 1 resultant forces Default is 0 Notes ----- * Forces are drawn as lines with arrow heads. * Forces are drawn on a sub-layer *Frictions* of the base layer, if a base layer was specified. * Forces are named according to the following pattern: ``"{assembly_name}.friction.{from block}-{to block}.{interface point}"`` """ layer = "{}::Frictions".format(self.layer) if self.layer else None scale = scale or self.defaults['scale.friction'] eps = eps or self.defaults['eps.friction'] color_friction = self.defaults['color.force:friction'] lines = [] resultant_lines = [] for a, b, attr in self.assembly.edges(True): if attr['interface_forces'] is None: continue u, v = attr['interface_uvw'][0], attr['interface_uvw'][1] forces = [] for i in range(len(attr['interface_points'])): sp = attr['interface_points'][i] fr_u_unit = attr['interface_forces'][i]['c_u'] fr_v_unit = attr['interface_forces'][i]['c_v'] fr_u = scale_vector(u, fr_u_unit) fr_v = scale_vector(v, fr_v_unit) f = add_vectors(fr_u, fr_v) f_l = length_vector(f) if f_l > 0.0: if scale * f_l < eps: continue color = color_friction else: continue f = scale_vector(f, scale) lines.append({ 'start': sp, 'end': [sp[axis] + f[axis] for axis in range(3)], 'color': color, 'name': "{0}.friction.{1}-{2}.{3}".format(self.assembly.name, a, b, i), 'arrow': 'end' }) if mode == 1: forces.append(f) if mode == 0: continue resultant_force = sum_vectors(forces) if len(forces) == 0: continue resultant_pt = sum_vectors([ scale_vector(attr['interface_points'][i], (length_vector(forces[i]) / length_vector(resultant_force))) for i in range(len(attr['interface_points'])) ]) resultant_lines.append({ 'start': resultant_pt, 'end': [ resultant_pt[axis] + resultant_force[axis] for axis in range(3) ], 'color': color, 'name': "{0}.resultant-friction.{1}-{2}.{3}".format( self.assembly.name, a, b, i), 'arrow': 'end' }) if mode == 0: compas_rhino.xdraw_lines(lines, layer=layer, clear=False, redraw=False) else: compas_rhino.xdraw_lines(resultant_lines, layer=layer, clear=False, redraw=False)
# multiply by the local thickness # assign to the correct item in the list of loads xyz, q, f, l, r = numerical.fd_numpy(xyz, edges, fixed, q, loads) # update the data structure for key in mesh.vertices(): if mesh.get_vertex_attribute(key, 'is_fixed'): continue # compute the load on the vertex # compute the forces in the edges connected to the vertex # sum up the vectors to compute the residual force resultant = sum_vectors(forces) mesh.set_vertex_attributes(key, ('rx', 'ry', 'rz'), resultant) # ============================================================================== # Visualise the result # ============================================================================== artist = MeshArtist(mesh, layer="Selfweight") artist.clear_layer() artist.draw_vertices() artist.draw_edges() artist.draw_faces() loads = [] for key in mesh.vertices(): if mesh.get_vertex_attribute(key, 'is_fixed'):
def discrete_coons_patch(ab, bc, dc, ad): """Creates a coons patch from a set of four or three boundary polylines (ab, bc, dc, ad). Parameters ---------- ab : list[[float, float, float] | :class:`compas.geometry.Point`] The XYZ coordinates of the vertices of the first polyline. bc : list[[float, float, float] | :class:`compas.geometry.Point`] The XYZ coordinates of the vertices of the second polyline. dc : list[[float, float, float] | :class:`compas.geometry.Point`] The XYZ coordinates of the vertices of the third polyline. ad : list[[float, float, float] | :class:`compas.geometry.Point`] The XYZ coordinates of the vertices of the fourth polyline. Returns ------- list[[float, float, float]] The points of the coons patch. list[list[int]] List of faces, with every face a list of indices into the point list. Notes ----- The vertices of the polylines are assumed to be in the following order:: b -----> c ^ ^ | | | | a -----> d To create a triangular patch, one of the input polylines should be None. (Warning! This will result in duplicate vertices.) For more information see [1]_ and [2]_. References ---------- .. [1] Wikipedia. *Coons patch*. Available at: https://en.wikipedia.org/wiki/Coons_patch. .. [2] Robert Ferreol. *Patch de Coons*. Available at: https://www.mathcurve.com/surfaces/patchcoons/patchcoons.shtml Examples -------- >>> """ if not ab: ab = [ad[0]] * len(dc) if not bc: bc = [ab[-1]] * len(ad) if not dc: dc = [bc[-1]] * len(ab) if not ad: ad = [dc[0]] * len(bc) n = len(ab) m = len(bc) n_norm = normalize_values(range(n)) m_norm = normalize_values(range(m)) array = [[0] * m for i in range(n)] for i, ki in enumerate(n_norm): for j, kj in enumerate(m_norm): # first function: linear interpolation of first two opposite curves lin_interp_ab_dc = add_vectors(scale_vector(ab[i], (1 - kj)), scale_vector(dc[i], kj)) # second function: linear interpolation of other two opposite curves lin_interp_bc_ad = add_vectors(scale_vector(ad[j], (1 - ki)), scale_vector(bc[j], ki)) # third function: linear interpolation of four corners resulting a hypar a = scale_vector(ab[0], (1 - ki) * (1 - kj)) b = scale_vector(bc[0], ki * (1 - kj)) c = scale_vector(dc[-1], ki * kj) d = scale_vector(ad[-1], (1 - ki) * kj) lin_interp_a_b_c_d = sum_vectors([a, b, c, d]) # coons patch = first + second - third functions array[i][j] = subtract_vectors( add_vectors(lin_interp_ab_dc, lin_interp_bc_ad), lin_interp_a_b_c_d) # create vertex list vertices = [] for i in range(n): vertices += array[i] # create face vertex list faces = [] for i in range(n - 1): for j in range(m - 1): faces.append([ i * m + j, i * m + j + 1, (i + 1) * m + j + 1, (i + 1) * m + j ]) return vertices, faces
for node in embedding.leaves(): u = embedding.neighbors(node)[0] if u in fixed: continue a = embedding.node_attributes(u, 'xyz') vectors = [] for v in noleaves.neighbors(u): b = embedding.node_attributes(v, 'xyz') vectors.append(normalize_vector(subtract_vectors(b, a))) ab = scale_vector( normalize_vector( scale_vector(sum_vectors(vectors), 1 / len(vectors))), length) embedding.node_attributes(node, 'xyz', subtract_vectors(a, ab)) # ============================================================================== # Construct a form diagram of the embedding # ============================================================================== form = FormDiagram.from_graph(embedding) # ============================================================================== # Visualize the result # ============================================================================== plotter = MeshPlotter(form, figsize=(12, 7.5)) plotter.draw_vertices(text='key', radius=0.3) plotter.draw_edges()