dsh-drawai 0.3.4 → 0.4.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/LICENSE +4154 -0
- package/README.md +49 -11
- package/lib/audio-media.js +150 -0
- package/lib/audio-service.js +66 -0
- package/lib/canvas-format.js +134 -0
- package/lib/client.js +5090 -184
- package/lib/comfyui-default-workflow.mjs +1825 -0
- package/lib/comfyui-workflow.mjs +426 -0
- package/lib/document-commands.js +12 -0
- package/lib/document-core.js +29 -0
- package/lib/drawing-runner.js +6 -4
- package/lib/drawing-tasks.js +26 -0
- package/lib/ffmpeg-manager.js +1056 -0
- package/lib/imagegen.mjs +596 -0
- package/lib/index.js +1762 -129
- package/lib/media.js +26 -13
- package/lib/mgstyle-core.js +75 -0
- package/lib/motion/apply.js +181 -0
- package/lib/motion/asset-pack-lock.js +10 -0
- package/lib/motion/asset-pack.js +335 -0
- package/lib/motion/assets.js +291 -0
- package/lib/motion/audio.js +103 -0
- package/lib/motion/audiolevel.js +63 -0
- package/lib/motion/audiomix.js +144 -0
- package/lib/motion/beats.js +338 -0
- package/lib/motion/builtin-assets.js +62 -0
- package/lib/motion/canvas-spec.js +442 -0
- package/lib/motion/capture.js +95 -0
- package/lib/motion/cards.js +80 -0
- package/lib/motion/collect.js +449 -0
- package/lib/motion/delivery.js +140 -0
- package/lib/motion/edge-speech.py +90 -0
- package/lib/motion/entry.jsx +73 -0
- package/lib/motion/export.js +200 -0
- package/lib/motion/ffmpeg.js +43 -0
- package/lib/motion/fonts.js +71 -0
- package/lib/motion/jobs.js +117 -0
- package/lib/motion/json.js +4 -0
- package/lib/motion/media-catalog.js +59 -0
- package/lib/motion/mgstyles/BRIEF-TEMPLATE.md +63 -0
- package/lib/motion/mgstyles/briefs/01-flat-vector.md +104 -0
- package/lib/motion/mgstyles/briefs/02-line-art.md +97 -0
- package/lib/motion/mgstyles/briefs/03-isometric.md +108 -0
- package/lib/motion/mgstyles/briefs/05-cel-boil.md +145 -0
- package/lib/motion/mgstyles/briefs/06-collage.md +149 -0
- package/lib/motion/mgstyles/briefs/07-liquid.md +106 -0
- package/lib/motion/mgstyles/briefs/08-morph.md +111 -0
- package/lib/motion/mgstyles/briefs/09-bauhaus.md +150 -0
- package/lib/motion/mgstyles/briefs/10-synthwave.md +151 -0
- package/lib/motion/mgstyles/briefs/12-aurora-glass.md +147 -0
- package/lib/motion/mgstyles/briefs/18-hanazi.md +140 -0
- package/lib/motion/mgstyles/briefs/19-paperclip.md +156 -0
- package/lib/motion/mgstyles/briefs/20-pixel.md +118 -0
- package/lib/motion/mgstyles/briefs/22-hud.md +118 -0
- package/lib/motion/mgstyles/catalog.d/01-flat-vector.json +34 -0
- package/lib/motion/mgstyles/catalog.d/02-line-art.json +34 -0
- package/lib/motion/mgstyles/catalog.d/03-isometric.json +35 -0
- package/lib/motion/mgstyles/catalog.d/05-cel-boil.json +28 -0
- package/lib/motion/mgstyles/catalog.d/06-collage.json +28 -0
- package/lib/motion/mgstyles/catalog.d/07-liquid.json +28 -0
- package/lib/motion/mgstyles/catalog.d/08-morph.json +28 -0
- package/lib/motion/mgstyles/catalog.d/09-bauhaus.json +28 -0
- package/lib/motion/mgstyles/catalog.d/10-synthwave.json +28 -0
- package/lib/motion/mgstyles/catalog.d/12-aurora-glass.json +28 -0
- package/lib/motion/mgstyles/catalog.d/18-hanazi.json +28 -0
- package/lib/motion/mgstyles/catalog.d/19-paperclip.json +28 -0
- package/lib/motion/mgstyles/catalog.d/20-pixel.json +28 -0
- package/lib/motion/mgstyles/catalog.d/22-hud.json +28 -0
- package/lib/motion/mgstyles/catalog.json +1356 -0
- package/lib/motion/mgstyles/upstream/README.md +147 -0
- package/lib/motion/mgstyles/upstream/README.zh-CN.md +147 -0
- package/lib/motion/mgstyles/upstream/demos/01-flat-vector/cues.json +1 -0
- package/lib/motion/mgstyles/upstream/demos/01-flat-vector/index.html +712 -0
- package/lib/motion/mgstyles/upstream/demos/02-line-art/cues.json +331 -0
- package/lib/motion/mgstyles/upstream/demos/02-line-art/film.js +676 -0
- package/lib/motion/mgstyles/upstream/demos/02-line-art/index.html +18 -0
- package/lib/motion/mgstyles/upstream/demos/02-line-art/tools/dump_timing.mjs +26 -0
- package/lib/motion/mgstyles/upstream/demos/03-isometric/cues.json +109 -0
- package/lib/motion/mgstyles/upstream/demos/03-isometric/index.html +25 -0
- package/lib/motion/mgstyles/upstream/demos/03-isometric/js/anim.js +82 -0
- package/lib/motion/mgstyles/upstream/demos/03-isometric/js/main.js +219 -0
- package/lib/motion/mgstyles/upstream/demos/03-isometric/js/night5.js +180 -0
- package/lib/motion/mgstyles/upstream/demos/03-isometric/js/render.js +202 -0
- package/lib/motion/mgstyles/upstream/demos/03-isometric/js/title.js +167 -0
- package/lib/motion/mgstyles/upstream/demos/03-isometric/js/title5.js +294 -0
- package/lib/motion/mgstyles/upstream/demos/03-isometric/js/ui.js +141 -0
- package/lib/motion/mgstyles/upstream/demos/03-isometric/js/world.js +1399 -0
- package/lib/motion/mgstyles/upstream/demos/05-cel-boil/cues.json +157 -0
- package/lib/motion/mgstyles/upstream/demos/05-cel-boil/index.html +34 -0
- package/lib/motion/mgstyles/upstream/demos/05-cel-boil/js/engine.js +326 -0
- package/lib/motion/mgstyles/upstream/demos/05-cel-boil/js/scene.js +2036 -0
- package/lib/motion/mgstyles/upstream/demos/06-collage/assets/gen/layout.json +157 -0
- package/lib/motion/mgstyles/upstream/demos/06-collage/cues.json +80 -0
- package/lib/motion/mgstyles/upstream/demos/06-collage/index.html +889 -0
- package/lib/motion/mgstyles/upstream/demos/07-liquid/cues.json +100 -0
- package/lib/motion/mgstyles/upstream/demos/07-liquid/index.html +108 -0
- package/lib/motion/mgstyles/upstream/demos/07-liquid/js/engine.js +153 -0
- package/lib/motion/mgstyles/upstream/demos/07-liquid/js/links.js +64 -0
- package/lib/motion/mgstyles/upstream/demos/07-liquid/js/lookdev.js +46 -0
- package/lib/motion/mgstyles/upstream/demos/07-liquid/js/scene.js +963 -0
- package/lib/motion/mgstyles/upstream/demos/07-liquid/js/shaders.js +647 -0
- package/lib/motion/mgstyles/upstream/demos/07-liquid/js/textsdf.js +114 -0
- package/lib/motion/mgstyles/upstream/demos/07-liquid/js/type.js +50 -0
- package/lib/motion/mgstyles/upstream/demos/08-morph/cues.json +131 -0
- package/lib/motion/mgstyles/upstream/demos/08-morph/glyphs.json +1 -0
- package/lib/motion/mgstyles/upstream/demos/08-morph/index.html +901 -0
- package/lib/motion/mgstyles/upstream/demos/09-bauhaus/cues.json +270 -0
- package/lib/motion/mgstyles/upstream/demos/09-bauhaus/film.js +697 -0
- package/lib/motion/mgstyles/upstream/demos/09-bauhaus/index.html +12 -0
- package/lib/motion/mgstyles/upstream/demos/10-synthwave/cues.json +44 -0
- package/lib/motion/mgstyles/upstream/demos/10-synthwave/index.html +1028 -0
- package/lib/motion/mgstyles/upstream/demos/12-aurora-glass/cues.json +67 -0
- package/lib/motion/mgstyles/upstream/demos/12-aurora-glass/index.html +16 -0
- package/lib/motion/mgstyles/upstream/demos/12-aurora-glass/main.js +967 -0
- package/lib/motion/mgstyles/upstream/demos/12-aurora-glass/shaders.js +468 -0
- package/lib/motion/mgstyles/upstream/demos/18-hanazi/cues.json +117 -0
- package/lib/motion/mgstyles/upstream/demos/18-hanazi/index.html +541 -0
- package/lib/motion/mgstyles/upstream/demos/19-paperclip/assets/countries-110m.json +1 -0
- package/lib/motion/mgstyles/upstream/demos/19-paperclip/assets/stickers/meta.json +34 -0
- package/lib/motion/mgstyles/upstream/demos/19-paperclip/cues.json +285 -0
- package/lib/motion/mgstyles/upstream/demos/19-paperclip/index.html +599 -0
- package/lib/motion/mgstyles/upstream/demos/20-pixel/cues.json +42 -0
- package/lib/motion/mgstyles/upstream/demos/20-pixel/film.js +914 -0
- package/lib/motion/mgstyles/upstream/demos/20-pixel/index.html +18 -0
- package/lib/motion/mgstyles/upstream/demos/22-hud/cues.json +108 -0
- package/lib/motion/mgstyles/upstream/demos/22-hud/index.html +125 -0
- package/lib/motion/mgstyles/upstream/demos/22-hud/js/holo.js +275 -0
- package/lib/motion/mgstyles/upstream/demos/22-hud/js/hud.js +855 -0
- package/lib/motion/mgstyles/upstream/demos/22-hud/js/post.js +150 -0
- package/lib/motion/mgstyles/upstream/demos/22-hud/js/util.js +65 -0
- package/lib/motion/mgstyles/upstream/demos/22-hud/js/v5.js +142 -0
- package/lib/motion/mgstyles/upstream/harness/render.mjs +319 -0
- package/lib/motion/mgstyles/upstream/manifest.json +269 -0
- package/lib/motion/mgstyles/upstream/prompts/01-flat-vector.md +98 -0
- package/lib/motion/mgstyles/upstream/prompts/02-line-art.md +95 -0
- package/lib/motion/mgstyles/upstream/prompts/03-isometric.md +97 -0
- package/lib/motion/mgstyles/upstream/prompts/05-cel-boil.md +95 -0
- package/lib/motion/mgstyles/upstream/prompts/06-collage.md +95 -0
- package/lib/motion/mgstyles/upstream/prompts/07-liquid.md +98 -0
- package/lib/motion/mgstyles/upstream/prompts/08-morph.md +95 -0
- package/lib/motion/mgstyles/upstream/prompts/09-bauhaus.md +96 -0
- package/lib/motion/mgstyles/upstream/prompts/10-synthwave.md +95 -0
- package/lib/motion/mgstyles/upstream/prompts/12-aurora-glass.md +96 -0
- package/lib/motion/mgstyles/upstream/prompts/18-hanazi.md +95 -0
- package/lib/motion/mgstyles/upstream/prompts/19-paperclip.md +96 -0
- package/lib/motion/mgstyles/upstream/prompts/20-pixel.md +96 -0
- package/lib/motion/mgstyles/upstream/prompts/22-hud.md +95 -0
- package/lib/motion/mgstyles/upstream/rubric.md +51 -0
- package/lib/motion/mgstyles/upstream/template.md +36 -0
- package/lib/motion/pixelqa.js +198 -0
- package/lib/motion/preview.js +97 -0
- package/lib/motion/project.js +395 -0
- package/lib/motion/qa.js +290 -0
- package/lib/motion/reference-media.jsx +10 -0
- package/lib/motion/render-settings.js +112 -0
- package/lib/motion/render.js +161 -0
- package/lib/motion/runtime/package-lock.json +3058 -0
- package/lib/motion/runtime/package.json +16 -0
- package/lib/motion/scene-kit.js +183 -0
- package/lib/motion/sfx.js +232 -0
- package/lib/motion/shotcraft/ClipCard.tsx +204 -0
- package/lib/motion/shotcraft/catalog.json +2461 -0
- package/lib/motion/shotcraft/upstream/SKILL.md +286 -0
- package/lib/motion/shotcraft/upstream/assets/lib/Caption.tsx +54 -0
- package/lib/motion/shotcraft/upstream/assets/lib/ClipCard.tsx +200 -0
- package/lib/motion/shotcraft/upstream/assets/lib/DigitRoll.tsx +45 -0
- package/lib/motion/shotcraft/upstream/assets/lib/FlashCut.tsx +20 -0
- package/lib/motion/shotcraft/upstream/assets/lib/FlatPanel.tsx +65 -0
- package/lib/motion/shotcraft/upstream/assets/lib/PageCam.tsx +151 -0
- package/lib/motion/shotcraft/upstream/assets/lib/VerticalTicker.tsx +129 -0
- package/lib/motion/shotcraft/upstream/assets/lib/helpers/camera.tsx +75 -0
- package/lib/motion/shotcraft/upstream/assets/lib/helpers/motion.ts +40 -0
- package/lib/motion/shotcraft/upstream/assets/lib/helpers/rand.ts +11 -0
- package/lib/motion/shotcraft/upstream/assets/lib/helpers/shake.ts +10 -0
- package/lib/motion/shotcraft/upstream/assets/scripts/__pycache__/sfx-source.cpython-313.pyc +0 -0
- package/lib/motion/shotcraft/upstream/assets/scripts/build-readme-cover.mjs +49 -0
- package/lib/motion/shotcraft/upstream/assets/scripts/capture-template.mjs +210 -0
- package/lib/motion/shotcraft/upstream/assets/scripts/sfx-source.py +423 -0
- package/lib/motion/shotcraft/upstream/assets/scripts/smoke-render-demos.py +170 -0
- package/lib/motion/shotcraft/upstream/assets/scripts/test_sfx_source.py +157 -0
- package/lib/motion/shotcraft/upstream/demos/README.md +120 -0
- package/lib/motion/shotcraft/upstream/demos/_fixtures/Brand.tsx +112 -0
- package/lib/motion/shotcraft/upstream/demos/_fixtures/Fixtures.tsx +647 -0
- package/lib/motion/shotcraft/upstream/demos/_fixtures/Look.tsx +316 -0
- package/lib/motion/shotcraft/upstream/demos/_fixtures/Motion.tsx +82 -0
- package/lib/motion/shotcraft/upstream/demos/_fixtures/PageCam2D.tsx +115 -0
- package/lib/motion/shotcraft/upstream/demos/_fixtures/Polish.tsx +243 -0
- package/lib/motion/shotcraft/upstream/demos/_textures/live-layout.json +389 -0
- package/lib/motion/shotcraft/upstream/demos/camera/basic-3d-scene/Basic3DScene.tsx +250 -0
- package/lib/motion/shotcraft/upstream/demos/camera/crash-zoom-punch/CrashImpactReal.tsx +305 -0
- package/lib/motion/shotcraft/upstream/demos/camera/crash-zoom-punch/CrashZoomReal.tsx +246 -0
- package/lib/motion/shotcraft/upstream/demos/camera/cursor-flyover/CursorFlyover.tsx +390 -0
- package/lib/motion/shotcraft/upstream/demos/camera/depth-layer-moves/MultiplaneReal.tsx +138 -0
- package/lib/motion/shotcraft/upstream/demos/camera/graze-face-tour/GrazeFaceTour.tsx +542 -0
- package/lib/motion/shotcraft/upstream/demos/camera/overhead-camera-moves/OverheadTabletopDrop.tsx +315 -0
- package/lib/motion/shotcraft/upstream/demos/camera/overhead-camera-moves/TiltReveal.tsx +299 -0
- package/lib/motion/shotcraft/upstream/demos/camera/space-camera-moves/DroneDiveLanding.tsx +173 -0
- package/lib/motion/shotcraft/upstream/demos/camera/space-camera-moves/ExplodedView.tsx +348 -0
- package/lib/motion/shotcraft/upstream/demos/camera/steep-tilt-glide/SteepTiltGlide.tsx +366 -0
- package/lib/motion/shotcraft/upstream/demos/camera/tension-camera-moves/BulletTimeFreezeOrbit.tsx +279 -0
- package/lib/motion/shotcraft/upstream/demos/camera/tension-camera-moves/PullBackIsolation.tsx +184 -0
- package/lib/motion/shotcraft/upstream/demos/camera/terminal-3d/Terminal3D.tsx +390 -0
- package/lib/motion/shotcraft/upstream/demos/data/before-after-slider-scrub/BeforeAfterSliderScrub.tsx +155 -0
- package/lib/motion/shotcraft/upstream/demos/data/chart-live-moves/AxisRescaleShockV2.tsx +303 -0
- package/lib/motion/shotcraft/upstream/demos/data/chart-live-moves/OscilloscopeStreamV2.tsx +290 -0
- package/lib/motion/shotcraft/upstream/demos/data/chart-live-moves/UnitDotSwarmRegroupV2.tsx +278 -0
- package/lib/motion/shotcraft/upstream/demos/data/gauge-readout-moves/NeedleSweepSelftest.tsx +307 -0
- package/lib/motion/shotcraft/upstream/demos/data/gauge-readout-moves/TapeScrollFixedPointer.tsx +215 -0
- package/lib/motion/shotcraft/upstream/demos/data/scroll-brake-moves/BrakeReticleLock.tsx +299 -0
- package/lib/motion/shotcraft/upstream/demos/data/scroll-brake-moves/ChangelogScrollBrake.tsx +232 -0
- package/lib/motion/shotcraft/upstream/demos/data/timeline-travel/TimelineTravel.tsx +290 -0
- package/lib/motion/shotcraft/upstream/demos/effects/assemble-then-type-flyin/AssembleThenTypeFlyin.tsx +273 -0
- package/lib/motion/shotcraft/upstream/demos/effects/aurora-bloom-bg-flip/AuroraBloomBgFlip.tsx +167 -0
- package/lib/motion/shotcraft/upstream/demos/effects/brand-frame-snap/BrandFrameSnap.tsx +536 -0
- package/lib/motion/shotcraft/upstream/demos/effects/fui-hud-moves/ReticleLockOn.tsx +164 -0
- package/lib/motion/shotcraft/upstream/demos/effects/glow-flyline-moves/FlylineArc.tsx +175 -0
- package/lib/motion/shotcraft/upstream/demos/effects/glow-flyline-moves/GlowOrbAmbient.tsx +197 -0
- package/lib/motion/shotcraft/upstream/demos/effects/glow-flyline-moves/OrbFlylineRelay.tsx +241 -0
- package/lib/motion/shotcraft/upstream/demos/effects/icon-performance-moves/AttentionBounce.tsx +279 -0
- package/lib/motion/shotcraft/upstream/demos/effects/impact-feedback/AnimeImpact.tsx +230 -0
- package/lib/motion/shotcraft/upstream/demos/effects/impact-feedback/HitCounter.tsx +345 -0
- package/lib/motion/shotcraft/upstream/demos/effects/light-play-moves/SheenSweepRetry.tsx +275 -0
- package/lib/motion/shotcraft/upstream/demos/effects/light-play-moves/SpotlightSweepReveal.tsx +229 -0
- package/lib/motion/shotcraft/upstream/demos/effects/radial-ripple-phone-chips/RadialRipplePhoneChips.tsx +312 -0
- package/lib/motion/shotcraft/upstream/demos/effects/riso-print-hits/RisoMisregistrationHit.tsx +222 -0
- package/lib/motion/shotcraft/upstream/demos/effects/scan-bracket-sweep/ScanBracketSweep.tsx +295 -0
- package/lib/motion/shotcraft/upstream/demos/effects/scanline-annotate-focus/ScanlineAnnotateFocus.tsx +329 -0
- package/lib/motion/shotcraft/upstream/demos/effects/scanline-assemble-flyin/ScanlineAssembleFlyin.tsx +315 -0
- package/lib/motion/shotcraft/upstream/demos/effects/slam-entrance-moves/ImpactBurstKit.tsx +264 -0
- package/lib/motion/shotcraft/upstream/demos/effects/slam-entrance-moves/KanadaPerspectiveSnap.tsx +260 -0
- package/lib/motion/shotcraft/upstream/demos/effects/slam-entrance-moves/ScoreSlam.tsx +263 -0
- package/lib/motion/shotcraft/upstream/demos/effects/spotlight-sweep-moves/CornerSpotlightReveal.tsx +123 -0
- package/lib/motion/shotcraft/upstream/demos/effects/spotlight-sweep-moves/GlowWakeSleepPanel.tsx +187 -0
- package/lib/motion/shotcraft/upstream/demos/effects/spotlight-sweep-moves/SlideSpotlightPan.tsx +175 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/autolayout-gap-dial/AutolayoutGapDial.tsx +305 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/canvas-materialize-moves/DiagramCascadeBuild.tsx +350 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/canvas-materialize-moves/PanelToCanvasMaterialize.tsx +352 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/chip-grid-single-select-blackout/ChipGridSingleSelectBlackout.tsx +218 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/collab-cursor-moves/CursorCastEnsemble.tsx +297 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/glass-pill-dictation-typing/GlassPillDictationTyping.tsx +157 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/hashtag-to-pill-materialize/HashtagToPillMaterialize.tsx +218 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/picker-carousel-feature-cycle/PickerCarouselFeatureCycle.tsx +202 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/segmented-thumb-hero/SegmentedThumbHero.tsx +265 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/theme-switch-moves/PaletteThemeRipple.tsx +457 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/theme-switch-moves/ThemeSweepToggle.tsx +446 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/type-and-filter/TypeAndFilter.tsx +249 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/voice-waveform-live/VoiceWaveformLive.tsx +278 -0
- package/lib/motion/shotcraft/upstream/demos/opening/crane-rise-reveal/CraneRiseReveal.tsx +332 -0
- package/lib/motion/shotcraft/upstream/demos/opening/dataviz-landscape-open/DatavizLandscapeOpen.tsx +358 -0
- package/lib/motion/shotcraft/upstream/demos/opening/fracture/Fracture.tsx +201 -0
- package/lib/motion/shotcraft/upstream/demos/opening/icon-field-colorize/IconFieldColorize.tsx +200 -0
- package/lib/motion/shotcraft/upstream/demos/opening/letterspace-materialize/LetterspaceMaterialize.tsx +208 -0
- package/lib/motion/shotcraft/upstream/demos/opening/magician-card-flourish/MagicianCardFlourish.tsx +341 -0
- package/lib/motion/shotcraft/upstream/demos/opening/orbit-ring-title-open/OrbitRingTitleOpen.tsx +389 -0
- package/lib/motion/shotcraft/upstream/demos/opening/spotlight-hero-card/SpotlightHeroCard.tsx +293 -0
- package/lib/motion/shotcraft/upstream/demos/opening/stroke-segment-build/StrokeSegmentBuild.tsx +183 -0
- package/lib/motion/shotcraft/upstream/demos/opening/text-as-mask/TextAsMask.tsx +245 -0
- package/lib/motion/shotcraft/upstream/demos/outro/grain-dissolve/GrainDissolve.tsx +222 -0
- package/lib/motion/shotcraft/upstream/demos/outro/logo-shrink-wordmark-lockup/LogoShrinkWordmarkLockup.tsx +223 -0
- package/lib/motion/shotcraft/upstream/demos/outro/neon-triple-marquee/NeonTripleMarquee.tsx +217 -0
- package/lib/motion/shotcraft/upstream/demos/outro/outro-group-photo-launch/OutroGroupPhotoLaunch.tsx +343 -0
- package/lib/motion/shotcraft/upstream/demos/outro/ui-strip-away-outro/UiStripAwayOutro.tsx +357 -0
- package/lib/motion/shotcraft/upstream/demos/outro/ui-to-brand-morph/IconFlipBloomLogo.tsx +187 -0
- package/lib/motion/shotcraft/upstream/demos/outro/ui-to-brand-morph/InputMorphsIntoLogo.tsx +333 -0
- package/lib/motion/shotcraft/upstream/demos/rhythm/beat-cut-moves/PaparazziFlash.tsx +274 -0
- package/lib/motion/shotcraft/upstream/demos/rhythm/beat-step-list-theme-cycle/BeatStepListThemeCycle.tsx +270 -0
- package/lib/motion/shotcraft/upstream/demos/rhythm/panel-grid-moves/ComicPanelSplit.tsx +430 -0
- package/lib/motion/shotcraft/upstream/demos/rhythm/quad-split-parallel-scenes/QuadSplitParallelScenes.tsx +252 -0
- package/lib/motion/shotcraft/upstream/demos/rhythm/rhythm-interrupt-moves/JumpCutPunchIn.tsx +106 -0
- package/lib/motion/shotcraft/upstream/demos/rhythm/sakuga-timing-shift/SakugaTimingShift.tsx +288 -0
- package/lib/motion/shotcraft/upstream/demos/rhythm/speed-ramp-freeze/FreezeAnnotateReal.tsx +362 -0
- package/lib/motion/shotcraft/upstream/demos/rhythm/trailer-grammar-moves/TrailerBumper.tsx +168 -0
- package/lib/motion/shotcraft/upstream/demos/transition/bottom-push-stack-wipe/BottomPushStackWipe.tsx +310 -0
- package/lib/motion/shotcraft/upstream/demos/transition/card-flip-reveal/CardFlipReveal.tsx +261 -0
- package/lib/motion/shotcraft/upstream/demos/transition/card-flock-tumble/CardFlockTumble.tsx +459 -0
- package/lib/motion/shotcraft/upstream/demos/transition/circle-match-iris/CircleMatchIris.tsx +294 -0
- package/lib/motion/shotcraft/upstream/demos/transition/gradient-transition/GradientTransition.tsx +161 -0
- package/lib/motion/shotcraft/upstream/demos/transition/line-carry-transition/LineCarryTransition.tsx +248 -0
- package/lib/motion/shotcraft/upstream/demos/transition/mosaic-reframe/MosaicReframe.tsx +359 -0
- package/lib/motion/shotcraft/upstream/demos/transition/page-turn-transitions/BarnDoorSplit.tsx +227 -0
- package/lib/motion/shotcraft/upstream/demos/transition/print-texture-transitions/InkBleedReveal.tsx +162 -0
- package/lib/motion/shotcraft/upstream/demos/transition/shot-transitions/PortalWipeV2.tsx +248 -0
- package/lib/motion/shotcraft/upstream/demos/transition/shot-transitions/WhipBrakeReal.tsx +186 -0
- package/lib/motion/shotcraft/upstream/demos/transition/tear-streak-transitions/GlitchDisplace.tsx +355 -0
- package/lib/motion/shotcraft/upstream/demos/transition/transition-hidden-cut/InvisibleCut.tsx +272 -0
- package/lib/motion/shotcraft/upstream/demos/transition/transition-hidden-cut/LightLeakBurn.tsx +262 -0
- package/lib/motion/shotcraft/upstream/demos/transition/transition-hidden-cut/VersusSlam.tsx +217 -0
- package/lib/motion/shotcraft/upstream/demos/transition/transition-travel/LetterformZoom.tsx +285 -0
- package/lib/motion/shotcraft/upstream/demos/transition/white-flash-logo-simplify-cut/WhiteFlashLogoSimplifyCut.tsx +205 -0
- package/lib/motion/shotcraft/upstream/demos/transition/wipe-transitions/BlindsSlice.tsx +178 -0
- package/lib/motion/shotcraft/upstream/demos/typography/blur-slide/BlurSlide.tsx +147 -0
- package/lib/motion/shotcraft/upstream/demos/typography/brace-expand/BraceExpand.tsx +143 -0
- package/lib/motion/shotcraft/upstream/demos/typography/brand-ink-open/BrandInkOpen.tsx +134 -0
- package/lib/motion/shotcraft/upstream/demos/typography/countdown-arc-scatter/CountdownArcScatter.tsx +217 -0
- package/lib/motion/shotcraft/upstream/demos/typography/flying-words/FlyingWords.tsx +198 -0
- package/lib/motion/shotcraft/upstream/demos/typography/glitch-cycle/GlitchCycle.tsx +221 -0
- package/lib/motion/shotcraft/upstream/demos/typography/gradient-word-sweep/GradientWordSweep.tsx +312 -0
- package/lib/motion/shotcraft/upstream/demos/typography/lead-word-zoom-assemble/LeadWordZoomAssemble.tsx +261 -0
- package/lib/motion/shotcraft/upstream/demos/typography/marker-underline-title/MarkerUnderlineTitle.tsx +121 -0
- package/lib/motion/shotcraft/upstream/demos/typography/outline-word-fill/OutlineWordFill.tsx +153 -0
- package/lib/motion/shotcraft/upstream/demos/typography/paper-title-card/PaperTitleCard.tsx +179 -0
- package/lib/motion/shotcraft/upstream/demos/typography/pill-chip-slot-cycle-handled/PillChipSlotCycleHandled.tsx +140 -0
- package/lib/motion/shotcraft/upstream/demos/typography/pill-slot-cycle/PillSlotCycle.tsx +309 -0
- package/lib/motion/shotcraft/upstream/demos/typography/text-column-converge/TextColumnConverge.tsx +191 -0
- package/lib/motion/shotcraft/upstream/demos/typography/title-demote-to-label/TitleDemoteToLabel.tsx +292 -0
- package/lib/motion/shotcraft/upstream/demos/typography/type-assembly-moves/LetterformDriftAssembly.tsx +135 -0
- package/lib/motion/shotcraft/upstream/demos/typography/type-assembly-moves/SplitTextStagger.tsx +124 -0
- package/lib/motion/shotcraft/upstream/demos/typography/type-assembly-moves/TextOnPath.tsx +255 -0
- package/lib/motion/shotcraft/upstream/demos/typography/type-entrance-moves/LetterDropPhysics.tsx +173 -0
- package/lib/motion/shotcraft/upstream/demos/typography/type-rhythm-sync/FontWeightPump.tsx +225 -0
- package/lib/motion/shotcraft/upstream/demos/typography/type-rhythm-sync/KaraokeFillSync.tsx +141 -0
- package/lib/motion/shotcraft/upstream/demos/typography/typewriter-moves/TypewriterErrorRetype.tsx +137 -0
- package/lib/motion/shotcraft/upstream/demos/typography/vertical-word-roll-blur-cycle/VerticalWordRollBlurCycle.tsx +147 -0
- package/lib/motion/shotcraft/upstream/demos/typography/word-relay-filmstrip/WordRelayFilmstrip.tsx +409 -0
- package/lib/motion/shotcraft/upstream/demos/typography/word-relay-geometry/WordRelayGeometry.tsx +280 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/avatar-bracket-carousel/AvatarBracketCarousel.tsx +271 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/bezier-source-converge-merge/BezierSourceConvergeMerge.tsx +385 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/card-stack/CardStack.tsx +312 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/carousel-3d/Carousel3D.tsx +303 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/clipcard-looping/ClipCardLooping.tsx +77 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/cloner-depth-echo/ClonerDepthEcho.tsx +311 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/deck-deal-flyin/DeckDealFlyin.tsx +307 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/doc-park-left-pill-deal/DocParkLeftPillDeal.tsx +349 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/document-typewriter-reveal/DocumentTypewriterReveal.tsx +255 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/draw-svg-trace/DrawSvgTrace.tsx +244 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/element-body-moves/ContactShadowLift.tsx +217 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/integration-hub-map/IntegrationHubMap.tsx +652 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/list-reveal/ListReveal.tsx +153 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/list-stack-press/ListStackPress.tsx +238 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/neon-frame-forerun/NeonFrameForerun.tsx +324 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/neon-frame-orbit-drop/NeonFrameForerunOrbit.tsx +354 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/page-waterfall-wall/PageWaterfallWall.tsx +101 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/page-waterfall-wall/VerticalTicker.tsx +129 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/product-card-progressive-assemble/ProductCardProgressiveAssemble.tsx +297 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/radial-wave/RadialWave.tsx +57 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/research-card-stack-scroll/ResearchCardStackScroll.tsx +155 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/row-embed/RowEmbed.tsx +175 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/value-stagger-gradient/ValueStaggerGradient.tsx +153 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/wall-reveal-moves/BentoLightUp.tsx +146 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/wall-reveal-moves/WireframeDrawOn.tsx +226 -0
- package/lib/motion/shotcraft/upstream/gallery/api/library.json +1 -0
- package/lib/motion/shotcraft/upstream/manifest.json +2715 -0
- package/lib/motion/shotcraft/upstream/references/aesthetic-rules.md +193 -0
- package/lib/motion/shotcraft/upstream/references/final-review.md +121 -0
- package/lib/motion/shotcraft/upstream/references/guided-free-creation.md +199 -0
- package/lib/motion/shotcraft/upstream/references/jianying-export.md +183 -0
- package/lib/motion/shotcraft/upstream/references/music-beat-sync.md +257 -0
- package/lib/motion/shotcraft/upstream/references/pipeline.md +382 -0
- package/lib/motion/shotcraft/upstream/references/publish-upload-post.md +111 -0
- package/lib/motion/shotcraft/upstream/references/sequences/promo-energy-arc.md +50 -0
- package/lib/motion/shotcraft/upstream/references/sfx-sourcing.md +95 -0
- package/lib/motion/shotcraft/upstream/references/shots/camera/basic-3d-scene.md +41 -0
- package/lib/motion/shotcraft/upstream/references/shots/camera/crash-zoom-punch.md +52 -0
- package/lib/motion/shotcraft/upstream/references/shots/camera/cursor-flyover.md +39 -0
- package/lib/motion/shotcraft/upstream/references/shots/camera/depth-layer-moves.md +39 -0
- package/lib/motion/shotcraft/upstream/references/shots/camera/graze-face-tour.md +57 -0
- package/lib/motion/shotcraft/upstream/references/shots/camera/overhead-camera-moves.md +51 -0
- package/lib/motion/shotcraft/upstream/references/shots/camera/space-camera-moves.md +46 -0
- package/lib/motion/shotcraft/upstream/references/shots/camera/steep-tilt-glide.md +52 -0
- package/lib/motion/shotcraft/upstream/references/shots/camera/tension-camera-moves.md +43 -0
- package/lib/motion/shotcraft/upstream/references/shots/camera/terminal-3d.md +42 -0
- package/lib/motion/shotcraft/upstream/references/shots/data/before-after-slider-scrub.md +48 -0
- package/lib/motion/shotcraft/upstream/references/shots/data/chart-live-moves.md +52 -0
- package/lib/motion/shotcraft/upstream/references/shots/data/gauge-readout-moves.md +45 -0
- package/lib/motion/shotcraft/upstream/references/shots/data/scroll-brake-moves.md +46 -0
- package/lib/motion/shotcraft/upstream/references/shots/data/timeline-travel.md +44 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/assemble-then-type-flyin.md +63 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/aurora-bloom-bg-flip.md +54 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/brand-frame-snap.md +58 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/fui-hud-moves.md +35 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/glow-flyline-moves.md +53 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/icon-performance-moves.md +42 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/impact-feedback.md +50 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/light-play-moves.md +46 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/radial-ripple-phone-chips.md +56 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/riso-print-hits.md +49 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/scan-bracket-sweep.md +50 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/scanline-annotate-focus.md +53 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/scanline-assemble-flyin.md +53 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/slam-entrance-moves.md +55 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/spotlight-sweep-moves.md +52 -0
- package/lib/motion/shotcraft/upstream/references/shots/interaction/autolayout-gap-dial.md +60 -0
- package/lib/motion/shotcraft/upstream/references/shots/interaction/canvas-materialize-moves.md +57 -0
- package/lib/motion/shotcraft/upstream/references/shots/interaction/chip-grid-single-select-blackout.md +51 -0
- package/lib/motion/shotcraft/upstream/references/shots/interaction/collab-cursor-moves.md +41 -0
- package/lib/motion/shotcraft/upstream/references/shots/interaction/glass-pill-dictation-typing.md +53 -0
- package/lib/motion/shotcraft/upstream/references/shots/interaction/hashtag-to-pill-materialize.md +60 -0
- package/lib/motion/shotcraft/upstream/references/shots/interaction/picker-carousel-feature-cycle.md +55 -0
- package/lib/motion/shotcraft/upstream/references/shots/interaction/segmented-thumb-hero.md +57 -0
- package/lib/motion/shotcraft/upstream/references/shots/interaction/theme-switch-moves.md +47 -0
- package/lib/motion/shotcraft/upstream/references/shots/interaction/type-and-filter.md +37 -0
- package/lib/motion/shotcraft/upstream/references/shots/interaction/voice-waveform-live.md +54 -0
- package/lib/motion/shotcraft/upstream/references/shots/opening/brand-ink-open.md +36 -0
- package/lib/motion/shotcraft/upstream/references/shots/opening/crane-rise-reveal.md +56 -0
- package/lib/motion/shotcraft/upstream/references/shots/opening/dataviz-landscape-open.md +56 -0
- package/lib/motion/shotcraft/upstream/references/shots/opening/fracture.md +40 -0
- package/lib/motion/shotcraft/upstream/references/shots/opening/icon-field-colorize.md +60 -0
- package/lib/motion/shotcraft/upstream/references/shots/opening/letterspace-materialize.md +57 -0
- package/lib/motion/shotcraft/upstream/references/shots/opening/magician-card-flourish.md +73 -0
- package/lib/motion/shotcraft/upstream/references/shots/opening/orbit-ring-title-open.md +87 -0
- package/lib/motion/shotcraft/upstream/references/shots/opening/spotlight-hero-card.md +55 -0
- package/lib/motion/shotcraft/upstream/references/shots/opening/stroke-segment-build.md +50 -0
- package/lib/motion/shotcraft/upstream/references/shots/opening/text-as-mask.md +59 -0
- package/lib/motion/shotcraft/upstream/references/shots/outro/grain-dissolve.md +38 -0
- package/lib/motion/shotcraft/upstream/references/shots/outro/logo-shrink-wordmark-lockup.md +38 -0
- package/lib/motion/shotcraft/upstream/references/shots/outro/neon-triple-marquee.md +59 -0
- package/lib/motion/shotcraft/upstream/references/shots/outro/outro-group-photo-launch.md +39 -0
- package/lib/motion/shotcraft/upstream/references/shots/outro/ui-strip-away-outro.md +61 -0
- package/lib/motion/shotcraft/upstream/references/shots/outro/ui-to-brand-morph.md +52 -0
- package/lib/motion/shotcraft/upstream/references/shots/rhythm/beat-cut-moves.md +50 -0
- package/lib/motion/shotcraft/upstream/references/shots/rhythm/beat-step-list-theme-cycle.md +66 -0
- package/lib/motion/shotcraft/upstream/references/shots/rhythm/panel-grid-moves.md +43 -0
- package/lib/motion/shotcraft/upstream/references/shots/rhythm/quad-split-parallel-scenes.md +38 -0
- package/lib/motion/shotcraft/upstream/references/shots/rhythm/rhythm-interrupt-moves.md +38 -0
- package/lib/motion/shotcraft/upstream/references/shots/rhythm/sakuga-timing-shift.md +56 -0
- package/lib/motion/shotcraft/upstream/references/shots/rhythm/speed-ramp-freeze.md +44 -0
- package/lib/motion/shotcraft/upstream/references/shots/rhythm/trailer-grammar-moves.md +38 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/bottom-push-stack-wipe.md +63 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/card-flip-reveal.md +57 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/card-flock-tumble.md +63 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/circle-match-iris.md +58 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/gradient-transition.md +45 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/line-carry-transition.md +55 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/mosaic-reframe.md +49 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/page-turn-transitions.md +42 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/print-texture-transitions.md +42 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/shot-transitions.md +45 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/tear-streak-transitions.md +48 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/transition-hidden-cut.md +55 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/transition-travel.md +43 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/white-flash-logo-simplify-cut.md +41 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/wipe-transitions.md +42 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/blur-slide.md +59 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/brace-expand.md +55 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/countdown-arc-scatter.md +65 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/document-typewriter-reveal.md +42 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/flying-words.md +64 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/glitch-cycle.md +64 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/gradient-word-sweep.md +54 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/lead-word-zoom-assemble.md +91 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/marker-underline-title.md +51 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/outline-word-fill.md +66 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/paper-title-card.md +38 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/pill-chip-slot-cycle-handled.md +66 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/pill-slot-cycle.md +58 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/text-column-converge.md +61 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/title-demote-to-label.md +64 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/type-assembly-moves.md +45 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/type-entrance-moves.md +41 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/type-rhythm-sync.md +51 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/typewriter-moves.md +42 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/vertical-word-roll-blur-cycle.md +63 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/word-relay-filmstrip.md +57 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/word-relay-geometry.md +75 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/avatar-bracket-carousel.md +59 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/bezier-source-converge-merge.md +67 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/card-stack.md +60 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/carousel-3d.md +66 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/cloner-depth-echo.md +40 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/deck-deal-flyin.md +45 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/doc-park-left-pill-deal.md +62 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/draw-svg-trace.md +55 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/element-body-moves.md +37 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/integration-hub-map.md +55 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/list-reveal.md +50 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/list-stack-press.md +43 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/neon-frame-forerun.md +55 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/neon-frame-orbit-drop.md +51 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/page-waterfall-wall.md +51 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/product-card-progressive-assemble.md +56 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/radial-wave.md +49 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/research-card-stack-scroll.md +57 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/row-embed.md +36 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/value-stagger-gradient.md +64 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/wall-reveal-moves.md +47 -0
- package/lib/motion/shotcraft/upstream/references/sound-design.md +170 -0
- package/lib/motion/shotcraft/upstream/references/workbench.md +157 -0
- package/lib/motion/shotcraft/upstream/template/TEMPLATE.md +103 -0
- package/lib/motion/shotcraft/upstream/template/THEMES.md +44 -0
- package/lib/motion/shotcraft/upstream/template/package.json +20 -0
- package/lib/motion/shotcraft/upstream/template/remotion.config.ts +6 -0
- package/lib/motion/shotcraft/upstream/template/src/Root.tsx +17 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/Caption.tsx +65 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/DigitRoll.tsx +48 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/FlashCut.tsx +24 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/Main.tsx +192 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/PaperTitleCard.tsx +111 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/live/PageCam.tsx +150 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneDetail.tsx +158 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneFlyIn.tsx +468 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneOpen.tsx +552 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneOutroLive.tsx +411 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/live/ScenePapers.tsx +200 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneWbr.tsx +324 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/live-layout.json +389 -0
- package/lib/motion/shotcraft/upstream/template/src/index.ts +4 -0
- package/lib/motion/shotcraft/upstream/template/src/themes/ThemedFilm.tsx +7 -0
- package/lib/motion/shotcraft/upstream/template/src/themes/manifest.tsx +68 -0
- package/lib/motion/shotcraft/upstream/template/src/themes/palette-assets.json +1 -0
- package/lib/motion/shotcraft/upstream/template/src/themes/palette-assets.ts +24 -0
- package/lib/motion/shotcraft/upstream/template/src/themes/palettes.json +74 -0
- package/lib/motion/shotcraft/upstream/template/src/themes/visual-theme.tsx +91 -0
- package/lib/motion/shotcraft/upstream/template/src/workbench.ts +213 -0
- package/lib/motion/shotcraft/upstream/template/tsconfig.json +15 -0
- package/lib/motion/spec.js +113 -0
- package/lib/motion/style-engine.js +153 -0
- package/lib/motion/style-frames.js +271 -0
- package/lib/motion/sync.js +214 -0
- package/lib/motion/theme.js +121 -0
- package/lib/motion/video-runtime.js +192 -0
- package/lib/motion/voice.js +417 -0
- package/lib/motion-agent-files.js +82 -0
- package/lib/motion-agent-protocol.js +66 -0
- package/lib/motion-agent-tools.js +196 -0
- package/lib/motion-agent.js +171 -0
- package/lib/motion-capture.js +93 -0
- package/lib/motion-core.js +238 -0
- package/lib/motion-film-review.js +71 -0
- package/lib/motion-library.js +210 -0
- package/lib/motion-planner.js +254 -0
- package/lib/motion-project.js +87 -0
- package/lib/motion-render-worker.js +153 -0
- package/lib/motion-session.js +32 -0
- package/lib/motion-source.js +49 -0
- package/lib/motion-tasks.js +452 -0
- package/lib/motion-vision.js +44 -0
- package/lib/mxfile.js +27 -7
- package/lib/routing/node-runtime.js +14 -0
- package/lib/routing/router.js +211 -0
- package/lib/routing/vendor/README.md +96 -0
- package/lib/routing/vendor/libavoid-routing.js +753 -0
- package/lib/routing/vendor/libavoid.min.js +59 -0
- package/lib/shotcard-core.js +39 -0
- package/lib/shotcard-reference.js +56 -0
- package/lib/style-kernel.js +37 -3
- package/lib/task-patches.js +26 -2
- package/lib/video-export.js +49 -22
- package/package.json +34 -4
|
@@ -0,0 +1,855 @@
|
|
|
1
|
+
// Canvas2D FUI layer — every element is a pure function of t (+ hologram projection state S)
|
|
2
|
+
// Everything is inked in the cyan family; the composite shader re-inks it to alert orange with a radial wave.
|
|
3
|
+
import { clamp, lerp, prog, E, hash, roll, typed, frameOf, LOCK, REEL, V5, NOLINK } from './util.js';
|
|
4
|
+
import { BH, BW, BG, plateW, titleMetrics, slamScale } from './v5.js';
|
|
5
|
+
const CITY_KM = { YTY: 235, YNZ: 267, JHA: 296, HYN: 286, WNZ: 366 };
|
|
6
|
+
|
|
7
|
+
const W = 1920, H = 1080, CX = 960, CY = 540;
|
|
8
|
+
const CYA = '#00E5FF', DIM = '#0B7C8C', DEEP = '#0E3A42', HOT = '#E8FEFF', RED = '#FF2A1F', INK = '#020A0D';
|
|
9
|
+
const MONO = '"JetBrains Mono"', RAJ = 'Rajdhani', CHK = '"Chakra Petch"', HOS = '"HarmonyOS Sans SC"';
|
|
10
|
+
let ctx, tctx, sctx, dpr = 1, K, GA = 1;
|
|
11
|
+
let TXT_ON = true, KC = false, LBLA = 1, TXA = 1, LGX, LTX;
|
|
12
|
+
// defocus a whole plane: draw it unfiltered into offscreen layers, then ONE blurred drawImage (a per-call ctx.filter blur is pathologically slow)
|
|
13
|
+
function blurLayer(px, fn) {
|
|
14
|
+
if (px < 0.3) { fn(); return; }
|
|
15
|
+
const c0 = ctx, t0 = tctx;
|
|
16
|
+
LGX.setTransform(1, 0, 0, 1, 0, 0); LGX.clearRect(0, 0, LGX.canvas.width, LGX.canvas.height); LGX.setTransform(c0.getTransform()); LGX.filter = 'none';
|
|
17
|
+
if (TXT_ON) { LTX.setTransform(1, 0, 0, 1, 0, 0); LTX.clearRect(0, 0, LTX.canvas.width, LTX.canvas.height); }
|
|
18
|
+
ctx = LGX; tctx = LTX;
|
|
19
|
+
try { fn(); } finally { ctx = c0; tctx = t0; }
|
|
20
|
+
const f = `blur(${(px * dpr).toFixed(2)}px)`;
|
|
21
|
+
c0.save(); c0.setTransform(1, 0, 0, 1, 0, 0); c0.filter = f; c0.globalAlpha = 1; c0.drawImage(LGX.canvas, 0, 0); c0.restore();
|
|
22
|
+
if (TXT_ON) { t0.save(); t0.setTransform(1, 0, 0, 1, 0, 0); t0.filter = f; t0.globalAlpha = 1; t0.drawImage(LTX.canvas, 0, 0); t0.restore(); }
|
|
23
|
+
} // TXT_ON: text pass (once per frame, frame centre) · KC: keep-cyan duotone marker · LBLA: ring-label alpha // GA = brightness tier of the element being drawn (100 / 60 / 30 %)
|
|
24
|
+
const SS = 1.26, H0 = 6.35; // scope scale (ring r 318 → 401 px, 42 % of frame width); camera h0 (see holo.js)
|
|
25
|
+
const DEG = Math.PI / 180;
|
|
26
|
+
export function initHUD(canvas, cues, pr, tcanvas) { const mk = () => { const c = document.createElement('canvas'); c.width = canvas.width; c.height = canvas.height; return c.getContext('2d'); }; sctx = mk(); LGX = mk(); LTX = mk(); ctx = canvas.getContext('2d'); tctx = tcanvas.getContext('2d'); dpr = pr; K = cues; }
|
|
27
|
+
// dive (frame-quantised so big numbers stay crisp under the 4-sample motion blur)
|
|
28
|
+
const zAt = (t) => E.outC(prog(t, LOCK, 1.45));
|
|
29
|
+
const magAt = (t) => H0 / Math.exp(lerp(Math.log(H0), Math.log(0.3), zAt(t)));
|
|
30
|
+
// HUD inertia: the planes surge forward on the lock kick and settle as the dive lands (end frame untouched)
|
|
31
|
+
const SURGE = (t) => { const d = t - LOCK; if (d <= 0) return 0; const u = d / 0.3; return u * Math.exp(1 - u); };
|
|
32
|
+
// round 2 · camera push THROUGH the scope ring: 6.43 → 7.10 the HUD flies past the lens (scope ×1 → ×1.62, columns out 120 px +
|
|
33
|
+
// 4 px defocus, top bar / tape out), the hologram unmasks full-frame; the 7.10 resolve flash (closed shutter) is a hard cut back to ×1.0
|
|
34
|
+
const DV0 = LOCK + 1 / 30, DV1 = 7.1;
|
|
35
|
+
export const DIVE = (t) => { if (t < DV0 - 0.02 || t >= DV1 - 1e-4) return 0; const u = clamp((t - DV0 + 0.02) / (DV1 - DV0 + 0.02)); return 0.8 * E.outC(u) + 0.2 * u; };
|
|
36
|
+
const PUSH = (t) => (t >= DV1 ? 0.02 * Math.exp(-(t - DV1) * 9) : 0) + 0.015 * prog(t, 8.4, 1.6); // post-cut settle + 1.5 % end push
|
|
37
|
+
const sstep = (a, b, x) => { const u = clamp((x - a) / (b - a)); return u * u * (3 - 2 * u); };
|
|
38
|
+
function tier(t, keys) { let v = keys[0][1]; for (let i = 1; i < keys.length; i++) v = lerp(v, keys[i][1], sstep(keys[i][0] - 0.12, keys[i][0] + 0.12, t)); return v; }
|
|
39
|
+
// type goes to its own canvas (composited after bloom); plates erase the type they cover
|
|
40
|
+
function knock(x, y, w, h, a = 1) { if (!TXT_ON || w <= 0 || h <= 0) return; tctx.save(); tctx.setTransform(ctx.getTransform()); tctx.globalCompositeOperation = 'destination-out'; tctx.globalAlpha = clamp(a); tctx.fillStyle = '#000'; tctx.fillRect(x, y, w, h); tctx.restore(); }
|
|
41
|
+
|
|
42
|
+
// ---------- primitives
|
|
43
|
+
const MKC = { [CYA]: '#007EFF', [DIM]: '#0B448C', [DEEP]: '#0E2042', [HOT]: '#E88CFF' };
|
|
44
|
+
const kc = (c) => (KC && MKC[c]) || c;
|
|
45
|
+
const st = (c = CYA, w = 1, a = 1) => { ctx.strokeStyle = kc(c); ctx.lineWidth = w; ctx.globalAlpha = clamp(a) * GA; };
|
|
46
|
+
const fl = (c = CYA, a = 1) => { ctx.fillStyle = kc(c); ctx.globalAlpha = clamp(a) * GA; };
|
|
47
|
+
function T(s, x, y, o = {}) {
|
|
48
|
+
if (!s) return 0;
|
|
49
|
+
const ls = o.ls ?? 0;
|
|
50
|
+
const c = tctx; c.setTransform(ctx.getTransform()); c.filter = ctx.filter;
|
|
51
|
+
c.font = `${o.w ?? 500} ${o.s ?? 13}px ${o.f ?? MONO}`;
|
|
52
|
+
c.letterSpacing = `${ls}px`;
|
|
53
|
+
c.textAlign = o.al ?? 'left'; c.textBaseline = o.bl ?? 'alphabetic';
|
|
54
|
+
c.fillStyle = kc(o.c ?? CYA); c.globalAlpha = clamp(o.a ?? 1) * GA * TXA;
|
|
55
|
+
const xx = o.al === 'right' ? x + ls : o.al === 'center' ? x + ls / 2 : x; // trailing tracking compensation
|
|
56
|
+
if (TXT_ON) c.fillText(s, xx, y);
|
|
57
|
+
return c.measureText(s).width - ls;
|
|
58
|
+
}
|
|
59
|
+
const TW = (s, o = {}) => { ctx.font = `${o.w ?? 500} ${o.s ?? 13}px ${o.f ?? MONO}`; ctx.letterSpacing = `${o.ls ?? 0}px`; return ctx.measureText(s).width - (o.ls ?? 0); };
|
|
60
|
+
function L(x0, y0, x1, y1, p = 1) { if (p <= 0) return; p = Math.min(p, 1); ctx.beginPath(); ctx.moveTo(x0, y0); ctx.lineTo(x0 + (x1 - x0) * p, y0 + (y1 - y0) * p); ctx.stroke(); }
|
|
61
|
+
function ARC(x, y, r, a0, a1, p = 1) { if (p <= 0.0005) return; ctx.beginPath(); ctx.arc(x, y, r, a0, a0 + (a1 - a0) * Math.min(p, 1)); ctx.stroke(); }
|
|
62
|
+
const F = (t) => frameOf(t);
|
|
63
|
+
// appear flicker (4 frames) — classic FUI power-in
|
|
64
|
+
function flick(t, t0) { if (t < t0) return 0; const f = F(t) - F(t0); return f >= 4 ? 1 : [0.7, 0.1, 1.0, 0.45][f]; }
|
|
65
|
+
const blink = (t, hz, t0 = 0) => Math.floor((t - t0) * hz * 2) % 2 === 0;
|
|
66
|
+
const pad = (v, n, d) => { const s = Math.abs(v).toFixed(d); const [i, f] = s.split('.'); return (v < 0 ? '-' : '') + i.padStart(n, '0') + (d ? '.' + f : ''); };
|
|
67
|
+
const dms = (v, h1, h2) => { const a = Math.abs(v), d = Math.floor(a), m = Math.round((a - d) * 60); return `${v >= 0 ? h1 : h2}${String(d).padStart(2, '0')}°${String(m).padStart(2, '0')}′`; };
|
|
68
|
+
const GL = 'ABCDEFGHJKLMNPRSTUVWXYZ0123456789#%&/<>';
|
|
69
|
+
function scramble(t, t0, s, id, n = 5) {
|
|
70
|
+
if (F(t0) === F(LOCK)) t0 = LOCK + 1 / 30; // the white-hot impact frame shows only legible words; decodes start on the next frame
|
|
71
|
+
const f = F(t) - F(t0); if (f < 0) return ''; if (f >= n) return s;
|
|
72
|
+
let o = ''; for (let i = 0; i < s.length; i++) o += s[i] === ' ' ? ' ' : (hash(id, i, f) < f / n ? s[i] : GL[Math.floor(hash(id, i, f, 9) * GL.length)]);
|
|
73
|
+
return o;
|
|
74
|
+
}
|
|
75
|
+
function brackets(x, y, hs, arm, lw, a, c = CYA) {
|
|
76
|
+
st(c, lw, a); ctx.lineCap = 'square';
|
|
77
|
+
for (const [sx, sy] of [[-1, -1], [1, -1], [1, 1], [-1, 1]]) {
|
|
78
|
+
const bx = x + sx * hs, by = y + sy * hs;
|
|
79
|
+
ctx.beginPath(); ctx.moveTo(bx, by - sy * arm); ctx.lineTo(bx, by); ctx.lineTo(bx - sx * arm, by); ctx.stroke();
|
|
80
|
+
}
|
|
81
|
+
ctx.lineCap = 'butt';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ---------- panel chrome
|
|
85
|
+
function panel(t, t0, x, y, w, h, idx, label, cn) {
|
|
86
|
+
const p = E.outC(prog(t, t0, 0.5)); if (p <= 0) return 0;
|
|
87
|
+
const a = flick(t, t0);
|
|
88
|
+
T(idx, x, y, { s: 13, c: DIM, a, w: 500 });
|
|
89
|
+
T(scramble(t, t0, label, x + y), x + 24, y, { f: RAJ, w: 600, s: 14, ls: 2.6, a });
|
|
90
|
+
T(cn, x + w, y, { f: HOS, w: 400, s: 14, ls: 1.5, c: DIM, al: 'right', a: a * E.outC(prog(t, t0 + 0.12, 0.3)) });
|
|
91
|
+
st(CYA, 1, 0.85); L(x, y + 9.5, x + w, y + 9.5, p);
|
|
92
|
+
fl(CYA, 1); ctx.fillRect(x, y + 8, 18 * p, 3);
|
|
93
|
+
st(DIM, 1, 0.8); L(x + 0.5, y + 16, x + 0.5, y + h, E.outC(prog(t, t0 + 0.1, 0.5)));
|
|
94
|
+
const q = E.outC(prog(t, t0 + 0.3, 0.3));
|
|
95
|
+
if (q > 0) {
|
|
96
|
+
st(DIM, 1, 0.9 * q);
|
|
97
|
+
L(x + 0.5, y + h + 0.5, x + 12, y + h + 0.5); L(x + w - 12, y + h + 0.5, x + w, y + h + 0.5); L(x + w - 0.5, y + h - 12, x + w - 0.5, y + h + 0.5);
|
|
98
|
+
fl(DIM, q); for (let i = 0; i < 3; i++) ctx.fillRect(x + w - 6 - i * 7, y + 14, 4, 4);
|
|
99
|
+
}
|
|
100
|
+
return a;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ---------- top bar / frame
|
|
104
|
+
function frameCorners(t) {
|
|
105
|
+
const p = E.outE(prog(t, 0.03, 0.2)); if (p <= 0) return;
|
|
106
|
+
st(CYA, 1.5, 0.9); const m = 40, a = 34 * p;
|
|
107
|
+
for (const [x, y, sx, sy] of [[m, m, 1, 1], [W - m, m, -1, 1], [W - m, H - m, -1, -1], [m, H - m, 1, -1]]) {
|
|
108
|
+
ctx.beginPath(); ctx.moveTo(x, y + sy * a); ctx.lineTo(x, y); ctx.lineTo(x + sx * a, y); ctx.stroke();
|
|
109
|
+
}
|
|
110
|
+
// registration marks mid-edges
|
|
111
|
+
st(DIM, 1, p); L(CX - 10, m + 0.5, CX + 10, m + 0.5); L(CX - 10, H - m - 0.5, CX + 10, H - m - 0.5);
|
|
112
|
+
L(m + 0.5, CY - 10, m + 0.5, CY + 10); L(W - m - 0.5, CY - 10, W - m - 0.5, CY + 10);
|
|
113
|
+
}
|
|
114
|
+
function topBar(t, S) {
|
|
115
|
+
const a = flick(t, 0.14); if (!a) return;
|
|
116
|
+
const y = 76;
|
|
117
|
+
// logo mark: hexagon + slash
|
|
118
|
+
st(CYA, 1.5, a); ctx.beginPath();
|
|
119
|
+
for (let i = 0; i < 6; i++) { const an = (i * 60 + 30) * Math.PI / 180; ctx.lineTo(108 + 11 * Math.cos(an), y - 7 + 11 * Math.sin(an)); }
|
|
120
|
+
ctx.closePath(); ctx.stroke(); L(103, y - 1, 113, y - 13);
|
|
121
|
+
const w1 = T('KESTREL-9', 128, y, { f: CHK, w: 600, s: 21, ls: 3.5, a });
|
|
122
|
+
T('隼眼-9 · 轨道光学系统', 128 + w1 + 16, y - 1, { f: HOS, w: 500, s: 14, ls: 2, c: DIM, a });
|
|
123
|
+
// top rule (grows out from centre)
|
|
124
|
+
const p = E.inOutC(prog(t, 0.2, 0.7)); st(DIM, 1, 0.7);
|
|
125
|
+
L(CX - 190, 100.5, 96, 100.5, p); L(CX + 190, 100.5, W - 96, 100.5, p);
|
|
126
|
+
// centre mode box
|
|
127
|
+
let mode = 'BOOT SEQUENCE', cn = '系统自检', hot = false;
|
|
128
|
+
if (t >= K.data_live) { mode = 'SEARCH'; cn = '扫描中'; }
|
|
129
|
+
if (t >= K.signal_detected) { mode = 'SIGNAL DETECTED'; cn = '信号捕获'; hot = true; }
|
|
130
|
+
if (t >= LOCK) { mode = REEL ? 'SIGNAL LOCKED' : 'TARGET LOCKED'; cn = REEL ? '信号锁定' : '目标锁定'; }
|
|
131
|
+
const bw = 360, bx = CX - bw / 2, by = 56;
|
|
132
|
+
st(CYA, 1, a * 0.9); ctx.beginPath();
|
|
133
|
+
ctx.moveTo(bx + 10, by + 0.5); ctx.lineTo(bx + bw - 10, by + 0.5); ctx.lineTo(bx + bw, by + 16); ctx.lineTo(bx + bw - 10, by + 31.5);
|
|
134
|
+
ctx.lineTo(bx + 10, by + 31.5); ctx.lineTo(bx, by + 16); ctx.closePath(); ctx.stroke();
|
|
135
|
+
const on = !hot || t >= LOCK || blink(t, 4, K.signal_detected);
|
|
136
|
+
if (t >= LOCK) { fl(CYA, 0.22 + 0.12 * Math.exp(-((t - LOCK) % 0.5) * 8)); ctx.fill(); }
|
|
137
|
+
const ch = [K.data_live, K.signal_detected, LOCK].filter((c) => t >= c).pop() ?? 0.14;
|
|
138
|
+
const ms = ch === LOCK ? mode : scramble(t, ch, mode, 77 + Math.floor(ch * 10));
|
|
139
|
+
const mw = TW(ms, { f: RAJ, w: 700, s: 17, ls: 3 }), cw = TW(cn, { f: HOS, w: 500, s: 15, ls: 3 });
|
|
140
|
+
const x0 = CX - (mw + 14 + cw) / 2;
|
|
141
|
+
if (on) { T(ms, x0, by + 22, { f: RAJ, w: 700, s: 17, ls: 3, a, c: t >= LOCK ? HOT : CYA }); T(cn, x0 + mw + 14, by + 21, { f: HOS, w: 500, s: 15, ls: 3, a, c: t >= LOCK ? HOT : CYA }); }
|
|
142
|
+
// right: clock + rec
|
|
143
|
+
const tt = 3 * 3600 + 14 * 60 + 52 + t, hh = Math.floor(tt / 3600), mm = Math.floor(tt / 60) % 60, ss = Math.floor(tt) % 60, ff = F(t) % 30;
|
|
144
|
+
const clk = `UTC ${pad(hh, 2, 0)}:${pad(mm, 2, 0)}:${pad(ss, 2, 0)}.${pad(ff, 2, 0)}`;
|
|
145
|
+
T(clk, W - 96, y, { s: 15, w: 500, al: 'right', a, ls: 1 });
|
|
146
|
+
T(`F ${pad(F(t), 4, 0)} · 30P`, W - 96, y - 22, { s: 13, c: DIM, al: 'right', a, ls: 1 });
|
|
147
|
+
const rx = W - 96 - TW(clk, { s: 15, w: 500, ls: 1 }) - 58;
|
|
148
|
+
if (blink(t, 1)) { fl(RED, a); ctx.beginPath(); ctx.arc(rx, y - 5, 5, 0, 7); ctx.fill(); }
|
|
149
|
+
T('REC', rx + 12, y, { f: RAJ, w: 700, s: 14, ls: 2, c: DIM, a });
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// ---------- central scope
|
|
153
|
+
const SEGS = (() => { const r = []; let a = 0; let i = 0; while (a < 360 && i < 40) { const len = 4 + hash(3, i) * 30, gap = 3 + hash(4, i) * 10; r.push([a, Math.min(a + len, 360), hash(5, i) > 0.7]); a += len + gap; i++; } return r; })();
|
|
154
|
+
// acquisition spin-up: rings accelerate (cubic) from the first candidate hop to the snap, then brake hard
|
|
155
|
+
function SPIN(t) {
|
|
156
|
+
const a = 4.9, b = 6.33, A = 55, v = 3 * A / (b - a);
|
|
157
|
+
if (t < a) return 0; if (t < b) return A * ((t - a) / (b - a)) ** 3;
|
|
158
|
+
return A + v * (1 - Math.exp(-(t - b) * 9)) / 9;
|
|
159
|
+
}
|
|
160
|
+
function scope(t, S) {
|
|
161
|
+
const dv = DIVE(t), k = SS * (1 + 0.62 * dv + PUSH(t)); LBLA = 1 - sstep(0.06, 0.3, dv);
|
|
162
|
+
ctx.save(); ctx.translate(CX, CY); ctx.scale(k, k); ctx.translate(-CX, -CY); TXA = LBLA; scopeInner(t, S); TXA = 1; ctx.restore();
|
|
163
|
+
}
|
|
164
|
+
function scopeInner(t, S) {
|
|
165
|
+
// crosshair (first thing on screen)
|
|
166
|
+
const pc = E.outE(prog(t, 0.04, 0.22));
|
|
167
|
+
if (pc > 0) {
|
|
168
|
+
st(HOT, 1.5, 0.95); const g = 16, l = 44;
|
|
169
|
+
L(CX - g, CY + 0.5, CX - g - (l - g) * pc, CY + 0.5); L(CX + g, CY + 0.5, CX + g + (l - g) * pc, CY + 0.5);
|
|
170
|
+
L(CX + 0.5, CY - g, CX + 0.5, CY - g - (l - g) * pc); L(CX + 0.5, CY + g, CX + 0.5, CY + g + (l - g) * pc);
|
|
171
|
+
fl(HOT, pc); ctx.fillRect(CX - 1, CY - 1, 3, 3);
|
|
172
|
+
st(CYA, 1, 0.22 * pc); L(CX - 60, CY + 0.5, CX - 232, CY + 0.5, pc); L(CX + 60, CY + 0.5, CX + 232, CY + 0.5, pc);
|
|
173
|
+
L(CX + 0.5, CY - 60, CX + 0.5, CY - 232, pc); L(CX + 0.5, CY + 60, CX + 0.5, CY + 232, pc);
|
|
174
|
+
}
|
|
175
|
+
// boot hook (frame snap → ring draw-on): a range ping expands from the crosshair and lands on the outer ring as it
|
|
176
|
+
// draws on at 0.40, while calibration rulers race out along both axes to the frame edge, then dim away by ~0.8 s
|
|
177
|
+
const b0 = K.boot.frame;
|
|
178
|
+
if (t >= b0 && t < 0.85) {
|
|
179
|
+
const tq = F(t) / 30 + 1e-4;
|
|
180
|
+
for (let e = 0; e < 4; e++) {
|
|
181
|
+
const kp = prog(tq - e / 30, b0, K.boot.ring_outer - b0); if (kp <= 0 || kp >= 1) continue;
|
|
182
|
+
const rr = Math.max(2, 318 * E.outC(kp)), fa = (1 - kp) ** 0.4 * [1, 0.42, 0.22, 0.1][e];
|
|
183
|
+
if (!e) { st(CYA, 6, 0.4 * fa); ARC(CX, CY, rr, 0, Math.PI * 2); }
|
|
184
|
+
st(HOT, e ? 1 : 1.6, fa); ARC(CX, CY, rr, 0, Math.PI * 2);
|
|
185
|
+
}
|
|
186
|
+
// landing: the ping leaves a faint full ring that the outer arcs draw over, then decays
|
|
187
|
+
if (t >= K.boot.ring_outer) { const d = t - K.boot.ring_outer; st(HOT, 1.5, 0.55 * Math.exp(-d * 6)); ARC(CX, CY, 318, 0, Math.PI * 2); st(CYA, 8, 0.12 * Math.exp(-d * 9)); ARC(CX, CY, 318, 0, Math.PI * 2); }
|
|
188
|
+
const ks = E.outE(prog(t, b0, 0.34)), fade = 1 - E.inOutC(prog(t, 0.45, 0.38));
|
|
189
|
+
const RX = 860 * ks, RY = 440 * ks;
|
|
190
|
+
st(CYA, 1, 0.8 * fade); L(CX - 240, CY + 0.5, CX - 240 - (RX - 240), CY + 0.5, ks > 0.28 ? 1 : 0); L(CX + 240, CY + 0.5, CX + RX, CY + 0.5, ks > 0.28 ? 1 : 0);
|
|
191
|
+
L(CX + 0.5, CY - 240, CX + 0.5, CY - Math.max(240, RY)); L(CX + 0.5, CY + 240, CX + 0.5, CY + Math.max(240, RY));
|
|
192
|
+
for (let d = 400; d <= RX; d += 20) {
|
|
193
|
+
const big = d % 100 === 0; st(big ? CYA : DIM, 1, (big ? 0.95 : 0.75) * fade);
|
|
194
|
+
for (const sx of [-1, 1]) { L(CX + sx * d + 0.5, CY + 3, CX + sx * d + 0.5, CY + 3 + (big ? 9 : 4)); if (d % 200 === 0) T(String(d), CX + sx * d, CY + 26, { f: RAJ, w: 600, s: 12, ls: 1, al: 'center', c: CYA, a: 0.9 * fade }); }
|
|
195
|
+
}
|
|
196
|
+
for (let d = 300; d <= RY; d += 20) { const big = d % 100 === 0; st(big ? CYA : DIM, 1, (big ? 0.7 : 0.55) * fade); for (const sy of [-1, 1]) L(CX + 3, CY + sy * d + 0.5, CX + 3 + (big ? 9 : 4), CY + sy * d + 0.5); }
|
|
197
|
+
const ha = fade * (1 - ks) ** 0.5; fl(HOT, ha);
|
|
198
|
+
if (RX > 240) { ctx.fillRect(CX + RX - 16, CY - 1, 16, 3); ctx.fillRect(CX - RX, CY - 1, 16, 3); }
|
|
199
|
+
if (RY > 240) { ctx.fillRect(CX - 1, CY - RY, 3, 16); ctx.fillRect(CX - 1, CY + RY - 16, 3, 16); }
|
|
200
|
+
}
|
|
201
|
+
// outer ring R=318, four arcs with gaps, slow rotation
|
|
202
|
+
const p1 = E.outC(prog(t, K.boot.ring_outer, 0.5));
|
|
203
|
+
const rot = (t * 2.2 + 0.35 * SPIN(t)) * Math.PI / 180;
|
|
204
|
+
st(CYA, 1.25, 0.95);
|
|
205
|
+
if (!NOLINK.has('scope')) for (let q = 0; q < 4; q++) { const a0 = rot + (q * 90 + 5) * Math.PI / 180, a1 = rot + (q * 90 + 85) * Math.PI / 180; ARC(CX, CY, 318, a0, a1, p1); }
|
|
206
|
+
// cardinal notches
|
|
207
|
+
if (p1 > 0.5 && !NOLINK.has('scope')) { st(CYA, 2, (p1 - 0.5) * 2); for (let q = 0; q < 4; q++) { const an = rot + q * Math.PI / 2; L(CX + Math.cos(an) * 312, CY + Math.sin(an) * 312, CX + Math.cos(an) * 326, CY + Math.sin(an) * 326); } }
|
|
208
|
+
// tick ring r=300 + bearing labels r=342
|
|
209
|
+
const p2 = prog(t, K.boot.ring_ticks, 0.5);
|
|
210
|
+
if (p2 > 0) {
|
|
211
|
+
for (let i = 0; i < 120; i++) {
|
|
212
|
+
const k = i / 120; if (k > p2) break;
|
|
213
|
+
const an = (i * 3 - 90) * Math.PI / 180, big = i % 10 === 0, mid = i % 5 === 0, len = big ? 14 : mid ? 9 : 5;
|
|
214
|
+
st(big ? CYA : DIM, big ? 1.5 : 1, big ? 1 : 0.9);
|
|
215
|
+
L(CX + Math.cos(an) * 300, CY + Math.sin(an) * 300, CX + Math.cos(an) * (300 - len), CY + Math.sin(an) * (300 - len));
|
|
216
|
+
if (big && i !== 60 && i !== 0) T(pad(i * 3, 3, 0), CX + Math.cos(an) * 344, CY + Math.sin(an) * 344 + 4, { f: RAJ, w: 600, s: 12, ls: 1, al: 'center', c: DIM, a: flick(t, K.boot.ring_ticks + k * 0.5) * LBLA });
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
// segmented ring r=276 (counter-rotating), dashed ring r=258, halo ring r=232
|
|
220
|
+
const p3 = E.outC(prog(t, K.boot.ring_inner, 0.6));
|
|
221
|
+
if (p3 > 0) {
|
|
222
|
+
const r2 = -(t * 7 + 20 * E.outQ(prog(t, K.signal_detected, 0.8)) + SPIN(t) + 45 * E.outQ(prog(t, LOCK, 0.9))) * Math.PI / 180;
|
|
223
|
+
for (const [a0, a1, b] of SEGS) {
|
|
224
|
+
if (a0 / 360 > p3) continue;
|
|
225
|
+
st(b ? CYA : DIM, b ? 3 : 2.2, b ? 0.95 : 0.85);
|
|
226
|
+
ARC(CX, CY, 276, r2 + a0 * Math.PI / 180, r2 + Math.min(a1, 360 * p3) * Math.PI / 180);
|
|
227
|
+
}
|
|
228
|
+
ctx.setLineDash([2, 7]); ctx.lineDashOffset = -t * 30; st(CYA, 1, 0.55 * p3); ARC(CX, CY, 258, 0, Math.PI * 2 * p3); ctx.setLineDash([]);
|
|
229
|
+
st(CYA, 1, 0.28 * p3); ARC(CX, CY, 232, -Math.PI / 2, Math.PI * 1.5, p3);
|
|
230
|
+
}
|
|
231
|
+
// main-scope radar sweep: conic phosphor trail (2.0 s/rev, 0.6 s trail) over the globe; its passes reveal the candidates
|
|
232
|
+
const sw = sstep(K.sweep[0], K.sweep[0] + 0.25, t) * (1 - sstep(K.sweep[1] - 0.25, K.sweep[1], t));
|
|
233
|
+
if (sw > 0) {
|
|
234
|
+
const a = (K.radar_theta0_deg + 180 * (t - K.radar_start) - 90) * DEG, tr = 0.6 * Math.PI, f = tr / (2 * Math.PI);
|
|
235
|
+
const g = ctx.createConicGradient(a - tr, CX, CY);
|
|
236
|
+
g.addColorStop(0, 'rgba(0,229,255,0)'); g.addColorStop(f * 0.5, 'rgba(0,229,255,0.06)'); g.addColorStop(f * 0.86, 'rgba(0,229,255,0.16)');
|
|
237
|
+
g.addColorStop(f, 'rgba(0,229,255,0.38)'); g.addColorStop(f + 0.0008, 'rgba(0,229,255,0)'); g.addColorStop(1, 'rgba(0,229,255,0)');
|
|
238
|
+
ctx.globalAlpha = sw * GA; ctx.fillStyle = g; ctx.beginPath(); ctx.arc(CX, CY, 298, 0, Math.PI * 2); ctx.fill();
|
|
239
|
+
st(HOT, 1.6, 0.95 * sw); L(CX + Math.cos(a) * 20, CY + Math.sin(a) * 20, CX + Math.cos(a) * 298, CY + Math.sin(a) * 298);
|
|
240
|
+
st(CYA, 5, 0.18 * sw); L(CX + Math.cos(a) * 20, CY + Math.sin(a) * 20, CX + Math.cos(a) * 298, CY + Math.sin(a) * 298);
|
|
241
|
+
}
|
|
242
|
+
// sweep highlight on SIGNAL DETECTED (bright arc racing around the ring)
|
|
243
|
+
if (t >= K.signal_detected && t < K.signal_detected + 0.6) {
|
|
244
|
+
const k = E.outC(prog(t, K.signal_detected, 0.55)), an = -Math.PI / 2 + k * Math.PI * 2;
|
|
245
|
+
st(HOT, 3, 1 - k * 0.6); ARC(CX, CY, 318, an - 0.5, an);
|
|
246
|
+
}
|
|
247
|
+
// scope corner readouts
|
|
248
|
+
const pa = flick(t, 1.0); if (!pa) return;
|
|
249
|
+
const mag = magAt(F(t) / 30 + 1e-4);
|
|
250
|
+
const rd = [
|
|
251
|
+
[CX - 262, CY - 262, 'right', 'MAG', `×${mag.toFixed(1).padStart(4, ' ')}`],
|
|
252
|
+
[CX + 262, CY - 262, 'left', 'FOV', `${(12.4 / mag).toFixed(2)}°`],
|
|
253
|
+
[CX - 262, CY + 272, 'right', 'HDG', `${pad(((S.lonC ?? 0) + 360) % 360, 3, 2)}°`],
|
|
254
|
+
[CX + 262, CY + 272, 'left', 'TLT', `${pad(S.tiltDeg ?? 0, 2, 2)}°`],
|
|
255
|
+
];
|
|
256
|
+
for (const [x, y, al, lab, v] of rd) {
|
|
257
|
+
const lw = TW(lab + ' ', { f: RAJ, w: 600, s: 12, ls: 2 });
|
|
258
|
+
if (al === 'right') { T(v, x, y, { s: 14, w: 600, al: 'right', a: pa }); T(lab, x - TW(v, { s: 14, w: 600 }) - 10, y, { f: RAJ, w: 600, s: 12, ls: 2, c: DIM, al: 'right', a: pa }); }
|
|
259
|
+
else { T(lab, x, y, { f: RAJ, w: 600, s: 12, ls: 2, c: DIM, a: pa }); T(v, x + lw + 4, y, { s: 14, a: pa }); }
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// ---------- left column (x 96–396): boot log · RNG hero readout · telemetry · radar minimap
|
|
264
|
+
const LOG = [['OPTIC CORE v4.2.1', 'OK'], ['CRYO FPA 4096² · 77K', 'OK'], ['GYRO ALIGN Δ0.0003°', 'OK'],
|
|
265
|
+
['ORBIT LEO 512 KM', 'OK'], ['UPLINK QKD-7 9.6G', 'OK'], ['SIG DB 14 220 PROF', 'OK']];
|
|
266
|
+
const CW = 300;
|
|
267
|
+
function leftCol(t, S) {
|
|
268
|
+
const x = 96, w = CW;
|
|
269
|
+
GA = tier(t, [[0, 1], [2.6, 0.55], [4.4, 1], [5.1, 0.5], [6.45, 1], [7.3, 0.5]]);
|
|
270
|
+
if (panel(t, 0.36, x, 128, w, 250, '01', 'SYS BOOT', '系统自检')) {
|
|
271
|
+
const lt = [0.40, 0.66, 0.92, 1.18, 1.44, 1.70], y = 164, lh = 24;
|
|
272
|
+
LOG.forEach(([s, ok], i) => {
|
|
273
|
+
const ty = typed(t, lt[i], s, 110); if (!ty) return;
|
|
274
|
+
T('>', x + 8, y + i * lh, { s: 14, c: DIM }); T(ty, x + 24, y + i * lh, { s: 14, w: 400, c: CYA, a: 0.95 });
|
|
275
|
+
const done = lt[i] + s.length / 110;
|
|
276
|
+
if (t >= done) {
|
|
277
|
+
const lw = TW(s, { s: 14, w: 400 });
|
|
278
|
+
ctx.setLineDash([1, 4]); st(DIM, 1, 0.7); L(x + 30 + lw, y + i * lh - 4, x + w - 42, y + i * lh - 4); ctx.setLineDash([]);
|
|
279
|
+
T(`[${ok}]`, x + w - 4, y + i * lh, { s: 14, w: 600, al: 'right', a: flick(t, done + 0.03) });
|
|
280
|
+
} else T('█', x + 24 + TW(ty, { s: 14, w: 400 }) + 1, y + i * lh, { s: 13, c: CYA });
|
|
281
|
+
});
|
|
282
|
+
const extra = [[1.98, '> SEARCH MODE ENGAGED', CYA], [K.signal_detected, '! SIGNAL · BRG 041°', HOT], [LOCK + 0.1, REEL ? '■ SIG-03 LOCKED' : '■ TGT-03 LOCKED', HOT]];
|
|
283
|
+
extra.forEach(([t0, s, c], j) => {
|
|
284
|
+
const ty = typed(t, t0, s, 120); if (!ty) return;
|
|
285
|
+
const yy = y + (6 + j) * lh, last = j === 2 ? true : t < extra[j + 1][0];
|
|
286
|
+
T(ty, x + 8, yy, { s: 14, w: 600, c, a: j === 1 && t < LOCK ? (blink(t, 4, t0) ? 1 : 0.55) : 1 });
|
|
287
|
+
if (last && blink(t, 2.5)) T('█', x + 8 + TW(ty, { s: 14, w: 600 }) + 3, yy, { s: 13, c: CYA });
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
// RNG — the typographic anchor (Chakra Petch 72 px). During the dive it becomes the effective optical range and plunges.
|
|
291
|
+
GA = 1;
|
|
292
|
+
const ra = flick(t, K.boot.panel_left + 0.25);
|
|
293
|
+
if (ra && !(V5 && t >= LOCK)) { // v5: the 72 px RNG readout sits under the title band → it gives way on the lock
|
|
294
|
+
const yb = 424, tq = F(t) / 30 + 1e-4, dive = tq >= LOCK, mag = magAt(tq);
|
|
295
|
+
const rng = lerp(1712.6, 1648.3, E.inOutC(prog(tq, K.data_live, 4.0))), val = dive ? rng / mag : rng;
|
|
296
|
+
const lab = dive ? 'EFF RNG' : 'SLANT RNG', cn = dive ? '等效距离' : '斜距';
|
|
297
|
+
T(scramble(t, dive ? LOCK : K.boot.panel_left + 0.25, lab, 61 + (dive ? 1 : 0), 5), x, yb, { f: RAJ, w: 700, s: 15, ls: 3, a: ra });
|
|
298
|
+
T(cn, x + w, yb, { f: HOS, w: 500, s: 14, ls: 2, c: DIM, al: 'right', a: ra });
|
|
299
|
+
st(CYA, 1, 0.85 * ra); L(x, yb + 9.5, x + w, yb + 9.5); fl(CYA, ra); ctx.fillRect(x, yb + 8, 18, 3);
|
|
300
|
+
const fin = pad(val, 4, 1), s = t < K.data_live ? null : roll(t, K.data_live, fin, 88, 7);
|
|
301
|
+
const nw = T(s ?? '----.-', x - 3, yb + 88, { f: CHK, w: 600, s: 72, ls: 1, c: s ? HOT : DIM, a: ra * (s ? 1 : 0.7) });
|
|
302
|
+
T('KM', x + w, yb + 88, { f: RAJ, w: 700, s: 20, ls: 2, al: 'right', c: CYA, a: ra });
|
|
303
|
+
// zoom meter: 30 ticks, fills with the dive (log-magnification)
|
|
304
|
+
const zz = dive ? zAt(tq) : 0;
|
|
305
|
+
for (let i = 0; i < 30; i++) { const on = i < Math.round(zz * 30); fl(on ? (i === Math.round(zz * 30) - 1 ? HOT : CYA) : DEEP, on ? 1 : 0.9); ctx.fillRect(x + i * 10, yb + 104, 7, on ? 6 : 4); }
|
|
306
|
+
T(`MAG ×${mag.toFixed(1)}`, x, yb + 136, { s: 16, w: 600, c: dive ? HOT : CYA, a: ra });
|
|
307
|
+
T(`FOV ${(12.4 / mag).toFixed(2)}°`, x + w, yb + 136, { s: 16, w: 500, al: 'right', c: CYA, a: ra * 0.85 });
|
|
308
|
+
void nw;
|
|
309
|
+
}
|
|
310
|
+
GA = tier(t, [[0, 1], [3.9, 0.6], [6.45, 0.85], [7.4, 0.4]]);
|
|
311
|
+
if (panel(t, K.boot.panel_left, x, 612, w, 186, '02', 'NAV TELEMETRY', '导航遥测')) {
|
|
312
|
+
const rows = [
|
|
313
|
+
['AZM', ((S.lonC ?? 0) + 360) % 360, 3, 2, '°'], ['ELV', S.tiltDeg ?? 0, 3, 2, '°'], ['ALT', 512.08 + 0.02 * Math.sin(t * 3), 3, 2, 'KM'],
|
|
314
|
+
['VEL', 7.612 + 0.001 * Math.floor(hash(F(t) >> 2, 5) * 4), 1, 3, 'KM/S'],
|
|
315
|
+
['SNR', t < K.signal_detected ? 11.4 + hash(F(t) >> 2, 9) * 0.6 : lerp(11.4, 41.2, E.outC(prog(t, K.signal_detected, 0.6))), 2, 1, 'dB'],
|
|
316
|
+
];
|
|
317
|
+
rows.forEach(([lab, v, n, d, u], i) => {
|
|
318
|
+
const yy = 652 + i * 31, t0 = K.data_rolls[i];
|
|
319
|
+
T(lab, x + 8, yy, { f: RAJ, w: 600, s: 14, ls: 2.5, c: DIM });
|
|
320
|
+
const fin = pad(v, n, d), s = roll(t, t0, fin, 30 + i, 3 + (i % 3));
|
|
321
|
+
T(s ?? fin.replace(/[0-9]/g, '-'), x + 156, yy + 1, { s: 18, w: 500, al: 'right', c: s ? HOT : DIM, a: s ? 1 : 0.6 });
|
|
322
|
+
T(u, x + 162, yy, { s: 13, c: DIM });
|
|
323
|
+
const m = clamp(0.25 + 0.6 * hash(i, 1) + 0.08 * Math.sin(t * (2 + i) + i)), mp = E.outC(prog(t, t0 + 0.1, 0.4));
|
|
324
|
+
st(DEEP, 4, 1); L(x + 212, yy - 5, x + w - 6, yy - 5); st(CYA, 4, 0.9); L(x + 212, yy - 5, x + 212 + (w - 218) * m * mp, yy - 5);
|
|
325
|
+
if (i < 4) { st(DEEP, 1, 0.9); L(x + 8, yy + 11.5, x + w - 6, yy + 11.5, mp); }
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
GA = tier(t, [[0, 0.6], [2.4, 0.8], [4.6, 0.35], [7.4, 0.5]]); KC = true;
|
|
329
|
+
if (panel(t, K.boot.panel_left + 0.12, x, 840, w, 160, '03', 'RDR · MINIMAP', '雷达')) radar(t, x + 66, 934, 52, x, w);
|
|
330
|
+
GA = 1; KC = false;
|
|
331
|
+
}
|
|
332
|
+
function radar(t, rx, ry, R, x, w) {
|
|
333
|
+
const p = E.outC(prog(t, K.boot.panel_left + 0.2, 0.6));
|
|
334
|
+
st(DIM, 1, 0.9); for (const k of [1 / 3, 2 / 3, 1]) ARC(rx, ry, R * k, -Math.PI / 2, Math.PI * 1.5, p);
|
|
335
|
+
st(DEEP, 1, 1); L(rx - R, ry + 0.5, rx + R, ry + 0.5, p); L(rx + 0.5, ry - R, rx + 0.5, ry + R, p);
|
|
336
|
+
for (let i = 0; i < 36; i++) { if (i / 36 > p) break; const an = (i * 10 - 90) * DEG, l = i % 9 === 0 ? 7 : 3; st(i % 9 === 0 ? CYA : DIM, 1, 1); L(rx + Math.cos(an) * (R + 2), ry + Math.sin(an) * (R + 2), rx + Math.cos(an) * (R + 2 + l), ry + Math.sin(an) * (R + 2 + l)); }
|
|
337
|
+
const lx = x + w - 4;
|
|
338
|
+
T('CONTACTS', lx, 876, { f: RAJ, w: 600, s: 14, ls: 2, c: DIM, al: 'right', a: p });
|
|
339
|
+
if (t < K.radar_start) return;
|
|
340
|
+
const brg = K.radar_theta0_deg + 180 * (t - K.radar_start);
|
|
341
|
+
const a = (brg - 90) * DEG, wedge = 1.1;
|
|
342
|
+
const g = ctx.createConicGradient(a - wedge, rx, ry);
|
|
343
|
+
g.addColorStop(0, 'rgba(0,229,255,0)'); g.addColorStop(wedge / (Math.PI * 2), KC ? 'rgba(0,126,255,0.34)' : 'rgba(0,229,255,0.34)'); g.addColorStop(wedge / (Math.PI * 2) + 0.001, 'rgba(0,229,255,0)'); g.addColorStop(1, 'rgba(0,229,255,0)');
|
|
344
|
+
ctx.globalAlpha = GA; ctx.fillStyle = g; ctx.beginPath(); ctx.arc(rx, ry, R, 0, Math.PI * 2); ctx.fill();
|
|
345
|
+
st(HOT, 1.5, 0.95); L(rx, ry, rx + Math.cos(a) * R, ry + Math.sin(a) * R);
|
|
346
|
+
const blips = [...K.radar_blips]; if (t >= K.signal_detected) blips.push({ brg: 41, r: 0.34, sig: true });
|
|
347
|
+
blips.forEach((b, i) => {
|
|
348
|
+
const since = (((brg - b.brg) % 360) + 360) % 360 / 180, age = t - K.radar_start - (((b.brg - K.radar_theta0_deg) % 360 + 360) % 360) / 180;
|
|
349
|
+
let I = age >= 0 ? Math.exp(-since * 1.6) : 0;
|
|
350
|
+
if (b.sig) I = Math.max(I, 0.55 + 0.45 * Math.cos((t - K.signal_detected) * 12));
|
|
351
|
+
const ba = (b.brg - 90) * DEG, bx = rx + Math.cos(ba) * R * b.r, by = ry + Math.sin(ba) * R * b.r;
|
|
352
|
+
if (I > 0.02) { fl(b.sig ? HOT : CYA, I); ctx.fillRect(bx - 2, by - 2, 4, 4); if (b.sig) { st(HOT, 1.2, 1); ctx.strokeRect(bx - 6.5, by - 6.5, 13, 13); } }
|
|
353
|
+
const yy = 898 + i * 20, seen = age >= 0 || b.sig;
|
|
354
|
+
if (seen) T(`${b.sig ? 'SIG' : 'B-' + (i + 1)} ${pad(b.brg, 3, 0)}° ${b.r.toFixed(2)}`, lx, yy, { s: 13, al: 'right', c: b.sig ? HOT : I > 0.3 ? CYA : DIM, a: flick(t, K.radar_start + (b.sig ? K.signal_detected - K.radar_start : ((((b.brg - K.radar_theta0_deg) % 360) + 360) % 360) / 180)) });
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// ---------- right column (x 1524–1824): three panels on their own rhythm + a 30 % footer strip
|
|
359
|
+
function spark(t, x, y, w, h, seed, t0, lab, unit, base, amp) {
|
|
360
|
+
const p = E.inOutC(prog(t, t0, 0.7)); if (p <= 0) return;
|
|
361
|
+
st(DEEP, 1, 1); for (let k = 0; k <= 2; k++) L(x, y + h * k / 2 + 0.5, x + w, y + h * k / 2 + 0.5);
|
|
362
|
+
const N = 90, win = 3.0, pts = [];
|
|
363
|
+
for (let i = 0; i <= N; i++) {
|
|
364
|
+
const u = i / N; if (u > p) break;
|
|
365
|
+
const ts = t - (1 - u) * win, j = Math.floor(ts * 20);
|
|
366
|
+
let v = 0.45 + 0.18 * Math.sin(ts * 3.1 + seed) + 0.12 * Math.sin(ts * 7.7 + seed * 2) + 0.14 * (hash(seed, j) - 0.5);
|
|
367
|
+
if (ts > K.signal_detected) v += 0.42 * Math.exp(-(ts - K.signal_detected) * 1.2) + 0.12;
|
|
368
|
+
if (ts > LOCK) v = 0.8 + 0.07 * Math.sin(ts * 9 + seed) + 0.05 * (hash(seed, j, 2) - 0.5);
|
|
369
|
+
pts.push([x + u * w, y + h - clamp(v, 0.02, 0.98) * h]);
|
|
370
|
+
}
|
|
371
|
+
ctx.beginPath(); pts.forEach(([a, b], i) => (i ? ctx.lineTo(a, b) : ctx.moveTo(a, b)));
|
|
372
|
+
st(CYA, 1.4, 1); ctx.stroke();
|
|
373
|
+
ctx.lineTo(pts[pts.length - 1][0], y + h); ctx.lineTo(x, y + h); ctx.closePath(); fl(CYA, 0.08); ctx.fill();
|
|
374
|
+
const [ex, ey] = pts[pts.length - 1]; fl(HOT, 1); ctx.fillRect(ex - 2, ey - 2, 4, 4);
|
|
375
|
+
const v = base + amp * (1 - (ey - y) / h);
|
|
376
|
+
T(lab, x, y - 9, { f: RAJ, w: 600, s: 14, ls: 2, c: DIM });
|
|
377
|
+
T(`${roll(t, t0, v.toFixed(1), seed, 4) ?? ''} ${unit}`, x + w, y - 9, { s: 16, w: 600, al: 'right', c: HOT });
|
|
378
|
+
}
|
|
379
|
+
const CND = REEL ? [['CND-01', 'QML', '70°30′S 020°00′E', 0.231], ['CND-02', 'WLK', '66°18′S 110°30′E', 0.418], ['CND-03', 'ERB', '77°32′S 167°09′E', 0.997]]
|
|
380
|
+
: [['CND-01', 'BKK', '13°45′N 100°30′E', 0.231], ['CND-02', 'URC', '43°48′N 087°36′E', 0.418], ['CND-03', 'SHA', '31°14′N 121°29′E', 0.997]];
|
|
381
|
+
function rightCol(t, S) {
|
|
382
|
+
const x = W - 96 - CW, w = CW;
|
|
383
|
+
GA = tier(t, [[0, 0.6], [1.2, 1], [2.4, 0.6], [2.9, 1], [4.6, 0.55], [6.45, 1], [7.3, 0.5]]);
|
|
384
|
+
if (panel(t, K.boot.panel_right, x, 128, w, 214, '04', 'SIGNAL ANALYSIS', '信号分析')) {
|
|
385
|
+
spark(t, x + 6, 182, w - 12, 50, 3, K.sparkline, 'SIG PWR', 'dBm', -92, 40);
|
|
386
|
+
spark(t, x + 6, 284, w - 12, 50, 8, K.sparkline + 0.2, 'COHERENCE', '%', 0, 100);
|
|
387
|
+
}
|
|
388
|
+
GA = tier(t, [[0, 0.6], [4.6, 1], [6.9, 0.55]]);
|
|
389
|
+
if (panel(t, K.boot.panel_right + 0.1, x, 392, w, 236, '05', 'CANDIDATES', '候选目标')) {
|
|
390
|
+
CND.forEach(([id, code, ll, m], i) => {
|
|
391
|
+
const t0 = K.candidates[i], yy = 434 + i * 66;
|
|
392
|
+
if (t < t0) { T(id, x + 8, yy, { s: 14, c: DIM, a: 0.7 }); T('AWAITING', x + w - 4, yy, { s: 14, c: DIM, al: 'right', a: 0.7 }); st(DEEP, 1, 1); L(x + 8, yy + 30.5, x + w - 6, yy + 30.5); return; }
|
|
393
|
+
const a = flick(t, t0), match = i === 2;
|
|
394
|
+
T(id, x + 8, yy, { s: 14, w: 600, c: match ? HOT : CYA, a });
|
|
395
|
+
T(code, x + 84, yy, { f: RAJ, w: 700, s: 16, ls: 2, a });
|
|
396
|
+
T(ll, x + 8, yy + 21, { s: 14, w: 400, c: DIM, a });
|
|
397
|
+
const mp = E.outC(prog(t, t0, 0.3)), pct = (m * 100 * mp).toFixed(1) + '%';
|
|
398
|
+
const res = t < t0 + 0.28 ? pct : match ? `MATCH ${pct}` : 'NO MATCH';
|
|
399
|
+
T(res, x + w - 4, yy, { s: 16, w: 600, al: 'right', c: match ? HOT : DIM, a: match && t < LOCK ? (blink(t, 5) ? 1 : 0.4) : a });
|
|
400
|
+
st(DEEP, 3, 1); L(x + 8, yy + 32, x + w - 6, yy + 32); st(match ? CYA : DIM, 3, 1); L(x + 8, yy + 32, x + 8 + (w - 14) * m * mp, yy + 32);
|
|
401
|
+
if (!match && t > t0 + 0.28) { st(DIM, 1, 0.9); L(x + 8, yy - 5.5, x + 118, yy - 5.5); }
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
GA = tier(t, [[0, 0.3], [4.4, 1], [5.0, 0.55], [7.4, 0.45]]); KC = true;
|
|
405
|
+
if (panel(t, K.boot.panel_right + 0.2, x, 676, w, 190, '06', 'SPECTRUM', '频谱')) {
|
|
406
|
+
const n = 28, bw = (w - 12) / n, p = E.outC(prog(t, K.boot.panel_right + 0.3, 0.6)), pkI = 18;
|
|
407
|
+
for (let i = 0; i < n; i++) {
|
|
408
|
+
const j = Math.floor(t * 15), v0 = 0.15 + 0.5 * Math.pow(hash(i, j, 3), 2) + 0.2 * Math.sin(i * 0.5 + t * 2) ** 2;
|
|
409
|
+
let v = v0 * p; if (t > K.signal_detected && Math.abs(i - pkI) < 3) v = Math.max(v, (0.95 - Math.abs(i - pkI) * 0.22) * (0.85 + 0.15 * hash(i, j)));
|
|
410
|
+
const nseg = Math.round(22 * clamp(v)), pk = i === pkI && t > K.signal_detected;
|
|
411
|
+
for (let q = 0; q < nseg; q++) { fl(pk ? HOT : q > 15 ? CYA : DIM, pk ? 0.95 : q > 15 ? 0.75 : 0.8); ctx.fillRect(x + 6 + i * bw, 846 - q * 4.4, bw - 3, 2.6); }
|
|
412
|
+
fl(DEEP, 1); ctx.fillRect(x + 6 + i * bw, 850, bw - 3, 2);
|
|
413
|
+
}
|
|
414
|
+
if (t > K.signal_detected) T('▲ 8.412 GHz', x + 6 + pkI * bw + bw / 2, 738, { s: 14, w: 600, al: 'center', c: HOT, a: flick(t, K.signal_detected) });
|
|
415
|
+
}
|
|
416
|
+
// footer: DATA LINK (keep-cyan) — label row, 4-row rolling hex dump (a new row every 6 frames), then the 9.40 UPLINK stamp
|
|
417
|
+
GA = tier(t, [[0, 0.35], [1.6, 0.6], [6.45, 0.75], [7.3, 0.6]]);
|
|
418
|
+
const fa = flick(t, K.boot.panel_right + 0.4);
|
|
419
|
+
if (fa) {
|
|
420
|
+
T('DATA LINK · QKD-7', x, 900, { f: RAJ, w: 600, s: 14, ls: 2, c: DIM, a: fa });
|
|
421
|
+
for (let k = 0; k < 12; k++) { const on = hash(k, Math.floor(t * 8)) > 0.35 || k < 5; fl(on ? CYA : DEEP, fa); ctx.fillRect(x + 176 + k * 6, 890, 4, 10); }
|
|
422
|
+
T('9.6 G', x + w, 900, { s: 14, w: 600, al: 'right', c: CYA, a: fa });
|
|
423
|
+
st(DEEP, 1, 1); L(x, 909.5, x + w, 909.5, E.outC(prog(t, K.boot.panel_right + 0.4, 0.5)));
|
|
424
|
+
const fr = F(t), row0 = Math.floor(fr / 6), HX = '0123456789ABCDEF';
|
|
425
|
+
// 8.80–9.00: the oldest row drops out and the dump slides up one row to make room for the UPLINK stamp (no collision)
|
|
426
|
+
const du = E.inOutC(prog(t, 8.84, 0.16)), d0a = 1 - prog(t, 8.8, 0.08);
|
|
427
|
+
for (let r = 0; r < 4; r++) {
|
|
428
|
+
const id = row0 - 3 + r, fresh = r === 3 && fr % 6 < 2, ry = 930 + (r - (r ? du : 0)) * 19, ra = r ? 1 : d0a;
|
|
429
|
+
if (ra <= 0) continue;
|
|
430
|
+
let line = ((0x3F20 + id * 8) & 0xFFFF).toString(16).toUpperCase().padStart(4, '0') + ' ';
|
|
431
|
+
for (let b = 0; b < 8; b++) { const v = Math.floor(hash(id, b, 11) * 256); line += ' ' + HX[v >> 4] + HX[v & 15]; }
|
|
432
|
+
T(line, x, ry, { s: 14, w: 400, ls: 0.4, c: fresh ? HOT : r === 3 ? CYA : DIM, a: ra * fa * (fresh ? 1 : 0.85) });
|
|
433
|
+
T(hash(id, 99) > 0.5 ? 'ACK' : 'ENC', x + w, ry, { s: 12, w: 500, al: 'right', c: DIM, a: ra * fa * 0.8 });
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
KC = false;
|
|
437
|
+
// UPLINK · 数据上传 (alert ink): bar fills 12 frames 9.00 → 9.40, stamps SENT ✓ / 已传送 on the 9.40 beat with a ping ring
|
|
438
|
+
if (t >= 8.9) {
|
|
439
|
+
GA = 1; const ua = flick(t, 8.9), y = 1003, bx0 = x + 128, bx1 = x + w - 92, n = 24;
|
|
440
|
+
T('UPLINK', x, y, { f: RAJ, w: 700, s: 14, ls: 2, c: HOT, a: ua });
|
|
441
|
+
T('数据上传', x + 62, y - 1, { f: HOS, w: 500, s: 12, ls: 1.5, c: CYA, a: ua });
|
|
442
|
+
const lit = Math.floor(n * prog(F(t) / 30, 9.0, 0.4) + 1e-6), sw = (bx1 - bx0) / n;
|
|
443
|
+
for (let q = 0; q < n; q++) { fl(q < lit ? (q === lit - 1 && lit < n ? HOT : CYA) : DEEP, ua); ctx.fillRect(bx0 + q * sw, y - 10, sw - 2, 10); }
|
|
444
|
+
if (t < 9.4) T(`${pad(Math.floor(100 * lit / n), 3, 0)}%`, x + w, y, { s: 14, w: 600, al: 'right', c: CYA, a: ua });
|
|
445
|
+
else {
|
|
446
|
+
const d = t - 9.4, sk = 1 + 0.35 * Math.exp(-d * 22), sx0 = x + w - 84, sy0 = y - 15;
|
|
447
|
+
ctx.save(); ctx.translate(sx0 + 42, sy0 + 10); ctx.scale(sk, sk); ctx.translate(-(sx0 + 42), -(sy0 + 10));
|
|
448
|
+
fl(HOT, 0.16 + 0.5 * Math.exp(-d * 12)); ctx.fillRect(sx0, sy0, 84, 20); st(HOT, 1.5, 1); ctx.strokeRect(sx0 + 0.5, sy0 + 0.5, 83, 19);
|
|
449
|
+
T('SENT', sx0 + 7, y, { f: RAJ, w: 700, s: 15, ls: 2, c: HOT });
|
|
450
|
+
st(HOT, 2, 1); ctx.beginPath(); ctx.moveTo(sx0 + 60, sy0 + 10); ctx.lineTo(sx0 + 65, sy0 + 15); ctx.lineTo(sx0 + 75, sy0 + 5); ctx.stroke();
|
|
451
|
+
ctx.restore();
|
|
452
|
+
T('已传送', x + w, y + 20, { f: HOS, w: 700, s: 12, ls: 2, al: 'right', c: HOT, a: clamp(d * 12) });
|
|
453
|
+
if (d < 0.6) { const u = E.outC(d / 0.6); st(HOT, 1.5, 0.9 * (1 - u)); ctx.strokeRect(sx0 - 40 * u, sy0 - 14 * u, 84 + 80 * u, 20 + 28 * u); }
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
GA = 1;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// ---------- bottom heading tape
|
|
460
|
+
function tape(t, S) {
|
|
461
|
+
const p = E.inOutC(prog(t, K.boot.panel_right, 0.6)); if (p <= 0) return;
|
|
462
|
+
const lon = S.lonC ?? 0, y = 986, half = 330 * p, ppd = 7;
|
|
463
|
+
ctx.save(); ctx.beginPath(); ctx.rect(CX - half, y - 20, half * 2, 60); ctx.clip();
|
|
464
|
+
for (let d = Math.floor(lon - 50); d <= lon + 50; d++) {
|
|
465
|
+
const xx = CX + (d - lon) * ppd, a = clamp(1 - Math.abs(xx - CX) / 330) ** 0.7;
|
|
466
|
+
const big = d % 10 === 0, mid = d % 5 === 0;
|
|
467
|
+
st(big ? CYA : DIM, 1, a); L(Math.round(xx) + 0.5, y, Math.round(xx) + 0.5, y + (big ? 14 : mid ? 9 : 5));
|
|
468
|
+
if (big) { const dd = ((d % 360) + 540) % 360 - 180; T(`${dd >= 0 ? 'E' : 'W'}${pad(Math.abs(dd), 3, 0)}`, xx, y + 30, { f: RAJ, w: 600, s: 14, ls: 1, al: 'center', c: DIM, a }); }
|
|
469
|
+
}
|
|
470
|
+
ctx.restore();
|
|
471
|
+
st(CYA, 1, 0.8 * p); L(CX - half, y - 0.5, CX + half, y - 0.5);
|
|
472
|
+
fl(HOT, p); ctx.beginPath(); ctx.moveTo(CX, y + 2); ctx.lineTo(CX - 6, y - 8); ctx.lineTo(CX + 6, y - 8); ctx.closePath(); ctx.fill();
|
|
473
|
+
const hd = ((lon % 360) + 540) % 360 - 180;
|
|
474
|
+
T(`${hd >= 0 ? 'E' : 'W'}${pad(Math.abs(hd), 3, 2)}°`, CX, y - 16, { s: 16, w: 600, al: 'center', c: HOT, a: p });
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// ---------- acquisition: candidate hops + lock brackets
|
|
478
|
+
// bearing line: dashed ray from the reticle out to the scope ring, notch on the ring + live BRG readout (triangulation)
|
|
479
|
+
function bearing(t, x, y, hs, a) {
|
|
480
|
+
const dx = x - CX, dy = y - CY, d = Math.hypot(dx, dy); if (d < 1 || d + hs + 16 > 292 * SS) return;
|
|
481
|
+
const ux = dx / d, uy = dy / d, aa = a * clamp((d - 30) / 50);
|
|
482
|
+
if (aa <= 0.01) return;
|
|
483
|
+
ctx.setLineDash([5, 5]); ctx.lineDashOffset = t * 45; st(CYA, 1, 0.6 * aa);
|
|
484
|
+
L(x + ux * (hs + 6), y + uy * (hs + 6), CX + ux * 296 * SS, CY + uy * 296 * SS); ctx.setLineDash([]);
|
|
485
|
+
st(HOT, 2.5, aa); L(CX + ux * 304 * SS, CY + uy * 304 * SS, CX + ux * 332 * SS, CY + uy * 332 * SS);
|
|
486
|
+
const brg = (Math.atan2(uy, ux) * 180 / Math.PI + 90 + 360) % 360;
|
|
487
|
+
const dg = 1 - clamp(Math.abs(((brg % 90) + 90) % 90 - 45) / 20), rl = (386 + 30 * dg) * SS; // clear the MAG/FOV/HDG/TLT diagonals
|
|
488
|
+
T(`BRG ${pad(brg, 3, 1)}°`, CX + ux * rl, CY + uy * rl + 4, { s: 15, w: 600, c: HOT, al: ux > 0.35 ? 'left' : ux < -0.35 ? 'right' : 'center', a: aa });
|
|
489
|
+
}
|
|
490
|
+
// draw vector work on the post-bloom (type) layer: crisp edges + dark knock-out halo; only in the text pass
|
|
491
|
+
function onText(fn) { if (!TXT_ON) return; const c0 = ctx; tctx.setTransform(c0.getTransform()); tctx.filter = 'none'; ctx = tctx; try { fn(); } finally { ctx = c0; } }
|
|
492
|
+
function acquire(t, S) {
|
|
493
|
+
if (t < 4.62) return;
|
|
494
|
+
const pts = [{ x: CX, y: CY }, S.cnd[0], S.cnd[1], S.target], times = [4.62, ...K.candidates];
|
|
495
|
+
// search reticle: hops between candidates (arrives exactly on each cue)
|
|
496
|
+
if (t < K.candidates[2]) {
|
|
497
|
+
let k = 0; while (k < 3 && t >= times[k + 1]) k++;
|
|
498
|
+
const hopD = 0.2, ta = times[k + 1];
|
|
499
|
+
let x, y;
|
|
500
|
+
if (k >= 3) { x = pts[3].x; y = pts[3].y; } else {
|
|
501
|
+
const u = E.inOutQ(clamp((t - (ta - hopD)) / hopD)), from = pts[k], to = pts[k + 1];
|
|
502
|
+
x = lerp(from.x, to.x, u); y = lerp(from.y, to.y, u);
|
|
503
|
+
}
|
|
504
|
+
if (k > 0) { x = pts[k].x; y = pts[k].y; }
|
|
505
|
+
const arrive = k > 0 ? times[k] : -9, pulse = 1 + 0.5 * Math.exp(-(t - arrive) * 14);
|
|
506
|
+
const moving = k < 3 && t > times[k + 1] - 0.2;
|
|
507
|
+
if (moving) { const u = E.inOutQ(clamp((t - (times[k + 1] - 0.2)) / 0.2)); x = lerp(pts[k].x, pts[k + 1].x, u); y = lerp(pts[k].y, pts[k + 1].y, u); }
|
|
508
|
+
const hs = 22 * (moving ? 1.25 : pulse);
|
|
509
|
+
brackets(x, y, hs, 7, 1.5, 0.95, CYA);
|
|
510
|
+
bearing(t, x, y, hs, clamp((t - 4.62) * 6));
|
|
511
|
+
if (moving) { const u = E.inOutQ(clamp((t - (times[k + 1] - 0.2)) / 0.2)), fr = pts[k]; st(HOT, 1.5, 0.7 * (1 - u * 0.5)); L(lerp(fr.x, x, 0.35 * u), lerp(fr.y, y, 0.35 * u), x, y); }
|
|
512
|
+
st(CYA, 1, 0.5); L(x - hs - 10, y + 0.5, x - hs - 3, y + 0.5); L(x + hs + 3, y + 0.5, x + hs + 10, y + 0.5);
|
|
513
|
+
if (k > 0 && !moving) {
|
|
514
|
+
const [id, code, ll] = CND[k - 1], dt = t - arrive;
|
|
515
|
+
T(`${id} · ${code}`, x + hs + 14, y - 6, { f: RAJ, w: 700, s: 16, ls: 2, a: flick(t, arrive) });
|
|
516
|
+
T(dt < 0.25 ? scramble(t, arrive, 'ANALYSING', 50 + k, 7) : 'NO MATCH', x + hs + 14, y + 12, { s: 14, w: 600, c: dt < 0.25 ? CYA : DIM });
|
|
517
|
+
if (dt > 0.25) { st(DIM, 1.5, 1); L(x - 8, y - 8, x + 8, y + 8); L(x + 8, y - 8, x - 8, y + 8); }
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
// previous candidates keep a faint X marker
|
|
521
|
+
for (let i = 0; i < 2; i++) if (t > K.candidates[i] + 0.25) { const c = S.cnd[i]; if (c.face > 0.05) { st(DIM, 1, 0.6 * clamp(1 - (t - K.candidates[i] - 0.8) * 2)); L(c.x - 5, c.y - 5, c.x + 5, c.y + 5); L(c.x + 5, c.y - 5, c.x - 5, c.y + 5); } }
|
|
522
|
+
// ---- HERO lock: brackets fly in from the four frame corners, hunt with ±16 px jitter, snap to a 260 px box
|
|
523
|
+
// (expo-out, 1-frame overshoot to 0.96, settled 6.333 → two clean frames with LOCK 100 %), then bite onto the target on the impact
|
|
524
|
+
if (t >= K.brackets_in) {
|
|
525
|
+
const tg = S.target, tb = K.brackets_in, sn = K.snap, stl = K.settle;
|
|
526
|
+
const pulseK = t > LOCK ? Math.max(...K.alert_pulses.map((p) => (t >= p ? Math.exp(-(t - p) * 10) : 0))) : 0;
|
|
527
|
+
let hx, hy, jit = 0;
|
|
528
|
+
const TRV = tb + 8 / 30; // 8-frame travel 5.90 → 6.167 (outCubic), then 2 frames of stepped hunting before the 6.233 snap
|
|
529
|
+
const BST = (tt) => { const u = E.outC(prog(tt, tb, TRV - tb)); let bx = lerp(872, 176, u), by = lerp(476, 176, u);
|
|
530
|
+
if (tt >= TRV) bx = by = ((F(tt) >> 1) & 1) ? 176 : 204; return { hx: bx, hy: by, jit: 18 }; };
|
|
531
|
+
if (t < sn) { const b = BST(t); hx = b.hx; hy = b.hy; jit = b.jit; }
|
|
532
|
+
else if (t < LOCK) {
|
|
533
|
+
const fr = (t - sn) * 30, KF = [1.35, 1.08, 0.96, 1.0];
|
|
534
|
+
const k = fr >= 3 ? 1.0 : lerp(KF[Math.floor(fr)], KF[Math.floor(fr) + 1], E.outC(fr - Math.floor(fr)));
|
|
535
|
+
hx = hy = 130 * k;
|
|
536
|
+
} else { const k = E.outE(prog(t, LOCK, 0.2)); hx = hy = lerp(130, 72, k) * (1 + 0.05 * pulseK); }
|
|
537
|
+
const a = t < LOCK ? Math.max(flick(t, tb), 0.8) : 1, jf = F(t) >> 1, arm = clamp(0.3 * Math.min(hx, hy), 22, 64);
|
|
538
|
+
const ofs = (q) => [(hash(jf, q, 1) - 0.5) * 2 * jit, (hash(jf, q, 2) - 0.5) * 2 * jit];
|
|
539
|
+
const drawB = (bhx, bhy, bjit, bjf, ba, echo) => {
|
|
540
|
+
const barm = clamp(0.3 * Math.min(bhx, bhy), 22, 64); ctx.lineCap = 'square';
|
|
541
|
+
[[-1, -1], [1, -1], [1, 1], [-1, 1]].forEach(([sx, sy], q) => {
|
|
542
|
+
const bx = tg.x + sx * bhx + (hash(bjf, q, 1) - 0.5) * 2 * bjit, by = tg.y + sy * bhy + (hash(bjf, q, 2) - 0.5) * 2 * bjit;
|
|
543
|
+
const path = () => { ctx.beginPath(); ctx.moveTo(bx, by - sy * barm); ctx.lineTo(bx, by); ctx.lineTo(bx - sx * barm, by); ctx.stroke(); };
|
|
544
|
+
if (echo) { st(CYA, 4, ba); path(); } else { st(CYA, 6, ba); path(); st(HOT, 2, ba); path(); }
|
|
545
|
+
});
|
|
546
|
+
ctx.lineCap = 'butt';
|
|
547
|
+
};
|
|
548
|
+
if (t < sn) for (const [e, ea] of [[3, 0.12], [2, 0.25], [1, 0.45]]) { const te = t - e * 2 / 30; if (te < tb) continue; const b = BST(te); drawB(b.hx, b.hy, b.jit, F(te) >> 1, ea * a, true); }
|
|
549
|
+
if (t >= LOCK) onText(() => drawB(hx, hy, jit, jf, a, false)); else drawB(hx, hy, jit, jf, a, false);
|
|
550
|
+
if (t < LOCK) bearing(t, tg.x, tg.y, 34, flick(t, tb) * (1 - sstep(6.1, 6.3, t)));
|
|
551
|
+
if (t >= LOCK) {
|
|
552
|
+
const k = E.outC(prog(t, LOCK, 0.4)), rr = 30 + 6 * pulseK, spin = (t - LOCK) * 1.4;
|
|
553
|
+
if (!NOLINK.has('reticle')) onText(() => { st(HOT, 1.5, k); for (let q = 0; q < 4; q++) ARC(tg.x, tg.y, rr, spin + q * Math.PI / 2 + 0.25, spin + q * Math.PI / 2 + Math.PI / 2 - 0.25, k);
|
|
554
|
+
fl(HOT, k); ctx.beginPath(); ctx.moveTo(tg.x, tg.y - 5); ctx.lineTo(tg.x + 5, tg.y); ctx.lineTo(tg.x, tg.y + 5); ctx.lineTo(tg.x - 5, tg.y); ctx.closePath(); ctx.fill(); });
|
|
555
|
+
{ const d = t - LOCK; if (d < 0.25) { const u = E.outC(d / 0.25); st(HOT, 2, 0.9 * (1 - u)); ctx.strokeRect(tg.x - 150 - 70 * u, tg.y - 150 - 70 * u, 300 + 140 * u, 300 + 140 * u); } }
|
|
556
|
+
for (const p0 of [LOCK, ...K.alert_pulses]) { const d = t - p0; if (d < 0 || d > 0.7) continue; const u = E.outC(d / 0.7); st(CYA, 1.5, (1 - u) * 0.9); ARC(tg.x, tg.y, 70 + u * 200, 0, Math.PI * 2); }
|
|
557
|
+
}
|
|
558
|
+
if (t < LOCK + 0.07) {
|
|
559
|
+
// lock-progress ring (inside the box, subordinate to the brackets) fills with an accelerating ease → full on the settle
|
|
560
|
+
const fillU = E.inQ(prog(t, tb, stl - tb)), nLit = Math.floor(36 * fillU + 1e-6), rp = 92;
|
|
561
|
+
if (t < LOCK && !NOLINK.has('reticle')) for (let q = 0; q < 36; q++) {
|
|
562
|
+
const a0 = -Math.PI / 2 + q * Math.PI / 18, lit = q < nLit, hot = q === nLit - 1 && nLit < 36;
|
|
563
|
+
st(hot ? HOT : lit ? CYA : DIM, lit ? 3 : 1.5, (lit ? 0.8 : 0.5) * a); ARC(tg.x, tg.y, rp, a0 + 0.03, a0 + Math.PI / 18 - 0.05);
|
|
564
|
+
}
|
|
565
|
+
// corner tags ride the brackets, each on an ink plate
|
|
566
|
+
// after the impact the tags stay pinned to the 260 px box (the brackets shrink away from them) and fade in 2 frames
|
|
567
|
+
const la = a * sstep(300, 230, hx) * (t < LOCK ? 1 : 1 - prog(t, LOCK + 1 / 30, 0.04)), [j0x, j0y] = ofs(0), [j1x, j1y] = ofs(1), [j2x, j2y] = ofs(2), [j3x, j3y] = ofs(3);
|
|
568
|
+
const hxT = Math.max(hx, 130), hyT = Math.max(hy, 130);
|
|
569
|
+
if (la > 0.01) {
|
|
570
|
+
const tag = (s, x, y, o) => { const w = TW(s, o), x0 = o.al === 'right' ? x - w : x; fl(INK, 0.75 * la); ctx.fillRect(x0 - 6, y - o.s + 1, w + 12, o.s + 7); T(s, x, y, { ...o, a: la }); };
|
|
571
|
+
const pct = String(Math.min(100, Math.floor(fillU * 100 + 1e-6))).padStart(3, '0');
|
|
572
|
+
tag('TGT-03', tg.x - hxT + j0x, tg.y - hyT + j0y - 14, { f: RAJ, w: 700, s: 17, ls: 2 });
|
|
573
|
+
tag(`LOCK ${pct}%`, tg.x + hxT + j1x, tg.y - hyT + j1y - 14, { s: 17, w: 600, al: 'right', c: fillU >= 1 ? HOT : CYA });
|
|
574
|
+
if (t < LOCK) tag(`T−${Math.max(0, LOCK - t).toFixed(2)} S`, tg.x - hxT + j3x, tg.y + hyT + j3y + 27, { s: 16, w: 500, c: CYA });
|
|
575
|
+
tag(`MATCH ${roll(t, tb, '99.7', 91, 6) ?? ''}%`, tg.x + hxT + j2x, tg.y + hyT + j2y + 27, { s: 16, w: 600, al: 'right', c: HOT });
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// ---------- lock banner: rules + titles slam clean on the impact; the dark strip only opens after the dive lands (7.10)
|
|
582
|
+
function banner(t, S) {
|
|
583
|
+
if (t < LOCK) return;
|
|
584
|
+
const w = E.outE(prog(t, LOCK, 0.3)) * 470, gap = 96, hh = 66;
|
|
585
|
+
const pulse = Math.max(...[LOCK, ...K.alert_pulses].map((p) => (t >= p ? Math.exp(-(t - p) * 7) : 0)));
|
|
586
|
+
const wp = E.outE(prog(t, K.band_open, 0.32)) * 470;
|
|
587
|
+
if (wp > gap) {
|
|
588
|
+
fl(INK, 0.74); ctx.fillRect(CX - wp, CY - hh, wp - gap, hh * 2); ctx.fillRect(CX + gap, CY - hh, wp - gap, hh * 2);
|
|
589
|
+
knock(CX - wp, CY - hh, wp - gap, hh * 2, 0.9); knock(CX + gap, CY - hh, wp - gap, hh * 2, 0.9);
|
|
590
|
+
ctx.save(); ctx.beginPath(); ctx.rect(CX - wp, CY - hh, 34, hh * 2); ctx.rect(CX + wp - 34, CY - hh, 34, hh * 2); ctx.clip();
|
|
591
|
+
st(CYA, 5, 0.35 + 0.4 * pulse); for (let k = -10; k < 20; k++) { const xx = CX - wp + k * 14; L(xx, CY + hh, xx + hh * 2, CY - hh); const x2 = CX + wp - 34 + k * 14 - 150; L(x2, CY + hh, x2 + hh * 2, CY - hh); }
|
|
592
|
+
ctx.restore();
|
|
593
|
+
}
|
|
594
|
+
st(CYA, 1.5, 0.9 + 0.1 * pulse); L(CX - w, CY - hh + 0.5, CX + w, CY - hh + 0.5); L(CX - w, CY + hh - 0.5, CX + w, CY + hh - 0.5);
|
|
595
|
+
st(CYA, 4, 1); L(CX - w, CY - hh - 3, CX - w + 60, CY - hh - 3, w / 470); L(CX + w, CY + hh + 3, CX + w - 60, CY + hh + 3, w / 470);
|
|
596
|
+
// titles: LOCKED + 目标锁定 land clean on the impact frame (scale 1.15 → 1 over 3 frames, first frame white-hot); only the small lines scramble
|
|
597
|
+
const k = E.outE(prog(t, LOCK, 0.1)), sc = lerp(1.15, 1, k), c1 = F(t) === F(LOCK) ? HOT : CYA;
|
|
598
|
+
ctx.save(); ctx.translate(CX, CY); ctx.scale(sc, sc); ctx.translate(-CX, -CY);
|
|
599
|
+
T(scramble(t, LOCK, REEL ? 'SIGNAL' : 'TARGET', 404, 6), CX + gap + 30, CY - 34, { f: RAJ, w: 700, s: 17, ls: 9, c: c1 });
|
|
600
|
+
T('LOCKED', CX + gap + 26, CY + 30, { f: CHK, w: 600, s: 60, ls: 5, c: c1 });
|
|
601
|
+
T(scramble(t, LOCK, '警报 · ALERT 03', 405, 6), CX - gap - 30, CY - 34, { f: HOS, w: 500, s: 15, ls: 3, al: 'right', c: c1 });
|
|
602
|
+
T(REEL ? '信号锁定' : '目标锁定', CX - gap - 28, CY + 30, { f: HOS, w: 900, s: 58, ls: 7, al: 'right', c: c1 });
|
|
603
|
+
ctx.restore();
|
|
604
|
+
// coordinates (typewriter) under the band
|
|
605
|
+
const coord = REEL ? 'S77°32′ E167°09′' : 'N31°14′ E121°29′', cw = TW(coord, { s: 26, w: 500, ls: 2 });
|
|
606
|
+
const ty = typed(t, K.coords, coord, 34);
|
|
607
|
+
if (ty) {
|
|
608
|
+
const pk = E.outE(prog(t, K.coords - 0.08, 0.24)), pw = 236 * pk, py0 = CY + hh + 12, py1 = CY + hh + 102, ch = 12;
|
|
609
|
+
if (pw > 2) {
|
|
610
|
+
ctx.beginPath(); ctx.moveTo(CX - pw + ch, py0); ctx.lineTo(CX + pw - ch, py0); ctx.lineTo(CX + pw, py0 + ch); ctx.lineTo(CX + pw, py1);
|
|
611
|
+
ctx.lineTo(CX - pw, py1); ctx.lineTo(CX - pw, py0 + ch); ctx.closePath(); fl(INK, 0.86); ctx.fill();
|
|
612
|
+
st(DIM, 1, 0.9); ctx.stroke(); knock(CX - pw, py0, pw * 2, py1 - py0, 0.95);
|
|
613
|
+
st(CYA, 2, 1); L(CX - pw, py1 + 3, CX - pw + 28, py1 + 3); L(CX + pw, py1 + 3, CX + pw - 28, py1 + 3);
|
|
614
|
+
st(CYA, 1, 0.5); L(CX - 1.5, py0, CX - 1.5, py0 - 12 * pk); L(CX + 1.5, py0, CX + 1.5, py0 - 12 * pk);
|
|
615
|
+
}
|
|
616
|
+
T('COORD · 坐标', CX, CY + hh + 31, { f: HOS, w: 500, s: 14, ls: 3, al: 'center', c: DIM });
|
|
617
|
+
T(ty, CX - cw / 2, CY + hh + 64, { s: 26, w: 500, ls: 2, c: HOT });
|
|
618
|
+
if (blink(t, 2.5) || ty.length < coord.length) T('█', CX - cw / 2 + TW(ty, { s: 26, w: 500, ls: 2 }) + 6, CY + hh + 63, { s: 22, c: CYA });
|
|
619
|
+
const r2 = typed(t, K.coords + 0.5, `RNG ${(1648.3 - (t - LOCK) * 23.7).toFixed(1)} KM · CONF 99.7 % · TRK 03`, 60);
|
|
620
|
+
T(r2, CX, CY + hh + 91, { s: 14, al: 'center', c: DIM });
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// ---------- DATA LIVE act: main-sweep returns on the globe (become CND-01/02/03) + KESTREL-9 ground track (3.40)
|
|
625
|
+
function sweepBlips(t, S) {
|
|
626
|
+
if (t < K.sweep[0] || t > 4.9) return;
|
|
627
|
+
const brg = K.radar_theta0_deg + 180 * (t - K.radar_start), fade = 1 - sstep(4.45, 4.85, t);
|
|
628
|
+
[S.cnd[0], S.cnd[1], S.target].forEach((c, i) => {
|
|
629
|
+
const vis = sstep(0.03, 0.16, c.face) * fade; if (vis <= 0.01) return;
|
|
630
|
+
const bb = (Math.atan2(c.y - CY, c.x - CX) / DEG + 90 + 360) % 360;
|
|
631
|
+
const first = K.radar_start + (((bb - K.radar_theta0_deg) % 360) + 360) % 360 / 180; if (t < first) return;
|
|
632
|
+
const since = (((brg - bb) % 360) + 360) % 360 / 180, I = Math.exp(-since * 2.2), a = vis * (0.35 + 0.65 * I);
|
|
633
|
+
fl(HOT, a); ctx.fillRect(c.x - 3, c.y - 3, 6, 6);
|
|
634
|
+
st(CYA, 1.5, vis * I); ARC(c.x, c.y, 7 + (1 - I) * 16, 0, Math.PI * 2);
|
|
635
|
+
st(CYA, 1, 0.7 * a); L(c.x + 6, c.y - 6, c.x + 16, c.y - 16); L(c.x + 16, c.y - 16, c.x + 30, c.y - 16);
|
|
636
|
+
const s = scramble(t, first, `UNK-0${i + 1}`, 120 + i, 5);
|
|
637
|
+
const tw = TW(s, { f: RAJ, w: 700, s: 15, ls: 2 }); fl(INK, 0.75 * vis); ctx.fillRect(c.x + 30, c.y - 31, tw + 10, 21);
|
|
638
|
+
T(s, c.x + 35, c.y - 15, { f: RAJ, w: 700, s: 15, ls: 2, c: I > 0.5 ? HOT : CYA, a: vis });
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
function groundTrack(t, S) {
|
|
642
|
+
const s = S.sat; if (!s || t < K.ground_track) return;
|
|
643
|
+
const vis = 1 - sstep(5.5, 5.9, t); if (vis <= 0) return;
|
|
644
|
+
const p = E.outC(prog(t, K.ground_track, 0.6)), fa = (q) => sstep(0.0, 0.2, q.face);
|
|
645
|
+
// track: past solid, future dashed, drawn outward from the satellite in both directions
|
|
646
|
+
for (const fut of [false, true]) {
|
|
647
|
+
ctx.setLineDash(fut ? [7, 7] : []); ctx.lineDashOffset = -t * 30;
|
|
648
|
+
for (let i = 1; i < s.trk.length; i++) {
|
|
649
|
+
const a0 = s.trk[i - 1], a1 = s.trk[i]; if ((a1.a > 0) !== fut) continue;
|
|
650
|
+
const lim = fut ? 0.9 : 1.7; if (Math.abs(a1.a) > lim * p) continue;
|
|
651
|
+
const al = Math.min(fa(a0), fa(a1)) * vis; if (al <= 0.01) continue;
|
|
652
|
+
st(fut ? CYA : HOT, fut ? 1.2 : 1.6, al * (fut ? 0.6 : 0.8)); L(a0.x, a0.y, a1.x, a1.y);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
ctx.setLineDash([]);
|
|
656
|
+
const sv = sstep(0.0, 0.2, s.face) * vis * sstep(K.ground_track, K.ground_track + 0.12, t); if (sv <= 0.01) return;
|
|
657
|
+
// footprint + sensor cone
|
|
658
|
+
ctx.beginPath(); s.fp.forEach((q, i) => (i ? ctx.lineTo(q.x, q.y) : ctx.moveTo(q.x, q.y))); ctx.closePath();
|
|
659
|
+
fl(CYA, 0.08 * sv); ctx.fill(); st(CYA, 1.2, 0.75 * sv); ctx.stroke();
|
|
660
|
+
st(CYA, 1, 0.45 * sv); L(s.x, s.y, s.fp[8].x, s.fp[8].y); L(s.x, s.y, s.fp[24].x, s.fp[24].y);
|
|
661
|
+
ctx.setLineDash([2, 4]); st(HOT, 1, 0.6 * sv); L(s.x, s.y, s.nad.x, s.nad.y); ctx.setLineDash([]);
|
|
662
|
+
// satellite glyph: body + panels, oriented along track
|
|
663
|
+
const i0 = s.trk.findIndex((q) => q.a >= 0), q0 = s.trk[Math.max(0, i0 - 1)], q1 = s.trk[Math.min(s.trk.length - 1, i0 + 1)];
|
|
664
|
+
const an = Math.atan2(q1.y - q0.y, q1.x - q0.x);
|
|
665
|
+
ctx.save(); ctx.translate(s.x, s.y); ctx.rotate(an);
|
|
666
|
+
fl(HOT, sv); ctx.fillRect(-4, -4, 8, 8); st(CYA, 2, sv); L(0, -6, 0, -18); L(0, 6, 0, 18); st(CYA, 5, sv); L(-3, -13, 3, -13); L(-3, 13, 3, 13);
|
|
667
|
+
ctx.restore();
|
|
668
|
+
const pg = t - K.ground_track; if (pg < 0.5) { st(HOT, 1.5, (1 - pg / 0.5) * sv); ARC(s.x, s.y, 10 + 60 * E.outC(pg / 0.5), 0, Math.PI * 2); }
|
|
669
|
+
const lx = s.x + 24, ly = s.y - 30, nm = scramble(t, K.ground_track, 'KESTREL-9', 131, 6);
|
|
670
|
+
st(CYA, 1, 0.8 * sv); L(s.x + 8, s.y - 8, lx - 4, ly + 6); L(lx - 4, ly + 6, lx + 140, ly + 6);
|
|
671
|
+
fl(INK, 0.75 * sv); ctx.fillRect(lx - 2, ly - 16, 146, 42);
|
|
672
|
+
T(nm, lx + 2, ly, { f: RAJ, w: 700, s: 17, ls: 2, c: HOT, a: sv });
|
|
673
|
+
T('隼眼-9 · 512 KM', lx + 2, ly + 22, { f: HOS, w: 500, s: 14, ls: 1, c: CYA, a: sv * flick(t, K.ground_track + 0.1) });
|
|
674
|
+
}
|
|
675
|
+
// dive: radial speed streaks rush past inside the scope while the camera falls (strongest at the kick)
|
|
676
|
+
function diveStreaks(t) {
|
|
677
|
+
const d = t - LOCK; if (d < 0.03 || d > 1.2) return;
|
|
678
|
+
const sp = (1 - d / 1.2) ** 2;
|
|
679
|
+
for (let i = 0; i < 44; i++) {
|
|
680
|
+
const u = (hash(i, 1) + d * (1.3 + 0.9 * hash(i, 2))) % 1, an = hash(i, 3) * Math.PI * 2;
|
|
681
|
+
const r = SS * (40 + 250 * u ** 1.5), len = SS * (14 + 90 * u) * sp, a = sp * Math.sin(Math.PI * u) * 0.8;
|
|
682
|
+
if (Math.abs(Math.sin(an)) * r < 70 && t > K.band_open) continue;
|
|
683
|
+
if (V5 && Math.abs(Math.sin(an)) * r < BH + 12) continue;
|
|
684
|
+
st(hash(i, 4) > 0.7 ? HOT : CYA, 1.4, a);
|
|
685
|
+
L(CX + Math.cos(an) * r, CY + Math.sin(an) * r, CX + Math.cos(an) * (r + len), CY + Math.sin(an) * (r + len));
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// ---------- post-dive map intelligence: city markers + sea tracks projected from the hologram
|
|
690
|
+
function clearOf(x, y) { // signed clearance from the band, the coord plate and the scope edge → 0..1 visibility
|
|
691
|
+
const dx = x - CX, dy = y - CY;
|
|
692
|
+
const dBand = V5 ? Math.max(Math.abs(dy) - BH - 18, Math.abs(dx) - BW - 10) : Math.max(Math.abs(dy) - 84, Math.abs(dx) - 480), dPlate = V5 ? Math.max(BH - dy, dy - BH - 114, Math.abs(dx) - 248) : Math.max(66 - dy, dy - 180, Math.abs(dx) - 248), dScope = 292 * SS - Math.hypot(dx, dy);
|
|
693
|
+
return Math.min(dBand, dPlate, dScope) > 10 ? 1 : 0;
|
|
694
|
+
}
|
|
695
|
+
function mapIntel(t, S) {
|
|
696
|
+
if (t < 7.05 || !S.cities) return;
|
|
697
|
+
S.cities.forEach((c, n) => {
|
|
698
|
+
const t0 = 7.1 + 0.12 * n, a = flick(t, t0) * clearOf(c.x, c.y) * sstep(0.1, 0.3, c.face);
|
|
699
|
+
if (a <= 0.01) return;
|
|
700
|
+
const k = E.outE(prog(t, t0, 0.3)), sx = c.x < CX ? -1 : 1, sy = c.y < CY ? -1 : 1, x = c.x, y = c.y;
|
|
701
|
+
st(HOT, 1.25, a); ctx.strokeRect(x - 3.5, y - 3.5, 7, 7); fl(HOT, a); ctx.fillRect(x - 1, y - 1, 2, 2);
|
|
702
|
+
st(CYA, 1, 0.8 * a); const ex = x + sx * 14 * k, ey = y + sy * 14 * k; L(x + sx * 4, y + sy * 4, ex, ey); L(ex, ey, ex + sx * 40 * k, ey);
|
|
703
|
+
if (k > 0.6) {
|
|
704
|
+
const al = sx < 0 ? 'right' : 'left', lx = ex + sx * 4, iw = TW(c.id, { f: RAJ, w: 700, s: 15, ls: 2 }) + 8;
|
|
705
|
+
const cw2 = TW(c.cn, { f: HOS, w: 500, s: 14 }), pw = iw + cw2 + 10;
|
|
706
|
+
fl(INK, 0.75 * a); ctx.fillRect(sx < 0 ? lx - pw + 4 : lx - 5, ey - 21, pw, 39);
|
|
707
|
+
T(c.id, lx, ey - 5, { f: RAJ, w: 700, s: 15, ls: 2, al, a });
|
|
708
|
+
T(c.cn, lx + sx * iw, ey - 5, { f: HOS, w: 500, s: 14, al, a, c: HOT });
|
|
709
|
+
T(`${roll(t, t0 + 0.1, String(CITY_KM[c.id] ?? 0).padStart(3, '0'), 70 + n, 5) ?? ''} KM`, lx, ey + 13, { s: 14, w: 500, al, c: CYA, a: a * 0.8 });
|
|
710
|
+
}
|
|
711
|
+
});
|
|
712
|
+
S.tracks.forEach((c, n) => {
|
|
713
|
+
const t0 = 7.35 + 0.1 * n, a = flick(t, t0) * clearOf(c.x, c.y) * sstep(0.1, 0.3, c.face);
|
|
714
|
+
if (a <= 0.01) return;
|
|
715
|
+
const hx = c.x - c.bx, hy = c.y - c.by, hl = Math.hypot(hx, hy) || 1, ux = hx / hl, uy = hy / hl;
|
|
716
|
+
// wake: fading dots behind, then chevron pointing along heading
|
|
717
|
+
for (let i = 1; i <= 5; i++) { fl(CYA, a * (0.7 - i * 0.12)); ctx.fillRect(c.x - ux * i * 7 - 1, c.y - uy * i * 7 - 1, 2, 2); }
|
|
718
|
+
st(HOT, 1.5, a); ctx.beginPath(); ctx.moveTo(c.x + ux * 7, c.y + uy * 7); ctx.lineTo(c.x - ux * 4 - uy * 5, c.y - uy * 4 + ux * 5);
|
|
719
|
+
ctx.lineTo(c.x - ux * 1.5, c.y - uy * 1.5); ctx.lineTo(c.x - ux * 4 + uy * 5, c.y - uy * 4 - ux * 5); ctx.closePath(); ctx.stroke();
|
|
720
|
+
st(CYA, 1, 0.45 * a); L(c.x + ux * 10, c.y + uy * 10, c.x + ux * 30, c.y + uy * 30); // velocity vector
|
|
721
|
+
fl(INK, 0.75 * a); ctx.fillRect(c.x + 8, c.y - 26, 64, 34);
|
|
722
|
+
T(c.id, c.x + 12, c.y - 11, { f: RAJ, w: 700, s: 14, ls: 1.5, a: a * 0.95 });
|
|
723
|
+
T(`${String(c.kn).padStart(2, '0')} KN`, c.x + 12, c.y + 4, { s: 13, w: 500, c: CYA, a: a * 0.8 });
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
// ?reel=1: the signal source is the showreel's coral dot (the reel's protagonist); it later flies out to become the decimal of "5.5"
|
|
728
|
+
function beacon(t, S) {
|
|
729
|
+
const tg = S.target, a = clamp((t - 5.8) / 0.25), lk = t >= LOCK ? Math.exp(-(t - LOCK) / 0.12) : 0;
|
|
730
|
+
const r = 13 * (1 + 0.6 * lk) * (1 + 0.08 * Math.sin((t - LOCK) * Math.PI * 4));
|
|
731
|
+
ctx.save(); ctx.globalAlpha = a;
|
|
732
|
+
const g = ctx.createRadialGradient(tg.x, tg.y, r * 0.5, tg.x, tg.y, r * 5); g.addColorStop(0, 'rgba(255,90,78,0.55)'); g.addColorStop(1, 'rgba(255,90,78,0)');
|
|
733
|
+
ctx.fillStyle = g; ctx.fillRect(tg.x - r * 5, tg.y - r * 5, r * 10, r * 10);
|
|
734
|
+
for (let k = 0; k < 2; k++) { const ph = ((t - 5.8) * 2 + k * 0.5) % 1; ctx.strokeStyle = `rgba(255,120,100,${0.6 * (1 - ph)})`; ctx.lineWidth = 2;
|
|
735
|
+
ctx.beginPath(); ctx.arc(tg.x, tg.y, r + ph * 46, 0, Math.PI * 2); ctx.stroke(); }
|
|
736
|
+
ctx.fillStyle = '#FF5A4E'; ctx.beginPath(); ctx.arc(tg.x, tg.y, r, 0, Math.PI * 2); ctx.fill();
|
|
737
|
+
ctx.restore();
|
|
738
|
+
}
|
|
739
|
+
// ---------- reel v5 (?v5=1): taller/wider lock band that carries the style-name title (drawn by v5.js on its own layer)
|
|
740
|
+
function bannerV5(t, S) {
|
|
741
|
+
if (t < LOCK) return;
|
|
742
|
+
const w = E.outE(prog(t, LOCK, 0.3)) * BW, hh = BH, gp = BG - 22;
|
|
743
|
+
const pulse = Math.max(...[LOCK, ...K.alert_pulses].map((p) => (t >= p ? Math.exp(-(t - p) * 7) : 0)));
|
|
744
|
+
const wp = plateW(t);
|
|
745
|
+
if (wp > gp) { // ink plate (centre gap keeps the reticle clear); dimmer during the dive so the coast still rushes through
|
|
746
|
+
const pa = t < K.band_open ? 0.62 : 0.8;
|
|
747
|
+
fl(INK, pa); ctx.fillRect(CX - wp, CY - hh, wp - gp, hh * 2); ctx.fillRect(CX + gp, CY - hh, wp - gp, hh * 2);
|
|
748
|
+
knock(CX - wp, CY - hh, wp - gp, hh * 2, 0.92); knock(CX + gp, CY - hh, wp - gp, hh * 2, 0.92);
|
|
749
|
+
}
|
|
750
|
+
st(CYA, 1.5, 0.9 + 0.1 * pulse); L(CX - w, CY - hh + 0.5, CX + w, CY - hh + 0.5); L(CX - w, CY + hh - 0.5, CX + w, CY + hh - 0.5);
|
|
751
|
+
st(CYA, 4, 1); L(CX - w, CY - hh - 3, CX - w + 80, CY - hh - 3, w / BW); L(CX + w, CY + hh + 3, CX + w - 80, CY + hh + 3, w / BW);
|
|
752
|
+
// data ticks under the bottom rule, running out from the centre with the rules
|
|
753
|
+
for (let d = 10; d <= w; d += 10) { const big = d % 50 === 0; st(big ? CYA : DIM, 1, big ? 0.9 : 0.6); for (const sx of [-1, 1]) L(CX + sx * d + 0.5, CY + hh, CX + sx * d + 0.5, CY + hh + (big ? 9 : 5)); }
|
|
754
|
+
// the lock beat survives as small captions over the title words (信号锁定 / SIGNAL LOCKED)
|
|
755
|
+
const c1 = F(t) === F(LOCK) ? HOT : CYA, cy = titleMetrics().top - 20;
|
|
756
|
+
T(scramble(t, LOCK, 'SIGNAL LOCKED', 404, 6), CX + BG + 2, cy, { f: RAJ, w: 700, s: 28, ls: 8, c: c1 });
|
|
757
|
+
T(scramble(t, LOCK, '信号锁定 · ALERT 03', 405, 6), CX - BG - 2, cy, { f: HOS, w: 500, s: 28, ls: 4, al: 'right', c: c1 });
|
|
758
|
+
// data ticks right of HUD: readouts flicker/roll in after the impact (balances the lockup against the four CJK glyphs)
|
|
759
|
+
{ const m = titleMetrics(), x0 = m.r + 44, x1 = CX + BW - 52;
|
|
760
|
+
[['TRK', 'SIG-03', 0.1], ['LOCK', '100%', 0.16], ['MATCH', '99.7%', 0.22], ['CONF', 'A+', 0.28]].forEach(([lab, v, dt], i) => {
|
|
761
|
+
const t0 = LOCK + dt, a = flick(t, t0); if (!a) return; const yy = Math.round(m.top) + 24 + i * 40;
|
|
762
|
+
T(lab, x0, yy, { f: RAJ, w: 600, s: 20, ls: 3, c: DIM, a });
|
|
763
|
+
T(roll(t, t0, v, 140 + i, 5) ?? '', x1, yy, { s: 24, w: 600, al: 'right', c: i === 1 ? HOT : CYA, a });
|
|
764
|
+
st(DEEP, 1, a); L(x0, yy + 11.5, x1, yy + 11.5); st(CYA, 2, a); L(x0, yy + 11, x0 + (x1 - x0) * E.outC(prog(t, t0, 0.3)) * [0.4, 1, 0.997, 0.8][i], yy + 11);
|
|
765
|
+
}); }
|
|
766
|
+
// coordinates (typewriter) under the band — as v4, moved down with the band
|
|
767
|
+
const coord = 'S77°32′ E167°09′', cw = TW(coord, { s: 26, w: 500, ls: 2 });
|
|
768
|
+
const ty = typed(t, K.coords, coord, 34);
|
|
769
|
+
if (ty) {
|
|
770
|
+
const pk = E.outE(prog(t, K.coords - 0.08, 0.24)), pw = 236 * pk, py0 = CY + hh + 16, py1 = CY + hh + 106, ch = 12;
|
|
771
|
+
if (pw > 2) {
|
|
772
|
+
ctx.beginPath(); ctx.moveTo(CX - pw + ch, py0); ctx.lineTo(CX + pw - ch, py0); ctx.lineTo(CX + pw, py0 + ch); ctx.lineTo(CX + pw, py1);
|
|
773
|
+
ctx.lineTo(CX - pw, py1); ctx.lineTo(CX - pw, py0 + ch); ctx.closePath(); fl(INK, 0.86); ctx.fill();
|
|
774
|
+
st(DIM, 1, 0.9); ctx.stroke(); knock(CX - pw, py0, pw * 2, py1 - py0, 0.95);
|
|
775
|
+
st(CYA, 2, 1); L(CX - pw, py1 + 3, CX - pw + 28, py1 + 3); L(CX + pw, py1 + 3, CX + pw - 28, py1 + 3);
|
|
776
|
+
}
|
|
777
|
+
T('COORD · 坐标', CX, py0 + 19, { f: HOS, w: 500, s: 14, ls: 3, al: 'center', c: DIM });
|
|
778
|
+
T(ty, CX - cw / 2, py0 + 52, { s: 26, w: 500, ls: 2, c: HOT });
|
|
779
|
+
if (blink(t, 2.5) || ty.length < coord.length) T('█', CX - cw / 2 + TW(ty, { s: 26, w: 500, ls: 2 }) + 6, py0 + 51, { s: 22, c: CYA });
|
|
780
|
+
const r2 = typed(t, K.coords + 0.5, `RNG ${(1648.3 - (t - LOCK) * 23.7).toFixed(1)} KM · CONF 99.7 % · TRK 03`, 60);
|
|
781
|
+
T(r2, CX, py0 + 79, { s: 14, al: 'center', c: DIM });
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
// v5 beacon: the reel's coral dot. Boot seed at the centre (the CRT opens around it, it fires the range ping at 0.12 and
|
|
785
|
+
// shrinks into the crosshair pip by 0.45); from 5.80 it pops onto the signal source (the brackets hunt it from 5.90).
|
|
786
|
+
export function beaconState(t, S) {
|
|
787
|
+
if (t < 0.45) {
|
|
788
|
+
const kick = t >= K.boot.frame ? 0.35 * Math.exp(-(t - K.boot.frame) / 0.05) : 0;
|
|
789
|
+
const r = 13 * (1 + kick) * (1 - E.inQ(prog(t, 0.2, 0.25)));
|
|
790
|
+
return r > 0.3 ? { x: CX, y: CY, r, a: 1, glow: 1 - prog(t, 0.2, 0.2), rings: 0, ph: 0 } : null;
|
|
791
|
+
}
|
|
792
|
+
if (t < 5.8) return null;
|
|
793
|
+
const tg = S.target, d = t - 5.8, land = 0.3 * Math.exp(-d / 0.06), lk = t >= LOCK ? Math.exp(-(t - LOCK) / 0.12) : 0;
|
|
794
|
+
const r = 13 * (1 + land) * (1 + 0.6 * lk) * (1 + 0.08 * Math.sin((t - LOCK) * Math.PI * 4));
|
|
795
|
+
return { x: tg.x, y: tg.y, r, a: 1, glow: 1, rings: 1, ph: d };
|
|
796
|
+
}
|
|
797
|
+
function beaconV5(t, S) {
|
|
798
|
+
const b = beaconState(t, S); if (!b) return;
|
|
799
|
+
const { x, y, r } = b;
|
|
800
|
+
ctx.save(); ctx.globalAlpha = b.a;
|
|
801
|
+
if (b.glow > 0) { ctx.globalAlpha = b.a * b.glow; const g = ctx.createRadialGradient(x, y, r * 0.5, x, y, r * 5); g.addColorStop(0, 'rgba(255,90,78,0.55)'); g.addColorStop(1, 'rgba(255,90,78,0)');
|
|
802
|
+
ctx.fillStyle = g; ctx.fillRect(x - r * 5, y - r * 5, r * 10, r * 10); ctx.globalAlpha = b.a; }
|
|
803
|
+
if (b.rings) for (let k = 0; k < 2; k++) { const ph = (b.ph * 2 + k * 0.5) % 1; ctx.strokeStyle = `rgba(255,120,100,${0.6 * (1 - ph)})`; ctx.lineWidth = 2;
|
|
804
|
+
ctx.beginPath(); ctx.arc(x, y, r + ph * 46, 0, Math.PI * 2); ctx.stroke(); }
|
|
805
|
+
ctx.fillStyle = '#FF5A4E'; ctx.beginPath(); ctx.arc(x, y, r, 0, Math.PI * 2); ctx.fill();
|
|
806
|
+
ctx.restore();
|
|
807
|
+
}
|
|
808
|
+
// window.linkAt(t): link elements in 1920×1080 output px (pure function of t and the hologram state S = holo.update(t))
|
|
809
|
+
export function linksV5(t, S) {
|
|
810
|
+
const out = [], tg = S.target;
|
|
811
|
+
const b = beaconState(t, S);
|
|
812
|
+
// visibility through the CRT: the power-on aperture opens from the centre line (0–0.3 s), the power-off squashes the
|
|
813
|
+
// picture into the centre line / dot (9.767–9.967 s; sy = vertical squash of the whole picture)
|
|
814
|
+
const cz = prog(t, K.power_off, 0.2), sy = cz > 0 ? Math.max(Math.pow(1 - Math.min(cz / 0.6, 1), 2.2), 0.003) : 1;
|
|
815
|
+
const vis = clamp(E.outC(prog(t, 0, 0.3)) * 540 / 13) * (cz > 0 ? clamp(sy * 2) : 1);
|
|
816
|
+
if (b) out.push({ id: 'dot', type: 'circle', x: b.x, y: b.y, r: b.r, a: b.a * vis, sy, look: 'flat', fill: '#FF5A4E', stroke: '#FF5A4E', sw: 0,
|
|
817
|
+
glow: { color: '#FF5A4E', r: b.r * 5, a: 0.55 * b.glow }, note: t < 0.45 ? 'boot seed: coral pip at the crosshair, fires the range ping' : 'coral signal beacon (flat disc + radial glow + 2 pulse rings), locked by the HUD' });
|
|
818
|
+
if (t >= K.brackets_in && t < LOCK) {
|
|
819
|
+
const a = Math.max(flick(t, K.brackets_in), 0.8);
|
|
820
|
+
out.push({ id: 'reticle', type: 'circle', x: tg.x, y: tg.y, r: 92, a, look: 'hud', fill: 'none', stroke: '#00E5FF', sw: 3, note: 'lock-progress ring: 36 segments, fills cyan 5.90→6.33' });
|
|
821
|
+
} else if (t >= LOCK) {
|
|
822
|
+
const pk = Math.max(...K.alert_pulses.map((p) => (t >= p ? Math.exp(-(t - p) * 10) : 0)));
|
|
823
|
+
out.push({ id: 'reticle', type: 'circle', x: tg.x, y: tg.y, r: 30 + 6 * pk, a: E.outC(prog(t, LOCK, 0.4)), look: 'hud', fill: 'none', stroke: '#FF2A8A', sw: 1.5, note: 'lock ring: 4 spinning arcs + centre diamond (alert ink)' });
|
|
824
|
+
}
|
|
825
|
+
const p1 = E.outC(prog(t, K.boot.ring_outer, 0.5));
|
|
826
|
+
if (p1 > 0) out.push({ id: 'scope', type: 'circle', x: CX, y: CY, r: 318 * SS * (1 + 0.62 * DIVE(t) + PUSH(t)), a: p1, look: 'hud', fill: 'none', stroke: t >= LOCK ? '#FF2A8A' : '#00E5FF', sw: 1.25 * SS, note: 'outer scope ring (4 rotating arcs); draws on 0.40–0.90, flies past the lens in the dive 6.43–7.10' });
|
|
827
|
+
if (t >= LOCK) {
|
|
828
|
+
const w = E.outE(prog(t, LOCK, 0.3)) * BW;
|
|
829
|
+
out.push({ id: 'rule-top', type: 'line', x0: CX - w, y0: CY - BH + 0.5, x1: CX + w, y1: CY - BH + 0.5, w: 1.5, a: 1, note: 'title band top rule (shoots out from the centre on the lock)' });
|
|
830
|
+
out.push({ id: 'rule-bot', type: 'line', x0: CX - w, y0: CY + BH - 0.5, x1: CX + w, y1: CY + BH - 0.5, w: 1.5, a: 1, note: 'title band bottom rule + data ticks' });
|
|
831
|
+
const m = titleMetrics(), sc = slamScale(t), cx = (m.l + m.r) / 2, cy = (m.top + m.bot) / 2;
|
|
832
|
+
out.push({ id: 'title', type: 'rect', x: CX + (cx - CX) * sc, y: CY + (cy - CY) * sc, w: (m.r - m.l) * sc, h: (m.bot - m.top) * sc, rot: 0, a: 1, note: '「赛博朋克」 ● HUD title lockup (glyph ink box, the dot sits in the gap)' });
|
|
833
|
+
}
|
|
834
|
+
return out;
|
|
835
|
+
}
|
|
836
|
+
export function drawHUD(t, S, mode = 'all') {
|
|
837
|
+
const main = ctx; if (mode === 'txt') ctx = sctx; TXT_ON = mode !== 'geo';
|
|
838
|
+
ctx.setTransform(dpr, 0, 0, dpr, 0, 0); ctx.filter = 'none';
|
|
839
|
+
ctx.clearRect(0, 0, W, H);
|
|
840
|
+
if (TXT_ON) { tctx.setTransform(dpr, 0, 0, dpr, 0, 0); tctx.filter = 'none'; tctx.clearRect(0, 0, W, H); }
|
|
841
|
+
ctx.globalAlpha = 1; ctx.lineJoin = 'miter'; GA = 1;
|
|
842
|
+
const sg = SURGE(t), dv = DIVE(t), dx = 120 * dv, bl = 4 * dv;
|
|
843
|
+
frameCorners(t);
|
|
844
|
+
ctx.save(); ctx.translate(0, -46 * dv); blurLayer(2 * dv, () => topBar(t, S)); ctx.restore();
|
|
845
|
+
scope(t, S); sweepBlips(t, S); groundTrack(t, S);
|
|
846
|
+
// near plane: side columns slide outward + defocus on the lock kick (parallax vs. the scope)
|
|
847
|
+
for (const [fn, sx] of [[leftCol, -1], [rightCol, 1]]) {
|
|
848
|
+
ctx.save(); ctx.translate(sx * dx, 0); blurLayer(bl, () => fn(t, S)); ctx.restore(); GA = 1;
|
|
849
|
+
}
|
|
850
|
+
ctx.save(); ctx.translate(0, 46 * dv); blurLayer(2 * dv, () => tape(t, S)); ctx.restore(); mapIntel(t, S); diveStreaks(t); if (V5) bannerV5(t, S); else banner(t, S); acquire(t, S);
|
|
851
|
+
if (V5) { if (mode !== 'txt' && !NOLINK.has('dot')) beaconV5(t, S); }
|
|
852
|
+
else if (REEL && mode !== 'txt' && t >= 5.8) beacon(t, S);
|
|
853
|
+
ctx.globalAlpha = 1; tctx.globalAlpha = 1; ctx.filter = 'none'; ctx = main; TXT_ON = true; KC = false;
|
|
854
|
+
return { surge: sg, dive: dv, scopeR: 322 * SS * (1 + 0.62 * dv + PUSH(t)) };
|
|
855
|
+
}
|