cozyclay 2.1.1 → 3.0.1

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 (585) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/README.md +12 -30
  3. package/bin/agent/agent-routes.mjs +43 -116
  4. package/bin/agent/agent-runner.mjs +14 -15
  5. package/bin/agent/cliproxy-provider.mjs +135 -0
  6. package/bin/agent/credential-store.mjs +30 -7
  7. package/bin/agent/pi-tools.mjs +4 -1
  8. package/bin/agent/provider-keys.mjs +32 -0
  9. package/bin/agent/provider-login.mjs +110 -0
  10. package/bin/agent/provider-routes.mjs +99 -0
  11. package/bin/agent/providers.mjs +19 -97
  12. package/bin/agent/script-tool.mjs +17 -4
  13. package/bin/agent/session-store.mjs +2 -2
  14. package/bin/agent/studio-context-budget.mjs +93 -0
  15. package/bin/agent/studio-motion-reference.mjs +23 -0
  16. package/bin/agent/studio-prompt.mjs +1 -1
  17. package/bin/agent/studio-rebase-text.mjs +32 -0
  18. package/bin/agent/studio-rebase.mjs +26 -39
  19. package/bin/agent/studio-tools.mjs +9 -28
  20. package/bin/agent/tool-arguments.mjs +49 -0
  21. package/bin/agent/turn-frames.mjs +66 -0
  22. package/bin/codex-auth.mjs +9 -2
  23. package/bin/cozyclay.mjs +10 -116
  24. package/bin/live/cli.mjs +3 -3
  25. package/bin/mcp-runtime.mjs +2 -12
  26. package/bin/package-signature.mjs +0 -4
  27. package/dist/app/index.html +7 -6
  28. package/dist/assets/EXRLoader-CvVIvGVE.js +1 -0
  29. package/dist/assets/analytics-BWCqdgWA.js +1 -0
  30. package/dist/assets/app-DfTCxaKk.js +5087 -0
  31. package/dist/assets/app-f-fkV5uP.css +1 -0
  32. package/dist/assets/compile-worker-BxKXh07s.js +5 -0
  33. package/dist/assets/fflate.module-KbGj5-H6.js +5 -0
  34. package/dist/assets/{first-shot-handoff-msLI71g1.js → first-shot-handoff-DNO8kv_N.js} +1 -1
  35. package/dist/assets/{landing-ChUvB_zL.js → landing-yfZaJkWc.js} +1 -1
  36. package/dist/assets/scene-asset-sync-U3kS2rvK.js +20 -0
  37. package/dist/assets/scene-asset-sync-_S8NQl5B.css +1 -0
  38. package/dist/assets/workflow-D78eiKTa.js +179 -0
  39. package/dist/cozyclay-package.json +1 -1
  40. package/dist/index.html +3 -3
  41. package/dist/sw.js +3 -6
  42. package/dist/workflow/index.html +5 -4
  43. package/mcp/LIVE-PROTOCOL.md +7 -47
  44. package/mcp/README.md +16 -26
  45. package/mcp/live-hub.mjs +6 -10
  46. package/mcp/package.json +1 -3
  47. package/mcp/tool-handlers.mjs +4 -168
  48. package/package.json +3 -22
  49. package/src/App.jsx +151 -2625
  50. package/src/agent-progress.js +80 -0
  51. package/src/agent-turn-mark.js +30 -0
  52. package/src/analytics.js +1 -19
  53. package/src/app-context.js +12 -2
  54. package/src/app-stage.jsx +2 -34
  55. package/src/asset-pane.jsx +6 -5
  56. package/src/camera-block.js +21 -0
  57. package/src/camera-direction.js +120 -0
  58. package/src/character-kind.js +3 -9
  59. package/src/command-bus.js +10 -8
  60. package/src/commands/cast.js +5 -34
  61. package/src/commands/elements/character.js +1 -1
  62. package/src/commands/elements/motion.js +1 -6
  63. package/src/commands/elements/object.js +6 -2
  64. package/src/commands/elements/shot.js +4 -1
  65. package/src/commands/elements.js +38 -14
  66. package/src/commands/index.js +3 -1
  67. package/src/commands/kits.js +37 -0
  68. package/src/commands/motion-timing.js +57 -0
  69. package/src/commands/motion.js +9 -152
  70. package/src/commands/motion3-compile.js +65 -0
  71. package/src/commands/motion3.js +214 -0
  72. package/src/commands/objects.js +6 -4
  73. package/src/commands/shot.js +55 -13
  74. package/src/commands/stage.js +14 -1
  75. package/src/commands/view.js +4 -1
  76. package/src/document-mirrors.js +42 -0
  77. package/src/document-store.js +4 -0
  78. package/src/domains/cast.js +19 -194
  79. package/src/domains/motion/motion-range-pin-rules.js +1 -1
  80. package/src/domains/motion/take-source.js +53 -0
  81. package/src/domains/motion.js +60 -1838
  82. package/src/domains/scenes.js +12 -55
  83. package/src/domains/shots.js +2 -2
  84. package/src/domains/stage.js +4 -2
  85. package/src/draft-ghost.js +47 -0
  86. package/src/draft-ghost.jsx +129 -0
  87. package/src/dualview.jsx +4 -3
  88. package/src/effects/EffectsLayer.jsx +112 -0
  89. package/src/effects/effect-meshes.js +232 -0
  90. package/src/effects/effect-record.js +103 -0
  91. package/src/effects/emitter.js +59 -0
  92. package/src/effects/simulate.js +261 -0
  93. package/src/effects/state.js +59 -0
  94. package/src/execution-telemetry.js +2 -3
  95. package/src/first-success-guide.jsx +5 -5
  96. package/src/ghost-set.js +257 -0
  97. package/src/held-object-edit.jsx +113 -0
  98. package/src/held-object-pose.js +175 -0
  99. package/src/hierarchy-model.js +1 -1
  100. package/src/hierarchy-panel.jsx +1 -1
  101. package/src/imported-mesh.jsx +103 -0
  102. package/src/kits/held.js +84 -0
  103. package/src/kits/index.js +70 -0
  104. package/src/kits/smoking.js +57 -0
  105. package/src/light-blockers.js +93 -0
  106. package/src/motion/beat-board-model.js +84 -0
  107. package/src/motion/beat-board.css +124 -0
  108. package/src/motion/beat-board.jsx +60 -0
  109. package/src/motion/center-of-mass.js +66 -0
  110. package/src/motion/fix-collisions.js +850 -0
  111. package/src/motion/physics-review.js +118 -0
  112. package/src/{ardy → motion}/physics-support.js +1 -76
  113. package/src/motion/rig-snapshot.js +22 -0
  114. package/src/motion/take-segments.jsx +140 -0
  115. package/src/{ardy → motion}/timeline-coordinates.js +0 -21
  116. package/src/{ardy → motion}/timeline.css +1 -2
  117. package/src/{ardy → motion}/timeline.jsx +42 -413
  118. package/src/motion/waypoints.js +76 -0
  119. package/src/motion3/beats.js +83 -0
  120. package/src/motion3/body.js +158 -0
  121. package/src/motion3/bones.js +47 -0
  122. package/src/motion3/carried.js +47 -0
  123. package/src/motion3/compile-job.js +79 -0
  124. package/src/motion3/compile-worker.js +10 -0
  125. package/src/motion3/compiler/anchors.js +113 -0
  126. package/src/motion3/compiler/arm-orient.js +128 -0
  127. package/src/motion3/compiler/channels.js +125 -0
  128. package/src/motion3/compiler/compile.js +205 -0
  129. package/src/motion3/compiler/gait-rhythm.js +97 -0
  130. package/src/motion3/compiler/grip.js +171 -0
  131. package/src/motion3/compiler/head-steady.js +48 -0
  132. package/src/motion3/compiler/hold.js +134 -0
  133. package/src/motion3/compiler/idle.js +113 -0
  134. package/src/motion3/compiler/limbs.js +197 -0
  135. package/src/motion3/compiler/math.js +72 -0
  136. package/src/motion3/compiler/pose.js +177 -0
  137. package/src/motion3/compiler/quat.js +49 -0
  138. package/src/motion3/compiler/skeleton.js +74 -0
  139. package/src/motion3/compiler/validate-common.js +32 -0
  140. package/src/motion3/compiler/validate-goals.js +118 -0
  141. package/src/motion3/compiler/validate-objects.js +144 -0
  142. package/src/motion3/compiler/validate-parts.js +118 -0
  143. package/src/motion3/compiler/validate.js +122 -0
  144. package/src/motion3/compiler/vocab.js +74 -0
  145. package/src/motion3/compiler/walk-pelvis.js +275 -0
  146. package/src/motion3/compiler/walk.js +207 -0
  147. package/src/motion3/compiler/wrist-clamp.js +32 -0
  148. package/src/motion3/delta.js +47 -0
  149. package/src/motion3/describe.js +177 -0
  150. package/src/motion3/docs.js +27 -0
  151. package/src/motion3/edit-carry.js +21 -0
  152. package/src/motion3/fix/ease.js +16 -0
  153. package/src/motion3/fix/ground.js +37 -0
  154. package/src/motion3/fix/index.js +52 -0
  155. package/src/motion3/fix/push.js +71 -0
  156. package/src/motion3/fix/reach.js +46 -0
  157. package/src/motion3/fix/retime.js +79 -0
  158. package/src/motion3/fix/self.js +211 -0
  159. package/src/motion3/fix/slide.js +70 -0
  160. package/src/motion3/fix/turn.js +51 -0
  161. package/src/motion3/follow.js +39 -0
  162. package/src/motion3/free-motion.js +204 -0
  163. package/src/motion3/grip-choice.js +130 -0
  164. package/src/motion3/hold-plan.js +157 -0
  165. package/src/motion3/json-patch.js +77 -0
  166. package/src/motion3/key-aim.js +105 -0
  167. package/src/motion3/key-edit.js +82 -0
  168. package/src/motion3/lenient.js +127 -0
  169. package/src/motion3/object-keys.js +236 -0
  170. package/src/motion3/object-pose.js +182 -0
  171. package/src/motion3/object-tracks.js +12 -0
  172. package/src/motion3/people.js +82 -0
  173. package/src/motion3/pipeline.js +102 -0
  174. package/src/motion3/playback.js +51 -0
  175. package/src/motion3/probes.js +91 -0
  176. package/src/motion3/qa/body-checks.js +34 -0
  177. package/src/motion3/qa/check-names.js +14 -0
  178. package/src/motion3/qa/checks.js +125 -0
  179. package/src/motion3/qa/contact-checks.js +51 -0
  180. package/src/motion3/qa/explain.js +80 -0
  181. package/src/motion3/qa/fmt.js +5 -0
  182. package/src/motion3/qa/foot-checks.js +72 -0
  183. package/src/motion3/qa/geometry.js +77 -0
  184. package/src/motion3/qa/goals.js +191 -0
  185. package/src/motion3/qa/ground.js +60 -0
  186. package/src/motion3/qa/hints-contact.js +195 -0
  187. package/src/motion3/qa/hints-goal.js +160 -0
  188. package/src/motion3/qa/hints-knee.js +24 -0
  189. package/src/motion3/qa/hints-motion.js +209 -0
  190. package/src/motion3/qa/hints-object.js +91 -0
  191. package/src/motion3/qa/index.js +44 -0
  192. package/src/motion3/qa/key-checks.js +82 -0
  193. package/src/motion3/qa/near.js +96 -0
  194. package/src/motion3/qa/object-checks.js +135 -0
  195. package/src/motion3/qa/patch.js +161 -0
  196. package/src/motion3/qa/people-checks.js +37 -0
  197. package/src/motion3/qa/severity.js +38 -0
  198. package/src/motion3/qa/spec-checks.js +65 -0
  199. package/src/motion3/qa/style.js +228 -0
  200. package/src/motion3/qa/wrist-checks.js +69 -0
  201. package/src/motion3/qa/wrist-obs.js +60 -0
  202. package/src/motion3/refs.js +129 -0
  203. package/src/motion3/repair-tune.js +121 -0
  204. package/src/motion3/repair.js +83 -0
  205. package/src/motion3/report.js +67 -0
  206. package/src/motion3/rigs/index.js +14 -0
  207. package/src/motion3/rigs/x-bot-tpose-body.json +1 -0
  208. package/src/motion3/rigs/x-bot-tpose.json +1 -0
  209. package/src/motion3/rigs/y-bot-tpose-body.json +1 -0
  210. package/src/motion3/rigs/y-bot-tpose.json +1 -0
  211. package/src/motion3/scene.js +60 -0
  212. package/src/motion3/schema/brief.js +89 -0
  213. package/src/motion3/schema/fields.js +44 -0
  214. package/src/motion3/schema/spec-examples.js +46 -0
  215. package/src/motion3/schema/topics.js +113 -0
  216. package/src/motion3/schema/types.js +149 -0
  217. package/src/motion3/search.js +117 -0
  218. package/src/motion3/second-hand.js +112 -0
  219. package/src/motion3/settle.js +136 -0
  220. package/src/motion3/skills/basic.js +83 -0
  221. package/src/motion3/skills/book.js +76 -0
  222. package/src/motion3/skills/car.js +145 -0
  223. package/src/motion3/skills/flip.js +200 -0
  224. package/src/motion3/skills/frames-auto.js +29 -0
  225. package/src/motion3/skills/impact.js +109 -0
  226. package/src/motion3/skills/index.js +85 -0
  227. package/src/motion3/skills/info.js +80 -0
  228. package/src/motion3/skills/smoke-kit.js +109 -0
  229. package/src/motion3/skills/smoke.js +160 -0
  230. package/src/motion3/spec-store.js +59 -0
  231. package/src/motion3/take-edit.js +155 -0
  232. package/src/motion3/take.js +63 -0
  233. package/src/motion3/timeline.js +127 -0
  234. package/src/motion3/views.js +121 -0
  235. package/src/motion3/walk-plan.js +119 -0
  236. package/src/object-catalog.jsx +7 -1
  237. package/src/object-colors.js +98 -0
  238. package/src/object-gizmo-parts.js +115 -0
  239. package/src/object-gizmo.jsx +37 -122
  240. package/src/object-glow.js +139 -0
  241. package/src/object-hidden.js +33 -0
  242. package/src/object-look.js +196 -0
  243. package/src/object-size.js +10 -0
  244. package/src/object-travel.js +71 -6
  245. package/src/panels/CameraPanel.jsx +7 -7
  246. package/src/panels/HdrEnvironmentControls.jsx +59 -0
  247. package/src/panels/LightPanel.jsx +25 -4
  248. package/src/panels/ObjectLookPanel.jsx +88 -0
  249. package/src/panels/PosePanel.jsx +2 -17
  250. package/src/panels/RigControlPanel.jsx +5 -53
  251. package/src/panels/SubjectBox.jsx +4 -1
  252. package/src/panels/details.css +14 -0
  253. package/src/posestudio.jsx +4 -27
  254. package/src/project-browser.jsx +3 -4
  255. package/src/project-resources.js +18 -31
  256. package/src/project.js +13 -104
  257. package/src/props.jsx +25 -280
  258. package/src/qa-label.js +25 -0
  259. package/src/rail-dot-drag.js +23 -0
  260. package/src/render-passes.js +10 -0
  261. package/src/resource-status.jsx +1 -0
  262. package/src/result-modal.jsx +4 -19
  263. package/src/room.jsx +20 -12
  264. package/src/scene-assets.js +8 -6
  265. package/src/scene-light-place.js +72 -0
  266. package/src/scene-lights.jsx +148 -0
  267. package/src/scene-objects.js +31 -139
  268. package/src/scenes.js +15 -105
  269. package/src/semantic-edit.js +2 -5
  270. package/src/set-pieces.jsx +183 -0
  271. package/src/settings-menu.jsx +3 -11
  272. package/src/shell/BottomDock.jsx +9 -51
  273. package/src/shell/DetailsSlot.jsx +16 -200
  274. package/src/shell/GhostsMenuGroup.jsx +74 -0
  275. package/src/shell/LibrarySlot.jsx +1 -3
  276. package/src/shell/MenuBar.jsx +3 -3
  277. package/src/shell/PreferencesDialog.jsx +15 -264
  278. package/src/shell/PreferencesSlot.jsx +2 -8
  279. package/src/shell/TopBar.jsx +5 -84
  280. package/src/shell/ViewportToolbar.jsx +10 -70
  281. package/src/shell/agent-glass.css +19 -6
  282. package/src/shell/details-selection.js +42 -0
  283. package/src/shell/ghosts-menu.css +22 -0
  284. package/src/shell/glass-regions.css +29 -3
  285. package/src/shell/preferences.css +6 -1
  286. package/src/shot-authoring.js +4 -7
  287. package/src/soft-shadows.js +37 -0
  288. package/src/stage-attach-frames.js +12 -12
  289. package/src/stage-canvas-rigs.jsx +3 -3
  290. package/src/stage-capture.jsx +42 -8
  291. package/src/stage-characters.jsx +22 -216
  292. package/src/stage-editor-gizmos.jsx +6 -6
  293. package/src/stage-environment.js +93 -0
  294. package/src/stage-hdr-builtins.js +107 -0
  295. package/src/stage-hdr-image.js +116 -0
  296. package/src/stage-hdr-library.js +70 -0
  297. package/src/stage-hdr-map.js +94 -0
  298. package/src/stage-hdr.js +130 -0
  299. package/src/stage-lighting.js +149 -0
  300. package/src/stage-occlusion.js +210 -0
  301. package/src/stage-sky.jsx +159 -0
  302. package/src/stage-startup-defaults.js +5 -9
  303. package/src/studio-actions.js +16 -14
  304. package/src/studio-agent-commands.js +19 -16
  305. package/src/studio-agent-motion.js +9 -9
  306. package/src/studio-agent-protocol.js +47 -77
  307. package/src/studio-app-binding.js +27 -47
  308. package/src/studio-asset-list.js +17 -0
  309. package/src/studio-contact-sheet.js +37 -5
  310. package/src/studio-elements.js +27 -7
  311. package/src/studio-entity-bounds.js +18 -0
  312. package/src/studio-error-codes.js +15 -0
  313. package/src/studio-export-framing.js +152 -0
  314. package/src/studio-lighting-check.js +103 -0
  315. package/src/studio-motion-visual.js +150 -0
  316. package/src/studio-schema-errors.js +80 -0
  317. package/src/studio-support-gap.js +74 -0
  318. package/src/styles.css +1 -19
  319. package/src/timeline-defaults.js +5 -0
  320. package/src/timeline-extent.js +8 -20
  321. package/src/tutorial-walk.js +17 -0
  322. package/src/ui-scrub.js +1 -1
  323. package/src/use-shot-camera-tracks.js +12 -0
  324. package/src/view-ghosts.js +97 -0
  325. package/src/view-ghosts.jsx +49 -0
  326. package/src/workflow/AgentPanel.jsx +47 -166
  327. package/src/workflow/AgentProgressCard.jsx +109 -0
  328. package/src/workflow/AgentReplyText.jsx +37 -0
  329. package/src/workflow/AgentTurnSteps.jsx +84 -0
  330. package/src/workflow/LoginPanel.jsx +140 -0
  331. package/src/workflow/WorkflowBuilder.jsx +7 -23
  332. package/src/workflow/agent-client.js +54 -51
  333. package/src/workflow/agent-panel.css +71 -1
  334. package/src/workflow/agent-progress-card.css +167 -0
  335. package/src/workflow/agent-progress-view.js +47 -0
  336. package/src/workflow/agent-turn-steps.css +113 -0
  337. package/src/workflow/agent-turn.js +191 -0
  338. package/src/workflow/login-client.js +34 -0
  339. package/src/workflow/motion-input.js +18 -31
  340. package/src/workflow/node-schema.js +1 -1
  341. package/src/workflow/reply-markdown.js +69 -0
  342. package/src/workflow/scene-asset-sync.js +8 -8
  343. package/tools/agent-session-stats.mjs +61 -0
  344. package/tools/dev-full.mjs +5 -56
  345. package/tools/eslint-baseline.json +12 -13
  346. package/tools/knip-baseline.json +0 -179
  347. package/tools/loc-baseline.json +31 -81
  348. package/tools/motion3/extract-body.mjs +103 -0
  349. package/tools/motion3/extract-rig.mjs +85 -0
  350. package/tools/process-supervisor.mjs +0 -88
  351. package/tools/run-tests.mjs +9 -125
  352. package/bin/agent/motion-runtime.mjs +0 -76
  353. package/dist/ardy/cskel27-rest.json +0 -557
  354. package/dist/assets/analytics-C9pMpW7s.js +0 -1
  355. package/dist/assets/app-B_2XB9rw.css +0 -1
  356. package/dist/assets/app-DAuvKPP8.js +0 -4920
  357. package/dist/assets/config-Bx7pYsD9.js +0 -1
  358. package/dist/assets/config-DeoFTYzb.css +0 -1
  359. package/dist/assets/demo-CvKuBgy5.js +0 -1
  360. package/dist/assets/scene-asset-sync-DKkpjKBV.js +0 -19
  361. package/dist/assets/scene-asset-sync-DjJ_cJ2R.css +0 -1
  362. package/dist/assets/ticket-BPk2Ni95.js +0 -1
  363. package/dist/assets/workflow-jJRBR94Z.js +0 -179
  364. package/dist/demo/walk-then-stop.npz +0 -0
  365. package/mcp/ardy-prompts.mjs +0 -218
  366. package/src/ardy/auto-fix-panel.css +0 -108
  367. package/src/ardy/auto-physics.js +0 -1194
  368. package/src/ardy/client.js +0 -153
  369. package/src/ardy/convert.js +0 -313
  370. package/src/ardy/cskel27-neutral.js +0 -39
  371. package/src/ardy/cskel27.js +0 -77
  372. package/src/ardy/export.js +0 -165
  373. package/src/ardy/fix-collisions.js +0 -2257
  374. package/src/ardy/foot-lock.js +0 -230
  375. package/src/ardy/motion-calibration.js +0 -118
  376. package/src/ardy/motion-edit.js +0 -213
  377. package/src/ardy/motion-url.js +0 -55
  378. package/src/ardy/mp4-duration.js +0 -102
  379. package/src/ardy/npz.js +0 -595
  380. package/src/ardy/physics-panel.css +0 -21
  381. package/src/ardy/physics-panel.jsx +0 -89
  382. package/src/ardy/physics-refine.js +0 -111
  383. package/src/ardy/physics-review.js +0 -567
  384. package/src/ardy/physics-temporal.js +0 -54
  385. package/src/ardy/platform-fit-panel.css +0 -64
  386. package/src/ardy/platform-fit-panel.jsx +0 -41
  387. package/src/ardy/platform-fit.js +0 -326
  388. package/src/ardy/playback.js +0 -711
  389. package/src/ardy/pose-pin.js +0 -97
  390. package/src/ardy/prompt-clips.js +0 -30
  391. package/src/ardy/retime.js +0 -211
  392. package/src/ardy/root-drop.js +0 -369
  393. package/src/ardy/timeline-resize.js +0 -13
  394. package/src/ardy/to-cskel27.js +0 -225
  395. package/src/ardy/trim.js +0 -33
  396. package/src/ardy/waypoints.js +0 -369
  397. package/src/ardy-bridge-contract.js +0 -281
  398. package/src/domains/motion/motion-video-capture.js +0 -664
  399. package/src/i2v-motion-client.js +0 -77
  400. package/src/i2v-motion-studio.jsx +0 -180
  401. package/src/line-edit.js +0 -1348
  402. package/src/motion/generation.js +0 -105
  403. package/src/motion-readiness-ui.jsx +0 -67
  404. package/src/motion-readiness.js +0 -40
  405. package/src/motion-resources.js +0 -177
  406. package/src/motion-store.js +0 -65
  407. package/src/motion-trail.js +0 -601
  408. package/src/multimodel-ingest.js +0 -394
  409. package/src/panels/PromptBlocksPanel.jsx +0 -444
  410. package/src/panels/VideoCapturePanel.jsx +0 -181
  411. package/src/pose-extract/browser.js +0 -42
  412. package/src/pose-extract/detector.js +0 -100
  413. package/src/pose-extract/fit.js +0 -227
  414. package/src/pose-extract/image-frame.js +0 -51
  415. package/src/pose-extract/index.js +0 -58
  416. package/src/pose-extract/keyframes.js +0 -74
  417. package/src/pose-extract/landmarks.js +0 -162
  418. package/src/pose-extract/mirror.js +0 -153
  419. package/src/pose-extract/take.js +0 -131
  420. package/src/pose-extract/video-frames.js +0 -91
  421. package/src/take-recipe.js +0 -165
  422. package/src/trail-key-conflicts.js +0 -39
  423. package/src/trail-pick.js +0 -59
  424. package/src/use-i2v-capture-modal.js +0 -15
  425. package/tools/ardy/BRIDGE.md +0 -37
  426. package/tools/ardy/GVHMR-NPZ.md +0 -68
  427. package/tools/ardy/README.md +0 -124
  428. package/tools/ardy/__pycache__/cclay_gvhmr_worker.cpython-313.pyc +0 -0
  429. package/tools/ardy/artifacts.mjs +0 -34
  430. package/tools/ardy/bridge.mjs +0 -1732
  431. package/tools/ardy/bvh-cskel27.mjs +0 -1265
  432. package/tools/ardy/cclay_constrained_generate.py +0 -561
  433. package/tools/ardy/cclay_gvhmr_worker.py +0 -264
  434. package/tools/ardy/cclay_motion_edit.py +0 -510
  435. package/tools/ardy/cclay_sequence_generate.py +0 -705
  436. package/tools/ardy/cclay_worker.py +0 -304
  437. package/tools/ardy/dump-npz.py +0 -205
  438. package/tools/ardy/extract-rest.mjs +0 -299
  439. package/tools/ardy/extract.mjs +0 -507
  440. package/tools/ardy/footage.mjs +0 -467
  441. package/tools/ardy/gvhmr-floor.mjs +0 -102
  442. package/tools/ardy/gvhmr-to-cskel27.mjs +0 -21
  443. package/tools/ardy/gvhmr_fastpath.py +0 -168
  444. package/tools/ardy/gvhmr_trajectory.py +0 -240
  445. package/tools/ardy/merge-text-encoder.py +0 -160
  446. package/tools/ardy/mocap-metrics.mjs +0 -200
  447. package/tools/ardy/mocap-quality-gate.mjs +0 -110
  448. package/tools/ardy/motion-stabilize.mjs +0 -348
  449. package/tools/ardy/npz.mjs +0 -414
  450. package/tools/ardy/pose-to-npz.mjs +0 -106
  451. package/tools/ardy/prompt-limits.mjs +0 -5
  452. package/tools/ardy/run-edit-on-box.sh +0 -73
  453. package/tools/ardy/run-local.mjs +0 -233
  454. package/tools/ardy/run-on-box.sh +0 -593
  455. package/tools/ardy/run-sequence-on-box.sh +0 -177
  456. package/tools/ardy/runners/gvhmr-worker.mjs +0 -206
  457. package/tools/ardy/runners/index.mjs +0 -18
  458. package/tools/ardy/runners/local.mjs +0 -455
  459. package/tools/ardy/runners/proc.mjs +0 -125
  460. package/tools/ardy/runners/remote.mjs +0 -252
  461. package/tools/ardy/setup-local.mjs +0 -189
  462. package/tools/ardy/setup-text-encoder-on-box.sh +0 -93
  463. package/tools/ardy/setup-text-encoder.py +0 -306
  464. package/tools/ardy/smpl-cskel27.mjs +0 -178
  465. package/tools/ardy/visual-qa.mjs +0 -196
  466. package/tools/ardy/vq-car.mjs +0 -52
  467. package/tools/bench/EXP3.md +0 -61
  468. package/tools/bench/cclay_bench_extract_incam.py +0 -125
  469. package/tools/bench/cclay_bench_extract_obs.py +0 -204
  470. package/tools/bench/cclay_bench_runner.py +0 -42
  471. package/tools/bench/cube-contact.mjs +0 -162
  472. package/tools/bench/exp3.mjs +0 -152
  473. package/tools/bench/extract-bench-lib.mjs +0 -216
  474. package/tools/bench/extract-bench.mjs +0 -302
  475. package/tools/bench/fal-generate.mjs +0 -88
  476. package/tools/bench/fit/README.md +0 -189
  477. package/tools/bench/fit/camera.mjs +0 -59
  478. package/tools/bench/fit/contact.mjs +0 -419
  479. package/tools/bench/fit/footlock.mjs +0 -234
  480. package/tools/bench/fit/motion.mjs +0 -85
  481. package/tools/bench/fit/pin.mjs +0 -27
  482. package/tools/bench/fit/remote.mjs +0 -105
  483. package/tools/bench/fit-bench.mjs +0 -126
  484. package/tools/bench/fit-sanity.mjs +0 -45
  485. package/tools/bench/metrics.mjs +0 -313
  486. package/tools/bench/obs/depth.mjs +0 -258
  487. package/tools/bench/obs/extrinsics.mjs +0 -175
  488. package/tools/bench/obs/fit_mannequin_betas.py +0 -340
  489. package/tools/bench/obs/ground.mjs +0 -294
  490. package/tools/bench/obs/heading.mjs +0 -151
  491. package/tools/bench/obs/ladder.mjs +0 -555
  492. package/tools/bench/obs/mannequin-betas.json +0 -124
  493. package/tools/bench/obs/remote.mjs +0 -83
  494. package/tools/bench/obs/rest_joints.py +0 -52
  495. package/tools/bench/obs/ybot-targets.mjs +0 -49
  496. package/tools/bench/obs-bench.mjs +0 -520
  497. package/tools/bench/score.mjs +0 -343
  498. package/tools/bench/summarize.mjs +0 -86
  499. package/tools/demo-worker/README.md +0 -137
  500. package/tools/demo-worker/api-client.mjs +0 -233
  501. package/tools/demo-worker/generate.mjs +0 -301
  502. package/tools/demo-worker/index.mjs +0 -466
  503. package/tools/demo-worker/sign-core.mjs +0 -112
  504. package/tools/demo-worker/sign.mjs +0 -161
  505. package/tools/gt-render/browser.mjs +0 -182
  506. package/tools/gt-render/camera-math.mjs +0 -276
  507. package/tools/gt-render/page.mjs +0 -267
  508. package/tools/gt-render/render.mjs +0 -514
  509. package/tools/gt-render/scene-box.mjs +0 -31
  510. package/tools/gt-render/take-transform.mjs +0 -87
  511. package/tools/kimodo/compare-seam.mjs +0 -108
  512. package/tools/kimodo/constraints.mjs +0 -244
  513. package/tools/kimodo/edit.mjs +0 -259
  514. package/tools/kimodo/effector-constraints.mjs +0 -200
  515. package/tools/kimodo/generate.mjs +0 -646
  516. package/tools/kimodo/local-output.mjs +0 -265
  517. package/tools/kimodo/measure-pose.mjs +0 -65
  518. package/tools/kimodo/measure-preserve.mjs +0 -899
  519. package/tools/kimodo/measure-waypoints.mjs +0 -55
  520. package/tools/kimodo/mlx-generate.py +0 -111
  521. package/tools/kimodo/pose-constraints.mjs +0 -248
  522. package/tools/kimodo/preserve-mask.mjs +0 -543
  523. package/tools/kimodo/read-npz.mjs +0 -189
  524. package/tools/kimodo/run-edit-on-box.mjs +0 -155
  525. package/tools/kimodo/run-sequence-on-box.mjs +0 -178
  526. package/tools/kimodo/runner.mjs +0 -284
  527. package/tools/kimodo/seam-batch.mjs +0 -77
  528. package/tools/kimodo/setup-local.mjs +0 -30
  529. package/tools/kimodo/setup-on-box.sh +0 -339
  530. package/tools/kimodo/soma77-to-cskel27.mjs +0 -305
  531. package/tools/morphgs/assets/gen_truth.py +0 -39
  532. package/tools/morphgs/assets/mesh_ori_rig.txt +0 -27
  533. package/tools/morphgs/assets/playback-check.mjs +0 -30
  534. package/tools/morphgs/demo-gate.sh +0 -26
  535. package/tools/morphgs/fbx2morphgs.mjs +0 -68
  536. package/tools/morphgs/morphgs-to-cskel27.mjs +0 -105
  537. package/tools/morphgs/patches/preprocess_src-none-mode.patch +0 -76
  538. package/tools/morphgs/setup-on-cluster.sh +0 -126
  539. package/tools/projflow/driver.py +0 -1069
  540. package/tools/projflow/generate.mjs +0 -805
  541. package/tools/projflow/hml22-to-cskel27.mjs +0 -735
  542. package/tools/projflow/line-edit-job.mjs +0 -547
  543. package/tools/projflow/replay.mjs +0 -452
  544. package/tools/projflow/runner.mjs +0 -269
  545. package/tools/projflow/service.mjs +0 -742
  546. package/tools/track/DESIGN.md +0 -513
  547. package/tools/track/backfill-provenance.mjs +0 -116
  548. package/tools/track/budget.mjs +0 -8
  549. package/tools/track/check-rig.mjs +0 -209
  550. package/tools/track/diagnostics.schema.json +0 -60
  551. package/tools/track/export-rig.mjs +0 -256
  552. package/tools/track/fallback.mjs +0 -7
  553. package/tools/track/fk-parity-fixture.mjs +0 -161
  554. package/tools/track/gate.mjs +0 -349
  555. package/tools/track/masks.mjs +0 -318
  556. package/tools/track/metrics.mjs +0 -201
  557. package/tools/track/publish-obs.mjs +0 -108
  558. package/tools/track/py/check_env.py +0 -61
  559. package/tools/track/py/eval_lr.py +0 -229
  560. package/tools/track/py/lr_viterbi.py +0 -294
  561. package/tools/track/py/masks.py +0 -405
  562. package/tools/track/py/objective.py +0 -382
  563. package/tools/track/py/rig.py +0 -508
  564. package/tools/track/py/scene.py +0 -326
  565. package/tools/track/py/test_joint_indices.py +0 -123
  566. package/tools/track/py/test_lr_viterbi.py +0 -195
  567. package/tools/track/py/test_masks.py +0 -229
  568. package/tools/track/py/test_rig.py +0 -288
  569. package/tools/track/py/test_scene.py +0 -354
  570. package/tools/track/py/test_track.py +0 -328
  571. package/tools/track/py/track.py +0 -411
  572. package/tools/track/remote.mjs +0 -140
  573. package/tools/track/rig-dump.mjs +0 -227
  574. package/tools/track/run-box-tests.mjs +0 -14
  575. package/tools/track/run-box.mjs +0 -168
  576. package/tools/track/setup-box.sh +0 -59
  577. package/tools/track/study-2d.mjs +0 -728
  578. /package/src/{ardy → motion}/collision-blockers.js +0 -0
  579. /package/src/{ardy → motion}/ground.js +0 -0
  580. /package/src/{ardy → motion}/ik-drag.js +0 -0
  581. /package/src/{ardy → motion}/ik-key-json.js +0 -0
  582. /package/src/{ardy → motion}/ik.js +0 -0
  583. /package/src/{ardy → motion}/physics-surface.js +0 -0
  584. /package/src/{ardy → motion}/playback-clock.js +0 -0
  585. /package/src/{ardy → motion}/range-pin.js +0 -0
@@ -1,302 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * extract-bench.mjs — GVHMR extraction bench (#430, validation step T2).
4
- *
5
- * Feeds T1 ground-truth renders (<input>/<motion>/<variant>/video.mp4 +
6
- * camera.json) to the GPU box under named conditions and converts every
7
- * result with production's post-processing, so T3 can score conditions
8
- * against the ground truth without re-running anything:
9
- *
10
- * <out>/<motion>/<variant>/<condition>/
11
- * gvhmr.npz the runner's SMPL output, untouched
12
- * motion.npz cskel27, smplToCskel27Motion -> stabilizeMotion ->
13
- * guardTrajectoryFloor, written like /ardy/extract writes it
14
- * log.txt the runner's log, lines prefixed with elapsed seconds
15
- * result.json flags, detection counts, timings, quality report,
16
- * stabilisation and trajectory diagnostics
17
- *
18
- * `prod` is handleExtract's path step for step (fps conform, upload, the
19
- * bridge's persistent worker with its request, conversion with the env
20
- * defaults); the other conditions call cclay_gvhmr_extract.py directly with
21
- * one variable changed. Runs are serial, on the box they use only
22
- * /tmp/cclay-bench-<stamp>/ (removed at exit) — the one exception is the
23
- * persistent worker's own content-addressed code dir, which gvhmrWorker owns.
24
- * Pure logic: extract-bench-lib.mjs.
25
- */
26
- import { spawn, spawnSync } from "node:child_process";
27
- import { createHash, randomBytes } from "node:crypto";
28
- import { appendFileSync, existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
29
- import { tmpdir } from "node:os";
30
- import { dirname, join, resolve } from "node:path";
31
- import { fileURLToPath } from "node:url";
32
- import { conformToExtractFps } from "../ardy/footage.mjs";
33
- import { qualityReportForMotion } from "../ardy/extract.mjs";
34
- import { guardTrajectoryFloor } from "../ardy/gvhmr-floor.mjs";
35
- import { stabilizeMotion } from "../ardy/motion-stabilize.mjs";
36
- import { motionArraysToNpzMembers, writeNpz } from "../ardy/npz.mjs";
37
- import { gvhmrWorker } from "../ardy/runners/gvhmr-worker.mjs";
38
- import { globalChildren, killGroup, streamLines, track } from "../ardy/runners/proc.mjs";
39
- import { smplToCskel27Motion } from "../ardy/smpl-cskel27.mjs";
40
- import { readNpz } from "../kimodo/read-npz.mjs";
41
- import { USAGE, parseArgs, parseRunnerLog, resolveCondition } from "./extract-bench-lib.mjs";
42
-
43
- const HERE = dirname(fileURLToPath(import.meta.url));
44
- const REPO = resolve(HERE, "..", "..");
45
- const GVHMR_DIR = "~/cclay-ingest/GVHMR";
46
- const LAUNCHER = join(HERE, "cclay_bench_runner.py");
47
- const RUN_TIMEOUT_MS = 30 * 60 * 1000;
48
- // Same ssh options handleExtract uses (CCLAY_EXTRACT_SSH_PORT included) — the
49
- // worker client is keyed on them, as the bridge's is.
50
- const SSH_BASE_OPTS = ["-o", "BatchMode=yes", "-o", "ConnectTimeout=15"];
51
- const SSH_PORT = process.env.CCLAY_EXTRACT_SSH_PORT?.trim() || "";
52
- const SSH_OPTS = SSH_PORT ? [...SSH_BASE_OPTS, "-p", SSH_PORT] : SSH_BASE_OPTS;
53
- const SCP_OPTS = SSH_PORT ? [...SSH_BASE_OPTS, "-P", SSH_PORT] : SSH_BASE_OPTS;
54
-
55
- /** Spawn a child in its own group; resolve on exit 0, reject with its last
56
- * output line otherwise. Every line (split on \r too, for tqdm) -> onLine. */
57
- function exec(command, args, { onLine, timeoutMs = 0 } = {}) {
58
- return new Promise((resolvePromise, reject) => {
59
- const child = spawn(command, args, { detached: true, stdio: ["ignore", "pipe", "pipe"] });
60
- track(child);
61
- let last = "";
62
- const feed = (line) => {
63
- for (const part of line.split("\r")) {
64
- if (!part.trim()) continue;
65
- last = part;
66
- onLine?.(part);
67
- }
68
- };
69
- streamLines(child.stdout, feed);
70
- streamLines(child.stderr, feed);
71
- const timer = timeoutMs ? setTimeout(() => { killGroup(child); reject(new Error(`${command} timed out`)); }, timeoutMs) : null;
72
- child.once("error", (err) => { clearTimeout(timer); reject(new Error(`spawn ${command}: ${err.message}`)); });
73
- child.once("close", (code) => {
74
- clearTimeout(timer);
75
- if (code === 0) resolvePromise();
76
- else reject(new Error(last.trim() || `${command} exited ${code}`));
77
- });
78
- });
79
- }
80
-
81
- const ssh = (host, command, options) => exec("ssh", [...SSH_OPTS, host, command], options);
82
- const scp = (from, to, options) => exec("scp", [...SCP_OPTS, from, to], options);
83
- const seconds = (since) => Number(((performance.now() - since) / 1000).toFixed(3));
84
- const readJson = (path) => (existsSync(path) ? JSON.parse(readFileSync(path, "utf8")) : null);
85
-
86
- function commitId() {
87
- const head = spawnSync("git", ["rev-parse", "HEAD"], { cwd: REPO, encoding: "utf8" }).stdout.trim();
88
- const dirty = spawnSync("git", ["status", "--porcelain"], { cwd: REPO, encoding: "utf8" }).stdout.trim();
89
- return head ? `${head}${dirty ? "-dirty" : ""}` : null;
90
- }
91
-
92
- function discoverMotions(input) {
93
- return readdirSync(input, { withFileTypes: true })
94
- .filter((entry) => entry.isDirectory())
95
- .filter((entry) => readdirSync(join(input, entry.name), { withFileTypes: true })
96
- .some((sub) => sub.isDirectory() && existsSync(join(input, entry.name, sub.name, "video.mp4"))))
97
- .map((entry) => entry.name)
98
- .sort();
99
- }
100
-
101
- function buildPlan(options) {
102
- const motions = options.motions ?? discoverMotions(options.input);
103
- const runs = [];
104
- for (const motion of motions) {
105
- for (const variant of options.variants) {
106
- const source = join(options.input, motion, variant);
107
- for (const condition of options.conditions) {
108
- const camera = readJson(join(source, "camera.json"));
109
- runs.push({
110
- motion, variant, source,
111
- out: join(options.out, motion, variant, condition.name),
112
- plan: resolveCondition(condition, { cameraFMm: camera?.gvhmrFMm ?? null, env: process.env }),
113
- });
114
- }
115
- }
116
- }
117
- return runs;
118
- }
119
-
120
- async function runOne(run, { host, benchDir, worker, commit }) {
121
- const started = performance.now();
122
- const { motion, variant, source, out, plan } = run;
123
- rmSync(out, { recursive: true, force: true });
124
- mkdirSync(out, { recursive: true });
125
- const logPath = join(out, "log.txt");
126
- writeFileSync(logPath, "");
127
- const log = (line) => appendFileSync(logPath, `[+${((performance.now() - started) / 1000).toFixed(2)}s] ${line}\n`);
128
- const video = join(source, "video.mp4");
129
- const videoBytes = readFileSync(video);
130
- const camera = readJson(join(source, "camera.json"));
131
- const meta = readJson(join(source, "meta.json"));
132
- const timings = {};
133
- const result = {
134
- ok: false,
135
- tool: "tools/bench/extract-bench.mjs",
136
- commit,
137
- createdAt: new Date().toISOString(),
138
- motion, variant, condition: plan.name, base: plan.base, fmm: plan.fmm, host,
139
- input: {
140
- video, sha256: createHash("sha256").update(videoBytes).digest("hex"), bytes: videoBytes.length,
141
- camera: camera && { width: camera.width, height: camera.height, fx: camera.fx, gvhmrFMm: camera.gvhmrFMm ?? null },
142
- meta: meta && { frames: meta.frames, fps: meta.fps, variant: meta.variant, source: meta.source?.path ?? null },
143
- },
144
- flags: {
145
- path: plan.path, detector: plan.detector, keypoints: plan.keypoints, fMm: plan.fMm,
146
- ...(plan.path === "direct" ? { runnerArgs: plan.runnerArgs, launcher: plan.detector === "palette" ? "runner" : "bench-launcher" } : { workerRequest: plan.workerFields }),
147
- stabilize: plan.stabilize,
148
- },
149
- timings,
150
- };
151
- const localTmp = mkdtempSync(join(tmpdir(), "cclay-bench-"));
152
- const runKey = `${motion}-${variant}-${plan.name}`.replace(/[^A-Za-z0-9_.-]/g, "_");
153
- const remoteDir = `${benchDir}/${runKey}`;
154
- const remoteVideo = `${remoteDir}/video.mp4`;
155
- const remoteNpz = `${remoteDir}/gvhmr.npz`;
156
- const outRoot = `${remoteDir}/gvhmr-out`;
157
- let step = "conform";
158
- try {
159
- // handleExtract's rate ceiling: a ≤30 fps render passes through as is.
160
- let at = performance.now();
161
- const conformed = await conformToExtractFps(video, join(localTmp, "capped.mp4"));
162
- result.conform = { fps: conformed.fps, capped: conformed.capped };
163
- timings.conformS = seconds(at);
164
-
165
- step = "upload";
166
- at = performance.now();
167
- await ssh(host, `umask 077 && mkdir -p '${remoteDir}'`, { timeoutMs: 60000 });
168
- await scp(conformed.path, `${host}:${remoteVideo}`, { timeoutMs: 300000 });
169
- timings.uploadS = seconds(at);
170
-
171
- step = "extract";
172
- at = performance.now();
173
- let performanceReport = null;
174
- if (plan.path === "worker") {
175
- const request = { video: remoteVideo, output: remoteNpz, outRoot, ...plan.workerFields };
176
- log(`[bench] worker request ${JSON.stringify(request)}`);
177
- performanceReport = await worker().run(request, { timeoutMs: RUN_TIMEOUT_MS, onLine: log });
178
- } else {
179
- const program = plan.detector === "palette"
180
- ? "cclay_gvhmr_extract.py"
181
- : `${benchDir}/cclay_bench_runner.py cclay_gvhmr_extract.py`;
182
- const command = `cd ${GVHMR_DIR} && .venv/bin/python ${program} ${remoteVideo} ${remoteNpz} ${plan.runnerArgs.join(" ")} --out-root ${outRoot}`;
183
- log(`[bench] ssh ${command}`);
184
- await ssh(host, command, { timeoutMs: RUN_TIMEOUT_MS, onLine: log });
185
- }
186
- timings.extractS = seconds(at);
187
- result.performance = performanceReport;
188
-
189
- step = "fetch";
190
- at = performance.now();
191
- const gvhmrNpz = join(out, "gvhmr.npz");
192
- await scp(`${host}:${remoteNpz}`, gvhmrNpz, { timeoutMs: 120000 });
193
- timings.fetchS = seconds(at);
194
-
195
- step = "convert";
196
- at = performance.now();
197
- const events = performanceReport?.trajectory?.events ?? [];
198
- const converted = stabilizeMotion(smplToCskel27Motion(readNpz(gvhmrNpz)), plan.stabilize);
199
- const guarded = guardTrajectoryFloor(converted, events);
200
- result.quality = qualityReportForMotion(guarded.motion);
201
- if (events.length) performanceReport.trajectory.floor = guarded.diagnostics;
202
- writeNpz(join(out, "motion.npz"), motionArraysToNpzMembers(guarded.motion));
203
- timings.convertS = seconds(at);
204
- result.motionInfo = { frames: guarded.motion.frames, fps: guarded.motion.fps, personScale: guarded.motion.personScale };
205
- result.stabilization = guarded.motion.stabilization ?? null;
206
- result.trajectory = { events, floor: guarded.diagnostics };
207
- result.ok = true;
208
- } catch (err) {
209
- result.error = { step, message: err.message };
210
- log(`[bench] FAILED at ${step}: ${err.message}`);
211
- } finally {
212
- rmSync(localTmp, { recursive: true, force: true });
213
- await ssh(host, `rm -rf '${remoteDir}'`, { timeoutMs: 60000 }).catch((err) => log(`[bench] remote cleanup failed: ${err.message}`));
214
- }
215
- const summary = parseRunnerLog(readFileSync(logPath, "utf8"));
216
- const segmentation = result.performance?.segmentation;
217
- result.detection = summary.detectedFrames != null
218
- ? { detector: summary.detector, detectedFrames: summary.detectedFrames, totalFrames: summary.totalFrames, detectionRate: summary.detectionRate, source: summary.detectionSource }
219
- : segmentation?.detectedFrames != null
220
- ? { detector: segmentation.detector, detectedFrames: segmentation.detectedFrames, totalFrames: segmentation.frames, detectionRate: segmentation.detectionRate, source: "worker-segmentation" }
221
- : { detector: summary.detector, detectedFrames: null, totalFrames: null, detectionRate: null, source: null };
222
- result.runnerLog = summary;
223
- timings.totalS = seconds(started);
224
- writeFileSync(join(out, "result.json"), `${JSON.stringify(result, null, "\t")}\n`);
225
- return result;
226
- }
227
-
228
- async function main() {
229
- let options;
230
- try {
231
- options = parseArgs(process.argv.slice(2), process.env);
232
- } catch (err) {
233
- console.error(`extract-bench: ${err.message}\n\n${USAGE}`);
234
- process.exit(2);
235
- }
236
- if (options.help) {
237
- console.log(USAGE);
238
- return;
239
- }
240
- options.input = resolve(options.input);
241
- options.out = resolve(options.out);
242
- const runs = buildPlan(options);
243
- for (const run of runs) {
244
- if (!existsSync(join(run.source, "video.mp4"))) throw new Error(`missing input ${join(run.source, "video.mp4")}`);
245
- }
246
- if (options.dryRun) {
247
- for (const { motion, variant, plan } of runs) {
248
- const flags = plan.path === "worker" ? JSON.stringify(plan.workerFields) : plan.runnerArgs.join(" ");
249
- console.log(`${motion}/${variant}/${plan.name}\t${plan.path}\t${flags}`);
250
- }
251
- return;
252
- }
253
-
254
- const host = options.host;
255
- const commit = commitId();
256
- const benchDir = `/tmp/cclay-bench-${Date.now()}-${randomBytes(3).toString("hex")}`;
257
- let client = null;
258
- const worker = () => (client ??= gvhmrWorker({ host, sshOptions: SSH_OPTS, scpOptions: SCP_OPTS }));
259
- const cleanupRemote = () => spawnSync("ssh", [...SSH_OPTS, host, `rm -rf '${benchDir}'`], { stdio: "ignore", timeout: 60000 });
260
- process.once("SIGINT", () => {
261
- console.error("extract-bench: interrupted; stopping children and removing the box temp dir");
262
- client?.stop("extract-cancelled");
263
- for (const child of globalChildren) killGroup(child);
264
- cleanupRemote();
265
- process.exit(130);
266
- });
267
-
268
- await ssh(host, `umask 077 && mkdir -p '${benchDir}'`, { timeoutMs: 60000 });
269
- if (runs.some((run) => run.plan.path === "direct" && run.plan.detector !== "palette")) {
270
- await scp(LAUNCHER, `${host}:${benchDir}/cclay_bench_runner.py`, { timeoutMs: 60000 });
271
- }
272
- let failures = 0;
273
- try {
274
- for (const run of runs) {
275
- const label = `${run.motion}/${run.variant}/${run.plan.name}`;
276
- const previous = readJson(join(run.out, "result.json"));
277
- if (previous?.ok && !options.force) {
278
- console.log(`skip ${label} (result.json ok; --force re-runs)`);
279
- continue;
280
- }
281
- console.log(`run ${label} [${run.plan.path}]`);
282
- const result = await runOne(run, { host, benchDir, worker, commit });
283
- if (!result.ok) failures += 1;
284
- const d = result.detection;
285
- console.log(`${result.ok ? "ok " : "FAIL"} ${label} detected ${d.detectedFrames ?? "?"}/${d.totalFrames ?? "?"} (${d.detector ?? "?"})` +
286
- ` extract ${result.timings.extractS ?? "-"} s total ${result.timings.totalS} s` +
287
- (result.ok ? ` quality ${result.quality.pass ? "pass" : "fail"}` : ` — ${result.error.step}: ${result.error.message}`));
288
- }
289
- } finally {
290
- client?.stop("extract-bench-finished");
291
- cleanupRemote();
292
- }
293
- if (failures) {
294
- console.error(`extract-bench: ${failures} run(s) failed`);
295
- process.exitCode = 1;
296
- }
297
- }
298
-
299
- main().catch((err) => {
300
- console.error(`extract-bench: ${err.stack ?? err.message}`);
301
- process.exit(1);
302
- });
@@ -1,88 +0,0 @@
1
- #!/usr/bin/env node
2
- import { execFileSync } from "node:child_process";
3
- import { createHash } from "node:crypto";
4
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
5
- import { basename, join, resolve } from "node:path";
6
- import { fileURLToPath } from "node:url";
7
- import { parseArgs } from "node:util";
8
- import { setTimeout as delay } from "node:timers/promises";
9
- import { buildH3LockedPrompt } from "../../bin/agent/video-adapters.mjs";
10
-
11
- export const FAL_MODEL = "minimax/h3-max-turbo/image-to-video";
12
- export function falRequest({ prompt, firstImage, lastImage }) {
13
- if (!String(prompt ?? "").trim() || !firstImage || !lastImage) throw new Error("prompt and both endpoint images are required");
14
- // H3's documented contract has no seed field: independent requests, not
15
- // a claimed deterministic seed sweep. Do not silently send ignored seeds.
16
- return { prompt: buildH3LockedPrompt(prompt), image_url: firstImage, end_image_url: lastImage, resolution: "480P", duration: 5, prompt_expansion_mode: "disabled" };
17
- }
18
- export function costFields(value) {
19
- if (!value || typeof value !== "object") return {};
20
- const out = {};
21
- for (const [key, entry] of Object.entries(value)) {
22
- if (/cost|price|billing|usage/i.test(key)) out[key] = entry;
23
- else if (entry && typeof entry === "object") { const nested = costFields(entry); if (Object.keys(nested).length) out[key] = nested; }
24
- }
25
- return out;
26
- }
27
- /** Unbounded queue wait, finite individual HTTP requests. Every response is
28
- * logged by the caller. Test injects fetch + wait, never wall-clock polling. */
29
- export async function runFal(body, { key, fetchImpl = fetch, wait = () => delay(5000), onEvent = () => {} }) {
30
- if (!key) throw new Error("fal key is required");
31
- const request = async (url, options = {}) => {
32
- const response = await fetchImpl(url, { ...options, headers: { authorization: `Key ${key}`, ...options.headers }, signal: AbortSignal.timeout(120000) });
33
- if (!response.ok) throw new Error(`fal HTTP ${response.status} at ${new URL(url).pathname}`);
34
- const value = await response.json(); onEvent({ at: new Date().toISOString(), url, response: value, cost: costFields(value) }); return value;
35
- };
36
- const queued = await request(`https://queue.fal.run/${FAL_MODEL}`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body) });
37
- let status = queued;
38
- for (;;) {
39
- const video = status.video?.url ?? status.output?.video?.url ?? status.video_url;
40
- if (video) return { queued, result: status, video };
41
- if (["FAILED", "CANCELLED"].includes(status.status)) throw new Error(`fal ${status.status}: ${status.error ?? "no error detail"}`);
42
- if (status.status === "COMPLETED") {
43
- const url = status.response_url ?? queued.response_url;
44
- if (!url) throw new Error("fal completed without response_url");
45
- status = await request(url);
46
- if (!(status.video?.url ?? status.output?.video?.url ?? status.video_url)) throw new Error("fal completed without video URL");
47
- continue;
48
- }
49
- const url = status.status_url ?? queued.status_url;
50
- if (!url) throw new Error("fal queue response lacks status_url");
51
- await wait(); status = await request(url);
52
- }
53
- }
54
- const sha = bytes => createHash("sha256").update(bytes).digest("hex");
55
- export async function main(argv = process.argv.slice(2)) {
56
- const { values: v } = parseArgs({ args: argv, options: { scenario: { type: "string" }, variant: { type: "string", default: "shaded" }, out: { type: "string" }, prompt: { type: "string" }, key: { type: "string" }, runs: { type: "string", default: "3" }, "dry-run": { type: "boolean" }, help: { type: "boolean" } } });
57
- if (v.help) { console.log("usage: node tools/bench/fal-generate.mjs --scenario <GT motion dir> --out <clips root> [--prompt <action>] [--key <file>] [--runs 3] [--variant shaded|skin] [--dry-run]\nReads scenario.json prompt or --prompt; writes <out>/<scenario>-<variant>-01/{video.mp4,request.json,A.png,B.png}. No seed is sent (independent H3 requests)."); return; }
58
- if (!v.scenario || !v.out) throw new Error("--scenario and --out required");
59
- const runs = Number(v.runs); if (!Number.isInteger(runs) || runs < 1) throw new Error("--runs must be a positive integer");
60
- if (!["shaded", "skin"].includes(v.variant)) throw new Error("--variant must be shaded or skin");
61
- const scenario = resolve(v.scenario), name = basename(scenario), source = join(scenario, v.variant);
62
- const meta = JSON.parse(readFileSync(join(source, "meta.json"))), info = existsSync(join(scenario, "scenario.json")) ? JSON.parse(readFileSync(join(scenario, "scenario.json"))) : {};
63
- const prompt = v.prompt ?? info.prompt;
64
- const still = frame => execFileSync("ffmpeg", ["-v", "error", "-i", join(source, "video.mp4"), "-vf", `select=eq(n\\,${frame})`, "-frames:v", "1", "-f", "image2pipe", "-c:v", "png", "pipe:1"], { maxBuffer: 10 * 1024 * 1024 });
65
- const a = still(0), b = still(meta.frames - 1);
66
- if (!a.length || !b.length) throw new Error("missing endpoint frame");
67
- const body = falRequest({ prompt, firstImage: `data:image/png;base64,${a.toString("base64")}`, lastImage: `data:image/png;base64,${b.toString("base64")}` });
68
- const keyPath = v.key ?? resolve(process.env.HOME, "ccFalToMocap/.fal-key");
69
- const dry = v["dry-run"] || !existsSync(keyPath);
70
- const key = dry ? null : readFileSync(keyPath, "utf8").trim();
71
- for (let i = 1; i <= runs; i++) {
72
- const dir = join(resolve(v.out), `${name}-${v.variant}-${String(i).padStart(2, "0")}`); mkdirSync(dir, { recursive: true });
73
- const recordPath = join(dir, "request.json");
74
- if (existsSync(recordPath) && JSON.parse(readFileSync(recordPath)).ok && existsSync(join(dir, "video.mp4"))) { console.log(`skip completed ${basename(dir)}`); continue; }
75
- writeFileSync(join(dir, "A.png"), a); writeFileSync(join(dir, "B.png"), b);
76
- const record = { model: FAL_MODEL, scenario, variant: v.variant, run: i, independentRun: true, seed: null, dryRun: dry, ok: false, body: { ...body, image_url: "A.png", end_image_url: "B.png" }, endpoints: { A: sha(a), B: sha(b), sourceFrames: [0, meta.frames - 1] }, events: [], cost: null };
77
- const save = () => writeFileSync(recordPath, JSON.stringify(record, null, 2) + "\n"); save();
78
- if (dry) { console.log(`dry-run ${basename(dir)}: ${v["dry-run"] ? "requested" : "key missing"}`); continue; }
79
- try {
80
- const result = await runFal(body, { key, onEvent: event => { record.events.push(event); save(); console.log(`[${basename(dir)}] ${event.response.status ?? "result"} ${event.response.request_id ?? ""}`); } });
81
- const response = await fetch(result.video, { signal: AbortSignal.timeout(300000) }); if (!response.ok) throw new Error(`video HTTP ${response.status}`);
82
- writeFileSync(join(dir, "video.mp4"), Buffer.from(await response.arrayBuffer()));
83
- record.ok = true; record.requestId = result.queued.request_id; record.result = result.result;
84
- record.cost = record.events.map(e => e.cost).filter(c => Object.keys(c).length); record.costNote = record.cost.length ? "Provider-reported fields, not estimated." : "Provider supplied no cost fields; unknown, not zero.";
85
- } catch (error) { record.error = error.message; throw error; } finally { save(); }
86
- }
87
- }
88
- if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) main().catch(e => { console.error(e.stack); process.exitCode = 1; });
@@ -1,189 +0,0 @@
1
- # F0-F5 cumulative fitting bench (#432)
2
-
3
- ```sh
4
- node tools/bench/fit-bench.mjs \
5
- --input /path/to/evidence/gt \
6
- --extract /path/to/evidence/exp1/extract \
7
- --poses /path/to/evidence/prep/gt-motions \
8
- --out /path/to/evidence/exp2/fit \
9
- --variant shaded --host yun@ubuntu-baremetal \
10
- --motions walk,run,turn,sit,standup,jump,pickup,stepup
11
-
12
- # Native YOLO/ViTPose on the grey mannequin. F0 exists in T2; if F1
13
- # yolo-vitpose+fmm is absent, fit-bench extracts it serially with --host.
14
- node tools/bench/fit-bench.mjs \
15
- --input /path/to/evidence/gt --extract /path/to/evidence/exp1/extract \
16
- --poses /path/to/evidence/prep/gt-motions --out /path/to/evidence/exp2/fit-skin \
17
- --variant skin --base-condition yolo-vitpose --host yun@ubuntu-baremetal
18
-
19
- # Evaluation ONLY. This program's output is never consumed by fitting.
20
- node tools/bench/fit-sanity.mjs /path/to/evidence/exp2/fit /path/to/evidence/prep/gt-motions
21
- ```
22
-
23
- Each `<out>/<motion>/F0..F5/motion.npz` is a normal cskel27 archive:
24
- `local_rot_mats`, `root_positions`, `posed_joints`, `fps`, with production's
25
- optional `bone_scale` / `person_scale`. `result.json` records provenance,
26
- registration, configuration, contact diagnostics and deviations. Camera evidence
27
- is saved to `<out>/<motion>/incam.npz`, with its run log and cache manifest.
28
- A different variant or base condition needs a different output root. Existing
29
- mismatched results are rejected, not silently overwritten. `--base-condition`
30
- defaults to `prod` (the existing F0/F1 motion bytes are unchanged); it accepts a
31
- T2 base condition without `+fmm`. F0 is copied from that condition and F1 from
32
- the same condition with `+fmm`. T2 already supports `yolo-vitpose+fmm` for direct
33
- runs. If only F1 is missing, the CLI invokes T2 for that one motion/variant/
34
- condition after the idle-GPU check. It never substitutes F0 for missing F1.
35
-
36
- ## Information boundary
37
-
38
- **No fitting stage reads or measures GT intermediate motion.** The allowed
39
- inputs are extracted video motion, known camera, known character body, the
40
- user's two endpoint poses and scene geometry. `readEndpoints` decodes a source
41
- archive but exposes only its first/last rotation matrices and roots. It never
42
- examines intermediate arrays, joint trajectories or GT body measurements. A
43
- test poisons all other source data and exercises the real CLI successfully.
44
- `fit-sanity.mjs` is a separate read-only evaluator and is the only code here
45
- that consumes the complete reference trajectory.
46
-
47
- | Stage | New information | Operation |
48
- |---|---|---|
49
- | F0 | None | Byte-identical T2 `<base-condition>/motion.npz` (default `prod`). |
50
- | F1 | Known focal | Byte-identical T2 `<base-condition>+fmm/motion.npz`. |
51
- | F2 | Known static OpenCV camera extrinsics | Register the first F1 pelvis/orientation to the predicted camera-space pelvis/orientation, then apply the same rigid transform to every frame. |
52
- | F3 | Known character bone factors | Regenerate FK from F2 rotations with canonical offsets times the character's factors. Defaults to all ones, equivalent to `boneScale:1`. |
53
- | F4 | User's A and B poses only | Slerp local rotations toward A/B; smoothstep endpoint root-offset ramps. Default 0.5 s, capped at half the clip. |
54
- | F5 | Floor Y=0 and optional axis-aligned boxes | Low/slow stance detection, rigid support locking, floor and skeletal bone/box penetration resolution. |
55
-
56
- ## F2: why one static registration
57
-
58
- The new `cclay_bench_extract_incam.py` imports the box's **unchanged**
59
- `cclay_gvhmr_extract.py` and runs its `main()` under the same imported
60
- `FastRuntime` and `trajectory_job` wrappers as the production worker. It passes
61
- `--static-cam`, known `--f-mm`, production detector/keypoint flags, and
62
- production smoothing sigma. It does not copy the model or preprocessing
63
- pipeline and does not edit anything in the GVHMR checkout. For a direct T2
64
- condition such as `yolo-vitpose`, the camera launcher uses the same YOLO and
65
- ViTPose flags and **does not** enable `FastRuntime` or `trajectory_job`. The
66
- bench-only `--bench-direct` switch is removed before calling the runner. This
67
- keeps camera evidence matched to F1 rather than quietly using palette evidence
68
- for a YOLO fit. The condition is included in cache signatures and result metadata.
69
-
70
- The launcher adds the raw in-camera SMPL-X parameters and recomputes the
71
- camera pelvis with the same SMPL-X -> SMPL mesh regressor as the runner.
72
- **SMPL model `transl` is not its mesh-regressed pelvis**, so substituting it
73
- would introduce a shape-dependent translation error. It smooths camera pelvis
74
- and root orientation with the runner's own `smooth_motion_params`.
75
-
76
- All transforms multiply column vectors. Added NPZ members:
77
-
78
- | Member | Shape | Meaning |
79
- |---|---|---|
80
- | `incam_raw_global_orient`, `incam_raw_transl` | T,3 | Raw camera-space SMPL-X model parameters. |
81
- | `incam_raw_body_pose`, `incam_raw_betas` | T,63 / T,10 | Raw model articulation and shape. |
82
- | `incam_global_orient`, `incam_pelvis` | T,3 | Smoothed camera orientation and mesh-regressed pelvis. |
83
- | `ayfz_to_camera` | T,4,4 | Per-frame relation between output ayfz pelvis/orientation and the camera prediction. |
84
- | `ay_to_ayfz` | 4,4 | Runner's exact pre-smoothing normalization, including floor and heading. |
85
- | `ay_global_orient`, `ay_transl` | T,3 | Raw predicted gravity-aligned model parameters. |
86
- | `K_fullimg` | T,3,3 | Intrinsics actually used by GVHMR. |
87
-
88
- The per-frame `ayfz_to_camera` is diagnostic. Camera-space depth and the
89
- integrated global trajectory can disagree even with a static camera. Applying
90
- the entire time-varying relation would replace F1's trajectory with noisy
91
- monocular camera translation. Instead F2 preserves production's trajectory
92
- (including stabilization/contact anchoring and any descent/floor correction)
93
- and only fixes its initial reference frame:
94
-
95
- ```
96
- R_f1_to_cam = R_incam[0] * transpose(R_f1[0])
97
- R = transpose(R_world_to_cam) * R_f1_to_cam
98
- p_world = transpose(R_world_to_cam) * (p_incam[0] - t_world_to_cam)
99
- t = p_world - R * p_f1[0]
100
- p_F2[f] = R * p_F1[f] + t
101
- ```
102
-
103
- Only the Hips local rotation is multiplied by `R`; child local rotations are
104
- unchanged. All positional channels get the same SE(3). No GT roots, floor
105
- alignment, heading search, scale search or Procrustes fit enters F2. Camera
106
- JSON must be static and a proper rigid OpenCV transform. Its `worldToCamera`
107
- (y down, z forward), not the Three/OpenGL matrix, is inverted. Camera evidence
108
- intrinsics must agree with the known focal.
109
-
110
- ## Deliberate conversion deviations
111
-
112
- - F0/F1 are untouched production archives: `smplToCskel27Motion`,
113
- `stabilizeMotion`, and `guardTrajectoryFloor` have already run in T2.
114
- - F2 is cumulative on **that F1**, not a fresh conversion of the additional
115
- inference. The new inference supplies camera evidence only. Its first
116
- pelvis replaces production's arbitrary initial floor datum; re-grounding
117
- here would undo camera placement. Static registration uses one frame and
118
- inherits that frame's monocular depth/orientation uncertainty.
119
- - F3 keeps F2's root in metres and `personScale=1`; it does not scale the
120
- camera translation to compensate for changed body proportions. It rebuilds
121
- positions by FK, avoiding production's independently smoothed joint arrays
122
- when those disagree with constant bone lengths. There is no second
123
- stabilization, foot anchoring or percentile-floor pass.
124
- - Character factors default to canonical cskel27 (all ones), not a fresh
125
- measurement of the rendered y-bot mesh. `--body body.json` accepts the known
126
- character's 27 positive per-bone factors. This follows the issue's
127
- `boneScale:1` option and leaves playback's own rig mapping intact.
128
- - F4 preserves F3 bone lengths and uses source NPZ first/last poses exactly as
129
- requested. The last source frame is pinned even when rate conversion puts
130
- the last rendered sample slightly before it (180@30 vs 144@24).
131
- - F5 is a **skeletal**, not skinned-mesh, constraint pass. One support point at
132
- a time is locked by rigid root translation, preserving rotations and bone
133
- vectors. Eligible feet/toes are within 4 cm of a known surface and move at
134
- <=0.18 m/s for >=0.08 s. A hand can support on a known box face; no box means
135
- no invented hand contact. Smoothstep bridges carry offsets between runs.
136
- Simultaneous two-foot or hand/foot contacts are not a multi-effector IK solve.
137
- - Box collision tests include open-interior bone segments, not just joints.
138
- On collision a conservative root shift clears the whole skeleton along a
139
- separating axis, preserving lengths. No skin radius, self-collision or
140
- collision-free swept trajectory between frames is claimed. Boxes can force
141
- substantial displacement. Conflicting projections fail rather than silently
142
- emit penetrations. Floor/scene safety wins over incompatible locks and A/B
143
- pins; `sceneOverrides` and `maxLockResidualM` disclose this.
144
-
145
- Scene format (`--scene`):
146
-
147
- ```json
148
- {"boxes":[{"min":[-0.5,0,-0.5],"max":[0.5,1,0.5]}]}
149
- ```
150
-
151
- ## GPU ownership and caches
152
-
153
- Wait until other T2 sweeps have finished. The CLI refuses any other GVHMR
154
- Python process, including an idle persistent worker, and checks `nvidia-smi`
155
- compute memory before upload and again immediately before inference. This is
156
- a safety check, not a lock respected by independently launched jobs; callers
157
- must coordinate those launches. No other process is killed. The bench runs
158
- serially and uses only `/tmp/cclay-fit-*` on the box; the run directory is
159
- removed in success/failure paths, and cleanup failure is an error. Abrupt
160
- process termination or a broken SSH connection can require manual cleanup.
161
-
162
- Local camera caches bind the video, focal/flags and deployed code hashes. The
163
- box runner SHA is recorded. The runner is externally managed: if it changes,
164
- use `--force-incam`. `--incam-root` supplies already extracted
165
- `<root>/<motion>/incam.npz` without touching the GPU (also the CPU integration
166
- test path).
167
-
168
- ## Verification and sanity metric
169
-
170
- ```sh
171
- node test/verify-fit-bench.mjs
172
- npm run build # dist is required by an existing suite
173
- node tools/run-tests.mjs
174
- ```
175
-
176
- Tests cover camera inverse and proper-rotation validation, rigid trajectory
177
- registration, body/FK preservation, endpoint slerp and antipodes, short clips,
178
- falloff boundaries, floor and stance locking, segment/box crossings, bone
179
- length preservation, archive round trips, byte-identical F0/F1, and the real
180
- CLI with poisoned intermediate GT data. No timing waits or external GPU are
181
- needed by tests.
182
-
183
- `fit-sanity.mjs` compares output cskel27 `posed_joints` with source NPZ
184
- `posed_joints` at matching seconds (linear interpolation of reference
185
- positions). Root-relative error averages joints 1..26; root error is Hips.
186
- There is **no alignment**. It writes per-motion/per-stage and aggregate tables
187
- in millimetres. These are not official rendered-rig errors: Studio playback
188
- applies rig scale and bind offsets, F2 is scene metres, and the requested F4
189
- source endpoints are native cskel27. Official grading by #431 remains separate.
@@ -1,59 +0,0 @@
1
- import { matMul, matTranspose, quatToMat } from "../../../src/ardy/convert.js";
2
- import { add, cloneMotion, matVec, member, readMat, sub, vec } from "./motion.mjs";
3
-
4
- export function axisAngleMatrix(v) {
5
- const angle = Math.hypot(...v), scale = angle < 1e-12 ? 0.5 : Math.sin(angle / 2) / angle;
6
- return quatToMat([Math.cos(angle / 2), ...v.map(x => x * scale)]);
7
- }
8
-
9
- /** OpenCV x-right/y-down/z-forward, NOT the OpenGL camera rotation. */
10
- export function cameraToWorld(camera) {
11
- const t = camera.worldToCamera;
12
- if (!camera.static || !Array.isArray(t) || t.length !== 4 || !t.every(row => Array.isArray(row) && row.length === 4 && row.every(Number.isFinite))) {
13
- throw new Error("camera: expected static OpenCV worldToCamera [4,4]");
14
- }
15
- const r = t.slice(0, 3).map(row => row.slice(0, 3)), rt = matTranspose(r);
16
- const identity = matMul(r, rt);
17
- const det = r[0][0] * (r[1][1] * r[2][2] - r[1][2] * r[2][1]) - r[0][1] * (r[1][0] * r[2][2] - r[1][2] * r[2][0]) + r[0][2] * (r[1][0] * r[2][1] - r[1][1] * r[2][0]);
18
- if (identity.some((row, i) => row.some((x, j) => Math.abs(x - Number(i === j)) > 1e-5)) || Math.abs(det - 1) > 1e-5 || t[3].some((x, i) => Math.abs(x - Number(i === 3)) > 1e-6)) {
19
- throw new Error("camera: worldToCamera must be a rigid proper transform");
20
- }
21
- return { rotation: rt, translation: matVec(rt, t.slice(0, 3).map(row => -row[3])) };
22
- }
23
-
24
- export function transformMotion(motion, { rotation, translation }) {
25
- const out = cloneMotion(motion);
26
- for (let f = 0; f < motion.frames; f++) {
27
- out.rootPos.set(add(matVec(rotation, vec(motion.rootPos, f * 3)), translation), f * 3);
28
- out.rotMats.set(matMul(rotation, readMat(motion.rotMats, f * 243)).flat(), f * 243);
29
- for (let j = 0; j < 27; j++) {
30
- const o = (f * 27 + j) * 3;
31
- out.posedJoints.set(add(matVec(rotation, vec(motion.posedJoints, o)), translation), o);
32
- }
33
- }
34
- return out;
35
- }
36
-
37
- /** F2: static registration, never a trajectory fit to GT. The new box run
38
- * supplies the camera-space pelvis and orientation. F1 supplies the SAME
39
- * production-smoothed ayfz root orientation. Register their first frames,
40
- * then apply ONE rigid transform to all of F1. This removes the arbitrary
41
- * initial heading/origin/floor datum while preserving production trajectory,
42
- * body, stabilisation and trajectory-floor safety relative to that datum.
43
- * Per-frame world->camera transforms are saved as diagnostics, not used as
44
- * a moving camera: their drift includes monocular depth/velocity disagreement.
45
- */
46
- export function registerCamera(motion, incam, camera) {
47
- const n = incam.incam_pelvis?.shape[0];
48
- if (n !== motion.frames) throw new Error("incam and F1 frame counts disagree");
49
- const pelvis = member(incam, "incam_pelvis", [n, 3]);
50
- const orient = member(incam, "incam_global_orient", [n, 3]);
51
- if (Math.abs(member(incam, "fps", [])[0] - motion.fps) > 1e-6) throw new Error("incam and F1 fps disagree");
52
- const c2w = cameraToWorld(camera);
53
- const f1ToCamera = matMul(axisAngleMatrix(vec(orient)), matTranspose(readMat(motion.rotMats)));
54
- const rotation = matMul(c2w.rotation, f1ToCamera);
55
- const worldPelvis = add(matVec(c2w.rotation, vec(pelvis)), c2w.translation);
56
- const translation = sub(worldPelvis, matVec(rotation, vec(motion.rootPos)));
57
- return { motion: transformMotion(motion, { rotation, translation }),
58
- diagnostics: { rotation, translation, worldPelvis, anchorFrame: 0, registration: "static-predicted-pelvis", preservesF1RelativeTrajectory: true } };
59
- }