@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
@@ -0,0 +1,106 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useLoader } from '@react-three/fiber';
4
+ import { useEffect, useMemo } from 'react';
5
+ import { BufferGeometry, Float32BufferAttribute } from 'three';
6
+ import { PLYLoader } from 'three/addons/loaders/PLYLoader.js';
7
+ import { rewriteLoopbackAssetUrl } from '../asset-url';
8
+ export function CapturePointCloudLayer({ artifactUrl, inline, maxPoints = 250_000, packets = [], pointSize = 0.012, }) {
9
+ const liveData = useMemo(() => buildPointCloudData(packets, maxPoints), [maxPoints, packets]);
10
+ const inlineData = useMemo(() => buildPointCloudPayloadData(inline, maxPoints), [inline, maxPoints]);
11
+ if (liveData.positions.length > 0) {
12
+ return _jsx(PointCloudDataLayer, { data: liveData, pointSize: pointSize });
13
+ }
14
+ if (inlineData.positions.length > 0) {
15
+ return _jsx(PointCloudDataLayer, { data: inlineData, pointSize: pointSize });
16
+ }
17
+ return artifactUrl ? _jsx(PlyPointCloud, { pointSize: pointSize, url: artifactUrl }) : null;
18
+ }
19
+ function PlyPointCloud({ pointSize, url }) {
20
+ const source = useLoader(PLYLoader, rewriteLoopbackAssetUrl(url));
21
+ const geometry = useMemo(() => source.clone(), [source]);
22
+ useEffect(() => () => geometry.dispose(), [geometry]);
23
+ return (_jsx("points", { frustumCulled: false, geometry: geometry, children: _jsx("pointsMaterial", { color: geometry.getAttribute('color') ? undefined : '#8fb8d8', size: pointSize, sizeAttenuation: true, vertexColors: Boolean(geometry.getAttribute('color')) }) }));
24
+ }
25
+ function PointCloudDataLayer({ data, pointSize }) {
26
+ const geometry = useMemo(() => {
27
+ const next = new BufferGeometry();
28
+ next.setAttribute('position', new Float32BufferAttribute(data.positions, 3));
29
+ if (data.colors)
30
+ next.setAttribute('color', new Float32BufferAttribute(data.colors, 3));
31
+ next.computeBoundingSphere();
32
+ return next;
33
+ }, [data]);
34
+ useEffect(() => () => geometry.dispose(), [geometry]);
35
+ return (_jsx("points", { frustumCulled: false, geometry: geometry, children: _jsx("pointsMaterial", { color: data.colors ? undefined : '#8fb8d8', size: pointSize, sizeAttenuation: true, vertexColors: Boolean(data.colors) }) }));
36
+ }
37
+ export function buildPointCloudData(packets, maxPoints) {
38
+ const chunks = [];
39
+ let pointCount = 0;
40
+ let allHaveColors = true;
41
+ for (let index = packets.length - 1; index >= 0 && pointCount < maxPoints; index -= 1) {
42
+ const parsed = parsePointPayload(packets[index]?.payload);
43
+ if (!parsed)
44
+ continue;
45
+ const availablePoints = Math.floor(parsed.positions.length / 3);
46
+ const keepPoints = Math.min(availablePoints, maxPoints - pointCount);
47
+ if (keepPoints <= 0)
48
+ continue;
49
+ const start = (availablePoints - keepPoints) * 3;
50
+ chunks.unshift({
51
+ colors: parsed.colors?.slice(start) ?? null,
52
+ positions: parsed.positions.slice(start),
53
+ });
54
+ pointCount += keepPoints;
55
+ allHaveColors &&= parsed.colors !== null;
56
+ }
57
+ const positions = new Float32Array(pointCount * 3);
58
+ const colors = allHaveColors && pointCount > 0 ? new Float32Array(pointCount * 3) : null;
59
+ let offset = 0;
60
+ for (const chunk of chunks) {
61
+ positions.set(chunk.positions, offset);
62
+ if (colors && chunk.colors)
63
+ colors.set(normalizeColors(chunk.colors), offset);
64
+ offset += chunk.positions.length;
65
+ }
66
+ return { colors, positions };
67
+ }
68
+ export function buildPointCloudPayloadData(value, maxPoints) {
69
+ const parsed = parsePointPayload(value);
70
+ if (!parsed)
71
+ return { colors: null, positions: new Float32Array() };
72
+ const availablePoints = Math.floor(parsed.positions.length / 3);
73
+ const keepPoints = Math.min(availablePoints, maxPoints);
74
+ const start = (availablePoints - keepPoints) * 3;
75
+ const positions = new Float32Array(parsed.positions.slice(start));
76
+ const colors = parsed.colors
77
+ ? new Float32Array(normalizeColors(parsed.colors.slice(start)))
78
+ : null;
79
+ return { colors, positions };
80
+ }
81
+ function parsePointPayload(value) {
82
+ if (!(value && typeof value === 'object'))
83
+ return null;
84
+ const payload = value;
85
+ const positions = numericArray(payload.positions);
86
+ if (!(positions && positions.length >= 3 && positions.length % 3 === 0))
87
+ return null;
88
+ const colors = numericArray(payload.colors);
89
+ return {
90
+ colors: colors && colors.length === positions.length ? colors : null,
91
+ positions,
92
+ };
93
+ }
94
+ function numericArray(value) {
95
+ if (Array.isArray(value) && value.every((entry) => Number.isFinite(entry)))
96
+ return value;
97
+ if (ArrayBuffer.isView(value)) {
98
+ const entries = Array.from(value);
99
+ return entries.every(Number.isFinite) ? entries : null;
100
+ }
101
+ return null;
102
+ }
103
+ function normalizeColors(colors) {
104
+ const divisor = colors.some((value) => value > 1) ? 255 : 1;
105
+ return colors.map((value) => Math.min(1, Math.max(0, value / divisor)));
106
+ }
@@ -0,0 +1,9 @@
1
+ import type { CaptureModelFormat } from '../stream-rendering';
2
+ export declare function CaptureRoomModel({ dollhouse, format, mediaType, opacity, url, }: {
3
+ dollhouse?: boolean;
4
+ format?: CaptureModelFormat;
5
+ mediaType: string;
6
+ opacity?: number;
7
+ url: string;
8
+ }): import("react").JSX.Element;
9
+ //# sourceMappingURL=room-model-layer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"room-model-layer.d.ts","sourceRoot":"","sources":["../../../src/capture/layers/room-model-layer.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAE7D,wBAAgB,gBAAgB,CAAC,EAC/B,SAAS,EACT,MAAM,EACN,SAAS,EACT,OAAa,EACb,GAAG,GACJ,EAAE;IACD,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,MAAM,CAAC,EAAE,kBAAkB,CAAA;IAC3B,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,GAAG,EAAE,MAAM,CAAA;CACZ,+BASA"}
@@ -0,0 +1,71 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useLoader } from '@react-three/fiber';
4
+ import { useEffect, useMemo } from 'react';
5
+ import { DoubleSide, FrontSide } from 'three';
6
+ import { USDLoader } from 'three/addons/loaders/USDLoader.js';
7
+ import { useGLTFKTX2 } from '../../hooks/use-gltf-ktx2';
8
+ import { rewriteLoopbackAssetUrl } from '../asset-url';
9
+ export function CaptureRoomModel({ dollhouse, format, mediaType, opacity = 100, url, }) {
10
+ if (format === 'usdz' ||
11
+ mediaType === 'model/vnd.usdz+zip' ||
12
+ url.toLowerCase().endsWith('.usdz')) {
13
+ return _jsx(UsdzRoomModel, { dollhouse: dollhouse, opacity: opacity, url: url });
14
+ }
15
+ return _jsx(GlbRoomModel, { dollhouse: dollhouse, opacity: opacity, url: url });
16
+ }
17
+ function UsdzRoomModel({ dollhouse, opacity, url, }) {
18
+ const source = useLoader(USDLoader, rewriteLoopbackAssetUrl(url));
19
+ const model = useClonedModel(source, opacity, dollhouse);
20
+ return _jsx("primitive", { object: model });
21
+ }
22
+ function GlbRoomModel({ dollhouse, opacity, url, }) {
23
+ const gltf = useGLTFKTX2(rewriteLoopbackAssetUrl(url));
24
+ const model = useClonedModel(gltf.scene, opacity, dollhouse);
25
+ return _jsx("primitive", { object: model });
26
+ }
27
+ function useClonedModel(source, opacity, dollhouse) {
28
+ const model = useMemo(() => {
29
+ const clone = source.clone(true);
30
+ clone.traverse((child) => {
31
+ const mesh = child;
32
+ if (!mesh.isMesh)
33
+ return;
34
+ mesh.material = Array.isArray(mesh.material)
35
+ ? mesh.material.map((material) => material.clone())
36
+ : mesh.material.clone();
37
+ for (const material of Array.isArray(mesh.material) ? mesh.material : [mesh.material]) {
38
+ if (dollhouse !== undefined)
39
+ material.side = dollhouse ? FrontSide : DoubleSide;
40
+ }
41
+ });
42
+ return clone;
43
+ }, [source, dollhouse]);
44
+ useEffect(() => {
45
+ const normalizedOpacity = opacity / 100;
46
+ const transparent = normalizedOpacity < 1;
47
+ model.traverse((child) => {
48
+ const mesh = child;
49
+ if (!mesh.isMesh)
50
+ return;
51
+ const materials = Array.isArray(mesh.material) ? mesh.material : [mesh.material];
52
+ for (const material of materials) {
53
+ material.transparent = transparent;
54
+ material.opacity = normalizedOpacity;
55
+ material.depthWrite = !transparent;
56
+ material.needsUpdate = true;
57
+ }
58
+ });
59
+ }, [model, opacity]);
60
+ useEffect(() => () => {
61
+ model.traverse((child) => {
62
+ const mesh = child;
63
+ if (!mesh.isMesh)
64
+ return;
65
+ const materials = Array.isArray(mesh.material) ? mesh.material : [mesh.material];
66
+ for (const material of materials)
67
+ material.dispose();
68
+ });
69
+ }, [model]);
70
+ return model;
71
+ }
@@ -0,0 +1,9 @@
1
+ import { BufferGeometry } from 'three';
2
+ export type SurfaceMeshData = {
3
+ colors: Float32Array;
4
+ indices: Uint16Array;
5
+ positions: Float32Array;
6
+ };
7
+ export declare function createSurfaceMeshGeometry(value: unknown): BufferGeometry | null;
8
+ export declare function buildSurfaceMeshData(value: unknown): SurfaceMeshData | null;
9
+ //# sourceMappingURL=surface-mesh-data.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"surface-mesh-data.d.ts","sourceRoot":"","sources":["../../../src/capture/layers/surface-mesh-data.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAiD,MAAM,OAAO,CAAA;AAErF,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,YAAY,CAAA;IACpB,OAAO,EAAE,WAAW,CAAA;IACpB,SAAS,EAAE,YAAY,CAAA;CACxB,CAAA;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,GAAG,IAAI,CAU/E;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,GAAG,IAAI,CAwC3E"}
@@ -0,0 +1,74 @@
1
+ import { SurfaceMeshPayloadSchema } from '@pascal-app/core/capture';
2
+ import { BufferGeometry, Float32BufferAttribute, Uint16BufferAttribute } from 'three';
3
+ export function createSurfaceMeshGeometry(value) {
4
+ const data = buildSurfaceMeshData(value);
5
+ if (!data)
6
+ return null;
7
+ const geometry = new BufferGeometry();
8
+ geometry.setAttribute('position', new Float32BufferAttribute(data.positions, 3));
9
+ geometry.setAttribute('color', new Float32BufferAttribute(data.colors, 3));
10
+ geometry.setIndex(new Uint16BufferAttribute(data.indices, 1));
11
+ geometry.computeVertexNormals();
12
+ geometry.computeBoundingSphere();
13
+ return geometry;
14
+ }
15
+ export function buildSurfaceMeshData(value) {
16
+ const parsed = SurfaceMeshPayloadSchema.safeParse(value);
17
+ if (!parsed.success)
18
+ return null;
19
+ const payload = parsed.data;
20
+ const positionBytes = decodeBase64(payload.positions);
21
+ const colorBytes = decodeBase64(payload.colors);
22
+ const indexBytes = decodeBase64(payload.indices);
23
+ if (positionBytes.byteLength !== payload.vertexCount * 3 * 2 ||
24
+ colorBytes.byteLength !== payload.vertexCount * 3 ||
25
+ indexBytes.byteLength !== payload.faceCount * 3 * 2) {
26
+ return null;
27
+ }
28
+ const positions = new Float32Array(payload.vertexCount * 3);
29
+ const colors = new Float32Array(payload.vertexCount * 3);
30
+ const indices = new Uint16Array(payload.faceCount * 3);
31
+ const positionView = new DataView(positionBytes.buffer, positionBytes.byteOffset, positionBytes.byteLength);
32
+ const indexView = new DataView(indexBytes.buffer, indexBytes.byteOffset, indexBytes.byteLength);
33
+ for (let index = 0; index < payload.vertexCount; index += 1) {
34
+ for (let axis = 0; axis < 3; axis += 1) {
35
+ const offset = index * 3 + axis;
36
+ const minimum = payload.boundsMin[axis] ?? 0;
37
+ const maximum = payload.boundsMax[axis] ?? minimum;
38
+ const quantized = positionView.getUint16(offset * 2, true);
39
+ positions[offset] = minimum + (quantized / 65_535) * (maximum - minimum);
40
+ colors[offset] = (colorBytes[offset] ?? 0) / 255;
41
+ }
42
+ }
43
+ for (let index = 0; index < indices.length; index += 1) {
44
+ const vertexIndex = indexView.getUint16(index * 2, true);
45
+ if (vertexIndex >= payload.vertexCount)
46
+ return null;
47
+ indices[index] = vertexIndex;
48
+ }
49
+ return { colors, indices, positions };
50
+ }
51
+ function decodeBase64(value) {
52
+ const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
53
+ const clean = value.replace(/\s/g, '');
54
+ const padding = clean.endsWith('==') ? 2 : clean.endsWith('=') ? 1 : 0;
55
+ const outputLength = Math.floor((clean.length * 3) / 4) - padding;
56
+ const output = new Uint8Array(Math.max(0, outputLength));
57
+ let outputIndex = 0;
58
+ for (let index = 0; index < clean.length; index += 4) {
59
+ const a = alphabet.indexOf(clean[index] ?? '');
60
+ const b = alphabet.indexOf(clean[index + 1] ?? '');
61
+ const c = clean[index + 2] === '=' ? 0 : alphabet.indexOf(clean[index + 2] ?? '');
62
+ const d = clean[index + 3] === '=' ? 0 : alphabet.indexOf(clean[index + 3] ?? '');
63
+ if (a < 0 || b < 0 || c < 0 || d < 0)
64
+ return new Uint8Array();
65
+ const bits = (a << 18) | (b << 12) | (c << 6) | d;
66
+ if (outputIndex < output.length)
67
+ output[outputIndex++] = (bits >> 16) & 0xff;
68
+ if (outputIndex < output.length)
69
+ output[outputIndex++] = (bits >> 8) & 0xff;
70
+ if (outputIndex < output.length)
71
+ output[outputIndex++] = bits & 0xff;
72
+ }
73
+ return output;
74
+ }
@@ -0,0 +1,7 @@
1
+ export { buildSurfaceMeshData, type SurfaceMeshData } from './surface-mesh-data';
2
+ export declare function CaptureSurfaceMeshLayer({ inline, dollhouse, appearance, }: {
3
+ inline: unknown;
4
+ dollhouse?: boolean;
5
+ appearance?: 'clay' | 'recorded';
6
+ }): import("react").JSX.Element | null;
7
+ //# sourceMappingURL=surface-mesh-layer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"surface-mesh-layer.d.ts","sourceRoot":"","sources":["../../../src/capture/layers/surface-mesh-layer.tsx"],"names":[],"mappings":"AAOA,OAAO,EAAE,oBAAoB,EAAE,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAEhF,wBAAgB,uBAAuB,CAAC,EACtC,MAAM,EACN,SAAiB,EACjB,UAAuB,GACxB,EAAE;IACD,MAAM,EAAE,OAAO,CAAA;IACf,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,GAAG,UAAU,CAAA;CACjC,sCAqBA"}
@@ -0,0 +1,16 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useEffect, useMemo } from 'react';
4
+ import { DoubleSide, FrontSide } from 'three';
5
+ import { createClayMatcap } from './clay-matcap';
6
+ import { createSurfaceMeshGeometry } from './surface-mesh-data';
7
+ export { buildSurfaceMeshData } from './surface-mesh-data';
8
+ export function CaptureSurfaceMeshLayer({ inline, dollhouse = false, appearance = 'recorded', }) {
9
+ const matcap = useMemo(() => (appearance === 'clay' ? createClayMatcap() : null), [appearance]);
10
+ useEffect(() => () => matcap?.dispose(), [matcap]);
11
+ const geometry = useMemo(() => createSurfaceMeshGeometry(inline), [inline]);
12
+ useEffect(() => () => geometry?.dispose(), [geometry]);
13
+ if (!geometry)
14
+ return null;
15
+ return (_jsx("mesh", { frustumCulled: false, geometry: geometry, children: matcap ? (_jsx("meshMatcapMaterial", { matcap: matcap, side: dollhouse ? FrontSide : DoubleSide })) : (_jsx("meshStandardMaterial", { metalness: 0, roughness: 0.9, side: dollhouse ? FrontSide : DoubleSide, vertexColors: true })) }));
16
+ }
@@ -0,0 +1,3 @@
1
+ export { createClayMatcap } from './layers/clay-matcap';
2
+ export { buildSurfaceMeshData, createSurfaceMeshGeometry, type SurfaceMeshData, } from './layers/surface-mesh-data';
3
+ //# sourceMappingURL=preview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../../src/capture/preview.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AACvD,OAAO,EACL,oBAAoB,EACpB,yBAAyB,EACzB,KAAK,eAAe,GACrB,MAAM,4BAA4B,CAAA"}
@@ -0,0 +1,2 @@
1
+ export { createClayMatcap } from './layers/clay-matcap';
2
+ export { buildSurfaceMeshData, createSurfaceMeshGeometry, } from './layers/surface-mesh-data';
@@ -0,0 +1,22 @@
1
+ import type { CaptureSessionDescriptor, CaptureSessionLocator, CaptureSource, CaptureSourceResolver, CaptureStreamDescriptor, CaptureStreamPacket } from '@pascal-app/core/capture';
2
+ export type CaptureSourceState = {
3
+ descriptor: CaptureSessionDescriptor | null;
4
+ descriptorVersion: number;
5
+ error: Error | null;
6
+ loading: boolean;
7
+ packets: Readonly<Record<string, readonly CaptureStreamPacket[]>>;
8
+ retry: () => void;
9
+ source: CaptureSource | null;
10
+ streamEpochs: Readonly<Record<string, string>>;
11
+ };
12
+ export type UseCaptureSourceOptions = {
13
+ maxPacketsPerStream?: number;
14
+ streamFilter?: (stream: CaptureStreamDescriptor) => boolean;
15
+ subscribe?: boolean;
16
+ };
17
+ export declare function useCaptureSource(locator: CaptureSessionLocator | null, resolveSource: CaptureSourceResolver, options?: UseCaptureSourceOptions): CaptureSourceState;
18
+ export declare function captureSubscriptionStreamIds(descriptor: CaptureSessionDescriptor, streamFilter: ((stream: CaptureStreamDescriptor) => boolean) | undefined): readonly string[] | undefined;
19
+ export declare function retainLiveCapturePackets(packetsByStream: Readonly<Record<string, readonly CaptureStreamPacket[]>>, descriptor: CaptureSessionDescriptor): Readonly<Record<string, readonly CaptureStreamPacket[]>>;
20
+ export declare function appendCapturePacket(packetsByStream: Readonly<Record<string, readonly CaptureStreamPacket[]>>, packet: CaptureStreamPacket, maxPacketsPerStream: number): Readonly<Record<string, readonly CaptureStreamPacket[]>>;
21
+ export declare function nextCaptureStreamEpoch(currentEpoch: string | undefined, previousPackets: readonly CaptureStreamPacket[], packet: CaptureStreamPacket): string;
22
+ //# sourceMappingURL=source-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source-state.d.ts","sourceRoot":"","sources":["../../src/capture/source-state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EACxB,qBAAqB,EACrB,aAAa,EACb,qBAAqB,EACrB,uBAAuB,EACvB,mBAAmB,EACpB,MAAM,0BAA0B,CAAA;AAGjC,MAAM,MAAM,kBAAkB,GAAG;IAC/B,UAAU,EAAE,wBAAwB,GAAG,IAAI,CAAA;IAC3C,iBAAiB,EAAE,MAAM,CAAA;IACzB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;IACnB,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,mBAAmB,EAAE,CAAC,CAAC,CAAA;IACjE,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,MAAM,EAAE,aAAa,GAAG,IAAI,CAAA;IAC5B,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CAC/C,CAAA;AAID,MAAM,MAAM,uBAAuB,GAAG;IACpC,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,uBAAuB,KAAK,OAAO,CAAA;IAC3D,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB,CAAA;AAKD,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,qBAAqB,GAAG,IAAI,EACrC,aAAa,EAAE,qBAAqB,EACpC,OAAO,GAAE,uBAA4B,GACpC,kBAAkB,CAoDpB;AA8FD,wBAAgB,4BAA4B,CAC1C,UAAU,EAAE,wBAAwB,EACpC,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,uBAAuB,KAAK,OAAO,CAAC,GAAG,SAAS,GACvE,SAAS,MAAM,EAAE,GAAG,SAAS,CAI/B;AAED,wBAAgB,wBAAwB,CACtC,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,mBAAmB,EAAE,CAAC,CAAC,EACzE,UAAU,EAAE,wBAAwB,GACnC,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,mBAAmB,EAAE,CAAC,CAAC,CAS1D;AAgBD,wBAAgB,mBAAmB,CACjC,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,mBAAmB,EAAE,CAAC,CAAC,EACzE,MAAM,EAAE,mBAAmB,EAC3B,mBAAmB,EAAE,MAAM,GAC1B,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,mBAAmB,EAAE,CAAC,CAAC,CA+B1D;AAED,wBAAgB,sBAAsB,CACpC,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,eAAe,EAAE,SAAS,mBAAmB,EAAE,EAC/C,MAAM,EAAE,mBAAmB,GAC1B,MAAM,CAYR"}
@@ -0,0 +1,182 @@
1
+ import { useCallback, useEffect, useState } from 'react';
2
+ const EMPTY_PACKETS = {};
3
+ const EMPTY_STREAM_EPOCHS = {};
4
+ export function useCaptureSource(locator, resolveSource, options = {}) {
5
+ const { maxPacketsPerStream = 32, streamFilter, subscribe = true } = options;
6
+ const [retryVersion, setRetryVersion] = useState(0);
7
+ const [state, setState] = useState({
8
+ descriptor: null,
9
+ descriptorVersion: 0,
10
+ error: null,
11
+ loading: Boolean(locator),
12
+ packets: EMPTY_PACKETS,
13
+ source: null,
14
+ streamEpochs: EMPTY_STREAM_EPOCHS,
15
+ });
16
+ useEffect(() => {
17
+ const abort = new AbortController();
18
+ if (!locator) {
19
+ setState({
20
+ descriptor: null,
21
+ descriptorVersion: 0,
22
+ error: null,
23
+ loading: false,
24
+ packets: EMPTY_PACKETS,
25
+ source: null,
26
+ streamEpochs: EMPTY_STREAM_EPOCHS,
27
+ });
28
+ return () => abort.abort();
29
+ }
30
+ setState({
31
+ descriptor: null,
32
+ descriptorVersion: retryVersion,
33
+ error: null,
34
+ loading: true,
35
+ packets: EMPTY_PACKETS,
36
+ source: null,
37
+ streamEpochs: EMPTY_STREAM_EPOCHS,
38
+ });
39
+ void consumeCaptureSource(locator, resolveSource, abort.signal, retryVersion, maxPacketsPerStream, streamFilter, subscribe, setState);
40
+ return () => abort.abort();
41
+ }, [locator, maxPacketsPerStream, resolveSource, retryVersion, streamFilter, subscribe]);
42
+ const retry = useCallback(() => setRetryVersion((current) => current + 1), []);
43
+ return { ...state, retry };
44
+ }
45
+ async function consumeCaptureSource(locator, resolveSource, signal, descriptorVersion, maxPacketsPerStream, streamFilter, subscribe, setState) {
46
+ try {
47
+ const source = await resolveSource(locator);
48
+ const descriptor = await source.describe(signal);
49
+ if (signal.aborted)
50
+ return;
51
+ setState({
52
+ descriptor,
53
+ descriptorVersion,
54
+ error: null,
55
+ loading: false,
56
+ packets: EMPTY_PACKETS,
57
+ source,
58
+ streamEpochs: EMPTY_STREAM_EPOCHS,
59
+ });
60
+ if (!(subscribe && source.subscribe))
61
+ return;
62
+ const streamIds = captureSubscriptionStreamIds(descriptor, streamFilter);
63
+ const iterator = source.subscribe({ signal, streamIds })[Symbol.asyncIterator]();
64
+ const closeIterator = () => void iterator.return?.();
65
+ signal.addEventListener('abort', closeIterator, { once: true });
66
+ try {
67
+ for (;;) {
68
+ const next = await iterator.next();
69
+ if (next.done || signal.aborted || next.value.type === 'closed')
70
+ return;
71
+ const event = next.value;
72
+ if (event.type === 'descriptor') {
73
+ setState((current) => {
74
+ const revisionChanged = current.descriptor?.revisionId !== event.descriptor.revisionId &&
75
+ (current.descriptor?.revisionId !== undefined ||
76
+ event.descriptor.revisionId !== undefined);
77
+ return {
78
+ ...current,
79
+ descriptor: event.descriptor,
80
+ descriptorVersion: current.descriptorVersion + 1,
81
+ packets: revisionChanged
82
+ ? EMPTY_PACKETS
83
+ : retainLiveCapturePackets(current.packets, event.descriptor),
84
+ streamEpochs: revisionChanged
85
+ ? EMPTY_STREAM_EPOCHS
86
+ : retainLiveCaptureStreamValues(current.streamEpochs, event.descriptor),
87
+ };
88
+ });
89
+ }
90
+ else {
91
+ setState((current) => {
92
+ const previousPackets = current.packets[event.packet.streamId] ?? [];
93
+ const packets = appendCapturePacket(current.packets, event.packet, maxPacketsPerStream);
94
+ if (packets === current.packets)
95
+ return current;
96
+ return {
97
+ ...current,
98
+ packets,
99
+ streamEpochs: {
100
+ ...current.streamEpochs,
101
+ [event.packet.streamId]: nextCaptureStreamEpoch(current.streamEpochs[event.packet.streamId], previousPackets, event.packet),
102
+ },
103
+ };
104
+ });
105
+ }
106
+ }
107
+ }
108
+ finally {
109
+ signal.removeEventListener('abort', closeIterator);
110
+ await iterator.return?.();
111
+ }
112
+ }
113
+ catch (cause) {
114
+ if (signal.aborted)
115
+ return;
116
+ setState({
117
+ descriptor: null,
118
+ descriptorVersion,
119
+ error: cause instanceof Error ? cause : new Error('Could not load capture session.'),
120
+ loading: false,
121
+ packets: EMPTY_PACKETS,
122
+ source: null,
123
+ streamEpochs: EMPTY_STREAM_EPOCHS,
124
+ });
125
+ }
126
+ }
127
+ export function captureSubscriptionStreamIds(descriptor, streamFilter) {
128
+ return streamFilter
129
+ ? descriptor.streams.filter(streamFilter).map((stream) => stream.id)
130
+ : undefined;
131
+ }
132
+ export function retainLiveCapturePackets(packetsByStream, descriptor) {
133
+ const liveStreamIds = new Set(descriptor.streams
134
+ .filter((stream) => stream.availability === 'live')
135
+ .map((stream) => stream.id));
136
+ return Object.fromEntries(Object.entries(packetsByStream).filter(([streamId]) => liveStreamIds.has(streamId)));
137
+ }
138
+ function retainLiveCaptureStreamValues(valuesByStream, descriptor) {
139
+ const liveStreamIds = new Set(descriptor.streams
140
+ .filter((stream) => stream.availability === 'live')
141
+ .map((stream) => stream.id));
142
+ return Object.fromEntries(Object.entries(valuesByStream).filter(([streamId]) => liveStreamIds.has(streamId)));
143
+ }
144
+ export function appendCapturePacket(packetsByStream, packet, maxPacketsPerStream) {
145
+ const previous = packetsByStream[packet.streamId] ?? [];
146
+ const latest = previous.at(-1);
147
+ const currentGeneration = latest?.generation;
148
+ if (currentGeneration !== undefined && packet.generation < currentGeneration)
149
+ return packetsByStream;
150
+ const resetSequence = previous[0]?.keyframe ? previous[0].sequence : null;
151
+ if (currentGeneration === packet.generation &&
152
+ resetSequence !== null &&
153
+ packet.sequence <= resetSequence) {
154
+ return packetsByStream;
155
+ }
156
+ const resetsStream = Boolean(packet.keyframe) || latest?.frameId !== packet.frameId;
157
+ if (latest &&
158
+ currentGeneration === packet.generation &&
159
+ resetsStream &&
160
+ packet.sequence <= latest.sequence) {
161
+ return packetsByStream;
162
+ }
163
+ const sameGeneration = currentGeneration === packet.generation && !resetsStream ? previous : [];
164
+ if (sameGeneration.some((candidate) => candidate.sequence === packet.sequence))
165
+ return packetsByStream;
166
+ const limit = Math.max(1, maxPacketsPerStream);
167
+ const next = [...sameGeneration, packet]
168
+ .sort((left, right) => left.sequence - right.sequence)
169
+ .slice(-limit);
170
+ return { ...packetsByStream, [packet.streamId]: next };
171
+ }
172
+ export function nextCaptureStreamEpoch(currentEpoch, previousPackets, packet) {
173
+ const latest = previousPackets.at(-1);
174
+ const resetsStream = previousPackets.length === 0 ||
175
+ latest?.generation !== packet.generation ||
176
+ latest.frameId !== packet.frameId ||
177
+ Boolean(packet.keyframe);
178
+ if (resetsStream)
179
+ return `${packet.generation}:${packet.frameId ?? ''}:${packet.sequence}`;
180
+ return (currentEpoch ??
181
+ `${latest.generation}:${latest.frameId ?? ''}:${previousPackets[0]?.sequence ?? latest.sequence}`);
182
+ }
@@ -0,0 +1,14 @@
1
+ import { type CaptureArtifactReference, type CaptureStreamDescriptor } from '@pascal-app/core/capture';
2
+ export type CaptureModelFormat = 'gltf' | 'usdz';
3
+ export declare function isCaptureStreamRenderable(stream: CaptureStreamDescriptor, customRendererKeys?: ReadonlySet<string>): boolean;
4
+ /**
5
+ * Extracted viewer previews (device motion, point cloud, surface mesh) are
6
+ * archived as JSON payload artifacts whose content matches the inline shape.
7
+ * One predicate decides both renderability and runtime hydration, so a
8
+ * stream can never be declared renderable without a hydration path.
9
+ */
10
+ export declare function streamHydratesJsonPayload(stream: CaptureStreamDescriptor): boolean;
11
+ export declare function isCaptureModelArtifact(artifact: CaptureArtifactReference | undefined): boolean;
12
+ export declare function captureModelFormat(artifact: CaptureArtifactReference | undefined): CaptureModelFormat | null;
13
+ export declare function isCapturePointCloudArtifact(artifact: CaptureArtifactReference | undefined): boolean;
14
+ //# sourceMappingURL=stream-rendering.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream-rendering.d.ts","sourceRoot":"","sources":["../../src/capture/stream-rendering.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAK7B,MAAM,0BAA0B,CAAA;AAOjC,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,MAAM,CAAA;AAEhD,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,uBAAuB,EAC/B,kBAAkB,GAAE,WAAW,CAAC,MAAM,CAAa,GAClD,OAAO,CA0BT;AAID;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,uBAAuB,GAAG,OAAO,CAKlF;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,wBAAwB,GAAG,SAAS,GAAG,OAAO,CAE9F;AAED,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,wBAAwB,GAAG,SAAS,GAC7C,kBAAkB,GAAG,IAAI,CAS3B;AAED,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,wBAAwB,GAAG,SAAS,GAC7C,OAAO,CAGT"}
@@ -0,0 +1,69 @@
1
+ import { captureLayerKey, DeviceMotionTrajectorySchema, PointCloudPayloadSchema, SurfaceMeshPayloadSchema, } from '@pascal-app/core/capture';
2
+ const GLB_MEDIA_TYPES = new Set(['model/gltf-binary', 'model/gltf+json']);
3
+ const USDZ_MEDIA_TYPES = new Set(['model/vnd.usdz+zip']);
4
+ const PLY_MEDIA_TYPES = new Set(['application/ply', 'application/vnd.ply', 'model/ply']);
5
+ const JSON_MEDIA_TYPES = new Set(['application/json']);
6
+ export function isCaptureStreamRenderable(stream, customRendererKeys = new Set()) {
7
+ if (stream.availability === 'failed' || stream.availability === 'pending')
8
+ return false;
9
+ const layerKey = captureLayerKey(stream);
10
+ if (customRendererKeys.has(layerKey) || customRendererKeys.has(stream.kind))
11
+ return true;
12
+ if (layerKey === 'model')
13
+ return isCaptureModelArtifact(stream.artifact);
14
+ if (layerKey === 'deviceMotion') {
15
+ return (stream.availability === 'live' ||
16
+ streamHydratesJsonPayload(stream) ||
17
+ DeviceMotionTrajectorySchema.safeParse(stream.inline).success);
18
+ }
19
+ if (layerKey === 'pointCloud') {
20
+ return (stream.availability === 'live' ||
21
+ isCapturePointCloudArtifact(stream.artifact) ||
22
+ streamHydratesJsonPayload(stream) ||
23
+ PointCloudPayloadSchema.safeParse(stream.inline).success);
24
+ }
25
+ if (layerKey === 'surfaceMesh') {
26
+ return (streamHydratesJsonPayload(stream) || SurfaceMeshPayloadSchema.safeParse(stream.inline).success);
27
+ }
28
+ return false;
29
+ }
30
+ const JSON_PAYLOAD_LAYER_KEYS = new Set(['deviceMotion', 'pointCloud', 'surfaceMesh']);
31
+ /**
32
+ * Extracted viewer previews (device motion, point cloud, surface mesh) are
33
+ * archived as JSON payload artifacts whose content matches the inline shape.
34
+ * One predicate decides both renderability and runtime hydration, so a
35
+ * stream can never be declared renderable without a hydration path.
36
+ */
37
+ export function streamHydratesJsonPayload(stream) {
38
+ const artifact = stream.artifact;
39
+ if (!artifact || stream.inline != null)
40
+ return false;
41
+ if (!JSON_PAYLOAD_LAYER_KEYS.has(captureLayerKey(stream)))
42
+ return false;
43
+ return JSON_MEDIA_TYPES.has(artifact.mediaType) || hasExtension(artifact.uri, ['.json']);
44
+ }
45
+ export function isCaptureModelArtifact(artifact) {
46
+ return captureModelFormat(artifact) !== null;
47
+ }
48
+ export function captureModelFormat(artifact) {
49
+ if (!artifact)
50
+ return null;
51
+ if (USDZ_MEDIA_TYPES.has(artifact.mediaType) || hasExtension(artifact.uri, ['.usdz'])) {
52
+ return 'usdz';
53
+ }
54
+ if (GLB_MEDIA_TYPES.has(artifact.mediaType) || hasExtension(artifact.uri, ['.glb', '.gltf'])) {
55
+ return 'gltf';
56
+ }
57
+ return null;
58
+ }
59
+ export function isCapturePointCloudArtifact(artifact) {
60
+ if (!artifact)
61
+ return false;
62
+ return PLY_MEDIA_TYPES.has(artifact.mediaType) || hasExtension(artifact.uri, ['.ply']);
63
+ }
64
+ function hasExtension(uri, extensions) {
65
+ if (!uri)
66
+ return false;
67
+ const path = uri.split(/[?#]/, 1)[0]?.toLowerCase() ?? '';
68
+ return extensions.some((extension) => path.endsWith(extension));
69
+ }
@@ -0,0 +1,20 @@
1
+ import { type DeviceMotionTrajectoryPayload } from '@pascal-app/core/capture';
2
+ export type DeviceTrajectoryPose = {
3
+ position: [number, number, number];
4
+ quaternion: [number, number, number, number];
5
+ segment: number;
6
+ timestamp: number;
7
+ };
8
+ export type DeviceTrajectory = {
9
+ duration: number;
10
+ poses: DeviceTrajectoryPose[];
11
+ };
12
+ export type DeviceTrajectoryFrame = {
13
+ alpha: number;
14
+ from: DeviceTrajectoryPose;
15
+ to: DeviceTrajectoryPose;
16
+ };
17
+ export declare function parseDeviceTrajectoryPayload(trajectory: DeviceMotionTrajectoryPayload | null | undefined): DeviceTrajectory | null;
18
+ export declare function parseDeviceTrajectoryPackets(payloads: readonly unknown[]): DeviceTrajectory | null;
19
+ export declare function sampleDeviceTrajectory(trajectory: DeviceTrajectory, elapsed: number): DeviceTrajectoryFrame;
20
+ //# sourceMappingURL=trajectory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trajectory.d.ts","sourceRoot":"","sources":["../../src/capture/trajectory.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,6BAA6B,EAEnC,MAAM,0BAA0B,CAAA;AAGjC,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAClC,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5C,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,oBAAoB,EAAE,CAAA;CAC9B,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,oBAAoB,CAAA;IAC1B,EAAE,EAAE,oBAAoB,CAAA;CACzB,CAAA;AAED,wBAAgB,4BAA4B,CAC1C,UAAU,EAAE,6BAA6B,GAAG,IAAI,GAAG,SAAS,GAC3D,gBAAgB,GAAG,IAAI,CAezB;AAED,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,SAAS,OAAO,EAAE,GAC3B,gBAAgB,GAAG,IAAI,CAezB;AAED,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,gBAAgB,EAC5B,OAAO,EAAE,MAAM,GACd,qBAAqB,CAqBvB"}