@ifc-lite/server-client 1.23.1 → 2.0.0
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/README.md +28 -1
- package/dist/client.d.ts +29 -3
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +89 -162
- package/dist/client.js.map +1 -1
- package/dist/data-model-decoder.d.ts +4 -0
- package/dist/data-model-decoder.d.ts.map +1 -1
- package/dist/data-model-decoder.js +14 -8
- package/dist/data-model-decoder.js.map +1 -1
- package/dist/geometry-diagnostics-types.d.ts +124 -0
- package/dist/geometry-diagnostics-types.d.ts.map +1 -0
- package/dist/geometry-diagnostics-types.js +5 -0
- package/dist/geometry-diagnostics-types.js.map +1 -0
- package/dist/parquet-columns.d.ts +81 -0
- package/dist/parquet-columns.d.ts.map +1 -0
- package/dist/parquet-columns.js +97 -0
- package/dist/parquet-columns.js.map +1 -0
- package/dist/parquet-decoder.d.ts +3 -1
- package/dist/parquet-decoder.d.ts.map +1 -1
- package/dist/parquet-decoder.js +40 -70
- package/dist/parquet-decoder.js.map +1 -1
- package/dist/parquet-nullable.d.ts +26 -0
- package/dist/parquet-nullable.d.ts.map +1 -0
- package/dist/parquet-nullable.js +36 -0
- package/dist/parquet-nullable.js.map +1 -0
- package/dist/parquet-rotation.d.ts +70 -0
- package/dist/parquet-rotation.d.ts.map +1 -0
- package/dist/parquet-rotation.js +96 -0
- package/dist/parquet-rotation.js.map +1 -0
- package/dist/parquet-stream-events.d.ts +23 -0
- package/dist/parquet-stream-events.d.ts.map +1 -0
- package/dist/parquet-stream-events.js +133 -0
- package/dist/parquet-stream-events.js.map +1 -0
- package/dist/parquet-tables.d.ts +11 -1
- package/dist/parquet-tables.d.ts.map +1 -1
- package/dist/parquet-tables.js +54 -94
- package/dist/parquet-tables.js.map +1 -1
- package/dist/parse-query.d.ts +18 -0
- package/dist/parse-query.d.ts.map +1 -0
- package/dist/parse-query.js +21 -0
- package/dist/parse-query.js.map +1 -0
- package/dist/symbolic-types.d.ts +168 -0
- package/dist/symbolic-types.d.ts.map +1 -0
- package/dist/symbolic-types.js +5 -0
- package/dist/symbolic-types.js.map +1 -0
- package/dist/types.d.ts +29 -205
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +2 -1
- package/dist/types.js.map +1 -1
- package/package.json +4 -3
package/dist/parquet-decoder.js
CHANGED
|
@@ -2,10 +2,7 @@
|
|
|
2
2
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
4
|
import { buildMeshesFromTables, buildMeshesFromOptimizedTables } from './parquet-tables.js';
|
|
5
|
-
|
|
6
|
-
// WASM initialization state
|
|
7
|
-
let parquetInitialized = false;
|
|
8
|
-
let parquetModule = null;
|
|
5
|
+
let parquetInit = null;
|
|
9
6
|
/**
|
|
10
7
|
* Ensure parquet-wasm WASM module is initialized.
|
|
11
8
|
* This MUST be called before using any parquet functions.
|
|
@@ -13,73 +10,39 @@ let parquetModule = null;
|
|
|
13
10
|
* @returns Initialized parquet-wasm module
|
|
14
11
|
*/
|
|
15
12
|
export async function ensureParquetInit() {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
console.warn('[parquet-decoder] ESM build initialized but readParquet not found');
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
catch (e) {
|
|
47
|
-
console.warn('[parquet-decoder] ESM import failed:', e);
|
|
48
|
-
}
|
|
49
|
-
// Strategy 2: Try web build with fetch (alternative for browsers)
|
|
50
|
-
try {
|
|
51
|
-
parquet = await import('parquet-wasm/esm/arrow2.js');
|
|
52
|
-
if (typeof parquet.default === 'function') {
|
|
53
|
-
console.log('[parquet-decoder] Trying web init with node_modules path...');
|
|
54
|
-
// Try common paths where WASM might be served
|
|
55
|
-
const wasmPaths = [
|
|
56
|
-
'/node_modules/parquet-wasm/esm/arrow2_bg.wasm',
|
|
57
|
-
'./node_modules/parquet-wasm/esm/arrow2_bg.wasm',
|
|
58
|
-
];
|
|
59
|
-
for (const wasmPath of wasmPaths) {
|
|
60
|
-
try {
|
|
61
|
-
const response = await fetch(wasmPath);
|
|
62
|
-
if (response.ok) {
|
|
63
|
-
console.log('[parquet-decoder] Found WASM at:', wasmPath);
|
|
64
|
-
await parquet.default(response);
|
|
65
|
-
if (typeof parquet.readParquet === 'function') {
|
|
66
|
-
parquetModule = parquet;
|
|
67
|
-
parquetInitialized = true;
|
|
68
|
-
console.log('[parquet-decoder] Web init successful');
|
|
69
|
-
return parquet;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
catch {
|
|
74
|
-
// Try next path
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
13
|
+
parquetInit ?? (parquetInit = loadParquet().catch((err) => {
|
|
14
|
+
// Do not cache a failed init: a later call (e.g. after the WASM asset
|
|
15
|
+
// becomes reachable) should be able to try again.
|
|
16
|
+
parquetInit = null;
|
|
17
|
+
throw err;
|
|
18
|
+
}));
|
|
19
|
+
return parquetInit;
|
|
20
|
+
}
|
|
21
|
+
async function loadParquet() {
|
|
22
|
+
// Import the package entry point and let its export map choose the build.
|
|
23
|
+
// Deep-importing a build (this module used `parquet-wasm/esm/arrow2.js`)
|
|
24
|
+
// breaks on parquet-wasm 0.6+, where that path no longer exists (#3845).
|
|
25
|
+
const parquet = await import('parquet-wasm');
|
|
26
|
+
// The browser/bundler ESM build does nothing until its default export is
|
|
27
|
+
// awaited; without it every call throws `Cannot read properties of
|
|
28
|
+
// undefined (reading '__wbindgen_malloc')`. The Node build initializes
|
|
29
|
+
// itself on import and exposes no default init, hence the guard rather
|
|
30
|
+
// than an unconditional call. Same shape as
|
|
31
|
+
// packages/export/src/columns-to-parquet.ts.
|
|
32
|
+
//
|
|
33
|
+
// The cast is the price of that guard: TypeScript resolves the export map
|
|
34
|
+
// under the `node` condition, so it only ever sees the Node build's types,
|
|
35
|
+
// where `default` is the CommonJS namespace object and not callable. Which
|
|
36
|
+
// build actually loads is a runtime question, so ask at runtime.
|
|
37
|
+
const init = parquet.default;
|
|
38
|
+
if (typeof init === 'function') {
|
|
39
|
+
await init();
|
|
78
40
|
}
|
|
79
|
-
|
|
80
|
-
|
|
41
|
+
if (typeof parquet.readParquet !== 'function') {
|
|
42
|
+
throw new Error('parquet-wasm: module loaded but readParquet is missing. ' +
|
|
43
|
+
'Install a supported parquet-wasm version (see this package\'s peerDependencies).');
|
|
81
44
|
}
|
|
82
|
-
|
|
45
|
+
return parquet;
|
|
83
46
|
}
|
|
84
47
|
/**
|
|
85
48
|
* Decode a Parquet geometry response from the server.
|
|
@@ -177,9 +140,15 @@ export async function decodeOptimizedParquetGeometry(data, vertexMultiplier = 10
|
|
|
177
140
|
// Read header
|
|
178
141
|
const version = view.getUint8(offset);
|
|
179
142
|
offset += 1;
|
|
180
|
-
|
|
143
|
+
// v2: no rotation columns (pre-#3575), every instance decodes as identity.
|
|
144
|
+
// v3: instance table carries rot0..rot8 (#3575) — `readRotationColumns`
|
|
145
|
+
// (parquet-tables.ts) reads them when present; `buildMeshesFromOptimizedTables`
|
|
146
|
+
// rejects a v3 payload that omits/truncates them (malformed wire data),
|
|
147
|
+
// rather than silently falling back to identity as it would for v2.
|
|
148
|
+
if (version !== 2 && version !== 3) {
|
|
181
149
|
throw new Error(`Unsupported optimized Parquet version: ${version}`);
|
|
182
150
|
}
|
|
151
|
+
const wireVersion = version;
|
|
183
152
|
const flags = view.getUint8(offset);
|
|
184
153
|
offset += 1;
|
|
185
154
|
const hasNormals = (flags & 1) !== 0;
|
|
@@ -225,6 +194,7 @@ export async function decodeOptimizedParquetGeometry(data, vertexMultiplier = 10
|
|
|
225
194
|
indexArrow,
|
|
226
195
|
hasNormals,
|
|
227
196
|
vertexMultiplier,
|
|
197
|
+
wireVersion,
|
|
228
198
|
});
|
|
229
199
|
}
|
|
230
200
|
//# sourceMappingURL=parquet-decoder.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parquet-decoder.js","sourceRoot":"","sources":["../src/parquet-decoder.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,sEAAsE;AACtE,4DAA4D;AAU5D,OAAO,EAAE,qBAAqB,EAAE,8BAA8B,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"parquet-decoder.js","sourceRoot":"","sources":["../src/parquet-decoder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Null-safe bulk extraction for a nullable numeric Arrow column.
|
|
3
|
+
*
|
|
4
|
+
* `Vector.toArray()` returns the column's raw values buffer directly (its
|
|
5
|
+
* fast path for a primitive type), and that buffer carries whatever bits sit
|
|
6
|
+
* at a NULL row's slot — 0 in practice for a Float64/Int column written by
|
|
7
|
+
* every writer this project has observed (DuckDB, parquet-wasm), not a
|
|
8
|
+
* detectable sentinel. `data-model-decoder.ts`'s own module doc cites
|
|
9
|
+
* `toArray()` as "10-20x faster" than per-row `.get(i)` for BULK STRING
|
|
10
|
+
* extraction; that justification does not carry over to a NULLABLE numeric
|
|
11
|
+
* column, where `.get(i)` is the only call that reports a null row as `null`
|
|
12
|
+
* instead of a real-looking `0`.
|
|
13
|
+
*
|
|
14
|
+
* Two server-emitted columns are nullable `Float64` today: spatial-node
|
|
15
|
+
* `elevation` (a storey whose elevation cannot be resolved, see
|
|
16
|
+
* `apps/server/src/services/data_model/spatial.rs`) and material
|
|
17
|
+
* `thickness` (every non-layer material association — single material,
|
|
18
|
+
* material list, material constituent — always has `thickness: None`, see
|
|
19
|
+
* `apps/server/src/services/data_model/materials.rs`). Both used to decode
|
|
20
|
+
* here as `0` instead of `undefined` — a legitimate "no elevation" storey or
|
|
21
|
+
* "not a layer" material silently reading as a real, wrong measurement.
|
|
22
|
+
*/
|
|
23
|
+
export declare function nullableFloat64Column(table: {
|
|
24
|
+
getChild(name: string): unknown;
|
|
25
|
+
}, name: string): (number | null)[] | undefined;
|
|
26
|
+
//# sourceMappingURL=parquet-nullable.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parquet-nullable.d.ts","sourceRoot":"","sources":["../src/parquet-nullable.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,EAC1C,IAAI,EAAE,MAAM,GACX,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,GAAG,SAAS,CAU/B"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
/**
|
|
5
|
+
* Null-safe bulk extraction for a nullable numeric Arrow column.
|
|
6
|
+
*
|
|
7
|
+
* `Vector.toArray()` returns the column's raw values buffer directly (its
|
|
8
|
+
* fast path for a primitive type), and that buffer carries whatever bits sit
|
|
9
|
+
* at a NULL row's slot — 0 in practice for a Float64/Int column written by
|
|
10
|
+
* every writer this project has observed (DuckDB, parquet-wasm), not a
|
|
11
|
+
* detectable sentinel. `data-model-decoder.ts`'s own module doc cites
|
|
12
|
+
* `toArray()` as "10-20x faster" than per-row `.get(i)` for BULK STRING
|
|
13
|
+
* extraction; that justification does not carry over to a NULLABLE numeric
|
|
14
|
+
* column, where `.get(i)` is the only call that reports a null row as `null`
|
|
15
|
+
* instead of a real-looking `0`.
|
|
16
|
+
*
|
|
17
|
+
* Two server-emitted columns are nullable `Float64` today: spatial-node
|
|
18
|
+
* `elevation` (a storey whose elevation cannot be resolved, see
|
|
19
|
+
* `apps/server/src/services/data_model/spatial.rs`) and material
|
|
20
|
+
* `thickness` (every non-layer material association — single material,
|
|
21
|
+
* material list, material constituent — always has `thickness: None`, see
|
|
22
|
+
* `apps/server/src/services/data_model/materials.rs`). Both used to decode
|
|
23
|
+
* here as `0` instead of `undefined` — a legitimate "no elevation" storey or
|
|
24
|
+
* "not a layer" material silently reading as a real, wrong measurement.
|
|
25
|
+
*/
|
|
26
|
+
export function nullableFloat64Column(table, name) {
|
|
27
|
+
const col = table.getChild(name);
|
|
28
|
+
if (!col)
|
|
29
|
+
return undefined;
|
|
30
|
+
const raw = col.toArray();
|
|
31
|
+
// The common case (no nulls in this column/payload) keeps the fast bulk path.
|
|
32
|
+
if (col.nullCount === 0)
|
|
33
|
+
return Array.from(raw);
|
|
34
|
+
return Array.from(raw, (value, i) => (col.get(i) === null ? null : value));
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=parquet-nullable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parquet-nullable.js","sourceRoot":"","sources":["../src/parquet-nullable.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,sEAAsE;AACtE,4DAA4D;AAE5D;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,qBAAqB,CACnC,KAA0C,EAC1C,IAAY;IAEZ,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAGlB,CAAC;IACd,IAAI,CAAC,GAAG;QAAE,OAAO,SAAS,CAAC;IAC3B,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC;IAC1B,8EAA8E;IAC9E,IAAI,GAAG,CAAC,SAAS,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChD,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;AAC7E,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-instance rotation, shared by both Parquet transports (issues #3575, #3888).
|
|
3
|
+
*
|
|
4
|
+
* Split out of `parquet-tables.ts` to stay under that file's module-size
|
|
5
|
+
* budget (see `parquet-columns.ts` for the same reasoning applied to the
|
|
6
|
+
* shared trailing columns).
|
|
7
|
+
*
|
|
8
|
+
* The server dedupes rotated `IfcMappedItem` / shared-`IfcRepresentationMap`
|
|
9
|
+
* occurrences by storing ONE template mesh in a canonical/local frame and
|
|
10
|
+
* carrying each instance's rotation (row-major 3x3, `rot0..rot8`) alongside
|
|
11
|
+
* its `origin_x/y/z`. The optimized transport has done this since #3575 and the
|
|
12
|
+
* flat one since `-parquet-v6` (#3888); the columns, the frame and the
|
|
13
|
+
* reconstruction are identical, which is why this module is shared rather than
|
|
14
|
+
* copied. Reconstruction contract:
|
|
15
|
+
* `world = origin + R * template_position` — the SAME `origin`-only contract
|
|
16
|
+
* as before (#1841) when `R` is the identity, which is what the server emits
|
|
17
|
+
* for every instance it did not verify a rotation-aware placement for.
|
|
18
|
+
*/
|
|
19
|
+
import type { ArrowTableLike } from './parquet-tables.js';
|
|
20
|
+
/** The nine `rot0..rot8` columns, present together or not at all. */
|
|
21
|
+
export type RotationColumns = ArrayLike<number>[];
|
|
22
|
+
/**
|
|
23
|
+
* Read the rotation columns, or `undefined` when the payload does not carry
|
|
24
|
+
* them — an optimized wire-version-2 payload (#3575), or a flat `-parquet-v5`
|
|
25
|
+
* blob (#3888). Callers then fall back to the identity rotation, which is
|
|
26
|
+
* exactly the behaviour those payloads were written for.
|
|
27
|
+
*
|
|
28
|
+
* `onAbsent` says what a MISSING or short block means to the caller, in the
|
|
29
|
+
* caller's own terms rather than in one transport's version numbering.
|
|
30
|
+
* `'identity'` is the reading above. `'throw'` is for a payload whose own
|
|
31
|
+
* header states the columns are there: the optimized format's version 3 is
|
|
32
|
+
* only emitted once a non-identity rotation has actually been written
|
|
33
|
+
* (`optimized_wire_version` in `parquet_instancing.rs`), so absence there is
|
|
34
|
+
* truncated wire data, and decoding it as identity would place a genuinely
|
|
35
|
+
* rotated occurrence at the wrong orientation with no signal why.
|
|
36
|
+
*
|
|
37
|
+
* The flat transport has no such header — it cannot tell a truncated v6 mesh
|
|
38
|
+
* table from a genuine v5 one — so it passes `'identity'`, which is right for
|
|
39
|
+
* the v5 case and no worse than throwing for the other.
|
|
40
|
+
*/
|
|
41
|
+
export declare function readRotationColumns(instanceArrow: ArrowTableLike, rowCount: number, onAbsent?: 'identity' | 'throw'): RotationColumns | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* Rotate `positions` (and, when present, `normals`) IN PLACE by row `index`'s
|
|
44
|
+
* 3x3 rotation. A no-op when the row is identity (translation-only or
|
|
45
|
+
* unverified placements — the overwhelming majority of rows on most models),
|
|
46
|
+
* so this costs nothing beyond the identity check for meshes #3575 did not
|
|
47
|
+
* touch.
|
|
48
|
+
*
|
|
49
|
+
* Normals get the SAME rotation matrix as positions, not its inverse
|
|
50
|
+
* transpose — correct for a rigid (orthogonal) rotation, which is what the
|
|
51
|
+
* server's per-vertex residual check (`RECOMPOSITION_TOLERANCE_M`) verifies
|
|
52
|
+
* before emitting a non-identity row. A non-uniform scale baked into the
|
|
53
|
+
* source `IfcCartesianTransformationOperator` would need the inverse
|
|
54
|
+
* transpose for normals specifically; this matches the convention the
|
|
55
|
+
* existing GPU-instancing wire format (`ifc_lite_geometry::instancing`)
|
|
56
|
+
* already uses, so it is not a new approximation this fix introduces.
|
|
57
|
+
*
|
|
58
|
+
* `isIdentityRow` already refuses to call a row with a NaN cell "identity"
|
|
59
|
+
* (`NaN !== 1` is true, so the row fails the comparison and falls through
|
|
60
|
+
* here) — a NaN can never silently render as an unrotated placement. But
|
|
61
|
+
* multiplying it through `rotateTriplets` would still corrupt every vertex
|
|
62
|
+
* of THIS instance to NaN, uncontrolled: no error, a mesh that may vanish
|
|
63
|
+
* or wreck a shared bounding-box computation with no signal why. A row that
|
|
64
|
+
* isn't finite is malformed wire data — the same class of fault
|
|
65
|
+
* `buildMeshesFromOptimizedTables` already fails loudly on for a bad
|
|
66
|
+
* mesh/material index — so it throws here too, instead of writing NaN
|
|
67
|
+
* silently into the buffer.
|
|
68
|
+
*/
|
|
69
|
+
export declare function applyInstanceRotation(positions: Float32Array, normals: Float32Array | undefined, rot: RotationColumns, index: number): void;
|
|
70
|
+
//# sourceMappingURL=parquet-rotation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parquet-rotation.d.ts","sourceRoot":"","sources":["../src/parquet-rotation.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAG1D,qEAAqE;AACrE,MAAM,MAAM,eAAe,GAAG,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;AAKlD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,mBAAmB,CACjC,aAAa,EAAE,cAAc,EAC7B,QAAQ,EAAE,MAAM,EAChB,QAAQ,GAAE,UAAU,GAAG,OAAoB,GAC1C,eAAe,GAAG,SAAS,CAe7B;AAUD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,YAAY,EACvB,OAAO,EAAE,YAAY,GAAG,SAAS,EACjC,GAAG,EAAE,eAAe,EACpB,KAAK,EAAE,MAAM,GACZ,IAAI,CAUN"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
import { numericColumn } from './parquet-columns.js';
|
|
5
|
+
/** Row-major identity, hoisted so the per-mesh identity check allocates nothing. */
|
|
6
|
+
const IDENTITY = [1, 0, 0, 0, 1, 0, 0, 0, 1];
|
|
7
|
+
/**
|
|
8
|
+
* Read the rotation columns, or `undefined` when the payload does not carry
|
|
9
|
+
* them — an optimized wire-version-2 payload (#3575), or a flat `-parquet-v5`
|
|
10
|
+
* blob (#3888). Callers then fall back to the identity rotation, which is
|
|
11
|
+
* exactly the behaviour those payloads were written for.
|
|
12
|
+
*
|
|
13
|
+
* `onAbsent` says what a MISSING or short block means to the caller, in the
|
|
14
|
+
* caller's own terms rather than in one transport's version numbering.
|
|
15
|
+
* `'identity'` is the reading above. `'throw'` is for a payload whose own
|
|
16
|
+
* header states the columns are there: the optimized format's version 3 is
|
|
17
|
+
* only emitted once a non-identity rotation has actually been written
|
|
18
|
+
* (`optimized_wire_version` in `parquet_instancing.rs`), so absence there is
|
|
19
|
+
* truncated wire data, and decoding it as identity would place a genuinely
|
|
20
|
+
* rotated occurrence at the wrong orientation with no signal why.
|
|
21
|
+
*
|
|
22
|
+
* The flat transport has no such header — it cannot tell a truncated v6 mesh
|
|
23
|
+
* table from a genuine v5 one — so it passes `'identity'`, which is right for
|
|
24
|
+
* the v5 case and no worse than throwing for the other.
|
|
25
|
+
*/
|
|
26
|
+
export function readRotationColumns(instanceArrow, rowCount, onAbsent = 'identity') {
|
|
27
|
+
const cols = [];
|
|
28
|
+
for (let i = 0; i < 9; i++) {
|
|
29
|
+
const col = numericColumn(instanceArrow, `rot${i}`);
|
|
30
|
+
if (!col || col.length < rowCount) {
|
|
31
|
+
if (onAbsent === 'throw') {
|
|
32
|
+
throw new Error('Malformed optimized Parquet geometry: format version 3 requires rot0..rot8 instance columns');
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
cols.push(col);
|
|
37
|
+
}
|
|
38
|
+
return cols;
|
|
39
|
+
}
|
|
40
|
+
/** `true` when row `index`'s rotation is exactly identity (the common case). */
|
|
41
|
+
function isIdentityRow(rot, index) {
|
|
42
|
+
for (let i = 0; i < 9; i++) {
|
|
43
|
+
if (rot[i][index] !== IDENTITY[i])
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Rotate `positions` (and, when present, `normals`) IN PLACE by row `index`'s
|
|
50
|
+
* 3x3 rotation. A no-op when the row is identity (translation-only or
|
|
51
|
+
* unverified placements — the overwhelming majority of rows on most models),
|
|
52
|
+
* so this costs nothing beyond the identity check for meshes #3575 did not
|
|
53
|
+
* touch.
|
|
54
|
+
*
|
|
55
|
+
* Normals get the SAME rotation matrix as positions, not its inverse
|
|
56
|
+
* transpose — correct for a rigid (orthogonal) rotation, which is what the
|
|
57
|
+
* server's per-vertex residual check (`RECOMPOSITION_TOLERANCE_M`) verifies
|
|
58
|
+
* before emitting a non-identity row. A non-uniform scale baked into the
|
|
59
|
+
* source `IfcCartesianTransformationOperator` would need the inverse
|
|
60
|
+
* transpose for normals specifically; this matches the convention the
|
|
61
|
+
* existing GPU-instancing wire format (`ifc_lite_geometry::instancing`)
|
|
62
|
+
* already uses, so it is not a new approximation this fix introduces.
|
|
63
|
+
*
|
|
64
|
+
* `isIdentityRow` already refuses to call a row with a NaN cell "identity"
|
|
65
|
+
* (`NaN !== 1` is true, so the row fails the comparison and falls through
|
|
66
|
+
* here) — a NaN can never silently render as an unrotated placement. But
|
|
67
|
+
* multiplying it through `rotateTriplets` would still corrupt every vertex
|
|
68
|
+
* of THIS instance to NaN, uncontrolled: no error, a mesh that may vanish
|
|
69
|
+
* or wreck a shared bounding-box computation with no signal why. A row that
|
|
70
|
+
* isn't finite is malformed wire data — the same class of fault
|
|
71
|
+
* `buildMeshesFromOptimizedTables` already fails loudly on for a bad
|
|
72
|
+
* mesh/material index — so it throws here too, instead of writing NaN
|
|
73
|
+
* silently into the buffer.
|
|
74
|
+
*/
|
|
75
|
+
export function applyInstanceRotation(positions, normals, rot, index) {
|
|
76
|
+
if (isIdentityRow(rot, index))
|
|
77
|
+
return;
|
|
78
|
+
const r = rot.map((col) => col[index]);
|
|
79
|
+
if (!r.every(Number.isFinite)) {
|
|
80
|
+
throw new Error(`Malformed Parquet geometry: non-finite rotation value for row ${index} (rot=[${r.join(', ')}])`);
|
|
81
|
+
}
|
|
82
|
+
rotateTriplets(positions, r);
|
|
83
|
+
if (normals)
|
|
84
|
+
rotateTriplets(normals, r);
|
|
85
|
+
}
|
|
86
|
+
function rotateTriplets(buf, r) {
|
|
87
|
+
for (let v = 0; v < buf.length; v += 3) {
|
|
88
|
+
const x = buf[v];
|
|
89
|
+
const y = buf[v + 1];
|
|
90
|
+
const z = buf[v + 2];
|
|
91
|
+
buf[v] = r[0] * x + r[1] * y + r[2] * z;
|
|
92
|
+
buf[v + 1] = r[3] * x + r[4] * y + r[5] * z;
|
|
93
|
+
buf[v + 2] = r[6] * x + r[7] * y + r[8] * z;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=parquet-rotation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parquet-rotation.js","sourceRoot":"","sources":["../src/parquet-rotation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ParquetBatch, ParquetStreamResult } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Raised when an SSE parse stream ends with no terminal event.
|
|
4
|
+
*
|
|
5
|
+
* Shared by `parseStream` and the Parquet stream reader below so the two
|
|
6
|
+
* cannot drift apart by an article. The wording has to hold on BOTH paths,
|
|
7
|
+
* which it does: this reader throws on an `error` event at its own
|
|
8
|
+
* `case 'error'`, so it can only reach its `!stats || !metadata` check in the
|
|
9
|
+
* same state `parseStream`'s `!terminated` describes, the stream stopped
|
|
10
|
+
* without ever saying why. Not re-exported from the package index: it is an
|
|
11
|
+
* internal guarantee about two call sites, not part of the published surface.
|
|
12
|
+
*/
|
|
13
|
+
export declare const STREAM_ENDED_WITHOUT_TERMINAL_EVENT = "Stream ended without a complete event (connection dropped or the server failed mid-parse)";
|
|
14
|
+
/**
|
|
15
|
+
* Drain a `parquet-stream` SSE response, decoding each batch and handing it to
|
|
16
|
+
* `onBatch` as it arrives.
|
|
17
|
+
*
|
|
18
|
+
* @param response - An OK response whose body is the SSE stream.
|
|
19
|
+
* @param onBatch - Called once per decoded batch, for immediate rendering.
|
|
20
|
+
* @param startedAt - `performance.now()` at request time, for the timing log.
|
|
21
|
+
*/
|
|
22
|
+
export declare function consumeParquetStream(response: Response, onBatch: (batch: ParquetBatch) => void, startedAt: number): Promise<ParquetStreamResult>;
|
|
23
|
+
//# sourceMappingURL=parquet-stream-events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parquet-stream-events.d.ts","sourceRoot":"","sources":["../src/parquet-stream-events.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAEV,YAAY,EAEZ,mBAAmB,EAGpB,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mCAAmC,8FAC6C,CAAC;AAE9F;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,EACtC,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,mBAAmB,CAAC,CA4G9B"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
/**
|
|
5
|
+
* Reading the SSE body of `POST /api/v1/parse/parquet-stream`.
|
|
6
|
+
*
|
|
7
|
+
* Its own module because there are now TWO requests that answer with that
|
|
8
|
+
* stream and they must be consumed identically (issue #3901): the hash-only
|
|
9
|
+
* probe, which sends no file at all and replays from cache, and the multipart
|
|
10
|
+
* upload it falls back to. The server guarantees a hit replays the same events
|
|
11
|
+
* a live parse emits; duplicating this loop per call site is how that
|
|
12
|
+
* guarantee stops being observable on the client.
|
|
13
|
+
*/
|
|
14
|
+
import { decodeParquetGeometry } from './parquet-decoder.js';
|
|
15
|
+
/**
|
|
16
|
+
* Raised when an SSE parse stream ends with no terminal event.
|
|
17
|
+
*
|
|
18
|
+
* Shared by `parseStream` and the Parquet stream reader below so the two
|
|
19
|
+
* cannot drift apart by an article. The wording has to hold on BOTH paths,
|
|
20
|
+
* which it does: this reader throws on an `error` event at its own
|
|
21
|
+
* `case 'error'`, so it can only reach its `!stats || !metadata` check in the
|
|
22
|
+
* same state `parseStream`'s `!terminated` describes, the stream stopped
|
|
23
|
+
* without ever saying why. Not re-exported from the package index: it is an
|
|
24
|
+
* internal guarantee about two call sites, not part of the published surface.
|
|
25
|
+
*/
|
|
26
|
+
export const STREAM_ENDED_WITHOUT_TERMINAL_EVENT = 'Stream ended without a complete event (connection dropped or the server failed mid-parse)';
|
|
27
|
+
/**
|
|
28
|
+
* Drain a `parquet-stream` SSE response, decoding each batch and handing it to
|
|
29
|
+
* `onBatch` as it arrives.
|
|
30
|
+
*
|
|
31
|
+
* @param response - An OK response whose body is the SSE stream.
|
|
32
|
+
* @param onBatch - Called once per decoded batch, for immediate rendering.
|
|
33
|
+
* @param startedAt - `performance.now()` at request time, for the timing log.
|
|
34
|
+
*/
|
|
35
|
+
export async function consumeParquetStream(response, onBatch, startedAt) {
|
|
36
|
+
if (!response.body) {
|
|
37
|
+
throw new Error('No response body for streaming');
|
|
38
|
+
}
|
|
39
|
+
const reader = response.body.getReader();
|
|
40
|
+
const decoder = new TextDecoder();
|
|
41
|
+
let buffer = '';
|
|
42
|
+
let cache_key = '';
|
|
43
|
+
let total_meshes = 0;
|
|
44
|
+
let stats = null;
|
|
45
|
+
let metadata = null;
|
|
46
|
+
let symbolic_data;
|
|
47
|
+
// A thrown 'error' event (or any other exception mid-loop) must not
|
|
48
|
+
// leave the reader locked on `response.body` — mirrors the try/finally
|
|
49
|
+
// `parseStream` already uses for the same SSE-reading shape.
|
|
50
|
+
try {
|
|
51
|
+
while (true) {
|
|
52
|
+
const { done, value } = await reader.read();
|
|
53
|
+
if (done)
|
|
54
|
+
break;
|
|
55
|
+
buffer += decoder.decode(value, { stream: true });
|
|
56
|
+
// Process complete SSE events
|
|
57
|
+
const lines = buffer.split('\n');
|
|
58
|
+
buffer = lines.pop() || ''; // Keep incomplete line in buffer
|
|
59
|
+
for (const line of lines) {
|
|
60
|
+
if (!line.startsWith('data:'))
|
|
61
|
+
continue;
|
|
62
|
+
const jsonStr = line.slice(5).trim();
|
|
63
|
+
if (!jsonStr)
|
|
64
|
+
continue;
|
|
65
|
+
try {
|
|
66
|
+
const event = JSON.parse(jsonStr);
|
|
67
|
+
switch (event.type) {
|
|
68
|
+
case 'start':
|
|
69
|
+
cache_key = event.cache_key;
|
|
70
|
+
console.log(`[client] Stream started: ${event.total_estimate} entities, cache_key: ${cache_key.substring(0, 16)}...`);
|
|
71
|
+
break;
|
|
72
|
+
case 'progress':
|
|
73
|
+
// Progress events can be used for UI feedback
|
|
74
|
+
break;
|
|
75
|
+
case 'batch': {
|
|
76
|
+
const decodeStart = performance.now();
|
|
77
|
+
// Decode base64 Parquet data
|
|
78
|
+
const binaryStr = atob(event.data);
|
|
79
|
+
const bytes = new Uint8Array(binaryStr.length);
|
|
80
|
+
for (let i = 0; i < binaryStr.length; i++) {
|
|
81
|
+
bytes[i] = binaryStr.charCodeAt(i);
|
|
82
|
+
}
|
|
83
|
+
// Decode Parquet to meshes
|
|
84
|
+
const meshes = await decodeParquetGeometry(bytes.buffer);
|
|
85
|
+
const decodeTime = performance.now() - decodeStart;
|
|
86
|
+
total_meshes += meshes.length;
|
|
87
|
+
console.log(`[client] Batch #${event.batch_number}: ${meshes.length} meshes, decode: ${decodeTime.toFixed(0)}ms`);
|
|
88
|
+
// Call the batch callback for immediate rendering
|
|
89
|
+
onBatch({
|
|
90
|
+
meshes,
|
|
91
|
+
batch_number: event.batch_number,
|
|
92
|
+
decode_time_ms: decodeTime,
|
|
93
|
+
});
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
case 'complete': {
|
|
97
|
+
stats = event.stats;
|
|
98
|
+
metadata = event.metadata;
|
|
99
|
+
symbolic_data = event.symbolic_data;
|
|
100
|
+
const totalTime = performance.now() - startedAt;
|
|
101
|
+
console.log(`[client] Stream complete: ${total_meshes} meshes in ${totalTime.toFixed(0)}ms`);
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
case 'error':
|
|
105
|
+
throw new Error(`Stream error: ${event.message}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch (e) {
|
|
109
|
+
if (e instanceof SyntaxError) {
|
|
110
|
+
console.warn('[client] Failed to parse SSE event:', jsonStr);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
throw e;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
finally {
|
|
120
|
+
reader.releaseLock();
|
|
121
|
+
}
|
|
122
|
+
if (!stats || !metadata) {
|
|
123
|
+
throw new Error(STREAM_ENDED_WITHOUT_TERMINAL_EVENT);
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
cache_key,
|
|
127
|
+
total_meshes,
|
|
128
|
+
stats,
|
|
129
|
+
metadata,
|
|
130
|
+
symbolic_data,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=parquet-stream-events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parquet-stream-events.js","sourceRoot":"","sources":["../src/parquet-stream-events.ts"],"names":[],"mappings":"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"}
|
package/dist/parquet-tables.d.ts
CHANGED
|
@@ -24,10 +24,19 @@ export interface ArrowTableLike {
|
|
|
24
24
|
getChild(name: string): ArrowColumnLike | null | undefined;
|
|
25
25
|
}
|
|
26
26
|
/**
|
|
27
|
-
* Rebuild `MeshData[]` from the standard
|
|
27
|
+
* Rebuild `MeshData[]` from the standard three-table layout.
|
|
28
28
|
*
|
|
29
29
|
* Positions/normals are already Y-up metres — the server applies the axis swap
|
|
30
30
|
* once, in `services::axis` (issue #1841), so every transport agrees.
|
|
31
|
+
*
|
|
32
|
+
* "Non-instanced" until `-parquet-v6` (issue #3888): several mesh rows can now
|
|
33
|
+
* name the SAME `vertex_start`/`index_start` block, each placed by its own
|
|
34
|
+
* `origin_x/y/z` plus a `rot0..rot8` rotation (`world = origin + R * p`) — the
|
|
35
|
+
* rotation-aware sharing the optimized transport has carried since #3575. Two
|
|
36
|
+
* things follow, and both are what keeps a `-parquet-v5` blob decoding here
|
|
37
|
+
* unchanged: the rotation columns are ABSENT on v5, and absent means identity;
|
|
38
|
+
* and `origin` was zero on every v5 flat row, so folding it in was a no-op
|
|
39
|
+
* there and is load-bearing here.
|
|
31
40
|
*/
|
|
32
41
|
export declare function buildMeshesFromTables(meshArrow: ArrowTableLike, vertexArrow: ArrowTableLike, indexArrow: ArrowTableLike): MeshData[];
|
|
33
42
|
/** Tables + header flags of the optimized (instanced, quantized) layout. */
|
|
@@ -41,6 +50,7 @@ export interface OptimizedTables {
|
|
|
41
50
|
hasNormals: boolean;
|
|
42
51
|
/** Quantization divisor: metres = quantized / vertexMultiplier. */
|
|
43
52
|
vertexMultiplier: number;
|
|
53
|
+
wireVersion: 2 | 3;
|
|
44
54
|
}
|
|
45
55
|
/**
|
|
46
56
|
* Rebuild `MeshData[]` from the optimized instanced layout.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parquet-tables.d.ts","sourceRoot":"","sources":["../src/parquet-tables.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"parquet-tables.d.ts","sourceRoot":"","sources":["../src/parquet-tables.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAI3C;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,OAAO,IAAI,SAAS,CAAC,MAAM,CAAC,CAAC;IAC7B,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,GAAG,SAAS,CAAC;CAC5D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,cAAc,EACzB,WAAW,EAAE,cAAc,EAC3B,UAAU,EAAE,cAAc,GACzB,QAAQ,EAAE,CAyIZ;AAED,4EAA4E;AAC5E,MAAM,WAAW,eAAe;IAC9B,aAAa,EAAE,cAAc,CAAC;IAC9B,SAAS,EAAE,cAAc,CAAC;IAC1B,aAAa,EAAE,cAAc,CAAC;IAC9B,WAAW,EAAE,cAAc,CAAC;IAC5B,UAAU,EAAE,cAAc,CAAC;IAC3B,+EAA+E;IAC/E,UAAU,EAAE,OAAO,CAAC;IACpB,mEAAmE;IACnE,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,CAAC,GAAG,CAAC,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,eAAe,GAAG,QAAQ,EAAE,CAiLlF;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,YAAY,EACvB,OAAO,EAAE,MAAM,EAAE,GAAG,WAAW,GAC9B,YAAY,CA0Cd"}
|