@biunow/agent-biuart 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,151 @@
1
+ // 1947 马蒂斯《爵士》剪纸(Jazz / Polynesia / Icarus / Lagoon)——纯代码。
2
+ // 手法:一切是「刷过水粉的彩纸剪出来、钉在墙上」:
3
+ // ①剪形:自生成形状一律做成剪刀折线(沿轮廓每 14–24px 取点+±2.5px 抖动、直线相连)
4
+ // ②RIG 角色的 Path2D 拿不到点 → 角色整层画完后做低频位移(fbm 波长≈35px、幅度 5px)让边缘像手剪的
5
+ // ③每张纸带投影(偏移 5,6、模糊 4、α.28)=钉在墙上
6
+ // ④整幅叠水粉刷痕(缓存的长条笔痕,overlay)
7
+ // 母题:海藻像纸片一样绕根部摆动+卷边(scaleX 呼吸)、伊卡洛斯的黄星转动闪、红心跳、一片黄纸翻飞飘落、白鸟扑翅。
8
+ SCENES['30_matisse'] = (() => {
9
+ const W = 1920, H = 1080, TAU = Math.PI * 2, { clamp, lerp } = U, P = PAINT;
10
+ const C = { cobalt: '#1d4fb8', sky: '#4f95dc', yel: '#f7d21e', red: '#e2322a', black: '#141414', white: '#f7f3e8', green: '#1f9a5a', pink: '#ef6f9a', org: '#f28a24', orgD: '#c8521a', skin: '#f3c9a6', violet: '#5b3a9a' };
11
+ const cut = P.cut; // 剪刀折线:沿轮廓每 step px 取点+抖动、直线相连
12
+ // 海藻形(马蒂斯 Lagoon/Polynesia 里那种):锥形茎 + 左右交替伸出的圆头「手指」。
13
+ // 每块都是剪刀折线;同向绕行的子路径用 nonzero 一次填充=并集(投影也只算一次)。
14
+ function capsule(A, th, L, w) { // th:相对竖直向上的角度
15
+ const d = [Math.sin(th), -Math.cos(th)], n = [-d[1], d[0]], T = [A[0] + d[0] * (L - w / 2), A[1] + d[1] * (L - w / 2)], pts = [];
16
+ pts.push([A[0] + n[0] * w * 0.42, A[1] + n[1] * w * 0.42]);
17
+ pts.push([A[0] + d[0] * L * 0.5 + n[0] * w * 0.55, A[1] + d[1] * L * 0.5 + n[1] * w * 0.55]);
18
+ for (let k = 0; k <= 6; k++) { const a = Math.atan2(n[1], n[0]) - k / 6 * Math.PI; pts.push([T[0] + Math.cos(a) * w / 2, T[1] + Math.sin(a) * w / 2]); }
19
+ pts.push([A[0] + d[0] * L * 0.5 - n[0] * w * 0.55, A[1] + d[1] * L * 0.5 - n[1] * w * 0.55]);
20
+ pts.push([A[0] - n[0] * w * 0.42, A[1] - n[1] * w * 0.42]);
21
+ return pts;
22
+ }
23
+ function algaeShape(len, width, lobes, seed, bend) {
24
+ const r = U.rng(seed), p = new Path2D(), sp = u => [Math.sin(u * 1.6) * bend * len * 0.25, -u * len];
25
+ p.addPath(cut(capsule(sp(0), bend * 0.2, len, width * 0.24), seed, 16, 2));
26
+ const n = Math.round(lobes * 2);
27
+ for (let k = 0; k < n; k++) { const u = 0.12 + 0.78 * k / Math.max(1, n - 1), side = k % 2 ? 1 : -1, A = sp(u);
28
+ const th = side * (1.05 - 0.55 * u) + bend * 0.2 + (r() - 0.5) * 0.25, L = width * (1.0 - 0.45 * u) * (0.8 + r() * 0.3), w = width * (0.34 - 0.12 * u);
29
+ p.addPath(cut(capsule(A, th, L, w), seed + k * 11, 15, 1.8)); }
30
+ return p;
31
+ }
32
+ const shapeCache = {};
33
+ const algaePath = (k, len, width, lobes, bend) => shapeCache['a' + k] || (shapeCache['a' + k] = algaeShape(len, width, lobes, k * 7 + 3, bend));
34
+ const starPath = (k, r0, r1, n) => shapeCache['s' + k] || (shapeCache['s' + k] = (() => { const r = U.rng(k * 31 + 5), pts = []; for (let i = 0; i < n * 2; i++) { const a = i / (n * 2) * TAU + (r() - 0.5) * 0.25, rr = (i % 2 ? r1 : r0) * (0.75 + r() * 0.45); pts.push([Math.cos(a) * rr, Math.sin(a) * rr]); } return cut(pts, k * 17, 14, 1.8); })());
35
+ const birdPath = () => shapeCache.bird || (shapeCache.bird = cut([[0, 0], [-26, -14], [-58, -10], [-34, 2], [-62, 18], [-18, 10], [0, 22], [24, 8], [50, 12], [30, -2], [44, -18]], 99, 12, 1.6));
36
+ const paperShadow = (c, fn) => { c.save(); c.shadowColor = 'rgba(10,20,40,.3)'; c.shadowOffsetX = 5; c.shadowOffsetY = 6; c.shadowBlur = 4; fn(); c.restore(); };
37
+ const placed = (c, path, x, y, rot, sx, sy, col) => { c.save(); c.translate(x, y); c.rotate(rot); c.scale(sx, sy); c.fillStyle = col; c.fill(path); c.restore(); };
38
+
39
+ // ---- 静态底:墙的双蓝棋盘(剪纸块)+地+桌(缓存) ----
40
+ const bg = () => P.cached('mt_bg', W, H, (g) => {
41
+ g.fillStyle = C.white; g.fillRect(0, 0, W, H);
42
+ const cw = 240, chh = 236;
43
+ for (let j = 0; j < 3; j++) for (let i = 0; i < 8; i++) { const x = i * cw, y = j * chh, r = U.rng(i * 7 + j * 31);
44
+ g.fillStyle = (i + j) % 2 ? C.cobalt : C.sky;
45
+ g.fill(cut([[x - 2 + r() * 4, y - 2], [x + cw + 2, y + r() * 4 - 2], [x + cw + r() * 4, y + chh + 2], [x - 2, y + chh + r() * 4]], i * 13 + j, 22, 2)); }
46
+ // 地:翠绿纸+黑踢脚条
47
+ g.fillStyle = C.green; g.fill(cut([[0, 704], [W, 698], [W, H], [0, H]], 5, 24, 2.5));
48
+ g.fillStyle = C.black; g.fill(cut([[0, 690], [W, 686], [W, 712], [0, 716]], 6, 20, 2));
49
+ // 地上的粉色大圆片、黄色剪纸碎片(《爵士》的散落纸片)
50
+ paperShadow(g, () => { g.fillStyle = C.pink; g.fill(cut(Array.from({ length: 24 }, (_, k) => [180 + Math.cos(k / 24 * TAU) * 120, 990 + Math.sin(k / 24 * TAU) * 44]), 8, 18, 3)); });
51
+ // 窗:白纸框里的《伊卡洛斯》蓝底
52
+ paperShadow(g, () => { g.fillStyle = C.white; g.fill(cut([[344, 114], [818, 118], [814, 568], [340, 564]], 11, 26, 2)); });
53
+ g.fillStyle = C.cobalt; g.fill(cut([[372, 142], [790, 144], [788, 540], [370, 538]], 12, 24, 2));
54
+ paperShadow(g, () => { g.fillStyle = C.white; g.fill(cut([[326, 550], [836, 552], [834, 584], [324, 582]], 13, 24, 2)); });
55
+ // 桌:黑色剪纸桌面+两条腿
56
+ paperShadow(g, () => { g.fillStyle = C.black; g.fill(cut([[806, 616], [1212, 612], [1206, 648], [810, 652]], 14, 20, 2)); g.fill(cut([[846, 648], [872, 648], [878, 904], [842, 906]], 15, 20, 2)); g.fill(cut([[1150, 648], [1176, 648], [1182, 904], [1146, 906]], 16, 20, 2)); });
57
+ // 椅:黄色剪纸椅背
58
+ paperShadow(g, () => { g.fillStyle = C.yel; g.fill(cut([[1430, 480], [1488, 470], [1500, 900], [1470, 902], [1462, 560], [1442, 560], [1446, 902], [1420, 902]], 17, 18, 2)); });
59
+ });
60
+ // 水粉刷痕叠层
61
+ const gouache = () => P.cached('mt_gouache', W, H, (g) => {
62
+ const r = U.rng(2024); g.lineCap = 'round';
63
+ for (let i = 0; i < 2600; i++) { const x = r() * W, y = r() * H, a = -0.25 + (r() - 0.5) * 0.5, L = 60 + r() * 240;
64
+ g.strokeStyle = r() < 0.5 ? `rgba(255,255,255,${0.05 + r() * 0.07})` : `rgba(0,0,0,${0.04 + r() * 0.06})`; g.lineWidth = 6 + r() * 16;
65
+ g.beginPath(); g.moveTo(x, y); g.lineTo(x + Math.cos(a) * L, y + Math.sin(a) * L); g.stroke(); }
66
+ });
67
+ // 角色层的「手剪」位移场(静态)
68
+ const BX = [330, 470, 1600, 980]; // x0,y0,x1,y1(猫+桌上+少女)
69
+ const roughen = layer => P.roughen(layer, BX, { freq: 0.028, amp: 9, key: 'matisse' }); // 整层低频位移=手剪边
70
+
71
+ // ---- 会动的剪纸母题 ----
72
+ const ALGAE = [ // x,y(根), 长, 宽, 叶数, 弯, 颜色, 相位
73
+ [110, 690, 400, 150, 3.5, 0.4, C.white, 0], [1600, 690, 440, 160, 4, -0.4, C.white, 1.3], [1810, 690, 300, 130, 3, 0.3, C.yel, 2.1],
74
+ [250, 470, 230, 110, 2.5, -0.3, C.white, 3.1], [1010, 540, 280, 120, 3, 0.3, C.pink, 0.7], [1780, 440, 200, 100, 2.5, 0.3, C.white, 2.6],
75
+ ];
76
+ function wallLife(c, t) {
77
+ ALGAE.forEach(([x, y, len, wd, lobes, bend, col, ph], k) => {
78
+ const rot = Math.sin(t * 2.8 + ph) * 0.13, sx = 0.93 + 0.07 * Math.sin(t * 3.6 + ph * 1.7); // 摆+卷边
79
+ paperShadow(c, () => placed(c, algaePath(k, len, wd, lobes, bend), x, y, rot, sx, 1, col));
80
+ });
81
+ // 白鸟(《波利尼西亚》)扑翅滑过
82
+ [[1220, 120, 0], [190, 160, 1.7]].forEach(([x0, y0, ph], k) => { const x = x0 + Math.sin(t * 1.2 + ph) * 40, y = y0 + Math.sin(t * 2.4 + ph) * 12, fl = 0.55 + 0.45 * Math.cos(t * 13 + ph);
83
+ paperShadow(c, () => placed(c, birdPath(), x, y, -0.15 + Math.sin(t * 2 + ph) * 0.08, 1.5, 1.5 * fl, C.white)); });
84
+ }
85
+ function icarus(c, t) {
86
+ c.save(); c.beginPath(); c.rect(372, 142, 418, 398); c.clip();
87
+ // 黄星:转+闪
88
+ [[420, 190, 34, 0], [740, 200, 42, 1], [460, 470, 30, 2], [752, 452, 36, 3], [560, 180, 22, 4], [700, 330, 26, 5], [410, 340, 24, 6]].forEach(([x, y, r, k]) => {
89
+ const s = 0.82 + 0.22 * Math.sin(t * 7 + k * 1.9); placed(c, starPath(k, r, r * 0.42, 5 + (k % 3)), x, y, t * (k % 2 ? 0.8 : -0.6), s, s, C.yel); });
90
+ // 伊卡洛斯:黑色人形,两臂张开,上下漂
91
+ const by = Math.sin(t * 2.4) * 10, ax = Math.sin(t * 3.1) * 0.12;
92
+ c.save(); c.translate(580, 340 + by); c.rotate(-0.08 + Math.sin(t * 1.7) * 0.05); c.fillStyle = C.black;
93
+ c.fill(shapeCache.icarus || (shapeCache.icarus = cut([[0, -118], [18, -112], [22, -90], [12, -78], [44, -70], [96, -94], [118, -84], [64, -40], [40, -20], [52, 40], [86, 110], [66, 118], [26, 60], [6, 40], [-18, 64], [-48, 118], [-70, 108], [-36, 40], [-40, -20], [-66, -42], [-118, -76], [-100, -92], [-44, -70], [-12, -78], [-22, -92], [-18, -112]], 77, 14, 1.8)));
94
+ const hb = 1 + 0.18 * Math.max(0, Math.sin(t * 9)); c.fillStyle = C.red; c.beginPath(); c.ellipse(6, -44, 14 * hb, 13 * hb, 0, 0, TAU); c.fill(); // 红心跳
95
+ c.restore();
96
+ c.restore();
97
+ }
98
+ function fallingPaper(c, t) {
99
+ // 一片黄纸从左上翻飞飘到右下(scaleX=cos 做翻面)
100
+ const q = (t * 0.32 + 0.15) % 1, x = lerp(130, 320, q) + Math.sin(t * 3) * 60, y = lerp(-60, 640, q), fl = Math.cos(t * 6);
101
+ paperShadow(c, () => placed(c, algaePath(20, 110, 60, 2.5, 0.2), x, y, t * 1.4, fl, 1, C.pink));
102
+ }
103
+
104
+ // ---- 角色 ----
105
+ function chars(g, t, ch, G, K) {
106
+ RIG.drawCat(g, K, { lw: 0, orange: C.org, white: C.white, stripe: C.orgD, stripeW: 12, earInner: C.pink,
107
+ face: (c, K) => { K.eyes.forEach(e => { c.fillStyle = K.blink ? C.orgD : C.green; c.beginPath(); c.ellipse(e.x, e.y, 10, K.blink ? 3 : 9, 0, 0, TAU); c.fill(); if (!K.blink) { c.fillStyle = C.black; c.beginPath(); c.ellipse(e.x + 2, e.y, 3, 7, 0, 0, TAU); c.fill(); } });
108
+ c.fillStyle = C.pink; c.beginPath(); c.moveTo(K.nose[0] - 7, K.nose[1] - 4); c.lineTo(K.nose[0] + 7, K.nose[1] - 4); c.lineTo(K.nose[0], K.nose[1] + 6); c.fill(); } });
109
+ // 少女:马蒂斯《罗马尼亚衬衫》——白色宽袖刺绣衬衫+红长裙+黑腰带
110
+ RIG.drawGirl(g, G, { lw: 0, line: '#111', locks: false, skin: C.skin, hair: C.yel, dress: C.white, skirt: C.red, sleeve: C.white, cuff: C.white, shoe: C.black, cupBody: C.yel, cupRim: C.orgD,
111
+ features: (c, G) => { c.fillStyle = C.black; c.beginPath(); c.ellipse(G.eye.x - 1, G.eye.y + 1, 7, ch.blink ? 1.5 : 4, 0, 0, TAU); c.fill();
112
+ c.save(); c.translate(0, -3); c.strokeStyle = C.black; c.lineWidth = 3.5; c.stroke(G.brow); c.restore();
113
+ c.fillStyle = C.red; c.beginPath(); c.moveTo(...G.lips[0]); c.lineTo(...G.lips[1]); c.lineTo(...G.lips[2]); c.closePath(); c.fill(); c.fillStyle = C.pink; c.beginPath(); c.arc(G.cheek[0], G.cheek[1], 10, 0, TAU); c.fill(); },
114
+ hooks: {
115
+ skirt: (c, G) => { c.fillStyle = C.black; const w = G.A.waist; c.fillRect(w[0] - 70, w[1] - 14, 150, 22); },
116
+ torso: (c, G) => { // 前襟刺绣竖带:红蓝交错的小三角
117
+ const ch0 = G.A.chest; c.fillStyle = C.cobalt; c.fillRect(ch0[0] - 6, ch0[1] - 60, 18, 150);
118
+ for (let i = 0; i < 9; i++) { c.fillStyle = i % 2 ? C.red : C.yel; c.beginPath(); c.moveTo(ch0[0] - 6, ch0[1] - 56 + i * 16); c.lineTo(ch0[0] + 12, ch0[1] - 48 + i * 16); c.lineTo(ch0[0] - 6, ch0[1] - 40 + i * 16); c.fill(); } },
119
+ head: (c, G) => { // 卷曲金发:一圈剪纸卷团
120
+ const h = G.A.headC; c.fillStyle = C.yel; [[60, -40, 30], [82, 4, 28], [70, 46, 26], [30, -64, 26], [-8, -66, 22]].forEach(([dx, dy, r]) => { c.beginPath(); c.arc(h[0] + dx, h[1] + dy, r, 0, TAU); c.fill(); }); },
121
+ arm: (c, G) => { // 袖上的刺绣横带(随手臂走)
122
+ const S = G.A.shoulder, E = G.A.elbow; c.save(); c.clip(G.upperArm);
123
+ const a = Math.atan2(E[1] - S[1], E[0] - S[0]); [0.25, 0.45].forEach((q, k) => { const x = lerp(S[0], E[0], q), y = lerp(S[1], E[1], q); c.save(); c.translate(x, y); c.rotate(a + Math.PI / 2);
124
+ c.fillStyle = k ? C.cobalt : C.red; c.fillRect(-40, -8, 80, 16); c.fillStyle = C.yel; for (let i = -3; i <= 3; i++) { c.beginPath(); c.arc(i * 11, 0, 4, 0, TAU); c.fill(); } c.restore(); });
125
+ c.restore(); } } });
126
+ }
127
+
128
+ return {
129
+ draw(c, lt, t) {
130
+ const ch = P.choreo(lt, t);
131
+ c.drawImage(bg(), 0, 0);
132
+ icarus(c, t);
133
+ wallLife(c, t);
134
+ // 桌上:白茶壶、一碗黄柠檬(剪纸)
135
+ paperShadow(c, () => { c.fillStyle = C.white; c.fill(shapeCache.pot || (shapeCache.pot = cut([[860, 616], [850, 584], [866, 556], [906, 548], [944, 556], [958, 588], [950, 616]], 51, 14, 1.8))); c.fill(shapeCache.spout || (shapeCache.spout = cut([[856, 590], [822, 560], [832, 552], [868, 578]], 52, 10, 1.4)));
136
+ c.fillStyle = C.yel; [[1040, 596], [1072, 592], [1056, 572]].forEach(([x, y], k) => { c.beginPath(); c.ellipse(x, y, 20, 15, 0.3 * k, 0, TAU); c.fill(); }); });
137
+ const G = RIG.girl({ cup: ch.cup, sip: ch.sip, hair: 'bun', breathe: ch.breathe, dy: Math.sin(t * 8) * 1.5 });
138
+ const K = RIG.cat({ tail: ch.tail, blink: ch.blink, breathe: ch.breathe });
139
+ const L = P.scratch('mtChars'), lg = L.getContext('2d', { willReadFrequently: true }); lg.reset();
140
+ chars(lg, t, ch, G, K);
141
+ roughen(L);
142
+ paperShadow(c, () => c.drawImage(L, 0, 0));
143
+ // 热气:两条白色剪纸卷须
144
+ const cp = G.cup; [0, 1].forEach(k => { const sw = Math.sin(t * 5 + k * 2) * 0.25; paperShadow(c, () => placed(c, algaePath(30 + k, 70, 16, 1.5, 0.6), cp.x - 8 + k * 18, cp.y - 10, sw, 1, 1, C.white)); });
145
+ fallingPaper(c, t);
146
+ c.save(); c.globalCompositeOperation = 'overlay'; c.drawImage(gouache(), 0, 0); c.restore();
147
+ // 计数器底:白纸片
148
+ paperShadow(c, () => { c.fillStyle = C.white; c.fill(shapeCache.lab || (shapeCache.lab = cut([[1500, 44], [1892, 38], [1888, 214], [1504, 208]], 61, 26, 2.2))); });
149
+ },
150
+ };
151
+ })();
@@ -0,0 +1,109 @@
1
+ // 1982 凯斯·哈林(地铁粉笔画 → 波普商店的纯色涂鸦)——纯代码。
2
+ // 手法:纯色底(黄墙、绿地)+一切都是 12–14px 圆头黑马克笔轮廓+平涂;人物无五官(少女保留一点眼和唇以便认人)。
3
+ // 小人=胶囊肢体,「先全部描粗黑、再统一填色」得到并集轮廓;4 个姿势 8fps 硬切(不插值)=哈林的跳舞小人。
4
+ // 放射动作线:物体外围成对的短直线,8fps 闪。
5
+ // 一切按 128 BPM 弹跳:少女/猫上下颠、桌子挤压拉伸、茶壶跳、窗框摇。
6
+ // 母题:窗里的光芒宝宝(光芒 8fps 闪)、墙上 3 个跳舞小人、桌上方的放射红心。
7
+ SCENES['31_haring'] = (() => {
8
+ const W = 1920, H = 1080, TAU = Math.PI * 2, { clamp, lerp } = U, P = PAINT;
9
+ const C = { yel: '#ffd51c', green: '#16a54a', red: '#e8262b', blue: '#1f62d6', pink: '#ff6fb0', org: '#ff8a1c', white: '#ffffff', ink: '#111111', skin: '#ffd9b8' };
10
+ const BEAT = 128 / 60; // 每秒拍数
11
+ const bounce = (t, ph = 0) => Math.pow(Math.abs(Math.sin((t * BEAT + ph) * Math.PI)), 0.6); // 0..1,拍点在 0
12
+ const LW = 13;
13
+ const ink = (c, p, fill, lw = LW) => { c.lineJoin = 'round'; c.lineCap = 'round'; if (fill) { c.fillStyle = fill; c.fill(p); } c.strokeStyle = C.ink; c.lineWidth = lw; c.stroke(p); };
14
+ // 放射动作线:围绕中心 (cx,cy) 半径 r 外的 n 对短线,8fps 换角度
15
+ function action(c, cx, cy, rx, ry, n, t, seed, len = 34, lw = 9) {
16
+ const st = Math.floor(t * 8), r = U.rng(seed * 97 + st * 13); c.save(); c.strokeStyle = C.ink; c.lineWidth = lw; c.lineCap = 'round';
17
+ for (let i = 0; i < n; i++) { const a = (i / n) * TAU + r() * 0.5; const x0 = cx + Math.cos(a) * rx, y0 = cy + Math.sin(a) * ry, L = len * (0.7 + r() * 0.6);
18
+ c.beginPath(); c.moveTo(x0, y0); c.lineTo(x0 + Math.cos(a) * L, y0 + Math.sin(a) * L); c.stroke(); }
19
+ c.restore();
20
+ }
21
+ // 跳舞小人:4 个姿势(局部坐标,原点=脚底中心,朝上为负)
22
+ const POSES = [
23
+ { head: [0, -210], neck: [0, -168], hip: [0, -86], hl: [-74, -150], hr: [70, -250], kl: [-30, -42], kr: [34, -42], fl: [-46, 0], fr: [44, 0] },
24
+ { head: [10, -200], neck: [6, -160], hip: [0, -84], hl: [-90, -220], hr: [86, -200], kl: [-40, -50], kr: [58, -64], fl: [-36, 0], fr: [96, -40] },
25
+ { head: [-12, -206], neck: [-6, -166], hip: [0, -86], hl: [-70, -250], hr: [80, -150], kl: [-50, -60], kr: [26, -40], fl: [-100, -36], fr: [40, 0] },
26
+ { head: [0, -224], neck: [0, -182], hip: [0, -100], hl: [-84, -256], hr: [84, -256], kl: [-40, -54], kr: [40, -54], fl: [-60, -14], fr: [60, -14] },
27
+ ];
28
+ function dancer(c, x, y, s, col, t, ph, flip = 1) {
29
+ const k = (Math.floor(t * 8) + ph) % 4, p = POSES[k], T = q => [x + q[0] * s * flip, y + q[1] * s];
30
+ const parts = [RIG.limb(T(p.neck), T(p.hip), 64 * s, 54 * s), RIG.limb(T(p.neck), T(p.hl), 34 * s, 30 * s), RIG.limb(T(p.neck), T(p.hr), 34 * s, 30 * s),
31
+ RIG.limb(T(p.hip), T(p.kl), 36 * s, 32 * s), RIG.limb(T(p.kl), T(p.fl), 32 * s, 30 * s), RIG.limb(T(p.hip), T(p.kr), 36 * s, 32 * s), RIG.limb(T(p.kr), T(p.fr), 32 * s, 30 * s)];
32
+ const hd = new Path2D(); const hc = T(p.head); hd.arc(hc[0], hc[1], 38 * s, 0, TAU); parts.push(hd);
33
+ c.save(); c.lineJoin = 'round'; c.strokeStyle = C.ink; c.lineWidth = LW * 2 * Math.max(0.7, s); parts.forEach(q => c.stroke(q)); c.fillStyle = col; parts.forEach(q => c.fill(q)); c.restore();
34
+ // 手脚旁的动作弧
35
+ c.save(); c.strokeStyle = C.ink; c.lineWidth = 8 * s; c.lineCap = 'round';
36
+ [p.hl, p.hr].forEach((h, i) => { const q = T(h); c.beginPath(); c.arc(q[0], q[1], 40 * s, (i ? -0.6 : 2.4), (i ? 0.4 : 3.4)); c.stroke(); });
37
+ c.restore();
38
+ }
39
+ function radiantBaby(c, x, y, s, t) {
40
+ // 光芒:12 根短线,8fps 交替长短
41
+ const st = Math.floor(t * 8); c.save(); c.strokeStyle = C.ink; c.lineWidth = 10; c.lineCap = 'round';
42
+ for (let i = 0; i < 14; i++) { const a = -Math.PI + i / 13 * Math.PI, L = (i + st) % 2 ? 40 : 66; c.beginPath(); c.moveTo(x + Math.cos(a) * 120 * s, y + Math.sin(a) * 96 * s); c.lineTo(x + Math.cos(a) * (120 * s + L), y + Math.sin(a) * (96 * s + L)); c.stroke(); }
43
+ c.restore();
44
+ // 爬行的宝宝:圆头+身体+四肢(两帧交替爬)
45
+ const cr = st % 2, parts = [];
46
+ const B = (q) => [x + q[0] * s, y + q[1] * s];
47
+ parts.push(RIG.limb(B([-40, -10]), B([40, -20]), 70 * s, 60 * s));
48
+ parts.push(RIG.limb(B([-34, 0]), B([-60 + cr * 14, 50]), 26 * s, 24 * s), RIG.limb(B([30, -8]), B([60 - cr * 14, 50]), 26 * s, 24 * s));
49
+ const hd = new Path2D(), hc = B([-82, -40]); hd.arc(hc[0], hc[1], 40 * s, 0, TAU); parts.push(hd);
50
+ c.save(); c.strokeStyle = C.ink; c.lineWidth = 22; parts.forEach(q => c.stroke(q)); c.fillStyle = C.white; parts.forEach(q => c.fill(q)); c.restore();
51
+ }
52
+ function heart(c, x, y, s, t) {
53
+ const b = 1 + 0.15 * bounce(t, 0.5), p = new Path2D();
54
+ p.moveTo(x, y + 40 * s * b); p.bezierCurveTo(x - 70 * s * b, y - 10 * s * b, x - 40 * s * b, y - 60 * s * b, x, y - 28 * s * b); p.bezierCurveTo(x + 40 * s * b, y - 60 * s * b, x + 70 * s * b, y - 10 * s * b, x, y + 40 * s * b); p.closePath();
55
+ ink(c, p, C.red, 11); action(c, x, y - 6, 78 * s, 70 * s, 10, t, 5, 30, 8);
56
+ }
57
+
58
+ return {
59
+ draw(c, lt, t) {
60
+ const ch = P.choreo(lt, t);
61
+ const bb = bounce(t), bob = -bb * 12;
62
+ // 底色
63
+ c.fillStyle = C.yel; c.fillRect(0, 0, W, 702); c.fillStyle = C.green; c.fillRect(0, 702, W, H - 702);
64
+ c.strokeStyle = C.ink; c.lineWidth = 14; c.beginPath(); c.moveTo(0, 702); c.lineTo(W, 702); c.stroke();
65
+ // 窗:黑粗框、蓝底、光芒宝宝;整个窗随拍子轻摇
66
+ c.save(); c.translate(580, 560); c.rotate(Math.sin(t * BEAT * Math.PI) * 0.025); c.translate(-580, -560);
67
+ const win = new Path2D(); win.rect(350, 122, 460, 436); ink(c, win, C.blue, 16);
68
+ c.strokeStyle = C.ink; c.lineWidth = 10; c.beginPath(); c.moveTo(580, 122); c.lineTo(580, 250); c.stroke();
69
+ c.save(); c.clip(win); radiantBaby(c, 610, 430 + bob * 0.5, 1.05, t); c.restore();
70
+ const sill = new Path2D(); sill.rect(326, 556, 508, 30); ink(c, sill, C.red, 12);
71
+ c.restore();
72
+ // 墙上的跳舞小人
73
+ dancer(c, 175, 660, 0.95, C.red, t, 0);
74
+ dancer(c, 1000, 520, 0.62, C.blue, t, 2, -1);
75
+ dancer(c, 1720, 680, 1.05, C.pink, t, 1, -1);
76
+ action(c, 1720, 520, 130, 160, 8, t, 9, 30, 8);
77
+ heart(c, 1000, 210, 1.0, t);
78
+ // 桌:挤压拉伸(以桌脚着地为轴)
79
+ const sy = 1 - 0.07 * (1 - bb), sx = 1 + 0.05 * (1 - bb);
80
+ c.save(); c.translate(1008, 904); c.scale(sx, sy); c.translate(-1008, -904);
81
+ [[846, 640], [1150, 640]].forEach(([x, y], k) => { const leg = new Path2D(); leg.moveTo(x, y); leg.quadraticCurveTo(x + (k ? 18 : -18) * (1 - bb), 780, x + 6, 904); c.strokeStyle = C.ink; c.lineWidth = 36; c.lineCap = 'round'; c.stroke(leg); c.strokeStyle = C.red; c.lineWidth = 12; c.stroke(leg); });
82
+ const top = new Path2D(); top.rect(806, 616, 404, 34); ink(c, top, C.red, 13);
83
+ // 茶壶在桌上跳
84
+ const jy = -Math.max(0, Math.sin((t * BEAT + 0.25) * Math.PI)) * 26;
85
+ const pot = RIG.smooth([[870, 616], [858, 586], [874, 556], [912, 548], [948, 556], [962, 588], [952, 616]].map(([x, y]) => [x, y + jy]), true, 0.5);
86
+ const sp = new Path2D(); sp.moveTo(864, 592 + jy); sp.lineTo(830, 562 + jy); ink(c, sp, null, 13); c.strokeStyle = C.white; c.lineWidth = 3; ink(c, pot, C.white, 12);
87
+ c.restore();
88
+ action(c, 910, 584 + jy, 70, 46, 6, t, 3, 22, 7);
89
+ // 地上影子小横线(哈林的「跳起来」标志)
90
+ c.strokeStyle = C.ink; c.lineWidth = 9; c.lineCap = 'round'; [[480, 932], [620, 932], [1200, 948], [1360, 948]].forEach(([x, y], k) => { const L = 30 + 30 * (1 - bb); c.beginPath(); c.moveTo(x - L, y); c.lineTo(x + L, y); c.stroke(); });
91
+ // 猫(橘白、粗黑线、跟着拍子颠;眼睛是两个黑点)
92
+ const K = RIG.cat({ tail: ch.tail * 1.6, blink: ch.blink, breathe: ch.breathe, dy: -bounce(t, 0.5) * 16 }) // RIG.limb 端帽已修,粗描边下腿关节不再露洞;
93
+ RIG.drawCat(c, K, { line: C.ink, lw: LW, orange: C.org, white: C.white, stripe: C.ink, stripeW: 8, earInner: C.pink,
94
+ tail: (c, K) => { c.lineCap = 'round'; c.strokeStyle = C.ink; c.lineWidth = K.tailW + LW * 2; c.stroke(K.tail); c.strokeStyle = C.org; c.lineWidth = K.tailW; c.stroke(K.tail); },
95
+ face: (c, K) => { K.eyes.forEach(e => { c.fillStyle = C.ink; c.beginPath(); c.arc(e.x, e.y, K.blink ? 3 : 8, 0, TAU); c.fill(); }); c.fillStyle = C.pink; c.beginPath(); c.arc(K.nose[0], K.nose[1], 7, 0, TAU); c.fill(); } });
96
+ action(c, 540, 760 - bounce(t, 0.5) * 16, 190, 200, 10, t, 7, 34, 9);
97
+ // 少女:80 年代纽约少女——宽大红毛衣+牛仔蓝长裙+白高帮球鞋;颠头;只留眼点和唇
98
+ const G = RIG.girl({ cup: ch.cup, sip: ch.sip, hair: 'long', breathe: ch.breathe, dy: bob });
99
+ RIG.drawGirl(c, G, { line: C.ink, lw: LW, lwOf: { hand: 9, farHand: 9, face: 10, neck: 9, cuff: 10, farCuff: 10 }, locks: false, skin: C.skin, hair: C.yel, dress: C.red, skirt: C.blue, sleeve: C.red, cuff: C.red, shoe: C.white, cupBody: C.white, cupRim: C.ink,
100
+ features: (c, G) => { c.fillStyle = C.ink; c.beginPath(); c.arc(G.eye.x, G.eye.y, ch.blink ? 2.5 : 6, 0, TAU); c.fill(); c.strokeStyle = C.red; c.lineWidth = 6; c.lineCap = 'round'; c.beginPath(); c.moveTo(...G.lips[0]); c.lineTo(...G.lips[2]); c.stroke(); },
101
+ hooks: { torso: (c, G) => { c.strokeStyle = C.ink; c.lineWidth = 8; const w = G.A.waist; c.beginPath(); c.moveTo(w[0] - 90, w[1] - 30); c.lineTo(w[0] + 60, w[1] - 26); c.stroke(); }, // 毛衣下摆罗纹
102
+ } });
103
+ action(c, G.A.headC[0] + 10, G.A.headC[1], 110, 110, 9, t, 11, 30, 9);
104
+ // 杯子上的放射线+热气(哈林式波浪线)
105
+ action(c, G.cup.x, G.cup.y + 14, 48, 40, 7, t, 13, 24, 7);
106
+ c.save(); c.strokeStyle = C.ink; c.lineWidth = 8; c.lineCap = 'round'; [0, 1].forEach(k => { c.beginPath(); for (let i = 0; i <= 10; i++) { const q = i / 10, x = G.cup.x - 10 + k * 20 + Math.sin(q * 8 + t * 14 + k) * 8, y = G.cup.y - 24 - q * 60; i ? c.lineTo(x, y) : c.moveTo(x, y); } c.stroke(); }); c.restore();
107
+ },
108
+ };
109
+ })();
@@ -0,0 +1,217 @@
1
+ // 1642 伦勃朗明暗法 —— 纯代码。
2
+ // 管线:①亮态底稿(按「全亮」时的颜色画房间、桌毯、窗)→ ②褐色笔触重画(静态缓存)→ ③角色平涂+体积渐变+细笔触
3
+ // → ④光照图 multiply(环境≈0.13,窗光池/人脸/猫/桌面/烛光,缓慢摇曳)→ ⑤厚涂高光(凸起:暗影偏移+主笔+亮脊)
4
+ // → ⑥光柱浮尘、烛焰、热气 → ⑦金褐釉+龟裂+暗角+颗粒
5
+ SCENES['32_rembrandt'] = (() => {
6
+ const W = 1920, H = 1080, { clamp, lerp, rng } = U, P = PAINT;
7
+ const WIN = [350, 120, 810, 560];
8
+ const flick = (t, k = 0) => 0.5 * P.noise(t * 2.2 + k * 7.3, k * 3.1) + 0.25 * P.noise(t * 6.5 + k, 9 + k); // 约 ±0.35
9
+
10
+ // ---------- ① 亮态底稿(静态) ----------
11
+ function room(g) {
12
+ // 墙:暖褐,向右变深
13
+ const wg = g.createLinearGradient(0, 0, W, 0); wg.addColorStop(0, '#6a4a2a'); wg.addColorStop(0.5, '#4a321c'); wg.addColorStop(1, '#2a1a0e');
14
+ g.fillStyle = wg; g.fillRect(0, 0, W, H);
15
+ // 地面
16
+ g.fillStyle = '#3a2614'; g.fillRect(0, 880, W, 200);
17
+ // 窗洞(厚墙斜面)
18
+ g.fillStyle = '#8a6a44'; g.beginPath(); g.moveTo(322, 96); g.lineTo(838, 96); g.lineTo(810, 120); g.lineTo(350, 120); g.closePath(); g.fill();
19
+ g.fillStyle = '#9a7a50'; g.beginPath(); g.moveTo(322, 96); g.lineTo(350, 120); g.lineTo(350, 560); g.lineTo(322, 590); g.closePath(); g.fill();
20
+ g.fillStyle = '#5a3e22'; g.beginPath(); g.moveTo(838, 96); g.lineTo(810, 120); g.lineTo(810, 560); g.lineTo(838, 590); g.closePath(); g.fill();
21
+ // 玻璃:铅条菱格+牛眼玻璃
22
+ g.save(); g.beginPath(); g.rect(350, 120, 460, 440); g.clip();
23
+ const gg = g.createRadialGradient(560, 300, 20, 580, 340, 360); gg.addColorStop(0, '#fff6dc'); gg.addColorStop(0.6, '#f0d8a0'); gg.addColorStop(1, '#c49a5a');
24
+ g.fillStyle = gg; g.fillRect(350, 120, 460, 440);
25
+ const r = rng(42);
26
+ for (let i = 0; i < 70; i++) { const x = 350 + r() * 460, y = 120 + r() * 440, rr = 6 + r() * 14; g.fillStyle = `rgba(${r() < .5 ? '255,250,230' : '190,150,90'},${0.15 + r() * 0.2})`; g.beginPath(); g.arc(x, y, rr, 0, Math.PI * 2); g.fill(); }
27
+ g.strokeStyle = '#3a2a1a'; g.lineWidth = 3;
28
+ for (let k = -12; k < 16; k++) { g.beginPath(); g.moveTo(350 + k * 46, 120); g.lineTo(350 + k * 46 + 440 * 0.55, 560); g.stroke(); g.beginPath(); g.moveTo(350 + k * 46, 560); g.lineTo(350 + k * 46 + 440 * 0.55, 120); g.stroke(); }
29
+ g.restore();
30
+ // 窗框与中梃、横档
31
+ g.fillStyle = '#2a1a0e'; g.fillRect(350, 120, 460, 12); g.fillRect(350, 548, 460, 12); g.fillRect(350, 120, 12, 440); g.fillRect(798, 120, 12, 440);
32
+ g.fillRect(573, 120, 14, 440); g.fillRect(350, 330, 460, 12);
33
+ // 窗台
34
+ g.fillStyle = '#7a5a36'; g.fillRect(310, 560, 540, 24); g.fillStyle = '#4a321c'; g.fillRect(310, 584, 540, 14);
35
+ // 右侧重帷幕(深红褐,大褶)
36
+ const cg = g.createLinearGradient(1500, 0, 1920, 0); cg.addColorStop(0, '#4a1a10'); cg.addColorStop(1, '#2a0c06'); g.fillStyle = cg;
37
+ g.beginPath(); g.moveTo(1560, 0); g.bezierCurveTo(1600, 300, 1540, 600, 1600, 1080); g.lineTo(1920, 1080); g.lineTo(1920, 0); g.closePath(); g.fill();
38
+ g.strokeStyle = 'rgba(120,50,30,.6)'; g.lineWidth = 10; [1640, 1720, 1810].forEach((x, k) => { g.beginPath(); g.moveTo(x, 0); g.bezierCurveTo(x + 30, 300, x - 30, 650, x + 10 * k, 1080); g.stroke(); });
39
+ // 椅背(少女身后,深色雕花)
40
+ g.fillStyle = '#2e1c0e'; g.fillRect(1428, 450, 22, 470); g.fillRect(1476, 460, 20, 460); g.fillRect(1424, 440, 80, 24);
41
+ g.fillStyle = '#6a3a22'; g.fillRect(1440, 500, 50, 180);
42
+ }
43
+ function table(g) {
44
+ // 桌:土耳其桌毯盖住,垂到地面,金色流苏
45
+ g.fillStyle = '#7a2a18'; g.beginPath(); g.moveTo(800, 618); g.lineTo(1215, 618); g.lineTo(1240, 880); g.quadraticCurveTo(1020, 900, 790, 885); g.closePath(); g.fill();
46
+ g.fillStyle = '#9a3a1e'; g.fillRect(800, 610, 415, 22);
47
+ // 毯面纹样:菱形徽章+边带
48
+ g.strokeStyle = '#c8943a'; g.lineWidth = 6; g.beginPath(); g.moveTo(805, 660); g.lineTo(1220, 660); g.stroke(); g.beginPath(); g.moveTo(800, 840); g.lineTo(1236, 840); g.stroke();
49
+ g.fillStyle = '#2a3a5a'; [[900, 750], [1020, 755], [1140, 750]].forEach(([x, y]) => { g.beginPath(); g.moveTo(x, y - 60); g.lineTo(x + 45, y); g.lineTo(x, y + 60); g.lineTo(x - 45, y); g.closePath(); g.fill(); });
50
+ g.fillStyle = '#c8943a'; [[900, 750], [1020, 755], [1140, 750]].forEach(([x, y]) => { g.beginPath(); g.moveTo(x, y - 26); g.lineTo(x + 20, y); g.lineTo(x, y + 26); g.lineTo(x - 20, y); g.closePath(); g.fill(); });
51
+ g.strokeStyle = '#d0a050'; g.lineWidth = 3; for (let x = 795; x < 1238; x += 9) { g.beginPath(); g.moveTo(x, 884); g.lineTo(x + 2, 902); g.stroke(); }
52
+ }
53
+ function tableProps(g) {
54
+ // 桌上:打开的书、锡壶、烛台
55
+ g.fillStyle = '#e8d6aa'; g.beginPath(); g.moveTo(940, 612); g.lineTo(1010, 598); g.lineTo(1080, 612); g.lineTo(1010, 624); g.closePath(); g.fill();
56
+ g.strokeStyle = '#8a6a3a'; g.lineWidth = 2; g.beginPath(); g.moveTo(1010, 598); g.lineTo(1010, 624); g.stroke();
57
+ g.fillStyle = '#7a7466'; g.beginPath(); g.moveTo(1100, 610); g.quadraticCurveTo(1090, 560, 1108, 530); g.lineTo(1132, 530); g.quadraticCurveTo(1150, 560, 1140, 610); g.closePath(); g.fill();
58
+ g.strokeStyle = '#7a7466'; g.lineWidth = 7; g.beginPath(); g.arc(1150, 565, 18, -1.3, 1.3); g.stroke();
59
+ // 烛台(黄铜)+蜡烛
60
+ g.fillStyle = '#9a7432'; g.beginPath(); g.ellipse(870, 612, 36, 9, 0, 0, Math.PI * 2); g.fill(); g.fillRect(864, 560, 12, 52); g.beginPath(); g.ellipse(870, 560, 20, 5, 0, 0, Math.PI * 2); g.fill();
61
+ g.fillStyle = '#efe2c0'; g.fillRect(861, 500, 18, 60);
62
+ }
63
+ // ---------- ② 褐色笔触重画(静态,缓存) ----------
64
+ const SW = {
65
+ wall: P.swatch(['#5a3c20', '#6e4a28', '#4e3418', '#6a4a28', '#4a3018', '#765030'], 0.7),
66
+ dark: P.swatch(['#2a1a0c', '#3a2412', '#1e1208', '#4a2e16'], 0.5),
67
+ rug: P.swatch(['#8a2e18', '#6a2010', '#a8441e', '#c8943a', '#2a3a5a'], 0.7),
68
+ glass: P.swatch(['#fff2d0', '#f2d8a0', '#e8c888', '#fff8e8'], 0.6),
69
+ };
70
+ const stat = () => P.cached('rb_static', W, H, (g, cv) => {
71
+ const b = P.canvas(), bg = b.getContext('2d'); room(bg);
72
+ g.drawImage(b, 0, 0);
73
+ // 背景:宽松的褐色笔触(伦勃朗背景的刷痕),再整体轻模糊成「揉开」的底子
74
+ P.strokes(g, b, { cell: 18, len: 54, width: 16, seed: 3, t: 0, boil: 0, outline: 0, jitterCol: 6,
75
+ angle: (x, y) => -0.9 + 1.6 * P.noise(x * 0.003, y * 0.003),
76
+ palette: (x, y, col, r) => (x > 1520 || y > 880 ? SW.dark(col, r) : SW.wall(col, r)),
77
+ mask: (x, y) => !(x > 300 && x < 860 && y > 80 && y < 610) });
78
+ { const tmp = P.canvas(), tg = tmp.getContext('2d'); tg.filter = 'blur(2.2px)'; tg.drawImage(cv, 0, 0); g.globalAlpha = 0.75; g.drawImage(tmp, 0, 0); g.globalAlpha = 1; }
79
+ g.drawImage(b, 300, 80, 560, 530, 300, 80, 560, 530); // 窗与窗台保留清楚的木作
80
+ // 桌毯:单独一层、单独更细的笔触(小物件不被大笔触打碎),再放桌上小物
81
+ const tb = P.canvas(), tbg = tb.getContext('2d'); table(tbg);
82
+ tbg.save(); tbg.globalCompositeOperation = 'source-atop';
83
+ P.strokes(tbg, tb, { cell: 8, len: 16, width: 6, seed: 5, t: 0, boil: 0, outline: 0.25, outlineCol: [30, 10, 5], outlineMix: 0.6, jitterCol: 14, alphaMask: true, angle: (x, y) => y > 840 ? Math.PI / 2 : 0.1 * P.noise(x * 0.02, y * 0.02) });
84
+ tbg.restore(); tableProps(tbg); g.drawImage(tb, 0, 0);
85
+ });
86
+
87
+ // ---------- 光照图(每帧) ----------
88
+ const pool = P.pool; // 光池(径向渐变)
89
+ const beam = () => P.cached('rb_beam', W, H, (g) => {
90
+ g.filter = 'blur(40px)'; g.fillStyle = 'rgba(255,232,190,0.55)';
91
+ g.beginPath(); g.moveTo(360, 130); g.lineTo(800, 130); g.lineTo(1500, 760); g.lineTo(1300, 1000); g.lineTo(380, 1000); g.lineTo(340, 560); g.closePath(); g.fill();
92
+ g.filter = 'none';
93
+ });
94
+ const cloudAt = t => 0.5 + 0.5 * Math.sin(t * 2.2 - 0.6); // 云影:窗光慢慢暗下去又回来
95
+ // 光照图:环境光 rgb(30,21,13) 打底,lighter 叠光池(P.lightMap),之后整幅 multiply(P.applyLight)
96
+ function lightMap(t, G, K) {
97
+ const f = flick(t), fc = flick(t * 1.7, 3);
98
+ return P.lightMap('rembrandt', 'rgb(30,21,13)', g => {
99
+ const cloud = cloudAt(t);
100
+ g.globalAlpha = (0.8 + 0.15 * f) * (1 - 0.35 * cloud); g.drawImage(beam(), 0, 0); g.globalAlpha = 1;
101
+ pool(g, 580, 340, 330, '255,246,225', 1 - 0.3 * cloud); // 窗本身
102
+ pool(g, 1470, 400, 300, '200,150,90', 0.35); // 背后墙面的分离光(暗侧亮墙)
103
+ const dx = 14 * P.noise(t * 0.9, 1), dy = 10 * P.noise(t * 0.9, 5); // 光池缓慢漂移(云影/烛焰摇曳)
104
+ pool(g, G.A.headC[0] - 30 + dx, G.A.headC[1] + 10 + dy, 250, '255,226,180', 0.95 + 0.08 * f);
105
+ pool(g, G.A.chest[0] - 40 + dx, G.A.chest[1] + 40, 230, '255,214,160', 0.55);
106
+ pool(g, K.headC[0] - 10, K.headC[1] + 90 + dy, 240, '255,220,170', 0.8 + 0.08 * f);
107
+ pool(g, 1010, 640 + dy, 280, '255,214,160', 0.6);
108
+ pool(g, 870, 490, 190 + 40 * fc, '255,180,90', 0.6 + 0.4 * fc); // 烛光(快闪)
109
+ });
110
+ }
111
+ // ---------- 厚涂:凸起的颜料(暗影偏移+主笔+亮脊) ----------
112
+ // 第一版「暗影+主笔+亮脊」三条整齐的线读成贴纸。改成鬃毛笔:一笔 = 若干条平行细鬃(各自明暗抖动、两端随机缩短),
113
+ // 下侧一条很淡的投影,上侧零星亮点——读成「一坨被刷开的厚颜料」。
114
+ const impasto = P.impasto; // 厚涂鬃毛笔
115
+
116
+ const girlPal = { skin: '#eccaa0', hair: '#b8873a', hairLine: '#6a4818', dress: '#3a2014', sleeve: '#5c2c18', fold: '#24120a', cuff: '#efe6d0',
117
+ cheek: 'rgba(220,120,90,.35)', lip: '#b8524a', iris: '#4a3a2a', line: '#3a2010', lw: 0, cupBody: '#e6e4dc', cupRim: '#6a4a28', shoe: '#1a0e06', browC: '#9a6a2a' };
118
+ const catPal = { orange: '#c47a34', white: '#eedcb8', stripe: '#8a4a18', line: '#2a160a', lw: 0, eye: '#b89a3a', earInner: '#c88a70', noseC: '#b86a5a', whisker: '#efe0c0' };
119
+
120
+ return {
121
+ draw(c, lt, t) {
122
+ const ch = P.choreo(lt, t);
123
+ const G = RIG.girl({ cup: ch.cup, sip: ch.sip, breathe: ch.breathe, hair: 'bun' });
124
+ const K = RIG.cat({ tail: ch.tail, blink: ch.blink, breathe: ch.breathe });
125
+ // ① ② 静态底
126
+ const S = P.scratch('rbScene'), sg = S.getContext('2d'); sg.reset(); sg.drawImage(stat(), 0, 0);
127
+ // ③ 角色层
128
+ const L = P.scratch('rbChars'), lg = L.getContext('2d'); lg.reset();
129
+ RIG.drawCat(lg, K, catPal);
130
+ RIG.drawGirl(lg, G, { ...girlPal, mode: 'fill' });
131
+ // 服饰:宽白领(平摊在肩上的花边大领)、白亚麻小帽、珍珠耳坠
132
+ lg.save(); lg.lineJoin = 'round';
133
+ const n = G.A.neck, ch_ = G.A.chest, bk = G.A.back;
134
+ lg.fillStyle = '#f0e8d4';
135
+ const collar = [[n[0] - 22, n[1] - 8], [n[0] - 30, n[1] + 22], [ch_[0] - 14, ch_[1] - 4], [n[0] + 10, ch_[1] + 16], [n[0] + 52, ch_[1] + 6], [bk[0] - 26, n[1] + 52], [n[0] + 46, n[1] - 4], [n[0] + 10, n[1] - 14]];
136
+ lg.fill(RIG.smooth(collar));
137
+ lg.fillStyle = '#f6f0e2'; for (let k = 0; k < 11; k++) { const q = k / 10, x = lerp(ch_[0] - 14, bk[0] - 26, q), y = lerp(ch_[1] - 4, n[1] + 52, q) + Math.sin(q * Math.PI) * 22; lg.beginPath(); lg.arc(x, y, 7, 0, Math.PI * 2); lg.fill(); }
138
+ lg.strokeStyle = 'rgba(150,130,100,.7)'; lg.lineWidth = 1.5; lg.setLineDash([2, 4]); lg.stroke(RIG.smooth(collar.map(p => [lerp(p[0], n[0] + 14, 0.25), lerp(p[1], n[1] + 30, 0.25)]))); lg.setLineDash([]);
139
+ const hc = G.A.headC;
140
+ lg.fillStyle = '#efe6d0'; for (let k = 0; k < 9; k++) { const a = -2.2 + k * 0.32; lg.beginPath(); lg.arc(G.bunSpiral[0] + Math.cos(a) * 27, G.bunSpiral[1] + Math.sin(a) * 27, 4.5, 0, Math.PI * 2); lg.fill(); }
141
+ lg.fillStyle = '#f6f0e0'; lg.beginPath(); lg.arc(G.ear[0] - 2, G.ear[1] + 24, 5, 0, Math.PI * 2); lg.fill();
142
+ lg.restore();
143
+ // 体积:左上受光 → 右下沉入暗部(source-atop 只染在角色上)
144
+ lg.save(); lg.globalCompositeOperation = 'source-atop';
145
+ let vg = lg.createLinearGradient(1180, 300, 1480, 760); vg.addColorStop(0, 'rgba(255,240,200,.12)'); vg.addColorStop(0.5, 'rgba(30,14,6,.15)'); vg.addColorStop(1, 'rgba(10,5,2,.7)'); lg.fillStyle = vg; lg.fillRect(1060, 200, 460, 760);
146
+ vg = lg.createLinearGradient(420, 600, 680, 900); vg.addColorStop(0, 'rgba(255,240,200,.1)'); vg.addColorStop(1, 'rgba(20,10,4,.55)'); lg.fillStyle = vg; lg.fillRect(380, 520, 360, 400);
147
+ // 裙子厚褶:几道深浅交替的宽笔
148
+ lg.lineCap = 'round'; G.folds.forEach((f, i) => { lg.strokeStyle = 'rgba(10,4,2,.5)'; lg.lineWidth = 16; lg.stroke(f); lg.strokeStyle = 'rgba(120,70,40,.25)'; lg.lineWidth = 6; lg.save(); lg.translate(-12, 0); lg.stroke(f); lg.restore(); });
149
+ // 细笔触肌理(不沸腾:古典油画)
150
+ P.strokes(lg, L, { cell: 6, len: 13, width: 5, seed: 8, t: 0, boil: 0, outline: 0, jitterCol: 9, alphaMask: true,
151
+ angle: (x, y) => x < 760 ? Math.atan2(y - 780, x - 540) + Math.PI / 2 : 1.2 + 0.4 * P.noise(x * 0.01, y * 0.01) });
152
+ lg.restore();
153
+ // 脸再盖一层平滑(伦勃朗的脸是揉开的,不留碎笔)+眼睛
154
+ lg.save(); lg.clip(G.face); const fg = lg.createRadialGradient(G.A.forehead[0], G.A.forehead[1] + 20, 5, G.A.headC[0], G.A.headC[1], 90);
155
+ fg.addColorStop(0, '#f6dcb6'); fg.addColorStop(0.6, '#d8a87a'); fg.addColorStop(1, '#7a4a2a'); lg.fillStyle = fg; lg.fill(G.face); lg.restore();
156
+ // 面部塑形:脸后半(靠耳)沉入暖褐半调;眼窝一抹阴影;颧骨一块三角受光(伦勃朗光的小三角)
157
+ lg.save(); lg.clip(G.face);
158
+ let sg2 = lg.createLinearGradient(G.A.forehead[0], 0, G.ear[0] + 10, 0); sg2.addColorStop(0, 'rgba(0,0,0,0)'); sg2.addColorStop(0.5, 'rgba(120,60,25,.25)'); sg2.addColorStop(1, 'rgba(70,32,12,.75)'); lg.fillStyle = sg2; lg.fill(G.face);
159
+ lg.fillStyle = 'rgba(110,55,25,.35)'; lg.beginPath(); lg.ellipse(G.eye.x + 4, G.eye.y - 2, 13, 8, 0.2, 0, 7); lg.fill();
160
+ lg.fillStyle = 'rgba(255,232,190,.55)'; lg.beginPath(); lg.moveTo(G.eye.x - 4, G.eye.y + 10); lg.lineTo(G.eye.x + 20, G.eye.y + 12); lg.lineTo(G.eye.x + 4, G.eye.y + 34); lg.closePath(); lg.fill();
161
+ lg.restore();
162
+ lg.fillStyle = '#c48a70'; lg.beginPath(); lg.moveTo(...G.lips[0]); lg.lineTo(...G.lips[1]); lg.lineTo(...G.lips[2]); lg.fill();
163
+ lg.strokeStyle = '#4a2a14'; lg.lineWidth = 2.6; lg.lineCap = 'round'; lg.stroke(G.lid); lg.strokeStyle = '#8a5a2a'; lg.lineWidth = 2; lg.stroke(G.brow);
164
+ if (!ch.blink) { lg.fillStyle = '#2a1a0e'; lg.beginPath(); lg.arc(G.eye.x - 1, G.eye.y + 1.5, 4, 0, Math.PI * 2); lg.fill(); }
165
+ // 刘海重新盖在脸上
166
+ lg.fillStyle = girlPal.hair; lg.fill(G.bangs); lg.strokeStyle = 'rgba(120,80,30,.6)'; lg.lineWidth = 2; G.hairLines.forEach(h => lg.stroke(h));
167
+ // 杯与手重新盖上(白瓷杯+青花一圈)
168
+ lg.fillStyle = '#e2b890'; lg.fill(G.hand);
169
+ RIG.drawCup(lg, G.cup, { body: '#e8e6de', rim: '#5a3a1c', hw: 6 });
170
+ lg.save(); lg.translate(G.cup.x, G.cup.y); lg.rotate(G.cup.tilt); lg.strokeStyle = '#3a4a8a'; lg.lineWidth = 3; lg.beginPath(); lg.moveTo(-G.cup.w * 0.46, G.cup.h * 0.45); lg.quadraticCurveTo(0, G.cup.h * 0.6, G.cup.w * 0.46, G.cup.h * 0.45); lg.stroke(); lg.restore();
171
+ lg.save(); lg.translate(G.cup.x, G.cup.y); lg.rotate(G.cup.tilt); lg.fillStyle = '#d9ad82';
172
+ for (let k = 0; k < 3; k++) { lg.beginPath(); lg.ellipse(G.cup.w * 0.42, G.cup.h * (0.3 + k * 0.22), 9, 5.5, 0.2, 0, 7); lg.fill(); } lg.restore();
173
+ lg.fillStyle = '#e2b890'; lg.beginPath(); lg.arc(G.thumb[0], G.thumb[1], 7, 0, Math.PI * 2); lg.fill();
174
+ // 「失边」:角色先糊一遍整层(暗侧会和背景融成一片),再叠 0.6 清晰层留住五官
175
+ sg.save(); sg.filter = 'blur(2.4px)'; sg.drawImage(L, 0, 0); sg.restore(); sg.globalAlpha = 0.62; sg.drawImage(L, 0, 0); sg.globalAlpha = 1;
176
+ // ④ 光照 multiply
177
+ P.applyLight(sg, lightMap(t, G, K));
178
+ // 窗玻璃本身是光源:再亮一点(screen)
179
+ sg.save(); sg.globalCompositeOperation = 'screen'; sg.globalAlpha = 0.35 + 0.08 * flick(t); sg.drawImage(stat(), 352, 122, 456, 436, 352, 122, 456, 436); sg.restore();
180
+ c.drawImage(S, 0, 0);
181
+ c.save(); c.beginPath(); c.rect(352, 122, 456, 436); c.clip();
182
+ const cx_ = 200 + ((t * 260) % 900); const cgr = c.createRadialGradient(cx_, 300, 20, cx_, 300, 260); cgr.addColorStop(0, 'rgba(60,40,20,.35)'); cgr.addColorStop(1, 'rgba(60,40,20,0)');
183
+ c.fillStyle = cgr; c.fillRect(352, 122, 456, 436); c.restore();
184
+ // ⑤ 厚涂高光(跟着锚点走)
185
+ const fh = G.A.forehead, hd = G.hand, cp = G.cup;
186
+ [[0, -14, 40, -26], [14, -2, 56, -12], [40, 6, 84, 2]].forEach(([a, b, c2, d]) => impasto(c, [[fh[0] + a, fh[1] + b], [fh[0] + (a + c2) / 2, fh[1] + (b + d) / 2 - 4], [fh[0] + c2, fh[1] + d]], 7, '#f0c870', 'rgba(255,240,190,.8)')); // 金发受光
187
+ impasto(c, [[G.cheek[0] - 22, G.cheek[1] - 10], [G.cheek[0] - 12, G.cheek[1] - 14]], 4, '#eecba0', 'rgba(255,240,210,.5)');
188
+ for (let k = 0; k < 6; k++) { const q = k / 5, x = lerp(n[0] - 26, ch_[0] - 12, q) + 6, y = lerp(n[1] + 6, ch_[1] - 6, q); impasto(c, [[x, y], [x + 9, y + 3]], 4.5, 'rgba(246,238,218,.9)', 'rgba(255,255,245,.45)'); } // 领口受光边:一串短厚笔
189
+ impasto(c, [[cp.x - cp.w * 0.28, cp.y + 6], [cp.x - cp.w * 0.3, cp.y + cp.h * 0.6]], 3.5, '#fbf8f0'); // 瓷杯反光
190
+ impasto(c, [[G.ear[0] - 3, G.ear[1] + 22], [G.ear[0] - 2, G.ear[1] + 23]], 3.5, '#ffffff'); // 珍珠
191
+ for (let k = 0; k < 9; k += 2) { const a = -2.2 + k * 0.32; const px = G.bunSpiral[0] + Math.cos(a) * 27, py = G.bunSpiral[1] + Math.sin(a) * 27; impasto(c, [[px - 1, py - 1], [px, py - 1.5]], 3, '#fff8e8'); }
192
+ const fur = U.rng(23); for (let k = 0; k < 9; k++) { const x = 572 + fur() * 40, y = 706 + k * 12; impasto(c, [[x, y], [x + 4, y + 8]], 4, '#ecdcba', 'rgba(255,248,226,.55)'); } // 猫白胸短毛
193
+ impasto(c, [[K.nose[0] - 28, K.nose[1] + 8], [K.nose[0] - 16, K.nose[1] + 12]], 4, '#efe0c0', 'rgba(255,248,226,.55)');
194
+ for (let k = 0; k < 5; k++) impasto(c, [[572 + k * 14, 590 - (k % 2) * 4], [580 + k * 14, 598]], 4, '#d88a40', 'rgba(255,214,150,.6)'); // 猫头顶橘毛
195
+ if (!ch.blink) K.eyes.forEach(e => { c.fillStyle = 'rgba(255,248,220,.95)'; c.beginPath(); c.arc(e.x - 2, e.y - 3, 2.6, 0, Math.PI * 2); c.fill(); });
196
+ impasto(c, [[1030, 610], [1060, 606]], 5, '#f6e8c0'); impasto(c, [[1114, 545], [1111, 588]], 4, '#a8a498', 'rgba(240,236,220,.6)'); // 书页、锡壶
197
+ // ⑥ 会动:烛焰(摇曳)、光柱浮尘、热气
198
+ const fc = flick(t * 1.7, 3), fx = 870 + 5 * P.noise(t * 5, 2), lean = 6 * P.noise(t * 3, 8);
199
+ c.save(); c.globalCompositeOperation = 'lighter';
200
+ pool(c, fx, 470, 70 + 12 * fc, '255,170,70', 0.45 + 0.2 * fc);
201
+ c.fillStyle = '#ffd890'; c.beginPath(); c.moveTo(fx - 8, 498); c.quadraticCurveTo(fx - 10, 478, fx + lean, 452 - 8 * fc); c.quadraticCurveTo(fx + 10, 478, fx + 8, 498); c.closePath(); c.fill();
202
+ c.fillStyle = '#fff6d8'; c.beginPath(); c.ellipse(fx + lean * 0.3, 488, 4, 9, 0, 0, Math.PI * 2); c.fill();
203
+ const dust = P.particles(70, 17, t * 0.35, { x0: 420, x1: 1300, y0: 980, y1: 160, speed: 40, drift: 26, life: 9 });
204
+ dust.forEach((d, i) => { const along = (d.x - 380) / 1000, inBeam = d.y > 130 + along * 520 - 120 && d.y < 560 + along * 520; if (!inBeam) return;
205
+ const tw = 0.5 + 0.5 * Math.sin(t * 4 + i * 2.1); c.fillStyle = `rgba(255,236,190,${0.25 + 0.5 * tw})`; c.beginPath(); c.arc(d.x, d.y, 1.4 + d.s * 1.6, 0, Math.PI * 2); c.fill(); });
206
+ c.restore();
207
+ P.steam(c, t, cp.x, cp.y - 6, { h: 60, n: 2, color: 'rgba(255,236,200,.5)', width: 3, spread: 12, wobble: 8 });
208
+ // ⑦ 罩层:金褐釉、龟裂、暗角、颗粒(缓存)
209
+ c.drawImage(P.cached('rb_over', W, H, (g) => {
210
+ const vg2 = g.createRadialGradient(900, 520, 300, 960, 540, 1150); vg2.addColorStop(0, 'rgba(10,5,0,0)'); vg2.addColorStop(1, 'rgba(8,4,0,.75)'); g.fillStyle = vg2; g.fillRect(0, 0, W, H);
211
+ g.globalAlpha = 0.28; g.drawImage(P.craquelure('rb', 0.28), 0, 0); g.globalAlpha = 1;
212
+ g.drawImage(P.grain('rbg', 0.05, [30, 15, 5], 0.12), 0, 0);
213
+ }), 0, 0);
214
+ c.save(); c.globalCompositeOperation = 'soft-light'; c.fillStyle = 'rgba(200,140,60,.35)'; c.fillRect(0, 0, W, H); c.restore();
215
+ },
216
+ };
217
+ })();