title: I'm having trouble projecting 3D coordinates to 2D screen coordinates
body: <p>NOTE:  Because I am a new member and I apparently need 10 reputation to post images or more than 2 links, I will be referring to <a href="http://imgur.com/a/XUf8F" rel="nofollow">this imgur album</a> for visuals.  Sorry for any inconvenience.</p>
  
  <p>I'm trying to make a program to visualize 3D objects in python, but I'm having a problem with the function that projects a 3D coordinate onto 2D screen coordinates.  It works in some cases, but not all.  When the camera is relatively far away from the point and about on the same level, the projection looks very nice, such as in this image depicting a decagon and rectangle, whose vertices are defined by 3D points (Figure 2).  However, when the camera is looking down on the points, the object they depict is flattened disproportionately (Figure 3), and when the camera is close to the points it must project, the shape gets warped (Figure 4).</p>
  
  <p>The function in question is Camera.projectPoint, and I have included all of the functions and classes that it uses in the following:</p>
  
  <pre><code>class Vector3:
  
      def __init__(self, x, y, z):
          self.x = x
          self.y = y
          self.z = z
          self.mag = math.sqrt(x**2+y**2+z**2)
          self.yaw = math.atan2(self.y, self.x)
          self.pitch = math.atan2(self.z, math.sqrt(self.x**2+self.y**2))
  
  class Point:
  
      def __init__(self, x, y, z):
          self.x = x
          self.y = y
          self.z = z
  
      def vectorTo(self, p):
          return(Vector3(p.x-self.x, p.y-self.y, p.z-self.z))
  
  class Camera:
  
      def __init__(self, pos, yaw, pitch, FOV=math.pi/2):
          self.pos = pos
          self.yaw = yaw
          self.pitch = pitch
          self.FOV = FOV
  
      def projectPoint(self, point):
          ## finding the vector from the camera position to the point
          v = self.pos.vectorTo(point)
          ## setting alpha to the difference in yaw between the camera and the vector to the point
          ## and beta to the difference in pitch
          alpha = v.yaw - self.yaw
          beta = v.pitch - self.pitch
          ## making sure that the smallest angle between the two is chosen
          ## (difference between 300 degrees and 10 degrees is 70, not 290)
          alpha = (alpha+math.pi)%(2*math.pi)-math.pi
          beta = (beta+math.pi)%(2*math.pi)-math.pi
          ## Doing the operation pictured in the diagram
          h = math.sin(self.FOV/2)/math.cos(self.FOV/2)
          x1 = math.sin(alpha)/math.cos(alpha)
          y1 = math.sin(beta)/math.cos(beta)
          ## adjusting for screen resolution
          return(x1*1000/h+500, y1*1000/h+325)
  </code></pre>
  
  <p>I have looked around for algorithms to project a 3D coordinate onto the screen, and have found a lot of things such as what is depicted in <a href="http://imgur.com/a/XUf8F#WQTMDJZ" rel="nofollow">Figure 1</a> (which is what my function is based off of), but it doesn't seem to be working very well.  Is it a problem with the algorithm?  The way I implimented it?  One of the functions it uses? (I'm like 99% sure all of the other functions and classes are perfectly fine).  Any ideas on what's wrong and how to fix it?  Thank you.</p>