@bitruvius/geo-core 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,167 @@
1
+ Bitruvius SDK License Agreement
2
+
3
+ Copyright (c) 2026 Bitruvius, Inc. All rights reserved.
4
+
5
+ This Bitruvius SDK License Agreement (the "Agreement") is between you and
6
+ Bitruvius, Inc., a Delaware corporation ("Bitruvius"), and governs the use of
7
+ the Bitruvius SDK, including all @bitruvius npm packages, browser bundles,
8
+ WebAssembly binaries, documentation, and related materials distributed with
9
+ this notice (collectively, the "SDK").
10
+
11
+ 1. LICENSE GRANT
12
+
13
+ Subject to the terms of this Agreement, Bitruvius grants you a worldwide,
14
+ royalty-free, non-exclusive, non-transferable, revocable license to:
15
+
16
+ (a) use the SDK, in unmodified form, in any application, website, or
17
+ service you develop, whether commercial or non-commercial (an
18
+ "Application"); and
19
+ (b) reproduce and distribute the SDK solely as embedded in or bundled
20
+ with an Application (for example, as part of a compiled or bundled
21
+ build of the Application), provided the SDK is not distributed on a
22
+ standalone basis and remains subject to this Agreement.
23
+
24
+ 2. RESTRICTIONS
25
+
26
+ Except as expressly permitted in Section 1 or by applicable law
27
+ notwithstanding this limitation, you may not, and may not permit or assist
28
+ any third party to:
29
+
30
+ (a) modify, adapt, translate, or create derivative works of the SDK;
31
+ (b) redistribute, publish, sublicense, sell, rent, lease, or otherwise
32
+ make the SDK available on a standalone basis, including republishing
33
+ the SDK or any portion of it to any package registry or content
34
+ delivery network;
35
+ (c) reverse engineer, decompile, disassemble, decode, or otherwise
36
+ attempt to derive or reconstruct the source code, underlying ideas,
37
+ algorithms, file formats, or non-public APIs of the SDK, including
38
+ the WebAssembly binaries contained in the SDK;
39
+ (d) use the SDK, or any output or knowledge derived from it, to develop,
40
+ train, or improve a product or service that competes with the SDK or
41
+ with Bitruvius codec technology, including implementing or
42
+ reimplementing encoders or decoders for Bitruvius codec formats;
43
+ (e) remove, obscure, or alter any copyright, license, or attribution
44
+ notices contained in the SDK.
45
+
46
+ THIRD-PARTY COMPONENTS. Nothing in this Section 2 limits any right granted to
47
+ you directly by a third party under the license covering a third-party
48
+ component distributed with the SDK. Those components and their licenses are
49
+ identified in the NOTICES files distributed alongside them, together with a
50
+ copy of each license. Where this Agreement conflicts with such a license as
51
+ applied to that component, the third-party license governs that component.
52
+
53
+ THIRD-PARTY DATA AND SERVICES. The SDK is able to connect to data, imagery,
54
+ tilesets, and services operated by third parties, some of which require
55
+ credentials that you obtain in your own name. This Agreement grants you no
56
+ right, title, or license in or to any such data or service. Your access to and
57
+ use of them is governed solely by your agreement with the relevant provider,
58
+ including any obligation to display attribution or to restrict redistribution.
59
+ Bitruvius does not supply, endorse, or warrant any third-party data or service,
60
+ is not a party to your agreement with any provider, and bears no responsibility
61
+ for its availability, accuracy, terms, or charges.
62
+
63
+ 3. OWNERSHIP
64
+
65
+ The SDK is licensed, not sold. Bitruvius and its licensors retain all
66
+ right, title, and interest in and to the SDK, including all intellectual
67
+ property rights. No rights are granted except as expressly set out in this
68
+ Agreement.
69
+
70
+ 4. PRERELEASE VERSIONS
71
+
72
+ Versions of the SDK identified as alpha or beta (including any 0.x or
73
+ prerelease-tagged versions) are provided for evaluation and testing,
74
+ may change or be discontinued at any time without notice, and are provided
75
+ without any commitment of support or backward compatibility.
76
+
77
+ 5. NO WARRANTY
78
+
79
+ THE SDK IS PROVIDED "AS IS" AND "AS AVAILABLE", WITHOUT WARRANTY OF ANY
80
+ KIND, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY WARRANTIES OF
81
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, OR
82
+ NON-INFRINGEMENT. BITRUVIUS DOES NOT WARRANT THAT THE SDK WILL BE
83
+ UNINTERRUPTED, ERROR-FREE, OR SECURE.
84
+
85
+ 6. LIMITATION OF LIABILITY
86
+
87
+ TO THE MAXIMUM EXTENT PERMITTED BY LAW, IN NO EVENT WILL BITRUVIUS BE
88
+ LIABLE FOR ANY INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL, OR EXEMPLARY
89
+ DAMAGES, OR FOR ANY LOSS OF PROFITS, REVENUE, DATA, OR GOODWILL, ARISING
90
+ OUT OF OR RELATING TO THIS AGREEMENT OR THE SDK, EVEN IF ADVISED OF THE
91
+ POSSIBILITY OF SUCH DAMAGES. BITRUVIUS'S TOTAL AGGREGATE LIABILITY ARISING
92
+ OUT OF OR RELATING TO THIS AGREEMENT WILL NOT EXCEED ONE HUNDRED (100) US
93
+ DOLLARS.
94
+
95
+ 7. INDEMNIFICATION
96
+
97
+ You will defend, indemnify, and hold harmless Bitruvius and its officers,
98
+ directors, employees, and agents from and against any third-party claim,
99
+ demand, suit, or proceeding, and any resulting losses, damages, liabilities,
100
+ settlements, costs, and reasonable attorneys' fees, arising out of or
101
+ relating to: (a) your Applications or any product or service you build with
102
+ or distribute using the SDK; (b) your data, content, or the services you
103
+ direct the SDK to access; (c) your use of the SDK in violation of this
104
+ Agreement or of any applicable law; or (d) your infringement or
105
+ misappropriation of any third party's rights. Bitruvius will notify you of
106
+ any such claim, allow you to control the defense (except that any settlement
107
+ requiring an admission or payment by Bitruvius requires its consent, not to
108
+ be unreasonably withheld), and provide reasonable cooperation at your
109
+ expense.
110
+
111
+ BITRUVIUS PROVIDES NO INDEMNITY OF ANY KIND UNDER THIS AGREEMENT, INCLUDING
112
+ NO DEFENSE OR INDEMNITY AGAINST ANY CLAIM THAT THE SDK INFRINGES OR
113
+ MISAPPROPRIATES ANY PATENT, COPYRIGHT, TRADEMARK, TRADE SECRET, OR OTHER
114
+ INTELLECTUAL PROPERTY RIGHT. THE SDK IS LICENSED AT NO CHARGE AND IS
115
+ PROVIDED WITHOUT SUCH PROTECTION. If you require an intellectual property
116
+ indemnity, contact legal@bitruvius.com about a commercial license; any such
117
+ indemnity applies only if expressly agreed in a signed written agreement.
118
+
119
+ 8. TERMINATION
120
+
121
+ This Agreement is effective until terminated. It terminates automatically,
122
+ without notice, if you breach any of its terms. Upon termination you must
123
+ cease all use of the SDK and destroy all copies in your possession or
124
+ control, except that Applications already distributed to end users with the
125
+ SDK embedded may continue to be used. Sections 2, 3, 5, 6, 7, 8, and 9
126
+ survive termination.
127
+
128
+ 9. GENERAL
129
+
130
+ This Agreement constitutes the entire agreement between you and Bitruvius
131
+ concerning the SDK and supersedes any prior agreements. Bitruvius may
132
+ publish updated or supplemental terms for future SDK versions at
133
+ https://bitruvius.com/eula; the terms accompanying a given SDK version
134
+ govern that version. If any provision of this Agreement is held
135
+ unenforceable, the remaining provisions remain in full force.
136
+
137
+ GOVERNING LAW AND VENUE. This Agreement is governed by and construed in
138
+ accordance with the laws of the State of Georgia and the applicable federal
139
+ law of the United States, without regard to conflict-of-law provisions, and
140
+ excluding the United Nations Convention on Contracts for the International
141
+ Sale of Goods. The exclusive venue for any dispute arising out of or
142
+ relating to this Agreement or the SDK is the state or federal courts located
143
+ in Cobb County, Georgia, and each party consents to personal jurisdiction
144
+ and venue in those courts.
145
+
146
+ ASSIGNMENT. You may not assign or transfer this Agreement, in whole or in
147
+ part, without Bitruvius's prior written consent, and any attempted
148
+ assignment without that consent is void. Bitruvius may assign this
149
+ Agreement freely, including in connection with a merger, acquisition, or
150
+ sale of assets. This Agreement binds and benefits the parties' permitted
151
+ successors and assigns.
152
+
153
+ EXPORT COMPLIANCE. The SDK may be subject to export control laws,
154
+ including the U.S. Export Administration Regulations and applicable
155
+ sanctions programs. You represent that you are not located in, and will
156
+ not use or export the SDK in or to, any country or to any person or entity
157
+ subject to such restrictions, and that you will comply with all applicable
158
+ export and import laws.
159
+
160
+ U.S. GOVERNMENT END USERS. The SDK is "commercial computer software" and
161
+ "commercial computer software documentation" as those terms are used in
162
+ 48 C.F.R. 12.212 and 48 C.F.R. 227.7202. Any use, duplication, or
163
+ disclosure by or on behalf of the U.S. Government is governed solely by
164
+ the terms of this Agreement.
165
+
166
+ For commercial licensing of Bitruvius encoder technology, or any questions
167
+ about this Agreement, contact: legal@bitruvius.com
package/README.md ADDED
@@ -0,0 +1,133 @@
1
+ <p align="center">
2
+ <a href="https://bitruvius.com"><img src="https://cdn.bitruvius.com/brand/bitruvius-logo.png" width="360" alt="Bitruvius"></a>
3
+ </p>
4
+
5
+ # @bitruvius/geo-core
6
+
7
+ **The contracts and geospatial math every Bitruvius package shares, in pure TypeScript with no GPU, wasm or DOM.**
8
+
9
+ > **Internal building block.** This package exists so that
10
+ > [`@bitruvius/sdk-maplibre`](https://www.npmjs.com/package/@bitruvius/sdk-maplibre) and the
11
+ > Bitruvius codecs can resolve their dependencies on npm. It has no standalone product story.
12
+ > Unless you are deliberately building against it, install the SDK instead.
13
+
14
+ ## Why it is a separate package
15
+
16
+ The SDK is assembled from packages that must not know about each other. A codec must not
17
+ depend on a streaming engine. An engine must not depend on a codec. A renderer must not
18
+ depend on a viewer. Each of those boundaries is an interface, and an interface has to live
19
+ somewhere both sides can import it from without importing each other. That is this package.
20
+
21
+ The seams are real, not theoretical:
22
+
23
+ - `@bitruvius/turbo-lepcc` and `@bitruvius/bvc` both implement `I3sPointCloudDecoder`.
24
+ The `@bitruvius/i3s` engine imports the interface and never a codec, so you swap LEPCC for
25
+ BVC without touching streaming or rendering.
26
+ - `@bitruvius/draco`, `@bitruvius/meshopt` and `@bitruvius/ktx2` implement
27
+ `DracoGeometryDecoder`, `MeshoptDecoder` and `Ktx2Transcoder`. They are injected into
28
+ `@bitruvius/gltf`, which is why a codec package depends on geo-core and never on the parser.
29
+ - The renderers (`splats`, `ptcloud`, `mesh`, `point-symbols`, `raster`) are driven entirely
30
+ through `ViewerAdapter` and `FrameState`. They do not know MapLibre exists. A new host is
31
+ one adapter file, not a renderer fork.
32
+
33
+ The second reason is purity. Nothing here touches WebGL, wasm or the DOM, so the same code
34
+ runs on the main thread, inside a decode worker, and under `bun test` with no browser. That
35
+ is a requirement, not a preference: the worker packs a tile into the shared ENU grid, the
36
+ shader expands it back, and the tests prove the two agree. Both sides have to be reading
37
+ the same arithmetic from the same file.
38
+
39
+ Twenty other packages in this repository depend on geo-core. It depends on one thing,
40
+ [`@bitruvius/foundation`](https://www.npmjs.com/package/@bitruvius/foundation).
41
+
42
+ ## What is in it
43
+
44
+ **Contracts.** `Decoder` and `DecoderFactory`, `Renderer` and `TileHandle`, `ViewerAdapter`
45
+ and `FrameState`, plus the per-format decode seams: `GltfSplatDecoder`,
46
+ `I3sPointCloudDecoder`, `I3sMeshDecoder`, `CesiumPointDecoder`, `MeshTileDecoder`.
47
+
48
+ **The decoded model.** `DecodedSplatTile`, `DecodedPointTile` and `ContainerMeta` carry
49
+ quantized structure-of-arrays straight out of the decoder, because dequantization is
50
+ deferred to the render shaders. `PackedPointTile` and `PackedMeshTile` are the ENU-rebased,
51
+ GPU-ready forms the renderers upload. No floating-point geometry crosses this boundary.
52
+
53
+ **Geospatial math.** WGS84 geodetic to ECEF and back (Bowring closed form), ECEF to ENU,
54
+ and Web Mercator implemented from the public definition to match MapLibre's
55
+ `MercatorCoordinate` exactly, so this math is interchangeable with the viewer's own.
56
+ Column-major `mat4`/`mat3` and planes for frustum culling and clipping.
57
+
58
+ **Measurement.** What the SDK's measure tools are built on: slope and horizontal
59
+ distance, bearing, polyline length, polygon area and perimeter, the sampling grid behind
60
+ cut-and-fill volume, and contour extraction. These solve in the ENU tangent plane at the
61
+ feature being measured rather than on the ellipsoid, which is exact at city scale and
62
+ starts to drift past roughly 50 km; `haversineDistance` on the mean sphere is the
63
+ long-range cross-check and the axis measure for elevation profiles.
64
+
65
+ **Packing.** `packPointTileEnu`, `packI3sPointsEnu`, `packGltfPointsEnu` and `packMeshEnu`
66
+ rebase a decoded tile into the shared ENU meters frame and requantize it onto the
67
+ renderer's integer grid, so coordinates stay f32-exact however far the dataset sits from
68
+ the scene anchor. `extractHardEdges` derives the lines a person would draw if asked to
69
+ sketch a building, since no I3S or 3D Tiles service ships edge geometry.
70
+
71
+ **Placement.** `ModelPlacement` is the vocabulary the single-model layers expose: `upAxis`
72
+ preset, `heading`/`pitch`/`roll` in degrees, `scale`, `anchorPoint`. `placementBasis` and
73
+ `modelPlacementMatrix` compose the correct basis, including the mercator Y-flip that users
74
+ must never see. `QuantBoundsAccumulator` derives source-space bounds from streamed
75
+ quantized tiles, with robust percentiles, because splat captures arrive wrapped in a halo
76
+ of far-field background outliers that would otherwise hold the bounding box hostage.
77
+
78
+ **Style and metadata.** `compileSqlFilter` (Esri Building Scene Layer `filterExpression`)
79
+ and `compileStyleExpression` (3D Tiles declarative styling) compile to one AST and one
80
+ closure, evaluated per feature against a `PropertyGetter`. Hand-written tokenizer and
81
+ recursive-descent parser, no `eval`. I3S attributes, 3D Tiles batch tables and glTF
82
+ `EXT_structural_metadata` property tables all normalize into the same feature columns.
83
+
84
+ **Dequant reference.** The canonical inverse-quantization formulas. The GLSL shaders port
85
+ these functions, and this module is the parity oracle they are tested against.
86
+
87
+ Because geo-core re-exports `@bitruvius/foundation`, importing it also gives you the
88
+ domain-agnostic base: `TaskQueue`, `WorkerPool`, `detectCapabilities`,
89
+ `recommendDecodeBackend`, SDK config and `BitruviusError`.
90
+
91
+ ```ts
92
+ import { placementMatrix, toFloat32 } from '@bitruvius/geo-core';
93
+
94
+ const model = placementMatrix({ lng: -73.99, lat: 40.75, altitude: 0 });
95
+ gl.uniformMatrix4fv(uModel, false, toFloat32(model));
96
+ ```
97
+
98
+ ## Should you depend on it directly
99
+
100
+ Usually not. Install [`@bitruvius/sdk-maplibre`](https://www.npmjs.com/package/@bitruvius/sdk-maplibre)
101
+ and you get this package as a transitive dependency.
102
+
103
+ Depend on it directly when you are implementing one of its seams: a decoder for your own
104
+ container format that an SDK engine should stream, or a `ViewerAdapter` for a host the SDK
105
+ does not ship. Those are the cases where you want the interface and nothing else.
106
+
107
+ It is published for that purpose, not as a general-purpose geospatial math library. The API
108
+ tracks what the SDK needs and moves with SDK releases.
109
+
110
+ ```sh
111
+ npm i @bitruvius/geo-core
112
+ ```
113
+
114
+ ## Trademarks
115
+
116
+ Esri, ArcGIS, I3S and LEPCC are trademarks of Environmental Systems Research Institute, Inc.
117
+ Cesium and 3D Tiles are trademarks of Cesium GS, Inc. Khronos, glTF and KTX are trademarks
118
+ of The Khronos Group Inc. Google and Draco are trademarks of Google LLC. Niantic and SPZ are
119
+ trademarks of Niantic, Inc. MapLibre is a trademark of the MapLibre organization. All other
120
+ marks are the property of their respective owners.
121
+
122
+ These names are used solely to describe the data formats this software interoperates with.
123
+ WebGL is a trademark of The Khronos Group Inc.
124
+
125
+ Bitruvius is not affiliated with, sponsored by, or endorsed by any of them, and no such
126
+ relationship is implied.
127
+
128
+ ## License
129
+
130
+ Proprietary. The full terms ship as `LICENSE` inside this package, and are readable
131
+ before installing at [cdn.bitruvius.com/legal/sdk-license-v1.txt](https://cdn.bitruvius.com/legal/sdk-license-v1.txt).
132
+
133
+ © Bitruvius, Inc.
@@ -0,0 +1,34 @@
1
+ /**
2
+ * `CesiumPointDecoder` — the loose-coupling seam for OGC 3D-Tiles **point cloud**
3
+ * (`.pnts` / glTF-1.1 POINTS) streaming, the Cesium analog of `I3sPointCloudDecoder`.
4
+ * It decodes one tile buffer into a GPU-ready, ENU-rebased `PackedPointTile`
5
+ * (the same shape the `@bitruvius/ptcloud` renderer already consumes), regardless
6
+ * of whether the geometry is uncompressed or Draco-compressed.
7
+ *
8
+ * The engine and the MapLibre layer depend ONLY on this interface — never on a
9
+ * concrete codec — so the uncompressed reader and the Draco-seam variant are
10
+ * interchangeable.
11
+ */
12
+ import type { Mat4 } from '../geo/mat4';
13
+ import type { PackedPointTile } from '../geo/pack-point-enu';
14
+ export interface CesiumPointDecodeContext {
15
+ /** ECEF → shared-scene ENU (meters), column-major 4×4. */
16
+ readonly enuFromEcef: Mat4;
17
+ /** Cumulative tile transform: tile-local → ECEF. */
18
+ readonly tileTransformEcef: Mat4;
19
+ /** Requantization step (meters) for the i32 position grid. */
20
+ readonly grid: number;
21
+ }
22
+ /** Decodes one OGC 3D-Tiles point-cloud tile (`.pnts` / glTF-1.1 POINTS) into a
23
+ * GPU-ready, ENU-rebased `PackedPointTile`. */
24
+ export interface CesiumPointDecoder {
25
+ /** @param buffer - The tile bytes (`.pnts` or `.glb`).
26
+ * @param ctx - Georeference + requantize context (`CesiumPointDecodeContext`).
27
+ * @returns The ENU-rebased `PackedPointTile`. */
28
+ decode(buffer: ArrayBuffer, ctx: CesiumPointDecodeContext): Promise<PackedPointTile>;
29
+ /** Worker-pool size (worker variants) — size the engine's decode concurrency to match. */
30
+ readonly concurrency?: number;
31
+ dispose?(): void;
32
+ }
33
+ /** Built-in point-decoder identifiers a layer/engine can resolve to an impl. */
34
+ export type CesiumPointDecoderKind = 'pnts';
@@ -0,0 +1,191 @@
1
+ /**
2
+ * Decoder contract + the normalized decoded model.
3
+ *
4
+ * The model the SDK passes around is the **quantized** structure-of-
5
+ * arrays straight from the wasm decoder, plus the container metadata
6
+ * needed to dequantize. Dequant itself is deferred to the render
7
+ * shaders (upload compact bytes, expand on the GPU), so nothing here is
8
+ * floating-point geometry.
9
+ */
10
+ import type { DecodeBackend } from '@bitruvius/foundation';
11
+ /** Which decoded model a container holds: a gaussian `splat` cloud or a `pointcloud`. */
12
+ export type ModelProfile = 'splat' | 'pointcloud';
13
+ /** Splat-container dequant metadata — the constants the render shader needs to expand
14
+ * the quantized {@link DecodedSplatTile} arrays back to meters/colors. */
15
+ export interface SplatContainerMeta {
16
+ /** Discriminant. */
17
+ readonly kind: 'splat';
18
+ /** Position grid divisor exponent: `meters = q / 2^fractionalBits`. */
19
+ readonly fractionalBits: number;
20
+ /** Container SH-degree hint (0..3). A per-tile degree may also be present. */
21
+ readonly shDegree: number;
22
+ }
23
+ /** Point-cloud-container dequant metadata: the per-axis affine that maps the quantized
24
+ * i32 grid back to world coordinates. */
25
+ export interface PointContainerMeta {
26
+ /** Discriminant. */
27
+ readonly kind: 'pointcloud';
28
+ /** Per-axis dequant scale (world = `q * scale + offset`). f64 — full precision. */
29
+ readonly scale: readonly [number, number, number];
30
+ /** Per-axis dequant offset (world = `q * scale + offset`). f64. */
31
+ readonly offset: readonly [number, number, number];
32
+ }
33
+ /** The container-level dequant metadata, discriminated by `kind`. */
34
+ export type ContainerMeta = SplatContainerMeta | PointContainerMeta;
35
+ /** One decoded gaussian-splat tile: quantized structure-of-arrays straight from the
36
+ * wasm decoder (dequant deferred to the render shader). All arrays have `count`
37
+ * elements unless noted; SH bands are present per `shDegree`. */
38
+ export interface DecodedSplatTile {
39
+ /** Discriminant. */
40
+ readonly kind: 'splat';
41
+ /** Splat count in this tile. */
42
+ readonly count: number;
43
+ /** Quantized position grid, X axis (`meters = q / 2^fractionalBits`). */
44
+ readonly x: Int32Array;
45
+ /** Quantized position grid, Y axis. */
46
+ readonly y: Int32Array;
47
+ /** Quantized position grid, Z axis. */
48
+ readonly z: Int32Array;
49
+ /** Opacity byte in the rendered (post-sigmoid) domain. */
50
+ readonly opacity: Uint8Array;
51
+ /** Log-quantized scale, X axis (see {@link splatScale}). */
52
+ readonly scaleX: Uint8Array;
53
+ /** Log-quantized scale, Y axis. */
54
+ readonly scaleY: Uint8Array;
55
+ /** Log-quantized scale, Z axis. */
56
+ readonly scaleZ: Uint8Array;
57
+ /** Smallest-three rotation: slot index of the omitted (largest `|component|`). */
58
+ readonly rotIdx: Uint8Array;
59
+ /** Smallest-three rotation component A (see {@link decodeSmallestThree}). */
60
+ readonly rotA: Int16Array;
61
+ /** Smallest-three rotation component B. */
62
+ readonly rotB: Int16Array;
63
+ /** Smallest-three rotation component C. */
64
+ readonly rotC: Int16Array;
65
+ /** SH degree-0 DC byte, red channel. */
66
+ readonly colorR: Uint8Array;
67
+ /** SH degree-0 DC byte, green channel. */
68
+ readonly colorG: Uint8Array;
69
+ /** SH degree-0 DC byte, blue channel. */
70
+ readonly colorB: Uint8Array;
71
+ /** This tile's SH degree (0..3). */
72
+ readonly shDegree: number;
73
+ /** Band-1 SH rest coefficients (`count*9` bytes); present when `shDegree >= 1`. */
74
+ readonly sh1?: Uint8Array;
75
+ /** Band-2 SH rest coefficients (`count*15` bytes); present when `shDegree >= 2`. */
76
+ readonly sh2?: Uint8Array;
77
+ /** Band-3 SH rest coefficients (`count*21` bytes); present when `shDegree === 3`. */
78
+ readonly sh3?: Uint8Array;
79
+ }
80
+ /** One decoded point-cloud tile: quantized i32 positions plus the present per-point
81
+ * attribute planes (all of length `count`). Dequant deferred to the render shader. */
82
+ export interface DecodedPointTile {
83
+ /** Discriminant. */
84
+ readonly kind: 'pointcloud';
85
+ /** Point count in this tile. */
86
+ readonly count: number;
87
+ /** Quantized position grid, X axis (`world = q * scale + offset`). */
88
+ readonly x: Int32Array;
89
+ /** Quantized position grid, Y axis. */
90
+ readonly y: Int32Array;
91
+ /** Quantized position grid, Z axis. */
92
+ readonly z: Int32Array;
93
+ /** Red channel (full-range u16), if colored. */
94
+ readonly r?: Uint16Array;
95
+ /** Green channel (full-range u16), if colored. */
96
+ readonly g?: Uint16Array;
97
+ /** Blue channel (full-range u16), if colored. */
98
+ readonly b?: Uint16Array;
99
+ /** LAS intensity (full-range u16), if present. */
100
+ readonly intensity?: Uint16Array;
101
+ /** LAS classification code, if present. */
102
+ readonly classification?: Uint8Array;
103
+ /** GPS time low 32 bits — reassemble as `lo + hi * 2^32`. */
104
+ readonly gpsLo?: Uint32Array;
105
+ /** GPS time high 32 bits. */
106
+ readonly gpsHi?: Uint32Array;
107
+ }
108
+ /** One decoded tile, discriminated by `kind`: a {@link DecodedSplatTile} or a
109
+ * {@link DecodedPointTile}. */
110
+ export type DecodedTile = DecodedSplatTile | DecodedPointTile;
111
+ /** Result of `Decoder.open` — the cheap header parse: which profile the
112
+ * container is, its dequant metadata, and how many tiles it holds. */
113
+ export interface DecodedModel {
114
+ /** `'splat'` or `'pointcloud'`. */
115
+ readonly profile: ModelProfile;
116
+ /** Container-level dequant metadata ({@link ContainerMeta}). */
117
+ readonly meta: ContainerMeta;
118
+ /** Number of tiles {@link Decoder.decodeTiles} will yield. */
119
+ readonly tileCount: number;
120
+ }
121
+ /** Options for {@link Decoder.decodeTiles}. */
122
+ export interface DecodeOptions {
123
+ /** Decode backend to use; omit to auto-select (see {@link recommendDecodeBackend}). */
124
+ readonly backend?: DecodeBackend;
125
+ /** Abort signal — aborting stops the stream and rejects in-flight work. */
126
+ readonly signal?: AbortSignal;
127
+ /** Streaming hook called as each tile finishes decoding.
128
+ * @param tile - The freshly decoded tile.
129
+ * @param index - Its 0-based index within the container. */
130
+ readonly onTile?: (tile: DecodedTile, index: number) => void;
131
+ }
132
+ /**
133
+ * The decode seam a codec satisfies and the streaming engines consume. Obtain one from
134
+ * a {@link DecoderFactory.create}; `open` the bytes to read the header, then
135
+ * `decodeTiles` to stream the quantized tiles.
136
+ */
137
+ export interface Decoder {
138
+ /** The backend this decoder is actually running. */
139
+ readonly backend: DecodeBackend;
140
+ /**
141
+ * Parse the container header (cheap — no per-tile decode).
142
+ * @param bytes - The full container bytes.
143
+ * @returns The `DecodedModel` (profile + dequant meta + tile count).
144
+ * @throws BitruviusError `BVX_DECODE_FAILED` on bad magic / truncation / unsupported version.
145
+ */
146
+ open(bytes: Uint8Array): Promise<DecodedModel>;
147
+ /**
148
+ * Stream-decode every tile. Each yielded tile's typed arrays are transferred from the
149
+ * worker (zero-copy), so do not reuse a tile's arrays after the next iteration.
150
+ * @param model - The `DecodedModel` from `open`.
151
+ * @param bytes - The full container bytes (same buffer passed to `open`).
152
+ * @param opts - Optional `DecodeOptions` (backend / abort / `onTile`).
153
+ * @returns An async iterable of `DecodedTile`s in container order.
154
+ * @throws BitruviusError `BVX_DECODE_FAILED` on a corrupt tile; `BVX_WORKER_SPAWN_BLOCKED`
155
+ * when a worker backend cannot start.
156
+ */
157
+ decodeTiles(model: DecodedModel, bytes: Uint8Array, opts?: DecodeOptions): AsyncIterable<DecodedTile>;
158
+ /** Release the decoder's resources (worker pool, wasm instance). */
159
+ dispose(): void;
160
+ }
161
+ /**
162
+ * The factory every codec package exports (one per format: `bvc`, `turbolzp`, …). The
163
+ * loose-coupling seam between a streaming engine and a concrete codec.
164
+ *
165
+ * @example
166
+ * ```ts
167
+ * const decoder = await factory.create({ poolSize: 4 });
168
+ * const model = await decoder.open(bytes);
169
+ * for await (const tile of decoder.decodeTiles(model, bytes)) {
170
+ * // upload `tile` to the GPU
171
+ * }
172
+ * decoder.dispose();
173
+ * ```
174
+ */
175
+ export interface DecoderFactory {
176
+ /** Stable codec id (e.g. `'bvc'`). */
177
+ readonly id: string;
178
+ /** The backends this codec can run on this build. */
179
+ readonly supportedBackends: readonly DecodeBackend[];
180
+ /**
181
+ * Construct a {@link Decoder}.
182
+ * @param opts - Optional backend override and worker `poolSize`.
183
+ * @returns A ready {@link Decoder}.
184
+ * @throws BitruviusError `BVX_NO_BACKEND` when no requested/available backend is usable;
185
+ * `BVX_WASM_LOAD_FAILED` when the codec wasm cannot load.
186
+ */
187
+ create(opts?: {
188
+ backend?: DecodeBackend;
189
+ poolSize?: number;
190
+ }): Promise<Decoder>;
191
+ }