@pascal-app/viewer 1.0.0-beta.5 → 1.0.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 (190) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +55 -0
  3. package/dist/capture/asset-url.d.ts +2 -0
  4. package/dist/capture/asset-url.d.ts.map +1 -0
  5. package/dist/capture/asset-url.js +13 -0
  6. package/dist/capture/capture-runtime.d.ts +43 -0
  7. package/dist/capture/capture-runtime.d.ts.map +1 -0
  8. package/dist/capture/capture-runtime.js +228 -0
  9. package/dist/capture/frame.d.ts +4 -0
  10. package/dist/capture/frame.d.ts.map +1 -0
  11. package/dist/capture/frame.js +21 -0
  12. package/dist/capture/index.d.ts +12 -0
  13. package/dist/capture/index.d.ts.map +1 -0
  14. package/dist/capture/index.js +11 -0
  15. package/dist/capture/layer-visibility.d.ts +5 -0
  16. package/dist/capture/layer-visibility.d.ts.map +1 -0
  17. package/dist/capture/layer-visibility.js +11 -0
  18. package/dist/capture/layers/clay-matcap.d.ts +3 -0
  19. package/dist/capture/layers/clay-matcap.d.ts.map +1 -0
  20. package/dist/capture/layers/clay-matcap.js +26 -0
  21. package/dist/capture/layers/device-motion-layer.d.ts +7 -0
  22. package/dist/capture/layers/device-motion-layer.d.ts.map +1 -0
  23. package/dist/capture/layers/device-motion-layer.js +78 -0
  24. package/dist/capture/layers/point-cloud-layer.d.ts +15 -0
  25. package/dist/capture/layers/point-cloud-layer.d.ts.map +1 -0
  26. package/dist/capture/layers/point-cloud-layer.js +106 -0
  27. package/dist/capture/layers/room-model-layer.d.ts +9 -0
  28. package/dist/capture/layers/room-model-layer.d.ts.map +1 -0
  29. package/dist/capture/layers/room-model-layer.js +71 -0
  30. package/dist/capture/layers/surface-mesh-data.d.ts +9 -0
  31. package/dist/capture/layers/surface-mesh-data.d.ts.map +1 -0
  32. package/dist/capture/layers/surface-mesh-data.js +74 -0
  33. package/dist/capture/layers/surface-mesh-layer.d.ts +7 -0
  34. package/dist/capture/layers/surface-mesh-layer.d.ts.map +1 -0
  35. package/dist/capture/layers/surface-mesh-layer.js +16 -0
  36. package/dist/capture/preview.d.ts +3 -0
  37. package/dist/capture/preview.d.ts.map +1 -0
  38. package/dist/capture/preview.js +2 -0
  39. package/dist/capture/source-state.d.ts +22 -0
  40. package/dist/capture/source-state.d.ts.map +1 -0
  41. package/dist/capture/source-state.js +182 -0
  42. package/dist/capture/stream-rendering.d.ts +14 -0
  43. package/dist/capture/stream-rendering.d.ts.map +1 -0
  44. package/dist/capture/stream-rendering.js +69 -0
  45. package/dist/capture/trajectory.d.ts +20 -0
  46. package/dist/capture/trajectory.d.ts.map +1 -0
  47. package/dist/capture/trajectory.js +77 -0
  48. package/dist/components/viewer/batched-mesh-spike.d.ts +14 -0
  49. package/dist/components/viewer/batched-mesh-spike.d.ts.map +1 -0
  50. package/dist/components/viewer/batched-mesh-spike.js +64 -0
  51. package/dist/components/viewer/frame-limiter.d.ts.map +1 -1
  52. package/dist/components/viewer/frame-limiter.js +4 -3
  53. package/dist/components/viewer/index.d.ts.map +1 -1
  54. package/dist/components/viewer/index.js +70 -62
  55. package/dist/components/viewer/lights.d.ts.map +1 -1
  56. package/dist/components/viewer/lights.js +123 -89
  57. package/dist/components/viewer/perf-monitor.d.ts +6 -1
  58. package/dist/components/viewer/perf-monitor.d.ts.map +1 -1
  59. package/dist/components/viewer/perf-monitor.js +307 -116
  60. package/dist/components/viewer/perf-panel.d.ts +3 -0
  61. package/dist/components/viewer/perf-panel.d.ts.map +1 -0
  62. package/dist/components/viewer/perf-panel.js +178 -0
  63. package/dist/components/viewer/pointer-raycast-layers.d.ts +15 -0
  64. package/dist/components/viewer/pointer-raycast-layers.d.ts.map +1 -0
  65. package/dist/components/viewer/pointer-raycast-layers.js +28 -0
  66. package/dist/components/viewer/post-processing.d.ts.map +1 -1
  67. package/dist/components/viewer/post-processing.js +124 -83
  68. package/dist/components/viewer/registered-systems.d.ts +14 -5
  69. package/dist/components/viewer/registered-systems.d.ts.map +1 -1
  70. package/dist/components/viewer/registered-systems.js +22 -10
  71. package/dist/components/viewer/scene-atmosphere.d.ts +30 -0
  72. package/dist/components/viewer/scene-atmosphere.d.ts.map +1 -0
  73. package/dist/components/viewer/scene-atmosphere.js +89 -0
  74. package/dist/components/viewer/scene-environment.d.ts.map +1 -1
  75. package/dist/components/viewer/scene-environment.js +19 -5
  76. package/dist/components/viewer/scene-ground-replacement.d.ts +5 -0
  77. package/dist/components/viewer/scene-ground-replacement.d.ts.map +1 -0
  78. package/dist/components/viewer/scene-ground-replacement.js +35 -0
  79. package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
  80. package/dist/components/viewer/viewer-camera.js +4 -1
  81. package/dist/components/viewer/viewer-presentations.d.ts +67 -0
  82. package/dist/components/viewer/viewer-presentations.d.ts.map +1 -0
  83. package/dist/components/viewer/viewer-presentations.js +120 -0
  84. package/dist/hooks/use-library-materials-version.d.ts +7 -0
  85. package/dist/hooks/use-library-materials-version.d.ts.map +1 -0
  86. package/dist/hooks/use-library-materials-version.js +10 -0
  87. package/dist/hooks/use-node-events.d.ts.map +1 -1
  88. package/dist/hooks/use-node-events.js +1 -0
  89. package/dist/index.d.ts +15 -6
  90. package/dist/index.d.ts.map +1 -1
  91. package/dist/index.js +15 -6
  92. package/dist/lib/csg-utils.d.ts +1 -0
  93. package/dist/lib/csg-utils.d.ts.map +1 -1
  94. package/dist/lib/csg-utils.js +6 -1
  95. package/dist/lib/gpu-perf.d.ts +0 -2
  96. package/dist/lib/gpu-perf.d.ts.map +1 -1
  97. package/dist/lib/gpu-perf.js +7 -21
  98. package/dist/lib/layer-pass.d.ts +28 -0
  99. package/dist/lib/layer-pass.d.ts.map +1 -0
  100. package/dist/lib/layer-pass.js +254 -0
  101. package/dist/lib/materials.d.ts +12 -0
  102. package/dist/lib/materials.d.ts.map +1 -1
  103. package/dist/lib/materials.js +51 -17
  104. package/dist/lib/merged-outline-node.d.ts +17 -8
  105. package/dist/lib/merged-outline-node.d.ts.map +1 -1
  106. package/dist/lib/merged-outline-node.js +194 -52
  107. package/dist/lib/perf-actions.d.ts +55 -0
  108. package/dist/lib/perf-actions.d.ts.map +1 -0
  109. package/dist/lib/perf-actions.js +211 -0
  110. package/dist/lib/perf-observers.d.ts +2 -0
  111. package/dist/lib/perf-observers.d.ts.map +1 -0
  112. package/dist/lib/perf-observers.js +17 -0
  113. package/dist/lib/perf-panel-store.d.ts +71 -0
  114. package/dist/lib/perf-panel-store.d.ts.map +1 -0
  115. package/dist/lib/perf-panel-store.js +29 -0
  116. package/dist/lib/perf-tracks.d.ts +43 -0
  117. package/dist/lib/perf-tracks.d.ts.map +1 -0
  118. package/dist/lib/perf-tracks.js +113 -0
  119. package/dist/lib/pointer-events.d.ts +7 -0
  120. package/dist/lib/pointer-events.d.ts.map +1 -0
  121. package/dist/lib/pointer-events.js +680 -0
  122. package/dist/lib/post-processing-resources.d.ts +12 -0
  123. package/dist/lib/post-processing-resources.d.ts.map +1 -0
  124. package/dist/lib/post-processing-resources.js +19 -0
  125. package/dist/lib/renderer-capability.d.ts.map +1 -1
  126. package/dist/lib/renderer-capability.js +4 -2
  127. package/dist/lib/scene-visibility.d.ts +2 -1
  128. package/dist/lib/scene-visibility.d.ts.map +1 -1
  129. package/dist/lib/scene-visibility.js +22 -1
  130. package/dist/store/use-viewer.d.ts +2 -2
  131. package/dist/store/use-viewer.d.ts.map +1 -1
  132. package/dist/systems/ceiling/ceiling-system.js +1 -1
  133. package/dist/systems/door/door-animation-system.d.ts.map +1 -1
  134. package/dist/systems/door/door-animation-system.js +6 -9
  135. package/dist/systems/door/door-system.d.ts.map +1 -1
  136. package/dist/systems/door/door-system.js +13 -2
  137. package/dist/systems/geometry/geometry-system.d.ts.map +1 -1
  138. package/dist/systems/geometry/geometry-system.js +3 -2
  139. package/dist/systems/level/level-system.d.ts.map +1 -1
  140. package/dist/systems/level/level-system.js +1 -1
  141. package/dist/systems/level/level-utils.d.ts +9 -0
  142. package/dist/systems/level/level-utils.d.ts.map +1 -1
  143. package/dist/systems/level/level-utils.js +14 -0
  144. package/dist/systems/perf-action-settle/perf-action-settle-system.d.ts +8 -0
  145. package/dist/systems/perf-action-settle/perf-action-settle-system.d.ts.map +1 -0
  146. package/dist/systems/perf-action-settle/perf-action-settle-system.js +32 -0
  147. package/dist/systems/roof/roof-system.d.ts.map +1 -1
  148. package/dist/systems/roof/roof-system.js +1196 -58
  149. package/dist/systems/scan/scan-system.js +6 -5
  150. package/dist/systems/slab/slab-system.d.ts.map +1 -1
  151. package/dist/systems/slab/slab-system.js +1 -2
  152. package/dist/systems/stair/stair-system.d.ts.map +1 -1
  153. package/dist/systems/stair/stair-system.js +9 -8
  154. package/dist/systems/surface-hole-geometry.d.ts +1 -1
  155. package/dist/systems/surface-hole-geometry.d.ts.map +1 -1
  156. package/dist/systems/surface-hole-geometry.js +1 -1
  157. package/dist/systems/wall/level-miter-cache.d.ts.map +1 -1
  158. package/dist/systems/wall/wall-build-lifecycle.d.ts +9 -0
  159. package/dist/systems/wall/wall-build-lifecycle.d.ts.map +1 -0
  160. package/dist/systems/wall/wall-build-lifecycle.js +74 -0
  161. package/dist/systems/wall/wall-cutout-cache.d.ts +57 -0
  162. package/dist/systems/wall/wall-cutout-cache.d.ts.map +1 -0
  163. package/dist/systems/wall/wall-cutout-cache.js +304 -0
  164. package/dist/systems/wall/wall-cutout.d.ts +12 -1
  165. package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
  166. package/dist/systems/wall/wall-cutout.js +24 -161
  167. package/dist/systems/wall/wall-material-variant.d.ts +31 -0
  168. package/dist/systems/wall/wall-material-variant.d.ts.map +1 -0
  169. package/dist/systems/wall/wall-material-variant.js +10 -0
  170. package/dist/systems/wall/wall-materials.d.ts +6 -0
  171. package/dist/systems/wall/wall-materials.d.ts.map +1 -1
  172. package/dist/systems/wall/wall-materials.js +29 -13
  173. package/dist/systems/wall/wall-placeholder-sweep.d.ts +50 -0
  174. package/dist/systems/wall/wall-placeholder-sweep.d.ts.map +1 -0
  175. package/dist/systems/wall/wall-placeholder-sweep.js +51 -0
  176. package/dist/systems/wall/wall-rebuild-notifications.d.ts +5 -0
  177. package/dist/systems/wall/wall-rebuild-notifications.d.ts.map +1 -0
  178. package/dist/systems/wall/wall-rebuild-notifications.js +23 -0
  179. package/dist/systems/wall/wall-system.d.ts +11 -4
  180. package/dist/systems/wall/wall-system.d.ts.map +1 -1
  181. package/dist/systems/wall/wall-system.js +405 -153
  182. package/dist/systems/window/window-animation-system.d.ts.map +1 -1
  183. package/dist/systems/window/window-animation-system.js +9 -8
  184. package/dist/systems/window/window-system.d.ts +1 -0
  185. package/dist/systems/window/window-system.d.ts.map +1 -1
  186. package/dist/systems/window/window-system.js +16 -2
  187. package/package.json +17 -4
  188. package/dist/lib/polygon-union.d.ts +0 -4
  189. package/dist/lib/polygon-union.d.ts.map +0 -1
  190. package/dist/lib/polygon-union.js +0 -289
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Pascal Group Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -120,6 +120,61 @@ floor-plan pan/zoom/rotation, and the compass synchronize through transient subs
120
120
  navigation does not require a React render per frame. Set `showCompass={false}` or
121
121
  `showSwitcher={false}` when the host supplies its own controls.
122
122
 
123
+ ## Capture Sessions
124
+
125
+ `@pascal-app/viewer/capture` holds the optional capture runtime and its reference layers. Mount
126
+ `CaptureRuntime` as a child of `Viewer` and provide a source resolver. The host owns access control
127
+ and transport; the runtime owns source lifecycle, scan-node placement, layer visibility, and
128
+ reference renderers for RoomPlan models, device trajectories, and PLY/live point clouds. The
129
+ session contracts it consumes live in `@pascal-app/core/capture`.
130
+
131
+ ```tsx
132
+ import { createHttpCaptureSource } from '@pascal-app/core/capture'
133
+ import { Viewer } from '@pascal-app/viewer'
134
+ import { CaptureRuntime } from '@pascal-app/viewer/capture'
135
+
136
+ function CaptureViewer() {
137
+ return (
138
+ <Viewer>
139
+ <CaptureRuntime
140
+ onError={(error, context) => reportCaptureError(error, context)}
141
+ resolveSource={(locator) => createHttpCaptureSource(locator, { credentials: 'include' })}
142
+ retryKey={retryVersion}
143
+ />
144
+ </Viewer>
145
+ )
146
+ }
147
+ ```
148
+
149
+ Unknown streams remain in the descriptor and can be rendered by passing a custom renderer keyed by
150
+ stream role or kind. A live transport implements `CaptureSource.subscribe()`; no particular
151
+ WebSocket, WebRTC, or collaboration backend is required.
152
+
153
+ `CaptureRuntime` keeps telemetry host-neutral: pass `onError` to report source or per-stream
154
+ failures in the host, then increment `retryKey` to reload every affected session. Direct
155
+ `useCaptureSource()` consumers can call its `retry()` function instead.
156
+
157
+ Hosts can pass `defaultLayerVisibility` to keep expensive optional layers disabled until a user
158
+ enables them. Persisted values in the scan node's `layers` map always override those host defaults;
159
+ without host defaults, every available layer remains visible for backwards compatibility. Hidden
160
+ sessions and layers are unmounted rather than only made visually transparent, so they stop
161
+ raycasting, artifact work, animation, and live packet subscriptions while disabled.
162
+
163
+ ### Local surface previews
164
+
165
+ `@pascal-app/viewer/capture/preview` exports `createSurfaceMeshGeometry` and `createClayMatcap`
166
+ without importing the React viewer runtime, so a capture client can render a locally saved surface
167
+ immediately, before its archive is uploaded. The geometry decoder uses the shared
168
+ `@pascal-app/core/capture` validator, including the native 20,000-face budget, byte lengths, and
169
+ index bounds. It returns `null` for invalid input. The host owns the returned geometry and matcap
170
+ texture and must dispose them on teardown.
171
+
172
+ Direct `CaptureStreamLayer` consumers can pass
173
+ `meshPresentation={{ previewMaterial: 'clay', dollhouse: true }}`. Clay replaces preliminary vertex
174
+ colors; dollhouse enables front-face rendering for surface previews and room models, revealing
175
+ inward-facing room surfaces from outside. It changes per-instance materials, not geometry or
176
+ loader-cached materials. Omitting these options preserves the existing presentation.
177
+
123
178
  ## Viewer State
124
179
 
125
180
  ```typescript
@@ -0,0 +1,2 @@
1
+ export declare function rewriteLoopbackAssetUrl(value: string): string;
2
+ //# sourceMappingURL=asset-url.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asset-url.d.ts","sourceRoot":"","sources":["../../src/capture/asset-url.ts"],"names":[],"mappings":"AAAA,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAa7D"}
@@ -0,0 +1,13 @@
1
+ export function rewriteLoopbackAssetUrl(value) {
2
+ try {
3
+ const url = new URL(value);
4
+ if (typeof window !== 'undefined' &&
5
+ (url.hostname === '127.0.0.1' || url.hostname === 'localhost')) {
6
+ url.hostname = window.location.hostname;
7
+ }
8
+ return url.toString();
9
+ }
10
+ catch {
11
+ return value;
12
+ }
13
+ }
@@ -0,0 +1,43 @@
1
+ import { type ScanNode } from '@pascal-app/core';
2
+ import { type CaptureSessionDescriptor, type CaptureSource, type CaptureSourceResolver, type CaptureStreamDescriptor, type CaptureStreamPacket } from '@pascal-app/core/capture';
3
+ import { type ComponentType } from 'react';
4
+ export type CaptureMeshPresentation = {
5
+ dollhouse?: boolean;
6
+ previewMaterial?: 'clay' | 'recorded';
7
+ };
8
+ export type CaptureStreamRendererProps = {
9
+ artifactUrl: string | null;
10
+ descriptor: CaptureSessionDescriptor;
11
+ meshPresentation?: CaptureMeshPresentation;
12
+ packets: readonly CaptureStreamPacket[];
13
+ scan: ScanNode;
14
+ source: CaptureSource;
15
+ stream: CaptureStreamDescriptor;
16
+ streamEpoch: string;
17
+ };
18
+ export type CaptureStreamRenderer = ComponentType<CaptureStreamRendererProps>;
19
+ export type CaptureRuntimeErrorContext = {
20
+ phase: 'source';
21
+ scanId: ScanNode['id'];
22
+ sessionId: string;
23
+ } | {
24
+ layerKey: string;
25
+ phase: 'stream';
26
+ scanId: ScanNode['id'];
27
+ sessionId: string;
28
+ streamId: string;
29
+ streamKind: string;
30
+ };
31
+ export type CaptureRuntimeProps = {
32
+ defaultLayerVisibility?: Readonly<Record<string, boolean>>;
33
+ maxPacketsPerStream?: number;
34
+ onError?: (error: Error, context: CaptureRuntimeErrorContext) => void;
35
+ renderers?: Readonly<Record<string, CaptureStreamRenderer>>;
36
+ resolveSource: CaptureSourceResolver;
37
+ retryKey?: number | string;
38
+ };
39
+ export declare function CaptureRuntime({ defaultLayerVisibility, maxPacketsPerStream, onError, renderers, resolveSource, retryKey, }: CaptureRuntimeProps): import("react").JSX.Element;
40
+ export declare function CaptureStreamLayer({ descriptor, meshPresentation, packets, renderers, scan, source, stream, streamEpoch, }: Omit<CaptureStreamRendererProps, 'artifactUrl'> & {
41
+ renderers: Readonly<Record<string, CaptureStreamRenderer>>;
42
+ }): import("react").JSX.Element | null;
43
+ //# sourceMappingURL=capture-runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capture-runtime.d.ts","sourceRoot":"","sources":["../../src/capture/capture-runtime.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,QAAQ,EAA2B,MAAM,kBAAkB,CAAA;AACzE,OAAO,EAIL,KAAK,wBAAwB,EAE7B,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,KAAK,uBAAuB,EAC5B,KAAK,mBAAmB,EAGzB,MAAM,0BAA0B,CAAA;AAEjC,OAAO,EACL,KAAK,aAAa,EAQnB,MAAM,OAAO,CAAA;AAsBd,MAAM,MAAM,uBAAuB,GAAG;IACpC,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,eAAe,CAAC,EAAE,MAAM,GAAG,UAAU,CAAA;CACtC,CAAA;AAED,MAAM,MAAM,0BAA0B,GAAG;IACvC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,UAAU,EAAE,wBAAwB,CAAA;IACpC,gBAAgB,CAAC,EAAE,uBAAuB,CAAA;IAC1C,OAAO,EAAE,SAAS,mBAAmB,EAAE,CAAA;IACvC,IAAI,EAAE,QAAQ,CAAA;IACd,MAAM,EAAE,aAAa,CAAA;IACrB,MAAM,EAAE,uBAAuB,CAAA;IAC/B,WAAW,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG,aAAa,CAAC,0BAA0B,CAAC,CAAA;AAE7E,MAAM,MAAM,0BAA0B,GAClC;IACE,KAAK,EAAE,QAAQ,CAAA;IACf,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAA;IACtB,SAAS,EAAE,MAAM,CAAA;CAClB,GACD;IACE,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,QAAQ,CAAA;IACf,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAA;IACtB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;CACnB,CAAA;AAEL,MAAM,MAAM,mBAAmB,GAAG;IAChC,sBAAsB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;IAC1D,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,0BAA0B,KAAK,IAAI,CAAA;IACrE,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC,CAAA;IAC3D,aAAa,EAAE,qBAAqB,CAAA;IACpC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CAC3B,CAAA;AAMD,wBAAgB,cAAc,CAAC,EAC7B,sBAA+C,EAC/C,mBAAwB,EACxB,OAAO,EACP,SAA2B,EAC3B,aAAa,EACb,QAAY,GACb,EAAE,mBAAmB,+BA6BrB;AAoHD,wBAAgB,kBAAkB,CAAC,EACjC,UAAU,EACV,gBAAgB,EAChB,OAAO,EACP,SAAS,EACT,IAAI,EACJ,MAAM,EACN,MAAM,EACN,WAAW,GACZ,EAAE,IAAI,CAAC,0BAA0B,EAAE,aAAa,CAAC,GAAG;IACnD,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC,CAAA;CAC3D,sCAqGA"}
@@ -0,0 +1,228 @@
1
+ 'use client';
2
+ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { sceneRegistry, useScene } from '@pascal-app/core';
4
+ import { CaptureArtifactReferenceSchema, captureLayerKey, DeviceMotionTrajectorySchema, } from '@pascal-app/core/capture';
5
+ import { createPortal, useFrame } from '@react-three/fiber';
6
+ import { Suspense, useCallback, useEffect, useMemo, useRef, useState, } from 'react';
7
+ import { ErrorBoundary } from '../components/error-boundary';
8
+ import { useNodeEvents } from '../hooks/use-node-events';
9
+ import useViewer from '../store/use-viewer';
10
+ import { rewriteLoopbackAssetUrl } from './asset-url';
11
+ import { resolveCaptureFrameMatrix } from './frame';
12
+ import { isCaptureSessionVisible, isCaptureStreamVisible } from './layer-visibility';
13
+ import { CaptureDeviceMotionLayer } from './layers/device-motion-layer';
14
+ import { CapturePointCloudLayer } from './layers/point-cloud-layer';
15
+ import { CaptureRoomModel } from './layers/room-model-layer';
16
+ import { CaptureSurfaceMeshLayer } from './layers/surface-mesh-layer';
17
+ import { useCaptureSource } from './source-state';
18
+ import { captureModelFormat, isCaptureModelArtifact, isCapturePointCloudArtifact, isCaptureStreamRenderable, streamHydratesJsonPayload, } from './stream-rendering';
19
+ import { parseDeviceTrajectoryPackets, parseDeviceTrajectoryPayload } from './trajectory';
20
+ const EMPTY_RENDERERS = {};
21
+ const EMPTY_LAYER_VISIBILITY = {};
22
+ export function CaptureRuntime({ defaultLayerVisibility = EMPTY_LAYER_VISIBILITY, maxPacketsPerStream = 32, onError, renderers = EMPTY_RENDERERS, resolveSource, retryKey = 0, }) {
23
+ const nodes = useScene((state) => state.nodes);
24
+ const showScans = useViewer((state) => state.showScans);
25
+ const scans = useMemo(() => Object.values(nodes).filter((node) => node.type === 'scan' &&
26
+ node.captureSession !== null &&
27
+ isCaptureSessionVisible(showScans, node.visible)), [nodes, showScans]);
28
+ return (_jsx(_Fragment, { children: scans.map((scan) => (_jsx(CaptureSessionPortal, { defaultLayerVisibility: defaultLayerVisibility, maxPacketsPerStream: maxPacketsPerStream, onError: onError, renderers: renderers, resolveSource: resolveSource, scan: scan }, `${scan.id}:${retryKey}`))) }));
29
+ }
30
+ function CaptureSessionPortal({ defaultLayerVisibility, maxPacketsPerStream, onError, renderers, resolveSource, scan, }) {
31
+ const [target, setTarget] = useState(null);
32
+ const onErrorRef = useRef(onError);
33
+ const handlers = useNodeEvents(scan, 'scan');
34
+ const customRendererKeys = useMemo(() => new Set(Object.keys(renderers)), [renderers]);
35
+ const streamFilter = useCallback((stream) => isCaptureStreamVisible(stream, scan.layers, defaultLayerVisibility) &&
36
+ isCaptureStreamRenderable(stream, customRendererKeys), [customRendererKeys, defaultLayerVisibility, scan.layers]);
37
+ const sourceState = useCaptureSource(scan.captureSession, resolveSource, {
38
+ maxPacketsPerStream,
39
+ streamFilter,
40
+ });
41
+ useEffect(() => {
42
+ onErrorRef.current = onError;
43
+ }, [onError]);
44
+ useEffect(() => {
45
+ if (!sourceState.error)
46
+ return;
47
+ onErrorRef.current?.(sourceState.error, {
48
+ phase: 'source',
49
+ scanId: scan.id,
50
+ sessionId: scan.captureSession.sessionId,
51
+ });
52
+ }, [scan.captureSession.sessionId, scan.id, sourceState.error]);
53
+ useFrame(() => {
54
+ const nextTarget = sceneRegistry.nodes.get(scan.id) ?? null;
55
+ if (nextTarget !== target)
56
+ setTarget(nextTarget);
57
+ });
58
+ const descriptor = sourceState.descriptor;
59
+ const source = sourceState.source;
60
+ if (!(target && descriptor && source))
61
+ return null;
62
+ const visibleStreams = descriptor.streams.filter(streamFilter);
63
+ return createPortal(_jsx("group", { ...handlers, children: visibleStreams.map((stream) => {
64
+ const layerKey = captureLayerKey(stream);
65
+ const renderKey = captureStreamRenderKey(stream);
66
+ const packets = sourceState.packets[stream.id] ?? [];
67
+ const streamEpoch = sourceState.streamEpochs[stream.id] ??
68
+ `descriptor:${descriptor.revisionId ?? ''}:${stream.id}:${stream.frameId ?? ''}`;
69
+ const latestPacket = packets.at(-1);
70
+ const packetRevision = latestPacket
71
+ ? `${latestPacket.generation}:${latestPacket.sequence}:${latestPacket.frameId ?? ''}`
72
+ : 'static';
73
+ return (_jsx(ErrorBoundary, { fallback: _jsx("group", {}), onError: (error) => onErrorRef.current?.(error, {
74
+ layerKey,
75
+ phase: 'stream',
76
+ scanId: scan.id,
77
+ sessionId: scan.captureSession.sessionId,
78
+ streamId: stream.id,
79
+ streamKind: stream.kind,
80
+ }), resetKey: `${renderKey}:${sourceState.descriptorVersion}:${streamEpoch}:${packetRevision}`, scope: `capture:${layerKey}`, children: _jsx(Suspense, { fallback: null, children: _jsx(CaptureStreamLayer, { descriptor: descriptor, packets: packets, renderers: renderers, scan: scan, source: source, stream: stream, streamEpoch: streamEpoch }) }) }, renderKey));
81
+ }) }), target);
82
+ }
83
+ function captureStreamRenderKey(stream) {
84
+ const artifact = stream.artifact;
85
+ return [
86
+ stream.id,
87
+ stream.availability,
88
+ artifact?.id ?? '',
89
+ artifact?.sha256 ?? '',
90
+ artifact?.uri ?? '',
91
+ ].join(':');
92
+ }
93
+ export function CaptureStreamLayer({ descriptor, meshPresentation, packets, renderers, scan, source, stream, streamEpoch, }) {
94
+ const artifactUrl = useResolvedArtifact(source, stream.artifact);
95
+ const layerKey = captureLayerKey(stream);
96
+ const Renderer = renderers[layerKey] ?? renderers[stream.kind];
97
+ // Extracted previews archive the inline payload shape as a JSON artifact.
98
+ const payloadArtifactUrl = streamHydratesJsonPayload(stream) ? artifactUrl : null;
99
+ const fetchedPayload = useJsonArtifactPayload(payloadArtifactUrl);
100
+ const payload = stream.inline ?? fetchedPayload;
101
+ const frameId = packets.at(-1)?.frameId ?? stream.frameId ?? stream.artifact?.frameId;
102
+ const frameMatrix = useMemo(() => resolveCaptureFrameMatrix(descriptor, frameId), [descriptor, frameId]);
103
+ const trajectory = useMemo(() => {
104
+ if (layerKey !== 'deviceMotion')
105
+ return null;
106
+ const inline = DeviceMotionTrajectorySchema.safeParse(payload);
107
+ return inline.success
108
+ ? parseDeviceTrajectoryPayload(inline.data)
109
+ : parseDeviceTrajectoryPackets(packets.map((packet) => packet.payload));
110
+ }, [layerKey, packets, payload]);
111
+ const motionPlaybackKey = useMemo(() => {
112
+ if (layerKey !== 'deviceMotion')
113
+ return '';
114
+ // Fetched payloads can be megabytes — key them by artifact identity and
115
+ // load state instead of stringifying their content.
116
+ const inlineVersion = packets.length === 0
117
+ ? stream.inline != null
118
+ ? JSON.stringify(stream.inline)
119
+ : `${payloadArtifactUrl ?? ''}:${fetchedPayload ? 'loaded' : 'pending'}`
120
+ : '';
121
+ return [descriptor.revisionId ?? '', streamEpoch, inlineVersion].join(':');
122
+ }, [
123
+ descriptor.revisionId,
124
+ fetchedPayload,
125
+ layerKey,
126
+ packets.length,
127
+ payloadArtifactUrl,
128
+ stream.inline,
129
+ streamEpoch,
130
+ ]);
131
+ if (frameId && !frameMatrix) {
132
+ throw new Error(`Capture stream ${stream.id} references an invalid frame: ${frameId}.`);
133
+ }
134
+ let content = null;
135
+ if (Renderer) {
136
+ content = (_jsx(Renderer, { artifactUrl: artifactUrl, descriptor: descriptor, meshPresentation: meshPresentation, packets: packets, scan: scan, source: source, stream: stream, streamEpoch: streamEpoch }));
137
+ }
138
+ else if (layerKey === 'model' &&
139
+ isCaptureModelArtifact(stream.artifact) &&
140
+ stream.artifact &&
141
+ artifactUrl) {
142
+ content = (_jsx(CaptureRoomModel, { dollhouse: meshPresentation?.dollhouse, format: captureModelFormat(stream.artifact) ?? undefined, mediaType: stream.artifact.mediaType, opacity: scan.opacity, url: artifactUrl }));
143
+ }
144
+ else if (layerKey === 'deviceMotion') {
145
+ content = trajectory ? (_jsx(CaptureDeviceMotionLayer, { trajectory: trajectory }, motionPlaybackKey)) : null;
146
+ }
147
+ else if (layerKey === 'pointCloud') {
148
+ content = (_jsx(CapturePointCloudLayer, { artifactUrl: isCapturePointCloudArtifact(stream.artifact) ? (artifactUrl ?? undefined) : undefined, inline: payload, packets: stream.availability === 'live' ? packets : [] }));
149
+ }
150
+ else if (layerKey === 'surfaceMesh') {
151
+ content = (_jsx(CaptureSurfaceMeshLayer, { appearance: meshPresentation?.previewMaterial, dollhouse: meshPresentation?.dollhouse, inline: payload }));
152
+ }
153
+ if (!(content && frameMatrix))
154
+ return content;
155
+ return (_jsx("group", { matrix: frameMatrix, matrixAutoUpdate: false, children: content }));
156
+ }
157
+ function useJsonArtifactPayload(url) {
158
+ const [error, setError] = useState(null);
159
+ const [payload, setPayload] = useState(null);
160
+ useEffect(() => {
161
+ setError(null);
162
+ setPayload(null);
163
+ if (!url)
164
+ return;
165
+ const abort = new AbortController();
166
+ void fetch(rewriteLoopbackAssetUrl(url), { signal: abort.signal })
167
+ .then(async (response) => {
168
+ if (!response.ok)
169
+ throw new Error(`Could not load ${url}: ${response.status}`);
170
+ return (await response.json());
171
+ })
172
+ .then((data) => {
173
+ if (!abort.signal.aborted)
174
+ setPayload(data);
175
+ })
176
+ .catch((cause) => {
177
+ if (abort.signal.aborted)
178
+ return;
179
+ setError(cause instanceof Error ? cause : new Error(`Could not load ${url}.`));
180
+ });
181
+ return () => abort.abort();
182
+ }, [url]);
183
+ if (error)
184
+ throw error;
185
+ return payload;
186
+ }
187
+ function useResolvedArtifact(source, artifact) {
188
+ const [error, setError] = useState(null);
189
+ const [url, setUrl] = useState(null);
190
+ const artifactKey = artifact ? JSON.stringify(artifact) : null;
191
+ const artifactSnapshot = useMemo(() => artifactKey
192
+ ? CaptureArtifactReferenceSchema.parse(JSON.parse(artifactKey))
193
+ : undefined, [artifactKey]);
194
+ useEffect(() => {
195
+ const abort = new AbortController();
196
+ let dispose;
197
+ setError(null);
198
+ setUrl(null);
199
+ if (!artifactSnapshot)
200
+ return () => abort.abort();
201
+ const resolve = source.resolveArtifact
202
+ ? source.resolveArtifact(artifactSnapshot, abort.signal)
203
+ : artifactSnapshot.uri
204
+ ? Promise.resolve({ url: artifactSnapshot.uri })
205
+ : Promise.reject(new Error(`Capture artifact ${artifactSnapshot.id} has no URI.`));
206
+ void resolve
207
+ .then((result) => {
208
+ if (abort.signal.aborted) {
209
+ result.dispose?.();
210
+ return;
211
+ }
212
+ dispose = result.dispose;
213
+ setUrl(result.url);
214
+ })
215
+ .catch((cause) => {
216
+ if (!abort.signal.aborted) {
217
+ setError(cause instanceof Error ? cause : new Error('Could not resolve capture artifact.'));
218
+ }
219
+ });
220
+ return () => {
221
+ abort.abort();
222
+ dispose?.();
223
+ };
224
+ }, [artifactSnapshot, source]);
225
+ if (error)
226
+ throw error;
227
+ return url;
228
+ }
@@ -0,0 +1,4 @@
1
+ import type { CaptureSessionDescriptor } from '@pascal-app/core/capture';
2
+ import { Matrix4 } from 'three';
3
+ export declare function resolveCaptureFrameMatrix(descriptor: CaptureSessionDescriptor, frameId: string | undefined): Matrix4 | null;
4
+ //# sourceMappingURL=frame.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frame.d.ts","sourceRoot":"","sources":["../../src/capture/frame.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAA;AACxE,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAA;AAE/B,wBAAgB,yBAAyB,CACvC,UAAU,EAAE,wBAAwB,EACpC,OAAO,EAAE,MAAM,GAAG,SAAS,GAC1B,OAAO,GAAG,IAAI,CAiBhB"}
@@ -0,0 +1,21 @@
1
+ import { Matrix4 } from 'three';
2
+ export function resolveCaptureFrameMatrix(descriptor, frameId) {
3
+ if (!frameId)
4
+ return null;
5
+ const frames = new Map(descriptor.coordinateFrames.map((frame) => [frame.id, frame]));
6
+ const visited = new Set();
7
+ const matrix = new Matrix4();
8
+ let currentId = frameId;
9
+ while (currentId) {
10
+ if (visited.has(currentId))
11
+ return null;
12
+ visited.add(currentId);
13
+ const frame = frames.get(currentId);
14
+ if (!frame)
15
+ return null;
16
+ if (frame.transform)
17
+ matrix.premultiply(new Matrix4().fromArray(frame.transform));
18
+ currentId = frame.parentId;
19
+ }
20
+ return matrix;
21
+ }
@@ -0,0 +1,12 @@
1
+ export { rewriteLoopbackAssetUrl } from './asset-url';
2
+ export { type CaptureMeshPresentation, CaptureRuntime, type CaptureRuntimeErrorContext, type CaptureRuntimeProps, CaptureStreamLayer, type CaptureStreamRenderer, type CaptureStreamRendererProps, } from './capture-runtime';
3
+ export { resolveCaptureFrameMatrix } from './frame';
4
+ export { isCaptureLayerVisible, isCaptureSessionVisible, isCaptureStreamVisible, } from './layer-visibility';
5
+ export { CaptureDeviceMotionLayer, DEVICE_MOTION_PLAYBACK_SPEED, } from './layers/device-motion-layer';
6
+ export { buildPointCloudData, CapturePointCloudLayer, type PointCloudData, } from './layers/point-cloud-layer';
7
+ export { CaptureRoomModel } from './layers/room-model-layer';
8
+ export { buildSurfaceMeshData, CaptureSurfaceMeshLayer, type SurfaceMeshData, } from './layers/surface-mesh-layer';
9
+ export { appendCapturePacket, type CaptureSourceState, captureSubscriptionStreamIds, type UseCaptureSourceOptions, useCaptureSource, } from './source-state';
10
+ export { type CaptureModelFormat, captureModelFormat, isCaptureModelArtifact, isCapturePointCloudArtifact, isCaptureStreamRenderable, } from './stream-rendering';
11
+ export { type DeviceTrajectory, type DeviceTrajectoryFrame, type DeviceTrajectoryPose, parseDeviceTrajectoryPackets, parseDeviceTrajectoryPayload, sampleDeviceTrajectory, } from './trajectory';
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/capture/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AACrD,OAAO,EACL,KAAK,uBAAuB,EAC5B,cAAc,EACd,KAAK,0BAA0B,EAC/B,KAAK,mBAAmB,EACxB,kBAAkB,EAClB,KAAK,qBAAqB,EAC1B,KAAK,0BAA0B,GAChC,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,yBAAyB,EAAE,MAAM,SAAS,CAAA;AACnD,OAAO,EACL,qBAAqB,EACrB,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACL,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,8BAA8B,CAAA;AACrC,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,KAAK,cAAc,GACpB,MAAM,4BAA4B,CAAA;AACnC,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAA;AAC5D,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,KAAK,eAAe,GACrB,MAAM,6BAA6B,CAAA;AACpC,OAAO,EACL,mBAAmB,EACnB,KAAK,kBAAkB,EACvB,4BAA4B,EAC5B,KAAK,uBAAuB,EAC5B,gBAAgB,GACjB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EACL,KAAK,kBAAkB,EACvB,kBAAkB,EAClB,sBAAsB,EACtB,2BAA2B,EAC3B,yBAAyB,GAC1B,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,4BAA4B,EAC5B,4BAA4B,EAC5B,sBAAsB,GACvB,MAAM,cAAc,CAAA"}
@@ -0,0 +1,11 @@
1
+ export { rewriteLoopbackAssetUrl } from './asset-url';
2
+ export { CaptureRuntime, CaptureStreamLayer, } from './capture-runtime';
3
+ export { resolveCaptureFrameMatrix } from './frame';
4
+ export { isCaptureLayerVisible, isCaptureSessionVisible, isCaptureStreamVisible, } from './layer-visibility';
5
+ export { CaptureDeviceMotionLayer, DEVICE_MOTION_PLAYBACK_SPEED, } from './layers/device-motion-layer';
6
+ export { buildPointCloudData, CapturePointCloudLayer, } from './layers/point-cloud-layer';
7
+ export { CaptureRoomModel } from './layers/room-model-layer';
8
+ export { buildSurfaceMeshData, CaptureSurfaceMeshLayer, } from './layers/surface-mesh-layer';
9
+ export { appendCapturePacket, captureSubscriptionStreamIds, useCaptureSource, } from './source-state';
10
+ export { captureModelFormat, isCaptureModelArtifact, isCapturePointCloudArtifact, isCaptureStreamRenderable, } from './stream-rendering';
11
+ export { parseDeviceTrajectoryPackets, parseDeviceTrajectoryPayload, sampleDeviceTrajectory, } from './trajectory';
@@ -0,0 +1,5 @@
1
+ import type { CaptureStreamDescriptor } from '@pascal-app/core/capture';
2
+ export declare function isCaptureLayerVisible(layers: Readonly<Record<string, boolean>>, layerKey: string, defaultLayerVisibility?: Readonly<Record<string, boolean>>): boolean;
3
+ export declare function isCaptureStreamVisible(stream: CaptureStreamDescriptor, layers: Readonly<Record<string, boolean>>, defaultLayerVisibility?: Readonly<Record<string, boolean>>): boolean;
4
+ export declare function isCaptureSessionVisible(showScans: boolean, scanVisible: boolean): boolean;
5
+ //# sourceMappingURL=layer-visibility.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layer-visibility.d.ts","sourceRoot":"","sources":["../../src/capture/layer-visibility.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAA;AAKvE,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACzC,QAAQ,EAAE,MAAM,EAChB,sBAAsB,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAA0B,GACjF,OAAO,CAET;AAED,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,uBAAuB,EAC/B,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACzC,sBAAsB,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAA0B,GACjF,OAAO,CAET;AAED,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,GAAG,OAAO,CAEzF"}
@@ -0,0 +1,11 @@
1
+ import { captureLayerKey } from '@pascal-app/core/capture';
2
+ const EMPTY_LAYER_VISIBILITY = {};
3
+ export function isCaptureLayerVisible(layers, layerKey, defaultLayerVisibility = EMPTY_LAYER_VISIBILITY) {
4
+ return layers[layerKey] ?? defaultLayerVisibility[layerKey] ?? true;
5
+ }
6
+ export function isCaptureStreamVisible(stream, layers, defaultLayerVisibility = EMPTY_LAYER_VISIBILITY) {
7
+ return isCaptureLayerVisible(layers, captureLayerKey(stream), defaultLayerVisibility);
8
+ }
9
+ export function isCaptureSessionVisible(showScans, scanVisible) {
10
+ return showScans && scanVisible;
11
+ }
@@ -0,0 +1,3 @@
1
+ import { DataTexture } from 'three';
2
+ export declare function createClayMatcap(): DataTexture;
3
+ //# sourceMappingURL=clay-matcap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clay-matcap.d.ts","sourceRoot":"","sources":["../../../src/capture/layers/clay-matcap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAA4C,MAAM,OAAO,CAAA;AAE7E,wBAAgB,gBAAgB,IAAI,WAAW,CA0B9C"}
@@ -0,0 +1,26 @@
1
+ import { DataTexture, LinearFilter, RGBAFormat, SRGBColorSpace } from 'three';
2
+ export function createClayMatcap() {
3
+ const size = 128;
4
+ const pixels = new Uint8Array(size * size * 4);
5
+ for (let row = 0; row < size; row += 1) {
6
+ for (let column = 0; column < size; column += 1) {
7
+ const x = (column / (size - 1)) * 2 - 1;
8
+ const y = (row / (size - 1)) * 2 - 1;
9
+ const z = Math.sqrt(Math.max(0, 1 - x * x - y * y));
10
+ const gloss = Math.exp(-((x + 0.34) ** 2 / 0.045 + (y - 0.42) ** 2 / 0.075));
11
+ const rim = Math.exp(-((x - 0.65) ** 2 / 0.025 + (y + 0.15) ** 2 / 0.5)) * 0.3;
12
+ const base = [0.3 + (1 - z) * 0.22, 0.35 + (x + 1) * 0.16 + z * 0.12, 0.64 + z * 0.2];
13
+ const offset = (row * size + column) * 4;
14
+ for (let channel = 0; channel < 3; channel += 1) {
15
+ pixels[offset + channel] = Math.round(Math.min(1, base[channel] + gloss * 0.58 + rim) * 255);
16
+ }
17
+ pixels[offset + 3] = 255;
18
+ }
19
+ }
20
+ const texture = new DataTexture(pixels, size, size, RGBAFormat);
21
+ texture.colorSpace = SRGBColorSpace;
22
+ texture.minFilter = LinearFilter;
23
+ texture.magFilter = LinearFilter;
24
+ texture.needsUpdate = true;
25
+ return texture;
26
+ }
@@ -0,0 +1,7 @@
1
+ import { type DeviceTrajectory } from '../trajectory';
2
+ export declare const DEVICE_MOTION_PLAYBACK_SPEED = 3;
3
+ export declare function CaptureDeviceMotionLayer({ lineWidth, trajectory, }: {
4
+ lineWidth?: number;
5
+ trajectory: DeviceTrajectory;
6
+ }): import("react").JSX.Element;
7
+ //# sourceMappingURL=device-motion-layer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device-motion-layer.d.ts","sourceRoot":"","sources":["../../../src/capture/layers/device-motion-layer.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAE,KAAK,gBAAgB,EAA0B,MAAM,eAAe,CAAA;AAE7E,eAAO,MAAM,4BAA4B,IAAI,CAAA;AAE7C,wBAAgB,wBAAwB,CAAC,EACvC,SAAe,EACf,UAAU,GACX,EAAE;IACD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,gBAAgB,CAAA;CAC7B,+BAiDA"}
@@ -0,0 +1,78 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useFrame } from '@react-three/fiber';
4
+ import { useEffect, useMemo, useRef } from 'react';
5
+ import { BufferGeometry, LineBasicMaterial, LineSegments, Quaternion, Line as ThreeLine, Vector3, } from 'three';
6
+ import { sampleDeviceTrajectory } from '../trajectory';
7
+ export const DEVICE_MOTION_PLAYBACK_SPEED = 3;
8
+ export function CaptureDeviceMotionLayer({ lineWidth = 2.5, trajectory, }) {
9
+ const deviceRef = useRef(null);
10
+ const elapsedRef = useRef(0);
11
+ const position = useMemo(() => new Vector3(), []);
12
+ const toPosition = useMemo(() => new Vector3(), []);
13
+ const fromQuaternion = useMemo(() => new Quaternion(), []);
14
+ const toQuaternion = useMemo(() => new Quaternion(), []);
15
+ const trajectorySegments = useMemo(() => {
16
+ const segments = new Map();
17
+ for (const pose of trajectory.poses) {
18
+ const points = segments.get(pose.segment) ?? [];
19
+ points.push(pose.position);
20
+ segments.set(pose.segment, points);
21
+ }
22
+ return [...segments.entries()]
23
+ .filter(([, points]) => points.length > 1)
24
+ .map(([segment, points]) => ({ points, segment }));
25
+ }, [trajectory]);
26
+ useFrame((_, delta) => {
27
+ if (!deviceRef.current)
28
+ return;
29
+ elapsedRef.current += delta * DEVICE_MOTION_PLAYBACK_SPEED;
30
+ const frame = sampleDeviceTrajectory(trajectory, elapsedRef.current);
31
+ position
32
+ .fromArray(frame.from.position)
33
+ .lerp(toPosition.fromArray(frame.to.position), frame.alpha);
34
+ fromQuaternion.fromArray(frame.from.quaternion);
35
+ toQuaternion.fromArray(frame.to.quaternion);
36
+ deviceRef.current.position.copy(position);
37
+ deviceRef.current.quaternion.slerpQuaternions(fromQuaternion, toQuaternion, frame.alpha);
38
+ });
39
+ return (_jsxs("group", { children: [trajectorySegments.map(({ points, segment }) => (_jsx(CaptureLine, { color: "#222326", lineWidth: lineWidth, opacity: 0.78, points: points }, segment))), _jsx("group", { ref: deviceRef, children: _jsx(CameraFrustum, { lineWidth: lineWidth }) })] }));
40
+ }
41
+ function CameraFrustum({ lineWidth }) {
42
+ const apex = [0, 0, 0];
43
+ const topRight = [0.14, 0.1, -0.28];
44
+ const topLeft = [-0.14, 0.1, -0.28];
45
+ const bottomLeft = [-0.14, -0.1, -0.28];
46
+ const bottomRight = [0.14, -0.1, -0.28];
47
+ const corners = [topRight, topLeft, bottomLeft, bottomRight];
48
+ const points = [
49
+ ...corners.map((corner) => [apex, corner]),
50
+ [topRight, topLeft],
51
+ [topLeft, bottomLeft],
52
+ [bottomLeft, bottomRight],
53
+ [bottomRight, topRight],
54
+ ].flat();
55
+ return (_jsxs("group", { children: [_jsx(CaptureLine, { color: "#f5b900", lineWidth: lineWidth, points: points, segments: true }), _jsxs("mesh", { children: [_jsx("sphereGeometry", { args: [0.025, 12, 12] }), _jsx("meshBasicMaterial", { color: "#ffd84d" })] })] }));
56
+ }
57
+ function CaptureLine({ color, lineWidth, opacity = 1, points, segments = false, }) {
58
+ const line = useMemo(() => {
59
+ const geometry = new BufferGeometry().setFromPoints(points.map(([x, y, z]) => new Vector3(x, y, z)));
60
+ const material = new LineBasicMaterial({
61
+ color,
62
+ depthWrite: opacity >= 1,
63
+ linewidth: lineWidth,
64
+ opacity,
65
+ transparent: opacity < 1,
66
+ });
67
+ const object = segments
68
+ ? new LineSegments(geometry, material)
69
+ : new ThreeLine(geometry, material);
70
+ object.frustumCulled = false;
71
+ return object;
72
+ }, [color, lineWidth, opacity, points, segments]);
73
+ useEffect(() => () => {
74
+ line.geometry.dispose();
75
+ line.material.dispose();
76
+ }, [line]);
77
+ return _jsx("primitive", { object: line });
78
+ }
@@ -0,0 +1,15 @@
1
+ import type { CaptureStreamPacket } from '@pascal-app/core/capture';
2
+ export type PointCloudData = {
3
+ colors: Float32Array | null;
4
+ positions: Float32Array;
5
+ };
6
+ export declare function CapturePointCloudLayer({ artifactUrl, inline, maxPoints, packets, pointSize, }: {
7
+ artifactUrl?: string;
8
+ inline?: unknown;
9
+ maxPoints?: number;
10
+ packets?: readonly CaptureStreamPacket[];
11
+ pointSize?: number;
12
+ }): import("react").JSX.Element | null;
13
+ export declare function buildPointCloudData(packets: readonly CaptureStreamPacket[], maxPoints: number): PointCloudData;
14
+ export declare function buildPointCloudPayloadData(value: unknown, maxPoints: number): PointCloudData;
15
+ //# sourceMappingURL=point-cloud-layer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"point-cloud-layer.d.ts","sourceRoot":"","sources":["../../../src/capture/layers/point-cloud-layer.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAOnE,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,YAAY,GAAG,IAAI,CAAA;IAC3B,SAAS,EAAE,YAAY,CAAA;CACxB,CAAA;AAED,wBAAgB,sBAAsB,CAAC,EACrC,WAAW,EACX,MAAM,EACN,SAAmB,EACnB,OAAY,EACZ,SAAiB,GAClB,EAAE;IACD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,SAAS,mBAAmB,EAAE,CAAA;IACxC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,sCAaA;AAyCD,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,SAAS,mBAAmB,EAAE,EACvC,SAAS,EAAE,MAAM,GAChB,cAAc,CA6BhB;AAED,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,cAAc,CAY5F"}