def animate_traj(traj): mesh = trimesh.load_mesh('../models/ionsat.stl') bounds = np.array(mesh.bounds) mesh.apply_translation(-(bounds[0] + bounds[1]) / 2) mesh.apply_scale(2) satellite = vp.compound([ vp.triangle(vs=[ vp.vertex(pos=vp.vector(*vertex), normal=vp.vector(*mesh.face_normals[itri])) for vertex in triangle ]) for itri, triangle in enumerate(mesh.triangles) ]) satellite = vp.compound([ satellite, *[ vp.arrow( pos=vp.vector(0, 0, 0), axis=axis, shaftwidth=0.01, color=axis) for axis in (vp.vector(1, 0, 0), vp.vector(0, 1, 0), vp.vector(0, 0, 1)) ] ]) # add frame to the satellite wind = vp.arrow(pos=vp.vector(0, 0, -3), axis=vp.vector(0, 0, 1), shaftwidth=0.01, color=vp.vector(1, 1, 1)) prevQ = None for Q in traj: if prevQ is not None: satellite.rotate(angle=-prevQ.angle(), axis=vp.vector(*prevQ.axis()), origin=vp.vector(0, 0, 0)) satellite.rotate(angle=Q.angle(), axis=vp.vector(*Q.axis()), origin=vp.vector(0, 0, 0)) prevQ = Q vp.rate(25)
def draw_mesh(mesh_list, opacity=0.5): """ Draw meshes to the scene. Args: mesh_list(list): A list of Mesh objects that should be drawn on the scene. opacity(float): Optional. Opacity of the mesh objects. Returns: A list of vpython.compound objects that represent the meshes. """ compounds = {} for mesh in mesh_list: surfaces = [] for surface in mesh.surfaces: vertices = [] for vertex in surface.vertices: v_pos = vertex.pos v = vpython.vertex( pos=vpython.vec(v_pos[0], v_pos[1], v_pos[2])) v.opacity = opacity if surface.collision: v.color = vpython.vec(1.0 - mesh.color[0], 1.0 - mesh.color[1], 1.0 - mesh.color[2]) else: v.color = vpython.vec(mesh.color[0], mesh.color[1], mesh.color[2]) vertices.append(v) surfaces.append( vpython.triangle(vs=[vertices[0], vertices[1], vertices[2]])) compounds[mesh.name] = vpython.compound(surfaces) # compounds[mesh.name].pickable = False compounds[mesh.name].name = mesh.name return compounds
def set_stl_origin(stl_obj, current_obj_origin, required_obj_origin, scene): """ Move the object so the required origin is at (0, 0, 0). Then set the origin for the generated stl object. Origin can't be changed, so creating a compound of itself allows setting an origin location :param stl_obj: The generated stl object. :type stl_obj: class:`vpython.compound` :param current_obj_origin: Current coordinates of the origin of the model :type current_obj_origin: class:`vpython.vector` :param required_obj_origin: Required coordinates to place the origin at (0, 0, 0) :type required_obj_origin: class:`vpython.vector` :param scene: The scene in which to draw the object :type scene: class:`vpython.canvas` :return: Compound object of itself, with origin set respective to the joint :rtype: class:`vpython.compound` """ # Move the object to put the origin at 0, 0, 0 movement = required_obj_origin - current_obj_origin stl_obj.pos += movement # Set invisible to return an overwritten copy stl_obj.visible = False # Return a compound of itself with the origin at (0, 0, 0) return compound([stl_obj], origin=vector(0, 0, 0), vector=x_axis_vector, canvas=scene)
def __set_graphic(self, structure): """ Set the graphic object depending on if one was given. If no object was given, create a box and return it :param structure: `float` or `str` representing the joint length or STL path to load from :type structure: `float`, `str` :raises ValueError: Joint length must be greater than 0 :return: Graphical object for the joint :rtype: class:`vpython.compound` """ if isinstance(structure, float): length = structure if length <= 0.0: raise ValueError("Joint length must be greater than 0") box_midpoint = vector(length / 2, 0, 0) # Create a box along the +x axis, with the origin (point of rotation) at (0, 0, 0) graphic_obj = box(canvas=self.__scene, pos=vector(box_midpoint.x, box_midpoint.y, box_midpoint.z), axis=x_axis_vector, size=vector(length, 0.1, 0.1), up=y_axis_vector) # Set the boxes new origin graphic_obj = compound([graphic_obj], origin=vector(0, 0, 0), axis=x_axis_vector, up=y_axis_vector) return graphic_obj else: return import_object_from_numpy_stl(structure, self.__scene)
def make_robot(): chassis_width = 155 # left side to right chassis_thickness = 3 # plastic thickness chassis_length = 200 # front to back wheel_thickness = 26 wheel_diameter = 70 axle_x = 30 # wheel axle from axle_z = -20 rear_castor_position = vp.vector(-80, -6, -30) rear_castor_radius = 14 rear_caster_thickness = 12 base = vp.box(length=chassis_length, height=chassis_thickness, width=chassis_width) # rotate to match body - so Z is height and Y is width base.rotate(angle=vp.radians(90), axis=vp.vector(1, 0, 0)) wheel_dist = chassis_width / 2 wheel_l = vp.cylinder(radius=wheel_diameter / 2, length=wheel_thickness, pos=vp.vector(axle_x, -wheel_dist, axle_z), axis=vp.vector(0, -1, 0)) wheel_r = vp.cylinder(radius=wheel_diameter / 2, length=wheel_thickness, pos=vp.vector(axle_x, wheel_dist, axle_z), axis=vp.vector(0, 1, 0)) rear_castor = vp.cylinder(radius=rear_castor_radius, length=rear_caster_thickness, pos=rear_castor_position, axis=vp.vector(0, 1, 0)) return vp.compound([base, wheel_l, wheel_r, rear_castor])
def __init__(self, x, y, shape='_', colour=color.white, player='first', opacity=1): self.x_pos = x self.y_pos = y self.shape = shape self.color = colour self.blocks = [] radius = 0.23 if player == 'first' else 0.22 for _ in range(3): block = compound([ box(pos=vector(x, y, 0), height=.8, width=.8, length=.8, opacity=opacity), sphere(pos=vector(x, y, 0.5), radius=radius, opacity=opacity), sphere(pos=vector(x, y, -0.5), radius=radius, opacity=opacity) ]) self.blocks.append(block) self.shape_dictionary = { '_': (0, 0, 1, 0, 2, 0), 'I': (0, 0, 0, 1, 0, 2), 'b': (0, 0, 1, 0, 0, 1), 'd': (0, 0, 1, 0, 1, 1), 'p': (0, 0, 0, 1, 1, 1), 'q': (0, 1, 1, 0, 1, 1), } self.updater(self.x_pos, self.y_pos)
def draw_robot(self): # retornar el centro de masa if self.robot_type == 0: caja = box(pos=self.initialPosition, size=self.size, color=self.color) rueda_trasera = cylinder( pos=caja.pos - vector(caja.size.x / 4, caja.size.y, caja.size.z / 4), axis=vector(0, 4, 0), radius=0.5, color=self.color) rueda_delantera = cylinder( pos=caja.pos + vector(caja.size.x / 4, -caja.size.y, -caja.size.z / 4), axis=vector(0, 4, 0), radius=0.5, color=self.color) robot = compound([caja, rueda_trasera, rueda_delantera]) elif self.robot_type == 1: cuerpo = cylinder(pos=self.initialPosition, radius=3, color=color.blue, axis=vector(0, 0, 1), opacity=1) rueda_1 = cylinder(pos=cuerpo.pos + vector(0, cuerpo.radius / 2, 0), radius=cuerpo.radius / 4, color=color.red, axis=vector(0, 1, 0), opacity=1) rueda_2 = cylinder( pos=cuerpo.pos - vector(0, cuerpo.radius / 2 + cuerpo.radius / 3, 0), radius=cuerpo.radius / 4, color=color.cyan, axis=vector(0, 1, 0), opacity=1) estabilizador = sphere( pos=cuerpo.pos + vector(cuerpo.radius / 2 + cuerpo.radius / 9, 0, 0), radius=cuerpo.radius / 4, color=color.green, axis=vector(0, 1, 0), opacity=1) robot = compound([cuerpo, rueda_1, rueda_2, estabilizador]) return robot
def import_object_from_stl(filename): """ Import an stl object and convert it into a usable vpython object. Function not directly part of the vpython package, but can by found as part of vpython git repo. Code was based on it. https://github.com/vpython/vpython-jupyter/blob/master/convert_stl.zip :param filename: Name of the stl file to import (Exclude path and extension). :type filename: `str` :return: Compound object of a collection of triangles formed from an stl file. :rtype: class:`vpython.compound` .. deprecated:: A new function using numpy import is available. It accepts both ASCII and BINARY formats. """ raise DeprecationWarning("This function is outdated. Use import_object_from_numpy_stl") # Open the file filepath = './graphics/models/' + filename + '.stl' stl_file = open(filepath, mode='rb') stl_file.seek(0) stl_text = stl_file.readlines() # Initial Conditions triangles = [] vertices = [] normal = None # For every line in the file for line in stl_text: file_line = line.split() # If blank line (skip) if not file_line: pass # If a face elif file_line[0] == b'facet': normal = vec( float(file_line[2]), float(file_line[3]), float(file_line[4]) ) # If a vertex elif file_line[0] == b'vertex': vertices.append( vertex( pos=vec( float(file_line[1]), float(file_line[2]), float(file_line[3]) ), normal=normal, color=color.white ) ) if len(vertices) == 3: triangles.append(triangle(vs=vertices)) vertices = [] return compound(triangles)
def import_object_from_numpy_stl(filename, scene): """ Import either an ASCII or BINARY file format of an STL file. The triangles will be combined into a single compound entity. :param filename: Path of the stl file to import. :type filename: `str` :param scene: The scene in which to draw the object :type scene: class:`vpython.canvas` :return: Compound object of a collection of triangles formed from an stl file. :rtype: class:`vpython.compound` """ # Load the mesh using NumPy-STL the_mesh = mesh.Mesh.from_file(filename) num_faces = len(the_mesh.vectors) triangles = [] # For every face in the model for face in range(0, num_faces): # Get the (3) 3D points point0 = the_mesh.vectors[face][0] point1 = the_mesh.vectors[face][1] point2 = the_mesh.vectors[face][2] # Get the normal direction for the face normal0 = the_mesh.normals[face][0] normal1 = the_mesh.normals[face][1] normal2 = the_mesh.normals[face][2] normal = vec(normal0, normal1, normal2) # Create the VPython 3D points vertex0 = vertex( pos=vec(point0[0], point0[1], point0[2]), normal=normal, color=color.white ) vertex1 = vertex( pos=vec(point1[0], point1[1], point1[2]), normal=normal, color=color.white ) vertex2 = vertex( pos=vec(point2[0], point2[1], point2[2]), normal=normal, color=color.white ) # Combine them in a list vertices = [vertex0, vertex1, vertex2] # Create a triangle using the points, and add it to the list triangles.append(triangle(canvas=scene, vs=vertices)) # Return a compound of the triangles visual_mesh = compound(triangles, origin=vector(0, 0, 0), canvas=scene) return visual_mesh
def draw_compound(xys): return compound([ quad(vs=[ vertex(pos=points[_idx[(x + dx, y + dy)]], normal=points_n[_idx[x + dx, y + dy]], color=colors[_idx[(x + dx, y + dy)]]) for dx, dy in [(0, 0), (D, 0), (D, D), (0, D)] ]) for x, y in xys ])
def __init__(self, x, y, shape='O', colour=color.white, player='first', opacity=1): self.x_pos = x self.y_pos = y self.shape = shape self.color = colour self.orientation = '0' self.blocks = [] radius = 0.23 if player == 'first' else 0.22 for _ in range(4): block = compound([ box(pos=vector(x, y, 0), height=.8, width=.8, length=.8, opacity=opacity), sphere(pos=vector(x, y, 0.5), radius=radius, opacity=opacity), sphere(pos=vector(x, y, -0.5), radius=radius, opacity=opacity) ]) self.blocks.append(block) self.shape_dictionary = { ('I', '0'): (-1, 0, 0, 0, 1, 0, 2, 0), ('I', '1'): (0, -1, 0, 0, 0, 1, 0, 2), ('I', '2'): (-1, 0, 0, 0, 1, 0, 2, 0), ('I', '3'): (0, -1, 0, 0, 0, 1, 0, 2), ('J', '0'): (-1, 0, 0, 0, 1, 0, -1, 1), ('J', '1'): (0, -1, 0, 0, 0, 1, -1, -1), ('J', '2'): (-1, 0, 0, 0, 1, 0, 1, -1), ('J', '3'): (0, -1, 0, 0, 0, 1, 1, 1), ('T', '0'): (-1, 0, 0, 0, 1, 0, 0, 1), ('T', '1'): (0, 0, 0, -1, 0, 1, -1, 0), ('T', '2'): (-1, 0, 0, 0, 1, 0, 0, -1), ('T', '3'): (0, 0, 0, -1, 0, 1, 1, 0), ('S', '0'): (1, 0, 0, 0, 1, 1, 2, 1), ('S', '1'): (1, 1, 1, 0, 0, 1, 0, 2), ('S', '2'): (1, 0, 0, 0, 1, 1, 2, 1), ('S', '3'): (1, 1, 1, 0, 0, 1, 0, 2), ('Z', '0'): (1, 1, 0, 1, 1, 0, 2, 0), ('Z', '1'): (0, 1, 0, 0, 1, 1, 1, 2), ('Z', '2'): (1, 1, 0, 1, 1, 0, 2, 0), ('Z', '3'): (0, 1, 0, 0, 1, 1, 1, 2), ('O', '0'): (1, 0, 0, 0, 0, 1, 1, 1), ('O', '1'): (1, 0, 0, 0, 0, 1, 1, 1), ('O', '2'): (1, 0, 0, 0, 0, 1, 1, 1), ('O', '3'): (1, 0, 0, 0, 0, 1, 1, 1), ('L', '0'): (-1, 0, 0, 0, 1, 0, 1, 1), ('L', '1'): (0, -1, 0, 0, 0, 1, -1, 1), ('L', '2'): (-1, 0, 0, 0, 1, 0, -1, -1), ('L', '3'): (0, -1, 0, 0, 0, 1, 1, -1), } self.updater(self.x_pos, self.y_pos, orientation=self.orientation)
def make_capsule(R, L): parts = [] sph1 = sphere(pos=vec(R, 0, 0), radius=R, color=color.cyan, opacity=0.3) cyl = cylinder(pos=vec(R, 0, 0), axis=vec(L - 2 * R, 0, 0), radius=R, color=color.cyan, opacity=0.3) sph2 = sphere(pos=sph1.pos+cyl.axis, radius=R, color=color.cyan, opacity=0.3) parts.append(sph1) parts.append(cyl) parts.append(sph2) obj = compound(parts) return obj
def show(mesh, Qs=None): """ if Qs is None, will display the original situation of the satellite if Qs is a single Quaternion, will display the satellite in this situation if Qs is a list, wil display step by step every situations """ if Qs is None: Qs = [loas.Quat(1, 0, 0, 0)] elif type(Qs) != list: Qs = [Qs] bounds = np.array(mesh.bounds) mesh.apply_scale(1 / np.linalg.norm(mesh.extents)) # auto resize satellite = vp.compound([ vp.triangle(vs=[ vp.vertex(pos=vp.vector(*vertex), normal=vp.vector(*mesh.face_normals[itri])) for vertex in triangle ]) for itri, triangle in enumerate(mesh.triangles) ]) satellite = vp.compound([ satellite, *[ vp.arrow( pos=vp.vector(0, 0, 0), axis=axis, shaftwidth=0.01, color=axis) for axis in (vp.vector(1, 0, 0), vp.vector(0, 1, 0), vp.vector(0, 0, 1)) ] ]) # add frame to the satellite wind = vp.arrow(pos=vp.vector(0, 0, 3), axis=vp.vector(0, 0, -1), shaftwidth=0.01, color=vp.vector(1, 1, 1)) prevQ = None for Q in Qs: if prevQ is not None: satellite.rotate(angle=-prevQ.angle(), axis=vp.vector(*prevQ.axis()), origin=vp.vector(0, 0, 0)) satellite.rotate(angle=Q.angle(), axis=vp.vector(*Q.axis()), origin=vp.vector(0, 0, 0)) prevQ = Q vp.rate(2)
def draw_reference_frame_axes(se3_pose, scene): """ Draw x, y, z axes from the given point. Each axis is represented in the objects reference frame. :param se3_pose: SE3 pose representation of the reference frame :type se3_pose: class:`spatialmath.pose3d.SE3` :param scene: Which scene to put the graphics in :type scene: class:`vpython.canvas` :return: Compound object of the 3 axis arrows. :rtype: class:`vpython.compound` """ origin = get_pose_pos(se3_pose) x_axis = get_pose_x_vec(se3_pose) y_axis = get_pose_y_vec(se3_pose) # Create Basic Frame # Draw X Axis x_arrow = arrow(canvas=scene, pos=origin, axis=x_axis_vector, length=0.25, color=color.red) # Draw Y Axis y_arrow = arrow(canvas=scene, pos=origin, axis=y_axis_vector, length=0.25, color=color.green) # Draw Z Axis z_arrow = arrow(canvas=scene, pos=origin, axis=z_axis_vector, length=0.25, color=color.blue) # Combine all to manipulate together # Set origin to where axis converge (instead of the middle of the resulting object bounding box) frame_ref = compound([x_arrow, y_arrow, z_arrow], origin=origin, canvas=scene) # Set frame axes frame_ref.axis = x_axis frame_ref.up = y_axis return frame_ref
def __init__(self, corners, faces, color=vpy.vec(.3, .3, .8), opacity=1, show_faces=True, show_edges=True, sort_faces=True, debug=False): """ inputs: ------- corners - (list) of vpython.vectors- list of 3d coordinates of the corners of the polyhedron faces - (list) of lists - lists of corner indices. one sub-list per face of the polyhedron examples: --------- # create a small pyramid >>> corners = [(0,0,0), (1,0,0), (0,1,0), (0,0,1)] >>> corners = [vpy.vec(*corner) for corner in corners] >>> faces = [[0,1,2], [1,2,3], [0,1,3], [0,2,3]] >>> poly = polyhedron(corners, faces) """ self.debug = debug self.color = color self._objects = [] self.vertices = [ vpy.vertex(pos=corner, color=self.color, opacity=opacity) for corner in corners ] # self.vertices = [vpy.vertex(pos=corner, color=corner, opacity=opacity) for corner in corners] self.pos = get_com(self.vertices) self.faces = faces self.face_centers = self._get_face_centers() if sort_faces: # sort the faces so that the polygons are displayed without holes self._sort_faces() if show_edges: self.draw_all_edges() if show_faces: self.draw_faces() # if self.debug: # self.show_faces() self.obj = vpy.compound(self._objects) for obj in self._objects: obj.visible = 0
def create_landscape_from_points(): # --- create quads quadlist = [] for y, line in enumerate(Game.points): for x, point in enumerate(line): #a = point try: a = Game.points[y][x] b = Game.points[y][x + 1] c = Game.points[y + 1][x + 1] d = Game.points[y + 1][x] except IndexError: continue quadlist.append(v.quad(vs=[a, b, c, d])) Game.landscape = v.compound(quadlist, origin=v.vector(0, 0, 0), pos=v.vector(0, 0, 0))
def make_ramp(alpha, H): parts = [] B = H * sin(0.5 * pi - alpha) / sin(alpha) front = triangle( v0=vertex(pos=vec(-0.5 * B, 0, 0.25 * H)), v1=vertex(pos=vec(-0.5 * B, H, 0.25 * H)), v2=vertex(pos=vec(0.5 * B, 0, 0.25 * H)) ) back = triangle( v0=vertex(pos=vec(-0.5 * B, 0, - 0.25 * H)), v1=vertex(pos=vec(-0.5 * B, H, - 0.25 * H)), v2=vertex(pos=vec(0.5 * B, 0, - 0.25 * H)) ) top = quad( v0=front.v1, v1=back.v1, v2=back.v2, v3=front.v2, ) bottom = quad( v0=front.v0, v1=back.v0, v2=back.v2, v3=front.v2 ) side = quad( v0=front.v0, v1=back.v0, v2=back.v1, v3=front.v1 ) parts.append(front) parts.append(back) parts.append(top) parts.append(bottom) parts.append(side) obj = compound(parts) obj.height = H obj.base = B obj.angle = alpha # obj.color = color.cyan # obj.opacity = 0.1 return obj
def __set_graphic(self, structure, axis_through): """ Set the graphic object depending on if one was given. If no object was given, create a box and return it :param structure: `float` or `str` representing the joint length or STL path to load from :type structure: `float`, `str` :param axis_through: The axis that the longest side goes through :type axis_through: class:`numpy.ndarray` :raises ValueError: Joint length must be greater than 0 :return: Graphical object for the joint :rtype: class:`vpython.compound` """ if isinstance(structure, float): length = structure if length <= 0.0: raise ValueError("Joint length must be greater than 0") axis = vector(axis_through[0], axis_through[1], axis_through[2]) axis.mag = length box_midpoint = axis / 2 box_tooltip = axis # Create a box along the +x axis, with the origin # (point of rotation) at (0, 0, 0) graphic_obj = box( canvas=self.__scene, pos=vector(box_midpoint.x, box_midpoint.y, box_midpoint.z), axis=axis, size=vector(length, 0.1, 0.1), # L, W, H # up=y_axis_vector ) # Set the boxes new origin graphic_obj = compound([graphic_obj], origin=box_tooltip, axis=axis) return graphic_obj else: # self.stl_offset = structure[2] return import_object_from_numpy_stl(structure, self.__scene)
def _create_obj(self, entity: Entity, origin: Entity, texture: Optional[str]) -> vpython.sphere: main_body = vpython.box() side_panels = vpython.box(height=2, width=0.5, length=0.6) obj = vpython.compound([main_body, side_panels], make_trail=True, texture=texture, bumpmap=vpython.textures.gravel) obj.pos = entity.screen_pos(origin) obj.axis = calc.angle_to_vpy(entity.heading) obj.length = entity.r * 2 obj.height = entity.r * 2 obj.width = entity.r * 2 # A compound object doesn't actually have a radius, but we need to # monkey-patch this for when we recentre the camera, to determine the # relevant_range of the space station obj.radius = entity.r return obj
def graphic(size: float): # Iterate over a list of Point 3-tuples, each representing the # vertices of a triangle in the sphere segment. vpython_tris = [] for tri in calc._build_sphere_segment_vertices(entity.r, size): # TODO: we pick an ocean blue colour for Earth, but really we # should find a better way to make the landing graphic not a # hardcoded value after the demo. vpython_verts = [ vpython.vertex(pos=vpython.vector(*coord), color=(vpython.vector(0, 0.6, 0.8) if entity.name == common.EARTH else vpython.vector(0.5, 0.5, 0.5))) for coord in tri ] vpython_tris.append(vpython.triangle(vs=vpython_verts)) return vpython.compound(vpython_tris, opacity=0, pos=self._obj.pos, up=common.DEFAULT_UP)
def _create_obj(self, entity: Entity, origin: Entity, texture_path: Optional[str]) -> vpython.sphere: ship = vpython.cone(pos=vpython.vector(5, 0, 0), axis=vpython.vector(5, 0, 0), radius=3) entrance = vpython.extrusion( path=[vpython.vec(0, 0, 0), vpython.vec(4, 0, 0)], shape=[ vpython.shapes.circle(radius=3), vpython.shapes.rectangle(pos=[0, 0], width=0.5, height=0.5) ], pos=vpython.vec(3, 0, 0)) docking_arm = vpython.extrusion( path=[ vpython.vec(0, 0, 0), vpython.vec(1.5, 0, 0), vpython.vec(1.5, 0.5, 0) ], shape=[vpython.shapes.circle(radius=0.03)]) obj = vpython.compound([ship, entrance, docking_arm], make_trail=True, texture=vpython.textures.metal, bumpmap=vpython.bumpmaps.gravel) obj.pos = entity.screen_pos(origin) obj.axis = calc.angle_to_vpy(entity.heading) obj.length = entity.r * 2 obj.height = entity.r * 2 obj.width = entity.r * 2 # A compound object doesn't actually have a radius, but we need to # monkey-patch this for when we recentre the camera, to determine the # relevant_range of the space station obj.radius = entity.r return obj
def show(modelPath, posX, posY, angle, size): global models global s if (len(s.objects) >= 65535): return 0 f = [] model = open(modelPath, "r") lines = model.read().split("\n") v = [] n = [] f = [] minV = [0, 0, 0] maxV = [0, 0, 0] vertexCount = 0 # Parse the OBJ file for line in lines: e = line.split(" ") if (e[0] == "v"): v.append(vec(float(e[1]), -float(e[3]), -float(e[2]))) minV, maxV = minmaxVertices( minV, maxV, [float(e[1]), -float(e[3]), -float(e[2])]) elif (e[0] == "vn"): n.append(vec(float(e[1]), -float(e[3]), -float(e[2]))) elif (e[0] == "f"): if vertexCount < 65533: t0 = e[1].split("/") t1 = e[2].split("/") t2 = e[3].split("/") vert0 = vertex(pos=v[int(t0[0]) - 1], normal=n[int(t0[2]) - 1]) vert1 = vertex(pos=v[int(t1[0]) - 1], normal=n[int(t1[2]) - 1]) vert2 = vertex(pos=v[int(t2[0]) - 1], normal=n[int(t2[2]) - 1]) f.append(triangle(vs=[vert0, vert1, vert2])) vertexCount += 3 modelSize = max(maxV[0] - minV[0], maxV[1] - minV[1], maxV[2] - minV[2]) models.append(compound(f)) origin = numpy.array([0, 0, 7.79423]) fov = numpy.pi / 2.0 direction = numpy.array([ posX * numpy.tan(fov / 2.0), posY * numpy.tan(fov / 2.0) * (2.0 / 3.0), -1 ]) direction = direction / numpy.linalg.norm(direction) point = numpy.array([0, 0, 0]) normal = numpy.array([0, 0, 0]) # Determine the portion of the screen the object is on. if (abs(posX) < 0.475 and abs(posY) < 0.475): point = numpy.array([0, 0, -roomHeight / 2]) normal = numpy.array([0, 0, 1]) else: if posY > posX: if posY > -posX: point = numpy.array([0, roomHeight / 2, 0]) normal = numpy.array([0, -1, 0]) else: point = numpy.array([-roomWidth / 2, 0, 0]) normal = numpy.array([1, 0, 0]) else: if posY < -posX: point = numpy.array([0, -roomHeight / 2, 0]) normal = numpy.array([0, 1, 0]) else: point = numpy.array([roomWidth / 2, 0, 0]) normal = numpy.array([-1, 0, 0]) # Linear interpolation to find the line to plain intersection d = numpy.dot(point - origin, normal) / numpy.dot(direction, normal) final = origin + d * direction dZ = abs(origin[2] - final[2]) worldSize = (size * dZ * numpy.tan(fov / 2)) final += (worldSize / 2) * normal models[-1].pos = vec(final[0], final[1], final[2]) models[-1].rotate(angle=angle, axis=vec(0, 1, 0)) models[-1].size = vec(worldSize / modelSize, worldSize / modelSize, worldSize / modelSize) return 0
def _create_hab(self, entity: Entity, texture: str) -> \ vpython.compound: def vertex(x: float, y: float, z: float) -> vpython.vertex: return vpython.vertex(pos=vpython.vector(x, y, z)) # See the docstring of ThreeDeeObj._create_obj for why the dimensions # that define the shape of the habitat will not actually directly # translate to world-space. body = vpython.cylinder(pos=vpython.vec(0, 0, 0), axis=vpython.vec(-5, 0, 0), radius=10) head = vpython.cone(pos=vpython.vec(0, 0, 0), axis=vpython.vec(2, 0, 0), radius=10) wing = vpython.triangle(v0=vertex(0, 0, 0), v1=vertex(-5, 30, 0), v2=vertex(-5, -30, 0)) wing2 = vpython.triangle(v0=vertex(0, 0, 0), v1=vertex(-5, 0, 30), v2=vertex(-5, 0, -30)) hab = vpython.compound([body, head, wing, wing2], make_trail=True, texture=texture) hab.axis = calc.angle_to_vpy(entity.heading) hab.radius = entity.r / 2 hab.shininess = 0.1 hab.length = entity.r * 2 boosters: List[vpython.cylinder] = [] body_radius = entity.r / 8 for quadrant in range(0, 4): # Generate four SRB bodies. normal = vpython.rotate(vpython.vector(0, 1, 1).hat, angle=quadrant * vpython.radians(90), axis=vpython.vector(1, 0, 0)) boosters.append( vpython.cylinder(radius=self.BOOSTER_RADIUS, pos=(self.BOOSTER_RADIUS + body_radius) * normal)) boosters.append( vpython.cone( radius=self.BOOSTER_RADIUS, length=0.2, pos=((self.BOOSTER_RADIUS + body_radius) * normal + vpython.vec(1, 0, 0)))) # Append an invisible point to shift the boosters down the fuselage. # For an explanation of why that matters, read the # ThreeDeeObj._create_obj docstring (and if that doesn't make sense, # get in touch with Patrick M please hello hi I'm always free!) boosters.append(vpython.sphere(opacity=0, pos=vpython.vec(1.2, 0, 0))) booster_texture = texture.replace(f'{entity.name}.jpg', 'SRB.jpg') hab.boosters = vpython.compound(boosters, texture=booster_texture) hab.boosters.length = entity.r * 2 hab.boosters.axis = hab.axis parachute: List[vpython.standardAttributes] = [] string_length = entity.r * 0.5 parachute_texture = texture.replace(f'{entity.name}.jpg', 'Parachute.jpg') # Build the parachute fabric. parachute.append( vpython.extrusion( path=vpython.paths.circle(radius=0.5, up=vpython.vec(-1, 0, 0)), shape=vpython.shapes.arc(angle1=vpython.radians(5), angle2=vpython.radians(95), radius=1), pos=vpython.vec(string_length + self.PARACHUTE_RADIUS / 2, 0, 0))) parachute[0].height = self.PARACHUTE_RADIUS * 2 parachute[0].width = self.PARACHUTE_RADIUS * 2 parachute[0].length = self.PARACHUTE_RADIUS for quadrant in range(0, 4): # Generate parachute attachment lines. string = vpython.cylinder(axis=vpython.vec(string_length, self.PARACHUTE_RADIUS, 0), radius=0.2) string.rotate(angle=(quadrant * vpython.radians(90) - vpython.radians(45)), axis=vpython.vector(1, 0, 0)) parachute.append(string) parachute.append( vpython.sphere(opacity=0, pos=vpython.vec( -(string_length + self.PARACHUTE_RADIUS), 0, 0))) hab.parachute = vpython.compound(parachute, texture=parachute_texture) hab.parachute.visible = False return hab
def __create_grid_objects(self): """ Draw a grid along each 3D plane, that is closest to the camera. :return: List of the three drawn axes. :rtype: `list` """ # Initial conditions xz_lines = [] xy_lines = [] yz_lines = [] camera_axes = self.camera_axes # Locate centre of axes if self.__relative_cam: x_origin, y_origin, z_origin = round(self.__scene.center.x, 2), \ round(self.__scene.center.y, 2), \ round(self.__scene.center.z, 2) self.__focal_point = [x_origin, y_origin, z_origin] # Convert focal point for 2D rendering. Puts focus point in centre of the view if not self.__is_3d: self.__focal_point = [ val - int(self.__num_squares / 2) for val in self.__focal_point ] x_origin = self.__focal_point[0] y_origin = self.__focal_point[1] z_origin = 0 self.__focal_point[2] = z_origin else: x_origin, y_origin, z_origin = self.__focal_point[0], \ self.__focal_point[1], \ self.__focal_point[2] # CAMERA AXES | DISPLAYED GRID | XZ PLANE | XY PLANE | YZ PLANE # x,y,z | x,y,z | x,z | x,y | y,z # -------------+-----------------+----------+----------+---------- # -,-,- | +,+,+ | +,+ | +,+ | +,+ # -,-,+ | +,+,- | +,- | +,+ | +,- # -,+,- | +,-,+ | +,+ | +,- | -,+ # -,+,+ | +,-,- | +,- | +,- | -,- # +,-,- | -,+,+ | -,+ | -,+ | +,+ # +,-,+ | -,+,- | -,- | -,+ | +,- # +,+,- | -,-,+ | -,+ | -,- | -,+ # +,+,+ | -,-,- | -,- | -,- | -,- # min = -num_squares or 0, around the default position # max = +num_squares or 0, around the default position # e.g. at the origin, for negative axes: -10 -> 0, positive axes: 0 -> 10 min_x_coord = round( x_origin + (-(self.__num_squares / 2) + (sign(camera_axes.x) * -1) * (self.__num_squares / 2)) * self.__scale, 2) max_x_coord = round( x_origin + ((self.__num_squares / 2) + (sign(camera_axes.x) * -1) * (self.__num_squares / 2)) * self.__scale, 2) min_y_coord = round( y_origin + (-(self.__num_squares / 2) + (sign(camera_axes.y) * -1) * (self.__num_squares / 2)) * self.__scale, 2) max_y_coord = round( y_origin + ((self.__num_squares / 2) + (sign(camera_axes.y) * -1) * (self.__num_squares / 2)) * self.__scale, 2) min_z_coord = round( z_origin + (-(self.__num_squares / 2) + (sign(camera_axes.z) * -1) * (self.__num_squares / 2)) * self.__scale, 2) max_z_coord = round( z_origin + ((self.__num_squares / 2) + (sign(camera_axes.z) * -1) * (self.__num_squares / 2)) * self.__scale, 2) x_coords = arange(min_x_coord, max_x_coord + self.__scale, self.__scale) y_coords = arange(min_y_coord, max_y_coord + self.__scale, self.__scale) z_coords = arange(min_z_coord, max_z_coord + self.__scale, self.__scale) # Compound origins are in the middle of the bounding boxes. Thus new pos will be between max and min. x_middle = (max_x_coord + min_x_coord) / 2 y_middle = (max_y_coord + min_y_coord) / 2 z_middle = (max_z_coord + min_z_coord) / 2 line_thickness = min(max(self.__scale / 25, 0.01), 5) # 0.01 -> 5 # XZ plane for x_point in x_coords: # Draw a line across for each x coord, along the same y-axis, from min to max z coord xz_lines.append( create_line(vector(x_point, y_origin, min_z_coord), vector(x_point, y_origin, max_z_coord), self.__scene, thickness=line_thickness)) for z_point in z_coords: # Draw a line across each z coord, along the same y-axis, from min to max z coord xz_lines.append( create_line(vector(min_x_coord, y_origin, z_point), vector(max_x_coord, y_origin, z_point), self.__scene, thickness=line_thickness)) # XY plane for x_point in x_coords: # Draw a line across each x coord, along the same z-axis, from min to max y coord xy_lines.append( create_line(vector(x_point, min_y_coord, z_origin), vector(x_point, max_y_coord, z_origin), self.__scene, thickness=line_thickness)) for y_point in y_coords: # Draw a line across each y coord, along the same z-axis, from min to max x coord xy_lines.append( create_line(vector(min_x_coord, y_point, z_origin), vector(max_x_coord, y_point, z_origin), self.__scene, thickness=line_thickness)) # YZ plane for y_point in y_coords: # Draw a line across each y coord, along the same x-axis, from min to max z coord yz_lines.append( create_line(vector(x_origin, y_point, min_z_coord), vector(x_origin, y_point, max_z_coord), self.__scene, thickness=line_thickness)) for z_point in z_coords: # Draw a line across each z coord, along the same x-axis, from min to max y coord yz_lines.append( create_line(vector(x_origin, min_y_coord, z_point), vector(x_origin, max_y_coord, z_point), self.__scene, thickness=line_thickness)) # Compound the lines together into respective objects # XY Plane if camera_axes.z < 0: xy_plane = compound(xy_lines, origin=vector(x_middle, y_middle, min_z_coord)) else: xy_plane = compound(xy_lines, origin=vector(x_middle, y_middle, max_z_coord)) # XZ Plane if camera_axes.y < 0: xz_plane = compound(xz_lines, origin=vector(x_middle, min_y_coord, z_middle)) else: xz_plane = compound(xz_lines, origin=vector(x_middle, max_y_coord, z_middle)) # YZ Plane if camera_axes.x < 0: yz_plane = compound(yz_lines, origin=vector(min_x_coord, y_middle, z_middle)) else: yz_plane = compound(yz_lines, origin=vector(max_x_coord, y_middle, z_middle)) # Combine all into one list grid = [None, None, None] grid[self.__xy_plane_idx] = xy_plane grid[self.__xz_plane_idx] = xz_plane grid[self.__yz_plane_idx] = yz_plane return grid
def create_grid(): Game.grid = [] # for curves basecolor = v.vector(0.5, 0.5, 0.5) #xbaselines = [] # return values # create xz ground floor = v.box(pos=v.vector( Game.world_size / 2, 0, Game.world_size / 2, ), size=v.vector(Game.world_size, 0.1, Game.world_size), color=v.vector(0.1, 0.1, 0.1), opacity=0.25) for z in range(0, Game.world_size + 1, Game.grid_size): Game.grid.append( v.curve(v.vector(0, 0, z), v.vector(Game.world_size, 0, z), color=basecolor)) for x in range(0, Game.world_size + 1, Game.grid_size): Game.grid.append( v.curve(v.vector(x, 0, 0), v.vector(x, 0, Game.world_size), color=basecolor)) # xy wall starty = 0 if Game.min_y >= 0 else int(Game.min_y) - 1 endy = int(Game.max_y) + 1 if Game.max_y > 0 else 0 wall1 = v.box(pos=v.vector(Game.world_size / 2, starty + (endy - starty) / 2, 0), size=v.vector(Game.world_size, (endy - starty), 0.1), color=v.vector(0.1, 0.1, 0.1), opacity=0.25) for y in range(starty, endy, Game.grid_size): Game.grid.append( v.curve(v.vector(0, y, 0), v.vector(Game.world_size, y, 0), color=basecolor)) for x in range(0, Game.world_size + 1, Game.grid_size): Game.grid.append( v.curve(v.vector(x, starty, 0), v.vector(x, endy, 0), color=basecolor)) # zy wall wall2 = v.box(pos=v.vector(0, starty + (endy - starty) / 2, Game.world_size / 2), size=v.vector(0.1, (endy - starty), Game.world_size), color=v.vector(0.1, 0.1, 0.1), opacity=0.25) for y in range(starty, endy, Game.grid_size): Game.grid.append( v.curve(v.vector(0, y, 0), v.vector(0, y, Game.world_size), color=basecolor)) for z in range(0, Game.world_size + 1, Game.grid_size): Game.grid.append( v.curve(v.vector(0, starty, z), v.vector(0, endy, z), color=basecolor)) #curves CANNOT be part of compound max_y_box = v.box(pos=v.vector(0, Game.max_y, 0), color=v.vector(1, 1, 0), size=v.vector(1, 0.1, 1)) min_y_box = v.box(pos=v.vector(0, Game.min_y, 0), color=v.vector(0, 1, 1), size=v.vector(1, 0.1, 1)) Game.worldbox = v.compound([floor, wall1, wall2, max_y_box, min_y_box], origin=v.vector(0, 0, 0))
def create_segmented_line(pos1, pos2, scene, segment_len, colour=None, thickness=0.01): """ Create a dashed line from position 1 to position 2. :param scene: The scene in which to draw the object :type scene: class:`vpython.canvas` :param pos1: 3D position of one end of the line. :type pos1: class:`vpython.vector` :param pos2: 3D position of the other end of the line. :type pos2: class:`vpython.vector` :param colour: RGB list to colour the line to :type colour: `list` :param thickness: Thickness of the line :type thickness: `float` :param segment_len: The length of the segment, and gap between segments :type segment_len: `float` :raises ValueError: RGB colour must be normalised between 0->1 :raises ValueError: Thickness must be greater than 0 :return: A box resembling a line :rtype: class:`vpython.box` """ # Set default colour # Stops a warning about mutable parameter if colour is None: colour = [0, 0, 0] if colour[0] > 1.0 or colour[1] > 1.0 or colour[2] > 1.0 or \ colour[0] < 0.0 or colour[1] < 0.0 or colour[2] < 0.0: raise ValueError("RGB values must be normalised between 0 and 1") if thickness < 0.0: raise ValueError("Thickness must be greater than 0") # Length of the line using the magnitude line_len = mag(pos2 - pos1) # Axis direction of the line (to align the box (line) to intersect the two points) axis_dir = pos2 - pos1 axis_dir.mag = 1.0 # Return a compound of boxes of thin width and height to resemble a dashed line dash_positions = [] boxes = [] pos1 = pos1 + ( axis_dir * segment_len / 2 ) # Translate centre pos to centre of where dashes will originate from # Range = number of dashes (vis and invis) for idx in range(0, int(ceil(line_len / (segment_len / axis_dir.mag)))): # Add every even point (zeroth, second...) to skip gaps between boxes if idx % 2 == 0: dash_positions.append(pos1) pos1 = (pos1 + axis_dir * segment_len) # If the axis between points changes, then the line has surpassed the end point. The line is done check_dir = pos2 - pos1 check_dir.mag = 1.0 if not vectors_approx_equal(axis_dir, check_dir): break for xyz in dash_positions: length = segment_len # If the box will surpass the end point len_to_end = (pos2 - xyz).mag if len_to_end < segment_len / 2: # Length is equal to dist to the end * 2 (as pos is middle of box) length = len_to_end * 2 boxes.append( box(canvas=scene, pos=xyz, axis=axis_dir, length=length, width=thickness, height=thickness, color=vector(colour[0], colour[1], colour[2]))) return compound(boxes)
def __create_grid_objects(self): """ Draw a grid along each 3D plane, that is closest to the camera. :return: List of the three drawn axes. :rtype: `list` """ # Initial conditions xz_lines = [] xy_lines = [] yz_lines = [] camera_axes = self.camera_axes # Locate centre of axes if self.__relative_cam: x_origin, y_origin, z_origin = round(self.__scene.center.x),\ round(self.__scene.center.y),\ round(self.__scene.center.z) self.__focal_point = [x_origin, y_origin, z_origin] else: x_origin, y_origin, z_origin = self.__focal_point[0], \ self.__focal_point[1], \ self.__focal_point[2] # CAMERA AXES | DISPLAYED GRID | XZ PLANE | XY PLANE | YZ PLANE # x,y,z | x,y,z | x,z | x,y | y,z # -------------+-----------------+----------+----------+---------- # -,-,- | +,+,+ | +,+ | +,+ | +,+ # -,-,+ | +,+,- | +,- | +,+ | +,- # -,+,- | +,-,+ | +,+ | +,- | -,+ # -,+,+ | +,-,- | +,- | +,- | -,- # +,-,- | -,+,+ | -,+ | -,+ | +,+ # +,-,+ | -,+,- | -,- | -,+ | +,- # +,+,- | -,-,+ | -,+ | -,- | -,+ # +,+,+ | -,-,- | -,- | -,- | -,- # min = -num_squares or 0, around the default position # max = +num_squares or 0, around the default position # e.g. at the origin, for negative axes: -10 -> 0, positive axes: 0 -> 10 min_x_coord = x_origin + int(-(self.__num_squares / 2) + (sign(camera_axes.x) * -1) * (self.__num_squares / 2)) max_x_coord = x_origin + int((self.__num_squares / 2) + (sign(camera_axes.x) * -1) * (self.__num_squares / 2)) min_y_coord = y_origin + int(-(self.__num_squares / 2) + (sign(camera_axes.y) * -1) * (self.__num_squares / 2)) max_y_coord = y_origin + int((self.__num_squares / 2) + (sign(camera_axes.y) * -1) * (self.__num_squares / 2)) min_z_coord = z_origin + int(-(self.__num_squares / 2) + (sign(camera_axes.z) * -1) * (self.__num_squares / 2)) max_z_coord = z_origin + int((self.__num_squares / 2) + (sign(camera_axes.z) * -1) * (self.__num_squares / 2)) # XZ plane for x_point in range(min_x_coord, max_x_coord + 1): # Draw a line across for each x coord, along the same y-axis, from min to max z coord xz_lines.append( create_line(vector(x_point, y_origin, min_z_coord), vector(x_point, y_origin, max_z_coord), self.__scene)) for z_point in range(min_z_coord, max_z_coord + 1): # Draw a line across each z coord, along the same y-axis, from min to max z coord xz_lines.append( create_line(vector(min_x_coord, y_origin, z_point), vector(max_x_coord, y_origin, z_point), self.__scene)) # XY plane for x_point in range(min_x_coord, max_x_coord + 1): # Draw a line across each x coord, along the same z-axis, from min to max y coord xy_lines.append( create_line(vector(x_point, min_y_coord, z_origin), vector(x_point, max_y_coord, z_origin), self.__scene)) for y_point in range(min_y_coord, max_y_coord + 1): # Draw a line across each y coord, along the same z-axis, from min to max x coord xy_lines.append( create_line(vector(min_x_coord, y_point, z_origin), vector(max_x_coord, y_point, z_origin), self.__scene)) # YZ plane for y_point in range(min_y_coord, max_y_coord + 1): # Draw a line across each y coord, along the same x-axis, from min to max z coord yz_lines.append( create_line(vector(x_origin, y_point, min_z_coord), vector(x_origin, y_point, max_z_coord), self.__scene)) for z_point in range(min_z_coord, max_z_coord + 1): # Draw a line across each z coord, along the same x-axis, from min to max y coord yz_lines.append( create_line(vector(x_origin, min_y_coord, z_point), vector(x_origin, max_y_coord, z_point), self.__scene)) # Compound the lines together into respective objects xz_plane = compound(xz_lines) xy_plane = compound(xy_lines) yz_plane = compound(yz_lines) # Combine all into one list grid = [None, None, None] grid[self.__xy_plane_idx] = xy_plane grid[self.__xz_plane_idx] = xz_plane grid[self.__yz_plane_idx] = yz_plane return grid
def __init__(self, corners, faces, color=vpy.vec(.3, .3, .8), opacity=1, show_faces=True, show_edges=True, show_corners=True, sort_faces=True, edge_color=vpy.vec(0, 0, 0), corner_color=None, edge_radius=0.025, corner_radius=None, pos="auto", debug=False): """ inputs: ------- corners - (list) of vpython.vectors- list of 3d coordinates of the corners of the polyhedron faces - (list) of lists - lists of corner indices. one sub-list per face of the polyhedron color - (vpython.vector) - color vector for all faces of the polyhedron opacity - (float) in [0,1] - opacity of all faces. 0 is invisible, 1 is opaque show_faces - (bool) - whether or not to draw the faces show_edges - (bool) - whether or not to draw the edges show_corners - (bool) - whether or not to draw the corners sort_faces - (bool) - whether or not to sort the face vertices before drawing the faces and edges. This should only be set to False if the faces are already sorted to save computation time. Otherwise not sorting can lead to major bugs when using the polyhedra later. edge_color - (vpython.vector) - color of all edges corner_color - (vpython.vector) or None - color of all corners defaults to edge_color examples: --------- # create a small pyramid >>> corners = [(0,0,0), (1,0,0), (0,1,0), (0,0,1)] >>> corners = [vpy.vec(*corner) for corner in corners] >>> faces = [[0,1,2], [1,2,3], [0,1,3], [0,2,3]] >>> poly = polyhedron(corners, faces) # intersection of two polyhedra P and Q: >>> R = P & Q """ self.debug = debug self.eps = 1e-10 # rounding accuracy to counteract floating point errors self.visible = False self.color = color self.edge_color = edge_color self.corner_color = corner_color if corner_color != None else edge_color self.edge_radius = edge_radius self.corner_radius = corner_radius if corner_radius != None else edge_radius self.vertices = [ vpy.vertex(pos=corner, color=self.color, opacity=opacity) for corner in corners ] self._objects = self.vertices[:] # self.vertices = [vpy.vertex(pos=corner, color=corner, opacity=opacity) for corner in corners] if isinstance(pos, vpy.vector): self.pos = pos else: self.pos = get_com(self.vertices) self.faces = faces self.face_centers = self._get_face_centers() if sort_faces: # sort the faces so that the polygons are displayed without holes self._sort_faces() if show_corners: self.draw_corners() self.visible = True if show_edges: self.draw_all_edges() self.visible = True if show_faces: self.draw_faces() self.visible = True # if self.debug: # self.show_faces() if len(self._objects) > 0 and show_faces: # if debug: # print(f"number of vertices:", sum(isinstance(obj, vpy.vertex)for obj in self._objects)) # print(f"number of corners:", sum(isinstance(obj, vpy.sphere)for obj in self._objects)) # print(f"number of edges:", sum(isinstance(obj, vpy.cylinder)for obj in self._objects)) # print(f"number of triangles:", sum(isinstance(obj, vpy.triangle)for obj in self._objects)) self.obj = vpy.compound( self._objects[len(self.vertices):], origin=self.pos, color=self.color) #, pos=self.pos, color=self.color while self._objects != []: # if not isinstance(self._objects[0], vpy.vertex): self._objects[0].visible = False del (self._objects[0])
def initRender(self): self.canvas = canvas(width=1200, height=900, title='Car-3D') # self.canvas = canvas(width=1660, height=1010, title='Car-3D') ground_y = 0.17 thk = 0.1 ground_width = 10.0 wallB = box(canvas=self.canvas, pos=vector(0, -ground_y, 0), size=vector(ground_width, thk, ground_width), color=color.blue) # vector:(front/rear,z,l/r) car_w, car_l, car_h = 0.132, 0.26 / 0.6, 0.2 wheel_r, wheel_h = 0.095, 0.035 chassis = box(canvas=self.canvas, pos=vector(0, wheel_r + 0.05, 0), size=vector(car_l, car_h, car_w), color=color.green) wheel_r / 2 + thk wheel1 = cylinder(pos=vector(0.6 * car_l / 2.0, wheel_r, car_w / 2.0), axis=vector(0, 0, wheel_h), radius=wheel_r, color=color.blue) wheel2 = cylinder(pos=vector(0.6 * car_l / 2.0, wheel_r, -car_w / 2.0 - wheel_h), axis=vector(0, 0, wheel_h), radius=wheel_r, color=color.blue) wheel3 = cylinder(pos=vector(-0.6 * car_l / 2.0, wheel_r, car_w / 2.0), axis=vector(0, 0, wheel_h), radius=wheel_r, color=color.blue) wheel4 = cylinder(pos=vector(-0.6 * car_l / 2.0, wheel_r, -car_w / 2.0 - wheel_h), axis=vector(0, 0, wheel_h), radius=wheel_r, color=color.blue) self.car = compound([chassis, wheel3, wheel4], pos=vector(self.x_pos, self.y_pos, self.z_pos), make_trail=True, retain=300) self.frontwheel = compound([wheel1, wheel2], pos=vector(self.x_pos, self.y_pos, self.z_pos)) self.car.axis = vector(1, 0, 0) self.car.up = vector(0, 1, 0) self.car.mass = self.car_mass self.pointer = arrow(pos=self.car.pos, axis=self.car.axis, shaftwidth=0.01) origin = sphere(pos=vector(0, 0, 0), radius=0.02) self.x_axis = arrow(pos=vector(0, 0, 0), axis=vector(0, 0, 3), shaftwidth=0.3, color=color.red) self.y_axis = arrow(pos=vector(0, 0, 0), axis=vector(3, 0, 0), shaftwidth=0.3, color=color.blue) self.z_axis = arrow(pos=vector(0, 0, 0), axis=vector(0, 3, 0), shaftwidth=0.3, color=color.green) rate(FPS)
axis=10 * ux, shaftwidth=0.1, color=vp.vector(1, 0, 0)) axe_y_s = vp.arrow(pos=vp.vector(10, 10, 10), axis=10 * uy, shaftwidth=0.1, color=vp.vector(0, 1, 0)) axe_z_s = vp.arrow(pos=vp.vector(10, 10, 10), axis=10 * uz, shaftwidth=0.1, color=vp.vector(0, 0, 1)) sugarbox = vp.box(pos=vp.vector(10, 10, 10), size=vp.vector(lx, ly, lz), axis=vp.vector(0, 0, 0), up=uy) satellite = vp.compound([axe_x_s, axe_y_s, axe_z_s, sugarbox]) # vecteur champ B b_vector = vp.arrow(pos=vp.vector(-5, -5, -5), axis=10 * vp.vector(B[0][0], B[1][0], B[2][0]), shaftwidth=0.1, color=vp.vector(1, 1, 1)) #################### # Fonctions utiles # #################### def plotAttitude(): for i in range(4): plt.plot([dt * i / orbite.getPeriod() for i in range(len(qs))], [q.vec()[i, 0] for q in qs]) plt.xlabel("t (orbits)")