Skip to content

Commit ef2875d

Browse files
authored
Merge branch 'main' into coverage-link
2 parents 1ccaa77 + 3418fa9 commit ef2875d

File tree

11 files changed

+168
-85
lines changed

11 files changed

+168
-85
lines changed

CHANGES.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,18 @@
1111

1212
#### Fixes :wrench:
1313

14-
- Fixes an event bug following recent changes, where adding a new listener during an event callback caused an infinite loop. [#12955](https://github.com/CesiumGS/cesium/pull/12955)
1514
- Fix issues with label background when updating properties while `label.show` is `false`. [#12138](https://github.com/CesiumGS/cesium/issues/12138)
1615
- Improved performance of `scene.drillPick`. [#12916](https://github.com/CesiumGS/cesium/pull/12916)
1716
- Improved performance when removing primitives. [#3018](https://github.com/CesiumGS/cesium/pull/3018)
17+
- Improved performance of terrain Quadtree handling of custom data [#12907](https://github.com/CesiumGS/cesium/pull/12907)
18+
19+
## 1.134.1 - 2025-10-10
20+
21+
### @cesium/engine
22+
23+
#### Fixes :wrench:
24+
25+
- Fixed an event bug following recent changes, where adding a new listener during an event callback caused an infinite loop. [#12955](https://github.com/CesiumGS/cesium/pull/12955)
1826

1927
## 1.134 - 2025-10-01
2028

ThirdParty.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"license": [
1313
"BSD-3-Clause"
1414
],
15-
"version": "2.8.4",
15+
"version": "2.8.7",
1616
"url": "https://www.npmjs.com/package/@zip.js/zip.js"
1717
},
1818
{

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "cesium",
3-
"version": "1.134.0",
3+
"version": "1.134.1",
44
"description": "CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.",
55
"homepage": "http://cesium.com/cesiumjs/",
66
"license": "Apache-2.0",
@@ -51,8 +51,8 @@
5151
"./Specs/**/*"
5252
],
5353
"dependencies": {
54-
"@cesium/engine": "^21.0.0",
55-
"@cesium/widgets": "^13.2.0"
54+
"@cesium/engine": "^21.0.1",
55+
"@cesium/widgets": "^13.2.1"
5656
},
5757
"devDependencies": {
5858
"@cesium/eslint-config": "^12.0.0",
@@ -169,4 +169,4 @@
169169
}
170170
}
171171
}
172-
}
172+
}

packages/engine/Source/Scene/GlobeSurfaceTile.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -493,10 +493,8 @@ GlobeSurfaceTile.prototype.updateExaggeration = function (
493493
if (quadtree !== undefined) {
494494
quadtree._tileToUpdateHeights.push(tile);
495495
const customData = tile.customData;
496-
const customDataLength = customData.length;
497-
for (let i = 0; i < customDataLength; i++) {
496+
for (const data of customData) {
498497
// Restart the level so that a height update is triggered
499-
const data = customData[i];
500498
data.level = -1;
501499
}
502500
}

packages/engine/Source/Scene/QuadtreePrimitive.js

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ function QuadtreePrimitive(options) {
9090
this._removeHeightCallbacks = [];
9191

9292
this._tileToUpdateHeights = [];
93-
this._lastTileIndex = 0;
9493
this._updateHeightsTimeSlice = 2.0;
9594

9695
// If a culled tile contains _cameraPositionCartographic or _cameraReferenceFrameOriginCartographic, it will be marked
@@ -217,10 +216,8 @@ function invalidateAllTiles(primitive) {
217216
for (let i = 0; i < levelZeroTiles.length; ++i) {
218217
const tile = levelZeroTiles[i];
219218
const customData = tile.customData;
220-
const customDataLength = customData.length;
221219

222-
for (let j = 0; j < customDataLength; ++j) {
223-
const data = customData[j];
220+
for (const data of customData) {
224221
data.level = 0;
225222
primitive._addHeightCallbacks.push(data);
226223
}
@@ -513,7 +510,6 @@ function selectTilesForRendering(primitive, frameState) {
513510
tilesToRender.length = 0;
514511

515512
// We can't render anything before the level zero tiles exist.
516-
let i;
517513
const tileProvider = primitive._tileProvider;
518514
if (!defined(primitive._levelZeroTiles)) {
519515
const tilingScheme = tileProvider.tilingScheme;
@@ -524,7 +520,7 @@ function selectTilesForRendering(primitive, frameState) {
524520
const numberOfRootTiles = primitive._levelZeroTiles.length;
525521
if (rootTraversalDetails.length < numberOfRootTiles) {
526522
rootTraversalDetails = new Array(numberOfRootTiles);
527-
for (i = 0; i < numberOfRootTiles; ++i) {
523+
for (let i = 0; i < numberOfRootTiles; ++i) {
528524
if (rootTraversalDetails[i] === undefined) {
529525
rootTraversalDetails[i] = new TraversalDetails();
530526
}
@@ -537,7 +533,6 @@ function selectTilesForRendering(primitive, frameState) {
537533

538534
primitive._occluders.ellipsoid.cameraPosition = frameState.camera.positionWC;
539535

540-
let tile;
541536
const levelZeroTiles = primitive._levelZeroTiles;
542537
const occluders =
543538
levelZeroTiles.length > 1 ? primitive._occluders : undefined;
@@ -550,18 +545,28 @@ function selectTilesForRendering(primitive, frameState) {
550545

551546
const customDataAdded = primitive._addHeightCallbacks;
552547
const customDataRemoved = primitive._removeHeightCallbacks;
553-
const frameNumber = frameState.frameNumber;
554548

555-
let len;
556-
if (customDataAdded.length > 0 || customDataRemoved.length > 0) {
557-
for (i = 0, len = levelZeroTiles.length; i < len; ++i) {
558-
tile = levelZeroTiles[i];
559-
tile._updateCustomData(frameNumber, customDataAdded, customDataRemoved);
549+
customDataAdded.forEach((data) => {
550+
const tile = levelZeroTiles.find((tile) =>
551+
Rectangle.contains(tile.rectangle, data.positionCartographic),
552+
);
553+
if (tile) {
554+
tile._addedCustomData.push(data);
560555
}
556+
});
561557

562-
customDataAdded.length = 0;
563-
customDataRemoved.length = 0;
564-
}
558+
customDataRemoved.forEach((data) => {
559+
const tile = levelZeroTiles.find((tile) =>
560+
Rectangle.contains(tile.rectangle, data.positionCartographic),
561+
);
562+
if (tile) {
563+
tile._removedCustomData.push(data);
564+
}
565+
});
566+
567+
levelZeroTiles.forEach((tile) => tile.updateCustomData());
568+
customDataAdded.length = 0;
569+
customDataRemoved.length = 0;
565570

566571
const camera = frameState.camera;
567572

@@ -577,8 +582,8 @@ function selectTilesForRendering(primitive, frameState) {
577582
);
578583

579584
// Traverse in depth-first, near-to-far order.
580-
for (i = 0, len = levelZeroTiles.length; i < len; ++i) {
581-
tile = levelZeroTiles[i];
585+
for (let i = 0; i < levelZeroTiles.length; ++i) {
586+
const tile = levelZeroTiles[i];
582587
primitive._tileReplacementQueue.markTileRendered(tile);
583588
if (!tile.renderable) {
584589
queueTileLoad(primitive, primitive._tileLoadQueueHigh, tile, frameState);
@@ -596,7 +601,7 @@ function selectTilesForRendering(primitive, frameState) {
596601
}
597602
}
598603

599-
primitive._lastSelectionFrameNumber = frameNumber;
604+
primitive._lastSelectionFrameNumber = frameState.frameNumber;
600605
}
601606

602607
function queueTileLoad(primitive, queue, tile, frameState) {
@@ -716,7 +721,7 @@ function visitTile(
716721
++debug.tilesVisited;
717722

718723
primitive._tileReplacementQueue.markTileRendered(tile);
719-
tile._updateCustomData(frameState.frameNumber);
724+
tile.updateCustomData();
720725

721726
if (tile.level > debug.maxDepthVisited) {
722727
debug.maxDepthVisited = tile.level;
@@ -1417,15 +1422,18 @@ function updateHeights(primitive, frameState) {
14171422
// Ensure stale position cache is cleared
14181423
tile.clearPositionCache();
14191424
tilesToUpdateHeights.shift();
1420-
primitive._lastTileIndex = 0;
14211425
continue;
14221426
}
14231427
const customData = tile.customData;
1424-
const customDataLength = customData.length;
1428+
if (!defined(tile._customDataIterator)) {
1429+
tile._customDataIterator = customData.values();
1430+
}
1431+
const customDataIterator = tile._customDataIterator;
14251432

14261433
let timeSliceMax = false;
1427-
for (i = primitive._lastTileIndex; i < customDataLength; ++i) {
1428-
const data = customData[i];
1434+
let nextData;
1435+
while (!(nextData = customDataIterator.next()).done) {
1436+
const data = nextData.value;
14291437

14301438
// No need to run this code when the tile is upsampled, because the height will be the same as its parent.
14311439
const terrainData = tile.data.terrainData;
@@ -1543,10 +1551,10 @@ function updateHeights(primitive, frameState) {
15431551
}
15441552

15451553
if (timeSliceMax) {
1546-
primitive._lastTileIndex = i;
1554+
tile._customDataIterator = customDataIterator;
15471555
break;
15481556
} else {
1549-
primitive._lastTileIndex = 0;
1557+
tile._customDataIterator = undefined;
15501558
tilesToUpdateHeights.shift();
15511559
}
15521560
}

packages/engine/Source/Scene/QuadtreeTile.js

Lines changed: 64 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import defined from "../Core/defined.js";
22
import DeveloperError from "../Core/DeveloperError.js";
33
import Rectangle from "../Core/Rectangle.js";
4+
import Cartographic from "../Core/Cartographic.js";
45
import QuadtreeTileLoadState from "./QuadtreeTileLoadState.js";
56
import TileSelectionResult from "./TileSelectionResult.js";
67

@@ -107,8 +108,10 @@ function QuadtreeTile(options) {
107108
this._distance = 0.0;
108109
this._loadPriority = 0.0;
109110

110-
this._customData = [];
111-
this._frameUpdated = undefined;
111+
this._customData = new Set();
112+
this._customDataIterator = undefined;
113+
this._addedCustomData = [];
114+
this._removedCustomData = [];
112115
this._lastSelectionResult = TileSelectionResult.NONE;
113116
this._lastSelectionResultFrame = undefined;
114117
this._loadedCallbacks = {};
@@ -311,52 +314,69 @@ QuadtreeTile.prototype.clearPositionCache = function () {
311314
}
312315
};
313316

314-
QuadtreeTile.prototype._updateCustomData = function (
315-
frameNumber,
316-
added,
317-
removed,
318-
) {
319-
let customData = this.customData;
320-
321-
let i;
322-
let data;
323-
let rectangle;
324-
325-
if (defined(added) && defined(removed)) {
326-
customData = customData.filter(function (value) {
327-
return removed.indexOf(value) === -1;
328-
});
329-
this._customData = customData;
330-
331-
rectangle = this._rectangle;
332-
for (i = 0; i < added.length; ++i) {
333-
data = added[i];
334-
if (Rectangle.contains(rectangle, data.positionCartographic)) {
335-
customData.push(data);
336-
}
337-
}
317+
QuadtreeTile.prototype.updateCustomData = function () {
318+
const added = this._addedCustomData;
319+
const removed = this._removedCustomData;
320+
if (added.length === 0 && removed.length === 0) {
321+
return;
322+
}
338323

339-
this._frameUpdated = frameNumber;
340-
} else {
341-
// interior or leaf tile, update from parent
342-
const parent = this._parent;
343-
if (defined(parent) && this._frameUpdated !== parent._frameUpdated) {
344-
customData.length = 0;
345-
346-
rectangle = this._rectangle;
347-
const parentCustomData = parent.customData;
348-
for (i = 0; i < parentCustomData.length; ++i) {
349-
data = parentCustomData[i];
350-
if (Rectangle.contains(rectangle, data.positionCartographic)) {
351-
customData.push(data);
352-
}
353-
}
324+
const customData = this.customData;
325+
for (let i = 0; i < added.length; ++i) {
326+
const data = added[i];
327+
customData.add(data);
328+
329+
const child = childTileAtPosition(this, data.positionCartographic);
330+
child._addedCustomData.push(data);
331+
}
332+
this._addedCustomData.length = 0;
354333

355-
this._frameUpdated = parent._frameUpdated;
334+
for (let i = 0; i < removed.length; ++i) {
335+
const data = removed[i];
336+
if (customData.has(data)) {
337+
customData.delete(data);
356338
}
339+
340+
const child = childTileAtPosition(this, data.positionCartographic);
341+
child._removedCustomData.push(data);
357342
}
343+
this._removedCustomData.length = 0;
358344
};
359345

346+
const splitPointScratch = new Cartographic();
347+
348+
/**
349+
* Determines which child tile that contains the specified position. Assumes the position is within
350+
* the bounds of the parent tile.
351+
* @private
352+
* @param {QuadtreeTile} tile - The parent tile.
353+
* @param {Cartographic} positionCartographic - The cartographic position.
354+
* @returns {QuadtreeTile} The child tile that contains the position.
355+
*/
356+
function childTileAtPosition(tile, positionCartographic) {
357+
// Can't assume that a given tiling scheme divides a parent into four tiles at its rectangle's center.
358+
// But we can safely take any child tile's rectangle and take its center-facing corner as the parent's split point.
359+
const nwChildRectangle = tile.northwestChild.rectangle;
360+
const tileSplitPoint = Rectangle.southeast(
361+
nwChildRectangle,
362+
splitPointScratch,
363+
);
364+
365+
const x = positionCartographic.longitude >= tileSplitPoint.longitude ? 1 : 0;
366+
const y = positionCartographic.latitude < tileSplitPoint.latitude ? 1 : 0;
367+
368+
switch (y * 2 + x) {
369+
case 0:
370+
return tile.northwestChild;
371+
case 1:
372+
return tile.northeastChild;
373+
case 2:
374+
return tile.southwestChild;
375+
default:
376+
return tile.southeastChild;
377+
}
378+
}
379+
360380
Object.defineProperties(QuadtreeTile.prototype, {
361381
/**
362382
* Gets the tiling scheme used to tile the surface.
@@ -522,9 +542,9 @@ Object.defineProperties(QuadtreeTile.prototype, {
522542
},
523543

524544
/**
525-
* An array of objects associated with this tile.
545+
* A set of objects associated with this tile.
526546
* @memberof QuadtreeTile.prototype
527-
* @type {Array}
547+
* @type {Set}
528548
*/
529549
customData: {
530550
get: function () {

packages/engine/Specs/Scene/QuadtreePrimitiveSpec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -899,7 +899,7 @@ describe("Scene/QuadtreePrimitive", function () {
899899

900900
let addedCallback = false;
901901
quadtree.forEachLoadedTile(function (tile) {
902-
addedCallback = addedCallback || tile.customData.length > 0;
902+
addedCallback = addedCallback || tile.customData.size > 0;
903903
});
904904

905905
expect(addedCallback).toEqual(true);
@@ -915,7 +915,7 @@ describe("Scene/QuadtreePrimitive", function () {
915915

916916
let removedCallback = true;
917917
quadtree.forEachLoadedTile(function (tile) {
918-
removedCallback = removedCallback && tile.customData.length === 0;
918+
removedCallback = removedCallback && tile.customData.size === 0;
919919
});
920920

921921
expect(removedCallback).toEqual(true);

0 commit comments

Comments
 (0)