cozyclay 2.1.1 → 3.0.1
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 +88 -0
- package/README.md +12 -30
- package/bin/agent/agent-routes.mjs +43 -116
- package/bin/agent/agent-runner.mjs +14 -15
- 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/session-store.mjs +2 -2
- package/bin/agent/studio-context-budget.mjs +93 -0
- 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-BWCqdgWA.js +1 -0
- package/dist/assets/app-DfTCxaKk.js +5087 -0
- package/dist/assets/app-f-fkV5uP.css +1 -0
- package/dist/assets/compile-worker-BxKXh07s.js +5 -0
- package/dist/assets/fflate.module-KbGj5-H6.js +5 -0
- package/dist/assets/{first-shot-handoff-msLI71g1.js → first-shot-handoff-DNO8kv_N.js} +1 -1
- package/dist/assets/{landing-ChUvB_zL.js → landing-yfZaJkWc.js} +1 -1
- package/dist/assets/scene-asset-sync-U3kS2rvK.js +20 -0
- package/dist/assets/scene-asset-sync-_S8NQl5B.css +1 -0
- package/dist/assets/workflow-D78eiKTa.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 +151 -2625
- package/src/agent-progress.js +80 -0
- package/src/agent-turn-mark.js +30 -0
- package/src/analytics.js +1 -19
- package/src/app-context.js +12 -2
- 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 +10 -8
- 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 +214 -0
- package/src/commands/objects.js +6 -4
- package/src/commands/shot.js +55 -13
- 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 +53 -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 +47 -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 +60 -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 +79 -0
- package/src/motion3/compile-worker.js +10 -0
- package/src/motion3/compiler/anchors.js +113 -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 +197 -0
- package/src/motion3/compiler/math.js +72 -0
- package/src/motion3/compiler/pose.js +177 -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 +144 -0
- package/src/motion3/compiler/validate-parts.js +118 -0
- package/src/motion3/compiler/validate.js +122 -0
- package/src/motion3/compiler/vocab.js +74 -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 +177 -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 +204 -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/people.js +82 -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 +125 -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 +195 -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 +44 -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/people-checks.js +37 -0
- package/src/motion3/qa/severity.js +38 -0
- package/src/motion3/qa/spec-checks.js +65 -0
- package/src/motion3/qa/style.js +228 -0
- package/src/motion3/qa/wrist-checks.js +69 -0
- package/src/motion3/qa/wrist-obs.js +60 -0
- package/src/motion3/refs.js +129 -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 +113 -0
- package/src/motion3/schema/types.js +149 -0
- package/src/motion3/search.js +117 -0
- package/src/motion3/second-hand.js +112 -0
- package/src/motion3/settle.js +136 -0
- package/src/motion3/skills/basic.js +83 -0
- package/src/motion3/skills/book.js +76 -0
- package/src/motion3/skills/car.js +145 -0
- package/src/motion3/skills/flip.js +200 -0
- package/src/motion3/skills/frames-auto.js +29 -0
- package/src/motion3/skills/impact.js +109 -0
- package/src/motion3/skills/index.js +85 -0
- package/src/motion3/skills/info.js +80 -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/qa-label.js +25 -0
- 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 +16 -14
- package/src/studio-agent-commands.js +19 -16
- package/src/studio-agent-motion.js +9 -9
- package/src/studio-agent-protocol.js +47 -77
- package/src/studio-app-binding.js +27 -47
- 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 +15 -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 +109 -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 +9 -125
- 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
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// Collision proxies for motion QA: body capsules on Mixamo joints, scene objects as oriented
|
|
2
|
+
// boxes (base pivot = bottom centre, CozyClay convention), the head as the measured ellipsoid.
|
|
3
|
+
import { add, sub, scale, dot, len, apply, T, clamp } from "../compiler/math.js";
|
|
4
|
+
import { bonePoint } from "../compiler/skeleton.js";
|
|
5
|
+
|
|
6
|
+
// [part, from joint, to joint, radius m]. Radii are fitted to the X/Y Bot skin.
|
|
7
|
+
export const CAPSULES = [
|
|
8
|
+
["torso_lo", "Spine", "Spine2", 0.12], ["torso_hi", "Spine2", "Neck", 0.105], ["hips", "LeftUpLeg", "RightUpLeg", 0.095],
|
|
9
|
+
["neck", "Neck", "Head", 0.055],
|
|
10
|
+
...["Left", "Right"].flatMap(S => {
|
|
11
|
+
const k = S === "Left" ? "l" : "r";
|
|
12
|
+
return [[`thigh_${k}`, `${S}UpLeg`, `${S}Leg`, 0.07], [`shin_${k}`, `${S}Leg`, `${S}Foot`, 0.05],
|
|
13
|
+
[`uarm_${k}`, `${S}Arm`, `${S}ForeArm`, 0.042], [`farm_${k}`, `${S}ForeArm`, `${S}Hand`, 0.033],
|
|
14
|
+
[`hand_${k}`, `${S}Hand`, `${S}HandMiddle3`, 0.022]];
|
|
15
|
+
}),
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
export const segPoints = (a, b, n = 9) => Array.from({ length: n }, (_, i) => add(a, scale(sub(b, a), i / (n - 1))));
|
|
19
|
+
|
|
20
|
+
/** Signed distance from p to a box (negative inside). */
|
|
21
|
+
export function sdBox(p, box) {
|
|
22
|
+
const q = apply(T(box.R), sub(p, box.pos));
|
|
23
|
+
const c = [0, box.half[1], 0];
|
|
24
|
+
const d = [Math.abs(q[0] - c[0]) - box.half[0], Math.abs(q[1] - c[1]) - box.half[1], Math.abs(q[2] - c[2]) - box.half[2]];
|
|
25
|
+
const outside = len([Math.max(d[0], 0), Math.max(d[1], 0), Math.max(d[2], 0)]);
|
|
26
|
+
return outside + Math.min(Math.max(d[0], d[1], d[2]), 0);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function segDist(p1, q1, p2, q2) {
|
|
30
|
+
let best = Infinity;
|
|
31
|
+
const d2 = sub(q2, p2), dd = dot(d2, d2);
|
|
32
|
+
for (const a of segPoints(p1, q1)) {
|
|
33
|
+
const u = dd < 1e-12 ? 0 : clamp(dot(sub(a, p2), d2) / dd, 0, 1);
|
|
34
|
+
best = Math.min(best, len(sub(a, add(p2, scale(d2, u)))));
|
|
35
|
+
}
|
|
36
|
+
return best;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Depth (m) of point p inside the head ellipsoid, negative when outside. */
|
|
40
|
+
export function headDepth(sk, s, p) {
|
|
41
|
+
const c = bonePoint(s, sk.index.Head, sk.landmarks.headCentre), R = s.R[sk.index.Head];
|
|
42
|
+
const q = apply(T(R), sub(p, c)), r = sk.landmarks.headRadii;
|
|
43
|
+
const v = len([q[0] / r[0], q[1] / r[1], q[2] / r[2]]);
|
|
44
|
+
return (1 - v) * Math.min(...r);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** The rig's standing ankle height (m): where foot_* stands a flat foot on the floor (X Bot 0.087, Y Bot 0.105). */
|
|
48
|
+
export const ankleY = sk => Math.round(sk.restWorldPos[sk.index.LeftFoot][1] * 1000) / 1000;
|
|
49
|
+
|
|
50
|
+
/** A point `drop` m straight below bone b's joint at bind, carried by b (the joint itself when 0). */
|
|
51
|
+
const below = (sk, s, b, drop) => (drop ? bonePoint(s, b, apply(T(sk.restWorldRot[b]), [0, -drop, 0])) : s.P[b]);
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Sole points carried by a foot, on the skin's sole (the surface verify_result's floor measure
|
|
55
|
+
* reads): heel 4.5 cm behind and 0.5 cm above the bottom of the ankle at bind; ball under the
|
|
56
|
+
* ToeBase joint at the toe pad; tip at the front edge. The rig's sole landmark says how far the
|
|
57
|
+
* skin lies below the joints (X Bot: on the ball joint; Y Bot: 3.3 cm under it).
|
|
58
|
+
*/
|
|
59
|
+
export function solePoints(sk, s, side) {
|
|
60
|
+
const I = sk.index, P = sk.restWorldPos, drop = sk.landmarks.sole ?? {};
|
|
61
|
+
const foot = I[`${side}Foot`], toe = I[`${side}ToeBase`], toeEnd = I[`${side}Toe_End`] ?? toe;
|
|
62
|
+
const heelRest = [P[foot][0], 0.005, P[foot][2] - 0.045];
|
|
63
|
+
const heelLocal = apply(T(sk.restWorldRot[foot]), sub(heelRest, P[foot]));
|
|
64
|
+
return { heel: bonePoint(s, foot, heelLocal), ball: below(sk, s, toe, drop.ball), tip: below(sk, s, toeEnd, drop.tip) };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Outward unit normal of a box at p (the nearest face from inside, the closest direction from outside). */
|
|
68
|
+
export function boxNormal(p, box) {
|
|
69
|
+
const q = apply(T(box.R), sub(p, box.pos));
|
|
70
|
+
q[1] -= box.half[1];
|
|
71
|
+
const d = q.map((v, i) => Math.abs(v) - box.half[i]);
|
|
72
|
+
let n;
|
|
73
|
+
if (Math.max(...d) < 0) { const i = d.indexOf(Math.max(...d)); n = [0, 0, 0]; n[i] = Math.sign(q[i]) || 1; }
|
|
74
|
+
else n = q.map((v, i) => (d[i] > 0 ? Math.sign(v) * d[i] : 0));
|
|
75
|
+
const l = len(n);
|
|
76
|
+
return apply(box.R, l > 1e-12 ? scale(n, 1 / l) : [0, 1, 0]);
|
|
77
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
// Goals (C8): what the motion must achieve, measured on the compiled frames. A goal names one
|
|
2
|
+
// predicate and the frames it must hold on (at: an absolute frame or [f0, f1]):
|
|
3
|
+
// near: [a, b] refDistance(a, b) <= tol (default 0.05 m)
|
|
4
|
+
// above: [a, b] a.y - b.y >= by (default 0 m)
|
|
5
|
+
// moved: ref an object's point (default its base) is at least by (default 0.05 m) from
|
|
6
|
+
// where the scene put it; any other point ref (prop:, joint:, landmark:, anchor:)
|
|
7
|
+
// gets at least by from where it was at at[0]: one value, its farthest frame
|
|
8
|
+
// still: ref the point stays within tol (default 0.01 m) of the same home moved measures
|
|
9
|
+
// from (an object: where the scene put it; any other point: where it is at at[0])
|
|
10
|
+
// speed: ref the point's speed (central difference, m/s) is at least by (no default)
|
|
11
|
+
// mode "all" (every frame of at; the default, except speed) or "any" (one frame; speed's default).
|
|
12
|
+
// after / before: "<goal name>" cut at to the frames after (before) the first frame the named goal
|
|
13
|
+
// holds, so an event the agent did not place on a frame can still order two goals ("the can moves
|
|
14
|
+
// after the kick"). A named goal that never holds, or holds too late, leaves no frames: the goal
|
|
15
|
+
// fails with value and at null and one GOAL issue (value 0.01) at at[0]; the named goal's own GOAL
|
|
16
|
+
// group carries the measure. Each goal answers one result { name, kind, ok, value, at, need }: for
|
|
17
|
+
// "all" the frame closest to failing (or the worst failure), for "any" the best frame. A failed goal
|
|
18
|
+
// adds GOAL issues (value = the shortfall, limit 0): "all" one per failing frame, so the group's
|
|
19
|
+
// count and first..last show how much of at fails and the search (search.js) sees progress; "any"
|
|
20
|
+
// one, at the best frame. Issue frames index out.frames (out.f0 + i); goals[].at and every frame in
|
|
21
|
+
// a message are absolute.
|
|
22
|
+
import { refPoint, refDistance } from "../refs.js";
|
|
23
|
+
import { partsOf } from "../object-pose.js";
|
|
24
|
+
import { add, apply, len, sub } from "../compiler/math.js";
|
|
25
|
+
import { m3 } from "./fmt.js";
|
|
26
|
+
|
|
27
|
+
/** The goal predicates, one per goal. */
|
|
28
|
+
export const GOAL_KINDS = ["near", "above", "moved", "still", "speed"];
|
|
29
|
+
const DEFAULT_NEED = { near: 0.05, above: 0, moved: 0.05, still: 0.01 };
|
|
30
|
+
/** Predicates met at value <= need (tol); the others are met at value >= need (by). */
|
|
31
|
+
const AT_MOST = new Set(["near", "still"]);
|
|
32
|
+
/** Predicates on one point ref (the others take [ref, ref]). */
|
|
33
|
+
export const ONE_REF = new Set(["moved", "still", "speed"]);
|
|
34
|
+
|
|
35
|
+
/** The predicates goal g names (validation wants exactly one). */
|
|
36
|
+
export const goalKinds = g => GOAL_KINDS.filter(k => g?.[k] !== undefined);
|
|
37
|
+
/** A goal's name: its own, else its place in spec.goals ("goals[1]", which groupKey keeps apart). */
|
|
38
|
+
export const goalName = (g, i) => g.name ?? `goals[${i}]`;
|
|
39
|
+
/** The bound: tol for near and still, by for above, moved and speed. */
|
|
40
|
+
export const goalNeed = (g, kind) => (AT_MOST.has(kind) ? g.tol : g.by) ?? DEFAULT_NEED[kind];
|
|
41
|
+
/** "all" or "any": the goal's own, else any for speed (a speed is reached) and all for the rest. */
|
|
42
|
+
const goalMode = (g, kind) => g.mode ?? (kind === "speed" ? "any" : "all");
|
|
43
|
+
|
|
44
|
+
/** moved and still measure the named point of an object ref (its base when none is named), any other ref as it is. */
|
|
45
|
+
export const movedRef = ref => (!ref.startsWith("object:") || /\.(center|top|base)$/.test(ref) ? ref : `${ref}.base`);
|
|
46
|
+
|
|
47
|
+
/** Where the scene put the point of a moved object ref: the same AABB point refs.js answers, from the scene boxes. */
|
|
48
|
+
function scenePoint(scene, ref) {
|
|
49
|
+
const [, id, which] = /^object:(.+)\.(center|top|base)$/s.exec(movedRef(ref)) ?? [];
|
|
50
|
+
const boxes = partsOf(scene, id).map(p => scene.objects[p]);
|
|
51
|
+
if (!boxes.length) return null;
|
|
52
|
+
const lo = [Infinity, Infinity, Infinity], hi = [-Infinity, -Infinity, -Infinity];
|
|
53
|
+
for (const b of boxes) for (const x of [-1, 1]) for (const y of [0, 2]) for (const z of [-1, 1]) {
|
|
54
|
+
const p = add(b.pos, apply(b.R, [x * b.half[0], y * b.half[1], z * b.half[2]]));
|
|
55
|
+
for (let i = 0; i < 3; i++) { lo[i] = Math.min(lo[i], p[i]); hi[i] = Math.max(hi[i], p[i]); }
|
|
56
|
+
}
|
|
57
|
+
const c = [(lo[0] + hi[0]) / 2, (lo[1] + hi[1]) / 2, (lo[2] + hi[2]) / 2];
|
|
58
|
+
return which === "top" ? [c[0], hi[1], c[2]] : which === "base" ? [c[0], lo[1], c[2]] : c;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** The point ref of a moved or still goal. */
|
|
62
|
+
const homeRef = g => g.moved ?? g.still;
|
|
63
|
+
|
|
64
|
+
/** Where a moved or still goal measures from: an object's point where the scene put it, any other point where it is at `from` (default at[0]). */
|
|
65
|
+
export const movedHome = (out, g, from = Array.isArray(g.at) ? g.at[0] : g.at) => (homeRef(g).startsWith("object:") ? scenePoint(out.ctx.scene, homeRef(g)) : refPoint(out, from, homeRef(g)));
|
|
66
|
+
|
|
67
|
+
/** The speed of point ref at absolute frame f (m/s): central difference, one-sided at the window's edges; null when unknown. */
|
|
68
|
+
function speedAt(out, ref, f) {
|
|
69
|
+
const p = refPoint(out, f, ref), fps = out.fps ?? 24;
|
|
70
|
+
if (!p) return null;
|
|
71
|
+
const a = refPoint(out, f - 1, ref), b = refPoint(out, f + 1, ref);
|
|
72
|
+
if (a && b) return (len(sub(b, a)) * fps) / 2;
|
|
73
|
+
const q = a ?? b;
|
|
74
|
+
return q ? len(sub(p, q)) * fps : null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** The measured value of goal g at absolute frame f (metres, m/s for speed), or null when a ref is unknown there. */
|
|
78
|
+
function measure(out, g, kind, f, home) {
|
|
79
|
+
if (kind === "near") return refDistance(out, f, g.near[0], g.near[1]);
|
|
80
|
+
if (kind === "above") {
|
|
81
|
+
const a = refPoint(out, f, g.above[0]), b = refPoint(out, f, g.above[1]);
|
|
82
|
+
return a && b ? a[1] - b[1] : null;
|
|
83
|
+
}
|
|
84
|
+
if (kind === "speed") return speedAt(out, g.speed, f);
|
|
85
|
+
const p = home && refPoint(out, f, movedRef(homeRef(g)));
|
|
86
|
+
return p ? len(sub(p, home)) : null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** How far value v is from meeting the bound (> 0 fails). */
|
|
90
|
+
const shortOf = (kind, v, need) => (AT_MOST.has(kind) ? v - need : need - v);
|
|
91
|
+
const round4 = x => Math.max(1e-4, Math.round(x * 1e4) / 1e4);
|
|
92
|
+
|
|
93
|
+
/** The measured frames of goal g over [a, b]: [{ f, v, short }], and the frame its point is measured from (a point's moved or still). */
|
|
94
|
+
function rowsOf(X, g, kind, a, b) {
|
|
95
|
+
const need = goalNeed(g, kind), point = (kind === "moved" || kind === "still") && !homeRef(g).startsWith("object:");
|
|
96
|
+
// a point is measured from where it is at the window's first frame (outside a windowed trial compile: unknown, so nothing is measured)
|
|
97
|
+
const home = kind === "moved" || kind === "still" ? movedHome(X.out, g, a) : null, rows = [];
|
|
98
|
+
for (let f = Math.max(a, X.f0); f <= Math.min(b, X.last); f++) {
|
|
99
|
+
const v = measure(X.out, g, kind, f, home);
|
|
100
|
+
if (v !== null) rows.push({ f, v, short: shortOf(kind, v, need) });
|
|
101
|
+
}
|
|
102
|
+
return { rows, from: point ? a : undefined };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* The first frame goal j holds (its own window, cut by its own after/before): a frame, null when
|
|
107
|
+
* it never holds there (or sits in an after/before loop), undefined when no frame of it is measured
|
|
108
|
+
* (a windowed trial compile that does not reach it).
|
|
109
|
+
*/
|
|
110
|
+
function eventOf(X, j) {
|
|
111
|
+
if (X.events.has(j)) return X.events.get(j);
|
|
112
|
+
if (X.visiting.has(j)) return null;
|
|
113
|
+
X.visiting.add(j);
|
|
114
|
+
const g = X.goals[j], kind = goalKinds(g)[0], w = kind && windowOf(X, j);
|
|
115
|
+
let e;
|
|
116
|
+
if (w && !w.wait) {
|
|
117
|
+
const { rows } = rowsOf(X, g, kind, w.a, w.b);
|
|
118
|
+
e = rows.length ? (rows.find(r => r.short <= 0)?.f ?? null) : undefined;
|
|
119
|
+
} else e = w?.wait ? null : undefined;
|
|
120
|
+
X.visiting.delete(j);
|
|
121
|
+
X.events.set(j, e);
|
|
122
|
+
return e;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** The frames goal i measures: at cut by after and before ({ a, b }), or { wait } when that leaves none, or { skip } when unknown here. */
|
|
126
|
+
function windowOf(X, i) {
|
|
127
|
+
const g = X.goals[i];
|
|
128
|
+
let [a, b] = Array.isArray(g.at) ? g.at : [g.at, g.at];
|
|
129
|
+
for (const side of ["after", "before"]) {
|
|
130
|
+
if (g[side] === undefined) continue;
|
|
131
|
+
const j = X.names.get(g[side]);
|
|
132
|
+
if (j === undefined || j === i) return { wait: `${side} "${g[side]}" names no other goal` };
|
|
133
|
+
const e = eventOf(X, j);
|
|
134
|
+
if (e === undefined) return { skip: true };
|
|
135
|
+
if (e === null) return { wait: `waits for "${g[side]}" (${side}), which never holds` };
|
|
136
|
+
if (side === "after") a = Math.max(a, e + 1); else b = Math.min(b, e - 1);
|
|
137
|
+
if (a > b) return { wait: `no frames left ${side} "${g[side]}" (it holds first at f${e}; at is f${Array.isArray(g.at) ? `${g.at[0]}-f${g.at[1]}` : g.at})` };
|
|
138
|
+
}
|
|
139
|
+
return { a, b };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function message(name, kind, row, need, any, from) {
|
|
143
|
+
const where = any ? `f${row.f}, the best of f${any[0]}-f${any[1]}` : `f${row.f}`;
|
|
144
|
+
if (kind === "near") return `${name}: ${m3(row.v)} m at ${where} (needs <= ${need})`;
|
|
145
|
+
if (kind === "above") return `${name}: ${m3(row.v)} m above at ${where} (needs >= ${need})`;
|
|
146
|
+
if (kind === "speed") return `${name}: ${m3(row.v)} m/s at ${where} (needs >= ${need})`;
|
|
147
|
+
if (kind === "still") return `${name}: ${m3(row.v)} m from ${from === undefined ? "its scene pose" : `f${from}`} at ${where} (needs <= ${need})`;
|
|
148
|
+
return `${name}: moved ${m3(row.v)} m${from === undefined ? "" : ` from f${from}`} at ${where} (needs >= ${need})`;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** The frames goal i measures in compile result out (after/before applied): { a, b } | { wait } | { skip } (hints-goal.js). */
|
|
152
|
+
export function goalWindow(out, goals, i) {
|
|
153
|
+
const f0 = out.f0 ?? 0, names = new Map(goals.map((g, j) => [goalName(g, j), j]));
|
|
154
|
+
return windowOf({ out, goals, names, f0, last: f0 + (out.frames?.length ?? 0) - 1, events: new Map(), visiting: new Set() }, i);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* goalChecks(out, goals) -> { issues, results }
|
|
159
|
+
* @param {object} out compileMotion result (refs.js reads it)
|
|
160
|
+
* @param {object[]} goals spec.goals
|
|
161
|
+
* @returns {{ issues: object[], results: { name: string, kind: string, ok: boolean, value: number|null, at: number|null, need: number }[] }}
|
|
162
|
+
*/
|
|
163
|
+
export function goalChecks(out, goals) {
|
|
164
|
+
const issues = [], results = [], f0 = out.f0 ?? 0;
|
|
165
|
+
const X = { out, goals, names: new Map(goals.map((g, j) => [goalName(g, j), j])), f0, last: f0 + (out.frames?.length ?? 0) - 1, events: new Map(), visiting: new Set() };
|
|
166
|
+
goals.forEach((g, i) => {
|
|
167
|
+
const kind = goalKinds(g)[0];
|
|
168
|
+
if (!kind) return;
|
|
169
|
+
const name = goalName(g, i), need = goalNeed(g, kind), w = windowOf(X, i), at0 = Array.isArray(g.at) ? g.at[0] : g.at;
|
|
170
|
+
if (w.skip) return;
|
|
171
|
+
if (w.wait) {
|
|
172
|
+
results.push({ name, kind, ok: false, value: null, at: null, need });
|
|
173
|
+
if (at0 >= f0 && at0 <= X.last) issues.push({ kind: "GOAL", msg: `${name}: ${w.wait}`, frame: at0 - f0, value: 0.01, limit: 0 });
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
let { rows, from } = rowsOf(X, g, kind, w.a, w.b);
|
|
177
|
+
// a windowed trial compile (search.js) may not reach at; those frames cannot change there
|
|
178
|
+
if (!rows.length) return;
|
|
179
|
+
// a point's moved is one value: the farthest it gets from where it was at at[0]
|
|
180
|
+
if (kind === "moved" && from !== undefined) rows = [rows.reduce((p, r) => (r.v > p.v ? r : p))];
|
|
181
|
+
const any = goalMode(g, kind) === "any", cut = Array.isArray(g.at) ? w.a !== w.b : false;
|
|
182
|
+
const pick = rows.reduce((p, r) => ((any ? r.short < p.short : r.short > p.short) ? r : p));
|
|
183
|
+
const ok = pick.short <= 0;
|
|
184
|
+
results.push({ name, kind, ok, value: m3(pick.v), at: pick.f, need });
|
|
185
|
+
if (ok) return;
|
|
186
|
+
for (const r of any ? [pick] : rows.filter(r => r.short > 0)) {
|
|
187
|
+
issues.push({ kind: "GOAL", msg: message(name, kind, r, need, any && cut ? [w.a, w.b] : null, from), frame: r.f - f0, value: round4(r.short), limit: 0 });
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
return { issues, results };
|
|
191
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// The ground under a foot: the floor (y 0) or the top of a scene obstacle the sole stands on.
|
|
2
|
+
// Every ground measurement reads sole heights from here: QA's air runs, FLOAT and balance
|
|
3
|
+
// (style.js), SLIDE (checks.js), the timeline's planted runs, and the fixes that keep to QA's
|
|
4
|
+
// planted definition (fix/slide.js, fix/turn.js). A foot on a box top is then on the ground as one
|
|
5
|
+
// on the floor is. Session 68d305dc: a foot on a 0.7 m cube read as "no foot on the ground"
|
|
6
|
+
// (FLOAT n57), and naming the cube in checks.supports did not clear it (a support rests the hips).
|
|
7
|
+
// The obstacles are out.objectsAt(i): carried and follow objects are not ground, a hinge is at its
|
|
8
|
+
// turned pose. GROUND (a sole under the floor) and fix.ground stay on the floor.
|
|
9
|
+
import { T, apply, sub, len } from "../compiler/math.js";
|
|
10
|
+
import { solePoints } from "./geometry.js";
|
|
11
|
+
|
|
12
|
+
/** A top up to this far (m) above a sole point is still the ground under it: a sole sunk into a top stands on it. */
|
|
13
|
+
const SINK = 0.05;
|
|
14
|
+
|
|
15
|
+
const prepared = new WeakMap();
|
|
16
|
+
|
|
17
|
+
/** The obstacle boxes of one frame's map, with what the vertical-line test needs. */
|
|
18
|
+
function boxesOf(objs) {
|
|
19
|
+
let list = prepared.get(objs);
|
|
20
|
+
if (!list) {
|
|
21
|
+
list = Object.values(objs).filter(box => box.half && !box.ignore).map(box => {
|
|
22
|
+
const RT = T(box.R), c = [box.pos[0] + box.R[0][1] * box.half[1], box.pos[1] + box.R[1][1] * box.half[1], box.pos[2] + box.R[2][1] * box.half[1]];
|
|
23
|
+
return { RT, c, half: box.half, up: [box.R[1][0], box.R[1][1], box.R[1][2]], r: len(box.half) };
|
|
24
|
+
});
|
|
25
|
+
prepared.set(objs, list);
|
|
26
|
+
}
|
|
27
|
+
return list;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Height of the box's top on the vertical line through p (any rotation), or null when the line misses it. */
|
|
31
|
+
function topOver(b, p) {
|
|
32
|
+
if (Math.hypot(p[0] - b.c[0], p[2] - b.c[2]) > b.r) return null;
|
|
33
|
+
const q = apply(b.RT, sub(p, b.c));
|
|
34
|
+
let lo = -Infinity, hi = Infinity;
|
|
35
|
+
for (let k = 0; k < 3; k++) {
|
|
36
|
+
const d = b.up[k], h = b.half[k];
|
|
37
|
+
if (Math.abs(d) < 1e-9) { if (Math.abs(q[k]) > h) return null; continue; }
|
|
38
|
+
const t1 = (-h - q[k]) / d, t2 = (h - q[k]) / d;
|
|
39
|
+
lo = Math.max(lo, Math.min(t1, t2));
|
|
40
|
+
hi = Math.min(hi, Math.max(t1, t2));
|
|
41
|
+
}
|
|
42
|
+
return lo <= hi ? p[1] + hi : null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** World height of the ground under point p at frame index i of out (out.frames[i]). */
|
|
46
|
+
export function groundY(out, i, p) {
|
|
47
|
+
let g = 0;
|
|
48
|
+
for (const b of boxesOf(out.objectsAt(i))) {
|
|
49
|
+
const top = topOver(b, p);
|
|
50
|
+
if (top !== null && top <= p[1] + SINK && top > g) g = top;
|
|
51
|
+
}
|
|
52
|
+
return g;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** solePoints of frame index i with y the height above the ground under each point (x, z are world). */
|
|
56
|
+
export function groundSole(out, i, side) {
|
|
57
|
+
const sp = solePoints(out.ctx.sk, out.frames[i].s, side);
|
|
58
|
+
for (const key of ["heel", "ball", "tip"]) sp[key] = [sp[key][0], sp[key][1] - groundY(out, i, sp[key]), sp[key][2]];
|
|
59
|
+
return sp;
|
|
60
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
// Hints for contact-type issues: a body part inside a box, the floor, the head, the torso or the
|
|
2
|
+
// other limb. Each answers a world move (direction x amount) for whatever drives that part.
|
|
3
|
+
import { add, sub, scale, norm, dot, len, clamp, apply, ry, T } from "../compiler/math.js";
|
|
4
|
+
import { bonePoint } from "../compiler/skeleton.js";
|
|
5
|
+
import { CAPSULES, segPoints, sdBox, boxNormal, headDepth, segDist, solePoints, ankleY } from "./geometry.js";
|
|
6
|
+
import { vec, m3 } from "./fmt.js";
|
|
7
|
+
import { kneeFoldHint, kneeWhy } from "./hints-knee.js";
|
|
8
|
+
import { objectFrame } from "../compiler/anchors.js";
|
|
9
|
+
import { bodyFor, bodyNormal, limbDepth } from "../body.js";
|
|
10
|
+
import { footProbes, footDepths } from "./foot-checks.js";
|
|
11
|
+
import { personCapsules } from "./people-checks.js";
|
|
12
|
+
|
|
13
|
+
const MARGIN = 0.003;
|
|
14
|
+
/** The most a foot hint lifts a foot over a body it went into through a side (a stair riser); higher, it backs out. */
|
|
15
|
+
const LIFT_MAX = 0.25;
|
|
16
|
+
const sideOf = name => (/(^|_)l$|^Left/.test(name) ? "l" : /(^|_)r$|^Right/.test(name) ? "r" : null);
|
|
17
|
+
|
|
18
|
+
/** fix (qa/patch.js) moving anchor `name` of hand k by a world delta at f: its own point, in the frame that point is written in. */
|
|
19
|
+
function anchorFix(X, name, f, delta, k) {
|
|
20
|
+
const a = X.spec.anchors?.[name], path = field => `/anchors/${name}/${field}`;
|
|
21
|
+
if (a?.kind === "world") return { path: path("p"), delta };
|
|
22
|
+
if (a?.kind === "body") return { path: path("p"), delta: apply(ry(-(X.fr(f).c.yaw ?? 0)), delta) };
|
|
23
|
+
if (["bone", "segment", "prop", "person"].includes(a?.kind)) return { path: path("offset"), delta };
|
|
24
|
+
if (a?.kind !== "object") return undefined;
|
|
25
|
+
// local is in the object's axes where that hand's target read it (anchors.js objectFrame)
|
|
26
|
+
const fr = X.fr(f), o = objectFrame(X.out.ctx, a, name, { ...fr, diag: { ...fr.diag, frame: f } }, k);
|
|
27
|
+
return { path: path("local"), delta: apply(T(o.R), delta) };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** cause + hint (+ fix, qa/patch.js) for moving a body part (capsule name, prop_<name>, hand_<k>) by `delta` at frame f. */
|
|
31
|
+
export function moveHint(X, part, f, delta, why = "") {
|
|
32
|
+
const prop = /^prop_(.+)$/.exec(part);
|
|
33
|
+
const k = prop ? X.spec.props?.[prop[1]]?.hand : sideOf(part);
|
|
34
|
+
let label, cause, fix, shown = delta;
|
|
35
|
+
if (/^(hand|farm|uarm)_/.test(part) || prop) {
|
|
36
|
+
const d = X.hand(k, f);
|
|
37
|
+
label = d.label;
|
|
38
|
+
fix = d.anchor ? anchorFix(X, d.anchor, f, delta, k) : d.ch === `hand_${k}` ? { channel: d.ch, f, delta } : undefined;
|
|
39
|
+
// an object anchor moves its point on the object (local, in the object's axes), never the object
|
|
40
|
+
const on = d.anchor && X.spec.anchors[d.anchor]?.kind === "object" ? X.spec.anchors[d.anchor].object : null;
|
|
41
|
+
if (on && fix) { label = `anchors.${d.anchor}.local (a point on ${on}, in ${on}'s axes; ${on} stays)`; shown = fix.delta; }
|
|
42
|
+
cause = d.anchor ? `anchor ${d.anchor}${d.fit !== null ? ` (fits[${d.fit}])` : ""} via ${X.cause(d.ch, f)}` : X.cause(d.ch, f);
|
|
43
|
+
} else if (/^(shin|foot|sole)_/.test(part)) { label = `foot_${k}`; cause = X.cause(label, f); fix = { channel: label, f, delta }; }
|
|
44
|
+
// a body held since its key (it breathes and sways there, compiler/idle.js): move what placed it (patch.js hold)
|
|
45
|
+
else { label = "root"; cause = X.cause("root", f); fix = { channel: "root", f, delta, ...(/^root held since /.test(cause) ? { hold: true } : {}) }; }
|
|
46
|
+
// a key the walk generator wrote: the agent changes the walk, not the key
|
|
47
|
+
const walk = / (walk|walkTo) /.test(` ${cause}`) && /step|start|end/.test(cause);
|
|
48
|
+
if (walk && !/^anchor/.test(label)) return { cause, hint: `walk path: ${label} needs ${vec(delta)} m at f${f}${why}; change that walk's heading/turnDistance/step (or give one a { range: [lo, hi] })` };
|
|
49
|
+
return { cause, hint: `${label}: move ${vec(shown)} m at f${f}${why}`, fix };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const capsule = name => CAPSULES.find(c => c[0] === name);
|
|
53
|
+
|
|
54
|
+
/** Deepest point of a capsule in a box at frame f and the box's outward normal there. */
|
|
55
|
+
function deepest(X, f, part, box) {
|
|
56
|
+
const [, a, b] = capsule(part), { s } = X.fr(f);
|
|
57
|
+
const pts = segPoints(s.P[X.I[a]], s.P[X.I[b]]), ds = pts.map(p => sdBox(p, box)), m = Math.min(...ds);
|
|
58
|
+
return { p: pts[ds.indexOf(m)], n: boxNormal(pts[ds.indexOf(m)], box), d: m };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function boxHint(X, g, part, id, need) {
|
|
62
|
+
const f = g.worstFrame, box = X.out.objectsAt(X.rel(f))[id];
|
|
63
|
+
if (!box || !capsule(part)) return null;
|
|
64
|
+
const { n } = deepest(X, f, part, box);
|
|
65
|
+
return moveHint(X, part, f, scale(n, need), ` (out of ${id})`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* A foot in a box (foot-checks.js): through a side face (a toe swung into a stair's riser) the foot rises over the
|
|
70
|
+
* box's top by what its deepest probe lacks, with the way straight out of that side as the other choice; through
|
|
71
|
+
* the top face (a sole sunk in a step) it comes out along that face.
|
|
72
|
+
*/
|
|
73
|
+
function footHint(X, g, part, id) {
|
|
74
|
+
const f = g.worstFrame, box = X.out.objectsAt(X.rel(f))[id];
|
|
75
|
+
if (!box) return null;
|
|
76
|
+
const probes = footProbes(X.sk, X.fr(f).s, part.endsWith("_l") ? "Left" : "Right"), d = footDepths(probes, box), need = g.worst - g.limit + MARGIN;
|
|
77
|
+
if (!(d.side > 0)) return d.topAt ? moveHint(X, part, f, scale(boxNormal(d.topAt, box), need), ` (out of ${id}'s top)`) : null;
|
|
78
|
+
const under = p => 2 * box.half[1] - apply(T(box.R), sub(p, box.pos))[1];
|
|
79
|
+
const rise = Math.max(...probes.filter(p => sdBox(p, box) < 0).map(under)) + MARGIN, out = scale(boxNormal(d.sideAt, box), need);
|
|
80
|
+
// a step's riser is cleared by lifting the swing; a panel the foot kicks or a wall is left the way the foot came
|
|
81
|
+
if (rise > LIFT_MAX) return moveHint(X, part, f, out, ` (out of ${id}'s side)`);
|
|
82
|
+
const r = moveHint(X, part, f, apply(box.R, [0, rise, 0]), ` (over ${id}'s top; its ${g.worst.toFixed(3)} m is through a side)`);
|
|
83
|
+
r.hint += /^walk path/.test(r.hint) ? "; a higher lift raises the swing" : `; or move ${vec(out)} m out of that side`;
|
|
84
|
+
return r;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function towardFrom(X, g, part, from, need, what) {
|
|
88
|
+
return moveHint(X, part, g.worstFrame, scale(norm(from), need), ` (away from the ${what})`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Probe points of a hand or prop at frame f (as the FACE/BODY checks probe them). */
|
|
92
|
+
function probes(X, f, nm) {
|
|
93
|
+
const { s, diag } = X.fr(f), I = X.I;
|
|
94
|
+
if (nm.startsWith("hand_")) { const S = nm.endsWith("l") ? "Left" : "Right"; return segPoints(s.P[I[`${S}Hand`]], s.P[I[`${S}HandMiddle3`]], 5); }
|
|
95
|
+
return Object.entries(diag.props?.[nm.slice(5)] ?? {}).filter(([k, p]) => Array.isArray(p) && k !== "origin").map(([, p]) => p);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export const contactHints = {
|
|
99
|
+
COLLIDE(X, g) {
|
|
100
|
+
const [, part, id] = /^(\S+) x (.+)$/.exec(g.subject) ?? [];
|
|
101
|
+
if (/^foot_[lr]$/.test(part ?? "")) return footHint(X, g, part, id);
|
|
102
|
+
const r = boxHint(X, g, part, id, g.worst - g.limit + MARGIN);
|
|
103
|
+
if (r && /^(hand|farm)_/.test(part) && !X.spec.fix?.penetration && g.worst - g.limit <= 0.05) {
|
|
104
|
+
r.hint += "; or fix.penetration: 0.02 pushes shallow hand hits out";
|
|
105
|
+
if (X.spec.fix !== false) r.fix = { path: "/fix/penetration", value: 0.02 };
|
|
106
|
+
}
|
|
107
|
+
return r;
|
|
108
|
+
},
|
|
109
|
+
/** PERSON "<part> x person:<id>": the part moves out of the other person's deepest capsule, along the line between their nearest points. */
|
|
110
|
+
PERSON(X, g) {
|
|
111
|
+
const [, part, id] = /^(\S+) x person:(.+)$/.exec(g.subject) ?? [], mine = capsule(part), p0 = X.out?.ctx?.scene?.people?.[id];
|
|
112
|
+
if (!mine || !p0) return null;
|
|
113
|
+
const f = g.worstFrame, { s } = X.fr(f), pts = segPoints(s.P[X.I[mine[1]]], s.P[X.I[mine[2]]]);
|
|
114
|
+
const near = ([other, C, D, q]) => {
|
|
115
|
+
const cd = sub(D, C), dd = Math.max(dot(cd, cd), 1e-12);
|
|
116
|
+
return pts.map(p => { const at = add(C, scale(cd, clamp(dot(sub(p, C), cd) / dd, 0, 1))); return { other, p, q: at, depth: mine[3] + q - len(sub(p, at)) }; })
|
|
117
|
+
.reduce((m, x) => (x.depth > m.depth ? x : m));
|
|
118
|
+
};
|
|
119
|
+
const hit = personCapsules(p0, f).map(near).reduce((m, x) => (x.depth > m.depth ? x : m));
|
|
120
|
+
const away = len(sub(hit.p, hit.q)) > 1e-6 ? norm(sub(hit.p, hit.q)) : norm([hit.p[0] - hit.q[0] || 1, 0, hit.p[2] - hit.q[2]]);
|
|
121
|
+
const r = moveHint(X, part, f, scale(away, g.worst - g.limit + MARGIN), ` (out of ${p0.name}'s ${hit.other})`);
|
|
122
|
+
return { ...r, hint: `${r.hint}; or, for a touch you mean, checks.allow [{ part: "${part}", object: "person:${id}", depth: ${m3(g.worst + MARGIN)} }]` };
|
|
123
|
+
},
|
|
124
|
+
CLEARANCE(X, g) {
|
|
125
|
+
const k = (X.spec.checks?.clearance ?? []).find(c => g.subject.startsWith(`${c.name}:`));
|
|
126
|
+
return k && boxHint(X, g, k.part, k.object, k.min - g.worst + MARGIN);
|
|
127
|
+
},
|
|
128
|
+
GROUND(X, g) {
|
|
129
|
+
const k = g.subject.startsWith("Left") ? "l" : "r", f = g.worstFrame, c = X.fr(f).c, fold = X.fr(f).diag[`leg_fold_${k}`];
|
|
130
|
+
// the knee on its stop left the ankle under its target (compiler/limbs.js): raising the foot folds it further, the root comes up
|
|
131
|
+
if (fold) return kneeFoldHint(X, k, f, kneeWhy("the sole is in the floor because the knee is on its stop: ", fold), c[`foot_${k}`]);
|
|
132
|
+
const pitch = c[`fpitch_${k}`] ? `, or less fpitch_${k} (now ${m3(c[`fpitch_${k}`])} deg; + lifts the heel)` : "";
|
|
133
|
+
return { cause: X.cause(`foot_${k}`, f), hint: `foot_${k}: raise ${m3(g.worst + 0.001)} m at f${f} (foot_* is the ankle; it stands ${ankleY(X.sk)} m above the floor)${pitch}`,
|
|
134
|
+
fix: { channel: `foot_${k}`, f, delta: [0, g.worst + 0.001, 0] } };
|
|
135
|
+
},
|
|
136
|
+
FACE(X, g) {
|
|
137
|
+
const nm = g.subject.replace(/ into head$/, ""), f = g.worstFrame, { s } = X.fr(f);
|
|
138
|
+
const c = bonePoint(s, X.I.Head, X.sk.landmarks.headCentre);
|
|
139
|
+
const p = probes(X, f, nm).reduce((best, q) => (headDepth(X.sk, s, q) > headDepth(X.sk, s, best) ? q : best));
|
|
140
|
+
return towardFrom(X, g, nm, sub(p, c), g.worst - g.limit + MARGIN, "head");
|
|
141
|
+
},
|
|
142
|
+
BODY(X, g) {
|
|
143
|
+
const [, nm, part] = /^(\S+) into (\S+)$/.exec(g.subject) ?? [];
|
|
144
|
+
const cap = capsule(part), f = g.worstFrame, { s } = X.fr(f);
|
|
145
|
+
// an arm on the fitted skin (body-checks.js): the hand target moves out along the skin where it goes deepest;
|
|
146
|
+
// an upper arm or forearm says so, since turning its elbow (elbow_<k>) away from the body may do instead
|
|
147
|
+
const limb = /^(hand|farm|uarm)_([lr])$/.exec(nm ?? ""), body = limb && bodyFor(X.sk);
|
|
148
|
+
if (body) {
|
|
149
|
+
const bone = X.I[`${limb[2] === "l" ? "Left" : "Right"}${{ hand: "Hand", farm: "ForeArm", uarm: "Arm" }[limb[1]]}`], hit = limbDepth(body, X.sk, s, bone);
|
|
150
|
+
const out = hit.p && bodyNormal(body, s, hit.p, body.skip.get(bone));
|
|
151
|
+
if (out) {
|
|
152
|
+
const r = moveHint(X, nm, f, scale(out, g.worst - g.limit + MARGIN), ` (out of the ${part})`);
|
|
153
|
+
if (limb[1] !== "hand") r.hint += `; or turn elbow_${limb[2]} away from the ${part}`;
|
|
154
|
+
return r;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (!cap) return null;
|
|
158
|
+
const A = s.P[X.I[cap[1]]], B = s.P[X.I[cap[2]]], ab = sub(B, A);
|
|
159
|
+
const axis = p => add(A, scale(ab, clamp(dot(sub(p, A), ab) / dot(ab, ab), 0, 1)));
|
|
160
|
+
const p = probes(X, f, nm).reduce((best, q) => (len(sub(q, axis(q))) < len(sub(best, axis(best))) ? q : best));
|
|
161
|
+
return towardFrom(X, g, nm, sub(p, axis(p)), g.worst - g.limit + MARGIN, "torso");
|
|
162
|
+
},
|
|
163
|
+
SELF(X, g) {
|
|
164
|
+
const f = g.worstFrame, { s } = X.fr(f), P = s.P, I = X.I;
|
|
165
|
+
if (g.subject === "hands overlap") {
|
|
166
|
+
const d = norm(sub(P[I.LeftHand], P[I.RightHand])), need = (g.worst - g.limit + MARGIN) / 2;
|
|
167
|
+
const r = moveHint(X, "hand_l", f, scale(d, need));
|
|
168
|
+
return { cause: r.cause, hint: `${r.hint}; and ${moveHint(X, "hand_r", f, scale(d, -need)).hint}`, fix: moveHint(X, "hand_l", f, scale(d, 2 * need)).fix };
|
|
169
|
+
}
|
|
170
|
+
const shins = g.subject === "shins touch", min = shins ? 0.1 : 0.135;
|
|
171
|
+
const [a, b] = shins ? ["Leg", "Foot"] : ["UpLeg", "Leg"];
|
|
172
|
+
const gap = segDist(P[I[`Left${a}`]], P[I[`Left${b}`]], P[I[`Right${a}`]], P[I[`Right${b}`]]);
|
|
173
|
+
const d = sub(P[I[`Left${b}`]], P[I[`Right${b}`]]); d[1] = 0;
|
|
174
|
+
const need = (min - gap + MARGIN) / 2, dir = norm(d);
|
|
175
|
+
return { cause: X.cause("foot_l", f), hint: `foot_l: move ${vec(scale(dir, need))} m and foot_r ${vec(scale(dir, -need))} m at f${f} (${shins ? "shins" : "thighs"} ${m3(gap)} m apart, need ${min}); or spread knee_l/knee_r`,
|
|
176
|
+
fix: { channel: "foot_l", f, delta: scale(dir, 2 * need) } };
|
|
177
|
+
},
|
|
178
|
+
OBJECT(X, g) {
|
|
179
|
+
const [, id, oid] = /^(\S+) into (.+)$/.exec(g.subject) ?? [];
|
|
180
|
+
const ch = X.spec.objects?.[id]?.channel;
|
|
181
|
+
if (!ch) return null;
|
|
182
|
+
const at = f => X.out.baked[ch]?.[f] ?? 0, clean = g.first > 0 ? at(g.first - 1) : at(0);
|
|
183
|
+
return { cause: X.cause(ch, g.worstFrame), hint: `${ch}: keep it within ${m3(clean)} deg (it reaches ${m3(at(g.worstFrame))} at f${g.worstFrame}, ${m3(g.worst)} m into ${oid}), or move ${oid}`,
|
|
184
|
+
fix: { channel: ch, f: g.worstFrame, value: clean } };
|
|
185
|
+
},
|
|
186
|
+
BALANCE(X, g) {
|
|
187
|
+
const f = g.worstFrame, { s } = X.fr(f), P = s.P, I = X.I;
|
|
188
|
+
if (g.subject === "no foot on the ground") return { cause: X.cause("root", f), hint: `at f${f} lower foot_l or foot_r onto the ground, the floor or the top of an object under it (the ankle stands ${ankleY(X.sk)} m above it), or declare checks.supports for the seat the hips rest on` };
|
|
189
|
+
const com = scale(["Hips", "Spine1", "Spine2", "Head"].reduce((acc, j) => add(acc, P[I[j]]), [0, 0, 0]), 0.25);
|
|
190
|
+
const feet = ["Left", "Right"].flatMap(S => Object.values(solePoints(X.sk, s, S)));
|
|
191
|
+
const xs = feet.map(p => p[0]), zs = feet.map(p => p[2]), mg = 0.08 - 0.01;
|
|
192
|
+
const to = [clamp(com[0], Math.min(...xs) - mg, Math.max(...xs) + mg), com[1], clamp(com[2], Math.min(...zs) - mg, Math.max(...zs) + mg)];
|
|
193
|
+
return { cause: X.cause("root", f), hint: `root: move ${vec(sub(to, com))} m at f${f} (centre of mass over the feet), or step a foot under it`, fix: { channel: "root", f, delta: sub(to, com) } };
|
|
194
|
+
},
|
|
195
|
+
};
|