def transformed_stress_vector_fields(mesh, vector_fields, stress_type, ref_vector): """ Rescales a vector field based on a plane stress transformation. """ vf1, vf2 = vector_fields # TODO: mapping is not robust! depends on naming convention stress_components = { "bending": { "names": ["mx", "my", "mxy"], "ps": "m_1" }, "axial": { "names": ["nx", "ny", "nxy"], "ps": "n_1" } } stress_names = stress_components[stress_type]["names"] vf_ps = mesh.vector_field(stress_components[stress_type]["ps"]) vf1_transf = VectorField() vf2_transf = VectorField() for fkey in mesh.faces(): # query stress components sx, sy, sxy = mesh.face_attributes(fkey, names=stress_names) # generate principal stresses and angles s1a, s1 = principal_stresses_and_angles(sx, sy, sxy) s1, angle1 = s1a vector_ps = vf_ps[fkey] vector1 = vf1[fkey] vector2 = vf2[fkey] # compute delta between reference vector and principal bending vector # TODO: will take m1 as reference. does this always hold? delta = angle1 - angle_vectors(vector_ps, ref_vector) # add delta to angles of the vector field to transform theta = delta + angle_vectors(vector1, ref_vector) # transform stresses - this becomes the scale of the vectors s1, s2, _ = transformed_stresses(sx, sy, sxy, theta) vf1_transf.add_vector(fkey, scale_vector(normalize_vector(vector1), s1)) vf2_transf.add_vector(fkey, scale_vector(normalize_vector(vector2), s2)) return vf1_transf, vf2_transf
def kinks(self, threshold=1e-3): """Return the XYZ coordinates of kinks, i.e. tangency discontinuities, along the surface's boundaries. Returns ------- list The list of XYZ coordinates of surface boundary kinks. """ kinks = [] borders = self.borders(type=0) for border in borders: border = RhinoCurve(border) extremities = map( lambda x: rs.EvaluateCurve(border.guid, rs.CurveParameter(border.guid, x)), [0., 1.]) if border.is_closed(): start_tgt, end_tgt = border.tangents(extremities) if angle_vectors(start_tgt, end_tgt) > threshold: kinks += extremities else: kinks += extremities return list(set(kinks))
def set_vertex_vectors_angles(self, name): for v in self.c_mesh.vertices(): # 1. find keys of vertex' neighbouring faces face_idxs = self.c_mesh.vertex_faces(v, ordered=True) # 2. get attributes of the found faces and calculate weights vec_a = self.c_mesh.faces_attribute(name=str(name) + '_a', keys=face_idxs) vec_b = self.c_mesh.faces_attribute(name=str(name) + '_b', keys=face_idxs) # 3. get connected edges angles = [] for face_idx in face_idxs: edges = [] for edge in self.c_mesh.face_halfedges(face_idx): if v in edge: edges.append(edge) # if len(edges) > 1: e_1 = self.c_mesh.edge_vector(edges[0][0], edges[0][1]) e_2 = self.c_mesh.edge_vector(edges[1][0], edges[1][1]) angle = cg.angle_vectors(e_1, e_2, deg=True) angles.append(angle) mass_angle = reduce(lambda x, y: x+y, angles) weights = list(map(lambda x: x/mass_angle, angles)) # 4. multiply vectors by weights nd_vec_a = ut.vectors_weight_reduce(vec_a, weights) nd_vec_b = ut.vectors_weight_reduce(vec_b, weights) # 6. assign vector attributes to vertices self.c_mesh.vertex_attribute(v, str(name) + '_a', nd_vec_a) self.c_mesh.vertex_attribute(v, str(name) + '_b', nd_vec_b)
def get_angle_weights(self, c_mesh, v_key, f_keys): angles = [] weigths = [] angles = [] for f_key in f_keys: edges = [] for edge in c_mesh.face_halfedges(f_key): if v_key in edge: edges.append(edge) e_1 = c_mesh.edge_vector(edges[0][0], edges[0][1]) e_2 = c_mesh.edge_vector(edges[1][0], edges[1][1]) try: angle = cg.angle_vectors(e_1, e_2, deg=True) except Exception: angle = 1e-6 angles.append(angle) if len(angles) > 1: mass_angle = reduce(lambda x, y: x + y, angles) if mass_angle > 0: return list(map(lambda x: x / mass_angle, angles)) return [1.0]
def find_points_extreme(pts_all, pts_init): """update a bar's axis end point based on all the contact projected points specified in `pts_all` Parameters ---------- pts_all : list of points all the contact points projected on the axis (specified by pts_init) pts_init : list of two points the initial axis end points Returns ------- [type] [description] """ vec_init = normalize_vector(vector_from_points(*pts_init)) # * find the pair of points with maximal distance sorted_pt_pairs = sorted(combinations(pts_all, 2), key=lambda pt_pair: distance_point_point(*pt_pair)) pts_draw = sorted_pt_pairs[-1] vec_new = normalize_vector(vector_from_points(*pts_draw)) if angle_vectors(vec_init, vec_new, deg=True) > 90: # angle can only be 0 or 180 pts_draw = pts_draw[::-1] # ext_len = 30 # pts_draw = (add_vectors(pts_draw[0], scale_vector(normalize_vector(vector_from_points(pts_draw[1], pts_draw[0])), ext_len)), add_vectors(pts_draw[1], scale_vector(normalize_vector(vector_from_points(pts_draw[0], pts_draw[1])), ext_len))) return pts_draw
def orient_points(points, reference_plane, target_plane): """Orient points from one plane to another. Parameters ---------- points : list of points XYZ coordinates of the points. reference_plane : plane Base point and normal defining a reference plane. target_plane : plane Base point and normal defining a target plane. Returns ------- points : list of point XYZ coordinates of the oriented points. Notes ----- This function is useful to orient a planar problem in the xy-plane to simplify the calculation (see example). Examples -------- >>> from compas.geometry import orient_points >>> from compas.geometry import intersection_segment_segment_xy >>> >>> refplane = ([0.57735, 0.57735, 0.57735], [1.0, 1.0, 1.0]) >>> tarplane = ([0.0, 0.0, 0.0], [0.0, 0.0, 1.0]) >>> >>> points = [\ [0.288675, 0.288675, 1.1547],\ [0.866025, 0.866025, 0.0],\ [1.077350, 0.077350, 0.57735],\ [0.077350, 1.077350, 0.57735]\ ] >>> >>> points = orient_points(points, refplane, tarplane) >>> >>> ab = points[0], points[1] >>> cd = points[2], points[3] >>> >>> point = intersection_segment_segment_xy(ab, cd) >>> >>> points = orient_points([point], tarplane, refplane) >>> Point(*points[0]) Point(0.577, 0.577, 0.577) """ axis = cross_vectors(reference_plane[1], target_plane[1]) angle = angle_vectors(reference_plane[1], target_plane[1]) origin = reference_plane[0] if angle: points = rotate_points(points, angle, axis, origin) vector = subtract_vectors(target_plane[0], reference_plane[0]) points = translate_points(points, vector) return points
def get_rebar_angles(x_dir, ori): angle = cg.angle_vectors(ori, x_dir, deg=True) angle_2 = angle + 90.0 # if angle > 90.0: # angle = angle-90 # angle_2 = angle - 90.0 return angle, angle_2
def update_angle_deviations(self): """Compute the angle deviation with the corresponding halfface normal in the ForceVolMesh. """ for edge in self.edges(): halfface = self.dual_halfface(edge) normal = self.dual.halfface_normal(halfface) edge_vector = self.edge_vector(*edge) a = angle_vectors(edge_vector, normal, deg=True) self.edge_attribute(edge, '_a', a) self.dual.face_attribute(halfface, '_a', a)
def _find_first_neighbour(key, network): angles = [] nbrs = list(network.halfedge[key].keys()) if len(nbrs) == 1: return nbrs[0] vu = [-1, -1, 0] for nbr in nbrs: w = [network.vertex[nbr][_] for _ in 'xyz'] v = [network.vertex[key][_] for _ in 'xyz'] vw = [w[0] - v[0], w[1] - v[1], 0] angles.append(angle_vectors(vu, vw)) return nbrs[angles.index(min(angles))]
def _check_deviation(volmesh, network): deviation = 0 for u, v in network.edges(): u_hf, v_hf = volmesh.cell_pair_halffaces(u, v) normal = volmesh.halfface_normal(u_hf) edge = network.edge_vector(u, v, unitized=True) perp_check = angle_vectors(normal, edge, deg=True) # dot = dot_vectors(normal, edge) # perp_check = 1 - abs(dot) if perp_check > deviation: deviation = perp_check return deviation
def branches_splitting_boundary_kinks(self): """Add new branches to fix the problem of boundary kinks not marked by the skeleton Due to a low density that did not spot the change of curvature at the kink. Does not modify the singularites on the contrarty to increasing the density. Returns ------- new_branches : list List of polylines as list of point XYZ-coordinates. """ new_branches = [] compas_singular_faces = set(self.compas_singular_faces()) for boundary in self.boundaries(): angles = {(u, v, w): angle_vectors( subtract_vectors(self.vertex_coordinates(v), self.vertex_coordinates(u)), subtract_vectors(self.vertex_coordinates(w), self.vertex_coordinates(v))) for u, v, w in window(boundary + boundary[:2], n=3)} for u, v, w, x, y in list(window(boundary + boundary[:4], n=5)): # check if not a corner if self.vertex_degree(w) == 2: continue angle = angles[(v, w, x)] adjacent_angles = (angles[(u, v, w)] + angles[(w, x, y)]) / 2 if angle - adjacent_angles > self.relative_kink_angle_limit: # check if not already marked via an adjacent compas_singular face if all([ fkey not in compas_singular_faces for fkey in self.vertex_faces(w) ]): fkeys = list(self.vertex_faces(w, ordered=True)) fkey = fkeys[int(floor(len(fkeys) / 2))] for edge in self.face_halfedges(fkey): if w in edge and not self.is_edge_on_boundary( *edge): new_branches += [[ trimesh_face_circle(self, fkey)[0], self.vertex_coordinates(vkey) ] for vkey in edge] break return new_branches
def corner_vertices(self, tol=160): vkeys = [] for key in self.vertices_on_boundary(): if self.vertex_degree(key) == 2: vkeys.append(key) else: nbrs = [] for nkey in self.vertex_neighbors(key): if self.is_edge_on_boundary(key, nkey): nbrs.append(nkey) u = (self.edge_vector(key, nbrs[0])) v = (self.edge_vector(key, nbrs[1])) if angle_vectors(u, v, deg=True) < tol: vkeys.append(key) return vkeys
def network_node_find_first_neighbor(network, key): nbrs = network.neighbors(key) if len(nbrs) == 1: return nbrs[0] ab = [-1.0, -1.0, 0.0] a = network.node_coordinates(key, 'xyz') b = [a[0] + ab[0], a[1] + ab[1], 0] angles = [] for nbr in nbrs: c = network.node_coordinates(nbr, 'xyz') ac = [c[0] - a[0], c[1] - a[1], 0] alpha = angle_vectors(ab, ac) if is_ccw_xy(a, b, c, True): alpha = PI2 - alpha angles.append(alpha) return nbrs[angles.index(min(angles))]
def _find_first_neighbor(key, network): nbrs = list(network.halfedge[key].keys()) if len(nbrs) == 1: return nbrs[0] ab = [-1.0, -1.0, 0.0] a = network.vertex_coordinates(key, 'xyz') b = [a[0] + ab[0], a[1] + ab[1], 0] angles = [] for nbr in nbrs: c = network.vertex_coordinates(nbr, 'xyz') ac = [c[0] - a[0], c[1] - a[1], 0] alpha = angle_vectors(ab, ac) if is_ccw_xy(a, b, c, True): alpha = PI2 - alpha angles.append(alpha) return nbrs[angles.index(min(angles))]
def blocks(self): """Compute the blocks. Returns ------- list A list of blocks defined as simple meshes. Notes ----- This method is used by the ``from_geometry`` constructor of the assembly data structure to create an assembly "from geometry". """ if self.rise > self.span / 2: raise Exception("Not a semicircular arch.") radius = self.rise / 2 + self.span**2 / (8 * self.rise) # base = [0.0, 0.0, 0.0] top = [0.0, 0.0, self.rise] left = [-self.span / 2, 0.0, 0.0] center = [0.0, 0.0, self.rise - radius] vector = subtract_vectors(left, center) springing = angle_vectors(vector, [-1.0, 0.0, 0.0]) sector = radians(180) - 2 * springing angle = sector / self.n a = top b = add_vectors(top, [0, self.depth, 0]) c = add_vectors(top, [0, self.depth, self.thickness]) d = add_vectors(top, [0, 0, self.thickness]) R = Rotation.from_axis_and_angle([0, 1.0, 0], 0.5 * sector, center) bottom = transform_points([a, b, c, d], R) blocks = [] for i in range(self.n): R = Rotation.from_axis_and_angle([0, 1.0, 0], -angle, center) top = transform_points(bottom, R) vertices = bottom + top faces = [[0, 1, 2, 3], [7, 6, 5, 4], [3, 7, 4, 0], [6, 2, 1, 5], [7, 3, 2, 6], [5, 1, 0, 4]] mesh = Mesh.from_vertices_and_faces(vertices, faces) blocks.append(mesh) bottom = top return blocks
def angle_vectors(left, right): """Compute the smallest angle between corresponding pairs of two lists of vectors. Parameters ---------- left : list A list of vectors. right : list A list of vectors. Returns ------- list A list of angles. Examples -------- >>> Vector.angle_vectors([[1.0, 0.0, 0.0], [2.0, 0.0, 0.0]], [[0.0, 1.0, 0.0], [0.0, 0.0, 2.0]]) [1.5707963267948966, 1.5707963267948966] """ return [angle_vectors(u, v) for u, v in zip(left, right)]
def angle(self, other): """Compute the smallest angle between this vector and another vector. Parameters ---------- other : :class:`compas.geometry.Vector` or list The other vector. Returns ------- float The smallest angle between the two vectors. Examples -------- >>> u = Vector(1.0, 0.0, 0.0) >>> v = Vector(0.0, 1.0, 0.0) >>> u.angle(v) == 0.5 * math.pi True """ return angle_vectors(self, other)
def _find_first_neighbour(key, network): nbrs = list(network.halfedge[key].keys()) if len(nbrs) == 1: return nbrs[0] vu = [-1.0, -1.0, 0.0] a = network.vertex_coordinates(key, 'xyz') b = [a[0] + vu[0], a[1] + vu[1], 0] cw = [] for nbr in nbrs: c = network.vertex_coordinates(nbr, 'xyz') if not is_ccw_xy(a, b, c, True): cw.append(nbr) if cw: nbrs = cw angles = [] v = [network.vertex[key][_] for _ in 'xyz'] for nbr in nbrs: w = [network.vertex[nbr][_] for _ in 'xyz'] vw = [w[0] - v[0], w[1] - v[1], 0] angles.append(angle_vectors(vu, vw)) if cw: return nbrs[angles.index(min(angles))] return nbrs[angles.index(max(angles))]
def arch_from_rise_and_span(height, span, depth, thickness, n): """Create a semicircular arch from rise and span. Parameters ---------- height : float ... span : float Dimension of the span in meter measured at the impost level (intrados-intrados). depth : float Depth in meter of the arch perpendicular to the front view plane. thickness : float Thickness in meter of the arch. n: int number of voussoirs. Returns ------- Assembly Data structure of the semicircular arch. """ assembly = Assembly() if height > span / 2: raise Exception("Not a semicircular arch.") radius = height / 2 + (span**2 / (8 * height)) base = [0.0, 0.0, 0.0] top = [0.0, 0.0, height] left = [-span / 2, 0.0, 0.0] center = [0.0, 0.0, height - radius] vector = subtract_vectors(left, center) springing = angle_vectors(vector, [-1.0, 0.0, 0.0]) sector = radians(180) - 2 * springing angle = sector / n a = top b = add_vectors(top, [0, depth, 0]) c = add_vectors(top, [0, depth, thickness]) d = add_vectors(top, [0, 0, thickness]) R = Rotation.from_axis_and_angle([0, 1.0, 0], 0.5 * sector, center) bottom = transform_points([a, b, c, d], R) blocks = [] for i in range(n): R = Rotation.from_axis_and_angle([0, 1.0, 0], -angle, center) top = transform_points(bottom, R) vertices = bottom + top faces = [[0, 1, 2, 3], [7, 6, 5, 4], [3, 7, 4, 0], [6, 2, 1, 5], [7, 3, 2, 6], [5, 1, 0, 4]] block = Block.from_vertices_and_faces(vertices, faces) assembly.add_block(block) bottom = top assembly.node_attribute(0, 'is_support', True) assembly.node_attribute(n - 1, 'is_support', True) return assembly
mesh.face_attribute(key=fkey, name=name_1, value=vec) vec_2 = cross_vectors(vec, [0.0, 0.0, 1.0]) mesh.face_attribute(key=fkey, name=name_2, value=vec_2) recalibrated_perp[fkey, :] = vec_2 # ============================================================================= # Calculate resulting deviation # ============================================================================= base = values target = recalibrated_values deviations = np.zeros(mesh.number_of_faces()) for fkey in mesh.faces(): deviations[fkey] = angle_vectors(base[fkey], target[fkey], deg=True) # ============================================================================= # Compute MSE Loss # ============================================================================= losses = np.zeros(mesh.number_of_faces()) for fkey in mesh.faces(): losses[fkey] = length_vector_sqrd(subtract_vectors(base[fkey], target[fkey])) print("MSE Loss: {}".format(np.mean(deviations))) # ============================================================================= # Draw kmeans vectors # =============================================================================
def plot_2d(filename, plate_height, e_modulus, poisson=0, basis_direction="X", draw_boundary_edges=True, draw_faces=True, draw_faces_centroids=False, draw_colorbar=True, draw_edges=False, save_img=True, show_img=False): """ Calculates the bending strain energy in a plate. Parameters ---------- filename : `str` The name of the JSON file that stores the clustering resultes w.r.t certain \n mesh, attribute, alglrithm and number of clusters. """ moment_names = ["mx", "my", "mxy"] basis_vectors = {"X": [1, 0, 0], "Y": [0, 1, 0]} basis_vector = basis_vectors[basis_direction] # ========================================================================== # Load Mesh # ========================================================================== # load a mesh from a JSON file name_in = filename + ".json" json_in = os.path.abspath(os.path.join(JSON, name_in)) mesh = MeshPlus.from_json(json_in) # ========================================================================== # Select vector fields # ========================================================================== # select a vector field along which the strain energy will be calculated available_vf = mesh.vector_fields() print("Avaliable vector fields on the mesh are:\n", available_vf) while True: vf_name = input( "Select a vector field to calculate the metric along: ") if vf_name in available_vf: break print("This vector field is not available. Please try again.") vf = mesh.vector_field(vf_name) # select a vector field to take as the basis reference while True: msg = "Now choose a field as the PS directional basis. Often m_1: " vf_basis_name = input(msg) if vf_basis_name in available_vf: break print("This vector field is not available. Please try again.") # TODO: taking m_1 as reference. does it always hold? vf_ps = mesh.vector_field(vf_basis_name) # ========================================================================== # Choose structural metric to compute # ========================================================================== structural_metrics = { "energy": { "cbar_legend": "Strain Energy [kNm]" }, "work": { "cbar_label": "Work [kNm]" }, "volume": { "func": volume_reinforcement_bending, "cbar_label": "Volume [KNm]" }, "volume1": { "func": volume_reinforcement_bending_dir, "cbar_label": "Volume 1 [KNm]" }, "volume2": { "func": volume_reinforcement_bending_dir, "cbar_label": "Volume 2 [KNm]" } } energy_func = partial(strain_energy_bending, height=plate_height, e_modulus=e_modulus, poisson=poisson) structural_metrics["energy"]["func"] = energy_func work_func = partial(virtual_work_bending, height=plate_height, e_modulus=e_modulus, poisson=poisson) structural_metrics["work"]["func"] = work_func # Choose metric while True: msg = "What metric should I compute, energy, work, volume, volume1, volume2? " metric_name = input(msg) if metric_name in structural_metrics: break print("This metric is not available. Please try again.") structure_func = structural_metrics[metric_name]["func"] cbar_label = structural_metrics[metric_name]["cbar_label"] # ========================================================================== # Calculate bending strain energy # ========================================================================== # transform bending moments data = np.zeros(mesh.number_of_faces()) sorted_fkeys = sorted(list(mesh.faces())) # compute strain energy per face in the mesh for fkey in sorted_fkeys: # get bending moments stored in the mesh mx, my, mxy = mesh.face_attributes(fkey, names=moment_names) # calculate the principal bending moments m1a, _ = principal_stresses_and_angles(mx, my, mxy) _, angle1 = m1a # compute delta between reference vector and principal bending vector # TODO: will take m1 as reference. does this always hold? # basis vector is often the global X vector, [1, 0, 0] vec_ps = vf_ps[fkey] delta = angle1 - angle_vectors(vec_ps, basis_vector) # add delta to target transformation vector field vec = vf[fkey] theta = delta + angle_vectors(vec, basis_vector) # transform bending moments with theta m1, m2, m12 = transformed_stresses(mx, my, mxy, theta) # calculate value of structural metric if metric_name == "volume1": value = structure_func(m1, m12) elif metric_name == "volume2": value = structure_func(m2, m12) else: value = structure_func(m1, m2, m12) # store the bending strain energy data[fkey] = value # ========================================================================== # Calculate total bending strain energy # ========================================================================== total_data = data.sum() print("Plate {0}: {1}".format(metric_name, round(total_data, 2))) # ========================================================================== # Plot Mesh # ========================================================================== # ClusterPlotter is a custom wrapper around a COMPAS MeshPlotter plotter = MeshPlusPlotter(mesh, figsize=(16, 9), dpi=300) if draw_boundary_edges: plotter.draw_edges(keys=list(mesh.edges_on_boundary())) # draw mesh edges if draw_edges: plotter.draw_edges() # color up the faces of the mesh according to their cluster if draw_faces or draw_faces_centroids: if draw_faces: collection = plotter.draw_faces(keys=sorted_fkeys) elif draw_faces_centroids: points = [] for fkey in sorted_fkeys: point = {} point["pos"] = mesh.face_centroid(fkey) point["radius"] = 0.03 point["edgewidth"] = 0.10 points.append(point) collection = plotter.draw_points(points) collection.set(array=data, cmap="YlGnBu") # Spectral, BrBG, viridis, PiYG collection.set_linewidth(lw=0.0) if draw_colorbar: # create label for color map ticks = np.linspace(data.min(), data.max(), 7) ticks_labels = [np.round(x, 2) for x in ticks] extend = "both" colorbar = plt.colorbar(collection, shrink=0.9, pad=0.01, extend=extend, extendfrac=0.05, ax=plotter.axes, aspect=30, orientation="vertical") colorbar.set_ticks(ticks) colorbar.ax.set_yticklabels(ticks_labels) colorbar.set_label(cbar_label, fontsize="large") # save image if save_img: dt = datetime.now().strftime("%Y_%m_%d_%H_%M_%S") img_name = filename.split( "/")[-1] + metric_name + "_" + vf_name + "_" + dt + ".png" img_path = os.path.abspath(os.path.join(DATA, "images", img_name)) plt.tight_layout() plotter.save(img_path, bbox_inches='tight', pad_inches=0.0) print("Saved image to : {}".format(img_path)) # show to screen if show_img: plotter.show()
"""Assignment: 1a Given two vectors, use the cross product to create a set of three orthonormal vectors. 1b Use the cross product to compute the area of a convex, 2D polygon. 1c Define a function for computing the cross products of two same-length arrays of vectors by a. Prototype in pure Python (loop over the arrays). b. Make Numpy equivalent without loops. """ # 1a. Given two vectors, use the cross product to create a set of three orthonormal vectors. from compas.geometry import cross_vectors from compas.geometry import angle_vectors import math as m v1 = [1,2,3] v2 = [4,5,6] # replace #... and fill in there x1 = #... x2 = #... x3 = #... print(x1) print(x2) print(x3) print(m.degrees(angle_vectors(x1, x2))) print(m.degrees(angle_vectors(x1, x3))) print(m.degrees(angle_vectors(x2, x3)))
mesh = Mesh() mesh.load(HERE) mesh_unify_cycles(mesh) # ========================================================================== # Create PS vector lines # ========================================================================== vector_tag = 'ps_1_top' # ps_1_top angles = {} centroids = {} for fkey, attr in mesh.faces(data=True): vector = attr.get(vector_tag) angle = angle_vectors([1.0, 0.0, 0.0], vector, deg=True) angles[fkey] = angle centroids[fkey] = np.array(mesh.face_centroid(fkey)) print('max angle', min(angles.values())) print('min angle', max(angles.values())) for idx, angle in angles.items(): if angle <= 90.0: continue angles[idx] = 180.0 - angle print('max angle', min(angles.values())) print('min angle', max(angles.values())) anglemax = max(angles.values())
def set_angle(self, vector): angle = cg.angle_vectors([1.0, 0.0, 0.0], vector, deg=True) if angle > 90.0: angle = 180.0 - angle self.angle = angle
descdent_tree = copy.deepcopy(convex_hull_mesh.halfedge) for u, v in convex_hull_mesh.edges(): descdent_tree[u][v] = {'jp': None, 'lp': None} descdent_tree[v][u] = {'jp': None, 'lp': None} current_key = convex_hull_mesh.number_of_vertices() for fkey in convex_hull_mesh.faces(): f_centroid = convex_hull_mesh.face_centroid(fkey) vec = Vector.from_start_end(pt_center, f_centroid) # if the branches has a 'convex' corner, # flip the vec to the corresponding face. f_normal = convex_hull_mesh.face_normal(fkey) angle = angle_vectors(f_normal, vec, False) if angle > math.pi * 0.5: # vec.scale(-1) pln = Plane(pt_center, f_normal) pt_mirror = mirror_point_plane(f_centroid, pln) vec = Vector.from_start_end(pt_center, pt_mirror) # angle = math.pi - angle vec.unitize() # dist = joint_width / math.cos(angle) # vec.scale(dist) vec.scale(joint_width) pt = add_vectors(pt_center, vec)
def test_angle_vectors(u, v, angle): assert angle_vectors(u, v) == pytest.approx(angle)
from compas.geometry import cross_vectors from compas.geometry import angle_vectors u = [1.0, 0.0, 0.0] v = [0.0, 1.0, 0.0] print(angle_vectors(u, v)) print(angle_vectors(v, u))
def test_angle_vectors_fails_when_input_is_zero(u, v): with pytest.raises(ZeroDivisionError): angle_vectors(u, v)
def test_angle_vectors(u, v, angle): assert close(angle_vectors(u, v), angle)
>>>>>>> 584331387c8b670cd9258e4c252df49c39192943 # ---------------------------------------------------------------------- # 4. update force diagram # ---------------------------------------------------------------------- if weight != 1: volmesh_planarise(volmesh, kmax=1, target_normals=target_normals) # boundary perpness for fkey in boundary_fkeys: f_normal = volmesh.halfface_normal(fkey) target_normal = target_normals[fkey] b_perpness = angle_vectors(f_normal, target_normal, deg=True) # 1 - abs(dot_vectors(f_normal, target_normal)) if b_perpness > deviation_boundary_perp: deviation_boundary_perp = b_perpness # ---------------------------------------------------------------------- # 5. check convergence # ---------------------------------------------------------------------- perpness = _check_deviation(volmesh, formdiagram) if perpness < tolerance and deviation_boundary_perp < tolerance_boundary: if print_result_info: print_result('Reciprocation', k, perpness)