"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _typeof = require("@babel/runtime/helpers/typeof");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.ellipsoidSizes = exports["default"] = void 0;
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var THREE = _interopRequireWildcard(require("three"));
var _proj = _interopRequireDefault(require("proj4"));
var _Coordinates = _interopRequireDefault(require("../Geographic/Coordinates"));
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
var ellipsoidSizes = new THREE.Vector3(_proj["default"].WGS84.a, _proj["default"].WGS84.a, _proj["default"].WGS84.b);
exports.ellipsoidSizes = ellipsoidSizes;
var normal = new THREE.Vector3();
var Ellipsoid = /*#__PURE__*/function () {
  function Ellipsoid() {
    var size = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ellipsoidSizes;
    (0, _classCallCheck2["default"])(this, Ellipsoid);
    this.size = new THREE.Vector3();
    this._radiiSquared = new THREE.Vector3();
    this._invRadiiSquared = new THREE.Vector3();
    this.eccentricity = 0;
    this.setSize(size);
  }
  (0, _createClass2["default"])(Ellipsoid, [{
    key: "geodeticSurfaceNormal",
    value: function geodeticSurfaceNormal(cartesian) {
      var target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new THREE.Vector3();
      return cartesian.toVector3(target).multiply(this._invRadiiSquared).normalize();
    }
  }, {
    key: "geodeticSurfaceNormalCartographic",
    value: function geodeticSurfaceNormalCartographic(coordCarto) {
      var target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new THREE.Vector3();
      var longitude = THREE.MathUtils.degToRad(coordCarto.longitude);
      var latitude = THREE.MathUtils.degToRad(coordCarto.latitude);
      var cosLatitude = Math.cos(latitude);
      return target.set(cosLatitude * Math.cos(longitude), cosLatitude * Math.sin(longitude), Math.sin(latitude));
    }
  }, {
    key: "setSize",
    value: function setSize(size) {
      this.size.set(size.x, size.y, size.z);
      this._radiiSquared.multiplyVectors(size, size);
      this._invRadiiSquared.x = size.x == 0 ? 0 : 1 / this._radiiSquared.x;
      this._invRadiiSquared.y = size.y == 0 ? 0 : 1 / this._radiiSquared.y;
      this._invRadiiSquared.z = size.z == 0 ? 0 : 1 / this._radiiSquared.z;
      this.eccentricity = Math.sqrt(this._radiiSquared.x - this._radiiSquared.z) / this.size.x;
    }
  }, {
    key: "cartographicToCartesian",
    value: function cartographicToCartesian(coordCarto) {
      var target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new THREE.Vector3();
      normal.copy(coordCarto.geodesicNormal);
      target.multiplyVectors(this._radiiSquared, normal);
      var gamma = Math.sqrt(normal.dot(target));
      target.divideScalar(gamma);
      normal.multiplyScalar(coordCarto.altitude);
      return target.add(normal);
    }

    /**
     * Convert cartesian coordinates to geographic according to the current ellipsoid of revolution.
     * @param {Object} position - The coordinate to convert
     * @param {number} position.x
     * @param {number} position.y
     * @param {number} position.z
     * @param {Coordinate} [target] coordinate to copy result
     * @returns {Coordinate} an object describing the coordinates on the reference ellipsoid, angles are in degree
     */
  }, {
    key: "cartesianToCartographic",
    value: function cartesianToCartographic(position) {
      var target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new _Coordinates["default"]('EPSG:4326', 0, 0, 0);
      // for details, see for example http://www.linz.govt.nz/data/geodetic-system/coordinate-conversion/geodetic-datum-conversions/equations-used-datum
      // TODO the following is only valable for oblate ellipsoid of revolution. do we want to support triaxial ellipsoid?
      var R = Math.sqrt(position.x * position.x + position.y * position.y + position.z * position.z);
      var a = this.size.x; // x
      var b = this.size.z; // z
      var e = Math.abs((a * a - b * b) / (a * a));
      var f = 1 - Math.sqrt(1 - e);
      var rsqXY = Math.sqrt(position.x * position.x + position.y * position.y);
      var theta = Math.atan2(position.y, position.x);
      var nu = Math.atan(position.z / rsqXY * (1 - f + e * a / R));
      var sinu = Math.sin(nu);
      var cosu = Math.cos(nu);
      var phi = Math.atan((position.z * (1 - f) + e * a * sinu * sinu * sinu) / ((1 - f) * (rsqXY - e * a * cosu * cosu * cosu)));
      var h = rsqXY * Math.cos(phi) + position.z * Math.sin(phi) - a * Math.sqrt(1 - e * Math.sin(phi) * Math.sin(phi));
      return target.setFromValues(THREE.MathUtils.radToDeg(theta), THREE.MathUtils.radToDeg(phi), h);
    }
  }, {
    key: "cartographicToCartesianArray",
    value: function cartographicToCartesianArray(coordCartoArray) {
      var cartesianArray = [];
      for (var i = 0; i < coordCartoArray.length; i++) {
        cartesianArray.push(this.cartographicToCartesian(coordCartoArray[i]));
      }
      return cartesianArray;
    }
  }, {
    key: "intersection",
    value: function intersection(ray) {
      var EPSILON = 0.0001;
      var O_C = ray.origin;
      var dir = ray.direction;
      // normalizeVector( dir );

      var a = dir.x * dir.x * this._invRadiiSquared.x + dir.y * dir.y * this._invRadiiSquared.y + dir.z * dir.z * this._invRadiiSquared.z;
      var b = 2 * O_C.x * dir.x * this._invRadiiSquared.x + 2 * O_C.y * dir.y * this._invRadiiSquared.y + 2 * O_C.z * dir.z * this._invRadiiSquared.z;
      var c = O_C.x * O_C.x * this._invRadiiSquared.x + O_C.y * O_C.y * this._invRadiiSquared.y + O_C.z * O_C.z * this._invRadiiSquared.z - 1;
      var d = b * b - 4 * a * c;
      if (d < 0 || a === 0 || b === 0 || c === 0) {
        return false;
      }
      d = Math.sqrt(d);
      var t1 = (-b + d) / (2 * a);
      var t2 = (-b - d) / (2 * a);
      if (t1 <= EPSILON && t2 <= EPSILON) {
        return false;
      } // both intersections are behind the ray origin
      // var back = (t1 <= EPSILON || t2 <= EPSILON); // If only one intersection (t>0) then we are inside the ellipsoid and the intersection is at the back of the ellipsoid
      var t = 0;
      if (t1 <= EPSILON) {
        t = t2;
      } else if (t2 <= EPSILON) {
        t = t1;
      } else {
        t = t1 < t2 ? t1 : t2;
      }
      if (t < EPSILON) {
        return false;
      } // Too close to intersection

      var inter = new THREE.Vector3();
      inter.addVectors(ray.origin, dir.clone().setLength(t));
      return inter;
    }
  }, {
    key: "computeDistance",
    value: function computeDistance(coordCarto1, coordCarto2) {
      console.warn('computeDistance is renamed to geodesicDistance');
      this.geodesicDistance(coordCarto1, coordCarto2);
    }

    /**
     * Calculate the geodesic distance, between coordCarto1 and coordCarto2.
     * It's most short distance on ellipsoid surface between coordCarto1 and coordCarto2.
     * It's called orthodromy.
     *
     * @param      {Coordinates}  coordCarto1  The coordinate carto 1
     * @param      {Coordinates}  coordCarto2  The coordinate carto 2
     * @return     {number}  The orthodromic distance between the two given coordinates.
     */
  }, {
    key: "geodesicDistance",
    value: function geodesicDistance(coordCarto1, coordCarto2) {
      // The formula uses the distance on approximated sphere,
      // with the nearest local radius of curvature of the ellipsoid
      // https://geodesie.ign.fr/contenu/fichiers/Distance_longitude_latitude.pdf
      var longitude1 = THREE.MathUtils.degToRad(coordCarto1.longitude);
      var latitude1 = THREE.MathUtils.degToRad(coordCarto1.latitude);
      var longitude2 = THREE.MathUtils.degToRad(coordCarto2.longitude);
      var latitude2 = THREE.MathUtils.degToRad(coordCarto2.latitude);
      var distRad = Math.acos(Math.sin(latitude1) * Math.sin(latitude2) + Math.cos(latitude1) * Math.cos(latitude2) * Math.cos(longitude2 - longitude1));
      var e = this.eccentricity;
      var es = Math.pow(e * Math.sin((latitude1 + latitude2) * 0.5), 2);
      var rho = this.size.x * (1 - Math.pow(e, 2)) / Math.pow(1 - es, 3 / 2);
      var N = this.size.x / Math.sqrt(1 - es);
      return distRad * Math.sqrt(rho * N);
    }
  }]);
  return Ellipsoid;
}();
var _default = Ellipsoid;
exports["default"] = _default;