def update_scene_transforms_and_positions(self, scene): """Walks through the scene tree and updates transforms and positions. To be used when nodes have been added or the nodes' matrices or TRS attributes have been set or updated. Parameters ---------- scene : :class:`compas.files.GLTFScene` Returns ------- """ origin = [0, 0, 0] for node_key in scene.children: node = self.nodes[node_key] node.transform = node.matrix or node.get_matrix_from_trs() node.position = transform_points([origin], node.transform)[0] queue = [node_key] while queue: cur_key = queue.pop(0) cur = self.nodes[cur_key] for child_key in cur.children: child = self.nodes[child_key] child.transform = multiply_matrices( cur.transform, child.matrix or child.get_matrix_from_trs() ) child.position = transform_points([origin], child.transform)[0] queue.append(child_key)
def get_forward_transformations(self, configuration): """Calculate the transformations according to the configuration. Args: configuration (:class:`Configuration`): The robot's configuration. Returns: transformations (:obj:`list` of :class:`Transformation`): The transformations for each link. """ q0, q1, q2, q3, q4, q5 = configuration.joint_values j0, j1, j2, j3, j4, j5 = self.j0, self.j1, self.j2, self.j3, self.j4, self.j5 T0 = Rotation.from_axis_and_angle(subtract_vectors(j0[1], j0[0]), q0, j0[1]) j1 = transform_points(j1, T0) T1 = Rotation.from_axis_and_angle(subtract_vectors(j1[1], j1[0]), q1, j1[1]) * T0 j2 = transform_points(j2, T1) T2 = Rotation.from_axis_and_angle(subtract_vectors(j2[1], j2[0]), q2, j2[1]) * T1 j3 = transform_points(j3, T2) T3 = Rotation.from_axis_and_angle(subtract_vectors(j3[1], j3[0]), q3, j3[1]) * T2 j4 = transform_points(j4, T3) T4 = Rotation.from_axis_and_angle(subtract_vectors(j4[1], j4[0]), q4, j4[1]) * T3 j5 = transform_points(j5, T4) T5 = Rotation.from_axis_and_angle(subtract_vectors(j5[1], j5[0]), q5, j5[1]) * T4 # now apply the transformation to the base T0 = self.transformation_RCS_WCS * T0 T1 = self.transformation_RCS_WCS * T1 T2 = self.transformation_RCS_WCS * T2 T3 = self.transformation_RCS_WCS * T3 T4 = self.transformation_RCS_WCS * T4 T5 = self.transformation_RCS_WCS * T5 return T0, T1, T2, T3, T4, T5
def offset_polygon_xy(points, dist, planarize=False): if len(points) < 3: return None frame = Frame.from_plane( Plane.from_three_points(points[0], points[1], points[2])) xform = Transformation.from_frame_to_frame(frame, Frame.worldXY()) if planarize: points = [point_on_plane(point, frame) for point in points] points = transform_points(points, xform) points = offset_polygon(points, dist) points = transform_points(points, xform.inverse()) return points
def mesh_transform(mesh, transformation): """Transform a mesh. Parameters ---------- mesh : Mesh The mesh. transformation : Transformation The transformation. Notes ----- The mesh is modified in-place. Examples -------- >>> mesh = Mesh.from_obj(compas.get('cube.obj')) >>> T = matrix_from_axis_and_angle([0, 0, 1], pi / 4) >>> tmesh = mesh.copy() >>> mesh_transform(tmesh, T) >>> viewer.mesh = tmesh # this should be a list of meshes >>> viewer.show() """ vertices = [mesh.vertex_coordinates(key) for key in mesh.vertices()] xyz = transform_points(vertices, transformation) for index, (key, attr) in enumerate(mesh.vertices(True)): attr['x'] = xyz[index][0] attr['y'] = xyz[index][1] attr['z'] = xyz[index][2]
def transform(self, T): """Transform this polygon. Parameters ---------- T : :class:`compas.geometry.Transformation` | list[list[float]] The transformation. Returns ------- None Examples -------- >>> from math import radians >>> from compas.geometry import Rotation >>> polygon = Polygon.from_sides_and_radius_xy(4, 1.0) >>> R = Rotation.from_axis_and_angle([0.0, 0.0, 1.0], radians(45)) >>> polygon.transform(R) >>> polygon.points[0] Point(-0.707, 0.707, 0.000) """ for index, point in enumerate(transform_points(self.points, T)): self.points[index].x = point[0] self.points[index].y = point[1] self.points[index].z = point[2]
def transform_collection(collection, X): """Transform a collection of points. Parameters ---------- collection : list of :class:`compas.geometry.Point` The collection of points. Returns ------- None The points are modified in-place. Examples -------- >>> T = Translation([1.0, 2.0, 3.0]) >>> a = Point(0.0, 0.0, 0.0) >>> points = [a] >>> Point.transform_collection(points, T) >>> b = points[0] >>> b Point(1.000, 2.000, 3.000) >>> a is b True """ data = transform_points(collection, X) for point, xyz in zip(collection, data): point.x = xyz[0] point.y = xyz[1] point.z = xyz[2]
def volmesh_transform(volmesh, transformation): """Transform a mesh. Parameters ---------- volmesh : :class:`compas.datastructures.VolMesh` The volmesh. transformation : :class:`compas.geometry.Transformation` The transformation. Returns ------- None Notes ----- The volmesh is modified in-place. Examples -------- >>> from compas.datastructures import VolMesh >>> from compas.geometry import Rotation >>> volmesh = VolMesh.from_obj(compas.get('boxes.obj')) >>> T = Rotation.from_axis_and_angle([0, 0, 1], math.pi / 4) >>> volmesh_transform(volmesh, T) """ vertices = list(volmesh.vertices()) xyz = [volmesh.vertex_coordinates(vertex) for vertex in vertices] xyz[:] = transform_points(xyz, transformation) for index, vertex in enumerate(vertices): volmesh.vertex_attributes(vertex, 'xyz', xyz[index])
def to_vertices_and_faces(self, **kwargs): """Returns a list of vertices and faces""" u = kwargs.get('u') or 10 if u < 3: raise ValueError('The value for u should be u > 3.') vertices = [] a = 2 * pi / u for i in range(u): x = self.circle.radius * cos(i * a) y = self.circle.radius * sin(i * a) z = self.height / 2 vertices.append([x, y, z]) vertices.append([x, y, -z]) # transform vertices to cylinder's plane frame = Frame.from_plane(self.circle.plane) M = matrix_from_frame(frame) vertices = transform_points(vertices, M) faces = [] for i in range(0, u * 2, 2): faces.append([i, i + 1, (i + 3) % (u * 2), (i + 2) % (u * 2)]) faces.append([i for i in range(0, u * 2, 2)]) faces.append([i for i in range(1, u * 2, 2)]) faces[-1].reverse() return vertices, faces
def mesh_transform(mesh, transformation): """Transform a mesh. Parameters ---------- mesh : :class:`compas.datastructures.Mesh` The mesh. transformation : :class:`compas.geometry.Transformation` The transformation. Returns ------- None The mesh is modified in-place. Examples -------- >>> from compas.datastructures import Mesh >>> from compas.geometry import matrix_from_axis_and_angle >>> mesh = Mesh.from_polyhedron(6) >>> T = matrix_from_axis_and_angle([0, 0, 1], math.pi / 4) >>> tmesh = mesh.copy() >>> mesh_transform(tmesh, T) """ vertices = list(mesh.vertices()) xyz = [mesh.vertex_coordinates(vertex) for vertex in vertices] xyz[:] = transform_points(xyz, transformation) for index, vertex in enumerate(vertices): mesh.vertex_attributes(vertex, 'xyz', xyz[index])
def network_transform(network, transformation): """Transform a network. Parameters ---------- network : :class:`compas.datastructures.Network` The network. transformation : :class:`compas.geometry.Transformation` The transformation. Returns ------- None Notes ----- The network is modified in-place. """ vertices = [network.node_coordinates(key) for key in network.nodes()] xyz = transform_points(vertices, transformation) for index, (key, attr) in enumerate(network.nodes(True)): attr['x'] = xyz[index][0] attr['y'] = xyz[index][1] attr['z'] = xyz[index][2]
def network_transform(network, transformation): """Transform a network. Parameters ---------- network : network The network. transformation : Transformation The transformation. Notes ----- The network is modified in-place. Examples -------- >>> network = network.from_obj(compas.get('cube.obj')) >>> T = matrix_from_axis_and_angle([0, 0, 1], pi / 4) >>> tnetwork = network.copy() >>> network_transform(tnetwork, T) >>> viewer.network = tnetwork # this should be a list of networkes >>> viewer.show() """ vertices = [network.vertex_coordinates(key) for key in network.vertices()] xyz = transform_points(vertices, transformation) for index, (key, attr) in enumerate(network.vertices(True)): attr['x'] = xyz[index][0] attr['y'] = xyz[index][1] attr['z'] = xyz[index][2]
def to_vertices_and_faces(self, **kwargs): """Returns a list of vertices and faces, called by `Mesh.from_shape()`.""" if 'u' in kwargs: u = kwargs['u'] else: u = 10 vertices = [] a = 2 * pi / u for i in range(u): x = self.circle.radius * cos(i * a) y = self.circle.radius * sin(i * a) vertices.append([x, y, 0]) vertices.append([0, 0, self.height]) # transform vertices to cylinder's plane frame = Frame.from_plane(self.circle.plane) M = matrix_from_frame(frame) vertices = transform_points(vertices, M) faces = [] last = len(vertices) - 1 for i in range(u): faces.append([i, (i + 1) % u, last]) faces.append([i for i in range(u)]) faces[-1].reverse() return vertices, faces
def mesh_transform(mesh, transformation): """Transform a mesh. Parameters ---------- mesh : compas.datastructures.Mesh The mesh. transformation : compas.geometry.Transformation The transformation. Notes ----- The mesh is modified in-place. Examples -------- >>> mesh = Mesh.from_obj(compas.get('cube.obj')) >>> T = matrix_from_axis_and_angle([0, 0, 1], pi / 4) >>> tmesh = mesh.copy() >>> mesh_transform(tmesh, T) """ vertices = [mesh.vertex_coordinates(key) for key in mesh.vertices()] xyz = transform_points(vertices, transformation) for index, (_, attr) in enumerate(mesh.vertices(True)): attr['x'] = xyz[index][0] attr['y'] = xyz[index][1] attr['z'] = xyz[index][2]
def get_transformed_model(self, transformation, xtransform_function=None): """Get the transformed meshes of the tool model. Args: transformation (:class:`Transformation`): The transformation to reach tool0_frame. xform_function (function name, optional): the name of the function used to transform the model. Defaults to None. Returns: model (:obj:`list` of :class:`Mesh`): The list of meshes in the respective class of the CAD environment """ tmodel = [] if xtransform_function: for m in self.model: tmodel.append(xtransform_function(m, transformation, copy=True)) else: for m in self.model: xyz = [(a['x'], a['y'], a['z']) for k, a in m.vertices(True)] mtxyz = transform_points(xyz, transformation) #mtxyz = transform_points(m.xyz, transformation) faces = [m.face_vertices(fkey) for fkey in m.faces()] tmodel.append(Mesh.from_vertices_and_faces(mtxyz, faces)) return tmodel
def volmesh_transform(volmesh, transformation): """Transform a mesh. Parameters ---------- volmesh : compas.datastructures.VolMesh The volmesh. transformation : compas.geometry.Transformation The transformation. Notes ----- The volmesh is modified in-place. Examples -------- >>> volmesh = VolMesh.from_obj(compas.get('cube.obj')) >>> T = matrix_from_axis_and_angle([0, 0, 1], pi / 4) >>> volmesh_transform(volmesh, T) """ vertices = list(volmesh.vertices()) xyz = [volmesh.vertex_coordinates(vertex) for vertex in vertices] xyz[:] = transform_points(xyz, transformation) for index, vertex in enumerate(vertices): volmesh.vertex_attributes(vertex, 'xyz', xyz[index])
def to_vertices_and_faces(self, **kwargs): if 'u' in kwargs: u = kwargs['u'] else: u = 10 vertices = [] a = 2 * pi / u for i in range(u): x = self.circle.radius * cos(i * a) y = self.circle.radius * sin(i * a) z = self.height / 2 vertices.append([x, y, z]) vertices.append([x, y, -z]) # transform vertices to cylinder's plane frame = Frame.from_plane(self.circle.plane) M = matrix_from_frame(frame) vertices = transform_points(vertices, M) faces = [] for i in range(0, u*2, 2): faces.append([i, i+1, (i+3)%(u*2), (i+2)%(u*2)]) faces.append([i for i in range(0, u*2, 2)]) faces.append([i for i in range(1, u*2, 2)]) faces[-1].reverse() return vertices, faces
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 offset_bbox_xy(pts, dist): bbox = pca_numpy(pts) frame1 = Frame(bbox[0], bbox[1][0], bbox[1][1]) xform = Transformation.from_frame_to_frame(frame1, Frame.worldXY()) pts = transform_points(pts, xform) bbox = bounding_box_xy(pts) bbox = offset_polygon(bbox, dist) return bbox, xform
def to_vertices_and_faces(self, u=16, triangulated=False): """Returns a list of vertices and faces. Parameters ---------- u : int, optional Number of faces in the "u" direction. triangulated: bool, optional Flag indicating that the faces have to be triangulated. Returns ------- (vertices, faces) A list of vertex locations and a list of faces, with each face defined as a list of indices into the list of vertices. """ if u < 3: raise ValueError('The value for u should be u > 3.') vertices = [] a = 2 * pi / u z = self.height / 2 for i in range(u): x = self.circle.radius * cos(i * a) y = self.circle.radius * sin(i * a) vertices.append([x, y, z]) vertices.append([x, y, -z]) # add v in bottom and top's circle center vertices.append([0, 0, z]) vertices.append([0, 0, -z]) # transform vertices to cylinder's plane frame = Frame.from_plane(self.circle.plane) M = matrix_from_frame(frame) vertices = transform_points(vertices, M) faces = [] # side faces for i in range(0, u * 2, 2): faces.append([i, i + 1, (i + 3) % (u * 2), (i + 2) % (u * 2)]) # top and bottom circle faces for i in range(0, u * 2, 2): top = [i, (i + 2) % (u * 2), len(vertices) - 2] bottom = [i + 1, (i + 3) % (u * 2), len(vertices) - 1] faces.append(top) faces.append(bottom[::-1]) if triangulated: triangles = [] for face in faces: if len(face) == 4: triangles.append(face[0:3]) triangles.append([face[0], face[2], face[3]]) else: triangles.append(face) faces = triangles return vertices, faces
def transform_network(self, transformation): nodes = [self.node_coordinates(key) for key in self.nodes()] xyz = transform_points(nodes, transformation) for index, (key, attr) in enumerate(self.nodes(True)): attr["x"] = xyz[index][0] attr["y"] = xyz[index][1] attr["z"] = xyz[index][2] self.build_dist_dict()
def transform(self, transformation): """Transform the polyhedron. Parameters ---------- transformation : :class:`Transformation` """ self.vertices = transform_points(self.vertices, transformation)
def apply_xform(self, M): key_index = self.key_index() points = self.get_vertices_attributes('xyz') points = transform_points(points, M) for key, attr in self.vertices(True): index = key_index[key] x, y, z = points[index] attr['x'] = x attr['y'] = y attr['z'] = z
def to_vertices_and_faces(self, u=16, triangulated=False): """Returns a list of vertices and faces. Parameters ---------- u : int, optional Number of faces in the "u" direction. triangulated: bool, optional If True, triangulate the faces. Returns ------- list[list[float]] A list of vertex locations. list[list[int]] And a list of faces, with each face defined as a list of indices into the list of vertices. """ if u < 3: raise ValueError('The value for u should be u > 3.') vertices = [[0, 0, 0]] a = 2 * pi / u radius = self.circle.radius for i in range(u): x = radius * cos(i * a) y = radius * sin(i * a) vertices.append([x, y, 0]) vertices.append([0, 0, self.height]) frame = Frame.from_plane(self.circle.plane) M = matrix_from_frame(frame) vertices = transform_points(vertices, M) faces = [] first = 0 last = len(vertices) - 1 for i, j in pairwise(range(1, last)): faces.append([i, j, last]) faces.append([j, i, first]) faces.append([last - 1, 1, last]) faces.append([1, last - 1, first]) if triangulated: triangles = [] for face in faces: if len(face) == 4: triangles.append(face[0:3]) triangles.append([face[0], face[2], face[3]]) else: triangles.append(face) faces = triangles return vertices, faces
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 transform(self, transformation): """Transform the polyhedron. Parameters ---------- transformation : :class:`compas.geometry.Transformation` Returns ------- None """ self.vertices = transform_points(self.vertices, transformation)
def transform(self, matrix): """Transform this ``Polygon`` using a given transformation matrix. Parameters ---------- matrix : list of list The transformation matrix. """ for index, point in enumerate(transform_points(self.points, matrix)): self.points[index].x = point[0] self.points[index].y = point[1] self.points[index].z = point[2]
def to_vertices_and_faces(self, u=10, v=10): """Returns a list of vertices and faces Parameters ---------- u : int, optional Number of faces in the "u" direction. Default is ``10``. v : int, optional Number of faces in the "v" direction. Default is ``10``. Returns ------- (vertices, faces) A list of vertex locations and a list of faces, with each face defined as a list of indices into the list of vertices. """ if u < 3: raise ValueError('The value for u should be u > 3.') if v < 3: raise ValueError('The value for v should be v > 3.') theta = pi * 2 / u phi = pi * 2 / v vertices = [] for i in range(u): for j in range(v): x = cos(i * theta) * (self.radius_axis + self.radius_pipe * cos(j * phi)) y = sin(i * theta) * (self.radius_axis + self.radius_pipe * cos(j * phi)) z = self.radius_pipe * sin(j * phi) vertices.append([x, y, z]) # transform vertices to torus' plane frame = Frame.from_plane(self.plane) M = matrix_from_frame(frame) vertices = transform_points(vertices, M) faces = [] for i in range(u): ii = (i + 1) % u for j in range(v): jj = (j + 1) % v a = i * v + j b = ii * v + j c = ii * v + jj d = i * v + jj faces.append([a, b, c, d]) return vertices, faces
def to_vertices_and_faces(self, u=10): """Returns a list of vertices and faces. Parameters ---------- u : int, optional Number of faces in the "u" direction. Default is ``10``. Returns ------- (vertices, faces) A list of vertex locations and a list of faces, with each face defined as a list of indices into the list of vertices. """ if u < 3: raise ValueError('The value for u should be u > 3.') vertices = [] a = 2 * pi / u z = self.height / 2 for i in range(u): x = self.circle.radius * cos(i * a) y = self.circle.radius * sin(i * a) vertices.append([x, y, z]) vertices.append([x, y, -z]) # add v in bottom and top's circle center vertices.append([0, 0, z]) vertices.append([0, 0, -z]) # transform vertices to cylinder's plane frame = Frame.from_plane(self.circle.plane) M = matrix_from_frame(frame) vertices = transform_points(vertices, M) faces = [] # side faces for i in range(0, u * 2, 2): faces.append([i, i + 1, (i + 3) % (u * 2), (i + 2) % (u * 2)]) # top and bottom circle faces for i in range(0, u * 2, 2): top = [i, (i + 2) % (u * 2), len(vertices) - 2] bottom = [i + 1, (i + 3) % (u * 2), len(vertices) - 1] faces.append(top) faces.append(bottom[::-1]) return vertices, faces
def transform(self, T): """Apply a transformation to the pointcloud. Parameters ---------- T : :class:`compas.geometry.Transformation` The transformation. Returns ------- None The cloud is modified in place. """ for index, point in enumerate(transform_points(self.points, T)): self.points[index].x = point[0] self.points[index].y = point[1] self.points[index].z = point[2]
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)