cozyclay 2.1.1 → 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 (577) hide show
  1. package/CHANGELOG.md +60 -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 +3 -3
  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-msLI71g1.js → first-shot-handoff-CC4xe4HM.js} +1 -1
  33. package/dist/assets/{landing-ChUvB_zL.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/sw.js +3 -6
  40. package/dist/workflow/index.html +5 -4
  41. package/mcp/LIVE-PROTOCOL.md +7 -47
  42. package/mcp/README.md +16 -26
  43. package/mcp/live-hub.mjs +6 -10
  44. package/mcp/package.json +1 -3
  45. package/mcp/tool-handlers.mjs +4 -168
  46. package/package.json +3 -22
  47. package/src/App.jsx +150 -2624
  48. package/src/agent-progress.js +79 -0
  49. package/src/agent-turn-mark.js +30 -0
  50. package/src/analytics.js +1 -19
  51. package/src/app-context.js +5 -0
  52. package/src/app-stage.jsx +2 -34
  53. package/src/asset-pane.jsx +6 -5
  54. package/src/camera-block.js +21 -0
  55. package/src/camera-direction.js +120 -0
  56. package/src/character-kind.js +3 -9
  57. package/src/command-bus.js +8 -6
  58. package/src/commands/cast.js +5 -34
  59. package/src/commands/elements/character.js +1 -1
  60. package/src/commands/elements/motion.js +1 -6
  61. package/src/commands/elements/object.js +6 -2
  62. package/src/commands/elements/shot.js +4 -1
  63. package/src/commands/elements.js +38 -14
  64. package/src/commands/index.js +3 -1
  65. package/src/commands/kits.js +37 -0
  66. package/src/commands/motion-timing.js +57 -0
  67. package/src/commands/motion.js +9 -152
  68. package/src/commands/motion3-compile.js +65 -0
  69. package/src/commands/motion3.js +207 -0
  70. package/src/commands/objects.js +6 -4
  71. package/src/commands/shot.js +49 -11
  72. package/src/commands/stage.js +14 -1
  73. package/src/commands/view.js +4 -1
  74. package/src/document-mirrors.js +42 -0
  75. package/src/document-store.js +4 -0
  76. package/src/domains/cast.js +19 -194
  77. package/src/domains/motion/motion-range-pin-rules.js +1 -1
  78. package/src/domains/motion/take-source.js +51 -0
  79. package/src/domains/motion.js +60 -1838
  80. package/src/domains/scenes.js +12 -55
  81. package/src/domains/shots.js +2 -2
  82. package/src/domains/stage.js +4 -2
  83. package/src/draft-ghost.js +46 -0
  84. package/src/draft-ghost.jsx +129 -0
  85. package/src/dualview.jsx +4 -3
  86. package/src/effects/EffectsLayer.jsx +112 -0
  87. package/src/effects/effect-meshes.js +232 -0
  88. package/src/effects/effect-record.js +103 -0
  89. package/src/effects/emitter.js +59 -0
  90. package/src/effects/simulate.js +261 -0
  91. package/src/effects/state.js +59 -0
  92. package/src/execution-telemetry.js +2 -3
  93. package/src/first-success-guide.jsx +5 -5
  94. package/src/ghost-set.js +257 -0
  95. package/src/held-object-edit.jsx +113 -0
  96. package/src/held-object-pose.js +175 -0
  97. package/src/hierarchy-model.js +1 -1
  98. package/src/hierarchy-panel.jsx +1 -1
  99. package/src/imported-mesh.jsx +103 -0
  100. package/src/kits/held.js +84 -0
  101. package/src/kits/index.js +70 -0
  102. package/src/kits/smoking.js +57 -0
  103. package/src/light-blockers.js +93 -0
  104. package/src/motion/beat-board-model.js +84 -0
  105. package/src/motion/beat-board.css +124 -0
  106. package/src/motion/beat-board.jsx +59 -0
  107. package/src/motion/center-of-mass.js +66 -0
  108. package/src/motion/fix-collisions.js +850 -0
  109. package/src/motion/physics-review.js +118 -0
  110. package/src/{ardy → motion}/physics-support.js +1 -76
  111. package/src/motion/rig-snapshot.js +22 -0
  112. package/src/motion/take-segments.jsx +140 -0
  113. package/src/{ardy → motion}/timeline-coordinates.js +0 -21
  114. package/src/{ardy → motion}/timeline.css +1 -2
  115. package/src/{ardy → motion}/timeline.jsx +42 -413
  116. package/src/motion/waypoints.js +76 -0
  117. package/src/motion3/beats.js +83 -0
  118. package/src/motion3/body.js +158 -0
  119. package/src/motion3/bones.js +47 -0
  120. package/src/motion3/carried.js +47 -0
  121. package/src/motion3/compile-job.js +75 -0
  122. package/src/motion3/compile-worker.js +10 -0
  123. package/src/motion3/compiler/anchors.js +105 -0
  124. package/src/motion3/compiler/arm-orient.js +128 -0
  125. package/src/motion3/compiler/channels.js +125 -0
  126. package/src/motion3/compiler/compile.js +205 -0
  127. package/src/motion3/compiler/gait-rhythm.js +97 -0
  128. package/src/motion3/compiler/grip.js +171 -0
  129. package/src/motion3/compiler/head-steady.js +48 -0
  130. package/src/motion3/compiler/hold.js +134 -0
  131. package/src/motion3/compiler/idle.js +113 -0
  132. package/src/motion3/compiler/limbs.js +170 -0
  133. package/src/motion3/compiler/math.js +72 -0
  134. package/src/motion3/compiler/pose.js +162 -0
  135. package/src/motion3/compiler/quat.js +49 -0
  136. package/src/motion3/compiler/skeleton.js +74 -0
  137. package/src/motion3/compiler/validate-common.js +32 -0
  138. package/src/motion3/compiler/validate-goals.js +118 -0
  139. package/src/motion3/compiler/validate-objects.js +143 -0
  140. package/src/motion3/compiler/validate-parts.js +116 -0
  141. package/src/motion3/compiler/validate.js +122 -0
  142. package/src/motion3/compiler/vocab.js +73 -0
  143. package/src/motion3/compiler/walk-pelvis.js +275 -0
  144. package/src/motion3/compiler/walk.js +207 -0
  145. package/src/motion3/compiler/wrist-clamp.js +32 -0
  146. package/src/motion3/delta.js +47 -0
  147. package/src/motion3/describe.js +174 -0
  148. package/src/motion3/docs.js +27 -0
  149. package/src/motion3/edit-carry.js +21 -0
  150. package/src/motion3/fix/ease.js +16 -0
  151. package/src/motion3/fix/ground.js +37 -0
  152. package/src/motion3/fix/index.js +52 -0
  153. package/src/motion3/fix/push.js +71 -0
  154. package/src/motion3/fix/reach.js +46 -0
  155. package/src/motion3/fix/retime.js +79 -0
  156. package/src/motion3/fix/self.js +211 -0
  157. package/src/motion3/fix/slide.js +70 -0
  158. package/src/motion3/fix/turn.js +51 -0
  159. package/src/motion3/follow.js +39 -0
  160. package/src/motion3/free-motion.js +173 -0
  161. package/src/motion3/grip-choice.js +130 -0
  162. package/src/motion3/hold-plan.js +157 -0
  163. package/src/motion3/json-patch.js +77 -0
  164. package/src/motion3/key-aim.js +105 -0
  165. package/src/motion3/key-edit.js +82 -0
  166. package/src/motion3/lenient.js +127 -0
  167. package/src/motion3/object-keys.js +236 -0
  168. package/src/motion3/object-pose.js +182 -0
  169. package/src/motion3/object-tracks.js +12 -0
  170. package/src/motion3/pipeline.js +102 -0
  171. package/src/motion3/playback.js +51 -0
  172. package/src/motion3/probes.js +91 -0
  173. package/src/motion3/qa/body-checks.js +34 -0
  174. package/src/motion3/qa/check-names.js +14 -0
  175. package/src/motion3/qa/checks.js +121 -0
  176. package/src/motion3/qa/contact-checks.js +51 -0
  177. package/src/motion3/qa/explain.js +80 -0
  178. package/src/motion3/qa/fmt.js +5 -0
  179. package/src/motion3/qa/foot-checks.js +72 -0
  180. package/src/motion3/qa/geometry.js +77 -0
  181. package/src/motion3/qa/goals.js +191 -0
  182. package/src/motion3/qa/ground.js +60 -0
  183. package/src/motion3/qa/hints-contact.js +179 -0
  184. package/src/motion3/qa/hints-goal.js +160 -0
  185. package/src/motion3/qa/hints-knee.js +24 -0
  186. package/src/motion3/qa/hints-motion.js +209 -0
  187. package/src/motion3/qa/hints-object.js +91 -0
  188. package/src/motion3/qa/index.js +42 -0
  189. package/src/motion3/qa/key-checks.js +82 -0
  190. package/src/motion3/qa/near.js +96 -0
  191. package/src/motion3/qa/object-checks.js +135 -0
  192. package/src/motion3/qa/patch.js +161 -0
  193. package/src/motion3/qa/severity.js +38 -0
  194. package/src/motion3/qa/spec-checks.js +65 -0
  195. package/src/motion3/qa/style.js +218 -0
  196. package/src/motion3/qa/wrist-checks.js +69 -0
  197. package/src/motion3/qa/wrist-obs.js +60 -0
  198. package/src/motion3/refs.js +120 -0
  199. package/src/motion3/repair-tune.js +121 -0
  200. package/src/motion3/repair.js +83 -0
  201. package/src/motion3/report.js +67 -0
  202. package/src/motion3/rigs/index.js +14 -0
  203. package/src/motion3/rigs/x-bot-tpose-body.json +1 -0
  204. package/src/motion3/rigs/x-bot-tpose.json +1 -0
  205. package/src/motion3/rigs/y-bot-tpose-body.json +1 -0
  206. package/src/motion3/rigs/y-bot-tpose.json +1 -0
  207. package/src/motion3/scene.js +60 -0
  208. package/src/motion3/schema/brief.js +89 -0
  209. package/src/motion3/schema/fields.js +44 -0
  210. package/src/motion3/schema/spec-examples.js +46 -0
  211. package/src/motion3/schema/topics.js +111 -0
  212. package/src/motion3/schema/types.js +147 -0
  213. package/src/motion3/search.js +117 -0
  214. package/src/motion3/second-hand.js +112 -0
  215. package/src/motion3/skills/basic.js +82 -0
  216. package/src/motion3/skills/book.js +76 -0
  217. package/src/motion3/skills/car.js +145 -0
  218. package/src/motion3/skills/frames-auto.js +29 -0
  219. package/src/motion3/skills/index.js +83 -0
  220. package/src/motion3/skills/info.js +65 -0
  221. package/src/motion3/skills/smoke-kit.js +109 -0
  222. package/src/motion3/skills/smoke.js +160 -0
  223. package/src/motion3/spec-store.js +59 -0
  224. package/src/motion3/take-edit.js +155 -0
  225. package/src/motion3/take.js +63 -0
  226. package/src/motion3/timeline.js +127 -0
  227. package/src/motion3/views.js +121 -0
  228. package/src/motion3/walk-plan.js +119 -0
  229. package/src/object-catalog.jsx +7 -1
  230. package/src/object-colors.js +98 -0
  231. package/src/object-gizmo-parts.js +115 -0
  232. package/src/object-gizmo.jsx +37 -122
  233. package/src/object-glow.js +139 -0
  234. package/src/object-hidden.js +33 -0
  235. package/src/object-look.js +196 -0
  236. package/src/object-size.js +10 -0
  237. package/src/object-travel.js +71 -6
  238. package/src/panels/CameraPanel.jsx +7 -7
  239. package/src/panels/HdrEnvironmentControls.jsx +59 -0
  240. package/src/panels/LightPanel.jsx +25 -4
  241. package/src/panels/ObjectLookPanel.jsx +88 -0
  242. package/src/panels/PosePanel.jsx +2 -17
  243. package/src/panels/RigControlPanel.jsx +5 -53
  244. package/src/panels/SubjectBox.jsx +4 -1
  245. package/src/panels/details.css +14 -0
  246. package/src/posestudio.jsx +4 -27
  247. package/src/project-browser.jsx +3 -4
  248. package/src/project-resources.js +18 -31
  249. package/src/project.js +13 -104
  250. package/src/props.jsx +25 -280
  251. package/src/rail-dot-drag.js +23 -0
  252. package/src/render-passes.js +10 -0
  253. package/src/resource-status.jsx +1 -0
  254. package/src/result-modal.jsx +4 -19
  255. package/src/room.jsx +20 -12
  256. package/src/scene-assets.js +8 -6
  257. package/src/scene-light-place.js +72 -0
  258. package/src/scene-lights.jsx +148 -0
  259. package/src/scene-objects.js +31 -139
  260. package/src/scenes.js +15 -105
  261. package/src/semantic-edit.js +2 -5
  262. package/src/set-pieces.jsx +183 -0
  263. package/src/settings-menu.jsx +3 -11
  264. package/src/shell/BottomDock.jsx +9 -51
  265. package/src/shell/DetailsSlot.jsx +16 -200
  266. package/src/shell/GhostsMenuGroup.jsx +74 -0
  267. package/src/shell/LibrarySlot.jsx +1 -3
  268. package/src/shell/MenuBar.jsx +3 -3
  269. package/src/shell/PreferencesDialog.jsx +15 -264
  270. package/src/shell/PreferencesSlot.jsx +2 -8
  271. package/src/shell/TopBar.jsx +5 -84
  272. package/src/shell/ViewportToolbar.jsx +10 -70
  273. package/src/shell/agent-glass.css +19 -6
  274. package/src/shell/details-selection.js +42 -0
  275. package/src/shell/ghosts-menu.css +22 -0
  276. package/src/shell/glass-regions.css +29 -3
  277. package/src/shell/preferences.css +6 -1
  278. package/src/shot-authoring.js +4 -7
  279. package/src/soft-shadows.js +37 -0
  280. package/src/stage-attach-frames.js +12 -12
  281. package/src/stage-canvas-rigs.jsx +3 -3
  282. package/src/stage-capture.jsx +42 -8
  283. package/src/stage-characters.jsx +22 -216
  284. package/src/stage-editor-gizmos.jsx +6 -6
  285. package/src/stage-environment.js +93 -0
  286. package/src/stage-hdr-builtins.js +107 -0
  287. package/src/stage-hdr-image.js +116 -0
  288. package/src/stage-hdr-library.js +70 -0
  289. package/src/stage-hdr-map.js +94 -0
  290. package/src/stage-hdr.js +130 -0
  291. package/src/stage-lighting.js +149 -0
  292. package/src/stage-occlusion.js +210 -0
  293. package/src/stage-sky.jsx +159 -0
  294. package/src/stage-startup-defaults.js +5 -9
  295. package/src/studio-actions.js +12 -14
  296. package/src/studio-agent-commands.js +19 -16
  297. package/src/studio-agent-motion.js +9 -9
  298. package/src/studio-agent-protocol.js +41 -71
  299. package/src/studio-app-binding.js +25 -45
  300. package/src/studio-asset-list.js +17 -0
  301. package/src/studio-contact-sheet.js +37 -5
  302. package/src/studio-elements.js +27 -7
  303. package/src/studio-entity-bounds.js +18 -0
  304. package/src/studio-error-codes.js +8 -0
  305. package/src/studio-export-framing.js +152 -0
  306. package/src/studio-lighting-check.js +103 -0
  307. package/src/studio-motion-visual.js +150 -0
  308. package/src/studio-schema-errors.js +80 -0
  309. package/src/studio-support-gap.js +74 -0
  310. package/src/styles.css +1 -19
  311. package/src/timeline-defaults.js +5 -0
  312. package/src/timeline-extent.js +8 -20
  313. package/src/tutorial-walk.js +17 -0
  314. package/src/ui-scrub.js +1 -1
  315. package/src/use-shot-camera-tracks.js +12 -0
  316. package/src/view-ghosts.js +97 -0
  317. package/src/view-ghosts.jsx +49 -0
  318. package/src/workflow/AgentPanel.jsx +47 -166
  319. package/src/workflow/AgentProgressCard.jsx +107 -0
  320. package/src/workflow/AgentReplyText.jsx +37 -0
  321. package/src/workflow/AgentTurnSteps.jsx +84 -0
  322. package/src/workflow/LoginPanel.jsx +140 -0
  323. package/src/workflow/WorkflowBuilder.jsx +7 -23
  324. package/src/workflow/agent-client.js +54 -51
  325. package/src/workflow/agent-panel.css +71 -1
  326. package/src/workflow/agent-progress-card.css +167 -0
  327. package/src/workflow/agent-progress-view.js +47 -0
  328. package/src/workflow/agent-turn-steps.css +113 -0
  329. package/src/workflow/agent-turn.js +191 -0
  330. package/src/workflow/login-client.js +34 -0
  331. package/src/workflow/motion-input.js +18 -31
  332. package/src/workflow/node-schema.js +1 -1
  333. package/src/workflow/reply-markdown.js +69 -0
  334. package/src/workflow/scene-asset-sync.js +8 -8
  335. package/tools/agent-session-stats.mjs +61 -0
  336. package/tools/dev-full.mjs +5 -56
  337. package/tools/eslint-baseline.json +12 -13
  338. package/tools/knip-baseline.json +0 -179
  339. package/tools/loc-baseline.json +31 -81
  340. package/tools/motion3/extract-body.mjs +103 -0
  341. package/tools/motion3/extract-rig.mjs +85 -0
  342. package/tools/process-supervisor.mjs +0 -88
  343. package/tools/run-tests.mjs +8 -124
  344. package/bin/agent/motion-runtime.mjs +0 -76
  345. package/dist/ardy/cskel27-rest.json +0 -557
  346. package/dist/assets/analytics-C9pMpW7s.js +0 -1
  347. package/dist/assets/app-B_2XB9rw.css +0 -1
  348. package/dist/assets/app-DAuvKPP8.js +0 -4920
  349. package/dist/assets/config-Bx7pYsD9.js +0 -1
  350. package/dist/assets/config-DeoFTYzb.css +0 -1
  351. package/dist/assets/demo-CvKuBgy5.js +0 -1
  352. package/dist/assets/scene-asset-sync-DKkpjKBV.js +0 -19
  353. package/dist/assets/scene-asset-sync-DjJ_cJ2R.css +0 -1
  354. package/dist/assets/ticket-BPk2Ni95.js +0 -1
  355. package/dist/assets/workflow-jJRBR94Z.js +0 -179
  356. package/dist/demo/walk-then-stop.npz +0 -0
  357. package/mcp/ardy-prompts.mjs +0 -218
  358. package/src/ardy/auto-fix-panel.css +0 -108
  359. package/src/ardy/auto-physics.js +0 -1194
  360. package/src/ardy/client.js +0 -153
  361. package/src/ardy/convert.js +0 -313
  362. package/src/ardy/cskel27-neutral.js +0 -39
  363. package/src/ardy/cskel27.js +0 -77
  364. package/src/ardy/export.js +0 -165
  365. package/src/ardy/fix-collisions.js +0 -2257
  366. package/src/ardy/foot-lock.js +0 -230
  367. package/src/ardy/motion-calibration.js +0 -118
  368. package/src/ardy/motion-edit.js +0 -213
  369. package/src/ardy/motion-url.js +0 -55
  370. package/src/ardy/mp4-duration.js +0 -102
  371. package/src/ardy/npz.js +0 -595
  372. package/src/ardy/physics-panel.css +0 -21
  373. package/src/ardy/physics-panel.jsx +0 -89
  374. package/src/ardy/physics-refine.js +0 -111
  375. package/src/ardy/physics-review.js +0 -567
  376. package/src/ardy/physics-temporal.js +0 -54
  377. package/src/ardy/platform-fit-panel.css +0 -64
  378. package/src/ardy/platform-fit-panel.jsx +0 -41
  379. package/src/ardy/platform-fit.js +0 -326
  380. package/src/ardy/playback.js +0 -711
  381. package/src/ardy/pose-pin.js +0 -97
  382. package/src/ardy/prompt-clips.js +0 -30
  383. package/src/ardy/retime.js +0 -211
  384. package/src/ardy/root-drop.js +0 -369
  385. package/src/ardy/timeline-resize.js +0 -13
  386. package/src/ardy/to-cskel27.js +0 -225
  387. package/src/ardy/trim.js +0 -33
  388. package/src/ardy/waypoints.js +0 -369
  389. package/src/ardy-bridge-contract.js +0 -281
  390. package/src/domains/motion/motion-video-capture.js +0 -664
  391. package/src/i2v-motion-client.js +0 -77
  392. package/src/i2v-motion-studio.jsx +0 -180
  393. package/src/line-edit.js +0 -1348
  394. package/src/motion/generation.js +0 -105
  395. package/src/motion-readiness-ui.jsx +0 -67
  396. package/src/motion-readiness.js +0 -40
  397. package/src/motion-resources.js +0 -177
  398. package/src/motion-store.js +0 -65
  399. package/src/motion-trail.js +0 -601
  400. package/src/multimodel-ingest.js +0 -394
  401. package/src/panels/PromptBlocksPanel.jsx +0 -444
  402. package/src/panels/VideoCapturePanel.jsx +0 -181
  403. package/src/pose-extract/browser.js +0 -42
  404. package/src/pose-extract/detector.js +0 -100
  405. package/src/pose-extract/fit.js +0 -227
  406. package/src/pose-extract/image-frame.js +0 -51
  407. package/src/pose-extract/index.js +0 -58
  408. package/src/pose-extract/keyframes.js +0 -74
  409. package/src/pose-extract/landmarks.js +0 -162
  410. package/src/pose-extract/mirror.js +0 -153
  411. package/src/pose-extract/take.js +0 -131
  412. package/src/pose-extract/video-frames.js +0 -91
  413. package/src/take-recipe.js +0 -165
  414. package/src/trail-key-conflicts.js +0 -39
  415. package/src/trail-pick.js +0 -59
  416. package/src/use-i2v-capture-modal.js +0 -15
  417. package/tools/ardy/BRIDGE.md +0 -37
  418. package/tools/ardy/GVHMR-NPZ.md +0 -68
  419. package/tools/ardy/README.md +0 -124
  420. package/tools/ardy/__pycache__/cclay_gvhmr_worker.cpython-313.pyc +0 -0
  421. package/tools/ardy/artifacts.mjs +0 -34
  422. package/tools/ardy/bridge.mjs +0 -1732
  423. package/tools/ardy/bvh-cskel27.mjs +0 -1265
  424. package/tools/ardy/cclay_constrained_generate.py +0 -561
  425. package/tools/ardy/cclay_gvhmr_worker.py +0 -264
  426. package/tools/ardy/cclay_motion_edit.py +0 -510
  427. package/tools/ardy/cclay_sequence_generate.py +0 -705
  428. package/tools/ardy/cclay_worker.py +0 -304
  429. package/tools/ardy/dump-npz.py +0 -205
  430. package/tools/ardy/extract-rest.mjs +0 -299
  431. package/tools/ardy/extract.mjs +0 -507
  432. package/tools/ardy/footage.mjs +0 -467
  433. package/tools/ardy/gvhmr-floor.mjs +0 -102
  434. package/tools/ardy/gvhmr-to-cskel27.mjs +0 -21
  435. package/tools/ardy/gvhmr_fastpath.py +0 -168
  436. package/tools/ardy/gvhmr_trajectory.py +0 -240
  437. package/tools/ardy/merge-text-encoder.py +0 -160
  438. package/tools/ardy/mocap-metrics.mjs +0 -200
  439. package/tools/ardy/mocap-quality-gate.mjs +0 -110
  440. package/tools/ardy/motion-stabilize.mjs +0 -348
  441. package/tools/ardy/npz.mjs +0 -414
  442. package/tools/ardy/pose-to-npz.mjs +0 -106
  443. package/tools/ardy/prompt-limits.mjs +0 -5
  444. package/tools/ardy/run-edit-on-box.sh +0 -73
  445. package/tools/ardy/run-local.mjs +0 -233
  446. package/tools/ardy/run-on-box.sh +0 -593
  447. package/tools/ardy/run-sequence-on-box.sh +0 -177
  448. package/tools/ardy/runners/gvhmr-worker.mjs +0 -206
  449. package/tools/ardy/runners/index.mjs +0 -18
  450. package/tools/ardy/runners/local.mjs +0 -455
  451. package/tools/ardy/runners/proc.mjs +0 -125
  452. package/tools/ardy/runners/remote.mjs +0 -252
  453. package/tools/ardy/setup-local.mjs +0 -189
  454. package/tools/ardy/setup-text-encoder-on-box.sh +0 -93
  455. package/tools/ardy/setup-text-encoder.py +0 -306
  456. package/tools/ardy/smpl-cskel27.mjs +0 -178
  457. package/tools/ardy/visual-qa.mjs +0 -196
  458. package/tools/ardy/vq-car.mjs +0 -52
  459. package/tools/bench/EXP3.md +0 -61
  460. package/tools/bench/cclay_bench_extract_incam.py +0 -125
  461. package/tools/bench/cclay_bench_extract_obs.py +0 -204
  462. package/tools/bench/cclay_bench_runner.py +0 -42
  463. package/tools/bench/cube-contact.mjs +0 -162
  464. package/tools/bench/exp3.mjs +0 -152
  465. package/tools/bench/extract-bench-lib.mjs +0 -216
  466. package/tools/bench/extract-bench.mjs +0 -302
  467. package/tools/bench/fal-generate.mjs +0 -88
  468. package/tools/bench/fit/README.md +0 -189
  469. package/tools/bench/fit/camera.mjs +0 -59
  470. package/tools/bench/fit/contact.mjs +0 -419
  471. package/tools/bench/fit/footlock.mjs +0 -234
  472. package/tools/bench/fit/motion.mjs +0 -85
  473. package/tools/bench/fit/pin.mjs +0 -27
  474. package/tools/bench/fit/remote.mjs +0 -105
  475. package/tools/bench/fit-bench.mjs +0 -126
  476. package/tools/bench/fit-sanity.mjs +0 -45
  477. package/tools/bench/metrics.mjs +0 -313
  478. package/tools/bench/obs/depth.mjs +0 -258
  479. package/tools/bench/obs/extrinsics.mjs +0 -175
  480. package/tools/bench/obs/fit_mannequin_betas.py +0 -340
  481. package/tools/bench/obs/ground.mjs +0 -294
  482. package/tools/bench/obs/heading.mjs +0 -151
  483. package/tools/bench/obs/ladder.mjs +0 -555
  484. package/tools/bench/obs/mannequin-betas.json +0 -124
  485. package/tools/bench/obs/remote.mjs +0 -83
  486. package/tools/bench/obs/rest_joints.py +0 -52
  487. package/tools/bench/obs/ybot-targets.mjs +0 -49
  488. package/tools/bench/obs-bench.mjs +0 -520
  489. package/tools/bench/score.mjs +0 -343
  490. package/tools/bench/summarize.mjs +0 -86
  491. package/tools/demo-worker/README.md +0 -137
  492. package/tools/demo-worker/api-client.mjs +0 -233
  493. package/tools/demo-worker/generate.mjs +0 -301
  494. package/tools/demo-worker/index.mjs +0 -466
  495. package/tools/demo-worker/sign-core.mjs +0 -112
  496. package/tools/demo-worker/sign.mjs +0 -161
  497. package/tools/gt-render/browser.mjs +0 -182
  498. package/tools/gt-render/camera-math.mjs +0 -276
  499. package/tools/gt-render/page.mjs +0 -267
  500. package/tools/gt-render/render.mjs +0 -514
  501. package/tools/gt-render/scene-box.mjs +0 -31
  502. package/tools/gt-render/take-transform.mjs +0 -87
  503. package/tools/kimodo/compare-seam.mjs +0 -108
  504. package/tools/kimodo/constraints.mjs +0 -244
  505. package/tools/kimodo/edit.mjs +0 -259
  506. package/tools/kimodo/effector-constraints.mjs +0 -200
  507. package/tools/kimodo/generate.mjs +0 -646
  508. package/tools/kimodo/local-output.mjs +0 -265
  509. package/tools/kimodo/measure-pose.mjs +0 -65
  510. package/tools/kimodo/measure-preserve.mjs +0 -899
  511. package/tools/kimodo/measure-waypoints.mjs +0 -55
  512. package/tools/kimodo/mlx-generate.py +0 -111
  513. package/tools/kimodo/pose-constraints.mjs +0 -248
  514. package/tools/kimodo/preserve-mask.mjs +0 -543
  515. package/tools/kimodo/read-npz.mjs +0 -189
  516. package/tools/kimodo/run-edit-on-box.mjs +0 -155
  517. package/tools/kimodo/run-sequence-on-box.mjs +0 -178
  518. package/tools/kimodo/runner.mjs +0 -284
  519. package/tools/kimodo/seam-batch.mjs +0 -77
  520. package/tools/kimodo/setup-local.mjs +0 -30
  521. package/tools/kimodo/setup-on-box.sh +0 -339
  522. package/tools/kimodo/soma77-to-cskel27.mjs +0 -305
  523. package/tools/morphgs/assets/gen_truth.py +0 -39
  524. package/tools/morphgs/assets/mesh_ori_rig.txt +0 -27
  525. package/tools/morphgs/assets/playback-check.mjs +0 -30
  526. package/tools/morphgs/demo-gate.sh +0 -26
  527. package/tools/morphgs/fbx2morphgs.mjs +0 -68
  528. package/tools/morphgs/morphgs-to-cskel27.mjs +0 -105
  529. package/tools/morphgs/patches/preprocess_src-none-mode.patch +0 -76
  530. package/tools/morphgs/setup-on-cluster.sh +0 -126
  531. package/tools/projflow/driver.py +0 -1069
  532. package/tools/projflow/generate.mjs +0 -805
  533. package/tools/projflow/hml22-to-cskel27.mjs +0 -735
  534. package/tools/projflow/line-edit-job.mjs +0 -547
  535. package/tools/projflow/replay.mjs +0 -452
  536. package/tools/projflow/runner.mjs +0 -269
  537. package/tools/projflow/service.mjs +0 -742
  538. package/tools/track/DESIGN.md +0 -513
  539. package/tools/track/backfill-provenance.mjs +0 -116
  540. package/tools/track/budget.mjs +0 -8
  541. package/tools/track/check-rig.mjs +0 -209
  542. package/tools/track/diagnostics.schema.json +0 -60
  543. package/tools/track/export-rig.mjs +0 -256
  544. package/tools/track/fallback.mjs +0 -7
  545. package/tools/track/fk-parity-fixture.mjs +0 -161
  546. package/tools/track/gate.mjs +0 -349
  547. package/tools/track/masks.mjs +0 -318
  548. package/tools/track/metrics.mjs +0 -201
  549. package/tools/track/publish-obs.mjs +0 -108
  550. package/tools/track/py/check_env.py +0 -61
  551. package/tools/track/py/eval_lr.py +0 -229
  552. package/tools/track/py/lr_viterbi.py +0 -294
  553. package/tools/track/py/masks.py +0 -405
  554. package/tools/track/py/objective.py +0 -382
  555. package/tools/track/py/rig.py +0 -508
  556. package/tools/track/py/scene.py +0 -326
  557. package/tools/track/py/test_joint_indices.py +0 -123
  558. package/tools/track/py/test_lr_viterbi.py +0 -195
  559. package/tools/track/py/test_masks.py +0 -229
  560. package/tools/track/py/test_rig.py +0 -288
  561. package/tools/track/py/test_scene.py +0 -354
  562. package/tools/track/py/test_track.py +0 -328
  563. package/tools/track/py/track.py +0 -411
  564. package/tools/track/remote.mjs +0 -140
  565. package/tools/track/rig-dump.mjs +0 -227
  566. package/tools/track/run-box-tests.mjs +0 -14
  567. package/tools/track/run-box.mjs +0 -168
  568. package/tools/track/setup-box.sh +0 -59
  569. package/tools/track/study-2d.mjs +0 -728
  570. /package/src/{ardy → motion}/collision-blockers.js +0 -0
  571. /package/src/{ardy → motion}/ground.js +0 -0
  572. /package/src/{ardy → motion}/ik-drag.js +0 -0
  573. /package/src/{ardy → motion}/ik-key-json.js +0 -0
  574. /package/src/{ardy → motion}/ik.js +0 -0
  575. /package/src/{ardy → motion}/physics-surface.js +0 -0
  576. /package/src/{ardy → motion}/playback-clock.js +0 -0
  577. /package/src/{ardy → motion}/range-pin.js +0 -0
@@ -0,0 +1,211 @@
1
+ // Self-collision auto-fix (spec.fix.self, on unless false): the arms keep out of the rest of the body,
2
+ // on the fitted skin the BODY check measures (body.js, qa/body-checks.js). Wrist targets inside the
3
+ // body already went out onto the skin when they were solved (compile.js keepOut); what is left here:
4
+ // - elbow: an upper arm or forearm inside the body turns the elbow about the shoulder-wrist line
5
+ // (ctx.swivel; the hand keeps its place and turn). The turn is planned over the whole take at once,
6
+ // absolute: each frame scores every turn in TURNS by the depth left, the size of the turn and how
7
+ // far the elbow rises past LOW under the shoulder (no chicken wing), plus the change from the frame before, and the cheapest
8
+ // path wins (Viterbi), so the elbow eases in and out instead of snapping;
9
+ // - hand: a hand or forearm still a little inside moves its wrist IK target out along the skin's
10
+ // normal (ctx.skinPush), at most STEP a pass and REACH in all, eased over neighbouring frames. Deeper hits, and the
11
+ // head and neck (a hand at the mouth is on purpose), stay for QA.
12
+ // Frames whose arm is FK (armw 0) only get the elbow turn. The report says what moved.
13
+ import { bodyFor, bodyNormal, limbDepth, segmentDepth } from "../body.js";
14
+ import { add, sub, scale, len, apply, axisRot } from "../compiler/math.js";
15
+ import { CAPSULES, segPoints, sdBox } from "../qa/geometry.js";
16
+ import { round4 } from "./ease.js";
17
+ import { turnWeight } from "../compiler/pose.js";
18
+ import { allowance } from "../qa/checks.js";
19
+ import { regionOf } from "../qa/body-checks.js";
20
+
21
+ const TURNS = Array.from({ length: 19 }, (_, i) => -90 + i * 10);
22
+ const PASSES = 3, EASE = 5, MARGIN = 0.003, ALLOW = 0.003, STEP = 0.03, REACH = 0.08;
23
+ /** The most of a declared allowance the push leaves alone (m): a touch (a hand in a pocket), not a skill's leeway for a sweep it cannot avoid. */
24
+ const TOUCH = 0.03;
25
+ /** Plan weights: per cm^2 of depth, per (10 deg)^2 of turn, per cm^2 of elbow over the shoulder, per (10 deg)^2 of change a frame. */
26
+ const DEEP = 40, TURN = 0.05, RISE = 4, CHANGE = 2;
27
+ /** How far (m) under the shoulder the elbow may come before RISE counts. Where the spec declares a chicken-wing check
28
+ * on this elbow (checks.below, joint <Side>ForeArm under <Side>Arm: the smoke skill's), rising past its min costs
29
+ * WING_PRICE: a smoking elbow the turn raised past it broke that check once the wrist stopped at its limit (wrist-clamp.js). */
30
+ const LOW = 0.09, WING_PRICE = 400;
31
+
32
+ /** The spec's chicken-wing min (m) for the elbow of `side` on frame i (a below check on it at least half on), else null. */
33
+ function wingMin(out, i, side) {
34
+ let min = null;
35
+ for (const b of out.ctx.spec.checks?.below ?? []) {
36
+ const joint = String(b.joint).replace(/^joint:/, ""), ref = String(b.ref).replace(/^joint:/, ""), f = out.f0 + i;
37
+ const on = Array.isArray(b.when) ? f >= b.when[0] && f <= b.when[1] : b.when ? (out.frames[i].c[b.when] ?? 0) > 0.5 : true;
38
+ if (on && joint === `${side}ForeArm` && ref === `${side}Arm` && Number.isFinite(b.min)) min = Math.max(min ?? 0, b.min);
39
+ }
40
+ return min;
41
+ }
42
+ /** The most the turn changes in one frame (deg): a fast arm turned faster than this pops. */
43
+ const STEP_TURN = 10;
44
+ const SIDES = { l: "Left", r: "Right" };
45
+
46
+ /** How much hand k is on a measured target at channels c: a fit of its own (the mouth), or a fit of the
47
+ * other hand anchored to a prop k holds (the flame under k's cigarette). Such a hand is not moved. */
48
+ export function fitWeight(spec, c, k) {
49
+ let w = 0;
50
+ for (const fit of spec.fits ?? []) {
51
+ const a = spec.anchors?.[fit.anchor], on = c[`w_${fit.anchor}_${fit.side}`] ?? 0;
52
+ if (fit.side === k || (a?.kind === "prop" && spec.props?.[a.prop]?.hand === k)) w = Math.max(w, on);
53
+ }
54
+ return Math.min(1, w);
55
+ }
56
+
57
+ const CAP = Object.fromEntries(CAPSULES.map(c => [c[0], c[3]]));
58
+ /** How deep (m) segment A-B of radius r goes into the scene's boxes (QA's COLLIDE on the same capsules). */
59
+ function sceneDepth(boxes, A, B, r) {
60
+ let worst = 0;
61
+ for (const box of boxes) worst = Math.max(worst, r - Math.min(...segPoints(A, B, 5).map(p => sdBox(p, box))));
62
+ return worst;
63
+ }
64
+
65
+ /** Deepest skin depth (m) of the upper arm and forearm with the elbow turned `deg` more about the shoulder-wrist line,
66
+ * and how deep they go into the scene's boxes (an elbow turned out of the body must not turn into a steering wheel). */
67
+ function elbowDepth(body, sk, s, side, deg, boxes = []) {
68
+ const I = sk.index, a = I[`${side}Arm`], fa = I[`${side}ForeArm`], S = s.P[a], W = s.P[I[`${side}Hand`]];
69
+ const E = deg ? add(S, apply(axisRot(sub(W, S), deg), sub(s.P[fa], S))) : s.P[fa];
70
+ const worst = Math.max(segmentDepth(body, sk, s, a, S, E), segmentDepth(body, sk, s, fa, E, W));
71
+ const k = side === "Left" ? "l" : "r", scene = boxes.length ? Math.max(sceneDepth(boxes, S, E, CAP[`uarm_${k}`]), sceneDepth(boxes, E, W, CAP[`farm_${k}`])) : 0;
72
+ return { depth: worst, scene, rise: Math.max(0, E[1] - (S[1] - LOW)), under: S[1] - E[1] };
73
+ }
74
+
75
+ /** Shoulder to wrist at full stretch (m), at bind. */
76
+ const armLength = (sk, side) => { const I = sk.index, P = sk.restWorldPos; return len(sub(P[I[`${side}ForeArm`]], P[I[`${side}Arm`]])) + len(sub(P[I[`${side}Hand`]], P[I[`${side}ForeArm`]])); };
77
+
78
+ /** Absolute elbow turns (deg) for side k over the take, or null when no frame needs or has one. */
79
+ function planTurns(out, body, k) {
80
+ const { sk } = out.ctx, side = SIDES[k], n = out.frames.length, now = out.ctx.swivel[k];
81
+ const cost = out.frames.map(({ s, c }, i) => {
82
+ const tw = turnWeight(c[`armw_${k}`] ?? 0), cur = (now.get(out.f0 + i) ?? 0) * tw;
83
+ if (!cur && elbowDepth(body, sk, s, side, 0).depth <= ALLOW) return null;
84
+ // only the boxes the arm can reach this frame (their bounding sphere within the arm's length of the shoulder)
85
+ const S = s.P[sk.index[`${side}Arm`]], reach = armLength(sk, side) + 0.15;
86
+ const boxes = Object.values(out.objectsAt(i)).filter(box => box.half && !box.ignore && len(sub(add(box.pos, apply(box.R, [0, box.half[1], 0])), S)) - len(box.half) < reach);
87
+ const base = elbowDepth(body, sk, s, side, -cur, boxes).scene, wmin = wingMin(out, i, side);
88
+ return TURNS.map(deg => {
89
+ const { depth, scene, rise, under } = elbowDepth(body, sk, s, side, deg * tw - cur, boxes), cm = Math.max(0, depth - ALLOW) * 100;
90
+ const wing = wmin === null ? 0 : Math.max(0, wmin - under);
91
+ // a box the arm was already in at no turn is QA's COLLIDE to report, not this plan's to avoid
92
+ const hit = Math.max(0, scene - Math.max(base, ALLOW)) * 100;
93
+ return DEEP * (cm * cm + hit * hit) + TURN * (deg / 10) ** 2 + RISE * (rise * 100) ** 2 + WING_PRICE * (wing * 100) ** 2;
94
+ });
95
+ });
96
+ if (cost.every(c => !c)) return null;
97
+ // a frame that needs no turn has none; the take may start already turned
98
+ const zero = TURNS.indexOf(0), still = TURNS.map((_, j) => (j === zero ? 0 : Infinity));
99
+ let acc = TURNS.map(() => 0);
100
+ const back = [];
101
+ for (let i = 0; i < n; i++) {
102
+ const here = cost[i] ?? still, prev = acc, from = [];
103
+ acc = here.map((h, j) => {
104
+ let best = Infinity, arg = zero;
105
+ for (let p = 0; p < TURNS.length; p++) {
106
+ if (Math.abs(TURNS[j] - TURNS[p]) > STEP_TURN) continue;
107
+ const v = prev[p] + CHANGE * ((TURNS[j] - TURNS[p]) / 10) ** 2;
108
+ if (v < best) { best = v; arg = p; }
109
+ }
110
+ from.push(arg);
111
+ return best + h;
112
+ });
113
+ back.push(from);
114
+ }
115
+ const path = new Array(n);
116
+ let j = acc.indexOf(Math.min(...acc));
117
+ for (let i = n - 1; i >= 0; i--) { path[i] = TURNS[j]; j = back[i][j]; }
118
+ // the 10-degree steps, smoothed
119
+ return path.map((_, i) => (path[Math.max(0, i - 1)] + 2 * path[i] + path[Math.min(n - 1, i + 1)]) / 4);
120
+ }
121
+
122
+ /** Per-frame wrist pushes for side k: a hand or forearm a little inside the skin (not the head or neck) goes out along its normal. */
123
+ function handPushes(out, body, k, stats) {
124
+ const { sk } = out.ctx, side = SIDES[k], I = sk.index, n = out.frames.length, v = new Array(n).fill(null);
125
+ const face = [I.Head, I.Neck, I.HeadTop_End].filter(b => b !== undefined);
126
+ for (let i = 0; i < n; i++) {
127
+ const { s, c } = out.frames[i], f = out.f0 + i;
128
+ // a hand on a measured target (a fit's anchor: the mouth, the flame) stays where its goal needs it; on its way
129
+ // there the push fades out with the target's weight
130
+ const free = 1 - fitWeight(out.ctx.spec, c, k);
131
+ if (free < 0.05) continue;
132
+ // past what the spec allows (a hand in a pocket touches the hip: checks.allow), as QA's BODY measures it
133
+ let hit = null;
134
+ for (const b of [I[`${side}Hand`], I[`${side}ForeArm`]]) {
135
+ const h = limbDepth(body, sk, s, b);
136
+ if (h.into < 0 || face.includes(h.into)) continue;
137
+ const allowed = Math.min(TOUCH, allowance(out.ctx.spec.checks?.allow, regionOf(sk, b), regionOf(sk, h.into)) ?? 0), past = h.depth - Math.max(allowed, ALLOW);
138
+ if (past > 0 && (!hit || past > hit.past)) hit = { ...h, b, past };
139
+ }
140
+ if (!hit) continue;
141
+ if (!(c[`armw_${k}`] > 1e-4)) { stats.skipped++; continue; }
142
+ const done = len(out.ctx.skinPush[k].get(f) ?? [0, 0, 0]);
143
+ if (done >= REACH) { stats.deep++; continue; }
144
+ const nrm = bodyNormal(body, s, hit.p, new Set([...body.skip.get(hit.b), ...face]));
145
+ if (nrm) v[i] = scale(nrm, free * Math.min(STEP, REACH - done, hit.past + MARGIN));
146
+ }
147
+ // eased dilation: neighbours get the strongest nearby push, scaled down
148
+ const eased = new Array(n).fill(null);
149
+ v.forEach((p, i) => {
150
+ if (!p) return;
151
+ for (let j = Math.max(0, i - EASE - 1); j <= Math.min(n - 1, i + EASE + 1); j++) {
152
+ const t = 1 - Math.abs(i - j) / (EASE + 1), w = scale(p, t * t * (3 - 2 * t));
153
+ if (!eased[j] || len(w) > len(eased[j])) eased[j] = w;
154
+ }
155
+ });
156
+ return eased;
157
+ }
158
+
159
+ export function selfClear(out, engine) {
160
+ const body = bodyFor(out.ctx.sk);
161
+ if (!body) return null;
162
+ const n = out.frames.length, stats = { skipped: 0, deep: 0 }, turned = new Set(), pushed = new Set();
163
+ let maxTurn = 0, maxPush = 0;
164
+ for (let pass = 0; pass < PASSES; pass++) {
165
+ let touched = new Set();
166
+ for (const k of ["r", "l"]) {
167
+ const plan = planTurns(out, body, k);
168
+ if (!plan) continue;
169
+ plan.forEach((deg, i) => {
170
+ const f = out.f0 + i, cur = out.ctx.swivel[k].get(f) ?? 0, next = Math.abs(deg) < 0.5 ? 0 : deg;
171
+ if (Math.abs(next - cur) < 0.5) return;
172
+ if (next) out.ctx.swivel[k].set(f, next); else out.ctx.swivel[k].delete(f);
173
+ maxTurn = Math.max(maxTurn, Math.abs(next)); if (next) turned.add(`${k}${i}`); touched.add(f);
174
+ });
175
+ }
176
+ if (touched.size) engine.solve([...touched]);
177
+ touched = new Set();
178
+ stats.skipped = 0; stats.deep = 0;
179
+ for (const k of ["r", "l"]) {
180
+ handPushes(out, body, k, stats).forEach((p, i) => {
181
+ if (!p) return;
182
+ const f = out.f0 + i, next = add(out.ctx.skinPush[k].get(f) ?? [0, 0, 0], p);
183
+ out.ctx.skinPush[k].set(f, next);
184
+ maxPush = Math.max(maxPush, len(next)); pushed.add(`${k}${i}`); touched.add(f);
185
+ });
186
+ }
187
+ if (!touched.size) break;
188
+ engine.solve([...touched]);
189
+ }
190
+ // the pushes add up pass by pass from frames that read their normals apart: smoothed over time, so the hand
191
+ // does not jerk (a 1-4-6-4-1 kernel, twice)
192
+ if (pushed.size) {
193
+ const touched = new Set();
194
+ for (const k of ["r", "l"]) {
195
+ const at = i => out.ctx.skinPush[k].get(out.f0 + i) ?? [0, 0, 0];
196
+ for (let pass = 0; pass < 2; pass++) {
197
+ const was = Array.from({ length: n }, (_, i) => at(i));
198
+ was.forEach((_, i) => {
199
+ const sm = [-2, -1, 0, 1, 2].reduce((acc, d, j) => add(acc, scale(was[Math.min(n - 1, Math.max(0, i + d))], [1, 4, 6, 4, 1][j] / 16)), [0, 0, 0]);
200
+ if (len(sm) < 1e-5 && !out.ctx.skinPush[k].has(out.f0 + i)) return;
201
+ out.ctx.skinPush[k].set(out.f0 + i, sm); touched.add(out.f0 + i);
202
+ });
203
+ }
204
+ }
205
+ if (touched.size) engine.solve([...touched]);
206
+ }
207
+ if (!turned.size && !pushed.size && !stats.skipped) return null;
208
+ return { elbowFrames: turned.size, maxTurn: Math.round(maxTurn), handFrames: pushed.size, maxPush: round4(maxPush), skipped: stats.skipped, tooDeep: stats.deep,
209
+ how: "elbow turned about the shoulder-wrist line; a hand a little inside pushed out along the skin" };
210
+ }
211
+
@@ -0,0 +1,70 @@
1
+ // Planted-foot lock: while a heel or ball is planted (QA's own definition, over the ground under
2
+ // it: the floor or an object's top, qa/ground.js) and drifts less than
3
+ // 1 cm, the ankle target is shifted so the planted point stays where it landed. The foot pose
4
+ // depends only on the ankle target, fyaw and fpitch, so the shift moves the sole rigidly.
5
+ // A pivot (the planted point is the only one down: the heel is up over a planted ball, or the
6
+ // ball over a planted heel) is held whatever the drift: fyaw and fpitch then turn the foot about
7
+ // that point, as a batter's back foot turns on its ball, instead of about the ankle. A pivot that
8
+ // held more than 1 cm keeps holding while its point stays under PIVOT_H (a hover, not a step) and
9
+ // lets go no faster than EASE_STEP a frame.
10
+ import { add, sub } from "../compiler/math.js";
11
+ import { QA_LIMITS } from "../qa/checks.js";
12
+ import { groundSole } from "../qa/ground.js";
13
+ import { round4 } from "./ease.js";
14
+
15
+ const MAX_DRIFT = 0.01, DECAY = 4, PIVOT_H = 0.03, EASE_STEP = 0.02;
16
+
17
+ export function lockFeet(out, engine) {
18
+ const n = out.frames.length, H = QA_LIMITS.plantH;
19
+ let plants = 0, pivots = 0, worst = 0;
20
+ const touched = new Set();
21
+ for (const [k, S] of [["l", "Left"], ["r", "Right"]]) {
22
+ const ch = out.baked[`foot_${k}`];
23
+ if (!ch) continue;
24
+ const sole = out.frames.map((_, i) => groundSole(out, i, S));
25
+ const planted = (i, key) => sole[i][key][1] < H && sole[i - 1][key][1] < H && Math.abs(sole[i][key][1] - sole[i - 1][key][1]) < 0.0005;
26
+ const delta = Array.from({ length: n }, () => null);
27
+ for (let i = 1; i < n;) {
28
+ if (!planted(i, "ball") && !planted(i, "heel")) { i++; continue; }
29
+ // one contact run: the ball wins over the heel; a switch re-anchors on the new point
30
+ let key = null, ref = null, j = i, max = 0, last = [0, 0, 0], pivot = false;
31
+ const run = [];
32
+ const hold = jj => {
33
+ const d = sub(ref, sole[jj][key]); d[1] = 0;
34
+ last = d;
35
+ run.push([jj, d]);
36
+ max = Math.max(max, Math.hypot(d[0], d[2]));
37
+ };
38
+ for (; j < n && (planted(j, "ball") || planted(j, "heel")); j++) {
39
+ const kj = planted(j, "ball") ? "ball" : "heel";
40
+ pivot ||= sole[j][kj === "ball" ? "heel" : "ball"][1] >= H;
41
+ // on a switch the new point is anchored where the previous frame's shift put it
42
+ if (kj !== key) { key = kj; ref = add(sole[j - 1][key], last); }
43
+ hold(j);
44
+ }
45
+ if (pivot && max >= MAX_DRIFT) for (; j < n && sole[j][key][1] < PIVOT_H; j++) hold(j);
46
+ if (max > 2e-4 && (max < MAX_DRIFT || pivot)) {
47
+ plants++; worst = Math.max(worst, max);
48
+ if (max >= MAX_DRIFT) pivots++;
49
+ for (const [jj, d] of run) delta[jj] = d;
50
+ const decay = Math.max(DECAY, Math.ceil(Math.hypot(last[0], last[2]) / EASE_STEP));
51
+ for (let m = 1; m <= decay && j - 1 + m < n; m++) {
52
+ if (delta[j - 1 + m]) break;
53
+ const t = m / (decay + 1), e = 1 - t * t * (3 - 2 * t);
54
+ delta[j - 1 + m] = [last[0] * e, 0, last[2] * e];
55
+ }
56
+ }
57
+ i = j;
58
+ }
59
+ for (let i = 0; i < n; i++) {
60
+ if (!delta[i]) continue;
61
+ const f = out.f0 + i;
62
+ ch[f] = add(ch[f], delta[i]);
63
+ touched.add(f);
64
+ }
65
+ }
66
+ if (!plants) return null;
67
+ engine.solve([...touched]);
68
+ return { plants, ...(pivots ? { pivots } : {}), frames: touched.size, max: round4(worst),
69
+ how: pivots ? "planted heel/ball held in place; turning feet pivot on it" : "planted heel/ball held in place" };
70
+ }
@@ -0,0 +1,51 @@
1
+ // A planted flat foot keeps its heading (lane Q1). While both its heel and its ball are planted
2
+ // (QA's definition: under plantH over the ground, qa/ground.js, and still), the foot cannot turn without one of them sliding: a
3
+ // flat foot under the body's weight grips the floor. A turn the channels ask for then (fyaw_<k>, or
4
+ // the body's yaw when fyaw_<k> is not keyed) waits: the foot keeps the heading it planted with
5
+ // until its heel or its toes come up or it lifts, and then eases onto the keyed heading at about
6
+ // TURN_RATE degrees a frame. With the heel up it turns on its ball (slide.js holds the ball and
7
+ // pivots the foot there), in the air it turns freely. Session 4db5d447: fyaw_r keyed 0 at f0 and
8
+ // 30 at f37 turned the flat back foot 9 deg over the load (f10-19), and its heel slid 2.2 cm.
9
+ // A foot that turns only while a heel or ball is up, and a flat foot that does not turn, are left
10
+ // as they were.
11
+ import { QA_LIMITS } from "../qa/checks.js";
12
+ import { groundSole } from "../qa/ground.js";
13
+ import { round4 } from "./ease.js";
14
+
15
+ const TURN_RATE = 6, DECAY = 4, TOL = 1;
16
+ const smooth = t => t * t * (3 - 2 * t);
17
+
18
+ /** Holds the heading of flat planted feet (fyaw_<k>), eases it onto the keys once a heel or ball is up; re-solves those frames. */
19
+ export function holdTurns(out, engine) {
20
+ const n = out.frames.length, H = QA_LIMITS.plantH, touched = new Set();
21
+ let worst = 0;
22
+ for (const [k, S] of [["l", "Left"], ["r", "Right"]]) {
23
+ const keyed = out.baked[`fyaw_${k}`] ?? out.baked.yaw;
24
+ if (!out.baked[`foot_${k}`] || !keyed) continue;
25
+ const sole = out.frames.map((_, i) => groundSole(out, i, S));
26
+ const down = (i, key) => sole[i][key][1] < H && sole[i - 1][key][1] < H && Math.abs(sole[i][key][1] - sole[i - 1][key][1]) < 0.0005;
27
+ const flat = i => i > 0 && i < n && down(i, "heel") && down(i, "ball");
28
+ const want = Array.from({ length: n }, (_, i) => keyed[out.f0 + i]), yaw = want.slice();
29
+ for (let i = 1; i < n; i++) {
30
+ if (!flat(i)) continue;
31
+ const held = yaw[i - 1];
32
+ let j = i, turn = 0;
33
+ for (; flat(j); j++) turn = Math.max(turn, Math.abs(yaw[j] - held));
34
+ // a turn within TOL degrees (the heel moves under 3 mm) is left as it is
35
+ if (turn <= TOL) { i = j; continue; }
36
+ worst = Math.max(worst, turn);
37
+ for (let m = i; m < j; m++) yaw[m] = held;
38
+ // then onto the keyed heading, as long as the foot does not plant flat again
39
+ const gap = j < n ? want[j] - held : 0, ease = Math.max(DECAY, Math.ceil(Math.abs(gap) / TURN_RATE));
40
+ for (let m = 0; gap && m < ease && j + m < n && !flat(j + m); m++) yaw[j + m] = want[j + m] - gap * (1 - smooth((m + 1) / (ease + 1)));
41
+ i = j;
42
+ }
43
+ const moved = yaw.map((v, i) => v !== want[i]);
44
+ if (!moved.some(Boolean)) continue;
45
+ const ch = (out.baked[`fyaw_${k}`] = keyed.slice());
46
+ moved.forEach((m, i) => { if (m) { ch[out.f0 + i] = yaw[i]; touched.add(out.f0 + i); } });
47
+ }
48
+ if (!touched.size) return null;
49
+ engine.solve([...touched]);
50
+ return { frames: touched.size, max: round4(worst) };
51
+ }
@@ -0,0 +1,39 @@
1
+ // C16. Follow entries: spec.objects[id] = { follow: <ref>, offset?, aim?, visible?, level? } puts a
2
+ // record (a body, a group root, or a record with no box: an effect, a light, an Empty) on a point
3
+ // of the body at every frame. The ref is any point ref (refs.js): landmark:mouth, joint:<Bone>,
4
+ // prop:<prop>.<point>, anchor:<name>, object:<id> (an object a follow entry does not move).
5
+ // aim where the record's +Y points (effects emit along it): "up" (default, world up), "face"
6
+ // (the face's forward direction, the Head bone's rest +Z, as checks.orient "face"), a
7
+ // world direction [x, y, z], or a ref (toward that point). The record turns the shortest
8
+ // way from upright onto it.
9
+ // offset metres along the record's own axes after aim (+Y along aim), from the ref point.
10
+ // The take bakes a world-space track at every frame (take.js: pos, quat and hold on every frame,
11
+ // so object-travel.js cuts the carry of the record's own parents), so the renderer and the effects
12
+ // (born at the emitter's pose at the birth frame) see the moving path. A follow entry is not an
13
+ // obstacle (compile.js marks its bodies carried) and QA checks it against nothing: it rides the body.
14
+ import { refPoint } from "./refs.js";
15
+ import { carried } from "./compiler/limbs.js";
16
+ import { add, sub, apply, norm, len, matFromQuat } from "./compiler/math.js";
17
+ import { arcQuat } from "./compiler/quat.js";
18
+
19
+ const UP = [0, 1, 0];
20
+
21
+ /** The unit direction aim gives at absolute frame f (inside the window), `from` the followed point. */
22
+ function aimDir(out, f, aim = "up", from) {
23
+ if (Array.isArray(aim)) return norm(aim);
24
+ if (aim === "face") { const { sk } = out.ctx; return norm(carried(sk, out.frames[f - (out.f0 ?? 0)].s, sk.index.Head, [0, 0, 1])); }
25
+ const to = aim === "up" ? null : refPoint(out, f, aim), d = to ? sub(to, from) : UP;
26
+ return len(d) > 1e-9 ? norm(d) : UP;
27
+ }
28
+
29
+ /**
30
+ * World pose { pos, R } of the record follow entry o puts at absolute frame f of compile result
31
+ * out (a frame outside the compiled window takes the nearest one in it), or null when the ref
32
+ * has no point there.
33
+ */
34
+ export function followPose(out, o, f) {
35
+ const lo = out.f0 ?? 0, ff = Math.max(lo, Math.min(lo + out.frames.length - 1, f)), p = refPoint(out, ff, o.follow);
36
+ if (!p) return null;
37
+ const R = matFromQuat(arcQuat(UP, aimDir(out, ff, o.aim, p)));
38
+ return { pos: add(p, apply(R, o.offset ?? [0, 0, 0])), R };
39
+ }
@@ -0,0 +1,173 @@
1
+ // C17. Objects nobody holds (a can inside a machine, a box on a shelf) move by keys and fall.
2
+ // spec.objects[id] with no prop, follow or hinge field, for a body or a group (its root):
3
+ // keys [{ f, pos, rot | aim (+ roll) }] the world pose of its root (a body's base pivot, a group's
4
+ // origin) at frame f, on the curve held keys use (object-keys.js keyedPose, C14). aim turns the
5
+ // scene pose's +Y onto aim, then roll degrees about it. Before the first key of a run it stays
6
+ // where it was (its scene pose, or where it landed), after the last it holds the last key.
7
+ // fall { at, onto?, bounce? } or a list from where it is at frame `at` it drops straight down
8
+ // under gravity (G m/s^2, no tumbling) onto the top of the highest still body under the centre of
9
+ // its parts (onto: that object's top; nothing below: the floor, y 0), bounces at most once (bounce:
10
+ // the restitution 0..0.6, default 0.25, never higher than BOUNCE_MAX m) and rests there.
11
+ // Keys and falls chain in frame order: a fall starts from the pose the keys before it leave (a key on the
12
+ // fall's own frame comes first), keys after a fall take over at their first frame.
13
+ // A carry (C3) takes fall too: before its grab (the hand finds it where it landed) or from its release on.
14
+ // A free object moves like a hinged one (compiler/compile.js objectTracks): an obstacle at its pose that
15
+ // frame, read through object-pose.js by refs, goals, probes, observations.near and QA. Pure over the spec,
16
+ // the scene and the time map (a retime moves its keys and falls with the body's keys): the body never
17
+ // moves it, and nothing it lands on moves (bodies a spec.objects entry moves are no support).
18
+ import { isCarried, compose, relative, movesRecord } from "./carried.js";
19
+ import { groupsOf } from "./scene.js";
20
+ import { curveOf, keyedPose } from "./object-keys.js";
21
+ import { add, apply, sub, T, quatFromMat } from "./compiler/math.js";
22
+
23
+ /** Gravity, m/s^2. */
24
+ export const G = 9.8;
25
+ /** The highest a bounce goes, metres. */
26
+ export const BOUNCE_MAX = 0.03;
27
+ const BOUNCE = 0.25, TOUCH = 0.005;
28
+ const HELD_OR_HINGED = ["prop", "follow", "pivot", "axis", "channel"];
29
+
30
+ /** A free entry (C17): no prop, follow or hinge field, and keys or fall. */
31
+ export const isFree = o => !!o && typeof o === "object" && !Array.isArray(o) && HELD_OR_HINGED.every(k => o[k] === undefined) && (o.keys !== undefined || o.fall !== undefined);
32
+ /** The falls of an entry (fall is one or a list), as given. */
33
+ const fallsOf = o => (o?.fall === undefined ? [] : Array.isArray(o.fall) ? o.fall : [o.fall]);
34
+
35
+ const restOf = (scene, id) => scene?.objects?.[id] ?? groupsOf(scene)[id] ?? null;
36
+ const partsIn = (scene, id) => Object.keys(scene?.objects ?? {}).filter(k => k === id || scene.objects[k].ancestors?.includes(id));
37
+
38
+ /** The 8 corners of a base-pivot box { pos, R, half }. */
39
+ function corners(box) {
40
+ const out = [];
41
+ for (const x of [-1, 1]) for (const y of [0, 2]) for (const z of [-1, 1]) out.push(add(box.pos, apply(box.R, [x * box.half[0], y * box.half[1], z * box.half[2]])));
42
+ return out;
43
+ }
44
+ const topOf = box => Math.max(...corners(box).map(p => p[1]));
45
+ /** True when the vertical through x/z point p crosses the footprint of box. */
46
+ const under = (box, p) => { const q = apply(T(box.R), sub([p[0], box.pos[1] + box.half[1], p[2]], box.pos)); return Math.abs(q[0]) <= box.half[0] && Math.abs(q[2]) <= box.half[2]; };
47
+
48
+ /** Bodies a spec.objects entry moves (itself or a group above it): they hold nothing up. */
49
+ function moversIn(spec, scene) {
50
+ const out = new Set();
51
+ for (const [id, o] of Object.entries(scene?.objects ?? {})) if ([id, ...(o.ancestors ?? [])].some(k => movesRecord(spec.objects?.[k]))) out.add(id);
52
+ return out;
53
+ }
54
+
55
+ /** The part boxes of `id` with its root at world pose `root`. */
56
+ function boxesAtRoot(scene, id, root) {
57
+ const rest = restOf(scene, id);
58
+ return partsIn(scene, id).map(p => ({ id: p, ...compose(root, relative(rest, scene.objects[p])), half: scene.objects[p].half }));
59
+ }
60
+
61
+ /** What `id` lands on from pose `start`: { support (a body id, or null for the floor), top, h (the drop, m) }. */
62
+ export function landingOf(spec, scene, id, start, onto = null) {
63
+ const boxes = boxesAtRoot(scene, id, start), own = new Set(boxes.map(b => b.id)), movers = moversIn(spec, scene);
64
+ if (!boxes.length) return { support: null, top: 0, h: 0 };
65
+ const low = Math.min(...boxes.flatMap(b => corners(b).map(p => p[1])));
66
+ const c = boxes.flatMap(corners).reduce((s, p) => add(s, p), [0, 0, 0]).map(v => v / (boxes.length * 8));
67
+ const pool = (onto ? partsIn(scene, onto) : Object.keys(scene.objects)).filter(k => !own.has(k) && !movers.has(k));
68
+ let best = null;
69
+ for (const k of pool) {
70
+ const box = scene.objects[k], top = topOf(box);
71
+ if (under(box, c) && top <= low + TOUCH && (!best || top > best.top)) best = { support: k, top };
72
+ }
73
+ // onto an object that is not right below: its highest top, the agent placed it
74
+ if (!best && onto) for (const k of pool) { const top = topOf(scene.objects[k]); if (top <= low + TOUCH && (!best || top > best.top)) best = { support: k, top }; }
75
+ best ??= { support: null, top: 0 };
76
+ return { ...best, h: Math.max(0, low - best.top) };
77
+ }
78
+
79
+ /** A fall from root pose `start` at (mapped) frame t0: its timing, its pose at a frame, where it rests. */
80
+ function fallPlan(spec, scene, id, start, t0, fall, fps) {
81
+ const land = landingOf(spec, scene, id, start, fall.onto ?? null), h = land.h, tl = Math.sqrt(2 * h / G);
82
+ const e = Math.min(0.6, Math.max(0, fall.bounce ?? BOUNCE)), vb = Math.min(e * G * tl, Math.sqrt(2 * G * BOUNCE_MAX)), tb = 2 * vb / G;
83
+ const rest = { pos: [start.pos[0], start.pos[1] - h, start.pos[2]], R: start.R };
84
+ const at = f => {
85
+ const t = (f - t0) / fps;
86
+ if (t <= 0) return start;
87
+ const dy = t <= tl ? -0.5 * G * t * t : t <= tl + tb ? -h + vb * (t - tl) - 0.5 * G * (t - tl) ** 2 : -h;
88
+ return { pos: [start.pos[0], start.pos[1] + Math.min(0, dy), start.pos[2]], R: start.R };
89
+ };
90
+ return { t0, land: t0 + tl * fps, end: t0 + (tl + tb) * fps, h, support: land.support, top: land.top, at, rest };
91
+ }
92
+
93
+ /** The events of an entry's falls and key runs in frame order (keys split into runs by the falls between them). */
94
+ function eventsOf(o, map, from = -Infinity, to = Infinity) {
95
+ // a carry's keys pose it in hand (C14); only its falls are free
96
+ const keys = isCarried(o) ? [] : [...(o.keys ?? [])].sort((a, b) => a.f - b.f);
97
+ const falls = fallsOf(o).map(fl => ({ ...fl, t: map(fl.at) })).filter(fl => fl.t >= from && fl.t < to).sort((a, b) => a.t - b.t);
98
+ const events = [];
99
+ let run = [], i = 0;
100
+ for (const k of keys) {
101
+ while (i < falls.length && falls[i].t < map(k.f)) { if (run.length) events.push({ run }); run = []; events.push({ fall: falls[i++] }); }
102
+ run.push(k);
103
+ }
104
+ if (run.length) events.push({ run });
105
+ while (i < falls.length) events.push({ fall: falls[i++] });
106
+ return events;
107
+ }
108
+
109
+ /** The pose events give at mapped frame f, starting from pose `start`; `keep` caches each fall's plan on its event. */
110
+ function walk(events, f, start, plan, keep = true) {
111
+ let pose = start;
112
+ for (const e of events) {
113
+ if (e.run) {
114
+ const { t } = e.curve;
115
+ if (f < t[0]) return pose;
116
+ if (f <= t[t.length - 1]) return keyedPose(e.curve, f);
117
+ pose = keyedPose(e.curve, t[t.length - 1]);
118
+ } else {
119
+ if (f < e.fall.t) return pose;
120
+ const p = keep ? (e.plan ??= plan(pose, e.fall)) : plan(pose, e.fall);
121
+ if (f < p.end) return p.at(f);
122
+ pose = p.rest;
123
+ }
124
+ }
125
+ return pose;
126
+ }
127
+
128
+ const timelines = new WeakMap();
129
+ /** The events of entry o under time map `map`, cached per scene, entry and span (key curves built once). */
130
+ function timelineOf(ctx, id, o, map, part) {
131
+ if (!timelines.has(ctx.scene)) timelines.set(ctx.scene, new Map());
132
+ const cache = timelines.get(ctx.scene), key = `${id}|${part}|${ctx.grab}|${ctx.release}`, had = cache.get(key);
133
+ if (had && had.map === map && had.o === o && had.spec === ctx.spec) return had.events;
134
+ const rest = restOf(ctx.scene, id), m = map ?? (f => f), [from, to] = part === "before" ? [-Infinity, ctx.grab] : part === "after" ? [ctx.release, Infinity] : [-Infinity, Infinity];
135
+ const events = eventsOf(o, m, from, to);
136
+ for (const e of events) if (e.run) e.curve = curveOf({ keys: e.run }, m, () => quatFromMat(rest.R), null);
137
+ cache.set(key, { map, o, spec: ctx.spec, events });
138
+ return events;
139
+ }
140
+
141
+ const planner = (ctx, id) => (start, fall) => fallPlan(ctx.spec, ctx.scene, id, start, fall.t, fall, ctx.spec.fps ?? 30);
142
+
143
+ /**
144
+ * World pose { pos, R } of the root of free entry `id` at absolute frame f (ctx = { spec, scene,
145
+ * timeMap }): keys and falls (C17) from its scene pose. null for an id the scene does not know.
146
+ */
147
+ export function freePoseAt(ctx, id, f) {
148
+ const rest = restOf(ctx.scene, id), o = ctx.spec.objects?.[id];
149
+ if (!rest || !o) return null;
150
+ return walk(timelineOf(ctx, id, o, ctx.timeMap, "all"), f, { pos: rest.pos, R: rest.R }, planner(ctx, id));
151
+ }
152
+
153
+ /** Where carry `id` (entry o) is before its grab at absolute f: its scene pose, or where a fall before grab left it. */
154
+ export function beforeGrab(ctx, id, o, f, grab) {
155
+ const rest = restOf(ctx.scene, id);
156
+ if (!rest || !fallsOf(o).length) return rest && { pos: rest.pos, R: rest.R };
157
+ return walk(timelineOf({ ...ctx, grab }, id, o, ctx.timeMap, "before"), f, { pos: rest.pos, R: rest.R }, planner(ctx, id));
158
+ }
159
+
160
+ /** Where carry `id` (entry o) is at absolute f after its release, from its release pose; null without a fall from the release on. */
161
+ export function afterRelease(ctx, id, o, f, release, releasePose) {
162
+ const events = fallsOf(o).length ? timelineOf({ ...ctx, release }, id, o, ctx.timeMap, "after") : [];
163
+ // the release pose comes from the solved body, which a rebake changes: plan these falls afresh
164
+ return events.length ? walk(events, f, releasePose, planner(ctx, id), false) : null;
165
+ }
166
+
167
+ /** The falls of entry `id` as planned under the current time map, for readers (timeline, tests): [{ t0, land, end, h, support, top }]. */
168
+ export function fallPlans(ctx, id) {
169
+ const o = ctx.spec.objects?.[id];
170
+ if (!isFree(o)) return [];
171
+ freePoseAt(ctx, id, Infinity);
172
+ return timelineOf(ctx, id, o, ctx.timeMap, "all").filter(e => e.plan).map(({ plan: { t0, land, end, h, support, top } }) => ({ t0, land, end, h, support, top }));
173
+ }