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,735 +0,0 @@
1
- /**
2
- * hml22-to-cskel27.mjs — ProjFlow's HumanML3D 22-joint POSITION stream → the
3
- * exact motion arrays the cclay npz encoder consumes ({ frames, fps, rotMats,
4
- * rootPos, posedJoints }), i.e. the same shape tools/kimodo/soma77-to-cskel27.mjs
5
- * and tools/ardy/bvh-cskel27.mjs produce. Everything downstream — the npz
6
- * writer, measure-preserve, playback, the splice — is written against that
7
- * shape, so this file's whole job is to end at it.
8
- *
9
- * WHY POSITIONS AND NOT ROTATIONS (the one structural difference from Kimodo).
10
- * The checkpoint we target is `ACMDM_Raw_Flow_S_PatchSize22`. ACMDM's "Raw"
11
- * family generates ABSOLUTE JOINT COORDINATES — the model's output tensor is
12
- * [T, 22, 3] metres at 20 fps with no rotation channel at all (that is the
13
- * backbone paper's thesis: "Absolute Coordinates Make Motion Generation Easy").
14
- * soma77ToCskel27Motion is handed `global_rot_mats` and only has to re-express
15
- * them; here the rotations DO NOT EXIST YET and must be lifted from positions.
16
- * Plan C8 amendment (2026-08-29) froze that: input is positions, the converter
17
- * lifts.
18
- *
19
- * THREE MOVES, deliberately the same three as soma77-to-cskel27.mjs:
20
- * 1. NAME MAPPING with an explicit null for every cskel27 joint the source
21
- * cannot author (CSKEL27_FROM_HML22).
22
- * 2. NEAREST-MAPPED-ANCESTOR rule for those nulls: an unauthored joint borrows
23
- * its nearest mapped ancestor's GLOBAL rotation, so its LOCAL comes out
24
- * identity — the same "not authored" convention poseToCskel27 and
25
- * bvhToCskel27Motion use.
26
- * 3. EVERYTHING THROUGH GLOBAL SPACE. cskel27 and hml22 do not share a spine
27
- * segmentation (cskel27 runs Hips→Spine→Spine1→Spine2→Spine3, four links
28
- * before the shoulder carrier; hml22 runs pelvis→spine1→spine2→spine3,
29
- * three), so a cskel27 local is only well defined as
30
- * parentGlobalᵀ · global. Anything between two mapped joints is absorbed
31
- * into the child's local and no rotation is lost.
32
- *
33
- * THE NEW MOVE: POSITIONS → GLOBALS ("aim + inherited twist").
34
- * For every mapped cskel27 joint we know a rest bone vector (from the canonical
35
- * cskel27 skeleton) and the observed direction to the same descendant in the
36
- * hml22 frame. The global rotation is then
37
- *
38
- * G_j = swing( G_parent · restDir_j → obsDir_j ) · G_parent
39
- *
40
- * i.e. start from the parent's orientation, then apply the MINIMAL rotation that
41
- * puts the bone on the observed direction. Twist is INHERITED from the parent
42
- * rather than invented, which is the physically right default (the forearm's
43
- * roll follows the upper arm unless something says otherwise) and — the reason
44
- * it is the right ENGINEERING default too — it is a continuous function of the
45
- * parent, so a smooth input clip produces a smooth output clip. Two joints get
46
- * better than that: Hips and Spine3 each see TWO independent directions (the hip
47
- * axis, the shoulder axis), so their full orientation including twist is
48
- * recovered exactly by orthonormalising a two-vector frame. Those two carry the
49
- * whole body's heading, which is why they are the ones worth pinning.
50
- *
51
- * TEMPORAL CONTINUITY IS PART OF THE CONTRACT, not a nicety. A twist flip
52
- * between two frames reads as a limb snapping through 180° and would survive
53
- * every positional gate, because positions are invariant to it. The lift is
54
- * continuous everywhere except at two measure-zero branch points — an
55
- * antiparallel swing (bone reversed exactly through its parent's rest aim) and a
56
- * two-vector frame whose secondary collapses onto its primary. At both, this
57
- * converter resolves the branch by continuing the PREVIOUS FRAME (`carry`),
58
- * never by an arbitrary seed. See liftHml22PoseGlobals.
59
- *
60
- * WHAT THIS CANNOT RECOVER, by construction, from positions alone:
61
- * - axial TWIST of any single-child bone (forearm pronation, humerus roll,
62
- * shin roll, the head's roll about the neck axis). A joint chain's positions
63
- * are invariant to twist of the parent bone about its own axis. Measured on
64
- * real takes this is 26–55° on the arms. Positions are sub-millimetre;
65
- * rotations are approximate. Plan GP4 carve-out: measure and report, do not
66
- * gate on it in v1.
67
- * - the 5 cskel27 joints hml22 has no source for (Spine2, L/R HandEnd,
68
- * L/R HandThumb1) — filled with identity locals, 4.7–6.1 cm positional
69
- * error, accepted losses per GP4. None of them is a line-edit target that
70
- * hml22 can author.
71
- * Neither loss moves a MAPPED joint's position, which is what a line edit is
72
- * scored on.
73
- *
74
- * PITFALL THIS FILE EXISTS TO DOCUMENT (the hml22 analogue of the soma77 leg
75
- * shift): hml22 uses SMPL naming, where `left_collar` is the CLAVICLE and
76
- * `left_shoulder` is the UPPER-ARM root. cskel27 calls those `LeftShoulder` and
77
- * `LeftArm`. Mapping by matching name text would put the shoulder's rotation on
78
- * the clavicle and shear the whole arm. Likewise hml22's `left_foot` (joint 10)
79
- * is the TOE BASE — the ankle is `left_ankle` (7).
80
- *
81
- * FPS: the output keeps the source rate (20 fps, ACMDM's own). Retiming onto the
82
- * 24 fps app timeline happens DOWNSTREAM, exactly as it does for Kimodo takes —
83
- * this file must not retime, or the retimer would run twice.
84
- */
85
-
86
- import { CSKEL27_JOINTS, CSKEL27_PARENTS } from "../../src/ardy/cskel27.js";
87
- import { deriveBoneOffsets, forwardKinematics, matMul, matTranspose } from "../../src/ardy/convert.js";
88
- import { canonicalCskel27Reference } from "../../src/ardy/to-cskel27.js";
89
-
90
- /**
91
- * The HumanML3D / t2m 22-joint skeleton, in npz index order.
92
- *
93
- * SOURCE OF TRUTH, two independent confirmations (S2, 2026-08-29):
94
- * 1. github.com/EricGuo5513/HumanML3D `paramUtil.py` → `t2m_kinematic_chain`
95
- * = [[0,2,5,8,11], [0,1,4,7,10], [0,3,6,9,12,15], [9,14,17,19,21],
96
- * [9,13,16,18,20]]. That fixes the TOPOLOGY and the left/right ordering
97
- * exactly: index 1 is the LEFT hip (it heads the chain that also contains
98
- * 4,7,10) and index 2 the right.
99
- * 2. The names are SMPL's, because HumanML3D's 22 joints are SMPL body joints
100
- * 0..21 verbatim: github.com/vchoutas/smplx `smplx/joint_names.py`.
101
- * The two agree on every entry: joint 9 (`spine3`) is the parent of 12/13/14,
102
- * which is precisely the "carries the neck and both clavicles" role.
103
- */
104
- export const HML22_JOINTS = [
105
- "pelvis", // 0
106
- "left_hip", // 1
107
- "right_hip", // 2
108
- "spine1", // 3
109
- "left_knee", // 4
110
- "right_knee", // 5
111
- "spine2", // 6
112
- "left_ankle", // 7
113
- "right_ankle", // 8
114
- "spine3", // 9
115
- "left_foot", // 10 ← TOE BASE, not the ankle
116
- "right_foot", // 11 ← TOE BASE, not the ankle
117
- "neck", // 12
118
- "left_collar", // 13 ← CLAVICLE (cskel27 "LeftShoulder")
119
- "right_collar", // 14
120
- "head", // 15
121
- "left_shoulder", // 16 ← UPPER ARM (cskel27 "LeftArm")
122
- "right_shoulder", // 17
123
- "left_elbow", // 18
124
- "right_elbow", // 19
125
- "left_wrist", // 20
126
- "right_wrist", // 21
127
- ];
128
-
129
- /** Parent index per hml22 joint, expanded from t2m_kinematic_chain. */
130
- export const HML22_PARENTS = [
131
- null, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 12, 13, 14, 16, 17, 18, 19,
132
- ];
133
-
134
- /**
135
- * cskel27 joint name → the hml22 joint that drives it, or null for "this joint
136
- * is not authored by the source".
137
- *
138
- * TORSO ANCHORING is structural, not cosmetic, exactly as in the soma77 case.
139
- * Hips↔pelvis is the root in both. cskel27's Spine3 carries the neck AND both
140
- * clavicles; hml22's `spine3` (9) carries `neck` (12) and both collars (13,14).
141
- * So Spine3↔spine3 is FORCED. That leaves three cskel27 links (Spine, Spine1,
142
- * Spine2) spanning the two hml22 links (spine1, spine2) between the anchors, so
143
- * exactly one cskel27 spine joint must go unauthored. Spine2 is the one chosen,
144
- * for the same reason soma77-to-cskel27 chose it: mapping bottom-up keeps the
145
- * lower spine — where the large trunk rotation lives — driven by real data and
146
- * pushes the identity link to the top of the chain, where the residual
147
- * Spine2→Spine3 rotation is absorbed into Spine3's local anyway.
148
- *
149
- * The hand ends and thumbs stay unauthored: hml22 stops at the wrist. Borrowing
150
- * anything would invent finger articulation the model never generated.
151
- */
152
- export const CSKEL27_FROM_HML22 = {
153
- Hips: "pelvis",
154
- Spine: "spine1",
155
- Spine1: "spine2",
156
- Spine2: null,
157
- Spine3: "spine3",
158
- Neck: "neck",
159
- Head: "head",
160
-
161
- RightShoulder: "right_collar",
162
- RightArm: "right_shoulder",
163
- RightForeArm: "right_elbow",
164
- RightHand: "right_wrist",
165
- RightHandEnd: null,
166
- RightHandThumb1: null,
167
-
168
- LeftShoulder: "left_collar",
169
- LeftArm: "left_shoulder",
170
- LeftForeArm: "left_elbow",
171
- LeftHand: "left_wrist",
172
- LeftHandEnd: null,
173
- LeftHandThumb1: null,
174
-
175
- RightUpLeg: "right_hip",
176
- RightLeg: "right_knee",
177
- RightFoot: "right_ankle",
178
- RightToeBase: "right_foot",
179
-
180
- LeftUpLeg: "left_hip",
181
- LeftLeg: "left_knee",
182
- LeftFoot: "left_ankle",
183
- LeftToeBase: "left_foot",
184
- };
185
-
186
- /**
187
- * Per mapped cskel27 joint, the DESCENDANT whose bone direction determines this
188
- * joint's global rotation, plus (where a second independent direction exists)
189
- * the pair whose difference pins the twist.
190
- *
191
- * `aim` names a cskel27 joint, but the direction is measured between the two
192
- * joints' HML22 SOURCES — which is why Spine1 aims at Spine3 and not Spine2:
193
- * Spine2 is unauthored, so in the reconstructed skeleton G_Spine2 === G_Spine1
194
- * and the EFFECTIVE bone leaving Spine1 is Spine1→Spine3.
195
- *
196
- * A null entry is a joint whose own orientation positions cannot observe (no
197
- * mapped child below it): Head, both Hands, both ToeBases. They inherit their
198
- * parent's global, so their local is identity — and their POSITION is still
199
- * exact, because it is produced by the parent's aim.
200
- */
201
- const AIM = {
202
- Hips: { aim: "Spine", twistRef: ["LeftUpLeg", "RightUpLeg"] },
203
- Spine: { aim: "Spine1" },
204
- Spine1: { aim: "Spine3" },
205
- Spine2: null,
206
- Spine3: { aim: "Neck", twistRef: ["LeftShoulder", "RightShoulder"] },
207
- Neck: { aim: "Head" },
208
- Head: null,
209
-
210
- LeftShoulder: { aim: "LeftArm" },
211
- LeftArm: { aim: "LeftForeArm" },
212
- LeftForeArm: { aim: "LeftHand" },
213
- LeftHand: null,
214
- LeftHandEnd: null,
215
- LeftHandThumb1: null,
216
-
217
- RightShoulder: { aim: "RightArm" },
218
- RightArm: { aim: "RightForeArm" },
219
- RightForeArm: { aim: "RightHand" },
220
- RightHand: null,
221
- RightHandEnd: null,
222
- RightHandThumb1: null,
223
-
224
- LeftUpLeg: { aim: "LeftLeg" },
225
- LeftLeg: { aim: "LeftFoot" },
226
- LeftFoot: { aim: "LeftToeBase" },
227
- LeftToeBase: null,
228
-
229
- RightUpLeg: { aim: "RightLeg" },
230
- RightLeg: { aim: "RightFoot" },
231
- RightFoot: { aim: "RightToeBase" },
232
- RightToeBase: null,
233
- };
234
-
235
- const IDENTITY = [
236
- [1, 0, 0],
237
- [0, 1, 0],
238
- [0, 0, 1],
239
- ];
240
-
241
- /** cskel27 joints whose Y decides where the floor is (same set as soma77). */
242
- const GROUND_JOINTS = ["LeftFoot", "LeftToeBase", "RightFoot", "RightToeBase"];
243
-
244
- const CSKEL_INDEX = new Map(CSKEL27_JOINTS.map((name, index) => [name, index]));
245
- const HML_INDEX = new Map(HML22_JOINTS.map((name, index) => [name, index]));
246
-
247
- /** cskel27 index → hml22 index, or -1 when unauthored. */
248
- export const CSKEL27_TO_HML22_INDEX = CSKEL27_JOINTS.map((name) => {
249
- const source = CSKEL27_FROM_HML22[name];
250
- return source === null || source === undefined ? -1 : HML_INDEX.get(source);
251
- });
252
-
253
- /** hml22 index → cskel27 index. Total: every hml22 joint has exactly one target. */
254
- export const HML22_TO_CSKEL27_INDEX = HML22_JOINTS.map((name) => {
255
- const target = CSKEL27_JOINTS.findIndex((joint) => CSKEL27_FROM_HML22[joint] === name);
256
- if (target < 0) {
257
- throw new Error(`hml22-to-cskel27: hml22 joint ${name} has no cskel27 target`);
258
- }
259
- return target;
260
- });
261
-
262
- /**
263
- * Per cskel27 joint, the hml22 index whose orientation it takes. An unmapped
264
- * joint walks up the cskel27 parent chain to the nearest mapped ancestor, which
265
- * is what makes its local come out identity. Verbatim the soma77 rule.
266
- */
267
- export function resolveSourceIndices() {
268
- const resolved = new Array(CSKEL27_JOINTS.length);
269
- for (let index = 0; index < CSKEL27_JOINTS.length; index += 1) {
270
- let cursor = index;
271
- let source = CSKEL27_FROM_HML22[CSKEL27_JOINTS[cursor]];
272
- while (source == null) {
273
- cursor = CSKEL27_PARENTS[cursor];
274
- if (cursor === null) {
275
- throw new Error(
276
- `hml22ToCskel27Motion: ${CSKEL27_JOINTS[index]} has no mapped ancestor`
277
- );
278
- }
279
- source = CSKEL27_FROM_HML22[CSKEL27_JOINTS[cursor]];
280
- }
281
- resolved[index] = HML_INDEX.get(source);
282
- }
283
- return resolved;
284
- }
285
-
286
- // ---------------------------------------------------------------------------
287
- // small vector helpers (convert.js exports matrix maths but no matVec)
288
- // ---------------------------------------------------------------------------
289
-
290
- const sub = (a, b) => [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
291
- const dot = (a, b) => a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
292
- const cross = (a, b) => [
293
- a[1] * b[2] - a[2] * b[1],
294
- a[2] * b[0] - a[0] * b[2],
295
- a[0] * b[1] - a[1] * b[0],
296
- ];
297
- const norm = (a) => Math.hypot(a[0], a[1], a[2]);
298
-
299
- function unit(a, label) {
300
- const length = norm(a);
301
- if (!(length > 1e-9)) {
302
- throw new Error(`hml22ToCskel27Motion: degenerate direction for ${label}`);
303
- }
304
- return [a[0] / length, a[1] / length, a[2] / length];
305
- }
306
-
307
- function matVec(m, v) {
308
- return [
309
- m[0][0] * v[0] + m[0][1] * v[1] + m[0][2] * v[2],
310
- m[1][0] * v[0] + m[1][1] * v[1] + m[1][2] * v[2],
311
- m[2][0] * v[0] + m[2][1] * v[1] + m[2][2] * v[2],
312
- ];
313
- }
314
-
315
- /** Component of `v` perpendicular to unit vector `axis`, or null if it vanishes. */
316
- function rejectFrom(v, axis) {
317
- if (!v) return null;
318
- const k = dot(v, axis);
319
- const out = [v[0] - axis[0] * k, v[1] - axis[1] * k, v[2] - axis[2] * k];
320
- return norm(out) > 1e-6 ? out : null;
321
- }
322
-
323
- /**
324
- * Minimal ("swing") rotation taking unit vector `from` onto unit vector `to`,
325
- * returned together with the axis it turned about.
326
- *
327
- * THE BRANCH POINT. When `from` and `to` are antiparallel every axis
328
- * perpendicular to `from` is a valid 180° rotation, and picking a fixed seed
329
- * (say +X) makes the choice jump the moment the bone crosses that
330
- * configuration — a visible 180° limb flip between two adjacent frames. `hint`
331
- * is the axis this same joint turned about on the PREVIOUS frame; continuing it
332
- * is the branch nearest the previous frame, which is exactly the continuity
333
- * rule this converter promises. The fixed seed survives only as the
334
- * first-frame fallback, where there is no previous frame to be near.
335
- */
336
- function swingWithAxis(from, to, hint, label) {
337
- const cosine = Math.max(-1, Math.min(1, dot(from, to)));
338
- if (cosine > 1 - 1e-12) {
339
- // Already aligned: identity, and the previous axis stays the live hint so
340
- // a momentary alignment does not erase the branch we have been following.
341
- return { matrix: IDENTITY.map((row) => row.slice()), axis: hint ?? null };
342
- }
343
- let axis;
344
- if (cosine < -1 + 1e-9) {
345
- const continued = rejectFrom(hint, from);
346
- const seed = continued ?? (Math.abs(from[0]) < 0.9 ? [1, 0, 0] : [0, 1, 0]);
347
- axis = continued
348
- ? unit(continued, `${label} antiparallel continuation`)
349
- : unit(cross(from, seed), `${label} antiparallel swing`);
350
- } else {
351
- axis = unit(cross(from, to), `${label} swing`);
352
- }
353
- const sine = Math.sqrt(Math.max(0, 1 - cosine * cosine));
354
- const t = 1 - cosine;
355
- const [x, y, z] = axis;
356
- return {
357
- matrix: [
358
- [t * x * x + cosine, t * x * y - sine * z, t * x * z + sine * y],
359
- [t * x * y + sine * z, t * y * y + cosine, t * y * z - sine * x],
360
- [t * x * z - sine * y, t * y * z + sine * x, t * z * z + cosine],
361
- ],
362
- axis,
363
- };
364
- }
365
-
366
- /**
367
- * Orthonormal frame (as a rotation matrix, columns e1/e2/e3) from two vectors.
368
- * `hint` is the previous frame's e2 for this joint: it only comes into play when
369
- * the secondary collapses onto the primary (a body folded so the hip axis lies
370
- * along the spine), which is the other branch point, resolved the same way.
371
- */
372
- function frameOf(primary, secondary, label, hint) {
373
- const e1 = unit(primary, `${label} primary`);
374
- let projected = rejectFrom(secondary, e1);
375
- if (!projected) projected = rejectFrom(hint, e1);
376
- if (!projected) {
377
- throw new Error(`hml22ToCskel27Motion: ${label} secondary is parallel to its primary`);
378
- }
379
- const e2 = unit(projected, `${label} secondary`);
380
- const e3 = cross(e1, e2);
381
- return [
382
- [e1[0], e2[0], e3[0]],
383
- [e1[1], e2[1], e3[1]],
384
- [e1[2], e2[2], e3[2]],
385
- ];
386
- }
387
-
388
- // ---------------------------------------------------------------------------
389
- // 27 → 22 (downsample). Positions only.
390
- //
391
- // This is the direction the BOX is fed: ProjFlow's preservation rows are the
392
- // source take's own frames expressed in hml22 space, so the driver needs the
393
- // inverse of this file. It is also what the verify harness uses to manufacture
394
- // an hml22 fixture out of a real cclay take without a HumanML3D download.
395
- // Pure index gather — lossless by construction, since every hml22 joint has a
396
- // cskel27 counterpart.
397
- // ---------------------------------------------------------------------------
398
-
399
- /**
400
- * @param {{frames?:number, posedJoints:ArrayLike<number>}} input cskel27 [T,27,3]
401
- * @returns {Float32Array} hml22 positions, [T,22,3]
402
- */
403
- export function cskel27ToHml22Positions({ frames, posedJoints }) {
404
- if (!(ArrayBuffer.isView(posedJoints) || Array.isArray(posedJoints))) {
405
- throw new Error("cskel27ToHml22Positions: posedJoints must be an array of numbers");
406
- }
407
- const count = frames ?? posedJoints.length / (27 * 3);
408
- if (!Number.isInteger(count) || count < 1) {
409
- throw new Error(
410
- `cskel27ToHml22Positions: posedJoints length ${posedJoints.length} is not a whole number of 27-joint frames`
411
- );
412
- }
413
- if (posedJoints.length !== count * 27 * 3) {
414
- throw new Error(
415
- `cskel27ToHml22Positions: posedJoints must be ${count * 27 * 3} long, got ${posedJoints.length}`
416
- );
417
- }
418
- const out = new Float32Array(count * 22 * 3);
419
- for (let frame = 0; frame < count; frame += 1) {
420
- const src = frame * 27 * 3;
421
- const dst = frame * 22 * 3;
422
- for (let joint = 0; joint < 22; joint += 1) {
423
- const source = HML22_TO_CSKEL27_INDEX[joint];
424
- out[dst + joint * 3] = posedJoints[src + source * 3];
425
- out[dst + joint * 3 + 1] = posedJoints[src + source * 3 + 1];
426
- out[dst + joint * 3 + 2] = posedJoints[src + source * 3 + 2];
427
- }
428
- }
429
- return out;
430
- }
431
-
432
- // ---------------------------------------------------------------------------
433
- // 22 → 27, pose level. These are the building blocks the motion loop composes;
434
- // they are exported so a test can pin one frame's behaviour (and one frame's
435
- // continuity carry) without going through the whole clip.
436
- // ---------------------------------------------------------------------------
437
-
438
- /**
439
- * The canonical cskel27 rest body re-expressed at hml22 indices — the rest
440
- * directions every aim is measured against.
441
- * @param {{posed_joints:number[][]}} reference
442
- * @returns {number[][]} 22 rest positions
443
- */
444
- export function restPositionsHml22(reference) {
445
- const rest = new Array(22);
446
- for (let joint = 0; joint < 22; joint += 1) {
447
- rest[joint] = reference.posed_joints[HML22_TO_CSKEL27_INDEX[joint]].slice();
448
- }
449
- return rest;
450
- }
451
-
452
- /**
453
- * One frame of hml22 positions → 27 GLOBAL rotation matrices.
454
- *
455
- * @param {number[][]} observed 22 world positions for this frame
456
- * @param {object} options
457
- * @param {number[][]} options.rest 22 rest positions (restPositionsHml22)
458
- * @param {{swingAxis:Array, frameSecondary:Array}} [options.carry]
459
- * the previous frame's branch state; pass the returned `carry` straight
460
- * back in on the next frame to keep the clip continuous.
461
- * @param {number} [options.frame] frame index, for error messages only
462
- * @returns {{globals:number[][][], carry:{swingAxis:Array, frameSecondary:Array}}}
463
- */
464
- export function liftHml22PoseGlobals(observed, { rest, carry = null, frame = 0 } = {}) {
465
- if (!Array.isArray(observed) || observed.length !== 22) {
466
- throw new Error("liftHml22PoseGlobals: observed must be 22 [x,y,z] positions");
467
- }
468
- if (!Array.isArray(rest) || rest.length !== 22) {
469
- throw new Error("liftHml22PoseGlobals: rest must be 22 [x,y,z] positions");
470
- }
471
- const globals = new Array(27);
472
- const swingAxis = new Array(27).fill(null);
473
- const frameSecondary = new Array(27).fill(null);
474
-
475
- // cskel27 indices are topologically ordered (parent < child) — asserted in
476
- // the verify file — so one ascending pass suffices.
477
- for (let joint = 0; joint < 27; joint += 1) {
478
- const name = CSKEL27_JOINTS[joint];
479
- const parent = CSKEL27_PARENTS[joint];
480
- const spec = AIM[name];
481
- const parentGlobal = parent === null ? IDENTITY : globals[parent];
482
-
483
- if (!spec) {
484
- // Unauthored joint, or a mapped joint with no mapped child: borrow the
485
- // nearest mapped ancestor's global ⇒ identity local.
486
- globals[joint] = parentGlobal.map((row) => row.slice());
487
- continue;
488
- }
489
-
490
- const label = `${name} (frame ${frame})`;
491
- const self = CSKEL27_TO_HML22_INDEX[joint];
492
- const aim = CSKEL27_TO_HML22_INDEX[CSKEL_INDEX.get(spec.aim)];
493
- const restDir = unit(sub(rest[aim], rest[self]), `${label} rest bone`);
494
- const observedDir = unit(sub(observed[aim], observed[self]), `${label} observed bone`);
495
-
496
- if (spec.twistRef) {
497
- // Two independent directions ⇒ the full orientation, twist and all.
498
- const [first, second] = spec.twistRef;
499
- const a = CSKEL27_TO_HML22_INDEX[CSKEL_INDEX.get(first)];
500
- const b = CSKEL27_TO_HML22_INDEX[CSKEL_INDEX.get(second)];
501
- const restFrame = frameOf(restDir, sub(rest[a], rest[b]), `${label} rest frame`);
502
- const observedFrame = frameOf(
503
- observedDir,
504
- sub(observed[a], observed[b]),
505
- `${label} observed frame`,
506
- carry?.frameSecondary?.[joint]
507
- );
508
- frameSecondary[joint] = [observedFrame[0][1], observedFrame[1][1], observedFrame[2][1]];
509
- globals[joint] = matMul(observedFrame, matTranspose(restFrame));
510
- } else {
511
- // Aim + inherited twist.
512
- const aimed = unit(matVec(parentGlobal, restDir), `${label} aimed rest bone`);
513
- const swung = swingWithAxis(aimed, observedDir, carry?.swingAxis?.[joint], label);
514
- swingAxis[joint] = swung.axis;
515
- globals[joint] = matMul(swung.matrix, parentGlobal);
516
- }
517
- }
518
- return { globals, carry: { swingAxis, frameSecondary } };
519
- }
520
-
521
- /**
522
- * 27 global rotations → 27 LOCAL rotations. The soma77 rule, unchanged: whatever
523
- * sits between two mapped joints composes into the child's local, and an
524
- * unauthored joint (which borrowed its parent's global) comes out identity.
525
- * @param {number[][][]} globals
526
- * @returns {number[][][]}
527
- */
528
- export function globalsToCskel27Locals(globals) {
529
- const locals = new Array(27);
530
- for (let joint = 0; joint < 27; joint += 1) {
531
- const parent = CSKEL27_PARENTS[joint];
532
- locals[joint] =
533
- parent === null
534
- ? globals[joint].map((row) => row.slice())
535
- : matMul(matTranspose(globals[parent]), globals[joint]);
536
- }
537
- return locals;
538
- }
539
-
540
- /**
541
- * Full single-frame convert: hml22 positions → cskel27 rotations + posed joints.
542
- * The motion path calls exactly this per frame; a test can call it on one pose.
543
- *
544
- * @param {number[][]} observed 22 world positions
545
- * @param {object} options
546
- * @param {number[][]} options.rest
547
- * @param {number[][]} options.boneOffsets canonical cskel27 bone offsets
548
- * @param {number} [options.rootScale=1]
549
- * @param {object} [options.carry]
550
- * @param {number} [options.frame]
551
- * @returns {{globals, locals, root:number[], posed:number[][], carry:object}}
552
- */
553
- export function hml22PoseToCskel27(observed, { rest, boneOffsets, rootScale = 1, carry = null, frame = 0 }) {
554
- const lifted = liftHml22PoseGlobals(observed, { rest, carry, frame });
555
- const locals = globalsToCskel27Locals(lifted.globals);
556
- const pelvis = observed[HML_INDEX.get("pelvis")];
557
- const root = [pelvis[0] * rootScale, pelvis[1] * rootScale, pelvis[2] * rootScale];
558
- // FK on OUR body, never the source's bone lengths: the take has to move
559
- // CozyClay's figure, not drag HumanML3D's average proportions into the scene.
560
- const posed = forwardKinematics(locals, boneOffsets, root);
561
- return { globals: lifted.globals, locals, root, posed, carry: lifted.carry };
562
- }
563
-
564
- // ---------------------------------------------------------------------------
565
- // 22 → 27, motion level. THE C8 ENTRY POINT.
566
- // ---------------------------------------------------------------------------
567
-
568
- function chainLengthHml(positions, names) {
569
- let total = 0;
570
- for (let step = 1; step < names.length; step += 1) {
571
- total += norm(sub(positions[HML_INDEX.get(names[step])], positions[HML_INDEX.get(names[step - 1])]));
572
- }
573
- return total;
574
- }
575
-
576
- function chainLengthCskel(positions, names) {
577
- let total = 0;
578
- for (let step = 1; step < names.length; step += 1) {
579
- total += norm(sub(positions[CSKEL_INDEX.get(names[step])], positions[CSKEL_INDEX.get(names[step - 1])]));
580
- }
581
- return total;
582
- }
583
-
584
- /**
585
- * ProjFlow motion → the cclay motion arrays.
586
- *
587
- * @param {object} raw
588
- * @param {Float32Array|number[]} raw.positions hml22 joint positions, [T,22,3]
589
- * flattened, world metres, Y-up. ACMDM's own output tensor.
590
- * @param {number} [raw.fps=20] source rate. NOT retimed here (see header).
591
- * @param {number} [raw.frames] optional; derived from `positions` when absent,
592
- * cross-checked against it when present.
593
- * @param {object} [raw.reference] {local_rot_mats, posed_joints} cskel27 rest
594
- * (default: canonicalCskel27Reference())
595
- * @param {boolean} [raw.scaleRoot=true] scale the root trajectory by the
596
- * leg-length ratio so stride survives reproportioning (soma77's rule)
597
- * @param {boolean} [raw.ground=true] one rigid floor shift for the clip
598
- * (soma77's rule; see the note at the bottom of this function)
599
- * @returns {{frames:number, fps:number, rotMats:Float32Array, rootPos:Float32Array, posedJoints:Float32Array}}
600
- * rotMats are LOCAL rotations, [T,27,3,3] flattened row-major — the exact
601
- * five fields soma77ToCskel27Motion returns, nothing more.
602
- */
603
- export function hml22ToCskel27Motion(raw) {
604
- if (!raw || typeof raw !== "object") {
605
- throw new Error("hml22ToCskel27Motion: expected an object { positions, fps }");
606
- }
607
- const {
608
- positions,
609
- fps = 20,
610
- frames: declaredFrames,
611
- reference = canonicalCskel27Reference(),
612
- scaleRoot = true,
613
- ground = true,
614
- } = raw;
615
-
616
- // `ArrayBuffer.isView || Array.isArray` and not a duck-typed `.length` check:
617
- // a string has a length too, and would sail past the shape check to die
618
- // later as a NaN with a misleading joint name.
619
- if (!(ArrayBuffer.isView(positions) || Array.isArray(positions))) {
620
- throw new Error(
621
- "hml22ToCskel27Motion: positions must be a Float32Array or number[] of [T,22,3] world metres"
622
- );
623
- }
624
- if (positions.length === 0 || positions.length % (22 * 3) !== 0) {
625
- throw new Error(
626
- `hml22ToCskel27Motion: positions length ${positions.length} is not a whole number of 22-joint frames (multiple of 66)`
627
- );
628
- }
629
- const frames = positions.length / (22 * 3);
630
- if (declaredFrames !== undefined && declaredFrames !== frames) {
631
- throw new Error(
632
- `hml22ToCskel27Motion: frames says ${declaredFrames} but positions holds ${frames} frames`
633
- );
634
- }
635
- if (!Number.isInteger(fps) || fps < 1) {
636
- throw new Error(`hml22ToCskel27Motion: fps must be a positive integer, got ${fps}`);
637
- }
638
- // Scan once up front rather than letting a NaN propagate into a rotation
639
- // matrix, where it would surface a thousand lines downstream as an empty
640
- // viewport instead of as "frame 61 left_wrist z is NaN".
641
- for (let index = 0; index < positions.length; index += 1) {
642
- if (!Number.isFinite(positions[index])) {
643
- const frame = Math.floor(index / (22 * 3));
644
- const joint = Math.floor((index % (22 * 3)) / 3);
645
- throw new Error(
646
- `hml22ToCskel27Motion: positions[${index}] is ${positions[index]} ` +
647
- `(frame ${frame}, joint ${joint} ${HML22_JOINTS[joint]}, axis ${"xyz"[index % 3]})`
648
- );
649
- }
650
- }
651
-
652
- const boneOffsets = deriveBoneOffsets(reference.posed_joints, reference.local_rot_mats);
653
- const rest = restPositionsHml22(reference);
654
-
655
- // Leg-length ratio, measured on the source's own first frame against the
656
- // reference body, so a stride generated for HumanML3D's average body covers
657
- // the same fraction of a step on ours. Same rule as soma77ToCskel27Motion.
658
- const firstFrame = [];
659
- for (let joint = 0; joint < 22; joint += 1) {
660
- firstFrame.push([positions[joint * 3], positions[joint * 3 + 1], positions[joint * 3 + 2]]);
661
- }
662
- const sourceLeg = chainLengthHml(firstFrame, ["left_hip", "left_knee", "left_ankle"]);
663
- const referenceLeg = chainLengthCskel(reference.posed_joints, ["LeftUpLeg", "LeftLeg", "LeftFoot"]);
664
- if (!(sourceLeg > 0)) {
665
- throw new Error("hml22ToCskel27Motion: source leg length measured as zero");
666
- }
667
- const rootScale = scaleRoot ? referenceLeg / sourceLeg : 1;
668
-
669
- const rotMats = new Float32Array(frames * 27 * 9);
670
- const rootPos = new Float32Array(frames * 3);
671
- const outPosed = new Float32Array(frames * 27 * 3);
672
- let floorY = Infinity;
673
- let carry = null;
674
-
675
- for (let frame = 0; frame < frames; frame += 1) {
676
- const base = frame * 22 * 3;
677
- const observed = new Array(22);
678
- for (let joint = 0; joint < 22; joint += 1) {
679
- observed[joint] = [
680
- positions[base + joint * 3],
681
- positions[base + joint * 3 + 1],
682
- positions[base + joint * 3 + 2],
683
- ];
684
- }
685
-
686
- // `carry` threads the previous frame's branch choices in and this frame's
687
- // out — the whole of the temporal-continuity guarantee lives on this line.
688
- const pose = hml22PoseToCskel27(observed, { rest, boneOffsets, rootScale, carry, frame });
689
- carry = pose.carry;
690
-
691
- for (const name of GROUND_JOINTS) {
692
- const y = pose.posed[CSKEL_INDEX.get(name)][1];
693
- if (y < floorY) floorY = y;
694
- }
695
-
696
- const rotBase = frame * 27 * 9;
697
- for (let joint = 0; joint < 27; joint += 1) {
698
- const m = pose.locals[joint];
699
- for (let row = 0; row < 3; row += 1) {
700
- for (let col = 0; col < 3; col += 1) {
701
- rotMats[rotBase + joint * 9 + row * 3 + col] = m[row][col];
702
- }
703
- }
704
- }
705
- rootPos[frame * 3] = pose.root[0];
706
- rootPos[frame * 3 + 1] = pose.root[1];
707
- rootPos[frame * 3 + 2] = pose.root[2];
708
- const posBase = frame * 27 * 3;
709
- for (let joint = 0; joint < 27; joint += 1) {
710
- outPosed[posBase + joint * 3] = pose.posed[joint][0];
711
- outPosed[posBase + joint * 3 + 1] = pose.posed[joint][1];
712
- outPosed[posBase + joint * 3 + 2] = pose.posed[joint][2];
713
- }
714
- }
715
-
716
- // ONE RIGID FLOOR SHIFT for the whole clip, so the lowest foot sample of the
717
- // clip touches Y=0. This is soma77ToCskel27Motion's convention copied
718
- // deliberately and it is a SPLICING requirement, not a cosmetic one: a
719
- // ProjFlow line edit gets spliced back into a Kimodo/ARDY take, and if the two
720
- // converters grounded differently the figure would step up or down at the
721
- // seam. Per-frame grounding is the wrong answer for the same reason it is
722
- // there: it would flatten the arc of a jump, which is exactly the motion this
723
- // pipeline exists to stage.
724
- if (ground && Number.isFinite(floorY) && floorY !== 0) {
725
- for (let frame = 0; frame < frames; frame += 1) {
726
- rootPos[frame * 3 + 1] -= floorY;
727
- const posBase = frame * 27 * 3;
728
- for (let joint = 0; joint < 27; joint += 1) outPosed[posBase + joint * 3 + 1] -= floorY;
729
- }
730
- }
731
-
732
- return { frames, fps, rotMats, rootPos, posedJoints: outPosed };
733
- }
734
-
735
- export { IDENTITY as CSKEL27_IDENTITY };