def __init__(self, board_layer, identification, board_no_in_layer, board_dimensions, z_value_toppoint, length_dir, width_dir): self.index = identification self.layer = board_layer self.no_in_layer = board_no_in_layer self.dimensions = board_dimensions self.width = self.dimensions[0] self.height = self.dimensions[1] self.length = self.dimensions[2] self.drop_point = Point(0, 0, 0) self.centre_point = Point(0, 0, 0) self.z_drop = z_value_toppoint self.length_direction = length_dir self.width_direction = width_dir self.glue_givers = [] self.glue_receivers = [] self.receiving_neighbours = [] self.giving_neighbours = [] if self.length_direction == 0: self.length_vector = Vector(1, 0, 0) self.width_vector = Vector(0, 1, 0) else: self.length_vector = Vector(0, 1, 0) self.width_vector = Vector(1, 0, 0) self.board_frame = Frame(self.centre_point, self.length_vector, self.width_vector) self.box = Box(self.board_frame, self.length, self.width, self.height)
def arrays_cross(array1, array2): cross_list = [] for i in range(len(array1)): u = Vector(array1[i][0], array1[i][1], array1[i][2]) v = Vector(array2[i][0], array2[i][1], array2[i][2]) cross_list.append(u.cross(v)) return cross_list
def from_data(cls, data): """Construct a PrintPoint from its data representation. Parameters ---------- data: dict The data dictionary. Returns ------- layer The constructed PrintPoint. """ pp = cls(pt=Point.from_data(data['point']), layer_height=data['layer_height'], mesh_normal=Vector.from_data(data['mesh_normal'])) pp.up_vector = Vector.from_data(data['up_vector']) pp.frame = Frame.from_data(data['frame']) pp.extruder_toggle = data['extruder_toggle'] pp.velocity = data['velocity'] pp.wait_time = data['wait_time'] pp.blend_radius = data['blend_radius'] pp.closest_support_pt = Point.from_data(data['closest_support_pt']) pp.distance_to_support = data['distance_to_support'] pp.is_feasible = data['is_feasible'] pp.attributes = data['attributes'] return pp
def __init__(self, pt, layer_height, mesh_normal): assert isinstance(pt, compas.geometry.Point) assert isinstance(mesh_normal, compas.geometry.Vector) assert layer_height # --- basic printpoint self.pt = pt self.layer_height = layer_height self.mesh_normal = mesh_normal # compas.geometry.Vector self.up_vector = Vector(0, 0, 1) # default value that can be updated self.frame = self.get_frame() # compas.geometry.Frame # --- attributes transferred from the mesh (vertex / face attributes) self.attributes = {} # dict. To fill this in, # --- print_organization related attributes self.extruder_toggle = None # bool self.velocity = None # float (mm/s) self.wait_time = None # float (sec) self.blend_radius = None # float (mm) # --- relation to support self.closest_support_pt = None # <compas.geometry.Point> self.distance_to_support = None # float self.is_feasible = True # bool
def is_cww(pt_A: Tuple[float], pt_B: Tuple[float], pt_C: Tuple[float]) -> bool: """ Checks if vector AB is oriented clockwise or counter-clockwise to vector AC. Parameters ---------- pt_a, pt_b, pt_c : list or tuple of floats Returns ------- bool >>> is_cww((20,30), (40,10), (12,25)) False """ # create the two vectors, but one is rotated 90 degrees # based on https://gamedev.stackexchange.com/questions/45412/ vector_AC = Vector.from_start_end((*pt_A, 0), (*pt_C, 0)) rot_B = rotate_points_xy([pt_B], radians(90), pt_A) rotated_AB = Vector.from_start_end((*pt_A, 0), *rot_B) # positive dot product ccw angle_between = dot_vectors_xy(rotated_AB, vector_AC) if angle_between > 0: return True if angle_between < 0: return False raise ValueError('Vectors have the same direction')
def pick_frame_from_grid(index, bullet_height): """Get next picking frame. Parameters ---------- index : int Counter to iterate through picking positions. bullet_height : float Height of bullet to pick up. Returns ------- list of `class`:compas.geometry.Frame """ # If index is larger than amount on picking plate, start from zero again index = index % (fab_conf["pick"]["xnum"].get() * fab_conf["pick"]["ynum"].get()) xpos = index % fab_conf["pick"]["xnum"].get() ypos = index // fab_conf["pick"]["xnum"].get() x = (fab_conf["pick"]["origin_grid"]["x"].get() + xpos * fab_conf["pick"]["grid_spacing"].get()) y = (fab_conf["pick"]["origin_grid"]["y"].get() + ypos * fab_conf["pick"]["grid_spacing"].get()) z = bullet_height * fab_conf["pick"]["compression_height_factor"].get() frame = Frame( Point(x, y, z), Vector(*fab_conf["pick"]["xaxis"].get()), Vector(*fab_conf["pick"]["yaxis"].get()), ) log.debug("Picking frame {:03d}: {}".format(index, frame)) return frame
def board_setup(self): for my_layer in range(layer_no): if my_layer % 2 == 0: my_frame = self.origin_fr my_grid = self.ceiling_grids[0] else: my_frame = self.sec_fr my_grid = self.ceiling_grids[1][0] my_dir1 = normalize_vector(my_frame[1]) my_dir2 = normalize_vector(my_frame[2]) # we have to separate i/board_code because of possible exceptions in the centre board_code = 0 for i, dist in enumerate(my_grid): my_board = self.timberboards[my_layer][board_code] # for the inner parts if self.skipping and 0 < my_layer < self.layer_no - 1 and my_layer%2 == 0 and \ 0 < i < len(my_grid) - 1 and i%2 != 0: continue # build the three vectors with which we later find he centre point my_vec1 = scale_vector(my_dir1, my_board.length / 2) my_vec2 = scale_vector(my_dir2, dist) my_vec3 = Vector(0, 0, my_board.z_drop - my_board.height / 2) my_centre = self.origin_pt + my_vec1 + my_vec2 + my_vec3 my_board.centre_point = my_centre my_board.drop_point = my_centre + Vector( 0, 0, my_board.height / 2) my_board.length_vector = normalize_vector(my_vec1) my_board.width_vector = normalize_vector(my_vec2) my_board.box_update() board_code += 1
def mesh_subdivide_tapered(mesh, k=1, height=1.0, ratio=0.5): """ TODO """ subd = mesh.copy() for fkey in mesh.faces(): centroid = mesh.face_centroid(fkey) centroid_vector = Vector(*centroid) normal = mesh.face_normal(fkey) normal_vector = Vector(*normal) normal_vector *= height face_verts = mesh.face_vertices(fkey) new_verts = [] for v in face_verts: v_coords = mesh.vertex_coordinates(v) v_vector = Vector(*v_coords) vert_to_cent = centroid_vector - v_vector vert_to_cent *= ratio new_vertex = v_vector + vert_to_cent + normal_vector x, y, z = new_vertex new_verts.append(subd.add_vertex(x=x, y=y, z=z)) for i,v in enumerate(face_verts): next_v = face_verts[(i+1) % len(face_verts)] new_v = new_verts[i] next_new_v = new_verts[(i+1) % len(face_verts)] new_face_key = subd.add_face([v, next_v, next_new_v, new_v]) top_face_key = subd.add_face(new_verts) del subd.face[fkey] return subd
def get_distance(self, point): """ single point distance function Parameters ---------- point: :class:`compas.geometry.Point` The point in R<sup>3</sup> space to query for it's distance. Returns ------- float The distance from the query point to the surface of the object. """ if not isinstance(point, Point): p = Point(*point) else: p = point p.transform(self.inversetransform) k0 = Vector(p.x / self.radiusX, p.y / self.radiusY, p.z / self.radiusZ).length k1 = Vector(p.x / self.radiusX**2, p.y / self.radiusY**2, p.z / self.radiusZ**2).length if k1 == 0: return -1 else: return k0 * (k0 - 1.0) / k1
def mesh_subdivide_pyramid(mesh, k=1, height=1.0): """ Subdivide a mesh using insertion of a vertex at centroid + height * face normal. Parameters ---------- mesh : Mesh The mesh object that will be subdivided. k : int Optional. The number of levels of subdivision. Default is ``1``. height : float The distance of the new vertex to the face. Returns ------- Mesh A new subdivided mesh. """ subd = mesh.copy() for fkey in mesh.faces(): centroid = mesh.face_centroid(fkey) centroid_vector = Vector(*centroid) normal = mesh.face_normal(fkey) normal_vector = Vector(*normal) new_vertex = centroid_vector + normal_vector * height subd.insert_vertex(fkey, xyz=new_vertex) return subd
def set_wait_time_on_sharp_corners(print_organizer, threshold=0.5 * math.pi, wait_time=0.3): """ Sets a wait time at the sharp corners of the path, based on the angle threshold. Parameters ---------- print_organizer: :class:`compas_slicer.print_organization.BasePrintOrganizer` threshold: float angle_threshold wait_time: float Time in seconds to introduce to add as a wait time """ number_of_wait_points = 0 for printpoint, i, j, k in print_organizer.printpoints_indices_iterator(): neighbors = print_organizer.get_printpoint_neighboring_items('layer_%d' % i, 'path_%d' % j, k) prev_ppt = neighbors[0] next_ppt = neighbors[1] if prev_ppt and next_ppt: v_to_prev = normalize_vector(Vector.from_start_end(printpoint.pt, prev_ppt.pt)) v_to_next = normalize_vector(Vector.from_start_end(printpoint.pt, next_ppt.pt)) a = abs(Vector(*v_to_prev).angle(v_to_next)) if a < threshold: printpoint.wait_time = wait_time printpoint.blend_radius = 0.0 # 0.0 blend radius for points where the robot will wait number_of_wait_points += 1 logger.info('Added wait times for %d points' % number_of_wait_points)
def taper_face(mesh, fkey, height=0.0, ratio=0.5, keep_original=False): centroid = mesh.face_centroid(fkey) centroid_vector = Vector(*centroid) normal = mesh.face_normal(fkey) normal_vector = Vector(*normal) normal_vector *= height face_verts = mesh.face_vertices(fkey) new_verts = [] for v in face_verts: v_coords = mesh.vertex_coordinates(v) v_vector = Vector(*v_coords) vert_to_cent = centroid_vector - v_vector vert_to_cent *= ratio new_vertex = v_vector + vert_to_cent + normal_vector x, y, z = new_vertex new_verts.append(mesh.add_vertex(x=x, y=y, z=z)) new_keys = [] for i,v in enumerate(face_verts): next_v = face_verts[(i+1) % len(face_verts)] new_v = new_verts[i] next_new_v = new_verts[(i+1) % len(face_verts)] new_face_key = mesh.add_face([v, next_v, next_new_v, new_v]) new_keys.append(new_face_key) top_face_key = mesh.add_face(new_verts) new_keys.append(top_face_key) if keep_original==False: del mesh.face[fkey] return new_keys
def board_geometry_setup(self): for my_layer in range(layer_no): if my_layer % 2 == 0: my_frame = self.origin_fr my_grid = self.ceiling_grids[0][0] else: my_frame = self.sec_fr my_grid = self.ceiling_grids[1][0] my_dir1 = normalize_vector(my_frame[1]) my_dir2 = normalize_vector(my_frame[2]) # we have to separate i/board_code because of possible exceptions in the centre board_code = 0 for my_board in self.timberboards[my_layer]: dist = my_board.grid_position # build the three vectors with which we later find he centre point # one advanced case if my_board.location == "high": my_vec1 = scale_vector(my_dir1, primary_length - my_board.length / 2) # all other cases else: my_vec1 = scale_vector(my_dir1, my_board.length / 2) my_vec2 = scale_vector(my_dir2, dist) my_vec3 = Vector(0, 0, my_board.z_drop - my_board.height / 2) my_centre = self.origin_pt + my_vec1 + my_vec2 + my_vec3 my_board.centre_point = my_centre my_board.drop_point = my_centre + Vector(0, 0, my_board.height / 2) my_board.length_vector = normalize_vector(my_vec1) my_board.width_vector = normalize_vector(my_vec2) my_board.box_update() board_code += 1
def to_local_geometry_xy(self): if self.Type == 'W': # wf == 'wireframe - center line of steel element, with origin at middle' wf_pt0 = Point(0, self.d * 0.5 - self.tw * 0.5, 0) wf_pt1 = Point(0, -(self.d * 0.5 - self.tw * 0.5), 0) wf_pt2 = translate_points([wf_pt0], Vector(-self.bf * 0.5, 0, 0))[0] wf_pt3 = translate_points([wf_pt0], Vector(self.bf * 0.5, 0, 0))[0] wf_pt4 = translate_points([wf_pt1], Vector(-self.bf * 0.5, 0, 0))[0] wf_pt5 = translate_points([wf_pt1], Vector(self.bf * 0.5, 0, 0))[0] wf_pt2 = Point(*wf_pt2) wf_pt3 = Point(*wf_pt3) wf_pt4 = Point(*wf_pt4) wf_pt5 = Point(*wf_pt5) web_surf = self.create_plate_xy(wf_pt0, wf_pt1, self.length) top_flange_srf = self.create_plate_xy(wf_pt2, wf_pt3, self.length) bottom_flange_srf = self.create_plate_xy(wf_pt4, wf_pt5, self.length) web_pl = self.extrude_plate(web_surf, self.tw, 'x') top_flange = self.extrude_plate(top_flange_srf, self.tf, 'y') bottom_flange = self.extrude_plate(bottom_flange_srf, self.tf, 'y') return [web_pl, top_flange, bottom_flange] if self.Type == 'HSS': raise NotImplementedError else: raise NotImplementedError
def pyramid_face(mesh, fkey, height=0.0): centroid = mesh.face_centroid(fkey) centroid_vector = Vector(*centroid) normal = mesh.face_normal(fkey) normal_vector = Vector(*normal) new_vertex = centroid_vector + normal_vector * height new_keys = mesh.insert_vertex(fkey, xyz=new_vertex, return_fkeys=True)[1] return new_keys
def basis_vectors(self): """Returns the basis vectors from the ``Rotation`` component of the ``Transformation``. """ from compas.geometry import Vector sc, sh, a, t, p = decompose_matrix(self.matrix) R = matrix_from_euler_angles(a, static=True, axes='xyz') xv, yv = basis_vectors_from_matrix(R) return Vector(*xv), Vector(*yv)
def cutting_plane(mesh, ry=10, rz=-50): plane = Plane(mesh.centroid(), Vector(1, 0, 0)) Ry = Rotation.from_axis_and_angle(Vector(0, 1, 0), radians(ry), plane.point) Rz = Rotation.from_axis_and_angle(Vector(0, 0, 1), radians(rz), plane.point) plane.transform(Rz * Ry) return plane
def get_frame(self): """ Returns a Frame with x-axis pointing up, y-axis pointing towards the mesh normal. """ if abs(dot_vectors(self.up_vector, self.mesh_normal) ) < 1.0: # if the normalized vectors are not co-linear c = cross_vectors(self.up_vector, self.mesh_normal) return Frame(self.pt, c, self.mesh_normal) else: # in horizontal surfaces the vectors happen to be co-linear return Frame(self.pt, Vector(1, 0, 0), Vector(0, 1, 0))
def board_geometry_setup(): for my_element in self.elements(): my_board = my_element[1] if my_board.layer % 2 == 0: my_frame = self.origin_fr layer_standard_length = self.primary_length else: my_frame = self.sec_fr layer_standard_length = self.secondary_length my_dir1 = normalize_vector(my_frame[1]) my_dir2 = normalize_vector(my_frame[2]) dist = my_board.grid_position if my_board.location == "high": if not my_board.supporter: length_attribute_1 = layer_standard_length - my_board.length / 2 else: length_attribute_1 = layer_standard_length - my_board.width / 2 elif my_board.location == "low": if not my_board.supporter: length_attribute_1 = my_board.length / 2 else: length_attribute_1 = my_board.width / 2 else: length_attribute_1 = layer_standard_length / 2 # position parallel to the boards (if not sup) my_vec1 = scale_vector(my_dir1, length_attribute_1) # position perpendicular to board direction (if not sup) my_vec2 = scale_vector(my_dir2, dist) # height vector my_vec3 = Vector(0, 0, my_board.z_drop - my_board.height / 2) my_centre = self.origin_pt + my_vec1 + my_vec2 + my_vec3 my_board.centre_point = my_centre my_board.drop_point = my_centre + Vector(0, 0, my_board.height / 2) if not my_board.perp: my_board.length_vector = normalize_vector(my_vec1) my_board.width_vector = normalize_vector(my_vec2) else: my_board.length_vector = normalize_vector(my_vec2) my_board.width_vector = normalize_vector(my_vec1) old_centre = my_board.center T = Translation(my_centre - old_centre) self.network.node[my_board.global_count]['x'] = my_centre[0] self.network.node[my_board.global_count]['y'] = my_centre[1] self.network.node[my_board.global_count]['z'] = my_centre[2] my_board.transform(T) my_board.board_frame = Frame(my_board.centre_point, my_board.length_vector, my_board.width_vector) my_board.tool_frame = Frame(my_board.drop_point, my_board.length_vector, my_board.width_vector) my_board.box = Box(my_board.board_frame, my_board.length, my_board.width, my_board.height)
def ray_mesh_intersect_reflect(ray, mesh, hit, facemap): base, vector = ray index = hit[0] distance = hit[3] face = facemap[index] p = base + vector * distance n = Vector(*mesh.face_normal(face)) n = n.cross(vector.cross(n)) r = base.transformed(Reflection.from_plane((p, n))) return p, r
def is_ccw(A, B, C): Vector_AB = Vector.from_start_end(A, B) Vector_AC = Vector.from_start_end(A, C) #Tests whether rotation from AB onto AC is ccw in the xy-plane angle = Vector.angle_signed(Vector_AB, Vector_AC, (0, 0, 1)) if angle < 0: return False else: return True
def __init__(self, o, e=0.01): self.o = o self.e = e self.ex = Point(e, 0, 0) self.ey = Point(0, e, 0) self.ez = Point(0, 0, e) self.k0 = Vector(1, -1, -1) self.k1 = Vector(-1, -1, 1) self.k2 = Vector(-1, 1, -1) self.k3 = Vector(1, 1, 1)
def basis_vectors(self): """Returns the basis vectors of the ``Rotation``. Returns ------- tuple: (:class:`Vector`, :class:`Vector`) """ from compas.geometry import Vector xaxis, yaxis = basis_vectors_from_matrix(self.matrix) return Vector(*xaxis), Vector(*yaxis)
def get_gradient(self, point): """ central differences, with tetrahedron technique. 30-40% faster regular `get_gradient_regular`. """ d0 = self.o.get_distance(Point(point.x + self.e, point.y - self.e, point.z - self.e)) d1 = self.o.get_distance(Point(point.x - self.e, point.y - self.e, point.z + self.e)) d2 = self.o.get_distance(Point(point.x - self.e, point.y + self.e, point.z - self.e)) d3 = self.o.get_distance(Point(point.x + self.e, point.y + self.e, point.z + self.e)) v = Vector(d0 - d1 - d2 + d3, -d0 - d1 + d2 + d3, -d0 + d1 - d2 + d3) v.unitize() return v
def mesh_subdivide_tapered(mesh, k=1, height=1.0, ratio=0.5, doCap=False ): """ Subdivide a mesh extruding its faces tapered like a window by creating an offset face and quads between every original edge and the corresponding new edge. Parameters ---------- mesh : Mesh The mesh object that will be subdivided. k : int Optional. The number of levels of subdivision. Default is ``1``. height : float The distance of the new vertex to the face. ratio : float The relative offset distance Returns ------- Mesh A new subdivided mesh. """ subd = mesh.copy() for fkey in mesh.faces(): centroid = mesh.face_centroid(fkey) centroid_vector = Vector(*centroid) normal = mesh.face_normal(fkey) normal_vector = Vector(*normal) normal_vector *= height face_verts = mesh.face_vertices(fkey) new_verts = [] for v in face_verts: v_coords = mesh.vertex_coordinates(v) v_vector = Vector(*v_coords) vert_to_cent = centroid_vector - v_vector vert_to_cent *= ratio new_vertex = v_vector + vert_to_cent + normal_vector x, y, z = new_vertex new_verts.append(subd.add_vertex(x=x, y=y, z=z)) for i, v in enumerate(face_verts): next_v = face_verts[(i+1) % len(face_verts)] new_v = new_verts[i] next_new_v = new_verts[(i+1) % len(face_verts)] new_face_key = subd.add_face([v, next_v, next_new_v, new_v]) if doCap: top_face_key = subd.add_face(new_verts) del subd.face[fkey] return subd
def test_basis_vectors(): trans1 = [1, 2, 3] angle1 = [-2.142, 1.141, -0.142] scale1 = [0.123, 2, 0.5] T1 = Translation(trans1) R1 = Rotation.from_euler_angles(angle1) S1 = Scale(scale1) M = (T1 * R1) * S1 x, y = M.basis_vectors assert np.allclose(x, Vector(0.41249169135312663, -0.05897071585157175, -0.9090506362335324)) assert np.allclose(y, Vector(-0.8335562904208867, -0.4269749553355485, -0.35053715668381935))
def basis_vectors(self): """The basis vectors from the rotation component of the transformation matrix. Returns ------- tuple of :class:`compas.geometry.Vector` The basis vectors of the rotation component of the tranformation. """ from compas.geometry import Vector x, y = basis_vectors_from_matrix(self.rotation.matrix) return Vector(*x), Vector(*y)
def test_from_tcf_to_t0cf(mesh, frame): tool = ToolModel(mesh, frame) frames_tcf = [ Frame((-0.309, -0.046, -0.266), (0.276, 0.926, -0.256), (0.879, -0.136, 0.456)) ] result = tool.from_tcf_to_t0cf(frames_tcf) expected = [ Frame(Point(-0.363, 0.003, -0.147), Vector(0.388, -0.351, -0.852), Vector(0.276, 0.926, -0.256)) ] assert allclose(result[0], expected[0], tol=1e-03)
def __init__(self, xyz='1 0 0', **kwargs): # We are not using Vector here because we # cannot attach _urdf_source to it due to __slots__ super(Axis, self).__init__() xyz = _parse_floats(xyz) xyz = Vector(*xyz) if xyz.length != 0: xyz.unitize() self.x = xyz[0] self.y = xyz[1] self.z = xyz[2] self.attr = kwargs
def get_orthonormal_frame(u,v): # Orthogonal frame a = Vector.cross(u,v) b = Vector.cross(a,u) c = Vector.cross(a,b) # Normalized frame a.unitize() b.unitize() c.unitize() return (a,b,c)