"use strict";

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

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

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

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

var _webgl = require("@luma.gl/webgl");

var _pickLayersPass = _interopRequireDefault(require("../passes/pick-layers-pass"));

var _queryObject = require("./picking/query-object");

var _pickInfo = require("./picking/pick-info");

function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }

function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }

function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }

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; }

var DeckPicker = function () {
  function DeckPicker(device) {
    (0, _classCallCheck2.default)(this, DeckPicker);
    this.device = void 0;
    this.pickingFBO = void 0;
    this.depthFBO = void 0;
    this.pickLayersPass = void 0;
    this.layerFilter = void 0;
    this.lastPickedInfo = void 0;
    this._pickable = true;
    this.device = device;
    this.pickLayersPass = new _pickLayersPass.default(device);
    this.lastPickedInfo = {
      index: -1,
      layerId: null,
      info: null
    };
  }

  var _proto = DeckPicker.prototype;

  _proto.setProps = function setProps(props) {
    if ('layerFilter' in props) {
      this.layerFilter = props.layerFilter;
    }

    if ('_pickable' in props) {
      this._pickable = props._pickable;
    }
  };

  _proto.finalize = function finalize() {
    if (this.pickingFBO) {
      this.pickingFBO.delete();
    }

    if (this.depthFBO) {
      this.depthFBO.colorAttachments[0].delete();
      this.depthFBO.delete();
    }
  };

  _proto.pickObject = function pickObject(opts) {
    return this._pickClosestObject(opts);
  };

  _proto.pickObjects = function pickObjects(opts) {
    return this._pickVisibleObjects(opts);
  };

  _proto.getLastPickedObject = function getLastPickedObject(_ref) {
    var x = _ref.x,
        y = _ref.y,
        layers = _ref.layers,
        viewports = _ref.viewports;
    var lastPickedInfo = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.lastPickedInfo.info;
    var lastPickedLayerId = lastPickedInfo && lastPickedInfo.layer && lastPickedInfo.layer.id;
    var lastPickedViewportId = lastPickedInfo && lastPickedInfo.viewport && lastPickedInfo.viewport.id;
    var layer = lastPickedLayerId ? layers.find(function (l) {
      return l.id === lastPickedLayerId;
    }) : null;
    var viewport = lastPickedViewportId && viewports.find(function (v) {
      return v.id === lastPickedViewportId;
    }) || viewports[0];
    var coordinate = viewport && viewport.unproject([x - viewport.x, y - viewport.y]);
    var info = {
      x: x,
      y: y,
      viewport: viewport,
      coordinate: coordinate,
      layer: layer
    };
    return _objectSpread(_objectSpread({}, lastPickedInfo), info);
  };

  _proto._resizeBuffer = function _resizeBuffer() {
    var _this$pickingFBO, _this$depthFBO;

    if (!this.pickingFBO) {
      this.pickingFBO = this.device.createFramebuffer({
        colorAttachments: ['rgba8unorm']
      });

      if (this.device.isTextureFormatRenderable('rgba32float')) {
        var depthFBO = this.device.createFramebuffer({
          colorAttachments: [this.device.createTexture({
            format: this.device.info.type === 'webgl2' ? 'rgba32float' : 'rgba8unorm'
          })]
        });
        this.depthFBO = depthFBO;
      }
    }

    var gl = this.device.gl;
    (_this$pickingFBO = this.pickingFBO) === null || _this$pickingFBO === void 0 ? void 0 : _this$pickingFBO.resize({
      width: gl.canvas.width,
      height: gl.canvas.height
    });
    (_this$depthFBO = this.depthFBO) === null || _this$depthFBO === void 0 ? void 0 : _this$depthFBO.resize({
      width: gl.canvas.width,
      height: gl.canvas.height
    });
  };

  _proto._getPickable = function _getPickable(layers) {
    var _this = this;

    if (this._pickable === false) {
      return null;
    }

    var pickableLayers = layers.filter(function (layer) {
      return _this.pickLayersPass.shouldDrawLayer(layer) && !layer.isComposite;
    });
    return pickableLayers.length ? pickableLayers : null;
  };

  _proto._pickClosestObject = function _pickClosestObject(_ref2) {
    var layers = _ref2.layers,
        views = _ref2.views,
        viewports = _ref2.viewports,
        x = _ref2.x,
        y = _ref2.y,
        _ref2$radius = _ref2.radius,
        radius = _ref2$radius === void 0 ? 0 : _ref2$radius,
        _ref2$depth = _ref2.depth,
        depth = _ref2$depth === void 0 ? 1 : _ref2$depth,
        _ref2$mode = _ref2.mode,
        mode = _ref2$mode === void 0 ? 'query' : _ref2$mode,
        unproject3D = _ref2.unproject3D,
        onViewportActive = _ref2.onViewportActive,
        effects = _ref2.effects;
    var pixelRatio = this.device.canvasContext.cssToDeviceRatio();

    var pickableLayers = this._getPickable(layers);

    if (!pickableLayers) {
      return {
        result: [],
        emptyInfo: (0, _pickInfo.getEmptyPickingInfo)({
          viewports: viewports,
          x: x,
          y: y,
          pixelRatio: pixelRatio
        })
      };
    }

    this._resizeBuffer();

    var devicePixelRange = this.device.canvasContext.cssToDevicePixels([x, y], true);
    var devicePixel = [devicePixelRange.x + Math.floor(devicePixelRange.width / 2), devicePixelRange.y + Math.floor(devicePixelRange.height / 2)];
    var deviceRadius = Math.round(radius * pixelRatio);
    var _ref3 = this.pickingFBO,
        width = _ref3.width,
        height = _ref3.height;

    var deviceRect = this._getPickingRect({
      deviceX: devicePixel[0],
      deviceY: devicePixel[1],
      deviceRadius: deviceRadius,
      deviceWidth: width,
      deviceHeight: height
    });

    var cullRect = {
      x: x - radius,
      y: y - radius,
      width: radius * 2 + 1,
      height: radius * 2 + 1
    };
    var infos;
    var result = [];
    var affectedLayers = new Set();

    for (var i = 0; i < depth; i++) {
      var pickInfo = void 0;

      if (deviceRect) {
        var pickedResult = this._drawAndSample({
          layers: pickableLayers,
          views: views,
          viewports: viewports,
          onViewportActive: onViewportActive,
          deviceRect: deviceRect,
          cullRect: cullRect,
          effects: effects,
          pass: "picking:".concat(mode)
        });

        pickInfo = (0, _queryObject.getClosestObject)(_objectSpread(_objectSpread({}, pickedResult), {}, {
          deviceX: devicePixel[0],
          deviceY: devicePixel[1],
          deviceRadius: deviceRadius,
          deviceRect: deviceRect
        }));
      } else {
        pickInfo = {
          pickedColor: null,
          pickedObjectIndex: -1
        };
      }

      var z = void 0;

      if (pickInfo.pickedLayer && unproject3D && this.depthFBO) {
        var _this$_drawAndSample = this._drawAndSample({
          layers: [pickInfo.pickedLayer],
          views: views,
          viewports: viewports,
          onViewportActive: onViewportActive,
          deviceRect: {
            x: pickInfo.pickedX,
            y: pickInfo.pickedY,
            width: 1,
            height: 1
          },
          cullRect: cullRect,
          effects: effects,
          pass: "picking:".concat(mode, ":z")
        }, true),
            pickedColors2 = _this$_drawAndSample.pickedColors;

        if (pickedColors2[3]) {
          z = pickedColors2[0];
        }
      }

      if (pickInfo.pickedLayer && i + 1 < depth) {
        affectedLayers.add(pickInfo.pickedLayer);
        pickInfo.pickedLayer.disablePickingIndex(pickInfo.pickedObjectIndex);
      }

      infos = (0, _pickInfo.processPickInfo)({
        pickInfo: pickInfo,
        lastPickedInfo: this.lastPickedInfo,
        mode: mode,
        layers: pickableLayers,
        viewports: viewports,
        x: x,
        y: y,
        z: z,
        pixelRatio: pixelRatio
      });

      var _iterator = _createForOfIteratorHelper(infos.values()),
          _step;

      try {
        for (_iterator.s(); !(_step = _iterator.n()).done;) {
          var info = _step.value;

          if (info.layer) {
            result.push(info);
          }
        }
      } catch (err) {
        _iterator.e(err);
      } finally {
        _iterator.f();
      }

      if (!pickInfo.pickedColor) {
        break;
      }
    }

    var _iterator2 = _createForOfIteratorHelper(affectedLayers),
        _step2;

    try {
      for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
        var layer = _step2.value;
        layer.restorePickingColors();
      }
    } catch (err) {
      _iterator2.e(err);
    } finally {
      _iterator2.f();
    }

    return {
      result: result,
      emptyInfo: infos.get(null)
    };
  };

  _proto._pickVisibleObjects = function _pickVisibleObjects(_ref4) {
    var layers = _ref4.layers,
        views = _ref4.views,
        viewports = _ref4.viewports,
        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,
        _ref4$mode = _ref4.mode,
        mode = _ref4$mode === void 0 ? 'query' : _ref4$mode,
        _ref4$maxObjects = _ref4.maxObjects,
        maxObjects = _ref4$maxObjects === void 0 ? null : _ref4$maxObjects,
        onViewportActive = _ref4.onViewportActive,
        effects = _ref4.effects;

    var pickableLayers = this._getPickable(layers);

    if (!pickableLayers) {
      return [];
    }

    this._resizeBuffer();

    var pixelRatio = this.device.canvasContext.cssToDeviceRatio();
    var leftTop = this.device.canvasContext.cssToDevicePixels([x, y], true);
    var deviceLeft = leftTop.x;
    var deviceTop = leftTop.y + leftTop.height;
    var rightBottom = this.device.canvasContext.cssToDevicePixels([x + width, y + height], true);
    var deviceRight = rightBottom.x + rightBottom.width;
    var deviceBottom = rightBottom.y;
    var deviceRect = {
      x: deviceLeft,
      y: deviceBottom,
      width: deviceRight - deviceLeft,
      height: deviceTop - deviceBottom
    };

    var pickedResult = this._drawAndSample({
      layers: pickableLayers,
      views: views,
      viewports: viewports,
      onViewportActive: onViewportActive,
      deviceRect: deviceRect,
      cullRect: {
        x: x,
        y: y,
        width: width,
        height: height
      },
      effects: effects,
      pass: "picking:".concat(mode)
    });

    var pickInfos = (0, _queryObject.getUniqueObjects)(pickedResult);
    var uniquePickedObjects = new Map();
    var uniqueInfos = [];
    var limitMaxObjects = Number.isFinite(maxObjects);

    for (var i = 0; i < pickInfos.length; i++) {
      var _info$object;

      if (limitMaxObjects && uniqueInfos.length >= maxObjects) {
        break;
      }

      var pickInfo = pickInfos[i];
      var info = {
        color: pickInfo.pickedColor,
        layer: null,
        index: pickInfo.pickedObjectIndex,
        picked: true,
        x: x,
        y: y,
        pixelRatio: pixelRatio
      };
      info = (0, _pickInfo.getLayerPickingInfo)({
        layer: pickInfo.pickedLayer,
        info: info,
        mode: mode
      });
      var pickedLayerId = info.layer.id;

      if (!uniquePickedObjects.has(pickedLayerId)) {
        uniquePickedObjects.set(pickedLayerId, new Set());
      }

      var uniqueObjectsInLayer = uniquePickedObjects.get(pickedLayerId);
      var pickedObjectKey = (_info$object = info.object) !== null && _info$object !== void 0 ? _info$object : info.index;

      if (!uniqueObjectsInLayer.has(pickedObjectKey)) {
        uniqueObjectsInLayer.add(pickedObjectKey);
        uniqueInfos.push(info);
      }
    }

    return uniqueInfos;
  };

  _proto._drawAndSample = function _drawAndSample(_ref5) {
    var layers = _ref5.layers,
        views = _ref5.views,
        viewports = _ref5.viewports,
        onViewportActive = _ref5.onViewportActive,
        deviceRect = _ref5.deviceRect,
        cullRect = _ref5.cullRect,
        effects = _ref5.effects,
        pass = _ref5.pass;
    var pickZ = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
    var pickingFBO = pickZ ? this.depthFBO : this.pickingFBO;
    var opts = {
      layers: layers,
      layerFilter: this.layerFilter,
      views: views,
      viewports: viewports,
      onViewportActive: onViewportActive,
      pickingFBO: pickingFBO,
      deviceRect: deviceRect,
      cullRect: cullRect,
      effects: effects,
      pass: pass,
      pickZ: pickZ,
      preRenderStats: {}
    };

    var _iterator3 = _createForOfIteratorHelper(effects),
        _step3;

    try {
      for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
        var effect = _step3.value;

        if (effect.useInPicking) {
          opts.preRenderStats[effect.id] = effect.preRender(this.device, opts);
        }
      }
    } catch (err) {
      _iterator3.e(err);
    } finally {
      _iterator3.f();
    }

    var _this$pickLayersPass$ = this.pickLayersPass.render(opts),
        decodePickingColor = _this$pickLayersPass$.decodePickingColor;

    var x = deviceRect.x,
        y = deviceRect.y,
        width = deviceRect.width,
        height = deviceRect.height;
    var pickedColors = new (pickZ ? Float32Array : Uint8Array)(width * height * 4);
    (0, _webgl.readPixelsToArray)(pickingFBO, {
      sourceX: x,
      sourceY: y,
      sourceWidth: width,
      sourceHeight: height,
      target: pickedColors
    });
    return {
      pickedColors: pickedColors,
      decodePickingColor: decodePickingColor
    };
  };

  _proto._getPickingRect = function _getPickingRect(_ref6) {
    var deviceX = _ref6.deviceX,
        deviceY = _ref6.deviceY,
        deviceRadius = _ref6.deviceRadius,
        deviceWidth = _ref6.deviceWidth,
        deviceHeight = _ref6.deviceHeight;
    var x = Math.max(0, deviceX - deviceRadius);
    var y = Math.max(0, deviceY - deviceRadius);
    var width = Math.min(deviceWidth, deviceX + deviceRadius + 1) - x;
    var height = Math.min(deviceHeight, deviceY + deviceRadius + 1) - y;

    if (width <= 0 || height <= 0) {
      return null;
    }

    return {
      x: x,
      y: y,
      width: width,
      height: height
    };
  };

  return DeckPicker;
}();

exports.default = DeckPicker;
//# sourceMappingURL=deck-picker.js.map