perf(cables): draw with batched primitives instead of entities

The layer loaded two GeoJsonDataSources and a CustomDataSource: 6,459
entities. Every enable rebuilt them (a 100-250 ms task or three and
about 1 GB of heap that stayed until the next collection), every disable
removed them in a 130-150 ms task, and each horizon sweep that showed a
landing marker made Cesium create its billboard again and read the
terrain height under it once more.

The layer now makes the primitives Cesium built from those entities
itself: one GroundPolylinePrimitive for every line, one
BillboardCollection for the point markers, one PointPrimitiveCollection
for the reference tips and one static polyline Primitive for the stems
in view, rebuilt in a worker when a sweep moves a tip. A disable hides
them and an enable shows them again, so only the first enable builds
anything. A marker is clamped to the ground once, 48 per frame as
before; after that Cesium follows the terrain within its own time budget
and hiding or showing the marker reads nothing. A map-stack change
re-classifies the lines in place.

Lines keep the distance-display attribute the entity display gave them:
with a float attribute present Cesium keeps the 2.5 px width, and
without one it is stored as a byte and drawn 2 px.

Co-Authored-By: Claude Fable 5.2 <[email protected]>
This commit is contained in:
Mazeyar Moeini FeizabadiandClaude Fable 5.2 committed 2026-11-13 17:59:47 +01:00
1 parent b6c241a68e
commit 1923547d43
13 files changed
+1280 -1330

No files matched your search

+8 -15
View File
@@ -41,7 +41,7 @@ export function selectCableReferenceLabelWinners(
.sort(
(a, b) =>
a.distanceM - b.distanceM ||
String(a.entity?.id || '').localeCompare(String(b.entity?.id || '')),
String(a.id || '').localeCompare(String(b.id || '')),
)
.slice(0, cap);
}
@@ -248,16 +248,15 @@ export function createCableReferenceSweepGate({
}
/**
* Recompute one staticized stem on the sweep cadence. Replaces the former
* per-frame `CallbackProperty` pair: constant properties are redefined only
* when the camera-scaled tip moved beyond the settle epsilon, and the two
* preallocated position buffers alternate so each real change raises exactly
* one geometry notification (the localGeojson double-buffer pattern).
* @param {object} record Reference record with entity/base/tip/nextTip/buffers.
* Recompute one stem tip on the sweep cadence. The tip is moved in place, so
* the label entry keeps pointing at it, and only when the camera-scaled tip
* moved beyond the settle epsilon; the tip point follows with one buffer
* write, and the caller redraws the stems when any tip moved.
* @param {object} record Reference record with point/base/tip/nextTip.
* @param {Cesium.Cartesian3} cameraPositionWC
* @param {number} canvasHeight CSS-pixel canvas height.
* @param {number} fov Camera frustum field of view (radians).
* @returns {boolean} True when the stem geometry was redefined.
* @returns {boolean} True when the tip moved.
*/
export function updateCableReferenceStem(
@@ -289,13 +288,7 @@ export function updateCableReferenceStem(
return false;
}
Cesium.Cartesian3.clone(record.nextTip, record.tip);
record.stemPositionBufferIndex = 1 - record.stemPositionBufferIndex;
const stemPositions =
record.stemPositionBuffers[record.stemPositionBufferIndex];
stemPositions[0] = record.base;
stemPositions[1] = record.tip;
record.entity.position.setValue(record.tip);
record.entity.polyline.positions.setValue(stemPositions);
record.point.position = record.tip;
return true;
}