"use strict";

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

var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");

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

var THREE = _interopRequireWildcard(require("three"));

var _CancelledCommandException = _interopRequireDefault(require("../Core/Scheduler/CancelledCommandException"));

// Draw a cube with lines (12 lines).
function cube(size) {
  var h = size.clone().multiplyScalar(0.5);
  var geometry = new THREE.Geometry();
  var line = new THREE.Line(geometry);
  geometry.vertices.push(new THREE.Vector3(-h.x, -h.y, -h.z), new THREE.Vector3(-h.x, h.y, -h.z), new THREE.Vector3(-h.x, h.y, -h.z), new THREE.Vector3(h.x, h.y, -h.z), new THREE.Vector3(h.x, h.y, -h.z), new THREE.Vector3(h.x, -h.y, -h.z), new THREE.Vector3(h.x, -h.y, -h.z), new THREE.Vector3(-h.x, -h.y, -h.z), new THREE.Vector3(-h.x, -h.y, h.z), new THREE.Vector3(-h.x, h.y, h.z), new THREE.Vector3(-h.x, h.y, h.z), new THREE.Vector3(h.x, h.y, h.z), new THREE.Vector3(h.x, h.y, h.z), new THREE.Vector3(h.x, -h.y, h.z), new THREE.Vector3(h.x, -h.y, h.z), new THREE.Vector3(-h.x, -h.y, h.z), new THREE.Vector3(-h.x, -h.y, -h.z), new THREE.Vector3(-h.x, -h.y, h.z), new THREE.Vector3(-h.x, h.y, -h.z), new THREE.Vector3(-h.x, h.y, h.z), new THREE.Vector3(h.x, h.y, -h.z), new THREE.Vector3(h.x, h.y, h.z), new THREE.Vector3(h.x, -h.y, -h.z), new THREE.Vector3(h.x, -h.y, h.z));
  line.computeLineDistances();
  return line.geometry;
}

function initBoundingBox(elt, layer) {
  var size = new THREE.Vector3();
  elt.tightbbox.getSize(size);
  elt.obj.boxHelper = new THREE.LineSegments(cube(size), elt.childrenBitField ? new THREE.LineDashedMaterial({
    color: 0,
    dashSize: 0.25,
    gapSize: 0.25
  }) : new THREE.LineBasicMaterial({
    color: 0
  }));
  elt.obj.boxHelper.frustumCulled = false;
  elt.obj.boxHelper.position.copy(elt.tightbbox.min);
  elt.obj.boxHelper.position.add(size.multiplyScalar(0.5));
  elt.obj.boxHelper.updateMatrixWorld(true);
  elt.obj.boxHelper.autoUpdateMatrix = false;
  elt.obj.boxHelper.material.linewidth = 2;
  elt.obj.boxHelper.layers.mask = layer.bboxes.layers.mask;
  layer.bboxes.add(elt.obj.boxHelper);
  elt.obj.boxHelper.updateMatrixWorld();
}

function computeScreenSpaceError(context, layer, elt, distance) {
  if (distance <= 0) {
    return Infinity;
  }

  var pointSpacing = layer.metadata.spacing / Math.pow(2, elt.name.length); // Estimate the onscreen distance between 2 points

  var onScreenSpacing = context.camera.preSSE * pointSpacing / distance; // [  P1  ]--------------[   P2   ]
  //     <--------------------->      = pointsSpacing (in world coordinates)
  //                                  ~ onScreenSpacing (in pixels)
  // <------>                         = layer.pointSize (in pixels)

  return Math.max(0.0, onScreenSpacing - layer.pointSize);
}

function markForDeletion(elt) {
  if (elt.obj) {
    elt.obj.material.visible = false;
  }

  if (!elt.notVisibleSince) {
    elt.notVisibleSince = Date.now(); // Set .sse to an invalid value

    elt.sse = -1;
  }

  var _iteratorNormalCompletion = true;
  var _didIteratorError = false;
  var _iteratorError = undefined;

  try {
    for (var _iterator = elt.children[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
      var child = _step.value;
      markForDeletion(child);
    }
  } catch (err) {
    _didIteratorError = true;
    _iteratorError = err;
  } finally {
    try {
      if (!_iteratorNormalCompletion && _iterator["return"] != null) {
        _iterator["return"]();
      }
    } finally {
      if (_didIteratorError) {
        throw _iteratorError;
      }
    }
  }
}

var _default = {
  preUpdate: function preUpdate(context, changeSources) {
    // Bail-out if not ready
    if (!this.root) {
      return [];
    } // See https://cesiumjs.org/hosted-apps/massiveworlds/downloads/Ring/WorldScaleTerrainRendering.pptx
    // slide 17


    context.camera.preSSE = context.camera.height / (2 * Math.tan(THREE.Math.degToRad(context.camera.camera3D.fov) * 0.5));

    if (this.material) {
      this.material.visible = this.visible;
      this.material.opacity = this.opacity;
      this.material.transparent = this.opacity < 1;
      this.material.size = this.pointSize;
    } // lookup lowest common ancestor of changeSources


    var commonAncestorName;
    var _iteratorNormalCompletion2 = true;
    var _didIteratorError2 = false;
    var _iteratorError2 = undefined;

    try {
      for (var _iterator2 = changeSources.values()[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
        var source = _step2.value;

        if (source.isCamera || source == this) {
          // if the change is caused by a camera move, no need to bother
          // to find common ancestor: we need to update the whole tree:
          // some invisible tiles may now be visible
          return [this.root];
        }

        if (source.obj === undefined) {
          continue;
        } // filter sources that belong to our layer


        if (source.obj.isPoints && source.obj.layer == this) {
          if (!commonAncestorName) {
            commonAncestorName = source.name;
          } else {
            var i = void 0;

            for (i = 0; i < Math.min(source.name.length, commonAncestorName.length); i++) {
              if (source.name[i] != commonAncestorName[i]) {
                break;
              }
            }

            commonAncestorName = commonAncestorName.substr(0, i);

            if (commonAncestorName.length == 0) {
              break;
            }
          }
        }
      }
    } catch (err) {
      _didIteratorError2 = true;
      _iteratorError2 = err;
    } finally {
      try {
        if (!_iteratorNormalCompletion2 && _iterator2["return"] != null) {
          _iterator2["return"]();
        }
      } finally {
        if (_didIteratorError2) {
          throw _iteratorError2;
        }
      }
    }

    if (commonAncestorName) {
      return [this.root.findChildrenByName(commonAncestorName)];
    } // Start updating from hierarchy root


    return [this.root];
  },
  update: function update(context, layer, elt) {
    elt.visible = false;

    if (layer.octreeDepthLimit >= 0 && layer.octreeDepthLimit < elt.name.length) {
      markForDeletion(elt);
      return;
    } // pick the best bounding box


    var bbox = elt.tightbbox ? elt.tightbbox : elt.bbox;
    elt.visible = context.camera.isBox3Visible(bbox, layer.object3d.matrixWorld);

    if (!elt.visible) {
      markForDeletion(elt);
      return;
    }

    elt.notVisibleSince = undefined; // only load geometry if this elements has points

    if (elt.numPoints > 0) {
      if (elt.obj) {
        if (elt.obj.material.update) {
          elt.obj.material.update(layer.material);
        } else {
          elt.obj.material.copy(layer.material);
        }
      } else if (!elt.promise) {
        var distance = Math.max(0.001, bbox.distanceToPoint(context.camera.camera3D.position)); // Increase priority of nearest node

        var priority = computeScreenSpaceError(context, layer, elt, distance) / distance;
        elt.promise = context.scheduler.execute({
          layer: layer,
          requester: elt,
          view: context.view,
          priority: priority,
          redraw: true,
          isLeaf: elt.childrenBitField == 0,
          earlyDropFunction: function earlyDropFunction(cmd) {
            return !cmd.requester.visible || !layer.visible;
          }
        }).then(function (pts) {
          if (layer.onPointsCreated) {
            layer.onPointsCreated(layer, pts);
          }

          elt.obj = pts; // store tightbbox to avoid ping-pong (bbox = larger => visible, tight => invisible)

          elt.tightbbox = pts.tightbbox; // make sure to add it here, otherwise it might never
          // be added nor cleaned

          layer.group.add(elt.obj);
          elt.obj.updateMatrixWorld(true);
          elt.promise = null;
        }, function (err) {
          if (err instanceof _CancelledCommandException["default"]) {
            elt.promise = null;
          }
        });
      }
    }

    if (elt.children && elt.children.length) {
      var _distance = bbox.distanceToPoint(context.camera.camera3D.position);

      elt.sse = computeScreenSpaceError(context, layer, elt, _distance) / layer.sseThreshold;

      if (elt.sse >= 1) {
        return elt.children;
      } else {
        var _iteratorNormalCompletion3 = true;
        var _didIteratorError3 = false;
        var _iteratorError3 = undefined;

        try {
          for (var _iterator3 = elt.children[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
            var child = _step3.value;
            markForDeletion(child);
          }
        } catch (err) {
          _didIteratorError3 = true;
          _iteratorError3 = err;
        } finally {
          try {
            if (!_iteratorNormalCompletion3 && _iterator3["return"] != null) {
              _iterator3["return"]();
            }
          } finally {
            if (_didIteratorError3) {
              throw _iteratorError3;
            }
          }
        }
      }
    }
  },
  postUpdate: function postUpdate(context, layer) {
    if (!layer.group) {
      return;
    }

    layer.displayedCount = 0;
    var _iteratorNormalCompletion4 = true;
    var _didIteratorError4 = false;
    var _iteratorError4 = undefined;

    try {
      for (var _iterator4 = layer.group.children[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
        var _pts2 = _step4.value;

        if (_pts2.material.visible) {
          var _count2 = _pts2.geometry.attributes.position.count;

          _pts2.geometry.setDrawRange(0, _count2);

          layer.displayedCount += _count2;
        }
      }
    } catch (err) {
      _didIteratorError4 = true;
      _iteratorError4 = err;
    } finally {
      try {
        if (!_iteratorNormalCompletion4 && _iterator4["return"] != null) {
          _iterator4["return"]();
        }
      } finally {
        if (_didIteratorError4) {
          throw _iteratorError4;
        }
      }
    }

    if (layer.displayedCount > layer.pointBudget) {
      // 2 different point count limit implementation, depending on the pointcloud source
      if (layer.supportsProgressiveDisplay) {
        // In this format, points are evenly distributed within a node,
        // so we can draw a percentage of each node and still get a correct
        // representation
        var reduction = layer.pointBudget / layer.displayedCount;
        var _iteratorNormalCompletion5 = true;
        var _didIteratorError5 = false;
        var _iteratorError5 = undefined;

        try {
          for (var _iterator5 = layer.group.children[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
            var pts = _step5.value;

            if (pts.material.visible) {
              var count = Math.floor(pts.geometry.drawRange.count * reduction);

              if (count > 0) {
                pts.geometry.setDrawRange(0, count);
              } else {
                pts.material.visible = false;
              }
            }
          }
        } catch (err) {
          _didIteratorError5 = true;
          _iteratorError5 = err;
        } finally {
          try {
            if (!_iteratorNormalCompletion5 && _iterator5["return"] != null) {
              _iterator5["return"]();
            }
          } finally {
            if (_didIteratorError5) {
              throw _iteratorError5;
            }
          }
        }

        layer.displayedCount *= reduction;
      } else {
        // This format doesn't require points to be evenly distributed, so
        // we're going to sort the nodes by "importance" (= on screen size)
        // and display only the first N nodes
        layer.group.children.sort(function (p1, p2) {
          return p2.userData.metadata.sse - p1.userData.metadata.sse;
        });
        var limitHit = false;
        layer.displayedCount = 0;
        var _iteratorNormalCompletion6 = true;
        var _didIteratorError6 = false;
        var _iteratorError6 = undefined;

        try {
          for (var _iterator6 = layer.group.children[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
            var _pts = _step6.value;
            var _count = _pts.geometry.attributes.position.count;

            if (limitHit || layer.displayedCount + _count > layer.pointBudget) {
              _pts.material.visible = false;
              limitHit = true;
            } else {
              layer.displayedCount += _count;
            }
          }
        } catch (err) {
          _didIteratorError6 = true;
          _iteratorError6 = err;
        } finally {
          try {
            if (!_iteratorNormalCompletion6 && _iterator6["return"] != null) {
              _iterator6["return"]();
            }
          } finally {
            if (_didIteratorError6) {
              throw _iteratorError6;
            }
          }
        }
      }
    }

    var now = Date.now();

    for (var i = layer.group.children.length - 1; i >= 0; i--) {
      var obj = layer.group.children[i];

      if (!obj.material.visible && now - obj.userData.metadata.notVisibleSince > 10000) {
        // remove from group
        layer.group.children.splice(i, 1);

        if (Array.isArray(obj.material)) {
          var _iteratorNormalCompletion7 = true;
          var _didIteratorError7 = false;
          var _iteratorError7 = undefined;

          try {
            for (var _iterator7 = obj.material[Symbol.iterator](), _step7; !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) {
              var material = _step7.value;
              material.dispose();
            }
          } catch (err) {
            _didIteratorError7 = true;
            _iteratorError7 = err;
          } finally {
            try {
              if (!_iteratorNormalCompletion7 && _iterator7["return"] != null) {
                _iterator7["return"]();
              }
            } finally {
              if (_didIteratorError7) {
                throw _iteratorError7;
              }
            }
          }
        } else {
          obj.material.dispose();
        }

        obj.geometry.dispose();
        obj.material = null;
        obj.geometry = null;
        obj.userData.metadata.obj = null;
      }
    }
  }
};
exports["default"] = _default;