"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports["default"] = void 0;
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
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 _lazPerf = require("laz-perf");
var _copc = require("copc");
/**
 * @typedef {Object} Header - Partial LAS header.
 * @property {number} header.pointDataRecordFormat - Type of point data
 * records contained by the buffer.
 * @property {number} header.pointDataRecordLength - Size (in bytes) of the
 * point data records. If the specified size is larger than implied by the
 * point data record format (see above) the remaining bytes are user-specfic
 * "extra bytes". Those are described by an Extra Bytes VLR.
 * @property {number[]} header.scale - Scale factors (an array `[xScale,
 * yScale, zScale]`) multiplied to the X, Y, Z point record values.
 * @property {number[]} header.offset - Offsets (an array `[xOffset,
 * xOffset, zOffset]`) added to the scaled X, Y, Z point record values.
 */
/**
 * @classdesc
 * Loader for LAS and LAZ (LASZip) point clouds. It uses the copc.js library and
 * the laz-perf decoder under the hood.
 *
 * The laz-perf web assembly module is lazily fetched at runtime when a parsing
 * request is initiated. Location of laz-perf wasm defaults to the unpkg
 * repository.
 */
var LASLoader = /*#__PURE__*/function () {
  function LASLoader() {
    (0, _classCallCheck2["default"])(this, LASLoader);
    this._wasmPath = 'https://unpkg.com/laz-perf@0.0.6/lib/';
    this._wasmPromise = null;
  }
  (0, _createClass2["default"])(LASLoader, [{
    key: "_initDecoder",
    value: function _initDecoder() {
      var _this = this;
      if (this._wasmPromise) {
        return this._wasmPromise;
      }
      this._wasmPromise = _lazPerf.LazPerf.create({
        locateFile: function locateFile(file) {
          return "".concat(_this._wasmPath, "/").concat(file);
        }
      });
      return this._wasmPromise;
    }
  }, {
    key: "_parseView",
    value: function _parseView(view, options) {
      var _options$colorDepth;
      var colorDepth = (_options$colorDepth = options.colorDepth) !== null && _options$colorDepth !== void 0 ? _options$colorDepth : 16;
      var getPosition = ['X', 'Y', 'Z'].map(view.getter);
      var getIntensity = view.getter('Intensity');
      var getReturnNumber = view.getter('ReturnNumber');
      var getNumberOfReturns = view.getter('NumberOfReturns');
      var getClassification = view.getter('Classification');
      var getPointSourceID = view.getter('PointSourceId');
      var getColor = view.dimensions.Red ? ['Red', 'Green', 'Blue'].map(view.getter) : undefined;
      var positions = new Float32Array(view.pointCount * 3);
      var intensities = new Uint16Array(view.pointCount);
      var returnNumbers = new Uint8Array(view.pointCount);
      var numberOfReturns = new Uint8Array(view.pointCount);
      var classifications = new Uint8Array(view.pointCount);
      var pointSourceIDs = new Uint16Array(view.pointCount);
      var colors = getColor ? new Uint8Array(view.pointCount * 4) : undefined;
      var _loop = function _loop(i) {
        // `getPosition` apply scale and offset transform to the X, Y, Z
        // values. See https://github.com/connormanning/copc.js/blob/master/src/las/extractor.ts.
        var _getPosition$map = getPosition.map(function (f) {
            return f(i);
          }),
          _getPosition$map2 = (0, _slicedToArray2["default"])(_getPosition$map, 3),
          x = _getPosition$map2[0],
          y = _getPosition$map2[1],
          z = _getPosition$map2[2];
        positions[i * 3] = x;
        positions[i * 3 + 1] = y;
        positions[i * 3 + 2] = z;
        intensities[i] = getIntensity(i);
        returnNumbers[i] = getReturnNumber(i);
        numberOfReturns[i] = getNumberOfReturns(i);
        if (getColor) {
          // Note that we do not infer color depth as it is expensive
          // (i.e. traverse the whole view to check if there exists a red,
          // green or blue value > 255).
          var _getColor$map = getColor.map(function (f) {
              return f(i);
            }),
            _getColor$map2 = (0, _slicedToArray2["default"])(_getColor$map, 3),
            r = _getColor$map2[0],
            g = _getColor$map2[1],
            b = _getColor$map2[2];
          if (colorDepth === 16) {
            r /= 256;
            g /= 256;
            b /= 256;
          }
          colors[i * 4] = r;
          colors[i * 4 + 1] = g;
          colors[i * 4 + 2] = b;
          colors[i * 4 + 3] = 255;
        }
        classifications[i] = getClassification(i);
        pointSourceIDs[i] = getPointSourceID(i);
      };
      for (var i = 0; i < view.pointCount; i++) {
        _loop(i);
      }
      return {
        position: positions,
        intensity: intensities,
        returnNumber: returnNumbers,
        numberOfReturns: numberOfReturns,
        classification: classifications,
        pointSourceID: pointSourceIDs,
        color: colors
      };
    }

    /**
     * Set LazPerf decoder path.
     * @param {string} path - path to `laz-perf.wasm` folder.
     */
  }, {
    key: "lazPerf",
    set: function set(path) {
      this._wasmPath = path;
      this._wasmPromise = null;
    }

    /**
     * Parses a LAS or LAZ (LASZip) file. Note that this function is
     * **CPU-bound** and shall be parallelised in a dedicated worker.
     * @param {ArrayBuffer} data - Binary data to parse.
     * @param {Object} [options] - Parsing options.
     * @param {8 | 16} [options.colorDepth] - Color depth encoding (in bits).
     * Either 8 or 16 bits. Defaults to 8 bits for LAS 1.2 and 16 bits for later
     * versions (as mandatory by the specification)
     */
  }, {
    key: "parseFile",
    value: function () {
      var _parseFile = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee2(data) {
        var _options$colorDepth2;
        var options,
          bytes,
          pointData,
          header,
          colorDepth,
          getter,
          vlrs,
          ebVlr,
          eb,
          view,
          attributes,
          _args2 = arguments;
        return _regenerator["default"].wrap(function _callee2$(_context2) {
          while (1) switch (_context2.prev = _context2.next) {
            case 0:
              options = _args2.length > 1 && _args2[1] !== undefined ? _args2[1] : {};
              bytes = new Uint8Array(data);
              _context2.next = 4;
              return _copc.Las.PointData.decompressFile(bytes, this._initDecoder());
            case 4:
              pointData = _context2.sent;
              header = _copc.Las.Header.parse(bytes);
              colorDepth = (_options$colorDepth2 = options.colorDepth) !== null && _options$colorDepth2 !== void 0 ? _options$colorDepth2 : header.majorVersion === 1 && header.minorVersion <= 2 ? 8 : 16;
              getter = /*#__PURE__*/function () {
                var _ref = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee(begin, end) {
                  return _regenerator["default"].wrap(function _callee$(_context) {
                    while (1) switch (_context.prev = _context.next) {
                      case 0:
                        return _context.abrupt("return", bytes.slice(begin, end));
                      case 1:
                      case "end":
                        return _context.stop();
                    }
                  }, _callee);
                }));
                return function getter(_x2, _x3) {
                  return _ref.apply(this, arguments);
                };
              }();
              _context2.next = 10;
              return _copc.Las.Vlr.walk(getter, header);
            case 10:
              vlrs = _context2.sent;
              ebVlr = _copc.Las.Vlr.find(vlrs, 'LASF_Spec', 4);
              _context2.t0 = ebVlr;
              if (!_context2.t0) {
                _context2.next = 19;
                break;
              }
              _context2.t1 = _copc.Las.ExtraBytes;
              _context2.next = 17;
              return _copc.Las.Vlr.fetch(getter, ebVlr);
            case 17:
              _context2.t2 = _context2.sent;
              _context2.t0 = _context2.t1.parse.call(_context2.t1, _context2.t2);
            case 19:
              eb = _context2.t0;
              view = _copc.Las.View.create(pointData, header, eb);
              attributes = this._parseView(view, {
                colorDepth: colorDepth
              });
              return _context2.abrupt("return", {
                header: header,
                attributes: attributes
              });
            case 23:
            case "end":
              return _context2.stop();
          }
        }, _callee2, this);
      }));
      function parseFile(_x) {
        return _parseFile.apply(this, arguments);
      }
      return parseFile;
    }()
  }]);
  return LASLoader;
}();
var _default = LASLoader;
exports["default"] = _default;