@ifc-lite/viewer 1.19.1 → 1.21.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/.turbo/turbo-build.log +59 -44
- package/.turbo/turbo-typecheck.log +1 -1
- package/CHANGELOG.md +488 -0
- package/dist/assets/{basketViewActivator-CA2CTcVo.js → basketViewActivator-Bzw51jhm.js} +6 -6
- package/dist/assets/decode-worker-t2EGKAxO.js +1708 -0
- package/dist/assets/drawing-2d-Bjy8YPrg.js +257 -0
- package/dist/assets/exporters-u0sz2Upj.js +259119 -0
- package/dist/assets/geometry-controller.worker-NH8pZmrU.js +7 -0
- package/dist/assets/geometry.worker-Bp4rW_R1.js +1 -0
- package/dist/assets/ids-B7AXEv7h.js +4067 -0
- package/dist/assets/ifc-lite-DfZHk36-.js +7 -0
- package/dist/assets/ifc-lite_bg-DlKs5-yM.wasm +0 -0
- package/dist/assets/ifc-lite_bg-PqmRe3Ph.wasm +0 -0
- package/dist/assets/index-CSWgTe1s.css +1 -0
- package/dist/assets/{index-D8Epw-e7.js → index-DVNSvEMh.js} +40146 -35823
- package/dist/assets/laz-perf-Cvr_Lepg.js +1 -0
- package/dist/assets/laz-perf-DnSyzVYH.wasm +0 -0
- package/dist/assets/{native-bridge-DKmx1z95.js → native-bridge-BiD01jI9.js} +1 -1
- package/dist/assets/parser.worker-Bnbrl6gy.js +182 -0
- package/dist/assets/{sandbox-tccwm5Bo.js → sandbox-DPD1ROr0.js} +4 -4
- package/dist/assets/{server-client-LoWPK1N2.js → server-client-DP8fMPY9.js} +1 -1
- package/dist/assets/{wasm-bridge-BsJGgPMs.js → wasm-bridge-CErti6zX.js} +1 -1
- package/dist/assets/workerHelpers-CBbWSJmd.js +36 -0
- package/dist/index.html +8 -8
- package/index.html +1 -1
- package/package.json +10 -10
- package/src/components/viewer/BasketPresentationDock.tsx +3 -0
- package/src/components/viewer/CesiumOverlay.tsx +165 -120
- package/src/components/viewer/DeviationPanel.tsx +172 -0
- package/src/components/viewer/HierarchyPanel.tsx +29 -3
- package/src/components/viewer/HoverTooltip.tsx +5 -0
- package/src/components/viewer/IDSAuditSummary.tsx +389 -0
- package/src/components/viewer/IDSPanel.tsx +80 -26
- package/src/components/viewer/MainToolbar.tsx +60 -7
- package/src/components/viewer/MergeLayersBanner.tsx +108 -0
- package/src/components/viewer/MobileToolbar.tsx +326 -0
- package/src/components/viewer/PointCloudClasses.tsx +111 -0
- package/src/components/viewer/PointCloudLegend.tsx +119 -0
- package/src/components/viewer/PointCloudPanel.tsx +52 -1
- package/src/components/viewer/PropertiesPanel.tsx +37 -6
- package/src/components/viewer/RectSelectionOverlay.tsx +48 -0
- package/src/components/viewer/StatusBar.tsx +14 -0
- package/src/components/viewer/ViewerLayout.tsx +288 -95
- package/src/components/viewer/Viewport.tsx +86 -18
- package/src/components/viewer/ViewportContainer.tsx +25 -11
- package/src/components/viewer/ViewportOverlays.tsx +41 -26
- package/src/components/viewer/mouseHandlerTypes.ts +22 -0
- package/src/components/viewer/properties/GeoreferencingPanel.tsx +77 -8
- package/src/components/viewer/properties/MaterialCard.tsx +2 -2
- package/src/components/viewer/selectionHandlers.ts +41 -0
- package/src/components/viewer/tools/SectionPanel.tsx +181 -24
- package/src/components/viewer/tools/SectionVisualization.tsx +384 -3
- package/src/components/viewer/useAnimationLoop.ts +22 -0
- package/src/components/viewer/useMouseControls.ts +296 -3
- package/src/components/viewer/usePointCloudSync.ts +8 -1
- package/src/components/viewer/useRenderUpdates.ts +21 -1
- package/src/components/viewer/useTouchControls.ts +100 -41
- package/src/hooks/federationLoadGate.test.ts +90 -0
- package/src/hooks/federationLoadGate.ts +127 -0
- package/src/hooks/ids/idsDataAccessor.ts +11 -259
- package/src/hooks/ingest/pointCloudIngest.ts +127 -16
- package/src/hooks/useDrawingGeneration.ts +81 -8
- package/src/hooks/useIDS.ts +90 -10
- package/src/hooks/useIfcFederation.ts +94 -16
- package/src/hooks/useIfcLoader.ts +289 -64
- package/src/hooks/useViewerSelectors.ts +10 -0
- package/src/lib/geo/cesium-bridge.ts +84 -67
- package/src/lib/geo/clamp-anchor.test.ts +80 -0
- package/src/lib/geo/clamp-anchor.ts +57 -0
- package/src/lib/geo/effective-georef.test.ts +79 -1
- package/src/lib/geo/effective-georef.ts +83 -0
- package/src/lib/geo/reproject.ts +26 -13
- package/src/lib/geo/terrain-elevation.ts +166 -0
- package/src/lib/lens/adapter.ts +1 -1
- package/src/lib/llm/context-builder.ts +1 -1
- package/src/lib/perf/memoryAccounting.test.ts +92 -0
- package/src/lib/perf/memoryAccounting.ts +235 -0
- package/src/sdk/adapters/mutation-view.ts +1 -1
- package/src/store/constants.ts +39 -2
- package/src/store/index.ts +6 -1
- package/src/store/slices/cesiumSlice.ts +1 -1
- package/src/store/slices/idsSlice.ts +24 -0
- package/src/store/slices/loadingSlice.ts +12 -0
- package/src/store/slices/pointCloudSlice.ts +72 -1
- package/src/store/slices/sectionSlice.test.ts +590 -1
- package/src/store/slices/sectionSlice.ts +344 -17
- package/src/store/slices/uiSlice.merge-layers.test.ts +217 -0
- package/src/store/slices/uiSlice.ts +60 -2
- package/src/store/types.ts +42 -0
- package/src/store.ts +13 -0
- package/src/utils/acquireFileBuffer.test.ts +231 -0
- package/src/utils/acquireFileBuffer.ts +128 -0
- package/src/utils/ifcConfig.ts +24 -0
- package/src/utils/nativeSpatialDataStore.ts +20 -2
- package/src/utils/spatialHierarchy.test.ts +116 -0
- package/src/utils/spatialHierarchy.ts +23 -0
- package/tailwind.config.js +5 -0
- package/tsconfig.json +1 -0
- package/vite.config.ts +6 -0
- package/dist/assets/decode-worker-Collf_X_.js +0 -1320
- package/dist/assets/drawing-2d-DoxKMqbO.js +0 -257
- package/dist/assets/exporters-xbXqEDlO.js +0 -81590
- package/dist/assets/geometry.worker-DQEZB2rB.js +0 -1
- package/dist/assets/ids-2WdONLlu.js +0 -2033
- package/dist/assets/ifc-lite_bg-4yUkDRD8.wasm +0 -0
- package/dist/assets/index-BXeEKqJG.css +0 -1
|
@@ -35,4 +35,120 @@ describe('rebuildSpatialHierarchy', () => {
|
|
|
35
35
|
assert.equal(hierarchy.elementToStorey.get(4), undefined);
|
|
36
36
|
assert.deepEqual(hierarchy.getPath(4).map((node) => node.expressId), [1, 2, 3]);
|
|
37
37
|
});
|
|
38
|
+
|
|
39
|
+
it('propagates storey assignment to aggregated descendants of wall parts (Revit multilayer walls)', () => {
|
|
40
|
+
// Scenario: Revit-exported wall with three IfcBuildingElementPart aggregate
|
|
41
|
+
// children. The wall is directly contained in the storey via
|
|
42
|
+
// IfcRelContainedInSpatialStructure; the parts are reachable only through
|
|
43
|
+
// IfcRelAggregates. Pre-fix, the parts had no `elementToStorey` entry and
|
|
44
|
+
// clicking a part returned "no storey". The fix walks aggregate descendants
|
|
45
|
+
// of every storey-contained element and inherits the storey assignment.
|
|
46
|
+
const strings = new StringTable();
|
|
47
|
+
const entities = new EntityTableBuilder(8, strings);
|
|
48
|
+
entities.add(1, 'IFCPROJECT', 'p0', 'Project', '', '');
|
|
49
|
+
entities.add(2, 'IFCSITE', 's0', 'Site', '', '');
|
|
50
|
+
entities.add(3, 'IFCBUILDING', 'b0', 'Building', '', '');
|
|
51
|
+
entities.add(4, 'IFCBUILDINGSTOREY', 'st0', 'Level 1', '', '');
|
|
52
|
+
entities.add(5, 'IFCWALL', 'w0', 'Multilayer Wall', '', '', true);
|
|
53
|
+
entities.add(6, 'IFCBUILDINGELEMENTPART', 'part-a', 'Layer A', '', '', true);
|
|
54
|
+
entities.add(7, 'IFCBUILDINGELEMENTPART', 'part-b', 'Layer B', '', '', true);
|
|
55
|
+
entities.add(8, 'IFCBUILDINGELEMENTPART', 'part-c', 'Layer C', '', '', true);
|
|
56
|
+
|
|
57
|
+
const relationships = new RelationshipGraphBuilder();
|
|
58
|
+
// Spatial decomposition.
|
|
59
|
+
relationships.addEdge(1, 2, RelationshipType.Aggregates, 100);
|
|
60
|
+
relationships.addEdge(2, 3, RelationshipType.Aggregates, 101);
|
|
61
|
+
relationships.addEdge(3, 4, RelationshipType.Aggregates, 102);
|
|
62
|
+
// Wall is directly contained in the storey.
|
|
63
|
+
relationships.addEdge(4, 5, RelationshipType.ContainsElements, 103);
|
|
64
|
+
// Parts are aggregated into the wall (NOT directly contained in the storey).
|
|
65
|
+
relationships.addEdge(5, 6, RelationshipType.Aggregates, 104);
|
|
66
|
+
relationships.addEdge(5, 7, RelationshipType.Aggregates, 105);
|
|
67
|
+
relationships.addEdge(5, 8, RelationshipType.Aggregates, 106);
|
|
68
|
+
|
|
69
|
+
const hierarchy = rebuildSpatialHierarchy(entities.build(), relationships.build());
|
|
70
|
+
assert.ok(hierarchy);
|
|
71
|
+
|
|
72
|
+
// The wall itself is in elementToStorey (direct containment).
|
|
73
|
+
assert.equal(hierarchy.elementToStorey.get(5), 4);
|
|
74
|
+
|
|
75
|
+
// Each aggregated part now inherits the wall's storey assignment.
|
|
76
|
+
assert.equal(hierarchy.elementToStorey.get(6), 4);
|
|
77
|
+
assert.equal(hierarchy.elementToStorey.get(7), 4);
|
|
78
|
+
assert.equal(hierarchy.elementToStorey.get(8), 4);
|
|
79
|
+
|
|
80
|
+
// getStoreyElements keeps its contract: only directly-contained elements.
|
|
81
|
+
// Parts are intentionally NOT in this list — tree views rely on this.
|
|
82
|
+
assert.deepEqual(hierarchy.getStoreyElements(4), [5]);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('preserves direct storey containment when a part is also aggregated under another element', () => {
|
|
86
|
+
// Edge case: a part is directly contained in storey A via
|
|
87
|
+
// IfcRelContainedInSpatialStructure AND aggregated under a wall in storey B.
|
|
88
|
+
// The direct containment must win (set first; descendant walk uses
|
|
89
|
+
// `if (!elementToStorey.has(...))` to avoid clobbering).
|
|
90
|
+
const strings = new StringTable();
|
|
91
|
+
const entities = new EntityTableBuilder(6, strings);
|
|
92
|
+
entities.add(1, 'IFCPROJECT', 'p0', 'Project', '', '');
|
|
93
|
+
entities.add(2, 'IFCBUILDING', 'b0', 'Building', '', '');
|
|
94
|
+
entities.add(3, 'IFCBUILDINGSTOREY', 'st-a', 'Storey A', '', '');
|
|
95
|
+
entities.add(4, 'IFCBUILDINGSTOREY', 'st-b', 'Storey B', '', '');
|
|
96
|
+
entities.add(5, 'IFCWALL', 'w0', 'Wall (Storey B)', '', '', true);
|
|
97
|
+
entities.add(6, 'IFCBUILDINGELEMENTPART', 'part-x', 'Shared Part', '', '', true);
|
|
98
|
+
|
|
99
|
+
const relationships = new RelationshipGraphBuilder();
|
|
100
|
+
relationships.addEdge(1, 2, RelationshipType.Aggregates, 200);
|
|
101
|
+
relationships.addEdge(2, 3, RelationshipType.Aggregates, 201);
|
|
102
|
+
relationships.addEdge(2, 4, RelationshipType.Aggregates, 202);
|
|
103
|
+
// Part is directly contained in storey A.
|
|
104
|
+
relationships.addEdge(3, 6, RelationshipType.ContainsElements, 203);
|
|
105
|
+
// Wall is contained in storey B.
|
|
106
|
+
relationships.addEdge(4, 5, RelationshipType.ContainsElements, 204);
|
|
107
|
+
// Wall aggregates the same part (rare but legal).
|
|
108
|
+
relationships.addEdge(5, 6, RelationshipType.Aggregates, 205);
|
|
109
|
+
|
|
110
|
+
const hierarchy = rebuildSpatialHierarchy(entities.build(), relationships.build());
|
|
111
|
+
assert.ok(hierarchy);
|
|
112
|
+
// Direct containment in storey A wins. The descendant walk from storey B's
|
|
113
|
+
// wall must NOT overwrite this.
|
|
114
|
+
assert.equal(hierarchy.elementToStorey.get(6), 3);
|
|
115
|
+
assert.equal(hierarchy.elementToStorey.get(5), 4);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('terminates in bounded time on malformed aggregate cycles', () => {
|
|
119
|
+
// Cycle guard: part references back to wall via IfcRelAggregates. Without
|
|
120
|
+
// the `seen` set, the descendant walk would infinite-loop.
|
|
121
|
+
const strings = new StringTable();
|
|
122
|
+
const entities = new EntityTableBuilder(6, strings);
|
|
123
|
+
entities.add(1, 'IFCPROJECT', 'p0', 'Project', '', '');
|
|
124
|
+
entities.add(2, 'IFCBUILDING', 'b0', 'Building', '', '');
|
|
125
|
+
entities.add(3, 'IFCBUILDINGSTOREY', 'st0', 'Storey', '', '');
|
|
126
|
+
entities.add(4, 'IFCWALL', 'w0', 'Wall', '', '', true);
|
|
127
|
+
entities.add(5, 'IFCBUILDINGELEMENTPART', 'part-a', 'Part A', '', '', true);
|
|
128
|
+
entities.add(6, 'IFCBUILDINGELEMENTPART', 'part-b', 'Part B', '', '', true);
|
|
129
|
+
|
|
130
|
+
const relationships = new RelationshipGraphBuilder();
|
|
131
|
+
relationships.addEdge(1, 2, RelationshipType.Aggregates, 300);
|
|
132
|
+
relationships.addEdge(2, 3, RelationshipType.Aggregates, 301);
|
|
133
|
+
relationships.addEdge(3, 4, RelationshipType.ContainsElements, 302);
|
|
134
|
+
relationships.addEdge(4, 5, RelationshipType.Aggregates, 303);
|
|
135
|
+
relationships.addEdge(5, 6, RelationshipType.Aggregates, 304);
|
|
136
|
+
// Malformed back-edges forming a cycle: part-b -> wall, part-a -> wall.
|
|
137
|
+
relationships.addEdge(6, 4, RelationshipType.Aggregates, 305);
|
|
138
|
+
relationships.addEdge(5, 4, RelationshipType.Aggregates, 306);
|
|
139
|
+
|
|
140
|
+
const start = Date.now();
|
|
141
|
+
const hierarchy = rebuildSpatialHierarchy(entities.build(), relationships.build());
|
|
142
|
+
const elapsedMs = Date.now() - start;
|
|
143
|
+
|
|
144
|
+
assert.ok(hierarchy);
|
|
145
|
+
// Must finish quickly; without cycle guard this would never terminate.
|
|
146
|
+
// Generous bound for slow CI runners.
|
|
147
|
+
assert.ok(elapsedMs < 1000, `expected fast termination, took ${elapsedMs}ms`);
|
|
148
|
+
|
|
149
|
+
// All entities still get correct storey assignment despite the cycle.
|
|
150
|
+
assert.equal(hierarchy.elementToStorey.get(4), 3);
|
|
151
|
+
assert.equal(hierarchy.elementToStorey.get(5), 3);
|
|
152
|
+
assert.equal(hierarchy.elementToStorey.get(6), 3);
|
|
153
|
+
});
|
|
38
154
|
});
|
|
@@ -103,6 +103,29 @@ export function rebuildSpatialHierarchy(
|
|
|
103
103
|
if (isStoreyLikeSpatialType(typeEnum)) {
|
|
104
104
|
for (const elementId of containedElements) {
|
|
105
105
|
elementToStorey.set(elementId, expressId);
|
|
106
|
+
// Propagate storey assignment to aggregated descendants (e.g. IfcBuildingElementPart
|
|
107
|
+
// children of an IfcWall). Without this, parts have no reverse-lookup entry even
|
|
108
|
+
// though the renderer emits them as standalone meshes.
|
|
109
|
+
// Cycle guard: malformed IFC files can have aggregate cycles.
|
|
110
|
+
// Direct storey containment wins — only set the descendant mapping if not already set.
|
|
111
|
+
const stack: number[] = [elementId];
|
|
112
|
+
const seen = new Set<number>([elementId]);
|
|
113
|
+
while (stack.length > 0) {
|
|
114
|
+
const current = stack.pop() as number;
|
|
115
|
+
const aggregatedKids = relationships.getRelated(
|
|
116
|
+
current,
|
|
117
|
+
RelationshipType.Aggregates,
|
|
118
|
+
'forward'
|
|
119
|
+
);
|
|
120
|
+
for (const kid of aggregatedKids) {
|
|
121
|
+
if (seen.has(kid)) continue;
|
|
122
|
+
seen.add(kid);
|
|
123
|
+
if (!elementToStorey.has(kid)) {
|
|
124
|
+
elementToStorey.set(kid, expressId);
|
|
125
|
+
}
|
|
126
|
+
stack.push(kid);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
106
129
|
}
|
|
107
130
|
}
|
|
108
131
|
|
package/tailwind.config.js
CHANGED
|
@@ -60,11 +60,16 @@ export default {
|
|
|
60
60
|
'0%, 100%': { opacity: '1' },
|
|
61
61
|
'50%': { opacity: '0.7' },
|
|
62
62
|
},
|
|
63
|
+
'fade-in-up': {
|
|
64
|
+
from: { opacity: '0', transform: 'translateY(-4px)' },
|
|
65
|
+
to: { opacity: '1', transform: 'translateY(0)' },
|
|
66
|
+
},
|
|
63
67
|
},
|
|
64
68
|
animation: {
|
|
65
69
|
'accordion-down': 'accordion-down 0.2s ease-out',
|
|
66
70
|
'accordion-up': 'accordion-up 0.2s ease-out',
|
|
67
71
|
'pulse-subtle': 'pulse-subtle 2s ease-in-out infinite',
|
|
72
|
+
'fade-in-up': 'fade-in-up 180ms ease-out both',
|
|
68
73
|
},
|
|
69
74
|
},
|
|
70
75
|
},
|
package/tsconfig.json
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
"paths": {
|
|
8
8
|
"@/*": ["./src/*"],
|
|
9
9
|
"@ifc-lite/parser": ["../../packages/parser/src"],
|
|
10
|
+
"@ifc-lite/parser/browser": ["../../packages/parser/src/browser"],
|
|
10
11
|
"@ifc-lite/geometry": ["../../packages/geometry/src"],
|
|
11
12
|
"@ifc-lite/renderer": ["../../packages/renderer/src"],
|
|
12
13
|
"@ifc-lite/query": ["../../packages/query/src"],
|
package/vite.config.ts
CHANGED
|
@@ -243,6 +243,7 @@ export default defineConfig({
|
|
|
243
243
|
resolve: {
|
|
244
244
|
alias: {
|
|
245
245
|
'@': path.resolve(__dirname, './src'),
|
|
246
|
+
'@ifc-lite/parser/browser': path.resolve(__dirname, '../../packages/parser/src/browser.ts'),
|
|
246
247
|
'@ifc-lite/parser': path.resolve(__dirname, '../../packages/parser/src'),
|
|
247
248
|
'@ifc-lite/geometry': path.resolve(__dirname, '../../packages/geometry/src'),
|
|
248
249
|
'@ifc-lite/renderer': path.resolve(__dirname, '../../packages/renderer/src'),
|
|
@@ -255,6 +256,11 @@ export default defineConfig({
|
|
|
255
256
|
'@ifc-lite/ifcx': path.resolve(__dirname, '../../packages/ifcx/src'),
|
|
256
257
|
'@ifc-lite/pointcloud': path.resolve(__dirname, '../../packages/pointcloud/src'),
|
|
257
258
|
'@ifc-lite/wasm': path.resolve(__dirname, '../../packages/wasm/pkg/ifc-lite.js'),
|
|
259
|
+
// Phase 2 of single-controller-rayon-design.md — threaded bundle
|
|
260
|
+
// imported by `geometry-controller.worker.ts`. Kept separate from
|
|
261
|
+
// `@ifc-lite/wasm` so legacy code that doesn't need threading
|
|
262
|
+
// stays on the slim single-thread bundle.
|
|
263
|
+
'@ifc-lite/wasm-threaded': path.resolve(__dirname, '../../packages/wasm-threaded/pkg/ifc-lite.js'),
|
|
258
264
|
'@ifc-lite/sdk': path.resolve(__dirname, '../../packages/sdk/src'),
|
|
259
265
|
'@ifc-lite/create': path.resolve(__dirname, '../../packages/create/src'),
|
|
260
266
|
'@ifc-lite/sandbox/schema': path.resolve(__dirname, '../../packages/sandbox/src/bridge-schema.ts'),
|