@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,273 @@
1
+ // 中国水墨写意(八大山人/齐白石)——纯代码。
2
+ // 管线:①宣纸(缓存)→ ②淡墨远山+朱日(晕染层)→ ③窗/桌:飞白笔触 → ④竹枝(浓墨个字叶,随风摆)
3
+ // → ⑤挂轴里的齐白石虾(游动)→ ⑥猫:赭石没骨晕染+留白+焦墨点斑,八大式白眼
4
+ // → ⑦少女:花青淡彩汉服+藤黄金发+白描面部 → ⑧热气、钤印
5
+ // 「画出来」开场:lt 0→0.45 各笔按序写出、各晕染层从无到有洇开;之后墨晕继续缓慢外扩(墨在纸上晕开)。
6
+ SCENES['17_ink'] = (() => {
7
+ const W = 1920, H = 1080, { clamp, lerp, rng, ease } = U, P = PAINT;
8
+ const { ss } = U;
9
+
10
+ // ---------- 宣纸 ----------
11
+ const paper = () => P.cached('ink_paper', W, H, (g) => {
12
+ g.drawImage(P.texture('xuan', '#efe6d0', { scale: 0.0025, amt: 9, grain: 7, seed: 5 }), 0, 0);
13
+ const r = rng(41);
14
+ g.lineCap = 'round';
15
+ for (let i = 0; i < 2600; i++) { // 纸纤维:短弧,亮暗混合
16
+ const x = r() * W, y = r() * H, a = r() * Math.PI * 2, l = 6 + r() * 26;
17
+ g.strokeStyle = r() < 0.5 ? `rgba(150,125,90,${0.05 + r() * 0.08})` : `rgba(255,252,240,${0.12 + r() * 0.15})`;
18
+ g.lineWidth = 0.6 + r() * 0.8; g.beginPath(); g.moveTo(x, y); g.quadraticCurveTo(x + Math.cos(a + 0.6) * l * 0.5, y + Math.sin(a + 0.6) * l * 0.5, x + Math.cos(a) * l, y + Math.sin(a) * l); g.stroke();
19
+ }
20
+ const vg = g.createRadialGradient(W / 2, H / 2, 500, W / 2, H / 2, 1250); vg.addColorStop(0, 'rgba(0,0,0,0)'); vg.addColorStop(1, 'rgba(120,90,50,.22)');
21
+ g.fillStyle = vg; g.fillRect(0, 0, W, H);
22
+ });
23
+
24
+ // 笔、叶、晕染都用库:P.brush(毛笔飞白)、P.leaf(两头尖单笔)、P.inkWash(墨晕洇开)
25
+ const brush = P.brush, leaf = P.leaf;
26
+ const washLayer = (c, key, grow, fn, o) => P.inkWash(c, 'ink_' + key, grow, fn, o);
27
+
28
+ // ---------- 静态笔触表(窗、桌、地) ----------
29
+ const WIN = [
30
+ { p: [[338, 116], [520, 112], [700, 118], [824, 114]], w: 16, d: 0.06, l: 0.18, dry: 0.55 },
31
+ { p: [[352, 104], [348, 300], [356, 470], [350, 578]], w: 15, d: 0.1, l: 0.16, dry: 0.5 },
32
+ { p: [[808, 108], [812, 330], [806, 572]], w: 14, d: 0.14, l: 0.16, dry: 0.6 },
33
+ { p: [[318, 572], [560, 566], [842, 574]], w: 18, d: 0.18, l: 0.16, dry: 0.45 },
34
+ { p: [[372, 140], [420, 140], [420, 186]], w: 4, d: 0.22, l: 0.08, dry: 0.3 }, { p: [[788, 140], [740, 140], [740, 186]], w: 4, d: 0.24, l: 0.08, dry: 0.3 },
35
+ { p: [[372, 546], [420, 546], [420, 500]], w: 4, d: 0.26, l: 0.08, dry: 0.3 }, { p: [[788, 546], [740, 546], [740, 500]], w: 4, d: 0.28, l: 0.08, dry: 0.3 },
36
+ ];
37
+ const TABLE = [
38
+ { p: [[800, 622], [1000, 614], [1212, 620]], w: 22, d: 0.12, l: 0.15, dry: 0.5 },
39
+ { p: [[820, 640], [1000, 646], [1196, 640]], w: 7, d: 0.2, l: 0.1, dry: 0.4 },
40
+ { p: [[842, 640], [846, 760], [838, 906]], w: 13, d: 0.18, l: 0.16, dry: 0.65 },
41
+ { p: [[1176, 640], [1172, 780], [1182, 906]], w: 13, d: 0.22, l: 0.16, dry: 0.65 },
42
+ { p: [[848, 664], [900, 690], [960, 676]], w: 5, d: 0.26, l: 0.1, dry: 0.4 }, { p: [[1170, 664], [1118, 690], [1058, 676]], w: 5, d: 0.28, l: 0.1, dry: 0.4 },
43
+ { p: [[300, 912], [700, 904], [1100, 914], [1520, 908]], w: 5, d: 0.3, l: 0.15, dry: 0.85, tone: 0.35 },
44
+ ];
45
+ const SKIRT = [
46
+ { p: [[1244, 630], [1190, 656], [1150, 722], [1118, 820], [1104, 926]], w: 6, dry: 0.3 },
47
+ { p: [[1420, 612], [1446, 650], [1452, 722], [1440, 762], [1366, 774], [1344, 806], [1352, 926]], w: 6, dry: 0.4 },
48
+ { p: [[1104, 928], [1220, 934], [1352, 928]], w: 5, dry: 0.6 },
49
+ { p: [[1232, 668], [1222, 760], [1226, 815]], w: 4, dry: 0.3 }, { p: [[1268, 668], [1262, 760], [1270, 840]], w: 4, dry: 0.3 },
50
+ { p: [[1160, 770], [1150, 860], [1146, 920]], w: 4, dry: 0.4 }, { p: [[1302, 830], [1308, 880], [1300, 925]], w: 4, dry: 0.4 },
51
+ { p: [[1350, 434], [1382, 448], [1406, 486], [1418, 546], [1424, 604]], w: 6, dry: 0.35 },
52
+ ];
53
+
54
+ // ---------- 远山、朱日 ----------
55
+ function mountains(g) {
56
+ g.save(); g.beginPath(); g.rect(366, 132, 428, 428); g.clip();
57
+ const R1 = rng(3);
58
+ const ridge = (y0, amp, f, seed) => { const pts = []; for (let x = 340; x <= 830; x += 10) pts.push([x, y0 - amp * (0.5 + 0.5 * P.fbm(x * f + seed, seed, 3)) - amp * 0.6 * Math.exp(-Math.pow((x - 560 - seed * 40) / 120, 2))]); return pts; };
59
+ const fill = (pts, a, y1) => { const gr = g.createLinearGradient(0, Math.min(...pts.map(p => p[1])), 0, y1); gr.addColorStop(0, `rgba(40,40,44,${a})`); gr.addColorStop(1, 'rgba(40,40,44,0)'); g.fillStyle = gr; g.beginPath(); pts.forEach((p, i) => i ? g.lineTo(...p) : g.moveTo(...p)); g.lineTo(830, y1); g.lineTo(340, y1); g.closePath(); g.fill(); };
60
+ fill(ridge(380, 120, 0.006, 1), 0.32, 520); fill(ridge(450, 90, 0.009, 2.4), 0.5, 560);
61
+ g.restore();
62
+ }
63
+
64
+ // ---------- 竹(窗前,从右上斜入) ----------
65
+ function bamboo(c, t, rev) {
66
+ const sw = Math.sin(t * 2.9) * 0.11 + Math.sin(t * 6.1 + 1) * 0.025;
67
+ const base = [835, 70];
68
+ const rot = (x, y, k) => { const a = sw * k, dx = x - base[0], dy = y - base[1]; return [base[0] + dx * Math.cos(a) - dy * Math.sin(a), base[1] + dx * Math.sin(a) + dy * Math.cos(a)]; };
69
+ // 主枝:分节(每节一笔,节间留白),淡墨
70
+ const nodes = [[835, 70], [760, 150], [690, 215], [620, 268], [550, 312]];
71
+ for (let i = 0; i < nodes.length - 1; i++) {
72
+ const a = rot(...nodes[i], i / 4), b = rot(...nodes[i + 1], (i + 1) / 4);
73
+ const q0 = 0.04, A = [lerp(a[0], b[0], q0), lerp(a[1], b[1], q0)], B = [lerp(a[0], b[0], 0.94), lerp(a[1], b[1], 0.94)];
74
+ brush(c, [A, [lerp(A[0], B[0], .5), lerp(A[1], B[1], .5)], B], { w: 9, tone: 0.5, dry: 0.3, seed: 60 + i, reveal: ss(0.08 + i * 0.03, 0.18 + i * 0.03, rev), head: 0.06, tail: 0.1, bristles: 5 });
75
+ }
76
+ // 叶:个字、介字组,浓墨
77
+ const groups = [[2, -0.2, 0], [3, 0.4, 1], [4, -0.9, 2], [3, 2.2, 3], [4, 0.9, 4]];
78
+ groups.forEach(([ni, a0, gi]) => {
79
+ const np = nodes[Math.min(nodes.length - 1, ni)];
80
+ const p = rot(np[0], np[1], ni / 4); const flut = Math.sin(t * 7 + gi * 1.3) * 0.2;
81
+ const rv = ss(0.2 + gi * 0.04, 0.3 + gi * 0.04, rev); if (rv <= 0) return;
82
+ [[0, 1], [0.45, 0.82], [-0.42, 0.78]].forEach(([da, ls], k) => leaf(c, p[0], p[1], a0 + 1.4 + da + flut * (1 + k * 0.4) + sw * 2, 110 * ls * rv, 15, 0.86));
83
+ });
84
+ }
85
+
86
+ // ---------- 挂轴+齐白石虾 ----------
87
+ function scrollStatic(g) {
88
+ g.fillStyle = 'rgba(170,140,95,.32)'; g.fillRect(1560, 240, 230, 640); // 绫边
89
+ g.fillStyle = 'rgba(250,246,234,.9)'; g.fillRect(1580, 290, 190, 540);
90
+ g.fillStyle = 'rgba(60,40,25,.85)'; g.fillRect(1548, 870, 254, 16); g.fillRect(1556, 232, 238, 10);
91
+ g.strokeStyle = 'rgba(60,40,25,.6)'; g.lineWidth = 1.5; g.beginPath(); g.moveTo(1640, 232); g.lineTo(1675, 196); g.lineTo(1710, 232); g.stroke();
92
+ g.fillStyle = '#c23a2b'; g.fillRect(1742, 788, 16, 16);
93
+ }
94
+ function shrimp(c, x, y, ang, s, t, ph) {
95
+ c.save(); c.translate(x, y); c.rotate(ang); c.scale(s, s);
96
+ const flex = Math.sin(t * 7 + ph) * 0.12;
97
+ // 身体:6 节淡墨弧,从头到尾弯曲
98
+ let px = 0, py = 0, a = 0; const segs = [];
99
+ for (let i = 0; i < 6; i++) { a += 0.2 + flex; const l = 13 - i * 1.2; px += Math.cos(a) * l; py += Math.sin(a) * l; segs.push([px, py, a, 11 - i * 1.3]); }
100
+ segs.forEach(([sx, sy, sa, sw], i) => { c.fillStyle = `rgba(30,28,26,${0.22 + i * 0.03})`; c.beginPath(); c.ellipse(sx, sy, 9, sw * 1.05, sa + 0.25, 0, Math.PI * 2); c.fill(); c.strokeStyle = 'rgba(20,18,16,.3)'; c.lineWidth = 2.4; c.beginPath(); c.ellipse(sx, sy, 9, sw * 1.05, sa + 0.25, -1.2, 1.2); c.stroke(); });
101
+ const tl = segs[5]; [-0.5, 0, 0.5].forEach(d => leaf(c, tl[0], tl[1], tl[2] + d + 0.4, 15, 6, 0.4));
102
+ // 头胸甲:淡墨+中间一笔焦墨
103
+ c.fillStyle = 'rgba(30,28,26,.33)'; c.beginPath(); c.ellipse(-10, -2, 17, 10, -0.15, 0, Math.PI * 2); c.fill();
104
+ c.fillStyle = 'rgba(10,10,10,.9)'; c.beginPath(); c.ellipse(-8, -3, 7, 3.2, -0.2, 0, Math.PI * 2); c.fill();
105
+ c.beginPath(); c.arc(-24, -8, 2.6, 0, Math.PI * 2); c.arc(-23, -1, 2.6, 0, Math.PI * 2); c.fill();
106
+ // 须:两长两短,波动
107
+ c.strokeStyle = 'rgba(10,10,10,.8)'; c.lineWidth = 1.1;
108
+ [[-1, 120, 0.9], [1, 105, 1.4], [-1, 40, 2.1], [1, 36, 2.8]].forEach(([sg, L, q]) => { c.beginPath(); c.moveTo(-26, -4); for (let i = 1; i <= 12; i++) { const u = i / 12; c.lineTo(-26 - u * L, -4 + sg * u * L * 0.18 + Math.sin(t * 5 + q + u * 4) * 6 * u); } c.stroke(); });
109
+ // 钳:长臂三节+钳
110
+ [[-0.35, 0], [0.1, 1]].forEach(([da, k]) => { c.strokeStyle = 'rgba(10,10,10,.75)'; c.lineWidth = 1.6; const sw2 = Math.sin(t * 4 + k * 2 + ph) * 0.15; let qx = -20, qy = 4; c.beginPath(); c.moveTo(qx, qy);
111
+ [[22, 2.4], [20, 2.6], [16, 2.9]].forEach(([l, aa]) => { qx += Math.cos(aa + da + sw2) * l; qy += Math.sin(aa + da + sw2) * l; c.lineTo(qx, qy); }); c.stroke();
112
+ c.fillStyle = 'rgba(10,10,10,.82)'; c.beginPath(); c.ellipse(qx - 6, qy + 2, 8, 3, 2.9 + da, 0, Math.PI * 2); c.fill(); });
113
+ // 腿
114
+ c.strokeStyle = 'rgba(20,20,20,.5)'; c.lineWidth = 1; for (let i = 0; i < 5; i++) { const sg = segs[Math.min(4, i)]; c.beginPath(); c.moveTo(sg[0] - 2, sg[1] + 5); c.lineTo(sg[0] - 6 + Math.sin(t * 12 + i) * 2, sg[1] + 14); c.stroke(); }
115
+ c.restore();
116
+ }
117
+
118
+ // ---------- 猫(赭石没骨+留白+焦墨点斑+八大白眼) ----------
119
+ function catWash(g, K) {
120
+ g.fillStyle = '#c47a3c'; g.fill(K.body); g.fill(K.head);
121
+ g.lineCap = 'round'; g.lineJoin = 'round'; g.strokeStyle = '#c47a3c'; g.lineWidth = K.tailW; g.stroke(K.tail);
122
+ g.save(); g.globalCompositeOperation = 'destination-out'; g.fill(K.white); K.legs.forEach(l => g.fill(l));
123
+ g.beginPath(); g.ellipse(K.nose[0] - 22, K.nose[1] + 12, 36, 26, 0, 0, Math.PI * 2); g.fill(); g.restore();
124
+ }
125
+ function catInk(c, K, t, rev) {
126
+ const R_ = (pts, o) => brush(c, pts, { ...o, reveal: ss(o.d || 0, (o.d || 0) + 0.18, rev) });
127
+ // 背线、头顶:焦墨几笔
128
+ const b = K.bodyPts;
129
+ R_([b[5], b[4], b[3], b[2]], { w: 12, dry: 0.55, seed: 81, d: 0.12, tone: 0.9 });
130
+ R_([b[2], b[1], b[0]], { w: 8, dry: 0.7, seed: 82, d: 0.18, tone: 0.75 });
131
+ R_([b[6], b[7], b[8]], { w: 5, dry: 0.6, seed: 83, d: 0.2, tone: 0.55 });
132
+ K.stripes.forEach((s, i) => R_([s[0], [(s[0][0] + s[1][0]) / 2 + 3, (s[0][1] + s[1][1]) / 2], s[1]], { w: i < 3 ? 9 : 13, dry: 0.4, seed: 90 + i, d: 0.2 + i * 0.015, tone: 0.92, head: 0.2, tail: 0.6, bristles: 5 }));
133
+ if (rev < 0.25) return;
134
+ c.save(); c.globalAlpha = ss(0.25, 0.4, rev);
135
+ // 耳:两笔焦墨三角
136
+ c.fillStyle = 'rgba(14,12,10,.9)'; K.earInner.forEach(e => { c.beginPath(); c.moveTo(...e[0]); c.lineTo(...e[1]); c.lineTo(...e[2]); c.closePath(); c.fill(); });
137
+ // 头轮廓:一笔淡墨
138
+ c.strokeStyle = 'rgba(20,18,16,.55)'; c.lineWidth = 3; c.stroke(K.head);
139
+ // 八大式白眼:圆眶,瞳点顶到上方(白眼向天)
140
+ K.eyes.forEach((e, i) => { c.strokeStyle = 'rgba(10,10,10,.92)'; c.lineWidth = 2.6; c.beginPath(); c.ellipse(e.x, e.y, K.eyeR * 1.15, K.eyeR * (K.blink ? 0.15 : 1), 0, 0, Math.PI * 2); c.stroke();
141
+ if (!K.blink) { c.fillStyle = 'rgba(8,8,8,.95)'; c.beginPath(); c.arc(e.x + 2.5, e.y - K.eyeR * 0.55 + Math.sin(t * 2) * 1.2, K.eyeR * 0.42, 0, Math.PI * 2); c.fill(); } });
142
+ c.fillStyle = 'rgba(14,12,10,.9)'; c.beginPath(); c.ellipse(K.nose[0], K.nose[1], 6, 4, 0, 0, Math.PI * 2); c.fill();
143
+ c.strokeStyle = 'rgba(14,12,10,.8)'; c.lineWidth = 2; c.beginPath(); c.moveTo(...K.mouth[0]); c.quadraticCurveTo(...K.mouth[1], ...K.mouth[2]); c.stroke();
144
+ K.whiskers.forEach((w, i) => brush(c, [w[0], [(w[0][0] + w[1][0]) / 2, (w[0][1] + w[1][1]) / 2 - 2], w[1]], { w: 2.4, dry: 0.5, bristles: 3, seed: 99 + i, tone: 0.8, head: 0.05, tail: 0.6 }));
145
+ // 腿、爪:淡墨轻勾
146
+ c.strokeStyle = 'rgba(20,18,16,.38)'; c.lineWidth = 2; K.legs.forEach(l => c.stroke(l));
147
+ // 尾:焦墨环纹
148
+ const tp = K.tailPts; [5, 9, 13, 17].forEach((i, k) => { const p = tp[i], q = tp[i + 1], a = Math.atan2(q[1] - p[1], q[0] - p[0]) + Math.PI / 2; brush(c, [[p[0] - Math.cos(a) * 12, p[1] - Math.sin(a) * 12], [p[0] + Math.cos(a) * 12, p[1] + Math.sin(a) * 12]], { w: 8, dry: 0.3, bristles: 4, seed: 120 + k, tone: 0.85 }); });
149
+ c.restore();
150
+ }
151
+
152
+ // ---------- 少女 ----------
153
+ function sleeveShape(G) {
154
+ const S = G.A.shoulder, E = G.A.elbow, Hd = G.A.hand;
155
+ const Wr = [lerp(E[0], Hd[0], 0.8), lerp(E[1], Hd[1], 0.8)];
156
+ const low = Math.max(S[1], E[1]) + 120;
157
+ return RIG.smooth([[S[0] + 22, S[1] - 18], [S[0] - 26, S[1] - 6], [E[0] - 22, E[1] - 6], [Wr[0] - 16, Wr[1] - 14], [Wr[0] + 14, Wr[1] + 18], [Wr[0] + 20, Math.max(Wr[1] + 40, low - 30)], [lerp(Wr[0], E[0], 0.5) + 30, low], [E[0] + 40, low - 10], [S[0] + 40, S[1] + 70]]);
158
+ }
159
+ function girlWash(g, G) {
160
+ g.fillStyle = '#6f8aa0'; [G.farSleeve, G.skirt, G.torso].forEach(p => g.fill(p));
161
+ g.save(); g.globalCompositeOperation = 'destination-out'; g.fill(G.bib); g.restore(); // 交领内衫留白
162
+ g.fillStyle = '#7d97ab'; g.fill(sleeveShape(G)); // 广袖与衣身同一层晕染(没骨),只靠线分开
163
+ }
164
+ function girlInk(c, G, t, rev, ch) {
165
+ const A = G.A;
166
+ SKIRT.forEach((s, i) => brush(c, s.p, { w: s.w, dry: s.dry, seed: 200 + i, tone: 0.78, reveal: ss(0.05 + i * 0.02, 0.25 + i * 0.02, rev), head: 0.08, tail: 0.5, bristles: 5 }));
167
+ // 腰带:胭脂红,两条垂带随动作摆(齐白石的一点红)
168
+ const wx = A.waist[0] - 40, wy = A.waist[1] - 8, sw = Math.sin(t * 3.4) * 10;
169
+ brush(c, [[1300, wy - 4], [1360, wy], [1420, wy - 2]], { w: 14, dry: 0.25, seed: 230, col: [190, 40, 40], tone: 0.85, reveal: ss(0.25, 0.4, rev) });
170
+ [[0, 0], [16, 1]].forEach(([o, k]) => brush(c, [[1400 + o, wy + 4], [1408 + o + sw * 0.4, wy + 70], [1404 + o + sw, wy + 150]], { w: 7, dry: 0.4, seed: 240 + k, col: [190, 40, 40], tone: 0.8, reveal: ss(0.3, 0.45, rev), tail: 0.6, bristles: 4 }));
171
+ if (rev < 0.12) return;
172
+ const al = ss(0.12, 0.32, rev);
173
+ c.save(); c.globalAlpha = al;
174
+ // 头发:藤黄晕+几笔淡墨发丝
175
+ c.globalCompositeOperation = 'multiply';
176
+ c.fillStyle = 'rgba(226,170,52,.75)'; [G.hairBack, G.bun, G.bangs].forEach(p => p && c.fill(p));
177
+ c.globalCompositeOperation = 'source-over';
178
+ c.strokeStyle = 'rgba(70,50,20,.55)'; c.lineWidth = 1.6; G.hairLines.forEach(h => c.stroke(h));
179
+ c.lineWidth = 1.4; c.strokeStyle = 'rgba(30,24,16,.7)'; c.stroke(G.hairBack); if (G.bun) c.stroke(G.bun);
180
+ // 发簪
181
+ const bc = G.bunSpiral; c.strokeStyle = 'rgba(14,12,10,.9)'; c.lineWidth = 3.5; c.beginPath(); c.moveTo(bc[0] - 36, bc[1] - 26); c.lineTo(bc[0] + 30, bc[1] + 16); c.stroke();
182
+ c.fillStyle = '#c0392b'; c.beginPath(); c.arc(bc[0] - 38, bc[1] - 28, 6, 0, Math.PI * 2); c.fill();
183
+ // 白描面部:细线勾脸,纸色留白
184
+ c.fillStyle = 'rgba(250,240,224,.9)'; c.fill(G.face); c.fillStyle = 'rgba(246,232,212,.85)'; c.fill(G.neck); c.strokeStyle = 'rgba(18,16,14,.6)'; c.lineWidth = 1.4; c.stroke(G.neck);
185
+ c.strokeStyle = 'rgba(18,16,14,.85)'; c.lineWidth = 1.8; c.stroke(G.face);
186
+ c.save(); c.clip(G.face); c.fillStyle = 'rgba(214,92,92,.16)'; c.filter = 'blur(5px)'; c.beginPath(); c.arc(G.cheek[0] - 4, G.cheek[1] + 2, 10, 0, Math.PI * 2); c.fill(); c.filter = 'none'; c.restore();
187
+ c.fillStyle = 'rgba(226,170,52,.75)'; c.fill(G.bangs); c.strokeStyle = 'rgba(30,24,16,.6)'; c.lineWidth = 1.3; c.stroke(G.bangs);
188
+ c.strokeStyle = 'rgba(12,10,8,.95)'; c.lineWidth = ch.blink ? 2 : 2.4; c.stroke(G.lid);
189
+ if (!ch.blink) { c.fillStyle = 'rgba(12,10,8,.95)'; c.beginPath(); c.ellipse(G.eye.x - 1, G.eye.y + 1.5, 3, 3.6, 0, 0, Math.PI * 2); c.fill(); }
190
+ c.lineWidth = 1.5; c.stroke(G.brow);
191
+ c.fillStyle = '#b8322c'; c.beginPath(); c.arc(G.lips[0][0] + 3, G.lips[0][1] + 3, 3.2, 0, Math.PI * 2); c.fill();
192
+ c.restore();
193
+ }
194
+ function girlArm(c, G, rev) {
195
+ const al = ss(0.12, 0.32, rev); if (al <= 0) return;
196
+ c.save(); c.globalAlpha = al;
197
+ // 广袖:花青淡彩一大片,垂坠
198
+ const sl = sleeveShape(G);
199
+ c.strokeStyle = 'rgba(18,16,14,.7)'; c.lineWidth = 2.2; c.stroke(sl);
200
+ // 手:白描
201
+ c.fillStyle = 'rgba(250,240,224,.95)'; c.fill(G.hand); c.strokeStyle = 'rgba(18,16,14,.8)'; c.lineWidth = 1.6; c.stroke(G.hand);
202
+ // 杯:白瓷,一圈青花
203
+ const cp = G.cup; c.save(); c.translate(cp.x, cp.y); c.rotate(cp.tilt);
204
+ c.beginPath(); c.moveTo(-cp.w / 2, 0); c.lineTo(-cp.w * 0.36, cp.h * 0.9); c.lineTo(cp.w * 0.36, cp.h * 0.9); c.lineTo(cp.w / 2, 0); c.closePath();
205
+ c.fillStyle = 'rgba(252,250,244,.96)'; c.fill(); c.strokeStyle = 'rgba(18,16,14,.85)'; c.lineWidth = 1.8; c.stroke();
206
+ c.strokeStyle = 'rgba(40,70,140,.75)'; c.lineWidth = 4; c.beginPath(); c.moveTo(-cp.w * 0.45, cp.h * 0.28); c.lineTo(cp.w * 0.45, cp.h * 0.28); c.stroke();
207
+ c.restore();
208
+ c.fillStyle = 'rgba(250,240,224,.95)'; c.beginPath(); c.arc(G.thumb[0], G.thumb[1], 6.5, 0, Math.PI * 2); c.fill(); c.strokeStyle = 'rgba(18,16,14,.8)'; c.lineWidth = 1.4; c.stroke();
209
+ c.restore();
210
+ }
211
+
212
+ return {
213
+ draw(c, lt, t) {
214
+ lt += 0.12; // 开场不从一张白纸开始:第 0 帧已经落下前几笔
215
+ const ch = KIT.choreo(lt, t);
216
+ const rev = clamp(lt / 0.45); // 开场「画出来」
217
+ const grow = ss(0, 1.15, lt); // 墨晕外扩
218
+ c.drawImage(paper(), 0, 0);
219
+ // 晕染:远山+朱日
220
+ washLayer(c, 'mtn', ss(0, 0.3, lt) * (0.6 + 0.4 * grow), mountains, { blur: 3, halo: 14, haloA: 0.3 });
221
+ c.save(); c.globalCompositeOperation = 'multiply'; c.globalAlpha = ss(0.1, 0.3, lt); c.filter = `blur(${2 + grow * 3}px)`; c.fillStyle = '#d8553f'; c.beginPath(); c.arc(700, 230, 30 + grow * 3, 0, Math.PI * 2); c.fill(); c.filter = 'none'; c.restore();
222
+ // 雾带:纸色横向晕带缓缓左移,远山时隐时现
223
+ c.save(); c.beginPath(); c.rect(366, 132, 428, 428); c.clip(); c.filter = 'blur(14px)';
224
+ [[420, 46, 0], [500, 34, 1], [330, 28, 2], [460, 30, 3]].forEach(([y, h, k]) => { const x = 900 - ((lt * (150 + k * 40) + k * 230) % 900); c.fillStyle = `rgba(242,234,214,${0.72 * ss(0.15, 0.4, lt)})`; c.beginPath(); c.ellipse(x, y, 230, h, 0, 0, Math.PI * 2); c.fill(); });
225
+ c.filter = 'none'; c.restore();
226
+ // 一只八大式小鸟掠过窗外(两笔翅,每 0.12s 扇一次)
227
+ if (lt > 0.25) { const bx = 820 - (lt - 0.25) * 420, by = 470 - Math.sin(lt * 3) * 26, fl = Math.sin(lt * 52) ;
228
+ c.save(); c.beginPath(); c.rect(366, 132, 428, 428); c.clip(); c.fillStyle = 'rgba(14,12,10,.9)';
229
+ c.beginPath(); c.ellipse(bx, by, 11, 6, -0.2, 0, Math.PI * 2); c.fill(); c.beginPath(); c.arc(bx - 11, by - 4, 5, 0, Math.PI * 2); c.fill();
230
+ leaf(c, bx - 2, by - 2, -1.2 - fl * 0.7, 28, 9, 0.9); leaf(c, bx + 4, by - 1, -0.5 - fl * 0.5, 24, 8, 0.75);
231
+ c.fillStyle = '#f2ead6'; c.beginPath(); c.arc(bx - 13, by - 5, 2, 0, Math.PI * 2); c.fill(); c.restore(); }
232
+ // 静态笔触(写完就缓存)
233
+ const stat = (c2, rv) => { WIN.forEach((s, i) => brush(c2, s.p, { w: s.w, dry: s.dry, seed: 10 + i, reveal: ss(s.d, s.d + s.l, rv), tone: s.tone || 0.88 })); TABLE.forEach((s, i) => brush(c2, s.p, { w: s.w, dry: s.dry, seed: 30 + i, reveal: ss(s.d, s.d + s.l, rv), tone: s.tone || 0.88 })); };
234
+ if (rev < 1) stat(c, rev); else c.drawImage(P.cached('ink_static', W, H, g => stat(g, 1)), 0, 0);
235
+ bamboo(c, t, rev);
236
+ // 挂轴+虾(三只,沿各自弧线游)
237
+ c.save(); c.globalAlpha = ss(0.15, 0.35, lt); scrollStatic(c);
238
+ c.beginPath(); c.rect(1580, 290, 190, 540); c.clip();
239
+ [[1716, 450, 0.5, 0], [1700, 700, 1.4, 1]].forEach(([x, y, a0, k]) => { const u = lt * 1.6 + k * 2; shrimp(c, x - 26 * Math.sin(u * 1.5) - lt * 40, y + Math.sin(u * 2) * 16 - lt * 30, -0.35 + a0 * 0.1 + Math.sin(u * 1.5) * 0.18, 1.45, t, k * 2); });
240
+ c.restore();
241
+ // 猫:赭石晕染 → 焦墨
242
+ const K = RIG.cat({ tail: ch.tail, blink: ch.blink, breathe: ch.breathe });
243
+ c.save(); const ks = new Path2D(); ks.addPath(K.body); ks.addPath(K.head); c.clip(ks); c.drawImage(paper(), 0, 0); c.restore(); // 猫身下先补回纸色:窗台墨线不透过赭石
244
+ washLayer(c, 'cat', ss(0.02, 0.3, lt) * (0.7 + 0.3 * grow), g => catWash(g, K), { blur: 2.5, halo: 9, haloA: 0.28, alpha: 0.78 });
245
+ catInk(c, K, t, rev);
246
+ // 少女:花青淡彩 → 白描与衣纹
247
+ const G = RIG.girl({ cup: ch.cup, sip: ch.sip, breathe: ch.breathe, hair: 'bun' });
248
+ c.save(); const gs = new Path2D(); [G.skirt, G.torso, G.farSleeve].forEach(p => gs.addPath(p)); c.clip(gs); c.drawImage(paper(), 0, 0); c.restore(); // 桌腿、桌面不透过衣裙
249
+ washLayer(c, 'girl', ss(0.0, 0.3, lt) * (0.7 + 0.3 * grow), g => girlWash(g, G), { blur: 2.5, halo: 12, haloA: 0.24, alpha: 0.6 });
250
+ girlInk(c, G, t, rev, ch);
251
+ girlArm(c, G, rev);
252
+ // 热气:两缕淡墨游丝
253
+ if (lt > 0.3) { c.save(); c.globalAlpha = ss(0.3, 0.5, lt);
254
+ [0, 1].forEach(k => { const pts = []; for (let i = 0; i <= 8; i++) { const q = i / 8; pts.push([G.cup.x - 8 + k * 14 + Math.sin(t * 3.5 + q * 5 + k * 2) * 10 * q - q * 40 * ch.cup, G.cup.y - 8 - q * 80]); } brush(c, pts, { w: 3, dry: 0.7, bristles: 3, seed: 300 + k + P.boilSeed(t, 10), tone: 0.45, head: 0.05, tail: 0.8 }); });
255
+ c.restore(); }
256
+ },
257
+ label(c, lt) {
258
+ const a = ss(0.0, 0.25, lt);
259
+ KIT.label(c, { title: '水墨写意', sub: '八大山人 · 齐白石', tFont: '80px "LXGWWenKai-500"', sFont: '30px "LXGWWenKai-500"', tCol: `rgba(14,12,10,${0.92 * a})`, sCol: `rgba(40,34,28,${0.85 * a})`, spacing: 6 });
260
+ // 钤印:朱文方印「白石」,0.32s 盖下(先放大再压实,印泥略洇)
261
+ const q = ss(0.3, 0.4, lt); if (q <= 0) return;
262
+ const s = 1.5 - 0.5 * ease.out(q);
263
+ const seal = P.cached('ink_seal', 80, 80, g => { g.translate(40, 40);
264
+ g.fillStyle = '#c4281e'; g.fillRect(-34, -34, 68, 68);
265
+ g.globalCompositeOperation = 'destination-out'; g.font = '30px "LXGWWenKai-500"'; g.textAlign = 'center'; g.textBaseline = 'middle';
266
+ g.fillText('白', 0, -14); g.fillText('石', 0, 16);
267
+ const r = rng(7); for (let i = 0; i < 40; i++) g.fillRect(-34 + r() * 68, -34 + r() * 68, 1 + r() * 3, 1 + r() * 2); // 印面斑驳
268
+ });
269
+ c.save(); c.translate(1500, 98); c.scale(s, s); c.globalAlpha = Math.min(1, q * 1.6); c.globalCompositeOperation = 'multiply';
270
+ c.drawImage(seal, -40, -40); c.restore();
271
+ },
272
+ };
273
+ })();
@@ -0,0 +1,227 @@
1
+ // 克里姆特金色时期(《吻》《阿黛尔·布洛赫-鲍尔肖像 I》,1907)——纯代码。
2
+ // 管线:①金箔墙(逐像素:斑驳+拉丝+金粉,缓存)→ ②窗:《白桦林》点彩风景 + 黑白方格金框
3
+ // → ③花草地(《吻》崖边的花毯)→ ④桌:维也纳工坊黑白方格 → ⑤猫:橘色螺旋纹+白色小圆环(装饰化)
4
+ // → ⑥少女:金袍(眼形纹、螺旋、同心圆、黑白长方块,平面)+ 写实的脸与手(渐变体积)
5
+ // → ⑦会动的:金粉闪烁、斜向流光扫过、袍上眼睛轮流眨、螺旋转、花点头、窗里落叶
6
+ SCENES['18_klimt'] = (() => {
7
+ const W = 1920, H = 1080, { clamp, lerp, rng } = U, P = PAINT;
8
+ const { ss } = U;
9
+ const C = { goldLo: [120, 78, 18], gold: [196, 148, 52], goldHi: [246, 216, 128], ink: '#1d140c', skin: '#f2d6c2' };
10
+
11
+ // ---------- 金箔(逐像素) ----------
12
+ const goldTex = () => P.cached('kl_gold', W, H, (g) => {
13
+ const img = g.createImageData(W, H), d = img.data, r = rng(19);
14
+ for (let y = 0; y < H; y++) for (let x = 0; x < W; x++) {
15
+ const mott = P.fbm(x * 0.004 + 3, y * 0.004, 4), brush = P.noise(x * 0.003, y * 0.08) * 0.45, gr = (r() - 0.5) * 0.1;
16
+ let f = clamp(0.5 + mott * 0.55 + brush * 0.5 + gr);
17
+ const lo = C.goldLo, mi = C.gold, hi = C.goldHi; let col = f < 0.5 ? P.mix(lo, mi, f * 2) : P.mix(mi, hi, (f - 0.5) * 2);
18
+ const i = (y * W + x) * 4; d[i] = col[0]; d[i + 1] = col[1]; d[i + 2] = col[2]; d[i + 3] = 255;
19
+ }
20
+ g.putImageData(img, 0, 0);
21
+ // 金箔接缝:一片片贴上去的方块
22
+ g.strokeStyle = 'rgba(90,55,10,.18)'; g.lineWidth = 1; for (let y = 0; y < H; y += 96) for (let x = (y / 96 % 2) * 48; x < W; x += 96) g.strokeRect(x + 0.5, y + 0.5, 96, 96);
23
+ });
24
+ // 墙:金箔压暗成《吻》的褐金斑驳底,撒金粉
25
+ const wall = () => P.cached('kl_wall', W, H, (g) => {
26
+ g.drawImage(goldTex(), 0, 0);
27
+ g.globalCompositeOperation = 'multiply';
28
+ const img = P.cached('kl_mott', 480, 270, (h) => { const im = h.createImageData(480, 270), d = im.data; for (let y = 0; y < 270; y++) for (let x = 0; x < 480; x++) { const n = P.fbm(x * 0.012 + 9, y * 0.012, 4); const v = clamp(0.62 + n * 0.7); const i = (y * 480 + x) * 4; d[i] = 255 * v; d[i + 1] = 235 * v; d[i + 2] = 190 * v; d[i + 3] = 255; } h.putImageData(im, 0, 0); });
29
+ g.drawImage(img, 0, 0, W, H);
30
+ g.globalCompositeOperation = 'source-over';
31
+ const r = rng(23); for (let i = 0; i < 9000; i++) { const x = r() * W, y = r() * H, s = 0.6 + r() * 2.2; g.fillStyle = r() < 0.7 ? `rgba(250,226,140,${0.3 + r() * 0.5})` : `rgba(70,40,10,${0.2 + r() * 0.3})`; g.fillRect(x, y, s, s); }
32
+ });
33
+ const DUST = (() => { const r = rng(31), o = []; for (let i = 0; i < 420; i++) o.push([r() * W, r() * H, 1.5 + r() * 3.5, r() * 6.28, 3 + r() * 6]); return o; })();
34
+
35
+ // ---------- 窗:克里姆特《白桦林》 ----------
36
+ const windowArt = () => P.cached('kl_win', W, H, (g) => {
37
+ g.save(); g.beginPath(); g.rect(370, 140, 420, 400); g.clip();
38
+ g.fillStyle = '#5a3a1c'; g.fillRect(370, 140, 420, 400);
39
+ const r = rng(41);
40
+ for (let i = 0; i < 2600; i++) { const x = 370 + r() * 420, y = 140 + r() * 400; const deep = (y - 140) / 400;
41
+ const pal = deep < 0.65 ? ['#c86a2a', '#e09a3a', '#a84c22', '#d8b04a', '#7a8a3a', '#e8c070'] : ['#b8502a', '#d8803a', '#8a3a1a', '#e0a050', '#c86a7a'];
42
+ g.fillStyle = pal[(r() * pal.length) | 0]; g.beginPath(); g.ellipse(x, y, 3 + r() * 5, 2 + r() * 4, r() * 3, 0, Math.PI * 2); g.fill(); }
43
+ [[410, 10], [470, 14], [545, 9], [612, 16], [690, 11], [748, 13]].forEach(([x, w], k) => {
44
+ g.fillStyle = '#efe8da'; g.fillRect(x, 140, w, 400); g.fillStyle = '#2a2018';
45
+ for (let y = 150; y < 540; y += 14 + (k * 7 + y) % 19) g.fillRect(x + ((y * 3) % w) * 0.4, y, w * (0.3 + ((y * 7) % 10) / 20), 3 + (y % 4)); });
46
+ g.restore();
47
+ // 金框+黑白小方格(维也纳分离派)
48
+ g.fillStyle = '#d6a845'; g.fillRect(346, 116, 468, 24); g.fillRect(346, 540, 468, 26); g.fillRect(346, 116, 24, 450); g.fillRect(790, 116, 24, 450);
49
+ for (let x = 350; x < 812; x += 12) { g.fillStyle = ((x / 12) | 0) % 2 ? '#111' : '#f4efe2'; g.fillRect(x, 122, 10, 10); g.fillRect(x, 549, 10, 10); }
50
+ for (let y = 140; y < 540; y += 12) { g.fillStyle = ((y / 12) | 0) % 2 ? '#111' : '#f4efe2'; g.fillRect(352, y, 10, 10); g.fillRect(798, y, 10, 10); }
51
+ g.strokeStyle = C.ink; g.lineWidth = 3; g.strokeRect(346, 116, 468, 450); g.strokeRect(370, 140, 420, 400);
52
+ });
53
+
54
+ // ---------- 地:花草地 ----------
55
+ const FLOW = (() => { const r = rng(57), o = []; for (let i = 0; i < 260; i++) { const y = 712 + Math.pow(r(), 0.8) * 370; o.push([r() * W, y, 6 + (y - 700) / 380 * 14 * (0.6 + r() * 0.6), (r() * 5) | 0, r() * 6.28]); } return o.sort((a, b) => a[1] - b[1]); })();
56
+ const FCOL = [['#d8402a', '#f4d65a'], ['#3a5ab0', '#f2eee0'], ['#f2eee0', '#d8402a'], ['#8a4ab0', '#f4d65a'], ['#e08aa0', '#3a5ab0']];
57
+ function meadow(c, t) {
58
+ c.drawImage(P.cached('kl_meadowBase', W, H, (g) => { const gr = g.createLinearGradient(0, 700, 0, H); gr.addColorStop(0, '#2c4a2a'); gr.addColorStop(1, '#1a3020'); g.fillStyle = gr; g.fillRect(0, 700, W, H - 700);
59
+ const r = rng(61); for (let i = 0; i < 1600; i++) { g.strokeStyle = r() < 0.5 ? '#4f7a3a' : '#7aa04a'; g.lineWidth = 2; const x = r() * W, y = 705 + r() * 380; g.beginPath(); g.moveTo(x, y); g.lineTo(x + (r() - .5) * 8, y - 8 - r() * 14); g.stroke(); }
60
+ g.fillStyle = '#d6a845'; g.fillRect(0, 696, W, 8); }), 0, 0);
61
+ FLOW.forEach(([x, y, s, k, ph]) => { const nod = Math.sin(t * 4.2 + ph) * s * 0.35, fc = FCOL[k];
62
+ c.fillStyle = fc[0]; c.beginPath(); c.arc(x + nod, y, s, 0, Math.PI * 2); c.fill();
63
+ c.strokeStyle = fc[1]; c.lineWidth = Math.max(1.5, s * 0.18); c.beginPath(); c.arc(x + nod, y, s * 0.62, 0, Math.PI * 2); c.stroke();
64
+ c.fillStyle = fc[1]; c.beginPath(); c.arc(x + nod, y, s * 0.25, 0, Math.PI * 2); c.fill(); });
65
+ }
66
+
67
+ // ---------- 桌:黑白方格(约瑟夫·霍夫曼) ----------
68
+ const table = () => P.cached('kl_table', W, H, (g) => {
69
+ g.fillStyle = '#111'; g.fillRect(810, 616, 400, 36); g.fillRect(836, 652, 348, 50); g.fillRect(842, 700, 26, 205); g.fillRect(1156, 700, 26, 205);
70
+ g.fillStyle = '#f4efe2'; for (let x = 814; x < 1206; x += 16) for (let y = 620; y < 648; y += 16) if (((x - 814) / 16 + (y - 620) / 16) % 2 === 0) g.fillRect(x, y, 12, 12);
71
+ for (let x = 842; x < 1180; x += 22) g.fillRect(x, 662, 14, 30);
72
+ for (let y = 706; y < 900; y += 20) { g.fillRect(848, y, 14, 12); g.fillRect(1162, y, 14, 12); }
73
+ g.fillStyle = '#d6a845'; g.fillRect(810, 612, 400, 6); g.fillRect(836, 698, 348, 5);
74
+ });
75
+
76
+ // ---------- 纹样 ----------
77
+ function spiral(c, x, y, R, rot, col, lw = 2.2) { c.strokeStyle = col; c.lineWidth = lw; c.beginPath(); for (let a = 0; a < Math.PI * 5.2; a += 0.22) { const rr = R * a / (Math.PI * 5.2); const px = x + Math.cos(a + rot) * rr, py = y + Math.sin(a + rot) * rr; a ? c.lineTo(px, py) : c.moveTo(px, py); } c.stroke(); }
78
+ function eye(c, x, y, L, open, iris) {
79
+ const h = L * 0.42 * open;
80
+ c.fillStyle = '#f6f0e0'; c.strokeStyle = C.ink; c.lineWidth = 2.2;
81
+ c.beginPath(); c.moveTo(x - L / 2, y); c.quadraticCurveTo(x, y - h * 2, x + L / 2, y); c.quadraticCurveTo(x, y + h * 2, x - L / 2, y); c.closePath(); c.fill(); c.stroke();
82
+ if (open > 0.25) { c.save(); c.clip(); c.fillStyle = iris; c.beginPath(); c.arc(x, y, L * 0.22, 0, Math.PI * 2); c.fill(); c.fillStyle = C.ink; c.beginPath(); c.arc(x, y, L * 0.1, 0, Math.PI * 2); c.fill(); c.restore(); }
83
+ c.beginPath(); c.moveTo(x - L / 2 - 4, y); c.quadraticCurveTo(x, y - L * 0.42 * 2 - 3, x + L / 2 + 4, y); c.stroke(); // 上眼睑弧(不随开合,像埃及荷鲁斯之眼的勾)
84
+ }
85
+ const ROBE = (() => { const r = rng(71), o = []; for (let i = 0; i < 150; i++) { const x = 1090 + r() * 380, y = 420 + r() * 520; o.push({ x, y, k: r(), s: 0.7 + r() * 0.6, ph: r() * 6.28 }); } return o; })();
86
+ function robePattern(c, t, lt) {
87
+ c.drawImage(goldTex(), 0, 0);
88
+ // 下摆的黑白长方块(《吻》男子袍的语言,竖向排)
89
+ const r = rng(77); for (let i = 0; i < 60; i++) { const x = 1100 + r() * 360, y = 640 + r() * 300, w = 10 + r() * 14, h = 26 + r() * 40; c.fillStyle = r() < 0.55 ? '#141010' : '#f4efe2'; c.fillRect(x, y, w, h); c.strokeStyle = '#d6a845'; c.lineWidth = 2; c.strokeRect(x, y, w, h); }
90
+ ROBE.forEach(({ x, y, k, s, ph }, i) => {
91
+ if (k < 0.3) { // 同心圆(彩色)
92
+ const R = 16 * s, cols = [['#c03a2a', '#f4efe2', '#2a4a9a'], ['#2a4a9a', '#d6a845', '#c03a2a'], ['#6a3a8a', '#f4efe2', '#d6a845']][i % 3];
93
+ cols.forEach((col, j) => { c.fillStyle = col; c.beginPath(); c.arc(x, y, R * (1 - j * 0.3), 0, Math.PI * 2); c.fill(); });
94
+ } else if (k < 0.62) { // 深褐底金螺旋,转
95
+ c.fillStyle = '#3a2410'; c.beginPath(); c.arc(x, y, 15 * s, 0, Math.PI * 2); c.fill();
96
+ spiral(c, x, y, 13 * s, t * 5 * (i % 2 ? 1 : -1) + ph, '#f2cf6a', 2);
97
+ } else if (k < 0.84) { // 眼形纹:轮流眨
98
+ const bl = Math.sin(t * 2.2 + ph * 3); const open = bl > 0.86 ? clamp(1 - (bl - 0.86) / 0.07) : 1;
99
+ eye(c, x, y, 34 * s, Math.max(0.08, open), ['#2a4a9a', '#2a7a5a', '#8a2a2a'][i % 3]);
100
+ } else { // 小三角+圆点簇
101
+ c.fillStyle = '#f4efe2'; for (let j = 0; j < 5; j++) { c.beginPath(); c.arc(x + Math.cos(j * 1.26) * 9 * s, y + Math.sin(j * 1.26) * 9 * s, 3.2 * s, 0, Math.PI * 2); c.fill(); }
102
+ c.fillStyle = '#c03a2a'; c.beginPath(); c.arc(x, y, 4 * s, 0, Math.PI * 2); c.fill();
103
+ }
104
+ });
105
+ }
106
+
107
+ // ---------- 写实的脸与手:柔和渐变体积,几乎无轮廓线(与平面金袍对比) ----------
108
+ function skin(c, path, cx, cy, R, lightDx = -0.35, lightDy = -0.4) {
109
+ const g = c.createRadialGradient(cx + R * lightDx, cy + R * lightDy, R * 0.1, cx, cy, R * 1.25);
110
+ g.addColorStop(0, '#fbe8da'); g.addColorStop(0.5, '#f0cfb8'); g.addColorStop(0.85, '#d8a88c'); g.addColorStop(1, '#b88068');
111
+ c.fillStyle = g; c.fill(path);
112
+ }
113
+ function face(c, G, ch) {
114
+ skin(c, G.neck, G.A.neck[0] - 8, G.A.neck[1] - 10, 36, -0.5, -0.6);
115
+ c.save(); c.clip(G.neck); c.fillStyle = 'rgba(120,70,50,.28)'; c.filter = 'blur(6px)'; c.beginPath(); c.ellipse(G.A.chin[0] + 20, G.A.chin[1] + 8, 40, 14, 0, 0, Math.PI * 2); c.fill(); c.filter = 'none'; c.restore();
116
+ skin(c, G.face, G.A.headC[0] - 20, G.A.headC[1], 72);
117
+ c.save(); c.clip(G.face); c.filter = 'blur(7px)';
118
+ c.fillStyle = 'rgba(232,120,110,.38)'; c.beginPath(); c.arc(G.cheek[0] - 6, G.cheek[1] - 2, 15, 0, Math.PI * 2); c.fill();
119
+ c.fillStyle = 'rgba(150,90,70,.3)'; c.beginPath(); c.ellipse(G.eye.x + 2, G.eye.y - 2, 14, 8, 0, 0, Math.PI * 2); c.fill(); // 眼窝阴影
120
+ c.filter = 'none'; c.restore();
121
+ // 眼:睫毛浓、半垂(克里姆特女人的倦眼)
122
+ c.strokeStyle = '#3a2216'; c.lineWidth = 2.6; c.stroke(G.lid);
123
+ if (!ch.blink) { c.fillStyle = '#4a3020'; c.beginPath(); c.ellipse(G.eye.x - 1, G.eye.y + 2, 3.4, 3.6, 0, 0, Math.PI * 2); c.fill(); c.fillStyle = 'rgba(255,255,255,.85)'; c.beginPath(); c.arc(G.eye.x - 2.4, G.eye.y + 0.6, 1.1, 0, Math.PI * 2); c.fill(); }
124
+ G.lashes.forEach(l => { c.beginPath(); c.moveTo(...l[0]); c.lineTo(...l[1]); c.lineWidth = 1.8; c.stroke(); });
125
+ c.strokeStyle = 'rgba(120,80,40,.8)'; c.lineWidth = 2; c.stroke(G.brow);
126
+ const lp = G.lips; c.fillStyle = '#b8323a'; c.beginPath(); c.moveTo(...lp[0]); c.quadraticCurveTo(lp[1][0] + 2, lp[1][1] - 2, lp[1][0], lp[1][1]); c.quadraticCurveTo(lp[1][0] + 1, lp[2][1] + 2, lp[2][0], lp[2][1]); c.closePath(); c.fill();
127
+ c.strokeStyle = 'rgba(150,90,70,.55)'; c.lineWidth = 1.2; c.stroke(G.face);
128
+ }
129
+ function hair(c, G, t) {
130
+ const hg = c.createLinearGradient(G.A.headTop[0], G.A.headTop[1], G.A.nape[0], G.A.nape[1] + 30); hg.addColorStop(0, '#f6dc8a'); hg.addColorStop(0.5, '#dcae4a'); hg.addColorStop(1, '#a8782a');
131
+ c.fillStyle = hg; [G.hairBack, G.bun].forEach(p => p && c.fill(p));
132
+ c.strokeStyle = 'rgba(120,80,20,.55)'; c.lineWidth = 1.3; G.hairLines.forEach(h => c.stroke(h));
133
+ if (G.bun) { spiral(c, G.bunSpiral[0], G.bunSpiral[1], 26, 0.5, 'rgba(130,90,25,.75)', 1.6); }
134
+ }
135
+ // 花冠:发间一串小花(《吻》),微微转
136
+ function wreath(c, G, t) {
137
+ const A = G.A, pts = [[A.headTop[0] - 40, A.headTop[1] + 18], [A.headTop[0] - 8, A.headTop[1] + 2], [A.headTop[0] + 28, A.headTop[1] + 6], [A.headTop[0] + 60, A.headTop[1] + 22], [A.headTop[0] + 82, A.headTop[1] + 50]];
138
+ pts.forEach(([x, y], k) => { const fc = FCOL[k % 5], rr = 11; c.save(); c.translate(x, y); c.rotate(t * 1.5 + k);
139
+ c.fillStyle = fc[0]; for (let j = 0; j < 5; j++) { c.beginPath(); c.arc(Math.cos(j * 1.256) * rr * 0.6, Math.sin(j * 1.256) * rr * 0.6, rr * 0.5, 0, Math.PI * 2); c.fill(); }
140
+ c.fillStyle = fc[1]; c.beginPath(); c.arc(0, 0, rr * 0.35, 0, Math.PI * 2); c.fill(); c.restore(); });
141
+ }
142
+ function bangs(c, G) {
143
+ const hg = c.createLinearGradient(G.A.forehead[0], G.A.forehead[1] - 20, G.A.forehead[0] + 70, G.A.forehead[1] + 40); hg.addColorStop(0, '#f8e098'); hg.addColorStop(1, '#c89a3a');
144
+ c.fillStyle = hg; c.fill(G.bangs); c.strokeStyle = 'rgba(120,80,20,.5)'; c.lineWidth = 1.2; c.stroke(G.bangs);
145
+ G.locks.forEach(h => { c.strokeStyle = '#c89a3a'; c.lineWidth = 6; c.stroke(h); });
146
+ }
147
+
148
+ // ---------- 猫 ----------
149
+ function cat(c, K, t) {
150
+ const L = P.scratch('kl_cat'), g = L.getContext('2d'); g.reset();
151
+ RIG.drawCat(g, K, { orange: '#d8873a', white: '#f6ecd8', stripe: '#a8501a', line: '#3a2414', lw: 3, eye: '#5aa04a' });
152
+ // 橘色区:金螺旋+红点(装饰化毛色)
153
+ g.save(); g.globalCompositeOperation = 'source-atop';
154
+ const r = rng(91); for (let i = 0; i < 46; i++) { const x = 420 + r() * 260, y = 560 + r() * 340; const inWhite = g.isPointInPath(K.white, x, y); if (inWhite) { g.strokeStyle = ['#3a5ab0', '#d8402a', '#d6a845'][i % 3]; g.lineWidth = 2.4; g.beginPath(); g.arc(x, y, 6, 0, Math.PI * 2); g.stroke(); } else if (g.isPointInPath(K.body, x, y) && !g.isPointInPath(K.head, x, y)) { spiral(g, x, y, 9, t * 2.5 + i, '#f2cf6a', 1.8); } }
155
+ g.restore();
156
+ c.drawImage(L, 0, 0);
157
+ }
158
+
159
+ return {
160
+ draw(c, lt, t) {
161
+ const ch = KIT.choreo(lt, t);
162
+ c.drawImage(wall(), 0, 0);
163
+ // 金粉闪烁(四角星),并缓缓上浮,保证每帧都在动
164
+ c.save(); c.globalCompositeOperation = 'lighter';
165
+ DUST.forEach(([x0, y0, s, ph, sp]) => { const x = x0 + Math.sin(t * 0.8 + ph) * 14, y = ((y0 - t * 26) % H + H) % H; const tw = Math.sin(t * sp + ph); if (tw < 0.55) return; const a = (tw - 0.55) / 0.45, R = s * (0.6 + a * 1.1);
166
+ c.fillStyle = `rgba(255,236,160,${0.8 * a})`; c.beginPath(); c.moveTo(x, y - R * 2); c.lineTo(x + R * 0.35, y - R * 0.35); c.lineTo(x + R * 2, y); c.lineTo(x + R * 0.35, y + R * 0.35); c.lineTo(x, y + R * 2); c.lineTo(x - R * 0.35, y + R * 0.35); c.lineTo(x - R * 2, y); c.lineTo(x - R * 0.35, y - R * 0.35); c.closePath(); c.fill(); });
167
+ c.restore();
168
+ // 金箔碎片飘落(《吻》背景里的金屑):翻面的小方块和小圆片,保证每一帧都在动
169
+ P.particles(34, 77, t, { x0: 0, x1: W, y0: -30, y1: 720, speed: 150, drift: 36, life: 3.2 }).forEach(p => { c.save(); c.translate(p.x, p.y); c.rotate(p.a); c.scale(1, Math.cos(t * 5 + p.i));
170
+ const s2 = 7 + p.s * 8, g2 = c.createLinearGradient(-s2, -s2, s2, s2); g2.addColorStop(0, '#fbe6a0'); g2.addColorStop(1, '#a8741e'); c.fillStyle = g2;
171
+ if (p.i % 3) c.fillRect(-s2, -s2, s2 * 2, s2 * 2); else { c.beginPath(); c.arc(0, 0, s2, 0, Math.PI * 2); c.fill(); } c.strokeStyle = 'rgba(70,40,10,.6)'; c.lineWidth = 1.2; c.strokeRect && (p.i % 3) && c.strokeRect(-s2, -s2, s2 * 2, s2 * 2); c.restore(); });
172
+ // 窗+落叶
173
+ c.drawImage(windowArt(), 0, 0);
174
+ c.save(); c.beginPath(); c.rect(370, 140, 420, 400); c.clip();
175
+ P.particles(14, 5, t, { x0: 380, x1: 780, y0: 130, y1: 560, speed: 120, drift: 22, life: 2.4 }).forEach(p => { c.save(); c.translate(p.x, p.y); c.rotate(p.a); c.scale(1, Math.cos(t * 6 + p.i)); c.fillStyle = ['#e8a83a', '#d8603a', '#f2d070'][p.i % 3]; c.beginPath(); c.ellipse(0, 0, 9, 5, 0, 0, Math.PI * 2); c.fill(); c.restore(); });
176
+ c.restore();
177
+ meadow(c, t);
178
+ c.drawImage(table(), 0, 0);
179
+ // 流光:斜向亮带匀速扫过整面金墙(角色在之后画,自然挡住)
180
+ const sweep = (c2, x0) => { const gr = c2.createLinearGradient(x0 - 260, 0, x0 + 260, 0); gr.addColorStop(0, 'rgba(255,240,180,0)'); gr.addColorStop(0.5, 'rgba(255,240,180,.24)'); gr.addColorStop(1, 'rgba(255,240,180,0)'); c2.save(); c2.globalCompositeOperation = 'lighter'; c2.transform(1, 0, -0.45, 1, 0, 0); c2.fillStyle = gr; c2.fillRect(x0 - 260 - 600, 0, 520 + 1200, 700); c2.restore(); };
181
+ const sx = lerp(-200, 2300, clamp(lt / 1.15));
182
+ sweep(c, sx);
183
+ // 猫
184
+ const K = RIG.cat({ tail: ch.tail, blink: ch.blink, breathe: ch.breathe });
185
+ cat(c, K, t);
186
+ // 少女:先金袍(剪影里填金+纹样),再写实的脸手
187
+ const G = RIG.girl({ cup: ch.cup, sip: ch.sip, breathe: ch.breathe, hair: 'bun' });
188
+ const R_ = P.scratch('kl_robe'), rg = R_.getContext('2d'); rg.reset();
189
+ const Pt = P.scratch('kl_pat'), pt = Pt.getContext('2d'); pt.reset(); robePattern(pt, t, lt); // 纹样先画满一张,再按剪影一次性 source-in(逐个纹样 source-in 会互相清掉)
190
+ rg.fillStyle = '#000'; [G.farSleeve, G.skirt, G.torso].forEach(p => rg.fill(p));
191
+ rg.globalCompositeOperation = 'source-in'; rg.drawImage(Pt, 0, 0);
192
+ rg.globalCompositeOperation = 'source-atop'; sweep(rg, sx + 260);
193
+ rg.globalCompositeOperation = 'source-over';
194
+ rg.strokeStyle = C.ink; rg.lineWidth = 3; [G.farSleeve, G.skirt, G.torso].forEach(p => rg.stroke(p));
195
+ skin(rg, G.farHand, 1180, 614, 40);
196
+ hair(rg, G, t);
197
+ // 远侧袖口:一圈黑白方格
198
+ face(rg, G, ch); bangs(rg, G); wreath(rg, G, t);
199
+ // 近侧手臂(金袖)在脸前
200
+ rg.save(); rg.beginPath(); rg.rect(0, 0, W, H); rg.clip();
201
+ rg.fillStyle = '#000'; const arm = new Path2D(); arm.addPath(G.upperArm); arm.addPath(G.foreArm);
202
+ rg.save(); rg.clip(arm); rg.drawImage(Pt, 0, 0); rg.restore(); rg.strokeStyle = C.ink; rg.lineWidth = 3; rg.stroke(G.upperArm); rg.stroke(G.foreArm);
203
+ rg.fillStyle = '#141010'; rg.fill(G.cuff); rg.save(); rg.clip(G.cuff); rg.fillStyle = '#f4efe2'; for (let k = 0; k < 40; k++) rg.fillRect(G.A.elbow[0] - 100 + (k % 10) * 20, G.A.elbow[1] - 100 + ((k / 10) | 0) * 50 + (k % 2) * 8, 8, 8); rg.restore();
204
+ rg.restore();
205
+ skin(rg, G.hand, G.A.hand[0], G.A.hand[1], 20);
206
+ // 金杯(黑方格)
207
+ const cp = G.cup; rg.save(); rg.translate(cp.x, cp.y); rg.rotate(cp.tilt);
208
+ rg.beginPath(); rg.moveTo(-cp.w / 2, 0); rg.lineTo(-cp.w * 0.38, cp.h); rg.lineTo(cp.w * 0.38, cp.h); rg.lineTo(cp.w / 2, 0); rg.closePath();
209
+ const cg = rg.createLinearGradient(-cp.w / 2, 0, cp.w / 2, 0); cg.addColorStop(0, '#f6d880'); cg.addColorStop(0.5, '#c8962e'); cg.addColorStop(1, '#8a5a14'); rg.fillStyle = cg; rg.fill(); rg.strokeStyle = C.ink; rg.lineWidth = 2; rg.stroke();
210
+ rg.fillStyle = '#141010'; for (let k = 0; k < 3; k++) rg.fillRect(-cp.w * 0.3 + k * cp.w * 0.24, cp.h * 0.35, 7, 7);
211
+ rg.restore();
212
+ rg.fillStyle = '#f0cfb8'; rg.beginPath(); rg.arc(G.thumb[0], G.thumb[1], 6.5, 0, Math.PI * 2); rg.fill();
213
+ c.drawImage(R_, 0, 0);
214
+ // 热气:金色细螺旋线上升
215
+ c.save(); c.globalAlpha = 0.85; [0, 1].forEach(k => { c.strokeStyle = '#f6e6a8'; c.lineWidth = 2.4; c.beginPath(); for (let i = 0; i <= 30; i++) { const q = i / 30; const x = G.cup.x - 6 + k * 14 - q * 50 * ch.cup + Math.sin(t * 4 + q * 8 + k * 2) * 9 * q, y = G.cup.y - 8 - q * 85; i ? c.lineTo(x, y) : c.moveTo(x, y); } c.stroke(); }); c.restore();
216
+ },
217
+ label(c, lt) {
218
+ c.save();
219
+ c.fillStyle = '#141010'; c.fillRect(1468, 36, 422, 176);
220
+ c.fillStyle = '#d6a845'; c.fillRect(1468, 36, 422, 6); c.fillRect(1468, 206, 422, 6);
221
+ for (let x = 1472; x < 1886; x += 14) { c.fillStyle = ((x - 1472) / 14) % 2 ? '#f4efe2' : '#d6a845'; c.fillRect(x, 48, 9, 9); c.fillRect(x, 192, 9, 9); }
222
+ c.restore();
223
+ const g = c.createLinearGradient(0, 60, 0, 136); g.addColorStop(0, '#fbe6a0'); g.addColorStop(0.5, '#d6a845'); g.addColorStop(1, '#9a6a1c');
224
+ KIT.label(c, { title: '金色时期', sub: 'GUSTAV KLIMT · 1907', tFont: '78px "LXGWWenKai-500"', sFont: '27px "Marcellus-400"', tCol: g, sCol: '#e8cf86', x: 1858, y: 140, sy: 182, spacing: 8 });
225
+ },
226
+ };
227
+ })();