| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392 |
- import * as THREE from "../libs/three.js/build/three.module.js";
- import {Points} from "./Points.js";
- export 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 Points()
- };
- this.segments.push(segment);
- }
- }
- size () {
- let size = 0;
- for (let segment of this.segments) {
- size += segment.points.numPoints;
- }
- return size;
- }
- };
- export 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 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: Infinity});
- };
- // 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(){
- if(!this.updateGeneratorInstance){
- this.updateGeneratorInstance = this.updateGenerator();
- }
- let result = this.updateGeneratorInstance.next();
- if(result.done){
- this.updateGeneratorInstance = null;
- }
- }
- * updateGenerator(){
- // 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 start = performance.now();
- 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.level > this.maxDepth){
- continue;
- }
- if (node.loaded) {
- // add points to result
- intersectedNodes.push(node);
- exports.lru.touch(node);
- this.highestLevelServed = Math.max(node.getLevel(), this.highestLevelServed);
- var geom = node.pcoGeometry;
- var hierarchyStepSize = geom ? geom.hierarchyStepSize : 1;
- var doTraverse = node.getLevel() === 0 ||
- (node.level % hierarchyStepSize === 0 && node.hasChildren);
- if (doTraverse) {
- this.traverse(node);
- }
- } else {
- node.load();
- this.priorityQueue.push(element);
- }
- }
- if (intersectedNodes.length > 0) {
- for(let done of this.getPointsInsideProfile(intersectedNodes, this.temporaryResult)){
- if(!done){
- //console.log("updateGenerator yields");
- yield false;
- }
- }
- if (this.temporaryResult.size() > 100) {
- this.pointsServed += this.temporaryResult.size();
- this.callback.onProgress({request: this, points: this.temporaryResult});
- this.temporaryResult = new 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 ProfileData(this.profile);
- }
- this.callback.onFinish({request: this});
- let index = this.pointcloud.profileRequests.indexOf(this);
- if (index >= 0) {
- this.pointcloud.profileRequests.splice(index, 1);
- }
- }
- yield true;
- };
- * getAccepted(numPoints, node, matrix, segment, segmentDir, points, totalMileage){
- let checkpoint = performance.now();
- let accepted = new Uint32Array(numPoints);
- let mileage = new Float64Array(numPoints);
- let acceptedPositions = new Float32Array(numPoints * 3);
- let numAccepted = 0;
- let pos = new THREE.Vector3();
- let svp = new THREE.Vector3();
- let view = new Float32Array(node.geometry.attributes.position.array);
- for (let i = 0; i < numPoints; i++) {
- pos.set(
- view[i * 3 + 0],
- view[i * 3 + 1],
- view[i * 3 + 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) {
- svp.subVectors(pos, segment.start);
- let localMileage = segmentDir.dot(svp);
- accepted[numAccepted] = i;
- mileage[numAccepted] = localMileage + totalMileage;
- points.boundingBox.expandByPoint(pos);
- pos.sub(this.pointcloud.position);
- acceptedPositions[3 * numAccepted + 0] = pos.x;
- acceptedPositions[3 * numAccepted + 1] = pos.y;
- acceptedPositions[3 * numAccepted + 2] = pos.z;
- numAccepted++;
- }
- if((i % 1000) === 0){
- let duration = performance.now() - checkpoint;
- if(duration > 4){
- //console.log(`getAccepted yield after ${duration}ms`);
- yield false;
- checkpoint = performance.now();
- }
- }
- }
- accepted = accepted.subarray(0, numAccepted);
- mileage = mileage.subarray(0, numAccepted);
- acceptedPositions = acceptedPositions.subarray(0, numAccepted * 3);
- //let end = performance.now();
- //let duration = end - start;
- //console.log("accepted duration ", duration)
- //console.log(`getAccepted finished`);
- yield [accepted, mileage, acceptedPositions];
- }
- * getPointsInsideProfile(nodes, target){
- let checkpoint = performance.now();
- let totalMileage = 0;
- let pointsProcessed = 0;
- for (let segment of target.segments) {
- for (let node of nodes) {
- let numPoints = node.numPoints;
- let geometry = node.geometry;
- if(!numPoints){
- continue;
- }
- { // skip if current node doesn't intersect current segment
- let bbWorld = node.boundingBox.clone().applyMatrix4(this.pointcloud.matrixWorld);
- let bsWorld = bbWorld.getBoundingSphere(new THREE.Sphere());
- let start = new THREE.Vector3(segment.start.x, segment.start.y, bsWorld.center.z);
- let end = new THREE.Vector3(segment.end.x, segment.end.y, bsWorld.center.z);
- let closest = new THREE.Line3(start, end).closestPointToPoint(bsWorld.center, true, new THREE.Vector3());
- let distance = closest.distanceTo(bsWorld.center);
- let intersects = (distance < (bsWorld.radius + target.profile.width));
- if(!intersects){
- continue;
- }
- }
- //{// DEBUG
- // console.log(node.name);
- // let boxHelper = new Potree.Box3Helper(node.getBoundingBox());
- // boxHelper.matrixAutoUpdate = false;
- // boxHelper.matrix.copy(viewer.scene.pointclouds[0].matrixWorld);
- // viewer.scene.scene.add(boxHelper);
- //}
- let sv = new THREE.Vector3().subVectors(segment.end, segment.start).setZ(0);
- let segmentDir = sv.clone().normalize();
- let points = new Points();
- let nodeMatrix = new THREE.Matrix4().makeTranslation(...node.boundingBox.min.toArray());
- let matrix = new THREE.Matrix4().multiplyMatrices(
- this.pointcloud.matrixWorld, nodeMatrix);
- pointsProcessed = pointsProcessed + numPoints;
- let accepted = null;
- let mileage = null;
- let acceptedPositions = null;
- for(let result of this.getAccepted(numPoints, node, matrix, segment, segmentDir, points,totalMileage)){
- if(!result){
- let duration = performance.now() - checkpoint;
- //console.log(`getPointsInsideProfile yield after ${duration}ms`);
- yield false;
- checkpoint = performance.now();
- }else{
- [accepted, mileage, acceptedPositions] = result;
- }
- }
- let duration = performance.now() - checkpoint;
- if(duration > 4){
- //console.log(`getPointsInsideProfile yield after ${duration}ms`);
- yield false;
- checkpoint = performance.now();
- }
- points.data.position = acceptedPositions;
- let relevantAttributes = Object.keys(geometry.attributes).filter(a => !["position", "indices"].includes(a));
- for(let attributeName of relevantAttributes){
- let attribute = geometry.attributes[attributeName];
- let numElements = attribute.array.length / numPoints;
- if(numElements !== parseInt(numElements)){
- debugger;
- }
- let Type = attribute.array.constructor;
- let filteredBuffer = new Type(numElements * accepted.length);
- let source = attribute.array;
- let target = filteredBuffer;
- for(let i = 0; i < accepted.length; i++){
- let index = accepted[i];
- let start = index * numElements;
- let end = start + numElements;
- let sub = source.subarray(start, end);
- target.set(sub, i * numElements);
- }
- points.data[attributeName] = filteredBuffer;
- }
- points.data['mileage'] = mileage;
- points.numPoints = accepted.length;
- segment.points.add(points);
- }
- totalMileage += segment.length;
- }
- for (let segment of target.segments) {
- target.boundingBox.union(segment.points.boundingBox);
- }
- //console.log(`getPointsInsideProfile finished`);
- yield true;
- };
- finishLevelThenCancel () {
- if (this.cancelRequested) {
- return;
- }
- this.maxDepth = this.highestLevelServed;
- 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);
- }
- };
- }
|