"use strict";

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

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 _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));

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

var _constants = require("@luma.gl/constants");

var _glUtils = require("./gl-utils");

var _typedArrayManager = _interopRequireDefault(require("../../utils/typed-array-manager"));

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

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

function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }

function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }

function resolveShaderAttribute(baseAccessor, shaderAttributeOptions) {
  if (shaderAttributeOptions.offset) {
    _log.default.removed('shaderAttribute.offset', 'vertexOffset, elementOffset')();
  }

  var stride = (0, _glUtils.getStride)(baseAccessor);
  var vertexOffset = shaderAttributeOptions.vertexOffset !== undefined ? shaderAttributeOptions.vertexOffset : baseAccessor.vertexOffset || 0;
  var elementOffset = shaderAttributeOptions.elementOffset || 0;
  var offset = vertexOffset * stride + elementOffset * baseAccessor.bytesPerElement + (baseAccessor.offset || 0);
  return _objectSpread(_objectSpread({}, shaderAttributeOptions), {}, {
    offset: offset,
    stride: stride
  });
}

function resolveDoublePrecisionShaderAttributes(baseAccessor, shaderAttributeOptions) {
  var resolvedOptions = resolveShaderAttribute(baseAccessor, shaderAttributeOptions);
  return {
    high: resolvedOptions,
    low: _objectSpread(_objectSpread({}, resolvedOptions), {}, {
      offset: resolvedOptions.offset + baseAccessor.size * 4
    })
  };
}

var DataColumn = function () {
  function DataColumn(device, opts, state) {
    (0, _classCallCheck2.default)(this, DataColumn);
    this.device = void 0;
    this.id = void 0;
    this.size = void 0;
    this.settings = void 0;
    this.value = void 0;
    this.doublePrecision = void 0;
    this._buffer = void 0;
    this.state = void 0;
    this.device = device;
    this.id = opts.id || '';
    this.size = opts.size || 1;
    var logicalType = opts.logicalType || opts.type;
    var doublePrecision = logicalType === 5130;
    var defaultValue = opts.defaultValue;
    defaultValue = Number.isFinite(defaultValue) ? [defaultValue] : defaultValue || new Array(this.size).fill(0);
    var bufferType;

    if (doublePrecision) {
      bufferType = 5126;
    } else if (!logicalType && opts.isIndexed) {
      bufferType = device.features.has('index-uint32-webgl1') ? 5125 : 5123;
    } else {
      bufferType = logicalType || 5126;
    }

    var defaultType = (0, _glUtils.glArrayFromType)(logicalType || bufferType || 5126);
    this.doublePrecision = doublePrecision;

    if (doublePrecision && opts.fp64 === false) {
      defaultType = Float32Array;
    }

    this.value = null;
    this.settings = _objectSpread(_objectSpread({}, opts), {}, {
      defaultType: defaultType,
      defaultValue: defaultValue,
      logicalType: logicalType,
      type: bufferType,
      size: this.size,
      bytesPerElement: defaultType.BYTES_PER_ELEMENT
    });
    this.state = _objectSpread(_objectSpread({}, state), {}, {
      externalBuffer: null,
      bufferAccessor: this.settings,
      allocatedValue: null,
      numInstances: 0,
      bounds: null,
      constant: false
    });
    this._buffer = this._createBuffer(0);
  }

  var _proto = DataColumn.prototype;

  _proto.delete = function _delete() {
    if (this._buffer) {
      this._buffer.delete();

      this._buffer = null;
    }

    _typedArrayManager.default.release(this.state.allocatedValue);
  };

  _proto.getBuffer = function getBuffer() {
    if (this.state.constant) {
      return null;
    }

    return this.state.externalBuffer || this._buffer;
  };

  _proto.getValue = function getValue() {
    var attributeName = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.id;
    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
    var result = {};

    if (this.state.constant) {
      var value = this.value;

      if (options) {
        var shaderAttributeDef = resolveShaderAttribute(this.getAccessor(), options);
        var offset = shaderAttributeDef.offset / value.BYTES_PER_ELEMENT;
        var size = shaderAttributeDef.size || this.size;
        result[attributeName] = value.subarray(offset, offset + size);
      } else {
        result[attributeName] = value;
      }
    } else {
      result[attributeName] = this.getBuffer();
    }

    if (this.doublePrecision) {
      if (this.value instanceof Float64Array) {
        result["".concat(attributeName, "64Low")] = result[attributeName];
      } else {
        result["".concat(attributeName, "64Low")] = new Float32Array(this.size);
      }
    }

    return result;
  };

  _proto.getBufferLayout = function getBufferLayout() {
    var attributeName = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.id;
    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
    var accessor = this.getAccessor();
    var attributes = [];
    var result = {
      name: this.id,
      byteStride: (0, _glUtils.getStride)(accessor),
      attributes: attributes
    };

    if (this.doublePrecision) {
      var doubleShaderAttributeDefs = resolveDoublePrecisionShaderAttributes(accessor, options || {});
      attributes.push((0, _glUtils.getBufferAttributeLayout)(attributeName, _objectSpread(_objectSpread({}, accessor), doubleShaderAttributeDefs.high)), (0, _glUtils.getBufferAttributeLayout)("".concat(attributeName, "64Low"), _objectSpread(_objectSpread({}, accessor), doubleShaderAttributeDefs.low)));
    } else if (options) {
      var shaderAttributeDef = resolveShaderAttribute(accessor, options);
      attributes.push((0, _glUtils.getBufferAttributeLayout)(attributeName, _objectSpread(_objectSpread({}, accessor), shaderAttributeDef)));
    } else {
      attributes.push((0, _glUtils.getBufferAttributeLayout)(attributeName, accessor));
    }

    return result;
  };

  _proto.setAccessor = function setAccessor(accessor) {
    this.state.bufferAccessor = accessor;
  };

  _proto.getAccessor = function getAccessor() {
    return this.state.bufferAccessor;
  };

  _proto.getBounds = function getBounds() {
    if (this.state.bounds) {
      return this.state.bounds;
    }

    var result = null;

    if (this.state.constant && this.value) {
      var min = Array.from(this.value);
      result = [min, min];
    } else {
      var value = this.value,
          numInstances = this.numInstances,
          size = this.size;
      var len = numInstances * size;

      if (value && len && value.length >= len) {
        var _min = new Array(size).fill(Infinity);

        var max = new Array(size).fill(-Infinity);

        for (var i = 0; i < len;) {
          for (var j = 0; j < size; j++) {
            var v = value[i++];
            if (v < _min[j]) _min[j] = v;
            if (v > max[j]) max[j] = v;
          }
        }

        result = [_min, max];
      }
    }

    this.state.bounds = result;
    return result;
  };

  _proto.setData = function setData(data) {
    var state = this.state;
    var opts;

    if (ArrayBuffer.isView(data)) {
      opts = {
        value: data
      };
    } else if (data instanceof _core.Buffer) {
      opts = {
        buffer: data
      };
    } else {
      opts = data;
    }

    var accessor = _objectSpread(_objectSpread({}, this.settings), opts);

    if (ArrayBuffer.isView(opts.value)) {
      var is64Bit = this.doublePrecision && opts.value instanceof Float64Array;
      accessor.type = opts.type || (is64Bit ? 5126 : (0, _glUtils.getGLTypeFromTypedArray)(opts.value));
      accessor.bytesPerElement = opts.value.BYTES_PER_ELEMENT;
      accessor.stride = (0, _glUtils.getStride)(accessor);
    }

    state.bounds = null;

    if (opts.constant) {
      var value = opts.value;
      value = this._normalizeValue(value, [], 0);

      if (this.settings.normalized) {
        value = this.normalizeConstant(value);
      }

      var hasChanged = !state.constant || !this._areValuesEqual(value, this.value);

      if (!hasChanged) {
        return false;
      }

      state.externalBuffer = null;
      state.constant = true;
      this.value = ArrayBuffer.isView(value) ? value : new Float32Array(value);
    } else if (opts.buffer) {
      var buffer = opts.buffer;
      state.externalBuffer = buffer;
      state.constant = false;
      this.value = opts.value || null;
    } else if (opts.value) {
      this._checkExternalBuffer(opts);

      var _value = opts.value;
      state.externalBuffer = null;
      state.constant = false;
      this.value = _value;
      var _buffer = this.buffer;
      var stride = (0, _glUtils.getStride)(accessor);
      var byteOffset = (accessor.vertexOffset || 0) * stride;

      if (this.doublePrecision && _value instanceof Float64Array) {
        _value = (0, _mathUtils.toDoublePrecisionArray)(_value, accessor);
      }

      if (this.settings.isIndexed) {
        var ArrayType = this.settings.defaultType;

        if (_value.constructor !== ArrayType) {
          _value = new ArrayType(_value);
        }
      }

      var requiredBufferSize = _value.byteLength + byteOffset + stride * 2;

      if (_buffer.byteLength < requiredBufferSize) {
        _buffer = this._createBuffer(requiredBufferSize);
      }

      _buffer.write(_value, byteOffset);
    }

    this.setAccessor(accessor);
    return true;
  };

  _proto.updateSubBuffer = function updateSubBuffer() {
    var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
    this.state.bounds = null;
    var value = this.value;
    var _opts$startOffset = opts.startOffset,
        startOffset = _opts$startOffset === void 0 ? 0 : _opts$startOffset,
        endOffset = opts.endOffset;
    this.buffer.write(this.doublePrecision && value instanceof Float64Array ? (0, _mathUtils.toDoublePrecisionArray)(value, {
      size: this.size,
      startIndex: startOffset,
      endIndex: endOffset
    }) : value.subarray(startOffset, endOffset), startOffset * value.BYTES_PER_ELEMENT + this.byteOffset);
  };

  _proto.allocate = function allocate(numInstances) {
    var copy = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
    var state = this.state;
    var oldValue = state.allocatedValue;

    var value = _typedArrayManager.default.allocate(oldValue, numInstances + 1, {
      size: this.size,
      type: this.settings.defaultType,
      copy: copy
    });

    this.value = value;
    var byteOffset = this.byteOffset;
    var buffer = this.buffer;

    if (buffer.byteLength < value.byteLength + byteOffset) {
      buffer = this._createBuffer(value.byteLength + byteOffset);

      if (copy && oldValue) {
        buffer.write(oldValue instanceof Float64Array ? (0, _mathUtils.toDoublePrecisionArray)(oldValue, this) : oldValue, byteOffset);
      }
    }

    state.allocatedValue = value;
    state.constant = false;
    state.externalBuffer = null;
    this.setAccessor(this.settings);
    return true;
  };

  _proto._checkExternalBuffer = function _checkExternalBuffer(opts) {
    var value = opts.value;

    if (!ArrayBuffer.isView(value)) {
      throw new Error("Attribute ".concat(this.id, " value is not TypedArray"));
    }

    var ArrayType = this.settings.defaultType;
    var illegalArrayType = false;

    if (this.doublePrecision) {
      illegalArrayType = value.BYTES_PER_ELEMENT < 4;
    }

    if (illegalArrayType) {
      throw new Error("Attribute ".concat(this.id, " does not support ").concat(value.constructor.name));
    }

    if (!(value instanceof ArrayType) && this.settings.normalized && !('normalized' in opts)) {
      _log.default.warn("Attribute ".concat(this.id, " is normalized"))();
    }
  };

  _proto.normalizeConstant = function normalizeConstant(value) {
    switch (this.settings.type) {
      case 5120:
        return new Float32Array(value).map(function (x) {
          return (x + 128) / 255 * 2 - 1;
        });

      case 5122:
        return new Float32Array(value).map(function (x) {
          return (x + 32768) / 65535 * 2 - 1;
        });

      case 5121:
        return new Float32Array(value).map(function (x) {
          return x / 255;
        });

      case 5123:
        return new Float32Array(value).map(function (x) {
          return x / 65535;
        });

      default:
        return value;
    }
  };

  _proto._normalizeValue = function _normalizeValue(value, out, start) {
    var _this$settings = this.settings,
        defaultValue = _this$settings.defaultValue,
        size = _this$settings.size;

    if (Number.isFinite(value)) {
      out[start] = value;
      return out;
    }

    if (!value) {
      var i = size;

      while (--i >= 0) {
        out[start + i] = defaultValue[i];
      }

      return out;
    }

    switch (size) {
      case 4:
        out[start + 3] = Number.isFinite(value[3]) ? value[3] : defaultValue[3];

      case 3:
        out[start + 2] = Number.isFinite(value[2]) ? value[2] : defaultValue[2];

      case 2:
        out[start + 1] = Number.isFinite(value[1]) ? value[1] : defaultValue[1];

      case 1:
        out[start + 0] = Number.isFinite(value[0]) ? value[0] : defaultValue[0];
        break;

      default:
        var _i = size;

        while (--_i >= 0) {
          out[start + _i] = Number.isFinite(value[_i]) ? value[_i] : defaultValue[_i];
        }

    }

    return out;
  };

  _proto._areValuesEqual = function _areValuesEqual(value1, value2) {
    if (!value1 || !value2) {
      return false;
    }

    var size = this.size;

    for (var i = 0; i < size; i++) {
      if (value1[i] !== value2[i]) {
        return false;
      }
    }

    return true;
  };

  _proto._createBuffer = function _createBuffer(byteLength) {
    var _this$_buffer;

    if (this._buffer) {
      this._buffer.destroy();
    }

    var _this$settings2 = this.settings,
        isIndexed = _this$settings2.isIndexed,
        type = _this$settings2.type;
    this._buffer = this.device.createBuffer(_objectSpread(_objectSpread({}, (_this$_buffer = this._buffer) === null || _this$_buffer === void 0 ? void 0 : _this$_buffer.props), {}, {
      id: this.id,
      usage: isIndexed ? _core.Buffer.INDEX : _core.Buffer.VERTEX,
      indexType: isIndexed ? type === 5123 ? 'uint16' : 'uint32' : undefined,
      byteLength: byteLength
    }));
    return this._buffer;
  };

  (0, _createClass2.default)(DataColumn, [{
    key: "isConstant",
    get: function get() {
      return this.state.constant;
    }
  }, {
    key: "buffer",
    get: function get() {
      return this._buffer;
    }
  }, {
    key: "byteOffset",
    get: function get() {
      var accessor = this.getAccessor();

      if (accessor.vertexOffset) {
        return accessor.vertexOffset * (0, _glUtils.getStride)(accessor);
      }

      return 0;
    }
  }, {
    key: "numInstances",
    get: function get() {
      return this.state.numInstances;
    },
    set: function set(n) {
      this.state.numInstances = n;
    }
  }]);
  return DataColumn;
}();

exports.default = DataColumn;
//# sourceMappingURL=data-column.js.map