def concatenate(): trans1 = [1, 2, 3] angle1 = [-2.142, 1.141, -0.142] T1 = Translation(trans1) R1 = Rotation.from_euler_angles(angle1) M1 = T1.concatenate(R1) assert np.allclose(M1, T1 * R1)
def node_xyz(self): """dict : The view coordinates of the mesh object.""" origin = Point(0, 0, 0) stack = [] if self.scale != 1.0: S = Scale.from_factors([self.scale] * 3) stack.append(S) if self.rotation != [0, 0, 0]: R = Rotation.from_euler_angles(self.rotation) stack.append(R) if self.location != origin: if self.anchor is not None: xyz = self.network.vertex_attributes(self.anchor, 'xyz') point = Point(*xyz) T1 = Translation.from_vector(origin - point) stack.insert(0, T1) T2 = Translation.from_vector(self.location) stack.append(T2) if stack: X = reduce(mul, stack[::-1]) network = self.network.transformed(X) else: network = self.network node_xyz = { network: network.node_attributes(node, 'xyz') for node in network.nodes() } return node_xyz
def vertex_xyz(self): """dict : The view coordinates of the volmesh object.""" origin = Point(0, 0, 0) stack = [] if self.scale != 1.0: S = Scale.from_factors([self.scale] * 3) stack.append(S) if self.rotation != [0, 0, 0]: R = Rotation.from_euler_angles(self.rotation) stack.append(R) if self.location != origin: if self.anchor is not None: xyz = self.volmesh.vertex_attributes(self.anchor, 'xyz') point = Point(*xyz) T1 = Translation.from_vector(origin - point) stack.insert(0, T1) T2 = Translation.from_vector(self.location) stack.append(T2) if stack: X = reduce(mul, stack[::-1]) volmesh = self.volmesh.transformed(X) else: volmesh = self.volmesh vertex_xyz = { vertex: volmesh.vertex_attributes(vertex, 'xyz') for vertex in volmesh.vertices() } return vertex_xyz
def create_group_action(self, objs, transformations, times): # TODO: how to start multiple animations at once if len(transformations) != len(times): raise ValueError("Pass equal amount of transformations and times") x, y, z, w = objs[0].quaternion Tinit = Rotation.from_quaternion([w, x, y, z]) * Translation( objs[0].position) positions = [] quaternions = [] for M in transformations: Sc, Sh, R, T, P = (M * Tinit).decomposed() positions.append(list(T.translation)) quaternions.append(R.quaternion.xyzw) position_track = p3js.VectorKeyframeTrack(name='.position', times=times, values=list( flatten(positions))) rotation_track = p3js.QuaternionKeyframeTrack( name='.quaternion', times=times, values=list(flatten(quaternions))) animation_group = p3js.AnimationObjectGroup() animation_group.exec_three_obj_method('add', objs[0]) # this is not working obj_clip = p3js.AnimationClip(tracks=[position_track, rotation_track]) mixer = p3js.AnimationMixer(animation_group) obj_action = p3js.AnimationAction(mixer, obj_clip, animation_group) return obj_action
def test_remeshing(): FILE = os.path.join(HERE, '../..', 'data', 'Bunny.ply') # ============================================================================== # Get the bunny and construct a mesh # ============================================================================== bunny = TriMesh.from_ply(FILE) bunny.cull_vertices() # ============================================================================== # Move the bunny to the origin and rotate it upright. # ============================================================================== vector = scale_vector(bunny.centroid, -1) T = Translation.from_vector(vector) S = Scale.from_factors([100, 100, 100]) R = Rotation.from_axis_and_angle(Vector(1, 0, 0), math.radians(90)) bunny.transform(R * S * T) # ============================================================================== # Remesh # ============================================================================== length = bunny.average_edge_length bunny.remesh(4 * length) bunny.to_mesh()
def test_concatenated(): trans1 = [1, 2, 3] angle1 = [-2.142, 1.141, -0.142] T1 = Translation.from_vector(trans1) R1 = Rotation.from_euler_angles(angle1) M1 = T1.concatenated(R1) assert allclose(M1, T1 * R1)
def correct_location_in_z(self, cylinder, dist_diff): corr_vector = cylinder.get_normal() * dist_diff T = Translation(corr_vector) cylinder.location.transform(T) cylinder.attrs["location_correction"] = corr_vector
def test___eq__(): i1 = Transformation() i2 = Transformation() t = Translation.from_vector([1, 0, 0]) assert i1 == i2 assert not (i1 != i2) assert i1 != t assert not (i1 == t)
def _to_occ(self) -> BRepPrimAPI_MakeBox: xaxis = self.frame.xaxis.scaled(-0.5 * self.xsize) yaxis = self.frame.yaxis.scaled(-0.5 * self.ysize) zaxis = self.frame.zaxis.scaled(-0.5 * self.zsize) frame = self.frame.transformed( Translation.from_vector(xaxis + yaxis + zaxis)) ax2 = gp_Ax2(gp_Pnt(*frame.point), gp_Dir(*frame.zaxis), gp_Dir(*frame.xaxis)) return BRepPrimAPI_MakeBox(ax2, self.xsize, self.ysize, self.zsize)
def vertex_xyz(self): bbox = self.mesh.bounding_box() xmin, ymin, zmin = bbox[0] if not self.binary and (xmin < 0 or ymin < 0 or zmin < 0): T = Translation.from_vector([-xmin, -ymin, -zmin]) mesh = self.mesh.transformed(T) else: mesh = self.mesh return {vertex: mesh.vertex_attributes(vertex, 'xyz') for vertex in mesh.vertices()}
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 vertex_xyz(self): """dict : The view coordinates of the mesh object.""" origin = Point(0, 0, 0) if self.anchor is not None: xyz = self.mesh.vertex_attributes(self.anchor, 'xyz') point = Point(* xyz) T1 = Translation.from_vector(origin - point) S = Scale.from_factors([self.scale] * 3) R = Rotation.from_euler_angles(self.rotation) T2 = Translation.from_vector(self.location) X = T2 * R * S * T1 else: S = Scale.from_factors([self.scale] * 3) R = Rotation.from_euler_angles(self.rotation) T = Translation.from_vector(self.location) X = T * R * S mesh = self.mesh.transformed(X) vertex_xyz = {vertex: mesh.vertex_attributes(vertex, 'xy') + [0.0] for vertex in mesh.vertices()} return vertex_xyz
def draw_blocks(self, keys=None, show_faces=False, show_vertices=False, show_edges=True): """Draw the blocks of the assembly. Parameters ---------- show_faces : bool, optional Draw the faces of the blocks. Default is ``False``. show_vertices : bool, optional Draw the vertices of the blocks. Default is ``False``. show_edges : bool, optional Draw the edges of the blocks. Default is ``True``. Notes ----- * By default, blocks are drawn as wireframes. * By default, blocks are drawn on a sublayer of the base layer, if a base layer was specified. * Block names have the following pattern: ``"{assembly_name}.block.{block_id}"`` * Faces and vertices can be drawn using the corresponding flags. * Block components have the following pattern: * face: ``"{assembly_name}.block.{block_id}.face.{face_id}"`` * edge: ``"{assembly_name}.block.{block_id}.edge.{edge_id}"`` * vertex: ``"{assembly_name}.block.{block_id}.vertex.{vertex_id}"`` Examples -------- >>> """ blocks = [] # compas_blender.clear_collection() for key in self.assembly.nodes(): block = self.assembly.blocks[key] centroid = block.centroid() T = Translation(subtract_vectors([0, 0, 0], centroid)) name = "Assembly.block.{}".format(key) vertices = transform_points(block.vertices_attributes('xyz'), T) edges = list(block.edges()) faces = [block.face_vertices(fkey) for fkey in block.faces()] mesh = compas_blender.bpy.data.meshes.new(name) mesh.from_pydata(vertices, edges, faces) obj = compas_blender.bpy.data.objects.new(name, mesh) obj.show_wire = True obj.location = centroid if self.assembly.node_attribute(key, 'is_support'): compas_blender.drawing.set_object_color(obj, [1.0, 0.0, 0.0]) else: compas_blender.drawing.set_object_color(obj, [1.0, 1.0, 1.0]) blocks.append(obj) for obj in blocks: for col in obj.users_collection: col.objects.unlink(obj) self.block_collection.objects.link(obj)
def get_compressed_centroid_frame(self): """Get frame at middle of compressed bullet. Returns ------- :class:`compas.geometry.Frame` """ vector = self.get_normal() * self.get_compressed_height() / 2 T = Translation(vector) return self.location.transformed(T)
def get_compressed_top_frame(self): """Top of compressed cylinder. Returns ------- :class:`compas.geometry.Frame` """ vector = self.get_normal() * self.get_compressed_height() T = Translation(vector) return self.location.transformed(T)
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 get_egress_frame(self): """Get Frame at end and start of trajectory to and from. Returns ------- :class:`compas.geometry.Frame` """ vector = self.get_normal() * self.egress_frame_distance T = Translation(vector) return self.get_uncompressed_top_frame().transformed(T)
def pick_element(self, element): """Send movement and IO instructions to pick up fabrication element. Parameter ---------- element : :class:`~rapid_clay_formations_fab.fab_data.FabricationElement` Representation of fabrication element to pick up. """ self.send(compas_rrc.SetTool(self.pick_place_tool.name)) log.debug("Tool {self.pick_place_tool.name} set.") self.send(compas_rrc.SetWorkObject(self.wobjs.pick)) log.debug(f"Work object {self.wobjs.pick} set.") # start watch self.send(compas_rrc.StartWatch()) # TODO: Use separate obj for pick elems? vector = element.get_normal( ) * self.pick_place_tool.elem_pick_egress_dist T = Translation(vector) egress_frame = element.get_uncompressed_top_frame().transformed(T) self.send( MoveToRobtarget( egress_frame, self.EXTERNAL_AXES_DUMMY, self.speed.travel, self.zone.travel, )) self.send( MoveToRobtarget( element.get_uncompressed_top_frame(), self.EXTERNAL_AXES_DUMMY, self.speed.pick_place, self.zone.pick, )) self.send(compas_rrc.WaitTime(self.pick_place_tool.needles_pause)) self.extend_needles() self.send(compas_rrc.WaitTime(self.pick_place_tool.needles_pause)) self.send( MoveToRobtarget( egress_frame, self.EXTERNAL_AXES_DUMMY, self.speed.pick_place, self.zone.pick, motion_type=Motion.LINEAR, )) self.send(compas_rrc.StopWatch()) return self.send(compas_rrc.ReadWatch())
def hexagongrid(): polygon = Polygon.from_sides_and_radius_xy(6, 1) vertices = polygon.points vertices.append(polygon.centroid) x, y, z = zip(*vertices) xmin = min(x) xmax = max(x) ymin = min(y) ymax = max(y) faces = [[0, 1, 6], [1, 2, 6], [2, 3, 6], [3, 4, 6], [4, 5, 6], [5, 0, 6]] mesh = Mesh.from_vertices_and_faces(vertices, faces) meshes = [] for i in range(2): T = Translation.from_vector([i * (xmax - xmin), 0, 0]) meshes.append(mesh.transformed(T)) for i in range(2): T = Translation.from_vector([i * (xmax - xmin), ymax - ymin, 0]) meshes.append(mesh.transformed(T)) mesh = meshes_join_and_weld(meshes) return mesh
def calculate_prismatic_transformation(self, position): """Returns a transformation of a prismatic joint. A prismatic joint slides along the axis and has a limited range specified by the upper and lower limits. Args: position (float): the movement in meters. """ position = max(min(position, self.limit.upper), self.limit.lower) return Translation(self.axis.vector * position)
def shift(block): """Shift a block along the X or Y axis by a randomly chosen amount. Parameters ---------- block : compas_assembly.datastructures.Block """ scale = choice([+0.01, -0.01, +0.05, -0.05, +0.1, -0.1]) axis = choice([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]) vector = scale_vector(axis, scale) T = Translation.from_vector(vector) block.transform(T)
def move_diagram(diagram, distance=1.5): bbox = diagram.bounding_box() a = bbox[0] b = bbox[1] ab = subtract_vectors(b, a) ab = scale_vector(ab, distance) T = Translation(ab) diagram.transform(T) return diagram
def test_decomposed(): 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 Sc, Sh, R, T, P = M.decomposed() assert S1 == Sc assert R1 == R assert T1 == T
def node_xyz(self): """dict : The view coordinates of the mesh object.""" origin = Point(0, 0, 0) if self.anchor is not None: xyz = self.network.node_attributes(self.anchor, 'xyz') point = Point(*xyz) T1 = Translation.from_vector(origin - point) S = Scale.from_factors([self.scale] * 3) R = Rotation.from_euler_angles(self.rotation) T2 = Translation.from_vector(self.location) X = T2 * R * S * T1 else: S = Scale.from_factors([self.scale] * 3) R = Rotation.from_euler_angles(self.rotation) T = Translation.from_vector(self.location) X = T * R * S network = self.network.transformed(X) node_xyz = { network: network.node_attributes(node, 'xyz') for node in network.nodes() } return node_xyz
def placement_frame(self): """Last frame in placement procedure. Derived from location, height and compression ratio. Returns ------- :class:`compas.geometry.Frame` """ vector = self.location.zaxis * self.compressed_height * -1 T = Translation(vector) return self._location.transformed(T)
def draw_box(self, box, color=None): geo = p3js.BoxBufferGeometry(width=box.xsize, height=box.zsize, depth=box.ysize, widthSegments=box.xsize, heightSegments=box.zsize, depthSegments=box.ysize) mat = material_from_color(color) mesh = p3js.Mesh(geometry=geo, material=mat) Tinit = Translation([box.xsize/2, box.ysize/2, box.zsize/2]) Sc, Sh, R, T, P = (Transformation.from_frame(box.frame) * Tinit).decompose() mesh.quaternion = R.quaternion.xyzw mesh.position = list(T.translation) self.geometry.append(mesh) return mesh
def create_action(self, obj, transformations, times): if len(transformations) != len(times): raise ValueError("Pass equal amount of transformations and times") x, y, z, w = obj.quaternion Tinit = Rotation.from_quaternion([w, x, y, z]) * Translation(obj.position) positions = [] quaternions = [] for M in transformations: Sc, Sh, R, T, P = (M * Tinit).decompose() positions.append(list(T.translation)) quaternions.append(R.quaternion.xyzw) position_track = p3js.VectorKeyframeTrack(name='.position', times=times, values=list(flatten(positions))) rotation_track = p3js.QuaternionKeyframeTrack(name='.quaternion', times=times, values=list(flatten(quaternions))) obj_clip = p3js.AnimationClip(tracks=[position_track, rotation_track]) obj_action = p3js.AnimationAction(p3js.AnimationMixer(obj), obj_clip, obj) return obj_action
def pick_bullet(self, pick_elem): """Send movement and IO instructions to pick up a clay cylinder. Parameter ---------- pick_elem : :class:`~rapid_clay_formations_fab.fab_data.ClayBullet` Representation of fabrication element to pick up. """ self.send(compas_rrc.SetTool(self.pick_place_tool.name)) log.debug("Tool {} set.".format(self.pick_place_tool.name)) self.send(compas_rrc.SetWorkObject(self.wobjs.pick)) log.debug("Work object {} set.".format(self.wobjs.pick)) # start watch self.send(compas_rrc.StartWatch()) # TODO: Use separate obj for pick elems? vector = pick_elem.get_normal( ) * self.pick_place_tool.elem_pick_egress_dist T = Translation(vector) egress_frame = pick_elem.get_uncompressed_top_frame().transformed(T) self.send( MoveToFrame(egress_frame, self.speed.travel, self.zone.travel)) self.send( MoveToFrame( pick_elem.get_uncompressed_top_frame(), self.speed.precise, self.zone.pick, )) self.send(compas_rrc.WaitTime(self.pick_place_tool.needles_pause)) self.extend_needles() self.send(compas_rrc.WaitTime(self.pick_place_tool.needles_pause)) self.send( MoveToFrame( egress_frame, self.speed.precise, self.zone.pick, motion_type=Motion.LINEAR, )) self.send(compas_rrc.StopWatch()) return self.send(compas_rrc.ReadWatch())
def decomposed(self): """Decompose the `Transformation` into its components. Returns ------- :class:`compas.geometry.Scale` The scale component of the current transformation. :class:`compas.geometry.Shear` The shear component of the current transformation. :class:`compas.geometry.Rotation` The rotation component of the current transformation. :class:`compas.geometry.Translation` The translation component of the current transformation. :class:`compas.geometry.Projection` The projection component of the current transformation. Examples -------- >>> from compas.geometry import Scale, Translation, Rotation >>> trans1 = [1, 2, 3] >>> angle1 = [-2.142, 1.141, -0.142] >>> scale1 = [0.123, 2, 0.5] >>> T1 = Translation.from_vector(trans1) >>> R1 = Rotation.from_euler_angles(angle1) >>> S1 = Scale.from_factors(scale1) >>> M = T1 * R1 * S1 >>> S, H, R, T, P = M.decomposed() >>> S1 == S True >>> R1 == R True >>> T1 == T True """ from compas.geometry import Scale # noqa: F811 from compas.geometry import Shear from compas.geometry import Rotation # noqa: F811 from compas.geometry import Translation # noqa: F811 from compas.geometry import Projection s, h, a, t, p = decompose_matrix(self.matrix) S = Scale.from_factors(s) H = Shear.from_entries(h) R = Rotation.from_euler_angles(a, static=True, axes='xyz') T = Translation.from_vector(t) P = Projection.from_entries(p) return S, H, R, T, P
def draw_interfaces(self, keys=None, color=None): """Draw the interfaces between the blocks. Parameters ---------- keys : list A list of interface identifiers (i.e. assembly edge (u, v) tuples). Default is ``None``, in which case all interfaces are drawn. color : str, tuple, dict The color specififcation for the interfaces. Colors should be specified in the form of a string (hex colors) or as a tuple of RGB components. To apply the same color to all interfaces, provide a single color specification. Individual colors can be assigned using a dictionary of key-color pairs. Missing keys will be assigned the default interface color (``self.settings['color.interface']``). The default is ``None``, in which case all interfaces are assigned the default interface color. Notes ----- * Interfaces are drawn as mesh faces. * Interfaces are drawn on a sub-layer *Interfaces* of the base layer, if a base layer was provided. * Interface names have the following pattern: ``"{assembly_name}.interface.{from block_id}-{to block_id}"`` * Interfaces have a direction, as suggested by the naming convention. """ interfaces = [] for key, attr in self.assembly.edges(True): u, v = key name = "Assembly.interface.{}-{}".format(u, v) points = attr['interface_points'] base = attr['interface_origin'] T = Translation(subtract_vectors([0, 0, 0], base)) vertices = transform_points(points, T) faces = [list(range(len(vertices)))] edges = list(pairwise(faces[0] + [0])) mesh = compas_blender.bpy.data.meshes.new(name) mesh.from_pydata(vertices, edges, faces) obj = compas_blender.bpy.data.objects.new(name, mesh) obj.location = base compas_blender.drawing.set_object_color(obj, [0, 0, 1.0]) interfaces.append(obj) for obj in interfaces: for col in obj.users_collection: col.objects.unlink(obj) self.interface_collection.objects.link(obj)