title: opengl select sphere with mouse
body: <p>I have a number of spheres in 3d space which the user should be able to select with a mouse click. Now I've seen some examples around using gluUnProject so I gave it a shot. So I have (please correct me every step of the way if I'm wrong because I'm not 100% sure of any part of it):</p>
  
  <pre><code>def compute_pos(x, y, z):
      '''
      Compute the 3d opengl coordinates for 3 coordinates.
      @param x,y: coordinates from canvas taken with mouse position
      @param z: coordinate for z-axis
      @return; (gl_x, gl_y, gl_z) tuple corresponding to coordinates in OpenGL context
      '''
      modelview = numpy.matrix(glGetDoublev(GL_MODELVIEW_MATRIX))
      projection = numpy.matrix(glGetDoublev(GL_PROJECTION_MATRIX))
      viewport = glGetIntegerv(GL_VIEWPORT)
  
      winX = float(x)
      winY = float(viewport[3] - float(y))
      winZ = z
      return gluUnProject(winX, winY, winZ, modelview, projection, viewport)
  </code></pre>
  
  <p>Then, having the x and y of a mouse click and the position of the center of the sphere:</p>
  
  <pre><code>def is_picking(x, y, point):
      ray_start = compute_pos(x, y, -1)
      ray_end = compute_pos(x, y, 1)
      d = _compute_2d_distance( (ray_start[0], ray_start[1]),
                             (ray_end[0], ray_end[1]), 
                             (point[0], point[1]))
      if d &gt; CUBE_SIZE:
          return False
  
      d = _compute_2d_distance( (ray_start[0], ray_start[2]),
                             (ray_end[0], ray_end[2]), 
                             (point[0], point[2]))
      if d &gt; CUBE_SIZE:
          return False
  
      d = _compute_2d_distance( (ray_start[1], ray_start[2]),
                             (ray_end[1], ray_end[2]), 
                             (point[1], point[2]))
      if d &gt; CUBE_SIZE:
          return False
      return True
  </code></pre>
  
  <p>So because my 3d geometry is not good at all, I compute two points as a ray start and end point, the go into 2d 3 times eliminating one dimension at a time and compute the distance there between my line and the center of the sphere. If any of those distances are bigger than my sphere ray the it's not clicked. I think the formula for the distance is correct but just in case:</p>
  
  <pre><code>def _compute_2d_distance(p1, p2, target):
  '''
  Compute the distance between the line defined by two points and a target point.
  @param p1: first point that defines the line
  @param p2: second point that defines the line
  @param target: the point to which distance needs to be computed
  @return: distance from point to line
  '''
      if p2[0] != p1[0]:
          if p2[1] == p1[1]:
              return abs(p2[0] - p1[0])
          a = (p2[1] - p1[1])/(p2[0] - p1[0])
          b = -1
          c = p1[1] + p1[0] * (p2[1] - p1[1]) / (p2[0] - p1[0])
          d = abs(a * target[0] + b * target[1] + c) / math.sqrt(a * a + b * b)
          return d
      if p2[0] == p1[0]:
          d = abs(p2[1] - p1[1])
          return d
      return None 
  </code></pre>
  
  <p>Now the code seems to work fine in the start position. But after you use to mouse and rotate the screen even for a little bit, nothing works as expected anymore.</p>