@ifc-lite/viewer 1.11.2 → 1.11.3
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/CHANGELOG.md +31 -0
- package/dist/assets/{Arrow.dom-C23ucqVf.js → Arrow.dom-pdznGhdb.js} +1 -1
- package/dist/assets/{browser-KT4JnbMQ.js → browser-BVFgLomt.js} +1 -1
- package/dist/assets/{index-B9MERyDx.js → index-CDQaZTvV.js} +1447 -1439
- package/dist/assets/{index-KHAuQKVq.js → index-Dg9-h2Mk.js} +4 -4
- package/dist/assets/{native-bridge-D9YLSIUl.js → native-bridge-DQBEBf70.js} +1 -1
- package/dist/assets/{wasm-bridge-DItniz60.js → wasm-bridge-BoLTUwxl.js} +1 -1
- package/dist/index.html +1 -1
- package/package.json +19 -19
- package/src/hooks/useIfcLoader.ts +30 -8
- package/src/store/slices/dataSlice.ts +20 -12
- package/src/utils/ifcConfig.ts +7 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/browser-
|
|
2
|
-
import { _ as u, b as S, __tla as __tla_0 } from "./index-
|
|
3
|
-
import { N as j, m as B, __tla as __tla_1 } from "./index-
|
|
1
|
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/browser-BVFgLomt.js","assets/index-CDQaZTvV.js","assets/index-BoYyWYAu.css"])))=>i.map(i=>d[i]);
|
|
2
|
+
import { _ as u, b as S, __tla as __tla_0 } from "./index-CDQaZTvV.js";
|
|
3
|
+
import { N as j, m as B, __tla as __tla_1 } from "./index-CDQaZTvV.js";
|
|
4
4
|
let c, g, L, D, x, R, A;
|
|
5
5
|
let __tla = Promise.all([
|
|
6
6
|
(()=>{
|
|
@@ -87,7 +87,7 @@ let __tla = Promise.all([
|
|
|
87
87
|
function k() {
|
|
88
88
|
return m || (m = (async ()=>{
|
|
89
89
|
try {
|
|
90
|
-
const e = await u(()=>import("./browser-
|
|
90
|
+
const e = await u(()=>import("./browser-BVFgLomt.js").then((i)=>i.b), __vite__mapDeps([0,1,2])), t = e.default ?? e;
|
|
91
91
|
let s;
|
|
92
92
|
try {
|
|
93
93
|
s = (await u(()=>import("./esbuild-COv63sf-.js"), [])).default;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{I as f,a as m}from"./index-
|
|
1
|
+
import{I as f,a as m}from"./index-CDQaZTvV.js";class u{bridge;initialized=!1;constructor(){this.bridge=new f}async init(){this.initialized||(await this.bridge.init(),this.initialized=!0)}isInitialized(){return this.initialized}async processGeometry(s){this.initialized||await this.init(),performance.now();const i=new m(this.bridge.getApi(),s),n=i.collectMeshes(),r=i.getBuildingRotation();performance.now();let e=0,o=0;for(const c of n)e+=c.positions.length/3,o+=c.indices.length/3;return{meshes:n,totalVertices:e,totalTriangles:o,coordinateInfo:{originShift:{x:0,y:0,z:0},originalBounds:{min:{x:0,y:0,z:0},max:{x:0,y:0,z:0}},shiftedBounds:{min:{x:0,y:0,z:0},max:{x:0,y:0,z:0}},hasLargeCoordinates:!1,buildingRotation:r}}}async processGeometryStreaming(s,i){this.initialized||await this.init();const n=performance.now(),r=new m(this.bridge.getApi(),s);let e=0,o=0,a=0;try{for await(const t of r.collectMeshesStreaming(50)){if(t&&typeof t=="object"&&"type"in t&&t.type==="colorUpdate")continue;const l=t;e+=l.length;for(const d of l)o+=d.positions.length/3,a+=d.indices.length/3;i.onBatch?.({meshes:l,progress:{processed:e,total:e,currentType:"processing"}})}}catch(t){throw i.onError?.(t instanceof Error?t:new Error(String(t))),t}const h=performance.now()-n,g={totalMeshes:e,totalVertices:o,totalTriangles:a,parseTimeMs:h*.3,geometryTimeMs:h*.7};return i.onComplete?.(g),g}getApi(){return this.bridge.getApi()}}export{u as WasmBridge};
|
package/dist/index.html
CHANGED
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
<meta name="theme-color" content="#7aa2f7">
|
|
45
45
|
<meta name="msapplication-TileColor" content="#1a1b26">
|
|
46
46
|
<meta name="msapplication-TileImage" content="/favicon-192x192-cropped.png">
|
|
47
|
-
<script type="module" crossorigin src="/assets/index-
|
|
47
|
+
<script type="module" crossorigin src="/assets/index-CDQaZTvV.js"></script>
|
|
48
48
|
<link rel="stylesheet" crossorigin href="/assets/index-BoYyWYAu.css">
|
|
49
49
|
</head>
|
|
50
50
|
<body>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ifc-lite/viewer",
|
|
3
|
-
"version": "1.11.
|
|
3
|
+
"version": "1.11.3",
|
|
4
4
|
"description": "IFC-Lite viewer application",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"dependencies": {
|
|
@@ -41,24 +41,24 @@
|
|
|
41
41
|
"tailwind-merge": "^3.4.0",
|
|
42
42
|
"tailwindcss": "^4.1.18",
|
|
43
43
|
"zustand": "^4.4.0",
|
|
44
|
-
"@ifc-lite/bcf": "^1.11.
|
|
45
|
-
"@ifc-lite/cache": "^1.11.
|
|
46
|
-
"@ifc-lite/data": "^1.11.
|
|
47
|
-
"@ifc-lite/drawing-2d": "^1.11.
|
|
48
|
-
"@ifc-lite/encoding": "^1.11.
|
|
49
|
-
"@ifc-lite/export": "^1.11.
|
|
50
|
-
"@ifc-lite/geometry": "^1.11.
|
|
51
|
-
"@ifc-lite/ids": "^1.11.
|
|
52
|
-
"@ifc-lite/lens": "^1.11.
|
|
53
|
-
"@ifc-lite/lists": "^1.11.
|
|
54
|
-
"@ifc-lite/mutations": "^1.11.
|
|
55
|
-
"@ifc-lite/parser": "^1.11.
|
|
56
|
-
"@ifc-lite/query": "^1.11.
|
|
57
|
-
"@ifc-lite/renderer": "^1.11.
|
|
58
|
-
"@ifc-lite/sandbox": "^1.11.
|
|
59
|
-
"@ifc-lite/server-client": "^1.11.
|
|
60
|
-
"@ifc-lite/spatial": "^1.11.
|
|
61
|
-
"@ifc-lite/wasm": "^1.11.
|
|
44
|
+
"@ifc-lite/bcf": "^1.11.3",
|
|
45
|
+
"@ifc-lite/cache": "^1.11.3",
|
|
46
|
+
"@ifc-lite/data": "^1.11.3",
|
|
47
|
+
"@ifc-lite/drawing-2d": "^1.11.3",
|
|
48
|
+
"@ifc-lite/encoding": "^1.11.3",
|
|
49
|
+
"@ifc-lite/export": "^1.11.3",
|
|
50
|
+
"@ifc-lite/geometry": "^1.11.3",
|
|
51
|
+
"@ifc-lite/ids": "^1.11.3",
|
|
52
|
+
"@ifc-lite/lens": "^1.11.3",
|
|
53
|
+
"@ifc-lite/lists": "^1.11.3",
|
|
54
|
+
"@ifc-lite/mutations": "^1.11.3",
|
|
55
|
+
"@ifc-lite/parser": "^1.11.3",
|
|
56
|
+
"@ifc-lite/query": "^1.11.3",
|
|
57
|
+
"@ifc-lite/renderer": "^1.11.3",
|
|
58
|
+
"@ifc-lite/sandbox": "^1.11.3",
|
|
59
|
+
"@ifc-lite/server-client": "^1.11.3",
|
|
60
|
+
"@ifc-lite/spatial": "^1.11.3",
|
|
61
|
+
"@ifc-lite/wasm": "^1.11.3"
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
64
|
"@tailwindcss/postcss": "^4.1.18",
|
|
@@ -17,7 +17,7 @@ import { GeometryProcessor, GeometryQuality, type MeshData, type CoordinateInfo
|
|
|
17
17
|
import { buildSpatialIndex } from '@ifc-lite/spatial';
|
|
18
18
|
import { type GeometryData, loadGLBToMeshData } from '@ifc-lite/cache';
|
|
19
19
|
|
|
20
|
-
import { SERVER_URL, USE_SERVER, CACHE_SIZE_THRESHOLD, getDynamicBatchConfig } from '../utils/ifcConfig.js';
|
|
20
|
+
import { SERVER_URL, USE_SERVER, CACHE_SIZE_THRESHOLD, CACHE_MAX_SOURCE_SIZE, getDynamicBatchConfig } from '../utils/ifcConfig.js';
|
|
21
21
|
import {
|
|
22
22
|
calculateMeshBounds,
|
|
23
23
|
createCoordinateInfo,
|
|
@@ -324,8 +324,9 @@ export function useIfcLoader() {
|
|
|
324
324
|
});
|
|
325
325
|
};
|
|
326
326
|
|
|
327
|
-
//
|
|
328
|
-
|
|
327
|
+
// Data model parsing is deferred to the 'complete' event (see below).
|
|
328
|
+
// Running it concurrently with geometry streaming steals main-thread cycles
|
|
329
|
+
// from the WASM↔JS bridge, adding ~1-2s to geometry completion time.
|
|
329
330
|
|
|
330
331
|
// Use adaptive processing: sync for small files, streaming for large files
|
|
331
332
|
let estimatedTotal = 0;
|
|
@@ -378,12 +379,14 @@ export function useIfcLoader() {
|
|
|
378
379
|
console.log(`[useIfc] Model opened at ${modelOpenMs.toFixed(0)}ms`);
|
|
379
380
|
break;
|
|
380
381
|
case 'colorUpdate': {
|
|
381
|
-
//
|
|
382
|
-
|
|
383
|
-
//
|
|
382
|
+
// Accumulate color updates locally during streaming.
|
|
383
|
+
// We apply them in a single pass at 'complete' instead of
|
|
384
|
+
// calling updateMeshColors() per event (which triggers a
|
|
385
|
+
// React reconciliation each time + O(n) scan over all meshes).
|
|
384
386
|
for (const [expressId, color] of event.updates) {
|
|
385
387
|
cumulativeColorUpdates.set(expressId, color);
|
|
386
388
|
}
|
|
389
|
+
// Keep local mesh snapshots in sync for cache serialization.
|
|
387
390
|
applyColorUpdatesToMeshes(allMeshes, event.updates);
|
|
388
391
|
applyColorUpdatesToMeshes(pendingMeshes, event.updates);
|
|
389
392
|
break;
|
|
@@ -447,6 +450,16 @@ export function useIfcLoader() {
|
|
|
447
450
|
|
|
448
451
|
finalCoordinateInfo = event.coordinateInfo ?? null;
|
|
449
452
|
|
|
453
|
+
// Start data model parsing NOW — after geometry streaming is done
|
|
454
|
+
// so the parser doesn't compete with WASM for main-thread CPU.
|
|
455
|
+
startDataModelParsing();
|
|
456
|
+
|
|
457
|
+
// Apply all accumulated color updates in a single store update
|
|
458
|
+
// instead of one updateMeshColors() call per colorUpdate event.
|
|
459
|
+
if (cumulativeColorUpdates.size > 0) {
|
|
460
|
+
updateMeshColors(cumulativeColorUpdates);
|
|
461
|
+
}
|
|
462
|
+
|
|
450
463
|
// Store captured RTC offset in coordinate info for multi-model alignment
|
|
451
464
|
if (finalCoordinateInfo && capturedRtcOffset) {
|
|
452
465
|
finalCoordinateInfo.wasmRtcOffset = capturedRtcOffset;
|
|
@@ -483,8 +496,17 @@ export function useIfcLoader() {
|
|
|
483
496
|
}
|
|
484
497
|
}
|
|
485
498
|
|
|
486
|
-
// Cache the result in the background (
|
|
487
|
-
|
|
499
|
+
// Cache the result in the background (files between 10 MB and 150 MB).
|
|
500
|
+
// Files above CACHE_MAX_SOURCE_SIZE are not cached because the
|
|
501
|
+
// source buffer is required for on-demand property/quantity
|
|
502
|
+
// extraction, spatial hierarchy elevations, and IFC re-export.
|
|
503
|
+
// Caching without it would silently degrade those features.
|
|
504
|
+
if (
|
|
505
|
+
buffer.byteLength >= CACHE_SIZE_THRESHOLD &&
|
|
506
|
+
buffer.byteLength <= CACHE_MAX_SOURCE_SIZE &&
|
|
507
|
+
allMeshes.length > 0 &&
|
|
508
|
+
finalCoordinateInfo
|
|
509
|
+
) {
|
|
488
510
|
// Final safety pass so cache always contains post-style colors.
|
|
489
511
|
applyColorUpdatesToMeshes(allMeshes, cumulativeColorUpdates);
|
|
490
512
|
const geometryData: GeometryData = {
|
|
@@ -62,27 +62,34 @@ export const createDataSlice: StateCreator<DataSlice, [], [], DataSlice> = (set)
|
|
|
62
62
|
setGeometryResult: (geometryResult) => set({ geometryResult }),
|
|
63
63
|
|
|
64
64
|
appendGeometryBatch: (meshes, coordinateInfo) => set((state) => {
|
|
65
|
+
// Incremental totals: O(batch_size) instead of O(total_accumulated) .reduce()
|
|
66
|
+
let batchTriangles = 0;
|
|
67
|
+
let batchVertices = 0;
|
|
68
|
+
for (let i = 0; i < meshes.length; i++) {
|
|
69
|
+
batchTriangles += meshes[i].indices.length / 3;
|
|
70
|
+
batchVertices += meshes[i].positions.length / 3;
|
|
71
|
+
}
|
|
72
|
+
|
|
65
73
|
if (!state.geometryResult) {
|
|
66
|
-
const totalTriangles = meshes.reduce((sum, m) => sum + (m.indices.length / 3), 0);
|
|
67
|
-
const totalVertices = meshes.reduce((sum, m) => sum + (m.positions.length / 3), 0);
|
|
68
74
|
return {
|
|
69
75
|
geometryResult: {
|
|
70
|
-
meshes,
|
|
71
|
-
totalTriangles,
|
|
72
|
-
totalVertices,
|
|
76
|
+
meshes: meshes.slice(),
|
|
77
|
+
totalTriangles: batchTriangles,
|
|
78
|
+
totalVertices: batchVertices,
|
|
73
79
|
coordinateInfo: coordinateInfo || getDefaultCoordinateInfo(),
|
|
74
80
|
},
|
|
75
81
|
};
|
|
76
82
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
83
|
+
|
|
84
|
+
// New array reference (required for React/Zustand change detection) but
|
|
85
|
+
// only O(n) pointer copies — the expensive part was the .reduce() calls
|
|
86
|
+
// which are now replaced by the incremental counters above.
|
|
80
87
|
return {
|
|
81
88
|
geometryResult: {
|
|
82
89
|
...state.geometryResult,
|
|
83
|
-
meshes:
|
|
84
|
-
totalTriangles,
|
|
85
|
-
totalVertices,
|
|
90
|
+
meshes: [...state.geometryResult.meshes, ...meshes],
|
|
91
|
+
totalTriangles: state.geometryResult.totalTriangles + batchTriangles,
|
|
92
|
+
totalVertices: state.geometryResult.totalVertices + batchVertices,
|
|
86
93
|
coordinateInfo: coordinateInfo || state.geometryResult.coordinateInfo,
|
|
87
94
|
},
|
|
88
95
|
};
|
|
@@ -98,7 +105,8 @@ export const createDataSlice: StateCreator<DataSlice, [], [], DataSlice> = (set)
|
|
|
98
105
|
return { pendingMeshColorUpdates: clonedUpdates };
|
|
99
106
|
}
|
|
100
107
|
|
|
101
|
-
//
|
|
108
|
+
// New array reference so useGeometryStreaming's useEffect detects the change.
|
|
109
|
+
// Only runs once at 'complete' (not per-batch), so O(n) .map() is fine.
|
|
102
110
|
const updatedMeshes = state.geometryResult.meshes.map(mesh => {
|
|
103
111
|
const newColor = clonedUpdates.get(mesh.expressId);
|
|
104
112
|
if (newColor) {
|
package/src/utils/ifcConfig.ts
CHANGED
|
@@ -39,6 +39,13 @@ export const USE_SERVER = SERVER_URL !== '' && import.meta.env.VITE_USE_SERVER =
|
|
|
39
39
|
/** Minimum file size to cache (10MB) - smaller files parse quickly anyway */
|
|
40
40
|
export const CACHE_SIZE_THRESHOLD = 10 * 1024 * 1024;
|
|
41
41
|
|
|
42
|
+
/** Maximum file size eligible for caching (150MB).
|
|
43
|
+
* Files above this are not cached at all because the source buffer is required
|
|
44
|
+
* for on-demand property/quantity extraction, spatial hierarchy elevations,
|
|
45
|
+
* and IFC re-export. Caching without it would silently degrade those features,
|
|
46
|
+
* and including it would make the IndexedDB write prohibitively large. */
|
|
47
|
+
export const CACHE_MAX_SOURCE_SIZE = 150 * 1024 * 1024;
|
|
48
|
+
|
|
42
49
|
/** File size thresholds for various optimizations */
|
|
43
50
|
export const THRESHOLDS = {
|
|
44
51
|
/** Use streaming Parquet above this (150MB) */
|