cozyclay 2.1.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (583) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/README.md +12 -30
  3. package/bin/agent/agent-routes.mjs +40 -113
  4. package/bin/agent/agent-runner.mjs +9 -10
  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/studio-motion-reference.mjs +23 -0
  14. package/bin/agent/studio-prompt.mjs +1 -1
  15. package/bin/agent/studio-rebase-text.mjs +32 -0
  16. package/bin/agent/studio-rebase.mjs +26 -39
  17. package/bin/agent/studio-tools.mjs +9 -28
  18. package/bin/agent/tool-arguments.mjs +49 -0
  19. package/bin/agent/turn-frames.mjs +66 -0
  20. package/bin/codex-auth.mjs +9 -2
  21. package/bin/cozyclay.mjs +10 -116
  22. package/bin/live/cli.mjs +22 -6
  23. package/bin/mcp-runtime.mjs +2 -12
  24. package/bin/package-signature.mjs +0 -4
  25. package/dist/app/index.html +7 -6
  26. package/dist/assets/EXRLoader-CvVIvGVE.js +1 -0
  27. package/dist/assets/analytics-C9sUnndd.js +1 -0
  28. package/dist/assets/app-CCh--1Hc.js +5087 -0
  29. package/dist/assets/app-f-fkV5uP.css +1 -0
  30. package/dist/assets/compile-worker-CkxiD6lH.js +5 -0
  31. package/dist/assets/fflate.module-KbGj5-H6.js +5 -0
  32. package/dist/assets/{first-shot-handoff-BlT33CGf.js → first-shot-handoff-CC4xe4HM.js} +1 -1
  33. package/dist/assets/{landing-BSesrCpL.js → landing-DjHD8u7k.js} +1 -1
  34. package/dist/assets/scene-asset-sync-Hx_FzdOH.js +20 -0
  35. package/dist/assets/scene-asset-sync-_S8NQl5B.css +1 -0
  36. package/dist/assets/workflow-C4yKH1p4.js +179 -0
  37. package/dist/cozyclay-package.json +1 -1
  38. package/dist/index.html +3 -3
  39. package/dist/llms.txt +14 -0
  40. package/dist/sw.js +3 -6
  41. package/dist/workflow/index.html +5 -4
  42. package/mcp/LIVE-PROTOCOL.md +7 -47
  43. package/mcp/README.md +16 -26
  44. package/mcp/live-hub.mjs +6 -10
  45. package/mcp/package.json +1 -3
  46. package/mcp/tool-handlers.mjs +4 -168
  47. package/package.json +3 -22
  48. package/src/App.jsx +174 -2639
  49. package/src/agent-progress.js +79 -0
  50. package/src/agent-turn-mark.js +30 -0
  51. package/src/analytics.js +1 -19
  52. package/src/app-context.js +5 -0
  53. package/src/app-stage.jsx +2 -34
  54. package/src/asset-pane.jsx +6 -5
  55. package/src/camera-block.js +21 -0
  56. package/src/camera-direction.js +120 -0
  57. package/src/character-kind.js +3 -9
  58. package/src/command-bus.js +8 -6
  59. package/src/commands/cast.js +5 -34
  60. package/src/commands/elements/character.js +1 -1
  61. package/src/commands/elements/motion.js +1 -6
  62. package/src/commands/elements/object.js +6 -2
  63. package/src/commands/elements/shot.js +4 -1
  64. package/src/commands/elements.js +38 -14
  65. package/src/commands/index.js +3 -1
  66. package/src/commands/kits.js +37 -0
  67. package/src/commands/motion-timing.js +57 -0
  68. package/src/commands/motion.js +9 -152
  69. package/src/commands/motion3-compile.js +65 -0
  70. package/src/commands/motion3.js +207 -0
  71. package/src/commands/objects.js +6 -4
  72. package/src/commands/shot.js +49 -11
  73. package/src/commands/stage.js +14 -1
  74. package/src/commands/view.js +4 -1
  75. package/src/document-mirrors.js +42 -0
  76. package/src/document-store.js +4 -0
  77. package/src/domains/cast.js +19 -194
  78. package/src/domains/motion/motion-range-pin-rules.js +1 -1
  79. package/src/domains/motion/take-source.js +51 -0
  80. package/src/domains/motion.js +60 -1838
  81. package/src/domains/scenes.js +12 -55
  82. package/src/domains/shots.js +2 -2
  83. package/src/domains/stage.js +4 -2
  84. package/src/draft-ghost.js +46 -0
  85. package/src/draft-ghost.jsx +129 -0
  86. package/src/dualview.jsx +4 -3
  87. package/src/effects/EffectsLayer.jsx +112 -0
  88. package/src/effects/effect-meshes.js +232 -0
  89. package/src/effects/effect-record.js +103 -0
  90. package/src/effects/emitter.js +59 -0
  91. package/src/effects/simulate.js +261 -0
  92. package/src/effects/state.js +59 -0
  93. package/src/execution-telemetry.js +2 -3
  94. package/src/first-success-guide.jsx +5 -5
  95. package/src/ghost-set.js +257 -0
  96. package/src/held-object-edit.jsx +113 -0
  97. package/src/held-object-pose.js +175 -0
  98. package/src/hierarchy-model.js +1 -1
  99. package/src/hierarchy-panel.jsx +1 -1
  100. package/src/imported-mesh.jsx +103 -0
  101. package/src/kits/held.js +84 -0
  102. package/src/kits/index.js +70 -0
  103. package/src/kits/smoking.js +57 -0
  104. package/src/light-blockers.js +93 -0
  105. package/src/motion/beat-board-model.js +84 -0
  106. package/src/motion/beat-board.css +124 -0
  107. package/src/motion/beat-board.jsx +59 -0
  108. package/src/motion/center-of-mass.js +66 -0
  109. package/src/motion/fix-collisions.js +850 -0
  110. package/src/motion/physics-review.js +118 -0
  111. package/src/{ardy → motion}/physics-support.js +1 -76
  112. package/src/motion/rig-snapshot.js +22 -0
  113. package/src/motion/take-segments.jsx +140 -0
  114. package/src/{ardy → motion}/timeline-coordinates.js +0 -21
  115. package/src/{ardy → motion}/timeline.css +1 -2
  116. package/src/{ardy → motion}/timeline.jsx +48 -419
  117. package/src/motion/waypoints.js +76 -0
  118. package/src/motion3/beats.js +83 -0
  119. package/src/motion3/body.js +158 -0
  120. package/src/motion3/bones.js +47 -0
  121. package/src/motion3/carried.js +47 -0
  122. package/src/motion3/compile-job.js +75 -0
  123. package/src/motion3/compile-worker.js +10 -0
  124. package/src/motion3/compiler/anchors.js +105 -0
  125. package/src/motion3/compiler/arm-orient.js +128 -0
  126. package/src/motion3/compiler/channels.js +125 -0
  127. package/src/motion3/compiler/compile.js +205 -0
  128. package/src/motion3/compiler/gait-rhythm.js +97 -0
  129. package/src/motion3/compiler/grip.js +171 -0
  130. package/src/motion3/compiler/head-steady.js +48 -0
  131. package/src/motion3/compiler/hold.js +134 -0
  132. package/src/motion3/compiler/idle.js +113 -0
  133. package/src/motion3/compiler/limbs.js +170 -0
  134. package/src/motion3/compiler/math.js +72 -0
  135. package/src/motion3/compiler/pose.js +162 -0
  136. package/src/motion3/compiler/quat.js +49 -0
  137. package/src/motion3/compiler/skeleton.js +74 -0
  138. package/src/motion3/compiler/validate-common.js +32 -0
  139. package/src/motion3/compiler/validate-goals.js +118 -0
  140. package/src/motion3/compiler/validate-objects.js +143 -0
  141. package/src/motion3/compiler/validate-parts.js +116 -0
  142. package/src/motion3/compiler/validate.js +122 -0
  143. package/src/motion3/compiler/vocab.js +73 -0
  144. package/src/motion3/compiler/walk-pelvis.js +275 -0
  145. package/src/motion3/compiler/walk.js +207 -0
  146. package/src/motion3/compiler/wrist-clamp.js +32 -0
  147. package/src/motion3/delta.js +47 -0
  148. package/src/motion3/describe.js +174 -0
  149. package/src/motion3/docs.js +27 -0
  150. package/src/motion3/edit-carry.js +21 -0
  151. package/src/motion3/fix/ease.js +16 -0
  152. package/src/motion3/fix/ground.js +37 -0
  153. package/src/motion3/fix/index.js +52 -0
  154. package/src/motion3/fix/push.js +71 -0
  155. package/src/motion3/fix/reach.js +46 -0
  156. package/src/motion3/fix/retime.js +79 -0
  157. package/src/motion3/fix/self.js +211 -0
  158. package/src/motion3/fix/slide.js +70 -0
  159. package/src/motion3/fix/turn.js +51 -0
  160. package/src/motion3/follow.js +39 -0
  161. package/src/motion3/free-motion.js +173 -0
  162. package/src/motion3/grip-choice.js +130 -0
  163. package/src/motion3/hold-plan.js +157 -0
  164. package/src/motion3/json-patch.js +77 -0
  165. package/src/motion3/key-aim.js +105 -0
  166. package/src/motion3/key-edit.js +82 -0
  167. package/src/motion3/lenient.js +127 -0
  168. package/src/motion3/object-keys.js +236 -0
  169. package/src/motion3/object-pose.js +182 -0
  170. package/src/motion3/object-tracks.js +12 -0
  171. package/src/motion3/pipeline.js +102 -0
  172. package/src/motion3/playback.js +51 -0
  173. package/src/motion3/probes.js +91 -0
  174. package/src/motion3/qa/body-checks.js +34 -0
  175. package/src/motion3/qa/check-names.js +14 -0
  176. package/src/motion3/qa/checks.js +121 -0
  177. package/src/motion3/qa/contact-checks.js +51 -0
  178. package/src/motion3/qa/explain.js +80 -0
  179. package/src/motion3/qa/fmt.js +5 -0
  180. package/src/motion3/qa/foot-checks.js +72 -0
  181. package/src/motion3/qa/geometry.js +77 -0
  182. package/src/motion3/qa/goals.js +191 -0
  183. package/src/motion3/qa/ground.js +60 -0
  184. package/src/motion3/qa/hints-contact.js +179 -0
  185. package/src/motion3/qa/hints-goal.js +160 -0
  186. package/src/motion3/qa/hints-knee.js +24 -0
  187. package/src/motion3/qa/hints-motion.js +209 -0
  188. package/src/motion3/qa/hints-object.js +91 -0
  189. package/src/motion3/qa/index.js +42 -0
  190. package/src/motion3/qa/key-checks.js +82 -0
  191. package/src/motion3/qa/near.js +96 -0
  192. package/src/motion3/qa/object-checks.js +135 -0
  193. package/src/motion3/qa/patch.js +161 -0
  194. package/src/motion3/qa/severity.js +38 -0
  195. package/src/motion3/qa/spec-checks.js +65 -0
  196. package/src/motion3/qa/style.js +218 -0
  197. package/src/motion3/qa/wrist-checks.js +69 -0
  198. package/src/motion3/qa/wrist-obs.js +60 -0
  199. package/src/motion3/refs.js +120 -0
  200. package/src/motion3/repair-tune.js +121 -0
  201. package/src/motion3/repair.js +83 -0
  202. package/src/motion3/report.js +67 -0
  203. package/src/motion3/rigs/index.js +14 -0
  204. package/src/motion3/rigs/x-bot-tpose-body.json +1 -0
  205. package/src/motion3/rigs/x-bot-tpose.json +1 -0
  206. package/src/motion3/rigs/y-bot-tpose-body.json +1 -0
  207. package/src/motion3/rigs/y-bot-tpose.json +1 -0
  208. package/src/motion3/scene.js +60 -0
  209. package/src/motion3/schema/brief.js +89 -0
  210. package/src/motion3/schema/fields.js +44 -0
  211. package/src/motion3/schema/spec-examples.js +46 -0
  212. package/src/motion3/schema/topics.js +111 -0
  213. package/src/motion3/schema/types.js +147 -0
  214. package/src/motion3/search.js +117 -0
  215. package/src/motion3/second-hand.js +112 -0
  216. package/src/motion3/skills/basic.js +82 -0
  217. package/src/motion3/skills/book.js +76 -0
  218. package/src/motion3/skills/car.js +145 -0
  219. package/src/motion3/skills/frames-auto.js +29 -0
  220. package/src/motion3/skills/index.js +83 -0
  221. package/src/motion3/skills/info.js +65 -0
  222. package/src/motion3/skills/smoke-kit.js +109 -0
  223. package/src/motion3/skills/smoke.js +160 -0
  224. package/src/motion3/spec-store.js +59 -0
  225. package/src/motion3/take-edit.js +155 -0
  226. package/src/motion3/take.js +63 -0
  227. package/src/motion3/timeline.js +127 -0
  228. package/src/motion3/views.js +121 -0
  229. package/src/motion3/walk-plan.js +119 -0
  230. package/src/object-catalog.jsx +7 -1
  231. package/src/object-colors.js +98 -0
  232. package/src/object-gizmo-parts.js +115 -0
  233. package/src/object-gizmo.jsx +37 -122
  234. package/src/object-glow.js +139 -0
  235. package/src/object-hidden.js +33 -0
  236. package/src/object-look.js +196 -0
  237. package/src/object-size.js +10 -0
  238. package/src/object-travel.js +71 -6
  239. package/src/panels/CameraPanel.jsx +7 -7
  240. package/src/panels/HdrEnvironmentControls.jsx +59 -0
  241. package/src/panels/LightPanel.jsx +25 -4
  242. package/src/panels/ObjectLookPanel.jsx +88 -0
  243. package/src/panels/PoseImagePanel.jsx +1 -1
  244. package/src/panels/PosePanel.jsx +2 -17
  245. package/src/panels/RigControlPanel.jsx +5 -53
  246. package/src/panels/SubjectBox.jsx +4 -1
  247. package/src/panels/details.css +14 -0
  248. package/src/posestudio.jsx +4 -27
  249. package/src/project-browser.jsx +3 -4
  250. package/src/project-resources.js +18 -31
  251. package/src/project.js +13 -104
  252. package/src/props.jsx +25 -280
  253. package/src/rail-dot-drag.js +23 -0
  254. package/src/render-passes.js +10 -0
  255. package/src/resource-status.jsx +1 -0
  256. package/src/result-modal.jsx +4 -19
  257. package/src/room.jsx +20 -12
  258. package/src/scene-assets.js +8 -6
  259. package/src/scene-light-place.js +72 -0
  260. package/src/scene-lights.jsx +148 -0
  261. package/src/scene-objects.js +31 -139
  262. package/src/scenes.js +15 -105
  263. package/src/semantic-edit.js +2 -5
  264. package/src/set-pieces.jsx +183 -0
  265. package/src/settings-menu.jsx +3 -11
  266. package/src/shell/BottomDock.jsx +11 -53
  267. package/src/shell/CompositionGuideButton.jsx +20 -0
  268. package/src/shell/DetailsSlot.jsx +16 -200
  269. package/src/shell/GhostsMenuGroup.jsx +74 -0
  270. package/src/shell/LibrarySlot.jsx +1 -3
  271. package/src/shell/MenuBar.jsx +3 -3
  272. package/src/shell/PreferencesDialog.jsx +15 -264
  273. package/src/shell/PreferencesSlot.jsx +2 -8
  274. package/src/shell/ShotPreviewHeader.jsx +17 -0
  275. package/src/shell/TopBar.jsx +5 -83
  276. package/src/shell/ViewportToolbar.jsx +23 -93
  277. package/src/shell/agent-glass.css +19 -6
  278. package/src/shell/details-selection.js +42 -0
  279. package/src/shell/ghosts-menu.css +22 -0
  280. package/src/shell/glass-regions.css +29 -3
  281. package/src/shell/preferences.css +6 -1
  282. package/src/shell/viewport.css +12 -0
  283. package/src/shot-authoring.js +4 -7
  284. package/src/soft-shadows.js +37 -0
  285. package/src/stage-attach-frames.js +12 -12
  286. package/src/stage-canvas-rigs.jsx +3 -3
  287. package/src/stage-capture.jsx +42 -8
  288. package/src/stage-characters.jsx +22 -216
  289. package/src/stage-editor-gizmos.jsx +6 -6
  290. package/src/stage-environment.js +93 -0
  291. package/src/stage-hdr-builtins.js +107 -0
  292. package/src/stage-hdr-image.js +116 -0
  293. package/src/stage-hdr-library.js +70 -0
  294. package/src/stage-hdr-map.js +94 -0
  295. package/src/stage-hdr.js +130 -0
  296. package/src/stage-lighting.js +149 -0
  297. package/src/stage-occlusion.js +210 -0
  298. package/src/stage-sky.jsx +159 -0
  299. package/src/stage-startup-defaults.js +5 -9
  300. package/src/studio-actions.js +12 -14
  301. package/src/studio-agent-commands.js +46 -16
  302. package/src/studio-agent-motion.js +9 -9
  303. package/src/studio-agent-protocol.js +46 -73
  304. package/src/studio-app-binding.js +29 -45
  305. package/src/studio-asset-list.js +17 -0
  306. package/src/studio-contact-sheet.js +37 -5
  307. package/src/studio-elements.js +27 -7
  308. package/src/studio-entity-bounds.js +18 -0
  309. package/src/studio-error-codes.js +8 -0
  310. package/src/studio-export-framing.js +152 -0
  311. package/src/studio-lighting-check.js +103 -0
  312. package/src/studio-motion-visual.js +150 -0
  313. package/src/studio-schema-errors.js +80 -0
  314. package/src/studio-support-gap.js +74 -0
  315. package/src/styles.css +1 -19
  316. package/src/timeline-defaults.js +5 -0
  317. package/src/timeline-extent.js +8 -20
  318. package/src/tutorial-walk.js +17 -0
  319. package/src/ui-scrub.js +1 -1
  320. package/src/use-shot-camera-tracks.js +12 -0
  321. package/src/view-ghosts.js +97 -0
  322. package/src/view-ghosts.jsx +49 -0
  323. package/src/workflow/AgentPanel.jsx +47 -166
  324. package/src/workflow/AgentProgressCard.jsx +107 -0
  325. package/src/workflow/AgentReplyText.jsx +37 -0
  326. package/src/workflow/AgentTurnSteps.jsx +84 -0
  327. package/src/workflow/LoginPanel.jsx +140 -0
  328. package/src/workflow/WorkflowBuilder.jsx +7 -23
  329. package/src/workflow/agent-client.js +54 -51
  330. package/src/workflow/agent-panel.css +71 -1
  331. package/src/workflow/agent-progress-card.css +167 -0
  332. package/src/workflow/agent-progress-view.js +47 -0
  333. package/src/workflow/agent-turn-steps.css +113 -0
  334. package/src/workflow/agent-turn.js +191 -0
  335. package/src/workflow/login-client.js +34 -0
  336. package/src/workflow/motion-input.js +18 -31
  337. package/src/workflow/node-schema.js +1 -1
  338. package/src/workflow/reply-markdown.js +69 -0
  339. package/src/workflow/scene-asset-sync.js +8 -8
  340. package/tools/agent-session-stats.mjs +61 -0
  341. package/tools/dev-full.mjs +5 -56
  342. package/tools/eslint-baseline.json +12 -13
  343. package/tools/headless-studio.mjs +85 -0
  344. package/tools/knip-baseline.json +0 -179
  345. package/tools/loc-baseline.json +33 -83
  346. package/tools/motion3/extract-body.mjs +103 -0
  347. package/tools/motion3/extract-rig.mjs +85 -0
  348. package/tools/process-supervisor.mjs +0 -88
  349. package/tools/run-tests.mjs +9 -124
  350. package/bin/agent/motion-runtime.mjs +0 -76
  351. package/dist/ardy/cskel27-rest.json +0 -557
  352. package/dist/assets/analytics-B41FmPYI.js +0 -1
  353. package/dist/assets/app-Do-Og5Pu.css +0 -1
  354. package/dist/assets/app-DwP-tl2x.js +0 -4920
  355. package/dist/assets/config-Bx7pYsD9.js +0 -1
  356. package/dist/assets/config-DeoFTYzb.css +0 -1
  357. package/dist/assets/demo-Dt0qZfS7.js +0 -1
  358. package/dist/assets/scene-asset-sync-BCWDJB1d.js +0 -19
  359. package/dist/assets/scene-asset-sync-DjJ_cJ2R.css +0 -1
  360. package/dist/assets/ticket-jqMhqL88.js +0 -1
  361. package/dist/assets/workflow-Br-K7Srs.js +0 -179
  362. package/dist/demo/walk-then-stop.npz +0 -0
  363. package/mcp/ardy-prompts.mjs +0 -218
  364. package/src/ardy/auto-fix-panel.css +0 -108
  365. package/src/ardy/auto-physics.js +0 -1194
  366. package/src/ardy/client.js +0 -153
  367. package/src/ardy/convert.js +0 -313
  368. package/src/ardy/cskel27-neutral.js +0 -39
  369. package/src/ardy/cskel27.js +0 -77
  370. package/src/ardy/export.js +0 -165
  371. package/src/ardy/fix-collisions.js +0 -2257
  372. package/src/ardy/foot-lock.js +0 -230
  373. package/src/ardy/motion-calibration.js +0 -118
  374. package/src/ardy/motion-edit.js +0 -213
  375. package/src/ardy/motion-url.js +0 -55
  376. package/src/ardy/mp4-duration.js +0 -102
  377. package/src/ardy/npz.js +0 -595
  378. package/src/ardy/physics-panel.css +0 -21
  379. package/src/ardy/physics-panel.jsx +0 -89
  380. package/src/ardy/physics-refine.js +0 -111
  381. package/src/ardy/physics-review.js +0 -567
  382. package/src/ardy/physics-temporal.js +0 -54
  383. package/src/ardy/platform-fit-panel.css +0 -64
  384. package/src/ardy/platform-fit-panel.jsx +0 -41
  385. package/src/ardy/platform-fit.js +0 -326
  386. package/src/ardy/playback.js +0 -711
  387. package/src/ardy/pose-pin.js +0 -97
  388. package/src/ardy/prompt-clips.js +0 -30
  389. package/src/ardy/retime.js +0 -211
  390. package/src/ardy/root-drop.js +0 -369
  391. package/src/ardy/timeline-resize.js +0 -13
  392. package/src/ardy/to-cskel27.js +0 -225
  393. package/src/ardy/trim.js +0 -33
  394. package/src/ardy/waypoints.js +0 -369
  395. package/src/ardy-bridge-contract.js +0 -281
  396. package/src/domains/motion/motion-video-capture.js +0 -664
  397. package/src/i2v-motion-client.js +0 -77
  398. package/src/i2v-motion-studio.jsx +0 -180
  399. package/src/line-edit.js +0 -1348
  400. package/src/motion/generation.js +0 -105
  401. package/src/motion-readiness-ui.jsx +0 -67
  402. package/src/motion-readiness.js +0 -40
  403. package/src/motion-resources.js +0 -177
  404. package/src/motion-store.js +0 -65
  405. package/src/motion-trail.js +0 -601
  406. package/src/multimodel-ingest.js +0 -394
  407. package/src/panels/PromptBlocksPanel.jsx +0 -444
  408. package/src/panels/VideoCapturePanel.jsx +0 -181
  409. package/src/pose-extract/browser.js +0 -42
  410. package/src/pose-extract/detector.js +0 -100
  411. package/src/pose-extract/fit.js +0 -227
  412. package/src/pose-extract/image-frame.js +0 -51
  413. package/src/pose-extract/index.js +0 -58
  414. package/src/pose-extract/keyframes.js +0 -74
  415. package/src/pose-extract/landmarks.js +0 -162
  416. package/src/pose-extract/mirror.js +0 -153
  417. package/src/pose-extract/take.js +0 -131
  418. package/src/pose-extract/video-frames.js +0 -91
  419. package/src/take-recipe.js +0 -165
  420. package/src/trail-key-conflicts.js +0 -39
  421. package/src/trail-pick.js +0 -59
  422. package/src/use-i2v-capture-modal.js +0 -15
  423. package/tools/ardy/BRIDGE.md +0 -37
  424. package/tools/ardy/GVHMR-NPZ.md +0 -68
  425. package/tools/ardy/README.md +0 -124
  426. package/tools/ardy/__pycache__/cclay_gvhmr_worker.cpython-313.pyc +0 -0
  427. package/tools/ardy/artifacts.mjs +0 -34
  428. package/tools/ardy/bridge.mjs +0 -1732
  429. package/tools/ardy/bvh-cskel27.mjs +0 -1265
  430. package/tools/ardy/cclay_constrained_generate.py +0 -561
  431. package/tools/ardy/cclay_gvhmr_worker.py +0 -264
  432. package/tools/ardy/cclay_motion_edit.py +0 -510
  433. package/tools/ardy/cclay_sequence_generate.py +0 -705
  434. package/tools/ardy/cclay_worker.py +0 -304
  435. package/tools/ardy/dump-npz.py +0 -205
  436. package/tools/ardy/extract-rest.mjs +0 -299
  437. package/tools/ardy/extract.mjs +0 -507
  438. package/tools/ardy/footage.mjs +0 -467
  439. package/tools/ardy/gvhmr-floor.mjs +0 -102
  440. package/tools/ardy/gvhmr-to-cskel27.mjs +0 -21
  441. package/tools/ardy/gvhmr_fastpath.py +0 -168
  442. package/tools/ardy/gvhmr_trajectory.py +0 -240
  443. package/tools/ardy/merge-text-encoder.py +0 -160
  444. package/tools/ardy/mocap-metrics.mjs +0 -200
  445. package/tools/ardy/mocap-quality-gate.mjs +0 -110
  446. package/tools/ardy/motion-stabilize.mjs +0 -348
  447. package/tools/ardy/npz.mjs +0 -414
  448. package/tools/ardy/pose-to-npz.mjs +0 -106
  449. package/tools/ardy/prompt-limits.mjs +0 -5
  450. package/tools/ardy/run-edit-on-box.sh +0 -73
  451. package/tools/ardy/run-local.mjs +0 -233
  452. package/tools/ardy/run-on-box.sh +0 -593
  453. package/tools/ardy/run-sequence-on-box.sh +0 -177
  454. package/tools/ardy/runners/gvhmr-worker.mjs +0 -206
  455. package/tools/ardy/runners/index.mjs +0 -18
  456. package/tools/ardy/runners/local.mjs +0 -455
  457. package/tools/ardy/runners/proc.mjs +0 -125
  458. package/tools/ardy/runners/remote.mjs +0 -252
  459. package/tools/ardy/setup-local.mjs +0 -189
  460. package/tools/ardy/setup-text-encoder-on-box.sh +0 -93
  461. package/tools/ardy/setup-text-encoder.py +0 -306
  462. package/tools/ardy/smpl-cskel27.mjs +0 -178
  463. package/tools/ardy/visual-qa.mjs +0 -196
  464. package/tools/ardy/vq-car.mjs +0 -52
  465. package/tools/bench/EXP3.md +0 -61
  466. package/tools/bench/cclay_bench_extract_incam.py +0 -125
  467. package/tools/bench/cclay_bench_extract_obs.py +0 -204
  468. package/tools/bench/cclay_bench_runner.py +0 -42
  469. package/tools/bench/cube-contact.mjs +0 -162
  470. package/tools/bench/exp3.mjs +0 -152
  471. package/tools/bench/extract-bench-lib.mjs +0 -216
  472. package/tools/bench/extract-bench.mjs +0 -302
  473. package/tools/bench/fal-generate.mjs +0 -88
  474. package/tools/bench/fit/README.md +0 -189
  475. package/tools/bench/fit/camera.mjs +0 -59
  476. package/tools/bench/fit/contact.mjs +0 -419
  477. package/tools/bench/fit/footlock.mjs +0 -234
  478. package/tools/bench/fit/motion.mjs +0 -85
  479. package/tools/bench/fit/pin.mjs +0 -27
  480. package/tools/bench/fit/remote.mjs +0 -105
  481. package/tools/bench/fit-bench.mjs +0 -126
  482. package/tools/bench/fit-sanity.mjs +0 -45
  483. package/tools/bench/metrics.mjs +0 -313
  484. package/tools/bench/obs/depth.mjs +0 -258
  485. package/tools/bench/obs/extrinsics.mjs +0 -175
  486. package/tools/bench/obs/fit_mannequin_betas.py +0 -340
  487. package/tools/bench/obs/ground.mjs +0 -294
  488. package/tools/bench/obs/heading.mjs +0 -151
  489. package/tools/bench/obs/ladder.mjs +0 -555
  490. package/tools/bench/obs/mannequin-betas.json +0 -124
  491. package/tools/bench/obs/remote.mjs +0 -83
  492. package/tools/bench/obs/rest_joints.py +0 -52
  493. package/tools/bench/obs/ybot-targets.mjs +0 -49
  494. package/tools/bench/obs-bench.mjs +0 -520
  495. package/tools/bench/score.mjs +0 -343
  496. package/tools/bench/summarize.mjs +0 -86
  497. package/tools/demo-worker/README.md +0 -137
  498. package/tools/demo-worker/api-client.mjs +0 -233
  499. package/tools/demo-worker/generate.mjs +0 -301
  500. package/tools/demo-worker/index.mjs +0 -466
  501. package/tools/demo-worker/sign-core.mjs +0 -112
  502. package/tools/demo-worker/sign.mjs +0 -161
  503. package/tools/gt-render/browser.mjs +0 -159
  504. package/tools/gt-render/camera-math.mjs +0 -276
  505. package/tools/gt-render/page.mjs +0 -267
  506. package/tools/gt-render/render.mjs +0 -514
  507. package/tools/gt-render/scene-box.mjs +0 -31
  508. package/tools/gt-render/take-transform.mjs +0 -87
  509. package/tools/kimodo/compare-seam.mjs +0 -108
  510. package/tools/kimodo/constraints.mjs +0 -244
  511. package/tools/kimodo/edit.mjs +0 -259
  512. package/tools/kimodo/effector-constraints.mjs +0 -200
  513. package/tools/kimodo/generate.mjs +0 -646
  514. package/tools/kimodo/local-output.mjs +0 -265
  515. package/tools/kimodo/measure-pose.mjs +0 -65
  516. package/tools/kimodo/measure-preserve.mjs +0 -899
  517. package/tools/kimodo/measure-waypoints.mjs +0 -55
  518. package/tools/kimodo/mlx-generate.py +0 -111
  519. package/tools/kimodo/pose-constraints.mjs +0 -248
  520. package/tools/kimodo/preserve-mask.mjs +0 -543
  521. package/tools/kimodo/read-npz.mjs +0 -189
  522. package/tools/kimodo/run-edit-on-box.mjs +0 -155
  523. package/tools/kimodo/run-sequence-on-box.mjs +0 -178
  524. package/tools/kimodo/runner.mjs +0 -284
  525. package/tools/kimodo/seam-batch.mjs +0 -77
  526. package/tools/kimodo/setup-local.mjs +0 -30
  527. package/tools/kimodo/setup-on-box.sh +0 -339
  528. package/tools/kimodo/soma77-to-cskel27.mjs +0 -305
  529. package/tools/morphgs/assets/gen_truth.py +0 -39
  530. package/tools/morphgs/assets/mesh_ori_rig.txt +0 -27
  531. package/tools/morphgs/assets/playback-check.mjs +0 -30
  532. package/tools/morphgs/demo-gate.sh +0 -26
  533. package/tools/morphgs/fbx2morphgs.mjs +0 -68
  534. package/tools/morphgs/morphgs-to-cskel27.mjs +0 -105
  535. package/tools/morphgs/patches/preprocess_src-none-mode.patch +0 -76
  536. package/tools/morphgs/setup-on-cluster.sh +0 -126
  537. package/tools/projflow/driver.py +0 -1069
  538. package/tools/projflow/generate.mjs +0 -805
  539. package/tools/projflow/hml22-to-cskel27.mjs +0 -735
  540. package/tools/projflow/line-edit-job.mjs +0 -547
  541. package/tools/projflow/replay.mjs +0 -452
  542. package/tools/projflow/runner.mjs +0 -269
  543. package/tools/projflow/service.mjs +0 -742
  544. package/tools/track/DESIGN.md +0 -513
  545. package/tools/track/backfill-provenance.mjs +0 -116
  546. package/tools/track/budget.mjs +0 -8
  547. package/tools/track/check-rig.mjs +0 -209
  548. package/tools/track/diagnostics.schema.json +0 -60
  549. package/tools/track/export-rig.mjs +0 -256
  550. package/tools/track/fallback.mjs +0 -7
  551. package/tools/track/fk-parity-fixture.mjs +0 -161
  552. package/tools/track/gate.mjs +0 -349
  553. package/tools/track/masks.mjs +0 -318
  554. package/tools/track/metrics.mjs +0 -201
  555. package/tools/track/publish-obs.mjs +0 -108
  556. package/tools/track/py/check_env.py +0 -61
  557. package/tools/track/py/eval_lr.py +0 -229
  558. package/tools/track/py/lr_viterbi.py +0 -294
  559. package/tools/track/py/masks.py +0 -405
  560. package/tools/track/py/objective.py +0 -382
  561. package/tools/track/py/rig.py +0 -508
  562. package/tools/track/py/scene.py +0 -326
  563. package/tools/track/py/test_joint_indices.py +0 -123
  564. package/tools/track/py/test_lr_viterbi.py +0 -195
  565. package/tools/track/py/test_masks.py +0 -229
  566. package/tools/track/py/test_rig.py +0 -288
  567. package/tools/track/py/test_scene.py +0 -354
  568. package/tools/track/py/test_track.py +0 -328
  569. package/tools/track/py/track.py +0 -411
  570. package/tools/track/remote.mjs +0 -140
  571. package/tools/track/rig-dump.mjs +0 -227
  572. package/tools/track/run-box-tests.mjs +0 -14
  573. package/tools/track/run-box.mjs +0 -168
  574. package/tools/track/setup-box.sh +0 -59
  575. package/tools/track/study-2d.mjs +0 -728
  576. /package/src/{ardy → motion}/collision-blockers.js +0 -0
  577. /package/src/{ardy → motion}/ground.js +0 -0
  578. /package/src/{ardy → motion}/ik-drag.js +0 -0
  579. /package/src/{ardy → motion}/ik-key-json.js +0 -0
  580. /package/src/{ardy → motion}/ik.js +0 -0
  581. /package/src/{ardy → motion}/physics-surface.js +0 -0
  582. /package/src/{ardy → motion}/playback-clock.js +0 -0
  583. /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
- }