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,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);