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.
- package/CHANGELOG.md +60 -0
- package/README.md +12 -30
- package/bin/agent/agent-routes.mjs +40 -113
- package/bin/agent/agent-runner.mjs +9 -10
- package/bin/agent/cliproxy-provider.mjs +135 -0
- package/bin/agent/credential-store.mjs +30 -7
- package/bin/agent/pi-tools.mjs +4 -1
- package/bin/agent/provider-keys.mjs +32 -0
- package/bin/agent/provider-login.mjs +110 -0
- package/bin/agent/provider-routes.mjs +99 -0
- package/bin/agent/providers.mjs +19 -97
- package/bin/agent/script-tool.mjs +17 -4
- package/bin/agent/studio-motion-reference.mjs +23 -0
- package/bin/agent/studio-prompt.mjs +1 -1
- package/bin/agent/studio-rebase-text.mjs +32 -0
- package/bin/agent/studio-rebase.mjs +26 -39
- package/bin/agent/studio-tools.mjs +9 -28
- package/bin/agent/tool-arguments.mjs +49 -0
- package/bin/agent/turn-frames.mjs +66 -0
- package/bin/codex-auth.mjs +9 -2
- package/bin/cozyclay.mjs +10 -116
- package/bin/live/cli.mjs +3 -3
- package/bin/mcp-runtime.mjs +2 -12
- package/bin/package-signature.mjs +0 -4
- package/dist/app/index.html +7 -6
- package/dist/assets/EXRLoader-CvVIvGVE.js +1 -0
- package/dist/assets/analytics-C9sUnndd.js +1 -0
- package/dist/assets/app-CCh--1Hc.js +5087 -0
- package/dist/assets/app-f-fkV5uP.css +1 -0
- package/dist/assets/compile-worker-CkxiD6lH.js +5 -0
- package/dist/assets/fflate.module-KbGj5-H6.js +5 -0
- package/dist/assets/{first-shot-handoff-msLI71g1.js → first-shot-handoff-CC4xe4HM.js} +1 -1
- package/dist/assets/{landing-ChUvB_zL.js → landing-DjHD8u7k.js} +1 -1
- package/dist/assets/scene-asset-sync-Hx_FzdOH.js +20 -0
- package/dist/assets/scene-asset-sync-_S8NQl5B.css +1 -0
- package/dist/assets/workflow-C4yKH1p4.js +179 -0
- package/dist/cozyclay-package.json +1 -1
- package/dist/index.html +3 -3
- package/dist/sw.js +3 -6
- package/dist/workflow/index.html +5 -4
- package/mcp/LIVE-PROTOCOL.md +7 -47
- package/mcp/README.md +16 -26
- package/mcp/live-hub.mjs +6 -10
- package/mcp/package.json +1 -3
- package/mcp/tool-handlers.mjs +4 -168
- package/package.json +3 -22
- package/src/App.jsx +150 -2624
- package/src/agent-progress.js +79 -0
- package/src/agent-turn-mark.js +30 -0
- package/src/analytics.js +1 -19
- package/src/app-context.js +5 -0
- package/src/app-stage.jsx +2 -34
- package/src/asset-pane.jsx +6 -5
- package/src/camera-block.js +21 -0
- package/src/camera-direction.js +120 -0
- package/src/character-kind.js +3 -9
- package/src/command-bus.js +8 -6
- package/src/commands/cast.js +5 -34
- package/src/commands/elements/character.js +1 -1
- package/src/commands/elements/motion.js +1 -6
- package/src/commands/elements/object.js +6 -2
- package/src/commands/elements/shot.js +4 -1
- package/src/commands/elements.js +38 -14
- package/src/commands/index.js +3 -1
- package/src/commands/kits.js +37 -0
- package/src/commands/motion-timing.js +57 -0
- package/src/commands/motion.js +9 -152
- package/src/commands/motion3-compile.js +65 -0
- package/src/commands/motion3.js +207 -0
- package/src/commands/objects.js +6 -4
- package/src/commands/shot.js +49 -11
- package/src/commands/stage.js +14 -1
- package/src/commands/view.js +4 -1
- package/src/document-mirrors.js +42 -0
- package/src/document-store.js +4 -0
- package/src/domains/cast.js +19 -194
- package/src/domains/motion/motion-range-pin-rules.js +1 -1
- package/src/domains/motion/take-source.js +51 -0
- package/src/domains/motion.js +60 -1838
- package/src/domains/scenes.js +12 -55
- package/src/domains/shots.js +2 -2
- package/src/domains/stage.js +4 -2
- package/src/draft-ghost.js +46 -0
- package/src/draft-ghost.jsx +129 -0
- package/src/dualview.jsx +4 -3
- package/src/effects/EffectsLayer.jsx +112 -0
- package/src/effects/effect-meshes.js +232 -0
- package/src/effects/effect-record.js +103 -0
- package/src/effects/emitter.js +59 -0
- package/src/effects/simulate.js +261 -0
- package/src/effects/state.js +59 -0
- package/src/execution-telemetry.js +2 -3
- package/src/first-success-guide.jsx +5 -5
- package/src/ghost-set.js +257 -0
- package/src/held-object-edit.jsx +113 -0
- package/src/held-object-pose.js +175 -0
- package/src/hierarchy-model.js +1 -1
- package/src/hierarchy-panel.jsx +1 -1
- package/src/imported-mesh.jsx +103 -0
- package/src/kits/held.js +84 -0
- package/src/kits/index.js +70 -0
- package/src/kits/smoking.js +57 -0
- package/src/light-blockers.js +93 -0
- package/src/motion/beat-board-model.js +84 -0
- package/src/motion/beat-board.css +124 -0
- package/src/motion/beat-board.jsx +59 -0
- package/src/motion/center-of-mass.js +66 -0
- package/src/motion/fix-collisions.js +850 -0
- package/src/motion/physics-review.js +118 -0
- package/src/{ardy → motion}/physics-support.js +1 -76
- package/src/motion/rig-snapshot.js +22 -0
- package/src/motion/take-segments.jsx +140 -0
- package/src/{ardy → motion}/timeline-coordinates.js +0 -21
- package/src/{ardy → motion}/timeline.css +1 -2
- package/src/{ardy → motion}/timeline.jsx +42 -413
- package/src/motion/waypoints.js +76 -0
- package/src/motion3/beats.js +83 -0
- package/src/motion3/body.js +158 -0
- package/src/motion3/bones.js +47 -0
- package/src/motion3/carried.js +47 -0
- package/src/motion3/compile-job.js +75 -0
- package/src/motion3/compile-worker.js +10 -0
- package/src/motion3/compiler/anchors.js +105 -0
- package/src/motion3/compiler/arm-orient.js +128 -0
- package/src/motion3/compiler/channels.js +125 -0
- package/src/motion3/compiler/compile.js +205 -0
- package/src/motion3/compiler/gait-rhythm.js +97 -0
- package/src/motion3/compiler/grip.js +171 -0
- package/src/motion3/compiler/head-steady.js +48 -0
- package/src/motion3/compiler/hold.js +134 -0
- package/src/motion3/compiler/idle.js +113 -0
- package/src/motion3/compiler/limbs.js +170 -0
- package/src/motion3/compiler/math.js +72 -0
- package/src/motion3/compiler/pose.js +162 -0
- package/src/motion3/compiler/quat.js +49 -0
- package/src/motion3/compiler/skeleton.js +74 -0
- package/src/motion3/compiler/validate-common.js +32 -0
- package/src/motion3/compiler/validate-goals.js +118 -0
- package/src/motion3/compiler/validate-objects.js +143 -0
- package/src/motion3/compiler/validate-parts.js +116 -0
- package/src/motion3/compiler/validate.js +122 -0
- package/src/motion3/compiler/vocab.js +73 -0
- package/src/motion3/compiler/walk-pelvis.js +275 -0
- package/src/motion3/compiler/walk.js +207 -0
- package/src/motion3/compiler/wrist-clamp.js +32 -0
- package/src/motion3/delta.js +47 -0
- package/src/motion3/describe.js +174 -0
- package/src/motion3/docs.js +27 -0
- package/src/motion3/edit-carry.js +21 -0
- package/src/motion3/fix/ease.js +16 -0
- package/src/motion3/fix/ground.js +37 -0
- package/src/motion3/fix/index.js +52 -0
- package/src/motion3/fix/push.js +71 -0
- package/src/motion3/fix/reach.js +46 -0
- package/src/motion3/fix/retime.js +79 -0
- package/src/motion3/fix/self.js +211 -0
- package/src/motion3/fix/slide.js +70 -0
- package/src/motion3/fix/turn.js +51 -0
- package/src/motion3/follow.js +39 -0
- package/src/motion3/free-motion.js +173 -0
- package/src/motion3/grip-choice.js +130 -0
- package/src/motion3/hold-plan.js +157 -0
- package/src/motion3/json-patch.js +77 -0
- package/src/motion3/key-aim.js +105 -0
- package/src/motion3/key-edit.js +82 -0
- package/src/motion3/lenient.js +127 -0
- package/src/motion3/object-keys.js +236 -0
- package/src/motion3/object-pose.js +182 -0
- package/src/motion3/object-tracks.js +12 -0
- package/src/motion3/pipeline.js +102 -0
- package/src/motion3/playback.js +51 -0
- package/src/motion3/probes.js +91 -0
- package/src/motion3/qa/body-checks.js +34 -0
- package/src/motion3/qa/check-names.js +14 -0
- package/src/motion3/qa/checks.js +121 -0
- package/src/motion3/qa/contact-checks.js +51 -0
- package/src/motion3/qa/explain.js +80 -0
- package/src/motion3/qa/fmt.js +5 -0
- package/src/motion3/qa/foot-checks.js +72 -0
- package/src/motion3/qa/geometry.js +77 -0
- package/src/motion3/qa/goals.js +191 -0
- package/src/motion3/qa/ground.js +60 -0
- package/src/motion3/qa/hints-contact.js +179 -0
- package/src/motion3/qa/hints-goal.js +160 -0
- package/src/motion3/qa/hints-knee.js +24 -0
- package/src/motion3/qa/hints-motion.js +209 -0
- package/src/motion3/qa/hints-object.js +91 -0
- package/src/motion3/qa/index.js +42 -0
- package/src/motion3/qa/key-checks.js +82 -0
- package/src/motion3/qa/near.js +96 -0
- package/src/motion3/qa/object-checks.js +135 -0
- package/src/motion3/qa/patch.js +161 -0
- package/src/motion3/qa/severity.js +38 -0
- package/src/motion3/qa/spec-checks.js +65 -0
- package/src/motion3/qa/style.js +218 -0
- package/src/motion3/qa/wrist-checks.js +69 -0
- package/src/motion3/qa/wrist-obs.js +60 -0
- package/src/motion3/refs.js +120 -0
- package/src/motion3/repair-tune.js +121 -0
- package/src/motion3/repair.js +83 -0
- package/src/motion3/report.js +67 -0
- package/src/motion3/rigs/index.js +14 -0
- package/src/motion3/rigs/x-bot-tpose-body.json +1 -0
- package/src/motion3/rigs/x-bot-tpose.json +1 -0
- package/src/motion3/rigs/y-bot-tpose-body.json +1 -0
- package/src/motion3/rigs/y-bot-tpose.json +1 -0
- package/src/motion3/scene.js +60 -0
- package/src/motion3/schema/brief.js +89 -0
- package/src/motion3/schema/fields.js +44 -0
- package/src/motion3/schema/spec-examples.js +46 -0
- package/src/motion3/schema/topics.js +111 -0
- package/src/motion3/schema/types.js +147 -0
- package/src/motion3/search.js +117 -0
- package/src/motion3/second-hand.js +112 -0
- package/src/motion3/skills/basic.js +82 -0
- package/src/motion3/skills/book.js +76 -0
- package/src/motion3/skills/car.js +145 -0
- package/src/motion3/skills/frames-auto.js +29 -0
- package/src/motion3/skills/index.js +83 -0
- package/src/motion3/skills/info.js +65 -0
- package/src/motion3/skills/smoke-kit.js +109 -0
- package/src/motion3/skills/smoke.js +160 -0
- package/src/motion3/spec-store.js +59 -0
- package/src/motion3/take-edit.js +155 -0
- package/src/motion3/take.js +63 -0
- package/src/motion3/timeline.js +127 -0
- package/src/motion3/views.js +121 -0
- package/src/motion3/walk-plan.js +119 -0
- package/src/object-catalog.jsx +7 -1
- package/src/object-colors.js +98 -0
- package/src/object-gizmo-parts.js +115 -0
- package/src/object-gizmo.jsx +37 -122
- package/src/object-glow.js +139 -0
- package/src/object-hidden.js +33 -0
- package/src/object-look.js +196 -0
- package/src/object-size.js +10 -0
- package/src/object-travel.js +71 -6
- package/src/panels/CameraPanel.jsx +7 -7
- package/src/panels/HdrEnvironmentControls.jsx +59 -0
- package/src/panels/LightPanel.jsx +25 -4
- package/src/panels/ObjectLookPanel.jsx +88 -0
- package/src/panels/PosePanel.jsx +2 -17
- package/src/panels/RigControlPanel.jsx +5 -53
- package/src/panels/SubjectBox.jsx +4 -1
- package/src/panels/details.css +14 -0
- package/src/posestudio.jsx +4 -27
- package/src/project-browser.jsx +3 -4
- package/src/project-resources.js +18 -31
- package/src/project.js +13 -104
- package/src/props.jsx +25 -280
- package/src/rail-dot-drag.js +23 -0
- package/src/render-passes.js +10 -0
- package/src/resource-status.jsx +1 -0
- package/src/result-modal.jsx +4 -19
- package/src/room.jsx +20 -12
- package/src/scene-assets.js +8 -6
- package/src/scene-light-place.js +72 -0
- package/src/scene-lights.jsx +148 -0
- package/src/scene-objects.js +31 -139
- package/src/scenes.js +15 -105
- package/src/semantic-edit.js +2 -5
- package/src/set-pieces.jsx +183 -0
- package/src/settings-menu.jsx +3 -11
- package/src/shell/BottomDock.jsx +9 -51
- package/src/shell/DetailsSlot.jsx +16 -200
- package/src/shell/GhostsMenuGroup.jsx +74 -0
- package/src/shell/LibrarySlot.jsx +1 -3
- package/src/shell/MenuBar.jsx +3 -3
- package/src/shell/PreferencesDialog.jsx +15 -264
- package/src/shell/PreferencesSlot.jsx +2 -8
- package/src/shell/TopBar.jsx +5 -84
- package/src/shell/ViewportToolbar.jsx +10 -70
- package/src/shell/agent-glass.css +19 -6
- package/src/shell/details-selection.js +42 -0
- package/src/shell/ghosts-menu.css +22 -0
- package/src/shell/glass-regions.css +29 -3
- package/src/shell/preferences.css +6 -1
- package/src/shot-authoring.js +4 -7
- package/src/soft-shadows.js +37 -0
- package/src/stage-attach-frames.js +12 -12
- package/src/stage-canvas-rigs.jsx +3 -3
- package/src/stage-capture.jsx +42 -8
- package/src/stage-characters.jsx +22 -216
- package/src/stage-editor-gizmos.jsx +6 -6
- package/src/stage-environment.js +93 -0
- package/src/stage-hdr-builtins.js +107 -0
- package/src/stage-hdr-image.js +116 -0
- package/src/stage-hdr-library.js +70 -0
- package/src/stage-hdr-map.js +94 -0
- package/src/stage-hdr.js +130 -0
- package/src/stage-lighting.js +149 -0
- package/src/stage-occlusion.js +210 -0
- package/src/stage-sky.jsx +159 -0
- package/src/stage-startup-defaults.js +5 -9
- package/src/studio-actions.js +12 -14
- package/src/studio-agent-commands.js +19 -16
- package/src/studio-agent-motion.js +9 -9
- package/src/studio-agent-protocol.js +41 -71
- package/src/studio-app-binding.js +25 -45
- package/src/studio-asset-list.js +17 -0
- package/src/studio-contact-sheet.js +37 -5
- package/src/studio-elements.js +27 -7
- package/src/studio-entity-bounds.js +18 -0
- package/src/studio-error-codes.js +8 -0
- package/src/studio-export-framing.js +152 -0
- package/src/studio-lighting-check.js +103 -0
- package/src/studio-motion-visual.js +150 -0
- package/src/studio-schema-errors.js +80 -0
- package/src/studio-support-gap.js +74 -0
- package/src/styles.css +1 -19
- package/src/timeline-defaults.js +5 -0
- package/src/timeline-extent.js +8 -20
- package/src/tutorial-walk.js +17 -0
- package/src/ui-scrub.js +1 -1
- package/src/use-shot-camera-tracks.js +12 -0
- package/src/view-ghosts.js +97 -0
- package/src/view-ghosts.jsx +49 -0
- package/src/workflow/AgentPanel.jsx +47 -166
- package/src/workflow/AgentProgressCard.jsx +107 -0
- package/src/workflow/AgentReplyText.jsx +37 -0
- package/src/workflow/AgentTurnSteps.jsx +84 -0
- package/src/workflow/LoginPanel.jsx +140 -0
- package/src/workflow/WorkflowBuilder.jsx +7 -23
- package/src/workflow/agent-client.js +54 -51
- package/src/workflow/agent-panel.css +71 -1
- package/src/workflow/agent-progress-card.css +167 -0
- package/src/workflow/agent-progress-view.js +47 -0
- package/src/workflow/agent-turn-steps.css +113 -0
- package/src/workflow/agent-turn.js +191 -0
- package/src/workflow/login-client.js +34 -0
- package/src/workflow/motion-input.js +18 -31
- package/src/workflow/node-schema.js +1 -1
- package/src/workflow/reply-markdown.js +69 -0
- package/src/workflow/scene-asset-sync.js +8 -8
- package/tools/agent-session-stats.mjs +61 -0
- package/tools/dev-full.mjs +5 -56
- package/tools/eslint-baseline.json +12 -13
- package/tools/knip-baseline.json +0 -179
- package/tools/loc-baseline.json +31 -81
- package/tools/motion3/extract-body.mjs +103 -0
- package/tools/motion3/extract-rig.mjs +85 -0
- package/tools/process-supervisor.mjs +0 -88
- package/tools/run-tests.mjs +8 -124
- package/bin/agent/motion-runtime.mjs +0 -76
- package/dist/ardy/cskel27-rest.json +0 -557
- package/dist/assets/analytics-C9pMpW7s.js +0 -1
- package/dist/assets/app-B_2XB9rw.css +0 -1
- package/dist/assets/app-DAuvKPP8.js +0 -4920
- package/dist/assets/config-Bx7pYsD9.js +0 -1
- package/dist/assets/config-DeoFTYzb.css +0 -1
- package/dist/assets/demo-CvKuBgy5.js +0 -1
- package/dist/assets/scene-asset-sync-DKkpjKBV.js +0 -19
- package/dist/assets/scene-asset-sync-DjJ_cJ2R.css +0 -1
- package/dist/assets/ticket-BPk2Ni95.js +0 -1
- package/dist/assets/workflow-jJRBR94Z.js +0 -179
- package/dist/demo/walk-then-stop.npz +0 -0
- package/mcp/ardy-prompts.mjs +0 -218
- package/src/ardy/auto-fix-panel.css +0 -108
- package/src/ardy/auto-physics.js +0 -1194
- package/src/ardy/client.js +0 -153
- package/src/ardy/convert.js +0 -313
- package/src/ardy/cskel27-neutral.js +0 -39
- package/src/ardy/cskel27.js +0 -77
- package/src/ardy/export.js +0 -165
- package/src/ardy/fix-collisions.js +0 -2257
- package/src/ardy/foot-lock.js +0 -230
- package/src/ardy/motion-calibration.js +0 -118
- package/src/ardy/motion-edit.js +0 -213
- package/src/ardy/motion-url.js +0 -55
- package/src/ardy/mp4-duration.js +0 -102
- package/src/ardy/npz.js +0 -595
- package/src/ardy/physics-panel.css +0 -21
- package/src/ardy/physics-panel.jsx +0 -89
- package/src/ardy/physics-refine.js +0 -111
- package/src/ardy/physics-review.js +0 -567
- package/src/ardy/physics-temporal.js +0 -54
- package/src/ardy/platform-fit-panel.css +0 -64
- package/src/ardy/platform-fit-panel.jsx +0 -41
- package/src/ardy/platform-fit.js +0 -326
- package/src/ardy/playback.js +0 -711
- package/src/ardy/pose-pin.js +0 -97
- package/src/ardy/prompt-clips.js +0 -30
- package/src/ardy/retime.js +0 -211
- package/src/ardy/root-drop.js +0 -369
- package/src/ardy/timeline-resize.js +0 -13
- package/src/ardy/to-cskel27.js +0 -225
- package/src/ardy/trim.js +0 -33
- package/src/ardy/waypoints.js +0 -369
- package/src/ardy-bridge-contract.js +0 -281
- package/src/domains/motion/motion-video-capture.js +0 -664
- package/src/i2v-motion-client.js +0 -77
- package/src/i2v-motion-studio.jsx +0 -180
- package/src/line-edit.js +0 -1348
- package/src/motion/generation.js +0 -105
- package/src/motion-readiness-ui.jsx +0 -67
- package/src/motion-readiness.js +0 -40
- package/src/motion-resources.js +0 -177
- package/src/motion-store.js +0 -65
- package/src/motion-trail.js +0 -601
- package/src/multimodel-ingest.js +0 -394
- package/src/panels/PromptBlocksPanel.jsx +0 -444
- package/src/panels/VideoCapturePanel.jsx +0 -181
- package/src/pose-extract/browser.js +0 -42
- package/src/pose-extract/detector.js +0 -100
- package/src/pose-extract/fit.js +0 -227
- package/src/pose-extract/image-frame.js +0 -51
- package/src/pose-extract/index.js +0 -58
- package/src/pose-extract/keyframes.js +0 -74
- package/src/pose-extract/landmarks.js +0 -162
- package/src/pose-extract/mirror.js +0 -153
- package/src/pose-extract/take.js +0 -131
- package/src/pose-extract/video-frames.js +0 -91
- package/src/take-recipe.js +0 -165
- package/src/trail-key-conflicts.js +0 -39
- package/src/trail-pick.js +0 -59
- package/src/use-i2v-capture-modal.js +0 -15
- package/tools/ardy/BRIDGE.md +0 -37
- package/tools/ardy/GVHMR-NPZ.md +0 -68
- package/tools/ardy/README.md +0 -124
- package/tools/ardy/__pycache__/cclay_gvhmr_worker.cpython-313.pyc +0 -0
- package/tools/ardy/artifacts.mjs +0 -34
- package/tools/ardy/bridge.mjs +0 -1732
- package/tools/ardy/bvh-cskel27.mjs +0 -1265
- package/tools/ardy/cclay_constrained_generate.py +0 -561
- package/tools/ardy/cclay_gvhmr_worker.py +0 -264
- package/tools/ardy/cclay_motion_edit.py +0 -510
- package/tools/ardy/cclay_sequence_generate.py +0 -705
- package/tools/ardy/cclay_worker.py +0 -304
- package/tools/ardy/dump-npz.py +0 -205
- package/tools/ardy/extract-rest.mjs +0 -299
- package/tools/ardy/extract.mjs +0 -507
- package/tools/ardy/footage.mjs +0 -467
- package/tools/ardy/gvhmr-floor.mjs +0 -102
- package/tools/ardy/gvhmr-to-cskel27.mjs +0 -21
- package/tools/ardy/gvhmr_fastpath.py +0 -168
- package/tools/ardy/gvhmr_trajectory.py +0 -240
- package/tools/ardy/merge-text-encoder.py +0 -160
- package/tools/ardy/mocap-metrics.mjs +0 -200
- package/tools/ardy/mocap-quality-gate.mjs +0 -110
- package/tools/ardy/motion-stabilize.mjs +0 -348
- package/tools/ardy/npz.mjs +0 -414
- package/tools/ardy/pose-to-npz.mjs +0 -106
- package/tools/ardy/prompt-limits.mjs +0 -5
- package/tools/ardy/run-edit-on-box.sh +0 -73
- package/tools/ardy/run-local.mjs +0 -233
- package/tools/ardy/run-on-box.sh +0 -593
- package/tools/ardy/run-sequence-on-box.sh +0 -177
- package/tools/ardy/runners/gvhmr-worker.mjs +0 -206
- package/tools/ardy/runners/index.mjs +0 -18
- package/tools/ardy/runners/local.mjs +0 -455
- package/tools/ardy/runners/proc.mjs +0 -125
- package/tools/ardy/runners/remote.mjs +0 -252
- package/tools/ardy/setup-local.mjs +0 -189
- package/tools/ardy/setup-text-encoder-on-box.sh +0 -93
- package/tools/ardy/setup-text-encoder.py +0 -306
- package/tools/ardy/smpl-cskel27.mjs +0 -178
- package/tools/ardy/visual-qa.mjs +0 -196
- package/tools/ardy/vq-car.mjs +0 -52
- package/tools/bench/EXP3.md +0 -61
- package/tools/bench/cclay_bench_extract_incam.py +0 -125
- package/tools/bench/cclay_bench_extract_obs.py +0 -204
- package/tools/bench/cclay_bench_runner.py +0 -42
- package/tools/bench/cube-contact.mjs +0 -162
- package/tools/bench/exp3.mjs +0 -152
- package/tools/bench/extract-bench-lib.mjs +0 -216
- package/tools/bench/extract-bench.mjs +0 -302
- package/tools/bench/fal-generate.mjs +0 -88
- package/tools/bench/fit/README.md +0 -189
- package/tools/bench/fit/camera.mjs +0 -59
- package/tools/bench/fit/contact.mjs +0 -419
- package/tools/bench/fit/footlock.mjs +0 -234
- package/tools/bench/fit/motion.mjs +0 -85
- package/tools/bench/fit/pin.mjs +0 -27
- package/tools/bench/fit/remote.mjs +0 -105
- package/tools/bench/fit-bench.mjs +0 -126
- package/tools/bench/fit-sanity.mjs +0 -45
- package/tools/bench/metrics.mjs +0 -313
- package/tools/bench/obs/depth.mjs +0 -258
- package/tools/bench/obs/extrinsics.mjs +0 -175
- package/tools/bench/obs/fit_mannequin_betas.py +0 -340
- package/tools/bench/obs/ground.mjs +0 -294
- package/tools/bench/obs/heading.mjs +0 -151
- package/tools/bench/obs/ladder.mjs +0 -555
- package/tools/bench/obs/mannequin-betas.json +0 -124
- package/tools/bench/obs/remote.mjs +0 -83
- package/tools/bench/obs/rest_joints.py +0 -52
- package/tools/bench/obs/ybot-targets.mjs +0 -49
- package/tools/bench/obs-bench.mjs +0 -520
- package/tools/bench/score.mjs +0 -343
- package/tools/bench/summarize.mjs +0 -86
- package/tools/demo-worker/README.md +0 -137
- package/tools/demo-worker/api-client.mjs +0 -233
- package/tools/demo-worker/generate.mjs +0 -301
- package/tools/demo-worker/index.mjs +0 -466
- package/tools/demo-worker/sign-core.mjs +0 -112
- package/tools/demo-worker/sign.mjs +0 -161
- package/tools/gt-render/browser.mjs +0 -182
- package/tools/gt-render/camera-math.mjs +0 -276
- package/tools/gt-render/page.mjs +0 -267
- package/tools/gt-render/render.mjs +0 -514
- package/tools/gt-render/scene-box.mjs +0 -31
- package/tools/gt-render/take-transform.mjs +0 -87
- package/tools/kimodo/compare-seam.mjs +0 -108
- package/tools/kimodo/constraints.mjs +0 -244
- package/tools/kimodo/edit.mjs +0 -259
- package/tools/kimodo/effector-constraints.mjs +0 -200
- package/tools/kimodo/generate.mjs +0 -646
- package/tools/kimodo/local-output.mjs +0 -265
- package/tools/kimodo/measure-pose.mjs +0 -65
- package/tools/kimodo/measure-preserve.mjs +0 -899
- package/tools/kimodo/measure-waypoints.mjs +0 -55
- package/tools/kimodo/mlx-generate.py +0 -111
- package/tools/kimodo/pose-constraints.mjs +0 -248
- package/tools/kimodo/preserve-mask.mjs +0 -543
- package/tools/kimodo/read-npz.mjs +0 -189
- package/tools/kimodo/run-edit-on-box.mjs +0 -155
- package/tools/kimodo/run-sequence-on-box.mjs +0 -178
- package/tools/kimodo/runner.mjs +0 -284
- package/tools/kimodo/seam-batch.mjs +0 -77
- package/tools/kimodo/setup-local.mjs +0 -30
- package/tools/kimodo/setup-on-box.sh +0 -339
- package/tools/kimodo/soma77-to-cskel27.mjs +0 -305
- package/tools/morphgs/assets/gen_truth.py +0 -39
- package/tools/morphgs/assets/mesh_ori_rig.txt +0 -27
- package/tools/morphgs/assets/playback-check.mjs +0 -30
- package/tools/morphgs/demo-gate.sh +0 -26
- package/tools/morphgs/fbx2morphgs.mjs +0 -68
- package/tools/morphgs/morphgs-to-cskel27.mjs +0 -105
- package/tools/morphgs/patches/preprocess_src-none-mode.patch +0 -76
- package/tools/morphgs/setup-on-cluster.sh +0 -126
- package/tools/projflow/driver.py +0 -1069
- package/tools/projflow/generate.mjs +0 -805
- package/tools/projflow/hml22-to-cskel27.mjs +0 -735
- package/tools/projflow/line-edit-job.mjs +0 -547
- package/tools/projflow/replay.mjs +0 -452
- package/tools/projflow/runner.mjs +0 -269
- package/tools/projflow/service.mjs +0 -742
- package/tools/track/DESIGN.md +0 -513
- package/tools/track/backfill-provenance.mjs +0 -116
- package/tools/track/budget.mjs +0 -8
- package/tools/track/check-rig.mjs +0 -209
- package/tools/track/diagnostics.schema.json +0 -60
- package/tools/track/export-rig.mjs +0 -256
- package/tools/track/fallback.mjs +0 -7
- package/tools/track/fk-parity-fixture.mjs +0 -161
- package/tools/track/gate.mjs +0 -349
- package/tools/track/masks.mjs +0 -318
- package/tools/track/metrics.mjs +0 -201
- package/tools/track/publish-obs.mjs +0 -108
- package/tools/track/py/check_env.py +0 -61
- package/tools/track/py/eval_lr.py +0 -229
- package/tools/track/py/lr_viterbi.py +0 -294
- package/tools/track/py/masks.py +0 -405
- package/tools/track/py/objective.py +0 -382
- package/tools/track/py/rig.py +0 -508
- package/tools/track/py/scene.py +0 -326
- package/tools/track/py/test_joint_indices.py +0 -123
- package/tools/track/py/test_lr_viterbi.py +0 -195
- package/tools/track/py/test_masks.py +0 -229
- package/tools/track/py/test_rig.py +0 -288
- package/tools/track/py/test_scene.py +0 -354
- package/tools/track/py/test_track.py +0 -328
- package/tools/track/py/track.py +0 -411
- package/tools/track/remote.mjs +0 -140
- package/tools/track/rig-dump.mjs +0 -227
- package/tools/track/run-box-tests.mjs +0 -14
- package/tools/track/run-box.mjs +0 -168
- package/tools/track/setup-box.sh +0 -59
- package/tools/track/study-2d.mjs +0 -728
- /package/src/{ardy → motion}/collision-blockers.js +0 -0
- /package/src/{ardy → motion}/ground.js +0 -0
- /package/src/{ardy → motion}/ik-drag.js +0 -0
- /package/src/{ardy → motion}/ik-key-json.js +0 -0
- /package/src/{ardy → motion}/ik.js +0 -0
- /package/src/{ardy → motion}/physics-surface.js +0 -0
- /package/src/{ardy → motion}/playback-clock.js +0 -0
- /package/src/{ardy → motion}/range-pin.js +0 -0
package/src/ardy/waypoints.js
DELETED
|
@@ -1,369 +0,0 @@
|
|
|
1
|
-
const cleanZero = (value) => (Object.is(value, -0) || Math.abs(value) < 1e-9 ? 0 : value);
|
|
2
|
-
import { isKo } from "../locale.js";
|
|
3
|
-
import { TIMELINE_FRAME_FPS } from "../timeline-fps.js";
|
|
4
|
-
|
|
5
|
-
const DEFAULT_WAYPOINT_SPACING_FRAMES = Math.round(0.4 * TIMELINE_FRAME_FPS);
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* ARDY root constraints live in the actor's clip-local coordinates. CozyClay's
|
|
9
|
-
* plan uses scene-world coordinates and rotates Subject 1 as a scene object, so
|
|
10
|
-
* both translation and actor yaw must be removed before generation. Playback
|
|
11
|
-
* applies the exact inverse transform when it places the generated root.
|
|
12
|
-
*/
|
|
13
|
-
export function toArdyWaypoints(waypoints, actorRotationDeg = 0) {
|
|
14
|
-
if (!waypoints.length) return [];
|
|
15
|
-
const origin = waypoints[0];
|
|
16
|
-
const yaw = (actorRotationDeg * Math.PI) / 180;
|
|
17
|
-
const cos = Math.cos(yaw);
|
|
18
|
-
const sin = Math.sin(yaw);
|
|
19
|
-
return waypoints.map((waypoint) => {
|
|
20
|
-
const worldX = waypoint.x - origin.x;
|
|
21
|
-
const worldZ = waypoint.z - origin.z;
|
|
22
|
-
return {
|
|
23
|
-
frame: waypoint.frame,
|
|
24
|
-
x: cleanZero(cos * worldX - sin * worldZ),
|
|
25
|
-
z: cleanZero(sin * worldX + cos * worldZ),
|
|
26
|
-
heading: waypoint.heading ?? null,
|
|
27
|
-
};
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Densify an authored root path for ARDY motion generation: rebase to
|
|
32
|
-
* clip-local coordinates exactly like toArdyWaypoints, interpolate on the
|
|
33
|
-
* authored polyline, and emit one sample roughly every `spacing` frames
|
|
34
|
-
* (capped at maxPoints). Authored frames are always kept, the first
|
|
35
|
-
* sample is always frame 0, and frames are strictly ascending without
|
|
36
|
-
* duplicates. The spacing is deliberately loose: a sample every 2-3
|
|
37
|
-
* frames rails the root so hard the model gives up its gait cadence and
|
|
38
|
-
* ice-skates; ~0.4 s between position pins leaves room for real steps
|
|
39
|
-
* while the interpolated headings still steer the facing.
|
|
40
|
-
* Each sample heading is the path tangent (central difference between
|
|
41
|
-
* neighbors; adjacent-segment direction at the ends), carried forward
|
|
42
|
-
* through stalls shorter than 1e-6 and 0 when the whole path is
|
|
43
|
-
* stationary; headings are always finite numbers in [-2π, 2π].
|
|
44
|
-
*/
|
|
45
|
-
export function densifyArdyWaypoints(waypoints, actorRotationDeg = 0, maxPoints = 32, spacing = DEFAULT_WAYPOINT_SPACING_FRAMES) {
|
|
46
|
-
if (waypoints.length < 2) return [];
|
|
47
|
-
const local = toArdyWaypoints(waypoints, actorRotationDeg);
|
|
48
|
-
|
|
49
|
-
// Authored frames and positions, deduplicated: these are the
|
|
50
|
-
// interpolation endpoints every sample set must contain.
|
|
51
|
-
const authored = [];
|
|
52
|
-
for (const point of local) {
|
|
53
|
-
if (authored.length === 0 || authored[authored.length - 1].frame !== point.frame) {
|
|
54
|
-
authored.push({ frame: point.frame, x: point.x, z: point.z });
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
if (authored.length === 1) {
|
|
58
|
-
return [{ frame: authored[0].frame, x: authored[0].x, z: authored[0].z, heading: 0 }];
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const denseCount = authored[authored.length - 1].frame - authored[0].frame + 1;
|
|
62
|
-
const limit = Math.max(0, Math.floor(maxPoints));
|
|
63
|
-
const gapSpacing = Math.max(1, Math.floor(spacing));
|
|
64
|
-
// One filler per started `spacing` chunk beyond the first, per gap.
|
|
65
|
-
let spacingExtra = 0;
|
|
66
|
-
for (let i = 0; i < authored.length - 1; i += 1) {
|
|
67
|
-
spacingExtra += Math.max(0, Math.ceil((authored[i + 1].frame - authored[i].frame) / gapSpacing) - 1);
|
|
68
|
-
}
|
|
69
|
-
const extra = Math.max(0, Math.min(limit, denseCount, authored.length + spacingExtra) - authored.length);
|
|
70
|
-
|
|
71
|
-
// Distribute the extra samples across the gaps between authored frames
|
|
72
|
-
// in proportion to gap length (largest remainder), so spacing stays
|
|
73
|
-
// uniform while authored frames are always kept.
|
|
74
|
-
const gaps = [];
|
|
75
|
-
for (let i = 0; i < authored.length - 1; i += 1) {
|
|
76
|
-
gaps.push(authored[i + 1].frame - authored[i].frame);
|
|
77
|
-
}
|
|
78
|
-
const gapSizes = gaps.map(() => 0);
|
|
79
|
-
if (extra > 0) {
|
|
80
|
-
const total = gaps.reduce((sum, size) => sum + size, 0);
|
|
81
|
-
const raw = gaps.map((size) => (extra * size) / total);
|
|
82
|
-
const base = raw.map((value) => Math.floor(value));
|
|
83
|
-
const order = raw
|
|
84
|
-
.map((value, i) => [value - base[i], i])
|
|
85
|
-
.sort((a, b) => b[0] - a[0]);
|
|
86
|
-
let used = base.reduce((sum, value) => sum + value, 0);
|
|
87
|
-
for (const [, i] of order) {
|
|
88
|
-
if (used >= extra) break;
|
|
89
|
-
const add = Math.min(extra - used, gaps[i] - 1 - base[i]);
|
|
90
|
-
base[i] += add;
|
|
91
|
-
used += add;
|
|
92
|
-
}
|
|
93
|
-
for (let i = 0; i < gaps.length; i += 1) gapSizes[i] = base[i];
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const frames = [];
|
|
97
|
-
for (let i = 0; i < authored.length; i += 1) {
|
|
98
|
-
if (i > 0) {
|
|
99
|
-
const count = gapSizes[i - 1];
|
|
100
|
-
if (count > 0) {
|
|
101
|
-
const step = gaps[i - 1] / (count + 1);
|
|
102
|
-
for (let j = 0; j < count; j += 1) {
|
|
103
|
-
frames.push(authored[i - 1].frame + Math.round((j + 1) * step));
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
frames.push(authored[i].frame);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// C1 resampling: cubic Hermite through the authored pins, with each pin's
|
|
111
|
-
// velocity as the finite-difference average of its two segment velocities
|
|
112
|
-
// (one-sided at the ends). A C0 polyline pins an instantaneous direction
|
|
113
|
-
// flip at every interior waypoint — and, when authored frames imply
|
|
114
|
-
// different paces, an instantaneous speed jump — which the generator can
|
|
115
|
-
// only satisfy by braking and pivoting on the pin: the hitch you see as
|
|
116
|
-
// the root crosses waypoint 2. Hermite keeps authored pins exact, keeps a
|
|
117
|
-
// uniform straight path exactly linear, and turns corners into arcs where
|
|
118
|
-
// both travel direction and speed ramp continuously. (A near-180° reversal
|
|
119
|
-
// will swing wide of the pin — that wide swing is the C1 answer to an
|
|
120
|
-
// about-face, not an error.)
|
|
121
|
-
const segmentVelocity = (a, b) => ({
|
|
122
|
-
x: (b.x - a.x) / (b.frame - a.frame),
|
|
123
|
-
z: (b.z - a.z) / (b.frame - a.frame),
|
|
124
|
-
});
|
|
125
|
-
const stalledSegment = (a, b) => (b.x - a.x) * (b.x - a.x) + (b.z - a.z) * (b.z - a.z) < 1e-12;
|
|
126
|
-
const velocity = authored.map((point, i) => {
|
|
127
|
-
const prev = authored[i - 1];
|
|
128
|
-
const next = authored[i + 1];
|
|
129
|
-
// A knot beside a stall comes to a full stop: leaking a neighbour's
|
|
130
|
-
// velocity into a hold would make the "stationary" span drift, and a
|
|
131
|
-
// walker entering a hold decelerates to zero anyway.
|
|
132
|
-
if (!prev) return stalledSegment(point, next) ? { x: 0, z: 0 } : segmentVelocity(point, next);
|
|
133
|
-
if (!next) return stalledSegment(prev, point) ? { x: 0, z: 0 } : segmentVelocity(prev, point);
|
|
134
|
-
if (stalledSegment(prev, point) || stalledSegment(point, next)) return { x: 0, z: 0 };
|
|
135
|
-
const before = segmentVelocity(prev, point);
|
|
136
|
-
const after = segmentVelocity(point, next);
|
|
137
|
-
return { x: (before.x + after.x) / 2, z: (before.z + after.z) / 2 };
|
|
138
|
-
});
|
|
139
|
-
const samples = [];
|
|
140
|
-
let seg = 0;
|
|
141
|
-
for (const frame of frames) {
|
|
142
|
-
while (frame > authored[seg + 1].frame) seg += 1;
|
|
143
|
-
const start = authored[seg];
|
|
144
|
-
const end = authored[seg + 1];
|
|
145
|
-
const span = end.frame - start.frame;
|
|
146
|
-
const t = (frame - start.frame) / span;
|
|
147
|
-
const h00 = (1 + 2 * t) * (1 - t) * (1 - t);
|
|
148
|
-
const h10 = t * (1 - t) * (1 - t);
|
|
149
|
-
const h01 = t * t * (3 - 2 * t);
|
|
150
|
-
const h11 = t * t * (t - 1);
|
|
151
|
-
const v0 = velocity[seg];
|
|
152
|
-
const v1 = velocity[seg + 1];
|
|
153
|
-
samples.push({
|
|
154
|
-
frame,
|
|
155
|
-
x: cleanZero(h00 * start.x + h10 * span * v0.x + h01 * end.x + h11 * span * v1.x),
|
|
156
|
-
z: cleanZero(h00 * start.z + h10 * span * v0.z + h01 * end.z + h11 * span * v1.z),
|
|
157
|
-
heading: 0,
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// Path-tangent headings. Interior samples use the central difference
|
|
162
|
-
// between neighbors, the ends use their adjacent segment; displacements
|
|
163
|
-
// under 1e-6 (stalls) carry the previous heading, 0 when stationary.
|
|
164
|
-
// Sign convention measured against the deployed generator, not assumed.
|
|
165
|
-
// With the path aligned so heading[0] = 0 matches the model's forced
|
|
166
|
-
// frame-0 facing, the generated hips reproduce the SENT angle exactly
|
|
167
|
-
// (a probe sending mirrored headings walked the second segment facing
|
|
168
|
-
// +77.7° while travelling at −77.7°). Earlier mirror-looking results
|
|
169
|
-
// came from clips whose frame-0 facing conflicted with heading[0] and
|
|
170
|
-
// corrupted the whole track. Model heading = atan2(dx, dz) of the
|
|
171
|
-
// facing/travel direction; 0 faces +Z.
|
|
172
|
-
let previousHeading = 0;
|
|
173
|
-
for (let i = 0; i < samples.length; i += 1) {
|
|
174
|
-
let dx = 0;
|
|
175
|
-
let dz = 0;
|
|
176
|
-
if (samples.length > 1) {
|
|
177
|
-
if (i === 0) {
|
|
178
|
-
dx = samples[1].x - samples[0].x;
|
|
179
|
-
dz = samples[1].z - samples[0].z;
|
|
180
|
-
} else if (i === samples.length - 1) {
|
|
181
|
-
dx = samples[i].x - samples[i - 1].x;
|
|
182
|
-
dz = samples[i].z - samples[i - 1].z;
|
|
183
|
-
} else {
|
|
184
|
-
dx = samples[i + 1].x - samples[i - 1].x;
|
|
185
|
-
dz = samples[i + 1].z - samples[i - 1].z;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
if (dx * dx + dz * dz >= 1e-12) previousHeading = Math.atan2(dx, dz);
|
|
189
|
-
samples[i].heading = cleanZero(previousHeading);
|
|
190
|
-
}
|
|
191
|
-
return samples;
|
|
192
|
-
}
|
|
193
|
-
/**
|
|
194
|
-
* Align an authored root path for ARDY generation: compute the clip-local
|
|
195
|
-
* first travel tangent (first non-zero authored displacement, squared
|
|
196
|
-
* length >= 1e-12) and fold it into the total rotation so the densified
|
|
197
|
-
* path starts with heading 0, the model's forced frame-0 +Z facing. The
|
|
198
|
-
* returned rotationDeg is the scene->clip total rotation; handing it to
|
|
199
|
-
* toSceneRootOffset at playback restores scene coordinates exactly.
|
|
200
|
-
*/
|
|
201
|
-
export function alignArdyPath(waypoints, actorRotationDeg = 0, maxPoints = 32) {
|
|
202
|
-
if (waypoints.length < 2) return { waypoints: [], rotationDeg: actorRotationDeg };
|
|
203
|
-
// Fold the first *sampled* travel direction into the rotation, not the
|
|
204
|
-
// authored chord: the C1 resampler bends inside the first segment, so only
|
|
205
|
-
// the dense samples know the true first step. Rotation is rigid, so
|
|
206
|
-
// densifying again with the folded rotation rotates those samples exactly
|
|
207
|
-
// and heading[0] lands on 0 by construction.
|
|
208
|
-
const probe = densifyArdyWaypoints(waypoints, actorRotationDeg, maxPoints);
|
|
209
|
-
let phi = 0;
|
|
210
|
-
for (let i = 0; i < probe.length - 1; i += 1) {
|
|
211
|
-
const dx = probe[i + 1].x - probe[i].x;
|
|
212
|
-
const dz = probe[i + 1].z - probe[i].z;
|
|
213
|
-
if (dx * dx + dz * dz >= 1e-12) {
|
|
214
|
-
phi = Math.atan2(dx, dz);
|
|
215
|
-
break;
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
const rotationDeg = actorRotationDeg + (phi * 180) / Math.PI;
|
|
219
|
-
return {
|
|
220
|
-
waypoints: densifyArdyWaypoints(waypoints, rotationDeg, maxPoints),
|
|
221
|
-
rotationDeg,
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/** Rotate an ARDY clip-local root offset back into CozyClay scene space. */
|
|
226
|
-
export function toSceneRootOffset(x, z, actorRotationDeg = 0) {
|
|
227
|
-
const yaw = (actorRotationDeg * Math.PI) / 180;
|
|
228
|
-
const cos = Math.cos(yaw);
|
|
229
|
-
const sin = Math.sin(yaw);
|
|
230
|
-
return {
|
|
231
|
-
x: cleanZero(cos * x + sin * z),
|
|
232
|
-
z: cleanZero(-sin * x + cos * z),
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* A full-body pose also constrains the root. On a root path it must share the
|
|
238
|
-
* frame-zero origin; otherwise the two constraints can pull the clip apart.
|
|
239
|
-
*/
|
|
240
|
-
export function poseConstraintFrame(requestedFrame, clipFrames, hasRootPath) {
|
|
241
|
-
if (hasRootPath) return 0;
|
|
242
|
-
return Math.max(0, Math.min(Math.round(requestedFrame) || 0, clipFrames - 1));
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// What the generator can actually express, learned the hard way: waypoints
|
|
246
|
-
// are inpainting observations ARDY cannot refuse, so a request outside the
|
|
247
|
-
// training distribution collapses into foot-sliding instead of erroring.
|
|
248
|
-
// These limits keep authored paths inside that distribution.
|
|
249
|
-
export const PATH_LIMITS = Object.freeze({
|
|
250
|
-
speedMinMps: 0.5, // below this, "walking" cannot cycle a gait — feet slide
|
|
251
|
-
speedMaxMps: 3.0, // above this is out of the locomotion band entirely
|
|
252
|
-
holdEpsM: 0.05, // a displacement this small is a deliberate hold, any duration
|
|
253
|
-
minPinGapFrames: 8, // authored pins closer than this over-rail the root
|
|
254
|
-
speedRatioWarn: 2, // adjacent legs differing more than 2x force a gait change
|
|
255
|
-
turnRateWarnDegPerS: 120, // sharper turns need wider arcs than the path gives
|
|
256
|
-
tailMaxS: 2, // unconstrained tail after the last pin inherits its history
|
|
257
|
-
clipMaxS: 10, // ARDY's trained window; one-shot constrained calls cannot exceed it
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
const NATURAL_WALK_MPS = 1.4;
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* Judge the segment a candidate pin would create after `last`. Returns
|
|
264
|
-
* { ok, error?, warnings: [] } — `error` blocks the placement and always
|
|
265
|
-
* names the fix (a workable frame or distance), `warnings` accompany a
|
|
266
|
-
* placement that is legal but will read oddly.
|
|
267
|
-
* @param {{frame:number,x:number,z:number}} last the previous pin (or the frame-0 start)
|
|
268
|
-
* @param {{frame:number,x:number,z:number}} candidate the pin being authored
|
|
269
|
-
* @param {number} fps
|
|
270
|
-
* @param {{frame:number,x:number,z:number}|null} prev the pin before `last`, for ratio/turn warnings
|
|
271
|
-
*/
|
|
272
|
-
export function judgeNextWaypoint(last, candidate, fps, prev = null) {
|
|
273
|
-
const gap = candidate.frame - last.frame;
|
|
274
|
-
if (gap < PATH_LIMITS.minPinGapFrames) {
|
|
275
|
-
return {
|
|
276
|
-
ok: false,
|
|
277
|
-
error: isKo ? `핀 사이에는 최소 ${PATH_LIMITS.minPinGapFrames}프레임(${(PATH_LIMITS.minPinGapFrames / fps).toFixed(1)}초)이 필요해요 — ${last.frame + PATH_LIMITS.minPinGapFrames}프레임 이후로 이동해 주세요` : `pins need at least ${PATH_LIMITS.minPinGapFrames} frames between them (${(PATH_LIMITS.minPinGapFrames / fps).toFixed(1)} s) — scrub past frame ${last.frame + PATH_LIMITS.minPinGapFrames}`,
|
|
278
|
-
warnings: [],
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
|
-
const dist = Math.hypot(candidate.x - last.x, candidate.z - last.z);
|
|
282
|
-
if (dist <= PATH_LIMITS.holdEpsM) return { ok: true, warnings: [] }; // a hold: legal at any length
|
|
283
|
-
const dt = gap / fps;
|
|
284
|
-
const speed = dist / dt;
|
|
285
|
-
if (speed > PATH_LIMITS.speedMaxMps) {
|
|
286
|
-
const neededFrames = Math.ceil((dist / PATH_LIMITS.speedMaxMps) * fps);
|
|
287
|
-
return {
|
|
288
|
-
ok: false,
|
|
289
|
-
error: isKo ? `${speed.toFixed(1)} m/s는 사람이 움직이기엔 너무 빨라요(최대 ${PATH_LIMITS.speedMaxMps}) — ${last.frame + neededFrames}프레임 이후에 핀을 찍거나 더 가까운 곳을 눌러 주세요` : `${speed.toFixed(1)} m/s is faster than anyone moves (max ${PATH_LIMITS.speedMaxMps}) — pin frame ${last.frame + neededFrames} or later, or click closer`,
|
|
290
|
-
warnings: [],
|
|
291
|
-
};
|
|
292
|
-
}
|
|
293
|
-
if (speed < PATH_LIMITS.speedMinMps) {
|
|
294
|
-
const walkFrame = last.frame + Math.max(PATH_LIMITS.minPinGapFrames, Math.round((dist / NATURAL_WALK_MPS) * fps));
|
|
295
|
-
const neededDist = (PATH_LIMITS.speedMinMps * dt).toFixed(1);
|
|
296
|
-
return {
|
|
297
|
-
ok: false,
|
|
298
|
-
error: isKo ? `${speed.toFixed(2)} m/s는 자연스럽게 걷기엔 너무 느려요(최소 ${PATH_LIMITS.speedMinMps}) — 대신 약 ${walkFrame}프레임에 핀을 찍거나 최소 ${neededDist} m 이상 떨어진 곳을 눌러 주세요` : `${speed.toFixed(2)} m/s is below a sustainable walk (min ${PATH_LIMITS.speedMinMps}) — pin frame ~${walkFrame} instead, or click at least ${neededDist} m away`,
|
|
299
|
-
warnings: [],
|
|
300
|
-
};
|
|
301
|
-
}
|
|
302
|
-
const warnings = [];
|
|
303
|
-
if (prev) {
|
|
304
|
-
const prevDist = Math.hypot(last.x - prev.x, last.z - prev.z);
|
|
305
|
-
if (prevDist > PATH_LIMITS.holdEpsM) {
|
|
306
|
-
const prevSpeed = prevDist / ((last.frame - prev.frame) / fps);
|
|
307
|
-
const ratio = Math.max(speed, prevSpeed) / Math.max(Math.min(speed, prevSpeed), 1e-6);
|
|
308
|
-
if (ratio > PATH_LIMITS.speedRatioWarn) {
|
|
309
|
-
warnings.push(isKo ? `이전 구간보다 속도가 ${ratio.toFixed(1)}배 달라요 — 걸음 변화가 눈에 보일 수 있어요` : `${ratio.toFixed(1)}x speed change from the previous leg — expect a visible gait shift`);
|
|
310
|
-
}
|
|
311
|
-
const before = Math.atan2(last.x - prev.x, last.z - prev.z);
|
|
312
|
-
const after = Math.atan2(candidate.x - last.x, candidate.z - last.z);
|
|
313
|
-
let turn = Math.abs(after - before);
|
|
314
|
-
if (turn > Math.PI) turn = 2 * Math.PI - turn;
|
|
315
|
-
const turnRate = (turn * 180) / Math.PI / dt;
|
|
316
|
-
if (turnRate > PATH_LIMITS.turnRateWarnDegPerS) {
|
|
317
|
-
warnings.push(isKo ? `${dt.toFixed(1)}초 동안 ${Math.round((turn * 180) / Math.PI)}° 회전은 걷는 동선치고 너무 급해요 — 프레임이나 거리를 더 주세요` : `${Math.round((turn * 180) / Math.PI)}° turn in ${dt.toFixed(1)} s is sharper than a walking arc — give it more frames or distance`);
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
return { ok: true, warnings };
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
/**
|
|
325
|
-
* Re-judge a whole authored path (frame-0 start included) before generation:
|
|
326
|
-
* placement-time checks can be invalidated later by removing a middle pin.
|
|
327
|
-
* @returns {{errors: string[], warnings: string[]}}
|
|
328
|
-
*/
|
|
329
|
-
export function judgeAuthoredPath(rootPath, fps, clipFrames, { chained = false } = {}) {
|
|
330
|
-
const errors = [];
|
|
331
|
-
const warnings = [];
|
|
332
|
-
const ordered = [...rootPath].sort((a, b) => a.frame - b.frame);
|
|
333
|
-
for (let i = 1; i < ordered.length; i += 1) {
|
|
334
|
-
const verdict = judgeNextWaypoint(ordered[i - 1], ordered[i], fps, i >= 2 ? ordered[i - 2] : null);
|
|
335
|
-
if (!verdict.ok) errors.push(isKo ? `구간 ${i} (${ordered[i - 1].frame}->${ordered[i].frame}프레임): ${verdict.error}` : `leg ${i} (frame ${ordered[i - 1].frame}->${ordered[i].frame}): ${verdict.error}`);
|
|
336
|
-
else for (const warning of verdict.warnings) warnings.push(isKo ? `구간 ${i}: ${warning}` : `leg ${i}: ${warning}`);
|
|
337
|
-
}
|
|
338
|
-
// The trained window binds ONE model call. A chained rollout (a prompt
|
|
339
|
-
// schedule) makes a call per block, so the whole clip may exceed it —
|
|
340
|
-
// each block is capped separately by the bridge and the box generator.
|
|
341
|
-
if (!chained && clipFrames / fps > PATH_LIMITS.clipMaxS) {
|
|
342
|
-
errors.push(
|
|
343
|
-
isKo
|
|
344
|
-
? `이동 경로는 전체 클립을 한 번의 모델 호출로 만들어요. 모션 모델이 학습한 길이는 ${PATH_LIMITS.clipMaxS}초까지예요 — 클립을 ${PATH_LIMITS.clipMaxS}초로 줄이거나 프롬프트를 블록으로 나눠 이어 만들기를 사용해 주세요`
|
|
345
|
-
: `a root path generates the whole clip in one model call, and the motion model's trained window is ${PATH_LIMITS.clipMaxS} s — shorten the clip to ${PATH_LIMITS.clipMaxS} s, or split the prompt into blocks so the path rides a chained rollout`,
|
|
346
|
-
);
|
|
347
|
-
}
|
|
348
|
-
const tail = (clipFrames - 1 - (ordered[ordered.length - 1]?.frame ?? 0)) / fps;
|
|
349
|
-
if (ordered.length > 1 && tail > PATH_LIMITS.tailMaxS) {
|
|
350
|
-
warnings.push(
|
|
351
|
-
isKo
|
|
352
|
-
? `마지막 핀 뒤에 클립이 ${tail.toFixed(1)}초 남아 있어요 — 제약이 없는 끝부분은 경로가 끝난 상태를 그대로 이어가요`
|
|
353
|
-
: `${tail.toFixed(1)} s of clip remain after the last pin — the unconstrained tail continues whatever the path ends in`,
|
|
354
|
-
);
|
|
355
|
-
}
|
|
356
|
-
return { errors, warnings };
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
/** Place a newly-authored keyframe where it is immediately visible and draggable. */
|
|
360
|
-
export function defaultWaypointPosition(waypoints, subject) {
|
|
361
|
-
if (!waypoints.length) return { x: subject.x, z: subject.z };
|
|
362
|
-
const last = waypoints[waypoints.length - 1];
|
|
363
|
-
if (waypoints.length > 1) {
|
|
364
|
-
const previous = waypoints[waypoints.length - 2];
|
|
365
|
-
return { x: last.x + (last.x - previous.x), z: last.z + (last.z - previous.z) };
|
|
366
|
-
}
|
|
367
|
-
const yaw = (subject.rot * Math.PI) / 180;
|
|
368
|
-
return { x: last.x + Math.sin(yaw), z: last.z + Math.cos(yaw) };
|
|
369
|
-
}
|
|
@@ -1,281 +0,0 @@
|
|
|
1
|
-
import { ko, isKo } from "./locale.js";
|
|
2
|
-
import { LINE_EDIT_TRACK_IDS, PINNED_CURVE_ENDS } from "./line-edit.js";
|
|
3
|
-
import { FK_TRACKS, IK_TRACKS, MID_TRACKS } from "./ardy/ik.js";
|
|
4
|
-
|
|
5
|
-
// Two clocks, one boundary. The app timeline runs the production 24 fps —
|
|
6
|
-
// the rate the reference footage and the recorded export are counted in.
|
|
7
|
-
// ARDY Core generates on its trained 20 fps clock and that cannot move, so
|
|
8
|
-
// takes are RETIMED inbound (bridge → app, retimeMotion) and frame numbers
|
|
9
|
-
// are CONVERTED outbound (app → bridge, toArdyFrame). Nothing between the
|
|
10
|
-
// boundaries may mix the clocks.
|
|
11
|
-
export const TIMELINE_FPS = 24;
|
|
12
|
-
// The motion bridge uses the Studio's fixed 24 fps production clock.
|
|
13
|
-
export const ARDY_FPS = 24;
|
|
14
|
-
export const toArdyFrame = (frame) => Math.round((frame * ARDY_FPS) / TIMELINE_FPS);
|
|
15
|
-
|
|
16
|
-
// Outbound converters: timeline-frame entries → strictly-ascending bridge
|
|
17
|
-
// frames. Rounding can land two timeline frames on one bridge frame; the
|
|
18
|
-
// first wins — the bridge refuses non-ascending lists outright.
|
|
19
|
-
/** Where a placed pose lands, in TIMELINE frames, for a clip of `clipFrames`. */
|
|
20
|
-
export function posePlacementFrame(placement, clipFrames, playheadFrame) {
|
|
21
|
-
const last = Math.max(0, clipFrames - 1);
|
|
22
|
-
if (placement === "end") return last;
|
|
23
|
-
if (placement === "middle") return Math.round(last / 2);
|
|
24
|
-
if (placement === "playhead") return Math.max(0, Math.min(last, playheadFrame));
|
|
25
|
-
return 0;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function toArdyFrameEntries(entries) {
|
|
29
|
-
const out = [];
|
|
30
|
-
for (const entry of entries) {
|
|
31
|
-
const frame = toArdyFrame(entry.frame);
|
|
32
|
-
if (out.length && frame <= out[out.length - 1].frame) continue;
|
|
33
|
-
out.push({ ...entry, frame });
|
|
34
|
-
}
|
|
35
|
-
return out;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function toArdySegments(segments) {
|
|
39
|
-
const out = [];
|
|
40
|
-
for (const segment of segments) {
|
|
41
|
-
const startFrame = toArdyFrame(segment.startFrame);
|
|
42
|
-
const endFrame = toArdyFrame(segment.endFrame);
|
|
43
|
-
if (endFrame <= startFrame) continue; // a zero-length rounding remnant covers nothing
|
|
44
|
-
out.push({ ...segment, startFrame, endFrame });
|
|
45
|
-
}
|
|
46
|
-
return out;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export const DEFAULT_DURATION_S = 15; // pre-motion timeline duration; shown as duration × TIMELINE_FPS frames
|
|
50
|
-
export const DEFAULT_PLAYBACK_SPEED = 1;
|
|
51
|
-
export const BRIDGE_RECHECK_MS = 3000;
|
|
52
|
-
export const ARDY_PROMPT_HORIZON_FRAMES = 2 * TIMELINE_FPS; // core model horizon: 2 seconds, counted on the timeline clock
|
|
53
|
-
export const MAX_WAYPOINTS = 32; // ARDY bridge contract: a root path holds 2..32 distinct waypoint frames
|
|
54
|
-
export const ARDY_PROMPT_MAX = 500; // bridge contract: prompt must be non-empty, capped at 500 chars
|
|
55
|
-
export const ARDY_DURATION_MIN = 1; // the UI works in whole seconds; the bridge floor is 0.15 s
|
|
56
|
-
export const ARDY_DURATION_MAX = 1200; // bridge contract: duration capped at 1200 s
|
|
57
|
-
export const ARDY_SEED_MAX = 2 ** 31 - 1; // bridge contract: optional seed, integer in 0..2**31-1
|
|
58
|
-
// Scheduled inpainting (contract C3): how hard a regeneration holds onto the
|
|
59
|
-
// take that is already loaded. 0 turns preserving OFF entirely; 0.5 is the
|
|
60
|
-
// paper's recommended setting. The app ships this RAW number and nothing else:
|
|
61
|
-
// the strength -> denoising-schedule mapping (sigma_s = round(1000 * strength),
|
|
62
|
-
// sigma_e = min(50, sigma_s), diffusion time 0..1000) lives on the box side, and
|
|
63
|
-
// duplicating it here is exactly how the two would silently drift apart.
|
|
64
|
-
export const ARDY_PRESERVE_DEFAULT = 0.5;
|
|
65
|
-
export const DEFAULT_PROMPT_CLIPS = [];
|
|
66
|
-
|
|
67
|
-
/** The prompt-block schedule: every authored block in frame order, with the gaps
|
|
68
|
-
* between them filled by the base prompt so the bridge always receives one
|
|
69
|
-
* contiguous 0..clipFrames sequence. Blocks shorter than 3 frames are dropped —
|
|
70
|
-
* they cannot carry a generation.
|
|
71
|
-
* Pure and shared on purpose: the request assembly decides which blocks count as
|
|
72
|
-
* "edited", and the preserve panel has to name the SAME blocks. Re-deriving them
|
|
73
|
-
* approximately beside the slider is how the UI starts promising a locality the
|
|
74
|
-
* wire does not carry. */
|
|
75
|
-
export function buildPromptSchedule(sourcePromptClips, clipFrames, prompt) {
|
|
76
|
-
const segments = [];
|
|
77
|
-
let cursor = 0;
|
|
78
|
-
for (const clip of sourcePromptClips) {
|
|
79
|
-
const startFrame = Math.max(cursor, Math.min(clipFrames, clip.startFrame));
|
|
80
|
-
const endFrame = Math.max(startFrame, Math.min(clipFrames, clip.endFrame));
|
|
81
|
-
if (startFrame > cursor) segments.push({ startFrame: cursor, endFrame: startFrame, prompt });
|
|
82
|
-
if (endFrame - startFrame >= 3) segments.push({ startFrame, endFrame, prompt: clip.text.trim() || prompt });
|
|
83
|
-
cursor = Math.max(cursor, endFrame);
|
|
84
|
-
if (cursor >= clipFrames) break;
|
|
85
|
-
}
|
|
86
|
-
if (cursor < clipFrames) segments.push({ startFrame: cursor, endFrame: clipFrames, prompt });
|
|
87
|
-
if (segments.length === 0) segments.push({ startFrame: 0, endFrame: clipFrames, prompt });
|
|
88
|
-
return segments;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/** The DISTINCT ik track ids keyed inside the half-open timeline span
|
|
92
|
-
* [startFrame, endFrame). `ikState.keys` is Map(frame -> Map(trackId -> pose)),
|
|
93
|
-
* so a frame with several dragged effectors contributes all of them. */
|
|
94
|
-
export function ikTracksInRange(ikState, ikFrames, startFrame, endFrame) {
|
|
95
|
-
const tracks = new Set();
|
|
96
|
-
for (const frame of ikFrames) {
|
|
97
|
-
if (frame < startFrame || frame >= endFrame) continue;
|
|
98
|
-
for (const trackId of ikState?.keys?.get(frame)?.keys() ?? []) tracks.add(trackId);
|
|
99
|
-
}
|
|
100
|
-
return [...tracks];
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/* Which limb a keyed IK track frees once the preserve mask is grouped
|
|
104
|
-
(contract C3v2). The single source of truth for track -> mask group is
|
|
105
|
-
TRACK_GROUPS in tools/kimodo/preserve-mask.mjs; nothing under tools/ may be
|
|
106
|
-
imported into src/, so the rule is transcribed here — as USER-FACING LABELS
|
|
107
|
-
only, never as a second copy of the mapping the wire depends on:
|
|
108
|
-
leftHand, leftElbow -> leftArm rightHand, rightElbow -> rightArm
|
|
109
|
-
leftFoot, leftKnee -> leftLeg rightFoot, rightKnee -> rightLeg
|
|
110
|
-
head, neck -> head spine, chest, leftShoulder,
|
|
111
|
-
hips -> torso AND root rightShoulder -> torso
|
|
112
|
-
The track ids are the same namespace on both sides (src/ardy/ik.js owns
|
|
113
|
-
them), so the app ships whatever ids it has and the mask builder maps them.
|
|
114
|
-
Torso and pelvis tracks are worded as the whole body: the pelvis carries the
|
|
115
|
-
global transform as well as a bone, and naming a limb beside it would promise
|
|
116
|
-
a locality the mask does not have. */
|
|
117
|
-
export const PRESERVE_TRACK_LIMBS = {
|
|
118
|
-
leftHand: "leftArm",
|
|
119
|
-
leftElbow: "leftArm",
|
|
120
|
-
rightHand: "rightArm",
|
|
121
|
-
rightElbow: "rightArm",
|
|
122
|
-
leftFoot: "leftLeg",
|
|
123
|
-
leftKnee: "leftLeg",
|
|
124
|
-
rightFoot: "rightLeg",
|
|
125
|
-
rightKnee: "rightLeg",
|
|
126
|
-
head: "head",
|
|
127
|
-
neck: "head",
|
|
128
|
-
spine: "body",
|
|
129
|
-
chest: "body",
|
|
130
|
-
leftShoulder: "body",
|
|
131
|
-
rightShoulder: "body",
|
|
132
|
-
hips: "body",
|
|
133
|
-
};
|
|
134
|
-
// A fixed reading order, so the same edit always reads back the same way.
|
|
135
|
-
export const PRESERVE_LIMB_ORDER = ["head", "leftArm", "rightArm", "leftLeg", "rightLeg"];
|
|
136
|
-
export const preserveLimbLabel = (limb) => ({
|
|
137
|
-
head: ko("head", "머리"),
|
|
138
|
-
leftArm: ko("left arm", "왼팔"),
|
|
139
|
-
rightArm: ko("right arm", "오른팔"),
|
|
140
|
-
leftLeg: ko("left leg", "왼발"),
|
|
141
|
-
rightLeg: ko("right leg", "오른발"),
|
|
142
|
-
}[limb]);
|
|
143
|
-
|
|
144
|
-
/** One muted line naming what a grouped preserve run will regenerate, or "" when
|
|
145
|
-
* no track can be attributed — then the request carries no `tracks` either and
|
|
146
|
-
* the panel's existing whole-take wording is already the truth. */
|
|
147
|
-
export function preserveTracksSummary(tracks) {
|
|
148
|
-
const limbs = new Set();
|
|
149
|
-
for (const trackId of tracks) {
|
|
150
|
-
const limb = PRESERVE_TRACK_LIMBS[trackId];
|
|
151
|
-
if (limb) limbs.add(limb);
|
|
152
|
-
}
|
|
153
|
-
if (limbs.size === 0) return "";
|
|
154
|
-
if (limbs.has("body")) return ko("regenerates the whole body", "몸 전체를 다시 생성");
|
|
155
|
-
const names = PRESERVE_LIMB_ORDER.filter((limb) => limbs.has(limb)).map(preserveLimbLabel);
|
|
156
|
-
return isKo ? `${names.join("·")}만 다시 생성` : `regenerates ${names.join(" · ")} only`;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/* --------------------------- line editing (C6) ---------------------------
|
|
160
|
-
* "Grab the joint's motion path and pull it; the joint follows the pulled path
|
|
161
|
-
* exactly" (ProjFlow). The arithmetic lives in ./line-edit.js; what stays here
|
|
162
|
-
* is the panel's own vocabulary and the one thing App owns that the pure module
|
|
163
|
-
* cannot — the mapping from a track id to a HUMAN LABEL, which is read straight
|
|
164
|
-
* out of ik.js's three tables so the picker can never drift from the pose
|
|
165
|
-
* studio's own naming. An id in LINE_EDIT_TRACK_IDS with no entry in those
|
|
166
|
-
* tables is a bug, and falling back to the raw id makes it visible instead of
|
|
167
|
-
* silent. */
|
|
168
|
-
export const LINE_EDIT_TRACK_OPTIONS = LINE_EDIT_TRACK_IDS.map((id) => ({
|
|
169
|
-
value: id,
|
|
170
|
-
label: [...IK_TRACKS, ...MID_TRACKS, ...FK_TRACKS].find((track) => track.id === id)?.label ?? id,
|
|
171
|
-
}));
|
|
172
|
-
/** The same human label, by id — what a version-strip chip says a refinement
|
|
173
|
-
* touched ("Refine · Left Hand"). */
|
|
174
|
-
export const lineTrackLabel = (id) => LINE_EDIT_TRACK_OPTIONS.find((option) => option.value === id)?.label ?? id;
|
|
175
|
-
/** The default edited track: a wrist is the joint the paper's own demo draws
|
|
176
|
-
* with, and the one whose screen trajectory a user can actually see. */
|
|
177
|
-
export const LINE_EDIT_DEFAULT_TRACK = "leftHand";
|
|
178
|
-
/** Draw a grab handle every Nth frame. Fewer would leave the curve looking
|
|
179
|
-
* un-grabbable in the gaps; more turns a 200-frame range into a solid bead of
|
|
180
|
-
* dots. The hit test tolerates CURVE_GRAB_RADIUS_PX regardless, so the handles
|
|
181
|
-
* are an affordance rather than the actual geometry. */
|
|
182
|
-
export const LINE_CURVE_MARKER_STRIDE = 4;
|
|
183
|
-
/** The shortest curve with anything draggable in it: both ends are hard-pinned
|
|
184
|
-
* to the original trajectory, so a range needs more than 2 x PINNED_CURVE_ENDS
|
|
185
|
-
* frames before the middle can move at all. */
|
|
186
|
-
export const MIN_CURVE_POINTS = PINNED_CURVE_ENDS * 2 + 1;
|
|
187
|
-
/** How long a released drag has to sit still before it costs a preview. Long
|
|
188
|
-
* enough that pull-pull-pull is one request instead of three, short enough that
|
|
189
|
-
* the loop still feels like it answers the gesture — a preview round trip is
|
|
190
|
-
* ~1 s on the warm service, so 150 ms is noise against it. */
|
|
191
|
-
export const LINE_PREVIEW_DEBOUNCE_MS = 150;
|
|
192
|
-
/** How often the line-edit capability is re-probed while the bridge is up and
|
|
193
|
-
* the answer is still no.
|
|
194
|
-
*
|
|
195
|
-
* The bridge answers `capabilities.lineEdit` from a LAZY ssh probe of the
|
|
196
|
-
* ProjFlow box behind a 5 s cache, so the very first /ardy/health of a session
|
|
197
|
-
* can land on a cold cache and come back false — a box that is perfectly fine
|
|
198
|
-
* two seconds later. Asking once per bridge-up transition therefore made the
|
|
199
|
-
* whole mode dead until a reload (first boot broken, second boot fine, which is
|
|
200
|
-
* exactly how it was reported). A capability is an ADVERTISEMENT, so it is
|
|
201
|
-
* allowed to arrive late; what is not allowed is to assume it. 4 s sits just
|
|
202
|
-
* under the bridge's own 5 s TTL, so a retry can actually see a new answer
|
|
203
|
-
* rather than re-reading the same cached false. */
|
|
204
|
-
export const LINE_CAPABILITY_RETRY_MS = 4000;
|
|
205
|
-
/** Why a validateLineEdit code refused, said once, in the user's language.
|
|
206
|
-
* Keyed by the pure module's stable codes so the copy and the check can never
|
|
207
|
-
* disagree about which field was wrong. */
|
|
208
|
-
export const LINE_EDIT_REFUSALS = {
|
|
209
|
-
sourceMotion: ["The current take has no bridge source — generate it once before editing a path", "현재 테이크에 브리지 원본이 없어요 — 궤적을 편집하기 전에 한 번 생성하세요"],
|
|
210
|
-
track: ["Pick a joint to edit first", "먼저 편집할 관절을 고르세요"],
|
|
211
|
-
frameRange: ["The frame range must sit inside the clip and span at least 2 frames", "프레임 구간은 클립 안에 있어야 하고 최소 2프레임이어야 해요"],
|
|
212
|
-
points: ["Pull a longer stretch of the path — a single point is not a path", "궤적을 좀 더 길게 잡아당겨 주세요 — 점 하나는 경로가 아니에요"],
|
|
213
|
-
camera: ["The camera could not be captured — nudge the view and try again", "카메라를 캡처하지 못했어요 — 뷰를 조금 움직인 뒤 다시 시도하세요"],
|
|
214
|
-
prompt: ["The motion prompt is not usable for a line edit", "모션 프롬프트를 라인 편집에 쓸 수 없어요"],
|
|
215
|
-
pins: ["The pinned moments could not be sent — try clearing them and pinning again", "찍은 순간을 보낼 수 없어요 — 지우고 다시 찍어 보세요"],
|
|
216
|
-
shape: ["The line edit could not be assembled", "라인 편집을 구성하지 못했어요"],
|
|
217
|
-
};
|
|
218
|
-
/** Why curveToPoints2d refused. Same shape, different stage: these are about
|
|
219
|
-
* the CURVE, before there is a payload to validate. */
|
|
220
|
-
export const LINE_CURVE_REFUSALS = {
|
|
221
|
-
empty: ["This joint is not visible over the chosen frames — frame it in view first", "선택한 구간에서 이 관절이 보이지 않아요 — 먼저 화면 안에 들어오도록 잡아 주세요"],
|
|
222
|
-
offscreen: ["The pulled path leaves the frame — keep it inside the dashed border", "잡아당긴 궤적이 화면을 벗어났어요 — 점선 테두리 안에 두세요"],
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
// Named ingest failures, in both locales. A reason the user cannot act on is
|
|
226
|
-
// not a message: each line says what was wrong with THIS source.
|
|
227
|
-
export const MULTIMODEL_REASONS = {
|
|
228
|
-
"url-empty": ["Enter a video URL first", "영상 URL을 먼저 입력하세요"],
|
|
229
|
-
"url-protocol-relative": ["Use a full https:// address", "https:// 로 시작하는 전체 주소를 쓰세요"],
|
|
230
|
-
"url-scheme-unsupported": ["Only http(s) or a local /path is accepted", "http(s) 또는 로컬 /경로만 사용할 수 있어요"],
|
|
231
|
-
"url-platform-page": [
|
|
232
|
-
"YouTube/Vimeo pages are players, not video files, and block browser downloads. Start the dev bridge to use the URL directly, or download the clip and use Choose video.",
|
|
233
|
-
"유튜브·비메오 주소는 영상 파일이 아니라 플레이어 페이지이고 브라우저 다운로드를 막습니다. 개발 브리지를 켜면 URL을 그대로 쓸 수 있고, 아니면 영상을 내려받아 '영상 선택'을 쓰세요.",
|
|
234
|
-
],
|
|
235
|
-
"url-malformed": ["That address could not be parsed", "주소를 해석할 수 없어요"],
|
|
236
|
-
"fetch-failed": ["The host refused a browser download (CORS or offline)", "호스트가 브라우저 다운로드를 거부했어요(CORS 또는 오프라인)"],
|
|
237
|
-
"not-a-video": ["That address did not return a video (check the path)", "그 주소는 영상을 반환하지 않았어요(경로를 확인하세요)"],
|
|
238
|
-
"decode-failed": ["This browser cannot decode that video", "이 브라우저가 디코딩할 수 없는 영상이에요"],
|
|
239
|
-
"duration-unreadable": ["The clip length could not be read", "클립 길이를 읽지 못했어요"],
|
|
240
|
-
"dimensions-unreadable": ["The frame size could not be read", "프레임 크기를 읽지 못했어요"],
|
|
241
|
-
"probe-timeout": ["The video never reported metadata", "영상이 메타데이터를 보내지 않았어요"],
|
|
242
|
-
"fetch-unavailable": ["This browser cannot download files", "이 브라우저는 파일을 내려받을 수 없어요"],
|
|
243
|
-
"footage-load-timeout": ["The clip never became seekable", "클립이 탐색 가능한 상태가 되지 않았어요"],
|
|
244
|
-
"seek-timeout": ["Seeking inside the clip stalled", "클립 내부 탐색이 멈췄어요"],
|
|
245
|
-
"pose-runtime-unavailable": ["The pose engine is missing from this build", "이 빌드에 포즈 엔진이 없어요"],
|
|
246
|
-
"pose-model-download-failed": ["The pose model could not be downloaded (offline?)", "포즈 모델을 내려받지 못했어요(오프라인인가요?)"],
|
|
247
|
-
"pose-engine-download-failed": ["The pose engine could not be downloaded (offline?)", "포즈 엔진을 내려받지 못했어요(오프라인인가요?)"],
|
|
248
|
-
"pose-engine-init-failed": ["The pose engine failed to start on this device", "이 기기에서 포즈 엔진을 시작하지 못했어요"],
|
|
249
|
-
"rest-unavailable": ["The rig rest data (/ardy/cskel27-rest.json) could not be loaded", "리그 레스트 데이터(/ardy/cskel27-rest.json)를 불러오지 못했어요"],
|
|
250
|
-
"no-person-found": ["No person was detected in the footage", "영상에서 사람을 감지하지 못했어요"],
|
|
251
|
-
"no-person-in-photo": ["No person was detected in that photograph", "사진에서 사람을 감지하지 못했어요"],
|
|
252
|
-
"image-load-timeout": ["The photograph never finished decoding", "사진 디코딩이 끝나지 않았어요"],
|
|
253
|
-
"pose-partly-occluded": ["Shoulders and hips must both be visible in the photograph", "사진에 어깨와 골반이 모두 보여야 해요"],
|
|
254
|
-
"no-usable-pose": ["A person was seen, but no stable pose could be fitted", "사람은 보였지만 안정적인 포즈를 만들지 못했어요"],
|
|
255
|
-
"rig-not-loaded": ["Subject 1's rig is not loaded yet", "인물 1 리그가 아직 로드되지 않았어요"],
|
|
256
|
-
"bridge-unreachable": ["The dev bridge did not answer", "개발 브리지가 응답하지 않아요"],
|
|
257
|
-
"bridge-footage-incomplete": ["The bridge stream ended without footage", "브리지 전송이 영상 없이 끝났어요"],
|
|
258
|
-
"footage-url-invalid": ["That address could not be used for a bridge download", "이 주소는 브리지 다운로드에 쓸 수 없어요"],
|
|
259
|
-
"footage-probe-failed": ["The bridge could not read that page's video info", "브리지가 그 페이지의 영상 정보를 읽지 못했어요"],
|
|
260
|
-
"footage-live-unsupported": ["Live streams have no fixed length and cannot be ingested", "라이브 스트림은 길이가 없어 인제스트할 수 없어요"],
|
|
261
|
-
"footage-too-long": ["That video is over 15 minutes — trim or pick a shorter one", "15분이 넘는 영상이에요 — 잘라내거나 더 짧은 걸 골라 주세요"],
|
|
262
|
-
"footage-download-failed": ["The bridge could not download that video", "브리지가 영상을 내려받지 못했어요"],
|
|
263
|
-
"footage-normalize-failed": ["The bridge could not convert that video for extraction", "브리지가 영상을 추출용으로 변환하지 못했어요"],
|
|
264
|
-
"footage-timeout": ["The bridge download took too long and was stopped", "브리지 다운로드가 너무 오래 걸려 중단됐어요"],
|
|
265
|
-
"bridge-extract-incomplete": ["The bridge stream ended without a take", "브리지 전송이 테이크 없이 끝났어요"],
|
|
266
|
-
"extract-host-missing": ["The bridge has no GPU box configured (CCLAY_EXTRACT_HOST)", "브리지에 GPU 박스가 설정돼 있지 않아요(CCLAY_EXTRACT_HOST)"],
|
|
267
|
-
"extract-bridge-required": ["GVHMR extraction requires the local GPU bridge", "GVHMR 추출에는 로컬 GPU 브리지가 필요해요"],
|
|
268
|
-
"extract-backend-unsupported": ["This bridge is not configured for GVHMR", "이 브리지가 GVHMR로 설정되지 않았어요"],
|
|
269
|
-
"extract-upload-failed": ["The footage could not be copied to the GPU box", "영상을 GPU 박스로 복사하지 못했어요"],
|
|
270
|
-
"extract-upload-too-large": ["That clip is too large to upload for extraction (300 MB cap)", "추출 업로드 한도(300MB)를 넘는 영상이에요"],
|
|
271
|
-
"extract-upload-empty": ["No video bytes arrived at the bridge", "브리지에 영상 데이터가 도착하지 않았어요"],
|
|
272
|
-
"extract-footage-unknown": ["The bridge no longer holds that download — re-ingest the URL", "브리지에 그 다운로드가 더 이상 없어요 — URL을 다시 넣어 주세요"],
|
|
273
|
-
"extract-run-failed": ["GVHMR failed on the GPU box (see the bridge log)", "GPU 박스에서 GVHMR 실행이 실패했어요(브리지 로그 확인)"],
|
|
274
|
-
"extract-no-person": ["The GPU box tracked no person in that footage", "GPU 박스가 영상에서 사람을 추적하지 못했어요"],
|
|
275
|
-
"extract-convert-failed": ["The extracted take could not be converted for the timeline", "추출된 테이크를 타임라인용으로 변환하지 못했어요"],
|
|
276
|
-
"extract-timeout": ["GPU extraction took too long and was stopped", "GPU 추출이 너무 오래 걸려 중단됐어요"],
|
|
277
|
-
};
|
|
278
|
-
|
|
279
|
-
// Extraction samples and bakes straight onto the production clock — an
|
|
280
|
-
// extracted take never touches ARDY, so it never needs retiming either.
|
|
281
|
-
export const MULTIMODEL_SAMPLE_FPS = TIMELINE_FPS;
|