Skip to content

Commit

Permalink
feat(PointCloudLayer): add priorityQueue
Browse files Browse the repository at this point in the history
  • Loading branch information
ftoromanoff committed Feb 4, 2025
1 parent 99bd5bb commit a2500d1
Show file tree
Hide file tree
Showing 2 changed files with 154 additions and 63 deletions.
2 changes: 1 addition & 1 deletion src/Core/PointCloudNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class PointCloudNode extends THREE.EventDispatcher {

this.children = [];
this.bbox = new THREE.Box3();
this.sse = -1;
this.sse = 1;
}

add(node, indexChild) {
Expand Down
215 changes: 153 additions & 62 deletions src/Layer/PointCloudLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as THREE from 'three';
import GeometryLayer from 'Layer/GeometryLayer';
import PointsMaterial, { PNTS_MODE } from 'Renderer/PointsMaterial';
import Picking from 'Core/Picking';
import FlatQueue from 'flatqueue';

const point = new THREE.Vector3();
const bboxMesh = new THREE.Mesh();
Expand Down Expand Up @@ -77,7 +78,7 @@ function markForDeletion(elt) {
if (!elt.notVisibleSince) {
elt.notVisibleSince = Date.now();
// Set .sse to an invalid value
elt.sse = -1;
elt.sse = 1;
}
for (const child of elt.children) {
markForDeletion(child);
Expand Down Expand Up @@ -281,85 +282,175 @@ class PointCloudLayer extends GeometryLayer {
return [this.root];
}

update(context, layer, elt) {
elt.visible = false;
updateAll(context, srcs) {
console.log('###updateAll');
const queue = new FlatQueue();
// console.log('PointCloudLayer.updateAll');
const elementsToUpdate = this.preUpdate(context, srcs);

if (this.octreeDepthLimit >= 0 && this.octreeDepthLimit < elt.depth) {
markForDeletion(elt);
return;
}
// console.log(elementsToUpdate, elementsToUpdate.sse);

// `update` is called in `updateElements`.
this.updateElements(context, elementsToUpdate, queue);
// console.log(' >>> updateAll', queue, queue.pop(), queue.pop(), queue.pop(), queue.pop(), queue.pop());

this.update(context, this, queue);

// pick the best bounding box
const bbox = (elt.tightbbox ? elt.tightbbox : elt.bbox);
elt.visible = context.camera.isBox3Visible(bbox, this.object3d.matrixWorld);
if (!elt.visible) {
markForDeletion(elt);
// `postUpdate` is called when this geom layer update process is finished
// this.postUpdate();
}

updateElements(context, elements, queue) {
console.log(' > updateElements');
if (!elements) {
return;
}
for (const element of elements) {
console.log(element.id);
element.visible = false;

elt.notVisibleSince = undefined;
point.copy(context.camera.camera3D.position).sub(this.object3d.getWorldPosition(new THREE.Vector3()));
point.applyQuaternion(this.object3d.getWorldQuaternion(new THREE.Quaternion()).invert());
if (this.octreeDepthLimit >= 0 && this.octreeDepthLimit < element.depth) {
markForDeletion(element);
return;
}

// only load geometry if this elements has points
if (elt.numPoints !== 0) {
if (elt.obj) {
elt.obj.visible = true;
// pick the best bounding box
const bbox = (element.tightbbox ? element.tightbbox : element.bbox);
element.visible = context.camera.isBox3Visible(bbox, this.object3d.matrixWorld);
if (!element.visible) {
markForDeletion(element);
return;
}

if (__DEBUG__) {
if (this.bboxes.visible) {
if (!elt.obj.boxHelper) {
initBoundingBox(elt, layer);
}
elt.obj.boxHelper.visible = true;
elt.obj.boxHelper.material.color.r = 1 - elt.sse;
elt.obj.boxHelper.material.color.g = elt.sse;
element.notVisibleSince = undefined;
point.copy(context.camera.camera3D.position).sub(this.object3d.getWorldPosition(new THREE.Vector3()));
point.applyQuaternion(this.object3d.getWorldQuaternion(new THREE.Quaternion()).invert());

const distance = bbox.distanceToPoint(point);
element.sse = -1 * computeScreenSpaceError(context, this.pointSize, this.spacing, element, distance) / this.sseThreshold;

// add element to the queue
queue.push(element, element.sse);

// update element
// TODO find a way to notify attachedLayers when geometryLayer deletes some elements
// and then update Debug.js:addGeometryLayerDebugFeatures

if (element.children && element.children.length) {
if (element.sse <= -1) {
this.updateElements(context, element.children, queue);
} else {
for (const child of element.children) {
markForDeletion(child);
}
}
} else if (!elt.promise) {
const distance = Math.max(0.001, bbox.distanceToPoint(point));
// Increase priority of nearest node
const priority = computeScreenSpaceError(context, layer.pointSize, layer.spacing, elt, distance) / distance;
elt.promise = context.scheduler.execute({
layer,
requester: elt,
view: context.view,
priority,
redraw: true,
earlyDropFunction: cmd => !cmd.requester.visible || !this.visible,
}).then((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
this.group.add(elt.obj);
elt.obj.updateMatrixWorld(true);
}).catch((err) => {
if (!err.isCancelledCommandException) {
return err;
}
}).finally(() => {
elt.promise = null;
});
}

// const sub = this.getObjectToUpdateForAttachedLayers(element);

// if (sub) {
// if (sub.element) {
// if (__DEBUG__) {
// if (!(sub.element.isObject3D)) {
// throw new Error(`
// Invalid object for attached layer to update.
// Must be a THREE.Object and have a THREE.Material`);
// }
// }
// // update attached layers
// for (const attachedLayer of this.attachedLayers) {
// if (attachedLayer.ready) {
// attachedLayer.update(context, attachedLayer, sub.element, sub.parent);
// attachedLayer.cache.flush();
// }
// }
// } else if (sub.elements) {
// for (let i = 0; i < sub.elements.length; i++) {
// if (!(sub.elements[i].isObject3D)) {
// throw new Error(`
// Invalid object for attached layer to update.
// Must be a THREE.Object and have a THREE.Material`);
// }
// // update attached layers
// for (const attachedLayer of this.attachedLayers) {
// if (attachedLayer.ready) {
// attachedLayer.update(context, attachedLayer, sub.elements[i], sub.parent);
// attachedLayer.cache.flush();
// }
// }
// }
// }
// }
// this.updateElements(context, newElementsToUpdate, priorityQueue, queue);
}
}

if (elt.children && elt.children.length) {
const distance = bbox.distanceToPoint(point);
elt.sse = computeScreenSpaceError(context, layer.pointSize, layer.spacing, elt, distance) / this.sseThreshold;
if (elt.sse >= 1) {
return elt.children;
} else {
for (const child of elt.children) {
markForDeletion(child);
update(context, layer, queue) {
console.log(' >> update');

while (queue.length > 0) {
const elt = queue.pop();
console.log(elt.id);

// pick the best bounding box
const bbox = (elt.tightbbox ? elt.tightbbox : elt.bbox);

elt.notVisibleSince = undefined;
point.copy(context.camera.camera3D.position).sub(this.object3d.getWorldPosition(new THREE.Vector3()));
point.applyQuaternion(this.object3d.getWorldQuaternion(new THREE.Quaternion()).invert());

// only load geometry if this elements has points
if (elt.numPoints !== 0) {
if (elt.obj) {
elt.obj.visible = true;

if (__DEBUG__) {
if (this.bboxes.visible) {
if (!elt.obj.boxHelper) {
initBoundingBox(elt, layer);
}
elt.obj.boxHelper.visible = true;
elt.obj.boxHelper.material.color.r = 1 - elt.sse;
elt.obj.boxHelper.material.color.g = elt.sse;
}
}
} else if (!elt.promise) {
const distance = Math.max(0.001, bbox.distanceToPoint(point));
// Increase priority of nearest node
const priority = computeScreenSpaceError(context, layer.pointSize, layer.spacing, elt, distance) / distance;
console.log('send promise');
elt.promise = context.scheduler.execute({
layer,
requester: elt,
view: context.view,
priority,
redraw: true,
earlyDropFunction: cmd => !cmd.requester.visible || !this.visible,
}).then((pts) => {
console.log('THEN loaded', elt.id, elt.sse, elt.visible);
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
this.group.add(elt.obj);
elt.obj.updateMatrixWorld(true);
}).catch((err) => {
if (!err.isCancelledCommandException) {
return err;
}
}).finally(() => {
elt.promise = null;
});
}
}
}
}

postUpdate() {
console.log(' >>> postUpdate');
this.displayedCount = 0;
for (const pts of this.group.children) {
if (pts.visible) {
Expand Down

0 comments on commit a2500d1

Please sign in to comment.