title: Calculating screen coordinates of horizontal edge of bounding box in three.js
body: <p>I have a three.js page that renders a set of particles (runnable demo and code at: <a href="https://dl.dropboxusercontent.com/u/31495717/cubemaker/index.html" rel="nofollow noreferrer">https://dl.dropboxusercontent.com/u/31495717/cubemaker/index.html</a>). </p>
  
  <p>Here is a snapshot of the default view, where we are basically looking into a unit cube centered at (0, 0, 0):</p>
  
  <p><img src="https://i.stack.imgur.com/WoSRU.png" alt="Default view"></p>
  
  <p>I have transparent bounding boxes centered at each particle inside this unit cube. If I modify the opacity, we can then see the boxes surrounding each particle:</p>
  
  <pre><code>var bounding_box = new THREE.Mesh(
      bounding_box_geometry, 
      new THREE.MeshLambertMaterial({ 
          opacity: 0.5, 
          transparent: true,
          alphaTest: 0.5
      })
  );
  </code></pre>
  
  <p><img src="https://i.stack.imgur.com/cY9ee.png" alt="Default view with bounding boxes"></p>
  
  <p>I use the <code>THREE.Raycaster()</code> to trace a vector from the camera to the world coordinate of the current mouse position:</p>
  
  <pre><code>var mouse_vector = new THREE.Vector3(mouse.x, mouse.y, 1).unproject(camera);
  raycaster.set(camera.position, mouse_vector.sub(camera.position).normalize());
  var bounding_box_intersections = raycaster.intersectObjects(bounding_boxes);
  </code></pre>
  
  <p>If there are any <code>bounding_box_intersections</code>, I retrieve the screen coordinates of the intersected bounding box and store it in <code>screen_object_center</code>:</p>
  
  <pre><code>var width = window.innerWidth, height = window.innerHeight;
  var widthHalf = width / 2, heightHalf = height / 2;
  var screen_object_center = new THREE.Vector3();
  var projector = new THREE.Projector();
  projector.projectVector(screen_object_center.setFromMatrixPosition(INTERSECTED.matrixWorld), camera);
  screen_object_center.x = (screen_object_center.x * widthHalf) + widthHalf;
  screen_object_center.y = -(screen_object_center.y * heightHalf) + heightHalf;
  </code></pre>
  
  <p>Once I have <code>screen_object_center</code> — the screen coordinates of the center of the intersected object or bounding box — I can place an offset-ed text label wherever the mouse hovers. </p>
  
  <p>As an example, here is a snapshot of how the text label shows up with mouse pointer hovered over the centermost particle's bounding box:</p>
  
  <p><img src="https://i.stack.imgur.com/4ORvA.png" alt="Default view with label"></p>
  
  <p>I use the <code>id_label</code> bounding box to adjust the vertical offset of the label:</p>
  
  <pre><code>id_label.style.top = (screen_object_center.y - 0.85 * (id_label_rect.height / 2)) + 'px';
  </code></pre>
  
  <p>This works okay.</p>
  
  <p>My question is how to set the <em>horizontal</em> offset of the label, relative to the position and depth of the particle. </p>
  
  <p>The complication is that deeper background particles are smaller than particles in the foreground, and so I need a smaller horizontal offset to keep the label at a constant distance away from its edge.</p>
  
  <p>My failed attempt involves calculating a <code>THREE.Vector3</code> called <code>screen_object_edge</code> that tries to get the edge of the particle's bounding box (the <code>INTERSECTED</code> object), instead of its center point. </p>
  
  <p>By retrieving the screen coordinates that correspond to the particle's edge, I am hoping to set a constant horizontal gap between the particle's edge and the label, regardless of the depth of the particle. </p>
  
  <p>I try this by copying the matrix of the <code>INTERSECTED</code> object, setting a new position that lies at the corner of the bounding box, and then projecting a vector called <code>screen_object_edge</code> that lies from that copied matrix position to the camera:</p>
  
  <pre><code>var INTERSECTED_matrix_copy = new THREE.Matrix4().copy(INTERSECTED.matrixWorld);
  INTERSECTED_matrix_copy.setPosition(new THREE.Vector3(INTERSECTED.position.x + 0.0375, INTERSECTED.position.y + 0.0375, INTERSECTED.position.z + 0.0375));
  var screen_object_edge = new THREE.Vector3();
  projector.projectVector(screen_object_edge.setFromMatrixPosition(INTERSECTED_matrix_copy), camera);
  screen_object_edge.x = (screen_object_edge.x * widthHalf) + widthHalf;
  screen_object_edge.y = -(screen_object_edge.y * heightHalf) + heightHalf;
  </code></pre>
  
  <p>Things break once I rotate the scene: the labels are set correctly in the vertical position, but they are set incorrectly in the horizontal position, in that some now overlap their particles' bounding box. </p>
  
  <p>Here is an example of the same label highlighted, as in the previous example, after rotation of the particle cloud:</p>
  
  <p><img src="https://i.stack.imgur.com/xRX4Z.png" alt="Default view, rotated and showing label"></p>
  
  <p>If things worked correctly, the label would be positioned at the same horizontal distance away from the right particle edge in the rotated scene, as in the original, non-rotated default scene. Here, the label overlaps the particle.</p>
  
  <p>My question is how to calculate <code>screen_object_edge</code> so that I can get the correct screen coordinates of the edge of the particle's bounding box, and then correctly offset the horizontal position of the text label, relative to the left or right edge of its respective particle.</p>
  
  <p><strong>EDIT</strong></p>
  
  <p>Thanks to vals for the correct answer. Here's a snippet of my modified <code>render()</code> function:</p>
  
  <pre><code>function render() {
      // ...
      var scene_right = new THREE.Vector3().crossVectors(raycaster.ray.origin, camera.up).setLength(0.0375);
      var bounding_box_intersections = raycaster.intersectObjects(bounding_boxes);
      if (bounding_box_intersections.length &gt; 0) { 
          // ...
          var INTERSECTED_matrix_copy = new THREE.Matrix4().copy(INTERSECTED.matrixWorld);
          INTERSECTED_matrix_copy.setPosition(new THREE.Vector3(INTERSECTED.position.x + scene_right.x, INTERSECTED.position.y + scene_right.y, INTERSECTED.position.z + scene_right.z));
          var screen_object_edge = new THREE.Vector3();
          projector.projectVector(screen_object_edge.setFromMatrixPosition(INTERSECTED_matrix_copy), camera);
          screen_object_edge.x = (screen_object_edge.x * widthHalf) + widthHalf;
          screen_object_edge.y = -(screen_object_edge.y * heightHalf) + heightHalf;
          // ...
          if (mouse.x &lt; 0)
              id_label.style.left = (screen_object_center.x - (screen_object_edge.x - screen_object_center.x)) + 'px';
          else {
              id_label.style.left = (screen_object_center.x + (screen_object_edge.x - screen_object_center.x) - id_label_rect.width) + 'px';
              id_label.style.textAlign = 'right';
          }
      }
  }
  </code></pre>
  
  <p>When I rotate the scene, the label position is now a constant distance from the bounding box edge, regardless of the z-depth of the bounding box:</p>
  
  <p><img src="https://i.stack.imgur.com/sJ1VW.png" alt="Revised edge vector, default position"></p>
  
  <p><img src="https://i.stack.imgur.com/E34b4.png" alt="Revised edge vector, rotated position"></p>