itowns
Version:
A JS/WebGL framework for 3D geospatial data visualization
442 lines (376 loc) • 15.1 kB
JavaScript
"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"));
var point = new THREE.Vector3(); // 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;
point.copy(context.camera.camera3D.position).sub(layer.object3d.position); // 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(point)); // 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(point);
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;