Skip to content

Commit

Permalink
Merge pull request #11 from sensat/DV-fix-global-tile-selection
Browse files Browse the repository at this point in the history
DV: Update tile selection to defer replace refinement until global selection
  • Loading branch information
Kaapp authored Aug 15, 2024
2 parents 5dae5f2 + 51fdb90 commit f7e79b4
Show file tree
Hide file tree
Showing 5 changed files with 2,476 additions and 152 deletions.
1 change: 0 additions & 1 deletion lerna.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
"bootstrap": {}
},
"npmClient": "yarn",
"useWorkspaces": true,
"exact": true,
"packages": [
"modules/*"
Expand Down
78 changes: 76 additions & 2 deletions modules/tiles/src/tileset/grouped-tiles.array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,82 @@ export class GroupedTilesArray {
this.array.push(other);
}

addTilesOrGroups(other: GroupedTilesArray) {
this.array.push(...other.array);
/**
* Adds the content of another GroupedTilesArray instance to this one.
* Optionally, a maximum size can be specified which will add items in
* order of _displayPriority up until the maximum size is reached.
* @param other The other instance to read from
* @param maxSize Optional maximum size of this GroupedTilesArray after
* the operation.
*/
addTilesOrGroups(other: GroupedTilesArray, maxSize: number = 0) {
const totalItems = this.array.length + other.array.length;

if (maxSize <= 0 || totalItems <= maxSize) {
// unlimited, or enough space to append all elements
this.array = this.array.concat(other.array);
} else {
// add as many items as possible by display priority, up to the maximum size
const replacedTileIds = new Set<string>();
let itemsToAdd = maxSize - this.array.length;

// shallow copy of other array to not modify other group
const otherArray = other.array.slice();

// ensure array is sorted DESCENDING by priority
otherArray.sort((a, b) => b._displayPriority - a._displayPriority);

while (itemsToAdd > 0 && otherArray.length > 0) {
// popping off end means iterating the list ASCENDING in priority
const nextItem = otherArray.pop()!; // safe assertion as we just checked the length

const nextItemReplacedIds =
nextItem instanceof Tile3D
? [nextItem._replacedTileId]
: nextItem.tiles.map((tile) => tile._replacedTileId);

const tilesInItem = nextItem instanceof Tile3D ? 1 : nextItem.tiles.length;
if (tilesInItem > itemsToAdd) {
// can't add the next item as it'd make the list too long
break;
}

// add the item
this.array.push(nextItem);
itemsToAdd -= tilesInItem;

// update replaced tile Ids (and increase the count if needed)
for (let i = 0; i < nextItemReplacedIds.length; i++) {
const replacedTileId = nextItemReplacedIds[i];

if (replacedTileId !== undefined && !replacedTileIds.has(replacedTileId)) {
// we're replacing a new item, so we need to add an extra item
itemsToAdd++;
replacedTileIds.add(replacedTileId);
}
}
}

// splice out any replaced tiles
this.array = this.array
.map((tileOrGroup) => {
if (tileOrGroup instanceof TileGroup3D) {
const filteredGroup = new TileGroup3D();

for (let i = 0; i < tileOrGroup.tiles.length; i++) {
const tile = tileOrGroup.tiles[i];
if (!replacedTileIds.has(tile.id)) {
filteredGroup.addTile(tile);
}
}

return filteredGroup.tiles.length > 0 ? filteredGroup : null;
}

return !replacedTileIds.has(tileOrGroup.id) ? tileOrGroup : null;
})
.filter((item): item is Tile3D | TileGroup3D => item !== null);
}
}

numTiles() {
Expand Down
14 changes: 7 additions & 7 deletions modules/tiles/src/tileset/tileset-traverser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,12 +136,12 @@ export class TilesetTraverser {

// replace tiles
} else if (tile.refine === TILE_REFINEMENT.REPLACE) {
// Always load tiles in the base traversal
// Select tiles that can't refine further
// Always load and select replacement tiles (so we can handle refinement at a global scale later)
// note (ck): This differs from the normal loadersgl implementation as we want to keep track of replaced
// root nodes so we can decide which level of detail to render based on the global tile budget rather than
// a per tile budget.
this.loadTile(tile, frameState);
if (stoppedRefining) {
this.selectTile(tile, frameState);
}
this.selectTile(tile, frameState);
}

// 3. update cache, most recent touched tiles have higher priority to be fetched from server
Expand Down Expand Up @@ -259,8 +259,8 @@ export class TilesetTraverser {
// only be necessary to set the replacedTileId once, on load.
if (tile.parent?.refine === TILE_REFINEMENT.REPLACE) {
// The root tile of a tileset does not have an ID, so when the tileset root is a REPLACE
// tile , its children set the _replacedTileId to the URL for the whole tileset.
tile._replacedTileId = tile.parent._replacedTileId ?? tile.parent.id ?? tile.tileset.url;
// tile, its children set the _replacedTileId to the URL for the whole tileset.
tile._replacedTileId = tile.parent.id ?? tile.tileset.url;
}
}
}
Expand Down
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,9 @@
"volta": {
"node": "18.19.0",
"yarn": "4.1.1"
}
},
"dependencies": {
"lerna": "^8.1.8"
},
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
}
Loading

0 comments on commit f7e79b4

Please sign in to comment.