Skip to content

Commit 496f6eb

Browse files
committed
feat(engine): Add BufferPointCollection, BufferPolylineCollection, BufferPolygonCollection
1 parent eefbe2d commit 496f6eb

18 files changed

+3036
-6
lines changed

CHANGES.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Change Log
22

3+
## 1.140 - 2026-04-01
4+
5+
### @cesium/engine
6+
7+
#### Additions :tada:
8+
9+
- Added experimental, performance-focused vector primitive APIs: `BufferPointCollection`, `BufferPolylineCollection`, and `BufferPolygonCollection`. [#13212](https://github.com/CesiumGS/cesium/pull/13212)
10+
311
## 1.139 - 2026-03-02
412

513
### @cesium/engine

Tools/jsdoc/cesiumTags.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ exports.defineTags = function (dictionary) {
6464
// https://github.com/microsoft/TypeScript/issues/22160#issuecomment-2021459033
6565
// https://devblogs.microsoft.com/typescript/announcing-typescript-5-5-beta/#type-imports-in-jsdoc
6666
dictionary.defineTag("import", {
67-
canHaveType: true,
68-
canHaveName: true,
6967
mustHaveValue: true,
68+
canHaveType: false,
69+
canHaveName: false,
7070
});
7171
};

packages/engine/Source/Core/BoundingSphere.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@ BoundingSphere.fromRectangle3D = function (
350350
* algorithms, a naive algorithm and Ritter's algorithm. The smaller of the two spheres is used to
351351
* ensure a tight fit.
352352
*
353-
* @param {number[]} [positions] An array of points that the bounding sphere will enclose. Each point
353+
* @param {number[]|TypedArray} [positions] An array of points that the bounding sphere will enclose. Each point
354354
* is formed from three elements in the array in the order X, Y, Z.
355355
* @param {Cartesian3} [center=Cartesian3.ZERO] The position to which the positions are relative, which need not be the
356356
* origin of the coordinate system. This is useful when the positions are to be used for

packages/engine/Source/Core/DeveloperError.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ DeveloperError.prototype.toString = function () {
6565
};
6666

6767
/**
68-
* @private
68+
* @returns {never}
69+
* @ignore
6970
*/
7071
DeveloperError.throwInstantiationError = function () {
7172
throw new DeveloperError(
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// @ts-check
2+
3+
import DeveloperError from "./DeveloperError.js";
4+
5+
/**
6+
* Checks that a condition is truthy, throwing a specified message if condition
7+
* fails. The `asserts condition` return type allows TypeScript to narrow the
8+
* types of the condition and enforce stricter types without further if/else
9+
* checks or nullish coalescing.
10+
*
11+
* @example
12+
* assert(object.optionalProperty, 'Missing .optionalProperty');
13+
* object.optionalProperty.toString(); // safe; no type error.
14+
*
15+
* @function
16+
*
17+
* @param {*} condition
18+
* @param {string} msg
19+
* @ignore
20+
*/
21+
function assert(condition, msg) {
22+
if (!condition) {
23+
throw new DeveloperError(msg);
24+
}
25+
}
26+
27+
export default assert;

packages/engine/Source/Renderer/RenderState.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,7 @@ let renderStateCache = {};
456456
* @see DrawCommand
457457
* @see ClearCommand
458458
*
459-
* @private
459+
* @ignore
460460
*/
461461
RenderState.fromCache = function (renderState) {
462462
const partialKey = JSON.stringify(renderState);
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
// @ts-check
2+
3+
import BufferPrimitive from "./BufferPrimitive.js";
4+
import Cartesian3 from "../Core/Cartesian3.js";
5+
import assert from "../Core/assert.js";
6+
import BufferPrimitiveCollection from "./BufferPrimitiveCollection.js";
7+
8+
/** @import BufferPointCollection from "./BufferPointCollection.js"; */
9+
10+
const { ERR_CAPACITY } = BufferPrimitiveCollection.Error;
11+
12+
const scratchCartesian = new Cartesian3();
13+
14+
/**
15+
* View bound to the underlying buffer data of a {@link BufferPointCollection}.
16+
*
17+
* <p>BufferPoint instances are {@link https://en.wikipedia.org/wiki/Flyweight_pattern|flyweights}:
18+
* a single BufferPoint instance can be temporarily bound to any conceptual
19+
* "point" in a BufferPointCollection, allowing very large collections to be
20+
* iterated and updated with a minimal memory footprint.</p>
21+
*
22+
* Represented as one (1) position.
23+
*
24+
* @see BufferPointCollection
25+
* @see BufferPrimitive
26+
* @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy.
27+
* @extends BufferPrimitive
28+
*/
29+
class BufferPoint extends BufferPrimitive {
30+
/**
31+
* @type {BufferPointCollection}
32+
* @ignore
33+
*/
34+
_collection = null;
35+
36+
/** @ignore */
37+
static Layout = {
38+
...BufferPrimitive.Layout,
39+
40+
/**
41+
* Offset in position array to current point vertex, number of VEC3 elements.
42+
* @type {number}
43+
* @ignore
44+
*/
45+
POSITION_OFFSET_U32: BufferPrimitive.Layout.__BYTE_LENGTH,
46+
47+
/**
48+
* @type {number}
49+
* @ignore
50+
*/
51+
__BYTE_LENGTH: BufferPrimitive.Layout.__BYTE_LENGTH + 4,
52+
};
53+
54+
/////////////////////////////////////////////////////////////////////////////
55+
// LIFECYCLE
56+
57+
/**
58+
* Copies data from source point to result.
59+
*
60+
* @param {BufferPoint} point
61+
* @param {BufferPoint} result
62+
* @return {BufferPoint}
63+
* @override
64+
*/
65+
static clone(point, result) {
66+
super.clone(point, result);
67+
result.setPosition(point.getPosition(scratchCartesian));
68+
return result;
69+
}
70+
71+
/////////////////////////////////////////////////////////////////////////////
72+
// GEOMETRY
73+
74+
/**
75+
* Offset in collection position array to position of this point, number
76+
* of VEC3 elements.
77+
*
78+
* @type {number}
79+
* @readonly
80+
* @ignore
81+
*/
82+
get vertexOffset() {
83+
return this._getUint32(BufferPoint.Layout.POSITION_OFFSET_U32);
84+
}
85+
86+
/**
87+
* Count of positions (vertices) in this primitive. Always 1.
88+
*
89+
* @type {number}
90+
* @readonly
91+
*/
92+
get vertexCount() {
93+
return 1;
94+
}
95+
96+
/**
97+
* Gets the position of this point.
98+
*
99+
* @param {Cartesian3} [result]
100+
* @returns {Cartesian3}
101+
*/
102+
getPosition(result) {
103+
const positionF64 = this._collection._positionF64;
104+
return Cartesian3.fromArray(positionF64, this.vertexOffset * 3, result);
105+
}
106+
107+
/**
108+
* Sets the position of this point.
109+
*
110+
* @param {Cartesian3} position
111+
*/
112+
setPosition(position) {
113+
const collection = this._collection;
114+
const vertexOffset = this.vertexOffset;
115+
116+
//>>includeStart('debug', pragmas.debug);
117+
assert(vertexOffset < collection.vertexCountMax, ERR_CAPACITY);
118+
//>>includeEnd('debug');
119+
120+
collection._positionF64[vertexOffset * 3] = position.x;
121+
collection._positionF64[vertexOffset * 3 + 1] = position.y;
122+
collection._positionF64[vertexOffset * 3 + 2] = position.z;
123+
124+
collection._makeDirtyBoundingVolume();
125+
}
126+
127+
/////////////////////////////////////////////////////////////////////////////
128+
// DEBUG
129+
130+
/**
131+
* Returns a JSON-serializable object representing the point. This encoding
132+
* is not memory-efficient, and should generally be used for debugging and
133+
* testing.
134+
*
135+
* @returns {Object} JSON-serializable object.
136+
* @override
137+
*/
138+
toJSON() {
139+
return {
140+
...super.toJSON(),
141+
position: Cartesian3.pack(this.getPosition(), []),
142+
};
143+
}
144+
}
145+
146+
export default BufferPoint;
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
// @ts-check
2+
3+
import BufferPrimitiveCollection from "./BufferPrimitiveCollection.js";
4+
import BufferPoint from "./BufferPoint.js";
5+
import Cartesian3 from "../Core/Cartesian3.js";
6+
import Frozen from "../Core/Frozen.js";
7+
8+
/** @import Color from "../Core/Color.js"; */
9+
/** @import FrameState from "./FrameState.js" */
10+
11+
/**
12+
* @typedef {object} BufferPointOptions
13+
* @property {boolean} [show=true]
14+
* @property {Color} [color=Color.WHITE]
15+
* @property {Cartesian3} [position=Cartesian3.ZERO]
16+
* @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy.
17+
*/
18+
19+
/**
20+
* Collection of points held in ArrayBuffer storage for performance and memory optimization.
21+
*
22+
* <p>Default buffer memory allocation is arbitrary, and collections cannot be resized,
23+
* so specific per-buffer capacities should be provided in the collection
24+
* constructor when available.</p>
25+
*
26+
* @example
27+
* const collection = new BufferPointCollection({primitiveCountMax: 1024});
28+
*
29+
* const point = new BufferPoint();
30+
*
31+
* // Create a new point, temporarily bound to 'point' local variable.
32+
* collection.add({
33+
* position: new Cartesian3(0.0, 0.0, 0.0),
34+
* color: Color.WHITE,
35+
* }, point);
36+
*
37+
* // Iterate over all points in collection, temporarily binding 'point'
38+
* // local variable to each, and updating point color.
39+
* for (let i = 0; i < collection.primitiveCount; i++) {
40+
* collection.get(i, point);
41+
* point.setColor(Color.RED);
42+
* }
43+
*
44+
* @see BufferPoint
45+
* @see BufferPrimitiveCollection
46+
* @extends BufferPrimitiveCollection<BufferPoint>
47+
* @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy.
48+
*/
49+
class BufferPointCollection extends BufferPrimitiveCollection {
50+
/**
51+
* @param {object} options
52+
* @param {number} [options.primitiveCountMax=BufferPrimitiveCollection.DEFAULT_CAPACITY]
53+
* @param {boolean} [options.show=true]
54+
* @param {boolean} [options.debugShowBoundingVolume=false]
55+
*/
56+
constructor(options = Frozen.EMPTY_OBJECT) {
57+
super({ ...options, vertexCountMax: options.primitiveCountMax });
58+
}
59+
60+
_getCollectionClass() {
61+
return BufferPointCollection;
62+
}
63+
64+
_getPrimitiveClass() {
65+
return BufferPoint;
66+
}
67+
68+
/////////////////////////////////////////////////////////////////////////////
69+
// COLLECTION LIFECYCLE
70+
71+
/**
72+
* @param {BufferPointCollection} collection
73+
* @returns {BufferPointCollection}
74+
* @override
75+
* @ignore
76+
*/
77+
static _cloneEmpty(collection) {
78+
return new BufferPointCollection({
79+
primitiveCountMax: collection.primitiveCountMax,
80+
});
81+
}
82+
83+
/////////////////////////////////////////////////////////////////////////////
84+
// PRIMITIVE LIFECYCLE
85+
86+
/**
87+
* Adds a new point to the collection, with the specified options. A
88+
* {@link BufferPoint} instance is linked to the new point, using
89+
* the 'result' argument if given, or a new instance if not. For repeated
90+
* calls, prefer to reuse a single BufferPoint instance rather than
91+
* allocating a new instance on each call.
92+
*
93+
* @param {BufferPointOptions} options
94+
* @param {BufferPoint} result
95+
* @returns {BufferPoint}
96+
* @override
97+
*/
98+
add(options, result = new BufferPoint()) {
99+
super.add(options, result);
100+
101+
result._setUint32(
102+
BufferPoint.Layout.POSITION_OFFSET_U32,
103+
this._positionCount++,
104+
);
105+
result.setPosition(options.position ?? Cartesian3.ZERO);
106+
107+
return result;
108+
}
109+
110+
/////////////////////////////////////////////////////////////////////////////
111+
// RENDER
112+
113+
/**
114+
* @param {FrameState} frameState
115+
* @ignore
116+
*/
117+
update(frameState) {
118+
super.update(frameState);
119+
}
120+
}
121+
122+
export default BufferPointCollection;

0 commit comments

Comments
 (0)