@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.
Files changed (106) hide show
  1. package/.turbo/turbo-build.log +59 -44
  2. package/.turbo/turbo-typecheck.log +1 -1
  3. package/CHANGELOG.md +488 -0
  4. package/dist/assets/{basketViewActivator-CA2CTcVo.js → basketViewActivator-Bzw51jhm.js} +6 -6
  5. package/dist/assets/decode-worker-t2EGKAxO.js +1708 -0
  6. package/dist/assets/drawing-2d-Bjy8YPrg.js +257 -0
  7. package/dist/assets/exporters-u0sz2Upj.js +259119 -0
  8. package/dist/assets/geometry-controller.worker-NH8pZmrU.js +7 -0
  9. package/dist/assets/geometry.worker-Bp4rW_R1.js +1 -0
  10. package/dist/assets/ids-B7AXEv7h.js +4067 -0
  11. package/dist/assets/ifc-lite-DfZHk36-.js +7 -0
  12. package/dist/assets/ifc-lite_bg-DlKs5-yM.wasm +0 -0
  13. package/dist/assets/ifc-lite_bg-PqmRe3Ph.wasm +0 -0
  14. package/dist/assets/index-CSWgTe1s.css +1 -0
  15. package/dist/assets/{index-D8Epw-e7.js → index-DVNSvEMh.js} +40146 -35823
  16. package/dist/assets/laz-perf-Cvr_Lepg.js +1 -0
  17. package/dist/assets/laz-perf-DnSyzVYH.wasm +0 -0
  18. package/dist/assets/{native-bridge-DKmx1z95.js → native-bridge-BiD01jI9.js} +1 -1
  19. package/dist/assets/parser.worker-Bnbrl6gy.js +182 -0
  20. package/dist/assets/{sandbox-tccwm5Bo.js → sandbox-DPD1ROr0.js} +4 -4
  21. package/dist/assets/{server-client-LoWPK1N2.js → server-client-DP8fMPY9.js} +1 -1
  22. package/dist/assets/{wasm-bridge-BsJGgPMs.js → wasm-bridge-CErti6zX.js} +1 -1
  23. package/dist/assets/workerHelpers-CBbWSJmd.js +36 -0
  24. package/dist/index.html +8 -8
  25. package/index.html +1 -1
  26. package/package.json +10 -10
  27. package/src/components/viewer/BasketPresentationDock.tsx +3 -0
  28. package/src/components/viewer/CesiumOverlay.tsx +165 -120
  29. package/src/components/viewer/DeviationPanel.tsx +172 -0
  30. package/src/components/viewer/HierarchyPanel.tsx +29 -3
  31. package/src/components/viewer/HoverTooltip.tsx +5 -0
  32. package/src/components/viewer/IDSAuditSummary.tsx +389 -0
  33. package/src/components/viewer/IDSPanel.tsx +80 -26
  34. package/src/components/viewer/MainToolbar.tsx +60 -7
  35. package/src/components/viewer/MergeLayersBanner.tsx +108 -0
  36. package/src/components/viewer/MobileToolbar.tsx +326 -0
  37. package/src/components/viewer/PointCloudClasses.tsx +111 -0
  38. package/src/components/viewer/PointCloudLegend.tsx +119 -0
  39. package/src/components/viewer/PointCloudPanel.tsx +52 -1
  40. package/src/components/viewer/PropertiesPanel.tsx +37 -6
  41. package/src/components/viewer/RectSelectionOverlay.tsx +48 -0
  42. package/src/components/viewer/StatusBar.tsx +14 -0
  43. package/src/components/viewer/ViewerLayout.tsx +288 -95
  44. package/src/components/viewer/Viewport.tsx +86 -18
  45. package/src/components/viewer/ViewportContainer.tsx +25 -11
  46. package/src/components/viewer/ViewportOverlays.tsx +41 -26
  47. package/src/components/viewer/mouseHandlerTypes.ts +22 -0
  48. package/src/components/viewer/properties/GeoreferencingPanel.tsx +77 -8
  49. package/src/components/viewer/properties/MaterialCard.tsx +2 -2
  50. package/src/components/viewer/selectionHandlers.ts +41 -0
  51. package/src/components/viewer/tools/SectionPanel.tsx +181 -24
  52. package/src/components/viewer/tools/SectionVisualization.tsx +384 -3
  53. package/src/components/viewer/useAnimationLoop.ts +22 -0
  54. package/src/components/viewer/useMouseControls.ts +296 -3
  55. package/src/components/viewer/usePointCloudSync.ts +8 -1
  56. package/src/components/viewer/useRenderUpdates.ts +21 -1
  57. package/src/components/viewer/useTouchControls.ts +100 -41
  58. package/src/hooks/federationLoadGate.test.ts +90 -0
  59. package/src/hooks/federationLoadGate.ts +127 -0
  60. package/src/hooks/ids/idsDataAccessor.ts +11 -259
  61. package/src/hooks/ingest/pointCloudIngest.ts +127 -16
  62. package/src/hooks/useDrawingGeneration.ts +81 -8
  63. package/src/hooks/useIDS.ts +90 -10
  64. package/src/hooks/useIfcFederation.ts +94 -16
  65. package/src/hooks/useIfcLoader.ts +289 -64
  66. package/src/hooks/useViewerSelectors.ts +10 -0
  67. package/src/lib/geo/cesium-bridge.ts +84 -67
  68. package/src/lib/geo/clamp-anchor.test.ts +80 -0
  69. package/src/lib/geo/clamp-anchor.ts +57 -0
  70. package/src/lib/geo/effective-georef.test.ts +79 -1
  71. package/src/lib/geo/effective-georef.ts +83 -0
  72. package/src/lib/geo/reproject.ts +26 -13
  73. package/src/lib/geo/terrain-elevation.ts +166 -0
  74. package/src/lib/lens/adapter.ts +1 -1
  75. package/src/lib/llm/context-builder.ts +1 -1
  76. package/src/lib/perf/memoryAccounting.test.ts +92 -0
  77. package/src/lib/perf/memoryAccounting.ts +235 -0
  78. package/src/sdk/adapters/mutation-view.ts +1 -1
  79. package/src/store/constants.ts +39 -2
  80. package/src/store/index.ts +6 -1
  81. package/src/store/slices/cesiumSlice.ts +1 -1
  82. package/src/store/slices/idsSlice.ts +24 -0
  83. package/src/store/slices/loadingSlice.ts +12 -0
  84. package/src/store/slices/pointCloudSlice.ts +72 -1
  85. package/src/store/slices/sectionSlice.test.ts +590 -1
  86. package/src/store/slices/sectionSlice.ts +344 -17
  87. package/src/store/slices/uiSlice.merge-layers.test.ts +217 -0
  88. package/src/store/slices/uiSlice.ts +60 -2
  89. package/src/store/types.ts +42 -0
  90. package/src/store.ts +13 -0
  91. package/src/utils/acquireFileBuffer.test.ts +231 -0
  92. package/src/utils/acquireFileBuffer.ts +128 -0
  93. package/src/utils/ifcConfig.ts +24 -0
  94. package/src/utils/nativeSpatialDataStore.ts +20 -2
  95. package/src/utils/spatialHierarchy.test.ts +116 -0
  96. package/src/utils/spatialHierarchy.ts +23 -0
  97. package/tailwind.config.js +5 -0
  98. package/tsconfig.json +1 -0
  99. package/vite.config.ts +6 -0
  100. package/dist/assets/decode-worker-Collf_X_.js +0 -1320
  101. package/dist/assets/drawing-2d-DoxKMqbO.js +0 -257
  102. package/dist/assets/exporters-xbXqEDlO.js +0 -81590
  103. package/dist/assets/geometry.worker-DQEZB2rB.js +0 -1
  104. package/dist/assets/ids-2WdONLlu.js +0 -2033
  105. package/dist/assets/ifc-lite_bg-4yUkDRD8.wasm +0 -0
  106. 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
 
@@ -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'),