@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,448 @@
1
+ // 公元前530 阿提卡黑绘陶器(Exekias 酒神杯 + 双耳瓶母题)——纯代码。
2
+ // 管线:①缓存陶土底(低频斑驳 + 拉坯水平细纹 + 颗粒 + 裂纹)→ ②缓存静态黑绘(画框、桌、椅、双耳瓶、希腊铭文)
3
+ // → ③滚动纹带(顶回纹左滚、底莲苞链+射线纹右滚)→ ④画框内船与海豚、里拉琴摆、玫瑰花饰转
4
+ // → ⑤橘白猫(橘剪影+黑虎斑+陶土留白)→ ⑥少女(黑釉剪影 + 刻线 + 紫红/白点附加色 + 金色卷发)→ ⑦热气、暗角
5
+ // 黑绘三件套:黑釉剪影(C.black)+刻线(在黑上「刻穿」露出陶土色细线 C.inc)+附加色(紫红 C.red、白点 C.white)。
6
+ SCENES['03_greek'] = (() => {
7
+ const W = 1920, H = 1080, { clamp, lerp, rng } = U, P = PAINT, IO = U.ease.inOut;
8
+ const C = {
9
+ clay: '#dc6a2f', clayHi: '#e8803f', black: '#1b120d', red: '#8a2a37', white: '#f6ead6',
10
+ inc: '#e8904c', gold: '#f2bd48', resv: '#f3cfa4', catO: '#f2913f', pink: '#e7897a',
11
+ };
12
+ const TAU = Math.PI * 2;
13
+ const GS = 0.97, GDY = -14; // 少女缩放/上移:脚落在地线 y≈903
14
+ const Tg = (x, y) => [1300 + (x - 1300) * GS, GDY + 620 + (y - 620) * GS];
15
+ const sm = (pts, closed = true) => RIG.smooth(pts, closed);
16
+ const smT = (pts, closed = false) => RIG.smooth(pts.map(p => Tg(p[0], p[1])), closed);
17
+ const line = (g, pts) => { g.beginPath(); pts.forEach((p, i) => i ? g.lineTo(p[0], p[1]) : g.moveTo(p[0], p[1])); };
18
+
19
+ // ---------------- ① 陶土底 ----------------
20
+ function crack(g, pts, seed) {
21
+ const r = rng(seed); g.save(); g.strokeStyle = 'rgba(52,18,6,.55)'; g.lineWidth = 1.3; g.lineJoin = 'round'; g.beginPath();
22
+ g.moveTo(pts[0][0], pts[0][1]);
23
+ for (let i = 1; i < pts.length; i++) { const a = pts[i - 1], b = pts[i], n = Math.max(2, Math.round(Math.hypot(b[0] - a[0], b[1] - a[1]) / 9));
24
+ for (let k = 1; k <= n; k++) g.lineTo(lerp(a[0], b[0], k / n) + (r() - .5) * 4, lerp(a[1], b[1], k / n) + (r() - .5) * 4); }
25
+ g.stroke();
26
+ g.strokeStyle = 'rgba(255,190,140,.18)'; g.translate(1, 1.2); g.stroke(); g.restore(); // 裂缝下沿的一丝亮边
27
+ }
28
+ const bg = () => P.cached('gr_bg', W, H, (g) => {
29
+ const lw = 480, lh = 270, lo = P.canvas(lw, lh), lg = lo.getContext('2d'), img = lg.createImageData(lw, lh), d = img.data, b = P.hex(C.clay);
30
+ for (let y = 0; y < lh; y++) for (let x = 0; x < lw; x++) {
31
+ const n = P.fbm(x * 0.016 + 3, y * 0.016, 4), s = P.noise(x * 0.006, y * 0.85) * 0.6 + P.noise(x * 0.013 + 7, y * 0.4) * 0.4;
32
+ const k = n * 30 + s * 9, i = (y * lw + x) * 4;
33
+ d[i] = b[0] + k; d[i + 1] = b[1] + k * 0.75; d[i + 2] = b[2] + k * 0.45; d[i + 3] = 255;
34
+ }
35
+ lg.putImageData(img, 0, 0); g.imageSmoothingEnabled = true; g.drawImage(lo, 0, 0, W, H);
36
+ const hl = g.createRadialGradient(980, 520, 60, 980, 520, 900); hl.addColorStop(0, 'rgba(255,170,90,.16)'); hl.addColorStop(1, 'rgba(255,170,90,0)');
37
+ g.fillStyle = hl; g.fillRect(0, 0, W, H);
38
+ const r = rng(31); // 拉坯留下的水平细纹
39
+ for (let i = 0; i < 560; i++) { const y = r() * H, x = r() * W - 300, L = 150 + r() * 900;
40
+ g.strokeStyle = r() < 0.55 ? `rgba(120,38,8,${0.03 + r() * 0.05})` : `rgba(255,196,140,${0.03 + r() * 0.05})`; g.lineWidth = 0.7 + r() * 1.8;
41
+ g.beginPath(); g.moveTo(x, y); g.lineTo(x + L, y + (r() - .5) * 2); g.stroke(); }
42
+ for (let i = 0; i < 420; i++) { g.fillStyle = `rgba(70,24,8,${0.25 + r() * 0.45})`; g.beginPath(); g.arc(r() * W, r() * H, 0.6 + r() * 1.6, 0, TAU); g.fill(); }
43
+ for (let i = 0; i < 120; i++) { g.fillStyle = `rgba(255,205,160,${0.2 + r() * 0.3})`; g.beginPath(); g.arc(r() * W, r() * H, 0.6 + r() * 1.2, 0, TAU); g.fill(); }
44
+ g.drawImage(P.grain('gr_grainA', 0.07, [70, 22, 6], 0.16), 0, 0); g.drawImage(P.grain('gr_grainBB', 0.04, [255, 214, 170], 0.12), 0, 0);
45
+ crack(g, [[1018, 96], [1030, 170], [1062, 240], [1100, 330], [1112, 430], [1104, 560], [1120, 620]], 3);
46
+ crack(g, [[0, 606], [160, 628], [300, 662], [440, 690], [560, 650], [650, 572], [700, 470]], 4);
47
+ crack(g, [[1500, 560], [1570, 590], [1615, 640], [1720, 668], [1920, 704]], 5);
48
+ crack(g, [[1500, 720], [1528, 800], [1522, 896]], 6); crack(g, [[300, 662], [330, 600], [322, 540]], 7);
49
+ });
50
+ const vignette = () => P.cached('gr_vig', W, H, (g) => {
51
+ const v = g.createRadialGradient(960, 540, 380, 960, 540, 1180); v.addColorStop(0, 'rgba(40,10,0,0)'); v.addColorStop(1, 'rgba(30,6,0,.62)');
52
+ g.fillStyle = v; g.fillRect(0, 0, W, H);
53
+ });
54
+
55
+ // ---------------- ② 希腊铭文:笔画表(单位格 0..1,y 向下) ----------------
56
+ const GL = {
57
+ 'Χ': [[[0, 0], [1, 1]], [[1, 0], [0, 1]]], 'Α': [[[0, 1], [.5, 0], [1, 1]], [[.24, .62], [.76, .62]]], 'Ι': [[[.5, 0], [.5, 1]]],
58
+ 'Ρ': [[[0, 1], [0, 0], [.6, 0], [.92, .2], [.62, .45], [0, .45]]], 'Ε': [[[.92, 0], [0, 0], [0, 1], [.92, 1]], [[0, .5], [.68, .5]]],
59
+ 'Κ': [[[0, 0], [0, 1]], [[.9, 0], [0, .56]], [[.3, .38], [.95, 1]]], 'Π': [[[0, 1], [0, 0], [.9, 0], [.9, 1]]], 'Λ': [[[0, 1], [.5, 0], [1, 1]]],
60
+ 'Υ': [[[0, 0], [.5, .48], [1, 0]], [[.5, .48], [.5, 1]]], 'Σ': [[[1, .02], [.05, 0], [.55, .5], [0, 1], [1, .98]]],
61
+ };
62
+ function greek(g, str, x, y, h, gap, seed) {
63
+ const r = rng(seed); g.save(); g.strokeStyle = C.black; g.lineWidth = 3.4; g.lineCap = 'round'; g.lineJoin = 'round';
64
+ let cx = x;
65
+ for (const ch of str) {
66
+ if (ch === ' ') { cx += h * 0.6; continue; }
67
+ const hh = h * (0.9 + r() * 0.16), oy = y + (r() - .5) * h * 0.12;
68
+ if (ch === 'Ο') { const rr = hh * 0.25; g.beginPath(); g.ellipse(cx + rr, oy + hh * 0.55, rr, rr * 1.05, 0, 0, TAU); g.stroke(); cx += rr * 2 + gap; continue; }
69
+ const w = ch === 'Ι' ? 2 : h * 0.62;
70
+ GL[ch].forEach(st => { g.beginPath(); st.forEach((p, i) => { const px = cx + p[0] * w + (r() - .5) * 1.4, py = oy + p[1] * hh + (r() - .5) * 1.4; i ? g.lineTo(px, py) : g.moveTo(px, py); }); g.stroke(); });
71
+ cx += w + gap;
72
+ }
73
+ g.restore();
74
+ }
75
+
76
+ // ---------------- 黑绘小件 ----------------
77
+ const dots = (g, pts, r = 2.6, col = C.white) => { g.fillStyle = col; pts.forEach(p => { g.beginPath(); g.arc(p[0], p[1], r, 0, TAU); g.fill(); }); };
78
+ const dotRow = (x0, x1, y, step) => { const o = []; for (let x = x0; x <= x1; x += step) o.push([x, y]); return o; };
79
+ function rosette(g, x, y, r, a) {
80
+ g.save(); g.translate(x, y); g.rotate(a); g.fillStyle = C.black;
81
+ for (let k = 0; k < 8; k++) { const q = k / 8 * TAU; g.beginPath(); g.ellipse(Math.cos(q) * r * 0.6, Math.sin(q) * r * 0.6, r * 0.42, r * 0.3, q, 0, TAU); g.fill(); }
82
+ g.beginPath(); g.arc(0, 0, r * 0.45, 0, TAU); g.fill();
83
+ g.strokeStyle = C.inc; g.lineWidth = 1.2; for (let k = 0; k < 8; k++) { const q = (k + 0.5) / 8 * TAU; g.beginPath(); g.moveTo(Math.cos(q) * r * 0.3, Math.sin(q) * r * 0.3); g.lineTo(Math.cos(q) * r * 0.85, Math.sin(q) * r * 0.85); g.stroke(); }
84
+ g.fillStyle = C.red; g.beginPath(); g.arc(0, 0, r * 0.24, 0, TAU); g.fill(); g.restore();
85
+ }
86
+ function dolphin(g, x, y, ang, s) {
87
+ g.save(); g.translate(x, y); g.rotate(ang); g.scale(s, s); g.fillStyle = C.black;
88
+ g.beginPath(); g.moveTo(34, 1); g.quadraticCurveTo(28, -4, 22, -6); g.bezierCurveTo(10, -13, -12, -12, -26, -4);
89
+ g.lineTo(-38, -12); g.lineTo(-34, 0); g.lineTo(-38, 12); g.lineTo(-26, 4); g.bezierCurveTo(-10, 10, 10, 10, 22, 5); g.quadraticCurveTo(28, 4, 34, 1); g.fill();
90
+ g.beginPath(); g.moveTo(-2, -10); g.quadraticCurveTo(-10, -22, -16, -21); g.lineTo(-12, -9); g.fill();
91
+ g.beginPath(); g.moveTo(4, 7); g.lineTo(-4, 16); g.lineTo(-8, 8); g.fill();
92
+ g.strokeStyle = C.inc; g.lineWidth = 1.4; g.beginPath(); g.moveTo(22, 2); g.bezierCurveTo(8, 6, -10, 5, -24, 1); g.stroke();
93
+ g.fillStyle = C.white; g.beginPath(); g.arc(18, -3, 1.8, 0, TAU); g.fill(); g.restore();
94
+ }
95
+
96
+ // ---------------- ② 静态黑绘层 ----------------
97
+ const stat = () => P.cached('gr_static', W, H, (g) => {
98
+ g.lineJoin = 'round'; g.lineCap = 'round';
99
+ // 顶:深色口沿 + 回纹带上下黑线;底:地线
100
+ const tg = g.createLinearGradient(0, 0, 0, 24); tg.addColorStop(0, 'rgba(18,10,6,.95)'); tg.addColorStop(1, 'rgba(26,14,8,.85)');
101
+ g.fillStyle = tg; g.fillRect(0, 0, W, 21); g.fillStyle = C.black; g.fillRect(0, 24, W, 4); g.fillRect(0, 86, W, 5);
102
+ g.fillRect(0, 899, W, 6);
103
+ // 画框:黑边 + 白点串 + 内细线
104
+ g.fillStyle = C.black; g.fillRect(362, 122, 436, 436); g.clearRect(380, 140, 400, 400);
105
+ dots(g, dotRow(371, 790, 131, 11.6), 2.3); dots(g, dotRow(371, 790, 549, 11.6), 2.3);
106
+ dots(g, dotRow(143, 540, 371, 11.6).map(p => [p[1], p[0]]), 2.3); dots(g, dotRow(143, 540, 789, 11.6).map(p => [p[1], p[0]]), 2.3);
107
+ g.strokeStyle = C.black; g.lineWidth = 2.5; g.strokeRect(386, 146, 388, 388);
108
+ // 葡萄藤(画框顶)
109
+ g.save(); g.beginPath(); g.rect(386, 146, 388, 388); g.clip();
110
+ g.strokeStyle = C.black; g.lineWidth = 3; g.beginPath(); for (let x = 392; x <= 770; x += 4) { const y = 160 + Math.sin((x - 392) / 378 * Math.PI * 3.5) * 9; x === 392 ? g.moveTo(x, y) : g.lineTo(x, y); } g.stroke();
111
+ [[420, 166], [470, 178], [545, 182], [600, 160], [665, 164], [742, 172]].forEach(([x, y], k) => {
112
+ g.fillStyle = C.black; for (let row = 0; row < 3; row++) for (let i = 0; i <= 2 - row; i++) { g.beginPath(); g.arc(x - (2 - row) * 4.5 + i * 9, y + 6 + row * 8, 4.6, 0, TAU); g.fill(); }
113
+ const lx = x + (k % 2 ? 16 : -16), ly = y - 6; g.beginPath(); g.ellipse(lx, ly, 9, 5, k % 2 ? -0.5 : 0.5, 0, TAU); g.fill();
114
+ g.strokeStyle = C.inc; g.lineWidth = 1; g.beginPath(); g.moveTo(lx - 6, ly); g.lineTo(lx + 6, ly); g.stroke();
115
+ });
116
+ g.restore();
117
+ // 桌
118
+ g.fillStyle = C.black; g.fillRect(820, 640, 380, 17); g.fillStyle = C.red; g.fillRect(824, 659, 372, 6); g.fillStyle = C.black; g.fillRect(820, 665, 380, 3);
119
+ dots(g, dotRow(832, 1190, 648.5, 13), 2.1);
120
+ const leg = (x) => { g.fillStyle = C.black; g.beginPath(); g.moveTo(x - 7, 668); g.lineTo(x + 7, 668); g.lineTo(x + 5, 682); g.lineTo(x + 4, 878); g.lineTo(x - 4, 878); g.lineTo(x - 5, 682); g.closePath(); g.fill();
121
+ g.beginPath(); g.ellipse(x + 1, 891, 11, 9, 0, Math.PI, 0); g.lineTo(x + 12, 900); g.lineTo(x - 10, 900); g.fill();
122
+ g.strokeStyle = C.inc; g.lineWidth = 1.2; g.beginPath(); g.moveTo(x - 4, 690); g.lineTo(x + 4, 690); g.stroke(); };
123
+ [866, 893, 1078, 1104].forEach(leg); g.fillStyle = C.black; g.fillRect(866, 706, 240, 5);
124
+ // 椅:天鹅颈椅背 + 车旋腿 + 紫红坐垫
125
+ const bal = (x) => { const ys = [[756, 9], [778, 22], [800, 11], [826, 9], [846, 21], [866, 11], [890, 15], [900, 15]];
126
+ g.fillStyle = C.black; g.beginPath(); ys.forEach(([y, w], i) => i ? g.lineTo(x + w / 2, y) : g.moveTo(x + w / 2, y)); [...ys].reverse().forEach(([y, w]) => g.lineTo(x - w / 2, y)); g.closePath(); g.fill();
127
+ g.fillStyle = C.red; g.fillRect(x - 10, 842, 20, 7); };
128
+ bal(1322); bal(1468);
129
+ g.fillStyle = C.black; g.fillRect(1288, 738, 206, 14); g.fillStyle = C.red; g.fillRect(1290, 752, 202, 7); dots(g, dotRow(1298, 1486, 745, 12), 2);
130
+ g.fillStyle = C.red; g.beginPath(); g.moveTo(1470, 738); g.quadraticCurveTo(1492, 718, 1506, 726); g.lineTo(1494, 740); g.closePath(); g.fill();
131
+ g.strokeStyle = C.black; g.lineWidth = 13; g.beginPath(); g.moveTo(1466, 742); g.lineTo(1470, 520); g.quadraticCurveTo(1468, 452, 1488, 440); g.quadraticCurveTo(1504, 432, 1512, 446); g.stroke();
132
+ g.fillStyle = C.black; g.beginPath(); g.ellipse(1512, 448, 13, 8, 0.1, 0, TAU); g.fill(); g.beginPath(); g.moveTo(1520, 444); g.lineTo(1543, 455); g.lineTo(1520, 454); g.fill();
133
+ g.fillStyle = C.white; g.beginPath(); g.arc(1513, 446, 2, 0, TAU); g.fill();
134
+ g.strokeStyle = C.inc; g.lineWidth = 1.5; [560, 600, 640, 690].forEach(y => { g.beginPath(); g.moveTo(1464, y); g.lineTo(1476, y); g.stroke(); });
135
+ // 双耳瓶
136
+ const am = [[1662, 604], [1738, 604], [1736, 620], [1722, 632], [1722, 662], [1764, 684], [1792, 724], [1796, 764], [1780, 812], [1748, 852], [1724, 868], [1730, 882], [1748, 900],
137
+ [1652, 900], [1670, 882], [1676, 868], [1652, 852], [1620, 812], [1604, 764], [1608, 724], [1636, 684], [1678, 662], [1678, 632], [1664, 620]];
138
+ const amp = new Path2D(); am.forEach((p, i) => i ? amp.lineTo(p[0], p[1]) : amp.moveTo(p[0], p[1])); amp.closePath();
139
+ const ams = sm(am);
140
+ g.strokeStyle = C.black; g.lineWidth = 12; g.beginPath(); g.moveTo(1724, 642); g.bezierCurveTo(1770, 626, 1784, 650, 1770, 692); g.stroke();
141
+ g.beginPath(); g.moveTo(1676, 642); g.bezierCurveTo(1630, 626, 1616, 650, 1630, 692); g.stroke();
142
+ g.fillStyle = C.black; g.fill(ams); g.fillRect(1656, 600, 88, 8);
143
+ g.save(); g.clip(ams);
144
+ const sh = g.createLinearGradient(1600, 0, 1800, 0); sh.addColorStop(0, 'rgba(255,220,180,0)'); sh.addColorStop(0.72, 'rgba(255,220,180,.09)'); sh.addColorStop(0.8, 'rgba(255,220,180,0)'); g.fillStyle = sh; g.fillRect(1600, 600, 200, 300);
145
+ g.fillStyle = C.red; g.fillRect(1600, 708, 200, 6); g.fillRect(1600, 806, 200, 5);
146
+ g.fillStyle = C.clayHi; g.fillRect(1658, 722, 84, 76); g.strokeStyle = C.black; g.lineWidth = 2.5; g.strokeRect(1658, 722, 84, 76);
147
+ g.fillStyle = C.clayHi; g.fillRect(1600, 822, 200, 32);
148
+ g.fillStyle = C.black; for (let x = 1600; x < 1800; x += 14) { g.beginPath(); g.moveTo(x, 854); g.lineTo(x + 7, 826); g.lineTo(x + 14, 854); g.fill(); }
149
+ g.restore();
150
+ // 瓶上小黑猫
151
+ g.fillStyle = C.black; g.beginPath(); g.ellipse(1696, 782, 15, 13, 0, 0, TAU); g.fill(); g.beginPath(); g.arc(1708, 760, 9, 0, TAU); g.fill();
152
+ g.beginPath(); g.moveTo(1701, 755); g.lineTo(1703, 743); g.lineTo(1708, 752); g.lineTo(1713, 742); g.lineTo(1716, 756); g.fill();
153
+ g.strokeStyle = C.black; g.lineWidth = 4; g.beginPath(); g.moveTo(1683, 790); g.quadraticCurveTo(1670, 792, 1672, 778); g.stroke();
154
+ g.fillRect(1680, 792, 34, 4);
155
+ dots(g, [[1705, 759], [1712, 759]], 1.6, '#f4f0d0');
156
+ // 铭文
157
+ greek(g, 'ΧΑΙΡΕ ΚΑΙ ΠΙΕΙ', 866, 474, 30, 9, 11);
158
+ greek(g, 'ΑΙΛΟΥΡΟΣ ΚΑΛΟΣ', 86, 708, 30, 10, 12);
159
+ });
160
+
161
+ // ---------------- ③ 滚动纹带 ----------------
162
+ const TOP_P = 60, BOT_P = 76;
163
+ const topStrip = () => P.cached('gr_topStrip', W + TOP_P * 2, 60, (g) => {
164
+ g.strokeStyle = C.black; g.lineWidth = 7; g.lineCap = 'square'; g.lineJoin = 'miter';
165
+ const y0 = 6, h = 46;
166
+ g.beginPath(); g.moveTo(0, y0 + h); g.lineTo(W + TOP_P * 2, y0 + h); g.stroke();
167
+ for (let x = 0; x < W + TOP_P * 2; x += TOP_P) {
168
+ line(g, [[x + 4, y0 + h], [x + 4, y0], [x + 50, y0], [x + 50, y0 + 34], [x + 18, y0 + 34], [x + 18, y0 + 14], [x + 36, y0 + 14], [x + 36, y0 + 22]]); g.stroke();
169
+ }
170
+ });
171
+ const botStrip = () => P.cached('gr_botStrip', W + BOT_P * 2, 150, (g) => {
172
+ const Y = 930; // 条带局部 y = 画面 y - 930
173
+ g.fillStyle = C.black; g.fillRect(0, 934 - Y, W + BOT_P * 2, 4); g.fillRect(0, 996 - Y, W + BOT_P * 2, 6); g.fillRect(0, 1072 - Y, W + BOT_P * 2, 8);
174
+ g.lineCap = 'round'; g.lineJoin = 'round';
175
+ for (let i = 0, x = 0; x < W + BOT_P * 2; i++, x += BOT_P / 2) {
176
+ g.strokeStyle = C.black; g.lineWidth = 2.6; g.beginPath(); g.moveTo(x, 946 - Y); g.quadraticCurveTo(x + 19, 962 - Y, x + 38, 946 - Y); g.stroke();
177
+ const bud = new Path2D(); bud.moveTo(x, 948 - Y); bud.bezierCurveTo(x + 11, 954 - Y, x + 10, 972 - Y, x, 988 - Y); bud.bezierCurveTo(x - 10, 972 - Y, x - 11, 954 - Y, x, 948 - Y);
178
+ g.fillStyle = i % 2 ? C.red : C.black; g.fill(bud); g.strokeStyle = C.black; g.lineWidth = 2.4; g.stroke(bud);
179
+ if (i % 2) { g.strokeStyle = C.black; g.lineWidth = 1.6; g.beginPath(); g.moveTo(x, 953 - Y); g.lineTo(x, 982 - Y); g.stroke(); }
180
+ g.fillStyle = C.black; g.beginPath(); g.arc(x + 19, 972 - Y, 2.6, 0, TAU); g.fill();
181
+ g.beginPath(); g.moveTo(x - 17, 1074 - Y); g.lineTo(x + 2, 1008 - Y); g.lineTo(x + 21, 1074 - Y); g.fill(); // 射线纹
182
+ }
183
+ });
184
+
185
+ // ---------------- ④ 画框里的船、海豚 ----------------
186
+ function panel(c, lt) {
187
+ c.save(); c.beginPath(); c.rect(388, 148, 384, 384); c.clip();
188
+ const w = Math.sin(lt * TAU / 0.9), w2 = Math.cos(lt * TAU / 0.9);
189
+ // 海豚:沿自身朝向来回游 + 微摆
190
+ [[416, 266, 1.45, 0.95], [748, 276, 1.75, 0.92], [462, 352, -1.05, 0.9], [442, 498, -0.25, 1.05], [652, 496, -0.12, 1.05], [738, 455, 0.25, 0.85]].forEach(([x, y, a, s], k) => {
191
+ const ph = lt * TAU / 0.85 + k * 1.3, d = 9 * Math.sin(ph);
192
+ dolphin(c, x + Math.cos(a) * d, y + Math.sin(a) * d, a + 0.12 * Math.cos(ph), s);
193
+ });
194
+ // 船:随波起伏、微微摇
195
+ c.translate(560, 420); c.rotate(0.025 * w2); c.translate(0, 4 * w); c.translate(-560, -420);
196
+ c.lineCap = 'round'; c.lineJoin = 'round';
197
+ c.strokeStyle = C.black; c.lineWidth = 1.6; line(c, [[458, 302], [418, 396]]); c.stroke(); line(c, [[662, 302], [700, 392]]); c.stroke();
198
+ c.lineWidth = 4.5; line(c, [[560, 214], [560, 400]]); c.stroke();
199
+ const sail = new Path2D(); sail.moveTo(458, 210); sail.lineTo(662, 210); sail.quadraticCurveTo(672, 258, 662, 304); sail.quadraticCurveTo(560, 312, 458, 304); sail.quadraticCurveTo(448, 258, 458, 210);
200
+ c.fillStyle = '#f1e7d6'; c.fill(sail); c.lineWidth = 2.6; c.strokeStyle = C.black; c.stroke(sail);
201
+ c.strokeStyle = C.red; c.lineWidth = 1.6; [500, 540, 582, 622].forEach(x => { c.beginPath(); c.moveTo(x, 212); c.lineTo(x + 1, 306); c.stroke(); });
202
+ c.fillStyle = '#7a3a2a'; [[478, 240], [512, 278], [596, 236], [640, 284], [548, 296]].forEach(([x, y]) => { c.beginPath(); c.arc(x, y, 1.6, 0, TAU); c.fill(); });
203
+ c.strokeStyle = C.black; c.lineWidth = 6; line(c, [[446, 208], [674, 208]]); c.stroke();
204
+ // 船身(新月形,船尾蝎尾般卷起)
205
+ c.fillStyle = C.black; c.beginPath(); c.moveTo(404, 400); c.quadraticCurveTo(430, 394, 470, 398); c.lineTo(690, 392); c.quadraticCurveTo(712, 370, 712, 330);
206
+ c.quadraticCurveTo(708, 312, 696, 316); c.quadraticCurveTo(718, 300, 728, 326); c.quadraticCurveTo(734, 384, 700, 420); c.quadraticCurveTo(600, 452, 470, 440); c.quadraticCurveTo(428, 430, 404, 400); c.fill();
207
+ c.fillStyle = C.red; c.beginPath(); c.moveTo(470, 410); c.lineTo(690, 404); c.quadraticCurveTo(600, 424, 470, 422); c.closePath(); c.fill();
208
+ c.strokeStyle = C.inc; c.lineWidth = 1.4; c.beginPath(); c.moveTo(440, 426); c.quadraticCurveTo(560, 444, 700, 414); c.stroke();
209
+ c.fillStyle = C.white; c.beginPath(); c.arc(428, 404, 3, 0, TAU); c.fill();
210
+ c.strokeStyle = C.black; c.lineWidth = 4; line(c, [[690, 410], [722, 474]]); c.stroke(); line(c, [[702, 406], [744, 462]]); c.stroke();
211
+ c.fillStyle = C.black; c.beginPath(); c.ellipse(724, 478, 5, 10, -0.5, 0, TAU); c.fill(); c.beginPath(); c.ellipse(746, 466, 5, 10, -0.7, 0, TAU); c.fill();
212
+ // 船上斜倚的人(酒神)
213
+ c.fillStyle = C.red; c.beginPath(); c.moveTo(520, 398); c.quadraticCurveTo(580, 372, 640, 384); c.lineTo(640, 398); c.closePath(); c.fill();
214
+ c.fillStyle = C.black; c.beginPath(); c.moveTo(560, 396); c.quadraticCurveTo(596, 360, 632, 362); c.quadraticCurveTo(652, 372, 646, 396); c.closePath(); c.fill();
215
+ c.beginPath(); c.arc(636, 348, 13, 0, TAU); c.fill();
216
+ c.strokeStyle = C.black; c.lineWidth = 7; line(c, [[620, 368], [596, 342], [594, 326]]); c.stroke();
217
+ c.strokeStyle = C.inc; c.lineWidth = 1.2; c.beginPath(); c.arc(638, 346, 5, 0, TAU); c.stroke();
218
+ c.restore();
219
+ }
220
+
221
+ // ---------------- ④ 里拉琴(以钉子为轴摆) ----------------
222
+ function lyre(c, lt) {
223
+ const piv = [1050, 150], a = 0.09 * Math.sin(lt * TAU / 0.8 + 0.5);
224
+ c.save(); c.translate(piv[0], piv[1]); c.rotate(a); c.translate(-piv[0], -piv[1]);
225
+ c.lineCap = 'round'; c.lineJoin = 'round';
226
+ c.strokeStyle = C.red; c.lineWidth = 2.4; line(c, [[986, 214], [1050, 150], [1112, 210]]); c.stroke();
227
+ c.strokeStyle = C.black; c.lineWidth = 8; c.beginPath(); c.moveTo(1004, 326); c.quadraticCurveTo(960, 270, 990, 200); c.stroke();
228
+ c.beginPath(); c.moveTo(1034, 326); c.quadraticCurveTo(1080, 272, 1104, 204); c.stroke();
229
+ c.lineWidth = 7; line(c, [[978, 214], [1116, 212]]); c.stroke();
230
+ c.fillStyle = C.black; [[976, 214], [1118, 212]].forEach(([x, y]) => { c.beginPath(); c.arc(x, y, 6, 0, TAU); c.fill(); });
231
+ c.lineWidth = 1.5; for (let k = 0; k < 7; k++) { c.beginPath(); c.moveTo(1000 + k * 14, 214); c.lineTo(1008 + k * 3.6, 322); c.stroke(); }
232
+ c.beginPath(); c.arc(1019, 356, 44, 0, TAU); c.fill();
233
+ c.strokeStyle = C.inc; c.lineWidth = 1.6; c.beginPath(); c.arc(1019, 356, 37, 0, TAU); c.stroke();
234
+ c.beginPath(); c.arc(1019, 332, 8, 0.1, Math.PI - 0.1); c.stroke();
235
+ [[1019, 360], [1004, 352], [1034, 352], [1008, 372], [1030, 372]].forEach(([x, y]) => { c.beginPath(); c.arc(x, y, 6.5, 0, TAU); c.stroke(); });
236
+ c.restore();
237
+ c.fillStyle = C.black; c.beginPath(); c.arc(piv[0], piv[1], 5, 0, TAU); c.fill();
238
+ }
239
+
240
+ // ---------------- ⑤ 橘白猫:黑绘画法(黑釉剪影 + 陶土橙刻线虎斑 + 紫红/白附加色) ----------------
241
+ // 「橘」= 陶土橙刻线条纹 + 背/头顶紫红附加色;「白」= 白彩(胸、口鼻、爪);眼 = 古风刻线杏仁眼。
242
+ const CAT_W = '#efe1c8';
243
+ function cat(c, ch) {
244
+ const K = RIG.cat({ tail: ch.tail, blink: ch.blink, breathe: ch.breathe });
245
+ c.save(); c.lineJoin = 'round'; c.lineCap = 'round';
246
+ c.translate(548, 903); c.scale(0.9, 0.9); c.translate(-548, -903); // 猫整体缩到标准框 (390,610)-(690,905)
247
+ // 尾巴:黑剪影 + 紫红芯 + 陶土橙刻线环
248
+ const tp = K.tailPts, tail = new Path2D(); tp.forEach((p, i) => i ? tail.lineTo(p[0], p[1]) : tail.moveTo(p[0], p[1]));
249
+ c.strokeStyle = C.black; c.lineWidth = 22; c.stroke(tail); c.strokeStyle = C.red; c.lineWidth = 7; c.stroke(tail);
250
+ c.strokeStyle = C.catO; c.lineWidth = 2.6;
251
+ for (let i = 2; i < tp.length - 1; i += 2) { const a = tp[i - 1], b = tp[i + 1], ang = Math.atan2(b[1] - a[1], b[0] - a[0]) + Math.PI / 2; c.beginPath(); c.moveTo(tp[i][0] - Math.cos(ang) * 9, tp[i][1] - Math.sin(ang) * 9); c.lineTo(tp[i][0] + Math.cos(ang) * 9, tp[i][1] + Math.sin(ang) * 9); c.stroke(); }
252
+ c.translate(540, 902); c.scale(1, 1 + ch.breathe); c.translate(-540, -902); // 呼吸
253
+ const body = sm([[566, 680], [520, 698], [480, 736], [454, 790], [444, 850], [454, 896], [520, 902], [672, 902], [676, 886], [660, 872], [654, 800], [648, 738], [636, 698]]);
254
+ c.fillStyle = C.black; c.fill(body);
255
+ c.save(); c.clip(body);
256
+ // 背部紫红附加色鞍区(沿背轮廓内侧一条宽带)
257
+ c.strokeStyle = C.red; c.lineWidth = 26; c.beginPath(); c.moveTo(575, 690); c.bezierCurveTo(500, 700, 456, 760, 456, 880); c.stroke();
258
+ // 白彩胸
259
+ const white = sm([[600, 692], [640, 700], [646, 760], [636, 830], [628, 886], [604, 888], [596, 820], [590, 740]]);
260
+ c.fillStyle = CAT_W; c.fill(white);
261
+ // 刻线虎斑:沿背轮廓等距取点、朝身体中心刻弯线(陶土橙)
262
+ const back = [[570, 680], [520, 698], [480, 736], [454, 790], [444, 850], [454, 896]], seg = [];
263
+ for (let i = 1; i < back.length; i++) seg.push(Math.hypot(back[i][0] - back[i - 1][0], back[i][1] - back[i - 1][1]));
264
+ const tot = seg.reduce((a, b) => a + b, 0);
265
+ const at = (u) => { let d = u * tot, i = 0; while (i < seg.length - 1 && d > seg[i]) { d -= seg[i]; i++; } const q = d / seg[i]; return [lerp(back[i][0], back[i + 1][0], q), lerp(back[i][1], back[i + 1][1], q)]; };
266
+ c.strokeStyle = C.catO; c.lineWidth = 3;
267
+ for (let k = 0; k < 14; k++) { const a = at(0.03 + k * 0.068), dir = Math.atan2(800 - a[1], 575 - a[0]) + 0.25, L = 40 + (k % 3) * 12, nx = Math.cos(dir + Math.PI / 2), ny = Math.sin(dir + Math.PI / 2);
268
+ const b = [a[0] + Math.cos(dir) * L, a[1] + Math.sin(dir) * L]; c.beginPath(); c.moveTo(a[0] - Math.cos(dir) * 6, a[1] - Math.sin(dir) * 6); c.quadraticCurveTo((a[0] + b[0]) / 2 + nx * 6, (a[1] + b[1]) / 2 + ny * 6, b[0], b[1]); c.stroke(); }
269
+ [[[500, 830], [530, 806], [566, 816]], [[494, 858], [530, 838], [570, 850]], [[508, 884], [534, 868], [566, 878]]].forEach(s => { c.beginPath(); c.moveTo(...s[0]); c.quadraticCurveTo(...s[1], ...s[2]); c.stroke(); });
270
+ [[[650, 776], [670, 772]], [[650, 806], [670, 804]], [[648, 836], [666, 836]]].forEach(([a, b]) => { c.beginPath(); c.moveTo(...a); c.lineTo(...b); c.stroke(); });
271
+ c.restore();
272
+ // 刻线:后腿大腿弧、前腿分界
273
+ c.strokeStyle = C.inc; c.lineWidth = 2.2; c.beginPath(); c.arc(512, 860, 52, Math.PI * 1.08, Math.PI * 1.95); c.stroke();
274
+ c.beginPath(); c.moveTo(626, 770); c.lineTo(628, 884); c.stroke();
275
+ // 爪:白彩 + 黑刻趾
276
+ c.fillStyle = CAT_W; [[606, 893], [650, 893]].forEach(([x, y]) => { c.beginPath(); c.ellipse(x, y, 21, 10, 0, 0, TAU); c.fill(); c.strokeStyle = C.black; c.lineWidth = 1.8; [-7, 1, 9].forEach(o => { c.beginPath(); c.moveTo(x + o, y - 3); c.lineTo(x + o, y + 8); c.stroke(); }); });
277
+ c.fillStyle = C.black; c.beginPath(); c.ellipse(512, 894, 22, 9, 0, 0, TAU); c.fill();
278
+ c.strokeStyle = C.inc; c.lineWidth = 1.6; [504, 512, 520].forEach(x => { c.beginPath(); c.moveTo(x, 889); c.lineTo(x - 1, 900); c.stroke(); });
279
+ // 头:黑剪影,头顶紫红,白彩口鼻
280
+ const head = new Path2D(); head.ellipse(600, 630, 60, 52, 0, 0, TAU);
281
+ const ears = new Path2D(); ears.moveTo(548, 606); ears.lineTo(556, 540); ears.lineTo(592, 584); ears.closePath(); ears.moveTo(612, 582); ears.lineTo(650, 540); ears.lineTo(656, 606); ears.closePath();
282
+ c.fillStyle = C.black; c.fill(ears); c.fill(head);
283
+ c.save(); c.clip(head);
284
+ c.fillStyle = C.red; c.beginPath(); c.ellipse(600, 586, 46, 22, 0, 0, TAU); c.fill();
285
+ c.fillStyle = CAT_W; c.beginPath(); c.ellipse(608, 664, 32, 20, 0, 0, TAU); c.fill();
286
+ c.strokeStyle = C.catO; c.lineWidth = 2.6;
287
+ [[[584, 580], [590, 604]], [[600, 578], [600, 604]], [[616, 580], [610, 604]], [[542, 628], [562, 634]], [[544, 646], [562, 646]], [[658, 626], [640, 634]], [[658, 642], [642, 646]]].forEach(([a, b]) => { c.beginPath(); c.moveTo(...a); c.lineTo(...b); c.stroke(); });
288
+ c.restore();
289
+ c.strokeStyle = C.inc; c.lineWidth = 1.8; c.beginPath(); c.moveTo(560, 592); c.lineTo(563, 558); c.lineTo(580, 584); c.moveTo(624, 584); c.lineTo(645, 558); c.lineTo(648, 596); c.stroke();
290
+ // 古风刻线杏仁眼(白彩眼白 + 黑瞳)/ 眨眼
291
+ [[580, 622, -0.08], [628, 618, 0.08]].forEach(([x, y, r]) => {
292
+ c.save(); c.translate(x, y); c.rotate(r); c.strokeStyle = C.inc; c.lineWidth = 2;
293
+ if (ch.blink) { c.beginPath(); c.moveTo(-15, 0); c.quadraticCurveTo(0, 6, 15, 0); c.stroke(); c.restore(); return; }
294
+ const eye = new Path2D(); eye.moveTo(-15, 1); eye.quadraticCurveTo(0, -13, 15, 0); eye.quadraticCurveTo(0, 10, -15, 1);
295
+ c.fillStyle = CAT_W; c.fill(eye); c.stroke(eye);
296
+ c.fillStyle = C.black; c.beginPath(); c.ellipse(3, -1, 3.4, 5.6, 0, 0, TAU); c.fill();
297
+ c.beginPath(); c.moveTo(15, 0); c.lineTo(20, -2); c.stroke(); c.restore(); });
298
+ // 鼻(紫红)、刻线嘴、须
299
+ c.fillStyle = C.red; c.beginPath(); c.moveTo(603, 646); c.lineTo(619, 646); c.lineTo(611, 656); c.closePath(); c.fill();
300
+ c.strokeStyle = C.black; c.lineWidth = 2; c.beginPath(); c.moveTo(611, 656); c.lineTo(611, 662); c.moveTo(597, 662); c.quadraticCurveTo(604, 669, 611, 662); c.quadraticCurveTo(618, 669, 625, 662); c.stroke();
301
+ c.strokeStyle = C.inc; c.lineWidth = 1.4; [[[568, 652], [534, 646]], [[568, 660], [536, 666]]].forEach(([a, b]) => { c.beginPath(); c.moveTo(...a); c.lineTo(...b); c.stroke(); });
302
+ c.strokeStyle = C.black; [[[640, 656], [690, 648]], [[640, 662], [692, 668]]].forEach(([a, b]) => { c.beginPath(); c.moveTo(...a); c.lineTo(...b); c.stroke(); });
303
+ // 紫红项圈 + 白点
304
+ c.strokeStyle = C.red; c.lineWidth = 14; c.beginPath(); c.moveTo(556, 684); c.quadraticCurveTo(604, 704, 652, 688); c.stroke();
305
+ c.strokeStyle = C.inc; c.lineWidth = 1.4; [-7, 7].forEach(o => { c.beginPath(); c.moveTo(556, 684 + o); c.quadraticCurveTo(604, 704 + o, 652, 688 + o); c.stroke(); });
306
+ dots(c, [0.1, 0.25, 0.4, 0.55, 0.7, 0.85].map(u => { const x = (1 - u) * (1 - u) * 556 + 2 * u * (1 - u) * 604 + u * u * 652, y = (1 - u) * (1 - u) * 684 + 2 * u * (1 - u) * 704 + u * u * 688; return [x, y]; }), 2.2);
307
+ c.restore();
308
+ }
309
+
310
+ // ---------------- ⑥ 少女:黑釉剪影 + 刻线 + 附加色 + 金色卷发 ----------------
311
+ const R6 = rng(606);
312
+ const crownStrands = [...Array(11).keys()].map(k => ({ A: [1259 + k * 3.4, 311 + k * 5], C: [1314 + k * 2.6, 272 + k * 8.5], B: [1388 + k * 2.2, 318 + k * 6.5], ph: R6() * 6 }));
313
+ const locks = [...Array(8).keys()].map(k => ({ x0: 1374 + k * 5.5, y0: 372 + k * 2, x1: 1380 + k * 4.6, y1: 516 + (k % 3) * 13, ph: R6() * 6, a: 4.5 + R6() * 2.5 }));
314
+ const hairOut = [[1262, 296], [1298, 281], [1342, 284], [1378, 302], [1398, 326], [1414, 344], [1410, 372], [1416, 402], [1424, 452], [1422, 510], [1410, 550], [1392, 558], [1380, 512], [1366, 464], [1346, 436]];
315
+ const hairSil = [[1251, 320], ...hairOut, [1326, 422]];
316
+ const hairRegion = sm([[1254, 318], ...hairOut, [1326, 420], [1314, 396], [1306, 372], [1298, 348], [1284, 330], [1266, 322]]);
317
+ const faceSil = sm([[1290, 432], [1282, 418], [1262, 411], [1251, 403], [1246, 395], [1250, 389], [1244, 384], [1246, 377], [1235, 370], [1240, 358], [1246, 345], [1251, 330], [1254, 318],
318
+ ...hairSil.slice(1)]);
319
+ function curl(c, x, y, r, turns, dir = 1) { c.beginPath(); for (let a = 0; a <= turns * TAU; a += 0.3) { const rr = r * (1 - a / (turns * TAU) * 0.75); const px = x + Math.cos(a * dir) * rr, py = y + Math.sin(a * dir) * rr; a ? c.lineTo(px, py) : c.moveTo(px, py); } c.stroke(); }
320
+ const headM = (G) => { const NP = Tg(1300, 425); return new DOMMatrix().translate(NP[0], NP[1]).rotate(G.tilt * 180 / Math.PI).translate(-NP[0], -NP[1]).translate(1300, GDY + 620).scale(GS, GS).translate(-1300, -620); };
321
+ function head(c, G, ch, lt) {
322
+ const NP = Tg(1300, 425);
323
+ c.save(); c.translate(NP[0], NP[1]); c.rotate(G.tilt); c.translate(-NP[0], -NP[1]);
324
+ c.translate(1300, GDY + 620); c.scale(GS, GS); c.translate(-1300, -620);
325
+ c.lineCap = 'round'; c.lineJoin = 'round';
326
+ c.fillStyle = C.black; c.fill(faceSil);
327
+ // 金色卷发:头顶波浪发丝 + 发髻螺旋 + 背后长卷发绺(裁进发区)
328
+ c.save(); c.clip(hairRegion); c.strokeStyle = C.gold; c.lineWidth = 2.5;
329
+ crownStrands.forEach(({ A, C: Q, B, ph }) => { c.beginPath(); for (let i = 0; i <= 34; i++) { const u = i / 34, v = 1 - u;
330
+ const x = v * v * A[0] + 2 * u * v * Q[0] + u * u * B[0], y = v * v * A[1] + 2 * u * v * Q[1] + u * u * B[1];
331
+ const dx = 2 * v * (Q[0] - A[0]) + 2 * u * (B[0] - Q[0]), dy = 2 * v * (Q[1] - A[1]) + 2 * u * (B[1] - Q[1]), L = Math.hypot(dx, dy) || 1, o = 2.6 * Math.sin(u * Math.PI * 7 + ph);
332
+ i ? c.lineTo(x - dy / L * o, y + dx / L * o) : c.moveTo(x - dy / L * o, y + dx / L * o); } c.stroke(); });
333
+ locks.forEach(({ x0, y0, x1, y1, ph, a }) => { c.beginPath(); for (let i = 0; i <= 30; i++) { const u = i / 30, x = lerp(x0, x1, u) + a * Math.sin(u * Math.PI * 4.5 + ph), y = lerp(y0, y1, u); i ? c.lineTo(x, y) : c.moveTo(x, y); } c.stroke();
334
+ curl(c, x1 + a * Math.sin(Math.PI * 4.5 + ph) - 4, y1 + 2, 5.5, 1.3); });
335
+ curl(c, 1398, 344, 17, 2.4); curl(c, 1398, 344, 10, 1.4, -1);
336
+ c.restore();
337
+ // 额前小卷 + 鬓角螺旋卷
338
+ c.strokeStyle = C.gold; c.lineWidth = 2; [[1262, 314], [1268, 305], [1277, 298], [1288, 293]].forEach(([x, y]) => curl(c, x, y, 4.2, 1.4));
339
+ c.lineWidth = 2.2; [[1298, 350, 1300, 420], [1307, 356, 1312, 428]].forEach(([x0, y0, x1, y1], k) => { c.strokeStyle = C.black; c.lineWidth = 6; const pth = new Path2D();
340
+ for (let i = 0; i <= 40; i++) { const u = i / 40, x = lerp(x0, x1, u) + 5 * Math.sin(u * Math.PI * 9 + k), y = lerp(y0, y1, u); i ? pth.lineTo(x, y) : pth.moveTo(x, y); }
341
+ c.stroke(pth); c.strokeStyle = C.gold; c.lineWidth = 2.2; c.stroke(pth); });
342
+ // 紫红发带 + 白点
343
+ const band = new Path2D(); band.moveTo(1259, 307); band.quadraticCurveTo(1318, 276, 1406, 322);
344
+ c.strokeStyle = C.black; c.lineWidth = 14; c.stroke(band); c.strokeStyle = C.red; c.lineWidth = 10; c.stroke(band);
345
+ dots(c, [0.12, 0.3, 0.48, 0.66, 0.84].map(u => [(1 - u) * (1 - u) * 1259 + 2 * u * (1 - u) * 1318 + u * u * 1406, (1 - u) * (1 - u) * 307 + 2 * u * (1 - u) * 276 + u * u * 322]), 1.9);
346
+ // 耳 + 金耳环
347
+ c.fillStyle = C.black; c.beginPath(); c.ellipse(1316, 374, 9, 13, 0.15, 0, TAU); c.fill();
348
+ c.strokeStyle = C.inc; c.lineWidth = 1.6; c.beginPath(); c.ellipse(1316, 374, 9, 13, 0.15, -1.9, 1.6); c.stroke(); c.beginPath(); c.arc(1316, 374, 4, -1.5, 1.6); c.stroke();
349
+ c.fillStyle = C.gold; c.beginPath(); c.arc(1317, 393, 4, 0, TAU); c.fill();
350
+ // 刻线五官:杏仁大眼(古风正面眼)、眉、鼻翼、唇
351
+ c.strokeStyle = C.inc; c.lineWidth = 1.7;
352
+ c.beginPath(); c.moveTo(1251, 335); c.quadraticCurveTo(1264, 326, 1281, 333); c.stroke();
353
+ if (ch.blink) { c.beginPath(); c.moveTo(1253, 347); c.quadraticCurveTo(1266, 351, 1281, 346); c.stroke(); }
354
+ else {
355
+ const eye = new Path2D(); eye.moveTo(1252, 346); eye.quadraticCurveTo(1265, 335, 1282, 345); eye.quadraticCurveTo(1266, 354, 1252, 346);
356
+ c.fillStyle = '#f3dcc0'; c.fill(eye); c.stroke(eye);
357
+ c.fillStyle = C.black; c.beginPath(); c.arc(1263, 345, 4, 0, TAU); c.fill();
358
+ c.beginPath(); c.moveTo(1282, 345); c.lineTo(1287, 343); c.stroke();
359
+ }
360
+ c.beginPath(); c.moveTo(1241, 373); c.quadraticCurveTo(1247, 376, 1250, 372); c.stroke();
361
+ c.beginPath(); c.moveTo(1247, 389); c.quadraticCurveTo(1252, 391, 1256, 387); c.stroke();
362
+ c.beginPath(); c.moveTo(1262, 402); c.quadraticCurveTo(1270, 404, 1276, 400); c.stroke();
363
+ c.restore();
364
+ }
365
+ function girl(c, ch, lt, t) {
366
+ const G = RIG.girl({ cup: ch.cup, sip: ch.sip, breathe: ch.breathe, s: GS, dy: GDY, hair: 'long' });
367
+ c.save(); c.lineCap = 'round'; c.lineJoin = 'round';
368
+ // 剪影:远侧袖与手、裙、身、颈
369
+ c.fillStyle = C.black; [G.farSleeve, G.skirt, G.torso, G.neck].forEach(p => c.fill(p));
370
+ const fh = Tg(1212, 618); c.beginPath(); c.ellipse(fh[0], fh[1], 15, 11, 0.2, 0, TAU); c.fill();
371
+ const foot = smT([[1118, 906], [1080, 912], [1052, 922], [1048, 930], [1128, 931], [1142, 914]], true); c.fill(foot);
372
+ c.strokeStyle = C.inc; c.lineWidth = 1.6; [[[1074, 914], [1082, 930]], [[1096, 910], [1102, 930]], [[1052, 927], [1128, 927]], [[1080, 912], [1112, 922]]].forEach(s => { c.stroke(smT(s)); });
373
+ const body = new Path2D(); [G.torso, G.skirt, G.farSleeve, G.neck].forEach(p => body.addPath(p));
374
+ // 刻线衣褶(希顿长袍)
375
+ c.save(); c.clip(body); c.strokeStyle = C.inc; c.lineWidth = 1.8;
376
+ [[[1292, 448], [1312, 458], [1336, 452]], [[1342, 470], [1322, 520], [1300, 566]], [[1366, 478], [1346, 540], [1320, 596]], [[1392, 498], [1374, 560], [1352, 606]], [[1404, 522], [1410, 572], [1406, 612]],
377
+ [[1288, 614], [1340, 604], [1416, 614]], [[1180, 690], [1262, 668], [1360, 686], [1438, 698]], [[1164, 736], [1262, 724], [1352, 750]], [[1446, 660], [1440, 720], [1420, 760]],
378
+ [[1150, 770], [1134, 846], [1122, 912]], [[1184, 776], [1174, 852], [1166, 916]], [[1220, 780], [1216, 852], [1214, 918]], [[1260, 784], [1262, 856], [1262, 920]], [[1304, 786], [1308, 856], [1310, 920]], [[1340, 800], [1344, 860], [1344, 918]],
379
+ [[1240, 634], [1218, 662], [1196, 700]]].forEach(s => c.stroke(smT(s)));
380
+ // 下摆紫红宽带 + 白点 + 白点小花
381
+ const hb = smT([[1080, 892], [1220, 896], [1370, 892]]); c.strokeStyle = C.red; c.lineWidth = 15; c.stroke(hb);
382
+ c.strokeStyle = C.inc; c.lineWidth = 1.4; c.stroke(smT([[1080, 880], [1220, 884], [1370, 880]])); c.stroke(smT([[1080, 905], [1220, 909], [1370, 905]]));
383
+ dots(c, dotRow(1104, 1350, 894, 17).map(p => Tg(p[0], p[1] + (p[0] - 1220) * 0.0)), 2.2);
384
+ [[1235, 845], [1290, 826], [1172, 866], [1330, 712], [1388, 566], [1340, 530], [1206, 742], [1300, 680]].forEach(([x, y]) => { const p = Tg(x, y); dots(c, [[p[0] - 4, p[1]], [p[0] + 4, p[1]], [p[0], p[1] - 4], [p[0], p[1] + 4]], 1.7); });
385
+ c.restore();
386
+ // 长卷发 + 头(含倾斜)
387
+ head(c, G, ch, lt);
388
+ // 近侧手臂:黑 + 与身体重叠处刻线分隔 + 袖口紫红带 + 紫红手镯
389
+ // 时代化端杯:杯沿右端贴在嘴唇(不是 RIG 的鼻尖附近),杯身近水平,手托在杯脚下、不挡下巴
390
+ const HM = headM(G), lip = new DOMPoint(1243, 391).matrixTransform(HM), tab = Tg(1190, 596), q = ch.cup;
391
+ const rim = [lerp(tab[0] - 4, lip.x - 30, q), lerp(tab[1] - 14, lip.y + 1, q)];
392
+ G.kylix = { x: rim[0], y: rim[1], tilt: -0.08 * q };
393
+ const S = G.A.shoulder, hT = [rim[0] + lerp(32, 6, q), rim[1] + 30];
394
+ const { E, H: Hd } = RIG.ik2(S, hT, 165 * GS, 122 * GS, -1), fa = Math.atan2(Hd[1] - E[1], Hd[0] - E[0]);
395
+ const Wr = [lerp(E[0], Hd[0], 0.82), lerp(E[1], Hd[1], 0.82)];
396
+ const upperArm = RIG.taper(S, E, 54 * GS, 40 * GS, 10 * GS), foreArm = RIG.taper(E, Wr, 40 * GS, 30 * GS, 2 * GS);
397
+ const hand = new Path2D(); hand.ellipse(Hd[0], Hd[1], 17 * GS, 14 * GS, fa, 0, TAU);
398
+ c.fillStyle = C.black; [upperArm, foreArm, hand].forEach(p => c.fill(p));
399
+ const bodyH = new Path2D(); bodyH.addPath(body); bodyH.addPath(faceSil, HM);
400
+ c.save(); c.clip(bodyH); c.strokeStyle = C.inc; c.lineWidth = 2; [upperArm, foreArm, hand].forEach(p => c.stroke(p)); c.restore();
401
+ c.save(); c.clip(upperArm);
402
+ const M = [lerp(S[0], E[0], 0.6), lerp(S[1], E[1], 0.6)], aa = Math.atan2(E[1] - S[1], E[0] - S[0]), nx = Math.cos(aa + Math.PI / 2), ny = Math.sin(aa + Math.PI / 2);
403
+ c.strokeStyle = C.red; c.lineWidth = 15; c.beginPath(); c.moveTo(M[0] - nx * 40, M[1] - ny * 40); c.lineTo(M[0] + nx * 40, M[1] + ny * 40); c.stroke();
404
+ dots(c, [-12, 0, 12].map(k => [M[0] + nx * k, M[1] + ny * k]), 1.9);
405
+ c.strokeStyle = C.inc; c.lineWidth = 1.4; [-9, 9].forEach(o => { c.beginPath(); c.moveTo(M[0] + Math.cos(aa) * o - nx * 40, M[1] + Math.sin(aa) * o - ny * 40); c.lineTo(M[0] + Math.cos(aa) * o + nx * 40, M[1] + Math.sin(aa) * o + ny * 40); c.stroke(); });
406
+ c.restore();
407
+ const wr = [lerp(E[0], Hd[0], 0.74), lerp(E[1], Hd[1], 0.74)], qx = Math.cos(fa + Math.PI / 2), qy = Math.sin(fa + Math.PI / 2);
408
+ c.strokeStyle = C.red; c.lineWidth = 6; c.beginPath(); c.moveTo(wr[0] - qx * 14, wr[1] - qy * 14); c.lineTo(wr[0] + qx * 14, wr[1] + qy * 14); c.stroke();
409
+ // 基里克斯杯(宽浅双耳)
410
+ const cp = G.kylix; c.save(); c.translate(cp.x, cp.y); c.rotate(cp.tilt);
411
+ c.fillStyle = C.black; c.beginPath(); c.moveTo(-32, 0); c.quadraticCurveTo(0, 26, 32, 0); c.closePath(); c.fill();
412
+ c.fillRect(-3, 11, 6, 10); c.beginPath(); c.ellipse(0, 21, 12, 3.5, 0, 0, TAU); c.fill();
413
+ c.strokeStyle = C.black; c.lineWidth = 3.6; c.beginPath(); c.moveTo(-26, 5); c.quadraticCurveTo(-44, 0, -38, -8); c.stroke(); c.beginPath(); c.moveTo(26, 5); c.quadraticCurveTo(44, 0, 38, -8); c.stroke();
414
+ c.strokeStyle = C.red; c.lineWidth = 3; c.beginPath(); c.moveTo(-30, 1); c.lineTo(30, 1); c.stroke();
415
+ c.restore();
416
+ // 手:刻线手指
417
+ c.strokeStyle = C.inc; c.lineWidth = 1.5; [-5, 1, 7].forEach(o => { c.beginPath(); c.moveTo(Hd[0] - 10 + Math.cos(fa) * o * 0, Hd[1] + o); c.lineTo(Hd[0] + 4, Hd[1] + o - 2); c.stroke(); });
418
+ c.restore();
419
+ return G;
420
+ }
421
+ // 热气:杯口上方三道「≀」卷曲细线
422
+ function steam(c, G, t) {
423
+ const cp = G.kylix; c.save(); c.strokeStyle = C.black; c.lineWidth = 2.6; c.lineCap = 'round';
424
+ for (let k = -1; k <= 1; k++) { c.beginPath(); for (let i = 0; i <= 16; i++) { const u = i / 16, x = cp.x - 10 + k * 14 + 4.5 * Math.sin(u * TAU * 1.2 + t * 9 + k * 1.3), y = cp.y - 14 - u * 28; i ? c.lineTo(x, y) : c.moveTo(x, y); } c.stroke(); }
425
+ c.restore();
426
+ }
427
+
428
+ return {
429
+ draw(c, lt, t) {
430
+ const ch = P.choreo(lt, t);
431
+ c.drawImage(bg(), 0, 0);
432
+ // 纹带:原片节拍上的一次 easeInOut 周期跳 + 全程匀速漂移
433
+ const topOff = 48 * lt + TOP_P * IO(clamp((lt - 0.49) / 0.35));
434
+ const botOff = -(30 * lt + BOT_P * IO(clamp((lt - 0.477) / 0.367)));
435
+ const ts = ((topOff % TOP_P) + TOP_P) % TOP_P, bs = ((botOff % BOT_P) + BOT_P) % BOT_P;
436
+ c.drawImage(topStrip(), -ts, 30);
437
+ c.drawImage(botStrip(), -bs, 930);
438
+ c.drawImage(stat(), 0, 0);
439
+ panel(c, lt);
440
+ lyre(c, lt);
441
+ [[498, 628, 12], [436, 662, 20], [745, 742, 20], [330, 800, 16], [752, 860, 12]].forEach(([x, y, r], k) => rosette(c, x, y, r, lt * 1.6 * (k % 2 ? 1 : -1) + k));
442
+ cat(c, ch);
443
+ const G = girl(c, ch, lt, t);
444
+ steam(c, G, t);
445
+ c.drawImage(vignette(), 0, 0);
446
+ },
447
+ };
448
+ })();