@bldrs-ai/conway 1.424.1275 → 1.426.1283
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/compiled/examples/browser-bundled.cjs +56 -1
- package/compiled/examples/cli-bundled.cjs +56 -1
- package/compiled/examples/cli-step-bundled.cjs +15 -1
- package/compiled/examples/validator-bundled.cjs +56 -1
- package/compiled/src/compat/web-ifc/ifc_api.d.ts +20 -0
- package/compiled/src/compat/web-ifc/ifc_api.d.ts.map +1 -1
- package/compiled/src/compat/web-ifc/ifc_api.js +7 -0
- package/compiled/src/compat/web-ifc/ifc_api_deferred_open.test.js +66 -9
- package/compiled/src/compat/web-ifc/ifc_api_preview_channel.test.d.ts +2 -0
- package/compiled/src/compat/web-ifc/ifc_api_preview_channel.test.d.ts.map +1 -0
- package/compiled/src/compat/web-ifc/ifc_api_preview_channel.test.js +185 -0
- package/compiled/src/compat/web-ifc/ifc_api_proxy_ifc.d.ts +40 -8
- package/compiled/src/compat/web-ifc/ifc_api_proxy_ifc.d.ts.map +1 -1
- package/compiled/src/compat/web-ifc/ifc_api_proxy_ifc.js +115 -28
- package/compiled/src/compat/web-ifc/streamed_preview_channel.d.ts +130 -0
- package/compiled/src/compat/web-ifc/streamed_preview_channel.d.ts.map +1 -0
- package/compiled/src/compat/web-ifc/streamed_preview_channel.js +335 -0
- package/compiled/src/core/progress.d.ts +8 -1
- package/compiled/src/core/progress.d.ts.map +1 -1
- package/compiled/src/core/progress.js +22 -1
- package/compiled/src/core/progress.test.js +11 -2
- package/compiled/src/ifc/ifc_model_geometry.d.ts +12 -0
- package/compiled/src/ifc/ifc_model_geometry.d.ts.map +1 -1
- package/compiled/src/ifc/ifc_model_geometry.js +15 -0
- package/compiled/src/step/parsing/columnar_index.d.ts +22 -0
- package/compiled/src/step/parsing/columnar_index.d.ts.map +1 -1
- package/compiled/src/step/parsing/columnar_index.js +26 -0
- package/compiled/src/version/version.js +1 -1
- package/compiled/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/* eslint-disable no-magic-numbers */
|
|
2
|
+
// Parse-time preview channel (demand/tiled rendering slice A2): prefix
|
|
3
|
+
// snapshots of the live columnar sink must be exact prefixes of the final
|
|
4
|
+
// columns, and a full drain of the channel (prefix == whole file) must
|
|
5
|
+
// reproduce the classic StreamAllMeshes instance set — same entities,
|
|
6
|
+
// same geometry ids, same placed transforms — since the durable pump the
|
|
7
|
+
// preview hands over to is already pinned to classic parity.
|
|
8
|
+
import * as fs from "fs";
|
|
9
|
+
import { beforeAll, describe, expect, test } from "@jest/globals";
|
|
10
|
+
import { ConwayGeometry } from "../../../dependencies/conway-geom/index.js";
|
|
11
|
+
import IfcStepParser from "../../ifc/ifc_step_parser.js";
|
|
12
|
+
import { BufferByteSource } from "../../step/parsing/byte_source.js";
|
|
13
|
+
import { ColumnarIndexSink, } from "../../step/parsing/columnar_index.js";
|
|
14
|
+
import { ParseResult } from "../../step/parsing/step_parser.js";
|
|
15
|
+
import { buildIndexStreaming } from "../../step/parsing/streaming_index_builder.js";
|
|
16
|
+
import { IfcAPI } from "./ifc_api.js";
|
|
17
|
+
import { StreamedPreviewChannel, } from "./streamed_preview_channel.js";
|
|
18
|
+
const SETTINGS = { COORDINATE_TO_ORIGIN: true, USE_FAST_BOOLS: true };
|
|
19
|
+
const POOL = 1024 * 1024;
|
|
20
|
+
let api;
|
|
21
|
+
let conwayGeometry;
|
|
22
|
+
let data;
|
|
23
|
+
/**
|
|
24
|
+
* Parse index.ifc's data block into a fresh columnar sink.
|
|
25
|
+
*
|
|
26
|
+
* @param wrap Optional sink wrapper (snapshot triggers).
|
|
27
|
+
* @return {ColumnarIndexSink} The filled sink.
|
|
28
|
+
*/
|
|
29
|
+
function buildSink(wrap) {
|
|
30
|
+
const sink = new ColumnarIndexSink();
|
|
31
|
+
const { result } = buildIndexStreaming(new BufferByteSource(data), IfcStepParser.Instance, POOL, void 0, wrap !== void 0 ? wrap(sink) : sink);
|
|
32
|
+
expect(result).toBe(ParseResult.COMPLETE);
|
|
33
|
+
return sink;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Classic reference: expressID -> list of placed instances.
|
|
37
|
+
*
|
|
38
|
+
* @param modelID An open classic model.
|
|
39
|
+
* @return {Map} expressID -> {geometryExpressID, flatTransformation}[].
|
|
40
|
+
*/
|
|
41
|
+
function classicInstances(modelID) {
|
|
42
|
+
const instances = new Map();
|
|
43
|
+
api.StreamAllMeshes(modelID, (mesh) => {
|
|
44
|
+
const list = instances.get(mesh.expressID) ?? [];
|
|
45
|
+
for (let where = 0; where < mesh.geometries.size(); ++where) {
|
|
46
|
+
const placed = mesh.geometries.get(where);
|
|
47
|
+
list.push({
|
|
48
|
+
geometryExpressID: placed.geometryExpressID,
|
|
49
|
+
flatTransformation: [...placed.flatTransformation],
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
instances.set(mesh.expressID, list);
|
|
53
|
+
});
|
|
54
|
+
return instances;
|
|
55
|
+
}
|
|
56
|
+
beforeAll(async () => {
|
|
57
|
+
api = new IfcAPI();
|
|
58
|
+
await api.Init();
|
|
59
|
+
conwayGeometry = new ConwayGeometry();
|
|
60
|
+
expect(await conwayGeometry.initialize()).toBe(true);
|
|
61
|
+
data = new Uint8Array(fs.readFileSync("data/index.ifc"));
|
|
62
|
+
}, 120000);
|
|
63
|
+
describe("ColumnarIndexSink.snapshot", () => {
|
|
64
|
+
test("a mid-parse snapshot is an exact prefix of the final columns", () => {
|
|
65
|
+
const snapshotAt = 50;
|
|
66
|
+
let snapshot;
|
|
67
|
+
const sink = buildSink((inner) => ({
|
|
68
|
+
pushTopLevel: (entry) => {
|
|
69
|
+
inner.pushTopLevel(entry);
|
|
70
|
+
if (inner.topLevelCount === snapshotAt) {
|
|
71
|
+
snapshot = inner.snapshot();
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
reset: () => inner.reset(),
|
|
75
|
+
}));
|
|
76
|
+
const full = sink.finalize();
|
|
77
|
+
expect(snapshot).toBeDefined();
|
|
78
|
+
expect(snapshot.firstInlineElement).toBe(snapshotAt);
|
|
79
|
+
expect(full.firstInlineElement).toBeGreaterThan(snapshotAt);
|
|
80
|
+
for (let where = 0; where < snapshotAt; ++where) {
|
|
81
|
+
expect(snapshot.address[where]).toBe(full.address[where]);
|
|
82
|
+
expect(snapshot.length[where]).toBe(full.length[where]);
|
|
83
|
+
expect(snapshot.typeID[where]).toBe(full.typeID[where]);
|
|
84
|
+
expect(snapshot.expressID[where]).toBe(full.expressID[where]);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
test("snapshotting does not disturb the sink (finalize sees everything)", () => {
|
|
88
|
+
const undisturbed = buildSink().finalize();
|
|
89
|
+
const snapshotted = buildSink((inner) => ({
|
|
90
|
+
pushTopLevel: (entry) => {
|
|
91
|
+
inner.pushTopLevel(entry);
|
|
92
|
+
// Snapshot aggressively — every 25 records.
|
|
93
|
+
if (inner.topLevelCount % 25 === 0) {
|
|
94
|
+
inner.snapshot();
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
reset: () => inner.reset(),
|
|
98
|
+
})).finalize();
|
|
99
|
+
expect(snapshotted.count).toBe(undisturbed.count);
|
|
100
|
+
expect(snapshotted.firstInlineElement).toBe(undisturbed.firstInlineElement);
|
|
101
|
+
expect([...snapshotted.address]).toEqual([...undisturbed.address]);
|
|
102
|
+
expect([...snapshotted.expressID]).toEqual([...undisturbed.expressID]);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
describe("StreamedPreviewChannel", () => {
|
|
106
|
+
test("a full drain reproduces the classic instance set exactly", async () => {
|
|
107
|
+
const classicID = api.OpenModel(data, SETTINGS);
|
|
108
|
+
const classic = classicInstances(classicID);
|
|
109
|
+
let classicTotal = 0;
|
|
110
|
+
for (const list of classic.values()) {
|
|
111
|
+
classicTotal += list.length;
|
|
112
|
+
}
|
|
113
|
+
expect(classicTotal).toBeGreaterThan(0);
|
|
114
|
+
// Channel over a finished sink: the "prefix" is the whole file, so a
|
|
115
|
+
// drain must be a complete, classic-parity extraction.
|
|
116
|
+
const sink = buildSink();
|
|
117
|
+
const payloads = [];
|
|
118
|
+
const channel = new StreamedPreviewChannel(data, conwayGeometry, sink, true, (mesh) => payloads.push(mesh), void 0, void 0, 1);
|
|
119
|
+
channel.drainForTest();
|
|
120
|
+
expect(payloads.length).toBe(classicTotal);
|
|
121
|
+
// Every payload matches a classic instance of the same entity and
|
|
122
|
+
// geometry, with the same placed transform.
|
|
123
|
+
const unmatched = new Map();
|
|
124
|
+
for (const [expressID, list] of classic) {
|
|
125
|
+
unmatched.set(expressID, list.map((entry) => ({ ...entry })));
|
|
126
|
+
}
|
|
127
|
+
for (const payload of payloads) {
|
|
128
|
+
const candidates = unmatched.get(payload.expressID);
|
|
129
|
+
expect(candidates).toBeDefined();
|
|
130
|
+
const matchIndex = candidates.findIndex((candidate) => candidate.geometryExpressID === payload.geometryExpressID &&
|
|
131
|
+
candidate.flatTransformation.every((value, where) => Math.abs(value - payload.flatTransformation[where]) < 0.000001));
|
|
132
|
+
expect(matchIndex).toBeGreaterThanOrEqual(0);
|
|
133
|
+
candidates.splice(matchIndex, 1);
|
|
134
|
+
}
|
|
135
|
+
// Geometry payloads: exactly one carrier per distinct geometry, each
|
|
136
|
+
// carrying a sane interleaved buffer.
|
|
137
|
+
const carriers = payloads.filter((payload) => payload.vertexData !== void 0);
|
|
138
|
+
const distinctGeometry = new Set(payloads.map((p) => p.geometryExpressID));
|
|
139
|
+
expect(carriers.length).toBe(distinctGeometry.size);
|
|
140
|
+
for (const carrier of carriers) {
|
|
141
|
+
expect(carrier.vertexData.length % 6).toBe(0);
|
|
142
|
+
expect(carrier.vertexData.length).toBeGreaterThan(0);
|
|
143
|
+
expect(carrier.indexData.length % 3).toBe(0);
|
|
144
|
+
expect(carrier.indexData.length).toBeGreaterThan(0);
|
|
145
|
+
}
|
|
146
|
+
// The channel derived and pinned a coordination frame (proved
|
|
147
|
+
// equivalent to classic's by the transform parity above), and the
|
|
148
|
+
// deferred open must keep the classic GetCoordinationMatrix
|
|
149
|
+
// contract: identity, because placed transforms are premultiplied
|
|
150
|
+
// and consumers stamp the result onto assembled models — a
|
|
151
|
+
// non-identity return would coordinate twice.
|
|
152
|
+
expect(channel.coordinationMatrix).toBeDefined();
|
|
153
|
+
const deferredID = await api.OpenModelStreamed(data, { ...SETTINGS, DEFER_GEOMETRY: true });
|
|
154
|
+
api.StreamAllMeshes(deferredID, () => { });
|
|
155
|
+
const classicCoordination = api.GetCoordinationMatrix(classicID);
|
|
156
|
+
const deferredCoordination = api.GetCoordinationMatrix(deferredID);
|
|
157
|
+
expect(deferredCoordination).toEqual(classicCoordination);
|
|
158
|
+
api.CloseModel(classicID);
|
|
159
|
+
api.CloseModel(deferredID);
|
|
160
|
+
}, 240000);
|
|
161
|
+
test("ON_PREVIEW_MESH on a deferred streamed open never breaks the durable path", async () => {
|
|
162
|
+
const classicID = api.OpenModel(data, SETTINGS);
|
|
163
|
+
const classic = classicInstances(classicID);
|
|
164
|
+
const payloads = [];
|
|
165
|
+
const deferredID = await api.OpenModelStreamed(data, {
|
|
166
|
+
...SETTINGS,
|
|
167
|
+
DEFER_GEOMETRY: true,
|
|
168
|
+
ON_PREVIEW_MESH: (mesh) => payloads.push(mesh),
|
|
169
|
+
});
|
|
170
|
+
expect(deferredID).toBeGreaterThanOrEqual(0);
|
|
171
|
+
// index.ifc parses inside one cooperative slice, so the timer-driven
|
|
172
|
+
// channel usually never fires — the contract here is that its presence
|
|
173
|
+
// changes nothing about the durable pump's output.
|
|
174
|
+
const drained = new Map();
|
|
175
|
+
api.StreamAllMeshes(deferredID, (mesh) => {
|
|
176
|
+
drained.set(mesh.expressID, (drained.get(mesh.expressID) ?? 0) + mesh.geometries.size());
|
|
177
|
+
});
|
|
178
|
+
expect(drained.size).toBe(classic.size);
|
|
179
|
+
for (const [expressID, list] of classic) {
|
|
180
|
+
expect(drained.get(expressID)).toBe(list.length);
|
|
181
|
+
}
|
|
182
|
+
api.CloseModel(classicID);
|
|
183
|
+
api.CloseModel(deferredID);
|
|
184
|
+
}, 240000);
|
|
185
|
+
});
|
|
@@ -20,6 +20,9 @@ interface IfcProxyLoadState {
|
|
|
20
20
|
conwaywasm: ConwayGeometry;
|
|
21
21
|
/** True when opened without extraction (createDeferred). */
|
|
22
22
|
deferred?: boolean;
|
|
23
|
+
/** Coordination matrix the parse-time preview channel derived (slice
|
|
24
|
+
* A2) — adopted by the durable capture so both share one frame. */
|
|
25
|
+
previewCoordinationMatrix?: glmatrix.mat4;
|
|
23
26
|
allTimeStart: number;
|
|
24
27
|
stepHeader: StepHeader;
|
|
25
28
|
model: IfcStepModel;
|
|
@@ -57,8 +60,28 @@ export declare class IfcApiProxyIfc implements IfcApiModelPassthrough {
|
|
|
57
60
|
private deferredMode_;
|
|
58
61
|
/** Deferred-mode product worklist (file order), lazily enumerated. */
|
|
59
62
|
private demandProducts_?;
|
|
63
|
+
/**
|
|
64
|
+
* Deferred capture watermarks: entity localID -> how many of its
|
|
65
|
+
* placed instances (in scene-walk order) have been captured. Shared
|
|
66
|
+
* (mapped) geometry attributes instances to an entity from OTHER
|
|
67
|
+
* products' extractions, so an entity's instance set grows across
|
|
68
|
+
* batches - the watermark makes each instance captured exactly once,
|
|
69
|
+
* in the same order the classic single walk would process it.
|
|
70
|
+
*/
|
|
71
|
+
private readonly demandCapturedCounts_;
|
|
60
72
|
/** Cursor into demandProducts_ — products before it are extracted. */
|
|
61
73
|
private demandCursor_;
|
|
74
|
+
/**
|
|
75
|
+
* Coordination matrix the deferred capture derived (or adopted from
|
|
76
|
+
* the parse-time preview channel). Kept OFF the model tuple's slot 5
|
|
77
|
+
* deliberately: classic streamAllMeshes derives its coordination into
|
|
78
|
+
* a local and getCoordinationMatrix therefore returns identity —
|
|
79
|
+
* consumers (Share) stamp that result onto the assembled model, so a
|
|
80
|
+
* deferred open must present the same identity or coordination would
|
|
81
|
+
* apply twice. This field is only the pump's internal multi-call
|
|
82
|
+
* memory.
|
|
83
|
+
*/
|
|
84
|
+
private demandCoordination_?;
|
|
62
85
|
_isCoordinated: boolean;
|
|
63
86
|
linearScalingFactor: number;
|
|
64
87
|
identity: number[];
|
|
@@ -393,17 +416,26 @@ export declare class IfcApiProxyIfc implements IfcApiModelPassthrough {
|
|
|
393
416
|
remaining: number;
|
|
394
417
|
};
|
|
395
418
|
/**
|
|
396
|
-
* Walk the scene and emit
|
|
397
|
-
*
|
|
398
|
-
*
|
|
399
|
-
*
|
|
419
|
+
* Walk the scene and emit every not-yet-captured placed instance as
|
|
420
|
+
* per-entity DELTA FlatMeshes — the incremental core of
|
|
421
|
+
* streamAllMeshes with identical placed-geometry math, processed in
|
|
422
|
+
* walk order exactly once per instance (per-entity watermarks). An
|
|
423
|
+
* entity re-emits with only its NEW instances when shared/mapped
|
|
424
|
+
* geometry attributes more to it in later batches; consumers render
|
|
425
|
+
* deltas additively (the shared meshMap still accumulates each
|
|
426
|
+
* entity's FULL vector, so getFlatMesh stays whole-model correct).
|
|
427
|
+
*
|
|
428
|
+
* Also fixes a latent multi-call bug: the derived coordination
|
|
429
|
+
* matrix is remembered (demandCoordination_), so later batches place
|
|
400
430
|
* with the SAME coordination the first batch established
|
|
401
|
-
* (streamAllMeshes never needed this — it runs once).
|
|
431
|
+
* (streamAllMeshes never needed this — it runs once). It is NOT
|
|
432
|
+
* exposed through getCoordinationMatrix, which keeps the classic
|
|
433
|
+
* identity contract consumers stamp onto assembled models.
|
|
402
434
|
*
|
|
403
|
-
* @param
|
|
404
|
-
*
|
|
435
|
+
* @param meshCallback Receives one delta FlatMesh per entity that
|
|
436
|
+
* gained instances this call.
|
|
405
437
|
*/
|
|
406
|
-
private
|
|
438
|
+
private streamNewMeshes_;
|
|
407
439
|
/**
|
|
408
440
|
*
|
|
409
441
|
* @param modelID
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ifc_api_proxy_ifc.d.ts","sourceRoot":"","sources":["../../../../src/compat/web-ifc/ifc_api_proxy_ifc.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,cAAc,EACf,MAAM,aAAa,CAAA;AAEpB,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAC7D,OAAO,YAAY,MAAM,0BAA0B,CAAA;AACnD,OAAO,EACL,QAAQ,EACR,WAAW,EACX,WAAW,EACX,cAAc,EACd,cAAc,EACd,WAAW,EACX,MAAM,EACP,MAAM,WAAW,CAAA;AAClB,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAA;AACvE,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAA;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAC1D,OAAO,KAAK,QAAQ,MAAM,WAAW,CAAA;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"ifc_api_proxy_ifc.d.ts","sourceRoot":"","sources":["../../../../src/compat/web-ifc/ifc_api_proxy_ifc.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,7 +9,9 @@ import { extractModelInfo } from "../../loaders/loading_utilities.js";
|
|
|
9
9
|
import IfcStepParser from "../../ifc/ifc_step_parser.js";
|
|
10
10
|
import ParsingBuffer from "../../parsing/parsing_buffer.js";
|
|
11
11
|
import { BufferByteSource } from "../../step/parsing/byte_source.js";
|
|
12
|
-
import {
|
|
12
|
+
import { buildIndexStreamingAsync, } from "../../step/parsing/streaming_index_builder.js";
|
|
13
|
+
import { ColumnarIndexSink } from "../../step/parsing/columnar_index.js";
|
|
14
|
+
import { StreamedPreviewChannel } from "./streamed_preview_channel.js";
|
|
13
15
|
import { ExtractResult } from "../../index.js";
|
|
14
16
|
import { IfcGeometryExtraction } from "../../ifc/ifc_geometry_extraction.js";
|
|
15
17
|
import { ParseResult } from "../../index.js";
|
|
@@ -19,6 +21,9 @@ import { shimIfcEntityMap, shimIfcEntityReverseMap } from "./shim_schema_mapping
|
|
|
19
21
|
import { EntityTypesIfcCount } from "../../ifc/ifc4_gen/entity_types_ifc.gen.js";
|
|
20
22
|
import { IfcProduct, IfcRoot } from "../../ifc/ifc4_gen/index.js";
|
|
21
23
|
import { CanonicalMeshType } from "../../index.js";
|
|
24
|
+
// Batch size used when a whole-model consumer (streamAllMeshes) drains
|
|
25
|
+
// a deferred model's remaining products synchronously.
|
|
26
|
+
const DEFERRED_DRAIN_BATCH = 256;
|
|
22
27
|
/* Moving-window size for the streamed columnar parse (matches the
|
|
23
28
|
* ifc_stream_open default; the window bounds parse-time scratch, not
|
|
24
29
|
* the source buffer, which the model keeps resident here). */
|
|
@@ -41,6 +46,15 @@ export class IfcApiProxyIfc {
|
|
|
41
46
|
this.fs = undefined;
|
|
42
47
|
/** Was this model opened without extraction (DEFER_GEOMETRY)? */
|
|
43
48
|
this.deferredMode_ = false;
|
|
49
|
+
/**
|
|
50
|
+
* Deferred capture watermarks: entity localID -> how many of its
|
|
51
|
+
* placed instances (in scene-walk order) have been captured. Shared
|
|
52
|
+
* (mapped) geometry attributes instances to an entity from OTHER
|
|
53
|
+
* products' extractions, so an entity's instance set grows across
|
|
54
|
+
* batches - the watermark makes each instance captured exactly once,
|
|
55
|
+
* in the same order the classic single walk would process it.
|
|
56
|
+
*/
|
|
57
|
+
this.demandCapturedCounts_ = new Map();
|
|
44
58
|
/** Cursor into demandProducts_ — products before it are extracted. */
|
|
45
59
|
this.demandCursor_ = 0;
|
|
46
60
|
this._isCoordinated = false;
|
|
@@ -138,6 +152,15 @@ export class IfcApiProxyIfc {
|
|
|
138
152
|
];
|
|
139
153
|
// save settings
|
|
140
154
|
this.settings = settings;
|
|
155
|
+
// Deferred opens whose preview channel already established the
|
|
156
|
+
// coordination frame: adopt it, so the durable capture skips its own
|
|
157
|
+
// derivation and places exactly where the preview did. (Internal
|
|
158
|
+
// only — getCoordinationMatrix stays identity, see
|
|
159
|
+
// demandCoordination_.)
|
|
160
|
+
if (this.deferredMode_ && loadState.previewCoordinationMatrix !== void 0) {
|
|
161
|
+
this.demandCoordination_ = loadState.previewCoordinationMatrix;
|
|
162
|
+
this._isCoordinated = true;
|
|
163
|
+
}
|
|
141
164
|
let FILE_NAME = stepHeader.headers.get("FILE_NAME");
|
|
142
165
|
if (FILE_NAME !== void 0) {
|
|
143
166
|
// strip start / end parenthesis
|
|
@@ -439,7 +462,21 @@ export class IfcApiProxyIfc {
|
|
|
439
462
|
const parseTick = tracker !== void 0 ?
|
|
440
463
|
(cursorBytes) => tracker.update(cursorBytes) : void 0;
|
|
441
464
|
const parseStartTime = Date.now();
|
|
442
|
-
|
|
465
|
+
// Inline twin of buildColumnarIndexStreamingAsync — the sink is created
|
|
466
|
+
// here so the parse-time preview channel (slice A2) can watch it grow
|
|
467
|
+
// and snapshot prefix models between the parse's cooperative yields.
|
|
468
|
+
const sink = new ColumnarIndexSink();
|
|
469
|
+
const previewChannel = deferGeometry && settings?.ON_PREVIEW_MESH !== void 0 ?
|
|
470
|
+
new StreamedPreviewChannel(data, conwaywasm, sink, settings.COORDINATE_TO_ORIGIN === true, settings.ON_PREVIEW_MESH) : void 0;
|
|
471
|
+
previewChannel?.start();
|
|
472
|
+
let result;
|
|
473
|
+
try {
|
|
474
|
+
({ result } = await buildIndexStreamingAsync(new BufferByteSource(data), parser, STREAMED_PARSE_POOL_BYTES, void 0, sink, parseTick));
|
|
475
|
+
}
|
|
476
|
+
finally {
|
|
477
|
+
previewChannel?.stop();
|
|
478
|
+
}
|
|
479
|
+
const columns = sink.finalize();
|
|
443
480
|
const parseEndTime = Date.now();
|
|
444
481
|
tracker?.endPhase(data.length);
|
|
445
482
|
if (result !== ParseResult.COMPLETE) {
|
|
@@ -460,6 +497,10 @@ export class IfcApiProxyIfc {
|
|
|
460
497
|
return {
|
|
461
498
|
conwaywasm,
|
|
462
499
|
deferred: true,
|
|
500
|
+
// Pin the durable pump's coordination to the preview channel's
|
|
501
|
+
// (derived from the same first instance with the same math), so
|
|
502
|
+
// preview payloads and durable meshes share one frame.
|
|
503
|
+
previewCoordinationMatrix: previewChannel?.coordinationMatrix,
|
|
463
504
|
allTimeStart,
|
|
464
505
|
stepHeader,
|
|
465
506
|
model,
|
|
@@ -990,40 +1031,57 @@ export class IfcApiProxyIfc {
|
|
|
990
1031
|
this.demandCursor_ = 0;
|
|
991
1032
|
}
|
|
992
1033
|
const end = Math.min(this.demandCursor_ + Math.max(batchSize, 1), this.demandProducts_.length);
|
|
993
|
-
|
|
1034
|
+
let extracted = 0;
|
|
994
1035
|
for (; this.demandCursor_ < end; ++this.demandCursor_) {
|
|
995
1036
|
const localID = this.demandProducts_[this.demandCursor_];
|
|
996
1037
|
if (this.conwayGeometry_.extractProductGeometryByLocalID(localID)) {
|
|
997
|
-
|
|
1038
|
+
++extracted;
|
|
998
1039
|
}
|
|
999
1040
|
}
|
|
1000
|
-
if (meshCallback !== void 0
|
|
1001
|
-
this.
|
|
1041
|
+
if (meshCallback !== void 0) {
|
|
1042
|
+
this.streamNewMeshes_(meshCallback);
|
|
1002
1043
|
}
|
|
1003
1044
|
return {
|
|
1004
|
-
extracted
|
|
1045
|
+
extracted,
|
|
1005
1046
|
remaining: this.demandProducts_.length - this.demandCursor_,
|
|
1006
1047
|
};
|
|
1007
1048
|
}
|
|
1008
1049
|
/**
|
|
1009
|
-
* Walk the scene and emit
|
|
1010
|
-
*
|
|
1011
|
-
*
|
|
1012
|
-
*
|
|
1050
|
+
* Walk the scene and emit every not-yet-captured placed instance as
|
|
1051
|
+
* per-entity DELTA FlatMeshes — the incremental core of
|
|
1052
|
+
* streamAllMeshes with identical placed-geometry math, processed in
|
|
1053
|
+
* walk order exactly once per instance (per-entity watermarks). An
|
|
1054
|
+
* entity re-emits with only its NEW instances when shared/mapped
|
|
1055
|
+
* geometry attributes more to it in later batches; consumers render
|
|
1056
|
+
* deltas additively (the shared meshMap still accumulates each
|
|
1057
|
+
* entity's FULL vector, so getFlatMesh stays whole-model correct).
|
|
1058
|
+
*
|
|
1059
|
+
* Also fixes a latent multi-call bug: the derived coordination
|
|
1060
|
+
* matrix is remembered (demandCoordination_), so later batches place
|
|
1013
1061
|
* with the SAME coordination the first batch established
|
|
1014
|
-
* (streamAllMeshes never needed this — it runs once).
|
|
1062
|
+
* (streamAllMeshes never needed this — it runs once). It is NOT
|
|
1063
|
+
* exposed through getCoordinationMatrix, which keeps the classic
|
|
1064
|
+
* identity contract consumers stamp onto assembled models.
|
|
1015
1065
|
*
|
|
1016
|
-
* @param
|
|
1017
|
-
*
|
|
1066
|
+
* @param meshCallback Receives one delta FlatMesh per entity that
|
|
1067
|
+
* gained instances this call.
|
|
1018
1068
|
*/
|
|
1019
|
-
|
|
1069
|
+
streamNewMeshes_(meshCallback) {
|
|
1020
1070
|
const [model, scene, meshMap, geometryMaterialTransformMap] = this.model;
|
|
1021
|
-
let coordinationMatrix = this.
|
|
1022
|
-
const
|
|
1071
|
+
let coordinationMatrix = this.demandCoordination_ ?? glmatrix.mat4.create();
|
|
1072
|
+
const seenThisPass = new Map();
|
|
1073
|
+
const deltas = new Map();
|
|
1023
1074
|
// eslint-disable-next-line no-unused-vars
|
|
1024
1075
|
for (const [_, nativeTransform, geometry, material, entity] of scene.walk()) {
|
|
1025
|
-
if (entity?.localID === void 0 ||
|
|
1026
|
-
|
|
1076
|
+
if (entity?.localID === void 0 || entity.expressID === void 0) {
|
|
1077
|
+
continue;
|
|
1078
|
+
}
|
|
1079
|
+
// Per-entity walk position vs watermark: instances before the
|
|
1080
|
+
// watermark were captured in an earlier call (append-only walk
|
|
1081
|
+
// order makes the count a stable cursor).
|
|
1082
|
+
const walkIndex = seenThisPass.get(entity.localID) ?? 0;
|
|
1083
|
+
seenThisPass.set(entity.localID, walkIndex + 1);
|
|
1084
|
+
if (walkIndex < (this.demandCapturedCounts_.get(entity.localID) ?? 0)) {
|
|
1027
1085
|
continue;
|
|
1028
1086
|
}
|
|
1029
1087
|
if (geometry.type !== CanonicalMeshType.BUFFER_GEOMETRY || geometry.temporary) {
|
|
@@ -1069,8 +1127,9 @@ export class IfcApiProxyIfc {
|
|
|
1069
1127
|
glmatrix.mat4.scale(scaleMatrix, scaleMatrix, scaleVec);
|
|
1070
1128
|
glmatrix.mat4.multiply(coordinationMatrix, this.NormalizeMat, coordinationMatrix);
|
|
1071
1129
|
glmatrix.mat4.multiply(coordinationMatrix, scaleMatrix, coordinationMatrix);
|
|
1072
|
-
// Persist for every later batch (
|
|
1073
|
-
|
|
1130
|
+
// Persist for every later batch (internal only — see
|
|
1131
|
+
// demandCoordination_'s identity-contract note).
|
|
1132
|
+
this.demandCoordination_ = coordinationMatrix;
|
|
1074
1133
|
this._isCoordinated = true;
|
|
1075
1134
|
}
|
|
1076
1135
|
const newTransform = glmatrix.mat4.create();
|
|
@@ -1111,15 +1170,26 @@ export class IfcApiProxyIfc {
|
|
|
1111
1170
|
}
|
|
1112
1171
|
mesh[0].push(placed);
|
|
1113
1172
|
mesh[1].geometries = mesh[0];
|
|
1114
|
-
|
|
1173
|
+
this.demandCapturedCounts_.set(entity.localID, (this.demandCapturedCounts_.get(entity.localID) ?? 0) + 1);
|
|
1174
|
+
let delta = deltas.get(entity.expressID);
|
|
1175
|
+
if (delta === void 0) {
|
|
1176
|
+
delta = [];
|
|
1177
|
+
deltas.set(entity.expressID, delta);
|
|
1178
|
+
}
|
|
1179
|
+
delta.push(placed);
|
|
1115
1180
|
}
|
|
1116
1181
|
const vectorFlatMesh = this.model[4];
|
|
1117
|
-
for (const expressID of
|
|
1118
|
-
const
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1182
|
+
for (const [expressID, placedList] of deltas) {
|
|
1183
|
+
const placedVector = {
|
|
1184
|
+
get: (index) => placedList[index] ?? placedList[0],
|
|
1185
|
+
size: () => placedList.length,
|
|
1186
|
+
push: (parameter) => {
|
|
1187
|
+
placedList.push(parameter);
|
|
1188
|
+
},
|
|
1189
|
+
};
|
|
1190
|
+
const deltaMesh = { geometries: placedVector, expressID };
|
|
1191
|
+
vectorFlatMesh.push(deltaMesh);
|
|
1192
|
+
meshCallback(deltaMesh);
|
|
1123
1193
|
}
|
|
1124
1194
|
}
|
|
1125
1195
|
/**
|
|
@@ -1128,6 +1198,23 @@ export class IfcApiProxyIfc {
|
|
|
1128
1198
|
* @param meshCallback
|
|
1129
1199
|
*/
|
|
1130
1200
|
streamAllMeshes(meshCallback) {
|
|
1201
|
+
// Deferred models: the delta capture has already populated (or will
|
|
1202
|
+
// populate) the shared meshMap — re-running the classic walk would
|
|
1203
|
+
// push every instance a second time. Pump any remainder to
|
|
1204
|
+
// completion and serve the accumulated full per-entity meshes.
|
|
1205
|
+
if (this.deferredMode_) {
|
|
1206
|
+
const noCallback = void 0;
|
|
1207
|
+
while (this.extractGeometryBatch(DEFERRED_DRAIN_BATCH, noCallback).remaining > 0) {
|
|
1208
|
+
// draining
|
|
1209
|
+
}
|
|
1210
|
+
this.streamNewMeshes_(() => { });
|
|
1211
|
+
const [, , meshMap, , vectorFlatMesh] = this.model;
|
|
1212
|
+
meshMap.forEach((mesh) => {
|
|
1213
|
+
vectorFlatMesh.push(mesh[1]);
|
|
1214
|
+
meshCallback(mesh[1]);
|
|
1215
|
+
});
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
1131
1218
|
const [model, scene, meshMap, geometryMaterialTransformMap, vectorFlatMesh] = this.model;
|
|
1132
1219
|
let coordinationMatrix = this.model[5];
|
|
1133
1220
|
// eslint-disable-next-line no-unused-vars
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { ConwayGeometry } from "../../index.js";
|
|
2
|
+
import EntityTypesIfc from "../../ifc/ifc4_gen/entity_types_ifc.gen.js";
|
|
3
|
+
import { ColumnarIndexSink } from "../../step/parsing/columnar_index.js";
|
|
4
|
+
import * as glmatrix from "gl-matrix";
|
|
5
|
+
/**
|
|
6
|
+
* One preview mesh instance, self-contained: geometry payload is COPIED out
|
|
7
|
+
* of the wasm heap at emission, so consumers can upload it directly and
|
|
8
|
+
* never touch the (still-loading) model. `vertexData` is interleaved
|
|
9
|
+
* position+normal, 6 floats per vertex, exactly the GetGeometry layout the
|
|
10
|
+
* classic FlatMesh path reads. For instances sharing geometry with an
|
|
11
|
+
* earlier emission (mapped items), `vertexData`/`indexData` are omitted and
|
|
12
|
+
* `geometryExpressID` identifies the earlier payload to reuse.
|
|
13
|
+
*/
|
|
14
|
+
export interface PreviewMeshPayload {
|
|
15
|
+
expressID: number;
|
|
16
|
+
geometryExpressID: number;
|
|
17
|
+
color: {
|
|
18
|
+
x: number;
|
|
19
|
+
y: number;
|
|
20
|
+
z: number;
|
|
21
|
+
w: number;
|
|
22
|
+
};
|
|
23
|
+
flatTransformation: number[];
|
|
24
|
+
vertexData?: Float32Array;
|
|
25
|
+
indexData?: Uint32Array;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Parse-time preview channel (demand/tiled rendering slice A2): while the
|
|
29
|
+
* deferred streamed open is still parsing, periodically snapshot the growing
|
|
30
|
+
* columnar index into a PREFIX model, extract a bounded number of products
|
|
31
|
+
* through a throwaway extraction, and emit self-contained mesh payloads —
|
|
32
|
+
* first pixels within the first seconds of a large parse instead of after
|
|
33
|
+
* it.
|
|
34
|
+
*
|
|
35
|
+
* Preview quality, by construction: IFC relationship records
|
|
36
|
+
* (IfcRelVoidsElement, IfcRelAssociatesMaterial, styled items) spread to the
|
|
37
|
+
* very end of real files (measured ~92–97% depth), so a prefix extraction
|
|
38
|
+
* can miss openings and materials. That is why these extractions are
|
|
39
|
+
* throwaway: the durable batch pump after the parse re-extracts every
|
|
40
|
+
* product with the full model and REPLACES the preview — final geometry
|
|
41
|
+
* parity is untouched by this channel.
|
|
42
|
+
*
|
|
43
|
+
* Scheduling: the cooperative parse yields to the event loop via macrotasks
|
|
44
|
+
* every ~50ms; each pump tick runs in one of those gaps under a hard time
|
|
45
|
+
* budget, so the parse keeps the bulk of the main thread.
|
|
46
|
+
*
|
|
47
|
+
* Product cursor: top-level localIDs are stable across snapshots (dense
|
|
48
|
+
* parse order) and `types(IfcProduct)` iterates in localID order, so a
|
|
49
|
+
* single ordinal cursor advances across generations without re-extracting.
|
|
50
|
+
* Products whose extraction throws mid-parse (unparsed forward references)
|
|
51
|
+
* are skipped — the durable pump extracts them correctly later.
|
|
52
|
+
*/
|
|
53
|
+
export declare class StreamedPreviewChannel {
|
|
54
|
+
private readonly data;
|
|
55
|
+
private readonly conwaywasm;
|
|
56
|
+
private readonly sink;
|
|
57
|
+
private readonly coordinateToOrigin;
|
|
58
|
+
private readonly onMesh;
|
|
59
|
+
private readonly maxProducts;
|
|
60
|
+
private readonly maxBytes;
|
|
61
|
+
private readonly firstGenerationMinRecords;
|
|
62
|
+
/** Coordination matrix pinned from the first captured instance, exactly
|
|
63
|
+
* the derivation the durable capture would perform — the proxy adopts it
|
|
64
|
+
* so preview and durable placements share one frame. */
|
|
65
|
+
coordinationMatrix?: glmatrix.mat4;
|
|
66
|
+
private stopped_;
|
|
67
|
+
private timer_?;
|
|
68
|
+
private emittedProducts_;
|
|
69
|
+
private emittedBytes_;
|
|
70
|
+
/** Ordinal cursor into the (append-stable) product list. */
|
|
71
|
+
private productOrdinal_;
|
|
72
|
+
/** Geometry expressIDs whose payload has been emitted (cross-generation
|
|
73
|
+
* dedup for mapped/shared geometry). */
|
|
74
|
+
private readonly emittedGeometry_;
|
|
75
|
+
private generation_?;
|
|
76
|
+
private lastSnapshotRecords_;
|
|
77
|
+
/**
|
|
78
|
+
* @param data The (fully resident) source buffer the parse is indexing.
|
|
79
|
+
* @param conwaywasm The shared geometry wasm wrapper (sequential use only
|
|
80
|
+
* — ticks run between parse yields, never concurrently with the durable
|
|
81
|
+
* extraction, which is created after the parse completes).
|
|
82
|
+
* @param sink The live columnar sink the streamed parse is filling.
|
|
83
|
+
* @param coordinateToOrigin The open's COORDINATE_TO_ORIGIN setting.
|
|
84
|
+
* @param onMesh Consumer callback for each preview payload.
|
|
85
|
+
* @param maxProducts Cap on products ever preview-extracted.
|
|
86
|
+
* @param maxBytes Cap on total payload bytes copied out.
|
|
87
|
+
* @param firstGenerationMinRecords Records required before the first
|
|
88
|
+
* snapshot (tests lower it for tiny fixtures).
|
|
89
|
+
*/
|
|
90
|
+
constructor(data: Uint8Array, conwaywasm: ConwayGeometry, sink: ColumnarIndexSink<EntityTypesIfc>, coordinateToOrigin: boolean, onMesh: (mesh: PreviewMeshPayload) => void, maxProducts?: number, maxBytes?: number, firstGenerationMinRecords?: number);
|
|
91
|
+
/** Begin ticking (call just before awaiting the parse). */
|
|
92
|
+
start(): void;
|
|
93
|
+
/**
|
|
94
|
+
* Stop ticking (call once the parse settles, before finalize/fallback).
|
|
95
|
+
* Idempotent; no tick runs after this returns (ticks are synchronous and
|
|
96
|
+
* scheduled on the same event loop).
|
|
97
|
+
*/
|
|
98
|
+
stop(): void;
|
|
99
|
+
/** True when a cap was hit and the channel retired itself early. */
|
|
100
|
+
get capped(): boolean;
|
|
101
|
+
private schedule_;
|
|
102
|
+
/**
|
|
103
|
+
* One pump tick: ensure a generation with pending products exists, then
|
|
104
|
+
* extract + capture under the time budget.
|
|
105
|
+
*/
|
|
106
|
+
private tick_;
|
|
107
|
+
/**
|
|
108
|
+
* Ensure a generation with pending products: keep the current one while
|
|
109
|
+
* it has work; otherwise snapshot a fresh prefix model once the index has
|
|
110
|
+
* grown enough to be worth the copy.
|
|
111
|
+
*
|
|
112
|
+
* @return {boolean} True when a generation with pending products exists.
|
|
113
|
+
*/
|
|
114
|
+
private ensureGeneration_;
|
|
115
|
+
/**
|
|
116
|
+
* Walk the current generation's scene and emit every not-yet-captured
|
|
117
|
+
* placed instance as a payload — the preview twin of the shim's durable
|
|
118
|
+
* delta capture, with the same placed-geometry math (normalize, scaling,
|
|
119
|
+
* coordination) so preview and durable placements coincide, but copying
|
|
120
|
+
* geometry OUT of the wasm heap instead of retaining native references.
|
|
121
|
+
*/
|
|
122
|
+
private captureNewInstances_;
|
|
123
|
+
/**
|
|
124
|
+
* Test seam: run generation building + extraction + capture synchronously
|
|
125
|
+
* until either every product currently in the sink is attempted or a cap
|
|
126
|
+
* is hit — what the timer-driven ticks do, without the timers.
|
|
127
|
+
*/
|
|
128
|
+
drainForTest(): void;
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=streamed_preview_channel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"streamed_preview_channel.d.ts","sourceRoot":"","sources":["../../../../src/compat/web-ifc/streamed_preview_channel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAM5C,OAAO,cAAc,MAAM,yCAAyC,CAAA;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAA;AAErE,OAAO,KAAK,QAAQ,MAAM,WAAW,CAAA;AAwCrC;;;;;;;;GAQG;AACH,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAA;IACjB,iBAAiB,EAAE,MAAM,CAAA;IACzB,KAAK,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACrD,kBAAkB,EAAE,MAAM,EAAE,CAAA;IAC5B,UAAU,CAAC,EAAE,YAAY,CAAA;IACzB,SAAS,CAAC,EAAE,WAAW,CAAA;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,qBAAa,sBAAsB;IA6C7B,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,yBAAyB;IAlD9C;;4DAEwD;IACjD,kBAAkB,CAAC,EAAE,QAAQ,CAAC,IAAI,CAAA;IAEzC,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,MAAM,CAAC,CAA+B;IAE9C,OAAO,CAAC,gBAAgB,CAAI;IAC5B,OAAO,CAAC,aAAa,CAAI;IAEzB,4DAA4D;IAC5D,OAAO,CAAC,eAAe,CAAI;IAE3B;4CACwC;IACxC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAoB;IAErD,OAAO,CAAC,WAAW,CAAC,CAOnB;IAED,OAAO,CAAC,oBAAoB,CAAI;IAEhC;;;;;;;;;;;;OAYG;gBAEkB,IAAI,EAAE,UAAU,EAChB,UAAU,EAAE,cAAc,EAC1B,IAAI,EAAE,iBAAiB,CAAC,cAAc,CAAC,EACvC,kBAAkB,EAAE,OAAO,EAC3B,MAAM,EAAE,CAAC,IAAI,EAAE,kBAAkB,KAAK,IAAI,EAC1C,WAAW,GAAE,MAAqC,EAClD,QAAQ,GAAE,MAAkC,EAC5C,yBAAyB,GAAE,MAChB;IAGhC,2DAA2D;IACpD,KAAK,IAAI,IAAI;IAIpB;;;;OAIG;IACI,IAAI,IAAI,IAAI;IASnB,oEAAoE;IACpE,IAAW,MAAM,IAAI,OAAO,CAG3B;IAGD,OAAO,CAAC,SAAS;IAqBjB;;;OAGG;IACH,OAAO,CAAC,KAAK;IAoCb;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB;IAiDzB;;;;;;OAMG;IACH,OAAO,CAAC,oBAAoB;IAgJ5B;;;;OAIG;IACI,YAAY,IAAI,IAAI;CA+B5B"}
|