UNPKG

itowns

Version:

A JS/WebGL framework for 3D geospatial data visualization

442 lines (376 loc) 15.1 kB
"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;