@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.
Files changed (377) hide show
  1. package/bin/server.mjs +575 -0
  2. package/data/styles.json +533 -0
  3. package/engine/audit_clip.py +46 -0
  4. package/engine/clip.html +24 -0
  5. package/engine/clip.js +104 -0
  6. package/engine/clip_schema.json +19 -0
  7. package/engine/clips/t1_3b1b.js +203 -0
  8. package/engine/clips/t2_keynote_ui.js +347 -0
  9. package/engine/clips/t3_finance_chart.js +206 -0
  10. package/engine/clips/y1_kurzgesagt.js +183 -0
  11. package/engine/clips/y2_vox.js +150 -0
  12. package/engine/clips/y3_whiteboard.js +209 -0
  13. package/engine/clips/y4_storytime.js +194 -0
  14. package/engine/clips/y5_kinetic_type.js +134 -0
  15. package/engine/compare.py +19 -0
  16. package/engine/demos/_shared/hero/c1_0.png +0 -0
  17. package/engine/demos/_shared/hero/c1_1.png +0 -0
  18. package/engine/demos/_shared/hero/c1_2.png +0 -0
  19. package/engine/demos/_shared/hero/c3_line.png +0 -0
  20. package/engine/demos/_shared/hero.js +96 -0
  21. package/engine/demos/long_scroll/eras.js +23 -0
  22. package/engine/demos/long_scroll/frames/8bit_act/01.png +0 -0
  23. package/engine/demos/long_scroll/frames/8bit_act/02.png +0 -0
  24. package/engine/demos/long_scroll/frames/8bit_act/03.png +0 -0
  25. package/engine/demos/long_scroll/frames/8bit_act/04.png +0 -0
  26. package/engine/demos/long_scroll/frames/8bit_act/05.png +0 -0
  27. package/engine/demos/long_scroll/frames/8bit_act/06.png +0 -0
  28. package/engine/demos/long_scroll/frames/8bit_act/07.png +0 -0
  29. package/engine/demos/long_scroll/frames/8bit_act/08.png +0 -0
  30. package/engine/demos/long_scroll/frames/8bit_act/meta.json +69 -0
  31. package/engine/demos/long_scroll/frames/8bit_walk/01.png +0 -0
  32. package/engine/demos/long_scroll/frames/8bit_walk/02.png +0 -0
  33. package/engine/demos/long_scroll/frames/8bit_walk/03.png +0 -0
  34. package/engine/demos/long_scroll/frames/8bit_walk/04.png +0 -0
  35. package/engine/demos/long_scroll/frames/8bit_walk/05.png +0 -0
  36. package/engine/demos/long_scroll/frames/8bit_walk/06.png +0 -0
  37. package/engine/demos/long_scroll/frames/8bit_walk/07.png +0 -0
  38. package/engine/demos/long_scroll/frames/8bit_walk/08.png +0 -0
  39. package/engine/demos/long_scroll/frames/8bit_walk/meta.json +69 -0
  40. package/engine/demos/long_scroll/frames/egypt_dodge/01.png +0 -0
  41. package/engine/demos/long_scroll/frames/egypt_dodge/02.png +0 -0
  42. package/engine/demos/long_scroll/frames/egypt_dodge/03.png +0 -0
  43. package/engine/demos/long_scroll/frames/egypt_dodge/04.png +0 -0
  44. package/engine/demos/long_scroll/frames/egypt_dodge/05.png +0 -0
  45. package/engine/demos/long_scroll/frames/egypt_dodge/06.png +0 -0
  46. package/engine/demos/long_scroll/frames/egypt_dodge/07.png +0 -0
  47. package/engine/demos/long_scroll/frames/egypt_dodge/08.png +0 -0
  48. package/engine/demos/long_scroll/frames/egypt_dodge/meta.json +69 -0
  49. package/engine/demos/long_scroll/frames/egypt_walk8/01.png +0 -0
  50. package/engine/demos/long_scroll/frames/egypt_walk8/02.png +0 -0
  51. package/engine/demos/long_scroll/frames/egypt_walk8/03.png +0 -0
  52. package/engine/demos/long_scroll/frames/egypt_walk8/04.png +0 -0
  53. package/engine/demos/long_scroll/frames/egypt_walk8/05.png +0 -0
  54. package/engine/demos/long_scroll/frames/egypt_walk8/06.png +0 -0
  55. package/engine/demos/long_scroll/frames/egypt_walk8/07.png +0 -0
  56. package/engine/demos/long_scroll/frames/egypt_walk8/08.png +0 -0
  57. package/engine/demos/long_scroll/frames/egypt_walk8/meta.json +69 -0
  58. package/engine/demos/long_scroll/frames/monet_throw/01.png +0 -0
  59. package/engine/demos/long_scroll/frames/monet_throw/02.png +0 -0
  60. package/engine/demos/long_scroll/frames/monet_throw/03.png +0 -0
  61. package/engine/demos/long_scroll/frames/monet_throw/04.png +0 -0
  62. package/engine/demos/long_scroll/frames/monet_throw/05.png +0 -0
  63. package/engine/demos/long_scroll/frames/monet_throw/06.png +0 -0
  64. package/engine/demos/long_scroll/frames/monet_throw/07.png +0 -0
  65. package/engine/demos/long_scroll/frames/monet_throw/08.png +0 -0
  66. package/engine/demos/long_scroll/frames/monet_throw/meta.json +71 -0
  67. package/engine/demos/long_scroll/frames/monet_walk/01.png +0 -0
  68. package/engine/demos/long_scroll/frames/monet_walk/02.png +0 -0
  69. package/engine/demos/long_scroll/frames/monet_walk/03.png +0 -0
  70. package/engine/demos/long_scroll/frames/monet_walk/04.png +0 -0
  71. package/engine/demos/long_scroll/frames/monet_walk/05.png +0 -0
  72. package/engine/demos/long_scroll/frames/monet_walk/06.png +0 -0
  73. package/engine/demos/long_scroll/frames/monet_walk/07.png +0 -0
  74. package/engine/demos/long_scroll/frames/monet_walk/08.png +0 -0
  75. package/engine/demos/long_scroll/frames/monet_walk/meta.json +71 -0
  76. package/engine/demos/long_scroll/lib/acts.js +87 -0
  77. package/engine/demos/long_scroll/lib/crossing.js +463 -0
  78. package/engine/demos/long_scroll/lib/world.js +143 -0
  79. package/engine/demos/long_scroll/lib/xing2_ding.js +42 -0
  80. package/engine/demos/long_scroll/lib/xing2_jia.js +114 -0
  81. package/engine/demos/long_scroll/lib/xing2_yi.js +133 -0
  82. package/engine/demos/long_scroll/segments/02_egypt.js +234 -0
  83. package/engine/demos/long_scroll/segments/14_8bit.js +118 -0
  84. package/engine/demos/long_scroll/segments/28_monet.js +86 -0
  85. package/engine/demos/t1_3b1b/eras.js +11 -0
  86. package/engine/demos/t1_3b1b/t1_s1.js +101 -0
  87. package/engine/demos/t1_3b1b/t1_s2.js +74 -0
  88. package/engine/demos/t1_3b1b/t1_s3.js +65 -0
  89. package/engine/demos/t2_keynote_ui/eras.js +10 -0
  90. package/engine/demos/t2_keynote_ui/t2_s1.js +57 -0
  91. package/engine/demos/t2_keynote_ui/t2_s2.js +70 -0
  92. package/engine/demos/t2_keynote_ui/t2_s3.js +61 -0
  93. package/engine/demos/t3_finance_chart/eras.js +11 -0
  94. package/engine/demos/t3_finance_chart/t3_s1.js +48 -0
  95. package/engine/demos/t3_finance_chart/t3_s2.js +42 -0
  96. package/engine/demos/t3_finance_chart/t3_s3.js +51 -0
  97. package/engine/demos/y1_kurzgesagt/eras.js +10 -0
  98. package/engine/demos/y1_kurzgesagt/kurzgesagt.js +347 -0
  99. package/engine/demos/y2_vox/eras.js +10 -0
  100. package/engine/demos/y2_vox/vox.js +240 -0
  101. package/engine/demos/y3_whiteboard/eras.js +10 -0
  102. package/engine/demos/y3_whiteboard/whiteboard.js +162 -0
  103. package/engine/demos/y4_storytime/eras.js +14 -0
  104. package/engine/demos/y4_storytime/s1_desk.js +18 -0
  105. package/engine/demos/y4_storytime/s2_screen.js +82 -0
  106. package/engine/demos/y4_storytime/s3_react.js +39 -0
  107. package/engine/demos/y4_storytime/s4_wide.js +16 -0
  108. package/engine/demos/y4_storytime/s5_close.js +18 -0
  109. package/engine/demos/y4_storytime/story.js +25 -0
  110. package/engine/demos/y4_storytime/vo_env.js +2 -0
  111. package/engine/demos/y4_storytime/world.js +73 -0
  112. package/engine/demos/y5_kinetic_type/eras.js +16 -0
  113. package/engine/demos/y5_kinetic_type/k1_title.js +38 -0
  114. package/engine/demos/y5_kinetic_type/k2_complete.js +55 -0
  115. package/engine/demos/y5_kinetic_type/k3_chat.js +47 -0
  116. package/engine/demos/y5_kinetic_type/k4_agent.js +49 -0
  117. package/engine/demos/y5_kinetic_type/kinetic.js +52 -0
  118. package/engine/engine.js +277 -0
  119. package/engine/eras.js +60 -0
  120. package/engine/eras_gallery.js +115 -0
  121. package/engine/eras_style_studies.js +20 -0
  122. package/engine/examples/assets/K/347/272/277/345/233/276_/346/250/252/345/261/217.png +0 -0
  123. 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
  124. package/engine/examples/assets//347/273/210/347/253/257_sum/346/261/202/345/222/214.png +0 -0
  125. package/engine/examples/assets//350/220/245/346/224/266/345/233/276_/347/253/226/345/261/217.png +0 -0
  126. 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
  127. package/engine/examples/t1_3b1b.json +19 -0
  128. package/engine/examples/t2_fullbleed_pulse.json +15 -0
  129. package/engine/examples/t2_keynote_ui.json +15 -0
  130. package/engine/examples/t3_finance_chart.json +29 -0
  131. package/engine/examples/y1_kurzgesagt.json +16 -0
  132. package/engine/examples/y2_vox.json +14 -0
  133. package/engine/examples/y3_whiteboard.json +17 -0
  134. package/engine/examples/y4_storytime.json +52 -0
  135. package/engine/examples/y5_kinetic_type.json +14 -0
  136. package/engine/index.html +42 -0
  137. package/engine/lib/THREE_LICENSE.txt +21 -0
  138. package/engine/lib/brush.js +149 -0
  139. package/engine/lib/camera.js +99 -0
  140. package/engine/lib/chart.js +152 -0
  141. package/engine/lib/collage.js +97 -0
  142. package/engine/lib/diagram.js +264 -0
  143. package/engine/lib/finish.js +70 -0
  144. package/engine/lib/fonts/AlfaSlabOne-400.woff +0 -0
  145. package/engine/lib/fonts/Anton-400.woff +0 -0
  146. package/engine/lib/fonts/ArchivoBlack-400.woff +0 -0
  147. package/engine/lib/fonts/Bangers-400.woff +0 -0
  148. package/engine/lib/fonts/Caveat-600.woff +0 -0
  149. package/engine/lib/fonts/Cinzel-400.woff +0 -0
  150. package/engine/lib/fonts/Cinzel-700.woff +0 -0
  151. package/engine/lib/fonts/CormorantGaramond-500.woff +0 -0
  152. package/engine/lib/fonts/CormorantGaramond-500i.woff +0 -0
  153. package/engine/lib/fonts/CormorantGaramond-700.woff +0 -0
  154. package/engine/lib/fonts/EBGaramond-400.woff +0 -0
  155. package/engine/lib/fonts/EBGaramond-400i.woff +0 -0
  156. package/engine/lib/fonts/EBGaramond-600.woff +0 -0
  157. package/engine/lib/fonts/Exo2-800i.woff +0 -0
  158. package/engine/lib/fonts/GochiHand-400.woff +0 -0
  159. package/engine/lib/fonts/IMFellEnglish-400.woff +0 -0
  160. package/engine/lib/fonts/IMFellEnglish-400i.woff +0 -0
  161. package/engine/lib/fonts/Inter-var.woff +0 -0
  162. package/engine/lib/fonts/JosefinSans-700.woff +0 -0
  163. package/engine/lib/fonts/Kalam-700.woff +0 -0
  164. package/engine/lib/fonts/LICENSES.md +40 -0
  165. package/engine/lib/fonts/LXGWWenKai-500.woff +0 -0
  166. package/engine/lib/fonts/LibreBaskerville-400.woff +0 -0
  167. package/engine/lib/fonts/LibreBaskerville-700.woff +0 -0
  168. package/engine/lib/fonts/LilitaOne-400.woff +0 -0
  169. package/engine/lib/fonts/Marcellus-400.woff +0 -0
  170. package/engine/lib/fonts/NotoSansSC-500.woff +0 -0
  171. package/engine/lib/fonts/NotoSansSC-700.woff +0 -0
  172. package/engine/lib/fonts/NotoSansSC-800.woff +0 -0
  173. package/engine/lib/fonts/NotoSansSC-900.woff +0 -0
  174. package/engine/lib/fonts/NotoSansSC-sub.woff +0 -0
  175. package/engine/lib/fonts/NotoSerifJP-600.woff +0 -0
  176. package/engine/lib/fonts/NotoSerifSC-sub.woff +0 -0
  177. package/engine/lib/fonts/OFL.txt +94 -0
  178. package/engine/lib/fonts/PlayfairDisplay-400i.woff +0 -0
  179. package/engine/lib/fonts/PlayfairDisplay-700.woff +0 -0
  180. package/engine/lib/fonts/PlayfairDisplay-700i.woff +0 -0
  181. package/engine/lib/fonts/Poppins-600.woff +0 -0
  182. package/engine/lib/fonts/Poppins-700.woff +0 -0
  183. package/engine/lib/fonts/Poppins-800.woff +0 -0
  184. package/engine/lib/fonts/PressStart2P-400.woff +0 -0
  185. package/engine/lib/fonts/Righteous-400.woff +0 -0
  186. package/engine/lib/fonts/RobotoCondensed-var.woff +0 -0
  187. package/engine/lib/fonts/Rye-400.woff +0 -0
  188. package/engine/lib/fonts/UnifrakturMaguntia-400.woff +0 -0
  189. package/engine/lib/fonts/cmunrm.woff +0 -0
  190. package/engine/lib/fonts/cmunti.woff +0 -0
  191. package/engine/lib/fonts/index.json +1 -0
  192. package/engine/lib/fonts.js +56 -0
  193. package/engine/lib/kit.js +91 -0
  194. package/engine/lib/media.js +118 -0
  195. package/engine/lib/medium.js +258 -0
  196. package/engine/lib/motion.js +111 -0
  197. package/engine/lib/paint.js +200 -0
  198. package/engine/lib/palettes.js +57 -0
  199. package/engine/lib/post.js +108 -0
  200. package/engine/lib/render.js +189 -0
  201. package/engine/lib/rig.js +390 -0
  202. package/engine/lib/rig_huashu.js +171 -0
  203. package/engine/lib/three.min.js +7 -0
  204. package/engine/lib/toon.js +186 -0
  205. package/engine/lib/typo.js +136 -0
  206. package/engine/lib/ui.js +141 -0
  207. package/engine/lib/util.js +92 -0
  208. package/engine/render.py +157 -0
  209. package/engine/scenes/01_cave.js +364 -0
  210. package/engine/scenes/02_egypt.js +524 -0
  211. package/engine/scenes/03_greek.js +448 -0
  212. package/engine/scenes/04_roman.js +555 -0
  213. package/engine/scenes/05_gothic.js +522 -0
  214. package/engine/scenes/06_renaissance.js +338 -0
  215. package/engine/scenes/08_impressionism.js +206 -0
  216. package/engine/scenes/09_postimp.js +183 -0
  217. package/engine/scenes/10_nouveau.js +334 -0
  218. package/engine/scenes/11_cubism.js +265 -0
  219. package/engine/scenes/12_bauhaus.js +214 -0
  220. package/engine/scenes/13_pop.js +269 -0
  221. package/engine/scenes/14_8bit.js +238 -0
  222. package/engine/scenes/15_raytrace.js +300 -0
  223. package/engine/scenes/16_2026.js +565 -0
  224. package/engine/scenes/17_ink.js +273 -0
  225. package/engine/scenes/18_klimt.js +227 -0
  226. package/engine/scenes/19_munch.js +153 -0
  227. package/engine/scenes/20_dunhuang.js +215 -0
  228. package/engine/scenes/21_kusama.js +124 -0
  229. package/engine/scenes/22_constructivism.js +205 -0
  230. package/engine/scenes/23_dali.js +221 -0
  231. package/engine/scenes/24_hopper.js +150 -0
  232. package/engine/scenes/25_ghibli.js +251 -0
  233. package/engine/scenes/26_vaporwave.js +191 -0
  234. package/engine/scenes/27_kirby.js +255 -0
  235. package/engine/scenes/28_monet.js +176 -0
  236. package/engine/scenes/29_seurat.js +104 -0
  237. package/engine/scenes/30_matisse.js +151 -0
  238. package/engine/scenes/31_haring.js +109 -0
  239. package/engine/scenes/32_rembrandt.js +217 -0
  240. package/engine/scenes/33_rubberhose.js +219 -0
  241. package/engine/scenes/34_shadowpuppet.js +195 -0
  242. package/engine/scenes/35_shinkai.js +197 -0
  243. package/engine/scenes/36_picasso_blue.js +123 -0
  244. package/engine/scenes/38_giotto.js +133 -0
  245. package/engine/scenes/39_piero.js +53 -0
  246. package/engine/scenes/40_raphael.js +96 -0
  247. package/engine/scenes/41_michelangelo.js +112 -0
  248. package/engine/scenes/42_guardi.js +70 -0
  249. package/engine/scenes/43_lorenzetti.js +145 -0
  250. package/engine/scenes/44_cortona.js +86 -0
  251. package/engine/scenes/45_renoir.js +86 -0
  252. package/engine/scenes/46_pissarro.js +82 -0
  253. package/engine/scenes/47_sisley.js +94 -0
  254. package/engine/scenes/48_gauguin.js +107 -0
  255. package/engine/scenes/49_chagall.js +60 -0
  256. package/engine/scenes/50_ma_yuan.js +131 -0
  257. package/engine/scenes/51_persian_miniature.js +149 -0
  258. package/engine/scenes/52_south_park.js +105 -0
  259. package/engine/scenes/53_pixar.js +60 -0
  260. package/engine/scenes/54_ghibli_lab.js +68 -0
  261. package/engine/scenes/55_dunhuang.js +161 -0
  262. package/engine/scenes/index.js +28 -0
  263. package/engine/spec_assets.py +76 -0
  264. package/engine/transitions.js +929 -0
  265. package/engine//346/227/266/344/273/243/344/275/234/350/200/205/350/247/204/350/214/203.md +49 -0
  266. package/lib/host.mjs +754 -0
  267. package/lib/render-bridge.mjs +543 -0
  268. package/lib/render-cli.mjs +98 -0
  269. package/lib/storyboard-lint.mjs +54 -0
  270. package/manifest.json +52 -0
  271. package/package.json +8 -4
  272. package/runtime/package.json +8 -0
  273. package/skills/biuart/SKILL.md +154 -0
  274. package/skills/biuart/references/01-/346/213/206/350/247/243.md +55 -0
  275. package/skills/biuart/references/02-/346/234/272/345/210/266.md +41 -0
  276. 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
  277. package/skills/biuart/references/04-/347/272/257/344/273/243/347/240/201/347/273/230/345/210/266.md +92 -0
  278. package/skills/biuart/references/05-/350/212/202/345/245/217/344/270/216/351/205/215/344/271/220.md +19 -0
  279. 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
  280. package/skills/biuart/references/07-/346/255/243/351/235/242/347/273/217/351/252/214.md +196 -0
  281. 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
  282. 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
  283. package/skills/biuart/references/10-/350/247/222/350/211/262.md +34 -0
  284. package/skills/biuart/references/11-/351/225/277/345/215/267/347/251/277/350/266/212/347/211/207.md +43 -0
  285. 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
  286. 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
  287. 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
  288. 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
  289. package/skills/biuart/references/about-author.md +99 -0
  290. package/skills/biuart/references/capabilities.md +91 -0
  291. package/skills/biuart/references/compatibility.md +34 -0
  292. package/skills/biuart/references/images.md +97 -0
  293. package/skills/biuart/references/voice-model-review.md +18 -0
  294. package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/t1_3b1b.md +122 -0
  295. package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/t2_keynote_ui.md +117 -0
  296. package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/t3_finance_chart.md +116 -0
  297. package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/y1_kurzgesagt.md +193 -0
  298. package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/y2_vox.md +195 -0
  299. package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/y3_whiteboard.md +190 -0
  300. package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/y4_storytime.md +166 -0
  301. package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/y5_kinetic_type.md +158 -0
  302. package/skills/biuart/references//345/212/250/347/224/273/350/257/255/346/263/225/y6_presenter_explainer.md +24 -0
  303. package/skills/biuart/references//345/217/243/346/222/255API.md +125 -0
  304. package/skills/biuart/references//350/211/262/346/235/277.md +38 -0
  305. package/skills/biuart/references//350/247/222/350/211/262/347/262/276/347/273/206/345/272/246.md +61 -0
  306. package/skills/biuart/references//351/225/234/345/244/264/350/241/250/346/250/241/346/235/277.md +80 -0
  307. package/skills/biuart/references//351/225/234/345/244/264/350/257/255/350/250/200.md +73 -0
  308. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/01_cave.md +46 -0
  309. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/02_egypt.md +64 -0
  310. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/03_greek.md +50 -0
  311. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/04_roman.md +99 -0
  312. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/05_gothic.md +32 -0
  313. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/06_renaissance.md +41 -0
  314. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/08_impressionism.md +34 -0
  315. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/09_postimp.md +17 -0
  316. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/10_nouveau.md +31 -0
  317. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/11_cubism.md +30 -0
  318. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/12_bauhaus.md +30 -0
  319. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/13_pop.md +29 -0
  320. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/14_8bit.md +27 -0
  321. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/15_raytrace.md +32 -0
  322. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/16_2026.md +55 -0
  323. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/17_ink.md +50 -0
  324. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/18_klimt.md +37 -0
  325. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/19_munch.md +42 -0
  326. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/20_dunhuang.md +49 -0
  327. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/21_kusama.md +32 -0
  328. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/22_constructivism.md +35 -0
  329. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/23_dali.md +29 -0
  330. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/24_hopper.md +30 -0
  331. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/25_ghibli.md +55 -0
  332. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/26_vaporwave.md +27 -0
  333. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/27_kirby.md +30 -0
  334. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/28_monet.md +39 -0
  335. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/29_seurat.md +31 -0
  336. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/30_matisse.md +26 -0
  337. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/31_haring.md +22 -0
  338. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/32_rembrandt.md +29 -0
  339. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/33_rubberhose.md +31 -0
  340. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/34_shadowpuppet.md +40 -0
  341. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/35_shinkai.md +34 -0
  342. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/36_picasso_blue.md +25 -0
  343. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/37_rick_morty.md +48 -0
  344. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/38_giotto.md +67 -0
  345. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/39_piero.md +59 -0
  346. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/40_raphael.md +65 -0
  347. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/41_michelangelo.md +65 -0
  348. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/42_guardi.md +65 -0
  349. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/43_lorenzetti.md +69 -0
  350. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/44_cortona.md +66 -0
  351. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/45_renoir.md +66 -0
  352. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/46_pissarro.md +66 -0
  353. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/47_sisley.md +67 -0
  354. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/48_gauguin.md +66 -0
  355. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/49_chagall.md +59 -0
  356. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/50_ma_yuan.md +68 -0
  357. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/51_persian_miniature.md +66 -0
  358. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/52_south_park.md +65 -0
  359. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/53_pixar.md +61 -0
  360. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/55_dunhuang.md +41 -0
  361. package/skills/biuart/references//351/243/216/346/240/274/351/205/215/346/226/271/INDEX.md +89 -0
  362. 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
  363. 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
  364. 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
  365. package/studio/app.js +380 -0
  366. package/studio/assert-panel-permit.mjs +67 -0
  367. package/studio/composer.css +465 -0
  368. package/studio/composer.js +480 -0
  369. package/studio/index.html +97 -0
  370. package/studio/optimize-prompt.mjs +84 -0
  371. package/studio/optimize-prompt.test.mjs +47 -0
  372. package/studio/recipes.mjs +87 -0
  373. package/studio/select-menu.js +450 -0
  374. package/studio/styles.css +71 -0
  375. package/studio/to-english-prompt.mjs +149 -0
  376. package/studio/to-english-prompt.test.mjs +72 -0
  377. package/README.md +0 -3
@@ -0,0 +1,153 @@
1
+ // 蒙克《呐喊》式表现主义(1893)——纯代码。
2
+ // 管线:①流线渲染器(每个种子点沿「随时间扭动的方向场」积分 9–14 步画成长弯笔,颜色按区域色带)铺满墙/天/峡湾/地板
3
+ // → ②窗里:桥、栏杆、捂脸尖叫的小人、两个远处的人影 → ③桌、椅(摇晃的腿)
4
+ // → ④角色:平涂+流线肌理(source-atop)+深色粗轮廓 → ⑤整幅按行做正弦位移:整个世界在流动
5
+ SCENES['19_munch'] = (() => {
6
+ const W = 1920, H = 1080, { clamp, lerp, rng } = U, P = PAINT;
7
+ const { ss } = U;
8
+ const WIN = [370, 140, 790, 540];
9
+ const inWin = (x, y) => x > WIN[0] && x < WIN[2] && y > WIN[1] && y < WIN[3];
10
+ const VP = [1000, 640];
11
+ const PAL = {
12
+ sky: ['#e2541c', '#f08a24', '#f4c03a', '#c8301e', '#e8743a', '#f6a63a', '#b82a1e'],
13
+ fjord: ['#1d3a6a', '#2c5a8a', '#3a4a7a', '#14243e', '#4a6aa0'],
14
+ hill: ['#2a4a2a', '#1e3a3a', '#43603a', '#5a6a2a'],
15
+ wall: ['#8a2e1c', '#a8441e', '#6a2418', '#b8562a', '#7a3020', '#c86a2a', '#3a2a4a'],
16
+ floor: ['#8a5a2a', '#a8703a', '#6a3a1e', '#c88a42', '#4a2a1a'],
17
+ };
18
+ const hx = Object.fromEntries(Object.entries(PAL).map(([k, v]) => [k, v.map(P.hex)]));
19
+ // 区域:窗内上部天空(色带随波浪起伏),中部峡湾,左下山坡;窗外是墙/地板
20
+ function region(x, y, t) {
21
+ if (inWin(x, y)) {
22
+ const shore = 330 + 60 * Math.sin((x - 370) * 0.012 + 0.5) + 10 * Math.sin(t * 2 + x * 0.02);
23
+ if (y < shore) return 'sky';
24
+ if (x < 470 + (y - shore) * 0.6) return 'hill';
25
+ return 'fjord';
26
+ }
27
+ return y > 700 ? 'floor' : 'wall';
28
+ }
29
+ function angle(x, y, t, reg) {
30
+ switch (reg) {
31
+ case 'sky': return 0.55 * Math.sin(x * 0.011 + y * 0.018 - t * 2.4) + 0.15 * Math.sin(y * 0.05 + t * 3);
32
+ case 'fjord': { const a = Math.atan2(y - 470, x - 620); return a + Math.PI / 2 + 0.3 * Math.sin(t * 2 + a * 2); }
33
+ case 'hill': return -0.9 + 0.4 * Math.sin(y * 0.03 + t * 2);
34
+ case 'floor': return Math.atan2(y - VP[1], x - VP[0]) + 0.18 * Math.sin(x * 0.01 + t * 2.6);
35
+ default: return -Math.PI / 2 + 0.5 * Math.sin(y * 0.011 + x * 0.005 + t * 2.4) + 0.2 * Math.sin(x * 0.03 - t * 3);
36
+ }
37
+ }
38
+ const SEEDS = P.flowSeeds(24, 5);
39
+ // 区域底色:先铺平涂,长笔之间的缝露出的是同色系底,不是黑
40
+ function baseFill(g, t) {
41
+ const wg = g.createLinearGradient(0, 0, 0, 700); wg.addColorStop(0, '#a8401e'); wg.addColorStop(1, '#5a1a14'); g.fillStyle = wg; g.fillRect(0, 0, W, 700);
42
+ const fg = g.createLinearGradient(0, 700, 0, H); fg.addColorStop(0, '#7a4a22'); fg.addColorStop(1, '#4a2614'); g.fillStyle = fg; g.fillRect(0, 700, W, H - 700);
43
+ const sg = g.createLinearGradient(0, 140, 0, 400); sg.addColorStop(0, '#f08a24'); sg.addColorStop(0.5, '#d8401c'); sg.addColorStop(1, '#2c4a7a'); g.fillStyle = sg; g.fillRect(370, 140, 420, 400);
44
+ }
45
+ // 流线:沿方向场积分画长弯笔(不描边、半透明叠色,蒙克是薄涂长笔,不是梵高的厚涂短笔)
46
+ // 天空色由「波浪化的 y」决定色带——呐喊的血色天空就是横向色带在扭
47
+ const RS = { sky: [32, 7, 10, 0.78], fjord: [26, 6, 9, 0.78], hill: [12, 6, 8, 0.8], wall: [42, 7, 13, 0.5], floor: [32, 8, 12, 0.55] }; // 长、宽、薄:一根笔走 200–300px
48
+ // 长弯流线笔:P.flowLines(种子沿方向场积分,越区即停);这里只给区域、方向场、配色
49
+ const flowColor = (reg, sx, sy, r1, t) => {
50
+ if (reg === 'sky') { const yy = sy + 22 * Math.sin(sx * 0.014 - t * 2.2) + 8 * Math.sin(sx * 0.05 + t * 3); const band = Math.floor((yy - 140) / 28 + r1 * 0.6); return hx.sky[((band % 7) + 7) % 7]; }
51
+ const pal = hx[reg]; const col = pal[(r1 * pal.length) | 0]; return reg === 'wall' ? P.mix(col, [30, 12, 10], clamp((sy - 300) / 900) * 0.4) : col;
52
+ };
53
+ const flowLines = (c, t) => P.flowLines(c, SEEDS, { t, region, angle, color: flowColor, params: RS });
54
+
55
+ function bridge(c, t) {
56
+ c.save(); c.beginPath(); c.rect(370, 140, 420, 400); c.clip();
57
+ // 桥面+栏杆(从右下斜向左上,栏杆随世界一起扭)
58
+ c.fillStyle = '#8a5a3a'; c.beginPath(); c.moveTo(790, 470); c.lineTo(370, 410); c.lineTo(370, 540); c.lineTo(790, 540); c.closePath(); c.fill();
59
+ c.lineCap = 'round'; for (let k = 0; k < 14; k++) { c.strokeStyle = ['#a8703a', '#6a3a22', '#c88a52', '#7a4a2a'][k % 4]; c.lineWidth = 6; c.beginPath(); for (let i = 0; i <= 12; i++) { const q = i / 12, x = lerp(800, 360, q), y = lerp(478 + k * 5, 418 + k * 9, q) + Math.sin(q * 5 + t * 3 + k) * 3; i ? c.lineTo(x, y) : c.moveTo(x, y); } c.stroke(); }
60
+ c.strokeStyle = '#3a1e14'; c.lineCap = 'round';
61
+ for (let k = 0; k < 3; k++) { c.lineWidth = 6 - k; c.beginPath(); for (let i = 0; i <= 20; i++) { const q = i / 20, x = lerp(800, 360, q), y = lerp(452 + k * 18, 372 + k * 12, q) + Math.sin(q * 6 + t * 3) * 4; i ? c.lineTo(x, y) : c.moveTo(x, y); } c.stroke(); }
62
+ for (let i = 0; i < 9; i++) { const q = i / 8, x = lerp(790, 380, q); c.lineWidth = 4; c.beginPath(); c.moveTo(x, lerp(452, 372, q)); c.lineTo(x + 2, lerp(500, 410, q)); c.stroke(); }
63
+ // 远处两个戴帽人影
64
+ [[480, 395], [515, 400]].forEach(([x, y], k) => { c.fillStyle = '#1a1420'; c.beginPath(); c.ellipse(x, y, 7, 22, 0.05 * Math.sin(t * 2 + k), 0, Math.PI * 2); c.fill(); c.fillRect(x - 9, y - 26, 18, 4); c.fillRect(x - 5, y - 32, 10, 7); });
65
+ // 尖叫者:骷髅般的头,双手捂脸,身体是一道扭动的黑袍
66
+ const fx = 690, fy = 470, sway = Math.sin(t * 3.2) * 0.12;
67
+ c.save(); c.translate(fx, fy + 70); c.rotate(sway);
68
+ c.fillStyle = '#2a1e2e'; c.beginPath(); c.moveTo(-30, 0); c.bezierCurveTo(-40, -40, -14, -50, -20, -86); c.lineTo(20, -86); c.bezierCurveTo(20, -50, 44, -30, 34, 0); c.closePath(); c.fill();
69
+ c.translate(0, -104); c.rotate(-sway * 0.6);
70
+ c.fillStyle = '#d8c890'; c.beginPath(); c.ellipse(0, 0, 21, 28, 0, 0, Math.PI * 2); c.fill();
71
+ c.strokeStyle = '#2a1e2e'; c.lineWidth = 2.5; c.stroke();
72
+ c.fillStyle = '#2a1e2e'; c.beginPath(); c.ellipse(-8, -6, 5, 7, 0, 0, Math.PI * 2); c.ellipse(8, -6, 5, 7, 0, 0, Math.PI * 2); c.fill();
73
+ const mo = 7 + Math.sin(t * 9) * 2; c.beginPath(); c.ellipse(0, 13, 5, mo, 0, 0, Math.PI * 2); c.fill();
74
+ c.fillStyle = '#c8b480'; [[-1], [1]].forEach(([s]) => { c.beginPath(); c.ellipse(s * 22, 4, 7, 15, -s * 0.25, 0, Math.PI * 2); c.fill(); c.stroke(); });
75
+ c.strokeStyle = '#2a1e2e'; c.lineWidth = 9; [[-1], [1]].forEach(([s]) => { c.beginPath(); c.moveTo(s * 22, 14); c.quadraticCurveTo(s * 30, 50, s * 20, 72); c.stroke(); });
76
+ c.restore();
77
+ c.restore();
78
+ }
79
+ function furniture(c, t) {
80
+ const wob = (x0, y0, x1, y1, w, col, ph) => { c.strokeStyle = col; c.lineWidth = w; c.lineCap = 'round'; c.beginPath(); for (let i = 0; i <= 12; i++) { const q = i / 12; const x = lerp(x0, x1, q) + Math.sin(q * 5 + t * 3.5 + ph) * 7 * Math.sin(q * Math.PI), y = lerp(y0, y1, q); i ? c.lineTo(x, y) : c.moveTo(x, y); } c.stroke(); };
81
+ // 椅子(在少女后)
82
+ wob(1440, 470, 1446, 910, 16, '#2a1a14', 0); wob(1486, 480, 1490, 910, 16, '#2a1a14', 1);
83
+ // 桌
84
+ c.fillStyle = '#5a2e1c'; c.beginPath(); c.moveTo(808, 618); c.quadraticCurveTo(1010, 606 + Math.sin(t * 3) * 6, 1212, 620); c.lineTo(1206, 652); c.quadraticCurveTo(1010, 642, 814, 652); c.closePath(); c.fill();
85
+ c.strokeStyle = '#1a0e14'; c.lineWidth = 5; c.stroke();
86
+ wob(846, 650, 840, 905, 22, '#3a1e14', 2); wob(1176, 650, 1182, 905, 22, '#3a1e14', 3);
87
+ wob(846, 650, 840, 905, 6, '#8a4a2a', 2.3); wob(1176, 650, 1182, 905, 6, '#8a4a2a', 3.3);
88
+ // 窗框:深色、歪的
89
+ c.strokeStyle = '#1a1020'; c.lineWidth = 16; c.lineJoin = 'round'; c.beginPath(); c.moveTo(358, 128); c.quadraticCurveTo(580, 116 + Math.sin(t * 2.5) * 8, 804, 132); c.quadraticCurveTo(810, 340, 800, 552); c.quadraticCurveTo(580, 562, 360, 550); c.quadraticCurveTo(352, 340, 358, 128); c.stroke();
90
+ c.lineWidth = 9; c.beginPath(); c.moveTo(580, 128); c.quadraticCurveTo(586 + Math.sin(t * 3) * 6, 340, 578, 552); c.stroke();
91
+ }
92
+ // 角色
93
+ const girlPal = { skin: '#e8d4a0', hair: '#e2b84a', hairLine: '#a8782a', dress: '#1e2238', line: '#120c18', lw: 5, cheek: 'rgba(200,90,70,.18)', lip: '#a8282a', iris: '#1a1420', cuff: '#e8e0c8', cupBody: '#e8e0c8', cupRim: '#6a2a1a', shoe: '#120c18', browC: '#6a4a2a' };
94
+ const catPal = { orange: '#e07a2a', white: '#efe6c8', stripe: '#8a3a14', line: '#120c18', lw: 5, eye: '#c8d040', noseC: '#c84a3a' };
95
+ function chars(c, t, lt, ch) {
96
+ const L = P.scratch('mu_chars'), g = L.getContext('2d'); g.reset();
97
+ const K = RIG.cat({ tail: ch.tail * 1.4, blink: 0, breathe: ch.breathe });
98
+ const G = RIG.girl({ cup: ch.cup, sip: ch.sip, breathe: ch.breathe, hair: 'long' });
99
+ RIG.drawCat(g, K, { ...catPal, lw: 0 });
100
+ RIG.drawGirl(g, G, { ...girlPal, lw: 0, mode: 'fill', apron: null });
101
+ // 绿影:蒙克脸上的黄绿阴影
102
+ g.save(); g.clip(G.face); g.fillStyle = 'rgba(120,150,70,.4)'; g.filter = 'blur(8px)'; g.beginPath(); g.ellipse(G.A.headC[0] + 20, G.A.headC[1] + 10, 40, 50, 0, 0, Math.PI * 2); g.fill(); g.filter = 'none'; g.restore();
103
+ // 流线肌理(角色身上也是长弯笔)
104
+ g.save(); g.globalCompositeOperation = 'source-atop'; g.globalAlpha = 0.55; g.lineCap = 'round';
105
+ const r = rng(17 + P.boilSeed(t, 10));
106
+ for (let i = 0; i < 900; i++) { const x = 380 + r() * 1100, y = 260 + r() * 680;
107
+ const a = x < 760 ? Math.atan2(y - 780, x - 540) + Math.PI / 2 : -Math.PI / 2 + 0.6 * Math.sin(y * 0.02 + t * 3);
108
+ g.strokeStyle = x < 760 ? (r() < 0.5 ? '#f0a040' : '#b8501a') : (y < 450 ? (r() < 0.5 ? '#f4d070' : '#b88a2a') : (r() < 0.5 ? '#34406a' : '#0e1020')); g.lineWidth = 5;
109
+ g.beginPath(); g.moveTo(x, y); g.quadraticCurveTo(x + Math.cos(a) * 14 + Math.sin(t * 4 + i) * 6, y + Math.sin(a) * 14, x + Math.cos(a) * 28, y + Math.sin(a) * 28); g.stroke(); }
110
+ g.restore();
111
+ c.drawImage(L, 0, 0);
112
+ // 白领(端庄的 1890 年代挪威少女裙)
113
+ c.fillStyle = '#ece4cc'; c.beginPath(); c.ellipse(G.A.neck[0] + 4, G.A.neck[1] + 8, 30, 12, -0.15, 0, Math.PI * 2); c.fill();
114
+ RIG.drawCat(c, K, { ...catPal, mode: 'line' });
115
+ RIG.drawGirl(c, G, { ...girlPal, mode: 'line', apron: null });
116
+ KIT.nearArm(c, G, { fill: girlPal.dress, line: girlPal.line, lw: 5, skin: girlPal.skin, cuff: girlPal.cuff, cupBody: girlPal.cupBody, cupRim: girlPal.cupRim,
117
+ pattern: g3 => { g3.lineCap = 'round'; g3.lineWidth = 5; for (let i = 0; i < 26; i++) { const x = 1150 + (i * 37) % 280, y = 330 + (i * 53) % 300; g3.strokeStyle = i % 2 ? '#34406a' : '#0e1020'; g3.beginPath(); g3.moveTo(x, y); g3.quadraticCurveTo(x + 6 + Math.sin(t * 4 + i) * 6, y + 14, x + 2, y + 30); g3.stroke(); } } });
118
+ // 长金发:一绺绺波浪线,向下传的行波
119
+ c.save(); c.lineCap = 'round';
120
+ for (let k = 0; k < 9; k++) { const x0 = G.A.nape[0] - 30 + k * 7, y0 = G.A.nape[1] - 60 + k * 4; c.strokeStyle = k % 2 ? '#f0c858' : '#b8862a'; c.lineWidth = 7;
121
+ c.beginPath(); for (let i = 0; i <= 16; i++) { const q = i / 16; const x = x0 + 18 * q + Math.sin(q * 9 - t * 7 + k) * (4 + 12 * q), y = y0 + q * 230; i ? c.lineTo(x, y) : c.moveTo(x, y); } c.stroke(); }
122
+ c.restore();
123
+ // 猫:张嘴(被画外的尖叫传染)
124
+ c.fillStyle = '#3a1418'; c.beginPath(); c.ellipse(K.nose[0] - 4, K.nose[1] + 16, 6, 7 + Math.sin(t * 9) * 3, 0, 0, Math.PI * 2); c.fill();
125
+ return G;
126
+ }
127
+
128
+ return {
129
+ draw(c, lt, t) {
130
+ const ch = KIT.choreo(lt, t);
131
+ const S = P.scratch('mu_frame'), g = S.getContext('2d'); g.reset();
132
+ baseFill(g, t);
133
+ flowLines(g, t);
134
+ bridge(g, t);
135
+ furniture(g, t);
136
+ const G = chars(g, t, lt, ch);
137
+ // 热气:三道粗波浪线(像天空色带)
138
+ g.save(); g.lineCap = 'round'; [0, 1].forEach(k => { g.strokeStyle = ['#f6e0a0', '#f4c03a'][k]; g.lineWidth = 3.5; g.beginPath(); for (let i = 0; i <= 20; i++) { const q = i / 20, x = G.cup.x - 10 + k * 10 - q * 60 * ch.cup + Math.sin(q * 7 - t * 8 + k) * 12 * q, y = G.cup.y - 10 - q * 90; i ? g.lineTo(x, y) : g.moveTo(x, y); } g.stroke(); }); g.restore();
139
+ // 整个世界在流动:行位移+轻微列位移
140
+ const T2 = P.scratch('mu_warp'), g2 = T2.getContext('2d'); g2.reset();
141
+ g2.fillStyle = '#3a1410'; g2.fillRect(0, 0, W, H);
142
+ KIT.warpRows(g2, S, y => 9 * Math.sin(y * 0.012 + t * 4.2) + 4 * Math.sin(y * 0.031 - t * 6), 4, 14);
143
+ c.fillStyle = '#3a1410'; c.fillRect(0, 0, W, H);
144
+ KIT.warpCols(c, T2, x => 6 * Math.sin(x * 0.009 - t * 3.6), 4, 8);
145
+ },
146
+ label(c, lt, t) {
147
+ const L = P.scratch('mu_label'), g = L.getContext('2d'); g.reset();
148
+ KIT.label(g, { title: '表现主义', sub: 'EDVARD MUNCH · 1893', tFont: '84px "LXGWWenKai-500"', sFont: '34px "IMFellEnglish-400"', tCol: '#f4c03a', sCol: '#f08a24', y: 132, sy: 186, spacing: 4,
149
+ before: g2 => { g2.save(); g2.font = '84px "LXGWWenKai-500"'; g2.letterSpacing = '4px'; g2.textAlign = 'right'; g2.lineWidth = 10; g2.strokeStyle = '#1a0a10'; g2.lineJoin = 'round'; g2.strokeText('表现主义', 1866, 132); g2.font = '34px "IMFellEnglish-400"'; g2.letterSpacing = '0px'; g2.lineWidth = 7; g2.strokeText('EDVARD MUNCH · 1893', 1866, 186); g2.restore(); } });
150
+ KIT.warpRows(c, L, y => (y > 30 && y < 210) ? 5 * Math.sin(y * 0.09 + t * 7) : 0, 3);
151
+ },
152
+ };
153
+ })();
@@ -0,0 +1,215 @@
1
+ // 敦煌壁画(莫高窟·盛唐)——纯代码。
2
+ // 管线:①土红地仗(斑驳、烟熏,缓存)→ ②顶部垂幔纹+卷草边 → ③窗=石青天:飞天带飘带飞过、祥云流动
3
+ // → ④莲花砖地、低案、油灯 → ⑤猫:赭红平涂+铁线描 → ⑥少女:唐代供养人(高髻、花钿、间色裙、披帛飘动)
4
+ // → ⑦天花(散落的花瓣)飘下 → ⑧罩层:颜料不匀、龟裂、剥落露出白灰地仗、整体褪色;剥落的颜料片偶尔掉下
5
+ SCENES['20_dunhuang'] = (() => {
6
+ const W = 1920, H = 1080, { clamp, lerp, rng } = U, P = PAINT;
7
+ const { ss } = U;
8
+ const C = { red: '#a5452f', redDk: '#7a2e1e', ochre: '#e08a3a', green: '#3f8f6e', green2: '#6ab08e', blue: '#2f5f8f', blue2: '#5a86b0', white: '#ecdfc4', ink: '#2a1a14', line: '#5a2414', skin: '#f2dcc4', gold: '#d8b05a', plaster: '#d8c6a2' };
9
+ const LW = 2.6;
10
+ const F = (c, p, fill, lw = LW, line = C.line) => { if (fill) { c.fillStyle = fill; c.fill(p); } if (lw) { c.strokeStyle = line; c.lineWidth = lw; c.stroke(p); } };
11
+
12
+ // ---------- 地仗:土红,斑驳 ----------
13
+ const ground = () => P.cached('dh_ground', W, H, (g) => {
14
+ g.drawImage(P.texture('dh_red', C.red, { scale: 0.003, amt: 30, grain: 16, dark: '#7a3020', seed: 3 }), 0, 0);
15
+ // 团花:稀疏的淡色圆形花饰(褪色)
16
+ const r = rng(8);
17
+ for (let i = 0; i < 16; i++) { const x = 100 + r() * 1700, y = 260 + r() * 400; if (x > 330 && x < 840 && y < 600) continue; if (x > 1080 && x < 1520) continue;
18
+ g.save(); g.translate(x, y); g.globalAlpha = 0.5;
19
+ for (let k = 0; k < 8; k++) { g.rotate(Math.PI / 4); g.fillStyle = k % 2 ? C.green2 : C.white; g.beginPath(); g.ellipse(0, -16, 7, 14, 0, 0, Math.PI * 2); g.fill(); }
20
+ g.fillStyle = C.blue2; g.beginPath(); g.arc(0, 0, 8, 0, Math.PI * 2); g.fill(); g.restore(); }
21
+ // 顶部:卷草边+垂幔纹(三角垂帐,石青石绿土红交替)
22
+ g.fillStyle = C.redDk; g.fillRect(0, 0, W, 34);
23
+ g.strokeStyle = C.green2; g.lineWidth = 4; g.beginPath(); for (let x = 0; x <= W; x += 4) { const y = 17 + Math.sin(x * 0.05) * 9; x ? g.lineTo(x, y) : g.moveTo(x, y); } g.stroke();
24
+ for (let x = 0; x < W; x += 80) { g.strokeStyle = C.white; g.lineWidth = 2.5; g.beginPath(); g.arc(x + 40, 17 + Math.sin((x + 40) * 0.05) * 9, 8, 0, Math.PI * 1.5); g.stroke(); }
25
+ const cols = [C.blue, C.green, C.white, C.red];
26
+ for (let x = 0, k = 0; x < W; x += 64, k++) { g.fillStyle = cols[k % 4]; g.beginPath(); g.moveTo(x, 34); g.lineTo(x + 64, 34); g.lineTo(x + 32, 84); g.closePath(); g.fill(); g.strokeStyle = C.line; g.lineWidth = 2; g.stroke();
27
+ g.fillStyle = C.gold; g.beginPath(); g.arc(x + 32, 88, 5, 0, Math.PI * 2); g.fill(); }
28
+ g.fillStyle = C.redDk; g.fillRect(0, 34, W, 5);
29
+ });
30
+
31
+ // ---------- 窗:石青天、祥云、飞天 ----------
32
+ function cloud(c, x, y, s, t, ph) {
33
+ c.save(); c.translate(x, y); c.scale(s, s);
34
+ c.fillStyle = C.white; c.strokeStyle = C.line; c.lineWidth = 2.2 / s;
35
+ [[0, 0, 18], [-22, 6, 13], [20, 6, 14], [-8, -14, 12], [10, -12, 11]].forEach(([dx, dy, r]) => { c.beginPath(); c.arc(dx, dy, r, 0, Math.PI * 2); c.fill(); c.stroke(); });
36
+ c.fillStyle = C.white; c.beginPath(); c.ellipse(0, 4, 32, 12, 0, 0, Math.PI * 2); c.fill();
37
+ [[0, 0, 10], [-22, 6, 7], [20, 6, 8]].forEach(([dx, dy, r]) => { c.strokeStyle = C.green; c.beginPath(); c.arc(dx, dy, r, 0.3, Math.PI * 1.7); c.stroke(); });
38
+ // 云尾:一条渐细的尾巴
39
+ c.fillStyle = C.white; c.fill(KIT.ribbon(KIT.densify([[30, 8], [60, 14 + Math.sin(ph) * 3], [95, 6 + Math.sin(ph + 1) * 5], [120, 12]], 5), q => 16 * (1 - q) + 1)); c.restore();
40
+ }
41
+ function apsara(c, t, lt) {
42
+ // 飞天:横卧的身体朝左飞,头在左,两条长飘带拖在身后成 S 形(行波)
43
+ const x = 760 - lt * 300, y = 300 + Math.sin(lt * 3.2) * 22;
44
+ c.save(); c.translate(x, y); c.scale(1.35, 1.35); c.rotate(-0.12 + Math.sin(lt * 3.2) * 0.05);
45
+ const rib = (ox, oy, len, col, ph, amp) => { const pts = []; for (let i = 0; i <= 18; i++) { const q = i / 18; pts.push([ox + q * len, oy + Math.sin(q * 5.5 - t * 7 + ph) * amp * (0.3 + q) - q * 30]); } const p = KIT.ribbon(pts, q => 15 * (1 - q * 0.7)); F(c, p, col, 2); };
46
+ rib(40, -6, 300, C.green, 0, 26); rib(30, 10, 270, C.red, 1.6, 30);
47
+ // 绕过头顶的大弧飘带(飞天最有辨识度的形):从肩起,弧过头顶,再甩到身后
48
+ { const pts = []; for (let i = 0; i <= 30; i++) { const q = i / 30; let x, y; if (q < 0.45) { const a = Math.PI * 0.9 - q / 0.45 * Math.PI * 1.15; x = -40 + Math.cos(a) * 70; y = -30 - Math.abs(Math.sin(a)) * 80; } else { const u = (q - 0.45) / 0.55; x = -40 + Math.cos(Math.PI * 0.9 - Math.PI * 1.15) * 70 + u * 260; y = -30 - Math.abs(Math.sin(-Math.PI * 0.25)) * 80 + Math.sin(u * 5 - t * 7) * 26 * u - u * 20; } pts.push([x, y]); }
49
+ F(c, KIT.ribbon(pts, q => 13 * (1 - q * 0.6)), C.blue2, 2); }
50
+ // 长裙(向后飘)
51
+ F(c, RIG.smooth([[0, -16], [70, -18], [150, -10 + Math.sin(t * 6) * 6], [190, 4 + Math.sin(t * 6 + 1) * 8], [140, 20], [60, 18], [0, 14]]), C.blue, 2.2);
52
+ for (let k = 0; k < 3; k++) { c.strokeStyle = C.white; c.lineWidth = 2; c.beginPath(); c.moveTo(30 + k * 40, -12); c.quadraticCurveTo(60 + k * 40, 0, 40 + k * 46, 16); c.stroke(); }
53
+ // 上身、手臂托花盘
54
+ F(c, RIG.smooth([[-40, -18], [0, -22], [8, 0], [0, 16], [-36, 12]]), C.skin, 2.2);
55
+ F(c, RIG.smooth([[-30, -14], [-70, -34], [-86, -30], [-78, -20], [-40, -4]]), C.skin, 2);
56
+ c.fillStyle = C.gold; c.beginPath(); c.ellipse(-90, -36, 18, 6, -0.2, 0, Math.PI * 2); c.fill(); c.strokeStyle = C.line; c.lineWidth = 2; c.stroke();
57
+ [[-98, -44, C.red], [-88, -46, C.white], [-80, -42, C.red]].forEach(([fx, fy, fc]) => { c.fillStyle = fc; c.beginPath(); c.arc(fx, fy, 5, 0, Math.PI * 2); c.fill(); });
58
+ // 头:头光(圆光)+发髻
59
+ c.fillStyle = 'rgba(106,176,142,.28)'; c.beginPath(); c.arc(-56, -24, 30, 0, Math.PI * 2); c.fill(); c.strokeStyle = C.line; c.lineWidth = 1.4; c.stroke();
60
+ c.strokeStyle = C.white; c.lineWidth = 2.5; c.beginPath(); c.arc(-56, -24, 24, 0, Math.PI * 2); c.stroke();
61
+ F(c, (() => { const p = new Path2D(); p.ellipse(-56, -22, 15, 17, 0, 0, Math.PI * 2); return p; })(), C.skin, 2);
62
+ c.fillStyle = '#1e1410'; c.beginPath(); c.ellipse(-52, -38, 13, 9, 0.3, 0, Math.PI * 2); c.fill(); c.beginPath(); c.arc(-46, -48, 7, 0, Math.PI * 2); c.fill();
63
+ c.fillStyle = C.line; c.fillRect(-64, -23, 4, 1.6); c.fillStyle = '#c0392b'; c.beginPath(); c.arc(-66, -14, 1.8, 0, Math.PI * 2); c.fill();
64
+ c.restore();
65
+ }
66
+ function windowPanel(c, t, lt) {
67
+ c.save(); c.beginPath(); c.rect(370, 140, 420, 400); c.clip();
68
+ const sg = c.createLinearGradient(0, 140, 0, 540); sg.addColorStop(0, '#2a5a86'); sg.addColorStop(1, '#4f86a8'); c.fillStyle = sg; c.fillRect(370, 140, 420, 400);
69
+ [[460, 200, 0.9, 0], [700, 450, 1.1, 1], [520, 490, 0.8, 2], [760, 190, 0.7, 3]].forEach(([x, y, s, k]) => cloud(c, ((x - lt * (40 + k * 12) - 370 + 1000) % 520) + 330, y + Math.sin(t * 1.5 + k) * 4, s, t, t * 3 + k));
70
+ apsara(c, t, lt);
71
+ c.restore();
72
+ // 窗框:卷草纹边框(石绿+白卷须)
73
+ c.fillStyle = C.green; c.fillRect(348, 118, 464, 22); c.fillRect(348, 540, 464, 24); c.fillRect(348, 118, 22, 446); c.fillRect(790, 118, 22, 446);
74
+ c.strokeStyle = C.white; c.lineWidth = 2.5;
75
+ const vine = (x0, y0, x1, y1) => { const L = Math.hypot(x1 - x0, y1 - y0), n = Math.floor(L / 34); for (let i = 0; i < n; i++) { const q = (i + 0.5) / n, x = lerp(x0, x1, q), y = lerp(y0, y1, q); c.beginPath(); c.arc(x, y, 7, i % 2 ? 0 : Math.PI, (i % 2 ? 0 : Math.PI) + Math.PI * 1.4); c.stroke(); } };
76
+ vine(359, 129, 801, 129); vine(359, 552, 801, 552); vine(359, 140, 359, 540); vine(801, 140, 801, 540);
77
+ c.strokeStyle = C.line; c.lineWidth = 3; c.strokeRect(348, 118, 464, 446); c.strokeRect(370, 140, 420, 400);
78
+ }
79
+ // ---------- 地:莲花砖 ----------
80
+ const floor = () => P.cached('dh_floor', W, H, (g) => {
81
+ g.fillStyle = '#b8654a'; g.fillRect(0, 704, W, H - 704);
82
+ g.fillStyle = C.redDk; g.fillRect(0, 700, W, 8);
83
+ for (let x = 0; x < W; x += 150) for (let y = 714; y < H; y += 150) {
84
+ g.strokeStyle = 'rgba(70,30,20,.6)'; g.lineWidth = 2; g.strokeRect(x + 3, y + 3, 144, 144);
85
+ g.save(); g.translate(x + 75, y + 75);
86
+ for (let k = 0; k < 8; k++) { g.rotate(Math.PI / 4); g.fillStyle = k % 2 ? C.green2 : C.white; g.beginPath(); g.ellipse(0, -34, 12, 26, 0, 0, Math.PI * 2); g.fill(); g.strokeStyle = C.line; g.lineWidth = 1.6; g.stroke(); }
87
+ g.fillStyle = C.blue2; g.beginPath(); g.arc(0, 0, 16, 0, Math.PI * 2); g.fill(); g.stroke(); g.fillStyle = C.gold; g.beginPath(); g.arc(0, 0, 6, 0, Math.PI * 2); g.fill();
88
+ g.restore(); }
89
+ });
90
+ // ---------- 案与油灯 ----------
91
+ function table(c, t) {
92
+ F(c, new Path2D('M806 616 L1214 616 L1206 650 L814 650 Z'), C.green);
93
+ F(c, new Path2D('M830 650 L1190 650 L1190 700 L830 700 Z'), C.red);
94
+ c.fillStyle = C.redDk; [[880, 660], [990, 660], [1100, 660]].forEach(([x, y]) => { c.beginPath(); c.moveTo(x, y + 34); c.quadraticCurveTo(x + 6, y + 4, x + 40, y + 6); c.quadraticCurveTo(x + 74, y + 4, x + 80, y + 34); c.closePath(); c.fill(); }); // 壸门
95
+ F(c, new Path2D('M838 700 L868 700 L862 905 L842 905 Z'), C.red); F(c, new Path2D('M1152 700 L1182 700 L1178 905 L1158 905 Z'), C.red);
96
+ // 油灯:灯盏+跳动的火苗
97
+ F(c, new Path2D('M880 604 Q910 618 940 604 L934 616 Q910 626 886 616 Z'), C.gold, 2);
98
+ const fl = 1 + Math.sin(t * 17) * 0.12 + Math.sin(t * 29) * 0.08, sw = Math.sin(t * 11) * 4;
99
+ c.save(); c.globalCompositeOperation = 'lighter'; const gg = c.createRadialGradient(910, 585, 2, 910, 585, 60); gg.addColorStop(0, 'rgba(255,200,90,.45)'); gg.addColorStop(1, 'rgba(255,200,90,0)'); c.fillStyle = gg; c.fillRect(850, 525, 120, 120); c.restore();
100
+ F(c, new Path2D(`M902 604 Q${898 + sw} ${598 - 14 * fl} ${910 + sw} ${574 - 14 * fl} Q${922 + sw * 0.5} ${596 - 10 * fl} 918 604 Z`), '#f2a43a', 1.8);
101
+ c.fillStyle = '#fbe7a0'; c.beginPath(); c.ellipse(910 + sw * 0.4, 596, 4, 7 * fl, 0, 0, Math.PI * 2); c.fill();
102
+ }
103
+
104
+ // 披帛:石绿正面+白色描花,带一点翻面(窄处露出土红里子)
105
+ function shawl(c, pts, w, t) {
106
+ const D = KIT.densify(pts, 3), wq = q => w * (0.75 + 0.25 * Math.sin(q * 9 - t * 5)) * (1 - q * 0.25);
107
+ F(c, KIT.ribbon(D, wq), C.green2, 2.2);
108
+ c.strokeStyle = C.white; c.lineWidth = 2; c.beginPath(); D.forEach((p, i) => i ? c.lineTo(p[0], p[1]) : c.moveTo(p[0], p[1])); c.setLineDash([3, 9]); c.stroke(); c.setLineDash([]);
109
+ D.forEach((p, i) => { if (i % 9 !== 4) return; c.fillStyle = C.red; c.beginPath(); c.arc(p[0], p[1], 3.2, 0, Math.PI * 2); c.fill(); });
110
+ }
111
+ // ---------- 少女:唐代供养人 ----------
112
+ function girl(c, t, ch) {
113
+ const G = RIG.girl({ cup: ch.cup, sip: ch.sip, breathe: ch.breathe, hair: 'bun' });
114
+ const A = G.A;
115
+ // 披帛(背后那段):搭过肩、从椅背后飘出一个大 S,末端向外扬(行波,越往下摆幅越大)
116
+ const pbBack = []; for (let i = 0; i <= 26; i++) { const q = i / 26; pbBack.push([A.back[0] - 10 + q * 230 + Math.sin(q * 4.2 - t * 5.5) * 64 * q, A.nape[1] - 6 + q * 400 - Math.max(0, q - 0.7) * 300 * (0.7 + 0.3 * Math.sin(t * 4))]); }
117
+ shawl(c, pbBack, 32, t);
118
+ F(c, G.farSleeve, C.green); F(c, G.farHand, C.skin); F(c, G.farCuff, C.red, 2);
119
+ // 间色裙:红绿竖条(唐代女装最有辨识度的一条)
120
+ F(c, G.skirt, C.red);
121
+ c.save(); c.clip(G.skirt); for (let x = 1090; x < 1470; x += 44) { c.fillStyle = C.green; c.beginPath(); c.moveTo(x + 6, 600); c.lineTo(x + 26, 600); c.lineTo(x + 12 - (x - 1300) * 0.18, 940); c.lineTo(x - 10 - (x - 1300) * 0.18, 940); c.closePath(); c.fill(); }
122
+ c.restore(); c.strokeStyle = C.line; c.lineWidth = LW; c.stroke(G.skirt);
123
+ G.folds.forEach(f => { c.strokeStyle = C.line; c.lineWidth = 1.6; c.stroke(f); });
124
+ F(c, G.shoe, C.ink);
125
+ // 襦:石青短衫,白边交领;高腰红带
126
+ F(c, G.torso, C.blue);
127
+ c.strokeStyle = C.white; c.lineWidth = 7; c.beginPath(); c.moveTo(A.neck[0] - 14, A.neck[1] + 2); c.quadraticCurveTo(A.chest[0] + 6, A.chest[1] - 20, A.chest[0] + 26, A.chest[1] + 40); c.stroke();
128
+ c.fillStyle = C.red; c.beginPath(); c.moveTo(1290, 590); c.lineTo(1422, 594); c.lineTo(1424, 616); c.lineTo(1294, 614); c.closePath(); c.fill(); c.strokeStyle = C.line; c.lineWidth = 2; c.stroke();
129
+ F(c, G.neck, C.skin);
130
+ // 高髻:金发盘在头顶(把 bun 移上去、加大),簪花、金钗
131
+ F(c, G.hairBack, '#e2b850', 2);
132
+ const top = A.headTop, bunC = [top[0] + 18, top[1] - 22];
133
+ F(c, (() => { const p = new Path2D(); p.ellipse(bunC[0], bunC[1], 34, 30, -0.3, 0, Math.PI * 2); p.moveTo(bunC[0] + 40, bunC[1] - 6); p.ellipse(bunC[0] + 22, bunC[1] - 14, 22, 20, 0.4, 0, Math.PI * 2); return p; })(), '#e2b850', 2);
134
+ c.strokeStyle = '#a8782a'; c.lineWidth = 1.6; for (let k = 0; k < 4; k++) { c.beginPath(); c.arc(bunC[0], bunC[1], 10 + k * 7, -2.4, -0.4); c.stroke(); }
135
+ c.strokeStyle = C.gold; c.lineWidth = 3.5; c.beginPath(); c.moveTo(bunC[0] - 50, bunC[1] + 6); c.lineTo(bunC[0] + 40, bunC[1] - 24); c.stroke();
136
+ [[bunC[0] - 30, bunC[1] - 26, C.red], [bunC[0] + 4, bunC[1] - 40, C.white], [bunC[0] + 44, bunC[1] - 30, C.red]].forEach(([x, y, fc], k) => { const sw = Math.sin(t * 5 + k) * 3; for (let j = 0; j < 5; j++) { c.fillStyle = fc; c.beginPath(); c.arc(x + sw + Math.cos(j * 1.26) * 6, y + Math.sin(j * 1.26) * 6, 4.5, 0, Math.PI * 2); c.fill(); } c.fillStyle = C.gold; c.beginPath(); c.arc(x + sw, y, 3, 0, Math.PI * 2); c.fill(); });
137
+ // 脸:白面、红线(铁线描)、花钿、斜红、点唇
138
+ F(c, G.face, C.skin, 2.2, '#8a3a24');
139
+ F(c, G.bangs, '#e2b850', 2);
140
+ c.fillStyle = '#c0392b'; const fh = G.A.forehead; c.save(); c.translate(fh[0] + 10, fh[1] + 12); for (let j = 0; j < 4; j++) { c.rotate(Math.PI / 2); c.beginPath(); c.ellipse(0, -4, 2, 4, 0, 0, Math.PI * 2); c.fill(); } c.restore();
141
+ c.strokeStyle = 'rgba(200,60,50,.32)'; c.lineWidth = 2; c.beginPath(); c.moveTo(G.eye.x + 16, G.eye.y - 4); c.quadraticCurveTo(G.eye.x + 22, G.eye.y + 10, G.eye.x + 16, G.eye.y + 22); c.stroke();
142
+ c.strokeStyle = C.ink; c.lineWidth = ch.blink ? 2 : 2.4; c.stroke(G.lid); if (!ch.blink) { c.fillStyle = C.ink; c.beginPath(); c.ellipse(G.eye.x - 1, G.eye.y + 1.5, 3, 3.4, 0, 0, Math.PI * 2); c.fill(); }
143
+ c.strokeStyle = '#5a3a1a'; c.lineWidth = 3; c.beginPath(); c.moveTo(G.eye.x - 10, G.eye.y - 14); c.quadraticCurveTo(G.eye.x + 2, G.eye.y - 20, G.eye.x + 14, G.eye.y - 15); c.stroke(); // 蛾眉
144
+ c.fillStyle = '#c0392b'; c.beginPath(); c.arc(G.lips[0][0] + 3, G.lips[0][1] + 3, 3.4, 0, Math.PI * 2); c.fill();
145
+ // 近侧手臂:石青窄袖+红袖口,披帛前段绕臂垂下
146
+ F(c, G.upperArm, C.blue); F(c, G.foreArm, C.blue); F(c, G.cuff, C.red, 2);
147
+ const E = A.elbow; const pbF = []; for (let i = 0; i <= 22; i++) { const q = i / 22; pbF.push([E[0] + 6 - q * 70 + Math.sin(q * 5 - t * 6 + 1) * 46 * q, E[1] + 6 + q * 300 - Math.max(0, q - 0.65) * 200 * (0.7 + 0.3 * Math.sin(t * 4.6 + 1))]); }
148
+ shawl(c, pbF, 28, t);
149
+ F(c, G.hand, C.skin, 2, '#8a3a24');
150
+ // 茶盏:越窑青瓷
151
+ const cp = G.cup; c.save(); c.translate(cp.x, cp.y); c.rotate(cp.tilt);
152
+ F(c, new Path2D(`M${-cp.w / 2} 0 Q0 ${cp.h * 1.3} ${cp.w / 2} 0 Z`), '#8ab89a', 2.2);
153
+ c.restore();
154
+ c.fillStyle = C.skin; c.beginPath(); c.arc(G.thumb[0], G.thumb[1], 6.5, 0, Math.PI * 2); c.fill(); c.strokeStyle = '#8a3a24'; c.lineWidth = 1.6; c.stroke();
155
+ return G;
156
+ }
157
+ function cat(c, t, ch) {
158
+ const K = RIG.cat({ tail: ch.tail, blink: ch.blink, breathe: ch.breathe * 2.5, look: Math.sin(t * 2.6) });
159
+ RIG.drawCat(c, K, { orange: C.ochre, white: C.white, stripe: '#8a3a1e', line: C.line, lw: LW, eye: '#5a9a6a', earInner: '#d88a7a' });
160
+ // 项圈:卷草小铃
161
+ c.strokeStyle = C.green; c.lineWidth = 7; c.beginPath(); c.arc(K.headC[0] - 6, K.headC[1] + 18, 52, 0.7, 2.3); c.stroke();
162
+ const bx = K.headC[0] + 4 + Math.sin(t * 8) * 4, by = K.headC[1] + 72; c.fillStyle = C.gold; c.beginPath(); c.arc(bx, by, 9, 0, Math.PI * 2); c.fill(); c.strokeStyle = C.line; c.lineWidth = 2; c.stroke();
163
+ }
164
+ // ---------- 罩层:颜料不匀、龟裂、剥落、褪色 ----------
165
+ const AGE_MASK = (x, y) => { const n = P.fbm(x * 0.006 + 11, y * 0.006 + 4, 4) + 0.18 * P.noise(x * 0.04, y * 0.04); const keep = (x > 1180 && x < 1360 && y > 260 && y < 470); return !keep && n > 0.33; };
166
+ const aging = () => P.cached('dh_age', W, H, (g) => {
167
+ const img = g.createImageData(W, H), d = img.data, pl = P.hex(C.plaster), r = rng(91);
168
+ for (let y = 0; y < H; y++) for (let x = 0; x < W; x++) {
169
+ const i = (y * W + x) * 4;
170
+ const n = P.fbm(x * 0.0032 + 11, y * 0.0032 + 4, 5) + 0.12 * P.noise(x * 0.03, y * 0.03);
171
+ const keep = (x > 1180 && x < 1360 && y > 260 && y < 470) || (x > 380 && x < 720 && y > 530 && y < 920) || (x > 1070 && x < 1530 && y > 240 && y < 950); // 角色身上不剥落(剥落在人身上会读成「衣服破洞」)
172
+ if (!keep && n > 0.3) { const v = (r() - 0.5) * 22 + (n - 0.3) * 60; d[i] = pl[0] + v; d[i + 1] = pl[1] + v; d[i + 2] = pl[2] + v * 0.8; d[i + 3] = 255; }
173
+ else if (!keep && n > 0.28) { d[i] = 70; d[i + 1] = 45; d[i + 2] = 30; d[i + 3] = 150; } // 剥落边缘的暗线
174
+ else { const m = P.fbm(x * 0.02, y * 0.02 + 7, 3); d[i] = 255; d[i + 1] = 245; d[i + 2] = 225; d[i + 3] = clamp(m * 0.5 + 0.1) * 80; } // 颜料粉化发白
175
+ }
176
+ g.putImageData(img, 0, 0);
177
+ // 龟裂
178
+ g.strokeStyle = 'rgba(50,25,15,.35)'; g.lineWidth = 1;
179
+ for (let k = 0; k < 500; k++) { let x = r() * W, y = r() * H, a = r() * 6.28; g.beginPath(); g.moveTo(x, y); for (let s = 0; s < 5 + r() * 8; s++) { a += (r() - .5) * 1.4; x += Math.cos(a) * (5 + r() * 12); y += Math.sin(a) * (5 + r() * 12); g.lineTo(x, y); } g.stroke(); }
180
+ // 顶部烟熏
181
+ const sg = g.createLinearGradient(0, 0, 0, 380); sg.addColorStop(0, 'rgba(40,20,10,.35)'); sg.addColorStop(1, 'rgba(40,20,10,0)'); g.fillStyle = sg; g.fillRect(0, 0, W, 380);
182
+ });
183
+ const FLAKES = (() => { const r = rng(55), o = []; let n = 0; while (o.length < 10 && n < 2000) { n++; const x = 100 + r() * 1700, y = 120 + r() * 700; { const n = P.fbm(x * 0.0032 + 11, y * 0.0032 + 4, 5); if (n > 0.26 && n < 0.3) o.push([x, y, 0.05 + r() * 0.7, 8 + r() * 10, r()]); } } return o; })();
184
+
185
+ return {
186
+ draw(c, lt, t) {
187
+ const ch = KIT.choreo(lt, t);
188
+ c.drawImage(ground(), 0, 0);
189
+ windowPanel(c, t, lt);
190
+ c.drawImage(floor(), 0, 0);
191
+ table(c, t);
192
+ cat(c, t, ch);
193
+ const G = girl(c, t, ch);
194
+ // 热气:几道白色卷云线
195
+ c.save(); c.strokeStyle = C.white; c.lineWidth = 3; c.lineCap = 'round'; [0, 1].forEach(k => { c.beginPath(); for (let i = 0; i <= 18; i++) { const q = i / 18, x = G.cup.x - 4 + k * 12 - q * 50 * ch.cup + Math.sin(q * 6 - t * 6 + k) * 8 * q, y = G.cup.y - 6 - q * 70; i ? c.lineTo(x, y) : c.moveTo(x, y); } c.stroke(); const e = [G.cup.x - 4 + k * 12 - 50 * ch.cup + Math.sin(6 - t * 6 + k) * 8, G.cup.y - 76]; c.beginPath(); c.arc(e[0] + 5, e[1], 5, Math.PI, Math.PI * 2.6); c.stroke(); }); c.restore();
196
+ // 天花:花瓣从窟顶散落
197
+ P.particles(30, 13, t, { x0: 0, x1: W, y0: 60, y1: H, speed: 90, drift: 50, life: 3 }).forEach(p => { c.save(); c.translate(p.x, p.y); c.rotate(p.a); c.scale(p.s * 1.6, p.s * 1.6 * (Math.abs(Math.cos(t * 3 + p.i)) * 0.8 + 0.2));
198
+ const fc = [C.white, C.red, C.green2, '#e8a0a0'][p.i % 4]; for (let j = 0; j < 4; j++) { c.rotate(Math.PI / 2); c.fillStyle = fc; c.beginPath(); c.ellipse(0, -7, 4, 8, 0, 0, Math.PI * 2); c.fill(); c.strokeStyle = C.line; c.lineWidth = 1.2; c.stroke(); } c.restore(); });
199
+ // 罩层
200
+ c.save(); c.globalCompositeOperation = 'saturation'; c.fillStyle = 'rgba(128,128,128,.3)'; c.fillRect(0, 0, W, H); c.restore();
201
+ c.save(); c.globalCompositeOperation = 'multiply'; c.globalAlpha = 0.5; c.drawImage(P.texture('dh_dirt', '#e8dcc8', { scale: 0.002, amt: 40, grain: 20, seed: 9 }), 0, 0); c.restore();
202
+ c.drawImage(aging(), 0, 0);
203
+ // 颜料片掉落:剥落边缘处一小片颜料脱开、旋转着落下
204
+ FLAKES.forEach(([x, y, t0, s, ph], k) => { const q = (lt - t0 * 0.9); if (q < 0) return; const fy = y + q * q * 600, fx = x + Math.sin(q * 6 + ph * 6) * 12; if (fy > H) return;
205
+ c.save(); c.translate(fx, fy); c.rotate(q * 6 + ph * 3); c.scale(1, Math.cos(q * 9 + ph)); c.fillStyle = [C.red, C.green, C.blue2][k % 3]; c.beginPath(); c.moveTo(-s, -s * 0.4); c.lineTo(s * 0.6, -s * 0.8); c.lineTo(s, s * 0.5); c.lineTo(-s * 0.3, s * 0.7); c.closePath(); c.fill(); c.restore(); });
206
+ },
207
+ label(c, lt) {
208
+ // 榜题框:浅色长方块、墨书,带剥落
209
+ c.save(); c.fillStyle = '#e6d6b2'; c.fillRect(1500, 52, 380, 150); c.strokeStyle = C.line; c.lineWidth = 4; c.strokeRect(1500, 52, 380, 150); c.strokeRect(1508, 60, 364, 134);
210
+ c.restore();
211
+ KIT.label(c, { title: '敦煌壁画', sub: '莫高窟 · 盛唐', tFont: '78px "LXGWWenKai-500"', sFont: '32px "LXGWWenKai-500"', tCol: '#2a1a14', sCol: C.redDk, x: 1858, y: 134, sy: 180, spacing: 6 });
212
+ c.save(); const r = rng(4); c.fillStyle = 'rgba(216,198,162,.9)'; for (let i = 0; i < 14; i++) { const x = 1500 + r() * 380, y = 52 + r() * 150; c.beginPath(); KIT.blob(c, x, y, 4 + r() * 10, i, 0.3, 8); c.fill(); } c.restore();
213
+ },
214
+ };
215
+ })();
@@ -0,0 +1,124 @@
1
+ // 草间弥生无限波点(《Infinity Mirror Room》1965–、黄南瓜 1994)——纯代码。
2
+ // 管线:①红房间:墙/地铺白点(大小两套网格,地板压扁成透视椭圆)→ ②窗=无限镜屋:黑底彩灯点从深处飞来、镜面倒影
3
+ // → ③桌上黄南瓜(瓣+黑点)→ ④角色:平涂+细黑线;红裙白点、猫的橘色区白点、白色区红点
4
+ // → ⑤会动的:所有点按「从猫出发的行波」一胀一缩(蠕动),裙上的点顺着裙子往下爬,镜屋灯飞来,南瓜呼吸
5
+ // → ⑥结尾 0.3s:自我消融(点越胀越大吞掉一切)
6
+ SCENES['21_kusama'] = (() => {
7
+ const W = 1920, H = 1080, { clamp, lerp, rng } = U, P = PAINT;
8
+ const { ss } = U;
9
+ const C = { red: '#d8181e', redDk: '#a80e14', white: '#fbf6ee', yellow: '#f6d02a', black: '#141210', skin: '#f6dcc8' };
10
+ const O = [540, 760]; // 行波原点(猫)
11
+ const wave = (x, y, t, k = 1) => 0.82 + 0.28 * Math.sin(Math.hypot(x - O[0], y - O[1]) * 0.012 - t * 6 * k);
12
+ const grid = (x0, y0, x1, y1, sp, seed, jit = 0.25) => { const r = rng(seed), o = []; for (let y = y0, j = 0; y < y1; y += sp * 0.87, j++) for (let x = x0 + (j % 2) * sp / 2; x < x1; x += sp) o.push([x + (r() - .5) * sp * jit, y + (r() - .5) * sp * jit, 0.75 + r() * 0.5, r() * 6.28, 0.85 + r() * 0.3]); return o; };
13
+ const WALL_BIG = grid(-40, 20, W + 40, 720, 104, 3), WALL_SM = grid(12, 64, W + 40, 720, 104, 4, 0.5);
14
+ const FLOOR = (() => { const r = rng(6), o = []; for (let row = 0; row < 9; row++) { const q = row / 8, y = 712 + Math.pow(q, 1.4) * 360, sp = 46 + q * 70, rr = 6 + q * 22; for (let x = -40 + (row % 2) * sp / 2; x < W + 40; x += sp) o.push([x + (r() - .5) * 10, y, rr * (0.8 + r() * 0.4), r() * 6.28, 0.9]); } return o; })();
15
+ const dot = (c, x, y, r, rot, asp, col) => { c.fillStyle = col; c.beginPath(); c.ellipse(x, y, r, r * asp, rot, 0, Math.PI * 2); c.fill(); };
16
+ let OBL = 0; // 消融程度(结尾)
17
+
18
+ function room(c, t) {
19
+ c.fillStyle = C.red; c.fillRect(0, 0, W, 708);
20
+ const fg = c.createLinearGradient(0, 700, 0, H); fg.addColorStop(0, '#c0141a'); fg.addColorStop(1, '#e02024'); c.fillStyle = fg; c.fillRect(0, 700, W, H - 700);
21
+ c.fillStyle = C.redDk; c.fillRect(0, 700, W, 6);
22
+ const g = 1 + OBL * 2.2;
23
+ WALL_BIG.forEach(([x, y, s, rot, asp]) => dot(c, x, y, 30 * s * wave(x, y, t) * g, rot, asp, C.white));
24
+ WALL_SM.forEach(([x, y, s, rot, asp]) => dot(c, x, y, 10 * s * wave(x, y, t, 1.3) * g, rot, asp, C.white));
25
+ FLOOR.forEach(([x, y, rr, rot]) => dot(c, x, y, rr * wave(x, y, t) * g, 0, 0.42, C.white));
26
+ }
27
+ // 无限镜屋:3D 点向镜头飞来(z 循环),投影+光晕;地面镜像
28
+ // 灯在一个 3D 格子上(镜面无限反射的感觉来自规则重复),z 循环向镜头飞来
29
+ const LIGHTS = (() => { const r = rng(9), o = []; for (let gx = -6; gx <= 6; gx++) for (let gy = -3; gy <= 3; gy++) for (let gz = 0; gz < 3; gz++) o.push([gx * 0.42 + (r() - .5) * 0.06, gy * 0.38 + (r() - .5) * 0.06, (gz + r() * 0.3) / 3, ['#ff3a4a', '#ffd43a', '#3aa8ff', '#6aff7a', '#ff7ad0', '#ffffff'][(r() * 6) | 0]]); return o; })();
30
+ function mirrorRoom(c, t) {
31
+ c.save(); c.beginPath(); c.rect(370, 140, 420, 400); c.clip();
32
+ c.fillStyle = '#05040a'; c.fillRect(370, 140, 420, 400);
33
+ const cx = 580, cy = 330, F = 150;
34
+ c.strokeStyle = 'rgba(120,120,160,.22)'; c.lineWidth = 1; [[370, 140], [790, 140], [370, 540], [790, 540]].forEach(([x, y]) => { c.beginPath(); c.moveTo(cx, cy); c.lineTo(x, y); c.stroke(); });
35
+ c.globalCompositeOperation = 'lighter';
36
+ const sorted = LIGHTS.map(([x, y, z0, col]) => [x, y, ((z0 - t * 0.3) % 1 + 1) % 1 * 3.6 + 0.4, col]).sort((a, b) => b[2] - a[2]);
37
+ for (const [x, y, z, col] of sorted) {
38
+ const px = cx + x * F / z, py = cy + y * F / z, r = 3.2 / z + 0.6; if (px < 350 || px > 810 || py < 120 || py > 560) continue;
39
+ const fade = clamp((4 - z) / 1.0) * clamp((z - 0.4) / 0.25);
40
+ c.globalAlpha = fade * 0.5; const g = c.createRadialGradient(px, py, 0, px, py, r * 3.2); g.addColorStop(0, col); g.addColorStop(1, col + '00'); c.fillStyle = g; c.beginPath(); c.arc(px, py, r * 3.2, 0, Math.PI * 2); c.fill();
41
+ c.globalAlpha = fade; c.fillStyle = col; c.beginPath(); c.arc(px, py, r, 0, Math.PI * 2); c.fill();
42
+ }
43
+ c.restore();
44
+ // 窗框:黄底黑点
45
+ c.save(); c.fillStyle = C.yellow; c.fillRect(348, 118, 464, 22); c.fillRect(348, 540, 464, 24); c.fillRect(348, 118, 22, 446); c.fillRect(790, 118, 22, 446);
46
+ c.fillStyle = C.black; for (let x = 360; x < 806; x += 22) { dot(c, x, 129, 6 * wave(x, 129, t), 0, 1, C.black); dot(c, x, 552, 6 * wave(x, 552, t), 0, 1, C.black); } for (let y = 150; y < 540; y += 22) { dot(c, 359, y, 6 * wave(359, y, t), 0, 1, C.black); dot(c, 801, y, 6 * wave(801, y, t), 0, 1, C.black); }
47
+ c.strokeStyle = C.black; c.lineWidth = 3; c.strokeRect(348, 118, 464, 446); c.restore();
48
+ }
49
+ // 黄南瓜:5 瓣,瓣间黑线,每瓣按行排黑点(中间大、两边小),整体呼吸
50
+ function pumpkin(c, t) {
51
+ const cx = 960, cy = 540, br = 1 + Math.sin(t * 5) * 0.025;
52
+ c.save(); c.translate(cx, cy + 78); c.scale(br, 2 - br); c.translate(-cx, -(cy + 78));
53
+ const lobes = [[-88, 46, 70], [-44, 58, 74], [0, 62, 80], [44, 58, 74], [88, 46, 70]];
54
+ [0, 4, 1, 3, 2].forEach(k => { const [dx, rx, ry] = lobes[k]; const p = new Path2D(); p.ellipse(cx + dx, cy + 8, rx, ry, 0, 0, Math.PI * 2);
55
+ c.fillStyle = C.yellow; c.fill(p); c.strokeStyle = C.black; c.lineWidth = 4; c.stroke(p);
56
+ c.save(); c.clip(p);
57
+ for (let row = -4; row <= 4; row++) { const y = cy + 8 + row * 17; const wRow = Math.sqrt(Math.max(0, 1 - Math.pow(row * 17 / ry, 2))) * rx; for (let col = -3; col <= 3; col++) { const x = cx + dx + col * wRow * 0.3; const edge = 1 - Math.abs(col) / 4; const r = (3 + 6.5 * edge) * (1 - Math.abs(row) / 6) * wave(x, y, t, 1.5); if (r > 0.8) dot(c, x, y, r, 0, 1, C.black); } }
58
+ c.restore(); });
59
+ c.fillStyle = '#2a3a14'; c.beginPath(); c.moveTo(cx - 10, cy - 50); c.quadraticCurveTo(cx - 14, cy - 86, cx + 6, cy - 92); c.lineTo(cx + 14, cy - 84); c.quadraticCurveTo(cx + 4, cy - 70, cx + 10, cy - 50); c.closePath(); c.fill(); c.strokeStyle = C.black; c.lineWidth = 3; c.stroke();
60
+ c.restore();
61
+ }
62
+ function table(c, t) {
63
+ c.fillStyle = C.yellow; c.strokeStyle = C.black; c.lineWidth = 4;
64
+ [[806, 616, 408, 34], [834, 650, 352, 46], [842, 696, 26, 210], [1152, 696, 26, 210]].forEach(([x, y, w, h]) => { c.fillRect(x, y, w, h); c.strokeRect(x, y, w, h);
65
+ c.save(); c.beginPath(); c.rect(x, y, w, h); c.clip(); for (let yy = y + 8; yy < y + h; yy += 18) for (let xx = x + 8 + ((yy / 18) | 0) % 2 * 9; xx < x + w; xx += 18) dot(c, xx, yy, 4.2 * wave(xx, yy, t), 0, 1, C.black); c.restore(); });
66
+ }
67
+ // 角色:平涂(RIG fill)+各区域点+细黑线
68
+ function chars(c, t, lt, ch) {
69
+ const L = P.scratch('ku_chars'), g = L.getContext('2d'); g.reset();
70
+ const K = RIG.cat({ tail: ch.tail, blink: ch.blink, breathe: ch.breathe });
71
+ const G = RIG.girl({ cup: ch.cup, sip: ch.sip, breathe: ch.breathe, hair: 'bun' });
72
+ const gp = { skin: C.skin, hair: '#f2c84a', hairLine: '#c8962a', dress: C.red, line: C.black, lw: 3, cheek: 'rgba(240,110,110,.4)', lip: '#d8181e', iris: '#2a2a2a', cuff: C.white, apron: null, cupBody: C.yellow, cupRim: '#6a3a14', shoe: C.black };
73
+ RIG.drawCat(g, K, { orange: '#ec8a2a', white: C.white, stripe: '#b85a14', line: C.black, lw: 3, eye: '#2a2a2a' });
74
+ // 猫:橘色区白点、白色区红点
75
+ const catDots = (path, col, sp, r0, dy) => { g.save(); g.clip(path); for (let y = 540 + dy, j = 0; y < 920; y += sp * 0.87, j++) for (let x = 380 + (j % 2) * sp / 2; x < 720; x += sp) dot(g, x, y, r0 * wave(x, y, t) * (1 + OBL * 2), 0, 1, col); g.restore(); };
76
+ catDots(K.body, C.white, 34, 7, (t * 30) % (34 * 0.87 * 2)); catDots(K.head, C.white, 26, 5, 0);
77
+ RIG.drawCat(g, K, { line: C.black, lw: 3, mode: 'line', eye: '#2a2a2a' });
78
+ // 少女
79
+ RIG.drawGirl(g, G, { ...gp, mode: 'fill' });
80
+ // 红裙白点:顺着裙子往下爬(点的 y 随时间平移,爬出下摆再从腰上出来)
81
+ const dress = new Path2D(); [G.skirt, G.torso, G.farSleeve].forEach(p => dress.addPath(p));
82
+ g.save(); g.clip(dress); const off = (t * 46) % (52 * 0.87 * 2); // 周期必须是「两行」的行距(奇偶行错位),取 52 会在回绕时整片跳一帧
83
+ for (let y = 380 - 52, j = 0; y < 960; y += 52 * 0.87, j++) for (let x = 1080 + (j % 2) * 26; x < 1480; x += 52) dot(g, x, y + off, 13 * wave(x, y, t) * (1 + OBL * 2.2), 0.2, 0.92, C.white);
84
+ g.restore();
85
+ const arm = new Path2D(); arm.addPath(G.upperArm); arm.addPath(G.foreArm);
86
+ g.fillStyle = C.red; g.fill(arm);
87
+ g.save(); g.clip(arm); for (let y = 300, j = 0; y < 700; y += 30, j++) for (let x = 1130 + (j % 2) * 15; x < 1420; x += 30) dot(g, x, y + off * 0.6, 8 * wave(x, y, t), 0, 1, C.white); g.restore();
88
+ RIG.drawGirl(g, G, { ...gp, mode: 'line', lw: 4.5 });
89
+ KIT.nearArm(g, G, { fill: C.red, line: C.black, lw: 4, skin: C.skin, cuff: C.white, cupBody: C.yellow, cupRim: '#6a3a14', pattern: gg => { for (let y = 300, j = 0; y < 700; y += 30, j++) for (let x = 1130 + (j % 2) * 15; x < 1420; x += 30) dot(gg, x, y + (off * 0.6) % 60, 8 * wave(x, y, t), 0, 1, C.white); } });
90
+ // 脸颊上画了两个点(草间的身体彩绘)
91
+ dot(g, G.cheek[0] - 4, G.cheek[1] + 2, 5 * wave(G.cheek[0], G.cheek[1], t), 0, 1, C.red);
92
+ // 发箍:红底白点蝴蝶结
93
+ const ht = G.A.headTop; g.fillStyle = C.red; g.beginPath(); g.ellipse(ht[0] + 34, ht[1] + 6, 24, 13, 0.4, 0, Math.PI * 2); g.ellipse(ht[0] + 70, ht[1] + 26, 22, 12, 0.9, 0, Math.PI * 2); g.fill(); g.strokeStyle = C.black; g.lineWidth = 2.5; g.stroke();
94
+ dot(g, ht[0] + 34, ht[1] + 6, 5, 0, 1, C.white); dot(g, ht[0] + 70, ht[1] + 26, 5, 0, 1, C.white);
95
+ c.drawImage(L, 0, 0);
96
+ return G;
97
+ }
98
+
99
+ return {
100
+ draw(c, lt, t) {
101
+ const ch = KIT.choreo(lt, t);
102
+ OBL = Math.pow(ss(1.0, 1.26, lt), 1.4); // 1.26 后停住 0.15s,给结尾一个落点
103
+ room(c, t);
104
+ mirrorRoom(c, t);
105
+ table(c, t);
106
+ pumpkin(c, t);
107
+ const G = chars(c, t, lt, ch);
108
+ // 热气:一串由小到大的白点往上飘(热气也是点)
109
+ for (let i = 0; i < 7; i++) { const q = ((t * 1.4 + i / 7) % 1); dot(c, G.cup.x - 4 - q * 50 * ch.cup + Math.sin(q * 6 + t * 3) * 10, G.cup.y - 10 - q * 90, 3 + q * 7, 0, 1, `rgba(251,246,238,${1 - q})`); }
110
+ // 自我消融:最后 0.3s,全屏红底白点从猫的位置一圈圈胀开,把人、猫、南瓜都「消」进波点里(停在点阵,不是白屏)
111
+ if (OBL > 0) { const sp = 120; c.save(); c.globalAlpha = clamp(OBL * 1.4); c.fillStyle = C.red; c.fillRect(0, 0, W, H); c.globalAlpha = 1;
112
+ for (let y = 0, j = 0; y < H + sp; y += sp * 0.87, j++) for (let x = (j % 2) * sp / 2; x < W + sp; x += sp) { const d = Math.hypot(x - O[0], y - O[1]); const r = clamp(OBL * 1.5 - d / 2600) * 0.47 * sp * wave(x, y, t); if (r > 0.5) dot(c, x, y, r, 0, 1, C.white); }
113
+ c.restore(); }
114
+ },
115
+ label(c, lt, t) {
116
+ c.save(); c.fillStyle = C.yellow; c.strokeStyle = C.black; c.lineWidth = 5;
117
+ c.beginPath(); c.roundRect(1488, 44, 392, 160, 80); c.fill(); c.stroke();
118
+ c.beginPath(); c.roundRect(1488, 44, 392, 160, 80); c.clip();
119
+ [[1520, 76, 12], [1530, 172, 9], [1850, 80, 10], [1858, 170, 13], [1690, 60, 6], [1612, 192, 7], [1780, 196, 6]].forEach(([x, y, r]) => dot(c, x, y, r * wave(x, y, t, 1.4), 0, 1, C.black));
120
+ c.restore();
121
+ KIT.label(c, { title: '无限波点', sub: 'YAYOI KUSAMA · 1965–', tFont: '74px "LXGWWenKai-500"', sFont: '26px "Poppins-800"', tCol: C.black, sCol: C.red, x: 1838, y: 128, sy: 172, spacing: 6 });
122
+ },
123
+ };
124
+ })();