cozyclay 2.1.1 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (585) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/README.md +12 -30
  3. package/bin/agent/agent-routes.mjs +43 -116
  4. package/bin/agent/agent-runner.mjs +14 -15
  5. package/bin/agent/cliproxy-provider.mjs +135 -0
  6. package/bin/agent/credential-store.mjs +30 -7
  7. package/bin/agent/pi-tools.mjs +4 -1
  8. package/bin/agent/provider-keys.mjs +32 -0
  9. package/bin/agent/provider-login.mjs +110 -0
  10. package/bin/agent/provider-routes.mjs +99 -0
  11. package/bin/agent/providers.mjs +19 -97
  12. package/bin/agent/script-tool.mjs +17 -4
  13. package/bin/agent/session-store.mjs +2 -2
  14. package/bin/agent/studio-context-budget.mjs +93 -0
  15. package/bin/agent/studio-motion-reference.mjs +23 -0
  16. package/bin/agent/studio-prompt.mjs +1 -1
  17. package/bin/agent/studio-rebase-text.mjs +32 -0
  18. package/bin/agent/studio-rebase.mjs +26 -39
  19. package/bin/agent/studio-tools.mjs +9 -28
  20. package/bin/agent/tool-arguments.mjs +49 -0
  21. package/bin/agent/turn-frames.mjs +66 -0
  22. package/bin/codex-auth.mjs +9 -2
  23. package/bin/cozyclay.mjs +10 -116
  24. package/bin/live/cli.mjs +3 -3
  25. package/bin/mcp-runtime.mjs +2 -12
  26. package/bin/package-signature.mjs +0 -4
  27. package/dist/app/index.html +7 -6
  28. package/dist/assets/EXRLoader-CvVIvGVE.js +1 -0
  29. package/dist/assets/analytics-BWCqdgWA.js +1 -0
  30. package/dist/assets/app-DfTCxaKk.js +5087 -0
  31. package/dist/assets/app-f-fkV5uP.css +1 -0
  32. package/dist/assets/compile-worker-BxKXh07s.js +5 -0
  33. package/dist/assets/fflate.module-KbGj5-H6.js +5 -0
  34. package/dist/assets/{first-shot-handoff-msLI71g1.js → first-shot-handoff-DNO8kv_N.js} +1 -1
  35. package/dist/assets/{landing-ChUvB_zL.js → landing-yfZaJkWc.js} +1 -1
  36. package/dist/assets/scene-asset-sync-U3kS2rvK.js +20 -0
  37. package/dist/assets/scene-asset-sync-_S8NQl5B.css +1 -0
  38. package/dist/assets/workflow-D78eiKTa.js +179 -0
  39. package/dist/cozyclay-package.json +1 -1
  40. package/dist/index.html +3 -3
  41. package/dist/sw.js +3 -6
  42. package/dist/workflow/index.html +5 -4
  43. package/mcp/LIVE-PROTOCOL.md +7 -47
  44. package/mcp/README.md +16 -26
  45. package/mcp/live-hub.mjs +6 -10
  46. package/mcp/package.json +1 -3
  47. package/mcp/tool-handlers.mjs +4 -168
  48. package/package.json +3 -22
  49. package/src/App.jsx +151 -2625
  50. package/src/agent-progress.js +80 -0
  51. package/src/agent-turn-mark.js +30 -0
  52. package/src/analytics.js +1 -19
  53. package/src/app-context.js +12 -2
  54. package/src/app-stage.jsx +2 -34
  55. package/src/asset-pane.jsx +6 -5
  56. package/src/camera-block.js +21 -0
  57. package/src/camera-direction.js +120 -0
  58. package/src/character-kind.js +3 -9
  59. package/src/command-bus.js +10 -8
  60. package/src/commands/cast.js +5 -34
  61. package/src/commands/elements/character.js +1 -1
  62. package/src/commands/elements/motion.js +1 -6
  63. package/src/commands/elements/object.js +6 -2
  64. package/src/commands/elements/shot.js +4 -1
  65. package/src/commands/elements.js +38 -14
  66. package/src/commands/index.js +3 -1
  67. package/src/commands/kits.js +37 -0
  68. package/src/commands/motion-timing.js +57 -0
  69. package/src/commands/motion.js +9 -152
  70. package/src/commands/motion3-compile.js +65 -0
  71. package/src/commands/motion3.js +214 -0
  72. package/src/commands/objects.js +6 -4
  73. package/src/commands/shot.js +55 -13
  74. package/src/commands/stage.js +14 -1
  75. package/src/commands/view.js +4 -1
  76. package/src/document-mirrors.js +42 -0
  77. package/src/document-store.js +4 -0
  78. package/src/domains/cast.js +19 -194
  79. package/src/domains/motion/motion-range-pin-rules.js +1 -1
  80. package/src/domains/motion/take-source.js +53 -0
  81. package/src/domains/motion.js +60 -1838
  82. package/src/domains/scenes.js +12 -55
  83. package/src/domains/shots.js +2 -2
  84. package/src/domains/stage.js +4 -2
  85. package/src/draft-ghost.js +47 -0
  86. package/src/draft-ghost.jsx +129 -0
  87. package/src/dualview.jsx +4 -3
  88. package/src/effects/EffectsLayer.jsx +112 -0
  89. package/src/effects/effect-meshes.js +232 -0
  90. package/src/effects/effect-record.js +103 -0
  91. package/src/effects/emitter.js +59 -0
  92. package/src/effects/simulate.js +261 -0
  93. package/src/effects/state.js +59 -0
  94. package/src/execution-telemetry.js +2 -3
  95. package/src/first-success-guide.jsx +5 -5
  96. package/src/ghost-set.js +257 -0
  97. package/src/held-object-edit.jsx +113 -0
  98. package/src/held-object-pose.js +175 -0
  99. package/src/hierarchy-model.js +1 -1
  100. package/src/hierarchy-panel.jsx +1 -1
  101. package/src/imported-mesh.jsx +103 -0
  102. package/src/kits/held.js +84 -0
  103. package/src/kits/index.js +70 -0
  104. package/src/kits/smoking.js +57 -0
  105. package/src/light-blockers.js +93 -0
  106. package/src/motion/beat-board-model.js +84 -0
  107. package/src/motion/beat-board.css +124 -0
  108. package/src/motion/beat-board.jsx +60 -0
  109. package/src/motion/center-of-mass.js +66 -0
  110. package/src/motion/fix-collisions.js +850 -0
  111. package/src/motion/physics-review.js +118 -0
  112. package/src/{ardy → motion}/physics-support.js +1 -76
  113. package/src/motion/rig-snapshot.js +22 -0
  114. package/src/motion/take-segments.jsx +140 -0
  115. package/src/{ardy → motion}/timeline-coordinates.js +0 -21
  116. package/src/{ardy → motion}/timeline.css +1 -2
  117. package/src/{ardy → motion}/timeline.jsx +42 -413
  118. package/src/motion/waypoints.js +76 -0
  119. package/src/motion3/beats.js +83 -0
  120. package/src/motion3/body.js +158 -0
  121. package/src/motion3/bones.js +47 -0
  122. package/src/motion3/carried.js +47 -0
  123. package/src/motion3/compile-job.js +79 -0
  124. package/src/motion3/compile-worker.js +10 -0
  125. package/src/motion3/compiler/anchors.js +113 -0
  126. package/src/motion3/compiler/arm-orient.js +128 -0
  127. package/src/motion3/compiler/channels.js +125 -0
  128. package/src/motion3/compiler/compile.js +205 -0
  129. package/src/motion3/compiler/gait-rhythm.js +97 -0
  130. package/src/motion3/compiler/grip.js +171 -0
  131. package/src/motion3/compiler/head-steady.js +48 -0
  132. package/src/motion3/compiler/hold.js +134 -0
  133. package/src/motion3/compiler/idle.js +113 -0
  134. package/src/motion3/compiler/limbs.js +197 -0
  135. package/src/motion3/compiler/math.js +72 -0
  136. package/src/motion3/compiler/pose.js +177 -0
  137. package/src/motion3/compiler/quat.js +49 -0
  138. package/src/motion3/compiler/skeleton.js +74 -0
  139. package/src/motion3/compiler/validate-common.js +32 -0
  140. package/src/motion3/compiler/validate-goals.js +118 -0
  141. package/src/motion3/compiler/validate-objects.js +144 -0
  142. package/src/motion3/compiler/validate-parts.js +118 -0
  143. package/src/motion3/compiler/validate.js +122 -0
  144. package/src/motion3/compiler/vocab.js +74 -0
  145. package/src/motion3/compiler/walk-pelvis.js +275 -0
  146. package/src/motion3/compiler/walk.js +207 -0
  147. package/src/motion3/compiler/wrist-clamp.js +32 -0
  148. package/src/motion3/delta.js +47 -0
  149. package/src/motion3/describe.js +177 -0
  150. package/src/motion3/docs.js +27 -0
  151. package/src/motion3/edit-carry.js +21 -0
  152. package/src/motion3/fix/ease.js +16 -0
  153. package/src/motion3/fix/ground.js +37 -0
  154. package/src/motion3/fix/index.js +52 -0
  155. package/src/motion3/fix/push.js +71 -0
  156. package/src/motion3/fix/reach.js +46 -0
  157. package/src/motion3/fix/retime.js +79 -0
  158. package/src/motion3/fix/self.js +211 -0
  159. package/src/motion3/fix/slide.js +70 -0
  160. package/src/motion3/fix/turn.js +51 -0
  161. package/src/motion3/follow.js +39 -0
  162. package/src/motion3/free-motion.js +204 -0
  163. package/src/motion3/grip-choice.js +130 -0
  164. package/src/motion3/hold-plan.js +157 -0
  165. package/src/motion3/json-patch.js +77 -0
  166. package/src/motion3/key-aim.js +105 -0
  167. package/src/motion3/key-edit.js +82 -0
  168. package/src/motion3/lenient.js +127 -0
  169. package/src/motion3/object-keys.js +236 -0
  170. package/src/motion3/object-pose.js +182 -0
  171. package/src/motion3/object-tracks.js +12 -0
  172. package/src/motion3/people.js +82 -0
  173. package/src/motion3/pipeline.js +102 -0
  174. package/src/motion3/playback.js +51 -0
  175. package/src/motion3/probes.js +91 -0
  176. package/src/motion3/qa/body-checks.js +34 -0
  177. package/src/motion3/qa/check-names.js +14 -0
  178. package/src/motion3/qa/checks.js +125 -0
  179. package/src/motion3/qa/contact-checks.js +51 -0
  180. package/src/motion3/qa/explain.js +80 -0
  181. package/src/motion3/qa/fmt.js +5 -0
  182. package/src/motion3/qa/foot-checks.js +72 -0
  183. package/src/motion3/qa/geometry.js +77 -0
  184. package/src/motion3/qa/goals.js +191 -0
  185. package/src/motion3/qa/ground.js +60 -0
  186. package/src/motion3/qa/hints-contact.js +195 -0
  187. package/src/motion3/qa/hints-goal.js +160 -0
  188. package/src/motion3/qa/hints-knee.js +24 -0
  189. package/src/motion3/qa/hints-motion.js +209 -0
  190. package/src/motion3/qa/hints-object.js +91 -0
  191. package/src/motion3/qa/index.js +44 -0
  192. package/src/motion3/qa/key-checks.js +82 -0
  193. package/src/motion3/qa/near.js +96 -0
  194. package/src/motion3/qa/object-checks.js +135 -0
  195. package/src/motion3/qa/patch.js +161 -0
  196. package/src/motion3/qa/people-checks.js +37 -0
  197. package/src/motion3/qa/severity.js +38 -0
  198. package/src/motion3/qa/spec-checks.js +65 -0
  199. package/src/motion3/qa/style.js +228 -0
  200. package/src/motion3/qa/wrist-checks.js +69 -0
  201. package/src/motion3/qa/wrist-obs.js +60 -0
  202. package/src/motion3/refs.js +129 -0
  203. package/src/motion3/repair-tune.js +121 -0
  204. package/src/motion3/repair.js +83 -0
  205. package/src/motion3/report.js +67 -0
  206. package/src/motion3/rigs/index.js +14 -0
  207. package/src/motion3/rigs/x-bot-tpose-body.json +1 -0
  208. package/src/motion3/rigs/x-bot-tpose.json +1 -0
  209. package/src/motion3/rigs/y-bot-tpose-body.json +1 -0
  210. package/src/motion3/rigs/y-bot-tpose.json +1 -0
  211. package/src/motion3/scene.js +60 -0
  212. package/src/motion3/schema/brief.js +89 -0
  213. package/src/motion3/schema/fields.js +44 -0
  214. package/src/motion3/schema/spec-examples.js +46 -0
  215. package/src/motion3/schema/topics.js +113 -0
  216. package/src/motion3/schema/types.js +149 -0
  217. package/src/motion3/search.js +117 -0
  218. package/src/motion3/second-hand.js +112 -0
  219. package/src/motion3/settle.js +136 -0
  220. package/src/motion3/skills/basic.js +83 -0
  221. package/src/motion3/skills/book.js +76 -0
  222. package/src/motion3/skills/car.js +145 -0
  223. package/src/motion3/skills/flip.js +200 -0
  224. package/src/motion3/skills/frames-auto.js +29 -0
  225. package/src/motion3/skills/impact.js +109 -0
  226. package/src/motion3/skills/index.js +85 -0
  227. package/src/motion3/skills/info.js +80 -0
  228. package/src/motion3/skills/smoke-kit.js +109 -0
  229. package/src/motion3/skills/smoke.js +160 -0
  230. package/src/motion3/spec-store.js +59 -0
  231. package/src/motion3/take-edit.js +155 -0
  232. package/src/motion3/take.js +63 -0
  233. package/src/motion3/timeline.js +127 -0
  234. package/src/motion3/views.js +121 -0
  235. package/src/motion3/walk-plan.js +119 -0
  236. package/src/object-catalog.jsx +7 -1
  237. package/src/object-colors.js +98 -0
  238. package/src/object-gizmo-parts.js +115 -0
  239. package/src/object-gizmo.jsx +37 -122
  240. package/src/object-glow.js +139 -0
  241. package/src/object-hidden.js +33 -0
  242. package/src/object-look.js +196 -0
  243. package/src/object-size.js +10 -0
  244. package/src/object-travel.js +71 -6
  245. package/src/panels/CameraPanel.jsx +7 -7
  246. package/src/panels/HdrEnvironmentControls.jsx +59 -0
  247. package/src/panels/LightPanel.jsx +25 -4
  248. package/src/panels/ObjectLookPanel.jsx +88 -0
  249. package/src/panels/PosePanel.jsx +2 -17
  250. package/src/panels/RigControlPanel.jsx +5 -53
  251. package/src/panels/SubjectBox.jsx +4 -1
  252. package/src/panels/details.css +14 -0
  253. package/src/posestudio.jsx +4 -27
  254. package/src/project-browser.jsx +3 -4
  255. package/src/project-resources.js +18 -31
  256. package/src/project.js +13 -104
  257. package/src/props.jsx +25 -280
  258. package/src/qa-label.js +25 -0
  259. package/src/rail-dot-drag.js +23 -0
  260. package/src/render-passes.js +10 -0
  261. package/src/resource-status.jsx +1 -0
  262. package/src/result-modal.jsx +4 -19
  263. package/src/room.jsx +20 -12
  264. package/src/scene-assets.js +8 -6
  265. package/src/scene-light-place.js +72 -0
  266. package/src/scene-lights.jsx +148 -0
  267. package/src/scene-objects.js +31 -139
  268. package/src/scenes.js +15 -105
  269. package/src/semantic-edit.js +2 -5
  270. package/src/set-pieces.jsx +183 -0
  271. package/src/settings-menu.jsx +3 -11
  272. package/src/shell/BottomDock.jsx +9 -51
  273. package/src/shell/DetailsSlot.jsx +16 -200
  274. package/src/shell/GhostsMenuGroup.jsx +74 -0
  275. package/src/shell/LibrarySlot.jsx +1 -3
  276. package/src/shell/MenuBar.jsx +3 -3
  277. package/src/shell/PreferencesDialog.jsx +15 -264
  278. package/src/shell/PreferencesSlot.jsx +2 -8
  279. package/src/shell/TopBar.jsx +5 -84
  280. package/src/shell/ViewportToolbar.jsx +10 -70
  281. package/src/shell/agent-glass.css +19 -6
  282. package/src/shell/details-selection.js +42 -0
  283. package/src/shell/ghosts-menu.css +22 -0
  284. package/src/shell/glass-regions.css +29 -3
  285. package/src/shell/preferences.css +6 -1
  286. package/src/shot-authoring.js +4 -7
  287. package/src/soft-shadows.js +37 -0
  288. package/src/stage-attach-frames.js +12 -12
  289. package/src/stage-canvas-rigs.jsx +3 -3
  290. package/src/stage-capture.jsx +42 -8
  291. package/src/stage-characters.jsx +22 -216
  292. package/src/stage-editor-gizmos.jsx +6 -6
  293. package/src/stage-environment.js +93 -0
  294. package/src/stage-hdr-builtins.js +107 -0
  295. package/src/stage-hdr-image.js +116 -0
  296. package/src/stage-hdr-library.js +70 -0
  297. package/src/stage-hdr-map.js +94 -0
  298. package/src/stage-hdr.js +130 -0
  299. package/src/stage-lighting.js +149 -0
  300. package/src/stage-occlusion.js +210 -0
  301. package/src/stage-sky.jsx +159 -0
  302. package/src/stage-startup-defaults.js +5 -9
  303. package/src/studio-actions.js +16 -14
  304. package/src/studio-agent-commands.js +19 -16
  305. package/src/studio-agent-motion.js +9 -9
  306. package/src/studio-agent-protocol.js +47 -77
  307. package/src/studio-app-binding.js +27 -47
  308. package/src/studio-asset-list.js +17 -0
  309. package/src/studio-contact-sheet.js +37 -5
  310. package/src/studio-elements.js +27 -7
  311. package/src/studio-entity-bounds.js +18 -0
  312. package/src/studio-error-codes.js +15 -0
  313. package/src/studio-export-framing.js +152 -0
  314. package/src/studio-lighting-check.js +103 -0
  315. package/src/studio-motion-visual.js +150 -0
  316. package/src/studio-schema-errors.js +80 -0
  317. package/src/studio-support-gap.js +74 -0
  318. package/src/styles.css +1 -19
  319. package/src/timeline-defaults.js +5 -0
  320. package/src/timeline-extent.js +8 -20
  321. package/src/tutorial-walk.js +17 -0
  322. package/src/ui-scrub.js +1 -1
  323. package/src/use-shot-camera-tracks.js +12 -0
  324. package/src/view-ghosts.js +97 -0
  325. package/src/view-ghosts.jsx +49 -0
  326. package/src/workflow/AgentPanel.jsx +47 -166
  327. package/src/workflow/AgentProgressCard.jsx +109 -0
  328. package/src/workflow/AgentReplyText.jsx +37 -0
  329. package/src/workflow/AgentTurnSteps.jsx +84 -0
  330. package/src/workflow/LoginPanel.jsx +140 -0
  331. package/src/workflow/WorkflowBuilder.jsx +7 -23
  332. package/src/workflow/agent-client.js +54 -51
  333. package/src/workflow/agent-panel.css +71 -1
  334. package/src/workflow/agent-progress-card.css +167 -0
  335. package/src/workflow/agent-progress-view.js +47 -0
  336. package/src/workflow/agent-turn-steps.css +113 -0
  337. package/src/workflow/agent-turn.js +191 -0
  338. package/src/workflow/login-client.js +34 -0
  339. package/src/workflow/motion-input.js +18 -31
  340. package/src/workflow/node-schema.js +1 -1
  341. package/src/workflow/reply-markdown.js +69 -0
  342. package/src/workflow/scene-asset-sync.js +8 -8
  343. package/tools/agent-session-stats.mjs +61 -0
  344. package/tools/dev-full.mjs +5 -56
  345. package/tools/eslint-baseline.json +12 -13
  346. package/tools/knip-baseline.json +0 -179
  347. package/tools/loc-baseline.json +31 -81
  348. package/tools/motion3/extract-body.mjs +103 -0
  349. package/tools/motion3/extract-rig.mjs +85 -0
  350. package/tools/process-supervisor.mjs +0 -88
  351. package/tools/run-tests.mjs +9 -125
  352. package/bin/agent/motion-runtime.mjs +0 -76
  353. package/dist/ardy/cskel27-rest.json +0 -557
  354. package/dist/assets/analytics-C9pMpW7s.js +0 -1
  355. package/dist/assets/app-B_2XB9rw.css +0 -1
  356. package/dist/assets/app-DAuvKPP8.js +0 -4920
  357. package/dist/assets/config-Bx7pYsD9.js +0 -1
  358. package/dist/assets/config-DeoFTYzb.css +0 -1
  359. package/dist/assets/demo-CvKuBgy5.js +0 -1
  360. package/dist/assets/scene-asset-sync-DKkpjKBV.js +0 -19
  361. package/dist/assets/scene-asset-sync-DjJ_cJ2R.css +0 -1
  362. package/dist/assets/ticket-BPk2Ni95.js +0 -1
  363. package/dist/assets/workflow-jJRBR94Z.js +0 -179
  364. package/dist/demo/walk-then-stop.npz +0 -0
  365. package/mcp/ardy-prompts.mjs +0 -218
  366. package/src/ardy/auto-fix-panel.css +0 -108
  367. package/src/ardy/auto-physics.js +0 -1194
  368. package/src/ardy/client.js +0 -153
  369. package/src/ardy/convert.js +0 -313
  370. package/src/ardy/cskel27-neutral.js +0 -39
  371. package/src/ardy/cskel27.js +0 -77
  372. package/src/ardy/export.js +0 -165
  373. package/src/ardy/fix-collisions.js +0 -2257
  374. package/src/ardy/foot-lock.js +0 -230
  375. package/src/ardy/motion-calibration.js +0 -118
  376. package/src/ardy/motion-edit.js +0 -213
  377. package/src/ardy/motion-url.js +0 -55
  378. package/src/ardy/mp4-duration.js +0 -102
  379. package/src/ardy/npz.js +0 -595
  380. package/src/ardy/physics-panel.css +0 -21
  381. package/src/ardy/physics-panel.jsx +0 -89
  382. package/src/ardy/physics-refine.js +0 -111
  383. package/src/ardy/physics-review.js +0 -567
  384. package/src/ardy/physics-temporal.js +0 -54
  385. package/src/ardy/platform-fit-panel.css +0 -64
  386. package/src/ardy/platform-fit-panel.jsx +0 -41
  387. package/src/ardy/platform-fit.js +0 -326
  388. package/src/ardy/playback.js +0 -711
  389. package/src/ardy/pose-pin.js +0 -97
  390. package/src/ardy/prompt-clips.js +0 -30
  391. package/src/ardy/retime.js +0 -211
  392. package/src/ardy/root-drop.js +0 -369
  393. package/src/ardy/timeline-resize.js +0 -13
  394. package/src/ardy/to-cskel27.js +0 -225
  395. package/src/ardy/trim.js +0 -33
  396. package/src/ardy/waypoints.js +0 -369
  397. package/src/ardy-bridge-contract.js +0 -281
  398. package/src/domains/motion/motion-video-capture.js +0 -664
  399. package/src/i2v-motion-client.js +0 -77
  400. package/src/i2v-motion-studio.jsx +0 -180
  401. package/src/line-edit.js +0 -1348
  402. package/src/motion/generation.js +0 -105
  403. package/src/motion-readiness-ui.jsx +0 -67
  404. package/src/motion-readiness.js +0 -40
  405. package/src/motion-resources.js +0 -177
  406. package/src/motion-store.js +0 -65
  407. package/src/motion-trail.js +0 -601
  408. package/src/multimodel-ingest.js +0 -394
  409. package/src/panels/PromptBlocksPanel.jsx +0 -444
  410. package/src/panels/VideoCapturePanel.jsx +0 -181
  411. package/src/pose-extract/browser.js +0 -42
  412. package/src/pose-extract/detector.js +0 -100
  413. package/src/pose-extract/fit.js +0 -227
  414. package/src/pose-extract/image-frame.js +0 -51
  415. package/src/pose-extract/index.js +0 -58
  416. package/src/pose-extract/keyframes.js +0 -74
  417. package/src/pose-extract/landmarks.js +0 -162
  418. package/src/pose-extract/mirror.js +0 -153
  419. package/src/pose-extract/take.js +0 -131
  420. package/src/pose-extract/video-frames.js +0 -91
  421. package/src/take-recipe.js +0 -165
  422. package/src/trail-key-conflicts.js +0 -39
  423. package/src/trail-pick.js +0 -59
  424. package/src/use-i2v-capture-modal.js +0 -15
  425. package/tools/ardy/BRIDGE.md +0 -37
  426. package/tools/ardy/GVHMR-NPZ.md +0 -68
  427. package/tools/ardy/README.md +0 -124
  428. package/tools/ardy/__pycache__/cclay_gvhmr_worker.cpython-313.pyc +0 -0
  429. package/tools/ardy/artifacts.mjs +0 -34
  430. package/tools/ardy/bridge.mjs +0 -1732
  431. package/tools/ardy/bvh-cskel27.mjs +0 -1265
  432. package/tools/ardy/cclay_constrained_generate.py +0 -561
  433. package/tools/ardy/cclay_gvhmr_worker.py +0 -264
  434. package/tools/ardy/cclay_motion_edit.py +0 -510
  435. package/tools/ardy/cclay_sequence_generate.py +0 -705
  436. package/tools/ardy/cclay_worker.py +0 -304
  437. package/tools/ardy/dump-npz.py +0 -205
  438. package/tools/ardy/extract-rest.mjs +0 -299
  439. package/tools/ardy/extract.mjs +0 -507
  440. package/tools/ardy/footage.mjs +0 -467
  441. package/tools/ardy/gvhmr-floor.mjs +0 -102
  442. package/tools/ardy/gvhmr-to-cskel27.mjs +0 -21
  443. package/tools/ardy/gvhmr_fastpath.py +0 -168
  444. package/tools/ardy/gvhmr_trajectory.py +0 -240
  445. package/tools/ardy/merge-text-encoder.py +0 -160
  446. package/tools/ardy/mocap-metrics.mjs +0 -200
  447. package/tools/ardy/mocap-quality-gate.mjs +0 -110
  448. package/tools/ardy/motion-stabilize.mjs +0 -348
  449. package/tools/ardy/npz.mjs +0 -414
  450. package/tools/ardy/pose-to-npz.mjs +0 -106
  451. package/tools/ardy/prompt-limits.mjs +0 -5
  452. package/tools/ardy/run-edit-on-box.sh +0 -73
  453. package/tools/ardy/run-local.mjs +0 -233
  454. package/tools/ardy/run-on-box.sh +0 -593
  455. package/tools/ardy/run-sequence-on-box.sh +0 -177
  456. package/tools/ardy/runners/gvhmr-worker.mjs +0 -206
  457. package/tools/ardy/runners/index.mjs +0 -18
  458. package/tools/ardy/runners/local.mjs +0 -455
  459. package/tools/ardy/runners/proc.mjs +0 -125
  460. package/tools/ardy/runners/remote.mjs +0 -252
  461. package/tools/ardy/setup-local.mjs +0 -189
  462. package/tools/ardy/setup-text-encoder-on-box.sh +0 -93
  463. package/tools/ardy/setup-text-encoder.py +0 -306
  464. package/tools/ardy/smpl-cskel27.mjs +0 -178
  465. package/tools/ardy/visual-qa.mjs +0 -196
  466. package/tools/ardy/vq-car.mjs +0 -52
  467. package/tools/bench/EXP3.md +0 -61
  468. package/tools/bench/cclay_bench_extract_incam.py +0 -125
  469. package/tools/bench/cclay_bench_extract_obs.py +0 -204
  470. package/tools/bench/cclay_bench_runner.py +0 -42
  471. package/tools/bench/cube-contact.mjs +0 -162
  472. package/tools/bench/exp3.mjs +0 -152
  473. package/tools/bench/extract-bench-lib.mjs +0 -216
  474. package/tools/bench/extract-bench.mjs +0 -302
  475. package/tools/bench/fal-generate.mjs +0 -88
  476. package/tools/bench/fit/README.md +0 -189
  477. package/tools/bench/fit/camera.mjs +0 -59
  478. package/tools/bench/fit/contact.mjs +0 -419
  479. package/tools/bench/fit/footlock.mjs +0 -234
  480. package/tools/bench/fit/motion.mjs +0 -85
  481. package/tools/bench/fit/pin.mjs +0 -27
  482. package/tools/bench/fit/remote.mjs +0 -105
  483. package/tools/bench/fit-bench.mjs +0 -126
  484. package/tools/bench/fit-sanity.mjs +0 -45
  485. package/tools/bench/metrics.mjs +0 -313
  486. package/tools/bench/obs/depth.mjs +0 -258
  487. package/tools/bench/obs/extrinsics.mjs +0 -175
  488. package/tools/bench/obs/fit_mannequin_betas.py +0 -340
  489. package/tools/bench/obs/ground.mjs +0 -294
  490. package/tools/bench/obs/heading.mjs +0 -151
  491. package/tools/bench/obs/ladder.mjs +0 -555
  492. package/tools/bench/obs/mannequin-betas.json +0 -124
  493. package/tools/bench/obs/remote.mjs +0 -83
  494. package/tools/bench/obs/rest_joints.py +0 -52
  495. package/tools/bench/obs/ybot-targets.mjs +0 -49
  496. package/tools/bench/obs-bench.mjs +0 -520
  497. package/tools/bench/score.mjs +0 -343
  498. package/tools/bench/summarize.mjs +0 -86
  499. package/tools/demo-worker/README.md +0 -137
  500. package/tools/demo-worker/api-client.mjs +0 -233
  501. package/tools/demo-worker/generate.mjs +0 -301
  502. package/tools/demo-worker/index.mjs +0 -466
  503. package/tools/demo-worker/sign-core.mjs +0 -112
  504. package/tools/demo-worker/sign.mjs +0 -161
  505. package/tools/gt-render/browser.mjs +0 -182
  506. package/tools/gt-render/camera-math.mjs +0 -276
  507. package/tools/gt-render/page.mjs +0 -267
  508. package/tools/gt-render/render.mjs +0 -514
  509. package/tools/gt-render/scene-box.mjs +0 -31
  510. package/tools/gt-render/take-transform.mjs +0 -87
  511. package/tools/kimodo/compare-seam.mjs +0 -108
  512. package/tools/kimodo/constraints.mjs +0 -244
  513. package/tools/kimodo/edit.mjs +0 -259
  514. package/tools/kimodo/effector-constraints.mjs +0 -200
  515. package/tools/kimodo/generate.mjs +0 -646
  516. package/tools/kimodo/local-output.mjs +0 -265
  517. package/tools/kimodo/measure-pose.mjs +0 -65
  518. package/tools/kimodo/measure-preserve.mjs +0 -899
  519. package/tools/kimodo/measure-waypoints.mjs +0 -55
  520. package/tools/kimodo/mlx-generate.py +0 -111
  521. package/tools/kimodo/pose-constraints.mjs +0 -248
  522. package/tools/kimodo/preserve-mask.mjs +0 -543
  523. package/tools/kimodo/read-npz.mjs +0 -189
  524. package/tools/kimodo/run-edit-on-box.mjs +0 -155
  525. package/tools/kimodo/run-sequence-on-box.mjs +0 -178
  526. package/tools/kimodo/runner.mjs +0 -284
  527. package/tools/kimodo/seam-batch.mjs +0 -77
  528. package/tools/kimodo/setup-local.mjs +0 -30
  529. package/tools/kimodo/setup-on-box.sh +0 -339
  530. package/tools/kimodo/soma77-to-cskel27.mjs +0 -305
  531. package/tools/morphgs/assets/gen_truth.py +0 -39
  532. package/tools/morphgs/assets/mesh_ori_rig.txt +0 -27
  533. package/tools/morphgs/assets/playback-check.mjs +0 -30
  534. package/tools/morphgs/demo-gate.sh +0 -26
  535. package/tools/morphgs/fbx2morphgs.mjs +0 -68
  536. package/tools/morphgs/morphgs-to-cskel27.mjs +0 -105
  537. package/tools/morphgs/patches/preprocess_src-none-mode.patch +0 -76
  538. package/tools/morphgs/setup-on-cluster.sh +0 -126
  539. package/tools/projflow/driver.py +0 -1069
  540. package/tools/projflow/generate.mjs +0 -805
  541. package/tools/projflow/hml22-to-cskel27.mjs +0 -735
  542. package/tools/projflow/line-edit-job.mjs +0 -547
  543. package/tools/projflow/replay.mjs +0 -452
  544. package/tools/projflow/runner.mjs +0 -269
  545. package/tools/projflow/service.mjs +0 -742
  546. package/tools/track/DESIGN.md +0 -513
  547. package/tools/track/backfill-provenance.mjs +0 -116
  548. package/tools/track/budget.mjs +0 -8
  549. package/tools/track/check-rig.mjs +0 -209
  550. package/tools/track/diagnostics.schema.json +0 -60
  551. package/tools/track/export-rig.mjs +0 -256
  552. package/tools/track/fallback.mjs +0 -7
  553. package/tools/track/fk-parity-fixture.mjs +0 -161
  554. package/tools/track/gate.mjs +0 -349
  555. package/tools/track/masks.mjs +0 -318
  556. package/tools/track/metrics.mjs +0 -201
  557. package/tools/track/publish-obs.mjs +0 -108
  558. package/tools/track/py/check_env.py +0 -61
  559. package/tools/track/py/eval_lr.py +0 -229
  560. package/tools/track/py/lr_viterbi.py +0 -294
  561. package/tools/track/py/masks.py +0 -405
  562. package/tools/track/py/objective.py +0 -382
  563. package/tools/track/py/rig.py +0 -508
  564. package/tools/track/py/scene.py +0 -326
  565. package/tools/track/py/test_joint_indices.py +0 -123
  566. package/tools/track/py/test_lr_viterbi.py +0 -195
  567. package/tools/track/py/test_masks.py +0 -229
  568. package/tools/track/py/test_rig.py +0 -288
  569. package/tools/track/py/test_scene.py +0 -354
  570. package/tools/track/py/test_track.py +0 -328
  571. package/tools/track/py/track.py +0 -411
  572. package/tools/track/remote.mjs +0 -140
  573. package/tools/track/rig-dump.mjs +0 -227
  574. package/tools/track/run-box-tests.mjs +0 -14
  575. package/tools/track/run-box.mjs +0 -168
  576. package/tools/track/setup-box.sh +0 -59
  577. package/tools/track/study-2d.mjs +0 -728
  578. /package/src/{ardy → motion}/collision-blockers.js +0 -0
  579. /package/src/{ardy → motion}/ground.js +0 -0
  580. /package/src/{ardy → motion}/ik-drag.js +0 -0
  581. /package/src/{ardy → motion}/ik-key-json.js +0 -0
  582. /package/src/{ardy → motion}/ik.js +0 -0
  583. /package/src/{ardy → motion}/physics-surface.js +0 -0
  584. /package/src/{ardy → motion}/playback-clock.js +0 -0
  585. /package/src/{ardy → motion}/range-pin.js +0 -0
@@ -0,0 +1,171 @@
1
+ // The hand a fit puts on a scene body takes a grip on it (lane wrist). A fit only places a point of the
2
+ // hand (reachGrip's fingers on the anchor); with nothing asking for the hand's turn, the wrist stayed
3
+ // straight on the forearm and kept whatever roll the arm left it, so the palm did not face what it held
4
+ // (a box side, a guitar neck: 0 deg of wrist on every holding frame, bench S10, session df388a9b), or
5
+ // the agent keyed wflex/wtwist to fix it at the wrist alone. Now, while a fit's weight is on and its
6
+ // anchor is on a body (an object anchor's body, or any body within NEAR m of the anchor):
7
+ // fingers stay on the forearm's line: no flex or deviation is asked (a power grip turns the
8
+ // forearm, not the wrist: the wrist stays in its comfortable middle)
9
+ // roll the hand turns about the forearm: on a thin body (two half sizes under THIN m: a can, a
10
+ // cup, a bar, a handle, a neck) the knuckle line takes the body's long axis, a cylindrical
11
+ // grasp with the body across the palm, at the end the forearm turns to least; on anything
12
+ // else the end-state-comfort thumb (people take the grip that ends comfortable, Rosenbaum):
13
+ // up when the forearm is level, toward the midline when it points down or up. The turn
14
+ // stops where the forearm's whole twist leaves its comfortable range (arm-orient.js
15
+ // WRIST_COMFORT, +-50 deg: Morrey's daily-task range); the forearm bone takes its share and
16
+ // the wrist the rest (hold.js orient), by the fit's weight
17
+ // point the fingers' point stays where the fit put it, the elbow in the plane the arm had
18
+ // The spec wins: a hand whose wtwist_<k> or wflex_<k> is turned on the frame keeps it, a fit with
19
+ // orient: false is left alone, and a prop anchor is the second-hand grip's (second-hand.js). A hand
20
+ // that carries something keeps the carry's own grip (its grab offset) once the carry leads it. A keyed
21
+ // carry's grab offset is read off the plain solve of its grab frame (object-keys.js attach), so when it
22
+ // carries a body that is not thin (a box, a crate) the carrying hand takes the grip on the reach, on
23
+ // that frame and up to its first key (ownGrip): the offset the carry keeps is then a grip, not the
24
+ // forearm's neutral roll. A thin body (a cup, a can) keeps the neutral offset: its aim keys pick the
25
+ // roll the arm holds most easily from it, and the cylindrical grip measured worse on every bench cup.
26
+ // Those frames read the carried body where it rested before the grab, never a pose the hand moves.
27
+ import { add, sub, scale, norm, cross, dot, len, apply, ry, mul, T, axisRot } from "./math.js";
28
+ import { palmAxes, twoBone } from "./limbs.js";
29
+ import { aimArm, orient } from "./hold.js";
30
+ import { ELBOW_REST, WRIST_COMFORT, wristNeed } from "./arm-orient.js";
31
+ import { resolveAnchor } from "./anchors.js";
32
+ import { sdBox } from "../qa/geometry.js";
33
+ import { partsOf } from "../object-pose.js";
34
+
35
+ const NEAR = 0.06, THIN = 0.045, ALIGN = 0.3, PREFER = 30, DEG = 180 / Math.PI, ITER = 2, UP = [0, 1, 0];
36
+ const cols = (a, b, c) => [[a[0], b[0], c[0]], [a[1], b[1], c[1]], [a[2], b[2], c[2]]];
37
+ const flat = (v, n) => sub(v, scale(n, dot(v, n)));
38
+
39
+ /** The fit of side k whose weight is on most at channels c (null when none), its weight times the arm's IK weight. */
40
+ function activeFit(spec, c, k) {
41
+ let best = null;
42
+ for (const fit of spec.fits ?? []) {
43
+ const w = c[`w_${fit.anchor}_${fit.side}`] ?? 0;
44
+ if (fit.side === k && fit.orient !== false && w > 1e-4 && (!best || w > best.w)) best = { fit, w };
45
+ }
46
+ return best && { ...best, w: best.w * Math.min(1, Math.max(0, c[`armw_${k}`] ?? 0)) };
47
+ }
48
+
49
+ /** The fit's effector point on pose s (anchors.js measureProps' rule for a prop point; the joints' mean otherwise). */
50
+ function effectorAt(sk, spec, s, side, eff) {
51
+ if (eff.prop) {
52
+ const prop = spec.props?.[eff.prop];
53
+ if (!prop) return null;
54
+ const ax = palmAxes(sk, s, side), o = scale(prop.origin.map(b => s.P[sk.index[b]]).reduce(add, [0, 0, 0]), 1 / prop.origin.length);
55
+ const [d, l, n] = eff.point === "origin" ? [0, 0, 0] : prop.points?.[eff.point] ?? [0, 0, 0];
56
+ return add(o, add(scale(ax.d, d), add(scale(ax.l, l), scale(ax.n, n))));
57
+ }
58
+ const js = eff.joints ?? [`${side}HandIndex2`, `${side}HandMiddle2`];
59
+ return scale(js.map(j => s.P[sk.index[j]]).reduce(add, [0, 0, 0]), 1 / js.length);
60
+ }
61
+
62
+ /** The carry entry over body id (its own or a group's) and the side whose prop carries it, else null. */
63
+ function carryOf(ctx, id) {
64
+ const { spec } = ctx, o = ctx.scene?.objects?.[id];
65
+ for (const k of [id, ...(o?.ancestors ?? [])]) { const e = spec.objects?.[k]; if (e?.prop) return { key: k, entry: e, side: spec.props?.[e.prop]?.hand ?? null }; }
66
+ return null;
67
+ }
68
+
69
+ /**
70
+ * Whether hand k takes the grip on keyed carry `key` at frame f although it carries it (see above): a keyed
71
+ * carry that keeps its grab offset, from its reach to its grab frame and up to its first key. Answers the
72
+ * frame to read the carried body at (before the grab), or null.
73
+ */
74
+ function ownGrip(ctx, key, k, f) {
75
+ const o = ctx.spec.objects?.[key];
76
+ const half = ctx.scene?.objects?.[key]?.half;
77
+ if (!ctx.hold?.span || !o?.prop || ctx.spec.props?.[o.prop]?.hand !== k || !o.keys?.length || o.grab === undefined || o.at !== undefined || o.along !== undefined || (half && thinAxis(half) >= 0)) return null;
78
+ const [grab] = ctx.hold.span(key);
79
+ return grab >= 1 && f <= Math.max(grab, ctx.hold.firstKey(key) - 1) ? Math.min(f, grab - 1) : null;
80
+ }
81
+
82
+ /** The body box the grip is on: the anchor's object (its nearest part) or the nearest body within NEAR m; null when none. */
83
+ function bodyAt(ctx, f, k, A, anchor, now) {
84
+ const scene = ctx.scene?.objects ?? {}, ids = anchor?.kind === "object" ? partsOf(ctx.scene, anchor.object) : Object.keys(scene);
85
+ let best = null;
86
+ for (const id of ids) {
87
+ const half = scene[id]?.half;
88
+ if (!half) continue;
89
+ const own = ownGrip(ctx, carryOf(ctx, id)?.key, k, f), pose = ctx.objectPose?.(id, own ?? f, own === null ? now : null) ?? scene[id];
90
+ if (!pose?.R) continue;
91
+ const box = { pos: pose.pos, R: pose.R, half }, d = sdBox(A, box);
92
+ if (!best || d < best.d) best = { box, d, id };
93
+ }
94
+ // a body this hand carries keeps the carry's own grip (its grab offset, which its keys are written on:
95
+ // the same with keys or without), and no other body takes its place
96
+ const carry = best && carryOf(ctx, best.id);
97
+ if (!best || (carry?.side === k && ownGrip(ctx, carry.key, k, f) === null)) return null;
98
+ return anchor?.kind === "object" || best.d <= NEAR ? best.box : null;
99
+ }
100
+
101
+ const AXES = [0, 1, 2];
102
+ /** The long axis of a thin body (two half sizes under THIN m: a can, a cup, a bar, a handle, a neck), else -1. */
103
+ function thinAxis(half) {
104
+ const long = AXES.reduce((a, i) => (half[i] > half[a] ? i : a), 0);
105
+ return AXES.filter(i => i !== long).every(i => half[i] < THIN) ? long : -1;
106
+ }
107
+
108
+ /**
109
+ * The hand's palm frame (columns d, l, n of limbs.js palmAxes) for the grip: the fingers stay on the
110
+ * forearm's line (no flex or deviation added: a power grip turns the forearm, not the wrist) and the
111
+ * hand turns about it. On a thin body the knuckle line takes the body's long axis (a cylindrical grasp:
112
+ * the can, the bar, the neck across the palm) with the thumb at its comfortable end; on anything else,
113
+ * or a thin body along the forearm, the end-state-comfort thumb: up when the forearm is level, toward
114
+ * the midline when it points down or up.
115
+ */
116
+ function gripFrame(side, d, now, yawDeg, axis) {
117
+ const right = side === "Right", medial = apply(ry(yawDeg ?? 0), [right ? 1 : -1, 0, 0]), vy = Math.abs(d[1]);
118
+ const thumb = flat(add(scale(UP, 1 - vy), scale(medial, vy)), d), want = norm(len(thumb) > 1e-3 ? thumb : flat(medial, d));
119
+ // a thin body lies across the palm, the knuckle line along its long axis: of its two ends the thumb takes the
120
+ // one the forearm turns to least (the end-state-comfort thumb counts PREFER deg in its favour)
121
+ const across = axis ? flat(axis, d) : null;
122
+ if (!across || len(across) <= ALIGN) return cols(d, want, cross(d, want));
123
+ const turn = l => Math.acos(Math.max(-1, Math.min(1, dot(l, now)))) * DEG - (dot(l, want) > 0 ? PREFER : 0);
124
+ const a = norm(across), l = turn(a) <= turn(scale(a, -1)) ? a : scale(a, -1);
125
+ return cols(d, l, cross(d, l));
126
+ }
127
+
128
+ /** Frame F (columns d, l, n) turned about its own d so the forearm's whole twist (from `twist` deg now) stays within the comfortable range: the roll stops where the forearm is comfortable. */
129
+ function comfortable(F, now, twist) {
130
+ const d = [F[0][0], F[1][0], F[2][0]], l = [F[0][1], F[1][1], F[2][1]];
131
+ const want = Math.atan2(dot(cross(now, l), d), dot(now, l)) * DEG, [lo, hi] = WRIST_COMFORT.twist;
132
+ const got = Math.min(hi, Math.max(lo, twist + want)) - twist;
133
+ if (Math.abs(got - want) < 1e-6) return F;
134
+ const r = apply(axisRot(d, got), now);
135
+ return cols(d, r, cross(d, r));
136
+ }
137
+
138
+ /**
139
+ * gripHand(ctx, sk, s, side, k, c, f, diag): turn the hand of a fit onto the body under its anchor
140
+ * (see above); a no-op when nothing asks for it. diag.grip_<k> = { w, twist, flex, dev, body }.
141
+ */
142
+ export function gripHand(ctx, sk, s, side, k, c, f, diag) {
143
+ // a wrist the spec turns on this frame is the agent's (or a skill's) own; a neutral 0 asks for nothing
144
+ if (Math.abs(c[`wtwist_${k}`] ?? 0) > 1e-3 || Math.abs(c[`wflex_${k}`] ?? 0) > 1e-3) return;
145
+ const on = activeFit(ctx.spec, c, k);
146
+ if (!on || on.w < 1e-3) return;
147
+ // a hand that carries something on this frame holds that (its carry's grip), whatever its fit's anchor
148
+ if (Object.keys(ctx.spec.objects ?? {}).some(id => ctx.spec.objects[id]?.prop && ctx.objectHand?.(id, f) === k && ownGrip(ctx, id, k, f) === null)) return;
149
+ const now = { s, diag, c }, anchor = ctx.spec.anchors?.[on.fit.anchor];
150
+ // a point of a prop another hand holds is the second-hand grip's (second-hand.js)
151
+ if (anchor?.kind === "prop") return;
152
+ const A = add(resolveAnchor(ctx, on.fit.anchor, s, diag, c, k), on.fit.offset ?? [0, 0, 0]);
153
+ const box = bodyAt(ctx, f, k, A, anchor, now);
154
+ const eff = box && effectorAt(sk, ctx.spec, s, side, on.fit.effector ?? {});
155
+ if (!eff) return;
156
+ const measure = st => ({ pos: effectorAt(sk, ctx.spec, st, side, on.fit.effector ?? {}), R: (({ d, l, n }) => cols(d, l, n))(palmAxes(sk, st, side)) });
157
+ const I = sk.index, a = I[`${side}Arm`], fa = I[`${side}ForeArm`], h = I[`${side}Hand`];
158
+ const long = thinAxis(box.half), axis = long >= 0 ? apply(box.R, AXES.map(i => (i === long ? 1 : 0))) : null;
159
+ const ax = palmAxes(sk, s, side), roll = norm(flat(ax.l, ax.d));
160
+ const axT = norm(sub(s.P[h], s.P[fa])), neutral = mul(s.R[fa], mul(T(sk.restWorldRot[fa]), sk.restWorldRot[h]));
161
+ const twist = wristNeed(neutral, s.R[h], axT, apply(mul(neutral, T(s.R[h])), ax.l)).twist;
162
+ const palm = { pos: eff, R: comfortable(gripFrame(side, ax.d, roll, c.yaw, axis), roll, twist) };
163
+ const { goal, plane, wristAt } = aimArm(sk, s, side, palm, measure(s), on.w, "palm");
164
+ let need = { twist: 0, flex: 0, dev: 0 };
165
+ for (let i = 0; i < ITER; i++) {
166
+ // the elbow keeps the plane the arm had (bendPlane), the fingers' point goes back where the fit put it
167
+ twoBone(sk, s, a, fa, h, wristAt(i ? s.R[h] : goal), plane, ELBOW_REST, "elbow");
168
+ need = orient(sk, s, side, h, fa, goal);
169
+ }
170
+ diag[`grip_${k}`] = { w: on.w, ...need };
171
+ }
@@ -0,0 +1,48 @@
1
+ // While a walk runs the head keeps the gaze steadier than the trunk: the trunk's quick sway (the root's and the
2
+ // spine's yaw, pitch and roll less their moving average over about a stride, so a turn, a lean or a bow the keys
3
+ // hold is not swayed against) is taken back by the head, a part of it (STEADY) and no more than LIMIT degrees; little
4
+ // of the pitch: a person's head nods with the trunk's dip at each step.
5
+ // Only from a walk's start to its last step (walk.js tags them "<tag> start", "<tag> step <n>"; the settle after
6
+ // holds still, and a take cut in it ends the same), faded in and out over FADE frames, and never on a head axis the
7
+ // spec's own keys move during that walk: a keyed look wins. The average holds the take's end value past its end.
8
+ const STEADY = { yaw: 0.5, pitch: 0.2, roll: 0.7 }, LIMIT = { yaw: 4, pitch: 6, roll: 10 }, WINDOW = 33, FADE = 8;
9
+ const AXES = ["yaw", "pitch", "roll"];
10
+
11
+ /** The walk spans in the keys, start to last step: [[f0, f1], ...]. */
12
+ function walkSpans(keys) {
13
+ const spans = new Map();
14
+ for (const k of keys) {
15
+ const m = / (start|step \d+)$/.exec(k.src ?? ""), tag = m && k.src.slice(0, -m[0].length);
16
+ if (!m || !/\bwalk(To)?$/.test(tag)) continue;
17
+ const span = spans.get(tag) ?? [Infinity, -Infinity];
18
+ spans.set(tag, [Math.min(span[0], k.f), Math.max(span[1], k.f)]);
19
+ }
20
+ return [...spans.values()].filter(([a, b]) => b > a);
21
+ }
22
+
23
+ const movingAverage = (xs, w) => {
24
+ const half = Math.floor(w / 2), at = i => xs[Math.max(0, Math.min(xs.length - 1, i))];
25
+ return xs.map((_, f) => { let s = 0; for (let i = f - half; i <= f + half; i++) s += at(i); return s / (2 * half + 1); });
26
+ };
27
+
28
+ /** Adds the head's share of the trunk's sway to the baked head channels inside every walk (in place). */
29
+ export function steadyHead(baked, keys, frameCount) {
30
+ const spans = walkSpans(keys);
31
+ if (!spans.length) return;
32
+ const series = name => baked[name] ?? new Array(frameCount).fill(0);
33
+ for (const axis of AXES) {
34
+ const spine = series(`spine_${axis}`), trunk = series(axis).map((v, f) => v + spine[f]);
35
+ const mean = movingAverage(trunk, WINDOW);
36
+ let head = null;
37
+ for (const [f0, f1] of spans) {
38
+ const keyed = keys.some(k => k.f >= f0 && k.f <= f1 && k.set?.[`head_${axis}`] !== undefined && !/\bwalk(To)? (start|step \d+|end)$/.test(k.src ?? ""));
39
+ if (keyed) continue;
40
+ head ??= series(`head_${axis}`).slice();
41
+ for (let f = Math.max(0, f0); f <= Math.min(frameCount - 1, f1); f++) {
42
+ const fade = Math.min(1, (f - f0) / FADE, (f1 - f) / FADE), sway = Math.max(-LIMIT[axis], Math.min(LIMIT[axis], trunk[f] - mean[f]));
43
+ head[f] -= STEADY[axis] * sway * fade * fade * (3 - 2 * fade);
44
+ }
45
+ }
46
+ if (head) baked[`head_${axis}`] = head;
47
+ }
48
+ }
@@ -0,0 +1,134 @@
1
+ // The hand follows a held object (docs/motion-api-v3.1.md C14). On frames where
2
+ // spec.objects[id].keys lead (../object-keys.js), pose.js calls holdHand after the plain arm
3
+ // (IK or FK, wrist channels, fingers): the wrist target and the hand's orientation are solved so
4
+ // the grip (the prop's palm frame) lands on the pose the object keys ask for, hand = object o
5
+ // attach^-1. The whole arm turns the object (arm-orient.js, lane K8): the elbow swings about the
6
+ // shoulder-wrist axis to the pole that leaves the wrist least strained, the forearm takes its
7
+ // share of the turn about its own axis, and the wrist adds the rest within anatomical limits
8
+ // relative to the forearm: twist about the forearm, flex about the knuckle line, deviation toward
9
+ // the thumb (+, radial) or the little finger (-, ulnar). What the arm or the wrist cannot do is
10
+ // left as a residual: QA measures it on the object (qa/key-checks.js) and reads the reasons from
11
+ // diag.hold_<side>.
12
+ import { add, sub, scale, norm, dot, len, mul, T, apply, axisRot, angle, quatFromMat, matFromQuat } from "./math.js";
13
+ import { twoBone, palmAxes, carried, askedFold, reachAt, ELBOW_FOLD } from "./limbs.js";
14
+ import { rotateSubtree } from "./skeleton.js";
15
+ import { slerp } from "./quat.js";
16
+ import { bodyFor, segmentDepth } from "../body.js";
17
+ import { WRIST_LIMITS, FOREARM_SHARE, ELBOW_REST, wristNeed, swivel, landscape, shoulderPrice, comfortPrice } from "./arm-orient.js";
18
+
19
+ // STRAIGHT: a plain arm whose elbow sits less than this (m) off its shoulder-wrist midpoint is
20
+ // straight and has no bend plane of its own. A straight FK arm keeps 1 to 3 mm there from its bone
21
+ // lengths (Y Bot: the upper arm is 2 mm shorter than the forearm, so the elbow sits 1 mm up the
22
+ // arm), and that direction is no bend: as the swivel's reference it put the elbow "up", so the
23
+ // limit 100 deg from it ruled out an elbow pointing down (session d5c110a7's batting stance).
24
+ // ROLL_MARGIN: the share of each wrist half range by which holdCost tightens the limits, so the
25
+ // roll picked for a key leaves the frames between keys (they turn between the picks) some room.
26
+ // The fold price (holdCost): an arm folded near flat turns the hand any way at all, so without a price
27
+ // the roll of a stance key at the shoulder folded it (session d5c110a7: wrist 6-9 cm from the shoulder,
28
+ // 162 deg), and the frames between keys, where the wrist moves a few cm, broke away from it. twoBone
29
+ // now stops the fold (limbs.js ELBOW_FOLD, ELBOW_STOP); the price still steers the roll off it.
30
+ const ITERATIONS = 3, REACH_PRICE = 1, STRAIGHT = 0.005, ROLL_MARGIN = 0.15, BODY_PRICE = 4;
31
+ const clampTo = (v, [lo, hi]) => Math.min(hi, Math.max(lo, v));
32
+
33
+ /**
34
+ * Turn the hand (and its fingers) about the wrist so its world rotation is `goal`, as far as the
35
+ * limits let it: the forearm bone turns FOREARM_SHARE of the twist about its own axis, the hand
36
+ * the rest plus flex and deviation. Returns what it needed, degrees { twist, flex, dev }.
37
+ */
38
+ export function orient(sk, s, side, h, fa, goal) {
39
+ const axT = scale(sub(s.P[h], s.P[fa]), 1 / len(sub(s.P[h], s.P[fa]))), neutral = mul(s.R[fa], mul(T(sk.restWorldRot[fa]), sk.restWorldRot[h]));
40
+ const { axF, axD, ...need } = wristNeed(neutral, goal, axT, apply(mul(neutral, T(s.R[h])), palmAxes(sk, s, side).l));
41
+ const got = Object.fromEntries(Object.entries(need).map(([k, v]) => [k, clampTo(v, WRIST_LIMITS[k])]));
42
+ const want = mul(mul(axisRot(add(scale(axF, got.flex), scale(axD, got.dev)), Math.hypot(got.flex, got.dev)), axisRot(axT, got.twist)), neutral);
43
+ rotateSubtree(sk, s, fa, s.P[fa].slice(), axisRot(axT, FOREARM_SHARE * got.twist));
44
+ rotateSubtree(sk, s, h, s.P[h].slice(), mul(want, T(s.R[h])));
45
+ return need;
46
+ }
47
+
48
+ /**
49
+ * The bend direction the channels gave the arm of `side` on pose s, what its swivel turns from: the
50
+ * elbow off the shoulder-wrist midpoint, or for a straight arm (STRAIGHT) where its elbow would
51
+ * point if it bent, the upper arm's rest bend carried by the bone, so the swivel's limits sit on
52
+ * the upper arm's own roll.
53
+ */
54
+ export function bendPlane(sk, s, side) {
55
+ const I = sk.index, a = I[`${side}Arm`], bend = sub(s.P[I[`${side}ForeArm`]], scale(add(s.P[a], s.P[I[`${side}Hand`]]), 0.5));
56
+ return len(bend) > STRAIGHT ? bend : carried(sk, s, a, ELBOW_REST);
57
+ }
58
+
59
+ /**
60
+ * What holding `palm` with weight w needs on pose s: the hand's goal rotation, the wrist target for
61
+ * a hand rotation (pin "wrist": where it is; "palm": where the palm stays put), the bend direction
62
+ * (bendPlane).
63
+ */
64
+ export function aimArm(sk, s, side, palm, now, w, pin) {
65
+ const I = sk.index, h = I[`${side}Hand`];
66
+ // the palm frame in the hand bone's frame: fixed for this frame's finger curl
67
+ const localR = mul(T(s.R[h]), now.R), localP = apply(T(s.R[h]), sub(now.pos, s.P[h]));
68
+ const goal = matFromQuat(slerp(quatFromMat(s.R[h]), quatFromMat(mul(palm.R, T(localR))), w));
69
+ const from = s.P[h].slice();
70
+ // the wrist goes where the grip lands with the orientation the wrist reached (the goal at first)
71
+ const grip = R => sub(palm.pos, apply(R, localP));
72
+ return { goal, plane: bendPlane(sk, s, side), wristAt: pin === "wrist" ? () => from : pin === "palm" ? grip : R => add(from, scale(sub(grip(R), from), w)) };
73
+ }
74
+
75
+ /**
76
+ * held = { w, palm: { pos, R }, measure(s) -> { pos, R }, pole?, second?, pin? }: blend weight
77
+ * (1 inside the keyed span), the palm frame the keys ask for, the function that measures it on a
78
+ * pose, the planned elbow pole (../hold-plan.js; searched here when absent) and, for a second hand
79
+ * (../second-hand.js), the id of the object it grips and what stays put (pin "wrist" or "palm").
80
+ */
81
+ export function holdHand(sk, s, side, held, restBend, diag, k) {
82
+ const I = sk.index, a = I[`${side}Arm`], fa = I[`${side}ForeArm`], h = I[`${side}Hand`], { w } = held;
83
+ const { goal, plane, wristAt } = aimArm(sk, s, side, held.palm, held.measure(s), w, held.pin);
84
+ const arm = swivel(sk, s, side, goal, wristAt(goal), plane, restBend, { toward: held.pole, w, comfort: held.comfort });
85
+ let miss = 0, fold = 0, need = { twist: 0, flex: 0, dev: 0 };
86
+ for (let i = 0; i < ITERATIONS; i++) {
87
+ const W = wristAt(i ? s.R[h] : goal);
88
+ fold = askedFold(sk, s, side, W);
89
+ miss = twoBone(sk, s, a, fa, h, W, arm.pole, restBend, "elbow");
90
+ need = orient(sk, s, side, h, fa, goal);
91
+ }
92
+ // fold: the flexion (deg) the wrist asked when past ELBOW_FOLD; miss is then how far inside the fold it is
93
+ diag[`hold_${k}`] = { w, miss: Math.max(0, miss), ...need, swivel: swungFrom(s, a, fa, h, plane), ...(fold > ELBOW_FOLD ? { fold } : {}), ...(held.second ? { second: held.second } : {}) };
94
+ diag[`arm_miss_${k}`] *= 1 - w;
95
+ }
96
+
97
+ /**
98
+ * The swivel landscape holdHand searches for `held` on pose s (the plain arm of `side`): what
99
+ * ../hold-plan.js plans the elbow's path on.
100
+ */
101
+ export function holdLandscape(sk, s, side, held) {
102
+ const { goal, plane, wristAt } = aimArm(sk, s, side, held.palm, held.measure(s), held.w);
103
+ return landscape(sk, s, side, goal, wristAt(goal), plane, ELBOW_REST, 0, held.comfort);
104
+ }
105
+
106
+ /**
107
+ * The arm's cost of holding the palm frame `palm` on pose s (the plain arm of `side`, `now` its
108
+ * palm frame there): the wrist strain at the pole holdHand would take, its limits ROLL_MARGIN
109
+ * tighter, plus REACH_PRICE per squared centimetre the arm is short or the wrist nearer the shoulder
110
+ * than the elbow folds (ELBOW_FOLD), plus the shoulder's price with opts.shoulder. An aim key without roll takes the cheapest roll (key-aim.js).
111
+ */
112
+ export function holdCost(sk, s, side, palm, now, { shoulder = false } = {}) {
113
+ const { goal, plane, wristAt } = aimArm(sk, s, side, palm, now, 1), W = wristAt(goal);
114
+ const arm = swivel(sk, s, side, goal, W, plane, ELBOW_REST, { margin: ROLL_MARGIN });
115
+ // the wrist no nearer the shoulder than the elbow folds (ELBOW_FOLD): an arm folded flat holds anything, and nothing between keys
116
+ const I = sk.index, S = s.P[I[`${side}Arm`]], A = len(sub(s.P[I[`${side}ForeArm`]], S)), B = len(sub(s.P[I[`${side}Hand`]], s.P[I[`${side}ForeArm`]]));
117
+ const fold = Math.max(0, reachAt(A, B, ELBOW_FOLD) - len(sub(W, S)));
118
+ // opts.shoulder adds the shoulder's price at that elbow (arm-orient.js shoulderPrice) and the arm's depth into the body
119
+ // (body.js, priced as grip-choice.js prices it): a leaning aim key's pick (key-aim.js) keeps the elbow down, not in the chest
120
+ const arm2 = shoulder ? swivel(sk, s, side, goal, W, plane, ELBOW_REST, { comfort: true }) : arm;
121
+ return (shoulder ? comfortPrice(arm2.need) + shoulderPrice(S, arm2.elbow) + intoBody(sk, s, side, S, arm2.elbow, W) : arm.cost) + REACH_PRICE * ((arm.miss * 100) ** 2 + (fold * 100) ** 2);
122
+ }
123
+
124
+ /** BODY_PRICE per squared centimetre the upper arm or the forearm at elbow E and wrist W goes into the body (body.js). */
125
+ function intoBody(sk, s, side, S, E, W) {
126
+ const body = bodyFor(sk), I = sk.index;
127
+ return body ? BODY_PRICE * (Math.max(segmentDepth(body, sk, s, I[`${side}Arm`], S, E), segmentDepth(body, sk, s, I[`${side}ForeArm`], E, W)) * 100) ** 2 : 0;
128
+ }
129
+
130
+ /** Degrees the elbow swung about the shoulder-wrist axis from the bend direction `plane` the channels gave. */
131
+ function swungFrom(s, a, fa, h, plane) {
132
+ const u = norm(sub(s.P[h], s.P[a])), off = v => sub(v, scale(u, dot(v, u)));
133
+ return angle(off(sub(s.P[fa], s.P[a])), off(plane));
134
+ }
@@ -0,0 +1,113 @@
1
+ // The idle layer: a body that holds its place is never a statue. Wherever the take holds a pose (no
2
+ // keyed channel changes: not a hand, a door or a finger) with its hands free (no arm on IK, no hand
3
+ // target or fit weight, nothing carried), it breathes (the chest lifts and settles:
4
+ // spine_pitch), lets its head drift a little (head_yaw, head_pitch) and, standing, shifts its weight
5
+ // from foot to foot (the hips sway over the planted feet, the pelvis lists toward the unloaded side and
6
+ // the spine rights it: root, roll, spine_roll). The layer only adds to channels that carry no contact:
7
+ // the feet, hand targets, anchors and objects are left as keyed, so a planted foot stays put and a hand
8
+ // on a target stays on it. It fades in RAMP s after the take starts or the body stops, out before it
9
+ // moves again, and stays off across the span of a `still` goal. Deterministic: its phases come from where the character
10
+ // first stands, so a recompile bakes the same take. spec.idle: false turns it off. The offsets ride on
11
+ // the solved pose only (compile.js solveFrame): out.baked keeps the keyed values, so QA's causes and
12
+ // patches still name the keys.
13
+ const BREATH = { period: 3.8, deg: 0.8 }, HEAD = { yaw: 0.9, pitch: 0.6 }, SWAY = { period: 5.6, m: 0.012, list: 1.4 };
14
+ const RAMP = 0.5, MOVE = 1e-4, TAU = 2 * Math.PI;
15
+ const smoothstep = x => x * x * (3 - 2 * x);
16
+ const differs = (a, b) => (Array.isArray(a) ? a.some((v, i) => Math.abs(v - b[i]) > MOVE)
17
+ : a && typeof a === "object" ? Object.keys({ ...a, ...b }).some(k => Math.abs((a[k] ?? 0) - (b?.[k] ?? 0)) > 1e-3) : Math.abs((a ?? 0) - (b ?? 0)) > 0.01);
18
+
19
+ /** Phases in [0, TAU) from a text (FNV-1a hash, then mulberry32). */
20
+ function phases(text, n) {
21
+ let h = 2166136261;
22
+ for (let i = 0; i < text.length; i++) h = Math.imul(h ^ text.charCodeAt(i), 16777619);
23
+ let a = h >>> 0;
24
+ return Array.from({ length: n }, () => {
25
+ a = (a + 0x6d2b79f5) >>> 0;
26
+ let t = Math.imul(a ^ (a >>> 15), a | 1);
27
+ t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
28
+ return (((t ^ (t >>> 14)) >>> 0) / 4294967296) * TAU;
29
+ });
30
+ }
31
+
32
+ /** The weight of the layer per frame: 0 while anything keyed moves, a hand works or a still goal holds the body, easing to 1 RAMP s into a hold. */
33
+ function holdWeights(baked, spec, frameCount, fps) {
34
+ // the take's first frame is its keyed pose: the layer fades in from there
35
+ const moving = new Array(frameCount).fill(false), watch = Object.keys(baked);
36
+ moving[0] = true;
37
+ for (let f = 1; f < frameCount; f++) if (watch.some(ch => differs(baked[ch][f], baked[ch][f - 1]))) moving[f] = moving[f - 1] = true;
38
+ // a working hand: an arm on IK, a hand target or fit weight, or an object carried from its grab on
39
+ const hands = watch.filter(ch => /^(armw_[lr]|w_.+_[lr])$/.test(ch));
40
+ for (let f = 0; f < frameCount; f++) if (hands.some(ch => baked[ch][f] > 1e-3)) moving[f] = true;
41
+ for (const o of Object.values(spec.objects ?? {})) {
42
+ if (!Number.isFinite(o?.grab)) continue;
43
+ for (let f = Math.max(0, o.grab); f < Math.min(frameCount, Number.isFinite(o.release) ? o.release : frameCount); f++) moving[f] = true;
44
+ }
45
+ for (const g of spec.goals ?? []) {
46
+ const at = Array.isArray(g?.at) ? g.at : null;
47
+ if (g?.still === undefined || !at?.length) continue;
48
+ for (let f = Math.max(0, Math.round(at[0])); f <= Math.min(frameCount - 1, Math.round(at[at.length - 1])); f++) moving[f] = true;
49
+ }
50
+ const ramp = Math.max(1, Math.round(RAMP * fps)), d = new Array(frameCount).fill(Infinity);
51
+ for (let f = 0, last = -Infinity; f < frameCount; f++) { if (moving[f]) last = f; d[f] = f - last; }
52
+ for (let f = frameCount - 1, next = Infinity; f >= 0; f--) { if (moving[f]) next = f; d[f] = Math.min(d[f], next - f); }
53
+ return d.map(x => smoothstep(Math.min(1, x / ramp)));
54
+ }
55
+
56
+ /**
57
+ * Per hand, how much it works at each frame: 1 while its arm is on IK, a hand target or fit weight of that hand is
58
+ * on, or it carries an object from its grab on; easing to 0 over RAMP s away from that. pose.js keeps a working
59
+ * hand's forearm on its slant (arm_hang), eased so the arm does not jump as it goes onto IK. null for an idle hand.
60
+ */
61
+ export function busyHands(baked, spec, frameCount, fps) {
62
+ const out = {}, ramp = Math.max(1, Math.round(RAMP * fps));
63
+ for (const k of ["l", "r"]) {
64
+ const own = Object.keys(baked).filter(ch => ch === `armw_${k}` || new RegExp(`^w_.+_${k}$`).test(ch));
65
+ const busy = Array.from({ length: frameCount }, (_, f) => own.some(ch => baked[ch][f] > 1e-3));
66
+ for (const o of Object.values(spec.objects ?? {})) {
67
+ if (!Number.isFinite(o?.grab) || spec.props?.[o.prop]?.hand !== k) continue;
68
+ for (let f = Math.max(0, o.grab); f < Math.min(frameCount, Number.isFinite(o.release) ? o.release : frameCount); f++) busy[f] = true;
69
+ }
70
+ if (!busy.some(Boolean)) { out[k] = null; continue; }
71
+ const d = new Array(frameCount).fill(Infinity);
72
+ for (let f = 0, last = -Infinity; f < frameCount; f++) { if (busy[f]) last = f; d[f] = f - last; }
73
+ for (let f = frameCount - 1, next = Infinity; f >= 0; f--) { if (busy[f]) next = f; d[f] = Math.min(d[f], next - f); }
74
+ out[k] = d.map(x => 1 - smoothstep(Math.min(1, x / ramp)));
75
+ }
76
+ return out;
77
+ }
78
+
79
+ /** The idle offsets per frame ({ channel: delta }, root a world [dx, 0, dz]) for the baked channels, or null (compile.js rebake). */
80
+ export function idleLayer(baked, { spec, sk, frameCount }) {
81
+ if (spec.idle === false || !baked.root || !baked.foot_l || !baked.foot_r) return null;
82
+ const fps = spec.fps ?? 30, w = holdWeights(baked, spec, frameCount, fps);
83
+ if (!w.some(x => x > 0)) return null;
84
+ const P = sk.restWorldPos, standY = P[sk.index.Hips][1], ankY = P[sk.index.LeftFoot][1];
85
+ const [pb, py, pp, ps, py2] = phases(JSON.stringify([baked.root[0].map(v => v.toFixed(3)), (baked.yaw?.[0] ?? 0).toFixed(1)]), 5);
86
+ const out = new Array(frameCount).fill(null);
87
+ for (let f = 0; f < frameCount; f++) {
88
+ if (!(w[f] > 0)) continue;
89
+ const t = f / fps, k = w[f];
90
+ // breathing in lifts the chest (spine_pitch + bends forward)
91
+ const d = out[f] = { spine_pitch: -k * BREATH.deg * Math.sin((TAU * t) / BREATH.period + pb),
92
+ head_yaw: k * HEAD.yaw * (0.6 * Math.sin((TAU * t) / 7.3 + py) + 0.4 * Math.sin((TAU * t) / 11.9 + py2)),
93
+ head_pitch: k * HEAD.pitch * Math.sin((TAU * t) / 9.1 + pp) };
94
+ // the weight shift: standing only (both feet flat on the floor, the hips at standing height)
95
+ const feet = ["l", "r"].every(s => baked[`foot_${s}`][f][1] < ankY + 0.03 && Math.abs(baked[`fpitch_${s}`]?.[f] ?? 0) < 2);
96
+ if (!feet || baked.root[f][1] < standY - 0.15) continue;
97
+ const s = k * Math.sin((TAU * t) / SWAY.period + ps), yaw = ((baked.yaw?.[f] ?? 0) * Math.PI) / 180;
98
+ d.root = [Math.cos(yaw) * SWAY.m * s, 0, -Math.sin(yaw) * SWAY.m * s];
99
+ // the hips over the left foot drop the right side (roll +), the spine stays upright
100
+ d.roll = SWAY.list * s;
101
+ d.spine_roll = -SWAY.list * s;
102
+ }
103
+ return out;
104
+ }
105
+
106
+ /** Frame f's channels with the idle offsets added (a new object; c itself is left alone). */
107
+ export function withIdle(c, layer, f) {
108
+ const d = layer?.[f];
109
+ if (!d) return c;
110
+ const out = { ...c };
111
+ for (const [ch, v] of Object.entries(d)) out[ch] = Array.isArray(v) ? (Array.isArray(c[ch]) ? c[ch].map((x, i) => x + v[i]) : c[ch]) : (Number(c[ch]) || 0) + v;
112
+ return out;
113
+ }