def from_ellipse(cls, ellipse): """Construct a NURBS curve from an ellipse. This construction method is similar to the method ``CreateFromEllipse`` of the Rhino API for NURBS curves [1]_. References ---------- .. [1] https://developer.rhino3d.com/api/RhinoCommon/html/Overload_Rhino_Geometry_NurbsCurve_CreateFromEllipse.htm """ frame = Frame.from_plane(ellipse.plane) frame = Frame.worldXY() w = 0.5 * sqrt(2) dx = frame.xaxis * ellipse.major dy = frame.yaxis * ellipse.minor points = [ frame.point - dy, frame.point - dy - dx, frame.point - dx, frame.point + dy - dx, frame.point + dy, frame.point + dy + dx, frame.point + dx, frame.point - dy + dx, frame.point - dy ] knots = [0, 1 / 4, 1 / 2, 3 / 4, 1] mults = [3, 2, 2, 2, 3] weights = [1, w, 1, w, 1, w, 1, w, 1] return cls.from_parameters(points=points, weights=weights, knots=knots, multiplicities=mults, degree=2)
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 from_circle(cls, circle): """Construct a NURBS curve from a circle. Parameters ---------- circle : :class:`~compas.geometry.Circle` The circle geometry. Returns ------- :class:`OCCNurbsCurve` """ frame = Frame.from_plane(circle.plane) w = 0.5 * sqrt(2) dx = frame.xaxis * circle.radius dy = frame.yaxis * circle.radius points = [ frame.point - dy, frame.point - dy - dx, frame.point - dx, frame.point + dy - dx, frame.point + dy, frame.point + dy + dx, frame.point + dx, frame.point - dy + dx, frame.point - dy ] knots = [0, 1 / 4, 1 / 2, 3 / 4, 1] mults = [3, 2, 2, 2, 3] weights = [1, w, 1, w, 1, w, 1, w, 1] return cls.from_parameters(points=points, weights=weights, knots=knots, multiplicities=mults, degree=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 from_ellipse(cls, ellipse): """Construct a NURBS curve from an ellipse. Parameters ---------- ellipse : :class:`~compas.geometry.Ellipse` The ellipse geometry. Returns ------- :class:`OCCNurbsCurve` """ frame = Frame.from_plane(ellipse.plane) frame = Frame.worldXY() w = 0.5 * sqrt(2) dx = frame.xaxis * ellipse.major dy = frame.yaxis * ellipse.minor points = [ frame.point - dy, frame.point - dy - dx, frame.point - dx, frame.point + dy - dx, frame.point + dy, frame.point + dy + dx, frame.point + dx, frame.point - dy + dx, frame.point - dy ] knots = [0, 1 / 4, 1 / 2, 3 / 4, 1] mults = [3, 2, 2, 2, 3] weights = [1, w, 1, w, 1, w, 1, w, 1] return cls.from_parameters(points=points, weights=weights, knots=knots, multiplicities=mults, degree=2)
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) 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 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 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_cylinder(cylinder, color=None, radius_segments=8): geo = p3js.CylinderBufferGeometry(radiusTop=cylinder.circle.radius, radiusBottom=cylinder.circle.radius, height=cylinder.height, radiusSegments=radius_segments) mat = material_from_color(color) mesh = p3js.Mesh(geometry=geo, material=mat) mesh.position = list(cylinder.circle.plane.point) mesh.quaternion = list( Frame.from_plane(cylinder.circle.plane).quaternion.xyzw) return mesh
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 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 points_on_sphere_generator(sphere): for theta_deg in range(0, 360, 20): for phi_deg in range(0, 90, 10): theta = math.radians(theta_deg) phi = math.radians(phi_deg) x = sphere.point.x + sphere.radius * math.cos(theta) * math.sin(phi) y = sphere.point.y + sphere.radius * math.sin(theta) * math.sin(phi) z = sphere.point.z + sphere.radius * math.cos(phi) point = Point(x, y, z) axis = sphere.point - point plane = Plane((x, y, z), axis) f = Frame.from_plane(plane) # for UR5 is zaxis the xaxis yield Frame(f.point, f.zaxis, f.yaxis)
def __iter__(self): if self.start_vector: # correct start_vector self.start_vector = (self.axis.cross( self.start_vector.unitized())).cross(self.axis) for alpha in arange(0, 2 * math.pi, self.angle_step): R = Rotation.from_axis_and_angle(self.axis, alpha) yield self.start_vector.transformed(R) else: f = Frame.from_plane(Plane((0, 0, 0), self.axis)) for alpha in arange(0, 2 * math.pi, self.angle_step): x = math.cos(alpha) y = math.sin(alpha) yield f.to_world_coordinates(Vector(x, y, 0))
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 __iter__(self): yield self.axis alphas = arange(self.max_alpha / self.step, self.max_alpha + self.max_alpha / self.step, self.max_alpha / self.step) radii = [math.sin(alpha) for alpha in alphas] x, y = math.cos(alphas[0]), math.sin(alphas[0]) d = math.sqrt((1 - x)**2 + y**2) # get any vector normal to axis axis2 = Frame.from_plane(Plane((0, 0, 0), self.axis)).xaxis for alpha, r in zip(alphas, radii): R1 = Rotation.from_axis_and_angle(axis2, alpha) amount = int(round(2 * math.pi * r / d)) betas = arange(0, 2 * math.pi, 2 * math.pi / amount) for beta in betas: R2 = Rotation.from_axis_and_angle(self.axis, beta) yield self.axis.transformed(R2 * R1)
def get_distance(self, point): """ single point distance function """ if not isinstance(point, Point): point = Point(*point) frame = Frame.from_plane(self.cone.plane) m = matrix_from_frame(frame) mi = matrix_inverse(m) point.transform(mi) dxy = length_vector_xy(point) a = 1.1 c = [sin(a), cos(a)] # dot product d = sum( [i * j for (i, j) in zip(c, [dxy, point.z - self.cone.height])]) return d
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 = [[0, 0, 0]] 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]) 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]) return vertices, faces
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 if 'v' in kwargs: v = kwargs['v'] else: v = 10 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 from_cylinder(cls, cylinder: compas.geometry.Cylinder) -> 'BRep': """Construct a BRep from a COMPAS cylinder. Parameters ---------- cylinder : :class:`~compas.geometry.Cylinder` Returns ------- :class:`~compas_occ.brep.BRep` """ plane = cylinder.circle.plane height = cylinder.height radius = cylinder.circle.radius frame = Frame.from_plane(plane) frame.transform(Translation.from_vector(frame.zaxis * (-0.5 * height))) ax2 = gp_Ax2(gp_Pnt(*frame.point), gp_Dir(*frame.zaxis), gp_Dir(*frame.xaxis)) brep = BRep() brep.shape = BRepPrimAPI_MakeCylinder(ax2, radius, height).Shape() return brep
def to_vertices_and_faces(self, **kwargs): """Returns a list of vertices and faces""" u = kwargs.get('u') or 10 v = kwargs.get('v') or 10 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, **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 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
blocks = [] world = Frame.worldXY() for guid in guids: surface = RhinoSurface.from_guid(guid) block = surface.to_compas() block.name = str(guid) bottom = sorted( block.faces(), key=lambda face: dot_vectors(block.face_normal(face), [0, 0, -1]))[-1] plane = block.face_centroid(bottom), block.face_normal(bottom) frame = Frame.from_plane(plane) T = Transformation.from_frame_to_frame(frame, world) block.transform(T) blank = Box.from_bounding_box(block.bounding_box()) blank.xsize += 25 blank.ysize += 25 blank.zsize += 25 block.attributes['blank'] = blank block.attributes['bottom'] = bottom blocks.append(block)
def __init__(self, torus): self.torus = torus frame = Frame.from_plane(self.torus.plane) transform = matrix_from_frame(frame) self.inversetransform = matrix_inverse(transform)
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.') if v % 2 == 1: v += 1 theta = pi / v phi = pi * 2 / u hpi = pi * 0.5 halfheight = self.line.length / 2 sidemult = -1 capswitch = 0 vertices = [] for i in range(1, v + 1): for j in range(u): a = i + capswitch tx = self.radius * cos(a * theta - hpi) * cos(j * phi) ty = self.radius * cos(a * theta - hpi) * sin(j * phi) tz = self.radius * sin(a * theta - hpi) + sidemult * halfheight vertices.append([tx, ty, tz]) # switch from lower pole cap to upper pole cap if i == v / 2 and sidemult == -1: capswitch = -1 sidemult *= -1 vertices.append([0, 0, halfheight + self.radius]) vertices.append([0, 0, -halfheight - self.radius]) # move points to correct location in space plane = Plane(self.line.midpoint, self.line.direction) frame = Frame.from_plane(plane) M = matrix_from_frame(frame) vertices = transform_points(vertices, M) faces = [] # south pole triangle fan sp = len(vertices) - 1 for j in range(u): faces.append([sp, (j + 1) % u, j]) for i in range(v - 1): for j in range(u): jj = (j + 1) % u a = i * u + j b = i * u + jj c = (i + 1) * u + jj d = (i + 1) * u + j faces.append([a, b, c, d]) # north pole triangle fan np = len(vertices) - 2 for j in range(u): nc = len(vertices) - 3 - j nn = len(vertices) - 3 - (j + 1) % u faces.append([np, nn, nc]) return vertices, faces
cylinder = Cylinder(circle, 0.7 * height) circle = [[0, 0, 0.7 * height], [0, 0, 1]], 0.1 cone = Cone(circle, 0.3 * height) for node in network.nodes(): a = network.node_attributes(node, 'xyz') for nbr in network.neighbors(node): edge = node, nbr if not network.has_edge(*edge): edge = nbr, node b = network.node_attributes(nbr, 'xyz') force = network.edge_attribute(edge, 'f') direction = normalize_vector(subtract_vectors(b, a)) frame = Frame.from_plane([a, direction]) X = Transformation.from_frame_to_frame(world, frame) S = Scale.from_factors([force, 1, 1]) X = X * S shaft = cylinder.transformed(X) tip = cone.transformed(X) artist = CylinderArtist(shaft, layer=layer, color=(255, 0, 0)) artist.draw(u=16) artist = ConeArtist(tip, layer=layer, color=(255, 0, 0)) artist.draw(u=16)
OFFSET_1 = 0.03 # ============================================================================== # Build blocks # ============================================================================== blocks = [] for face_key in cablenet.faces(): vertices = cablenet.face_vertices(face_key) points = cablenet.get_vertices_attributes('xyz', keys=vertices) normals = [cablenet.vertex_normal(key) for key in vertices] face_normal = cablenet.face_normal(face_key) face_normal = scale_vector(face_normal, THICKNESS) point = add_vectors(points[0], face_normal) top_plane = Frame.from_plane(Plane(point, face_normal)) bottom = points[:] top = [] for point, normal in zip(points, normals): pt = point_on_plane(point, top_plane, normal) top.append(pt) bottom = offset_polygon_xy(bottom, OFFSET_0) top = offset_polygon_xy(top, OFFSET_1, True) vertices = bottom + top faces = [[0, 3, 2, 1], [4, 5, 6, 7], [3, 0, 4, 7], [2, 3, 7, 6], [1, 2, 6, 5], [0, 1, 5, 4]] block = Mesh.from_vertices_and_faces(vertices, faces)
def __init__(self, cone): self.cone = cone self.frame = Frame.from_plane(self.cone.plane) self.matrix = matrix_from_frame(self.frame) self.inversedmatrix = matrix_inverse(self.matrix)
def __init__(self, cylinder): self.cylinder = cylinder frame = Frame.from_plane(self.cylinder.plane) transform = matrix_from_frame(frame) self.inversetransform = matrix_inverse(transform)
"""There are several ways to construct a `Frame`. """ from compas.geometry import Point from compas.geometry import Vector from compas.geometry import Frame from compas.geometry import Plane # Frame autocorrects axes to be orthonormal F = Frame(Point(1, 0, 0), Vector(-0.45, 0.1, 0.3), Vector(1, 0, 0)) F = Frame([1, 0, 0], [-0.45, 0.1, 0.3], [1, 0, 0]) F = Frame.from_points([1, 1, 1], [2, 3, 6], [6, 3, 0]) F = Frame.from_plane(Plane([0, 0, 0], [0.5, 0.2, 0.1])) F = Frame.from_euler_angles([0.5, 1., 0.2]) F = Frame.worldXY()
# * Create a box at a certain location with a certain orientation. # * Create a Projection (can be orthogonal, parallel or perspective) # * Convert the box to a mesh and project the it onto the xy-plane. # * Use artists to draw the result """Assignment 01: Project box to xy-plane """ from compas.geometry import Point, Box, Frame, Plane from compas.geometry import Projection, matrix_from_parallel_projection from compas_rhino.artists import BoxArtist from compas_rhino.artists import MeshArtist from compas_rhino.artists import FrameArtist, PlaneArtist from compas.datastructures import Mesh # Define a Frame, which is not in the origin and a bit tilted to the world frame frame = Frame.from_plane(Plane([20, 40, 60], [0, 1, 1])) p = Plane(Point(0, 0, 0), [0, 0, 1]) d = 1, 0, 1 # Create a Box with that frame box = Box(frame, 10, 15, 20) # Create a Projection (can be orthogonal, parallel or perspective) P = Projection.from_plane_and_direction(p, d) # P = matrix_from_parallel_projection(p, d) # Create a Mesh from the Box mesh = Mesh.from_shape(box) ## Apply the Projection onto the mesh mesh_projected = mesh.transformed(P)