diff --git a/src/Process/PointCloudProcessing.js b/src/Process/PointCloudProcessing.js index ab987720dc..c0cbe14892 100644 --- a/src/Process/PointCloudProcessing.js +++ b/src/Process/PointCloudProcessing.js @@ -163,18 +163,50 @@ function markForDeletion(elt) { } export default { - preUpdate(context, layer) { - // TODO: use changeSource - layer.counters = { - pointCount: 0, - displayedCount: 0, - }; - + preUpdate(context, layer, changeSources) { // Bail-out if not ready if (!layer.root) { return []; } + if (changeSources.has(undefined) || changeSources.size == 0) { + return [layer.root]; + } + + // lookup lowest common ancestor of changeSources + let commonAncestorName; + for (const source of changeSources.values()) { + if (source.isCamera) { + // 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 [layer.root]; + } + if (source.obj === undefined) { + continue; + } + // filter sources that belong to our layer + if (source.obj.isPoints && source.obj.layer == layer.id) { + if (!commonAncestorName) { + commonAncestorName = source.name; + } else { + let i; + 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; + } + } + } + } + if (commonAncestorName) { + return [layer.root.findChildrenByName(commonAncestorName)]; + } + // Start updating from hierarchy root return [layer.root]; }, @@ -205,8 +237,6 @@ export default { } const count = Math.max(1.0, Math.floor(shouldBeLoaded * elt.obj.geometry.attributes.position.count)); elt.obj.geometry.setDrawRange(0, count); - layer.counters.pointCount += elt.obj.realPointCount; - layer.counters.displayedCount += Math.floor(shouldBeLoaded * elt.obj.geometry.attributes.position.count); elt.obj.material.uniforms.size.value = layer.pointSize; } else if (!elt.promise) { // TODO: @@ -261,6 +291,13 @@ export default { return; } + layer.counters = { + displayedCount: 0, + }; + for (const pts of layer.group.children) { + layer.counters.displayedCount += pts.geometry.drawRange.count; + } + if (layer.counters.displayedCount > layer.pointBudget) { const reduction = layer.pointBudget / layer.counters.displayedCount; for (const pts of layer.group.children) { diff --git a/src/Provider/PointCloudProvider.js b/src/Provider/PointCloudProvider.js index db8777e468..f88c0400aa 100644 --- a/src/Provider/PointCloudProvider.js +++ b/src/Provider/PointCloudProvider.js @@ -94,6 +94,19 @@ function parseOctree(layer, hierarchyStepSize, root) { }); } +function findChildrenByName(node, name) { + if (node.name === name) { + return node; + } + const charIndex = node.name.length; + for (let i = 0; i < node.children.length; i++) { + if (node.children[i].name[charIndex] == name[charIndex]) { + return findChildrenByName(node.children[i], name); + } + } + throw new Error(`Cannot find node with name '${name}'`); +} + let nextuuid = 1; function addPickingAttribute(points) { // generate unique id for picking @@ -206,6 +219,8 @@ export default { // eslint-disable-next-line no-console console.log('LAYER metadata:', root); layer.root = root; + root.findChildrenByName = findChildrenByName.bind(root, root); + return layer; }); }, diff --git a/test/pointcloudprocessing_unit_test.js b/test/pointcloudprocessing_unit_test.js new file mode 100644 index 0000000000..f394290217 --- /dev/null +++ b/test/pointcloudprocessing_unit_test.js @@ -0,0 +1,54 @@ +import PointCloudProcessing from '../src/Process/PointCloudProcessing'; +/* global describe, it */ + +const assert = require('assert'); + +describe('preUpdate', function () { + it('should return root if no change source', () => { + const layer = { root: {} }; + const sources = new Set(); + assert.equal( + layer.root, + PointCloudProcessing.preUpdate(null, layer, sources)[0]); + }); + + it('should return root if no common ancestors', () => { + const layer = { root: {}, id: 'a' }; + const elt1 = { name: '12', obj: { layer: 'a', isPoints: true } }; + const elt2 = { name: '345', obj: { layer: 'a', isPoints: true } }; + const sources = new Set(); + sources.add(elt1); + sources.add(elt2); + assert.equal( + layer.root, + PointCloudProcessing.preUpdate(null, layer, sources)[0]); + }); + + it('should return common ancestor', () => { + const layer = { root: {}, id: 'a' }; + const elt1 = { name: '123', obj: { layer: 'a', isPoints: true } }; + const elt2 = { name: '12567', obj: { layer: 'a', isPoints: true } }; + const elt3 = { name: '122', obj: { layer: 'a', isPoints: true } }; + const sources = new Set(); + sources.add(elt1); + sources.add(elt2); + sources.add(elt3); + layer.root.findChildrenByName = (name) => { + assert.equal('12', name); + }; + PointCloudProcessing.preUpdate(null, layer, sources); + }); + + it('should not search ancestors if layer are different root if no common ancestors', () => { + const layer = { root: {}, id: 'a' }; + const elt1 = { name: '12', obj: { layer: 'a', isPoints: true } }; + const elt2 = { name: '13', obj: { layer: 'b', isPoints: true } }; + const sources = new Set(); + sources.add(elt1); + sources.add(elt2); + layer.root.findChildrenByName = (name) => { + assert.equal('12', name); + }; + PointCloudProcessing.preUpdate(null, layer, sources); + }); +});