@ifc-lite/geometry 1.17.0 → 1.18.2

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.
@@ -0,0 +1,4 @@
1
+ import type { GeometryWorkerInitMessage, GeometryWorkerProcessMessage, GeometryWorkerStreamStartMessage, GeometryWorkerStreamChunkMessage, GeometryWorkerStreamEndMessage, GeometryWorkerSetStylesMessage, GeometryWorkerSetEntityIndexMessage, GeometryWorkerSetMergeLayersMessage, GeometryWorkerPrePassMessage, GeometryWorkerBatchMessage, GeometryWorkerCompleteMessage, GeometryWorkerErrorMessage, GeometryWorkerMemoryMessage } from './geometry.worker.js';
2
+ export type GeometryControllerRequest = GeometryWorkerInitMessage | GeometryWorkerProcessMessage | GeometryWorkerStreamStartMessage | GeometryWorkerStreamChunkMessage | GeometryWorkerStreamEndMessage | GeometryWorkerSetStylesMessage | GeometryWorkerSetEntityIndexMessage | GeometryWorkerSetMergeLayersMessage | GeometryWorkerPrePassMessage;
3
+ export type GeometryControllerResponse = GeometryWorkerBatchMessage | GeometryWorkerCompleteMessage | GeometryWorkerErrorMessage | GeometryWorkerMemoryMessage;
4
+ //# sourceMappingURL=geometry-controller.worker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"geometry-controller.worker.d.ts","sourceRoot":"","sources":["../src/geometry-controller.worker.ts"],"names":[],"mappings":"AAqCA,OAAO,KAAK,EACV,yBAAyB,EACzB,4BAA4B,EAC5B,gCAAgC,EAChC,gCAAgC,EAChC,8BAA8B,EAC9B,8BAA8B,EAC9B,mCAAmC,EACnC,mCAAmC,EACnC,4BAA4B,EAC5B,0BAA0B,EAC1B,6BAA6B,EAC7B,0BAA0B,EAC1B,2BAA2B,EAC5B,MAAM,sBAAsB,CAAC;AAG9B,MAAM,MAAM,yBAAyB,GACjC,yBAAyB,GACzB,4BAA4B,GAC5B,gCAAgC,GAChC,gCAAgC,GAChC,8BAA8B,GAC9B,8BAA8B,GAC9B,mCAAmC,GACnC,mCAAmC,GACnC,4BAA4B,CAAC;AAEjC,MAAM,MAAM,0BAA0B,GAClC,0BAA0B,GAC1B,6BAA6B,GAC7B,0BAA0B,GAC1B,2BAA2B,CAAC"}
@@ -0,0 +1,349 @@
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
+ * Single-controller geometry worker (Phase 2 of single-controller-rayon-design.md).
6
+ *
7
+ * Replaces the N-independent-Web-Workers pool. ONE WASM instance with
8
+ * an internal rayon thread pool of (navigator.hardwareConcurrency - 1)
9
+ * helper threads. All `processGeometryBatchParallel` work happens
10
+ * inside this worker; rayon's `par_iter` distributes per-entity work
11
+ * across the helpers via shared memory.
12
+ *
13
+ * Message protocol is INTENTIONALLY identical to `geometry.worker.ts`
14
+ * so the host (`geometry-parallel.ts`) can swap implementations behind
15
+ * a feature flag without changing the dispatch / event-collection
16
+ * code. The controller accepts the same set: `init`, `stream-start`,
17
+ * `stream-chunk`, `stream-end`, `set-styles`, `set-entity-index`.
18
+ *
19
+ * Why this matters: the per-worker entity-index FxHashMap (~600 MB
20
+ * per worker on the 986 MB test file) was triplicated across 3
21
+ * workers. Single controller holds ONE copy. Total peakWasm should
22
+ * drop from ~5.3 GB to ~3 GB. Plus rayon work-stealing replaces
23
+ * the contention pattern that capped useful workers at 3 even on
24
+ * 10-core hosts.
25
+ */
26
+ // Import the THREADED bundle. The viewer's vite.config.ts maps
27
+ // `@ifc-lite/wasm-threaded` to `packages/wasm-threaded/pkg/ifc-lite.js`.
28
+ // (See vite.config.ts alias added by Phase 2 wiring.)
29
+ import init, { initSync, IfcAPI, initThreadPool } from '@ifc-lite/wasm-threaded';
30
+ let api = null;
31
+ let threadPoolReady = false;
32
+ /**
33
+ * Cached merge-layers flag (issue #540). The host may post
34
+ * `set-merge-layers` BEFORE `init`, so we remember the latest value
35
+ * and re-apply once the threaded IfcAPI is constructed.
36
+ */
37
+ let mergeLayersFlag = false;
38
+ let mergeLayersApplied = false;
39
+ function applyMergeLayersToApi() {
40
+ if (!api || mergeLayersApplied)
41
+ return;
42
+ const merging = api;
43
+ if (typeof merging.setMergeLayers === 'function') {
44
+ merging.setMergeLayers(mergeLayersFlag);
45
+ }
46
+ mergeLayersApplied = true;
47
+ }
48
+ let activeSession = null;
49
+ function viewSharedBytes(sharedBuffer) {
50
+ return new Uint8Array(sharedBuffer);
51
+ }
52
+ function materialiseSharedBytes(sharedBuffer) {
53
+ const local = new Uint8Array(sharedBuffer.byteLength);
54
+ local.set(new Uint8Array(sharedBuffer));
55
+ return local;
56
+ }
57
+ function startSession(input) {
58
+ return {
59
+ sharedBuffer: input.sharedBuffer,
60
+ localBytes: viewSharedBytes(input.sharedBuffer),
61
+ sabFallbackTaken: false,
62
+ unitScale: input.unitScale,
63
+ rtcX: input.rtcX, rtcY: input.rtcY, rtcZ: input.rtcZ,
64
+ needsShift: input.needsShift,
65
+ voidKeys: input.voidKeys,
66
+ voidCounts: input.voidCounts,
67
+ voidValues: input.voidValues,
68
+ styleIds: input.styleIds,
69
+ styleColors: input.styleColors,
70
+ pendingMeshes: [],
71
+ pendingTransfers: [],
72
+ totalMeshesEmitted: 0,
73
+ cumulativeMeshBytes: 0,
74
+ };
75
+ }
76
+ function flushPending(session) {
77
+ if (session.pendingMeshes.length === 0)
78
+ return;
79
+ const meshes = session.pendingMeshes;
80
+ const transfers = session.pendingTransfers;
81
+ session.pendingMeshes = [];
82
+ session.pendingTransfers = [];
83
+ session.totalMeshesEmitted += meshes.length;
84
+ self.postMessage({ type: 'batch', meshes }, transfers);
85
+ }
86
+ function collectMeshes(session, collection) {
87
+ for (let i = 0; i < collection.length; i++) {
88
+ const mesh = collection.get(i);
89
+ if (!mesh)
90
+ continue;
91
+ const positions = new Float32Array(mesh.positions);
92
+ const normals = new Float32Array(mesh.normals);
93
+ const indices = new Uint32Array(mesh.indices);
94
+ session.pendingMeshes.push({
95
+ expressId: mesh.expressId,
96
+ ifcType: mesh.ifcType,
97
+ positions, normals, indices,
98
+ color: [mesh.color[0], mesh.color[1], mesh.color[2], mesh.color[3]],
99
+ });
100
+ session.pendingTransfers.push(positions.buffer, normals.buffer, indices.buffer);
101
+ session.cumulativeMeshBytes += positions.byteLength + normals.byteLength + indices.byteLength;
102
+ mesh.free();
103
+ }
104
+ collection.free();
105
+ }
106
+ /**
107
+ * Run one chunk through the rayon-parallel batch entry. Same binary-
108
+ * split recovery as the N-worker path: if WASM rejects the whole slice
109
+ * (typical: SAB-view incompatible with this runtime), fall back to a
110
+ * materialised copy; on per-entity failure, halve and retry.
111
+ */
112
+ async function processBatchParallel(session, jobs) {
113
+ const numJobs = Math.floor(jobs.length / 3);
114
+ if (numJobs === 0)
115
+ return;
116
+ try {
117
+ if (!api) {
118
+ throw new Error('controller API not initialised before stream-chunk');
119
+ }
120
+ const collection = api.processGeometryBatchParallel(session.localBytes, jobs, session.unitScale, session.rtcX, session.rtcY, session.rtcZ, session.needsShift, session.voidKeys, session.voidCounts, session.voidValues, session.styleIds, session.styleColors);
121
+ collectMeshes(session, collection);
122
+ }
123
+ catch (err) {
124
+ const msg = err.message;
125
+ if (!session.sabFallbackTaken && session.localBytes.buffer instanceof SharedArrayBuffer) {
126
+ session.sabFallbackTaken = true;
127
+ console.warn(`[controller] processGeometryBatchParallel rejected SAB view (${msg}), copying`);
128
+ session.localBytes = materialiseSharedBytes(session.sharedBuffer);
129
+ await processBatchParallel(session, jobs);
130
+ return;
131
+ }
132
+ if (numJobs === 1) {
133
+ console.warn(`[controller] skipping entity #${jobs[0]}: ${msg}`);
134
+ return;
135
+ }
136
+ console.warn(`[controller] batch of ${numJobs} entities failed (${msg}), splitting…`);
137
+ const mid = Math.floor(numJobs / 2) * 3;
138
+ await processBatchParallel(session, jobs.slice(0, mid));
139
+ await processBatchParallel(session, jobs.slice(mid));
140
+ }
141
+ }
142
+ /**
143
+ * Run a slice in STREAM_BATCH_SIZE chunks, flushing after each. Same
144
+ * cadence as the N-worker path so downstream React/render code sees
145
+ * familiar timing.
146
+ */
147
+ const STREAM_BATCH_SIZE = 1_000_000;
148
+ async function processSliceStreaming(session, jobsFlat) {
149
+ const totalJobs = Math.floor(jobsFlat.length / 3);
150
+ for (let jobOffset = 0; jobOffset < totalJobs; jobOffset += STREAM_BATCH_SIZE) {
151
+ const start = jobOffset * 3;
152
+ const end = Math.min(start + STREAM_BATCH_SIZE * 3, jobsFlat.length);
153
+ await processBatchParallel(session, jobsFlat.slice(start, end));
154
+ flushPending(session);
155
+ }
156
+ }
157
+ function emitSessionEnd(session) {
158
+ flushPending(session);
159
+ // Memory snapshot — single WASM heap (vs N-worker pool sums).
160
+ let wasmHeapBytes = 0;
161
+ try {
162
+ const memJs = api?.getMemory();
163
+ const buf = memJs?.buffer;
164
+ wasmHeapBytes = buf?.byteLength ?? 0;
165
+ }
166
+ catch { /* memory probe is best-effort */ }
167
+ self.postMessage({
168
+ type: 'memory',
169
+ wasmHeapBytes,
170
+ meshBytes: session.cumulativeMeshBytes,
171
+ });
172
+ self.postMessage({
173
+ type: 'complete',
174
+ totalMeshes: session.totalMeshesEmitted,
175
+ });
176
+ }
177
+ /**
178
+ * Tail-promise serialiser — copied from `geometry.worker.ts`. Web
179
+ * Worker `onmessage` is fire-and-forget; without serialisation, an
180
+ * async handler for `stream-start` (which awaits init() + initThreadPool)
181
+ * can be overtaken by a synchronous `stream-chunk` handler that
182
+ * dispatches before the session is ready.
183
+ */
184
+ let messageTail = Promise.resolve();
185
+ self.onmessage = (rawEvent) => {
186
+ const e = rawEvent;
187
+ messageTail = messageTail.then(async () => {
188
+ try {
189
+ if (e.data.type === 'init') {
190
+ if (e.data.wasmModule) {
191
+ initSync({ module_or_path: e.data.wasmModule });
192
+ }
193
+ else {
194
+ await init();
195
+ }
196
+ api = new IfcAPI();
197
+ mergeLayersApplied = false;
198
+ applyMergeLayersToApi();
199
+ // Spin up the rayon thread pool. Per the design (and upstream
200
+ // guidance from issue #36), call from inside this worker (NOT
201
+ // main thread) to dodge the Atomics.wait deadlock that fires
202
+ // when initThreadPool runs on the main thread.
203
+ //
204
+ // Thread count: hardwareConcurrency - 1 to leave one core for
205
+ // main-thread render. Wrap with retry-25ms-backoff x5 in case
206
+ // we hit transient deadlocks anyway.
207
+ const cores = (typeof navigator !== 'undefined'
208
+ ? (navigator.hardwareConcurrency ?? 4)
209
+ : 4);
210
+ const targetThreads = Math.max(1, cores - 1);
211
+ let lastInitErr = null;
212
+ for (let attempt = 1; attempt <= 5; attempt++) {
213
+ try {
214
+ const t0 = performance.now();
215
+ await initThreadPool(targetThreads);
216
+ console.log(`[controller] rayon pool ready (${targetThreads} threads, attempt ${attempt}) @ ${Math.round(performance.now() - t0)}ms`);
217
+ threadPoolReady = true;
218
+ lastInitErr = null;
219
+ break;
220
+ }
221
+ catch (err) {
222
+ lastInitErr = err;
223
+ console.warn(`[controller] initThreadPool attempt ${attempt} failed:`, err);
224
+ if (attempt < 5) {
225
+ await new Promise((r) => setTimeout(r, 25));
226
+ }
227
+ }
228
+ }
229
+ if (!threadPoolReady) {
230
+ // Surface the failure as an error message back to the host
231
+ // instead of silently posting `ready` and then hanging on
232
+ // the first par_iter call. The host's processParallel will
233
+ // see this and reject the load with a clear cause.
234
+ const errMsg = lastInitErr instanceof Error
235
+ ? lastInitErr.message
236
+ : String(lastInitErr ?? 'unknown initThreadPool failure');
237
+ self.postMessage({
238
+ type: 'error',
239
+ message: `controller: initThreadPool failed after 5 attempts: ${errMsg}`,
240
+ });
241
+ return;
242
+ }
243
+ // Phase 2 microbenchmark — run a CPU-pure parallel task to
244
+ // measure rayon's actual speedup on this hardware. Helps
245
+ // distinguish "rayon is broken" vs "our workload doesn't fit
246
+ // rayon" when stream tail is slow.
247
+ try {
248
+ const benchApi = api;
249
+ if (typeof benchApi.benchmarkPureCpuParallelism === 'function') {
250
+ // 9 tasks → one per helper thread — best case for scaling.
251
+ benchApi.benchmarkPureCpuParallelism(9);
252
+ }
253
+ }
254
+ catch (err) {
255
+ console.warn('[controller] microbench failed:', err);
256
+ }
257
+ self.postMessage({ type: 'ready' });
258
+ return;
259
+ }
260
+ if (e.data.type === 'stream-start') {
261
+ // The thread pool MUST be ready before we accept stream
262
+ // messages — `processGeometryBatchParallel` calls par_iter
263
+ // which would silently run on the calling thread (slow
264
+ // serial fallback) if the pool isn't initialized. Refuse
265
+ // and surface a clear error so the host can fall back.
266
+ if (!api || !threadPoolReady) {
267
+ throw new Error('controller: stream-start before init/threadPool ready — host must wait for {type:"ready"} before dispatching');
268
+ }
269
+ activeSession = startSession({
270
+ sharedBuffer: e.data.sharedBuffer,
271
+ unitScale: e.data.unitScale,
272
+ rtcX: e.data.rtcX, rtcY: e.data.rtcY, rtcZ: e.data.rtcZ,
273
+ needsShift: e.data.needsShift,
274
+ voidKeys: e.data.voidKeys,
275
+ voidCounts: e.data.voidCounts,
276
+ voidValues: e.data.voidValues,
277
+ styleIds: e.data.styleIds,
278
+ styleColors: e.data.styleColors,
279
+ });
280
+ return;
281
+ }
282
+ if (e.data.type === 'stream-chunk') {
283
+ if (!activeSession || !api || !threadPoolReady) {
284
+ throw new Error('controller: stream-chunk before init/stream-start');
285
+ }
286
+ await processSliceStreaming(activeSession, e.data.jobsFlat);
287
+ return;
288
+ }
289
+ if (e.data.type === 'stream-end') {
290
+ if (!activeSession)
291
+ return;
292
+ emitSessionEnd(activeSession);
293
+ activeSession = null;
294
+ return;
295
+ }
296
+ if (e.data.type === 'set-styles') {
297
+ if (!activeSession)
298
+ return;
299
+ activeSession.styleIds = e.data.styleIds;
300
+ activeSession.styleColors = e.data.styleColors;
301
+ activeSession.voidKeys = e.data.voidKeys;
302
+ activeSession.voidCounts = e.data.voidCounts;
303
+ activeSession.voidValues = e.data.voidValues;
304
+ return;
305
+ }
306
+ if (e.data.type === 'set-merge-layers') {
307
+ // Cache the requested merge-layers flag (issue #540). The
308
+ // host may post this BEFORE `init` (rare but legal); if so
309
+ // we just hold onto the value and the `init` branch above
310
+ // applies it to the newly-constructed API.
311
+ mergeLayersFlag = e.data.enabled === true;
312
+ mergeLayersApplied = false;
313
+ applyMergeLayersToApi();
314
+ return;
315
+ }
316
+ if (e.data.type === 'set-entity-index') {
317
+ if (!api) {
318
+ // Should never happen — host always sends `init` first and
319
+ // waits for `ready`. If we hit this, it indicates a host
320
+ // sequencing bug; surface clearly rather than silently
321
+ // initializing a fresh API (which would skip thread-pool
322
+ // setup and break later par_iter calls).
323
+ throw new Error('controller: set-entity-index before init — host must wait for {type:"ready"} before dispatching');
324
+ }
325
+ // Eager FxHashMap build — happens during the styles wait so
326
+ // by the time stream-chunk arrives the cache is hot.
327
+ api.setEntityIndex(e.data.ids, e.data.starts, e.data.lengths);
328
+ return;
329
+ }
330
+ // process / prepass-* message types are NOT handled by the
331
+ // controller path — those are pre-pass-only contracts that stay
332
+ // on the existing pre-pass worker (geometry.worker.ts). The
333
+ // controller is for stream-chunk dispatch only.
334
+ console.warn(`[controller] ignoring unhandled message type: ${e.data.type}`);
335
+ }
336
+ catch (err) {
337
+ const msg = err instanceof Error ? err.message : String(err);
338
+ console.error('[controller] handler error:', err);
339
+ self.postMessage({
340
+ type: 'error',
341
+ message: msg,
342
+ });
343
+ }
344
+ });
345
+ };
346
+ // Mark threadPoolReady reachable to silence potential unused warning
347
+ // when the future Phase 3 code references it for back-pressure.
348
+ void threadPoolReady;
349
+ //# sourceMappingURL=geometry-controller.worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"geometry-controller.worker.js","sourceRoot":"","sources":["../src/geometry-controller.worker.ts"],"names":[],"mappings":"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"}
@@ -1,23 +1,65 @@
1
1
  /**
2
- * Multi-worker parallel geometry processing.
2
+ * Multi-worker parallel geometry processing with streaming pre-pass.
3
3
  *
4
- * Spawns Web Workers that each get their own WASM instance and process
5
- * disjoint slices of the geometry entity list. Batches are yielded as
6
- * they arrive from any worker, enabling progressive rendering while
7
- * utilizing multiple cores.
4
+ * Architecture:
5
+ * 1. Spawn a single "pre-pass" worker that runs the WASM streaming
6
+ * scanner. The scanner walks the file once and emits:
7
+ * - `meta` once, when RTC + unit are resolved (~1-2 % of scan)
8
+ * - `jobs` repeatedly, every ~50 K entities
9
+ * - `complete` once, when the scan finishes
10
+ * 2. On `meta`: spawn N geometry process workers (memory-budget-aware
11
+ * count) and send each a `stream-start` so they hold the metadata
12
+ * before any chunk arrives.
13
+ * 3. On each `jobs` chunk: round-robin the chunk to a worker via
14
+ * `stream-chunk`. Workers process and emit `batch` messages.
15
+ * 4. On `complete`: send `stream-end` to each worker so they emit
16
+ * their final `batch`/`memory`/`complete`.
17
+ *
18
+ * Net effect for a 1 GB file: time-to-first-batch drops from ~17 s
19
+ * (full pre-pass + worker spawn + first batch) to ~3-5 s (pre-pass
20
+ * scans first 100 K bytes → meta → first chunk → first batch).
8
21
  */
9
22
  import type { CoordinateHandler } from './coordinate-handler.js';
10
23
  import type { StreamingGeometryEvent } from './index.js';
11
- /**
12
- * Run the full pre-pass in a dedicated worker, then fan geometry jobs
13
- * out to N workers and yield batches as they complete.
14
- *
15
- * @param buffer Raw IFC file bytes
16
- * @param coordinator CoordinateHandler used to accumulate bounds
17
- */
24
+ export interface ProcessParallelOptions {
25
+ /**
26
+ * Fires when the streaming pre-pass finishes building the entity index
27
+ * (after styles), with SAB-backed Uint32Array views over the shared
28
+ * column buffers. The parser worker uses this to skip its own
29
+ * `scanEntitiesFastBytes` call (~10 s on 1 GB files under WASM
30
+ * contention with the geometry workers).
31
+ */
32
+ onEntityIndex?: (ids: Uint32Array, starts: Uint32Array, lengths: Uint32Array) => void;
33
+ /**
34
+ * Phase 2 of single-controller-rayon-design.md — when true, spawn ONE
35
+ * controller worker that runs `processGeometryBatchParallel` with
36
+ * an internal rayon thread pool, instead of the N-independent-worker
37
+ * pool. The host can opt in via `localStorage.getItem('ifc-lite:single-controller')`
38
+ * (set in `useIfcLoader.ts`). Same input/output contract; the
39
+ * controller mirrors `geometry.worker.ts`'s message protocol so this
40
+ * file's dispatch loop is unchanged.
41
+ *
42
+ * Trade-offs documented in the design doc:
43
+ * - peakWasm should drop ~5.3 GB → ~3 GB (one heap, not three).
44
+ * - Stream tail should drop on rayon-friendly hosts (10+ cores).
45
+ * - Falls back silently to N-worker path if the threaded WASM
46
+ * bundle fails to load (no COI, Safari, etc.).
47
+ */
48
+ useSingleController?: boolean;
49
+ /**
50
+ * Issue #540 — "Merge Multilayer Walls" load-time toggle. When
51
+ * `true`, the geometry workers' IfcAPI receive
52
+ * `setMergeLayers(true)` before the first stream-chunk lands, so
53
+ * Revit-style multilayer-wall part meshes are suppressed at the
54
+ * Rust layer. Default `false` keeps existing behaviour.
55
+ */
56
+ mergeLayers?: boolean;
57
+ }
18
58
  export declare function processParallel(buffer: Uint8Array, coordinator: CoordinateHandler, sharedRtcOffset?: {
19
59
  x: number;
20
60
  y: number;
21
61
  z: number;
22
- }): AsyncGenerator<StreamingGeometryEvent>;
62
+ },
63
+ /** Optional pre-allocated SAB the caller already shares with another worker. */
64
+ existingSab?: SharedArrayBuffer, options?: ProcessParallelOptions): AsyncGenerator<StreamingGeometryEvent>;
23
65
  //# sourceMappingURL=geometry-parallel.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"geometry-parallel.d.ts","sourceRoot":"","sources":["../src/geometry-parallel.ts"],"names":[],"mappings":"AAIA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAEjE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAEzD;;;;;;GAMG;AACH,wBAAuB,eAAe,CACpC,MAAM,EAAE,UAAU,EAClB,WAAW,EAAE,iBAAiB,EAC9B,eAAe,CAAC,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,GACpD,cAAc,CAAC,sBAAsB,CAAC,CA+MxC"}
1
+ {"version":3,"file":"geometry-parallel.d.ts","sourceRoot":"","sources":["../src/geometry-parallel.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAEjE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAUzD,MAAM,WAAW,sBAAsB;IACrC;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,CACd,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,WAAW,EACnB,OAAO,EAAE,WAAW,KACjB,IAAI,CAAC;IACV;;;;;;;;;;;;;;OAcG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,wBAAuB,eAAe,CACpC,MAAM,EAAE,UAAU,EAClB,WAAW,EAAE,iBAAiB,EAC9B,eAAe,CAAC,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE;AACrD,gFAAgF;AAChF,WAAW,CAAC,EAAE,iBAAiB,EAC/B,OAAO,CAAC,EAAE,sBAAsB,GAC/B,cAAc,CAAC,sBAAsB,CAAC,CA4kBxC"}