def distance_point_line(point, line): """Compute the distance between a point and a line. This implementation computes the *right angle distance* from a point P to a line defined by points A and B as twice the area of the triangle ABP divided by the length of AB. Parameters ---------- point : list, tuple Point location. line : list, tuple Line defined by two points. Returns ------- d : float The distance between the point and the line. Examples -------- >>> References ---------- https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line """ a, b = line ab = subtract_vectors(b, a) pa = subtract_vectors(a, point) pb = subtract_vectors(b, point) l = length_vector(cross_vectors(pa, pb)) l_ab = length_vector(ab) return l / l_ab
def angle_vectors(u, v, deg=False): """Compute the smallest angle between two vectors. Parameters ---------- u : sequence of float XYZ components of the first vector. v : sequence of float XYZ components of the second vector. deg : boolean returns angle in degrees if True Returns ------- float The smallest angle in radians (in degrees if deg == True). The angle is always positive. Examples -------- >>> angle_vectors([0.0, 1.0, 0.0], [1.0, 0.0, 0.0]) """ a = dot_vectors(u, v) / (length_vector(u) * length_vector(v)) a = max(min(a, 1), -1) if deg: return degrees(acos(a)) return acos(a)
def area_polygon(polygon): """Compute the area of a polygon. Parameters ---------- polygon : sequence The XYZ coordinates of the vertices/corners of the polygon. The vertices are assumed to be in order. The polygon is assumed to be closed: the first and last vertex in the sequence should not be the same. Returns ------- float The area of the polygon. """ o = centroid_points(polygon) u = subtract_vectors(polygon[-1], o) v = subtract_vectors(polygon[0], o) a = 0.5 * length_vector(cross_vectors(u, v)) for i in range(0, len(polygon) - 1): u = v v = subtract_vectors(polygon[i + 1], o) a += 0.5 * length_vector(cross_vectors(u, v)) return a
def lines_tangent_to_cylinder(base_point, line_vect, ref_point, dist): """Calculating of plane tangents to one cylinder See SP dissertation 3.1.3.b (p. 74) .. image:: ../images/plane_tangent_to_one_cylinder.png :scale: 80 % :align: center Parameters ---------- base_point : [type] [description] line_vect : [type] vector [other end of the axis, base_point], **the direction here is very important!** ref_point : point new point Q dist : float radius of the cylinder Returns ------- list [origin point M, vector MB, -1 * vector MB], the latter two entries represent the tangent points' local coordinate in the plane [point M, e_x, e_y] """ l_vect = normalize_vector(line_vect) line_QMprime = subtract_vectors(ref_point, base_point) # ppol = point M, project out longitutude axis component of base_point ppol = add_vectors(base_point, scale_vector(l_vect, dot_vectors(line_QMprime, l_vect))) # ppolr_vect = line QB line_QM = subtract_vectors(ref_point, ppol) e_x = normalize_vector(line_QM) e_y = cross_vectors(e_x, l_vect) if length_vector(line_QM) == 0: return None x = dist / length_vector(line_QM) # x coordinate in the local axis d_e1 = scale_vector(e_x, dist * x) # d(radius of bar section) has to be larger than l(distance from point to bar axis), otherwise the sqrt turns negative # if d < l: change ref_point if x * x < 1.0: # y coordinate in the local axis d_e2 = scale_vector(e_y, dist * math.sqrt(1.0 - x * x)) else: return None # upper tangent point d_e_add = add_vectors(d_e1, d_e2) # lower tangent point d_e_sub = subtract_vectors(d_e1, d_e2) return [ppol, d_e_add, d_e_sub]
def centroid_polygon_edges(polygon): """Compute the centroid of the edges of a polygon. Parameters ---------- polygon : list of point A sequence of polygon point coordinates. Returns ------- list The XYZ coordinates of the centroid. Notes ----- The centroid of the edges is the centroid of the midpoints of the edges, with each midpoint weighted by the length of the corresponding edge proportional to the total length of the boundary. """ L = 0 cx = 0 cy = 0 cz = 0 p = len(polygon) for i in range(-1, p - 1): p1 = polygon[i] p2 = polygon[i + 1] d = length_vector(subtract_vectors(p2, p1)) cx += 0.5 * d * (p1[0] + p2[0]) cy += 0.5 * d * (p1[1] + p2[1]) cz += 0.5 * d * (p1[2] + p2[2]) L += d return [cx / L, cy / L, cz / L]
def distance_point_point(a, b): """Compute the distance bewteen a and b. Parameters ---------- a : sequence of float XYZ coordinates of point a. b : sequence of float XYZ coordinates of point b. Returns ------- d : float Distance bewteen a and b. Examples -------- >>> distance_point_point([0.0, 0.0, 0.0], [2.0, 0.0, 0.0]) 2.0 See Also -------- distance_point_point_xy """ ab = subtract_vectors(b, a) return length_vector(ab)
def normal_triangle(triangle, unitized=True): """Compute the normal vector of a triangle. Parameters ---------- triangle : list of list A list of triangle point coordinates. Returns ------- list The normal vector. Raises ------ ValueError If the triangle does not have three vertices. """ assert len(triangle) == 3, "Three points are required." a, b, c = triangle ab = subtract_vectors(b, a) ac = subtract_vectors(c, a) n = cross_vectors(ab, ac) if not unitized: return n lvec = length_vector(n) return n[0] / lvec, n[1] / lvec, n[2] / lvec
def normal_polygon(points, unitized=True): """Compute the normal of a polygon defined by a sequence of points. Parameters: points (sequence): A sequence of points. Returns: list: The normal vector. Raises: ValueError: If less than three points are provided. Notes: The points in the list should be unique. For example, the first and last point in the list should not be the same. """ p = len(points) assert p > 2, "At least three points required" nx = 0 ny = 0 nz = 0 o = centroid_points(points) a = subtract_vectors(points[-1], o) for i in range(p): b = subtract_vectors(points[i], o) n = cross_vectors(a, b) a = b nx += n[0] ny += n[1] nz += n[2] if not unitized: return nx, ny, nz l = length_vector([nx, ny, nz]) return nx / l, ny / l, nz / l
def _normal_polygon(points, normalised=True): """Compute the normal of a polygon defined by a sequence of points. Note: The points in the list should be unique. For example, the first and last point in the list should not be the same. Parameters: points (sequence): A sequence of points. Returns: list: The normal vector. Raises: ValueError: If less than three points are provided. """ p = len(points) assert p > 2, "At least three points required" nx = 0 ny = 0 nz = 0 for i in range(-1, p - 1): p1 = points[i - 1] p2 = points[i] p3 = points[i + 1] v1 = subtract_vectors(p1, p2) v2 = subtract_vectors(p3, p2) n = cross_vectors(v1, v2) nx += n[0] ny += n[1] nz += n[2] if not normalised: return nx, ny, nz l = length_vector([nx, ny, nz]) return nx / l, ny / l, nz / l
def normal_triangle(triangle, unitized=True): """Compute the normal vector of a triangle. """ assert len(triangle) == 3, "Three points are required." a, b, c = triangle ab = subtract_vectors(b, a) ac = subtract_vectors(c, a) n = cross_vectors(ab, ac) if not unitized: return n lvec = length_vector(n) return n[0] / lvec, n[1] / lvec, n[2] / lvec
def normal_polygon(polygon, unitized=True): """Compute the normal of a polygon defined by a sequence of points. Parameters ---------- polygon : list of list A list of polygon point coordinates. Returns ------- list The normal vector. Raises ------ ValueError If less than three points are provided. Notes ----- The points in the list should be unique. For example, the first and last point in the list should not be the same. """ p = len(polygon) assert p > 2, "At least three points required" nx = 0 ny = 0 nz = 0 o = centroid_points(polygon) a = polygon[-1] oa = subtract_vectors(a, o) for i in range(p): b = polygon[i] ob = subtract_vectors(b, o) n = cross_vectors(oa, ob) oa = ob nx += n[0] ny += n[1] nz += n[2] if not unitized: return nx, ny, nz l = length_vector([nx, ny, nz]) return nx / l, ny / l, nz / l
def angle_smallest_vectors(u, v): """Compute the smallest angle (radians) between two vectors. Parameters ---------- u : sequence of float XYZ components of the first vector. v : sequence of float) XYZ components of the second vector. Returns ------- float The smallest angle in radians. The angle is always positive. Examples -------- >>> angle_smallest_vectors([0.0, 1.0, 0.0], [1.0, 0.0, 0.0]) """ a = dot_vectors(u, v) / (length_vector(u) * length_vector(v)) a = max(min(a, 1), -1) return acos(a)
def area_triangle(triangle): """Compute the area of a triangle defined by three points. Parameters ---------- triangle : list of list XYZ coordinates of the corners of the triangle. Returns ------- float The area of the triangle. """ return 0.5 * length_vector(normal_triangle(triangle, False))
def distance_line_line(l1, l2, tol=0.0): """Compute the shortest distance between two lines. Parameters ---------- l1 : tuple Two points defining a line. l2 : tuple Two points defining a line. Returns ------- d : float The distance between the two lines. Notes ----- The distance is the absolute value of the dot product of a unit vector that is perpendicular to the two lines, and the vector between two points on the lines ([1]_, [2]_). If each of the lines is defined by two points (:math:`l_1 = (\mathbf{x_1}, \mathbf{x_2})`, :math:`l_2 = (\mathbf{x_3}, \mathbf{x_4})`), then the unit vector that is perpendicular to both lines is... References ---------- .. [1] Weisstein, E.W. *Line-line Distance*. Available at: http://mathworld.wolfram.com/Line-LineDistance.html. .. [2] Wikipedia. *Skew lines Distance*. Available at: https://en.wikipedia.org/wiki/Skew_lines#Distance. Examples -------- >>> """ a, b = l1 c, d = l2 ab = subtract_vectors(b, a) cd = subtract_vectors(d, c) ac = subtract_vectors(c, a) n = cross_vectors(ab, cd) l = length_vector(n) if l <= tol: return distance_point_point(closest_point_on_line(l1[0], l2), l1[0]) n = scale_vector(n, 1.0 / l) return fabs(dot_vectors(n, ac))
def circle_from_points(a, b, c): """Construct a circle from three points. Parameters ---------- a : sequence of float XYZ coordinates. b : sequence of float XYZ coordinates. c : sequence of float XYZ coordinates. Returns ------- circle : tuple Center, radius, normal of the circle. References ---------- https://en.wikipedia.org/wiki/Circumscribed_circle Examples -------- >>> """ ab = subtract_vectors(b, a) cb = subtract_vectors(b, c) ba = subtract_vectors(a, b) ca = subtract_vectors(a, c) ac = subtract_vectors(c, a) bc = subtract_vectors(c, b) normal = normalize_vector(cross_vectors(ab, ac)) d = 2 * length_vector_sqrd(cross_vectors(ba, cb)) A = length_vector_sqrd(cb) * dot_vectors(ba, ca) / d B = length_vector_sqrd(ca) * dot_vectors(ab, cb) / d C = length_vector_sqrd(ba) * dot_vectors(ac, bc) / d Aa = scale_vector(a, A) Bb = scale_vector(b, B) Cc = scale_vector(c, C) center = sum_vectors([Aa, Bb, Cc]) radius = length_vector(subtract_vectors(a, center)) return center, radius, normal
def angle_vectors_signed(u, v, normal, deg=False, threshold=1e-3): """Computes the signed angle between two vectors. It calculates the angle such that rotating vector u about the normal by angle would result in a vector that looks into the same direction as v. Parameters ---------- u : sequence of float XYZ components of the first vector. v : sequence of float XYZ components of the second vector. normal : sequence of float XYZ components of the plane's normal spanned by u and v. deg : boolean returns angle in degrees if True threshold : The threshold (radians) used to consider if the angle is zero. Defaults to 1e-3. Returns ------- float The signed angle in radians (in degrees if deg == True). Examples -------- >>> normal = [0.0, 0.0, 1.0] >>> angle_vectors_signed([0.0, 1.0, 0.0], [1.0, 0.0, 0.0], normal) """ angle = angle_vectors(u, v) normal_uv = cross_vectors(u, v) if length_vector(normal_uv) > threshold: # check if normal_uv has the same direction as normal angle_btw_normals = angle_vectors(normal, normal_uv) if angle_btw_normals > threshold: angle *= -1 if deg: return degrees(angle) else: return angle
def from_axis_angle_vector(cls, axis_angle_vector, point=[0, 0, 0]): """Calculates a ``Rotation`` from an axis-angle vector. Args: axis_angle_vector (:obj:`list` of :obj:`float`): Three numbers that represent the axis of rotation and angle of rotation through the vector's magnitude. point (:obj:`list` of :obj:`float`, optional): A point to perform a rotation around an origin other than [0, 0, 0]. Example: >>> aav1 = [-0.043, -0.254, 0.617] >>> R = Rotation.from_axis_angle_vector(aav1) >>> aav2 = R.axis_angle_vector >>> allclose(aav1, aav2) True """ axis_angle_vector = list(axis_angle_vector) angle = length_vector(axis_angle_vector) return cls.from_axis_and_angle(axis_angle_vector, angle, point)
def center_of_mass_polygon(polygon): """Compute the center of mass of a polygon defined as a sequence of points. The center of mass of a polygon is the centroid of the midpoints of the edges, each weighted by the length of the corresponding edge. Parameters ---------- polygon : sequence A sequence of XYZ coordinates representing the locations of the corners of a polygon. Returns ------- tuple of floats The XYZ coordinates of the center of mass. Examples -------- >>> pts = [(0.,0.,0.),(1.,0.,0.),(0.,10.,0.)] >>> print("Center of mass: {0}".format(center_of_mass(pts))) >>> print("Centroid: {0}".format(centroid(pts))) """ L = 0 cx = 0 cy = 0 cz = 0 p = len(polygon) for i in range(-1, p - 1): p1 = polygon[i] p2 = polygon[i + 1] d = length_vector(subtract_vectors(p2, p1)) cx += 0.5 * d * (p1[0] + p2[0]) cy += 0.5 * d * (p1[1] + p2[1]) cz += 0.5 * d * (p1[2] + p2[2]) L += d cx = cx / L cy = cy / L cz = cz / L return cx, cy, cz
def matrix_from_axis_angle_vector(axis_angle_vector, point=[0, 0, 0]): """Calculates a rotation matrix from an axis-angle vector. Parameters ---------- axis_angle_vector : list of float Three numbers that represent the axis of rotation and angle of rotation through the vector's magnitude. point : list of float, optional A point to perform a rotation around an origin other than [0, 0, 0]. Examples -------- >>> aav1 = [-0.043, -0.254, 0.617] >>> R = matrix_from_axis_angle_vector(aav1) >>> aav2 = axis_angle_vector_from_matrix(R) >>> allclose(aav1, aav2) True """ axis = list(axis_angle_vector) angle = length_vector(axis_angle_vector) return matrix_from_axis_and_angle(axis, angle, point)
def matrix_from_axis_and_angle(axis, angle, point=None, rtype='list'): """Calculates a rotation matrix from an rotation axis, an angle and an optional point of rotation. Parameters ---------- axis : list of float Three numbers that represent the axis of rotation. angle : float The rotation angle in radians. point : list of float, optional A point to perform a rotation around an origin other than [0, 0, 0]. Returns ------- list of list of float The matrix. Notes ----- The rotation is based on the right hand rule, i.e. anti-clockwise if the axis of rotation points towards the observer. Examples -------- >>> axis1 = normalize_vector([-0.043, -0.254, 0.617]) >>> angle1 = 0.1 >>> R = matrix_from_axis_and_angle(axis1, angle1) >>> axis2, angle2 = axis_and_angle_from_matrix(R) >>> allclose(axis1, axis2) True >>> allclose([angle1], [angle2]) True """ if not point: point = [0.0, 0.0, 0.0] axis = list(axis) if length_vector(axis): axis = normalize_vector(axis) sina = math.sin(angle) cosa = math.cos(angle) R = [[cosa, 0.0, 0.0], [0.0, cosa, 0.0], [0.0, 0.0, cosa]] outer_product = [[axis[i] * axis[j] * (1.0 - cosa) for i in range(3)] for j in range(3)] R = [[R[i][j] + outer_product[i][j] for i in range(3)] for j in range(3)] axis = scale_vector(axis, sina) m = [[0.0, -axis[2], axis[1]], [axis[2], 0.0, -axis[0]], [-axis[1], axis[0], 0.0]] M = [[1. if x == y else 0. for x in range(4)] for y in range(4)] for i in range(3): for j in range(3): R[i][j] += m[i][j] M[i][j] = R[i][j] # rotation about axis, angle AND point includes also translation t = subtract_vectors(point, multiply_matrix_vector(R, point)) M[0][3] = t[0] M[1][3] = t[1] M[2][3] = t[2] if rtype == 'list': return M if rtype == 'array': from numpy import asarray return asarray(M) raise NotImplementedError
def centroid_polygon(polygon): r"""Compute the centroid of the surface of a polygon. Parameters ---------- polygon : list of point A sequence of polygon point coordinates. Returns ------- list The XYZ coordinates of the centroid. Examples -------- .. code-block:: python from compas.geometry import centroid_polygon polygon = [ [0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [1.0, 1.0, 0.0], [0.0, 1.0, 0.0] ] c = centroid_polygon(polygon) print(c) # [0.5, 0.5, 0.0] .. code-block:: python from compas.geometry import centroid_polygon from compas.geometry import centroid_points polygon = [ [0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [1.0, 1.0, 0.0], [0.5, 1.0, 0.0], [0.0, 1.0, 0.0] ] c = centroid_polygon(polygon) print(c) # [0.5, 0.5, 0.0] c = centroid_points(polygon) print(c) # [0.5, 0.6, 0.0] Notes ----- The centroid is the centre of gravity of the polygon surface if mass would be uniformly distributed over it. It is calculated by triangulating the polygon surface with respect to the centroid of the polygon vertices, and then computing the centroid of the centroids of the individual triangles, weighted by the corresponding triangle area in proportion to the total surface area. .. math:: c_x = \frac{1}{A} \sum_{i=1}^{N} A_i \cdot c_{x,i} c_y = \frac{1}{A} \sum_{i=1}^{N} A_i \cdot c_{y,i} c_z = \frac{1}{A} \sum_{i=1}^{N} A_i \cdot c_{z,i} Warning ------- The polygon need not be convex. The polygon need not be flat. However, it is unclear what the meaning of the centroid is in that case. The polygon may be self-intersecting. However, it is unclear what the meaning of the centroid is in that case. """ p = len(polygon) assert p > 2, "At least three points required" if p == 3: return centroid_points(polygon) cx, cy, cz = 0.0, 0.0, 0.0 A2 = 0 o = centroid_points(polygon) a = polygon[-1] b = polygon[0] oa = subtract_vectors(a, o) ob = subtract_vectors(b, o) n0 = cross_vectors(oa, ob) x, y, z = centroid_points([o, a, b]) a2 = length_vector(n0) A2 += a2 cx += a2 * x cy += a2 * y cz += a2 * z for i in range(1, len(polygon)): a = b b = polygon[i] oa = ob ob = subtract_vectors(b, o) n = cross_vectors(oa, ob) x, y, z = centroid_points([o, a, b]) if dot_vectors(n, n0) > 0: a2 = length_vector(n) else: a2 = -length_vector(n) A2 += a2 cx += a2 * x cy += a2 * y cz += a2 * z return [cx / A2, cy / A2, cz / A2]
n = cross_vectors(ab, ac) V += dot_vectors(a, n) return V / 6. # ============================================================================== # Main # ============================================================================== if __name__ == "__main__": from compas.geometry import Polyhedron cube = Polyhedron.generate(6) L = length_vector(subtract_vectors(cube.vertices[0], cube.vertices[1])) V1 = L * L * L V2 = volume_polyhedron(cube) print(V1 - V2 <= 1e-6) # plotter = Plotter(figsize=(10, 7)) # polygon = [ # [0, 0, 0], # [1.0, 0, 0], # [1.0, 1.0, 0], # [0.5, 0.0, 0], # [0, 1.0, 0] # ]
def area_triangle(triangle): """Compute the area of a triangle defined by three points. """ return 0.5 * length_vector(normal_triangle(triangle, False))
def area_polygon(polygon): """Compute the area of a polygon. Parameters ---------- polygon : sequence The XYZ coordinates of the vertices/corners of the polygon. The vertices are assumed to be in order. The polygon is assumed to be closed: the first and last vertex in the sequence should not be the same. Returns ------- float The area of the polygon. Examples -------- .. plot:: :include-source: from compas.geometry import area_polygon from compas_plotters import Plotter plotter = Plotter() polygon = [ [0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [1.0, 1.0, 0.0], [0.0, 1.0, 0.0] ] area_polygon(polygon) # 1.0 plotter.draw_polygons([{'points': polygon}]) plotter.show() .. plot:: :include-source: from compas.geometry import area_polygon from compas_plotters import Plotter plotter = Plotter() polygon = [ [0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [1.0, 1.0, 0.0], [0.5, 0.0, 0.0], [0.0, 1.0, 0.0] ] area_polygon(polygon) # 0.5 plotter.draw_polygons([{'points': polygon}]) plotter.show() """ o = centroid_points(polygon) a = polygon[-1] b = polygon[0] oa = subtract_vectors(a, o) ob = subtract_vectors(b, o) n0 = cross_vectors(oa, ob) area = 0.5 * length_vector(n0) for i in range(0, len(polygon) - 1): oa = ob b = polygon[i + 1] ob = subtract_vectors(b, o) n = cross_vectors(oa, ob) if dot_vectors(n, n0) > 0: area += 0.5 * length_vector(n) else: area -= 0.5 * length_vector(n) return area
def length_vectors(vectors): return [length_vector(vector) for vector in vectors]
def length(self): """float: The length of this ``Vector``.""" return length_vector(self)
def calculate_gripping_plane(b_struct, v, pt_mean, nb_rot=8, nb_trans=8, planes_rot=True, planes_trans=True): """calculate gripping planes for a given bar structure and the vertex key (representing a bar) Parameters ---------- b_struct : [type] [description] v : [type] [description] pt_mean : [type] [description] nb_rot : int, optional number of rotational division, by default 8 nb_trans : int, optional number of translational division, by default 8 planes_rot : bool, optional [description], by default True planes_trans : bool, optional [description], by default True """ end_pts_0 = b_struct.vertex[v]["axis_endpoints"] # local coordinate system vec_x, vec_y, vec_z = calculate_coord_sys(end_pts_0, pt_mean) # mid point of the bar axis pt_o = centroid_points(end_pts_0) b_struct.vertex[v].update({"gripping_plane": (pt_o, vec_x, vec_y, vec_z)}) gripping_plane = b_struct.vertex[v]["gripping_plane"] frames_all = [] if planes_trans == True: # extend both end points for 30 mm vec_bar = scale_vector( normalize_vector(subtract_vectors(end_pts_0[1], end_pts_0[0])), 30) pt1 = add_vectors(end_pts_0[0], vec_bar) vec_bar = scale_vector(vec_bar, -1) pt2 = add_vectors(end_pts_0[1], vec_bar) vec_n = subtract_vectors(pt2, pt1) len_vec = length_vector(vec_n) len_new = len_vec / (nb_trans - 1) for i in range(nb_trans): origin = add_vectors( pt1, scale_vector(normalize_vector(vec_n), len_new * i)) frame_n = [origin, gripping_plane[1], gripping_plane[2]] if not planes_rot: frames_all.append(frame_n) # if planes_flip == True: # frame_n = [frame_n[0], scale_vector(frame_n[1], -1), scale_vector(frame_n[2], -1)] # frames_all.append(frame_n) else: ang = math.radians(360 / nb_rot) for n in range(nb_rot): gripping_plane = frame_n vecs_n = rotate_points( [gripping_plane[1], gripping_plane[2]], angle=n * ang, axis=subtract_vectors(end_pts_0[1], end_pts_0[0]), origin=(0, 0, 0)) frame_n = [gripping_plane[0], vecs_n[0], vecs_n[1]] frames_all.append(frame_n) # if planes_flip == True: # frame_n = [frame_n[0], scale_vector(frame_n[1], -1), scale_vector(frame_n[2], -1)] # frames_all.append(frame_n) elif planes_rot == True: ang = math.radians(360 / nb_rot) for n in range(nb_rot): vecs_n = rotate_points([gripping_plane[1], gripping_plane[2]], angle=n * ang, axis=subtract_vectors( end_pts_0[1], end_pts_0[0]), origin=(0, 0, 0)) frame_n = [gripping_plane[0], vecs_n[0], vecs_n[1]] frames_all.append(frame_n) # if planes_flip == True: # frame_n = [frame_n[0], scale_vector(frame_n[1], -1), scale_vector(frame_n[2], -1)] # frames_all.append(frame_n) for i, f in enumerate(frames_all): z_vec = cross_vectors(f[1], f[2]) frames_all[i].append(z_vec) b_struct.vertex[v].update({"gripping_planes_all": frames_all})