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,211 @@
1
+ // Self-collision auto-fix (spec.fix.self, on unless false): the arms keep out of the rest of the body,
2
+ // on the fitted skin the BODY check measures (body.js, qa/body-checks.js). Wrist targets inside the
3
+ // body already went out onto the skin when they were solved (compile.js keepOut); what is left here:
4
+ // - elbow: an upper arm or forearm inside the body turns the elbow about the shoulder-wrist line
5
+ // (ctx.swivel; the hand keeps its place and turn). The turn is planned over the whole take at once,
6
+ // absolute: each frame scores every turn in TURNS by the depth left, the size of the turn and how
7
+ // far the elbow rises past LOW under the shoulder (no chicken wing), plus the change from the frame before, and the cheapest
8
+ // path wins (Viterbi), so the elbow eases in and out instead of snapping;
9
+ // - hand: a hand or forearm still a little inside moves its wrist IK target out along the skin's
10
+ // normal (ctx.skinPush), at most STEP a pass and REACH in all, eased over neighbouring frames. Deeper hits, and the
11
+ // head and neck (a hand at the mouth is on purpose), stay for QA.
12
+ // Frames whose arm is FK (armw 0) only get the elbow turn. The report says what moved.
13
+ import { bodyFor, bodyNormal, limbDepth, segmentDepth } from "../body.js";
14
+ import { add, sub, scale, len, apply, axisRot } from "../compiler/math.js";
15
+ import { CAPSULES, segPoints, sdBox } from "../qa/geometry.js";
16
+ import { round4 } from "./ease.js";
17
+ import { turnWeight } from "../compiler/pose.js";
18
+ import { allowance } from "../qa/checks.js";
19
+ import { regionOf } from "../qa/body-checks.js";
20
+
21
+ const TURNS = Array.from({ length: 19 }, (_, i) => -90 + i * 10);
22
+ const PASSES = 3, EASE = 5, MARGIN = 0.003, ALLOW = 0.003, STEP = 0.03, REACH = 0.08;
23
+ /** The most of a declared allowance the push leaves alone (m): a touch (a hand in a pocket), not a skill's leeway for a sweep it cannot avoid. */
24
+ const TOUCH = 0.03;
25
+ /** Plan weights: per cm^2 of depth, per (10 deg)^2 of turn, per cm^2 of elbow over the shoulder, per (10 deg)^2 of change a frame. */
26
+ const DEEP = 40, TURN = 0.05, RISE = 4, CHANGE = 2;
27
+ /** How far (m) under the shoulder the elbow may come before RISE counts. Where the spec declares a chicken-wing check
28
+ * on this elbow (checks.below, joint <Side>ForeArm under <Side>Arm: the smoke skill's), rising past its min costs
29
+ * WING_PRICE: a smoking elbow the turn raised past it broke that check once the wrist stopped at its limit (wrist-clamp.js). */
30
+ const LOW = 0.09, WING_PRICE = 400;
31
+
32
+ /** The spec's chicken-wing min (m) for the elbow of `side` on frame i (a below check on it at least half on), else null. */
33
+ function wingMin(out, i, side) {
34
+ let min = null;
35
+ for (const b of out.ctx.spec.checks?.below ?? []) {
36
+ const joint = String(b.joint).replace(/^joint:/, ""), ref = String(b.ref).replace(/^joint:/, ""), f = out.f0 + i;
37
+ const on = Array.isArray(b.when) ? f >= b.when[0] && f <= b.when[1] : b.when ? (out.frames[i].c[b.when] ?? 0) > 0.5 : true;
38
+ if (on && joint === `${side}ForeArm` && ref === `${side}Arm` && Number.isFinite(b.min)) min = Math.max(min ?? 0, b.min);
39
+ }
40
+ return min;
41
+ }
42
+ /** The most the turn changes in one frame (deg): a fast arm turned faster than this pops. */
43
+ const STEP_TURN = 10;
44
+ const SIDES = { l: "Left", r: "Right" };
45
+
46
+ /** How much hand k is on a measured target at channels c: a fit of its own (the mouth), or a fit of the
47
+ * other hand anchored to a prop k holds (the flame under k's cigarette). Such a hand is not moved. */
48
+ export function fitWeight(spec, c, k) {
49
+ let w = 0;
50
+ for (const fit of spec.fits ?? []) {
51
+ const a = spec.anchors?.[fit.anchor], on = c[`w_${fit.anchor}_${fit.side}`] ?? 0;
52
+ if (fit.side === k || (a?.kind === "prop" && spec.props?.[a.prop]?.hand === k)) w = Math.max(w, on);
53
+ }
54
+ return Math.min(1, w);
55
+ }
56
+
57
+ const CAP = Object.fromEntries(CAPSULES.map(c => [c[0], c[3]]));
58
+ /** How deep (m) segment A-B of radius r goes into the scene's boxes (QA's COLLIDE on the same capsules). */
59
+ function sceneDepth(boxes, A, B, r) {
60
+ let worst = 0;
61
+ for (const box of boxes) worst = Math.max(worst, r - Math.min(...segPoints(A, B, 5).map(p => sdBox(p, box))));
62
+ return worst;
63
+ }
64
+
65
+ /** Deepest skin depth (m) of the upper arm and forearm with the elbow turned `deg` more about the shoulder-wrist line,
66
+ * and how deep they go into the scene's boxes (an elbow turned out of the body must not turn into a steering wheel). */
67
+ function elbowDepth(body, sk, s, side, deg, boxes = []) {
68
+ const I = sk.index, a = I[`${side}Arm`], fa = I[`${side}ForeArm`], S = s.P[a], W = s.P[I[`${side}Hand`]];
69
+ const E = deg ? add(S, apply(axisRot(sub(W, S), deg), sub(s.P[fa], S))) : s.P[fa];
70
+ const worst = Math.max(segmentDepth(body, sk, s, a, S, E), segmentDepth(body, sk, s, fa, E, W));
71
+ const k = side === "Left" ? "l" : "r", scene = boxes.length ? Math.max(sceneDepth(boxes, S, E, CAP[`uarm_${k}`]), sceneDepth(boxes, E, W, CAP[`farm_${k}`])) : 0;
72
+ return { depth: worst, scene, rise: Math.max(0, E[1] - (S[1] - LOW)), under: S[1] - E[1] };
73
+ }
74
+
75
+ /** Shoulder to wrist at full stretch (m), at bind. */
76
+ const armLength = (sk, side) => { const I = sk.index, P = sk.restWorldPos; return len(sub(P[I[`${side}ForeArm`]], P[I[`${side}Arm`]])) + len(sub(P[I[`${side}Hand`]], P[I[`${side}ForeArm`]])); };
77
+
78
+ /** Absolute elbow turns (deg) for side k over the take, or null when no frame needs or has one. */
79
+ function planTurns(out, body, k) {
80
+ const { sk } = out.ctx, side = SIDES[k], n = out.frames.length, now = out.ctx.swivel[k];
81
+ const cost = out.frames.map(({ s, c }, i) => {
82
+ const tw = turnWeight(c[`armw_${k}`] ?? 0), cur = (now.get(out.f0 + i) ?? 0) * tw;
83
+ if (!cur && elbowDepth(body, sk, s, side, 0).depth <= ALLOW) return null;
84
+ // only the boxes the arm can reach this frame (their bounding sphere within the arm's length of the shoulder)
85
+ const S = s.P[sk.index[`${side}Arm`]], reach = armLength(sk, side) + 0.15;
86
+ const boxes = Object.values(out.objectsAt(i)).filter(box => box.half && !box.ignore && len(sub(add(box.pos, apply(box.R, [0, box.half[1], 0])), S)) - len(box.half) < reach);
87
+ const base = elbowDepth(body, sk, s, side, -cur, boxes).scene, wmin = wingMin(out, i, side);
88
+ return TURNS.map(deg => {
89
+ const { depth, scene, rise, under } = elbowDepth(body, sk, s, side, deg * tw - cur, boxes), cm = Math.max(0, depth - ALLOW) * 100;
90
+ const wing = wmin === null ? 0 : Math.max(0, wmin - under);
91
+ // a box the arm was already in at no turn is QA's COLLIDE to report, not this plan's to avoid
92
+ const hit = Math.max(0, scene - Math.max(base, ALLOW)) * 100;
93
+ return DEEP * (cm * cm + hit * hit) + TURN * (deg / 10) ** 2 + RISE * (rise * 100) ** 2 + WING_PRICE * (wing * 100) ** 2;
94
+ });
95
+ });
96
+ if (cost.every(c => !c)) return null;
97
+ // a frame that needs no turn has none; the take may start already turned
98
+ const zero = TURNS.indexOf(0), still = TURNS.map((_, j) => (j === zero ? 0 : Infinity));
99
+ let acc = TURNS.map(() => 0);
100
+ const back = [];
101
+ for (let i = 0; i < n; i++) {
102
+ const here = cost[i] ?? still, prev = acc, from = [];
103
+ acc = here.map((h, j) => {
104
+ let best = Infinity, arg = zero;
105
+ for (let p = 0; p < TURNS.length; p++) {
106
+ if (Math.abs(TURNS[j] - TURNS[p]) > STEP_TURN) continue;
107
+ const v = prev[p] + CHANGE * ((TURNS[j] - TURNS[p]) / 10) ** 2;
108
+ if (v < best) { best = v; arg = p; }
109
+ }
110
+ from.push(arg);
111
+ return best + h;
112
+ });
113
+ back.push(from);
114
+ }
115
+ const path = new Array(n);
116
+ let j = acc.indexOf(Math.min(...acc));
117
+ for (let i = n - 1; i >= 0; i--) { path[i] = TURNS[j]; j = back[i][j]; }
118
+ // the 10-degree steps, smoothed
119
+ return path.map((_, i) => (path[Math.max(0, i - 1)] + 2 * path[i] + path[Math.min(n - 1, i + 1)]) / 4);
120
+ }
121
+
122
+ /** Per-frame wrist pushes for side k: a hand or forearm a little inside the skin (not the head or neck) goes out along its normal. */
123
+ function handPushes(out, body, k, stats) {
124
+ const { sk } = out.ctx, side = SIDES[k], I = sk.index, n = out.frames.length, v = new Array(n).fill(null);
125
+ const face = [I.Head, I.Neck, I.HeadTop_End].filter(b => b !== undefined);
126
+ for (let i = 0; i < n; i++) {
127
+ const { s, c } = out.frames[i], f = out.f0 + i;
128
+ // a hand on a measured target (a fit's anchor: the mouth, the flame) stays where its goal needs it; on its way
129
+ // there the push fades out with the target's weight
130
+ const free = 1 - fitWeight(out.ctx.spec, c, k);
131
+ if (free < 0.05) continue;
132
+ // past what the spec allows (a hand in a pocket touches the hip: checks.allow), as QA's BODY measures it
133
+ let hit = null;
134
+ for (const b of [I[`${side}Hand`], I[`${side}ForeArm`]]) {
135
+ const h = limbDepth(body, sk, s, b);
136
+ if (h.into < 0 || face.includes(h.into)) continue;
137
+ const allowed = Math.min(TOUCH, allowance(out.ctx.spec.checks?.allow, regionOf(sk, b), regionOf(sk, h.into)) ?? 0), past = h.depth - Math.max(allowed, ALLOW);
138
+ if (past > 0 && (!hit || past > hit.past)) hit = { ...h, b, past };
139
+ }
140
+ if (!hit) continue;
141
+ if (!(c[`armw_${k}`] > 1e-4)) { stats.skipped++; continue; }
142
+ const done = len(out.ctx.skinPush[k].get(f) ?? [0, 0, 0]);
143
+ if (done >= REACH) { stats.deep++; continue; }
144
+ const nrm = bodyNormal(body, s, hit.p, new Set([...body.skip.get(hit.b), ...face]));
145
+ if (nrm) v[i] = scale(nrm, free * Math.min(STEP, REACH - done, hit.past + MARGIN));
146
+ }
147
+ // eased dilation: neighbours get the strongest nearby push, scaled down
148
+ const eased = new Array(n).fill(null);
149
+ v.forEach((p, i) => {
150
+ if (!p) return;
151
+ for (let j = Math.max(0, i - EASE - 1); j <= Math.min(n - 1, i + EASE + 1); j++) {
152
+ const t = 1 - Math.abs(i - j) / (EASE + 1), w = scale(p, t * t * (3 - 2 * t));
153
+ if (!eased[j] || len(w) > len(eased[j])) eased[j] = w;
154
+ }
155
+ });
156
+ return eased;
157
+ }
158
+
159
+ export function selfClear(out, engine) {
160
+ const body = bodyFor(out.ctx.sk);
161
+ if (!body) return null;
162
+ const n = out.frames.length, stats = { skipped: 0, deep: 0 }, turned = new Set(), pushed = new Set();
163
+ let maxTurn = 0, maxPush = 0;
164
+ for (let pass = 0; pass < PASSES; pass++) {
165
+ let touched = new Set();
166
+ for (const k of ["r", "l"]) {
167
+ const plan = planTurns(out, body, k);
168
+ if (!plan) continue;
169
+ plan.forEach((deg, i) => {
170
+ const f = out.f0 + i, cur = out.ctx.swivel[k].get(f) ?? 0, next = Math.abs(deg) < 0.5 ? 0 : deg;
171
+ if (Math.abs(next - cur) < 0.5) return;
172
+ if (next) out.ctx.swivel[k].set(f, next); else out.ctx.swivel[k].delete(f);
173
+ maxTurn = Math.max(maxTurn, Math.abs(next)); if (next) turned.add(`${k}${i}`); touched.add(f);
174
+ });
175
+ }
176
+ if (touched.size) engine.solve([...touched]);
177
+ touched = new Set();
178
+ stats.skipped = 0; stats.deep = 0;
179
+ for (const k of ["r", "l"]) {
180
+ handPushes(out, body, k, stats).forEach((p, i) => {
181
+ if (!p) return;
182
+ const f = out.f0 + i, next = add(out.ctx.skinPush[k].get(f) ?? [0, 0, 0], p);
183
+ out.ctx.skinPush[k].set(f, next);
184
+ maxPush = Math.max(maxPush, len(next)); pushed.add(`${k}${i}`); touched.add(f);
185
+ });
186
+ }
187
+ if (!touched.size) break;
188
+ engine.solve([...touched]);
189
+ }
190
+ // the pushes add up pass by pass from frames that read their normals apart: smoothed over time, so the hand
191
+ // does not jerk (a 1-4-6-4-1 kernel, twice)
192
+ if (pushed.size) {
193
+ const touched = new Set();
194
+ for (const k of ["r", "l"]) {
195
+ const at = i => out.ctx.skinPush[k].get(out.f0 + i) ?? [0, 0, 0];
196
+ for (let pass = 0; pass < 2; pass++) {
197
+ const was = Array.from({ length: n }, (_, i) => at(i));
198
+ was.forEach((_, i) => {
199
+ const sm = [-2, -1, 0, 1, 2].reduce((acc, d, j) => add(acc, scale(was[Math.min(n - 1, Math.max(0, i + d))], [1, 4, 6, 4, 1][j] / 16)), [0, 0, 0]);
200
+ if (len(sm) < 1e-5 && !out.ctx.skinPush[k].has(out.f0 + i)) return;
201
+ out.ctx.skinPush[k].set(out.f0 + i, sm); touched.add(out.f0 + i);
202
+ });
203
+ }
204
+ }
205
+ if (touched.size) engine.solve([...touched]);
206
+ }
207
+ if (!turned.size && !pushed.size && !stats.skipped) return null;
208
+ return { elbowFrames: turned.size, maxTurn: Math.round(maxTurn), handFrames: pushed.size, maxPush: round4(maxPush), skipped: stats.skipped, tooDeep: stats.deep,
209
+ how: "elbow turned about the shoulder-wrist line; a hand a little inside pushed out along the skin" };
210
+ }
211
+
@@ -0,0 +1,70 @@
1
+ // Planted-foot lock: while a heel or ball is planted (QA's own definition, over the ground under
2
+ // it: the floor or an object's top, qa/ground.js) and drifts less than
3
+ // 1 cm, the ankle target is shifted so the planted point stays where it landed. The foot pose
4
+ // depends only on the ankle target, fyaw and fpitch, so the shift moves the sole rigidly.
5
+ // A pivot (the planted point is the only one down: the heel is up over a planted ball, or the
6
+ // ball over a planted heel) is held whatever the drift: fyaw and fpitch then turn the foot about
7
+ // that point, as a batter's back foot turns on its ball, instead of about the ankle. A pivot that
8
+ // held more than 1 cm keeps holding while its point stays under PIVOT_H (a hover, not a step) and
9
+ // lets go no faster than EASE_STEP a frame.
10
+ import { add, sub } from "../compiler/math.js";
11
+ import { QA_LIMITS } from "../qa/checks.js";
12
+ import { groundSole } from "../qa/ground.js";
13
+ import { round4 } from "./ease.js";
14
+
15
+ const MAX_DRIFT = 0.01, DECAY = 4, PIVOT_H = 0.03, EASE_STEP = 0.02;
16
+
17
+ export function lockFeet(out, engine) {
18
+ const n = out.frames.length, H = QA_LIMITS.plantH;
19
+ let plants = 0, pivots = 0, worst = 0;
20
+ const touched = new Set();
21
+ for (const [k, S] of [["l", "Left"], ["r", "Right"]]) {
22
+ const ch = out.baked[`foot_${k}`];
23
+ if (!ch) continue;
24
+ const sole = out.frames.map((_, i) => groundSole(out, i, S));
25
+ const planted = (i, key) => sole[i][key][1] < H && sole[i - 1][key][1] < H && Math.abs(sole[i][key][1] - sole[i - 1][key][1]) < 0.0005;
26
+ const delta = Array.from({ length: n }, () => null);
27
+ for (let i = 1; i < n;) {
28
+ if (!planted(i, "ball") && !planted(i, "heel")) { i++; continue; }
29
+ // one contact run: the ball wins over the heel; a switch re-anchors on the new point
30
+ let key = null, ref = null, j = i, max = 0, last = [0, 0, 0], pivot = false;
31
+ const run = [];
32
+ const hold = jj => {
33
+ const d = sub(ref, sole[jj][key]); d[1] = 0;
34
+ last = d;
35
+ run.push([jj, d]);
36
+ max = Math.max(max, Math.hypot(d[0], d[2]));
37
+ };
38
+ for (; j < n && (planted(j, "ball") || planted(j, "heel")); j++) {
39
+ const kj = planted(j, "ball") ? "ball" : "heel";
40
+ pivot ||= sole[j][kj === "ball" ? "heel" : "ball"][1] >= H;
41
+ // on a switch the new point is anchored where the previous frame's shift put it
42
+ if (kj !== key) { key = kj; ref = add(sole[j - 1][key], last); }
43
+ hold(j);
44
+ }
45
+ if (pivot && max >= MAX_DRIFT) for (; j < n && sole[j][key][1] < PIVOT_H; j++) hold(j);
46
+ if (max > 2e-4 && (max < MAX_DRIFT || pivot)) {
47
+ plants++; worst = Math.max(worst, max);
48
+ if (max >= MAX_DRIFT) pivots++;
49
+ for (const [jj, d] of run) delta[jj] = d;
50
+ const decay = Math.max(DECAY, Math.ceil(Math.hypot(last[0], last[2]) / EASE_STEP));
51
+ for (let m = 1; m <= decay && j - 1 + m < n; m++) {
52
+ if (delta[j - 1 + m]) break;
53
+ const t = m / (decay + 1), e = 1 - t * t * (3 - 2 * t);
54
+ delta[j - 1 + m] = [last[0] * e, 0, last[2] * e];
55
+ }
56
+ }
57
+ i = j;
58
+ }
59
+ for (let i = 0; i < n; i++) {
60
+ if (!delta[i]) continue;
61
+ const f = out.f0 + i;
62
+ ch[f] = add(ch[f], delta[i]);
63
+ touched.add(f);
64
+ }
65
+ }
66
+ if (!plants) return null;
67
+ engine.solve([...touched]);
68
+ return { plants, ...(pivots ? { pivots } : {}), frames: touched.size, max: round4(worst),
69
+ how: pivots ? "planted heel/ball held in place; turning feet pivot on it" : "planted heel/ball held in place" };
70
+ }
@@ -0,0 +1,51 @@
1
+ // A planted flat foot keeps its heading (lane Q1). While both its heel and its ball are planted
2
+ // (QA's definition: under plantH over the ground, qa/ground.js, and still), the foot cannot turn without one of them sliding: a
3
+ // flat foot under the body's weight grips the floor. A turn the channels ask for then (fyaw_<k>, or
4
+ // the body's yaw when fyaw_<k> is not keyed) waits: the foot keeps the heading it planted with
5
+ // until its heel or its toes come up or it lifts, and then eases onto the keyed heading at about
6
+ // TURN_RATE degrees a frame. With the heel up it turns on its ball (slide.js holds the ball and
7
+ // pivots the foot there), in the air it turns freely. Session 4db5d447: fyaw_r keyed 0 at f0 and
8
+ // 30 at f37 turned the flat back foot 9 deg over the load (f10-19), and its heel slid 2.2 cm.
9
+ // A foot that turns only while a heel or ball is up, and a flat foot that does not turn, are left
10
+ // as they were.
11
+ import { QA_LIMITS } from "../qa/checks.js";
12
+ import { groundSole } from "../qa/ground.js";
13
+ import { round4 } from "./ease.js";
14
+
15
+ const TURN_RATE = 6, DECAY = 4, TOL = 1;
16
+ const smooth = t => t * t * (3 - 2 * t);
17
+
18
+ /** Holds the heading of flat planted feet (fyaw_<k>), eases it onto the keys once a heel or ball is up; re-solves those frames. */
19
+ export function holdTurns(out, engine) {
20
+ const n = out.frames.length, H = QA_LIMITS.plantH, touched = new Set();
21
+ let worst = 0;
22
+ for (const [k, S] of [["l", "Left"], ["r", "Right"]]) {
23
+ const keyed = out.baked[`fyaw_${k}`] ?? out.baked.yaw;
24
+ if (!out.baked[`foot_${k}`] || !keyed) continue;
25
+ const sole = out.frames.map((_, i) => groundSole(out, i, S));
26
+ const down = (i, key) => sole[i][key][1] < H && sole[i - 1][key][1] < H && Math.abs(sole[i][key][1] - sole[i - 1][key][1]) < 0.0005;
27
+ const flat = i => i > 0 && i < n && down(i, "heel") && down(i, "ball");
28
+ const want = Array.from({ length: n }, (_, i) => keyed[out.f0 + i]), yaw = want.slice();
29
+ for (let i = 1; i < n; i++) {
30
+ if (!flat(i)) continue;
31
+ const held = yaw[i - 1];
32
+ let j = i, turn = 0;
33
+ for (; flat(j); j++) turn = Math.max(turn, Math.abs(yaw[j] - held));
34
+ // a turn within TOL degrees (the heel moves under 3 mm) is left as it is
35
+ if (turn <= TOL) { i = j; continue; }
36
+ worst = Math.max(worst, turn);
37
+ for (let m = i; m < j; m++) yaw[m] = held;
38
+ // then onto the keyed heading, as long as the foot does not plant flat again
39
+ const gap = j < n ? want[j] - held : 0, ease = Math.max(DECAY, Math.ceil(Math.abs(gap) / TURN_RATE));
40
+ for (let m = 0; gap && m < ease && j + m < n && !flat(j + m); m++) yaw[j + m] = want[j + m] - gap * (1 - smooth((m + 1) / (ease + 1)));
41
+ i = j;
42
+ }
43
+ const moved = yaw.map((v, i) => v !== want[i]);
44
+ if (!moved.some(Boolean)) continue;
45
+ const ch = (out.baked[`fyaw_${k}`] = keyed.slice());
46
+ moved.forEach((m, i) => { if (m) { ch[out.f0 + i] = yaw[i]; touched.add(out.f0 + i); } });
47
+ }
48
+ if (!touched.size) return null;
49
+ engine.solve([...touched]);
50
+ return { frames: touched.size, max: round4(worst) };
51
+ }
@@ -0,0 +1,39 @@
1
+ // C16. Follow entries: spec.objects[id] = { follow: <ref>, offset?, aim?, visible?, level? } puts a
2
+ // record (a body, a group root, or a record with no box: an effect, a light, an Empty) on a point
3
+ // of the body at every frame. The ref is any point ref (refs.js): landmark:mouth, joint:<Bone>,
4
+ // prop:<prop>.<point>, anchor:<name>, object:<id> (an object a follow entry does not move).
5
+ // aim where the record's +Y points (effects emit along it): "up" (default, world up), "face"
6
+ // (the face's forward direction, the Head bone's rest +Z, as checks.orient "face"), a
7
+ // world direction [x, y, z], or a ref (toward that point). The record turns the shortest
8
+ // way from upright onto it.
9
+ // offset metres along the record's own axes after aim (+Y along aim), from the ref point.
10
+ // The take bakes a world-space track at every frame (take.js: pos, quat and hold on every frame,
11
+ // so object-travel.js cuts the carry of the record's own parents), so the renderer and the effects
12
+ // (born at the emitter's pose at the birth frame) see the moving path. A follow entry is not an
13
+ // obstacle (compile.js marks its bodies carried) and QA checks it against nothing: it rides the body.
14
+ import { refPoint } from "./refs.js";
15
+ import { carried } from "./compiler/limbs.js";
16
+ import { add, sub, apply, norm, len, matFromQuat } from "./compiler/math.js";
17
+ import { arcQuat } from "./compiler/quat.js";
18
+
19
+ const UP = [0, 1, 0];
20
+
21
+ /** The unit direction aim gives at absolute frame f (inside the window), `from` the followed point. */
22
+ function aimDir(out, f, aim = "up", from) {
23
+ if (Array.isArray(aim)) return norm(aim);
24
+ if (aim === "face") { const { sk } = out.ctx; return norm(carried(sk, out.frames[f - (out.f0 ?? 0)].s, sk.index.Head, [0, 0, 1])); }
25
+ const to = aim === "up" ? null : refPoint(out, f, aim), d = to ? sub(to, from) : UP;
26
+ return len(d) > 1e-9 ? norm(d) : UP;
27
+ }
28
+
29
+ /**
30
+ * World pose { pos, R } of the record follow entry o puts at absolute frame f of compile result
31
+ * out (a frame outside the compiled window takes the nearest one in it), or null when the ref
32
+ * has no point there.
33
+ */
34
+ export function followPose(out, o, f) {
35
+ const lo = out.f0 ?? 0, ff = Math.max(lo, Math.min(lo + out.frames.length - 1, f)), p = refPoint(out, ff, o.follow);
36
+ if (!p) return null;
37
+ const R = matFromQuat(arcQuat(UP, aimDir(out, ff, o.aim, p)));
38
+ return { pos: add(p, apply(R, o.offset ?? [0, 0, 0])), R };
39
+ }
@@ -0,0 +1,173 @@
1
+ // C17. Objects nobody holds (a can inside a machine, a box on a shelf) move by keys and fall.
2
+ // spec.objects[id] with no prop, follow or hinge field, for a body or a group (its root):
3
+ // keys [{ f, pos, rot | aim (+ roll) }] the world pose of its root (a body's base pivot, a group's
4
+ // origin) at frame f, on the curve held keys use (object-keys.js keyedPose, C14). aim turns the
5
+ // scene pose's +Y onto aim, then roll degrees about it. Before the first key of a run it stays
6
+ // where it was (its scene pose, or where it landed), after the last it holds the last key.
7
+ // fall { at, onto?, bounce? } or a list from where it is at frame `at` it drops straight down
8
+ // under gravity (G m/s^2, no tumbling) onto the top of the highest still body under the centre of
9
+ // its parts (onto: that object's top; nothing below: the floor, y 0), bounces at most once (bounce:
10
+ // the restitution 0..0.6, default 0.25, never higher than BOUNCE_MAX m) and rests there.
11
+ // Keys and falls chain in frame order: a fall starts from the pose the keys before it leave (a key on the
12
+ // fall's own frame comes first), keys after a fall take over at their first frame.
13
+ // A carry (C3) takes fall too: before its grab (the hand finds it where it landed) or from its release on.
14
+ // A free object moves like a hinged one (compiler/compile.js objectTracks): an obstacle at its pose that
15
+ // frame, read through object-pose.js by refs, goals, probes, observations.near and QA. Pure over the spec,
16
+ // the scene and the time map (a retime moves its keys and falls with the body's keys): the body never
17
+ // moves it, and nothing it lands on moves (bodies a spec.objects entry moves are no support).
18
+ import { isCarried, compose, relative, movesRecord } from "./carried.js";
19
+ import { groupsOf } from "./scene.js";
20
+ import { curveOf, keyedPose } from "./object-keys.js";
21
+ import { add, apply, sub, T, quatFromMat } from "./compiler/math.js";
22
+
23
+ /** Gravity, m/s^2. */
24
+ export const G = 9.8;
25
+ /** The highest a bounce goes, metres. */
26
+ export const BOUNCE_MAX = 0.03;
27
+ const BOUNCE = 0.25, TOUCH = 0.005;
28
+ const HELD_OR_HINGED = ["prop", "follow", "pivot", "axis", "channel"];
29
+
30
+ /** A free entry (C17): no prop, follow or hinge field, and keys or fall. */
31
+ export const isFree = o => !!o && typeof o === "object" && !Array.isArray(o) && HELD_OR_HINGED.every(k => o[k] === undefined) && (o.keys !== undefined || o.fall !== undefined);
32
+ /** The falls of an entry (fall is one or a list), as given. */
33
+ const fallsOf = o => (o?.fall === undefined ? [] : Array.isArray(o.fall) ? o.fall : [o.fall]);
34
+
35
+ const restOf = (scene, id) => scene?.objects?.[id] ?? groupsOf(scene)[id] ?? null;
36
+ const partsIn = (scene, id) => Object.keys(scene?.objects ?? {}).filter(k => k === id || scene.objects[k].ancestors?.includes(id));
37
+
38
+ /** The 8 corners of a base-pivot box { pos, R, half }. */
39
+ function corners(box) {
40
+ const out = [];
41
+ for (const x of [-1, 1]) for (const y of [0, 2]) for (const z of [-1, 1]) out.push(add(box.pos, apply(box.R, [x * box.half[0], y * box.half[1], z * box.half[2]])));
42
+ return out;
43
+ }
44
+ const topOf = box => Math.max(...corners(box).map(p => p[1]));
45
+ /** True when the vertical through x/z point p crosses the footprint of box. */
46
+ const under = (box, p) => { const q = apply(T(box.R), sub([p[0], box.pos[1] + box.half[1], p[2]], box.pos)); return Math.abs(q[0]) <= box.half[0] && Math.abs(q[2]) <= box.half[2]; };
47
+
48
+ /** Bodies a spec.objects entry moves (itself or a group above it): they hold nothing up. */
49
+ function moversIn(spec, scene) {
50
+ const out = new Set();
51
+ for (const [id, o] of Object.entries(scene?.objects ?? {})) if ([id, ...(o.ancestors ?? [])].some(k => movesRecord(spec.objects?.[k]))) out.add(id);
52
+ return out;
53
+ }
54
+
55
+ /** The part boxes of `id` with its root at world pose `root`. */
56
+ function boxesAtRoot(scene, id, root) {
57
+ const rest = restOf(scene, id);
58
+ return partsIn(scene, id).map(p => ({ id: p, ...compose(root, relative(rest, scene.objects[p])), half: scene.objects[p].half }));
59
+ }
60
+
61
+ /** What `id` lands on from pose `start`: { support (a body id, or null for the floor), top, h (the drop, m) }. */
62
+ export function landingOf(spec, scene, id, start, onto = null) {
63
+ const boxes = boxesAtRoot(scene, id, start), own = new Set(boxes.map(b => b.id)), movers = moversIn(spec, scene);
64
+ if (!boxes.length) return { support: null, top: 0, h: 0 };
65
+ const low = Math.min(...boxes.flatMap(b => corners(b).map(p => p[1])));
66
+ const c = boxes.flatMap(corners).reduce((s, p) => add(s, p), [0, 0, 0]).map(v => v / (boxes.length * 8));
67
+ const pool = (onto ? partsIn(scene, onto) : Object.keys(scene.objects)).filter(k => !own.has(k) && !movers.has(k));
68
+ let best = null;
69
+ for (const k of pool) {
70
+ const box = scene.objects[k], top = topOf(box);
71
+ if (under(box, c) && top <= low + TOUCH && (!best || top > best.top)) best = { support: k, top };
72
+ }
73
+ // onto an object that is not right below: its highest top, the agent placed it
74
+ if (!best && onto) for (const k of pool) { const top = topOf(scene.objects[k]); if (top <= low + TOUCH && (!best || top > best.top)) best = { support: k, top }; }
75
+ best ??= { support: null, top: 0 };
76
+ return { ...best, h: Math.max(0, low - best.top) };
77
+ }
78
+
79
+ /** A fall from root pose `start` at (mapped) frame t0: its timing, its pose at a frame, where it rests. */
80
+ function fallPlan(spec, scene, id, start, t0, fall, fps) {
81
+ const land = landingOf(spec, scene, id, start, fall.onto ?? null), h = land.h, tl = Math.sqrt(2 * h / G);
82
+ const e = Math.min(0.6, Math.max(0, fall.bounce ?? BOUNCE)), vb = Math.min(e * G * tl, Math.sqrt(2 * G * BOUNCE_MAX)), tb = 2 * vb / G;
83
+ const rest = { pos: [start.pos[0], start.pos[1] - h, start.pos[2]], R: start.R };
84
+ const at = f => {
85
+ const t = (f - t0) / fps;
86
+ if (t <= 0) return start;
87
+ const dy = t <= tl ? -0.5 * G * t * t : t <= tl + tb ? -h + vb * (t - tl) - 0.5 * G * (t - tl) ** 2 : -h;
88
+ return { pos: [start.pos[0], start.pos[1] + Math.min(0, dy), start.pos[2]], R: start.R };
89
+ };
90
+ return { t0, land: t0 + tl * fps, end: t0 + (tl + tb) * fps, h, support: land.support, top: land.top, at, rest };
91
+ }
92
+
93
+ /** The events of an entry's falls and key runs in frame order (keys split into runs by the falls between them). */
94
+ function eventsOf(o, map, from = -Infinity, to = Infinity) {
95
+ // a carry's keys pose it in hand (C14); only its falls are free
96
+ const keys = isCarried(o) ? [] : [...(o.keys ?? [])].sort((a, b) => a.f - b.f);
97
+ const falls = fallsOf(o).map(fl => ({ ...fl, t: map(fl.at) })).filter(fl => fl.t >= from && fl.t < to).sort((a, b) => a.t - b.t);
98
+ const events = [];
99
+ let run = [], i = 0;
100
+ for (const k of keys) {
101
+ while (i < falls.length && falls[i].t < map(k.f)) { if (run.length) events.push({ run }); run = []; events.push({ fall: falls[i++] }); }
102
+ run.push(k);
103
+ }
104
+ if (run.length) events.push({ run });
105
+ while (i < falls.length) events.push({ fall: falls[i++] });
106
+ return events;
107
+ }
108
+
109
+ /** The pose events give at mapped frame f, starting from pose `start`; `keep` caches each fall's plan on its event. */
110
+ function walk(events, f, start, plan, keep = true) {
111
+ let pose = start;
112
+ for (const e of events) {
113
+ if (e.run) {
114
+ const { t } = e.curve;
115
+ if (f < t[0]) return pose;
116
+ if (f <= t[t.length - 1]) return keyedPose(e.curve, f);
117
+ pose = keyedPose(e.curve, t[t.length - 1]);
118
+ } else {
119
+ if (f < e.fall.t) return pose;
120
+ const p = keep ? (e.plan ??= plan(pose, e.fall)) : plan(pose, e.fall);
121
+ if (f < p.end) return p.at(f);
122
+ pose = p.rest;
123
+ }
124
+ }
125
+ return pose;
126
+ }
127
+
128
+ const timelines = new WeakMap();
129
+ /** The events of entry o under time map `map`, cached per scene, entry and span (key curves built once). */
130
+ function timelineOf(ctx, id, o, map, part) {
131
+ if (!timelines.has(ctx.scene)) timelines.set(ctx.scene, new Map());
132
+ const cache = timelines.get(ctx.scene), key = `${id}|${part}|${ctx.grab}|${ctx.release}`, had = cache.get(key);
133
+ if (had && had.map === map && had.o === o && had.spec === ctx.spec) return had.events;
134
+ const rest = restOf(ctx.scene, id), m = map ?? (f => f), [from, to] = part === "before" ? [-Infinity, ctx.grab] : part === "after" ? [ctx.release, Infinity] : [-Infinity, Infinity];
135
+ const events = eventsOf(o, m, from, to);
136
+ for (const e of events) if (e.run) e.curve = curveOf({ keys: e.run }, m, () => quatFromMat(rest.R), null);
137
+ cache.set(key, { map, o, spec: ctx.spec, events });
138
+ return events;
139
+ }
140
+
141
+ const planner = (ctx, id) => (start, fall) => fallPlan(ctx.spec, ctx.scene, id, start, fall.t, fall, ctx.spec.fps ?? 30);
142
+
143
+ /**
144
+ * World pose { pos, R } of the root of free entry `id` at absolute frame f (ctx = { spec, scene,
145
+ * timeMap }): keys and falls (C17) from its scene pose. null for an id the scene does not know.
146
+ */
147
+ export function freePoseAt(ctx, id, f) {
148
+ const rest = restOf(ctx.scene, id), o = ctx.spec.objects?.[id];
149
+ if (!rest || !o) return null;
150
+ return walk(timelineOf(ctx, id, o, ctx.timeMap, "all"), f, { pos: rest.pos, R: rest.R }, planner(ctx, id));
151
+ }
152
+
153
+ /** Where carry `id` (entry o) is before its grab at absolute f: its scene pose, or where a fall before grab left it. */
154
+ export function beforeGrab(ctx, id, o, f, grab) {
155
+ const rest = restOf(ctx.scene, id);
156
+ if (!rest || !fallsOf(o).length) return rest && { pos: rest.pos, R: rest.R };
157
+ return walk(timelineOf({ ...ctx, grab }, id, o, ctx.timeMap, "before"), f, { pos: rest.pos, R: rest.R }, planner(ctx, id));
158
+ }
159
+
160
+ /** Where carry `id` (entry o) is at absolute f after its release, from its release pose; null without a fall from the release on. */
161
+ export function afterRelease(ctx, id, o, f, release, releasePose) {
162
+ const events = fallsOf(o).length ? timelineOf({ ...ctx, release }, id, o, ctx.timeMap, "after") : [];
163
+ // the release pose comes from the solved body, which a rebake changes: plan these falls afresh
164
+ return events.length ? walk(events, f, releasePose, planner(ctx, id), false) : null;
165
+ }
166
+
167
+ /** The falls of entry `id` as planned under the current time map, for readers (timeline, tests): [{ t0, land, end, h, support, top }]. */
168
+ export function fallPlans(ctx, id) {
169
+ const o = ctx.spec.objects?.[id];
170
+ if (!isFree(o)) return [];
171
+ freePoseAt(ctx, id, Infinity);
172
+ return timelineOf(ctx, id, o, ctx.timeMap, "all").filter(e => e.plan).map(({ plan: { t0, land, end, h, support, top } }) => ({ t0, land, end, h, support, top }));
173
+ }