@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,269 @@
1
+ // 1962 波普(利希滕斯坦漫画格+沃霍尔四宫格)——纯代码。
2
+ // 管线:①缓存底版(米白漫画格+45°本戴网点蓝墙+白踢脚线+黄地+红带+MEANWHILE 框)
3
+ // → ②会动的母题(沃霍尔四格猫按拍换配色、思考泡泡三点依次闪、WIGGLE 爆炸框抖、猫扭屁股)
4
+ // → ③角色:粗黑勾线平涂+皮肤红色本戴点(createPattern,零成本)+高光白条 + 斜线阴影
5
+ SCENES['13_pop'] = (() => {
6
+ const W = 1920, H = 1080, { clamp, lerp } = U, P = PAINT, TAU = Math.PI * 2;
7
+ const C = { ink: '#141414', paper: '#f3eedb', blue: '#2f5cc8', yellow: '#f7d52c', red: '#d8232a', redD: '#a8161c', white: '#fbf8ee',
8
+ skin: '#fae0cc', dotRed: '#e2483c', hair: '#f8d62c', hairD: '#d9a514', orange: '#f39a2c', orangeD: '#c4561a' };
9
+ const hash = (i, j = 0) => U.hash(i, j);
10
+
11
+ // 本戴点图案:方格 + 中心点 = 45° 网格。pitch 是方格边长
12
+ const dotPattern = (c, key, pitch, r, col, bg) => {
13
+ const tile = P.cached('pop_tile_' + key, pitch, pitch, (g) => { if (bg) { g.fillStyle = bg; g.fillRect(0, 0, pitch, pitch); } g.fillStyle = col;
14
+ [[0, 0], [pitch, 0], [0, pitch], [pitch, pitch], [pitch / 2, pitch / 2]].forEach(([x, y]) => { g.beginPath(); g.arc(x, y, r, 0, TAU); g.fill(); }); });
15
+ return c.createPattern(tile, 'repeat');
16
+ };
17
+ // 斜线阴影(漫画的投影)
18
+ const hatch = (c, cx, cy, rx, ry, gap = 10, w = 3.5) => { c.save(); c.beginPath(); c.ellipse(cx, cy, rx, ry, 0, 0, TAU); c.clip();
19
+ c.strokeStyle = C.ink; c.lineWidth = w; c.beginPath(); for (let x = cx - rx - ry * 2; x < cx + rx + ry; x += gap) { c.moveTo(x, cy + ry); c.lineTo(x + ry * 2.2, cy - ry); } c.stroke(); c.restore(); };
20
+ const ink = (c, p, fill, lw = 5) => { if (fill) { c.fillStyle = fill; c.fill(p); } c.strokeStyle = C.ink; c.lineWidth = lw; c.lineJoin = 'round'; c.lineCap = 'round'; c.stroke(p); };
21
+ const poly = (pts, closed = true) => { const p = new Path2D(); pts.forEach((q, i) => i ? p.lineTo(q[0], q[1]) : p.moveTo(q[0], q[1])); if (closed) p.closePath(); return p; };
22
+
23
+ // ---------- ① 底版 ----------
24
+ const bg = () => P.cached('pop_bg', W, H, (g) => {
25
+ g.fillStyle = C.paper; g.fillRect(0, 0, W, H);
26
+ // 蓝网点墙
27
+ g.fillStyle = dotPattern(g, 'wall', 24, 5.6, C.blue, C.white); g.fillRect(20, 16, W - 40, 738);
28
+ // 踢脚线、黄地、红带
29
+ g.fillStyle = C.white; g.fillRect(20, 754, W - 40, 38);
30
+ g.fillStyle = C.ink; g.fillRect(20, 750, W - 40, 6); g.fillRect(20, 790, W - 40, 6);
31
+ g.fillStyle = C.yellow; g.beginPath(); g.moveTo(20, 796); g.lineTo(W - 20, 796); g.lineTo(W - 20, 990); g.lineTo(20, 1003); g.closePath(); g.fill();
32
+ // 黄地上浅浅的网点(印刷感)
33
+ g.save(); g.clip(); g.globalAlpha = 0.18; g.fillStyle = dotPattern(g, 'floor', 14, 2.2, '#c99a10'); g.fillRect(20, 796, W - 40, 210); g.restore();
34
+ g.fillStyle = C.red; g.beginPath(); g.moveTo(20, 1003); g.lineTo(W - 20, 990); g.lineTo(W - 20, 1064); g.lineTo(20, 1064); g.closePath(); g.fill();
35
+ g.strokeStyle = C.ink; g.lineWidth = 6; g.beginPath(); g.moveTo(20, 1003); g.lineTo(W - 20, 990); g.stroke();
36
+ // 漫画格外框
37
+ g.strokeStyle = C.ink; g.lineWidth = 6; g.strokeRect(17, 14, W - 34, H - 28);
38
+ // MEANWHILE...
39
+ g.fillStyle = C.yellow; g.fillRect(32, 32, 272, 70); g.lineWidth = 5; g.strokeRect(32, 32, 272, 70);
40
+ g.font = '46px "Bangers-400"'; g.fillStyle = C.ink; g.textBaseline = 'middle'; g.letterSpacing = '1px'; g.fillText('MEANWHILE...', 50, 70); g.letterSpacing = '0px';
41
+ // 沃霍尔画框(白框)+窗台
42
+ g.fillStyle = C.white; g.fillRect(350, 110, 462, 472); g.lineWidth = 5; g.strokeRect(350, 110, 462, 472);
43
+ g.fillStyle = C.white; g.fillRect(334, 574, 496, 22); g.strokeRect(334, 574, 496, 22); g.fillStyle = C.blue; g.fillRect(338, 592, 488, 4);
44
+ // 椅子(蓝)
45
+ const blue = '#2448b0';
46
+ [[1452, 478, 24, 420], [1300, 760, 18, 140], [1460, 760, 18, 140]].forEach(([x, y, w, h]) => { g.fillStyle = blue; g.fillRect(x, y, w, h); g.lineWidth = 4; g.strokeRect(x, y, w, h); });
47
+ g.fillStyle = blue; g.fillRect(1282, 740, 210, 24); g.strokeRect(1282, 740, 210, 24);
48
+ [[1440, 560], [1440, 640]].forEach(([x, y]) => { g.fillRect(x, y, 30, 12); });
49
+ g.fillStyle = '#6f8ee8'; g.fillRect(1458, 486, 6, 400);
50
+ // 纸面颗粒
51
+ g.drawImage(P.grain('pop', 0.035, [60, 50, 30], 0.12), 0, 0);
52
+ });
53
+
54
+ // ---------- ② 沃霍尔四格猫 ----------
55
+ const SCHEMES = [
56
+ { bg: '#9ccf3a', face: '#e0302a', low: '#9a6ad0', eye: '#3a7ad8', lip: '#f06a3a' },
57
+ { bg: '#e0359a', face: '#f0902a', low: '#78d6e6', eye: '#2a9a5a', lip: '#e0302a' },
58
+ { bg: '#2fc0b0', face: '#f27fb2', low: '#f8e040', eye: '#7a3ac8', lip: '#e0302a' },
59
+ { bg: '#f08a2a', face: '#f8d830', low: '#f6a2c4', eye: '#2ab0d8', lip: '#e0302a' },
60
+ ];
61
+ function warholCat(c, cx, cy, s) {
62
+ c.save(); c.translate(cx, cy);
63
+ const head = poly([[-70, -14], [-64, -78], [-30, -44], [30, -44], [64, -78], [70, -14], [62, 32], [30, 58], [-30, 58], [-62, 32]]);
64
+ const hd = RIG.smooth([[-70, -10], [-66, -76], [-28, -46], [28, -46], [66, -76], [70, -10], [60, 34], [0, 62], [-60, 34]], true, 0.35);
65
+ c.fillStyle = s.face; c.fill(hd);
66
+ c.save(); c.clip(hd); c.fillStyle = s.low; c.beginPath(); c.ellipse(4, 34, 58, 30, 0, 0, TAU); c.fill();
67
+ c.fillStyle = 'rgba(20,20,20,.88)'; c.beginPath(); c.moveTo(40, -40); c.quadraticCurveTo(76, 0, 52, 60); c.lineTo(90, 60); c.lineTo(90, -40); c.fill(); c.restore(); // 丝网错版的黑块
68
+ c.strokeStyle = C.ink; c.lineWidth = 5; c.lineJoin = 'round'; c.stroke(hd);
69
+ // 眼影+眼
70
+ [-26, 26].forEach(x => { c.fillStyle = s.eye; c.beginPath(); c.ellipse(x, -10, 19, 13, 0, Math.PI, TAU); c.fill();
71
+ c.fillStyle = '#fff'; c.beginPath(); c.ellipse(x, -4, 14, 9, 0, 0, TAU); c.fill(); c.lineWidth = 3; c.stroke();
72
+ c.fillStyle = C.ink; c.beginPath(); c.arc(x + 2, -4, 6, 0, TAU); c.fill(); c.lineWidth = 3.5; c.beginPath(); c.moveTo(x - 16, -10); c.lineTo(x - 22, -18); c.moveTo(x + 14, -12); c.lineTo(x + 20, -19); c.stroke(); });
73
+ // 嘴(口红)、鼻、胡须
74
+ c.fillStyle = s.lip; c.beginPath(); c.moveTo(-14, 30); c.quadraticCurveTo(0, 22, 14, 30); c.quadraticCurveTo(0, 44, -14, 30); c.fill(); c.lineWidth = 2.5; c.stroke();
75
+ c.fillStyle = C.ink; c.beginPath(); c.moveTo(-6, 14); c.lineTo(6, 14); c.lineTo(0, 21); c.fill();
76
+ c.lineWidth = 2; c.beginPath(); [[-1, 18], [1, 22], [-1, 26]].forEach(([d, y]) => { c.moveTo(-36, y); c.lineTo(-74, y - 4 + d * 2); c.moveTo(36, y); c.lineTo(74, y - 4 + d * 2); }); c.stroke();
77
+ // 耳内
78
+ c.fillStyle = C.ink; c.beginPath(); c.moveTo(-58, -64); c.lineTo(-56, -38); c.lineTo(-38, -44); c.fill();
79
+ c.restore();
80
+ }
81
+ function warhol(c, lt) {
82
+ const step = Math.floor(clamp(lt, 0, 9) * 11); // ~每 5–6 帧换一次配色
83
+ const cells = [[372, 132], [585, 132], [372, 350], [585, 350]];
84
+ cells.forEach(([x, y], i) => { const s = SCHEMES[(i + step) % 4];
85
+ c.fillStyle = s.bg; c.fillRect(x, y, 206, 212); warholCat(c, x + 103, y + 116, s);
86
+ c.strokeStyle = C.ink; c.lineWidth = 4; c.strokeRect(x, y, 206, 212); });
87
+ // 玻璃反光
88
+ c.strokeStyle = 'rgba(255,255,255,.85)'; c.lineWidth = 6; c.beginPath(); c.moveTo(712, 140); c.lineTo(660, 222); c.moveTo(736, 140); c.lineTo(684, 222); c.stroke();
89
+ }
90
+
91
+ // ---------- 思考泡泡(三点依次闪) ----------
92
+ const cloud = [[975, 220, 62], [1040, 180, 64], [1110, 170, 62], [1180, 200, 58], [1210, 250, 50], [1160, 292, 48], [1090, 300, 52], [1010, 292, 50], [960, 262, 46], [1080, 240, 90]];
93
+ function bubble(c, lt) {
94
+ c.lineWidth = 11; c.strokeStyle = C.ink; cloud.forEach(([x, y, r]) => { c.beginPath(); c.arc(x, y, r, 0, TAU); c.stroke(); });
95
+ c.fillStyle = C.white; cloud.forEach(([x, y, r]) => { c.beginPath(); c.arc(x, y, r, 0, TAU); c.fill(); });
96
+ [[1196, 334, 18], [1236, 338, 12]].forEach(([x, y, r]) => { c.beginPath(); c.arc(x, y, r, 0, TAU); c.fillStyle = C.white; c.fill(); c.lineWidth = 5; c.stroke(); });
97
+ const f = lt * 60;
98
+ [1025, 1082, 1139].forEach((x, k) => { const ph = ((f - 2 * k) % 12 + 12) % 12, on = ph < 4;
99
+ c.fillStyle = C.ink; c.beginPath(); c.arc(x, 242 - (on ? 6 : 0), on ? 20 : 12, 0, TAU); c.fill(); });
100
+ }
101
+
102
+ // ---------- WIGGLE WIGGLE 爆炸框 ----------
103
+ const burst = (cx, cy, r0, r1, n, seed) => { const pts = []; for (let i = 0; i < n * 2; i++) { const a = i / (n * 2) * TAU + 0.1, r = i % 2 ? r0 * (0.92 + hash(i, seed) * 0.12) : r1 * (0.85 + hash(i, seed + 1) * 0.3); pts.push([cx + Math.cos(a) * r * 1.12, cy + Math.sin(a) * r * 0.8]); } return poly(pts); };
104
+ function wiggleBox(c, lt) {
105
+ const st = Math.floor(lt * 30), jx = (hash(st, 3) - 0.5) * 16, jy = (hash(st, 7) - 0.5) * 12, rot = (hash(st, 11) - 0.5) * 0.06;
106
+ const cx = 222, cy = 690;
107
+ // 放射动作线(黑三角)
108
+ c.fillStyle = C.ink;
109
+ [[-2.45, 1], [-2.0, 0.9], [-1.62, 1.1], [-1.25, 0.8], [2.2, 1], [1.85, 0.9], [1.5, 1.1], [1.15, 0.8], [2.7, 0.7], [-2.85, 0.8]].forEach(([a, k], i) => {
110
+ const pl = 0.85 + 0.25 * Math.sin(lt * 40 + i * 2), r0 = 230 * k, r1 = r0 + 90 * pl, w = 0.06;
111
+ c.beginPath(); c.moveTo(cx + Math.cos(a - w) * r0 * 1.12, cy + Math.sin(a - w) * r0 * 0.9); c.lineTo(cx + Math.cos(a) * r1 * 1.12, cy + Math.sin(a) * r1 * 0.9); c.lineTo(cx + Math.cos(a + w) * r0 * 1.12, cy + Math.sin(a + w) * r0 * 0.9); c.fill(); });
112
+ c.save(); c.translate(cx + jx, cy + jy); c.rotate(rot); c.translate(-cx, -cy);
113
+ const outer = burst(cx, cy, 128, 178, 14, 1); c.fillStyle = C.white; c.fill(outer); c.lineWidth = 6; c.strokeStyle = C.ink; c.stroke(outer);
114
+ const inner = burst(cx, cy, 104, 140, 12, 5); c.fillStyle = C.yellow; c.fill(inner);
115
+ c.font = '82px "Bangers-400"'; c.textAlign = 'center'; c.textBaseline = 'middle'; c.lineJoin = 'round';
116
+ c.save(); c.translate(cx, cy); c.rotate(-0.08); c.letterSpacing = '2px';
117
+ [['WIGGLE', -34], ['WIGGLE', 40]].forEach(([s, y], i) => { c.lineWidth = 12; c.strokeStyle = C.ink; c.strokeText(s, i * 14 - 4, y + 5); c.fillStyle = C.ink; c.fillText(s, i * 14 - 4 + 4, y + 9); c.fillStyle = C.red; c.fillText(s, i * 14 - 4, y + 5); });
118
+ c.restore(); c.restore();
119
+ }
120
+
121
+ // ---------- 桌、杯 ----------
122
+ function table(c) {
123
+ hatch(c, 1010, 902, 150, 18);
124
+ c.fillStyle = C.ink; c.beginPath(); c.moveTo(985, 668); c.lineTo(1015, 668); c.lineTo(1024, 860); c.lineTo(976, 860); c.closePath(); c.fill();
125
+ c.beginPath(); c.moveTo(920, 900); c.quadraticCurveTo(930, 852, 1000, 848); c.quadraticCurveTo(1070, 852, 1080, 900); c.closePath(); c.fill();
126
+ c.strokeStyle = '#4a7ae8'; c.lineWidth = 5; c.lineCap = 'round'; c.beginPath(); c.moveTo(1030, 862); c.quadraticCurveTo(1062, 870, 1068, 892); c.moveTo(1010, 690); c.lineTo(1014, 830); c.stroke();
127
+ const top = poly([[822, 640], [1200, 640], [1196, 664], [826, 668]]); ink(c, top, C.red, 5);
128
+ c.strokeStyle = C.white; c.lineWidth = 5; c.beginPath(); c.moveTo(850, 650); c.lineTo(960, 650); c.moveTo(985, 650); c.lineTo(1010, 650); c.stroke();
129
+ }
130
+ function cup(c, G, t) {
131
+ const k = G.cup; c.save(); c.translate(k.x, k.y); c.rotate(k.tilt); const w = 50, h = 48;
132
+ const body = poly([[-w / 2, 0], [-w * 0.46, h], [w * 0.46, h], [w / 2, 0]]);
133
+ c.fillStyle = C.white; c.fill(body);
134
+ c.save(); c.clip(body); c.strokeStyle = '#2f5cc8'; c.lineWidth = 5; for (let x = 4; x < 50; x += 11) { c.beginPath(); c.moveTo(x, 0); c.lineTo(x - 18, h); c.stroke(); } c.restore();
135
+ c.strokeStyle = C.ink; c.lineWidth = 4; c.stroke(body); c.beginPath(); c.arc(w * 0.6, h * 0.45, 14, -1.3, 1.4); c.lineWidth = 6; c.stroke();
136
+ c.fillStyle = '#5a3016'; c.beginPath(); c.ellipse(0, 1, w / 2 - 2, 6, 0, 0, TAU); c.fill(); c.lineWidth = 3.5; c.stroke();
137
+ c.restore();
138
+ // 漫画热气:两条交叉的黑色波浪
139
+ c.save(); c.strokeStyle = C.ink; c.lineWidth = 4; c.lineCap = 'round';
140
+ [[-8, 0], [10, 2]].forEach(([dx, ph]) => { c.beginPath(); for (let i = 0; i <= 14; i++) { const q = i / 14, x = k.x + dx + Math.sin(q * 7 + ph + t * 14) * 9 * (dx < 0 ? 1 : -1), y = k.y - 14 - q * 62; i ? c.lineTo(x, y) : c.moveTo(x, y); } c.stroke(); });
141
+ c.restore();
142
+ }
143
+
144
+ // ---------- 少女(利希滕斯坦) ----------
145
+ function girl(c, G, lt, t, ch) {
146
+ const skinDots = dotPattern(c, 'skin', 13, 3.3, C.dotRed, C.skin);
147
+ const fillSkin = (p) => { c.fillStyle = skinDots; c.fill(p); c.strokeStyle = C.ink; c.lineWidth = 5; c.stroke(p); };
148
+ const a = G.A, h = a.headC, sh = [h[0] - 1318, h[1] - 360];
149
+ // 大波浪发团(背后)
150
+ const sway = Math.sin(t * 5) * 6;
151
+ // 外缘做成一串大波浪(利希滕斯坦的金发):沿后缘按弧长加 sin 鼓包
152
+ const spine = [[1262, 300], [1290, 262], [1346, 250], [1404, 276], [1440, 330], [1462, 396], [1492, 452], [1522, 510], [1540, 570], [1520, 624]];
153
+ const inner = [[1488, 640], [1452, 610], [1420, 628], [1392, 596], [1372, 548], [1356, 496], [1342, 452], [1330, 418], [1320, 392]];
154
+ const wav = spine.map(([x, y], i) => i < 4 ? [x, y] : [x + Math.sin(i * 2.1 + t * 4) * 14 + 10, y + Math.cos(i * 2.1) * 6]);
155
+ const hairPts = wav.concat(inner).map(([x, y]) => [x + sh[0] * (y < 430 ? 1 : 0.4) + (y > 430 ? sway * (y - 430) / 180 : 0), y + sh[1] * (y < 430 ? 1 : 0.4)]);
156
+ const hair = RIG.smooth(hairPts, true, 0.75);
157
+ // 大波浪:发团+沿后缘的一串卷团(先统一描粗黑边再统一填黄 = 并集轮廓)
158
+ const lobes = [[1440, 360, 44], [1478, 430, 46], [1500, 505, 48], [1520, 578, 44], [1478, 622, 38], [1430, 606, 34], [1400, 560, 30]]
159
+ .map(([x, y, r], i) => [x + sh[0] * (y < 430 ? 1 : 0.4) + sway * Math.max(0, y - 430) / 180 + Math.sin(t * 6 + i) * 3, y + sh[1] * (y < 430 ? 1 : 0.4), r]);
160
+ c.strokeStyle = C.ink; c.lineWidth = 10; c.stroke(hair); lobes.forEach(([x, y, r]) => { c.beginPath(); c.arc(x, y, r, 0, TAU); c.stroke(); });
161
+ c.fillStyle = C.hair; c.fill(hair); lobes.forEach(([x, y, r]) => { c.beginPath(); c.arc(x, y, r, 0, TAU); c.fill(); });
162
+ c.lineWidth = 4.5; c.lineCap = 'round'; lobes.forEach(([x, y, r], i) => { c.beginPath(); c.arc(x - 4, y + 2, r * 0.58, i % 2 ? -2.6 : -0.6, i % 2 ? 0.9 : 2.9); c.stroke(); });
163
+ c.strokeStyle = C.hairD; c.lineWidth = 7; lobes.forEach(([x, y, r]) => { c.beginPath(); c.arc(x, y, r - 6, 0.2, 1.2); c.stroke(); });
164
+ // 远侧手臂(桌上)
165
+ ink(c, G.farSleeve, C.red); fillSkin(G.farHand);
166
+ // 腿+红高跟
167
+ const knee = [1222, 760], ank = [1196, 878];
168
+ const leg = RIG.taper(knee, ank, 46, 30, 4); fillSkin(leg);
169
+ const leg2 = RIG.taper([1262, 772], [1236, 878], 44, 28, 4); fillSkin(leg2);
170
+ const shoe = (x, y) => { const p = poly([[x - 40, y + 2], [x - 30, y - 16], [x + 6, y - 14], [x + 16, y + 20], [x + 8, y + 22], [x - 2, y + 4], [x - 26, y + 12]]); ink(c, p, C.red, 4.5); c.strokeStyle = C.white; c.lineWidth = 3; c.beginPath(); c.moveTo(x - 26, y - 8); c.lineTo(x - 6, y - 8); c.stroke(); };
171
+ shoe(1236, 884); shoe(1196, 884);
172
+ // 裙(截短到膝)
173
+ c.save(); c.beginPath(); c.moveTo(0, 0); c.lineTo(W, 0); c.lineTo(W, 786); c.lineTo(1400, 786); c.quadraticCurveTo(1280, 806, 1150, 790); c.lineTo(0, 790); c.closePath(); c.clip();
174
+ ink(c, G.skirt, C.red, 5); c.restore();
175
+ c.strokeStyle = C.ink; c.lineWidth = 5; c.beginPath(); c.moveTo(1150, 790); c.quadraticCurveTo(1280, 806, 1400, 786); c.stroke();
176
+ ink(c, G.torso, C.red, 5);
177
+ // 高光白条
178
+ c.strokeStyle = C.white; c.lineWidth = 7; c.lineCap = 'round';
179
+ c.beginPath(); c.moveTo(a.chest[0] + 20, a.chest[1] - 10); c.quadraticCurveTo(a.chest[0] + 34, a.chest[1] + 40, a.chest[0] + 26, a.chest[1] + 80); c.moveTo(1200, 700); c.quadraticCurveTo(1250, 690, 1300, 700); c.stroke();
180
+ c.strokeStyle = C.redD; c.lineWidth = 4; c.beginPath(); c.moveTo(1380, 640); c.quadraticCurveTo(1400, 700, 1390, 760); c.stroke();
181
+ fillSkin(G.neck);
182
+ // 头
183
+ fillSkin(G.face);
184
+ c.fillStyle = C.skin; c.beginPath(); c.ellipse(G.ear[0], G.ear[1], 9, 13, 0.2, 0, TAU); c.fill(); c.lineWidth = 4; c.strokeStyle = C.ink; c.stroke();
185
+ // 刘海+头顶(大波浪)
186
+ const bangs = RIG.smooth([[1246, 330], [1250, 296], [1282, 268], [1336, 262], [1384, 284], [1404, 330], [1384, 312], [1350, 300], [1312, 302], [1280, 314], [1262, 340]].map(([x, y]) => [x + sh[0], y + sh[1]]), true, 0.5);
187
+ ink(c, bangs, C.hair, 5);
188
+ // 发丝黑线与白高光
189
+ c.strokeStyle = C.ink; c.lineWidth = 4;
190
+ [[1300, 272, 1418, 420, 0], [1350, 300, 1460, 520, 1], [1420, 360, 1520, 600, 2], [1380, 440, 1470, 630, 3], [1336, 420, 1410, 600, 4]].forEach(([x0, y0, x1, y1, k]) => {
191
+ c.beginPath(); for (let i = 0; i <= 24; i++) { const q = i / 24, x = lerp(x0, x1, q) + Math.sin(q * 9 + k) * 16 * q, y = lerp(y0, y1, q);
192
+ const X = x + sh[0] * 0.7 + (y > 430 ? sway * (y - 430) / 180 : 0), Y = y + sh[1] * 0.7; i ? c.lineTo(X, Y) : c.moveTo(X, Y); } c.stroke(); });
193
+ c.strokeStyle = C.white; c.lineWidth = 6; c.beginPath(); c.moveTo(1300 + sh[0], 280 + sh[1]); c.quadraticCurveTo(1340 + sh[0], 276 + sh[1], 1370 + sh[0], 292 + sh[1]); c.moveTo(1440 + sway * 0.3, 470); c.lineTo(1456 + sway * 0.4, 500); c.stroke();
194
+ // 五官:大眼、浓睫毛、红唇
195
+ const e = G.eye;
196
+ if (ch.blink) { c.strokeStyle = C.ink; c.lineWidth = 4; c.beginPath(); c.arc(e.x, e.y - 2, 9, 0.2, Math.PI - 0.2); c.stroke(); }
197
+ else { c.fillStyle = C.white; c.beginPath(); c.ellipse(e.x - 1, e.y, 12, 8.5, 0, 0, TAU); c.fill(); c.fillStyle = '#3a8ae0'; c.beginPath(); c.arc(e.x - 3, e.y + 1, 7, 0, TAU); c.fill(); c.fillStyle = C.ink; c.beginPath(); c.arc(e.x - 3, e.y + 1, 3.2, 0, TAU); c.fill(); c.fillStyle = '#fff'; c.beginPath(); c.arc(e.x - 6, e.y - 2, 2, 0, TAU); c.fill();
198
+ c.strokeStyle = C.ink; c.lineWidth = 4.5; c.stroke(G.lid); c.lineWidth = 3.5; G.lashes.forEach(l => { c.beginPath(); c.moveTo(...l[0]); c.lineTo(l[1][0] - 5, l[1][1] - 5); c.stroke(); }); c.beginPath(); c.moveTo(e.x + 10, e.y - 6); c.lineTo(e.x + 16, e.y - 12); c.stroke(); }
199
+ c.save(); c.translate(0, -5); c.strokeStyle = C.ink; c.lineWidth = 4; c.stroke(G.brow); c.restore();
200
+ c.fillStyle = C.red; c.beginPath(); c.moveTo(G.lips[0][0] - 3, G.lips[0][1] - 1); c.quadraticCurveTo(G.lips[1][0] + 4, G.lips[1][1] - 4, G.lips[1][0] + 3, G.lips[1][1] + 2); c.quadraticCurveTo(G.lips[2][0] + 3, G.lips[2][1] + 3, G.lips[2][0] - 3, G.lips[2][1]); c.closePath(); c.fill(); c.lineWidth = 2.5; c.stroke();
201
+ // 近侧手臂(无袖:皮肤点)+杯+手
202
+ fillSkin(G.upperArm); fillSkin(G.foreArm);
203
+ cup(c, G, t);
204
+ fillSkin(G.hand); c.fillStyle = C.skin; c.beginPath(); c.arc(G.thumb[0], G.thumb[1], 7, 0, TAU); c.fill(); c.lineWidth = 3.5; c.stroke();
205
+ c.fillStyle = C.red; c.beginPath(); c.arc(G.thumb[0] - 4, G.thumb[1] - 1, 3, 0, TAU); c.fill(); // 红指甲
206
+ }
207
+
208
+ // ---------- 猫:趴低、准备扑,屁股左右扭 ----------
209
+ function cat(c, K, lt, t, ch) {
210
+ const wig = Math.sin(lt * TAU * 6) * 16; // 6Hz 扭屁股
211
+ const bx = K.box[0][0] - 390, by = K.box[1][1] - 905; // 骨架原点偏移
212
+ const def = ([x, y]) => { const w = clamp((560 - x) / 140); return [x + bx + wig * w, y + by - Math.abs(wig) * 0.6 * w]; };
213
+ hatch(c, 580, 912, 230, 22);
214
+ // 尾巴
215
+ const tailBase = def([440, 870]), tip = [318 + bx + wig * 1.4 + Math.sin(ch.tail) * 20, 846 + by - Math.cos(ch.tail) * 18];
216
+ const tailPts = P.bez(tailBase, [400 + bx + wig, 900 + by], [350 + bx + wig * 1.2, 900 + by], tip, 16);
217
+ c.lineCap = 'round'; c.strokeStyle = C.ink; c.lineWidth = 34; c.beginPath(); tailPts.forEach((p, i) => i ? c.lineTo(p[0], p[1]) : c.moveTo(p[0], p[1])); c.stroke();
218
+ c.strokeStyle = C.orange; c.lineWidth = 24; c.stroke();
219
+ c.strokeStyle = C.orangeD; c.lineWidth = 6; [4, 8, 12].forEach(i => { const p = tailPts[i], q = tailPts[i + 1], a = Math.atan2(q[1] - p[1], q[0] - p[0]) + Math.PI / 2; c.beginPath(); c.moveTo(p[0] + Math.cos(a) * 11, p[1] + Math.sin(a) * 11); c.lineTo(p[0] - Math.cos(a) * 11, p[1] - Math.sin(a) * 11); c.stroke(); });
220
+ c.fillStyle = C.white; c.beginPath(); c.arc(tip[0], tip[1], 12, 0, TAU); c.fill(); c.lineWidth = 4; c.strokeStyle = C.ink; c.stroke();
221
+ // 身体(翘起的屁股在左)
222
+ const body = RIG.smooth([[452, 900], [420, 872], [418, 820], [440, 780], [482, 756], [540, 756], [600, 770], [652, 800], [690, 850], [700, 900]].map(def), true, 0.55);
223
+ ink(c, body, C.orange, 5);
224
+ c.save(); c.clip(body);
225
+ c.fillStyle = C.white; c.beginPath(); c.ellipse(610 + bx, 880 + by, 120, 46, 0, 0, TAU); c.fill();
226
+ c.strokeStyle = C.orangeD; c.lineWidth = 9; c.lineCap = 'round';
227
+ [[450, 790, 486, 830], [478, 772, 512, 816], [512, 764, 540, 806], [546, 766, 566, 806], [430, 830, 470, 858]].forEach(([x0, y0, x1, y1]) => { const p0 = def([x0, y0]), p1 = def([x1, y1]); c.beginPath(); c.moveTo(...p0); c.quadraticCurveTo(p0[0] + 18, (p0[1] + p1[1]) / 2, p1[0], p1[1]); c.stroke(); });
228
+ c.restore(); c.strokeStyle = C.ink; c.lineWidth = 5; c.stroke(body);
229
+ // 前爪(向前伸)
230
+ [[650, 886], [704, 888]].forEach(([x, y]) => { const p = new Path2D(); p.ellipse(x + bx, y + by, 30, 15, 0, 0, TAU); ink(c, p, C.white, 4.5); c.lineWidth = 3; c.beginPath(); c.moveTo(x + bx + 6, y + by - 8); c.lineTo(x + bx + 6, y + by + 6); c.moveTo(x + bx + 16, y + by - 7); c.lineTo(x + bx + 16, y + by + 5); c.stroke(); });
231
+ // 头(压低,盯着前方)
232
+ const hc = [K.headC[0] + 22, K.headC[1] + 104], r = 58;
233
+ const head = new Path2D(); head.ellipse(hc[0], hc[1], r * 1.08, r * 0.92, 0, 0, TAU);
234
+ const ears = poly([[hc[0] - 50, hc[1] - 30], [hc[0] - 46, hc[1] - 94], [hc[0] - 8, hc[1] - 52], [hc[0] + 14, hc[1] - 54], [hc[0] + 52, hc[1] - 96], [hc[0] + 56, hc[1] - 30]]);
235
+ ink(c, ears, C.orange, 5); ink(c, head, C.orange, 5);
236
+ c.fillStyle = '#f39ab8'; c.beginPath(); c.moveTo(hc[0] - 40, hc[1] - 50); c.lineTo(hc[0] - 40, hc[1] - 80); c.lineTo(hc[0] - 18, hc[1] - 56); c.fill(); c.beginPath(); c.moveTo(hc[0] + 44, hc[1] - 52); c.lineTo(hc[0] + 46, hc[1] - 82); c.lineTo(hc[0] + 22, hc[1] - 58); c.fill();
237
+ c.save(); c.clip(head); c.fillStyle = C.white; c.beginPath(); c.ellipse(hc[0] + 4, hc[1] + 36, 44, 34, 0, 0, TAU); c.fill();
238
+ c.strokeStyle = C.orangeD; c.lineWidth = 7; [[-14, -60, -10, -38], [4, -62, 4, -40], [22, -60, 18, -38]].forEach(([a0, b0, a1, b1]) => { c.beginPath(); c.moveTo(hc[0] + a0, hc[1] + b0); c.lineTo(hc[0] + a1, hc[1] + b1); c.stroke(); });
239
+ c.restore(); c.strokeStyle = C.ink; c.lineWidth = 5; c.stroke(head);
240
+ [[-22, -2], [24, -2]].forEach(([dx, dy]) => { const x = hc[0] + dx, y = hc[1] + dy;
241
+ if (ch.blink) { c.lineWidth = 4; c.beginPath(); c.arc(x, y, 13, 0.2, Math.PI - 0.2); c.stroke(); return; }
242
+ c.fillStyle = '#f8d020'; c.beginPath(); c.arc(x, y, 16, 0, TAU); c.fill(); c.lineWidth = 4; c.stroke();
243
+ c.fillStyle = C.ink; c.beginPath(); c.ellipse(x + 4, y + 1, 7, 12, 0, 0, TAU); c.fill(); c.fillStyle = '#fff'; c.beginPath(); c.arc(x + 7, y - 5, 4, 0, TAU); c.fill(); });
244
+ c.fillStyle = '#f07aa0'; c.beginPath(); c.moveTo(hc[0] - 7, hc[1] + 18); c.lineTo(hc[0] + 7, hc[1] + 18); c.lineTo(hc[0], hc[1] + 27); c.closePath(); c.fill(); c.lineWidth = 2.5; c.stroke();
245
+ c.lineWidth = 3.5; c.beginPath(); c.moveTo(hc[0] - 14, hc[1] + 32); c.quadraticCurveTo(hc[0] - 7, hc[1] + 40, hc[0], hc[1] + 30); c.quadraticCurveTo(hc[0] + 7, hc[1] + 40, hc[0] + 14, hc[1] + 32); c.stroke();
246
+ c.lineWidth = 2.5; c.beginPath(); [[-40, 22, -96, 12], [-40, 30, -98, 34], [40, 22, 98, 12], [40, 30, 100, 34]].forEach(([a0, b0, a1, b1]) => { c.moveTo(hc[0] + a0, hc[1] + b0); c.lineTo(hc[0] + a1, hc[1] + b1); }); c.stroke();
247
+ // 动作线:屁股旁的弧线跟着扭
248
+ c.strokeStyle = C.ink; c.lineWidth = 5; c.lineCap = 'round';
249
+ const side = wig > 0 ? 1 : -1;
250
+ [[0, 0], [1, 12], [2, 24]].forEach(([k, off]) => { c.beginPath(); c.arc(476 + bx + wig * 0.6, 760 + by - off, 46 + off, -Math.PI * 0.86, -Math.PI * 0.62); c.stroke(); });
251
+ [[0, 0], [1, 14]].forEach(([k, off]) => { c.beginPath(); c.arc(430 + bx + wig, 830 + by, 40 + off, Math.PI * (side > 0 ? 0.8 : 0.9), Math.PI * (side > 0 ? 1.05 : 1.15)); c.stroke(); });
252
+ }
253
+
254
+ return {
255
+ draw(c, lt, t) {
256
+ const ch = P.choreo(lt, t);
257
+ c.drawImage(bg(), 0, 0);
258
+ warhol(c, lt);
259
+ bubble(c, lt);
260
+ table(c);
261
+ hatch(c, 1340, 905, 170, 18);
262
+ const G = RIG.girl({ cup: ch.cup, sip: ch.sip, hair: 'long', breathe: ch.breathe });
263
+ girl(c, G, lt, t, ch);
264
+ const K = RIG.cat({ tail: ch.tail, blink: ch.blink, breathe: ch.breathe });
265
+ cat(c, K, lt, t, ch);
266
+ wiggleBox(c, lt);
267
+ },
268
+ };
269
+ })();
@@ -0,0 +1,238 @@
1
+ // 1985 8-bit 像素游戏(超级马里奥式)——纯代码。
2
+ // 管线:整幅在 240×135 低分辨率上逐像素画(1 低像素 = 8 屏幕像素)→ 角色用 RIG 路径缩到低分辨率填色
3
+ // → 调色板量化 + 阈值去抗锯齿 + 每个部件单独描 1px 黑边(NES 精灵的分层描边)→ 最近邻放大 8 倍
4
+ // → HUD 用 PressStart2P 64px(字体 1 像素 = 8 屏幕像素,坐标对齐 8 的倍数)直接画在全分辨率上。
5
+ // 片中只停约 0.23s(可见约 11 帧),所以所有小循环都是 15–30fps 的「步进」动画,幅度按整像素给。
6
+ SCENES['14_8bit'] = (() => {
7
+ const W = 1920, H = 1080, S = 8, LW = 240, LH = 135, P = PAINT;
8
+ const C = {
9
+ wall: '#064556', plus: '#0b988a', black: '#000000', white: '#fcfcfc', gray: '#bcbcbc',
10
+ wood: '#a77f06', woodHi: '#eeba18', brick: '#d85214', mortar: '#fbe2a6', base: '#0b988a',
11
+ sky0: '#0856ef', sky1: '#47c4f8', sky2: '#aae9fb', sun: '#f1bc08', hill: '#62ee58', hill2: '#0dc405',
12
+ red: '#e82704', redDk: '#7c0b02', blue: '#0856ef', blueDk: '#0838a8', skin: '#f9e0ab', skinDk: '#e8a878',
13
+ hair: '#f8d038', hairDk: '#c89010', orange: '#f39d48', orangeDk: '#d85214', pink: '#f8a0b0', leaf: '#0dc405', leafHi: '#62ee58',
14
+ };
15
+ const PAL_CHAR = ['#000000', '#fcfcfc', '#bcbcbc', '#e82704', '#7c0b02', '#0856ef', '#0838a8', '#f9e0ab', '#e8a878', '#f8d038', '#c89010', '#f39d48', '#d85214', '#f8a0b0'].map(P.hex);
16
+
17
+ // ---------- 低分辨率画布工具 ----------
18
+ const lc = (k) => P.cached('8b_' + k, LW, LH, () => {});
19
+ const px = (g, x, y, w = 1, h = 1, col) => { g.fillStyle = col; g.fillRect(x, y, w, h); };
20
+ // 把一层(在低分辨率上用抗锯齿画的路径)量化到调色板、alpha 阈值化,再在外轮廓描 1px 黑边
21
+ function spriteize(g, outline = true) {
22
+ const im = g.getImageData(0, 0, LW, LH), d = im.data, N = LW * LH, op = new Uint8Array(N);
23
+ for (let i = 0; i < N; i++) {
24
+ const a = d[i * 4 + 3];
25
+ if (a < 110) { d[i * 4 + 3] = 0; continue; }
26
+ // 去预乘后找最近色
27
+ const r = d[i * 4], gg = d[i * 4 + 1], b = d[i * 4 + 2];
28
+ let best = 0, bd = 1e9; for (let j = 0; j < PAL_CHAR.length; j++) { const q = PAL_CHAR[j], dd = (r - q[0]) ** 2 + (gg - q[1]) ** 2 + (b - q[2]) ** 2; if (dd < bd) { bd = dd; best = j; } }
29
+ const q = PAL_CHAR[best]; d[i * 4] = q[0]; d[i * 4 + 1] = q[1]; d[i * 4 + 2] = q[2]; d[i * 4 + 3] = 255; op[i] = 1;
30
+ }
31
+ if (outline) for (let y = 0; y < LH; y++) for (let x = 0; x < LW; x++) {
32
+ const i = y * LW + x; if (op[i]) continue;
33
+ if ((x > 0 && op[i - 1]) || (x < LW - 1 && op[i + 1]) || (y > 0 && op[i - LW]) || (y < LH - 1 && op[i + LW])) { d[i * 4] = 0; d[i * 4 + 1] = 0; d[i * 4 + 2] = 0; d[i * 4 + 3] = 255; }
34
+ }
35
+ g.putImageData(im, 0, 0);
36
+ }
37
+ // 一个精灵层:在低分辨率画布上以 1/8 缩放画全分辨率坐标的路径,再 spriteize
38
+ function layer(key, fn, outline = true) {
39
+ const cv = lc('L_' + key), g = cv.getContext('2d');
40
+ g.setTransform(1, 0, 0, 1, 0, 0); g.clearRect(0, 0, LW, LH);
41
+ g.save(); g.scale(1 / S, 1 / S); fn(g); g.restore();
42
+ spriteize(g, outline); return cv;
43
+ }
44
+
45
+ // ---------- 静态背景(低分辨率,逐像素) ----------
46
+ function background(g) {
47
+ px(g, 0, 0, LW, LH, C.wall);
48
+ // 十字点纹墙:6px 网格、隔行错 3px
49
+ for (let j = 0; j * 6 < 100; j++) for (let i = -1; i * 6 < LW + 6; i++) {
50
+ const x = i * 6 + (j % 2 ? 3 : 0) + 1, y = j * 6 + 2;
51
+ if (y > 97) continue;
52
+ px(g, x, y - 1, 1, 3, C.plus); px(g, x - 1, y, 3, 1, C.plus);
53
+ }
54
+ px(g, 0, 100, LW, 1, C.base);
55
+ // 木地板条
56
+ px(g, 0, 106, LW, 1, C.black); px(g, 0, 107, LW, 6, C.wood); px(g, 0, 107, LW, 1, C.woodHi);
57
+ for (let x = 7; x < LW; x += 24) px(g, x, 107, 1, 6, C.black);
58
+ // 砖块地面:行高 7(含缝),砖宽 16,隔行错 8
59
+ px(g, 0, 113, LW, LH - 113, C.black);
60
+ for (let r = 0; 113 + r * 7 < LH; r++) { const y = 114 + r * 7; for (let x = -16 + (r % 2 ? 8 : 0); x < LW; x += 16) {
61
+ px(g, x, y, 15, 6, C.brick); px(g, x, y, 15, 1, C.mortar); px(g, x, y, 1, 6, C.mortar);
62
+ } }
63
+ // 窗(45,15)-(100,73)
64
+ px(g, 44, 14, 57, 60, C.black); px(g, 45, 15, 55, 58, C.white); px(g, 98, 15, 2, 58, C.gray); px(g, 45, 71, 55, 2, C.gray);
65
+ const pane = (x0, y0, w, h) => {
66
+ for (let y = 0; y < h; y++) for (let x = 0; x < w; x++) {
67
+ const q = (y0 + y - 17) / 52, X = x0 + x, Y = y0 + y;
68
+ // 天空:深蓝→浅蓝→淡蓝,交界用 2×2 棋盘抖动(NES 渐变)
69
+ let col = q < 0.36 ? C.sky0 : q < 0.62 ? C.sky1 : C.sky2;
70
+ if (Math.abs(q - 0.36) < 0.05 && (X + Y) % 2) col = C.sky1;
71
+ if (Math.abs(q - 0.62) < 0.05 && (X + Y) % 2) col = q < 0.62 ? C.sky2 : C.sky1;
72
+ px(g, X, Y, 1, 1, col);
73
+ }
74
+ };
75
+ pane(47, 17, 24, 25); pane(75, 17, 23, 25); pane(47, 45, 24, 25); pane(75, 45, 23, 25);
76
+ px(g, 71, 17, 4, 53, C.white); px(g, 73, 17, 2, 53, C.gray); px(g, 47, 42, 51, 3, C.white); px(g, 47, 44, 51, 1, C.gray);
77
+ // 太阳(像素圆)
78
+ for (let y = -4; y <= 4; y++) for (let x = -4; x <= 4; x++) if (x * x + y * y <= 17) px(g, 58 + x, 28 + y, 1, 1, (x + y < -3) ? '#f8e858' : C.sun);
79
+ // 绿山(下两格)
80
+ const hill = (cx, cy, r, x0, x1, col) => { for (let x = x0; x < x1; x++) { const h = Math.sqrt(Math.max(0, r * r - (x - cx) ** 2)); if (h <= 0) continue; const top = Math.round(cy - h); px(g, x, top, 1, 70 - top, col); px(g, x, top, 1, 1, C.black); } };
81
+ hill(56, 70, 9, 47, 71, C.hill); hill(66, 72, 8, 47, 71, C.hill2);
82
+ hill(90, 76, 18, 75, 98, C.hill2);
83
+ for (const [x, y] of [[52, 64], [55, 62], [86, 63], [92, 66], [80, 68]]) px(g, x, y, 2, 1, C.hill);
84
+ // 砖块+问号块(右下格上方)
85
+ for (let k = 0; k < 2; k++) { const x = 81 + k * 4; px(g, x, 47, 4, 4, C.black); px(g, x, 47, 3, 3, C.brick); px(g, x, 47, 3, 1, C.mortar); }
86
+ px(g, 89, 47, 4, 4, C.black); px(g, 89, 47, 3, 3, C.sun); px(g, 90, 48, 1, 1, C.black);
87
+ px(g, 43, 73, 59, 3, C.black); px(g, 43, 73, 59, 2, C.gray); px(g, 43, 73, 59, 1, C.white);
88
+ // 鱼画框
89
+ px(g, 114, 29, 20, 15, C.black); px(g, 115, 30, 18, 13, C.woodHi); px(g, 117, 32, 14, 9, C.sky2);
90
+ px(g, 120, 35, 7, 3, C.brick); px(g, 119, 36, 1, 1, C.brick); px(g, 127, 34, 2, 5, C.brick); px(g, 121, 35, 1, 1, C.black);
91
+ px(g, 123, 27, 2, 2, C.gray);
92
+ // 椅子(梯背,人后面)
93
+ px(g, 178, 57, 3, 57, C.black); px(g, 179, 58, 2, 55, C.woodHi);
94
+ px(g, 185, 59, 3, 55, C.black); px(g, 186, 60, 2, 53, C.woodHi);
95
+ for (let k = 0; k < 4; k++) { px(g, 180, 63 + k * 7, 6, 2, C.black); px(g, 180, 63 + k * 7, 6, 1, C.wood); }
96
+ px(g, 160, 91, 28, 3, C.black); px(g, 160, 91, 28, 2, C.woodHi);
97
+ px(g, 163, 94, 2, 19, C.woodHi); px(g, 162, 94, 1, 19, C.black);
98
+ // 桌子 (102,80)-(150,113)
99
+ px(g, 101, 79, 50, 4, C.black); px(g, 102, 79, 48, 2, C.woodHi); px(g, 102, 81, 48, 1, C.wood);
100
+ px(g, 104, 83, 44, 3, C.wood); px(g, 104, 85, 44, 1, C.black);
101
+ for (const x of [106, 144]) { px(g, x - 1, 86, 4, 27, C.black); px(g, x, 86, 2, 27, C.woodHi); }
102
+ px(g, 107, 102, 38, 2, C.black); px(g, 107, 102, 38, 1, C.wood);
103
+ }
104
+ // 盆栽(会摆):叶子用整像素偏移 sway
105
+ function plant(g, sway) {
106
+ const pot = (x, y, w, h) => { px(g, x - 1, y - 1, w + 2, h + 2, C.black); px(g, x, y, w, h, C.brick); px(g, x + 1, y, 2, h, '#f39d48'); };
107
+ pot(208, 99, 20, 13); px(g, 206, 97, 24, 3, C.black); px(g, 207, 97, 22, 2, C.brick);
108
+ px(g, 217, 82, 2, 16, C.black); px(g, 218, 82, 1, 16, C.leaf);
109
+ const leaf = (cx, cy, w, h, dx) => { // 圆角像素叶:先画黑色外轮廓(缺角),再填绿、上沿一行高光、中间一条叶脉
110
+ const x0 = cx + dx - w / 2;
111
+ for (let j = -1; j <= h; j++) { const cut = (j === -1 || j === h) ? 1 : 0; px(g, x0 - 1 + cut, cy + j, w + 2 - cut * 2, 1, C.black); }
112
+ for (let j = 0; j < h; j++) { const cut = (j === 0 || j === h - 1) ? 1 : 0; px(g, x0 + cut, cy + j, w - cut * 2, 1, C.leaf); }
113
+ px(g, x0 + 1, cy, w - 3, 1, C.leafHi); px(g, x0 + 1, cy + (h >> 1), w - 2, 1, '#0a8a04');
114
+ };
115
+ leaf(218, 70, 6, 6, sway); leaf(213, 76, 6, 4, sway); leaf(224, 76, 6, 4, sway);
116
+ leaf(209, 84, 8, 4, Math.round(sway * 0.6)); leaf(227, 84, 8, 4, Math.round(sway * 0.6)); leaf(212, 91, 7, 3, 0); leaf(225, 91, 7, 3, 0);
117
+ }
118
+
119
+ // ---------- HUD(全分辨率,PressStart2P 64px,对齐 8px 网格) ----------
120
+ const BM = { // 位图:. 透明 o 橘 w 白 k 黑 r 红 p 粉
121
+ cat: ['o.....o.', 'oo...oo.', 'ooooooo.', 'okoooko.', 'ooopooo.', 'wwwwwww.', '.wwwww..'],
122
+ heart: ['.rr.rr.', 'rrrrrrr', 'rrrrrrr', '.rrrrr.', '..rrr..', '...r...'],
123
+ heartE: ['.ww.ww.', 'w..w..w', 'w.....w', '.w...w.', '..w.w..', '...w...'],
124
+ apple: ['...w....', '..w.....', '.wwwwww.', 'wwwwwwww', 'wwwwwwkw', 'wwwwwwww', '.wwwwww.', '..w..w..'],
125
+ };
126
+ const COL = { o: C.orange, w: C.white, k: C.black, r: C.red, p: C.pink, R: C.white };
127
+ const bitmap = (c, rows, x, y, map = COL, shadow = true) => {
128
+ if (shadow) rows.forEach((row, j) => [...row].forEach((ch, i) => { if (ch !== '.') { c.fillStyle = '#0b1d24'; c.fillRect(x + i * S + S, y + j * S + S, S, S); } }));
129
+ rows.forEach((row, j) => [...row].forEach((ch, i) => { if (ch !== '.') { c.fillStyle = map[ch]; c.fillRect(x + i * S, y + j * S, S, S); } }));
130
+ };
131
+ const text = (c, s, x, y) => { c.font = '48px "PressStart2P-400"'; c.textBaseline = 'top'; c.fillStyle = '#0b1d24'; c.fillText(s, x + S, y + S); c.fillStyle = C.white; c.fillText(s, x, y); };
132
+ function hud(c, lt) {
133
+ bitmap(c, BM.cat, 64, 40);
134
+ text(c, 'x9', 144, 48);
135
+ // 心:第 630 帧(本段 lt≈0.19)闪一下——两颗满心变白、空心变红,2 帧一换
136
+ const fl = lt > 0.16 && lt < 0.23 && Math.floor(lt * 30) % 2 === 0;
137
+ const red = fl ? { ...COL, r: C.white } : COL, emp = fl ? { ...COL, w: C.red } : COL;
138
+ bitmap(c, BM.heart, 312, 48, red); bitmap(c, BM.heart, 384, 48, red); bitmap(c, fl ? BM.heart.map(r => r) : BM.heartE, 456, 48, fl ? { ...COL, r: C.red } : emp);
139
+ text(c, 'WORLD 1-1', 624, 48);
140
+ bitmap(c, BM.apple, 1168, 40);
141
+ text(c, 'x03', 1248, 48);
142
+ }
143
+
144
+ // ---------- 角色 ----------
145
+ const legs = (g, G) => { // 牛仔裤腿+白球鞋(替换 RIG 的裙子)
146
+ const hip = G.hipSeat, knee = [1222, 724], ank = [1214, 872];
147
+ g.fillStyle = C.blueDk; g.fill(RIG.taper([hip[0] + 10, hip[1] - 6], [knee[0] + 26, knee[1] - 8], 84, 70)); g.fill(RIG.taper([knee[0] + 26, knee[1] - 8], [ank[0] + 30, ank[1]], 64, 50));
148
+ g.fillStyle = C.blue; g.fill(RIG.taper(hip, knee, 92, 76)); g.fill(RIG.taper(knee, ank, 70, 56));
149
+ g.fillStyle = C.white; g.fill(RIG.smooth([[1150, 868], [1238, 862], [1250, 900], [1124, 904], [1118, 888]]));
150
+ g.fillStyle = C.red; g.fillRect(1176, 880, 30, 8);
151
+ };
152
+ function girlLayers(ch, lt) {
153
+ const G = RIG.girl({ cup: ch.cup, sip: ch.sip, hair: 'long', breathe: ch.breathe });
154
+ // 每个部件单独成层、单独描黑边:NES 精灵靠这些「内轮廓」分出头发/脸/手臂
155
+ const back = layer('gBack', g => { g.fillStyle = C.red; g.fill(G.farSleeve); g.fillStyle = C.skin; g.fill(G.farHand); });
156
+ const body = layer('gBody', g => {
157
+ legs(g, G);
158
+ g.fillStyle = C.red; g.fill(G.torso);
159
+ g.fillStyle = C.redDk; g.fillRect(G.A.waist[0] - 40, G.A.waist[1] - 18, 120, 16); // 毛衣下摆罗纹
160
+ g.fillStyle = C.red; g.fillRect(G.A.neck[0] - 28, G.A.neck[1] - 10, 56, 22); // 高领
161
+ });
162
+ const hair = layer('gHair', g => { g.fillStyle = C.hair; g.fill(G.hairBack); g.fill(G.hairDown); g.fillStyle = C.hairDk; g.fillRect(G.A.nape[0] + 10, G.A.nape[1] + 20, 14, 120); });
163
+ const face = layer('gFace', g => { g.fillStyle = C.skin; g.fill(G.face); g.beginPath(); g.ellipse(G.ear[0], G.ear[1], 9, 13, 0.2, 0, 7); g.fill(); });
164
+ const bangs = layer('gBangs', g => { g.fillStyle = C.hair; g.fill(G.bangs); });
165
+ const arm = layer('gArm', g => {
166
+ g.fillStyle = C.red; g.fill(G.upperArm); g.fill(G.foreArm); g.fillStyle = C.redDk; g.fill(G.cuff);
167
+ g.fillStyle = C.skin; g.fill(G.hand);
168
+ });
169
+ return { G, back, body, hair, face, bangs, arm };
170
+ }
171
+ // 白底红条马克杯:直接在低分辨率上逐像素画(6×5 + 把手),杯子跟着手走
172
+ function cupPx(g, G) {
173
+ const x = Math.round(G.cup.x / S) - 3, y = Math.round(G.cup.y / S);
174
+ px(g, x - 1, y - 1, 8, 7, C.black); px(g, x + 6, y, 3, 4, C.black); px(g, x + 6, y + 1, 2, 2, C.white); px(g, x + 6, y + 1, 1, 2, C.black);
175
+ px(g, x, y, 6, 5, C.white); px(g, x, y, 6, 1, C.gray); px(g, x, y + 2, 6, 1, C.red); px(g, x, y + 4, 6, 1, C.red); px(g, x + 5, y + 1, 1, 4, C.gray);
176
+ px(g, Math.round(G.thumb[0] / S), Math.round(G.thumb[1] / S), 2, 2, C.skin);
177
+ return [x + 3, y - 1];
178
+ }
179
+ function catLayers(ch, lt) {
180
+ // 尾巴:15fps 步进,4 个姿态循环(像素动画的「两三张图交替」)
181
+ const step = Math.floor(lt * 15) % 4, tail = [-0.55, 0, 0.55, 0][step];
182
+ const K = RIG.cat({ tail, blink: ch.blink, breathe: 0 });
183
+ const body = layer('catB', g => {
184
+ g.lineCap = 'round'; g.strokeStyle = C.orange; g.lineWidth = K.tailW; g.stroke(K.tail);
185
+ const tip = K.tailPts[K.tailPts.length - 1]; g.fillStyle = C.white; g.beginPath(); g.arc(tip[0], tip[1], 14, 0, 7); g.fill();
186
+ g.fillStyle = C.orange; g.fill(K.body);
187
+ g.fillStyle = C.white; g.fill(K.white); K.legs.forEach(l => g.fill(l));
188
+ g.strokeStyle = C.orangeDk; g.lineWidth = 9; K.stripes.slice(3).forEach(s => { g.beginPath(); g.moveTo(...s[0]); g.lineTo(...s[1]); g.stroke(); });
189
+ });
190
+ const head = layer('catH', g => {
191
+ g.fillStyle = C.orange; g.fill(K.head);
192
+ g.save(); g.clip(K.head); g.fillStyle = C.white; g.beginPath(); g.ellipse(K.nose[0] - 14, K.nose[1] + 20, 34, 22, 0, 0, 7); g.fill(); g.restore();
193
+ g.strokeStyle = C.orangeDk; g.lineWidth = 9; K.stripes.slice(0, 3).forEach(s => { g.beginPath(); g.moveTo(...s[0]); g.lineTo(...s[1]); g.stroke(); });
194
+ g.fillStyle = C.pink; K.earInner.forEach(e => { g.beginPath(); e.forEach((p, i) => i ? g.lineTo(...p) : g.moveTo(...p)); g.fill(); });
195
+ });
196
+ return { K, body, head };
197
+ }
198
+
199
+ return {
200
+ draw(c, lt, t) {
201
+ const ch = P.choreo(lt, t);
202
+ const L = lc('frame'), g = L.getContext('2d');
203
+ g.setTransform(1, 0, 0, 1, 0, 0);
204
+ g.drawImage(P.cached('8b_bg', LW, LH, background), 0, 0);
205
+ // 窗里的云:2 帧挪 1px 往左;问号块 lt≈0.08 起顶出一枚金币(上去再落回)
206
+ const cx = 92 - Math.floor(lt * 30);
207
+ for (const [x, y, w] of [[cx - 34, 35, 8], [cx, 22, 9]]) { if (x < 47 || x + w > 98 || (x + w > 70 && x < 75)) continue; px(g, x + 1, y - 1, w - 2, 1, C.white); px(g, x, y, w, 2, C.white); px(g, x, y + 2, w, 1, C.gray); }
208
+ const cq = (lt - 0.06) / 0.16;
209
+ if (cq > 0 && cq < 1) { const y = Math.round(46 - Math.sin(cq * Math.PI) * 9); px(g, 90, y - 3, 2, 3, C.sun); px(g, 90, y - 3, 1, 3, '#f8e858'); }
210
+ // 盆栽在 630 帧(lt≈0.19)抖一下
211
+ const sw = lt > 0.15 && lt < 0.24 ? [0, 1, 0, -1][Math.floor(lt * 30) % 4] : 0;
212
+ plant(g, sw);
213
+ // 角色
214
+ const CL = catLayers(ch, lt), K = CL.K;
215
+ g.drawImage(CL.body, 0, 0); g.drawImage(CL.head, 0, 0);
216
+ // 猫脸:像素眼(2×2 黑+1 白高光)、粉鼻、白胡须
217
+ K.eyes.forEach(e => { const x = Math.round(e.x / S), y = Math.round(e.y / S); if (ch.blink) px(g, x - 1, y, 2, 1, C.black); else { px(g, x - 1, y - 1, 2, 2, C.black); px(g, x - 1, y - 1, 1, 1, C.white); } });
218
+ px(g, Math.round(K.nose[0] / S), Math.round(K.nose[1] / S), 1, 1, C.pink);
219
+ const hx = Math.round((K.headC[0] + 70) / S), hy = Math.round(K.headC[1] / S) + 2;
220
+ px(g, hx, hy, 3, 1, C.white); px(g, hx, hy + 2, 3, 1, C.white); px(g, hx - 17, hy + 1, 2, 1, C.white); px(g, hx - 17, hy + 3, 2, 1, C.white);
221
+ const GL = girlLayers(ch, lt), G = GL.G;
222
+ g.drawImage(GL.back, 0, 0); g.drawImage(GL.body, 0, 0); g.drawImage(GL.hair, 0, 0); g.drawImage(GL.face, 0, 0); g.drawImage(GL.bangs, 0, 0);
223
+ // 少女五官(逐像素):1×2 黑眼+眉、腮红、红唇
224
+ const ex = Math.round(G.eye.x / S), ey = Math.round(G.eye.y / S);
225
+ if (ch.blink) px(g, ex - 1, ey, 2, 1, C.black); else { px(g, ex, ey - 1, 1, 2, C.black); px(g, ex - 1, ey - 3, 2, 1, C.hairDk); }
226
+ px(g, Math.round(G.cheek[0] / S) - 1, Math.round(G.cheek[1] / S), 2, 1, C.pink);
227
+ px(g, Math.round(G.lips[1][0] / S), Math.round(G.lips[1][1] / S), 1, 1, C.red);
228
+ g.drawImage(GL.arm, 0, 0);
229
+ const [sx, sy0] = cupPx(g, G);
230
+ // 像素热气:三列 1×2 的点,30fps 每帧上移 1px,循环 6px
231
+ const ph = Math.floor(lt * 30), sy = sy0 - 1;
232
+ for (let k = 0; k < 3; k++) for (let j = 0; j < 2; j++) { const y = sy - ((ph + k * 2 + j * 3) % 6) - j * 3; px(g, sx - 2 + k * 2 + ((ph + k) % 2), y, 1, 2, k === 1 ? C.white : C.gray); }
233
+ // 放大 8 倍(最近邻)
234
+ c.imageSmoothingEnabled = false; c.drawImage(L, 0, 0, LW, LH, 0, 0, W, H); c.imageSmoothingEnabled = true;
235
+ hud(c, lt);
236
+ },
237
+ };
238
+ })();