def endPolygon(self): """Only works for convex polygons in a single plane. This is just simple fan triangulation """ self.polygon = False for i in range(2, len(self.listOfVectors)): triangle(vs=[ vertex(pos=self.listOfVectors[0], color=self.currentColor), vertex(pos=self.listOfVectors[i - 1], color=self.currentColor), vertex(pos=self.listOfVectors[i], color=self.currentColor) ]) self.listOfVectors = []
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 __init__(self, poly, center=[0, 0, 0]): self.poly = poly self.nfaces = len(self.poly.faces) colors = [vp.vector(*np.random.rand(3)) for i in range(self.nfaces)] self.vcenter = vp.vector(*center) self.vcolors = colors center = sum([v for v in self.poly.vertices]) / len(self.poly.vertices) vertices = [vertex - center for vertex in self.poly.vertices] vpoints = [vp.sphere(pos=self.vcenter+vp.vector(*v),\ radius=0.02, emissive=True, opacity=0.6)\ for v in vertices] vfaces = [] vnormals = [] vedges = [] for f, face in enumerate(self.poly.faces): vtriangles = [] for tri in itertools.combinations(face.vertices, 3): triangle = vp.triangle(vs=[vp.vertex(pos=self.vcenter+vp.vector(*vertices[v]),\ color=self.vcolors[f],\ opacity=0.5,\ emissive=True,\ shininess=0.5) for v in tri]) vtriangles.append(triangle) vfaces.append(vtriangles) face_center = sum([vertices[vertex] for vertex in face.vertices ]) / len(face.vertices) vnormals.append(vp.arrow(pos=self.vcenter+vp.vector(*face_center), color=self.vcolors[f],\ opacity=0.5,\ axis=vp.vector(*face.unormal)/3)) self.vpoints, self.vfaces, self.vnormals = vpoints, vfaces, vnormals
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 update(self, poly): self.poly = poly center = sum([v for v in self.poly.vertices]) / len(self.poly.vertices) vertices = [vertex - center for vertex in self.poly.vertices] for i, v in enumerate(vertices): self.vpoints[i].pos = self.vcenter + vp.vector(*v) for vface in self.vfaces: for vtri in vface: vtri.visible = False self.vfaces = [] for f, face in enumerate(self.poly.faces): vtriangles = [] for tri in itertools.combinations(face.vertices, 3): triangle = vp.triangle(vs=[vp.vertex(pos=self.vcenter+vp.vector(*vertices[v]),\ color=self.vcolors[f],\ opacity=0.5,\ emissive=True,\ shininess=0.5) for v in tri]) vtriangles.append(triangle) self.vfaces.append(vtriangles) face_center = sum([vertices[vertex] for vertex in face.vertices ]) / len(face.vertices) self.vnormals[f].pos = self.vcenter + vp.vector(*face_center) self.vnormals[f].axis = vp.vector(*face.unormal) / 3
def face(center, rpos): n = rpos.shape[0] # normalize relative vectors normalized = np.zeros_like(rpos) for i in range(n): normalized[i] = rpos[i] / np.linalg.norm(rpos[i]) #normal for each triangle normals = np.zeros_like(rpos) for i in range(n): normals[i] = np.cross(normalized[i-1], normalized[i]) # central normal c_normal = np.sum(normals, axis=0) c_normal /= np.linalg.norm(c_normal) if np.dot(c_normal, sun) < 0.0: c_normal = - c_normal normals = - normals hue, sat = hue_sat[n] bri = 1 r,g,b = colorsys.hsv_to_rgb(hue/360., sat, bri) pos = rpos + center v_center = vp.vertex( pos=vp.vector(*center), normal=vp.vector(*c_normal), color=vp.vector(r,g,b)) vertices = [vp.vertex( pos=vp.vector(*p), normal=vp.vector(*(normals[i])), color=vp.vector(r,g,b) ) for i,p in enumerate(pos)] faces = set() for i in range(n): faces.add(vp.triangle(v0=vertices[i-1], v1=vertices[i], v2=v_center, )) # group.add(sw.shapes.Polygon(pos, fill=rgb, stroke_linejoin="round", fill_opacity="1.0", stroke="#444", stroke_width=3)) return faces
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 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 _draw_face(self, vertices, face_indices): """ draw a single face of the polyhedron using vpython triangles face_indices must be sorted either clockwise or counterclockwise """ for i in range(1, len(face_indices) - 1): obj = vpy.triangle(v0=vertices[face_indices[0]], v1=vertices[face_indices[i]], v2=vertices[face_indices[i + 1]]) self._objects.append(obj)
def create_flag(particles): flag = [[Seam() for y in range(NUM_COLUMNS)] for x in range(NUM_ROWS)] for r in range(NUM_ROWS): for c in range(NUM_COLUMNS): seam = flag[r][c] if r + 1 < NUM_ROWS and c + 1 < NUM_COLUMNS: if r >= NUM_ROWS // 2: color = vp.color.red else: color = vp.color.white seam.tr1 = vp.triangle( v0=vp.vertex(pos=particles[r][c].pos, color=color), v1=vp.vertex(pos=particles[r][c + 1].pos, color=color), v2=vp.vertex(pos=particles[r + 1][c + 1].pos, color=color)) seam.tr2 = vp.triangle( v0=vp.vertex(pos=particles[r][c].pos, color=color), v1=vp.vertex(pos=particles[r + 1][c + 1].pos, color=color), v2=vp.vertex(pos=particles[r + 1][c].pos, color=color)) return flag
def face(center, rpos): n = rpos.shape[0] # normalize relative vectors normalized = np.zeros_like(rpos) for i in range(n): normalized[i] = rpos[i] / np.linalg.norm(rpos[i]) #normal for each triangle normals = np.zeros_like(rpos) for i in range(n): normals[i] = np.cross(normalized[i - 1], normalized[i]) # central normal c_normal = np.sum(normals, axis=0) c_normal /= np.linalg.norm(c_normal) if np.dot(c_normal, sun) < 0.0: c_normal = -c_normal normals = -normals hue, sat = hue_sat[n] bri = 1 r, g, b = colorsys.hsv_to_rgb(hue / 360., sat, bri) pos = rpos + center v_center = vp.vertex(pos=vp.vector(*center), normal=vp.vector(*c_normal), color=vp.vector(r, g, b)) vertices = [ vp.vertex(pos=vp.vector(*p), normal=vp.vector(*(normals[i])), color=vp.vector(r, g, b)) for i, p in enumerate(pos) ] faces = set() for i in range(n): faces.add( vp.triangle( v0=vertices[i - 1], v1=vertices[i], v2=v_center, )) # group.add(sw.shapes.Polygon(pos, fill=rgb, stroke_linejoin="round", fill_opacity="1.0", stroke="#444", stroke_width=3)) return faces
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 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_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
#------------------------------------------------------------------------------- #%%DRAWING scene = canvas(width=900, height=700, center=vector(5, 5, 0)) turquoise = color.hsv_to_rgb(vector(0.5, 1, 0.8)) red = color.red white = color.white colors = [turquoise, red, white] Figures = [] #Central sphere Figures.append(sphere(pos=vector(*cord_c), radius=r, opacity=0.8)) vec_vertices = [vector(*ver) for ver in vor.vertices] for n, ver in enumerate(vec_vertices): Figures.append(sphere(pos=ver, color=white, radius=0.1)) #Drawing Voronoi Cropped Region for n, reg in enumerate(crop_reg): # region = [vector(*vor.vertices[ver]) for ver in reg] # drawPoints(region, color = colors[region_id[n]]) #Overdrawing of points conv_hull = ConvexHull([vor.vertices[ver] for ver in reg]) for sim in conv_hull.simplices: pts = [conv_hull.points[pt] for pt in sim] Figures.append( triangle(vs=[ vertex( pos=vector(*ver), color=colors[region_id[n]], opacity=0.05) for ver in pts ]))
radius=max_coord / 50, color=yellow, opacity=0.9)) # for pt in intersection_point: # Figures.append(sphere(pos = vector(*pt), # radius = max_coord/70, # color = turquoise, # opacity = 0.5)) simpl = [] for sim in intersectiong_triang.simplices: pts = [intersectiong_triang.points[pt] for pt in sim] simpl.append( triangle(vs=[ vertex(pos=vector(*ver, sel_z), color=turquoise, opacity=0.9) for ver in pts ])) #drawing lines between couples for couple in couples: v1 = vector(*vor.vertices[sel_reg][couple[0]]) v2 = vector(*vor.vertices[sel_reg][couple[1]]) Figures.append( cylinder(pos=v1, axis=v2 - v1, radius=0.01, opacity=0.5, color=color.gray(0.2))) for pt in container_points: Figures.append(
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