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,115 @@
1
+ import * as THREE from "three";
2
+ import { objectSize } from "./scene-objects.js";
3
+
4
+ // The ObjectGizmo's fixed parts: handle layout, sizes, colours and the two
5
+ // placement rules (where the gizmo stands, and which way its rings face).
6
+ export const AXES = [
7
+ { axis: "x", dir: new THREE.Vector3(1, 0, 0), color: "#ff5340" },
8
+ { axis: "y", dir: new THREE.Vector3(0, 1, 0), color: "#54e05c" },
9
+ { axis: "z", dir: new THREE.Vector3(0, 0, 1), color: "#3d8bff" },
10
+ ];
11
+ /** the same three directions, addressable by the record's axis key */
12
+ export const AXIS_VECTORS = { x: AXES[0].dir, y: AXES[1].dir, z: AXES[2].dir };
13
+ /** Move-tool plane handles. Unity colours each square after the axis it LOCKS,
14
+ * so the blue square (z locked) slides in xy. `normal` is the pinned axis. */
15
+ export const PLANES = [
16
+ { id: "xz", axes: ["x", "z"], normal: "y", color: "#54e05c" },
17
+ { id: "xy", axes: ["x", "y"], normal: "z", color: "#3d8bff" },
18
+ { id: "yz", axes: ["y", "z"], normal: "x", color: "#ff5340" },
19
+ ];
20
+ export const PLANE_OFFSET = 0.26; // where the square sits along each of its two axes
21
+ export const PLANE_SIZE = 0.2;
22
+ export const ARROW_LEN = 0.62;
23
+ export const SHAFT_R = 0.016;
24
+ export const TIP_R = 0.05;
25
+ export const TIP_LEN = 0.14;
26
+ export const RING_R = 0.52;
27
+ /**
28
+ * The axis rings are drawn as two half-tori instead of one full torus. A full
29
+ * ring is misleading: the half facing away from the camera is pixel-identical
30
+ * to the near half, so a drag started on the far side reads backwards.
31
+ * Depth-sorting the ring against the object would only help when the object
32
+ * is big enough to occlude the far half — the gizmo sits at the object's
33
+ * centre and the ring is only 0.52 m across, so for the small props a
34
+ * blocking tool is full of, the far half projects outside the silhouette and
35
+ * stays ambiguous. Fading it instead (FAR_HALF_ALPHA vs the near half's
36
+ * 0.95) is unambiguous at every object size and camera angle. Only the drawn
37
+ * halves fade — the pick proxy stays one full invisible torus per ring.
38
+ */
39
+ export const FAR_HALF_ALPHA = 0.28;
40
+ /** the outer screen-space ring: larger than the axis rings (RING_R) so it
41
+ * encloses them from every angle, and far enough out that its pick band
42
+ * [0.61, 0.75] clears the axis rings' [0.45, 0.59] — the two can never fight
43
+ * for a grab. (§3.3: "a larger circle enclosing the sphere, drawn flat to
44
+ * the viewer" — rotates about the view axis.) */
45
+ export const SCREEN_RING_R = 0.68;
46
+ /** Unity's yellow for the handle being dragged — and, after release, for the
47
+ * last-dragged one, until the selection or the tool changes (§3.1). */
48
+ export const ACTIVE_COLOR = "#ffd23d";
49
+ export const RING_TUBE = 0.022;
50
+ export const BOX_SIZE = 0.09; // scale-mode axis knobs
51
+ export const CENTRE_BOX = 0.11; // uniform-scale knob at the pivot
52
+ /** Pick proxies. A drawn 1.6 cm shaft is ~3 screen pixels — nothing anyone can
53
+ * reliably grab — so hit testing runs against invisible fat volumes around each
54
+ * arrow, ring and knob, the way every DCC gizmo does it. */
55
+ export const PICK_SHAFT_R = 0.09;
56
+ export const PICK_TUBE_R = 0.07;
57
+ export const PICK_CENTRE_R = 0.16;
58
+ /** metres of gizmo per metre of camera distance: keeps it a constant size on
59
+ * screen whether the object is at the lens or across the room */
60
+ export const SCREEN_SCALE = 0.16;
61
+
62
+ /** the object's own centre height, where the gizmo sits */
63
+ function gizmoHeight(object) {
64
+ return (object.y ?? 0) + Math.max(objectSize(object).height / 2, 0.25);
65
+ }
66
+
67
+ /** the handles-Map key a pick entry was registered under — the same
68
+ * expression __gizmoPick reports, so highlights and QA hooks agree on handle
69
+ * identity */
70
+ export function handleKey(entry) {
71
+ if (entry.corner) return `corner:${entry.corner.id}`;
72
+ return entry.axis ?? (entry.plane ? `plane:${entry.plane.id}` : "centre");
73
+ }
74
+
75
+ /**
76
+ * The four corners of a cutout card, in the card's own local space.
77
+ *
78
+ * A standee is a picture, and a picture is resized by its corners — grabbing
79
+ * one and pulling is the gesture everybody already has for "make this bigger",
80
+ * and unlike an axis knob it says which way is bigger without reading a label.
81
+ * `sx`/`sy` are which way each corner lies from the centre, so the drag can
82
+ * measure travel along the diagonal that corner actually points down.
83
+ */
84
+ export const CARD_CORNERS = [
85
+ { id: "tr", sx: 1, sy: 1 },
86
+ { id: "tl", sx: -1, sy: 1 },
87
+ { id: "br", sx: 1, sy: -1 },
88
+ { id: "bl", sx: -1, sy: -1 },
89
+ ];
90
+ export const CORNER_BOX = 0.085;
91
+ export const PICK_CORNER_R = 0.13;
92
+
93
+ /**
94
+ * Screen-space ring result: the object rotated `deltaDeg` about the world
95
+ * `viewAxis`, written back as all three Euler channels in one patch. The
96
+ * stored channels are Euler degrees per world axis, so the drag-start
97
+ * orientation is composed with the delta in quaternion space and decomposed
98
+ * again in the same order the renderer uses — exact for any start
99
+ * orientation, where a single-channel approximation would drift. Snap
100
+ * quantizes the accumulated delta to the same 5° grid the axis rings snap
101
+ * their start+delta to (start values are always on the grid themselves).
102
+ */
103
+
104
+ // Path authoring intersects presses with the set floor, exactly where a
105
+ // dropped pin will live.
106
+ export const GROUND = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
107
+ /** Where the gizmo stands: on the pose a take draws the object at (`held.pose`, held-object-edit.jsx), else at the authored centre. */
108
+ export function gizmoOrigin(object, held, out) {
109
+ return held?.pose ? out.fromArray(held.pose.centre) : out.set(object.x, gizmoHeight(object), object.z);
110
+ }
111
+
112
+ /** The rotation the handles take: the drawn object's own axes while a held-object drag edits it in rotate mode, else none (world axes). */
113
+ export function gizmoTurn(held, mode) {
114
+ return mode === "rotate" && held?.editable ? held.pose.quat : null;
115
+ }
@@ -3,15 +3,11 @@ import { useFrame, useThree } from "@react-three/fiber";
3
3
  import * as THREE from "three";
4
4
  import { GIZMO_LAYER, SHOT_ASPECT, fitAspect } from "./dualview.jsx";
5
5
  import { HANDLE_PROXY_FLAG, claimsPress } from "./gizmo-claim.js";
6
- import {
7
- CUTOUT_KIND,
8
- objectSize,
9
- rotatePatch,
10
- scalePatch,
11
- screenRotatePatch,
12
- translatePatch,
13
- wrapAngle,
14
- } from "./scene-objects.js";
6
+ import { CUTOUT_KIND, rotatePatch, scalePatch, screenRotatePatch, translatePatch, wrapAngle } from "./scene-objects.js";
7
+ import { dragTravel, dragTurn } from "./held-object-pose.js";
8
+ import { HeldTrail } from "./held-object-edit.jsx";
9
+ import { ACTIVE_COLOR, ARROW_LEN, AXES, AXIS_VECTORS, BOX_SIZE, CARD_CORNERS, CENTRE_BOX, CORNER_BOX, FAR_HALF_ALPHA, GROUND, PICK_CENTRE_R, PICK_CORNER_R, PICK_SHAFT_R, PICK_TUBE_R,
10
+ PLANES, PLANE_OFFSET, PLANE_SIZE, RING_R, RING_TUBE, SCREEN_RING_R, SCREEN_SCALE, SHAFT_R, TIP_LEN, TIP_R, gizmoOrigin, gizmoTurn, handleKey } from "./object-gizmo-parts.js";
15
11
 
16
12
  /**
17
13
  * The transform gizmo for the selected scene object — the thing every 3D tool
@@ -46,109 +42,8 @@ import {
46
42
  * space and decomposed back into the same channels (screenRotatePatch).
47
43
  */
48
44
 
49
- const AXES = [
50
- { axis: "x", dir: new THREE.Vector3(1, 0, 0), color: "#ff5340" },
51
- { axis: "y", dir: new THREE.Vector3(0, 1, 0), color: "#54e05c" },
52
- { axis: "z", dir: new THREE.Vector3(0, 0, 1), color: "#3d8bff" },
53
- ];
54
- /** the same three directions, addressable by the record's axis key */
55
- const AXIS_VECTORS = { x: AXES[0].dir, y: AXES[1].dir, z: AXES[2].dir };
56
- /** Move-tool plane handles. Unity colours each square after the axis it LOCKS,
57
- * so the blue square (z locked) slides in xy. `normal` is the pinned axis. */
58
- const PLANES = [
59
- { id: "xz", axes: ["x", "z"], normal: "y", color: "#54e05c" },
60
- { id: "xy", axes: ["x", "y"], normal: "z", color: "#3d8bff" },
61
- { id: "yz", axes: ["y", "z"], normal: "x", color: "#ff5340" },
62
- ];
63
- const PLANE_OFFSET = 0.26; // where the square sits along each of its two axes
64
- const PLANE_SIZE = 0.2;
65
- const ARROW_LEN = 0.62;
66
- const SHAFT_R = 0.016;
67
- const TIP_R = 0.05;
68
- const TIP_LEN = 0.14;
69
- const RING_R = 0.52;
70
- /**
71
- * The axis rings are drawn as two half-tori instead of one full torus. A full
72
- * ring is misleading: the half facing away from the camera is pixel-identical
73
- * to the near half, so a drag started on the far side reads backwards.
74
- * Depth-sorting the ring against the object would only help when the object
75
- * is big enough to occlude the far half — the gizmo sits at the object's
76
- * centre and the ring is only 0.52 m across, so for the small props a
77
- * blocking tool is full of, the far half projects outside the silhouette and
78
- * stays ambiguous. Fading it instead (FAR_HALF_ALPHA vs the near half's
79
- * 0.95) is unambiguous at every object size and camera angle. Only the drawn
80
- * halves fade — the pick proxy stays one full invisible torus per ring.
81
- */
82
- const FAR_HALF_ALPHA = 0.28;
83
- /** the outer screen-space ring: larger than the axis rings (RING_R) so it
84
- * encloses them from every angle, and far enough out that its pick band
85
- * [0.61, 0.75] clears the axis rings' [0.45, 0.59] — the two can never fight
86
- * for a grab. (§3.3: "a larger circle enclosing the sphere, drawn flat to
87
- * the viewer" — rotates about the view axis.) */
88
- const SCREEN_RING_R = 0.68;
89
- /** Unity's yellow for the handle being dragged — and, after release, for the
90
- * last-dragged one, until the selection or the tool changes (§3.1). */
91
- const ACTIVE_COLOR = "#ffd23d";
92
- const RING_TUBE = 0.022;
93
- const BOX_SIZE = 0.09; // scale-mode axis knobs
94
- const CENTRE_BOX = 0.11; // uniform-scale knob at the pivot
95
- /** Pick proxies. A drawn 1.6 cm shaft is ~3 screen pixels — nothing anyone can
96
- * reliably grab — so hit testing runs against invisible fat volumes around each
97
- * arrow, ring and knob, the way every DCC gizmo does it. */
98
- const PICK_SHAFT_R = 0.09;
99
- const PICK_TUBE_R = 0.07;
100
- const PICK_CENTRE_R = 0.16;
101
- /** metres of gizmo per metre of camera distance: keeps it a constant size on
102
- * screen whether the object is at the lens or across the room */
103
- const SCREEN_SCALE = 0.16;
104
-
105
- /** the object's own centre height, where the gizmo sits */
106
- function gizmoHeight(object) {
107
- return (object.y ?? 0) + Math.max(objectSize(object).height / 2, 0.25);
108
- }
109
-
110
- /** the handles-Map key a pick entry was registered under — the same
111
- * expression __gizmoPick reports, so highlights and QA hooks agree on handle
112
- * identity */
113
- function handleKey(entry) {
114
- if (entry.corner) return `corner:${entry.corner.id}`;
115
- return entry.axis ?? (entry.plane ? `plane:${entry.plane.id}` : "centre");
116
- }
117
-
118
- /**
119
- * The four corners of a cutout card, in the card's own local space.
120
- *
121
- * A standee is a picture, and a picture is resized by its corners — grabbing
122
- * one and pulling is the gesture everybody already has for "make this bigger",
123
- * and unlike an axis knob it says which way is bigger without reading a label.
124
- * `sx`/`sy` are which way each corner lies from the centre, so the drag can
125
- * measure travel along the diagonal that corner actually points down.
126
- */
127
- const CARD_CORNERS = [
128
- { id: "tr", sx: 1, sy: 1 },
129
- { id: "tl", sx: -1, sy: 1 },
130
- { id: "br", sx: 1, sy: -1 },
131
- { id: "bl", sx: -1, sy: -1 },
132
- ];
133
- const CORNER_BOX = 0.085;
134
- const PICK_CORNER_R = 0.13;
135
-
136
- /**
137
- * Screen-space ring result: the object rotated `deltaDeg` about the world
138
- * `viewAxis`, written back as all three Euler channels in one patch. The
139
- * stored channels are Euler degrees per world axis, so the drag-start
140
- * orientation is composed with the delta in quaternion space and decomposed
141
- * again in the same order the renderer uses — exact for any start
142
- * orientation, where a single-channel approximation would drift. Snap
143
- * quantizes the accumulated delta to the same 5° grid the axis rings snap
144
- * their start+delta to (start values are always on the grid themselves).
145
- */
146
-
147
- // Path authoring intersects presses with the set floor, exactly where a
148
- // dropped pin will live.
149
- const GROUND = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
150
45
 
151
- export default function ObjectGizmo({ object, objects = [], mode = "move", snap = true, enabled, pickOnly = false, paneRef, camRef, shotAspect = SHOT_ASPECT, onChange, onSelect, onGroundClick, onDragStart, onDragEnd, claimPointer }) {
46
+ export default function ObjectGizmo({ object, objects = [], mode = "move", snap = true, enabled, pickOnly = false, paneRef, camRef, shotAspect = SHOT_ASPECT, onChange, onSelect, onGroundClick, onDragStart, onDragEnd, claimPointer, held = null }) {
152
47
  const { gl, scene } = useThree();
153
48
  // shotAspect null = the camera fills the pane (the editor working view):
154
49
  // pointer mapping uses the pane rect itself instead of a letterboxed
@@ -174,7 +69,10 @@ export default function ObjectGizmo({ object, objects = [], mode = "move", snap
174
69
  const hoverRef = useRef(null); // last hovered key: pointer moves that keep it skip the re-render
175
70
  const dragRef = useRef(null);
176
71
  const stateRef = useRef(null);
177
- stateRef.current = { object, objects, mode, snap, onChange, onSelect, onGroundClick, onDragStart, onDragEnd, claimPointer };
72
+ // `held` (held-object-edit.jsx): the object is drawn by a compiled take. The
73
+ // gizmo stands on that drawn pose, and while a hand holds the object a move
74
+ // or rotate drag edits its motion through `held` instead of the record.
75
+ stateRef.current = { object, objects, mode, snap, onChange, onSelect, onGroundClick, onDragStart, onDragEnd, claimPointer, held };
178
76
  const tools = useMemo(
179
77
  () => ({
180
78
  raycaster: new THREE.Raycaster(),
@@ -347,6 +245,7 @@ export default function ObjectGizmo({ object, objects = [], mode = "move", snap
347
245
  // the moment the pointer crosses the ring's ±180° seam.
348
246
  drag.turned += wrapAngle(((angle - drag.lastAngle) * 180) / Math.PI);
349
247
  drag.lastAngle = angle;
248
+ if (drag.held) return void stateRef.current.held?.rotate(drag.dir, dragTurn(drag.turned, snapping ? 5 : 0));
350
249
  if (drag.axis === "screen") {
351
250
  // The screen ring spins about the view axis: compose the
352
251
  // delta onto the drag-start orientation in quaternion space
@@ -399,6 +298,7 @@ export default function ObjectGizmo({ object, objects = [], mode = "move", snap
399
298
  return;
400
299
  }
401
300
  tools.delta.subVectors(tools.hit, drag.hitStart);
301
+ if (drag.held) return void stateRef.current.held?.move(dragTravel(tools.delta, drag.planeAxes ? drag.planeAxes.map((axis) => AXIS_VECTORS[axis]) : [drag.dir], snapping ? 0.05 : 0));
402
302
  if (drag.planeAxes) {
403
303
  // Plane handle: two axes travel, the third is pinned. This is what
404
304
  // replaced body-dragging — the XZ square is how a set gets dressed.
@@ -438,7 +338,8 @@ export default function ObjectGizmo({ object, objects = [], mode = "move", snap
438
338
  const end = stateRef.current.onDragEnd;
439
339
  const token = drag.token;
440
340
  teardownDrag();
441
- if (token != null && end) end(token, { commit });
341
+ if (drag.held) stateRef.current.held?.end(commit);
342
+ else if (token != null && end) end(token, { commit });
442
343
  };
443
344
 
444
345
  /** pointerup and pointercancel both commit: the travel already applied
@@ -482,7 +383,8 @@ export default function ObjectGizmo({ object, objects = [], mode = "move", snap
482
383
  // (a jump of however far the car has driven). The pivot, and the yaw
483
384
  // the card's corners are seen at, are the shown ones.
484
385
  const live = stateRef.current.objects.find((entry) => entry.id === shown.id) ?? shown;
485
- tools.origin.set(shown.x, gizmoHeight(shown), shown.z);
386
+ const heldEdit = stateRef.current.held;
387
+ gizmoOrigin(shown, heldEdit, tools.origin);
486
388
  camera.getWorldDirection(tools.eye);
487
389
  let plane;
488
390
  let drag;
@@ -491,7 +393,9 @@ export default function ObjectGizmo({ object, objects = [], mode = "move", snap
491
393
  // pivot. The screen ring has no axis of its own: it spins
492
394
  // about the camera's own forward, so its plane is the
493
395
  // camera-facing plane through the pivot.
494
- const spinDir = axis === "screen" ? tools.eye : dir;
396
+ // A held object turns about its own axes (its rings are drawn turned too).
397
+ const turn = gizmoTurn(heldEdit, kind);
398
+ const spinDir = axis === "screen" ? tools.eye : turn ? dir.clone().applyQuaternion(new THREE.Quaternion(...turn)) : dir;
495
399
  plane = new THREE.Plane().setFromNormalAndCoplanarPoint(spinDir, tools.origin.clone());
496
400
  const hit = new THREE.Vector3();
497
401
  if (!tools.raycaster.ray.intersectPlane(plane, hit)) return false;
@@ -577,12 +481,14 @@ export default function ObjectGizmo({ object, objects = [], mode = "move", snap
577
481
  // (§6.1). The `cancel` handed over is teardown-only — the
578
482
  // coordinator owns the close, and a close attempted from inside it
579
483
  // is inert because the token is retired first (§5.2).
580
- const token = stateRef.current.onDragStart?.({
484
+ // A held-object drag edits the motion, not the record: no scene transaction.
485
+ const heldDrag = Boolean(heldEdit?.editable && (kind === "rotate" || (!heldEdit.hinge && (kind === "move" || kind === "plane"))) && heldEdit.begin());
486
+ const token = heldDrag ? undefined : stateRef.current.onDragStart?.({
581
487
  owner: "gizmo",
582
488
  cancel: teardownDrag,
583
489
  });
584
490
  const corner = kind === "corner" ? dir : null;
585
- dragRef.current = { ...drag, id: live.id, axis, plane, corner, start: { ...live }, token };
491
+ dragRef.current = { ...drag, id: live.id, axis, plane, corner, start: { ...live }, token, held: heldDrag };
586
492
  // the grabbed handle turns yellow now and stays yellow after
587
493
  // release, until the selection or the tool changes (§3.1)
588
494
  setActiveHandle(handleKey({ axis, plane, corner }));
@@ -593,7 +499,7 @@ export default function ObjectGizmo({ object, objects = [], mode = "move", snap
593
499
  // Only a real transaction gets the extra lifecycle edges: blur and
594
500
  // Escape would otherwise change mid-drag behaviour for callers that
595
501
  // pass no transaction props (the byte-for-byte fallback path).
596
- if (token != null) {
502
+ if (token != null || heldDrag) {
597
503
  window.addEventListener("blur", onBlur);
598
504
  window.addEventListener("keydown", onKeyDown, true);
599
505
  }
@@ -753,7 +659,7 @@ export default function ObjectGizmo({ object, objects = [], mode = "move", snap
753
659
  const camera = camRef?.current;
754
660
  if (!root || !camera || !object) return;
755
661
  camera.getWorldPosition(tools.camPos);
756
- tools.origin.set(object.x, gizmoHeight(object), object.z);
662
+ gizmoOrigin(object, held, tools.origin);
757
663
  root.scale.setScalar(Math.max(0.35, tools.camPos.distanceTo(tools.origin) * SCREEN_SCALE));
758
664
  if (mode !== "rotate") return;
759
665
  // The rotate rings are view-dependent, so their orientation is frame
@@ -764,6 +670,14 @@ export default function ObjectGizmo({ object, objects = [], mode = "move", snap
764
670
  camera.getWorldDirection(tools.eye);
765
671
  const screen = screenRingRef.current;
766
672
  if (screen) screen.quaternion.copy(camera.quaternion);
673
+ // Turned rings (a held object's own axes): the near/far split and the
674
+ // billboard are worked out in the gizmo's own frame.
675
+ const turn = gizmoTurn(held, mode);
676
+ if (turn) {
677
+ const unturn = new THREE.Quaternion(...turn).invert();
678
+ tools.eye.applyQuaternion(unturn);
679
+ screen?.quaternion.premultiply(unturn);
680
+ }
767
681
  for (const { axis, u, v } of ringBases) {
768
682
  const ring = ringGroupsRef.current[axis];
769
683
  if (!ring) continue;
@@ -855,8 +769,9 @@ export default function ObjectGizmo({ object, objects = [], mode = "move", snap
855
769
  const screenHovered = hoveredHandle === "screen";
856
770
  const centreActive = activeHandle === "centre";
857
771
  const centreHovered = hoveredHandle === "centre";
858
- return (
859
- <group ref={rootRef} position={[object.x, gizmoHeight(object), object.z]} renderOrder={999}>
772
+ return (<>
773
+ <HeldTrail trail={held?.trail ?? null} />
774
+ <group ref={rootRef} position={gizmoOrigin(object, held, new THREE.Vector3()).toArray()} quaternion={gizmoTurn(held, mode) ?? [0, 0, 0, 1]} renderOrder={999}>
860
775
  {AXES.map(({ axis, dir, color }) => {
861
776
  const quat = new THREE.Quaternion().setFromUnitVectors(new THREE.Vector3(0, 1, 0), dir);
862
777
  // The handle under a drag — and, after release, the last one —
@@ -1033,5 +948,5 @@ export default function ObjectGizmo({ object, objects = [], mode = "move", snap
1033
948
  </>
1034
949
  )}
1035
950
  </group>
1036
- );
951
+ </>);
1037
952
  }
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Glow: a record that gives off light of its own, drawn in one pass.
3
+ *
4
+ * There is no post-processing anywhere (the viewport, the shot preview,
5
+ * capture_frame, contact sheets and the video export all render the scene with
6
+ * one gl.render), so a bloom pass would have to be bolted onto every path.
7
+ * Instead a glow is two single-pass parts, both on the default layer:
8
+ * - an emissive colour on every material the record draws, which no stage
9
+ * light can darken, and
10
+ * - an additive, camera-facing halo sprite centred on the record, `halo` ×
11
+ * the record's radius. It writes no depth, casts no shadow and is never
12
+ * raycast, so it cannot occlude, shadow or be picked.
13
+ * Both scale with `gain` (the take's level, times a light's flicker).
14
+ * A glowing record is self-lit, so its meshes cast no shadow while it glows: a
15
+ * practical light placed inside a glowing bulb or flame lights the set instead
16
+ * of being shut in by the bulb's own faces (light-blockers.js).
17
+ *
18
+ * State is kept per group in a WeakMap, never in `userData` (React replaces a
19
+ * group's userData on every render). Removing the glow restores each
20
+ * material's own emissive and drops the halo.
21
+ */
22
+
23
+ import * as THREE from "three";
24
+
25
+ /** Halo brightness per unit of glow intensity. */
26
+ const HALO_GAIN = 0.2;
27
+ /** Radius used for a record with no size of its own (a light, an Empty), metres. */
28
+ const POINT_RADIUS = 0.02;
29
+ const noRaycast = () => {};
30
+ const glowStates = new WeakMap();
31
+
32
+ /** True while the group carries a glow that a later frame may have to remove. */
33
+ export function hasGlow(group) {
34
+ return glowStates.has(group);
35
+ }
36
+
37
+ let haloMap = null;
38
+ /** A white disc whose alpha is a bright core over a long soft tail. */
39
+ function haloTexture() {
40
+ if (haloMap) return haloMap;
41
+ const size = 64;
42
+ const data = new Uint8Array(size * size * 4);
43
+ for (let y = 0; y < size; y += 1) {
44
+ for (let x = 0; x < size; x += 1) {
45
+ const r = Math.hypot(x + 0.5 - size / 2, y + 0.5 - size / 2) / (size / 2);
46
+ const tail = r >= 1 ? 0 : (1 - r) ** 1.6;
47
+ const alpha = Math.min(1, 0.7 * Math.exp(-((r / 0.25) ** 2)) + 0.5 * tail);
48
+ data.set([255, 255, 255, Math.round(alpha * 255)], (y * size + x) * 4);
49
+ }
50
+ }
51
+ haloMap = new THREE.DataTexture(data, size, size);
52
+ haloMap.magFilter = THREE.LinearFilter;
53
+ haloMap.minFilter = THREE.LinearFilter;
54
+ haloMap.needsUpdate = true;
55
+ return haloMap;
56
+ }
57
+
58
+ function makeHalo() {
59
+ const material = new THREE.SpriteMaterial({ map: haloTexture(), blending: THREE.AdditiveBlending, transparent: true, depthWrite: false, fog: false });
60
+ const halo = new THREE.Sprite(material);
61
+ halo.userData.glowHalo = true;
62
+ // Beauty render only: the ink prepass and the depth/normal passes draw with
63
+ // an override material, which would turn the soft halo into a solid quad.
64
+ halo.userData.beautyOnly = true;
65
+ halo.raycast = noRaycast;
66
+ halo.castShadow = false;
67
+ halo.receiveShadow = false;
68
+ halo.renderOrder = 2;
69
+ return halo;
70
+ }
71
+
72
+ /** Each mesh the record draws on the default layer, and each of its lit
73
+ * materials (editor markers and the halo itself are left alone). */
74
+ function forEachGlowMesh(group, visitMesh, visitMaterial) {
75
+ group.traverse((node) => {
76
+ if (!node.isMesh || !node.layers.isEnabled(0)) return;
77
+ visitMesh(node);
78
+ for (const material of Array.isArray(node.material) ? node.material : [node.material]) {
79
+ if (material?.emissive?.isColor) visitMaterial(material);
80
+ }
81
+ });
82
+ }
83
+
84
+ function clearGlow(group, state) {
85
+ for (const [material, base] of state.bases) {
86
+ material.emissive.copy(base.emissive);
87
+ material.emissiveIntensity = base.intensity;
88
+ }
89
+ for (const mesh of state.shadowless) mesh.castShadow = true;
90
+ state.halo.removeFromParent();
91
+ state.halo.material.dispose();
92
+ glowStates.delete(group);
93
+ }
94
+
95
+ /** The record's centre (local) and its radius (world): half its largest
96
+ * extent, so a sphere's radius is exact; a record with no size (a light, an
97
+ * Empty) counts as POINT_RADIUS. Size comes from the record, the same numbers
98
+ * the selection cage and the plan use. */
99
+ function recordSphere(object, scale) {
100
+ const width = (object.footprint?.width ?? 0) * Math.abs(scale.x);
101
+ const height = (object.height ?? 0) * Math.abs(scale.y);
102
+ const depth = (object.footprint?.depth ?? 0) * Math.abs(scale.z);
103
+ const radius = Math.max(width, height, depth) / 2;
104
+ return { centreY: (object.height ?? 0) / 2, radius: radius > 0 ? radius : POINT_RADIUS };
105
+ }
106
+
107
+ /** Apply, update or remove the record's glow for this frame. */
108
+ export function placeObjectGlow(group, object, gain) {
109
+ const glow = object.glow;
110
+ let state = glowStates.get(group);
111
+ if (!glow) {
112
+ if (state) clearGlow(group, state);
113
+ return;
114
+ }
115
+ if (!state) {
116
+ state = { halo: makeHalo(), bases: new Map(), shadowless: new Set() };
117
+ glowStates.set(group, state);
118
+ }
119
+ if (state.halo.parent !== group) group.add(state.halo);
120
+ const strength = glow.intensity * gain;
121
+ forEachGlowMesh(group, (mesh) => {
122
+ // a mesh rebuilt while glowing (a new kind, a loaded file) casts again until this pass
123
+ if (!mesh.castShadow) return;
124
+ mesh.castShadow = false;
125
+ state.shadowless.add(mesh);
126
+ }, (material) => {
127
+ if (!state.bases.has(material)) state.bases.set(material, { emissive: material.emissive.clone(), intensity: material.emissiveIntensity });
128
+ material.emissive.set(glow.color);
129
+ material.emissiveIntensity = strength;
130
+ });
131
+ const { centreY, radius } = recordSphere(object, group.scale);
132
+ const size = 2 * radius * glow.halo * gain;
133
+ const halo = state.halo;
134
+ halo.visible = size > 0 && strength > 0;
135
+ halo.position.set(0, centreY, 0);
136
+ // The sprite inherits the record's scale; divide it out so the halo stays round.
137
+ halo.scale.set(size / (Math.abs(group.scale.x) || 1), size / (Math.abs(group.scale.y) || 1), 1);
138
+ halo.material.color.set(glow.color).multiplyScalar(HALO_GAIN * strength);
139
+ }
@@ -0,0 +1,33 @@
1
+ // Effective visibility of a scene record through its parents and its carrying
2
+ // character. Pure and dependency-free, so the renderers, the agent commands and
3
+ // the effects can share it; scene-objects.js re-exports it.
4
+
5
+ /** True when this record, a parent above it, or a character carrying any of
6
+ * those records is hidden. A cycle in parent links ends the walk. When the
7
+ * id is in `objects`, that stored record is what the walk reads, so a display
8
+ * copy cannot disagree with the authored flag. Nothing in the lists is changed. */
9
+ export function isEffectivelyHidden(entity, objects = [], characters = []) {
10
+ if (!entity || typeof entity !== "object") return false;
11
+ const byId = new Map();
12
+ for (const object of objects) {
13
+ if (object && typeof object.id === "string") byId.set(object.id, object);
14
+ }
15
+ const characterHidden = (id) => characters.some((item) => item && typeof item === "object" && item.id === id && item.hidden === true);
16
+ const seen = new Set();
17
+ let current = entity;
18
+ while (current && typeof current === "object") {
19
+ const id = typeof current.id === "string" ? current.id : null;
20
+ if (id) {
21
+ if (seen.has(id)) break;
22
+ seen.add(id);
23
+ if (byId.has(id)) current = byId.get(id);
24
+ }
25
+ if (current.hidden === true) return true;
26
+ const characterId = current.attach && typeof current.attach.characterId === "string" ? current.attach.characterId : null;
27
+ if (characterId && characterHidden(characterId)) return true;
28
+ const parentId = typeof current.parent === "string" ? current.parent : null;
29
+ if (!parentId) break;
30
+ current = byId.get(parentId) ?? null;
31
+ }
32
+ return false;
33
+ }