UNPKG

potree

Version:

WebGL point cloud viewer - WORK IN PROGRESS

254 lines (201 loc) 7.34 kB
Potree.ProfileData = class ProfileData { constructor (profile) { this.profile = profile; this.segments = []; this.boundingBox = new THREE.Box3(); for (let i = 0; i < profile.points.length - 1; i++) { let start = profile.points[i]; let end = profile.points[i + 1]; let startGround = new THREE.Vector3(start.x, start.y, 0); let endGround = new THREE.Vector3(end.x, end.y, 0); let center = new THREE.Vector3().addVectors(endGround, startGround).multiplyScalar(0.5); let length = startGround.distanceTo(endGround); let side = new THREE.Vector3().subVectors(endGround, startGround).normalize(); let up = new THREE.Vector3(0, 0, 1); let forward = new THREE.Vector3().crossVectors(side, up).normalize(); let N = forward; let cutPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(N, startGround); let halfPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(side, center); let segment = { start: start, end: end, cutPlane: cutPlane, halfPlane: halfPlane, length: length, points: new Potree.Points() }; this.segments.push(segment); } } size () { let size = 0; for (let segment of this.segments) { size += segment.points.numPoints; } return size; } }; Potree.ProfileRequest = class ProfileRequest { constructor (pointcloud, profile, maxDepth, callback) { this.pointcloud = pointcloud; this.profile = profile; this.maxDepth = maxDepth || Number.MAX_VALUE; this.callback = callback; this.temporaryResult = new Potree.ProfileData(this.profile); this.pointsServed = 0; this.highestLevelServed = 0; this.priorityQueue = new BinaryHeap(function (x) { return 1 / x.weight; }); this.initialize(); } initialize () { this.priorityQueue.push({node: this.pointcloud.pcoGeometry.root, weight: 1}); this.traverse(this.pointcloud.pcoGeometry.root); }; // traverse the node and add intersecting descendants to queue traverse (node) { let stack = []; for (let i = 0; i < 8; i++) { let child = node.children[i]; if (child && this.pointcloud.nodeIntersectsProfile(child, this.profile)) { stack.push(child); } } while (stack.length > 0) { let node = stack.pop(); let weight = node.boundingSphere.radius; this.priorityQueue.push({node: node, weight: weight}); // add children that intersect the cutting plane if (node.level < this.maxDepth) { for (let i = 0; i < 8; i++) { let child = node.children[i]; if (child && this.pointcloud.nodeIntersectsProfile(child, this.profile)) { stack.push(child); } } } } } update () { // load nodes in queue // if hierarchy expands, also load nodes from expanded hierarchy // once loaded, add data to this.points and remove node from queue // only evaluate 1-50 nodes per frame to maintain responsiveness let maxNodesPerUpdate = 1; let intersectedNodes = []; for (let i = 0; i < Math.min(maxNodesPerUpdate, this.priorityQueue.size()); i++) { let element = this.priorityQueue.pop(); let node = element.node; if (node.loaded) { // add points to result intersectedNodes.push(node); Potree.getLRU().touch(node); this.highestLevelServed = node.getLevel(); if ((node.level % node.pcoGeometry.hierarchyStepSize) === 0 && node.hasChildren) { this.traverse(node); } } else { node.load(); this.priorityQueue.push(element); } } if (intersectedNodes.length > 0) { this.getPointsInsideProfile(intersectedNodes, this.temporaryResult); if (this.temporaryResult.size() > 100) { this.pointsServed += this.temporaryResult.size(); this.callback.onProgress({request: this, points: this.temporaryResult}); this.temporaryResult = new Potree.ProfileData(this.profile); } } if (this.priorityQueue.size() === 0) { // we're done! inform callback and remove from pending requests if (this.temporaryResult.size() > 0) { this.pointsServed += this.temporaryResult.size(); this.callback.onProgress({request: this, points: this.temporaryResult}); this.temporaryResult = new Potree.ProfileData(this.profile); } this.callback.onFinish({request: this}); let index = this.pointcloud.profileRequests.indexOf(this); if (index >= 0) { this.pointcloud.profileRequests.splice(index, 1); } } }; getPointsInsideProfile (nodes, target) { let totalMileage = 0; for (let segment of target.segments) { for (let node of nodes) { let geometry = node.geometry; let positions = geometry.attributes.position; let p = positions.array; let numPoints = node.numPoints; let sv = new THREE.Vector3().subVectors(segment.end, segment.start).setZ(0); let segmentDir = sv.clone().normalize(); let accepted = []; let mileage = []; let acceptedPositions = []; let points = new Potree.Points(); let nodeMatrix = new THREE.Matrix4().makeTranslation(...node.boundingBox.min.toArray()); let matrix = new THREE.Matrix4().multiplyMatrices( this.pointcloud.matrixWorld, nodeMatrix); for (let i = 0; i < numPoints; i++) { let pos = new THREE.Vector3(p[3 * i], p[3 * i + 1], p[3 * i + 2]); pos.applyMatrix4(matrix); let distance = Math.abs(segment.cutPlane.distanceToPoint(pos)); let centerDistance = Math.abs(segment.halfPlane.distanceToPoint(pos)); if (distance < this.profile.width / 2 && centerDistance < segment.length / 2) { let svp = new THREE.Vector3().subVectors(pos, segment.start); let localMileage = segmentDir.dot(svp); accepted.push(i); mileage.push(localMileage + totalMileage); points.boundingBox.expandByPoint(pos); acceptedPositions.push(pos.x); acceptedPositions.push(pos.y); acceptedPositions.push(pos.z); } } for (let attribute of Object.keys(geometry.attributes).filter(a => a !== 'indices')) { let bufferedAttribute = geometry.attributes[attribute]; let Type = bufferedAttribute.array.constructor; let filteredBuffer = null; if (attribute === 'position') { filteredBuffer = new Type(acceptedPositions); } else { filteredBuffer = new Type(accepted.length * bufferedAttribute.itemSize); for (let i = 0; i < accepted.length; i++) { let index = accepted[i]; filteredBuffer.set( bufferedAttribute.array.subarray( bufferedAttribute.itemSize * index, bufferedAttribute.itemSize * index + bufferedAttribute.itemSize), bufferedAttribute.itemSize * i); } } points.data[attribute] = filteredBuffer; } points.data['mileage'] = new Float64Array(mileage); points.numPoints = accepted.length; segment.points.add(points); } totalMileage += segment.length; } for (let segment of target.segments) { target.boundingBox.union(segment.points.boundingBox); } }; finishLevelThenCancel () { if (this.cancelRequested) { return; } this.maxDepth = this.highestLevelServed + 1; this.cancelRequested = true; console.log(`maxDepth: ${this.maxDepth}`); }; cancel () { this.callback.onCancel(); this.priorityQueue = new BinaryHeap(function (x) { return 1 / x.weight; }); let index = this.pointcloud.profileRequests.indexOf(this); if (index >= 0) { this.pointcloud.profileRequests.splice(index, 1); } }; };