cozyclay 2.1.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (583) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/README.md +12 -30
  3. package/bin/agent/agent-routes.mjs +40 -113
  4. package/bin/agent/agent-runner.mjs +9 -10
  5. package/bin/agent/cliproxy-provider.mjs +135 -0
  6. package/bin/agent/credential-store.mjs +30 -7
  7. package/bin/agent/pi-tools.mjs +4 -1
  8. package/bin/agent/provider-keys.mjs +32 -0
  9. package/bin/agent/provider-login.mjs +110 -0
  10. package/bin/agent/provider-routes.mjs +99 -0
  11. package/bin/agent/providers.mjs +19 -97
  12. package/bin/agent/script-tool.mjs +17 -4
  13. package/bin/agent/studio-motion-reference.mjs +23 -0
  14. package/bin/agent/studio-prompt.mjs +1 -1
  15. package/bin/agent/studio-rebase-text.mjs +32 -0
  16. package/bin/agent/studio-rebase.mjs +26 -39
  17. package/bin/agent/studio-tools.mjs +9 -28
  18. package/bin/agent/tool-arguments.mjs +49 -0
  19. package/bin/agent/turn-frames.mjs +66 -0
  20. package/bin/codex-auth.mjs +9 -2
  21. package/bin/cozyclay.mjs +10 -116
  22. package/bin/live/cli.mjs +22 -6
  23. package/bin/mcp-runtime.mjs +2 -12
  24. package/bin/package-signature.mjs +0 -4
  25. package/dist/app/index.html +7 -6
  26. package/dist/assets/EXRLoader-CvVIvGVE.js +1 -0
  27. package/dist/assets/analytics-C9sUnndd.js +1 -0
  28. package/dist/assets/app-CCh--1Hc.js +5087 -0
  29. package/dist/assets/app-f-fkV5uP.css +1 -0
  30. package/dist/assets/compile-worker-CkxiD6lH.js +5 -0
  31. package/dist/assets/fflate.module-KbGj5-H6.js +5 -0
  32. package/dist/assets/{first-shot-handoff-BlT33CGf.js → first-shot-handoff-CC4xe4HM.js} +1 -1
  33. package/dist/assets/{landing-BSesrCpL.js → landing-DjHD8u7k.js} +1 -1
  34. package/dist/assets/scene-asset-sync-Hx_FzdOH.js +20 -0
  35. package/dist/assets/scene-asset-sync-_S8NQl5B.css +1 -0
  36. package/dist/assets/workflow-C4yKH1p4.js +179 -0
  37. package/dist/cozyclay-package.json +1 -1
  38. package/dist/index.html +3 -3
  39. package/dist/llms.txt +14 -0
  40. package/dist/sw.js +3 -6
  41. package/dist/workflow/index.html +5 -4
  42. package/mcp/LIVE-PROTOCOL.md +7 -47
  43. package/mcp/README.md +16 -26
  44. package/mcp/live-hub.mjs +6 -10
  45. package/mcp/package.json +1 -3
  46. package/mcp/tool-handlers.mjs +4 -168
  47. package/package.json +3 -22
  48. package/src/App.jsx +174 -2639
  49. package/src/agent-progress.js +79 -0
  50. package/src/agent-turn-mark.js +30 -0
  51. package/src/analytics.js +1 -19
  52. package/src/app-context.js +5 -0
  53. package/src/app-stage.jsx +2 -34
  54. package/src/asset-pane.jsx +6 -5
  55. package/src/camera-block.js +21 -0
  56. package/src/camera-direction.js +120 -0
  57. package/src/character-kind.js +3 -9
  58. package/src/command-bus.js +8 -6
  59. package/src/commands/cast.js +5 -34
  60. package/src/commands/elements/character.js +1 -1
  61. package/src/commands/elements/motion.js +1 -6
  62. package/src/commands/elements/object.js +6 -2
  63. package/src/commands/elements/shot.js +4 -1
  64. package/src/commands/elements.js +38 -14
  65. package/src/commands/index.js +3 -1
  66. package/src/commands/kits.js +37 -0
  67. package/src/commands/motion-timing.js +57 -0
  68. package/src/commands/motion.js +9 -152
  69. package/src/commands/motion3-compile.js +65 -0
  70. package/src/commands/motion3.js +207 -0
  71. package/src/commands/objects.js +6 -4
  72. package/src/commands/shot.js +49 -11
  73. package/src/commands/stage.js +14 -1
  74. package/src/commands/view.js +4 -1
  75. package/src/document-mirrors.js +42 -0
  76. package/src/document-store.js +4 -0
  77. package/src/domains/cast.js +19 -194
  78. package/src/domains/motion/motion-range-pin-rules.js +1 -1
  79. package/src/domains/motion/take-source.js +51 -0
  80. package/src/domains/motion.js +60 -1838
  81. package/src/domains/scenes.js +12 -55
  82. package/src/domains/shots.js +2 -2
  83. package/src/domains/stage.js +4 -2
  84. package/src/draft-ghost.js +46 -0
  85. package/src/draft-ghost.jsx +129 -0
  86. package/src/dualview.jsx +4 -3
  87. package/src/effects/EffectsLayer.jsx +112 -0
  88. package/src/effects/effect-meshes.js +232 -0
  89. package/src/effects/effect-record.js +103 -0
  90. package/src/effects/emitter.js +59 -0
  91. package/src/effects/simulate.js +261 -0
  92. package/src/effects/state.js +59 -0
  93. package/src/execution-telemetry.js +2 -3
  94. package/src/first-success-guide.jsx +5 -5
  95. package/src/ghost-set.js +257 -0
  96. package/src/held-object-edit.jsx +113 -0
  97. package/src/held-object-pose.js +175 -0
  98. package/src/hierarchy-model.js +1 -1
  99. package/src/hierarchy-panel.jsx +1 -1
  100. package/src/imported-mesh.jsx +103 -0
  101. package/src/kits/held.js +84 -0
  102. package/src/kits/index.js +70 -0
  103. package/src/kits/smoking.js +57 -0
  104. package/src/light-blockers.js +93 -0
  105. package/src/motion/beat-board-model.js +84 -0
  106. package/src/motion/beat-board.css +124 -0
  107. package/src/motion/beat-board.jsx +59 -0
  108. package/src/motion/center-of-mass.js +66 -0
  109. package/src/motion/fix-collisions.js +850 -0
  110. package/src/motion/physics-review.js +118 -0
  111. package/src/{ardy → motion}/physics-support.js +1 -76
  112. package/src/motion/rig-snapshot.js +22 -0
  113. package/src/motion/take-segments.jsx +140 -0
  114. package/src/{ardy → motion}/timeline-coordinates.js +0 -21
  115. package/src/{ardy → motion}/timeline.css +1 -2
  116. package/src/{ardy → motion}/timeline.jsx +48 -419
  117. package/src/motion/waypoints.js +76 -0
  118. package/src/motion3/beats.js +83 -0
  119. package/src/motion3/body.js +158 -0
  120. package/src/motion3/bones.js +47 -0
  121. package/src/motion3/carried.js +47 -0
  122. package/src/motion3/compile-job.js +75 -0
  123. package/src/motion3/compile-worker.js +10 -0
  124. package/src/motion3/compiler/anchors.js +105 -0
  125. package/src/motion3/compiler/arm-orient.js +128 -0
  126. package/src/motion3/compiler/channels.js +125 -0
  127. package/src/motion3/compiler/compile.js +205 -0
  128. package/src/motion3/compiler/gait-rhythm.js +97 -0
  129. package/src/motion3/compiler/grip.js +171 -0
  130. package/src/motion3/compiler/head-steady.js +48 -0
  131. package/src/motion3/compiler/hold.js +134 -0
  132. package/src/motion3/compiler/idle.js +113 -0
  133. package/src/motion3/compiler/limbs.js +170 -0
  134. package/src/motion3/compiler/math.js +72 -0
  135. package/src/motion3/compiler/pose.js +162 -0
  136. package/src/motion3/compiler/quat.js +49 -0
  137. package/src/motion3/compiler/skeleton.js +74 -0
  138. package/src/motion3/compiler/validate-common.js +32 -0
  139. package/src/motion3/compiler/validate-goals.js +118 -0
  140. package/src/motion3/compiler/validate-objects.js +143 -0
  141. package/src/motion3/compiler/validate-parts.js +116 -0
  142. package/src/motion3/compiler/validate.js +122 -0
  143. package/src/motion3/compiler/vocab.js +73 -0
  144. package/src/motion3/compiler/walk-pelvis.js +275 -0
  145. package/src/motion3/compiler/walk.js +207 -0
  146. package/src/motion3/compiler/wrist-clamp.js +32 -0
  147. package/src/motion3/delta.js +47 -0
  148. package/src/motion3/describe.js +174 -0
  149. package/src/motion3/docs.js +27 -0
  150. package/src/motion3/edit-carry.js +21 -0
  151. package/src/motion3/fix/ease.js +16 -0
  152. package/src/motion3/fix/ground.js +37 -0
  153. package/src/motion3/fix/index.js +52 -0
  154. package/src/motion3/fix/push.js +71 -0
  155. package/src/motion3/fix/reach.js +46 -0
  156. package/src/motion3/fix/retime.js +79 -0
  157. package/src/motion3/fix/self.js +211 -0
  158. package/src/motion3/fix/slide.js +70 -0
  159. package/src/motion3/fix/turn.js +51 -0
  160. package/src/motion3/follow.js +39 -0
  161. package/src/motion3/free-motion.js +173 -0
  162. package/src/motion3/grip-choice.js +130 -0
  163. package/src/motion3/hold-plan.js +157 -0
  164. package/src/motion3/json-patch.js +77 -0
  165. package/src/motion3/key-aim.js +105 -0
  166. package/src/motion3/key-edit.js +82 -0
  167. package/src/motion3/lenient.js +127 -0
  168. package/src/motion3/object-keys.js +236 -0
  169. package/src/motion3/object-pose.js +182 -0
  170. package/src/motion3/object-tracks.js +12 -0
  171. package/src/motion3/pipeline.js +102 -0
  172. package/src/motion3/playback.js +51 -0
  173. package/src/motion3/probes.js +91 -0
  174. package/src/motion3/qa/body-checks.js +34 -0
  175. package/src/motion3/qa/check-names.js +14 -0
  176. package/src/motion3/qa/checks.js +121 -0
  177. package/src/motion3/qa/contact-checks.js +51 -0
  178. package/src/motion3/qa/explain.js +80 -0
  179. package/src/motion3/qa/fmt.js +5 -0
  180. package/src/motion3/qa/foot-checks.js +72 -0
  181. package/src/motion3/qa/geometry.js +77 -0
  182. package/src/motion3/qa/goals.js +191 -0
  183. package/src/motion3/qa/ground.js +60 -0
  184. package/src/motion3/qa/hints-contact.js +179 -0
  185. package/src/motion3/qa/hints-goal.js +160 -0
  186. package/src/motion3/qa/hints-knee.js +24 -0
  187. package/src/motion3/qa/hints-motion.js +209 -0
  188. package/src/motion3/qa/hints-object.js +91 -0
  189. package/src/motion3/qa/index.js +42 -0
  190. package/src/motion3/qa/key-checks.js +82 -0
  191. package/src/motion3/qa/near.js +96 -0
  192. package/src/motion3/qa/object-checks.js +135 -0
  193. package/src/motion3/qa/patch.js +161 -0
  194. package/src/motion3/qa/severity.js +38 -0
  195. package/src/motion3/qa/spec-checks.js +65 -0
  196. package/src/motion3/qa/style.js +218 -0
  197. package/src/motion3/qa/wrist-checks.js +69 -0
  198. package/src/motion3/qa/wrist-obs.js +60 -0
  199. package/src/motion3/refs.js +120 -0
  200. package/src/motion3/repair-tune.js +121 -0
  201. package/src/motion3/repair.js +83 -0
  202. package/src/motion3/report.js +67 -0
  203. package/src/motion3/rigs/index.js +14 -0
  204. package/src/motion3/rigs/x-bot-tpose-body.json +1 -0
  205. package/src/motion3/rigs/x-bot-tpose.json +1 -0
  206. package/src/motion3/rigs/y-bot-tpose-body.json +1 -0
  207. package/src/motion3/rigs/y-bot-tpose.json +1 -0
  208. package/src/motion3/scene.js +60 -0
  209. package/src/motion3/schema/brief.js +89 -0
  210. package/src/motion3/schema/fields.js +44 -0
  211. package/src/motion3/schema/spec-examples.js +46 -0
  212. package/src/motion3/schema/topics.js +111 -0
  213. package/src/motion3/schema/types.js +147 -0
  214. package/src/motion3/search.js +117 -0
  215. package/src/motion3/second-hand.js +112 -0
  216. package/src/motion3/skills/basic.js +82 -0
  217. package/src/motion3/skills/book.js +76 -0
  218. package/src/motion3/skills/car.js +145 -0
  219. package/src/motion3/skills/frames-auto.js +29 -0
  220. package/src/motion3/skills/index.js +83 -0
  221. package/src/motion3/skills/info.js +65 -0
  222. package/src/motion3/skills/smoke-kit.js +109 -0
  223. package/src/motion3/skills/smoke.js +160 -0
  224. package/src/motion3/spec-store.js +59 -0
  225. package/src/motion3/take-edit.js +155 -0
  226. package/src/motion3/take.js +63 -0
  227. package/src/motion3/timeline.js +127 -0
  228. package/src/motion3/views.js +121 -0
  229. package/src/motion3/walk-plan.js +119 -0
  230. package/src/object-catalog.jsx +7 -1
  231. package/src/object-colors.js +98 -0
  232. package/src/object-gizmo-parts.js +115 -0
  233. package/src/object-gizmo.jsx +37 -122
  234. package/src/object-glow.js +139 -0
  235. package/src/object-hidden.js +33 -0
  236. package/src/object-look.js +196 -0
  237. package/src/object-size.js +10 -0
  238. package/src/object-travel.js +71 -6
  239. package/src/panels/CameraPanel.jsx +7 -7
  240. package/src/panels/HdrEnvironmentControls.jsx +59 -0
  241. package/src/panels/LightPanel.jsx +25 -4
  242. package/src/panels/ObjectLookPanel.jsx +88 -0
  243. package/src/panels/PoseImagePanel.jsx +1 -1
  244. package/src/panels/PosePanel.jsx +2 -17
  245. package/src/panels/RigControlPanel.jsx +5 -53
  246. package/src/panels/SubjectBox.jsx +4 -1
  247. package/src/panels/details.css +14 -0
  248. package/src/posestudio.jsx +4 -27
  249. package/src/project-browser.jsx +3 -4
  250. package/src/project-resources.js +18 -31
  251. package/src/project.js +13 -104
  252. package/src/props.jsx +25 -280
  253. package/src/rail-dot-drag.js +23 -0
  254. package/src/render-passes.js +10 -0
  255. package/src/resource-status.jsx +1 -0
  256. package/src/result-modal.jsx +4 -19
  257. package/src/room.jsx +20 -12
  258. package/src/scene-assets.js +8 -6
  259. package/src/scene-light-place.js +72 -0
  260. package/src/scene-lights.jsx +148 -0
  261. package/src/scene-objects.js +31 -139
  262. package/src/scenes.js +15 -105
  263. package/src/semantic-edit.js +2 -5
  264. package/src/set-pieces.jsx +183 -0
  265. package/src/settings-menu.jsx +3 -11
  266. package/src/shell/BottomDock.jsx +11 -53
  267. package/src/shell/CompositionGuideButton.jsx +20 -0
  268. package/src/shell/DetailsSlot.jsx +16 -200
  269. package/src/shell/GhostsMenuGroup.jsx +74 -0
  270. package/src/shell/LibrarySlot.jsx +1 -3
  271. package/src/shell/MenuBar.jsx +3 -3
  272. package/src/shell/PreferencesDialog.jsx +15 -264
  273. package/src/shell/PreferencesSlot.jsx +2 -8
  274. package/src/shell/ShotPreviewHeader.jsx +17 -0
  275. package/src/shell/TopBar.jsx +5 -83
  276. package/src/shell/ViewportToolbar.jsx +23 -93
  277. package/src/shell/agent-glass.css +19 -6
  278. package/src/shell/details-selection.js +42 -0
  279. package/src/shell/ghosts-menu.css +22 -0
  280. package/src/shell/glass-regions.css +29 -3
  281. package/src/shell/preferences.css +6 -1
  282. package/src/shell/viewport.css +12 -0
  283. package/src/shot-authoring.js +4 -7
  284. package/src/soft-shadows.js +37 -0
  285. package/src/stage-attach-frames.js +12 -12
  286. package/src/stage-canvas-rigs.jsx +3 -3
  287. package/src/stage-capture.jsx +42 -8
  288. package/src/stage-characters.jsx +22 -216
  289. package/src/stage-editor-gizmos.jsx +6 -6
  290. package/src/stage-environment.js +93 -0
  291. package/src/stage-hdr-builtins.js +107 -0
  292. package/src/stage-hdr-image.js +116 -0
  293. package/src/stage-hdr-library.js +70 -0
  294. package/src/stage-hdr-map.js +94 -0
  295. package/src/stage-hdr.js +130 -0
  296. package/src/stage-lighting.js +149 -0
  297. package/src/stage-occlusion.js +210 -0
  298. package/src/stage-sky.jsx +159 -0
  299. package/src/stage-startup-defaults.js +5 -9
  300. package/src/studio-actions.js +12 -14
  301. package/src/studio-agent-commands.js +46 -16
  302. package/src/studio-agent-motion.js +9 -9
  303. package/src/studio-agent-protocol.js +46 -73
  304. package/src/studio-app-binding.js +29 -45
  305. package/src/studio-asset-list.js +17 -0
  306. package/src/studio-contact-sheet.js +37 -5
  307. package/src/studio-elements.js +27 -7
  308. package/src/studio-entity-bounds.js +18 -0
  309. package/src/studio-error-codes.js +8 -0
  310. package/src/studio-export-framing.js +152 -0
  311. package/src/studio-lighting-check.js +103 -0
  312. package/src/studio-motion-visual.js +150 -0
  313. package/src/studio-schema-errors.js +80 -0
  314. package/src/studio-support-gap.js +74 -0
  315. package/src/styles.css +1 -19
  316. package/src/timeline-defaults.js +5 -0
  317. package/src/timeline-extent.js +8 -20
  318. package/src/tutorial-walk.js +17 -0
  319. package/src/ui-scrub.js +1 -1
  320. package/src/use-shot-camera-tracks.js +12 -0
  321. package/src/view-ghosts.js +97 -0
  322. package/src/view-ghosts.jsx +49 -0
  323. package/src/workflow/AgentPanel.jsx +47 -166
  324. package/src/workflow/AgentProgressCard.jsx +107 -0
  325. package/src/workflow/AgentReplyText.jsx +37 -0
  326. package/src/workflow/AgentTurnSteps.jsx +84 -0
  327. package/src/workflow/LoginPanel.jsx +140 -0
  328. package/src/workflow/WorkflowBuilder.jsx +7 -23
  329. package/src/workflow/agent-client.js +54 -51
  330. package/src/workflow/agent-panel.css +71 -1
  331. package/src/workflow/agent-progress-card.css +167 -0
  332. package/src/workflow/agent-progress-view.js +47 -0
  333. package/src/workflow/agent-turn-steps.css +113 -0
  334. package/src/workflow/agent-turn.js +191 -0
  335. package/src/workflow/login-client.js +34 -0
  336. package/src/workflow/motion-input.js +18 -31
  337. package/src/workflow/node-schema.js +1 -1
  338. package/src/workflow/reply-markdown.js +69 -0
  339. package/src/workflow/scene-asset-sync.js +8 -8
  340. package/tools/agent-session-stats.mjs +61 -0
  341. package/tools/dev-full.mjs +5 -56
  342. package/tools/eslint-baseline.json +12 -13
  343. package/tools/headless-studio.mjs +85 -0
  344. package/tools/knip-baseline.json +0 -179
  345. package/tools/loc-baseline.json +33 -83
  346. package/tools/motion3/extract-body.mjs +103 -0
  347. package/tools/motion3/extract-rig.mjs +85 -0
  348. package/tools/process-supervisor.mjs +0 -88
  349. package/tools/run-tests.mjs +9 -124
  350. package/bin/agent/motion-runtime.mjs +0 -76
  351. package/dist/ardy/cskel27-rest.json +0 -557
  352. package/dist/assets/analytics-B41FmPYI.js +0 -1
  353. package/dist/assets/app-Do-Og5Pu.css +0 -1
  354. package/dist/assets/app-DwP-tl2x.js +0 -4920
  355. package/dist/assets/config-Bx7pYsD9.js +0 -1
  356. package/dist/assets/config-DeoFTYzb.css +0 -1
  357. package/dist/assets/demo-Dt0qZfS7.js +0 -1
  358. package/dist/assets/scene-asset-sync-BCWDJB1d.js +0 -19
  359. package/dist/assets/scene-asset-sync-DjJ_cJ2R.css +0 -1
  360. package/dist/assets/ticket-jqMhqL88.js +0 -1
  361. package/dist/assets/workflow-Br-K7Srs.js +0 -179
  362. package/dist/demo/walk-then-stop.npz +0 -0
  363. package/mcp/ardy-prompts.mjs +0 -218
  364. package/src/ardy/auto-fix-panel.css +0 -108
  365. package/src/ardy/auto-physics.js +0 -1194
  366. package/src/ardy/client.js +0 -153
  367. package/src/ardy/convert.js +0 -313
  368. package/src/ardy/cskel27-neutral.js +0 -39
  369. package/src/ardy/cskel27.js +0 -77
  370. package/src/ardy/export.js +0 -165
  371. package/src/ardy/fix-collisions.js +0 -2257
  372. package/src/ardy/foot-lock.js +0 -230
  373. package/src/ardy/motion-calibration.js +0 -118
  374. package/src/ardy/motion-edit.js +0 -213
  375. package/src/ardy/motion-url.js +0 -55
  376. package/src/ardy/mp4-duration.js +0 -102
  377. package/src/ardy/npz.js +0 -595
  378. package/src/ardy/physics-panel.css +0 -21
  379. package/src/ardy/physics-panel.jsx +0 -89
  380. package/src/ardy/physics-refine.js +0 -111
  381. package/src/ardy/physics-review.js +0 -567
  382. package/src/ardy/physics-temporal.js +0 -54
  383. package/src/ardy/platform-fit-panel.css +0 -64
  384. package/src/ardy/platform-fit-panel.jsx +0 -41
  385. package/src/ardy/platform-fit.js +0 -326
  386. package/src/ardy/playback.js +0 -711
  387. package/src/ardy/pose-pin.js +0 -97
  388. package/src/ardy/prompt-clips.js +0 -30
  389. package/src/ardy/retime.js +0 -211
  390. package/src/ardy/root-drop.js +0 -369
  391. package/src/ardy/timeline-resize.js +0 -13
  392. package/src/ardy/to-cskel27.js +0 -225
  393. package/src/ardy/trim.js +0 -33
  394. package/src/ardy/waypoints.js +0 -369
  395. package/src/ardy-bridge-contract.js +0 -281
  396. package/src/domains/motion/motion-video-capture.js +0 -664
  397. package/src/i2v-motion-client.js +0 -77
  398. package/src/i2v-motion-studio.jsx +0 -180
  399. package/src/line-edit.js +0 -1348
  400. package/src/motion/generation.js +0 -105
  401. package/src/motion-readiness-ui.jsx +0 -67
  402. package/src/motion-readiness.js +0 -40
  403. package/src/motion-resources.js +0 -177
  404. package/src/motion-store.js +0 -65
  405. package/src/motion-trail.js +0 -601
  406. package/src/multimodel-ingest.js +0 -394
  407. package/src/panels/PromptBlocksPanel.jsx +0 -444
  408. package/src/panels/VideoCapturePanel.jsx +0 -181
  409. package/src/pose-extract/browser.js +0 -42
  410. package/src/pose-extract/detector.js +0 -100
  411. package/src/pose-extract/fit.js +0 -227
  412. package/src/pose-extract/image-frame.js +0 -51
  413. package/src/pose-extract/index.js +0 -58
  414. package/src/pose-extract/keyframes.js +0 -74
  415. package/src/pose-extract/landmarks.js +0 -162
  416. package/src/pose-extract/mirror.js +0 -153
  417. package/src/pose-extract/take.js +0 -131
  418. package/src/pose-extract/video-frames.js +0 -91
  419. package/src/take-recipe.js +0 -165
  420. package/src/trail-key-conflicts.js +0 -39
  421. package/src/trail-pick.js +0 -59
  422. package/src/use-i2v-capture-modal.js +0 -15
  423. package/tools/ardy/BRIDGE.md +0 -37
  424. package/tools/ardy/GVHMR-NPZ.md +0 -68
  425. package/tools/ardy/README.md +0 -124
  426. package/tools/ardy/__pycache__/cclay_gvhmr_worker.cpython-313.pyc +0 -0
  427. package/tools/ardy/artifacts.mjs +0 -34
  428. package/tools/ardy/bridge.mjs +0 -1732
  429. package/tools/ardy/bvh-cskel27.mjs +0 -1265
  430. package/tools/ardy/cclay_constrained_generate.py +0 -561
  431. package/tools/ardy/cclay_gvhmr_worker.py +0 -264
  432. package/tools/ardy/cclay_motion_edit.py +0 -510
  433. package/tools/ardy/cclay_sequence_generate.py +0 -705
  434. package/tools/ardy/cclay_worker.py +0 -304
  435. package/tools/ardy/dump-npz.py +0 -205
  436. package/tools/ardy/extract-rest.mjs +0 -299
  437. package/tools/ardy/extract.mjs +0 -507
  438. package/tools/ardy/footage.mjs +0 -467
  439. package/tools/ardy/gvhmr-floor.mjs +0 -102
  440. package/tools/ardy/gvhmr-to-cskel27.mjs +0 -21
  441. package/tools/ardy/gvhmr_fastpath.py +0 -168
  442. package/tools/ardy/gvhmr_trajectory.py +0 -240
  443. package/tools/ardy/merge-text-encoder.py +0 -160
  444. package/tools/ardy/mocap-metrics.mjs +0 -200
  445. package/tools/ardy/mocap-quality-gate.mjs +0 -110
  446. package/tools/ardy/motion-stabilize.mjs +0 -348
  447. package/tools/ardy/npz.mjs +0 -414
  448. package/tools/ardy/pose-to-npz.mjs +0 -106
  449. package/tools/ardy/prompt-limits.mjs +0 -5
  450. package/tools/ardy/run-edit-on-box.sh +0 -73
  451. package/tools/ardy/run-local.mjs +0 -233
  452. package/tools/ardy/run-on-box.sh +0 -593
  453. package/tools/ardy/run-sequence-on-box.sh +0 -177
  454. package/tools/ardy/runners/gvhmr-worker.mjs +0 -206
  455. package/tools/ardy/runners/index.mjs +0 -18
  456. package/tools/ardy/runners/local.mjs +0 -455
  457. package/tools/ardy/runners/proc.mjs +0 -125
  458. package/tools/ardy/runners/remote.mjs +0 -252
  459. package/tools/ardy/setup-local.mjs +0 -189
  460. package/tools/ardy/setup-text-encoder-on-box.sh +0 -93
  461. package/tools/ardy/setup-text-encoder.py +0 -306
  462. package/tools/ardy/smpl-cskel27.mjs +0 -178
  463. package/tools/ardy/visual-qa.mjs +0 -196
  464. package/tools/ardy/vq-car.mjs +0 -52
  465. package/tools/bench/EXP3.md +0 -61
  466. package/tools/bench/cclay_bench_extract_incam.py +0 -125
  467. package/tools/bench/cclay_bench_extract_obs.py +0 -204
  468. package/tools/bench/cclay_bench_runner.py +0 -42
  469. package/tools/bench/cube-contact.mjs +0 -162
  470. package/tools/bench/exp3.mjs +0 -152
  471. package/tools/bench/extract-bench-lib.mjs +0 -216
  472. package/tools/bench/extract-bench.mjs +0 -302
  473. package/tools/bench/fal-generate.mjs +0 -88
  474. package/tools/bench/fit/README.md +0 -189
  475. package/tools/bench/fit/camera.mjs +0 -59
  476. package/tools/bench/fit/contact.mjs +0 -419
  477. package/tools/bench/fit/footlock.mjs +0 -234
  478. package/tools/bench/fit/motion.mjs +0 -85
  479. package/tools/bench/fit/pin.mjs +0 -27
  480. package/tools/bench/fit/remote.mjs +0 -105
  481. package/tools/bench/fit-bench.mjs +0 -126
  482. package/tools/bench/fit-sanity.mjs +0 -45
  483. package/tools/bench/metrics.mjs +0 -313
  484. package/tools/bench/obs/depth.mjs +0 -258
  485. package/tools/bench/obs/extrinsics.mjs +0 -175
  486. package/tools/bench/obs/fit_mannequin_betas.py +0 -340
  487. package/tools/bench/obs/ground.mjs +0 -294
  488. package/tools/bench/obs/heading.mjs +0 -151
  489. package/tools/bench/obs/ladder.mjs +0 -555
  490. package/tools/bench/obs/mannequin-betas.json +0 -124
  491. package/tools/bench/obs/remote.mjs +0 -83
  492. package/tools/bench/obs/rest_joints.py +0 -52
  493. package/tools/bench/obs/ybot-targets.mjs +0 -49
  494. package/tools/bench/obs-bench.mjs +0 -520
  495. package/tools/bench/score.mjs +0 -343
  496. package/tools/bench/summarize.mjs +0 -86
  497. package/tools/demo-worker/README.md +0 -137
  498. package/tools/demo-worker/api-client.mjs +0 -233
  499. package/tools/demo-worker/generate.mjs +0 -301
  500. package/tools/demo-worker/index.mjs +0 -466
  501. package/tools/demo-worker/sign-core.mjs +0 -112
  502. package/tools/demo-worker/sign.mjs +0 -161
  503. package/tools/gt-render/browser.mjs +0 -159
  504. package/tools/gt-render/camera-math.mjs +0 -276
  505. package/tools/gt-render/page.mjs +0 -267
  506. package/tools/gt-render/render.mjs +0 -514
  507. package/tools/gt-render/scene-box.mjs +0 -31
  508. package/tools/gt-render/take-transform.mjs +0 -87
  509. package/tools/kimodo/compare-seam.mjs +0 -108
  510. package/tools/kimodo/constraints.mjs +0 -244
  511. package/tools/kimodo/edit.mjs +0 -259
  512. package/tools/kimodo/effector-constraints.mjs +0 -200
  513. package/tools/kimodo/generate.mjs +0 -646
  514. package/tools/kimodo/local-output.mjs +0 -265
  515. package/tools/kimodo/measure-pose.mjs +0 -65
  516. package/tools/kimodo/measure-preserve.mjs +0 -899
  517. package/tools/kimodo/measure-waypoints.mjs +0 -55
  518. package/tools/kimodo/mlx-generate.py +0 -111
  519. package/tools/kimodo/pose-constraints.mjs +0 -248
  520. package/tools/kimodo/preserve-mask.mjs +0 -543
  521. package/tools/kimodo/read-npz.mjs +0 -189
  522. package/tools/kimodo/run-edit-on-box.mjs +0 -155
  523. package/tools/kimodo/run-sequence-on-box.mjs +0 -178
  524. package/tools/kimodo/runner.mjs +0 -284
  525. package/tools/kimodo/seam-batch.mjs +0 -77
  526. package/tools/kimodo/setup-local.mjs +0 -30
  527. package/tools/kimodo/setup-on-box.sh +0 -339
  528. package/tools/kimodo/soma77-to-cskel27.mjs +0 -305
  529. package/tools/morphgs/assets/gen_truth.py +0 -39
  530. package/tools/morphgs/assets/mesh_ori_rig.txt +0 -27
  531. package/tools/morphgs/assets/playback-check.mjs +0 -30
  532. package/tools/morphgs/demo-gate.sh +0 -26
  533. package/tools/morphgs/fbx2morphgs.mjs +0 -68
  534. package/tools/morphgs/morphgs-to-cskel27.mjs +0 -105
  535. package/tools/morphgs/patches/preprocess_src-none-mode.patch +0 -76
  536. package/tools/morphgs/setup-on-cluster.sh +0 -126
  537. package/tools/projflow/driver.py +0 -1069
  538. package/tools/projflow/generate.mjs +0 -805
  539. package/tools/projflow/hml22-to-cskel27.mjs +0 -735
  540. package/tools/projflow/line-edit-job.mjs +0 -547
  541. package/tools/projflow/replay.mjs +0 -452
  542. package/tools/projflow/runner.mjs +0 -269
  543. package/tools/projflow/service.mjs +0 -742
  544. package/tools/track/DESIGN.md +0 -513
  545. package/tools/track/backfill-provenance.mjs +0 -116
  546. package/tools/track/budget.mjs +0 -8
  547. package/tools/track/check-rig.mjs +0 -209
  548. package/tools/track/diagnostics.schema.json +0 -60
  549. package/tools/track/export-rig.mjs +0 -256
  550. package/tools/track/fallback.mjs +0 -7
  551. package/tools/track/fk-parity-fixture.mjs +0 -161
  552. package/tools/track/gate.mjs +0 -349
  553. package/tools/track/masks.mjs +0 -318
  554. package/tools/track/metrics.mjs +0 -201
  555. package/tools/track/publish-obs.mjs +0 -108
  556. package/tools/track/py/check_env.py +0 -61
  557. package/tools/track/py/eval_lr.py +0 -229
  558. package/tools/track/py/lr_viterbi.py +0 -294
  559. package/tools/track/py/masks.py +0 -405
  560. package/tools/track/py/objective.py +0 -382
  561. package/tools/track/py/rig.py +0 -508
  562. package/tools/track/py/scene.py +0 -326
  563. package/tools/track/py/test_joint_indices.py +0 -123
  564. package/tools/track/py/test_lr_viterbi.py +0 -195
  565. package/tools/track/py/test_masks.py +0 -229
  566. package/tools/track/py/test_rig.py +0 -288
  567. package/tools/track/py/test_scene.py +0 -354
  568. package/tools/track/py/test_track.py +0 -328
  569. package/tools/track/py/track.py +0 -411
  570. package/tools/track/remote.mjs +0 -140
  571. package/tools/track/rig-dump.mjs +0 -227
  572. package/tools/track/run-box-tests.mjs +0 -14
  573. package/tools/track/run-box.mjs +0 -168
  574. package/tools/track/setup-box.sh +0 -59
  575. package/tools/track/study-2d.mjs +0 -728
  576. /package/src/{ardy → motion}/collision-blockers.js +0 -0
  577. /package/src/{ardy → motion}/ground.js +0 -0
  578. /package/src/{ardy → motion}/ik-drag.js +0 -0
  579. /package/src/{ardy → motion}/ik-key-json.js +0 -0
  580. /package/src/{ardy → motion}/ik.js +0 -0
  581. /package/src/{ardy → motion}/physics-surface.js +0 -0
  582. /package/src/{ardy → motion}/playback-clock.js +0 -0
  583. /package/src/{ardy → motion}/range-pin.js +0 -0
@@ -0,0 +1,118 @@
1
+ import * as THREE from "three";
2
+ import { createSurfaceSampler } from "./physics-surface.js";
3
+
4
+ // Contact measurement for motion verification: support sites, inferred
5
+ // contact spans and the floor/slide/float metrics. It never edits a take.
6
+ export const SUPPORT_SITES = [
7
+ { id: "leftFoot", label: "Left foot", ko: "왼발", bone: "LeftFoot", chain: "leftFoot", kind: "foot", match: /Left(Foot|Toe)/ },
8
+ { id: "rightFoot", label: "Right foot", ko: "오른발", bone: "RightFoot", chain: "rightFoot", kind: "foot", match: /Right(Foot|Toe)/ },
9
+ { id: "leftHand", label: "Left hand", ko: "왼손", bone: "LeftHand", chain: "leftHand", kind: "hand", match: /LeftHand/ },
10
+ { id: "rightHand", label: "Right hand", ko: "오른손", bone: "RightHand", chain: "rightHand", kind: "hand", match: /RightHand/ },
11
+ { id: "leftKnee", label: "Left knee", ko: "왼무릎", bone: "LeftLeg", chain: "leftFoot", kind: "knee", match: /Left(UpLeg|Leg)$/ },
12
+ { id: "rightKnee", label: "Right knee", ko: "오른무릎", bone: "RightLeg", chain: "rightFoot", kind: "knee", match: /Right(UpLeg|Leg)$/ },
13
+ ];
14
+ export const PHYSICS_LIMITS = Object.freeze({ root: 0.18, pull: 0.10, tilt: Math.PI / 60, floor: 0.005, slide: 0.015, float: 0.025 });
15
+ const quantile = (a, t) => { const s = a.filter(Number.isFinite).sort((x, y) => x - y); return s[Math.floor((s.length - 1) * t)] ?? 0; };
16
+
17
+ export function copyPhysicsKeys(keys) {
18
+ return new Map([...keys].map(([f, entries]) => [f, new Map([...entries].map(([id, k]) => [id, {
19
+ ...k, q: k.q?.map((q) => q.clone()) ?? null, p: k.p?.clone() ?? null,
20
+ ...(k.baseQ ? { baseQ: k.baseQ.map((q) => q.clone()) } : {}),
21
+ ...(k.basePos ? { basePos: k.basePos.clone() } : {}),
22
+ ...(k.chainP ? { chainP: k.chainP.map((p) => p.clone()) } : {}),
23
+ ...(k.correctionRange ? { correctionRange: { ...k.correctionRange } } : {}),
24
+ }]))]));
25
+ }
26
+ export function physicsKeyStamp(keys) {
27
+ return JSON.stringify([...keys].sort((a, b) => a[0] - b[0]).map(([f, e]) => [f, [...e].sort(([a], [b]) => a.localeCompare(b)).map(([id, k]) => [id, k.p?.toArray(), k.q?.map((q) => q.toArray()), k.basePos?.toArray(), k.baseQ?.map((q) => q.toArray()), k.keepTranslations, k.chainP?.map((p) => p.toArray()), k.correctionRange])]));
28
+ }
29
+
30
+ /** Measured skin, shared exact transforms; never a cached bind-pose sole. */
31
+ export function createSupportSampler(rig) { return createSurfaceSampler(rig, SUPPORT_SITES); }
32
+
33
+ function sitePoint(row, site) {
34
+ return site.kind === "foot" ? (row.toes?.[site.id] ?? row.support[site.id]?.position) : row.support[site.id]?.position;
35
+ }
36
+
37
+ export function supportIntervals(samples, fps, overrides = [], floorY = 0, groundAt = null) {
38
+ const spans = [], rejected = [], count = samples.length;
39
+ const masks = Array.from({ length: count }, () => new Map());
40
+ for (const site of SUPPORT_SITES) {
41
+ const floor = quantile(samples.map((r) => r.support[site.id]?.floor), 0.08);
42
+ const flags = samples.map((r, i) => {
43
+ const a = samples[Math.max(0, i - 2)].support[site.id], b = samples[Math.min(count - 1, i + 2)].support[site.id], c = r.support[site.id];
44
+ const pa = sitePoint(samples[Math.max(0, i - 2)], site), pb = sitePoint(samples[Math.min(count - 1, i + 2)], site);
45
+ if (!a || !b || !c || !pa || !pb) return false;
46
+ const dt = (Math.min(count - 1, i + 2) - Math.max(0, i - 2)) / fps || 1 / fps;
47
+ const localGround = r.ground?.[site.id] ?? (groundAt ? groundAt((sitePoint(r, site) ?? c.position).x, (sitePoint(r, site) ?? c.position).z, c.position.y + 0.001) : floorY);
48
+ return c.floor < localGround + (site.kind === "foot" ? 0.15 : 0.07)
49
+ && c.floor < floor + 0.055 && Math.abs(b.position.y - a.position.y) / dt < 0.25
50
+ && Math.hypot(pb.x - pa.x, pb.z - pa.z) / dt < 0.16;
51
+ });
52
+ const forced = new Set();
53
+ for (const edit of overrides.filter((o) => o.site === site.id)) {
54
+ for (let f = Math.max(0, edit.start); f <= Math.min(count - 1, edit.end); f += 1) {
55
+ flags[f] = edit.mode === "plant";
56
+ if (flags[f]) forced.add(f); else forced.delete(f);
57
+ }
58
+ }
59
+ let start = -1;
60
+ for (let f = 0; f <= count; f += 1) {
61
+ if (flags[f] && start < 0) start = f;
62
+ if ((f === count || !flags[f]) && start >= 0) {
63
+ const manual = [...forced].some((i) => i >= start && i < f);
64
+ if (f - start >= Math.max(3, Math.round(fps * 0.12)) || manual) {
65
+ const rows = samples.slice(start, f).map((r) => sitePoint(r, site));
66
+ const anchor = new THREE.Vector3(quantile(rows.map((r) => r.x), 0.5), 0, quantile(rows.map((r) => r.z), 0.5));
67
+ const wander = Math.max(...rows.map((r) => Math.hypot(r.x - anchor.x, r.z - anchor.z)));
68
+ if (site.kind === "foot") anchor.y = groundAt ? groundAt(anchor.x, anchor.z, quantile(rows.map((r) => r.y), 0.5) + 0.001) : quantile(rows.map((r) => r.y), 0.5);
69
+ const span = { id: `${site.id}:${start}`, site: site.id, start, end: f - 1, anchor, manual, wander };
70
+ if (wander <= PHYSICS_LIMITS.pull || manual) {
71
+ spans.push(span);
72
+ for (let i = start; i < f; i += 1) masks[i].set(site.id, span);
73
+ } else rejected.push({ ...span, reason: "moving-contact" });
74
+ }
75
+ start = -1;
76
+ }
77
+ }
78
+ }
79
+ return { spans, masks, rejected };
80
+ }
81
+
82
+ export function physicsMetrics(samples, masks, fps, floorY = 0, groundAt = null) {
83
+ const m = { penetration: 0, penetrationFrame: 0, float: 0, floatFrame: 0, slide: 0, slideFrame: 0, meanSlide: 0, kneeStep: 0, kneeStepFrame: 0, kneeAcceleration: 0, rootAcceleration: 0, count: 0, surfaceMeasured: samples.every((s) => SUPPORT_SITES.slice(0, 2).every((site) => s.support[site.id]?.vertices > 0)) };
84
+ for (let f = 0; f < samples.length; f += 1) {
85
+ for (const [id, point] of Object.entries(samples[f].support)) if (point.vertices > 0) { const g = samples[f].ground?.[id] ?? floorY; if (g - point.floor > m.penetration) { m.penetration = g - point.floor; m.penetrationFrame = f; } }
86
+ for (const site of SUPPORT_SITES) {
87
+ const r = samples[f].support[site.id]; if (!r) continue;
88
+ const ground = samples[f].ground?.[site.id] ?? floorY;
89
+ const depth = Math.max(0, ground - r.floor);
90
+ if (depth > m.penetration) { m.penetration = depth; m.penetrationFrame = f; }
91
+ const span = masks[f]?.get(site.id);
92
+ if (span) {
93
+ const point = sitePoint(samples[f], site);
94
+ const slide = Math.hypot(point.x - span.anchor.x, point.z - span.anchor.z);
95
+ if (slide > m.slide) { m.slide = slide; m.slideFrame = f; }
96
+ const gap = Math.max(0, r.floor - ground);
97
+ if (gap > m.float) { m.float = gap; m.floatFrame = f; }
98
+ m.meanSlide += slide; m.count += 1;
99
+ }
100
+ }
101
+ for (const side of ["leftFoot", "rightFoot"]) {
102
+ if (f > 0) {
103
+ const step = Math.abs(samples[f].knees[side] - samples[f - 1].knees[side]);
104
+ if (step > m.kneeStep) { m.kneeStep = step; m.kneeStepFrame = f; }
105
+ }
106
+ if (f > 1) {
107
+ const a = Math.abs(samples[f].knees[side] - 2 * samples[f - 1].knees[side] + samples[f - 2].knees[side]) * fps * fps;
108
+ if (a > m.kneeAcceleration) { m.kneeAcceleration = a; m.kneeAccelerationFrame = f; }
109
+ }
110
+ }
111
+ if (f > 1) {
112
+ const a = samples[f].root.clone().add(samples[f - 2].root).addScaledVector(samples[f - 1].root, -2).length() * fps * fps;
113
+ if (a > m.rootAcceleration) { m.rootAcceleration = a; m.rootAccelerationFrame = f; }
114
+ }
115
+ }
116
+ m.meanSlide /= m.count || 1;
117
+ return m;
118
+ }
@@ -1,10 +1,9 @@
1
1
  import * as THREE from "three";
2
2
  import { findBone } from "./ik.js";
3
- import { SEGMENT_MASSES } from "./auto-physics.js";
3
+ import { SEGMENT_MASSES } from "./center-of-mass.js";
4
4
 
5
5
  const G = 9.81;
6
6
  const V = () => new THREE.Vector3();
7
- const smooth = (t) => { t = Math.max(0, Math.min(1, t)); return t * t * (3 - 2 * t); };
8
7
  const SUPPORT_LIMITS = Object.freeze({ gap: .025, force: .22, moment: .10, maxDrop: .60, friction: .6 });
9
8
 
10
9
  /** Cached bone lookup and an explicit approximate segmented physical body.
@@ -121,80 +120,6 @@ function patchesAt(row, floorY, shift = 0, overrides = [], frame = 0) {
121
120
  && !overrides.some((o) => o.site === id && o.mode === "free" && frame >= o.start && frame <= o.end));
122
121
  }
123
122
 
124
- /** Joint hypothesis search over vertical body alignment and support patches.
125
- * The scene plane stays fixed. No action names, pose labels, or forced feet.
126
- * Flight is a temporal acceleration hypothesis, never 'low velocity'. */
127
- export function inferSupportAlignment(rows, fps, { floorY = 0, overrides = [], protectedFrames = [], strength = 1 } = {}) {
128
- const motion = supportMotion(rows, fps), n = rows.length;
129
- const shifts = new Float64Array(n), candidates = [], flight = new Set(), unknown = [];
130
- const observed = rows.map((row) => Math.min(...Object.values(row.support).filter((p) => p.vertices > 0).map((p) => p.floor - floorY)));
131
- for (let f = 0; f < n; f += 1) {
132
- const demand = motion[f], row = rows[f];
133
- const baseline = solveSupportForces(row, demand, patchesAt(row, floorY, 0, overrides, f));
134
- // Preserve true ballistic motion, including nearby transition frames.
135
- let ballistic = 0, tested = 0;
136
- for (let i = Math.max(0, f - 2); i <= Math.min(n - 1, f + 2); i += 1) { tested += 1; if (motion[i].force.length() < .28) ballistic += 1; }
137
- if (ballistic >= Math.ceil(tested * .6)) { flight.add(f); candidates.push({ frame: f, mode: "flight", shift: 0, ...baseline }); continue; }
138
- let shift = 0, selected = baseline, mode = baseline.forceResidual <= SUPPORT_LIMITS.force && baseline.momentResidual <= SUPPORT_LIMITS.moment ? "supported" : "unknown";
139
- const gap = observed[f];
140
- // Only a sustained support demand is eligible for automatic alignment.
141
- // Fast/transient failure is left visible rather than flattened to floor.
142
- const stableDemand = demand.force.y > .55 && demand.force.y < 1.5 && Math.hypot(demand.force.x, demand.force.z) < .55;
143
- let hints = [];
144
- if (mode === "unknown" && stableDemand && gap > -.12 && gap <= SUPPORT_LIMITS.maxDrop) {
145
- let best = Infinity;
146
- // Body translation and reachable limb contact are hypotheses together.
147
- // A low foot may rise via IK while a hand/knee descends. Rigidly
148
- // lowering everything to the single lowest vertex cannot solve this.
149
- for (const proposed of [...new Set([0, .002 - gap, -.033 - gap, -.068 - gap, -.098 - gap])]) {
150
- if (proposed > .12 || proposed < -SUPPORT_LIMITS.maxDrop) continue;
151
- const movable = /^(left|right)(Foot|Hand|Knee)$/;
152
- const rigid = Object.entries(row.support).filter(([id, p]) => !movable.test(id) && p.vertices > 0);
153
- if (rigid.some(([, p]) => p.floor + proposed < floorY - .025)) continue;
154
- const patches = []; let correction = 0;
155
- for (const [id, p] of Object.entries(row.support)) {
156
- if (!p.vertices || overrides.some((o) => o.site === id && o.mode === "free" && f >= o.start && f <= o.end)) continue;
157
- const distance = p.floor + proposed - floorY;
158
- if (Math.abs(distance) > (movable.test(id) ? .105 : .025)) continue;
159
- const dy = floorY + .002 - proposed - p.floor;
160
- patches.push([id, { ...p, patch: (p.patch?.length ? p.patch : [p.point ?? p.position]).map((v) => v.clone().add(new THREE.Vector3(0, dy, 0))) }]);
161
- correction += Math.abs(dy);
162
- }
163
- const fit = solveSupportForces(row, demand, patches);
164
- const cost = fit.residual + .08 * Math.abs(proposed) + .10 * correction / Math.max(1, patches.length);
165
- if (fit.forceResidual < .16 && fit.momentResidual < .075 && fit.residual < baseline.residual * .55 && cost < best) {
166
- best = cost; shift = proposed; selected = fit; mode = "align";
167
- hints = patches.filter(([id]) => (fit.loads[id] ?? 0) > .03 && movable.test(id)).map(([id]) => id);
168
- }
169
- }
170
- }
171
- candidates.push({ frame: f, mode, shift, hints, ...selected });
172
- }
173
- // A one-frame lucky force solution must not turn into a root jump.
174
- const minRun = Math.max(5, Math.ceil(fps * .25));
175
- for (let a = 0; a < n;) {
176
- if (candidates[a].mode !== "align") { a += 1; continue; }
177
- let b = a + 1; while (b < n && candidates[b].mode === "align") b += 1;
178
- if (b - a >= minRun) for (let f = a; f < b; f += 1) shifts[f] = candidates[f].shift;
179
- else for (let f = a; f < b; f += 1) candidates[f].mode = "unknown";
180
- a = b;
181
- }
182
- const radius = Math.max(3, Math.round(fps * .35)), filtered = new Float64Array(n);
183
- for (let f = 0; f < n; f += 1) {
184
- if (flight.has(f) || protectedFrames.includes(f)) continue;
185
- let sum = 0, weight = 0;
186
- for (let i = Math.max(0, f - radius); i <= Math.min(n - 1, f + radius); i += 1) { const w = radius + 1 - Math.abs(f - i); sum += shifts[i] * w; weight += w; }
187
- let influence = strength;
188
- for (const p of protectedFrames) influence *= smooth(Math.abs(f - p) / (radius + 1));
189
- // Never push a lower unrelated surface through the ground.
190
- // IK will resolve the limited limb penetration admitted by a candidate;
191
- // rigid-body patches were checked above and remain protected.
192
- filtered[f] = sum / weight * influence;
193
- if (candidates[f].mode === "unknown") unknown.push(f);
194
- }
195
- return { shifts: filtered, frames: candidates, flight, unknown, motion };
196
- }
197
-
198
123
  /** Deliberately independent of detected contact masks: 'no contacts' is not a
199
124
  * zero-error success when gravity requires support. */
200
125
  export function supportDiagnostics(rows, fps, floorY = 0) {
@@ -0,0 +1,22 @@
1
+ // Bone transform snapshots for a rig: playback writes bone rotations (and the hips position),
2
+ // so starting or clearing a take captures every bone first and restores the prior pose exactly.
3
+
4
+ /** Capture the local position and quaternion of every bone in the rig. */
5
+ export function snapshotPlaybackBones(rig) {
6
+ const out = [];
7
+ rig.traverse((bone) => {
8
+ if (!bone.isBone) return;
9
+ out.push([bone, bone.quaternion.x, bone.quaternion.y, bone.quaternion.z, bone.quaternion.w, bone.position.x, bone.position.y, bone.position.z]);
10
+ });
11
+ return out;
12
+ }
13
+
14
+ /** Restore transforms captured by snapshotPlaybackBones. */
15
+ export function restorePlaybackBones(rig, snapshot) {
16
+ if (!rig || !snapshot) return;
17
+ for (const entry of snapshot) {
18
+ entry[0].quaternion.set(entry[1], entry[2], entry[3], entry[4]);
19
+ if (entry.length > 5) entry[0].position.set(entry[5], entry[6], entry[7]);
20
+ }
21
+ rig.updateMatrixWorld(true);
22
+ }
@@ -0,0 +1,140 @@
1
+ // The Full-Body strip of the Sequencer as timing segments of the installed take (take-edit.js):
2
+ // Cut at the playhead, drag a segment's amber grip to retime it (wider is slower), drag the
3
+ // outer green grips to trim, right-click a segment to drop it. Each gesture previews here and
4
+ // commits ONE timing command on release (commands/motion-timing.js), so each is one Undo.
5
+ import { useRef, useState } from "react";
6
+ import { isKo, ko } from "../locale.js";
7
+ import { frameFromClientX } from "./timeline-coordinates.js";
8
+ import { createTakeEdit, segmentSpeedForFrames, takeEditLayout, TRIM_MIN_FRAMES } from "../motion3/take-edit.js";
9
+
10
+ /** The motion prop the Sequencer draws: frames, label and timing segments of the active
11
+ * character's take, with the timing commands bound to that character. */
12
+ export function timelineTake(motion, characterId, run) {
13
+ if (!motion) return null;
14
+ const act = (action, args = {}) => (characterId ? run?.(action, { characterId, ...args }) : null);
15
+ return {
16
+ frames: motion.frames, label: motion.prompt || ko("Compiled take", "컴파일된 테이크"),
17
+ segments: takeEditLayout(motion.edit ?? createTakeEdit(motion.frames)),
18
+ cut: frame => act("motion.cut", { frame }), speed: (segmentId, speed) => act("motion.setSegmentSpeed", { segmentId, speed }),
19
+ remove: segmentId => act("motion.removeSegment", { segmentId }), trim: (start, end) => act("motion.trim", { start, end }), reset: () => act("motion.resetTiming"),
20
+ };
21
+ }
22
+
23
+ const selectedSegment = (segments, frame) => segments.find(segment => frame >= segment.timelineStart && frame <= segment.timelineEnd) ?? segments[0] ?? null;
24
+
25
+ function trimRange(edge, frame, preview, max) {
26
+ return edge === "start"
27
+ ? { ...preview, start: Math.max(0, Math.min(frame, preview.end - TRIM_MIN_FRAMES)) }
28
+ : { ...preview, end: Math.min(max, Math.max(frame, preview.start + TRIM_MIN_FRAMES)) };
29
+ }
30
+
31
+ /** While a speed grip is held the dragged segment shows its would-be width and the segments
32
+ * after it slide by the delta: the same reflow the commit produces. */
33
+ function shownSegments(segments, preview) {
34
+ if (preview?.kind === "trim") return [{ id: "motion-trim-preview", timelineStart: preview.start, timelineEnd: preview.end, preview: true }];
35
+ if (preview?.kind !== "speed") return segments;
36
+ let shift = 0;
37
+ return segments.map(segment => {
38
+ if (segment.id !== preview.id) return shift ? { ...segment, timelineStart: segment.timelineStart + shift, timelineEnd: segment.timelineEnd + shift } : segment;
39
+ const shown = { ...segment, timelineStart: segment.timelineStart + shift, timelineEnd: segment.timelineStart + shift + preview.timelineFrames - 1, previewSpeed: preview.speed };
40
+ shift += preview.timelineFrames - segment.timelineFrames;
41
+ return shown;
42
+ });
43
+ }
44
+
45
+ export function TakeSegmentClips({ motion, frame, frameCount, displayFrameCount, clipPct, tools }) {
46
+ const drag = useRef(null);
47
+ const [preview, setPreview] = useState(null);
48
+ const segments = motion.segments, selected = selectedSegment(segments, frame), max = motion.frames - 1;
49
+ function begin(e, state) {
50
+ if (e.button !== 0) return;
51
+ e.preventDefault();
52
+ e.stopPropagation();
53
+ const rect = e.currentTarget.closest(".tl-lane")?.getBoundingClientRect();
54
+ if (!rect || rect.width < 2) return;
55
+ e.currentTarget.setPointerCapture?.(e.pointerId);
56
+ drag.current = { ...state, rect, displayFrameCount };
57
+ setPreview(state.preview ?? null);
58
+ }
59
+ function move(e) {
60
+ const active = drag.current;
61
+ if (!active) return;
62
+ if (active.preview?.kind === "trim") active.preview = trimRange(active.edge, Math.min(max, frameFromClientX(e.clientX, active.rect.left, active.rect.width, active.displayFrameCount, frameCount)), active.preview, max);
63
+ else {
64
+ // a slower segment outgrows the timeline: the grip keeps counting frames past the lane's end
65
+ const at = ((e.clientX - active.rect.left) / Math.max(1, active.rect.width)) * Math.max(0, active.displayFrameCount - 1);
66
+ const speed = segmentSpeedForFrames(active.segment, Math.max(1, Math.round(at) - active.segment.timelineStart + 1));
67
+ const timelineFrames = Math.max(1, Math.round((active.segment.sourceEnd - active.segment.sourceStart + 1) / speed));
68
+ if (active.preview?.speed === speed) return;
69
+ active.preview = { kind: "speed", id: active.segment.id, speed, timelineFrames };
70
+ }
71
+ setPreview(active.preview);
72
+ }
73
+ function end(e) {
74
+ const active = drag.current;
75
+ drag.current = null;
76
+ e.currentTarget.releasePointerCapture?.(e.pointerId);
77
+ setPreview(null);
78
+ const next = active?.preview;
79
+ if (next?.kind === "trim" && (next.start > 0 || next.end < max)) motion.trim(next.start, next.end);
80
+ if (next?.kind === "speed" && next.speed !== active.segment.speed) motion.speed(active.segment.id, next.speed);
81
+ }
82
+ const grip = (className, label, state, extra = {}) => (
83
+ <button className={`tl-motion-clip-handle ${className}`} type="button" aria-label={label} title={extra.title}
84
+ onPointerDown={e => begin(e, state)} onPointerMove={move} onPointerUp={end} onPointerCancel={end}
85
+ onContextMenu={extra.reset ? e => { e.preventDefault(); e.stopPropagation(); motion.reset(); } : undefined} />
86
+ );
87
+ const trimState = edge => ({ edge, preview: { kind: "trim", start: 0, end: max } });
88
+ const shown = shownSegments(segments, preview), single = segments.length === 1;
89
+ return shown.map((segment, index) => {
90
+ const speed = segment.previewSpeed ?? segment.speed, grips = tools && (segment.preview || selected?.id === segment.id);
91
+ const label = segment.preview
92
+ ? `${segment.timelineStart}–${segment.timelineEnd} (${segment.timelineEnd - segment.timelineStart + 1}f)`
93
+ : single ? `${motion.label}${speed !== 1 ? ` · ${speed}×` : ""}` : `${index + 1} · ${speed}×`;
94
+ const trimReset = { title: ko("Drag to trim · right-click restores the full take", "드래그로 자르기 · 우클릭으로 전체 테이크 복원"), reset: true };
95
+ return (
96
+ <div
97
+ key={segment.id}
98
+ className={"tl-motion-clip" + (preview?.kind === "trim" ? " trimming" : "") + (segment.previewSpeed !== undefined ? " retiming" : "") + (!single && selected?.id === segment.id ? " selected" : "")}
99
+ style={{ "--tl-f-start": clipPct(Math.min(segment.timelineStart, displayFrameCount)), "--tl-f-end": clipPct(Math.min(segment.timelineEnd + 1, displayFrameCount)) }}
100
+ title={segment.preview ? undefined : isKo
101
+ ? `전신 구간 ${index + 1} — ${segment.speed}× · ${segment.timelineFrames}프레임. 재생 헤드에서 컷 · 주황 그립을 끌어 배속 조절 · 우클릭으로 구간 삭제`
102
+ : `Full-Body segment ${index + 1} — ${segment.speed}× · ${segment.timelineFrames} frames. Cut at the playhead; drag the amber grip to retime; right-click to delete`}
103
+ onContextMenu={segment.preview || single ? undefined : e => { e.preventDefault(); e.stopPropagation(); motion.remove(segment.id); }}
104
+ >
105
+ {index === 0 && grips && grip("start", ko("Trim take start", "테이크 시작점 자르기"), trimState("start"), trimReset)}
106
+ <span className="tl-motion-clip-label">{label}</span>
107
+ {!segment.preview && grips && grip("speed", ko("Retime segment by stretch", "드래그로 구간 배속 조절"), { segment },
108
+ { title: ko("Drag — wider is slower, narrower is faster", "드래그 — 늘리면 느리게, 줄이면 빠르게") })}
109
+ {index === shown.length - 1 && grips && grip("end", ko("Trim take end", "테이크 끝점 자르기"), trimState("end"), trimReset)}
110
+ </div>
111
+ );
112
+ });
113
+ }
114
+
115
+ /** Sequencer head tools for the take: Cut at the playhead and the selected segment's speed. */
116
+ export function TakeSegmentTools({ motion, frame }) {
117
+ const selected = selectedSegment(motion.segments, frame), index = motion.segments.indexOf(selected);
118
+ const [draft, setDraft] = useState(null);
119
+ const commit = value => {
120
+ setDraft(null);
121
+ if (selected && Number.isFinite(value) && value !== selected.speed) motion.speed(selected.id, Math.max(0.1, Math.min(4, value)));
122
+ };
123
+ const canCut = Boolean(selected) && frame > selected.timelineStart && frame <= selected.timelineEnd && frame < motion.frames;
124
+ return (<>
125
+ <button type="button" className="tl-seq-action cut" data-testid="sequencer-cut" disabled={!canCut}
126
+ title={ko("Cut the Full-Body clip at the playhead", "재생 헤드에서 전신 클립 자르기")} onClick={() => motion.cut(frame)}>
127
+ {ko("Cut", "컷")}
128
+ </button>
129
+ {selected && (
130
+ <label className="tl-motion-speed-editor">
131
+ <span>{ko(`Segment ${index + 1} speed`, `구간 ${index + 1} 배속`)}</span>
132
+ <input type="range" min="0.1" max="4" step="0.1" value={draft ?? selected.speed} aria-label={ko("Selected Full-Body segment speed", "선택한 전신 구간 배속")}
133
+ onChange={e => setDraft(e.currentTarget.valueAsNumber)} onPointerUp={e => commit(e.currentTarget.valueAsNumber)} onKeyUp={e => commit(e.currentTarget.valueAsNumber)} />
134
+ <input type="number" min="0.1" max="4" step="0.1" value={selected.speed} aria-label={ko("Selected Full-Body segment speed value", "선택한 전신 구간 배속 값")}
135
+ onChange={e => commit(e.currentTarget.valueAsNumber)} />
136
+ <small>×</small>
137
+ </label>
138
+ )}
139
+ </>);
140
+ }
@@ -8,27 +8,6 @@ export function frameFromClientX(clientX, laneLeft, laneWidth, displayFrameCount
8
8
  return Math.max(0, Math.min(frameCount - 1, displayedFrame));
9
9
  }
10
10
 
11
- /** Convert drag delta into a raw prompt-block start frame. Geometry and frame
12
- * count are captured at pointer-down so extending the timeline cannot feed
13
- * back into the next pointermove. Scroll cancels out because this uses delta X. */
14
- export function promptMoveStartFrame(startFrame, startClientX, clientX, laneWidth, displayFrameCount) {
15
- const framesPerPixel = Math.max(0, displayFrameCount - 1) / Math.max(1, laneWidth);
16
- return startFrame + (clientX - startClientX) * framesPerPixel;
17
- }
18
-
19
- /** Half a second at 24 fps: a shorter take is a pose, not a motion. */
20
- export const TRIM_MIN_FRAMES = 12;
21
-
22
- /** Clamp one trim-handle drag of the loaded take into a legal in/out range.
23
- * `preview` is the range the gesture currently shows, `frame` the frame under
24
- * the pointer and `max` the take's last displayed frame. The opposite edge
25
- * never moves, and the remaining take never drops below `minFrames`. */
26
- export function motionTrimRange(edge, frame, preview, max, minFrames = TRIM_MIN_FRAMES) {
27
- return edge === "start"
28
- ? { ...preview, start: Math.max(0, Math.min(frame, preview.end - minFrames)) }
29
- : { ...preview, end: Math.min(max, Math.max(frame, preview.start + minFrames)) };
30
- }
31
-
32
11
  /** Optional Shot blocks use their own explicit inclusive range. Gaps remain
33
12
  * visible free-camera time instead of being inferred from the next card. */
34
13
  export function shotBlockGeometry(shots, index, frameCount, displayFrameCount = frameCount) {
@@ -433,9 +433,9 @@
433
433
  min-height: 0;
434
434
  height: 100%;
435
435
  display: grid;
436
+ /* Ruler, then the lanes in TRACKS order: Body group, Root path row, Camera Cuts group. */
436
437
  grid-template-rows:
437
438
  var(--tl-ruler-height, 24px)
438
- var(--tl-track-row-height, 38px)
439
439
  var(--tl-track-group-height, 114px)
440
440
  var(--tl-track-row-height, 38px)
441
441
  var(--tl-track-group-height, 114px);
@@ -447,7 +447,6 @@
447
447
  .v2-sequencer .tl-surface.empty-shots {
448
448
  grid-template-rows:
449
449
  var(--tl-ruler-height, 24px)
450
- var(--tl-track-row-height, 38px)
451
450
  var(--tl-track-group-height, 114px)
452
451
  var(--tl-track-row-height, 38px)
453
452
  var(--tl-track-group-height, 114px);