@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,524 @@
1
+ // 公元前 1350 年 埃及墓室壁画(阿马尔纳时期)——纯代码。
2
+ // 管线:①静态层(灰泥墙+彩色几何边框+象形文字栏+窗/阿顿日盘+陶罐/供桌/椅)缓存
3
+ // → ②会动的母题(阿顿光线末端小手一张一合、水纹象形字流动、圣甲虫推日球)
4
+ // → ③角色:埃及程式(侧脸正面眼、正面肩、平涂+深色勾线)→ ④壁画表面(斑驳 multiply+裂纹+颗粒)
5
+ SCENES['02_egypt'] = (() => {
6
+ const W = 1920, H = 1080, { clamp, lerp } = U, P = PAINT;
7
+ const C = {
8
+ wall: '#eadcb8', wall2: '#d8c597', line: '#2a1810', rule: '#9a2b22',
9
+ red: '#b8322a', blue: '#2a58a8', green: '#2e8a55', yellow: '#e2b13a', black: '#231a14',
10
+ gold: '#e0ab38', goldD: '#a8741c', goldL: '#f3d27a', navy: '#1c2236', cream: '#f2ead6', white: '#f6f2e8',
11
+ skin: '#e6b282', skinD: '#c98d5c', terra: '#d4936a', lapis: '#2c56b0', turq: '#3fa6a0',
12
+ catO: '#e08a3c', catS: '#b45a1c', catW: '#f6efe0',
13
+ };
14
+ const BAND = [C.red, C.blue, C.yellow, C.green]; // 埃及边框四色
15
+
16
+ // ---------------- 小工具 ----------------
17
+ const poly = (g, pts, close = true) => { g.beginPath(); pts.forEach((p, i) => i ? g.lineTo(p[0], p[1]) : g.moveTo(p[0], p[1])); if (close) g.closePath(); };
18
+ const fs = (g, fill, stroke, lw = 2.5) => { if (fill) { g.fillStyle = fill; g.fill(); } if (stroke) { g.strokeStyle = stroke; g.lineWidth = lw; g.stroke(); } };
19
+ const sm = (pts, closed = true) => RIG.smooth(pts, closed);
20
+
21
+ // ---------------- 象形文字字形库(原点居中,约 50px 宽;h = 占位高度) ----------------
22
+ // 每个字形:{h, cols:[可选颜色], d(g) };g 已设好 strokeStyle/fillStyle/lineWidth
23
+ const GL = {
24
+ ankh: { h: 60, cols: [C.blue, C.green, C.black, C.red], d(g) { g.lineWidth = 3.6; g.beginPath(); g.ellipse(0, -17, 8, 11, 0, 0, 7); g.moveTo(-16, -3); g.lineTo(16, -3); g.moveTo(0, -5); g.lineTo(0, 29); g.stroke(); } },
25
+ sun: { h: 40, cols: [C.black, C.blue, C.red], d(g) { g.lineWidth = 3; g.beginPath(); g.arc(0, 0, 15, 0, 7); g.stroke(); g.beginPath(); g.arc(0, 0, 4, 0, 7); g.fill(); } },
26
+ bread: { h: 26, cols: [C.red, C.blue, C.green], d(g) { g.beginPath(); g.arc(0, 9, 19, Math.PI, 0); g.closePath(); g.stroke(); g.beginPath(); g.moveTo(-12, 4); g.lineTo(12, 4); g.stroke(); } },
27
+ reed: { h: 62, cols: [C.black, C.green, C.red], d(g) { g.beginPath(); g.moveTo(-2, 29); g.quadraticCurveTo(-11, 0, 1, -29); g.quadraticCurveTo(7, -2, 4, 29); g.stroke(); g.beginPath(); g.moveTo(1, -29); g.quadraticCurveTo(9, -31, 10, -24); g.stroke(); } },
28
+ owl: { h: 60, cols: [C.black, C.blue], d(g) { g.beginPath(); g.ellipse(3, 6, 12, 19, 0.1, 0, 7); g.stroke(); g.beginPath(); g.arc(-2, -18, 10, 0, 7); g.stroke(); g.beginPath(); g.arc(-6, -19, 2.4, 0, 7); g.arc(2, -19, 2.4, 0, 7); g.fill(); g.beginPath(); g.moveTo(-2, -14); g.lineTo(-3, -9); g.moveTo(9, 22); g.lineTo(18, 29); g.moveTo(-3, 24); g.lineTo(-5, 30); g.moveTo(4, 24); g.lineTo(3, 30); g.stroke(); } },
29
+ falcon: { h: 60, cols: [C.black, C.red, C.blue], d(g) { g.beginPath(); g.arc(-6, -20, 7, 0, 7); g.fill(); poly(g, [[-12, -21], [-19, -16], [-11, -15]]); g.fill(); g.beginPath(); g.ellipse(2, 1, 10, 20, -0.35, 0, 7); g.stroke(); poly(g, [[8, 16], [20, 30], [6, 27]]); g.fill(); g.beginPath(); g.moveTo(-3, 19); g.lineTo(-5, 30); g.moveTo(2, 19); g.lineTo(1, 30); g.moveTo(-1, -8); g.quadraticCurveTo(10, 0, 12, 14); g.stroke(); } },
30
+ heron: { h: 64, cols: [C.green, C.black, C.blue], d(g) { g.beginPath(); g.moveTo(-20, -22); g.lineTo(-8, -26); g.quadraticCurveTo(0, -28, -1, -18); g.quadraticCurveTo(-6, -8, 2, -2); g.stroke(); g.beginPath(); g.ellipse(8, 4, 13, 8, 0.4, 0, 7); g.stroke(); g.beginPath(); g.moveTo(4, 11); g.lineTo(2, 31); g.moveTo(10, 11); g.lineTo(12, 31); g.moveTo(18, 9); g.lineTo(24, 14); g.stroke(); } },
31
+ snake: { h: 30, cols: [C.green, C.black, C.red], d(g) { g.lineWidth = 3.2; g.beginPath(); g.moveTo(22, 8); g.bezierCurveTo(10, -4, 4, 14, -6, 6); g.bezierCurveTo(-14, 0, -16, -4, -20, -2); g.stroke(); g.beginPath(); g.moveTo(-20, -2); g.lineTo(-23, -9); g.moveTo(-17, -3); g.lineTo(-15, -10); g.stroke(); } },
32
+ eye: { h: 36, cols: [C.green, C.black, C.blue], d(g) { g.beginPath(); g.moveTo(-18, 0); g.quadraticCurveTo(0, -12, 18, 0); g.quadraticCurveTo(0, 9, -18, 0); g.stroke(); g.beginPath(); g.arc(0, -1, 4.5, 0, 7); g.fill(); g.beginPath(); g.moveTo(-18, -12); g.quadraticCurveTo(0, -20, 20, -11); g.moveTo(-4, 6); g.lineTo(-6, 16); g.moveTo(6, 5); g.quadraticCurveTo(14, 18, 4, 16); g.stroke(); } },
33
+ cartouche: { h: 74, cols: [C.black, C.red], d(g) { g.beginPath(); g.ellipse(0, -2, 15, 31, 0, 0, 7); g.stroke(); g.beginPath(); g.moveTo(-14, 33); g.lineTo(14, 33); g.stroke(); g.lineWidth = 2; g.beginPath(); g.arc(0, -18, 5, 0, 7); g.moveTo(-7, -2); g.lineTo(7, -2); g.moveTo(-6, 8); g.lineTo(-3, 13); g.lineTo(0, 8); g.lineTo(3, 13); g.lineTo(6, 8); g.stroke(); } },
34
+ scarab: { h: 46, cols: [C.blue, C.black], d(g) { g.beginPath(); g.ellipse(0, 6, 10, 14, 0, 0, 7); g.fill(); g.beginPath(); g.arc(0, -11, 6, 0, 7); g.fill(); g.beginPath(); for (const s of [-1, 1]) for (const k of [-1, 0, 1]) { g.moveTo(s * 9, 6 + k * 8); g.lineTo(s * 17, 2 + k * 11); } g.stroke(); } },
35
+ seated: { h: 58, cols: [C.red, C.black, C.blue], d(g) { g.beginPath(); g.arc(-2, -21, 6.5, 0, 7); g.fill(); poly(g, [[-6, -13], [4, -13], [6, 6], [16, 8], [17, 18], [-10, 18], [-10, 2]]); g.fill(); g.beginPath(); g.moveTo(-6, -8); g.lineTo(-17, -16); g.stroke(); g.beginPath(); g.moveTo(-14, 26); g.lineTo(18, 26); g.stroke(); } },
36
+ leg: { h: 50, cols: [C.green, C.black, C.red], d(g) { poly(g, [[-3, -24], [6, -24], [6, 18], [16, 22], [16, 26], [-4, 26]]); g.stroke(); } },
37
+ arm: { h: 24, cols: [C.black, C.red], d(g) { poly(g, [[-22, -4], [10, -4], [16, -8], [22, -6], [16, 2], [-22, 4]]); g.stroke(); } },
38
+ basket: { h: 26, cols: [C.blue, C.green, C.black], d(g) { g.beginPath(); g.moveTo(-20, -4); g.lineTo(20, -4); g.quadraticCurveTo(18, 12, 0, 12); g.quadraticCurveTo(-18, 12, -20, -4); g.stroke(); g.beginPath(); g.arc(22, 2, 5, -1.5, 1.6); g.stroke(); } },
39
+ house: { h: 36, cols: [C.black, C.red], d(g) { g.beginPath(); g.moveTo(-6, 14); g.lineTo(-18, 14); g.lineTo(-18, -14); g.lineTo(18, -14); g.lineTo(18, 14); g.lineTo(6, 14); g.stroke(); } },
40
+ strokes: { h: 34, cols: [C.red, C.black], d(g) { g.beginPath(); for (const x of [-8, 0, 8]) { g.moveTo(x, -14); g.lineTo(x, 14); } g.stroke(); } },
41
+ feather: { h: 60, cols: [C.blue, C.green, C.black], d(g) { g.beginPath(); g.moveTo(-2, 29); g.quadraticCurveTo(-8, -10, 4, -29); g.quadraticCurveTo(12, -18, 6, 0); g.quadraticCurveTo(3, 15, 2, 29); g.stroke(); } },
42
+ djed: { h: 62, cols: [C.blue, C.green], d(g) { g.beginPath(); g.moveTo(-6, 29); g.lineTo(-6, -10); g.moveTo(6, 29); g.lineTo(6, -10); for (let k = 0; k < 4; k++) { g.moveTo(-13, -12 - k * 5); g.lineTo(13, -12 - k * 5); } g.moveTo(-12, 29); g.lineTo(12, 29); g.stroke(); } },
43
+ shen: { h: 42, cols: [C.red, C.blue], d(g) { g.beginPath(); g.arc(0, -4, 13, 0, 7); g.stroke(); g.beginPath(); g.moveTo(-14, 13); g.lineTo(14, 13); g.stroke(); } },
44
+ jug: { h: 50, cols: [C.red, C.green, C.blue], d(g) { g.beginPath(); g.moveTo(-6, -22); g.lineTo(6, -22); g.lineTo(5, -14); g.quadraticCurveTo(16, -6, 12, 12); g.quadraticCurveTo(8, 22, 0, 22); g.quadraticCurveTo(-8, 22, -12, 12); g.quadraticCurveTo(-16, -6, -5, -14); g.closePath(); g.stroke(); g.beginPath(); g.moveTo(-10, 0); g.lineTo(10, 0); g.stroke(); } },
45
+ lotus: { h: 56, cols: [C.green, C.blue], d(g) { g.beginPath(); g.moveTo(0, 27); g.lineTo(0, -6); g.stroke(); poly(g, [[0, -6], [-12, -18], [-6, -26], [0, -16], [6, -26], [12, -18]]); g.stroke(); } },
46
+ nfr: { h: 56, cols: [C.black, C.red], d(g) { g.beginPath(); g.ellipse(0, 12, 10, 14, 0, 0, 7); g.moveTo(0, -2); g.lineTo(0, -26); g.moveTo(-6, -18); g.lineTo(6, -18); g.moveTo(-6, -12); g.lineTo(6, -12); g.stroke(); } },
47
+ mouth: { h: 20, cols: [C.red], d(g) { g.beginPath(); g.moveTo(-18, 0); g.quadraticCurveTo(0, -9, 18, 0); g.quadraticCurveTo(0, 9, -18, 0); g.stroke(); } },
48
+ water: { h: 30, cols: [C.blue], water: true, d(g, ph = 0) { g.lineWidth = 2.6; for (let r = -1; r <= 1; r++) { g.beginPath(); for (let i = 0; i <= 16; i++) { const x = -21 + i * 2.625, y = r * 9 + ((i + ph) % 2 < 1 ? -3 : 3); i ? g.lineTo(x, y) : g.moveTo(x, y); } g.stroke(); } } },
49
+ };
50
+ const GN = Object.keys(GL);
51
+ // 栏位:[分栏线 x 列表, y0, y1](照原片量的位置)
52
+ const GROUPS = [[[78, 154, 230, 306], 66, 566], [[852, 932, 1012, 1092, 1172], 66, 452], [[1560, 1637, 1713, 1790, 1866], 252, 882], [[298, 366], 614, 886]];
53
+ const GLYPHS = (() => { // 种子化排布,模块初始化时算好(水纹字要单独动起来,需要知道位置)
54
+ const r = U.rng(1350), out = [];
55
+ GROUPS.forEach(([xs, y0, y1]) => { for (let k = 0; k < xs.length - 1; k++) {
56
+ const used = {};
57
+ const cx = (xs[k] + xs[k + 1]) / 2; let y = y0 + 8, prev = '';
58
+ while (true) {
59
+ let n; do { n = GN[(r() * GN.length) | 0]; } while (n === prev || used[n] > 2 || ((n === 'scarab' || n === 'water') && r() < 0.6)); prev = n; used[n] = (used[n] || 0) + 1;
60
+ const gl = GL[n], s = (xs[k + 1] - xs[k]) / 78 * (1.0 + r() * 0.14);
61
+ if (y + gl.h * s > y1) break;
62
+ out.push({ n, x: cx + (r() - 0.5) * 4, y: y + gl.h * s / 2, s, col: gl.cols[(r() * gl.cols.length) | 0], ph: r() * 2 });
63
+ y += gl.h * s + 6 + r() * 5;
64
+ }
65
+ } });
66
+ // 保证有 ≥6 个水纹字(它们是会流动的那批),不够就把扁字形换成水纹
67
+ let nw = out.filter(q => q.n === 'water').length;
68
+ for (const q of out) { if (nw >= 6) break; if (['mouth', 'bread', 'basket', 'arm', 'strokes'].includes(q.n)) { q.n = 'water'; q.col = C.blue; nw++; } }
69
+ return out;
70
+ })();
71
+ const drawGlyph = (g, q, ph = 0, rv = 1) => { g.save(); g.translate(q.x, q.y); g.scale(q.s, q.s); g.strokeStyle = q.col; g.fillStyle = q.col; g.lineWidth = 3.1; g.lineCap = 'round'; g.lineJoin = 'round';
72
+ if (rv < 1) { g.setLineDash([Math.max(0.01, rv * 95), 999]); if (rv < 0.55) g.fillStyle = 'rgba(0,0,0,0)'; }
73
+ GL[q.n].d(g, ph); g.restore(); };
74
+ // 「铭文被写出来」:每 0.12s 从打乱的字表里取 4 个字(分散在各栏),擦掉→按笔画描出(dash 揭示),笔迹先是亮金再落回原色
75
+ const WRITERS = (() => { const r = U.rng(77), idx = GLYPHS.map((q, i) => i).filter(i => !GL[GLYPHS[i].n].water); for (let i = idx.length - 1; i > 0; i--) { const j = (r() * (i + 1)) | 0; [idx[i], idx[j]] = [idx[j], idx[i]]; } return idx; })();
76
+ const WRITE_SLOT = 0.12, WRITE_DUR = 0.36, WRITE_N = 6;
77
+ function writeGlyphs(c, lt) {
78
+ const wall = P.texture('eg_wall', C.wall), s0 = Math.floor(lt / WRITE_SLOT);
79
+ for (let s = Math.max(0, s0 - 2); s <= s0; s++) for (let m = 0; m < WRITE_N; m++) {
80
+ const q = GLYPHS[WRITERS[(s * WRITE_N + m) % WRITERS.length]], p = clamp((lt - s * WRITE_SLOT) / WRITE_DUR);
81
+ if (p >= 1) continue;
82
+ const w = 64 * q.s, h = (GL[q.n].h + 10) * q.s, x0 = q.x - w / 2, y0 = q.y - h / 2;
83
+ c.drawImage(wall, x0, y0, w, h, x0, y0, w, h);
84
+ const glow = 1 - p, col = P.rgb(P.mix(P.hex(q.col), [246, 200, 70], glow * 0.85));
85
+ c.save(); c.shadowColor = `rgba(255,200,80,${0.9 * glow})`; c.shadowBlur = 10 * glow;
86
+ drawGlyph(c, { ...q, col }, q.ph, Math.min(1, p * 1.35));
87
+ c.restore();
88
+ }
89
+ }
90
+
91
+ // ---------------- 彩色几何边框 ----------------
92
+ function blocks(g, x0, y0, x1, y1, len, off = 0) { // 沿长边排彩块,块间米白细缝,外框黑线
93
+ const hor = (x1 - x0) > (y1 - y0), L = hor ? x1 - x0 : y1 - y0;
94
+ g.fillStyle = C.cream; g.fillRect(x0, y0, x1 - x0, y1 - y0);
95
+ for (let s = 0, k = off; s < L; s += len, k++) {
96
+ g.fillStyle = BAND[k % 4];
97
+ if (hor) g.fillRect(x0 + s + 3, y0 + 3, Math.min(len - 6, L - s - 3), y1 - y0 - 6); else g.fillRect(x0 + 3, y0 + s + 3, x1 - x0 - 6, Math.min(len - 6, L - s - 3));
98
+ g.fillStyle = C.black; if (hor) g.fillRect(x0 + s, y0, 2, y1 - y0); else g.fillRect(x0, y0 + s, x1 - x0, 2);
99
+ }
100
+ g.strokeStyle = C.black; g.lineWidth = 2; g.strokeRect(x0, y0, x1 - x0, y1 - y0);
101
+ }
102
+
103
+ // ---------------- 静态层 ----------------
104
+ const DISK = [580, 266];
105
+ function staticLayer(g) {
106
+ // 灰泥墙:底色+斑驳+颗粒
107
+ g.drawImage(P.texture('eg_wall', C.wall, { scale: 0.0035, amt: 16, grain: 12, dark: C.wall2, seed: 13 }), 0, 0);
108
+ // 象形文字栏:分栏红线+字形
109
+ g.lineCap = 'round';
110
+ GROUPS.forEach(([xs, y0, y1]) => xs.forEach(x => { g.strokeStyle = C.rule; g.lineWidth = 2.6; g.beginPath(); g.moveTo(x, y0); g.lineTo(x, y1); g.stroke(); }));
111
+ GLYPHS.forEach(q => { if (!GL[q.n].water) drawGlyph(g, q); });
112
+ // —— 窗 ——
113
+ // 檐口(cavetto):上宽下窄的竖条纹弧面
114
+ const top = [338, 822], bot = [362, 798], cy0 = 128, cy1 = 172, st = [C.blue, C.white, C.red, C.white, C.green, C.white];
115
+ const n = 30;
116
+ for (let i = 0; i < n; i++) {
117
+ const a = i / n, b = (i + 1) / n;
118
+ poly(g, [[lerp(top[0], top[1], a), cy0], [lerp(top[0], top[1], b), cy0], [lerp(bot[0], bot[1], b), cy1], [lerp(bot[0], bot[1], a), cy1]]);
119
+ g.fillStyle = st[i % 6]; g.fill();
120
+ }
121
+ g.strokeStyle = C.black; g.lineWidth = 1.5;
122
+ for (let i = 0; i <= n; i++) { const a = i / n; g.beginPath(); g.moveTo(lerp(top[0], top[1], a), cy0); g.lineTo(lerp(bot[0], bot[1], a), cy1); g.stroke(); }
123
+ // 檐口内凹的阴影(上部暗一点)
124
+ const cg = g.createLinearGradient(0, cy0, 0, cy1); cg.addColorStop(0, 'rgba(40,20,10,.28)'); cg.addColorStop(0.5, 'rgba(40,20,10,0)'); g.fillStyle = cg;
125
+ poly(g, [[top[0], cy0], [top[1], cy0], [bot[1], cy1], [bot[0], cy1]]); g.fill();
126
+ g.fillStyle = C.gold; g.fillRect(334, 118, 492, 11); g.strokeStyle = C.black; g.lineWidth = 2; g.strokeRect(334, 118, 492, 11);
127
+ g.fillStyle = C.gold; g.fillRect(358, 172, 444, 13); g.strokeRect(358, 172, 444, 13);
128
+ g.fillStyle = C.goldD; for (let x = 364; x < 798; x += 14) g.fillRect(x, 175, 6, 7);
129
+ // 窗框:彩块(左、右、下)+顶部蓝带
130
+ blocks(g, 378, 185, 408, 560, 40, 1); blocks(g, 772, 185, 802, 560, 40, 3); blocks(g, 378, 530, 802, 560, 40, 0);
131
+ g.fillStyle = C.blue; g.fillRect(408, 185, 364, 15); g.strokeStyle = C.black; g.strokeRect(408, 185, 364, 15);
132
+ // 窗内:白底(略带灰泥纹)
133
+ g.fillStyle = '#f2ede1'; g.fillRect(408, 200, 364, 330);
134
+ g.globalAlpha = 0.25; g.drawImage(P.texture('eg_wall', C.wall), 408, 200, 364, 330, 408, 200, 364, 330); g.globalAlpha = 1;
135
+ g.fillStyle = C.blue; g.fillRect(408, 200, 364, 9); g.fillRect(762, 200, 10, 30); poly(g, [[740, 209], [772, 209], [772, 222]]); g.fill();
136
+ g.strokeStyle = C.black; g.lineWidth = 2; g.strokeRect(408, 200, 364, 330);
137
+ // 阿顿日盘+圣蛇+安卡
138
+ g.beginPath(); g.arc(DISK[0], DISK[1], 46, 0, 7); g.fillStyle = '#c8372b'; g.fill(); g.lineWidth = 3; g.strokeStyle = '#7a1c14'; g.stroke();
139
+ g.beginPath(); g.arc(DISK[0], DISK[1], 38, 0, 7); g.strokeStyle = 'rgba(255,200,150,.35)'; g.lineWidth = 2; g.stroke();
140
+ g.fillStyle = C.gold; g.beginPath(); g.ellipse(580, 300, 7, 10, 0, 0, 7); g.fill(); g.strokeStyle = C.black; g.lineWidth = 1.6; g.stroke();
141
+ g.beginPath(); g.moveTo(580, 290); g.quadraticCurveTo(592, 305, 580, 318); g.lineWidth = 4; g.strokeStyle = C.gold; g.stroke();
142
+ drawGlyph(g, { n: 'ankh', x: 580, y: 332, s: 0.42, col: C.blue });
143
+ // —— 左侧尖底陶罐 ×2,桌边小罐 ——
144
+ jar(g, 108, 644, 1); jar(g, 212, 644, 1); jar(g, 886, 714, 0.72);
145
+ // —— 供桌 ——
146
+ poly(g, [[975, 660], [962, 702], [984, 748], [984, 792], [950, 862], [930, 900], [1090, 900], [1070, 862], [1036, 792], [1036, 748], [1058, 702], [1045, 660]]); fs(g, C.white, C.line, 2.5);
147
+ g.save(); g.clip(); g.fillStyle = C.blue; g.fillRect(940, 752, 140, 10); g.fillStyle = C.red; g.fillRect(940, 768, 140, 10); g.restore();
148
+ g.strokeStyle = 'rgba(140,110,80,.5)'; g.lineWidth = 1.5; g.beginPath(); g.moveTo(1000, 800); g.quadraticCurveTo(990, 850, 960, 890); g.stroke();
149
+ g.fillStyle = C.white; g.fillRect(820, 640, 380, 20); g.strokeStyle = C.line; g.lineWidth = 2.5; g.strokeRect(820, 640, 380, 20);
150
+ g.strokeStyle = C.red; g.lineWidth = 2; g.beginPath(); g.moveTo(824, 655); g.lineTo(1196, 655); g.stroke();
151
+ // 供品:白圆锥面包、金碟、蓝葡萄、绿荷叶盘
152
+ poly(g, [[836, 640], [844, 590], [852, 568], [860, 590], [870, 640]]); fs(g, C.white, C.line, 2.2);
153
+ g.beginPath(); g.moveTo(870, 640); g.quadraticCurveTo(872, 604, 908, 602); g.quadraticCurveTo(944, 604, 946, 640); g.closePath(); fs(g, C.gold, C.line, 2.2);
154
+ g.beginPath(); g.arc(908, 626, 5, 0, 7); g.strokeStyle = C.goldD; g.lineWidth = 2; g.stroke();
155
+ [[892, 600], [906, 600], [920, 600], [899, 587], [913, 587], [906, 574], [885, 588], [927, 588], [892, 575], [920, 575], [906, 561]].forEach(([x, y]) => { g.beginPath(); g.arc(x, y, 8, 0, 7); fs(g, '#2c4aa0', '#141a3a', 1.5); g.fillStyle = 'rgba(160,190,255,.6)'; g.beginPath(); g.arc(x - 2.5, y - 2.5, 2.2, 0, 7); g.fill(); });
156
+ g.beginPath(); g.ellipse(888, 541, 52, 9, 0, 0, 7); fs(g, C.green, C.line, 2); g.strokeStyle = '#1f6a40'; g.lineWidth = 1.5; g.beginPath(); g.moveTo(842, 541); g.lineTo(934, 541); g.stroke();
157
+ // —— 椅子(金色细腿椅,椅背红蓝块) ——
158
+ chair(g);
159
+ // 脚边蓝碗
160
+ g.beginPath(); g.moveTo(1342, 878); g.lineTo(1418, 878); g.quadraticCurveTo(1412, 900, 1380, 900); g.quadraticCurveTo(1348, 900, 1342, 878); fs(g, '#3a78c8', C.line, 2);
161
+ g.beginPath(); g.ellipse(1380, 878, 38, 5, 0, 0, 7); fs(g, '#8cc0ee', C.line, 1.6);
162
+ // —— 地线与底部多层饰带 ——
163
+ g.fillStyle = C.black; g.fillRect(0, 898, W, 6);
164
+ g.fillStyle = C.cream; g.fillRect(0, 904, W, 106);
165
+ blocks(g, -20, 914, W + 20, 948, 64, 0);
166
+ g.fillStyle = C.yellow; g.fillRect(0, 961, W, 15); g.fillStyle = C.red; g.fillRect(0, 984, W, 14);
167
+ g.fillStyle = C.black; [912, 960, 977, 983, 999, 1008].forEach(y => g.fillRect(0, y, W, 1.6));
168
+ // 深蓝带(细颗粒)
169
+ g.fillStyle = C.navy; g.fillRect(0, 1010, W, 70);
170
+ const r = U.rng(7); for (let i = 0; i < 2600; i++) { g.fillStyle = r() < 0.5 ? 'rgba(90,100,140,.35)' : 'rgba(5,8,20,.45)'; g.fillRect(r() * W, 1010 + r() * 70, 1 + r() * 2, 1 + r() * 2); }
171
+ // —— 顶部与两侧彩色边框 ——
172
+ blocks(g, -10, 6, W + 10, 40, 56, 2);
173
+ blocks(g, 32, 40, 58, 898, 46, 1); blocks(g, 1890, 40, 1916, 898, 46, 3);
174
+ }
175
+ function jar(g, cx, y0, s) {
176
+ g.save(); g.translate(cx, y0); g.scale(s, s);
177
+ // 支架(X 形白腿)
178
+ g.strokeStyle = C.line; g.lineWidth = 2.5; g.fillStyle = C.white;
179
+ poly(g, [[-26, 256], [-20, 256], [10, 196], [4, 196]]); fs(g, C.white, C.line, 2); poly(g, [[26, 256], [20, 256], [-10, 196], [-4, 196]]); fs(g, C.white, C.line, 2);
180
+ g.fillRect(-24, 198, 48, 6); g.strokeRect(-24, 198, 48, 6);
181
+ // 罐身:圆肩、尖底
182
+ g.beginPath(); g.moveTo(-10, 30); g.bezierCurveTo(-40, 50, -38, 120, -20, 180); g.quadraticCurveTo(-6, 222, 0, 228); g.quadraticCurveTo(6, 222, 20, 180); g.bezierCurveTo(38, 120, 40, 50, 10, 30); g.closePath(); fs(g, C.terra, C.line, 2.5);
183
+ g.save(); g.clip();
184
+ g.fillStyle = C.green; g.fillRect(-40, 52, 80, 6); g.fillStyle = C.blue; g.fillRect(-40, 58, 80, 4);
185
+ g.fillStyle = '#2f9a8a'; for (let x = -36; x < 36; x += 9) { poly(g, [[x, 62], [x + 9, 62], [x + 4.5, 82]]); g.fill(); }
186
+ g.strokeStyle = 'rgba(80,40,20,.6)'; g.lineWidth = 1.6; [[150], [158]].forEach(([y]) => { g.beginPath(); g.moveTo(-40, y); g.lineTo(40, y); g.stroke(); });
187
+ g.fillStyle = 'rgba(255,230,200,.25)'; g.beginPath(); g.ellipse(-14, 110, 6, 40, 0, 0, 7); g.fill();
188
+ g.restore();
189
+ // 颈与盖
190
+ g.fillStyle = C.terra; g.fillRect(-9, 18, 18, 14); g.strokeStyle = C.line; g.lineWidth = 2; g.strokeRect(-9, 18, 18, 14);
191
+ g.beginPath(); g.moveTo(-14, 20); g.quadraticCurveTo(-14, -6, 0, -6); g.quadraticCurveTo(14, -6, 14, 20); g.closePath(); fs(g, '#3a302a', C.line, 2);
192
+ g.restore();
193
+ }
194
+ function chair(g) {
195
+ g.lineJoin = 'round';
196
+ // 后腿、前腿(收腰的金腿+红白条纹狮爪座)
197
+ const leg = (x) => { g.beginPath(); g.moveTo(x - 9, 748); g.bezierCurveTo(x - 16, 790, x - 4, 830, x - 12, 872); g.lineTo(x + 12, 872); g.bezierCurveTo(x + 4, 830, x + 16, 790, x + 9, 748); g.closePath(); fs(g, C.gold, C.line, 2.4);
198
+ g.fillStyle = C.white; g.fillRect(x - 16, 872, 32, 26); g.strokeStyle = C.line; g.lineWidth = 2; g.strokeRect(x - 16, 872, 32, 26); g.fillStyle = C.red; g.fillRect(x - 16, 878, 32, 5); g.fillRect(x - 16, 888, 32, 5); };
199
+ leg(1458); leg(1312);
200
+ // 座下竖栏+横档
201
+ g.fillStyle = C.gold; for (let x = 1330; x < 1446; x += 20) { g.fillRect(x, 750, 7, 54); g.strokeStyle = C.line; g.lineWidth = 1.4; g.strokeRect(x, 750, 7, 54); }
202
+ g.fillRect(1300, 802, 170, 8); g.strokeRect(1300, 802, 170, 8);
203
+ // 椅背:竖柱(红蓝块)
204
+ g.beginPath(); g.moveTo(1476, 742); g.lineTo(1478, 478); g.quadraticCurveTo(1480, 458, 1492, 458); g.quadraticCurveTo(1504, 460, 1500, 478); g.lineTo(1498, 742); g.closePath(); fs(g, C.gold, C.line, 2.4);
205
+ for (let k = 0; k < 9; k++) { g.fillStyle = k % 2 ? C.blue : C.red; g.fillRect(1483, 494 + k * 28, 10, 13); }
206
+ // 座面
207
+ g.fillStyle = C.gold; g.fillRect(1292, 734, 210, 16); g.strokeStyle = C.line; g.lineWidth = 2.4; g.strokeRect(1292, 734, 210, 16);
208
+ g.fillStyle = C.red; for (let x = 1300; x < 1496; x += 24) g.fillRect(x, 739, 10, 6);
209
+ }
210
+
211
+ // ---------------- 壁画表面:斑驳(multiply)+裂纹+颗粒 ----------------
212
+ const surface = () => P.cached('eg_surface', W, H, (g) => {
213
+ const img = g.createImageData(W, H), d = img.data, r = U.rng(99);
214
+ for (let y = 0; y < H; y += 2) for (let x = 0; x < W; x += 2) {
215
+ const n = P.fbm(x * 0.006 + 40, y * 0.006, 4), m = P.fbm(x * 0.03, y * 0.03 + 9, 2);
216
+ const v = 243 + n * 30 + m * 12 + (r() - 0.5) * 18;
217
+ for (const [dx, dy] of [[0, 0], [1, 0], [0, 1], [1, 1]]) { const i = ((y + dy) * W + x + dx) * 4; d[i] = v; d[i + 1] = v - 6; d[i + 2] = v - 16; d[i + 3] = 255; }
218
+ }
219
+ g.putImageData(img, 0, 0);
220
+ // 发丝裂纹(墙面老化,跨过人物)
221
+ const rr = U.rng(5);
222
+ [[0, 610, 0.1], [700, 0, 1.3], [1210, 0, 1.1], [1500, 980, -0.9], [380, 1080, -1.2], [1920, 420, 2.9]].forEach(([x, y, a0]) => {
223
+ g.strokeStyle = 'rgba(90,70,50,.55)'; g.lineWidth = 1.1; let a = a0;
224
+ g.beginPath(); g.moveTo(x, y); for (let s = 0; s < 34; s++) { a += (rr() - 0.5) * 0.7; x += Math.cos(a) * 14; y += Math.sin(a) * 14; g.lineTo(x, y); if (rr() < 0.08) { g.moveTo(x, y); } } g.stroke();
225
+ });
226
+ });
227
+ const specks = () => P.grain('eg_specks', 0.018, [70, 50, 30], 0.4);
228
+
229
+ // ---------------- 阿顿光线:末端小手一张一合 ----------------
230
+ const HANDS = [[444, 322], [436, 358], [428, 402], [440, 462], [446, 508], [492, 512], [538, 498], [612, 470], [680, 452], [712, 424], [732, 390], [746, 356], [748, 326]];
231
+ function handAt(g, x, y, ang, open, col = '#b4452c') {
232
+ g.save(); g.translate(x, y); g.rotate(ang);
233
+ g.fillStyle = col; g.strokeStyle = '#5a1c10'; g.lineWidth = 1.3;
234
+ g.scale(1.35, 1.35);
235
+ g.lineCap = 'round'; g.lineWidth = 4.2;
236
+ g.scale(0.9 + 0.3 * open, 0.9 + 0.3 * open);
237
+ for (let k = 0; k < 4; k++) { const a = (k - 1.5) * (0.03 + open * 0.42); g.beginPath(); g.moveTo(Math.cos(a) * 5, Math.sin(a) * 5); g.lineTo(Math.cos(a) * (12.5 + open * 1.5), Math.sin(a) * (12.5 + open * 1.5)); g.strokeStyle = '#5a1c10'; g.lineWidth = 5.6; g.stroke(); g.strokeStyle = col; g.lineWidth = 3.4; g.stroke(); }
238
+ g.beginPath(); g.moveTo(1, -5); g.lineTo(5, -10 - open * 3); g.strokeStyle = '#5a1c10'; g.lineWidth = 5.4; g.stroke(); g.strokeStyle = col; g.lineWidth = 3.2; g.stroke();
239
+ g.beginPath(); g.ellipse(0, 0, 8, 6.6, 0, 0, 7); g.fillStyle = col; g.fill(); g.strokeStyle = '#5a1c10'; g.lineWidth = 1.2; g.stroke();
240
+ g.beginPath(); g.moveTo(-9, 0); g.lineTo(-14, 0); g.strokeStyle = col; g.lineWidth = 4; g.stroke(); // 手腕
241
+ g.restore();
242
+ }
243
+ function rays(c, lt, t) {
244
+ c.save(); c.lineCap = 'round';
245
+ // 日盘脉动光晕(周期 0.45s)
246
+ const pu = 0.5 + 0.5 * Math.sin(lt * Math.PI * 2 / 0.45);
247
+ c.save(); c.beginPath(); c.rect(408, 200, 364, 330); c.clip();
248
+ const hg = c.createRadialGradient(DISK[0], DISK[1], 46, DISK[0], DISK[1], 70 + 30 * pu); hg.addColorStop(0, `rgba(255,180,80,${0.5 * pu + 0.12})`); hg.addColorStop(1, 'rgba(255,170,70,0)');
249
+ c.fillStyle = hg; c.beginPath(); c.arc(DISK[0], DISK[1], 100 + 30 * pu, 0, 7); c.fill();
250
+ c.beginPath(); c.arc(DISK[0], DISK[1], 52 + 6 * pu, 0, 7); c.strokeStyle = `rgba(255,214,110,${0.75 * pu})`; c.lineWidth = 5; c.stroke();
251
+ c.beginPath(); c.arc(DISK[0], DISK[1], 46, 0, 7); c.fillStyle = '#c8372b'; c.fill(); c.lineWidth = 3; c.strokeStyle = '#7a1c14'; c.stroke();
252
+ c.fillStyle = C.gold; c.beginPath(); c.ellipse(580, 300, 7, 10, 0, 0, 7); c.fill(); c.strokeStyle = C.black; c.lineWidth = 1.6; c.stroke();
253
+ c.restore();
254
+ const ray = (ex0, ey0, k, long) => {
255
+ // 光线沿扇形依次伸缩(波沿 k 传播),末端 ±9%
256
+ const ext = 1 + 0.09 * Math.sin(lt * Math.PI * 2 / 0.45 - k * 0.55);
257
+ let ex = DISK[0] + (ex0 - DISK[0]) * ext, ey = DISK[1] + (ey0 - DISK[1]) * ext; if (!long) { ex = clamp(ex, 428, 752); ey = Math.min(ey, 512); }
258
+ const wob = Math.sin(t * 7 + k * 1.3) * 3, wx = ex + wob, wy = ey + Math.cos(t * 6 + k) * 2;
259
+ const ang = Math.atan2(wy - DISK[1], wx - DISK[0]);
260
+ c.strokeStyle = '#c23a2a'; c.lineWidth = (long ? 2.6 : 2.2) + 1.6 * (ext - 0.91) / 0.18;
261
+ c.beginPath(); c.moveTo(DISK[0] + Math.cos(ang) * 44, DISK[1] + Math.sin(ang) * 44); c.lineTo(wx - Math.cos(ang) * 6, wy - Math.sin(ang) * 6); c.stroke();
262
+ const open = 0.5 + 0.5 * Math.sin(lt * Math.PI * 2 / 0.4 + k * 0.9);
263
+ handAt(c, wx, wy, ang, open);
264
+ return [wx, wy, ang];
265
+ };
266
+ c.save(); c.beginPath(); c.rect(408, 200, 364, 330); c.clip();
267
+ HANDS.forEach(([x, y], k) => ray(x, y, k, false));
268
+ c.restore();
269
+ // 两条长光线的窗内段(窗外段与小手在画猫之后补,压在猫上)
270
+ LONG.forEach(([x, y, k]) => { const wx = x + Math.sin(t * 7 + k * 1.3) * 3, wy = y + Math.cos(t * 6 + k) * 2, ang = Math.atan2(wy - DISK[1], wx - DISK[0]);
271
+ c.strokeStyle = '#c23a2a'; c.lineWidth = 2.6; c.beginPath(); c.moveTo(DISK[0] + Math.cos(ang) * 44, DISK[1] + Math.sin(ang) * 44); c.lineTo(lerp(DISK[0], wx, 0.64), lerp(DISK[1], wy, 0.64)); c.stroke(); });
272
+ c.restore();
273
+ }
274
+
275
+ // ---------------- 圣甲虫推日球 ----------------
276
+ function scarab(c, lt) {
277
+ let x = 800 + 642 * (lt - 0.394); x = ((x + 120) % 2160 + 2160) % 2160 - 120;
278
+ const y = 1044, bx = x + 54, br = 23, rot = bx / br, pose = Math.floor(lt * 15) % 2;
279
+ c.save(); c.lineCap = 'round'; c.lineJoin = 'round';
280
+ c.translate(x + 27, y); c.scale(1.22, 1.22); c.translate(-x - 27, -y);
281
+ // 日球:红底黄点,随前进滚动
282
+ c.beginPath(); c.arc(bx, y - 2, br, 0, 7); c.fillStyle = '#c8302a'; c.fill(); c.strokeStyle = '#3a0c08'; c.lineWidth = 2.2; c.stroke();
283
+ c.save(); c.clip(); c.fillStyle = '#f2c23a';
284
+ for (let k = 0; k < 9; k++) { const a = rot + k * 0.698, rr = k % 3 === 0 ? 0 : 14; const px = bx + Math.cos(a) * rr, py = y - 2 + Math.sin(a) * rr * 0.9; if (k % 3 === 0 && k) continue; c.beginPath(); c.arc(px, py, 3.6, 0, 7); c.fill(); }
285
+ c.restore();
286
+ // 腿:两套姿态,每 4 帧(60fps)换一次
287
+ c.strokeStyle = '#141a30'; c.lineWidth = 3;
288
+ const legs = pose ? [[-14, -16, -26], [0, -18, -6], [14, -14, 18]] : [[-14, -14, -32], [0, -19, 2], [14, -18, 24]];
289
+ legs.forEach(([lx, ly, ex], i) => { for (const s of [-1, 1]) { const q = s * (i === 1 ? 1 : (pose ? 1 : -1)); c.beginPath(); c.moveTo(x + lx, y); c.lineTo(x + lx + (ex - lx) * 0.5, y + s * (ly - 4)); c.lineTo(x + ex + q * 2, y + s * (ly + 4)); c.stroke(); } });
290
+ // 前足顶着球
291
+ c.beginPath(); c.moveTo(x + 26, y - 4); c.lineTo(bx - br + 2, y - 12 + pose * 3); c.moveTo(x + 26, y + 4); c.lineTo(bx - br + 1, y + 8 - pose * 3); c.stroke();
292
+ // 身体:青金石蓝+绿松石鞘翅高光
293
+ c.beginPath(); c.ellipse(x - 4, y, 24, 17, 0, 0, 7); c.fillStyle = C.lapis; c.fill(); c.strokeStyle = '#0d1430'; c.lineWidth = 2.2; c.stroke();
294
+ c.beginPath(); c.moveTo(x - 28, y); c.lineTo(x + 18, y); c.stroke();
295
+ c.fillStyle = C.turq; c.beginPath(); c.ellipse(x - 8, y - 8, 12, 4, -0.1, 0, 7); c.fill(); c.beginPath(); c.ellipse(x - 8, y + 8, 12, 4, 0.1, 0, 7); c.fill();
296
+ c.beginPath(); c.ellipse(x + 22, y, 8, 11, 0, 0, 7); c.fillStyle = '#3a68c4'; c.fill(); c.strokeStyle = '#0d1430'; c.stroke();
297
+ c.fillStyle = C.gold; c.beginPath(); c.arc(x + 25, y, 3, 0, 7); c.fill();
298
+ c.restore();
299
+ }
300
+
301
+ // ---------------- 巴斯特坐姿的橘白猫 ----------------
302
+ const CAT_S = 0.85, CAT_ANCHOR = [540, 898];
303
+ const catPt = (x, y) => [CAT_ANCHOR[0] + (x - CAT_ANCHOR[0]) * CAT_S, CAT_ANCHOR[1] + (y - CAT_ANCHOR[1]) * CAT_S];
304
+ const LONG = [[...catPt(540, 676), 20], [...catPt(668, 626), 21]]; // 两条长光线的末端:跟着猫头缩放
305
+ function cat(c, K, ch) {
306
+ const L = C.line, hc = K.headC, br = ch.breathe * 260;
307
+ const B = (x, y) => [x, y - (y < 800 ? br * (800 - y) / 130 : 0)];
308
+ c.save(); c.lineJoin = 'round'; c.lineCap = 'round';
309
+ // 以脚底 (540,898) 为锚整体缩到 0.85:耳尖从 y≈530 落到 ≈585,和窗框底边(y≈560)留出空隙
310
+ c.translate(CAT_ANCHOR[0], CAT_ANCHOR[1]); c.scale(CAT_S, CAT_S); c.translate(-CAT_ANCHOR[0], -CAT_ANCHOR[1]);
311
+ // 尾巴:从臀后绕向身前
312
+ const tp = K.tailPts;
313
+ c.strokeStyle = L; c.lineWidth = 24; c.beginPath(); tp.forEach((p, i) => i ? c.lineTo(p[0], p[1] - 4) : c.moveTo(p[0], p[1] - 4)); c.stroke();
314
+ c.strokeStyle = C.catO; c.lineWidth = 19; c.stroke();
315
+ c.strokeStyle = C.catS; c.lineWidth = 4;
316
+ for (let i = 3; i < tp.length - 3; i += 3) { const a = tp[i], b = tp[i + 1], ang = Math.atan2(b[1] - a[1], b[0] - a[0]) + Math.PI / 2; c.beginPath(); c.moveTo(a[0] + Math.cos(ang) * 8, a[1] - 4 + Math.sin(ang) * 8); c.lineTo(a[0] - Math.cos(ang) * 8, a[1] - 4 - Math.sin(ang) * 8); c.stroke(); }
317
+ const tip = tp[tp.length - 1]; c.fillStyle = C.catW; c.beginPath(); c.arc(tip[0], tip[1] - 4, 9, 0, 7); c.fill(); c.strokeStyle = L; c.lineWidth = 2; c.beginPath(); c.arc(tip[0], tip[1] - 4, 10.5, -1.2, 1.9); c.stroke();
318
+ // 身体:挺直上身、圆臀;前腿并在剪影里(胸口到前爪一整片白)
319
+ const body = sm([[568, 666], [538, 690], [506, 726], [482, 772], [468, 818], [464, 860], [474, 897], [560, 899], [666, 899], [663, 862], [659, 800], [654, 744], [646, 706], [640, 680]].map(p => B(...p)));
320
+ const cw = sm([B(616, 688), B(640, 678), B(650, 716), B(656, 790), [661, 860], [666, 899], [598, 899], [600, 850], [604, 790], B(610, 736)]);
321
+ c.fillStyle = C.catO; c.fill(body);
322
+ c.save(); c.clip(body);
323
+ c.fillStyle = C.catW; c.fill(cw);
324
+ c.strokeStyle = C.catS; c.lineWidth = 6;
325
+ [[[548, 690], [532, 716]], [[524, 704], [508, 736]], [[500, 730], [484, 766]], [[480, 764], [466, 800]], [[464, 806], [456, 840]], [[558, 716], [542, 748]], [[536, 744], [520, 780]], [[512, 776], [498, 812]], [[578, 742], [566, 770]]].forEach(([a, b]) => { c.beginPath(); c.moveTo(...B(...a)); c.quadraticCurveTo(a[0] - 14, (a[1] + b[1]) / 2, ...B(...b)); c.stroke(); });
326
+ // 后腿(大腿弧)+腿上条纹
327
+ c.strokeStyle = C.catS; c.lineWidth = 5; [[496, 850, 512, 826], [520, 836, 536, 812], [546, 830, 560, 810], [568, 836, 578, 818]].forEach(([a, b, x2, y2]) => { c.beginPath(); c.moveTo(a, b); c.lineTo(x2, y2); c.stroke(); });
328
+ c.strokeStyle = L; c.lineWidth = 2.6; c.beginPath(); c.moveTo(492, 897); c.bezierCurveTo(484, 830, 552, 796, 598, 852); c.stroke();
329
+ // 两条前腿的分界、前爪趾线
330
+ c.lineWidth = 2.2; c.beginPath(); c.moveTo(631, 792); c.quadraticCurveTo(634, 850, 632, 899); c.stroke();
331
+ c.beginPath(); c.moveTo(604, 800); c.quadraticCurveTo(600, 850, 601, 899); c.stroke();
332
+ c.lineWidth = 1.4; for (const x of [612, 621, 642, 652]) { c.beginPath(); c.moveTo(x, 890); c.lineTo(x, 898); c.stroke(); }
333
+ c.restore();
334
+ c.strokeStyle = L; c.lineWidth = 2.8; c.stroke(body);
335
+ // 后爪(白)
336
+ c.beginPath(); c.ellipse(574, 893, 22, 6.5, 0, 0, 7); c.fillStyle = C.catW; c.fill(); c.lineWidth = 2.2; c.stroke();
337
+ // 头:侧脸、高耸尖耳
338
+ const Hd = (x, y) => [hc[0] + x, hc[1] + y];
339
+ // 耳朵抖:每 ~0.38s 一次 0.1s 的快速一抖(两耳错相),绕耳根中点转
340
+ const twitch = (ph) => { const f = (ch.lt * 2.6 + ph) % 1; return f < 0.26 ? Math.sin(f / 0.26 * Math.PI) : 0; };
341
+ const earRot = (e, a) => { const bx = (e[0][0] + e[2][0]) / 2, by = (e[0][1] + e[2][1]) / 2, cs = Math.cos(a), sn = Math.sin(a); return e.map(([x, y]) => [bx + (x - bx) * cs - (y - by) * sn, by + (x - bx) * sn + (y - by) * cs]); };
342
+ const ear1 = earRot([Hd(-40, -26), Hd(-30, -84), Hd(-8, -36)], -0.38 * twitch(0.1)), ear2 = earRot([Hd(4, -38), Hd(22, -90), Hd(36, -28)], 0.32 * twitch(0.6));
343
+ [ear1, ear2].forEach((e, k) => { poly(c, e); fs(c, C.catO, L, 2.6); poly(c, [[lerp(e[0][0], e[1][0], 0.2) + 4, lerp(e[0][1], e[1][1], 0.2)], [lerp(e[0][0], e[1][0], 0.85) + (k ? 2 : 3), lerp(e[0][1], e[1][1], 0.85) + 4], [lerp(e[2][0], e[1][0], 0.2) - 3, lerp(e[2][1], e[1][1], 0.2)]]); c.fillStyle = '#e99a86'; c.fill(); });
344
+ const head = sm([Hd(-44, 40), Hd(-50, 4), Hd(-38, -30), Hd(-6, -40), Hd(30, -32), Hd(52, -16), Hd(64, 2), Hd(72, 14), Hd(68, 30), Hd(52, 44), Hd(22, 52), Hd(-10, 52)]);
345
+ c.fillStyle = C.catO; c.fill(head);
346
+ c.save(); c.clip(head);
347
+ c.fillStyle = C.catW; c.beginPath(); c.ellipse(...Hd(54, 34), 30, 22, -0.2, 0, 7); c.fill(); c.beginPath(); c.ellipse(...Hd(30, 52), 30, 12, 0, 0, 7); c.fill();
348
+ c.strokeStyle = C.catS; c.lineWidth = 4.5; [[-8, -38, -4, -20], [6, -40, 8, -22], [18, -36, 20, -20], [-34, -6, -18, 0], [-36, 10, -20, 12]].forEach(([a, b, x2, y2]) => { c.beginPath(); c.moveTo(...Hd(a, b)); c.lineTo(...Hd(x2, y2)); c.stroke(); });
349
+ c.restore();
350
+ c.strokeStyle = L; c.lineWidth = 2.8; c.stroke(head);
351
+ // 正面眼(埃及式杏仁大眼+眼线拖尾)
352
+ const e = Hd(32, -8);
353
+ if (K.blink) { c.strokeStyle = L; c.lineWidth = 3; c.beginPath(); c.moveTo(e[0] - 14, e[1]); c.quadraticCurveTo(e[0], e[1] + 5, e[0] + 14, e[1]); c.stroke(); }
354
+ else {
355
+ c.beginPath(); c.moveTo(e[0] - 15, e[1]); c.quadraticCurveTo(e[0], e[1] - 13, e[0] + 15, e[1] - 1); c.quadraticCurveTo(e[0], e[1] + 10, e[0] - 15, e[1]); c.closePath(); fs(c, '#e8cf4a', L, 2.6);
356
+ c.fillStyle = L; c.beginPath(); c.ellipse(e[0] + 2, e[1] - 1, 3, 7, 0, 0, 7); c.fill();
357
+ }
358
+ c.strokeStyle = L; c.lineWidth = 3.4; c.beginPath(); c.moveTo(e[0] - 15, e[1]); c.lineTo(e[0] - 30, e[1] - 3); c.stroke();
359
+ // 鼻、嘴、胡须
360
+ poly(c, [Hd(66, 8), Hd(73, 9), Hd(70, 15)]); c.fillStyle = '#d0706a'; c.fill();
361
+ c.strokeStyle = L; c.lineWidth = 2; c.beginPath(); c.moveTo(...Hd(70, 15)); c.quadraticCurveTo(...Hd(66, 26), ...Hd(58, 28)); c.stroke();
362
+ c.lineWidth = 1.2; [[60, 22, 104, 12], [60, 26, 106, 28], [58, 30, 100, 40]].forEach(([a, b, x2, y2]) => { c.beginPath(); c.moveTo(...Hd(a, b)); c.lineTo(...Hd(x2, y2)); c.stroke(); });
363
+ // 金耳环
364
+ c.strokeStyle = C.gold; c.lineWidth = 3.4; c.beginPath(); c.arc(...Hd(-28, -18), 7, 0.3, 5.9); c.stroke();
365
+ // 彩色项圈(红蓝金)+荷鲁斯之眼坠子
366
+ const clipU = new Path2D(); clipU.addPath(body); clipU.addPath(head);
367
+ c.save(); c.clip(clipU);
368
+ const col = [B(556, 664), B(600, 690), B(652, 694)];
369
+ [[C.gold, 22], [C.red, 16], [C.blue, 9], [C.gold, 3]].forEach(([cc, w]) => { c.strokeStyle = cc; c.lineWidth = w; c.beginPath(); c.moveTo(...col[0]); c.quadraticCurveTo(...col[1], ...col[2]); c.stroke(); });
370
+ c.strokeStyle = L; c.lineWidth = 1.6; for (const s of [-1, 1]) { c.beginPath(); c.moveTo(col[0][0], col[0][1] + s * 11); c.quadraticCurveTo(col[1][0], col[1][1] + s * 11, col[2][0], col[2][1] + s * 11); c.stroke(); }
371
+ c.fillStyle = C.gold; for (let q = 0.1; q < 1; q += 0.12) { const x = (1 - q) * (1 - q) * col[0][0] + 2 * q * (1 - q) * col[1][0] + q * q * col[2][0], y = (1 - q) * (1 - q) * col[0][1] + 2 * q * (1 - q) * col[1][1] + q * q * col[2][1]; c.beginPath(); c.arc(x, y + 15, 3, 0, 7); c.fill(); }
372
+ c.restore();
373
+ const pd = B(640, 742);
374
+ c.strokeStyle = C.goldD; c.lineWidth = 2; c.beginPath(); c.moveTo(col[2][0] - 14, col[2][1] + 8); c.lineTo(pd[0], pd[1] - 14); c.stroke();
375
+ c.beginPath(); c.arc(pd[0], pd[1], 13, 0, 7); fs(c, C.gold, L, 2);
376
+ c.beginPath(); c.moveTo(pd[0] - 8, pd[1]); c.quadraticCurveTo(pd[0], pd[1] - 6, pd[0] + 8, pd[1]); c.quadraticCurveTo(pd[0], pd[1] + 5, pd[0] - 8, pd[1]); fs(c, '#f4f0e4', '#14204a', 1.5);
377
+ c.fillStyle = '#14204a'; c.beginPath(); c.arc(pd[0], pd[1], 2.4, 0, 7); c.fill();
378
+ c.strokeStyle = '#14204a'; c.lineWidth = 1.6; c.beginPath(); c.moveTo(pd[0] - 1, pd[1] + 3); c.lineTo(pd[0] - 3, pd[1] + 9); c.stroke();
379
+ c.restore();
380
+ }
381
+
382
+ // ---------------- 少女:埃及程式(侧脸正面眼、正面双肩、白亚麻长裙、宽领项圈、金发辫) ----------------
383
+ const DY = -25;
384
+ function girl(c, G, ch) {
385
+ const L = C.line, tilt = G.tilt, nk = [1300, 425 + DY], cs = Math.cos(tilt), sn = Math.sin(tilt);
386
+ const HP = (x, y) => { const X = x - nk[0], Y = y + DY - nk[1]; return [nk[0] + X * cs - Y * sn, nk[1] + X * sn + Y * cs]; };
387
+ const TB = (x, y) => [x, y + DY + (y < 600 ? ch.breathe * 150 * (600 - y) / 170 : 0)];
388
+ const T = (x, y) => [x, y + DY];
389
+ c.save(); c.lineJoin = 'round'; c.lineCap = 'round';
390
+ // 躯干(正面化双肩;亚麻半透明:先肤色再罩白)
391
+ const torso = sm([[1270, 466], [1298, 450], [1340, 447], [1420, 454], [1452, 474], [1452, 530], [1440, 590], [1432, 616], [1300, 616], [1288, 560], [1280, 504]].map(p => TB(...p)));
392
+ // 长裙下摆:膝前垂直落到脚踝
393
+ const skirt = sm([[1300, 600], [1238, 620], [1186, 642], [1156, 670], [1145, 724], [1140, 800], [1134, 905], [1262, 905], [1270, 820], [1282, 762], [1330, 754], [1462, 758], [1470, 700], [1452, 640], [1432, 602]].map(p => T(...p)));
394
+ // 脚(赤足,侧面)
395
+ [[1198, 1118], [1170, 1092]].forEach(([hx, tx], k) => { const y0 = 900 - 0; const f = sm([[hx + 10, y0 - 22], [hx + 14, y0 - 6], [hx + 6, y0], [tx + 4, y0], [tx - 2, y0 - 6], [tx + 10, y0 - 12], [hx - 30, y0 - 20]]); c.fillStyle = k ? C.skin : C.skinD; c.fill(f); c.strokeStyle = L; c.lineWidth = 2.2; c.stroke(f); });
396
+ c.fillStyle = C.skin; c.fill(torso);
397
+ c.globalAlpha = 0.9; c.fillStyle = C.white; c.fill(torso); c.fill(skirt); c.globalAlpha = 1;
398
+ // 褶:裙身扇形细褶(从腰往膝、再垂到下摆)
399
+ c.save(); c.clip(skirt); c.strokeStyle = 'rgba(120,96,70,.5)'; c.lineWidth = 1.3;
400
+ for (let i = 0; i <= 26; i++) { const a = i / 26; c.beginPath(); c.moveTo(lerp(1290, 1460, a), 600 + DY); c.bezierCurveTo(lerp(1200, 1460, a), lerp(650, 752, a) + DY, lerp(1150, 1272, a), lerp(700, 790, a) + DY, lerp(1134, 1262, a), 905 + DY); c.stroke(); }
401
+ c.strokeStyle = 'rgba(90,60,40,.55)'; c.lineWidth = 1.8; c.beginPath(); c.moveTo(1170, 686 + DY); c.quadraticCurveTo(1320, 700 + DY, 1462, 742 + DY); c.stroke();
402
+ c.restore();
403
+ c.save(); c.clip(torso); c.strokeStyle = 'rgba(120,96,70,.45)'; c.lineWidth = 1.2;
404
+ for (let x = 1290; x < 1455; x += 9) { c.beginPath(); c.moveTo(x, 470 + DY); c.quadraticCurveTo(x + 6, 540 + DY, x + 2, 620 + DY); c.stroke(); }
405
+ c.fillStyle = 'rgba(255,255,255,.0)'; c.restore();
406
+ c.strokeStyle = L; c.lineWidth = 2.4; c.stroke(skirt); c.stroke(torso);
407
+ // 腰带(红色细带,垂两条带尾)
408
+ c.strokeStyle = C.red; c.lineWidth = 6; c.beginPath(); c.moveTo(...T(1298, 604)); c.quadraticCurveTo(...T(1370, 616), ...T(1440, 606)); c.stroke();
409
+ c.lineWidth = 4; c.beginPath(); c.moveTo(...T(1306, 606)); c.lineTo(...T(1292, 660)); c.moveTo(...T(1316, 607)); c.lineTo(...T(1310, 664)); c.stroke();
410
+ // 宽领项圈(同心色带+水滴坠)
411
+ const cc = TB(1358, 440);
412
+ const bands = [[C.blue, 50, 24], [C.red, 60, 32], [C.green, 70, 40], [C.gold, 80, 48], [C.blue, 88, 55]];
413
+ for (let k = bands.length - 1; k >= 0; k--) { const [col, rx, ry] = bands[k]; c.beginPath(); c.ellipse(cc[0], cc[1], rx + 4, ry + 4, 0.05, 0, Math.PI); c.lineTo(cc[0] - rx - 4, cc[1]); c.fillStyle = col; c.fill(); }
414
+ c.strokeStyle = L; c.lineWidth = 1.2; bands.forEach(([, rx, ry]) => { c.beginPath(); c.ellipse(cc[0], cc[1], rx + 4, ry + 4, 0.05, 0.05, Math.PI - 0.05); c.stroke(); });
415
+ for (let a = 0.12; a < Math.PI - 0.1; a += 0.17) { const x = cc[0] + Math.cos(a) * 99, y = cc[1] + Math.sin(a) * 66; c.save(); c.translate(x, y); c.rotate(a - Math.PI / 2); c.beginPath(); c.moveTo(-4, -6); c.quadraticCurveTo(-5, 4, 0, 7); c.quadraticCurveTo(5, 4, 4, -6); c.closePath(); c.fillStyle = (Math.round(a / 0.17) % 2) ? C.red : C.blue; c.fill(); c.lineWidth = 1; c.stroke(); c.restore(); }
416
+ // 后侧手臂:沿身侧下垂,前臂搭在腿上(金臂环、手镯)
417
+ const sh2 = TB(1446, 478), el2 = T(1452, 612), hd2 = T(1300, 662);
418
+ c.fillStyle = C.skin; c.strokeStyle = L; c.lineWidth = 2.4;
419
+ const ua = RIG.taper(sh2, el2, 36, 28, 4), fa = RIG.taper(el2, hd2, 28, 20, 2);
420
+ c.fill(ua); c.stroke(ua); c.fill(fa); c.stroke(fa);
421
+ const hand2 = sm([[hd2[0] + 14, hd2[1] - 10], [hd2[0] - 14, hd2[1] - 8], [hd2[0] - 36, hd2[1] - 2], [hd2[0] - 38, hd2[1] + 5], [hd2[0] - 10, hd2[1] + 9], [hd2[0] + 14, hd2[1] + 9]]); c.fill(hand2); c.stroke(hand2);
422
+ c.lineWidth = 1.2; for (const s of [-3, 2]) { c.beginPath(); c.moveTo(hd2[0] - 12, hd2[1] + s); c.lineTo(hd2[0] - 34, hd2[1] + s + 2); c.stroke(); }
423
+ const band = (A, B, q, w) => { const x = lerp(A[0], B[0], q), y = lerp(A[1], B[1], q), a = Math.atan2(B[1] - A[1], B[0] - A[0]) + Math.PI / 2; c.strokeStyle = C.gold; c.lineWidth = 8; c.beginPath(); c.moveTo(x + Math.cos(a) * w, y + Math.sin(a) * w); c.lineTo(x - Math.cos(a) * w, y - Math.sin(a) * w); c.stroke(); c.strokeStyle = C.blue; c.lineWidth = 2.5; c.stroke(); };
424
+ band(sh2, el2, 0.32, 19); band(el2, hd2, 0.84, 12);
425
+ // 颈、脸
426
+ const neck = sm([[1286, 400], [1316, 400], [1326, 450], [1288, 452]].map(p => T(...p)));
427
+ c.fillStyle = C.skin; c.fill(neck); c.strokeStyle = L; c.lineWidth = 2.2; c.stroke(neck);
428
+ // 假发:头顶到肩后的金色编发(一束束竖向、分节)
429
+ const wig = sm([[1258, 322], [1266, 298], [1300, 283], [1348, 285], [1388, 304], [1410, 342], [1416, 400], [1422, 470], [1418, 548], [1396, 556], [1372, 548], [1360, 470], [1344, 432], [1328, 404], [1316, 372], [1312, 336], [1290, 324]].map(p => HP(...p)));
430
+ c.fillStyle = '#9a6a18'; c.fill(wig);
431
+ c.save(); c.clip(wig);
432
+ // 一束束竖向编发:每束 = 金色圆头长条+斜向分节(人字纹),末端长短错落
433
+ for (let i = 0; i < 15; i++) {
434
+ const x0 = 1260 + i * 11, yEnd = (i < 5 ? 420 : 560) - (i * 37 % 23);
435
+ const pts = []; for (let y = 284; y <= yEnd; y += 8) pts.push(HP(x0 + (y > 420 ? (y - 420) * 0.06 : 0), y));
436
+ c.strokeStyle = '#7a4e10'; c.lineWidth = 11.5; c.beginPath(); pts.forEach((p, k) => k ? c.lineTo(...p) : c.moveTo(...p)); c.stroke();
437
+ c.strokeStyle = (i % 3 === 1) ? C.goldL : C.gold; c.lineWidth = 8.5; c.stroke();
438
+ c.strokeStyle = '#8a5a12'; c.lineWidth = 1.4;
439
+ for (let k = 1; k < pts.length; k++) { const p = pts[k], side = k % 2 ? 1 : -1; c.beginPath(); c.moveTo(p[0] - 4.2, p[1] - 3 * side); c.lineTo(p[0] + 4.2, p[1] + 3 * side); c.stroke(); }
440
+ }
441
+ c.restore();
442
+ c.strokeStyle = L; c.lineWidth = 2.4; c.stroke(wig);
443
+ // 脸(rig 侧脸轮廓)
444
+ c.fillStyle = C.skin; c.fill(G.face); c.strokeStyle = L; c.lineWidth = 2.4; c.stroke(G.face);
445
+ // 耳+大金耳环
446
+ c.beginPath(); c.ellipse(G.ear[0] - 2, G.ear[1], 7, 11, 0.2, 0, 7); fs(c, C.skin, L, 1.8);
447
+ c.beginPath(); c.arc(G.ear[0] - 2, G.ear[1] + 22, 9, 0, 7); fs(c, C.gold, L, 1.8); c.beginPath(); c.arc(G.ear[0] - 2, G.ear[1] + 22, 3.5, 0, 7); c.fillStyle = C.red; c.fill();
448
+ // 金发箍+后垂带
449
+ const hb = [HP(1260, 314), HP(1300, 300), HP(1352, 302), HP(1398, 324)];
450
+ c.strokeStyle = L; c.lineWidth = 15; c.beginPath(); c.moveTo(...hb[0]); c.bezierCurveTo(...hb[1], ...hb[2], ...hb[3]); c.stroke();
451
+ c.strokeStyle = C.gold; c.lineWidth = 11; c.stroke(); c.strokeStyle = C.goldL; c.lineWidth = 3; c.stroke();
452
+ const kn = HP(1402, 326), kt = HP(1440, 392), kt2 = HP(1430, 410);
453
+ poly(c, [kn, kt, kt2, HP(1396, 340)]); fs(c, C.gold, L, 1.8);
454
+ c.fillStyle = C.blue; c.beginPath(); c.arc(...HP(1262, 314), 5, 0, 7); c.fill();
455
+ // 正面眼(杏仁形、黑眼线拖尾、长眉)
456
+ const ex = G.eye.x + 2, ey = G.eye.y;
457
+ if (ch.blink) { c.strokeStyle = L; c.lineWidth = 3; c.beginPath(); c.moveTo(ex - 11, ey); c.quadraticCurveTo(ex, ey + 4, ex + 12, ey); c.stroke(); }
458
+ else {
459
+ c.beginPath(); c.moveTo(ex - 11, ey); c.quadraticCurveTo(ex, ey - 9, ex + 12, ey - 1); c.quadraticCurveTo(ex, ey + 6, ex - 11, ey); c.closePath(); fs(c, '#fbf7ee', null);
460
+ c.fillStyle = '#1a120c'; c.beginPath(); c.arc(ex - 1, ey - 1.5, 4.2, 0, 7); c.fill();
461
+ }
462
+ c.strokeStyle = '#140c08'; c.lineWidth = 3.2; c.beginPath(); c.moveTo(ex - 12, ey); c.quadraticCurveTo(ex, ey - 10, ex + 12, ey - 1); c.lineTo(ex + 30, ey - 3); c.stroke();
463
+ c.lineWidth = 1.8; c.beginPath(); c.moveTo(ex - 10, ey + 1); c.quadraticCurveTo(ex, ey + 6, ex + 14, ey + 1); c.stroke();
464
+ c.lineWidth = 3; c.beginPath(); c.moveTo(ex - 12, ey - 12); c.quadraticCurveTo(ex + 4, ey - 19, ex + 26, ey - 12); c.stroke();
465
+ // 唇
466
+ c.fillStyle = '#b6443a'; c.beginPath(); c.moveTo(...G.lips[0]); c.lineTo(...G.lips[1]); c.lineTo(...G.lips[2]); c.closePath(); c.fill();
467
+ // 近侧手臂:从前肩抬到嘴边端杯(自己的 IK,肘向下垂)
468
+ // 手托在杯身下(握杯脚),而不是 rig 默认的杯侧:肘才能像设定稿那样垂到胸前
469
+ const cpp = G.cup, hd = [cpp.x + 4 - Math.sin(cpp.tilt) * 40, cpp.y + Math.cos(cpp.tilt) * 42];
470
+ const sh = TB(1282, 476);
471
+ const { E } = RIG.ik2(sh, hd, 140, 160, -1);
472
+ const ua1 = RIG.taper(sh, E, 38, 30, 4), fa1 = RIG.taper(E, hd, 30, 22, 2);
473
+ c.fillStyle = C.skin; c.strokeStyle = L; c.lineWidth = 2.4; c.fill(ua1); c.stroke(ua1); c.fill(fa1); c.stroke(fa1);
474
+ band(sh, E, 0.4, 19); band(E, hd, 0.8, 13);
475
+ // 蓝色莲花杯
476
+ const cp = G.cup;
477
+ c.save(); c.translate(cp.x, cp.y); c.rotate(cp.tilt);
478
+ c.beginPath(); c.moveTo(-26, 0); c.quadraticCurveTo(-22, 26, -5, 34); c.lineTo(-4, 44); c.quadraticCurveTo(-14, 46, -14, 50); c.lineTo(14, 50); c.quadraticCurveTo(14, 46, 4, 44); c.lineTo(5, 34); c.quadraticCurveTo(22, 26, 26, 0); c.closePath(); fs(c, '#2f62c4', L, 2.2);
479
+ c.strokeStyle = '#8ab8f0'; c.lineWidth = 1.6; [[-14, 2, -4, 30], [0, 1, 0, 32], [14, 2, 4, 30]].forEach(([a, b, x2, y2]) => { c.beginPath(); c.moveTo(a, b); c.quadraticCurveTo((a + x2) / 2 + (a < 0 ? -4 : a > 0 ? 4 : 0), 18, x2, y2); c.stroke(); });
480
+ c.beginPath(); c.ellipse(0, 0, 26, 4.5, 0, 0, 7); fs(c, '#1c3f8a', L, 1.8);
481
+ c.restore();
482
+ // 手:握杯
483
+ const hf = sm([[hd[0] - 20, hd[1] - 4], [hd[0] - 8, hd[1] - 10], [hd[0] + 10, hd[1] - 8], [hd[0] + 16, hd[1] + 6], [hd[0] + 2, hd[1] + 14], [hd[0] - 18, hd[1] + 6]]);
484
+ c.fillStyle = C.skin; c.fill(hf); c.strokeStyle = L; c.lineWidth = 2.2; c.stroke(hf);
485
+ c.lineWidth = 1.2; for (const s of [-4, 2]) { c.beginPath(); c.moveTo(hd[0] - 18, hd[1] + s); c.lineTo(hd[0] - 4, hd[1] + s + 1); c.stroke(); }
486
+ c.restore();
487
+ }
488
+
489
+ return {
490
+ init() { P.cached('eg_static', W, H, staticLayer); surface(); specks(); },
491
+ draw(c, lt, t) {
492
+ const ch = P.choreo(lt, t);
493
+ c.drawImage(P.cached('eg_static', W, H, staticLayer), 0, 0);
494
+ // 水纹象形字:zigzag 相位流动(12fps 步进,像手绘逐张)
495
+ const wp = Math.floor(lt * 12) * 0.5; // 每 1/12s 走半个齿,4 步一循环(0.33s)
496
+ GLYPHS.forEach(q => { if (GL[q.n].water) drawGlyph(c, q, wp + q.ph); });
497
+ rays(c, lt, t);
498
+ writeGlyphs(c, lt);
499
+ ch.lt = lt;
500
+ const K = RIG.cat({ tail: ch.tail * 2.1 + 0.25 * Math.sin(t * 9.5), blink: ch.blink, breathe: ch.breathe, dy: -5 });
501
+ cat(c, K, ch);
502
+ // 两条长光线要压在猫上
503
+ c.save(); c.lineCap = 'round';
504
+ LONG.forEach(([x, y, k]) => {
505
+ const wx = x + Math.sin(t * 7 + k * 1.3) * 3, wy = y + Math.cos(t * 6 + k) * 2, ang = Math.atan2(wy - DISK[1], wx - DISK[0]);
506
+ c.strokeStyle = '#c23a2a'; c.lineWidth = 2.6; c.beginPath(); c.moveTo(lerp(DISK[0], wx, 0.62), lerp(DISK[1], wy, 0.62)); c.lineTo(wx - Math.cos(ang) * 6, wy - Math.sin(ang) * 6); c.stroke();
507
+ handAt(c, wx, wy, ang, 0.5 + 0.5 * Math.sin(lt * Math.PI * 2 / 0.4 + k * 0.9)); // 开合幅度在 handAt 里已放大
508
+ drawGlyph(c, { n: 'ankh', x: wx + Math.cos(ang) * 16, y: wy + Math.sin(ang) * 16 + 4, s: 0.36, col: C.blue });
509
+ });
510
+ c.restore();
511
+ // 少女:杯基本一直在嘴边(原片如此),choreo 的举杯只走最后一小段+小口啜饮
512
+ // 端杯:先按 choreo 举到嘴边;lt>0.35 起每 0.6s 一个循环:杯离嘴下放(cup 1→0.62)再举回,杯在嘴边时仰头大口喝(倾角到 ~0.2rad)
513
+ let cupV = lerp(0.8, 1, ch.cup), sipV = ch.sip;
514
+ if (lt > 0.35) { const u = (lt - 0.35) / 0.6; cupV = 1 - 0.46 * (1 - Math.cos(u * Math.PI * 2)) / 2; }
515
+ sipV += 0.3 * clamp((cupV - 0.78) / 0.22) * (0.65 + 0.35 * Math.sin(lt * 13));
516
+ const G = RIG.girl({ cup: cupV, sip: sipV, breathe: ch.breathe, dy: DY, hair: 'down' });
517
+ girl(c, G, ch);
518
+ scarab(c, lt);
519
+ // 壁画表面:斑驳 multiply + 颗粒
520
+ c.save(); c.globalCompositeOperation = 'multiply'; c.drawImage(surface(), 0, 0); c.restore();
521
+ c.drawImage(specks(), 0, 0);
522
+ },
523
+ };
524
+ })();