@ifc-lite/viewer 1.16.0 → 1.17.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 (55) hide show
  1. package/.turbo/turbo-build.log +46 -0
  2. package/.turbo/turbo-typecheck.log +4 -0
  3. package/CHANGELOG.md +15 -0
  4. package/dist/assets/{Arrow.dom--gdrQd-q.js → Arrow.dom-CcoDLP6E.js} +1 -1
  5. package/dist/assets/{basketViewActivator-CI3y6VYQ.js → basketViewActivator-FtbS__bG.js} +1 -1
  6. package/dist/assets/{browser-vWDubxDI.js → browser-CXd3z0DO.js} +1 -1
  7. package/dist/assets/ifc-lite-TI3u_Zyw.js +7 -0
  8. package/dist/assets/ifc-lite_bg-DeZrXTKQ.wasm +0 -0
  9. package/dist/assets/index-Ba4eoTe7.css +1 -0
  10. package/dist/assets/{index-BImINgzG.js → index-D99fzcwI.js} +28962 -26947
  11. package/dist/assets/{index-RXIK18da.js → index-DqNiuQep.js} +4 -4
  12. package/dist/assets/{native-bridge-4rLidc3f.js → native-bridge-DjDj2M6p.js} +1 -1
  13. package/dist/assets/{wasm-bridge-BkfXfw8O.js → wasm-bridge-CDTF4ZQc.js} +1 -1
  14. package/dist/assets/workerHelpers-G7llXNMi.js +36 -0
  15. package/dist/index.html +2 -2
  16. package/package.json +15 -14
  17. package/src/components/viewer/BCFPanel.tsx +12 -0
  18. package/src/components/viewer/BulkPropertyEditor.tsx +315 -154
  19. package/src/components/viewer/CommandPalette.tsx +0 -6
  20. package/src/components/viewer/DataConnector.tsx +489 -284
  21. package/src/components/viewer/ExportDialog.tsx +66 -6
  22. package/src/components/viewer/KeyboardShortcutsDialog.tsx +44 -1
  23. package/src/components/viewer/MainToolbar.tsx +1 -5
  24. package/src/components/viewer/Viewport.tsx +42 -56
  25. package/src/components/viewer/ViewportContainer.tsx +3 -0
  26. package/src/components/viewer/ViewportOverlays.tsx +12 -10
  27. package/src/components/viewer/bcf/BCFOverlay.tsx +254 -0
  28. package/src/components/viewer/lists/ListPanel.tsx +0 -21
  29. package/src/components/viewer/lists/ListResultsTable.tsx +93 -5
  30. package/src/components/viewer/measureHandlers.ts +558 -0
  31. package/src/components/viewer/mouseHandlerTypes.ts +108 -0
  32. package/src/components/viewer/selectionHandlers.ts +86 -0
  33. package/src/components/viewer/useAnimationLoop.ts +116 -44
  34. package/src/components/viewer/useGeometryStreaming.ts +155 -367
  35. package/src/components/viewer/useKeyboardControls.ts +30 -46
  36. package/src/components/viewer/useMouseControls.ts +169 -695
  37. package/src/components/viewer/useRenderUpdates.ts +9 -59
  38. package/src/components/viewer/useTouchControls.ts +55 -40
  39. package/src/hooks/bcfIdLookup.ts +70 -0
  40. package/src/hooks/useBCF.ts +12 -31
  41. package/src/hooks/useIfcCache.ts +2 -20
  42. package/src/hooks/useIfcFederation.ts +5 -11
  43. package/src/hooks/useIfcLoader.ts +47 -56
  44. package/src/hooks/useIfcServer.ts +9 -1
  45. package/src/hooks/useKeyboardShortcuts.ts +0 -10
  46. package/src/hooks/useLatestRef.ts +24 -0
  47. package/src/sdk/adapters/export-adapter.ts +2 -2
  48. package/src/sdk/adapters/model-adapter.ts +1 -0
  49. package/src/sdk/local-backend.ts +2 -0
  50. package/src/store/basketVisibleSet.ts +12 -0
  51. package/src/store/slices/bcfSlice.ts +9 -0
  52. package/src/utils/loadingUtils.ts +46 -0
  53. package/src/utils/serverDataModel.ts +4 -3
  54. package/dist/assets/ifc-lite_bg-CyWQTvp5.wasm +0 -0
  55. package/dist/assets/index-ax1X2WPd.css +0 -1
@@ -4,7 +4,7 @@
4
4
 
5
5
  /**
6
6
  * Keyboard controls hook for the 3D viewport
7
- * Handles keyboard shortcuts, first-person mode, continuous movement
7
+ * Handles keyboard shortcuts, walk mode, continuous movement
8
8
  */
9
9
 
10
10
  import { useEffect, type MutableRefObject } from 'react';
@@ -37,6 +37,12 @@ export interface UseKeyboardControlsParams {
37
37
  calculateScale: () => void;
38
38
  }
39
39
 
40
+ /** Keys that trigger continuous movement (arrow keys + WASD + shift for sprint) */
41
+ const MOVEMENT_KEYS = new Set([
42
+ 'arrowup', 'arrowdown', 'arrowleft', 'arrowright',
43
+ 'w', 's', 'a', 'd', 'shift',
44
+ ]);
45
+
40
46
  export function useKeyboardControls(params: UseKeyboardControlsParams): void {
41
47
  const {
42
48
  rendererRef,
@@ -69,6 +75,10 @@ export function useKeyboardControls(params: UseKeyboardControlsParams): void {
69
75
  let moveLoopRunning = false;
70
76
  let moveFrameId: number | null = null;
71
77
 
78
+ const renderScene = () => {
79
+ renderer.requestRender();
80
+ };
81
+
72
82
  const handleKeyDown = (e: KeyboardEvent) => {
73
83
  const target = e.target as HTMLElement;
74
84
  if (
@@ -82,8 +92,7 @@ export function useKeyboardControls(params: UseKeyboardControlsParams): void {
82
92
  keyState[e.key.toLowerCase()] = true;
83
93
 
84
94
  // Start movement loop when a movement key is pressed
85
- const isMovementKey = ['arrowup', 'arrowdown', 'arrowleft', 'arrowright'].includes(e.key.toLowerCase());
86
- if (isMovementKey && !moveLoopRunning) {
95
+ if (MOVEMENT_KEYS.has(e.key.toLowerCase()) && !moveLoopRunning) {
87
96
  moveLoopRunning = true;
88
97
  keyboardMove();
89
98
  }
@@ -92,18 +101,7 @@ export function useKeyboardControls(params: UseKeyboardControlsParams): void {
92
101
  const setViewAndRender = (view: 'top' | 'bottom' | 'front' | 'back' | 'left' | 'right') => {
93
102
  const rotation = coordinateInfoRef.current?.buildingRotation;
94
103
  camera.setPresetView(view, geometryBoundsRef.current, rotation);
95
- renderer.render({
96
- hiddenIds: hiddenEntitiesRef.current,
97
- isolatedIds: isolatedEntitiesRef.current,
98
- selectedId: selectedEntityIdRef.current,
99
- selectedModelIndex: selectedModelIndexRef.current,
100
- clearColor: clearColorRef.current,
101
- sectionPlane: activeToolRef.current === 'section' ? {
102
- ...sectionPlaneRef.current,
103
- min: sectionRangeRef.current?.min,
104
- max: sectionRangeRef.current?.max,
105
- } : undefined,
106
- });
104
+ renderScene();
107
105
  updateCameraRotationRealtime(camera.getRotation());
108
106
  calculateScale();
109
107
  };
@@ -119,13 +117,11 @@ export function useKeyboardControls(params: UseKeyboardControlsParams): void {
119
117
  if (e.key === 'f' || e.key === 'F') {
120
118
  const selectedId = selectedEntityIdRef.current;
121
119
  if (selectedId !== null) {
122
- // Frame selection - zoom to fit selected element
123
120
  const bounds = getEntityBounds(geometryRef.current, selectedId);
124
121
  if (bounds) {
125
122
  camera.frameBounds(bounds.min, bounds.max, 300);
126
123
  }
127
124
  } else {
128
- // No selection - fit all
129
125
  camera.zoomExtent(geometryBoundsRef.current.min, geometryBoundsRef.current.max, 300);
130
126
  }
131
127
  calculateScale();
@@ -141,20 +137,14 @@ export function useKeyboardControls(params: UseKeyboardControlsParams): void {
141
137
  camera.zoomExtent(geometryBoundsRef.current.min, geometryBoundsRef.current.max, 300);
142
138
  calculateScale();
143
139
  }
144
-
145
- // Toggle first-person mode
146
- if (e.key === 'c' || e.key === 'C') {
147
- firstPersonModeRef.current = !firstPersonModeRef.current;
148
- camera.enableFirstPersonMode(firstPersonModeRef.current);
149
- }
150
140
  };
151
141
 
152
142
  const handleKeyUp = (e: KeyboardEvent) => {
153
143
  keyState[e.key.toLowerCase()] = false;
154
144
 
155
145
  // Stop movement loop when no movement keys are held
156
- const anyMovementKey = keyState['arrowup'] || keyState['arrowdown'] || keyState['arrowleft'] || keyState['arrowright'];
157
- if (!anyMovementKey && moveLoopRunning) {
146
+ const anyHeld = Array.from(MOVEMENT_KEYS).some(k => keyState[k]);
147
+ if (!anyHeld && moveLoopRunning) {
158
148
  moveLoopRunning = false;
159
149
  if (moveFrameId !== null) {
160
150
  cancelAnimationFrame(moveFrameId);
@@ -170,16 +160,21 @@ export function useKeyboardControls(params: UseKeyboardControlsParams): void {
170
160
  if (aborted || !moveLoopRunning) return;
171
161
 
172
162
  let moved = false;
173
- const panSpeed = 5;
174
-
175
- if (firstPersonModeRef.current) {
176
- // Arrow keys for first-person navigation (camera-relative)
177
- if (keyState['arrowup']) { camera.moveFirstPerson(-1, 0, 0); moved = true; }
178
- if (keyState['arrowdown']) { camera.moveFirstPerson(1, 0, 0); moved = true; }
179
- if (keyState['arrowleft']) { camera.moveFirstPerson(0, 1, 0); moved = true; }
180
- if (keyState['arrowright']) { camera.moveFirstPerson(0, -1, 0); moved = true; }
163
+ const isWalkMode = activeToolRef.current === 'walk';
164
+
165
+ if (isWalkMode) {
166
+ // Walk mode: arrow keys + WASD move on horizontal plane
167
+ // Up/W = forward, Down/S = backward, Left/A = strafe left, Right/D = strafe right
168
+ const fwd = (keyState['arrowup'] || keyState['w'] ? 1 : 0) + (keyState['arrowdown'] || keyState['s'] ? -1 : 0);
169
+ const strafe = (keyState['arrowleft'] || keyState['a'] ? -1 : 0) + (keyState['arrowright'] || keyState['d'] ? 1 : 0);
170
+ if (fwd !== 0 || strafe !== 0) {
171
+ const sprint = keyState['shift'] ? 2 : 1;
172
+ camera.moveFirstPerson(fwd * sprint, strafe * sprint, 0);
173
+ moved = true;
174
+ }
181
175
  } else {
182
- // Arrow keys for panning (camera-relative: arrow direction = camera movement)
176
+ // Normal mode: arrow keys pan the view
177
+ const panSpeed = 5;
183
178
  if (keyState['arrowup']) { camera.pan(0, -panSpeed, false); moved = true; }
184
179
  if (keyState['arrowdown']) { camera.pan(0, panSpeed, false); moved = true; }
185
180
  if (keyState['arrowleft']) { camera.pan(panSpeed, 0, false); moved = true; }
@@ -187,18 +182,7 @@ export function useKeyboardControls(params: UseKeyboardControlsParams): void {
187
182
  }
188
183
 
189
184
  if (moved) {
190
- renderer.render({
191
- hiddenIds: hiddenEntitiesRef.current,
192
- isolatedIds: isolatedEntitiesRef.current,
193
- selectedId: selectedEntityIdRef.current,
194
- selectedModelIndex: selectedModelIndexRef.current,
195
- clearColor: clearColorRef.current,
196
- sectionPlane: activeToolRef.current === 'section' ? {
197
- ...sectionPlaneRef.current,
198
- min: sectionRangeRef.current?.min,
199
- max: sectionRangeRef.current?.max,
200
- } : undefined,
201
- });
185
+ renderScene();
202
186
  }
203
187
  moveFrameId = requestAnimationFrame(keyboardMove);
204
188
  };