@biunow/agent-biuart 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// 雷诺阿(印象派,人间的光):树叶筛下的斑驳光——暖黄的亮斑、蓝紫的影;人都是一团团粉彩的色点,脸颊泛红,边缘融进周围;没有黑线,黑色衣服也是深蓝紫。
|
|
2
|
+
// 题材(新构图):午后树下的露天舞场,近处一对舞者旋转,中景桌边人群说笑,光斑缓缓挪动。
|
|
3
|
+
// 原作锚定(风格,非原画):亮斑 #f6e4a0/影 #6f78b8、肤色暖粉 #eab090 带红晕、衣料粉蓝白 #e8c8d0 #a8b8dc #f4f0e4、黑衣=#2c3050、草帽黄 #e6c070、地面碎笔多向;所有边缘被光吃掉。
|
|
4
|
+
// 管线:静底(树冠+地+远景人群+桌)→ MEDIUM.broken → 缓存;光斑层(暖亮/冷影软笔)单独 broken 缓存、两份错位叠加做摇曳;近景舞者每帧小画布重绘;中景人群每个轻微摇摆(小画布)。
|
|
5
|
+
SCENES['45_renoir'] = (() => {
|
|
6
|
+
const W = 1920, H = 1080, { clamp, lerp, rng, ss } = U, P = PAINT, M = MEDIUM, TAU = Math.PI * 2;
|
|
7
|
+
let base, crowdLayer, lightA, lightB, built = false;
|
|
8
|
+
// 一个人(色点化):x,y 脚底;s 比例。女:窄肩、收腰、钟形长裙、小头戴帽;男:深蓝紫礼服、礼帽。reach=[dx,dy] 局部坐标,指定一只手去哪(相拥舞)
|
|
9
|
+
function person(g, x, y, s, o) {
|
|
10
|
+
const { dress = '#e8c8d0', trim = '#f4f0e4', hat = null, skin = '#eab090', lean = 0, arms = 0, dark = false, hair = '#8a5a3a', reach = null } = o;
|
|
11
|
+
g.save(); g.translate(x, y); g.scale(s, s); g.rotate(lean);
|
|
12
|
+
const body = dark ? '#2c3050' : dress, lit = dark ? '#4a4f82' : trim, sh = dark ? '#1c1f38' : 'rgba(110,100,150,.45)';
|
|
13
|
+
// 裙/下装:女=钟形长裙(左亮右影),男=直筒裤袍
|
|
14
|
+
if (dark) { g.fillStyle = body; g.beginPath(); g.moveTo(-15, -72); g.lineTo(-17, -4); g.lineTo(-3, -4); g.lineTo(0, -34); g.lineTo(3, -4); g.lineTo(17, -4); g.lineTo(15, -72); g.closePath(); g.fill();
|
|
15
|
+
g.fillStyle = body; g.beginPath(); g.moveTo(-15, -108); g.quadraticCurveTo(-21, -96, -17, -52); g.lineTo(17, -52); g.quadraticCurveTo(21, -96, 15, -108); g.closePath(); g.fill(); g.fillStyle = lit; g.globalAlpha = 0.5; g.fillRect(-15, -108, 6, 54); g.globalAlpha = 1;
|
|
16
|
+
g.fillStyle = '#e8e0ea'; g.beginPath(); g.moveTo(-4, -108); g.lineTo(4, -108); g.lineTo(1, -80); g.closePath(); g.fill(); }
|
|
17
|
+
else { const gr = g.createLinearGradient(-40, 0, 40, 0); gr.addColorStop(0, trim); gr.addColorStop(0.4, body); gr.addColorStop(1, sh);
|
|
18
|
+
g.fillStyle = gr; g.beginPath(); g.moveTo(-10, -104); g.quadraticCurveTo(-13, -84, -9, -68); g.quadraticCurveTo(-34, -34, -44, -3); g.quadraticCurveTo(0, 6, 44, -3); g.quadraticCurveTo(34, -34, 9, -68); g.quadraticCurveTo(13, -84, 10, -104); g.closePath(); g.fill();
|
|
19
|
+
g.strokeStyle = sh; g.lineWidth = 3; g.globalAlpha = 0.5; [[-10, -22], [6, -20], [20, -18]].forEach(([u, v]) => { g.beginPath(); g.moveTo(u * 0.3, -66); g.quadraticCurveTo(u + 4, -36, u * 1.7 + 4, v); g.stroke(); }); g.globalAlpha = 1;
|
|
20
|
+
g.fillStyle = trim; g.fillRect(-10, -72, 20, 5); g.fillStyle = '#c85a6a'; g.fillRect(-10, -70, 20, 2); }
|
|
21
|
+
// 手臂:上臂下垂,前臂抬到 arms 指定高度;reach 时一只手伸向目标
|
|
22
|
+
const sl = dark ? body : (dress), hx = 20 + arms * 12, hy = -70 - arms * 18;
|
|
23
|
+
g.strokeStyle = sl; g.lineCap = 'round'; g.lineWidth = 8; g.beginPath(); g.moveTo(-10, -100); g.lineTo(-20 - arms * 4, -80); g.lineTo(-hx, hy);
|
|
24
|
+
if (reach) { g.moveTo(10, -100); g.lineTo(22, -84); g.lineTo(reach[0], reach[1]); } else { g.moveTo(10, -100); g.lineTo(20 + arms * 4, -80); g.lineTo(hx, hy); } g.stroke();
|
|
25
|
+
g.fillStyle = skin; g.beginPath(); g.arc(-hx, hy, 4.2, 0, TAU); g.arc(reach ? reach[0] : hx, reach ? reach[1] : hy, 4.2, 0, TAU); g.fill();
|
|
26
|
+
// 颈、头(小)、脸颊红晕、发
|
|
27
|
+
g.fillStyle = skin; g.fillRect(-3.4, -117, 6.8, 11); g.beginPath(); g.ellipse(0, -126, 9.4, 11, 0, 0, TAU); g.fill();
|
|
28
|
+
g.fillStyle = 'rgba(214,90,84,.38)'; g.beginPath(); g.arc(4.5, -122, 4.4, 0, TAU); g.fill();
|
|
29
|
+
g.fillStyle = hair; g.beginPath(); g.ellipse(-1.5, -130, 9.6, 8, 0, Math.PI * 0.95, Math.PI * 2.05); g.fill(); if (!dark) { g.beginPath(); g.arc(-8, -122, 5, 0, TAU); g.fill(); }
|
|
30
|
+
g.fillStyle = '#3a2a30'; g.fillRect(4, -127, 1.8, 1.8); g.fillStyle = '#b05a54'; g.fillRect(3, -120, 4.2, 1.4);
|
|
31
|
+
if (hat === 'straw') { g.fillStyle = '#e6c070'; g.beginPath(); g.ellipse(0, -136, 21, 5.6, 0, 0, TAU); g.fill(); g.beginPath(); g.ellipse(0, -140, 11, 8, 0, Math.PI, TAU); g.fill(); g.fillStyle = '#c85a6a'; g.fillRect(-11, -140, 22, 3.4); }
|
|
32
|
+
else if (hat === 'top') { g.fillStyle = '#2c3050'; g.fillRect(-9, -153, 18, 19); g.fillRect(-14, -136, 28, 3.6); }
|
|
33
|
+
g.restore();
|
|
34
|
+
}
|
|
35
|
+
function basePlate() {
|
|
36
|
+
const cv = P.canvas(), g = cv.getContext('2d'), r = rng(5);
|
|
37
|
+
// 天光漏口+树冠:大片深绿蓝影+暖绿
|
|
38
|
+
g.fillStyle = '#9db8a0'; g.fillRect(0, 0, W, H);
|
|
39
|
+
const gr = g.createLinearGradient(0, 0, 0, 460); gr.addColorStop(0, '#5f7f6a'); gr.addColorStop(1, '#a8b890'); g.fillStyle = gr; g.fillRect(0, 0, W, 460);
|
|
40
|
+
for (let i = 0; i < 160; i++) { const x = r() * W, y = r() * 420, rx = 70 + r() * 150, ry = 40 + r() * 80; g.fillStyle = r() < 0.5 ? 'rgba(54,86,70,.55)' : r() < 0.7 ? 'rgba(170,196,120,.5)' : 'rgba(236,224,150,.4)'; g.beginPath(); g.ellipse(x, y, rx, ry, r(), 0, TAU); g.fill(); }
|
|
41
|
+
for (let i = 0; i < 40; i++) { const x = r() * W, y = r() * 330, rx = 12 + r() * 34, ry = 8 + r() * 20; g.fillStyle = 'rgba(190,214,236,.8)'; g.beginPath(); g.ellipse(x, y, rx, ry, r(), 0, TAU); g.fill(); } // 叶隙里透出的天
|
|
42
|
+
// 树干
|
|
43
|
+
[[260, 470, 70], [1060, 450, 60], [1700, 470, 80]].forEach(([x, y, w]) => { const tg = g.createLinearGradient(x - w, 0, x + w, 0); tg.addColorStop(0, '#6f6a8a'); tg.addColorStop(0.5, '#a89a8a'); tg.addColorStop(1, '#5c5878'); g.fillStyle = tg; g.fillRect(x - w / 2, 0, w, y + 80); });
|
|
44
|
+
// 地:沙土色,明暗斑
|
|
45
|
+
const ggr = g.createLinearGradient(0, 470, 0, H); ggr.addColorStop(0, '#c8b890'); ggr.addColorStop(1, '#b0a07c'); g.fillStyle = ggr; g.fillRect(0, 470, W, H - 470);
|
|
46
|
+
// 桌与远景人群(色点人)
|
|
47
|
+
const T = (x, y, w) => { g.fillStyle = '#e8e0d0'; g.beginPath(); g.ellipse(x, y, w, w * 0.22, 0, 0, TAU); g.fill(); g.fillStyle = '#a89a8a'; g.fillRect(x - 3, y, 6, w * 0.5); };
|
|
48
|
+
[[560, 640, 120], [1180, 620, 110], [1480, 700, 130], [820, 560, 90]].forEach(([x, y, w]) => T(x, y, w));
|
|
49
|
+
const cv2 = P.canvas(), g2 = cv2.getContext('2d');
|
|
50
|
+
const crowd = [[430, 640, 0.9, '#a8b8dc', 'straw'], [520, 676, 0.95, '#e8c8d0', null], [620, 650, 0.9, '#f4f0e4', 'straw'], [700, 662, 0.9, '#2c3050', 'top'], [1090, 640, 0.85, '#e8c8d0', 'straw'], [1190, 650, 0.88, '#a8b8dc', null], [1280, 636, 0.85, '#2c3050', 'top'],
|
|
51
|
+
[1400, 720, 1.0, '#f0d6dc', 'straw'], [1500, 730, 1.02, '#2c3050', 'top'], [1590, 716, 1.0, '#a8c8dc', null], [780, 590, 0.7, '#e8c8d0', 'straw'], [880, 596, 0.7, '#a8b8dc', null], [330, 600, 0.75, '#f4f0e4', null]];
|
|
52
|
+
crowd.forEach(([x, y, s, c, h], i) => person(g2, x, y, s, { dress: c, hat: h, dark: c === '#2c3050', lean: Math.sin(i * 1.7) * 0.08, arms: (i % 3) * 0.25 }));
|
|
53
|
+
crowdLayer = M.broken(cv2, { cell: 4, len: 12, wid: 4.2, passes: 2, angle: (x, y) => 1.25 + Math.sin(x * 0.05) * 0.3, seed: 19, jit: 26, skip: 0.02, sat: 1.12, warm: [255, 226, 170], cool: [130, 130, 200] });
|
|
54
|
+
return M.broken(cv, { cell: 7, len: 22, wid: 7, passes: 2, angle: (x, y) => y < 460 ? 0.7 + Math.sin(x * 0.01 + y * 0.013) * 0.35 : 0.08 + Math.sin(x * 0.012 + y * 0.01) * 0.3, seed: 11, jit: 30, skip: 0.04, warm: [255, 226, 160], cool: [120, 126, 196], sat: 1.12 });
|
|
55
|
+
}
|
|
56
|
+
function lightPlate(seed) {
|
|
57
|
+
const cv = P.canvas(), g = cv.getContext('2d'), r = rng(seed); g.clearRect(0, 0, W, H);
|
|
58
|
+
for (let i = 0; i < 90; i++) { const x = r() * W, y = 440 + r() * 600, rx = 26 + r() * 80, ry = 12 + r() * 30; g.fillStyle = r() < 0.62 ? 'rgba(252,232,150,.85)' : 'rgba(96,106,176,.6)'; g.beginPath(); g.ellipse(x, y, rx, ry, (r() - .5) * 0.6, 0, TAU); g.fill(); }
|
|
59
|
+
return M.broken(cv, { cell: 8, len: 24, wid: 8, passes: 1, angle: () => -0.3, seed: seed + 3, jit: 24, skip: 0.1, warm: [255, 238, 170], cool: [96, 110, 190], base: false });
|
|
60
|
+
}
|
|
61
|
+
function build() { base = basePlate(); lightA = lightPlate(21); lightB = lightPlate(47); built = true; }
|
|
62
|
+
// 近景舞者:小画布整体重绘(两人,随旋转交换位置)
|
|
63
|
+
function dancers(c, t) {
|
|
64
|
+
const cv = P.scratch('renoir_dance'); if (cv.width !== 760) { cv.width = 760; cv.height = 680; } const g = cv.getContext('2d'); g.reset(); g.clearRect(0, 0, 760, 680);
|
|
65
|
+
const a = t * 1.6, cx = 380, cy = 660, sway = Math.sin(a) * 0.05, S2 = 2.5;
|
|
66
|
+
const A = [cx + 18 + Math.sin(a) * 14, cy + Math.cos(a) * 6], B = [cx - 52 + Math.sin(a) * 6, cy - 10]; // 女在前、男在后左:圆舞里她旋出,他半步退后
|
|
67
|
+
const hold = [(A[0] + B[0]) / 2 + 40, A[1] - 205]; // 两人相握的手
|
|
68
|
+
const loc = (p, d) => [(hold[0] - p[0]) / S2 + d[0], (hold[1] - p[1]) / S2 + d[1]];
|
|
69
|
+
const pa = { dress: '#f4d4dc', trim: '#fff8ee', hat: 'straw', lean: 0.12 + sway, arms: 0.1, reach: loc(A, [0, 0]) }, pb = { dress: '#2c3050', dark: true, hat: 'top', lean: -0.06 + sway, arms: 0.1, skin: '#e2a888', hair: '#4a3a2a', reach: loc(B, [-10, 18]) };
|
|
70
|
+
person(g, B[0], B[1], S2, pb); person(g, A[0], A[1], S2, pa);
|
|
71
|
+
const rep = M.broken(cv, { cell: 5, len: 15, wid: 5, passes: 2, angle: (x, y) => 1.3 + Math.sin(x * 0.04) * 0.3, seed: 31, jit: 24, skip: 0.02, sat: 1.1, warm: [255, 226, 170], cool: [130, 130, 200] });
|
|
72
|
+
c.drawImage(rep, 420, 340);
|
|
73
|
+
}
|
|
74
|
+
function draw(c, lt) {
|
|
75
|
+
if (!built) build();
|
|
76
|
+
const t = clamp(lt, 0, 6);
|
|
77
|
+
c.drawImage(base, 0, 0);
|
|
78
|
+
const q = 0.5 + 0.5 * Math.sin(t * 1.3), lights = (k) => { c.save(); c.globalAlpha = k * (0.55 * (1 - q) + 0.1); c.drawImage(lightA, Math.sin(t * 0.7) * 10, Math.cos(t * 0.6) * 6); c.globalAlpha = k * (0.55 * q + 0.1); c.drawImage(lightB, -Math.sin(t * 0.6) * 10, Math.cos(t * 0.8) * 6); c.restore(); };
|
|
79
|
+
lights(1); // 光斑落在地上
|
|
80
|
+
c.drawImage(crowdLayer, 0, 0); // 人站在光里
|
|
81
|
+
lights(0.28); // 少量光斑再落到人身上:衣服上有碎光,但人不被盖没
|
|
82
|
+
dancers(c, t);
|
|
83
|
+
c.save(); c.globalCompositeOperation = 'multiply'; c.globalAlpha = 0.2; c.drawImage(P.texture('canvas_renoir', '#ece2cc', { scale: 0.006, amt: 12, grain: 7, seed: 12 }), 0, 0); c.restore();
|
|
84
|
+
}
|
|
85
|
+
return { draw };
|
|
86
|
+
})();
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// 毕沙罗(印象派田园):人是环境的一部分——人物融进树影、田垄、小路;笔触比西斯莱更短、更多向,绿色里有大量橙黄与粉的互补跳动;小路把视线带进深处。
|
|
2
|
+
// 题材(新构图):集市日的乡间小路,两位挑着篮子的农妇和一个赶着小推车的人沿路走来,路两边是开着花的果树,远处一座小教堂。
|
|
3
|
+
// 原作锚定(风格,非原画):天占三分之一、云小而碎;树冠=一团团短笔的绿、黄绿、橙、粉、淡紫;地面笔触顺着地势(田垄、小路的方向);人物只有几笔,但有重量与动势。
|
|
4
|
+
// 管线:干净的形(天/远景/树/地)→ MEDIUM.broken(短笔、多向)→ 缓存;动态:行人沿路(小画布→broken→贴回)、树冠风动(顶层细碎小笔漂)、云慢漂。
|
|
5
|
+
SCENES['46_pissarro'] = (() => {
|
|
6
|
+
const W = 1920, H = 1080, { clamp, lerp, rng, ss } = U, P = PAINT, M = MEDIUM, TAU = Math.PI * 2;
|
|
7
|
+
let sky, ground, trees, built = false;
|
|
8
|
+
const PATH = [[250, 1100], [520, 880], [820, 700], [1080, 600], [1250, 548]]; // 小路中线(从近到远)
|
|
9
|
+
const bez = (u) => { const a = PATH, n = a.length - 1, f = u * n, i = Math.min(n - 1, Math.floor(f)), q = f - i; return [lerp(a[i][0], a[i + 1][0], q), lerp(a[i][1], a[i + 1][1], q)]; };
|
|
10
|
+
const widthAt = (y) => 40 + (y - 540) * 0.55; // 路在近处更宽
|
|
11
|
+
function skyPlate() {
|
|
12
|
+
const cv = P.canvas(2400, 560), g = cv.getContext('2d'), r = rng(4);
|
|
13
|
+
const gr = g.createLinearGradient(0, 0, 0, 560); gr.addColorStop(0, '#7fa6d2'); gr.addColorStop(0.6, '#b4cadc'); gr.addColorStop(1, '#efe3c8'); g.fillStyle = gr; g.fillRect(0, 0, 2400, 560);
|
|
14
|
+
for (let k = 0; k < 18; k++) { const x = r() * 2400, y = 40 + r() * 330, s = 0.5 + r() * 0.9; g.save(); g.filter = 'blur(7px)'; for (let j = 0; j < 6; j++) { const dx = (r() - .5) * 150 * s, dy = (r() - .5) * 26 * s, rx = (34 + r() * 50) * s, ry = (16 + r() * 18) * s;
|
|
15
|
+
g.fillStyle = 'rgba(176,176,204,.7)'; g.beginPath(); g.ellipse(x + dx + 6, y + dy + ry * 0.5, rx, ry * 0.9, 0, 0, TAU); g.fill(); g.fillStyle = 'rgba(246,240,226,.95)'; g.beginPath(); g.ellipse(x + dx, y + dy, rx, ry, 0, 0, TAU); g.fill(); g.fillStyle = 'rgba(252,230,184,.5)'; g.beginPath(); g.ellipse(x + dx - rx * 0.2, y + dy - ry * 0.4, rx * 0.55, ry * 0.4, 0, 0, TAU); g.fill(); } g.restore(); }
|
|
16
|
+
return M.broken(cv, { cell: 9, len: 34, wid: 7, passes: 2, angle: () => -0.12, seed: 8, jit: 20, skip: 0.06 });
|
|
17
|
+
}
|
|
18
|
+
function groundPlate() {
|
|
19
|
+
const cv = P.canvas(), g = cv.getContext('2d'), r = rng(9);
|
|
20
|
+
// 远景:丘陵、田野带、小教堂与农舍
|
|
21
|
+
const hg = g.createLinearGradient(0, 430, 0, 560); hg.addColorStop(0, '#9fb4a8'); hg.addColorStop(1, '#8aa27a'); g.fillStyle = hg; g.beginPath(); g.moveTo(0, 520); g.quadraticCurveTo(300, 420, 640, 480); g.quadraticCurveTo(1000, 440, 1300, 470); g.quadraticCurveTo(1640, 420, 1920, 490); g.lineTo(1920, 600); g.lineTo(0, 600); g.fill();
|
|
22
|
+
g.fillStyle = '#b8a890'; g.fillRect(1250, 440, 70, 80); g.fillStyle = '#c9b8a0'; g.fillRect(1320, 458, 50, 62); g.fillStyle = '#8d5a4a'; g.beginPath(); g.moveTo(1240, 440); g.lineTo(1285, 400); g.lineTo(1330, 440); g.fill(); g.beginPath(); g.moveTo(1316, 458); g.lineTo(1345, 430); g.lineTo(1376, 458); g.fill();
|
|
23
|
+
g.fillStyle = '#a89a84'; g.fillRect(1280, 380, 26, 62); g.fillStyle = '#6f6a78'; g.beginPath(); g.moveTo(1274, 382); g.lineTo(1293, 346); g.lineTo(1312, 382); g.fill();
|
|
24
|
+
// 田:两种绿的垄(沿地势:近处宽斜,远处窄横)
|
|
25
|
+
const gg = g.createLinearGradient(0, 540, 0, H); gg.addColorStop(0, '#9bb87a'); gg.addColorStop(1, '#5f8a42'); g.fillStyle = gg; g.fillRect(0, 540, W, H - 540);
|
|
26
|
+
const FP = ['#7fa05a', '#b8c070', '#5f8a42', '#d2c47a', '#8aa86a', '#9a8a5a', '#6f9a56'];
|
|
27
|
+
for (let i = 0; i < 160; i++) { const k = Math.pow(r(), 1.6), y = 556 + k * 540, sc = 0.25 + k * 1.1, x = r() * W, wd = (60 + r() * 220) * sc, ht = (10 + r() * 30) * sc;
|
|
28
|
+
g.fillStyle = FP[(r() * FP.length) | 0]; g.globalAlpha = 0.55; g.beginPath(); g.ellipse(x, y, wd, ht, (r() - .5) * 0.12, 0, TAU); g.fill(); }
|
|
29
|
+
g.globalAlpha = 1;
|
|
30
|
+
// 树影:横过小路与田的淡紫蓝软影
|
|
31
|
+
[[470, 840, 240, 40], [1500, 930, 300, 50], [900, 690, 150, 22]].forEach(([x, y, rx, ry]) => { g.save(); g.filter = 'blur(10px)'; g.fillStyle = 'rgba(86,86,150,.38)'; g.beginPath(); g.ellipse(x, y, rx, ry, -0.06, 0, TAU); g.fill(); g.restore(); });
|
|
32
|
+
// 小路:沿 PATH 画变宽的赭色带,路边有草,路面有车辙
|
|
33
|
+
g.fillStyle = '#c9a778'; g.beginPath(); const L = [], R = [];
|
|
34
|
+
for (let k = 0; k <= 40; k++) { const [x, y] = bez(k / 40), w = widthAt(y); L.push([x - w, y]); R.push([x + w, y]); } L.forEach((p, i) => i ? g.lineTo(p[0], p[1]) : g.moveTo(p[0], p[1])); R.reverse().forEach(p => g.lineTo(p[0], p[1])); g.closePath(); g.fill();
|
|
35
|
+
g.strokeStyle = 'rgba(120,86,56,.5)'; g.lineWidth = 6; for (const o of [-0.35, 0.35]) { g.beginPath(); for (let k = 0; k <= 40; k++) { const [x, y] = bez(k / 40); const px = x + o * widthAt(y); k ? g.lineTo(px, y) : g.moveTo(px, y); } g.stroke(); }
|
|
36
|
+
return M.broken(cv, { cell: 9, len: 30, wid: 7, passes: 2, angle: (x, y) => y < 560 ? 0.02 : (Math.abs(x - bez(clamp((1100 - y) / 560))[0]) < widthAt(y) ? -0.75 : 0.1 + Math.sin(x * 0.01) * 0.2), seed: 11, jit: 24, skip: 0.05, warm: [255, 214, 140], cool: [130, 120, 190] });
|
|
37
|
+
}
|
|
38
|
+
function treesPlate() {
|
|
39
|
+
const cv = P.canvas(), g = cv.getContext('2d'), r = rng(31);
|
|
40
|
+
const tree = (x, y, h, kind, seed) => {
|
|
41
|
+
const rr = rng(seed); g.fillStyle = '#6b5a4a'; g.beginPath(); g.moveTo(x - h * 0.03, y); g.lineTo(x - h * 0.012, y - h * 0.55); g.lineTo(x + h * 0.012, y - h * 0.55); g.lineTo(x + h * 0.035, y); g.fill();
|
|
42
|
+
const pal = kind === 'bloom' ? ['#e6b4b0', '#f0d0c0', '#a8c070', '#8aa850', '#f4e4d4', '#c8a8b8'] : ['#86a850', '#b8c060', '#d6a84a', '#5f8a42', '#e0c070', '#4d6b3a'];
|
|
43
|
+
for (let i = 0; i < 70; i++) { const a = rr() * TAU, d = Math.pow(rr(), 0.7) * h * 0.36, cx = x + Math.cos(a) * d * 1.2, cy = y - h * 0.66 + Math.sin(a) * d * 0.88, rx = h * (0.035 + rr() * 0.05), ry = rx * (0.7 + rr() * 0.3);
|
|
44
|
+
const low = Math.sin(a) > 0.2, hi = Math.sin(a) < -0.35; // 下沿阴、上沿亮
|
|
45
|
+
g.fillStyle = low && rr() < 0.7 ? '#4d6b3a' : hi && rr() < 0.6 ? '#f0d878' : pal[(rr() * pal.length) | 0]; g.beginPath(); g.ellipse(cx, cy, rx, ry, rr() * 3, 0, TAU); g.fill(); }
|
|
46
|
+
};
|
|
47
|
+
tree(260, 900, 640, 'bloom', 1); tree(560, 760, 380, 'leaf', 2); tree(1560, 880, 620, 'leaf', 3); tree(1780, 780, 400, 'bloom', 4); tree(1040, 580, 200, 'leaf', 5);
|
|
48
|
+
return M.broken(cv, { cell: 8, len: 22, wid: 7, passes: 2, angle: (x, y) => 0.9 + Math.sin(x * 0.02 + y * 0.017) * 1.1, seed: 13, jit: 34, skip: 0.04, warm: [255, 216, 150], cool: [110, 130, 190], sat: 1.12 });
|
|
49
|
+
}
|
|
50
|
+
function build() { sky = skyPlate(); ground = groundPlate(); trees = treesPlate(); built = true; }
|
|
51
|
+
// 行人(小画布→broken→贴回):u 沿路位置,face 朝向,basket 篮子,kind 类型
|
|
52
|
+
function walker(c, u, kind, t, ph) {
|
|
53
|
+
const [x, y] = bez(u), s = lerp(0.34, 1.0, (y - 560) / 540), cv = P.scratch('pis_walker'); if (cv.width !== 360) { cv.width = 360; cv.height = 420; } const g = cv.getContext('2d'); g.reset(); g.clearRect(0, 0, 360, 420);
|
|
54
|
+
g.save(); g.translate(180, 380); g.scale(s * 1.6, s * 1.6); const step = Math.sin(t * 5 + ph), bob = Math.abs(step) * 3;
|
|
55
|
+
g.translate(0, -bob);
|
|
56
|
+
g.fillStyle = '#4a4a5a'; g.fillRect(-14, -38, 10, 38 + step * 4); g.fillRect(4, -38, 10, 38 - step * 4); // 腿
|
|
57
|
+
const robe = kind === 'a' ? '#5a78a8' : kind === 'b' ? '#a8584a' : '#6f7f4a';
|
|
58
|
+
g.fillStyle = robe; g.beginPath(); g.moveTo(-26, -40); g.quadraticCurveTo(-30, -96, -14, -128); g.lineTo(14, -128); g.quadraticCurveTo(30, -96, 28, -40); g.closePath(); g.fill(); // 衣
|
|
59
|
+
g.fillStyle = '#e8d8c0'; g.beginPath(); g.moveTo(-12, -128); g.lineTo(12, -128); g.lineTo(6, -100); g.lineTo(-6, -100); g.fill(); // 围裙/领
|
|
60
|
+
g.fillStyle = '#d8b090'; g.beginPath(); g.arc(0, -142, 12, 0, TAU); g.fill(); g.fillStyle = kind === 'c' ? '#3a3a3a' : '#efe6d4'; g.beginPath(); g.ellipse(0, -152, 14, 5, 0, 0, TAU); g.fill(); g.beginPath(); g.arc(0, -154, 8, Math.PI, 0); g.fill(); // 头与帽
|
|
61
|
+
if (kind !== 'c') { g.strokeStyle = '#4a3a2a'; g.lineWidth = 4; g.beginPath(); g.moveTo(-26, -118); g.quadraticCurveTo(-48, -96, -42, -70); g.stroke(); g.fillStyle = '#b88a4a'; g.beginPath(); g.ellipse(-42, -66, 20, 12, 0, 0, TAU); g.fill(); g.fillStyle = '#d6a84a'; for (let k = 0; k < 4; k++) { g.beginPath(); g.arc(-50 + k * 8, -76, 5, 0, TAU); g.fill(); } }
|
|
62
|
+
else { g.strokeStyle = '#4a3a2a'; g.lineWidth = 5; g.beginPath(); g.moveTo(14, -110); g.lineTo(80, -70); g.stroke(); g.fillStyle = '#8a6a3a'; g.fillRect(60, -64, 70, 36); g.fillStyle = '#6b8a3a'; for (let k = 0; k < 4; k++) { g.beginPath(); g.arc(70 + k * 15, -70, 9, 0, TAU); g.fill(); } g.fillStyle = '#14120f'; g.beginPath(); g.arc(78, -22, 10, 0, TAU); g.arc(120, -22, 10, 0, TAU); g.fill(); }
|
|
63
|
+
g.restore();
|
|
64
|
+
const rep = M.broken(cv, { cell: 5, len: 12, wid: 4.5, passes: 2, angle: (xx, yy) => (yy > 330 ? 0.4 : 1.3), seed: 15, jit: 20, skip: 0.02, sat: 1.08 });
|
|
65
|
+
c.drawImage(rep, x - 180, y - 380);
|
|
66
|
+
}
|
|
67
|
+
function draw(c, lt) {
|
|
68
|
+
if (!built) build();
|
|
69
|
+
const t = clamp(lt, 0, 6);
|
|
70
|
+
c.drawImage(sky, t * 12, 0, W, 560, 0, 0, W, 560);
|
|
71
|
+
c.drawImage(ground, 0, 0);
|
|
72
|
+
// 远景的行人先画(小),再画树,再画近的人,保证层次
|
|
73
|
+
walker(c, lerp(0.66, 0.34, ss(0, 6, t)), 'c', t, 2.0);
|
|
74
|
+
c.drawImage(trees, 0, 0);
|
|
75
|
+
// 树冠风动:顶层再撒一小把随风漂的叶笔
|
|
76
|
+
const r = rng(77); c.save(); c.lineCap = 'round'; for (let i = 0; i < 260; i++) { const tx = [260, 560, 1560, 1780][i % 4], ty = [330, 480, 380, 460][i % 4], a = r() * TAU, d = Math.sqrt(r()) * [230, 140, 220, 150][i % 4], ph = r() * 6;
|
|
77
|
+
const x = tx + Math.cos(a) * d + Math.sin(t * 1.6 + ph) * 5, y = ty + Math.sin(a) * d * 0.8 + Math.cos(t * 1.9 + ph) * 3, col = ['#e9b6b8', '#b8c060', '#f2d2c4', '#86a850', '#d6a84a'][(r() * 5) | 0]; c.strokeStyle = col; c.globalAlpha = 0.85; c.lineWidth = 6; c.beginPath(); c.moveTo(x - 8, y - 3); c.lineTo(x + 8, y + 3); c.stroke(); } c.restore();
|
|
78
|
+
walker(c, lerp(0.5, 0.06, ss(0, 6, t)), 'a', t, 0.0); walker(c, lerp(0.58, 0.16, ss(0, 6, t)), 'b', t, 1.3);
|
|
79
|
+
c.save(); c.globalCompositeOperation = 'multiply'; c.globalAlpha = 0.2; c.drawImage(P.texture('canvas_pis', '#e8e0cc', { scale: 0.006, amt: 12, grain: 7, seed: 5 }), 0, 0); c.restore();
|
|
80
|
+
}
|
|
81
|
+
return { draw };
|
|
82
|
+
})();
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// 西斯莱(印象派风景):天占大半、积云有暖白与淡紫灰的阴面、水面把房子拖成碎笔倒影、整幅是一笔一笔短弯笔,没有轮廓线。
|
|
2
|
+
// 题材(新构图):涨水后的村口,一位摆渡人撑船载着一只木箱穿过水面,左边是半淹的客栈,右边是光秃的白杨与电线杆。
|
|
3
|
+
// 原作锚定(fetch_reference.py:《路维希安的雪》《马尔利港的洪水》《塞纳河上的船》):天 #8fb0d0→#e9dcc0、云白 #f2ecde/阴 #aab0c4/暖 #f8e4b8、
|
|
4
|
+
// 水 #9aa89c+倒影、屋墙赭黄 #d6bd86、屋顶石板 #6d7684、近景笔触宽而横、远处窄而碎;暖亮处偏黄粉,阴暗处偏冷紫。
|
|
5
|
+
// 管线:干净的形(天/水/景)→ MEDIUM.broken 各自重画成笔触(缓存)→ 动态:云慢漂、水面逐行位移+涟漪、摆渡船与撑篙。
|
|
6
|
+
SCENES['47_sisley'] = (() => {
|
|
7
|
+
const W = 1920, H = 1080, { clamp, lerp, rng, ss } = U, P = PAINT, M = MEDIUM, TAU = Math.PI * 2;
|
|
8
|
+
const WL = 540, BASE = 722; // 水面起点(远岸脚)与房脚吃水线
|
|
9
|
+
let sky, water, scene, built = false;
|
|
10
|
+
function skyPlate() {
|
|
11
|
+
const cv = P.canvas(2600, 600), g = cv.getContext('2d');
|
|
12
|
+
const gr = g.createLinearGradient(0, 0, 0, 600); gr.addColorStop(0, '#6a98c8'); gr.addColorStop(0.55, '#a8c0d8'); gr.addColorStop(1, '#ebdfc2'); g.fillStyle = gr; g.fillRect(0, 0, 2600, 600);
|
|
13
|
+
const r = rng(17);
|
|
14
|
+
const cloud = (x, y, s) => {
|
|
15
|
+
const blobs = Array.from({ length: 9 }, () => [x + (r() - .5) * 300 * s, y + (r() - .5) * 60 * s, (50 + r() * 70) * s, (30 + r() * 36) * s]);
|
|
16
|
+
g.save(); g.filter = 'blur(10px)';
|
|
17
|
+
blobs.forEach(([bx, by, rx, ry]) => { g.fillStyle = 'rgba(166,170,196,.75)'; g.beginPath(); g.ellipse(bx + 8 * s, by + ry * 0.55, rx * 1.02, ry * 0.9, 0, 0, TAU); g.fill(); }); // 阴面:淡紫灰,压在下
|
|
18
|
+
blobs.forEach(([bx, by, rx, ry]) => { g.fillStyle = 'rgba(244,238,224,.95)'; g.beginPath(); g.ellipse(bx, by, rx, ry, 0, 0, TAU); g.fill(); }); // 本体:暖白
|
|
19
|
+
blobs.forEach(([bx, by, rx, ry]) => { g.fillStyle = 'rgba(252,232,186,.55)'; g.beginPath(); g.ellipse(bx - rx * 0.2, by - ry * 0.45, rx * 0.6, ry * 0.4, 0, 0, TAU); g.fill(); }); // 受光:暖黄
|
|
20
|
+
g.restore();
|
|
21
|
+
};
|
|
22
|
+
[[260, 160, 1.5], [820, 110, 1.2], [1320, 190, 1.7], [1900, 120, 1.3], [2380, 210, 1.5], [620, 330, 0.9], [1560, 380, 0.8], [2100, 400, 0.9], [60, 380, 0.8]].forEach(([x, y, s]) => cloud(x, y, s));
|
|
23
|
+
return M.broken(cv, { cell: 10, len: 54, wid: 10, passes: 2, angle: (x, y) => -0.06 + Math.sin(x * 0.004) * 0.05, seed: 3, jit: 18, skip: 0.06 });
|
|
24
|
+
}
|
|
25
|
+
function above(g) { // 水面之上的东西:远岸、客栈、电线杆、白杨
|
|
26
|
+
const r = rng(23);
|
|
27
|
+
// 远岸一带:低矮房屋+灌木,偏雾蓝灰(空气透视)
|
|
28
|
+
g.fillStyle = '#9fa9a8'; g.fillRect(0, 486, W, 60);
|
|
29
|
+
[[700, 470, 80, 56, '#b9a58a'], [790, 480, 60, 44, '#a99a86'], [880, 462, 100, 66, '#b7a690'], [1010, 476, 70, 50, '#a99a86'], [1120, 470, 120, 58, '#b9a58a']].forEach(([x, y, w, h, col], i) => { g.fillStyle = col; g.fillRect(x, y, w, h); g.fillStyle = '#7d7f8a'; g.beginPath(); g.moveTo(x - 6, y); g.lineTo(x + w / 2, y - 24); g.lineTo(x + w + 6, y); g.fill(); });
|
|
30
|
+
g.fillStyle = '#7f8e72'; for (let i = 0; i < 40; i++) { g.beginPath(); g.ellipse(560 + r() * 1300, 520 + r() * 20, 30 + r() * 30, 16 + r() * 14, 0, 0, TAU); g.fill(); }
|
|
31
|
+
// 客栈(左):两个立面——正面赭黄、侧面阴面更暗;陡屋顶+老虎窗;一层是浅色招牌带(不写字)
|
|
32
|
+
const fx = 40, fw = 470, top = 250, sideW = 150;
|
|
33
|
+
g.fillStyle = '#d8bf88'; g.fillRect(fx, top, fw, BASE - top);
|
|
34
|
+
g.fillStyle = '#b8a070'; g.beginPath(); g.moveTo(fx + fw, top); g.lineTo(fx + fw + sideW, top + 40); g.lineTo(fx + fw + sideW, BASE - 6); g.lineTo(fx + fw, BASE); g.fill();
|
|
35
|
+
g.fillStyle = '#6a7482'; g.beginPath(); g.moveTo(fx - 20, top + 6); g.lineTo(fx + fw * 0.45, top - 130); g.lineTo(fx + fw + sideW + 20, top + 36); g.lineTo(fx + fw + 8, top + 18); g.lineTo(fx + fw * 0.45, top - 96); g.closePath(); g.fill();
|
|
36
|
+
g.fillStyle = '#4e5766'; g.beginPath(); g.moveTo(fx + fw * 0.45, top - 130); g.lineTo(fx + fw + sideW + 20, top + 36); g.lineTo(fx + fw + sideW, top + 50); g.lineTo(fx + fw * 0.45, top - 100); g.fill();
|
|
37
|
+
g.fillStyle = '#6a7482'; g.fillRect(fx + 270, top - 40, 70, 62); g.fillStyle = '#4e5766'; g.beginPath(); g.moveTo(fx + 262, top - 40); g.lineTo(fx + 305, top - 76); g.lineTo(fx + 348, top - 40); g.fill(); g.fillStyle = '#3a4150'; g.fillRect(fx + 286, top - 26, 38, 40);
|
|
38
|
+
[[fx + 40, top + 60], [fx + 150, top + 60], [fx + 260, top + 60], [fx + 370, top + 60]].forEach(([x, y]) => { g.fillStyle = '#7a8a86'; g.fillRect(x - 10, y, 18, 96); g.fillRect(x + 62, y, 18, 96); g.fillStyle = '#3d4348'; g.fillRect(x + 8, y + 4, 54, 90); g.fillStyle = 'rgba(210,225,235,.55)'; g.fillRect(x + 12, y + 8, 46, 36); });
|
|
39
|
+
g.fillStyle = '#efe3bd'; g.fillRect(fx, top + 200, fw, 56); g.fillStyle = '#c8b27a'; g.fillRect(fx, top + 256, fw, 6); // 招牌带
|
|
40
|
+
[[fx + 60, top + 275], [fx + 220, top + 275]].forEach(([x, y]) => { g.fillStyle = '#7a8a86'; g.fillRect(x - 8, y, 14, 150); g.fillRect(x + 58, y, 14, 150); g.fillStyle = '#31373c'; g.fillRect(x + 6, y + 4, 52, 140); }); // 一层的窗
|
|
41
|
+
g.fillStyle = '#6b4d34'; g.fillRect(fx + 360, top + 290, 70, 132); g.fillStyle = '#2a2420'; g.fillRect(fx + 368, top + 300, 54, 120); // 门(半淹)
|
|
42
|
+
// 白杨:细长的光秃树,枝丫上挑
|
|
43
|
+
const poplar = (x, base, hgt, seed) => { const rr = rng(seed); g.strokeStyle = '#7d7566'; g.lineCap = 'round'; g.lineWidth = 11; g.beginPath(); g.moveTo(x, base); g.lineTo(x + 6, base - hgt); g.stroke();
|
|
44
|
+
for (let k = 0; k < 22; k++) { const y = base - hgt * (0.18 + rr() * 0.8), side = rr() < 0.5 ? -1 : 1, L = 60 + rr() * 110 * (1 - (base - y) / hgt * 0.6); g.lineWidth = 3 + rr() * 3; g.beginPath(); g.moveTo(x + 3, y); g.quadraticCurveTo(x + side * L * 0.4, y - L * 0.5, x + side * L * 0.55, y - L); g.stroke(); } };
|
|
45
|
+
poplar(1600, BASE + 20, 560, 5); poplar(1740, BASE + 60, 500, 6); poplar(1830, BASE + 30, 430, 7);
|
|
46
|
+
// 电线杆
|
|
47
|
+
g.fillStyle = '#6a5d4e'; g.fillRect(1292, 160, 18, BASE - 150); g.fillRect(1250, 190, 102, 10); g.strokeStyle = '#6a5d4e'; g.lineWidth = 3; g.beginPath(); g.moveTo(1250, 194); g.quadraticCurveTo(900, 250, 600, 214); g.moveTo(1352, 194); g.quadraticCurveTo(1650, 240, 1920, 200); g.stroke();
|
|
48
|
+
}
|
|
49
|
+
function build() {
|
|
50
|
+
sky = skyPlate();
|
|
51
|
+
// 干净的景(透明底)
|
|
52
|
+
const sp = P.canvas(), sg = sp.getContext('2d'); above(sg);
|
|
53
|
+
// 水:底色+倒影(景的竖向镜像,压暗压冷)+天光碎片
|
|
54
|
+
const wp = P.canvas(), wg = wp.getContext('2d'); const gr = wg.createLinearGradient(0, WL, 0, H); gr.addColorStop(0, '#c4cbc0'); gr.addColorStop(0.4, '#9fb0aa'); gr.addColorStop(1, '#788a82'); wg.fillStyle = gr; wg.fillRect(0, WL, W, H - WL);
|
|
55
|
+
wg.save(); wg.beginPath(); wg.rect(0, WL, W, H - WL); wg.clip(); wg.translate(0, BASE * 2 - 2); wg.scale(1, -1); wg.globalAlpha = 0.62; wg.filter = 'blur(3px) brightness(.82) saturate(.9)'; wg.drawImage(sp, 0, 0); wg.restore();
|
|
56
|
+
const r = rng(41); wg.globalAlpha = 1; for (let i = 0; i < 90; i++) { const x = r() * W, y = WL + 30 + r() * (H - WL), w = 40 + r() * 190; wg.fillStyle = `rgba(${r() < 0.6 ? '236,236,224' : '150,170,176'},${0.12 + r() * 0.2})`; wg.fillRect(x, y, w, 3 + r() * 5); }
|
|
57
|
+
water = M.broken(wp, { cell: 11, len: 70, wid: 8, passes: 2, angle: () => 0, seed: 5, jit: 16, skip: 0.04, cool: [96, 120, 156], warm: [236, 222, 176] });
|
|
58
|
+
scene = M.broken(sp, { cell: 8, len: 26, wid: 8, passes: 2, angle: (x, y) => x < 700 && y < 330 ? (x < 280 ? -0.5 : 0.5) : (x > 1500 ? 1.45 : (x > 1270 && x < 1330 && y < 740 ? 1.57 : (y < 520 ? 0.02 : 1.5))), seed: 9, jit: 18, skip: 0.04 });
|
|
59
|
+
built = true;
|
|
60
|
+
}
|
|
61
|
+
function boat(c, t) {
|
|
62
|
+
const u = ss(0, 6, t), X = lerp(1380, 840, u), Y = 842 + Math.sin(t * 1.5) * 3, tilt = Math.sin(t * 1.5 + 1) * 0.025, pole = Math.sin(t * 2.2) * 0.5 + 0.5;
|
|
63
|
+
const cvs = P.scratch('sisley_boat'); if (cvs.width !== 520) { cvs.width = 520; cvs.height = 360; } const cg = cvs.getContext('2d'); cg.reset(); cg.clearRect(0, 0, 520, 360);
|
|
64
|
+
const x = 260, y = 230, cc = c; c = cg;
|
|
65
|
+
// 倒影(先画,暗、窄、被水纹打碎)
|
|
66
|
+
c.save(); c.translate(x, y + 40); c.scale(1, -0.5); c.globalAlpha = 0.28; c.fillStyle = '#2d2a28'; c.beginPath(); c.moveTo(-130, 0); c.quadraticCurveTo(0, 30, 130, 0); c.lineTo(100, -36); c.lineTo(-100, -36); c.fill(); c.fillRect(-8, -150, 16, 114); c.restore();
|
|
67
|
+
c.save(); c.translate(x, y); c.rotate(tilt);
|
|
68
|
+
c.fillStyle = '#3a2f2a'; c.beginPath(); c.moveTo(-150, -8); c.quadraticCurveTo(-110, 34, 0, 36); c.quadraticCurveTo(110, 34, 150, -14); c.lineTo(120, -30); c.lineTo(-120, -28); c.closePath(); c.fill();
|
|
69
|
+
c.fillStyle = '#6d5a42'; c.beginPath(); c.moveTo(-150, -8); c.lineTo(-120, -28); c.lineTo(120, -30); c.lineTo(150, -14); c.lineTo(110, -8); c.lineTo(-110, -4); c.closePath(); c.fill();
|
|
70
|
+
c.fillStyle = '#c9b88a'; c.beginPath(); c.moveTo(26, -26); c.quadraticCurveTo(20, -88, 70, -96); c.quadraticCurveTo(122, -88, 116, -26); c.closePath(); c.fill(); c.strokeStyle = '#6b5a3a'; c.lineWidth = 4; c.beginPath(); c.moveTo(48, -92); c.quadraticCurveTo(70, -80, 94, -92); c.stroke(); // 麻袋
|
|
71
|
+
// 摆渡人:深色身形,白帽,竿从右手斜入水
|
|
72
|
+
c.fillStyle = '#2a3340'; c.beginPath(); c.moveTo(-52, -26); c.quadraticCurveTo(-60, -90, -46, -126); c.lineTo(-18, -126); c.quadraticCurveTo(-8, -90, -14, -26); c.fill();
|
|
73
|
+
c.fillStyle = '#d9b48c'; c.beginPath(); c.arc(-32, -142, 13, 0, TAU); c.fill(); c.fillStyle = '#e8e0cc'; c.beginPath(); c.ellipse(-32, -152, 20, 7, 0, 0, TAU); c.fill();
|
|
74
|
+
const px = -14 + pole * 8, ang = -0.5 - pole * 0.15; c.strokeStyle = '#4a3a2a'; c.lineWidth = 6; c.lineCap = 'round'; c.beginPath(); c.moveTo(-18, -104 - pole * 6); c.lineTo(px + Math.cos(ang) * -210 * -1, 30 + pole * 14 + 0); c.stroke();
|
|
75
|
+
c.restore();
|
|
76
|
+
const rep = M.broken(cvs, { cell: 7, len: 18, wid: 6, passes: 2, angle: (xx, yy) => (yy > 250 ? 0 : 1.2), seed: 12, jit: 14, skip: 0.02, base: true });
|
|
77
|
+
cc.drawImage(rep, X - 260, Y - 230);
|
|
78
|
+
c = cc; const x2 = X, y2 = Y;
|
|
79
|
+
// 船行水纹:船尾拖出两道渐开的 V
|
|
80
|
+
c.save(); c.strokeStyle = 'rgba(244,240,226,.55)'; c.lineWidth = 3; c.lineCap = 'round'; for (let k = 0; k < 3; k++) { const q = ((t * 0.5 + k / 3) % 1); c.globalAlpha = (1 - q) * 0.8; c.beginPath(); c.ellipse(x2 + 90 + q * 220, y2 + 34, 40 + q * 110, 6 + q * 14, 0, Math.PI * 0.05, Math.PI * 0.95); c.stroke(); } c.restore();
|
|
81
|
+
}
|
|
82
|
+
function draw(c, lt) {
|
|
83
|
+
if (!built) build();
|
|
84
|
+
const t = clamp(lt, 0, 6);
|
|
85
|
+
c.drawImage(sky, t * 18, 0, W, 600, 0, 0, W, 600); // 云慢漂
|
|
86
|
+
c.fillStyle = '#ebdfc2'; c.fillRect(0, 560, W, WL - 540 + 40);
|
|
87
|
+
// 水:逐行位移,越近越大(倒影被水纹拉碎)
|
|
88
|
+
for (let y = WL; y < H; y += 6) { const k = (y - WL) / (H - WL), dx = Math.sin(t * 1.3 + y * 0.09) * (1.5 + k * 9) + Math.sin(t * 2.7 + y * 0.21) * 1.5 * k; c.drawImage(water, 0, y, W, 6, dx - 24, y, W + 48, 6); }
|
|
89
|
+
c.drawImage(scene, 0, 0);
|
|
90
|
+
boat(c, t);
|
|
91
|
+
c.save(); c.globalCompositeOperation = 'multiply'; c.globalAlpha = 0.22; c.drawImage(P.texture('canvas_sisley', '#e8e0cc', { scale: 0.006, amt: 12, grain: 7, seed: 3 }), 0, 0); c.restore(); // 画布纹
|
|
92
|
+
}
|
|
93
|
+
return { draw };
|
|
94
|
+
})();
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// 高更(综合主义/掐丝珐琅):不写实的饱和色场、粗而不均的深群青轮廓、斜线分割画面的树干、被画框裁掉的前景人物、没有投影。
|
|
2
|
+
// 题材(新构图):红色草场上,三位戴白帽的妇人(前景,裁切)看着一个男孩牵山羊横穿画面;一棵斜树把画面劈开。
|
|
3
|
+
// 原作锚定(fetch_reference.py:《布道后的幻象》《黄色基督》《我们从哪里来》):朱红 #d63a1c 色场、黄赭 #e0a526 地、
|
|
4
|
+
// 紫褐树干 #5b2c48、白帽 #efece2+蓝灰影 #a9b4cf、黑裙 #1b1a22、轮廓深群青 #1d2a52;没有透视收束,远近靠重叠和裁切。
|
|
5
|
+
// 管线:色场(缓存,顺笔向短条)→ 斜树(缓存)→ 黄地 → 动态:山羊与男孩(走)、前景妇人(风吹帽翼)、叶团摇 → 无投影,不加光效。
|
|
6
|
+
SCENES['48_gauguin'] = (() => {
|
|
7
|
+
const W = 1920, H = 1080, { clamp, lerp, rng, ss } = U, P = PAINT, M = MEDIUM, TAU = Math.PI * 2;
|
|
8
|
+
const LINE = '#1d2a52';
|
|
9
|
+
let layer;
|
|
10
|
+
const poly = (pts, close = true) => { const p = new Path2D(); pts.forEach((q, i) => i ? p.lineTo(q[0], q[1]) : p.moveTo(q[0], q[1])); if (close) p.closePath(); return p; };
|
|
11
|
+
// Catmull 式平滑闭合/开放曲线:高更什么都是圆的,不要折线
|
|
12
|
+
const smooth = (pts, close = true) => { const p = new Path2D(), n = pts.length; if (close) { const m = i => [(pts[i % n][0] + pts[(i + 1) % n][0]) / 2, (pts[i % n][1] + pts[(i + 1) % n][1]) / 2]; p.moveTo(...m(n - 1)); for (let i = 0; i < n; i++) p.quadraticCurveTo(pts[i][0], pts[i][1], ...m(i)); p.closePath(); }
|
|
13
|
+
else { p.moveTo(...pts[0]); for (let i = 1; i < n - 1; i++) p.quadraticCurveTo(pts[i][0], pts[i][1], (pts[i][0] + pts[i + 1][0]) / 2, (pts[i][1] + pts[i + 1][1]) / 2); p.lineTo(...pts[n - 1]); } return p; };
|
|
14
|
+
const blob = (cx, cy, rx, ry, rot = 0) => { const p = new Path2D(); p.ellipse(cx, cy, rx, ry, rot, 0, TAU); return p; };
|
|
15
|
+
function build() {
|
|
16
|
+
layer = P.canvas(); const g = layer.getContext('2d');
|
|
17
|
+
// 朱红色场:整幅铺满,笔向斜 30°,同色里有橙红/深红的条
|
|
18
|
+
M.flat(g, poly([[0, 0], [W, 0], [W, H], [0, H]]), '#d63a1c', { streak: 0.5, vari: 34, lw: 0, seed: 1, n: 520 });
|
|
19
|
+
// 黄赭地:右下一块大而圆的坡,波浪形上缘
|
|
20
|
+
const ground = new Path2D(); ground.moveTo(520, H); ground.bezierCurveTo(700, 820, 980, 700, 1280, 730); ground.bezierCurveTo(1560, 760, 1740, 700, W, 640); ground.lineTo(W, H); ground.closePath();
|
|
21
|
+
M.flat(g, ground, '#e0a526', { streak: -0.15, vari: 26, lw: 7, seed: 2, n: 300, line: LINE });
|
|
22
|
+
// 紫褐小径:沿地面斜向的一条
|
|
23
|
+
M.flat(g, poly([[900, 1010], [1260, 840], [1420, 860], [1060, 1060]]), '#7a3a5a', { streak: -0.45, vari: 20, lw: 5, seed: 3, n: 80, line: LINE });
|
|
24
|
+
// 斜树干:从左上劈向右下,上粗下细再略弯,把画面分成两半
|
|
25
|
+
const trunk = smooth([[620, -40], [830, -40], [970, 150], [1100, 330], [1300, 490], [1480, 590], [1580, 730], [1450, 760], [1350, 690], [1170, 620], [1090, 520], [930, 340], [770, 200], [670, 90]]);
|
|
26
|
+
M.flat(g, trunk, '#5b2c48', { streak: 0.8, vari: 26, lw: 8, seed: 4, n: 180, line: LINE, bounds: [500, 0, 1120, 780] });
|
|
27
|
+
M.flat(g, smooth([[640, -30], [760, -30], [940, 170], [1120, 360], [1300, 520], [1420, 600], [1330, 600], [1160, 480], [940, 300], [720, 120]]), '#8d4a66', { streak: 0.8, vari: 14, lw: 0, seed: 5, n: 50, bounds: [500, 0, 1100, 760] });
|
|
28
|
+
// (树干上原有三个暗斑,读成笛孔,已去掉)
|
|
29
|
+
// 枝:树干中段向右上分出两枝,叶团另画(动态)
|
|
30
|
+
M.flat(g, smooth([[1200, 400], [1380, 250], [1560, 140], [1630, 190], [1500, 330], [1310, 480]]), '#5b2c48', { streak: -0.7, vari: 20, lw: 7, seed: 6, n: 70, line: LINE, bounds: [1200, 140, 460, 340] });
|
|
31
|
+
M.flat(g, smooth([[980, 280], [1090, 150], [1190, 30], [1250, 70], [1170, 220], [1090, 340]]), '#5b2c48', { streak: -1.0, vari: 20, lw: 7, seed: 7, n: 40, line: LINE, bounds: [980, 30, 280, 300] });
|
|
32
|
+
}
|
|
33
|
+
function leaves(c, t) { // 叶团:深绿大圆块,叠几个黄点(果),整团微摇
|
|
34
|
+
[[1590, 150, 190, 120, 0.2], [1250, 70, 150, 100, -0.3], [1460, 300, 120, 80, 0.1]].forEach(([x, y, rx, ry, rot], i) => {
|
|
35
|
+
const sw = Math.sin(t * 1.6 + i * 1.7) * 7, ox = x + sw, oy = y + Math.sin(t * 2.1 + i) * 3, rr = rng(70 + i), p = smooth(Array.from({ length: 11 }, (_, k) => { const a = k / 11 * TAU, j = 0.82 + rr() * 0.3; return [ox + Math.cos(a) * rx * j, oy + Math.sin(a) * ry * j]; }));
|
|
36
|
+
M.flat(c, p, '#2f5a3a', { streak: 0.2, vari: 22, lw: 7, seed: 10 + i, n: 90, line: LINE, bounds: [x - rx, y - ry, rx * 2, ry * 2] });
|
|
37
|
+
const r = rng(30 + i); for (let k = 0; k < 6; k++) { const a = r() * TAU, d = r() * 0.7; M.flat(c, blob(x + sw + Math.cos(a) * rx * d, y + Math.sin(a) * ry * d, 15, 13), '#f2c335', { vari: 8, lw: 3.5, seed: 40 + k, n: 6, line: LINE, bounds: [x - rx, y - ry, rx * 2, ry * 2] }); }
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
// 戴白帽的妇人(布列塔尼):黑裙大圆块+肩上白披领;侧脸朝右(前额—鼻—唇—下巴一条干净的曲线);高白帽冠立在头顶、帽翼垂在脑后随风;back=true 背对
|
|
41
|
+
function woman(c, x, y, s, t, seed, bow, back) {
|
|
42
|
+
const sw = Math.sin(t * 2.2 + seed) * 8 * s;
|
|
43
|
+
c.save(); c.translate(x, y); c.scale(s, s);
|
|
44
|
+
M.flat(c, smooth([[-170, 0], [-190, -200], [-150, -360], [-60, -430], [60, -430], [150, -340], [190, -190], [175, 0]]), '#1b1a22', { streak: 1.2, vari: 12, lw: 8, seed: seed, n: 60, line: LINE, bounds: [-190, -440, 380, 440] });
|
|
45
|
+
// 白披领:肩上一圈,前襟 V 形,蓝灰影在一侧
|
|
46
|
+
M.flat(c, smooth([[-120, -420], [-30, -470], [80, -462], [150, -400], [90, -350], [10, -330], [-70, -350]]), '#efece2', { streak: 0.3, vari: 8, lw: 7, seed: seed + 1, n: 16, line: LINE, bounds: [-130, -475, 290, 150] });
|
|
47
|
+
M.flat(c, smooth([[-40, -352], [10, -332], [80, -352], [40, -380]]), '#a9b4cf', { vari: 6, lw: 0, seed: seed + 8, n: 6, bounds: [-40, -380, 130, 50] });
|
|
48
|
+
const hy = -560 + bow * 14;
|
|
49
|
+
// 帽翼(脑后垂下,随风)
|
|
50
|
+
M.flat(c, smooth([[-10, hy + 30], [-70 - sw, hy + 20], [-150 - sw * 1.6, hy + 90], [-120 - sw, hy + 170], [-50, hy + 140], [-10, hy + 90]]), '#efece2', { streak: 0.5, vari: 8, lw: 7, seed: seed + 2, n: 16, line: LINE, bounds: [-160, hy + 10, 160, 170] });
|
|
51
|
+
M.flat(c, smooth([[-100 - sw, hy + 110], [-60, hy + 130], [-40, hy + 100]]), '#a9b4cf', { vari: 6, lw: 0, seed: seed + 5, n: 4, bounds: [-110, hy + 90, 80, 50] });
|
|
52
|
+
if (!back) {
|
|
53
|
+
// 脸:侧向右,一条连续的曲线;颈接到披领
|
|
54
|
+
M.flat(c, smooth([[-10, hy + 0], [52, hy - 6], [92, hy + 22], [96, hy + 56], [126, hy + 90], [100, hy + 98], [104, hy + 118], [94, hy + 134], [98, hy + 160], [60, hy + 188], [10, hy + 176], [-20, hy + 140], [-24, hy + 70]]), '#e8b078', { streak: 1.2, vari: 10, lw: 7, seed: seed + 6, n: 14, line: LINE, bounds: [-30, hy - 10, 160, 200] });
|
|
55
|
+
c.strokeStyle = LINE; c.lineWidth = 5.5; c.lineCap = 'round';
|
|
56
|
+
c.beginPath(); c.moveTo(56, hy + 52); c.quadraticCurveTo(76, hy + 64, 96, hy + 52); c.stroke(); // 闭眼
|
|
57
|
+
c.beginPath(); c.moveTo(50, hy + 34); c.quadraticCurveTo(76, hy + 24, 98, hy + 34); c.stroke(); // 眉
|
|
58
|
+
c.strokeStyle = '#8a3a30'; c.lineWidth = 6; c.beginPath(); c.moveTo(74, hy + 138); c.quadraticCurveTo(90, hy + 144, 102, hy + 134); c.stroke(); // 唇:平静
|
|
59
|
+
M.flat(c, smooth([[8, hy + 90], [40, hy + 120], [30, hy + 160], [-6, hy + 120]]), '#d49a66', { vari: 6, lw: 0, seed: seed + 9, n: 4, bounds: [-6, hy + 90, 50, 70] }); // 颊下影
|
|
60
|
+
}
|
|
61
|
+
// 帽冠:高、圆顶、正面略宽,压在额头之上(不盖脸)
|
|
62
|
+
M.flat(c, smooth([[-26, hy + 70], [-34, hy - 40], [-4, hy - 120], [58, hy - 150], [108, hy - 100], [112, hy - 20], [86, hy + 6], [20, hy + 12]]), '#efece2', { streak: 1.3, vari: 8, lw: 8, seed: seed + 4, n: 26, line: LINE, bounds: [-40, hy - 160, 160, 180] });
|
|
63
|
+
M.flat(c, smooth([[-26, hy - 30], [-4, hy - 110], [20, hy - 100], [0, hy + 20]]), '#a9b4cf', { vari: 6, lw: 0, seed: seed + 3, n: 6, bounds: [-30, hy - 120, 60, 150] });
|
|
64
|
+
c.restore();
|
|
65
|
+
}
|
|
66
|
+
// 男孩和山羊:向左走。羊在后,绳牵在男孩手里;步态用腿相位摆
|
|
67
|
+
function boyGoat(c, t) {
|
|
68
|
+
const u = ss(0.0, 6, t), x = lerp(1830, 1420, u), step = Math.sin(t * 7), bob = Math.abs(step) * 4, y = 808 + lerp(0, 22, u);
|
|
69
|
+
c.save(); c.translate(x, y - bob); c.scale(0.58, 0.58);
|
|
70
|
+
// 羊(朝左):长躯干+粗颈+楔形头+后弯角+细腿带膝;远侧两腿先画,近侧后画;肚下蓝灰影
|
|
71
|
+
const leg = (dx, ph, near) => { const sw = Math.sin(t * 7 + ph) * 26, kx = dx + sw * 0.4, fx = dx + sw; return smooth([[dx - 10, -70], [dx + 10, -70], [kx + 8, -36], [fx + 7, 6], [fx + 10, 20], [fx - 14, 20], [fx - 8, 4], [kx - 8, -34]]); };
|
|
72
|
+
[[-62, 3.1, 0], [58, 0, 0]].forEach(([dx, ph], i) => M.flat(c, leg(dx, ph), '#c9c8c4', { vari: 6, lw: 6, seed: 60 + i, n: 4, line: LINE, bounds: [dx - 30, -74, 70, 110] }));
|
|
73
|
+
M.flat(c, smooth([[-96, -126], [-40, -156], [50, -158], [120, -138], [134, -100], [104, -66], [30, -58], [-50, -60], [-98, -82]]), '#efece2', { streak: 0.05, vari: 10, lw: 8, seed: 66, n: 40, line: LINE, bounds: [-100, -160, 240, 100] });
|
|
74
|
+
M.flat(c, smooth([[-100, -86], [-30, -62], [60, -64], [118, -86], [100, -66], [30, -58], [-50, -60]]), '#a9b4cf', { vari: 6, lw: 0, seed: 65, n: 8, bounds: [-110, -90, 240, 40] });
|
|
75
|
+
[[-30, -110, 34, 22], [40, -130, 30, 20], [70, -90, 24, 16]].forEach(([x, y, rx, ry], i) => M.flat(c, blob(x, y, rx, ry, 0.3), '#6a3a2a', { vari: 8, lw: 5, seed: 80 + i, n: 6, line: LINE, bounds: [x - rx, y - ry, rx * 2, ry * 2] }));
|
|
76
|
+
M.flat(c, smooth([[-96, -128], [-100, -198], [-140, -242], [-196, -236], [-224, -200], [-216, -152], [-170, -148], [-130, -110]]), '#efece2', { streak: 0.6, vari: 8, lw: 8, seed: 67, n: 22, line: LINE, bounds: [-250, -232, 160, 130] }); // 颈+头
|
|
77
|
+
M.flat(c, smooth([[-176, -232], [-150, -262], [-130, -226], [-150, -216]]), '#efece2', { vari: 4, lw: 6, seed: 68, n: 2, line: LINE }); // 耳
|
|
78
|
+
c.strokeStyle = LINE; c.lineWidth = 9; c.lineCap = 'round'; c.beginPath(); c.moveTo(-196, -232); c.quadraticCurveTo(-190, -290, -128, -296); c.stroke(); // 角(向后弯)
|
|
79
|
+
c.lineWidth = 6; c.beginPath(); c.moveTo(-244, -166); c.quadraticCurveTo(-254, -134, -240, -110); c.stroke(); // 胡须
|
|
80
|
+
M.flat(c, blob(-208, -172, 22, 17, 0.2), '#c7a591', { vari: 6, lw: 5, seed: 90, n: 4, line: LINE, bounds: [-258, -186, 44, 32] });
|
|
81
|
+
c.fillStyle = LINE; c.beginPath(); c.arc(-184, -208, 6.5, 0, TAU); c.fill();
|
|
82
|
+
[[-34, 0, 0], [86, 3.1, 0]].forEach(([dx, ph], i) => M.flat(c, leg(dx, ph), '#efece2', { vari: 6, lw: 7, seed: 62 + i, n: 4, line: LINE, bounds: [dx - 30, -74, 70, 110] }));
|
|
83
|
+
// 男孩:侧身朝左,蓝衣、黄裤、赭色头发小帽,拉绳
|
|
84
|
+
const bx = -380, sw = Math.sin(t * 7) * 20;
|
|
85
|
+
[0, 3.1].forEach((ph, i) => { const s2 = Math.sin(t * 7 + ph) * 22; M.flat(c, smooth([[bx - 16, -104], [bx + 18, -104], [bx + 14 + s2 * 0.5, -40], [bx + 14 + s2, 22], [bx - 20 + s2, 22], [bx - 12 + s2 * 0.5, -40]]), i ? '#1d2a52' : '#2f3f7a', { lw: 6, seed: 70 + i, n: 8, line: LINE, bounds: [bx - 30, -104, 60, 130] }); });
|
|
86
|
+
M.flat(c, smooth([[bx - 46, -270], [bx + 4, -290], [bx + 48, -250], [bx + 56, -96], [bx - 54, -96]]), '#2f5f9a', { streak: 1.3, vari: 16, lw: 7, seed: 71, n: 30, line: LINE, bounds: [bx - 58, -290, 118, 200] });
|
|
87
|
+
M.flat(c, smooth([[bx - 30, -330], [bx + 10, -352], [bx + 46, -320], [bx + 40, -276], [bx - 8, -262], [bx - 36, -290]]), '#e8b078', { vari: 10, lw: 6, seed: 72, n: 10, line: LINE, bounds: [bx - 40, -352, 90, 90] });
|
|
88
|
+
M.flat(c, smooth([[bx - 40, -318], [bx - 8, -366], [bx + 40, -352], [bx + 52, -322], [bx + 4, -334]]), '#8a2f24', { vari: 6, lw: 6, seed: 73, n: 8, line: LINE, bounds: [bx - 44, -368, 100, 50] }); // 红帽
|
|
89
|
+
c.strokeStyle = LINE; c.lineWidth = 4.5; c.beginPath(); c.moveTo(bx - 22, -312); c.quadraticCurveTo(bx - 14, -306, bx - 6, -312); c.stroke(); // 眼
|
|
90
|
+
c.strokeStyle = LINE; c.lineWidth = 6; c.beginPath(); c.moveTo(bx - 44, -230); c.quadraticCurveTo(bx - 120, -170, bx - 90, -150 + sw * 0.3); c.stroke(); // 左臂向前
|
|
91
|
+
c.lineWidth = 4; c.beginPath(); c.moveTo(bx + 40, -214 + sw * 0.2); c.quadraticCurveTo(-60, -100, -186, -190); c.stroke(); // 绳从男孩右手到羊颈
|
|
92
|
+
c.restore();
|
|
93
|
+
}
|
|
94
|
+
function draw(c, lt) {
|
|
95
|
+
if (!layer) build();
|
|
96
|
+
const t = clamp(lt, 0, 6);
|
|
97
|
+
c.save(); c.translate(W, 0); c.scale(-1, 1); // 水平镜像:不复刻名作的左右布局
|
|
98
|
+
c.drawImage(layer, 0, 0);
|
|
99
|
+
leaves(c, t); boyGoat(c, t);
|
|
100
|
+
// 前景妇人:被画框裁掉(脚在画外),两个并排朝右,一个在最左背对
|
|
101
|
+
woman(c, 230, 1190 + Math.sin(t * 1.4) * 2, 1.0, t, 100, 0.2, true);
|
|
102
|
+
woman(c, 560, 1250, 0.86, t, 200, 0.5, false);
|
|
103
|
+
woman(c, 820, 1310, 0.72, t, 300, 0.3, false);
|
|
104
|
+
c.restore();
|
|
105
|
+
}
|
|
106
|
+
return { draw };
|
|
107
|
+
})();
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Original scene; fixed pigment seed, continuous object motion, arbitrary-seek safe.
|
|
2
|
+
SCENES['49_chagall'] = (() => {
|
|
3
|
+
const W=1920,H=1080,TAU=Math.PI*2;
|
|
4
|
+
const rng=(seed)=>()=>{seed=(Math.imul(seed,1664525)+1013904223)>>>0;return seed/4294967296;};
|
|
5
|
+
const canvas=(w=W,h=H)=>{const a=document.createElement('canvas');a.width=w;a.height=h;return a;};
|
|
6
|
+
const poly=(g,pts,col)=>{g.fillStyle=col;g.beginPath();pts.forEach((p,i)=>i?g.lineTo(...p):g.moveTo(...p));g.closePath();g.fill();};
|
|
7
|
+
const shape=(g,d,col,stroke=null,width=2)=>{const p=new Path2D(d);g.fillStyle=col;g.fill(p);if(stroke){g.strokeStyle=stroke;g.lineWidth=width;g.stroke(p);}return p;};
|
|
8
|
+
const oval=(g,x,y,rx,ry,col,a=0)=>{g.fillStyle=col;g.beginPath();g.ellipse(x,y,rx,ry,a,0,TAU);g.fill();};
|
|
9
|
+
const line=(g,pts,col,w=2)=>{g.strokeStyle=col;g.lineWidth=w;g.lineCap='round';g.beginPath();pts.forEach((p,i)=>i?g.lineTo(...p):g.moveTo(...p));g.stroke();};
|
|
10
|
+
function paint(src,seed,cell,len,angle,amount=17){
|
|
11
|
+
const out=canvas(src.width,src.height),g=out.getContext('2d'),s=src.getContext('2d',{willReadFrequently:true}),ww=src.width,hh=src.height,data=s.getImageData(0,0,ww,hh).data,r=rng(seed);
|
|
12
|
+
g.drawImage(src,0,0);g.lineCap='butt';
|
|
13
|
+
for(let yy=0;yy<hh+cell;yy+=cell)for(let xx=0;xx<ww+cell;xx+=cell){
|
|
14
|
+
const x=xx+(r()-.5)*cell*1.8,y=yy+(r()-.5)*cell*1.8,ix=Math.min(ww-1,Math.max(0,x|0)),iy=Math.min(hh-1,Math.max(0,y|0)),i=(iy*ww+ix)*4;if(data[i+3]<200)continue;
|
|
15
|
+
const a=angle(x,y)+(r()-.5)*.45,l=len*(.5+r()),q=(r()-.5)*amount,rr=data[i]+q,gg=data[i+1]+q*.8,bb=data[i+2]+q*.65;
|
|
16
|
+
g.strokeStyle=`rgba(${rr|0},${gg|0},${bb|0},.68)`;g.lineWidth=cell*(.55+r()*.65);g.beginPath();g.moveTo(x-Math.cos(a)*l/2,y-Math.sin(a)*l/2);g.quadraticCurveTo(x,y+(r()-.5)*3,x+Math.cos(a)*l/2,y+Math.sin(a)*l/2);g.stroke();
|
|
17
|
+
if(r()<.22){g.strokeStyle=`rgba(${(rr+17)|0},${(gg+15)|0},${(bb+10)|0},.3)`;g.lineWidth=.7;g.stroke();}
|
|
18
|
+
}
|
|
19
|
+
// Fine weave remains stationary in the object coordinate system.
|
|
20
|
+
g.globalAlpha=.035;g.strokeStyle='#fff4d6';g.lineWidth=.6;for(let y=1;y<hh;y+=3){g.beginPath();g.moveTo(0,y);g.lineTo(ww,y);g.stroke();}g.globalAlpha=1;return out;
|
|
21
|
+
}
|
|
22
|
+
let bg,bouquet,caseBase,lid,letter;
|
|
23
|
+
function house(g,x,y,w,h,a,wall,roof){g.save();g.translate(x,y);g.rotate(a);shape(g,`M${-w/2} 0 L${-w/2} ${-h} L${w/2} ${-h-8} L${w/2} 0Z`,wall,'#3c426c',3);shape(g,`M${-w/2-15} ${-h} L-4 ${-h-w*.55} L${w/2+18} ${-h-7}Z`,roof,'#54405d',3);const n=w>140?3:2;for(let j=0;j<2;j++)for(let i=0;i<n;i++){const xx=-w/2+18+i*(w-32)/n,yy=-h+24+j*(h-36)/2;g.fillStyle=j%2?'#e2cb8a':'#a1c8bf';g.fillRect(xx,yy,w/n*.38,h*.19);line(g,[[xx+w/n*.18,yy],[xx+w/n*.18,yy+h*.19]],'#78616d',2);}line(g,[[-w/2+3,-h+5],[-w/2+3,-5]],'#b8c4b4',2);g.restore();}
|
|
24
|
+
function init(){if(bg)return;const a=canvas(),g=a.getContext('2d',{willReadFrequently:true}),r=rng(491);
|
|
25
|
+
let gr=g.createLinearGradient(0,0,1700,1000);gr.addColorStop(0,'#153f81');gr.addColorStop(.45,'#2a4a93');gr.addColorStop(1,'#66517e');g.fillStyle=gr;g.fillRect(0,0,W,H);
|
|
26
|
+
// Translucent emotional colour zones, full of stationary dry pigment.
|
|
27
|
+
shape(g,'M0 0 L770 0 Q650 230 791 482 Q387 463 0 870Z','#224f8b');shape(g,'M1323 0 L1920 0 L1920 666 Q1590 587 1438 358 Q1263 230 1323 0Z','#4d7d8555');shape(g,'M0 857 Q380 719 751 848 Q1098 995 1455 769 Q1700 608 1920 764 L1920 1080 L0 1080Z','#625480');
|
|
28
|
+
for(let i=0;i<18000;i++){const x=r()*W,y=r()*H,aa=r()*TAU;line(g,[[x,y],[x+Math.cos(aa)*(3+r()*23),y+Math.sin(aa)*(3+r()*23)]],['#7d93b31b','#142d681a','#596eae22','#b381a419'][i%4],1+r()*5);}
|
|
29
|
+
oval(g,1547,185,80,87,'#c9d6ac',-.15);shape(g,'M1536 99 Q1460 159 1524 242 Q1465 220 1465 165 Q1472 115 1536 99Z','#8fbdab');
|
|
30
|
+
for(let i=0;i<120;i++){const x=60+r()*1800,y=35+r()*580;oval(g,x,y,1+r()*2,1+r()*2,['#c7d3bb88','#d4b78b77','#8cc2c0aa'][i%3]);}
|
|
31
|
+
// A street bends upward into remembered architecture; each roof has a different axis.
|
|
32
|
+
for(let i=0;i<9;i++){const x=890+i*122,y=1000-Math.sin(i*.35)*170;house(g,x,y,116+r()*62,140+r()*90,-.12+i*.07,['#73918d','#b9a1a0','#8b95af','#b29a82'][i%4],['#9a5365','#c66e6d','#597d91'][i%3]);}
|
|
33
|
+
house(g,1535,561,268,229,-.34,'#a2bdaf','#b95369');house(g,1840,409,179,170,.28,'#96b6aa','#9d5d77');
|
|
34
|
+
// A door hangs in memory above its own pavement; no borrowed character symbols.
|
|
35
|
+
shape(g,'M1140 719 L1165 477 L1297 485 L1283 735Z','#517993','#283e6b',4);shape(g,'M1160 704 L1178 491 L1282 498 L1272 716Z','#87a2a3');shape(g,'M1193 689 L1195 511 L1267 510 L1257 702Z','#d5b998');oval(g,1244,613,4,5,'#755e72');
|
|
36
|
+
// Fine chalk/pigment traces do not move independently of the houses.
|
|
37
|
+
line(g,[[1010,1040],[1168,930],[1430,928],[1628,879],[1920,972]],'#a69ea155',4);
|
|
38
|
+
for(let i=0;i<60;i++){let x=850+r()*1060,y=880+r()*200;line(g,[[x,y],[x+8+r()*26,y-8]],['#9e8b9f77','#b4a8a966','#574b7a66'][i%3],2);}
|
|
39
|
+
bg=paint(a,4901,6,19,(x,y)=>y>800?-.2:Math.sin(x*.003+y*.004)*.8,16);
|
|
40
|
+
const b=canvas(700,900),q=b.getContext('2d',{willReadFrequently:true}),rr=rng(494);
|
|
41
|
+
// An oversized bouquet is the memory of a garden, distinct from literal village scale.
|
|
42
|
+
const flowers=[[195,240,67,'#d7b7c3'],[382,127,92,'#b85e86'],[501,334,74,'#e8cfb5'],[268,440,74,'#bd6387'],[419,464,63,'#b4ccd0'],[125,416,61,'#e1c9bf']];
|
|
43
|
+
for(const [x,y,sz,col] of flowers){line(q,[[341,885],[329+(x-330)*.3,610],[x,y]],'#668b8a',7);line(q,[[342,881],[332+(x-330)*.3,610],[x+4,y]],'#96a497',2);for(let j=0;j<11;j++){const aa=j*TAU/11+rr()*.25,rx=x+Math.cos(aa)*sz*.48,ry=y+Math.sin(aa)*sz*.43;oval(q,rx,ry,sz*(.3+rr()*.2),sz*(.18+rr()*.17),col,aa);line(q,[[x,y],[rx+Math.cos(aa)*sz*.2,ry+Math.sin(aa)*sz*.2]],'#f0d7c23c',2);}oval(q,x,y,sz*.2,sz*.17,'#a78568');for(let k=0;k<30;k++){const aa=rr()*TAU,ra=rr()*sz*.2;oval(q,x+Math.cos(aa)*ra,y+Math.sin(aa)*ra,2,2,k%2?'#dec89d':'#706b76');}}
|
|
44
|
+
for(let i=0;i<18;i++){const y=520+i*18,x=330+Math.sin(i*1.4)*60,dir=i%2?-1:1;shape(q,`M${x} ${y} Q${x+dir*105} ${y-83} ${x+dir*104} ${y-26} Q${x+dir*72} ${y+29} ${x} ${y}Z`,['#567d80','#7c9d92','#3e7079'][i%3]);line(q,[[x,y],[x+dir*87,y-34]],'#99b09b',2);}
|
|
45
|
+
bouquet=paint(b,4904,4,12,(x,y)=>Math.atan2(y-450,x-335)+.6,17);
|
|
46
|
+
const cb=canvas(640,260),v=cb.getContext('2d',{willReadFrequently:true});shape(v,'M16 43 Q310 20 610 54 L626 194 Q327 246 30 197Z','#7b4f62','#463e63',5);shape(v,'M25 49 Q316 27 600 59 L586 106 Q311 156 40 105Z','#b88f85');shape(v,'M36 57 Q316 40 590 68 L577 94 Q310 134 49 98Z','#524763');for(const x of [116,486]){shape(v,`M${x} 104 L${x+29} 105 L${x+37} 209 L${x+8} 213Z`,'#bba18d');g.fillStyle='#fff';}shape(v,'M266 174 Q272 143 355 154 L362 183 L347 185 L344 168 L283 167 L281 184Z','#b6a28a');for(let i=0;i<100;i++){const xx=45+rr()*540,yy=123+rr()*68;line(v,[[xx,yy],[xx+4+rr()*12,yy]],'#bd8f801f',2);}caseBase=paint(cb,4905,4,11,()=>.05,12);
|
|
47
|
+
const ld=canvas(640,330),z=ld.getContext('2d',{willReadFrequently:true});shape(z,'M25 300 L42 38 Q318 0 590 40 L612 303Z','#976c79','#453f66',5);shape(z,'M45 286 L58 55 Q317 20 575 56 L593 287Z','#b89a98');shape(z,'M65 272 L74 70 Q317 41 560 70 L576 271Z','#b4a2a5');for(let j=0;j<12;j++)line(z,[[75,78+j*16],[562,77+j*16]],'#897b911f',3);shape(z,'M159 104 L482 109 L478 228 L165 233Z','#a58297','#c6b0a9',3);shape(z,'M215 118 L428 127 L420 211 L211 202Z','#c2c5b2');shape(z,'M213 179 L259 151 L301 168 L337 146 L423 181 L420 211 L211 202Z','#788f9c');shape(z,'M213 190 Q292 169 420 192 L420 211 L211 202Z','#94ac92');for(let j=0;j<30;j++){line(z,[[165+j*10,110],[168+j*10,114]],'#d8c0b0',1);line(z,[[165+j*10,226],[168+j*10,230]],'#d8c0b0',1);}lid=paint(ld,4906,4,12,()=>0,12);
|
|
48
|
+
const e=canvas(118,86),u=e.getContext('2d',{willReadFrequently:true});shape(u,'M5 9 L111 3 L116 76 L9 82Z','#dfc9ad','#ab8996',1.5);line(u,[[7,10],[61,46],[109,5]],'#9c8c98',2);line(u,[[10,80],[47,39]],'#baa8a3',1.5);line(u,[[113,76],[74,39]],'#baa8a3',1.5);letter=paint(e,4907,2.8,7,()=>.1,9);
|
|
49
|
+
}
|
|
50
|
+
function pos(p){const u=1-p;return [u*u*u*530+3*u*u*p*880+3*u*p*p*900+p*p*p*1255,u*u*u*890+3*u*u*p*200+3*u*p*p*170+p*p*p*575];}
|
|
51
|
+
return {draw(c,lt){init();const t=Math.max(0,lt);c.drawImage(bg,0,0);
|
|
52
|
+
// The bouquet grows out of the open case as its lid lifts, then breathes in the wind.
|
|
53
|
+
const open=.56+.32*(.5-.5*Math.cos(Math.min(t/2.2,1)*Math.PI));c.save();c.translate(255,920);c.transform(1,-.08,-.13,open,0,0);c.drawImage(lid,0,-300);c.restore();
|
|
54
|
+
c.save();c.translate(625,1000);c.rotate(-.14+Math.sin(t*.75)*.016);c.scale(.94,.94);c.drawImage(bouquet,-350,-890);c.restore();
|
|
55
|
+
for(let i=0;i<7;i++){const p=(t*.135+i/7)%1,[x,y]=pos(p),sc=.48+Math.sin(p*Math.PI)*.32;c.save();c.translate(x,y);c.rotate(-.35+Math.sin(p*9+i)*.5);c.scale(sc,sc);c.drawImage(letter,-59,-43);c.restore();}
|
|
56
|
+
c.save();c.translate(258,877);c.rotate(-.07);c.drawImage(caseBase,0,0);c.restore();
|
|
57
|
+
// Petals are independent light fragments; their motion never re-seeds the canvas.
|
|
58
|
+
for(let i=0;i<11;i++){const p=(t*.1+i*.091)%1,xx=590+p*570+Math.sin(p*7+i)*28,yy=370+p*540;oval(c,xx,yy,5+i%3*2,2.5,['#d9b1c6','#bca5c2','#e5ccbd'][i%3],p*5+i);}
|
|
59
|
+
}};
|
|
60
|
+
})();
|