"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));

var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));

var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));

var _log = _interopRequireDefault(require("../utils/log"));

var _mathUtils = require("../utils/math-utils");

var _core = require("@math.gl/core");

var _webMercator = require("@math.gl/web-mercator");

var _constants = require("../lib/constants");

var DEGREES_TO_RADIANS = Math.PI / 180;
var IDENTITY = (0, _mathUtils.createMat4)();
var ZERO_VECTOR = [0, 0, 0];
var DEFAULT_DISTANCE_SCALES = {
  unitsPerMeter: [1, 1, 1],
  metersPerUnit: [1, 1, 1]
};

function createProjectionMatrix(_ref) {
  var width = _ref.width,
      height = _ref.height,
      orthographic = _ref.orthographic,
      fovyRadians = _ref.fovyRadians,
      focalDistance = _ref.focalDistance,
      padding = _ref.padding,
      near = _ref.near,
      far = _ref.far;
  var aspect = width / height;
  var matrix = orthographic ? new _core.Matrix4().orthographic({
    fovy: fovyRadians,
    aspect: aspect,
    focalDistance: focalDistance,
    near: near,
    far: far
  }) : new _core.Matrix4().perspective({
    fovy: fovyRadians,
    aspect: aspect,
    near: near,
    far: far
  });

  if (padding) {
    var _padding$left = padding.left,
        left = _padding$left === void 0 ? 0 : _padding$left,
        _padding$right = padding.right,
        right = _padding$right === void 0 ? 0 : _padding$right,
        _padding$top = padding.top,
        top = _padding$top === void 0 ? 0 : _padding$top,
        _padding$bottom = padding.bottom,
        bottom = _padding$bottom === void 0 ? 0 : _padding$bottom;
    var offsetX = (0, _core.clamp)((left + width - right) / 2, 0, width) - width / 2;
    var offsetY = (0, _core.clamp)((top + height - bottom) / 2, 0, height) - height / 2;
    matrix[8] -= offsetX * 2 / width;
    matrix[9] += offsetY * 2 / height;
  }

  return matrix;
}

var Viewport = function () {
  function Viewport() {
    var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
    (0, _classCallCheck2.default)(this, Viewport);
    this.id = void 0;
    this.x = void 0;
    this.y = void 0;
    this.width = void 0;
    this.height = void 0;
    this.padding = void 0;
    this.isGeospatial = void 0;
    this.zoom = void 0;
    this.focalDistance = void 0;
    this.position = void 0;
    this.modelMatrix = void 0;
    this.distanceScales = void 0;
    this.scale = void 0;
    this.center = void 0;
    this.cameraPosition = void 0;
    this.projectionMatrix = void 0;
    this.viewMatrix = void 0;
    this.viewMatrixUncentered = void 0;
    this.viewMatrixInverse = void 0;
    this.viewProjectionMatrix = void 0;
    this.pixelProjectionMatrix = void 0;
    this.pixelUnprojectionMatrix = void 0;
    this.resolution = void 0;
    this._frustumPlanes = {};
    this.id = opts.id || this.constructor.displayName || 'viewport';
    this.x = opts.x || 0;
    this.y = opts.y || 0;
    this.width = opts.width || 1;
    this.height = opts.height || 1;
    this.zoom = opts.zoom || 0;
    this.padding = opts.padding;
    this.distanceScales = opts.distanceScales || DEFAULT_DISTANCE_SCALES;
    this.focalDistance = opts.focalDistance || 1;
    this.position = opts.position || ZERO_VECTOR;
    this.modelMatrix = opts.modelMatrix || null;
    var longitude = opts.longitude,
        latitude = opts.latitude;
    this.isGeospatial = Number.isFinite(latitude) && Number.isFinite(longitude);

    this._initProps(opts);

    this._initMatrices(opts);

    this.equals = this.equals.bind(this);
    this.project = this.project.bind(this);
    this.unproject = this.unproject.bind(this);
    this.projectPosition = this.projectPosition.bind(this);
    this.unprojectPosition = this.unprojectPosition.bind(this);
    this.projectFlat = this.projectFlat.bind(this);
    this.unprojectFlat = this.unprojectFlat.bind(this);
  }

  var _proto = Viewport.prototype;

  _proto.equals = function equals(viewport) {
    if (!(viewport instanceof Viewport)) {
      return false;
    }

    if (this === viewport) {
      return true;
    }

    return viewport.width === this.width && viewport.height === this.height && viewport.scale === this.scale && (0, _core.equals)(viewport.projectionMatrix, this.projectionMatrix) && (0, _core.equals)(viewport.viewMatrix, this.viewMatrix);
  };

  _proto.project = function project(xyz) {
    var _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
        _ref2$topLeft = _ref2.topLeft,
        topLeft = _ref2$topLeft === void 0 ? true : _ref2$topLeft;

    var worldPosition = this.projectPosition(xyz);
    var coord = (0, _webMercator.worldToPixels)(worldPosition, this.pixelProjectionMatrix);

    var _coord = (0, _slicedToArray2.default)(coord, 2),
        x = _coord[0],
        y = _coord[1];

    var y2 = topLeft ? y : this.height - y;
    return xyz.length === 2 ? [x, y2] : [x, y2, coord[2]];
  };

  _proto.unproject = function unproject(xyz) {
    var _ref3 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
        _ref3$topLeft = _ref3.topLeft,
        topLeft = _ref3$topLeft === void 0 ? true : _ref3$topLeft,
        targetZ = _ref3.targetZ;

    var _xyz = (0, _slicedToArray2.default)(xyz, 3),
        x = _xyz[0],
        y = _xyz[1],
        z = _xyz[2];

    var y2 = topLeft ? y : this.height - y;
    var targetZWorld = targetZ && targetZ * this.distanceScales.unitsPerMeter[2];
    var coord = (0, _webMercator.pixelsToWorld)([x, y2, z], this.pixelUnprojectionMatrix, targetZWorld);

    var _this$unprojectPositi = this.unprojectPosition(coord),
        _this$unprojectPositi2 = (0, _slicedToArray2.default)(_this$unprojectPositi, 3),
        X = _this$unprojectPositi2[0],
        Y = _this$unprojectPositi2[1],
        Z = _this$unprojectPositi2[2];

    if (Number.isFinite(z)) {
      return [X, Y, Z];
    }

    return Number.isFinite(targetZ) ? [X, Y, targetZ] : [X, Y];
  };

  _proto.projectPosition = function projectPosition(xyz) {
    var _this$projectFlat = this.projectFlat(xyz),
        _this$projectFlat2 = (0, _slicedToArray2.default)(_this$projectFlat, 2),
        X = _this$projectFlat2[0],
        Y = _this$projectFlat2[1];

    var Z = (xyz[2] || 0) * this.distanceScales.unitsPerMeter[2];
    return [X, Y, Z];
  };

  _proto.unprojectPosition = function unprojectPosition(xyz) {
    var _this$unprojectFlat = this.unprojectFlat(xyz),
        _this$unprojectFlat2 = (0, _slicedToArray2.default)(_this$unprojectFlat, 2),
        X = _this$unprojectFlat2[0],
        Y = _this$unprojectFlat2[1];

    var Z = (xyz[2] || 0) * this.distanceScales.metersPerUnit[2];
    return [X, Y, Z];
  };

  _proto.projectFlat = function projectFlat(xyz) {
    if (this.isGeospatial) {
      var result = (0, _webMercator.lngLatToWorld)(xyz);
      result[1] = (0, _core.clamp)(result[1], -318, 830);
      return result;
    }

    return xyz;
  };

  _proto.unprojectFlat = function unprojectFlat(xyz) {
    if (this.isGeospatial) {
      return (0, _webMercator.worldToLngLat)(xyz);
    }

    return xyz;
  };

  _proto.getBounds = function getBounds() {
    var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
    var unprojectOption = {
      targetZ: options.z || 0
    };
    var topLeft = this.unproject([0, 0], unprojectOption);
    var topRight = this.unproject([this.width, 0], unprojectOption);
    var bottomLeft = this.unproject([0, this.height], unprojectOption);
    var bottomRight = this.unproject([this.width, this.height], unprojectOption);
    return [Math.min(topLeft[0], topRight[0], bottomLeft[0], bottomRight[0]), Math.min(topLeft[1], topRight[1], bottomLeft[1], bottomRight[1]), Math.max(topLeft[0], topRight[0], bottomLeft[0], bottomRight[0]), Math.max(topLeft[1], topRight[1], bottomLeft[1], bottomRight[1])];
  };

  _proto.getDistanceScales = function getDistanceScales(coordinateOrigin) {
    if (coordinateOrigin) {
      return (0, _webMercator.getDistanceScales)({
        longitude: coordinateOrigin[0],
        latitude: coordinateOrigin[1],
        highPrecision: true
      });
    }

    return this.distanceScales;
  };

  _proto.containsPixel = function containsPixel(_ref4) {
    var x = _ref4.x,
        y = _ref4.y,
        _ref4$width = _ref4.width,
        width = _ref4$width === void 0 ? 1 : _ref4$width,
        _ref4$height = _ref4.height,
        height = _ref4$height === void 0 ? 1 : _ref4$height;
    return x < this.x + this.width && this.x < x + width && y < this.y + this.height && this.y < y + height;
  };

  _proto.getFrustumPlanes = function getFrustumPlanes() {
    if (this._frustumPlanes.near) {
      return this._frustumPlanes;
    }

    Object.assign(this._frustumPlanes, (0, _mathUtils.getFrustumPlanes)(this.viewProjectionMatrix));
    return this._frustumPlanes;
  };

  _proto.panByPosition = function panByPosition(coords, pixel) {
    return null;
  };

  _proto._initProps = function _initProps(opts) {
    var longitude = opts.longitude;
    var latitude = opts.latitude;

    if (this.isGeospatial) {
      if (!Number.isFinite(opts.zoom)) {
        this.zoom = (0, _webMercator.getMeterZoom)({
          latitude: latitude
        }) + Math.log2(this.focalDistance);
      }

      this.distanceScales = opts.distanceScales || (0, _webMercator.getDistanceScales)({
        latitude: latitude,
        longitude: longitude
      });
    }

    var scale = Math.pow(2, this.zoom);
    this.scale = scale;
    var position = opts.position,
        modelMatrix = opts.modelMatrix;
    var meterOffset = ZERO_VECTOR;

    if (position) {
      meterOffset = modelMatrix ? new _core.Matrix4(modelMatrix).transformAsVector(position, []) : position;
    }

    if (this.isGeospatial) {
      var center = this.projectPosition([longitude, latitude, 0]);
      this.center = new _core.Vector3(meterOffset).scale(this.distanceScales.unitsPerMeter).add(center);
    } else {
      this.center = this.projectPosition(meterOffset);
    }
  };

  _proto._initMatrices = function _initMatrices(opts) {
    var _opts$viewMatrix = opts.viewMatrix,
        viewMatrix = _opts$viewMatrix === void 0 ? IDENTITY : _opts$viewMatrix,
        _opts$projectionMatri = opts.projectionMatrix,
        projectionMatrix = _opts$projectionMatri === void 0 ? null : _opts$projectionMatri,
        _opts$orthographic = opts.orthographic,
        orthographic = _opts$orthographic === void 0 ? false : _opts$orthographic,
        fovyRadians = opts.fovyRadians,
        _opts$fovy = opts.fovy,
        fovy = _opts$fovy === void 0 ? 75 : _opts$fovy,
        _opts$near = opts.near,
        near = _opts$near === void 0 ? 0.1 : _opts$near,
        _opts$far = opts.far,
        far = _opts$far === void 0 ? 1000 : _opts$far,
        _opts$padding = opts.padding,
        padding = _opts$padding === void 0 ? null : _opts$padding,
        _opts$focalDistance = opts.focalDistance,
        focalDistance = _opts$focalDistance === void 0 ? 1 : _opts$focalDistance;
    this.viewMatrixUncentered = viewMatrix;
    this.viewMatrix = new _core.Matrix4().multiplyRight(viewMatrix).translate(new _core.Vector3(this.center).negate());
    this.projectionMatrix = projectionMatrix || createProjectionMatrix({
      width: this.width,
      height: this.height,
      orthographic: orthographic,
      fovyRadians: fovyRadians || fovy * DEGREES_TO_RADIANS,
      focalDistance: focalDistance,
      padding: padding,
      near: near,
      far: far
    });
    var vpm = (0, _mathUtils.createMat4)();

    _core.mat4.multiply(vpm, vpm, this.projectionMatrix);

    _core.mat4.multiply(vpm, vpm, this.viewMatrix);

    this.viewProjectionMatrix = vpm;
    this.viewMatrixInverse = _core.mat4.invert([], this.viewMatrix) || this.viewMatrix;
    this.cameraPosition = (0, _mathUtils.getCameraPosition)(this.viewMatrixInverse);
    var viewportMatrix = (0, _mathUtils.createMat4)();
    var pixelProjectionMatrix = (0, _mathUtils.createMat4)();

    _core.mat4.scale(viewportMatrix, viewportMatrix, [this.width / 2, -this.height / 2, 1]);

    _core.mat4.translate(viewportMatrix, viewportMatrix, [1, -1, 0]);

    _core.mat4.multiply(pixelProjectionMatrix, viewportMatrix, this.viewProjectionMatrix);

    this.pixelProjectionMatrix = pixelProjectionMatrix;
    this.pixelUnprojectionMatrix = _core.mat4.invert((0, _mathUtils.createMat4)(), this.pixelProjectionMatrix);

    if (!this.pixelUnprojectionMatrix) {
      _log.default.warn('Pixel project matrix not invertible')();
    }
  };

  (0, _createClass2.default)(Viewport, [{
    key: "subViewports",
    get: function get() {
      return null;
    }
  }, {
    key: "metersPerPixel",
    get: function get() {
      return this.distanceScales.metersPerUnit[2] / this.scale;
    }
  }, {
    key: "projectionMode",
    get: function get() {
      if (this.isGeospatial) {
        return this.zoom < 12 ? _constants.PROJECTION_MODE.WEB_MERCATOR : _constants.PROJECTION_MODE.WEB_MERCATOR_AUTO_OFFSET;
      }

      return _constants.PROJECTION_MODE.IDENTITY;
    }
  }]);
  return Viewport;
}();

exports.default = Viewport;
Viewport.displayName = 'Viewport';
//# sourceMappingURL=viewport.js.map