@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,197 @@
1
+ // 2016 新海诚光影 —— 纯代码。
2
+ // 管线:①天空(极透明的蓝渐变+积雨云:逐团球体打光+暖色受光边,缓慢翻涌)+电线杆/电线/远处屋顶 + 飞机拉线
3
+ // → ②写实背景(墙面冷暖渐变、木地板反光、桌椅盆栽)→ ③窗玻璃水珠(折射天空、高光、一颗慢慢滑下)
4
+ // → ④赛璐珞角色:每个部件「底色 → 背光侧硬阴影 → 受光侧亮边 → 细描线」,阴影/亮边用「自身减去平移后的自身」算(evenodd 反选)
5
+ // → ⑤丁达尔光柱+光尘 → ⑥镜头光晕(星芒、彩色光斑沿太阳-画心连线、横向拉丝)→ ⑦缩小糊化 screen 回叠=泛光
6
+ SCENES['35_shinkai'] = (() => {
7
+ const W = 1920, H = 1080, { clamp, lerp, rng } = U, P = PAINT;
8
+ const SUN = [712, 196], GL = [360, 132, 800, 548];
9
+ // ---------- 赛璐珞上色 ----------
10
+ const cel = P.cel; // 赛璐珞:底色 → 背光侧硬阴影带(自身减去平移后的自身)→ 受光侧亮边 → 细描线
11
+ // ---------- 天空与积雨云 ----------
12
+ const cloudBlobs = (() => { const r = rng(808), out = [];
13
+ // 主塔:底部宽、向上收、顶部铁砧略右偏;外加两团小云
14
+ for (let i = 0; i < 70; i++) { const h = Math.pow(r(), 0.8); const y = 540 - h * 360, w = h > 0.85 ? 300 : lerp(240, 110, h); out.push({ x: 590 + (r() - 0.5) * w * 1.5 + h * 50, y: y + (r() - 0.5) * 30, r: lerp(64, 34, h) * (0.7 + r() * 0.6), ph: r() * 6 }); }
15
+ for (let i = 0; i < 12; i++) out.push({ x: 410 + r() * 90, y: 290 + r() * 50, r: 22 + r() * 22, ph: r() * 6 });
16
+ return out.sort((a, b) => b.y - a.y); })();
17
+ function sky(c, t) {
18
+ c.save(); c.beginPath(); c.rect(GL[0], GL[1], GL[2] - GL[0], GL[3] - GL[1]); c.clip();
19
+ const sg = c.createLinearGradient(0, GL[1], 0, GL[3]); sg.addColorStop(0, '#1360d0'); sg.addColorStop(0.45, '#3f9bf0'); sg.addColorStop(0.85, '#a8dcff'); sg.addColorStop(1, '#e4f6ff');
20
+ c.fillStyle = sg; c.fillRect(GL[0], GL[1], 440, 416);
21
+ // 积雨云:离屏 → ①所有团块画蓝灰剪影(暗部)②每团一个偏向太阳的亮球(受光面)③整层糊 3px 再叠 55% 清晰层(软而不糊)
22
+ const drift = t * 10, CL = P.scratch('skCloud'), cg = CL.getContext('2d'); cg.reset();
23
+ const at = b => [b.x + drift, b.y - Math.sin(t * 0.8 + b.ph) * 3, b.r * (1 + 0.05 * Math.sin(t * 1.3 + b.ph))];
24
+ cloudBlobs.forEach(b => { const [x, y, r] = at(b); const g = cg.createLinearGradient(0, y - r, 0, y + r); g.addColorStop(0, '#b7c6e6'); g.addColorStop(1, '#8297c6'); cg.fillStyle = g; cg.beginPath(); cg.arc(x, y, r, 0, 7); cg.fill(); });
25
+ cloudBlobs.forEach(b => { const [x, y, r] = at(b); const ox = x + r * 0.22, oy = y - r * 0.3, rr = r * 0.82;
26
+ const g = cg.createRadialGradient(ox + rr * 0.3, oy - rr * 0.3, rr * 0.05, ox, oy, rr); g.addColorStop(0, '#ffffff'); g.addColorStop(0.6, '#f6f8ff'); g.addColorStop(0.9, '#e2e9fa'); g.addColorStop(1, 'rgba(226,233,250,0)');
27
+ cg.fillStyle = g; cg.beginPath(); cg.arc(ox, oy, rr, 0, 7); cg.fill(); });
28
+ c.save(); c.filter = 'blur(3px)'; c.drawImage(CL, 0, 0); c.restore(); c.globalAlpha = 0.55; c.drawImage(CL, 0, 0); c.globalAlpha = 1;
29
+ // 远处屋顶与电线杆(逆光、雾蓝)
30
+ c.fillStyle = '#7d9cc6'; c.beginPath(); c.moveTo(360, 548); [[360, 500], [420, 500], [420, 480], [470, 480], [480, 462], [540, 462], [545, 490], [610, 490], [615, 470], [690, 470], [700, 495], [760, 495], [760, 476], [800, 476], [800, 548]].forEach(p => c.lineTo(...p)); c.fill();
31
+ c.fillStyle = '#5f7fae'; c.fillRect(708, 300, 8, 250); c.fillRect(690, 318, 46, 6); c.fillRect(694, 338, 38, 5);
32
+ c.strokeStyle = 'rgba(50,70,110,.8)'; c.lineWidth = 1.6; [[318, 330], [338, 352], [321, 400]].forEach(([y0, y1], k) => { c.beginPath(); c.moveTo(360, y1 + 30 + k * 6); c.quadraticCurveTo(540, y1 + 60, 712, y0); c.quadraticCurveTo(760, y0 + 10, 800, y0 + 18 + k * 4); c.stroke(); });
33
+ // 飞机拉线:从左往右飞,线在身后变宽变淡
34
+ const px = 380 + (t * 140) % 520, py = 196 - (px - 380) * 0.05;
35
+ const ctg = c.createLinearGradient(px - 260, 0, px, 0); ctg.addColorStop(0, 'rgba(255,255,255,0)'); ctg.addColorStop(1, 'rgba(255,255,255,.9)');
36
+ c.strokeStyle = ctg; c.lineWidth = 3; c.beginPath(); c.moveTo(px - 260, py + 13); c.lineTo(px, py); c.stroke(); c.fillStyle = '#ffffff'; c.beginPath(); c.arc(px + 3, py, 2.5, 0, 7); c.fill();
37
+ // 鸟群:三只,翅膀 V 形扇动
38
+ for (let k = 0; k < 3; k++) { const bx = 470 + t * 90 + k * 34, by = 250 + k * 14 + Math.sin(t * 3 + k) * 6, f = Math.sin(t * 16 + k * 2) * 6; c.strokeStyle = '#3a4a6a'; c.lineWidth = 2; c.beginPath(); c.moveTo(bx - 9, by - f); c.lineTo(bx, by); c.lineTo(bx + 9, by - f); c.stroke(); }
39
+ c.restore();
40
+ }
41
+ // ---------- 静态室内(缓存) ----------
42
+ const room = () => P.cached('sk_room', W, H, (g) => {
43
+ const wg = g.createLinearGradient(0, 0, W, 0); wg.addColorStop(0, '#f2ece2'); wg.addColorStop(0.45, '#e9e4dc'); wg.addColorStop(1, '#bfc3d2'); g.fillStyle = wg; g.fillRect(0, 0, W, 720);
44
+ const vg = g.createLinearGradient(0, 0, 0, 720); vg.addColorStop(0, 'rgba(120,130,170,.25)'); vg.addColorStop(0.5, 'rgba(0,0,0,0)'); g.fillStyle = vg; g.fillRect(0, 0, W, 720);
45
+ // 踢脚线+木地板(透视板缝、反光)
46
+ g.fillStyle = '#d8d4cc'; g.fillRect(0, 700, W, 18);
47
+ const fg = g.createLinearGradient(0, 718, 0, H); fg.addColorStop(0, '#9a6a48'); fg.addColorStop(1, '#6a4430'); g.fillStyle = fg; g.fillRect(0, 718, W, H - 718);
48
+ g.strokeStyle = 'rgba(60,36,20,.45)'; g.lineWidth = 2; for (let i = -20; i <= 20; i++) { g.beginPath(); g.moveTo(960 + i * 70, 718); g.lineTo(960 + i * 190, H); g.stroke(); }
49
+ // 窗:铝框推拉窗(窗洞、框、中梃、窗台)
50
+ g.fillStyle = '#d4d2d4'; g.fillRect(330, 104, 500, 470); g.fillStyle = '#f6f6f8'; g.fillRect(342, 116, 476, 448);
51
+ g.fillStyle = '#b8bcc8'; g.fillRect(352, 124, 456, 432);
52
+ g.fillStyle = '#eef0f4'; g.fillRect(570, 124, 22, 432); g.fillStyle = '#c6cad4'; g.fillRect(588, 124, 4, 432);
53
+ g.fillStyle = '#fbfbfc'; g.fillRect(316, 562, 530, 22); g.fillStyle = '#c4c4cc'; g.fillRect(316, 584, 530, 8);
54
+ // 墙上:挂历和一张照片(写实的小细节)
55
+ g.fillStyle = '#ffffff'; g.fillRect(1580, 250, 150, 200); g.fillStyle = '#e2534a'; g.fillRect(1580, 250, 150, 40); g.fillStyle = '#9aa0b0'; for (let i = 0; i < 5; i++) for (let j = 0; j < 7; j++) g.fillRect(1592 + j * 19, 304 + i * 26, 12, 12);
56
+ // 桌:浅木书桌
57
+ g.fillStyle = '#7a5236'; g.fillRect(836, 650, 26, 255); g.fillRect(1160, 650, 26, 255);
58
+ g.fillStyle = '#c99a68'; g.fillRect(806, 612, 410, 30); g.fillStyle = '#f0cf9c'; g.fillRect(806, 612, 410, 6); g.fillStyle = '#8e623e'; g.fillRect(806, 638, 410, 14);
59
+ // 椅
60
+ g.fillStyle = '#8e623e'; g.fillRect(1430, 470, 18, 440); g.fillRect(1478, 480, 18, 430); g.fillRect(1426, 470, 74, 60);
61
+ g.fillStyle = '#b8865a'; g.fillRect(1426, 470, 74, 8);
62
+ // 桌上:书
63
+ g.fillStyle = '#3d5a8e'; g.fillRect(1040, 594, 110, 18); g.fillStyle = '#f2ecdc'; g.fillRect(1044, 590, 102, 6); g.fillStyle = '#c94f4a'; g.fillRect(1052, 578, 90, 14);
64
+ });
65
+ // ---------- 少女(逐部件赛璐珞) ----------
66
+ const SK = { b: '#ffe8dc', s: '#f2b6a8', r: '#fffaf4' }, HR = { b: '#f8d681', s: '#d8a050', r: '#fff6d6' }, WT = { b: '#fbfbfe', s: '#b9c3dc', r: '#ffffff' }, NV = { b: '#2c3a66', s: '#1b2446', r: '#5a6ea8' };
67
+ function girl(c, G, t, lt) {
68
+ const L = '#6a4632', o = { line: L };
69
+ cel(c, G.farSleeve, WT.b, WT.s, null, o); cel(c, G.farHand, SK.b, SK.s, null, o);
70
+ cel(c, G.skirt, NV.b, NV.s, NV.r, { ...o, sd: 30 });
71
+ c.save(); c.clip(G.skirt); c.strokeStyle = '#141a33'; c.lineWidth = 2.5; [[1170, 700, 1140, 925], [1210, 690, 1190, 930], [1250, 680, 1244, 930], [1290, 680, 1296, 925], [1330, 760, 1346, 925]].forEach(([a, b, c2, d]) => { c.beginPath(); c.moveTo(a, b); c.lineTo(c2, d); c.stroke(); }); c.restore();
72
+ cel(c, G.shoe, '#3a2a24', '#22160f', null, o);
73
+ cel(c, G.torso, WT.b, WT.s, WT.r, { ...o, sd: 26 });
74
+ // 水手领(背后的大方领,藏青+两道白线)
75
+ const nape = G.A.nape, bk = G.A.back, nk = G.A.neck;
76
+ const col = RIG.smooth([[nk[0] - 8, nk[1] - 6], [nape[0] + 10, nape[1] - 4], [bk[0] + 6, bk[1] - 20], [bk[0] - 4, bk[1] + 30], [nk[0] + 40, nk[1] + 70], [nk[0] + 6, nk[1] + 30]]);
77
+ cel(c, col, NV.b, NV.s, NV.r, { ...o, sd: 14 });
78
+ c.save(); c.clip(col); c.strokeStyle = '#f2f4fa'; c.lineWidth = 3; c.stroke(RIG.smooth([[nk[0] + 4, nk[1] + 6], [nape[0] + 12, nape[1] + 8], [bk[0] - 6, bk[1] - 14], [bk[0] - 14, bk[1] + 18], [nk[0] + 38, nk[1] + 58], [nk[0] + 12, nk[1] + 28]])); c.restore();
79
+ // 红领巾结
80
+ const ch = G.A.chest; const tie = RIG.smooth([[nk[0] - 8, nk[1] + 14], [nk[0] + 16, nk[1] + 18], [ch[0] + 10, ch[1] + 10], [ch[0] - 4, ch[1] + 50], [ch[0] - 16, ch[1] + 8]]);
81
+ cel(c, tie, '#e04454', '#a8243a', '#ff8a96', { ...o, sd: 10, rw: 4 });
82
+ cel(c, G.neck, SK.b, SK.s, null, { ...o, sd: 14 });
83
+ cel(c, G.hairBack, HR.b, HR.s, HR.r, { ...o, line: '#8a5a2a', sd: 26, rw: 9 }); cel(c, G.bun, HR.b, HR.s, HR.r, { ...o, line: '#8a5a2a', sd: 16 });
84
+ // 天使环:后脑一道亮带
85
+ c.save(); c.clip(G.hairBack); c.strokeStyle = 'rgba(255,250,226,.85)'; c.lineWidth = 9; c.beginPath(); c.arc(G.A.headC[0] + 10, G.A.headC[1] + 10, 64, -2.2, -0.6); c.stroke(); c.restore();
86
+ cel(c, G.face, SK.b, SK.s, null, { ...o, sd: 10 });
87
+ c.save(); c.clip(G.face); c.fillStyle = 'rgba(255,140,140,.35)'; c.beginPath(); c.ellipse(G.cheek[0] - 4, G.cheek[1], 16, 9, 0, 0, 7); c.fill(); c.restore();
88
+ c.fillStyle = SK.b; c.beginPath(); c.ellipse(G.ear[0], G.ear[1], 8, 12, 0.2, 0, 7); c.fill(); c.strokeStyle = L; c.lineWidth = 1.8; c.stroke();
89
+ cel(c, G.bangs, HR.b, HR.s, HR.r, { ...o, line: '#8a5a2a', sd: 12, rw: 6 });
90
+ c.strokeStyle = 'rgba(170,110,50,.7)'; c.lineWidth = 1.6; G.hairLines.forEach(h => c.stroke(h));
91
+ // 被窗口吹动的发丝
92
+ const wind = Math.sin(t * 3.2) * 6 + Math.sin(t * 7.1) * 2;
93
+ c.save(); c.translate(wind * 0.5, 0); G.locks.forEach(h => { c.strokeStyle = '#8a5a2a'; c.lineWidth = 8; c.stroke(h); c.strokeStyle = HR.b; c.lineWidth = 5.5; c.stroke(h); }); c.restore();
94
+ c.strokeStyle = HR.s; c.lineWidth = 3; for (let k = 0; k < 3; k++) { const s = G.A.nape; c.beginPath(); c.moveTo(s[0] + 10 + k * 8, s[1] - 10); c.quadraticCurveTo(s[0] + 26 + k * 8 + wind, s[1] + 30, s[0] + 18 + k * 10 + wind * 1.6, s[1] + 70); c.stroke(); }
95
+ // 动画眼:大、纵向渐变虹膜、两颗高光;眼睑粗
96
+ const e = G.eye, bl = P.choreo(lt, t).blink;
97
+ if (bl) { c.strokeStyle = '#3a2418'; c.lineWidth = 3.2; c.beginPath(); c.moveTo(e.x - 9, e.y + 3); c.quadraticCurveTo(e.x, e.y + 7, e.x + 9, e.y + 3); c.stroke(); }
98
+ else {
99
+ const ig = c.createLinearGradient(0, e.y - 12, 0, e.y + 12); ig.addColorStop(0, '#1f3f7a'); ig.addColorStop(0.6, '#3f7fd0'); ig.addColorStop(1, '#9fd4ff');
100
+ c.fillStyle = '#ffffff'; c.beginPath(); c.ellipse(e.x, e.y + 1, 8, 12, 0, 0, 7); c.fill();
101
+ c.fillStyle = ig; c.beginPath(); c.ellipse(e.x - 1.5, e.y + 2, 6.5, 11, 0, 0, 7); c.fill();
102
+ c.fillStyle = '#ffffff'; c.beginPath(); c.arc(e.x - 3.5, e.y - 3, 2.6, 0, 7); c.fill(); c.beginPath(); c.arc(e.x + 1, e.y + 7, 1.3, 0, 7); c.fill();
103
+ c.strokeStyle = '#3a2418'; c.lineWidth = 3.4; c.lineCap = 'round'; c.beginPath(); c.moveTo(e.x - 10, e.y - 7); c.quadraticCurveTo(e.x, e.y - 14, e.x + 9, e.y - 9); c.stroke();
104
+ c.lineWidth = 2; c.beginPath(); c.moveTo(e.x - 10, e.y - 7); c.lineTo(e.x - 15, e.y - 11); c.stroke();
105
+ }
106
+ c.strokeStyle = '#a07040'; c.lineWidth = 2; c.stroke(G.brow);
107
+ c.fillStyle = '#e07a78'; c.beginPath(); c.moveTo(...G.lips[0]); c.lineTo(...G.lips[1]); c.lineTo(...G.lips[2]); c.closePath(); c.fill();
108
+ // 近侧手臂(白短袖改长袖:17 岁端庄款)
109
+ cel(c, G.upperArm, WT.b, WT.s, WT.r, { ...o, sd: 16 }); cel(c, G.foreArm, WT.b, WT.s, WT.r, { ...o, sd: 12 }); cel(c, G.cuff, NV.b, NV.s, null, { ...o, sd: 8 });
110
+ cel(c, G.hand, SK.b, SK.s, SK.r, { ...o, sd: 8, rw: 3 });
111
+ const cp = G.cup; c.save(); c.translate(cp.x, cp.y); c.rotate(cp.tilt);
112
+ const cup = RIG.smooth([[-22, 0], [-19, 32], [0, 40], [19, 32], [22, 0]]); cel(c, cup, '#ffffff', '#c6d0e6', null, { ...o, sd: 10 });
113
+ c.strokeStyle = '#ffffff'; c.lineWidth = 6; c.beginPath(); c.arc(25, 15, 10, -1.3, 1.5); c.stroke(); c.strokeStyle = L; c.lineWidth = 1.6; c.stroke();
114
+ c.fillStyle = '#c98a4a'; c.beginPath(); c.ellipse(0, 1, 20, 4.5, 0, 0, 7); c.fill(); c.restore();
115
+ c.fillStyle = SK.b; c.beginPath(); c.arc(G.thumb[0], G.thumb[1], 7, 0, 7); c.fill(); c.strokeStyle = L; c.lineWidth = 1.8; c.stroke();
116
+ }
117
+ function cat(c, K, t) {
118
+ const o = { line: '#7a4a28', lw: 2.2 };
119
+ const OR = { b: '#f4a758', s: '#c8743a', r: '#fff0c8' }, WH = { b: '#fff8ee', s: '#c6cce2', r: '#ffffff' };
120
+ // 尾
121
+ c.save(); c.lineCap = 'round'; c.strokeStyle = o.line; c.lineWidth = K.tailW + 4; c.stroke(K.tail); c.strokeStyle = OR.b; c.lineWidth = K.tailW; c.stroke(K.tail);
122
+ c.strokeStyle = OR.s; c.lineWidth = K.tailW * 0.45; c.save(); c.translate(4, 5); c.stroke(K.tail); c.restore();
123
+ const tip = K.tailPts[K.tailPts.length - 1]; c.fillStyle = WH.b; c.beginPath(); c.arc(tip[0], tip[1], K.tailW / 2, 0, 7); c.fill(); c.restore();
124
+ const body = RIG.smooth(K.bodyPts);
125
+ cel(c, body, OR.b, OR.s, OR.r, { ...o, sd: 34, rw: 8, lx: 0.5, ly: 1 }); // 猫在窗下:背光,暗部在下侧
126
+ c.save(); c.clip(body); c.strokeStyle = '#d07a34'; c.lineWidth = 8; c.lineCap = 'round'; K.stripes.slice(3).forEach(s => { c.beginPath(); c.moveTo(...s[0]); c.lineTo(...s[1]); c.stroke(); });
127
+ cel(c, RIG.smooth([[566, 700], [612, 690], [642, 742], [636, 830], [596, 870], [560, 800]]), WH.b, WH.s, null, { ...o, lw: 0, sd: 18, lx: 0.5, ly: 1 }); c.restore();
128
+ c.strokeStyle = o.line; c.lineWidth = 2.2; c.stroke(body);
129
+ [[588, 812], [634, 814]].forEach(([x, y]) => cel(c, RIG.taper([x, y], [x, 890], 30, 30), WH.b, WH.s, null, { ...o, sd: 10, lx: 0.5, ly: 1 }));
130
+ cel(c, K.head, OR.b, OR.s, OR.r, { ...o, sd: 22, rw: 8, lx: 0.5, ly: 1 });
131
+ c.save(); c.clip(K.head); c.strokeStyle = '#d07a34'; c.lineWidth = 7; c.lineCap = 'round'; K.stripes.slice(0, 3).forEach(s => { c.beginPath(); c.moveTo(...s[0]); c.lineTo(...s[1]); c.stroke(); });
132
+ c.fillStyle = WH.b; c.beginPath(); c.ellipse(K.nose[0] - 24, K.nose[1] + 12, 36, 25, 0, 0, 7); c.fill(); c.restore();
133
+ c.fillStyle = '#f6b0b0'; K.earInner.forEach(e => { c.beginPath(); e.forEach((p, i) => i ? c.lineTo(...p) : c.moveTo(...p)); c.fill(); });
134
+ K.eyes.forEach(e => { if (K.blink) { c.strokeStyle = '#3a2418'; c.lineWidth = 3; c.beginPath(); c.arc(e.x, e.y, 9, 0.2, Math.PI - 0.2); c.stroke(); return; }
135
+ const g = c.createLinearGradient(0, e.y - 10, 0, e.y + 10); g.addColorStop(0, '#5a8a2a'); g.addColorStop(1, '#d8f080'); c.fillStyle = g; c.beginPath(); c.ellipse(e.x, e.y, 9, 11, 0, 0, 7); c.fill();
136
+ c.fillStyle = '#1a1a1a'; c.beginPath(); c.ellipse(e.x + 1.5, e.y, 3, 8, 0, 0, 7); c.fill(); c.fillStyle = '#fff'; c.beginPath(); c.arc(e.x - 2.5, e.y - 4, 2.4, 0, 7); c.fill(); c.strokeStyle = '#3a2418'; c.lineWidth = 2; c.beginPath(); c.ellipse(e.x, e.y, 9, 11, 0, 0, 7); c.stroke(); });
137
+ c.fillStyle = '#e88a8a'; c.beginPath(); c.arc(K.nose[0], K.nose[1], 5, 0, 7); c.fill();
138
+ c.strokeStyle = '#7a4a28'; c.lineWidth = 2; c.beginPath(); c.moveTo(...K.mouth[0]); c.quadraticCurveTo(...K.mouth[1], ...K.mouth[2]); c.stroke();
139
+ c.lineWidth = 1.2; c.strokeStyle = 'rgba(255,255,255,.9)'; K.whiskers.forEach(w => { c.beginPath(); c.moveTo(...w[0]); c.lineTo(...w[1]); c.stroke(); });
140
+ // 逆光:猫的头顶和背上沿一圈亮边(窗在它头顶)
141
+ c.save(); c.globalCompositeOperation = 'lighter'; c.strokeStyle = 'rgba(255,220,160,.55)'; c.lineWidth = 4; c.save(); c.clip(K.head); c.translate(0, 5); c.stroke(K.head); c.restore(); c.save(); c.clip(body); c.translate(4, 6); c.stroke(body); c.restore(); c.restore();
142
+ }
143
+
144
+ return {
145
+ draw(c, lt, t) {
146
+ const ch = P.choreo(lt, t);
147
+ c.drawImage(room(), 0, 0);
148
+ sky(c, t);
149
+ // 窗玻璃:水珠(折射一小块天空+下沿暗边+左上高光),一颗沿玻璃滑下
150
+ const dr = rng(64);
151
+ for (let i = 0; i < 20; i++) { let x = 366 + dr() * 428, y = 140 + dr() * 400, r = 2 + dr() * 3.5; if (i === 0) { r = 8; x = 470; y = 180 + clamp(lt - 0.2) * 220; }
152
+ if (x > 566 && x < 596) continue;
153
+ c.strokeStyle = 'rgba(30,60,120,.45)'; c.lineWidth = 1.4; c.beginPath(); c.arc(x, y, r, 0.3, Math.PI - 0.3); c.stroke();
154
+ c.fillStyle = 'rgba(220,240,255,.22)'; c.beginPath(); c.arc(x, y, r, 0, 7); c.fill();
155
+ c.fillStyle = '#ffffff'; c.beginPath(); c.arc(x - r * 0.35, y - r * 0.35, r * 0.28, 0, 7); c.fill();
156
+ if (i === 0) { c.strokeStyle = 'rgba(220,240,255,.45)'; c.lineWidth = 3; c.beginPath(); c.moveTo(x, y - 10); c.lineTo(x, 180); c.stroke(); } }
157
+ // 窗帘:右侧白纱,随风鼓起(贝塞尔边缘按时间摆)
158
+ const wv = Math.sin(t * 2.6) * 22 + Math.sin(t * 5.3) * 6;
159
+ const cur = new Path2D(); cur.moveTo(760, 108); cur.lineTo(840, 108); cur.bezierCurveTo(850, 260, 830 + wv * 0.3, 420, 846, 600); cur.lineTo(760 - wv, 600); cur.bezierCurveTo(720 - wv * 1.4, 430, 760 - wv * 0.6, 260, 760, 108); cur.closePath();
160
+ c.fillStyle = 'rgba(255,255,255,.55)'; c.fill(cur); c.strokeStyle = 'rgba(200,210,230,.6)'; c.lineWidth = 2; for (let k = 0; k < 4; k++) { c.beginPath(); c.moveTo(775 + k * 18, 110); c.bezierCurveTo(780 + k * 18, 300, 770 + k * 16 - wv * (0.4 + k * 0.1), 460, 770 + k * 20 - wv * (0.8 - k * 0.15), 598); c.stroke(); }
161
+ // 地板上的窗形光斑(暖,投影平行四边形)+桌面一角受光
162
+ c.save(); c.globalCompositeOperation = 'screen';
163
+ c.drawImage(P.cached('sk_floorlight', W, H, (g) => { g.filter = 'blur(14px)'; g.fillStyle = 'rgba(255,214,150,.6)'; g.beginPath(); g.moveTo(560, 740); g.lineTo(980, 740); g.lineTo(1240, 1040); g.lineTo(720, 1040); g.closePath(); g.fill();
164
+ g.fillStyle = 'rgba(80,60,40,1)'; g.globalCompositeOperation = 'destination-out'; g.beginPath(); g.moveTo(760, 740); g.lineTo(790, 740); g.lineTo(990, 1040); g.lineTo(950, 1040); g.closePath(); g.fill(); }), 0, 0);
165
+ c.fillStyle = 'rgba(255,214,150,.3)'; c.fillRect(806, 612, 300, 26);
166
+ // 墙上窗形光斑(右上→右下的平行四边形)+窗帘的影子在里面摆
167
+ const WL = P.scratch('skWallLight'), wlg = WL.getContext('2d'); wlg.reset(); wlg.filter = 'blur(10px)';
168
+ wlg.fillStyle = 'rgba(255,222,170,.55)'; wlg.beginPath(); wlg.moveTo(980, 150); wlg.lineTo(1330, 230); wlg.lineTo(1330, 600); wlg.lineTo(980, 560); wlg.closePath(); wlg.fill();
169
+ wlg.globalCompositeOperation = 'destination-out'; wlg.fillStyle = '#000'; wlg.beginPath(); wlg.moveTo(1140, 186); wlg.lineTo(1166, 192); wlg.lineTo(1166, 580); wlg.lineTo(1140, 578); wlg.fill();
170
+ const cs = Math.sin(t * 2.6) * 30 + Math.sin(t * 5.3) * 8; wlg.globalAlpha = 0.7; wlg.beginPath(); wlg.moveTo(1240 + cs * 0.3, 200); wlg.bezierCurveTo(1230 + cs, 330, 1250 - cs * 0.4, 460, 1220 + cs, 600); wlg.lineTo(1340, 600); wlg.lineTo(1340, 200); wlg.fill();
171
+ c.drawImage(WL, 0, 0);
172
+ c.restore();
173
+ // 盆栽(叶子随风摆)
174
+ const sway = Math.sin(t * 2.6) * 0.06;
175
+ c.fillStyle = '#e8e2d6'; c.fillRect(872, 566, 56, 46); c.fillStyle = '#c9c0b0'; c.fillRect(900, 566, 28, 46);
176
+ [[-0.8, 60], [-0.3, 74], [0.2, 70], [0.7, 58], [-1.2, 44], [1.1, 46]].forEach(([a, l], k) => { c.save(); c.translate(900, 566); c.rotate(a + sway * (1 + k * 0.3)); const leaf = new Path2D(); leaf.ellipse(0, -l * 0.6, 13, l * 0.55, 0, 0, 7); cel(c, leaf, '#5fae5a', '#2f7a42', '#b8f09a', { line: '#2a5a32', lw: 1.6, sd: 7, rw: 3 }); c.restore(); });
177
+ // 角色
178
+ const K = RIG.cat({ tail: ch.tail, blink: ch.blink, breathe: ch.breathe });
179
+ const G = RIG.girl({ cup: ch.cup, sip: ch.sip, breathe: ch.breathe, hair: 'bun' });
180
+ cat(c, K, t); girl(c, G, t, lt);
181
+ P.steam(c, t, G.cup.x, G.cup.y - 6, { h: 60, n: 2, color: 'rgba(255,255,255,.7)', width: 3, spread: 12, wobble: 8 });
182
+ // 丁达尔光柱(缓存的模糊楔形,按时间轻微闪动)+光尘
183
+ c.save(); c.globalCompositeOperation = 'screen';
184
+ const beams = P.cached('sk_beams', W, H, (g) => { g.filter = 'blur(18px)'; [[380, 130, 470, 130, 1060, 1000, 860, 1000, 0.32], [520, 130, 600, 130, 1220, 980, 1080, 1000, 0.22], [640, 130, 790, 130, 1450, 900, 1240, 1000, 0.26]].forEach(([a, b, c2, d, e, f, gx, h, al]) => {
185
+ const lg = g.createLinearGradient(a, b, e, f); lg.addColorStop(0, `rgba(255,240,205,${al})`); lg.addColorStop(1, 'rgba(255,240,205,0)'); g.fillStyle = lg; g.beginPath(); g.moveTo(a, b); g.lineTo(c2, d); g.lineTo(e, f); g.lineTo(gx, h); g.closePath(); g.fill(); }); });
186
+ c.globalAlpha = 0.75 + 0.25 * Math.sin(t * 3.4); c.drawImage(beams, 0, 0); c.globalAlpha = 1;
187
+ const motes = P.particles(60, 91, t * 0.5, { x0: 480, x1: 1300, y0: 980, y1: 140, speed: 30, drift: 30, life: 8 });
188
+ motes.forEach((m, i) => { const tw = 0.5 + 0.5 * Math.sin(t * 6 + i * 1.3); const rr = 1.5 + m.s * 3; const g2 = c.createRadialGradient(m.x, m.y, 0, m.x, m.y, rr * 2.5); g2.addColorStop(0, `rgba(255,250,230,${0.8 * tw})`); g2.addColorStop(1, 'rgba(255,250,230,0)'); c.fillStyle = g2; c.beginPath(); c.arc(m.x, m.y, rr * 2.5, 0, 7); c.fill(); });
189
+ c.restore();
190
+ // 镜头光晕(P.lensFlare:太阳核、慢转星芒、横向拉丝、沿太阳→画心的六边形光斑)+泛光(P.bloom:缩小糊化 screen 回叠)
191
+ P.lensFlare(c, SUN, t);
192
+ P.bloom(c, { key: 'skBloom' });
193
+ // 整体色调:高光偏暖、暗部偏蓝(新海诚的冷暖分离)
194
+ c.save(); c.globalCompositeOperation = 'soft-light'; const tg = c.createLinearGradient(0, 0, W, H); tg.addColorStop(0, 'rgba(255,220,170,.35)'); tg.addColorStop(1, 'rgba(80,120,220,.35)'); c.fillStyle = tg; c.fillRect(0, 0, W, H); c.restore();
195
+ },
196
+ };
197
+ })();
@@ -0,0 +1,123 @@
1
+ // 1903 毕加索蓝色时期 —— 纯代码。
2
+ // 管线:①蓝单色平涂底稿(墙/地/窗外夜海)→ ②宽而干的笔触重画(区域蓝色板,墙竖刷、地横刷,不沸腾)
3
+ // → ③角色:骨架整体拉长变瘦(El Greco 式:绕脚底 scale(0.88,1.09)),平涂+小笔触,披肩罩头 → ④普鲁士蓝粗轮廓(只取看得见的线)
4
+ // → ⑤会动的:缓慢呼吸(幅度×2、频率×0.45)、窗外海浪横移、月光碎影闪、热气慢升 → ⑥画布纹+冷暗角
5
+ SCENES['36_picasso_blue'] = (() => {
6
+ const W = 1920, H = 1080, { clamp, lerp, rng } = U, P = PAINT;
7
+ const C = { navy: '#132240', prus: '#1d3866', cob: '#2d5a94', cer: '#6b8fb6', pale: '#b6c8d8', teal: '#3b6878', ochre: '#8c7a54', line: '#0c1830' };
8
+ // 拉长变瘦(El Greco 式):RIG 原生非等比缩放,以脚底为轴——路径、锚点、杯子一起变,线宽不被压扁
9
+ const GSTR = { sx: 0.84, sy: 1.12, anchor: [1240, 932] }, CSTR = { sx: 0.82, sy: 1.14, anchor: [540, 905] };
10
+
11
+ function base(g) {
12
+ g.fillStyle = C.prus; g.fillRect(0, 0, W, 720);
13
+ const wg = g.createLinearGradient(0, 0, W, 0); wg.addColorStop(0, 'rgba(110,145,185,.35)'); wg.addColorStop(0.6, 'rgba(0,0,0,0)'); wg.addColorStop(1, 'rgba(5,10,25,.45)'); g.fillStyle = wg; g.fillRect(0, 0, W, 720);
14
+ g.fillStyle = '#4a6c94'; g.fillRect(0, 720, W, 360);
15
+ const fg = g.createLinearGradient(0, 720, 0, H); fg.addColorStop(0, 'rgba(10,20,40,.4)'); fg.addColorStop(1, 'rgba(150,175,200,.25)'); g.fillStyle = fg; g.fillRect(0, 720, W, 360);
16
+ // 窗:夜海+月
17
+ g.fillStyle = '#7d9cbc'; g.fillRect(340, 110, 480, 460);
18
+ const sk = g.createLinearGradient(0, 130, 0, 400); sk.addColorStop(0, '#1a2f58'); sk.addColorStop(1, '#4f74a4'); g.fillStyle = sk; g.fillRect(360, 130, 440, 270);
19
+ g.fillStyle = '#16284c'; g.fillRect(360, 400, 440, 150);
20
+ g.fillStyle = '#c9d6e2'; g.beginPath(); g.arc(670, 220, 30, 0, 7); g.fill();
21
+ g.fillStyle = '#7d9cbc'; g.fillRect(572, 130, 16, 420); g.fillRect(360, 330, 440, 12);
22
+ g.fillStyle = '#91aecb'; g.fillRect(320, 560, 520, 24);
23
+ }
24
+ function table(g) {
25
+ g.fillStyle = '#20406c'; g.fillRect(806, 614, 408, 34); g.fillStyle = '#16305a'; g.fillRect(826, 648, 368, 40); g.fillRect(842, 688, 22, 216); g.fillRect(1156, 688, 22, 216);
26
+ g.fillStyle = '#1a3460'; g.fillRect(1430, 470, 18, 440); g.fillRect(1478, 480, 18, 430); for (let k = 0; k < 3; k++) g.fillRect(1434, 520 + k * 70, 60, 12);
27
+ // 桌上:一只水罐和一块面包(蓝色时期静物的穷与静)
28
+ g.fillStyle = '#9fb6cc'; g.beginPath(); g.moveTo(880, 614); g.quadraticCurveTo(860, 560, 886, 530); g.lineTo(914, 530); g.quadraticCurveTo(940, 560, 920, 614); g.closePath(); g.fill();
29
+ g.fillStyle = '#6c7f8e'; g.beginPath(); g.ellipse(1040, 604, 50, 14, 0, 0, 7); g.fill();
30
+ }
31
+ const SW = {
32
+ wall: P.swatch(['#1d3866', '#24457a', '#163056', '#2e5a94', '#20406e', '#3b6878'], 0.8),
33
+ floor: P.swatch(['#4a6c94', '#587ca2', '#3e5f86', '#6b8fb6', '#456a8a'], 0.75),
34
+ win: P.swatch(['#7d9cbc', '#8eaac6', '#6f90b2'], 0.6),
35
+ };
36
+ // 第一版 cell16/len40/w14 满墙竖刷=「下雨/毛毯」,像印象派。蓝色时期是宽、长、低对比的刷面:cell34 len110 w34,色板混底 0.8,再糊 3px。
37
+ const stat = () => P.cached('bp_static', W, H, (g, cv) => {
38
+ const b = P.canvas(), bg = b.getContext('2d'); base(bg); g.drawImage(b, 0, 0);
39
+ P.strokes(g, b, { cell: 34, len: 110, width: 34, seed: 12, t: 0, boil: 0, outline: 0, jitterCol: 4,
40
+ angle: (x, y) => y > 720 ? 0.05 * P.noise(x * 0.01, y * 0.01) : -Math.PI / 2 + 0.5 * P.noise(x * 0.003, y * 0.003),
41
+ palette: (x, y, col, r) => (y > 720 ? SW.floor(col, r) : SW.wall(col, r)),
42
+ mask: (x, y) => !(x > 330 && x < 830 && y > 100 && y < 595) && Math.abs(y - 720) > 20 });
43
+ { const tmp = P.canvas(), tg = tmp.getContext('2d'); tg.filter = 'blur(7px)'; tg.drawImage(cv, 0, 0); g.globalAlpha = 0.8; g.drawImage(tmp, 0, 0); g.globalAlpha = 1; }
44
+ // 干刷:笔触末端露底的细丝(很淡)
45
+ const r = rng(9); g.globalAlpha = 0.07; g.strokeStyle = '#a8c0d6'; g.lineWidth = 1.2;
46
+ for (let i = 0; i < 900; i++) { const x = r() * W, y = r() * H; if (x > 330 && x < 830 && y > 100 && y < 595) continue; const a = y > 720 ? 0 : -Math.PI / 2, L = 30 + r() * 60; g.beginPath(); g.moveTo(x, y); g.lineTo(x + Math.cos(a) * L, y + Math.sin(a) * L); g.stroke(); }
47
+ g.globalAlpha = 1;
48
+ // 窗:保留清楚的框,只给框上小刷痕
49
+ g.drawImage(b, 320, 100, 520, 500, 320, 100, 520, 500);
50
+ g.save(); g.beginPath(); g.rect(320, 100, 520, 500); g.clip();
51
+ P.strokes(g, b, { cell: 9, len: 20, width: 8, seed: 13, t: 0, boil: 0, outline: 0, jitterCol: 8, angle: (x, y) => (x > 560 && x < 600) ? -Math.PI / 2 : 0.03,
52
+ palette: (x, y, col, r) => SW.win(col, r), mask: (x, y) => !(x > 362 && x < 798 && y > 132 && y < 548) || (x > 572 && x < 588) || (y > 330 && y < 342) });
53
+ g.restore();
54
+ // 桌:单独一层+小笔触
55
+ const tb = P.canvas(), tbg = tb.getContext('2d'); table(tbg); tbg.save(); tbg.globalCompositeOperation = 'source-atop';
56
+ P.strokes(tbg, tb, { cell: 9, len: 26, width: 9, seed: 14, t: 0, boil: 0, outline: 0, jitterCol: 10, alphaMask: true, angle: (x, y) => y > 690 ? -Math.PI / 2 : 0.02 }); tbg.restore(); g.drawImage(tb, 0, 0);
57
+ // 环境轮廓(普鲁士蓝粗线,不抖)
58
+ g.strokeStyle = C.line; g.lineWidth = 5; g.lineJoin = 'round';
59
+ [[340, 110, 480, 460], [360, 130, 440, 420], [806, 614, 408, 34], [826, 648, 368, 40], [842, 688, 22, 216], [1156, 688, 22, 216]].forEach(r_ => g.strokeRect(...r_));
60
+ g.beginPath(); g.moveTo(0, 720); g.lineTo(806, 720); g.moveTo(1214, 720); g.lineTo(W, 720); g.stroke();
61
+ g.beginPath(); g.moveTo(880, 614); g.quadraticCurveTo(860, 560, 886, 530); g.lineTo(914, 530); g.quadraticCurveTo(940, 560, 920, 614); g.stroke();
62
+ });
63
+
64
+ const girlPal = { skin: '#90a9be', hair: '#cbd8e0', hairLine: '#5f7896', dress: '#1e3a6a', sleeve: '#24457a', fold: '#10223f', cuff: '#24457a', cheek: null, lip: '#4a6a8c', iris: '#0c1830', line: C.line, lw: 5, cupBody: '#c6d4e0', cupRim: '#2d4a6e', shoe: '#0c1830', browC: '#2a3a52' };
65
+ const catPal = { orange: '#4f719a', white: '#b6c8d8', stripe: '#273f66', line: C.line, lw: 5, eye: '#c9d6e2', earInner: '#6b8fb6', noseC: '#4a6a8c', whisker: '#c9d6e2' };
66
+
67
+ return {
68
+ draw(c, lt, t) {
69
+ const br = Math.sin(t * 1.8) * 0.026; // 缓慢而深的呼吸
70
+ const cup = 0.5 - 0.5 * Math.cos(Math.min(1, lt / 1.25) * Math.PI); // 端杯也慢
71
+ const tail = Math.sin(t * 2.2) * 0.3;
72
+ c.drawImage(stat(), 0, 0);
73
+ // 窗外:海浪(横向短笔,左移)+月光碎影(闪)
74
+ c.save(); c.beginPath(); c.rect(360, 400, 440, 150); c.clip();
75
+ const wr = rng(5); c.lineCap = 'round';
76
+ for (let i = 0; i < 70; i++) { const y = 405 + wr() * 140, sp = 20 + (y - 400) * 0.25, x = 360 + ((wr() * 520 - t * sp) % 520 + 520) % 520 - 40;
77
+ c.strokeStyle = wr() < 0.5 ? 'rgba(90,125,170,.8)' : 'rgba(30,55,95,.9)'; c.lineWidth = 3 + (y - 400) * 0.04; c.beginPath(); c.moveTo(x, y); c.lineTo(x + 26 + (y - 400) * 0.2, y + 1); c.stroke(); }
78
+ for (let k = 0; k < 14; k++) { const y = 410 + k * 10, tw = 0.5 + 0.5 * Math.sin(t * 5 + k * 1.9), w = (10 + k * 4) * tw; c.fillStyle = `rgba(205,220,235,${0.35 + 0.5 * tw})`; c.fillRect(670 - w / 2 + Math.sin(t * 2 + k) * 6, y, w, 3); }
79
+ c.restore();
80
+ // 云:一条苍白的长云缓慢横过月亮
81
+ c.save(); c.beginPath(); c.rect(360, 130, 440, 270); c.clip(); c.fillStyle = 'rgba(150,175,205,.45)'; const cx = 400 + t * 45; c.beginPath(); c.ellipse(cx, 236, 130, 14, -0.04, 0, 7); c.fill(); c.beginPath(); c.ellipse(cx + 90, 250, 90, 9, 0, 0, 7); c.fill(); c.restore();
82
+ // 角色
83
+ const K = RIG.cat({ tail, blink: 0, breathe: br * 1.4, ...CSTR });
84
+ const G = RIG.girl({ cup, sip: 0, breathe: br, hair: 'down', ...GSTR });
85
+ const L = P.scratch('bpChars'), lg = L.getContext('2d'); lg.reset();
86
+ RIG.drawCat(lg, K, { ...catPal, lw: 0 });
87
+ lg.save();
88
+ RIG.drawGirl(lg, G, { ...girlPal, mode: 'fill' });
89
+ // 披肩:从头顶罩下,盖住后脑与肩背,落到腰
90
+ const ht = G.A.headTop, fh = G.A.forehead, np = G.A.nape, bk = G.A.back, wa = G.A.waist, sh = G.A.shoulder;
91
+ const shawl = RIG.smooth([[fh[0] + 14, fh[1] - 22], [ht[0] + 10, ht[1] - 26], [ht[0] + 80, ht[1] + 4], [np[0] + 50, np[1] + 10], [bk[0] + 24, bk[1] + 20], [wa[0] + 70, wa[1] - 10], [wa[0] + 20, wa[1] - 40], [sh[0] + 10, sh[1] + 30], [np[0] - 10, np[1] + 10], [ht[0] + 30, ht[1] + 40], [fh[0] + 30, fh[1] - 4]]);
92
+ lg.fillStyle = '#6b8fb6'; lg.fill(shawl);
93
+ // 端杯的手:长指(El Greco 式),手指绕杯
94
+ lg.fillStyle = girlPal.skin; const hd = G.A.hand; for (let k = 0; k < 3; k++) { lg.save(); lg.translate(hd[0], hd[1]); lg.rotate(-2.4 + k * 0.28); lg.beginPath(); lg.ellipse(18, 0, 22, 4.5, 0, 0, 7); lg.fill(); lg.restore(); }
95
+ lg.restore();
96
+ // 小笔触肌理(只在角色上)
97
+ lg.save(); lg.globalCompositeOperation = 'source-atop';
98
+ P.strokes(lg, L, { cell: 7, len: 18, width: 6, seed: 21, t: 0, boil: 0, outline: 0, jitterCol: 14, alphaMask: true,
99
+ angle: (x, y) => x < 760 ? -Math.PI / 2 + 0.6 * Math.sin(y * 0.02) : -Math.PI / 2 + 0.25 * Math.sin(x * 0.03) });
100
+ // 冷光:左上来的微弱亮、右下沉暗
101
+ let vg = lg.createLinearGradient(1150, 250, 1480, 900); vg.addColorStop(0, 'rgba(190,210,230,.18)'); vg.addColorStop(1, 'rgba(5,12,30,.45)'); lg.fillStyle = vg; lg.fillRect(1050, 150, 500, 820);
102
+ vg = lg.createLinearGradient(420, 560, 700, 920); vg.addColorStop(0, 'rgba(190,210,230,.12)'); vg.addColorStop(1, 'rgba(5,12,30,.4)'); lg.fillStyle = vg; lg.fillRect(360, 500, 380, 420);
103
+ lg.restore();
104
+ c.drawImage(L, 0, 0);
105
+ // 粗轮廓:只取看得见的线(猫、少女各自在拉长后的坐标里算)
106
+ c.drawImage(RIG.visibleLines('bpCat', (g, lc) => RIG.drawCat(g, K, { ...catPal, line: lc, whisker: lc }), C.line, [330, 480, 760, 940]), 0, 0);
107
+ c.drawImage(RIG.visibleLines('bpGirl', (g, lc) => { RIG.drawGirl(g, G, { ...girlPal, line: lc });
108
+ g.fillStyle = '#6b8fb6'; g.fill(shawl); g.strokeStyle = lc; g.lineWidth = 5; g.stroke(shawl); }, C.line, [1040, 150, 1540, 960]), 0, 0);
109
+ // 披肩褶:几道深蓝长线
110
+ c.save(); c.save(); c.clip(shawl); c.strokeStyle = 'rgba(20,40,80,.6)'; c.lineWidth = 4; c.lineCap = 'round';
111
+ [[0, 0], [24, 10], [48, 22]].forEach(([dx, dy]) => { c.beginPath(); c.moveTo(ht[0] + 30 + dx, ht[1] + dy); c.quadraticCurveTo(np[0] + 30 + dx, np[1] + 40, bk[0] + dx * 0.3, bk[1] + 60 + dy); c.stroke(); }); c.restore();
112
+ c.restore();
113
+ // 热气:慢
114
+ const cpp = [G.cup.x, G.cup.y];
115
+ P.steam(c, t * 0.5, cpp[0], cpp[1] - 8, { h: 70, n: 2, color: 'rgba(200,215,230,.55)', width: 3.5, spread: 12, wobble: 10 });
116
+ // 画布纹+冷暗角
117
+ c.drawImage(P.cached('bp_over', W, H, (g) => {
118
+ g.strokeStyle = 'rgba(10,20,40,.06)'; g.lineWidth = 1; for (let x = 0; x < W; x += 4) { g.beginPath(); g.moveTo(x, 0); g.lineTo(x, H); g.stroke(); } for (let y = 0; y < H; y += 4) { g.beginPath(); g.moveTo(0, y); g.lineTo(W, y); g.stroke(); }
119
+ const v = g.createRadialGradient(900, 520, 380, 960, 540, 1150); v.addColorStop(0, 'rgba(5,10,25,0)'); v.addColorStop(1, 'rgba(5,10,25,.6)'); g.fillStyle = v; g.fillRect(0, 0, W, H);
120
+ }), 0, 0);
121
+ },
122
+ };
123
+ })();
@@ -0,0 +1,133 @@
1
+ // 乔托(斯克罗威尼礼拜堂湿壁画):群青天、重量感的袍、视线连接人物、斜置的建筑体块、灰泥与一天的活接缝。
2
+ // 题材(新构图,不搬名作):码头仓库门口,两位搬运工把一只大木箱交接,第三人低头看着箱角。
3
+ // 原作锚定(scripts/analyze/fetch_reference.py 拉的公版图,色值与签名见 配方卡):群青天 #3a5a9a→#8fa4c8、土红/赭/橄榄绿袍、
4
+ // 灰粉建筑 #c9b9ae、金边装饰带、人物视线互相咬合、衣褶大块明暗;整体粉白哑光。
5
+ // 管线:plate(干净的形:天→岩坡→建筑→人→箱)→ MEDIUM.fresco(轻糊+石灰罩+灰泥相乘+针孔接缝)→ 装饰边框 → 动作。
6
+ SCENES['38_giotto'] = (() => {
7
+ const W = 1920, H = 1080, { clamp, lerp, rng, ss } = U, P = PAINT, M = MEDIUM, TAU = Math.PI * 2;
8
+ let plate, pg;
9
+ const ease = x => { x = clamp(x); return x * x * (3 - 2 * x); };
10
+
11
+ function sky(g) {
12
+ const gr = g.createLinearGradient(0, 0, 0, 620); gr.addColorStop(0, '#2347a0'); gr.addColorStop(0.5, '#3f68b8'); gr.addColorStop(1, '#a9c0e6');
13
+ g.fillStyle = gr; g.fillRect(0, 0, W, H);
14
+ // 群青是一层层罩的:大尺度斜向软斑(fbm),不是横条
15
+ const r = rng(11);
16
+ for (let i = 0; i < 140; i++) { const x = r() * W, y = r() * 600, rx = 90 + r() * 260, ry = 24 + r() * 70; g.save(); g.translate(x, y); g.rotate(-0.25 + (r() - .5) * 0.5); g.filter = 'blur(14px)';
17
+ g.fillStyle = `rgba(${r() < 0.55 ? '24,52,132' : '170,190,232'},${0.06 + r() * 0.09})`; g.beginPath(); g.ellipse(0, 0, rx, ry, 0, 0, TAU); g.fill(); g.restore(); }
18
+ g.filter = 'none';
19
+ // 金星点(原作天幕有星)
20
+ for (let i = 0; i < 26; i++) { const x = 60 + r() * (W - 120), y = 40 + r() * 330, s = 5 + r() * 4; g.fillStyle = 'rgba(224,190,100,.85)'; g.beginPath();
21
+ for (let k = 0; k < 8; k++) { const a = k / 8 * TAU, rr = k % 2 ? s * 0.4 : s; g.lineTo(x + Math.cos(a) * rr, y + Math.sin(a) * rr); } g.closePath(); g.fill(); }
22
+ }
23
+ function rocks(g) { // 乔托的岩坡:斜向的大块面,亮面朝左上,暗面朝下,边缘有「梯田」台阶
24
+ const poly = (pts, c0, c1) => { const gr = g.createLinearGradient(pts[0][0], pts[0][1], pts[2][0], pts[2][1] + 120); gr.addColorStop(0, c0); gr.addColorStop(1, c1);
25
+ g.fillStyle = gr; g.beginPath(); pts.forEach((p, i) => i ? g.lineTo(p[0], p[1]) : g.moveTo(p[0], p[1])); g.closePath(); g.fill(); M.outline(g, [...pts, pts[0]], { w: [2, 3.4, 2], col: '#6e5a4a', a: 0.6 }); };
26
+ poly([[0, 420], [340, 300], [620, 410], [700, 640], [0, 700]], '#c4b79c', '#8e8068');
27
+ poly([[0, 560], [420, 470], [640, 620], [700, 700], [0, 760]], '#b4a487', '#7f7058');
28
+ poly([[300, 420], [560, 330], [760, 470], [640, 620], [420, 470]], '#d1c6ac', '#a1937a');
29
+ g.fillStyle = 'rgba(80,100,50,.55)'; [[60, 640, 120], [250, 600, 90], [470, 640, 80]].forEach(([x, y, w]) => { g.beginPath(); g.ellipse(x, y, w, 20, -0.15, 0, TAU); g.fill(); });
30
+ g.strokeStyle = 'rgba(70,90,45,.9)'; g.lineWidth = 3; // 一棵小树,几笔叶团
31
+ g.beginPath(); g.moveTo(520, 440); g.lineTo(522, 360); g.stroke(); g.fillStyle = '#5f7a43'; [[522, 340, 34], [498, 372, 26], [548, 368, 26]].forEach(([x, y, rr]) => { g.beginPath(); g.arc(x, y, rr, 0, TAU); g.fill(); });
32
+ }
33
+ function building(g) { // 仓库:两个可见立面(正面+斜侧),透视故意不统一(前期文艺复兴之前的「舞台盒」),带拱门和垛口
34
+ const front = [[1040, 250], [1620, 250], [1620, 860], [1040, 860]], side = [[1620, 250], [1810, 190], [1810, 820], [1620, 860]];
35
+ let gr = g.createLinearGradient(1040, 0, 1620, 0); gr.addColorStop(0, '#d9c9bd'); gr.addColorStop(1, '#bcab9f'); g.fillStyle = gr; g.beginPath(); front.forEach((p, i) => i ? g.lineTo(p[0], p[1]) : g.moveTo(p[0], p[1])); g.fill();
36
+ gr = g.createLinearGradient(1620, 0, 1810, 0); gr.addColorStop(0, '#9c8b82'); gr.addColorStop(1, '#7d6d66'); g.fillStyle = gr; g.beginPath(); side.forEach((p, i) => i ? g.lineTo(p[0], p[1]) : g.moveTo(p[0], p[1])); g.fill();
37
+ // 檐下装饰带:红白相间的菱格(科斯马蒂式)
38
+ g.fillStyle = '#8c3d34'; g.fillRect(1040, 262, 580, 30); g.fillStyle = '#ead9aa'; for (let x = 1052; x < 1610; x += 36) { g.beginPath(); g.moveTo(x, 277); g.lineTo(x + 12, 266); g.lineTo(x + 24, 277); g.lineTo(x + 12, 288); g.closePath(); g.fill(); }
39
+ // 侧面小窗
40
+ g.fillStyle = '#4a3a30'; [[1685, 380], [1685, 560]].forEach(([x, y]) => { g.beginPath(); g.moveTo(x, y + 80); g.lineTo(x, y + 20); g.arc(x + 24, y + 20, 24, Math.PI, 0); g.lineTo(x + 48, y + 80); g.fill(); });
41
+ // 垛口
42
+ for (let i = 0; i < 6; i++) { const x = 1050 + i * 98; g.fillStyle = '#cdbdb0'; g.fillRect(x, 200, 62, 52); M.outline(g, [[x, 252], [x, 200], [x + 62, 200], [x + 62, 252]], { w: [2, 3, 2], col: '#6e5a4a', a: 0.6 }); }
43
+ // 石砌水平缝(不画方格,画几道长缝,缝处有错位)
44
+ g.strokeStyle = 'rgba(110,90,78,.28)'; g.lineWidth = 2; for (let y = 300; y < 860; y += 62) { g.beginPath(); g.moveTo(1040, y); g.lineTo(1620, y + (y % 124 ? 2 : -2)); g.stroke(); }
45
+ // 拱门:金边环+深洞
46
+ const cx = 1290, top = 330, w = 250;
47
+ g.fillStyle = '#e4d3aa'; g.beginPath(); g.moveTo(cx - w / 2 - 26, 860); g.lineTo(cx - w / 2 - 26, top + w / 2); g.arc(cx, top + w / 2, w / 2 + 26, Math.PI, 0); g.lineTo(cx + w / 2 + 26, 860); g.closePath(); g.fill();
48
+ gr = g.createLinearGradient(0, top, 0, 860); gr.addColorStop(0, '#4a3a30'); gr.addColorStop(1, '#7a634e'); g.fillStyle = gr;
49
+ g.beginPath(); g.moveTo(cx - w / 2, 860); g.lineTo(cx - w / 2, top + w / 2); g.arc(cx, top + w / 2, w / 2, Math.PI, 0); g.lineTo(cx + w / 2, 860); g.closePath(); g.fill();
50
+ g.strokeStyle = '#c9a24b'; g.lineWidth = 7; g.beginPath(); g.arc(cx, top + w / 2, w / 2 + 14, Math.PI, 0); g.stroke();
51
+ // 门洞里的暗处有几袋货
52
+ g.fillStyle = '#6b5846'; [[cx - 70, 800, 54], [cx + 22, 810, 60]].forEach(([x, y, s]) => { g.beginPath(); g.ellipse(x, y, s, s * 0.7, 0, 0, TAU); g.fill(); });
53
+ M.outline(g, [...front, front[0]], { w: [2.4, 3.6, 2.4], col: '#5f4a3d', a: 0.7 }); M.outline(g, [...side, side[0]], { w: [2.4, 3.6, 2.4], col: '#5f4a3d', a: 0.7 });
54
+ // 墙脚台阶
55
+ g.fillStyle = '#b5a592'; g.fillRect(1010, 860, 840, 26); g.fillStyle = '#9a8a78'; g.fillRect(1010, 886, 840, 16);
56
+ }
57
+ function ground(g) {
58
+ const gr = g.createLinearGradient(0, 760, 0, H); gr.addColorStop(0, '#b9a56e'); gr.addColorStop(0.5, '#9a8754'); gr.addColorStop(1, '#6a5a36'); g.fillStyle = gr; g.fillRect(0, 760, W, H - 760);
59
+ // 远处水带(码头边):一条冷青带,岩坡脚下
60
+ const wg = g.createLinearGradient(0, 735, 0, 790); wg.addColorStop(0, '#7f9fb4'); wg.addColorStop(1, '#5f8294'); g.fillStyle = wg; g.fillRect(0, 735, 1010, 52);
61
+ g.strokeStyle = 'rgba(230,240,245,.5)'; g.lineWidth = 2; for (let i = 0; i < 18; i++) { const x = 40 + i * 52, y = 745 + (i % 3) * 14; g.beginPath(); g.moveTo(x, y); g.quadraticCurveTo(x + 14, y - 3, x + 30, y); g.stroke(); }
62
+ // 土路的亮带与车辙
63
+ g.fillStyle = 'rgba(214,194,140,.55)'; g.beginPath(); g.moveTo(0, 820); g.quadraticCurveTo(520, 780, 1010, 830); g.lineTo(1010, 930); g.quadraticCurveTo(500, 900, 0, 960); g.fill();
64
+ const r = rng(5); for (let i = 0; i < 420; i++) { g.fillStyle = `rgba(${r() < 0.5 ? '84,64,36' : '214,198,150'},${0.1 + r() * 0.18})`; g.fillRect(r() * W, 790 + r() * 290, 6 + r() * 44, 2 + r() * 2); }
65
+ // 草簇与石子
66
+ g.strokeStyle = '#4d6a36'; g.lineWidth = 2.4; for (let i = 0; i < 26; i++) { const x = r() * W, y = 960 + r() * 100; for (let k = -2; k <= 2; k++) { g.beginPath(); g.moveTo(x, y); g.quadraticCurveTo(x + k * 5, y - 14, x + k * 9, y - 26 - r() * 8); g.stroke(); } }
67
+ g.fillStyle = '#8c7e66'; for (let i = 0; i < 26; i++) { g.beginPath(); g.ellipse(r() * W, 900 + r() * 170, 7 + r() * 12, 4 + r() * 6, 0, 0, TAU); g.fill(); }
68
+ // 门边的麻袋和木桶(货)
69
+ [[1460, 905, 62], [1550, 915, 54]].forEach(([x, y, s2]) => { const sg = g.createLinearGradient(x - s2, 0, x + s2, 0); sg.addColorStop(0, '#d8c79a'); sg.addColorStop(1, '#a89468'); g.fillStyle = sg; g.beginPath(); g.moveTo(x - s2 * 0.8, y); g.quadraticCurveTo(x - s2, y - s2 * 1.1, x, y - s2 * 1.3); g.quadraticCurveTo(x + s2, y - s2 * 1.1, x + s2 * 0.8, y); g.closePath(); g.fill(); g.strokeStyle = 'rgba(90,70,48,.7)'; g.lineWidth = 3; g.stroke(); g.beginPath(); g.moveTo(x - s2 * 0.35, y - s2 * 1.1); g.quadraticCurveTo(x, y - s2 * 0.9, x + s2 * 0.35, y - s2 * 1.1); g.stroke(); });
70
+ const bgd = g.createLinearGradient(1640, 0, 1760, 0); bgd.addColorStop(0, '#9a6a3c'); bgd.addColorStop(1, '#6e4724'); g.fillStyle = bgd; g.fillRect(1640, 790, 120, 130); g.beginPath(); g.ellipse(1700, 790, 60, 14, 0, 0, TAU); g.fill(); g.strokeStyle = '#3d3a36'; g.lineWidth = 6; [820, 880].forEach(y => { g.beginPath(); g.moveTo(1640, y); g.lineTo(1760, y); g.stroke(); });
71
+ }
72
+ function build() {
73
+ plate = P.canvas(); pg = plate.getContext('2d');
74
+ sky(pg); rocks(pg); building(pg); ground(pg);
75
+ }
76
+ const GROUND = M.plaster('giotto', { base: '#d8cdb6', seed: 9, seams: [[[0, 372], [380, 380], [760, 366], [1120, 378], [1500, 368], [1920, 376]], [[640, 0], [652, 190], [630, 372]]] });
77
+
78
+ // 木箱:有体积(正面+顶面),箱角包铁
79
+ function crate(c, x, y, s, tilt) {
80
+ c.save(); c.translate(x, y); c.rotate(tilt);
81
+ const w = 150 * s, h = 110 * s, d = 28 * s;
82
+ c.fillStyle = '#a77b49'; c.fillRect(-w / 2, -h / 2, w, h);
83
+ c.fillStyle = '#c59a62'; c.beginPath(); c.moveTo(-w / 2, -h / 2); c.lineTo(-w / 2 + d, -h / 2 - d); c.lineTo(w / 2 + d, -h / 2 - d); c.lineTo(w / 2, -h / 2); c.fill();
84
+ c.fillStyle = '#7d5a31'; c.beginPath(); c.moveTo(w / 2, -h / 2); c.lineTo(w / 2 + d, -h / 2 - d); c.lineTo(w / 2 + d, h / 2 - d); c.lineTo(w / 2, h / 2); c.fill();
85
+ c.strokeStyle = 'rgba(70,45,25,.55)'; c.lineWidth = 2 * s; for (let k = 1; k < 4; k++) { c.beginPath(); c.moveTo(-w / 2, -h / 2 + k * h / 4); c.lineTo(w / 2, -h / 2 + k * h / 4); c.stroke(); }
86
+ c.strokeStyle = '#3f3a36'; c.lineWidth = 7 * s; c.strokeRect(-w / 2 + 4, -h / 2 + 4, w - 8, h - 8);
87
+ c.beginPath(); c.moveTo(-w / 2, -h / 2); c.lineTo(w / 2, h / 2); c.stroke();
88
+ M.outline(c, [[-w / 2, -h / 2], [w / 2, -h / 2], [w / 2, h / 2], [-w / 2, h / 2], [-w / 2, -h / 2]], { w: [2.4, 3.2, 2.4], col: '#4a2f22', a: 0.8 });
89
+ c.restore();
90
+ }
91
+
92
+ function draw(c, lt, t) {
93
+ if (!plate) build();
94
+ const tt = clamp(lt, 0, 6);
95
+ const hand = ease((tt - 0.6) / 3.2); // 箱子从左到右交接(0.6→3.8s),之后右边的人接稳、放下
96
+ const settle = ease((tt - 3.8) / 1.4);
97
+ const g = pg;
98
+ // 动态的形画到 plate 副本:背景 plate 不动,人物每帧画在 live 上
99
+ const live = P.scratch('giotto_live'), lg = live.getContext('2d'); lg.reset(); lg.drawImage(plate, 0, 0);
100
+ // 人物:左搬运工(赭红袍+深绿披风)、右搬运工(橄榄绿袍+土红披风)、门边低头者(青灰袍)
101
+ const bx = lerp(800, 890, hand), by = lerp(735, 748, hand) + Math.sin(hand * Math.PI) * -34 + settle * 38, tilt = Math.sin(hand * Math.PI) * -0.07;
102
+ const boxTop = [bx, by];
103
+ M.figure(lg, { hemK: 0.82, halo: true, beard: false, x: 745, y: 905, h: 480, face: 1, lean: 0.10 + 0.07 * (1 - ease(hand * 1.4)), bow: 0.4, t: tt, seed: 1, sway: 0.3,
104
+ robe: ['#f0a98c', '#cf5f4a', '#8c3430'], mantle: ['#7da07c', '#4f7456', '#2f4f3c'], under: '#ecdcae', skin: '#e2b48a', hair: '#5b3a22',
105
+ gaze: [bx + 30, by - 20], hands: [[bx - 74, by + 14], [bx - 74, by - 24]] });
106
+ M.figure(lg, { hemK: 1.12, halo: true, beard: false, x: 1030, y: 915, h: 500, face: -1, lean: 0.06 + 0.1 * ss(0.2, 0.9, hand), bow: 0.5, t: tt, seed: 2, sway: 0.3,
107
+ robe: ['#bccb8d', '#86a05c', '#4c6a3a'], mantle: ['#d28a6e', '#a8553f', '#6f3329'], under: '#efe2b8', skin: '#d9a97e', hair: '#3b2a1c',
108
+ gaze: [bx, by - 12], hands: [[bx + 74, by + 16 + settle * 6], [bx + 74, by - 24]] });
109
+ crate(lg, bx, by, 1, tilt);
110
+ // 前景手要压在箱前:重画两只手
111
+ // 第三人:站在左岩坡边,低头
112
+ M.figure(lg, { hemK: 0.7, halo: true, x: 330, y: 885, h: 430, face: 1, lean: 0.05, bow: 0.9, t: tt, seed: 3, sway: 0.35,
113
+ robe: ['#b8c6d0', '#8399ab', '#53677d'], under: '#e9dcb4', skin: '#dcae85', hair: '#6b4b30', gaze: [bx - 120, by + 20], hands: [[430, 720], [445, 762]] });
114
+ // 箱子落地时扬起的一点灰
115
+ const dust = ss(3.7, 4.0, tt) * (1 - ss(4.0, 5.4, tt));
116
+ if (dust > 0) { lg.fillStyle = `rgba(210,190,150,${0.35 * dust})`; for (let i = 0; i < 9; i++) { const a = i / 9 * TAU; lg.beginPath(); lg.ellipse(bx + Math.cos(a) * 90 * (0.5 + dust), 868 + Math.sin(a) * 8, 22, 9, 0, 0, TAU); lg.fill(); } }
117
+ // 一只鸟横过天(慢慢,不抢戏)
118
+ const bt = ss(0.2, 5.8, tt), bxp = lerp(-60, 1000, bt), byp = 130 + Math.sin(bt * 9) * 14;
119
+ lg.strokeStyle = 'rgba(240,235,220,.9)'; lg.lineWidth = 3; lg.beginPath(); lg.moveTo(bxp - 16, byp + Math.sin(tt * 11) * 6); lg.quadraticCurveTo(bxp, byp - 8, bxp + 16, byp + Math.sin(tt * 11 + 1) * 6); lg.stroke();
120
+
121
+ M.fresco(c, live, { ground: GROUND, chalk: 0.07, soften: 0.7, plasterAlpha: 0.5, sat: 1.12 });
122
+ // 装饰边框:深红带+金线+菱形 cosmati 纹(原作每幅都被这样一条带框住)
123
+ c.save(); c.lineWidth = 1;
124
+ c.fillStyle = '#7b2f2a'; c.fillRect(0, 0, W, 34); c.fillRect(0, H - 34, W, 34); c.fillRect(0, 0, 34, H); c.fillRect(W - 34, 0, 34, H);
125
+ c.strokeStyle = '#d7b35c'; c.lineWidth = 3; c.strokeRect(34, 34, W - 68, H - 68);
126
+ c.fillStyle = '#e8d6a0'; for (let x = 60; x < W - 40; x += 44) { [[x, 17], [x, H - 17]].forEach(([a, b]) => { c.beginPath(); c.moveTo(a, b - 8); c.lineTo(a + 10, b); c.lineTo(a, b + 8); c.lineTo(a - 10, b); c.fill(); }); }
127
+ for (let y = 60; y < H - 40; y += 44) { [[17, y], [W - 17, y]].forEach(([a, b]) => { c.beginPath(); c.moveTo(a, b - 10); c.lineTo(a + 8, b); c.lineTo(a, b + 10); c.lineTo(a - 8, b); c.fill(); }); }
128
+ c.restore();
129
+ // 边缘剥落:灰泥外圈略亮、角上几处脱落(只在边框与天空,不碰人物)
130
+ c.save(); c.globalCompositeOperation = 'screen'; const vg = c.createRadialGradient(W / 2, H / 2, 600, W / 2, H / 2, 1180); vg.addColorStop(0, 'rgba(255,245,225,0)'); vg.addColorStop(1, 'rgba(255,245,225,.12)'); c.fillStyle = vg; c.fillRect(0, 0, W, H); c.restore();
131
+ }
132
+ return { draw };
133
+ })();
@@ -0,0 +1,53 @@
1
+ SCENES['39_piero']=(()=>{
2
+ const TAU=Math.PI*2; let bg,grain,raw;
3
+ const rng=(s)=>()=>{s=(Math.imul(s,1664525)+1013904223)|0;return (s>>>0)/4294967296;};
4
+ function path(c,d,col,st,lw=2){const p=new Path2D(d);if(col){c.fillStyle=col;c.fill(p);}if(st){c.strokeStyle=st;c.lineWidth=lw;c.stroke(p);}return p;}
5
+ function poly(c,pts,col,st,lw=2){c.beginPath();pts.forEach((p,i)=>i?c.lineTo(...p):c.moveTo(...p));c.closePath();if(col){c.fillStyle=col;c.fill();}if(st){c.strokeStyle=st;c.lineWidth=lw;c.stroke();}}
6
+ function el(c,x,y,rx,ry,col,st,lw=2){c.beginPath();c.ellipse(x,y,rx,ry,0,0,TAU);if(col){c.fillStyle=col;c.fill();}if(st){c.strokeStyle=st;c.lineWidth=lw;c.stroke();}}
7
+ function line(c,pts,col,w=2){c.beginPath();pts.forEach((p,i)=>i?c.lineTo(...p):c.moveTo(...p));c.strokeStyle=col;c.lineWidth=w;c.stroke();}
8
+ function grad(c,x,y,X,Y,colors){const g=c.createLinearGradient(x,y,X,Y);colors.forEach((a,i)=>g.addColorStop(i/(colors.length-1),a));return g;}
9
+ function layer(fn){let a=document.createElement('canvas');a.width=1920;a.height=1080;fn(a.getContext('2d'));return a;}
10
+ function finish(c){if(!grain)grain=layer(g=>{let r=rng(7891);for(let i=0;i<21000;i++){g.fillStyle=i%3?'rgba(49,32,22,.045)':'rgba(255,248,220,.08)';let x=r()*1920,y=r()*1080;g.fillRect(x,y,1+r()*2.4,1+r()*1.5);}});c.drawImage(grain,0,0);}
11
+
12
+ function back(c){
13
+ c.fillStyle=grad(c,0,0,0,1080,['#c3d4d3','#e0e0cd']);c.fillRect(0,0,1920,1080);
14
+ // A contemporary measuring hall, ordered through repeated intervals rather than antique iconography.
15
+ c.fillStyle='#d9d2b9';c.fillRect(0,80,1920,660);
16
+ for(let x=100;x<1920;x+=420){c.fillStyle='#718c8c';c.fillRect(x,153,274,451);c.fillStyle=grad(c,0,150,0,610,['#b5cccf','#edf0d8']);c.fillRect(x+14,166,246,424);for(let j=0;j<3;j++)line(c,[[x+15,270+j*105],[x+259,270+j*105]],'#d7d8c4',7);line(c,[[x+138,165],[x+138,590]],'#d6d8c6',9);}
17
+ poly(c,[[0,0],[1920,0],[1920,84],[0,84]],'#6b827e');c.fillStyle='#eee5cd';c.fillRect(0,78,1920,23);
18
+ for(let x=20;x<1920;x+=420){c.fillStyle=grad(c,x,0,x+64,0,['#b1b19b','#e8e0c7','#c4bea8']);c.fillRect(x,101,64,620);c.fillStyle='#b3ac97';c.fillRect(x-10,690,85,37);}
19
+ poly(c,[[0,735],[1920,735],[1920,1080],[0,1080]],'#d2c5a5');
20
+ const vx=1010,vy=550;for(let x=-1800;x<4200;x+=260)line(c,[[vx+(x-vx)*.38,735],[x,1080]],'#a89e85',2);for(let y of [762,803,861,945,1060])line(c,[[0,y],[1920,y]],'#a89e85',2);
21
+ // Stacked instruments inhabit different measured planes.
22
+ poly(c,[[1310,718],[1650,682],[1820,754],[1485,811]],'#d3d0b8','#9a9f90',2);poly(c,[[1485,811],[1820,754],[1820,970],[1485,1042]],'#a6b0a3');poly(c,[[1310,718],[1485,811],[1485,1042],[1310,935]],'#b8b9a3');
23
+ for(let y=836;y<1010;y+=42)line(c,[[1504,y],[1793,y-60]],'#81968f',3);
24
+ c.fillStyle='#b7a889';c.fillRect(120,689,375,226);poly(c,[[120,689],[354,638],[535,682],[300,739]],'#e5d5b0');poly(c,[[300,739],[535,682],[535,910],[300,966]],'#8e9d90');
25
+ let r=rng(391);for(let i=0;i<1400;i++){c.fillStyle=i%2?'rgba(255,255,228,.06)':'rgba(70,91,83,.04)';c.fillRect(r()*1920,r()*1080,1+r()*18,1);}
26
+ }
27
+ function sphere(c,x,y,r,col){let g=c.createRadialGradient(x-r*.4,y-r*.45,2,x,y,r);g.addColorStop(0,'#f3ebcc');g.addColorStop(.45,col);g.addColorStop(1,'#647d77');el(c,x,y,r,r,g);}
28
+ return {draw(dst,lt){if(!raw)raw=layer(()=>{});const c=raw.getContext('2d',{willReadFrequently:true});c.reset();if(!bg)bg=layer(back);c.drawImage(bg,0,0);const t=lt;
29
+ el(c,878,927,341,62,'rgba(85,100,88,.14)');
30
+ // Plinth, precisely vertical axis and a suspended mechanical planetary model.
31
+ poly(c,[[560,868],[898,802],[1180,880],[829,971]],'#ded8bb','#88988a',2);poly(c,[[560,868],[829,971],[829,1053],[560,947]],'#b2b79e');poly(c,[[829,971],[1180,880],[1180,964],[829,1053]],'#8e9c8b');
32
+ c.fillStyle=grad(c,835,0,902,0,['#7c968b','#d8d9bd','#a4b19c']);c.fillRect(835,533,67,359);el(c,868,893,113,29,'#bec4aa','#819589',3);
33
+ c.save();c.translate(868,444);
34
+ // Meridian rings, with alternate back and front arcs around the central orb.
35
+ c.save();c.rotate(-.35+t*.18);for(let i=0;i<3;i++){c.beginPath();c.ellipse(0,0,287-i*7,310-i*7,0,0,TAU);c.strokeStyle=i===1?'#d9c79a':'#7e8a71';c.lineWidth=i===1?13:3;c.stroke();}for(let j=0;j<64;j++){let a=j*TAU/64;line(c,[[Math.cos(a)*292,Math.sin(a)*315],[Math.cos(a)*(j%4?280:272),Math.sin(a)*(j%4?303:295)]],'#8a8f74',2);}c.restore();
36
+ const a=t*.43,px=Math.cos(a)*251,py=Math.sin(a)*87;
37
+ c.beginPath();c.ellipse(0,0,318,111,-.15,Math.PI,TAU);c.strokeStyle='#a89e79';c.lineWidth=10;c.stroke();
38
+ line(c,[[0,0],[px,py]],'#8f987d',9);if(py<0)sphere(c,px,py,43,'#b87959');
39
+ sphere(c,0,0,119,'#c5b979');
40
+ // Very restrained latitude bands maintain rounded, clear volume.
41
+ c.save();c.beginPath();c.arc(0,0,119,0,TAU);c.clip();for(let y=-80;y<110;y+=40)el(c,0,y,118,25,null,'rgba(100,120,94,.17)',2);c.restore();
42
+ if(py>=0)sphere(c,px,py,43,'#b87959');
43
+ c.beginPath();c.ellipse(0,0,318,111,-.15,0,Math.PI);c.strokeStyle='#d0bd8f';c.lineWidth=10;c.stroke();
44
+ const b=-t*.69+1.4;let qx=Math.cos(b)*173,qy=Math.sin(b)*260;
45
+ c.save();c.rotate(.68);c.beginPath();c.ellipse(0,0,173,260,0,0,TAU);c.strokeStyle='#9da98d';c.lineWidth=5;c.stroke();sphere(c,qx,qy,20,'#9ab6af');c.restore();
46
+ el(c,0,-325,10,10,'#a39d76');el(c,0,325,10,10,'#a39d76');c.restore();
47
+ // Displaced secondary objects form unequal but balanced intervals.
48
+ sphere(c,1606,640,85,'#9aafa3');el(c,1606,727,91,13,'#8d9a88');
49
+ c.save();c.translate(327,625);c.rotate(-.12+Math.sin(t*.45)*.045);poly(c,[[-125,-12],[108,-12],[108,16],[-125,16]],'#83988e','#687d76',2);el(c,111,0,14,35,'#5e7877','#c5c8af',7);for(let i=-105;i<100;i+=22)line(c,[[i,-9],[i,4]],'#d9d9be',2);c.restore();
50
+ for(let i=0;i<24;i++){let y=330+(i*39)%400,x=200+(i*127)%1450;el(c,x+Math.sin(t*.35+i)*6,y-((t*5+i*13)%27),1.2,1.2,'rgba(255,255,230,.32)');}
51
+ finish(c);dst.drawImage(raw,0,0);dst.save();dst.globalAlpha=0.18;PAINT.strokes(dst,raw,{cell:7,len:9,width:4,jitterCol:5,angle:(x,y)=>-.45,seed:39,boil:0,outline:0});dst.restore();
52
+ }};
53
+ })();