Skip to content

Commit

Permalink
feat(pointcloud): use 'changeSource' mechanism to avoid useless work
Browse files Browse the repository at this point in the history
Implements a simple logic to avoid updating the full layer if we
already know that the result won't change.

This feature is already implemented for tile based layers
(see 3f340b9).
  • Loading branch information
peppsac committed Apr 13, 2018
1 parent c82685d commit 38e7c11
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 9 deletions.
55 changes: 46 additions & 9 deletions src/Process/PointCloudProcessing.js
Original file line number Diff line number Diff line change
Expand Up @@ -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];
},
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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) {
Expand Down
15 changes: 15 additions & 0 deletions src/Provider/PointCloudProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
});
},
Expand Down
54 changes: 54 additions & 0 deletions test/pointcloudprocessing_unit_test.js
Original file line number Diff line number Diff line change
@@ -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);
});
});

0 comments on commit 38e7c11

Please sign in to comment.