"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _typeof = require("@babel/runtime/helpers/typeof");
Object.defineProperty(exports, "__esModule", {
  value: true
});
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 _Ellipsoid = _interopRequireDefault(require("../Math/Ellipsoid"));
var _Coordinates = _interopRequireDefault(require("../Geographic/Coordinates"));
var _C3DTilesEnums = require("./C3DTilesEnums");
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 ellipsoid = new _Ellipsoid["default"]();

// bounding box scratch variables
var boxSize = new THREE.Vector3();
var boxCenter = new THREE.Vector3();

// Bounding region scratch variables
var southEastUpCarto = new _Coordinates["default"]('EPSG:4326');
var southEastUpVec3 = new THREE.Vector3();
var northWestBottomCarto = new _Coordinates["default"]('EPSG:4326');
var northWestBottomVec3 = new THREE.Vector3();
var radiusScratch = new THREE.Vector3();

// Culling scratch value
var worldCoordinateCenter = new THREE.Vector3();

/**
 * Bounding region is converted to a bounding sphere to simplify and speed computation and culling. This function
 * computes a sphere enclosing the bounding region.
 * @param {Object} region - the parsed json from the tile representing the region
 * @param {THREE.Matrix4} tileMatrixInverse - the inverse transformation matrix of the tile to transform the produced
 * sphere from a global to a reference local to the tile
 * @return {THREE.Sphere} a sphere enclosing the given region
 */
function initFromRegion(region, tileMatrixInverse) {
  var east = region[2];
  var west = region[0];
  var south = region[1];
  var north = region[3];
  var minHeight = region[4];
  var maxHeight = region[5];
  var eastDeg = THREE.MathUtils.radToDeg(east);
  var westDeg = THREE.MathUtils.radToDeg(west);
  var southDeg = THREE.MathUtils.radToDeg(south);
  var northDeg = THREE.MathUtils.radToDeg(north);
  northWestBottomCarto.setFromValues(westDeg, northDeg, minHeight);
  ellipsoid.cartographicToCartesian(northWestBottomCarto, northWestBottomVec3);
  southEastUpCarto.setFromValues(eastDeg, southDeg, maxHeight);
  ellipsoid.cartographicToCartesian(southEastUpCarto, southEastUpVec3);
  var regionCenter = new THREE.Vector3();
  regionCenter.lerpVectors(northWestBottomVec3, southEastUpVec3, 0.5);
  var radius = radiusScratch.subVectors(northWestBottomVec3, southEastUpVec3).length() / 2;
  var sphere = new THREE.Sphere(regionCenter, radius);
  sphere.applyMatrix4(tileMatrixInverse);
  return sphere;
}

/**
 * Create a bounding box from a json describing a box in a 3D Tiles tile.
 * @param {Object} box - the parsed json from the tile representing the box
 * @return {THREE.Box3} the bounding box of the tile
 */
function initFromBox(box) {
  // box[0], box[1], box[2] = center of the box
  // box[3], box[4], box[5] = x axis direction and half-length
  // box[6], box[7], box[8] = y axis direction and half-length
  // box[9], box[10], box[11] = z axis direction and half-length
  boxCenter.set(box[0], box[1], box[2]);
  boxSize.set(box[3], box[7], box[11]).multiplyScalar(2);
  var box3 = new THREE.Box3();
  box3.setFromCenterAndSize(boxCenter, boxSize);
  return box3;
}

/**
 * Creats a bounding sphere from a json describing a sphere in a 3D Tiles tile.
 * @param {Object} sphere - the parsed json from the tile representing the sphere
 * @returns {THREE.Sphere} the bounding sphere of the tile
 */
function initFromSphere(sphere) {
  var sphereCenter = new THREE.Vector3();
  sphereCenter.set(sphere[0], sphere[1], sphere[2]);
  return new THREE.Sphere(sphereCenter, sphere[3]);
}

/**
 * @classdesc 3D Tiles
 * [bounding volume](https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/specification/schema/boundingVolume.schema.json)
 * Used to represent bounding volumes and viewer request volumes. The input bounding volume (from the dataset) can be a
 * box, a sphere or a region. Regions are transformed to spheres internally for simplification of parsing and to speed
 * up computations such as culling.
 * @property {C3DTilesTypes} type - Used by 3D Tiles extensions
 * (e.g. {@link C3DTBatchTableHierarchyExtension}) to know in which context
 * (i.e. for which 3D Tiles class) the parsing of the extension should be done.
 * @property {String} initialVolumeType - the initial volume type to be able to dissociate spheres
 * and regions if needed since both are converted to spheres (one of {@link C3DTilesBoundingVolumeTypes})
 * @property {THREE.Box3|THREE.Sphere} volume - The 3D bounding volume created. Can be a THREE.Box3 for bounding volumes
 * of types box or a THREE.Sphere for bounding volumes of type sphere or region.
 * @property {object} extensions - 3D Tiles extensions of the bounding volume
 * stored in the following format:
 * {extensioName1: extensionObject1, extensioName2: extensionObject2, ...}
 */
var C3DTBoundingVolume = /*#__PURE__*/function () {
  function C3DTBoundingVolume(json, tileMatrixInverse, registeredExtensions) {
    (0, _classCallCheck2["default"])(this, C3DTBoundingVolume);
    this.type = _C3DTilesEnums.C3DTilesTypes.boundingVolume;
    if (json.region) {
      this.initialVolumeType = _C3DTilesEnums.C3DTilesBoundingVolumeTypes.region;
      this.volume = initFromRegion(json.region, tileMatrixInverse);
    } else if (json.box) {
      this.initialVolumeType = _C3DTilesEnums.C3DTilesBoundingVolumeTypes.box;
      this.volume = initFromBox(json.box);
    } else if (json.sphere) {
      this.initialVolumeType = _C3DTilesEnums.C3DTilesBoundingVolumeTypes.sphere;
      this.volume = initFromSphere(json.sphere);
    } else {
      throw new Error("Unknown bounding volume type: ".concat(json, ". 3D Tiles nodes must have a bounding volume of type\n            region, box or sphere."));
    }
    if (json.extensions) {
      this.extensions = registeredExtensions.parseExtensions(json.extensions, this.type);
    }
  }

  /**
   * Performs camera frustum culling on bounding volumes.
   * @param {Camera} camera - the camera to perform culling for
   * @param {THREE.Matrix4} tileMatrixWorld - the world matrix of the tile
   * @returns {boolean} true if the tile should be culled out (bounding volume not in camera frustum), false otherwise.
   */
  (0, _createClass2["default"])(C3DTBoundingVolume, [{
    key: "boundingVolumeCulling",
    value: function boundingVolumeCulling(camera, tileMatrixWorld) {
      if (this.initialVolumeType === _C3DTilesEnums.C3DTilesBoundingVolumeTypes.box) {
        return !camera.isBox3Visible(this.volume, tileMatrixWorld);
      } else if (this.initialVolumeType === _C3DTilesEnums.C3DTilesBoundingVolumeTypes.sphere || this.initialVolumeType === _C3DTilesEnums.C3DTilesBoundingVolumeTypes.region) {
        return !camera.isSphereVisible(this.volume, tileMatrixWorld);
      } else {
        throw new Error('Unknown bounding volume type.');
      }
    }

    /**
     * Checks if the camera is inside the [viewer request volumes](@link https://github.com/CesiumGS/3d-tiles/tree/main/specification#viewer-request-volume).
     * @param {Camera} camera - the camera to perform culling for
     * @param {THREE.Matrix4} tileMatrixWorld - the world matrix of the tile
     * @returns {boolean} true if the camera is outside the viewer request volume, false otherwise.
     */
  }, {
    key: "viewerRequestVolumeCulling",
    value: function viewerRequestVolumeCulling(camera, tileMatrixWorld) {
      if (this.initialVolumeType === _C3DTilesEnums.C3DTilesBoundingVolumeTypes.region) {
        console.warn('Region viewerRequestVolume not yet supported');
        return true;
      }
      if (this.initialVolumeType === _C3DTilesEnums.C3DTilesBoundingVolumeTypes.box) {
        console.warn('Bounding box viewerRequestVolume not yet supported');
        return true;
      }
      if (this.initialVolumeType === _C3DTilesEnums.C3DTilesBoundingVolumeTypes.sphere) {
        worldCoordinateCenter.copy(this.volume.center);
        worldCoordinateCenter.applyMatrix4(tileMatrixWorld);
        // To check the distance between the center sphere and the camera
        return !(camera.camera3D.position.distanceTo(worldCoordinateCenter) <= this.volume.radius);
      }
      return false;
    }
  }]);
  return C3DTBoundingVolume;
}();
var _default = C3DTBoundingVolume;
exports["default"] = _default;