Beispiel #1
0
 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 = []
Beispiel #2
0
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
Beispiel #3
0
    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
Beispiel #4
0
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)
Beispiel #5
0
    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
Beispiel #6
0
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
Beispiel #7
0
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)
Beispiel #8
0
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
Beispiel #9
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
Beispiel #10
0
 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
Beispiel #12
0
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
Beispiel #13
0
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)
Beispiel #14
0
 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)
Beispiel #15
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
Beispiel #16
0
#-------------------------------------------------------------------------------
#%%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
            ]))
Beispiel #17
0
               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