def make_centroid(self): """Find the vertex centroid of the face, and turn it into a Vertex. Returns: Vertex([xc, yc, zc]) """ (xc, yc, zc) = self.find_centroid() return Vertex([xc, yc, zc])
def project(self, plane='xy', new_frame=None): """Project a copy of the face onto a plane that is spanned by two axes. The projection is orthographic. If this face is not attached to the global coordinate frame, then the face returned by this function will be in terms of the local frame. TODO: A "new_frame" can be specified, which will make the projection elements children of this frame. Otherwise, the elements will be children of the global coordinate frame. TODO: Generalize this function for arbitrary projection plane. """ # Eliminate minus sign in front of the plane: if plane[0] == '-': plane = plane[1:] pj_xyz = [] # Flatten the coordinates of each vertex onto the projection plane: for vertex in [self.p1, self.p2, self.p3, self.p4]: if plane == 'xy': pj_x = vertex.x pj_y = vertex.y pj_z = 0 elif plane == 'xz': pj_x = vertex.x pj_y = 0 pj_z = vertex.z elif plane == 'yz': pj_x = 0 pj_y = vertex.y pj_z = vertex.z else: raise ValueError("No valid projection plane given to " "projection method! " "Valid options: 'xy', 'xz', 'yz'") pj_xyz.append(Vertex([pj_x, pj_y, pj_z], parenttype="face")) # Create the projected face using the projected vertices. if not new_frame: pj = Face(pj_xyz[0], pj_xyz[1], pj_xyz[2], pj_xyz[3]) else: pj = Face(pj_xyz[0], pj_xyz[1], pj_xyz[2], pj_xyz[3], parenttype="frame") new_frame.add_face(pj) return pj
def __init__(self, head: Vertex, tail: Vertex = Vertex([0, 0, 0]), parenttype="global"): self.head = head # Vertex self.tail = tail # Vertex self.parenttype = "global" # Re-assign self.parenttype to check for validity of given parenttype if parenttype != "global": self.set_parenttype(parenttype) # Let child vertices know their parenttype is 'vector' for vertex in self.vertices(): vertex.set_parenttype("vector")
def project_face(self, face: Face, plane='xy', new_frame=None): """Project a copy of the face onto a plane that is spanned by two axes. The projection is orthographic. Coordinates will be in terms of the GLOBAL frame. TODO: A "new_frame" can be specified, which will make the projection elements children of this frame. Otherwise, the elements will be children of the global coordinate frame. TODO: Generalize this function for arbitrary projection plane. """ # Eliminate minus sign in front of the plane: if plane[0] == '-': plane = plane[1:] pj_xyz = [] for vertex in [face.p1, face.p2, face.p3, face.p4]: global_coords = self.vertex_xyz_global(vertex) if plane == 'xy': pj_x = global_coords[0] pj_y = global_coords[1] pj_z = 0 elif plane == 'xz': pj_x = global_coords[0] pj_y = 0 pj_z = global_coords[2] elif plane == 'yz': pj_x = 0 pj_y = global_coords[1] pj_z = global_coords[2] else: raise ValueError("No valid projection plane given to " "projection method! " "Valid options: 'xy', 'xz', 'yz'") pj_xyz.append(Vertex([pj_x, pj_y, pj_z], parenttype="face")) # Create the projected face using the projected vertices. if not new_frame: pj = Face(pj_xyz[0], pj_xyz[1], pj_xyz[2], pj_xyz[3], parenttype="global") else: raise TypeError("new_frame functionality is not yet implemented.") return pj
def find_cuboid_centroid(self): """Locate the centroid of a cuboid geometry. Cuboids have eight corners, which this method checks first. """ vertices = self.vertices() if len(vertices) != 8: raise ValueError("Tried to use method find_cuboid_centroid() " "with a geometry containing {} vertices. " "All cuboids must have exactly 8 vertices." "".format(len(vertices))) xc = 0 yc = 0 zc = 0 for vertex in vertices: xc += vertex.x / len(vertices) yc += vertex.y / len(vertices) zc += vertex.z / len(vertices) return Vertex(xc, yc, zc)
def plot_vertex( axes: plt.matplotlib.axes, vertex: Vertex, xyz_global=None, vertexfill=True, # If False, vertex will not be plotted vertexcolour="#000", # Specifies the vertex colour vertexsize=10, # Size of the plotted vertex vertexalpha=1 # Opacity of the plotted vertex ): # print("[DEBUG] Plotting {}".format(vertex)) # Check whether vertex should be plotted: if vertexfill: # If vertex is an orphan, use its local coordinates for plotting # if vertex.parenttype == "global": if not xyz_global.all(): x, y, z = vertex.xyz() # If not, check and use xyz_global coordinates provided else: # Verifying type of xyz_global if (isinstance(xyz_global, list) or isinstance(xyz_global, np.ndarray)): # Verifying length of xyz_global if len(xyz_global) == 3: [x, y, z] = xyz_global else: raise ValueError("Error: Tried to plot vertex using {}\ coordinates, but exactly 3 are needed!\ ".format(len(xyz_global))) # elif xyz_global == None: # raise TypeError("Something went wrong with vertex plot!") else: raise TypeError("Error: Tried to plot vertex, but argument \ 'xyz_global' was invalid type ({})!\ ".format(type(xyz_global))) axes.scatter(x, y, z, c=vertexcolour, s=vertexsize, alpha=vertexalpha)
def rotate(self, a, b, c, cor=None, seq='321'): """ Rotates the frame around a point in the global frame . a, b, c is Euler angle of rotation around local x, y, z axes expressed in radians seq is a string with the rotation sequence, e.g. '321' for: Rz(c).Ry(b).Rx(a).vertex Default rotation sequence: '321' (zyx) cor is the centre of rotation, which can be specified as a Vertex, or a list or numpy.ndarray of local coordinates. """ # ==== COR argument handling ==== # Case: COR is given as a list/ndarray of coordinates if (isinstance(cor, list) or isinstance(cor, np.ndarray)): if len(cor) == 3: cor = Vertex(list(cor)) # Case: COR is given as a Vertex elif isinstance(cor, Vertex): pass # If COR is None, ignore now and handle later elif not cor: pass # All other cases else: raise TypeError("Error when rotating frame. Centre of rotation \ argument not provided correctly!") # ==== Define relevant rotation matrices ==== Rx = np.array([[1, 0, 0], [0, cos(a), -sin(a)], [0, sin(a), cos(a)]]) Ry = np.array([[ cos(b), 0, sin(b)], [ 0, 1, 0], [-sin(b), 0, cos(b)]]) Rz = np.array([[cos(c), -sin(c), 0], [sin(c), cos(c), 0], [ 0, 0, 1]]) # ==== Perform rotation ==== for axis in reversed(seq): # If no Centre of Rotation (cor) is given, take frame origin. # If not, subtract coordinates of cor first before applying # the rotation, and then reverse this afterwards. temp = (self.x, self.y, self.z) if cor: self.translate(-1*temp[0], -1*temp[1], -1*temp[2]) if axis == '1': self.xdir = np.dot(Rx, self.xdir) self.ydir = np.dot(Rx, self.ydir) self.zdir = np.dot(Rx, self.zdir) if axis == '2': self.xdir = np.dot(Ry, self.xdir) self.ydir = np.dot(Ry, self.ydir) self.zdir = np.dot(Ry, self.zdir) if axis == '3': self.xdir = np.dot(Rz, self.xdir) self.ydir = np.dot(Rz, self.ydir) self.zdir = np.dot(Rz, self.zdir) # Reverse temporary translation. if cor: self.translate(1*temp[0], 1*temp[1], 1*temp[2]) # Update the frame's direction cosine matrix: self.recalculate_dcm()
def find_centroid(self): """Find the vertex centroid of the face.""" xc = 0.25 * (self.p1.x + self.p2.x + self.p3.x + self.p4.x) yc = 0.25 * (self.p1.y + self.p2.y + self.p3.y + self.p4.y) zc = 0.25 * (self.p1.z + self.p2.z + self.p3.z + self.p4.z) return Vertex(xc, yc, zc)
frame2 = Frame() # p1 = Vertex(0,0,0,frame1) # p2 = Vertex(1,0,0) # p3 = Vertex(1,1,0) # p4 = Vertex(0,1,0) # p5 = Vertex(0,1,1) # p6 = Vertex(0,0,1) # f1 = Face(p1, p2, p3, p4, frame1) # f2 = Face(p1, p4, p5, p6, frame1) frame1.readout() f1 = Face(Vertex(0, 0, 0), Vertex(1, 0, 0), Vertex(1, 1, 0), Vertex(0, 1, 0), frame1) frame1.readout() del (f1) frame1.readout() #%% ==== Plotting ==== # Toggle plotting functionality: if False: # Setting up the plot: fig = plt.figure(figsize=(8, 8))
""" === CHANGE ROTATION OF BODY HERE === """ # Note: choosing bdeg=90 will result in gimbal lock. adeg = 30 # Rotation around x axis in degrees bdeg = 40 # Rotation around y axis in degrees cdeg = 0 # Rotation around z axis in degrees """ ==================================== """ a = d2r(adeg) b = d2r(bdeg) c = d2r(cdeg) """Basic Cubesat model:""" p1 = Vertex(0.0, 0.0, 0.0) p2 = Vertex(0.0, 0.0, 0.2) p3 = Vertex(0.1, 0.0, 0.2) p4 = Vertex(0.1, 0.0, 0.0) p5 = Vertex(0.0, 0.1, 0.0) p6 = Vertex(0.0, 0.1, 0.2) p7 = Vertex(0.1, 0.1, 0.2) p8 = Vertex(0.1, 0.1, 0.0) # Manually calculate centroid of Cubesat geometry # COR = Vertex(0.05, 0.05, 0.1) # COR.translate(0.1, 0.1, 0.1) # Define faces of Cubesat fA = Face(p4, p3, p2, p1)
import numpy as np from numpy import sin, cos import matplotlib.pyplot as plt import matplotlib.animation as animation import mpl_toolkits.mplot3d as mp3d from cp_vertex import Vertex from cp_face import Face from cp_geometry import Geometry from cp_vector import Vector from cp_frame import Frame from cp_utilities import d2r #, r2d from cp_plotting import plot_global_tripod, plot_frame, \ plot_vertex, plot_face, plot_geometry_perpendiculars, plot_illumination p0 = Vertex([0.5, 0.5, 0.5]) p1 = Vertex([-0.05, -0.05, -0.1]) p2 = Vertex([-0.05, -0.05, 0.1]) p3 = Vertex([0.05, -0.05, 0.1]) p4 = Vertex([0.05, -0.05, -0.1]) p5 = Vertex([-0.05, 0.05, -0.1]) p6 = Vertex([-0.05, 0.05, 0.1]) p7 = Vertex([0.05, 0.05, 0.1]) p8 = Vertex([0.05, 0.05, -0.1]) fA = Face(p4, p3, p2, p1) fB = Face(p2, p3, p7, p6) fC = Face(p3, p4, p8, p7) fD = Face(p4, p1, p5, p8) fE = Face(p1, p2, p6, p5) fF = Face(p5, p6, p7, p8)
if True: # Setting up the plot: fig = plt.figure(figsize=(10, 7)) ax = mp3d.Axes3D(fig) """ TO CHANGE THE DEFAULT CAMERA VIEW, CHANGE THESE: """ ax.view_init(elev=20, azim=-60) steps = 40 angle_step = d2r(360 / steps) frame1 = Frame() frame1.translate(0.5, 0.5, 0.5) frame1.rotate(0, 0, d2r(1 * 360 / 12)) p1 = Vertex(-0.05, -0.05, -0.1, frame1) p2 = Vertex(-0.05, -0.05, 0.1, frame1) p3 = Vertex(0.05, -0.05, 0.1, frame1) p4 = Vertex(0.05, -0.05, -0.1, frame1) p5 = Vertex(-0.05, 0.05, -0.1, frame1) p6 = Vertex(-0.05, 0.05, 0.1, frame1) p7 = Vertex(0.05, 0.05, 0.1, frame1) p8 = Vertex(0.05, 0.05, -0.1, frame1) fA = Face(p4, p3, p2, p1, frame1) fB = Face(p2, p3, p7, p6, frame1) fC = Face(p3, p4, p8, p7, frame1) fD = Face(p4, p1, p5, p8, frame1) fE = Face(p1, p2, p6, p5, frame1) fF = Face(p5, p6, p7, p8, frame1)
#%% ==== Plotting ==== # Toggle plotting functionality: if True: # Setting up the plot: fig = plt.figure(figsize=(10, 7)) ax = mp3d.Axes3D(fig) """ TO CHANGE THE DEFAULT CAMERA VIEW, CHANGE THESE: """ ax.view_init(elev=20, azim=-60) steps = 24 angle_step = d2r(360 / steps) p0 = Vertex([0.5, 0.5, 0.5]) p1 = Vertex([-0.05, -0.05, -0.1]) p2 = Vertex([-0.05, -0.05, 0.1]) p3 = Vertex([0.05, -0.05, 0.1]) p4 = Vertex([0.05, -0.05, -0.1]) p5 = Vertex([-0.05, 0.05, -0.1]) p6 = Vertex([-0.05, 0.05, 0.1]) p7 = Vertex([0.05, 0.05, 0.1]) p8 = Vertex([0.05, 0.05, -0.1]) fA = Face(p4, p3, p2, p1) fB = Face(p2, p3, p7, p6) fC = Face(p3, p4, p8, p7) fD = Face(p4, p1, p5, p8) fE = Face(p1, p2, p6, p5) fF = Face(p5, p6, p7, p8)
#%% ==== Plotting ==== # Toggle plotting functionality: if True: # Setting up the plot: fig = plt.figure(figsize=(10, 7)) ax = mp3d.Axes3D(fig) """ TO CHANGE THE DEFAULT CAMERA VIEW, CHANGE THESE: """ ax.view_init(elev=20, azim=-50) steps = 64 angle_step = d2r(360 / steps) p0 = Vertex([0.5, 0.5, 0.5]) p1 = Vertex([-0.05, -0.05, -0.1]) p2 = Vertex([-0.05, -0.05, 0.1]) p3 = Vertex([0.05, -0.05, 0.1]) p4 = Vertex([0.05, -0.05, -0.1]) p5 = Vertex([-0.05, 0.05, -0.1]) p6 = Vertex([-0.05, 0.05, 0.1]) p7 = Vertex([0.05, 0.05, 0.1]) p8 = Vertex([0.05, 0.05, -0.1]) fA = Face(p4, p3, p2, p1) fB = Face(p2, p3, p7, p6) fC = Face(p3, p4, p8, p7) fD = Face(p4, p1, p5, p8) fE = Face(p1, p2, p6, p5) fF = Face(p5, p6, p7, p8)
def add_vertex(self, vertex: Vertex): self.vertices.append(vertex) vertex.set_parenttype="frame"
# Toggle plotting functionality: if True: # Setting up the plot: fig = plt.figure(figsize=(10, 7)) ax = mp3d.Axes3D(fig) """ TO CHANGE THE DEFAULT CAMERA VIEW, CHANGE THESE: """ ax.view_init(elev=20, azim=-60) steps = 40 angle_step = d2r(360 / steps) frame1 = Frame() vertex1 = Vertex(0.5, 0, 0, parent=frame1) vertex2 = Vertex(0.5, 0.5, 0, parent=frame1) def update(i): # Transforming frame1 frame1.translate(2 / steps, 2 / steps, 2 / steps) # frame1.rotate(0,0,-2*np.pi/(steps)) # Local transformation of vertex1 - rotate around vertex2 vertex1.rotate(2 * np.pi / steps, 0, 0, cor=vertex2) # Setting up the axes object ax.clear() ax.set_title("Wireframe visualization. Frame: {}".format(str(i)))
steps = 64 # steps is a global variable, so we can access it outside the update() # function. It should be noted that relying on global variables is # bad practice, but I'm an adult and I'll do what I like! # Defining an angle step that ensures the CubeSat model rotates completely, # but only once. angle_step = d2r(360/steps) """ ================= DEFINING THE GEOMETRIC OBJECTS ================= """ # Defining the vertices that will be the corners of the cubesat. I # recommend that you keep the units in meters. # ^ Z # | p1 = Vertex([-0.05, -0.05, -0.1]) # 2 ___ 6 p2 = Vertex([-0.05, -0.05, 0.1]) # |\3__\7 p3 = Vertex([ 0.05, -0.05, 0.1]) # | | | Y p4 = Vertex([ 0.05, -0.05, -0.1]) # 1| | 5| --------> p5 = Vertex([-0.05, 0.05, -0.1]) # \|__| p6 = Vertex([-0.05, 0.05, 0.1]) # 4 8 p7 = Vertex([ 0.05, 0.05, 0.1]) # \ p8 = Vertex([ 0.05, 0.05, -0.1]) # v X # Assembling the side panels of the Cubesat. Note that the order in which # the points is specified DOES matter! (see definition of Face class) # ^ Z # | fA = Face(p4, p3, p2, p1) # E ___ fB = Face(p2, p3, p7, p6) # |\ B_\
def remove_vertex(self, vertex: Vertex): self.vertices.remove(vertex) vertex.set_parenttype="global"
def make_cuboid_centroid(self): """Locates the centroid of a cuboid geometry and creates a Vertex at this point.""" (xc, yc, zc) = self.find_cuboid_centroid() return Vertex([xc, yc, zc])