@biunow/agent-biuart 0.0.0-stage → 0.1.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/bin/server.mjs +575 -0
- package/data/styles.json +533 -0
- package/engine/audit_clip.py +46 -0
- package/engine/clip.html +24 -0
- package/engine/clip.js +104 -0
- package/engine/clip_schema.json +19 -0
- package/engine/clips/t1_3b1b.js +203 -0
- package/engine/clips/t2_keynote_ui.js +347 -0
- package/engine/clips/t3_finance_chart.js +206 -0
- package/engine/clips/y1_kurzgesagt.js +183 -0
- package/engine/clips/y2_vox.js +150 -0
- package/engine/clips/y3_whiteboard.js +209 -0
- package/engine/clips/y4_storytime.js +194 -0
- package/engine/clips/y5_kinetic_type.js +134 -0
- package/engine/compare.py +19 -0
- package/engine/demos/_shared/hero/c1_0.png +0 -0
- package/engine/demos/_shared/hero/c1_1.png +0 -0
- package/engine/demos/_shared/hero/c1_2.png +0 -0
- package/engine/demos/_shared/hero/c3_line.png +0 -0
- package/engine/demos/_shared/hero.js +96 -0
- package/engine/demos/long_scroll/eras.js +23 -0
- package/engine/demos/long_scroll/frames/8bit_act/01.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_act/02.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_act/03.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_act/04.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_act/05.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_act/06.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_act/07.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_act/08.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_act/meta.json +69 -0
- package/engine/demos/long_scroll/frames/8bit_walk/01.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_walk/02.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_walk/03.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_walk/04.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_walk/05.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_walk/06.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_walk/07.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_walk/08.png +0 -0
- package/engine/demos/long_scroll/frames/8bit_walk/meta.json +69 -0
- package/engine/demos/long_scroll/frames/egypt_dodge/01.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_dodge/02.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_dodge/03.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_dodge/04.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_dodge/05.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_dodge/06.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_dodge/07.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_dodge/08.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_dodge/meta.json +69 -0
- package/engine/demos/long_scroll/frames/egypt_walk8/01.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_walk8/02.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_walk8/03.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_walk8/04.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_walk8/05.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_walk8/06.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_walk8/07.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_walk8/08.png +0 -0
- package/engine/demos/long_scroll/frames/egypt_walk8/meta.json +69 -0
- package/engine/demos/long_scroll/frames/monet_throw/01.png +0 -0
- package/engine/demos/long_scroll/frames/monet_throw/02.png +0 -0
- package/engine/demos/long_scroll/frames/monet_throw/03.png +0 -0
- package/engine/demos/long_scroll/frames/monet_throw/04.png +0 -0
- package/engine/demos/long_scroll/frames/monet_throw/05.png +0 -0
- package/engine/demos/long_scroll/frames/monet_throw/06.png +0 -0
- package/engine/demos/long_scroll/frames/monet_throw/07.png +0 -0
- package/engine/demos/long_scroll/frames/monet_throw/08.png +0 -0
- package/engine/demos/long_scroll/frames/monet_throw/meta.json +71 -0
- package/engine/demos/long_scroll/frames/monet_walk/01.png +0 -0
- package/engine/demos/long_scroll/frames/monet_walk/02.png +0 -0
- package/engine/demos/long_scroll/frames/monet_walk/03.png +0 -0
- package/engine/demos/long_scroll/frames/monet_walk/04.png +0 -0
- package/engine/demos/long_scroll/frames/monet_walk/05.png +0 -0
- package/engine/demos/long_scroll/frames/monet_walk/06.png +0 -0
- package/engine/demos/long_scroll/frames/monet_walk/07.png +0 -0
- package/engine/demos/long_scroll/frames/monet_walk/08.png +0 -0
- package/engine/demos/long_scroll/frames/monet_walk/meta.json +71 -0
- package/engine/demos/long_scroll/lib/acts.js +87 -0
- package/engine/demos/long_scroll/lib/crossing.js +463 -0
- package/engine/demos/long_scroll/lib/world.js +143 -0
- package/engine/demos/long_scroll/lib/xing2_ding.js +42 -0
- package/engine/demos/long_scroll/lib/xing2_jia.js +114 -0
- package/engine/demos/long_scroll/lib/xing2_yi.js +133 -0
- package/engine/demos/long_scroll/segments/02_egypt.js +234 -0
- package/engine/demos/long_scroll/segments/14_8bit.js +118 -0
- package/engine/demos/long_scroll/segments/28_monet.js +86 -0
- package/engine/demos/t1_3b1b/eras.js +11 -0
- package/engine/demos/t1_3b1b/t1_s1.js +101 -0
- package/engine/demos/t1_3b1b/t1_s2.js +74 -0
- package/engine/demos/t1_3b1b/t1_s3.js +65 -0
- package/engine/demos/t2_keynote_ui/eras.js +10 -0
- package/engine/demos/t2_keynote_ui/t2_s1.js +57 -0
- package/engine/demos/t2_keynote_ui/t2_s2.js +70 -0
- package/engine/demos/t2_keynote_ui/t2_s3.js +61 -0
- package/engine/demos/t3_finance_chart/eras.js +11 -0
- package/engine/demos/t3_finance_chart/t3_s1.js +48 -0
- package/engine/demos/t3_finance_chart/t3_s2.js +42 -0
- package/engine/demos/t3_finance_chart/t3_s3.js +51 -0
- package/engine/demos/y1_kurzgesagt/eras.js +10 -0
- package/engine/demos/y1_kurzgesagt/kurzgesagt.js +347 -0
- package/engine/demos/y2_vox/eras.js +10 -0
- package/engine/demos/y2_vox/vox.js +240 -0
- package/engine/demos/y3_whiteboard/eras.js +10 -0
- package/engine/demos/y3_whiteboard/whiteboard.js +162 -0
- package/engine/demos/y4_storytime/eras.js +14 -0
- package/engine/demos/y4_storytime/s1_desk.js +18 -0
- package/engine/demos/y4_storytime/s2_screen.js +82 -0
- package/engine/demos/y4_storytime/s3_react.js +39 -0
- package/engine/demos/y4_storytime/s4_wide.js +16 -0
- package/engine/demos/y4_storytime/s5_close.js +18 -0
- package/engine/demos/y4_storytime/story.js +25 -0
- package/engine/demos/y4_storytime/vo_env.js +2 -0
- package/engine/demos/y4_storytime/world.js +73 -0
- package/engine/demos/y5_kinetic_type/eras.js +16 -0
- package/engine/demos/y5_kinetic_type/k1_title.js +38 -0
- package/engine/demos/y5_kinetic_type/k2_complete.js +55 -0
- package/engine/demos/y5_kinetic_type/k3_chat.js +47 -0
- package/engine/demos/y5_kinetic_type/k4_agent.js +49 -0
- package/engine/demos/y5_kinetic_type/kinetic.js +52 -0
- package/engine/engine.js +277 -0
- package/engine/eras.js +60 -0
- package/engine/eras_gallery.js +115 -0
- package/engine/eras_style_studies.js +20 -0
- package/engine/examples/assets/K/347/272/277/345/233/276_/346/250/252/345/261/217.png +0 -0
- package/engine/examples/assets//347/273/210/347/253/257_for/345/276/252/347/216/257/346/261/202/345/222/214.png +0 -0
- package/engine/examples/assets//347/273/210/347/253/257_sum/346/261/202/345/222/214.png +0 -0
- package/engine/examples/assets//350/220/245/346/224/266/345/233/276_/347/253/226/345/261/217.png +0 -0
- package/engine/examples/assets//350/264/242/347/273/217/345/233/276/350/241/250_/346/250/252/345/261/217.png +0 -0
- package/engine/examples/t1_3b1b.json +19 -0
- package/engine/examples/t2_fullbleed_pulse.json +15 -0
- package/engine/examples/t2_keynote_ui.json +15 -0
- package/engine/examples/t3_finance_chart.json +29 -0
- package/engine/examples/y1_kurzgesagt.json +16 -0
- package/engine/examples/y2_vox.json +14 -0
- package/engine/examples/y3_whiteboard.json +17 -0
- package/engine/examples/y4_storytime.json +52 -0
- package/engine/examples/y5_kinetic_type.json +14 -0
- package/engine/index.html +42 -0
- package/engine/lib/THREE_LICENSE.txt +21 -0
- package/engine/lib/brush.js +149 -0
- package/engine/lib/camera.js +99 -0
- package/engine/lib/chart.js +152 -0
- package/engine/lib/collage.js +97 -0
- package/engine/lib/diagram.js +264 -0
- package/engine/lib/finish.js +70 -0
- package/engine/lib/fonts/AlfaSlabOne-400.woff +0 -0
- package/engine/lib/fonts/Anton-400.woff +0 -0
- package/engine/lib/fonts/ArchivoBlack-400.woff +0 -0
- package/engine/lib/fonts/Bangers-400.woff +0 -0
- package/engine/lib/fonts/Caveat-600.woff +0 -0
- package/engine/lib/fonts/Cinzel-400.woff +0 -0
- package/engine/lib/fonts/Cinzel-700.woff +0 -0
- package/engine/lib/fonts/CormorantGaramond-500.woff +0 -0
- package/engine/lib/fonts/CormorantGaramond-500i.woff +0 -0
- package/engine/lib/fonts/CormorantGaramond-700.woff +0 -0
- package/engine/lib/fonts/EBGaramond-400.woff +0 -0
- package/engine/lib/fonts/EBGaramond-400i.woff +0 -0
- package/engine/lib/fonts/EBGaramond-600.woff +0 -0
- package/engine/lib/fonts/Exo2-800i.woff +0 -0
- package/engine/lib/fonts/GochiHand-400.woff +0 -0
- package/engine/lib/fonts/IMFellEnglish-400.woff +0 -0
- package/engine/lib/fonts/IMFellEnglish-400i.woff +0 -0
- package/engine/lib/fonts/Inter-var.woff +0 -0
- package/engine/lib/fonts/JosefinSans-700.woff +0 -0
- package/engine/lib/fonts/Kalam-700.woff +0 -0
- package/engine/lib/fonts/LICENSES.md +40 -0
- package/engine/lib/fonts/LXGWWenKai-500.woff +0 -0
- package/engine/lib/fonts/LibreBaskerville-400.woff +0 -0
- package/engine/lib/fonts/LibreBaskerville-700.woff +0 -0
- package/engine/lib/fonts/LilitaOne-400.woff +0 -0
- package/engine/lib/fonts/Marcellus-400.woff +0 -0
- package/engine/lib/fonts/NotoSansSC-500.woff +0 -0
- package/engine/lib/fonts/NotoSansSC-700.woff +0 -0
- package/engine/lib/fonts/NotoSansSC-800.woff +0 -0
- package/engine/lib/fonts/NotoSansSC-900.woff +0 -0
- package/engine/lib/fonts/NotoSansSC-sub.woff +0 -0
- package/engine/lib/fonts/NotoSerifJP-600.woff +0 -0
- package/engine/lib/fonts/NotoSerifSC-sub.woff +0 -0
- package/engine/lib/fonts/OFL.txt +94 -0
- package/engine/lib/fonts/PlayfairDisplay-400i.woff +0 -0
- package/engine/lib/fonts/PlayfairDisplay-700.woff +0 -0
- package/engine/lib/fonts/PlayfairDisplay-700i.woff +0 -0
- package/engine/lib/fonts/Poppins-600.woff +0 -0
- package/engine/lib/fonts/Poppins-700.woff +0 -0
- package/engine/lib/fonts/Poppins-800.woff +0 -0
- package/engine/lib/fonts/PressStart2P-400.woff +0 -0
- package/engine/lib/fonts/Righteous-400.woff +0 -0
- package/engine/lib/fonts/RobotoCondensed-var.woff +0 -0
- package/engine/lib/fonts/Rye-400.woff +0 -0
- package/engine/lib/fonts/UnifrakturMaguntia-400.woff +0 -0
- package/engine/lib/fonts/cmunrm.woff +0 -0
- package/engine/lib/fonts/cmunti.woff +0 -0
- package/engine/lib/fonts/index.json +1 -0
- package/engine/lib/fonts.js +56 -0
- package/engine/lib/kit.js +91 -0
- package/engine/lib/media.js +118 -0
- package/engine/lib/medium.js +258 -0
- package/engine/lib/motion.js +111 -0
- package/engine/lib/paint.js +200 -0
- package/engine/lib/palettes.js +57 -0
- package/engine/lib/post.js +108 -0
- package/engine/lib/render.js +189 -0
- package/engine/lib/rig.js +390 -0
- package/engine/lib/rig_huashu.js +171 -0
- package/engine/lib/three.min.js +7 -0
- package/engine/lib/toon.js +186 -0
- package/engine/lib/typo.js +136 -0
- package/engine/lib/ui.js +141 -0
- package/engine/lib/util.js +92 -0
- package/engine/render.py +157 -0
- package/engine/scenes/01_cave.js +364 -0
- package/engine/scenes/02_egypt.js +524 -0
- package/engine/scenes/03_greek.js +448 -0
- package/engine/scenes/04_roman.js +555 -0
- package/engine/scenes/05_gothic.js +522 -0
- package/engine/scenes/06_renaissance.js +338 -0
- package/engine/scenes/08_impressionism.js +206 -0
- package/engine/scenes/09_postimp.js +183 -0
- package/engine/scenes/10_nouveau.js +334 -0
- package/engine/scenes/11_cubism.js +265 -0
- package/engine/scenes/12_bauhaus.js +214 -0
- package/engine/scenes/13_pop.js +269 -0
- package/engine/scenes/14_8bit.js +238 -0
- package/engine/scenes/15_raytrace.js +300 -0
- package/engine/scenes/16_2026.js +565 -0
- package/engine/scenes/17_ink.js +273 -0
- package/engine/scenes/18_klimt.js +227 -0
- package/engine/scenes/19_munch.js +153 -0
- package/engine/scenes/20_dunhuang.js +215 -0
- package/engine/scenes/21_kusama.js +124 -0
- package/engine/scenes/22_constructivism.js +205 -0
- package/engine/scenes/23_dali.js +221 -0
- package/engine/scenes/24_hopper.js +150 -0
- package/engine/scenes/25_ghibli.js +251 -0
- package/engine/scenes/26_vaporwave.js +191 -0
- package/engine/scenes/27_kirby.js +255 -0
- package/engine/scenes/28_monet.js +176 -0
- package/engine/scenes/29_seurat.js +104 -0
- package/engine/scenes/30_matisse.js +151 -0
- package/engine/scenes/31_haring.js +109 -0
- package/engine/scenes/32_rembrandt.js +217 -0
- package/engine/scenes/33_rubberhose.js +219 -0
- package/engine/scenes/34_shadowpuppet.js +195 -0
- package/engine/scenes/35_shinkai.js +197 -0
- package/engine/scenes/36_picasso_blue.js +123 -0
- package/engine/scenes/38_giotto.js +133 -0
- package/engine/scenes/39_piero.js +53 -0
- package/engine/scenes/40_raphael.js +96 -0
- package/engine/scenes/41_michelangelo.js +112 -0
- package/engine/scenes/42_guardi.js +70 -0
- package/engine/scenes/43_lorenzetti.js +145 -0
- package/engine/scenes/44_cortona.js +86 -0
- package/engine/scenes/45_renoir.js +86 -0
- package/engine/scenes/46_pissarro.js +82 -0
- package/engine/scenes/47_sisley.js +94 -0
- package/engine/scenes/48_gauguin.js +107 -0
- package/engine/scenes/49_chagall.js +60 -0
- package/engine/scenes/50_ma_yuan.js +131 -0
- package/engine/scenes/51_persian_miniature.js +149 -0
- package/engine/scenes/52_south_park.js +105 -0
- package/engine/scenes/53_pixar.js +60 -0
- package/engine/scenes/54_ghibli_lab.js +68 -0
- package/engine/scenes/55_dunhuang.js +161 -0
- package/engine/scenes/index.js +28 -0
- package/engine/spec_assets.py +76 -0
- package/engine/transitions.js +929 -0
- package/engine//346/227/266/344/273/243/344/275/234/350/200/205/350/247/204/350/214/203.md +49 -0
- package/lib/host.mjs +754 -0
- package/lib/render-bridge.mjs +543 -0
- package/lib/render-cli.mjs +98 -0
- package/lib/storyboard-lint.mjs +54 -0
- package/manifest.json +52 -0
- package/package.json +8 -4
- package/runtime/package.json +8 -0
- package/skills/biuart/SKILL.md +154 -0
- package/skills/biuart/references/01-/346/213/206/350/247/243.md +55 -0
- package/skills/biuart/references/02-/346/234/272/345/210/266.md +41 -0
- package/skills/biuart/references/03-/344/270/200/345/270/247/345/205/210/350/241/214/345/233/233/346/235/241/350/267/257/347/272/277.md +39 -0
- package/skills/biuart/references/04-/347/272/257/344/273/243/347/240/201/347/273/230/345/210/266.md +92 -0
- package/skills/biuart/references/05-/350/212/202/345/245/217/344/270/216/351/205/215/344/271/220.md +19 -0
- package/skills/biuart/references/06-/345/217/243/346/222/255/351/251/261/345/212/250/347/232/204/350/211/272/346/234/257/347/237/255/347/211/207.md +31 -0
- package/skills/biuart/references/07-/346/255/243/351/235/242/347/273/217/351/252/214.md +196 -0
- package/skills/biuart/references/08-/351/243/216/346/240/274/344/275/234/350/200/205/350/247/204/350/214/203.md +61 -0
- package/skills/biuart/references/09-/350/247/206/351/242/221/345/212/250/347/224/273/350/257/255/346/263/225.md +382 -0
- package/skills/biuart/references/10-/350/247/222/350/211/262.md +34 -0
- package/skills/biuart/references/11-/351/225/277/345/215/267/347/251/277/350/266/212/347/211/207.md +43 -0
- package/skills/biuart/references/12-/345/217/243/346/222/255/346/225/264/347/211/207/344/270/216/347/273/217/351/252/214/345/233/236/346/265/201.md +25 -0
- package/skills/biuart/references/13-/345/217/243/346/222/255/344/270/216/350/257/255/351/237/263/345/244/215/345/210/273.md +7 -0
- package/skills/biuart/references/15-/350/267/250/345/234/272/346/231/257/351/243/216/346/240/274/346/217/220/347/202/274.md +48 -0
- package/skills/biuart/references/16-/351/243/216/346/240/274/346/274/224/347/244/27295/345/210/206/345/215/217/350/256/256.md +103 -0
- package/skills/biuart/references/about-author.md +99 -0
- package/skills/biuart/references/capabilities.md +91 -0
- package/skills/biuart/references/compatibility.md +34 -0
- package/skills/biuart/references/images.md +97 -0
- package/skills/biuart/references/voice-model-review.md +18 -0
- package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/t1_3b1b.md +122 -0
- package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/t2_keynote_ui.md +117 -0
- package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/t3_finance_chart.md +116 -0
- package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/y1_kurzgesagt.md +193 -0
- package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/y2_vox.md +195 -0
- package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/y3_whiteboard.md +190 -0
- package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/y4_storytime.md +166 -0
- package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/y5_kinetic_type.md +158 -0
- package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/y6_presenter_explainer.md +24 -0
- package/skills/biuart/references//345/217/243/346/222/255API.md +125 -0
- package/skills/biuart/references//350/211/262/346/235/277.md +38 -0
- package/skills/biuart/references//350/247/222/350/211/262/347/262/276/347/273/206/345/272/246.md +61 -0
- package/skills/biuart/references//351/225/234/345/244/264/350/241/250/346/250/241/346/235/277.md +80 -0
- package/skills/biuart/references//351/225/234/345/244/264/350/257/255/350/250/200.md +73 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/01_cave.md +46 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/02_egypt.md +64 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/03_greek.md +50 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/04_roman.md +99 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/05_gothic.md +32 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/06_renaissance.md +41 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/08_impressionism.md +34 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/09_postimp.md +17 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/10_nouveau.md +31 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/11_cubism.md +30 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/12_bauhaus.md +30 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/13_pop.md +29 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/14_8bit.md +27 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/15_raytrace.md +32 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/16_2026.md +55 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/17_ink.md +50 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/18_klimt.md +37 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/19_munch.md +42 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/20_dunhuang.md +49 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/21_kusama.md +32 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/22_constructivism.md +35 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/23_dali.md +29 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/24_hopper.md +30 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/25_ghibli.md +55 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/26_vaporwave.md +27 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/27_kirby.md +30 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/28_monet.md +39 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/29_seurat.md +31 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/30_matisse.md +26 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/31_haring.md +22 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/32_rembrandt.md +29 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/33_rubberhose.md +31 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/34_shadowpuppet.md +40 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/35_shinkai.md +34 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/36_picasso_blue.md +25 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/37_rick_morty.md +48 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/38_giotto.md +67 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/39_piero.md +59 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/40_raphael.md +65 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/41_michelangelo.md +65 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/42_guardi.md +65 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/43_lorenzetti.md +69 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/44_cortona.md +66 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/45_renoir.md +66 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/46_pissarro.md +66 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/47_sisley.md +67 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/48_gauguin.md +66 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/49_chagall.md +59 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/50_ma_yuan.md +68 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/51_persian_miniature.md +66 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/52_south_park.md +65 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/53_pixar.md +61 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/55_dunhuang.md +41 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/INDEX.md +89 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/_/350/275/254/345/234/272_/350/277/201/347/247/273/346/265/213/350/257/225.md +53 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/_/351/207/215/345/201/232/350/277/233/345/272/246.md +37 -0
- package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/_/351/237/263/350/275/250_/350/211/272/346/234/257/345/217/262/351/200/237/351/200/232.md +135 -0
- package/studio/app.js +380 -0
- package/studio/assert-panel-permit.mjs +67 -0
- package/studio/composer.css +465 -0
- package/studio/composer.js +480 -0
- package/studio/index.html +97 -0
- package/studio/optimize-prompt.mjs +84 -0
- package/studio/optimize-prompt.test.mjs +47 -0
- package/studio/recipes.mjs +87 -0
- package/studio/select-menu.js +450 -0
- package/studio/styles.css +71 -0
- package/studio/to-english-prompt.mjs +149 -0
- package/studio/to-english-prompt.test.mjs +72 -0
- package/README.md +0 -3
package/engine/clip.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// 参数化片段运行时:一份 spec(JSON)→ 一段时长严格等于 spec.duration 的动画。给口播视频管线当「动画段」用。
|
|
2
|
+
// 契约见 references/09-视频动画语法.md「片段契约」。和 engine.js 的区别:没有段落表/转场,一个语法一个 draw(c, t, ctx),画布尺寸从 spec 读(竖屏也行)。
|
|
3
|
+
//
|
|
4
|
+
// spec.finish(可选):成片质感层,预设 soft / film / punch / clean 或对象,见 lib/finish.js;alpha 模式忽略。
|
|
5
|
+
// spec = { grammar, duration, fps=30, width=1920, height=1080, safe?: {top,bottom,left,right(px,留给字幕/平台 UI), fill?(让开的边涂这个色)}, theme?, alpha?, data?, cues: [{at, kind, text?, sub?, data?, image?, frames?, fps?, dur?}] }
|
|
6
|
+
// spec.theme:全片共用的色板(lib/palettes.js)——名字(paper poster ink navy bauhaus snow wood chalk)、{name, 要覆盖的键}、或完整 6 键 {bg, surface, ink, sub, accent, accent2}。
|
|
7
|
+
// 解析成 ctx.pal(完整 6 键+name),各语法按它上色;名字写错 / 缺键 → 启动失败。不写 → ctx.pal = null,各语法用自己的默认配色。
|
|
8
|
+
// 语法可以声明 legacyTheme(theme) → true:这个 theme 是它自己的旧写法(t3 的 'light' / 'dark' / 旧主题对象),不当色板解析,语法照旧读 ctx.theme。
|
|
9
|
+
// 素材路径(render.py 用 spec_assets.py 核存在并改写):cue.image、cue.frames(帧序列:目录或路径数组)、data.image、data.character.frames(y4 帧库)。
|
|
10
|
+
// 任何一张加载失败 → 启动失败、拒绝渲染(不出空白帧)。语法里取素材:ctx.still(q) = 这条 cue 的静态图(有 frames 时是第一帧),ctx.frame(q, t) = t 时刻该画的那一帧。
|
|
11
|
+
// 语法文件 clips/<grammar>.js 注册 CLIPS[grammar] = { init?(ctx), draw(c, t, ctx), safe?: true(按 ctx.safe / ctx.box 排版了才写,否则 spec.safe 会告警) }。
|
|
12
|
+
// 对齐约定(CLIP.lt):cue 的「揭开那一帧」= at 所在的那一帧——元素在 t = at 这一帧第一次可见,动画内部时间从 at − 1 帧起算。
|
|
13
|
+
(() => {
|
|
14
|
+
const cv = document.getElementById('c'), c = cv.getContext('2d');
|
|
15
|
+
window.__canvas = cv;
|
|
16
|
+
window.CLIPS = window.CLIPS || {};
|
|
17
|
+
const C = window.CLIP = {};
|
|
18
|
+
const fail = msg => { window.__bootFailed = msg; console.error('CLIP BOOT FAILED\n' + msg); };
|
|
19
|
+
|
|
20
|
+
// ---------- 给语法文件用的小工具 ----------
|
|
21
|
+
// 片内时间:cue 在 at 这一帧第一次可见
|
|
22
|
+
C.lt = (t, at, fps) => t - at + 1 / fps;
|
|
23
|
+
// 进度:从 at 起 dur 秒走完 0→1(at 这一帧已经 >0)
|
|
24
|
+
C.p = (t, at, dur, fps) => U.clamp(C.lt(t, at, fps) / dur);
|
|
25
|
+
// 等比放进框里(contain,不裁):返回 {x, y, w, h}
|
|
26
|
+
C.fit = (iw, ih, bx, by, bw, bh) => { const s = Math.min(bw / iw, bh / ih); const w = iw * s, h = ih * s; return { x: bx + (bw - w) / 2, y: by + (bh - h) / 2, w, h, s }; };
|
|
27
|
+
// 归一化矩形 [x,y,w,h](0..1,相对图片)→ 画面矩形
|
|
28
|
+
C.sub = (r, box) => ({ x: box.x + r[0] * box.w, y: box.y + r[1] * box.h, w: r[2] * box.w, h: r[3] * box.h });
|
|
29
|
+
C.text = (cue, ...fallback) => (cue && (cue.text ?? cue.data?.text)) ?? fallback.find(v => v != null) ?? '';
|
|
30
|
+
|
|
31
|
+
async function boot() {
|
|
32
|
+
let spec = window.CLIP_SPEC;
|
|
33
|
+
if (!spec) { // 预览:?spec=<相对 engine 的路径>
|
|
34
|
+
const u = new URLSearchParams(location.search).get('spec');
|
|
35
|
+
if (!u) return fail('没有 spec:render.py --spec 会注入 window.CLIP_SPEC;预览用 clip.html?spec=examples/<名>.json');
|
|
36
|
+
spec = await (await fetch(u)).json();
|
|
37
|
+
const base = u.replace(/[^/]*$/, '');
|
|
38
|
+
const fix = p => p && !/^(data:|https?:|\/)/.test(p) ? base + p : p; // 预览模式:frames 要写成路径数组(浏览器列不了目录),帧库要写 {frames: {姿势: 路径}}
|
|
39
|
+
(spec.cues || []).forEach(q => { if (q.image) q.image = fix(q.image); if (Array.isArray(q.frames)) q.frames = q.frames.map(fix); });
|
|
40
|
+
if (spec.data && spec.data.image) spec.data.image = fix(spec.data.image);
|
|
41
|
+
const ch = spec.data && spec.data.character; if (ch && typeof ch === 'object' && ch.frames && typeof ch.frames === 'object') for (const k in ch.frames) ch.frames[k] = fix(ch.frames[k]);
|
|
42
|
+
}
|
|
43
|
+
if (location.search.includes('render=1')) document.body.classList.add('render');
|
|
44
|
+
const W = spec.width || 1920, H = spec.height || 1080, FPS = spec.fps || 30;
|
|
45
|
+
if (!spec.grammar) return fail('spec 缺 grammar');
|
|
46
|
+
if (!(spec.duration > 0)) return fail('spec.duration 必须 > 0');
|
|
47
|
+
cv.width = W; cv.height = H; U.setStage(W, H);
|
|
48
|
+
// 语法文件(同步 XHR + eval,带 sourceURL,报错能定位)
|
|
49
|
+
const x = new XMLHttpRequest(); x.open('GET', `clips/${spec.grammar}.js`, false);
|
|
50
|
+
try { x.send(); } catch (e) { return fail('请求语法文件失败 ' + e); }
|
|
51
|
+
if (x.status !== 200) return fail(`没有这个语法:clips/${spec.grammar}.js(HTTP ${x.status})`);
|
|
52
|
+
try { (0, eval)(x.responseText + `\n//# sourceURL=clips/${spec.grammar}.js`); } catch (e) { return fail(`clips/${spec.grammar}.js 执行出错 ${e.stack || e}`); }
|
|
53
|
+
const G = CLIPS[spec.grammar]; if (!G || typeof G.draw !== 'function') return fail(`clips/${spec.grammar}.js 没注册 CLIPS['${spec.grammar}'] = { draw }`);
|
|
54
|
+
// 主题色板:在加载素材之前核,写错就别白等图片
|
|
55
|
+
let pal = null;
|
|
56
|
+
if (!(G.legacyTheme && G.legacyTheme(spec.theme))) { try { pal = PAL.resolve(spec.theme); } catch (e) { return fail(e.message); } }
|
|
57
|
+
// 成片质感层(spec.finish):写错预设名就别白等图片
|
|
58
|
+
let fin = null; try { fin = FINISH.resolve(spec.finish); } catch (e) { return fail(e.message); }
|
|
59
|
+
// 图片
|
|
60
|
+
const IMG = {}, urls = new Set();
|
|
61
|
+
for (const q of (spec.cues || [])) {
|
|
62
|
+
if (q.image) urls.add(q.image);
|
|
63
|
+
if (q.frames != null) { if (!Array.isArray(q.frames) || !q.frames.length) return fail(`cue ${q.kind} at=${q.at} 的 frames 要是非空的路径数组(render.py 会把目录展开成数组;预览模式只认数组)`); q.frames.forEach(f => urls.add(f)); }
|
|
64
|
+
}
|
|
65
|
+
if (spec.data && spec.data.image) urls.add(spec.data.image);
|
|
66
|
+
const chr = spec.data && spec.data.character;
|
|
67
|
+
if (chr && typeof chr === 'object') { if (!chr.frames || typeof chr.frames !== 'object' || Array.isArray(chr.frames)) return fail('data.character 写成对象时要有 frames: {姿势名: 图片路径}(或在 render.py 里给一个帧库目录)');
|
|
68
|
+
Object.values(chr.frames).forEach(f => urls.add(f)); }
|
|
69
|
+
try { await Promise.all([...urls].map(u => new Promise((res, rej) => { const im = new Image(); im.onload = () => { IMG[u] = im; res(); }; im.onerror = () => rej(u); im.src = u; }))); }
|
|
70
|
+
catch (u) { return fail('图片加载失败:' + u); }
|
|
71
|
+
// 字体+字形检查(spec 里的字缺字形只警告:回退系统字体照样能渲,但换台机器会变样)
|
|
72
|
+
for (const f of (window.FONT_FACES || [])) { const ff = new FontFace(f.family, `url(${f.url})`, f.desc || {}); await ff.load(); document.fonts.add(ff); }
|
|
73
|
+
await document.fonts.ready; await U.loadCmaps();
|
|
74
|
+
const cues0 = (spec.cues || []).map((q, i) => ({ ...q, i, at: +q.at || 0 })).sort((a, b) => a.at - b.at || a.i - b.i);
|
|
75
|
+
for (const q of cues0) if (q.at > spec.duration + 1e-9) console.warn(`cue ${q.kind} at=${q.at} 超出片段时长 ${spec.duration}s,看不到,已丢掉(不占版面)`);
|
|
76
|
+
const cues = cues0.filter(q => q.at <= spec.duration + 1e-9); // 看不到的 cue 不进语法:否则照样占排版位置、进度轨照样数它
|
|
77
|
+
// number 的说明字段各语法以前各认各的(t2/t3 认 text,y5 认 sub),写错了不报错只是没字。统一:text 与 sub 互为别名,data.text 也算
|
|
78
|
+
for (const q of cues) if (q.kind === 'number') { const cap = q.text ?? q.sub ?? (q.data && q.data.text); if (cap != null) { q.text = cap; q.sub = cap; } }
|
|
79
|
+
const safe = { top: 0, bottom: 0, left: 0, right: 0, ...(spec.safe || {}) }; // 让开的边(px):管线烧录字幕、平台 UI 压在这里,带字的语法不往里排
|
|
80
|
+
const box = { x: safe.left, y: safe.top, w: W - safe.left - safe.right, h: H - safe.top - safe.bottom }; // 让开之后的内容框,语法排版用它(ctx.box)
|
|
81
|
+
const ctx = { spec, data: spec.data || {}, theme: spec.theme || {}, pal, cues, W, H, FPS, u: Math.min(W, H) / 1080, portrait: H > W, alpha: !!spec.alpha, IMG, dur: spec.duration, safe, box,
|
|
82
|
+
of: (...kinds) => cues.filter(q => kinds.includes(q.kind)), lt: (t, at) => C.lt(t, at, FPS), p: (t, at, d) => C.p(t, at, d, FPS),
|
|
83
|
+
// 素材:still = 定版式用的那张(image,或 frames 的第一帧);frame = t 时刻画哪一帧(frames 从 at 起按 q.fps(默认 30)播,播完停在最后一帧,q.loop 循环)
|
|
84
|
+
still: q => q && (q.image ? IMG[q.image] : q.frames ? IMG[q.frames[0]] : null),
|
|
85
|
+
frame: (q, t) => !q ? null : q.frames ? MD.frameAt(q.frames.map(f => IMG[f]), t - q.at, q.fps || 30, !!q.loop) : q.image ? IMG[q.image] : null };
|
|
86
|
+
if (G.fonts) { const txt = cues.map(q => [q.text, q.sub, q.data && q.data.text].filter(Boolean).join('')).join('') + JSON.stringify(spec.data || {});
|
|
87
|
+
for (const fam of G.fonts) { const miss = U.missingGlyphs(fam, [...txt].filter(ch => ch.codePointAt(0) >= 0x2E80).join('')); /* 只查汉字与全角符号:拉丁/希腊/数学符号各语法另有字体 */ if (miss.length) console.warn(`字体「${fam}」缺字:${miss.join('')}(会回退系统字体;常用字已在 GB2312 子集里,生僻字用 scripts/font_subset.py --text 补)`); } }
|
|
88
|
+
if (spec.safe && (safe.top || safe.bottom || safe.left || safe.right) && !G.safe)
|
|
89
|
+
console.warn(`语法 ${spec.grammar} 还没接 safe(语法对象里没有 safe: true),spec.safe 被忽略:字幕带可能压在字上`);
|
|
90
|
+
if (G.init) G.init(ctx);
|
|
91
|
+
window.CLIP_CTX = ctx;
|
|
92
|
+
window.__total = spec.duration; window.__fps = FPS; window.__size = [W, H];
|
|
93
|
+
// safe.fill:让开的边涂成这个颜色(竖屏片把图表做成和相邻 contain 素材同一块「卡片」,外面是片子的底色);透明模式下不涂
|
|
94
|
+
const band = (safe.fill && !spec.alpha) ? (cc => { cc.fillStyle = safe.fill; const { top: t0, bottom: b0, left: l0, right: r0 } = safe;
|
|
95
|
+
if (t0) cc.fillRect(0, 0, W, t0); if (b0) cc.fillRect(0, H - b0, W, b0); if (l0) cc.fillRect(0, 0, l0, H); if (r0) cc.fillRect(W - r0, 0, r0, H); }) : null;
|
|
96
|
+
window.renderFrame = t => { c.setTransform(1, 0, 0, 1, 0, 0); c.clearRect(0, 0, W, H); c.save(); G.draw(c, t, ctx); c.restore(); if (fin && !spec.alpha) FINISH.apply(c, t, fin, W, H, FPS); if (band) band(c); };
|
|
97
|
+
window.prepare = async () => {};
|
|
98
|
+
window.__ready = true;
|
|
99
|
+
window.renderFrame(0);
|
|
100
|
+
const sc = document.getElementById('scrub'), tt = document.getElementById('tt'); sc.max = spec.duration; sc.step = 1 / FPS;
|
|
101
|
+
sc.oninput = () => { window.renderFrame(+sc.value); tt.textContent = (+sc.value).toFixed(3); };
|
|
102
|
+
}
|
|
103
|
+
boot().catch(e => fail(String(e && e.stack || e)));
|
|
104
|
+
})();
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_说明": "参数化片段 spec 的字段表,clip_lint.py 读它。来源是各 clips/<语法>.js 头注释;改语法时同步改这里。cue.data 里写了表外的键只是告警(语法可能认得更多)。",
|
|
3
|
+
"common": {
|
|
4
|
+
"spec": ["grammar", "duration", "fps", "width", "height", "safe", "theme", "alpha", "data", "cues", "finish"],
|
|
5
|
+
"cue": ["at", "kind", "text", "sub", "data", "image", "frames", "fps", "loop", "dur"],
|
|
6
|
+
"themes": ["paper", "poster", "ink", "navy", "bauhaus", "snow", "wood", "chalk"],
|
|
7
|
+
"finish": ["clean", "soft", "film", "punch"]
|
|
8
|
+
},
|
|
9
|
+
"grammars": {
|
|
10
|
+
"t1_3b1b": { "kinds": ["title", "equation", "point", "line", "highlight"], "specData": ["camera"], "cueData": ["tokens", "col", "color", "values", "points", "target", "index", "dur"], "needs": { "equation": ["text|data.tokens"], "point": ["text"], "title": ["text"], "line": ["data.values|data.points"] } },
|
|
11
|
+
"t2_keynote_ui": { "kinds": ["title", "card", "step", "image", "highlight", "number"], "specData": ["eyebrow", "title", "subtitle", "accent", "frame", "camera"], "cueData": ["step", "fit", "focus", "icon", "value", "prefix", "suffix", "decimals", "label", "rect", "fill", "over", "frame", "text"], "needs": { "number": ["data.value"], "highlight": ["data.rect"] } },
|
|
12
|
+
"t3_finance_chart": { "kinds": ["title", "bar", "line", "candle", "highlight", "number"], "specData": ["title", "unit", "source", "badge", "chart", "series", "decimals", "prefix", "suffix", "highlight", "colors", "upDown", "theme", "camera"], "cueData": ["index", "value", "prefix", "suffix", "decimals", "text", "sub"], "needs": {}, "specNeeds": ["data.series"] },
|
|
13
|
+
"y1_kurzgesagt": { "kinds": ["title", "point", "highlight", "enter"], "specData": ["center", "center_style", "image", "flow", "camera"], "cueData": ["color", "index", "style"], "needs": { "point": ["text"], "title": ["text"] } },
|
|
14
|
+
"y2_vox": { "kinds": ["image", "clip", "title", "point", "highlight"], "specData": ["camera"], "cueData": ["halftone", "fit", "focus", "aspect", "rect"], "needs": { "highlight": ["data.rect"], "title": ["text"], "point": ["text"] } },
|
|
15
|
+
"y3_whiteboard": { "kinds": ["title", "point", "draw", "image", "highlight"], "specData": ["camera"], "cueData": ["icon", "arrow", "index", "target", "style"], "needs": { "draw": ["data.icon"], "point": ["text"], "title": ["text"] } },
|
|
16
|
+
"y4_storytime": { "kinds": ["title", "point", "highlight", "react", "insert"], "specData": ["character", "camera"], "cueData": ["pose", "mark", "hit", "count", "unit"], "needs": {}, "specNeeds": ["data.character"] },
|
|
17
|
+
"y5_kinetic_type": { "kinds": ["title", "point", "number", "highlight"], "specData": ["palette", "camera"], "cueData": ["label", "key", "word", "value", "prefix", "suffix", "decimals", "num"], "needs": { "number": ["data.value"], "point": ["text"], "title": ["text"] } }
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
// 片段 · 3b1b / manim 式讲解(t1):纯黑底,一次只放正在讲的那一个东西;字和公式都是 Write(先描边后填色),缓动一律 smooth。
|
|
2
|
+
// 招牌是 Transform:上一个概念直接变成下一个,不切、不淡出重来。
|
|
3
|
+
// cues:title(at 这一帧开始 Write;再来一个 title 时旧标题 FadeOut 上移;太长先缩字号、再折两行)
|
|
4
|
+
// equation(data.tokens = DG.math 的 token 数组,或只给 text;颜色=概念名:col 写 'BLUE_C' 等 manim 色名或 #hex)
|
|
5
|
+
// 第二个起的 equation 不重新 Write,而是从上一个 Transform 过来(TransformMatchingTex:同名符号移到新位置,对不上的淡出/淡入;默认 1.5s)
|
|
6
|
+
// point(要点一句一行,Write;新要点出来时旧的变灰)
|
|
7
|
+
// line(坐标系先 Create,再画曲线:data.values = [y...](等距)或 data.points = [[x,y]...];text = 曲线标签;data.color)
|
|
8
|
+
// 第二个起的 line 是 Transform:上一条曲线逐点(按弧长对齐重采样)形变成这一条,颜色和标签一起换,坐标系不动
|
|
9
|
+
// highlight(Indicate+黄色框住并留住:data.index = 第几个要点(只数 point,从 0 起;默认最新);data.target = 'equation' 时框公式)
|
|
10
|
+
// 布局:有 line 时横屏左文右图、竖屏上图下文;要点块在剩下的高度里竖直居中。safe:内容只排在 top..H−bottom 之间。
|
|
11
|
+
// 公式太宽就整体缩字号(所有公式同一字号,Transform 时不跳),竖屏长公式不出画。
|
|
12
|
+
// 字形:CMU 斜体把 θ 画成 ϑ,θ 改用 EB Garamond 斜体(闭合的 θ);连续两个以上的字母(softmax、ReLU)按正体单词排,单个字母才是斜体变量。
|
|
13
|
+
// alpha:透明底时字外面描一圈深色边,压在亮画面上也看得见。
|
|
14
|
+
// spec.theme(全片色板 ctx.pal):底 bg、字 ink、变灰的旧要点和坐标轴 sub、强调框与 Indicate accent、默认曲线色 accent2;
|
|
15
|
+
// cue 里写的 manim 色名也映射进色板(YELLOW/GOLD/ORANGE/RED/MAROON/PINK → accent,BLUE/TEAL/GREEN/PURPLE → accent2,GREY → sub,WHITE → ink),#hex 照写的用。
|
|
16
|
+
CLIPS.t1_3b1b = (() => {
|
|
17
|
+
const { clamp, lerp } = U;
|
|
18
|
+
let M, L, INK = '#fff';
|
|
19
|
+
const col = v => !v ? null : M[v] || v;
|
|
20
|
+
// 全片色板时 manim 色名 → 色板里的色(只认 DG.MANIM 里有的名字;#hex 原样)
|
|
21
|
+
const palMap = p => { const m = { ...DG.MANIM };
|
|
22
|
+
for (const k in m) m[k] = /^(YELLOW|GOLD|ORANGE|RED|MAROON|PINK)/.test(k) ? p.accent : /^(BLUE|TEAL|GREEN|PURPLE)/.test(k) ? p.accent2 : /^GRE[YA]_[AB]$/.test(k) ? PAL.mix(p.sub, p.ink, 0.4) : /^GRE[YA]/.test(k) ? p.sub : k === 'WHITE' ? p.ink : k === 'BLACK' ? p.bg : m[k];
|
|
23
|
+
return m; };
|
|
24
|
+
const isZh = ch => ch.codePointAt(0) >= 0x2E80;
|
|
25
|
+
// 一段字 → token:汉字/全角走普惠体;连续 ≥2 个字母是单词(正体);单个字母斜体;其余(数字、符号、希腊字母)正体
|
|
26
|
+
const toks = text => (String(text).match(/[A-Za-z]{2,}|./gu) || []).flatMap(s => s.length > 1 && !/[a-z]/.test(s) ? [...s] : [s]) // 全大写的一串(QK)是几个变量,拆开
|
|
27
|
+
.map(s => ({ s, zh: isZh(s), it: /^[a-zA-Z]$/.test(s), gap: 0 }));
|
|
28
|
+
// θ 用 EB Garamond 斜体画(CMU 的 θ 是 ϑ 字形)。只换这一个 token 的字体:逐 token 调 DG.math,期间临时改 DG.FONT
|
|
29
|
+
const THETA = /[θΘ]/;
|
|
30
|
+
const withFont = (tk, fn) => { if (!THETA.test(tk.s)) return fn(tk); const o = { ...DG.FONT }; DG.FONT.it = DG.FONT.rm = 'EBGaramond-400i';
|
|
31
|
+
try { return fn({ ...tk, zh: false, it: true }); } finally { Object.assign(DG.FONT, o); } };
|
|
32
|
+
const layoutT = (c, tokens, size) => { let x = 0; return tokens.map(tk => { const w = withFont(tk, t2 => DG.layout(c, [{ ...t2, gap: 0 }], size)[0].w), sz = (tk.size || 1) * size;
|
|
33
|
+
const r = { x, w }; x += w + (tk.gap != null ? tk.gap : sz * 0.06); return r; }); };
|
|
34
|
+
const widthT = (c, tokens, size) => { const l = layoutT(c, tokens, size); return l.length ? l[l.length - 1].x + l[l.length - 1].w : 0; };
|
|
35
|
+
// Write 一串 token(等价于 DG.math 整串调用:token 间按 manim LaggedStart 错开),返回每个 token 的屏幕 {x, w}
|
|
36
|
+
const writeT = (c, tokens, x, y, size, p, { align = 'center', col: cl = '#fff' } = {}) => {
|
|
37
|
+
const Lt = layoutT(c, tokens, size), tot = Lt.length ? Lt[Lt.length - 1].x + Lt[Lt.length - 1].w : 0, x0 = align === 'center' ? x - tot / 2 : align === 'right' ? x - tot : x;
|
|
38
|
+
const n = tokens.length, r = Math.min(4 / (n + 1), 0.2);
|
|
39
|
+
tokens.forEach((tk, i) => { const q = MO.lagged(i, n, clamp(p), r); if (q > 0) withFont(tk, t2 => DG.math(c, [t2], x0 + Lt[i].x, y, size, { p: q, align: 'left', col: cl })); });
|
|
40
|
+
return Lt.map(l => ({ x: x0 + l.x, w: l.w }));
|
|
41
|
+
};
|
|
42
|
+
// 按弧长等分重采样成 n 个点(Transform 前两条曲线要点数相同、起点对齐)
|
|
43
|
+
const resample = (pts, n) => { const cum = DG.cum(pts), T = cum[cum.length - 1]; return Array.from({ length: n }, (_, i) => DG.pointAt(pts, cum, T * i / (n - 1))); };
|
|
44
|
+
// 折行但各行等长(不让一个字母/单词孤零零挂到下一行)
|
|
45
|
+
const balance = (g, text, maxW, font) => { const n = TY.wrap(g, text, maxW, font).length; if (n < 2) return [text];
|
|
46
|
+
let lo = maxW * 0.4, hi = maxW; for (let k = 0; k < 14; k++) { const m = (lo + hi) / 2; if (TY.wrap(g, text, m, font).length > n) lo = m; else hi = m; } return TY.wrap(g, text, hi, font); };
|
|
47
|
+
// 标题:先缩字号放一行(最小 0.75),放不下就等长折行(竖屏最多 3 行、横屏 2 行),再缩到每行放得下
|
|
48
|
+
const titleLines = (c, text, maxW, size, maxLines) => {
|
|
49
|
+
let s = size; while (s > size * 0.75 && widthT(c, toks(text), s) > maxW) s *= 0.95;
|
|
50
|
+
if (widthT(c, toks(text), s) <= maxW) return { size: s, lines: [text] };
|
|
51
|
+
s = size * 0.9; let lines = balance(c, text, maxW, `${s}px "PuHui-Medium"`);
|
|
52
|
+
while (lines.length > maxLines) { s *= 0.94; lines = balance(c, text, maxW, `${s}px "PuHui-Medium"`); }
|
|
53
|
+
while (s > size * 0.45 && Math.max(...lines.map(l => widthT(c, toks(l), s))) > maxW) s *= 0.95;
|
|
54
|
+
return { size: s, lines };
|
|
55
|
+
};
|
|
56
|
+
let BUF;
|
|
57
|
+
return {
|
|
58
|
+
fonts: ['PuHui-Medium'],
|
|
59
|
+
safe: true,
|
|
60
|
+
init(ctx) {
|
|
61
|
+
M = ctx.pal ? palMap(ctx.pal) : DG.MANIM; INK = ctx.pal ? ctx.pal.ink : '#fff'; DG.FONT.zh = 'PuHui-Medium'; // 任意中文:普惠体(思源宋子集只有示范片用到的字)
|
|
62
|
+
const { W, H, u, safe } = ctx, g = document.createElement('canvas').getContext('2d');
|
|
63
|
+
const pad = (ctx.portrait ? 70 : 140) * u, yTop = safe.top, yBot = H - safe.bottom - (ctx.portrait ? 90 : 70) * u;
|
|
64
|
+
const hasLine = ctx.of('line').length > 0, hasEq = ctx.of('equation').length > 0;
|
|
65
|
+
// 标题
|
|
66
|
+
const titles = ctx.of('title').map(q => ({ q, ...titleLines(g, q.text || '', W - 2 * pad, (ctx.portrait ? 72 : 62) * u, ctx.portrait ? 3 : 2) }));
|
|
67
|
+
const tH = titles.length ? Math.max(...titles.map(z => z.lines.length * z.size * 1.25)) : 0;
|
|
68
|
+
const titleY = yTop + (ctx.portrait ? 170 : 130) * u;
|
|
69
|
+
let y = titles.length ? titleY + tH : yTop + 90 * u;
|
|
70
|
+
let eqSz = (ctx.portrait ? 72 : 66) * u;
|
|
71
|
+
for (const q of ctx.of('equation')) { const tk = q.data && q.data.tokens ? q.data.tokens : toks(q.text || ''), w = widthT(g, tk, eqSz) + 60 * u; if (w > W - 2 * pad) eqSz *= (W - 2 * pad) / w; }
|
|
72
|
+
const eqY = y + eqSz * (titles.length ? 1.15 : 0.9);
|
|
73
|
+
if (hasEq) y = eqY + eqSz * 0.6;
|
|
74
|
+
y += 30 * u;
|
|
75
|
+
// 要点:预先折行(每条可能多行)
|
|
76
|
+
const size0 = (hasLine ? (ctx.portrait ? 56 : 50) : (ctx.portrait ? 62 : 58)) * u;
|
|
77
|
+
let text, plot = null;
|
|
78
|
+
if (hasLine && ctx.portrait) { plot = { x: pad + 20 * u, y: y + 20 * u, w: W - 2 * pad - 20 * u, h: (yBot - y) * 0.5 }; text = { x: pad, y0: plot.y + plot.h + 70 * u, y1: yBot, w: W - 2 * pad }; }
|
|
79
|
+
else if (hasLine) { plot = { x: W * 0.52, y: y + 20 * u, w: W * 0.48 - pad, h: yBot - y - 40 * u }; text = { x: pad, y0: y, y1: yBot, w: W * 0.47 - pad }; }
|
|
80
|
+
else text = { x: pad, y0: y, y1: yBot, w: W - 2 * pad };
|
|
81
|
+
// 要点放不下(竖屏有曲线、要点多):先让曲线让位(竖屏 50% → 最少 30%),再缩字号(最小 0.6 倍),不压进 safe.bottom
|
|
82
|
+
let points, blockH, size = size0; const gapP = 36 * u, room = yBot - y;
|
|
83
|
+
for (let k = 1; ; k *= 0.94) {
|
|
84
|
+
size = size0 * k;
|
|
85
|
+
points = ctx.of('point').map(q => { const lines = balance(g, q.text || '', text.w - 50 * u, `${size}px "PuHui-Medium"`); return { q, lines, h: lines.length * size * 1.35 }; });
|
|
86
|
+
blockH = points.reduce((s, p) => s + p.h + gapP, 0) - gapP;
|
|
87
|
+
if (plot && ctx.portrait) { plot.h = Math.max(room * 0.3, Math.min(room * 0.5, room - 110 * u - blockH)); text.y0 = plot.y + plot.h + 70 * u; }
|
|
88
|
+
if (blockH <= text.y1 - text.y0 || k < 0.6) break;
|
|
89
|
+
}
|
|
90
|
+
if (blockH > text.y1 - text.y0 + 1) console.warn(`t1:${points.length} 条要点缩到 0.6 倍字号仍放不下(超出 ${Math.round(blockH - (text.y1 - text.y0))}px):新要点出来时整块上卷、最早的淡出;想全留在屏上就删几条或拆成两段`);
|
|
91
|
+
let py = text.y0 + Math.max(0, (text.y1 - text.y0 - blockH) / 2) * (hasLine && !ctx.portrait ? 1 : 0.6); // 要点块竖直居中(竖屏略偏上)
|
|
92
|
+
for (const p of points) { p.y = py + size; py += p.h + gapP; p.need = Math.max(0, py - gapP - text.y1); } // need:这一条出来时整块要上卷多少才不越过 y1
|
|
93
|
+
// 曲线:坐标统一用所有 line 的数据范围(Transform 时坐标系不动)
|
|
94
|
+
const lines = ctx.of('line').map(q => { const d = q.data || {}; return { q, d, pts0: d.points || (d.values || []).map((v, i) => [i, v]) }; }).filter(l => l.pts0.length >= 2);
|
|
95
|
+
if (plot && lines.length) {
|
|
96
|
+
const all = lines.flatMap(l => l.pts0), xs = all.map(p => p[0]), ys = all.map(p => p[1]);
|
|
97
|
+
const x0 = Math.min(...xs), x1 = Math.max(...xs), y0 = Math.min(0, ...ys); let y1 = Math.max(...ys); if (y1 === y0) y1 = y0 + 1;
|
|
98
|
+
plot.X = v => plot.x + (v - x0) / ((x1 - x0) || 1) * plot.w; plot.Y = v => plot.y + plot.h - (v - y0) / (y1 - y0) * plot.h * 0.9;
|
|
99
|
+
for (const l of lines) { const [lx0, lx1] = [Math.min(...l.pts0.map(p => p[0])), Math.max(...l.pts0.map(p => p[0]))];
|
|
100
|
+
// 每条曲线横向铺满坐标系(不同 line 的 x 量纲可以不同:损失-参数 → 损失-步数)
|
|
101
|
+
l.scr = l.pts0.map(([px, py]) => [plot.x + (px - lx0) / ((lx1 - lx0) || 1) * plot.w, plot.Y(py)]); l.rs = resample(l.scr, 160); }
|
|
102
|
+
}
|
|
103
|
+
L = { titleY, titles, eqY, eqSz, text, plot, points, size, lines };
|
|
104
|
+
},
|
|
105
|
+
draw(c0, t, ctx) {
|
|
106
|
+
const { W, H, u } = ctx;
|
|
107
|
+
const c = ctx.alpha ? (BUF = BUF || document.createElement('canvas'), BUF.width = W, BUF.height = H, BUF.getContext('2d')) : c0;
|
|
108
|
+
if (!ctx.alpha) { c.fillStyle = ctx.pal ? ctx.pal.bg : '#000'; c.fillRect(0, 0, W, H); }
|
|
109
|
+
// ---- 标题(同一时间只有一个,换标题 = 旧的上移淡出、新的 Write) ----
|
|
110
|
+
L.titles.forEach((z, i) => {
|
|
111
|
+
const q = z.q, nx = L.titles[i + 1], out = nx ? MO.smooth(MO.seg(t, nx.q.at - 0.45, nx.q.at)) : 0; if (out >= 1 || ctx.lt(t, q.at) <= 0) return;
|
|
112
|
+
const n = [...(q.text || '')].length, dur = q.dur || clamp(n / 14, 0.6, 1.6);
|
|
113
|
+
c.save(); c.globalAlpha = 1 - out; c.translate(0, -50 * u * out);
|
|
114
|
+
z.lines.forEach((ln, k) => writeT(c, toks(ln), W / 2, L.titleY + k * z.size * 1.25, z.size, ctx.p(t, q.at + k * dur / z.lines.length, dur / z.lines.length), { col: col(q.data && q.data.color) || INK }));
|
|
115
|
+
c.restore();
|
|
116
|
+
});
|
|
117
|
+
// ---- 公式:第一个 Write,之后每个从上一个 TransformMatchingTex 过来 ----
|
|
118
|
+
let eqBox = null;
|
|
119
|
+
const eqs = ctx.of('equation').map(q => (q.data && q.data.tokens ? q.data.tokens : toks(q.text || '')).map(k => ({ ...k, col: col(k.col) })));
|
|
120
|
+
const eqCues = ctx.of('equation'), sz = L.eqSz;
|
|
121
|
+
let k = -1; eqCues.forEach((q, i) => { if (ctx.lt(t, q.at) > 0) k = i; });
|
|
122
|
+
if (k >= 0) {
|
|
123
|
+
const q = eqCues[k], tk = eqs[k], Ln = layoutT(c, tk, sz), tot = Ln.length ? Ln[Ln.length - 1].x + Ln[Ln.length - 1].w : 0, x0 = W / 2 - tot / 2;
|
|
124
|
+
if (k === 0) writeT(c, tk, W / 2, L.eqY, sz, ctx.p(t, q.at, q.dur || 1.2), { col: INK });
|
|
125
|
+
else {
|
|
126
|
+
const p = MO.smooth(ctx.p(t, q.at, q.dur || 1.5)), old = eqs[k - 1], Lo = layoutT(c, old, sz), toto = Lo.length ? Lo[Lo.length - 1].x + Lo[Lo.length - 1].w : 0, xo = W / 2 - toto / 2;
|
|
127
|
+
const used = new Set(), from = tk.map(nt => { const j = old.findIndex((ot, jj) => !used.has(jj) && ot.s === nt.s && (ot.sub || '') === (nt.sub || '') && (ot.sup || '') === (nt.sup || '')); if (j >= 0) used.add(j); return j; });
|
|
128
|
+
const one = (tok, x, a, cl) => { if (a <= 0) return; c.save(); c.globalAlpha *= a; withFont(tok, t2 => DG.math(c, [{ ...t2, col: cl }], x, L.eqY, sz, { p: 1, align: 'left' })); c.restore(); };
|
|
129
|
+
old.forEach((ot, j) => { if (!used.has(j)) { c.save(); c.translate(0, -30 * u * p); one(ot, xo + Lo[j].x, 1 - clamp(p * 1.6), ot.col || INK); c.restore(); } }); // 对不上的旧符号:淡出上移
|
|
130
|
+
tk.forEach((nt, i) => { const j = from[i];
|
|
131
|
+
if (j >= 0) one(nt, lerp(xo + Lo[j].x, x0 + Ln[i].x, p), 1, DG.mix(old[j].col || INK, nt.col || INK, p)); // 同名符号:移到新位置,颜色插值
|
|
132
|
+
else { c.save(); c.translate(0, 30 * u * (1 - p)); one(nt, x0 + Ln[i].x, clamp((p - 0.35) / 0.65), nt.col || INK); c.restore(); } }); // 新符号:淡入
|
|
133
|
+
}
|
|
134
|
+
if (Ln.length) eqBox = { x: x0 - 20 * u, y: L.eqY - sz * 0.95, w: tot + 40 * u, h: sz * 1.35 };
|
|
135
|
+
}
|
|
136
|
+
// ---- 坐标系+曲线:第一条 Create,之后每条从上一条逐点形变过来 ----
|
|
137
|
+
const P = L.plot;
|
|
138
|
+
if (P && L.lines.length) {
|
|
139
|
+
const l0 = L.lines[0];
|
|
140
|
+
if (ctx.lt(t, l0.q.at) > 0) {
|
|
141
|
+
const ap = MO.smooth(ctx.p(t, l0.q.at, 0.6)), lw = 3 * u;
|
|
142
|
+
c.save(); c.strokeStyle = M.GREY_B; c.lineWidth = lw; c.lineCap = 'round';
|
|
143
|
+
c.beginPath(); c.moveTo(P.x, P.Y(0)); c.lineTo(P.x + P.w * ap, P.Y(0)); c.moveTo(P.x, P.y + P.h); c.lineTo(P.x, P.y + P.h - P.h * ap); c.stroke();
|
|
144
|
+
c.globalAlpha = ap; for (let kk = 1; kk <= 4; kk++) { const xx = P.x + P.w * kk / 4; c.beginPath(); c.moveTo(xx, P.Y(0) - 8 * u); c.lineTo(xx, P.Y(0) + 8 * u); c.stroke(); }
|
|
145
|
+
c.restore();
|
|
146
|
+
let j = 0; L.lines.forEach((l, i) => { if (ctx.lt(t, l.q.at) > 0) j = i; });
|
|
147
|
+
const l = L.lines[j], lc = col(l.d.color) || M.BLUE_C;
|
|
148
|
+
c.save(); c.lineWidth = 5 * u; c.lineJoin = 'round'; c.lineCap = 'round';
|
|
149
|
+
if (j === 0) {
|
|
150
|
+
const cp = MO.smooth(ctx.p(t, l.q.at + 0.5, l.q.dur || 1.4));
|
|
151
|
+
if (cp > 0) { const cum = DG.cum(l.scr); c.strokeStyle = lc; const tip = DG.drawPartial(c, l.scr, cum, cum[cum.length - 1] * cp);
|
|
152
|
+
if (cp < 1) DG.neuron(c, tip[0], tip[1], 10 * u, 1, { fill: lc, stroke: INK, lw: 2 * u }); }
|
|
153
|
+
label(c, l, MO.smooth(MO.seg(cp, 0.8, 1)), lc, u);
|
|
154
|
+
} else {
|
|
155
|
+
const pv = L.lines[j - 1], p = MO.smooth(ctx.p(t, l.q.at, l.q.dur || 1.5)), pc = col(pv.d.color) || M.BLUE_C;
|
|
156
|
+
const pts = DG.morph(pv.rs, l.rs, p); c.strokeStyle = DG.mix(pc, lc, p); c.beginPath(); pts.forEach((q, i) => i ? c.lineTo(q[0], q[1]) : c.moveTo(q[0], q[1])); c.stroke();
|
|
157
|
+
label(c, pv, 1 - clamp(p * 2), pc, u); label(c, l, clamp(p * 2 - 1), lc, u);
|
|
158
|
+
}
|
|
159
|
+
c.restore();
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// ---- 要点 ----
|
|
163
|
+
const hl = ctx.of('highlight'), size = L.size;
|
|
164
|
+
const target = h => h.data && h.data.target === 'equation' ? -2 : h.data && h.data.index != null ? h.data.index : L.points.filter(p => p.q.at <= h.at).length - 1;
|
|
165
|
+
const roll = Math.max(0, ...L.points.map(p => p.need * MO.smooth(ctx.p(t, p.q.at - 0.3, 0.4)))); // 放不下时:新要点出来前 0.3s 整块上卷,越过顶的淡出
|
|
166
|
+
L.points.forEach((it0, i) => {
|
|
167
|
+
const q = it0.q; if (ctx.lt(t, q.at) <= 0) return;
|
|
168
|
+
const it = roll ? { ...it0, y: it0.y - roll } : it0, over = L.text.y0 - (it.y - size);
|
|
169
|
+
if (over > 0 && clamp(1 - over / (size * 1.2)) <= 0) return;
|
|
170
|
+
c.save(); if (over > 0) c.globalAlpha *= clamp(1 - over / (size * 1.2));
|
|
171
|
+
const nx = L.points[i + 1], h = [...hl].reverse().find(z => target(z) === i && t >= z.at - 1e-6) || null, hOther = hl.some(z => t >= z.at && target(z) !== i && z.at > (h ? h.at : -1));
|
|
172
|
+
const dimN = nx ? MO.smooth(ctx.p(t, nx.q.at, 0.4)) : 0;
|
|
173
|
+
const n = [...(q.text || '')].length, dur = q.dur || clamp(n / 16, 0.6, 1.8);
|
|
174
|
+
// Indicate:放大 1.2、染黄、回来;之后这一条保持黄色(被强调的留住),其余变灰
|
|
175
|
+
const ip = h && !hOther ? ctx.p(t, h.at, 0.8) : 0, ind = DG.indicate(ip), keep = h && !hOther ? MO.smooth(clamp(ip * 2)) : 0;
|
|
176
|
+
const dim = keep > 0 ? 0 : dimN;
|
|
177
|
+
const base = DG.mixA(DG.mixA(INK, M.GREY_C, dim), M.YELLOW, Math.max(keep, ind.k)), colr = DG.rgba(base);
|
|
178
|
+
const bx = L.text.x + 14 * u, by = it.y - size * 0.36;
|
|
179
|
+
c.save(); c.translate(bx, by); c.scale(ind.s, ind.s); c.translate(-bx, -by);
|
|
180
|
+
DG.neuron(c, bx, by, 12 * u, 1 - dim * 0.6, { lw: 2 * u, stroke: colr, fill: col(q.data && q.data.color) || M.BLUE_C, sa: MO.smooth(ctx.p(t, q.at, 0.3)) });
|
|
181
|
+
let wMax = 0;
|
|
182
|
+
it.lines.forEach((ln, kk) => { const r = writeT(c, toks(ln), L.text.x + 50 * u, it.y + kk * size * 1.35, size, ctx.p(t, q.at + kk * dur / it.lines.length, dur / it.lines.length), { align: 'left', col: colr });
|
|
183
|
+
if (r.length) wMax = Math.max(wMax, r[r.length - 1].x + r[r.length - 1].w - (L.text.x + 50 * u)); });
|
|
184
|
+
c.restore();
|
|
185
|
+
if (h && !hOther) box(c, L.text.x - 16 * u, it.y - size * 1.05, wMax + 90 * u, it.lines.length * size * 1.35 + 18 * u, ctx.lt(t, h.at), u);
|
|
186
|
+
c.restore();
|
|
187
|
+
});
|
|
188
|
+
const he = [...hl].reverse().find(z => target(z) === -2 && t >= z.at);
|
|
189
|
+
if (he && eqBox && !hl.some(z => t >= z.at && z.at > he.at)) box(c, eqBox.x, eqBox.y, eqBox.w, eqBox.h, ctx.lt(t, he.at), u);
|
|
190
|
+
if (ctx.alpha) { c0.save(); c0.shadowColor = 'rgba(0,0,0,0.9)'; c0.shadowBlur = 10 * u; c0.drawImage(BUF, 0, 0); c0.shadowBlur = 3 * u; c0.drawImage(BUF, 0, 0); c0.restore(); }
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
// 曲线标签:放在曲线末端上方
|
|
194
|
+
function label(c, l, a, colr, u) { if (!l.q.text || a <= 0) return; const e = l.scr[l.scr.length - 1];
|
|
195
|
+
c.save(); c.globalAlpha *= a; c.font = `${40 * u}px "PuHui-Medium"`; c.fillStyle = colr; c.textAlign = 'right'; c.fillText(l.q.text, e[0], e[1] - 30 * u); c.restore(); }
|
|
196
|
+
// 强调框:Circumscribe 先绕一圈(1s),同时 SurroundingRectangle 画出来并留住(末帧有焦点)
|
|
197
|
+
function box(c, x, y, w, h, lt, u) {
|
|
198
|
+
if (lt <= 0) return; DG.circumscribe(c, x - 8 * u, y - 8 * u, w + 16 * u, h + 16 * u, MO.seg(lt, 0, 1.0), { lw: 4 * u, col: M.YELLOW });
|
|
199
|
+
const p = MO.smooth(MO.seg(lt, 0.3, 1.0)); if (p <= 0) return;
|
|
200
|
+
const pts = DG.rectPts(x + w / 2, y + h / 2, w, h, 120); c.save(); c.strokeStyle = M.YELLOW; c.lineWidth = 3 * u; c.lineJoin = 'round';
|
|
201
|
+
DG.drawPartial(c, [...pts, pts[0]], DG.cum([...pts, pts[0]]), DG.len([...pts, pts[0]]) * p); c.restore();
|
|
202
|
+
}
|
|
203
|
+
})();
|