@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,555 @@
1
+ // 公元79年 庞贝马赛克(opus tessellatum / vermiculatum)——纯代码。
2
+ // 管线:①平涂底稿(静态部分缓存,少女/猫/火山烟/边框卷涡每帧重画)
3
+ // →②自写镶嵌渲染器:init 时一次性算好「石块布局」——距离场等值线上走线放石块(沿轮廓排,andamento)、
4
+ // 远处规则成行;每块石头是带四边独立抖动的旋转方块,用「加权 L∞ Voronoi」光栅成 ID 图,相邻石块之间自然留灰浆缝;
5
+ // 每像素预存斜面明暗(左上受光)+石纹颗粒
6
+ // →③每帧只在动态矩形(少女、猫、火山画、边框带)里按石块中心去底稿取色重填——石块不动,颜色在石块间流动。
7
+ SCENES['04_roman'] = (() => {
8
+ const W = 1920, H = 1080, { clamp, lerp, rng } = U, P = PAINT;
9
+ const BW = 84; // 外框波浪纹带宽
10
+ const C = {
11
+ wall: '#e4ddcd', halo: '#f5f1e8', outline: '#46322a',
12
+ floor: '#dac9a0', floorLine: '#8f6c40',
13
+ bW: '#ece6d8', bK: '#24201d',
14
+ red: '#a8322a', red2: '#7c221d', gold: '#d0a23c', gold2: '#9f7420',
15
+ sky: '#9ec0d8', sky2: '#d3e3e4', cloud: '#88847d', cloud2: '#a9a49b',
16
+ cone: '#4e5796', coneL: '#7c84b8', coneD: '#2f3672', ridge: '#373e7c', green: '#6f9341', green2: '#92b254', ground: '#cba344', ground2: '#a67d2a',
17
+ cyp: '#2b5a2f', cyp2: '#4b7c3c', lava: '#d64a22', lava2: '#f2952c',
18
+ table: '#7f95ab', tableHi: '#aebfcd', tableD: '#5d7288',
19
+ chair: '#c69c3c', amph: '#b4472e', amphHi: '#d66c4a', amphD: '#7e2c1e',
20
+ stola: '#efe7d5', stolaF: '#cbbd9c', skin: '#efc4a0', hair: '#dcaa42', hairD: '#9c6c1c', lip: '#b8433a', eye: '#2a2420',
21
+ catO: '#dc8530', catO2: '#c06a24', catS: '#9c4818', catW: '#f7f2e8', catEye: '#e9c22c', collar: '#b02a22',
22
+ };
23
+ // 石块类别:尺寸 / 沿轮廓排几圈(99=全部沿轮廓)/ 颜色抖动幅度
24
+ const K_WALL = 0, K_HALO = 1, K_FIG = 2, K_FACE = 3, K_HAIR = 4, K_PIC = 5, K_FRAME = 6, K_BORDER = 7, K_TEXT = 8, K_FLOOR = 9, K_OBJ = 10, K_CAT = 11;
25
+ const SZ = [12, 11, 10, 5, 8, 9, 9, 7.6, 7, 13, 10, 9];
26
+ const KR = [1, 9, 99, 99, 99, 2, 99, 0, 99, 1, 99, 99];
27
+ const JIT = [.08, .05, .05, .04, .05, .06, .05, .05, .05, .065, .05, .05];
28
+
29
+ // ---------- 静态物件的路径 ----------
30
+ const rectP = (x, y, w, h) => { const p = new Path2D(); p.rect(x, y, w, h); return p; };
31
+ const FRAME = rectP(360, 120, 440, 440);
32
+ const TABLE_TOP = (() => { const p = new Path2D(); p.roundRect(820, 634, 380, 30, 15); return p; })();
33
+ const TABLE_PED = (() => { const p = new Path2D(); [[994, 664], [1026, 664], [1022, 700], [1034, 738], [1026, 780], [1040, 840], [1076, 882], [1080, 898], [940, 898], [944, 882], [980, 840], [994, 780], [986, 738], [998, 700]].forEach((q, i) => i ? p.lineTo(...q) : p.moveTo(...q)); p.closePath(); return p; })();
34
+ const CHAIR = (() => { const p = new Path2D(); p.rect(1460, 462, 14, 440); p.rect(1296, 724, 184, 14); p.rect(1306, 738, 12, 164); p.rect(1306, 818, 168, 9); p.moveTo(1476, 454); p.arc(1467, 454, 9, 0, Math.PI * 2); return p; })();
35
+ const AMPH = RIG.smooth([[1712, 616], [1744, 616], [1746, 652], [1772, 690], [1768, 760], [1752, 830], [1734, 884], [1729, 900], [1724, 884], [1704, 830], [1688, 760], [1686, 690], [1710, 652]]);
36
+ const AMPH_LIP = rectP(1702, 604, 52, 13);
37
+ const AMPH_H = (() => { const p = new Path2D(); p.moveTo(1714, 628); p.bezierCurveTo(1684, 628, 1682, 660, 1692, 690); p.moveTo(1742, 628); p.bezierCurveTo(1772, 628, 1774, 660, 1764, 690); return p; })();
38
+ // 少女的罗马服饰:披肩 palla(背后斜披+盖过膝+前襟垂下)
39
+ const PALLA_BACK = RIG.smooth([[1350, 436], [1384, 440], [1408, 472], [1422, 532], [1432, 600], [1448, 662], [1454, 722], [1440, 766], [1392, 772], [1398, 700], [1394, 620], [1380, 540], [1362, 482]]);
40
+ const PALLA_LAP = RIG.smooth([[1418, 612], [1448, 652], [1454, 722], [1440, 766], [1362, 778], [1290, 768], [1226, 744], [1200, 722], [1236, 712], [1300, 716], [1360, 700], [1392, 660]]);
41
+ const PALLA_HANG = RIG.smooth([[1322, 768], [1366, 774], [1360, 850], [1356, 912], [1324, 914], [1330, 840]]);
42
+ const PALLA_FOLDS = [[[1372, 470], [1398, 560], [1412, 660], [1418, 740]], [[1236, 730], [1300, 742], [1370, 744], [1424, 734]]].map(q => RIG.smooth(q, false));
43
+
44
+ // ---------- 平涂底稿:静态部分 ----------
45
+ function picture(g) {
46
+ g.save(); g.beginPath(); g.rect(392, 152, 376, 376); g.clip();
47
+ const sg = g.createLinearGradient(0, 152, 0, 470); sg.addColorStop(0, C.sky); sg.addColorStop(1, C.sky2); g.fillStyle = sg; g.fillRect(392, 152, 376, 376);
48
+ g.strokeStyle = '#e4eeee'; g.lineWidth = 5; [[410, 250, 500], [620, 262, 760], [400, 300, 470], [640, 318, 750]].forEach(([a, y, b]) => { g.beginPath(); g.moveTo(a, y); g.lineTo(b, y); g.stroke(); });
49
+ // 火山:三面
50
+ const poly = (pts, col) => { g.fillStyle = col; g.beginPath(); pts.forEach((q, i) => i ? g.lineTo(...q) : g.moveTo(...q)); g.closePath(); g.fill(); };
51
+ poly([[402, 472], [535, 222], [560, 222], [520, 472]], C.coneL);
52
+ poly([[520, 472], [560, 222], [586, 222], [646, 472]], C.cone);
53
+ poly([[646, 472], [586, 222], [738, 472]], C.coneD);
54
+ g.strokeStyle = C.ridge; g.lineWidth = 5; [[[548, 226], [500, 330], [470, 420]], [[574, 226], [600, 330], [620, 440]]].forEach(l => { g.beginPath(); l.forEach((q, i) => i ? g.lineTo(...q) : g.moveTo(...q)); g.stroke(); });
55
+ g.fillStyle = '#2a2c48'; g.beginPath(); g.ellipse(560, 222, 27, 6, 0, 0, Math.PI * 2); g.fill();
56
+ // 熔岩
57
+ g.lineCap = 'round'; g.lineJoin = 'round';
58
+ g.strokeStyle = C.lava; g.lineWidth = 8; g.beginPath(); g.moveTo(566, 226); g.lineTo(580, 262); g.lineTo(596, 298); g.lineTo(614, 334); g.stroke();
59
+ g.strokeStyle = C.lava2; g.lineWidth = 5; g.beginPath(); g.moveTo(552, 228); g.lineTo(544, 262); g.lineTo(538, 290); g.stroke();
60
+ // 山坡、地面、柏树
61
+ g.fillStyle = C.green; g.beginPath(); g.moveTo(392, 462); g.quadraticCurveTo(470, 430, 560, 456); g.quadraticCurveTo(650, 482, 768, 448); g.lineTo(768, 528); g.lineTo(392, 528); g.fill();
62
+ g.fillStyle = C.green2; g.beginPath(); g.moveTo(392, 482); g.quadraticCurveTo(480, 462, 580, 484); g.quadraticCurveTo(680, 502, 768, 476); g.lineTo(768, 528); g.lineTo(392, 528); g.fill();
63
+ g.fillStyle = C.ground; g.beginPath(); g.moveTo(392, 502); g.quadraticCurveTo(560, 488, 768, 504); g.lineTo(768, 528); g.lineTo(392, 528); g.fill();
64
+ g.strokeStyle = C.ground2; g.lineWidth = 4; g.beginPath(); g.moveTo(400, 515); for (let x = 400; x <= 768; x += 24) g.quadraticCurveTo(x + 6, 510, x + 12, 516); g.stroke();
65
+ [[702, 258, 500, 17], [740, 276, 498, 14]].forEach(([x, top, bot, w]) => {
66
+ g.fillStyle = C.cyp; g.beginPath(); g.moveTo(x, top); g.quadraticCurveTo(x + w * 1.1, top + 110, x + w * 0.75, bot); g.lineTo(x - w * 0.75, bot); g.quadraticCurveTo(x - w * 1.1, top + 110, x, top); g.fill();
67
+ g.strokeStyle = C.cyp2; g.lineWidth = 5; g.beginPath(); g.moveTo(x - 3, top + 30); g.lineTo(x - 6, bot - 10); g.stroke();
68
+ });
69
+ g.restore();
70
+ }
71
+ function staticScene(g) {
72
+ g.fillStyle = C.wall; g.fillRect(0, 0, W, H);
73
+ g.fillStyle = C.floor; g.fillRect(BW, 905, W - 2 * BW, H - BW - 905);
74
+ // 物件晕圈(白石沿轮廓)
75
+ g.lineJoin = 'round'; g.strokeStyle = C.halo;
76
+ g.lineWidth = 44; g.stroke(FRAME); g.stroke(TABLE_TOP); g.stroke(TABLE_PED); g.stroke(AMPH); g.stroke(AMPH_LIP); g.stroke(AMPH_H);
77
+ g.lineWidth = 34; g.stroke(CHAIR);
78
+ g.strokeStyle = C.floorLine; g.lineWidth = 6; g.beginPath(); g.moveTo(BW, 905); g.lineTo(W - BW, 905); g.stroke();
79
+ // 画框:红框 → 深线 → 金线 → 深线 → 画
80
+ g.fillStyle = C.red; g.fillRect(360, 120, 440, 440);
81
+ g.fillStyle = C.red2; g.fillRect(376, 136, 408, 408);
82
+ g.fillStyle = C.gold; g.fillRect(381, 141, 398, 398);
83
+ g.fillStyle = '#3a2a22'; g.fillRect(387, 147, 386, 386);
84
+ picture(g);
85
+ g.strokeStyle = C.outline; g.lineWidth = 5; g.stroke(FRAME);
86
+ // 椅子(在少女后面)
87
+ g.fillStyle = C.chair; g.fill(CHAIR); g.strokeStyle = '#7d5c1c'; g.lineWidth = 5; g.stroke(CHAIR);
88
+ // 桌子:蓝灰圆桌面+喇叭形单柱
89
+ g.fillStyle = C.table; g.fill(TABLE_PED);
90
+ g.save(); g.clip(TABLE_PED); g.fillStyle = '#93a8bb'; g.fillRect(940, 660, 52, 240); g.fillStyle = C.tableD; g.fillRect(1030, 660, 60, 240);
91
+ g.fillStyle = '#c33a2c'; g.fillRect(940, 736, 140, 9); g.fillStyle = '#3d5a9a'; g.fillRect(940, 748, 140, 7); g.restore();
92
+ g.strokeStyle = C.outline; g.lineWidth = 6; g.stroke(TABLE_PED);
93
+ g.fillStyle = C.table; g.fill(TABLE_TOP); g.fillStyle = C.tableHi; g.fillRect(834, 638, 352, 9); g.stroke(TABLE_TOP);
94
+ // 陶罐
95
+ g.strokeStyle = C.amphD; g.lineWidth = 9; g.lineCap = 'round'; g.stroke(AMPH_H);
96
+ g.fillStyle = C.amph; g.fill(AMPH); g.fill(AMPH_LIP);
97
+ g.save(); g.clip(AMPH); g.fillStyle = C.amphHi; g.fillRect(1698, 640, 14, 200); g.fillStyle = C.amphD; g.fillRect(1748, 640, 30, 260); g.restore();
98
+ g.strokeStyle = C.outline; g.lineWidth = 6; g.stroke(AMPH); g.stroke(AMPH_LIP);
99
+ // CAVE CATTVM:碑刻大写,黑石
100
+ // 字母自己写笔画(等宽粗笔画最像石块拼字;字体的细横笔在 7px 石块下会断):60px 高,笔画 12px
101
+ const GL = { C: [44, g2 => g2.ellipse(22, 30, 19, 26, 0, Math.PI * 0.27, Math.PI * 1.73)], A: [46, [[[0, 60], [23, 0], [46, 60]], [[11, 38], [35, 38]]]], V: [46, [[[0, 0], [23, 60], [46, 0]]]],
102
+ E: [38, [[[38, 6], [6, 6], [6, 54], [38, 54]], [[6, 30], [32, 30]]]], T: [46, [[[0, 6], [46, 6]], [[23, 6], [23, 60]]]], M: [52, [[[4, 60], [6, 0], [26, 46], [46, 0], [48, 60]]]] };
103
+ g.save(); g.strokeStyle = '#1d1a18'; g.lineWidth = 14; g.lineJoin = 'miter'; g.lineCap = 'butt';
104
+ const TXT = 'CAVE CATTVM', gap = 16; let tx = 0; const lay = [];
105
+ for (const ch of TXT) { if (ch === ' ') { tx += 30; continue; } lay.push([ch, tx]); tx += GL[ch][0] + gap; }
106
+ const sc = 600 / (tx - gap); g.translate(270, 922); g.scale(sc, 1);
107
+ for (const [ch, x0] of lay) { const gl = GL[ch][1]; g.beginPath(); if (typeof gl === 'function') { g.save(); g.translate(x0, 0); gl(g); g.restore(); } else gl.forEach(l => l.forEach((q, k) => k ? g.lineTo(x0 + q[0], q[1]) : g.moveTo(x0 + q[0], q[1]))); g.stroke(); }
108
+ g.restore();
109
+ // 鱼与老鼠(小石块)
110
+ g.fillStyle = '#5f5b57'; g.beginPath(); g.ellipse(1446, 950, 40, 8, 0, 0, Math.PI * 2); g.fill();
111
+ g.beginPath(); g.moveTo(1408, 950); g.lineTo(1386, 938); g.lineTo(1386, 962); g.closePath(); g.fill();
112
+ g.strokeStyle = '#d8ccb0'; g.lineWidth = 2.5; for (let k = 0; k < 5; k++) { g.beginPath(); g.moveTo(1420 + k * 11, 944); g.lineTo(1426 + k * 11, 956); g.stroke(); }
113
+ g.fillStyle = '#86807a'; g.beginPath(); g.ellipse(1784, 960, 22, 10, 0, 0, Math.PI * 2); g.fill();
114
+ g.beginPath(); g.moveTo(1800, 952); g.lineTo(1824, 962); g.lineTo(1800, 968); g.fill(); g.beginPath(); g.arc(1798, 950, 5, 0, Math.PI * 2); g.fill();
115
+ g.strokeStyle = '#86807a'; g.lineWidth = 3; g.beginPath(); g.moveTo(1764, 962); g.quadraticCurveTo(1748, 950, 1738, 966); g.stroke();
116
+ // 内框黑线
117
+ g.fillStyle = C.bK; g.fillRect(BW - 2, BW - 2, W - 2 * BW + 4, 6); g.fillRect(BW - 2, H - BW - 4, W - 2 * BW + 4, 6); g.fillRect(BW - 2, BW - 2, 6, H - 2 * BW + 4); g.fillRect(W - BW - 4, BW - 2, 6, H - 2 * BW + 4);
118
+ }
119
+
120
+ // ---------- 平涂底稿:会动的部分 ----------
121
+ // 四边波浪纹(维特鲁威卷涡),每条边在自己的局部坐标里画同一个图案:u 沿顺时针方向,v 从外往里
122
+ const PER = 81;
123
+ const SIDES = [[1, 0, 0, 1, 0, 0, W], [0, 1, -1, 0, W, 0, H], [-1, 0, 0, -1, W, H, W], [0, -1, 1, 0, 0, H, H]];
124
+ function border(g, phase) {
125
+ for (const [a, b, c2, d, e, f, len] of SIDES) {
126
+ g.save(); g.setTransform(a, b, c2, d, e, f);
127
+ g.beginPath(); g.rect(BW, 0, len - 2 * BW, BW); g.clip();
128
+ g.fillStyle = C.bW; g.fillRect(0, 0, len, BW);
129
+ g.fillStyle = C.bK; g.fillRect(0, 0, len, 8); g.fillRect(0, 66, len, BW - 66);
130
+ g.strokeStyle = C.bK; g.lineWidth = 13; g.lineCap = 'round'; g.lineJoin = 'round';
131
+ const off = ((phase % PER) + PER) % PER;
132
+ for (let u0 = BW - 2 * PER + off; u0 < len + PER; u0 += PER) {
133
+ const cx = u0 + PER * 0.52, cy = 38, r0 = 24;
134
+ g.beginPath(); g.moveTo(u0 - 6, 68); g.quadraticCurveTo(u0 + 2, 42, cx - r0, cy);
135
+ for (let th = Math.PI; th <= Math.PI * 3.25; th += 0.14) { const r = r0 - (th - Math.PI) / (Math.PI * 2.25) * (r0 - 5); g.lineTo(cx + Math.cos(th) * r, cy + Math.sin(th) * r); }
136
+ g.stroke();
137
+ }
138
+ g.restore();
139
+ }
140
+ // 四角方块饰
141
+ for (const [x, y] of [[0, 0], [W - BW, 0], [0, H - BW], [W - BW, H - BW]]) {
142
+ g.fillStyle = C.bK; g.fillRect(x, y, BW, BW); g.fillStyle = C.bW; g.fillRect(x + 10, y + 10, BW - 20, BW - 20);
143
+ g.fillStyle = C.bK; g.fillRect(x + 22, y + 22, BW - 44, BW - 44); g.fillStyle = C.red; g.fillRect(x + 33, y + 33, BW - 66, BW - 66);
144
+ }
145
+ }
146
+ // 火山烟团+火星(全局时间驱动,连续)
147
+ function volcanoFX(g, t) {
148
+ g.save(); g.beginPath(); g.rect(392, 152, 376, 376); g.clip();
149
+ // 云带(火山灰云)向右漂:周期 380px,一秒 55px
150
+ const cx0 = (t * 55) % 380;
151
+ for (const ox of [-380, 0]) [[28, 168, 46, 18, C.cloud], [108, 162, 52, 16, C.cloud2], [218, 166, 50, 18, C.cloud], [308, 170, 58, 17, C.cloud2], [63, 192, 36, 10, C.cloud2], [278, 195, 40, 10, C.cloud]]
152
+ .forEach(([x, y, rx, ry, col]) => { g.fillStyle = col; g.beginPath(); g.ellipse(392 + x + ox + cx0, y, rx, ry, 0, 0, Math.PI * 2); g.fill(); });
153
+ const N = 9;
154
+ for (let i = 0; i < N; i++) {
155
+ const q = ((t * 0.85 + i / N) % 1), e = 1 - Math.pow(1 - q, 1.6);
156
+ const x = 560 + e * 175 + Math.sin(t * 2 + i * 2.1) * 10, y = 212 - e * 62 - Math.sin(e * 3.1) * 18, r = 13 + q * 40; // 烟柱往右压低飘(留在画框天空里才看得见)
157
+ const col = P.mix(P.hex('#3f3c3a'), P.hex('#a29c94'), Math.min(1, q * 1.1));
158
+ g.globalAlpha = q > 0.8 ? (1 - q) / 0.2 : 1;
159
+ g.fillStyle = P.rgb(col); g.beginPath(); g.arc(x, y, r, 0, Math.PI * 2); g.fill();
160
+ g.fillStyle = P.rgb(P.mix(col, [255, 255, 255], 0.3)); g.beginPath(); g.arc(x - r * 0.3, y - r * 0.3, r * 0.45, 0, Math.PI * 2); g.fill();
161
+ }
162
+ g.globalAlpha = 1;
163
+ const r = rng(79);
164
+ for (let i = 0; i < 12; i++) {
165
+ const ang = -Math.PI / 2 + (r() - 0.5) * 2.0, sp = 130 + r() * 110, ph = r();
166
+ const q = (t * 1.25 + ph) % 1, tt = q * 0.9;
167
+ const x = 560 + Math.cos(ang) * sp * tt, y = 216 + Math.sin(ang) * sp * tt + 260 * tt * tt;
168
+ g.fillStyle = r() < 0.5 ? '#f6d23c' : '#f08a22'; g.fillRect(x - 5, y - 5, 10, 10);
169
+ }
170
+ // 火山口熔岩脉动
171
+ g.fillStyle = `rgba(246,160,40,${0.55 + 0.45 * Math.sin(t * 9)})`; g.beginPath(); g.ellipse(560, 220, 20, 6, 0, 0, Math.PI * 2); g.fill();
172
+ g.restore();
173
+ }
174
+ // 少女:罗马斯托拉+红色披肩+金发罗马发髻
175
+ const rot = (G, x, y) => { const c = Math.cos(G.tilt), s = Math.sin(G.tilt), X = x - 1300, Y = y - 425; return [1300 + X * c - Y * s, 425 + X * s + Y * c]; };
176
+ // 近侧手臂:不用骨架原举杯路线(终点杯子戳在鼻子上、手臂挡脸)。杯沿右端贴到唇高、倾角 ≤8.6°,手托在杯脚下,
177
+ // 杯子走「桌面 → 前上方 → 唇前」的二次贝塞尔;IK 用骨架同一副臂长(165/122)与肘向。
178
+ function armPose(G, k) {
179
+ const S = G.A.shoulder, lip = G.lips[0];
180
+ const A0 = [1186, 584], A2 = [lip[0] - 31, lip[1] - 3], A1 = [1150, 470], u = 1 - k;
181
+ const cp = [u * u * A0[0] + 2 * u * k * A1[0] + k * k * A2[0], u * u * A0[1] + 2 * u * k * A1[1] + k * k * A2[1]];
182
+ const cup = { x: cp[0], y: cp[1], w: 44, h: 38, tilt: 0.15 * k };
183
+ const hT = [cp[0] + lerp(40, 3, k), cp[1] + lerp(32, 56, k)];
184
+ const { E, H } = RIG.ik2(S, hT, 165, 122, -1);
185
+ const W_ = [lerp(E[0], H[0], 0.82), lerp(E[1], H[1], 0.82)], fa = Math.atan2(H[1] - E[1], H[0] - E[0]);
186
+ const hand = new Path2D(); hand.ellipse(H[0], H[1], 17, 14, fa, 0, Math.PI * 2);
187
+ return { S, E, H, cup, upperArm: RIG.taper(S, E, 54, 40, 10), foreArm: RIG.taper(E, W_, 40, 30, 2), cuff: RIG.taper([lerp(E[0], H[0], 0.74), lerp(E[1], H[1], 0.74)], W_, 32, 31), hand };
188
+ }
189
+ function girl(g, G, { nearArm = true, steam = 0, t = 0, blink = 0, cup = 0 } = {}) {
190
+ const AP = armPose(G, cup);
191
+ g.save(); g.lineJoin = 'round'; g.lineCap = 'round';
192
+ const sil = [G.skirt, G.torso, G.farSleeve, G.farHand, G.hairBack, G.bun, G.neck, G.face, G.bangs, PALLA_BACK, PALLA_LAP, PALLA_HANG, G.shoe];
193
+ const arm = nearArm ? [AP.upperArm, AP.foreArm, AP.hand] : [];
194
+ g.strokeStyle = C.halo; g.lineWidth = 50; sil.concat(arm).forEach(p => p && g.stroke(p));
195
+ if (nearArm) { g.fillStyle = C.halo; g.beginPath(); g.arc(AP.cup.x, AP.cup.y + 26, 60, 0, Math.PI * 2); g.fill(); }
196
+ const F = (p, col, lw = 8) => { if (!p) return; g.fillStyle = col; g.fill(p); if (lw) { g.strokeStyle = C.outline; g.lineWidth = lw; g.stroke(p); } };
197
+ F(G.farSleeve, C.stola); F(G.farHand, C.skin, 6); F(G.farCuff, C.gold, 0);
198
+ F(G.skirt, C.stola);
199
+ g.save(); g.clip(G.skirt); g.fillStyle = C.red; g.fillRect(1080, 878, 400, 10); g.fillRect(1080, 897, 400, 9);
200
+ g.strokeStyle = C.stolaF; g.lineWidth = 6; G.folds.forEach(f => g.stroke(f)); g.restore();
201
+ g.strokeStyle = C.outline; g.lineWidth = 8; g.stroke(G.skirt);
202
+ F(G.shoe, '#6b4428', 6);
203
+ F(G.torso, C.stola);
204
+ g.save(); g.clip(G.torso); g.strokeStyle = C.stolaF; g.lineWidth = 6; [[1300, 470, 1306, 600], [1330, 480, 1340, 605]].forEach(([a, b, c2, d]) => { g.beginPath(); g.moveTo(a, b); g.quadraticCurveTo(a + 8, (b + d) / 2, c2, d); g.stroke(); }); g.restore();
205
+ F(PALLA_BACK, C.red); F(PALLA_LAP, C.red); F(PALLA_HANG, C.red, 7);
206
+ g.strokeStyle = C.red2; g.lineWidth = 7; PALLA_FOLDS.forEach(f => g.stroke(f));
207
+ g.fillStyle = C.gold; g.beginPath(); g.arc(1372, 446, 10, 0, Math.PI * 2); g.fill(); g.strokeStyle = C.gold2; g.lineWidth = 3; g.stroke();
208
+ F(G.neck, C.skin, 6);
209
+ F(G.hairBack, C.hair, 7); F(G.bun, C.hair, 7);
210
+ g.strokeStyle = C.hairD; g.lineWidth = 4; g.beginPath(); for (let a = 0; a < 8; a += 0.25) { const r = 4 + a * 3, x = G.bunSpiral[0] + Math.cos(a) * r, y = G.bunSpiral[1] + Math.sin(a) * r; a ? g.lineTo(x, y) : g.moveTo(x, y); } g.stroke();
211
+ F(G.face, C.skin, 10);
212
+ g.fillStyle = C.skin; g.beginPath(); g.ellipse(G.ear[0], G.ear[1], 8, 12, 0.2, 0, Math.PI * 2); g.fill(); g.strokeStyle = C.outline; g.lineWidth = 4; g.stroke();
213
+ g.fillStyle = C.gold; g.beginPath(); g.arc(G.ear[0] - 1, G.ear[1] + 16, 5, 0, Math.PI * 2); g.fill();
214
+ F(G.bangs, C.hair, 6);
215
+ g.strokeStyle = C.hairD; g.lineWidth = 4; G.hairLines.forEach(h => g.stroke(h));
216
+ // 红色发带
217
+ const f0 = rot(G, 1256, 322), f1 = rot(G, 1282, 294), f2 = rot(G, 1342, 286), f3 = rot(G, 1378, 334);
218
+ g.strokeStyle = C.red; g.lineWidth = 8; g.beginPath(); g.moveTo(...f0); g.bezierCurveTo(...f1, ...f2, ...f3); g.stroke();
219
+ // 五官(小石块尺度:眼一块深色,眉、唇)
220
+ if (blink) { g.strokeStyle = C.eye; g.lineWidth = 4; g.stroke(G.lid); }
221
+ else { g.fillStyle = '#fbf8f0'; g.beginPath(); g.ellipse(G.eye.x + 3, G.eye.y + 1, 13, 7, 0, 0, Math.PI * 2); g.fill(); g.fillStyle = C.eye; g.beginPath(); g.ellipse(G.eye.x - 1, G.eye.y + 1, 8, 7, 0, 0, Math.PI * 2); g.fill(); g.strokeStyle = C.eye; g.lineWidth = 5; g.stroke(G.lid); }
222
+ g.strokeStyle = '#4a3020'; g.lineWidth = 6; g.stroke(G.brow);
223
+ { const l0 = G.lips[0], l2 = G.lips[2]; g.fillStyle = '#a8282a'; g.beginPath(); g.ellipse((l0[0] + l2[0]) / 2 + 4, (l0[1] + l2[1]) / 2, 8.5, 6, -0.3, 0, Math.PI * 2); g.fill(); }
224
+ if (nearArm) {
225
+ F(AP.upperArm, C.skin, 6); F(AP.foreArm, C.skin, 6);
226
+ g.save(); g.clip(AP.upperArm); const sh0 = G.A.shoulder; g.fillStyle = C.stola; g.beginPath(); g.arc(sh0[0], sh0[1], 62, 0, Math.PI * 2); g.fill(); g.strokeStyle = C.red; g.lineWidth = 9; g.beginPath(); g.arc(sh0[0], sh0[1], 62, 0, Math.PI * 2); g.stroke(); g.restore(); g.strokeStyle = C.outline; g.lineWidth = 6; g.stroke(AP.upperArm);
227
+ F(AP.cuff, C.gold, 0);
228
+ F(AP.hand, C.skin, 6); // 手托杯脚;杯子本身由 cupSprite 叠在马赛克上
229
+ }
230
+ g.restore();
231
+ }
232
+ // 猫:用 RIG.cat 的锚点重画——平滑梨形身体、虎斑、白胸白爪、大黄眼、红项圈
233
+ const CATS = 0.85, catT = (g) => { g.translate(540, 905); g.scale(CATS, CATS); g.translate(-540, -905); };
234
+ const catPt = ([x, y]) => [540 + (x - 540) * CATS, 905 + (y - 905) * CATS];
235
+ function cat(g, K) {
236
+ g.save(); catT(g); g.lineJoin = 'round'; g.lineCap = 'round';
237
+ const body = RIG.smooth(K.bodyPts);
238
+ const tail = new Path2D(); K.tailPts.forEach((q, i) => i ? tail.lineTo(...q) : tail.moveTo(...q));
239
+ g.strokeStyle = C.halo; g.lineWidth = 50; g.stroke(body); g.stroke(K.head); K.legs.forEach(l => g.stroke(l)); g.lineWidth = K.tailW + 50; g.stroke(tail);
240
+ // 尾巴:深描边 → 橘 → 深橘环纹 → 白尖
241
+ g.strokeStyle = C.outline; g.lineWidth = K.tailW + 12; g.stroke(tail);
242
+ g.strokeStyle = C.catO; g.lineWidth = K.tailW; g.stroke(tail);
243
+ g.strokeStyle = C.catS; g.setLineDash([9, 13]); g.lineDashOffset = 4; g.lineWidth = K.tailW - 2; g.lineCap = 'butt'; g.stroke(tail); g.setLineDash([]); g.lineCap = 'round';
244
+ const tip = K.tailPts[K.tailPts.length - 1]; g.fillStyle = C.catW; g.beginPath(); g.arc(tip[0], tip[1], K.tailW / 2, 0, Math.PI * 2); g.fill();
245
+ // 身体
246
+ g.fillStyle = C.catO; g.fill(body);
247
+ g.save(); g.clip(body); g.fillStyle = C.catO2; g.beginPath(); g.ellipse(K.bodyPts[2][0] + 10, K.bodyPts[2][1] + 40, 52, 110, 0.1, 0, Math.PI * 2); g.fill();
248
+ g.strokeStyle = C.catS; g.lineWidth = 10; K.stripes.slice(3).forEach(s => { g.beginPath(); g.moveTo(...s[0]); g.lineTo(...s[1]); g.stroke(); });
249
+ const hx = K.bodyPts[1][0], hy = K.bodyPts[1][1];
250
+ g.lineWidth = 8; g.strokeStyle = C.catS; g.beginPath(); g.arc(hx + 62, hy - 6, 50, Math.PI * 0.95, Math.PI * 1.6); g.stroke(); // 后腿轮廓
251
+ g.beginPath(); g.arc(hx + 62, hy - 6, 30, Math.PI * 1.0, Math.PI * 1.55); g.stroke();
252
+ g.restore();
253
+ g.strokeStyle = C.outline; g.lineWidth = 8; g.stroke(body);
254
+ K.legs.forEach(l => { g.fillStyle = C.catW; g.fill(l); });
255
+ g.fillStyle = C.catW; g.fill(K.white);
256
+ K.legs.forEach(l => { g.strokeStyle = C.outline; g.lineWidth = 5; g.stroke(l); });
257
+ // 红项圈+金坠
258
+ const hc = K.headC;
259
+ g.strokeStyle = C.collar; g.lineWidth = 13; g.beginPath(); g.moveTo(hc[0] - 46, hc[1] + 52); g.quadraticCurveTo(hc[0] + 2, hc[1] + 80, hc[0] + 52, hc[1] + 52); g.stroke();
260
+ g.fillStyle = C.gold; g.beginPath(); g.arc(hc[0] + 6, hc[1] + 76, 7, 0, Math.PI * 2); g.fill();
261
+ // 头
262
+ // 头:正脸(庞贝猫马赛克是正面瞪眼)。脸中线 x≈604(两耳尖中点)
263
+ const fx = hc[0] + 4, fy = hc[1];
264
+ g.fillStyle = C.catO; g.fill(K.head);
265
+ g.save(); g.clip(K.head); g.fillStyle = C.catW; g.beginPath(); g.ellipse(fx, fy + 30, 34, 24, 0, 0, Math.PI * 2); g.fill();
266
+ g.strokeStyle = C.catS; g.lineWidth = 7; K.stripes.slice(0, 3).forEach(s => { g.beginPath(); g.moveTo(...s[0]); g.lineTo(...s[1]); g.stroke(); });
267
+ [[-1], [1]].forEach(([d]) => { g.beginPath(); g.moveTo(fx + d * 66, fy + 4); g.lineTo(fx + d * 44, fy + 8); g.stroke(); g.beginPath(); g.moveTo(fx + d * 64, fy + 20); g.lineTo(fx + d * 46, fy + 20); g.stroke(); });
268
+ g.restore();
269
+ g.strokeStyle = C.outline; g.lineWidth = 11; g.stroke(K.head);
270
+ g.fillStyle = '#e9a08e'; K.earInner.forEach(e => { g.beginPath(); e.forEach((p, i) => i ? g.lineTo(...p) : g.moveTo(...p)); g.fill(); });
271
+ // 大黄圆眼+黑竖瞳+深色眼眶(庞贝猫标志)
272
+ [[fx - 24, fy - 8], [fx + 24, fy - 10]].forEach(([ex, ey]) => {
273
+ const R = 17;
274
+ if (K.blink) { g.fillStyle = C.catO; g.beginPath(); g.arc(ex, ey, R, 0, Math.PI * 2); g.fill(); g.strokeStyle = C.eye; g.lineWidth = 7; g.beginPath(); g.moveTo(ex - R, ey + 2); g.quadraticCurveTo(ex, ey + 8, ex + R, ey + 2); g.stroke(); }
275
+ else { g.fillStyle = C.catEye; g.beginPath(); g.arc(ex, ey, R, 0, Math.PI * 2); g.fill(); g.strokeStyle = C.eye; g.lineWidth = 7; g.stroke(); g.fillStyle = C.eye; g.beginPath(); g.ellipse(ex, ey, 4.5, 11, 0, 0, Math.PI * 2); g.fill(); }
276
+ });
277
+ g.fillStyle = '#c96a5c'; g.beginPath(); g.moveTo(fx - 8, fy + 16); g.lineTo(fx + 8, fy + 16); g.lineTo(fx, fy + 25); g.closePath(); g.fill();
278
+ g.strokeStyle = C.eye; g.lineWidth = 4; g.beginPath(); g.moveTo(fx, fy + 25); g.lineTo(fx, fy + 31); g.moveTo(fx - 12, fy + 36); g.quadraticCurveTo(fx - 5, fy + 38, fx, fy + 31); g.quadraticCurveTo(fx + 5, fy + 38, fx + 12, fy + 36); g.stroke();
279
+ g.restore();
280
+ }
281
+
282
+ // ---------- 编舞 ----------
283
+ const ease = U.ease.inOut;
284
+ const borderPhase = (lt) => 15 * lt + PER * ease(clamp((lt - 0.485) / 0.365)); // 匀速漂移+原片 226–248 帧走一个周期
285
+ const poses = (lt, t) => {
286
+ const ch = P.choreo(lt, t);
287
+ return { ch, G: RIG.girl({ cup: ch.cup, sip: ch.sip, breathe: ch.breathe }), K: RIG.cat({ tail: ch.tail * 1.6, blink: ch.blink, breathe: ch.breathe }) };
288
+ };
289
+ function fullBase(g, sb, lt, t) {
290
+ const { ch, G, K } = poses(lt, t);
291
+ g.drawImage(sb, 0, 0);
292
+ border(g, borderPhase(lt));
293
+ volcanoFX(g, t);
294
+ cat(g, K);
295
+ girl(g, G, { steam: 1 - ch.cup * 0.85, t, blink: ch.blink, cup: ch.cup });
296
+ }
297
+
298
+ // ---------- 镶嵌渲染器:布局(只算一次) ----------
299
+ let S = null;
300
+ function build() {
301
+ const t0 = performance.now();
302
+ const sb = P.cached('rm_static', W, H, g => staticScene(g));
303
+ const mk = () => { const cv = P.canvas(); return [cv, cv.getContext('2d', { willReadFrequently: true })]; };
304
+ // 结构图(决定石块怎么排):静态+少女(不含会动的近侧手臂)+猫
305
+ const [sc, sg] = mk(); sg.drawImage(sb, 0, 0);
306
+ const G0 = RIG.girl({ cup: 0 }), K0 = RIG.cat({});
307
+ cat(sg, K0); girl(sg, G0, { nearArm: false, steam: 0 });
308
+ // 类别图
309
+ const [cc, cg] = mk(); const cs = k => `rgb(${k * 16},0,0)`;
310
+ cg.fillStyle = cs(K_WALL); cg.fillRect(0, 0, W, H);
311
+ cg.fillStyle = cs(K_FLOOR); cg.fillRect(BW, 905, W - 2 * BW, H - BW - 905);
312
+ cg.lineJoin = 'round'; cg.strokeStyle = cs(K_HALO); cg.lineWidth = 44; [FRAME, TABLE_TOP, TABLE_PED, AMPH, AMPH_LIP].forEach(p => cg.stroke(p)); cg.lineWidth = 34; cg.stroke(CHAIR);
313
+ cg.fillStyle = cs(K_FRAME); cg.fill(FRAME); cg.fillStyle = cs(K_PIC); cg.fillRect(388, 148, 384, 384);
314
+ cg.fillStyle = cs(K_OBJ); [TABLE_TOP, TABLE_PED, AMPH, AMPH_LIP, CHAIR].forEach(p => cg.fill(p));
315
+ const gs = [G0.skirt, G0.torso, G0.farSleeve, G0.farHand, G0.neck, PALLA_BACK, PALLA_LAP, PALLA_HANG, G0.shoe], gh = [G0.hairBack, G0.bun, G0.bangs];
316
+ const kb = [RIG.smooth(K0.bodyPts), K0.head].concat(K0.legs), ktail = new Path2D(); K0.tailPts.forEach((q, i) => i ? ktail.lineTo(...q) : ktail.moveTo(...q));
317
+ cg.strokeStyle = cs(K_HALO); cg.lineWidth = 50; gs.concat(gh, [G0.face]).forEach(p => cg.stroke(p)); cg.save(); catT(cg); kb.forEach(p => cg.stroke(p)); cg.lineWidth = K0.tailW + 50; cg.stroke(ktail); cg.restore();
318
+ cg.lineWidth = 9; cg.fillStyle = cg.strokeStyle = cs(K_FIG); gs.forEach(p => { cg.fill(p); cg.stroke(p); }); cg.fillStyle = cg.strokeStyle = cs(K_HAIR); gh.forEach(p => { cg.fill(p); cg.stroke(p); }); cg.fillStyle = cg.strokeStyle = cs(K_FACE); cg.fill(G0.face); cg.stroke(G0.face);
319
+ cg.save(); catT(cg); cg.lineWidth = 9; cg.fillStyle = cg.strokeStyle = cs(K_CAT); kb.forEach(p => { cg.fill(p); cg.stroke(p); }); cg.strokeStyle = cs(K_CAT); cg.lineWidth = K0.tailW + 12; cg.stroke(ktail); cg.fillStyle = cg.strokeStyle = cs(K_FACE); cg.lineWidth = 13; cg.fill(K0.head); cg.stroke(K0.head); cg.restore();
320
+ cg.fillStyle = cs(K_BORDER); cg.fillRect(0, 0, W, BW); cg.fillRect(0, H - BW, W, BW); cg.fillRect(0, 0, BW, H); cg.fillRect(W - BW, 0, BW, H);
321
+ const cd = cg.getImageData(0, 0, W, H).data, CL = new Uint8Array(W * H);
322
+ for (let i = 0; i < W * H; i++) CL[i] = Math.round(cd[i * 4] / 16);
323
+ const sdat = sg.getImageData(0, 0, W, H).data;
324
+ for (let y = 905; y < H - BW; y++) for (let x = 255; x < 885; x++) { const i = y * W + x; if (sdat[i * 4] + sdat[i * 4 + 1] + sdat[i * 4 + 2] < 240) CL[i] = K_TEXT; }
325
+ // 半分辨率:边缘 → 精确欧氏距离变换(Felzenszwalb)
326
+ const HW = 960, HH = 540, N = HW * HH;
327
+ const hc = P.canvas(HW, HH), hg = hc.getContext('2d', { willReadFrequently: true }); hg.drawImage(sc, 0, 0, HW, HH);
328
+ const hd = hg.getImageData(0, 0, HW, HH).data;
329
+ const CH = new Uint8Array(N); for (let y = 0; y < HH; y++) for (let x = 0; x < HW; x++) CH[y * HW + x] = CL[(2 * y) * W + 2 * x];
330
+ const F = new Float64Array(N);
331
+ for (let y = 0; y < HH; y++) for (let x = 0; x < HW; x++) {
332
+ const i = y * HW + x, o = i * 4; let e = false;
333
+ if (x < HW - 1) { const q = o + 4; e = Math.abs(hd[o] - hd[q]) + Math.abs(hd[o + 1] - hd[q + 1]) + Math.abs(hd[o + 2] - hd[q + 2]) > 34; }
334
+ if (!e && y < HH - 1) { const q = o + HW * 4; e = Math.abs(hd[o] - hd[q]) + Math.abs(hd[o + 1] - hd[q + 1]) + Math.abs(hd[o + 2] - hd[q + 2]) > 34; }
335
+ if (!e && x < HW - 1 && CH[i] !== CH[i + 1] && (CH[i] === K_BORDER || CH[i + 1] === K_BORDER)) e = true;
336
+ if (!e && y < HH - 1 && CH[i] !== CH[i + HW] && (CH[i] === K_BORDER || CH[i + HW] === K_BORDER)) e = true;
337
+ F[i] = e ? 0 : 1e20;
338
+ }
339
+ const D = edt(F, HW, HH); // 半分辨率像素的平方距离 → 全分辨率像素距离
340
+ for (let i = 0; i < N; i++) D[i] = Math.sqrt(D[i]) * 2;
341
+ const tangent = (i) => { const x = i % HW, y = (i / HW) | 0; const gx = D[i + (x < HW - 1 ? 1 : 0)] - D[i - (x > 0 ? 1 : 0)], gy = D[i + (y < HH - 1 ? HW : 0)] - D[i - (y > 0 ? HW : 0)]; return Math.atan2(gy, gx) + Math.PI / 2; };
342
+ // 石块种子:空间哈希保证最小间距
343
+ const sx = [], sy = [], ss = [], sa = [], scl = [];
344
+ const CS = 24, GWc = Math.ceil(W / CS), GHc = Math.ceil(H / CS), cell = Array.from({ length: GWc * GHc }, () => []);
345
+ const ok = (x, y, s, f) => { const cx = (x / CS) | 0, cy = (y / CS) | 0; for (let j = cy - 1; j <= cy + 1; j++) for (let i = cx - 1; i <= cx + 1; i++) { if (i < 0 || j < 0 || i >= GWc || j >= GHc) continue; for (const k of cell[j * GWc + i]) { const m = f * (s + ss[k]) / 2; const dx = sx[k] - x, dy = sy[k] - y; if (dx * dx + dy * dy < m * m) return false; } } return true; };
346
+ const add = (x, y, s, a, c) => { const k = sx.length; sx.push(x); sy.push(y); ss.push(s); sa.push(a); scl.push(c); cell[((y / CS) | 0) * GWc + ((x / CS) | 0)].push(k); return k; };
347
+ // ① 外框:沿边成行
348
+ const r = rng(2079), NR = 11, bs = BW / NR;
349
+ for (const [a, b, c2, d, e, f, len] of SIDES) {
350
+ for (let j = 0; j < NR; j++) { let u = BW + (j % 2) * bs * 0.5 + r() * 2; while (u < len - BW) { const w = bs * (0.86 + r() * 0.28), uc = u + w / 2, v = (j + 0.5) * bs; add(a * uc + c2 * v + e, b * uc + d * v + f, bs, Math.atan2(b, a) + (r() - .5) * 0.06, K_BORDER); u += w; } }
351
+ }
352
+ for (const [x0, y0] of [[0, 0], [W - BW, 0], [0, H - BW], [W - BW, H - BW]]) for (let j = 0; j < NR; j++) for (let i = 0; i < NR; i++) add(x0 + (i + 0.5) * bs, y0 + (j + 0.5) * bs, bs, (r() - .5) * 0.06, K_BORDER);
353
+ // ② 沿轮廓:在距离场等值线 d=(k+½)s 上走线,每走一个石块宽放一块
354
+ const cand = new Uint8Array(N), vis = new Uint8Array(N);
355
+ for (let i = 0; i < N; i++) { const c = CH[i]; if (c === K_BORDER) continue; const s = SZ[c], d = D[i]; if (d < 1) continue; const k = Math.floor(d / s); if (k >= KR[c]) continue; if (Math.abs(d - (k + 0.5) * s) < 1.5) cand[i] = 1; }
356
+ const NB = [[1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1], [0, -1], [1, -1]];
357
+ for (let i0 = 0; i0 < N; i0++) {
358
+ if (!cand[i0] || vis[i0]) continue;
359
+ let cur = i0, px = -1e9, py = -1e9, dx = 1, dy = 0;
360
+ for (;;) {
361
+ vis[cur] = 1; const hx = cur % HW, hy = (cur / HW) | 0, x = 2 * hx + 1, y = 2 * hy + 1, c = CH[cur], s = SZ[c];
362
+ if ((x - px) ** 2 + (y - py) ** 2 >= s * s * 0.98 && ok(x, y, s, 0.8)) { add(x, y, s, tangent(cur), c); px = x; py = y; }
363
+ let best = -1, bd = -9, bx = 0, by = 0;
364
+ for (const [ox, oy] of NB) { const nx = hx + ox, ny = hy + oy; if (nx < 0 || ny < 0 || nx >= HW || ny >= HH) continue; const n = ny * HW + nx; if (!cand[n] || vis[n]) continue; const l = Math.hypot(ox, oy), dt = (ox * dx + oy * dy) / l; if (dt > bd) { bd = dt; best = n; bx = ox / l; by = oy / l; } }
365
+ if (best < 0) break;
366
+ dx = dx * 0.6 + bx * 0.4; dy = dy * 0.6 + by * 0.4; const l = Math.hypot(dx, dy) || 1; dx /= l; dy /= l; cur = best;
367
+ }
368
+ }
369
+ // ③ 其余:规则成行(砖缝错半块)+补洞
370
+ for (let c = 0; c < SZ.length; c++) {
371
+ if (c === K_BORDER) continue; const s = SZ[c];
372
+ for (let j = 0; (j + 0.5) * s < H; j++) for (let i = -1; (i + 0.5) * s < W; i++) {
373
+ const x = (i + 0.5 + (j % 2) * 0.5) * s + (r() - .5) * 0.18 * s, y = (j + 0.5) * s + (r() - .5) * 0.1 * s;
374
+ if (x < 0 || y < 0 || x >= W || y >= H) continue;
375
+ if (CL[(y | 0) * W + (x | 0)] !== c) continue;
376
+ const hi = Math.min(HH - 1, (y / 2) | 0) * HW + Math.min(HW - 1, (x / 2) | 0);
377
+ const contour = KR[c] >= 99 || D[hi] < KR[c] * s;
378
+ if (ok(x, y, s, 0.86)) add(x, y, s, contour ? tangent(hi) : (r() - .5) * 0.07, c);
379
+ }
380
+ }
381
+ // ④ 光栅化:加权 L∞ Voronoi(每块石头四边独立抖动 → 不规则方块),D2−D1 太小处是灰浆缝
382
+ const n = sx.length;
383
+ const HUP = new Float32Array(n), HUM = new Float32Array(n), HVP = new Float32Array(n), HVM = new Float32Array(n), CA = new Float32Array(n), SA = new Float32Array(n);
384
+ const JL = new Float32Array(n), TR = new Float32Array(n), TG = new Float32Array(n), TB = new Float32Array(n), SIDX = new Int32Array(n);
385
+ for (let k = 0; k < n; k++) {
386
+ const h = ss[k] / 2, c = scl[k];
387
+ HUP[k] = h * (1.08 + r() * 0.2); HUM[k] = h * (1.08 + r() * 0.2); HVP[k] = h * (1.08 + r() * 0.2); HVM[k] = h * (1.08 + r() * 0.2);
388
+ const a = sa[k] + (r() - .5) * 0.12; CA[k] = Math.cos(a); SA[k] = Math.sin(a);
389
+ JL[k] = (r() - .5) * 2 * JIT[c]; const add_ = (r() - .5) * 14; TR[k] = add_; TG[k] = add_; TB[k] = add_;
390
+ const q = r(); if (q < 0.09) { TR[k] += 12; TG[k] += 4; TB[k] -= 8; } else if (q < 0.16) { TR[k] -= 6; TB[k] += 9; }
391
+ if (c === K_WALL && r() < 0.07) JL[k] -= 0.12;
392
+ SIDX[k] = clamp(Math.round(sy[k]), 0, H - 1) * W + clamp(Math.round(sx[k]), 0, W - 1);
393
+ }
394
+ const D1 = new Float32Array(W * H).fill(9), D2 = new Float32Array(W * H).fill(9), ID = new Int32Array(W * H).fill(-1);
395
+ for (let k = 0; k < n; k++) {
396
+ const cx = sx[k], cy = sy[k], ca = CA[k], sn = SA[k], hm = Math.max(HUP[k], HUM[k], HVP[k], HVM[k]), R = Math.ceil(hm * 1.5) + 1;
397
+ const x0 = Math.max(0, (cx - R) | 0), x1 = Math.min(W - 1, (cx + R) | 0), y0 = Math.max(0, (cy - R) | 0), y1 = Math.min(H - 1, (cy + R) | 0);
398
+ const hup = HUP[k], hum = HUM[k], hvp = HVP[k], hvm = HVM[k];
399
+ for (let y = y0; y <= y1; y++) { const ddy = y + 0.5 - cy; let p = y * W + x0; for (let x = x0; x <= x1; x++, p++) {
400
+ const ddx = x + 0.5 - cx, u = ddx * ca + ddy * sn, v = -ddx * sn + ddy * ca;
401
+ const du = u > 0 ? u / hup : -u / hum, dv = v > 0 ? v / hvp : -v / hvm, Dd = du > dv ? du : dv;
402
+ if (Dd < D1[p]) { D2[p] = D1[p]; D1[p] = Dd; ID[p] = k; } else if (Dd < D2[p]) D2[p] = Dd;
403
+ } }
404
+ }
405
+ // ⑤ 每像素:石块 or 灰浆;石面斜面明暗(左上受光)+颗粒
406
+ const SH = new Uint8Array(W * H), LX = -0.6, LY = -0.8;
407
+ const gr = rng(7);
408
+ for (let y = 0; y < H; y++) for (let x = 0; x < W; x++) {
409
+ const p = y * W + x, k = ID[p]; if (k < 0) continue;
410
+ const h = (HUP[k] + HUM[k] + HVP[k] + HVM[k]) / 4, g = clamp(0.1 * ss[k], 0.75, 1.25), d1 = D1[p];
411
+ const gap = (D2[p] - d1) * h / 2;
412
+ if (d1 > 1 || gap < g) { ID[p] = -1; continue; }
413
+ const e = Math.min((1 - d1) * h, gap - g);
414
+ let sh = 1 - 0.05 * d1 * d1 + (gr() - 0.5) * 0.06;
415
+ if (e < 2.2) {
416
+ const ddx = x + 0.5 - sx[k], ddy = y + 0.5 - sy[k], ca = CA[k], sn = SA[k], u = ddx * ca + ddy * sn, v = -ddx * sn + ddy * ca;
417
+ const du = u > 0 ? u / HUP[k] : -u / HUM[k], dv = v > 0 ? v / HVP[k] : -v / HVM[k];
418
+ const nx = du > dv ? Math.sign(u) * ca : -Math.sign(v) * sn, ny = du > dv ? Math.sign(u) * sn : Math.sign(v) * ca;
419
+ sh += 0.16 * (1 - e / 2.2) * (nx * LX + ny * LY);
420
+ }
421
+ SH[p] = clamp(Math.round(sh * 128), 0, 255);
422
+ }
423
+ // ⑥ 第 0 帧全幅上色 → 静态马赛克
424
+ const [bc, bg] = mk();
425
+ fullBase(bg, sb, 0, 3.28125);
426
+ const bd = bg.getImageData(0, 0, W, H).data;
427
+ const TCr = new Uint8ClampedArray(n), TCg = new Uint8ClampedArray(n), TCb = new Uint8ClampedArray(n);
428
+ const color = (k, d) => { const o = SIDX[k] * 4, f = 1 + JL[k]; TCr[k] = d[o] * f + TR[k]; TCg[k] = d[o + 1] * f + TG[k]; TCb[k] = d[o + 2] * f + TB[k]; };
429
+ for (let k = 0; k < n; k++) color(k, bd);
430
+ const [oc, og] = mk(), oimg = og.createImageData(W, H), SO = oimg.data, grr = rng(13);
431
+ for (let p = 0; p < W * H; p++) {
432
+ const k = ID[p], o = p * 4;
433
+ if (k < 0) { const nz = (grr() - 0.5) * 16; SO[o] = 112 + nz; SO[o + 1] = 103 + nz; SO[o + 2] = 92 + nz; }
434
+ else { const sh = SH[p]; SO[o] = (TCr[k] * sh) >> 7; SO[o + 1] = (TCg[k] * sh) >> 7; SO[o + 2] = (TCb[k] * sh) >> 7; }
435
+ SO[o + 3] = 255;
436
+ }
437
+ og.putImageData(oimg, 0, 0);
438
+ // ⑦ 动态矩形:由各姿态的包围盒算出
439
+ const bbox = (pts, m) => { let x0 = 1e9, y0 = 1e9, x1 = -1e9, y1 = -1e9; pts.forEach(([x, y]) => { x0 = Math.min(x0, x); y0 = Math.min(y0, y); x1 = Math.max(x1, x); y1 = Math.max(y1, y); }); return [Math.max(0, (x0 - m) | 0), Math.max(0, (y0 - m) | 0), Math.min(W, Math.ceil(x1 + m)), Math.min(H, Math.ceil(y1 + m))]; };
440
+ const gp = [[1180, 270], [1460, 720]];
441
+ for (let cu = 0; cu <= 1.001; cu += 0.05) for (const sip of [0, 0.15]) { const G = RIG.girl({ cup: cu, sip }), A = armPose(G, cu); gp.push(A.H, A.E, G.A.headTop, G.A.forehead, G.A.chin, G.A.nape, [A.cup.x - 40, A.cup.y - 95], [A.cup.x + 40, A.cup.y + 60], [G.bunSpiral[0] + 36, G.bunSpiral[1] - 36]); }
442
+ const kp = [[420, 540], [700, 912]];
443
+ for (let tl = -0.75; tl <= 0.75; tl += 0.05) { const K = RIG.cat({ tail: tl }); kp.push(...K.tailPts.map(catPt)); }
444
+ const rects = [[392, 152, 768, 528], bbox(gp, 58), bbox(kp, 58)]
445
+ .map(([x0, y0, x1, y1]) => ({ x: x0, y: y0, w: x1 - x0, h: y1 - y0, img: og.createImageData(x1 - x0, y1 - y0) }));
446
+ const mark = new Uint8Array(n), dyn = [];
447
+ for (const R of rects) for (let y = R.y; y < R.y + R.h; y++) for (let x = R.x; x < R.x + R.w; x++) { const k = ID[y * W + x]; if (k >= 0 && !mark[k]) { mark[k] = 1; dyn.push(k); } }
448
+ S = { sb, bc, bg, oc, og, ID, SH, SO, TCr, TCg, TCb, color, rects, dyn: Int32Array.from(dyn), n, strip: borderStrip(), cupSp: cupSprite(), ms: performance.now() - t0 };
449
+ window.__rm_stats = { tiles: n, dynTiles: dyn.length, buildMs: Math.round(S.ms), rects: rects.map(R => [R.x, R.y, R.w, R.h]) };
450
+ }
451
+ // 外框:一条「周期 = 卷涡周期 81px」的石块带(石块布局也按 81px 循环),每帧整条平移相位——
452
+ // 石块跟着图案一起走(像传送带),比「石块不动只换色」明显得多(7.6px 石块会把 1–3px/帧的相位移动量化掉)。
453
+ function borderStrip() {
454
+ const NP = 26, SL = PER * NP, NR = 11, bs = BW / NR;
455
+ const pc = P.canvas(SL, BW), pg = pc.getContext('2d', { willReadFrequently: true });
456
+ pg.fillStyle = C.bW; pg.fillRect(0, 0, SL, BW); pg.fillStyle = C.bK; pg.fillRect(0, 0, SL, 8); pg.fillRect(0, 66, SL, BW - 66);
457
+ pg.strokeStyle = C.bK; pg.lineWidth = 13; pg.lineCap = 'round'; pg.lineJoin = 'round';
458
+ for (let k = -1; k <= NP; k++) { const u0 = k * PER, cx = u0 + PER * 0.52, cy = 38, r0 = 24;
459
+ pg.beginPath(); pg.moveTo(u0 - 6, 68); pg.quadraticCurveTo(u0 + 2, 42, cx - r0, cy);
460
+ for (let th = Math.PI; th <= Math.PI * 3.25; th += 0.14) { const r = r0 - (th - Math.PI) / (Math.PI * 2.25) * (r0 - 5); pg.lineTo(cx + Math.cos(th) * r, cy + Math.sin(th) * r); } pg.stroke(); }
461
+ const pd = pg.getImageData(0, 0, SL, BW).data;
462
+ const sc = P.canvas(SL, BW), g = sc.getContext('2d');
463
+ g.fillStyle = 'rgb(112,103,92)'; g.fillRect(0, 0, SL, BW);
464
+ for (let j = 0; j < NR; j++) {
465
+ const r = rng(500 + j), n = Math.round(PER / bs), ws = Array.from({ length: n }, () => 0.84 + r() * 0.32), sum = ws.reduce((a, b) => a + b, 0);
466
+ const tiles = []; let u = (j % 2) * bs * 0.5;
467
+ ws.forEach(w0 => { const w = w0 / sum * PER; tiles.push([u, w, Array.from({ length: 8 }, () => (r() - .5) * 1.4), (r() - .5) * 0.12, (r() - .5) * 14]); u += w; });
468
+ for (let k = -1; k <= NP; k++) for (const [u0, w, jt, jl, ja] of tiles) {
469
+ const x0 = k * PER + u0, y0 = j * bs, cx = x0 + w / 2, cy = y0 + bs / 2;
470
+ if (cx < -w || cx > SL + w) continue;
471
+ const o = (Math.min(BW - 1, cy | 0) * SL + Math.max(0, Math.min(SL - 1, cx | 0))) * 4, f = 1 + jl;
472
+ const col = [pd[o] * f + ja, pd[o + 1] * f + ja, pd[o + 2] * f + ja], q = [[x0 + 0.9, y0 + 0.9], [x0 + w - 0.9, y0 + 0.9], [x0 + w - 0.9, y0 + bs - 0.9], [x0 + 0.9, y0 + bs - 0.9]].map((p, i) => [p[0] + jt[i * 2] * 0.6, p[1] + jt[i * 2 + 1] * 0.6]);
473
+ g.fillStyle = P.rgb(col); g.beginPath(); q.forEach((p, i) => i ? g.lineTo(...p) : g.moveTo(...p)); g.closePath(); g.fill();
474
+ g.strokeStyle = P.rgb(P.mix(col, [255, 255, 255], 0.22)); g.lineWidth = 1; g.beginPath(); g.moveTo(...q[0]); g.lineTo(...q[1]); g.stroke();
475
+ g.strokeStyle = P.rgb(P.mix(col, [0, 0, 0], 0.18)); g.beginPath(); g.moveTo(...q[3]); g.lineTo(...q[2]); g.stroke();
476
+ }
477
+ }
478
+ return sc;
479
+ }
480
+ // 杯子:4.4px 小石块拼片(深色描边石块+金色杯身+亮杯口+双耳+杯脚),整块随手移动
481
+ const CSC = 1.3, CSP = [80, 24];
482
+ function cupSprite() {
483
+ const w = 170, h = 110, fc = P.canvas(w, h), fg = fc.getContext('2d', { willReadFrequently: true });
484
+ fg.translate(CSP[0], CSP[1]); fg.scale(CSC, CSC);
485
+ const cw = 46, ch = 38;
486
+ fg.lineJoin = 'round'; fg.lineCap = 'round';
487
+ fg.strokeStyle = C.outline; fg.lineWidth = 11; [-1, 1].forEach(d => { fg.beginPath(); fg.arc(d * cw * 0.5, ch * 0.28, ch * 0.27, d < 0 ? Math.PI * 0.5 : -Math.PI * 0.5, d < 0 ? Math.PI * 1.5 : Math.PI * 0.5); fg.stroke(); });
488
+ fg.strokeStyle = C.gold; fg.lineWidth = 5; [-1, 1].forEach(d => { fg.beginPath(); fg.arc(d * cw * 0.5, ch * 0.28, ch * 0.27, d < 0 ? Math.PI * 0.5 : -Math.PI * 0.5, d < 0 ? Math.PI * 1.5 : Math.PI * 0.5); fg.stroke(); });
489
+ const bowl = new Path2D(); bowl.moveTo(-cw / 2, 0); bowl.quadraticCurveTo(-cw * 0.46, ch * 0.74, 0, ch * 0.74); bowl.quadraticCurveTo(cw * 0.46, ch * 0.74, cw / 2, 0); bowl.closePath();
490
+ const foot = new Path2D(); foot.rect(-4, ch * 0.7, 8, ch * 0.3); foot.ellipse(0, ch * 1.02, cw * 0.3, 5, 0, 0, Math.PI * 2);
491
+ fg.lineWidth = 6; fg.strokeStyle = C.outline; fg.stroke(bowl); fg.stroke(foot);
492
+ fg.fillStyle = C.gold; fg.fill(bowl); fg.fill(foot);
493
+ fg.save(); fg.clip(bowl); fg.fillStyle = C.gold2; fg.fillRect(cw * 0.12, 0, cw, ch); fg.fillStyle = '#f4d77a'; fg.fillRect(-cw * 0.36, 6, 6, ch * 0.5); fg.restore();
494
+ fg.fillStyle = '#f2d77c'; fg.beginPath(); fg.ellipse(0, 0, cw / 2, 5.5, 0, 0, Math.PI * 2); fg.fill(); fg.strokeStyle = C.outline; fg.lineWidth = 4; fg.stroke();
495
+ fg.fillStyle = '#7a5418'; fg.beginPath(); fg.ellipse(0, 0.5, cw / 2 - 6, 2.6, 0, 0, Math.PI * 2); fg.fill();
496
+ const fd = fg.getImageData(0, 0, w, h).data, sp = P.canvas(w, h), sg = sp.getContext('2d'), ts = 4.4, r = rng(91);
497
+ // 灰浆底:只在杯形(alpha)里
498
+ sg.drawImage(fc, 0, 0); sg.globalCompositeOperation = 'source-in'; sg.fillStyle = 'rgb(96,86,74)'; sg.fillRect(0, 0, w, h); sg.globalCompositeOperation = 'source-over';
499
+ for (let y = 0; y < h; y += ts) for (let x = ((y / ts) % 2) * ts * 0.5 - ts; x < w; x += ts) {
500
+ const cx = x + ts / 2, cy = y + ts / 2; if (cx < 0 || cx >= w || cy >= h) continue;
501
+ const o = ((cy | 0) * w + (cx | 0)) * 4; if (fd[o + 3] < 110) continue;
502
+ const f = 1 + (r() - .5) * 0.12; sg.fillStyle = P.rgb([fd[o] * f, fd[o + 1] * f, fd[o + 2] * f]);
503
+ sg.fillRect(x + 0.6 + (r() - .5) * 0.5, y + 0.6 + (r() - .5) * 0.5, ts - 1.2, ts - 1.2);
504
+ }
505
+ return sp;
506
+ }
507
+ function steamTiles(c, cup, t, a) { // 白石热气:两列方石沿正弦上行
508
+ if (a < 0.03) return;
509
+ c.save(); c.globalAlpha = a;
510
+ for (let k = 0; k < 2; k++) for (let i = 0; i < 7; i++) {
511
+ const q = ((i + (t * 2.2) % 1) / 7), x = cup.x - 9 + k * 18 + Math.sin(t * 6 + k * 2.2 - q * 6) * 9 * q, y = cup.y - 12 - q * 76, s = 7 - q * 2;
512
+ c.globalAlpha = a * (1 - q * 0.7); c.fillStyle = 'rgb(96,86,74)'; c.fillRect(x - s / 2 - 1, y - s / 2 - 1, s + 2, s + 2); c.fillStyle = '#fbf8f0'; c.fillRect(x - s / 2, y - s / 2, s, s);
513
+ }
514
+ c.restore();
515
+ }
516
+ function edt(F, w, h) { // 2D 平方欧氏距离变换(Felzenszwalb–Huttenlocher)
517
+ const n = Math.max(w, h), f = new Float64Array(n), d = new Float64Array(n), v = new Int32Array(n), z = new Float64Array(n + 1);
518
+ const pass = (len) => { let k = 0; v[0] = 0; z[0] = -1e30; z[1] = 1e30;
519
+ for (let q = 1; q < len; q++) { let s = ((f[q] + q * q) - (f[v[k]] + v[k] * v[k])) / (2 * q - 2 * v[k]); while (s <= z[k]) { k--; s = ((f[q] + q * q) - (f[v[k]] + v[k] * v[k])) / (2 * q - 2 * v[k]); } k++; v[k] = q; z[k] = s; z[k + 1] = 1e30; }
520
+ k = 0; for (let q = 0; q < len; q++) { while (z[k + 1] < q) k++; d[q] = (q - v[k]) * (q - v[k]) + f[v[k]]; } };
521
+ const out = new Float64Array(w * h);
522
+ for (let x = 0; x < w; x++) { for (let y = 0; y < h; y++) f[y] = F[y * w + x]; pass(h); for (let y = 0; y < h; y++) out[y * w + x] = d[y]; }
523
+ for (let y = 0; y < h; y++) { for (let x = 0; x < w; x++) f[x] = out[y * w + x]; pass(w); for (let x = 0; x < w; x++) out[y * w + x] = d[x]; }
524
+ return out;
525
+ }
526
+
527
+ return {
528
+ init() { if (!S) build(); },
529
+ draw(c, lt, t) {
530
+ if (!S) build();
531
+ const { bg, og, ID, SH, SO, TCr, TCg, TCb } = S;
532
+ fullBase(bg, S.sb, lt, t);
533
+ const bd = bg.getImageData(0, 0, W, H).data;
534
+ const dyn = S.dyn; for (let i = 0; i < dyn.length; i++) S.color(dyn[i], bd);
535
+ for (const R of S.rects) {
536
+ const dd = R.img.data; let o = 0;
537
+ for (let y = R.y; y < R.y + R.h; y++) { let p = y * W + R.x; for (let x = 0; x < R.w; x++, p++, o += 4) {
538
+ const k = ID[p];
539
+ if (k < 0) { const q = p * 4; dd[o] = SO[q]; dd[o + 1] = SO[q + 1]; dd[o + 2] = SO[q + 2]; }
540
+ else { const sh = SH[p]; dd[o] = (TCr[k] * sh) >> 7; dd[o + 1] = (TCg[k] * sh) >> 7; dd[o + 2] = (TCb[k] * sh) >> 7; }
541
+ dd[o + 3] = 255;
542
+ } }
543
+ og.putImageData(R.img, R.x, R.y);
544
+ }
545
+ c.drawImage(S.oc, 0, 0);
546
+ // 外框传送带:同一条周期石块带,四边各自局部坐标(顺时针),按相位平移
547
+ const off = ((borderPhase(lt) % PER) + PER) % PER;
548
+ for (const [a, b, c2, d, e, f, len] of SIDES) { c.save(); c.transform(a, b, c2, d, e, f); c.beginPath(); c.rect(BW, 0, len - 2 * BW, BW); c.clip(); c.drawImage(S.strip, BW - 2 * PER + off, 0); c.restore(); }
549
+ // 杯子拼片与热气(跟着手走)
550
+ const { ch, G } = poses(lt, t), AP = armPose(G, ch.cup);
551
+ c.save(); c.translate(AP.cup.x, AP.cup.y); c.rotate(AP.cup.tilt); c.drawImage(S.cupSp, -CSP[0], -CSP[1]); c.restore();
552
+ steamTiles(c, AP.cup, t, 1 - ch.cup * 0.85);
553
+ },
554
+ };
555
+ })();