@hraness/slopcamera 3.2.4

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 (441) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE.md +60 -0
  3. package/PRIVACY.md +46 -0
  4. package/README.md +311 -0
  5. package/SECURITY.md +21 -0
  6. package/apps/desktop/README.md +966 -0
  7. package/apps/desktop/analysis/protocol.ts +223 -0
  8. package/apps/desktop/application/capability-binding.ts +594 -0
  9. package/apps/desktop/application/context.ts +97 -0
  10. package/apps/desktop/application/creative-iteration.ts +1385 -0
  11. package/apps/desktop/application/default-registry.ts +146 -0
  12. package/apps/desktop/application/directing-contract.ts +218 -0
  13. package/apps/desktop/application/directing-plan.ts +178 -0
  14. package/apps/desktop/application/errors.ts +18 -0
  15. package/apps/desktop/application/gateway-port.ts +867 -0
  16. package/apps/desktop/application/html-overlay-browser-runtime.ts +687 -0
  17. package/apps/desktop/application/html-overlay-integrity.ts +337 -0
  18. package/apps/desktop/application/html-overlay-renderer.ts +45 -0
  19. package/apps/desktop/application/index.ts +13 -0
  20. package/apps/desktop/application/operation-completion-checkpoint.ts +333 -0
  21. package/apps/desktop/application/operation-effects.ts +94 -0
  22. package/apps/desktop/application/operation.ts +224 -0
  23. package/apps/desktop/application/operations/analysis/faces.ts +244 -0
  24. package/apps/desktop/application/operations/analysis/music.ts +248 -0
  25. package/apps/desktop/application/operations/analysis/project-auto-zooms.ts +302 -0
  26. package/apps/desktop/application/operations/analysis/project-inactivity.ts +381 -0
  27. package/apps/desktop/application/operations/analysis/scenes.ts +272 -0
  28. package/apps/desktop/application/operations/analysis/shared.ts +202 -0
  29. package/apps/desktop/application/operations/derive/edit-batch.ts +967 -0
  30. package/apps/desktop/application/operations/derive/follow-faces.ts +454 -0
  31. package/apps/desktop/application/operations/edit/bind-candidate-revision.ts +91 -0
  32. package/apps/desktop/application/operations/edit/create-candidate-revision.ts +377 -0
  33. package/apps/desktop/application/operations/edit/create-revision.ts +255 -0
  34. package/apps/desktop/application/operations/edit/freeze-revision.ts +182 -0
  35. package/apps/desktop/application/operations/gateway/image.ts +41 -0
  36. package/apps/desktop/application/operations/gateway/index.ts +17 -0
  37. package/apps/desktop/application/operations/gateway/shared.ts +122 -0
  38. package/apps/desktop/application/operations/gateway/speech.ts +41 -0
  39. package/apps/desktop/application/operations/gateway/transcription.ts +42 -0
  40. package/apps/desktop/application/operations/gateway/video.ts +41 -0
  41. package/apps/desktop/application/operations/index.ts +31 -0
  42. package/apps/desktop/application/operations/iteration/create-candidate.ts +240 -0
  43. package/apps/desktop/application/operations/iteration/create-matrix.ts +117 -0
  44. package/apps/desktop/application/operations/iteration/select.ts +155 -0
  45. package/apps/desktop/application/operations/iteration/shared.ts +250 -0
  46. package/apps/desktop/application/operations/media/audio-effects.ts +376 -0
  47. package/apps/desktop/application/operations/media/binding.ts +79 -0
  48. package/apps/desktop/application/operations/media/capabilities.ts +112 -0
  49. package/apps/desktop/application/operations/media/color-grade.ts +343 -0
  50. package/apps/desktop/application/operations/media/html-overlay.ts +802 -0
  51. package/apps/desktop/application/operations/media/index.ts +8 -0
  52. package/apps/desktop/application/operations/media/ingest.ts +303 -0
  53. package/apps/desktop/application/operations/media/overlay.ts +729 -0
  54. package/apps/desktop/application/operations/media/shared.ts +845 -0
  55. package/apps/desktop/application/operations/media/transform-platform.ts +120 -0
  56. package/apps/desktop/application/operations/project/commit-edits.ts +2054 -0
  57. package/apps/desktop/application/operations/project/promote-selection.ts +495 -0
  58. package/apps/desktop/application/operations/project/snapshot.ts +95 -0
  59. package/apps/desktop/application/operations/recording/index.ts +5 -0
  60. package/apps/desktop/application/operations/recording/pause.ts +34 -0
  61. package/apps/desktop/application/operations/recording/resume.ts +34 -0
  62. package/apps/desktop/application/operations/recording/shared.ts +199 -0
  63. package/apps/desktop/application/operations/recording/start.ts +169 -0
  64. package/apps/desktop/application/operations/recording/stop.ts +34 -0
  65. package/apps/desktop/application/operations/render/bind-candidate-output.ts +324 -0
  66. package/apps/desktop/application/operations/render/materialize-selection.ts +322 -0
  67. package/apps/desktop/application/operations/render/project-caption-plan.ts +287 -0
  68. package/apps/desktop/application/operations/render/project-plan.ts +939 -0
  69. package/apps/desktop/application/operations/render/project-spatial-receipt.ts +70 -0
  70. package/apps/desktop/application/operations/render/project.ts +1922 -0
  71. package/apps/desktop/application/operations/shared.ts +67 -0
  72. package/apps/desktop/application/operations/slopcamera-portable.ts +1188 -0
  73. package/apps/desktop/application/operations/slopcamera-visuals.ts +784 -0
  74. package/apps/desktop/application/operations/spatial-project.ts +209 -0
  75. package/apps/desktop/application/operations/spatial-render.ts +415 -0
  76. package/apps/desktop/application/operations/spatial-scene.ts +97 -0
  77. package/apps/desktop/application/operations/studio.ts +143 -0
  78. package/apps/desktop/application/output-publication-lease.ts +163 -0
  79. package/apps/desktop/application/project-publication-lease.ts +233 -0
  80. package/apps/desktop/application/project-store.ts +196 -0
  81. package/apps/desktop/application/receipts.ts +533 -0
  82. package/apps/desktop/application/registry.ts +182 -0
  83. package/apps/desktop/application/spatial-assets.ts +675 -0
  84. package/apps/desktop/application/spatial-compositor-cadence.ts +23 -0
  85. package/apps/desktop/application/spatial-project-authority.ts +35 -0
  86. package/apps/desktop/application/spatial-project-lease.ts +126 -0
  87. package/apps/desktop/application/spatial-project-render-preparation.ts +225 -0
  88. package/apps/desktop/application/spatial-project-store.ts +286 -0
  89. package/apps/desktop/application/spatial-render-projection.ts +201 -0
  90. package/apps/desktop/application/spatial-render.ts +677 -0
  91. package/apps/desktop/application/spatial-spz.ts +44 -0
  92. package/apps/desktop/application/spatial-world-import.ts +135 -0
  93. package/apps/desktop/application/spatial-world-provenance.ts +46 -0
  94. package/apps/desktop/application/studio-port.ts +58 -0
  95. package/apps/desktop/application/verified-receipt-reconciliation.ts +1574 -0
  96. package/apps/desktop/capture/protocol.ts +689 -0
  97. package/apps/desktop/cli/analyzer.ts +406 -0
  98. package/apps/desktop/cli/args.ts +3181 -0
  99. package/apps/desktop/cli/asset-ingest.ts +747 -0
  100. package/apps/desktop/cli/atomic-render-effects.ts +210 -0
  101. package/apps/desktop/cli/atomic-render-platform.ts +205 -0
  102. package/apps/desktop/cli/atomic-render.ts +50 -0
  103. package/apps/desktop/cli/audio-analysis.ts +252 -0
  104. package/apps/desktop/cli/bounded-map.ts +39 -0
  105. package/apps/desktop/cli/bundle-service.ts +256 -0
  106. package/apps/desktop/cli/capabilities.ts +212 -0
  107. package/apps/desktop/cli/capture-bundle.ts +1117 -0
  108. package/apps/desktop/cli/command-host-resources.ts +251 -0
  109. package/apps/desktop/cli/commands.ts +6836 -0
  110. package/apps/desktop/cli/directing-blob.ts +291 -0
  111. package/apps/desktop/cli/directing-media.ts +517 -0
  112. package/apps/desktop/cli/directing-quote.ts +89 -0
  113. package/apps/desktop/cli/directing-service.ts +288 -0
  114. package/apps/desktop/cli/emoji-assets.ts +538 -0
  115. package/apps/desktop/cli/errors.ts +29 -0
  116. package/apps/desktop/cli/face-analysis-service.ts +940 -0
  117. package/apps/desktop/cli/filter-script.ts +76 -0
  118. package/apps/desktop/cli/gateway-application-port.ts +1142 -0
  119. package/apps/desktop/cli/gateway-credential.ts +81 -0
  120. package/apps/desktop/cli/gateway-media-artifacts.ts +723 -0
  121. package/apps/desktop/cli/gateway-media-catalog.ts +922 -0
  122. package/apps/desktop/cli/gateway-media-service.ts +3106 -0
  123. package/apps/desktop/cli/gateway-media-signature.ts +122 -0
  124. package/apps/desktop/cli/gateway-provider-options.ts +562 -0
  125. package/apps/desktop/cli/gateway-scene-provider.ts +391 -0
  126. package/apps/desktop/cli/help.ts +501 -0
  127. package/apps/desktop/cli/html-overlay-gpu.ts +97 -0
  128. package/apps/desktop/cli/html-overlay-renderer.ts +2478 -0
  129. package/apps/desktop/cli/io.ts +290 -0
  130. package/apps/desktop/cli/main.ts +123 -0
  131. package/apps/desktop/cli/media-effects-service.ts +885 -0
  132. package/apps/desktop/cli/media-ingest-model.ts +364 -0
  133. package/apps/desktop/cli/media-ingest-platform.ts +227 -0
  134. package/apps/desktop/cli/media-ingest-program.ts +200 -0
  135. package/apps/desktop/cli/media-ingest.ts +30 -0
  136. package/apps/desktop/cli/music-analysis-service.ts +434 -0
  137. package/apps/desktop/cli/mutation-lock.ts +530 -0
  138. package/apps/desktop/cli/native-assets.d.ts +12 -0
  139. package/apps/desktop/cli/native-media-runtime.macos.ts +7 -0
  140. package/apps/desktop/cli/overlay-playback.ts +177 -0
  141. package/apps/desktop/cli/paths.ts +265 -0
  142. package/apps/desktop/cli/portable-surface.ts +242 -0
  143. package/apps/desktop/cli/project-camera-receipt.ts +112 -0
  144. package/apps/desktop/cli/project-face-camera.ts +394 -0
  145. package/apps/desktop/cli/project-inactivity-service.ts +552 -0
  146. package/apps/desktop/cli/project-media-integrity.ts +121 -0
  147. package/apps/desktop/cli/project-renderer.ts +1526 -0
  148. package/apps/desktop/cli/project-service.ts +519 -0
  149. package/apps/desktop/cli/project-state-transaction.ts +520 -0
  150. package/apps/desktop/cli/query-limits.ts +1 -0
  151. package/apps/desktop/cli/recording-controller.ts +1567 -0
  152. package/apps/desktop/cli/recording-daemon.ts +931 -0
  153. package/apps/desktop/cli/recording-ref.ts +56 -0
  154. package/apps/desktop/cli/renamed-environment.ts +6 -0
  155. package/apps/desktop/cli/renderer.ts +1439 -0
  156. package/apps/desktop/cli/run-cli-test-helper.ts +60 -0
  157. package/apps/desktop/cli/scene-analysis-service.ts +879 -0
  158. package/apps/desktop/cli/spatial-compositor-verifier.ts +213 -0
  159. package/apps/desktop/cli/spatial-project-service.ts +90 -0
  160. package/apps/desktop/cli/spatial-scene-service.ts +105 -0
  161. package/apps/desktop/cli/spatial-world-service.ts +39 -0
  162. package/apps/desktop/cli/speech-analysis-service.ts +682 -0
  163. package/apps/desktop/cli/studio-assemble.ts +58 -0
  164. package/apps/desktop/cli/studio-command.ts +60 -0
  165. package/apps/desktop/cli/studio-custody.ts +60 -0
  166. package/apps/desktop/cli/studio-encode.ts +216 -0
  167. package/apps/desktop/cli/studio-exr.ts +52 -0
  168. package/apps/desktop/cli/studio-files.ts +179 -0
  169. package/apps/desktop/cli/studio-output-validation.ts +162 -0
  170. package/apps/desktop/cli/studio-process.ts +145 -0
  171. package/apps/desktop/cli/studio-runtime.ts +77 -0
  172. package/apps/desktop/cli/studio-scaffold.ts +70 -0
  173. package/apps/desktop/cli/studio-service.ts +301 -0
  174. package/apps/desktop/cli/studio-spatial-asset.ts +248 -0
  175. package/apps/desktop/cli/studio-template-names.ts +3 -0
  176. package/apps/desktop/cli/studio-workflow.ts +19 -0
  177. package/apps/desktop/cli/time.ts +25 -0
  178. package/apps/desktop/cli/workflow-code.ts +584 -0
  179. package/apps/desktop/cli/workflow-runs.ts +576 -0
  180. package/apps/desktop/code/advanced.ts +139 -0
  181. package/apps/desktop/code/application-node-planner.ts +1288 -0
  182. package/apps/desktop/code/compiler.ts +329 -0
  183. package/apps/desktop/code/contracts.ts +13 -0
  184. package/apps/desktop/code/creative-recipes.ts +94 -0
  185. package/apps/desktop/code/define-workflow.ts +115 -0
  186. package/apps/desktop/code/editing.ts +284 -0
  187. package/apps/desktop/code/file-candidate-provenance.ts +420 -0
  188. package/apps/desktop/code/file-candidate.ts +69 -0
  189. package/apps/desktop/code/graph-builder.ts +10 -0
  190. package/apps/desktop/code/host-resource-policy.ts +55 -0
  191. package/apps/desktop/code/plan-contracts.ts +159 -0
  192. package/apps/desktop/code/planning.ts +361 -0
  193. package/apps/desktop/code/public.ts +134 -0
  194. package/apps/desktop/code/reference-projection.ts +62 -0
  195. package/apps/desktop/code/run-contracts.ts +466 -0
  196. package/apps/desktop/code/run-store.ts +2478 -0
  197. package/apps/desktop/code/runtime-identity.ts +328 -0
  198. package/apps/desktop/code/scheduler.ts +3067 -0
  199. package/apps/desktop/code/semantic-builder.ts +1436 -0
  200. package/apps/desktop/code/source-bundle.ts +1641 -0
  201. package/apps/desktop/code/source-typecheck.ts +211 -0
  202. package/apps/desktop/code/testing.ts +79 -0
  203. package/apps/desktop/code/worker-client.ts +1675 -0
  204. package/apps/desktop/code/worker-effects.ts +141 -0
  205. package/apps/desktop/code/worker-entry.ts +435 -0
  206. package/apps/desktop/code/worker-lease-guardian.ts +179 -0
  207. package/apps/desktop/code/worker-process-identity.ts +484 -0
  208. package/apps/desktop/code/worker-protocol.ts +211 -0
  209. package/apps/desktop/code/workflow-effects.ts +132 -0
  210. package/apps/desktop/contracts/analysis.ts +949 -0
  211. package/apps/desktop/contracts/edit.ts +576 -0
  212. package/apps/desktop/contracts/index.ts +9 -0
  213. package/apps/desktop/contracts/media-effects.ts +139 -0
  214. package/apps/desktop/contracts/project-render.ts +644 -0
  215. package/apps/desktop/contracts/project.ts +740 -0
  216. package/apps/desktop/contracts/recording.ts +1434 -0
  217. package/apps/desktop/contracts/render.ts +334 -0
  218. package/apps/desktop/contracts/runtime.ts +155 -0
  219. package/apps/desktop/contracts/spatial-compositor.ts +58 -0
  220. package/apps/desktop/contracts/spatial-project.ts +159 -0
  221. package/apps/desktop/contracts/spatial-world.ts +48 -0
  222. package/apps/desktop/contracts/video-effects.ts +120 -0
  223. package/apps/desktop/core/alignment-apply.ts +400 -0
  224. package/apps/desktop/core/audio-alignment.ts +466 -0
  225. package/apps/desktop/core/auto-zoom.ts +209 -0
  226. package/apps/desktop/core/canonical-json.ts +14 -0
  227. package/apps/desktop/core/emoji.ts +210 -0
  228. package/apps/desktop/core/events.ts +409 -0
  229. package/apps/desktop/core/face-framing.ts +533 -0
  230. package/apps/desktop/core/face-tracks.ts +326 -0
  231. package/apps/desktop/core/inactivity.ts +137 -0
  232. package/apps/desktop/core/index.ts +30 -0
  233. package/apps/desktop/core/intervals.ts +123 -0
  234. package/apps/desktop/core/montage.ts +236 -0
  235. package/apps/desktop/core/music-analysis.ts +518 -0
  236. package/apps/desktop/core/plan.ts +244 -0
  237. package/apps/desktop/core/project-auto-zoom.ts +75 -0
  238. package/apps/desktop/core/project-camera-spatial.ts +676 -0
  239. package/apps/desktop/core/project-camera.ts +403 -0
  240. package/apps/desktop/core/project-captions.ts +888 -0
  241. package/apps/desktop/core/project-inactivity.ts +125 -0
  242. package/apps/desktop/core/project-metadata.ts +927 -0
  243. package/apps/desktop/core/project-plan.ts +375 -0
  244. package/apps/desktop/core/project-render-plan.ts +303 -0
  245. package/apps/desktop/core/project-time.ts +469 -0
  246. package/apps/desktop/core/render-plan.ts +442 -0
  247. package/apps/desktop/core/scene-cache.ts +174 -0
  248. package/apps/desktop/core/scene-sampling.ts +448 -0
  249. package/apps/desktop/core/spatial-compositor.ts +22 -0
  250. package/apps/desktop/core/spatial-durability.ts +99 -0
  251. package/apps/desktop/core/spatial-project.ts +169 -0
  252. package/apps/desktop/core/spatial-shot-clock.ts +32 -0
  253. package/apps/desktop/core/speech-analysis.ts +333 -0
  254. package/apps/desktop/core/storage.ts +970 -0
  255. package/apps/desktop/core/time-map.ts +91 -0
  256. package/apps/desktop/core/time.ts +61 -0
  257. package/apps/desktop/core/video-effects.ts +660 -0
  258. package/apps/desktop/dist/cli/NebulaSans-Bold-26se8aek.otf +0 -0
  259. package/apps/desktop/dist/cli/NebulaSans-Bold-bcz7y08t.woff2 +0 -0
  260. package/apps/desktop/dist/cli/NebulaSans-Book-5ax05zvn.woff2 +0 -0
  261. package/apps/desktop/dist/cli/NebulaSans-Book-8cenzchw.otf +0 -0
  262. package/apps/desktop/dist/cli/main.js +5086 -0
  263. package/apps/desktop/html-overlay/catalog.ts +171 -0
  264. package/apps/desktop/html-overlay/contracts.ts +372 -0
  265. package/apps/desktop/html-overlay/execution-profile.ts +77 -0
  266. package/apps/desktop/html-overlay/index.ts +98 -0
  267. package/apps/desktop/html-overlay/libraries.ts +263 -0
  268. package/apps/desktop/html-overlay/random.ts +49 -0
  269. package/apps/desktop/html-overlay/runtime.ts +813 -0
  270. package/apps/desktop/html-overlay/scaffolds.ts +792 -0
  271. package/apps/desktop/html-overlay/spark-runtime.ts +88 -0
  272. package/apps/desktop/html-overlay/spatial-splat-runtime.ts +60 -0
  273. package/apps/desktop/html-overlay/spatial.ts +720 -0
  274. package/apps/desktop/studio/assets/README.md +39 -0
  275. package/apps/desktop/studio/assets/contracts.ts +129 -0
  276. package/apps/desktop/studio/assets/network.ts +64 -0
  277. package/apps/desktop/studio/assets/poly-haven.ts +148 -0
  278. package/apps/desktop/studio/drivers/blender_driver.py +466 -0
  279. package/apps/desktop/studio/drivers/cadquery_driver.py +176 -0
  280. package/apps/desktop/studio/education/driver.py +308 -0
  281. package/apps/desktop/studio/education/lesson.py +180 -0
  282. package/apps/desktop/studio/education/toolkit.py +249 -0
  283. package/apps/desktop/workflows/chaptered-demo.ts +48 -0
  284. package/apps/desktop/workflows/creative-iteration.ts +107 -0
  285. package/apps/desktop/workflows/creative-selection.ts +92 -0
  286. package/apps/desktop/workflows/fragments.ts +486 -0
  287. package/apps/desktop/workflows/index.ts +93 -0
  288. package/apps/desktop/workflows/polished-screen-demo.ts +128 -0
  289. package/apps/desktop/workflows/scene-direction.ts +12 -0
  290. package/apps/desktop/workflows/social-variants.ts +75 -0
  291. package/apps/desktop/workflows/talking-head-cleanup.ts +53 -0
  292. package/dist/NebulaSans-Bold-26se8aek.otf +0 -0
  293. package/dist/NebulaSans-Bold-bcz7y08t.woff2 +0 -0
  294. package/dist/NebulaSans-Book-5ax05zvn.woff2 +0 -0
  295. package/dist/NebulaSans-Book-8cenzchw.otf +0 -0
  296. package/dist/cli.js +479 -0
  297. package/dist/code/advanced.js +270 -0
  298. package/dist/code/index.js +2240 -0
  299. package/dist/generate.js +24 -0
  300. package/dist/host-resources.js +26 -0
  301. package/dist/index-42zsesc1.js +2257 -0
  302. package/dist/index-7308egqr.js +48 -0
  303. package/dist/index-8txs6fkn.js +712 -0
  304. package/dist/index-mcy8z0br.js +192 -0
  305. package/dist/index-p63wavx0.js +2029 -0
  306. package/dist/index-r7gdhmsp.js +424 -0
  307. package/dist/index-sh6xbav6.js +1219 -0
  308. package/dist/index-z1w83f81.js +4 -0
  309. package/dist/index-z7239b4h.js +2363 -0
  310. package/dist/index-zfeg9hmw.js +2013 -0
  311. package/dist/index.js +241 -0
  312. package/dist/operations.js +29 -0
  313. package/dist/skill-install-dh0ntqba.js +10 -0
  314. package/dist/vectorize/worker.js +142 -0
  315. package/dist/workflow.js +16 -0
  316. package/docs/README.md +38 -0
  317. package/docs/studio.md +215 -0
  318. package/examples/studio/blender/README.md +26 -0
  319. package/examples/studio/blender/character.py +152 -0
  320. package/examples/studio/blender/cloth.py +101 -0
  321. package/examples/studio/blender/color_chart.py +29 -0
  322. package/examples/studio/blender/fluid.py +78 -0
  323. package/examples/studio/blender/import_model.py +74 -0
  324. package/examples/studio/blender/product.py +53 -0
  325. package/examples/studio/blender/shaded_street.py +481 -0
  326. package/examples/studio/blender/studio_scene.py +147 -0
  327. package/examples/studio/cadquery/README.md +13 -0
  328. package/examples/studio/cadquery/bracket.py +36 -0
  329. package/examples/studio/cadquery/import_step.py +12 -0
  330. package/examples/studio/education/README.md +46 -0
  331. package/examples/studio/education/lesson.json +99 -0
  332. package/examples/studio/education/scene.py +7 -0
  333. package/examples/studio/hybrid-scene.ts +96 -0
  334. package/examples/studio/native-workflow.ts +14 -0
  335. package/examples/studio/vgpu/README.md +64 -0
  336. package/examples/studio/vgpu/heat-field.mjs +290 -0
  337. package/package.json +304 -0
  338. package/packages/scene/src/index.ts +1 -0
  339. package/packages/scene/src/scene.ts +215 -0
  340. package/schema/diagram.schema.json +334 -0
  341. package/skills/slopcamera/SKILL.md +45 -0
  342. package/skills/slopcamera/agents/openai.yaml +4 -0
  343. package/skills/slopcamera/references/customization.md +77 -0
  344. package/skills/slopcamera/references/diagrams.md +153 -0
  345. package/skills/slopcamera/references/directed-scenes.md +42 -0
  346. package/skills/slopcamera/references/directing-video.md +18 -0
  347. package/skills/slopcamera/references/educational-video.md +26 -0
  348. package/skills/slopcamera/references/gateway-media.md +134 -0
  349. package/skills/slopcamera/references/install.md +62 -0
  350. package/skills/slopcamera/references/native-studio.md +31 -0
  351. package/skills/slopcamera/references/reference-led-3d.md +140 -0
  352. package/skills/slopcamera/references/rubber-stamp-examples/poster-example-1.jpg +0 -0
  353. package/skills/slopcamera/references/rubber-stamp-examples/poster-example-2.jpg +0 -0
  354. package/skills/slopcamera/references/rubber-stamp-examples/stamp-style-1.png +0 -0
  355. package/skills/slopcamera/references/rubber-stamp-examples/stamp-style-2.png +0 -0
  356. package/skills/slopcamera/references/rubber-stamp-field-notes.md +144 -0
  357. package/skills/slopcamera/references/vectorization.md +29 -0
  358. package/skills/slopcamera/references/video-projects.md +187 -0
  359. package/skills/slopcamera/references/visual-communication.md +120 -0
  360. package/skills/slopcamera/references/workflows-sdk.md +45 -0
  361. package/skills/slopcamera/scripts/compose-rubber-stamp-field-note.ts +229 -0
  362. package/src/artifacts.ts +106 -0
  363. package/src/assets/fonts/nebula-sans/LICENSE.txt +96 -0
  364. package/src/assets/fonts/nebula-sans/NebulaSans-Bold.otf +0 -0
  365. package/src/assets/fonts/nebula-sans/NebulaSans-Bold.woff2 +0 -0
  366. package/src/assets/fonts/nebula-sans/NebulaSans-Book.otf +0 -0
  367. package/src/assets/fonts/nebula-sans/NebulaSans-Book.woff2 +0 -0
  368. package/src/assets/fonts/nebula-sans/PROVENANCE.md +21 -0
  369. package/src/assets.d.ts +9 -0
  370. package/src/cli.ts +601 -0
  371. package/src/cloud-errors.ts +26 -0
  372. package/src/code/advanced.ts +10 -0
  373. package/src/code/boundary.ts +21 -0
  374. package/src/code/canonical-json.ts +129 -0
  375. package/src/code/compiler.ts +953 -0
  376. package/src/code/contracts.ts +576 -0
  377. package/src/code/define-workflow.ts +334 -0
  378. package/src/code/errors.ts +41 -0
  379. package/src/code/graph-builder.ts +945 -0
  380. package/src/code/index.ts +121 -0
  381. package/src/code/json-snapshot.ts +899 -0
  382. package/src/code/json-utf8.ts +88 -0
  383. package/src/code/portable-builder.ts +136 -0
  384. package/src/code/projection.ts +378 -0
  385. package/src/code/public-operations.ts +450 -0
  386. package/src/code/runtime.ts +580 -0
  387. package/src/code/sha256.ts +36 -0
  388. package/src/config.ts +180 -0
  389. package/src/desktop.ts +253 -0
  390. package/src/fs.ts +10 -0
  391. package/src/generate.ts +704 -0
  392. package/src/host-resource-posix.ts +161 -0
  393. package/src/host-resources.ts +1749 -0
  394. package/src/icons.ts +58 -0
  395. package/src/index.ts +200 -0
  396. package/src/label-layout.ts +130 -0
  397. package/src/layout.ts +413 -0
  398. package/src/lint.ts +127 -0
  399. package/src/mcp/boundary.ts +354 -0
  400. package/src/mcp/index.ts +33 -0
  401. package/src/mcp/server.ts +272 -0
  402. package/src/mcp/tools.ts +963 -0
  403. package/src/mcp/types.ts +71 -0
  404. package/src/operations.ts +775 -0
  405. package/src/parse.ts +861 -0
  406. package/src/process-environment.ts +17 -0
  407. package/src/render.ts +437 -0
  408. package/src/skill-install.ts +49 -0
  409. package/src/spatial-scene/authoring.ts +27 -0
  410. package/src/spatial-scene/camera-track.ts +83 -0
  411. package/src/spatial-scene/contracts.ts +277 -0
  412. package/src/spatial-scene/evaluate.ts +145 -0
  413. package/src/spatial-scene/gltf.ts +558 -0
  414. package/src/spatial-scene/identity.ts +260 -0
  415. package/src/spatial-scene/index.ts +10 -0
  416. package/src/spatial-scene/inspect.ts +68 -0
  417. package/src/spatial-scene/math.ts +280 -0
  418. package/src/spatial-scene/patch.ts +131 -0
  419. package/src/spatial-scene/time.ts +53 -0
  420. package/src/studio/contracts.ts +129 -0
  421. package/src/studio/index.ts +2 -0
  422. package/src/studio/plan.ts +156 -0
  423. package/src/studio/shared.ts +41 -0
  424. package/src/theme.ts +68 -0
  425. package/src/tldr.ts +474 -0
  426. package/src/types.ts +212 -0
  427. package/src/vectorize/archive.ts +101 -0
  428. package/src/vectorize/command.ts +797 -0
  429. package/src/vectorize/index.ts +27 -0
  430. package/src/vectorize/limits.ts +61 -0
  431. package/src/vectorize/metrics.ts +351 -0
  432. package/src/vectorize/pixels.ts +323 -0
  433. package/src/vectorize/supervisor.ts +339 -0
  434. package/src/vectorize/svg.ts +188 -0
  435. package/src/vectorize/tool.ts +542 -0
  436. package/src/vectorize/types.ts +113 -0
  437. package/src/vectorize/vectorize.ts +634 -0
  438. package/src/vectorize/worker-protocol.ts +40 -0
  439. package/src/vectorize/worker.ts +209 -0
  440. package/src/version.ts +1 -0
  441. package/src/workflow.ts +409 -0
@@ -0,0 +1,2240 @@
1
+ // @bun
2
+ import {
3
+ AuthoredGraphNodeV1Schema,
4
+ AuthoredWorkflowGraphV1Schema,
5
+ CompiledWorkflowGraphSchema,
6
+ DEFAULT_GRAPH_COMPILER_LIMITS,
7
+ GRAPH_ABI,
8
+ GraphCompilerLimitsSchema,
9
+ JsonValueSchema,
10
+ MAX_WORKFLOW_RESULT_BYTES,
11
+ MAX_WORKFLOW_RESULT_DEPTH,
12
+ MAX_WORKFLOW_RESULT_VALUES,
13
+ OperationDiscoverySchema,
14
+ OperationKindSchema,
15
+ OperationPolicySchema,
16
+ PORTABLE_SLOPCAMERA_OPERATION_CONTRACTS,
17
+ PORTABLE_SLOPCAMERA_OPERATION_KINDS,
18
+ PUBLIC_SLOPCAMERA_WORKFLOW_PROJECTION,
19
+ PUBLIC_WORKFLOW_REGISTRY_PROJECTION,
20
+ PUBLIC_WORKFLOW_REGISTRY_PROJECTION_ID,
21
+ PortableWorkflowBuilder,
22
+ REQUIREMENT_ENVELOPE_VERSION,
23
+ RequirementEnvelopeSchema,
24
+ SerializedRefV1Schema,
25
+ SlopcameraDiagramCheckInputSchema,
26
+ SlopcameraDiagramCheckOutputSchema,
27
+ SlopcameraDiagramRenderInputSchema,
28
+ SlopcameraDiagramRenderOutputSchema,
29
+ SlopcameraImageGenerateInputSchema,
30
+ SlopcameraImageGenerateOutputSchema,
31
+ SlopcameraImageModelSchema,
32
+ SlopcameraImageVectorizeInputSchema,
33
+ SlopcameraImageVectorizeOutputSchema,
34
+ SlopcameraLintFindingSchema,
35
+ SlopcameraRenderArtifactsSchema,
36
+ SlopcameraVectorizeProvenanceSchema,
37
+ SlopcameraVectorizeQualityReceiptSchema,
38
+ SlopcameraVectorizeReceiptSchema,
39
+ SlopcameraWorkflowRunError,
40
+ WORKFLOW_COMPILATION_HASH_DOMAIN,
41
+ WORKFLOW_COMPILATION_VERSION,
42
+ WORKFLOW_GRAPH_HASH_DOMAIN,
43
+ WORKFLOW_GRAPH_VERSION,
44
+ WORKFLOW_NODE_RECEIPT_HASH_DOMAIN,
45
+ WORKFLOW_NODE_RECEIPT_VERSION,
46
+ WORKFLOW_REF_BRAND,
47
+ WORKFLOW_REF_VERSION,
48
+ buildWorkflow,
49
+ buildWorkflowGraph,
50
+ compileWorkflowGraph,
51
+ createGraphHash,
52
+ createPublicWorkflowRegistryProjection,
53
+ createSlopcameraCodeHost,
54
+ createWorkflowCompilationHash,
55
+ createWorkflowGraphHash,
56
+ definePortableWorkflowFragment,
57
+ defineWorkflow,
58
+ isPortableSlopcameraOperationKind,
59
+ runBuiltWorkflow,
60
+ runWorkflow,
61
+ seconds
62
+ } from "../index-42zsesc1.js";
63
+ import {
64
+ SlopcameraCodeError,
65
+ asSlopcameraCodeError,
66
+ boundedCanonicalJson,
67
+ boundedCanonicalJsonSha256,
68
+ canonicalJson,
69
+ canonicalJsonSha256,
70
+ createBoundedJsonSnapshot,
71
+ createBoundedJsonValueSnapshot,
72
+ deepFreezeJson,
73
+ sha256Hex,
74
+ slopcameraCodeErrorMessage
75
+ } from "../index-8txs6fkn.js";
76
+ import"../index-z1w83f81.js";
77
+
78
+ // src/spatial-scene/contracts.ts
79
+ import { z } from "zod";
80
+ var SPATIAL_SCENE_LIMITS = Object.freeze({
81
+ sourceBytes: 2097152,
82
+ sourceDepth: 32,
83
+ sourceValues: 200000,
84
+ entities: 4096,
85
+ assets: 128,
86
+ cameras: 64,
87
+ channels: 4096,
88
+ keysPerChannel: 4096,
89
+ durationUs: 3600000000,
90
+ patchOperations: 256
91
+ });
92
+ var SpatialDigestSchema = z.string().regex(/^[a-f0-9]{64}$/u);
93
+ var stableId = (prefix) => z.string().min(prefix.length + 1).max(128).regex(new RegExp(`^${prefix}[a-zA-Z0-9][a-zA-Z0-9_-]*$`, "u"));
94
+ var SpatialSceneIdSchema = stableId("scene_");
95
+ var SpatialEntityIdSchema = stableId("entity_");
96
+ var SpatialCameraIdSchema = stableId("camera_");
97
+ var SpatialAssetIdSchema = stableId("asset_");
98
+ var SpatialGeneratorIdSchema = stableId("generator_");
99
+ var SpatialChannelIdSchema = stableId("channel_");
100
+ var SpatialShotIdSchema = stableId("shot_");
101
+ var finiteCoordinate = z.number().finite().min(-1e6).max(1e6);
102
+ var positiveDimension = z.number().finite().min(0.000001).max(1e6);
103
+ var unit = z.number().finite().min(0).max(1);
104
+ var SpatialTimeUsSchema = z.number().int().safe().min(0).max(SPATIAL_SCENE_LIMITS.durationUs);
105
+ var SpatialVec3Schema = z.tuple([finiteCoordinate, finiteCoordinate, finiteCoordinate]);
106
+ var SpatialQuaternionSchema = z.tuple([
107
+ z.number().finite().min(-1).max(1),
108
+ z.number().finite().min(-1).max(1),
109
+ z.number().finite().min(-1).max(1),
110
+ z.number().finite().min(-1).max(1)
111
+ ]).refine((value) => Math.abs(value.reduce((sum, part) => sum + part * part, 0) - 1) <= 0.000001, "Rotation must be a unit quaternion in XYZW order.");
112
+ var SpatialTransformSchema = z.strictObject({
113
+ position: SpatialVec3Schema,
114
+ rotation: SpatialQuaternionSchema,
115
+ scale: z.tuple([positiveDimension, positiveDimension, positiveDimension])
116
+ });
117
+ var SpatialPoseSchema = z.strictObject({
118
+ position: SpatialVec3Schema,
119
+ rotation: SpatialQuaternionSchema
120
+ });
121
+ var SpatialFrameRateSchema = z.strictObject({
122
+ numerator: z.number().int().safe().min(1).max(1e6),
123
+ denominator: z.number().int().safe().min(1).max(1e6)
124
+ }).refine((rate) => rate.numerator / rate.denominator <= 1000, "Frame rate exceeds 1,000 fps.");
125
+ var dimensions = {
126
+ width: z.number().int().min(1).max(16384),
127
+ height: z.number().int().min(1).max(16384)
128
+ };
129
+ var clipping = { near: positiveDimension, far: positiveDimension };
130
+ var SpatialProjectionSchema = z.discriminatedUnion("kind", [
131
+ z.strictObject({
132
+ kind: z.literal("perspective"),
133
+ ...dimensions,
134
+ ...clipping,
135
+ fx: positiveDimension,
136
+ fy: positiveDimension,
137
+ cx: finiteCoordinate,
138
+ cy: finiteCoordinate
139
+ }),
140
+ z.strictObject({
141
+ kind: z.literal("orthographic"),
142
+ ...dimensions,
143
+ ...clipping,
144
+ left: finiteCoordinate,
145
+ right: finiteCoordinate,
146
+ top: finiteCoordinate,
147
+ bottom: finiteCoordinate
148
+ })
149
+ ]).superRefine((projection, context) => {
150
+ if (projection.far <= projection.near)
151
+ context.addIssue({ code: "custom", message: "Far clipping must exceed near clipping." });
152
+ if (projection.width * projection.height > 33554432)
153
+ context.addIssue({ code: "custom", message: "Camera exceeds the 32-megapixel limit." });
154
+ if (projection.kind === "orthographic" && (projection.right <= projection.left || projection.top <= projection.bottom)) {
155
+ context.addIssue({ code: "custom", message: "Orthographic extents must have positive width and height." });
156
+ }
157
+ });
158
+ var SpatialCameraSchema = z.strictObject({
159
+ cameraId: SpatialCameraIdSchema,
160
+ name: z.string().min(1).max(256),
161
+ pose: SpatialPoseSchema,
162
+ projection: SpatialProjectionSchema
163
+ });
164
+ var relativePath = z.string().min(1).max(1024).refine((value) => !value.startsWith("/") && !/[\\\u0000-\u001f]/u.test(value) && !/^[a-zA-Z]:/u.test(value) && value.split("/").every((part) => part !== "" && part !== "." && part !== ".."), "Asset paths must be contained root-relative paths.");
165
+ var SpatialPayloadSchema = z.strictObject({
166
+ path: relativePath,
167
+ sha256: SpatialDigestSchema,
168
+ bytes: z.number().int().safe().min(1).max(134217728)
169
+ });
170
+ var imageInterpretation = {
171
+ ...dimensions,
172
+ colorSpace: z.literal("srgb"),
173
+ alpha: z.enum(["straight", "opaque"])
174
+ };
175
+ var SpatialAssetInterpretationSchema = z.discriminatedUnion("kind", [
176
+ z.strictObject({ kind: z.literal("image"), ...imageInterpretation, mimeType: z.enum(["image/png", "image/jpeg", "image/svg+xml"]) }),
177
+ z.strictObject({ kind: z.literal("video"), ...imageInterpretation, durationUs: SpatialTimeUsSchema.refine((value) => value > 0), frameRate: SpatialFrameRateSchema }),
178
+ z.strictObject({ kind: z.literal("diagram"), schemaVersion: z.literal(1), theme: z.enum(["light", "dark"]) }),
179
+ z.strictObject({ kind: z.literal("gltf"), format: z.enum(["glb", "gltf"]), metersPerUnit: positiveDimension, sourceUp: z.enum(["x", "y", "z"]) }),
180
+ z.strictObject({ kind: z.literal("font"), format: z.enum(["otf", "woff2"]), family: z.string().min(1).max(128) }),
181
+ z.strictObject({ kind: z.literal("splat"), format: z.enum(["spz", "ply"]), metersPerUnit: positiveDimension, sourceUp: z.enum(["x", "y", "z"]) }),
182
+ z.strictObject({ kind: z.literal("metadata"), format: z.literal("json"), schema: z.enum(["slopcamera.spatial-world-import", "slopcamera.world-labs-provenance"]) })
183
+ ]);
184
+ var SpatialAssetManifestSchema = z.strictObject({
185
+ assetId: SpatialAssetIdSchema,
186
+ payload: SpatialPayloadSchema,
187
+ interpretation: SpatialAssetInterpretationSchema,
188
+ dependencies: z.array(SpatialAssetIdSchema).max(SPATIAL_SCENE_LIMITS.assets),
189
+ provenance: z.strictObject({
190
+ source: z.enum(["authored", "imported", "generated", "derived"]),
191
+ description: z.string().min(1).max(2048),
192
+ receiptSha256: SpatialDigestSchema.optional()
193
+ })
194
+ });
195
+ var color = z.string().regex(/^#[a-fA-F0-9]{6}$/u);
196
+ var SpatialMaterialSchema = z.discriminatedUnion("kind", [
197
+ z.strictObject({ kind: z.literal("unlit"), color, opacity: unit }),
198
+ z.strictObject({ kind: z.literal("standard"), color, opacity: unit, roughness: unit, metalness: unit })
199
+ ]);
200
+ var SpatialGeometrySchema = z.discriminatedUnion("kind", [
201
+ z.strictObject({ kind: z.literal("box"), size: z.tuple([positiveDimension, positiveDimension, positiveDimension]) }),
202
+ z.strictObject({ kind: z.literal("sphere"), radius: positiveDimension }),
203
+ z.strictObject({ kind: z.literal("plane"), width: positiveDimension, height: positiveDimension }),
204
+ z.strictObject({ kind: z.literal("cylinder"), radius: positiveDimension, height: positiveDimension }),
205
+ z.strictObject({
206
+ kind: z.literal("asset"),
207
+ assetId: SpatialAssetIdSchema,
208
+ nodeIndex: z.number().int().min(0).max(65535).optional(),
209
+ materialMode: z.enum(["entity", "source"]).optional(),
210
+ clip: z.strictObject({ index: z.number().int().min(0).max(255), offsetUs: SpatialTimeUsSchema, playback: z.enum(["once", "loop", "freeze"]) }).optional()
211
+ })
212
+ ]);
213
+ var SpatialOriginSchema = z.discriminatedUnion("kind", [
214
+ z.strictObject({ kind: z.literal("authored") }),
215
+ z.strictObject({ kind: z.literal("generated"), generatorId: SpatialGeneratorIdSchema, key: z.string().min(1).max(256) })
216
+ ]);
217
+ var SpatialPlacementSchema = z.discriminatedUnion("kind", [
218
+ z.strictObject({ kind: z.literal("world") }),
219
+ z.strictObject({ kind: z.literal("view"), cameraId: SpatialCameraIdSchema, units: z.enum(["pixels", "normalized"]), order: z.number().int().min(-4096).max(4096) })
220
+ ]);
221
+ var entityBase = {
222
+ entityId: SpatialEntityIdSchema,
223
+ name: z.string().min(1).max(256),
224
+ parentId: SpatialEntityIdSchema.nullable(),
225
+ transform: SpatialTransformSchema,
226
+ placement: SpatialPlacementSchema,
227
+ origin: SpatialOriginSchema,
228
+ visible: z.boolean()
229
+ };
230
+ var surfaceBase = {
231
+ assetId: SpatialAssetIdSchema,
232
+ width: positiveDimension,
233
+ height: positiveDimension,
234
+ fit: z.enum(["contain", "cover", "stretch"]),
235
+ opacity: unit
236
+ };
237
+ var SpatialEntitySchema = z.discriminatedUnion("kind", [
238
+ z.strictObject({ ...entityBase, kind: z.literal("group") }),
239
+ z.strictObject({ ...entityBase, kind: z.literal("mesh"), geometry: SpatialGeometrySchema, material: SpatialMaterialSchema }),
240
+ z.strictObject({ ...entityBase, ...surfaceBase, kind: z.literal("image") }),
241
+ z.strictObject({ ...entityBase, ...surfaceBase, kind: z.literal("diagram") }),
242
+ z.strictObject({ ...entityBase, ...surfaceBase, kind: z.literal("video"), sourceOffsetUs: SpatialTimeUsSchema, playback: z.enum(["once", "loop", "freeze"]) }),
243
+ z.strictObject({ ...entityBase, kind: z.literal("text"), text: z.string().max(16384), fontAssetId: SpatialAssetIdSchema, fontSize: positiveDimension, width: positiveDimension, color, align: z.enum(["left", "center", "right"]) }),
244
+ z.strictObject({ ...entityBase, kind: z.literal("light"), light: z.enum(["ambient", "directional", "point"]), color, intensity: z.number().finite().min(0).max(1e5) }),
245
+ z.strictObject({ ...entityBase, kind: z.literal("splat"), assetId: SpatialAssetIdSchema })
246
+ ]);
247
+ var key = (value) => z.strictObject({ timeUs: SpatialTimeUsSchema, value });
248
+ var channelBase = { channelId: SpatialChannelIdSchema, targetId: z.union([SpatialEntityIdSchema, SpatialCameraIdSchema]) };
249
+ var SpatialAnimationSchema = z.discriminatedUnion("property", [
250
+ z.strictObject({ ...channelBase, property: z.literal("position"), interpolation: z.enum(["step", "linear"]), keys: z.array(key(SpatialVec3Schema)).min(1).max(SPATIAL_SCENE_LIMITS.keysPerChannel) }),
251
+ z.strictObject({ ...channelBase, property: z.literal("rotation"), interpolation: z.enum(["step", "slerp"]), keys: z.array(key(SpatialQuaternionSchema)).min(1).max(SPATIAL_SCENE_LIMITS.keysPerChannel) }),
252
+ z.strictObject({ ...channelBase, property: z.literal("scale"), interpolation: z.enum(["step", "linear"]), keys: z.array(key(z.tuple([positiveDimension, positiveDimension, positiveDimension]))).min(1).max(SPATIAL_SCENE_LIMITS.keysPerChannel) }),
253
+ z.strictObject({ ...channelBase, property: z.literal("opacity"), interpolation: z.enum(["step", "linear"]), keys: z.array(key(unit)).min(1).max(SPATIAL_SCENE_LIMITS.keysPerChannel) })
254
+ ]);
255
+ var SpatialOverrideSchema = z.discriminatedUnion("property", [
256
+ z.strictObject({ entityId: SpatialEntityIdSchema, property: z.literal("color"), value: color }),
257
+ z.strictObject({ entityId: SpatialEntityIdSchema, property: z.literal("opacity"), value: unit }),
258
+ z.strictObject({ entityId: SpatialEntityIdSchema, property: z.literal("transform"), value: SpatialTransformSchema })
259
+ ]);
260
+ var SpatialGeneratorSchema = z.strictObject({
261
+ generatorId: SpatialGeneratorIdSchema,
262
+ sourceSha256: SpatialDigestSchema,
263
+ closureSha256: SpatialDigestSchema,
264
+ parametersSha256: SpatialDigestSchema,
265
+ seed: z.number().int().safe().min(0).max(4294967295),
266
+ outputSha256: SpatialDigestSchema,
267
+ execution: z.discriminatedUnion("kind", [
268
+ z.strictObject({ kind: z.literal("qualified"), runtimeSha256: SpatialDigestSchema }),
269
+ z.strictObject({ kind: z.literal("attempt"), attemptId: z.string().min(1).max(128), runtimeSha256: SpatialDigestSchema })
270
+ ]),
271
+ editableKeys: z.array(z.strictObject({ key: z.string().min(1).max(256), properties: z.array(z.enum(["color", "opacity", "transform"])).min(1).max(3) })).max(SPATIAL_SCENE_LIMITS.entities)
272
+ });
273
+ var SpatialSceneV1Schema = z.strictObject({
274
+ kind: z.literal("slopcamera.spatial-scene"),
275
+ schemaVersion: z.literal(1),
276
+ sceneId: SpatialSceneIdSchema,
277
+ coordinates: z.literal("right-handed-y-up-meters"),
278
+ durationUs: SpatialTimeUsSchema.refine((value) => value > 0),
279
+ entities: z.array(SpatialEntitySchema).max(SPATIAL_SCENE_LIMITS.entities),
280
+ cameras: z.array(SpatialCameraSchema).min(1).max(SPATIAL_SCENE_LIMITS.cameras),
281
+ assets: z.array(SpatialAssetManifestSchema).max(SPATIAL_SCENE_LIMITS.assets),
282
+ animations: z.array(SpatialAnimationSchema).max(SPATIAL_SCENE_LIMITS.channels),
283
+ generators: z.array(SpatialGeneratorSchema).max(128),
284
+ overrides: z.array(SpatialOverrideSchema).max(SPATIAL_SCENE_LIMITS.entities)
285
+ });
286
+ var SpatialPatchOperationSchema = z.discriminatedUnion("kind", [
287
+ z.strictObject({ kind: z.literal("add-asset"), asset: SpatialAssetManifestSchema }),
288
+ z.strictObject({ kind: z.literal("replace-asset"), asset: SpatialAssetManifestSchema }),
289
+ z.strictObject({ kind: z.literal("set-mesh-geometry"), entityId: SpatialEntityIdSchema, geometry: SpatialGeometrySchema }),
290
+ z.strictObject({ kind: z.literal("rename-entity"), entityId: SpatialEntityIdSchema, name: z.string().min(1).max(256) }),
291
+ z.strictObject({ kind: z.literal("reparent-entity"), entityId: SpatialEntityIdSchema, parentId: SpatialEntityIdSchema.nullable() }),
292
+ z.strictObject({ kind: z.literal("set-transform"), entityId: SpatialEntityIdSchema, transform: SpatialTransformSchema }),
293
+ z.strictObject({ kind: z.literal("set-color"), entityId: SpatialEntityIdSchema, color }),
294
+ z.strictObject({ kind: z.literal("set-opacity"), entityId: SpatialEntityIdSchema, opacity: unit }),
295
+ z.strictObject({ kind: z.literal("set-camera"), camera: SpatialCameraSchema }),
296
+ z.strictObject({ kind: z.literal("set-channel"), channel: SpatialAnimationSchema }),
297
+ z.strictObject({ kind: z.literal("remove-channel"), channelId: SpatialChannelIdSchema }),
298
+ z.strictObject({ kind: z.literal("add-entity"), entity: SpatialEntitySchema }),
299
+ z.strictObject({ kind: z.literal("remove-entity"), entityId: SpatialEntityIdSchema }),
300
+ z.strictObject({ kind: z.literal("set-override"), override: SpatialOverrideSchema }),
301
+ z.strictObject({ kind: z.literal("remove-override"), entityId: SpatialEntityIdSchema, property: z.enum(["color", "opacity", "transform"]) }),
302
+ z.strictObject({ kind: z.literal("replace-generator-output"), generator: SpatialGeneratorSchema, entities: z.array(SpatialEntitySchema).max(SPATIAL_SCENE_LIMITS.entities) })
303
+ ]);
304
+ var SpatialScenePatchV1Schema = z.strictObject({
305
+ kind: z.literal("slopcamera.spatial-scene-patch"),
306
+ schemaVersion: z.literal(1),
307
+ expectedSceneSha256: SpatialDigestSchema,
308
+ operations: z.array(SpatialPatchOperationSchema).min(1).max(SPATIAL_SCENE_LIMITS.patchOperations)
309
+ });
310
+ var SpatialShotV1Schema = z.strictObject({
311
+ shotId: SpatialShotIdSchema,
312
+ sceneSha256: SpatialDigestSchema,
313
+ cameraId: SpatialCameraIdSchema,
314
+ range: z.strictObject({ startUs: SpatialTimeUsSchema, endUs: SpatialTimeUsSchema }),
315
+ sceneStartUs: SpatialTimeUsSchema,
316
+ playback: z.enum(["once", "loop", "freeze"]),
317
+ overrides: z.array(SpatialOverrideSchema).max(SPATIAL_SCENE_LIMITS.entities),
318
+ cameraPoseOverride: SpatialPoseSchema.optional()
319
+ }).refine((shot) => shot.range.endUs > shot.range.startUs, "Shot range must be nonempty and half-open.");
320
+ var matrixNumber = z.number().finite().min(-1000000000000).max(1000000000000);
321
+ var SpatialMatrixSchema = z.tuple([
322
+ matrixNumber,
323
+ matrixNumber,
324
+ matrixNumber,
325
+ matrixNumber,
326
+ matrixNumber,
327
+ matrixNumber,
328
+ matrixNumber,
329
+ matrixNumber,
330
+ matrixNumber,
331
+ matrixNumber,
332
+ matrixNumber,
333
+ matrixNumber,
334
+ matrixNumber,
335
+ matrixNumber,
336
+ matrixNumber,
337
+ matrixNumber
338
+ ]);
339
+ var EvaluatedSpatialSceneSchema = z.strictObject({
340
+ kind: z.literal("slopcamera.spatial-snapshot"),
341
+ schemaVersion: z.literal(1),
342
+ sceneSha256: SpatialDigestSchema,
343
+ stateSha256: SpatialDigestSchema,
344
+ viewSha256: SpatialDigestSchema,
345
+ timeUs: SpatialTimeUsSchema,
346
+ camera: SpatialCameraSchema,
347
+ entities: z.array(z.strictObject({
348
+ entity: SpatialEntitySchema,
349
+ worldMatrix: SpatialMatrixSchema,
350
+ visible: z.boolean(),
351
+ selectionId: z.number().int().min(1).max(SPATIAL_SCENE_LIMITS.entities)
352
+ })).max(SPATIAL_SCENE_LIMITS.entities),
353
+ assets: z.array(SpatialAssetManifestSchema).max(SPATIAL_SCENE_LIMITS.assets)
354
+ });
355
+
356
+ // src/spatial-scene/identity.ts
357
+ class SpatialSceneError extends SlopcameraCodeError {
358
+ path;
359
+ constructor(code, message, path = "scene") {
360
+ super(code, message, { path });
361
+ this.name = "SpatialSceneError";
362
+ this.path = path;
363
+ }
364
+ }
365
+ var limits = { maximumDepth: SPATIAL_SCENE_LIMITS.sourceDepth, maximumValues: SPATIAL_SCENE_LIMITS.sourceValues };
366
+ function parseSpatialValue(schema, input, name) {
367
+ try {
368
+ const captured = createBoundedJsonValueSnapshot(input, SPATIAL_SCENE_LIMITS.sourceBytes, name, limits);
369
+ const parsed = schema.safeParse(captured.value);
370
+ if (!parsed.success) {
371
+ const issue = parsed.error.issues[0];
372
+ throw new SpatialSceneError("invalid-data", issue?.message ?? `Invalid ${name}.`, `${name}.${issue?.path.join(".") ?? ""}`);
373
+ }
374
+ return parsed.data;
375
+ } catch (error) {
376
+ if (error instanceof SpatialSceneError)
377
+ throw error;
378
+ throw new SpatialSceneError("invalid-data", error instanceof Error ? error.message : `Invalid ${name}.`, name);
379
+ }
380
+ }
381
+ function spatialValueSha256(input) {
382
+ return createBoundedJsonSnapshot(input, SPATIAL_SCENE_LIMITS.sourceBytes, "spatial identity", limits).sha256;
383
+ }
384
+ function spatialStateValueSha256(input) {
385
+ return createBoundedJsonSnapshot(input, SPATIAL_SCENE_LIMITS.sourceBytes * 4, "spatial state identity", {
386
+ maximumDepth: SPATIAL_SCENE_LIMITS.sourceDepth + 4,
387
+ maximumValues: SPATIAL_SCENE_LIMITS.sourceValues + SPATIAL_SCENE_LIMITS.entities * 24
388
+ }).sha256;
389
+ }
390
+ function compare(a, b) {
391
+ return a < b ? -1 : a > b ? 1 : 0;
392
+ }
393
+ function sortSpatialBy(items, id) {
394
+ return [...items].sort((a, b) => compare(id(a), id(b)));
395
+ }
396
+ function unique(items, id, path) {
397
+ const result = new Map;
398
+ for (const item of items) {
399
+ const key2 = id(item);
400
+ if (result.has(key2))
401
+ throw new SpatialSceneError("invalid-data", `Duplicate identity ${key2}.`, path);
402
+ result.set(key2, item);
403
+ }
404
+ return result;
405
+ }
406
+ function requireReference(map, id, path) {
407
+ const result = map.get(id);
408
+ if (result === undefined)
409
+ throw new SpatialSceneError("invalid-data", `Missing reference ${id}.`, path);
410
+ return result;
411
+ }
412
+ function spatialTopologicalIds(edges, name) {
413
+ const done = new Set;
414
+ const active = new Set;
415
+ const result = [];
416
+ for (const id of edges.keys()) {
417
+ const pending = [{ id, exit: false }];
418
+ while (pending.length) {
419
+ const next = pending.pop();
420
+ if (next.exit) {
421
+ active.delete(next.id);
422
+ done.add(next.id);
423
+ result.push(next.id);
424
+ continue;
425
+ }
426
+ if (done.has(next.id))
427
+ continue;
428
+ if (active.has(next.id))
429
+ throw new SpatialSceneError("invalid-data", `Cycle at ${next.id}.`, name);
430
+ const dependencies = requireReference(edges, next.id, name);
431
+ active.add(next.id);
432
+ pending.push({ id: next.id, exit: true });
433
+ for (const dependency of dependencies)
434
+ pending.push({ id: dependency, exit: false });
435
+ }
436
+ }
437
+ return Object.freeze(result);
438
+ }
439
+ function generatedSpatialEntityId(generatorId, key2) {
440
+ SpatialGeneratorIdSchema.parse(generatorId);
441
+ if (typeof key2 !== "string" || key2.length < 1 || key2.length > 256)
442
+ throw new SpatialSceneError("invalid-data", "Generator keys must contain 1\u2013256 characters.");
443
+ return `entity_${spatialValueSha256({ domain: "slopcamera.generated-entity.v1", generatorId, key: key2 })}`;
444
+ }
445
+ function normalizeEntity(entity) {
446
+ if (entity.kind === "mesh")
447
+ return { ...entity, material: { ...entity.material, color: entity.material.color.toLowerCase() } };
448
+ if (entity.kind === "text" || entity.kind === "light")
449
+ return { ...entity, color: entity.color.toLowerCase() };
450
+ return entity;
451
+ }
452
+ function normalizeAsset(asset) {
453
+ if (asset.interpretation.kind !== "video")
454
+ return asset;
455
+ const rate = asset.interpretation.frameRate;
456
+ let { numerator: divisor, denominator: remainder } = rate;
457
+ while (remainder !== 0) {
458
+ const next = divisor % remainder;
459
+ divisor = remainder;
460
+ remainder = next;
461
+ }
462
+ return { ...asset, interpretation: { ...asset.interpretation, frameRate: { numerator: rate.numerator / divisor, denominator: rate.denominator / divisor } } };
463
+ }
464
+ function spatialGeneratorOutputSha256(input) {
465
+ const entities = parseSpatialValue(SpatialEntitySchema.array().max(SPATIAL_SCENE_LIMITS.entities), input, "generator output");
466
+ unique(entities, (item) => item.entityId, "generator output");
467
+ return spatialValueSha256({ domain: "slopcamera.generator-output.v1", entities: sortSpatialBy(entities.map(normalizeEntity), (item) => item.entityId) });
468
+ }
469
+ function spatialAssetManifestSha256(input, dependencyDigests = {}) {
470
+ const asset = normalizeAsset(parseSpatialValue(SpatialAssetManifestSchema, input, "asset manifest"));
471
+ const captured = createBoundedJsonValueSnapshot(dependencyDigests, SPATIAL_SCENE_LIMITS.sourceBytes, "dependency identities", limits).value;
472
+ if (captured === null || Array.isArray(captured) || typeof captured !== "object")
473
+ throw new SpatialSceneError("invalid-data", "Dependency digests must be an object.");
474
+ unique(asset.dependencies, (item) => item, "asset.dependencies");
475
+ const dependencies = [...asset.dependencies].sort(compare).map((assetId) => {
476
+ const sha256 = captured[assetId];
477
+ if (typeof sha256 !== "string" || !/^[a-f0-9]{64}$/u.test(sha256))
478
+ throw new SpatialSceneError("invalid-data", `Missing dependency digest for ${assetId}.`);
479
+ return { assetId, sha256 };
480
+ });
481
+ return spatialValueSha256({ domain: "slopcamera.asset-manifest.v1", payload: { sha256: asset.payload.sha256, bytes: asset.payload.bytes }, interpretation: asset.interpretation, dependencies });
482
+ }
483
+ function spatialAssetClosureDigests(assets) {
484
+ const map = unique(assets, (asset) => asset.assetId, "assets");
485
+ const order = spatialTopologicalIds(new Map(assets.map((asset) => [asset.assetId, asset.dependencies])), "asset dependencies");
486
+ const digests = Object.create(null);
487
+ for (const id of order)
488
+ digests[id] = spatialAssetManifestSha256(map.get(id), digests);
489
+ return Object.freeze(digests);
490
+ }
491
+ function spatialPropertySupported(entity, property) {
492
+ if ((property === "color" || property === "opacity") && entity.kind === "mesh" && entity.geometry.kind === "asset" && entity.geometry.materialMode === "source")
493
+ return false;
494
+ if (property === "opacity")
495
+ return ["mesh", "image", "video", "diagram"].includes(entity.kind);
496
+ if (property === "color")
497
+ return ["mesh", "text", "light"].includes(entity.kind);
498
+ return true;
499
+ }
500
+ function validateSpatialOverrides(scene, overrides) {
501
+ const entities = new Map(scene.entities.map((entity) => [entity.entityId, entity]));
502
+ const generators = new Map(scene.generators.map((generator) => [generator.generatorId, generator]));
503
+ unique(overrides, (override) => `${override.entityId}:${override.property}`, "overrides");
504
+ for (const override of overrides) {
505
+ const entity = requireReference(entities, override.entityId, "overrides");
506
+ if (!spatialPropertySupported(entity, override.property))
507
+ throw new SpatialSceneError("conflict", `Entity ${entity.entityId} does not support ${override.property}.`, "overrides");
508
+ if (entity.origin.kind === "generated") {
509
+ const origin = entity.origin;
510
+ const generator = requireReference(generators, origin.generatorId, "overrides");
511
+ if (!generator.editableKeys.some((item) => item.key === origin.key && item.properties.includes(override.property))) {
512
+ throw new SpatialSceneError("conflict", `Undeclared override ${origin.key}.${override.property}.`, "overrides");
513
+ }
514
+ }
515
+ const properties = override.property === "transform" ? ["position", "rotation", "scale"] : [override.property];
516
+ if (scene.animations.some((channel) => channel.targetId === entity.entityId && properties.includes(channel.property))) {
517
+ throw new SpatialSceneError("conflict", `Animation and override both write ${entity.entityId}.${override.property}.`, "overrides");
518
+ }
519
+ }
520
+ }
521
+ function parseSpatialScene(input) {
522
+ const parsed = parseSpatialValue(SpatialSceneV1Schema, input, "scene");
523
+ const scene = {
524
+ ...parsed,
525
+ entities: sortSpatialBy(parsed.entities.map(normalizeEntity), (item) => item.entityId),
526
+ cameras: sortSpatialBy(parsed.cameras, (item) => item.cameraId),
527
+ assets: sortSpatialBy(parsed.assets.map(normalizeAsset).map((asset) => ({ ...asset, dependencies: [...asset.dependencies].sort(compare) })), (item) => item.assetId),
528
+ animations: sortSpatialBy(parsed.animations, (item) => item.channelId),
529
+ generators: sortSpatialBy(parsed.generators.map((generator) => ({ ...generator, editableKeys: sortSpatialBy(generator.editableKeys.map((item) => ({ ...item, properties: [...item.properties].sort(compare) })), (item) => item.key) })), (item) => item.generatorId),
530
+ overrides: sortSpatialBy(parsed.overrides.map((item) => item.property === "color" ? { ...item, value: item.value.toLowerCase() } : item), (item) => `${item.entityId}:${item.property}`)
531
+ };
532
+ const entities = unique(scene.entities, (entity) => entity.entityId, "entities");
533
+ const cameras = unique(scene.cameras, (camera) => camera.cameraId, "cameras");
534
+ const assets = unique(scene.assets, (asset) => asset.assetId, "assets");
535
+ const generators = unique(scene.generators, (generator) => generator.generatorId, "generators");
536
+ unique(scene.animations, (channel) => channel.channelId, "animations");
537
+ unique(scene.animations, (channel) => `${channel.targetId}:${channel.property}`, "animation writers");
538
+ let payloadBytes = 0;
539
+ for (const asset of scene.assets) {
540
+ unique(asset.dependencies, (id) => id, "asset dependencies");
541
+ payloadBytes += asset.payload.bytes;
542
+ if (payloadBytes > 268435456)
543
+ throw new SpatialSceneError("invalid-data", "Asset closure exceeds 256 MiB.", "assets");
544
+ if ((asset.interpretation.kind === "image" || asset.interpretation.kind === "video") && asset.interpretation.width * asset.interpretation.height > 33554432)
545
+ throw new SpatialSceneError("invalid-data", "Asset exceeds the 32-megapixel limit.", "assets");
546
+ if (asset.interpretation.kind === "metadata" && asset.payload.bytes > 1048576)
547
+ throw new SpatialSceneError("invalid-data", "Retained metadata exceeds one MiB.", "assets");
548
+ }
549
+ spatialTopologicalIds(new Map(scene.assets.map((asset) => [asset.assetId, asset.dependencies])), "asset dependencies");
550
+ spatialTopologicalIds(new Map(scene.entities.map((entity) => [entity.entityId, entity.parentId === null ? [] : [entity.parentId]])), "entity hierarchy");
551
+ const generatedKeys = new Set;
552
+ for (const entity of scene.entities) {
553
+ if (entity.placement.kind === "view")
554
+ requireReference(cameras, entity.placement.cameraId, "placement");
555
+ if (entity.parentId !== null) {
556
+ const parent = requireReference(entities, entity.parentId, "parent");
557
+ const a = parent.placement, b = entity.placement;
558
+ if (a.kind !== b.kind || a.kind === "view" && b.kind === "view" && (a.cameraId !== b.cameraId || a.units !== b.units))
559
+ throw new SpatialSceneError("invalid-data", "Parent and child must share their world or view coordinate domain.", "placement");
560
+ }
561
+ if (entity.origin.kind === "generated") {
562
+ const { generatorId, key: key2 } = entity.origin;
563
+ requireReference(generators, generatorId, "origin");
564
+ if (entity.entityId !== generatedSpatialEntityId(generatorId, key2))
565
+ throw new SpatialSceneError("invalid-data", "Generated entity identity must derive from its generator and stable key.", "origin");
566
+ const identity = `${generatorId}:${key2}`;
567
+ if (generatedKeys.has(identity))
568
+ throw new SpatialSceneError("invalid-data", "Duplicate generator output key.", "origin");
569
+ generatedKeys.add(identity);
570
+ }
571
+ const reference = entity.kind === "mesh" && entity.geometry.kind === "asset" ? { assetId: entity.geometry.assetId, kind: "gltf" } : entity.kind === "text" ? { assetId: entity.fontAssetId, kind: "font" } : ("assetId" in entity) ? { assetId: entity.assetId, kind: entity.kind } : undefined;
572
+ if (reference) {
573
+ const asset = requireReference(assets, reference.assetId, "entity asset");
574
+ if (asset.interpretation.kind !== reference.kind)
575
+ throw new SpatialSceneError("invalid-data", `Entity ${entity.entityId} requires a ${reference.kind} asset.`, "entity asset");
576
+ if (entity.kind === "video" && asset.interpretation.kind === "video" && entity.sourceOffsetUs >= asset.interpretation.durationUs)
577
+ throw new SpatialSceneError("invalid-data", "Video source offset must precede its duration.", "sourceOffsetUs");
578
+ }
579
+ }
580
+ for (const generator of scene.generators) {
581
+ unique(generator.editableKeys, (item) => item.key, "generator editable keys");
582
+ for (const editable of generator.editableKeys) {
583
+ unique(editable.properties, (item) => item, "generator editable properties");
584
+ const entity = entities.get(generatedSpatialEntityId(generator.generatorId, editable.key));
585
+ if (!entity)
586
+ throw new SpatialSceneError("conflict", `Orphan editable key ${editable.key}.`, "generators");
587
+ if (editable.properties.some((property) => !spatialPropertySupported(entity, property)))
588
+ throw new SpatialSceneError("invalid-data", `Editable key ${editable.key} declares an unsupported property.`, "generators");
589
+ }
590
+ const retained = scene.entities.filter((entity) => entity.origin.kind === "generated" && entity.origin.generatorId === generator.generatorId);
591
+ if (spatialGeneratorOutputSha256(retained) !== generator.outputSha256)
592
+ throw new SpatialSceneError("conflict", "Retained generator output does not match its pinned digest.", "generators");
593
+ }
594
+ for (const channel of scene.animations) {
595
+ const entity = entities.get(channel.targetId);
596
+ if (entity === undefined) {
597
+ requireReference(cameras, channel.targetId, "animation target");
598
+ if (channel.property !== "position" && channel.property !== "rotation")
599
+ throw new SpatialSceneError("invalid-data", "Camera animation supports only position and rotation.", "animations");
600
+ } else {
601
+ if (!spatialPropertySupported(entity, channel.property))
602
+ throw new SpatialSceneError("invalid-data", `Unsupported ${channel.property} animation.`, "animations");
603
+ if (entity.origin.kind === "generated") {
604
+ const origin = entity.origin;
605
+ const control = channel.property === "opacity" ? "opacity" : "transform";
606
+ if (!generators.get(origin.generatorId).editableKeys.some((item) => item.key === origin.key && item.properties.includes(control)))
607
+ throw new SpatialSceneError("conflict", `Generated animation requires declared ${control} control.`, "animations");
608
+ }
609
+ }
610
+ let previous = -1;
611
+ for (const key2 of channel.keys) {
612
+ if (key2.timeUs <= previous || key2.timeUs > scene.durationUs)
613
+ throw new SpatialSceneError("invalid-data", "Animation keys must be strictly ordered within scene duration.", "animations");
614
+ previous = key2.timeUs;
615
+ }
616
+ }
617
+ validateSpatialOverrides(scene, scene.overrides);
618
+ return deepFreezeJson(scene);
619
+ }
620
+ function spatialSceneSha256(input) {
621
+ return spatialValueSha256(parseSpatialScene(input));
622
+ }
623
+
624
+ // src/spatial-scene/evaluate.ts
625
+ import { z as z2 } from "zod";
626
+
627
+ // src/spatial-scene/math.ts
628
+ var MAX_ABS_COMPONENT = 1000000000000;
629
+ var MAX_IMAGE_DIMENSION = 1e6;
630
+ var AFFINE_TOLERANCE = 0.000000000001;
631
+ var RIGID_TOLERANCE = 0.00000001;
632
+ var MIN_RELATIVE_DETERMINANT = 0.000000000001;
633
+ function number(value, label) {
634
+ if (!Number.isFinite(value) || Math.abs(value) > MAX_ABS_COMPONENT) {
635
+ throw new RangeError(`${label} must be finite with magnitude <= ${MAX_ABS_COMPONENT}`);
636
+ }
637
+ return value;
638
+ }
639
+ function values(value, length, label) {
640
+ if (!Array.isArray(value) || value.length !== length)
641
+ throw new RangeError(`${label} must have ${length} components`);
642
+ for (let index = 0;index < length; index++)
643
+ number(value[index], `${label}[${index}]`);
644
+ }
645
+ function vec2(x, y) {
646
+ return Object.freeze([number(x, "x"), number(y, "y")]);
647
+ }
648
+ function vec3(x, y, z2) {
649
+ return Object.freeze([number(x, "x"), number(y, "y"), number(z2, "z")]);
650
+ }
651
+ function matrix(value) {
652
+ values(value, 16, "matrix");
653
+ return Object.freeze(value);
654
+ }
655
+ function affine(value) {
656
+ values(value, 16, "transform");
657
+ if (Math.abs(value[3]) > AFFINE_TOLERANCE || Math.abs(value[7]) > AFFINE_TOLERANCE || Math.abs(value[11]) > AFFINE_TOLERANCE || Math.abs(value[15] - 1) > AFFINE_TOLERANCE) {
658
+ throw new RangeError("transform must be affine with bottom row [0,0,0,1]");
659
+ }
660
+ }
661
+ var IDENTITY_MATRIX = matrix([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
662
+ function normalizeQuaternion(q) {
663
+ values(q, 4, "quaternion");
664
+ const length = Math.hypot(...q);
665
+ if (length === 0)
666
+ throw new RangeError("quaternion must be nonzero");
667
+ return Object.freeze([q[0] / length, q[1] / length, q[2] / length, q[3] / length]);
668
+ }
669
+ function composeTransform(transform) {
670
+ values(transform.position, 3, "position");
671
+ values(transform.scale, 3, "scale");
672
+ const [x, y, z2, w] = normalizeQuaternion(transform.rotation);
673
+ const [sx, sy, sz] = transform.scale;
674
+ return matrix([
675
+ (1 - 2 * (y * y + z2 * z2)) * sx,
676
+ 2 * (x * y + z2 * w) * sx,
677
+ 2 * (x * z2 - y * w) * sx,
678
+ 0,
679
+ 2 * (x * y - z2 * w) * sy,
680
+ (1 - 2 * (x * x + z2 * z2)) * sy,
681
+ 2 * (y * z2 + x * w) * sy,
682
+ 0,
683
+ 2 * (x * z2 + y * w) * sz,
684
+ 2 * (y * z2 - x * w) * sz,
685
+ (1 - 2 * (x * x + y * y)) * sz,
686
+ 0,
687
+ ...transform.position,
688
+ 1
689
+ ]);
690
+ }
691
+ function multiplyTransforms(parent, local) {
692
+ affine(parent);
693
+ affine(local);
694
+ const output = new Array(16).fill(0);
695
+ for (let col = 0;col < 4; col++) {
696
+ for (let row = 0;row < 3; row++) {
697
+ output[col * 4 + row] = parent[row] * local[col * 4] + parent[4 + row] * local[col * 4 + 1] + parent[8 + row] * local[col * 4 + 2] + (col === 3 ? parent[12 + row] : 0);
698
+ }
699
+ }
700
+ output[15] = 1;
701
+ return matrix(output);
702
+ }
703
+ function invertTransform(transform) {
704
+ affine(transform);
705
+ const scale = Math.max(...[0, 1, 2, 4, 5, 6, 8, 9, 10].map((index) => Math.abs(transform[index])));
706
+ if (scale === 0)
707
+ throw new RangeError("transform is singular");
708
+ const [a, b, c, d, e, f, g, h, i] = [0, 4, 8, 1, 5, 9, 2, 6, 10].map((index) => transform[index] / scale);
709
+ const det = a * (e * i - f * h) - b * (d * i - f * g) + c * (d * h - e * g);
710
+ if (Math.abs(det) <= MIN_RELATIVE_DETERMINANT)
711
+ throw new RangeError("transform is singular or ill-conditioned");
712
+ const factor = 1 / det / scale;
713
+ const r00 = (e * i - f * h) * factor, r01 = (c * h - b * i) * factor, r02 = (b * f - c * e) * factor;
714
+ const r10 = (f * g - d * i) * factor, r11 = (a * i - c * g) * factor, r12 = (c * d - a * f) * factor;
715
+ const r20 = (d * h - e * g) * factor, r21 = (b * g - a * h) * factor, r22 = (a * e - b * d) * factor;
716
+ const [tx, ty, tz] = [transform[12], transform[13], transform[14]];
717
+ return matrix([
718
+ r00,
719
+ r10,
720
+ r20,
721
+ 0,
722
+ r01,
723
+ r11,
724
+ r21,
725
+ 0,
726
+ r02,
727
+ r12,
728
+ r22,
729
+ 0,
730
+ -(r00 * tx + r01 * ty + r02 * tz),
731
+ -(r10 * tx + r11 * ty + r12 * tz),
732
+ -(r20 * tx + r21 * ty + r22 * tz),
733
+ 1
734
+ ]);
735
+ }
736
+ function transformPoint(transform, point) {
737
+ affine(transform);
738
+ values(point, 3, "point");
739
+ return apply(transform, point, true);
740
+ }
741
+ function transformDirection(transform, direction) {
742
+ affine(transform);
743
+ values(direction, 3, "direction");
744
+ return apply(transform, direction, false);
745
+ }
746
+ function apply(m, v, translate) {
747
+ return vec3(m[0] * v[0] + m[4] * v[1] + m[8] * v[2] + (translate ? m[12] : 0), m[1] * v[0] + m[5] * v[1] + m[9] * v[2] + (translate ? m[13] : 0), m[2] * v[0] + m[6] * v[1] + m[10] * v[2] + (translate ? m[14] : 0));
748
+ }
749
+ function slerpQuaternion(from, to, t) {
750
+ number(t, "t");
751
+ if (t < 0 || t > 1)
752
+ throw new RangeError("t must be in [0,1]");
753
+ const a = normalizeQuaternion(from);
754
+ const normalizedTo = normalizeQuaternion(to);
755
+ let dot = a.reduce((sum, value, index) => sum + value * normalizedTo[index], 0);
756
+ const b = dot < 0 ? normalizedTo.map((value) => -value) : normalizedTo;
757
+ dot = Math.min(1, Math.max(0, Math.abs(dot)));
758
+ let left = 1 - t, right = t;
759
+ if (dot < 0.9995) {
760
+ const angle = Math.acos(dot), sine = Math.sin(angle);
761
+ left = Math.sin((1 - t) * angle) / sine;
762
+ right = Math.sin(t * angle) / sine;
763
+ }
764
+ return normalizeQuaternion([
765
+ left * a[0] + right * b[0],
766
+ left * a[1] + right * b[1],
767
+ left * a[2] + right * b[2],
768
+ left * a[3] + right * b[3]
769
+ ]);
770
+ }
771
+ function projection(p) {
772
+ for (const value of [p.width, p.height]) {
773
+ if (!Number.isSafeInteger(value) || value <= 0 || value > MAX_IMAGE_DIMENSION)
774
+ throw new RangeError("image dimensions must be positive bounded integers");
775
+ }
776
+ number(p.near, "near");
777
+ number(p.far, "far");
778
+ if (p.near <= 0 || p.far <= p.near)
779
+ throw new RangeError("clipping requires 0 < near < far");
780
+ if (p.kind === "perspective") {
781
+ for (const key2 of ["fx", "fy", "cx", "cy"])
782
+ number(p[key2], key2);
783
+ if (p.fx <= 0 || p.fy <= 0)
784
+ throw new RangeError("focal lengths in pixels must be positive");
785
+ } else if (p.kind === "orthographic") {
786
+ for (const key2 of ["left", "right", "bottom", "top"])
787
+ number(p[key2], key2);
788
+ if (p.left >= p.right || p.bottom >= p.top)
789
+ throw new RangeError("orthographic extents must be ordered");
790
+ } else {
791
+ throw new RangeError("unsupported projection");
792
+ }
793
+ }
794
+ function camera(camera2) {
795
+ projection(camera2.projection);
796
+ const m = camera2.cameraToWorld;
797
+ affine(m);
798
+ const columns = [[m[0], m[1], m[2]], [m[4], m[5], m[6]], [m[8], m[9], m[10]]];
799
+ for (let i = 0;i < 3; i++) {
800
+ for (let j = i;j < 3; j++) {
801
+ const dot = columns[i].reduce((sum, value, index) => sum + value * columns[j][index], 0);
802
+ if (Math.abs(dot - (i === j ? 1 : 0)) > RIGID_TOLERANCE)
803
+ throw new RangeError("camera pose must be rigid without scale or shear");
804
+ }
805
+ }
806
+ const determinant = m[0] * (m[5] * m[10] - m[9] * m[6]) - m[4] * (m[1] * m[10] - m[9] * m[2]) + m[8] * (m[1] * m[6] - m[5] * m[2]);
807
+ if (determinant <= 0)
808
+ throw new RangeError("camera pose must preserve handedness");
809
+ }
810
+ function projectPoint(view, worldPoint) {
811
+ camera(view);
812
+ values(worldPoint, 3, "world point");
813
+ const local = apply(invertTransform(view.cameraToWorld), worldPoint, true);
814
+ const depthMeters = -local[2];
815
+ if (depthMeters <= 0)
816
+ return null;
817
+ const p = view.projection;
818
+ const pixel = p.kind === "perspective" ? vec2(p.fx * local[0] / depthMeters + p.cx, p.cy - p.fy * local[1] / depthMeters) : vec2((local[0] - p.left) / (p.right - p.left) * p.width, (p.top - local[1]) / (p.top - p.bottom) * p.height);
819
+ return Object.freeze({
820
+ pixel,
821
+ depthMeters,
822
+ insideImage: pixel[0] >= 0 && pixel[0] < p.width && pixel[1] >= 0 && pixel[1] < p.height,
823
+ insideClip: depthMeters >= p.near && depthMeters <= p.far
824
+ });
825
+ }
826
+ function unprojectPixel(view, pixel, depthMeters) {
827
+ camera(view);
828
+ values(pixel, 2, "pixel");
829
+ number(depthMeters, "depth");
830
+ if (depthMeters <= 0)
831
+ throw new RangeError("axial depth must be positive");
832
+ const p = view.projection;
833
+ const local = p.kind === "perspective" ? [(pixel[0] - p.cx) / p.fx * depthMeters, (p.cy - pixel[1]) / p.fy * depthMeters, -depthMeters] : [p.left + pixel[0] / p.width * (p.right - p.left), p.top - pixel[1] / p.height * (p.top - p.bottom), -depthMeters];
834
+ values(local, 3, "unprojected local point");
835
+ return apply(view.cameraToWorld, local, true);
836
+ }
837
+ function pixelRay(view, pixel) {
838
+ camera(view);
839
+ values(pixel, 2, "pixel");
840
+ const p = view.projection;
841
+ const x = p.kind === "perspective" ? (pixel[0] - p.cx) / p.fx : p.left + pixel[0] / p.width * (p.right - p.left);
842
+ const y = p.kind === "perspective" ? (p.cy - pixel[1]) / p.fy : p.top - pixel[1] / p.height * (p.top - p.bottom);
843
+ const length = p.kind === "perspective" ? Math.hypot(x, y, 1) : 1;
844
+ const origin = apply(view.cameraToWorld, p.kind === "perspective" ? [0, 0, 0] : vec3(x, y, 0), true);
845
+ const direction = apply(view.cameraToWorld, p.kind === "perspective" ? vec3(x / length, y / length, -1 / length) : [0, 0, -1], false);
846
+ return Object.freeze({
847
+ origin,
848
+ direction,
849
+ nearDistanceMeters: number(p.near * length, "near ray distance"),
850
+ farDistanceMeters: number(p.far * length, "far ray distance")
851
+ });
852
+ }
853
+ function transformBounds(transform, bounds) {
854
+ affine(transform);
855
+ values(bounds.min, 3, "bounds min");
856
+ values(bounds.max, 3, "bounds max");
857
+ if (bounds.min.some((value, index) => value > bounds.max[index]))
858
+ throw new RangeError("bounds must have min <= max");
859
+ const min = [Infinity, Infinity, Infinity], max = [-Infinity, -Infinity, -Infinity];
860
+ for (let mask = 0;mask < 8; mask++) {
861
+ const point = apply(transform, [mask & 1 ? bounds.max[0] : bounds.min[0], mask & 2 ? bounds.max[1] : bounds.min[1], mask & 4 ? bounds.max[2] : bounds.min[2]], true);
862
+ for (let index = 0;index < 3; index++) {
863
+ min[index] = Math.min(min[index], point[index]);
864
+ max[index] = Math.max(max[index], point[index]);
865
+ }
866
+ }
867
+ return Object.freeze({ min: vec3(min[0], min[1], min[2]), max: vec3(max[0], max[1], max[2]) });
868
+ }
869
+ function cameraMathView(camera2) {
870
+ return Object.freeze({ projection: camera2.projection, cameraToWorld: composeTransform({ ...camera2.pose, scale: [1, 1, 1] }) });
871
+ }
872
+
873
+ // src/spatial-scene/evaluate.ts
874
+ var EvaluatedOptionsSchema = z2.strictObject({
875
+ timeUs: SpatialTimeUsSchema,
876
+ cameraId: SpatialCameraIdSchema,
877
+ overrides: z2.array(SpatialOverrideSchema).max(SPATIAL_SCENE_LIMITS.entities).optional(),
878
+ cameraPoseOverride: SpatialPoseSchema.optional()
879
+ });
880
+ function mergeSpatialOverrides(sceneOverrides, shotOverrides) {
881
+ const effective = new Map(sceneOverrides.map((override) => [`${override.entityId}:${override.property}`, override]));
882
+ const seen = new Set;
883
+ for (const override of shotOverrides) {
884
+ const key2 = `${override.entityId}:${override.property}`;
885
+ if (seen.has(key2))
886
+ throw new SpatialSceneError("conflict", `Duplicate shot override ${key2}.`, "shot.overrides");
887
+ seen.add(key2);
888
+ effective.set(key2, override);
889
+ }
890
+ return deepFreezeJson(sortSpatialBy([...effective.values()], (item) => `${item.entityId}:${item.property}`));
891
+ }
892
+ function applySpatialEntityOverride(entity, override) {
893
+ if (!spatialPropertySupported(entity, override.property))
894
+ throw new SpatialSceneError("conflict", `Unsupported override ${entity.entityId}.${override.property}.`);
895
+ if (override.property === "transform")
896
+ return { ...entity, transform: override.value };
897
+ if (override.property === "color") {
898
+ const color2 = override.value.toLowerCase();
899
+ if (entity.kind === "mesh")
900
+ return { ...entity, material: { ...entity.material, color: color2 } };
901
+ if (entity.kind === "text" || entity.kind === "light")
902
+ return { ...entity, color: color2 };
903
+ }
904
+ if (override.property === "opacity") {
905
+ if (entity.kind === "mesh")
906
+ return { ...entity, material: { ...entity.material, opacity: override.value } };
907
+ if (entity.kind === "image" || entity.kind === "diagram" || entity.kind === "video")
908
+ return { ...entity, opacity: override.value };
909
+ }
910
+ throw new SpatialSceneError("conflict", `Unsupported override ${entity.entityId}.${override.property}.`);
911
+ }
912
+ function sampleChannel(channel, timeUs) {
913
+ const keys = channel.keys;
914
+ if (timeUs <= keys[0].timeUs)
915
+ return keys[0].value;
916
+ if (timeUs >= keys[keys.length - 1].timeUs)
917
+ return keys[keys.length - 1].value;
918
+ let lower = 0, upper = keys.length - 1;
919
+ while (upper - lower > 1) {
920
+ const middle = Math.floor((lower + upper) / 2);
921
+ if (keys[middle].timeUs <= timeUs)
922
+ lower = middle;
923
+ else
924
+ upper = middle;
925
+ }
926
+ const a = keys[lower], b = keys[upper];
927
+ if (channel.interpolation === "step")
928
+ return a.value;
929
+ const t = (timeUs - a.timeUs) / (b.timeUs - a.timeUs);
930
+ if (channel.property === "rotation") {
931
+ return slerpQuaternion(channel.keys[lower].value, channel.keys[upper].value, t);
932
+ }
933
+ if (typeof a.value === "number" && typeof b.value === "number")
934
+ return a.value + (b.value - a.value) * t;
935
+ const av = a.value, bv = b.value;
936
+ return av.map((value, index) => value + (bv[index] - value) * t);
937
+ }
938
+ function validateSpatialShot(sceneInput, shotInput) {
939
+ const scene = parseSpatialScene(sceneInput);
940
+ const shot = parseSpatialValue(SpatialShotV1Schema, shotInput, "shot");
941
+ if (shot.sceneSha256 !== spatialValueSha256(scene))
942
+ throw new SpatialSceneError("conflict", "Shot pins another scene revision.", "shot.sceneSha256");
943
+ if (!scene.cameras.some((camera2) => camera2.cameraId === shot.cameraId))
944
+ throw new SpatialSceneError("invalid-data", "Shot camera is absent from its scene.", "shot.cameraId");
945
+ if (shot.sceneStartUs >= scene.durationUs)
946
+ throw new SpatialSceneError("invalid-data", "Shot scene start must precede scene duration.", "shot.sceneStartUs");
947
+ if (shot.playback === "once" && shot.sceneStartUs + shot.range.endUs - shot.range.startUs > scene.durationUs)
948
+ throw new SpatialSceneError("invalid-data", "Once playback exceeds scene duration.", "shot.range");
949
+ validateSpatialOverrides(scene, mergeSpatialOverrides(scene.overrides, shot.overrides));
950
+ if (shot.cameraPoseOverride && scene.animations.some((channel) => channel.targetId === shot.cameraId))
951
+ throw new SpatialSceneError("conflict", "Camera animation and shot pose override both own camera pose.", "shot.cameraPoseOverride");
952
+ return deepFreezeJson(shot);
953
+ }
954
+ function evaluateSpatialScene(sceneInput, options) {
955
+ const scene = parseSpatialScene(sceneInput);
956
+ const capturedOptions = parseSpatialValue(EvaluatedOptionsSchema, options, "evaluation options");
957
+ const timeUs = capturedOptions.timeUs;
958
+ if (timeUs > scene.durationUs)
959
+ throw new SpatialSceneError("invalid-data", "Evaluation time exceeds scene duration.", "timeUs");
960
+ let camera2 = scene.cameras.find((item) => item.cameraId === capturedOptions.cameraId);
961
+ if (!camera2)
962
+ throw new SpatialSceneError("not-found", `Unknown camera ${capturedOptions.cameraId}.`, "cameraId");
963
+ const overrides = mergeSpatialOverrides(scene.overrides, capturedOptions.overrides ?? []);
964
+ validateSpatialOverrides(scene, overrides);
965
+ if (capturedOptions.cameraPoseOverride && scene.animations.some((channel) => channel.targetId === camera2.cameraId))
966
+ throw new SpatialSceneError("conflict", "Camera animation conflicts with camera pose override.", "cameraPoseOverride");
967
+ const entities = new Map(scene.entities.map((entity) => [entity.entityId, entity]));
968
+ for (const channel of scene.animations) {
969
+ const value = sampleChannel(channel, timeUs);
970
+ if (channel.targetId === camera2.cameraId) {
971
+ camera2 = { ...camera2, pose: { ...camera2.pose, [channel.property]: value } };
972
+ } else {
973
+ const entity = entities.get(channel.targetId);
974
+ if (!entity)
975
+ continue;
976
+ if (channel.property === "opacity")
977
+ entities.set(entity.entityId, applySpatialEntityOverride(entity, { entityId: entity.entityId, property: "opacity", value }));
978
+ else
979
+ entities.set(entity.entityId, { ...entity, transform: { ...entity.transform, [channel.property]: value } });
980
+ }
981
+ }
982
+ for (const override of overrides)
983
+ entities.set(override.entityId, applySpatialEntityOverride(entities.get(override.entityId), override));
984
+ if (capturedOptions.cameraPoseOverride)
985
+ camera2 = { ...camera2, pose: capturedOptions.cameraPoseOverride };
986
+ const matrices = new Map;
987
+ const visibility = new Map;
988
+ const order = spatialTopologicalIds(new Map(scene.entities.map((entity) => [entity.entityId, entity.parentId === null ? [] : [entity.parentId]])), "entity hierarchy");
989
+ for (const id of order) {
990
+ const entity = entities.get(id);
991
+ try {
992
+ const local = composeTransform(entity.transform);
993
+ matrices.set(id, entity.parentId === null ? local : multiplyTransforms(matrices.get(entity.parentId), local));
994
+ } catch (error) {
995
+ throw new SpatialSceneError("invalid-data", error instanceof Error ? error.message : "Invalid evaluated transform.", `entities.${id}.transform`);
996
+ }
997
+ visibility.set(id, entity.visible && (entity.parentId === null || visibility.get(entity.parentId) === true));
998
+ }
999
+ const evaluated = scene.entities.map((source, index) => ({
1000
+ entity: entities.get(source.entityId),
1001
+ worldMatrix: matrices.get(source.entityId),
1002
+ visible: visibility.get(source.entityId),
1003
+ selectionId: index + 1
1004
+ }));
1005
+ const assetDigests = spatialAssetClosureDigests(scene.assets);
1006
+ const stateSha256 = spatialStateValueSha256({ domain: "slopcamera.spatial-state.v1", timeUs, entities: evaluated, assetDigests });
1007
+ const viewSha256 = spatialValueSha256({ domain: "slopcamera.spatial-view.v1", stateSha256, camera: camera2 });
1008
+ const result = EvaluatedSpatialSceneSchema.parse({
1009
+ kind: "slopcamera.spatial-snapshot",
1010
+ schemaVersion: 1,
1011
+ sceneSha256: spatialValueSha256(scene),
1012
+ stateSha256,
1013
+ viewSha256,
1014
+ timeUs,
1015
+ camera: camera2,
1016
+ entities: evaluated.map((item) => ({ ...item, visible: item.visible && (item.entity.placement.kind === "world" || item.entity.placement.cameraId === camera2.cameraId) })),
1017
+ assets: scene.assets
1018
+ });
1019
+ return deepFreezeJson(result);
1020
+ }
1021
+
1022
+ // src/spatial-scene/patch.ts
1023
+ function diffCollection(collection, before, after, identify) {
1024
+ const old = new Map(before.map((item) => [identify(item), item]));
1025
+ const current = new Map(after.map((item) => [identify(item), item]));
1026
+ const result = [];
1027
+ for (const id of [...new Set([...old.keys(), ...current.keys()])].sort()) {
1028
+ const a = old.get(id), b = current.get(id);
1029
+ if (a === undefined || b === undefined) {
1030
+ result.push({ kind: a === undefined ? "added" : "removed", collection, id, properties: Object.keys(a ?? b).sort() });
1031
+ } else {
1032
+ const aRecord = a, bRecord = b;
1033
+ const properties = [...new Set([...Object.keys(a), ...Object.keys(b)])].sort().filter((key2) => spatialValueSha256(Object.hasOwn(a, key2) ? { value: aRecord[key2] } : {}) !== spatialValueSha256(Object.hasOwn(b, key2) ? { value: bRecord[key2] } : {}));
1034
+ if (properties.length)
1035
+ result.push({ kind: "changed", collection, id, properties });
1036
+ }
1037
+ }
1038
+ return result;
1039
+ }
1040
+ function applySpatialScenePatch(sceneInput, patchInput) {
1041
+ const original = parseSpatialScene(sceneInput);
1042
+ const patch = parseSpatialValue(SpatialScenePatchV1Schema, patchInput, "scene patch");
1043
+ if (patch.expectedSceneSha256 !== spatialValueSha256(original))
1044
+ throw new SpatialSceneError("conflict", "Scene revision changed; inspect and rebase the patch.", "expectedSceneSha256");
1045
+ const entities = new Map(original.entities.map((entity) => [entity.entityId, entity]));
1046
+ const assets = new Map(original.assets.map((asset) => [asset.assetId, asset]));
1047
+ const addressedGeometry = new Set, replacedGenerators = new Set;
1048
+ const cameras = new Map(original.cameras.map((camera2) => [camera2.cameraId, camera2]));
1049
+ const animations = new Map(original.animations.map((channel) => [channel.channelId, channel]));
1050
+ const generators = new Map(original.generators.map((generator) => [generator.generatorId, generator]));
1051
+ const overrides = new Map(original.overrides.map((override) => [`${override.entityId}:${override.property}`, override]));
1052
+ function authored(id) {
1053
+ const entity = entities.get(id);
1054
+ if (!entity)
1055
+ throw new SpatialSceneError("not-found", `Entity ${id} does not exist.`, "operations");
1056
+ if (entity.origin.kind !== "authored")
1057
+ throw new SpatialSceneError("conflict", "Generated entities can be edited only through declared overrides or retained output replacement.", "operations");
1058
+ return entity;
1059
+ }
1060
+ for (const operation of patch.operations) {
1061
+ switch (operation.kind) {
1062
+ case "add-asset":
1063
+ if (assets.has(operation.asset.assetId))
1064
+ throw new SpatialSceneError("conflict", `Asset ${operation.asset.assetId} already exists.`);
1065
+ assets.set(operation.asset.assetId, operation.asset);
1066
+ break;
1067
+ case "replace-asset":
1068
+ if (!assets.has(operation.asset.assetId))
1069
+ throw new SpatialSceneError("not-found", `Asset ${operation.asset.assetId} does not exist.`);
1070
+ assets.set(operation.asset.assetId, operation.asset);
1071
+ break;
1072
+ case "set-mesh-geometry": {
1073
+ const entity = authored(operation.entityId);
1074
+ if (entity.kind !== "mesh")
1075
+ throw new SpatialSceneError("conflict", "Geometry replacement requires an authored mesh wrapper.");
1076
+ entities.set(operation.entityId, { ...entity, geometry: operation.geometry });
1077
+ addressedGeometry.add(operation.entityId);
1078
+ break;
1079
+ }
1080
+ case "rename-entity":
1081
+ entities.set(operation.entityId, { ...authored(operation.entityId), name: operation.name });
1082
+ break;
1083
+ case "reparent-entity":
1084
+ entities.set(operation.entityId, { ...authored(operation.entityId), parentId: operation.parentId });
1085
+ break;
1086
+ case "set-transform":
1087
+ entities.set(operation.entityId, { ...authored(operation.entityId), transform: operation.transform });
1088
+ break;
1089
+ case "set-color":
1090
+ entities.set(operation.entityId, applySpatialEntityOverride(authored(operation.entityId), { entityId: operation.entityId, property: "color", value: operation.color }));
1091
+ break;
1092
+ case "set-opacity":
1093
+ entities.set(operation.entityId, applySpatialEntityOverride(authored(operation.entityId), { entityId: operation.entityId, property: "opacity", value: operation.opacity }));
1094
+ break;
1095
+ case "set-camera":
1096
+ cameras.set(operation.camera.cameraId, operation.camera);
1097
+ break;
1098
+ case "set-channel":
1099
+ animations.set(operation.channel.channelId, operation.channel);
1100
+ break;
1101
+ case "remove-channel":
1102
+ if (!animations.delete(operation.channelId))
1103
+ throw new SpatialSceneError("not-found", `Channel ${operation.channelId} does not exist.`);
1104
+ break;
1105
+ case "add-entity":
1106
+ if (entities.has(operation.entity.entityId))
1107
+ throw new SpatialSceneError("conflict", `Entity ${operation.entity.entityId} already exists.`);
1108
+ if (operation.entity.origin.kind !== "authored")
1109
+ throw new SpatialSceneError("conflict", "Add generated entities through retained generator output replacement.");
1110
+ entities.set(operation.entity.entityId, operation.entity);
1111
+ if (operation.entity.kind === "mesh")
1112
+ addressedGeometry.add(operation.entity.entityId);
1113
+ break;
1114
+ case "remove-entity":
1115
+ authored(operation.entityId);
1116
+ entities.delete(operation.entityId);
1117
+ break;
1118
+ case "set-override":
1119
+ overrides.set(`${operation.override.entityId}:${operation.override.property}`, operation.override);
1120
+ break;
1121
+ case "remove-override":
1122
+ if (!overrides.delete(`${operation.entityId}:${operation.property}`))
1123
+ throw new SpatialSceneError("not-found", "Override does not exist.");
1124
+ break;
1125
+ case "replace-generator-output": {
1126
+ const generatorId = operation.generator.generatorId;
1127
+ replacedGenerators.add(generatorId);
1128
+ for (const entity of entities.values())
1129
+ if (entity.origin.kind === "generated" && entity.origin.generatorId === generatorId)
1130
+ entities.delete(entity.entityId);
1131
+ for (const entity of operation.entities) {
1132
+ if (entity.origin.kind !== "generated" || entity.origin.generatorId !== generatorId)
1133
+ throw new SpatialSceneError("conflict", "Generator replacement must contain only its own retained output.");
1134
+ if (entities.has(entity.entityId))
1135
+ throw new SpatialSceneError("conflict", `Generator output collides with ${entity.entityId}.`);
1136
+ entities.set(entity.entityId, entity);
1137
+ }
1138
+ generators.set(generatorId, operation.generator);
1139
+ break;
1140
+ }
1141
+ }
1142
+ }
1143
+ const scene = parseSpatialScene({ ...original, assets: [...assets.values()], entities: [...entities.values()], cameras: [...cameras.values()], animations: [...animations.values()], generators: [...generators.values()], overrides: [...overrides.values()] });
1144
+ const beforeAssets = new Map(original.assets.map((asset) => [asset.assetId, asset]));
1145
+ const oldClosure = spatialAssetClosureDigests(original.assets), newClosure = spatialAssetClosureDigests(scene.assets);
1146
+ for (const entity of scene.entities) {
1147
+ const assetId = entity.kind === "mesh" && entity.geometry.kind === "asset" ? entity.geometry.assetId : entity.kind === "text" ? entity.fontAssetId : ("assetId" in entity) ? entity.assetId : undefined;
1148
+ if (assetId === undefined)
1149
+ continue;
1150
+ if (entity.origin.kind === "generated" && oldClosure[assetId] !== undefined && oldClosure[assetId] !== newClosure[assetId] && !replacedGenerators.has(entity.origin.generatorId)) {
1151
+ throw new SpatialSceneError("conflict", "Changing a generated part's asset closure requires explicit retained generator output replacement.");
1152
+ }
1153
+ if (entity.kind === "mesh" && entity.geometry.kind === "asset" && (entity.geometry.nodeIndex !== undefined || entity.geometry.clip !== undefined) && beforeAssets.has(assetId) && beforeAssets.get(assetId).payload.sha256 !== assets.get(assetId).payload.sha256 && !addressedGeometry.has(entity.entityId) && !(entity.origin.kind === "generated" && replacedGenerators.has(entity.origin.generatorId))) {
1154
+ throw new SpatialSceneError("conflict", "Replacing addressed GLB bytes requires explicit set-mesh-geometry with the new local node/clip addresses; internal correspondence is not inferred.");
1155
+ }
1156
+ }
1157
+ const diff = [
1158
+ ...diffCollection("assets", original.assets, scene.assets, (asset) => asset.assetId),
1159
+ ...diffCollection("entities", original.entities, scene.entities, (entity) => entity.entityId),
1160
+ ...diffCollection("cameras", original.cameras, scene.cameras, (camera2) => camera2.cameraId),
1161
+ ...diffCollection("animations", original.animations, scene.animations, (channel) => channel.channelId),
1162
+ ...diffCollection("generators", original.generators, scene.generators, (generator) => generator.generatorId),
1163
+ ...diffCollection("overrides", original.overrides, scene.overrides, (override) => `${override.entityId}:${override.property}`)
1164
+ ];
1165
+ return deepFreezeJson({ scene, sceneSha256: spatialValueSha256(scene), diff });
1166
+ }
1167
+
1168
+ // src/spatial-scene/inspect.ts
1169
+ function localBounds(entity) {
1170
+ let half;
1171
+ if (entity.kind === "mesh") {
1172
+ switch (entity.geometry.kind) {
1173
+ case "asset":
1174
+ return { status: "unknown", reason: "requires-asset-decoding" };
1175
+ case "box":
1176
+ half = entity.geometry.size.map((value) => value / 2);
1177
+ break;
1178
+ case "plane":
1179
+ half = [entity.geometry.width / 2, entity.geometry.height / 2, 0];
1180
+ break;
1181
+ case "sphere":
1182
+ half = [entity.geometry.radius, entity.geometry.radius, entity.geometry.radius];
1183
+ break;
1184
+ case "cylinder":
1185
+ half = [entity.geometry.radius, entity.geometry.height / 2, entity.geometry.radius];
1186
+ break;
1187
+ }
1188
+ } else if (entity.kind === "image" || entity.kind === "diagram" || entity.kind === "video")
1189
+ half = [entity.width / 2, entity.height / 2, 0];
1190
+ else
1191
+ return { status: "unknown", reason: entity.kind === "text" ? "requires-text-layout" : entity.kind === "splat" ? "requires-asset-decoding" : "no-surface" };
1192
+ return { min: [-half[0], -half[1], -half[2]], max: half };
1193
+ }
1194
+ function inspectSpatialScene(input) {
1195
+ const scene = parseSpatialScene(input);
1196
+ const snapshot = evaluateSpatialScene(scene, { timeUs: 0, cameraId: scene.cameras[0].cameraId });
1197
+ const digests = spatialAssetClosureDigests(scene.assets);
1198
+ return deepFreezeJson({
1199
+ sceneId: scene.sceneId,
1200
+ sceneSha256: spatialValueSha256(scene),
1201
+ durationUs: scene.durationUs,
1202
+ entities: snapshot.entities.map(({ entity, worldMatrix }) => {
1203
+ const origin = entity.origin;
1204
+ const declared = origin.kind === "generated" ? scene.generators.find((generator) => generator.generatorId === origin.generatorId).editableKeys.find((item) => item.key === origin.key)?.properties ?? [] : ["color", "opacity", "transform"].filter((property) => spatialPropertySupported(entity, property));
1205
+ const animatedProperties = scene.animations.filter((channel) => channel.targetId === entity.entityId).map((channel) => channel.property);
1206
+ const editableControls = declared.filter((property) => !animatedProperties.some((animated) => animated === property || property === "transform" && ["position", "rotation", "scale"].includes(animated)));
1207
+ const local = localBounds(entity);
1208
+ const bounds = "status" in local ? local : { status: "authored-enclosure", coordinateDomain: entity.placement, atTimeUs: 0, bounds: transformBounds(worldMatrix, local) };
1209
+ const assetIds = entity.kind === "mesh" && entity.geometry.kind === "asset" ? [entity.geometry.assetId] : entity.kind === "text" ? [entity.fontAssetId] : ("assetId" in entity) ? [entity.assetId] : [];
1210
+ return { entityId: entity.entityId, name: entity.name, kind: entity.kind, origin, parentId: entity.parentId, placement: entity.placement, editableControls, animatedProperties, assetIds, bounds };
1211
+ }),
1212
+ cameras: scene.cameras,
1213
+ assets: scene.assets.map((manifest) => ({ assetId: manifest.assetId, manifestSha256: digests[manifest.assetId], manifest })),
1214
+ generators: scene.generators
1215
+ });
1216
+ }
1217
+
1218
+ // src/spatial-scene/time.ts
1219
+ function gcd(a, b) {
1220
+ while (b !== 0n) {
1221
+ const remainder = a % b;
1222
+ a = b;
1223
+ b = remainder;
1224
+ }
1225
+ return a;
1226
+ }
1227
+ function rational(numerator, denominator) {
1228
+ const divisor = gcd(numerator, denominator);
1229
+ return Object.freeze({ numerator: String(numerator / divisor), denominator: String(denominator / divisor) });
1230
+ }
1231
+ function integer(input, name) {
1232
+ if (!Number.isSafeInteger(input) || input < 0)
1233
+ throw new SpatialSceneError("invalid-data", `${name} must be a nonnegative safe integer.`);
1234
+ return BigInt(input);
1235
+ }
1236
+ function duration(input) {
1237
+ const value = parseSpatialValue(SpatialTimeUsSchema, input, "durationUs");
1238
+ if (value === 0)
1239
+ throw new SpatialSceneError("invalid-data", "Output duration must be positive.");
1240
+ return BigInt(value);
1241
+ }
1242
+ function reduceSpatialFrameRate(input) {
1243
+ const rate = parseSpatialValue(SpatialFrameRateSchema, input, "frame rate");
1244
+ const divisor = Number(gcd(BigInt(rate.numerator), BigInt(rate.denominator)));
1245
+ return Object.freeze({ numerator: rate.numerator / divisor, denominator: rate.denominator / divisor });
1246
+ }
1247
+ function spatialFrameCount(durationUs, rateInput) {
1248
+ const end = duration(durationUs), rate = reduceSpatialFrameRate(rateInput);
1249
+ const numerator = end * BigInt(rate.numerator), denominator = 1000000n * BigInt(rate.denominator);
1250
+ return Number((numerator + denominator - 1n) / denominator);
1251
+ }
1252
+ function spatialFrameSample(frameIndex, durationUs, rateInput) {
1253
+ const index = integer(frameIndex, "frameIndex"), end = duration(durationUs), rate = reduceSpatialFrameRate(rateInput);
1254
+ const numerator = index * 1000000n * BigInt(rate.denominator), denominator = BigInt(rate.numerator);
1255
+ if (numerator >= end * denominator)
1256
+ throw new SpatialSceneError("invalid-data", "Frame lies outside the half-open duration before quantization.", "frameIndex");
1257
+ const timeUs = Number((2n * numerator + denominator) / (2n * denominator));
1258
+ return deepFreezeJson({ frameIndex, timeUs, exactTimeUs: rational(numerator, denominator) });
1259
+ }
1260
+ function spatialOutputDuration(durationUs, rateInput) {
1261
+ const rate = reduceSpatialFrameRate(rateInput);
1262
+ return rational(BigInt(spatialFrameCount(durationUs, rate)) * 1000000n * BigInt(rate.denominator), BigInt(rate.numerator));
1263
+ }
1264
+
1265
+ // src/spatial-scene/authoring.ts
1266
+ function createSpatialSceneStarter() {
1267
+ const transform = { position: [0, 0, 0], rotation: [0, 0, 0, 1], scale: [1, 1, 1] };
1268
+ const common = { parentId: null, placement: { kind: "world" }, origin: { kind: "authored" }, visible: true };
1269
+ return parseSpatialScene({
1270
+ kind: "slopcamera.spatial-scene",
1271
+ schemaVersion: 1,
1272
+ sceneId: "scene_starter",
1273
+ coordinates: "right-handed-y-up-meters",
1274
+ durationUs: 4000000,
1275
+ entities: [
1276
+ {
1277
+ ...common,
1278
+ entityId: "entity_product",
1279
+ name: "Product",
1280
+ kind: "mesh",
1281
+ transform,
1282
+ geometry: { kind: "box", size: [1.4, 1.4, 1.4] },
1283
+ material: { kind: "standard", color: "#3674ee", opacity: 1, roughness: 0.35, metalness: 0.1 }
1284
+ },
1285
+ {
1286
+ ...common,
1287
+ entityId: "entity_pedestal",
1288
+ name: "Pedestal",
1289
+ kind: "mesh",
1290
+ transform: { ...transform, position: [0, -0.9, 0] },
1291
+ geometry: { kind: "cylinder", radius: 1.15, height: 0.35 },
1292
+ material: { kind: "standard", color: "#cbd5e1", opacity: 1, roughness: 0.7, metalness: 0 }
1293
+ },
1294
+ {
1295
+ ...common,
1296
+ entityId: "entity_fill",
1297
+ name: "Fill",
1298
+ kind: "light",
1299
+ transform,
1300
+ light: "ambient",
1301
+ color: "#ffffff",
1302
+ intensity: 2
1303
+ },
1304
+ {
1305
+ ...common,
1306
+ entityId: "entity_key",
1307
+ name: "Key",
1308
+ kind: "light",
1309
+ transform: { ...transform, position: [3, 4, 5] },
1310
+ light: "directional",
1311
+ color: "#ffffff",
1312
+ intensity: 3
1313
+ }
1314
+ ],
1315
+ cameras: [{
1316
+ cameraId: "camera_hero",
1317
+ name: "Hero",
1318
+ pose: { position: [0, 0.3, 5], rotation: [0, 0, 0, 1] },
1319
+ projection: { kind: "perspective", width: 960, height: 540, fx: 650, fy: 650, cx: 480, cy: 270, near: 0.1, far: 100 }
1320
+ }],
1321
+ animations: [{
1322
+ channelId: "channel_turn",
1323
+ targetId: "entity_product",
1324
+ property: "rotation",
1325
+ interpolation: "slerp",
1326
+ keys: [{ timeUs: 0, value: [0, 0, 0, 1] }, { timeUs: 2000000, value: [0, 0.7071067811865475, 0, 0.7071067811865476] }, { timeUs: 4000000, value: [0, 1, 0, 0] }]
1327
+ }],
1328
+ assets: [],
1329
+ generators: [],
1330
+ overrides: []
1331
+ });
1332
+ }
1333
+
1334
+ // src/spatial-scene/gltf.ts
1335
+ import { z as z3 } from "zod";
1336
+ var SPATIAL_GLB_PROFILE = "slopcamera.glb-triangles-trs-pbr-basecolor-v1";
1337
+ var SPATIAL_GLB_LIMITS = Object.freeze({
1338
+ bytes: 134217728,
1339
+ jsonBytes: 2097152,
1340
+ jsonValues: 200000,
1341
+ jsonDepth: 32,
1342
+ nodes: 4096,
1343
+ meshes: 256,
1344
+ primitives: 256,
1345
+ verticesPerPrimitive: 65536,
1346
+ triangles: 1e5,
1347
+ decodedAccessorValues: 2000000,
1348
+ accessors: 4096,
1349
+ bufferViews: 4096,
1350
+ materials: 256,
1351
+ images: 128,
1352
+ imageBytes: 16777216,
1353
+ imageTotalBytes: 33554432,
1354
+ imagePixels: 67108864,
1355
+ clips: 256,
1356
+ channels: 4096,
1357
+ animationKeys: 4096,
1358
+ durationSeconds: 3600
1359
+ });
1360
+ var finite = z3.number().finite().min(-1e6).max(1e6);
1361
+ var index = z3.number().int().min(0).max(65535);
1362
+ var unit2 = z3.number().finite().min(0).max(1);
1363
+ var vec32 = z3.tuple([finite, finite, finite]);
1364
+ var signedUnit = z3.number().finite().min(-1).max(1);
1365
+ var quaternion = z3.tuple([signedUnit, signedUnit, signedUnit, signedUnit]);
1366
+ var metadata = { name: z3.string().max(1024).optional(), extras: z3.unknown().optional(), extensions: z3.never().optional() };
1367
+ var byteOffset = z3.number().int().min(0).max(SPATIAL_GLB_LIMITS.bytes);
1368
+ var textureInfo = z3.strictObject({ ...metadata, index, texCoord: z3.literal(0).optional() });
1369
+ var samplerSchema = z3.strictObject({
1370
+ ...metadata,
1371
+ magFilter: z3.union([z3.literal(9728), z3.literal(9729)]).optional(),
1372
+ minFilter: z3.union([z3.literal(9728), z3.literal(9729), z3.literal(9984), z3.literal(9985), z3.literal(9986), z3.literal(9987)]).optional(),
1373
+ wrapS: z3.union([z3.literal(33071), z3.literal(33648), z3.literal(10497)]).default(10497),
1374
+ wrapT: z3.union([z3.literal(33071), z3.literal(33648), z3.literal(10497)]).default(10497)
1375
+ });
1376
+ var nodeSchema = z3.strictObject({
1377
+ ...metadata,
1378
+ children: z3.array(index).max(SPATIAL_GLB_LIMITS.nodes).default([]),
1379
+ mesh: index.optional(),
1380
+ translation: vec32.optional(),
1381
+ rotation: quaternion.optional(),
1382
+ scale: vec32.optional(),
1383
+ matrix: z3.array(finite).length(16).optional()
1384
+ });
1385
+ var accessorSchema = z3.strictObject({
1386
+ ...metadata,
1387
+ bufferView: index,
1388
+ byteOffset: byteOffset.default(0),
1389
+ componentType: z3.union([z3.literal(5121), z3.literal(5123), z3.literal(5125), z3.literal(5126)]),
1390
+ normalized: z3.boolean().default(false),
1391
+ count: z3.number().int().min(1).max(SPATIAL_GLB_LIMITS.triangles * 3),
1392
+ type: z3.enum(["SCALAR", "VEC2", "VEC3", "VEC4"]),
1393
+ min: z3.array(finite).min(1).max(4).optional(),
1394
+ max: z3.array(finite).min(1).max(4).optional()
1395
+ });
1396
+ var gltfSchema = z3.strictObject({
1397
+ ...metadata,
1398
+ asset: z3.strictObject({ version: z3.literal("2.0"), minVersion: z3.literal("2.0").optional(), generator: z3.string().max(1024).optional(), copyright: z3.string().max(4096).optional(), extras: z3.unknown().optional(), extensions: z3.never().optional() }),
1399
+ extensionsUsed: z3.array(z3.never()).optional(),
1400
+ extensionsRequired: z3.array(z3.never()).optional(),
1401
+ buffers: z3.array(z3.strictObject({ ...metadata, byteLength: z3.number().int().min(1).max(SPATIAL_GLB_LIMITS.bytes) })).length(1),
1402
+ bufferViews: z3.array(z3.strictObject({ ...metadata, buffer: z3.literal(0), byteOffset: byteOffset.default(0), byteLength: z3.number().int().min(1).max(SPATIAL_GLB_LIMITS.bytes), byteStride: z3.number().int().min(4).max(252).optional(), target: z3.union([z3.literal(34962), z3.literal(34963)]).optional() })).max(SPATIAL_GLB_LIMITS.bufferViews),
1403
+ accessors: z3.array(accessorSchema).max(SPATIAL_GLB_LIMITS.accessors),
1404
+ scene: index.optional(),
1405
+ scenes: z3.array(z3.strictObject({ ...metadata, nodes: z3.array(index).min(1).max(SPATIAL_GLB_LIMITS.nodes) })).min(1).max(128),
1406
+ nodes: z3.array(nodeSchema).min(1).max(SPATIAL_GLB_LIMITS.nodes),
1407
+ meshes: z3.array(z3.strictObject({ ...metadata, primitives: z3.array(z3.strictObject({
1408
+ ...metadata,
1409
+ attributes: z3.strictObject({ POSITION: index, NORMAL: index.optional(), TEXCOORD_0: index.optional() }),
1410
+ indices: index.optional(),
1411
+ material: index.optional(),
1412
+ mode: z3.literal(4).default(4)
1413
+ })).min(1).max(SPATIAL_GLB_LIMITS.primitives) })).min(1).max(SPATIAL_GLB_LIMITS.meshes),
1414
+ materials: z3.array(z3.strictObject({
1415
+ ...metadata,
1416
+ pbrMetallicRoughness: z3.strictObject({ ...metadata, baseColorFactor: z3.tuple([unit2, unit2, unit2, unit2]).default([1, 1, 1, 1]), metallicFactor: unit2.default(1), roughnessFactor: unit2.default(1), baseColorTexture: textureInfo.optional() }).optional(),
1417
+ alphaMode: z3.enum(["OPAQUE", "MASK", "BLEND"]).default("OPAQUE"),
1418
+ alphaCutoff: unit2.default(0.5),
1419
+ doubleSided: z3.boolean().default(false),
1420
+ emissiveFactor: z3.tuple([z3.literal(0), z3.literal(0), z3.literal(0)]).optional()
1421
+ })).max(SPATIAL_GLB_LIMITS.materials).default([]),
1422
+ images: z3.array(z3.strictObject({ ...metadata, bufferView: index, mimeType: z3.enum(["image/png", "image/jpeg"]) })).max(SPATIAL_GLB_LIMITS.images).default([]),
1423
+ textures: z3.array(z3.strictObject({ ...metadata, source: index, sampler: index.optional() })).max(SPATIAL_GLB_LIMITS.images).default([]),
1424
+ samplers: z3.array(samplerSchema).max(SPATIAL_GLB_LIMITS.images).default([]),
1425
+ animations: z3.array(z3.strictObject({
1426
+ ...metadata,
1427
+ samplers: z3.array(z3.strictObject({ ...metadata, input: index, output: index, interpolation: z3.enum(["STEP", "LINEAR"]).default("LINEAR") })).min(1).max(SPATIAL_GLB_LIMITS.channels),
1428
+ channels: z3.array(z3.strictObject({ ...metadata, sampler: index, target: z3.strictObject({ ...metadata, node: index, path: z3.enum(["translation", "rotation", "scale"]) }) })).min(1).max(SPATIAL_GLB_LIMITS.channels)
1429
+ })).max(SPATIAL_GLB_LIMITS.clips).default([])
1430
+ });
1431
+ var optionsSchema = z3.strictObject({
1432
+ metersPerUnit: z3.number().finite().min(0.000001).max(1e6),
1433
+ sourceUp: z3.enum(["x", "y", "z"]),
1434
+ nodeIndex: index.optional(),
1435
+ materialMode: z3.enum(["source", "entity"]).default("entity"),
1436
+ timeUs: z3.number().int().min(0).max(3600000000),
1437
+ clip: z3.strictObject({ index, offsetUs: z3.number().int().min(0).max(3600000000), playback: z3.enum(["once", "loop", "freeze"]) }).optional()
1438
+ });
1439
+ function fail(message, path = "glb") {
1440
+ throw new SpatialSceneError("invalid-data", `${SPATIAL_GLB_PROFILE}: ${message}`, path);
1441
+ }
1442
+ function at(array, index2, path) {
1443
+ return array[index2] ?? fail(`Missing index ${index2}.`, path);
1444
+ }
1445
+ function schemaValue(schema, input, name) {
1446
+ const captured = createBoundedJsonValueSnapshot(input, SPATIAL_GLB_LIMITS.jsonBytes, name, { maximumDepth: SPATIAL_GLB_LIMITS.jsonDepth, maximumValues: SPATIAL_GLB_LIMITS.jsonValues });
1447
+ const result = schema.safeParse(captured.value);
1448
+ if (!result.success) {
1449
+ const issue = result.error.issues[0];
1450
+ return fail(`Unsupported or invalid field: ${issue?.message ?? "invalid data"}.`, `${name}.${issue?.path.join(".") ?? ""}`);
1451
+ }
1452
+ return result.data;
1453
+ }
1454
+ function normalizedRotation(value, path) {
1455
+ if (value.length !== 4 || Math.abs(Math.hypot(...value) - 1) > 0.00001)
1456
+ fail("Rotation must be a unit XYZW quaternion.", path);
1457
+ return normalizeQuaternion(value);
1458
+ }
1459
+ function safeMatrix(value, path) {
1460
+ try {
1461
+ invertTransform(value);
1462
+ } catch {
1463
+ fail("Node transform must be an invertible affine matrix.", path);
1464
+ }
1465
+ return value;
1466
+ }
1467
+ function nodeTransform(node, path) {
1468
+ if (node.matrix) {
1469
+ if (node.translation || node.rotation || node.scale)
1470
+ fail("Node matrix and TRS cannot be combined.", path);
1471
+ const matrix2 = safeMatrix(Object.freeze([...node.matrix]), path);
1472
+ const axes = [[matrix2[0], matrix2[1], matrix2[2]], [matrix2[4], matrix2[5], matrix2[6]], [matrix2[8], matrix2[9], matrix2[10]]];
1473
+ for (let a = 0;a < 3; a++)
1474
+ for (let b = a + 1;b < 3; b++) {
1475
+ const left = axes[a], right = axes[b];
1476
+ const dot = left.reduce((sum, value, i) => sum + value * right[i], 0);
1477
+ if (Math.abs(dot) > Math.hypot(...left) * Math.hypot(...right) * 0.000001)
1478
+ fail("Node matrix contains unsupported shear.", path);
1479
+ }
1480
+ return matrix2;
1481
+ }
1482
+ const rotation = normalizedRotation(node.rotation ?? [0, 0, 0, 1], path);
1483
+ return safeMatrix(composeTransform({ position: node.translation ?? [0, 0, 0], rotation, scale: node.scale ?? [1, 1, 1] }), path);
1484
+ }
1485
+ function imageHeader(bytes, mimeType) {
1486
+ const data = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
1487
+ let width = 0, height = 0;
1488
+ if (mimeType === "image/png") {
1489
+ if (bytes.length < 45 || [137, 80, 78, 71, 13, 10, 26, 10].some((value, i) => bytes[i] !== value))
1490
+ fail("Embedded image MIME does not match a PNG signature.");
1491
+ let cursor = 8, chunks = 0, hasData = false, ended = false;
1492
+ while (cursor < bytes.length) {
1493
+ if (++chunks > 65536 || cursor + 12 > bytes.length)
1494
+ fail("Malformed PNG chunk envelope.");
1495
+ const length = data.getUint32(cursor), type = data.getUint32(cursor + 4);
1496
+ if (length > bytes.length - cursor - 12)
1497
+ fail("PNG chunk exceeds its image view.");
1498
+ if (cursor === 8) {
1499
+ if (type !== 1229472850 || length !== 13)
1500
+ fail("PNG must begin with IHDR.");
1501
+ width = data.getUint32(cursor + 8);
1502
+ height = data.getUint32(cursor + 12);
1503
+ } else if (type === 1229472850)
1504
+ fail("Duplicate PNG IHDR.");
1505
+ if (type === 1633899596)
1506
+ fail("Animated PNG textures are unsupported.");
1507
+ if (type === 1229209940)
1508
+ hasData = true;
1509
+ cursor += 12 + length;
1510
+ if (type === 1229278788) {
1511
+ if (length !== 0 || cursor !== bytes.length)
1512
+ fail("PNG IEND must end its image view.");
1513
+ ended = true;
1514
+ break;
1515
+ }
1516
+ }
1517
+ if (!hasData || !ended)
1518
+ fail("PNG requires IDAT and IEND chunks.");
1519
+ } else {
1520
+ if (bytes.length < 10 || bytes[0] !== 255 || bytes[1] !== 216 || bytes.at(-2) !== 255 || bytes.at(-1) !== 217)
1521
+ fail("Embedded image MIME does not match a complete JPEG envelope.");
1522
+ let cursor = 2, segments = 0;
1523
+ while (cursor + 4 <= bytes.length) {
1524
+ if (++segments > 65536 || bytes[cursor++] !== 255)
1525
+ fail("Malformed JPEG marker.");
1526
+ while (bytes[cursor] === 255)
1527
+ cursor++;
1528
+ const marker = bytes[cursor++];
1529
+ if (marker === 218 || marker === 217)
1530
+ break;
1531
+ if (marker === 0 || marker === 216 || marker >= 208 && marker <= 215)
1532
+ fail("Unexpected JPEG standalone marker.");
1533
+ if (cursor + 2 > bytes.length)
1534
+ fail("Truncated JPEG segment.");
1535
+ const length = data.getUint16(cursor);
1536
+ if (length < 2 || cursor + length > bytes.length)
1537
+ fail("JPEG segment exceeds its image view.");
1538
+ if ([192, 193, 194].includes(marker)) {
1539
+ if (width !== 0 || length < 8 || bytes[cursor + 2] !== 8 || ![1, 3].includes(bytes[cursor + 7]))
1540
+ fail("JPEG requires one 8-bit grayscale or RGB frame.");
1541
+ height = data.getUint16(cursor + 3);
1542
+ width = data.getUint16(cursor + 5);
1543
+ } else if (marker >= 192 && marker <= 207 && ![196, 200, 204].includes(marker))
1544
+ fail("JPEG frame encoding is unsupported.");
1545
+ cursor += length;
1546
+ }
1547
+ }
1548
+ if (width < 1 || height < 1 || width > 8192 || height > 8192 || width * height > SPATIAL_GLB_LIMITS.imagePixels)
1549
+ fail("Embedded image dimensions exceed the decoded pixel profile.");
1550
+ return { width, height };
1551
+ }
1552
+ function readAccessors(document, binary) {
1553
+ let totalValues = 0;
1554
+ const data = new DataView(binary.buffer, binary.byteOffset, binary.byteLength);
1555
+ for (const [index2, view] of document.bufferViews.entries()) {
1556
+ if (view.byteOffset + view.byteLength > document.buffers[0].byteLength)
1557
+ fail("Buffer view exceeds the declared BIN payload.", `bufferViews.${index2}`);
1558
+ if (view.byteStride !== undefined && view.byteStride % 4 !== 0)
1559
+ fail("Vertex stride must be a multiple of four.", `bufferViews.${index2}`);
1560
+ }
1561
+ return Object.freeze(document.accessors.map((accessor, index2) => {
1562
+ const path = `accessors.${index2}`;
1563
+ const view = at(document.bufferViews, accessor.bufferView, path);
1564
+ const bytes = accessor.componentType === 5121 ? 1 : accessor.componentType === 5123 ? 2 : 4;
1565
+ const components = accessor.type === "SCALAR" ? 1 : Number(accessor.type.slice(3));
1566
+ const stride = view.byteStride ?? components * bytes;
1567
+ if (accessor.byteOffset % bytes !== 0 || (view.byteOffset + accessor.byteOffset) % bytes !== 0 || stride < components * bytes || stride % bytes !== 0)
1568
+ fail("Accessor alignment or stride is invalid.", path);
1569
+ if (accessor.byteOffset + (accessor.count - 1) * stride + components * bytes > view.byteLength)
1570
+ fail("Accessor exceeds its buffer view.", path);
1571
+ if (accessor.normalized && (accessor.componentType === 5125 || accessor.componentType === 5126))
1572
+ fail("Only unsigned byte/short UVs support normalized storage in this profile.", path);
1573
+ totalValues += accessor.count * components;
1574
+ if (totalValues > SPATIAL_GLB_LIMITS.decodedAccessorValues)
1575
+ fail("Decoded accessor budget exceeded.", path);
1576
+ const values2 = [];
1577
+ const low = new Array(components).fill(Infinity), high = new Array(components).fill(-Infinity);
1578
+ for (let element = 0;element < accessor.count; element++)
1579
+ for (let component = 0;component < components; component++) {
1580
+ const offset = view.byteOffset + accessor.byteOffset + element * stride + component * bytes;
1581
+ const raw = accessor.componentType === 5121 ? data.getUint8(offset) : accessor.componentType === 5123 ? data.getUint16(offset, true) : accessor.componentType === 5125 ? data.getUint32(offset, true) : data.getFloat32(offset, true);
1582
+ if (!Number.isFinite(raw))
1583
+ fail("Accessor contains nonfinite data.", path);
1584
+ low[component] = Math.min(low[component], raw);
1585
+ high[component] = Math.max(high[component], raw);
1586
+ values2.push(accessor.normalized ? raw / (accessor.componentType === 5121 ? 255 : 65535) : raw);
1587
+ }
1588
+ for (const [declared, computed, label] of [[accessor.min, low, "min"], [accessor.max, high, "max"]]) {
1589
+ if (declared !== undefined && (declared.length !== components || declared.some((value, component) => Math.abs(value - computed[component]) > 0.000001 * Math.max(1, Math.abs(value)))))
1590
+ fail(`Accessor ${label} does not match decoded values.`, path);
1591
+ }
1592
+ return Object.freeze({ source: accessor, components, values: Object.freeze(values2) });
1593
+ }));
1594
+ }
1595
+ function validateViewRoles(document) {
1596
+ const roles = new Map, vertexAccessors = new Map;
1597
+ const assign = (viewIndex, role) => {
1598
+ const previous = roles.get(viewIndex);
1599
+ if (previous !== undefined && previous !== role)
1600
+ fail(`Buffer view mixes ${previous} and ${role} data.`, `bufferViews.${viewIndex}`);
1601
+ roles.set(viewIndex, role);
1602
+ };
1603
+ const accessor = (index2, role) => {
1604
+ const source = at(document.accessors, index2, "accessors");
1605
+ assign(source.bufferView, role);
1606
+ if (role === "vertex") {
1607
+ const ids = vertexAccessors.get(source.bufferView) ?? new Set;
1608
+ ids.add(index2);
1609
+ vertexAccessors.set(source.bufferView, ids);
1610
+ }
1611
+ };
1612
+ for (const mesh of document.meshes)
1613
+ for (const primitive of mesh.primitives) {
1614
+ for (const index2 of Object.values(primitive.attributes))
1615
+ if (index2 !== undefined)
1616
+ accessor(index2, "vertex");
1617
+ if (primitive.indices !== undefined)
1618
+ accessor(primitive.indices, "index");
1619
+ }
1620
+ for (const clip of document.animations)
1621
+ for (const sampler of clip.samplers) {
1622
+ accessor(sampler.input, "animation");
1623
+ accessor(sampler.output, "animation");
1624
+ }
1625
+ for (const image of document.images)
1626
+ assign(image.bufferView, "image");
1627
+ for (const [viewIndex, ids] of vertexAccessors)
1628
+ if (ids.size > 1 && at(document.bufferViews, viewIndex, "bufferViews").byteStride === undefined)
1629
+ fail("Shared vertex-attribute views require an explicit stride.", `bufferViews.${viewIndex}`);
1630
+ }
1631
+ function cleanSampler(sampler) {
1632
+ return Object.freeze({
1633
+ wrapS: sampler?.wrapS ?? 10497,
1634
+ wrapT: sampler?.wrapT ?? 10497,
1635
+ ...sampler?.magFilter === undefined ? {} : { magFilter: sampler.magFilter },
1636
+ ...sampler?.minFilter === undefined ? {} : { minFilter: sampler.minFilter }
1637
+ });
1638
+ }
1639
+ function materials(document) {
1640
+ for (const texture of document.textures) {
1641
+ at(document.images, texture.source, "textures.source");
1642
+ if (texture.sampler !== undefined)
1643
+ at(document.samplers, texture.sampler, "textures.sampler");
1644
+ }
1645
+ return deepFreezeJson(document.materials.map((material) => {
1646
+ const pbr = material.pbrMetallicRoughness;
1647
+ const texture = pbr?.baseColorTexture === undefined ? undefined : at(document.textures, pbr.baseColorTexture.index, "baseColorTexture");
1648
+ return {
1649
+ baseColorLinear: pbr?.baseColorFactor ?? [1, 1, 1, 1],
1650
+ metalness: pbr?.metallicFactor ?? 1,
1651
+ roughness: pbr?.roughnessFactor ?? 1,
1652
+ alphaMode: material.alphaMode,
1653
+ alphaCutoff: material.alphaCutoff,
1654
+ doubleSided: material.doubleSided,
1655
+ ...texture === undefined ? {} : { baseColorTexture: { imageIndex: texture.source, sampler: cleanSampler(texture.sampler === undefined ? undefined : document.samplers[texture.sampler]) } }
1656
+ };
1657
+ }));
1658
+ }
1659
+ function readMeshes(document, accessors) {
1660
+ const sources = materials(document);
1661
+ const defaultMaterial = { baseColorLinear: [1, 1, 1, 1], metalness: 1, roughness: 1, alphaMode: "OPAQUE", alphaCutoff: 0.5, doubleSided: false };
1662
+ let primitiveCount = 0, triangles = 0;
1663
+ return deepFreezeJson(document.meshes.map((mesh, meshIndex) => mesh.primitives.map((primitive, primitiveIndex) => {
1664
+ const path = `meshes.${meshIndex}.primitives.${primitiveIndex}`;
1665
+ if (++primitiveCount > SPATIAL_GLB_LIMITS.primitives)
1666
+ fail("Source primitive count exceeds this profile.", path);
1667
+ const positions = at(accessors, primitive.attributes.POSITION, path);
1668
+ const normals = primitive.attributes.NORMAL === undefined ? undefined : at(accessors, primitive.attributes.NORMAL, path);
1669
+ const uvs = primitive.attributes.TEXCOORD_0 === undefined ? undefined : at(accessors, primitive.attributes.TEXCOORD_0, path);
1670
+ const indices = primitive.indices === undefined ? undefined : at(accessors, primitive.indices, path);
1671
+ if (positions.source.type !== "VEC3" || positions.source.componentType !== 5126 || positions.source.normalized || positions.source.min === undefined || positions.source.max === undefined || positions.source.count > SPATIAL_GLB_LIMITS.verticesPerPrimitive)
1672
+ fail("POSITION requires bounded float32 VEC3 with declared min/max.", path);
1673
+ for (const attribute of [positions, normals, uvs])
1674
+ if (attribute !== undefined) {
1675
+ const view = document.bufferViews[attribute.source.bufferView];
1676
+ if ((view.byteOffset + attribute.source.byteOffset) % 4 !== 0 || attribute.source.byteOffset % 4 !== 0 || view.target !== undefined && view.target !== 34962)
1677
+ fail("Vertex attributes require four-byte alignment and ARRAY_BUFFER target.", path);
1678
+ const componentBytes = attribute.source.componentType === 5121 ? 1 : attribute.source.componentType === 5123 ? 2 : 4;
1679
+ if ((view.byteStride ?? attribute.components * componentBytes) % 4 !== 0)
1680
+ fail("Every vertex attribute element must remain four-byte aligned.", path);
1681
+ if (attribute.source.count !== positions.source.count || attribute.values.some((value) => Math.abs(value) > 1e6))
1682
+ fail("Vertex attributes require matching counts and bounded coordinates.", path);
1683
+ }
1684
+ if (normals) {
1685
+ if (normals.source.type !== "VEC3" || normals.source.componentType !== 5126 || normals.source.normalized)
1686
+ fail("NORMAL requires float32 VEC3.", path);
1687
+ for (let i = 0;i < normals.values.length; i += 3)
1688
+ if (Math.abs(Math.hypot(...normals.values.slice(i, i + 3)) - 1) > 0.0001)
1689
+ fail("Normals must be unit vectors.", path);
1690
+ }
1691
+ if (uvs && (uvs.source.type !== "VEC2" || uvs.source.componentType !== 5126 && !([5121, 5123].includes(uvs.source.componentType) && uvs.source.normalized)))
1692
+ fail("TEXCOORD_0 requires float32 or normalized unsigned byte/short VEC2.", path);
1693
+ if (indices) {
1694
+ const view = document.bufferViews[indices.source.bufferView];
1695
+ if (indices.source.type !== "SCALAR" || ![5121, 5123, 5125].includes(indices.source.componentType) || indices.source.normalized || view.byteStride !== undefined || view.target !== undefined && view.target !== 34963)
1696
+ fail("Triangle indices require tightly packed unsigned scalar storage.", path);
1697
+ const restart = indices.source.componentType === 5121 ? 255 : indices.source.componentType === 5123 ? 65535 : 4294967295;
1698
+ if (indices.values.some((value) => value >= positions.source.count || value === restart))
1699
+ fail("Triangle index is out of range or reserved for primitive restart.", path);
1700
+ }
1701
+ const vertices = indices?.values.length ?? positions.source.count;
1702
+ if (vertices % 3 !== 0)
1703
+ fail("TRIANGLES require complete index or vertex triples.", path);
1704
+ triangles += vertices / 3;
1705
+ if (triangles > SPATIAL_GLB_LIMITS.triangles)
1706
+ fail("Source triangle budget exceeded.", path);
1707
+ const material = primitive.material === undefined ? defaultMaterial : at(sources, primitive.material, path);
1708
+ if (material.baseColorTexture && uvs === undefined)
1709
+ fail("Base-color textures require TEXCOORD_0.", path);
1710
+ return { positions: positions.values, ...normals === undefined ? {} : { normals: normals.values }, ...uvs === undefined ? {} : { uvs: uvs.values }, ...indices === undefined ? {} : { indices: indices.values }, material };
1711
+ })));
1712
+ }
1713
+ function hierarchy(document) {
1714
+ const parents = document.nodes.map(() => null);
1715
+ for (const [index2, node] of document.nodes.entries()) {
1716
+ nodeTransform(node, `nodes.${index2}`);
1717
+ if (node.mesh !== undefined)
1718
+ at(document.meshes, node.mesh, `nodes.${index2}.mesh`);
1719
+ const seen2 = new Set;
1720
+ for (const child of node.children) {
1721
+ at(document.nodes, child, `nodes.${index2}.children`);
1722
+ if (seen2.has(child) || parents[child] !== null)
1723
+ fail("Nodes may have only one parent and unique child references.", `nodes.${index2}`);
1724
+ seen2.add(child);
1725
+ parents[child] = index2;
1726
+ }
1727
+ }
1728
+ const roots = document.nodes.map((_, index2) => index2).filter((index2) => parents[index2] === null);
1729
+ const order = [], seen = new Set, pending = [...roots];
1730
+ while (pending.length) {
1731
+ const index2 = pending.pop();
1732
+ if (seen.has(index2))
1733
+ fail("Node hierarchy contains a cycle.");
1734
+ seen.add(index2);
1735
+ order.push(index2);
1736
+ pending.push(...document.nodes[index2].children);
1737
+ }
1738
+ if (seen.size !== document.nodes.length)
1739
+ fail("Node hierarchy contains a cycle.");
1740
+ for (const scene2 of document.scenes) {
1741
+ const unique2 = new Set;
1742
+ for (const index2 of scene2.nodes) {
1743
+ at(document.nodes, index2, "scenes.nodes");
1744
+ if (parents[index2] !== null || unique2.has(index2))
1745
+ fail("Scene roots must be unique nodes without parents.");
1746
+ unique2.add(index2);
1747
+ }
1748
+ }
1749
+ if (document.scene === undefined && document.scenes.length !== 1)
1750
+ fail("Multiple scenes require an explicit glTF default scene.");
1751
+ const scene = at(document.scenes, document.scene ?? 0, "scene");
1752
+ const reachable = new Set, visit = [...scene.nodes];
1753
+ while (visit.length) {
1754
+ const index2 = visit.pop();
1755
+ reachable.add(index2);
1756
+ visit.push(...document.nodes[index2].children);
1757
+ }
1758
+ return { parents: Object.freeze(parents), order: Object.freeze(order), reachable };
1759
+ }
1760
+ function animationDurations(document, accessors) {
1761
+ let totalChannels = 0;
1762
+ return Object.freeze(document.animations.map((clip, clipIndex) => {
1763
+ totalChannels += clip.channels.length;
1764
+ if (totalChannels > SPATIAL_GLB_LIMITS.channels)
1765
+ fail("Animation channel budget exceeded.");
1766
+ let duration2 = 0;
1767
+ for (const sampler of clip.samplers) {
1768
+ const input = at(accessors, sampler.input, "animation input"), output = at(accessors, sampler.output, "animation output");
1769
+ if (input.source.type !== "SCALAR" || input.source.componentType !== 5126 || input.source.normalized || input.source.count > SPATIAL_GLB_LIMITS.animationKeys || input.source.min === undefined || input.source.max === undefined)
1770
+ fail("Animation input requires bounded float32 scalar seconds with min/max.");
1771
+ if (output.source.componentType !== 5126 || output.source.normalized || output.source.count !== input.source.count)
1772
+ fail("Animation output must be float32 with matching key count.");
1773
+ for (const accessor of [input, output]) {
1774
+ const view = document.bufferViews[accessor.source.bufferView];
1775
+ if (view.byteStride !== undefined || view.target !== undefined)
1776
+ fail("Animation data must be tightly packed without a GPU buffer target.");
1777
+ }
1778
+ let previous = -1;
1779
+ for (const time of input.values) {
1780
+ if (time < 0 || time <= previous || time > SPATIAL_GLB_LIMITS.durationSeconds)
1781
+ fail("Animation times must be strictly ordered nonnegative seconds within the duration profile.");
1782
+ previous = time;
1783
+ }
1784
+ duration2 = Math.max(duration2, previous);
1785
+ }
1786
+ const writers = new Set;
1787
+ for (const channel of clip.channels) {
1788
+ const node = at(document.nodes, channel.target.node, "animation target");
1789
+ if (node.matrix)
1790
+ fail("Animated nodes must use TRS, never a matrix.");
1791
+ const key2 = `${channel.target.node}:${channel.target.path}`;
1792
+ if (writers.has(key2))
1793
+ fail("Animation has multiple writers for one node property.", `animations.${clipIndex}`);
1794
+ writers.add(key2);
1795
+ const sampler = at(clip.samplers, channel.sampler, "animation sampler");
1796
+ const output = accessors[sampler.output];
1797
+ if (output.source.type !== (channel.target.path === "rotation" ? "VEC4" : "VEC3"))
1798
+ fail("Animation output arity does not match its target property.");
1799
+ if (channel.target.path === "rotation")
1800
+ for (let i = 0;i < output.values.length; i += 4)
1801
+ normalizedRotation(output.values.slice(i, i + 4), "animation rotation");
1802
+ else if (output.values.some((value) => Math.abs(value) > 1e6 || channel.target.path === "scale" && value === 0))
1803
+ fail("Animation transform values are unbounded or singular.");
1804
+ if (channel.target.path === "scale" && sampler.interpolation === "LINEAR") {
1805
+ for (let i = 3;i < output.values.length; i++)
1806
+ if (Math.sign(output.values[i]) !== Math.sign(output.values[i - 3]))
1807
+ fail("Linear scale animation crosses a singular transform.");
1808
+ }
1809
+ }
1810
+ return duration2;
1811
+ }));
1812
+ }
1813
+
1814
+ class SpatialGlbModel {
1815
+ profile = SPATIAL_GLB_PROFILE;
1816
+ nodeCount;
1817
+ clipDurationsSeconds;
1818
+ #state;
1819
+ constructor(state) {
1820
+ this.#state = state;
1821
+ this.nodeCount = state.document.nodes.length;
1822
+ this.clipDurationsSeconds = state.clipDurations;
1823
+ Object.freeze(this);
1824
+ }
1825
+ static parse(input) {
1826
+ if (!(input instanceof Uint8Array) || input.byteLength < 28 || input.byteLength > SPATIAL_GLB_LIMITS.bytes || input.buffer instanceof SharedArrayBuffer)
1827
+ fail("Expected bounded, non-shared GLB bytes.");
1828
+ const bytes = Uint8Array.from(input);
1829
+ const header = new DataView(bytes.buffer);
1830
+ if (header.getUint32(0, true) !== 1179937895 || header.getUint32(4, true) !== 2 || header.getUint32(8, true) !== bytes.length)
1831
+ fail("Invalid GLB 2.0 header or total length.");
1832
+ const jsonLength = header.getUint32(12, true);
1833
+ if (header.getUint32(16, true) !== 1313821514 || jsonLength % 4 !== 0 || jsonLength < 4 || jsonLength > SPATIAL_GLB_LIMITS.jsonBytes || 20 + jsonLength + 8 > bytes.length)
1834
+ fail("Expected bounded first JSON chunk and following BIN chunk.");
1835
+ const binHeader = 20 + jsonLength, binLength = header.getUint32(binHeader, true);
1836
+ if (header.getUint32(binHeader + 4, true) !== 5130562 || binLength % 4 !== 0 || binHeader + 8 + binLength !== bytes.length)
1837
+ fail("Expected exactly one BIN chunk and no trailing chunks.");
1838
+ let json;
1839
+ try {
1840
+ json = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes.subarray(20, binHeader)));
1841
+ } catch {
1842
+ return fail("GLB JSON must be valid UTF-8 JSON.");
1843
+ }
1844
+ const document = schemaValue(gltfSchema, json, "gltf");
1845
+ const payloadLength = document.buffers[0].byteLength;
1846
+ if (payloadLength > binLength || binLength - payloadLength > 3)
1847
+ fail("Declared BIN length does not match its padding.");
1848
+ const binary = bytes.subarray(binHeader + 8);
1849
+ if (binary.subarray(payloadLength).some((byte) => byte !== 0))
1850
+ fail("BIN padding must contain zero bytes.");
1851
+ validateViewRoles(document);
1852
+ const accessors = readAccessors(document, binary);
1853
+ const meshPrimitives = readMeshes(document, accessors);
1854
+ const graph = hierarchy(document);
1855
+ const clipDurations = animationDurations(document, accessors);
1856
+ let totalImageBytes = 0, totalPixels = 0;
1857
+ const images = document.images.map((image, imageIndex) => {
1858
+ const view = at(document.bufferViews, image.bufferView, "images.bufferView");
1859
+ if (view.byteStride !== undefined || view.target !== undefined || view.byteLength > SPATIAL_GLB_LIMITS.imageBytes)
1860
+ fail("Embedded images require bounded untargeted byte views.");
1861
+ totalImageBytes += view.byteLength;
1862
+ if (totalImageBytes > SPATIAL_GLB_LIMITS.imageTotalBytes)
1863
+ fail("Embedded image byte budget exceeded.");
1864
+ const imageBytes = binary.slice(view.byteOffset, view.byteOffset + view.byteLength);
1865
+ const dimensions2 = imageHeader(imageBytes, image.mimeType);
1866
+ totalPixels += dimensions2.width * dimensions2.height;
1867
+ if (totalPixels > SPATIAL_GLB_LIMITS.imagePixels)
1868
+ fail("Embedded decoded image pixel budget exceeded.");
1869
+ return Object.freeze({ imageIndex, mimeType: image.mimeType, ...dimensions2, bytes: imageBytes });
1870
+ });
1871
+ return new SpatialGlbModel({ document: deepFreezeJson(document), accessors, meshPrimitives, images: Object.freeze(images), ...graph, clipDurations });
1872
+ }
1873
+ evaluate(input) {
1874
+ const options = schemaValue(optionsSchema, input, "glb evaluation");
1875
+ const state = this.#state, document = state.document;
1876
+ if (options.nodeIndex !== undefined && !state.reachable.has(options.nodeIndex))
1877
+ fail("Selected node is absent from the default scene.", "nodeIndex");
1878
+ let sourceTimeSeconds = null;
1879
+ const animated = new Map;
1880
+ if (options.clip !== undefined) {
1881
+ const clip = at(document.animations, options.clip.index, "clip.index");
1882
+ const duration2 = state.clipDurations[options.clip.index];
1883
+ const requested = (options.timeUs + options.clip.offsetUs) / 1e6;
1884
+ if (options.clip.offsetUs / 1e6 > duration2)
1885
+ fail("Clip source offset exceeds its duration.");
1886
+ if (options.clip.playback === "once" && requested > duration2)
1887
+ fail("Once clip playback exceeds its duration.");
1888
+ sourceTimeSeconds = options.clip.playback === "loop" ? duration2 === 0 ? 0 : requested % duration2 : Math.min(requested, duration2);
1889
+ for (const channel of clip.channels) {
1890
+ const sampler = clip.samplers[channel.sampler], times = state.accessors[sampler.input].values, output = state.accessors[sampler.output];
1891
+ let lower = 0, upper = times.length - 1;
1892
+ if (sourceTimeSeconds <= times[0])
1893
+ upper = 0;
1894
+ else if (sourceTimeSeconds >= times[upper])
1895
+ lower = upper;
1896
+ else
1897
+ while (upper - lower > 1) {
1898
+ const middle = Math.floor((lower + upper) / 2);
1899
+ if (times[middle] <= sourceTimeSeconds)
1900
+ lower = middle;
1901
+ else
1902
+ upper = middle;
1903
+ }
1904
+ const left = output.values.slice(lower * output.components, (lower + 1) * output.components);
1905
+ let value = left;
1906
+ if (sampler.interpolation === "LINEAR" && upper !== lower) {
1907
+ const right = output.values.slice(upper * output.components, (upper + 1) * output.components);
1908
+ const t = (sourceTimeSeconds - times[lower]) / (times[upper] - times[lower]);
1909
+ value = channel.target.path === "rotation" ? slerpQuaternion(left, right, t) : left.map((part, index2) => part + (right[index2] - part) * t);
1910
+ }
1911
+ const pose = animated.get(channel.target.node) ?? {};
1912
+ if (channel.target.path === "rotation")
1913
+ pose.rotation = normalizedRotation(value, "evaluated clip rotation");
1914
+ else
1915
+ pose[channel.target.path] = value;
1916
+ animated.set(channel.target.node, pose);
1917
+ }
1918
+ }
1919
+ const rotation = options.sourceUp === "x" ? [0, 0, Math.SQRT1_2, Math.SQRT1_2] : options.sourceUp === "z" ? [-Math.SQRT1_2, 0, 0, Math.SQRT1_2] : [0, 0, 0, 1];
1920
+ const conversion = composeTransform({ position: [0, 0, 0], rotation, scale: [options.metersPerUnit, options.metersPerUnit, options.metersPerUnit] });
1921
+ const matrices = new Map;
1922
+ for (const index2 of state.order) {
1923
+ const node = document.nodes[index2], overrides = animated.get(index2);
1924
+ const local = overrides ? nodeTransform({ ...node, ...overrides }, `nodes.${index2}`) : nodeTransform(node, `nodes.${index2}`);
1925
+ const parent = state.parents[index2];
1926
+ matrices.set(index2, multiplyTransforms(parent === null ? conversion : matrices.get(parent), local));
1927
+ }
1928
+ const selected = new Set;
1929
+ if (options.nodeIndex === undefined)
1930
+ for (const index2 of state.reachable)
1931
+ selected.add(index2);
1932
+ else {
1933
+ const pending = [options.nodeIndex];
1934
+ while (pending.length) {
1935
+ const index2 = pending.pop();
1936
+ selected.add(index2);
1937
+ pending.push(...document.nodes[index2].children);
1938
+ }
1939
+ }
1940
+ const primitives = [], imageIds = new Set;
1941
+ let triangles = 0;
1942
+ for (const sourceNodeIndex of [...selected].sort((a, b) => a - b)) {
1943
+ const node = document.nodes[sourceNodeIndex];
1944
+ if (node.mesh === undefined)
1945
+ continue;
1946
+ const matrix2 = safeMatrix(matrices.get(sourceNodeIndex), `nodes.${sourceNodeIndex}`);
1947
+ for (const [sourcePrimitiveIndex, primitive] of state.meshPrimitives[node.mesh].entries()) {
1948
+ if (primitives.length >= SPATIAL_GLB_LIMITS.primitives)
1949
+ fail("Instanced primitive budget exceeded.");
1950
+ triangles += (primitive.indices?.length ?? primitive.positions.length / 3) / 3;
1951
+ if (triangles > SPATIAL_GLB_LIMITS.triangles)
1952
+ fail("Instanced triangle budget exceeded.");
1953
+ const bounds2 = vertexBounds(primitive.positions, primitive.indices, matrix2);
1954
+ const { material, ...geometry } = primitive;
1955
+ if (options.materialMode === "source" && material.baseColorTexture)
1956
+ imageIds.add(material.baseColorTexture.imageIndex);
1957
+ primitives.push({ ...geometry, matrix: matrix2, bounds: bounds2, sourceNodeIndex, sourcePrimitiveIndex, ...options.materialMode === "source" ? { material } : {} });
1958
+ }
1959
+ }
1960
+ if (primitives.length === 0)
1961
+ fail("Selected scene or subtree contains no triangle geometry.");
1962
+ const bounds = combineBounds(primitives.map((primitive) => primitive.bounds));
1963
+ const images = [...imageIds].sort((a, b) => a - b).map((index2) => {
1964
+ const image = state.images[index2];
1965
+ return Object.freeze({ ...image, bytes: image.bytes.slice() });
1966
+ });
1967
+ return Object.freeze({ profile: SPATIAL_GLB_PROFILE, primitives: deepFreezeJson(primitives), images: Object.freeze(images), bounds, sourceTimeSeconds });
1968
+ }
1969
+ }
1970
+ function vertexBounds(positions, indices, matrix2) {
1971
+ const low = [Infinity, Infinity, Infinity], high = [-Infinity, -Infinity, -Infinity];
1972
+ const count = indices?.length ?? positions.length / 3;
1973
+ for (let index2 = 0;index2 < count; index2++) {
1974
+ const offset = (indices?.[index2] ?? index2) * 3;
1975
+ const point = transformPoint(matrix2, [positions[offset], positions[offset + 1], positions[offset + 2]]);
1976
+ for (let axis = 0;axis < 3; axis++) {
1977
+ low[axis] = Math.min(low[axis], point[axis]);
1978
+ high[axis] = Math.max(high[axis], point[axis]);
1979
+ }
1980
+ }
1981
+ return deepFreezeJson({ min: low, max: high });
1982
+ }
1983
+ function combineBounds(bounds) {
1984
+ const min = [Infinity, Infinity, Infinity], max = [-Infinity, -Infinity, -Infinity];
1985
+ for (const bound of bounds)
1986
+ for (let axis = 0;axis < 3; axis++) {
1987
+ min[axis] = Math.min(min[axis], bound.min[axis]);
1988
+ max[axis] = Math.max(max[axis], bound.max[axis]);
1989
+ }
1990
+ return deepFreezeJson({ min, max });
1991
+ }
1992
+ function parseSpatialGlb(bytes) {
1993
+ return SpatialGlbModel.parse(bytes);
1994
+ }
1995
+ function evaluateSpatialGlb(model, options) {
1996
+ if (!(model instanceof SpatialGlbModel))
1997
+ fail("Evaluation requires a parsed GLB model.");
1998
+ return model.evaluate(options);
1999
+ }
2000
+
2001
+ // src/spatial-scene/camera-track.ts
2002
+ import { z as z4 } from "zod";
2003
+ var SPATIAL_CAMERA_TRACK_MAX_FRAMES = 2048;
2004
+ var clockSchema = z4.strictObject({
2005
+ startUs: SpatialTimeUsSchema,
2006
+ frameRate: SpatialFrameRateSchema,
2007
+ frameCount: z4.number().int().min(1).max(SPATIAL_CAMERA_TRACK_MAX_FRAMES)
2008
+ });
2009
+ var rationalSchema = z4.strictObject({
2010
+ numerator: z4.string().regex(/^(0|[1-9][0-9]{0,19})$/u),
2011
+ denominator: z4.string().regex(/^[1-9][0-9]{0,6}$/u)
2012
+ });
2013
+ var SpatialCameraTrackSchema = z4.strictObject({
2014
+ kind: z4.literal("slopcamera.spatial-camera-track"),
2015
+ schemaVersion: z4.literal(1),
2016
+ sceneSha256: SpatialDigestSchema,
2017
+ cameraId: SpatialCameraIdSchema,
2018
+ clock: clockSchema,
2019
+ samples: z4.array(z4.strictObject({
2020
+ frameIndex: z4.number().int().min(0).max(SPATIAL_CAMERA_TRACK_MAX_FRAMES - 1),
2021
+ timeUs: SpatialTimeUsSchema,
2022
+ exactTimeUs: rationalSchema,
2023
+ camera: SpatialCameraSchema
2024
+ })).min(1).max(SPATIAL_CAMERA_TRACK_MAX_FRAMES)
2025
+ });
2026
+ var optionsSchema2 = clockSchema.extend({ cameraId: SpatialCameraIdSchema });
2027
+ function absoluteSample(frameIndex, clock) {
2028
+ const sample = spatialFrameSample(frameIndex, 3600000000, clock.frameRate);
2029
+ const denominator = BigInt(sample.exactTimeUs.denominator);
2030
+ return { frameIndex, timeUs: clock.startUs + sample.timeUs, exactTimeUs: {
2031
+ numerator: String(BigInt(clock.startUs) * denominator + BigInt(sample.exactTimeUs.numerator)),
2032
+ denominator: String(denominator)
2033
+ } };
2034
+ }
2035
+ function parseSpatialCameraTrack(input) {
2036
+ const track = parseSpatialValue(SpatialCameraTrackSchema, input, "camera track");
2037
+ const rate = reduceSpatialFrameRate(track.clock.frameRate);
2038
+ if (rate.numerator !== track.clock.frameRate.numerator || rate.denominator !== track.clock.frameRate.denominator || track.samples.length !== track.clock.frameCount)
2039
+ throw new SpatialSceneError("invalid-data", "Camera track clock or coverage is not canonical.");
2040
+ const first = track.samples[0].camera.projection;
2041
+ for (const [index2, sample] of track.samples.entries()) {
2042
+ const expected = absoluteSample(index2, track.clock);
2043
+ if (sample.frameIndex !== index2 || sample.timeUs !== expected.timeUs || sample.exactTimeUs.numerator !== expected.exactTimeUs.numerator || sample.exactTimeUs.denominator !== expected.exactTimeUs.denominator || sample.camera.cameraId !== track.cameraId || sample.camera.projection.width !== first.width || sample.camera.projection.height !== first.height) {
2044
+ throw new SpatialSceneError("invalid-data", "Camera track must preserve exact clock, camera identity, order and image dimensions.");
2045
+ }
2046
+ }
2047
+ return deepFreezeJson(track);
2048
+ }
2049
+ function sampleSpatialCameraTrack(sceneInput, optionsInput) {
2050
+ const scene = parseSpatialScene(sceneInput);
2051
+ const { cameraId, ...inputClock } = parseSpatialValue(optionsSchema2, optionsInput, "camera track options");
2052
+ const clock = { ...inputClock, frameRate: reduceSpatialFrameRate(inputClock.frameRate) };
2053
+ const last = absoluteSample(clock.frameCount - 1, clock);
2054
+ if (BigInt(last.exactTimeUs.numerator) >= BigInt(scene.durationUs) * BigInt(last.exactTimeUs.denominator)) {
2055
+ throw new SpatialSceneError("invalid-data", "Camera samples exceed the half-open scene duration.");
2056
+ }
2057
+ const camera2 = scene.cameras.find((item) => item.cameraId === cameraId);
2058
+ if (!camera2)
2059
+ throw new SpatialSceneError("not-found", `Unknown camera ${cameraId}.`);
2060
+ const cameraScene = {
2061
+ ...scene,
2062
+ cameras: [camera2],
2063
+ entities: [],
2064
+ assets: [],
2065
+ generators: [],
2066
+ overrides: [],
2067
+ animations: scene.animations.filter((channel) => channel.targetId === cameraId)
2068
+ };
2069
+ return parseSpatialCameraTrack({
2070
+ kind: "slopcamera.spatial-camera-track",
2071
+ schemaVersion: 1,
2072
+ sceneSha256: spatialValueSha256(scene),
2073
+ cameraId,
2074
+ clock,
2075
+ samples: Array.from({ length: clock.frameCount }, (_, frameIndex) => {
2076
+ const sample = absoluteSample(frameIndex, clock);
2077
+ return { ...sample, camera: evaluateSpatialScene(cameraScene, { cameraId, timeUs: sample.timeUs }).camera };
2078
+ })
2079
+ });
2080
+ }
2081
+ // src/code/index.ts
2082
+ function compileWorkflowGraph2(options) {
2083
+ return compileWorkflowGraph({
2084
+ graph: options.graph,
2085
+ ...options.limits === undefined ? {} : { limits: options.limits },
2086
+ projection: PUBLIC_WORKFLOW_REGISTRY_PROJECTION
2087
+ });
2088
+ }
2089
+ export {
2090
+ validateSpatialShot,
2091
+ validateSpatialOverrides,
2092
+ unprojectPixel,
2093
+ transformPoint,
2094
+ transformDirection,
2095
+ transformBounds,
2096
+ spatialValueSha256,
2097
+ spatialTopologicalIds,
2098
+ spatialStateValueSha256,
2099
+ spatialSceneSha256,
2100
+ spatialPropertySupported,
2101
+ spatialOutputDuration,
2102
+ spatialGeneratorOutputSha256,
2103
+ spatialFrameSample,
2104
+ spatialFrameCount,
2105
+ spatialAssetManifestSha256,
2106
+ spatialAssetClosureDigests,
2107
+ sortSpatialBy,
2108
+ slopcameraCodeErrorMessage,
2109
+ slerpQuaternion,
2110
+ sha256Hex,
2111
+ seconds,
2112
+ sampleSpatialCameraTrack,
2113
+ runWorkflow,
2114
+ runBuiltWorkflow,
2115
+ reduceSpatialFrameRate,
2116
+ projectPoint,
2117
+ pixelRay,
2118
+ parseSpatialValue,
2119
+ parseSpatialScene,
2120
+ parseSpatialGlb,
2121
+ parseSpatialCameraTrack,
2122
+ normalizeQuaternion,
2123
+ multiplyTransforms,
2124
+ mergeSpatialOverrides,
2125
+ isPortableSlopcameraOperationKind,
2126
+ invertTransform,
2127
+ inspectSpatialScene,
2128
+ generatedSpatialEntityId,
2129
+ evaluateSpatialScene,
2130
+ evaluateSpatialGlb,
2131
+ defineWorkflow,
2132
+ definePortableWorkflowFragment,
2133
+ createWorkflowGraphHash,
2134
+ createWorkflowCompilationHash,
2135
+ createSpatialSceneStarter,
2136
+ createSlopcameraCodeHost,
2137
+ createPublicWorkflowRegistryProjection,
2138
+ createGraphHash,
2139
+ composeTransform,
2140
+ compileWorkflowGraph2 as compileWorkflowGraph,
2141
+ canonicalJsonSha256,
2142
+ canonicalJson,
2143
+ cameraMathView,
2144
+ buildWorkflowGraph,
2145
+ buildWorkflow,
2146
+ boundedCanonicalJsonSha256,
2147
+ boundedCanonicalJson,
2148
+ asSlopcameraCodeError,
2149
+ applySpatialScenePatch,
2150
+ applySpatialEntityOverride,
2151
+ WORKFLOW_REF_VERSION,
2152
+ WORKFLOW_REF_BRAND,
2153
+ WORKFLOW_NODE_RECEIPT_VERSION,
2154
+ WORKFLOW_NODE_RECEIPT_HASH_DOMAIN,
2155
+ WORKFLOW_GRAPH_VERSION,
2156
+ WORKFLOW_GRAPH_HASH_DOMAIN,
2157
+ WORKFLOW_COMPILATION_VERSION,
2158
+ WORKFLOW_COMPILATION_HASH_DOMAIN,
2159
+ SpatialVec3Schema,
2160
+ SpatialTransformSchema,
2161
+ SpatialTimeUsSchema,
2162
+ SpatialShotV1Schema,
2163
+ SpatialShotIdSchema,
2164
+ SpatialSceneV1Schema,
2165
+ SpatialScenePatchV1Schema,
2166
+ SpatialSceneIdSchema,
2167
+ SpatialSceneError,
2168
+ SpatialQuaternionSchema,
2169
+ SpatialProjectionSchema,
2170
+ SpatialPoseSchema,
2171
+ SpatialPlacementSchema,
2172
+ SpatialPayloadSchema,
2173
+ SpatialPatchOperationSchema,
2174
+ SpatialOverrideSchema,
2175
+ SpatialOriginSchema,
2176
+ SpatialMatrixSchema,
2177
+ SpatialMaterialSchema,
2178
+ SpatialGlbModel,
2179
+ SpatialGeometrySchema,
2180
+ SpatialGeneratorSchema,
2181
+ SpatialGeneratorIdSchema,
2182
+ SpatialFrameRateSchema,
2183
+ SpatialEntitySchema,
2184
+ SpatialEntityIdSchema,
2185
+ SpatialDigestSchema,
2186
+ SpatialChannelIdSchema,
2187
+ SpatialCameraTrackSchema,
2188
+ SpatialCameraSchema,
2189
+ SpatialCameraIdSchema,
2190
+ SpatialAssetManifestSchema,
2191
+ SpatialAssetInterpretationSchema,
2192
+ SpatialAssetIdSchema,
2193
+ SpatialAnimationSchema,
2194
+ SlopcameraWorkflowRunError,
2195
+ SlopcameraVectorizeReceiptSchema,
2196
+ SlopcameraVectorizeQualityReceiptSchema,
2197
+ SlopcameraVectorizeProvenanceSchema,
2198
+ SlopcameraRenderArtifactsSchema,
2199
+ SlopcameraLintFindingSchema,
2200
+ SlopcameraImageVectorizeOutputSchema,
2201
+ SlopcameraImageVectorizeInputSchema,
2202
+ SlopcameraImageModelSchema,
2203
+ SlopcameraImageGenerateOutputSchema,
2204
+ SlopcameraImageGenerateInputSchema,
2205
+ SlopcameraDiagramRenderOutputSchema,
2206
+ SlopcameraDiagramRenderInputSchema,
2207
+ SlopcameraDiagramCheckOutputSchema,
2208
+ SlopcameraDiagramCheckInputSchema,
2209
+ SlopcameraCodeError,
2210
+ SerializedRefV1Schema,
2211
+ SPATIAL_SCENE_LIMITS,
2212
+ SPATIAL_GLB_PROFILE,
2213
+ SPATIAL_GLB_LIMITS,
2214
+ SPATIAL_CAMERA_TRACK_MAX_FRAMES,
2215
+ RequirementEnvelopeSchema,
2216
+ REQUIREMENT_ENVELOPE_VERSION,
2217
+ PortableWorkflowBuilder,
2218
+ PUBLIC_WORKFLOW_REGISTRY_PROJECTION_ID,
2219
+ PUBLIC_WORKFLOW_REGISTRY_PROJECTION,
2220
+ PUBLIC_SLOPCAMERA_WORKFLOW_PROJECTION,
2221
+ PORTABLE_SLOPCAMERA_OPERATION_KINDS,
2222
+ PORTABLE_SLOPCAMERA_OPERATION_CONTRACTS,
2223
+ OperationPolicySchema,
2224
+ OperationKindSchema,
2225
+ OperationDiscoverySchema,
2226
+ MAX_WORKFLOW_RESULT_VALUES,
2227
+ MAX_WORKFLOW_RESULT_DEPTH,
2228
+ MAX_WORKFLOW_RESULT_BYTES,
2229
+ MAX_IMAGE_DIMENSION,
2230
+ MAX_ABS_COMPONENT,
2231
+ JsonValueSchema,
2232
+ IDENTITY_MATRIX,
2233
+ GraphCompilerLimitsSchema,
2234
+ GRAPH_ABI,
2235
+ EvaluatedSpatialSceneSchema,
2236
+ DEFAULT_GRAPH_COMPILER_LIMITS,
2237
+ CompiledWorkflowGraphSchema,
2238
+ AuthoredWorkflowGraphV1Schema,
2239
+ AuthoredGraphNodeV1Schema
2240
+ };