unprojectOnTbSurface method

dynamic unprojectOnTbSurface(
  1. dynamic camera,
  2. dynamic cursorX,
  3. dynamic cursorY,
  4. dynamic canvas,
  5. dynamic tbRadius,
)
  • Unproject the cursor on the trackball surface
    • @param {Camera} camera The virtual camera
      • @param {Number} cursorX Cursor horizontal coordinate on screen
      • @param {Number} cursorY Cursor vertical coordinate on screen
      • @param {HTMLElement} canvas The canvas where the renderer draws its output
      • @param {number} tbRadius The trackball radius
      • @returns {Vector3} The unprojected point on the trackball surface

Implementation

unprojectOnTbSurface(camera, cursorX, cursorY, canvas, tbRadius) {
  if (camera.type == 'OrthographicCamera') {
    this._v2_1.copy(this.getCursorPosition(cursorX, cursorY, canvas));
    this._v3_1.set(this._v2_1.x, this._v2_1.y, 0);

    var x2 = Math.pow(this._v2_1.x, 2);
    var y2 = Math.pow(this._v2_1.y, 2);
    var r2 = Math.pow(this._tbRadius, 2);

    if (x2 + y2 <= r2 * 0.5) {
      //intersection with sphere
      this._v3_1.setZ(Math.sqrt(r2 - (x2 + y2)));
    } else {
      //intersection with hyperboloid
      this._v3_1.setZ((r2 * 0.5) / (Math.sqrt(x2 + y2)));
    }

    return this._v3_1;
  } else if (camera.type == 'PerspectiveCamera') {
    //unproject cursor on the near plane
    this._v2_1.copy(this.getCursorNDC(cursorX, cursorY, canvas));

    this._v3_1.set(this._v2_1.x, this._v2_1.y, -1);
    this._v3_1.applyMatrix4(camera.projectionMatrixInverse);

    var rayDir = this._v3_1.clone().normalize(); //unprojected ray direction
    var cameraGizmoDistance =
        camera.position.distanceTo(this._gizmos.position);
    var radius2 = Math.pow(tbRadius, 2);

    //	  camera
    //		|\
    //		| \
    //		|  \
    //	h	|	\
    //		| 	 \
    //		| 	  \
    //	_ _ | _ _ _\ _ _  near plane
    //			l

    var h = this._v3_1.z;
    var l = Math.sqrt(Math.pow(this._v3_1.x, 2) + Math.pow(this._v3_1.y, 2));

    if (l == 0) {
      //ray aligned with camera
      rayDir.set(this._v3_1.x, this._v3_1.y, tbRadius);
      return rayDir;
    }

    var m = h / l;
    var q = cameraGizmoDistance;

    /*
			 * calculate intersection point between unprojected ray and trackball surface
			 *|y = m * x + q
			 *|x^2 + y^2 = r^2
			 *
			 * (m^2 + 1) * x^2 + (2 * m * q) * x + q^2 - r^2 = 0
			 */
    var a = Math.pow(m, 2) + 1;
    var b = 2 * m * q;
    var c = Math.pow(q, 2) - radius2;
    var delta = Math.pow(b, 2) - (4 * a * c);

    if (delta >= 0) {
      //intersection with sphere
      this._v2_1.setX((-b - Math.sqrt(delta)) / (2 * a));
      this._v2_1.setY(m * this._v2_1.x + q);

      var angle = MathUtils.RAD2DEG * this._v2_1.angle();

      if (angle >= 45) {
        //if angle between intersection point and X' axis is >= 45°, return that point
        //otherwise, calculate intersection point with hyperboloid

        var rayLength = Math.sqrt(Math.pow(this._v2_1.x, 2) +
            Math.pow((cameraGizmoDistance - this._v2_1.y), 2));
        rayDir.multiplyScalar(rayLength);
        rayDir.z += cameraGizmoDistance;
        return rayDir;
      }
    }

    //intersection with hyperboloid
    /*
			 *|y = m * x + q
			 *|y = (1 / x) * (r^2 / 2)
			 *
			 * m * x^2 + q * x - r^2 / 2 = 0
			 */

    a = m;
    b = q;
    c = -radius2 * 0.5;
    delta = Math.pow(b, 2) - (4 * a * c);
    this._v2_1.setX((-b - Math.sqrt(delta)) / (2 * a));
    this._v2_1.setY(m * this._v2_1.x + q);

    var rayLength = Math.sqrt(Math.pow(this._v2_1.x, 2) +
        Math.pow((cameraGizmoDistance - this._v2_1.y), 2));

    rayDir.multiplyScalar(rayLength);
    rayDir.z += cameraGizmoDistance;
    return rayDir;
  }
}